Compare commits

...
Author SHA1 Message Date
Simon Kelley f74b74e521 Refactor relay_reply6().
Traverse the nested data structures more efficiently,
more understandably, and with watertight checking of
buffer boundaries.
2025-07-15 15:17:46 +01:00
Simon Kelley c9342cb556 Check for constructed name in --srv-host exceeding length limit.
Thanks to Mikhail Dmitrichenko for spotting this.
2025-07-15 15:15:32 +01:00
Simon Kelley 2b844b8c83 Fix BNF from --srv-host in manpage. 2025-07-14 15:15:26 +01:00
Simon Kelley 65e9a8957c Fix order of reads dhcp-hostsfile and /etc/ethers.
Reading /etc/ethers assumes that dhcp-host cofig has already
been read, and that is the case for dhcp-host, but for unknown
reasons, the analogous congiguration is dhcp-hostfile was being
read after /etc/ethers.

Swap the order.

Thanks to Andreas Kuropka for spotting the problem.
2025-07-10 10:58:14 +01:00
Simon Kelley da868a2fbe Restore bit-rotted warning about non-recursive upstreams. 2025-07-08 16:18:03 +01:00
Simon Kelley bceb5287b9 Fix FTBFS on MacOS.
Fallout from 98189ff988

Thanks to Travis Rayan for the bug report.
2025-07-07 17:58:41 +01:00
Simon Kelley 84445dec26 Fix server selection for DS queries.
This was mainly done in 57e582492b
This commit extends the new behaviour to cases where DNSSEC
validation is not compiled-in or not enabled.
2025-07-07 17:16:32 +01:00
Rob Gill via Dnsmasq-discuss c70b92b2a4 Add CG-NAT range to non-globally routed IP space per RFC7793
The Shared address space (100.64.0.0/10) used by CG-NAT, defined in RFC6598 should
not have reverse DNS queries sent to global DNS infrastructure.

Confirmed in RFC7793 and listed on
https://www.iana.org/assignments/locally-served-dns-zones/locally-served-dns-zones.xhtml

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2025-06-26 13:10:48 +01:00
Simon Kelley 57e582492b Fix to 57f0489f38
When choosing a server to send a DS query to take
account of the need for DS records for a domain
to come from the parent of that domain.
2025-06-25 15:02:47 +01:00
9 changed files with 90 additions and 79 deletions

No files matched your search

