Compare commits

...

39 Commits

Author SHA1 Message Date
Simon Kelley
baf3c57af5 Fix compiler warnings. 2025-05-18 18:22:48 +01:00
Simon Kelley
e48a2af4f5 Overhaul extract_addresses() function.
The proximate cause for doing this is to fix a bug that
causes replies to PTR queries with more than one answer to have the
second and subsequent answers ignored.

The fix turned into a small re-write which removed a very old hack.
When caching reponses which include CNAME records, the cache system
stores the CNAME with a link to the record representing the target of
the CNAME. This isn't possible for PTR records representing IP
addresses since the name stored is the target of the PTR, record and
its name is inferred from the address in the cache record. Such
cache records have the F_REVERSE flag set. To get
around this, long ago, the code which stores such records elided the
CNAME entirely, so
4.3.2.1.in-addr.arpa   CNAME 18/3.2.1.in-addr.arpa
18/3.2.1.in-addr.arpa   PTR   myhost.example.com

would be stored as
4.3.2.1.in-addr.arpa PTR   myhost.example.com

and returned from the cache to subsequent requestor in that form.

Since that hack was committed, dnsmasq has learned to cache arbitrary
RRs. So now we can store the PTR records for all the no-trivial cases.

The means the CNAME chains ending in PTR records don't get mangled,
and we can store PTR records whose name in not w.x.y.x.in-addr.arpa
or the IPv6 equivalent.
2025-05-18 17:24:41 +01:00
Simon Kelley
91b800cc62 CHANGELOG update. 2025-05-14 22:38:14 +01:00
Simon Kelley
075e4a56b7 --leasequery can now take a subnet argument. 2025-05-14 22:36:03 +01:00
Simon Kelley
48658ebc54 Add address filtering for leasequery. 2025-05-14 22:36:03 +01:00
Simon Kelley
692ed0dd32 Log source address of leasequeries. 2025-05-14 22:36:03 +01:00
Simon Kelley
43805c1859 Omit message type from leasequery.c option dump. 2025-05-14 22:36:03 +01:00
Simon Kelley
4fbe1add95 Implement RFC-4388 DHCPv4 leasequery. 2025-05-14 22:35:51 +01:00
Simon Kelley
57c7ae8fc0 CHANGELOG update. 2025-05-14 22:33:44 +01:00
Simon Kelley
d1008215dc Better error message when rejecting a TFTP transfer. 2025-05-14 21:15:17 +01:00
Simon Kelley
b0aa604fcc Tidy-up of TCP-child pipe handling code.
Functionality is unchanged, but the code is easier to read and understand.

Also fix memory leak of blocks when cache insert fails.
2025-05-11 15:30:30 +01:00
Simon Kelley
8ddabd11bc DNSSEC validation change for reverse lookups in RFC-1918 ranges and friends.
The large public DNS services seem not to return proof-of-nonexistence
for DS records at the start of RFC-1918 in-addr.arpa domains and the their
IPv6 equivalents. 10.in-addr.arpa, 168.192.in-addr.arpa etc.

Since dnsmasq already has an option which instructs it not bother
upstream servers with pointless queries about these address ranges,
namely --bogus-priv, we extend that to enable behaviour which allows
dnsmasq to assume that insecure NXDOMAIN replies for these domains
are expected and to assume that the domains are legitimately unsigned.

This behaviour only matters when some address range is directed to
another upstream server using --rev-server. In that case it allows
replies from that server to pass DNSSEC validation. Without such a
server configured, queries are never sent upstream so they are never
validated and the new behaviour is moot.
2025-05-09 21:46:50 +01:00
Simon Kelley
98189ff988 Fix problems with ipset or nftset and TCP DNS transport.
If DNS is happening over TCP, the query is handled by a forked
process. Of ipset ot nftset is configured, this might include
inserting addresses in the *sets. Before this update, that
was done by the forked process using handles inherited from the
parent "master" process.

This is inherently racy. If the master process or another
child process tries to do updates at the same time, the
updates can clash and fail.

To see this, you need a busy server doing lots of DNS
queries over TCP, and ipset or nftset configured.

Going forward, we use the already established pipe to send the
updates from the child back to the master process, which
serialises them.
2025-05-08 16:07:19 +01:00
Simon Kelley
e86d53c438 Fix some edge cases wth domains and --address and --server.
Consider what happens when the same domain appears in
--address and --server.

This commit fixes the order, I think correctly like this:
highest to lowest priority.

--address with a IPv4 or IPv6 address (as long as the query matches the type)
--address with # for all-zeros, as long as the query is A or AAAA)
--address with no address, which returns NXDOMAIN or NOERROR for all types.
--server with address set to # to use the unqualified servers.
--server with matching domain.
--server without domain or from /etc/resolv.conf.

Note that the above is only valid when same domain appears.
The domain being matched is determined first, and has a higher
priority, so you can send google.com to a server and force com
to return NXDOMAIN and for google.com the server config will
override the address config, because there's a longer match.
2025-04-29 16:33:22 +01:00
Simon Kelley
e127a972d1 Fix logging booboo. 2025-04-27 23:25:30 +01:00
Simon Kelley
a458c2bfb0 Tidy up pipe-to-parent code in DNS TCP path. 2025-04-23 12:14:00 +01:00
Simon Kelley
9e67099ce7 Tidy up replies to non-QUERY DNS opcodes in auth mode. 2025-04-22 18:07:24 +01:00
Rob Gill
cfa1313e1f Log format error from upstream as 'FORMERR'
Signed-off-by: Rob Gill <rrobgill@protonmail.com>

At the moment if a misformatted query is reported by the upstream server
it is not clear from the log.
Other error codes from RFC1035 (server failure, not implemented,
refused) are logged with text, but format error is logged merely as "1".

Such that an upstream reporting a format error is presently logged as eg:
Apr 20 12:01:55 dnsmasq[3023]: reply error is 1

After this patch they are logged informatively, eg:
Apr 20 12:48:40 dnsmasq[3023]: reply error is FORMERR

This is a two line fix, FORMERR is already defined in dns-protocol.h.
2025-04-20 22:38:43 +01:00
Simon Kelley
e3a2c8dadf Add --log-queries=auth option. 2025-04-20 22:20:52 +01:00
Simon Kelley
95b74a7acf Fix copy 'n paste error in DBUS server-statistics code. 2025-04-18 23:50:46 +01:00
Simon Kelley
ae57f84061 Do a better job of 942a35f517 2025-04-18 14:01:14 +01:00
Simon Kelley
0620309b73 Revise negative DNS caching to better comply with RFC2308. 2025-04-16 21:29:42 +01:00
Opty
942a35f517 Silence compiler warnings. 2025-04-16 16:00:47 +01:00
Simon Kelley
83658efbf4 Fix occasional crashes with DNSSEC and large nunbers of --address configs.
Commit 3e659bd4ec removed the concept of
an usptream DNS server which is capable of DNSSEC: they are all
(at least in theory) now usable. As a very unfortunate side-effect,
this removed the filter that ensured that dnssec_server() ONLY
returns servers, and not domains with literal addresses.

If we try and do DNSSEC queries for a domain, and there's
a --address line which matches the domain, then dnssec_server()
will return that. This would break DNSSEC validation, but that's
turns out not to matter, because under these circumstances
dnssec_server() will probably return an out-of-bounds index into
the servers[] array, and the process dies with SIGSEGV.

Many thanks to the hard workers at the Tomato project who
found this bug and provided enough information to diagnose it.
2025-04-04 22:01:51 +01:00
Paul Donald
b0b4d90b6a Multiple typo and spelling fixes. 2025-03-29 21:41:40 +00:00
Simon Kelley
bdce03f928 DNAME documentation update. 2025-03-15 17:02:02 +00:00
Simon Kelley
d390dc0338 Implement RFC6672 para 5.3.2. check for DNAME.
Also fix overflow checking of NSEC type maps.
2025-03-15 16:47:55 +00:00
Simon Kelley
105c25e561 Fix DNSSEC and DNAME.
Do the correct things to validate replies which
include a DNAME record.

Thanks to Graham Clinch for pointing this out.
2025-03-15 09:05:47 +00:00
Simon Kelley
67e07b7fe8 Make extract_name() easier to call operating on first name in message. 2025-03-14 15:12:46 +00:00
Simon Kelley
f5659b406b Move find_pseudoheader() before add_edns0_config() in TCP codepath.
There's no point in checking if the query has edns0 headers _after_
adding our own.

This has the affect that if --add-cpe-id or --add-subnet or their friends
are configured,  a query via TCP without EDNS0 will get an answer with EDNS0.

It's highly unlikely that this breaks anything, but it is incorrect.

Thanks to  Tijs Van Buggenhout  for spotting this.
2025-03-14 15:12:46 +00:00
Simon Kelley
484fea238a Silence compiler warning. 2025-03-14 15:12:46 +00:00
Simon Kelley
1e587bec57 Silence compiler warning. 2025-03-14 15:12:45 +00:00
Simon Kelley
581c201aa8 Avoid division by zero with unlucky choices of max-port and min-port. 2025-03-14 15:12:45 +00:00
Simon Kelley
5487f6979e Fix (benign) use of uninitialised data. 2025-03-14 15:12:45 +00:00
Simon Kelley
99f12e3541 Default --dump-mask to all-on, rather than all-off. 2025-03-14 15:12:45 +00:00
Simon Kelley
7c1212e3d1 Fix query-combining for queries with class other than IN.
Along the way, use of extract_request() and extract_name() got further
refined.
2025-03-14 15:12:45 +00:00
Simon Kelley
0ccbdf8087 Make extract_name() easier to call operating on first name in message. 2025-03-14 15:12:45 +00:00
Simon Kelley
57f0489f38 Redesign the interaction between DNSSEC vaildation and per-domain servers.
This should just work in all cases now. If the normal chain-of-trust exists into
the delegated domain then whether the domain is signed or not, DNSSEC
validation will function normally. In the case the delgated domain
is an "overlay" on top of the global DNS and no NS and/or DS records
exist connecting it to the global dns, then if the domain is
unsigned the situation will be handled by synthesising a
proof-of-non-existance-of-DS for the domain and queries will be
answered unvalidated; this action will be logged. A signed domain
without chain-of-trust can be validated if a suitable trust-anchor
is provided using --trust-anchor.

Thanks to Uwe Kleine-König for prompting this change, and contributing
valuable insights into what could be improved.
2025-03-14 15:12:45 +00:00
Simon Kelley
3e659bd4ec Remove the concept of "DNSSEC incapable servers".
We're going to replace this with configured or extrapolated DS records.
2025-03-14 15:12:45 +00:00
31 changed files with 2271 additions and 872 deletions

View File

@@ -1,3 +1,65 @@
version 2.92
Redesign the interaction between DNSSEC validation and per-domain
servers, specified as --server=/<domain>/<ip-address>. This should
just work in all cases now. If the normal chain-of-trust exists into
the delegated domain then whether the domain is signed or not, DNSSEC
validation will function normally. In the case the delegated domain
is an "overlay" on top of the global DNS and no NS and/or DS records
exist connecting it to the global dns, then if the domain is
unsigned the situation will be handled by synthesising a
proof-of-non-existence-of-DS for the domain and queries will be
answered unvalidated; this action will be logged. A signed domain
without chain-of-trust can be validated if a suitable trust-anchor
is provided using --trust-anchor. This change should be backwards
compatible for all existing working configurations; it extends the
space of possible configurations which are functional.
Fix a couple of problems with DNSSEC validation and DNAME. One
could cause validation failure on correct domains, and the other
would fail to spot an invalid domain. Thanks to Graham Clinch
for spotting the problem.
Add --log-queries=auth option to only log replies from the auth DNS
facility.
Fix some edge-cases with domains and --address and --server. There
has been some regressions with this in previous releases. This change
fixes the priority order from loqwer to highest as:
--address with a IPv4 or IPv6 address (as long as the query matches the type)
--address with # for all-zeros, as long as the query is A or AAAA)
--address with no address, which returns NXDOMAIN or NOERROR for all types.
--server with address set to # to use the unqualified servers.
--server with matching domain.
--server without domain or from /etc/resolv.conf.
Fix problems with ipset or nftset and TCP DNS transport. Previously
this was racy, and insertion of addresses could fail on a busy server
when DNS-over-TCP transport was involved.
DNSSEC validation change for reverse lookups in RFC-1918 ranges and friends.
The large public DNS services seem not to return proof-of-nonexistence
for DS records at the start of RFC-1918 in-addr.arpa domains and the their
IPv6 equivalents. 10.in-addr.arpa, 168.192.in-addr.arpa etc.
Since dnsmasq already has an option which instructs it not bother
upstream servers with pointless queries about these address ranges,
namely --bogus-priv, we extend that to enable behaviour which allows
dnsmasq to assume that insecure NXDOMAIN replies for these domains
are expected and to assume that the domains are legitimately unsigned.
This behaviour only matters when some address range is directed to
another upstream server using --rev-server. In that case it allows
replies from that server to pass DNSSEC validation. Without such a
server configured, queries are never sent upstream so they are never
validated and the new behaviour is moot.
Add support for leasequery to the dnsmasq DHCPv4 server.
This has to be specifically enabled with the --leasequery option.
Many thanks to JAXPORT, Jacksonville Port Authority for sponsoring
this enhancement to dnsmasq.
Fix failure to cache PTR RRs when a reply contains more than one answer.
Thanks to Dmitry for spotting this.
version 2.91
Fix spurious "resource limit exceeded messages". Thanks to
Dominik Derigs for the bug report.
@@ -119,7 +181,7 @@ version 2.91
changing the behaviour of an installation with --no-x20-encode.
Fix a long-standing problem when two queries which are identical
in every repect _except_ case, get combined by dnsmasq. If
in every respect _except_ case, get combined by dnsmasq. If
dnsmasq gets eg, two queries for example.com and Example.com
in quick succession it will get the answer for example.com from
upstream and send that answer to both requestors. This means that
@@ -137,7 +199,7 @@ version 2.90
for a particular domain. Thanks to Daniel Danzberger for
spotting this bug.
Set the default maximum DNS UDP packet sice to 1232. This
Set the default maximum DNS UDP packet size to 1232. This
has been the recommended value since 2020 because it's the
largest value that avoid fragmentation, and fragmentation
is just not reliable on the modern internet, especially
@@ -145,14 +207,14 @@ version 2.90
--edns-packet-max for special circumstances.
Add --no-dhcpv4-interface and --no-dhcpv6-interface for
better control over which inetrfaces are providing DHCP service.
better control over which interfaces are providing DHCP service.
Fix issue with stale caching: After replying with stale data,
dnsmasq sends the query upstream to refresh the cache asynchronously
and sometimes sends the wrong packet: packet length can be wrong,
and if an EDE marking stale data is added to the answer that can
end up in the query also. This bug only seems to cause problems
when the usptream server is a DOH/DOT proxy. Thanks to Justin He
when the upstream server is a DOH/DOT proxy. Thanks to Justin He
for the bug report.
Add configurable caching for arbitrary RR-types.
@@ -190,7 +252,7 @@ version 2.90
Applied Cybersecurity ATHENE for finding this vulnerability.
CVE 2023-50387 and CVE 2023-50868 apply.
Note that the is a security vulnerablity only when DNSSEC validation
Note that this a security vulnerability only when DNSSEC validation
is enabled.
Fix memory-leak when attempting to cache SRV records with zero TTL.
@@ -276,7 +338,7 @@ version 2.88
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.
searching 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.
@@ -339,7 +401,7 @@ version 2.87
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
which reflects the interface they are attached to in a way which
doesn't require hard-coding addresses. Thanks to Sten Spans for
the idea.
@@ -713,22 +775,22 @@ version 2.80
but those which used the default of no checking will need to be
altered to explicitly select no checking. The new default is
because switching off checking for unsigned replies is
inherently dangerous. Not only does it open the possiblity of forged
inherently dangerous. Not only does it open the possibility of forged
replies, but it allows everything to appear to be working even
when the upstream namesevers do not support DNSSEC, and in this
case no DNSSEC validation at all is occuring.
case no DNSSEC validation at all is occurring.
Fix DHCP broken-ness when --no-ping AND --dhcp-sequential-ip
are set. Thanks to Daniel Miess for help with this.
Add a facilty to store DNS packets sent/recieved in a
Add a facility to store DNS packets sent/received in a
pcap-format file for later debugging. The file location
is given by the --dumpfile option, and a bitmap controlling
which packets should be dumped is given by the --dumpmask
option.
Handle the case of both standard and constructed dhcp-ranges on the
same interface better. We don't now contruct a dhcp-range if there's
same interface better. We don't now construct a dhcp-range if there's
already one specified. This allows the specified interface to
have different parameters and avoids advertising the same
prefix twice. Thanks to Luis Marsano for spotting this case.
@@ -1198,7 +1260,7 @@ version 2.73
Use inotify for checking on updates to /etc/resolv.conf and
friends under Linux. This fixes race conditions when the files are
updated rapidly and saves CPU by noy polling. To build
updated rapidly and saves CPU by not polling. To build
a binary that runs on old Linux kernels without inotify,
use make COPTS=-DNO_INOTIFY
@@ -1538,7 +1600,7 @@ version 2.68
are dynamic and works much better than the previous
work-around which exempted constructed DHCP ranges from the
IP address filtering. As a consequence, that work-around
is removed. Under certain circumstances, this change wil
is removed. Under certain circumstances, this change will
break existing configuration: if you're relying on the
constructed-range exception, you need to change --auth-zone
to specify the same interface as is used to construct your

View File

