mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-04 13:48:30 +00:00
Compare commits
68
Commits
v2.93test5
...
v2.93
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ff66da573 | ||
|
|
d823d8e9cd | ||
|
|
2c78b6db6c | ||
|
|
4bf46ceb37 | ||
|
|
7d42376a3a | ||
|
|
08cc209e7a | ||
|
|
ff4964d01d | ||
|
|
45771dc92c | ||
|
|
af73e48067 | ||
|
|
77d4749a5e | ||
|
|
0375e40824 | ||
|
|
18a139f48f | ||
|
|
d5d2bfb74a | ||
|
|
7f5802c144 | ||
|
|
7280e46aa5 | ||
|
|
754177b368 | ||
|
|
228d7ff964 | ||
|
|
14094e88be | ||
|
|
c705607122 | ||
|
|
9d2c93b608 | ||
|
|
073082ddc0 | ||
|
|
9f651f3398 | ||
|
|
e3a26d092e | ||
|
|
10e6b5b83e | ||
|
|
788b4e0f6c | ||
|
|
7b151eb606 | ||
|
|
014e909f78 | ||
|
|
29f8712f02 | ||
|
|
0450f9e28a | ||
|
|
0637bcf010 | ||
|
|
4bcc9650fa | ||
|
|
fc2286805f | ||
|
|
d158aeca75 | ||
|
|
36d081e374 | ||
|
|
9eb0757da9 | ||
|
|
2d0e0c7a54 | ||
|
|
cfc2244c2a | ||
|
|
9ad74926d4 | ||
|
|
a6069da373 | ||
|
|
14f53171ca | ||
|
|
8fd06d2cc0 | ||
|
|
d152e81e42 | ||
|
|
e0bec3342b | ||
|
|
dd707937d5 | ||
|
|
c5f46412a3 | ||
|
|
cd04c0ce20 | ||
|
|
0630a45ae1 | ||
|
|
3ea905b98b | ||
|
|
ca509cb611 | ||
|
|
8ceb8ee9b5 | ||
|
|
1c4a97edb5 | ||
|
|
9e3e0465cd | ||
|
|
b12a9249f6 | ||
|
|
9dec84ca42 | ||
|
|
68c149e9dd | ||
|
|
5ea054b264 | ||
|
|
b95d5a2577 | ||
|
|
5be5dc1f16 | ||
|
|
48348a2d77 | ||
|
|
b4a0663654 | ||
|
|
53313014b5 | ||
|
|
cf08eeee12 | ||
|
|
0597828b74 | ||
|
|
c8168e888e | ||
|
|
3a052d55fb | ||
|
|
1eab169173 | ||
|
|
4070a74862 | ||
|
|
65adc53130 |
No files matched your search
@@ -1,6 +1,6 @@
|
||||
version 2.93
|
||||
Fix a corner-case in DNSSEC validation with wildcards. If we have
|
||||
a wildcard record *.example.com and recieve a query for
|
||||
a wildcard record *.example.com and receive a query for
|
||||
a.example.com then that's OK, but we have to check that there isn't
|
||||
an actual a.example.com record. The corner case is when we get a
|
||||
query for *.example.com in that case the non-existence check
|
||||
@@ -19,7 +19,7 @@ version 2.93
|
||||
(eg rivcoed.org) will still fail. Thanks to Petr Menšík
|
||||
for the bug report.
|
||||
|
||||
Fix DNSSEC fail with CNAME replies to DS queries. As CNAME reply
|
||||
Fix DNSSEC fail with CNAME replies to DS queries. A CNAME reply
|
||||
to a DNSSEC query was confusing the validation logic. It now
|
||||
accepts a signed CNAME reply to a DS query as proof that no DS
|
||||
exists at the domain. This fixes the DS/zone break detection logic.
|
||||
@@ -28,6 +28,22 @@ version 2.93
|
||||
relay is in use. Many thanks to Jørgen Søvik for help
|
||||
finding this bug.
|
||||
|
||||
Modify the inotify implementation so that inotify watches are
|
||||
only created after dnsmasq has changed permissions and userid.
|
||||
This means that the permissions used when creating the watches
|
||||
are the same as used for accessing watched files, which makes
|
||||
more sense and avoids odd and confusing error conditions.
|
||||
|
||||
Rework storage allocation for domain names. This fixes a security
|
||||
bug that can cause heap-overwrite with long domain names.
|
||||
CVE-2026-2291 covers this and a simple patch for existing
|
||||
releases was released with the CVE. This patch reworks the whole
|
||||
code base to make it cleaner and less liable to future
|
||||
confusions. Either upgrading to 2.93 or applying the patch
|
||||
to earlier versions is sufficient to close the security hole.
|
||||
Thanks to Andrew S. Fasano for spotting this problem in the
|
||||
first place.
|
||||
|
||||
|
||||
version 2.92
|
||||
Redesign the interaction between DNSSEC validation and per-domain
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
# dnsmasq is Copyright (c) 2000-2026 Simon Kelley
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -233,31 +233,55 @@ struct dhcp6_packet create_release_packet(const char* iaid, const char* ip, cons
|
||||
return result;
|
||||
}
|
||||
|
||||
static void copy_option_string(char *dst, size_t dst_len, const char *src, size_t src_len)
|
||||
{
|
||||
if (dst_len == 0)
|
||||
return;
|
||||
|
||||
if (src_len >= dst_len)
|
||||
src_len = dst_len - 1;
|
||||
|
||||
memcpy(dst, src, src_len);
|
||||
dst[src_len] = '\0';
|
||||
}
|
||||
|
||||
uint16_t parse_iana_suboption(char* buf, size_t len)
|
||||
{
|
||||
size_t current_pos = 0;
|
||||
char option_value[1024];
|
||||
while (current_pos < len)
|
||||
{
|
||||
if (len - current_pos < 2 * sizeof(uint16_t))
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
uint16_t option_type, option_len;
|
||||
memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
|
||||
memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
|
||||
option_type = ntohs(option_type);
|
||||
option_len = ntohs(option_len);
|
||||
current_pos += 2 * sizeof(uint16_t);
|
||||
if (option_len > len - current_pos)
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
if (option_type == STATUS_CODE)
|
||||
{
|
||||
uint16_t status;
|
||||
if (option_len < sizeof(uint16_t))
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
memcpy(&status, buf + current_pos, sizeof(uint16_t));
|
||||
status = ntohs(status);
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
memcpy(option_value, buf + current_pos + sizeof(uint16_t) , option_len - sizeof(uint16_t));
|
||||
option_value[option_len-sizeof(uint16_t)] ='\0';
|
||||
copy_option_string(option_value, sizeof(option_value),
|
||||
buf + current_pos + sizeof(uint16_t),
|
||||
option_len - sizeof(uint16_t));
|
||||
fprintf(stderr, "Error: %s\n", option_value);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
current_pos += option_len;
|
||||
}
|
||||
|
||||
return -2;
|
||||
@@ -266,6 +290,9 @@ uint16_t parse_iana_suboption(char* buf, size_t len)
|
||||
int16_t parse_packet(char* buf, size_t len)
|
||||
{
|
||||
int16_t ret = -1;
|
||||
if (len < 4)
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
uint8_t type = buf[0];
|
||||
/*skipping tx id. you need it, uncomment following line
|
||||
uint16_t tx_id = ntohs((buf[1] <<16) + (buf[2] <<8) + buf[3]);
|
||||
@@ -277,20 +304,31 @@ int16_t parse_packet(char* buf, size_t len)
|
||||
char option_value[1024];
|
||||
while (current_pos < len)
|
||||
{
|
||||
if (len - current_pos < 2 * sizeof(uint16_t))
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
uint16_t option_type, option_len;
|
||||
memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
|
||||
memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
|
||||
option_type = ntohs(option_type);
|
||||
option_len = ntohs(option_len);
|
||||
current_pos += 2 * sizeof(uint16_t);
|
||||
if (option_len > len - current_pos)
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
if (option_type == STATUS_CODE)
|
||||
{
|
||||
uint16_t status;
|
||||
if (option_len < sizeof(uint16_t))
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
memcpy(&status, buf + current_pos, sizeof(uint16_t));
|
||||
status = ntohs(status);
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
memcpy(option_value, buf + current_pos +sizeof(uint16_t) , option_len -sizeof(uint16_t));
|
||||
copy_option_string(option_value, sizeof(option_value),
|
||||
buf + current_pos + sizeof(uint16_t),
|
||||
option_len - sizeof(uint16_t));
|
||||
fprintf(stderr, "Error: %d %s\n", status, option_value);
|
||||
return status;
|
||||
}
|
||||
@@ -301,6 +339,9 @@ int16_t parse_packet(char* buf, size_t len)
|
||||
|
||||
if (option_type == IA_NA )
|
||||
{
|
||||
if (option_len < 24)
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
uint16_t result = parse_iana_suboption(buf + current_pos +24, option_len -24);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
@@ -331,9 +331,15 @@ static char *print_mac(char *buff, unsigned char *mac, int len)
|
||||
if (len == 0)
|
||||
sprintf(p, "<null>");
|
||||
else
|
||||
for (i = 0; i < len; i++)
|
||||
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
|
||||
|
||||
{
|
||||
/* Server-controlled lengths (e.g. BOOTREPLY hlen) must not be trusted
|
||||
to fit the destination buffer; cap at the actual chaddr field size. */
|
||||
if (len > DHCP_CHADDR_MAX)
|
||||
len = DHCP_CHADDR_MAX;
|
||||
for (i = 0; i < len; i++)
|
||||
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
|
||||
}
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
|
||||
+8
-3
@@ -154,6 +154,9 @@ allows the log file to be rotated without stopping dnsmasq.
|
||||
.B --log-debug
|
||||
Enable extra logging intended for debugging rather than information.
|
||||
.TP
|
||||
.B --log-malloc
|
||||
Enable logging of memory allocation and release, for debugging.
|
||||
.TP
|
||||
.B --log-async[=<lines>]
|
||||
Enable asynchronous logging and optionally set the limit on the
|
||||
number of lines
|
||||
@@ -1515,7 +1518,7 @@ The normal default DNS server (the same address as the DHCP server)
|
||||
will not be appropriate when there is no route bewteen the
|
||||
two, so this will have to be explicitly configured.
|
||||
.TP
|
||||
.B \-U, --dhcp-vendorclass=set:<tag>,[enterprise:<IANA-enterprise number>,]<vendor-class>
|
||||
.B \-U, --dhcp-vendorclass=set:<tag>,[enterprise:<IANA-enterprise number>,][<vendor-class>]
|
||||
Map from a vendor-class string to a tag. Most DHCP clients provide a
|
||||
"vendor class" which represents, in some sense, the type of host. This option
|
||||
maps vendor classes to tags, so that DHCP options may be selectively delivered
|
||||
@@ -1529,9 +1532,11 @@ allow fuzzy matching. The set: prefix is optional but allowed for
|
||||
consistency.
|
||||
|
||||
Note that in IPv6 only, vendorclasses are namespaced with an
|
||||
IANA-allocated enterprise number. This is given with enterprise:
|
||||
IANA-allocated enterprise number. This may be given with an enterprise:
|
||||
keyword and specifies that only vendorclasses matching the specified
|
||||
number should be searched.
|
||||
number should be searched. Similarly, an enterprise number may be given
|
||||
with no vendor-class string. This always matches vendorclasses with
|
||||
the given enterprise number.
|
||||
.TP
|
||||
.B \-j, --dhcp-userclass=set:<tag>,<user-class>
|
||||
Map from a user-class string to a tag (with substring
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -47,24 +47,20 @@ static union all_addr del_addr;
|
||||
|
||||
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
|
||||
|
||||
int arp_enumerate(void *parm, callback_t callback)
|
||||
static int arp_enumerate_family(int family, void *parm, callback_t callback)
|
||||
{
|
||||
int mib[6];
|
||||
size_t needed;
|
||||
char *next;
|
||||
struct rt_msghdr *rtm;
|
||||
struct sockaddr_inarp *sin2;
|
||||
struct sockaddr_dl *sdl;
|
||||
struct iovec buff;
|
||||
static struct iovec buff = { NULL, 0 };
|
||||
int rc;
|
||||
|
||||
buff.iov_base = NULL;
|
||||
buff.iov_len = 0;
|
||||
|
||||
mib[0] = CTL_NET;
|
||||
mib[1] = PF_ROUTE;
|
||||
mib[2] = 0;
|
||||
mib[3] = AF_INET;
|
||||
mib[3] = family;
|
||||
mib[4] = NET_RT_FLAGS;
|
||||
#ifdef RTF_LLINFO
|
||||
mib[5] = RTF_LLINFO;
|
||||
@@ -72,8 +68,8 @@ int arp_enumerate(void *parm, callback_t callback)
|
||||
mib[5] = 0;
|
||||
#endif
|
||||
if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
|
||||
return 0;
|
||||
|
||||
return 1; /* not a failure: unsupported or empty table */
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (!expand_buf(&buff, needed))
|
||||
@@ -83,20 +79,50 @@ int arp_enumerate(void *parm, callback_t callback)
|
||||
break;
|
||||
needed += needed / 8;
|
||||
}
|
||||
|
||||
if (rc == -1)
|
||||
return 0;
|
||||
|
||||
for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen)
|
||||
{
|
||||
rtm = (struct rt_msghdr *)next;
|
||||
sin2 = (struct sockaddr_inarp *)(rtm + 1);
|
||||
sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
|
||||
if (!callback.af_unspec(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm))
|
||||
return 0;
|
||||
if (family == AF_INET)
|
||||
{
|
||||
struct sockaddr_inarp *sin2 = (struct sockaddr_inarp *)(rtm + 1);
|
||||
sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
|
||||
if (!callback.af_unspec(AF_INET, &sin2->sin_addr,
|
||||
LLADDR(sdl), sdl->sdl_alen, parm))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(rtm + 1);
|
||||
sdl = (struct sockaddr_dl *)((char *)sin6 + SA_SIZE(sin6));
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
|
||||
{
|
||||
/* PF_ROUTE sysctl returns raw kernel structures with the interface
|
||||
index embedded in bytes 2-3 of link-local addresses. Extract it
|
||||
into sin6_scope_id per the KAME API contract before clearing. */
|
||||
sin6->sin6_scope_id =
|
||||
((uint32_t)(sin6->sin6_addr.s6_addr[2]) << 8) | sin6->sin6_addr.s6_addr[3];
|
||||
sin6->sin6_addr.s6_addr[2] = 0;
|
||||
sin6->sin6_addr.s6_addr[3] = 0;
|
||||
}
|
||||
if (!callback.af_unspec(AF_INET6, &sin6->sin6_addr,
|
||||
LLADDR(sdl), sdl->sdl_alen, parm))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int arp_enumerate(void *parm, callback_t callback)
|
||||
{
|
||||
if (!arp_enumerate_family(AF_INET, parm, callback))
|
||||
return 0;
|
||||
return arp_enumerate_family(AF_INET6, parm, callback);
|
||||
}
|
||||
#endif /* defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) */
|
||||
|
||||
|
||||
|
||||
+39
-23
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -22,6 +22,7 @@ static struct crec *new_chain = NULL;
|
||||
static int insert_error;
|
||||
static union bigname *big_free = NULL;
|
||||
static int bignames_left, hash_size;
|
||||
|
||||
struct nameblock {
|
||||
struct nameblock *next;
|
||||
unsigned int last, index;
|
||||
@@ -221,6 +222,9 @@ static char *store_name(unsigned int namelen, unsigned int index)
|
||||
struct nameblock *block;
|
||||
char *ret = NULL;
|
||||
|
||||
if (namelen > NAMEBLOCK_CHARS)
|
||||
return NULL;
|
||||
|
||||
for (block = hostblocks; block; block = block->next)
|
||||
if (block->index == index && NAMEBLOCK_CHARS - block->last >= namelen)
|
||||
break;
|
||||
@@ -1083,11 +1087,16 @@ int cache_recv_insert(time_t now, int fd)
|
||||
!read_write(fd, (unsigned char *)&validatecount, sizeof(validatecount), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&validatecountp, sizeof(validatecountp), RW_READ)))
|
||||
return 0;
|
||||
|
||||
if (op == PIPE_OP_KILLED)
|
||||
my_syslog(LOG_INFO, _("TCP process for DNSSEC validation timed out"));
|
||||
|
||||
/* There's a tiny chance that the frec may have been freed
|
||||
and reused before the TCP process returns. Detect that with
|
||||
the uid field which is unique modulo 2^32 for each use. */
|
||||
if (uid == forward->uid)
|
||||
/* There's a chance that the frec may have timed-out and been
|
||||
freed before the TCP process returns, so check that the
|
||||
frec is still marked in use. Even if it is in use, there's a tiny chance
|
||||
that the frec may have been freed and _reused_ before the TCP process returns.
|
||||
Detect that with the uid field which is unique modulo 2^32 for each use. */
|
||||
if (forward->sentto && uid == forward->uid)
|
||||
{
|
||||
if (op == PIPE_OP_RESULT)
|
||||
{
|
||||
@@ -1400,9 +1409,10 @@ static int eatspace(FILE *f)
|
||||
}
|
||||
}
|
||||
|
||||
static int gettok(FILE *f, char *token)
|
||||
static int gettok(FILE *f, char *token, size_t buffsz)
|
||||
{
|
||||
int c, count = 0;
|
||||
int c;
|
||||
unsigned int count = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
@@ -1415,7 +1425,7 @@ static int gettok(FILE *f, char *token)
|
||||
return eatspace(f);
|
||||
}
|
||||
|
||||
if (count < (MAXDNAME - 1))
|
||||
if (count < (buffsz - 1))
|
||||
{
|
||||
token[count++] = c;
|
||||
token[count] = 0;
|
||||
@@ -1440,7 +1450,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
|
||||
lineno += eatspace(f);
|
||||
|
||||
while ((atnl = gettok(f, token)) != -1)
|
||||
while ((atnl = gettok(f, token, MAXDNAMESTR)) != -1)
|
||||
{
|
||||
if (inet_pton(AF_INET, token, &addr) > 0)
|
||||
{
|
||||
@@ -1458,7 +1468,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
{
|
||||
my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno);
|
||||
while (atnl == 0)
|
||||
atnl = gettok(f, token);
|
||||
atnl = gettok(f, token, MAXDNAMESTR);
|
||||
lineno += atnl;
|
||||
continue;
|
||||
}
|
||||
@@ -1476,7 +1486,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
int fqdn, nomem;
|
||||
char *canon;
|
||||
|
||||
if ((atnl = gettok(f, token)) == -1)
|
||||
if ((atnl = gettok(f, token, MAXDNAMESTR)) == -1)
|
||||
break;
|
||||
|
||||
fqdn = !!strchr(token, '.');
|
||||
@@ -1790,7 +1800,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
|
||||
/* Name in hosts, address doesn't match */
|
||||
if (fail_crec)
|
||||
{
|
||||
inet_ntop(prot, &fail_crec->addr, daemon->namebuff, MAXDNAME);
|
||||
inet_ntop(prot, &fail_crec->addr, daemon->namebuff, MAXDNAMESTR);
|
||||
my_syslog(MS_DHCP | LOG_WARNING,
|
||||
_("not giving name %s to the DHCP lease of %s because "
|
||||
"the name exists in %s with address %s"),
|
||||
@@ -2328,7 +2338,7 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
return;
|
||||
|
||||
/* build query type string if requested */
|
||||
if (!(flags & (F_SERVER | F_IPSET | F_QUERY)) && type > 0)
|
||||
if (!(flags & (F_SERVER | F_IPSET | F_QUERY | F_KEYTAG | F_RR)) && type > 0)
|
||||
arg = querystr(arg, type);
|
||||
|
||||
dest = arg;
|
||||
@@ -2344,19 +2354,25 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
{
|
||||
dest = daemon->addrbuff;
|
||||
|
||||
if (flags & F_RR)
|
||||
{
|
||||
if (flags & F_KEYTAG)
|
||||
dest = querystr(NULL, addr->rrblock.rrtype);
|
||||
else
|
||||
dest = querystr(NULL, addr->rrdata.rrtype);
|
||||
}
|
||||
else if (flags & F_KEYTAG)
|
||||
sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest);
|
||||
if (flags & F_RR)
|
||||
{
|
||||
if (flags & F_KEYTAG)
|
||||
dest = querystr(NULL, addr->rrblock.rrtype);
|
||||
else
|
||||
dest = querystr(NULL, addr->rrdata.rrtype);
|
||||
}
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if (flags & F_KEYTAG)
|
||||
{
|
||||
snprintf(daemon->addrbuff, ADDRSTRLEN, arg, addr->log.keytag, addr->log.algo, addr->log.digest);
|
||||
if (type)
|
||||
extra = " (not supported)";
|
||||
}
|
||||
#endif
|
||||
else if (flags & F_RCODE)
|
||||
{
|
||||
unsigned int rcode = addr->log.rcode;
|
||||
|
||||
|
||||
if (rcode == SERVFAIL)
|
||||
dest = "SERVFAIL";
|
||||
else if (rcode == REFUSED)
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
#define FTABSIZ 150 /* max number of outstanding requests (default) */
|
||||
#define MAX_PROCS 20 /* default max no children for TCP requests */
|
||||
#define MAX_PROCS 30 /* default max no children for TCP requests */
|
||||
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
|
||||
#define TCP_MAX_QUERIES 100 /* Maximum number of queries per incoming TCP connection */
|
||||
#define TCP_TIMEOUT 5 /* timeout waiting to connect to an upstream server - double this for answer */
|
||||
@@ -43,7 +43,7 @@
|
||||
#define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */
|
||||
#define DECLINE_BACKOFF 600 /* disable DECLINEd static addresses for this long */
|
||||
#define DHCP_PACKET_MAX 16384 /* hard limit on DHCP packet size */
|
||||
#define SMALLDNAME 50 /* most domain names are smaller than this */
|
||||
#define SMALLDNAME 75 /* most domain names are smaller than this */
|
||||
#define CNAME_CHAIN 10 /* chains longer than this atr dropped for loop protection */
|
||||
#define DNSSEC_MIN_TTL 60 /* DNSKEY and DS records in cache last at least this long */
|
||||
#define HOSTSFILE "/etc/hosts"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+23
-2
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -93,7 +93,15 @@ static void null_hash_update(void *ctxv, size_t length, const uint8_t *src)
|
||||
memcpy(null_hash_buff + ctx->len, src, length);
|
||||
ctx->len += length;
|
||||
}
|
||||
|
||||
|
||||
/* The prototype changes in nettle 4.0 to omit the length argument */
|
||||
#if MIN_VERSION(4, 0)
|
||||
static void null_hash_digest(void *ctx, uint8_t *dst)
|
||||
{
|
||||
((struct null_hash_digest *)dst)->buff = null_hash_buff;
|
||||
((struct null_hash_digest *)dst)->len = ((struct null_hash_ctx *)ctx)->len;
|
||||
}
|
||||
#else
|
||||
static void null_hash_digest(void *ctx, size_t length, uint8_t *dst)
|
||||
{
|
||||
(void)length;
|
||||
@@ -101,6 +109,7 @@ static void null_hash_digest(void *ctx, size_t length, uint8_t *dst)
|
||||
((struct null_hash_digest *)dst)->buff = null_hash_buff;
|
||||
((struct null_hash_digest *)dst)->len = ((struct null_hash_ctx *)ctx)->len;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct nettle_hash null_hash = {
|
||||
"null_hash",
|
||||
@@ -501,4 +510,16 @@ const struct nettle_hash *hash_find(char *name)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* The prototype changes in nettle 4.0 to omit the length argument */
|
||||
void nettle_digest_wrapper(const struct nettle_hash *hash, void *ctx, size_t length, uint8_t *dst)
|
||||
{
|
||||
#if MIN_VERSION(4, 0)
|
||||
(void)length;
|
||||
hash->digest(ctx, dst);
|
||||
#else
|
||||
hash->digest(ctx, length, dst);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#endif /* defined(HAVE_DNSSEC) */
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -736,7 +736,7 @@ static void add_dict_entry(DBusMessageIter *container, const char *key, const ch
|
||||
|
||||
static void add_dict_int(DBusMessageIter *container, const char *key, const unsigned int val)
|
||||
{
|
||||
snprintf(daemon->namebuff, MAXDNAME, "%u", val);
|
||||
snprintf(daemon->namebuff, MAXDNAMESTR, "%u", val);
|
||||
|
||||
add_dict_entry(container, key, daemon->namebuff);
|
||||
}
|
||||
@@ -1055,7 +1055,7 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
|
||||
DBusConnection *connection = (DBusConnection *)daemon->dbus;
|
||||
DBusMessage* message = NULL;
|
||||
DBusMessageIter args;
|
||||
char *action_str, *mac = daemon->namebuff;
|
||||
char *action_str, *mac;
|
||||
unsigned char *p;
|
||||
int i;
|
||||
|
||||
@@ -1068,7 +1068,7 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
|
||||
#ifdef HAVE_DHCP6
|
||||
if (lease->flags & (LEASE_TA | LEASE_NA))
|
||||
{
|
||||
print_mac(mac, lease->clid, lease->clid_len);
|
||||
mac = print_mac(lease->clid, lease->clid_len);
|
||||
inet_ntop(AF_INET6, &lease->addr6, daemon->addrbuff, ADDRSTRLEN);
|
||||
}
|
||||
else
|
||||
@@ -1076,7 +1076,7 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
|
||||
{
|
||||
p = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len,
|
||||
lease->hwaddr, lease->clid_len, lease->clid, &i);
|
||||
print_mac(mac, p, i);
|
||||
mac = print_mac(p, i);
|
||||
inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
}
|
||||
|
||||
|
||||
+13
-14
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -278,9 +278,9 @@ void log_tags(struct dhcp_netid *netid, u32 xid)
|
||||
|
||||
if (!n)
|
||||
{
|
||||
strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
|
||||
strncat (s, netid->net, MAXDNAMESTR - strlen(s));
|
||||
if (netid->next)
|
||||
strncat (s, ", ", (MAXDNAME-1) - strlen(s));
|
||||
strncat (s, ", ", MAXDNAMESTR - strlen(s));
|
||||
}
|
||||
}
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), xid, s);
|
||||
@@ -882,7 +882,7 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len,
|
||||
buf[j++] = c;
|
||||
}
|
||||
i = l;
|
||||
if (val[i] != 0 && j < buf_len)
|
||||
if (i < opt_len && val[i] != 0 && j < buf_len)
|
||||
buf[j++] = '.';
|
||||
}
|
||||
}
|
||||
@@ -892,24 +892,23 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len,
|
||||
unsigned char *p;
|
||||
|
||||
i = 0, j = 0;
|
||||
while (1)
|
||||
while (i + 2 <= opt_len)
|
||||
{
|
||||
p = &val[i];
|
||||
GETSHORT(len, p);
|
||||
if (i + 2 + len > opt_len)
|
||||
break; /* malformed: body extends beyond option */
|
||||
for (k = 0; k < len && j < buf_len; k++)
|
||||
{
|
||||
char c = *p++;
|
||||
if (isprint((unsigned char)c))
|
||||
buf[j++] = c;
|
||||
}
|
||||
i += len +2;
|
||||
if (i >= opt_len)
|
||||
break;
|
||||
|
||||
if (j < buf_len)
|
||||
i += len + 2;
|
||||
if (i < opt_len && j < buf_len)
|
||||
buf[j++] = ',';
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
|
||||
{
|
||||
@@ -937,11 +936,11 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len,
|
||||
trunc = 1;
|
||||
opt_len = 14;
|
||||
}
|
||||
print_mac(buf, val, opt_len);
|
||||
|
||||
*buf = 0;
|
||||
strncat(buf, print_mac(val, opt_len), buf_len);
|
||||
if (trunc)
|
||||
strncat(buf, "...", buf_len - strlen(buf));
|
||||
|
||||
|
||||
}
|
||||
|
||||
return ot[o].name ? ot[o].name : "";
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+9
-8
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -925,7 +925,8 @@ void dhcp_read_ethers(void)
|
||||
{
|
||||
FILE *f = fopen(ETHERSFILE, "r");
|
||||
unsigned int flags;
|
||||
char *buff = daemon->namebuff;
|
||||
size_t linesz = 0;
|
||||
char *line = NULL;
|
||||
char *ip, *cp;
|
||||
struct in_addr addr;
|
||||
unsigned char hwaddr[ETHER_ADDR_LEN];
|
||||
@@ -958,22 +959,22 @@ void dhcp_read_ethers(void)
|
||||
up = &config->next;
|
||||
}
|
||||
|
||||
while (fgets(buff, MAXDNAME, f))
|
||||
while (get_line_alloc(f, &line, &linesz))
|
||||
{
|
||||
char *host = NULL;
|
||||
|
||||
lineno++;
|
||||
|
||||
while (strlen(buff) > 0 && isspace((unsigned char)buff[strlen(buff)-1]))
|
||||
buff[strlen(buff)-1] = 0;
|
||||
while (strlen(line) > 0 && isspace((unsigned char)line[strlen(line)-1]))
|
||||
line[strlen(line)-1] = 0;
|
||||
|
||||
if ((*buff == '#') || (*buff == '+') || (*buff == 0))
|
||||
if ((*line == '#') || (*line == '+') || (*line == 0))
|
||||
continue;
|
||||
|
||||
for (ip = buff; *ip && !isspace((unsigned char)*ip); ip++);
|
||||
for (ip = line; *ip && !isspace((unsigned char)*ip); ip++);
|
||||
for(; *ip && isspace((unsigned char)*ip); ip++)
|
||||
*ip = 0;
|
||||
if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
|
||||
if (!*ip || parse_hex(line, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
|
||||
{
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
|
||||
continue;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+31
-3
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -22,8 +22,34 @@
|
||||
#define IN6ADDRSZ 16
|
||||
#define INADDRSZ 4
|
||||
|
||||
/* Dnsmasq handles domains names internally as NULL-terminated C strings.
|
||||
The '.' characters in these strings are significant as label-seperators.
|
||||
'.' and /0 characters _within_ labels are escaped and take up two
|
||||
characters to allow labels to contain arbitrary characters.
|
||||
|
||||
The maximum length of a domain name in wire format is 255 bytes.
|
||||
and the maximum length of this when coverted to a <label>.<label> string
|
||||
is 253 characters. Reasoning: the wire format representation of a
|
||||
label is one byte length followed by up to length characters so each of
|
||||
these labels except the last takes the same space since the length is
|
||||
replaced by the '.' seperator.
|
||||
The last label doesn't have the "." so it takes one less character
|
||||
and the terminating zero-length label takes no character either.
|
||||
|
||||
When we introduce the dnsmasq escaped format, the maximum length
|
||||
of this representation is when there are the minimum number of labels,
|
||||
since that gives us the maximum number of characters that may need
|
||||
escaping. The minimum number of labels is four, so the overhead
|
||||
is five bytes (four non-zero lengths plus the zero length terminator)
|
||||
leaving a total of 250 characters in labels. If every one of these
|
||||
is escaped and becomes two characters, that gives 500 characters,
|
||||
plus the three '.' seperators between the four labels, for a total of
|
||||
503 characters.
|
||||
*/
|
||||
#define MAXDNAME 255 /* Maximum size of a domain name in wire format */
|
||||
#define MAXDNAMESTR 503 /* Maximum size of dnsmasq c-string representaion of a domain name. */
|
||||
|
||||
#define PACKETSZ 512 /* maximum packet size */
|
||||
#define MAXDNAME 1025 /* maximum presentation domain name */
|
||||
#define RRFIXEDSZ 10 /* #/bytes of fixed data in r record */
|
||||
#define MAXLABEL 63 /* maximum length of domain label */
|
||||
|
||||
@@ -185,10 +211,12 @@ struct dns_header {
|
||||
#define ADD_RDLEN(header, pp, plen, len) \
|
||||
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
|
||||
|
||||
/* Escape character in our presentation format for names.
|
||||
/* Escape character in our internal c-string format for names.
|
||||
Cannot be '.' or /000 and must be !isprint().
|
||||
Note that escaped chars are stored as
|
||||
<NAME_ESCAPE> <orig-char+1>
|
||||
to ensure that the escaped form of /000 doesn't include /000
|
||||
*/
|
||||
#define NAME_ESCAPE 1
|
||||
#define IS_NAME_ESCAPE(x) ((x) == 0 || (x) == '.' || (x) == NAME_ESCAPE)
|
||||
|
||||
+32
-19
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -135,13 +135,8 @@ int main (int argc, char **argv)
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
/* Note that both /000 and '.' are allowed within labels. These get
|
||||
represented in presentation format using NAME_ESCAPE as an escape
|
||||
character. In theory, if all the characters in a name were /000 or
|
||||
'.' or NAME_ESCAPE then all would have to be escaped, so the
|
||||
presentation format would be twice as long as the spec. */
|
||||
daemon->keyname = safe_malloc((MAXDNAME * 2) + 1);
|
||||
daemon->cname = safe_malloc((MAXDNAME * 2) + 1);
|
||||
daemon->keyname = safe_malloc(MAXDNAMESTR + 1);
|
||||
daemon->cname = safe_malloc(MAXDNAMESTR + 1);
|
||||
/* one char flag per possible RR in answer section (may get extended). */
|
||||
daemon->rr_status_sz = 64;
|
||||
daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz);
|
||||
@@ -199,7 +194,7 @@ int main (int argc, char **argv)
|
||||
/* Must have at least a root trust anchor, or the DNSSEC code
|
||||
can loop forever. */
|
||||
for (ds = daemon->ds; ds; ds = ds->next)
|
||||
if (ds->name[0] == 0)
|
||||
if (ds->name && ds->name[0] == 0)
|
||||
break;
|
||||
|
||||
if (!ds)
|
||||
@@ -437,14 +432,6 @@ int main (int argc, char **argv)
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
if ((daemon->port != 0 && !option_bool(OPT_NO_RESOLV)) ||
|
||||
daemon->dynamic_dirs)
|
||||
inotify_dnsmasq_init();
|
||||
else
|
||||
daemon->inotifyfd = -1;
|
||||
#endif
|
||||
|
||||
if (daemon->dump_file)
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_init();
|
||||
@@ -864,6 +851,14 @@ int main (int argc, char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
if ((daemon->port != 0 && !option_bool(OPT_NO_RESOLV)) ||
|
||||
daemon->dynamic_dirs)
|
||||
inotify_dnsmasq_init(err_pipe[1]);
|
||||
else
|
||||
daemon->inotifyfd = -1;
|
||||
#endif
|
||||
|
||||
/* Don't start logging malloc before logging is set up. */
|
||||
daemon->log_malloc = option_bool(OPT_LOG_MALLOC);
|
||||
|
||||
@@ -1385,8 +1380,6 @@ static void sig_handler(int sig)
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)(&daemon->forward_to_tcp), sizeof(daemon->forward_to_tcp), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)(&daemon->forward_to_tcp->uid), sizeof(daemon->forward_to_tcp->uid), RW_WRITE);
|
||||
|
||||
my_syslog(LOG_INFO, _("TCP process for DNSSEC validation timed out"));
|
||||
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
@@ -1545,6 +1538,26 @@ static void fatal_event(struct event_desc *ev, char *msg)
|
||||
/* fall through */
|
||||
case EVENT_TIME_ERR:
|
||||
die(_("cannot create timestamp file %s: %s" ), msg, EC_BADCONF);
|
||||
|
||||
/* fall through */
|
||||
case EVENT_LINK_ERR:
|
||||
die(_("cannot access path %s: %s" ), msg, EC_MISC);
|
||||
|
||||
/* fall through */
|
||||
case EVENT_INOTFY_ERR:
|
||||
die(_("failed to create inotify: %s" ), NULL, EC_MISC);
|
||||
|
||||
/* fall through */
|
||||
case EVENT_TMSL_ERR:
|
||||
die(_("too many symlinks following %s"), msg, EC_MISC);
|
||||
|
||||
/* fall through */
|
||||
case EVENT_RESOLV_ERR:
|
||||
die(_("directory %s for resolv-file is missing, cannot poll"), msg, EC_MISC);
|
||||
|
||||
/* fall through */
|
||||
case EVENT_IFILE_ERR:
|
||||
die(_("failed to create inotify for %s: %s"), msg, EC_MISC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
-13
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -14,7 +14,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define COPYRIGHT "Copyright (c) 2000-2025 Simon Kelley"
|
||||
#define COPYRIGHT "Copyright (c) 2000-2026 Simon Kelley"
|
||||
|
||||
/* We do defines that influence behavior of stdio.h, so complain
|
||||
if included too early. */
|
||||
@@ -201,6 +201,11 @@ struct event_desc {
|
||||
#define EVENT_TIME_ERR 24
|
||||
#define EVENT_SCRIPT_LOG 25
|
||||
#define EVENT_TIME 26
|
||||
#define EVENT_LINK_ERR 27
|
||||
#define EVENT_INOTFY_ERR 28
|
||||
#define EVENT_TMSL_ERR 29
|
||||
#define EVENT_RESOLV_ERR 30
|
||||
#define EVENT_IFILE_ERR 31
|
||||
|
||||
/* Exit codes. */
|
||||
#define EC_GOOD 0
|
||||
@@ -481,7 +486,7 @@ struct interface_name {
|
||||
};
|
||||
|
||||
union bigname {
|
||||
char name[MAXDNAME];
|
||||
char name[MAXDNAMESTR+1];
|
||||
union bigname *next; /* freelist */
|
||||
};
|
||||
|
||||
@@ -1266,10 +1271,10 @@ extern struct daemon {
|
||||
/* globally used stuff for DNS */
|
||||
char *packet; /* packet buffer */
|
||||
int packet_buff_sz; /* size of above */
|
||||
char *namebuff; /* MAXDNAME size buffer */
|
||||
char *namebuff; /* MAXDNAMESTR+1 size buffer */
|
||||
char *workspacename;
|
||||
#ifdef HAVE_DNSSEC
|
||||
char *keyname, *cname; /* MAXDNAME size buffer */
|
||||
char *keyname, *cname; /* MAXDNAMESTR+1 size buffer */
|
||||
unsigned long *rr_status; /* ceiling in TTL from DNSSEC or zero for insecure */
|
||||
int rr_status_sz;
|
||||
int dnssec_no_time_check;
|
||||
@@ -1463,11 +1468,12 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
|
||||
|
||||
/* dnssec.c */
|
||||
#ifdef HAVE_DNSSEC
|
||||
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int id, int type);
|
||||
size_t dnssec_generate_query(struct dns_header *header, size_t outlen, char *name, int class, int id, int type);
|
||||
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name,
|
||||
char *keyname, int class, int *validate_count);
|
||||
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name,
|
||||
char *keyname, int class, int *validate_count);
|
||||
int cache_neg_ds(char *name, int flags, int class, time_t now, int neg_ttl);
|
||||
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class,
|
||||
int check_unsigned, int *neganswer, int *prim_ok, int *nons, int *nsec_ttl, int *validate_count);
|
||||
int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen);
|
||||
@@ -1484,6 +1490,7 @@ int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig,
|
||||
char *ds_digest_name(int digest);
|
||||
char *algo_digest_name(int algo);
|
||||
char *nsec3_digest_name(int digest);
|
||||
void nettle_digest_wrapper(const struct nettle_hash *hash, void *ctx, size_t length, uint8_t *dst);
|
||||
|
||||
/* util.c */
|
||||
void rand_init(void);
|
||||
@@ -1493,7 +1500,7 @@ u64 rand64(void);
|
||||
int rr_on_list(struct rrlist *list, unsigned short rr);
|
||||
int legal_hostname(char *name);
|
||||
char *canonicalise(char *in, int *nomem);
|
||||
unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit);
|
||||
unsigned char *do_rfc1035_name(unsigned char *p, char *sval, unsigned char *limit);
|
||||
void *safe_malloc(size_t size);
|
||||
void safe_strncpy(char *dest, const char *src, size_t size);
|
||||
void safe_pipe(int *fd, int read_noblock);
|
||||
@@ -1509,6 +1516,7 @@ void *whine_malloc_real(const char *func, unsigned int line, size_t size);
|
||||
void *whine_realloc_real(const char *wrapper, const char *func, unsigned int line, void *ptr, size_t size);
|
||||
int expand_buf_real(const char *func, unsigned int line, struct iovec *iov, size_t size);
|
||||
int expand_workspace_real(const char *func, unsigned int line, unsigned char ***wkspc, int *szp, int new);
|
||||
int get_line_alloc(FILE *f, char **buffp, size_t *sizep);
|
||||
int sa_len(union mysockaddr *addr);
|
||||
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2);
|
||||
int sockaddr_isnull(const union mysockaddr *s);
|
||||
@@ -1530,7 +1538,7 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
|
||||
unsigned int *wildcard_mask, int *mac_type);
|
||||
int memcmp_masked(unsigned char *a, unsigned char *b, int len,
|
||||
unsigned int mask);
|
||||
char *print_mac(char *buff, unsigned char *mac, int len);
|
||||
char *print_mac(unsigned char *mac, int len);
|
||||
int read_write(int fd, unsigned char *packet, int size, int rw);
|
||||
int read_writev(int fd, struct iovec *iov, int iovcnt, int rw);
|
||||
void close_fds(long max_fd, int spare1, int spare2, int spare3);
|
||||
@@ -1907,7 +1915,7 @@ int detect_loop(char *query, int type);
|
||||
|
||||
/* inotify.c */
|
||||
#ifdef HAVE_INOTIFY
|
||||
void inotify_dnsmasq_init(void);
|
||||
void inotify_dnsmasq_init(int errfd);
|
||||
int inotify_check(time_t now);
|
||||
void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz);
|
||||
#endif
|
||||
@@ -1931,11 +1939,11 @@ void from_wire(char *name);
|
||||
/* edns0.c */
|
||||
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
|
||||
size_t *len, unsigned char **p, int *is_sign, int *is_last);
|
||||
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
size_t add_pseudoheader(struct dns_header *header, size_t plen, size_t out_size,
|
||||
int optno, unsigned char *opt, size_t optlen, int set_do, int replace);
|
||||
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit);
|
||||
size_t add_do_bit(struct dns_header *header, size_t plen, size_t outlen);
|
||||
void edns0_needs_mac(union mysockaddr *addr, time_t now);
|
||||
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
size_t add_edns0_config(struct dns_header *header, size_t plen, size_t outlen,
|
||||
union mysockaddr *source, time_t now, int *cacheable);
|
||||
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
|
||||
|
||||
@@ -1958,7 +1966,7 @@ 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);
|
||||
char *name, size_t 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 is_ds, int *firstp, int *lastp);
|
||||
|
||||
+113
-127
@@ -1,5 +1,5 @@
|
||||
/* dnssec.c is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
|
||||
and Copyright (c) 2012-2025 Simon Kelley
|
||||
and Copyright (c) 2012-2026 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
|
||||
@@ -144,7 +144,7 @@ static int is_check_date(unsigned long curtime)
|
||||
Init state->ip with the RR, and state->end with the end of same.
|
||||
Init state->op to NULL.
|
||||
Init state->desc to RR descriptor.
|
||||
Init state->buff with a MAXDNAME * 2 buffer.
|
||||
Init state->buff with a MAXDNAMESTR+1 buffer.
|
||||
|
||||
After each call which returns 1, state->op points to the next byte of data.
|
||||
On returning 0, the end has been reached.
|
||||
@@ -177,7 +177,7 @@ static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state
|
||||
if ((state->c = state->end - state->ip) != 0)
|
||||
{
|
||||
state->op = state->ip;
|
||||
state->ip = state->end;;
|
||||
state->ip = state->end;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
@@ -189,24 +189,28 @@ static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state
|
||||
if (d == (u16)0)
|
||||
{
|
||||
/* domain-name, canonicalise */
|
||||
int len;
|
||||
|
||||
/* If the name is malformed, or runs beyond the end of the RR, abort.
|
||||
under no circumstances should state->ip be beyond state->end since
|
||||
that will cause the run-out code above to turn the negative
|
||||
calculted length into a very large postive value for state->c
|
||||
and overrun the buffer. */
|
||||
if (!extract_name(header, plen, &state->ip, state->buff, EXTR_NAME_EXTRACT, 0) ||
|
||||
(len = to_wire(state->buff)) == 0)
|
||||
continue;
|
||||
|
||||
state->c = len;
|
||||
state->ip > state->end)
|
||||
return 0;
|
||||
|
||||
/* to_wire() always returns length > 0 */
|
||||
state->c = to_wire(state->buff);
|
||||
state->op = (unsigned char *)state->buff;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* plain data preceding a domain-name, don't run off the end of the data */
|
||||
if ((state->end - state->ip) < d)
|
||||
d = state->end - state->ip;
|
||||
|
||||
if (d == 0)
|
||||
continue;
|
||||
|
||||
/* Plain data preceding a domain-name.
|
||||
Handle short RR, if there are no bytes left, return finished. */
|
||||
if ((state->end - state->ip) < d &&
|
||||
(d = state->end - state->ip) == 0)
|
||||
return 0;
|
||||
|
||||
state->op = state->ip;
|
||||
state->c = d;
|
||||
state->ip += d;
|
||||
@@ -383,7 +387,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
|
||||
an attacker using signatures made with the key of an unrelated
|
||||
zone he controls. Note that the root key is always allowed.
|
||||
Ignore sigs which aren't valid */
|
||||
if (*daemon->workspacename == 0 || hostname_issubdomain(name, daemon->workspacename) != 0)
|
||||
if (*daemon->workspacename == 0 || hostname_issubdomain(daemon->workspacename, name) != 0)
|
||||
{
|
||||
if (gotkey)
|
||||
{
|
||||
@@ -543,10 +547,14 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
|
||||
*ttl_out = ttl;
|
||||
}
|
||||
|
||||
|
||||
/* Don't trust rdlen not to be too small and give us a negative sig_len
|
||||
It has already been checked that it doesn't run us off the end
|
||||
of the packet. */
|
||||
if ((sig_len = rdlen - (p - psav)) <= 0)
|
||||
return STAT_BOGUS;
|
||||
|
||||
sig = p;
|
||||
sig_len = rdlen - (p - psav);
|
||||
|
||||
nsigttl = htonl(orig_ttl);
|
||||
|
||||
hash->update(ctx, 18, psav);
|
||||
@@ -656,7 +664,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
}
|
||||
}
|
||||
|
||||
hash->digest(ctx, hash->digest_size, digest);
|
||||
nettle_digest_wrapper(hash, ctx, hash->digest_size, digest);
|
||||
|
||||
/* namebuff used for workspace above, restore to leave unchanged on exit */
|
||||
p = (unsigned char*)(rrset[0]);
|
||||
@@ -836,7 +844,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
rather then O(keys x DSs) */
|
||||
hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name);
|
||||
hash->update(ctx, (unsigned int)rdlen, psave);
|
||||
hash->digest(ctx, hash->digest_size, digest);
|
||||
nettle_digest_wrapper(hash, ctx, hash->digest_size, digest);
|
||||
|
||||
from_wire(name);
|
||||
|
||||
@@ -950,10 +958,7 @@ 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", 0);
|
||||
else
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)", 0);
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu", !algo_digest_name(algo));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1027,21 +1032,21 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
(flags = in_arpa_name_2_addr(name, &a)) &&
|
||||
((flags == F_IPV6 && private_net6(&a.addr6, 0)) || (flags == F_IPV4 && private_net(a.addr4, 0))))
|
||||
{
|
||||
my_syslog(LOG_INFO, _("Insecure reply received for DS %s, assuming that's OK for a RFC-1918 address."), name);
|
||||
my_syslog(LOG_INFO, _("insecure reply received for DS %s, assuming that's OK for a RFC-1918 address"), name);
|
||||
neganswer = 1;
|
||||
nons = 0; /* If we're faking a DS, fake one with an NS. */
|
||||
neg_ttl = DNSSEC_ASSUMED_DS_TTL;
|
||||
}
|
||||
else if (lookup_domain(name, F_DOMAINSRV, NULL, NULL))
|
||||
{
|
||||
my_syslog(LOG_INFO, _("Insecure reply received for DS %s, assuming non-DNSSEC domain-specific server."), name);
|
||||
my_syslog(LOG_INFO, _("insecure reply received for DS %s, assuming non-DNSSEC domain-specific server"), name);
|
||||
neganswer = 1;
|
||||
nons = 0; /* If we're faking a DS, fake one with an NS. */
|
||||
neg_ttl = DNSSEC_ASSUMED_DS_TTL;
|
||||
}
|
||||
else
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
|
||||
my_syslog(LOG_WARNING, _("insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0);
|
||||
return STAT_BOGUS | DNSSEC_FAIL_INDET;
|
||||
}
|
||||
@@ -1101,7 +1106,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
a.log.keytag = keytag;
|
||||
a.log.algo = algo;
|
||||
a.log.digest = digest;
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS for keytag %hu, algo %hu, digest %hu (not supported)", 0);
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS for keytag %hu, algo %hu, digest %hu", 1);
|
||||
neg_ttl = ttl;
|
||||
}
|
||||
else if ((key = blockdata_alloc((char*)p, rdlen - 4)))
|
||||
@@ -1141,44 +1146,47 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
}
|
||||
|
||||
flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
|
||||
|
||||
|
||||
if (neganswer)
|
||||
{
|
||||
if (RCODE(header) == NXDOMAIN)
|
||||
flags |= F_NXDOMAIN;
|
||||
|
||||
/* We only cache validated DS records, DNSSECOK flag hijacked
|
||||
to store presence/absence of NS. */
|
||||
if (nons)
|
||||
{
|
||||
if (lookup_domain(name, F_DOMAINSRV, NULL, NULL))
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("Negative DS reply without NS record received for %s, assuming non-DNSSEC domain-specific server."), name);
|
||||
my_syslog(LOG_WARNING, _("negative DS reply without NS record received for %s, assuming non-DNSSEC domain-specific server"), name);
|
||||
nons = 0;
|
||||
neg_ttl = DNSSEC_ASSUMED_DS_TTL;
|
||||
}
|
||||
else
|
||||
/* We only cache validated DS records, DNSSECOK flag hijacked
|
||||
to store presence/absence of NS. */
|
||||
flags &= ~F_DNSSECOK;
|
||||
}
|
||||
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL,
|
||||
servfail ? "SERVFAIL" : (nons ? "no DS/cut" : "no DS"), 0);
|
||||
}
|
||||
|
||||
|
||||
return cache_neg_ds(name, flags, class, now, neg_ttl);
|
||||
|
||||
}
|
||||
|
||||
int cache_neg_ds(char *name, int flags, int class, time_t now, int ttl)
|
||||
{
|
||||
cache_start_insert();
|
||||
|
||||
/* Use TTL from NSEC for negative cache entries */
|
||||
if (!cache_insert(name, NULL, class, now, neg_ttl, flags))
|
||||
if (!cache_insert(name, NULL, class, now, ttl, flags))
|
||||
return STAT_ABANDONED;
|
||||
|
||||
cache_end_insert();
|
||||
|
||||
if (neganswer)
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL,
|
||||
servfail ? "SERVFAIL" : (nons ? "no DS/cut" : "no DS"), 0);
|
||||
|
||||
return STAT_OK;
|
||||
}
|
||||
|
||||
|
||||
/* 4034 6.1 */
|
||||
static int hostname_cmp(const char *a, const char *b)
|
||||
{
|
||||
@@ -1243,15 +1251,23 @@ static int hostname_cmp(const char *a, const char *b)
|
||||
}
|
||||
}
|
||||
|
||||
static int check_type_bitmap(unsigned char *p, int type)
|
||||
{
|
||||
int offset = (type & 0xff) >> 3;
|
||||
|
||||
if (p[0] == type >> 8 && p[1] > offset && (p[offset + 2] & (0x80 >> (type & 0x07))))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns 0 on success, or DNSSEC_FAIL_* value on failure. */
|
||||
static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, unsigned char **labels, int nsec_count,
|
||||
char *workspace1_in, char *workspace2, char *name, int type, int *nons)
|
||||
{
|
||||
int i, rc, rdlen;
|
||||
unsigned char *p, *psave;
|
||||
int offset = (type & 0xff) >> 3;
|
||||
int mask = 0x80 >> (type & 0x07);
|
||||
|
||||
|
||||
if (nons)
|
||||
*nons = 1;
|
||||
|
||||
@@ -1295,17 +1311,6 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
|
||||
/* rdlen is now length of type map, and p points to it
|
||||
packet checked to be as long as rdlen implies in prove_non_existence() */
|
||||
|
||||
/* check that the first typemap is complete. */
|
||||
if (rdlen < 2 || rdlen < p[1] + 2)
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
/* RFC 6672 5.3.4.1. */
|
||||
#define DNAME_OFFSET (T_DNAME >> 3)
|
||||
#define DNAME_MASK (0x80 >> (T_DNAME & 0x07))
|
||||
if (p[0] == 0 && (p[1] >= DNAME_OFFSET + 1) && (p[2 + DNAME_OFFSET] & DNAME_MASK) != 0 &&
|
||||
hostname_issubdomain(name, workspace1) == 1)
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
rc = hostname_cmp(workspace1, name);
|
||||
|
||||
if (rc == 0)
|
||||
@@ -1314,42 +1319,37 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
|
||||
if (type == T_NSEC || type == T_RRSIG)
|
||||
return 0;
|
||||
|
||||
/* NSEC with the same name as the RR we're testing, check
|
||||
that the type in question doesn't appear in the type map */
|
||||
if (p[0] == 0 && p[1] >= 1)
|
||||
{
|
||||
/* If we can prove that there's no NS record, return that information. */
|
||||
if (nons && (p[2] & (0x80 >> T_NS)) != 0)
|
||||
*nons = 0;
|
||||
|
||||
/* A CNAME answer would also be valid, so if there's a CNAME is should
|
||||
have been returned. */
|
||||
if ((p[2] & (0x80 >> T_CNAME)) != 0)
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
/* If the SOA bit is set for a DS record, then we have the
|
||||
DS from the wrong side of the delegation. For the root DS,
|
||||
this is expected. */
|
||||
if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
}
|
||||
|
||||
while (rdlen > 0)
|
||||
{
|
||||
if (rdlen < 2 || rdlen < p[1] + 2)
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
if (p[0] == type >> 8)
|
||||
{
|
||||
/* Does the NSEC say our type exists? */
|
||||
if (offset < p[1] && (p[offset+2] & mask) != 0)
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
break; /* finished checking */
|
||||
}
|
||||
/* If we can prove that there's no NS record, return that information. */
|
||||
if (nons && check_type_bitmap(p, T_NS))
|
||||
*nons = 0;
|
||||
|
||||
rdlen -= p[1];
|
||||
p += p[1];
|
||||
/* NSEC with the same name as the RR we're testing, check
|
||||
that the type in question doesn't appear in the type map */
|
||||
if (check_type_bitmap(p, type))
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
/* A CNAME answer would also be valid, so if there's a CNAME it should
|
||||
have been returned. */
|
||||
if (check_type_bitmap(p, T_CNAME))
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
/* If the SOA bit is set for a DS record, then we have the
|
||||
DS from the wrong side of the delegation. For the root DS,
|
||||
this is expected. */
|
||||
if (name_labels != 0 && type == T_DS && check_type_bitmap(p, T_SOA))
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
/* RFC 6672 5.3.4.1. */
|
||||
if (check_type_bitmap(p, T_DNAME) && hostname_issubdomain(name, workspace1) == 1)
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
rdlen -= p[1] + 2;
|
||||
p += p[1] + 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1385,13 +1385,13 @@ static int hash_name(char *in, unsigned char **out, struct nettle_hash const *ha
|
||||
|
||||
hash->update(ctx, to_wire(in), (unsigned char *)in);
|
||||
hash->update(ctx, salt_len, salt);
|
||||
hash->digest(ctx, hash->digest_size, digest);
|
||||
nettle_digest_wrapper(hash, ctx, hash->digest_size, digest);
|
||||
|
||||
for(i = 0; i < iterations; i++)
|
||||
{
|
||||
hash->update(ctx, hash->digest_size, digest);
|
||||
hash->update(ctx, salt_len, salt);
|
||||
hash->digest(ctx, hash->digest_size, digest);
|
||||
nettle_digest_wrapper(hash, ctx, hash->digest_size, digest);
|
||||
}
|
||||
|
||||
from_wire(in);
|
||||
@@ -1437,6 +1437,7 @@ static int base32_decode(char *in, unsigned char *out)
|
||||
return p - out;
|
||||
}
|
||||
|
||||
/* return 1 if we can prove record _doesn't_ exist */
|
||||
static int check_nsec3_coverage(struct dns_header *header, size_t plen, int digest_len, unsigned char *digest, int type,
|
||||
char *workspace1, char *workspace2, unsigned char **nsecs, int nsec_count, int *nons, int name_labels)
|
||||
{
|
||||
@@ -1473,50 +1474,35 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
|
||||
we just need to check the type map. p points to the RR data for the record.
|
||||
Note we have packet length up to rdlen bytes checked. */
|
||||
|
||||
int offset = (type & 0xff) >> 3;
|
||||
int mask = 0x80 >> (type & 0x07);
|
||||
|
||||
p += hash_len; /* skip next-domain hash */
|
||||
rdlen -= p - psave;
|
||||
|
||||
/* check that the first typemap is complete. */
|
||||
if (rdlen < 2 || rdlen < p[1] + 2)
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
if (p[0] == 0 && p[1] >= 1)
|
||||
{
|
||||
/* If we can prove that there's no NS record, return that information. */
|
||||
if (nons && (p[2] & (0x80 >> T_NS)) != 0)
|
||||
*nons = 0;
|
||||
|
||||
/* A CNAME answer would also be valid, so if there's a CNAME is should
|
||||
have been returned. */
|
||||
if ((p[2] & (0x80 >> T_CNAME)) != 0)
|
||||
return 0;
|
||||
|
||||
/* If the SOA bit is set for a DS record, then we have the
|
||||
DS from the wrong side of the delegation. For the root DS,
|
||||
this is expected. */
|
||||
if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (rdlen > 0)
|
||||
{
|
||||
if (rdlen < 2 || rdlen < p[1] + 2)
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
return 0;
|
||||
|
||||
/* If we can prove that there's no NS record, return that information. */
|
||||
if (nons && check_type_bitmap(p, T_NS))
|
||||
*nons = 0;
|
||||
|
||||
if (p[0] == type >> 8)
|
||||
{
|
||||
/* Does the NSEC3 say our type exists? */
|
||||
if (offset < p[1] && (p[offset+2] & mask) != 0)
|
||||
return 0;
|
||||
|
||||
break; /* finished checking */
|
||||
}
|
||||
|
||||
rdlen -= p[1];
|
||||
p += p[1];
|
||||
/* Does the NSEC3 say our type exists? */
|
||||
if (check_type_bitmap(p, type))
|
||||
return 0;
|
||||
|
||||
/* A CNAME answer would also be valid, so if there's a CNAME it should
|
||||
have been returned. */
|
||||
if (check_type_bitmap(p, T_CNAME))
|
||||
return 0;
|
||||
|
||||
/* If the SOA bit is set for a DS record, then we have the
|
||||
DS from the wrong side of the delegation. For the root DS,
|
||||
this is expected. */
|
||||
if (name_labels != 0 && type == T_DS && check_type_bitmap(p, T_SOA))
|
||||
return 0;
|
||||
|
||||
rdlen -= p[1] + 2;
|
||||
p += p[1] + 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -2113,8 +2099,8 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
CNAME must be <subdomain>.<dname>
|
||||
CNAME target must be <subdomain>.<dname_target>
|
||||
<subdomain>s must match for name and target. */
|
||||
if (hostname_issubdomain(daemon->cname, name) == 1 &&
|
||||
hostname_issubdomain(daemon->workspacename, keyname) == 1 &&
|
||||
if (hostname_issubdomain(name, daemon->cname) == 1 &&
|
||||
hostname_issubdomain(keyname, daemon->workspacename) == 1 &&
|
||||
name_prefix_len == strlen(daemon->workspacename) - strlen(keyname))
|
||||
{
|
||||
char save = daemon->cname[name_prefix_len];
|
||||
@@ -2350,7 +2336,7 @@ int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen)
|
||||
}
|
||||
}
|
||||
|
||||
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name,
|
||||
size_t dnssec_generate_query(struct dns_header *header, size_t outlen, char *name,
|
||||
int class, int id, int type)
|
||||
{
|
||||
unsigned char *p;
|
||||
@@ -2374,7 +2360,7 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char
|
||||
PUTSHORT(type, p);
|
||||
PUTSHORT(class, p);
|
||||
|
||||
return add_do_bit(header, p - (unsigned char *)header, end);
|
||||
return add_do_bit(header, p - (unsigned char *)header, outlen);
|
||||
}
|
||||
|
||||
int errflags_to_ede(int status)
|
||||
|
||||
+8
-5
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -79,7 +79,9 @@ void build_server_array(void)
|
||||
for (serv = daemon->local_domains; serv; serv = serv->next, count++)
|
||||
daemon->serverarray[count] = serv;
|
||||
|
||||
qsort(daemon->serverarray, daemon->serverarraysz, sizeof(struct server *), order_qsort);
|
||||
/* serverarray may be unallocated if we have no servers yet. */
|
||||
if (daemon->serverarray)
|
||||
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. */
|
||||
@@ -406,12 +408,13 @@ int is_local_answer(time_t now, int first, char *name)
|
||||
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)
|
||||
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, size_t limit, int first, int last, int ede)
|
||||
{
|
||||
int trunc = 0, anscount = 0;
|
||||
unsigned char *p;
|
||||
int start;
|
||||
union all_addr addr;
|
||||
char *out_end = ((char *)header) + limit;
|
||||
|
||||
setup_reply(header, flags, ede);
|
||||
|
||||
@@ -441,7 +444,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
|
||||
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))
|
||||
if (add_resource_record(header, out_end, &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);
|
||||
}
|
||||
@@ -456,7 +459,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
|
||||
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))
|
||||
if (add_resource_record(header, out_end, &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);
|
||||
}
|
||||
|
||||
+12
-12
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -162,12 +162,12 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
|
||||
if (c->indexed)
|
||||
{
|
||||
unsigned int index = ntohl(addr->addr4.s_addr) - ntohl(c->start.s_addr);
|
||||
snprintf(name, MAXDNAME, "%s%u", c->prefix ? c->prefix : "", index);
|
||||
snprintf(name, MAXDNAMESTR, "%s%u", c->prefix ? c->prefix : "", index);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c->prefix)
|
||||
strncpy(name, c->prefix, MAXDNAME - ADDRSTRLEN);
|
||||
strncpy(name, c->prefix, MAXDNAMESTR - ADDRSTRLEN);
|
||||
|
||||
inet_ntop(AF_INET, &addr->addr4, name + strlen(name), ADDRSTRLEN);
|
||||
for (p = name; *p; p++)
|
||||
@@ -175,8 +175,8 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
|
||||
*p = '-';
|
||||
}
|
||||
|
||||
strncat(name, ".", MAXDNAME);
|
||||
strncat(name, c->domain, MAXDNAME);
|
||||
strncat(name, ".", MAXDNAMESTR - strlen(name) - 1);
|
||||
strncat(name, c->domain, MAXDNAMESTR - strlen(name) - 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -188,7 +188,7 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
|
||||
if (c->indexed)
|
||||
{
|
||||
u64 index = addr6part(&addr->addr6) - addr6part(&c->start6);
|
||||
snprintf(name, MAXDNAME, "%s%llu", c->prefix ? c->prefix : "", index);
|
||||
snprintf(name, MAXDNAMESTR, "%s%llu", c->prefix ? c->prefix : "", index);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -196,21 +196,21 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
|
||||
char frag[6];
|
||||
|
||||
if (c->prefix)
|
||||
strncpy(name, c->prefix, MAXDNAME);
|
||||
strncpy(name, c->prefix, MAXDNAMESTR);
|
||||
|
||||
for (i = 0; i < 16; i += 2)
|
||||
{
|
||||
sprintf(frag, "%s%02x%02x", i == 0 ? "" : "-", addr->addr6.s6_addr[i], addr->addr6.s6_addr[i+1]);
|
||||
strncat(name, frag, MAXDNAME);
|
||||
strncat(name, frag, MAXDNAMESTR - strlen(name) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
strncat(name, ".", MAXDNAME);
|
||||
strncat(name, c->domain, MAXDNAME);
|
||||
|
||||
strncat(name, ".", MAXDNAMESTR - strlen(name) - 1);
|
||||
strncat(name, c->domain, MAXDNAMESTR - strlen(name) - 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -85,7 +85,8 @@ void dump_init(void)
|
||||
/* count existing records */
|
||||
while (read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), RW_READ))
|
||||
{
|
||||
lseek(daemon->dumpfd, pcap_header.incl_len, SEEK_CUR);
|
||||
if (lseek(daemon->dumpfd, pcap_header.incl_len, SEEK_CUR) == (off_t)-1)
|
||||
break;
|
||||
packet_count++;
|
||||
}
|
||||
}
|
||||
|
||||
+28
-23
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -100,13 +100,14 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t
|
||||
replace == 1 ->replace existing or add option
|
||||
replace == 2 ->relpace existing option only.
|
||||
*/
|
||||
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
size_t add_pseudoheader(struct dns_header *header, size_t plen, size_t out_size,
|
||||
int optno, unsigned char *opt, size_t optlen, int set_do, int replace)
|
||||
{
|
||||
unsigned char *lenp = NULL, *datap = NULL, *p, *udp_len, *buff = NULL;
|
||||
int rdlen = 0, is_sign, is_last;
|
||||
unsigned short flags = set_do ? 0x8000 : 0, rcode = 0;
|
||||
|
||||
unsigned char *limit = ((unsigned char *)header) + out_size;
|
||||
|
||||
p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last);
|
||||
|
||||
if (is_sign)
|
||||
@@ -247,9 +248,9 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
|
||||
return p - (unsigned char *)header;
|
||||
}
|
||||
|
||||
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit)
|
||||
size_t add_do_bit(struct dns_header *header, size_t plen, size_t outlen)
|
||||
{
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1, 0);
|
||||
return add_pseudoheader(header, plen, outlen, 0, NULL, 0, 1, 0);
|
||||
}
|
||||
|
||||
static unsigned char char64(unsigned char c)
|
||||
@@ -276,14 +277,16 @@ void edns0_needs_mac(union mysockaddr *addr, time_t now)
|
||||
|
||||
/* OPT_ADD_MAC = MAC is added (if available)
|
||||
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
|
||||
OPT_STRIP_MAC = MAC is removed */
|
||||
static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
OPT_STRIP_MAC = MAC is removed
|
||||
Handle only six byte MACs. */
|
||||
static size_t add_dns_client(struct dns_header *header, size_t plen, size_t outlen,
|
||||
union mysockaddr *l3, time_t now, int *cacheablep)
|
||||
{
|
||||
int replace = 0, maclen = 0;
|
||||
unsigned char mac[DHCP_CHADDR_MAX];
|
||||
char encode[18]; /* handle 6 byte MACs ONLY */
|
||||
|
||||
char encode[9] = { 0 };
|
||||
char *encoded = encode;
|
||||
|
||||
if ((option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX)) && (maclen = find_mac(l3, mac, 1, now)) == 6)
|
||||
{
|
||||
if (option_bool(OPT_STRIP_MAC))
|
||||
@@ -291,7 +294,7 @@ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned ch
|
||||
*cacheablep = 0;
|
||||
|
||||
if (option_bool(OPT_MAC_HEX))
|
||||
print_mac(encode, mac, maclen);
|
||||
encoded = print_mac(mac, maclen);
|
||||
else
|
||||
{
|
||||
encoder(mac, encode);
|
||||
@@ -303,7 +306,7 @@ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned ch
|
||||
replace = 2;
|
||||
|
||||
if (replace != 0 || maclen == 6)
|
||||
plen = add_pseudoheader(header, plen, limit, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
|
||||
plen = add_pseudoheader(header, plen, outlen, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encoded, strlen(encoded), 0, replace);
|
||||
|
||||
return plen;
|
||||
}
|
||||
@@ -312,7 +315,7 @@ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned ch
|
||||
/* OPT_ADD_MAC = MAC is added (if available)
|
||||
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
|
||||
OPT_STRIP_MAC = MAC is removed */
|
||||
static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
static size_t add_mac(struct dns_header *header, size_t plen, size_t outlen,
|
||||
union mysockaddr *l3, time_t now, int *cacheablep)
|
||||
{
|
||||
int maclen = 0, replace = 0;
|
||||
@@ -328,7 +331,7 @@ static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *lim
|
||||
replace = 2;
|
||||
|
||||
if (replace != 0 || maclen != 0)
|
||||
plen = add_pseudoheader(header, plen, limit, EDNS0_OPTION_MAC, mac, maclen, 0, replace);
|
||||
plen = add_pseudoheader(header, plen, outlen, EDNS0_OPTION_MAC, mac, maclen, 0, replace);
|
||||
|
||||
return plen;
|
||||
}
|
||||
@@ -409,7 +412,7 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source,
|
||||
/* OPT_CLIENT_SUBNET = client subnet is added
|
||||
OPT_CLIENT_SUBNET + OPT_STRIP_ECS = client subnet is replaced
|
||||
OPT_STRIP_ECS = client subnet is removed */
|
||||
static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
static size_t add_source_addr(struct dns_header *header, size_t plen, size_t outlen,
|
||||
union mysockaddr *source, int *cacheable)
|
||||
{
|
||||
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
|
||||
@@ -439,7 +442,7 @@ static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned c
|
||||
return plen;
|
||||
}
|
||||
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace);
|
||||
return add_pseudoheader(header, plen, outlen, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace);
|
||||
}
|
||||
|
||||
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
|
||||
@@ -469,6 +472,8 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
|
||||
{
|
||||
GETSHORT(code, p);
|
||||
GETSHORT(len, p);
|
||||
if (i + 4 + len > rdlen)
|
||||
break; /* malformed: option body extends beyond RDATA */
|
||||
if (code == EDNS0_OPTION_CLIENT_SUBNET)
|
||||
{
|
||||
if (peer)
|
||||
@@ -514,7 +519,7 @@ struct umbrella_opt {
|
||||
u8 fields[4 * UMBRELLA_TYPESZ + UMBRELLA_ORGSZ + IN6ADDRSZ + UMBRELLA_DEVICESZ + UMBRELLA_ASSETSZ];
|
||||
};
|
||||
|
||||
static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
|
||||
static size_t add_umbrella_opt(struct dns_header *header, size_t plen, size_t outlen, union mysockaddr *source, int *cacheable)
|
||||
{
|
||||
*cacheable = 0;
|
||||
|
||||
@@ -546,28 +551,28 @@ static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned
|
||||
PUTLONG(daemon->umbrella_asset, u);
|
||||
}
|
||||
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1);
|
||||
return add_pseudoheader(header, plen, outlen, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1);
|
||||
}
|
||||
|
||||
/* Set *check_subnet if we add a client subnet option, which needs to checked
|
||||
in the reply. Set *cacheable to zero if we add an option which the answer
|
||||
may depend on. */
|
||||
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
size_t add_edns0_config(struct dns_header *header, size_t plen, size_t outlen,
|
||||
union mysockaddr *source, time_t now, int *cacheable)
|
||||
{
|
||||
*cacheable = 1;
|
||||
|
||||
plen = add_mac(header, plen, limit, source, now, cacheable);
|
||||
plen = add_dns_client(header, plen, limit, source, now, cacheable);
|
||||
plen = add_mac(header, plen, outlen, source, now, cacheable);
|
||||
plen = add_dns_client(header, plen, outlen, source, now, cacheable);
|
||||
|
||||
if (daemon->dns_client_id)
|
||||
plen = add_pseudoheader(header, plen, limit, EDNS0_OPTION_NOMCPEID,
|
||||
plen = add_pseudoheader(header, plen, outlen, EDNS0_OPTION_NOMCPEID,
|
||||
(unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
|
||||
|
||||
if (option_bool(OPT_UMBRELLA))
|
||||
plen = add_umbrella_opt(header, plen, limit, source, cacheable);
|
||||
plen = add_umbrella_opt(header, plen, outlen, source, cacheable);
|
||||
|
||||
plen = add_source_addr(header, plen, limit, source, cacheable);
|
||||
plen = add_source_addr(header, plen, outlen, source, cacheable);
|
||||
|
||||
return plen;
|
||||
}
|
||||
|
||||
+60
-42
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -375,7 +375,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
plen = add_do_bit(header, plen, ((unsigned char *) header) + daemon->edns_pktsz);
|
||||
plen = add_do_bit(header, plen, daemon->edns_pktsz);
|
||||
|
||||
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
|
||||
this allows it to select auth servers when one is returning bad data. */
|
||||
@@ -514,7 +514,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
forwarded = 0;
|
||||
|
||||
/* Advertise the size of UDP reply we can accept. */
|
||||
plen = add_pseudoheader(header, plen, (unsigned char *)(header + daemon->edns_pktsz), 0, NULL, 0, 0, 0);
|
||||
plen = add_pseudoheader(header, plen, daemon->edns_pktsz, 0, NULL, 0, 0, 0);
|
||||
|
||||
/* check for send errors here (no route to host)
|
||||
if we fail to send to all nameservers, send back an error
|
||||
@@ -589,7 +589,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
reply:
|
||||
if (udpfd != -1)
|
||||
{
|
||||
if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, (char *)(header + replylimit), first, last, ede)))
|
||||
if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, replylimit, first, last, ede)))
|
||||
return;
|
||||
|
||||
if (fwd_flags & FREC_HAS_PHEADER)
|
||||
@@ -597,9 +597,9 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
u16 swap = htons((u16)ede);
|
||||
|
||||
if (ede != EDE_UNSET)
|
||||
plen = add_pseudoheader(header, plen, (unsigned char *)(header + replylimit), EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, 0, 0);
|
||||
plen = add_pseudoheader(header, plen, replylimit, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, 0, 0);
|
||||
else
|
||||
plen = add_pseudoheader(header, plen, (unsigned char *)(header + replylimit), 0, NULL, 0, 0, 0);
|
||||
plen = add_pseudoheader(header, plen, replylimit, 0, NULL, 0, 0, 0);
|
||||
}
|
||||
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
@@ -695,7 +695,7 @@ static struct ipsets *domain_find_sets(struct ipsets *setlist, const char *domai
|
||||
|
||||
static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind,
|
||||
int no_cache, int cache_secure, int bogusanswer, int ad_reqd, int do_bit, int added_pheader,
|
||||
union mysockaddr *query_source, unsigned char *limit, int ede)
|
||||
union mysockaddr *query_source, size_t outlen, int ede)
|
||||
{
|
||||
unsigned char *pheader, *sizep;
|
||||
struct ipsets *ipsets = NULL, *nftsets = NULL;
|
||||
@@ -724,7 +724,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
/* Get extended RCODE. */
|
||||
rcode |= sizep[2] << 4;
|
||||
|
||||
if (option_bool(OPT_CLIENT_SUBNET) && !check_source(header, plen, pheader, query_source))
|
||||
if (option_bool(OPT_CLIENT_SUBNET) && !check_source(header, n, pheader, query_source))
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
|
||||
return 0;
|
||||
@@ -879,7 +879,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
if (pheader && ede != EDE_UNSET)
|
||||
{
|
||||
u16 swap = htons((u16)ede);
|
||||
n = add_pseudoheader(header, n, limit, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1);
|
||||
n = add_pseudoheader(header, n, outlen, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1);
|
||||
}
|
||||
|
||||
if (RCODE(header) == NXDOMAIN)
|
||||
@@ -954,6 +954,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
||||
/* As soon as anything returns BOGUS, we stop and unwind, to do otherwise
|
||||
would invite infinite loops, since the answers to DNSKEY and DS queries
|
||||
will not be cached, so they'll be repeated. */
|
||||
ds_retry:
|
||||
if (forward->flags & FREC_DNSKEY_QUERY)
|
||||
status = dnssec_validate_by_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class, &orig->validate_counter);
|
||||
else if (forward->flags & FREC_DS_QUERY)
|
||||
@@ -1026,7 +1027,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
||||
allocation of a new one: third arg of get_new_frec() does that. */
|
||||
if ((serverind = dnssec_server(forward->sentto, daemon->keyname, STAT_ISEQUAL(status, STAT_NEED_DS), NULL, NULL)) != -1 &&
|
||||
(server = daemon->serverarray[serverind]) &&
|
||||
(nn = dnssec_generate_query(header, ((unsigned char *) header) + daemon->edns_pktsz,
|
||||
(nn = dnssec_generate_query(header, daemon->edns_pktsz,
|
||||
daemon->keyname, forward->class, get_id(),
|
||||
STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS)) &&
|
||||
(fd = allocate_rfd(&rfds, server)) != -1 &&
|
||||
@@ -1083,6 +1084,18 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
||||
STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If there's no server for the parent of a domain-specific server's domain,
|
||||
assume that said server's contents it legitimately unsigned, as if the parent
|
||||
contained a negative DS record. This is part of the same logic that's found
|
||||
in dnssec_validate_ds() when it gets a negative DS repsonse. */
|
||||
if (STAT_ISEQUAL(status, STAT_NEED_DS) && serverind == -1 && lookup_domain(daemon->keyname, F_DOMAINSRV, NULL, NULL) &&
|
||||
cache_neg_ds(daemon->keyname, F_FORWARD | F_DS | F_NEG | F_DNSSECOK, forward->class, now, DNSSEC_ASSUMED_DS_TTL) == STAT_OK)
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("no server for parent domain of %s, assuming unsigned domain"), daemon->keyname);
|
||||
blockdata_free(stash);
|
||||
goto ds_retry;
|
||||
}
|
||||
|
||||
/* error unwind */
|
||||
free_rfds(&rfds);
|
||||
@@ -1192,10 +1205,10 @@ void reply_query(int fd, time_t now)
|
||||
server = daemon->serverarray[serv];
|
||||
if (server->sfd && server->sfd->fd == fd)
|
||||
break;
|
||||
|
||||
if (serv == last)
|
||||
return;
|
||||
}
|
||||
|
||||
if (serv == last)
|
||||
return;
|
||||
}
|
||||
|
||||
/* spoof check: answer must come from known server, also
|
||||
@@ -1429,7 +1442,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
|
||||
if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer,
|
||||
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION,
|
||||
!(forward->flags & FREC_HAS_PHEADER), &forward->frec_src.source,
|
||||
((unsigned char *)header) + daemon->edns_pktsz, ede)))
|
||||
daemon->edns_pktsz, ede)))
|
||||
{
|
||||
struct frec_src *src, *prev;
|
||||
int do_trunc;
|
||||
@@ -1932,7 +1945,7 @@ void receive_query(struct listener *listen, time_t now)
|
||||
{
|
||||
int cacheable;
|
||||
|
||||
n = add_edns0_config(header, n, ((unsigned char *)header) + daemon->edns_pktsz, &source_addr, now, &cacheable);
|
||||
n = add_edns0_config(header, n, daemon->edns_pktsz, &source_addr, now, &cacheable);
|
||||
saved_question = blockdata_alloc((char *) header, (size_t)n);
|
||||
|
||||
if (!cacheable)
|
||||
@@ -1966,12 +1979,12 @@ void receive_query(struct listener *listen, time_t now)
|
||||
{
|
||||
u16 swap = htons(ede);
|
||||
|
||||
m = add_pseudoheader(header, m, ((unsigned char *) header) + daemon->edns_pktsz,
|
||||
EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||
m = add_pseudoheader(header, m, daemon->edns_pktsz, EDNS0_OPTION_EDE,
|
||||
(unsigned char *)&swap, 2, do_bit, 0);
|
||||
}
|
||||
else
|
||||
m = add_pseudoheader(header, m, ((unsigned char *) header) + daemon->edns_pktsz,
|
||||
0, NULL, 0, do_bit, 0);
|
||||
m = add_pseudoheader(header, m, daemon->edns_pktsz, 0,
|
||||
NULL, 0, do_bit, 0);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
@@ -2162,9 +2175,12 @@ static ssize_t tcp_talk(int first, int last, int start, struct dns_header *heade
|
||||
someone might be attempting to insert bogus values into the cache by
|
||||
sending replies containing questions and bogus answers.
|
||||
Try another server, or give up */
|
||||
p = (unsigned char *)(((struct dns_header *)recvbuff->iov_base)+1);
|
||||
if (extract_name(((struct dns_header *)recvbuff->iov_base), rsize, &p, daemon->namebuff, EXTR_NAME_COMPARE, 4) != 1)
|
||||
struct dns_header *header = (struct dns_header *)recvbuff->iov_base;
|
||||
p = (unsigned char *)(header+1);
|
||||
if (rsize < (unsigned int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) || ntohs(header->qdcount) != 1 ||
|
||||
extract_name(header, rsize, &p, daemon->namebuff, EXTR_NAME_COMPARE, 4) != 1)
|
||||
continue;
|
||||
|
||||
GETSHORT(rtype, p);
|
||||
GETSHORT(rclass, p);
|
||||
|
||||
@@ -2310,7 +2326,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
query_header = (struct dns_header *)daemon->packet;
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
m = dnssec_generate_query(query_header, ((unsigned char *)query_header) + daemon->edns_pktsz, keyname, class, 0,
|
||||
m = dnssec_generate_query(query_header, daemon->edns_pktsz, keyname, class, 0,
|
||||
STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS);
|
||||
|
||||
if ((start = dnssec_server(server, keyname, STAT_ISEQUAL(new_status, STAT_NEED_DS), &first, &last)) == -1 ||
|
||||
@@ -2442,25 +2458,34 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
break;
|
||||
|
||||
/* Now get the query into the normal UDP packet buffer.
|
||||
Ignore queries long than this. If we're answering locally,
|
||||
Ignore queries longer than this. If we're answering locally,
|
||||
copy the query into the output buffer, but for forwarding, tcp_talk()
|
||||
wants the query in a a different buffer from the reply.
|
||||
Note that we overwrote any saved UDP query - this onlt matters in debug mode. */
|
||||
wants the query in different buffer from the reply.
|
||||
Note that we overwrote any saved UDP query - this only matters in debug mode. */
|
||||
daemon->srv_save = NULL;
|
||||
if (!read_write(confd, (unsigned char *)&tcp_len, sizeof(tcp_len), RW_READ) ||
|
||||
!(size = ntohs(tcp_len)) || size > (size_t)daemon->packet_buff_sz ||
|
||||
!read_write(confd, (unsigned char *)daemon->packet, size, RW_READ))
|
||||
break;
|
||||
|
||||
if (size < (int)sizeof(struct dns_header))
|
||||
/* header == query */
|
||||
header = (struct dns_header *)daemon->packet;
|
||||
|
||||
if (size < (int)sizeof(struct dns_header) || (header->hb3 & HB3_QR))
|
||||
continue;
|
||||
|
||||
/* Make sure we have a buffer big enough for the largest answer. */
|
||||
expand_buf(bigbuff, 65536 + MAXDNAME + RRFIXEDSZ);
|
||||
out_header = bigbuff->iov_base;
|
||||
|
||||
/* header == query */
|
||||
header = (struct dns_header *)daemon->packet;
|
||||
/* Add edns0 pheader to query */
|
||||
size = add_edns0_config(header, size, daemon->packet_buff_sz, &peer_addr, now, &cacheable);
|
||||
|
||||
/* Clear buffer to avoid risk of information disclosure. */
|
||||
memset(bigbuff->iov_base, 0, bigbuff->iov_len);
|
||||
/* Copy query into output buffer for local answering */
|
||||
memcpy(out_header, header, size);
|
||||
|
||||
query_count++;
|
||||
|
||||
/* log_query gets called indirectly all over the place, so
|
||||
@@ -2493,13 +2518,6 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
do_bit = 1; /* do bit */
|
||||
}
|
||||
|
||||
size = add_edns0_config(header, size, ((unsigned char *) header) + daemon->edns_pktsz, &peer_addr, now, &cacheable);
|
||||
|
||||
/* Clear buffer to avoid risk of information disclosure. */
|
||||
memset(bigbuff->iov_base, 0, bigbuff->iov_len);
|
||||
/* Copy query into output buffer for local answering */
|
||||
memcpy(out_header, header, size);
|
||||
|
||||
log_query_mysockaddr((auth_dns ? F_NOERR | F_AUTH : 0) | F_QUERY | F_FORWARD, daemon->namebuff,
|
||||
&peer_addr, NULL, qtype);
|
||||
|
||||
@@ -2552,7 +2570,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
else if (!allowed)
|
||||
{
|
||||
ede = EDE_BLOCKED;
|
||||
m = answer_disallowed(header, size, (u32)mark, daemon->namebuff);
|
||||
m = answer_disallowed(out_header, size, (u32)mark, daemon->namebuff);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_AUTH
|
||||
@@ -2568,7 +2586,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
/* Do this by steam now we're not in the select() loop */
|
||||
check_log_writer(1);
|
||||
|
||||
if (m == 0 && ede == EDE_UNSET)
|
||||
if (!flags && m == 0 && ede == EDE_UNSET)
|
||||
{
|
||||
struct server *master;
|
||||
int start;
|
||||
@@ -2602,7 +2620,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
size = add_do_bit(header, size, ((unsigned char *) header) + daemon->edns_pktsz);
|
||||
size = add_do_bit(header, size, daemon->edns_pktsz);
|
||||
|
||||
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
|
||||
this allows it to select auth servers when one is returning bad data. */
|
||||
@@ -2697,7 +2715,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
|
||||
m = process_reply(out_header, now, serv, (unsigned int)m,
|
||||
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
|
||||
ad_reqd, do_bit, !have_pseudoheader, &peer_addr, ((unsigned char *)out_header) + 65536, ede);
|
||||
ad_reqd, do_bit, !have_pseudoheader, &peer_addr, 65536, ede);
|
||||
|
||||
/* process_reply() adds pheader itself */
|
||||
have_pseudoheader = 0;
|
||||
@@ -2713,7 +2731,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
if (m == 0)
|
||||
{
|
||||
if (!(m = make_local_answer(flags, gotname, size, out_header, daemon->namebuff,
|
||||
((char *) out_header) + 65536, first, last, ede)))
|
||||
65536, first, last, ede)))
|
||||
break;
|
||||
}
|
||||
else if (ede == EDE_UNSET)
|
||||
@@ -2729,9 +2747,9 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
u16 swap = htons((u16)ede);
|
||||
|
||||
if (ede != EDE_UNSET)
|
||||
m = add_pseudoheader(out_header, m, ((unsigned char *) out_header) + 65536, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||
m = add_pseudoheader(out_header, m, 65536, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||
else
|
||||
m = add_pseudoheader(out_header, m, ((unsigned char *) out_header) + 65536, 0, NULL, 0, do_bit, 0);
|
||||
m = add_pseudoheader(out_header, m, 65536, 0, NULL, 0, do_bit, 0);
|
||||
}
|
||||
|
||||
check_log_writer(1);
|
||||
|
||||
+13
-17
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -253,7 +253,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
}
|
||||
|
||||
/* supplied data may just exceed normal buffer (unlikely) */
|
||||
if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
|
||||
if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAMESTR &&
|
||||
!(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
|
||||
continue;
|
||||
|
||||
@@ -261,8 +261,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
data.hostname_len + data.ed_len + data.clid_len, RW_READ))
|
||||
continue;
|
||||
|
||||
/* CLID into packet */
|
||||
for (p = daemon->packet, i = 0; i < data.clid_len; i++)
|
||||
/* CLID into packet: limit to 100 bytes to avoid overflowing buffer. */
|
||||
for (p = daemon->packet, i = 0; i < data.clid_len && i < 100; i++)
|
||||
{
|
||||
p += sprintf(p, "%.2x", buf[i]);
|
||||
if (i != data.clid_len - 1)
|
||||
@@ -533,18 +533,12 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
close(pipeout[0]);
|
||||
else
|
||||
{
|
||||
while (fgets(daemon->packet, daemon->packet_buff_sz, fp))
|
||||
{
|
||||
/* do not include new lines, log will append them */
|
||||
size_t len = strlen(daemon->packet);
|
||||
if (len > 0)
|
||||
{
|
||||
--len;
|
||||
if (daemon->packet[len] == '\n')
|
||||
daemon->packet[len] = 0;
|
||||
}
|
||||
send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet);
|
||||
}
|
||||
char *line = NULL;
|
||||
size_t linesz = 0;
|
||||
|
||||
while (get_line_alloc(fp, &line, &linesz))
|
||||
send_event(event_fd, EVENT_SCRIPT_LOG, 0, line);
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
@@ -740,9 +734,11 @@ static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end,
|
||||
if (!buf || (buf == end))
|
||||
return NULL;
|
||||
|
||||
for (next = buf; *next != 0; next++)
|
||||
for (next = buf; ; next++)
|
||||
if (next == end)
|
||||
return NULL;
|
||||
else if (*next == 0)
|
||||
break;
|
||||
|
||||
if (next != buf)
|
||||
{
|
||||
|
||||
+26
-11
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -40,7 +40,7 @@ static char *inotify_buffer;
|
||||
points to, made absolute if relative.
|
||||
If path doesn't exist or is not a symlink, return NULL.
|
||||
Return value is malloc'ed */
|
||||
static char *my_readlink(char *path)
|
||||
static char *my_readlink(int errfd, char *path)
|
||||
{
|
||||
ssize_t rc, size = 64;
|
||||
char *buf;
|
||||
@@ -59,7 +59,10 @@ static char *my_readlink(char *path)
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
die(_("cannot access path %s: %s"), path, EC_MISC);
|
||||
{
|
||||
send_event(errfd, EVENT_LINK_ERR, errno, path);
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
else if (rc < size-1)
|
||||
{
|
||||
@@ -85,15 +88,18 @@ static char *my_readlink(char *path)
|
||||
}
|
||||
}
|
||||
|
||||
void inotify_dnsmasq_init()
|
||||
void inotify_dnsmasq_init(int errfd)
|
||||
{
|
||||
struct resolvc *res;
|
||||
inotify_buffer = safe_malloc(INOTIFY_SZ);
|
||||
daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||
|
||||
if (daemon->inotifyfd == -1)
|
||||
die(_("failed to create inotify: %s"), NULL, EC_MISC);
|
||||
|
||||
{
|
||||
send_event(errfd, EVENT_INOTFY_ERR, errno, NULL);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
if (daemon->port == 0 || option_bool(OPT_NO_RESOLV))
|
||||
return;
|
||||
|
||||
@@ -105,10 +111,13 @@ void inotify_dnsmasq_init()
|
||||
strcpy(path, res->name);
|
||||
|
||||
/* Follow symlinks until we reach a non-symlink, or a non-existent file. */
|
||||
while ((new_path = my_readlink(path)))
|
||||
while ((new_path = my_readlink(errfd, path)))
|
||||
{
|
||||
if (links-- == 0)
|
||||
die(_("too many symlinks following %s"), res->name, EC_MISC);
|
||||
{
|
||||
send_event(errfd, EVENT_TMSL_ERR, 0, res->name);
|
||||
_exit(0);
|
||||
}
|
||||
free(path);
|
||||
path = new_path;
|
||||
}
|
||||
@@ -124,12 +133,18 @@ void inotify_dnsmasq_init()
|
||||
*d = '/';
|
||||
|
||||
if (res->wd == -1 && errno == ENOENT)
|
||||
die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC);
|
||||
{
|
||||
send_event(errfd, EVENT_RESOLV_ERR, 0, res->name);
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (res->wd == -1)
|
||||
die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
|
||||
|
||||
{
|
||||
send_event(errfd, EVENT_IFILE_ERR, errno, res->name);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+12
-4
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -35,7 +35,7 @@ static int read_leases(time_t now, FILE *leasestream)
|
||||
|
||||
Check various buffers are big enough for the code below */
|
||||
|
||||
#if (DHCP_BUFF_SZ < 255) || (MAXDNAME < 64) || (PACKETSZ+MAXDNAME+RRFIXEDSZ < 764)
|
||||
#if (DHCP_BUFF_SZ < 255) || (MAXDNAMESTR < 64) || (PACKETSZ+MAXDNAME+RRFIXEDSZ < 764)
|
||||
# error Buffer size breakage in leasefile parsing.
|
||||
#endif
|
||||
|
||||
@@ -73,7 +73,9 @@ static int read_leases(time_t now, FILE *leasestream)
|
||||
if (lease)
|
||||
{
|
||||
opt_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
|
||||
|
||||
if (opt_len < 0)
|
||||
continue;
|
||||
|
||||
if (strcmp(daemon->dhcp_buff3, "vendorclass") == 0)
|
||||
lease_set_vendorclass(lease, (unsigned char *)daemon->packet, opt_len);
|
||||
else if (strcmp(daemon->dhcp_buff3, "agent-info") == 0)
|
||||
@@ -99,6 +101,8 @@ static int read_leases(time_t now, FILE *leasestream)
|
||||
|
||||
|
||||
hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type);
|
||||
if (hw_len < 0)
|
||||
hw_len = 0;
|
||||
/* For backwards compatibility, no explicit MAC address type means ether. */
|
||||
if (hw_type == 0 && hw_len != 0)
|
||||
hw_type = ARPHRD_ETHER;
|
||||
@@ -131,7 +135,11 @@ static int read_leases(time_t now, FILE *leasestream)
|
||||
die (_("too many stored leases"), NULL, EC_MISC);
|
||||
|
||||
if (strcmp(daemon->packet, "*") != 0)
|
||||
clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
|
||||
{
|
||||
clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
|
||||
if (clid_len < 0)
|
||||
clid_len = 0;
|
||||
}
|
||||
|
||||
lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet,
|
||||
hw_len, hw_type, clid_len, now, 0);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -113,7 +113,7 @@ int log_start(struct passwd *ent_pw, int errfd)
|
||||
struct stat ls;
|
||||
if (getgid() == 0 && fstat(log_fd, &ls) == 0 && ls.st_gid == 0 &&
|
||||
(ls.st_mode & S_IWGRP) == 0)
|
||||
(void)fchmod(log_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
|
||||
(void)fchmod(log_fd, ls.st_mode | S_IWGRP);
|
||||
if (fchown(log_fd, ent_pw->pw_uid, -1) != 0)
|
||||
ret = errno;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+6
-6
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -1699,19 +1699,19 @@ void check_servers(int no_loop_check)
|
||||
int reload_servers(char *fname)
|
||||
{
|
||||
FILE *f;
|
||||
char *line;
|
||||
char *line = NULL;
|
||||
size_t linesz = 0;
|
||||
int gotone = 0;
|
||||
|
||||
/* buff happens to be MAXDNAME long... */
|
||||
if (!(f = fopen(fname, "r")))
|
||||
{
|
||||
my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
mark_servers(SERV_FROM_RESOLV);
|
||||
|
||||
while ((line = fgets(daemon->namebuff, MAXDNAME, f)))
|
||||
|
||||
while (get_line_alloc(f, &line, &linesz))
|
||||
{
|
||||
union mysockaddr addr, source_addr;
|
||||
char *token = strtok(line, " \t\n\r");
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+74
-68
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -4580,39 +4580,41 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
only allowed for agent-options. */
|
||||
|
||||
arg = comma;
|
||||
if ((comma = split(arg)))
|
||||
if (option == 'U' && strstr(arg, "enterprise:") == arg)
|
||||
{
|
||||
if (option != 'U' || strstr(arg, "enterprise:") != arg)
|
||||
comma = split(arg);
|
||||
new->enterprise = atoi(arg+11);
|
||||
arg = comma;
|
||||
}
|
||||
|
||||
if (arg)
|
||||
{
|
||||
for (dig = 0, colon = 0, p = (unsigned char *)arg; *p; p++)
|
||||
if (isxdigit(*p))
|
||||
dig = 1;
|
||||
else if (*p == ':')
|
||||
colon = 1;
|
||||
else
|
||||
break;
|
||||
|
||||
unhide_metas(arg);
|
||||
if (option == 'U' || option == 'j' || *p || !dig || !colon)
|
||||
{
|
||||
free(new->netid.net);
|
||||
ret_err_free(gen_err, new);
|
||||
new->len = strlen(arg);
|
||||
new->data = opt_malloc(new->len);
|
||||
memcpy(new->data, arg, new->len);
|
||||
}
|
||||
else
|
||||
new->enterprise = atoi(arg+11);
|
||||
{
|
||||
new->len = parse_hex(comma, (unsigned char *)arg, strlen(arg), NULL, NULL);
|
||||
new->data = opt_malloc(new->len);
|
||||
memcpy(new->data, arg, new->len);
|
||||
}
|
||||
}
|
||||
else
|
||||
comma = arg;
|
||||
|
||||
for (dig = 0, colon = 0, p = (unsigned char *)comma; *p; p++)
|
||||
if (isxdigit(*p))
|
||||
dig = 1;
|
||||
else if (*p == ':')
|
||||
colon = 1;
|
||||
else
|
||||
break;
|
||||
|
||||
unhide_metas(comma);
|
||||
if (option == 'U' || option == 'j' || *p || !dig || !colon)
|
||||
else if (option != 'U' || new->enterprise == 0)
|
||||
{
|
||||
new->len = strlen(comma);
|
||||
new->data = opt_malloc(new->len);
|
||||
memcpy(new->data, comma, new->len);
|
||||
}
|
||||
else
|
||||
{
|
||||
new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
|
||||
new->data = opt_malloc(new->len);
|
||||
memcpy(new->data, comma, new->len);
|
||||
free(new->netid.net);
|
||||
ret_err_free(gen_err, new);
|
||||
}
|
||||
|
||||
switch (option)
|
||||
@@ -4635,7 +4637,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
}
|
||||
new->next = daemon->dhcp_vendors;
|
||||
daemon->dhcp_vendors = new;
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4958,7 +4960,8 @@ err:
|
||||
arg = NULL; /* provoke error below */
|
||||
}
|
||||
|
||||
if (!domain || !arg || !(new->name = canonicalise_opt(domain)))
|
||||
if (!domain || !arg || !new->intr ||
|
||||
!(new->name = canonicalise_opt(domain)))
|
||||
ret_err(option == LOPT_DYNHOST ?
|
||||
_("bad dynamic host") : _("bad interface name"));
|
||||
|
||||
@@ -5468,9 +5471,20 @@ err:
|
||||
static void read_file(char *file, FILE *f, int hard_opt, int from_script)
|
||||
{
|
||||
volatile int lineno = 0;
|
||||
char *buff = daemon->namebuff;
|
||||
char *buff;
|
||||
size_t buffsz;
|
||||
|
||||
/* Memory allocation failure longjmps here if mem_recover == 1 */
|
||||
if (hard_opt != 0)
|
||||
{
|
||||
setjmp(mem_jmp);
|
||||
mem_recover = 1;
|
||||
}
|
||||
|
||||
while (fgets(buff, MAXDNAME, f))
|
||||
buff = NULL;
|
||||
buffsz = 0;
|
||||
|
||||
while (get_line_alloc(f, &buff, &buffsz))
|
||||
{
|
||||
int white, i;
|
||||
volatile int option;
|
||||
@@ -5478,15 +5492,7 @@ static void read_file(char *file, FILE *f, int hard_opt, int from_script)
|
||||
size_t len;
|
||||
|
||||
option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
|
||||
|
||||
/* Memory allocation failure longjmps here if mem_recover == 1 */
|
||||
if (option != 0 || hard_opt == LOPT_REV_SERV)
|
||||
{
|
||||
if (setjmp(mem_jmp))
|
||||
continue;
|
||||
mem_recover = 1;
|
||||
}
|
||||
|
||||
|
||||
arg = NULL;
|
||||
lineno++;
|
||||
errmess = NULL;
|
||||
@@ -5945,23 +5951,17 @@ void reread_dhcp(void)
|
||||
|
||||
void read_opts(int argc, char **argv, char *compile_opts)
|
||||
{
|
||||
size_t argbuf_size = MAXDNAME;
|
||||
char *argbuf = opt_malloc(argbuf_size);
|
||||
/* Note that both /000 and '.' are allowed within labels. These get
|
||||
represented in presentation format using NAME_ESCAPE as an escape
|
||||
character. In theory, if all the characters in a name were /000 or
|
||||
'.' or NAME_ESCAPE then all would have to be escaped, so the
|
||||
presentation format would be twice as long as the spec. */
|
||||
char *buff = opt_malloc((MAXDNAME * 2) + 1);
|
||||
size_t argbuf_size = 300;
|
||||
char *argbuf = safe_malloc(argbuf_size);
|
||||
int option, testmode = 0;
|
||||
char *arg, *conffile = NULL;
|
||||
|
||||
opterr = 0;
|
||||
|
||||
|
||||
daemon = opt_malloc(sizeof(struct daemon));
|
||||
memset(daemon, 0, sizeof(struct daemon));
|
||||
daemon->namebuff = buff;
|
||||
daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1);
|
||||
daemon->namebuff = safe_malloc(MAXDNAMESTR+1);
|
||||
daemon->workspacename = safe_malloc(MAXDNAMESTR+1);
|
||||
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
|
||||
|
||||
/* Set defaults - everything else is zero or NULL */
|
||||
@@ -6199,9 +6199,9 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
/* create default, if not specified */
|
||||
if (daemon->authserver && !daemon->hostmaster)
|
||||
{
|
||||
strcpy(buff, "hostmaster.");
|
||||
strcat(buff, daemon->authserver);
|
||||
daemon->hostmaster = opt_string_alloc(buff);
|
||||
strcpy(daemon->namebuff, "hostmaster.");
|
||||
strncat(daemon->namebuff, daemon->authserver, MAXDNAMESTR - strlen(daemon->namebuff));
|
||||
daemon->hostmaster = opt_string_alloc(daemon->namebuff);
|
||||
}
|
||||
|
||||
if (!daemon->dhcp_pxe_vendors)
|
||||
@@ -6216,11 +6216,11 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
{
|
||||
struct mx_srv_record *mx;
|
||||
|
||||
if (gethostname(buff, MAXDNAME) == -1)
|
||||
if (gethostname(daemon->namebuff, MAXDNAMESTR) == -1)
|
||||
die(_("cannot get host-name: %s"), NULL, EC_MISC);
|
||||
|
||||
for (mx = daemon->mxnames; mx; mx = mx->next)
|
||||
if (!mx->issrv && hostname_isequal(mx->name, buff))
|
||||
if (!mx->issrv && hostname_isequal(mx->name, daemon->namebuff))
|
||||
break;
|
||||
|
||||
if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
|
||||
@@ -6229,12 +6229,12 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
mx->next = daemon->mxnames;
|
||||
mx->issrv = 0;
|
||||
mx->target = NULL;
|
||||
mx->name = opt_string_alloc(buff);
|
||||
mx->name = opt_string_alloc(daemon->namebuff);
|
||||
daemon->mxnames = mx;
|
||||
}
|
||||
|
||||
if (!daemon->mxtarget)
|
||||
daemon->mxtarget = opt_string_alloc(buff);
|
||||
daemon->mxtarget = opt_string_alloc(daemon->namebuff);
|
||||
|
||||
for (mx = daemon->mxnames; mx; mx = mx->next)
|
||||
if (!mx->issrv && !mx->target)
|
||||
@@ -6249,7 +6249,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
|
||||
if (option_bool(OPT_RESOLV_DOMAIN))
|
||||
{
|
||||
char *line;
|
||||
char *line = NULL;
|
||||
size_t linesz = 0;
|
||||
FILE *f;
|
||||
|
||||
if (option_bool(OPT_NO_RESOLV) ||
|
||||
@@ -6260,7 +6261,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
if (!(f = fopen((daemon->resolv_files)->name, "r")))
|
||||
die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
|
||||
|
||||
while ((line = fgets(buff, MAXDNAME, f)))
|
||||
while (get_line_alloc(f, &line, &linesz))
|
||||
{
|
||||
char *token = strtok(line, " \t\n\r");
|
||||
|
||||
@@ -6269,7 +6270,11 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
|
||||
if ((token = strtok(NULL, " \t\n\r")) &&
|
||||
(daemon->domain_suffix = canonicalise_opt(token)))
|
||||
break;
|
||||
{
|
||||
/* We don't call get_line_alloc() until it returns false and frees the automatically */
|
||||
get_line_alloc(NULL, &line, &linesz);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
@@ -6280,7 +6285,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
|
||||
if (daemon->domain_suffix)
|
||||
{
|
||||
/* add domain for any srv record without one. */
|
||||
/* add domain for any srv record without one. */
|
||||
struct mx_srv_record *srv;
|
||||
|
||||
for (srv = daemon->mxnames; srv; srv = srv->next)
|
||||
@@ -6288,13 +6293,14 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
strchr(srv->name, '.') &&
|
||||
strchr(srv->name, '.') == strrchr(srv->name, '.'))
|
||||
{
|
||||
if (strlen(srv->name) + 1 + strlen(daemon->domain_suffix) > MAXDNAME)
|
||||
if (strlen(srv->name) + 1 + strlen(daemon->domain_suffix) > MAXDNAMESTR)
|
||||
die(_("srv-host name %s too long after domain appended"), srv->name, EC_MISC);
|
||||
strcpy(buff, srv->name);
|
||||
strcat(buff, ".");
|
||||
strcat(buff, daemon->domain_suffix);
|
||||
strcpy(daemon->namebuff, srv->name);
|
||||
strcat(daemon->namebuff, ".");
|
||||
strcat(daemon->namebuff, daemon->domain_suffix);
|
||||
free(srv->name);
|
||||
srv->name = opt_string_alloc(buff);
|
||||
if (!(srv->name = canonicalise_opt(daemon->namebuff)))
|
||||
die(_("bad srv-host name %s after domain appended"), srv->name, EC_MISC);
|
||||
}
|
||||
}
|
||||
else if (option_bool(OPT_DHCP_FQDN))
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+4
-7
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -215,11 +215,8 @@ void icmp6_packet(time_t now)
|
||||
if (opt_sz == 0 || opt_sz > rem)
|
||||
return; /* Bad packet */
|
||||
|
||||
if (p[0] == ICMP6_OPT_SOURCE_MAC && ((opt_sz - 2) * 3 - 1 < MAXDNAME))
|
||||
{
|
||||
print_mac(daemon->namebuff, &p[2], opt_sz - 2);
|
||||
mac = daemon->namebuff;
|
||||
}
|
||||
if (p[0] == ICMP6_OPT_SOURCE_MAC)
|
||||
mac = print_mac(&p[2], opt_sz - 2);
|
||||
}
|
||||
|
||||
if (!option_bool(OPT_QUIET_RA))
|
||||
@@ -435,7 +432,7 @@ static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_ad
|
||||
sprintf(daemon->namebuff, "/proc/sys/net/ipv6/conf/%s/mtu", mtu_name ? mtu_name : iface_name);
|
||||
if ((f = fopen(daemon->namebuff, "r")))
|
||||
{
|
||||
if (fgets(daemon->namebuff, MAXDNAME, f))
|
||||
if (fgets(daemon->namebuff, MAXDNAMESTR, f))
|
||||
mtu = atoi(daemon->namebuff);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
+27
-16
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -106,11 +106,16 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
|
||||
p = l + (unsigned char *)header;
|
||||
}
|
||||
else if (label_type == 0x00)
|
||||
{ /* label_type = 0 -> label. */
|
||||
namelen += l + 1; /* include period */
|
||||
else if (label_type == 0x00) /* label_type = 0 -> label. */
|
||||
{
|
||||
/* reject wire-format names > MAXDNAME bytes
|
||||
This also ensures that internal-format
|
||||
names don't exceed MAXDNAMESTR characters. */
|
||||
namelen += l + 1;
|
||||
/* namelen == MAXDNAME means terminator will overflow. */
|
||||
if (namelen >= MAXDNAME)
|
||||
return 0;
|
||||
|
||||
if (!CHECK_LEN(header, p, plen, l))
|
||||
return 0;
|
||||
|
||||
@@ -119,13 +124,13 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
{
|
||||
unsigned char c = *p;
|
||||
|
||||
if (c == 0 || c == '.' || c == NAME_ESCAPE)
|
||||
if (IS_NAME_ESCAPE(c))
|
||||
{
|
||||
*cp++ = NAME_ESCAPE;
|
||||
*cp++ = c+1;
|
||||
}
|
||||
else
|
||||
*cp++ = c;
|
||||
*cp++ = c;
|
||||
}
|
||||
else if (flip)
|
||||
{
|
||||
@@ -317,7 +322,7 @@ unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t
|
||||
else if (label_type == 0x40)
|
||||
{
|
||||
/* Extended label type */
|
||||
unsigned int count;
|
||||
unsigned int count, llen;
|
||||
|
||||
if (!CHECK_LEN(header, ansp, plen, 2))
|
||||
return NULL;
|
||||
@@ -328,9 +333,12 @@ unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t
|
||||
count = *(ansp++); /* Bits in bitstring */
|
||||
|
||||
if (count == 0) /* count == 0 means 256 bits */
|
||||
ansp += 32;
|
||||
llen = 32;
|
||||
else
|
||||
ansp += ((count-1)>>3)+1;
|
||||
llen = ((count-1)>>3)+1;
|
||||
|
||||
if (!ADD_RDLEN(header, ansp, plen, llen))
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{ /* label type == 0 Bottom six bits is length */
|
||||
@@ -440,7 +448,7 @@ int private_net6(struct in6_addr *a, int ban_localhost)
|
||||
(IN6_IS_ADDR_LOOPBACK(a) && ban_localhost) || /* RFC 6303 4.3 */
|
||||
IN6_IS_ADDR_LINKLOCAL(a) || /* RFC 6303 4.5 */
|
||||
IN6_IS_ADDR_SITELOCAL(a) ||
|
||||
((unsigned char *)a)[0] == 0xfd || /* RFC 6303 4.4 */
|
||||
(((unsigned char *)a)[0] & 0xfe) == 0xfc || /* RFC 4196 3.1 */
|
||||
((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
|
||||
}
|
||||
|
||||
@@ -495,7 +503,7 @@ int do_doctor(struct dns_header *header, size_t qlen, char *namebuff)
|
||||
header->hb3 &= ~HB3_AA;
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* remove validated flag from this RR, since we changed it! */
|
||||
if (option_bool(OPT_DNSSEC_VALID) && i < ntohs(header->ancount))
|
||||
if (option_bool(OPT_DNSSEC_VALID) && i < daemon->rr_status_sz && i < ntohs(header->ancount))
|
||||
daemon->rr_status[i] = 0;
|
||||
#endif
|
||||
done = 1;
|
||||
@@ -537,7 +545,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
|
||||
for (i = 0; i < ntohs(header->nscount); i++)
|
||||
{
|
||||
if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
|
||||
if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 10))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -607,7 +615,9 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
addr.rrblock.datalen += 20;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[i + ntohs(header->ancount)] != 0)
|
||||
if (option_bool(OPT_DNSSEC_VALID) &&
|
||||
i + ntohs(header->ancount) < daemon->rr_status_sz &&
|
||||
daemon->rr_status[i + ntohs(header->ancount)] != 0)
|
||||
{
|
||||
secflag = F_DNSSECOK;
|
||||
|
||||
@@ -943,7 +953,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
/* Name, extract it then re-encode. */
|
||||
int len;
|
||||
|
||||
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
|
||||
/* rdlen may lie, and extract_name() advances p1 past where it says the record ends. */
|
||||
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0) || (p1 > endrr))
|
||||
{
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
return 2;
|
||||
@@ -1458,7 +1469,7 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
|
||||
else
|
||||
{
|
||||
char *name = va_arg(ap, char *);
|
||||
if (name && !(p = do_rfc1035_name(p, name, limit)))
|
||||
if (name && !(p = do_rfc1035_name(p, name, (unsigned char *)limit)))
|
||||
goto truncated;
|
||||
|
||||
if (nameoffset < 0)
|
||||
@@ -1522,7 +1533,7 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
|
||||
/* get domain-name answer arg and store it in RDATA field */
|
||||
if (offset)
|
||||
*offset = p - (unsigned char *)header;
|
||||
if (!(p = do_rfc1035_name(p, va_arg(ap, char *), limit)))
|
||||
if (!(p = do_rfc1035_name(p, va_arg(ap, char *), (unsigned char *)limit)))
|
||||
goto truncated;
|
||||
CHECK_LIMIT(1);
|
||||
*p++ = 0;
|
||||
|
||||
+16
-14
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -413,7 +413,7 @@ 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)
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAME);
|
||||
inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAMESTR);
|
||||
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->netmask, daemon->addrbuff, ADDRSTRLEN);
|
||||
@@ -597,7 +597,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
if (mess->file[0])
|
||||
{
|
||||
memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
|
||||
daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
|
||||
daemon->dhcp_buff2[sizeof(mess->file)] = 0; /* ensure zero term. */
|
||||
id.net = (char *)daemon->dhcp_buff2;
|
||||
id.next = netid;
|
||||
netid = &id;
|
||||
@@ -1313,7 +1313,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"),
|
||||
daemon->addrbuff, print_mac(daemon->namebuff, mac, len));
|
||||
daemon->addrbuff, print_mac(mac, len));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1537,7 +1537,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
{
|
||||
inet_ntop(AF_INET, <mp->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),
|
||||
print_mac(ltmp->hwaddr, ltmp->hwaddr_len),
|
||||
daemon->addrbuff);
|
||||
lease = ltmp;
|
||||
}
|
||||
@@ -1918,16 +1918,18 @@ 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)
|
||||
{
|
||||
char *mac = "";
|
||||
|
||||
if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
|
||||
return;
|
||||
|
||||
daemon->addrbuff[0] = daemon->namebuff[0] = 0;
|
||||
daemon->addrbuff[0] = 0;
|
||||
|
||||
if (addr)
|
||||
inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
if (ext_mac)
|
||||
print_mac(daemon->namebuff, ext_mac, mac_len);
|
||||
mac = print_mac(ext_mac, mac_len);
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s%s%s%s",
|
||||
@@ -1936,7 +1938,7 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
||||
interface,
|
||||
daemon->addrbuff,
|
||||
addr ? " " : "",
|
||||
daemon->namebuff,
|
||||
mac,
|
||||
ext_mac ? " " : "",
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
@@ -1946,16 +1948,16 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
||||
interface,
|
||||
daemon->addrbuff,
|
||||
addr ? " " : "",
|
||||
daemon->namebuff,
|
||||
mac,
|
||||
ext_mac ? " " : "",
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
|
||||
#ifdef HAVE_UBUS
|
||||
if (!strcmp(type, "DHCPACK"))
|
||||
ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
|
||||
ubus_event_bcast("dhcp.ack", mac, addr ? daemon->addrbuff : NULL, string, interface);
|
||||
else if (!strcmp(type, "DHCPRELEASE"))
|
||||
ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
|
||||
ubus_event_bcast("dhcp.release", mac, addr ? daemon->addrbuff : NULL, string, interface);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1963,7 +1965,7 @@ static void log_options(unsigned char *start, u32 xid)
|
||||
{
|
||||
while (*start != OPTION_END)
|
||||
{
|
||||
char *optname = option_string(AF_INET, start[0], option_ptr(start, 0), option_len(start), daemon->namebuff, MAXDNAME);
|
||||
char *optname = option_string(AF_INET, start[0], option_ptr(start, 0), option_len(start), daemon->namebuff, MAXDNAMESTR);
|
||||
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
|
||||
ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
|
||||
@@ -2642,7 +2644,7 @@ static void do_options(struct dhcp_context *context,
|
||||
for (i = 0; req_options[i] != OPTION_END; i++)
|
||||
{
|
||||
char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
|
||||
q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
|
||||
q += snprintf(q, MAXDNAMESTR - (q - daemon->namebuff),
|
||||
"%d%s%s%s",
|
||||
req_options[i],
|
||||
strlen(s) != 0 ? ":" : "",
|
||||
@@ -3248,7 +3250,7 @@ unsigned int relay_reply4(struct dhcp_packet *mess, size_t sz, char *arrival_int
|
||||
|
||||
/* delete agent info before return RFC 3046 para 2.1 */
|
||||
*opt = OPTION_END;
|
||||
memset(opt + 1, 0, option_len(opt) + 2);
|
||||
memset(opt + 1, 0, option_len(opt) + 1);
|
||||
}
|
||||
}
|
||||
else if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
|
||||
|
||||
+142
-118
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -24,7 +24,7 @@ struct state {
|
||||
int clid_len, ia_type, interface, hostname_auth, lease_allocate;
|
||||
char *client_hostname, *hostname, *domain, *send_domain;
|
||||
struct dhcp_context *context;
|
||||
struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr;
|
||||
struct in6_addr *relay_address, *fallback, *ll_addr, *ula_addr;
|
||||
unsigned int xid, fqdn_flags, iaid;
|
||||
char *iface_name;
|
||||
void *packet_options, *end;
|
||||
@@ -34,13 +34,14 @@ struct state {
|
||||
};
|
||||
|
||||
static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t sz,
|
||||
struct in6_addr *client_addr, time_t now);
|
||||
struct in6_addr *client_addr, struct in6_addr *link_address, time_t now);
|
||||
static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbuff, size_t sz, time_t now);
|
||||
static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts);
|
||||
static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string);
|
||||
static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string);
|
||||
static void *opt6_find (uint8_t *opts, uint8_t *end, unsigned int search, unsigned int minsize);
|
||||
static void *opt6_next(uint8_t *opts, uint8_t *end);
|
||||
static void *opt6_find (uint8_t *opts, uint8_t *end, unsigned int search, int minsize);
|
||||
static void *opt6_first(uint8_t *opt, uint8_t *end);
|
||||
static void *opt6_user_vendor_first(uint8_t *opt, uint8_t *end);
|
||||
static unsigned int opt6_uint(unsigned char *opt, int offset, int size);
|
||||
static void get_context_tag(struct state *state, struct dhcp_context *context);
|
||||
static int check_ia(struct state *state, void *opt, void **endp, void **ia_option);
|
||||
@@ -62,11 +63,12 @@ static void calculate_times(struct dhcp_context *context, unsigned int *min_time
|
||||
#define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2)))
|
||||
#define opt6_type(opt) (opt6_uint(opt, -4, 2))
|
||||
#define opt6_ptr(opt, i) ((void *)&(((uint8_t *)(opt))[4+(i)]))
|
||||
#define opt6_next(opt, end) (opt6_first(opt6_ptr((opt), opt6_len((opt))), (end)))
|
||||
|
||||
#define opt6_user_vendor_ptr(opt, i) ((void *)&(((uint8_t *)(opt))[2+(i)]))
|
||||
#define opt6_user_vendor_len(opt) ((int)(opt6_uint(opt, -4, 2)))
|
||||
#define opt6_user_vendor_next(opt, end) (opt6_next(((uint8_t *) opt) - 2, end))
|
||||
|
||||
#define opt6_user_vendor_len(opt) ((int)(opt6_uint((opt), -4, 2)))
|
||||
#define opt6_user_vendor_next(opt, end) (opt6_user_vendor_first(opt6_user_vendor_ptr((opt), opt6_user_vendor_len((opt))), (end)))
|
||||
|
||||
|
||||
unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int interface, char *iface_name,
|
||||
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
|
||||
@@ -98,9 +100,8 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int
|
||||
state.ula_addr = ula_addr;
|
||||
state.mac_len = 0;
|
||||
state.tags = NULL;
|
||||
state.link_address = NULL;
|
||||
|
||||
if (dhcp6_maybe_relay(&state, daemon->dhcp_packet.iov_base, sz, client_addr, now))
|
||||
|
||||
if (dhcp6_maybe_relay(&state, daemon->dhcp_packet.iov_base, sz, client_addr, NULL, now))
|
||||
return msg_type == DHCP6RELAYFORW ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;
|
||||
|
||||
return 0;
|
||||
@@ -108,15 +109,22 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int
|
||||
|
||||
/* This cost me blood to write, it will probably cost you blood to understand - srk. */
|
||||
static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t sz,
|
||||
struct in6_addr *client_addr, time_t now)
|
||||
struct in6_addr *client_addr, struct in6_addr *link_address, time_t now)
|
||||
{
|
||||
uint8_t *end = inbuff + sz;
|
||||
uint8_t *opts = inbuff + 34;
|
||||
int msg_type = *inbuff;
|
||||
int msg_type;
|
||||
unsigned char *outmsgtypep;
|
||||
uint8_t *opt;
|
||||
struct dhcp_vendor *vendor;
|
||||
|
||||
/* must have at least msg_type+trans_id
|
||||
which is 1 + 3 = 4 */
|
||||
if (sz < 4)
|
||||
return 0;
|
||||
|
||||
msg_type = *inbuff;
|
||||
|
||||
/* if not an encapsulated relayed message, just do the stuff */
|
||||
if (msg_type != DHCP6RELAYFORW)
|
||||
{
|
||||
@@ -128,7 +136,7 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
|
||||
link_address == NULL means there's no relay in use, so we try and find the client's
|
||||
MAC address from the local ND cache. */
|
||||
|
||||
if (!state->link_address)
|
||||
if (!link_address)
|
||||
get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type, now);
|
||||
else
|
||||
{
|
||||
@@ -136,9 +144,9 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
|
||||
struct shared_network *share = NULL;
|
||||
state->context = NULL;
|
||||
|
||||
if (!IN6_IS_ADDR_LOOPBACK(state->link_address) &&
|
||||
!IN6_IS_ADDR_LINKLOCAL(state->link_address) &&
|
||||
!IN6_IS_ADDR_MULTICAST(state->link_address))
|
||||
if (!IN6_IS_ADDR_LOOPBACK(link_address) &&
|
||||
!IN6_IS_ADDR_LINKLOCAL(link_address) &&
|
||||
!IN6_IS_ADDR_MULTICAST(link_address))
|
||||
for (c = daemon->dhcp6; c; c = c->next)
|
||||
{
|
||||
for (share = daemon->shared_networks; share; share = share->next)
|
||||
@@ -147,7 +155,7 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
|
||||
continue;
|
||||
|
||||
if (share->if_index != 0 ||
|
||||
!IN6_ARE_ADDR_EQUAL(state->link_address, &share->match_addr6))
|
||||
!IN6_ARE_ADDR_EQUAL(link_address, &share->match_addr6))
|
||||
continue;
|
||||
|
||||
if ((c->flags & CONTEXT_DHCP) &&
|
||||
@@ -160,8 +168,8 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
|
||||
if (share ||
|
||||
((c->flags & CONTEXT_DHCP) &&
|
||||
!(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
|
||||
is_same_net6(state->link_address, &c->start6, c->prefix) &&
|
||||
is_same_net6(state->link_address, &c->end6, c->prefix)))
|
||||
is_same_net6(link_address, &c->start6, c->prefix) &&
|
||||
is_same_net6(link_address, &c->end6, c->prefix)))
|
||||
{
|
||||
c->preferred = c->valid = 0xffffffff;
|
||||
c->current = state->context;
|
||||
@@ -171,7 +179,7 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
|
||||
|
||||
if (!state->context)
|
||||
{
|
||||
inet_ntop(AF_INET6, state->link_address, daemon->addrbuff, ADDRSTRLEN);
|
||||
inet_ntop(AF_INET6, link_address, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_WARNING,
|
||||
_("no address range available for DHCPv6 request from relay at %s"),
|
||||
daemon->addrbuff);
|
||||
@@ -186,6 +194,8 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->relay_address = link_address;
|
||||
|
||||
return dhcp6_no_relay(state, msg_type, inbuff, sz, now);
|
||||
}
|
||||
|
||||
@@ -233,11 +243,8 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
|
||||
memcpy(&state->mac[0], opt6_ptr(opt, 2), state->mac_len);
|
||||
}
|
||||
|
||||
for (opt = opts; opt; opt = opt6_next(opt, end))
|
||||
for (opt = opt6_first(opts, end); opt; opt = opt6_next(opt, end))
|
||||
{
|
||||
if ((uint8_t *)opt6_ptr(opt, 0) + opt6_len(opt) > end)
|
||||
return 0;
|
||||
|
||||
/* Don't copy MAC address into reply. */
|
||||
if (opt6_type(opt) != OPTION6_CLIENT_MAC)
|
||||
{
|
||||
@@ -248,11 +255,12 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
|
||||
/* the packet data is unaligned, copy to aligned storage */
|
||||
memcpy(&align, inbuff + 2, IN6ADDRSZ);
|
||||
|
||||
|
||||
/* RFC6221 para 4 */
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED(&align))
|
||||
state->link_address = &align;
|
||||
if (!dhcp6_maybe_relay(state, opt6_ptr(opt, 0), opt6_len(opt), client_addr, now))
|
||||
/* RFC6221 para 4 says if link_address in encapulation
|
||||
is zero, ignore it, and, by implication, use the link
|
||||
address of any enclosing encapsulation or, failing that
|
||||
of the arrival interface on the the server. */
|
||||
if (!dhcp6_maybe_relay(state, opt6_ptr(opt, 0), opt6_len(opt), client_addr,
|
||||
IN6_IS_ADDR_UNSPECIFIED(&align) ? link_address : &align, now))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
@@ -290,7 +298,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
state->hostname = NULL;
|
||||
state->client_hostname = NULL;
|
||||
state->fqdn_flags = 0x01; /* default to send if we receive no FQDN option */
|
||||
|
||||
|
||||
/* set tag with name == interface */
|
||||
iface_id.net = state->iface_name;
|
||||
iface_id.next = state->tags;
|
||||
@@ -387,14 +395,28 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
if (opt6_len(opt) < 4)
|
||||
continue;
|
||||
|
||||
if (vendor->enterprise != opt6_uint(opt, 0, 4))
|
||||
if (vendor->enterprise != 0 && vendor->enterprise != opt6_uint(opt, 0, 4))
|
||||
continue;
|
||||
|
||||
|
||||
/* matching enterprise, no string match. */
|
||||
if (vendor->enterprise != 0 && vendor->len == 0)
|
||||
{
|
||||
vendor->netid.next = state->tags;
|
||||
state->tags = &vendor->netid;
|
||||
break;
|
||||
}
|
||||
|
||||
offset = 4;
|
||||
|
||||
/* If we're going to search the strings below, there must be at least one empty string to search
|
||||
I think a vendor_class option with just the enterprise number is valid. */
|
||||
if (opt6_len(opt) < 6)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Note that format if user/vendor classes is different to DHCP options - no option types. */
|
||||
for (enc_opt = opt6_ptr(opt, offset); enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end))
|
||||
|
||||
/* Note that format if user/vendor classes is different to DHCP options - no option types. */
|
||||
for (enc_opt = opt6_user_vendor_first(opt6_ptr(opt, offset), enc_end);
|
||||
enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end))
|
||||
for (i = 0; i <= (opt6_user_vendor_len(enc_opt) - vendor->len); i++)
|
||||
if (memcmp(vendor->data, opt6_user_vendor_ptr(enc_opt, i), vendor->len) == 0)
|
||||
{
|
||||
@@ -452,10 +474,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
if (state->mac_len != 0)
|
||||
{
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
print_mac(daemon->dhcp_buff, state->mac, state->mac_len);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u client MAC address: %s"), state->xid, daemon->dhcp_buff);
|
||||
}
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u client MAC address: %s"), state->xid,
|
||||
print_mac(state->mac, state->mac_len));
|
||||
|
||||
for (mac_opt = daemon->dhcp_macs; mac_opt; mac_opt = mac_opt->next)
|
||||
if ((unsigned)mac_opt->hwaddr_len == state->mac_len &&
|
||||
@@ -657,7 +677,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
for (c = state->context; c; c = c->current)
|
||||
c->flags &= ~CONTEXT_CONF_USED;
|
||||
|
||||
for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
|
||||
for (opt = opt6_first(state->packet_options, state->end); opt; opt = opt6_next(opt, state->end))
|
||||
{
|
||||
void *ia_option, *ia_end;
|
||||
unsigned int min_time = 0xffffffff;
|
||||
@@ -821,7 +841,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
if (ignore)
|
||||
return 0;
|
||||
|
||||
for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
|
||||
for (opt = opt6_first(state->packet_options, state->end); opt; opt = opt6_next(opt, state->end))
|
||||
{
|
||||
void *ia_option, *ia_end;
|
||||
unsigned int min_time = 0xffffffff;
|
||||
@@ -932,7 +952,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
|
||||
log6_quiet(state, msg_type == DHCP6RENEW ? "DHCPRENEW" : "DHCPREBIND", NULL, NULL);
|
||||
|
||||
for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
|
||||
for (opt = opt6_first(state->packet_options, state->end); opt; opt = opt6_next(opt, state->end))
|
||||
{
|
||||
void *ia_option, *ia_end;
|
||||
unsigned int min_time = 0xffffffff;
|
||||
@@ -1067,7 +1087,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
|
||||
log6_quiet(state, "DHCPCONFIRM", NULL, NULL);
|
||||
|
||||
for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
|
||||
for (opt = opt6_first(state->packet_options, state->end); opt; opt = opt6_next(opt, state->end))
|
||||
{
|
||||
void *ia_option, *ia_end;
|
||||
|
||||
@@ -1143,7 +1163,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
|
||||
log6_quiet(state, "DHCPRELEASE", NULL, NULL);
|
||||
|
||||
for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
|
||||
for (opt = opt6_first(state->packet_options, state->end); opt; opt = opt6_next(opt, state->end))
|
||||
{
|
||||
void *ia_option, *ia_end;
|
||||
int made_ia = 0;
|
||||
@@ -1208,7 +1228,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
|
||||
log6_quiet(state, "DHCPDECLINE", NULL, NULL);
|
||||
|
||||
for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
|
||||
for (opt = opt6_first(state->packet_options, state->end); opt; opt = opt6_next(opt, state->end))
|
||||
{
|
||||
void *ia_option, *ia_end;
|
||||
int made_ia = 0;
|
||||
@@ -1514,7 +1534,7 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
|
||||
for (i = 0; i < opt6_len(oro) - 1; i += 2)
|
||||
{
|
||||
char *s = option_string(AF_INET6, opt6_uint(oro, i, 2), NULL, 0, NULL, 0);
|
||||
q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
|
||||
q += snprintf(q, MAXDNAMESTR - (q - daemon->namebuff),
|
||||
"%d%s%s%s",
|
||||
opt6_uint(oro, i, 2),
|
||||
strlen(s) != 0 ? ":" : "",
|
||||
@@ -1580,10 +1600,7 @@ static int check_ia(struct state *state, void *opt, void **endp, void **ia_optio
|
||||
{
|
||||
*ia_option = NULL;
|
||||
|
||||
/* must be a minimal option to check without stepping outside received packet. */
|
||||
if (opt6_ptr(opt, 4) > state->end)
|
||||
return 0;
|
||||
|
||||
/* callee ensures packet is long enough for opt6_len(opt) to be valid and believe-able. */
|
||||
state->ia_type = opt6_type(opt);
|
||||
|
||||
if (state->ia_type != OPTION6_IA_NA && state->ia_type != OPTION6_IA_TA)
|
||||
@@ -1595,10 +1612,7 @@ static int check_ia(struct state *state, void *opt, void **endp, void **ia_optio
|
||||
if (state->ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
|
||||
return 0;
|
||||
|
||||
/* Check we don't overflow the received packet. */
|
||||
if ((*endp = opt6_ptr(opt, opt6_len(opt))) > state->end)
|
||||
return 0;
|
||||
|
||||
*endp = opt6_ptr(opt, opt6_len(opt));
|
||||
state->iaid = opt6_uint(opt, 0, 4);
|
||||
*ia_option = opt6_find(opt6_ptr(opt, state->ia_type == OPTION6_IA_NA ? 12 : 4), *endp, OPTION6_IAADDR, 24);
|
||||
|
||||
@@ -1913,10 +1927,11 @@ static void update_leases(struct state *state, struct dhcp_context *context, str
|
||||
lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0);
|
||||
|
||||
if (opt6_len(opt) >= 6)
|
||||
for (enc_opt = opt6_ptr(opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
|
||||
for (enc_opt = opt6_user_vendor_first(opt6_ptr(opt, 4), enc_end);
|
||||
enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end))
|
||||
{
|
||||
lease->vendorclass_count++;
|
||||
lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
|
||||
lease_add_extradata(lease, opt6_user_vendor_ptr(enc_opt, 0), opt6_user_vendor_len(enc_opt), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1965,16 +1980,17 @@ static void update_leases(struct state *state, struct dhcp_context *context, str
|
||||
}
|
||||
}
|
||||
|
||||
if (state->link_address)
|
||||
inet_ntop(AF_INET6, state->link_address, daemon->addrbuff, ADDRSTRLEN);
|
||||
if (state->relay_address)
|
||||
inet_ntop(AF_INET6, state->relay_address, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
lease_add_extradata(lease, (unsigned char *)daemon->addrbuff, state->link_address ? strlen(daemon->addrbuff) : 0, 0);
|
||||
lease_add_extradata(lease, (unsigned char *)daemon->addrbuff, state->relay_address ? strlen(daemon->addrbuff) : 0, 0);
|
||||
|
||||
if ((opt = opt6_find(state->packet_options, state->end, OPTION6_USER_CLASS, 2)))
|
||||
{
|
||||
void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt));
|
||||
for (enc_opt = opt6_ptr(opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
|
||||
lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
|
||||
for (enc_opt = opt6_user_vendor_first(opt6_ptr(opt, 0), enc_end);
|
||||
enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end))
|
||||
lease_add_extradata(lease, opt6_user_vendor_ptr(enc_opt, 0), opt6_user_vendor_len(enc_opt), 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1989,10 +2005,10 @@ static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_op
|
||||
void *opt;
|
||||
char *desc = nest ? "nest" : "sent";
|
||||
|
||||
if (!option_bool(OPT_LOG_OPTS) || start_opts == end_opts)
|
||||
if (!option_bool(OPT_LOG_OPTS))
|
||||
return;
|
||||
|
||||
for (opt = start_opts; opt; opt = opt6_next(opt, end_opts))
|
||||
for (opt = opt6_first(start_opts, end_opts); opt; opt = opt6_next(opt, end_opts))
|
||||
{
|
||||
int type = opt6_type(opt);
|
||||
void *ia_options = NULL;
|
||||
@@ -2025,16 +2041,22 @@ static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_op
|
||||
}
|
||||
else if (type == OPTION6_STATUS_CODE)
|
||||
{
|
||||
int len = sprintf(daemon->namebuff, "%u ", opt6_uint(opt, 0, 2));
|
||||
memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), opt6_len(opt)-2);
|
||||
daemon->namebuff[len + opt6_len(opt) - 2] = 0;
|
||||
if (opt6_len(opt) >= 2)
|
||||
{
|
||||
int slen = opt6_len(opt) - 2;
|
||||
int len = sprintf(daemon->namebuff, "%u ", opt6_uint(opt, 0, 2));
|
||||
if (slen > (MAXDNAMESTR) - len - 1)
|
||||
slen = (MAXDNAMESTR) - len - 1;
|
||||
memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), slen);
|
||||
daemon->namebuff[len + slen] = 0;
|
||||
}
|
||||
optname = "status";
|
||||
}
|
||||
else
|
||||
{
|
||||
/* account for flag byte on FQDN */
|
||||
int offset = type == OPTION6_FQDN ? 1 : 0;
|
||||
optname = option_string(AF_INET6, type, opt6_ptr(opt, offset), opt6_len(opt) - offset, daemon->namebuff, MAXDNAME);
|
||||
optname = option_string(AF_INET6, type, opt6_ptr(opt, offset), opt6_len(opt) - offset, daemon->namebuff, MAXDNAMESTR);
|
||||
}
|
||||
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%u %s size:%3d option:%3d %s %s",
|
||||
@@ -2053,14 +2075,6 @@ static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, c
|
||||
|
||||
static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string)
|
||||
{
|
||||
int clid_len = state->clid_len;
|
||||
|
||||
/* avoid buffer overflow */
|
||||
if (clid_len > 100)
|
||||
clid_len = 100;
|
||||
|
||||
print_mac(daemon->namebuff, state->clid, clid_len);
|
||||
|
||||
if (addr)
|
||||
{
|
||||
inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, DHCP_BUFF_SZ - 1);
|
||||
@@ -2075,58 +2089,56 @@ static void log6_packet(struct state *state, char *type, struct in6_addr *addr,
|
||||
type,
|
||||
state->iface_name,
|
||||
daemon->dhcp_buff2,
|
||||
daemon->namebuff,
|
||||
print_mac(state->clid, state->clid_len),
|
||||
string ? string : "");
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s %s",
|
||||
type,
|
||||
state->iface_name,
|
||||
daemon->dhcp_buff2,
|
||||
daemon->namebuff,
|
||||
print_mac(state->clid, state->clid_len),
|
||||
string ? string : "");
|
||||
}
|
||||
|
||||
static void *opt6_find (uint8_t *opts, uint8_t *end, unsigned int search, unsigned int minsize)
|
||||
static void *opt6_find(uint8_t *opts, uint8_t *end, unsigned int search, int minsize)
|
||||
{
|
||||
u16 opt, opt_len;
|
||||
void *start;
|
||||
|
||||
if (!opts)
|
||||
return NULL;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (end - opts < 4)
|
||||
return NULL;
|
||||
|
||||
start = opts;
|
||||
GETSHORT(opt, opts);
|
||||
GETSHORT(opt_len, opts);
|
||||
|
||||
if (opt_len > (end - opts))
|
||||
return NULL;
|
||||
|
||||
if (opt == search && (opt_len >= minsize))
|
||||
return start;
|
||||
|
||||
opts += opt_len;
|
||||
}
|
||||
for (opts = opt6_first(opts, end); opts; opts = opt6_next(opts, end))
|
||||
if (opt6_type(opts) == search && opt6_len(opts) >= minsize)
|
||||
return opts;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *opt6_next(uint8_t *opts, uint8_t *end)
|
||||
static void *opt6_first(uint8_t *opt, uint8_t *end)
|
||||
{
|
||||
u16 opt_len;
|
||||
|
||||
if (end - opts < 4)
|
||||
if (!opt)
|
||||
return NULL;
|
||||
|
||||
opts += 2;
|
||||
GETSHORT(opt_len, opts);
|
||||
|
||||
if (opt_len >= (end - opts))
|
||||
|
||||
/* make sure we have option number and length. */
|
||||
if ((uint8_t *)opt6_ptr(opt, 0) > end)
|
||||
return NULL;
|
||||
|
||||
return opts + opt_len;
|
||||
|
||||
/* make sure we have bytes promised by length. */
|
||||
if ((uint8_t *)opt6_ptr(opt, opt6_len(opt)) > end)
|
||||
return NULL;
|
||||
|
||||
return opt;
|
||||
}
|
||||
|
||||
static void *opt6_user_vendor_first(uint8_t *opt, uint8_t *end)
|
||||
{
|
||||
if (!opt)
|
||||
return NULL;
|
||||
|
||||
/* make sure we have length. */
|
||||
if ((uint8_t *)opt6_user_vendor_ptr(opt, 0) > end)
|
||||
return NULL;
|
||||
|
||||
/* make sure we have bytes promised by length. */
|
||||
if ((uint8_t *)opt6_user_vendor_ptr(opt, opt6_user_vendor_len(opt)) > end)
|
||||
return NULL;
|
||||
|
||||
return opt;
|
||||
}
|
||||
|
||||
static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
|
||||
@@ -2142,12 +2154,12 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* return 0 if we're not configured to relay. */
|
||||
int relay_upstream6(int iface_index, ssize_t sz,
|
||||
struct in6_addr *peer_address, u32 scope_id, time_t now)
|
||||
{
|
||||
unsigned char *header;
|
||||
unsigned char *inbuff = daemon->dhcp_packet.iov_base;
|
||||
int msg_type = *inbuff;
|
||||
int hopcount, o;
|
||||
struct in6_addr multicast;
|
||||
unsigned int maclen, mactype;
|
||||
@@ -2164,10 +2176,22 @@ int relay_upstream6(int iface_index, ssize_t sz,
|
||||
|
||||
inet_pton(AF_INET6, ALL_SERVERS, &multicast);
|
||||
get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now);
|
||||
|
||||
/* We need at least four bytes for valid client/server message
|
||||
(type, and transaction ID) A relay-originated message needs more. */
|
||||
if (sz < 4)
|
||||
return 1;
|
||||
|
||||
/* Get hop count from nested relayed message */
|
||||
if (msg_type == DHCP6RELAYFORW)
|
||||
hopcount = *((unsigned char *)inbuff+1) + 1;
|
||||
if (inbuff[0] == DHCP6RELAYFORW)
|
||||
{
|
||||
/* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
|
||||
which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */
|
||||
if (sz < 38)
|
||||
return 1;
|
||||
|
||||
/* Get hop count from nested relayed message. */
|
||||
hopcount = inbuff[1] + 1;
|
||||
}
|
||||
else
|
||||
hopcount = 0;
|
||||
|
||||
@@ -2233,7 +2257,7 @@ int relay_upstream6(int iface_index, ssize_t sz,
|
||||
{
|
||||
inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN);
|
||||
if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast))
|
||||
snprintf(daemon->namebuff, MAXDNAME, _("multicast via %s"), relay->interface);
|
||||
snprintf(daemon->namebuff, MAXDNAMESTR, _("multicast via %s"), relay->interface);
|
||||
else
|
||||
inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->namebuff);
|
||||
|
||||
+12
-17
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -344,14 +344,6 @@ short *rrfilter_desc(int type)
|
||||
thus generating names in canonical form.
|
||||
Calling to_wire followed by from_wire is almost an identity,
|
||||
except that the UC remains mapped to LC.
|
||||
|
||||
Note that both /000 and '.' are allowed within labels. These get
|
||||
represented in presentation format using NAME_ESCAPE as an escape
|
||||
character. In theory, if all the characters in a name were /000 or
|
||||
'.' or NAME_ESCAPE then all would have to be escaped, so the
|
||||
presentation format would be twice as long as the spec (1024).
|
||||
The buffers are all declared as 2049 (allowing for the trailing zero)
|
||||
for this reason.
|
||||
*/
|
||||
int to_wire(char *name)
|
||||
{
|
||||
@@ -397,14 +389,17 @@ void from_wire(char *name)
|
||||
len = *l;
|
||||
memmove(l, l+1, len);
|
||||
for (p = l; p < l + len; p++)
|
||||
if (*p == '.' || *p == 0 || *p == NAME_ESCAPE)
|
||||
{
|
||||
memmove(p+1, p, 1 + last - p);
|
||||
len++;
|
||||
*p++ = NAME_ESCAPE;
|
||||
(*p)++;
|
||||
}
|
||||
|
||||
{
|
||||
unsigned char c = *p;
|
||||
if (IS_NAME_ESCAPE(c))
|
||||
{
|
||||
memmove(p+1, p, 1 + last - p);
|
||||
last++;
|
||||
len++;
|
||||
*p++ = NAME_ESCAPE;
|
||||
(*p)++;
|
||||
}
|
||||
}
|
||||
l[len] = '.';
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
|
||||
+15
-10
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -434,17 +434,17 @@ static void tftp_request(char *packet, ssize_t plen, struct listener *listen, ti
|
||||
{
|
||||
if (prefix[0] == '/')
|
||||
daemon->namebuff[0] = 0;
|
||||
strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
strncat(daemon->namebuff, prefix, MAXDNAMESTR - strlen(daemon->namebuff));
|
||||
if (prefix[strlen(prefix)-1] != '/')
|
||||
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
strncat(daemon->namebuff, "/", MAXDNAMESTR - strlen(daemon->namebuff));
|
||||
|
||||
if (option_bool(OPT_TFTP_APREF_IP))
|
||||
{
|
||||
size_t oldlen = strlen(daemon->namebuff);
|
||||
struct stat statbuf;
|
||||
|
||||
strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
strncat(daemon->namebuff, daemon->addrbuff, MAXDNAMESTR - strlen(daemon->namebuff));
|
||||
strncat(daemon->namebuff, "/", MAXDNAMESTR - strlen(daemon->namebuff));
|
||||
|
||||
/* remove unique-directory if it doesn't exist */
|
||||
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
|
||||
@@ -475,7 +475,7 @@ static void tftp_request(char *packet, ssize_t plen, struct listener *listen, ti
|
||||
size_t oldlen = strlen(daemon->namebuff);
|
||||
struct stat statbuf;
|
||||
|
||||
snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/",
|
||||
snprintf(daemon->namebuff + oldlen, MAXDNAMESTR - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/",
|
||||
macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
|
||||
|
||||
/* remove unique-directory if it doesn't exist */
|
||||
@@ -495,7 +495,7 @@ static void tftp_request(char *packet, ssize_t plen, struct listener *listen, ti
|
||||
}
|
||||
else if (filename[0] == '/')
|
||||
daemon->namebuff[0] = 0;
|
||||
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
strncat(daemon->namebuff, filename, MAXDNAMESTR - strlen(daemon->namebuff));
|
||||
|
||||
/* check permissions and open file */
|
||||
if ((transfer->file = check_tftp_fileperm(packet, &len, prefix, daemon->addrbuff)))
|
||||
@@ -545,8 +545,13 @@ static struct tftp_file *check_tftp_fileperm(char *packet, ssize_t *len, char *p
|
||||
int fd = -1;
|
||||
|
||||
/* trick to ban moving out of the subtree */
|
||||
if (prefix && strstr(namebuff, "/../"))
|
||||
goto perm;
|
||||
if (prefix)
|
||||
{
|
||||
char *suspect = strstr(namebuff, "/..");
|
||||
|
||||
if (suspect && (suspect[3] == '/' || suspect[3] == 0))
|
||||
goto perm;
|
||||
}
|
||||
|
||||
if ((fd = open(namebuff, O_RDONLY)) == -1)
|
||||
{
|
||||
@@ -622,7 +627,7 @@ void check_tftp_listeners(time_t now)
|
||||
{
|
||||
/* Use workspace to receive (small) request/ACK, to avoid overwriting precomputed reply */
|
||||
char *packet = daemon->workspacename;
|
||||
ssize_t plen = MAXDNAME * 2;
|
||||
ssize_t plen = MAXDNAMESTR;
|
||||
struct listener *listener;
|
||||
struct tftp_transfer *transfer, *tmp, **up;
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+100
-28
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000-2026 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
|
||||
@@ -105,8 +105,6 @@ u32 rand32(void)
|
||||
|
||||
u64 rand64(void)
|
||||
{
|
||||
static int outleft = 0;
|
||||
|
||||
if (outleft < 2)
|
||||
{
|
||||
if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
|
||||
@@ -138,14 +136,15 @@ static int check_name(char *in)
|
||||
{
|
||||
/* remove trailing .
|
||||
also fail empty string and label > 63 chars */
|
||||
size_t dotgap = 0, l = strlen(in);
|
||||
size_t dotgap = 0, wiresize = 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;
|
||||
if (l == 0)
|
||||
return 0;
|
||||
|
||||
if (in[l-1] == '.')
|
||||
{
|
||||
@@ -156,7 +155,10 @@ static int check_name(char *in)
|
||||
for (; (c = *in); in++)
|
||||
{
|
||||
if (c == '.')
|
||||
dotgap = 0;
|
||||
{
|
||||
wiresize += dotgap + 1;
|
||||
dotgap = 0;
|
||||
}
|
||||
else if (++dotgap > MAXLABEL)
|
||||
return 0;
|
||||
else if (isascii((unsigned char)c) && iscntrl((unsigned char)c))
|
||||
@@ -198,6 +200,10 @@ static int check_name(char *in)
|
||||
idn_encode = idn_encode || hasucase;
|
||||
#endif
|
||||
|
||||
/* length of final label and terminaton added */
|
||||
if (!idn_encode && wiresize + dotgap + 2 > MAXDNAME)
|
||||
return 0; /* wire representation too long */
|
||||
|
||||
return (idn_encode) ? 2 : 1;
|
||||
}
|
||||
|
||||
@@ -285,20 +291,24 @@ char *canonicalise(char *in, int *nomem)
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit)
|
||||
unsigned char *do_rfc1035_name(unsigned char *p, char *sval, unsigned char *limit)
|
||||
{
|
||||
int j;
|
||||
|
||||
/* Never make a name larger than the RFC limit */
|
||||
if (!limit || (limit - p > MAXDNAME))
|
||||
limit = p + MAXDNAME;
|
||||
|
||||
while (sval && *sval)
|
||||
{
|
||||
unsigned char *cp = p++;
|
||||
|
||||
if (limit && p > (unsigned char*)limit)
|
||||
if (p > limit)
|
||||
return NULL;
|
||||
|
||||
for (j = 0; *sval && (*sval != '.'); sval++, j++)
|
||||
{
|
||||
if (limit && p + 1 > (unsigned char*)limit)
|
||||
if (p + 1 > limit)
|
||||
return NULL;
|
||||
|
||||
if (*sval == NAME_ESCAPE)
|
||||
@@ -429,10 +439,14 @@ int hostname_issubdomain(char *a, char *b)
|
||||
for (ap = a; *ap; ap++);
|
||||
for (bp = b; *bp; bp++);
|
||||
|
||||
/* a shorter than b */
|
||||
if ((ap - a) < (bp - b))
|
||||
/* anything is a subdomain of the root domain. */
|
||||
if (ap == a)
|
||||
return (bp == b) ? 2 : 1;
|
||||
|
||||
/* b shorter than a */
|
||||
if ((bp - b) < (ap - a))
|
||||
return 0;
|
||||
|
||||
|
||||
do
|
||||
{
|
||||
c1 = (unsigned char) *(--ap);
|
||||
@@ -445,18 +459,17 @@ int hostname_issubdomain(char *a, char *b)
|
||||
|
||||
if (c1 != c2)
|
||||
return 0;
|
||||
} while (bp != b);
|
||||
} while (ap != a);
|
||||
|
||||
if (ap == a)
|
||||
if (bp == b)
|
||||
return 2;
|
||||
|
||||
if (*(--ap) == '.')
|
||||
if (*(--bp) == '.')
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
time_t dnsmasq_time(void)
|
||||
{
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
@@ -581,6 +594,7 @@ void prettyprint_time(char *buf, unsigned int t)
|
||||
else
|
||||
{
|
||||
unsigned int x, p = 0;
|
||||
buf[0] = '\0';
|
||||
if ((x = t/86400))
|
||||
p += sprintf(&buf[p], "%ud", x);
|
||||
if ((x = (t/3600)%24))
|
||||
@@ -678,18 +692,24 @@ int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask
|
||||
return count;
|
||||
}
|
||||
|
||||
char *print_mac(char *buff, unsigned char *mac, int len)
|
||||
char *print_mac(unsigned char *mac, int len)
|
||||
{
|
||||
char *p = buff;
|
||||
int i;
|
||||
|
||||
if (len == 0)
|
||||
sprintf(p, "<null>");
|
||||
else
|
||||
for (i = 0; i < len; i++)
|
||||
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
|
||||
static struct iovec buff = { NULL, 0 };
|
||||
|
||||
return buff;
|
||||
/* each byte is two digits plus ':' except that last
|
||||
which is two digits plus terminator. */
|
||||
if (len == 0 || !expand_buf(&buff, len*3))
|
||||
return "<null>";
|
||||
else
|
||||
{
|
||||
char *p = buff.iov_base;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
|
||||
}
|
||||
|
||||
return buff.iov_base;
|
||||
}
|
||||
|
||||
/* rc is return from sendto and friends.
|
||||
@@ -927,7 +947,7 @@ int kernel_version(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#define hash_ptr(x) (((unsigned int)(((char *)(x)) - ((char *)NULL))) & 0xffffff)
|
||||
#define hash_ptr(x) (((uintptr_t)(x)) & 0xffffff)
|
||||
|
||||
void *whine_malloc_real(const char *func, unsigned int line, size_t size)
|
||||
{
|
||||
@@ -991,7 +1011,7 @@ int expand_workspace_real(const char *func, unsigned int line, unsigned char ***
|
||||
if (!(p = whine_realloc_real("expand_workspace", func, line, *wkspc, new * sizeof(unsigned char *))))
|
||||
return 0;
|
||||
|
||||
memset(p+old, 0, new-old);
|
||||
memset(p+old, 0, (new-old) * sizeof(unsigned char *));
|
||||
|
||||
*wkspc = p;
|
||||
*szp = new;
|
||||
@@ -1016,3 +1036,55 @@ void free_real(const char *func, unsigned int line, void *ptr)
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* get lines from f, expand buffer as required. Buffer is freed when
|
||||
EOF reached and we return zero. Buffer also freed if f == NULL. */
|
||||
int get_line_alloc(FILE *f, char **buffp, size_t *sizep)
|
||||
{
|
||||
size_t cnt = 0;
|
||||
char *buff = *buffp;
|
||||
|
||||
while (1) {
|
||||
int c = f ? getc(f) : EOF;
|
||||
|
||||
if (cnt == *sizep)
|
||||
{
|
||||
void *new;
|
||||
|
||||
if ((new = whine_realloc(buff, cnt + 1024)))
|
||||
{
|
||||
buff = *buffp = new;
|
||||
*sizep = cnt + 1024;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* allocation failed, ignore line.
|
||||
Whine_realloc will have complained. */
|
||||
while (c != '\n' && c != EOF)
|
||||
c = getc(f);
|
||||
|
||||
cnt = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
buff[cnt] = 0;
|
||||
|
||||
if (c == '\n')
|
||||
return 1;
|
||||
|
||||
if (c == EOF)
|
||||
{
|
||||
/* handle last line without '\n' */
|
||||
if (cnt != 0)
|
||||
return 1;
|
||||
|
||||
free(buff);
|
||||
*buffp = NULL;
|
||||
*sizep = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
buff[cnt++] = c;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user