Compare commits

...
25 Commits
Author SHA1 Message Date
Simon Kelley 65adc53130 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.
2026-02-28 21:30:57 +00:00
Simon Kelley 66d9658f5f Fix crash with empty DHCP SNAME option.
dhcp-option=66,

sets the servername to nothing, and causes a segfault at DHCP
transaction time. There may be other ways to provoke this, for
instance by using an empty filename.

The patch make safe_strncpy() even safer, by handing a NULL src argument.

Thanks to Jeff Allen for spotting and reporting this.
2026-02-26 15:10:41 +00:00
Thomas Erbesdobler ca30c82647 Fix broken NS responses in certain auth-zone configurations.
If dnsmasq is configured as an authoritatve server for zone transfer
_only_, (ie no interface name or address in --auth-server)
and secondary auth servers are configured, then
queries for NS RRs at the auth zone will get mangled answers.
This problem doesn't occur in AXFR or SOA queries, only NS queries.

Thanks to Thomas Erbesdobler for finding and analysing this problem.
The patch here is substantially his, with a little but of
collateral code tidying by srk.
2026-02-26 13:30:44 +00:00
Simon Kelley 32a54fc8a5 Remove duplicate configured trust anchors.
Well-known trust anchors can turn up in multiple config files.

Leaving the duplicates makes logging messy and costs some
CPU checking the same DS twice.
2026-02-16 22:20:05 +00:00
Simon Kelley 3f2bfca3df Log memory allocated by libidn when --log-malloc active. 2026-02-13 13:15:07 +00:00
Simon Kelley e4d0bb4579 Convert hash_init() to use realloc(). 2026-02-06 15:05:59 +00:00
Clayton O'Neill 84415a87be Fix PXE boot server (PXEBS) responses broken in 2.92
I think I've found a regression in dnsmasq 2.92 that breaks PXE boot server
(PXEBS) responses when running in proxy DHCP mode. Fair warning: I'm not
familiar with the dnsmasq codebase and used AI tooling to help trace through
the source and identify the issue, so please take the analysis below with
appropriate skepticism. PXE boot works fine on 2.91
but fails on 2.92 — the client gets the initial proxy DHCPOFFER, but the PXEBS
ACK on port 4011 never reaches it.

My setup is dnsmasq in proxy DHCP mode serving iPXE to Proxmox VMs via their
virtio-net ROM. Here's a stripped-down version of my config:

  port=0
  enable-tftp
  tftp-root=/tftpboot
  dhcp-range=172.19.74.0,proxy,255.255.255.0
  interface=eno1
  bind-interfaces
  dhcp-match=set:ipxe,175
  pxe-service=tag:ipxe,x86PC,"Network Boot",http://server:8081/boot.ipxe
  pxe-service=tag:!ipxe,x86PC,"Network Boot",undionly.kpxe
  log-dhcp

The issue seems to be in src/dhcp.c in the response routing logic after
dhcp_reply() returns. In 2.91, the destination selection was an if/else-if
chain:

  if (pxe_fd)
      { ... }
  else if (mess->giaddr.s_addr && !is_relay_reply)
      { ... }
  else if (mess->ciaddr.s_addr)
      { ... }
  else
      { ... broadcast to 255.255.255.255:68 ... }

In 2.92, the else between the pxe_fd block and the giaddr/relay check was
removed in commit 4fbe1ad ("Implement RFC-4388 DHCPv4 leasequery") to
accommodate the new is_relay_use_source logic:

  if (pxe_fd)
      { ... }
  if ((is_relay_use_source || mess->giaddr.s_addr) && !is_relay_reply)
      { ... }
  else if (mess->ciaddr.s_addr)
      { ... }
  else
      { ... broadcast to 255.255.255.255:68 ... }

For PXEBS responses, dhcp_reply() in rfc2131.c (around line 924-925) does:

  mess->yiaddr = mess->ciaddr;
  mess->ciaddr.s_addr = 0;

So after dhcp_reply() returns for a PXEBS request, ciaddr is 0, giaddr is 0
(no relay), and is_relay_use_source is 0. In 2.91, the pxe_fd block runs and
the rest of the chain is skipped — dest stays as received from recvmsg, and the
response goes back to the client correctly. In 2.92, the pxe_fd block runs but
then falls through to the standalone if, which is false, so the else block runs
and sets dest to 255.255.255.255 port 68. The client is listening on port 4011
and ignores it.