@@ -0,0 +1,695 @@
/* leasequery is Copyright (c) 2025 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
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
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, see <http://www.gnu.org/licenses/>.
*/
/* Author's email: simon@thekelleys.org.uk */
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <limits.h>
#include <net/if.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <netdb.h>
#include <linux/types.h>
#include <linux/capability.h>
#include <arpa/inet.h>
#include <ctype.h>
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
#define INADDRSZ 4
#define ADDRSTRLEN 46
#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
#define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
#define DHCP_CHADDR_MAX 16
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define BOOTREQUEST 1
#define BOOTREPLY 2
#define DHCP_COOKIE 0x63825363
#define DHCPLEASEQUERY 10
#define DHCPLEASEUNASSIGNED 11
#define DHCPLEASEUNKNOWN 12
#define DHCPLEASEACTIVE 13
#define OPTION_END 255
#define OPTION_ROUTER 3
#define OPTION_VENDOR_CLASS_OPT 43
#define OPTION_LEASE_TIME 51
#define OPTION_MESSAGE_TYPE 53
#define OPTION_SERVER_IDENTIFIER 54
#define OPTION_REQUESTED_OPTIONS 55
#define OPTION_VENDOR_ID 60
#define OPTION_CLIENT_ID 61
/* flags in top of length field for DHCP-option tables */
#define OT_ADDR_LIST 0x8000
#define OT_RFC1035_NAME 0x4000
#define OT_INTERNAL 0x2000
#define OT_NAME 0x1000
#define OT_CSTRING 0x0800
#define OT_DEC 0x0400
#define OT_TIME 0x0200
#define GETSHORT(s, cp) do { \
unsigned char *t_cp = (unsigned char *)(cp); \
(s) = ((u16)t_cp[0] << 8) \
| ((u16)t_cp[1]) \
; \
(cp) += 2; \
} while(0)
#define GETLONG(l, cp) do { \
unsigned char *t_cp = (unsigned char *)(cp); \
(l) = ((u32)t_cp[0] << 24) \
| ((u32)t_cp[1] << 16) \
| ((u32)t_cp[2] << 8) \
| ((u32)t_cp[3]) \
; \
(cp) += 4; \
} while (0)
#define PUTSHORT(s, cp) do { \
u16 t_s = (u16)(s); \
unsigned char *t_cp = (unsigned char *)(cp); \
*t_cp++ = t_s >> 8; \
*t_cp = t_s; \
(cp) += 2; \
} while(0)
#define PUTLONG(l, cp) do { \
u32 t_l = (u32)(l); \
unsigned char *t_cp = (unsigned char *)(cp); \
*t_cp++ = t_l >> 24; \
*t_cp++ = t_l >> 16; \
*t_cp++ = t_l >> 8; \
*t_cp = t_l; \
(cp) += 4; \
} while (0)
union all_addr {
struct in_addr addr4;
struct in6_addr addr6;
};
struct dhcp_packet_with_opts{
struct dhcp_packet {
unsigned char op, htype, hlen, hops;
unsigned int xid;
unsigned short secs, flags;
struct in_addr ciaddr, yiaddr, siaddr, giaddr;
unsigned char chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
} header;
unsigned char options[312];
};
static const struct opttab_t {
char *name;
u16 val, size;
} opttab[] = {
{ "netmask", 1, OT_ADDR_LIST },
{ "time-offset", 2, 4 },
{ "router", 3, OT_ADDR_LIST },
{ "dns-server", 6, OT_ADDR_LIST },
{ "log-server", 7, OT_ADDR_LIST },
{ "lpr-server", 9, OT_ADDR_LIST },
{ "hostname", 12, OT_INTERNAL | OT_NAME },
{ "boot-file-size", 13, 2 | OT_DEC },
{ "domain-name", 15, OT_NAME },
{ "swap-server", 16, OT_ADDR_LIST },
{ "root-path", 17, OT_NAME },
{ "extension-path", 18, OT_NAME },
{ "ip-forward-enable", 19, 1 },
{ "non-local-source-routing", 20, 1 },
{ "policy-filter", 21, OT_ADDR_LIST },
{ "max-datagram-reassembly", 22, 2 | OT_DEC },
{ "default-ttl", 23, 1 | OT_DEC },
{ "mtu", 26, 2 | OT_DEC },
{ "all-subnets-local", 27, 1 },
{ "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
{ "router-discovery", 31, 1 },
{ "router-solicitation", 32, OT_ADDR_LIST },
{ "static-route", 33, OT_ADDR_LIST },
{ "trailer-encapsulation", 34, 1 },
{ "arp-timeout", 35, 4 | OT_DEC },
{ "ethernet-encap", 36, 1 },
{ "tcp-ttl", 37, 1 },
{ "tcp-keepalive", 38, 4 | OT_DEC },
{ "nis-domain", 40, OT_NAME },
{ "nis-server", 41, OT_ADDR_LIST },
{ "ntp-server", 42, OT_ADDR_LIST },
{ "vendor-encap", 43, OT_INTERNAL },
{ "netbios-ns", 44, OT_ADDR_LIST },
{ "netbios-dd", 45, OT_ADDR_LIST },
{ "netbios-nodetype", 46, 1 },
{ "netbios-scope", 47, 0 },
{ "x-windows-fs", 48, OT_ADDR_LIST },
{ "x-windows-dm", 49, OT_ADDR_LIST },
{ "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
{ "lease-time", 51, OT_INTERNAL | OT_TIME },
{ "option-overload", 52, OT_INTERNAL },
{ "message-type", 53, OT_INTERNAL | OT_DEC },
{ "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
{ "parameter-request", 55, OT_INTERNAL },
{ "message", 56, OT_INTERNAL },
{ "max-message-size", 57, OT_INTERNAL },
{ "T1", 58, OT_TIME},
{ "T2", 59, OT_TIME},
{ "vendor-class", 60, OT_NAME },
{ "client-id", 61, OT_INTERNAL },
{ "nis+-domain", 64, OT_NAME },
{ "nis+-server", 65, OT_ADDR_LIST },
{ "tftp-server", 66, OT_NAME },
{ "bootfile-name", 67, OT_NAME },
{ "mobile-ip-home", 68, OT_ADDR_LIST },
{ "smtp-server", 69, OT_ADDR_LIST },
{ "pop3-server", 70, OT_ADDR_LIST },
{ "nntp-server", 71, OT_ADDR_LIST },
{ "irc-server", 74, OT_ADDR_LIST },
{ "user-class", 77, 0 },
{ "rapid-commit", 80, 0 },
{ "FQDN", 81, OT_INTERNAL },
{ "agent-info", 82, OT_INTERNAL },
{ "last-transaction", 91, 4 | OT_TIME },
{ "associated-ip", 92, OT_ADDR_LIST },
{ "client-arch", 93, 2 | OT_DEC },
{ "client-interface-id", 94, 0 },
{ "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 },
{ "classless-static-route", 121, 0 },
{ "vendor-id-encap", 125, 0 },
{ "tftp-server-address", 150, OT_ADDR_LIST },
{ "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
{ NULL, 0, 0 }
};
static void prettyprint_time(char *buf, unsigned int t);
static char *print_mac(char *buff, unsigned char *mac, int len);
int lookup_dhcp_opt(char *name)
{
const struct opttab_t *t = opttab;
int i;
for (i = 0; t[i].name; i++)
if (strcasecmp(t[i].name, name) == 0)
return t[i].val;
return -1;
}
char *option_string(unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
{
int o, i, j, nodecode = 0;
const struct opttab_t *ot = opttab;
char addrbuff[ADDRSTRLEN];
for (o = 0; ot[o].name; o++)
if (ot[o].val == opt)
{
if (buf)
{
memset(buf, 0, buf_len);
if (ot[o].size & OT_ADDR_LIST)
{
union all_addr addr;
int addr_len = INADDRSZ;
for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len)
{
if (i != 0)
strncat(buf, ", ", buf_len - strlen(buf));
/* align */
memcpy(&addr, &val[i], addr_len);
inet_ntop(AF_INET, &val[i], addrbuff, ADDRSTRLEN);
strncat(buf, addrbuff, buf_len - strlen(buf));
}
}
else if (ot[o].size & OT_NAME)
for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
{
char c = val[i];
if (isprint((unsigned char)c))
buf[j++] = c;
}
else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
{
unsigned int dec = 0;
for (i = 0; i < opt_len; i++)
dec = (dec << 8) | val[i];
if (ot[o].size & OT_TIME)
prettyprint_time(buf, dec);
else
sprintf(buf, "%u", dec);
}
else
nodecode = 1;
}
break;
}
if (opt_len != 0 && buf && (!ot[o].name || nodecode))
{
int trunc = 0;
if (opt_len > 14)
{
trunc = 1;
opt_len = 14;
}
print_mac(buf, val, opt_len);
if (trunc)
strncat(buf, "...", buf_len - strlen(buf));
}
return ot[o].name ? ot[o].name : "";
}
static void prettyprint_time(char *buf, unsigned int t)
{
if (t == 0xffffffff)
sprintf(buf, "infinite");
else
{
unsigned int x, p = 0;
if ((x = t/86400))
p += sprintf(&buf[p], "%ud", x);
if ((x = (t/3600)%24))
p += sprintf(&buf[p], "%uh", x);
if ((x = (t/60)%60))
p += sprintf(&buf[p], "%um", x);
if ((x = t%60))
sprintf(&buf[p], "%us", x);
}
}
static char *print_mac(char *buff, unsigned char *mac, int len)
{
char *p = buff;
int i;
if (len == 0)
sprintf(p, "<null>");
else
for (i = 0; i < len; i++)
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
return buff;
}
static unsigned char *dhcp_skip_opts(struct dhcp_packet_with_opts *pktp)
{
unsigned char *start = (unsigned char *)(((unsigned int *)&pktp->options[0]) + 1);
while (*start != 0)
start += start[1] + 2;
return start;
}
static unsigned char *dhcp_find_opt(struct dhcp_packet_with_opts *pktp, int optno)
{
unsigned char *start = (unsigned char *)(((unsigned int *)&pktp->options[0]) + 1);
while (*start != OPTION_END)
{
if (*start == optno)
return start;
start += start[1] + 2;
}
return NULL;
}
static void option_put(struct dhcp_packet_with_opts *pktp, int opt, int len, unsigned int val)
{
int i;
unsigned char *p = dhcp_skip_opts(pktp);
if (p)
{
*p++ = opt;
*p++ = len;
for (i = 0; i < len; i++)
*(p++) = val >> (8 * (len - (i + 1)));
}
}
static void option_put_string(struct dhcp_packet_with_opts *pktp, int opt,
const char *string, int null_term)
{
unsigned char *p;
size_t len = strlen(string);
if (null_term && len != 255)
len++;
if ((p = dhcp_skip_opts(pktp)))
{
*p++ = opt;
*p++ = len;
memcpy(p, string, len);
}
}
/* in may equal out, when maxlen may be -1 (No max len).
Return -1 for extraneous no-hex chars found. */
int parse_hex(char *in, unsigned char *out, int maxlen,
unsigned int *wildcard_mask, int *mac_type)
{
int done = 0, mask = 0, i = 0;
char *r;
if (mac_type)
*mac_type = 0;
while (!done && (maxlen == -1 || i < maxlen))
{
for (r = in; *r != 0 && *r != ':' && *r != '-' && *r != ' '; r++)
if (*r != '*' && !isxdigit((unsigned char)*r))
return -1;
if (*r == 0)
done = 1;
if (r != in )
{
if (*r == '-' && i == 0 && mac_type)
{
*r = 0;
*mac_type = strtol(in, NULL, 16);
mac_type = NULL;
}
else
{
*r = 0;
if (strcmp(in, "*") == 0)
{
mask = (mask << 1) | 1;
i++;
}
else
{
int j, bytes = (1 + (r - in))/2;
for (j = 0; j < bytes; j++)
{
char sav;
if (j < bytes - 1)
{
sav = in[(j+1)*2];
in[(j+1)*2] = 0;
}
/* checks above allow mix of hexdigit and *, which
is illegal. */
if (strchr(&in[j*2], '*'))
return -1;
out[i] = strtol(&in[j*2], NULL, 16);
mask = mask << 1;
if (++i == maxlen)
break;
if (j < bytes - 1)
in[(j+1)*2] = sav;
}
}
}
}
in = r+1;
}
if (wildcard_mask)
*wildcard_mask = mask;
return i;
}
static char *split_chr(char *s, char c)
{
char *comma, *p;
if (!s || !(comma = strchr(s, c)))
return NULL;
p = comma;
*comma = ' ';
for (; *comma == ' '; comma++);
for (; (p >= s) && *p == ' '; p--)
*p = 0;
return comma;
}
static char *split(char *s)
{
return split_chr(s, ',');
}
int main(int argc, char **argv)
{
int fd;
struct ifreq ifr;
struct in_addr iface_addr, server_addr, lease_addr;
struct dhcp_packet_with_opts pkt;
struct sockaddr_in saddr;
unsigned char *p;
ssize_t sz;
unsigned char mac[DHCP_CHADDR_MAX], clid[256], req_options[256];
char buff[500];
int clid_len = 0, mac_len = 0, mac_type = 0, opts_len = 0;
unsigned short port = DHCP_SERVER_PORT;
unsigned int xid;
struct timeval tv;
gettimeofday(&tv, NULL);
srand(tv.tv_usec);
xid = rand();
server_addr.s_addr = lease_addr.s_addr = iface_addr.s_addr = 0;
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
{
perror("leasequery: cannot create socket");
exit(1);
}
while (1)
{
int option = getopt(argc, argv, "i:s:l:m:c:p:r:");
if (option == -1)
break;
switch (option)
{
default:
fprintf(stderr,
"-p <port> port on DHCP server.\n"
"-i <interface> exit interface to DHCP server.\n"
"-s <inet-addr> DHCP server address.\n"
"-l <inet-addr> Query lease by IP address.\n"
"-m <MAC-addr> Query lease by MAC address.\n"
"-c <hex> Query lease by client-id.\n"
"-r <name>|<int>[,...] List of options to return.\n");
exit(1);
case 'p':
port = atoi(optarg);
break;
case 'i':
strncpy(ifr.ifr_name, optarg, IF_NAMESIZE);
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(fd, SIOCGIFADDR, &ifr) == -1)
{
perror("leasequery: cannot get interface address");
exit(1);
}
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
break;
case 's':
if (inet_pton(AF_INET, optarg, &server_addr) <= 0)
{
fprintf(stderr, "leasequery: bad server address\n");
exit(1);
}
break;
case 'l':
if (inet_pton(AF_INET, optarg, &lease_addr) <= 0)
{
fprintf(stderr, "leasequery: bad lease address\n");
exit(1);
}
break;
case 'm':
mac_type = 1; /* default ethernet */
mac_len = parse_hex(optarg, mac, DHCP_CHADDR_MAX, NULL, &mac_type);
if (!mac_type)
mac_type = 1; /* default ethernet */
break;
case 'c':
clid_len = parse_hex(optarg, clid, 256, NULL, NULL);
break;
case 'r':
{
char *comma;
int opt;
while (optarg)
{
comma = split(optarg);
if ((opt = lookup_dhcp_opt(optarg)) != -1 || (opt = atoi(optarg)) != 0)
req_options[opts_len++] = opt;
optarg = comma;
}
break;
}
}
}
if (!server_addr.s_addr)
{
fprintf(stderr, "leasequery: no server address\n");
exit(1);
}
memset(&pkt, 0, sizeof pkt);
pkt.header.op = BOOTREQUEST;
pkt.header.xid = xid;
pkt.header.ciaddr = lease_addr;
pkt.header.hlen = mac_len;
pkt.header.htype = mac_type;
memcpy(pkt.header.chaddr, mac, mac_len);
*((unsigned int *)&pkt.options[0]) = htonl(DHCP_COOKIE);
/* Dnsmasq extension. */
pkt.header.giaddr.s_addr = iface_addr.s_addr ? iface_addr.s_addr : INADDR_BROADCAST;
option_put(&pkt, OPTION_MESSAGE_TYPE, 1, DHCPLEASEQUERY);
if (clid_len != 0)
{
p = dhcp_skip_opts(&pkt);
*p++ = OPTION_CLIENT_ID;
*p++ = clid_len;
memcpy(p, clid, clid_len);
}
if (opts_len != 0)
{
p = dhcp_skip_opts(&pkt);
*p++ = OPTION_REQUESTED_OPTIONS;
*p++ = opts_len;
memcpy(p, req_options, opts_len);
}
if (iface_addr.s_addr)
{
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
saddr.sin_addr.s_addr = iface_addr.s_addr;
if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
{
perror("leasequery: cannot bind DHCP server socket");
exit(1);
}
}
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
saddr.sin_addr = server_addr;
while((sz = sendto(fd, &pkt, dhcp_skip_opts(&pkt) - ((unsigned char *)&pkt), 0, (struct sockaddr *)&saddr, sizeof(saddr))) == -1 &&
errno == EINTR);
if (sz == -1)
{
perror("leasequery: sendto()");
exit(1);
}
sz = recv(fd, &pkt, sizeof(pkt), 0);
if (sz == -1)
{
perror("leasequery: recv()");
exit(1);
}
if (sz >= sizeof(pkt.header) &&
pkt.header.op == BOOTREPLY &&
(p = dhcp_find_opt(&pkt, OPTION_MESSAGE_TYPE)) &&
pkt.header.xid == xid)
{
if (p[2] == DHCPLEASEUNASSIGNED)
printf("UNASSIGNED\n");
else if (p[2] == DHCPLEASEUNKNOWN)
printf("UNKNOWN\n");
else if (p[2] == DHCPLEASEACTIVE)
{
print_mac(buff, pkt.header.chaddr, pkt.header.hlen);
printf("ACTIVE %s %s\n", inet_ntoa(pkt.header.ciaddr), buff);
p = (unsigned char *)(((unsigned int *)&pkt.options[0]) + 1);
while (*p != OPTION_END)
{
if (*p != OPTION_MESSAGE_TYPE)
{
char *optname = option_string(p[0], option_ptr(p, 0), option_len(p), buff, 500);
printf("size:%3d option:%3d %s %s\n", option_len(p), p[0], optname, buff);
}
p += p[1] + 2;
}
}
}
}

View File

@@ -134,7 +134,8 @@ only, to stop dnsmasq daemonising in production, use
Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on receipt of SIGUSR1. If the argument "extra" is supplied, ie
.B --log-queries=extra
then the log has extra information at the start of each line.
This consists of a serial number which ties together the log lines associated with an individual query, and the IP address of the requestor. If the argument "proto" is supplied, this shows everything that "extra" does and also the network protocol used to communicate the queries.
This consists of a serial number which ties together the log lines associated with an individual query, and the IP address of the requestor. If the argument "proto" is supplied, this shows everything that "extra" does and also the network protocol used to communicate the queries. Logging of only queries to the authoritative server can be configured with
.B --log-queries=auth
.TP
.B \-8, --log-facility=<facility>
Set the facility to which dnsmasq will send syslog entries, this
@@ -345,6 +346,10 @@ Bogus private reverse lookups. All reverse lookups for private IP ranges (ie 192
which are not found in /etc/hosts or the DHCP leases file are answered
with "no such domain" rather than being forwarded upstream. The
set of prefixes affected is the list given in RFC6303, for IPv4 and IPv6.
Enabling this also subtly alters DNSSEC validation for reverse lookups in the
private ranges such that a non-secure DS record is accepted as proof that
the range is not signed. This works around behaviour by the public DNS services
which seem not to return validated proof-of-non-existence for DS records in these domains.
.TP
.B \-V, --alias=[<old-ip>]|[<start-ip>-<end-ip>],<new-ip>[,<mask>]
Modify IPv4 addresses returned from upstream nameservers; old-ip is
@@ -498,10 +503,7 @@ xxx.internal.thekelleys.org.uk at 192.168.1.1 then giving the flag
.B --server=/internal.thekelleys.org.uk/192.168.1.1
will send all queries for
internal machines to that nameserver, everything else will go to the
servers in /etc/resolv.conf. DNSSEC validation is turned off for such
private nameservers, UNLESS a
.B --trust-anchor
is specified for the domain in question. An empty domain specification,
servers in /etc/resolv.conf. An empty domain specification,
.B //
has the special meaning of "unqualified names only" ie names without any
dots in them. A non-standard port may be specified as
@@ -876,7 +878,7 @@ Set the maximum number of concurrent DNS queries. The default value is
150, which should be fine for most setups. The only known situation
where this needs to be increased is when using web-server log file
resolvers, which can generate large numbers of concurrent queries. This
parameter actually controls the number of concurrent queries per server group, where a server group is the set of server(s) associated with a single domain. So if a domain has it's own server via --server=/example.com/1.2.3.4 and 1.2.3.4 is not responding, but queries for *.example.com cannot go elsewhere, then other queries will not be affected. On configurations with many such server groups and tight resources, this value may need to be reduced.
parameter actually controls the number of concurrent queries per server group, where a server group is the set of server(s) associated with a single domain. So if a domain has its own server via --server=/example.com/1.2.3.4 and 1.2.3.4 is not responding, but queries for *.example.com cannot go elsewhere, then other queries will not be affected. On configurations with many such server groups and tight resources, this value may need to be reduced.
.TP
.B --dnssec
Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, dnsmasq requests the
@@ -894,12 +896,15 @@ ie capable of returning DNSSEC records with data. If they are not,
then dnsmasq will not be able to determine the trusted status of
answers and this means that DNS service will be entirely broken.
.TP
.B --trust-anchor=<domain>,[<class>,]<key-tag>,<algorithm>,<digest-type>,<digest>
.B --trust-anchor=<domain>,[<class>,][<key-tag>,<algorithm>,<digest-type>,<digest>]
Provide DS records to act a trust anchors for DNSSEC
validation. Typically these will be the DS record(s) for Key Signing
validation. The class defaults to IN. Typically these will be the DS record(s) for Key Signing
key(s) (KSK) of the root zone,
but trust anchors for limited domains are also possible. The current
root-zone trust anchors may be downloaded from https://data.iana.org/root-anchors/root-anchors.xml
but trust anchors for limited domains are also possible.
A negative trust anchor (ie. proof that a DS record doesn't exist) may be configured be specifying
only the name or only the name and class. This can be useful for forcing dnsmasq to treat zones delegated
using \fB--server=/<domain>/<ip-address>\fP as unsigned. The current
root-zone trust anchors may be downloaded from https://data.iana.org/root-anchors/root-anchors.xml
.TP
.B --dnssec-check-unsigned[=no]
As a default, dnsmasq checks that unsigned DNS replies are
@@ -1433,6 +1438,18 @@ 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 --leasequery[=<addr>[/<prefix>]]
Enable RFC 4388 leasequery. The dnsmasq DHCP server will answer LEASEQUERY messages from DHCP relays
when this option is given. If an address or subnet is given, only queries from a relay at that source are allowed. Repeat
the --leasequery option to specify multiple allowed sources.
To correctly answer lease queries it is necessary to store extra data in
the lease database, and this is also enabled by the \fB--leasequery\fP option. The extra fields
(agent-info and vendorclass) are stored in the leases file in a somewhat backwards compatible manner.
Enabling and then disabling leasequery will not cause problems; the extra information will be aged
out of the database. However, enabling leasequery in release 2.92 or later, storing leases
which come via DHCP relays which add option-82 agent-info data, and then moving back to a pre-2.92 dnsmasq
binary may cause problems reading the leases file. As of release 2.92, leasequery is only supported for DHCPv4.
.TP
.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

View File

@@ -103,9 +103,9 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
unsigned char *p, *ansp;
int qtype, qclass, rc;
int nameoffset, axfroffset = 0;
int q, anscount = 0, authcount = 0;
int anscount = 0, authcount = 0;
struct crec *crecp;
int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0, out_of_zone = 0;
int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0, out_of_zone = 0, notimp = 0;
struct auth_zone *zone = NULL;
struct addrlist *subnet = NULL;
char *cut;
@@ -116,18 +116,20 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
union all_addr addr;
struct cname *a, *candidate;
unsigned int wclen;
unsigned int log_flags = local_query ? 0 : F_NOERR;
if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
if (ntohs(header->qdcount) != 1)
return 0;
/* determine end of question section (we put answers there) */
if (!(ansp = skip_questions(header, qlen)))
return 0; /* bad packet */
/* now process each question, answers go in RRs after the question */
p = (unsigned char *)(header+1);
for (q = ntohs(header->qdcount); q != 0; q--)
if (OPCODE(header) != QUERY)
notimp = 1;
else
{
unsigned int flag = 0;
int found = 0;
@@ -147,7 +149,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
{
auth = 0;
out_of_zone = 1;
continue;
goto done;
}
if ((qtype == T_PTR || qtype == T_SOA || qtype == T_NS) &&
@@ -162,7 +164,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
{
out_of_zone = 1;
auth = 0;
continue;
goto done;
}
else if (qtype == T_SOA)
soa = 1, found = 1;
@@ -210,7 +212,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (local_query || in_zone(zone, intr->name, NULL))
{
found = 1;
log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, 0);
log_query(log_flags | flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL,
T_PTR, C_IN, "d", intr->name))
@@ -234,7 +236,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
strcat(name, ".");
strcat(name, zone->domain);
}
log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid), 0);
log_query(log_flags | flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid), 0);
found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL,
@@ -243,7 +245,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
}
else if (crecp->flags & (F_DHCP | F_HOSTS) && (local_query || in_zone(zone, name, NULL)))
{
log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid), 0);
log_query(log_flags | (crecp->flags & ~F_FORWARD), name, &addr, record_source(crecp->uid), 0);
found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL,
@@ -257,7 +259,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (!found && is_rev_synth(flag, &addr, name) && (local_query || in_zone(zone, name, NULL)))
{
log_query(F_CONFIG | F_REVERSE | flag, name, &addr, NULL, 0);
log_query(log_flags | F_CONFIG | F_REVERSE | flag, name, &addr, NULL, 0);
found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
@@ -269,9 +271,9 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (found)
nxdomain = 0;
else
log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL, 0);
log_query(log_flags | flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL, 0);
continue;
goto done;
}
cname_restart:
@@ -288,7 +290,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
{
out_of_zone = 1;
auth = 0;
continue;
goto done;
}
}
@@ -300,7 +302,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (rc == 2 && qtype == T_MX)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>", 0);
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, "<MX>", 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
NULL, T_MX, C_IN, "sd", rec->weight, rec->target))
anscount++;
@@ -315,7 +317,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (rc == 2 && qtype == T_SRV)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>", 0);
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, "<SRV>", 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
NULL, T_SRV, C_IN, "sssd",
rec->priority, rec->weight, rec->srvport, rec->target))
@@ -349,7 +351,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (rc == 2 && txt->class == qtype)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, NULL, txt->class);
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, NULL, txt->class);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
NULL, txt->class, C_IN, "t", txt->len, txt->txt))
anscount++;
@@ -363,7 +365,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (rc == 2 && qtype == T_TXT)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>", 0);
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, "<TXT>", 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
NULL, T_TXT, C_IN, "t", txt->len, txt->txt))
anscount++;
@@ -377,7 +379,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (rc == 2 && qtype == T_NAPTR)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>", 0);
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>", 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
NULL, T_NAPTR, C_IN, "sszzzd",
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
@@ -407,7 +409,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
continue;
found = 1;
log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, 0);
log_query(log_flags | F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN,
qtype == T_A ? "4" : "6", &addrlist->addr))
@@ -419,7 +421,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
{
nxdomain = 0;
log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL, 0);
log_query(log_flags | F_FORWARD | F_CONFIG | flag, name, &addr, NULL, 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN, qtype == T_A ? "4" : "6", &addr))
anscount++;
@@ -432,7 +434,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (qtype == T_SOA)
{
auth = soa = 1; /* inhibits auth section */
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>", 0);
log_query(log_flags | F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>", 0);
}
else if (qtype == T_AXFR)
{
@@ -468,13 +470,13 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
ns = 1; /* ensure we include NS records! */
axfr = 1;
axfroffset = nameoffset;
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>", 0);
log_query(log_flags | F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>", 0);
}
else if (qtype == T_NS)
{
auth = 1;
ns = 1; /* inhibits auth section */
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>", 0);
log_query(log_flags | F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>", 0);
}
}
@@ -492,7 +494,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
(local_query || filter_zone(zone, flag, &(crecp->addr))))
{
*cut = '.'; /* restore domain part */
log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid), 0);
log_query(log_flags | crecp->flags, name, &crecp->addr, record_source(crecp->uid), 0);
*cut = 0; /* remove domain part */
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN,
@@ -513,7 +515,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
nxdomain = 0;
if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr))))
{
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr, record_source(crecp->uid), 0);
log_query(log_flags | (crecp->flags & ~F_REVERSE), name, &crecp->addr, record_source(crecp->uid), 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN,
qtype == T_A ? "4" : "6", &crecp->addr))
@@ -560,7 +562,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (candidate)
{
log_query(F_CONFIG | F_CNAME, name, NULL, NULL, 0);
log_query(log_flags | F_CONFIG | F_CNAME, name, NULL, NULL, 0);
strcpy(name, candidate->target);
if (!strchr(name, '.'))
{
@@ -578,10 +580,12 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
else if (cache_find_non_terminal(name, now))
nxdomain = 0;
log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL, 0);
log_query(log_flags | flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL, 0);
}
}
done:
/* Add auth section */
if (auth && zone)
@@ -873,7 +877,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (!(ansp = skip_questions(header, qlen)))
return 0; /* bad packet */
anscount = authcount = 0;
log_query(F_AUTH, "reply", NULL, "truncated", 0);
log_query(log_flags | F_AUTH, "reply", NULL, "truncated", 0);
}
if ((auth || local_query) && nxdomain)
@@ -885,14 +889,23 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
header->nscount = htons(authcount);
header->arcount = htons(0);
if (!local_query && out_of_zone)
if ((!local_query && out_of_zone) || notimp)
{
SET_RCODE(header, REFUSED);
if (out_of_zone)
{
addr.log.rcode = REFUSED;
addr.log.ede = EDE_NOT_AUTH;
}
else
{
addr.log.rcode = NOTIMP;
addr.log.ede = EDE_UNSET;
}
SET_RCODE(header, addr.log.rcode);
header->ancount = htons(0);
header->nscount = htons(0);
addr.log.rcode = REFUSED;
addr.log.ede = EDE_NOT_AUTH;
log_query(F_UPSTREAM | F_RCODE, "error", &addr, NULL, 0);
log_query(log_flags | F_UPSTREAM | F_RCODE, "error", &addr, NULL, 0);
return resize_packet(header, ansp - (unsigned char *)header, NULL, 0);
}

