Compare commits

..
141 Commits
Author SHA1 Message Date
Dominik Derigs 72fac0810c dnsmasq.h has to be included first as it sources config.h
Signed-off-by: DL6ER <dl6er@dl6er.de>
2021-10-07 09:28:34 +01:00
Simon Kelley c166c07a93 Support IDN in --auth-zone. 2021-10-06 23:48:06 +01:00
Petr Menšík 39a625ff72 Disable transitional IDN rules, accept only sane names
Transitional encoding accepts every emoticon you can think about.
Because setlocale were not enabled before, IDN 2003 input was not
accepted by dnsmasq. It makes no sense therefore to maintain backward
compatibility. Accept only proper encoded unicode names and reject
random unicode characters.

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

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

Thanks to Brad Jorsch for finding and analysing this bug.

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

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

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

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

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

Thanks to Johannes Stezenbach for finding the problem.
2021-09-27 22:37:02 +01:00
Simon Kelley 47aefca5e4 Add --nftset option, like --ipset but for the newer nftables.
Thanks to Chen Zhenge for the original patch, which I've
reworked. Any bugs down to SRK.
2021-09-27 21:49:28 +01:00
Simon Kelley 981fb03710 Make --rebind-domain-ok work with IDN. 2021-09-24 15:25:05 +01:00
Paul Fertser ef2f8d70d2 manpage: clarify tags: semantics for --dhcp-host
Mention that several tags can be specified and instruct the user that
some other match must still be provided for the directive to have any
effect.
2021-09-24 14:46:25 +01:00
Dominik Derigs d9995a1add Improve last patch by splitting the previously combined if
Signed-off-by: DL6ER <dl6er@dl6er.de>

(also cosmetic change to logging for improved translation from
Matthias Andree <matthias.andree@gmx.de>)
2021-09-23 22:54:17 +01:00
Dominik Derigs ea7a05ad43 Correcly warn if dynamic directory is actually no directory
Signed-off-by: DL6ER <dl6er@dl6er.de>
2021-09-23 11:02:11 +01:00
Simon Kelley 26bbf5a314 Fix --address=/#/...... which was lost in 2.86
A victim of the domain-search rewrite. Apologies.
2021-09-23 10:54:46 +01:00
Dominik DL6ER c147329823 Check if allocation of 66573 bytes succeeded before accessing the memory to avoid crash in busy times
Signed-off-by: DL6ER <dl6er@dl6er.de>
2021-09-20 21:20:41 +01:00
hev eb88eed1fc Optimize inserting records into server list.
Signed-off-by: hev <r@hev.cc>
2021-09-20 20:12:50 +01:00
Simon Kelley 8312a3ba4f Thinko in immediately previous commit. 2021-09-20 19:44:56 +01:00
Simon Kelley 35f93081dc Add support for arbitrary prefix lengths in --rev-server and --domain=....,local
Previously, the prefix was limited to [8,16,24,32] for IPv4 and
to multiples of 4 for IPv6. This patch also makes the prefix-length optional
for --rev-server.

Inspired by a patch from DL6ER <dl6er@dl6er.de>, but completely
re-written by srk. All bugs are his.
2021-09-20 00:05:42 +01:00
Simon Kelley de372d6914 Fix confusion is server=/domain/# combined with server|address=/domain/....
The 2.86 domain matching rewrite failed to take into account the possibilty that

server=/example.com/#

could be combined with, for example

address=/example.com/1.2.3.4

resulting in the struct server datastructure for the former getting passed
to forward_query(), rapidly followed by a SEGV.

This fix makes server=/example.com/# a fully fledged member of the
priority list, which is now  IPv6 addr, IPv4 addr, all zero return,
resolvconf servers, upstream servers, no-data return

