mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-04 13:48:30 +00:00
Compare commits
69
Commits
v2.92rc3
...
v2.93test10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
66d9658f5f | ||
|
|
ca30c82647 | ||
|
|
32a54fc8a5 | ||
|
|
3f2bfca3df | ||
|
|
e4d0bb4579 | ||
|
|
84415a87be | ||
|
|
1537645e8f | ||
|
|
ceab392c60 | ||
|
|
5f0f6aa9b1 | ||
|
|
578ea08391 | ||
|
|
fbfcf87f8b | ||
|
|
19f3e06e7a | ||
|
|
40d1152059 | ||
|
|
3c830c4f1c | ||
|
|
e9f400dd37 | ||
|
|
870f14108f | ||
|
|
714d36229a | ||
|
|
60e90e2224 | ||
|
|
ed2ba5b07a | ||
|
|
729c16a8ac | ||
|
|
80acb76f46 | ||
|
|
f1fa05d754 | ||
|
|
b148ba4752 | ||
|
|
81f619612e | ||
|
|
cb321709e9 | ||
|
|
8eb36844a0 | ||
|
|
8fa68a6e1e | ||
|
|
1269f074f8 | ||
|
|
e1faf705c9 | ||
|
|
f603a4f920 | ||
|
|
c090f1d17b | ||
|
|
fa48bdb939 |
No files matched your search
@@ -1,3 +1,40 @@
|
||||
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.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
|
||||
is not required, was being done. Thanks to Jan Breig for
|
||||
spotting this.
|
||||
|
||||
Enable support for inotify on FreeBSD 15.0-RELEASE, which added
|
||||
Linux-compatible inotify support.
|
||||
|
||||
Fix DNSSEC failure with spurious RRSIGs. The presence of wrong
|
||||
RRSIG RRs in replies caused DNSSEC validation to fail even
|
||||
when the RRs do not require validation because the zone is
|
||||
unsigned. Note that, at the time of this commit, Google
|
||||
DNS appears to have the same bug, so if you're using 8.8.8.8
|
||||
or friends as upstream, resolving the broken zones
|
||||
(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
|
||||
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.
|
||||
|
||||
Fix regression in 2.92 release which broke DHCPv6 when a DHCP
|
||||
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.
|
||||
|
||||
|
||||
version 2.92
|
||||
Redesign the interaction between DNSSEC validation and per-domain
|
||||
servers, specified as --server=/<domain>/<ip-address>. This should
|
||||
|
||||
@@ -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
|
||||
|
||||
+7
-3
@@ -136,6 +136,8 @@ Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on r
|
||||
then the log has extra information at the start of each line.
|
||||
This consists of a serial number which ties together the log lines associated with an individual query, and the IP address of the requestor. If the argument "proto" is supplied, this shows everything that "extra" does and also the network protocol used to communicate the queries. Logging of only queries to the authoritative server can be configured with
|
||||
.B --log-queries=auth
|
||||
and logging only failed replies (Error, NXDOMAIN or NODATA) can be configured with
|
||||
.B --log-queries=only_failed
|
||||
.TP
|
||||
.B \-8, --log-facility=<facility>
|
||||
Set the facility to which dnsmasq will send syslog entries, this
|
||||
@@ -1513,7 +1515,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
|
||||
@@ -1527,9 +1529,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
|
||||
|
||||
+8
-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
|
||||
@@ -591,7 +591,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (auth && zone)
|
||||
{
|
||||
char *authname;
|
||||
int newoffset, offset = 0;
|
||||
int newoffset = ansp - (unsigned char *)header, offset = 0;
|
||||
|
||||
if (!subnet)
|
||||
authname = zone->domain;
|
||||
@@ -631,8 +631,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
}
|
||||
|
||||
/* handle NS and SOA in auth section or for explicit queries */
|
||||
newoffset = ansp - (unsigned char *)header;
|
||||
if (((anscount == 0 && !ns) || soa) &&
|
||||
if (((anscount == 0 && !ns) || soa) &&
|
||||
add_resource_record(header, limit, &trunc, 0, &ansp,
|
||||
daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
|
||||
authname, daemon->authserver, daemon->hostmaster,
|
||||
@@ -650,11 +649,10 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (anscount != 0 || ns)
|
||||
{
|
||||
struct name_list *secondary;
|
||||
|
||||
|
||||
/* Only include the machine running dnsmasq if it's acting as an auth server */
|
||||
if (daemon->authinterface)
|
||||
{
|
||||
newoffset = ansp - (unsigned char *)header;
|
||||
if (add_resource_record(header, limit, &trunc, -offset, &ansp,
|
||||
daemon->auth_ttl, NULL, T_NS, C_IN, "d", offset == 0 ? authname : NULL, daemon->authserver))
|
||||
{
|
||||
@@ -669,9 +667,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
|
||||
if (!subnet)
|
||||
for (secondary = daemon->secondary_forward_server; secondary; secondary = secondary->next)
|
||||
if (add_resource_record(header, limit, &trunc, offset, &ansp,
|
||||
daemon->auth_ttl, NULL, T_NS, C_IN, "d", secondary->name))
|
||||
if (add_resource_record(header, limit, &trunc, -offset, &ansp,
|
||||
daemon->auth_ttl, NULL, T_NS, C_IN, "d", offset == 0 ? authname : NULL, secondary->name))
|
||||
{
|
||||
if (offset == 0)
|
||||
offset = newoffset;
|
||||
if (ns)
|
||||
anscount++;
|
||||
else
|
||||
|
||||
+10
-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
|
||||
@@ -193,20 +193,22 @@ void *blockdata_retrieve(struct blockdata *block, size_t len, void *data)
|
||||
{
|
||||
size_t blen;
|
||||
struct blockdata *b;
|
||||
uint8_t *new, *d;
|
||||
uint8_t *d;
|
||||
|
||||
static unsigned int buff_len = 0;
|
||||
static unsigned char *buff = NULL;
|
||||
|
||||
if (!data)
|
||||
{
|
||||
static unsigned int buff_len = 0;
|
||||
static unsigned char *buff = NULL;
|
||||
uint8_t *new;
|
||||
|
||||
if (len > buff_len)
|
||||
{
|
||||
if (!(new = whine_malloc(len)))
|
||||
blen = len + 1024;
|
||||
if (!(new = whine_realloc(buff, blen)))
|
||||
return NULL;
|
||||
if (buff)
|
||||
free(buff);
|
||||
|
||||
buff = new;
|
||||
buff_len = blen;
|
||||
}
|
||||
data = buff;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+196
-123
@@ -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
|
||||
@@ -17,13 +17,16 @@
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL;
|
||||
#ifdef HAVE_DHCP
|
||||
static struct crec *dhcp_spare = NULL;
|
||||
#endif
|
||||
static struct crec *config_spare = NULL;
|
||||
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;
|
||||
char data[NAMEBLOCK_CHARS];
|
||||
} *hostblocks = NULL;
|
||||
|
||||
static void make_non_terminals(struct crec *source);
|
||||
static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class,
|
||||
@@ -192,6 +195,61 @@ void cache_init(void)
|
||||
rehash(daemon->cachesize);
|
||||
}
|
||||
|
||||
static struct crec *get_config_crec(void)
|
||||
{
|
||||
struct crec *ret;
|
||||
|
||||
if (config_spare)
|
||||
{
|
||||
ret = config_spare;
|
||||
config_spare = config_spare->next;
|
||||
}
|
||||
else
|
||||
ret = whine_malloc(SIZEOF_POINTER_CREC);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void free_config_crec(struct crec *p)
|
||||
{
|
||||
p->next = config_spare;
|
||||
config_spare = p;
|
||||
}
|
||||
|
||||
static char *store_name(unsigned int namelen, unsigned int index)
|
||||
{
|
||||
struct nameblock *block;
|
||||
char *ret = NULL;
|
||||
|
||||
for (block = hostblocks; block; block = block->next)
|
||||
if (block->index == index && NAMEBLOCK_CHARS - block->last >= namelen)
|
||||
break;
|
||||
|
||||
if (!block && ((block = whine_malloc(sizeof(struct nameblock)))))
|
||||
{
|
||||
block->next = hostblocks;
|
||||
block->index = index;
|
||||
hostblocks = block;
|
||||
}
|
||||
|
||||
if (block && NAMEBLOCK_CHARS - block->last >= namelen)
|
||||
{
|
||||
ret = &block->data[block->last];
|
||||
block->last += namelen;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void free_names(unsigned int index)
|
||||
{
|
||||
struct nameblock *block;
|
||||
|
||||
for (block = hostblocks; block; block = block->next)
|
||||
if (index == UID_NONE || block->index == index)
|
||||
block->last = 0;
|
||||
}
|
||||
|
||||
/* In most cases, we create the hash table once here by calling this with (hash_table == NULL)
|
||||
but if the hosts file(s) are big (some people have 50000 ad-block entries), the table
|
||||
will be much too small, so the hosts reading code calls rehash every 1000 addresses, to
|
||||
@@ -441,12 +499,14 @@ unsigned int cache_remove_uid(const unsigned int uid)
|
||||
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid)
|
||||
{
|
||||
*up = tmp;
|
||||
free(crecp);
|
||||
free_config_crec(crecp);
|
||||
removed++;
|
||||
}
|
||||
else
|
||||
up = &crecp->hash_next;
|
||||
}
|
||||
|
||||
free_names(uid);
|
||||
|
||||
return removed;
|
||||
}
|
||||
@@ -855,7 +915,7 @@ void cache_end_insert(void)
|
||||
#ifdef HAVE_DNSSEC
|
||||
void cache_update_hwm(void)
|
||||
{
|
||||
/* Sneak out possibly updated crypto HWM values. */
|
||||
/* Sneak out possibly updated crypto HWM values. */
|
||||
unsigned char op = PIPE_OP_STATS;
|
||||
|
||||
read_write(daemon->pipe_to_parent, &op, sizeof(op), RW_WRITE);
|
||||
@@ -1000,6 +1060,7 @@ int cache_recv_insert(time_t now, int fd)
|
||||
}
|
||||
|
||||
case PIPE_OP_RESULT:
|
||||
case PIPE_OP_KILLED:
|
||||
{
|
||||
/* UDP validation moved to TCP to avoid truncation.
|
||||
Restart UDP validation process with the returned result. */
|
||||
@@ -1013,21 +1074,30 @@ int cache_recv_insert(time_t now, int fd)
|
||||
!read_write(fd, (unsigned char *)&ret_len, sizeof(ret_len), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)daemon->packet, ret_len, RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&forward, sizeof(forward), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&uid, sizeof(uid), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&keycount, sizeof(keycount), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&keycountp, sizeof(keycountp), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&validatecount, sizeof(validatecount), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&validatecountp, sizeof(validatecountp), RW_READ))
|
||||
!read_write(fd, (unsigned char *)&uid, sizeof(uid), RW_READ))
|
||||
return 0;
|
||||
|
||||
if (op == PIPE_OP_RESULT &&
|
||||
(!read_write(fd, (unsigned char *)&keycount, sizeof(keycount), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&keycountp, sizeof(keycountp), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&validatecount, sizeof(validatecount), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&validatecountp, sizeof(validatecountp), RW_READ)))
|
||||
return 0;
|
||||
|
||||
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)
|
||||
{
|
||||
/* repatriate the work counters from the child process. */
|
||||
*keycountp = keycount;
|
||||
*validatecountp = validatecount;
|
||||
if (op == PIPE_OP_RESULT)
|
||||
{
|
||||
/* repatriate the work counters from the child process. */
|
||||
*keycountp = keycount;
|
||||
*validatecountp = validatecount;
|
||||
}
|
||||
|
||||
if (!forward->dependent)
|
||||
return_reply(now, forward, (struct dns_header *)daemon->packet, ret_len, status);
|
||||
@@ -1254,7 +1324,7 @@ static void add_hosts_entry(struct crec *cache, union all_addr *addr, int addrle
|
||||
while ((lookup = cache_find_by_name(lookup, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6))))
|
||||
if ((lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
|
||||
{
|
||||
free(cache);
|
||||
free_config_crec(cache);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1377,13 +1447,13 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
{
|
||||
if (inet_pton(AF_INET, token, &addr) > 0)
|
||||
{
|
||||
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
|
||||
flags = F_NAMEP | F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
|
||||
addrlen = INADDRSZ;
|
||||
domain_suffix = get_domain(addr.addr4);
|
||||
}
|
||||
else if (inet_pton(AF_INET6, token, &addr) > 0)
|
||||
{
|
||||
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
|
||||
flags = F_NAMEP | F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
|
||||
addrlen = IN6ADDRSZ;
|
||||
domain_suffix = get_domain6(&addr.addr6);
|
||||
}
|
||||
@@ -1417,27 +1487,37 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
if ((canon = canonicalise(token, &nomem)))
|
||||
{
|
||||
/* If set, add a version of the name with a default domain appended */
|
||||
if (option_bool(OPT_EXPAND) && domain_suffix && !fqdn &&
|
||||
(cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 2 + strlen(domain_suffix))))
|
||||
if (option_bool(OPT_EXPAND) && domain_suffix && !fqdn && (cache = get_config_crec()))
|
||||
{
|
||||
strcpy(cache->name.sname, canon);
|
||||
strcat(cache->name.sname, ".");
|
||||
strcat(cache->name.sname, domain_suffix);
|
||||
cache->flags = flags;
|
||||
cache->ttd = daemon->local_ttl;
|
||||
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
|
||||
name_count++;
|
||||
names_done++;
|
||||
if (!(cache->name.namep = store_name(strlen(canon) + 2 + strlen(domain_suffix), index)))
|
||||
free_config_crec(cache);
|
||||
else
|
||||
{
|
||||
strcpy(cache->name.namep, canon);
|
||||
strcat(cache->name.namep, ".");
|
||||
strcat(cache->name.namep, domain_suffix);
|
||||
cache->flags = flags;
|
||||
cache->ttd = daemon->local_ttl;
|
||||
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
|
||||
name_count++;
|
||||
names_done++;
|
||||
}
|
||||
}
|
||||
if ((cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 1)))
|
||||
if ((cache = get_config_crec()))
|
||||
{
|
||||
strcpy(cache->name.sname, canon);
|
||||
cache->flags = flags;
|
||||
cache->ttd = daemon->local_ttl;
|
||||
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
|
||||
name_count++;
|
||||
names_done++;
|
||||
if (!(cache->name.namep = store_name(strlen(canon) + 1, index)))
|
||||
free_config_crec(cache);
|
||||
else
|
||||
{
|
||||
strcpy(cache->name.namep, canon);
|
||||
cache->flags = flags;
|
||||
cache->ttd = daemon->local_ttl;
|
||||
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
|
||||
name_count++;
|
||||
names_done++;
|
||||
}
|
||||
}
|
||||
|
||||
free(canon);
|
||||
|
||||
}
|
||||
@@ -1488,7 +1568,7 @@ void cache_reload(void)
|
||||
if (cache->flags & (F_HOSTS | F_CONFIG))
|
||||
{
|
||||
*up = cache->hash_next;
|
||||
free(cache);
|
||||
free_config_crec(cache);
|
||||
}
|
||||
else if (!(cache->flags & F_DHCP))
|
||||
{
|
||||
@@ -1503,11 +1583,13 @@ void cache_reload(void)
|
||||
else
|
||||
up = &cache->hash_next;
|
||||
}
|
||||
|
||||
free_names(UID_NONE); /* free everything */
|
||||
|
||||
/* Add locally-configured CNAMEs to the cache */
|
||||
for (a = daemon->cnames; a; a = a->next)
|
||||
if (a->alias[1] != '*' &&
|
||||
((cache = whine_malloc(SIZEOF_POINTER_CREC))))
|
||||
((cache = get_config_crec())))
|
||||
{
|
||||
cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;
|
||||
cache->ttd = a->ttl;
|
||||
@@ -1521,25 +1603,30 @@ void cache_reload(void)
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
for (ds = daemon->ds; ds; ds = ds->next)
|
||||
if ((cache = whine_malloc(SIZEOF_POINTER_CREC)) &&
|
||||
(cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
|
||||
if (ds->name && (cache = get_config_crec()))
|
||||
{
|
||||
cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
|
||||
cache->ttd = daemon->local_ttl;
|
||||
cache->name.namep = ds->name;
|
||||
cache->uid = ds->class;
|
||||
if (ds->digestlen != 0)
|
||||
{
|
||||
cache->addr.ds.keylen = ds->digestlen;
|
||||
cache->addr.ds.algo = ds->algo;
|
||||
cache->addr.ds.keytag = ds->keytag;
|
||||
cache->addr.ds.digest = ds->digest_type;
|
||||
}
|
||||
else
|
||||
cache->flags |= F_NEG | F_DNSSECOK | F_NO_RR;
|
||||
|
||||
cache_hash(cache);
|
||||
make_non_terminals(cache);
|
||||
if (!(cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
|
||||
free_config_crec(cache);
|
||||
else
|
||||
{
|
||||
cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
|
||||
cache->ttd = daemon->local_ttl;
|
||||
cache->name.namep = ds->name;
|
||||
cache->uid = ds->class;
|
||||
if (ds->digestlen != 0)
|
||||
{
|
||||
cache->addr.ds.keylen = ds->digestlen;
|
||||
cache->addr.ds.algo = ds->algo;
|
||||
cache->addr.ds.keytag = ds->keytag;
|
||||
cache->addr.ds.digest = ds->digest_type;
|
||||
}
|
||||
else
|
||||
cache->flags |= F_NEG | F_DNSSECOK | F_NO_RR;
|
||||
|
||||
cache_hash(cache);
|
||||
make_non_terminals(cache);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1554,7 +1641,7 @@ void cache_reload(void)
|
||||
for (nl = hr->names; nl; nl = nl->next)
|
||||
{
|
||||
if ((hr->flags & HR_4) &&
|
||||
(cache = whine_malloc(SIZEOF_POINTER_CREC)))
|
||||
(cache = get_config_crec()))
|
||||
{
|
||||
cache->name.namep = nl->name;
|
||||
cache->ttd = hr->ttl;
|
||||
@@ -1563,7 +1650,7 @@ void cache_reload(void)
|
||||
}
|
||||
|
||||
if ((hr->flags & HR_6) &&
|
||||
(cache = whine_malloc(SIZEOF_POINTER_CREC)))
|
||||
(cache = get_config_crec()))
|
||||
{
|
||||
cache->name.namep = nl->name;
|
||||
cache->ttd = hr->ttl;
|
||||
@@ -1655,8 +1742,7 @@ void cache_unhash_dhcp(void)
|
||||
if (cache->flags & F_DHCP)
|
||||
{
|
||||
*up = cache->hash_next;
|
||||
cache->next = dhcp_spare;
|
||||
dhcp_spare = cache;
|
||||
free_config_crec(cache);
|
||||
}
|
||||
else
|
||||
up = &cache->hash_next;
|
||||
@@ -1727,12 +1813,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
|
||||
else
|
||||
flags |= F_REVERSE;
|
||||
|
||||
if ((crec = dhcp_spare))
|
||||
dhcp_spare = dhcp_spare->next;
|
||||
else /* need new one */
|
||||
crec = whine_malloc(SIZEOF_POINTER_CREC);
|
||||
|
||||
if (crec) /* malloc may fail */
|
||||
if ((crec = get_config_crec()))
|
||||
{
|
||||
crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD;
|
||||
if (ttd == 0)
|
||||
@@ -1779,15 +1860,7 @@ static void make_non_terminals(struct crec *source)
|
||||
hostname_isequal(name, cache_get_name(crecp)))
|
||||
{
|
||||
*up = crecp->hash_next;
|
||||
#ifdef HAVE_DHCP
|
||||
if (type & F_DHCP)
|
||||
{
|
||||
crecp->next = dhcp_spare;
|
||||
dhcp_spare = crecp;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
free(crecp);
|
||||
free_config_crec(crecp);
|
||||
break;
|
||||
}
|
||||
else
|
||||
@@ -1820,17 +1893,7 @@ static void make_non_terminals(struct crec *source)
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if ((source->flags & F_DHCP) && dhcp_spare)
|
||||
{
|
||||
crecp = dhcp_spare;
|
||||
dhcp_spare = dhcp_spare->next;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
crecp = whine_malloc(SIZEOF_POINTER_CREC);
|
||||
|
||||
if (crecp)
|
||||
if ((crecp = get_config_crec()))
|
||||
{
|
||||
crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_RR | F_DNSKEY | F_DS | F_REVERSE);
|
||||
if (!(crecp->flags & F_IMMORTAL))
|
||||
@@ -2159,9 +2222,9 @@ static char *querystr(char *desc, unsigned short type)
|
||||
unsigned int i;
|
||||
int len = 10; /* strlen("type=xxxxx") */
|
||||
const char *types = NULL;
|
||||
static char *buff = NULL;
|
||||
static int bufflen = 0;
|
||||
|
||||
static struct iovec buff = { NULL, 0 };
|
||||
char *buffp;
|
||||
|
||||
for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
|
||||
if (typestr[i].type == type)
|
||||
{
|
||||
@@ -2176,37 +2239,28 @@ static char *querystr(char *desc, unsigned short type)
|
||||
len += strlen(desc);
|
||||
}
|
||||
len++; /* terminator */
|
||||
|
||||
if (!buff || bufflen < len)
|
||||
{
|
||||
if (buff)
|
||||
free(buff);
|
||||
else if (len < 20)
|
||||
len = 20;
|
||||
|
||||
buff = whine_malloc(len);
|
||||
bufflen = len;
|
||||
}
|
||||
|
||||
if (buff)
|
||||
if (!expand_buf(&buff, len))
|
||||
return "";
|
||||
|
||||
buffp = buff.iov_base;
|
||||
|
||||
if (desc)
|
||||
{
|
||||
if (desc)
|
||||
{
|
||||
if (types)
|
||||
sprintf(buff, "%s[%s]", desc, types);
|
||||
else
|
||||
sprintf(buff, "%s[type=%d]", desc, type);
|
||||
}
|
||||
if (types)
|
||||
sprintf(buffp, "%s[%s]", desc, types);
|
||||
else
|
||||
{
|
||||
if (types)
|
||||
sprintf(buff, "<%s>", types);
|
||||
else
|
||||
sprintf(buff, "<type=%d>", type);
|
||||
}
|
||||
sprintf(buffp, "%s[type=%d]", desc, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (types)
|
||||
sprintf(buffp, "<%s>", types);
|
||||
else
|
||||
sprintf(buffp, "<type=%d>", type);
|
||||
}
|
||||
|
||||
return buff ? buff : "";
|
||||
return buffp;
|
||||
}
|
||||
|
||||
static char *edestr(int ede)
|
||||
@@ -2243,10 +2297,20 @@ static char *edestr(int ede)
|
||||
case EDE_UNS_NS3_ITER: return "unsupported NSEC3 iterations value";
|
||||
case EDE_UNABLE_POLICY: return "uanble to conform to policy";
|
||||
case EDE_SYNTHESIZED: return "synthesized";
|
||||
case EDE_US_SERVFAIL: return "upstream returned SERVFAIL";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static int error_occured(unsigned int flags) {
|
||||
if (flags & F_RCODE)
|
||||
return 1;
|
||||
else if (flags & F_NEG)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, unsigned short type)
|
||||
{
|
||||
char *source, *dest;
|
||||
@@ -2259,12 +2323,15 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
if (!option_bool(OPT_LOG))
|
||||
return;
|
||||
|
||||
if(option_bool(OPT_LOG_ONLY_FAILED) && !error_occured(flags))
|
||||
return;
|
||||
|
||||
/* F_NOERR is reused here to indicate logs arrising from auth queries */
|
||||
if (!(flags & F_NOERR) && option_bool(OPT_AUTH_LOG))
|
||||
return;
|
||||
|
||||
/* build query type string if requested */
|
||||
if (!(flags & (F_SERVER | F_IPSET | F_QUERY)) && type > 0)
|
||||
if (!(flags & (F_SERVER | F_IPSET | F_QUERY | F_KEYTAG | F_RR)) && type > 0)
|
||||
arg = querystr(arg, type);
|
||||
|
||||
dest = arg;
|
||||
@@ -2280,19 +2347,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)
|
||||
|
||||
+14
-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
|
||||
@@ -15,13 +15,14 @@
|
||||
*/
|
||||
|
||||
#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 */
|
||||
#define TCP_BACKLOG 32 /* kernel backlog limit for TCP connections */
|
||||
#define EDNS_PKTSZ 1232 /* default max EDNS.0 UDP packet from from /dnsflagday.net/2020 */
|
||||
#define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */
|
||||
#define NAMEBLOCK_CHARS 1500 /* quantum of memory allocation for names from /etc/hosts */
|
||||
#define DNSSEC_LIMIT_WORK 40 /* Max number of queries to validate one question */
|
||||
#define DNSSEC_LIMIT_SIG_FAIL 20 /* Number of signature that can fail to validate in one answer */
|
||||
#define DNSSEC_LIMIT_CRYPTO 200 /* max no. of crypto operations to validate one query. */
|
||||
@@ -142,7 +143,8 @@ HAVE_LOOP
|
||||
include functionality to probe for and remove DNS forwarding loops.
|
||||
|
||||
HAVE_INOTIFY
|
||||
use the Linux inotify facility to efficiently re-read configuration files.
|
||||
use the Linux and FreeBSD >= 15 inotify facility
|
||||
to efficiently re-read configuration files.
|
||||
|
||||
NO_ID
|
||||
Don't report *.bind CHAOS info to clients, forward such requests upstream instead.
|
||||
@@ -375,8 +377,15 @@ HAVE_SOCKADDR_SA_LEN
|
||||
#undef HAVE_DUMPFILE
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_LINUX_NETWORK) && !defined(NO_INOTIFY)
|
||||
#define HAVE_INOTIFY
|
||||
#if !defined(NO_INOTIFY)
|
||||
# if defined (HAVE_LINUX_NETWORK)
|
||||
# define HAVE_INOTIFY
|
||||
# elif defined (__FreeBSD__) && __FreeBSD__ + 0 >= 15
|
||||
# include <osreldate.h>
|
||||
# if __FreeBSD_version >= 1500068 /* 15.0.0 */
|
||||
# define HAVE_INOTIFY
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* This never compiles code, it's only used by the makefile to fingerprint builds. */
|
||||
|
||||
+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
|
||||
|
||||
+27
-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
|
||||
@@ -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",
|
||||
@@ -126,20 +135,18 @@ int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **diges
|
||||
|
||||
if (ctx_sz < hash->context_size)
|
||||
{
|
||||
if (!(new = whine_malloc(hash->context_size)))
|
||||
if (!(new = whine_realloc(ctx, hash->context_size)))
|
||||
return 0;
|
||||
if (ctx)
|
||||
free(ctx);
|
||||
|
||||
ctx = new;
|
||||
ctx_sz = hash->context_size;
|
||||
}
|
||||
|
||||
if (digest_sz < hash->digest_size)
|
||||
{
|
||||
if (!(new = whine_malloc(hash->digest_size)))
|
||||
if (!(new = whine_realloc(digest, hash->digest_size)))
|
||||
return 0;
|
||||
if (digest)
|
||||
free(digest);
|
||||
|
||||
digest = new;
|
||||
digest_sz = hash->digest_size;
|
||||
}
|
||||
@@ -503,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) */
|
||||
+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
|
||||
|
||||
+8
-9
@@ -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
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
+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
|
||||
|
||||
+2
-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
|
||||
@@ -399,7 +399,7 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
if (mess->ciaddr.s_addr != 0)
|
||||
dest.sin_addr = mess->ciaddr;
|
||||
}
|
||||
if ((is_relay_use_source || mess->giaddr.s_addr) && !is_relay_reply)
|
||||
else if ((is_relay_use_source || mess->giaddr.s_addr) && !is_relay_reply)
|
||||
{
|
||||
/* Send to BOOTP relay. */
|
||||
if (is_relay_use_source)
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-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
|
||||
@@ -86,6 +86,7 @@
|
||||
#define EDNS0_OPTION_UMBRELLA 20292 /* Cisco Umbrella temporary assignment */
|
||||
|
||||
/* RFC-8914 extended errors, negative values are our definitions */
|
||||
#define EDE_US_SERVFAIL -2 /* SERVFAIL from usptream */
|
||||
#define EDE_UNSET -1 /* No extended DNS error available */
|
||||
#define EDE_OTHER 0 /* Other */
|
||||
#define EDE_USUPDNSKEY 1 /* Unsupported DNSKEY algo */
|
||||
|
||||
+149
-81
@@ -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
|
||||
@@ -199,7 +199,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 +437,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 +856,17 @@ 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);
|
||||
|
||||
if (daemon->port == 0)
|
||||
my_syslog(LOG_INFO, _("started, version %s DNS disabled"), VERSION);
|
||||
else
|
||||
@@ -943,9 +946,27 @@ int main (int argc, char **argv)
|
||||
my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until system time valid"));
|
||||
|
||||
for (ds = daemon->ds; ds; ds = ds->next)
|
||||
my_syslog(LOG_INFO,
|
||||
ds->digestlen == 0 ? _("configured with negative trust anchor for %s") : _("configured with trust anchor for %s keytag %u"),
|
||||
ds->name[0] == 0 ? "<root>" : ds->name, ds->keytag);
|
||||
{
|
||||
struct ds_config *ds1;
|
||||
|
||||
for (ds1 = ds->next; ds1; ds1 = ds1->next)
|
||||
if (strcmp(ds->name, ds1->name) == 0 &&
|
||||
ds->digestlen == ds1->digestlen &&
|
||||
(ds->digestlen == 0 ||
|
||||
(ds->algo == ds1->algo &&
|
||||
ds->keytag == ds1->keytag &&
|
||||
ds->digest_type == ds1->digest_type &&
|
||||
memcmp(ds->digest, ds1->digest, ds->digestlen) == 0)))
|
||||
{
|
||||
ds->name = NULL; /* Mark as duplicate */
|
||||
break;
|
||||
}
|
||||
|
||||
if (ds->name)
|
||||
my_syslog(LOG_INFO,
|
||||
ds->digestlen == 0 ? _("configured with negative trust anchor for %s") : _("configured with trust anchor for %s keytag %u"),
|
||||
ds->name[0] == 0 ? "<root>" : ds->name, ds->keytag);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1335,11 +1356,40 @@ static void sig_handler(int sig)
|
||||
if (sig == SIGTERM || sig == SIGINT)
|
||||
exit(EC_MISC);
|
||||
}
|
||||
else if (pid != getpid())
|
||||
else if (daemon->pipe_to_parent != -1)
|
||||
{
|
||||
/* alarm is used to kill TCP children after a fixed time. */
|
||||
if (sig == SIGALRM)
|
||||
_exit(0);
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (!daemon->forward_to_tcp)
|
||||
#endif
|
||||
_exit(0); /* Normal TCP child */
|
||||
#ifdef HAVE_DNSSEC
|
||||
else
|
||||
{
|
||||
/* udp_to_tcp transfer.
|
||||
If daemon->header_to_tcp is NULL the waiting is over and
|
||||
we can let things take their course, otherwise, send a failure
|
||||
return down the pipe to unblock the UDP transaction and kill
|
||||
the process. */
|
||||
if (daemon->header_to_tcp)
|
||||
{
|
||||
unsigned char op = PIPE_OP_KILLED;
|
||||
int status = STAT_ABANDONED;
|
||||
|
||||
read_write(daemon->pipe_to_parent, &op, sizeof(op), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&status, sizeof(status), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)(&daemon->plen_to_tcp), sizeof(daemon->plen_to_tcp), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)(daemon->header_to_tcp), daemon->plen_to_tcp, RW_WRITE);
|
||||
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);
|
||||
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1416,8 +1466,6 @@ void send_event(int fd, int event, int data, char *msg)
|
||||
while (writev(fd, iov, msg ? 2 : 1) == -1 && errno == EINTR);
|
||||
}
|
||||
|
||||
/* NOTE: the memory used to return msg is leaked: use msgs in events only
|
||||
to describe fatal errors. */
|
||||
static int read_event(int fd, struct event_desc *evp, char **msg)
|
||||
{
|
||||
char *buf;
|
||||
@@ -1427,12 +1475,22 @@ static int read_event(int fd, struct event_desc *evp, char **msg)
|
||||
|
||||
*msg = NULL;
|
||||
|
||||
if (evp->msg_sz != 0 &&
|
||||
(buf = malloc(evp->msg_sz + 1)) &&
|
||||
read_write(fd, (unsigned char *)buf, evp->msg_sz, RW_READ))
|
||||
if (evp->msg_sz != 0)
|
||||
{
|
||||
buf[evp->msg_sz] = 0;
|
||||
*msg = buf;
|
||||
if (!(buf = whine_malloc(evp->msg_sz + 1)))
|
||||
{
|
||||
int i;
|
||||
unsigned char a;
|
||||
|
||||
/* Keep the stream synchronised if malloc fails. */
|
||||
for (i = 0; i < evp->msg_sz; i++)
|
||||
read_write(fd, &a, 1, RW_READ);
|
||||
}
|
||||
else if (read_write(fd, (unsigned char *)buf, evp->msg_sz, RW_READ))
|
||||
{
|
||||
buf[evp->msg_sz] = 0;
|
||||
*msg = buf;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -1485,6 +1543,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1495,9 +1573,6 @@ static void async_event(int pipe, time_t now)
|
||||
int wstatus, i, check = 0;
|
||||
char *msg;
|
||||
|
||||
/* NOTE: the memory used to return msg is leaked: use msgs in events only
|
||||
to describe fatal errors. */
|
||||
|
||||
if (read_event(pipe, &ev, &msg))
|
||||
switch (ev.event)
|
||||
{
|
||||
@@ -1603,7 +1678,6 @@ static void async_event(int pipe, time_t now)
|
||||
case EVENT_SCRIPT_LOG:
|
||||
my_syslog(MS_SCRIPT | LOG_DEBUG, "%s", msg ? msg : "");
|
||||
free(msg);
|
||||
msg = NULL;
|
||||
break;
|
||||
|
||||
/* necessary for fatal errors in helper */
|
||||
@@ -1941,20 +2015,21 @@ static void check_dns_listeners(time_t now)
|
||||
|
||||
static void do_tcp_connection(struct listener *listener, time_t now, int slot)
|
||||
{
|
||||
int confd, client_ok = 1;
|
||||
int confd;
|
||||
struct irec *iface = NULL;
|
||||
pid_t p;
|
||||
union mysockaddr tcp_addr;
|
||||
socklen_t tcp_len = sizeof(union mysockaddr);
|
||||
unsigned char *buff;
|
||||
struct server *s;
|
||||
int flags, auth_dns;
|
||||
int flags, auth_dns = 0;
|
||||
struct in_addr netmask;
|
||||
int pipefd[2];
|
||||
struct iovec tcpbuff;
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
unsigned char a = 0;
|
||||
#endif
|
||||
|
||||
netmask.s_addr = 0;
|
||||
|
||||
while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
|
||||
|
||||
if (confd == -1)
|
||||
@@ -1981,58 +2056,63 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
|
||||
|
||||
enumerate_interfaces(0);
|
||||
|
||||
if (option_bool(OPT_NOWILD))
|
||||
iface = listener->iface; /* May be NULL */
|
||||
if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
|
||||
{
|
||||
if ((iface = listener->iface))
|
||||
{
|
||||
netmask = iface->netmask;
|
||||
auth_dns = iface->dns_auth;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int if_index;
|
||||
int if_index, got_index = 0;
|
||||
char intr_name[IF_NAMESIZE];
|
||||
|
||||
/* if we can find the arrival interface, check it's one that's allowed */
|
||||
/* if we can find the arrival interface, check it's one that's allowed
|
||||
tcp_interface() is not implemented on non-Linux platforms */
|
||||
if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
|
||||
indextoname(listener->tcpfd, if_index, intr_name))
|
||||
{
|
||||
union all_addr addr;
|
||||
|
||||
got_index = 1;
|
||||
|
||||
if (tcp_addr.sa.sa_family == AF_INET6)
|
||||
addr.addr6 = tcp_addr.in6.sin6_addr;
|
||||
else
|
||||
addr.addr4 = tcp_addr.in.sin_addr;
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (iface->index == if_index &&
|
||||
iface->addr.sa.sa_family == tcp_addr.sa.sa_family)
|
||||
break;
|
||||
|
||||
if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
|
||||
client_ok = 0;
|
||||
if (!iface_check(tcp_addr.sa.sa_family, &addr, intr_name, &auth_dns) &&
|
||||
!loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
|
||||
goto closeconandreturn;
|
||||
}
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
iface = listener->iface; /* May be NULL */
|
||||
else
|
||||
/* When binding the wildcard address, try and get the
|
||||
netmask of the interface for localisation. */
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&iface->addr, &tcp_addr))
|
||||
{
|
||||
netmask = iface->netmask;
|
||||
break;
|
||||
}
|
||||
|
||||
/* On platforms where tcp_interface() doesn't work, we rely
|
||||
of the presence of the local address of the connection in the
|
||||
interface list to check if we're accepting this connection and
|
||||
for its auth status. */
|
||||
if (!got_index)
|
||||
{
|
||||
/* Check for allowed interfaces when binding the wildcard address:
|
||||
we do this by looking for an interface with the same address as
|
||||
the local address of the TCP connection, then looking to see if that's
|
||||
an allowed interface. As a side effect, we get the netmask of the
|
||||
interface too, for localisation. */
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&iface->addr, &tcp_addr))
|
||||
break;
|
||||
|
||||
if (!iface)
|
||||
client_ok = 0;
|
||||
goto closeconandreturn;
|
||||
|
||||
auth_dns = iface->dns_auth;
|
||||
}
|
||||
}
|
||||
|
||||
if (!client_ok)
|
||||
goto closeconandreturn;
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
/* The code in edns0.c qthat decorates queries with the source MAC address depends
|
||||
/* The code in edns0.c that decorates queries with the source MAC address depends
|
||||
on the code in arp.c, which populates a cache with the contents of the ARP table
|
||||
using netlink. Since the child process can't use netlink, we pre-populate
|
||||
the cache with the ARP table entry for our source here, including a negative entry
|
||||
@@ -2100,23 +2180,9 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (iface)
|
||||
{
|
||||
netmask = iface->netmask;
|
||||
auth_dns = iface->dns_auth;
|
||||
}
|
||||
else
|
||||
{
|
||||
netmask.s_addr = 0;
|
||||
auth_dns = 0;
|
||||
}
|
||||
|
||||
/* Arrange for SIGALRM after CHILD_LIFETIME seconds to
|
||||
terminate the process. */
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
|
||||
/* Arrange for SIGALRM after CHILD_LIFETIME seconds to
|
||||
terminate the process. */
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* See comment above re: netlink socket. */
|
||||
close(daemon->netlinkfd);
|
||||
@@ -2133,10 +2199,8 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
|
||||
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
|
||||
while(retry_send(fcntl(confd, F_SETFL, flags & ~O_NONBLOCK)));
|
||||
|
||||
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
|
||||
|
||||
if (buff)
|
||||
free(buff);
|
||||
tcp_request(confd, now, &tcpbuff, &tcp_addr, netmask, auth_dns);
|
||||
free(tcpbuff.iov_base);
|
||||
|
||||
for (s = daemon->servers; s; s = s->next)
|
||||
if (s->tcpfd != -1)
|
||||
@@ -2239,8 +2303,12 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
|
||||
close(daemon->netlinkfd);
|
||||
read_write(pipefd[1], &a, 1, RW_WRITE);
|
||||
#endif
|
||||
alarm(CHILD_LIFETIME);
|
||||
close(pipefd[0]); /* close read end in child. */
|
||||
daemon->pipe_to_parent = pipefd[1];
|
||||
daemon->pipe_to_parent = pipefd[1];
|
||||
daemon->forward_to_tcp = forward;
|
||||
daemon->header_to_tcp = header;
|
||||
daemon->plen_to_tcp = *plen;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+38
-15
@@ -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
|
||||
@@ -289,7 +294,9 @@ struct event_desc {
|
||||
#define OPT_DO_0x20 75
|
||||
#define OPT_AUTH_LOG 76
|
||||
#define OPT_LEASEQUERY 77
|
||||
#define OPT_LAST 78
|
||||
#define OPT_LOG_ONLY_FAILED 78
|
||||
#define OPT_LOG_MALLOC 79
|
||||
#define OPT_LAST 80
|
||||
|
||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||
@@ -549,6 +556,7 @@ struct crec {
|
||||
#define PIPE_OP_STATS 3 /* Update parent's stats */
|
||||
#define PIPE_OP_IPSET 4 /* Update IPset */
|
||||
#define PIPE_OP_NFTSET 5 /* Update NFTset */
|
||||
#define PIPE_OP_KILLED 6 /* child killed by SIGALARM */
|
||||
|
||||
/* struct sockaddr is not large enough to hold any address,
|
||||
and specifically not big enough to hold an IPv6 address.
|
||||
@@ -760,10 +768,9 @@ struct dyndir {
|
||||
#define STAT_NEED_DS 0x40000
|
||||
#define STAT_NEED_KEY 0x50000
|
||||
#define STAT_TRUNCATED 0x60000
|
||||
#define STAT_SECURE_WILDCARD 0x70000
|
||||
#define STAT_OK 0x80000
|
||||
#define STAT_ABANDONED 0x90000
|
||||
#define STAT_ASYNC 0xa0000
|
||||
#define STAT_OK 0x70000
|
||||
#define STAT_ABANDONED 0x80000
|
||||
#define STAT_ASYNC 0x90000
|
||||
|
||||
#define DNSSEC_FAIL_NYV 0x0001 /* key not yet valid */
|
||||
#define DNSSEC_FAIL_EXP 0x0002 /* key expired */
|
||||
@@ -777,6 +784,7 @@ struct dyndir {
|
||||
#define DNSSEC_FAIL_NSEC3_ITERS 0x0200 /* too many iterations in NSEC3 */
|
||||
#define DNSSEC_FAIL_BADPACKET 0x0400 /* bad packet */
|
||||
#define DNSSEC_FAIL_WORK 0x0800 /* too much crypto */
|
||||
#define DNSSEC_FAIL_UPSTREAM 0x1000 /* SERVFAIL from UPSTREAM */
|
||||
|
||||
#define STAT_ISEQUAL(a, b) (((a) & 0xffff0000) == (b))
|
||||
|
||||
@@ -1205,6 +1213,7 @@ extern struct daemon {
|
||||
int log_fac; /* log facility */
|
||||
char *log_file; /* optional log file */
|
||||
int max_logs; /* queue limit */
|
||||
int log_malloc; /* log malloc/realloc/free */
|
||||
int randport_limit; /* Maximum number of source ports for query. */
|
||||
int cachesize, ftabsize;
|
||||
int port, query_port, min_port, max_port;
|
||||
@@ -1271,6 +1280,9 @@ extern struct daemon {
|
||||
int dnssec_no_time_check;
|
||||
int back_to_the_future;
|
||||
int limit[LIMIT_MAX];
|
||||
struct frec *forward_to_tcp;
|
||||
struct dns_header *header_to_tcp;
|
||||
ssize_t plen_to_tcp;
|
||||
#endif
|
||||
struct frec *frec_list;
|
||||
struct frec_src *free_frec_src;
|
||||
@@ -1461,8 +1473,9 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
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 *nons, int *nsec_ttl, int *validate_count);
|
||||
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);
|
||||
size_t filter_rrsigs(struct dns_header *header, size_t plen);
|
||||
int setup_timestamp(void);
|
||||
@@ -1477,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);
|
||||
@@ -1490,8 +1504,18 @@ unsigned char *do_rfc1035_name(unsigned char *p, char *sval, 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);
|
||||
void *whine_malloc(size_t size);
|
||||
void *whine_realloc(void *ptr, size_t size);
|
||||
#define malloc_log(x, y) malloc_log_real(__func__, __LINE__, (x), (y))
|
||||
#define whine_malloc(x) whine_malloc_real(__func__, __LINE__, (x))
|
||||
#define whine_realloc(x, y) whine_realloc_real(NULL, __func__, __LINE__, (x), (y))
|
||||
#define expand_buf(x, y) expand_buf_real(__func__, __LINE__, (x), (y))
|
||||
#define expand_workspace(x, y, z) expand_workspace_real(__func__, __LINE__, (x), (y), (z))
|
||||
#define free(x) free_real(__func__, __LINE__, (x))
|
||||
void malloc_log_real(const char *func, unsigned int line, void *mem, size_t size);
|
||||
void free_real(const char *func, unsigned int line, void *ptr);
|
||||
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 sa_len(union mysockaddr *addr);
|
||||
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2);
|
||||
int sockaddr_isnull(const union mysockaddr *s);
|
||||
@@ -1513,9 +1537,9 @@ 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);
|
||||
int expand_buf(struct iovec *iov, size_t size);
|
||||
char *print_mac(char *buff, 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);
|
||||
int wildcard_match(const char* wildcard, const char* match);
|
||||
int wildcard_matchn(const char* wildcard, const char* match, int num);
|
||||
@@ -1558,8 +1582,8 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *n,
|
||||
int class, char *name, struct server *server,
|
||||
int *keycount, int *validatecount);
|
||||
#endif
|
||||
unsigned char *tcp_request(int confd, time_t now,
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
||||
void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
||||
void server_gone(struct server *server);
|
||||
int send_from(int fd, int nowild, char *packet, size_t len,
|
||||
union mysockaddr *to, union all_addr *source,
|
||||
@@ -1890,7 +1914,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
|
||||
@@ -1904,7 +1928,6 @@ int do_poll(int timeout);
|
||||
/* rrfilter.c */
|
||||
size_t rrfilter(struct dns_header *header, size_t *plen, int mode);
|
||||
short *rrfilter_desc(int type);
|
||||
int expand_workspace(unsigned char ***wkspc, int *szp, int new);
|
||||
int to_wire(char *name);
|
||||
void from_wire(char *name);
|
||||
/* modes. */
|
||||
|
||||
+152
-150
@@ -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
|
||||
@@ -370,45 +370,42 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
|
||||
|
||||
GETSHORT(type_covered, p);
|
||||
p += 16; /* algo, labels, orig_ttl, sig_expiration, sig_inception, key_tag */
|
||||
|
||||
if (gotkey)
|
||||
|
||||
if (type_covered == type)
|
||||
{
|
||||
/* If there's more than one SIG, ensure they all have same keyname */
|
||||
if (extract_name(header, plen, &p, keyname, EXTR_NAME_COMPARE, 0) != 1)
|
||||
if (!extract_name(header, plen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
gotkey = 1;
|
||||
|
||||
if (!extract_name(header, plen, &p, keyname, EXTR_NAME_EXTRACT, 0))
|
||||
return 0;
|
||||
|
||||
|
||||
/* RFC 4035 5.3.1 says that the Signer's Name field MUST equal
|
||||
the name of the zone containing the RRset. We can't tell that
|
||||
for certain, but we can check that the RRset name is equal to
|
||||
or encloses the signers name, which should be enough to stop
|
||||
an attacker using signatures made with the key of an unrelated
|
||||
zone he controls. Note that the root key is always allowed. */
|
||||
if (*keyname != 0)
|
||||
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)
|
||||
{
|
||||
char *name_start;
|
||||
for (name_start = name; !hostname_isequal(name_start, keyname); )
|
||||
if ((name_start = strchr(name_start, '.')))
|
||||
name_start++; /* chop a label off and try again */
|
||||
else
|
||||
return 0;
|
||||
if (gotkey)
|
||||
{
|
||||
/* If there's more than one valid SIG, they must all have same keyname */
|
||||
if (!hostname_isequal(keyname, daemon->workspacename))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(keyname, daemon->workspacename);
|
||||
gotkey = 1;
|
||||
}
|
||||
|
||||
if (gotkey)
|
||||
{
|
||||
if (!expand_workspace(&sigs, &sig_sz, sigidx))
|
||||
return 0;
|
||||
|
||||
sigs[sigidx++] = pdata;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (type_covered == type)
|
||||
{
|
||||
if (!expand_workspace(&sigs, &sig_sz, sigidx))
|
||||
return 0;
|
||||
|
||||
sigs[sigidx++] = pdata;
|
||||
}
|
||||
|
||||
p = pdata + 6; /* restore for ADD_RDLEN */
|
||||
}
|
||||
@@ -438,8 +435,6 @@ int dec_counter(int *counter, char *message)
|
||||
/* Validate a single RRset (class, type, name) in the supplied DNS reply
|
||||
Return code:
|
||||
STAT_SECURE if it validates.
|
||||
STAT_SECURE_WILDCARD if it validates and is the result of wildcard expansion.
|
||||
(In this case *wildcard_out points to the "body" of the wildcard within name.)
|
||||
STAT_BOGUS signature is wrong, bad packet.
|
||||
STAT_ABANDONED validation abandoned do to excess resource usage.
|
||||
STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
|
||||
@@ -455,7 +450,7 @@ int dec_counter(int *counter, char *message)
|
||||
ttl_out is the floor on TTL, based on TTL and orig_ttl and expiration of sig used to validate.
|
||||
*/
|
||||
static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, int type, int sigidx, int rrsetidx,
|
||||
char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen,
|
||||
char *name, char *keyname, int *wildcard_offset_out, struct blockdata *key, int keylen,
|
||||
int algo_in, int keytag_in, unsigned long *ttl_out, int *validate_counter)
|
||||
{
|
||||
unsigned char *p;
|
||||
@@ -463,13 +458,15 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
struct crec *crecp = NULL;
|
||||
short *rr_desc = rrfilter_desc(type);
|
||||
u32 sig_expiration, sig_inception;
|
||||
int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP;
|
||||
|
||||
unsigned long curtime = time(0);
|
||||
unsigned long curtime = time(0);
|
||||
int time_check = is_check_date(curtime);
|
||||
int failflags = DNSSEC_FAIL_NOSIG;
|
||||
|
||||
if (wildcard_out)
|
||||
*wildcard_out = NULL;
|
||||
if (sigidx != 0)
|
||||
failflags |= DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP;
|
||||
|
||||
if (wildcard_offset_out)
|
||||
*wildcard_offset_out = 0;
|
||||
|
||||
name_labels = count_labels(name); /* For 4035 5.3.2 check */
|
||||
|
||||
@@ -574,7 +571,9 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
name_start = name;
|
||||
|
||||
/* if more labels than in RRsig name, hash *.<no labels in rrsig labels field> 4035 5.3.2 */
|
||||
if (labels < name_labels)
|
||||
/* If the name is already the wildcard, we're not going to change it. */
|
||||
if (labels < name_labels &&
|
||||
!(name_labels - labels == 1 && name_start[0] == '*' && name_start[1] == '.'))
|
||||
{
|
||||
for (j = name_labels - labels; j != 0; j--)
|
||||
{
|
||||
@@ -584,11 +583,11 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
name_start++;
|
||||
}
|
||||
|
||||
if (wildcard_out)
|
||||
*wildcard_out = name_start+1;
|
||||
|
||||
if (wildcard_offset_out)
|
||||
*wildcard_offset_out = name_start - name + 1;
|
||||
|
||||
name_start--;
|
||||
*name_start = '*';
|
||||
*name_start = '*';
|
||||
}
|
||||
|
||||
wire_len = to_wire(name_start);
|
||||
@@ -657,7 +656,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]);
|
||||
@@ -687,7 +686,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
return STAT_ABANDONED;
|
||||
|
||||
if (verify(crecp->addr.key.keydata, crecp->addr.key.keylen, sig, sig_len, digest, hash->digest_size, algo))
|
||||
return (labels < name_labels) ? STAT_SECURE_WILDCARD : STAT_SECURE;
|
||||
return STAT_SECURE;
|
||||
|
||||
/* An attacker can waste a lot of our CPU by setting up a giant DNSKEY RRSET full of failing
|
||||
keys, all of which we have to try. Since many failing keys is not likely for
|
||||
@@ -837,7 +836,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);
|
||||
|
||||
@@ -951,10 +950,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -992,7 +988,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
char *keyname, int class, int *validate_counter)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
int qtype, qclass, rc, i, neganswer = 0, nons = 0, servfail = 0, neg_ttl = 0, found_supported = 0;
|
||||
int qtype, qclass, rc, i, neganswer = 0, prim_ok = 0, nons = 0, servfail = 0, neg_ttl = 0, found_supported = 0;
|
||||
int aclass, atype, rdlen, flags;
|
||||
unsigned long ttl;
|
||||
union all_addr a;
|
||||
@@ -1003,7 +999,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
if (RCODE(header) == SERVFAIL)
|
||||
servfail = neganswer = nons = 1;
|
||||
else
|
||||
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl, validate_counter);
|
||||
rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &prim_ok, &nons, &neg_ttl, validate_counter);
|
||||
|
||||
p = (unsigned char *)(header+1);
|
||||
if (ntohs(header->qdcount) != 1 ||
|
||||
@@ -1020,27 +1016,32 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
{
|
||||
if (STAT_ISEQUAL(rc, STAT_INSECURE))
|
||||
{
|
||||
if (option_bool(OPT_BOGUSPRIV) &&
|
||||
(flags = in_arpa_name_2_addr(name, &a)) &&
|
||||
((flags == F_IPV6 && private_net6(&a.addr6, 0)) || (flags == F_IPV4 && private_net(a.addr4, 0))))
|
||||
/* A INSECURE DS answer is OK if it's negative and there's a CNAME answer to the DS answer which is
|
||||
signed, since that's enough to prove that the DS record doesn't exist. */
|
||||
if (!neganswer || !prim_ok)
|
||||
{
|
||||
my_syslog(LOG_INFO, _("Insecure reply received for DS %s, assuming that's OK for a RFC-1918 address."), name);
|
||||
neganswer = 1;
|
||||
nons = 0; /* If we're faking a DS, fake one with an NS. */
|
||||
neg_ttl = DNSSEC_ASSUMED_DS_TTL;
|
||||
}
|
||||
else if (lookup_domain(name, F_DOMAINSRV, NULL, NULL))
|
||||
{
|
||||
my_syslog(LOG_INFO, _("Insecure reply received for DS %s, assuming non-DNSSEC domain-specific server."), name);
|
||||
neganswer = 1;
|
||||
nons = 0; /* If we're faking a DS, fake one with an NS. */
|
||||
neg_ttl = DNSSEC_ASSUMED_DS_TTL;
|
||||
}
|
||||
else
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0);
|
||||
return STAT_BOGUS | DNSSEC_FAIL_INDET;
|
||||
if (option_bool(OPT_BOGUSPRIV) &&
|
||||
(flags = in_arpa_name_2_addr(name, &a)) &&
|
||||
((flags == F_IPV6 && private_net6(&a.addr6, 0)) || (flags == F_IPV4 && private_net(a.addr4, 0))))
|
||||
{
|
||||
my_syslog(LOG_INFO, _("insecure reply received for DS %s, assuming that's OK for a RFC-1918 address"), name);
|
||||
neganswer = 1;
|
||||
nons = 0; /* If we're faking a DS, fake one with an NS. */
|
||||
neg_ttl = DNSSEC_ASSUMED_DS_TTL;
|
||||
}
|
||||
else if (lookup_domain(name, F_DOMAINSRV, NULL, NULL))
|
||||
{
|
||||
my_syslog(LOG_INFO, _("insecure reply received for DS %s, assuming non-DNSSEC domain-specific server"), name);
|
||||
neganswer = 1;
|
||||
nons = 0; /* If we're faking a DS, fake one with an NS. */
|
||||
neg_ttl = DNSSEC_ASSUMED_DS_TTL;
|
||||
}
|
||||
else
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0);
|
||||
return STAT_BOGUS | DNSSEC_FAIL_INDET;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1097,7 +1098,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)))
|
||||
@@ -1137,44 +1138,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)
|
||||
{
|
||||
@@ -1381,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);
|
||||
@@ -1419,7 +1423,10 @@ static int base32_decode(char *in, unsigned char *out)
|
||||
oc |= 1;
|
||||
mask = mask >> 1;
|
||||
if (((++on) & 7) == 0)
|
||||
*p++ = oc;
|
||||
{
|
||||
*p++ = oc;
|
||||
oc = 0;
|
||||
}
|
||||
oc = oc << 1;
|
||||
}
|
||||
}
|
||||
@@ -1545,11 +1552,11 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
|
||||
|
||||
/* returns 0 on success, or DNSSEC_FAIL_* value on failure. */
|
||||
static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count, char *workspace1,
|
||||
char *workspace2, char *name, int type, char *wildname, int *nons, int *validate_counter)
|
||||
char *workspace2, char *name, int type, int wild_offset, int *nons, int *validate_counter)
|
||||
{
|
||||
unsigned char *salt, *p, *digest;
|
||||
int digest_len, i, iterations, salt_len, base32_len, algo = 0;
|
||||
struct nettle_hash const *hash;
|
||||
struct nettle_hash const *hash = NULL;
|
||||
char *closest_encloser, *next_closest, *wildcard;
|
||||
|
||||
if (nons)
|
||||
@@ -1643,9 +1650,9 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
|
||||
if (*closest_encloser == '.')
|
||||
closest_encloser++;
|
||||
|
||||
if (wildname && hostname_isequal(closest_encloser, wildname))
|
||||
if (wild_offset != 0 && name - closest_encloser == wild_offset)
|
||||
break;
|
||||
|
||||
|
||||
if (dec_counter(validate_counter, NULL))
|
||||
return DNSSEC_FAIL_WORK;
|
||||
|
||||
@@ -1687,7 +1694,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
/* Finally, check that there's no seat of wildcard synthesis */
|
||||
if (!wildname)
|
||||
if (wild_offset == 0)
|
||||
{
|
||||
if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
@@ -1710,7 +1717,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
|
||||
|
||||
/* returns 0 on success, or DNSSEC_FAIL_* value on failure. */
|
||||
static int prove_non_existence(struct dns_header *header, size_t plen, char *keyname, char *name, int qtype, int qclass,
|
||||
char *wildname, int *nons, int *nsec_ttl, int *validate_counter)
|
||||
int wild_offset, int *nons, int *nsec_ttl, int *validate_counter)
|
||||
{
|
||||
static unsigned char **nsecset = NULL, **rrsig_labels = NULL;
|
||||
static int nsecset_sz = 0, rrsig_labels_sz = 0;
|
||||
@@ -1858,7 +1865,7 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
|
||||
if (type_found == T_NSEC)
|
||||
return prove_non_existence_nsec(header, plen, nsecset, rrsig_labels, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
|
||||
else if (type_found == T_NSEC3)
|
||||
return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons, validate_counter);
|
||||
return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wild_offset, nons, validate_counter);
|
||||
else
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
}
|
||||
@@ -1965,7 +1972,7 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
|
||||
if the nons argument is non-NULL.
|
||||
*/
|
||||
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 *nons, int *nsec_ttl, int *validate_counter)
|
||||
int *class, int check_unsigned, int *neganswer, int *prim_ok, int *nons, int *nsec_ttl, int *validate_counter)
|
||||
{
|
||||
static unsigned char **targets = NULL;
|
||||
static int target_sz = 0;
|
||||
@@ -1994,8 +2001,11 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
|
||||
if (neganswer)
|
||||
*neganswer = 0;
|
||||
|
||||
if (RCODE(header) == SERVFAIL)
|
||||
return STAT_BOGUS | DNSSEC_FAIL_UPSTREAM;
|
||||
|
||||
if (RCODE(header) == SERVFAIL || ntohs(header->qdcount) != 1)
|
||||
if (ntohs(header->qdcount) != 1)
|
||||
return STAT_BOGUS;
|
||||
|
||||
if (RCODE(header) != NXDOMAIN && RCODE(header) != NOERROR)
|
||||
@@ -2103,8 +2113,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(name, daemon->cname) == 1 &&
|
||||
hostname_issubdomain(keyname, daemon->workspacename) == 1 &&
|
||||
if (hostname_issubdomain(daemon->cname, name) == 1 &&
|
||||
hostname_issubdomain(daemon->workspacename, keyname) == 1 &&
|
||||
name_prefix_len == strlen(daemon->workspacename) - strlen(keyname))
|
||||
{
|
||||
char save = daemon->cname[name_prefix_len];
|
||||
@@ -2183,47 +2193,32 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
else
|
||||
{
|
||||
/* Not done, validate now */
|
||||
int sigcnt, rrcnt;
|
||||
char *wildname;
|
||||
int sigcnt, rrcnt, wild_offset;
|
||||
|
||||
if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt))
|
||||
return STAT_BOGUS;
|
||||
|
||||
/* No signatures for RRset. We can be configured to assume this is OK and return an INSECURE result. */
|
||||
if (sigcnt == 0)
|
||||
{
|
||||
/* NSEC and NSEC3 records must be signed. We make this assumption elsewhere. */
|
||||
if (type1 == T_NSEC || type1 == T_NSEC3)
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NOSIG;
|
||||
else if (nons && i >= ntohs(header->ancount))
|
||||
/* If we're validating a DS reply, rather than looking for the value of AD bit,
|
||||
we only care that NSEC and NSEC3 RRs in the auth section are signed.
|
||||
Return SECURE even if others (SOA....) are not. */
|
||||
rc = STAT_SECURE;
|
||||
else
|
||||
{
|
||||
/* unsigned RRsets in auth section are not BOGUS, but do make reply insecure. */
|
||||
if (check_unsigned && i < ntohs(header->ancount))
|
||||
{
|
||||
rc = zone_status(name, class1, keyname, now);
|
||||
if (STAT_ISEQUAL(rc, STAT_SECURE))
|
||||
rc = STAT_BOGUS | DNSSEC_FAIL_NOSIG;
|
||||
|
||||
if (class)
|
||||
*class = class1; /* Class for NEED_DS or NEED_KEY */
|
||||
}
|
||||
else
|
||||
rc = STAT_INSECURE;
|
||||
|
||||
if (!STAT_ISEQUAL(rc, STAT_INSECURE))
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
/* NSEC and NSEC3 records must be signed. We make this assumption elsewhere. */
|
||||
if (sigcnt == 0 && (type1 == T_NSEC || type1 == T_NSEC3))
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NOSIG;
|
||||
else if (sigcnt == 0 && nons && i >= ntohs(header->ancount))
|
||||
/* If we're validating a DS reply, rather than looking for the value of AD bit,
|
||||
we only care that NSEC and NSEC3 RRs in the auth section are signed.
|
||||
Return SECURE even if others (SOA....) are not. */
|
||||
rc = STAT_SECURE;
|
||||
else if (sigcnt == 0 && (!check_unsigned || i >= ntohs(header->ancount)))
|
||||
/* unsigned RRsets in auth section are not BOGUS, but do make reply insecure. */
|
||||
rc = STAT_INSECURE;
|
||||
else
|
||||
{
|
||||
/* explore_rrset() gives us key name from sigs in keyname.
|
||||
/* explore_rrset() gives us zone name from sigs in keyname, if
|
||||
it didn't find a key, use the name we're validating.
|
||||
Can't overwrite name here. */
|
||||
strcpy(daemon->workspacename, keyname);
|
||||
if (sigcnt == 0)
|
||||
strcpy(daemon->workspacename, name);
|
||||
else
|
||||
strcpy(daemon->workspacename, keyname);
|
||||
|
||||
rc = zone_status(daemon->workspacename, class1, keyname, now);
|
||||
|
||||
if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS))
|
||||
@@ -2233,12 +2228,12 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Zone is insecure, don't need to validate RRset */
|
||||
/* If zone is insecure, don't need to validate RRset, and rc remains as STAT_INSECURE*/
|
||||
if (STAT_ISEQUAL(rc, STAT_SECURE))
|
||||
{
|
||||
unsigned long sig_ttl;
|
||||
rc = validate_rrset(now, header, plen, class1, type1, sigcnt,
|
||||
rrcnt, name, keyname, &wildname, NULL, 0, 0, 0, &sig_ttl, validate_counter);
|
||||
rrcnt, name, keyname, &wild_offset, NULL, 0, 0, 0, &sig_ttl, validate_counter);
|
||||
|
||||
if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS) || STAT_ISEQUAL(rc, STAT_ABANDONED))
|
||||
{
|
||||
@@ -2247,7 +2242,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* rc is now STAT_SECURE or STAT_SECURE_WILDCARD */
|
||||
/* rc is now STAT_SECURE */
|
||||
|
||||
/* Note that RR is validated */
|
||||
daemon->rr_status[i] = sig_ttl;
|
||||
@@ -2273,8 +2268,8 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
Note that we may not yet have validated the NSEC/NSEC3 RRsets.
|
||||
That's not a problem since if the RRsets later fail
|
||||
we'll return BOGUS then. */
|
||||
if (STAT_ISEQUAL(rc, STAT_SECURE_WILDCARD) &&
|
||||
((rc_nsec = prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL, NULL, validate_counter))) != 0)
|
||||
if (wild_offset != 0 &&
|
||||
((rc_nsec = prove_non_existence(header, plen, keyname, name, type1, class1, wild_offset, NULL, NULL, validate_counter))) != 0)
|
||||
return (rc_nsec & DNSSEC_FAIL_WORK) ? STAT_ABANDONED : (STAT_BOGUS | rc_nsec);
|
||||
|
||||
rc = STAT_SECURE;
|
||||
@@ -2286,6 +2281,20 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
secure = STAT_INSECURE;
|
||||
}
|
||||
|
||||
|
||||
/* For a DS record, we are interested also in if the answer to the DS query was
|
||||
a CNAME RRset which validated. That's proof that the DS doesn't exist,
|
||||
even if it's a CNAME which is not signed, and therefore we have no proof
|
||||
of what it actually _is_. This return tells us that the answer to
|
||||
primary query is secure, even is the whole answer is insecure, because
|
||||
something down the CNAME list doesn't validate or doesn't exist.
|
||||
Note that prim_ok is only valid when neganswer is true, ie either
|
||||
the answer is the requested record or it's a CNAME that ends
|
||||
in a missing answer or an unsigned zone.
|
||||
*/
|
||||
if (prim_ok)
|
||||
*prim_ok = !targets[0];
|
||||
|
||||
/* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
|
||||
for (j = 0; j <targetidx; j++)
|
||||
if ((p2 = targets[j]))
|
||||
@@ -2296,20 +2305,12 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
if (!extract_name(header, plen, &p2, name, EXTR_NAME_EXTRACT, 10))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
/* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */
|
||||
|
||||
/* For anything other than a DS record, this situation is OK if either
|
||||
the answer is in an unsigned zone, or there's NSEC records.
|
||||
For a DS record, we return INSECURE, which almost always turns
|
||||
into BOGUS in the caller. */
|
||||
if ((rc_nsec = prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons, nsec_ttl, validate_counter)) != 0)
|
||||
/* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype)
|
||||
This situation is OK if either the answer is in an unsigned zone, or there's NSEC records. */
|
||||
if ((rc_nsec = prove_non_existence(header, plen, keyname, name, qtype, qclass, 0, nons, nsec_ttl, validate_counter)) != 0)
|
||||
{
|
||||
if (rc_nsec & DNSSEC_FAIL_WORK)
|
||||
return STAT_ABANDONED;
|
||||
|
||||
/* Empty DS without NSECS */
|
||||
if (qtype == T_DS)
|
||||
return STAT_INSECURE;
|
||||
|
||||
if ((rc_nsec & (DNSSEC_FAIL_NONSEC | DNSSEC_FAIL_NSEC3_ITERS)) &&
|
||||
!STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))
|
||||
@@ -2381,8 +2382,9 @@ int errflags_to_ede(int status)
|
||||
/* We can end up with more than one flag set for some errors,
|
||||
so this encodes a rough priority so the (eg) No sig is reported
|
||||
before no-unexpired-sig. */
|
||||
|
||||
if (status & DNSSEC_FAIL_NYV)
|
||||
if (status & DNSSEC_FAIL_UPSTREAM)
|
||||
return EDE_US_SERVFAIL;
|
||||
else if (status & DNSSEC_FAIL_NYV)
|
||||
return EDE_SIG_NYV;
|
||||
else if (status & DNSSEC_FAIL_EXP)
|
||||
return EDE_SIG_EXP;
|
||||
|
||||
+4
-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
|
||||
@@ -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. */
|
||||
|
||||
+8
-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
|
||||
@@ -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, ".", MAXDNAME - strlen(name) - 1);
|
||||
strncat(name, c->domain, MAXDNAME - strlen(name) - 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -201,16 +201,16 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
|
||||
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, MAXDNAME - strlen(name) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
strncat(name, ".", MAXDNAME);
|
||||
strncat(name, c->domain, MAXDNAME);
|
||||
|
||||
strncat(name, ".", MAXDNAME - strlen(name) - 1);
|
||||
strncat(name, c->domain, MAXDNAME - 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++;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-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
|
||||
@@ -103,7 +103,7 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t
|
||||
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
int optno, unsigned char *opt, size_t optlen, int set_do, int replace)
|
||||
{
|
||||
unsigned char *lenp, *datap, *p, *udp_len, *buff = NULL;
|
||||
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;
|
||||
|
||||
@@ -469,6 +469,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)
|
||||
|
||||
+173
-145
@@ -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,9 +135,9 @@ static void log_query_mysockaddr(unsigned int flags, char *name, union mysockadd
|
||||
}
|
||||
|
||||
static void server_send(struct server *server, int fd,
|
||||
const void *header, size_t plen, int flags)
|
||||
const void *header, size_t plen)
|
||||
{
|
||||
while (retry_send(sendto(fd, header, plen, flags,
|
||||
while (retry_send(sendto(fd, header, plen, 0,
|
||||
&server->addr.sa,
|
||||
sa_len(&server->addr))));
|
||||
}
|
||||
@@ -954,13 +954,14 @@ 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)
|
||||
status = dnssec_validate_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class, &orig->validate_counter);
|
||||
else
|
||||
status = dnssec_validate_reply(now, header, plen, daemon->namebuff, daemon->keyname, &forward->class,
|
||||
!option_bool(OPT_DNSSEC_IGN_NS), NULL, NULL, NULL, &orig->validate_counter);
|
||||
!option_bool(OPT_DNSSEC_IGN_NS), NULL, NULL, NULL, NULL, &orig->validate_counter);
|
||||
|
||||
if (STAT_ISEQUAL(status, STAT_ABANDONED))
|
||||
log_resource = 1;
|
||||
@@ -1074,7 +1075,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
||||
set_outgoing_mark(orig, fd);
|
||||
#endif
|
||||
|
||||
server_send(server, fd, header, nn, 0);
|
||||
server_send(server, fd, header, nn);
|
||||
server->queries++;
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet_udp(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr, fd);
|
||||
@@ -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
|
||||
@@ -1399,6 +1412,9 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
|
||||
|
||||
a.log.ede = ede;
|
||||
log_query(F_SECSTAT, domain, &a, result, 0);
|
||||
|
||||
if (ede == EDE_US_SERVFAIL)
|
||||
ede = EDE_DNSSEC_BOGUS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2015,20 +2031,21 @@ void receive_query(struct listener *listen, time_t now)
|
||||
}
|
||||
|
||||
|
||||
/* Send query in packet, qsize to a server determined by first,last,start and
|
||||
get the reply. return reply size. */
|
||||
static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, size_t qsize,
|
||||
int have_mark, unsigned int mark, struct server **servp)
|
||||
/* Send query in header, qsize to a server determined by first,last,start and
|
||||
get the reply into the buffer passed by outbuff. Return reply size. */
|
||||
static ssize_t tcp_talk(int first, int last, int start, struct dns_header *header, size_t qsize,
|
||||
struct iovec *recvbuff, int have_mark, unsigned int mark, struct server **servp)
|
||||
{
|
||||
int firstsendto = -1;
|
||||
u16 *length = (u16 *)packet;
|
||||
unsigned char *payload = &packet[2];
|
||||
struct dns_header *header = (struct dns_header *)payload;
|
||||
unsigned int rsize;
|
||||
u16 length;
|
||||
unsigned int rsize = 0;
|
||||
int class, rclass, type, rtype;
|
||||
unsigned char *p;
|
||||
struct blockdata *saved_question;
|
||||
struct timeval tv;
|
||||
struct iovec sendio[2];
|
||||
#ifdef MSG_FASTOPEN
|
||||
struct msghdr msg;
|
||||
#endif
|
||||
|
||||
(void)mark;
|
||||
(void)have_mark;
|
||||
@@ -2040,10 +2057,12 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
GETSHORT(type, p);
|
||||
GETSHORT(class, p);
|
||||
|
||||
/* Save question for retry. */
|
||||
if (!(saved_question = blockdata_alloc((char *)header, (size_t)qsize)))
|
||||
return 0;
|
||||
|
||||
length = htons(qsize);
|
||||
sendio[0].iov_base = &length;
|
||||
sendio[0].iov_len = sizeof(length);
|
||||
sendio[1].iov_base = header;
|
||||
sendio[1].iov_len = qsize;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int data_sent = 0, fatal = 0;
|
||||
@@ -2065,10 +2084,6 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
*servp = serv = daemon->serverarray[start];
|
||||
|
||||
retry:
|
||||
blockdata_retrieve(saved_question, qsize, header);
|
||||
|
||||
*length = htons(qsize);
|
||||
|
||||
if (serv->tcpfd == -1)
|
||||
{
|
||||
if ((serv->tcpfd = socket(serv->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
|
||||
@@ -2099,10 +2114,17 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
tv.tv_sec += TCP_TIMEOUT;
|
||||
setsockopt(serv->tcpfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||
#endif
|
||||
|
||||
#ifdef MSG_FASTOPEN
|
||||
server_send(serv, serv->tcpfd, packet, qsize + sizeof(u16), MSG_FASTOPEN);
|
||||
|
||||
msg.msg_name = &serv->addr.sa;
|
||||
msg.msg_namelen = sa_len(&serv->addr);
|
||||
msg.msg_iov = sendio;
|
||||
msg.msg_iovlen = 2;
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
while (retry_send(sendmsg(serv->tcpfd, &msg, MSG_FASTOPEN)));
|
||||
|
||||
if (errno == 0)
|
||||
data_sent = 1;
|
||||
else if (errno == ETIMEDOUT || errno == EHOSTUNREACH || errno == EINPROGRESS || errno == ECONNREFUSED)
|
||||
@@ -2128,12 +2150,13 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
serv->flags &= ~SERV_GOT_TCP;
|
||||
}
|
||||
|
||||
/* We us the _ONCE veriant of read_write() here because we've set a timeout on the tcp socket
|
||||
/* We use the _ONCE variant of read_write() here because we've set a timeout on the tcp socket
|
||||
and wish to abort if the whole data is not read/written within the timeout. */
|
||||
if ((!data_sent && !read_write(serv->tcpfd, (unsigned char *)packet, qsize + sizeof(u16), RW_WRITE_ONCE)) ||
|
||||
!read_write(serv->tcpfd, (unsigned char *)length, sizeof (*length), RW_READ_ONCE) ||
|
||||
!read_write(serv->tcpfd, payload, (rsize = ntohs(*length)), RW_READ_ONCE))
|
||||
{
|
||||
if ((!data_sent && !read_writev(serv->tcpfd, sendio, 2, RW_WRITE_ONCE)) ||
|
||||
!read_write(serv->tcpfd, (unsigned char *)&length, sizeof(length), RW_READ_ONCE) ||
|
||||
!expand_buf(recvbuff, (rsize = ntohs(length))) ||
|
||||
!read_write(serv->tcpfd, recvbuff->iov_base, rsize, RW_READ_ONCE))
|
||||
{
|
||||
/* We get data then EOF, reopen connection to same server,
|
||||
else try next. This avoids DoS from a server which accepts
|
||||
connections and then closes them. */
|
||||
@@ -2146,28 +2169,31 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
else
|
||||
goto failed;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If the question section of the reply doesn't match the question we sent, then
|
||||
someone might be attempting to insert bogus values into the cache by
|
||||
sending replies containing questions and bogus answers.
|
||||
Try another server, or give up */
|
||||
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);
|
||||
|
||||
/* If the question section of the reply doesn't match the question we sent, then
|
||||
someone might be attempting to insert bogus values into the cache by
|
||||
sending replies containing questions and bogus answers.
|
||||
Try another server, or give up */
|
||||
p = (unsigned char *)(header+1);
|
||||
if (extract_name(header, rsize, &p, daemon->namebuff, EXTR_NAME_COMPARE, 4) != 1)
|
||||
continue;
|
||||
GETSHORT(rtype, p);
|
||||
GETSHORT(rclass, p);
|
||||
|
||||
if (type != rtype || class != rclass)
|
||||
continue;
|
||||
if (type != rtype || class != rclass)
|
||||
continue;
|
||||
}
|
||||
|
||||
serv->flags |= SERV_GOT_TCP;
|
||||
|
||||
*servp = serv;
|
||||
blockdata_free(saved_question);
|
||||
return rsize;
|
||||
}
|
||||
|
||||
blockdata_free(saved_question);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2179,19 +2205,16 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple
|
||||
int class, char *name, struct server *server,
|
||||
int *keycount, int *validatecount)
|
||||
{
|
||||
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
|
||||
struct dns_header *new_header = (struct dns_header *)&packet[2];
|
||||
int start, first, last, new_status;
|
||||
ssize_t n = *plenp;
|
||||
int log_save = daemon->log_display_id;
|
||||
struct iovec recvbuff;
|
||||
|
||||
recvbuff.iov_len = 0;
|
||||
recvbuff.iov_base = NULL;
|
||||
|
||||
*plenp = 0;
|
||||
|
||||
if (!packet)
|
||||
return STAT_ABANDONED;
|
||||
|
||||
memcpy(new_header, header, n);
|
||||
|
||||
/* Set TCP flag in logs. */
|
||||
daemon->log_display_id = -daemon->log_display_id;
|
||||
|
||||
@@ -2209,10 +2232,11 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple
|
||||
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, name, &server->addr,
|
||||
STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0);
|
||||
|
||||
if ((n = tcp_talk(first, last, start, packet, n, 0, 0, &server)) == 0)
|
||||
if ((n = tcp_talk(first, last, start, header, n, &recvbuff, 0, 0, &server)) == 0)
|
||||
new_status = STAT_ABANDONED;
|
||||
else
|
||||
{
|
||||
struct dns_header *new_header = (struct dns_header *)recvbuff.iov_base;
|
||||
new_status = tcp_key_recurse(now, status, new_header, n, class, daemon->namebuff, daemon->keyname, server, 0, 0, keycount, validatecount);
|
||||
|
||||
if (STAT_ISEQUAL(status, STAT_OK))
|
||||
@@ -2223,7 +2247,7 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple
|
||||
|
||||
if (n >= daemon->edns_pktsz)
|
||||
{
|
||||
/* still too bIg, strip optional sections and try again. */
|
||||
/* still too big, strip optional sections and try again. */
|
||||
new_header->nscount = htons(0);
|
||||
new_header->arcount = htons(0);
|
||||
n = resize_packet(new_header, n, NULL, 0);
|
||||
@@ -2237,6 +2261,10 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple
|
||||
}
|
||||
}
|
||||
|
||||
/* we have succeeded and are no longer blocked talking
|
||||
on a TCP connection, so if the watchdog alarm goes off,
|
||||
ignore it. */
|
||||
daemon->forward_to_tcp = NULL;
|
||||
/* return the stripped or truncated reply. */
|
||||
memcpy(header, new_header, n);
|
||||
*plenp = n;
|
||||
@@ -2245,7 +2273,7 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple
|
||||
}
|
||||
|
||||
daemon->log_display_id = log_save;
|
||||
free(packet);
|
||||
free(recvbuff.iov_base);
|
||||
return new_status;
|
||||
}
|
||||
|
||||
@@ -2255,9 +2283,12 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
int have_mark, unsigned int mark, int *keycount, int *validatecount)
|
||||
{
|
||||
int first, last, start, new_status;
|
||||
unsigned char *packet = NULL;
|
||||
struct dns_header *new_header = NULL;
|
||||
struct iovec new_packet;
|
||||
struct dns_header *query_header = NULL, *new_header;
|
||||
|
||||
new_packet.iov_base = NULL;
|
||||
new_packet.iov_len = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
size_t m;
|
||||
@@ -2270,7 +2301,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
new_status = dnssec_validate_ds(now, header, n, name, keyname, class, validatecount);
|
||||
else
|
||||
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class,
|
||||
!option_bool(OPT_DNSSEC_IGN_NS), NULL, NULL, NULL, validatecount);
|
||||
!option_bool(OPT_DNSSEC_IGN_NS), NULL, NULL, NULL, NULL, validatecount);
|
||||
|
||||
if (!STAT_ISEQUAL(new_status, STAT_NEED_DS) && !STAT_ISEQUAL(new_status, STAT_NEED_KEY) && !STAT_ISEQUAL(new_status, STAT_ABANDONED))
|
||||
break;
|
||||
@@ -2291,34 +2322,21 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
}
|
||||
|
||||
/* Can't validate because we need a key/DS whose name now in keyname.
|
||||
Make query for same, and recurse to validate */
|
||||
if (!packet)
|
||||
{
|
||||
packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
|
||||
new_header = (struct dns_header *)&packet[2];
|
||||
}
|
||||
|
||||
if (!packet)
|
||||
{
|
||||
new_status = STAT_ABANDONED;
|
||||
break;
|
||||
}
|
||||
|
||||
m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, 0,
|
||||
Make query for same in UDP packet buffer, recurse to validate*/
|
||||
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,
|
||||
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)
|
||||
if ((start = dnssec_server(server, keyname, STAT_ISEQUAL(new_status, STAT_NEED_DS), &first, &last)) == -1 ||
|
||||
(m = tcp_talk(first, last, start, query_header, m, &new_packet, have_mark, mark, &server)) == 0)
|
||||
{
|
||||
new_status = STAT_ABANDONED;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((m = tcp_talk(first, last, start, packet, m, have_mark, mark, &server)) == 0)
|
||||
{
|
||||
new_status = STAT_ABANDONED;
|
||||
break;
|
||||
}
|
||||
|
||||
new_header = new_packet.iov_base;
|
||||
log_save = daemon->log_display_id;
|
||||
daemon->log_display_id = -(++daemon->log_id);
|
||||
|
||||
@@ -2335,8 +2353,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
break;
|
||||
}
|
||||
|
||||
if (packet)
|
||||
free(packet);
|
||||
free(new_packet.iov_base);
|
||||
|
||||
return new_status;
|
||||
}
|
||||
@@ -2346,11 +2363,11 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
/* The daemon forks before calling this: it should deal with one connection,
|
||||
blocking as necessary, and then return. Note, need to be a bit careful
|
||||
about resources for debug mode, when the fork is suppressed: that's
|
||||
done by the caller. */
|
||||
unsigned char *tcp_request(int confd, time_t now,
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns)
|
||||
done by the caller, which also frees bigbuff. */
|
||||
void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns)
|
||||
{
|
||||
size_t size = 0, saved_size = 0;
|
||||
size_t size = 0;
|
||||
int norebind = 0;
|
||||
#ifdef HAVE_CONNTRACK
|
||||
int allowed = 1;
|
||||
@@ -2359,16 +2376,10 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
int local_auth = 0;
|
||||
#endif
|
||||
int checking_disabled, do_bit = 0, ad_reqd = 0, have_pseudoheader = 0;
|
||||
struct blockdata *saved_question = NULL;
|
||||
unsigned short qtype;
|
||||
unsigned int gotname = 0;
|
||||
/* Max TCP packet + slop + size */
|
||||
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
|
||||
unsigned char *payload = &packet[2];
|
||||
u16 tcp_len;
|
||||
/* largest field in header is 16-bits, so this is still sufficiently aligned */
|
||||
struct dns_header *header = (struct dns_header *)payload;
|
||||
u16 *length = (u16 *)packet;
|
||||
u16 tcp_len, out_len;
|
||||
struct dns_header *header, *out_header;
|
||||
struct server *serv;
|
||||
struct in_addr dst_addr_4;
|
||||
union mysockaddr peer_addr;
|
||||
@@ -2378,9 +2389,13 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
unsigned int mark = 0;
|
||||
int have_mark = 0;
|
||||
int first, last, filtered, do_stale = 0;
|
||||
|
||||
if (!packet || getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
|
||||
return packet;
|
||||
struct iovec out_iov[2];
|
||||
|
||||
bigbuff->iov_base = NULL;
|
||||
bigbuff->iov_len = 0;
|
||||
|
||||
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
/* Get connection mark of incoming query to set on outgoing connections. */
|
||||
@@ -2401,7 +2416,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
if (option_bool(OPT_LOCAL_SERVICE))
|
||||
{
|
||||
struct addrlist *addr;
|
||||
|
||||
|
||||
if (peer_addr.sa.sa_family == AF_INET6)
|
||||
{
|
||||
for (addr = daemon->interface_addrs; addr; addr = addr->next)
|
||||
@@ -2424,7 +2439,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
{
|
||||
prettyprint_addr(&peer_addr, daemon->addrbuff);
|
||||
my_syslog(LOG_WARNING, _("ignoring query from non-local network %s"), daemon->addrbuff);
|
||||
return packet;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2442,18 +2457,35 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
if (query_count >= TCP_MAX_QUERIES)
|
||||
break;
|
||||
|
||||
/* Now get the query into the normal UDP packet buffer.
|
||||
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 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)) ||
|
||||
!read_write(confd, payload, size, 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;
|
||||
|
||||
/* Clear buffer beyond request to avoid risk of
|
||||
information disclosure. */
|
||||
memset(payload + size, 0, 65536 - size);
|
||||
/* Make sure we have a buffer big enough for the largest answer. */
|
||||
expand_buf(bigbuff, 65536 + MAXDNAME + RRFIXEDSZ);
|
||||
out_header = bigbuff->iov_base;
|
||||
|
||||
/* Add edns0 pheader to query */
|
||||
size = add_edns0_config(header, size, ((unsigned char *) header) + 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
|
||||
@@ -2472,9 +2504,6 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
ede = EDE_INVALID_DATA;
|
||||
else
|
||||
{
|
||||
if (saved_question)
|
||||
blockdata_free(saved_question);
|
||||
|
||||
do_bit = 0;
|
||||
|
||||
if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL))
|
||||
@@ -2489,10 +2518,6 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
do_bit = 1; /* do bit */
|
||||
}
|
||||
|
||||
size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &cacheable);
|
||||
saved_question = blockdata_alloc((char *)header, (size_t)size);
|
||||
saved_size = size;
|
||||
|
||||
log_query_mysockaddr((auth_dns ? F_NOERR | F_AUTH : 0) | F_QUERY | F_FORWARD, daemon->namebuff,
|
||||
&peer_addr, NULL, qtype);
|
||||
|
||||
@@ -2545,15 +2570,15 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
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
|
||||
else if (auth_dns)
|
||||
m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr, local_auth);
|
||||
m = answer_auth(out_header, ((char *) out_header) + 65536, (size_t)size, now, &peer_addr, local_auth);
|
||||
#endif
|
||||
else
|
||||
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
|
||||
m = answer_request(out_header, ((char *) out_header) + 65536, (size_t)size,
|
||||
dst_addr_4, netmask, now, ad_reqd, do_bit, !cacheable, &stale, &filtered);
|
||||
}
|
||||
}
|
||||
@@ -2561,15 +2586,12 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
/* Do this by steam now we're not in the select() loop */
|
||||
check_log_writer(1);
|
||||
|
||||
if (m == 0 && ede == EDE_UNSET && saved_question)
|
||||
if (!flags && m == 0 && ede == EDE_UNSET)
|
||||
{
|
||||
struct server *master;
|
||||
int start;
|
||||
int no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
|
||||
|
||||
blockdata_retrieve(saved_question, (size_t)saved_size, header);
|
||||
size = saved_size;
|
||||
|
||||
/* save state of "cd" flag in query */
|
||||
checking_disabled = header->hb4 & HB4_CD;
|
||||
|
||||
@@ -2598,7 +2620,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
size = add_do_bit(header, size, ((unsigned char *) header) + 65536);
|
||||
size = add_do_bit(header, size, ((unsigned char *) header) + 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. */
|
||||
@@ -2608,12 +2630,14 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
#endif
|
||||
|
||||
/* Loop round available servers until we succeed in connecting to one. */
|
||||
if ((m = tcp_talk(first, last, start, packet, size, have_mark, mark, &serv)) == 0)
|
||||
if ((m = tcp_talk(first, last, start, header, size, bigbuff, have_mark, mark, &serv)) == 0)
|
||||
ede = EDE_NETERR;
|
||||
else
|
||||
{
|
||||
/* just in case tcp_talk() expanded buffer - should never happen */
|
||||
out_header = bigbuff->iov_base;
|
||||
/* get query name again for logging - may have been overwritten */
|
||||
if (!extract_name(header, (unsigned int)size, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
|
||||
if (!extract_name(out_header, (unsigned int)size, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
|
||||
strcpy(daemon->namebuff, "query");
|
||||
log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff, &serv->addr, NULL, 0);
|
||||
|
||||
@@ -2629,7 +2653,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
{
|
||||
int keycount = daemon->limit[LIMIT_WORK]; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
|
||||
int validatecount = daemon->limit[LIMIT_CRYPTO];
|
||||
int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname,
|
||||
/* tcp_key_recurse() may overwrite packetbuf, and thuse *header is now invalid */
|
||||
int status = tcp_key_recurse(now, STAT_OK, out_header, m, 0, daemon->namebuff, daemon->keyname,
|
||||
serv, have_mark, mark, &keycount, &validatecount);
|
||||
char *result, *domain = "result";
|
||||
|
||||
@@ -2655,12 +2680,14 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
no_cache_dnssec = 1;
|
||||
bogusanswer = 1;
|
||||
|
||||
if (extract_name(header, m, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
|
||||
if (extract_name(out_header, m, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
|
||||
domain = daemon->namebuff;
|
||||
}
|
||||
|
||||
a.log.ede = ede;
|
||||
log_query(F_SECSTAT, domain, &a, result, 0);
|
||||
if (ede == EDE_US_SERVFAIL)
|
||||
ede = EDE_DNSSEC_BOGUS;
|
||||
|
||||
if ((daemon->limit[LIMIT_CRYPTO] - validatecount) > (int)daemon->metrics[METRIC_CRYPTO_HWM])
|
||||
daemon->metrics[METRIC_CRYPTO_HWM] = daemon->limit[LIMIT_CRYPTO] - validatecount;
|
||||
@@ -2677,18 +2704,18 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
|
||||
/* restore CD bit to the value in the query */
|
||||
if (checking_disabled)
|
||||
header->hb4 |= HB4_CD;
|
||||
out_header->hb4 |= HB4_CD;
|
||||
else
|
||||
header->hb4 &= ~HB4_CD;
|
||||
out_header->hb4 &= ~HB4_CD;
|
||||
|
||||
/* Never cache answers which are contingent on the source or MAC address EDSN0 option,
|
||||
since the cache is ignorant of such things. */
|
||||
if (!cacheable)
|
||||
no_cache_dnssec = 1;
|
||||
|
||||
m = process_reply(header, now, serv, (unsigned int)m,
|
||||
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 *)header) + 65536, ede);
|
||||
ad_reqd, do_bit, !have_pseudoheader, &peer_addr, ((unsigned char *)out_header) + 65536, ede);
|
||||
|
||||
/* process_reply() adds pheader itself */
|
||||
have_pseudoheader = 0;
|
||||
@@ -2703,8 +2730,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
/* In case of local answer or no connections made. */
|
||||
if (m == 0)
|
||||
{
|
||||
if (!(m = make_local_answer(flags, gotname, size, header, daemon->namebuff,
|
||||
((char *) header) + 65536, first, last, ede)))
|
||||
if (!(m = make_local_answer(flags, gotname, size, out_header, daemon->namebuff,
|
||||
((char *) out_header) + 65536, first, last, ede)))
|
||||
break;
|
||||
}
|
||||
else if (ede == EDE_UNSET)
|
||||
@@ -2720,14 +2747,12 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
u16 swap = htons((u16)ede);
|
||||
|
||||
if (ede != EDE_UNSET)
|
||||
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||
m = add_pseudoheader(out_header, m, ((unsigned char *) out_header) + 65536, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||
else
|
||||
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, 0, NULL, 0, do_bit, 0);
|
||||
m = add_pseudoheader(out_header, m, ((unsigned char *) out_header) + 65536, 0, NULL, 0, do_bit, 0);
|
||||
}
|
||||
|
||||
check_log_writer(1);
|
||||
|
||||
*length = htons(m);
|
||||
check_log_writer(1);
|
||||
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
#ifdef HAVE_AUTH
|
||||
@@ -2737,7 +2762,13 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
report_addresses(header, m, mark);
|
||||
#endif
|
||||
|
||||
if (!read_write(confd, packet, m + sizeof(u16), RW_WRITE))
|
||||
/* use scatter-gather IO so that length doesn't end up in separate packet. */
|
||||
out_len = htons(m);
|
||||
out_iov[0].iov_len = sizeof(out_len);
|
||||
out_iov[0].iov_base = &out_len;
|
||||
out_iov[1].iov_len = m;
|
||||
out_iov[1].iov_base = bigbuff->iov_base;
|
||||
if (!read_writev(confd, out_iov, 2, RW_WRITE))
|
||||
break;
|
||||
|
||||
/* If we answered with stale data, this process will now try and get fresh data into
|
||||
@@ -2753,18 +2784,15 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
daemon->log_source_addr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we ran once to get fresh data, confd is already closed. */
|
||||
|
||||
/* If we ran once to get fresh data, confd is already closed. */
|
||||
if (!do_stale)
|
||||
{
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
}
|
||||
|
||||
blockdata_free(saved_question);
|
||||
|
||||
check_log_writer(1);
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
/* return a UDP socket bound to a random port, have to cope with straying into
|
||||
@@ -3265,7 +3293,7 @@ void resend_query(void)
|
||||
{
|
||||
if (daemon->srv_save)
|
||||
server_send(daemon->srv_save, daemon->fd_save,
|
||||
daemon->packet, daemon->packet_len, 0);
|
||||
daemon->packet, daemon->packet_len);
|
||||
}
|
||||
|
||||
/* A server record is going away, remove references to it */
|
||||
|
||||
+4
-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
|
||||
@@ -740,9 +740,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
|
||||
|
||||
+11
-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
|
||||
@@ -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
|
||||
|
||||
+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
|
||||
|
||||
+44
-33
@@ -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,6 +23,9 @@ static volatile int mem_recover = 0;
|
||||
static jmp_buf mem_jmp;
|
||||
static int one_file(char *file, int hard_opt);
|
||||
|
||||
static void *opt_malloc_real(const char *func, unsigned int line, size_t size);
|
||||
#define opt_malloc(x) opt_malloc_real(__func__, __LINE__, (x))
|
||||
|
||||
/* Solaris headers don't have facility names. */
|
||||
#ifdef HAVE_SOLARIS_NETWORK
|
||||
static const struct {
|
||||
@@ -197,6 +200,7 @@ struct myoption {
|
||||
#define LOPT_DO_ENCODE 388
|
||||
#define LOPT_LEASEQUERY 389
|
||||
#define LOPT_SPLIT_RELAY 390
|
||||
#define LOPT_LOG_MALLOC 391
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -398,6 +402,7 @@ static const struct myoption opts[] =
|
||||
{ "no-ident", 0, 0, LOPT_NO_IDENT },
|
||||
{ "max-tcp-connections", 1, 0, LOPT_MAX_PROCS },
|
||||
{ "leasequery", 2, 0, LOPT_LEASEQUERY },
|
||||
{ "log-malloc", 0, 0, LOPT_LOG_MALLOC },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
@@ -606,6 +611,7 @@ static struct {
|
||||
{ LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL },
|
||||
{ LOPT_CACHE_RR, ARG_DUP, "<RR-type>", gettext_noop("Cache this DNS resource record type."), NULL },
|
||||
{ LOPT_MAX_PROCS, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent tcp connections."), NULL },
|
||||
{ LOPT_LOG_MALLOC, OPT_LOG_MALLOC, NULL, gettext_noop("Log memory allocation for debugging."), NULL },
|
||||
{ 0, 0, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
@@ -652,13 +658,13 @@ static void unhide_metas(char *cp)
|
||||
*cp = unhide_meta(*cp);
|
||||
}
|
||||
|
||||
static void *opt_malloc(size_t size)
|
||||
static void *opt_malloc_real(const char *func, unsigned int line, size_t size)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
if (mem_recover)
|
||||
{
|
||||
ret = whine_malloc(size);
|
||||
ret = whine_malloc_real(func, line, size);
|
||||
if (!ret)
|
||||
longjmp(mem_jmp, 1);
|
||||
}
|
||||
@@ -3463,6 +3469,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
}
|
||||
else if (strcmp(arg, "auth") == 0)
|
||||
set_option_bool(OPT_AUTH_LOG);
|
||||
else if (strcmp(arg, "only_failed") == 0)
|
||||
set_option_bool(OPT_LOG_ONLY_FAILED);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -4572,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)
|
||||
@@ -4627,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;
|
||||
}
|
||||
|
||||
@@ -4950,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"));
|
||||
|
||||
@@ -5729,7 +5740,7 @@ struct hostsfile *expand_filelist(struct hostsfile *list)
|
||||
struct dirent **namelist;
|
||||
|
||||
/* find largest used index */
|
||||
for (i = SRC_AH, ah = list; ah; ah = ah->next)
|
||||
for (last = NULL, i = SRC_AH, ah = list; ah; ah = ah->next)
|
||||
{
|
||||
last = ah;
|
||||
|
||||
|
||||
+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
|
||||
|
||||
+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
|
||||
@@ -317,7 +317,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 +328,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 */
|
||||
@@ -1427,6 +1430,11 @@ int check_for_ignored_address(struct dns_header *header, size_t qlen)
|
||||
return check_bad_address(header, qlen, daemon->ignore_addr, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Nameoffset > 0 means that the name of the new record already exists at the given offset,
|
||||
so use a "jump" to that.
|
||||
Nameoffset == 0 means use the first variable argument as the name of the new record.
|
||||
nameoffset < 0 means use the first variable argument as the start of the new record name,
|
||||
then "jump" to -nameoffset to complete it. */
|
||||
int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp,
|
||||
unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
|
||||
{
|
||||
|
||||
+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
|
||||
@@ -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;
|
||||
@@ -3248,7 +3248,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)
|
||||
|
||||
+114
-130
@@ -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
|
||||
@@ -21,10 +21,10 @@
|
||||
|
||||
struct state {
|
||||
unsigned char *clid;
|
||||
int multicast_dest, clid_len, ia_type, interface, hostname_auth, lease_allocate;
|
||||
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,13 @@ struct state {
|
||||
};
|
||||
|
||||
static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t sz,
|
||||
struct in6_addr *client_addr, int is_unicast, time_t now);
|
||||
static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbuff, size_t sz, int is_unicast, 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 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,6 +62,7 @@ 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)))
|
||||
@@ -80,6 +81,10 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int
|
||||
return 0;
|
||||
|
||||
msg_type = *((unsigned char *)daemon->dhcp_packet.iov_base);
|
||||
|
||||
/* request from a client must be multicast RFC-9915 section 16 */
|
||||
if (msg_type != DHCP6RELAYFORW && !multicast_dest)
|
||||
return 0;
|
||||
|
||||
/* Mark these so we only match each at most once, to avoid tangled linked lists */
|
||||
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
|
||||
@@ -87,7 +92,6 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int
|
||||
|
||||
reset_counter();
|
||||
state.context = context;
|
||||
state.multicast_dest = multicast_dest;
|
||||
state.interface = interface;
|
||||
state.iface_name = iface_name;
|
||||
state.fallback = fallback;
|
||||
@@ -95,10 +99,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,
|
||||
IN6_IS_ADDR_MULTICAST(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;
|
||||
@@ -106,15 +108,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, int is_unicast, 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)
|
||||
{
|
||||
@@ -126,7 +135,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
|
||||
{
|
||||
@@ -134,9 +143,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)
|
||||
@@ -145,7 +154,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) &&
|
||||
@@ -158,8 +167,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;
|
||||
@@ -169,7 +178,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);
|
||||
@@ -184,7 +193,9 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
return dhcp6_no_relay(state, msg_type, inbuff, sz, is_unicast, now);
|
||||
state->relay_address = link_address;
|
||||
|
||||
return dhcp6_no_relay(state, msg_type, inbuff, sz, now);
|
||||
}
|
||||
|
||||
/* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
|
||||
@@ -231,11 +242,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)
|
||||
{
|
||||
@@ -246,13 +254,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;
|
||||
/* zero is_unicast since that is now known to refer to the
|
||||
relayed packet, not the original sent by the client */
|
||||
if (!dhcp6_maybe_relay(state, opt6_ptr(opt, 0), opt6_len(opt), client_addr, 0, 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
|
||||
@@ -264,7 +271,7 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbuff, size_t sz, int is_unicast, time_t now)
|
||||
static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbuff, size_t sz, time_t now)
|
||||
{
|
||||
void *opt;
|
||||
int i, o, o1, start_opts, start_msg;
|
||||
@@ -290,7 +297,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;
|
||||
@@ -340,22 +347,17 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
|
||||
opt = opt6_find(state->packet_options, state->end, OPTION6_SERVER_ID, 1);
|
||||
|
||||
if (msg_type == DHCP6SOLICIT || msg_type == DHCP6CONFIRM || msg_type == DHCP6REBIND || msg_type == DHCP6IREQ)
|
||||
if (msg_type == DHCP6SOLICIT || msg_type == DHCP6CONFIRM || msg_type == DHCP6REBIND)
|
||||
{
|
||||
/* Above message types must be multicast 3315 Section 15. */
|
||||
if (!state->multicast_dest)
|
||||
/* SOLICIT, CONFIRM and REBIND messages MUST NOT have a server-id. 3315 para 15.x */
|
||||
if (opt)
|
||||
return 0;
|
||||
|
||||
/* server-id must match except for SOLICIT, CONFIRM and REBIND messages, which MUST NOT
|
||||
have a server-id. 3315 para 15.x */
|
||||
if (msg_type == DHCP6IREQ)
|
||||
{
|
||||
/* If server-id provided in IREQ, it must match. */
|
||||
if (opt && (opt6_len(opt) != daemon->duid_len ||
|
||||
memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))
|
||||
return 0;
|
||||
}
|
||||
else if (opt)
|
||||
}
|
||||
else if (msg_type == DHCP6IREQ)
|
||||
{
|
||||
/* If server-id provided in IREQ, it must match. */
|
||||
if (opt && (opt6_len(opt) != daemon->duid_len ||
|
||||
memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
@@ -370,18 +372,6 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
put_opt6(daemon->duid, daemon->duid_len);
|
||||
end_opt6(o);
|
||||
|
||||
if (is_unicast &&
|
||||
(msg_type == DHCP6REQUEST || msg_type == DHCP6RENEW || msg_type == DHCP6RELEASE || msg_type == DHCP6DECLINE))
|
||||
|
||||
{
|
||||
outmsgtype = DHCP6REPLY;
|
||||
o1 = new_opt6(OPTION6_STATUS_CODE);
|
||||
put_opt6_short(DHCP6USEMULTI);
|
||||
put_opt6_string("Use multicast");
|
||||
end_opt6(o1);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* match vendor and user class options */
|
||||
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
|
||||
{
|
||||
@@ -404,13 +394,26 @@ 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. */
|
||||
|
||||
/* 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))
|
||||
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)
|
||||
@@ -674,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;
|
||||
@@ -838,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;
|
||||
@@ -949,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;
|
||||
@@ -1084,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;
|
||||
|
||||
@@ -1160,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;
|
||||
@@ -1225,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;
|
||||
@@ -1304,7 +1307,6 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
|
||||
log_tags(tagif, state->xid);
|
||||
|
||||
done:
|
||||
/* Fill in the message type. Note that we store the offset,
|
||||
not a direct pointer, since the packet memory may have been
|
||||
reallocated. */
|
||||
@@ -1598,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)
|
||||
@@ -1613,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);
|
||||
|
||||
@@ -1931,10 +1927,10 @@ 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_ptr(opt, 4); 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1983,16 +1979,16 @@ 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_ptr(opt, 0); 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
|
||||
@@ -2007,10 +2003,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;
|
||||
@@ -2043,9 +2039,15 @@ 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 > (MAXDNAME * 2) - len - 1)
|
||||
slen = (MAXDNAME * 2) - len - 1;
|
||||
memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), slen);
|
||||
daemon->namebuff[len + slen] = 0;
|
||||
}
|
||||
optname = "status";
|
||||
}
|
||||
else
|
||||
@@ -2104,49 +2106,31 @@ static void log6_packet(struct state *state, char *type, struct in6_addr *addr,
|
||||
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))
|
||||
return NULL;
|
||||
|
||||
return opts + opt_len;
|
||||
}
|
||||
|
||||
/* make sure we have option number and length. */
|
||||
if ((uint8_t *)opt6_ptr(opt, 0) > end)
|
||||
return NULL;
|
||||
|
||||
/* make sure we have bytes promised by length. */
|
||||
if ((uint8_t *)opt6_ptr(opt, opt6_len(opt)) > end)
|
||||
return NULL;
|
||||
|
||||
return opt;
|
||||
}
|
||||
|
||||
static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
|
||||
{
|
||||
/* this worries about unaligned data and byte order */
|
||||
|
||||
+1
-22
@@ -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
|
||||
@@ -337,27 +337,6 @@ short *rrfilter_desc(int type)
|
||||
return p+1;
|
||||
}
|
||||
|
||||
int expand_workspace(unsigned char ***wkspc, int *szp, int new)
|
||||
{
|
||||
unsigned char **p;
|
||||
int old = *szp;
|
||||
|
||||
if (old >= new+1)
|
||||
return 1;
|
||||
|
||||
new += 5;
|
||||
|
||||
if (!(p = whine_realloc(*wkspc, new * sizeof(unsigned char *))))
|
||||
return 0;
|
||||
|
||||
memset(p+old, 0, new-old);
|
||||
|
||||
*wkspc = p;
|
||||
*szp = new;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Convert from presentation format to wire format, in place.
|
||||
Also map UC -> LC.
|
||||
Note that using extract_name to get presentation format
|
||||
|
||||
+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
|
||||
|
||||
+8
-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
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
+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
|
||||
|
||||
+146
-80
@@ -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];
|
||||
@@ -266,6 +264,10 @@ char *canonicalise(char *in, int *nomem)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* IDN library doesnt call our malloc wrapper, so log this by steam */
|
||||
if (ret)
|
||||
malloc_log(ret, strlen(ret)+1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -323,13 +325,15 @@ void *safe_malloc(size_t size)
|
||||
}
|
||||
|
||||
/* Ensure limited size string is always terminated.
|
||||
* Can be replaced by (void)strlcpy() on some platforms */
|
||||
Can be replaced by (void)strlcpy() on some platforms.
|
||||
src may be NULL in which case we return an empty string. */
|
||||
void safe_strncpy(char *dest, const char *src, size_t size)
|
||||
{
|
||||
if (size != 0)
|
||||
{
|
||||
dest[size-1] = '\0';
|
||||
strncpy(dest, src, size-1);
|
||||
dest[0] = dest[size-1] = '\0';
|
||||
if (src)
|
||||
strncpy(dest, src, size-1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,26 +345,6 @@ void safe_pipe(int *fd, int read_noblock)
|
||||
die(_("cannot create pipe: %s"), NULL, EC_MISC);
|
||||
}
|
||||
|
||||
void *whine_malloc(size_t size)
|
||||
{
|
||||
void *ret = calloc(1, size);
|
||||
|
||||
if (!ret)
|
||||
my_syslog(LOG_ERR, _("failed to allocate %d bytes"), (int) size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *whine_realloc(void *ptr, size_t size)
|
||||
{
|
||||
void *ret = realloc(ptr, size);
|
||||
|
||||
if (!ret)
|
||||
my_syslog(LOG_ERR, _("failed to reallocate %d bytes"), (int) size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2)
|
||||
{
|
||||
if (s1->sa.sa_family == s2->sa.sa_family)
|
||||
@@ -443,8 +427,8 @@ int hostname_issubdomain(char *a, char *b)
|
||||
for (ap = a; *ap; ap++);
|
||||
for (bp = b; *bp; bp++);
|
||||
|
||||
/* a shorter than b or a empty. */
|
||||
if ((bp - b) < (ap - a) || ap == a)
|
||||
/* a shorter than b */
|
||||
if ((ap - a) < (bp - b))
|
||||
return 0;
|
||||
|
||||
do
|
||||
@@ -459,12 +443,12 @@ int hostname_issubdomain(char *a, char *b)
|
||||
|
||||
if (c1 != c2)
|
||||
return 0;
|
||||
} while (ap != a);
|
||||
} while (bp != b);
|
||||
|
||||
if (bp == b)
|
||||
if (ap == a)
|
||||
return 2;
|
||||
|
||||
if (*(--bp) == '.')
|
||||
if (*(--ap) == '.')
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@@ -595,6 +579,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))
|
||||
@@ -692,32 +677,6 @@ int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask
|
||||
return count;
|
||||
}
|
||||
|
||||
/* _note_ may copy buffer */
|
||||
int expand_buf(struct iovec *iov, size_t size)
|
||||
{
|
||||
void *new;
|
||||
|
||||
if (size <= (size_t)iov->iov_len)
|
||||
return 1;
|
||||
|
||||
if (!(new = whine_malloc(size)))
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (iov->iov_base)
|
||||
{
|
||||
memcpy(new, iov->iov_base, iov->iov_len);
|
||||
free(iov->iov_base);
|
||||
}
|
||||
|
||||
iov->iov_base = new;
|
||||
iov->iov_len = size;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *print_mac(char *buff, unsigned char *mac, int len)
|
||||
{
|
||||
char *p = buff;
|
||||
@@ -777,43 +736,60 @@ int retry_send(ssize_t rc)
|
||||
"once" fails on EAGAIN, as this a timeout.
|
||||
This indicates a timeout of a TCP socket.
|
||||
*/
|
||||
int read_write(int fd, unsigned char *packet, int size, int rw)
|
||||
int read_writev(int fd, struct iovec *iov, int iovcnt, int rw)
|
||||
{
|
||||
ssize_t n, done;
|
||||
|
||||
for (done = 0; done < size; done += n)
|
||||
{
|
||||
if (rw & 1)
|
||||
n = read(fd, &packet[done], (size_t)(size - done));
|
||||
else
|
||||
n = write(fd, &packet[done], (size_t)(size - done));
|
||||
|
||||
if (n == 0)
|
||||
return 0;
|
||||
int cur = 0;
|
||||
ssize_t n, done = 0;
|
||||
|
||||
while (cur < iovcnt)
|
||||
{
|
||||
iov[cur].iov_len -= done;
|
||||
iov[cur].iov_base = ((char *)iov[cur].iov_base) + done;
|
||||
|
||||
if (rw & 1)
|
||||
n = readv(fd, &iov[cur], iovcnt - cur);
|
||||
else
|
||||
n = writev(fd, &iov[cur], iovcnt - cur);
|
||||
|
||||
iov[cur].iov_len += done;
|
||||
iov[cur].iov_base = ((char *)iov[cur].iov_base) - done;
|
||||
|
||||
if (n == -1)
|
||||
{
|
||||
n = 0; /* don't mess with counter when we loop. */
|
||||
|
||||
if (errno == EINTR || errno == ENOMEM || errno == ENOBUFS)
|
||||
continue;
|
||||
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
{
|
||||
/* "once" variant */
|
||||
if (rw & 2)
|
||||
return 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(rw & 2) && (errno == EAGAIN || errno == EWOULDBLOCK))
|
||||
continue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n == 0 && (rw & 1))
|
||||
return 0;
|
||||
|
||||
done += n;
|
||||
while (cur < iovcnt && (size_t)done >= iov[cur].iov_len)
|
||||
done -= iov[cur++].iov_len;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read_write(int fd, unsigned char *packet, int size, int rw)
|
||||
{
|
||||
struct iovec iov;
|
||||
|
||||
/* size == 0 is not an error, just a NOOP. */
|
||||
if (size == 0)
|
||||
return 1;
|
||||
|
||||
iov.iov_len = (size_t)size;
|
||||
iov.iov_base = packet;
|
||||
|
||||
return read_writev(fd, &iov, 1, rw);
|
||||
}
|
||||
|
||||
/* close all fds except STDIN, STDOUT and STDERR, spare1, spare2 and spare3 */
|
||||
void close_fds(long max_fd, int spare1, int spare2, int spare3)
|
||||
{
|
||||
@@ -949,3 +925,93 @@ int kernel_version(void)
|
||||
return version * 256 + (split ? atoi(split) : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define hash_ptr(x) (((unsigned int)(((char *)(x)) - ((char *)NULL))) & 0xffffff)
|
||||
|
||||
void *whine_malloc_real(const char *func, unsigned int line, size_t size)
|
||||
{
|
||||
void *ret = calloc(1, size);
|
||||
|
||||
if (!ret)
|
||||
my_syslog(LOG_ERR, _("failed to allocate %d bytes"), (int) size);
|
||||
else if (daemon->log_malloc)
|
||||
my_syslog(LOG_INFO, _("malloc: %s:%u %zu bytes at %x"), func, line, size, hash_ptr(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *whine_realloc_real(const char *wrapper, const char *func, unsigned int line, void *ptr, size_t size)
|
||||
{
|
||||
unsigned int old = hash_ptr(ptr);
|
||||
void *ret = realloc(ptr, size);
|
||||
|
||||
if (!ret)
|
||||
my_syslog(LOG_ERR, _("failed to reallocate %d bytes"), (int) size);
|
||||
else if (daemon->log_malloc)
|
||||
{
|
||||
if (!wrapper)
|
||||
wrapper = "realloc";
|
||||
my_syslog(LOG_INFO, _("%s: %s:%u %zu bytes from %x to %x"), wrapper, func, line, size, old, hash_ptr(ret));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* _note_ may copy buffer */
|
||||
int expand_buf_real(const char *func, unsigned int line, struct iovec *iov, size_t size)
|
||||
{
|
||||
void *new;
|
||||
|
||||
if (size <= (size_t)iov->iov_len)
|
||||
return 1;
|
||||
|
||||
if (!(new = whine_realloc_real("expand_buf", func, line, iov->iov_base, size)))
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
iov->iov_base = new;
|
||||
iov->iov_len = size;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int expand_workspace_real(const char *func, unsigned int line, unsigned char ***wkspc, int *szp, int new)
|
||||
{
|
||||
unsigned char **p;
|
||||
int old = *szp;
|
||||
|
||||
if (old >= new+1)
|
||||
return 1;
|
||||
|
||||
new += 5;
|
||||
|
||||
if (!(p = whine_realloc_real("expand_workspace", func, line, *wkspc, new * sizeof(unsigned char *))))
|
||||
return 0;
|
||||
|
||||
memset(p+old, 0, (new-old) * sizeof(unsigned char *));
|
||||
|
||||
*wkspc = p;
|
||||
*szp = new;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void malloc_log_real(const char *func, unsigned int line, void *mem, size_t size)
|
||||
{
|
||||
if (mem && daemon->log_malloc)
|
||||
my_syslog(LOG_INFO, _("malloc: %s:%u %zu bytes at %x"), func, line, size, hash_ptr(mem));
|
||||
}
|
||||
|
||||
#undef free
|
||||
void free_real(const char *func, unsigned int line, void *ptr)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
if (daemon->log_malloc)
|
||||
my_syslog(LOG_INFO, _("free: %s:%u block at %x"), func, line, hash_ptr(ptr));
|
||||
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user