Compare commits

...

176 Commits

Author SHA1 Message Date
Dominik Derigs
e5e8cae1ca Add --no-ident option. 2023-01-23 22:48:01 +00:00
Simon Kelley
7f42ca8af8 Add acknowledgements to CHANGELOG for the 2.88 AWS efforts. 2023-01-14 11:12:17 +00:00
Simon Kelley
e4251eb13b Fix Changelog typos. 2023-01-14 11:01:10 +00:00
Simon Kelley
5083876910 Bump version in Debian changelog. 2023-01-13 22:03:33 +00:00
Simon Kelley
f172fdbb77 Fix bug which can break the invariants on the order of a hash chain.
If there are multiple cache records with the same name but different
F_REVERSE and/or F_IMMORTAL flags, the code added in fe9a134b could
concievable break the REVERSE-FORWARD-IMMORTAL order invariant.

Reproducing this is damn near impossible, but it is responsible
for rare and otherwise inexplicable reversion between 2.87 and 2.88
which manifests itself as a cache internal error. All observed
cases have depended on DNSSEC being enabled, but the bug could in
theory manifest itself without DNSSEC

Thanks to Timo van Roermund for reporting the bug and huge
efforts to isolate it.
2023-01-13 21:12:53 +00:00
Simon Kelley
3822825e54 Fix cosmetic big in dump_cache_entry() 2023-01-04 23:10:07 +00:00
Simon Kelley
1da54210fc Log all cache internal errors. 2023-01-02 22:17:57 +00:00
Simon Kelley
43a2a66531 If we hit a cache internal error, log the entry we failed to remove.
This is code which should never run, but if it does,
we now log information useful for debugging.
2022-12-22 23:19:05 +00:00
Simon Kelley
e6841ea2e0 Add posix-timezone and tzdb-timezone DHCPv6 options.
They are already in place for DHCPv4.
2022-12-04 22:00:54 +00:00
Simon Kelley
e939b45c9f Handle malformed DNS replies better.
If we detect that that reply from usptream is malformed,
transform it into a SERVFAIL reply before sending to the
original requestor.
2022-11-26 22:19:29 +00:00
Brad Smith
e3068ed111 Fix warning in cache.c 2022-11-26 21:48:17 +00:00
Dominik Derigs
efbf80be58 Make max staleness of stale cache entries configurable and default to one day. 2022-11-26 21:18:34 +00:00
Petr Menšík
022ad63f0c Fix use-after-free in mark_servers() 2022-11-26 18:49:21 +00:00
Petr Menšík
02f8754339 fixup! Handle DS records for unsupported crypto algorithms. 2022-11-22 22:51:11 +00:00
Simon Kelley
142456cfd0 Merge i18n strings. 2022-11-21 16:56:51 +00:00
Simon Kelley
207ce40db2 Add /etc/hosts gotcha to man page section for --dhcp-hosts. 2022-11-21 16:53:56 +00:00
Simon Kelley
881eaa4dbc Optimise readng large number --server options at start up.
When re-reading upstream servers from /etc/resolv.conf or other
sources that can change dnsmasq tries to avoid memory fragmentation by
re-using existing records that are being re-read unchanged. This
involves seaching all the server records for each new one installed.
During startup this search is pointless, and can cause long start
times with thousands of --server options because the work needed is
O(n^2). Handle this case more intelligently.  Thanks to Ye Zhou for
spotting the problem and an initial patch.
2022-11-21 16:40:53 +00:00
Matthias Andree
d6d7527c95 Fix CHANGELOG typos. 2022-11-18 09:10:32 +00:00
Conrad Kostecki
11b4be2036 Update german translation for release 2.88. 2022-11-17 20:00:42 +00:00
Simon Kelley
3e306c1202 Fix SEGV on --local= added by immediately previous commit. 2022-11-17 19:51:15 +00:00
Simon Kelley
7f227a83f2 Fix struct hostinfo free code and BSD compile.
The code added in6 c596f1cc1d92b2b90ef5ce043ace314eefa868b
fails to free the returned datastructures from gethostinfo()
because sdetails.hostinfo is used to loop through the addresses
and ends up NULL. In some libc implementations this results
in a SEGV when freeaddrinfo() is called.

Also fix FTBFS under BSD. Thanks to Johnny S. Lee for the bug report.
2022-11-17 13:16:55 +00:00
Simon Kelley
9ed3ee67ec Handle DS records for unsupported crypto algorithms correctly.
Such a DS, as long as it is validated, should allow answers
in the domain is attests to be returned as unvalidated, and not
as a validation error.
2022-11-16 16:49:30 +00:00
Simon Kelley
1f9215f5f9 Fix GOST signature algorithms for DNSSEC validation.
Use CryptoPro version of the hash function.
Handle the little-endian wire format of key data.
Get the wire order of S and R correct.

Note that Nettle version 3.6 or later is required for GOST support.
2022-11-16 15:57:31 +00:00
Simon Kelley
f52cfdd8c3 Handle known DNSSEC signature algorithms which are not supported.
This fixes a confusion if certain algorithms are not supported
because the version is the crypto library is too old.  The validation
should be treated the same as for a completely unknown algorithm,
(ie return unverified answer) and not as a validation failure
(ie return SERVFAIL).

The algorithems affected are GOST and ED448.
2022-11-13 15:55:09 +00:00
Simon Kelley
2fc904111d Fix --server=/domain/# 2022-11-07 23:24:31 +00:00
Simon Kelley
262dadf50e Fix --server with multiple domains. 2022-11-07 23:14:30 +00:00
Simon Kelley
6c596f1cc1 Make specifying nameservers by name work for DBus API. 2022-11-07 23:00:34 +00:00
Simon Kelley
dafa16c400 Call freeaddrinfo() in domain_rev[46]() 2022-11-07 22:01:28 +00:00
Simon Kelley
1db9943c68 Extend specifying DNS servers by domain-name to --rev-server
Also Dbus SetDomainServers method.

Revert getaddrinfo hints.ai_socktype to SOCK_DGRAM to eliminate
duplicating every address three times for DGRAM, STREAM and RAW
in the results.
2022-11-06 21:10:19 +00:00
Simon Kelley
5b868c213b Fix breakage of --local=/domain.name/1.2.3.4 in immediately previous commit. 2022-11-06 20:18:27 +00:00
Dominik Derigs
2d8905dafd Allow domain names as well is IP addresses in --server options. 2022-11-05 11:49:52 +00:00
Simon Kelley
9002108551 Bump Debian version and close bug. 2022-11-02 22:18:35 +00:00
Simon Kelley
d3c21c596e Reconcile "names" and "address" counts when reading hostfiles. 2022-10-30 15:40:20 +00:00
Simon Kelley
34fac952b6 Inotify: make "flushed" log message more understandable.
Saying we've "flushed x outdated entries" is confusing, since
the count is the total number of entries in the modified file,
most of which are going	to get added straight back when	the file
is re-read.

The log now looks like

dnsmasq: inotify: /tmp/dir/1 (new or modified)
dnsmasq: inotify: flushed 1 addresses read from /tmp/dir/1
dnsmasq: read /tmp/dir/1 - 2 addresses

which hopefully make it more obvious that /tmp/dir/1 contained one
address before, and now contains two.
2022-10-27 13:24:37 +01:00
Dominik Derigs
92c32e0bac Do not (try to) re-read deleted files inside a --hostsdir. 2022-10-27 12:36:38 +01:00
Simon Kelley
1bcad67806 Fix in dhcpv4 rapid-commit code.
1) Cosmetic: don't log the tags twice.

2) Functional. If a host has an old lease for a different address,
   the rapid-commit will appear to work, but the old lease will
   not be removed and the new lease will not be recorded, so
   the client and server will have conflicting state, leading to
   problems later.
2022-10-27 12:04:58 +01:00
Simon Kelley
fe9a134baf Add --no-round-robin option. 2022-10-18 16:06:48 +01:00
Simon Kelley
930428fb97 Fix loss of DNS servers on config reload.
A bug, introduced in 2.87, which could result in DNS
servers being removed from the configuration when reloading
server configuration from DBus, or re-reading /etc/resolv.conf
Only servers from the same source should be replaced, but some
servers from other sources (ie hard coded or another dynamic source)
could mysteriously disappear.
2022-10-17 21:15:43 +01:00
Dominik Derigs
936be022d9 Handle multiple addresses when removing duplicates in host files. 2022-10-16 22:30:08 +01:00
Dominik Derigs
0017dd74d5 Enhance --hostdir so that records are automatically removed when re-reading.
Initial patch from Dominik Derigs, re-written by Simon Kelley.
2022-10-16 22:10:48 +01:00
Dominik Derigs
0ba25a0512 Improve logging of DNS record source from --hostsdir files.
Patch author Dominik Derigs <dl6er@dl6er.de> with subsequent bugfixes
and tweaks from Simon Kelley.
2022-10-16 21:14:16 +01:00
Simon Kelley
a176cf1bc3 Move fast-dns-retry and use-stale-cache writeups in the CHANGELOG.
These are 2.88 changes, but the branch merge put them unde 2.87.
2022-10-14 11:46:13 +01:00
Simon Kelley
fdd9a96a8c Merge branch 'aws' 2022-10-13 15:37:52 +01:00
Simon Kelley
b87d7aa041 Fix bug in --dynamic-host when interface has /16 IPv4 address. 2022-10-13 15:02:54 +01:00
Temuri Doghonadze
f753e7eba6 Add Georgian translation. 2022-10-13 14:33:01 +01:00
Simon Kelley
78a5a21655 Fix Debian changelog date Fubar. 2022-09-25 23:55:09 +01:00
Simon Kelley
a5cbe6d112 Add ClearMetrics Dbus method. 2022-09-16 12:58:41 +01:00
Simon Kelley
9403664616 Optimise cache code when stale caching in use.
Exclude DNSSEC entries from stale caching.
2022-09-16 12:44:04 +01:00
Simon Kelley
f32498465d Don't exclude stale-cache answers from "local answered" metric. 2022-09-16 09:35:44 +01:00
Simon Kelley
fa45e06431 Initialise modified-moving-average latency calc better.
Use the first value, rather than initialising at zero,
which takes many queries to converge.
2022-09-16 00:16:18 +01:00
Simon Kelley
6722ec6c78 Split failed queries in retries in stat counting. 2022-09-16 00:07:36 +01:00
Simon Kelley
d882dfdae9 Tweak server-selection logic in the fast-retry case. 2022-09-15 23:54:53 +01:00
Simon Kelley
a2ee2426bf Keep a per-DNS-server moving average of query latency. 2022-09-15 23:22:02 +01:00
Simon Kelley
84bd46ddd7 Combine server stats from all records for the same server in DBUS method.
The DBUS per-server stats method should combine the stats from
different records (for different domains) in the same way at the
logging code.
2022-09-15 22:43:08 +01:00
Simon Kelley
271790685a Count NXDOMAIN replies from each server. 2022-09-15 22:29:44 +01:00
Simon Kelley
7a74037267 Add metric for queries which never see an answer. 2022-09-15 22:06:39 +01:00
Simon Kelley
9a9f6e147c Make fast-retry more configurable and do exponential backoff. 2022-09-15 19:29:49 +01:00
Simon Kelley
8f2d432799 Remove unused vars. 2022-09-13 09:36:08 +01:00
Simon Kelley
92eab03b12 Return EDE_STALE extended error when returning stale data from cache. 2022-09-12 15:28:46 +01:00
Simon Kelley
1ba4ae2830 Add stale cache replies to metrics. 2022-09-12 14:50:17 +01:00
Simon Kelley
0076481dfd Add GetServerMetrics method to DBus interface. 2022-09-12 14:35:40 +01:00
Simon Kelley
c0e731d545 Further optimisation of --port-limit.
No longer try and fail to open every port when the port range
is in complete use; go straight to re-using an existing socket.

Die at startup if port range is smaller than --port-limit, since
the code behaves badly in this case.
2022-09-09 23:15:50 +01:00
Simon Kelley
3f56bb8ba1 Second try at port-limit option.
1) It's expected to fail to bind a new source port when they
   are scarce, suppress warning in log in this case.

2) Optimse bind_local when max_port - min_port is small. There's no
   randomness in this case, so we try all possible source ports
   rather than poking at random ones for an arbitrary number of tries.

3) In allocate_rfd() handle the case that all available source ports
   are already open. In this case we need to pick an existing
   socket/port to use, such that it has a different port from any we
   already hold. This gives the required property that the set of ports
   utilised by any given query is set by --port-limit and we don't
   re-use any until we have port-limit different ones.
2022-09-09 17:09:32 +01:00
Simon Kelley
e518e87533 Fix namebuff overwrite leading to wrong log after socket bind warning. 2022-09-09 15:57:39 +01:00
Simon Kelley
c4b9bc63e0 Fix a problem in overload handling.
Sending the same query repeatedly to a dnsmasq instance which
doesn't get replies from upstream will eventually hit the
hard limit on frec_src structures and start gettin REFUSED
replies. This is OK, except that since the queries are no longer
being forwarded, an upstream server coming back doesn't reset the
situation. If there is any other traffic, frec allocation will
eventually delete the timed-out frec and get things moving again,
but that's not guaranteed.

To fix this we explicitly delete the frec once timed out in this case.

Thanks to Filip Jenicek for noticing and characterising this problem.
2022-09-09 12:53:49 +01:00
Simon Kelley
1d53d958bb Remove fast-retry development logging. 2022-09-06 22:43:33 +01:00
Simon Kelley
d334e7c34f Add --use-stale-cache option. 2022-09-06 22:43:33 +01:00
Simon Kelley
d21438a7df Add --fast-dns-retry option.
This gives dnsmasq the ability to originate retries for upstream DNS
queries itself, rather than relying on the downstream client. This is
most useful when doing DNSSEC over unreliable upstream network. It
comes with some cost in memory usage and network bandwidth.
2022-09-06 22:43:33 +01:00
Simon Kelley
24c3b5b3d4 Add --port-limit option.
By default, when sending a query via random ports to multiple upstream servers or
retrying a query dnsmasq will use a single random port for all the tries/retries.
This option allows a larger number of ports to be used, which can increase robustness
in certain network configurations. Note that increasing this to more than
two or three can have security and resource implications and should only
be done with understanding of those.
2022-09-06 22:43:33 +01:00
Simon Kelley
4447d48bb9 Add DHCPv4 option 108 "ipv6-only" to the options table. 2022-09-06 22:40:06 +01:00
Simon Kelley
04cc2ae1a6 Fix logic when a SERVFAIL reply is received after good replt for DNSSEC.
If we get a SERVFAIL or REFUSED answer to a DNSSEC query for which
we already have a good answer, just ignore it.
2022-09-06 18:31:59 +01:00
Simon Kelley
32588c755a Add source address to RA packet dumps. 2022-09-06 18:08:39 +01:00
Simon Kelley
84a6d07cdd Fix DHCPv6 relay to use a more sensble source address.
Tweak things so that packets relayed towards a server
have source address on the server-facing network, not the
client-facing network. Thanks to Luis Thomas for spotting this
and initial patch.
2022-09-06 15:40:42 +01:00
Simon Kelley
d6c69f6bdb Free sockets awaiting upstream DNS replies ASAP.
Once we have a good answer, close the socket so that the fd can
be reused during DNSSEC validation and we don't have to read and
discard more replies from other servers.
2022-09-06 15:35:54 +01:00
Simon Kelley
ce372917fe Tweak packet dump code to make port numbers more accurate.
Also add query-ids with log-queries=extra.
2022-09-05 18:04:35 +01:00
Simon Kelley
09d741f58a Simplify realloc use in poll.c 2022-08-11 17:04:54 +01:00
Petr Menšík
0666ae3d27 Introduce whine_realloc
Move few patters with whine_malloc, if (successful) copy+free, to a new
whine_realloc. It should do the same thing, but with a help from OS it
can avoid unnecessary copy and free if allocation of more data after
current data is possible.

Added few setting remanining space to 0, because realloc does not use
calloc like whine_malloc does. There is no advantage of zeroing what we
will immediately overwrite. Zero only remaining space.
2022-08-11 16:56:58 +01:00
Simon Kelley
ba4c7d906b CHANGELOG typo. 2022-08-08 15:36:47 +01:00
Simon Kelley
f4b2813818 Fix bad interaction between --address=/#/<ip> and --server=/some.domain/#
This would return <ip> for queries in some.domain, rather than
forwarding the query via the default server(s) read from /etc/resolv.conf.
2022-08-08 15:27:32 +01:00
Bertie, Taylor
5586934da0 Bound the value of UDP packet size in the EDNS0 header of
forwarded queries to the configured or default value of
edns-packet-max. There's no point letting a client set a larger
value if we're unable to return the answer.
2022-07-31 17:20:21 +01:00
Simon Kelley
6134b94c02 Update man page on DHCP data provided to scripts. Provide requested options for DHCPv6 also. 2022-07-31 12:15:38 +01:00
Simon Kelley
05e6728e98 Fix bit-rotted data handling code for LUA scripts. 2022-07-31 11:33:05 +01:00
Simon Kelley
6578acd668 Tidy last two commits. 2022-07-31 11:04:12 +01:00
Kevin Yeich
b5581ed173 Pass MUD URLs (RFC 8520) supplied via DHCPv4 to DHCP scripts
Extract Manufacturer Usage Description (MUD) URL from DHCP Option 161
and make it available to DHCP scripts as DNSMASQ_MUD_URL.

See https://datatracker.ietf.org/doc/html/rfc8520#section-17.3
and https://datatracker.ietf.org/doc/html/rfc8520#section-10

Co-authored-by: Jasper Wiegratz <wiegratz@uni-bremen.de>
2022-07-29 13:01:47 +01:00
Hugo Hakim Damer
508d6b4885 Pass MUD URLs (RFC 8520) supplied via DHCPv6 to DHCP scripts
Extract Manufacturer Usage Description (MUD) URL from DHCP Option 112
and make it available to DHCP scripts as DNSMASQ_MUD_URL.

This expands on the initial support for Manufacturer Usage Description
URLs that has been added in the previous commit for DHCPv4 by also
supporting MUD URLs supplied using DHCPv6.

See https://datatracker.ietf.org/doc/html/rfc8520#section-17.3
and https://datatracker.ietf.org/doc/html/rfc8520#section-10

Co-authored-by: Jasper Wiegratz <wiegratz@uni-bremen.de>
2022-07-29 12:57:27 +01:00
Simon Kelley
ef6efd69ed Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq 2022-07-19 23:40:50 +01:00
Simon Kelley
151d7dc5ea Fix comment typo. 2022-07-19 23:40:11 +01:00
Simon Kelley
20b4a4ea5b Fix comment typo. 2022-07-07 20:56:07 +01:00
Beniamino Galvani
770bce967c Fix parsing of IPv6 addresses with peer from netlink.
In the most common case, an IPv6 address doesn't have a peer and the
IFA_ADDRESS netlink attribute contains the address itself.

But if the address has a peer (typically for point to point links),
then IFA_ADDRESS contains the peer address and IFA_LOCAL contains the
address [1].

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv6/addrconf.c?h=v5.17#n5030

Fix the parsing of IPv6 addresses with peers, as currently dnsmasq
unsuccessfully tries to bind on the peer address.

A simple reproducer is:

  dnsmasq --conf-file=/dev/null -i dummy1 -d --bind-dynamic &
  sleep 2
  ip link add dummy1 type dummy
  ip link set dummy1 up
  ip addr add dev dummy1 fd01::1/64 peer fd01::2/64
  ip addr add dev dummy1 fd01::42/64
  sleep 2
  ss -lnp | grep dnsmasq | grep fd01