Here are the relevant dnsmasq logs. With 2.91 (working), I see normal proxy
DHCP and PXE boot server exchanges:

  dnsmasq-dhcp: DHCPDISCOVER(eno1) bc:24:11:59:85:90
  dnsmasq-dhcp: DHCPOFFER(eno1) 172.19.74.60 bc:24:11:59:85:90
  dnsmasq-dhcp: DHCPREQUEST(eno1) 172.19.74.60 bc:24:11:59:85:90
  dnsmasq-dhcp: DHCPACK(eno1) 172.19.74.60 bc:24:11:59:85:90
  dnsmasq-dhcp: PXE(eno1) bc:24:11:59:85:90 proxy
  dnsmasq-dhcp: PXE(eno1) bc:24:11:59:85:90 proxy
  dnsmasq-dhcp: PXEBS(eno1) bc:24:11:59:85:90 undionly.kpxe
  dnsmasq-dhcp: PXE(eno1) bc:24:11:59:85:90 proxy
  dnsmasq-dhcp: PXEBS(eno1) bc:24:11:59:85:90 http://infra1.oneill.net:8081/boot.ipxe

With 2.92 (broken), the DHCPDISCOVER/OFFER/REQUEST/ACK cycle and the proxy
PXE response work, but the PXEBS response never reaches the client — it times
out after repeated attempts. The dnsmasq side shows it sending the response,
but the client keeps retrying:

  dnsmasq-dhcp: PXE(eno1) bc:24:11:59:85:90 proxy
  dnsmasq-dhcp: PXE(eno1) bc:24:11:59:85:90 proxy
  dnsmasq-dhcp: PXEBS(eno1) bc:24:11:59:85:90 undionly.kpxe
  dnsmasq-dhcp: PXEBS(eno1) bc:24:11:59:85:90 undionly.kpxe
  dnsmasq-dhcp: PXEBS(eno1) bc:24:11:59:85:90 undionly.kpxe
  dnsmasq-dhcp: PXEBS(eno1) bc:24:11:59:85:90 undionly.kpxe

I tested by restoring the else keyword and the fix appears to work — 2.92 with
the patch below PXE boots successfully. I believe this change preserves the
leasequery behavior since that path only applies when pxe_fd is false (normal
DHCP handling, not port 4011).
2026-02-05 15:38:27 +00:00
Simon Kelley 1537645e8f treat opt_malloc as a wrapper for logging purposes. 2026-02-05 14:53:10 +00:00
Simon Kelley ceab392c60 Improve memory allocation for /etc/hosts etc.
Clearing and reloading the DNS cache involved freeing and re-allocating
many small memory blocks. Doing this frequently with a significant
number of static DNS configuraions or DHCP leases could result
in excessive heap fragmentation and memory use.

This patch extends the pool memory allocation technique used
else where to this memory. On cache clear, the memory is not
freed, but added to a pool of useable but unused data structures
which get re-used to store the new data.
2026-02-05 13:26:08 +00:00
Simon Kelley 5f0f6aa9b1 Enhance --log-malloc
Log callers of wrapper functions of realloc()
These are expand_buf() and expand_workspace()
2026-02-03 22:33:32 +00:00
Simon Kelley 578ea08391 CHANGELOG entry for 2.92 DHCPv6 and relay regression.
Bug introduced in 3ceea9e755
Bug fixed in 714d36229a
2026-02-03 22:06:07 +00:00
Simon Kelley fbfcf87f8b Rewrite blockdata_retrieve() and expand_buf() to use realloc(). 2026-02-01 21:56:30 +00:00
Simon Kelley 19f3e06e7a Fix compiler warning. 2026-02-01 15:44:13 +00:00
Matthias Andree 40d1152059 read_writev: avoid reading past the last iovec elem
If iovcnt is exhausted and the first vector element's operation
is satisfied, the while loop will read past the end of the
iovec array.  This triggers the address sanitizer and leads to
undefined program state.  Avoid reading too far.
2026-02-01 15:36:16 +00:00
Matthias Andree 3c830c4f1c Avoid uninitialized-value warnings from the compiler 2026-02-01 14:02:08 +00:00
Matthias Andree e9f400dd37 base32_decode: avoid shifting into the sign bit
While this won't do harm on systems that do 2's completement,
it triggers the compilers' undefined-behavior sanitizer and
fixes sanitizer error such as the one below (where the 1694... will
vary) and is distracting while debugging.