View File

@@ -779,7 +779,14 @@ void cache_end_insert(void)
{
if (insert_error)
return;
/* signal start of cache insert transaction to master process */
if (daemon->pipe_to_parent != -1)
{
unsigned char op = PIPE_OP_INSERT;
read_write(daemon->pipe_to_parent, &op, sizeof(op), RW_WRITE);
}
while (new_chain)
{
struct crec *tmp = new_chain->next;
@@ -806,7 +813,7 @@ void cache_end_insert(void)
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
read_write(daemon->pipe_to_parent, (unsigned char *)name, m, RW_WRITE);
read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), RW_WRITE);
read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), RW_WRITE);
read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), RW_WRITE);
read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), RW_WRITE);
if (flags & F_RR)
@@ -816,7 +823,7 @@ void cache_end_insert(void)
blockdata_write(new_chain->addr.rrblock.rrdata, new_chain->addr.rrblock.datalen, daemon->pipe_to_parent);
}
#ifdef HAVE_DNSSEC
if (flags & F_DNSKEY)
else if (flags & F_DNSKEY)
{
read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), RW_WRITE);
blockdata_write(new_chain->addr.key.keydata, new_chain->addr.key.keylen, daemon->pipe_to_parent);
@@ -839,166 +846,237 @@ void cache_end_insert(void)
if (daemon->pipe_to_parent != -1)
{
ssize_t m = -1;
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
#ifdef HAVE_DNSSEC
/* Sneak out possibly updated crypto HWM values. */
m = daemon->metrics[METRIC_CRYPTO_HWM];
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
m = daemon->metrics[METRIC_SIG_FAIL_HWM];
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
m = daemon->metrics[METRIC_WORK_HWM];
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
#endif
}
new_chain = NULL;
}
#ifdef HAVE_DNSSEC
void cache_update_hwm(void)
{
/* Sneak out possibly updated crypto HWM values. */
unsigned char op = PIPE_OP_STATS;
/* A marshalled cache entry arrives on fd, read, unmarshall and insert into cache of master process. */
read_write(daemon->pipe_to_parent, &op, sizeof(op), RW_WRITE);
read_write(daemon->pipe_to_parent,
(unsigned char *)&daemon->metrics[METRIC_CRYPTO_HWM],
sizeof(daemon->metrics[METRIC_CRYPTO_HWM]), RW_WRITE);
read_write(daemon->pipe_to_parent,
(unsigned char *)&daemon->metrics[METRIC_SIG_FAIL_HWM],
sizeof(daemon->metrics[METRIC_SIG_FAIL_HWM]), RW_WRITE);
read_write(daemon->pipe_to_parent,
(unsigned char *)&daemon->metrics[METRIC_WORK_HWM],
sizeof(daemon->metrics[METRIC_WORK_HWM]), RW_WRITE);
}
#endif
#if defined(HAVE_IPSET) || defined(HAVE_NFTSET)
void cache_send_ipset(unsigned char op, struct ipsets *sets, int flags, union all_addr *addr)
{
read_write(daemon->pipe_to_parent, &op, sizeof(op), RW_WRITE);
read_write(daemon->pipe_to_parent, (unsigned char *)&sets, sizeof(sets), RW_WRITE);
read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), RW_WRITE);
read_write(daemon->pipe_to_parent, (unsigned char *)addr, sizeof(*addr), RW_WRITE);
}
#endif
/* Retrieve and handle a result from child TCP-handler.
Return 0 when pipe is closed by far end. */
int cache_recv_insert(time_t now, int fd)
{
ssize_t m;
union all_addr addr;
unsigned long ttl;
time_t ttd;
unsigned int flags;
struct crec *crecp = NULL;
unsigned char op;
cache_start_insert();
if (!read_write(fd, &op, sizeof(op), RW_READ))
return 0;
while (1)
switch (op)
{
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
return 0;
case PIPE_OP_INSERT:
{
/* A marshalled set if cache entries arrives on fd, read, unmarshall and insert into cache of master process. */
ssize_t m;
union all_addr addr;
unsigned long ttl;
time_t ttd;
unsigned int flags;
struct crec *crecp = NULL;
cache_start_insert();
/* loop reading RRs, since we don't want to go back to the poll() loop
and start processing other queries which might pollute the insertion
chain. The child will never block between the first OP_RR and the
minus-one length marking the end. */
while (1)
{
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
return 0;
if (m == -1)
{
cache_end_insert();
return 1;
}
if (!read_write(fd, (unsigned char *)daemon->namebuff, m, RW_READ) ||
!read_write(fd, (unsigned char *)&ttd, sizeof(ttd), RW_READ) ||
!read_write(fd, (unsigned char *)&flags, sizeof(flags), RW_READ) ||
!read_write(fd, (unsigned char *)&addr, sizeof(addr), RW_READ))
return 0;
daemon->namebuff[m] = 0;
ttl = difftime(ttd, now);
if (flags & F_CNAME)
{
struct crec *newc = really_insert(daemon->namebuff, NULL, C_IN, now, ttl, flags);
/* This relies on the fact that the target of a CNAME immediately precedes
it because of the order of extraction in extract_addresses, and
the order reversal on the new_chain. */
if (newc)
{
newc->addr.cname.is_name_ptr = 0;
if (!crecp)
newc->addr.cname.target.cache = NULL;
else
{
next_uid(crecp);
newc->addr.cname.target.cache = crecp;
newc->addr.cname.uid = crecp->uid;
}
}
}
else
{
unsigned short class = C_IN;
struct blockdata *block = NULL;
if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG)
&& !(block = addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen)))
continue;
#ifdef HAVE_DNSSEC
else if (flags & F_DNSKEY)
{
if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ))
return 0;
if (!(block = addr.key.keydata = blockdata_read(fd, addr.key.keylen)))
continue;
}
else if (flags & F_DS)
{
if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ))
return 0;
if (!(flags & F_NEG) && !(block = addr.ds.keydata = blockdata_read(fd, addr.ds.keylen)))
continue;
}
#endif
if (!(crecp = really_insert(daemon->namebuff, &addr, class, now, ttl, flags)))
blockdata_free(block);
}
}
}
if (m == -1)
{
#ifdef HAVE_DNSSEC
/* Sneak in possibly updated crypto HWM. */
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
return 0;
if (m > daemon->metrics[METRIC_CRYPTO_HWM])
daemon->metrics[METRIC_CRYPTO_HWM] = m;
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
return 0;
if (m > daemon->metrics[METRIC_SIG_FAIL_HWM])
daemon->metrics[METRIC_SIG_FAIL_HWM] = m;
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
return 0;
if (m > daemon->metrics[METRIC_WORK_HWM])
daemon->metrics[METRIC_WORK_HWM] = m;
#endif
cache_end_insert();
return 1;
}
#ifdef HAVE_DNSSEC
/* UDP validation moved to TCP to avoid truncation.
Restart UDP validation process with the returned result. */
if (m == -2)
{
int status, uid, keycount, validatecount;
int *keycountp, *validatecountp;
size_t ret_len;
struct frec *forward;
if (!read_write(fd, (unsigned char *)&status, sizeof(status), RW_READ))
return 0;
if (!read_write(fd, (unsigned char *)&ret_len, sizeof(ret_len), RW_READ))
return 0;
if (!read_write(fd, (unsigned char *)daemon->packet, ret_len, RW_READ))
return 0;
if (!read_write(fd, (unsigned char *)&forward, sizeof(forward), RW_READ))
return 0;
if (!read_write(fd, (unsigned char *)&uid, sizeof(uid), RW_READ))
return 0;
if (!read_write(fd, (unsigned char *)&keycount, sizeof(keycount), RW_READ))
return 0;
if (!read_write(fd, (unsigned char *)&keycountp, sizeof(keycountp), RW_READ))
return 0;
if (!read_write(fd, (unsigned char *)&validatecount, sizeof(validatecount), RW_READ))
return 0;
if (!read_write(fd, (unsigned char *)&validatecountp, sizeof(validatecountp), RW_READ))
return 0;
/* There's a tiny chance that the frec may have been freed
and reused before the TCP process returns. Detect that with
the uid field which is unique modulo 2^32 for each use. */
if (uid == forward->uid)
{
/* repatriate the work counters from the child process. */
*keycountp = keycount;
*validatecountp = validatecount;
if (!forward->dependent)
return_reply(now, forward, (struct dns_header *)daemon->packet, ret_len, status);
else
pop_and_retry_query(forward, status, now);
}
return 1;
}
#endif
if (!read_write(fd, (unsigned char *)daemon->namebuff, m, RW_READ) ||
!read_write(fd, (unsigned char *)&ttd, sizeof(ttd), RW_READ) ||
!read_write(fd, (unsigned char *)&flags, sizeof(flags), RW_READ) ||
!read_write(fd, (unsigned char *)&addr, sizeof(addr), RW_READ))
return 0;
daemon->namebuff[m] = 0;
ttl = difftime(ttd, now);
case PIPE_OP_STATS:
{
/* Sneak in possibly updated crypto HWM. */
unsigned int val;
if (!read_write(fd, (unsigned char *)&val, sizeof(val), RW_READ))
return 0;
if (val > daemon->metrics[METRIC_CRYPTO_HWM])
daemon->metrics[METRIC_CRYPTO_HWM] = val;
if (!read_write(fd, (unsigned char *)&val, sizeof(val), RW_READ))
return 0;
if (val > daemon->metrics[METRIC_SIG_FAIL_HWM])
daemon->metrics[METRIC_SIG_FAIL_HWM] = val;
if (!read_write(fd, (unsigned char *)&val, sizeof(val), RW_READ))
return 0;
if (val > daemon->metrics[METRIC_WORK_HWM])
daemon->metrics[METRIC_WORK_HWM] = val;
return 1;
}
if (flags & F_CNAME)
{
struct crec *newc = really_insert(daemon->namebuff, NULL, C_IN, now, ttl, flags);
/* This relies on the fact that the target of a CNAME immediately precedes
it because of the order of extraction in extract_addresses, and
the order reversal on the new_chain. */
if (newc)
{
newc->addr.cname.is_name_ptr = 0;
if (!crecp)
newc->addr.cname.target.cache = NULL;
else
{
next_uid(crecp);
newc->addr.cname.target.cache = crecp;
newc->addr.cname.uid = crecp->uid;
}
}
}
else
{
unsigned short class = C_IN;
if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG)
&& !(addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen)))
return 0;
#ifdef HAVE_DNSSEC
if (flags & F_DNSKEY)
{
if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ) ||
!(addr.key.keydata = blockdata_read(fd, addr.key.keylen)))
return 0;
}
else if (flags & F_DS)
{
if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ) ||
(!(flags & F_NEG) && !(addr.key.keydata = blockdata_read(fd, addr.key.keylen))))
return 0;
}
case PIPE_OP_RESULT:
{
/* UDP validation moved to TCP to avoid truncation.
Restart UDP validation process with the returned result. */
int status, uid, keycount, validatecount;
int *keycountp, *validatecountp;
size_t ret_len;
struct frec *forward;
if (!read_write(fd, (unsigned char *)&status, sizeof(status), RW_READ) ||
!read_write(fd, (unsigned char *)&ret_len, sizeof(ret_len), RW_READ) ||
!read_write(fd, (unsigned char *)daemon->packet, ret_len, RW_READ) ||
!read_write(fd, (unsigned char *)&forward, sizeof(forward), RW_READ) ||
!read_write(fd, (unsigned char *)&uid, sizeof(uid), RW_READ) ||
!read_write(fd, (unsigned char *)&keycount, sizeof(keycount), RW_READ) ||
!read_write(fd, (unsigned char *)&keycountp, sizeof(keycountp), RW_READ) ||
!read_write(fd, (unsigned char *)&validatecount, sizeof(validatecount), RW_READ) ||
!read_write(fd, (unsigned char *)&validatecountp, sizeof(validatecountp), RW_READ))
return 0;
/* There's a tiny chance that the frec may have been freed
and reused before the TCP process returns. Detect that with
the uid field which is unique modulo 2^32 for each use. */
if (uid == forward->uid)
{
/* repatriate the work counters from the child process. */
*keycountp = keycount;
*validatecountp = validatecount;
if (!forward->dependent)
return_reply(now, forward, (struct dns_header *)daemon->packet, ret_len, status);
else
pop_and_retry_query(forward, status, now);
}
return 1;
}
#endif
crecp = really_insert(daemon->namebuff, &addr, class, now, ttl, flags);
}
#if defined(HAVE_IPSET) || defined(HAVE_NFTSET)
case PIPE_OP_IPSET:
case PIPE_OP_NFTSET:
{
struct ipsets *sets;
char **sets_cur;
unsigned int flags;
union all_addr addr;
if (!read_write(fd, (unsigned char *)&sets, sizeof(sets), RW_READ) ||
!read_write(fd, (unsigned char *)&flags, sizeof(flags), RW_READ) ||
!read_write(fd, (unsigned char *)&addr, sizeof(addr), RW_READ))
return 0;
for (sets_cur = sets->sets; *sets_cur; sets_cur++)
{
int rc = -1;
#ifdef HAVE_IPSET
if (op == PIPE_OP_IPSET)
rc = add_to_ipset(*sets_cur, &addr, flags, 0);
#endif
#ifdef HAVE_NFTSET
if (op == PIPE_OP_NFTSET)
rc = add_to_nftset(*sets_cur, &addr, flags, 0);
#endif
if (rc == 0)
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, sets->domain, &addr, *sets_cur, op == PIPE_OP_IPSET);
}
return 1;
}
#endif
}
return 0;
}
int cache_find_non_terminal(char *name, time_t now)
@@ -1448,11 +1526,17 @@ void cache_reload(void)
cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
cache->ttd = daemon->local_ttl;
cache->name.namep = ds->name;
cache->addr.ds.keylen = ds->digestlen;
cache->addr.ds.algo = ds->algo;
cache->addr.ds.keytag = ds->keytag;
cache->addr.ds.digest = ds->digest_type;
cache->uid = ds->class;
if (ds->digestlen != 0)
{
cache->addr.ds.keylen = ds->digestlen;
cache->addr.ds.algo = ds->algo;
cache->addr.ds.keytag = ds->keytag;
cache->addr.ds.digest = ds->digest_type;
}
else
cache->flags |= F_NEG | F_DNSSECOK | F_NO_RR;
cache_hash(cache);
make_non_terminals(cache);
}
@@ -2169,12 +2253,17 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
char *extra = "";
char *gap = " ";
char portstring[7]; /* space for #<portnum> */
char opcodestring[3]; /* maximum is 15 */
if (!option_bool(OPT_LOG))
return;
/* F_NOERR is reused here to indicate logs arrising from auth queries */
if (!(flags & F_NOERR) && option_bool(OPT_AUTH_LOG))
return;
/* build query type string if requested */
if (!(flags & (F_SERVER | F_IPSET)) && type > 0)
if (!(flags & (F_SERVER | F_IPSET | F_QUERY)) && type > 0)
arg = querystr(arg, type);
dest = arg;
@@ -2207,6 +2296,8 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
dest = "SERVFAIL";
else if (rcode == REFUSED)
dest = "REFUSED";
else if (rcode == FORMERR)
dest = "FORMERR";
else if (rcode == NOTIMP)
dest = "not implemented";
else
@@ -2265,6 +2356,8 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
source = arg;
else if (flags & F_UPSTREAM)
source = "reply";
else if (flags & F_AUTH)
source = "auth";
else if (flags & F_SECSTAT)
{
if (addr && addr->log.ede != EDE_UNSET && option_bool(OPT_EXTRALOG))
@@ -2275,8 +2368,6 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
source = "validation";
dest = arg;
}
else if (flags & F_AUTH)
source = "auth";
else if (flags & F_DNSSEC)
{
source = arg;
@@ -2287,11 +2378,6 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
source = "forwarded";
verb = "to";
}
else if (flags & F_QUERY)
{
source = arg;
verb = "from";
}
else if (flags & F_IPSET)
{
source = type ? "ipset add" : "nftset add";
@@ -2303,7 +2389,21 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
source = "cached-stale";
else
source = "cached";
if (flags & F_QUERY)
{
if (flags & F_CONFIG)
{
sprintf(opcodestring, "%u", type & 0xf);
source = "non-query opcode";
name = opcodestring;
}
else if (!(flags & F_AUTH))
source = "query";
verb = "from";
}
if (!name)
gap = name = "";
else if (!name[0])

View File

@@ -26,6 +26,7 @@
#define DNSSEC_LIMIT_SIG_FAIL 20 /* Number of signature that can fail to validate in one answer */
#define DNSSEC_LIMIT_CRYPTO 200 /* max no. of crypto operations to validate one query. */
#define DNSSEC_LIMIT_NSEC3_ITERS 150 /* Max. number if iterations allowed in NSEC3 record. */
#define DNSSEC_ASSUMED_DS_TTL 3600 /* TTL for negative DS records implied by server=/domain/ */
#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 */
@@ -152,6 +153,7 @@ NO_AUTH
NO_DUMPFILE
NO_LOOP
NO_INOTIFY
NO_IPSET
these are available to explicitly disable compile time options which would
otherwise be enabled automatically or which are enabled by default
in the distributed source tree. Building dnsmasq
@@ -286,7 +288,6 @@ HAVE_SOCKADDR_SA_LEN
#define HAVE_BSD_NETWORK
#define HAVE_GETOPT_LONG
#define HAVE_SOCKADDR_SA_LEN
#define NO_IPSET
/* Define before sys/socket.h is included so we get socklen_t */
#define _BSD_SOCKLEN_T_
/* Select the RFC_3542 version of the IPv6 socket API.
@@ -296,7 +297,6 @@ HAVE_SOCKADDR_SA_LEN
#ifndef SOL_TCP
# define SOL_TCP IPPROTO_TCP
#endif
#define NO_IPSET
#elif defined(__NetBSD__)
#define HAVE_BSD_NETWORK
@@ -346,6 +346,11 @@ HAVE_SOCKADDR_SA_LEN
#undef HAVE_AUTH
#endif
#if !defined(HAVE_LINUX_NETWORK)
#undef HAVE_IPSET
#undef HAVE_NFTSET
#endif
#if defined(NO_IPSET)
#undef HAVE_IPSET
#endif
@@ -459,4 +464,4 @@ static char *compile_opts =
#endif
"dumpfile";
#endif /* defined(HAVE_DHCP) */
#endif /* defined(DNSMASQ_COMPILE_OPTS) */

View File

@@ -768,10 +768,10 @@ static DBusMessage *dbus_get_server_metrics(DBusMessage* message)
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, "queries", queries);
add_dict_int(&dict_array, "failed_queries", failed_queries);
add_dict_int(&dict_array, "nxdomain", nxdomain_replies);
add_dict_int(&dict_array, "retries", retrys);
add_dict_int(&dict_array, "latency", sigma_latency/count_latency);
dbus_message_iter_close_container(&server_array, &dict_array);