+1 -1
View File
@@ -673,7 +673,7 @@ machine on which dnsmasq is running) for each
local machine. Local machines are those in /etc/hosts or with DHCP local machine. Local machines are those in /etc/hosts or with DHCP
leases. leases.
.TP .TP
.B \-W, --srv-host=<_service>.<_prot>.[<domain>],[<target>[,<port>[,<priority>[,<weight>]]]] .B \-W, --srv-host=<_service>.<_prot>[.<domain>],[<target>[,<port>[,<priority>[,<weight>]]]]
Return a SRV DNS record. See RFC2782 for details. If not supplied, the Return a SRV DNS record. See RFC2782 for details. If not supplied, the
domain defaults to that given by domain defaults to that given by
.B --domain. .B --domain.
+1
View File
@@ -290,6 +290,7 @@ HAVE_SOCKADDR_SA_LEN
#define HAVE_BSD_NETWORK #define HAVE_BSD_NETWORK
#define HAVE_GETOPT_LONG #define HAVE_GETOPT_LONG
#define HAVE_SOCKADDR_SA_LEN #define HAVE_SOCKADDR_SA_LEN
#define NO_IPSET
/* Define before sys/socket.h is included so we get socklen_t */ /* Define before sys/socket.h is included so we get socklen_t */
#define _BSD_SOCKLEN_T_ #define _BSD_SOCKLEN_T_
/* Select the RFC_3542 version of the IPv6 socket API. /* Select the RFC_3542 version of the IPv6 socket API.
+1 -1
View File
@@ -1777,9 +1777,9 @@ void clear_cache_and_reload(time_t now)
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
if (daemon->dhcp || daemon->doing_dhcp6) if (daemon->dhcp || daemon->doing_dhcp6)
{ {
reread_dhcp();
if (option_bool(OPT_ETHERS)) if (option_bool(OPT_ETHERS))
dhcp_read_ethers(); dhcp_read_ethers();
reread_dhcp();
dhcp_update_configs(daemon->dhcp_conf); dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs(); lease_update_from_configs();
lease_update_file(now); lease_update_file(now);
+1 -1
View File
@@ -1938,7 +1938,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
char *name, char *limit, int first, int last, int ede); char *name, char *limit, int first, int last, int ede);
int server_samegroup(struct server *a, struct server *b); int server_samegroup(struct server *a, struct server *b);
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp); int dnssec_server(struct server *server, char *keyname, int is_ds, int *firstp, int *lastp);
#endif #endif
void mark_servers(int flag); void mark_servers(int flag);
void cleanup_servers(void); void cleanup_servers(void);
+18 -6
View File
@@ -95,6 +95,7 @@ void build_server_array(void)
A flag of F_SERVER returns an upstream server only. A flag of F_SERVER returns an upstream server only.
A flag of F_DNSSECOK disables NODOTS servers from consideration. A flag of F_DNSSECOK disables NODOTS servers from consideration.
A flag of F_DS returns parent domain server.
A flag of F_DOMAINSRV returns a domain-specific server only. A flag of F_DOMAINSRV returns a domain-specific server only.
A flag of F_CONFIG returns anything that generates a local A flag of F_CONFIG returns anything that generates a local
reply of IPv4 or IPV6. reply of IPv4 or IPV6.
@@ -106,12 +107,23 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout)
ssize_t qlen; ssize_t qlen;
int try, high, low = 0; int try, high, low = 0;
int nlow = 0, nhigh = 0; int nlow = 0, nhigh = 0;
char *cp, *qdomain = domain; char *cp, *qdomain;
/* may be no configured servers. */ /* may be no configured servers. */
if (daemon->serverarraysz == 0) if (daemon->serverarraysz == 0)
return 0; return 0;
/* DS records should come from the parent domain. */
if (flags & F_DS)
{
if ((cp = strchr(domain, '.')))
domain = cp+1;
else
domain = "";
}
qdomain = domain;
/* find query length and presence of '.' */ /* find query length and presence of '.' */
for (cp = qdomain, nodots = 1, qlen = 0; *cp; qlen++, cp++) for (cp = qdomain, nodots = 1, qlen = 0; *cp; qlen++, cp++)
if (*cp == '.') if (*cp == '.')
@@ -403,7 +415,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
setup_reply(header, flags, ede); setup_reply(header, flags, ede);
gotname &= ~F_QUERY; gotname &= ~(F_QUERY | F_DS);
if (flags & (F_NXDOMAIN | F_NOERR)) if (flags & (F_NXDOMAIN | F_NOERR))
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL, 0); log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL, 0);
@@ -463,14 +475,14 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
} }
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp) int dnssec_server(struct server *server, char *keyname, int is_ds, int *firstp, int *lastp)
{ {
int first, last, index; int first, last, index;
/* Find server to send DNSSEC query to. This will normally be the /* Find server to send DNSSEC query to. This will normally be the
same as for the original query, but may be another if same as for the original query, but may be another if
servers for domains are involved. */ servers for domains are involved. */
if (!lookup_domain(keyname, F_SERVER | F_DNSSECOK, &first, &last)) if (!lookup_domain(keyname, F_SERVER | F_DNSSECOK | (is_ds ? F_DS : 0), &first, &last))
return -1; return -1;
for (index = first; index != last; index++) for (index = first; index != last; index++)
+13 -13
View File
@@ -763,6 +763,16 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (!is_sign && !option_bool(OPT_DNSSEC_PROXY)) if (!is_sign && !option_bool(OPT_DNSSEC_PROXY))
header->hb4 &= ~HB4_AD; header->hb4 &= ~HB4_AD;
/* Complain loudly if the upstream server is non-recursive. */
if (!(header->hb4 & HB4_RA) && rcode == NOERROR &&
server && !(server->flags & SERV_WARNED_RECURSIVE))
{
(void)prettyprint_addr(&server->addr, daemon->namebuff);
my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
if (!option_bool(OPT_LOG))
server->flags |= SERV_WARNED_RECURSIVE;
}
header->hb4 |= HB4_RA; /* recursion if available */ header->hb4 |= HB4_RA; /* recursion if available */
if (OPCODE(header) != QUERY) if (OPCODE(header) != QUERY)
@@ -778,16 +788,6 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
return resize_packet(header, n, pheader, plen); return resize_packet(header, n, pheader, plen);
} }
/* Complain loudly if the upstream server is non-recursive. */
if (!(header->hb4 & HB4_RA) && rcode == NOERROR &&
server && !(server->flags & SERV_WARNED_RECURSIVE))
{
(void)prettyprint_addr(&server->addr, daemon->namebuff);
my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
if (!option_bool(OPT_LOG))
server->flags |= SERV_WARNED_RECURSIVE;
}
if (header->hb3 & HB3_TC) if (header->hb3 & HB3_TC)
log_query(F_UPSTREAM, NULL, NULL, "truncated", 0); log_query(F_UPSTREAM, NULL, NULL, "truncated", 0);
else if (!bogusanswer || (header->hb4 & HB4_CD)) else if (!bogusanswer || (header->hb4 & HB4_CD))
@@ -1024,7 +1024,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* Make sure we don't expire and free the orig frec during the /* Make sure we don't expire and free the orig frec during the
allocation of a new one: third arg of get_new_frec() does that. */ allocation of a new one: third arg of get_new_frec() does that. */
if ((serverind = dnssec_server(forward->sentto, daemon->keyname, NULL, NULL)) != -1 && if ((serverind = dnssec_server(forward->sentto, daemon->keyname, STAT_ISEQUAL(status, STAT_NEED_DS), NULL, NULL)) != -1 &&
(server = daemon->serverarray[serverind]) && (server = daemon->serverarray[serverind]) &&
(nn = dnssec_generate_query(header, ((unsigned char *) header) + daemon->edns_pktsz, (nn = dnssec_generate_query(header, ((unsigned char *) header) + daemon->edns_pktsz,
daemon->keyname, forward->class, get_id(), daemon->keyname, forward->class, get_id(),
@@ -2199,7 +2199,7 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple
first = start = server->arrayposn; first = start = server->arrayposn;
last = first + 1; last = first + 1;
if (!STAT_ISEQUAL(status, STAT_OK) && (start = dnssec_server(server, name, &first, &last)) == -1) if (!STAT_ISEQUAL(status, STAT_OK) && (start = dnssec_server(server, name, STAT_ISEQUAL(status, STAT_NEED_DS), &first, &last)) == -1)
new_status = STAT_ABANDONED; new_status = STAT_ABANDONED;
else else
{ {
@@ -2307,7 +2307,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, 0, m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, 0,
STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS); STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS);
if ((start = dnssec_server(server, keyname, &first, &last)) == -1) if ((start = dnssec_server(server, keyname, STAT_ISEQUAL(new_status, STAT_NEED_DS), &first, &last)) == -1)
{ {
new_status = STAT_ABANDONED; new_status = STAT_ABANDONED;
break; break;
+2
View File
@@ -6262,6 +6262,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
strchr(srv->name, '.') && strchr(srv->name, '.') &&
strchr(srv->name, '.') == strrchr(srv->name, '.')) strchr(srv->name, '.') == strrchr(srv->name, '.'))
{ {
if (strlen(srv->name) + 1 + strlen(daemon->domain_suffix) > MAXDNAME)
die(_("srv-host name %s too long after domain appended"), srv->name, EC_MISC);
strcpy(buff, srv->name); strcpy(buff, srv->name);
strcat(buff, "."); strcat(buff, ".");
strcat(buff, daemon->domain_suffix); strcat(buff, daemon->domain_suffix);
+3 -4
View File
@@ -415,6 +415,7 @@ int private_net(struct in_addr addr, int ban_localhost)
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ || (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
(((ip_addr & 0xFF000000) == 0x00000000) && ban_localhost) /* RFC 5735 section 3. "here" network */ || (((ip_addr & 0xFF000000) == 0x00000000) && ban_localhost) /* RFC 5735 section 3. "here" network */ ||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ || ((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
((ip_addr & 0xFFC00000) == 0x64400000) /* 100.64.0.0/10 (CG-NAT) RFC6598/RFC7793*/ ||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ || ((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ || ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ || ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ||
@@ -1244,12 +1245,10 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
return F_IPV4 | F_IPV6; return F_IPV4 | F_IPV6;
} }
#ifdef HAVE_DNSSEC
/* Make the behaviour for DS and DNSKEY queries we forward the same /* Make the behaviour for DS and DNSKEY queries we forward the same
as for DS and DNSKEY queries we originate. */ as for DS and DNSKEY queries we originate. */
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DS || qtype == T_DNSKEY)) if (qtype == T_DS || qtype == T_DNSKEY)
return F_DNSSECOK; return F_DNSSECOK | (qtype == T_DS ? F_DS : 0);
#endif
return F_QUERY; return F_QUERY;
} }
+50 -53
View File
@@ -2276,64 +2276,61 @@ int relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface)
{ {
void *opt, *opts = inbuff + 34; void *opt, *opts = inbuff + 34;
void *end = inbuff + sz; void *end = inbuff + sz;
for (opt = opts; opt; opt = opt6_next(opt, end))
if (opt6_type(opt) == OPTION6_RELAY_MSG && opt6_len(opt) > 0)
{
int encap_type = *((unsigned char *)opt6_ptr(opt, 0));
put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ);
peer->sin6_scope_id = relay->iface_index;
if (encap_type == DHCP6RELAYREPL)
{
peer->sin6_port = ntohs(DHCPV6_SERVER_PORT);
return 1;
}
peer->sin6_port = ntohs(DHCPV6_CLIENT_PORT);
#ifdef HAVE_SCRIPT
if (daemon->lease_change_command && encap_type == DHCP6REPLY)
{
/* decapsulate relayed message */
opts = opt6_ptr(opt, 4);
end = opt6_ptr(opt, opt6_len(opt));
for (opt = opts; opt; opt = opt6_next(opt, end))
if (opt6_type(opt) == OPTION6_IA_PD && opt6_len(opt) > 12)
{
void *ia_opts = opt6_ptr(opt, 12);
void *ia_end = opt6_ptr(opt, opt6_len(opt));
void *ia_opt;
for (ia_opt = ia_opts; ia_opt; ia_opt = opt6_next(ia_opt, ia_end))
/* valid lifetime must not be zero. */
if (opt6_type(ia_opt) == OPTION6_IAPREFIX && opt6_len(ia_opt) >= 25 && opt6_uint(ia_opt, 4, 4) != 0)
{
if (daemon->free_snoops ||
(daemon->free_snoops = whine_malloc(sizeof(struct snoop_record))))
{
struct snoop_record *snoop = daemon->free_snoops;
daemon->free_snoops = snoop->next;
snoop->client = peer->sin6_addr;
snoop->prefix_len = opt6_uint(ia_opt, 8, 1);
memcpy(&snoop->prefix, opt6_ptr(ia_opt, 9), IN6ADDRSZ);
snoop->next = relay->snoop_records;
relay->snoop_records = snoop;
}
}
}
}
#endif
return 1;
}
if ((opt = opt6_find(opts, end, OPTION6_RELAY_MSG, 4)))
{
int encap_type = opt6_uint(opt, 0, 1);
put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ);
peer->sin6_scope_id = relay->iface_index;
if (encap_type == DHCP6RELAYREPL)
{
peer->sin6_port = ntohs(DHCPV6_SERVER_PORT);
return 1;
}
peer->sin6_port = ntohs(DHCPV6_CLIENT_PORT);
#ifdef HAVE_SCRIPT
if (daemon->lease_change_command && encap_type == DHCP6REPLY)
{
/* skip over message type and transaction-id. to get to options. */
opts = opt6_ptr(opt, 4);
end = opt6_ptr(opt, opt6_len(opt));
if ((opt = opt6_find(opts, end, OPTION6_IA_PD, 12)))
{
opts = opt6_ptr(opt, 12);
end = opt6_ptr(opt, opt6_len(opt));
for (opt = opt6_find(opts, end, OPTION6_IAPREFIX, 25); opt; opt = opt6_find(opt6_next(opt, end), end, OPTION6_IAPREFIX, 25))
/* valid lifetime must not be zero. */
if (opt6_uint(opt, 4, 4) != 0)
{
if (daemon->free_snoops ||
(daemon->free_snoops = whine_malloc(sizeof(struct snoop_record))))
{
struct snoop_record *snoop = daemon->free_snoops;
daemon->free_snoops = snoop->next;
snoop->client = peer->sin6_addr;
snoop->prefix_len = opt6_uint(opt, 8, 1);
memcpy(&snoop->prefix, opt6_ptr(opt, 9), IN6ADDRSZ);
snoop->next = relay->snoop_records;
relay->snoop_records = snoop;
}
}
}
}
#endif
return 1;
}
} }
return 0; return 0;
} }
#ifdef HAVE_SCRIPT #ifdef HAVE_SCRIPT
int do_snoop_script_run(void) int do_snoop_script_run(void)
{ {