dnssec.c:1427:12: runtime error: left shift of 1694604366 by 1 places
cannot be represented in type 'int'
2026-02-01 14:00:31 +00:00
Simon Kelley 870f14108f Tidy up memory allocation in read_event() 2026-01-31 21:31:10 +00:00
Simon Kelley 714d36229a Tidy up check for muticast DHCPv6 requests.
Anything sent by a client must be multicast. A relay can unicast
to a server.
2026-01-31 20:40:21 +00:00
Simon Kelley 60e90e2224 Remove DHCPv6 UseMulticast option code.
This almost certainly never worked and was never use, and
it's rendered obsolete in RFC 9915.
2026-01-31 16:50:21 +00:00
Simon Kelley ed2ba5b07a Optimise TCP send.
In the DNS TCP code, there are a couple of places where
we have a buffer containing a message which we need to
send via TCP. The DNS protocol is that this is sent as
<16-bit length in network order><message>

Making two write calls, one for the length and one
for the message causes the TCP stack to send two packets,
one for each. A single packet containing both is
preferable from a performance POV.

Implement a scatter-gather version of our read_write()
wrapper and use it where necessary to send TCP DNS messages.
2026-01-28 14:57:50 +00:00
Simon Kelley 729c16a8ac Rationalise DNS TCP buffer use.
This fixes the plethora of 64k buffers that got allocated when doing
DNSSEC over TCP.

By using the UDP buffer is pass the query into tcp_talk() and
allowing tcp_talk to allocate its output buffer one the size
of the reply is known, we only need to allocate as much memory as
is required. The final reply to the TCP query still needs the 64k
buffer because answer_request() and answer_auth() are not capable
of extending their output buffers.
2026-01-26 15:25:22 +00:00
Simon Kelley 80acb76f46 Don't start malloc() logging until the log system is configured. 2026-01-25 12:20:04 +00:00
Simon Kelley f1fa05d754 Don't log free(NULL) calls. 2026-01-25 12:16:48 +00:00
Simon Kelley b148ba4752 Add --log-malloc debugging option.
Log all blocks malloced and all blocks freed.
2026-01-24 22:16:23 +00:00
Simon Kelley 81f619612e Fix memory allocation in blockdata_retrieve()
This was functionally correct, but every call
malloced a new buffer and freed the previous one, rather
than only doing that when the buffer needed expansion.
2026-01-24 22:00:35 +00:00
18 changed files with 613 additions and 441 deletions

No files matched your search