View File

@@ -701,7 +701,9 @@ static const struct opttab_t {
{ "user-class", 77, 0 },
{ "rapid-commit", 80, 0 },
{ "FQDN", 81, OT_INTERNAL },
{ "agent-id", 82, OT_INTERNAL },
{ "agent-info", 82, OT_INTERNAL },
{ "last-transaction", 91, 4 | OT_TIME },
{ "associated-ip", 92, OT_ADDR_LIST },
{ "client-arch", 93, 2 | OT_DEC },
{ "client-interface-id", 94, 0 },
{ "client-machine-id", 97, 0 },

View File

@@ -57,6 +57,8 @@
#define OPTION_RAPID_COMMIT 80
#define OPTION_CLIENT_FQDN 81
#define OPTION_AGENT_ID 82
#define OPTION_LAST_TRANSACTION 91
#define OPTION_ASSOCIATED_IP 92
#define OPTION_ARCH 93
#define OPTION_PXE_UUID 97
#define OPTION_SUBNET_SELECT 118
@@ -87,6 +89,11 @@
#define DHCPNAK 6
#define DHCPRELEASE 7
#define DHCPINFORM 8
#define DHCPFORCERENEW 9
#define DHCPLEASEQUERY 10
#define DHCPLEASEUNASSIGNED 11
#define DHCPLEASEUNKNOWN 12
#define DHCPLEASEACTIVE 13
#define BRDBAND_FORUM_IANA 3561 /* Broadband forum IANA enterprise */

View File

@@ -135,7 +135,7 @@ void dhcp_packet(time_t now, int pxe_fd)
struct dhcp_packet *mess;
struct dhcp_context *context;
struct dhcp_relay *relay;
int is_relay_reply = 0;
int is_relay_reply = 0, is_relay_use_source = 0;
struct iname *tmp;
struct ifreq ifr;
struct msghdr msg;
@@ -230,6 +230,20 @@ void dhcp_packet(time_t now, int pxe_fd)
return;
mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
/* Non-standard extension:
If giaddr == 255.255.255.255 we reply to the source
address in the request packet header. This makes
stand-alone leasequery clients easier, as they
can leave source address determination to the kernel.
In this case, set a flag and clear giaddr here,
to avoid massive relay confusion. */
if (mess->giaddr.s_addr == INADDR_BROADCAST)
{
mess->giaddr.s_addr = 0;
is_relay_use_source = 1;
}
loopback = !mess->giaddr.s_addr && (ifr.ifr_flags & IFF_LOOPBACK);
#ifdef HAVE_LINUX_NETWORK
@@ -337,8 +351,9 @@ void dhcp_packet(time_t now, int pxe_fd)
return;
lease_prune(NULL, now); /* lose any expired leases */
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
now, unicast_dest, loopback, &is_inform, pxe_fd, iface_addr, recvtime);
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, now, unicast_dest,
loopback, &is_inform, pxe_fd, iface_addr, recvtime,
is_relay_use_source ? dest.sin_addr : mess->giaddr);
lease_update_file(now);
lease_update_dns(0);
@@ -365,11 +380,17 @@ void dhcp_packet(time_t now, int pxe_fd)
if (mess->ciaddr.s_addr != 0)
dest.sin_addr = mess->ciaddr;
}
else if (mess->giaddr.s_addr && !is_relay_reply)
if ((is_relay_use_source || mess->giaddr.s_addr) && !is_relay_reply)
{
/* Send to BOOTP relay */
dest.sin_port = htons(daemon->dhcp_server_port);
dest.sin_addr = mess->giaddr;
/* Send to BOOTP relay. */
if (is_relay_use_source)
/* restore as-received value */
mess->giaddr.s_addr = INADDR_BROADCAST;
else
{
dest.sin_addr = mess->giaddr;
dest.sin_port = htons(daemon->dhcp_server_port);
}
}
else if (mess->ciaddr.s_addr)
{

View File

@@ -812,7 +812,7 @@ void dhcp_construct_contexts(time_t now)
{
if ((context->flags & CONTEXT_RA) || option_bool(OPT_RA))
{
/* previously constructed context has gone. advertise it's demise */
/* previously constructed context has gone; advertise its demise */
context->flags |= CONTEXT_OLD;
context->address_lost_time = now;
/* Apply same ceiling of configured lease time as in radv.c */

View File

@@ -38,7 +38,6 @@ static void async_event(int pipe, time_t now);
static void fatal_event(struct event_desc *ev, char *msg);
static int read_event(int fd, struct event_desc *evp, char **msg);
static void poll_resolv(int force, int do_reload, time_t now);
static void tcp_init(void);
int main (int argc, char **argv)
{
@@ -133,6 +132,7 @@ int main (int argc, char **argv)
'.' or NAME_ESCAPE then all would have to be escaped, so the
presentation format would be twice as long as the spec. */
daemon->keyname = safe_malloc((MAXDNAME * 2) + 1);
daemon->cname = safe_malloc((MAXDNAME * 2) + 1);
/* one char flag per possible RR in answer section (may get extended). */
daemon->rr_status_sz = 64;
daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz);
@@ -421,7 +421,11 @@ int main (int argc, char **argv)
/* safe_malloc returns zero'd memory */
daemon->randomsocks = safe_malloc(daemon->numrrand * sizeof(struct randfd));
tcp_init();
daemon->tcp_pids = safe_malloc(daemon->max_procs*sizeof(pid_t));
daemon->tcp_pipes = safe_malloc(daemon->max_procs*sizeof(int));
for (i = 0; i < daemon->max_procs; i++)
daemon->tcp_pipes[i] = -1;
}
#ifdef HAVE_INOTIFY
@@ -930,7 +934,8 @@ int main (int argc, char **argv)
my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until system time valid"));
for (ds = daemon->ds; ds; ds = ds->next)
my_syslog(LOG_INFO, _("configured with trust anchor for %s keytag %u"),
my_syslog(LOG_INFO,
ds->digestlen == 0 ? _("configured with negative trust anchor for %s") : _("configured with trust anchor for %s keytag %u"),
ds->name[0] == 0 ? "<root>" : ds->name, ds->keytag);
}
#endif
@@ -1069,10 +1074,6 @@ int main (int argc, char **argv)
daemon->pipe_to_parent = -1;
if (daemon->port != 0)
for (i = 0; i < daemon->max_procs; i++)
daemon->tcp_pipes[i] = -1;
#ifdef HAVE_INOTIFY
/* Using inotify, have to select a resolv file at startup */
poll_resolv(1, 0, now);
@@ -2122,6 +2123,10 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
if (!option_bool(OPT_DEBUG))
{
#ifdef HAVE_DNSSEC
cache_update_hwm(); /* Sneak out possibly updated crypto HWM values. */
#endif
close(daemon->pipe_to_parent);
flush_log();
_exit(0);
@@ -2140,7 +2145,7 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
cache_recv_insert() calls pop_and_retry_query() after the result
arrives via the pipe to the parent. */
int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header *header,
ssize_t *plen, int class, struct server *server, int *keycount, int *validatecount)
ssize_t *plen, char *name, int class, struct server *server, int *keycount, int *validatecount)
{
struct server *s;
@@ -2214,8 +2219,7 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
}
}
status = tcp_from_udp(now, status, header, plen, class, daemon->namebuff, daemon->keyname,
server, keycount, validatecount);
status = tcp_from_udp(now, status, header, plen, class, name, server, keycount, validatecount);
/* close upstream connections. */
for (s = daemon->servers; s; s = s->next)
@@ -2228,10 +2232,10 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
if (!option_bool(OPT_DEBUG))
{
ssize_t m = -2;
unsigned char op = PIPE_OP_RESULT;
/* tell our parent we're done, and what the result was then exit. */
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
read_write(daemon->pipe_to_parent, &op, sizeof(op), RW_WRITE);
read_write(daemon->pipe_to_parent, (unsigned char *)&status, sizeof(status), RW_WRITE);
read_write(daemon->pipe_to_parent, (unsigned char *)plen, sizeof(*plen), RW_WRITE);
read_write(daemon->pipe_to_parent, (unsigned char *)header, *plen, RW_WRITE);
@@ -2241,6 +2245,9 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
read_write(daemon->pipe_to_parent, (unsigned char *)&keycount, sizeof(keycount), RW_WRITE);
read_write(daemon->pipe_to_parent, (unsigned char *)validatecount, sizeof(*validatecount), RW_WRITE);
read_write(daemon->pipe_to_parent, (unsigned char *)&validatecount, sizeof(validatecount), RW_WRITE);
cache_update_hwm(); /* Sneak out possibly updated crypto HWM values. */
close(daemon->pipe_to_parent);
flush_log();
@@ -2411,8 +2418,4 @@ int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
}
#endif /* HAVE_DHCP */
void tcp_init(void)
{
daemon->tcp_pids = safe_malloc(daemon->max_procs*sizeof(pid_t));
daemon->tcp_pipes = safe_malloc(daemon->max_procs*sizeof(int));
}

View File

@@ -281,7 +281,9 @@ struct event_desc {
#define OPT_LOG_PROTO 73
#define OPT_NO_0x20 74
#define OPT_DO_0x20 75
#define OPT_LAST 76
#define OPT_AUTH_LOG 76
#define OPT_LEASEQUERY 77
#define OPT_LAST 78
#define OPTION_BITS (sizeof(unsigned int)*8)
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
@@ -536,6 +538,11 @@ struct crec {
#define SRC_HOSTS 2
#define SRC_AH 3
#define PIPE_OP_INSERT 1 /* Cache entry */
#define PIPE_OP_RESULT 2 /* Validation result */
#define PIPE_OP_STATS 3 /* Update parent's stats */
#define PIPE_OP_IPSET 4 /* Update IPset */
#define PIPE_OP_NFTSET 5 /* Update NFTset */
/* struct sockaddr is not large enough to hold any address,
and specifically not big enough to hold an IPv6 address.
@@ -553,9 +560,9 @@ union mysockaddr {
/* The actual values here matter, since we sort on them to get records in the order
IPv6 addr, IPv4 addr, all zero return, resolvconf servers, upstream server, no-data return */
#define SERV_LITERAL_ADDRESS 1 /* addr is the answer, or NoDATA is the answer, depending on the next four flags */
#define SERV_USE_RESOLV 2 /* forward this domain in the normal way */
IPv6 addr, IPv4 addr, all zero return, no-data return, resolvconf servers, upstream server */
#define SERV_USE_RESOLV 1 /* forward this domain in the normal way */
#define SERV_LITERAL_ADDRESS 2 /* addr is the answer, or NoDATA is the answer, depending on the next four flags */
#define SERV_ALL_ZEROS 4 /* return all zeros for A and AAAA */
#define SERV_4ADDR 8 /* addr is IPv4 */
#define SERV_6ADDR 16 /* addr is IPv6 */
@@ -858,6 +865,8 @@ struct dhcp_lease {
int last_interface;
int new_interface; /* save possible originated interface */
int new_prefixlen; /* and its prefix length */
unsigned char *agent_id, *vendorclass;
int agent_id_len, vendorclass_len;
#ifdef HAVE_DHCP6
struct in6_addr addr6;
unsigned int iaid;
@@ -1177,7 +1186,7 @@ extern struct daemon {
char *runfile;
char *lease_change_command;
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
struct bogus_addr *bogus_addr, *ignore_addr;
struct bogus_addr *bogus_addr, *ignore_addr, *leasequery_addr;
struct server *servers, *servers_tail, *local_domains, **serverarray;
struct rebind_domain *no_rebind;
int server_has_wildcard;
@@ -1248,7 +1257,7 @@ extern struct daemon {
char *namebuff; /* MAXDNAME size buffer */
char *workspacename;
#ifdef HAVE_DNSSEC
char *keyname; /* MAXDNAME size buffer */
char *keyname, *cname; /* MAXDNAME size buffer */
unsigned long *rr_status; /* ceiling in TTL from DNSSEC or zero for insecure */
int rr_status_sz;
int dnssec_no_time_check;
@@ -1353,6 +1362,13 @@ 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);
#ifdef HAVE_DNSSEC
void cache_update_hwm(void);
#endif
#if defined(HAVE_IPSET) || defined(HAVE_NFTSET)
void cache_send_ipset(unsigned char op, struct ipsets *sets,
int flags, union all_addr *addr);
#endif
struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class,
time_t now, unsigned long ttl, unsigned int flags);
void cache_reload(void);
@@ -1393,11 +1409,11 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes);
unsigned char *skip_questions(struct dns_header *header, size_t plen);
unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen);
unsigned int extract_request(struct dns_header *header, size_t qlen,
char *name, unsigned short *typep);
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
unsigned short *typep, unsigned short *classp);
void setup_reply(struct dns_header *header, unsigned int flags, int ede);
int extract_addresses(struct dns_header *header, size_t qlen, char *name,
time_t now, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign,
time_t now, struct ipsets *ipsets, struct ipsets *nftsets,
int check_rebind, int no_cache_dnssec, int secure);
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
void report_addresses(struct dns_header *header, size_t len, u32 mark);
@@ -1416,6 +1432,7 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp,
int *offset, unsigned short type, unsigned short class, char *format, ...);
int in_arpa_name_2_addr(char *namein, union all_addr *addrp);
int private_net(struct in_addr addr, int ban_localhost);
int private_net6(struct in6_addr *a, int ban_localhost);
/* extract_name ops */
#define EXTR_NAME_EXTRACT 1
#define EXTR_NAME_COMPARE 2
@@ -1530,7 +1547,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
#ifdef HAVE_DNSSEC
void pop_and_retry_query(struct frec *forward, int status, time_t now);
int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *n,
int class, char *name, char *keyname, struct server *server,
int class, char *name, struct server *server,
int *keycount, int *validatecount);
#endif
unsigned char *tcp_request(int confd, time_t now,
@@ -1605,6 +1622,7 @@ void lease6_reset(void);
struct dhcp_lease *lease6_find_by_client(struct dhcp_lease *first, int lease_type,
unsigned char *clid, int clid_len, unsigned int iaid);
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr);
struct dhcp_lease *lease6_find_by_plain_addr(struct in6_addr *addr);
u64 lease_find_max_addr6(struct dhcp_context *context);
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface);
void lease_update_slaac(time_t now);
@@ -1617,6 +1635,8 @@ void lease_set_hwaddr(struct dhcp_lease *lease, const unsigned char *hwaddr,
void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, char *domain, char *config_domain);
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now);
void lease_set_agent_id(struct dhcp_lease *lease, unsigned char *new, int len);
void lease_set_vendorclass(struct dhcp_lease *lease, unsigned char *new, int len);
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
unsigned char *clid, int clid_len);
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
@@ -1637,7 +1657,8 @@ void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data,
#ifdef HAVE_DHCP
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
size_t sz, time_t now, int unicast_dest, int loopback,
int *is_inform, int pxe, struct in_addr fallback, time_t recvtime);
int *is_inform, int pxe, struct in_addr fallback,
time_t recvtime, struct in_addr leasequery_source);
unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
int clid_len, unsigned char *clid, int *len_out);
#endif
@@ -1654,7 +1675,7 @@ void send_event(int fd, int event, int data, char *msg);
void clear_cache_and_reload(time_t now);
#ifdef HAVE_DNSSEC
int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header *header,
ssize_t *plen, int class, struct server *server, int *keycount, int *validatecount);
ssize_t *plen, char *name, int class, struct server *server, int *keycount, int *validatecount);
#endif
/* netlink.c */

View File