Before the patch:
  dnsmasq: failed to create listening socket for fd01::2: Cannot assign requested address
  dnsmasq: failed to create listening socket for fd01::2: Cannot assign requested address
  udp   UNCONN 0   [fd01::42]:53   [::]:*    users:(("dnsmasq",pid=23947,fd=14))
  tcp   LISTEN 0   [fd01::42]:53   [::]:*    users:(("dnsmasq",pid=23947,fd=15

After:
  udp   UNCONN 0   [fd01::42]:53   [::]:*    users:(("dnsmasq",pid=23973,fd=16))
  udp   UNCONN 0    [fd01::1]:53   [::]:*    users:(("dnsmasq",pid=23973,fd=14))
  tcp   LISTEN 0   [fd01::42]:53   [::]:*    users:(("dnsmasq",pid=23973,fd=17))
  tcp   LISTEN 0    [fd01::1]:53   [::]:*    users:(("dnsmasq",pid=23973,fd=15))
2022-05-27 21:16:18 +01:00
Simon Kelley
a267a9e489 Add the ability to specify destination port in DHCP-relay mode.
This change also removes a previous bug
where --dhcp-alternate-port would affect the port used
to relay _to_ as well as the port being listened on.
The new feature allows configuration to provide bug-for-bug
compatibility, if required. Thanks to Damian Kaczkowski
for the feature suggestion.
2022-05-26 16:40:44 +01:00
Simon Kelley
f65d210012 Fix outdated comment. 2022-05-26 14:49:10 +01:00
Petr Menšík
858bfcf261 Update GNU GPL file. 2022-05-13 21:22:11 +01:00
Dominik Derigs
9b801c4e72 Also log upstream port for dnssec-retry
Signed-off-by: DL6ER <dl6er@dl6er.de>
2022-04-18 15:28:27 +01:00
袁建鹏
1a98d1a94f Add inode compare while checking resolv file change
Fix a bug found on OpenWrt when IPv4/6 dual stack enabled:

The resolv file is located on tmpfs whose mtime resolution
is 1 second. If the resolv file is updated twice within one
second dnsmasq may can't notice the second update.

netifd updates the resolv file with method: write temp then move,
so adding an inode check fixes this bug.
2022-04-18 15:25:54 +01:00
Simon Kelley
03345ecefe Fix write-after-free error in DHCPv6 code. CVE-2022-0934 refers. 2022-03-31 21:35:20 +01:00
Simon Kelley
191924576c Add DNSMASQ_DATA_MISSING envvar to lease-change script. 2022-03-22 13:47:05 +00:00
Simon Kelley
756a1dcc19 Manpage update for --localise-queries.
Thanks to Leonardo Romor for the suggestion.
2022-03-05 18:13:15 +00:00
Simon Kelley
3ab6dd1c37 Enhance --domain to accept, interface names for the address range.
This allows hosts get a domain which relects the interface they
are attached to in a way which doesn't require hard-coding addresses.

Thanks to Sten Spans for the idea.
2022-03-05 18:07:07 +00:00
Simon Kelley
4458d87289 Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq 2022-03-05 16:31:17 +00:00
Conrad Kostecki
b7f62475d0 Update German translation. 2022-03-02 19:28:26 +00:00
Simon Kelley
4732aa663b Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq 2022-02-24 23:21:55 +00:00
Simon Kelley
c27cfeaa7b Fix memory leak when DBUS connection fails. 2022-02-24 23:18:54 +00:00
Simon Kelley
bb6f6bae0b Fix longjump() compiler warnings. 2022-02-24 23:16:04 +00:00
Simon Kelley
f4c87b504b Fix missing reverse-records from --dynamic-host.
Thanks to Sten Spans for spotting the bug.
2022-02-18 20:53:56 +00:00
Simon Kelley
e426c2d3bc Add --conf-script 2022-02-08 12:10:27 +00:00
Simon Kelley
6279d9eaf3 Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq 2022-02-04 22:28:53 +00:00
Simon Kelley
12949aa0c0 Ask netlink for new address events unconditionally.
The circumstances under which actions occur depending on
configuration is now controlled only by newaddress() in network.c
2022-02-04 22:24:00 +00:00
Simon Kelley
84f3357dd9 Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq 2022-02-04 21:00:16 +00:00
Johnny S. Lee via Dnsmasq-discuss
4333d5d93a Fix FTBFS on BSD platforms.
Bug introduced in fc664d114d
2022-02-03 23:42:00 +00:00
Simon Kelley
fa580ad3eb Handle changing interface indexes when binding DHCP sockets. 2022-02-03 17:26:28 +00:00
Simon Kelley
292dfa653e Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq 2022-02-02 18:31:22 +00:00
Simon Kelley
7fbf1cce7b Improve the performance of DHCP relay.
On machines with many interfaces, enumerating them
via netlink on each packet reciept is slow,
and unneccesary. All we need is the local address->interface
mapping, which can be cached in the relay structures.
2022-02-02 18:28:27 +00:00
Simon Kelley
dbceeb4178 Dump.c Fix IPv6 checksum on big-endian. 2022-01-30 00:42:46 +00:00
Simon Kelley
ed200fa001 Handle options other than source link-layer address in router solicitations.
RFC 4861 para 4.1 is a MUST.
2022-01-29 23:22:52 +00:00
Simon Kelley
b5dafc0b7e Extend packet dump system to RA. 2022-01-29 22:52:21 +00:00
Simon Kelley
fc664d114d Extend packet-dump system to DHCP and TFTP. 2022-01-29 15:55:04 +00:00
Simon Kelley
c6d4c33d61 Bump copyright to 2022. 2022-01-24 15:19:00 +00:00
Simon Kelley
bf1fc6c6fd Tidy iface_check(). 2022-01-21 15:47:09 +00:00
Olaf Hering
b18e9c8c61 fix dnsmasq typo in man page
Fixes commit 27ce754b3d

Signed-off-by: Olaf Hering <olaf@aepfle.de>
2022-01-21 12:18:08 +00:00
Simon Kelley
a3293bb242 Fix indentation in Umbrella option code. 2022-01-21 12:07:42 +00:00
Simon Kelley
4e2a4b8788 Fix crash in PXE code with bad config. 2022-01-18 00:55:13 +00:00
Simon Kelley
2362784bc0 Debian bug management. 2022-01-18 00:32:15 +00:00
Simon Kelley
b2cec1b881 Debian: fold in 2.86-1.1 changelog and close bug introducded by same. 2022-01-18 00:02:32 +00:00
Simon Kelley
a946857133 Remove temporary debugging message and close related Debian bug. 2022-01-17 23:54:58 +00:00
Simon Kelley
10cd342f5c Document change of behaviour of --address in 2.86 onwards. 2022-01-17 16:01:02 +00:00
Simon Kelley
27ce754b3d Tidy previous commit and add manpage entries for new options. 2022-01-15 17:57:57 +00:00
Dominik Derigs
3ab0ad8748 Strip EDNS(0) Client Subnet / MAC information if --strip-subnet or --strip-mac is set. If both the add and strip options are set, incoming EDNS0 options are replaced. This ensures we do not unintentionally forward client information somewhere upstream when ECS is used in lower DNS layers in our local network.
Signed-off-by: DL6ER <dl6er@dl6er.de>
2022-01-15 15:36:28 +00:00
Dominik Derigs
4308236262 Minimum safe size is recommended to be 1232. See https://dnsflagday.net/2020/
Signed-off-by: DL6ER <dl6er@dl6er.de>
2022-01-13 00:31:54 +00:00
Simon Kelley
ebd8350300 Fix DNSSEC failure to validate unsigned NoDATA replies.
A reply with an empty answer section would not always be checked
for either suitable NSEC records or proof of non-existence of
the relevant DS record.
2022-01-13 00:12:07 +00:00
Simon Kelley
8285d335f4 Fix error introduced in 11c52d032b 2022-01-12 23:05:25 +00:00
Simon Kelley
9db275ebea Small fix to ff43d35aee 2022-01-12 23:00:16 +00:00
Petr Menšík
1f8f78a49b Add root group writeable flag to log file
Some systems strips even root process capability of writing to different
users file. That include systemd under Fedora. When
log-facility=/var/log/dnsmasq.log is used, log file with mode 0640
is created. But restart then fails, because such log file can be used
only when created new. Existing file cannot be opened by root when
starting, causing fatal error. Avoid that by adding root group writeable flag.

Ensure group is always root when granting write access. If it is
anything else, administrator has to configure correct rights.
2022-01-11 23:43:09 +00:00
Simon Kelley
c2f129ba3d Fix FTBFS when HAVE_DNSSEC not defined. 2022-01-11 22:48:14 +00:00
Simon Kelley
07c47416a9 Log source of ignored query when local-service is used.
Thanks to Dominik Derigs for the initial patch.
2022-01-11 22:36:01 +00:00
Dominik Derigs
8f2a62b386 Extend cache dump: "!" as type for non-terminals, new flag "C" for config-provided and log source when applicable.
Signed-off-by: DL6ER <dl6er@dl6er.de>
2022-01-11 22:17:14 +00:00
Dominik Derigs
a6c0edd4f4 Fix header of cache dump. The width of the host and address fields are 30 and 40 characters, respectively.
Signed-off-by: DL6ER <dl6er@dl6er.de>
2022-01-11 22:16:48 +00:00
Simon Kelley
ff43d35aee Log port numbers in server addresses when non-standard ports in use. 2022-01-11 22:09:09 +00:00
Simon Kelley
70fca205be Overhaul code which sends DNSSEC queries.
There are two functional changes in this commit.

1) When searching for an in-flight DNSSEC query to use
   (rather than starting a new one), compare the already
   sent query (stored in the frec "stash" field, rather than
   using the hash of the query. This is probably faster (no hash
   calculation) and eliminates having to worry about the
   consequences of a hash collision.

2) Check for dependency loops in DNSSEC validation,
   say validating A requires DS B and validating DS B
   requires DNSKEY C and validating DNSKEY C requires DS B.
   This should never happen in correctly signed records, but it's
   likely the case that sufficiently broken ones can cause
   our validation code requests to exhibit cycles.
   The result is that the ->blocking_query list
   can form a cycle, and under certain circumstances that can lock us in
   an infinite loop.
   Instead we transform the situation into an ABANDONED state.
2022-01-11 00:29:36 +00:00
Simon Kelley
1033130b6c Handle malformed query packets sensibly.
Previously, hash_questions() would return a random hash
if the packet was malformed, and probably the hash of a previous
query. Now handle this as an error.
2022-01-09 23:21:55 +00:00
Andreas Metzler
8cfcd9ff63 Clarify man page for --filterwin2k 2022-01-06 23:12:53 +00:00
Daniel Collins
80a6c16dcc Implements a SetLocaliseQueriesOption D-Bus method.
For setting the state of the -y/--localise-queries option.
2022-01-06 00:23:53 +00:00
Simon Kelley
553c4c99cc Fix massive confusion on server reload.
The 2.86 upstream server rewrite severely broke re-reading
of server configuration. It would get everyting right the first
time, but on re-reading /etc/resolv.conf or --servers-file
or setting things with DBUS, the results were just wrong.

This should put things right again.
2022-01-03 23:32:30 +00:00
Simon Kelley
4165c1331b Fix fail to build when NO_SCRIPT set. 2022-01-03 23:31:15 +00:00
Fabrice Fontaine
b2690415bf src/option.c: fix build with gcc 4.8
Thanks for applying and fixing my patch. Here is another one on src/pattern.c

Best Regards,

Fabrice

Le dim. 2 janv. 2022 à 00:36, Simon Kelley <simon@thekelleys.org.uk> a écrit :
>
>
>
> Thanks,
>
>
> patch applied. Followed by a small fix, and then a larger fix when I was
> forced to look at the code in question ;)
>
>
>
> Cheers,
>
> Simon.
>
> On 31/12/2021 16:29, Fabrice Fontaine wrote:
> > Fix the following build failure with gcc 4.8 raised since version 2.86:
> >
> > option.c: In function 'one_opt':
> > option.c:2445:11: error: 'for' loop initial declarations are only allowed in C99 mode
> >            for (char *p = arg; *p; p++) {
> >            ^
> > option.c:2445:11: note: use option -std=c99 or -std=gnu99 to compile your code
> > option.c:2453:11: error: 'for' loop initial declarations are only allowed in C99 mode
> >            for (u8 i = 0; i < sizeof(daemon->umbrella_device); i++, arg+=2) {
> >            ^
> >
> > Fixes:
> >  - http://autobuild.buildroot.org/results/39b34a4e69fc10f4bd9d4ddb0ed8c0aae5741c84
> >
> > Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
> > ---
> >  src/option.c | 6 ++++--
> >  1 file changed, 4 insertions(+), 2 deletions(-)
> >
> > diff --git a/src/option.c b/src/option.c
> > index ff54def..c57f6d8 100644
> > --- a/src/option.c
> > +++ b/src/option.c
> > @@ -2525,7 +2525,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
> >            arg += 9;
> >            if (strlen(arg) != 16)
> >                ret_err(gen_err);
> > -          for (char *p = arg; *p; p++) {
> > +          char *p;
> > +          for (*p = arg; *p; p++) {
> >              if (!isxdigit((int)*p))
> >                ret_err(gen_err);
> >            }
> > @@ -2533,7 +2534,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
> >
> >            u8 *u = daemon->umbrella_device;
> >            char word[3];
> > -          for (u8 i = 0; i < sizeof(daemon->umbrella_device); i++, arg+=2) {
> > +          u8 i;
> > +          for (i = 0; i < sizeof(daemon->umbrella_device); i++, arg+=2) {
> >              memcpy(word, &(arg[0]), 2);
> >              *u++ = strtoul(word, NULL, 16);
> >            }
> >
>

From 0c89dd2fa0fe50b00bca638dbbacfbd361526e0a Mon Sep 17 00:00:00 2001
From: Fabrice Fontaine <fontaine.fabrice@gmail.com>
Date: Sun, 2 Jan 2022 21:57:52 +0100
Subject: [PATCH] src/pattern.c: fix build with gcc 4.8

Fix the following build failure:

pattern.c: In function 'is_valid_dns_name':
pattern.c:134:3: error: 'for' loop initial declarations are only allowed in C99 mode
   for (const char *c = value;; c++)
   ^
pattern.c:134:3: note: use option -std=c99 or -std=gnu99 to compile your code
pattern.c: In function 'is_valid_dns_name_pattern':
pattern.c:249:3: error: 'for' loop initial declarations are only allowed in C99 mode
   for (const char *c = value;; c++)
   ^

Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2022-01-03 15:13:53 +00:00
Simon Kelley
011f8cf1d0 Tidy code for --umbrella option. 2022-01-01 23:33:39 +00:00
Simon Kelley
2748fb81e2 Fix 46312909d9 typo. 2022-01-01 23:03:26 +00:00
Fabrice Fontaine
46312909d9 src/option.c: fix build with gcc 4.8
Fix the following build failure with gcc 4.8 raised since version 2.86:

option.c: In function 'one_opt':
option.c:2445:11: error: 'for' loop initial declarations are only allowed in C99 mode
           for (char *p = arg; *p; p++) {
           ^
option.c:2445:11: note: use option -std=c99 or -std=gnu99 to compile your code
option.c:2453:11: error: 'for' loop initial declarations are only allowed in C99 mode
           for (u8 i = 0; i < sizeof(daemon->umbrella_device); i++, arg+=2) {
           ^

Fixes:
 - http://autobuild.buildroot.org/results/39b34a4e69fc10f4bd9d4ddb0ed8c0aae5741c84

Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2022-01-01 23:00:25 +00:00
Simon Kelley
41adecad14 Include client address if TFTP file-not-found errors. 2022-01-01 22:15:16 +00:00
Simon Kelley
ea5d8c56a0 Finesse parsing of --dhcp-remoteid and --dhcp-subscrid.
To be treated as hex, the pattern must consist of only hex digits AND
contain at least one ':'. Thanks to Bengt-Erik Sandstrom who tripped
over a pattern consisting of a decimal number which was interpreted
surprisingly.
2021-12-30 23:22:43 +00:00
Simon Kelley
d242cbffa4 Add snooping of DHCPv6 prefix delegation to the DHCP-relay function. 2021-12-30 21:20:37 +00:00
Simon Kelley
1c8855ed10 Fix wrong client address for dhcp-script when DHCPv4 relay in use. 2021-12-26 16:35:54 +00:00
Simon Kelley
ea33a01303 Fix rare "Internal error in cache" messages.
Fix error created in 1ce1c6beae

Many thanks to Hartmut Birr for finding the bug and bisecting to
the guilty commit.

The breaking commit creates cache entries which have F_NXDOMAIN
set but none of F_IPV4, F_IPV6 or F_SRV. If cache_scan_free() is called
to delete such an entry it will fail to do so.

If the cache has no free slots and the least-recently-used slot is such
an entry, then a new insertion will attempt to make space by calling
cache_scan_free(), which will fail when it should be impossible and
trigger the internal error.
2021-12-24 18:58:35 +00:00
Simon Kelley
18b1d1424e Generalise --dhcp-relay.
Sending via broadcast/multicast is now supported for both
IPv4 and IPv6 and the configuration syntax made
easier (but backwards compatible).
2021-12-20 16:40:41 +00:00
Simon Kelley
1176cd58c9 Fix regression in --rebind-domain-ok in 2.86
The 2.86 domain-match rewrite changed matching from
whole-labels to substring matching, so example.com
would match example.com and www.example.com, as before,
but also goodexample.com, which is a regression. This
restores the original behaviour.

Also restore the behaviour of --rebind-domain-ok=//
to match domains with onlt a single label and no dots.

Thanks to Sung Pae for reporting these bugs and supplying
an initial patch.
2021-12-08 23:51:38 +00:00
guns
44a4643b62 Correctly return a heap-allocated empty string instead of NULL
Commit 32e15c3f45 added the following
change:

  --- a/src/option.c
  +++ b/src/option.c
  @@ -654,7 +654,7 @@ static char *canonicalise_opt(char *s)
       return 0;

     if (strlen(s) == 0)
  -    return "";
  +    return opt_string_alloc("");

     unhide_metas(s);
     if (!(ret = canonicalise(s, &nomem)) && nomem)

Unfortunately, opt_string_alloc(const char *cp) returns NULL when
strlen(cp) == 0, which in turn causes --rebind-domain-ok='' to crash
with SIGSEGV.
2021-12-04 12:03:31 +00:00
Simon Kelley
ed96efd865 Fix confusion with log-IDs and DNS retries.
The IDs logged when --log-queries=extra is in effect
can be wrong in three cases.

1) When query is retried in response to a a SERVFAIL or REFUSED
answer from upstream. In this case the ID of an unrelated query will
appear in the answer log lines.

2) When the same query arrives from two clients. The query is
sent upstream once, as designed, and the result returned to both clients,
as designed, but the reply to the first client gets the log-ID of the
second query in error.

3) When a query arrives, is sent upstream, and the reply comes back,
but the transaction is blocked awaiting a DNSSEC query needed to validate
the reply. If the client retries the query in this state, the blocking
DNSSEC query will be resent, as designed, but that send will be logged with
the ID of the original, currently blocked, query.

Thanks to  Dominik Derigs for his analysis of this problem.
2021-12-01 16:40:26 +00:00
Simon Kelley
e3093b532c Fix problems with upper-case in domain-match.
The domain-match rewrite didn't take into account
that domain names are case-insensitive, so things like

--address=/Example.com/.....

didn't work correctly.
2021-11-28 18:39:42 +00:00
Simon Kelley
9560658c5b Fix crash in PXE/netboot when DNS server disabled. 2021-10-19 15:33:41 +01:00
Simon Kelley
37a70d39e0 Add --filter and --filter-AAAA options. 2021-10-07 23:12:59 +01:00
Dominik Derigs
72fac0810c dnsmasq.h has to be included first as it sources config.h
Signed-off-by: DL6ER <dl6er@dl6er.de>
2021-10-07 09:28:34 +01:00
Simon Kelley
c166c07a93 Support IDN in --auth-zone. 2021-10-06 23:48:06 +01:00
Petr Menšík
39a625ff72 Disable transitional IDN rules, accept only sane names
Transitional encoding accepts every emoticon you can think about.
Because setlocale were not enabled before, IDN 2003 input was not
accepted by dnsmasq. It makes no sense therefore to maintain backward
compatibility. Accept only proper encoded unicode names and reject
random unicode characters.

Signed-off-by: Petr Menšík <pemensik@redhat.com>
2021-10-06 23:33:13 +01:00
Petr Menšík
ad32ca18a7 Enable locale support for IDN at startup
--address=/münchen.de/ is not accepted unless LOCALEDIR is defined on
build. It is not by default. If LIBIDN1 or 2 is defined, call setlocale
to initialize locale required to translate domains to ascii form.

Signed-off-by: Petr Menšík <pemensik@redhat.com>
2021-10-06 23:23:51 +01:00
Simon Kelley
efea282396 Fix logic in add_update_server() to make optimisation actually optimise. 2021-10-06 23:01:14 +01:00
Simon Kelley
33d6a01cd3 Use host byte-order variable for answer counting. 2021-10-06 22:54:35 +01:00
Simon Kelley
d290630d31 Fix crash after re-reading an empty resolv.conf file.
If dnsmasq re-reads a resolv file, and it's empty, it will
retry after a delay. In the meantime, the old servers from the
resolv file have been deleted, but the servers_array doesn't
get updated, leading to dangling pointers and crashes.

Thanks to Brad Jorsch for finding and analysing this bug.

This problem was introduced in 2.86.
2021-10-06 22:31:06 +01:00
Simon Kelley
d2ad5dc073 Fix truncation logic in make_local_answer()
add_resource_record() returns 1 if the record was added.
Only increment anscount of so.

Thanks to Petr Menšík for spotting the problem.
2021-10-05 23:38:20 +01:00
Simon Kelley
68ab5127af Man page tweak for --address and more than one address. 2021-10-05 22:50:58 +01:00
DL6ER
089a11f340 --local should behave as --server, not as --address according to the man page
Signed-off-by: DL6ER <dl6er@dl6er.de>
2021-10-05 22:47:10 +01:00
Olaf Hering
de1d04eb66 remove stale contrib/Suse
dnsmasq is included in SUSE Linux since 2004.

Signed-off-by: Olaf Hering <olaf@aepfle.de>
2021-09-30 12:17:44 +01:00
Dominik Derigs
ed4e7defd7 Do not fail hard when rev-server has a non-zero final address part
Signed-off-by: DL6ER <dl6er@dl6er.de>
2021-09-30 11:52:24 +01:00
Simon Kelley
267ab619c4 Get compilation flags for libnftables from pkg-config.
Omission spotted by Olaf Hering. Thanks.
2021-09-29 23:24:52 +01:00
Matt Whitlock
0140454ba2 dnsmasq_time: avoid signed integer overflow when HAVE_BROKEN_RTC
The dnsmasq_time() function, in the case of HAVE_BROKEN_RTC, was calling
times() to read the number of ticks "elapsed since an arbitrary point in
the past" and then dividing that by sysconf(_SC_CLK_TCK) to compute the
number of seconds elapsed since that arbitrary instant. This works fine
until the number of ticks exceeds 2^31, beyond which time the function
would begin erroneously returning negative times. On my system this
happens after approximately 248 days of uptime. A symptom is that
dnsmasq no longer populates the resolver cache with DHCP-derived names
at startup, as the inserted cache entries immediately expire due to
having negative expiration times that cause is_expired() to return true
when called with now==0.

This commit replaces the archaic implementation of dnsmasq_time() with a
call to the POSIX-standardized clock_gettime(CLOCK_MONOTONIC), thereby
eliminating the need to convert manually from ticks to seconds. The new
implementation will yield correct results until the system uptime
exceeds approximately 68 years.

Signed-off-by: Matt Whitlock <dnsmasq@mattwhitlock.name>
2021-09-29 09:46:13 +01:00
Simon Kelley
2c60441239 Fix FTBFS when CONNTRACK and UBUS but not DNSSEC compile options selected. 2021-09-28 23:42:15 +01:00
Simon Kelley
cbbd56c965 Build Debian binaries with NFTset support. 2021-09-27 23:16:18 +01:00
Simon Kelley
2561f9fe0e Fix confusion in DNS retries and --strict-order.
Behaviour to stop infinite loops when all servers return REFUSED
was wrongly activated on client retries, resulting in
incorrect REFUSED replies to client retries.

Thanks to Johannes Stezenbach for finding the problem.
2021-09-27 22:37:02 +01:00
74 changed files with 14023 additions and 7588 deletions

161
CHANGELOG
View File

@@ -1,3 +1,88 @@
version 2.89
Fix bug introduced in 2.88 (commit fe91134b) which can result
in corruption of the DNS cache internal data structures and
logging of "cache internal error". This has only been seen
in one place in the wild, and it took considerable effort
to even generate a test case to reproduce it, but there's
no way to be sure it won't strike, and the effect is to break
the cache badly. Installations with DNSSEC enabled are more
likely to see the problem, but not running DNSSEC does not
guarantee that it won't happen. Thanks to Timo van Roermund
for reporting the bug and for his great efforts in chasing
it down.
version 2.88
Fix bug in --dynamic-host when an interface has /16 IPv4
address. Thanks to Mark Dietzer for spotting this.
Add --fast-dns-retry option. This gives dnsmasq the ability
to originate retries for upstream DNS queries itself, rather
than relying on the downstream client. This is most useful
when doing DNSSEC over unreliable upstream networks. It comes
with some cost in memory usage and network bandwidth.
Add --use-stale-cache option. When set, if a DNS name exists
in the cache, but its time-to-live has expired, dnsmasq will
return the data anyway. (It attempts to refresh the
data with an upstream query after returning the stale data.)
This can improve speed and reliability. It comes
at the expense of sometimes returning out-of-date data and
less efficient cache utilisation, since old data cannot be
flushed when its TTL expires, so the cache becomes
strictly least-recently-used.
Add --port-limit option which allows tuning for robustness in
the face of some upstream network errors. Thanks to
Prashant Kumar Singh, Ravi Nagayach and Mike Danilov,
all of Amazon Web Services, for their efforts in developing this
and the stale-cache and fast-retry options.
Make --hostsdir (but NOT --dhcp-hostsdir and --dhcp-optsdir)
handle removal of whole files or entries within files.
Thanks to Dominik Derigs for the initial patches for this.
Fix bug, introduced in 2.87, which could result in DNS
servers being removed from the configuration when reloading
server configuration from DBus, or re-reading /etc/resolv.conf
Only servers from the same source should be replaced, but some
servers from other sources (i.e., hard coded or another dynamic source)
could mysteriously disappear. Thanks to all reporting this,
but especially Christopher J. Madsen who reduced the problem
to an easily reproducible case which saved much labour in
finding it.
Add --no-round-robin option.
Allow domain names as well as IP addresses when specifying
upstream DNS servers. There are some gotchas associated with this
(it will mysteriously fail to work if the dnsmasq instance
being started is in the path from the system resolver to the DNS),
and a seemingly sensible configuration like
--server=domain.name@1.2.3.4 is unactionable if domain.name
only resolves to an IPv6 address). There are, however,
cases where is can be useful. Thanks to Dominik Derigs for
the patch.
Handle DS records for unsupported crypto algorithms correctly.
Such a DS, as long as it is validated, should allow answers
in the domain it attests to be returned as unvalidated, and not
as a validation error.
Optimise reading large numbers of --server options. When re-reading
upstream servers from /etc/resolv.conf or other sources that
can change dnsmasq tries to avoid memory fragmentation by re-using
existing records that are being re-read unchanged. This involves
seaching all the server records for each new one installed.
During startup this search is pointless, and can cause long
start times with thousands of --server options because the work
needed is O(n^2). Handle this case more intelligently.
Thanks to Ye Zhou for spotting the problem and an initial patch.
If we detect that a DNS reply from upstream is malformed don't
return it to the requestor; send a SEVFAIL rcode instead.
version 2.87
Allow arbitrary prefix lengths in --rev-server and
--domain=....,local
@@ -8,7 +93,83 @@ version 2.87
Add --nftset option, like --ipset but for the newer nftables.
Thanks to Chen Zhenge for the patch.
Add --filter-A and --filter-AAAA options, to remove IPv4 or IPv6
addresses from DNS answers.
Fix crash doing netbooting when --port is set to zero
to disable the DNS server. Thanks to Drexl Johannes
for the bug report.
Generalise --dhcp-relay. Sending via broadcast/multicast is
now supported for both IPv4 and IPv6 and the configuration
syntax made easier (but backwards compatible).
Add snooping of IPv6 prefix-delegations to the DHCP-relay system.
Finesse parsing of --dhcp-remoteid and --dhcp-subscrid. To be treated
as hex, the pattern must consist of only hex digits AND contain
at least one ':'. Thanks to Bengt-Erik Sandstrom who tripped
over a pattern consisting of a decimal number which was interpreted
surprisingly.
Include client address in TFTP file-not-found error reports.
Thanks to Stefan Rink for the initial patch, which has been
re-worked by me (srk). All bugs mine.
Note in manpage the change in behaviour of -address. This behaviour
actually changed in v2.86, but was undocumented there. From 2.86 on,
(eg) --address=/example.com/1.2.3.4 ONLY applies to A queries. All other
types of query will be sent upstream. Pre 2.86, that would catch the
whole example.com domain and queries for other types would get
a local NODATA answer. The pre-2.86 behaviour is still available,
by configuring --address=/example.com/1.2.3.4 --local=/example.com/
Fix problem with binding DHCP sockets to an individual interface.
Despite the fact that the system call tales the interface _name_ as
a parameter, it actually, binds the socket to interface _index_.
Deleting the interface and creating a new one with the same name
leaves the socket bound to the old index. (Creating new sockets
always allocates a fresh index, they are not reused). We now
take this behaviour into account and keep up with changing indexes.
Add --conf-script configuration option.
Enhance --domain to accept, for instance,
--domain=net2.thekelleys.org.uk,eth2 so that hosts get a domain
which relects the interface they are attached to in a way which
doesn't require hard-coding addresses. Thanks to Sten Spans for
the idea.
Fix write-after-free error in DHCPv6 server code.
CVE-2022-0934 refers.
Add the ability to specify destination port in
DHCP-relay mode. This change also removes a previous bug
where --dhcp-alternate-port would affect the port used
to relay _to_ as well as the port being listened on.
The new feature allows configuration to provide bug-for-bug
compatibility, if required. Thanks to Damian Kaczkowski
for the feature suggestion.
Bound the value of UDP packet size in the EDNS0 header of
forwarded queries to the configured or default value of
edns-packet-max. There's no point letting a client set a larger
value if we're unable to return the answer. Thanks to Bertie
Taylor for pointing out the problem and supplying the patch.
Fix problem with the configuration
--server=/some.domain/# --address=/#/<ip> --server=<server_ip>
This would return <ip> for queries in some.domain, rather than
forwarding the query via the default server.
Tweak DHCPv6 relay code so that packets relayed towards a server
have source address on the server-facing network, not the
client facing network. Thanks to Luis Thomas for spotting this
and initial patch.
version 2.86
Handle DHCPREBIND requests in the DHCPv6 server code.
Thanks to Aichun Li for spotting this omission, and the initial

43
COPYING
View File

@@ -1,12 +1,12 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
@@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
@@ -225,7 +225,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
@@ -291,7 +291,7 @@ convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -303,17 +303,16 @@ the "copyright" line and a pointer to where the full notice is found.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
@@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names:
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -1,4 +1,4 @@
# dnsmasq is Copyright (c) 2000-2021 Simon Kelley
# dnsmasq is Copyright (c) 2000-2022 Simon Kelley
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -70,8 +70,9 @@ nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CO
HAVE_NETTLEHASH $(PKG_CONFIG) --libs nettle`
gmp_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --copy -lgmp`
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
nft_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_NFTSET $(PKG_CONFIG) --libs libnftables`
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
nft_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_NFTSET $(PKG_CONFIG) --cflags libnftables`
nft_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_NFTSET $(PKG_CONFIG) --libs libnftables`
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
sum?=$(shell $(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ')
sum!=$(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' '
@@ -91,7 +92,7 @@ hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
all : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags) $(nft_cflags)" \
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs) $(nft_libs)" \
-f $(top)/Makefile dnsmasq
@@ -116,7 +117,7 @@ all-i18n : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \
i18n=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags) $(nft_cflags)" \
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs) $(nft_libs)" \
-f $(top)/Makefile dnsmasq
for f in `cd $(PO); echo *.po`; do \

View File

@@ -1,6 +0,0 @@
This packaging is now unmaintained in the dnsmasq source: dnsmasq is
included in Suse proper, and up-to-date packages are now available
from
ftp://ftp.suse.com/pub/people/ug/

View File

@@ -1,27 +0,0 @@
This is a patch against SuSEfirewall2-3.1-206 (SuSE 9.x and older)
It fixes the dependency from the dns daemon name 'named'
After appending the patch, the SuSEfirewall is again able to autodetect
the dnsmasq named service.
This is a very old bug in the SuSEfirewall script.
The SuSE people think the name of the dns server will always 'named'
--- /sbin/SuSEfirewall2.orig 2004-01-23 13:30:09.000000000 +0100
+++ /sbin/SuSEfirewall2 2004-01-23 13:31:56.000000000 +0100
@@ -764,7 +764,7 @@
echo 'FW_ALLOW_INCOMING_HIGHPORTS_UDP should be set to yes, if you are running a DNS server!'
test "$FW_SERVICE_AUTODETECT" = yes -o "$FW_SERVICE_AUTODETECT" = dmz -o "$FW_SERVICE_AUTODETECT" = ext && {
- test "$FW_SERVICE_DNS" = no -a '!' "$START_NAMED" = no && check_srv named && {
+ test "$FW_SERVICE_DNS" = no -a '!' "$START_NAMED" = no && check_srv dnsmasq && {
echo -e 'Warning: detected activated named, enabling FW_SERVICE_DNS!
You still have to allow tcp/udp port 53 on internal, dmz and/or external.'
FW_SERVICE_DNS=$FW_SERVICE_AUTODETECT
@@ -878,7 +878,7 @@
test -e /etc/resolv.conf || echo "Warning: /etc/resolv.conf not found"
# Get ports/IP bindings of NAMED/SQUID
test "$FW_SERVICE_DNS" = yes -o "$FW_SERVICE_DNS" = dmz -o "$FW_SERVICE_DNS" = ext -o "$START_NAMED" = yes && DNS_PORT=`$LSOF -i -n -P | \
- $AWK -F: '/^named .* UDP / {print $2}'| $GREP -vw 53 | $SORT -un`
+ $AWK -F: '/^dnsmasq .* UDP / {print $2}'| $GREP -vw 53 | $SORT -un`
test "$FW_SERVICE_SQUID" = yes -o "$FW_SERVICE_SQUID" = dmz -o "$FW_SERVICE_SQUID" = ext -o "$START_SQUID" = yes && SQUID_PORT=`$LSOF -i -n -P | \
$AWK -F: '/^squid .* UDP/ {print $2}'| $SORT -un`

View File

@@ -1,23 +0,0 @@
--- man/dnsmasq.8 2004-08-08 20:57:56.000000000 +0200
+++ man/dnsmasq.8 2004-08-12 00:40:01.000000000 +0200
@@ -69,7 +69,7 @@
.TP
.B \-g, --group=<groupname>
Specify the group which dnsmasq will run
-as. The defaults to "dip", if available, to facilitate access to
+as. The defaults to "dialout", if available, to facilitate access to
/etc/ppp/resolv.conf which is not normally world readable.
.TP
.B \-v, --version
--- src/config.h 2004-08-11 11:39:18.000000000 +0200
+++ src/config.h 2004-08-12 00:40:01.000000000 +0200
@@ -44,7 +44,7 @@
#endif
#define DEFLEASE 3600 /* default lease time, 1 hour */
#define CHUSER "nobody"
-#define CHGRP "dip"
+#define CHGRP "dialout"
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68

View File

@@ -1,111 +0,0 @@
###############################################################################
#
# General
#
###############################################################################
Name: dnsmasq
Version: 2.33
Release: 1
Copyright: GPL
Group: Productivity/Networking/DNS/Servers
Vendor: Simon Kelley
Packager: Simon Kelley
URL: http://www.thekelleys.org.uk/dnsmasq
Provides: dns_daemon
Conflicts: bind bind8 bind9
PreReq: %fillup_prereq %insserv_prereq
Autoreqprov: on
Source0: %{name}-%{version}.tar.bz2
BuildRoot: /var/tmp/%{name}-%{version}
Summary: A lightweight caching nameserver
%description
Dnsmasq is lightweight, easy to configure DNS forwarder and DHCP server. It
is designed to provide DNS and, optionally, DHCP, to a small network. It can
serve the names of local machines which are not in the global DNS. The DHCP
server integrates with the DNS server and allows machines with DHCP-allocated
addresses to appear in the DNS with names configured either in each host or
in a central configuration file. Dnsmasq supports static and dynamic DHCP
leases and BOOTP for network booting of diskless machines.
###############################################################################
#
# Build
#
###############################################################################
%prep
%setup -q
patch -p0 <rpm/%{name}-SuSE.patch
%build
%{?suse_update_config:%{suse_update_config -f}}
make all-i18n DESTDIR=$RPM_BUILD_ROOT PREFIX=/usr
###############################################################################
#
# Install
#
###############################################################################
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p ${RPM_BUILD_ROOT}/etc/init.d
make install-i18n DESTDIR=$RPM_BUILD_ROOT PREFIX=/usr
install -o root -g root -m 755 rpm/rc.dnsmasq-suse $RPM_BUILD_ROOT/etc/init.d/dnsmasq
install -o root -g root -m 644 dnsmasq.conf.example $RPM_BUILD_ROOT/etc/dnsmasq.conf
strip $RPM_BUILD_ROOT/usr/sbin/dnsmasq
ln -sf ../../etc/init.d/dnsmasq $RPM_BUILD_ROOT/usr/sbin/rcdnsmasq
###############################################################################
#
# Clean up
#
###############################################################################
%clean
rm -rf $RPM_BUILD_ROOT
###############################################################################
#
# Post-install scriptlet
#
###############################################################################
%post
%{fillup_and_insserv dnsmasq}
###############################################################################
#
# Post-uninstall scriptlet
#
# The %postun script executes after the package has been removed. It is the
# last chance for a package to clean up after itself.
#
###############################################################################
%postun
%{insserv_cleanup}
###############################################################################
#
# File list
#
###############################################################################
%files
%defattr(-,root,root)
%doc CHANGELOG COPYING FAQ doc.html setup.html UPGRADING_to_2.0 rpm/README.susefirewall
%doc contrib
%config /etc/init.d/dnsmasq
%config /etc/dnsmasq.conf
/usr/sbin/rcdnsmasq
/usr/sbin/dnsmasq
/usr/share/locale/*/LC_MESSAGES/*
%doc %{_mandir}/man8/dnsmasq.8.gz
%doc %{_mandir}/*/man8/dnsmasq.8.gz

View File

@@ -1,79 +0,0 @@
#! /bin/sh
#
# init.d/dnsmasq
#
### BEGIN INIT INFO
# Provides: dnsmasq
# Required-Start: $network $remote_fs $syslog
# Required-Stop:
# Default-Start: 3 5
# Default-Stop:
# Description: Starts internet name service masq caching server (DNS)
### END INIT INFO
NAMED_BIN=/usr/sbin/dnsmasq
NAMED_PID=/var/run/dnsmasq.pid
NAMED_CONF=/etc/dnsmasq.conf
if [ ! -x $NAMED_BIN ] ; then
echo -n "dnsmasq not installed ! "
exit 5
fi
. /etc/rc.status
rc_reset
case "$1" in
start)
echo -n "Starting name service masq caching server "
checkproc -p $NAMED_PID $NAMED_BIN
if [ $? -eq 0 ] ; then
echo -n "- Warning: dnsmasq already running ! "
else
[ -e $NAMED_PID ] && echo -n "- Warning: $NAMED_PID exists ! "
fi
startproc -p $NAMED_PID $NAMED_BIN -u nobody
rc_status -v
;;
stop)
echo -n "Shutting name service masq caching server "
checkproc -p $NAMED_PID $NAMED_BIN
[ $? -ne 0 ] && echo -n "- Warning: dnsmasq not running ! "
killproc -p $NAMED_PID -TERM $NAMED_BIN
rc_status -v
;;
try-restart)
$0 stop && $0 start
rc_status
;;
restart)
$0 stop
$0 start
rc_status
;;
force-reload)
$0 reload
rc_status
;;
reload)
echo -n "Reloading name service masq caching server "
checkproc -p $NAMED_PID $NAMED_BIN
[ $? -ne 0 ] && echo -n "- Warning: dnsmasq not running ! "
killproc -p $NAMED_PID -HUP $NAMED_BIN
rc_status -v
;;
status)
echo -n "Checking for name service masq caching server "
checkproc -p $NAMED_PID $NAMED_BIN
rc_status -v
;;
probe)
test $NAMED_CONF -nt $NAMED_PID && echo reload
;;
*)
echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}"
exit 1
;;
esac
rc_exit