Thanks to dl6er@dl6er.de for finding and characterising the bug.
2021-09-18 23:01:12 +01:00
Petr Menšík 4ac517e4ac Fix coverity issues in dnssec.c
Error: CHECKED_RETURN (CWE-252): [#def26]
dnsmasq-2.86rc3/src/dnssec.c:727: check_return: Calling "extract_name" without checking return value (as is done elsewhere 9 out of 10 times).
dnsmasq-2.86rc3/src/dnssec.c:459: example_checked: Example 1: "extract_name(header, plen, &p, keyname, 1, 0)" has its value checked in "extract_name(header, plen, &p, keyname, 1, 0)".
dnsmasq-2.86rc3/src/dnssec.c:269: example_checked: Example 2: "extract_name(header, plen, &state->ip, state->buff, 1, 0)" has its value checked in "extract_name(header, plen, &state->ip, state->buff, 1, 0)".
dnsmasq-2.86rc3/src/dnssec.c:569: example_checked: Example 3: "extract_name(header, plen, &p, keyname, 1, 0)" has its value checked in "extract_name(header, plen, &p, keyname, 1, 0)".
dnsmasq-2.86rc3/src/rfc1035.c:648: example_checked: Example 4: "extract_name(header, qlen, &p1, name, 1, 0)" has its value checked in "extract_name(header, qlen, &p1, name, 1, 0)".
dnsmasq-2.86rc3/src/rfc1035.c:787: example_checked: Example 5: "extract_name(header, qlen, &p1, name, 1, 0)" has its value checked in "extract_name(header, qlen, &p1, name, 1, 0)".
 #  725|         /* namebuff used for workspace above, restore to leave unchanged on exit */
 #  726|         p = (unsigned char*)(rrset[0]);
 #  727|->       extract_name(header, plen, &p, name, 1, 0);
 #  728|
 #  729|         if (key)

Error: CHECKED_RETURN (CWE-252): [#def27]
dnsmasq-2.86rc3/src/dnssec.c:1020: check_return: Calling "extract_name" without checking return value (as is done elsewhere 7 out of 8 times).
dnsmasq-2.86rc3/src/auth.c:140: example_checked: Example 1: "extract_name(header, qlen, &p, name, 1, 4)" has its value checked in "extract_name(header, qlen, &p, name, 1, 4)".
dnsmasq-2.86rc3/src/dnssec.c:771: example_checked: Example 2: "extract_name(header, plen, &p, name, 1, 4)" has its value checked in "extract_name(header, plen, &p, name, 1, 4)".
dnsmasq-2.86rc3/src/hash-questions.c:57: example_checked: Example 3: "extract_name(header, plen, &p, name, 1, 4)" has its value checked in "extract_name(header, plen, &p, name, 1, 4)".
dnsmasq-2.86rc3/src/rfc1035.c:1028: example_checked: Example 4: "extract_name(header, qlen, &p, name, 1, 4)" has its value checked in "extract_name(header, qlen, &p, name, 1, 4)".
dnsmasq-2.86rc3/src/rfc1035.c:1438: example_checked: Example 5: "extract_name(header, qlen, &p, name, 1, 4)" has its value checked in "extract_name(header, qlen, &p, name, 1, 4)".
 # 1018|
 # 1019|     p = (unsigned char *)(header+1);
 # 1020|->   extract_name(header, plen, &p, name, 1, 4);
 # 1021|     p += 4; /* qtype, qclass */
 # 1022|
2021-09-11 22:08:25 +01:00
Petr Menšík e3651367b3 Fix coverity detected issues in dnsmasq.c
Error: DEADCODE (CWE-561): [#def12]
dnsmasq-2.86rc3/src/dnsmasq.c:37: assignment: Assigning: "bind_fallback" = "0".
dnsmasq-2.86rc3/src/dnsmasq.c:927: const: At condition "bind_fallback", the value of "bind_fallback" must be equal to 0.
dnsmasq-2.86rc3/src/dnsmasq.c:927: dead_error_condition: The condition "bind_fallback" cannot be true.
dnsmasq-2.86rc3/src/dnsmasq.c:928: dead_error_line: Execution cannot reach this statement: "my_syslog(4, "setting --bin...".
dnsmasq-2.86rc3/src/dnsmasq.c:928: effectively_constant: Local variable "bind_fallback" is assigned only once, to a constant value, making it effectively constant throughout its scope. If this is not the intent, examine the logic to see if there is a missing assignment that would make "bind_fallback" not remain constant.
 #  926|
 #  927|     if (bind_fallback)
 #  928|->     my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
 #  929|
 #  930|     if (option_bool(OPT_NOWILD))

Error: REVERSE_NEGATIVE (CWE-191): [#def13]
dnsmasq-2.86rc3/src/dnsmasq.c:383: negative_sink_in_call: Passing "dnsmasq_daemon->pxefd" to a parameter that cannot be negative.
dnsmasq-2.86rc3/src/dnsmasq.c:1086: check_after_sink: You might be using variable "dnsmasq_daemon->pxefd" before verifying that it is >= 0.
 # 1084|   	{
 # 1085|   	  poll_listen(daemon->dhcpfd, POLLIN);
 # 1086|-> 	  if (daemon->pxefd != -1)
 # 1087|   	    poll_listen(daemon->pxefd, POLLIN);
 # 1088|   	}

Error: CHECKED_RETURN (CWE-252): [#def18]
dnsmasq-2.86rc3/src/dnsmasq.c:1582: check_return: Calling "fcntl(dnsmasq_daemon->helperfd, 4, i & 0xfffffffffffff7ff)" without checking return value. This library function may fail and return an error code.
 # 1580|   	    /* block in writes until all done */
 # 1581|   	    if ((i = fcntl(daemon->helperfd, F_GETFL)) != -1)
 # 1582|-> 	      fcntl(daemon->helperfd, F_SETFL, i & ~O_NONBLOCK);
 # 1583|   	    do {
 # 1584|   	      helper_write();

Error: CHECKED_RETURN (CWE-252): [#def22]
dnsmasq-2.86rc3/src/dnsmasq.c:1991: check_return: Calling "fcntl(confd, 4, flags & 0xfffffffffffff7ff)" without checking return value. This library function may fail and return an error code.
 # 1989|   		 Reset that here. */
 # 1990|   	      if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
 # 1991|-> 		fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
 # 1992|
 # 1993|   	      buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);

Error: CHECKED_RETURN (CWE-252): [#def26]
dnsmasq-2.86rc3/src/dnssec.c:727: check_return: Calling "extract_name" without checking return value (as is done elsewhere 9 out of 10 times).
dnsmasq-2.86rc3/src/dnssec.c:459: example_checked: Example 1: "extract_name(header, plen, &p, keyname, 1, 0)" has its value checked in "extract_name(header, plen, &p, keyname, 1, 0)".
dnsmasq-2.86rc3/src/dnssec.c:269: example_checked: Example 2: "extract_name(header, plen, &state->ip, state->buff, 1, 0)" has its value checked in "extract_name(header, plen, &state->ip, state->buff, 1, 0)".
dnsmasq-2.86rc3/src/dnssec.c:569: example_checked: Example 3: "extract_name(header, plen, &p, keyname, 1, 0)" has its value checked in "extract_name(header, plen, &p, keyname, 1, 0)".
dnsmasq-2.86rc3/src/rfc1035.c:648: example_checked: Example 4: "extract_name(header, qlen, &p1, name, 1, 0)" has its value checked in "extract_name(header, qlen, &p1, name, 1, 0)".
dnsmasq-2.86rc3/src/rfc1035.c:787: example_checked: Example 5: "extract_name(header, qlen, &p1, name, 1, 0)" has its value checked in "extract_name(header, qlen, &p1, name, 1, 0)".
 #  725|         /* namebuff used for workspace above, restore to leave unchanged on exit */
 #  726|         p = (unsigned char*)(rrset[0]);
 #  727|->       extract_name(header, plen, &p, name, 1, 0);
 #  728|
 #  729|         if (key)

Error: CHECKED_RETURN (CWE-252): [#def27]
dnsmasq-2.86rc3/src/dnssec.c:1020: check_return: Calling "extract_name" without checking return value (as is done elsewhere 7 out of 8 times).
dnsmasq-2.86rc3/src/auth.c:140: example_checked: Example 1: "extract_name(header, qlen, &p, name, 1, 4)" has its value checked in "extract_name(header, qlen, &p, name, 1, 4)".
dnsmasq-2.86rc3/src/dnssec.c:771: example_checked: Example 2: "extract_name(header, plen, &p, name, 1, 4)" has its value checked in "extract_name(header, plen, &p, name, 1, 4)".
dnsmasq-2.86rc3/src/hash-questions.c:57: example_checked: Example 3: "extract_name(header, plen, &p, name, 1, 4)" has its value checked in "extract_name(header, plen, &p, name, 1, 4)".
dnsmasq-2.86rc3/src/rfc1035.c:1028: example_checked: Example 4: "extract_name(header, qlen, &p, name, 1, 4)" has its value checked in "extract_name(header, qlen, &p, name, 1, 4)".
dnsmasq-2.86rc3/src/rfc1035.c:1438: example_checked: Example 5: "extract_name(header, qlen, &p, name, 1, 4)" has its value checked in "extract_name(header, qlen, &p, name, 1, 4)".
 # 1018|
 # 1019|     p = (unsigned char *)(header+1);
 # 1020|->   extract_name(header, plen, &p, name, 1, 4);
 # 1021|     p += 4; /* qtype, qclass */
 # 1022|
2021-09-11 22:08:14 +01:00
Petr Menšík 02ea41ddd1 Fix coverity issues detected in domain-match.c
Error: CHECKED_RETURN (CWE-252): [#def28]
dnsmasq-2.86rc3/src/domain-match.c:414: check_return: Calling "add_resource_record" without checking return value (as is done elsewhere 44 out of 46 times).
dnsmasq-2.86rc3/src/auth.c:214: example_checked: Example 1: "add_resource_record(header, limit, &trunc, nameoffset, &ansp, dnsmasq_daemon->auth_ttl, NULL, 12, 1, "d", intr->name)" has its value checked in "add_resource_record(header, limit, &trunc, nameoffset, &ansp, dnsmasq_daemon->auth_ttl, NULL, 12, 1, "d", intr->name)".
dnsmasq-2.86rc3/src/auth.c:239: example_checked: Example 2: "add_resource_record(header, limit, &trunc, nameoffset, &ansp, dnsmasq_daemon->auth_ttl, NULL, 12, 1, "d", name)" has its value checked in "add_resource_record(header, limit, &trunc, nameoffset, &ansp, dnsmasq_daemon->auth_ttl, NULL, 12, 1, "d", name)".
dnsmasq-2.86rc3/src/rfc1035.c:1463: example_checked: Example 3: "add_resource_record(header, limit, &trunc, nameoffset, &ansp, crec_ttl(crecp, now), &nameoffset, 5, 1, "d", cname_target)" has its value checked in "add_resource_record(header, limit, &trunc, nameoffset, &ansp, crec_ttl(crecp, now), &nameoffset, 5, 1, "d", cname_target)".
dnsmasq-2.86rc3/src/rfc1035.c:1500: example_checked: Example 4: "add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL, 16, t->class, "t", t->len, t->txt)" has its value checked in "add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL, 16, t->class, "t", t->len, t->txt)".
dnsmasq-2.86rc3/src/rfc1035.c:2021: example_checked: Example 5: "add_resource_record(header, limit, NULL, rec->offset, &ansp, crec_ttl(crecp, now), NULL, type, 1, ((crecp->flags & 0x80U) ? "4" : "6"), &crecp->addr)" has its value checked in "add_resource_record(header, limit, NULL, rec->offset, &ansp, crec_ttl(crecp, now), NULL, type, 1, ((crecp->flags & 0x80U) ? "4" : "6"), &crecp->addr)".
 #  412|
 #  413|   	header->ancount = htons(ntohs(header->ancount) + 1);
 #  414|-> 	add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr);
 #  415|   	log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL);
 #  416|         }

Error: CHECKED_RETURN (CWE-252): [#def29]
dnsmasq-2.86rc3/src/domain-match.c:429: check_return: Calling "add_resource_record" without checking return value (as is done elsewhere 44 out of 46 times).
dnsmasq-2.86rc3/src/auth.c:214: example_checked: Example 1: "add_resource_record(header, limit, &trunc, nameoffset, &ansp, dnsmasq_daemon->auth_ttl, NULL, 12, 1, "d", intr->name)" has its value checked in "add_resource_record(header, limit, &trunc, nameoffset, &ansp, dnsmasq_daemon->auth_ttl, NULL, 12, 1, "d", intr->name)".
dnsmasq-2.86rc3/src/auth.c:239: example_checked: Example 2: "add_resource_record(header, limit, &trunc, nameoffset, &ansp, dnsmasq_daemon->auth_ttl, NULL, 12, 1, "d", name)" has its value checked in "add_resource_record(header, limit, &trunc, nameoffset, &ansp, dnsmasq_daemon->auth_ttl, NULL, 12, 1, "d", name)".
dnsmasq-2.86rc3/src/rfc1035.c:1463: example_checked: Example 3: "add_resource_record(header, limit, &trunc, nameoffset, &ansp, crec_ttl(crecp, now), &nameoffset, 5, 1, "d", cname_target)" has its value checked in "add_resource_record(header, limit, &trunc, nameoffset, &ansp, crec_ttl(crecp, now), &nameoffset, 5, 1, "d", cname_target)".
dnsmasq-2.86rc3/src/rfc1035.c:1500: example_checked: Example 4: "add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL, 16, t->class, "t", t->len, t->txt)" has its value checked in "add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL, 16, t->class, "t", t->len, t->txt)".
dnsmasq-2.86rc3/src/rfc1035.c:2021: example_checked: Example 5: "add_resource_record(header, limit, NULL, rec->offset, &ansp, crec_ttl(crecp, now), NULL, type, 1, ((crecp->flags & 0x80U) ? "4" : "6"), &crecp->addr)" has its value checked in "add_resource_record(header, limit, NULL, rec->offset, &ansp, crec_ttl(crecp, now), NULL, type, 1, ((crecp->flags & 0x80U) ? "4" : "6"), &crecp->addr)".
 #  427|
 #  428|   	header->ancount = htons(ntohs(header->ancount) + 1);
 #  429|-> 	add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr);
 #  430|   	log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL);
 #  431|         }

Error: NULL_RETURNS (CWE-476): [#def30]
dnsmasq-2.86rc3/src/domain-match.c:611: returned_null: "whine_malloc" returns "NULL" (checked 72 out of 76 times).
dnsmasq-2.86rc3/src/domain-match.c:611: var_assigned: Assigning: "alloc_domain" = "NULL" return value from "whine_malloc".
dnsmasq-2.86rc3/src/domain-match.c:620: dereference: Dereferencing a pointer that might be "NULL" "alloc_domain" when calling "hostname_isequal".
dnsmasq-2.86rc3/src/arp.c:88: example_checked: Example 1: "whine_malloc(48UL)" has its value checked in "arp = whine_malloc(48UL)".
dnsmasq-2.86rc3/src/blockdata.c:24: example_assign: Example 2: Assigning: "new" = return value from "whine_malloc(n * 48UL)".
dnsmasq-2.86rc3/src/blockdata.c:26: example_checked: Example 2 (cont.): "new" has its value checked in "new".
dnsmasq-2.86rc3/src/cache.c:1545: example_assign: Example 3: Assigning: "crecp" = return value from "whine_malloc(70UL)".
dnsmasq-2.86rc3/src/cache.c:1547: example_checked: Example 3 (cont.): "crecp" has its value checked in "crecp".
dnsmasq-2.86rc3/src/forward.c:1791: example_assign: Example 4: Assigning: "packet" = return value from "whine_malloc(66573UL)".
dnsmasq-2.86rc3/src/forward.c:1795: example_checked: Example 4 (cont.): "packet" has its value checked in "packet".
dnsmasq-2.86rc3/src/inotify.c:186: example_checked: Example 5: "whine_malloc(lendir + lenfile + 2UL)" has its value checked in "path = whine_malloc(lendir + lenfile + 2UL)".
 #  618|     if (flags & SERV_IS_LOCAL)
 #  619|       for (serv = daemon->servers; serv; serv = serv->next)
 #  620|->       if ((serv->flags & SERV_MARK) &&
 #  621|   	  hostname_isequal(alloc_domain, serv->domain))
 #  622|   	break;

Error: RESOURCE_LEAK (CWE-772): [#def31] [important]
dnsmasq-2.86rc3/src/domain-match.c:611: alloc_fn: Storage is returned from allocation function "whine_malloc".
dnsmasq-2.86rc3/src/domain-match.c:611: var_assign: Assigning: "alloc_domain" = storage returned from "whine_malloc(1UL)".
dnsmasq-2.86rc3/src/domain-match.c:620: noescape: Resource "alloc_domain" is not freed or pointed-to in "hostname_isequal".
dnsmasq-2.86rc3/src/domain-match.c:646: leaked_storage: Variable "alloc_domain" going out of scope leaks the storage it points to.
 #  644|
 #  645|         if (!(serv = whine_malloc(size)))
 #  646|-> 	return 0;
 #  647|
 #  648|         if (flags & SERV_IS_LOCAL)

Error: NULL_RETURNS (CWE-476): [#def32]
dnsmasq-2.86rc3/src/domain-match.c:611: returned_null: "whine_malloc" returns "NULL" (checked 72 out of 76 times).
dnsmasq-2.86rc3/src/domain-match.c:611: var_assigned: Assigning: "alloc_domain" = "NULL" return value from "whine_malloc".
dnsmasq-2.86rc3/src/domain-match.c:674: dereference: Dereferencing a pointer that might be "NULL" "alloc_domain" when calling "strlen".
dnsmasq-2.86rc3/src/arp.c:88: example_checked: Example 1: "whine_malloc(48UL)" has its value checked in "arp = whine_malloc(48UL)".
dnsmasq-2.86rc3/src/blockdata.c:24: example_assign: Example 2: Assigning: "new" = return value from "whine_malloc(n * 48UL)".
dnsmasq-2.86rc3/src/blockdata.c:26: example_checked: Example 2 (cont.): "new" has its value checked in "new".
dnsmasq-2.86rc3/src/cache.c:1545: example_assign: Example 3: Assigning: "crecp" = return value from "whine_malloc(70UL)".
dnsmasq-2.86rc3/src/cache.c:1547: example_checked: Example 3 (cont.): "crecp" has its value checked in "crecp".
dnsmasq-2.86rc3/src/forward.c:1791: example_assign: Example 4: Assigning: "packet" = return value from "whine_malloc(66573UL)".
dnsmasq-2.86rc3/src/forward.c:1795: example_checked: Example 4 (cont.): "packet" has its value checked in "packet".
dnsmasq-2.86rc3/src/inotify.c:186: example_checked: Example 5: "whine_malloc(lendir + lenfile + 2UL)" has its value checked in "path = whine_malloc(lendir + lenfile + 2UL)".
 #  672|     serv->flags = flags;
 #  673|     serv->domain = alloc_domain;
 #  674|->   serv->domain_len = strlen(alloc_domain);
 #  675|
 #  676|     if (flags & SERV_4ADDR)
2021-09-11 22:04:05 +01:00
Simon Kelley 51ffae4eab Fix coverity detected issues in cache.c
Error: UNINIT (CWE-457): [#def27]
dnsmasq-2.86test7/src/cache.c:1193: var_decl: Declaring variable "lrec" without initializer.
dnsmasq-2.86test7/src/cache.c:1315: uninit_use_in_call: Using uninitialized value "lrec.ttd" when calling "make_non_terminals".
 # 1313|       {
 # 1314|         lrec.name.namep = txt->name;
 # 1315|->       make_non_terminals(&lrec);
 # 1316|       }
 # 1317|

Error: CLANG_WARNING: [#def29]
dnsmasq-2.86test7/src/cache.c:1552:15: warning[core.uninitialized.Assign]: Assigned value is garbage or undefined
 # 1550|   	{
 # 1551|   	  crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_REVERSE);
 # 1552|-> 	  crecp->ttd = source->ttd;
 # 1553|   	  crecp->name.namep = name;
 # 1554|
2021-09-11 21:57:30 +01:00
Petr Menšík afe84f37f8 Fix coverity detected issue in radv.c
Error: NULL_RETURNS (CWE-476): [#def114]
dnsmasq-2.86test7/src/radv.c:748: returned_null: "expand" returns "NULL" (checked 10 out of 11 times).
dnsmasq-2.86test7/src/radv.c:748: var_assigned: Assigning: "p" = "NULL" return value from "expand".
dnsmasq-2.86test7/src/radv.c:749: dereference: Dereferencing a pointer that might be "NULL" "p" when calling "memset". [Note: The source code implementation of the function has been overridden by a builtin model.]
dnsmasq-2.86test7/src/outpacket.c:83: example_checked: Example 1: "expand(len)" has its value checked in "p = expand(len)".
dnsmasq-2.86test7/src/outpacket.c:109: example_checked: Example 2: "expand(1UL)" has its value checked in "p = expand(1UL)".
dnsmasq-2.86test7/src/radv.c:269: example_checked: Example 3: "expand(16UL)" has its value checked in "ra = expand(16UL)".
dnsmasq-2.86test7/src/radv.c:363: example_checked: Example 4: "expand(32UL)" has its value checked in "opt = expand(32UL)".
dnsmasq-2.86test7/src/radv.c:708: example_checked: Example 5: "expand(32UL)" has its value checked in "opt = expand(32UL)".
 #  747|         int len = (maclen + 9) >> 3;
 #  748|         unsigned char *p = expand(len << 3);
 #  749|->       memset(p, 0, len << 3);
 #  750|         *p++ = ICMP6_OPT_SOURCE_MAC;
 #  751|         *p++ = len;

Error: NULL_RETURNS (CWE-476): [#def115]
dnsmasq-2.86test7/src/radv.c:748: returned_null: "expand" returns "NULL" (checked 10 out of 11 times).
dnsmasq-2.86test7/src/radv.c:748: var_assigned: Assigning: "p" = "NULL" return value from "expand".
dnsmasq-2.86test7/src/radv.c:750: dereference: Incrementing a pointer which might be null: "p".
dnsmasq-2.86test7/src/outpacket.c:83: example_checked: Example 1: "expand(len)" has its value checked in "p = expand(len)".
dnsmasq-2.86test7/src/outpacket.c:109: example_checked: Example 2: "expand(1UL)" has its value checked in "p = expand(1UL)".
dnsmasq-2.86test7/src/radv.c:269: example_checked: Example 3: "expand(16UL)" has its value checked in "ra = expand(16UL)".
dnsmasq-2.86test7/src/radv.c:363: example_checked: Example 4: "expand(32UL)" has its value checked in "opt = expand(32UL)".
dnsmasq-2.86test7/src/radv.c:708: example_checked: Example 5: "expand(32UL)" has its value checked in "opt = expand(32UL)".
 #  748|         unsigned char *p = expand(len << 3);
 #  749|         memset(p, 0, len << 3);
 #  750|->       *p++ = ICMP6_OPT_SOURCE_MAC;
 #  751|         *p++ = len;
 #  752|         memcpy(p, mac, maclen);
2021-09-11 21:51:10 +01:00
Petr Menšík 0afeef0e00 Fix coverity detected issues in option.c
Error: STRING_OVERFLOW (CWE-120): [#def99]
dnsmasq-2.86test7/src/option.c:801: fixed_size_dest: You might overrun the 100-character fixed-size string "buff" by copying "usage[i].arg" without checking the length.
#  799|         if (usage[i].arg)
#  800|   	{
#  801|-> 	  strcpy(buff, usage[i].arg);
#  802|   	  for (j = 0; tab[j].handle; j++)
#  803|   	    if (tab[j].handle == *(usage[i].arg))

Error: CLANG_WARNING: [#def100]
dnsmasq-2.86test7/src/option.c:962:3: warning[deadcode.DeadStores]: Value stored to 'domain' is never read
#  960|       }
#  961|
#  962|->   domain += sprintf(domain, "in-addr.arpa");
#  963|
#  964|     return 1;

Error: CLANG_WARNING: [#def101]
dnsmasq-2.86test7/src/option.c:981:3: warning[deadcode.DeadStores]: Value stored to 'domain' is never read
#  979|         domain += sprintf(domain, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
#  980|       }
#  981|->   domain += sprintf(domain, "ip6.arpa");
#  982|
#  983|     return 1;

Error: RESOURCE_LEAK (CWE-772): [#def102] [important]
dnsmasq-2.86test7/src/option.c:1809: alloc_fn: Storage is returned from allocation function "opt_malloc".
dnsmasq-2.86test7/src/option.c:1809: var_assign: Assigning: "path" = storage returned from "opt_malloc(strlen(directory) + len + 2UL)".
dnsmasq-2.86test7/src/option.c:1810: noescape: Resource "path" is not freed or pointed-to in "strcpy". [Note: The source code implementation of the function has been overridden by a builtin model.]
dnsmasq-2.86test7/src/option.c:1811: noescape: Resource "path" is not freed or pointed-to in "strcat". [Note: The source code implementation of the function has been overridden by a builtin model.]
dnsmasq-2.86test7/src/option.c:1812: noescape: Resource "path" is not freed or pointed-to in "strcat". [Note: The source code implementation of the function has been overridden by a builtin model.]
dnsmasq-2.86test7/src/option.c:1815: noescape: Resource "path" is not freed or pointed-to in "stat".
dnsmasq-2.86test7/src/option.c:1809: overwrite_var: Overwriting "path" in "path = opt_malloc(strlen(directory) + len + 2UL)" leaks the storage that "path" points to.
# 1807|   	      continue;
# 1808|
# 1809|-> 	    path = opt_malloc(strlen(directory) + len + 2);
# 1810|   	    strcpy(path, directory);
# 1811|   	    strcat(path, "/");

Error: RESOURCE_LEAK (CWE-772): [#def103] [important]
dnsmasq-2.86test7/src/option.c:1809: alloc_fn: Storage is returned from allocation function "opt_malloc".
dnsmasq-2.86test7/src/option.c:1809: var_assign: Assigning: "path" = storage returned from "opt_malloc(strlen(directory) + len + 2UL)".
dnsmasq-2.86test7/src/option.c:1810: noescape: Resource "path" is not freed or pointed-to in "strcpy". [Note: The source code implementation of the function has been overridden by a builtin model.]
dnsmasq-2.86test7/src/option.c:1811: noescape: Resource "path" is not freed or pointed-to in "strcat". [Note: The source code implementation of the function has been overridden by a builtin model.]
dnsmasq-2.86test7/src/option.c:1812: noescape: Resource "path" is not freed or pointed-to in "strcat". [Note: The source code implementation of the function has been overridden by a builtin model.]
dnsmasq-2.86test7/src/option.c:1815: noescape: Resource "path" is not freed or pointed-to in "stat".
dnsmasq-2.86test7/src/option.c:1858: leaked_storage: Variable "path" going out of scope leaks the storage it points to.
# 1856|   	    free(files);
# 1857|   	  }
# 1858|-> 	break;
# 1859|         }
# 1860|

Error: RESOURCE_LEAK (CWE-772): [#def104] [important]
dnsmasq-2.86test7/src/option.c:1996: alloc_fn: Storage is returned from allocation function "canonicalise_opt".
dnsmasq-2.86test7/src/option.c:1996: var_assign: Assigning: "name" = storage returned from "canonicalise_opt(arg)".
dnsmasq-2.86test7/src/option.c:1998: leaked_storage: Variable "name" going out of scope leaks the storage it points to.
# 1996|   	if (!(name = canonicalise_opt(arg)) ||
# 1997|   	    (comma && !(target = canonicalise_opt(comma))))
# 1998|-> 	  ret_err(_("bad MX name"));
# 1999|
# 2000|   	new = opt_malloc(sizeof(struct mx_srv_record));

Error: RESOURCE_LEAK (CWE-772): [#def106] [important]
dnsmasq-2.86test7/src/option.c:3477: alloc_fn: Storage is returned from allocation function "opt_malloc".
dnsmasq-2.86test7/src/option.c:3477: var_assign: Assigning: "new" = storage returned from "opt_malloc(96UL)".
dnsmasq-2.86test7/src/option.c:3618: leaked_storage: Variable "new" going out of scope leaks the storage it points to.
# 3616|   		      sprintf(errstr, _("duplicate dhcp-host IP address %s"),
# 3617|   			      daemon->addrbuff);
# 3618|-> 		      return 0;
# 3619|   		    }
# 3620|   	      }

Error: RESOURCE_LEAK (CWE-772): [#def108] [important]
dnsmasq-2.86test7/src/option.c:3781: alloc_fn: Storage is returned from allocation function "opt_malloc".
dnsmasq-2.86test7/src/option.c:3781: var_assign: Assigning: "new" = storage returned from "opt_malloc(32UL)".
dnsmasq-2.86test7/src/option.c:3786: leaked_storage: Variable "new" going out of scope leaks the storage it points to.
# 3784|
# 3785|   	if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
# 3786|-> 	  ret_err(gen_err);
# 3787|
# 3788|   	new->wildcard = 0;

Error: RESOURCE_LEAK (CWE-772): [#def109] [important]
dnsmasq-2.86test7/src/option.c:3921: alloc_fn: Storage is returned from allocation function "opt_malloc".
dnsmasq-2.86test7/src/option.c:3921: var_assign: Assigning: "new" = storage returned from "opt_malloc(56UL)".
dnsmasq-2.86test7/src/option.c:3994: leaked_storage: Variable "new" going out of scope leaks the storage it points to.
# 3992|   	   }
# 3993|
# 3994|-> 	 ret_err(gen_err);
# 3995|          }
# 3996|

Error: CLANG_WARNING: [#def111]
dnsmasq-2.86test7/src/option.c:4693:25: warning[deadcode.DeadStores]: Value stored to 'tmp' during its initialization is never read
# 4691|   		if (!canon)
# 4692|                     {
# 4693|-> 		    struct name_list *tmp = new->names, *next;
# 4694|   		    for (tmp = new->names; tmp; tmp = next)
# 4695|
2021-09-11 21:50:33 +01:00
Petr Menšík 94a17fd97f Address coverity issues detected in util.c 2021-09-11 21:49:28 +01:00
Petr Menšík 5b5ec55445 Fix coverity warnings on dbus
Error: CLANG_WARNING: [#def30]
dnsmasq-2.86test7/src/dbus.c:117:3: warning[deadcode.DeadStores]: Value stored to 'w' is never read
 #  115|     daemon->watches = w;
 #  116|
 #  117|->   w = data; /* no warning */
 #  118|     return TRUE;
 #  119|   }

Error: CLANG_WARNING: [#def31]
dnsmasq-2.86test7/src/dbus.c:137:3: warning[deadcode.DeadStores]: Value stored to 'w' is never read
 #  135|       }
 #  136|
 #  137|->   w = data; /* no warning */
 #  138|   }
 #  139|

Error: CHECKED_RETURN (CWE-252): [#def32]
dnsmasq-2.86test7/src/dbus.c:146: check_return: Calling "dbus_message_iter_init" without checking return value (as is done elsewhere 4 out of 5 times).
dnsmasq-2.86test7/src/dbus.c:460: example_checked: Example 1: "dbus_message_iter_init(message, &iter)" has its value checked in "dbus_message_iter_init(message, &iter)".
dnsmasq-2.86test7/src/dbus.c:573: example_checked: Example 2: "dbus_message_iter_init(message, &iter)" has its value checked in "dbus_message_iter_init(message, &iter)".
dnsmasq-2.86test7/src/dbus.c:257: example_checked: Example 3: "dbus_message_iter_init(message, &iter)" has its value checked in "dbus_message_iter_init(message, &iter)".
dnsmasq-2.86test7/src/dbus.c:427: example_checked: Example 4: "dbus_message_iter_init(message, &iter)" has its value checked in "dbus_message_iter_init(message, &iter)".
 #  144|     char *domain;
 #  145|
 #  146|->   dbus_message_iter_init(message, &iter);
 #  147|
 #  148|     mark_servers(SERV_FROM_DBUS);

Error: NEGATIVE_RETURNS (CWE-394): [#def33]
dnsmasq-2.86test7/src/dbus.c:547: negative_return_fn: Function "parse_hex((char *)hwaddr, dhcp_chaddr, 16, NULL, &hw_type)" returns a negative number.
dnsmasq-2.86test7/src/dbus.c:547: assign: Assigning: "hw_len" = "parse_hex((char *)hwaddr, dhcp_chaddr, 16, NULL, &hw_type)".
dnsmasq-2.86test7/src/dbus.c:551: negative_returns: "hw_len" is passed to a parameter that cannot be negative.
 #  549|       hw_type = ARPHRD_ETHER;
 #  550|
 #  551|->   lease_set_hwaddr(lease, dhcp_chaddr, clid, hw_len, hw_type,
 #  552|                      clid_len, now, 0);
 #  553|     lease_set_expires(lease, expires, now);

Error: CLANG_WARNING: [#def34]
dnsmasq-2.86test7/src/dbus.c:722:3: warning[deadcode.DeadStores]: Value stored to 'method' is never read
 #  720|       clear_cache_and_reload(dnsmasq_time());
 #  721|
 #  722|->   method = user_data; /* no warning */
 #  723|
 #  724|     /* If no reply or no error, return nothing */
2021-09-11 21:37:18 +01:00
Petr Menšík 1e6565c1a5 Retry dhcp6 ping on interrupts
Error: CHECKED_RETURN (CWE-252): [#def35]
dnsmasq-2.86test7/src/dhcp6.c:295: check_return: Calling "sendto(dnsmasq_daemon->icmp6fd, &neigh, 24UL, 0, __CONST_SOCKADDR_ARG({.__sockaddr__ = &addr.sa}), 28U)" without checking return value. This library function may fail and return an error code.
 #  293|   	break;
 #  294|
 #  295|->       sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr));
 #  296|
 #  297|         ts.tv_sec = 0;
2021-09-11 21:36:30 +01:00
Petr Menšík fc522515b9 Fix coverity formats issues in blockdata
Error: PRINTF_ARGS (CWE-686): [#def16]
dnsmasq-2.86test7/src/blockdata.c:56: invalid_type: Argument "blockdata_count * 48UL" to format specifier "%u" was expected to have type "unsigned int" but has type "unsigned long".
 #   54|   {
 #   55|     my_syslog(LOG_INFO, _("pool memory in use %u, max %u, allocated %u"),
 #   56|-> 	    blockdata_count * sizeof(struct blockdata),
 #   57|   	    blockdata_hwm * sizeof(struct blockdata),
 #   58|   	    blockdata_alloced * sizeof(struct blockdata));

Error: PRINTF_ARGS (CWE-686): [#def17]
dnsmasq-2.86test7/src/blockdata.c:57: invalid_type: Argument "blockdata_hwm * 48UL" to format specifier "%u" was expected to have type "unsigned int" but has type "unsigned long".
 #   55|     my_syslog(LOG_INFO, _("pool memory in use %u, max %u, allocated %u"),
 #   56|   	    blockdata_count * sizeof(struct blockdata),
 #   57|-> 	    blockdata_hwm * sizeof(struct blockdata),
 #   58|   	    blockdata_alloced * sizeof(struct blockdata));
 #   59|   }

Error: PRINTF_ARGS (CWE-686): [#def18]
dnsmasq-2.86test7/src/blockdata.c:58: invalid_type: Argument "blockdata_alloced * 48UL" to format specifier "%u" was expected to have type "unsigned int" but has type "unsigned long".
 #   56|   	    blockdata_count * sizeof(struct blockdata),
 #   57|   	    blockdata_hwm * sizeof(struct blockdata),
 #   58|-> 	    blockdata_alloced * sizeof(struct blockdata));
 #   59|   }
 #   60|
2021-09-11 21:36:01 +01:00
Petr Menšík 9881b0736d Fix few coverity warnings in lease-tools
Error: UNINIT (CWE-457): [#def2]
dnsmasq-2.86test7/contrib/lease-tools/dhcp_release.c:265: var_decl: Declaring variable "ifr" without initializer.
dnsmasq-2.86test7/contrib/lease-tools/dhcp_release.c:285: uninit_use_in_call: Using uninitialized value "ifr". Field "ifr.ifr_ifru" is uninitialized when calling "setsockopt".
 #  283|     strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name)-1);
 #  284|     ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';
 #  285|->   if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
 #  286|       {
 #  287|         perror("cannot setup interface");

Error: CHECKED_RETURN (CWE-252): [#def3]
dnsmasq-2.86test7/contrib/lease-tools/dhcp_release6.c:346: check_return: Calling "inet_pton" without checking return value (as is done elsewhere 61 out of 72 times).
dnsmasq-2.86test7/contrib/lease-tools/dhcp_release6.c:188: example_assign: Example 1: Assigning: "s" = return value from "inet_pton(10, ip, &result.ip)".
dnsmasq-2.86test7/contrib/lease-tools/dhcp_release6.c:189: example_checked: Example 1 (cont.): "s" has its value checked in "s <= 0".
dnsmasq-2.86test7/src/cache.c:1108: example_checked: Example 2: "inet_pton(10, token, &addr)" has its value checked in "inet_pton(10, token, &addr) > 0".
dnsmasq-2.86test7/src/dbus.c:525: example_checked: Example 3: "inet_pton(2, ipaddr, &addr.addr4)" has its value checked in "inet_pton(2, ipaddr, &addr.addr4)".
dnsmasq-2.86test7/src/domain.c:138: example_checked: Example 4: "inet_pton(prot, tail, addr)" has its value checked in "inet_pton(prot, tail, addr)".
dnsmasq-2.86test7/src/lease.c:81: example_checked: Example 5: "inet_pton(10, dnsmasq_daemon->namebuff, &addr.addr6)" has its value checked in "inet_pton(10, dnsmasq_daemon->namebuff, &addr.addr6)".
 #  344|       client_addr.sin6_flowinfo = 0;
 #  345|       client_addr.sin6_scope_id =0;
 #  346|->     inet_pton(AF_INET6, "::", &client_addr.sin6_addr);
 #  347|       bind(sock, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in6));
 #  348|       inet_pton(AF_INET6, DHCP6_MULTICAST_ADDRESS, &server_addr.sin6_addr);

Error: CHECKED_RETURN (CWE-252): [#def4]
dnsmasq-2.86test7/contrib/lease-tools/dhcp_release6.c:347: check_return: Calling "bind(sock, (struct sockaddr *)&client_addr, 28U)" without checking return value. This library function may fail and return an error code.
 #  345|       client_addr.sin6_scope_id =0;
 #  346|       inet_pton(AF_INET6, "::", &client_addr.sin6_addr);
 #  347|->     bind(sock, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in6));
 #  348|       inet_pton(AF_INET6, DHCP6_MULTICAST_ADDRESS, &server_addr.sin6_addr);
 #  349|       server_addr.sin6_port = htons(DHCP6_SERVER_PORT);

Error: CHECKED_RETURN (CWE-252): [#def5]
dnsmasq-2.86test7/contrib/lease-tools/dhcp_release6.c:348: check_return: Calling "inet_pton" without checking return value (as is done elsewhere 61 out of 72 times).
dnsmasq-2.86test7/contrib/lease-tools/dhcp_release6.c:188: example_assign: Example 1: Assigning: "s" = return value from "inet_pton(10, ip, &result.ip)".
dnsmasq-2.86test7/contrib/lease-tools/dhcp_release6.c:189: example_checked: Example 1 (cont.): "s" has its value checked in "s <= 0".
dnsmasq-2.86test7/src/cache.c:1108: example_checked: Example 2: "inet_pton(10, token, &addr)" has its value checked in "inet_pton(10, token, &addr) > 0".
dnsmasq-2.86test7/src/dbus.c:525: example_checked: Example 3: "inet_pton(2, ipaddr, &addr.addr4)" has its value checked in "inet_pton(2, ipaddr, &addr.addr4)".
dnsmasq-2.86test7/src/domain.c:138: example_checked: Example 4: "inet_pton(prot, tail, addr)" has its value checked in "inet_pton(prot, tail, addr)".
dnsmasq-2.86test7/src/lease.c:81: example_checked: Example 5: "inet_pton(10, dnsmasq_daemon->namebuff, &addr.addr6)" has its value checked in "inet_pton(10, dnsmasq_daemon->namebuff, &addr.addr6)".
 #  346|       inet_pton(AF_INET6, "::", &client_addr.sin6_addr);
 #  347|       bind(sock, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in6));
 #  348|->     inet_pton(AF_INET6, DHCP6_MULTICAST_ADDRESS, &server_addr.sin6_addr);
 #  349|       server_addr.sin6_port = htons(DHCP6_SERVER_PORT);
 #  350|       int16_t recv_size = 0;

Error: NEGATIVE_RETURNS (CWE-394): [#def6]
dnsmasq-2.86test7/contrib/lease-tools/dhcp_release6.c:360: var_tested_neg: Variable "recv_size" tests negative.
dnsmasq-2.86test7/contrib/lease-tools/dhcp_release6.c:373: negative_returns: "recv_size" is passed to a parameter that cannot be negative.
 #  371|   	  }
 #  372|
 #  373|->         int16_t result = parse_packet(response, recv_size);
 #  374|           if (result == NOT_REPLY_CODE)
 #  375|   	  {
2021-09-11 18:01:49 +01:00
Petr Menšík e52b4b1466 Fix bunch of warnings in auth.c
Error: CLANG_WARNING: [#def7]
dnsmasq-2.86test7/src/auth.c:420:5: warning[deadcode.DeadStores]: Value stored to 'found' is never read
 #  418|          if (!found && is_name_synthetic(flag, name, &addr) )
 #  419|   	 {
 #  420|-> 	   found = 1;
 #  421|   	   nxdomain = 0;
 #  422|

Error: CLANG_WARNING: [#def8]
dnsmasq-2.86test7/src/auth.c:436:8: warning[deadcode.DeadStores]: Value stored to 'found' is never read
 #  434|   	    {
 #  435|   	      auth = soa = 1; /* inhibits auth section */
 #  436|-> 	      found = 1;
 #  437|   	      log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>");
 #  438|   	    }

Error: CLANG_WARNING: [#def9]
dnsmasq-2.86test7/src/auth.c:472:8: warning[deadcode.DeadStores]: Value stored to 'found' is never read
 #  470|   	      ns = 1; /* ensure we include NS records! */
 #  471|   	      axfr = 1;
 #  472|-> 	      found = 1;
 #  473|   	      axfroffset = nameoffset;
 #  474|   	      log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>");

Error: CLANG_WARNING: [#def10]
dnsmasq-2.86test7/src/auth.c:480:8: warning[deadcode.DeadStores]: Value stored to 'found' is never read
 #  478|   	      auth = 1;
 #  479|   	      ns = 1; /* inhibits auth section */
 #  480|-> 	      found = 1;
 #  481|   	      log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>");
 #  482|   	    }

Error: CLANG_WARNING: [#def11]
dnsmasq-2.86test7/src/auth.c:501:4: warning[deadcode.DeadStores]: Value stored to 'found' is never read
 #  499|   			log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid));
 #  500|   			*cut  = 0; /* remove domain part */
 #  501|-> 			found = 1;
 #  502|   			if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
 #  503|   						daemon->auth_ttl, NULL, qtype, C_IN,

Error: CLANG_WARNING: [#def12]
dnsmasq-2.86test7/src/auth.c:522:8: warning[deadcode.DeadStores]: Value stored to 'found' is never read
 #  520|   		   {
 #  521|   		     log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid));
 #  522|-> 		     found = 1;
 #  523|   		     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
 #  524|   					     daemon->auth_ttl, NULL, qtype, C_IN,

Error: CLANG_WARNING: [#def13]
dnsmasq-2.86test7/src/auth.c:617:8: warning[deadcode.DeadStores]: Value stored to 'p' is never read
 #  615|   		p += sprintf(p, "%u.", a & 0xff);
 #  616|   	      a = a >> 8;
 #  617|-> 	      p += sprintf(p, "%u.in-addr.arpa", a & 0xff);
 #  618|
 #  619|   	    }

Error: CPPCHECK_WARNING (CWE-758): [#def14]
dnsmasq-2.86test7/src/auth.c:627: warning[objectIndex]: The address of local variable 'addr6' might be accessed at non-zero index.
 #  625|   	      for (i = subnet->prefixlen-1; i >= 0; i -= 4)
 #  626|   		{
 #  627|-> 		  int dig = ((unsigned char *)&subnet->addr.addr6)[i>>3];
 #  628|   		  p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
 #  629|   		}

Error: CLANG_WARNING: [#def15]
dnsmasq-2.86test7/src/auth.c:630:8: warning[deadcode.DeadStores]: Value stored to 'p' is never read
 #  628|   		  p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
 #  629|   		}
 #  630|-> 	      p += sprintf(p, "ip6.arpa");
 #  631|
 #  632|   	    }
2021-09-11 17:56:01 +01:00
Petr Menšík 2f45670951 Add safety checks to places pointed by Coverity
GCC Analyzer (experimental)

1. dnsmasq-2.85/src/forward.c:0: scope_hint: In function 'allocate_rfd.part.0'
2. dnsmasq-2.85/src/forward.c:2321:18: warning[-Wanalyzer-null-dereference]: dereference of NULL 'rfd'
 #  2319|     *fdlp = rfl;
 #  2320|
 #  2321|->   return rfl->rfd->fd;
 #  2322|   }
 #  2323|

1. dnsmasq-2.85/src/cache.c:0: scope_hint: In function 'log_query'
2. dnsmasq-2.85/src/cache.c:1969:20: warning[-Wanalyzer-null-dereference]: dereference of NULL 'name'
 #  1967|       source = "cached";
 #  1968|
 #  1969|->   if (strlen(name) == 0)
 #  1970|       name = ".";
 #  1971|

1. dnsmasq-2.85/src/cache.c:0: scope_hint: In function 'cache_scan_free'
2. dnsmasq-2.85/src/cache.c:436:20: warning[-Wanalyzer-null-argument]: use of NULL 'addr' where non-null expected
40. /usr/include/sys/un.h:37: included_from: Included from here.
41. dnsmasq-2.85/src/dnsmasq.h:101: included_from: Included from here.
42. dnsmasq-2.85/src/cache.c:17: included_from: Included from here.
43. /usr/include/string.h:64:12: note: argument 2 of 'memcmp' must be non-null
 #   434|   		   (flags & crecp->flags & F_REVERSE) &&
 #   435|   		   (flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
 #   436|-> 		   memcmp(&crecp->addr, addr, addrlen) == 0)
 #   437|   	    {
 #   438|   	      *up = crecp->hash_next;
2021-09-11 14:48:17 +01:00
Petr Menšík 50d75ae514 Retry on interrupted error in tftp
Interrupt might arrive when sending error reply. Retry if possible.

Wrong Check of Return Value

10. dnsmasq-2.85/src/tftp.c:603: check_return: Calling "sendto(transfer->sockfd, dnsmasq_daemon->packet, len, 0, __CONST_SOCKADDR_ARG({.__sockaddr__ = &peer.sa}), sa_len(&peer))" without checking return value. This library function may fail and return an error code.
 #   601|   		  prettyprint_addr(&peer, daemon->addrbuff);
 #   602|   		  len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff);
 #   603|-> 		  sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer));
 #   604|   		}
 #   605|   	    }
2021-09-11 14:39:36 +01:00
Petr Menšík dea69a12aa Small sanity check in wildcard tag matching code. 2021-09-11 14:26:03 +01:00
Dominik DL6ER e0ce3c12f2 Add all current RR types to the table of type names used for query logging.
This patch also changes the method of calling querystr() such that
it is only called when logging is enabled, to eliminate any
possible performance problems from searching the larger table.
2021-09-10 23:13:53 +01:00
Gustaf Ullberg 93cf516bf1 check_name() determines if IDN processing is needed.
Optimization that only runs IDN processing if it would alter the domain
name (non-ascii or uppercase characters).

This patch has conributions from Petr Menšík.
2021-09-10 00:13:39 +01:00
Simon Kelley 6f4de018af Revert "Skip ascii-only names IDN processing"
This reverts commit 9cb7f8a655.
2021-09-10 00:02:11 +01:00
Simon Kelley 6e91cf3172 Bump version in Debian changelog. 2021-09-08 23:19:08 +01:00
Petr Menšík 9cb7f8a655 Skip ascii-only names IDN processing
Calls to libidn on names without with only a-z A-Z - _ 0-9
have no effect, but are slow. This change elides those calls.

Patch inspire by analysis and an earlier patch from
Gustaf Ullberg <gustaf.ullberg@gmail.com>
2021-09-08 23:08:21 +01:00
Simon Kelley 5d8d1ad14b Merge branch 'nxdomain' 2021-09-08 23:05:35 +01:00
Simon Kelley cac9ca38f6 Treat ANY queries the same as CNAME queries WRT to DNSSEC on CNAME targets. 2021-09-08 21:21:22 +01:00
Simon Kelley c4523639d5 Treat ANY queries the same as CNAME queries WRT to DNSSEC on CNAME targets. 2021-09-08 21:19:15 +01:00
Simon Kelley 1ce1c6beae Caching cleanup. Use cached NXDOMAIN to answer queries of any type. 2021-09-05 18:47:45 +01:00
DL6ER 51d56df7a3 Add RFC 4833 DHCP options "posix-timezone" and "tzdb-timezone".
Signed-off-by: DL6ER <dl6er@dl6er.de>
2021-09-04 22:24:14 +01:00
Simon Kelley 860a9a57d6 Get logging of DNSSEC status right when Checking Disabled bit set. 2021-09-02 10:07:08 +01:00
Dominik DL6ER c83e33d608 Final logging tweaks. 2021-09-01 21:19:47 +01:00
Simon Kelley 7b80c75d9d Rationalise query-reply logging.
Try and log exactly what was returned, rather than just what
got cached. Also give validation status of RRsets if extra logging specified.

This commit also fixes a long-standing bug in caching of CNAME chains
leading to a PTR record.

Based on and inspired by a patch from Dominik DL6ER <dl6er@dl6er.de>
2021-08-31 18:23:03 +01:00
Geoff Back 79337f99ae Support limited wildcards in the input tags for --tag-if. 2021-08-29 13:27:27 +01:00
Geert Stappers via Dnsmasq-discuss a42ee397f3 Man page BNF error fix.
Move dhcp-range bracket indicating option.

There should already be an end-address or mode  when adding a netmask.

Also the date bumped.

Signed-off-by: Geert Stappers <stappers@stappers.nl>
2021-08-25 14:32:43 +01:00
Simon Kelley e58f8bb8c1 Merge message changes into I18N files. 2021-08-25 14:11:42 +01:00
Dominik DL6ER 58cf958e41 Fix empty domain in server option parsing when more than one domain is given
Signed-off-by: DL6ER <dl6er@dl6er.de>
2021-08-25 14:09:40 +01:00
Etan Kissling 06d01f7ae4 Make comment style consistent.
Majority of code base does not use C90-style // end of line comments.
This formats the few existing exceptions using /* */ for consistency.
2021-08-12 17:02:45 +01:00
Etan Kissling 1a33eec0ba Adjust logging levels for connmark patterns.
This brings the log levels emitted by connmark pattern code in line with
the rest of the code base. LOG_DEBUG is used for diagnostics that may be
verbose depending on the request patterns. LOG_ERR is used for problems
with the implementation itself.

Signed-off-by: Etan Kissling <etan.kissling@gmail.com>
2021-08-12 17:02:24 +01:00
Etan Kissling 82de7a1e96 Re-order UBus teardown logic.
When destroying the UBus context, private fields of our ubus_object were
being reset to 0 while UBus was still owning those objects. While this
seems to work out fine, it seems cleaner to first release the object so
that UBus no longer owns it, before proceding to reset those fields.

Signed-off-by: Etan Kissling <etan.kissling@gmail.com>
2021-08-12 17:02:07 +01:00
Etan Kissling 8e9bde57c5 Eliminate redundant UBus notify variable.
There was a `notify` variable to keep track whether a subscriber is
observing our UBus object. However, it was not properly cleaned up in
`ubus_destroy`, potentially becoming stale over UBus reconnections.
The variable was removed and the current state is examined when sending
notifications, similarly as is done in other existing OpenWrt code.

Signed-off-by: Etan Kissling <etan.kissling@gmail.com>
2021-08-12 17:01:17 +01:00
Etan Kissling 1bb70e08be Handle UBus serialization errors.
The various blob / blobmsg commands can fail, e.g., when memory is low.
Previously, those errors were silently discarded. This patch adds checks
for the error conditions, logging them and exiting from the functions.

Signed-off-by: Etan Kissling <etan.kissling@gmail.com>
2021-08-12 17:00:18 +01:00
Simon Kelley 9afcb7ae61 Revert "There was a notify variable to keep track whether a subscriber is"
This reverts commit ea43234c86.
2021-08-12 17:00:10 +01:00
Simon Kelley 545c4955e6 Revert "Re-order UBus teardown logic."
This reverts commit d387f8f06c.
2021-08-12 16:58:30 +01:00
Simon Kelley 2f2d59b35c Define order of reading files when --addn-hosts given a directory.
Also applies to --dhcp-hostsfile and --dhcp-optsfile though it is
less useful there.
2021-08-12 16:48:54 +01:00
Simon Kelley a1729deed3 Fiz sizeof() confusion in 527c3c7d0d 2021-08-11 09:10:39 +01:00
Simon Kelley fc64b97cd5 dhcp_buff2 not availble in log_packet, use daemon->addrbuff 2021-08-10 23:54:13 +01:00
Simon Kelley daddc8cb80 Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq 2021-08-10 23:15:46 +01:00
Petr Menšík 527c3c7d0d Remove remaining uses of deprecated inet_ntoa() 2021-08-10 22:50:33 +01:00
Petr Menšík fcb4dcaf7c Remove remaining uses of deprecated inet_addr() function. 2021-08-10 22:21:01 +01:00
Matthias Andree 3ca4995d34 CHANGELOG: spell-check 2021-08-10 21:40:06 +01:00
Etan Kissling d387f8f06c Re-order UBus teardown logic.
When destroying the UBus context, private fields of our ubus_object were
being reset to 0 while UBus was still owning those objects. While this
seems to work out fine, it seems cleaner to first release the object so
that UBus no longer owns it, before proceding to reset those fields.

Signed-off-by: Etan Kissling <etan.kissling@gmail.com>
2021-08-10 21:16:03 +01:00
Etan Kissling ea43234c86 There was a notify variable to keep track whether a subscriber is
observing our UBus object. However, it was not properly cleaned up in
`ubus_destroy`, potentially becoming stale over UBus reconnections.
The variable was removed and the current state is examined when sending
notifications, similarly as is done in other existing OpenWrt code.

Signed-off-by: Etan Kissling <etan.kissling@gmail.com>
2021-08-10 21:15:09 +01:00
Simon Kelley 867e56a45e Fix NOERR/NXDOMAIN in answers configured by --domain-needed. 2021-08-10 13:00:23 +01:00
Simon Kelley a163c63787 CONNTRACK needs CAP_NET_ADMIN. 2021-08-05 23:40:04 +01:00
Simon Kelley 8389b943d3 Better fix than f2266d9678 2021-07-21 21:27:14 +01:00
Simon Kelley f2266d9678 Add UINT32_MAX if not defined by system. 2021-07-21 00:23:28 +01:00
Simon Kelley 56bd806978 Typo in new EDE code. 2021-07-21 00:15:58 +01:00
Simon Kelley ac7eeea44d Handle empty hostmaster in --auth-soa
Spotted by Max Julian Hofmann and the Advanced Research Team at CrowdStrike
2021-07-21 00:15:15 +01:00
Simon Kelley b741059549 Detect malformed --dhcp-relay option.
Spotted by Max Julian Hofmann and the Advanced Research Team at CrowdStrike
2021-07-20 23:49:38 +01:00
Simon Kelley cbd984287f Fix argument checking for --dhcp-match.
Spotted by Max Julian Hofmann and the Advanced Research Team at CrowdStrike
2021-07-20 23:45:36 +01:00
Simon Kelley 32e15c3f45 canonicalise_opt must always return heap memory.
Thanks to Max Julian Hofmann for spotting this.
2021-07-20 23:22:37 +01:00
Simon Kelley f0dc324e35 Checks on prefix-length in --domain --synth-domain and --rev-server. 2021-07-20 23:15:28 +01:00
Simon Kelley f83c6cf51a Return REFUSED in auth mode when we are not authoritative for the query. 2021-07-20 17:15:36 +01:00
Simon Kelley c068b3ae2f --synth-domain now works in auth mode. 2021-07-19 09:38:48 +01:00
Simon Kelley adf9dec1e6 Allow shorter IPv6 prefix lengths in (some) --synth-domain options. 2021-07-18 18:18:56 +01:00
Kevin Darbyshire-Bryant 767d9cbd96 Add --quiet-tftp. 2021-07-09 22:48:49 +01:00
Dominik DL6ER e7ccd95c04 Add EDE return when no matching key found. 2021-07-09 22:12:42 +01:00
Simon Kelley 719f79a8fd Subtle change to priority of --server types.
Make --server=/example.com/1.2.3.4 take priority over
--server=/example.com/ (AKA --address=/example.com/ or --local=/example.com/)

This corrects a regression in the domain-match rewrite, and appears
to be the more useful order. It got swapped because I didn't consider
that both could usefully co-exist.
2021-07-06 21:02:35 +01:00
Kevin Darbyshire-Bryant 96f6444958 Fix thinko in a92c6d77dc 2021-07-05 21:00:47 +01:00
Simon Kelley df25f204ba Fix logical error in d0ae3f5a4d
The code which checked for a possible local answer to a domain,
like --address=/example.com/1.2.3.4 could return false positives,
causing upstream NXDOMAIN replies to be rewritten as NOERROR.

Thanks to Dominik DL6ER for the bug report and analysis.
2021-07-05 20:56:11 +01:00
Simon Kelley 8acdc3ede7 Add calls to dump internally generated answers for dumpmask=0x0002 2021-07-04 23:12:14 +01:00
Simon Kelley 857b445522 Fix order of calls to resize-packet() and add_pseudoheader().
Avoids malformed replies with EDE in certain circumstances.
2021-07-04 22:38:26 +01:00
Simon Kelley 5bcca1219a Support IPv6 in --bogus-nxdomian and --ignore-address 2021-07-04 22:27:00 +01:00
Simon Kelley 4558c26fcd Make --rebind-localhost-ok apply to :: and 0.0.0.0
Also make the definition of local IPv6 addresses
the same for --bogus-priv and rebind protection.
2021-07-04 21:09:10 +01:00
Simon Kelley a92c6d77dc Tidy domain parsing, make --server=/*/1.2.3.4 equivalent to --server=1.2.3.4 2021-07-03 12:56:50 +01:00
Petr Menšík 0c95a5ff53 Modify and propagate changed lease.
If hostname is reset on existing lease, propagate such change to leases
file and script.
2021-07-02 16:58:48 +01:00
Simon Kelley cb6d06bb54 Rationalise SERV_MARK use. 2021-07-01 23:00:22 +01:00
Simon Kelley 3ef955c85a Fix oversight in build_server_array().
The index computation went awry when servers are disabled
by the loop-detection system.

Thanks to Xingcong Li for spotting this.
2021-07-01 22:40:31 +01:00
Simon Kelley 5e95c16c32 Allow wildcards in domain patterns.
Domain patterns in --address, --server and --local have, for many years,
matched complete labels only, so
--server=/google.com/1.2.3.4
will apply to google.com and www.google.com but NOT supergoogle.com

This commit introduces an optional '*' at the LHS of the domain string which
changes this behaviour so as to include substring matches _within_ labels. So,
--server=/*google.com/1.2.3.4
applies to google.com, www.google.com AND supergoogle.com.
2021-07-01 22:28:24 +01:00
Simon Kelley 4205e2ebcf Reuse workspace bit in struct server ->flags. 2021-07-01 13:22:10 +01:00
Etan Kissling 9d806c51c2 Fix ipset support.
This fixes a problem with ipset processing that got recently introduced
when `extract_request` filtering was tightened. During the recent change
an incorrect assumption was made that `extract_request` was only called
for requests but with ipset it is also called when processing responses.

The fix ensures that the new filters only apply to requests (QR=0 @ hdr)

Signed-off-by: Etan Kissling <etan.kissling@gmail.com>
2021-06-30 12:31:51 +01:00
Simon Kelley a38bb31727 Revert "Treat failure of ubus_add_object() in ubus_init() as retry-able."
This reverts commit 8a1ef367e2.
2021-06-30 12:30:15 +01:00
Simon Kelley 8a1ef367e2 Treat failure of ubus_add_object() in ubus_init() as retry-able.
3c93e8eb41 regularised ubus_init()
by avoiding logging calls (it can be called before logging is up)
but it instead returned any error from ubus_add_object() which
made such an error fatal. It turns out this is awkward, so this
patch returns NULL always, so that the event-loop will continue
attemping to connect to ubus forever.

This is not necessarily optimal either, and should be looked at
by a UBUS grown-up, but it does solve the immediate problem.
2021-06-27 21:32:10 +01:00
Simon Kelley 1291865c92 Fix trivial breakage of DBUS done by 85bc7534da 2021-06-27 21:16:30 +01:00
Simon Kelley a9ebbee7b6 Compiler warnings. 2021-06-27 21:03:52 +01:00
Simon Kelley 06df5ad7d0 Tidy up interface to dbus and ubus modules.
Consistently treat a non-NULL return from [ud]bus-init() as a fatal error:
either die() if still starting, or log an error and disable
the relevant module if dnsmasq has already started.

Also rationalise calls to set and check listeners depending on
configuration.
2021-06-27 20:56:58 +01:00
Simon Kelley 66b863c989 Fix problem with re-allocation of serverarray. 2021-06-26 21:13:41 +01:00
Simon Kelley c9efe8e5e1 Rationalise domain parsing for --rev-server and --domain. 2021-06-26 18:51:05 +01:00
Simon Kelley d515223bb5 Don't re-use datastructures for --address and --local.
Doing so makes the loading process quadratic, which is a problem
when there are a large number.
2021-06-26 01:00:37 +01:00
Simon Kelley b908f4334b Merge branch 'extended-error' 2021-06-26 00:38:55 +01:00
Simon Kelley 6261aba026 Initial implementation of RFC-8914 extended DNS errors. 2021-06-26 00:38:01 +01:00
Simon Kelley 85bc7534da Rationalise --server parsing and datastructure building.
Use add_update_server for everything.
2021-06-25 22:09:08 +01:00
Simon Kelley 1b30fd1732 Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq 2021-06-25 10:46:53 +01:00
Simon Kelley 8c9196bff8 Correct domain search algorithm.
For reasons unknown, I (srk) assumed that the orginal
substring domain matching algorithm was still in use,
where example.com would match eg. sexample.com

In fact the far more sensible label-based match, where
example.com (or .example.com) matches example.com and
www.example.com, but not sexample.com, has been in use
since release 2.22. This commit implements the 2.22 to 2.85
behaviour in the new domain-search code.

Thanks to Kevin Darbyshire-Bryant for spotting my mistake.
2021-06-25 10:46:06 +01:00
Simon Kelley b1daf44954 Correct domain search algorithm.
For reasons unknown, I (srk) assumed that the orginal
substring domain matching algorithm was still in use,
where example.comKevin Darbyshire-Bryant <kevin@darbyshire-bryant.me.uk> would match eg. sexample.com

In fact the far more sensible label-based match, where
example.com (or .example.com) matches example.com and
www.example.com, but not sexample.com, has been in use
since release 2.22. This commit implements the 2.22 to 2.85
behaviour in the new domain-search code.

Thanks to Kevin Darbyshire-Bryant for spotting my mistake.
2021-06-24 23:28:47 +01:00
Simon Kelley 11c52d032b Initial changes for extended DNS error codes. 2021-06-21 17:37:46 +01:00
Simon Kelley be291d979d Include EDNS0 in connmark REFUSED replies. 2021-06-21 16:59:42 +01:00
Simon Kelley 6d1edd8d32 Use correct packet-size limit in make_local_answer() 2021-06-21 15:59:07 +01:00
Simon Kelley 25ff956c7d Tidy up name buffer use in report_addresses().
Buffer may need to be twice MAXDNAME is escaping is
enabled in extract_name. The name may include weird characters.
2021-06-21 15:05:28 +01:00
Simon Kelley 38179500f8 CHANGELOG entry for new connmark code. 2021-06-21 14:35:36 +01:00
Simon Kelley 5f7be5f0d6 Fix compiler warning. 2021-06-21 14:31:54 +01:00
Etan Kissling 627056febb Connection track mark based DNS query filtering.
This extends query filtering support beyond what is currently possible
with the `--ipset` configuration option, by adding support for:
1) Specifying allowlists on a per-client basis, based on their
   associated Linux connection track mark.
2) Dynamic configuration of allowlists via Ubus.
3) Reporting when a DNS query resolves or is rejected via Ubus.
4) DNS name patterns containing wildcards.

Disallowed queries are not forwarded; they are rejected
with a REFUSED error code.

Signed-off-by: Etan Kissling <etan_kissling@apple.com>
(addressed reviewer feedback)
Signed-off-by: Etan Kissling <etan.kissling@gmail.com>
2021-06-21 14:14:55 +01:00
Simon Kelley cbd76447fd Further work from a0a3b8ad3e
When query longer than longest domain, crop directly to length
of longest domain.
2021-06-21 00:01:51 +01:00
Simon Kelley a60a233329 Fix bug introduced in 6860cf932b
Breakage 0f --no-rebind-domain due to incomplete edit.

Thanks to Kevin Darbyshire-Bryant for spotting this.
2021-06-20 23:02:54 +01:00
Simon Kelley a0a3b8ad3e Fix bug in 6860cf932b
The optimisation based on the assumption that
"try now points to the last domain that sorts before the query"
fails in the specific edge case that the query sorts before
_any_ of the server domains. Handle this case.

Thanks to Xingcong Li for finding a test case for this bug.
2021-06-20 22:57:54 +01:00
Simon Kelley d0ae3f5a4d Fix specific NOERR/NXDOMAIN confusion.
In the specific case of configuring an A record for a domain

address=/example.com/1.2.3.4

queries for *example.com for any other type will now return
NOERR, and not the previous erroneous NXDOMAIN. The same thing
applies for

address=/example.com/::1:2:3:4
address=/example.com/#
2021-06-17 23:11:17 +01:00
Simon Kelley 6860cf932b Optimise lokkup_domain() 2021-06-17 21:30:40 +01:00
Simon Kelley 0276e0805b merge development machines.
Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq
2021-06-16 14:05:49 +01:00
Simon Kelley 06ff3d8a26 Log the correct name when we retry a DNSSEC query.
If we retry a DNSSEC query because our client retries on us, and
we have an answer but are waiting on a DNSSEC query to validate it,
log the name of the DNSSEC query, not the client's query.
2021-06-16 13:59:57 +01:00
Simon Kelley 1a3b69aa56 Fix error in new domain-search code.
SERV_USE_RESOLV set implies struct serv_local,
so don't can't set ->arrayposn

Thanks to Xingcong Li for the cod review which led to this.
2021-06-16 09:57:41 +01:00
Simon Kelley 8237d06ab7 Typo in FAQ.
Reported by Alexander Traud.
2021-06-15 23:14:59 +01:00
Simon Kelley 1c9f136b57 Man page update, lease times can be given in days or weeks. 2021-06-15 22:07:59 +01:00
Simon Kelley 5ab7e4a475 Improve efficiency of DNSSEC.
The sharing point for DNSSEC RR data used to be when it entered the
cache, having been validated. After that queries requiring the KEY or
DS records would share the cached values. There is a common case in
dual-stack hosts that queries for A and AAAA records for the same
domain are made simultaneously.  If required keys were not in the
cache, this would result in two requests being sent upstream for the
same key data (and all the subsequent chain-of-trust queries.) Now we
combine these requests and elide the duplicates, resulting in fewer
queries upstream and better performance. To keep a better handle on
what's going on, the "extra" logging mode has been modified to
associate queries and answers for DNSSEC queries in the same way as
ordinary queries. The requesting address and port have been removed
from DNSSEC logging lines, since this is no longer strictly defined.
2021-06-15 15:27:29 +01:00
Simon Kelley 3236f358f8 Revise resource handling for number of concurrent DNS queries.
This used to have a global limit, but that has a problem when using
different servers for different upstream domains. Queries which are
routed by domain to an upstream server which is not responding will
build up and trigger the limit, which breaks DNS service for all other
domains which could be handled by other servers. The change is to make
the limit per server-group, where a server group is the set of servers
configured for a particular domain. In the common case, where only
default servers are declared, there is no effective change.
2021-06-13 21:29:22 +01:00
Simon Kelley 4a6550d69a Move make_local_answer() to domain-match.c 2021-06-10 21:40:52 +01:00
Simon Kelley ff523d0c67 Fix TCP replies with --domain-needed. 2021-06-10 21:31:38 +01:00
Simon Kelley 3c93e8eb41 Re-order UBus initialisation to avoid logging before logs set up. 2021-06-08 23:13:48 +01:00
57 changed files with 10968 additions and 7528 deletions

No files matched your search

+73 -2
View File
@@ -1,6 +1,17 @@
version 2.87
Allow arbitrary prefix lengths in --rev-server and
--domain=....,local
Replace --address=/#/..... functionality which got
missed in the 2.86 domain search rewrite.
Add --nftset option, like --ipset but for the newer nftables.
Thanks to Chen Zhenge for the patch.
version 2.86
Handle DHCPREBIND requests in the DHCPv6 server code.
Thanks to Aichun Li for spotting this ommision, and the initial
Thanks to Aichun Li for spotting this omission, and the initial
patch.
Fix bug which caused dnsmasq to lose track of processes forked
@@ -33,12 +44,69 @@ version 2.86
address=/example.com/1.2.3.4
address=/example.com/5.6.7.8
It also handles multiple upstream servers for a domain better; using
the same try/retry alogrithms as non domain-specific servers. This
the same try/retry algorithms as non domain-specific servers. This
also applies to DNSSEC-generated queries.
Finally, some of the oldest and gnarliest code in dnsmasq has had
a significant clean-up. It's far from perfect, but it _is_ better.
Revise resource handling for number of concurrent DNS queries. This
used to have a global limit, but that has a problem when using
different servers for different upstream domains. Queries which are
routed by domain to an upstream server which is not responding will
build up and trigger the limit, which breaks DNS service for
all other domains which could be handled by other servers. The
change is to make the limit per server-group, where a server group
is the set of servers configured for a particular domain. In the
common case, where only default servers are declared, there is
no effective change.
Improve efficiency of DNSSEC. The sharing point for DNSSEC RR data
used to be when it entered the cache, having been validated. After
that queries requiring the KEY or DS records would share the cached
values. There is a common case in dual-stack hosts that queries for
A and AAAA records for the same domain are made simultaneously.
If required keys were not in the cache, this would result in two
requests being sent upstream for the same key data (and all the
subsequent chain-of-trust queries.) Now we combine these requests
and elide the duplicates, resulting in fewer queries upstream
and better performance. To keep a better handle on what's
going on, the "extra" logging mode has been modified to associate
queries and answers for DNSSEC queries in the same way as ordinary
queries. The requesting address and port have been removed from
DNSSEC logging lines, since this is no longer strictly defined.
Connection track mark based DNS query filtering. Thanks to
Etan Kissling for implementing this It extends query filtering
support beyond what is currently possible
with the `--ipset` configuration option, by adding support for:
1) Specifying allowlists on a per-client basis, based on their
associated Linux connection track mark.
2) Dynamic configuration of allowlists via Ubus.
3) Reporting when a DNS query resolves or is rejected via Ubus.
4) DNS name patterns containing wildcards.
Disallowed queries are not forwarded; they are rejected
with a REFUSED error code.
Allow smaller than 64 prefix lengths in synth-domain, with caveats.
--synth-domain=1234:4567::/56,example.com is now valid.
Make domains generated by --synth-domain appear in replies
when in authoritative mode.
Ensure CAP_NET_ADMIN capability is available when
conntrack is configured. Thanks to Yick Xie for spotting
the lack of this.
When --dhcp-hostsfile --dhcp-optsfile and --addn-hosts are
given a directory as argument, define the order in which
files within that directory are read (alphabetical order
of filename). Thanks to Ed Wildgoose for the initial patch
and motivation for this.
Allow adding IP address to nftables set in addition to
ipset.
version 2.85
Fix problem with DNS retries in 2.83/2.84.
The new logic in 2.83/2.84 which merges distinct requests
@@ -109,6 +177,9 @@ version 2.85
Tweak TFTP code to check sender of all received packets, as
specified in RFC 1350 para 4.
Support some wildcard matching of input tags to --tag-if.
Thanks to Geoff Back for the idea and the patch.
version 2.84
Fix a problem, introduced in 2.83, which could see DNS replies
+1 -1
View File
@@ -236,7 +236,7 @@ Q: What network types are supported by the DHCP server?
A: Ethernet (and 802.11 wireless) are supported on all platforms. On
Linux all network types (including FireWire) are supported.
Q: What are these strange "bind-interface" and "bind-dynamic" options?
Q: What are these strange "bind-interfaces" and "bind-dynamic" options?
A: Dnsmasq from v2.63 can operate in one of three different "networking
modes". This is unfortunate as it requires users configuring dnsmasq
+9 -7
View File
@@ -70,7 +70,9 @@ nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CO
HAVE_NETTLEHASH $(PKG_CONFIG) --libs nettle`
gmp_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --copy -lgmp`
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
nft_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_NFTSET $(PKG_CONFIG) --cflags libnftables`
nft_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_NFTSET $(PKG_CONFIG) --libs libnftables`
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
sum?=$(shell $(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ')
sum!=$(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' '
@@ -79,10 +81,10 @@ copts_conf = .copts_$(sum)
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o pattern.o \
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o \
metrics.o hash-questions.o domain-match.o
metrics.o hash-questions.o domain-match.o nftset.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h ip6addr.h metrics.h
@@ -90,8 +92,8 @@ hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
all : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs)" \
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags) $(nft_cflags)" \
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs) $(nft_libs)" \
-f $(top)/Makefile dnsmasq
mostly_clean :
@@ -115,8 +117,8 @@ all-i18n : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \
i18n=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs)" \
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags) $(nft_cflags)" \
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs) $(nft_libs)" \
-f $(top)/Makefile dnsmasq
for f in `cd $(PO); echo *.po`; do \
cd $(top) && cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/Makefile $${f%.po}.mo; \
-6
View File
@@ -1,6 +0,0 @@
This packaging is now unmaintained in the dnsmasq source: dnsmasq is
included in Suse proper, and up-to-date packages are now available
from
ftp://ftp.suse.com/pub/people/ug/
-27
View File
@@ -1,27 +0,0 @@
This is a patch against SuSEfirewall2-3.1-206 (SuSE 9.x and older)
It fixes the dependency from the dns daemon name 'named'
After appending the patch, the SuSEfirewall is again able to autodetect
the dnsmasq named service.
This is a very old bug in the SuSEfirewall script.
The SuSE people think the name of the dns server will always 'named'
--- /sbin/SuSEfirewall2.orig 2004-01-23 13:30:09.000000000 +0100
+++ /sbin/SuSEfirewall2 2004-01-23 13:31:56.000000000 +0100
@@ -764,7 +764,7 @@
echo 'FW_ALLOW_INCOMING_HIGHPORTS_UDP should be set to yes, if you are running a DNS server!'
test "$FW_SERVICE_AUTODETECT" = yes -o "$FW_SERVICE_AUTODETECT" = dmz -o "$FW_SERVICE_AUTODETECT" = ext && {
- test "$FW_SERVICE_DNS" = no -a '!' "$START_NAMED" = no && check_srv named && {
+ test "$FW_SERVICE_DNS" = no -a '!' "$START_NAMED" = no && check_srv dnsmasq && {
echo -e 'Warning: detected activated named, enabling FW_SERVICE_DNS!
You still have to allow tcp/udp port 53 on internal, dmz and/or external.'
FW_SERVICE_DNS=$FW_SERVICE_AUTODETECT
@@ -878,7 +878,7 @@
test -e /etc/resolv.conf || echo "Warning: /etc/resolv.conf not found"
# Get ports/IP bindings of NAMED/SQUID
test "$FW_SERVICE_DNS" = yes -o "$FW_SERVICE_DNS" = dmz -o "$FW_SERVICE_DNS" = ext -o "$START_NAMED" = yes && DNS_PORT=`$LSOF -i -n -P | \
- $AWK -F: '/^named .* UDP / {print $2}'| $GREP -vw 53 | $SORT -un`
+ $AWK -F: '/^dnsmasq .* UDP / {print $2}'| $GREP -vw 53 | $SORT -un`
test "$FW_SERVICE_SQUID" = yes -o "$FW_SERVICE_SQUID" = dmz -o "$FW_SERVICE_SQUID" = ext -o "$START_SQUID" = yes && SQUID_PORT=`$LSOF -i -n -P | \
$AWK -F: '/^squid .* UDP/ {print $2}'| $SORT -un`
-23
View File
@@ -1,23 +0,0 @@
--- man/dnsmasq.8 2004-08-08 20:57:56.000000000 +0200
+++ man/dnsmasq.8 2004-08-12 00:40:01.000000000 +0200
@@ -69,7 +69,7 @@
.TP
.B \-g, --group=<groupname>
Specify the group which dnsmasq will run
-as. The defaults to "dip", if available, to facilitate access to
+as. The defaults to "dialout", if available, to facilitate access to
/etc/ppp/resolv.conf which is not normally world readable.
.TP
.B \-v, --version
--- src/config.h 2004-08-11 11:39:18.000000000 +0200
+++ src/config.h 2004-08-12 00:40:01.000000000 +0200
@@ -44,7 +44,7 @@
#endif
#define DEFLEASE 3600 /* default lease time, 1 hour */
#define CHUSER "nobody"
-#define CHGRP "dip"
+#define CHGRP "dialout"
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
-111
View File
@@ -1,111 +0,0 @@
###############################################################################
#
# General
#
###############################################################################
Name: dnsmasq
Version: 2.33
Release: 1
Copyright: GPL
Group: Productivity/Networking/DNS/Servers
Vendor: Simon Kelley
Packager: Simon Kelley
URL: http://www.thekelleys.org.uk/dnsmasq
Provides: dns_daemon
Conflicts: bind bind8 bind9
PreReq: %fillup_prereq %insserv_prereq
Autoreqprov: on
Source0: %{name}-%{version}.tar.bz2
BuildRoot: /var/tmp/%{name}-%{version}
Summary: A lightweight caching nameserver
%description
Dnsmasq is lightweight, easy to configure DNS forwarder and DHCP server. It
is designed to provide DNS and, optionally, DHCP, to a small network. It can
serve the names of local machines which are not in the global DNS. The DHCP
server integrates with the DNS server and allows machines with DHCP-allocated
addresses to appear in the DNS with names configured either in each host or
in a central configuration file. Dnsmasq supports static and dynamic DHCP
leases and BOOTP for network booting of diskless machines.
###############################################################################
#
# Build
#
###############################################################################
%prep
%setup -q
patch -p0 <rpm/%{name}-SuSE.patch
%build
%{?suse_update_config:%{suse_update_config -f}}
make all-i18n DESTDIR=$RPM_BUILD_ROOT PREFIX=/usr
###############################################################################
#
# Install
#
###############################################################################
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p ${RPM_BUILD_ROOT}/etc/init.d
make install-i18n DESTDIR=$RPM_BUILD_ROOT PREFIX=/usr
install -o root -g root -m 755 rpm/rc.dnsmasq-suse $RPM_BUILD_ROOT/etc/init.d/dnsmasq
install -o root -g root -m 644 dnsmasq.conf.example $RPM_BUILD_ROOT/etc/dnsmasq.conf
strip $RPM_BUILD_ROOT/usr/sbin/dnsmasq
ln -sf ../../etc/init.d/dnsmasq $RPM_BUILD_ROOT/usr/sbin/rcdnsmasq
###############################################################################
#
# Clean up
#
###############################################################################
%clean
rm -rf $RPM_BUILD_ROOT
###############################################################################
#
# Post-install scriptlet
#
###############################################################################
%post
%{fillup_and_insserv dnsmasq}
###############################################################################
#
# Post-uninstall scriptlet
#
# The %postun script executes after the package has been removed. It is the
# last chance for a package to clean up after itself.
#
###############################################################################
%postun
%{insserv_cleanup}
###############################################################################
#
# File list
#
###############################################################################
%files
%defattr(-,root,root)
%doc CHANGELOG COPYING FAQ doc.html setup.html UPGRADING_to_2.0 rpm/README.susefirewall
%doc contrib
%config /etc/init.d/dnsmasq
%config /etc/dnsmasq.conf
/usr/sbin/rcdnsmasq
/usr/sbin/dnsmasq
/usr/share/locale/*/LC_MESSAGES/*
%doc %{_mandir}/man8/dnsmasq.8.gz
%doc %{_mandir}/*/man8/dnsmasq.8.gz
-79
View File
@@ -1,79 +0,0 @@
#! /bin/sh
#
# init.d/dnsmasq
#
### BEGIN INIT INFO
# Provides: dnsmasq
# Required-Start: $network $remote_fs $syslog
# Required-Stop:
# Default-Start: 3 5
# Default-Stop:
# Description: Starts internet name service masq caching server (DNS)
### END INIT INFO
NAMED_BIN=/usr/sbin/dnsmasq
NAMED_PID=/var/run/dnsmasq.pid
NAMED_CONF=/etc/dnsmasq.conf
if [ ! -x $NAMED_BIN ] ; then
echo -n "dnsmasq not installed ! "
exit 5
fi
. /etc/rc.status
rc_reset
case "$1" in
start)
echo -n "Starting name service masq caching server "
checkproc -p $NAMED_PID $NAMED_BIN
if [ $? -eq 0 ] ; then
echo -n "- Warning: dnsmasq already running ! "
else
[ -e $NAMED_PID ] && echo -n "- Warning: $NAMED_PID exists ! "
fi
startproc -p $NAMED_PID $NAMED_BIN -u nobody
rc_status -v
;;
stop)
echo -n "Shutting name service masq caching server "
checkproc -p $NAMED_PID $NAMED_BIN
[ $? -ne 0 ] && echo -n "- Warning: dnsmasq not running ! "
killproc -p $NAMED_PID -TERM $NAMED_BIN
rc_status -v
;;
try-restart)
$0 stop && $0 start
rc_status
;;
restart)
$0 stop
$0 start
rc_status
;;
force-reload)
$0 reload
rc_status
;;
reload)
echo -n "Reloading name service masq caching server "
checkproc -p $NAMED_PID $NAMED_BIN
[ $? -ne 0 ] && echo -n "- Warning: dnsmasq not running ! "
killproc -p $NAMED_PID -HUP $NAMED_BIN
rc_status -v
;;
status)
echo -n "Checking for name service masq caching server "
checkproc -p $NAMED_PID $NAMED_BIN
rc_status -v
;;
probe)
test $NAMED_CONF -nt $NAMED_PID && echo reload
;;
*)
echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}"
exit 1
;;
esac
rc_exit
+7 -3
View File
@@ -153,7 +153,11 @@ int main(int argc, char **argv)
exit(1);
}
lease.s_addr = inet_addr(argv[1]);
if (inet_pton(AF_INET, argv[1], &lease) < 1)
{
fprintf(stderr, "invalid address: %s\n", argv[1]);
exit(1);
}
memset(&packet, 0, sizeof(packet));
@@ -176,8 +180,8 @@ int main(int argc, char **argv)
*(p++) = OPTION_END;
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("127.0.0.1");
dest.sin_family = AF_INET;
(void)inet_pton(AF_INET, "127.0.0.1", &dest.sin_addr);
dest.sin_port = ntohs(DHCP_SERVER_PORT);
if (sendto(fd, &packet, sizeof(packet), 0,
+2 -2
View File
@@ -280,6 +280,7 @@ int main(int argc, char **argv)
/* This voodoo fakes up a packet coming from the correct interface, which really matters for
a DHCP server */
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name)-1);
ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
@@ -288,13 +289,12 @@ int main(int argc, char **argv)
exit(1);
}
if (inet_addr(argv[2]) == INADDR_NONE)
if (inet_pton(AF_INET, argv[2], &lease.s_addr) < 1)
{
perror("invalid ip address");
exit(1);
}
lease.s_addr = inet_addr(argv[2]);
server = find_interface(lease, nl, if_nametoindex(argv[1]), fd, &ifr);
memset(&packet, 0, sizeof(packet));
+23 -14
View File
@@ -318,6 +318,12 @@ void usage(const char* arg, FILE* stream)
fprintf (stream, "Usage: %s %s\n", arg, usage_string);
}
static void fail_fatal(const char *errstr, int exitcode)
{
perror(errstr);
exit(exitcode);
}
int send_release_packet(const char* iface, struct dhcp6_packet* packet)
{
struct sockaddr_in6 server_addr, client_addr;
@@ -343,18 +349,19 @@ int send_release_packet(const char* iface, struct dhcp6_packet* packet)
client_addr.sin6_port = htons(DHCP6_CLIENT_PORT);
client_addr.sin6_flowinfo = 0;
client_addr.sin6_scope_id =0;
inet_pton(AF_INET6, "::", &client_addr.sin6_addr);
bind(sock, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in6));
inet_pton(AF_INET6, DHCP6_MULTICAST_ADDRESS, &server_addr.sin6_addr);
if (inet_pton(AF_INET6, "::", &client_addr.sin6_addr) <= 0)
fail_fatal("inet_pton", 5);
if (bind(sock, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in6)) != 0)
perror("bind"); /* continue on bind error */
if (inet_pton(AF_INET6, DHCP6_MULTICAST_ADDRESS, &server_addr.sin6_addr) <= 0)
fail_fatal("inet_pton", 5);
server_addr.sin6_port = htons(DHCP6_SERVER_PORT);
int16_t recv_size = 0;
ssize_t recv_size = 0;
int result;
for (i = 0; i < 5; i++)
{
if (sendto(sock, packet->buf, packet->len, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("sendto failed");
exit(4);
}
fail_fatal("sendto failed", 4);
recv_size = recvfrom(sock, response, sizeof(response), MSG_DONTWAIT, NULL, 0);
if (recv_size == -1)
@@ -367,16 +374,18 @@ int send_release_packet(const char* iface, struct dhcp6_packet* packet)
else
{
perror("recvfrom");
result = UNSPEC_FAIL;
}
}
int16_t result = parse_packet(response, recv_size);
if (result == NOT_REPLY_CODE)
else
{
sleep(1);
continue;
result = parse_packet(response, recv_size);
if (result == NOT_REPLY_CODE)
{
sleep(1);
continue;
}
}
close(sock);
return result;
}
+7
View File
@@ -1,3 +1,10 @@
dnsmasq (2.87-1) unstable; urgency=low
* New upstream.
* Include new NFTset support in the build.
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 08 Sep 2021 23:11:25 +0000
dnsmasq (2.86-1) unstable; urgency=low
* Fix debian/changelog format error. (closes: #986626)
+1 -1
View File
@@ -5,7 +5,7 @@ Build-depends: gettext, libnetfilter-conntrack-dev [linux-any],
libidn2-dev, libdbus-1-dev (>=0.61), libgmp-dev,
nettle-dev (>=2.4-3), libbsd-dev [kfreebsd-any],
liblua5.2-dev, dh-runit, debhelper-compat (= 10),
pkg-config
pkg-config, libnftables-dev
Maintainer: Simon Kelley <simon@thekelleys.org.uk>
Homepage: http://www.thekelleys.org.uk/dnsmasq/doc.html
Vcs-Git: http://thekelleys.org.uk/git/dnsmasq.git
+1
View File
@@ -60,6 +60,7 @@ Notes on configuring dnsmasq as packaged for Debian.
nodbus : omit DBus support.
noconntrack : omit connection tracking support.
noipset : omit IPset support.
nonftset : omit nftset support.
nortc : compile alternate mode suitable for systems without an RTC.
noi18n : omit translations and internationalisation support.
noidn : omit international domain name support, must be
+4
View File
@@ -52,6 +52,10 @@ ifeq (,$(filter noidn, $(DEB_BUILD_OPTIONS)))
DEB_COPTS += -DHAVE_LIBIDN2
endif
ifeq (,$(filter nonftset, $(DEB_BUILD_OPTIONS)))
DEB_COPTS += -DHAVE_NFTSET
endif
ifeq (,$(filter noconntrack,$(DEB_BUILD_OPTIONS)))
ifeq ($(DEB_HOST_ARCH_OS),linux)
DEB_COPTS += -DHAVE_CONNTRACK
+10
View File
@@ -85,6 +85,16 @@
# subdomains to the vpn and search ipsets:
#ipset=/yahoo.com/google.com/vpn,search
# Add the IPs of all queries to yahoo.com, google.com, and their
# subdomains to netfilters sets, which is equivalent to
# 'nft add element ip test vpn { ... }; nft add element ip test search { ... }'
#nftset=/yahoo.com/google.com/ip#test#vpn,ip#test#search
# Use netfilters sets for both IPv4 and IPv6:
# This adds all addresses in *.yahoo.com to vpn4 and vpn6 for IPv4 and IPv6 addresses.
#nftset=/yahoo.com/4#ip#test#vpn4
#nftset=/yahoo.com/6#ip#test#vpn6
# You can control how dnsmasq talks to a server: this forces
# queries to 10.1.2.3 to be routed via eth1
# server=10.1.2.3@eth1
+89 -26
View File
@@ -1,4 +1,4 @@
.TH DNSMASQ 8 2020-04-05
.TH DNSMASQ 8 2021-08-16
.SH NAME
dnsmasq \- A lightweight DHCP and caching DNS server.
.SH SYNOPSIS
@@ -55,7 +55,8 @@ Don't read the hostnames in /etc/hosts.
.B \-H, --addn-hosts=<file>
Additional hosts file. Read the specified file as well as /etc/hosts. If \fB--no-hosts\fP is given, read
only the specified file. This option may be repeated for more than one
additional hosts file. If a directory is given, then read all the files contained in that directory.
additional hosts file. If a directory is given, then read all the files contained in that directory
in alphabetical order.
.TP
.B --hostsdir=<path>
Read all the hosts files contained in the directory. New or changed files
@@ -327,8 +328,8 @@ are re-written. So
maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40
.TP
.B \-B, --bogus-nxdomain=<ipaddr>[/prefix]
Transform replies which contain the IP specified address or subnet into "No such
domain" replies. This is intended to counteract a devious move made by
Transform replies which contain the specified address or subnet into "No such
domain" replies. IPv4 and IPv6 are supported. This is intended to counteract a devious move made by
Verisign in September 2003 when they started returning the address of
an advertising web page in response to queries for unregistered names,
instead of the correct NXDOMAIN response. This option tells dnsmasq to
@@ -336,7 +337,7 @@ fake the correct response when it sees this behaviour. As at Sept 2003
the IP address being returned by Verisign is 64.94.110.11
.TP
.B --ignore-address=<ipaddr>[/prefix]
Ignore replies to A-record queries which include the specified address or subnet.
Ignore replies to A or AAAA queries which include the specified address or subnet.
No error is generated, dnsmasq simply continues to listen for another reply.
This is useful to defeat blocking strategies which rely on quickly supplying a
forged answer to a DNS request for certain domain, before the correct answer can arrive.
@@ -371,7 +372,10 @@ provides service at that name, rather than the default which is
.TP
.B --enable-ubus[=<service-name>]
Enable dnsmasq UBus interface. It sends notifications via UBus on
DHCPACK and DHCPRELEASE events. Furthermore it offers metrics.
DHCPACK and DHCPRELEASE events. Furthermore it offers metrics
and allows configuration of Linux connection track mark based filtering.
When DNS query filtering based on Linux connection track marks is enabled
UBus notifications are generated for each resolved or filtered DNS query.
Requires that dnsmasq has been built with UBus support. If the service
name is given, dnsmasq provides service at that namespace, rather than
the default which is
@@ -457,13 +461,22 @@ repeated domain or ipaddr parts as required.
More specific domains take precedence over less specific domains, so:
.B --server=/google.com/1.2.3.4
.B --server=/www.google.com/2.3.4.5
will send queries for *.google.com to 1.2.3.4, except *www.google.com,
which will go to 2.3.4.5
will send queries for google.com and gmail.google.com to 1.2.3.4, but www.google.com
will go to 2.3.4.5
Matching of domains is normally done on complete labels, so /google.com/ matches google.com and www.google.com
but NOT supergoogle.com. This can be overridden with a * at the start of a pattern only: /*google.com/
will match google.com and www.google.com AND supergoogle.com. The non-wildcard form has priority, so
if /google.com/ and /*google.com/ are both specified then google.com and www.google.com will match /google.com/
and /*google.com/ will only match supergoogle.com.
For historical reasons, the pattern /.google.com/ is equivalent to /google.com/ if you wish to match any subdomain
of google.com but NOT google.com itself, use /*.google.com/
The special server address '#' means, "use the standard servers", so
.B --server=/google.com/1.2.3.4
.B --server=/www.google.com/#
will send queries for *.google.com to 1.2.3.4, except *www.google.com which will
will send queries for google.com and its subdomains to 1.2.3.4, except www.google.com (and its subdomains) which will
be forwarded as usual.
Also permitted is a -S
@@ -492,21 +505,20 @@ source address specified but the port may be specified directly as
part of the source address. Forcing queries to an interface is not
implemented on all platforms supported by dnsmasq.
.TP
.B --rev-server=<ip-address>/<prefix-len>[,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
.B --rev-server=<ip-address>[/<prefix-len>][,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
This is functionally the same as
.B --server,
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
.B --rev-server=1.2.3.0/24,192.168.0.1
is exactly equivalent to
.B --server=/3.2.1.in-addr.arpa/192.168.0.1
Allowed prefix lengths are 1-32 (IPv4) and 1-128 (IPv6). If the prefix length is omitted, dnsmasq substitutes either 32 (IPv4) or 128 (IPv6).
.TP
.B \-A, --address=/<domain>[/<domain>...]/[<ipaddr>]
Specify an IP address to return for any host in the given domains.
Queries in the domains are never forwarded and always replied to
with the specified IP address which may be IPv4 or IPv6. To give
both IPv4 and IPv6 addresses for a domain, use repeated \fB--address\fP flags.
To include multiple IP addresses for a single query, use
\fB--addn-hosts=<path>\fP instead.
multiple addresses or both IPv4 and IPv6 addresses for a domain, use repeated \fB--address\fP flags.
Note that /etc/hosts and DHCP leases override this for individual
names. A common use of this is to redirect the entire doubleclick.net
domain to some friendly local web server to avoid banner ads. The
@@ -536,6 +548,41 @@ These IP sets must already exist. See
.BR ipset (8)
for more details.
.TP
.B --nftset=/<domain>[/<domain>...]/[(6|4)#[<family>#]<table>#<set>[,[(6|4)#[<family>#]<table>#<set>]...]
Similar to the \fB--ipset\fP option, but accepts one or more nftables
sets to add IP addresses into.
These sets must already exist. See
.BR nft (8)
for more details. The family, table and set are passed directly to the nft. If the spec starts with 4# or 6# then
only A or AAAA records respectively are added to the set. Since an nftset can hold only IPv4 or IPv6 addresses, this
avoids errors being logged for addresses of the wrong type.
.TP
.B --connmark-allowlist-enable[=<mask>]
Enables filtering of incoming DNS queries with associated Linux connection track marks
according to individual allowlists configured via a series of \fB--connmark-allowlist\fP
options. Disallowed queries are not forwarded; they are rejected with a REFUSED error code.
DNS queries are only allowed if they do not have an associated Linux connection
track mark, or if the queried domains match the configured DNS patterns for the
associated Linux connection track mark. If no allowlist is configured for a
Linux connection track mark, all DNS queries associated with that mark are rejected.
If a mask is specified, Linux connection track marks are first bitwise ANDed
with the given mask before being processed.
.TP
.B --connmark-allowlist=<connmark>[/<mask>][,<pattern>[/<pattern>...]]
Configures the DNS patterns that are allowed in DNS queries associated with
the given Linux connection track mark.
If a mask is specified, Linux connection track marks are first bitwise ANDed
with the given mask before they are compared to the given connection track mark.
Patterns follow the syntax of DNS names, but additionally allow the wildcard
character "*" to be used up to twice per label to match 0 or more characters
within that label. Note that the wildcard never matches a dot (e.g., "*.example.com"
matches "api.example.com" but not "api.us.example.com"). Patterns must be
fully qualified, i.e., consist of at least two labels. The final label must not be
fully numeric, and must not be the "local" pseudo-TLD. A pattern must end with at least
two literal (non-wildcard) labels.
Instead of a pattern, "*" can be specified to disable allowlist filtering
for a given Linux connection track mark entirely.
.TP
.B \-m, --mx-host=<mx name>[[,<hostname>],<preference>]
Return an MX record named <mx name> pointing to the given hostname (if
given), or
@@ -667,7 +714,8 @@ configured a zero is added in front of the label. ::1 becomes 0--1.
V4 mapped IPv6 addresses, which have a representation like ::ffff:1.2.3.4 are handled specially, and become like 0--ffff-1-2-3-4
The address range can be of the form
<ip address>,<ip address> or <ip address>/<netmask> in both forms of the option.
<start address>,<end address> or <ip address>/<prefix-length> in both forms of the option. For IPv6 the start and end addresses
must fall in the same /64 network, or prefix-length must be greater than or equal to 64 except that shorter prefix lengths than 64 are allowed only if non-sequential names are in use.
.TP
.B --dumpfile=<path/to/file>
Specify the location of a pcap-format file which dnsmasq uses to dump copies of network packets for debugging purposes. If the file exists when dnsmasq starts, it is not deleted; new packets are added to the end.
@@ -731,7 +779,8 @@ identical queries without forwarding them again.
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.
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.
.TP
.B --dnssec
Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, dnsmasq requests the
@@ -865,7 +914,7 @@ compiled in and the kernel must have conntrack support
included and configured. This option cannot be combined with
.B --query-port.
.TP
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-addr>[,<end-addr>|<mode>][,<netmask>[,<broadcast>]][,<lease time>]
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-addr>[,<end-addr>|<mode>[,<netmask>[,<broadcast>]]][,<lease time>]
.TP
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-IPv6addr>[,<end-IPv6addr>|constructor:<interface>][,<mode>][,<prefix-len>][,<lease time>]
@@ -875,7 +924,7 @@ in
.B --dhcp-host
options. If the lease time is given, then leases
will be given for that length of time. The lease time is in seconds,
or minutes (eg 45m) or hours (eg 1h) or "infinite". If not given,
or minutes (eg 45m) or hours (eg 1h) or days (2d) or weeks (1w) or "infinite". If not given,
the default lease time is one hour for IPv4 and one day for IPv6. The
minimum lease time is two minutes. For IPv6 ranges, the lease time
maybe "deprecated"; this sets the preferred lifetime sent in a DHCP
@@ -995,7 +1044,7 @@ is also included, as described in RFC-3775 section 7.3.
tells dnsmasq to advertise the prefix without the on-link (aka L) bit set.
.TP
.B \-G, --dhcp-host=[<hwaddr>][,id:<client_id>|*][,set:<tag>][tag:<tag>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
.B \-G, --dhcp-host=[<hwaddr>][,id:<client_id>|*][,set:<tag>][,tag:<tag>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
Specify per host parameters for the DHCP server. This allows a machine
with a particular hardware address to be always allocated the same
hostname, IP address and lease time. A hostname specified like this
@@ -1094,7 +1143,10 @@ ignore requests from unknown machines using
If the host matches only a \fB--dhcp-host\fP directive which cannot
be used because it specifies an address on different subnet, the tag "known-othernet" is set.
The tag:<tag> construct filters which dhcp-host directives are used. Tagged directives are used in preference to untagged ones.
The tag:<tag> construct filters which dhcp-host directives are used; more than
one can be provided, in this case the request must match all of them. Tagged
directives are used in preference to untagged ones. Note that one of <hwaddr>,
<client_id> or <hostname> still needs to be specified (can be a wildcard).
Ethernet addresses (but not client-ids) may have
wildcard bytes, so for example
@@ -1125,7 +1177,7 @@ has both wired and wireless interfaces.
.TP
.B --dhcp-hostsfile=<path>
Read DHCP host information from the specified file. If a directory
is given, then read all the files contained in that directory. The file contains
is given, then read all the files contained in that directory in alphabetical order. The file contains
information about one host per line. The format of a line is the same
as text to the right of '=' in \fB--dhcp-host\fP. The advantage of storing DHCP host information
in this file is that it can be changed without re-starting dnsmasq:
@@ -1133,7 +1185,7 @@ the file will be re-read when dnsmasq receives SIGHUP.
.TP
.B --dhcp-optsfile=<path>
Read DHCP option information from the specified file. If a directory
is given, then read all the files contained in that directory. The advantage of
is given, then read all the files contained in that directory in alphabetical order. The advantage of
using this option is the same as for \fB--dhcp-hostsfile\fP: the
\fB--dhcp-optsfile\fP will be re-read when dnsmasq receives SIGHUP. Note that
it is possible to encode the information in a
@@ -1148,7 +1200,8 @@ directory, and not an individual file. Changed or new files within
the directory are read automatically, without the need to send SIGHUP.
If a file is deleted or changed after it has been read by dnsmasq, then the
host record it contained will remain until dnsmasq receives a SIGHUP, or
is restarted; ie host records are only added dynamically.
is restarted; ie host records are only added dynamically. The order in which the
files in a directory are read is not defined.
.TP
.B --dhcp-optsdir=<path>
This is equivalent to \fB--dhcp-optsfile\fP, with the differences noted for \fB--dhcp-hostsdir\fP.
@@ -1393,6 +1446,12 @@ Any number of set: and tag: forms may appear, in any order.
tag set by another
.B --tag-if,
the line which sets the tag must precede the one which tests it.
As an extension, the tag:<tag> clauses support limited wildcard matching,
similar to the matching in the \fB--interface\fP directive. This allows, for
example, using \fB--tag-if=set:ppp,tag:ppp*\fP to set the tag 'ppp' for all requests
received on any matching interface (ppp0, ppp1, etc). This can be used in conjunction
with the tag:!<tag> format meaning that no tag matching the wildcard may be set.
.TP
.B \-J, --dhcp-ignore=tag:<tag>[,tag:<tag>]
When all the given tags appear in the tag set ignore the host and do
@@ -1573,10 +1632,11 @@ tried. This flag disables this check. Use with caution.
Extra logging for DHCP: log all the options sent to DHCP clients and
the tags used to determine them.
.TP
.B --quiet-dhcp, --quiet-dhcp6, --quiet-ra
.B --quiet-dhcp, --quiet-dhcp6, --quiet-ra, --quiet-tftp
Suppress logging of the routine operation of these protocols. Errors and
problems will still be logged. \fB--quiet-dhcp\fP and quiet-dhcp6 are
over-ridden by \fB--log-dhcp\fP.
problems will still be logged. \fB--quiet-tftp\fP does not consider file not
found to be an error. \fB--quiet-dhcp\fP and quiet-dhcp6 are over-ridden by
\fB--log-dhcp\fP.
.TP
.B \-l, --dhcp-leasefile=<path>
Use the specified file to store DHCP lease information.
@@ -1874,7 +1934,6 @@ additional flag "local" may be supplied which has the effect of adding
is identical to
.B --domain=thekelleys.org.uk,192.168.0.0/24
.B --local=/thekelleys.org.uk/ --local=/0.168.192.in-addr.arpa/
The network size must be 8, 16 or 24 for this to be legal.
.TP
.B --dhcp-fqdn
In the default mode, dnsmasq inserts the unqualified names of
@@ -2378,6 +2437,10 @@ following data is used to populate the authoritative zone.
.B --mx-host, --srv-host, --dns-rr, --txt-record, --naptr-record, --caa-record,
as long as the record names are in the authoritative domain.
.PP
.B --synth-domain
as long as the domain is in the authoritative zone and, for
reverse (PTR) queries, the address is in the relevant subnet.
.PP
.B --cname
as long as the record name is in the authoritative domain. If the
target of the CNAME is unqualified, then it is qualified with the
+671 -512
View File
File diff suppressed because it is too large. Load diff
+689 -524
View File
File diff suppressed because it is too large. Load diff
+731 -579
View File
File diff suppressed because it is too large. Load diff
+690 -525
View File
File diff suppressed because it is too large. Load diff
+698 -530
View File
File diff suppressed because it is too large. Load diff
+731 -579
View File
File diff suppressed because it is too large. Load diff
+717 -554
View File
File diff suppressed because it is too large. Load diff
+690 -524
View File
File diff suppressed because it is too large. Load diff
+731 -579
View File
File diff suppressed because it is too large. Load diff
+717 -554
View File
File diff suppressed because it is too large. Load diff
+56 -25
View File
@@ -105,7 +105,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
int nameoffset, axfroffset = 0;
int q, anscount = 0, authcount = 0;
struct crec *crecp;
int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0;
int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0, out_of_zone = 0;
struct auth_zone *zone = NULL;
struct addrlist *subnet = NULL;
char *cut;
@@ -146,6 +146,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (qclass != C_IN)
{
auth = 0;
out_of_zone = 1;
continue;
}
@@ -159,6 +160,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (!zone)
{
out_of_zone = 1;
auth = 0;
continue;
}
@@ -208,7 +210,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);
log_query(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))
@@ -232,7 +234,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));
log_query(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,
@@ -241,7 +243,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));
log_query(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,
@@ -253,10 +255,21 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
} while ((crecp = cache_find_by_addr(crecp, &addr, now, flag)));
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);
found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL,
T_PTR, C_IN, "d", name))
anscount++;
}
if (found)
nxdomain = 0;
else
log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL);
log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL, 0);
continue;
}
@@ -273,6 +286,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (!zone)
{
out_of_zone = 1;
auth = 0;
continue;
}
@@ -286,7 +300,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>");
log_query(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++;
@@ -301,7 +315,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>");
log_query(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))
@@ -335,7 +349,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, querystr(NULL, txt->class));
log_query(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++;
@@ -349,7 +363,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>");
log_query(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++;
@@ -363,7 +377,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>");
log_query(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))
@@ -393,13 +407,23 @@ 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);
log_query(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))
anscount++;
}
}
if (!found && is_name_synthetic(flag, name, &addr) )
{
nxdomain = 0;
log_query(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++;
}
if (!cut)
{
@@ -408,8 +432,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 */
found = 1;
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>");
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>", 0);
}
else if (qtype == T_AXFR)
{
@@ -444,16 +467,14 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
soa = 1; /* inhibits auth section */
ns = 1; /* ensure we include NS records! */
axfr = 1;
found = 1;
axfroffset = nameoffset;
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>");
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>", 0);
}
else if (qtype == T_NS)
{
auth = 1;
ns = 1; /* inhibits auth section */
found = 1;
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>");
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>", 0);
}
}
@@ -471,9 +492,8 @@ 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));
log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid), 0);
*cut = 0; /* remove domain part */
found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN,
qtype == T_A ? "4" : "6", &crecp->addr))
@@ -493,8 +513,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, name, &crecp->addr, record_source(crecp->uid));
found = 1;
log_query(crecp->flags, 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))
@@ -541,7 +560,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);
log_query(F_CONFIG | F_CNAME, name, NULL, NULL, 0);
strcpy(name, candidate->target);
if (!strchr(name, '.'))
{
@@ -559,7 +578,7 @@ 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);
log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL, 0);
}
}
@@ -589,7 +608,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
if (subnet->prefixlen >= 16 )
p += sprintf(p, "%u.", a & 0xff);
a = a >> 8;
p += sprintf(p, "%u.in-addr.arpa", a & 0xff);
sprintf(p, "%u.in-addr.arpa", a & 0xff);
}
else
@@ -602,7 +621,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
int dig = ((unsigned char *)&subnet->addr.addr6)[i>>3];
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
}
p += sprintf(p, "ip6.arpa");
sprintf(p, "ip6.arpa");
}
}
@@ -855,10 +874,22 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
SET_RCODE(header, NXDOMAIN);
else
SET_RCODE(header, NOERROR); /* no error */
header->ancount = htons(anscount);
header->nscount = htons(authcount);
header->arcount = htons(0);
if (!local_query && out_of_zone)
{
SET_RCODE(header, REFUSED);
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);
return resize_packet(header, ansp - (unsigned char *)header, NULL, 0);
}
/* Advertise our packet size limit in our reply */
if (have_pseudoheader)
return add_pseudoheader(header, ansp - (unsigned char *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
+1 -1
View File
@@ -52,7 +52,7 @@ void blockdata_init(void)
void blockdata_report(void)
{
my_syslog(LOG_INFO, _("pool memory in use %u, max %u, allocated %u"),
my_syslog(LOG_INFO, _("pool memory in use %zu, max %zu, allocated %zu"),
blockdata_count * sizeof(struct blockdata),
blockdata_hwm * sizeof(struct blockdata),
blockdata_alloced * sizeof(struct blockdata));
+178 -68
View File
@@ -30,50 +30,100 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
time_t now, unsigned long ttl, unsigned int flags);
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
/* taken from https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml */
static const struct {
unsigned int type;
const char * const name;
} typestr[] = {
{ 1, "A" },
{ 2, "NS" },
{ 5, "CNAME" },
{ 6, "SOA" },
{ 10, "NULL" },
{ 11, "WKS" },
{ 12, "PTR" },
{ 13, "HINFO" },
{ 15, "MX" },
{ 16, "TXT" },
{ 22, "NSAP" },
{ 23, "NSAP_PTR" },
{ 24, "SIG" },
{ 25, "KEY" },
{ 28, "AAAA" },
{ 29, "LOC" },
{ 33, "SRV" },
{ 35, "NAPTR" },
{ 36, "KX" },
{ 37, "CERT" },
{ 38, "A6" },
{ 39, "DNAME" },
{ 41, "OPT" },
{ 43, "DS" },
{ 46, "RRSIG" },
{ 47, "NSEC" },
{ 48, "DNSKEY" },
{ 50, "NSEC3" },
{ 51, "NSEC3PARAM" },
{ 52, "TLSA" },
{ 53, "SMIMEA" },
{ 55, "HIP" },
{ 249, "TKEY" },
{ 250, "TSIG" },
{ 251, "IXFR" },
{ 252, "AXFR" },
{ 253, "MAILB" },
{ 254, "MAILA" },
{ 255, "ANY" },
{ 257, "CAA" }
{ 1, "A" }, /* a host address [RFC1035] */
{ 2, "NS" }, /* an authoritative name server [RFC1035] */
{ 3, "MD" }, /* a mail destination (OBSOLETE - use MX) [RFC1035] */
{ 4, "MF" }, /* a mail forwarder (OBSOLETE - use MX) [RFC1035] */
{ 5, "CNAME" }, /* the canonical name for an alias [RFC1035] */
{ 6, "SOA" }, /* marks the start of a zone of authority [RFC1035] */
{ 7, "MB" }, /* a mailbox domain name (EXPERIMENTAL) [RFC1035] */
{ 8, "MG" }, /* a mail group member (EXPERIMENTAL) [RFC1035] */
{ 9, "MR" }, /* a mail rename domain name (EXPERIMENTAL) [RFC1035] */
{ 10, "NULL" }, /* a null RR (EXPERIMENTAL) [RFC1035] */
{ 11, "WKS" }, /* a well known service description [RFC1035] */
{ 12, "PTR" }, /* a domain name pointer [RFC1035] */
{ 13, "HINFO" }, /* host information [RFC1035] */
{ 14, "MINFO" }, /* mailbox or mail list information [RFC1035] */
{ 15, "MX" }, /* mail exchange [RFC1035] */
{ 16, "TXT" }, /* text strings [RFC1035] */
{ 17, "RP" }, /* for Responsible Person [RFC1183] */
{ 18, "AFSDB" }, /* for AFS Data Base location [RFC1183][RFC5864] */
{ 19, "X25" }, /* for X.25 PSDN address [RFC1183] */
{ 20, "ISDN" }, /* for ISDN address [RFC1183] */
{ 21, "RT" }, /* for Route Through [RFC1183] */
{ 22, "NSAP" }, /* for NSAP address, NSAP style A record [RFC1706] */
{ 23, "NSAP_PTR" }, /* for domain name pointer, NSAP style [RFC1348][RFC1637][RFC1706] */
{ 24, "SIG" }, /* for security signature [RFC2535][RFC2536][RFC2537][RFC2931][RFC3008][RFC3110][RFC3755][RFC4034] */
{ 25, "KEY" }, /* for security key [RFC2535][RFC2536][RFC2537][RFC2539][RFC3008][RFC3110][RFC3755][RFC4034] */
{ 26, "PX" }, /* X.400 mail mapping information [RFC2163] */
{ 27, "GPOS" }, /* Geographical Position [RFC1712] */
{ 28, "AAAA" }, /* IP6 Address [RFC3596] */
{ 29, "LOC" }, /* Location Information [RFC1876] */
{ 30, "NXT" }, /* Next Domain (OBSOLETE) [RFC2535][RFC3755] */
{ 31, "EID" }, /* Endpoint Identifier [Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt] 1995-06*/
{ 32, "NIMLOC" }, /* Nimrod Locator [1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt] 1995-06*/
{ 33, "SRV" }, /* Server Selection [1][RFC2782] */
{ 34, "ATMA" }, /* ATM Address [ ATM Forum Technical Committee, "ATM Name System, V2.0", Doc ID: AF-DANS-0152.000, July 2000. Available from and held in escrow by IANA.] */
{ 35, "NAPTR" }, /* Naming Authority Pointer [RFC2168][RFC2915][RFC3403] */
{ 36, "KX" }, /* Key Exchanger [RFC2230] */
{ 37, "CERT" }, /* CERT [RFC4398] */
{ 38, "A6" }, /* A6 (OBSOLETE - use AAAA) [RFC2874][RFC3226][RFC6563] */
{ 39, "DNAME" }, /* DNAME [RFC6672] */
{ 40, "SINK" }, /* SINK [Donald_E_Eastlake][http://tools.ietf.org/html/draft-eastlake-kitchen-sink] 1997-11*/
{ 41, "OPT" }, /* OPT [RFC3225][RFC6891] */
{ 42, "APL" }, /* APL [RFC3123] */
{ 43, "DS" }, /* Delegation Signer [RFC3658][RFC4034] */
{ 44, "SSHFP" }, /* SSH Key Fingerprint [RFC4255] */
{ 45, "IPSECKEY" }, /* IPSECKEY [RFC4025] */
{ 46, "RRSIG" }, /* RRSIG [RFC3755][RFC4034] */
{ 47, "NSEC" }, /* NSEC [RFC3755][RFC4034][RFC9077] */
{ 48, "DNSKEY" }, /* DNSKEY [RFC3755][RFC4034] */
{ 49, "DHCID" }, /* DHCID [RFC4701] */
{ 50, "NSEC3" }, /* NSEC3 [RFC5155][RFC9077] */
{ 51, "NSEC3PARAM" }, /* NSEC3PARAM [RFC5155] */
{ 52, "TLSA" }, /* TLSA [RFC6698] */
{ 53, "SMIMEA" }, /* S/MIME cert association [RFC8162] SMIMEA/smimea-completed-template 2015-12-01*/
{ 55, "HIP" }, /* Host Identity Protocol [RFC8005] */
{ 56, "NINFO" }, /* NINFO [Jim_Reid] NINFO/ninfo-completed-template 2008-01-21*/
{ 57, "RKEY" }, /* RKEY [Jim_Reid] RKEY/rkey-completed-template 2008-01-21*/
{ 58, "TALINK" }, /* Trust Anchor LINK [Wouter_Wijngaards] TALINK/talink-completed-template 2010-02-17*/
{ 59, "CDS" }, /* Child DS [RFC7344] CDS/cds-completed-template 2011-06-06*/
{ 60, "CDNSKEY" }, /* DNSKEY(s) the Child wants reflected in DS [RFC7344] 2014-06-16*/
{ 61, "OPENPGPKEY" }, /* OpenPGP Key [RFC7929] OPENPGPKEY/openpgpkey-completed-template 2014-08-12*/
{ 62, "CSYNC" }, /* Child-To-Parent Synchronization [RFC7477] 2015-01-27*/
{ 63, "ZONEMD" }, /* Message Digest Over Zone Data [RFC8976] ZONEMD/zonemd-completed-template 2018-12-12*/
{ 64, "SVCB" }, /* Service Binding [draft-ietf-dnsop-svcb-https-00] SVCB/svcb-completed-template 2020-06-30*/
{ 65, "HTTPS" }, /* HTTPS Binding [draft-ietf-dnsop-svcb-https-00] HTTPS/https-completed-template 2020-06-30*/
{ 99, "SPF" }, /* [RFC7208] */
{ 100, "UINFO" }, /* [IANA-Reserved] */
{ 101, "UID" }, /* [IANA-Reserved] */
{ 102, "GID" }, /* [IANA-Reserved] */
{ 103, "UNSPEC" }, /* [IANA-Reserved] */
{ 104, "NID" }, /* [RFC6742] ILNP/nid-completed-template */
{ 105, "L32" }, /* [RFC6742] ILNP/l32-completed-template */
{ 106, "L64" }, /* [RFC6742] ILNP/l64-completed-template */
{ 107, "LP" }, /* [RFC6742] ILNP/lp-completed-template */
{ 108, "EUI48" }, /* an EUI-48 address [RFC7043] EUI48/eui48-completed-template 2013-03-27*/
{ 109, "EUI64" }, /* an EUI-64 address [RFC7043] EUI64/eui64-completed-template 2013-03-27*/
{ 249, "TKEY" }, /* Transaction Key [RFC2930] */
{ 250, "TSIG" }, /* Transaction Signature [RFC8945] */
{ 251, "IXFR" }, /* incremental transfer [RFC1995] */
{ 252, "AXFR" }, /* transfer of an entire zone [RFC1035][RFC5936] */
{ 253, "MAILB" }, /* mailbox-related RRs (MB, MG or MR) [RFC1035] */
{ 254, "MAILA" }, /* mail agent RRs (OBSOLETE - see MX) [RFC1035] */
{ 255, "ANY" }, /* A request for some or all records the server has available [RFC1035][RFC6895][RFC8482] */
{ 256, "URI" }, /* URI [RFC7553] URI/uri-completed-template 2011-02-22*/
{ 257, "CAA" }, /* Certification Authority Restriction [RFC8659] CAA/caa-completed-template 2011-04-07*/
{ 258, "AVC" }, /* Application Visibility and Control [Wolfgang_Riedel] AVC/avc-completed-template 2016-02-26*/
{ 259, "DOA" }, /* Digital Object Architecture [draft-durand-doa-over-dns] DOA/doa-completed-template 2017-08-30*/
{ 260, "AMTRELAY" }, /* Automatic Multicast Tunneling Relay [RFC8777] AMTRELAY/amtrelay-completed-template 2019-02-06*/
{ 32768, "TA" }, /* DNSSEC Trust Authorities [Sam_Weiler][http://cameo.library.cmu.edu/][ Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.] 2005-12-13*/
{ 32769, "DLV" }, /* DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431] */
};
static void cache_free(struct crec *crecp);
@@ -433,7 +483,7 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s
else if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) &&
(flags & crecp->flags & F_REVERSE) &&
(flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
memcmp(&crecp->addr, addr, addrlen) == 0)
addr && memcmp(&crecp->addr, addr, addrlen) == 0)
{
*up = crecp->hash_next;
cache_unlink(crecp);
@@ -488,8 +538,6 @@ struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class
else
#endif
{
/* Don't log DNSSEC records here, done elsewhere */
log_query(flags | F_UPSTREAM, name, addr, NULL);
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
ttl = daemon->max_cache_ttl;
if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl)
@@ -1549,7 +1597,8 @@ static void make_non_terminals(struct crec *source)
if (crecp)
{
crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_REVERSE);
crecp->ttd = source->ttd;
if (!(crecp->flags & F_IMMORTAL))
crecp->ttd = source->ttd;
crecp->name.namep = name;
cache_hash(crecp);
@@ -1602,18 +1651,18 @@ int cache_make_stat(struct txt_record *t)
case TXT_STAT_SERVERS:
/* sum counts from different records for same server */
for (serv = daemon->servers; serv; serv = serv->next)
serv->flags &= ~SERV_COUNTED;
serv->flags &= ~SERV_MARK;
for (serv = daemon->servers; serv; serv = serv->next)
if (!(serv->flags & SERV_COUNTED))
if (!(serv->flags & SERV_MARK))
{
char *new, *lenp;
int port, newlen, bytes_avail, bytes_needed;
unsigned int queries = 0, failed_queries = 0;
for (serv1 = serv; serv1; serv1 = serv1->next)
if (!(serv1->flags & SERV_COUNTED) && sockaddr_isequal(&serv->addr, &serv1->addr))
if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
{
serv1->flags |= SERV_COUNTED;
serv1->flags |= SERV_MARK;
queries += serv1->queries;
failed_queries += serv1->failed_queries;
}
@@ -1641,6 +1690,7 @@ int cache_make_stat(struct txt_record *t)
}
t->txt = (unsigned char *)buff;
t->len = p - buff;
return 1;
}
@@ -1683,24 +1733,24 @@ void dump_cache(time_t now)
/* sum counts from different records for same server */
for (serv = daemon->servers; serv; serv = serv->next)
serv->flags &= ~SERV_COUNTED;
serv->flags &= ~SERV_MARK;
for (serv = daemon->servers; serv; serv = serv->next)
if (!(serv->flags & SERV_COUNTED))
if (!(serv->flags & SERV_MARK))
{
int port;
unsigned int queries = 0, failed_queries = 0;
for (serv1 = serv; serv1; serv1 = serv1->next)
if (!(serv1->flags & SERV_COUNTED) && sockaddr_isequal(&serv->addr, &serv1->addr))
if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr))
{
serv1->flags |= SERV_COUNTED;
serv1->flags |= SERV_MARK;
queries += serv1->queries;
failed_queries += serv1->failed_queries;
}
port = prettyprint_addr(&serv->addr, daemon->addrbuff);
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);
}
if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
{
struct crec *cache ;
@@ -1806,7 +1856,7 @@ char *record_source(unsigned int index)
return "<unknown>";
}
char *querystr(char *desc, unsigned short type)
static char *querystr(char *desc, unsigned short type)
{
unsigned int i;
int len = 10; /* strlen("type=xxxxx") */
@@ -1854,25 +1904,70 @@ char *querystr(char *desc, unsigned short type)
if (types)
sprintf(buff, "<%s>", types);
else
sprintf(buff, "type=%d", type);
sprintf(buff, "<type=%d>", type);
}
}
return buff ? buff : "";
}
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
static char *edestr(int ede)
{
char *source, *dest = daemon->addrbuff;
switch (ede)
{
case EDE_OTHER: return "other";
case EDE_USUPDNSKEY: return "unsupported DNSKEY algorithm";
case EDE_USUPDS: return "unsupported DS digest";
case EDE_STALE: return "stale answer";
case EDE_FORGED: return "forged";
case EDE_DNSSEC_IND: return "DNSSEC indeterminate";
case EDE_DNSSEC_BOGUS: return "DNSSEC bogus";
case EDE_SIG_EXP: return "DNSSEC signature expired";
case EDE_SIG_NYV: return "DNSSEC sig not yet valid";
case EDE_NO_DNSKEY: return "DNSKEY missing";
case EDE_NO_RRSIG: return "RRSIG missing";
case EDE_NO_ZONEKEY: return "no zone key bit set";
case EDE_NO_NSEC: return "NSEC(3) missing";
case EDE_CACHED_ERR: return "cached error";
case EDE_NOT_READY: return "not ready";
case EDE_BLOCKED: return "blocked";
case EDE_CENSORED: return "censored";
case EDE_FILTERED: return "filtered";
case EDE_PROHIBITED: return "prohibited";
case EDE_STALE_NXD: return "stale NXDOMAIN";
case EDE_NOT_AUTH: return "not authoritative";
case EDE_NOT_SUP: return "not supported";
case EDE_NO_AUTH: return "no reachable authority";
case EDE_NETERR: return "network error";
case EDE_INVALID_DATA: return "invalid data";
default: return "unknown";
}
}
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, unsigned short type)
{
char *source, *dest = arg;
char *verb = "is";
char *extra = "";
if (!option_bool(OPT_LOG))
return;
/* build query type string if requested */
if(type > 0)
arg = querystr(arg, type);
#ifdef HAVE_DNSSEC
if ((flags & F_DNSSECOK) && option_bool(OPT_EXTRALOG))
extra = " (DNSSEC signed)";
#endif
name = sanitise(name);
if (addr)
{
dest = daemon->addrbuff;
if (flags & F_KEYTAG)
sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest);
else if (flags & F_RCODE)
@@ -1887,14 +1982,19 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
dest = "not implemented";
else
sprintf(daemon->addrbuff, "%u", rcode);
if (addr->log.ede != EDE_UNSET)
{
extra = daemon->addrbuff;
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
}
}
else
else if (flags & (F_IPV4 | F_IPV6))
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, daemon->addrbuff, ADDRSTRLEN);
else
dest = arg;
}
else
dest = arg;
if (flags & F_REVERSE)
{
@@ -1932,7 +2032,15 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
else if (flags & F_UPSTREAM)
source = "reply";
else if (flags & F_SECSTAT)
source = "validation";
{
if (addr && addr->log.ede != EDE_UNSET && option_bool(OPT_EXTRALOG))
{
extra = daemon->addrbuff;
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
}
source = "validation";
dest = arg;
}
else if (flags & F_AUTH)
source = "auth";
else if (flags & F_SERVER)
@@ -1952,7 +2060,7 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
}
else if (flags & F_IPSET)
{
source = "ipset add";
source = type ? "ipset add" : "nftset add";
dest = name;
name = arg;
verb = daemon->addrbuff;
@@ -1960,17 +2068,19 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
else
source = "cached";
if (strlen(name) == 0)
if (name && !name[0])
name = ".";
if (option_bool(OPT_EXTRALOG))
{
int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
if (flags & F_NOEXTRA)
my_syslog(LOG_INFO, "* %s/%u %s %s %s %s", daemon->addrbuff2, port, source, name, verb, dest);
my_syslog(LOG_INFO, "%u %s %s %s %s%s", daemon->log_display_id, source, name, verb, dest, extra);
else
my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest);
{
int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s%s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest, extra);
}
}
else
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
my_syslog(LOG_INFO, "%s %s %s %s%s", source, name, verb, dest, extra);
}
+10 -2
View File
@@ -23,7 +23,7 @@
#define SAFE_PKTSZ 1280 /* "go anywhere" UDP packet size */
#define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */
#define DNSSEC_WORK 50 /* Max number of queries to validate one question */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define FORWARD_TEST 50 /* try all servers every 50 queries */
#define FORWARD_TIME 20 /* or 20 seconds */
#define UDP_TEST_TIME 60 /* How often to reset our idea of max packet size. */
@@ -115,6 +115,10 @@ HAVE_IPSET
define this to include the ability to selectively add resolved ip addresses
to given ipsets.
HAVE_NFTSET
define this to include the ability to selectively add resolved ip addresses
to given nftables sets.
HAVE_AUTH
define this to include the facility to act as an authoritative DNS
server for one or more zones.
@@ -192,7 +196,7 @@ RESOLVFILE
/* #define HAVE_CONNTRACK */
/* #define HAVE_CRYPTOHASH */
/* #define HAVE_DNSSEC */
/* #define HAVE_NFTSET */
/* Default locations for important system files. */
@@ -420,6 +424,10 @@ static char *compile_opts =
"no-"
#endif
"ipset "
#ifndef HAVE_NFTSET
"no-"
#endif
"nftset "
#ifndef HAVE_AUTH
"no-"
#endif
+21 -16
View File
@@ -114,7 +114,7 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *data)
w->next = daemon->watches;
daemon->watches = w;
w = data; /* no warning */
(void)data; /* no warning */
return TRUE;
}
@@ -134,16 +134,20 @@ static void remove_watch(DBusWatch *watch, void *data)
up = &(w->next);
}
w = data; /* no warning */
(void)data; /* no warning */
}
static void dbus_read_servers(DBusMessage *message)
static DBusMessage* dbus_read_servers(DBusMessage *message)
{
DBusMessageIter iter;
union mysockaddr addr, source_addr;
char *domain;
dbus_message_iter_init(message, &iter);
if (!dbus_message_iter_init(message, &iter))
{
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Failed to initialize dbus message iter");
}
mark_servers(SERV_FROM_DBUS);
@@ -215,13 +219,14 @@ static void dbus_read_servers(DBusMessage *message)
domain = NULL;
if (!skip)
add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain);
add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain, NULL);
} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
}
/* unlink and free anything still marked. */
cleanup_servers();
return NULL;
}
#ifdef HAVE_LOOP
@@ -276,7 +281,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
{
const char *str = NULL;
union mysockaddr addr, source_addr;
int flags = 0;
u16 flags = 0;
char interface[IF_NAMESIZE];
char *str_addr, *str_domain = NULL;
@@ -361,10 +366,6 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
strcpy(str_addr, str);
}
memset(&addr, 0, sizeof(addr));
memset(&source_addr, 0, sizeof(source_addr));
memset(&interface, 0, sizeof(interface));
/* parse the IP address */
if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
{
@@ -392,7 +393,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
else
p = NULL;
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain);
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
} while ((str_domain = p));
}
else
@@ -407,7 +408,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
dbus_message_iter_get_basic(&string_iter, &str);
dbus_message_iter_next (&string_iter);
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str);
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
}
@@ -416,7 +417,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
}
cleanup_servers();
if (dup)
free(dup);
@@ -549,6 +550,10 @@ static DBusMessage *dbus_add_lease(DBusMessage* message)
"Invalid IP address '%s'", ipaddr);
hw_len = parse_hex((char*)hwaddr, dhcp_chaddr, DHCP_CHADDR_MAX, NULL, &hw_type);
if (hw_len < 0)
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid HW address '%s'", hwaddr);
if (hw_type == 0 && hw_len != 0)
hw_type = ARPHRD_ETHER;
@@ -672,7 +677,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
#endif
else if (strcmp(method, "SetServers") == 0)
{
dbus_read_servers(message);
reply = dbus_read_servers(message);
new_servers = 1;
}
else if (strcmp(method, "SetServersEx") == 0)
@@ -715,7 +720,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
if (new_servers)
{
my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
check_servers();
check_servers(0);
if (option_bool(OPT_RELOAD))
clear_cache = 1;
}
@@ -723,7 +728,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
if (clear_cache)
clear_cache_and_reload(dnsmasq_time());
method = user_data; /* no warning */
(void)user_data; /* no warning */
/* If no reply or no error, return nothing */
if (!reply)
+36 -1
View File
@@ -79,13 +79,46 @@ ssize_t recv_dhcp_packet(int fd, struct msghdr *msg)
return (msg->msg_flags & MSG_TRUNC) ? -1 : new_sz;
}
/* like match_netid() except that the check can have a trailing * for wildcard */
/* started as a direct copy of match_netid() */
int match_netid_wild(struct dhcp_netid *check, struct dhcp_netid *pool)
{
struct dhcp_netid *tmp1;
for (; check; check = check->next)
{
const int check_len = strlen(check->net);
const int is_wc = (check_len > 0 && check->net[check_len - 1] == '*');
/* '#' for not is for backwards compat. */
if (check->net[0] != '!' && check->net[0] != '#')
{
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (is_wc ? (strncmp(check->net, tmp1->net, check_len-1) == 0) :
(strcmp(check->net, tmp1->net) == 0))
break;
if (!tmp1)
return 0;
}
else
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (is_wc ? (strncmp((check->net)+1, tmp1->net, check_len-2) == 0) :
(strcmp((check->net)+1, tmp1->net) == 0))
return 0;
}
return 1;
}
struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
{
struct tag_if *exprs;
struct dhcp_netid_list *list;
/* this now uses match_netid_wild() above so that tag_if can
* be used to set a 'group of interfaces' tag.
*/
for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
if (match_netid(exprs->tag, tags, 1))
if (match_netid_wild(exprs->tag, tags))
for (list = exprs->set; list; list = list->next)
{
list->list->next = tags;
@@ -620,6 +653,8 @@ static const struct opttab_t {
{ "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 */
{ "subnet-select", 118, OT_INTERNAL },
{ "domain-search", 119, OT_RFC1035_NAME },
{ "sip-server", 120, 0 },
+12 -7
View File
@@ -468,8 +468,11 @@ void dhcp_packet(time_t now, int pxe_fd)
/* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
if (errno != 0)
my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
inet_ntoa(dest.sin_addr), strerror(errno));
{
inet_ntop(AF_INET, &dest.sin_addr, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
daemon->addrbuff, strerror(errno));
}
}
/* check against secondary interface addresses */
@@ -521,10 +524,11 @@ static void guess_range_netmask(struct in_addr addr, struct in_addr netmask)
!(is_same_net(addr, context->start, netmask) &&
is_same_net(addr, context->end, netmask)))
{
strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
inet_ntop(AF_INET, &context->start, daemon->dhcp_buff, DHCP_BUFF_SZ);
inet_ntop(AF_INET, &context->end, daemon->dhcp_buff2, DHCP_BUFF_SZ);
inet_ntop(AF_INET, &netmask, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
daemon->dhcp_buff, daemon->dhcp_buff2, daemon->addrbuff);
}
context->netmask = netmask;
}
@@ -922,7 +926,7 @@ void dhcp_read_ethers(void)
if (!*cp)
{
if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
if (inet_pton(AF_INET, ip, &addr.s_addr) < 1)
{
my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
continue;
@@ -1097,7 +1101,8 @@ static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess,
if (option_bool(OPT_LOG_OPTS))
{
inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, inet_ntoa(relay->server.addr4));
inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
}
/* Save this for replies */
+1 -1
View File
@@ -292,7 +292,7 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi
if ((maclen = find_mac(&addr, mac, 0, now)) != 0)
break;
sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr));
while(retry_send(sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr))));
ts.tv_sec = 0;
ts.tv_nsec = 100000000; /* 100ms */
+32
View File
@@ -80,10 +80,42 @@
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
#define EDNS0_OPTION_EDE 15 /* IANA - RFC 8914 */
#define EDNS0_OPTION_NOMDEVICEID 65073 /* Nominum temporary assignment */
#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */
#define EDNS0_OPTION_UMBRELLA 20292 /* Cisco Umbrella temporary assignment */
/* RFC-8914 extended errors, negative values are our definitions */
#define EDE_UNSET -1 /* No extended DNS error available */
#define EDE_OTHER 0 /* Other */
#define EDE_USUPDNSKEY 1 /* Unsupported DNSKEY algo */
#define EDE_USUPDS 2 /* Unsupported DS Digest */
#define EDE_STALE 3 /* Stale answer */
#define EDE_FORGED 4 /* Forged answer */
#define EDE_DNSSEC_IND 5 /* DNSSEC Indeterminate */
#define EDE_DNSSEC_BOGUS 6 /* DNSSEC Bogus */
#define EDE_SIG_EXP 7 /* Signature Expired */
#define EDE_SIG_NYV 8 /* Signature Not Yet Valid */
#define EDE_NO_DNSKEY 9 /* DNSKEY missing */
#define EDE_NO_RRSIG 10 /* RRSIGs missing */
#define EDE_NO_ZONEKEY 11 /* No Zone Key Bit Set */
#define EDE_NO_NSEC 12 /* NSEC Missing */
#define EDE_CACHED_ERR 13 /* Cached Error */
#define EDE_NOT_READY 14 /* Not Ready */
#define EDE_BLOCKED 15 /* Blocked */
#define EDE_CENSORED 16 /* Censored */
#define EDE_FILTERED 17 /* Filtered */
#define EDE_PROHIBITED 18 /* Prohibited */
#define EDE_STALE_NXD 19 /* Stale NXDOMAIN */
#define EDE_NOT_AUTH 20 /* Not Authoritative */
#define EDE_NOT_SUP 21 /* Not Supported */
#define EDE_NO_AUTH 22 /* No Reachable Authority */
#define EDE_NETERR 23 /* Network error */
#define EDE_INVALID_DATA 24 /* Invalid Data */
struct dns_header {
u16 id;
u8 hb3,hb4;
+95 -55
View File
@@ -17,14 +17,19 @@
/* Declare static char *compiler_opts in config.h */
#define DNSMASQ_COMPILE_OPTS
/* dnsmasq.h has to be included first as it sources config.h */
#include "dnsmasq.h"
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) || defined(LOCALEDIR)
#include <locale.h>
#endif
struct daemon *daemon;
static volatile pid_t pid = 0;
static volatile int pipewrite;
static int set_dns_listeners(time_t now);
static void set_dns_listeners(void);
static void check_dns_listeners(time_t now);
static void sig_handler(int sig);
static void async_event(int pipe, time_t now);
@@ -34,7 +39,6 @@ static void poll_resolv(int force, int do_reload, time_t now);
int main (int argc, char **argv)
{
int bind_fallback = 0;
time_t now;
struct sigaction sigact;
struct iname *if_tmp;
@@ -59,6 +63,8 @@ int main (int argc, char **argv)
int did_bind = 0;
struct server *serv;
char *netlink_warn;
#else
int bind_fallback = 0;
#endif
#if defined(HAVE_DHCP) || defined(HAVE_DHCP6)
struct dhcp_context *context;
@@ -68,8 +74,10 @@ int main (int argc, char **argv)
int tftp_prefix_missing = 0;
#endif
#ifdef LOCALEDIR
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) || defined(LOCALEDIR)
setlocale(LC_ALL, "");
#endif
#ifdef LOCALEDIR
bindtextdomain("dnsmasq", LOCALEDIR);
textdomain("dnsmasq");
#endif
@@ -109,7 +117,6 @@ int main (int argc, char **argv)
daemon->packet_buff_sz = daemon->edns_pktsz + MAXDNAME + RRFIXEDSZ;
daemon->packet = safe_malloc(daemon->packet_buff_sz);
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
if (option_bool(OPT_EXTRALOG))
daemon->addrbuff2 = safe_malloc(ADDRSTRLEN);
@@ -135,6 +142,13 @@ int main (int argc, char **argv)
}
#endif
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
/* CONNTRACK UBUS code uses this buffer, so if not allocated above,
we need to allocate it here. */
if (option_bool(OPT_CMARK_ALST_EN) && !daemon->workspacename)
daemon->workspacename = safe_malloc(MAXDNAME);
#endif
#ifdef HAVE_DHCP
if (!daemon->lease_file)
{
@@ -205,8 +219,13 @@ int main (int argc, char **argv)
#endif
#ifdef HAVE_CONNTRACK
if (option_bool(OPT_CONNTRACK) && (daemon->query_port != 0 || daemon->osport))
die (_("cannot use --conntrack AND --query-port"), NULL, EC_BADCONF);
if (option_bool(OPT_CONNTRACK))
{
if (daemon->query_port != 0 || daemon->osport)
die (_("cannot use --conntrack AND --query-port"), NULL, EC_BADCONF);
need_cap_net_admin = 1;
}
#else
if (option_bool(OPT_CONNTRACK))
die(_("conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF);
@@ -334,6 +353,16 @@ int main (int argc, char **argv)
}
#endif
#ifdef HAVE_NFTSET
if (daemon->nftsets)
{
nftset_init();
# ifdef HAVE_LINUX_NETWORK
need_cap_net_admin = 1;
# endif
}
#endif
#if defined(HAVE_LINUX_NETWORK)
netlink_warn = netlink_init();
#elif defined(HAVE_BSD_NETWORK)
@@ -366,7 +395,7 @@ int main (int argc, char **argv)
bindtodevice(bound_device, daemon->dhcpfd);
did_bind = 1;
}
if (daemon->enable_pxe && bound_device)
if (daemon->enable_pxe && bound_device && daemon->pxefd != -1)
{
bindtodevice(bound_device, daemon->pxefd);
did_bind = 1;
@@ -430,8 +459,6 @@ int main (int argc, char **argv)
#ifdef HAVE_DBUS
{
char *err;
daemon->dbus = NULL;
daemon->watches = NULL;
if ((err = dbus_init()))
die(_("DBus error: %s"), err, EC_MISC);
}
@@ -442,8 +469,9 @@ int main (int argc, char **argv)
if (option_bool(OPT_UBUS))
#ifdef HAVE_UBUS
{
daemon->ubus = NULL;
ubus_init();
char *err;
if ((err = ubus_init()))
die(_("UBus error: %s"), err, EC_MISC);
}
#else
die(_("UBus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF);
@@ -910,8 +938,10 @@ int main (int argc, char **argv)
my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"),
daemon->log_file, strerror(log_err));
#ifndef HAVE_LINUX_NETWORK
if (bind_fallback)
my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
#endif
if (option_bool(OPT_NOWILD))
warn_bound_listeners();
@@ -1025,7 +1055,7 @@ int main (int argc, char **argv)
close(err_pipe[1]);
if (daemon->port != 0)
check_servers();
check_servers(0);
pid = getpid();
@@ -1040,16 +1070,10 @@ int main (int argc, char **argv)
while (1)
{
int t, timeout = -1;
int timeout = -1;
poll_reset();
/* if we are out of resources, find how long we have to wait
for some to come free, we'll loop around then and restart
listening for queries */
if ((t = set_dns_listeners(now)) != 0)
timeout = t * 1000;
/* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
if (daemon->tftp_trans ||
(option_bool(OPT_DBUS) && !daemon->dbus))
@@ -1059,15 +1083,18 @@ int main (int argc, char **argv)
else if (is_dad_listeners())
timeout = 1000;
#ifdef HAVE_DBUS
set_dbus_listeners();
#endif
set_dns_listeners();
#ifdef HAVE_DBUS
if (option_bool(OPT_DBUS))
set_dbus_listeners();
#endif
#ifdef HAVE_UBUS
if (option_bool(OPT_UBUS))
set_ubus_listeners();
#endif
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->relay4)
{
@@ -1187,28 +1214,44 @@ int main (int argc, char **argv)
#ifdef HAVE_DBUS
/* if we didn't create a DBus connection, retry now. */
if (option_bool(OPT_DBUS) && !daemon->dbus)
if (option_bool(OPT_DBUS))
{
char *err;
if ((err = dbus_init()))
my_syslog(LOG_WARNING, _("DBus error: %s"), err);
if (daemon->dbus)
my_syslog(LOG_INFO, _("connected to system DBus"));
if (!daemon->dbus)
{
char *err = dbus_init();
if (daemon->dbus)
my_syslog(LOG_INFO, _("connected to system DBus"));
else if (err)
{
my_syslog(LOG_ERR, _("DBus error: %s"), err);
reset_option_bool(OPT_DBUS); /* fatal error, stop trying. */
}
}
check_dbus_listeners();
}
check_dbus_listeners();
#endif
#ifdef HAVE_UBUS
/* if we didn't create a UBus connection, retry now. */
if (option_bool(OPT_UBUS))
{
/* if we didn't create a UBus connection, retry now. */
if (!daemon->ubus)
{
ubus_init();
}
{
if (!daemon->ubus)
{
char *err = ubus_init();
check_ubus_listeners();
}
if (daemon->ubus)
my_syslog(LOG_INFO, _("connected to system UBus"));
else if (err)
{
my_syslog(LOG_ERR, _("UBus error: %s"), err);
reset_option_bool(OPT_UBUS); /* fatal error, stop trying. */
}
}
check_ubus_listeners();
}
#endif
check_dns_listeners(now);
@@ -1441,7 +1484,7 @@ static void async_event(int pipe, time_t now)
}
if (check)
check_servers();
check_servers(0);
}
#ifdef HAVE_DHCP
@@ -1552,7 +1595,7 @@ static void async_event(int pipe, time_t now)
{
/* block in writes until all done */
if ((i = fcntl(daemon->helperfd, F_GETFL)) != -1)
fcntl(daemon->helperfd, F_SETFL, i & ~O_NONBLOCK);
while(retry_send(fcntl(daemon->helperfd, F_SETFL, i & ~O_NONBLOCK)));
do {
helper_write();
} while (!helper_buf_empty() || do_script_run(now));
@@ -1640,12 +1683,17 @@ static void poll_resolv(int force, int do_reload, time_t now)
{
my_syslog(LOG_INFO, _("reading %s"), latest->name);
warned = 0;
check_servers();
check_servers(0);
if (option_bool(OPT_RELOAD) && do_reload)
clear_cache_and_reload(now);
}
else
{
/* If we're delaying things, we don't call check_servers(), but
reload_servers() may have deleted some servers, rendering the server_array
invalid, so just rebuild that here. Once reload_servers() succeeds,
we call check_servers() above, which calls build_server_array itself. */
build_server_array();
latest->mtime = 0;
if (!warned)
{
@@ -1683,12 +1731,12 @@ void clear_cache_and_reload(time_t now)
#endif
}
static int set_dns_listeners(time_t now)
static void set_dns_listeners(void)
{
struct serverfd *serverfdp;
struct listener *listener;
struct randfd_list *rfl;
int wait = 0, i;
int i;
#ifdef HAVE_TFTP
int tftp = 0;
@@ -1701,10 +1749,6 @@ static int set_dns_listeners(time_t now)
}
#endif
/* will we be able to get memory? */
if (daemon->port != 0)
get_new_frec(now, &wait, NULL);
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
poll_listen(serverfdp->fd, POLLIN);
@@ -1723,10 +1767,9 @@ static int set_dns_listeners(time_t now)
for (listener = daemon->listeners; listener; listener = listener->next)
{
/* only listen for queries if we have resources */
if (listener->fd != -1 && wait == 0)
if (listener->fd != -1)
poll_listen(listener->fd, POLLIN);
/* Only listen for TCP connections when a process slot
is available. Death of a child goes through the select loop, so
we don't need to explicitly arrange to wake up here,
@@ -1739,15 +1782,12 @@ static int set_dns_listeners(time_t now)
if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
poll_listen(listener->tftpfd, POLLIN);
#endif
}
if (!option_bool(OPT_DEBUG))
for (i = 0; i < MAX_PROCS; i++)
if (daemon->tcp_pipes[i] != -1)
poll_listen(daemon->tcp_pipes[i], POLLIN);
return wait;
}
static void check_dns_listeners(time_t now)
@@ -1969,7 +2009,7 @@ static void check_dns_listeners(time_t now)
attribute from the listening socket.
Reset that here. */
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
while(retry_send(fcntl(confd, F_SETFL, flags & ~O_NONBLOCK)));
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
@@ -2098,7 +2138,7 @@ int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
poll_reset();
if (fd != -1)
poll_listen(fd, POLLIN);
set_dns_listeners(now);
set_dns_listeners();
set_log_writer();
#ifdef HAVE_DHCP6
+117 -58
View File
@@ -107,6 +107,7 @@ typedef unsigned long long u64;
#endif
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
@@ -272,7 +273,9 @@ struct event_desc {
#define OPT_LOG_DEBUG 62
#define OPT_UMBRELLA 63
#define OPT_UMBRELLA_DEVID 64
#define OPT_LAST 65
#define OPT_CMARK_ALST_EN 65
#define OPT_QUIET_TFTP 66
#define OPT_LAST 67
#define OPTION_BITS (sizeof(unsigned int)*8)
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
@@ -322,12 +325,14 @@ union all_addr {
/* for log_query */
struct {
unsigned short keytag, algo, digest, rcode;
int ede;
} log;
};
struct bogus_addr {
struct in_addr addr, mask;
int is6, prefix;
union all_addr addr;
struct bogus_addr *next;
};
@@ -525,23 +530,23 @@ 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, no-data return, send upstream. */
#define SERV_LITERAL_ADDRESS 1 /* addr is the answer, or NoDATA is the answer, depending on the next three flags */
#define SERV_ALL_ZEROS 2 /* return all zeros for A and AAAA */
#define SERV_4ADDR 4 /* addr is IPv4 */
#define SERV_6ADDR 8 /* addr is IPv6 */
#define SERV_HAS_SOURCE 16 /* source address defined */
#define SERV_FOR_NODOTS 32 /* server for names with no domain part only */
#define SERV_WARNED_RECURSIVE 64 /* avoid warning spam */
#define SERV_FROM_DBUS 128 /* 1 if source is DBus */
#define SERV_MARK 256 /* for mark-and-delete */
#define SERV_COUNTED 512 /* workspace for log code */
#define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */
#define SERV_FROM_RESOLV 2048 /* 1 for servers from resolv, 0 for command line. */
#define SERV_FROM_FILE 4096 /* read from --servers-file */
#define SERV_LOOP 8192 /* server causes forwarding loop */
#define SERV_DO_DNSSEC 16384 /* Validate DNSSEC when using this server */
#define SERV_GOT_TCP 32768 /* Got some data from the TCP connection */
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 */
#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 */
#define SERV_HAS_SOURCE 32 /* source address defined */
#define SERV_FOR_NODOTS 64 /* server for names with no domain part only */
#define SERV_WARNED_RECURSIVE 128 /* avoid warning spam */
#define SERV_FROM_DBUS 256 /* 1 if source is DBus */
#define SERV_MARK 512 /* for mark-and-delete and log code */
#define SERV_WILDCARD 1024 /* domain has leading '*' */
#define SERV_FROM_RESOLV 2048 /* 1 for servers from resolv, 0 for command line. */
#define SERV_FROM_FILE 4096 /* read from --servers-file */
#define SERV_LOOP 8192 /* server causes forwarding loop */
#define SERV_DO_DNSSEC 16384 /* Validate DNSSEC when using this server */
#define SERV_GOT_TCP 32768 /* Got some data from the TCP connection */
struct serverfd {
int fd;
@@ -564,7 +569,7 @@ struct randfd_list {
struct server {
int flags;
u16 flags, domain_len;
char *domain;
struct server *next;
int serial, arrayposn;
@@ -583,33 +588,44 @@ struct server {
#endif
};
/* First three fields must match struct server in next three definitions.. */
/* First four fields must match struct server in next three definitions.. */
struct serv_addr4 {
int flags;
u16 flags, domain_len;
char *domain;
struct server *next;
struct in_addr addr;
};
struct serv_addr6 {
int flags;
u16 flags, domain_len;
char *domain;
struct server *next;
struct in6_addr addr;
};
struct serv_local {
int flags;
u16 flags, domain_len;
char *domain;
struct server *next;
};
struct rebind_domain {
char *domain;
struct rebind_domain *next;
};
struct ipsets {
char **sets;
char *domain;
struct ipsets *next;
};
struct allowlist {
u32 mark, mask;
char **patterns;
struct allowlist *next;
};
struct irec {
union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
@@ -679,18 +695,28 @@ struct hostsfile {
#define DUMP_BOGUS 0x0040
#define DUMP_SEC_BOGUS 0x0080
/* DNSSEC status values. */
#define STAT_SECURE 1
#define STAT_INSECURE 2
#define STAT_BOGUS 3
#define STAT_NEED_DS 4
#define STAT_NEED_KEY 5
#define STAT_TRUNCATED 6
#define STAT_SECURE_WILDCARD 7
#define STAT_OK 8
#define STAT_ABANDONED 9
#define STAT_INPROGRESS 10
#define STAT_SECURE 0x10000
#define STAT_INSECURE 0x20000
#define STAT_BOGUS 0x30000
#define STAT_NEED_DS 0x40000
#define STAT_NEED_KEY 0x50000
#define STAT_TRUNCATED 0x60000
#define STAT_SECURE_WILDCARD 0x70000
#define STAT_OK 0x80000
#define STAT_ABANDONED 0x90000
#define DNSSEC_FAIL_NYV 0x0001 /* key not yet valid */
#define DNSSEC_FAIL_EXP 0x0002 /* key expired */
#define DNSSEC_FAIL_INDET 0x0004 /* indetermined */
#define DNSSEC_FAIL_NOKEYSUP 0x0008 /* no supported key algo. */
#define DNSSEC_FAIL_NOSIG 0x0010 /* No RRsigs */
#define DNSSEC_FAIL_NOZONE 0x0020 /* No Zone bit set */
#define DNSSEC_FAIL_NONSEC 0x0040 /* No NSEC */
#define DNSSEC_FAIL_NODSSUP 0x0080 /* no supported DS algo. */
#define DNSSEC_FAIL_NOKEY 0x0100 /* no DNSKEY */
#define STAT_ISEQUAL(a, b) (((a) & 0xffff0000) == (b))
#define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2
@@ -727,6 +753,7 @@ struct frec {
struct blockdata *stash; /* Saved reply, whilst we validate */
size_t stash_len;
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
struct frec *next_dependent; /* list of above. */
struct frec *blocking_query; /* Query which is blocking us. */
#endif
struct frec *next;
@@ -939,10 +966,10 @@ struct dhcp_bridge {
};
struct cond_domain {
char *domain, *prefix;
char *domain, *prefix; /* prefix is text-prefix on domain name */
struct in_addr start, end;
struct in6_addr start6, end6;
int is6, indexed;
int is6, indexed, prefixlen;
struct cond_domain *next;
};
@@ -1083,9 +1110,13 @@ extern struct daemon {
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 server *servers, *local_domains, **serverarray, *no_rebind;
int serverarraysz;
struct ipsets *ipsets;
struct server *servers, *servers_tail, *local_domains, **serverarray;
struct rebind_domain *no_rebind;
int server_has_wildcard;
int serverarraysz, serverarrayhwm;
struct ipsets *ipsets, *nftsets;
u32 allowlist_mask;
struct allowlist *allowlists;
int log_fac; /* log facility */
char *log_file; /* optional log file */
int max_logs; /* queue limit */
@@ -1142,9 +1173,12 @@ extern struct daemon {
char *packet; /* packet buffer */
int packet_buff_sz; /* size of above */
char *namebuff; /* MAXDNAME size buffer */
#if (defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)) || defined(HAVE_DNSSEC)
/* CONNTRACK UBUS code uses this buffer, as well as DNSSEC code. */
char *workspacename;
#endif
#ifdef HAVE_DNSSEC
char *keyname; /* MAXDNAME size buffer */
char *workspacename; /* ditto */
unsigned long *rr_status; /* ceiling in TTL from DNSSEC or zero for insecure */
int rr_status_sz;
int dnssec_no_time_check;
@@ -1220,9 +1254,8 @@ extern struct daemon {
/* cache.c */
void cache_init(void);
void next_uid(struct crec *crecp);
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg);
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, unsigned short type);
char *record_source(unsigned int index);
char *querystr(char *desc, unsigned short type);
int cache_find_non_terminal(char *name, time_t now);
struct crec *cache_find_by_addr(struct crec *crecp,
union all_addr *addr, time_t now,
@@ -1271,10 +1304,13 @@ 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);
void setup_reply(struct dns_header *header, unsigned int flags);
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, char **ipsets, int is_sign, int check_rebind,
int no_cache_dnssec, int secure, int *doctored);
time_t now, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign,
int check_rebind, int no_cache_dnssec, int secure, int *doctored);
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
void report_addresses(struct dns_header *header, size_t len, u32 mark);
#endif
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask,
time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
@@ -1299,6 +1335,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
#endif
/* dnssec.c */
#ifdef HAVE_DNSSEC
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, int edns_pktsz);
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
@@ -1307,6 +1344,8 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen);
size_t filter_rrsigs(struct dns_header *header, size_t plen);
int setup_timestamp(void);
int errflags_to_ede(int status);
#endif
/* hash_questions.c */
void hash_questions_init(void);
@@ -1340,6 +1379,7 @@ int hostname_issubdomain(char *a, char *b);
time_t dnsmasq_time(void);
int netmask_length(struct in_addr mask);
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix);
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
u64 addr6part(struct in6_addr *addr);
void setaddr6part(struct in6_addr *addr, u64 host);
@@ -1381,7 +1421,7 @@ void set_option_bool(unsigned int opt);
void reset_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list);
char *parse_server(char *arg, union mysockaddr *addr,
union mysockaddr *source_addr, char *interface, int *flags);
union mysockaddr *source_addr, char *interface, u16 *flags);
int option_read_dynfile(char *file, int flags);
/* forward.c */
@@ -1390,7 +1430,6 @@ void receive_query(struct listener *listen, time_t now);
unsigned char *tcp_request(int confd, time_t now,
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
void server_gone(struct server *server);
struct frec *get_new_frec(time_t now, int *wait, struct frec *force);
int send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, union all_addr *source,
unsigned int iface);
@@ -1403,14 +1442,7 @@ int indextoname(int fd, int index, char *name);
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp);
void pre_allocate_sfds(void);
int reload_servers(char *fname);
void mark_servers(int flag);
void cleanup_servers(void);
void add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain);
void check_servers(void);
void check_servers(int no_loop_call);
int enumerate_interfaces(int reset);
void create_wildcard_listeners(void);
void create_bound_listeners(int dienow);
@@ -1543,10 +1575,14 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
/* ubus.c */
#ifdef HAVE_UBUS
void ubus_init(void);
char *ubus_init(void);
void set_ubus_listeners(void);
void check_ubus_listeners(void);
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface);
# ifdef HAVE_CONNTRACK
void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name);
void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *pattern, const char *ip, u32 ttl);
# endif
#endif
/* ipset.c */
@@ -1555,6 +1591,19 @@ void ipset_init(void);
int add_to_ipset(const char *setname, const union all_addr *ipaddr, int flags, int remove);
#endif
/* nftset.c */
#ifdef HAVE_NFTSET
void nftset_init(void);
int add_to_nftset(const char *setpath, const union all_addr *ipaddr, int flags, int remove);
#endif
/* pattern.c */
#ifdef HAVE_CONNTRACK
int is_valid_dns_name(const char *value);
int is_valid_dns_name_pattern(const char *value);
int is_dns_name_matching_pattern(const char *name, const char *pattern);
#endif
/* helper.c */
#if defined(HAVE_SCRIPT)
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
@@ -1726,7 +1775,17 @@ void build_server_array(void);
int lookup_domain(char *qdomain, int flags, int *lowout, int *highout);
int filter_servers(int seed, int flags, int *lowout, int *highout);
int is_local_answer(time_t now, int first, char *name);
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header,
char *name, char *limit, int first, int last, int ede);
int server_samegroup(struct server *a, struct server *b);
#ifdef HAVE_DNSSEC
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp);
#endif
void mark_servers(int flag);
void cleanup_servers(void);
int add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain,
union all_addr *local_addr);
+128 -59
View File
@@ -215,14 +215,6 @@ static int is_check_date(unsigned long curtime)
return !daemon->dnssec_no_time_check;
}
/* Check whether today/now is between date_start and date_end */
static int check_date_range(unsigned long curtime, u32 date_start, u32 date_end)
{
/* We must explicitly check against wanted values, because of SERIAL_UNDEF */
return serial_compare_32(curtime, date_start) == SERIAL_GT
&& serial_compare_32(curtime, date_end) == SERIAL_LT;
}
/* Return bytes of canonicalised rrdata one by one.
Init state->ip with the RR, and state->end with the end of same.
Init state->op to NULL.
@@ -534,7 +526,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
struct crec *crecp = NULL;
u16 *rr_desc = rrfilter_desc(type);
u32 sig_expiration, sig_inception;
int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP;
unsigned long curtime = time(0);
int time_check = is_check_date(curtime);
@@ -557,6 +550,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
void *ctx;
char *name_start;
u32 nsigttl, ttl, orig_ttl;
failflags &= ~DNSSEC_FAIL_NOSIG;
p = sigs[j];
GETLONG(ttl, p);
@@ -574,12 +569,31 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
if (!extract_name(header, plen, &p, keyname, 1, 0))
return STAT_BOGUS;
if ((time_check && !check_date_range(curtime, sig_inception, sig_expiration)) ||
labels > name_labels ||
!(hash = hash_find(algo_digest_name(algo))) ||
if (!time_check)
failflags &= ~(DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP);
else
{
/* We must explicitly check against wanted values, because of SERIAL_UNDEF */
if (serial_compare_32(curtime, sig_inception) == SERIAL_LT)
continue;
else
failflags &= ~DNSSEC_FAIL_NYV;
if (serial_compare_32(curtime, sig_expiration) == SERIAL_GT)
continue;
else
failflags &= ~DNSSEC_FAIL_EXP;
}
if (!(hash = hash_find(algo_digest_name(algo))))
continue;
else
failflags &= ~DNSSEC_FAIL_NOKEYSUP;
if (labels > name_labels ||
!hash_init(hash, &ctx, &digest))
continue;
/* OK, we have the signature record, see if the relevant DNSKEY is in the cache. */
if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
return STAT_NEED_KEY;
@@ -710,7 +724,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
/* namebuff used for workspace above, restore to leave unchanged on exit */
p = (unsigned char*)(rrset[0]);
extract_name(header, plen, &p, name, 1, 0);
if (!extract_name(header, plen, &p, name, 1, 0))
return STAT_BOGUS;
if (key)
{
@@ -730,7 +745,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
}
}
return STAT_BOGUS;
/* If we reach this point, no verifying key was found */
return STAT_BOGUS | failflags | DNSSEC_FAIL_NOKEY;
}
@@ -751,17 +767,18 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
unsigned long ttl, sig_ttl;
struct blockdata *key;
union all_addr a;
int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NODSSUP | DNSSEC_FAIL_NOZONE | DNSSEC_FAIL_NOKEY;
if (ntohs(header->qdcount) != 1 ||
RCODE(header) == SERVFAIL || RCODE(header) == REFUSED ||
!extract_name(header, plen, &p, name, 1, 4))
return STAT_BOGUS;
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
GETSHORT(qtype, p);
GETSHORT(qclass, p);
if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)
return STAT_BOGUS;
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
/* See if we have cached a DS record which validates this key */
if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
@@ -795,14 +812,17 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
GETSHORT(flags, p);
if (*p++ != 3)
return STAT_BOGUS;
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
algo = *p++;
keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
key = NULL;
/* key must have zone key flag set */
if (flags & 0x100)
key = blockdata_alloc((char*)p, rdlen - 4);
{
key = blockdata_alloc((char*)p, rdlen - 4);
failflags &= ~DNSSEC_FAIL_NOZONE;
}
p = psave;
@@ -823,15 +843,23 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
unsigned char *digest, *ds_digest;
const struct nettle_hash *hash;
int sigcnt, rrcnt;
int wire_len;
if (recp1->addr.ds.algo == algo &&
recp1->addr.ds.keytag == keytag &&
recp1->uid == (unsigned int)class &&
(hash = hash_find(ds_digest_name(recp1->addr.ds.digest))) &&
hash_init(hash, &ctx, &digest))
recp1->uid == (unsigned int)class)
{
int wire_len = to_wire(name);
failflags &= ~DNSSEC_FAIL_NOKEY;
if (!(hash = hash_find(ds_digest_name(recp1->addr.ds.digest))))
continue;
else
failflags &= ~DNSSEC_FAIL_NODSSUP;
if (!hash_init(hash, &ctx, &digest))
continue;
wire_len = to_wire(name);
/* Note that digest may be different between DSs, so
we can't move this outside the loop. */
@@ -846,12 +874,23 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
(ds_digest = blockdata_retrieve(recp1->addr.ds.keydata, recp1->addr.ds.keylen, NULL)) &&
memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 &&
explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) &&
sigcnt != 0 && rrcnt != 0 &&
validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname,
NULL, key, rdlen - 4, algo, keytag, &sig_ttl) == STAT_SECURE)
rrcnt != 0)
{
valid = 1;
break;
if (sigcnt == 0)
continue;
else
failflags &= ~DNSSEC_FAIL_NOSIG;
rc = validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname,
NULL, key, rdlen - 4, algo, keytag, &sig_ttl);
failflags &= rc;
if (STAT_ISEQUAL(rc, STAT_SECURE))
{
valid = 1;
break;
}
}
}
}
@@ -916,9 +955,9 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
a.log.keytag = keytag;
a.log.algo = algo;
if (algo_digest_name(algo))
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu");
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu", 0);
else
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)");
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)", 0);
}
}
}
@@ -935,8 +974,8 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
return STAT_OK;
}
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
return STAT_BOGUS;
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY", 0);
return STAT_BOGUS | failflags;
}
/* The DNS packet is expected to contain the answer to a DS query
@@ -971,26 +1010,29 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
else
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl);
if (rc == STAT_INSECURE)
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);
rc = STAT_BOGUS;
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0);
return STAT_BOGUS | DNSSEC_FAIL_INDET;
}
p = (unsigned char *)(header+1);
extract_name(header, plen, &p, name, 1, 4);
if (!extract_name(header, plen, &p, name, 1, 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 (rc == STAT_BOGUS || (rc == STAT_NEED_KEY && hostname_isequal(name, keyname)))
if (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname))
{
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS", 0);
return STAT_BOGUS;
}
if (rc != STAT_SECURE)
if (!STAT_ISEQUAL(rc, STAT_SECURE))
return rc;
if (!neganswer)
@@ -1042,9 +1084,9 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
a.log.algo = algo;
a.log.digest = digest;
if (ds_digest_name(digest) && algo_digest_name(algo))
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu");
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0);
else
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)");
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0);
}
}
@@ -1077,7 +1119,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
cache_end_insert();
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS");
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0);
}
return STAT_OK;
@@ -1456,7 +1498,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
if (!(p = skip_name(nsecs[i], header, plen, 15)))
return 0; /* bad packet */
p += 10; /* type, class, TTL, rdlen */
p += 10; /* type, class, TTL, rdlen */
algo = *p++;
if ((hash = hash_find(nsec3_digest_name(algo))))
@@ -1854,7 +1896,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
/* Find all the targets we're looking for answers to.
The zeroth array element is for the query, subsequent ones
for CNAME targets, unless the query is for a CNAME. */
for CNAME targets, unless the query is for a CNAME or ANY. */
if (!expand_workspace(&targets, &target_sz, 0))
return STAT_BOGUS;
@@ -1873,7 +1915,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
if (qtype == T_RRSIG)
return STAT_INSECURE;
if (qtype != T_CNAME)
if (qtype != T_CNAME && qtype != T_ANY)
for (j = ntohs(header->ancount); j != 0; j--)
{
if (!(p1 = skip_name(p1, header, plen, 10)))
@@ -1959,15 +2001,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
if (check_unsigned && i < ntohs(header->ancount))
{
rc = zone_status(name, class1, keyname, now);
if (rc == STAT_SECURE)
rc = STAT_BOGUS;
if (STAT_ISEQUAL(rc, STAT_SECURE))
rc = STAT_BOGUS | DNSSEC_FAIL_NOSIG;
if (class)
*class = class1; /* Class for NEED_DS or NEED_KEY */
}
else
rc = STAT_INSECURE;
if (rc != STAT_INSECURE)
if (!STAT_ISEQUAL(rc, STAT_INSECURE))
return rc;
}
}
@@ -1978,7 +2020,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
strcpy(daemon->workspacename, keyname);
rc = zone_status(daemon->workspacename, class1, keyname, now);
if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS))
{
if (class)
*class = class1; /* Class for NEED_DS or NEED_KEY */
@@ -1986,13 +2028,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
}
/* Zone is insecure, don't need to validate RRset */
if (rc == STAT_SECURE)
if (STAT_ISEQUAL(rc, STAT_SECURE))
{
unsigned long sig_ttl;
rc = validate_rrset(now, header, plen, class1, type1, sigcnt,
rrcnt, name, keyname, &wildname, NULL, 0, 0, 0, &sig_ttl);
if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS))
{
if (class)
*class = class1; /* Class for DS or DNSKEY */
@@ -2025,21 +2067,21 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
Note that we may not yet have validated the NSEC/NSEC3 RRsets.
That's not a problem since if the RRsets later fail
we'll return BOGUS then. */
if (rc == STAT_SECURE_WILDCARD &&
if (STAT_ISEQUAL(rc, STAT_SECURE_WILDCARD) &&
!prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL, NULL))
return STAT_BOGUS;
return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
rc = STAT_SECURE;
}
}
}
if (rc == STAT_INSECURE)
if (STAT_ISEQUAL(rc, STAT_INSECURE))
secure = STAT_INSECURE;
}
/* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
if (secure == STAT_SECURE)
if (STAT_ISEQUAL(secure, STAT_SECURE))
for (j = 0; j <targetidx; j++)
if ((p2 = targets[j]))
{
@@ -2057,16 +2099,16 @@ 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;
return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
if (STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))
{
if (class)
*class = qclass; /* Class for NEED_DS or NEED_KEY */
return rc;
}
return STAT_BOGUS; /* signed zone, no NSECs */
return STAT_BOGUS | DNSSEC_FAIL_NONSEC; /* signed zone, no NSECs */
}
}
@@ -2130,4 +2172,31 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char
return ret;
}
int errflags_to_ede(int status)
{
/* We can end up with more than one flag set for some errors,
so this encodes a rough priority so the (eg) No sig is reported
before no-unexpired-sig. */
if (status & DNSSEC_FAIL_NYV)
return EDE_SIG_NYV;
else if (status & DNSSEC_FAIL_EXP)
return EDE_SIG_EXP;
else if (status & DNSSEC_FAIL_NOKEYSUP)
return EDE_USUPDNSKEY;
else if (status & DNSSEC_FAIL_NOZONE)
return EDE_NO_ZONEKEY;
else if (status & DNSSEC_FAIL_NOKEY)
return EDE_NO_DNSKEY;
else if (status & DNSSEC_FAIL_NODSSUP)
return EDE_USUPDS;
else if (status & DNSSEC_FAIL_NONSEC)
return EDE_NO_NSEC;
else if (status & DNSSEC_FAIL_INDET)
return EDE_DNSSEC_IND;
else if (status & DNSSEC_FAIL_NOSIG)
return EDE_NO_RRSIG;
else
return EDE_UNSET;
}
#endif /* HAVE_DNSSEC */
+437 -109
View File
@@ -16,53 +16,75 @@
#include "dnsmasq.h"
static int order(char *qdomain, int leading_dot, size_t qlen, struct server *serv);
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. */
#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
void build_server_array(void)
{
struct server *serv;
int count = 0;
for (serv = daemon->servers; serv; serv = serv->next)
count++;
for (serv = daemon->local_domains; serv; serv = serv->next)
count++;
#ifdef HAVE_LOOP
if (!(serv->flags & SERV_LOOP))
#endif
{
count++;
if (serv->flags & SERV_WILDCARD)
daemon->server_has_wildcard = 1;
}
if (count > daemon->serverarraysz)
for (serv = daemon->local_domains; serv; serv = serv->next)
{
count++;
if (serv->flags & SERV_WILDCARD)
daemon->server_has_wildcard = 1;
}
daemon->serverarraysz = count;
if (count > daemon->serverarrayhwm)
{
struct server **new;
count += 10; /* A few extra without re-allocating. */
if ((new = whine_malloc(count * sizeof(struct server *))))
{
if (daemon->serverarray)
free(daemon->serverarray);
daemon->serverarray = new;
daemon->serverarraysz = count;
daemon->serverarrayhwm = count;
}
}
count = 0;
for (serv = daemon->servers; serv; serv = serv->next, count++)
{
daemon->serverarray[count] = serv;
serv->serial = count;
serv->last_server = -1;
}
for (serv = daemon->servers; serv; serv = serv->next)
#ifdef HAVE_LOOP
if (!(serv->flags & SERV_LOOP))
#endif
{
daemon->serverarray[count] = serv;
serv->serial = count;
serv->last_server = -1;
count++;
}
for (serv = daemon->local_domains; serv; serv = serv->next, count++)
daemon->serverarray[count] = serv;
qsort(daemon->serverarray, daemon->serverarraysz, sizeof(struct server *), order_qsort);
/* servers need the location in the array to find all the whole
set of equivalent servers from a pointer to a single one. */
for (count = 0; count < daemon->serverarraysz; count++)
if (!(daemon->serverarray[count]->flags & SERV_LITERAL_ADDRESS))
if (!(daemon->serverarray[count]->flags & SERV_IS_LOCAL))
daemon->serverarray[count]->arrayposn = count;
}
@@ -75,22 +97,22 @@ void build_server_array(void)
A flag of F_DNSSECOK returns a DNSSEC capable server only and
also 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.
return 0 if nothing found, 1 otherwise.
*/
int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
int lookup_domain(char *domain, int flags, int *lowout, int *highout)
{
int rc, nodots, leading_dot = 1;
ssize_t qlen, maxlen;
int rc, crop_query, nodots;
ssize_t qlen;
int try, high, low = 0;
int nlow = 0, nhigh = 0;
char *cp;
char *cp, *qdomain = domain;
/* may be no configured servers. */
if (daemon->serverarraysz == 0)
return 0;
maxlen = strlen(daemon->serverarray[0]->domain);
/* find query length and presence of '.' */
for (cp = qdomain, nodots = 1, qlen = 0; *cp; qlen++, cp++)
if (*cp == '.')
@@ -101,88 +123,126 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout)
if (qlen == 0 || flags & F_DNSSECOK)
nodots = 0;
/* No point trying to match more than the largest server domain */
if (qlen > maxlen)
{
qdomain += qlen - maxlen;
qlen = maxlen;
leading_dot = 0;
}
/* Search shorter and shorter RHS substrings for a match */
while (qlen >= 0)
{
/* Note that when we chop off a character, all the possible matches
/* Note that when we chop off a label, all the possible matches
MUST be at a larger index than the nearest failing match with one more
character, since the array is sorted longest to smallest. Hence
we don't reset low here. */
we don't reset low to zero here, we can go further below and crop the
search string to the size of the largest remaining server
when this match fails. */
high = daemon->serverarraysz;
crop_query = 1;
/* binary search */
do
while (1)
{
try = (low + high)/2;
if ((rc = order(qdomain, leading_dot, qlen, daemon->serverarray[try])) == 0)
if ((rc = order(qdomain, qlen, daemon->serverarray[try])) == 0)
break;
if (rc < 0)
if (rc < 0)
{
if (high == try)
break;
{
/* qdomain is longer or same length as longest domain, and try == 0
crop the query to the longest domain. */
crop_query = qlen - daemon->serverarray[try]->domain_len;
break;
}
high = try;
}
else
{
if (low == try)
break;
{
/* try now points to the last domain that sorts before the query, so
we know that a substring of the query shorter than it is required to match, so
find the largest domain that's shorter than try. Note that just going to
try+1 is not optimal, consider searching bbb in (aaa,ccc,bb). try will point
to aaa, since ccc sorts after bbb, but the first domain that has a chance to
match is bb. So find the length of the first domain later than try which is
is shorter than it.
There's a nasty edge case when qdomain sorts before _any_ of the
server domains, where try _doesn't point_ to the last domain that sorts
before the query, since no such domain exists. In that case, the loop
exits via the rc < 0 && high == try path above and this code is
not executed. */
ssize_t len, old = daemon->serverarray[try]->domain_len;
while (++try != daemon->serverarraysz)
{
if (old != (len = daemon->serverarray[try]->domain_len))
{
crop_query = qlen - len;
break;
}
}
break;
}
low = try;
}
}
while (low != high);
};
if (rc == 0)
{
/* We've matched a setting which says to use servers without a domain.
Continue the search with empty query (the last character gets stripped
by the loop. */
if (daemon->serverarray[try]->flags & SERV_USE_RESOLV)
int found = 1;
if (daemon->server_has_wildcard)
{
qdomain += qlen - 1;
qlen = 1;
/* if we have example.com and *example.com we need to check against *example.com,
but the binary search may have found either. Use the fact that example.com is sorted before *example.com
We favour example.com in the case that both match (ie www.example.com) */
while (try != 0 && order(qdomain, qlen, daemon->serverarray[try-1]) == 0)
try--;
if (!(qdomain == domain || *qdomain == 0 || *(qdomain-1) == '.'))
{
while (try < daemon->serverarraysz-1 && order(qdomain, qlen, daemon->serverarray[try+1]) == 0)
try++;
if (!(daemon->serverarray[try]->flags & SERV_WILDCARD))
found = 0;
}
}
else
if (found && filter_servers(try, flags, &nlow, &nhigh))
/* We have a match, but it may only be (say) an IPv6 address, and
if the query wasn't for an AAAA record, it's no good, and we need
to continue generalising */
{
/* We have a match, but it may only be (say) an IPv6 address, and
if the query wasn't for an AAAA record, it's no good, and we need
to continue generalising */
if (filter_servers(try, flags, &nlow, &nhigh))
/* We've matched a setting which says to use servers without a domain.
Continue the search with empty query */
if (daemon->serverarray[nlow]->flags & SERV_USE_RESOLV)
crop_query = qlen;
else
break;
}
}
/* crop_query must be at least one always. */
if (crop_query == 0)
crop_query = 1;
if (leading_dot)
leading_dot = 0;
else
{
qlen--;
qdomain++;
}
/* strip chars off the query based on the largest possible remaining match,
then continue to the start of the next label unless we have a wildcard
domain somewhere, in which case we have to go one at a time. */
qlen -= crop_query;
qdomain += crop_query;
if (!daemon->server_has_wildcard)
while (qlen > 0 && (*(qdomain-1) != '.'))
qlen--, qdomain++;
}
/* domain has no dots, and we have at least one server configured to handle such,
These servers always sort to the very end of the array.
A configured server eg server=/lan/ will take precdence. */
if (nodots &&
(daemon->serverarray[daemon->serverarraysz-1]->flags & SERV_FOR_NODOTS) &&
(nlow == nhigh || strlen(daemon->serverarray[nlow]->domain) == 0))
(nlow == nhigh || daemon->serverarray[nlow]->domain_len == 0))
filter_servers(daemon->serverarraysz-1, flags, &nlow, &nhigh);
/* F_DOMAINSRV returns only domain-specific servers, so if we got to a
general server, return empty set. */
if (nlow != nhigh && (flags & F_DOMAINSRV) && strlen(daemon->serverarray[nlow]->domain) == 0)
nlow = nhigh;
if (lowout)
*lowout = nlow;
@@ -195,6 +255,11 @@ int lookup_domain(char *qdomain, 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;
}
int filter_servers(int seed, int flags, int *lowout, int *highout)
{
@@ -208,51 +273,83 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
nlow--;
while (nhigh < daemon->serverarraysz-1 && order_servers(daemon->serverarray[nhigh], daemon->serverarray[nhigh+1]) == 0)
nhigh++;
nhigh++;
nhigh++;
/* Now the servers are on order between low and high, in the order
return zero for both, IPv6 addr, IPv4 addr, no-data return, send upstream.
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 (i != nlow && (flags & F_IPV6))
nhigh = i;
#define SERV_LOCAL_ADDRESS (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)
if (flags & F_CONFIG)
{
/* We're just lookin for any matches that return an RR. */
for (i = nlow; i < nhigh; i++)
if (daemon->serverarray[i]->flags & SERV_LOCAL_ADDRESS)
break;
/* failed, return failure. */
if (i == nhigh)
nhigh = nlow;
}
else
{
nlow = i;
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_4ADDR); i++);
/* 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.
See which of those match our query in that priority order and narrow (low, high) */
if (i != nlow && (flags & F_IPV4))
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
if (i != nlow && (flags & F_IPV6))
nhigh = i;
else
{
nlow = i;
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_ALL_ZEROS); i++);
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_4ADDR); i++);
if (i != nlow && (flags & (F_IPV4 | F_IPV6)))
if (i != nlow && (flags & F_IPV4))
nhigh = i;
else
{
nlow = i;
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_ALL_ZEROS); i++);
/* --local=/domain/, only return if we don't need a server. */
if (i != nlow && !(flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER)))
if (i != nlow && (flags & (F_IPV4 | F_IPV6)))
nhigh = i;
else
{
nlow = i;
/* If we want a server that can do DNSSEC, and this one can't,
return nothing. */
if ((flags & F_DNSSECOK) && !(daemon->serverarray[nlow]->flags & SERV_DO_DNSSEC))
nlow = nhigh;
/* Short to resolv.conf servers */
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++);
if (i != nlow)
nhigh = i;
else
{
/* now look for a server */
for (i = nlow; i < nhigh && !(daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); 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;
}
else
{
/* --local=/domain/, only return if we don't need a server. */
if (flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER))
nhigh = i;
}
}
}
}
}
@@ -268,7 +365,7 @@ int is_local_answer(time_t now, int first, char *name)
{
int flags = 0;
int rc = 0;
if ((flags = daemon->serverarray[first]->flags) & SERV_LITERAL_ADDRESS)
{
if (flags & SERV_4ADDR)
@@ -278,12 +375,76 @@ int is_local_answer(time_t now, int first, char *name)
else if (flags & SERV_ALL_ZEROS)
rc = F_IPV4 | F_IPV6;
else
rc = check_for_local_domain(name, now) ? F_NOERR : F_NXDOMAIN;
{
/* argument first is the first struct server which matches the query type;
now roll back to the server which is just the same domain, to check if that
provides an answer of a different type. */
for (;first > 0 && order_servers(daemon->serverarray[first-1], daemon->serverarray[first]) == 0; first--);
if ((daemon->serverarray[first]->flags & SERV_LOCAL_ADDRESS) ||
check_for_local_domain(name, now))
rc = F_NOERR;
else
rc = F_NXDOMAIN;
}
}
return rc;
}
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede)
{
int trunc = 0, anscount = 0;
unsigned char *p;
int start;
union all_addr addr;
if (flags & (F_NXDOMAIN | F_NOERR))
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL, 0);
setup_reply(header, flags, ede);
if (!(p = skip_questions(header, size)))
return 0;
if (flags & gotname & F_IPV4)
for (start = first; start != last; start++)
{
struct serv_addr4 *srv = (struct serv_addr4 *)daemon->serverarray[start];
if (srv->flags & SERV_ALL_ZEROS)
memset(&addr, 0, sizeof(addr));
else
addr.addr4 = srv->addr;
if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr))
anscount++;
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL, 0);
}
if (flags & gotname & F_IPV6)
for (start = first; start != last; start++)
{
struct serv_addr6 *srv = (struct serv_addr6 *)daemon->serverarray[start];
if (srv->flags & SERV_ALL_ZEROS)
memset(&addr, 0, sizeof(addr));
else
addr.addr6 = srv->addr;
if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr))
anscount++;
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL, 0);
}
if (trunc)
header->hb3 |= HB3_TC;
header->ancount = htons(anscount);
return p - (unsigned char *)header;
}
#ifdef HAVE_DNSSEC
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp)
{
@@ -316,20 +477,16 @@ int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp)
#endif
/* order by size, then by dictionary order */
static int order(char *qdomain, int leading_dot, size_t qlen, struct server *serv)
static int order(char *qdomain, size_t qlen, struct server *serv)
{
size_t dlen = 0;
int rc;
/* servers for dotless names always sort last
searched for name is never dotless. */
if (serv->flags & SERV_FOR_NODOTS)
return -1;
if (leading_dot)
qlen++;
dlen = strlen(serv->domain);
dlen = serv->domain_len;
if (qlen < dlen)
return 1;
@@ -337,22 +494,27 @@ static int order(char *qdomain, int leading_dot, size_t qlen, struct server *ser
if (qlen > dlen)
return -1;
if (leading_dot && (rc = '.' - serv->domain[0]) != 0)
return rc;
return strcmp(qdomain, leading_dot ? &serv->domain[1] : serv->domain);
return strcmp(qdomain, serv->domain);
}
static int order_servers(struct server *s1, struct server *s2)
{
size_t dlen = strlen(s1->domain);
int rc;
/* need full comparison of dotless servers in
order_qsort() and filter_servers() */
if (s1->flags & SERV_FOR_NODOTS)
/* need full comparison of dotless servers in
order_qsort() and filter_servers() */
if (s1->flags & SERV_FOR_NODOTS)
return (s2->flags & SERV_FOR_NODOTS) ? 0 : 1;
return order(s1->domain, 0, dlen, s2);
if ((rc = order(s1->domain, s1->domain_len, s2)) != 0)
return rc;
/* For identical domains, sort wildcard ones first */
if (s1->flags & SERV_WILDCARD)
return (s2->flags & SERV_WILDCARD) ? 0 : 1;
return (s2->flags & SERV_WILDCARD) ? -1 : 0;
}
static int order_qsort(const void *a, const void *b)
@@ -365,10 +527,12 @@ 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. */
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. */
if (rc == 0)
rc = (s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS)) -
(s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS));
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);
/* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
if (rc == 0)
@@ -377,3 +541,167 @@ static int order_qsort(const void *a, const void *b)
return rc;
}
void mark_servers(int flag)
{
struct server *serv;
/* mark everything with argument flag */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & flag)
serv->flags |= SERV_MARK;
else
serv->flags &= ~SERV_MARK;
for (serv = daemon->local_domains; serv; serv = serv->next)
if (serv->flags & flag)
serv->flags |= SERV_MARK;
else
serv->flags &= ~SERV_MARK;
}
void cleanup_servers(void)
{
struct server *serv, *tmp, **up;
/* unlink and free anything still marked. */
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
server_gone(serv);
*up = serv->next;
free(serv->domain);
free(serv);
}
else
{
up = &serv->next;
daemon->servers_tail = serv;
}
}
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
*up = serv->next;
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
}
int add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain,
union all_addr *local_addr)
{
struct server *serv = NULL;
char *alloc_domain;
if (!domain)
domain = "";
/* .domain == domain, for historical reasons. */
if (*domain == '.')
while (*domain == '.') domain++;
else if (*domain == '*')
{
domain++;
if (*domain != 0)
flags |= SERV_WILDCARD;
}
if (*domain == 0)
alloc_domain = whine_malloc(1);
else
alloc_domain = canonicalise((char *)domain, NULL);
if (!alloc_domain)
return 0;
/* See if there is a suitable candidate, and unmark
only do this for forwarding servers, not
address or local, to avoid delays on large numbers. */
if (!(flags & SERV_IS_LOCAL))
for (serv = daemon->servers; serv; serv = serv->next)
if ((serv->flags & SERV_MARK) &&
hostname_isequal(alloc_domain, serv->domain))
break;
if (serv)
{
free(alloc_domain);
alloc_domain = serv->domain;
}
else
{
size_t size;
if (flags & SERV_IS_LOCAL)
{
if (flags & SERV_6ADDR)
size = sizeof(struct serv_addr6);
else if (flags & SERV_4ADDR)
size = sizeof(struct serv_addr4);
else
size = sizeof(struct serv_local);
}
else
size = sizeof(struct server);
if (!(serv = whine_malloc(size)))
{
free(alloc_domain);
return 0;
}
if (flags & SERV_IS_LOCAL)
{
serv->next = daemon->local_domains;
daemon->local_domains = serv;
if (flags & SERV_4ADDR)
((struct serv_addr4*)serv)->addr = local_addr->addr4;
if (flags & SERV_6ADDR)
((struct serv_addr6*)serv)->addr = local_addr->addr6;
}
else
{
memset(serv, 0, sizeof(struct server));
/* Add to the end of the chain, for order */
if (daemon->servers_tail)
daemon->servers_tail->next = serv;
else
daemon->servers = serv;
daemon->servers_tail = serv;
#ifdef HAVE_LOOP
serv->uid = rand32();
#endif
if (interface)
safe_strncpy(serv->interface, interface, sizeof(serv->interface));
if (addr)
serv->addr = *addr;
if (source_addr)
serv->source_addr = *source_addr;
}
}
serv->flags = flags;
serv->domain = alloc_domain;
serv->domain_len = strlen(alloc_domain);
return 1;
}
+36 -32
View File
@@ -18,8 +18,9 @@
static struct cond_domain *search_domain(struct in_addr addr, struct cond_domain *c);
static int match_domain(struct in_addr addr, struct cond_domain *c);
static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c);
static int match_domain6(struct in6_addr *addr, struct cond_domain *c);
int is_name_synthetic(int flags, char *name, union all_addr *addr)
{
@@ -135,28 +136,9 @@ int is_name_synthetic(int flags, char *name, union all_addr *addr)
}
if (hostname_isequal(c->domain, p+1) && inet_pton(prot, tail, addr))
{
if (prot == AF_INET)
{
if (!c->is6 &&
ntohl(addr->addr4.s_addr) >= ntohl(c->start.s_addr) &&
ntohl(addr->addr4.s_addr) <= ntohl(c->end.s_addr))
found = 1;
}
else
{
u64 addrpart = addr6part(&addr->addr6);
if (c->is6 &&
is_same_net6(&addr->addr6, &c->start6, 64) &&
addrpart >= addr6part(&c->start6) &&
addrpart <= addr6part(&c->end6))
found = 1;
}
}
found = (prot == AF_INET) ? match_domain(addr->addr4, c) : match_domain6(&addr->addr6, c);
}
/* restore name */
for (p = tail; *p; p++)
if (*p == '.' || *p == ':')
@@ -246,14 +228,22 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
}
static int match_domain(struct in_addr addr, struct cond_domain *c)
{
if (!c->is6 &&
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
return 1;
return 0;
}
static struct cond_domain *search_domain(struct in_addr addr, struct cond_domain *c)
{
for (; c; c = c->next)
if (!c->is6 &&
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
if (match_domain(addr, c))
return c;
return NULL;
}
@@ -267,16 +257,30 @@ char *get_domain(struct in_addr addr)
return daemon->domain_suffix;
}
static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c)
static int match_domain6(struct in6_addr *addr, struct cond_domain *c)
{
u64 addrpart = addr6part(addr);
if (c->is6)
{
if (c->prefixlen >= 64)
{
if (is_same_net6(addr, &c->start6, 64) &&
addrpart >= addr6part(&c->start6) &&
addrpart <= addr6part(&c->end6))
return 1;
}
else if (is_same_net6(addr, &c->start6, c->prefixlen))
return 1;
}
return 0;
}
static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c)
{
for (; c; c = c->next)
if (c->is6 &&
is_same_net6(addr, &c->start6, 64) &&
addrpart >= addr6part(&c->start6) &&
addrpart <= addr6part(&c->end6))
if (match_domain6(addr, c))
return c;
return NULL;
+598 -407
View File
File diff suppressed because it is too large. Load diff
+7 -7
View File
@@ -76,9 +76,9 @@ unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name
#else /* HAVE_DNSSEC || HAVE_CRYPTOHASH */
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
typedef unsigned char BYTE; // 8-bit byte
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
#define SHA256_BLOCK_SIZE 32 /* SHA256 outputs a 32 byte digest */
typedef unsigned char BYTE; /* 8-bit byte */
typedef unsigned int WORD; /* 32-bit word, change to "long" for 16-bit machines */
typedef struct {
BYTE data[64];
@@ -238,7 +238,7 @@ static void sha256_final(SHA256_CTX *ctx, BYTE hash[])
i = ctx->datalen;
// Pad whatever data is left in the buffer.
/* Pad whatever data is left in the buffer. */
if (ctx->datalen < 56)
{
ctx->data[i++] = 0x80;
@@ -254,7 +254,7 @@ static void sha256_final(SHA256_CTX *ctx, BYTE hash[])
memset(ctx->data, 0, 56);
}
// Append to the padding the total message's length in bits and transform.
/* Append to the padding the total message's length in bits and transform. */
ctx->bitlen += ctx->datalen * 8;
ctx->data[63] = ctx->bitlen;
ctx->data[62] = ctx->bitlen >> 8;
@@ -266,8 +266,8 @@ static void sha256_final(SHA256_CTX *ctx, BYTE hash[])
ctx->data[56] = ctx->bitlen >> 56;
sha256_transform(ctx, ctx->data);
// Since this implementation uses little endian byte ordering and SHA uses big endian,
// reverse all the bytes when copying the final state to the output hash.
/* Since this implementation uses little endian byte ordering and SHA uses big endian,
reverse all the bytes when copying the final state to the output hash. */
for (i = 0; i < 4; ++i)
{
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
+9 -4
View File
@@ -235,7 +235,6 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
}
else
continue;
/* stringify MAC into dhcp_buff */
p = daemon->dhcp_buff;
@@ -433,7 +432,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
buf = grab_extradata_lua(buf, end, "relay_address");
else if (data.giaddr.s_addr != 0)
{
lua_pushstring(lua, inet_ntoa(data.giaddr));
inet_ntop(AF_INET, &data.giaddr, daemon->addrbuff, ADDRSTRLEN);
lua_pushstring(lua, daemon->addrbuff);
lua_setfield(lua, -2, "relay_address");
}
@@ -611,8 +611,13 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
if (is6)
buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
else
my_setenv("DNSMASQ_RELAY_ADDRESS", data.giaddr.s_addr != 0 ? inet_ntoa(data.giaddr) : NULL, &err);
else
{
const char *giaddr = NULL;
if (data.giaddr.s_addr != 0)
giaddr = inet_ntop(AF_INET, &data.giaddr, daemon->addrbuff, ADDRSTRLEN);
my_setenv("DNSMASQ_RELAY_ADDRESS", giaddr, &err);
}
for (i = 0; buf; i++)
{
+8 -1
View File
@@ -148,12 +148,19 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
if (!(ah->flags & flag))
continue;
if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
if (stat(ah->fname, &buf) == -1)
{
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
ah->fname, strerror(errno));
continue;
}
if (!(S_ISDIR(buf.st_mode)))
{
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
ah->fname, _("not a directory"));
continue;
}
if (!(ah->flags & AH_WD_DONE))
{
+1
View File
@@ -1021,6 +1021,7 @@ void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, ch
}
kill_name(lease_tmp);
lease_tmp->flags |= LEASE_CHANGED; /* run script on change */
break;
}
}
+4 -2
View File
@@ -31,11 +31,13 @@ void loop_send_probes()
identifiable, via the uid. If we see that query back again, then the server is looping, and we should not use it. */
for (serv = daemon->servers; serv; serv = serv->next)
if (strlen(serv->domain) == 0 &&
!(serv->flags & (SERV_FOR_NODOTS | SERV_LOOP)))
!(serv->flags & (SERV_FOR_NODOTS)))
{
ssize_t len = loop_make_probe(serv->uid);
int fd;
serv->flags &= ~SERV_LOOP;
if ((fd = allocate_rfd(&rfds, serv)) == -1)
continue;
@@ -101,7 +103,7 @@ int detect_loop(char *query, int type)
uid == serv->uid)
{
serv->flags |= SERV_LOOP;
check_servers(); /* log new state */
check_servers(1); /* log new state - don't send more probes. */
return 1;
}
+22 -152
View File
@@ -1494,149 +1494,7 @@ void pre_allocate_sfds(void)
}
}
void mark_servers(int flag)
{
struct server *serv;
/* mark everything with argument flag */
for (serv = daemon->servers; serv; serv = serv->next)
{
if (serv->flags & flag)
serv->flags |= SERV_MARK;
#ifdef HAVE_LOOP
/* Give looped servers another chance */
serv->flags &= ~SERV_LOOP;
#endif
}
for (serv = daemon->local_domains; serv; serv = serv->next)
if (serv->flags & flag)
serv->flags |= SERV_MARK;
}
void cleanup_servers(void)
{
struct server *serv, *tmp, **up;
/* unlink and free anything still marked. */
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
server_gone(serv);
*up = serv->next;
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
*up = serv->next;
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
#ifdef HAVE_LOOP
/* Now we have a new set of servers, test for loops. */
loop_send_probes();
#endif
}
void add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain)
{
struct server *serv;
char *domain_str;
if (!domain)
domain = "";
/* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain.
NOTE that we can get local=/domain/ here, but NOT address=/domain/1.2.3.4 */
#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
/* See if there is a suitable candidate, and unmark */
for (serv = (flags & SERV_IS_LOCAL) ? daemon->local_domains : daemon->servers; serv; serv = serv->next)
if ((serv->flags & SERV_MARK) && hostname_isequal(domain, serv->domain))
break;
if (serv)
domain_str = serv->domain;
else if ((serv = whine_malloc(sizeof (struct server))))
{
/* Not found, create a new one. */
if (!(domain_str = whine_malloc(strlen(domain)+1)))
{
free(serv);
serv = NULL;
}
else
{
strcpy(domain_str, domain);
if (flags & SERV_IS_LOCAL)
{
serv->next = daemon->local_domains;
daemon->local_domains = serv;
}
else
{
struct server *s;
/* Add to the end of the chain, for order */
if (!daemon->servers)
daemon->servers = serv;
else
{
for (s = daemon->servers; s->next; s = s->next);
s->next = serv;
}
serv->next = NULL;
}
}
}
if (serv)
{
if (!(flags & SERV_IS_LOCAL))
memset(serv, 0, sizeof(struct server));
serv->flags = flags;
serv->domain = domain_str;
if (!(flags & SERV_IS_LOCAL))
{
serv->queries = serv->failed_queries = 0;
#ifdef HAVE_LOOP
serv->uid = rand32();
#endif
if (interface)
safe_strncpy(serv->interface, interface, sizeof(serv->interface));
if (addr)
serv->addr = *addr;
if (source_addr)
serv->source_addr = *source_addr;
}
}
}
void check_servers(void)
void check_servers(int no_loop_check)
{
struct irec *iface;
struct server *serv;
@@ -1644,7 +1502,15 @@ void check_servers(void)
int port = 0, count;
int locals = 0;
/* interface may be new since startup */
#ifdef HAVE_LOOP
if (!no_loop_check)
loop_send_probes();
#endif
/* clear all marks. */
mark_servers(0);
/* interface may be new since startup */
if (!option_bool(OPT_NOWILD))
enumerate_interfaces(0);
@@ -1720,12 +1586,15 @@ void check_servers(void)
if (serv->sfd)
serv->sfd->used = 1;
if (count == SERVERS_LOGGED)
my_syslog(LOG_INFO, _("more servers are defined but not logged"));
if (++count > SERVERS_LOGGED)
continue;
if (strlen(serv->domain) != 0 || (serv->flags & SERV_FOR_NODOTS))
{
char *s1, *s2, *s3 = "";
char *s1, *s2, *s3 = "", *s4 = "";
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
@@ -1736,9 +1605,9 @@ void check_servers(void)
else if (strlen(serv->domain) == 0)
s1 = _("default"), s2 = "";
else
s1 = _("domain"), s2 = serv->domain;
s1 = _("domain"), s2 = serv->domain, s4 = (serv->flags & SERV_WILDCARD) ? "*" : "";
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s %s"), daemon->namebuff, port, s1, s2, s3);
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s%s %s"), daemon->namebuff, port, s1, s4, s2, s3);
}
#ifdef HAVE_LOOP
else if (serv->flags & SERV_LOOP)
@@ -1757,7 +1626,8 @@ void check_servers(void)
continue;
if ((serv->flags & SERV_LITERAL_ADDRESS) &&
!(serv->flags & (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)))
!(serv->flags & (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)) &&
strlen(serv->domain))
{
count--;
if (++locals <= LOCALS_LOGGED)
@@ -1786,8 +1656,8 @@ void check_servers(void)
up = &sfd->next;
}
cleanup_servers();
build_server_array();
cleanup_servers(); /* remove servers we just deleted. */
build_server_array();
}
/* Return zero if no servers found, in that case we keep polling.
@@ -1822,7 +1692,7 @@ int reload_servers(char *fname)
memset(&addr, 0, sizeof(addr));
memset(&source_addr, 0, sizeof(source_addr));
if ((addr.in.sin_addr.s_addr = inet_addr(token)) != (in_addr_t) -1)
if (inet_pton(AF_INET, token, &addr.in.sin_addr) > 0)
{
#ifdef HAVE_SOCKADDR_SA_LEN
source_addr.in.sin_len = addr.in.sin_len = sizeof(source_addr.in);
@@ -1860,7 +1730,7 @@ int reload_servers(char *fname)
continue;
}
add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL);
add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL, NULL);
gotone = 1;
}
+94
View File
@@ -0,0 +1,94 @@
/* dnsmasq is Copyright (c) 2000-2021 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/>.
*/
#include "dnsmasq.h"
#if defined (HAVE_NFTSET) && defined (HAVE_LINUX_NETWORK)
#include <nftables/libnftables.h>
#include <string.h>
#include <arpa/inet.h>
static struct nft_ctx *ctx = NULL;
static const char *cmd_add = "add element %s { %s }";
static const char *cmd_del = "delete element %s { %s }";
void nftset_init()
{
ctx = nft_ctx_new(NFT_CTX_DEFAULT);
if (ctx == NULL)
die(_("failed to create nftset context"), NULL, EC_MISC);
/* disable libnftables output */
nft_ctx_buffer_error(ctx);
}
int add_to_nftset(const char *setname, const union all_addr *ipaddr, int flags, int remove)
{
const char *cmd = remove ? cmd_del : cmd_add;
int ret, af = (flags & F_IPV4) ? AF_INET : AF_INET6;
size_t new_sz;
char *new, *err, *nl;
static char *cmd_buf = NULL;
static size_t cmd_buf_sz = 0;
inet_ntop(af, ipaddr, daemon->addrbuff, ADDRSTRLEN);
if (setname[1] == ' ' && (setname[0] == '4' || setname[0] == '6'))
{
if (setname[0] == '4' && !(flags & F_IPV4))
return -1;
if (setname[0] == '6' && !(flags & F_IPV6))
return -1;
setname += 2;
}
if (cmd_buf_sz == 0)
new_sz = 150; /* initial allocation */
else
new_sz = snprintf(cmd_buf, cmd_buf_sz, cmd, setname, daemon->addrbuff);
if (new_sz > cmd_buf_sz)
{
if (!(new = whine_malloc(new_sz + 10)))
return 0;
if (cmd_buf)
free(cmd_buf);
cmd_buf = new;
cmd_buf_sz = new_sz + 10;
snprintf(cmd_buf, cmd_buf_sz, cmd, setname, daemon->addrbuff);
}
ret = nft_run_cmd_from_buffer(ctx, cmd_buf);
err = (char *)nft_ctx_get_error_buffer(ctx);
if (ret != 0)
{
/* Log only first line of error return. */
if ((nl = strchr(err, '\n')))
*nl = 0;
my_syslog(LOG_ERR, "nftset %s %s", setname, err);
}
return ret;
}
#endif
+483 -271
View File
File diff suppressed because it is too large. Load diff
+386
View File
@@ -0,0 +1,386 @@
/* dnsmasq is Copyright (c) 2000-2021 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/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_CONNTRACK
#define LOG(...) \
do { \
my_syslog(LOG_DEBUG, __VA_ARGS__); \
} while (0)
#define ASSERT(condition) \
do { \
if (!(condition)) \
my_syslog(LOG_ERR, _("[pattern.c:%d] Assertion failure: %s"), __LINE__, #condition); \
} while (0)
/**
* Determines whether a given string value matches against a glob pattern
* which may contain zero-or-more-character wildcards denoted by '*'.
*
* Based on "Glob Matching Can Be Simple And Fast Too" by Russ Cox,
* See https://research.swtch.com/glob
*
* @param value A string value.
* @param num_value_bytes The number of bytes of the string value.
* @param pattern A glob pattern.
* @param num_pattern_bytes The number of bytes of the glob pattern.
*
* @return 1 If the provided value matches against the glob pattern.
* @return 0 Otherwise.
*/
static int is_string_matching_glob_pattern(
const char *value,
size_t num_value_bytes,
const char *pattern,
size_t num_pattern_bytes)
{
ASSERT(value);
ASSERT(pattern);
size_t value_index = 0;
size_t next_value_index = 0;
size_t pattern_index = 0;
size_t next_pattern_index = 0;
while (value_index < num_value_bytes || pattern_index < num_pattern_bytes)
{
if (pattern_index < num_pattern_bytes)
{
char pattern_character = pattern[pattern_index];
if ('a' <= pattern_character && pattern_character <= 'z')
pattern_character -= 'a' - 'A';
if (pattern_character == '*')
{
/* zero-or-more-character wildcard */
/* Try to match at value_index, otherwise restart at value_index + 1 next. */
next_pattern_index = pattern_index;
pattern_index++;
if (value_index < num_value_bytes)
next_value_index = value_index + 1;
else
next_value_index = 0;
continue;
}
else
{
/* ordinary character */
if (value_index < num_value_bytes)
{
char value_character = value[value_index];
if ('a' <= value_character && value_character <= 'z')
value_character -= 'a' - 'A';
if (value_character == pattern_character)
{
pattern_index++;
value_index++;
continue;
}
}
}
}
if (next_value_index)
{
pattern_index = next_pattern_index;
value_index = next_value_index;
continue;
}
return 0;
}
return 1;
}
/**
* Determines whether a given string value represents a valid DNS name.
*
* - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels
* delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only
* ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen.
*
* - A valid name must be fully qualified, i.e., consist of at least two labels.
* The final label must not be fully numeric, and must not be the "local" pseudo-TLD.
*
* - Examples:
* Valid: "example.com"
* Invalid: "ipcamera", "ipcamera.local", "8.8.8.8"
*
* @param value A string value.
*
* @return 1 If the provided string value is a valid DNS name.
* @return 0 Otherwise.
*/
int is_valid_dns_name(const char *value)
{
ASSERT(value);
size_t num_bytes = 0;
size_t num_labels = 0;
const char *label = NULL;
int is_label_numeric = 1;
for (const char *c = value;; c++)
{
if (*c &&
*c != '-' && *c != '.' &&
(*c < '0' || *c > '9') &&
(*c < 'A' || *c > 'Z') &&
(*c < 'a' || *c > 'z'))
{
LOG(_("Invalid DNS name: Invalid character %c."), *c);
return 0;
}
if (*c)
num_bytes++;
if (!label)
{
if (!*c || *c == '.')
{
LOG(_("Invalid DNS name: Empty label."));
return 0;
}
if (*c == '-')
{
LOG(_("Invalid DNS name: Label starts with hyphen."));
return 0;
}
label = c;
}
if (*c && *c != '.')
{
if (*c < '0' || *c > '9')
is_label_numeric = 0;
}
else
{
if (c[-1] == '-')
{
LOG(_("Invalid DNS name: Label ends with hyphen."));
return 0;
}
size_t num_label_bytes = (size_t) (c - label);
if (num_label_bytes > 63)
{
LOG(_("Invalid DNS name: Label is too long (%zu)."), num_label_bytes);
return 0;
}
num_labels++;
if (!*c)
{
if (num_labels < 2)
{
LOG(_("Invalid DNS name: Not enough labels (%zu)."), num_labels);
return 0;
}
if (is_label_numeric)
{
LOG(_("Invalid DNS name: Final label is fully numeric."));
return 0;
}
if (num_label_bytes == 5 &&
(label[0] == 'l' || label[0] == 'L') &&
(label[1] == 'o' || label[1] == 'O') &&
(label[2] == 'c' || label[2] == 'C') &&
(label[3] == 'a' || label[3] == 'A') &&
(label[4] == 'l' || label[4] == 'L'))
{
LOG(_("Invalid DNS name: \"local\" pseudo-TLD."));
return 0;
}
if (num_bytes < 1 || num_bytes > 253)
{
LOG(_("DNS name has invalid length (%zu)."), num_bytes);
return 0;
}
return 1;
}
label = NULL;
is_label_numeric = 1;
}
}
}
/**
* Determines whether a given string value represents a valid DNS name pattern.
*
* - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels
* delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only
* ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen.
*
* - Patterns follow the syntax of DNS names, but additionally allow the wildcard character "*" to be used up to
* twice per label to match 0 or more characters within that label. Note that the wildcard never matches a dot
* (e.g., "*.example.com" matches "api.example.com" but not "api.us.example.com").
*
* - A valid name or pattern must be fully qualified, i.e., consist of at least two labels.
* The final label must not be fully numeric, and must not be the "local" pseudo-TLD.
* A pattern must end with at least two literal (non-wildcard) labels.
*
* - Examples:
* Valid: "example.com", "*.example.com", "video*.example.com", "api*.*.example.com", "*-prod-*.example.com"
* Invalid: "ipcamera", "ipcamera.local", "*", "*.com", "8.8.8.8"
*
* @param value A string value.
*
* @return 1 If the provided string value is a valid DNS name pattern.
* @return 0 Otherwise.
*/
int is_valid_dns_name_pattern(const char *value)
{
ASSERT(value);
size_t num_bytes = 0;
size_t num_labels = 0;
const char *label = NULL;
int is_label_numeric = 1;
size_t num_wildcards = 0;
int previous_label_has_wildcard = 1;
for (const char *c = value;; c++)
{
if (*c &&
*c != '*' && /* Wildcard. */
*c != '-' && *c != '.' &&
(*c < '0' || *c > '9') &&
(*c < 'A' || *c > 'Z') &&
(*c < 'a' || *c > 'z'))
{
LOG(_("Invalid DNS name pattern: Invalid character %c."), *c);
return 0;
}
if (*c && *c != '*')
num_bytes++;
if (!label)
{
if (!*c || *c == '.')
{
LOG(_("Invalid DNS name pattern: Empty label."));
return 0;
}
if (*c == '-')
{
LOG(_("Invalid DNS name pattern: Label starts with hyphen."));
return 0;
}
label = c;
}
if (*c && *c != '.')
{
if (*c < '0' || *c > '9')
is_label_numeric = 0;
if (*c == '*')
{
if (num_wildcards >= 2)
{
LOG(_("Invalid DNS name pattern: Wildcard character used more than twice per label."));
return 0;
}
num_wildcards++;
}
}
else
{
if (c[-1] == '-')
{
LOG(_("Invalid DNS name pattern: Label ends with hyphen."));
return 0;
}
size_t num_label_bytes = (size_t) (c - label) - num_wildcards;
if (num_label_bytes > 63)
{
LOG(_("Invalid DNS name pattern: Label is too long (%zu)."), num_label_bytes);
return 0;
}
num_labels++;
if (!*c)
{
if (num_labels < 2)
{
LOG(_("Invalid DNS name pattern: Not enough labels (%zu)."), num_labels);
return 0;
}
if (num_wildcards != 0 || previous_label_has_wildcard)
{
LOG(_("Invalid DNS name pattern: Wildcard within final two labels."));
return 0;
}
if (is_label_numeric)
{
LOG(_("Invalid DNS name pattern: Final label is fully numeric."));
return 0;
}
if (num_label_bytes == 5 &&
(label[0] == 'l' || label[0] == 'L') &&
(label[1] == 'o' || label[1] == 'O') &&
(label[2] == 'c' || label[2] == 'C') &&
(label[3] == 'a' || label[3] == 'A') &&
(label[4] == 'l' || label[4] == 'L'))
{
LOG(_("Invalid DNS name pattern: \"local\" pseudo-TLD."));
return 0;
}
if (num_bytes < 1 || num_bytes > 253)
{
LOG(_("DNS name pattern has invalid length after removing wildcards (%zu)."), num_bytes);
return 0;
}
return 1;
}
label = NULL;
is_label_numeric = 1;
previous_label_has_wildcard = num_wildcards != 0;
num_wildcards = 0;
}
}
}
/**
* Determines whether a given DNS name matches against a DNS name pattern.
*
* @param name A valid DNS name.
* @param pattern A valid DNS name pattern.
*
* @return 1 If the provided DNS name matches against the DNS name pattern.
* @return 0 Otherwise.
*/
int is_dns_name_matching_pattern(const char *name, const char *pattern)
{
ASSERT(name);
ASSERT(is_valid_dns_name(name));
ASSERT(pattern);
ASSERT(is_valid_dns_name_pattern(pattern));
const char *n = name;
const char *p = pattern;
do {
const char *name_label = n;
while (*n && *n != '.')
n++;
const char *pattern_label = p;
while (*p && *p != '.')
p++;
if (!is_string_matching_glob_pattern(
name_label, (size_t) (n - name_label),
pattern_label, (size_t) (p - pattern_label)))
break;
if (*n)
n++;
if (*p)
p++;
} while (*n && *p);
return !*n && !*p;
}
#endif
+2
View File
@@ -746,6 +746,8 @@ static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void
add 7 to round up */
int len = (maclen + 9) >> 3;
unsigned char *p = expand(len << 3);
if (!p)
return 1;
memset(p, 0, len << 3);
*p++ = ICMP6_OPT_SOURCE_MAC;
*p++ = len;
+559 -378
View File
File diff suppressed because it is too large. Load diff
+59 -37
View File
@@ -372,9 +372,22 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (!context)
{
my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
subnet_addr.s_addr ? _("with subnet selector") : _("via"),
subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
const char *via;
if (subnet_addr.s_addr)
{
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;
}
@@ -383,13 +396,19 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
struct dhcp_context *context_tmp;
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
{
strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAME);
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
{
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
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
{
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);
}
}
}
@@ -1031,8 +1050,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
config->addr.s_addr == option_addr(opt).s_addr)
{
prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
inet_ntop(AF_INET, &config->addr, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
inet_ntoa(config->addr), daemon->dhcp_buff);
daemon->addrbuff, daemon->dhcp_buff);
config->flags |= CONFIG_DECLINED;
config->decline_time = now;
}
@@ -1078,7 +1098,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (have_config(config, CONFIG_ADDR))
{
char *addrs = inet_ntoa(config->addr);
inet_ntop(AF_INET, &config->addr, daemon->addrbuff, ADDRSTRLEN);
if ((ltmp = lease_find_by_addr(config->addr)) &&
ltmp != lease &&
@@ -1088,7 +1108,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
addrs, print_mac(daemon->namebuff, mac, len));
daemon->addrbuff, print_mac(daemon->namebuff, mac, len));
}
else
{
@@ -1097,10 +1117,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (context->router.s_addr == config->addr.s_addr)
break;
if (tmp)
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), addrs);
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), daemon->addrbuff);
else if (have_config(config, CONFIG_DECLINED) &&
difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), daemon->addrbuff);
else
conf = config->addr;
}
@@ -1303,9 +1323,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
a lease from one of it's 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);
my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
inet_ntoa(ltmp->addr));
daemon->addrbuff);
lease = ltmp;
}
else
@@ -1674,42 +1695,40 @@ static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
static void log_packet(char *type, void *addr, unsigned char *ext_mac,
int mac_len, char *interface, char *string, char *err, u32 xid)
{
struct in_addr a;
if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
return;
/* addr may be misaligned */
daemon->addrbuff[0] = 0;
if (addr)
memcpy(&a, addr, sizeof(a));
inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN);
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",
ntohl(xid),
type,
interface,
addr ? inet_ntoa(a) : "",
addr ? " " : "",
daemon->namebuff,
string ? string : "",
err ? err : "");
else
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
if (option_bool(OPT_LOG_OPTS))
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
ntohl(xid),
type,
interface,
addr ? inet_ntoa(a) : "",
daemon->addrbuff,
addr ? " " : "",
daemon->namebuff,
string ? string : "",
err ? err : "");
else
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
type,
interface,
daemon->addrbuff,
addr ? " " : "",
daemon->namebuff,
string ? string : "",
err ? err : "");
#ifdef HAVE_UBUS
if (!strcmp(type, "DHCPACK"))
ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
else if (!strcmp(type, "DHCPRELEASE"))
ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
if (!strcmp(type, "DHCPACK"))
ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
else if (!strcmp(type, "DHCPRELEASE"))
ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
#endif
}
@@ -1861,7 +1880,10 @@ static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id
if (option_bool(OPT_LOG_OPTS))
{
if (mess->siaddr.s_addr != 0)
my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
{
inet_ntop(AF_INET, &mess->siaddr, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), daemon->addrbuff);
}
if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
+3 -2
View File
@@ -600,7 +600,7 @@ void check_tftp_listeners(time_t now)
/* Wrong source address. See rfc1350 para 4. */
prettyprint_addr(&peer, daemon->addrbuff);
len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff);
sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer));
while(retry_send(sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer))));
}
}
}
@@ -761,7 +761,8 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
len = snprintf(mess->message, MAXMESSAGE, message, file, errstr);
ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */
my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
if (err != ERR_FNF || !option_bool(OPT_QUIET_TFTP))
my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
return ret;
}
+223 -42
View File
@@ -21,17 +21,44 @@
#include <libubus.h>
static struct blob_buf b;
static int notify;
static int error_logged = 0;
static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
#ifdef HAVE_CONNTRACK
enum {
SET_CONNMARK_ALLOWLIST_MARK,
SET_CONNMARK_ALLOWLIST_MASK,
SET_CONNMARK_ALLOWLIST_PATTERNS
};
static const struct blobmsg_policy set_connmark_allowlist_policy[] = {
[SET_CONNMARK_ALLOWLIST_MARK] = {
.name = "mark",
.type = BLOBMSG_TYPE_INT32
},
[SET_CONNMARK_ALLOWLIST_MASK] = {
.name = "mask",
.type = BLOBMSG_TYPE_INT32
},
[SET_CONNMARK_ALLOWLIST_PATTERNS] = {
.name = "patterns",
.type = BLOBMSG_TYPE_ARRAY
}
};
static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
#endif
static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj);
static const struct ubus_method ubus_object_methods[] = {
UBUS_METHOD_NOARG("metrics", ubus_handle_metrics),
#ifdef HAVE_CONNTRACK
UBUS_METHOD("set_connmark_allowlist", ubus_handle_set_connmark_allowlist, set_connmark_allowlist_policy),
#endif
};
static struct ubus_object_type ubus_object_type =
@@ -50,17 +77,16 @@ static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
(void)ctx;
my_syslog(LOG_DEBUG, _("UBus subscription callback: %s subscriber(s)"), obj->has_subscribers ? "1" : "0");
notify = obj->has_subscribers;
}
static void ubus_destroy(struct ubus_context *ubus)
{
// Forces re-initialization when we're reusing the same definitions later on.
ubus_object.id = 0;
ubus_object_type.id = 0;
ubus_free(ubus);
daemon->ubus = NULL;
/* Forces re-initialization when we're reusing the same definitions later on. */
ubus_object.id = 0;
ubus_object_type.id = 0;
}
static void ubus_disconnect_cb(struct ubus_context *ubus)
@@ -76,42 +102,27 @@ static void ubus_disconnect_cb(struct ubus_context *ubus)
}
}
void ubus_init()
char *ubus_init()
{
struct ubus_context *ubus = NULL;
int ret = 0;
ubus = ubus_connect(NULL);
if (!ubus)
{
if (!error_logged)
{
my_syslog(LOG_ERR, _("Cannot initialize UBus: connection failed"));
error_logged = 1;
}
ubus_destroy(ubus);
return;
}
if (!(ubus = ubus_connect(NULL)))
return NULL;
ubus_object.name = daemon->ubus_name;
ret = ubus_add_object(ubus, &ubus_object);
if (ret)
{
if (!error_logged)
{
my_syslog(LOG_ERR, _("Cannot add object to UBus: %s"), ubus_strerror(ret));
error_logged = 1;
}
ubus_destroy(ubus);
return;
}
return (char *)ubus_strerror(ret);
}
ubus->connection_lost = ubus_disconnect_cb;
daemon->ubus = ubus;
error_logged = 0;
my_syslog(LOG_INFO, _("Connected to system UBus"));
return NULL;
}
void set_ubus_listeners()
@@ -160,6 +171,16 @@ void check_ubus_listeners()
}
}
#define CHECK(stmt) \
do { \
int e = (stmt); \
if (e) \
{ \
my_syslog(LOG_ERR, _("UBus command failed: %d (%s)"), e, #stmt); \
return (UBUS_STATUS_UNKNOWN_ERROR); \
} \
} while (0)
static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
@@ -170,36 +191,196 @@ static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj
(void)method;
(void)msg;
blob_buf_init(&b, BLOBMSG_TYPE_TABLE);
CHECK(blob_buf_init(&b, BLOBMSG_TYPE_TABLE));
for (i=0; i < __METRIC_MAX; i++)
blobmsg_add_u32(&b, get_metric_name(i), daemon->metrics[i]);
CHECK(blobmsg_add_u32(&b, get_metric_name(i), daemon->metrics[i]));
return ubus_send_reply(ctx, req, b.head);
CHECK(ubus_send_reply(ctx, req, b.head));
return UBUS_STATUS_OK;
}
#ifdef HAVE_CONNTRACK
static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
const struct blobmsg_policy *policy = set_connmark_allowlist_policy;
size_t policy_len = countof(set_connmark_allowlist_policy);
struct allowlist *allowlists = NULL, **allowlists_pos;
char **patterns = NULL, **patterns_pos;
u32 mark, mask = UINT32_MAX;
size_t num_patterns = 0;
struct blob_attr *tb[policy_len];
struct blob_attr *attr;
if (blobmsg_parse(policy, policy_len, tb, blob_data(msg), blob_len(msg)))
return UBUS_STATUS_INVALID_ARGUMENT;
if (!tb[SET_CONNMARK_ALLOWLIST_MARK])
return UBUS_STATUS_INVALID_ARGUMENT;
mark = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MARK]);
if (!mark)
return UBUS_STATUS_INVALID_ARGUMENT;
if (tb[SET_CONNMARK_ALLOWLIST_MASK])
{
mask = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MASK]);
if (!mask || (mark & ~mask))
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS])
{
struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
__blob_for_each_attr(attr, head, len)
{
char *pattern;
if (blob_id(attr) != BLOBMSG_TYPE_STRING)
return UBUS_STATUS_INVALID_ARGUMENT;
if (!(pattern = blobmsg_get_string(attr)))
return UBUS_STATUS_INVALID_ARGUMENT;
if (strcmp(pattern, "*") && !is_valid_dns_name_pattern(pattern))
return UBUS_STATUS_INVALID_ARGUMENT;
num_patterns++;
}
}
for (allowlists_pos = &daemon->allowlists; *allowlists_pos; allowlists_pos = &(*allowlists_pos)->next)
if ((*allowlists_pos)->mark == mark && (*allowlists_pos)->mask == mask)
{
struct allowlist *allowlists_next = (*allowlists_pos)->next;
for (patterns_pos = (*allowlists_pos)->patterns; *patterns_pos; patterns_pos++)
{
free(*patterns_pos);
*patterns_pos = NULL;
}
free((*allowlists_pos)->patterns);
(*allowlists_pos)->patterns = NULL;
free(*allowlists_pos);
*allowlists_pos = allowlists_next;
break;
}
if (!num_patterns)
return UBUS_STATUS_OK;
patterns = whine_malloc((num_patterns + 1) * sizeof(char *));
if (!patterns)
goto fail;
patterns_pos = patterns;
if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS])
{
struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]);
__blob_for_each_attr(attr, head, len)
{
char *pattern;
if (!(pattern = blobmsg_get_string(attr)))
goto fail;
if (!(*patterns_pos = whine_malloc(strlen(pattern) + 1)))
goto fail;
strcpy(*patterns_pos++, pattern);
}
}
allowlists = whine_malloc(sizeof(struct allowlist));
if (!allowlists)
goto fail;
memset(allowlists, 0, sizeof(struct allowlist));
allowlists->mark = mark;
allowlists->mask = mask;
allowlists->patterns = patterns;
allowlists->next = daemon->allowlists;
daemon->allowlists = allowlists;
return UBUS_STATUS_OK;
fail:
if (patterns)
{
for (patterns_pos = patterns; *patterns_pos; patterns_pos++)
{
free(*patterns_pos);
*patterns_pos = NULL;
}
free(patterns);
patterns = NULL;
}
if (allowlists)
{
free(allowlists);
allowlists = NULL;
}
return UBUS_STATUS_UNKNOWN_ERROR;
}
#endif
#undef CHECK
#define CHECK(stmt) \
do { \
int e = (stmt); \
if (e) \
{ \
my_syslog(LOG_ERR, _("UBus command failed: %d (%s)"), e, #stmt); \
return; \
} \
} while (0)
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface)
{
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
int ret;
if (!ubus || !notify)
if (!ubus || !ubus_object.has_subscribers)
return;
blob_buf_init(&b, BLOBMSG_TYPE_TABLE);
CHECK(blob_buf_init(&b, BLOBMSG_TYPE_TABLE));
if (mac)
blobmsg_add_string(&b, "mac", mac);
CHECK(blobmsg_add_string(&b, "mac", mac));
if (ip)
blobmsg_add_string(&b, "ip", ip);
CHECK(blobmsg_add_string(&b, "ip", ip));
if (name)
blobmsg_add_string(&b, "name", name);
CHECK(blobmsg_add_string(&b, "name", name));
if (interface)
blobmsg_add_string(&b, "interface", interface);
CHECK(blobmsg_add_string(&b, "interface", interface));
ret = ubus_notify(ubus, &ubus_object, type, b.head, -1);
if (!ret)
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
CHECK(ubus_notify(ubus, &ubus_object, type, b.head, -1));
}
#ifdef HAVE_CONNTRACK
void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name)
{
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
if (!ubus || !ubus_object.has_subscribers)
return;
CHECK(blob_buf_init(&b, 0));
CHECK(blobmsg_add_u32(&b, "mark", mark));
CHECK(blobmsg_add_string(&b, "name", name));
CHECK(ubus_notify(ubus, &ubus_object, "connmark-allowlist.refused", b.head, -1));
}
void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *name, const char *value, u32 ttl)
{
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
if (!ubus || !ubus_object.has_subscribers)
return;
CHECK(blob_buf_init(&b, 0));
CHECK(blobmsg_add_u32(&b, "mark", mark));
CHECK(blobmsg_add_string(&b, "name", name));
CHECK(blobmsg_add_string(&b, "value", value));
CHECK(blobmsg_add_u32(&b, "ttl", ttl));
/* Set timeout to allow UBus subscriber to configure firewall rules before returning. */
CHECK(ubus_notify(ubus, &ubus_object, "connmark-allowlist.resolved", b.head, /* timeout: */ 1000));
}
#endif
#undef CHECK
#endif /* HAVE_UBUS */
+58 -29
View File
@@ -115,7 +115,8 @@ u64 rand64(void)
return (u64)out[outleft+1] + (((u64)out[outleft]) << 32);
}
/* returns 2 if names is OK but contains one or more underscores */
/* returns 1 if name is OK and ascii printable
* returns 2 if name should be processed by IDN */
static int check_name(char *in)
{
/* remove trailing .
@@ -123,7 +124,9 @@ static int check_name(char *in)
size_t dotgap = 0, l = strlen(in);
char c;
int nowhite = 0;
int idn_encode = 0;
int hasuscore = 0;
int hasucase = 0;
if (l == 0 || l > MAXDNAME) return 0;
@@ -136,28 +139,49 @@ static int check_name(char *in)
for (; (c = *in); in++)
{
if (c == '.')
dotgap = 0;
dotgap = 0;
else if (++dotgap > MAXLABEL)
return 0;
return 0;
else if (isascii((unsigned char)c) && iscntrl((unsigned char)c))
/* iscntrl only gives expected results for ascii */
return 0;
#if !defined(HAVE_IDN) && !defined(HAVE_LIBIDN2)
/* iscntrl only gives expected results for ascii */
return 0;
else if (!isascii((unsigned char)c))
return 0;
#if !defined(HAVE_IDN) && !defined(HAVE_LIBIDN2)
return 0;
#else
idn_encode = 1;
#endif
else if (c != ' ')
{
nowhite = 1;
if (c == '_')
hasuscore = 1;
}
{
nowhite = 1;
#if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003)
if (c == '_')
hasuscore = 1;
#else
(void)hasuscore;
#endif
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2)
if (c >= 'A' && c <= 'Z')
hasucase = 1;
#else
(void)hasucase;
#endif
}
}
if (!nowhite)
return 0;
return hasuscore ? 2 : 1;
#if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003)
/* Older libidn2 strips underscores, so don't do IDN processing
if the name has an underscore unless it also has non-ascii characters. */
idn_encode = idn_encode || (hasucase && !hasuscore);
#else
idn_encode = idn_encode || hasucase;
#endif
return (idn_encode) ? 2 : 1;
}
/* Hostnames have a more limited valid charset than domain names
@@ -204,17 +228,11 @@ char *canonicalise(char *in, int *nomem)
if (!(rc = check_name(in)))
return NULL;
#if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003)
/* older libidn2 strips underscores, so don't do IDN processing
if the name has an underscore (check_name() returned 2) */
if (rc != 2)
#endif
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2)
if (rc == 2)
{
# ifdef HAVE_LIBIDN2
rc = idn2_to_ascii_lz(in, &ret, IDN2_NONTRANSITIONAL);
if (rc == IDN2_DISALLOWED)
rc = idn2_to_ascii_lz(in, &ret, IDN2_TRANSITIONAL);
# else
rc = idna_to_ascii_lz(in, &ret, 0);
# endif
@@ -234,12 +252,14 @@ char *canonicalise(char *in, int *nomem)
return ret;
}
#else
(void)rc;
#endif
if ((ret = whine_malloc(strlen(in)+1)))
strcpy(ret, in);
else if (nomem)
*nomem = 1;
*nomem = 1;
return ret;
}
@@ -408,13 +428,12 @@ int hostname_issubdomain(char *a, char *b)
time_t dnsmasq_time(void)
{
#ifdef HAVE_BROKEN_RTC
struct tms dummy;
static long tps = 0;
struct timespec ts;
if (tps == 0)
tps = sysconf(_SC_CLK_TCK);
if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
die(_("cannot read monotonic clock: %s"), NULL, EC_MISC);
return (time_t)(times(&dummy)/tps);
return ts.tv_sec;
#else
return time(NULL);
#endif
@@ -436,7 +455,17 @@ int netmask_length(struct in_addr mask)
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
{
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
}
}
int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix)
{
struct in_addr mask;
mask.s_addr = htonl(~((1 << (32 - prefix)) - 1));
return is_same_net(a, b, mask);
}
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
{
@@ -518,7 +547,7 @@ void prettyprint_time(char *buf, unsigned int t)
if ((x = (t/60)%60))
p += sprintf(&buf[p], "%um", x);
if ((x = t%60))
p += sprintf(&buf[p], "%us", x);
sprintf(&buf[p], "%us", x);
}
}
@@ -564,7 +593,7 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
int j, bytes = (1 + (r - in))/2;
for (j = 0; j < bytes; j++)
{
char sav = sav;
char sav;
if (j < bytes - 1)
{
sav = in[(j+1)*2];