@@ -997,49 +997,66 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
unsigned long ttl;
union all_addr a;
if (ntohs(header->qdcount) != 1 ||
!(p = skip_name(p, header, plen, 4)))
return STAT_BOGUS;
GETSHORT(qtype, p);
GETSHORT(qclass, p);
if (qtype != T_DS || qclass != class)
return STAT_BOGUS;
/* A SERVFAIL answer has been seen to a DS query not at start of authority,
/* A SERVFAIL answer has been seen to a DS query not at start of authority,
so treat it as such and continue to search for a DS or proof of no existence
further down the tree. */
if (RCODE(header) == SERVFAIL)
servfail = neganswer = nons = 1;
else
{
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl, validate_counter);
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl, validate_counter);
p = (unsigned char *)(header+1);
if (ntohs(header->qdcount) != 1 ||
!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 4))
return STAT_BOGUS;
GETSHORT(qtype, p);
GETSHORT(qclass, p);
if (qtype != T_DS || qclass != class)
return STAT_BOGUS;
if (!servfail)
{
if (STAT_ISEQUAL(rc, STAT_INSECURE))
{
my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0);
return STAT_BOGUS | DNSSEC_FAIL_INDET;
if (option_bool(OPT_BOGUSPRIV) &&
(flags = in_arpa_name_2_addr(name, &a)) &&
((flags == F_IPV6 && private_net6(&a.addr6, 0)) || (flags == F_IPV4 && private_net(a.addr4, 0))))
{
my_syslog(LOG_INFO, _("Insecure reply received for DS %s, assuming that's OK for a RFC-1918 address."), name);
neganswer = 1;
nons = 0; /* If we're faking a DS, fake one with an NS. */
neg_ttl = DNSSEC_ASSUMED_DS_TTL;
}
else if (lookup_domain(name, F_DOMAINSRV, NULL, NULL))
{
my_syslog(LOG_INFO, _("Insecure reply received for DS %s, assuming non-DNSSEC domain-specific server."), name);
neganswer = 1;
nons = 0; /* If we're faking a DS, fake one with an NS. */
neg_ttl = DNSSEC_ASSUMED_DS_TTL;
}
else
{
my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0);
return STAT_BOGUS | DNSSEC_FAIL_INDET;
}
}
p = (unsigned char *)(header+1);
if (!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 4))
return STAT_BOGUS;
p += 4; /* qtype, qclass */
/* If the key needed to validate the DS is on the same domain as the DS, we'll
loop getting nowhere. Stop that now. This can happen of the DS answer comes
from the DS's zone, and not the parent zone. */
if (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname))
else
{
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS", 0);
return STAT_BOGUS;
if (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname))
{
/* If the key needed to validate the DS is on the same domain as the DS, we'll
loop getting nowhere. Stop that now. This can happen of the DS answer comes
from the DS's zone, and not the parent zone. */
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS", 0);
return STAT_BOGUS;
}
if (!STAT_ISEQUAL(rc, STAT_SECURE))
return rc;
}
if (!STAT_ISEQUAL(rc, STAT_SECURE))
return rc;
}
if (!neganswer)
@@ -1129,9 +1146,19 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
/* We only cache validated DS records, DNSSECOK flag hijacked
to store presence/absence of NS. */
if (nons)
flags &= ~F_DNSSECOK;
{
if (lookup_domain(name, F_DOMAINSRV, NULL, NULL))
{
my_syslog(LOG_WARNING, _("Negative DS reply without NS record received for %s, assuming non-DNSSEC domain-specific server."), name);
nons = 0;
}
else
/* We only cache validated DS records, DNSSECOK flag hijacked
to store presence/absence of NS. */
flags &= ~F_DNSSECOK;
}
}
cache_start_insert();
/* Use TTL from NSEC for negative cache entries */
@@ -1236,6 +1263,7 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
p += 8; /* class, type, TTL */
GETSHORT(rdlen, p);
psave = p;
if (!extract_name(header, plen, &p, workspace2, EXTR_NAME_EXTRACT, 0))
return DNSSEC_FAIL_BADPACKET;
@@ -1258,7 +1286,22 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
workspace1--;
*workspace1 = '*';
}
rdlen -= p - psave;
/* rdlen is now length of type map, and p points to it
packet checked to be as long as rdlen implies in prove_non_existence() */
/* check that the first typemap is complete. */
if (rdlen < 2 || rdlen < p[1] + 2)
return DNSSEC_FAIL_BADPACKET;
/* RFC 6672 5.3.4.1. */
#define DNAME_OFFSET (T_DNAME >> 3)
#define DNAME_MASK (0x80 >> (T_DNAME & 0x07))
if (p[0] == 0 && (p[1] >= DNAME_OFFSET + 1) && (p[2 + DNAME_OFFSET] & DNAME_MASK) != 0 &&
hostname_issubdomain(name, workspace1) == 1)
return DNSSEC_FAIL_NONSEC;
rc = hostname_cmp(workspace1, name);
if (rc == 0)
@@ -1269,16 +1312,12 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
/* NSEC with the same name as the RR we're testing, check
that the type in question doesn't appear in the type map */
rdlen -= p - psave;
/* rdlen is now length of type map, and p points to it
packet checked to be as long as rdlen implies in prove_non_existence() */
/* If we can prove that there's no NS record, return that information. */
if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) != 0)
*nons = 0;
if (rdlen >= 2 && p[0] == 0)
if (p[0] == 0 && p[1] >= 1)
{
/* If we can prove that there's no NS record, return that information. */
if (nons && (p[2] & (0x80 >> T_NS)) != 0)
*nons = 0;
/* A CNAME answer would also be valid, so if there's a CNAME is should
have been returned. */
if ((p[2] & (0x80 >> T_CNAME)) != 0)
@@ -1290,10 +1329,10 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
return DNSSEC_FAIL_NONSEC;
}
while (rdlen >= 2)
while (rdlen > 0)
{
if (!CHECK_LEN(header, p, plen, rdlen))
if (rdlen < 2 || rdlen < p[1] + 2)
return DNSSEC_FAIL_BADPACKET;
if (p[0] == type >> 8)
@@ -1433,7 +1472,11 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
p += hash_len; /* skip next-domain hash */
rdlen -= p - psave;
if (rdlen >= 2 && p[0] == 0)
/* check that the first typemap is complete. */
if (rdlen < 2 || rdlen < p[1] + 2)
return DNSSEC_FAIL_BADPACKET;
if (p[0] == 0 && p[1] >= 1)
{
/* If we can prove that there's no NS record, return that information. */
if (nons && (p[2] & (0x80 >> T_NS)) != 0)
@@ -1451,8 +1494,11 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
return 0;
}
while (rdlen >= 2)
while (rdlen > 0)
{
if (rdlen < 2 || rdlen < p[1] + 2)
return DNSSEC_FAIL_BADPACKET;
if (p[0] == type >> 8)
{
/* Does the NSEC3 say our type exists? */
@@ -1924,11 +1970,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
static unsigned char **targets = NULL;
static int target_sz = 0;
unsigned char *ans_start, *p1, *p2;
int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx;
int i, j, rc = STAT_INSECURE;
unsigned char *ans_start, *p1, *p2, *p3;
int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx, gotdname;
int i, j, k, rc = STAT_INSECURE;
int secure = STAT_SECURE;
int rc_nsec;
unsigned long ttl;
/* extend rr_status if necessary */
if (daemon->rr_status_sz < ntohs(header->ancount) + ntohs(header->nscount))
{
@@ -1975,27 +2023,119 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* Can't validate an RRSIG query */
if (qtype == T_RRSIG)
return STAT_INSECURE;
/* Find CNAME targets. */
for (gotdname = i = 0; i < ntohs(header->ancount); i++)
{
if (!(p1 = skip_name(p1, header, plen, 10)))
return STAT_BOGUS; /* bad packet */
GETSHORT(type1, p1);
GETSHORT(class1, p1);
p1 += 4; /* TTL */
GETSHORT(rdlen1, p1);
if (type1 == T_DNAME)
gotdname = 1;
if (qtype != T_CNAME && qtype != T_ANY && type1 == T_CNAME && class1 == qclass)
{
if (!expand_workspace(&targets, &target_sz, targetidx))
return STAT_BOGUS;
targets[targetidx++] = p1; /* pointer to target name */
}
if (!ADD_RDLEN(header, p1, plen, rdlen1))
return STAT_BOGUS;
}
if (qtype != T_CNAME && qtype != T_ANY)
for (j = ntohs(header->ancount); j != 0; j--)
/* A DNAME capable of sythesising a CNAME means we don't need to validate the CNAME,
we can just assume that it's valid. RFC 4035 3.2.3 */
if (gotdname)
for (p1 = ans_start, i = 0; i < ntohs(header->ancount); i++)
{
if (!(p1 = skip_name(p1, header, plen, 10)))
if (!extract_name(header, plen, &p1, name, EXTR_NAME_EXTRACT, 10))
return STAT_BOGUS; /* bad packet */
GETSHORT(type2, p1);
p1 += 6; /* class, TTL */
GETSHORT(rdlen2, p1);
GETSHORT(type1, p1);
GETSHORT(class1, p1);
p1 += 4; /* TTL */
GETSHORT(rdlen1, p1);
if (type2 == T_CNAME)
if (type1 != T_DNAME)
{
if (!expand_workspace(&targets, &target_sz, targetidx))
if (!ADD_RDLEN(header, p1, plen, rdlen1))
return STAT_BOGUS;
targets[targetidx++] = p1; /* pointer to target name */
}
if (!ADD_RDLEN(header, p1, plen, rdlen2))
return STAT_BOGUS;
else
{
if (!extract_name(header, plen, &p1, keyname, EXTR_NAME_EXTRACT, 0))
return STAT_BOGUS; /* bad packet */
/* We now have the name of the DNAME in name, and the target in keyname.
Look for any CNAMEs which could have been synthesised from this DNAME
and pre-qualify them. */
for (p2 = ans_start, j = 0; j < ntohs(header->ancount); j++)
{
if (!extract_name(header, plen, &p2, daemon->cname, EXTR_NAME_EXTRACT, 10))
return STAT_BOGUS; /* bad packet */
GETSHORT(type2, p2);
GETSHORT(class2, p2);
GETLONG(ttl, p2);
GETSHORT(rdlen2, p2);
if (type2 != T_CNAME || class2 != class1)
{
if (!ADD_RDLEN(header, p2, plen, rdlen2))
return STAT_BOGUS;
}
else
{
size_t name_prefix_len = strlen(daemon->cname) - strlen(name);
if (!extract_name(header, plen, &p2, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
return STAT_BOGUS; /* bad packet */
/* We have the name of the CNAME in daemon->cname, and the target in daemon->workspacename.
See if the CNAME was sythesised from the DNAME.
CNAME must be <subdomain>.<dname>
CNAME target must be <subdomain>.<dname_target>
<subdomain>s must match for name and target. */
if (hostname_issubdomain(name, daemon->cname) == 1 &&
hostname_issubdomain(keyname, daemon->workspacename) == 1 &&
name_prefix_len == strlen(daemon->workspacename) - strlen(keyname))
{
char save = daemon->cname[name_prefix_len];
daemon->cname[name_prefix_len] = 0;
daemon->workspacename[name_prefix_len] = 0;
if (hostname_isequal(daemon->cname, daemon->workspacename))
{
/* pre-qualify this as validated */
daemon->rr_status[j] = ttl > 0 ? ttl : 1;
/* and remove it from the targets we need to have validated answers to. */
if (class2 == qclass)
{
daemon->cname[name_prefix_len] = save;
for (k = 0; k <targetidx; k++)
if ((p3 = targets[k]))
{
int rc1;
if (!(rc1 = extract_name(header, plen, &p3, daemon->cname, EXTR_NAME_COMPARE, 0)))
return STAT_BOGUS; /* bad packet */
if (rc1 == 1)
targets[k] = NULL;
}
}
}
}
}
}
}
}
for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++)
@@ -2014,6 +2154,10 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* Don't try and validate RRSIGs! */
if (type1 == T_RRSIG)
continue;
/* Pre-validated by DNAME above don't validate. */
if (daemon->rr_status[i] != 0)
continue;
/* Check if we've done this RRset already */
for (p2 = ans_start, j = 0; j < i; j++)
@@ -2155,7 +2299,9 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* 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. */
the answer is in an unsigned zone, or there's NSEC records.
For a DS record, we return INSECURE, which almost always turns
into BOGUS in the caller. */
if ((rc_nsec = prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons, nsec_ttl, validate_counter)) != 0)
{
if (rc_nsec & DNSSEC_FAIL_WORK)
@@ -2163,7 +2309,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* Empty DS without NSECS */
if (qtype == T_DS)
return STAT_BOGUS | rc_nsec;
return STAT_INSECURE;
if ((rc_nsec & (DNSSEC_FAIL_NONSEC | DNSSEC_FAIL_NSEC3_ITERS)) &&
!STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))

View File

@@ -20,7 +20,7 @@ static int order(char *qdomain, size_t qlen, struct server *serv);
static int order_qsort(const void *a, const void *b);
static int order_servers(struct server *s, struct server *s2);
/* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain. */
/* If the server is USE_RESOLV or LITERAL_ADDRESS, it lives on the local_domains chain. */
#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
void build_server_array(void)
@@ -94,8 +94,7 @@ void build_server_array(void)
server=/.example.com/ works.
A flag of F_SERVER returns an upstream server only.
A flag of F_DNSSECOK returns a DNSSEC capable server only and
also disables NODOTS servers from consideration.
A flag of F_DNSSECOK disables NODOTS servers from consideration.
A flag of F_DOMAINSRV returns a domain-specific server only.
A flag of F_CONFIG returns anything that generates a local
reply of IPv4 or IPV6.
@@ -260,7 +259,6 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout)
return 1;
}
/* Return first server in group of equivalent servers; this is the "master" record. */
int server_samegroup(struct server *a, struct server *b)
{
return order_servers(a, b) == 0;
@@ -297,11 +295,13 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
}
else
{
/* Now the servers are on order between low and high, in the order
IPv6 addr, IPv4 addr, return zero for both, resolvconf servers, send upstream, no-data return.
/* Now the matching server records are all between low and high.
order_qsort() ensures that they are in the order
IPv6 addr, IPv4 addr, return zero for both, no-data return,
"use resolvconf" servers, domain-specific upstream servers.
See which of those match our query in that priority order and narrow (low, high) */
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
if (!(flags & F_SERVER) && i != nlow && (flags & F_IPV6))
@@ -326,33 +326,27 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
{
nlow = i;
/* Short to resolv.conf servers */
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++);
/* now look for a NXDOMAIN answer --local=/domain/ */
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
if (i != nlow)
if (!(flags & (F_DOMAINSRV | F_SERVER)) && i != nlow)
nhigh = i;
else
{
/* now look for a server */
for (i = nlow; i < nhigh && !(daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
nlow = i;
/* return "use resolv.conf servers" if they exist */
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++);
if (i != nlow)
{
/* If we want a server that can do DNSSEC, and this one can't,
return nothing, similarly if were looking only for a server
for a particular domain. */
if ((flags & F_DNSSECOK) && !(daemon->serverarray[nlow]->flags & SERV_DO_DNSSEC))
nlow = nhigh;
else if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
nlow = nhigh;
else
nhigh = i;
}
nhigh = i;
else
{
/* --local=/domain/, only return if we don't need a server. */
if (flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER))
nhigh = i;
/* If we want a server for a particular domain, and this one isn't, return nothing. */
if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
nlow = nhigh;
else
nlow = i;
}
}
}
@@ -407,6 +401,8 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
setup_reply(header, flags, ede);
gotname &= ~F_QUERY;
if (flags & (F_NXDOMAIN | F_NOERR))
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL, 0);
@@ -472,7 +468,7 @@ int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp)
/* Find server to send DNSSEC query to. This will normally be the
same as for the original query, but may be another if
servers for domains are involved. */
if (!lookup_domain(keyname, F_DNSSECOK, &first, &last))
if (!lookup_domain(keyname, F_SERVER | F_DNSSECOK, &first, &last))
return -1;
for (index = first; index != last; index++)
@@ -546,12 +542,14 @@ static int order_qsort(const void *a, const void *b)
rc = order_servers(s1, s2);
/* Sort all literal NODATA and local IPV4 or IPV6 responses together,
in a very specific order. We flip the SERV_LITERAL_ADDRESS bit
so the order is IPv6 literal, IPv4 literal, all-zero literal,
unqualified servers, upstream server, NXDOMAIN literal. */
in a very specific order IPv6 literal, IPv4 literal, all-zero literal,
NXDOMAIN literal. We also include SERV_USE_RESOLV in this, so that
use-standard servers sort before ordinary servers. (SERV_USR_RESOLV set
implies that none of SERV_LITERAL_ADDRESS,SERV_4ADDR,SERV_6ADDR,SERV_ALL_ZEROS
are set) */
if (rc == 0)
rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) -
((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS);
rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS | SERV_USE_RESOLV))) -
((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS | SERV_USE_RESOLV)));
/* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
if (rc == 0)

View File

@@ -176,9 +176,9 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
int first, last, start = 0;
int forwarded = 0;
int ede = EDE_UNSET;
unsigned short rrtype;
unsigned short rrtype, rrclass;
gotname = extract_request(header, plen, daemon->namebuff, &rrtype);
gotname = extract_request(header, plen, daemon->namebuff, &rrtype, &rrclass);
/* Check for retry on existing query.
FREC_DNSKEY and FREC_DS_QUERY are never set in flags, so the test below
@@ -192,7 +192,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
old_reply = 1;
fwd_flags = forward->flags;
}
else if (gotname && (forward = lookup_frec(daemon->namebuff, C_IN, (int)rrtype, -1, fwd_flags,
else if (gotname && (forward = lookup_frec(daemon->namebuff, (int)rrclass, (int)rrtype, -1, fwd_flags,
FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION |
FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE)))
{
@@ -264,7 +264,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
The original query we sent is now in packet buffer and the query name in the
new instance is on daemon->namebuff. */
if (extract_request(header, forward->stash_len, daemon->workspacename, NULL))
if (extract_name(header, forward->stash_len, NULL, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
{
unsigned int i, gobig = 0;
char *s1, *s2;
@@ -329,8 +329,6 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
/* new query */
if (!forward)
{
unsigned char *p;
if (OPCODE(header) != QUERY)
{
flags = F_RCODE;
@@ -375,7 +373,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
forward->flags = fwd_flags;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (master->flags & SERV_DO_DNSSEC))
if (option_bool(OPT_DNSSEC_VALID))
{
plen = add_do_bit(header, plen, ((unsigned char *) header) + daemon->edns_pktsz);
@@ -390,11 +388,10 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
forward->frec_src.orig_id = ntohs(header->id);
forward->new_id = get_id();
header->id = ntohs(forward->new_id);
forward->frec_src.encode_bitmap = (!option_bool(OPT_NO_0x20) && option_bool(OPT_DO_0x20)) ? rand32() : 0;
forward->frec_src.encode_bigmap = NULL;
p = (unsigned char *)(header+1);
if (!extract_name(header, plen, &p, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
if (!extract_name(header, plen, NULL, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
goto reply;
/* Keep copy of query for retries and move to TCP */
@@ -459,7 +456,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
plen = forward->stash_len;
/* get query for logging. */
gotname = extract_request(header, plen, daemon->namebuff, NULL);
gotname = extract_request(header, plen, daemon->namebuff, NULL, NULL);
/* Find suitable servers: should never fail. */
if (!filter_servers(forward->sentto->arrayposn, F_DNSSECOK, &first, &last))
@@ -673,6 +670,7 @@ int fast_retry(time_t now)
return ret;
}
#if defined(HAVE_IPSET) || defined(HAVE_NFTSET)
static struct ipsets *domain_find_sets(struct ipsets *setlist, const char *domain) {
/* Similar algorithm to search_servers. */
struct ipsets *ipset_pos, *ret = NULL;
@@ -693,6 +691,7 @@ static struct ipsets *domain_find_sets(struct ipsets *setlist, const char *domai
return ret;
}
#endif
static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind,
int no_cache, int cache_secure, int bogusanswer, int ad_reqd, int do_bit, int added_pheader,
@@ -707,16 +706,19 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
(void)ad_reqd;
(void)do_bit;
#ifdef HAVE_IPSET
if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL))
ipsets = domain_find_sets(daemon->ipsets, daemon->namebuff);
#if defined(HAVE_IPSET) || defined(HAVE_NFTSET)
if ((daemon->ipsets || daemon->nftsets) && extract_name(header, n, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
{
# ifdef HAVE_IPSET
ipsets = domain_find_sets(daemon->ipsets, daemon->namebuff);
# endif
# ifdef HAVE_NFTSET
nftsets = domain_find_sets(daemon->nftsets, daemon->namebuff);
# endif
}
#endif
#ifdef HAVE_NFTSET
if (daemon->nftsets && extract_request(header, n, daemon->namebuff, NULL))
nftsets = domain_find_sets(daemon->nftsets, daemon->namebuff);
#endif
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL)))
{
/* Get extended RCODE. */
@@ -790,7 +792,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
log_query(F_UPSTREAM, NULL, NULL, "truncated", 0);
else if (!bogusanswer || (header->hb4 & HB4_CD))
{
if (rcode == NXDOMAIN && extract_request(header, n, daemon->namebuff, NULL) &&
if (rcode == NXDOMAIN && extract_name(header, n, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0) &&
(check_for_local_domain(daemon->namebuff, now) || lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL)))
{
/* if we forwarded a query for a locally known name (because it was for
@@ -804,7 +806,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (daemon->doctors && do_doctor(header, n, daemon->namebuff))
cache_secure = 0;
/* check_for_bogus_wildcard() does it's own caching, so
/* check_for_bogus_wildcard() does its own caching, so
don't call extract_addresses() if it triggers. */
if (daemon->bogus_addr && rcode != NXDOMAIN &&
check_for_bogus_wildcard(header, n, daemon->namebuff, now))
@@ -819,7 +821,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
}
else
{
int rc = extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure);
int rc = extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, check_rebind, no_cache, cache_secure);
if (rc != 0)
{
@@ -922,22 +924,26 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* Get the query we sent by UDP */
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
if (extract_request(header, forward->stash_len, daemon->namebuff, NULL))
log_query(F_UPSTREAM | F_NOEXTRA, daemon->namebuff, NULL, "truncated", 0);
/* Don't count failed UDP attempt AND TCP */
if (status != STAT_OK)
orig->work_counter++;
/* NOTE: Can't move connection marks from UDP to TCP */
plen = forward->stash_len;
status = swap_to_tcp(forward, now, status, header, &plen, forward->class, forward->sentto, &orig->work_counter, &orig->validate_counter);
/* We forked a new process. pop_and_retry_query() will be called when is completes. */
if (STAT_ISEQUAL(status, STAT_ASYNC))
if (!extract_name(header, forward->stash_len, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
status = STAT_ABANDONED;
else
{
forward->flags |= FREC_GONE_TO_TCP;
return;
log_query(F_UPSTREAM | F_NOEXTRA, daemon->namebuff, NULL, "truncated", 0);
/* Don't count failed UDP attempt AND TCP */
if (status != STAT_OK)
orig->work_counter++;
/* NOTE: Can't move connection marks from UDP to TCP */
plen = forward->stash_len;
status = swap_to_tcp(forward, now, status, header, &plen, daemon->namebuff, forward->class, forward->sentto, &orig->work_counter, &orig->validate_counter);
/* We forked a new process. pop_and_retry_query() will be called when is completes. */
if (STAT_ISEQUAL(status, STAT_ASYNC))
{
forward->flags |= FREC_GONE_TO_TCP;
return;
}
}
}
else
@@ -954,8 +960,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
status = dnssec_validate_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class, &orig->validate_counter);
else
status = dnssec_validate_reply(now, header, plen, daemon->namebuff, daemon->keyname, &forward->class,
!option_bool(OPT_DNSSEC_IGN_NS) && (forward->sentto->flags & SERV_DO_DNSSEC),
NULL, NULL, NULL, &orig->validate_counter);
!option_bool(OPT_DNSSEC_IGN_NS), NULL, NULL, NULL, &orig->validate_counter);
if (STAT_ISEQUAL(status, STAT_ABANDONED))
log_resource = 1;
@@ -1094,7 +1099,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
if (log_resource)
{
/* Log the actual validation that made us barf. */
if (extract_request(header, plen, daemon->namebuff, NULL))
if (extract_name(header, plen, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
my_syslog(LOG_WARNING, _("validation of %s failed: resource limit exceeded."),
daemon->namebuff[0] ? daemon->namebuff : ".");
}
@@ -1271,14 +1276,13 @@ void reply_query(int fd, time_t now)
server->query_latency = server->mma_latency/128;
/* Flip the bits back in the query name. */
p = (unsigned char *)(header+1);
if (!extract_name(header, n, &p, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
if (!extract_name(header, n, NULL, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
return;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
if ((forward->sentto->flags & SERV_DO_DNSSEC) && !(forward->flags & FREC_CHECKING_DISABLED))
if (!(forward->flags & FREC_CHECKING_DISABLED))
{
dnssec_validate(forward, header, n, STAT_OK, now);
return;
@@ -1305,8 +1309,7 @@ static void xor_array(unsigned int *arg1, unsigned int *arg2, unsigned int len)
/* Call extract_name() to flip case of query in packet according to the XOR of the bit maps help in arg1 and arg2 */
static void flip_queryname(struct dns_header *header, ssize_t len, struct frec_src *arg1, struct frec_src *arg2)
{
unsigned char *p = (unsigned char *)(header+1);
unsigned int *arg1p, *arg2p, arg1len, arg2len, *swapp, swap;
unsigned int *arg1p, *arg2p, arg1len, arg2len;
/* Two cases: bitmap is single 32 bit int, or it's arbitrary-length array of 32bit ints.
The two args may be different and of different lengths.
@@ -1325,17 +1328,14 @@ static void flip_queryname(struct dns_header *header, ssize_t len, struct frec_s
/* make arg1 the longer, if they differ. */
if (arg2len > arg1len)
{
swap = arg1len;
swapp = arg1p;
arg1len = arg2len;
arg1p = arg2p;
arg2len = swap;
arg2p = swapp;
unsigned int swapl = arg1len, *swapp = arg1p;
arg1len = arg2len, arg1p = arg2p;
arg2len = swapl, arg2p = swapp;
}
/* XOR on shorter length, flip on longer, operate on longer */
xor_array(arg1p, arg2p, arg2len);
extract_name(header, len, &p, (char *)arg1p, EXTR_NAME_FLIP, arg1len);
extract_name(header, len, NULL, (char *)arg1p, EXTR_NAME_FLIP, arg1len);
xor_array(arg1p, arg2p, arg2len); /* restore */
}
@@ -1393,7 +1393,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
no_cache_dnssec = 1;
bogusanswer = 1;
if (extract_request(header, n, daemon->namebuff, NULL))
if (extract_name(header, n, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
domain = daemon->namebuff;
}
@@ -1821,14 +1821,14 @@ void receive_query(struct listener *listen, time_t now)
#endif
if (OPCODE(header) != QUERY)
log_query_mysockaddr(F_QUERY | F_FORWARD, "opcode", &source_addr, "non-query", 0);
else if (extract_request(header, (size_t)n, daemon->namebuff, &type))
log_query_mysockaddr((auth_dns ? F_NOERR : 0) | F_QUERY | F_FORWARD | F_CONFIG, NULL, &source_addr, NULL, OPCODE(header));
else if (extract_request(header, (size_t)n, daemon->namebuff, &type, NULL))
{
#ifdef HAVE_AUTH
struct auth_zone *zone;
#endif
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
&source_addr, auth_dns ? "auth" : "query", type);
log_query_mysockaddr((auth_dns ? F_NOERR | F_AUTH : 0 ) | F_QUERY | F_FORWARD, daemon->namebuff,
&source_addr, NULL, type);
#ifdef HAVE_AUTH
/* Find queries for zones we're authoritative for, and answer them directly.
@@ -2130,7 +2130,7 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
/* We us the _ONCE veriant of read_write() here because we've set a timeout on the tcp socket
and wish to abort if the whole data is not read/written within the timeout. */
if ((!data_sent && !read_write(serv->tcpfd, (unsigned char *)packet, qsize + sizeof(u16), RW_WRITE_ONCE)) ||
if ((!data_sent && !read_write(serv->tcpfd, (unsigned char *)packet, qsize + sizeof(u16), RW_WRITE_ONCE)) ||
!read_write(serv->tcpfd, (unsigned char *)length, sizeof (*length), RW_READ_ONCE) ||
!read_write(serv->tcpfd, payload, (rsize = ntohs(*length)), RW_READ_ONCE))
{
@@ -2146,7 +2146,7 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
else
goto failed;
}
/* If the question section of the reply doesn't match the question we sent, then
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers.
@@ -2176,14 +2176,13 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
returned truncated. (Which type held in status).
Resend the query (in header) via TCP */
int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *plenp,
int class, char *name, char *keyname, struct server *server,
int class, char *name, struct server *server,
int *keycount, int *validatecount)
{
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
struct dns_header *new_header = (struct dns_header *)&packet[2];
int start, first, last, new_status;
ssize_t n = *plenp;
int have_req = extract_request(header, n, keyname, NULL);
int log_save = daemon->log_display_id;
*plenp = 0;
@@ -2200,48 +2199,48 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple
first = start = server->arrayposn;
last = first + 1;
if (!STAT_ISEQUAL(status, STAT_OK) && (!have_req || (start = dnssec_server(server, keyname, &first, &last)) == -1))
new_status = STAT_ABANDONED;
else if ((n = tcp_talk(first, last, start, packet, n, 0, 0, &server)) == 0)
if (!STAT_ISEQUAL(status, STAT_OK) && (start = dnssec_server(server, name, &first, &last)) == -1)
new_status = STAT_ABANDONED;
else
{
if (have_req)
{
if (STAT_ISEQUAL(status, STAT_OK))
log_query_mysockaddr(F_SERVER | F_FORWARD, keyname, &server->addr, NULL, 0);
else
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, keyname, &server->addr,
STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0);
}
new_status = tcp_key_recurse(now, status, new_header, n, class, name, keyname, server, 0, 0, keycount, validatecount);
if (STAT_ISEQUAL(status, STAT_OK))
{
/* downstream query: strip DNSSSEC RRs and see if it will
fit in a UDP reply. */
rrfilter(new_header, (size_t *)&n, RRFILTER_DNSSEC);
log_query_mysockaddr(F_SERVER | F_FORWARD, name, &server->addr, NULL, 0);
else
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, name, &server->addr,
STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0);
if (n >= daemon->edns_pktsz)
if ((n = tcp_talk(first, last, start, packet, n, 0, 0, &server)) == 0)
new_status = STAT_ABANDONED;
else
{
new_status = tcp_key_recurse(now, status, new_header, n, class, daemon->namebuff, daemon->keyname, server, 0, 0, keycount, validatecount);
if (STAT_ISEQUAL(status, STAT_OK))
{
/* still too bIg, strip optional sections and try again. */
new_header->nscount = htons(0);
new_header->arcount = htons(0);
n = resize_packet(new_header, n, NULL, 0);
/* downstream query: strip DNSSSEC RRs and see if it will
fit in a UDP reply. */
rrfilter(new_header, (size_t *)&n, RRFILTER_DNSSEC);
if (n >= daemon->edns_pktsz)
{
/* truncating the packet will break the answers, so remove them too
and mark the reply as truncated. */
new_header->ancount = htons(0);
/* still too bIg, strip optional sections and try again. */
new_header->nscount = htons(0);
new_header->arcount = htons(0);
n = resize_packet(new_header, n, NULL, 0);
new_status = STAT_TRUNCATED;
if (n >= daemon->edns_pktsz)
{
/* truncating the packet will break the answers, so remove them too
and mark the reply as truncated. */
new_header->ancount = htons(0);
n = resize_packet(new_header, n, NULL, 0);
new_status = STAT_TRUNCATED;
}
}
/* return the stripped or truncated reply. */
memcpy(header, new_header, n);
*plenp = n;
}
/* return the stripped or truncated reply. */
memcpy(header, new_header, n);
*plenp = n;
}
}
@@ -2271,8 +2270,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
new_status = dnssec_validate_ds(now, header, n, name, keyname, class, validatecount);
else
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class,
!option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC),
NULL, NULL, NULL, validatecount);
!option_bool(OPT_DNSSEC_IGN_NS), NULL, NULL, NULL, validatecount);
if (!STAT_ISEQUAL(new_status, STAT_NEED_DS) && !STAT_ISEQUAL(new_status, STAT_NEED_KEY) && !STAT_ISEQUAL(new_status, STAT_ABANDONED))
break;
@@ -2286,7 +2284,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
if (STAT_ISEQUAL(new_status, STAT_ABANDONED))
{
/* Log the actual validation that made us barf. */
if (extract_request(header, n, daemon->namebuff, NULL))
if (extract_name(header, n, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
my_syslog(LOG_WARNING, _("validation of %s failed: resource limit exceeded."),
daemon->namebuff[0] ? daemon->namebuff : ".");
break;
@@ -2466,23 +2464,37 @@ unsigned char *tcp_request(int confd, time_t now,
if (OPCODE(header) != QUERY)
{
log_query_mysockaddr(F_QUERY | F_FORWARD, "opcode", &peer_addr, "non-query", 0);
log_query_mysockaddr((auth_dns ? F_NOERR : 0) | F_QUERY | F_FORWARD | F_CONFIG, NULL, &peer_addr, NULL, OPCODE(header));
gotname = 0;
flags = F_RCODE;
}
else if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
else if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype, NULL)))
ede = EDE_INVALID_DATA;
else
{
if (saved_question)
blockdata_free(saved_question);
do_bit = 0;
if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL))
{
unsigned short ede_flags;
have_pseudoheader = 1;
pheader += 4; /* udp_size, ext_rcode */
GETSHORT(ede_flags, pheader);
if (ede_flags & 0x8000)
do_bit = 1; /* do bit */
}
size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &cacheable);
saved_question = blockdata_alloc((char *)header, (size_t)size);
saved_size = size;
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
&peer_addr, auth_dns ? "auth" : "query", qtype);
log_query_mysockaddr((auth_dns ? F_NOERR | F_AUTH : 0) | F_QUERY | F_FORWARD, daemon->namebuff,
&peer_addr, NULL, qtype);
#ifdef HAVE_AUTH
/* Find queries for zones we're authoritative for, and answer them directly.
@@ -2515,20 +2527,6 @@ unsigned char *tcp_request(int confd, time_t now,
else
dst_addr_4.s_addr = 0;
do_bit = 0;
if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL))
{
unsigned short ede_flags;
have_pseudoheader = 1;
pheader += 4; /* udp_size, ext_rcode */
GETSHORT(ede_flags, pheader);
if (ede_flags & 0x8000)
do_bit = 1; /* do bit */
}
ad_reqd = do_bit;
/* RFC 6840 5.7 */
if (header->hb4 & HB4_AD)
@@ -2598,7 +2596,7 @@ unsigned char *tcp_request(int confd, time_t now,
start = master->last_server;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (master->flags & SERV_DO_DNSSEC))
if (option_bool(OPT_DNSSEC_VALID))
{
size = add_do_bit(header, size, ((unsigned char *) header) + 65536);
@@ -2615,7 +2613,7 @@ unsigned char *tcp_request(int confd, time_t now,
else
{
/* get query name again for logging - may have been overwritten */
if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
if (!extract_name(header, (unsigned int)size, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
strcpy(daemon->namebuff, "query");
log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff, &serv->addr, NULL, 0);
@@ -2627,7 +2625,7 @@ unsigned char *tcp_request(int confd, time_t now,
if (checking_disabled || (header->hb4 & HB4_CD))
no_cache_dnssec = 1;
else if (master->flags & SERV_DO_DNSSEC)
else
{
int keycount = daemon->limit[LIMIT_WORK]; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
int validatecount = daemon->limit[LIMIT_CRYPTO];
@@ -2657,7 +2655,7 @@ unsigned char *tcp_request(int confd, time_t now,
no_cache_dnssec = 1;
bogusanswer = 1;
if (extract_request(header, m, daemon->namebuff, NULL))
if (extract_name(header, m, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
domain = daemon->namebuff;
}

View File

@@ -16,7 +16,7 @@
#include "dnsmasq.h"
#if defined(HAVE_IPSET) && defined(HAVE_LINUX_NETWORK)
#if defined(HAVE_IPSET)
#include <string.h>
#include <errno.h>

View File

@@ -25,7 +25,7 @@ static int read_leases(time_t now, FILE *leasestream)
unsigned long ei;
union all_addr addr;
struct dhcp_lease *lease;
int clid_len, hw_len, hw_type;
int opt_len, clid_len, hw_len, hw_type;
int items;
*daemon->dhcp_buff3 = *daemon->dhcp_buff2 = '\0';
@@ -55,6 +55,34 @@ static int read_leases(time_t now, FILE *leasestream)
continue;
}
#endif
/* Weird backwards compatible way of adding extra fields to leases */
if ((strcmp(daemon->dhcp_buff3, "vendorclass") == 0 || strcmp(daemon->dhcp_buff3, "agent-info") == 0))
{
if (fscanf(leasestream, " %764s", daemon->packet) == 1)
{
if (inet_pton(AF_INET, daemon->dhcp_buff2, &addr.addr4))
lease = lease_find_by_addr(addr.addr4);
#ifdef HAVE_DHCP6
else if (inet_pton(AF_INET6, daemon->dhcp_buff2, &addr.addr6))
lease = lease6_find_by_plain_addr(&addr.addr6);
#endif
else
continue;
if (lease)
{
opt_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
if (strcmp(daemon->dhcp_buff3, "vendorclass") == 0)
lease_set_vendorclass(lease, (unsigned char *)daemon->packet, opt_len);
else if (strcmp(daemon->dhcp_buff3, "agent-info") == 0)
lease_set_agent_id(lease, (unsigned char *)daemon->packet, opt_len);
}
}
continue;
}
if (fscanf(leasestream, " %64s %255s %764s",
daemon->namebuff, daemon->dhcp_buff, daemon->packet) != 3)
@@ -251,8 +279,8 @@ void lease_update_file(time_t now)
{
struct dhcp_lease *lease;
time_t next_event;
int i, err = 0;
int i, err = 0, extras;
if (file_dirty != 0 && daemon->lease_stream)
{
errno = 0;
@@ -260,9 +288,11 @@ void lease_update_file(time_t now)
if (errno != 0 || ftruncate(fileno(daemon->lease_stream), 0) != 0)
err = errno;
for (lease = leases; lease; lease = lease->next)
for (extras = 0, lease = leases; lease; lease = lease->next)
{
if (lease->agent_id || lease->vendorclass)
extras = 1;
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
continue;
@@ -335,7 +365,37 @@ void lease_update_file(time_t now)
}
}
#endif
if (extras)
{
/* Dump this at the end for least confusion with older parsing code. */
for (lease = leases; lease; lease = lease->next)
{
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
inet_ntop(AF_INET6, &lease->addr6, daemon->addrbuff, ADDRSTRLEN);
else
#endif
inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN);
if (lease->agent_id)
{
ourprintf(&err, "agent-info %s ", daemon->addrbuff);
for (i = 0; i < lease->agent_id_len - 1; i++)
ourprintf(&err, "%.2x:", lease->agent_id[i]);
ourprintf(&err, "%.2x\n", lease->agent_id[i]);
}
if (lease->vendorclass)
{
ourprintf(&err, "vendorclass %s ", daemon->addrbuff);
for (i = 0; i < lease->vendorclass_len - 1; i++)
ourprintf(&err, "%.2x:", lease->vendorclass[i]);
ourprintf(&err, "%.2x\n", lease->vendorclass[i]);
}
}
}
if (fflush(daemon->lease_stream) != 0 ||
fsync(fileno(daemon->lease_stream)) < 0)
err = errno;
@@ -713,6 +773,22 @@ struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 add
return NULL;
}
struct dhcp_lease *lease6_find_by_plain_addr(struct in6_addr *addr)
{
struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next)
{
if (!(lease->flags & (LEASE_TA | LEASE_NA)))
continue;
if (IN6_ARE_ADDR_EQUAL(&lease->addr6, addr))
return lease;
}
return NULL;
}
/* Find largest assigned address in context */
u64 lease_find_max_addr6(struct dhcp_context *context)
{
@@ -1084,6 +1160,46 @@ void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now)
#endif
}
void lease_set_agent_id(struct dhcp_lease *lease, unsigned char *new, int len)
{
if (!lease->agent_id && !new)
return;
if (lease->agent_id && new && lease->agent_id_len == len && memcmp(lease->agent_id, new, len) == 0)
return;
file_dirty = 1;
free(lease->agent_id);
lease->agent_id = NULL;
if (new && (lease->agent_id = whine_malloc(len)))
{
memcpy(lease->agent_id, new, len);
lease->agent_id_len = len;
}
}
void lease_set_vendorclass(struct dhcp_lease *lease, unsigned char *new, int len)
{
if (!lease->vendorclass && !new)
return;
if (lease->vendorclass && new && lease->vendorclass_len == len && memcmp(lease->vendorclass, new, len) == 0)
return;
file_dirty = 1;
free(lease->vendorclass);
lease->vendorclass = NULL;
if (new && (lease->vendorclass = whine_malloc(len)))
{
memcpy(lease->vendorclass, new, len);
lease->vendorclass_len = len;
}
}
void rerun_scripts(void)
{
struct dhcp_lease *lease;
@@ -1143,9 +1259,11 @@ int do_script_run(time_t now)
#endif
old_leases = lease->next;
free(lease->old_hostname);
free(lease->hostname);
free(lease->clid);
free(lease->extradata);
free(lease->agent_id);
free(lease->vendorclass);
free(lease);
return 1;

View File

@@ -43,6 +43,10 @@ const char * metric_names[] = {
"leases_allocated_6",
"leases_pruned_6",
"tcp_connections",
"dhcp_leasequery",
"dhcp_lease_unassigned",
"dhcp_lease_actve",
"dhcp_lease_unknown"
};
const char* get_metric_name(int i) {

View File

@@ -42,6 +42,10 @@ enum {
METRIC_LEASES_ALLOCATED_6,
METRIC_LEASES_PRUNED_6,
METRIC_TCP_CONNECTIONS,
METRIC_DHCPLEASEQUERY,
METRIC_DHCPLEASEUNASSIGNED,
METRIC_DHCPLEASEACTIVE,
METRIC_DHCPLEASEUNKNOWN,
__METRIC_MAX,
};

View File

@@ -1379,7 +1379,7 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
/* cannot set source _port_ for TCP connections. */
if (is_tcp)
port = 0;
else if (port == 0 && daemon->max_port != 0)
else if (port == 0 && daemon->max_port != 0 && daemon->max_port >= daemon->min_port)
{
/* Bind a random port within the range given by min-port and max-port if either
or both are set. Otherwise use the OS's random ephemeral port allocation by
@@ -1587,33 +1587,6 @@ void check_servers(int no_loop_check)
for (count = 0, serv = daemon->servers; serv; serv = serv->next)
{
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
if (!(serv->flags & SERV_FOR_NODOTS))
serv->flags |= SERV_DO_DNSSEC;
/* Disable DNSSEC validation when using server=/domain/.... servers
unless there's a configured trust anchor. */
if (strlen(serv->domain) != 0)
{
struct ds_config *ds;
char *domain = serv->domain;
/* .example.com is valid */
while (*domain == '.')
domain++;
for (ds = daemon->ds; ds; ds = ds->next)
if (ds->name[0] != 0 && hostname_isequal(domain, ds->name))
break;
if (!ds)
serv->flags &= ~SERV_DO_DNSSEC;
}
}
#endif
port = prettyprint_addr(&serv->addr, daemon->namebuff);
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
@@ -1659,10 +1632,6 @@ void check_servers(int no_loop_check)
{
char *s1, *s2, *s3 = "", *s4 = "";
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
s3 = _("(no DNSSEC)");
#endif
if (serv->flags & SERV_FOR_NODOTS)
s1 = _("unqualified"), s2 = _("names");
else if (strlen(serv->domain) == 0)
@@ -1696,7 +1665,7 @@ void check_servers(int no_loop_check)
if (++locals <= LOCALS_LOGGED)
my_syslog(LOG_INFO, _("using only locally-known addresses for %s"), serv->domain);
}
else if (serv->flags & SERV_USE_RESOLV)
else if (serv->flags & SERV_USE_RESOLV && serv->domain_len != 0)
my_syslog(LOG_INFO, _("using standard nameservers for %s"), serv->domain);
}

View File

@@ -17,7 +17,7 @@
#include "dnsmasq.h"
#if defined (HAVE_NFTSET) && defined (HAVE_LINUX_NETWORK)
#if defined (HAVE_NFTSET)
#include <nftables/libnftables.h>

View File

@@ -195,6 +195,7 @@ struct myoption {
#define LOPT_PXE_OPT 386
#define LOPT_NO_ENCODE 387
#define LOPT_DO_ENCODE 388
#define LOPT_LEASEQUERY 389
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -394,6 +395,7 @@ static const struct myoption opts[] =
{ "use-stale-cache", 2, 0 , LOPT_STALE_CACHE },
{ "no-ident", 0, 0, LOPT_NO_IDENT },
{ "max-tcp-connections", 1, 0, LOPT_MAX_PROCS },
{ "leasequery", 2, 0, LOPT_LEASEQUERY },
{ NULL, 0, 0, 0 }
};
@@ -499,6 +501,7 @@ static struct {
{ '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
{ LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
{ LOPT_SHARED_NET, ARG_DUP, "<iface>|<addr>,<addr>", gettext_noop("Specify extra networks sharing a broadcast domain for DHCP"), NULL},
{ LOPT_LEASEQUERY, ARG_DUP, "[<addr>[/prefix>]]", gettext_noop("Enable RFC 4388 leasequery functions for DHCPv4"), NULL },
{ '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
{ '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
{ LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
@@ -961,7 +964,7 @@ char *parse_server(char *arg, struct server_details *sdetails)
hints.ai_family = AF_UNSPEC;
/* Get addresses suitable for sending datagrams. We assume that we can use the
same addresses for TCP connections. Settting this to zero gets each address
same addresses for TCP connections. Setting this to zero gets each address
threes times, for SOCK_STREAM, SOCK_RAW and SOCK_DGRAM, which is not useful. */
hints.ai_socktype = SOCK_DGRAM;
@@ -2675,15 +2678,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (msize > 128)
ret_err_free(_("bad prefix length"), new);
mask = (1LLU << (128 - msize)) - 1LLU;
/* prefix==64 overflows the mask calculation */
if (msize <= 64)
mask = (u64)-1LL;
else
mask = (1LLU << (128 - msize)) - 1LLU;
new->is6 = 1;
new->prefixlen = msize;
/* prefix==64 overflows the mask calculation above */
if (msize <= 64)
mask = (u64)-1LL;
new->end6 = new->start6;
setaddr6part(&new->start6, addrpart & ~mask);
setaddr6part(&new->end6, addrpart | mask);
@@ -2912,7 +2915,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
arg = comma;
} while (arg);
break;
#ifdef HAVE_DHCP
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
case LOPT_LEASEQUERY:
set_option_bool(OPT_LEASEQUERY);
if (!arg)
break;
#pragma GCC diagnostic pop
#endif
case 'B': /* --bogus-nxdomain */
case LOPT_IGNORE_ADDR: /* --ignore-address */
{
@@ -2948,6 +2960,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
baddr->next = daemon->bogus_addr;
daemon->bogus_addr = baddr;
}
#ifdef HAVE_DHCP
else if (option == LOPT_LEASEQUERY)
{
baddr->next = daemon->leasequery_addr;
daemon->leasequery_addr = baddr;
}
#endif
else
{
baddr->next = daemon->ignore_addr;
@@ -3435,6 +3454,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
set_option_bool(OPT_EXTRALOG);
set_option_bool(OPT_LOG_PROTO);
}
else if (strcmp(arg, "auth") == 0)
set_option_bool(OPT_AUTH_LOG);
}
break;
@@ -3987,7 +4008,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
while (arg)
{
comma = split(arg);
if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
if (strchr(arg, ':')) /* Ethernet address, netid or binary CLID */
{
if ((arg[0] == 'i' || arg[0] == 'I') &&
(arg[1] == 'd' || arg[1] == 'D') &&
@@ -5337,7 +5358,8 @@ err:
new->class = C_IN;
new->name = NULL;
new->digestlen = 0;
if ((comma = split(arg)) && (algo = split(comma)))
{
int class = 0;
@@ -5355,29 +5377,37 @@ err:
algo = split(comma);
}
}
if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
!atoi_check16(comma, &new->keytag) ||
!atoi_check8(algo, &new->algo) ||
!atoi_check8(digest, &new->digest_type) ||
!(new->name = canonicalise_opt(arg)))
if (!(new->name = canonicalise_opt(arg)))
ret_err_free(_("bad trust anchor"), new);
/* Upper bound on length */
len = (2*strlen(keyhex))+1;
new->digest = opt_malloc(len);
unhide_metas(keyhex);
/* 4034: "Whitespace is allowed within digits" */
for (cp = keyhex; *cp; )
if (isspace((unsigned char)*cp))
for (cp1 = cp; *cp1; cp1++)
*cp1 = *(cp1+1);
else
cp++;
if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
if (comma)
{
free(new->name);
ret_err_free(_("bad HEX in trust anchor"), new);
if (!algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
!atoi_check16(comma, &new->keytag) ||
!atoi_check8(algo, &new->algo) ||
!atoi_check8(digest, &new->digest_type))
{
free(new->name);
ret_err_free(_("bad trust anchor"), new);
}
/* Upper bound on length */
len = (2*strlen(keyhex))+1;
new->digest = opt_malloc(len);
unhide_metas(keyhex);
/* 4034: "Whitespace is allowed within digits" */
for (cp = keyhex; *cp; )
if (isspace((unsigned char)*cp))
for (cp1 = cp; *cp1; cp1++)
*cp1 = *(cp1+1);
else
cp++;
if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
{
free(new->name);
ret_err_free(_("bad HEX in trust anchor"), new);
}
}
new->next = daemon->ds;
@@ -5926,6 +5956,9 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon->randport_limit = 1;
daemon->host_index = SRC_AH;
daemon->max_procs = MAX_PROCS;
#ifdef HAVE_DUMPFILE
daemon->dump_mask = 0xffffffff;
#endif
#ifdef HAVE_DNSSEC
daemon->limit[LIMIT_SIG_FAIL] = DNSSEC_LIMIT_SIG_FAIL;
daemon->limit[LIMIT_CRYPTO] = DNSSEC_LIMIT_CRYPTO;

View File

@@ -98,7 +98,7 @@ void poll_listen(int fd, short event)
{
if (arrsize == nfds)
{
/* Array too small, extend. */
/* Array too small. Extend. */
struct pollfd *new;
arrsize = (arrsize == 0) ? 64 : arrsize * 2;

View File

@@ -411,7 +411,7 @@ static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_ad
if (!old_prefix && !parm.found_context)
return;
/* If we're sending router address instead of prefix in at least on prefix,
/* If we're sending router address instead of prefix in at least one prefix,
include the advertisement interval option. */
if (parm.adv_router)
{
@@ -825,10 +825,10 @@ time_t periodic_ra(time_t now)
}
else if (iface_enumerate(AF_INET6, &param, (callback_t){.af_inet6=iface_search}))
/* There's a context overdue, but we can't find an interface
associated with it, because it's for a subnet we dont
associated with it, because it's for a subnet we don't
have an interface on. Probably we're doing DHCP on
a remote subnet via a relay. Zero the timer, since we won't
ever be able to send ra's and satisfy it. */
ever be able to send RAs to satisfy it. */
context->ra_time = 0;
if (param.iface != 0 &&

View File

@@ -29,16 +29,19 @@
return = 1 -> extract OK, compare OK, flip OK
return = 2 -> extract OK, compare failed.
return = 3 -> extract OK, compare failed but only on case.
If pp == NULL, operate on the query name in the packet.
*/
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int func, unsigned int parm)
{
unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
unsigned char *cp = (unsigned char *)name, *p1 = NULL;
unsigned int j, l, namelen = 0, hops = 0;
unsigned int bigmap_counter = 0, bigmap_posn = 0, bigmap_size = parm, bitmap = 0;
int retvalue = 1, case_insens = 1, isExtract = 0, flip = 0, extrabytes = (int)parm;
unsigned int *bigmap = (unsigned int *)name;
unsigned char *p = pp ? *pp : (unsigned char *)(header+1);
if (func == EXTR_NAME_EXTRACT)
isExtract = 1, *cp = 0;
else if (func == EXTR_NAME_NOCASE)
@@ -71,11 +74,14 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
}
else if (!flip && *cp != 0)
retvalue = 2;
if (p1) /* we jumped via compression */
*pp = p1;
else
*pp = p;
if (pp)
{
if (p1) /* we jumped via compression */
*pp = p1;
else
*pp = p;
}
return retvalue;
}
@@ -418,7 +424,7 @@ int private_net(struct in_addr addr, int ban_localhost)
((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ;
}
static int private_net6(struct in6_addr *a, int ban_localhost)
int private_net6(struct in6_addr *a, int ban_localhost)
{
/* Block IPv4-mapped IPv6 addresses in private IPv4 address space */
if (IN6_IS_ADDR_V4MAPPED(a))
@@ -528,9 +534,6 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
if (substring)
*substring = name_len;
if (ttlp)
*ttlp = daemon->neg_ttl;
for (i = 0; i < ntohs(header->nscount); i++)
{
if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
@@ -590,18 +593,18 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
}
/* rest of RR */
if (!no_cache && !blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p, 20))
{
blockdata_free(addr.rrblock.rrdata);
return 0;
}
addr.rrblock.datalen += 20;
if (!no_cache)
{
int secflag = 0;
if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p, 20))
{
blockdata_free(addr.rrblock.rrdata);
return 0;
}
addr.rrblock.datalen += 20;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[i + ntohs(header->ancount)] != 0)
{
@@ -684,12 +687,12 @@ static int log_txt(char *name, unsigned char *p, const int ardlen, int flag)
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,
struct ipsets *ipsets, struct ipsets *nftsets, int check_rebind,
int no_cache_dnssec, int secure)
{
unsigned char *p, *p1, *endrr, *namep;
int j, qtype, qclass, aqtype, aqclass, ardlen, res;
unsigned long ttl = 0;
unsigned long ttl;
union all_addr addr;
#ifdef HAVE_IPSET
char **ipsets_cur;
@@ -701,13 +704,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
#else
(void)nftsets; /* unused */
#endif
int found = 0, cname_count = CNAME_CHAIN;
struct crec *cpp = NULL;
int name_encoding, found = 0, ptr = 0;
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
#ifdef HAVE_DNSSEC
int cname_short = 0;
#endif
unsigned long cttl = ULONG_MAX, attl;
cache_start_insert();
@@ -721,114 +719,73 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (qclass != C_IN)
return 0;
/* PTRs: we chase CNAMEs here, since we have no way to
represent them in the cache. */
if (qtype == T_PTR)
/* If the PTR record encodes an address, store using a name/address record with F_REVERSE set.
Otherwise, it gets stored as an arbitrary RR below. If the query is answerable with
a CNAME, also take the arbitrary-RR route, since the cache can't represent a CNAME
whose target is stored in a F_REVERSE record. */
if (qtype == T_PTR && !(flags & F_NXDOMAIN) && (name_encoding = in_arpa_name_2_addr(name, &addr)))
{
int insert = 1, name_encoding = in_arpa_name_2_addr(name, &addr);
ptr = 1;
if (!(flags & F_NXDOMAIN))
{
cname_loop:
if (!(p1 = skip_questions(header, qlen)))
return 2;
for (j = 0; j < ntohs(header->ancount); j++)
{
int secflag = 0;
if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10)))
return 2; /* bad packet */
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{
(p1) -= 4;
PUTLONG(daemon->max_ttl, p1);
}
GETSHORT(ardlen, p1);
endrr = p1+ardlen;
/* TTL of record is minimum of CNAMES and PTR */
if (attl < cttl)
cttl = attl;
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
{
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && j < daemon->rr_status_sz && daemon->rr_status[j] != 0)
{
/* validated RR anywhere in CNAME chain, don't cache. */
if (cname_short || aqtype == T_CNAME)
insert = 0;
secflag = F_DNSSECOK;
/* limit TTL based on signature. */
if (daemon->rr_status[j] < cttl)
cttl = daemon->rr_status[j];
}
#endif
if (aqtype == T_CNAME)
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
return 2;
if (aqtype == T_CNAME)
{
if (!cname_count--)
return 0; /* looped CNAMES, we can't cache. */
#ifdef HAVE_DNSSEC
cname_short = 1;
#endif
goto cname_loop;
}
found = 1;
if (!name_encoding)
log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, NULL, aqtype);
else
{
log_query(name_encoding | secflag | F_REVERSE | F_UPSTREAM, name, &addr, NULL, 0);
if (insert)
cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE);
}
}
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 2; /* bad packet */
}
}
if (!(p1 = skip_questions(header, qlen)))
return 2;
if (!found && !option_bool(OPT_NO_NEG))
for (j = 0; j < ntohs(header->ancount); j++)
{
/* For reverse records, we use the name field to store the SOA name. */
int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
int secflag = 0;
if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10)))
return 2; /* bad packet */
flags |= F_NEG | (secure ? F_DNSSECOK : 0);
if (name_encoding && ttl)
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
p = p1;
GETLONG(ttl, p1);
GETSHORT(ardlen, p1);
endrr = p1+ardlen;
if (aqclass == C_IN && res == 1 && aqtype == T_PTR)
{
flags |= F_REVERSE | name_encoding;
if (!have_soa)
flags |= F_NO_RR; /* Marks no SOA found. */
cache_insert(name + substring, &addr, C_IN, now, ttl, flags);
found = 1;
if ((daemon->max_ttl != 0) && (ttl > daemon->max_ttl))
ttl = daemon->max_ttl;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && j < daemon->rr_status_sz && daemon->rr_status[j] != 0)
{
secflag = F_DNSSECOK;
/* limit TTL based on signature. */
if (daemon->rr_status[j] < ttl)
ttl = daemon->rr_status[j];
}
#endif
PUTLONG(ttl, p);
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
return 2;
log_query(name_encoding | secflag | F_REVERSE | F_UPSTREAM, name, &addr, NULL, 0);
cache_insert(name, &addr, C_IN, now, ttl, name_encoding | secflag | F_REVERSE);
/* restore query into name */
p1 = namep;
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
return 2;
}
log_query(flags | F_UPSTREAM, name, &addr, NULL, 0);
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 2; /* bad packet */
}
}
else
if (!ptr || !found)
{
/* everything other than PTR */
struct crec *newc;
int addrlen = 0, insert = 1;
struct crec *newc, *cpp = NULL;
int cname_count = CNAME_CHAIN, addrlen = 0, insert = 1;
if (qtype == T_A)
{
addrlen = INADDRSZ;
@@ -840,12 +797,12 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
flags |= F_IPV6;
}
else if (qtype != T_CNAME &&
(qtype == T_SRV || rr_on_list(daemon->cache_rr, qtype) || rr_on_list(daemon->cache_rr, T_ANY)))
(qtype == T_SRV || qtype == T_PTR || rr_on_list(daemon->cache_rr, qtype) || rr_on_list(daemon->cache_rr, T_ANY)))
flags |= F_RR;
else
insert = 0; /* NOTE: do not cache data from CNAME queries. */
cname_loop1:
cname_loop:
if (!(p1 = skip_questions(header, qlen)))
return 2;
@@ -858,12 +815,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{
(p1) -= 4;
PUTLONG(daemon->max_ttl, p1);
}
p = p1;
GETLONG(ttl, p1);
GETSHORT(ardlen, p1);
endrr = p1+ardlen;
@@ -876,6 +829,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
p1 = endrr;
continue;
}
if ((daemon->max_ttl != 0) && (ttl > daemon->max_ttl))
ttl = daemon->max_ttl;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && j < daemon->rr_status_sz && daemon->rr_status[j] != 0)
@@ -883,10 +839,12 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
secflag = F_DNSSECOK;
/* limit TTl based on sig. */
if (daemon->rr_status[j] < attl)
attl = daemon->rr_status[j];
if (daemon->rr_status[j] < ttl)
ttl = daemon->rr_status[j];
}
#endif
PUTLONG(ttl, p);
if (aqtype == T_CNAME)
{
@@ -897,7 +855,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (insert)
{
if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag)))
if ((newc = cache_insert(name, NULL, C_IN, now, ttl, F_CNAME | F_FORWARD | secflag)))
{
newc->addr.cname.target.cache = NULL;
newc->addr.cname.is_name_ptr = 0;
@@ -910,16 +868,15 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
}
cpp = newc;
if (attl < cttl)
cttl = attl;
}
/* Set the query to the CNAME target and go again unless the query was just for a CNAME. */
namep = p1;
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
return 2;
if (qtype != T_CNAME)
goto cname_loop1;
goto cname_loop;
found = 1;
}
@@ -1043,24 +1000,39 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND)))
return 1;
}
if (flags & (F_IPV4 | F_IPV6))
{
/* If we're a child process, send this to the parent,
since the ipset and nfset access is not re-entrant. */
#ifdef HAVE_IPSET
if (ipsets && (flags & (F_IPV4 | F_IPV6)))
for (ipsets_cur = ipsets->sets; *ipsets_cur; ipsets_cur++)
if (add_to_ipset(*ipsets_cur, &addr, flags, 0) == 0)
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, ipsets->domain, &addr, *ipsets_cur, 1);
if (ipsets)
{
if (daemon->pipe_to_parent != -1)
cache_send_ipset(PIPE_OP_IPSET, ipsets, flags, &addr);
else
for (ipsets_cur = ipsets->sets; *ipsets_cur; ipsets_cur++)
if (add_to_ipset(*ipsets_cur, &addr, flags, 0) == 0)
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, ipsets->domain, &addr, *ipsets_cur, 1);
}
#endif
#ifdef HAVE_NFTSET
if (nftsets && (flags & (F_IPV4 | F_IPV6)))
for (nftsets_cur = nftsets->sets; *nftsets_cur; nftsets_cur++)
if (add_to_nftset(*nftsets_cur, &addr, flags, 0) == 0)
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, nftsets->domain, &addr, *nftsets_cur, 0);
if (nftsets)
{
if (daemon->pipe_to_parent != -1)
cache_send_ipset(PIPE_OP_NFTSET, nftsets, flags, &addr);
else
for (nftsets_cur = nftsets->sets; *nftsets_cur; nftsets_cur++)
if (add_to_nftset(*nftsets_cur, &addr, flags, 0) == 0)
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, nftsets->domain, &addr, *nftsets_cur, 0);
}
#endif
}
}
if (insert)
{
newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag);
newc = cache_insert(name, &addr, C_IN, now, ttl, flags | F_FORWARD | secflag);
if (newc && cpp)
{
next_uid(newc);
@@ -1108,26 +1080,27 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{
int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
/* If there's no SOA to get the TTL from, but there is a CNAME
pointing at this, inherit its TTL */
if (ttl || cpp)
{
if (!ttl)
ttl = cttl;
addr.rrdata.datalen = substring;
addr.rrdata.rrtype = qtype;
if (!have_soa)
flags |= F_NO_RR; /* Marks no SOA found. */
}
newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
if (newc && cpp)
{
next_uid(newc);
cpp->addr.cname.target.cache = newc;
cpp->addr.cname.uid = newc->uid;
if (have_soa || daemon->neg_ttl)
{
if (have_soa)
{
addr.rrdata.datalen = substring;
addr.rrdata.rrtype = qtype;
}
else
{
/* If daemon->neg_ttl is set, we can cache even without an SOA. */
ttl = daemon->neg_ttl;
flags |= F_NO_RR; /* Marks no SOA found. */
}
newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
if (newc && cpp)
{
next_uid(newc);
cpp->addr.cname.target.cache = newc;
cpp->addr.cname.uid = newc->uid;
}
}
}
}
@@ -1232,7 +1205,8 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark)
/* If the packet holds exactly one query
return F_IPV4 or F_IPV6 and leave the name from the query in name */
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
unsigned short *typep, unsigned short *classp)
{
unsigned char *p = (unsigned char *)(header+1);
int qtype, qclass;
@@ -1257,6 +1231,9 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
if (typep)
*typep = qtype;
if (classp)
*classp = qclass;
if (qclass == C_IN)
{
if (qtype == T_A)
@@ -1268,9 +1245,7 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
}
#ifdef HAVE_DNSSEC
/* F_DNSSECOK as agument to search_servers() inhibits forwarding
to servers for domains without a trust anchor. This make the
behaviour for DS and DNSKEY queries we forward the same
/* Make the behaviour for DS and DNSKEY queries we forward the same
as for DS and DNSKEY queries we originate. */
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DS || qtype == T_DNSKEY))
return F_DNSSECOK;
@@ -1636,7 +1611,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
int rd_bit = (header->hb3 & HB3_RD);
int count = 255; /* catch loops */
/* Suppress cached answers of no_cache set. */
/* Suppress cached answers if no_cache set. */
if (no_cache)
rd_bit = 0;
@@ -1646,14 +1621,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (filtered)
*filtered = 0;
/* never answer queries with RD unset, to avoid cache snooping. */
if ( ntohs(header->qdcount) != 1 ||
ntohs(header->ancount) != 0 ||
if (ntohs(header->qdcount) != 1 ||
ntohs(header->ancount) != 0 ||
ntohs(header->nscount) != 0 ||
ntohs(header->qdcount) == 0 ||
OPCODE(header) != QUERY )
return 0;
/* Don't return AD set if checking disabled. */
if (header->hb4 & HB4_CD)
sec_data = 0;
@@ -2316,14 +2289,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
For FORWARD NEG records, the addr.rrdata.datalen field of the othewise
empty addr is used to held an offset in to the name which yields the SOA
name. For REVERSE NEG records, the otherwise empty name field holds the
SOA name. If soa_name has zero length, then no SOA is known. soa_lookup
MUST be a neg record here.
name.
If the F_NO_RR flag is set, there was no SOA record supplied with the RR. */
if (soa_lookup && !(soa_lookup->flags & F_NO_RR))
{
char *soa_name = soa_lookup->flags & F_REVERSE ? cache_get_name(soa_lookup) : name + soa_lookup->addr.rrdata.datalen;
char *soa_name = name + soa_lookup->addr.rrdata.datalen;
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, soa_name, now, F_RR)))