View File

@@ -48,6 +48,10 @@ SetBogusPrivOption
------------------
Takes boolean, sets or resets the --bogus-priv option.
SetLocaliseQueriesOption
------------------------
Takes boolean, sets or resets the --localise-queries option.
SetServers
----------
Returns nothing. Takes a set of arguments representing the new
@@ -248,6 +252,15 @@ GetMetrics
Returns an array with various metrics for DNS and DHCP.
GetServerMetrics
----------------
Returns per-DNS-server metrics.
ClearMetrics
------------
Clear call metric counters, global and per-server.
2. SIGNALS
----------

29
debian/changelog vendored
View File

@@ -1,8 +1,33 @@
dnsmasq (2.87-1) unstable; urgency=low
dnsmasq (2.89-1) unstable; urgency=low
* New upstream.
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 08 Sep 2021 23:11:25 +0000
-- Simon Kelley <simon@thekelleys.org.uk> Fri, 13 Jan 2023 21:57:01 +0000
dnsmasq (2.88-1) unstable; urgency=low
* New upstream.
* Fix loss of server configuration (closes: #1020830)
Git commit 930428fb970f4991e5c2933fd5a5d2504c18a551
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 2 Nov 2022 22:15:45 +0000
dnsmasq (2.87-1) unstable; urgency=low
* New upstream. (closes: #1001209, #1003156)
* Include new NFTset support in the build.
* Fix crash on netboot with DNS server disabled. (closes: #996332)
* Fix rare lockup in DNSSEC. (closes: #1001576)
* Close old bug. (closes: #902963)
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 25 Sep 2022 23:11:25 +0000
dnsmasq (2.86-1.1) unstable; urgency=medium
* Non-maintainer upload.
* Fix --address=/#/...... which was lost in 2.86. (closes: #995655)
-- Michael Biebl <biebl@debian.org> Wed, 10 Nov 2021 22:05:45 +0100
dnsmasq (2.86-1) unstable; urgency=low

2
debian/control vendored
View File

@@ -5,7 +5,7 @@ Build-depends: gettext, libnetfilter-conntrack-dev [linux-any],
libidn2-dev, libdbus-1-dev (>=0.61), libgmp-dev,
nettle-dev (>=2.4-3), libbsd-dev [kfreebsd-any],
liblua5.2-dev, dh-runit, debhelper-compat (= 10),
pkg-config
pkg-config, libnftables-dev
Maintainer: Simon Kelley <simon@thekelleys.org.uk>
Homepage: http://www.thekelleys.org.uk/dnsmasq/doc.html
Vcs-Git: http://thekelleys.org.uk/git/dnsmasq.git

1
debian/readme vendored
View File

@@ -60,6 +60,7 @@ Notes on configuring dnsmasq as packaged for Debian.
nodbus : omit DBus support.
noconntrack : omit connection tracking support.
noipset : omit IPset support.
nonftset : omit nftset support.
nortc : compile alternate mode suitable for systems without an RTC.
noi18n : omit translations and internationalisation support.
noidn : omit international domain name support, must be

4
debian/rules vendored
View File

@@ -52,6 +52,10 @@ ifeq (,$(filter noidn, $(DEB_BUILD_OPTIONS)))
DEB_COPTS += -DHAVE_LIBIDN2
endif
ifeq (,$(filter nonftset, $(DEB_BUILD_OPTIONS)))
DEB_COPTS += -DHAVE_NFTSET
endif
ifeq (,$(filter noconntrack,$(DEB_BUILD_OPTIONS)))
ifeq ($(DEB_HOST_ARCH_OS),linux)
DEB_COPTS += -DHAVE_CONNTRACK

View File

@@ -60,7 +60,8 @@ in alphabetical order.
.TP
.B --hostsdir=<path>
Read all the hosts files contained in the directory. New or changed files
are read automatically. See \fB--dhcp-hostsdir\fP for details.
are read automatically and modified and deleted files have removed records
automatically deleted.
.TP
.B \-E, --expand-hosts
Add the domain to simple names (without a period) in /etc/hosts
@@ -105,6 +106,16 @@ Dnsmasq limits the value of this option to one hour, unless recompiled.
.B --auth-ttl=<time>
Set the TTL value returned in answers from the authoritative server.
.TP
.B --fast-dns-retry=[<initial retry delay in ms>[,<time to continue retries in ms>]]
Under normal circumstances, dnsmasq relies on DNS clients to do retries; it
does not generate timeouts itself. Setting this option
instructs dnsmasq to generate its own retries starting after a delay
which defaults to 1000ms. If the second parameter is given this controls
how long the retries will continue for
otherwise this defaults to 10000ms. Retries are repeated with exponential
backoff. Using this option increases memory usage and
network bandwidth.
.TP
.B \-k, --keep-in-foreground
Do not go into the background at startup but otherwise run as
normal. This is intended for use when dnsmasq is run under daemontools
@@ -180,7 +191,15 @@ specific UDP port <query_port> instead of using random ports. NOTE
that using this option will make dnsmasq less secure against DNS
spoofing attacks but it may be faster and use less resources. Setting this option
to zero makes dnsmasq use a single port allocated to it by the
OS: this was the default behaviour in versions prior to 2.43.
OS: this was the default behaviour in versions prior to 2.43.
.TP
.B --port-limit=<#ports>
By default, when sending a query via random ports to multiple upstream servers or
retrying a query dnsmasq will use a single random port for all the tries/retries.
This option allows a larger number of ports to be used, which can increase robustness
in certain network configurations. Note that increasing this to more than
two or three can have security and resource implications and should only
be done with understanding of those.
.TP
.B --min-port=<port>
Do not use ports less than that given as source for outbound DNS
@@ -304,7 +323,8 @@ Return answers to DNS queries from /etc/hosts and \fB--interface-name\fP and \fB
received. If a name has more than one address associated with
it, and at least one of those addresses is on the same subnet as the
interface to which the query was sent, then return only the
address(es) on that subnet. This allows for a server to have multiple
address(es) on that subnet and return all the available addresses otherwise.
This allows for a server to have multiple
addresses in /etc/hosts corresponding to each of its interfaces, and
hosts will get the correct address based on which network they are
attached to. Currently this facility is limited to IPv4.
@@ -345,8 +365,15 @@ forged answer to a DNS request for certain domain, before the correct answer can
.B \-f, --filterwin2k
Later versions of windows make periodic DNS requests which don't get sensible answers from
the public DNS and can cause problems by triggering dial-on-demand links. This flag turns on an option
to filter such requests. The requests blocked are for records of types SOA and SRV, and type ANY where the
requested name has underscores, to catch LDAP requests.
to filter such requests. The requests blocked are for records of type ANY
where the requested name has underscores, to catch LDAP requests, and for
\fBall\fP records of types SOA and SRV.
.TP
.B --filter-A
Remove A records from answers. No IPv4 addresses will be returned.
.TP
.B --filter-AAAA
Remove AAAA records from answers. No IPv6 addresses will be returned.
.TP
.B \-r, --resolv-file=<file>
Read the IP addresses of the upstream nameservers from <file>, instead of
@@ -435,8 +462,8 @@ Tells dnsmasq to never forward A or AAAA queries for plain names, without dots
or domain parts, to upstream nameservers. If the name is not known
from /etc/hosts or DHCP then a "not found" answer is returned.
.TP
.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>]][@<interface>][@<source-ip>[#<port>]]
Specify IP address of upstream servers directly. Setting this flag does
.B \-S, --local, --server=[/[<domain>]/[domain/]][<server>[#<port>]][@<interface>][@<source-ip>[#<port>]]
Specify upstream servers directly. Setting this flag does
not suppress reading of /etc/resolv.conf, use \fB--no-resolv\fP to do that. If one or more
optional domains are given, that server is used only for those domains
and they are queried only using the specified server. This is
@@ -504,8 +531,14 @@ The query-port flag is ignored for any servers which have a
source address specified but the port may be specified directly as
part of the source address. Forcing queries to an interface is not
implemented on all platforms supported by dnsmasq.
Upstream servers may be specified with a hostname rather than an IP address.
In this case, dnsmasq will try to use the system resolver to get the IP address
of a server during startup. If name resolution fails, starting dnsmasq fails, too.
If the system's configuration is such that the system resolver sends DNS queries
through the dnsmasq instance which is starting up then this will time-out and fail.
.TP
.B --rev-server=<ip-address>[/<prefix-len>][,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
.B --rev-server=<ip-address>[/<prefix-len>][,<server>][#<port>][@<interface>][@<source-ip>[#<port>]]
This is functionally the same as
.B --server,
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
@@ -516,11 +549,9 @@ Allowed prefix lengths are 1-32 (IPv4) and 1-128 (IPv6). If the prefix length is
.TP
.B \-A, --address=/<domain>[/<domain>...]/[<ipaddr>]
Specify an IP address to return for any host in the given domains.
Queries in the domains are never forwarded and always replied to
A (or AAAA) queries in the domains are never forwarded and always replied to
with the specified IP address which may be IPv4 or IPv6. To give
both IPv4 and IPv6 addresses for a domain, use repeated \fB--address\fP flags.
To include multiple IP addresses for a single query, use
\fB--addn-hosts=<path>\fP instead.
multiple addresses or both IPv4 and IPv6 addresses for a domain, use repeated \fB--address\fP flags.
Note that /etc/hosts and DHCP leases override this for individual
names. A common use of this is to redirect the entire doubleclick.net
domain to some friendly local web server to avoid banner ads. The
@@ -538,6 +569,11 @@ address of 0.0.0.0 and its IPv6 equivalent of :: so
its subdomains. This is partly syntactic sugar for \fB--address=/example.com/0.0.0.0\fP
and \fB--address=/example.com/::\fP but is also more efficient than including both
as separate configuration lines. Note that NULL addresses normally work in the same way as localhost, so beware that clients looking up these names are likely to end up talking to themselves.
Note that the behaviour for queries which don't match the specified address literal changed in version 2.86.
Previous versions, configured with (eg) --address=/example.com/1.2.3.4 and then queried for a RR type other than
A would return a NoData answer. From 2.86, the query is sent upstream. To restore the pre-2.86 behaviour,
use the configuration --address=/example.com/1.2.3.4 --local=/example.com/
.TP
.B --ipset=/<domain>[/<domain>...]/<ipset>[,<ipset>...]
Places the resolved IP addresses of queries for one or more domains in
@@ -724,7 +760,7 @@ Specify the location of a pcap-format file which dnsmasq uses to dump copies of
.TP
.B --dumpmask=<mask>
Specify which types of packets should be added to the dumpfile. The argument should be the OR of the bitmasks for each type of packet to be dumped: it can be specified in hex by preceding the number with 0x in the normal way. Each time a packet is written to the dumpfile, dnsmasq logs the packet sequence and the mask
representing its type. The current types are: 0x0001 - DNS queries from clients 0x0002 DNS replies to clients 0x0004 - DNS queries to upstream 0x0008 - DNS replies from upstream 0x0010 - queries send upstream for DNSSEC validation 0x0020 - replies to queries for DNSSEC validation 0x0040 - replies to client queries which fail DNSSEC validation 0x0080 replies to queries for DNSSEC validation which fail validation.
representing its type. The current types are: 0x0001 - DNS queries from clients, 0x0002 DNS replies to clients, 0x0004 - DNS queries to upstream, 0x0008 - DNS replies from upstream, 0x0010 - queries send upstream for DNSSEC validation, 0x0020 - replies to queries for DNSSEC validation, 0x0040 - replies to client queries which fail DNSSEC validation, 0x0080 replies to queries for DNSSEC validation which fail validation, 0x1000 - DHCPv4, 0x2000 - DHCPv6, 0x4000 - Router advertisement, 0x8000 - TFTP.
.TP
.B --add-mac[=base64|text]
Add the MAC address of the requestor to DNS queries which are
@@ -737,6 +773,9 @@ have security and privacy implications. The warning about caching
given for \fB--add-subnet\fP applies to \fB--add-mac\fP too. An alternative encoding of the
MAC, as base64, is enabled by adding the "base64" parameter and a human-readable encoding of hex-and-colons is enabled by added the "text" parameter.
.TP
.B --strip-mac
Remove any MAC address information already in downstream queries before forwarding upstream.
.TP
.B --add-cpe-id=<string>
Add an arbitrary identifying string to DNS queries which are
forwarded upstream.
@@ -762,12 +801,18 @@ will add 1.2.3.0/24 for IPv4 requestors and ::/0 for IPv6 requestors.
.B --add-subnet=1.2.3.4/24,1.2.3.4/24
will add 1.2.3.0/24 for both IPv4 and IPv6 requestors.
.TP
.B --umbrella[=deviceid:<deviceid>[,orgid:<orgid>]]
.B --strip-subnet
Remove any subnet address already present in a downstream query before forwarding it upstream. If --add-subnet is set this also
ensures that any downstream-provided subnet is replaced by the one added by dnsmasq. Otherwise, dnsmasq will NOT replace an
existing subnet in the query.
.TP
.B --umbrella[=[deviceid:<deviceid>][,orgid:<orgid>][,assetid:<id>]]
Embeds the requestor's IP address in DNS queries forwarded upstream.
If device id or organization id are specified, the information is
If device id or, asset id or organization id are specified, the information is
included in the forwarded queries and may be able to be used in
filtering policies and reporting. The order of the deviceid and orgid
attributes is irrelevant, but must be separated by a comma.
filtering policies and reporting. The order of the id
attributes is irrelevant, but they must be separated by a comma. Deviceid is
a sixteen digit hexadecimal number, org and asset ids are decimal numbers.
.TP
.B \-c, --cache-size=<cachesize>
Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching. Note: huge cache size impacts performance.
@@ -777,6 +822,19 @@ Disable negative caching. Negative caching allows dnsmasq to remember
"no such domain" answers from upstream nameservers and answer
identical queries without forwarding them again.
.TP
.B --no-round-robin
Dnsmasq normally permutes the order of A or AAAA records for the same
name on successive queries, for load-balancing. This turns off that
behaviour, so that the records are always returned in the order
that they are received from upstream.
.TP
.B --use-stale-cache[=<max TTL excess in s>]
When set, if a DNS name exists in the cache, but its time-to-live has expired, dnsmasq will return the data anyway. (It attempts to refresh the
data with an upstream query after returning the stale data.) This can improve speed and reliability. It comes at the expense
of sometimes returning out-of-date data and less efficient cache utilisation, since old data cannot be flushed when its TTL expires, so the cache becomes
mostly least-recently-used. To mitigate issues caused by massively outdated DNS replies, the maximum overaging of cached records can be specified in seconds
(defaulting to not serve anything older than one day). Setting the TTL excess time to zero will serve stale cache data regardless how long it has expired.
.TP
.B \-0, --dns-forward-max=<queries>
Set the maximum number of concurrent DNS queries. The default value is
150, which should be fine for most setups. The only known situation
@@ -1109,7 +1167,8 @@ given in a
.B --dhcp-host
option, but aliases are possible by using CNAMEs. (See
.B --cname
).
). Note that /etc/hosts is NOT used when the DNS server side of dnsmasq
is disabled by setting the DNS server port to zero.
More than one
.B --dhcp-host
@@ -1322,7 +1381,7 @@ DHCP options. This make extra space available in the DHCP packet for
options but can, rarely, confuse old or broken clients. This flag
forces "simple and safe" behaviour to avoid problems in such a case.
.TP
.B --dhcp-relay=<local address>,<server address>[,<interface]
.B --dhcp-relay=<local address>[,<server address>[#<server port>]][,<interface]
Configure dnsmasq to do DHCP relay. The local address is an address
allocated to an interface on the host running dnsmasq. All DHCP
requests arriving on that interface will we relayed to a remote DHCP
@@ -1330,10 +1389,12 @@ server at the server address. It is possible to relay from a single local
address to multiple remote servers by using multiple \fB--dhcp-relay\fP
configs with the same local address and different server
addresses. A server address must be an IP literal address, not a
domain name. In the case of DHCPv6, the server address may be the
ALL_SERVERS multicast address, ff05::1:3. In this case the interface
must be given, not be wildcard, and is used to direct the multicast to the
correct interface to reach the DHCP server.
domain name. If the server address is omitted, the request will be
forwarded by broadcast (IPv4) or multicast (IPv6). In this case the interface
must be given and not be wildcard. The server address may specify a non-standard
port to relay to. If this is used then \fB--dhcp-proxy\fP should likely also be set,
otherwise parts of the DHCP conversation which do not pass through the relay
will be delivered to the wrong port.
Access control for DHCP clients has the same rules as for the DHCP
server, see \fB--interface\fP, \fB--except-interface\fP, etc. The optional
@@ -1353,6 +1414,11 @@ supported: the relay function will take precedence.
Both DHCPv4 and DHCPv6 relay is supported. It's not possible to relay
DHCPv4 to a DHCPv6 server or vice-versa.
The DHCP relay function for IPv6 includes the ability to snoop
prefix-delegation from relayed DHCP transactions. See
.B --dhcp-script
for details.
.TP
.B \-U, --dhcp-vendorclass=set:<tag>,[enterprise:<IANA-enterprise number>,]<vendor-class>
Map from a vendor-class string to a tag. Most DHCP clients provide a
@@ -1688,7 +1754,13 @@ If dnsmasq was compiled with HAVE_BROKEN_RTC, then
the length of the lease (in seconds) is stored in
DNSMASQ_LEASE_LENGTH, otherwise the time of lease expiry is stored in
DNSMASQ_LEASE_EXPIRES. The number of seconds until lease expiry is
always stored in DNSMASQ_TIME_REMAINING.
always stored in DNSMASQ_TIME_REMAINING.
DNSMASQ_DATA_MISSING is set to "1" during "old" events for existing
leases generated at startup to indicate that data not stored in the
persistent lease database will not be present. This comprises everything
other than IP address, hostname, MAC address, DUID, IAID and lease length
or expiry time.
If a lease used to have a hostname, which is
removed, an "old" event is generated with the new state of the lease,
@@ -1710,6 +1782,11 @@ DNSMASQ_LOG_DHCP is set if
.B --log-dhcp
is in effect.
DNSMASQ_REQUESTED_OPTIONS a string containing the decimal values in the Parameter Request List option, comma separated, if the parameter request list option is provided by the client.
DNSMASQ_MUD_URL the Manufacturer Usage Description URL if provided by the client. (See RFC8520 for details.)
For IPv4 only:
DNSMASQ_CLIENT_ID if the host provided a client-id.
@@ -1719,8 +1796,6 @@ DHCP relay-agent added any of these options.
If the client provides vendor-class, DNSMASQ_VENDOR_CLASS.
DNSMASQ_REQUESTED_OPTIONS a string containing the decimal values in the Parameter Request List option, comma separated, if the parameter request list option is provided by the client.
For IPv6 only:
If the client provides vendor-class, DNSMASQ_VENDOR_CLASS_ID,
@@ -1763,15 +1838,25 @@ receives a HUP signal, the script will be invoked for existing leases
with an "old" event.
There are four further actions which may appear as the first argument
to the script, "init", "arp-add", "arp-del" and "tftp". More may be added in the future, so
There are five further actions which may appear as the first argument
to the script, "init", "arp-add", "arp-del", "relay-snoop" and "tftp".
More may be added in the future, so
scripts should be written to ignore unknown actions. "init" is
described below in
.B --leasefile-ro
The "tftp" action is invoked when a TFTP file transfer completes: the
arguments are the file size in bytes, the address to which the file
was sent, and the complete pathname of the file.
The "relay-snoop" action is invoked when dnsmasq is configured as a DHCP
relay for DHCPv6 and it relays a prefx delegation to a client. The arguments
are the name of the interface where the client is conected, its (link-local)
address on that interface and the delegated prefix. This information is
sufficient to install routes to the delegated prefix of a router. See
.B --dhcp-relay
for more details on configuring DHCP relay.
The "arp-add" and "arp-del" actions are only called if enabled with
.B --script-arp
They are are supplied with a MAC address and IP address as arguments. "arp-add" indicates
@@ -1902,7 +1987,7 @@ is the address of the relay and the second, as before, specifies an extra subnet
addresses may be allocated from.
.TP
.B \-s, --domain=<domain>[,<address range>[,local]]
.B \-s, --domain=<domain>[[,<address range>[,local]]|<interface>]
Specifies DNS domains for the DHCP server. Domains may be be given
unconditionally (without the IP range) or for limited IP ranges. This has two effects;
firstly it causes the DHCP server to return the domain to any hosts
@@ -1936,6 +2021,11 @@ additional flag "local" may be supplied which has the effect of adding
is identical to
.B --domain=thekelleys.org.uk,192.168.0.0/24
.B --local=/thekelleys.org.uk/ --local=/0.168.192.in-addr.arpa/
The address range can also be given as a network interface name, in which case
all of the subnets currently assigned to the interface are used in matching the
address. This allows hosts on different physical subnets to be given different
domains in a way which updates automatically as the interface addresses change.
.TP
.B --dhcp-fqdn
In the default mode, dnsmasq inserts the unqualified names of
@@ -2108,6 +2198,37 @@ A special case of
which differs in two respects. Firstly, only \fB--server\fP and \fB--rev-server\fP are allowed
in the configuration file included. Secondly, the file is re-read and the configuration
therein is updated when dnsmasq receives SIGHUP.
.TP
.B \--conf-script=<file>[ <arg]
Execute <file>, and treat what it emits to stdout as the contents of a configuration file.
If the script exits with a non-zero exit code, dnsmasq treats this as a fatal error.
The script can be passed arguments, space seperated from the filename and each other so, for instance
.B --conf-dir="/etc/dnsmasq-uncompress-ads /share/ads-domains.gz"
with /etc/dnsmasq-uncompress-ads containing
set -e
zcat ${1} | sed -e "s:^:address=/:" -e "s:$:/:"
exit 0
and /share/ads-domains.gz containing a compressed
list of ad server domains will save disk space with large ad-server blocklists.
.TP
.B --no-ident
Do not respond to class CHAOS and type TXT in domain bind queries.
Without this option being set, the cache statistics are also available in the
DNS as answers to queries of class CHAOS and type TXT in domain bind. The domain
names are cachesize.bind, insertions.bind, evictions.bind, misses.bind,
hits.bind, auth.bind and servers.bind unless disabled at compile-time. An
example command to query this, using the
.B dig
utility would be
dig +short chaos txt cachesize.bind
.SH CONFIG FILE
At startup, dnsmasq reads
.I /etc/dnsmasq.conf,
@@ -2157,15 +2278,6 @@ resulted in an error. In
mode or when full logging is enabled (\fB--log-queries\fP), a complete dump of the
contents of the cache is made.
The cache statistics are also available in the DNS as answers to
queries of class CHAOS and type TXT in domain bind. The domain names are cachesize.bind, insertions.bind, evictions.bind,
misses.bind, hits.bind, auth.bind and servers.bind. An example command to query this, using the
.B dig
utility would be
dig +short chaos txt cachesize.bind
.PP
When it receives SIGUSR2 and it is logging direct to a file (see
.B --log-facility
)
@@ -2504,7 +2616,9 @@ file/directory, permissions).
5 - Other miscellaneous problem.
.PP
11 or greater - a non zero return code was received from the
lease-script process "init" call. The exit code from dnsmasq is the
lease-script process "init" call or a
.B \--conf-script
file. The exit code from dnsmasq is the
script's exit code with 10 added.
.SH LIMITS

1345
po/de.po

File diff suppressed because it is too large Load Diff

1237
po/es.po

File diff suppressed because it is too large Load Diff

1527
po/fi.po

File diff suppressed because it is too large Load Diff

1238
po/fr.po

File diff suppressed because it is too large Load Diff

1251
po/id.po

File diff suppressed because it is too large Load Diff

1527
po/it.po

File diff suppressed because it is too large Load Diff

2746
po/ka.po Normal file

File diff suppressed because it is too large Load Diff

1233
po/no.po

File diff suppressed because it is too large Load Diff

1237
po/pl.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1233
po/ro.po

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@ static int bignames_left, hash_size;
static void make_non_terminals(struct crec *source);
static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class,
time_t now, unsigned long ttl, unsigned int flags);
static void dump_cache_entry(struct crec *cache, time_t now);
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
/* taken from https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml */
@@ -189,7 +190,7 @@ static void rehash(int size)
else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *))))
return;
for(i = 0; i < new_size; i++)
for (i = 0; i < new_size; i++)
new[i] = NULL;
old = hash_table;
@@ -233,17 +234,27 @@ static void cache_hash(struct crec *crecp)
immortal entries are at the end of the hash-chain.
This allows reverse searches and garbage collection to be optimised */
struct crec **up = hash_bucket(cache_get_name(crecp));
if (!(crecp->flags & F_REVERSE))
char *name = cache_get_name(crecp);
struct crec **up = hash_bucket(name);
unsigned int flags = crecp->flags & (F_IMMORTAL | F_REVERSE);
if (!(flags & F_REVERSE))
{
while (*up && ((*up)->flags & F_REVERSE))
up = &((*up)->hash_next);
if (crecp->flags & F_IMMORTAL)
if (flags & F_IMMORTAL)
while (*up && !((*up)->flags & F_IMMORTAL))
up = &((*up)->hash_next);
}
/* Preserve order when inserting the same name multiple times.
Do not mess up the flag invariants. */
while (*up &&
hostname_isequal(cache_get_name(*up), name) &&
flags == ((*up)->flags & (F_IMMORTAL | F_REVERSE)))
up = &((*up)->hash_next);
crecp->hash_next = *up;
*up = crecp;
}
@@ -374,6 +385,19 @@ static int is_outdated_cname_pointer(struct crec *crecp)
static int is_expired(time_t now, struct crec *crecp)
{
/* Don't dump expired entries if they are within the accepted timeout range.
The cache becomes approx. LRU. Never use expired DS or DNSKEY entries.
Possible values for daemon->cache_max_expiry:
-1 == serve cached content regardless how long ago it expired
0 == the option is disabled, expired content isn't served
<n> == serve cached content only if it expire less than <n> seconds
ago (where n is a positive integer) */
if (daemon->cache_max_expiry != 0 &&
(daemon->cache_max_expiry == -1 ||
difftime(now, crecp->ttd) < daemon->cache_max_expiry) &&
!(crecp->flags & (F_DS | F_DNSKEY)))
return 0;
if (crecp->flags & F_IMMORTAL)
return 0;
@@ -383,6 +407,27 @@ static int is_expired(time_t now, struct crec *crecp)
return 1;
}
/* Remove entries with a given UID from the cache */
unsigned int cache_remove_uid(const unsigned int uid)
{
int i;
unsigned int removed = 0;
struct crec *crecp, **up;
for (i = 0; i < hash_size; i++)
for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid)
{
*up = crecp->hash_next;
free(crecp);
removed++;
}
else
up = &crecp->hash_next;
return removed;
}
static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned short class, time_t now,
unsigned int flags, struct crec **target_crec, unsigned int *target_uid)
{
@@ -413,7 +458,7 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s
if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
{
/* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV)) ||
if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV | F_NXDOMAIN)) ||
(((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
{
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
@@ -553,8 +598,8 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
{
struct crec *new, *target_crec = NULL;
union bigname *big_name = NULL;
int freed_all = flags & F_REVERSE;
int free_avail = 0;
int freed_all = (flags & F_REVERSE);
struct crec *free_avail = NULL;
unsigned int target_uid;
/* if previous insertion failed give up now. */
@@ -602,7 +647,7 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
/* Free entry at end of LRU list, use it. */
if (!(new->flags & (F_FORWARD | F_REVERSE)))
break;
break;
/* End of LRU list is still in use: if we didn't scan all the hash
chains for expired entries do that now. If we already tried that
@@ -614,12 +659,9 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
insert. Once in this state, all inserts will probably fail. */
if (free_avail)
{
static int warned = 0;
if (!warned)
{
my_syslog(LOG_ERR, _("Internal error in cache."));
warned = 1;
}
my_syslog(LOG_ERR, _("Internal error in cache."));
/* Log the entry we tried to delete. */
dump_cache_entry(free_avail, now);
insert_error = 1;
return NULL;
}
@@ -627,9 +669,13 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
if (freed_all)
{
/* For DNSSEC records, uid holds class. */
free_avail = 1; /* Must be free space now. */
cache_scan_free(cache_get_name(new), &new->addr, new->uid, now, new->flags, NULL, NULL);
daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]++;
free_avail = new; /* Must be free space now. */
/* condition valid when stale-caching */
if (difftime(now, new->ttd) < 0)
daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]++;
cache_scan_free(cache_get_name(new), &new->addr, new->uid, now, new->flags, NULL, NULL);
}
else
{
@@ -692,7 +738,7 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
new->ttd = now + (time_t)ttl;
new->next = new_chain;
new_chain = new;
return new;
}
@@ -870,7 +916,7 @@ int cache_find_non_terminal(char *name, time_t now)
struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned int prot)
{
struct crec *ans;
int no_rr = prot & F_NO_RR;
int no_rr = (prot & F_NO_RR) || option_bool(OPT_NORR);
prot &= ~F_NO_RR;
@@ -1018,16 +1064,17 @@ struct crec *cache_find_by_addr(struct crec *crecp, union all_addr *addr,
static void add_hosts_entry(struct crec *cache, union all_addr *addr, int addrlen,
unsigned int index, struct crec **rhash, int hashsz)
{
struct crec *lookup = cache_find_by_name(NULL, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6));
int i;
unsigned int j;
struct crec *lookup = NULL;
/* Remove duplicates in hosts files. */
if (lookup && (lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
{
free(cache);
return;
}
while ((lookup = cache_find_by_name(lookup, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6))))
if ((lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
{
free(cache);
return;
}
/* Ensure there is only one address -> name mapping (first one trumps)
We do this by steam here, The entries are kept in hash chains, linked
@@ -1035,7 +1082,6 @@ static void add_hosts_entry(struct crec *cache, union all_addr *addr, int addrle
the array rhash, hashed on address. Note that rhash and the values
in ->next are only valid whilst reading hosts files: the buckets are
then freed, and the ->next pointer used for other things.
Only insert each unique address once into this hashing structure.
This complexity avoids O(n^2) divergent CPU use whilst reading
@@ -1132,7 +1178,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
{
FILE *f = fopen(filename, "r");
char *token = daemon->namebuff, *domain_suffix = NULL;
int addr_count = 0, name_count = cache_size, lineno = 1;
int names_done = 0, name_count = cache_size, lineno = 1;
unsigned int flags = 0;
union all_addr addr;
int atnl, addrlen = 0;
@@ -1168,8 +1214,6 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
continue;
}
addr_count++;
/* rehash every 1000 names. */
if (rhash && ((name_count - cache_size) > 1000))
{
@@ -1201,6 +1245,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
cache->ttd = daemon->local_ttl;
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
names_done++;
}
if ((cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 1)))
{
@@ -1209,6 +1254,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
cache->ttd = daemon->local_ttl;
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
names_done++;
}
free(canon);
@@ -1225,7 +1271,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
if (rhash)
rehash(name_count);
my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
my_syslog(LOG_INFO, _("read %s - %d names"), filename, names_done);
return name_count;
}
@@ -1399,10 +1445,12 @@ struct in_addr a_record_from_hosts(char *name, time_t now)
struct crec *crecp = NULL;
struct in_addr ret;
while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
if (crecp->flags & F_HOSTS)
return crecp->addr.addr4;
/* If no DNS service, cache not initialised. */
if (daemon->port != 0)
while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
if (crecp->flags & F_HOSTS)
return crecp->addr.addr4;
my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);
ret.s_addr = 0;
@@ -1674,10 +1722,8 @@ int cache_make_stat(struct txt_record *t)
{
/* expand buffer if necessary */
newlen = bytes_needed + 1 + bufflen - bytes_avail;
if (!(new = whine_malloc(newlen)))
if (!(new = whine_realloc(buff, newlen)))
return 0;
memcpy(new, buff, bufflen);
free(buff);
p = new + (p - buff);
lenp = p - 1;
buff = new;
@@ -1715,6 +1761,94 @@ static char *sanitise(char *name)
return name;
}
static void dump_cache_entry(struct crec *cache, time_t now)
{
(void)now;
static char *buff = NULL;
char *p, *t = " ";
char *a = daemon->addrbuff, *n = cache_get_name(cache);
/* String length is limited below */
if (!buff && !(buff = whine_malloc(150)))
return;
p = buff;
*a = 0;
if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
n = "<Root>";
p += sprintf(p, "%-30.30s ", sanitise(n));
if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
a = sanitise(cache_get_cname_target(cache));
else if ((cache->flags & F_SRV) && !(cache->flags & F_NEG))
{
int targetlen = cache->addr.srv.targetlen;
ssize_t len = sprintf(a, "%u %u %u ", cache->addr.srv.priority,
cache->addr.srv.weight, cache->addr.srv.srvport);
if (targetlen > (40 - len))
targetlen = 40 - len;
blockdata_retrieve(cache->addr.srv.target, targetlen, a + len);
a[len + targetlen] = 0;
}
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
{
if (!(cache->flags & F_NEG))
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
}
else if (cache->flags & F_DNSKEY)
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
cache->addr.key.algo, cache->addr.key.flags);
#endif
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
{
a = daemon->addrbuff;
if (cache->flags & F_IPV4)
inet_ntop(AF_INET, &cache->addr, a, ADDRSTRLEN);
else if (cache->flags & F_IPV6)
inet_ntop(AF_INET6, &cache->addr, a, ADDRSTRLEN);
}
if (cache->flags & F_IPV4)
t = "4";
else if (cache->flags & F_IPV6)
t = "6";
else if (cache->flags & F_CNAME)
t = "C";
else if (cache->flags & F_SRV)
t = "V";
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
t = "S";
else if (cache->flags & F_DNSKEY)
t = "K";
#endif
else if (!(cache->flags & F_NXDOMAIN)) /* non-terminal */
t = "!";
p += sprintf(p, "%-40.40s %s%s%s%s%s%s%s%s%s%s ", a, t,
cache->flags & F_FORWARD ? "F" : " ",
cache->flags & F_REVERSE ? "R" : " ",
cache->flags & F_IMMORTAL ? "I" : " ",
cache->flags & F_DHCP ? "D" : " ",
cache->flags & F_NEG ? "N" : " ",
cache->flags & F_NXDOMAIN ? "X" : " ",
cache->flags & F_HOSTS ? "H" : " ",
cache->flags & F_CONFIG ? "C" : " ",
cache->flags & F_DNSSECOK ? "V" : " ");
#ifdef HAVE_BROKEN_RTC
p += sprintf(p, "%-24lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
#else
p += sprintf(p, "%-24.24s", cache->flags & F_IMMORTAL ? "" : ctime(&(cache->ttd)));
#endif
if(cache->flags & (F_HOSTS | F_CONFIG) && cache->uid > 0)
p += sprintf(p, " %-40.40s", record_source(cache->uid));
my_syslog(LOG_INFO, "%s", buff);
}
void dump_cache(time_t now)
{
@@ -1725,6 +1859,8 @@ void dump_cache(time_t now)
daemon->cachesize, daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED], daemon->metrics[METRIC_DNS_CACHE_INSERTED]);
my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED], daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]);
if (daemon->cache_max_expiry != 0)
my_syslog(LOG_INFO, _("queries answered from stale cache %u"), daemon->metrics[METRIC_DNS_STALE_ANSWERED]);
#ifdef HAVE_AUTH
my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
#endif
@@ -1739,105 +1875,45 @@ void dump_cache(time_t now)
if (!(serv->flags & SERV_MARK))
{
int port;
unsigned int queries = 0, failed_queries = 0;
unsigned int queries = 0, failed_queries = 0, nxdomain_replies = 0, retrys = 0;
unsigned int sigma_latency = 0, count_latency = 0;
for (serv1 = serv; serv1; serv1 = serv1->next)
if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
{
serv1->flags |= SERV_MARK;
queries += serv1->queries;
failed_queries += serv1->failed_queries;
nxdomain_replies += serv1->nxdomain_replies;
retrys += serv1->retrys;
sigma_latency += serv1->query_latency;
count_latency++;
}
port = prettyprint_addr(&serv->addr, daemon->addrbuff);
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums"),
daemon->addrbuff, port, queries, retrys, failed_queries, nxdomain_replies, sigma_latency/count_latency);
}
if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
{
struct crec *cache ;
struct crec *cache;
int i;
my_syslog(LOG_INFO, "Host Address Flags Expires");
my_syslog(LOG_INFO, "Host Address Flags Expires Source");
my_syslog(LOG_INFO, "------------------------------ ---------------------------------------- ---------- ------------------------ ------------");
for (i=0; i<hash_size; i++)
for (cache = hash_table[i]; cache; cache = cache->hash_next)
{
char *t = " ";
char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache);
*a = 0;
if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
n = "<Root>";
p += sprintf(p, "%-30.30s ", sanitise(n));
if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
a = sanitise(cache_get_cname_target(cache));
else if ((cache->flags & F_SRV) && !(cache->flags & F_NEG))
{
int targetlen = cache->addr.srv.targetlen;
ssize_t len = sprintf(a, "%u %u %u ", cache->addr.srv.priority,
cache->addr.srv.weight, cache->addr.srv.srvport);
if (targetlen > (40 - len))
targetlen = 40 - len;
blockdata_retrieve(cache->addr.srv.target, targetlen, a + len);
a[len + targetlen] = 0;
}
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
{
if (!(cache->flags & F_NEG))
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
}
else if (cache->flags & F_DNSKEY)
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
cache->addr.key.algo, cache->addr.key.flags);
#endif
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
{
a = daemon->addrbuff;
if (cache->flags & F_IPV4)
inet_ntop(AF_INET, &cache->addr, a, ADDRSTRLEN);
else if (cache->flags & F_IPV6)
inet_ntop(AF_INET6, &cache->addr, a, ADDRSTRLEN);
}
if (cache->flags & F_IPV4)
t = "4";
else if (cache->flags & F_IPV6)
t = "6";
else if (cache->flags & F_CNAME)
t = "C";
else if (cache->flags & F_SRV)
t = "V";
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
t = "S";
else if (cache->flags & F_DNSKEY)
t = "K";
#endif
p += sprintf(p, "%-40.40s %s%s%s%s%s%s%s%s%s ", a, t,
cache->flags & F_FORWARD ? "F" : " ",
cache->flags & F_REVERSE ? "R" : " ",
cache->flags & F_IMMORTAL ? "I" : " ",
cache->flags & F_DHCP ? "D" : " ",
cache->flags & F_NEG ? "N" : " ",
cache->flags & F_NXDOMAIN ? "X" : " ",
cache->flags & F_HOSTS ? "H" : " ",
cache->flags & F_DNSSECOK ? "V" : " ");
#ifdef HAVE_BROKEN_RTC
p += sprintf(p, "%lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
#else
p += sprintf(p, "%s", cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd)));
/* ctime includes trailing \n - eat it */
*(p-1) = 0;
#endif
my_syslog(LOG_INFO, "%s", daemon->namebuff);
}
dump_cache_entry(cache, now);
}
}
char *record_source(unsigned int index)
{
struct hostsfile *ah;
#ifdef HAVE_INOTIFY
struct dyndir *dd;
#endif
if (index == SRC_CONFIG)
return "config";
else if (index == SRC_HOSTS)
@@ -1848,9 +1924,11 @@ char *record_source(unsigned int index)
return ah->fname;
#ifdef HAVE_INOTIFY
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
if (ah->index == index)
return ah->fname;
/* Dynamic directories contain multiple files */
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
for (ah = dd->files; ah; ah = ah->next)
if (ah->index == index)
return ah->fname;
#endif
return "<unknown>";
@@ -1949,12 +2027,13 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
char *source, *dest = arg;
char *verb = "is";
char *extra = "";
char portstring[7]; /* space for #<portnum> */
if (!option_bool(OPT_LOG))
return;
/* build query type string if requested */
if(type > 0)
if (!(flags & (F_SERVER | F_IPSET)) && type > 0)
arg = querystr(arg, type);
#ifdef HAVE_DNSSEC
@@ -1990,8 +2069,15 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
}
}
else if (flags & (F_IPV4 | F_IPV6))
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, daemon->addrbuff, ADDRSTRLEN);
{
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, daemon->addrbuff, ADDRSTRLEN);
if ((flags & F_SERVER) && type != NAMESERVER_PORT)
{
extra = portstring;
sprintf(portstring, "#%u", type);
}
}
else
dest = arg;
}
@@ -2043,7 +2129,12 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
}
else if (flags & F_AUTH)
source = "auth";
else if (flags & F_SERVER)
else if (flags & F_DNSSEC)
{
source = arg;
verb = "to";
}
else if (flags & F_SERVER)
{
source = "forwarded";
verb = "to";
@@ -2053,11 +2144,6 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
source = arg;
verb = "from";
}
else if (flags & F_DNSSEC)
{
source = arg;
verb = "to";
}
else if (flags & F_IPSET)
{
source = type ? "ipset add" : "nftset add";
@@ -2065,6 +2151,8 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
name = arg;
verb = daemon->addrbuff;
}
else if (flags & F_STALE)
source = "cached-stale";
else
source = "cached";

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,10 +20,11 @@
#define TCP_MAX_QUERIES 100 /* Maximum number of queries per incoming TCP connection */
#define TCP_BACKLOG 32 /* kernel backlog limit for TCP connections */
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
#define SAFE_PKTSZ 1280 /* "go anywhere" UDP packet size */
#define SAFE_PKTSZ 1232 /* "go anywhere" UDP packet size, see https://dnsflagday.net/2020/ */
#define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */
#define DNSSEC_WORK 50 /* Max number of queries to validate one question */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define SMALL_PORT_RANGE 30 /* If DNS port range is smaller than this, use different allocation. */
#define FORWARD_TEST 50 /* try all servers every 50 queries */
#define FORWARD_TIME 20 /* or 20 seconds */
#define UDP_TEST_TIME 60 /* How often to reset our idea of max packet size. */
@@ -58,6 +59,8 @@
#define SOA_EXPIRY 1209600 /* SOA expiry default */
#define LOOP_TEST_DOMAIN "test" /* domain for loop testing, "test" is reserved by RFC 2606 and won't therefore clash */
#define LOOP_TEST_TYPE T_TXT
#define DEFAULT_FAST_RETRY 1000 /* ms, default delay before fast retry */
#define STALE_CACHE_EXPIRY 86400 /* 1 day in secs, default maximum expiry time for stale cache data */
/* compile-time options: uncomment below to enable or do eg.
make COPTS=-DHAVE_BROKEN_RTC

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -309,14 +309,14 @@ static int dnsmasq_gostdsa_verify(struct blockdata *key_data, unsigned int key_l
mpz_init(y);
}
mpz_import(x, 32 , 1, 1, 0, 0, p);
mpz_import(y, 32 , 1, 1, 0, 0, p + 32);
mpz_import(x, 32, -1, 1, 0, 0, p);
mpz_import(y, 32, -1, 1, 0, 0, p + 32);
if (!ecc_point_set(gost_key, x, y))
return 0;
return 0;
mpz_import(sig_struct->r, 32, 1, 1, 0, 0, sig);
mpz_import(sig_struct->s, 32, 1, 1, 0, 0, sig + 32);
mpz_import(sig_struct->s, 32, 1, 1, 0, 0, sig);
mpz_import(sig_struct->r, 32, 1, 1, 0, 0, sig + 32);
return nettle_gostdsa_verify(gost_key, digest_len, digest, sig_struct);
}
@@ -390,7 +390,12 @@ static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key
return dnsmasq_ecdsa_verify;
#if MIN_VERSION(3, 1)
case 15: case 16:
case 15:
return dnsmasq_eddsa_verify;
#endif
#if MIN_VERSION(3, 6)
case 16:
return dnsmasq_eddsa_verify;
#endif
}
@@ -425,7 +430,9 @@ char *ds_digest_name(int digest)
{
case 1: return "sha1";
case 2: return "sha256";
case 3: return "gosthash94";
#if MIN_VERSION(3, 6)
case 3: return "gosthash94cp";
#endif
case 4: return "sha384";
default: return NULL;
}
@@ -444,11 +451,17 @@ char *algo_digest_name(int algo)
case 7: return "sha1"; /* RSASHA1-NSEC3-SHA1 */
case 8: return "sha256"; /* RSA/SHA-256 */
case 10: return "sha512"; /* RSA/SHA-512 */
case 12: return "gosthash94"; /* ECC-GOST */
#if MIN_VERSION(3, 6)
case 12: return "gosthash94cp"; /* ECC-GOST */
#endif
case 13: return "sha256"; /* ECDSAP256SHA256 */
case 14: return "sha384"; /* ECDSAP384SHA384 */
#if MIN_VERSION(3, 1)
case 15: return "null_hash"; /* ED25519 */
# if MIN_VERSION(3, 6)
case 16: return "null_hash"; /* ED448 */
# endif
#endif
default: return NULL;
}
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -52,6 +52,9 @@ const char* introspection_xml_template =
" <method name=\"SetFilterWin2KOption\">\n"
" <arg name=\"filterwin2k\" direction=\"in\" type=\"b\"/>\n"
" </method>\n"
" <method name=\"SetLocaliseQueriesOption\">\n"
" <arg name=\"localise-queries\" direction=\"in\" type=\"b\"/>\n"
" </method>\n"
" <method name=\"SetBogusPrivOption\">\n"
" <arg name=\"boguspriv\" direction=\"in\" type=\"b\"/>\n"
" </method>\n"
@@ -88,6 +91,11 @@ const char* introspection_xml_template =
" <method name=\"GetMetrics\">\n"
" <arg name=\"metrics\" direction=\"out\" type=\"a{su}\"/>\n"
" </method>\n"
" <method name=\"GetServerMetrics\">\n"
" <arg name=\"metrics\" direction=\"out\" type=\"a{ss}\"/>\n"
" </method>\n"
" <method name=\"ClearMetrics\">\n"
" </method>\n"
" </interface>\n"
"</node>\n";
@@ -284,6 +292,11 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
u16 flags = 0;
char interface[IF_NAMESIZE];
char *str_addr, *str_domain = NULL;
struct server_details sdetails = { 0 };
sdetails.addr = &addr;
sdetails.source_addr = &source_addr;
sdetails.interface = interface;
sdetails.flags = &flags;
if (strings)
{
@@ -366,20 +379,6 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
strcpy(str_addr, str);
}
/* parse the IP address */
if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
/* 0.0.0.0 for server address == NULL, for Dbus */
if (addr.in.sin_family == AF_INET &&
addr.in.sin_addr.s_addr == 0)
flags |= SERV_LITERAL_ADDRESS;
if (strings)
{
char *p;
@@ -393,7 +392,31 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
else
p = NULL;
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
if (strings && strlen(str_addr) == 0)
add_update_server(SERV_LITERAL_ADDRESS | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
else
{
if ((addr_err = parse_server(str_addr, &sdetails)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
while (parse_server_next(&sdetails))
{
if ((addr_err = parse_server_addr(&sdetails)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
}
}
} while ((str_domain = p));
}
else
@@ -407,11 +430,40 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
if (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING)
dbus_message_iter_get_basic(&string_iter, &str);
dbus_message_iter_next (&string_iter);
if ((addr_err = parse_server(str_addr, &sdetails)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
while (parse_server_next(&sdetails))
{
if ((addr_err = parse_server_addr(&sdetails)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
/* 0.0.0.0 for server address == NULL, for Dbus */
if (addr.in.sin_family == AF_INET &&
addr.in.sin_addr.s_addr == 0)
flags |= SERV_LITERAL_ADDRESS;
else
flags &= ~SERV_LITERAL_ADDRESS;
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
}
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
}
if (sdetails.orig_hostinfo)
freeaddrinfo(sdetails.orig_hostinfo);
/* jump to next element in outer array */
dbus_message_iter_next(&array_iter);
}
@@ -641,6 +693,77 @@ static DBusMessage *dbus_get_metrics(DBusMessage* message)
return reply;
}
static void add_dict_entry(DBusMessageIter *container, const char *key, const char *val)
{
DBusMessageIter dict;
dbus_message_iter_open_container(container, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &key);
dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &val);
dbus_message_iter_close_container(container, &dict);
}
static void add_dict_int(DBusMessageIter *container, const char *key, const unsigned int val)
{
snprintf(daemon->namebuff, MAXDNAME, "%u", val);
add_dict_entry(container, key, daemon->namebuff);
}
static DBusMessage *dbus_get_server_metrics(DBusMessage* message)
{
DBusMessage *reply = dbus_message_new_method_return(message);
DBusMessageIter server_array, dict_array, server_iter;
struct server *serv;
dbus_message_iter_init_append(reply, &server_iter);
dbus_message_iter_open_container(&server_iter, DBUS_TYPE_ARRAY, "a{ss}", &server_array);
/* sum counts from different records for same server */
for (serv = daemon->servers; serv; serv = serv->next)
serv->flags &= ~SERV_MARK;
for (serv = daemon->servers; serv; serv = serv->next)
if (!(serv->flags & SERV_MARK))
{
unsigned int port;
unsigned int queries = 0, failed_queries = 0, nxdomain_replies = 0, retrys = 0;
unsigned int sigma_latency = 0, count_latency = 0;
struct server *serv1;
for (serv1 = serv; serv1; serv1 = serv1->next)
if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
{
serv1->flags |= SERV_MARK;
queries += serv1->queries;
failed_queries += serv1->failed_queries;
nxdomain_replies += serv1->nxdomain_replies;
retrys += serv1->retrys;
sigma_latency += serv1->query_latency;
count_latency++;
}
dbus_message_iter_open_container(&server_array, DBUS_TYPE_ARRAY, "{ss}", &dict_array);
port = prettyprint_addr(&serv->addr, daemon->namebuff);
add_dict_entry(&dict_array, "address", daemon->namebuff);
add_dict_int(&dict_array, "port", port);
add_dict_int(&dict_array, "queries", serv->queries);
add_dict_int(&dict_array, "failed_queries", serv->failed_queries);
add_dict_int(&dict_array, "nxdomain", serv->nxdomain_replies);
add_dict_int(&dict_array, "retries", serv->retrys);
add_dict_int(&dict_array, "latency", sigma_latency/count_latency);
dbus_message_iter_close_container(&server_array, &dict_array);
}
dbus_message_iter_close_container(&server_iter, &server_array);
return reply;
}
DBusHandlerResult message_handler(DBusConnection *connection,
DBusMessage *message,
void *user_data)
@@ -694,6 +817,10 @@ DBusHandlerResult message_handler(DBusConnection *connection,
{
reply = dbus_set_bool(message, OPT_FILTER, "filterwin2k");
}
else if (strcmp(method, "SetLocaliseQueriesOption") == 0)
{
reply = dbus_set_bool(message, OPT_LOCALISE, "localise-queries");
}
else if (strcmp(method, "SetBogusPrivOption") == 0)
{
reply = dbus_set_bool(message, OPT_BOGUSPRIV, "bogus-priv");
@@ -712,6 +839,14 @@ DBusHandlerResult message_handler(DBusConnection *connection,
{
reply = dbus_get_metrics(message);
}
else if (strcmp(method, "GetServerMetrics") == 0)
{
reply = dbus_get_server_metrics(message);
}
else if (strcmp(method, "ClearMetrics") == 0)
{
clear_metrics();
}
else if (strcmp(method, "ClearCache") == 0)
clear_cache = 1;
else
@@ -754,8 +889,11 @@ char *dbus_init(void)
dbus_error_init (&dbus_error);
if (!(connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error)))
return NULL;
{
dbus_error_free(&dbus_error);
return NULL;
}
dbus_connection_set_exit_on_disconnect(connection, FALSE);
dbus_connection_set_watch_functions(connection, add_watch, remove_watch,
NULL, NULL, NULL);

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -566,12 +566,16 @@ char *whichdevice(void)
}
if (found)
return found->name;
{
char *ret = safe_malloc(strlen(found->name)+1);
strcpy(ret, found->name);
return ret;
}
return NULL;
}
void bindtodevice(char *device, int fd)
static int bindtodevice(char *device, int fd)
{
size_t len = strlen(device)+1;
if (len > IFNAMSIZ)
@@ -579,7 +583,33 @@ void bindtodevice(char *device, int fd)
/* only allowed by root. */
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, len) == -1 &&
errno != EPERM)
die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
return 2;
return 1;
}
int bind_dhcp_devices(char *bound_device)
{
int ret = 0;
if (bound_device)
{
if (daemon->dhcp)
{
if (!daemon->relay4)
ret |= bindtodevice(bound_device, daemon->dhcpfd);
if (daemon->enable_pxe && daemon->pxefd != -1)
ret |= bindtodevice(bound_device, daemon->pxefd);
}
#if defined(HAVE_DHCP6)
if (daemon->doing_dhcp6 && !daemon->relay6)
ret |= bindtodevice(bound_device, daemon->dhcp6fd);
#endif
}
return ret;
}
#endif
@@ -655,6 +685,7 @@ static const struct opttab_t {
{ "client-machine-id", 97, 0 },
{ "posix-timezone", 100, OT_NAME }, /* RFC 4833, Sec. 2 */
{ "tzdb-timezone", 101, OT_NAME }, /* RFC 4833, Sec. 2 */
{ "ipv6-only", 108, 4 | OT_DEC }, /* RFC 8925 */
{ "subnet-select", 118, OT_INTERNAL },
{ "domain-search", 119, OT_RFC1035_NAME },
{ "sip-server", 120, 0 },
@@ -691,6 +722,8 @@ static const struct opttab_t opttab6[] = {
{ "sntp-server", 31, OT_ADDR_LIST },
{ "information-refresh-time", 32, OT_TIME },
{ "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
{ "posix-timezone", 41, OT_NAME }, /* RFC 4833, Sec. 3 */
{ "tzdb-timezone", 42, OT_NAME }, /* RFC 4833, Sec. 3 */
{ "ntp-server", 56, 0 /* OT_ADDR_LIST | OT_RFC1035_NAME */ },
{ "bootfile-url", 59, OT_NAME },
{ "bootfile-param", 60, OT_CSTRING },
@@ -985,11 +1018,34 @@ void log_context(int family, struct dhcp_context *context)
void log_relay(int family, struct dhcp_relay *relay)
{
int broadcast = relay->server.addr4.s_addr == 0;
inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN);
inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN);
inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN);
if (family == AF_INET && relay->port != DHCP_SERVER_PORT)
sprintf(daemon->namebuff + strlen(daemon->namebuff), "#%u", relay->port);
#ifdef HAVE_DHCP6
struct in6_addr multicast;
inet_pton(AF_INET6, ALL_SERVERS, &multicast);
if (family == AF_INET6)
{
broadcast = IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast);
if (relay->port != DHCPV6_SERVER_PORT)
sprintf(daemon->namebuff + strlen(daemon->namebuff), "#%u", relay->port);
}
#endif
if (relay->interface)
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
{
if (broadcast)
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s via %s"), daemon->addrbuff, relay->interface);
else
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
}
else
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff);
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -64,6 +64,7 @@
#define OPTION_SIP_SERVER 120
#define OPTION_VENDOR_IDENT 124
#define OPTION_VENDOR_IDENT_OPT 125
#define OPTION_MUD_URL_V4 161
#define OPTION_END 255
#define SUBOPT_CIRCUIT_ID 1

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,8 +20,6 @@
struct iface_param {
struct dhcp_context *current;
struct dhcp_relay *relay;
struct in_addr relay_local;
int ind;
};
@@ -34,7 +32,7 @@ static int complete_context(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
static int check_listen_addrs(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index);
static int relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz);
static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);
static int make_fd(int port)
@@ -177,11 +175,15 @@ void dhcp_packet(time_t now, int pxe_fd)
if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||
(sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
return;
#if defined (HAVE_LINUX_NETWORK)
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_DHCP, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&dest, NULL, fd);
#endif
#if defined (HAVE_LINUX_NETWORK)
if (ioctl(fd, SIOCGSTAMP, &tv) == 0)
recvtime = tv.tv_sec;
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
@@ -302,12 +304,7 @@ void dhcp_packet(time_t now, int pxe_fd)
for (context = daemon->dhcp; context; context = context->next)
context->current = context;
for (relay = daemon->relay4; relay; relay = relay->next)
relay->current = relay;
parm.current = NULL;
parm.relay = NULL;
parm.relay_local.s_addr = 0;
parm.ind = iface_index;
if (!iface_check(AF_INET, (union all_addr *)&iface_addr, ifr.ifr_name, NULL))
@@ -329,15 +326,19 @@ void dhcp_packet(time_t now, int pxe_fd)
there is more than one address on the interface in the same subnet */
complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
}
if (relay_upstream4(iface_index, mess, (size_t)sz))
return;
if (!iface_enumerate(AF_INET, &parm, complete_context))
return;
/* We're relaying this request */
if (parm.relay_local.s_addr != 0 &&
relay_upstream4(parm.relay, mess, (size_t)sz, iface_index))
/* Check for a relay again after iface_enumerate/complete_context has had
chance to fill in relay->iface_index fields. This handles first time through
and any changes in interface config. */
if (relay_upstream4(iface_index, mess, (size_t)sz))
return;
/* May have configured relay, but not DHCP server */
if (!daemon->dhcp)
return;
@@ -455,6 +456,17 @@ void dhcp_packet(time_t now, int pxe_fd)
#elif defined(HAVE_BSD_NETWORK)
else
{
#ifdef HAVE_DUMPFILE
if (ntohs(mess->flags) & 0x8000)
dest.sin_addr.s_addr = INADDR_BROADCAST;
else
dest.sin_addr = mess->yiaddr;
dest.sin_port = htons(daemon->dhcp_client_port);
dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
(union mysockaddr *)&dest, fd);
#endif
send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
return;
}
@@ -463,6 +475,11 @@ void dhcp_packet(time_t now, int pxe_fd)
#ifdef HAVE_SOLARIS_NETWORK
setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
#endif
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
(union mysockaddr *)&dest, fd);
#endif
while(retry_send(sendmsg(fd, &msg, 0)));
@@ -613,14 +630,9 @@ static int complete_context(struct in_addr local, int if_index, char *label,
}
for (relay = daemon->relay4; relay; relay = relay->next)
if (if_index == param->ind && relay->local.addr4.s_addr == local.s_addr && relay->current == relay &&
(param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr))
{
relay->current = param->relay;
param->relay = relay;
param->relay_local = local;
}
if (relay->local.addr4.s_addr == local.s_addr)
relay->iface_index = if_index;
return 1;
}
@@ -1061,53 +1073,96 @@ char *host_from_dns(struct in_addr addr)
return NULL;
}
static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index)
static int relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz)
{
/* ->local is same value for all relays on ->current chain */
union all_addr from;
struct in_addr giaddr = mess->giaddr;
u8 hops = mess->hops;
struct dhcp_relay *relay;
if (mess->op != BOOTREQUEST)
return 0;
/* source address == relay address */
from.addr4 = relay->local.addr4;
for (relay = daemon->relay4; relay; relay = relay->next)
if (relay->iface_index != 0 && relay->iface_index == iface_index)
break;
/* No relay config. */
if (!relay)
return 0;
/* already gatewayed ? */
if (mess->giaddr.s_addr)
{
/* if so check if by us, to stomp on loops. */
if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
return 1;
}
else
{
/* plug in our address */
mess->giaddr.s_addr = relay->local.addr4.s_addr;
}
for (; relay; relay = relay->next)
if (relay->iface_index != 0 && relay->iface_index == iface_index)
{
union mysockaddr to;
union all_addr from;
if ((mess->hops++) > 20)
return 1;
mess->hops = hops;
mess->giaddr = giaddr;
if ((mess->hops++) > 20)
continue;
/* source address == relay address */
from.addr4 = relay->local.addr4;
for (; relay; relay = relay->current)
{
union mysockaddr to;
to.sa.sa_family = AF_INET;
to.in.sin_addr = relay->server.addr4;
to.in.sin_port = htons(daemon->dhcp_server_port);
send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
if (option_bool(OPT_LOG_OPTS))
/* already gatewayed ? */
if (giaddr.s_addr)
{
/* if so check if by us, to stomp on loops. */
if (giaddr.s_addr == relay->local.addr4.s_addr)
continue;
}
else
{
/* plug in our address */
mess->giaddr.s_addr = relay->local.addr4.s_addr;
}
to.sa.sa_family = AF_INET;
to.in.sin_addr = relay->server.addr4;
to.in.sin_port = htons(relay->port);
/* Broadcasting to server. */
if (relay->server.addr4.s_addr == 0)
{
struct ifreq ifr;
if (relay->interface)
safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE);
if (!relay->interface || strchr(relay->interface, '*') ||
ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1)
{
my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface);
continue;
}
to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
#ifdef HAVE_DUMPFILE
{
inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
union mysockaddr fromsock;
fromsock.in.sin_port = htons(daemon->dhcp_server_port);
fromsock.in.sin_addr = from.addr4;
fromsock.sa.sa_family = AF_INET;
dump_packet_udp(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, -1);
}
/* Save this for replies */
relay->iface_index = iface_index;
}
#endif
send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
if (option_bool(OPT_LOG_OPTS))
{
inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
if (relay->server.addr4.s_addr == 0)
snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface);
else
inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
}
}
return 1;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -55,12 +55,15 @@
#define OPTION6_RECONF_ACCEPT 20
#define OPTION6_DNS_SERVER 23
#define OPTION6_DOMAIN_SEARCH 24
#define OPTION6_IA_PD 25
#define OPTION6_IAPREFIX 26
#define OPTION6_REFRESH_TIME 32
#define OPTION6_REMOTE_ID 37
#define OPTION6_SUBSCRIBER_ID 38
#define OPTION6_FQDN 39
#define OPTION6_NTP_SERVER 56
#define OPTION6_CLIENT_MAC 79
#define OPTION6_MUD_URL 112
#define NTP_SUBOPTION_SRV_ADDR 1
#define NTP_SUBOPTION_MC_ADDR 2

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,8 +22,7 @@
struct iface_param {
struct dhcp_context *current;
struct dhcp_relay *relay;
struct in6_addr fallback, relay_local, ll_addr, ula_addr;
struct in6_addr fallback, ll_addr, ula_addr;
int ind, addr_match;
};
@@ -90,7 +89,6 @@ void dhcp6_init(void)
void dhcp6_packet(time_t now)
{
struct dhcp_context *context;
struct dhcp_relay *relay;
struct iface_param parm;
struct cmsghdr *cmptr;
struct msghdr msg;
@@ -105,7 +103,8 @@ void dhcp6_packet(time_t now)
struct iname *tmp;
unsigned short port;
struct in6_addr dst_addr;
struct in6_addr all_servers;
memset(&dst_addr, 0, sizeof(dst_addr));
msg.msg_control = control_u.control6;
@@ -119,6 +118,11 @@ void dhcp6_packet(time_t now)
if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
return;
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_DHCPV6, (void *)daemon->dhcp_packet.iov_base, sz,
(union mysockaddr *)&from, NULL, daemon->dhcp6fd);
#endif
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{
@@ -135,9 +139,13 @@ void dhcp6_packet(time_t now)
if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
return;
if ((port = relay_reply6(&from, sz, ifr.ifr_name)) != 0)
if (relay_reply6(&from, sz, ifr.ifr_name))
{
from.sin6_port = htons(port);
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), NULL,
(union mysockaddr *)&from, daemon->dhcp6fd);
#endif
while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
save_counter(-1), 0, (struct sockaddr *)&from,
sizeof(from))));
@@ -145,7 +153,7 @@ void dhcp6_packet(time_t now)
else
{
struct dhcp_bridge *bridge, *alias;
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
return;
@@ -155,14 +163,12 @@ void dhcp6_packet(time_t now)
return;
parm.current = NULL;
parm.relay = NULL;
memset(&parm.relay_local, 0, IN6ADDRSZ);
parm.ind = if_index;
parm.addr_match = 0;
memset(&parm.fallback, 0, IN6ADDRSZ);
memset(&parm.ll_addr, 0, IN6ADDRSZ);
memset(&parm.ula_addr, 0, IN6ADDRSZ);
/* If the interface on which the DHCPv6 request was received is
an alias of some other interface (as specified by the
--bridge-interface option), change parm.ind so that we look
@@ -200,13 +206,25 @@ void dhcp6_packet(time_t now)
context->current = context;
memset(&context->local6, 0, IN6ADDRSZ);
}
for (relay = daemon->relay6; relay; relay = relay->next)
relay->current = relay;
/* Ignore requests sent to the ALL_SERVERS multicast address for relay when
we're listening there for DHCPv6 server reasons. */
inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) &&
relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now))
return;
if (!iface_enumerate(AF_INET6, &parm, complete_context6))
return;
/* Check for a relay again after iface_enumerate/complete_context has had
chance to fill in relay->iface_index fields. This handles first time through
and any changes in interface config. */
if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) &&
relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now))
return;
if (daemon->if_names || daemon->if_addrs)
{
@@ -218,23 +236,10 @@ void dhcp6_packet(time_t now)
return;
}
if (parm.relay)
{
/* Ignore requests sent to the ALL_SERVERS multicast address for relay when
we're listening there for DHCPv6 server reasons. */
struct in6_addr all_servers;
inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id, now);
return;
}
/* May have configured relay, but not DHCP server */
if (!daemon->doing_dhcp6)
return;
lease_prune(NULL, now); /* lose any expired leases */
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
@@ -247,11 +252,16 @@ void dhcp6_packet(time_t now)
if (port != 0)
{
from.sin6_port = htons(port);
while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
save_counter(-1), 0, (struct sockaddr *)&from,
sizeof(from))));
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1),
NULL, (union mysockaddr *)&from, daemon->dhcp6fd);
#endif
while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
save_counter(-1), 0, (struct sockaddr *)&from, sizeof(from))));
}
/* These need to be called _after_ we send DHCPv6 packet, since lease_update_file()
may trigger sending an RA packet, which overwrites our buffer. */
lease_update_file(now);
@@ -312,6 +322,7 @@ static int complete_context6(struct in6_addr *local, int prefix,
struct dhcp_relay *relay;
struct iface_param *param = vparam;
struct iname *tmp;
int match = !daemon->if_addrs;
(void)scope; /* warning */
@@ -333,7 +344,7 @@ static int complete_context6(struct in6_addr *local, int prefix,
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (tmp->addr.sa.sa_family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
param->addr_match = 1;
match = param->addr_match = 1;
/* Determine a globally address on the arrival interface, even
if we have no matching dhcp-context, because we're only
@@ -405,16 +416,12 @@ static int complete_context6(struct in6_addr *local, int prefix,
}
}
}
for (relay = daemon->relay6; relay; relay = relay->next)
if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6) && relay->current == relay &&
(IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
{
relay->current = param->relay;
param->relay = relay;
param->relay_local = *local;
}
if (match)
for (relay = daemon->relay6; relay; relay = relay->next)
if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6))
relay->iface_index = if_index;
return 1;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,8 +17,13 @@
/* Declare static char *compiler_opts in config.h */
#define DNSMASQ_COMPILE_OPTS
/* dnsmasq.h has to be included first as it sources config.h */
#include "dnsmasq.h"
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) || defined(LOCALEDIR)
#include <locale.h>
#endif
struct daemon *daemon;
static volatile pid_t pid = 0;
@@ -69,8 +74,10 @@ int main (int argc, char **argv)
int tftp_prefix_missing = 0;
#endif
#ifdef LOCALEDIR
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) || defined(LOCALEDIR)
setlocale(LC_ALL, "");
#endif
#ifdef LOCALEDIR
bindtextdomain("dnsmasq", LOCALEDIR);
textdomain("dnsmasq");
#endif
@@ -258,6 +265,10 @@ int main (int argc, char **argv)
if (daemon->max_port < daemon->min_port)
die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF);
if (daemon->max_port != 0 &&
daemon->max_port - daemon->min_port + 1 < daemon->randport_limit)
die(_("port_limit must not be larger than available port range"), NULL, EC_BADCONF);
now = dnsmasq_time();
@@ -380,28 +391,9 @@ int main (int argc, char **argv)
#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP)
/* after enumerate_interfaces() */
bound_device = whichdevice();
if (daemon->dhcp)
{
if (!daemon->relay4 && bound_device)
{
bindtodevice(bound_device, daemon->dhcpfd);
did_bind = 1;
}
if (daemon->enable_pxe && bound_device && daemon->pxefd != -1)
{
bindtodevice(bound_device, daemon->pxefd);
did_bind = 1;
}
}
#endif
#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP6)
if (daemon->doing_dhcp6 && !daemon->relay6 && bound_device)
{
bindtodevice(bound_device, daemon->dhcp6fd);
did_bind = 1;
}
if ((did_bind = bind_dhcp_devices(bound_device)) & 2)
die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
#endif
}
else
@@ -727,7 +719,11 @@ int main (int argc, char **argv)
/* if we are to run scripts, we need to fork a helper before dropping root. */
daemon->helperfd = -1;
#ifdef HAVE_SCRIPT
if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_SCRIPT_ARP)) &&
if ((daemon->dhcp ||
daemon->dhcp6 ||
daemon->relay6 ||
option_bool(OPT_TFTP) ||
option_bool(OPT_SCRIPT_ARP)) &&
(daemon->lease_change_command || daemon->luascript))
daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
#endif
@@ -1063,19 +1059,20 @@ int main (int argc, char **argv)
while (1)
{
int timeout = -1;
int timeout = fast_retry(now);
poll_reset();
/* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
if (daemon->tftp_trans ||
(option_bool(OPT_DBUS) && !daemon->dbus))
if ((daemon->tftp_trans || (option_bool(OPT_DBUS) && !daemon->dbus)) &&
(timeout == -1 || timeout > 250))
timeout = 250;
/* Wake every second whilst waiting for DAD to complete */
else if (is_dad_listeners())
else if (is_dad_listeners() &&
(timeout == -1 || timeout > 1000))
timeout = 1000;
set_dns_listeners();
#ifdef HAVE_DBUS
@@ -1089,6 +1086,17 @@ int main (int argc, char **argv)
#endif
#ifdef HAVE_DHCP
# if defined(HAVE_LINUX_NETWORK)
if (bind_dhcp_devices(bound_device) & 2)
{
static int warned = 0;
if (!warned)
{
my_syslog(LOG_ERR, _("error binding DHCP socket to device %s"), bound_device);
warned = 1;
}
}
# endif
if (daemon->dhcp || daemon->relay4)
{
poll_listen(daemon->dhcpfd, POLLIN);
@@ -1132,6 +1140,10 @@ int main (int argc, char **argv)
while (helper_buf_empty() && do_tftp_script_run());
# endif
# ifdef HAVE_DHCP6
while (helper_buf_empty() && do_snoop_script_run());
# endif
if (!helper_buf_empty())
poll_listen(daemon->helperfd, POLLOUT);
#else
@@ -1658,9 +1670,10 @@ static void poll_resolv(int force, int do_reload, time_t now)
else
{
res->logged = 0;
if (force || (statbuf.st_mtime != res->mtime))
if (force || (statbuf.st_mtime != res->mtime || statbuf.st_ino != res->ino))
{
res->mtime = statbuf.st_mtime;
res->ino = statbuf.st_ino;
if (difftime(statbuf.st_mtime, last_change) > 0.0)
{
last_change = statbuf.st_mtime;
@@ -1682,6 +1695,11 @@ static void poll_resolv(int force, int do_reload, time_t now)
}
else
{
/* If we're delaying things, we don't call check_servers(), but
reload_servers() may have deleted some servers, rendering the server_array
invalid, so just rebuild that here. Once reload_servers() succeeds,
we call check_servers() above, which calls build_server_array itself. */
build_server_array();
latest->mtime = 0;
if (!warned)
{
@@ -2001,9 +2019,6 @@ static void check_dns_listeners(time_t now)
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
shutdown(confd, SHUT_RDWR);
close(confd);
if (buff)
free(buff);

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define COPYRIGHT "Copyright (c) 2000-2021 Simon Kelley"
#define COPYRIGHT "Copyright (c) 2000-2022 Simon Kelley"
/* We do defines that influence behavior of stdio.h, so complain
if included too early. */
@@ -133,6 +133,7 @@ typedef unsigned long long u64;
#include <sys/uio.h>
#include <syslog.h>
#include <dirent.h>
#include <netdb.h>
#ifndef HAVE_LINUX_NETWORK
# include <net/if_dl.h>
#endif
@@ -275,7 +276,13 @@ struct event_desc {
#define OPT_UMBRELLA_DEVID 64
#define OPT_CMARK_ALST_EN 65
#define OPT_QUIET_TFTP 66
#define OPT_LAST 67
#define OPT_FILTER_A 67
#define OPT_FILTER_AAAA 68
#define OPT_STRIP_ECS 69
#define OPT_STRIP_MAC 70
#define OPT_NORR 71
#define OPT_NO_IDENT 72
#define OPT_LAST 73
#define OPTION_BITS (sizeof(unsigned int)*8)
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
@@ -506,6 +513,7 @@ struct crec {
#define F_DOMAINSRV (1u<<28)
#define F_RCODE (1u<<29)
#define F_SRV (1u<<30)
#define F_STALE (1u<<31)
#define UID_NONE 0
/* Values of uid in crecs with F_CONFIG bit set. */
@@ -580,7 +588,8 @@ struct server {
struct serverfd *sfd;
int tcpfd, edns_pktsz;
time_t pktsz_reduced;
unsigned int queries, failed_queries;
unsigned int queries, failed_queries, nxdomain_replies, retrys;
unsigned int query_latency, mma_latency;
time_t forwardtime;
int forwardcount;
#ifdef HAVE_LOOP
@@ -661,6 +670,7 @@ struct resolvc {
struct resolvc *next;
int is_default, logged;
time_t mtime;
ino_t ino;
char *name;
#ifdef HAVE_INOTIFY
int wd; /* inotify watch descriptor */
@@ -679,21 +689,32 @@ struct hostsfile {
struct hostsfile *next;
int flags;
char *fname;
#ifdef HAVE_INOTIFY
int wd; /* inotify watch descriptor */
#endif
unsigned int index; /* matches to cache entries for logging */
};
struct dyndir {
struct dyndir *next;
struct hostsfile *files;
int flags;
char *dname;
#ifdef HAVE_INOTIFY
int wd; /* inotify watch descriptor */
#endif
};
/* packet-dump flags */
#define DUMP_QUERY 0x0001
#define DUMP_REPLY 0x0002
#define DUMP_UP_QUERY 0x0004
#define DUMP_UP_REPLY 0x0008
#define DUMP_SEC_QUERY 0x0010
#define DUMP_SEC_REPLY 0x0020
#define DUMP_BOGUS 0x0040
#define DUMP_SEC_BOGUS 0x0080
#define DUMP_QUERY 0x0001
#define DUMP_REPLY 0x0002
#define DUMP_UP_QUERY 0x0004
#define DUMP_UP_REPLY 0x0008
#define DUMP_SEC_QUERY 0x0010
#define DUMP_SEC_REPLY 0x0020
#define DUMP_BOGUS 0x0040
#define DUMP_SEC_BOGUS 0x0080
#define DUMP_DHCP 0x1000
#define DUMP_DHCPV6 0x2000
#define DUMP_RA 0x4000
#define DUMP_TFTP 0x8000
/* DNSSEC status values. */
#define STAT_SECURE 0x10000
@@ -720,7 +741,7 @@ struct hostsfile {
#define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2
#define FREC_HAS_SUBNET 4
#define FREC_NO_CACHE 4
#define FREC_DNSKEY_QUERY 8
#define FREC_DS_QUERY 16
#define FREC_AD_QUESTION 32
@@ -729,7 +750,6 @@ struct hostsfile {
#define FREC_TEST_PKTSZ 256
#define FREC_HAS_EXTRADATA 512
#define FREC_HAS_PHEADER 1024
#define FREC_NO_CACHE 2048
#define HASH_SIZE 32 /* SHA-256 digest size */
@@ -747,11 +767,13 @@ struct frec {
unsigned short new_id;
int forwardall, flags;
time_t time;
u32 forward_timestamp;
int forward_delay;
unsigned char *hash[HASH_SIZE];
#ifdef HAVE_DNSSEC
int class, work_counter;
struct blockdata *stash; /* Saved reply, whilst we validate */
size_t stash_len;
#ifdef HAVE_DNSSEC
int class, work_counter;
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
struct frec *next_dependent; /* list of above. */
struct frec *blocking_query; /* Query which is blocking us. */
@@ -776,6 +798,7 @@ struct frec {
#define ACTION_TFTP 5
#define ACTION_ARP 6
#define ACTION_ARP_DEL 7
#define ACTION_RELAY_SNOOP 8
#define LEASE_NEW 1 /* newly created */
#define LEASE_CHANGED 2 /* modified */
@@ -967,6 +990,8 @@ struct dhcp_bridge {
struct cond_domain {
char *domain, *prefix; /* prefix is text-prefix on domain name */
char *interface; /* These two set when domain comes from interface. */
struct addrlist *al;
struct in_addr start, end;
struct in6_addr start6, end6;
int is6, indexed, prefixlen;
@@ -1074,7 +1099,15 @@ struct dhcp_relay {
union all_addr local, server;
char *interface; /* Allowable interface for replies from server, and dest for IPv6 multicast */
int iface_index; /* working - interface in which requests arrived, for return */
struct dhcp_relay *current, *next;
int port; /* Port of relay we forward to. */
#ifdef HAVE_SCRIPT
struct snoop_record {
struct in6_addr client, prefix;
int prefix_len;
struct snoop_record *next;
} *snoop_records;
#endif
struct dhcp_relay *next;
};
extern struct daemon {
@@ -1120,6 +1153,7 @@ extern struct daemon {
int log_fac; /* log facility */
char *log_file; /* optional log file */
int max_logs; /* queue limit */
int randport_limit; /* Maximum number of source ports for query. */
int cachesize, ftabsize;
int port, query_port, min_port, max_port;
unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl, dhcp_ttl, use_dhcp_ttl;
@@ -1127,6 +1161,7 @@ extern struct daemon {
u32 umbrella_org;
u32 umbrella_asset;
u8 umbrella_device[8];
int host_index;
struct hostsfile *addn_hosts;
struct dhcp_context *dhcp, *dhcp6;
struct ra_interface *ra_interfaces;
@@ -1147,7 +1182,8 @@ extern struct daemon {
int doing_ra, doing_dhcp6;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file;
struct dyndir *dynamic_dirs;
int dhcp_max, tftp_max, tftp_mtu;
int dhcp_server_port, dhcp_client_port;
int start_tftp_port, end_tftp_port;
@@ -1164,6 +1200,8 @@ extern struct daemon {
int dump_mask;
unsigned long soa_sn, soa_refresh, soa_retry, soa_expiry;
u32 metrics[__METRIC_MAX];
int fast_retry_time, fast_retry_timeout;
int cache_max_expiry;
#ifdef HAVE_DNSSEC
struct ds_config *ds;
char *timestamp_file;
@@ -1173,9 +1211,12 @@ extern struct daemon {
char *packet; /* packet buffer */
int packet_buff_sz; /* size of above */
char *namebuff; /* MAXDNAME size buffer */
#if (defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)) || defined(HAVE_DNSSEC)
/* CONNTRACK UBUS code uses this buffer, as well as DNSSEC code. */
char *workspacename;
#endif
#ifdef HAVE_DNSSEC
char *keyname; /* MAXDNAME size buffer */
char *workspacename; /* ditto */
unsigned long *rr_status; /* ceiling in TTL from DNSSEC or zero for insecure */
int rr_status_sz;
int dnssec_no_time_check;
@@ -1222,13 +1263,18 @@ extern struct daemon {
unsigned char *duid;
struct iovec outpacket;
int dhcp6fd, icmp6fd;
# ifdef HAVE_SCRIPT
struct snoop_record *free_snoops;
# endif
#endif
/* DBus stuff */
/* void * here to avoid depending on dbus headers outside dbus.c */
void *dbus;
#ifdef HAVE_DBUS
struct watch *watches;
#endif
/* UBus stuff */
#ifdef HAVE_UBUS
/* void * here to avoid depending on ubus headers outside ubus.c */
@@ -1248,6 +1294,14 @@ extern struct daemon {
#endif
} *daemon;
struct server_details {
union mysockaddr *addr, *source_addr;
struct addrinfo *hostinfo, *orig_hostinfo;
char *interface, *source, *scope_id, *interface_opt;
int serv_port, source_port, addr_type, scope_index, valid;
u16 *flags;
};
/* cache.c */
void cache_init(void);
void next_uid(struct crec *crecp);
@@ -1261,6 +1315,7 @@ struct crec *cache_find_by_name(struct crec *crecp,
char *name, time_t now, unsigned int prot);
void cache_end_insert(void);
void cache_start_insert(void);
unsigned int cache_remove_uid(const unsigned int uid);
int cache_recv_insert(time_t now, int fd);
struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class,
time_t now, unsigned long ttl, unsigned int flags);
@@ -1310,7 +1365,8 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark);
#endif
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask,
time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
time_t now, int ad_reqd, int do_bit, int have_pseudoheader,
int *stale);
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
time_t now);
int check_for_ignored_address(struct dns_header *header, size_t qlen);
@@ -1369,11 +1425,15 @@ void *safe_malloc(size_t size);
void safe_strncpy(char *dest, const char *src, size_t size);
void safe_pipe(int *fd, int read_noblock);
void *whine_malloc(size_t size);
void *whine_realloc(void *ptr, size_t size);
int sa_len(union mysockaddr *addr);
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2);
int sockaddr_isnull(const union mysockaddr *s);
int hostname_order(const char *a, const char *b);
int hostname_isequal(const char *a, const char *b);
int hostname_issubdomain(char *a, char *b);
time_t dnsmasq_time(void);
u32 dnsmasq_milliseconds(void);
int netmask_length(struct in_addr mask);
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix);
@@ -1417,8 +1477,9 @@ void read_servers_file(void);
void set_option_bool(unsigned int opt);
void reset_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list);
char *parse_server(char *arg, union mysockaddr *addr,
union mysockaddr *source_addr, char *interface, u16 *flags);
char *parse_server(char *arg, struct server_details *sdetails);
char *parse_server_addr(struct server_details *sdetails);
int parse_server_next(struct server_details *sdetails);
int option_read_dynfile(char *file, int flags);
/* forward.c */
@@ -1433,6 +1494,7 @@ int send_from(int fd, int nowild, char *packet, size_t len,
void resend_query(void);
int allocate_rfd(struct randfd_list **fdlp, struct server *serv);
void free_rfds(struct randfd_list **fdlp);
int fast_retry(time_t now);
/* network.c */
int indextoname(int fd, int index, char *name);
@@ -1613,6 +1675,9 @@ void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer);
void queue_arp(int action, unsigned char *mac, int maclen,
int family, union all_addr *addr);
int helper_buf_empty(void);
#ifdef HAVE_DHCP6
void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *prefix, int prefix_len);
#endif
#endif
/* tftp.c */
@@ -1655,10 +1720,13 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
size_t sz, struct in6_addr *client_addr, time_t now);
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address,
int relay_upstream6(int iface_index, ssize_t sz, struct in6_addr *peer_address,
u32 scope_id, time_t now);
unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface);
int relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface);
# ifdef HAVE_SCRIPT
int do_snoop_script_run(void);
# endif
#endif
/* dhcp-common.c */
@@ -1685,7 +1753,7 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type);
#ifdef HAVE_LINUX_NETWORK
char *whichdevice(void);
void bindtodevice(char *device, int fd);
int bind_dhcp_devices(char *bound_device);
#endif
# ifdef HAVE_DHCP6
void display_opts6(void);
@@ -1746,7 +1814,11 @@ int do_poll(int timeout);
size_t rrfilter(struct dns_header *header, size_t plen, int mode);
u16 *rrfilter_desc(int type);
int expand_workspace(unsigned char ***wkspc, int *szp, int new);
/* modes. */
#define RRFILTER_EDNS0 0
#define RRFILTER_DNSSEC 1
#define RRFILTER_A 2
#define RRFILTER_AAAA 3
/* edns0.c */
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
size_t *len, unsigned char **p, int *is_sign, int *is_last);
@@ -1754,7 +1826,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do, int replace);
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit);
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
union mysockaddr *source, time_t now, int *check_subnet, int *cacheable);
union mysockaddr *source, time_t now, int *cacheable);
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
/* arp.c */
@@ -1764,7 +1836,10 @@ int do_arp_script_run(void);
/* dump.c */
#ifdef HAVE_DUMPFILE
void dump_init(void);
void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst);
void dump_packet_udp(int mask, void *packet, size_t len, union mysockaddr *src,
union mysockaddr *dst, int fd);
void dump_packet_icmp(int mask, void *packet, size_t len, union mysockaddr *src,
union mysockaddr *dst);
#endif
/* domain-match.c */

View File

@@ -979,10 +979,13 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
}
/* The DNS packet is expected to contain the answer to a DS query
Put all DSs in the answer which are valid into the cache.
Put all DSs in the answer which are valid and have hash and signature algos
we support into the cache.
Also handles replies which prove that there's no DS at this location,
either because the zone is unsigned or this isn't a zone cut. These are
cached too.
If none of the DS's are for supported algos, treat the answer as if
it's a proof of no DS at this location. RFC4035 para 5.2.
return codes:
STAT_OK At least one valid DS found and in cache.
STAT_BOGUS no DS in reply or not signed, fails validation, bad packet.
@@ -993,8 +996,8 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
{
unsigned char *p = (unsigned char *)(header+1);
int qtype, qclass, rc, i, neganswer, nons, neg_ttl = 0;
int aclass, atype, rdlen;
int qtype, qclass, rc, i, neganswer, nons, neg_ttl = 0, found_supported = 0;
int aclass, atype, rdlen, flags;
unsigned long ttl;
union all_addr a;
@@ -1065,14 +1068,22 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
algo = *p++;
digest = *p++;
if ((key = blockdata_alloc((char*)p, rdlen - 4)))
if (!ds_digest_name(digest) || !algo_digest_name(algo))
{
a.log.keytag = keytag;
a.log.algo = algo;
a.log.digest = digest;
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0);
neg_ttl = ttl;
}
else if ((key = blockdata_alloc((char*)p, rdlen - 4)))
{
a.ds.digest = digest;
a.ds.keydata = key;
a.ds.algo = algo;
a.ds.keytag = keytag;
a.ds.keylen = rdlen - 4;
if (!cache_insert(name, &a, class, now, ttl, F_FORWARD | F_DS | F_DNSSECOK))
{
blockdata_free(key);
@@ -1083,26 +1094,29 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
a.log.keytag = keytag;
a.log.algo = algo;
a.log.digest = digest;
if (ds_digest_name(digest) && algo_digest_name(algo))
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0);
else
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0);
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0);
found_supported = 1;
}
}
p = psave;
}
if (!ADD_RDLEN(header, p, plen, rdlen))
return STAT_BOGUS; /* bad packet */
}
cache_end_insert();
/* Fall through if no supported algo DS found. */
if (found_supported)
return STAT_OK;
}
else
flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
if (neganswer)
{
int flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
if (RCODE(header) == NXDOMAIN)
flags |= F_NXDOMAIN;
@@ -1110,17 +1124,18 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
to store presence/absence of NS. */
if (nons)
flags &= ~F_DNSSECOK;
cache_start_insert();
/* Use TTL from NSEC for negative cache entries */
if (!cache_insert(name, NULL, class, now, neg_ttl, flags))
return STAT_BOGUS;
cache_end_insert();
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0);
}
cache_start_insert();
/* Use TTL from NSEC for negative cache entries */
if (!cache_insert(name, NULL, class, now, neg_ttl, flags))
return STAT_BOGUS;
cache_end_insert();
if (neganswer)
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0);
return STAT_OK;
}
@@ -1851,7 +1866,7 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
STAT_NEED_DS need DS to complete validation (name is returned in keyname)
daemon->rr_status points to a char array which corressponds to the RRs in the
answer and auth sections. This is set to 1 for each RR which is validated, and 0 for any which aren't.
answer and auth sections. This is set to >1 for each RR which is validated, and 0 for any which aren't.
When validating replies to DS records, we're only interested in the NSEC{3} RRs in the auth section.
Other RRs in that section missing sigs will not cause am INSECURE reply. We determine this mode
@@ -1867,7 +1882,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx;
int i, j, rc = STAT_INSECURE;
int secure = STAT_SECURE;
/* extend rr_status if necessary */
if (daemon->rr_status_sz < ntohs(header->ancount) + ntohs(header->nscount))
{
@@ -1989,7 +2004,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
{
/* NSEC and NSEC3 records must be signed. We make this assumption elsewhere. */
if (type1 == T_NSEC || type1 == T_NSEC3)
rc = STAT_INSECURE;
return STAT_BOGUS | DNSSEC_FAIL_NOSIG;
else if (nons && i >= ntohs(header->ancount))
/* If we're validating a DS reply, rather than looking for the value of AD bit,
we only care that NSEC and NSEC3 RRs in the auth section are signed.
@@ -2003,6 +2018,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
rc = zone_status(name, class1, keyname, now);
if (STAT_ISEQUAL(rc, STAT_SECURE))
rc = STAT_BOGUS | DNSSEC_FAIL_NOSIG;
if (class)
*class = class1; /* Class for NEED_DS or NEED_KEY */
}
@@ -2081,36 +2097,35 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
}
/* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
if (STAT_ISEQUAL(secure, STAT_SECURE))
for (j = 0; j <targetidx; j++)
if ((p2 = targets[j]))
{
if (neganswer)
*neganswer = 1;
if (!extract_name(header, plen, &p2, name, 1, 10))
return STAT_BOGUS; /* bad packet */
/* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */
/* For anything other than a DS record, this situation is OK if either
the answer is in an unsigned zone, or there's a NSEC records. */
if (!prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons, nsec_ttl))
{
/* Empty DS without NSECS */
if (qtype == T_DS)
return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
if (STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))
{
if (class)
*class = qclass; /* Class for NEED_DS or NEED_KEY */
return rc;
}
return STAT_BOGUS | DNSSEC_FAIL_NONSEC; /* signed zone, no NSECs */
}
}
for (j = 0; j <targetidx; j++)
if ((p2 = targets[j]))
{
if (neganswer)
*neganswer = 1;
if (!extract_name(header, plen, &p2, name, 1, 10))
return STAT_BOGUS; /* bad packet */
/* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */
/* For anything other than a DS record, this situation is OK if either
the answer is in an unsigned zone, or there's a NSEC records. */
if (!prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons, nsec_ttl))
{
/* Empty DS without NSECS */
if (qtype == T_DS)
return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
if (!STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))
{
if (class)
*class = qclass; /* Class for NEED_DS or NEED_KEY */
return rc;
}
return STAT_BOGUS | DNSSEC_FAIL_NONSEC; /* signed zone, no NSECs */
}
}
return secure;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -213,9 +213,13 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout)
to continue generalising */
{
/* We've matched a setting which says to use servers without a domain.
Continue the search with empty query */
Continue the search with empty query. We set the F_SERVER flag
so that --address=/#/... doesn't match. */
if (daemon->serverarray[nlow]->flags & SERV_USE_RESOLV)
crop_query = qlen;
{
crop_query = qlen;
flags |= F_SERVER;
}
else
break;
}
@@ -299,7 +303,7 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
if (i != nlow && (flags & F_IPV6))
if (!(flags & F_SERVER) && i != nlow && (flags & F_IPV6))
nhigh = i;
else
{
@@ -307,7 +311,7 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_4ADDR); i++);
if (i != nlow && (flags & F_IPV4))
if (!(flags & F_SERVER) && i != nlow && (flags & F_IPV4))
nhigh = i;
else
{
@@ -315,7 +319,7 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_ALL_ZEROS); i++);
if (i != nlow && (flags & (F_IPV4 | F_IPV6)))
if (!(flags & F_SERVER) && i != nlow && (flags & (F_IPV4 | F_IPV6)))
nhigh = i;
else
{
@@ -395,7 +399,7 @@ int is_local_answer(time_t now, int first, char *name)
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede)
{
int trunc = 0;
int trunc = 0, anscount = 0;
unsigned char *p;
int start;
union all_addr addr;
@@ -418,9 +422,8 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
else
addr.addr4 = srv->addr;
header->ancount = htons(ntohs(header->ancount) + 1);
if (!add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr))
return 0;
if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr))
anscount++;
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL, 0);
}
@@ -434,14 +437,15 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
else
addr.addr6 = srv->addr;
header->ancount = htons(ntohs(header->ancount) + 1);
add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr);
if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr))
anscount++;
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL, 0);
}
if (trunc)
header->hb3 |= HB3_TC;
header->ancount = htons(anscount);
return p - (unsigned char *)header;
}
@@ -494,7 +498,7 @@ static int order(char *qdomain, size_t qlen, struct server *serv)
if (qlen > dlen)
return -1;
return strcmp(qdomain, serv->domain);
return hostname_order(qdomain, serv->domain);
}
static int order_servers(struct server *s1, struct server *s2)
@@ -542,22 +546,53 @@ static int order_qsort(const void *a, const void *b)
return rc;
}
/* When loading large numbers of server=.... lines during startup,
there's no possibility that there will be server records that can be reused, but
searching a long list for each server added grows as O(n^2) and slows things down.
This flag is set only if is known there may be free server records that can be reused.
There's a call to mark_servers(0) in read_opts() to reset the flag before
main config read. */
static int maybe_free_servers = 0;
/* Must be called before add_update_server() to set daemon->servers_tail */
void mark_servers(int flag)
{
struct server *serv;
struct server *serv, *next, **up;
maybe_free_servers = !!flag;
daemon->servers_tail = NULL;
/* mark everything with argument flag */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & flag)
serv->flags |= SERV_MARK;
else
serv->flags &= ~SERV_MARK;
{
if (serv->flags & flag)
serv->flags |= SERV_MARK;
else
serv->flags &= ~SERV_MARK;
for (serv = daemon->local_domains; serv; serv = serv->next)
if (serv->flags & flag)
serv->flags |= SERV_MARK;
else
serv->flags &= ~SERV_MARK;
daemon->servers_tail = serv;
}
/* --address etc is different: since they are expected to be
1) numerous and 2) not reloaded often. We just delete
and recreate. */
if (flag)
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = next)
{
next = serv->next;
if (serv->flags & flag)
{
*up = next;
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
}
void cleanup_servers(void)
@@ -565,7 +600,7 @@ void cleanup_servers(void)
struct server *serv, *tmp, **up;
/* unlink and free anything still marked. */
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
for (serv = daemon->servers, up = &daemon->servers, daemon->servers_tail = NULL; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
@@ -581,19 +616,6 @@ void cleanup_servers(void)
daemon->servers_tail = serv;
}
}
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
*up = serv->next;
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
}
int add_update_server(int flags,
@@ -626,36 +648,17 @@ int add_update_server(int flags,
if (!alloc_domain)
return 0;
/* See if there is a suitable candidate, and unmark
only do this for forwarding servers, not
address or local, to avoid delays on large numbers. */
if (flags & SERV_IS_LOCAL)
for (serv = daemon->servers; serv; serv = serv->next)
if ((serv->flags & SERV_MARK) &&
hostname_isequal(alloc_domain, serv->domain))
break;
if (serv)
{
free(alloc_domain);
alloc_domain = serv->domain;
}
else
{
size_t size;
if (flags & SERV_IS_LOCAL)
{
if (flags & SERV_6ADDR)
size = sizeof(struct serv_addr6);
else if (flags & SERV_4ADDR)
size = sizeof(struct serv_addr4);
else
size = sizeof(struct serv_local);
}
if (flags & SERV_6ADDR)
size = sizeof(struct serv_addr6);
else if (flags & SERV_4ADDR)
size = sizeof(struct serv_addr4);
else
size = sizeof(struct server);
size = sizeof(struct serv_local);
if (!(serv = whine_malloc(size)))
{
@@ -663,19 +666,58 @@ int add_update_server(int flags,
return 0;
}
if (flags & SERV_IS_LOCAL)
{
serv->next = daemon->local_domains;
daemon->local_domains = serv;
serv->next = daemon->local_domains;
daemon->local_domains = serv;
if (flags & SERV_4ADDR)
((struct serv_addr4*)serv)->addr = local_addr->addr4;
if (flags & SERV_6ADDR)
((struct serv_addr6*)serv)->addr = local_addr->addr6;
}
else
{
/* Upstream servers. See if there is a suitable candidate, if so unmark
and move to the end of the list, for order. The entry found may already
be at the end. */
struct server **up, *tmp;
if (flags & SERV_4ADDR)
((struct serv_addr4*)serv)->addr = local_addr->addr4;
if (flags & SERV_6ADDR)
((struct serv_addr6*)serv)->addr = local_addr->addr6;
serv = NULL;
if (maybe_free_servers)
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if ((serv->flags & SERV_MARK) &&
hostname_isequal(alloc_domain, serv->domain))
{
/* Need to move down? */
if (serv->next)
{
*up = serv->next;
daemon->servers_tail->next = serv;
daemon->servers_tail = serv;
serv->next = NULL;
}
break;
}
else
up = &serv->next;
}
if (serv)
{
free(alloc_domain);
alloc_domain = serv->domain;
}
else
{
if (!(serv = whine_malloc(sizeof(struct server))))
{
free(alloc_domain);
return 0;
}
memset(serv, 0, sizeof(struct server));
/* Add to the end of the chain, for order */
@@ -684,20 +726,20 @@ int add_update_server(int flags,
else
daemon->servers = serv;
daemon->servers_tail = serv;
}
#ifdef HAVE_LOOP
serv->uid = rand32();
serv->uid = rand32();
#endif
if (interface)
safe_strncpy(serv->interface, interface, sizeof(serv->interface));
if (addr)
serv->addr = *addr;
if (source_addr)
serv->source_addr = *source_addr;
}
if (interface)
safe_strncpy(serv->interface, interface, sizeof(serv->interface));
if (addr)
serv->addr = *addr;
if (source_addr)
serv->source_addr = *source_addr;
}
serv->flags = flags;
serv->domain = alloc_domain;
serv->domain_len = strlen(alloc_domain);

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -230,9 +230,17 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
static int match_domain(struct in_addr addr, struct cond_domain *c)
{
if (!c->is6 &&
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
if (c->interface)
{
struct addrlist *al;
for (al = c->al; al; al = al->next)
if (!(al->flags & ADDRLIST_IPV6) &&
is_same_net_prefix(addr, al->addr.addr4, al->prefixlen))
return 1;
}
else if (!c->is6 &&
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
return 1;
return 0;
@@ -259,12 +267,21 @@ char *get_domain(struct in_addr addr)
static int match_domain6(struct in6_addr *addr, struct cond_domain *c)
{
u64 addrpart = addr6part(addr);
if (c->is6)
/* subnet from interface address. */
if (c->interface)
{
struct addrlist *al;
for (al = c->al; al; al = al->next)
if (al->flags & ADDRLIST_IPV6 &&
is_same_net6(addr, &al->addr.addr6, al->prefixlen))
return 1;
}
else if (c->is6)
{
if (c->prefixlen >= 64)
{
u64 addrpart = addr6part(addr);
if (is_same_net6(addr, &c->start6, 64) &&
addrpart >= addr6part(&c->start6) &&
addrpart <= addr6part(&c->end6))
@@ -273,7 +290,7 @@ static int match_domain6(struct in6_addr *addr, struct cond_domain *c)
else if (is_same_net6(addr, &c->start6, c->prefixlen))
return 1;
}
return 0;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,11 @@
#ifdef HAVE_DUMPFILE
#include <netinet/icmp6.h>
static u32 packet_count;
static void do_dump_packet(int mask, void *packet, size_t len,
union mysockaddr *src, union mysockaddr *dst, int port, int proto);
/* https://wiki.wireshark.org/Development/LibpcapFileFormat */
struct pcap_hdr_s {
@@ -79,7 +83,45 @@ void dump_init(void)
}
}
void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst)
void dump_packet_udp(int mask, void *packet, size_t len,
union mysockaddr *src, union mysockaddr *dst, int fd)
{
union mysockaddr fd_addr;
socklen_t addr_len = sizeof(fd_addr);
if (daemon->dumpfd != -1 && (mask & daemon->dump_mask))
{
/* if fd is negative it carries a port number (negated)
which we use as a source or destination when not otherwise
specified so wireshark can ID the packet.
If both src and dst are specified, set this to -1 to avoid
a spurious getsockname() call. */
int port = (fd < 0) ? -fd : -1;
/* fd >= 0 is a file descriptor and the address of that file descriptor is used
in place of a NULL src or dst. */
if (fd >= 0 && getsockname(fd, (struct sockaddr *)&fd_addr, &addr_len) != -1)
{
if (!src)
src = &fd_addr;
if (!dst)
dst = &fd_addr;
}
do_dump_packet(mask, packet, len, src, dst, port, IPPROTO_UDP);
}
}
void dump_packet_icmp(int mask, void *packet, size_t len,
union mysockaddr *src, union mysockaddr *dst)
{
if (daemon->dumpfd != -1 && (mask & daemon->dump_mask))
do_dump_packet(mask, packet, len, src, dst, -1, IPPROTO_ICMP);
}
static void do_dump_packet(int mask, void *packet, size_t len,
union mysockaddr *src, union mysockaddr *dst, int port, int proto)
{
struct ip ip;
struct ip6_hdr ip6;
@@ -96,13 +138,14 @@ void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, unio
void *iphdr;
size_t ipsz;
int rc;
/* if port != -1 it carries a port number
which we use as a source or destination when not otherwise
specified so wireshark can ID the packet.
If both src and dst are specified, set this to -1 to avoid
a spurious getsockname() call. */
udp.uh_sport = udp.uh_dport = htons(port < 0 ? 0 : port);
if (daemon->dumpfd == -1 || !(mask & daemon->dump_mask))
return;
/* So wireshark can Id the packet. */
udp.uh_sport = udp.uh_dport = htons(NAMESERVER_PORT);
if (src)
family = src->sa.sa_family;
else
@@ -115,10 +158,16 @@ void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, unio
memset(&ip6, 0, sizeof(ip6));
ip6.ip6_vfc = 6 << 4;
ip6.ip6_plen = htons(sizeof(struct udphdr) + len);
ip6.ip6_nxt = IPPROTO_UDP;
ip6.ip6_hops = 64;
if ((ip6.ip6_nxt = proto) == IPPROTO_UDP)
ip6.ip6_plen = htons(sizeof(struct udphdr) + len);
else
{
proto = ip6.ip6_nxt = IPPROTO_ICMPV6;
ip6.ip6_plen = htons(len);
}
if (src)
{
memcpy(&ip6.ip6_src, &src->in6.sin6_addr, IN6ADDRSZ);
@@ -134,9 +183,8 @@ void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, unio
/* start UDP checksum */
for (sum = 0, i = 0; i < IN6ADDRSZ; i+=2)
{
sum += ip6.ip6_src.s6_addr[i] + (ip6.ip6_src.s6_addr[i+1] << 8) ;
sum += ip6.ip6_dst.s6_addr[i] + (ip6.ip6_dst.s6_addr[i+1] << 8) ;
sum += ntohs((ip6.ip6_src.s6_addr[i] << 8) + (ip6.ip6_src.s6_addr[i+1])) ;
sum += ntohs((ip6.ip6_dst.s6_addr[i] << 8) + (ip6.ip6_dst.s6_addr[i+1])) ;
}
}
else
@@ -147,9 +195,15 @@ void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, unio
ip.ip_v = IPVERSION;
ip.ip_hl = sizeof(struct ip) / 4;
ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len);
ip.ip_ttl = IPDEFTTL;
ip.ip_p = IPPROTO_UDP;
if ((ip.ip_p = proto) == IPPROTO_UDP)
ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len);
else
{
ip.ip_len = htons(sizeof(struct ip) + len);
proto = ip.ip_p = IPPROTO_ICMP;
}
if (src)
{
@@ -170,7 +224,7 @@ void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, unio
sum = (sum & 0xffff) + (sum >> 16);
ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
/* start UDP checksum */
/* start UDP/ICMP checksum */
sum = ip.ip_src.s_addr & 0xffff;
sum += (ip.ip_src.s_addr >> 16) & 0xffff;
sum += ip.ip_dst.s_addr & 0xffff;
@@ -180,31 +234,61 @@ void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, unio
if (len & 1)
((unsigned char *)packet)[len] = 0; /* for checksum, in case length is odd. */
udp.uh_sum = 0;
udp.uh_ulen = htons(sizeof(struct udphdr) + len);
sum += htons(IPPROTO_UDP);
sum += htons(sizeof(struct udphdr) + len);
for (i = 0; i < sizeof(struct udphdr)/2; i++)
sum += ((u16 *)&udp)[i];
for (i = 0; i < (len + 1) / 2; i++)
sum += ((u16 *)packet)[i];
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
if (proto == IPPROTO_UDP)
{
/* Add Remaining part of the pseudoheader. Note that though the
IPv6 pseudoheader is very different to the IPv4 one, the
net result of this calculation is correct as long as the
packet length is less than 65536, which is fine for us. */
sum += htons(IPPROTO_UDP);
sum += htons(sizeof(struct udphdr) + len);
udp.uh_sum = 0;
udp.uh_ulen = htons(sizeof(struct udphdr) + len);
for (i = 0; i < sizeof(struct udphdr)/2; i++)
sum += ((u16 *)&udp)[i];
for (i = 0; i < (len + 1) / 2; i++)
sum += ((u16 *)packet)[i];
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
pcap_header.incl_len = pcap_header.orig_len = ipsz + sizeof(udp) + len;
}
else
{
/* ICMP - ICMPv6 packet is a superset of ICMP */
struct icmp6_hdr *icmp = packet;
/* See comment in UDP code above. */
sum += htons(proto);
sum += htons(len);
icmp->icmp6_cksum = 0;
for (i = 0; i < (len + 1) / 2; i++)
sum += ((u16 *)packet)[i];
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
icmp->icmp6_cksum = (sum == 0xffff) ? sum : ~sum;
pcap_header.incl_len = pcap_header.orig_len = ipsz + len;
}
rc = gettimeofday(&time, NULL);
pcap_header.ts_sec = time.tv_sec;
pcap_header.ts_usec = time.tv_usec;
pcap_header.incl_len = pcap_header.orig_len = ipsz + sizeof(udp) + len;
if (rc == -1 ||
!read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 0) ||
!read_write(daemon->dumpfd, iphdr, ipsz, 0) ||
!read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0) ||
(proto == IPPROTO_UDP && !read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0)) ||
!read_write(daemon->dumpfd, (void *)packet, len, 0))
my_syslog(LOG_ERR, _("failed to write packet dump"));
else if (option_bool(OPT_EXTRALOG))
my_syslog(LOG_INFO, _("%u dumping packet %u mask 0x%04x"), daemon->log_display_id, ++packet_count, mask);
else
my_syslog(LOG_INFO, _("dumping UDP packet %u mask 0x%04x"), ++packet_count, mask);
my_syslog(LOG_INFO, _("dumping packet %u mask 0x%04x"), ++packet_count, mask);
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -178,7 +178,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
memcpy(buff, datap, rdlen);
/* now, delete OPT RR */
plen = rrfilter(header, plen, 0);
plen = rrfilter(header, plen, RRFILTER_EDNS0);
/* Now, force addition of a new one */
p = NULL;
@@ -264,44 +264,62 @@ static void encoder(unsigned char *in, char *out)
out[3] = char64(in[2]);
}
/* OPT_ADD_MAC = MAC is added (if available)
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
OPT_STRIP_MAC = MAC is removed */
static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit,
union mysockaddr *l3, time_t now, int *cacheablep)
{
int maclen, replace = 2; /* can't get mac address, just delete any incoming. */
int replace = 0, maclen = 0;
unsigned char mac[DHCP_CHADDR_MAX];
char encode[18]; /* handle 6 byte MACs */
char encode[18]; /* handle 6 byte MACs ONLY */
if ((maclen = find_mac(l3, mac, 1, now)) == 6)
if ((option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX)) && (maclen = find_mac(l3, mac, 1, now)) == 6)
{
replace = 1;
*cacheablep = 0;
if (option_bool(OPT_MAC_HEX))
print_mac(encode, mac, maclen);
else
{
encoder(mac, encode);
encoder(mac+3, encode+4);
encode[8] = 0;
}
if (option_bool(OPT_STRIP_MAC))
replace = 1;
*cacheablep = 0;
if (option_bool(OPT_MAC_HEX))
print_mac(encode, mac, maclen);
else
{
encoder(mac, encode);
encoder(mac+3, encode+4);
encode[8] = 0;
}
}
else if (option_bool(OPT_STRIP_MAC))
replace = 2;
return add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
if (replace != 0 || maclen == 6)
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
return plen;
}
/* OPT_ADD_MAC = MAC is added (if available)
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
OPT_STRIP_MAC = MAC is removed */
static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit,
union mysockaddr *l3, time_t now, int *cacheablep)
{
int maclen;
int maclen = 0, replace = 0;
unsigned char mac[DHCP_CHADDR_MAX];
if ((maclen = find_mac(l3, mac, 1, now)) != 0)
if (option_bool(OPT_ADD_MAC) && (maclen = find_mac(l3, mac, 1, now)) != 0)
{
*cacheablep = 0;
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, 0);
if (option_bool(OPT_STRIP_MAC))
replace = 1;
}
else if (option_bool(OPT_STRIP_MAC))
replace = 2;
if (replace != 0 || maclen != 0)
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, replace);
return plen;
}
@@ -378,15 +396,29 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source,
return len + 4;
}
static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
/* OPT_CLIENT_SUBNET = client subnet is added
OPT_CLIENT_SUBNET + OPT_STRIP_ECS = client subnet is replaced
OPT_STRIP_ECS = client subnet is removed */
static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit,
union mysockaddr *source, int *cacheable)
{
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
int len;
int replace = 0, len = 0;
struct subnet_opt opt;
len = calc_subnet_opt(&opt, source, cacheable);
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, 0);
if (option_bool(OPT_CLIENT_SUBNET))
{
if (option_bool(OPT_STRIP_ECS))
replace = 1;
len = calc_subnet_opt(&opt, source, cacheable);
}
else if (option_bool(OPT_STRIP_ECS))
replace = 2;
else
return plen;
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace);
}
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
@@ -460,47 +492,45 @@ static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned
struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {}};
u8 *u = &opt.fields[0];
if (daemon->umbrella_org) {
PUTSHORT(UMBRELLA_ORG, u);
PUTLONG(daemon->umbrella_org, u);
}
int family = source->sa.sa_family;
PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u);
int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ;
if (daemon->umbrella_org)
{
PUTSHORT(UMBRELLA_ORG, u);
PUTLONG(daemon->umbrella_org, u);
}
PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u);
memcpy(u, get_addrp(source, family), size);
u += size;
if (option_bool(OPT_UMBRELLA_DEVID))
{
PUTSHORT(UMBRELLA_DEVICE, u);
memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ);
u += UMBRELLA_DEVICESZ;
}
if (option_bool(OPT_UMBRELLA_DEVID)) {
PUTSHORT(UMBRELLA_DEVICE, u);
memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ);
u += UMBRELLA_DEVICESZ;
}
if (daemon->umbrella_asset) {
PUTSHORT(UMBRELLA_ASSET, u);
PUTLONG(daemon->umbrella_asset, u);
}
int len = u - &opt.magic[0];
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, len, 0, 1);
if (daemon->umbrella_asset)
{
PUTSHORT(UMBRELLA_ASSET, u);
PUTLONG(daemon->umbrella_asset, u);
}
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1);
}
/* Set *check_subnet if we add a client subnet option, which needs to checked
in the reply. Set *cacheable to zero if we add an option which the answer
may depend on. */
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
union mysockaddr *source, time_t now, int *check_subnet, int *cacheable)
union mysockaddr *source, time_t now, int *cacheable)
{
*check_subnet = 0;
*cacheable = 1;
if (option_bool(OPT_ADD_MAC))
plen = add_mac(header, plen, limit, source, now, cacheable);
if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX))
plen = add_dns_client(header, plen, limit, source, now, cacheable);
plen = add_mac(header, plen, limit, source, now, cacheable);
plen = add_dns_client(header, plen, limit, source, now, cacheable);
if (daemon->dns_client_id)
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID,
@@ -509,11 +539,7 @@ size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *l
if (option_bool(OPT_UMBRELLA))
plen = add_umbrella_opt(header, plen, limit, source, cacheable);
if (option_bool(OPT_CLIENT_SUBNET))
{
plen = add_source_addr(header, plen, limit, source, cacheable);
*check_subnet = 1;
}
plen = add_source_addr(header, plen, limit, source, cacheable);
return plen;
}

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,7 @@
The hash used is SHA-256. If we're building with DNSSEC support,
we use the Nettle cypto library. If not, we prefer not to
add a dependency on Nettle, and use a stand-alone implementaion.
add a dependency on Nettle, and use a stand-alone implementation.
*/
#include "dnsmasq.h"
@@ -55,7 +55,7 @@ unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name
char *cp, c;
if (!extract_name(header, plen, &p, name, 1, 4))
break; /* bad packet */
return NULL; /* bad packet */
for (cp = name; (c = *cp); cp++)
if (c >= 'A' && c <= 'Z')
@@ -67,7 +67,7 @@ unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name
p += 4;
if (!CHECK_LEN(header, p, plen, 0))
break; /* bad packet */
return NULL; /* bad packet */
}
hash->digest(ctx, hash->digest_size, digest);
@@ -109,7 +109,7 @@ unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name
char *cp, c;
if (!extract_name(header, plen, &p, name, 1, 4))
break; /* bad packet */
return NULL; /* bad packet */
for (cp = name; (c = *cp); cp++)
if (c >= 'A' && c <= 'Z')
@@ -121,7 +121,7 @@ unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name
p += 4;
if (!CHECK_LEN(header, p, plen, 0))
break; /* bad packet */
return NULL; /* bad packet */
}
sha256_final(&ctx, digest);

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -233,8 +233,13 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
is6 = (data.flags != AF_INET);
data.action = ACTION_ARP;
}
else
continue;
else if (data.action == ACTION_RELAY_SNOOP)
{
is6 = 1;
action_str = "relay-snoop";
}
else
continue;
/* stringify MAC into dhcp_buff */
p = daemon->dhcp_buff;
@@ -286,7 +291,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
char *dot;
hostname = (char *)buf;
hostname[data.hostname_len - 1] = 0;
if (data.action != ACTION_TFTP)
if (data.action != ACTION_TFTP && data.action != ACTION_RELAY_SNOOP)
{
if (!legal_hostname(hostname))
hostname = NULL;
@@ -332,6 +337,24 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
}
}
else if (data.action == ACTION_RELAY_SNOOP)
{
lua_getglobal(lua, "snoop");
if (lua_type(lua, -1) != LUA_TFUNCTION)
lua_pop(lua, 1); /* tftp function optional */
else
{
lua_pushstring(lua, action_str); /* arg1 - action */
lua_newtable(lua); /* arg2 - data table */
lua_pushstring(lua, daemon->addrbuff);
lua_setfield(lua, -2, "client_address");
lua_pushstring(lua, hostname);
lua_setfield(lua, -2, "prefix");
lua_pushstring(lua, data.interface);
lua_setfield(lua, -2, "client_interface");
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
}
}
else if (data.action == ACTION_ARP)
{
lua_getglobal(lua, "arp");
@@ -398,6 +421,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
end = extradata + data.ed_len;
buf = extradata;
lua_pushnumber(lua, data.ed_len == 0 ? 1 : 0);
lua_setfield(lua, -2, "data_missing");
if (!is6)
buf = grab_extradata_lua(buf, end, "vendor_class");
@@ -425,15 +451,17 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
buf = grab_extradata_lua(buf, end, "subscriber_id");
buf = grab_extradata_lua(buf, end, "remote_id");
}
buf = grab_extradata_lua(buf, end, "requested_options");
buf = grab_extradata_lua(buf, end, "mud_url");
buf = grab_extradata_lua(buf, end, "tags");
if (is6)
buf = grab_extradata_lua(buf, end, "relay_address");
else if (data.giaddr.s_addr != 0)
{
inet_ntop(AF_INET, &data.giaddr, daemon->addrbuff, ADDRSTRLEN);
lua_pushstring(lua, daemon->addrbuff);
inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN);
lua_pushstring(lua, daemon->dhcp_buff2);
lua_setfield(lua, -2, "relay_address");
}
@@ -553,7 +581,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
close(pipeout[1]);
}
if (data.action != ACTION_TFTP && data.action != ACTION_ARP)
if (data.action != ACTION_TFTP && data.action != ACTION_ARP && data.action != ACTION_RELAY_SNOOP)
{
#ifdef HAVE_DHCP6
my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
@@ -576,6 +604,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
end = extradata + data.ed_len;
buf = extradata;
if (data.ed_len == 0)
my_setenv("DNSMASQ_DATA_MISSING", "1", &err);
if (!is6)
buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
@@ -604,18 +635,19 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
buf = grab_extradata(buf, end, "DNSMASQ_REQUESTED_OPTIONS", &err);
}
buf = grab_extradata(buf, end, "DNSMASQ_REQUESTED_OPTIONS", &err);
buf = grab_extradata(buf, end, "DNSMASQ_MUD_URL", &err);
buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
if (is6)
buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
else
{
const char *giaddr = NULL;
if (data.giaddr.s_addr != 0)
giaddr = inet_ntop(AF_INET, &data.giaddr, daemon->addrbuff, ADDRSTRLEN);
giaddr = inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN);
my_setenv("DNSMASQ_RELAY_ADDRESS", giaddr, &err);
}
@@ -640,6 +672,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
close(pipefd[0]);
if (data.action == ACTION_RELAY_SNOOP)
strcpy(daemon->packet, data.interface);
p = strrchr(daemon->lease_change_command, '/');
if (err == 0)
{
@@ -810,6 +845,29 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
bytes_in_buf = p - (unsigned char *)buf;
}
#ifdef HAVE_DHCP6
void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *prefix, int prefix_len)
{
/* no script */
if (daemon->helperfd == -1)
return;
inet_ntop(AF_INET6, prefix, daemon->addrbuff, ADDRSTRLEN);
/* 5 for /nnn and zero on the end of the prefix. */
buff_alloc(sizeof(struct script_data) + ADDRSTRLEN + 5);
memset(buf, 0, sizeof(struct script_data));
buf->action = ACTION_RELAY_SNOOP;
buf->addr6 = *client;
buf->hostname_len = sprintf((char *)(buf+1), "%s/%u", daemon->addrbuff, prefix_len) + 1;
indextoname(daemon->dhcp6fd, if_index, buf->interface);
bytes_in_buf = sizeof(struct script_data) + buf->hostname_len;
}
#endif
#ifdef HAVE_TFTP
/* This nastily re-uses DHCP-fields for TFTP stuff */
void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -133,81 +133,112 @@ void inotify_dnsmasq_init()
}
}
static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *path)
{
/* Check if this file is already known in dd->files */
struct hostsfile *ah = NULL;
for(ah = dd->files; ah; ah = ah->next)
if(ah && ah->fname && strcmp(path, ah->fname) == 0)
return ah;
/* Not known, create new hostsfile record for this dyndir */
struct hostsfile *newah = NULL;
if(!(newah = whine_malloc(sizeof(struct hostsfile))))
return NULL;
/* Add this file to the tip of the linked list */
newah->next = dd->files;
dd->files = newah;
/* Copy flags, set index and the full file path */
newah->flags = dd->flags;
newah->index = daemon->host_index++;
newah->fname = path;
return newah;
}
/* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
{
struct hostsfile *ah;
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
struct dyndir *dd;
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
{
DIR *dir_stream = NULL;
struct dirent *ent;
struct stat buf;
if (!(ah->flags & flag))
if (!(dd->flags & flag))
continue;
if (stat(ah->fname, &buf) == -1)
if (stat(dd->dname, &buf) == -1)
{
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
ah->fname, strerror(errno));
dd->dname, strerror(errno));
continue;
}
if (!(S_ISDIR(buf.st_mode)))
{
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
ah->fname, _("not a directory"));
dd->dname, _("not a directory"));
continue;
}
if (!(ah->flags & AH_WD_DONE))
if (!(dd->flags & AH_WD_DONE))
{
ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
ah->flags |= AH_WD_DONE;
dd->wd = inotify_add_watch(daemon->inotifyfd, dd->dname, IN_CLOSE_WRITE | IN_MOVED_TO | IN_DELETE);
dd->flags |= AH_WD_DONE;
}
/* Read contents of dir _after_ calling add_watch, in the hope of avoiding
a race which misses files being added as we start */
if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
if (dd->wd == -1 || !(dir_stream = opendir(dd->dname)))
{
my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
ah->fname, strerror(errno));
dd->dname, strerror(errno));
continue;
}
while ((ent = readdir(dir_stream)))
{
size_t lendir = strlen(ah->fname);
size_t lendir = strlen(dd->dname);
size_t lenfile = strlen(ent->d_name);
char *path;
/* ignore emacs backups and dotfiles */
if (lenfile == 0 ||
ent->d_name[lenfile - 1] == '~' ||
(ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
ent->d_name[0] == '.')
continue;
if ((path = whine_malloc(lendir + lenfile + 2)))
{
strcpy(path, ah->fname);
struct hostsfile *ah;
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, ent->d_name);
if (!(ah = dyndir_addhosts(dd, path)))
{
free(path);
continue;
}
/* ignore non-regular files */
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
{
if (ah->flags & AH_HOSTS)
if (dd->flags & AH_HOSTS)
total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
#ifdef HAVE_DHCP
else if (ah->flags & (AH_DHCP_HST | AH_DHCP_OPT))
option_read_dynfile(path, ah->flags);
else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
option_read_dynfile(path, dd->flags);
#endif
}
free(path);
}
}
@@ -218,7 +249,7 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
int inotify_check(time_t now)
{
int hit = 0;
struct hostsfile *ah;
struct dyndir *dd;
while (1)
{
@@ -249,36 +280,51 @@ int inotify_check(time_t now)
if (res->wd == in->wd && strcmp(res->file, in->name) == 0)
hit = 1;
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
if (ah->wd == in->wd)
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
if (dd->wd == in->wd)
{
size_t lendir = strlen(ah->fname);
size_t lendir = strlen(dd->dname);
char *path;
if ((path = whine_malloc(lendir + in->len + 2)))
{
strcpy(path, ah->fname);
struct hostsfile *ah = NULL;
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, in->name);
my_syslog(LOG_INFO, _("inotify, new or changed file %s"), path);
if (ah->flags & AH_HOSTS)
/* Is this is a deletion event? */
if (in->mask & IN_DELETE)
my_syslog(LOG_INFO, _("inotify: %s removed"), path);
else
my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
if (dd->flags & AH_HOSTS)
{
read_hostsfile(path, ah->index, 0, NULL, 0);
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->doing_dhcp6)
if ((ah = dyndir_addhosts(dd, path)))
{
/* Propagate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns(1);
}
const unsigned int removed = cache_remove_uid(ah->index);
if (removed > 0)
my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, path);
/* (Re-)load hostsfile only if this event isn't triggered by deletion */
if (!(in->mask & IN_DELETE))
read_hostsfile(path, ah->index, 0, NULL, 0);
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->doing_dhcp6)
{
/* Propagate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns(1);
}
#endif
}
}
#ifdef HAVE_DHCP
else if (ah->flags & AH_DHCP_HST)
else if (dd->flags & AH_DHCP_HST)
{
if (option_read_dynfile(path, AH_DHCP_HST))
{
@@ -289,11 +335,12 @@ int inotify_check(time_t now)
lease_update_dns(1);
}
}
else if (ah->flags & AH_DHCP_OPT)
else if (dd->flags & AH_DHCP_OPT)
option_read_dynfile(path, AH_DHCP_OPT);
#endif
free(path);
if (!ah)
free(path);
}
}
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1180,17 +1180,11 @@ void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned
if ((lease->extradata_size - lease->extradata_len) < (len + 1))
{
size_t newsz = lease->extradata_len + len + 100;
unsigned char *new = whine_malloc(newsz);
unsigned char *new = whine_realloc(lease->extradata, newsz);
if (!new)
return;
if (lease->extradata)
{
memcpy(new, lease->extradata, lease->extradata_len);
free(lease->extradata);
}
lease->extradata = new;
lease->extradata_size = newsz;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -100,10 +100,23 @@ int log_start(struct passwd *ent_pw, int errfd)
/* If we're running as root and going to change uid later,
change the ownership here so that the file is always owned by
the dnsmasq user. Then logrotate can just copy the owner.
Failure of the chown call is OK, (for instance when started as non-root) */
if (log_to_file && !log_stderr && ent_pw && ent_pw->pw_uid != 0 &&
fchown(log_fd, ent_pw->pw_uid, -1) != 0)
ret = errno;
Failure of the chown call is OK, (for instance when started as non-root).
If we've created a file with group-id root, we also make
the file group-writable. This gives processes in the root group
write access to the file and avoids the problem that on some systems,
once the file is owned by the dnsmasq user, it can't be written
whilst dnsmasq is running as root during startup.
*/
if (log_to_file && !log_stderr && ent_pw && ent_pw->pw_uid != 0)
{
struct stat ls;
if (getgid() == 0 && fstat(log_fd, &ls) == 0 && ls.st_gid == 0 &&
(ls.st_mode & S_IWGRP) == 0)
(void)fchmod(log_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
if (fchown(log_fd, ent_pw->pw_uid, -1) != 0)
ret = errno;
}
return ret;
}
@@ -118,7 +131,7 @@ int log_reopen(char *log_file)
/* NOTE: umask is set to 022 by the time this gets called */
if (log_file)
log_fd = open(log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP);
log_fd = open(log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP);
else
{
#if defined(HAVE_SOLARIS_NETWORK) || defined(__ANDROID__)

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,6 +22,8 @@ const char * metric_names[] = {
"dns_queries_forwarded",
"dns_auth_answered",
"dns_local_answered",
"dns_stale_answered",
"dns_unanswered",
"bootp",
"pxe",
"dhcp_ack",
@@ -42,3 +44,23 @@ const char * metric_names[] = {
const char* get_metric_name(int i) {
return metric_names[i];
}
void clear_metrics(void)
{
int i;
struct server *serv;
for (i = 0; i < __METRIC_MAX; i++)
daemon->metrics[i] = 0;
for (serv = daemon->servers; serv; serv = serv->next)
{
serv->queries = 0;
serv->failed_queries = 0;
serv->failed_queries = 0;
serv->retrys = 0;
serv->nxdomain_replies = 0;
serv->query_latency = 0;
}
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@ enum {
METRIC_DNS_QUERIES_FORWARDED,
METRIC_DNS_AUTH_ANSWERED,
METRIC_DNS_LOCAL_ANSWERED,
METRIC_DNS_STALE_ANSWERED,
METRIC_DNS_UNANSWERED_QUERY,
METRIC_BOOTP,
METRIC_PXE,
METRIC_DHCPACK,
@@ -41,3 +43,4 @@ enum {
};
const char* get_metric_name(int);
void clear_metrics(void);

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -66,17 +66,10 @@ char *netlink_init(void)
addr.nl_pad = 0;
addr.nl_pid = 0; /* autobind */
addr.nl_groups = RTMGRP_IPV4_ROUTE;
if (option_bool(OPT_CLEVERBIND))
addr.nl_groups |= RTMGRP_IPV4_IFADDR;
addr.nl_groups |= RTMGRP_IPV4_IFADDR;
addr.nl_groups |= RTMGRP_IPV6_ROUTE;
if (option_bool(OPT_CLEVERBIND))
addr.nl_groups |= RTMGRP_IPV6_IFADDR;
addr.nl_groups |= RTMGRP_IPV6_IFADDR;
#ifdef HAVE_DHCP6
if (daemon->doing_ra || daemon->doing_dhcp6)
addr.nl_groups |= RTMGRP_IPV6_IFADDR;
#endif
/* May not be able to have permission to set multicast groups don't die in that case */
if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1)
{
@@ -265,7 +258,16 @@ int iface_enumerate(int family, void *parm, int (*callback)())
while (RTA_OK(rta, len1))
{
if (rta->rta_type == IFA_ADDRESS)
/*
* Important comment: (from if_addr.h)
* IFA_ADDRESS is prefix address, rather than local interface address.
* It makes no difference for normally configured broadcast interfaces,
* but for point-to-point IFA_ADDRESS is DESTINATION address,
* local address is supplied in IFA_LOCAL attribute.
*/
if (rta->rta_type == IFA_LOCAL)
addrp = ((struct in6_addr *)(rta+1));
else if (rta->rta_type == IFA_ADDRESS && !addrp)
addrp = ((struct in6_addr *)(rta+1));
else if (rta->rta_type == IFA_CACHEINFO)
{

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -114,13 +114,8 @@ int iface_check(int family, union all_addr *addr, char *name, int *auth)
struct iname *tmp;
int ret = 1, match_addr = 0;
/* Note: have to check all and not bail out early, so that we set the
"used" flags.
May be called with family == AF_LOCALto check interface by name only. */
if (auth)
*auth = 0;
/* Note: have to check all and not bail out early, so that we set the "used" flags.
May be called with family == AF_LOCAL to check interface by name only. */
if (daemon->if_names || daemon->if_addrs)
{
@@ -149,25 +144,29 @@ int iface_check(int family, union all_addr *addr, char *name, int *auth)
if (tmp->name && wildcard_match(tmp->name, name))
ret = 0;
for (tmp = daemon->authinterface; tmp; tmp = tmp->next)
if (tmp->name)
{
if (strcmp(tmp->name, name) == 0 &&
(tmp->addr.sa.sa_family == 0 || tmp->addr.sa.sa_family == family))
break;
}
else if (addr && tmp->addr.sa.sa_family == AF_INET && family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == addr->addr4.s_addr)
break;
else if (addr && tmp->addr.sa.sa_family == AF_INET6 && family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr6))
break;
if (tmp && auth)
if (auth)
{
*auth = 1;
ret = 1;
*auth = 0;
for (tmp = daemon->authinterface; tmp; tmp = tmp->next)
if (tmp->name)
{
if (strcmp(tmp->name, name) == 0 &&
(tmp->addr.sa.sa_family == 0 || tmp->addr.sa.sa_family == family))
break;
}
else if (addr && tmp->addr.sa.sa_family == AF_INET && family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == addr->addr4.s_addr)
break;
else if (addr && tmp->addr.sa.sa_family == AF_INET6 && family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr6))
break;
if (tmp)
{
*auth = 1;
ret = 1;
}
}
return ret;
@@ -232,6 +231,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
union mysockaddr *addr, struct in_addr netmask, int prefixlen, int iface_flags)
{
struct irec *iface;
struct cond_domain *cond;
int loopback;
struct ifreq ifr;
int tftp_ok = !!option_bool(OPT_TFTP);
@@ -360,7 +360,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
if (int_name->flags & INP4)
{
if (netmask.s_addr == 0xffff)
if (netmask.s_addr == 0xffffffff)
continue;
newaddr.s_addr = (addr->in.sin_addr.s_addr & netmask.s_addr) |
@@ -454,7 +454,37 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
}
}
}
/* Update addresses for domain=<domain>,<interface> */
for (cond = daemon->cond_domain; cond; cond = cond->next)
if (cond->interface && strncmp(label, cond->interface, IF_NAMESIZE) == 0)
{
struct addrlist *al;
if (param->spare)
{
al = param->spare;
param->spare = al->next;
}
else
al = whine_malloc(sizeof(struct addrlist));
if (addr->sa.sa_family == AF_INET)
{
al->addr.addr4 = addr->in.sin_addr;
al->flags = 0;
}
else
{
al->addr.addr6 = addr->in6.sin6_addr;
al->flags = ADDRLIST_IPV6;
}
al->prefixlen = prefixlen;
al->next = cond->al;
cond->al = al;
}
/* check whether the interface IP has been added already
we call this routine multiple times. */
for (iface = daemon->interfaces; iface; iface = iface->next)
@@ -692,6 +722,7 @@ int enumerate_interfaces(int reset)
int errsave, ret = 1;
struct addrlist *addr, *tmp;
struct interface_name *intname;
struct cond_domain *cond;
struct irec *iface;
#ifdef HAVE_AUTH
struct auth_zone *zone;
@@ -751,6 +782,19 @@ again:
intname->addr = NULL;
}
/* remove addresses stored against cond-domains. */
for (cond = daemon->cond_domain; cond; cond = cond->next)
{
for (addr = cond->al; addr; addr = tmp)
{
tmp = addr->next;
addr->next = spare;
spare = addr;
}
cond->al = NULL;
}
/* Remove list of addresses of local interfaces */
for (addr = daemon->interface_addrs; addr; addr = tmp)
{
@@ -1327,7 +1371,7 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
or both are set. Otherwise use the OS's random ephemeral port allocation by
leaving port == 0 and tries == 1 */
ports_avail = daemon->max_port - daemon->min_port + 1;
tries = ports_avail < 30 ? 3 * ports_avail : 100;
tries = (ports_avail < SMALL_PORT_RANGE) ? ports_avail : 100;
port = htons(daemon->min_port + (rand16() % ports_avail));
}
@@ -1356,7 +1400,16 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
if (--tries == 0)
return 0;
port = htons(daemon->min_port + (rand16() % ports_avail));
/* For small ranges, do a systematic search, not a random one. */
if (ports_avail < SMALL_PORT_RANGE)
{
unsigned short hport = ntohs(port);
if (hport++ == daemon->max_port)
hport = daemon->min_port;
port = htons(hport);
}
else
port = htons(daemon->min_port + (rand16() % ports_avail));
}
if (!is_tcp && ifindex > 0)
@@ -1743,6 +1796,8 @@ int reload_servers(char *fname)
/* Called when addresses are added or deleted from an interface */
void newaddress(time_t now)
{
struct dhcp_relay *relay;
(void)now;
if (option_bool(OPT_CLEVERBIND) || option_bool(OPT_LOCAL_SERVICE) ||
@@ -1751,6 +1806,12 @@ void newaddress(time_t now)
if (option_bool(OPT_CLEVERBIND))
create_bound_listeners(0);
#ifdef HAVE_DHCP
/* clear cache of subnet->relay index */
for (relay = daemon->relay4; relay; relay = relay->next)
relay->iface_index = 0;
#endif
#ifdef HAVE_DHCP6
if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
@@ -1761,5 +1822,8 @@ void newaddress(time_t now)
if (daemon->doing_dhcp6)
lease_find_interfaces(now);
for (relay = daemon->relay6; relay; relay = relay->next)
relay->iface_index = 0;
#endif
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -129,9 +129,9 @@ int is_valid_dns_name(const char *value)
size_t num_bytes = 0;
size_t num_labels = 0;
const char *label = NULL;
const char *c, *label = NULL;
int is_label_numeric = 1;
for (const char *c = value;; c++)
for (c = value;; c++)
{
if (*c &&
*c != '-' && *c != '.' &&
@@ -242,11 +242,11 @@ int is_valid_dns_name_pattern(const char *value)
size_t num_bytes = 0;
size_t num_labels = 0;
const char *label = NULL;
const char *c, *label = NULL;
int is_label_numeric = 1;
size_t num_wildcards = 0;
int previous_label_has_wildcard = 1;
for (const char *c = value;; c++)
for (c = value;; c++)
{
if (*c &&
*c != '*' && /* Wildcard. */

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -96,28 +96,21 @@ void poll_listen(int fd, short event)
pollfds[i].events |= event;
else
{
if (arrsize != nfds)
memmove(&pollfds[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd));
else
if (arrsize == nfds)
{
/* Array too small, extend. */
struct pollfd *new;
arrsize = (arrsize == 0) ? 64 : arrsize * 2;
if (!(new = whine_malloc(arrsize * sizeof(struct pollfd))))
if (!(new = whine_realloc(pollfds, arrsize * sizeof(struct pollfd))))
return;
if (pollfds)
{
memcpy(new, pollfds, i * sizeof(struct pollfd));
memcpy(&new[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd));
free(pollfds);
}
pollfds = new;
}
memmove(&pollfds[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd));
pollfds[i].fd = fd;
pollfds[i].events = event;
nfds++;

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -123,7 +123,11 @@ void ra_start_unsolicited(time_t now, struct dhcp_context *context)
and pick up new interfaces */
if (context)
context->ra_short_period_start = context->ra_time = now;
{
context->ra_short_period_start = now;
/* start after 1 second to get logging right at startup. */
context->ra_time = now + 1;
}
else
for (context = daemon->dhcp6; context; context = context->next)
if (!(context->flags & CONTEXT_TEMPLATE))
@@ -162,7 +166,7 @@ void icmp6_packet(time_t now)
return;
packet = (unsigned char *)daemon->outpacket.iov_base;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{
@@ -187,24 +191,36 @@ void icmp6_packet(time_t now)
if (packet[1] != 0)
return;
if (packet[0] == ICMP6_ECHO_REPLY)
lease_ping_reply(&from.sin6_addr, packet, interface);
else if (packet[0] == ND_ROUTER_SOLICIT)
{
char *mac = "";
struct dhcp_bridge *bridge, *alias;
ssize_t rem;
unsigned char *p;
int opt_sz;
#ifdef HAVE_DUMPFILE
dump_packet_icmp(DUMP_RA, (void *)packet, sz, (union mysockaddr *)&from, NULL);
#endif
/* look for link-layer address option for logging */
if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
for (rem = sz - 8, p = &packet[8]; rem >= 2; rem -= opt_sz, p += opt_sz)
{
if ((packet[9] * 8 - 2) * 3 - 1 >= MAXDNAME) {
return;
}
print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2);
mac = daemon->namebuff;
opt_sz = p[1] * 8;
if (opt_sz == 0 || opt_sz > rem)
return; /* Bad packet */
if (p[0] == ICMP6_OPT_SOURCE_MAC && ((opt_sz - 2) * 3 - 1 < MAXDNAME))
{
print_mac(daemon->namebuff, &p[2], opt_sz - 2);
mac = daemon->namebuff;
}
}
if (!option_bool(OPT_QUIET_RA))
my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
@@ -543,6 +559,16 @@ static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_ad
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &send_iface, sizeof(send_iface));
}
#ifdef HAVE_DUMPFILE
{
struct sockaddr_in6 src;
src.sin6_family = AF_INET6;
src.sin6_addr = parm.link_local;
dump_packet_icmp(DUMP_RA, (void *)daemon->outpacket.iov_base, save_counter(-1), (union mysockaddr *)&src, (union mysockaddr *)&addr);
}
#endif
while (retry_send(sendto(daemon->icmp6fd, daemon->outpacket.iov_base,
save_counter(-1), 0, (struct sockaddr *)&addr,
sizeof(addr))));

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -538,7 +538,9 @@ static int print_txt(struct dns_header *header, const size_t qlen, char *name,
/* Note that the following code can create CNAME chains that don't point to a real record,
either because of lack of memory, or lack of SOA records. These are treated by the cache code as
expired and cleaned out that way.
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
Return 1 if we reject an address because it look like part of dns-rebinding attack.
Return 2 if the packet is malformed.
*/
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind,
int no_cache_dnssec, int secure, int *doctored)
@@ -589,7 +591,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
namep = p = (unsigned char *)(header+1);
if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4))
return 0; /* bad packet */
return 2; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
@@ -607,13 +609,13 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{
cname_loop:
if (!(p1 = skip_questions(header, qlen)))
return 0;
return 2;
for (j = 0; j < ntohs(header->ancount); j++)
{
int secflag = 0;
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
return 0; /* bad packet */
return 2; /* bad packet */
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
@@ -651,7 +653,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0;
return 2;
if (aqtype == T_CNAME)
{
@@ -677,7 +679,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 0; /* bad packet */
return 2; /* bad packet */
}
}
@@ -722,14 +724,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
cname_loop1:
if (!(p1 = skip_questions(header, qlen)))
return 0;
return 2;
for (j = 0; j < ntohs(header->ancount); j++)
{
int secflag = 0;
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
return 0; /* bad packet */
return 2; /* bad packet */
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
@@ -747,7 +749,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 0; /* bad packet */
return 2; /* bad packet */
continue;
}
@@ -790,7 +792,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
namep = p1;
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0;
return 2;
if (qtype != T_CNAME)
goto cname_loop1;
@@ -813,25 +815,25 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
unsigned char *tmp = namep;
if (!CHECK_LEN(header, p1, qlen, 6))
return 0; /* bad packet */
return 2; /* bad packet */
GETSHORT(addr.srv.priority, p1);
GETSHORT(addr.srv.weight, p1);
GETSHORT(addr.srv.srvport, p1);
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0;
return 2;
addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */
if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen)))
return 0;
/* we overwrote the original name, so get it back here. */
if (!extract_name(header, qlen, &tmp, name, 1, 0))
return 0;
return 2;
}
else if (flags & (F_IPV4 | F_IPV6))
{
/* copy address into aligned storage */
if (!CHECK_LEN(header, p1, qlen, addrlen))
return 0; /* bad packet */
return 2; /* bad packet */
memcpy(&addr, p1, addrlen);
/* check for returned address in private space */
@@ -875,7 +877,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (aqtype == T_TXT)
{
if (!print_txt(header, qlen, name, p1, ardlen, secflag))
return 0;
return 2;
}
else
log_query(flags | F_FORWARD | secflag | F_UPSTREAM, name, &addr, NULL, aqtype);
@@ -883,7 +885,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 0; /* bad packet */
return 2; /* bad packet */
}
if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN)))
@@ -1360,8 +1362,15 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
#undef CHECK_LIMIT
}
static int crec_isstale(struct crec *crecp, time_t now)
{
return (!(crecp->flags & F_IMMORTAL)) && difftime(crecp->ttd, now) < 0;
}
static unsigned long crec_ttl(struct crec *crecp, time_t now)
{
signed long ttl = difftime(crecp->ttd, now);
/* Return 0 ttl for DHCP entries, which might change
before the lease expires, unless configured otherwise. */
@@ -1370,8 +1379,8 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
int conf_ttl = daemon->use_dhcp_ttl ? daemon->dhcp_ttl : daemon->local_ttl;
/* Apply ceiling of actual lease length to configured TTL. */
if (!(crecp->flags & F_IMMORTAL) && (crecp->ttd - now) < conf_ttl)
return crecp->ttd - now;
if (!(crecp->flags & F_IMMORTAL) && ttl < conf_ttl)
return ttl;
return conf_ttl;
}
@@ -1380,9 +1389,13 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
if (crecp->flags & F_IMMORTAL)
return crecp->ttd;
/* Stale cache entries. */
if (ttl < 0)
return 0;
/* Return the Max TTL value if it is lower than the actual TTL */
if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))
return crecp->ttd - now;
if (daemon->max_ttl == 0 || ((unsigned)ttl < daemon->max_ttl))
return ttl;
else
return daemon->max_ttl;
}
@@ -1395,7 +1408,8 @@ static int cache_validated(const struct crec *crecp)
/* return zero if we can't answer from cache, or packet size if we can */
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask,
time_t now, int ad_reqd, int do_bit, int have_pseudoheader)
time_t now, int ad_reqd, int do_bit, int have_pseudoheader,
int *stale)
{
char *name = daemon->namebuff;
unsigned char *p, *ansp;
@@ -1411,6 +1425,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
size_t len;
int rd_bit = (header->hb3 & HB3_RD);
if (stale)
*stale = 0;
/* never answer queries with RD unset, to avoid cache snooping. */
if (ntohs(header->ancount) != 0 ||
ntohs(header->nscount) != 0 ||
@@ -1459,13 +1476,22 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
while (--count != 0 && (crecp = cache_find_by_name(NULL, name, now, F_CNAME | F_NXDOMAIN)))
{
char *cname_target;
int stale_flag = 0;
if (crec_isstale(crecp, now))
{
if (stale)
*stale = 1;
stale_flag = F_STALE;
}
if (crecp->flags & F_NXDOMAIN)
{
if (qtype == T_CNAME)
{
if (!dryrun)
log_query(crecp->flags, name, NULL, record_source(crecp->uid), 0);
log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0);
auth = 0;
nxdomain = 1;
ans = 1;
@@ -1487,7 +1513,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!dryrun)
{
log_query(crecp->flags, name, NULL, record_source(crecp->uid), 0);
log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_CNAME, C_IN, "d", cname_target))
@@ -1597,7 +1623,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (addrlist)
break;
else
else if (!(intr->flags & INP4))
while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
intr = intr->next;
}
@@ -1612,7 +1638,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (addrlist)
break;
else
else if (!(intr->flags & INP6))
while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
intr = intr->next;
}
@@ -1656,22 +1682,33 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
{
do
{
int stale_flag = 0;
if (crec_isstale(crecp, now))
{
if (stale)
*stale = 1;
stale_flag = F_STALE;
}
/* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
continue;
if (!(crecp->flags & F_DNSSECOK))
sec_data = 0;
ans = 1;
if (crecp->flags & F_NEG)
{
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL, 0);
log_query(stale_flag | (crecp->flags & ~F_FORWARD), name, &addr, NULL, 0);
}
else
{
@@ -1679,7 +1716,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
auth = 0;
if (!dryrun)
{
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
log_query(stale_flag | (crecp->flags & ~F_FORWARD), cache_get_name(crecp), &addr,
record_source(crecp->uid), 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
@@ -1788,7 +1825,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if ((crecp = cache_find_by_name(NULL, name, now, flag | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))))
{
int localise = 0;
/* See if a putative address is on the network from which we received
the query, is so we'll filter other answers. */
if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4)
@@ -1810,6 +1847,16 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
(rd_bit && (!do_bit || cache_validated(crecp)) ))
do
{
int stale_flag = 0;
if (crec_isstale(crecp, now))
{
if (stale)
*stale = 1;
stale_flag = F_STALE;
}
/* don't answer wildcard queries with data not from /etc/hosts
or DHCP leases */
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
@@ -1825,7 +1872,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags, name, NULL, NULL, 0);
log_query(stale_flag | crecp->flags, name, NULL, NULL, 0);
}
else
{
@@ -1842,7 +1889,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
ans = 1;
if (!dryrun)
{
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr,
log_query(stale_flag | (crecp->flags & ~F_REVERSE), name, &crecp->addr,
record_source(crecp->uid), 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
@@ -1953,6 +2000,15 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))))
do
{
int stale_flag = 0;
if (crec_isstale(crecp, now))
{
if (stale)
*stale = 1;
stale_flag = F_STALE;
}
/* don't answer wildcard queries with data not from /etc/hosts or dhcp leases, except for NXDOMAIN */
if (qtype == T_ANY && !(crecp->flags & (F_NXDOMAIN)))
break;
@@ -1968,12 +2024,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags, name, NULL, NULL, 0);
log_query(stale_flag | crecp->flags, name, NULL, NULL, 0);
}
else if (!dryrun)
{
char *target = blockdata_retrieve(crecp->addr.srv.target, crecp->addr.srv.targetlen, NULL);
log_query(crecp->flags, name, NULL, NULL, 0);
log_query(stale_flag | crecp->flags, name, NULL, NULL, 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), NULL, T_SRV, C_IN, "sssd",

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1153,15 +1153,22 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
tagif_netid = run_tag_if(&context->netid);
}
log_tags(tagif_netid, ntohl(mess->xid));
apply_delay(mess->xid, recvtime, tagif_netid);
if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0))
{
rapid_commit = 1;
/* If a lease exists for this host and another address, squash it. */
if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
{
lease_prune(lease, now);
lease = NULL;
}
goto rapid_commit;
}
log_tags(tagif_netid, ntohl(mess->xid));
daemon->metrics[METRIC_DHCPOFFER]++;
log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
@@ -1420,21 +1427,18 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
/* DNSMASQ_REQUESTED_OPTIONS */
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 1)))
{
int len = option_len(opt);
int i, len = option_len(opt);
unsigned char *rop = option_ptr(opt, 0);
char *q = daemon->namebuff;
int i;
for (i = 0; i < len; i++)
{
q += snprintf(q, MAXDNAME - (q - daemon->namebuff), "%d%s", rop[i], i + 1 == len ? "" : ",");
}
lease_add_extradata(lease, (unsigned char *)daemon->namebuff, (q - daemon->namebuff), 0);
lease_add_extradata(lease, (unsigned char *)daemon->namebuff,
sprintf(daemon->namebuff, "%u", rop[i]), (i + 1) == len ? 0 : ',');
}
else
{
add_extradata_opt(lease, NULL);
}
lease_add_extradata(lease, NULL, 0, 0);
add_extradata_opt(lease, option_find(mess, sz, OPTION_MUD_URL_V4, 1));
/* space-concat tag set */
if (!tagif_netid)
add_extradata_opt(lease, NULL);
@@ -2200,8 +2204,9 @@ static int pxe_uefi_workaround(int pxe_arch, struct dhcp_netid *netid, struct dh
inet_ntop(AF_INET, &mess->siaddr, (char *)mess->sname, INET_ADDRSTRLEN);
}
snprintf((char *)mess->file, sizeof(mess->file),
strchr(found->basename, '.') ? "%s" : "%s.0", found->basename);
if (found->basename)
snprintf((char *)mess->file, sizeof(mess->file),
strchr(found->basename, '.') ? "%s" : "%s.0", found->basename);
return 1;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,9 +33,9 @@ struct state {
unsigned int mac_len, mac_type;
};
static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t sz,
struct in6_addr *client_addr, int is_unicast, time_t now);
static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_t sz, int is_unicast, time_t now);
static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbuff, size_t sz, int is_unicast, time_t now);
static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts);
static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string);
static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string);
@@ -104,12 +104,12 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if
}
/* This cost me blood to write, it will probably cost you blood to understand - srk. */
static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t sz,
struct in6_addr *client_addr, int is_unicast, time_t now)
{
void *end = inbuff + sz;
void *opts = inbuff + 34;
int msg_type = *((unsigned char *)inbuff);
int msg_type = *inbuff;
unsigned char *outmsgtypep;
void *opt;
struct dhcp_vendor *vendor;
@@ -259,15 +259,15 @@ static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
return 1;
}
static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_t sz, int is_unicast, time_t now)
static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbuff, size_t sz, int is_unicast, time_t now)
{
void *opt;
int i, o, o1, start_opts;
int i, o, o1, start_opts, start_msg;
struct dhcp_opt *opt_cfg;
struct dhcp_netid *tagif;
struct dhcp_config *config = NULL;
struct dhcp_netid known_id, iface_id, v6_id;
unsigned char *outmsgtypep;
unsigned char outmsgtype;
struct dhcp_vendor *vendor;
struct dhcp_context *context_tmp;
struct dhcp_mac *mac_opt;
@@ -296,12 +296,13 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
v6_id.next = state->tags;
state->tags = &v6_id;
/* copy over transaction-id, and save pointer to message type */
if (!(outmsgtypep = put_opt6(inbuff, 4)))
start_msg = save_counter(-1);
/* copy over transaction-id */
if (!put_opt6(inbuff, 4))
return 0;
start_opts = save_counter(-1);
state->xid = outmsgtypep[3] | outmsgtypep[2] << 8 | outmsgtypep[1] << 16;
state->xid = inbuff[3] | inbuff[2] << 8 | inbuff[1] << 16;
/* We're going to be linking tags from all context we use.
mark them as unused so we don't link one twice and break the list */
for (context_tmp = state->context; context_tmp; context_tmp = context_tmp->current)
@@ -347,7 +348,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
(msg_type == DHCP6REQUEST || msg_type == DHCP6RENEW || msg_type == DHCP6RELEASE || msg_type == DHCP6DECLINE))
{
*outmsgtypep = DHCP6REPLY;
outmsgtype = DHCP6REPLY;
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6USEMULTI);
put_opt6_string("Use multicast");
@@ -619,11 +620,11 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
struct dhcp_netid *solicit_tags;
struct dhcp_context *c;
*outmsgtypep = DHCP6ADVERTISE;
outmsgtype = DHCP6ADVERTISE;
if (opt6_find(state->packet_options, state->end, OPTION6_RAPID_COMMIT, 0))
{
*outmsgtypep = DHCP6REPLY;
outmsgtype = DHCP6REPLY;
state->lease_allocate = 1;
o = new_opt6(OPTION6_RAPID_COMMIT);
end_opt6(o);
@@ -809,7 +810,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
int start = save_counter(-1);
/* set reply message type */
*outmsgtypep = DHCP6REPLY;
outmsgtype = DHCP6REPLY;
state->lease_allocate = 1;
log6_quiet(state, "DHCPREQUEST", NULL, ignore ? _("ignored") : NULL);
@@ -924,7 +925,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
int address_assigned = 0;
/* set reply message type */
*outmsgtypep = DHCP6REPLY;
outmsgtype = DHCP6REPLY;
log6_quiet(state, msg_type == DHCP6RENEW ? "DHCPRENEW" : "DHCPREBIND", NULL, NULL);
@@ -1057,7 +1058,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
int good_addr = 0;
/* set reply message type */
*outmsgtypep = DHCP6REPLY;
outmsgtype = DHCP6REPLY;
log6_quiet(state, "DHCPCONFIRM", NULL, NULL);
@@ -1121,7 +1122,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
log6_quiet(state, "DHCPINFORMATION-REQUEST", NULL, ignore ? _("ignored") : state->hostname);
if (ignore)
return 0;
*outmsgtypep = DHCP6REPLY;
outmsgtype = DHCP6REPLY;
tagif = add_options(state, 1);
break;
}
@@ -1130,7 +1131,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
case DHCP6RELEASE:
{
/* set reply message type */
*outmsgtypep = DHCP6REPLY;
outmsgtype = DHCP6REPLY;
log6_quiet(state, "DHCPRELEASE", NULL, NULL);
@@ -1195,7 +1196,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
case DHCP6DECLINE:
{
/* set reply message type */
*outmsgtypep = DHCP6REPLY;
outmsgtype = DHCP6REPLY;
log6_quiet(state, "DHCPDECLINE", NULL, NULL);
@@ -1275,7 +1276,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
}
}
/* Fill in the message type. Note that we store the offset,
not a direct pointer, since the packet memory may have been
reallocated. */
((unsigned char *)(daemon->outpacket.iov_base))[start_msg] = outmsgtype;
log_tags(tagif, state->xid);
log6_opts(0, state->xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1));
@@ -1873,23 +1879,24 @@ static void update_leases(struct state *state, struct dhcp_context *context, str
#ifdef HAVE_SCRIPT
if (daemon->lease_change_command)
{
void *class_opt;
void *opt;
lease->flags |= LEASE_CHANGED;
free(lease->extradata);
lease->extradata = NULL;
lease->extradata_size = lease->extradata_len = 0;
lease->vendorclass_count = 0;
if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4)))
if ((opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4)))
{
void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));
void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt));
lease->vendorclass_count++;
/* send enterprise number first */
sprintf(daemon->dhcp_buff2, "%u", opt6_uint(class_opt, 0, 4));
sprintf(daemon->dhcp_buff2, "%u", opt6_uint(opt, 0, 4));
lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0);
if (opt6_len(class_opt) >= 6)
for (enc_opt = opt6_ptr(class_opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
if (opt6_len(opt) >= 6)
for (enc_opt = opt6_ptr(opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
{
lease->vendorclass_count++;
lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
@@ -1899,6 +1906,24 @@ static void update_leases(struct state *state, struct dhcp_context *context, str
lease_add_extradata(lease, (unsigned char *)state->client_hostname,
state->client_hostname ? strlen(state->client_hostname) : 0, 0);
/* DNSMASQ_REQUESTED_OPTIONS */
if ((opt = opt6_find(state->packet_options, state->end, OPTION6_ORO, 2)))
{
int i, len = opt6_len(opt)/2;
u16 *rop = opt6_ptr(opt, 0);
for (i = 0; i < len; i++)
lease_add_extradata(lease, (unsigned char *)daemon->namebuff,
sprintf(daemon->namebuff, "%u", ntohs(rop[i])), (i + 1) == len ? 0 : ',');
}
else
lease_add_extradata(lease, NULL, 0, 0);
if ((opt = opt6_find(state->packet_options, state->end, OPTION6_MUD_URL, 1)))
lease_add_extradata(lease, opt6_ptr(opt, 0), opt6_len(opt), 0);
else
lease_add_extradata(lease, NULL, 0, 0);
/* space-concat tag set */
if (!tagif && !context->netid.net)
lease_add_extradata(lease, NULL, 0, 0);
@@ -1928,10 +1953,10 @@ static void update_leases(struct state *state, struct dhcp_context *context, str
lease_add_extradata(lease, (unsigned char *)daemon->addrbuff, state->link_address ? strlen(daemon->addrbuff) : 0, 0);
if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_USER_CLASS, 2)))
if ((opt = opt6_find(state->packet_options, state->end, OPTION6_USER_CLASS, 2)))
{
void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));
for (enc_opt = opt6_ptr(class_opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt));
for (enc_opt = opt6_ptr(opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
}
}
@@ -2100,95 +2125,106 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
return ret;
}
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz,
struct in6_addr *peer_address, u32 scope_id, time_t now)
int relay_upstream6(int iface_index, ssize_t sz,
struct in6_addr *peer_address, u32 scope_id, time_t now)
{
/* ->local is same value for all relays on ->current chain */
union all_addr from;
unsigned char *header;
unsigned char *inbuff = daemon->dhcp_packet.iov_base;
int msg_type = *inbuff;
int hopcount;
int hopcount, o;
struct in6_addr multicast;
unsigned int maclen, mactype;
unsigned char mac[DHCP_CHADDR_MAX];
struct dhcp_relay *relay;
for (relay = daemon->relay6; relay; relay = relay->next)
if (relay->iface_index != 0 && relay->iface_index == iface_index)
break;
/* No relay config. */
if (!relay)
return 0;
inet_pton(AF_INET6, ALL_SERVERS, &multicast);
get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now);
/* source address == relay address */
from.addr6 = relay->local.addr6;
/* Get hop count from nested relayed message */
if (msg_type == DHCP6RELAYFORW)
hopcount = *((unsigned char *)inbuff+1) + 1;
else
hopcount = 0;
/* RFC 3315 HOP_COUNT_LIMIT */
if (hopcount > 32)
return;
reset_counter();
if ((header = put_opt6(NULL, 34)))
/* RFC 3315 HOP_COUNT_LIMIT */
if (hopcount > 32 || !(header = put_opt6(NULL, 34)))
return 1;
header[0] = DHCP6RELAYFORW;
header[1] = hopcount;
memcpy(&header[18], peer_address, IN6ADDRSZ);
/* RFC-6939 */
if (maclen != 0)
{
int o;
header[0] = DHCP6RELAYFORW;
header[1] = hopcount;
memcpy(&header[2], &relay->local.addr6, IN6ADDRSZ);
memcpy(&header[18], peer_address, IN6ADDRSZ);
/* RFC-6939 */
if (maclen != 0)
{
o = new_opt6(OPTION6_CLIENT_MAC);
put_opt6_short(mactype);
put_opt6(mac, maclen);
end_opt6(o);
}
o = new_opt6(OPTION6_RELAY_MSG);
put_opt6(inbuff, sz);
o = new_opt6(OPTION6_CLIENT_MAC);
put_opt6_short(mactype);
put_opt6(mac, maclen);
end_opt6(o);
for (; relay; relay = relay->current)
{
union mysockaddr to;
to.sa.sa_family = AF_INET6;
to.in6.sin6_addr = relay->server.addr6;
to.in6.sin6_port = htons(DHCPV6_SERVER_PORT);
to.in6.sin6_flowinfo = 0;
to.in6.sin6_scope_id = 0;
if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast))
{
int multicast_iface;
if (!relay->interface || strchr(relay->interface, '*') ||
(multicast_iface = if_nametoindex(relay->interface)) == 0 ||
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1)
my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast to DHCPv6 server without correct interface"));
}
send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(-1), &to, &from, 0);
if (option_bool(OPT_LOG_OPTS))
{
inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN);
inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->namebuff);
}
/* Save this for replies */
relay->iface_index = scope_id;
}
}
o = new_opt6(OPTION6_RELAY_MSG);
put_opt6(inbuff, sz);
end_opt6(o);
for (; relay; relay = relay->next)
if (relay->iface_index != 0 && relay->iface_index == iface_index)
{
union mysockaddr to;
memcpy(&header[2], &relay->local.addr6, IN6ADDRSZ);
to.sa.sa_family = AF_INET6;
to.in6.sin6_addr = relay->server.addr6;
to.in6.sin6_port = htons(relay->port);
to.in6.sin6_flowinfo = 0;
to.in6.sin6_scope_id = 0;
if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast))
{
int multicast_iface;
if (!relay->interface || strchr(relay->interface, '*') ||
(multicast_iface = if_nametoindex(relay->interface)) == 0 ||
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1)
{
my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast DHCP relay via interface %s"), relay->interface);
continue;
}
}
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), NULL, &to, daemon->dhcp6fd);
#endif
while (retry_send(sendto(daemon->dhcp6fd, (void *)daemon->outpacket.iov_base, save_counter(-1),
0, (struct sockaddr *)&to, sa_len(&to))));
if (option_bool(OPT_LOG_OPTS))
{
inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN);
if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast))
snprintf(daemon->namebuff, MAXDNAME, _("multicast via %s"), relay->interface);
else
inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->namebuff);
}
}
return 1;
}
unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface)
int relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface)
{
struct dhcp_relay *relay;
struct in6_addr link;
@@ -2220,11 +2256,76 @@ unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival
put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ);
peer->sin6_scope_id = relay->iface_index;
return encap_type == DHCP6RELAYREPL ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;
}
}
if (encap_type == DHCP6RELAYREPL)
{
peer->sin6_port = ntohs(DHCPV6_SERVER_PORT);
return 1;
}
peer->sin6_port = ntohs(DHCPV6_CLIENT_PORT);
#ifdef HAVE_SCRIPT
if (daemon->lease_change_command && encap_type == DHCP6REPLY)
{
/* decapsulate relayed message */
opts = opt6_ptr(opt, 4);
end = opt6_ptr(opt, opt6_len(opt));
for (opt = opts; opt; opt = opt6_next(opt, end))
if (opt6_type(opt) == OPTION6_IA_PD && opt6_len(opt) > 12)
{
void *ia_opts = opt6_ptr(opt, 12);
void *ia_end = opt6_ptr(opt, opt6_len(opt));
void *ia_opt;
for (ia_opt = ia_opts; ia_opt; ia_opt = opt6_next(ia_opt, ia_end))
/* valid lifetime must not be zero. */
if (opt6_type(ia_opt) == OPTION6_IAPREFIX && opt6_len(ia_opt) >= 25 && opt6_uint(ia_opt, 4, 4) != 0)
{
if (daemon->free_snoops ||
(daemon->free_snoops = whine_malloc(sizeof(struct snoop_record))))
{
struct snoop_record *snoop = daemon->free_snoops;
daemon->free_snoops = snoop->next;
snoop->client = peer->sin6_addr;
snoop->prefix_len = opt6_uint(ia_opt, 8, 1);
memcpy(&snoop->prefix, opt6_ptr(ia_opt, 9), IN6ADDRSZ);
snoop->next = relay->snoop_records;
relay->snoop_records = snoop;
}
}
}
}
#endif
return 1;
}
}
return 0;
}
#ifdef HAVE_SCRIPT
int do_snoop_script_run(void)
{
struct dhcp_relay *relay;
struct snoop_record *snoop;
for (relay = daemon->relay6; relay; relay = relay->next)
if ((snoop = relay->snoop_records))
{
relay->snoop_records = snoop->next;
snoop->next = daemon->free_snoops;
daemon->free_snoops = snoop;
queue_relay_snoop(&snoop->client, relay->iface_index, &snoop->prefix, snoop->prefix_len);
return 1;
}
return 0;
}
#endif
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -156,10 +156,10 @@ static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, i
}
/* mode is 0 to remove EDNS0, 1 to filter DNSSEC RRs */
/* mode may be remove EDNS0 or DNSSEC RRs or remove A or AAAA from answer section. */
size_t rrfilter(struct dns_header *header, size_t plen, int mode)
{
static unsigned char **rrs;
static unsigned char **rrs = NULL;
static int rr_sz = 0;
unsigned char *p = (unsigned char *)(header+1);
@@ -192,20 +192,37 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode)
if (!ADD_RDLEN(header, p, plen, rdlen))
return plen;
/* Don't remove the answer. */
if (i < ntohs(header->ancount) && type == qtype && class == qclass)
continue;
if (mode == 0) /* EDNS */
if (mode == RRFILTER_EDNS0) /* EDNS */
{
/* EDNS mode, remove T_OPT from additional section only */
if (i < (ntohs(header->nscount) + ntohs(header->ancount)) || type != T_OPT)
continue;
}
else if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
/* DNSSEC mode, remove SIGs and NSECs from all three sections. */
continue;
else if (mode == RRFILTER_DNSSEC)
{
if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
/* DNSSEC mode, remove SIGs and NSECs from all three sections. */
continue;
/* Don't remove the answer. */
if (i < ntohs(header->ancount) && type == qtype && class == qclass)
continue;
}
else
{
/* Only looking at answer section now. */
if (i >= ntohs(header->ancount))
break;
if (class != C_IN)
continue;
if (mode == RRFILTER_A && type != T_A)
continue;
if (mode == RRFILTER_AAAA && type != T_AAAA)
continue;
}
if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
return plen;
@@ -322,15 +339,11 @@ int expand_workspace(unsigned char ***wkspc, int *szp, int new)
return 0;
new += 5;
if (!(p = whine_malloc(new * sizeof(unsigned char *))))
return 0;
if (old != 0 && *wkspc)
{
memcpy(p, *wkspc, old * sizeof(unsigned char *));
free(*wkspc);
}
if (!(p = whine_realloc(*wkspc, new * sizeof(unsigned char *))))
return 0;
memset(p+old, 0, new-old);
*wkspc = p;
*szp = new;

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,9 +19,9 @@
#ifdef HAVE_TFTP
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len);
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix);
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client);
static void free_transfer(struct tftp_transfer *transfer);
static ssize_t tftp_err(int err, char *packet, char *message, char *file);
static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2);
static ssize_t tftp_err_oops(char *packet, const char *file);
static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
static char *next(char **p, char *end);
@@ -95,6 +95,10 @@ void tftp_request(struct listener *listen, time_t now)
if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
return;
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_TFTP, (void *)packet, len, (union mysockaddr *)&peer, NULL, listen->tftpfd);
#endif
/* Can always get recvd interface for IPv6 */
if (!check_dest)
@@ -362,7 +366,7 @@ void tftp_request(struct listener *listen, time_t now)
!(mode = next(&p, end)) ||
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
{
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff, NULL);
is_err = 1;
}
else
@@ -472,7 +476,7 @@ void tftp_request(struct listener *listen, time_t now)
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
/* check permissions and open file */
if ((transfer->file = check_tftp_fileperm(&len, prefix)))
if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff)))
{
if ((len = get_block(packet, transfer)) == -1)
len = tftp_err_oops(packet, daemon->namebuff);
@@ -482,6 +486,10 @@ void tftp_request(struct listener *listen, time_t now)
}
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index);
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
#endif
if (is_err)
free_transfer(transfer);
@@ -492,7 +500,7 @@ void tftp_request(struct listener *listen, time_t now)
}
}
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client)
{
char *packet = daemon->packet, *namebuff = daemon->namebuff;
struct tftp_file *file;
@@ -509,7 +517,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
{
if (errno == ENOENT)
{
*len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
*len = tftp_err(ERR_FNF, packet, _("file %s not found for %s"), namebuff, client);
return NULL;
}
else if (errno == EACCES)
@@ -562,8 +570,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
return file;
perm:
errno = EACCES;
*len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff);
*len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff, strerror(EACCES));
if (fd != -1)
close(fd);
return NULL;
@@ -599,8 +606,12 @@ void check_tftp_listeners(time_t now)
{
/* Wrong source address. See rfc1350 para 4. */
prettyprint_addr(&peer, daemon->addrbuff);
len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff);
len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff, NULL);
while(retry_send(sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer))));
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
#endif
}
}
}
@@ -635,9 +646,14 @@ void check_tftp_listeners(time_t now)
}
if (len != 0)
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
&transfer->peer, &transfer->source, transfer->if_index);
{
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
&transfer->peer, &transfer->source, transfer->if_index);
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd);
#endif
}
if (endcon || len == 0)
{
strcpy(daemon->namebuff, transfer->file->filename);
@@ -743,22 +759,21 @@ static void sanitise(char *buf)
}
#define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */
static ssize_t tftp_err(int err, char *packet, char *message, char *file)
static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2)
{
struct errmess {
unsigned short op, err;
char message[];
} *mess = (struct errmess *)packet;
ssize_t len, ret = 4;
char *errstr = strerror(errno);
memset(packet, 0, daemon->packet_buff_sz);
if (file)
sanitise(file);
mess->op = htons(OP_ERR);
mess->err = htons(err);
len = snprintf(mess->message, MAXMESSAGE, message, file, errstr);
len = snprintf(mess->message, MAXMESSAGE, message, file, arg2);
ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */
if (err != ERR_FNF || !option_bool(OPT_QUIET_TFTP))
@@ -772,7 +787,7 @@ static ssize_t tftp_err_oops(char *packet, const char *file)
/* May have >1 refs to file, so potentially mangle a copy of the name */
if (file != daemon->namebuff)
strcpy(daemon->namebuff, file);
return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff, strerror(errno));
}
/* return -1 for error, zero for done. */

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -233,8 +233,6 @@ char *canonicalise(char *in, int *nomem)
{
# ifdef HAVE_LIBIDN2
rc = idn2_to_ascii_lz(in, &ret, IDN2_NONTRANSITIONAL);
if (rc == IDN2_DISALLOWED)
rc = idn2_to_ascii_lz(in, &ret, IDN2_TRANSITIONAL);
# else
rc = idna_to_ascii_lz(in, &ret, 0);
# endif
@@ -338,6 +336,16 @@ void *whine_malloc(size_t size)
return ret;
}
void *whine_realloc(void *ptr, size_t size)
{
void *ret = realloc(ptr, size);
if (!ret)
my_syslog(LOG_ERR, _("failed to reallocate %d bytes"), (int) size);
return ret;
}
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2)
{
if (s1->sa.sa_family == s2->sa.sa_family)
@@ -356,6 +364,19 @@ int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2)
return 0;
}
int sockaddr_isnull(const union mysockaddr *s)
{
if (s->sa.sa_family == AF_INET &&
s->in.sin_addr.s_addr == 0)
return 1;
if (s->sa.sa_family == AF_INET6 &&
IN6_IS_ADDR_UNSPECIFIED(&s->in6.sin6_addr))
return 1;
return 0;
}
int sa_len(union mysockaddr *addr)
{
#ifdef HAVE_SOCKADDR_SA_LEN
@@ -369,7 +390,7 @@ int sa_len(union mysockaddr *addr)
}
/* don't use strcasecmp and friends here - they may be messed up by LOCALE */
int hostname_isequal(const char *a, const char *b)
int hostname_order(const char *a, const char *b)
{
unsigned int c1, c2;
@@ -382,11 +403,19 @@ int hostname_isequal(const char *a, const char *b)
if (c2 >= 'A' && c2 <= 'Z')
c2 += 'a' - 'A';
if (c1 != c2)
return 0;
if (c1 < c2)
return -1;
else if (c1 > c2)
return 1;
} while (c1);
return 1;
return 0;
}
int hostname_isequal(const char *a, const char *b)
{
return hostname_order(a, b) == 0;
}
/* is b equal to or a subdomain of a return 2 for equal, 1 for subdomain */
@@ -430,18 +459,26 @@ int hostname_issubdomain(char *a, char *b)
time_t dnsmasq_time(void)
{
#ifdef HAVE_BROKEN_RTC
struct tms dummy;
static long tps = 0;
struct timespec ts;
if (tps == 0)
tps = sysconf(_SC_CLK_TCK);
if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
die(_("cannot read monotonic clock: %s"), NULL, EC_MISC);
return (time_t)(times(&dummy)/tps);
return ts.tv_sec;
#else
return time(NULL);
#endif
}
u32 dnsmasq_milliseconds(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec) * 1000 + (tv.tv_usec / 1000);
}
int netmask_length(struct in_addr mask)
{
int zero_count = 0;