+10
View File
@@ -24,6 +24,16 @@ version 2.93
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
+7 -7
View File
@@ -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
+9 -7
View File
@@ -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;
}
+146 -102
View File
@@ -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;
}
@@ -1261,7 +1321,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;
}
@@ -1384,13 +1444,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);
}
@@ -1424,27 +1484,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);
}
@@ -1495,7 +1565,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))
{
@@ -1510,11 +1580,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;
@@ -1528,25 +1600,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
@@ -1561,7 +1638,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;
@@ -1570,7 +1647,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;
@@ -1662,8 +1739,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;
@@ -1734,12 +1810,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)
@@ -1786,15 +1857,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
@@ -1827,17 +1890,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))
@@ -2166,9 +2219,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)
{
@@ -2183,37 +2236,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)
+1
View File
@@ -22,6 +22,7 @@
#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. */
+4 -6
View File
@@ -126,20 +126,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;
}
+1 -1
View File
@@ -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)
+70 -27
View File
@@ -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
@@ -1447,8 +1468,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;
@@ -1458,12 +1477,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;
@@ -1516,6 +1545,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);
}
}
@@ -1526,9 +1575,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)
{
@@ -1634,7 +1680,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 */
@@ -1977,11 +2022,11 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
pid_t p;
union mysockaddr tcp_addr;
socklen_t tcp_len = sizeof(union mysockaddr);
unsigned char *buff;
struct server *s;
int flags, auth_dns = 0;
struct in_addr netmask;
int pipefd[2];
struct iovec tcpbuff;
#ifdef HAVE_LINUX_NETWORK
unsigned char a = 0;
#endif
@@ -2156,10 +2201,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)
+24 -8
View File
@@ -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
@@ -290,7 +295,8 @@ struct event_desc {
#define OPT_AUTH_LOG 76
#define OPT_LEASEQUERY 77
#define OPT_LOG_ONLY_FAILED 78
#define OPT_LAST 79
#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) )
@@ -1207,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;
@@ -1495,8 +1502,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);
@@ -1518,9 +1535,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);
@@ -1563,8 +1580,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,
@@ -1895,7 +1912,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
@@ -1909,7 +1926,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. */
+5 -2
View File
@@ -1423,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;
}
}
@@ -1553,7 +1556,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
{
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)
+1 -1
View File
@@ -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;
+136 -135
View File
@@ -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))));
}
@@ -1074,7 +1074,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);
@@ -2018,20 +2018,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;
@@ -2043,10 +2044,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;
@@ -2068,10 +2071,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)
@@ -2102,10 +2101,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)
@@ -2131,12 +2137,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. */
@@ -2149,28 +2156,28 @@ 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 */
p = (unsigned char *)(((struct dns_header *)recvbuff->iov_base)+1);
if (extract_name(((struct dns_header *)recvbuff->iov_base), rsize, &p, daemon->namebuff, EXTR_NAME_COMPARE, 4) != 1)
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;
}
@@ -2182,19 +2189,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;
@@ -2212,10 +2216,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))
@@ -2252,7 +2257,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;
}
@@ -2262,9 +2267,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;
@@ -2298,34 +2306,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);
@@ -2342,8 +2337,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;
}
@@ -2353,11 +2347,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;
@@ -2366,16 +2360,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;
@@ -2385,9 +2373,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. */
@@ -2408,7 +2400,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)
@@ -2431,7 +2423,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;
}
}
@@ -2449,18 +2441,26 @@ 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 long than this. If we're answering locally,
copy the query into the output buffer, but for forwarding, tcp_talk()
wants the query in a a different buffer from the reply.
Note that we overwrote any saved UDP query - this onlt matters in debug mode. */
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))
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;
/* header == query */
header = (struct dns_header *)daemon->packet;
query_count++;
/* log_query gets called indirectly all over the place, so
@@ -2479,9 +2479,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))
@@ -2496,9 +2493,12 @@ 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;
size = add_edns0_config(header, size, ((unsigned char *) header) + daemon->edns_pktsz, &peer_addr, now, &cacheable);
/* Clear buffer to avoid risk of information disclosure. */
memset(bigbuff->iov_base, 0, bigbuff->iov_len);
/* Copy query into output buffer for local answering */
memcpy(out_header, header, size);
log_query_mysockaddr((auth_dns ? F_NOERR | F_AUTH : 0) | F_QUERY | F_FORWARD, daemon->namebuff,
&peer_addr, NULL, qtype);
@@ -2557,10 +2557,10 @@ unsigned char *tcp_request(int confd, time_t now,
#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);
}
}
@@ -2568,15 +2568,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 (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;
@@ -2605,7 +2602,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. */
@@ -2615,12 +2612,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);
@@ -2636,7 +2635,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";
@@ -2662,7 +2662,7 @@ 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;
}
@@ -2686,18 +2686,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;
@@ -2712,8 +2712,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)
@@ -2729,14 +2729,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
@@ -2746,7 +2744,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
@@ -2762,18 +2766,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
@@ -3274,7 +3275,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 */
+25 -10
View File
@@ -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);
}
}
}
+9 -3
View File
@@ -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);
}
@@ -5731,7 +5737,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;
+5
View File
@@ -1427,6 +1427,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, ...)
{
+21 -39
View File
@@ -21,7 +21,7 @@
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;
@@ -34,8 +34,8 @@ 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, 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);
@@ -80,6 +80,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 +91,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;
@@ -97,8 +100,7 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int
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, now))
return msg_type == DHCP6RELAYFORW ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;
return 0;
@@ -106,7 +108,7 @@ 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, time_t now)
{
uint8_t *end = inbuff + sz;
uint8_t *opts = inbuff + 34;
@@ -184,7 +186,7 @@ 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);
return dhcp6_no_relay(state, msg_type, inbuff, sz, now);
}
/* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
@@ -250,9 +252,7 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
/* 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))
if (!dhcp6_maybe_relay(state, opt6_ptr(opt, 0), opt6_len(opt), client_addr, now))
return 0;
}
else
@@ -264,7 +264,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;
@@ -340,22 +340,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 +365,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)
{
@@ -1304,7 +1287,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. */
-21
View File
@@ -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
+139 -72
View File
@@ -266,6 +266,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 +327,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 +347,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)
@@ -692,32 +678,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 +737,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 +926,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);
*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);
}
}