View File

@@ -56,7 +56,8 @@ static void do_options(struct dhcp_context *context,
time_t now,
unsigned int lease_time,
unsigned short fuzz,
const char *pxevendor);
const char *pxevendor,
int leasequery);
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
@@ -73,7 +74,7 @@ static void handle_encap(struct dhcp_packet *mess, unsigned char *end, unsigned
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
size_t sz, time_t now, int unicast_dest, int loopback,
int *is_inform, int pxe, struct in_addr fallback, time_t recvtime)
int *is_inform, int pxe, struct in_addr fallback, time_t recvtime, struct in_addr leasequery_source)
{
unsigned char *opt, *clid = NULL;
struct dhcp_lease *ltmp, *lease = NULL;
@@ -108,7 +109,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
#endif
subnet_addr.s_addr = override.s_addr = 0;
/* set tag with name == interface */
iface_id.net = iface_name;
iface_id.next = NULL;
@@ -185,7 +186,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
}
if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
if (mess_type != DHCPLEASEQUERY && (opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
{
/* Any agent-id needs to be copied back out, verbatim, as the last option
in the packet. Here, we shift it to the very end of the buffer, if it doesn't
@@ -211,7 +212,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ)))
override = option_addr(sopt);
/* if a circuit-id or remote-is option is provided, exact-match to options. */
/* if a circuit-id or remote-is option is provided, exact-match to options. */
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
{
int search;
@@ -224,7 +225,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
search = SUBOPT_SUBSCR_ID;
else
continue;
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) &&
vendor->len == option_len(sopt) &&
memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
@@ -234,7 +235,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
}
}
/* Check for RFC3011 subnet selector - only if RFC3527 one not present */
if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
subnet_addr = option_addr(opt);
@@ -373,44 +374,47 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
context = context_new;
}
if (!context)
if (mess_type != DHCPLEASEQUERY)
{
const char *via;
if (subnet_addr.s_addr)
if (!context)
{
via = _("with subnet selector");
inet_ntop(AF_INET, &subnet_addr, daemon->addrbuff, ADDRSTRLEN);
}
else
{
via = _("via");
if (mess->giaddr.s_addr)
inet_ntop(AF_INET, &mess->giaddr, daemon->addrbuff, ADDRSTRLEN);
else
safe_strncpy(daemon->addrbuff, iface_name, ADDRSTRLEN);
}
my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
via, daemon->addrbuff);
return 0;
}
if (option_bool(OPT_LOG_OPTS))
{
struct dhcp_context *context_tmp;
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
{
inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAME);
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
const char *via;
if (subnet_addr.s_addr)
{
inet_ntop(AF_INET, &context_tmp->netmask, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
via = _("with subnet selector");
inet_ntop(AF_INET, &subnet_addr, daemon->addrbuff, ADDRSTRLEN);
}
else
{
inet_ntop(AF_INET, &context_tmp->end, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
via = _("via");
if (mess->giaddr.s_addr)
inet_ntop(AF_INET, &mess->giaddr, daemon->addrbuff, ADDRSTRLEN);
else
safe_strncpy(daemon->addrbuff, iface_name, ADDRSTRLEN);
}
my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
via, daemon->addrbuff);
return 0;
}
if (option_bool(OPT_LOG_OPTS))
{
struct dhcp_context *context_tmp;
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
{
inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAME);
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
{
inet_ntop(AF_INET, &context_tmp->netmask, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
}
else
{
inet_ntop(AF_INET, &context_tmp->end, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
}
}
}
}
@@ -672,7 +676,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
clear_packet(mess, end);
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL);
netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL, 0);
}
}
@@ -818,8 +822,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
}
}
if (config)
if (mess_type != DHCPLEASEQUERY && config)
{
struct dhcp_netid_list *list;
@@ -860,7 +864,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
clid = NULL;
/* Check if client is PXE client. */
if (daemon->enable_pxe &&
if (mess_type != DHCPLEASEQUERY &&
daemon->enable_pxe &&
is_pxe_client(mess, sz, &pxevendor))
{
if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
@@ -1035,7 +1040,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
/* if we're just a proxy server, go no further */
if ((context->flags & CONTEXT_PROXY) || pxe)
if (mess_type != DHCPLEASEQUERY &&
((context->flags & CONTEXT_PROXY) || pxe))
return 0;
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
@@ -1047,6 +1053,176 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
switch (mess_type)
{
case DHCPLEASEQUERY:
mess_type = DHCPLEASEUNKNOWN;
if (!option_bool(OPT_LEASEQUERY))
return 0;
if (leasequery_source.s_addr == 0)
return 0;
inet_ntop(AF_INET, &leasequery_source, daemon->workspacename, ADDRSTRLEN);
if (daemon->leasequery_addr)
{
struct bogus_addr *baddrp;
for (baddrp = daemon->leasequery_addr; baddrp; baddrp = baddrp->next)
if (!baddrp->is6 && is_same_net_prefix(leasequery_source, baddrp->addr.addr4, baddrp->prefix))
break;
if (!baddrp)
{
my_syslog(MS_DHCP | LOG_WARNING, _("leasequery from %s not permitted"), daemon->workspacename);
return 0;
}
}
daemon->metrics[METRIC_DHCPLEASEQUERY]++;
log_packet("DHCPLEASEQUERY", mess->ciaddr.s_addr ? &mess->ciaddr : NULL, emac_len != 0 ? emac : NULL, emac_len,
iface_name, "from ", daemon->workspacename, mess->xid);
/* Put all the contexts on the ->current list for the next stages. */
for (context = daemon->dhcp; context; context = context->next)
context->current = context->next;
/* Have maybe already found the lease by MAC or clid. */
if (mess->ciaddr.s_addr != 0 &&
!(lease = lease_find_by_addr(mess->ciaddr)) &&
address_available(daemon->dhcp, mess->ciaddr, tagif_netid))
{
mess_type = DHCPLEASEUNASSIGNED;
daemon->metrics[METRIC_DHCPLEASEUNASSIGNED]++;
}
if (lease)
{
/* RFC4388 para 6.4.2 */
if (lease->agent_id)
{
unsigned char *sopt ;
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
{
int search;
if (vendor->match_type == MATCH_CIRCUIT)
search = SUBOPT_CIRCUIT_ID;
else if (vendor->match_type == MATCH_REMOTE)
search = SUBOPT_REMOTE_ID;
else if (vendor->match_type == MATCH_SUBSCRIBER)
search = SUBOPT_SUBSCR_ID;
else
continue;
if ((sopt = option_find1(lease->agent_id, lease->agent_id + lease->agent_id_len, search, 1)) &&
vendor->len == option_len(sopt) &&
memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
{
vendor->netid.next = netid;
netid = &vendor->netid;
}
}
tagif_netid = run_tag_if(netid);
}
/* Now find the context for this lease and config for this host. */
if ((context = narrow_context(daemon->dhcp, lease->addr, tagif_netid)))
{
if ((config = find_config(daemon->dhcp_conf, context, lease->clid, lease->clid_len,
lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, lease->hostname, tagif_netid)))
{
struct dhcp_netid_list *list;
for (list = config->netid; list; list = list->next)
{
list->list->next = netid;
netid = list->list;
}
tagif_netid = run_tag_if(netid);
}
if (context->netid.net)
{
context->netid.next = netid;
tagif_netid = run_tag_if(&context->netid);
}
log_tags(tagif_netid, ntohl(mess->xid));
emac = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len, lease->hwaddr, lease->clid_len, lease->clid, &emac_len);
mess_type = DHCPLEASEACTIVE;
daemon->metrics[METRIC_DHCPLEASEACTIVE]++;
}
}
log_packet(mess_type == DHCPLEASEACTIVE ? "DHCPLEASEACTIVE" : (mess_type == DHCPLEASEUNASSIGNED ? "DHCPLEASEUNASSIGNED" : "DHCPLEASEUNKNOWN"),
mess_type == DHCPLEASEACTIVE ? &lease->addr : (mess->ciaddr.s_addr != 0 ? &mess->ciaddr : NULL),
emac_len != 0 ? emac : NULL, emac_len,
iface_name, mess_type == DHCPLEASEACTIVE ? lease->hostname : NULL, NULL, mess->xid);
clear_packet(mess, end);
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, mess_type);
if (mess_type == DHCPLEASEUNKNOWN)
{
daemon->metrics[METRIC_DHCPLEASEUNKNOWN]++;
mess->ciaddr.s_addr = 0;
}
if (mess_type == DHCPLEASEACTIVE)
{
unsigned char *p;
mess->ciaddr = lease->addr;
mess->hlen = lease->hwaddr_len;
mess->htype = lease->hwaddr_type;
memcpy(mess->chaddr, lease->hwaddr, lease->hwaddr_len);
if (lease->clid && in_list(req_options, OPTION_CLIENT_ID) &&
(p = free_space(mess, end, OPTION_CLIENT_ID, lease->clid_len)))
memcpy(p, lease->clid, lease->clid_len);
if (in_list(req_options, OPTION_LEASE_TIME))
{
if (lease->expires == 0) /* infinite lease */
option_put(mess, end, OPTION_LEASE_TIME, 4, 0xffffffff);
else
option_put(mess, end, OPTION_LEASE_TIME, 4, (unsigned int)(lease->expires - now));
}
if (lease->expires != 0)
{
time = calc_time(context, config, NULL);
if (in_list(req_options, OPTION_T1) && (lease->expires - now) > time/2)
option_put(mess, end, OPTION_T1, 4, ((unsigned int)(lease->expires - now)) - time/2);
if (in_list(req_options, OPTION_T2) && (lease->expires - now) > time/8)
option_put(mess, end, OPTION_T2, 4, ((unsigned int)(lease->expires - now)) - time/8);
if (in_list(req_options, OPTION_LAST_TRANSACTION) && (lease->expires - now) < time)
option_put(mess, end, OPTION_LAST_TRANSACTION, 4, time - ((unsigned int)(lease->expires - now)));
}
if (lease->vendorclass)
{
memcpy(daemon->dhcp_buff3, lease->vendorclass, lease->vendorclass_len);
vendor_class_len = lease->vendorclass_len;
}
subnet_addr.s_addr = 0;
do_options(context, mess, end, req_options, lease->hostname, get_domain(lease->addr), netid, subnet_addr,
0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL, 1);
/* Does this have to be last for leasequery replies also? RFC 4388 is silent on the subject. */
if (lease->agent_id && in_list(req_options, OPTION_AGENT_ID) &&
(p = free_space(mess, end, OPTION_AGENT_ID, lease->agent_id_len)))
memcpy(p, lease->agent_id, lease->agent_id_len);
}
return dhcp_packet_size(mess, NULL, real_end);
case DHCPDECLINE:
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
@@ -1197,7 +1373,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_LEASE_TIME, 4, time);
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor);
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor, 0);
return dhcp_packet_size(mess, agent_id, real_end);
@@ -1345,7 +1521,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
{
/* If a host is configured with more than one MAC address, it's OK to 'nix
a lease from one of it's MACs to give the address to another. */
a lease from one of its MACs to give the address to another. */
if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
{
inet_ntop(AF_INET, &ltmp->addr, daemon->addrbuff, ADDRSTRLEN);
@@ -1528,7 +1704,21 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
lease_set_expires(lease, time, now);
lease_set_interface(lease, int_index, now);
if (option_bool(OPT_LEASEQUERY))
{
if (agent_id)
lease_set_agent_id(lease, option_ptr(agent_id, 0), option_len(agent_id));
if (vendor_class_len != 0)
lease_set_vendorclass(lease, (unsigned char *)daemon->dhcp_buff3, vendor_class_len);
}
else
{
/* if leasequery no longer enabled, remove stuff that may have been stored when it was. */
lease_set_agent_id(lease, NULL, 0);
lease_set_vendorclass(lease, NULL, 0);
}
if (override.s_addr != 0)
lease->override = override;
else
@@ -1544,7 +1734,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (rapid_commit)
option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0);
do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor);
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor, 0);
}
return dhcp_packet_size(mess, agent_id, real_end);
@@ -1611,7 +1801,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0, pxevendor);
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0, pxevendor, 0);
*is_inform = 1; /* handle reply differently */
return dhcp_packet_size(mess, agent_id, real_end);
@@ -1720,29 +1910,33 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
return;
daemon->addrbuff[0] = 0;
daemon->addrbuff[0] = daemon->namebuff[0] = 0;
if (addr)
inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN);
print_mac(daemon->namebuff, ext_mac, mac_len);
if (ext_mac)
print_mac(daemon->namebuff, ext_mac, mac_len);
if (option_bool(OPT_LOG_OPTS))
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s%s%s%s",
ntohl(xid),
type,
interface,
daemon->addrbuff,
addr ? " " : "",
daemon->namebuff,
ext_mac ? " " : "",
string ? string : "",
err ? err : "");
else
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s%s%s%s",
type,
interface,
daemon->addrbuff,
addr ? " " : "",
daemon->namebuff,
ext_mac ? " " : "",
string ? string : "",
err ? err : "");
@@ -2412,7 +2606,8 @@ static void do_options(struct dhcp_context *context,
time_t now,
unsigned int lease_time,
unsigned short fuzz,
const char *pxevendor)
const char *pxevendor,
int leasequery)
{
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
struct dhcp_boot *boot;
@@ -2450,70 +2645,73 @@ static void do_options(struct dhcp_context *context,
}
}
for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
if ((!id_list->list) || match_netid(id_list->list, netid, 0))
break;
if (id_list)
mess->flags |= htons(0x8000); /* force broadcast */
if (context)
mess->siaddr = context->local;
/* See if we can send the boot stuff as options.
To do this we need a requested option list, BOOTP
and very old DHCP clients won't have this, we also
provide a manual option to disable it.
Some PXE ROMs have bugs (surprise!) and need zero-terminated
names, so we always send those. */
if ((boot = find_boot(tagif)))
if (!leasequery)
{
if (boot->sname)
{
if (!option_bool(OPT_NO_OVERRIDE) &&
req_options &&
in_list(req_options, OPTION_SNAME))
option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
else
safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname));
}
for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
if ((!id_list->list) || match_netid(id_list->list, netid, 0))
break;
if (id_list)
mess->flags |= htons(0x8000); /* force broadcast */
if (boot->file)
if (context)
mess->siaddr = context->local;
/* See if we can send the boot stuff as options.
To do this we need a requested option list, BOOTP
and very old DHCP clients won't have this, we also
provide a manual option to disable it.
Some PXE ROMs have bugs (surprise!) and need zero-terminated
names, so we always send those. */
if ((boot = find_boot(tagif)))
{
if (!option_bool(OPT_NO_OVERRIDE) &&
req_options &&
in_list(req_options, OPTION_FILENAME))
option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
else
safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
if (boot->sname)
{
if (!option_bool(OPT_NO_OVERRIDE) &&
req_options &&
in_list(req_options, OPTION_SNAME))
option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
else
safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname));
}
if (boot->file)
{
if (!option_bool(OPT_NO_OVERRIDE) &&
req_options &&
in_list(req_options, OPTION_FILENAME))
option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
else
safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
}
if (boot->next_server.s_addr)
mess->siaddr = boot->next_server;
else if (boot->tftp_sname)
mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
}
if (boot->next_server.s_addr)
mess->siaddr = boot->next_server;
else if (boot->tftp_sname)
mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
}
else
/* Use the values of the relevant options if no dhcp-boot given and
they're not explicitly asked for as options. OPTION_END is used
as an internal way to specify siaddr without using dhcp-boot, for use in
dhcp-optsfile. */
{
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
(opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
else
/* Use the values of the relevant options if no dhcp-boot given and
they're not explicitly asked for as options. OPTION_END is used
as an internal way to specify siaddr without using dhcp-boot, for use in
dhcp-optsfile. */
{
safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file));
done_file = 1;
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
(opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
{
safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file));
done_file = 1;
}
if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
(opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
{
safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname));
done_server = 1;
}
if ((opt = option_find2(OPTION_END)))
mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
}
if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
(opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
{
safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname));
done_server = 1;
}
if ((opt = option_find2(OPTION_END)))
mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
}
/* We don't want to do option-overload for BOOTP, so make the file and sname
@@ -2534,7 +2732,7 @@ static void do_options(struct dhcp_context *context,
end -= 3;
/* rfc3011 says this doesn't need to be in the requested options list. */
if (subnet_addr.s_addr)
if (!leasequery && subnet_addr.s_addr)
option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
if (lease_time != 0xffffffff)
@@ -2575,14 +2773,17 @@ static void do_options(struct dhcp_context *context,
/* replies to DHCPINFORM may not have a valid context */
if (context)
{
if (!option_find2(OPTION_NETMASK))
/* Netmask and broadcast always sent, except leasequery. */
if (!option_find2(OPTION_NETMASK) &&
(!leasequery || in_list(req_options, OPTION_NETMASK)))
option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
/* May not have a "guessed" broadcast address if we got no packets via a relay
from this net yet (ie just unicast renewals after a restart */
if (context->broadcast.s_addr &&
!option_find2(OPTION_BROADCAST))
option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
!option_find2(OPTION_BROADCAST) &&
(!leasequery || in_list(req_options, OPTION_BROADCAST)))
option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
/* Same comments as broadcast apply, and also may not be able to get a sensible
default when using subnet select. User must configure by steam in that case. */
@@ -2663,7 +2864,7 @@ static void do_options(struct dhcp_context *context,
continue;
/* was it asked for, or are we sending it anyway? */
if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
if ((!(opt->flags & DHOPT_FORCE) || leasequery) && !in_list(req_options, optno))
continue;
/* prohibit some used-internally options. T1 and T2 already handled. */
@@ -2721,7 +2922,7 @@ static void do_options(struct dhcp_context *context,
handle_encap(mess, end, req_options, null_term, tagif, pxe_arch != 1);
force_encap = prune_vendor_opts(tagif);
if (context && pxe_arch != -1)
{
pxe_misc(mess, end, uuid, pxevendor);
@@ -2729,8 +2930,8 @@ static void do_options(struct dhcp_context *context,
config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
}
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT) || in_list(req_options, OPTION_VENDOR_ID)) &&
(leasequery || do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term)) &&
pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
(p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
/* If we send vendor encapsulated options, and haven't already sent option 60,

View File

@@ -377,7 +377,7 @@ int expand_workspace(unsigned char ***wkspc, int *szp, int new)
int to_wire(char *name)
{
unsigned char *l, *p, *q, term;
int len;
unsigned int len;
for (l = (unsigned char*)name; *l != 0; l = p)
{
@@ -409,7 +409,7 @@ int to_wire(char *name)
void from_wire(char *name)
{
unsigned char *l, *p, *last;
int len;
unsigned int len;
for (last = (unsigned char *)name; *last != 0; last += *last+1);

View File

@@ -61,7 +61,7 @@ void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
else if (lease->clid_len == 9 &&
lease->clid[0] == ARPHRD_EUI64 &&
lease->hwaddr_type == ARPHRD_IEEE1394)
/* firewire has EUI-64 identifier as clid */
/* FireWire has EUI-64 identifier as clid */
memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
#endif
else

View File

@@ -274,7 +274,7 @@ void tftp_request(struct listener *listen, time_t now)
}
/* Enforce simultaneous transfer limit. In non-single-port mode
this is doene by not listening on the server socket when
this is done by not listening on the server socket when
too many transfers are in progress. */
if (!transfer && tftp_cnt >= daemon->tftp_max)
return;
@@ -362,12 +362,16 @@ void tftp_request(struct listener *listen, time_t now)
p = packet + 2;
end = packet + len;
if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
!(filename = next(&p, end)) ||
if (!(filename = next(&p, end)) ||
!(mode = next(&p, end)) ||
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0) ||
ntohs(*((unsigned short *)packet)) != OP_RRQ)
{
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff, NULL);
if (!filename)
len = tftp_err(ERR_ILL, packet, _("empty filename in request from %s"), daemon->addrbuff, NULL);
else
len = tftp_err(ERR_ILL, packet, _("unsupported %srequest from %s"),
(ntohs(*((unsigned short *)packet)) == OP_WRQ) ? _("write ") : "", daemon->addrbuff);
is_err = 1;
}
else
@@ -755,14 +759,22 @@ static char *next(char **p, char *end)
return ret;
}
/* If we don't do anything, don't write the the input/ouptut
buffer. This allows us to pass in safe read-only strings constants. */
static void sanitise(char *buf)
{
unsigned char *q, *r;
for (q = r = (unsigned char *)buf; *r; r++)
if (isprint((int)*r))
*(q++) = *r;
*q = 0;
{
if (q != r)
*q = *r;
q++;
}
if (q != r)
*q = 0;
}
#define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */

View File

@@ -426,7 +426,7 @@ int hostname_order(const char *a, const char *b)
int hostname_isequal(const char *a, const char *b)
{
return hostname_order(a, b) == 0;
return strlen(a) == strlen(b) && hostname_order(a, b) == 0;
}
/* is b equal to or a subdomain of a return 2 for equal, 1 for subdomain */