Compare commits

...
31 Commits
Author SHA1 Message Date
Matthias Andree 3ff66da573 Fix two typos in v2.93 CHANGELOG 2026-06-04 12:47:22 +01:00
Simon Kelley d823d8e9cd Fix OOB-read crash in get_rdata().
get_rdata() contains another instance of a crash caused by
a false value of RDLEN in a resource-record.

An RR containing name which is longer than the rdlen of the RR can
get get_rdata into a state where it calculates the length of the data
following the name as a negative number which is then promoted
to a very large positive number, and causing SIGSEGV.

This can be triggered by crafted packets from malicous DNS servers,
but only when DNSSEC validation is enabled. The attack packets do not
have to be correctly DNSSEC signed.

Thanks to Tristan G. for spotting this problem.

Also fix error handling for RRs with a fixed size fields in the specification
where the rrsize indicates that there are zero bytes in that position.
2026-06-04 12:47:18 +01:00
Simon Kelley 2c78b6db6c Better code to make human-friendly tokens for pointers in log-malloc.
It turns out that subtracting NULL from a pointer to turn it into
an integer is not a good idea, because subtracting a NULL pointer
is undefined behaviour in C.

The correct way to do this is simply to cast the pointer to
uintptr_t. The only fly in this ointment is that the C standard
does not mandate support for uintptr_t.  In practice, I doubt
anyone is running dnsmasq on the long-dead architectures where
this might be a problem.

Thanks to Matthias Andree for the heads-up.
2026-06-02 13:00:18 +01:00
Simon Kelley 4bf46ceb37 Add the ability to bail before EOF to get_line_alloc();
If we stop reading a file before getting to the end, get_line_alloc()
will not clean up the memory it allocated. Give get_line_alloc()
the ability to do a forced cleanup by calling it with a NULL file,
and use that ability where we stop reading a file early.

Also clear the state variables when we free the memory, to
pre-empt use-after-free.
2026-06-02 00:22:05 +01:00
Simon Kelley 7d42376a3a Fix get_line_alloc()
Assigning the result of getc() to a char makes comparing it to EOF
unreliable, at the very least. You're never too old to make newbie
mistakes.

Thanks to Sebastian Gottschall  for spotting this.
2026-06-01 22:56:23 +01:00
Simon Kelley 08cc209e7a Add --log-debug to man page. 2026-06-01 22:45:49 +01:00
Simon Kelley ff4964d01d Re-implement print_mac() to do its own buffer managment.
print_mac() used to output into caller-supplied buffer with no
length checking. The various callers supplied different
length buffers and did their own, ad-hoc, length checking before
calling print_mac(). This was an accident waiting to happen and
it had actually happened in couple of places, with buffer
overflows possible.

print_mac() now returns the address of its own buffer, which
is grown as required.

Thanks to Nicholas Carlini <npc@anthropic.com> for spotting this problem.
2026-05-31 21:37:48 +01:00
Michalis Vasileiadis 45771dc92c Fix OOB writes in contrib/leasequesry/leasequery.c
Thanks to Michalis Vasileiadis for spotting this.

print_mac() writes each MAC byte to its output buffer with unbounded
sprintf and is called from main() with pkt.header.hlen straight out
of a BOOTREPLY. hlen is an attacker-controlled uint8_t (up to 255),
each byte expands to up to 3 chars, and the destination is a 500-byte
stack buffer. A malicious leasequery server that echoes the client's
transaction ID and replies with DHCPLEASEACTIVE and an oversized
hlen overflows that stack buffer.

The patch caps len inside print_mac at DHCP_CHADDR_MAX (16), which is
the actual size of chaddr in the BOOTP header and an upper bound on
any legitimate hardware-address length. The other in-file caller of
print_mac already clamps its length argument to 14 before the call,
so this change is local to the vulnerable path.
2026-05-30 15:58:52 +01:00
Simon Kelley af73e48067 Include fc00::/8 in the set of local addresses.
The current local address range is fd00::/8, but RFC 4193
para 3.1/3.2 reserves fc00::/8 for future use as local addresses,
so add that to the list.

Thanks to Michalis Vasileiadis for spotting this omission.
2026-05-30 13:46:44 +01:00
Simon Kelley 77d4749a5e Fix DNSSEC frec use-after-free in TCP validation path.
Full credit to Jerry Tom <goonsomeway@gmail.com> for finding this.

When DNSSEC validation falls back to TCP (via a forked child process),
the parent uses a uid-based check to detect whether a frec has been
freed and reused before the child returns. It doesn't, however check for
the more likely scenario which is that the frec has simply been
freed via the timeout path in get_new_frec(). This leads to a
use-after-free on an already freed frec.
2026-05-29 17:24:54 +01:00
OpenAI Security Research Team 0375e40824 Fix bounds-checking in contrib/lease-tools/dhcp_release6.c
Credit to OpenAI Security Research Team, outbounddisclosures@openai.com
2026-05-29 16:28:49 +01:00
Simon Kelley 18a139f48f Change "limit" arg to add_pseudoheader() to a length.
Also ripple effects for most of its callers.

This fixes an OOB write caused by bad pointer arithmetic
calculating the pointer-to-end-of-packet used before.

Thanks to Donghyeon Jeong for spotting the bug.
2026-05-17 13:06:17 +01:00
Simon Kelley d5d2bfb74a Change "limit" arg to make_local_answer() to a length.
This fixes an OOB write caused by bad pointer arithmetic
calculating the pointer-to-end-of-packet used before.

Thanks to Donghyeon Jeong for spotting the bug.
2026-05-17 13:05:35 +01:00
Simon Kelley 7f5802c144 Tidy up get_line_alloc() 2026-05-17 00:33:55 +01:00
Simon Kelley 7280e46aa5 Check for too-small packets in relay_upstream6()
Before this fix, the code could read a couple of bytes beyond
valid data when an invalid too-short packet was recieved.

Thanks to Sokhna Walo DIAKHATE <sokhnawalodiakhate@esp.sn>
for the bug report.
2026-05-17 00:24:57 +01:00
Simon Kelley 754177b368 Fix hostname_issubdomain() confusion.
commit 1269f074f8 broke
hostname_issubdomain() and then accidentally fixed
some of the problems by inverting the argument order
in the new uses of the function it introduced.
All the pre-existing calls to hostname_issubdomain()
were left in a non-working state.

This fixes hostname_issubdomain() back to the state
before 1269f074f8, adds
some logic to give correct answers when comparing
to the root domain, and gets right the new calls to
hostname_issubdomain()  added in
1269f074f8

Thanks to Jean Thomas for spotting this problem.
2026-05-16 23:52:29 +01:00
Simon Kelley 228d7ff964 Remove arbitrary limit on config file line lengths.
Stop using the namebuff buffer to read config file
lines. This reduced in size in
014e909f78 and caused
regressions. We now resize the buffer as needed,
so there is no practical limit.

Use the same code to remove the same limit when reading
/etc/ethers and /etc/resolv.conf. Also when copying output
from the lease-change script into the log.

Thanks to Kevin Darbyshire-Bryant for finding the regresssion.
2026-05-16 21:37:11 +01:00
do litli 14094e88be Fix buffer OOB read in find_soa()
In find_soa() extract_name() is called with extrabytes=0 when parsing NS
record names, which means it only validates that the DNS name fits
within the packet but does not check that 10 additional bytes exist for
the type/class/TTL/rdlen fixed fields. Lines 546-549 then
unconditionally read these 10 bytes via GETSHORT/GETLONG macros. An
attacker controlling a DNS zone can craft a NXDOMAIN response where the
NS record name extends to the packet boundary, causing a 10-byte
out-of-bounds read past the valid packet data (CWE-125, CVSS 5.3
Medium). The read stays within the over-allocated packet buffer in
default configurations, limiting crash risk, but accesses data outside
the logical packet boundary. Under certain conditions, the overread may
access stale heap data from prior transactions.

The fix is straightforward: change the extrabytes
argument from 0 to 10, consistent with other call sites in
the same file.

Credit is due to do litli for finding this problem.
2026-05-10 22:03:50 +01:00
Simon Kelley c705607122 Fix problen in from_wire() when names contain escape chars.
Thanks to Royce M <royce@xchglabs.com> for spotting this.
2026-05-10 21:49:40 +01:00
Simon Kelley 9d2c93b608 Sync 3ea905b98b with 7cd050075ea73cd7707fce5b70f0fec25f722f67 2026-05-10 21:49:40 +01:00
Simon Kelley 073082ddc0 Fix buffer overflow vulnerability in extract_addresses() CVE-2026-5172
Thanks to Hugo Martinez Ray for spotting this.

The value of rdlen for an RR can be a lie, allowing the
call to extract_name() at rfc1025.c:952 to advance the value of p1
past the calculated end of the record. The makes the calculation
of bytes remaining in the RR underflow to a huge number and results
in a massive heap OOB read and certain crash.
2026-05-10 21:49:40 +01:00
Simon Kelley 9f651f3398 Updates to a345b075b3b8696a18631b46e53ab654354ed961 following review. 2026-05-10 21:49:40 +01:00
Simon Kelley e3a26d092e Fix broken client subnet validation. CVE-2026-4893
Bug report from Royce M <royce@xchglabs.com>

Location: forward.c:713, edns0.c:421

With --add-subnet enabled, process_reply() passes the OPT record
length (~23 bytes) instead of the packet length to check_source().
All internal bounds checks fail, and the function always returns 1.
ECS source validation per RFC 7871 Section 9.2 is completely bypassed.
2026-05-10 21:49:40 +01:00
Simon Kelley 10e6b5b83e Fix buffer overflow in helper.c with large CLIDs. CVE-2026-4892
Bug reported bt Royce M <royce@xchglabs.com>

Location: helper.c:265-270
DHCPv6 CLIDs can be up to 65535 bytes. When --dhcp-script is configured,
the helper hex-encodes raw CLID bytes via sprintf("%.2x") into daemon->packet (5131 bytes).
A 1000-byte CLID writes ~3000 bytes. The helper process retains root privileges.

Note: log6_packet() correctly caps CLID to 100 bytes for logging, but the helper code path was missed.
2026-05-10 21:49:40 +01:00
Simon Kelley 788b4e0f6c Verify rdlen field in RRSIG packets. CVE-2026-4891
Bug report from Royce M <royce@xchglabs.com>

This avoids crafted packets which give a value for rdlen _less_
then the space taken up by the fixed data and the signer's name
and engender a negative calculated length for the signature.
2026-05-10 21:49:40 +01:00
Simon Kelley 7b151eb606 Fix NSEC bitmap parsing infinite loop. CVE-2026-4890
Report from Royce M <royce@xchglabs.com>.

Location: dnssec.c:1290-1306, dnssec.c:1450-1463

The bitmap window iteration advances by p[1] instead of p[1]+2
(missing the 2-byte window header). With bitmap_length=0, both rdlen and p are
unchanged, causing an infinite loop and dnsmasq stops responding to all queries.

Bug is reachable before RRSIG validation
(confirmed by the source comment at line 2125), so no valid
DNSSEC signatures are needed.
2026-05-10 21:49:40 +01:00
Simon Kelley 014e909f78 Rework storage allocation for domain names. CVE-2026-2291
This is the stable release fix for CVE-2026-2291

Dnsmasq recieves and sends domain names within DNS packets in RFC-1035
format, a string of counted labels terminated by a zero length label.
Internally to dnsmasq, domain names are represented as zero-terminated C strings
with the labels seperated by '.' characters, like the normal presentation
format for domain names.

The RFC-1035 limit for a domain name in wire format is 255 octets, and
when converted to presentation format, this makes a string of
maximum 253 characters. However dnsmasq needs to be able to
represent any 8-bit character within a label, and this causes
problems with /000 (which would terminate the c-string early) and
'.' (which would terminate a label early). To avoid this, dnsmasq
escapes both of these characters, which means that they take two bytes
in the string. The domain name with must charaters in it has four labels
and the 255 octet wire length limits these four labels to a total of
250 characters. The other five octets are the four label length bytes
and the zero length byte terminator. If all 250 characters need escaping
that makes the maximum length of the dnsmasq internal format 503
characters (250 escaped characters in labels, amd three dots between
four labels) Since this format is zero-terminated c-string, buffers
have to be allocated as 504 bytes.

This arrangement grew in a somewhat ad-hoc manner and early dnsmasq
didn't do the escaping trick, and didn't really differentiate between
the lengths of the wire format and the presentation format, since
they were very similar. It also, for reasons lost in time, inherited
a defintion for MAXDNAME from early BIND headers, set at 1025 bytes.
This value was used as the buffer size for both wire and presentation
formats.

This patch makes everything consistent. It declares two constants

MAXDNAME 255     /* max size of wire format domain name */
MAXDNAMESTR 503  /* max size of internal format created from a 255 octet wire format name */

All internal name buffers are allocated as MAXDNAMESTR+1 bytes,
to hold a maximum size internal format name and zero termination.

Names parsed out of incoming packets are rejected if their
wire format is greater than 255 bytes, which ensures that
their internal representation cannot exceed 503 characters.
Names inserted into outgoing packets are failed similarly if
they exceed 255 bytes. (this would in theory be possible
for a legal internal-representaion name if it has at least
one unescaped character in a label.
2026-05-10 21:49:39 +01:00
Simon Kelley 29f8712f02 Fix OOB read/write of rr_status.
Crafted packets can write zeros outside the limits of the rr_status
array, causing a crash and DoS.

Thanks to Haiyang Huang for finding and reprting this.
2026-05-10 21:47:12 +01:00
Simon Kelley 0450f9e28a Fix OOB read when parsing DHCPv6 userclass or vendorclass options.
Credit to Haiyang Huang for finding this.

The fix extends the improved framework for DHCPv6 options added in
b95d5a2577
to the encapsulated vendor and user options.
2026-05-10 16:49:59 +01:00
Sagie D. 0637bcf010 Add support for IPV6 when reading ARP/ND table on *BSD.
The arp_enumerate() function is extended into a per-family
helper arp_enumerate_family(), called sequentially for AF_INET and
AF_INET6, allowing the NDP neighbour cache to be enumerated alongside
the ARP table. An empty table for either family is treated as vacuous
success rather than an error. Both tables are acquired in *BSD via
PF_ROUTE sysctl calls that return raw kernel structures; consequently,
for IPv6, the sockaddr_in6 peer address extracted from them has an
embedded link-local scope ID. This ID is extracted into sin6_scope_id,
and bytes 2-3 of the address are then cleared, per the KAME API
contract.

Signed-off-by: Sagie D.
2026-05-09 21:44:38 +01:00
Simon Kelley 4bcc9650fa Fix memory leak reading ARP cache on *BSD.
arp_enumerate() on *BSD platforms has a bad memory leak.

Thanks to Sagie Duchovne-Nave for finding this problem. His
description is included below:

 arp_enumerate() allocates a heap buffer via expand_buf() to hold the
 kernel ARP table dump retrieved through sysctl(NET_RT_FLAGS). This
 buffer is never freed on any return path -- neither the early error
 returns nor the normal return after iteration -- causing a leak on
 every call.

 The leak is most acute in the DHCPv6 path. get_client_mac() calls
 find_mac() up to five times per packet with lazy=0. Because the
 'updated' flag is local to each find_mac() invocation, a cached
 ARP_EMPTY entry for an unresolvable IPv6 address does not short-
 circuit the kernel lookup: each call falls through to iface_enumerate()
 -> arp_enumerate(), leaking one buffer per call. This yields up to
 five leaked allocations per DHCPv6 SOLICIT packet. The leak size per
 call equals the full system-wide IPv4 ARP table dump across all
 interfaces.

 The condition is readily triggered by a DHCPv6 client whose MAC
 address cannot be resolved via NDP -- which is the common case on
 FreeBSD, because arp_enumerate() queries NET_RT_FLAGS/RTF_LLINFO,
 which returns IPv4 ARP entries only; IPv6 NDP neighbour entries are
 not included. As a result every IPv6 MAC lookup fails unconditionally
 on FreeBSD, every failed lookup produces an ARP_EMPTY record that is
 never promoted, and every subsequent packet for that client leaks five
 buffers.

This fix is by Simon Kelley, not Sagie, so any bugs are my fault.
The fix is to make the buffer statically allocated, so that expand_buf()
is normally a NOOP, but when the ARP cache grows, it grows to match.
This avoids fragmenting the heap with many malloc/free calls.
It's a common design pattern in dnsmasq, but the original author
of this code missed the importance of that static allocation. Since
that was before dnsmasq moved into git, the details are lost to time.
2026-05-04 16:18:33 +01:00
29 changed files with 596 additions and 394 deletions

No files matched your search

+13 -3
View File
@@ -1,6 +1,6 @@
version 2.93
Fix a corner-case in DNSSEC validation with wildcards. If we have
a wildcard record *.example.com and recieve a query for
a wildcard record *.example.com and receive a query for
a.example.com then that's OK, but we have to check that there isn't
an actual a.example.com record. The corner case is when we get a
query for *.example.com in that case the non-existence check
@@ -19,7 +19,7 @@ version 2.93
(eg rivcoed.org) will still fail. Thanks to Petr Menšík
for the bug report.
Fix DNSSEC fail with CNAME replies to DS queries. As CNAME reply
Fix DNSSEC fail with CNAME replies to DS queries. A CNAME reply
to a DNSSEC query was confusing the validation logic. It now
accepts a signed CNAME reply to a DS query as proof that no DS
exists at the domain. This fixes the DS/zone break detection logic.
@@ -33,7 +33,17 @@ version 2.93
This means that the permissions used when creating the watches
are the same as used for accessing watched files, which makes
more sense and avoids odd and confusing error conditions.
Rework storage allocation for domain names. This fixes a security
bug that can cause heap-overwrite with long domain names.
CVE-2026-2291 covers this and a simple patch for existing
releases was released with the CVE. This patch reworks the whole
code base to make it cleaner and less liable to future
confusions. Either upgrading to 2.93 or applying the patch
to earlier versions is sufficient to close the security hole.
Thanks to Andrew S. Fasano for spotting this problem in the
first place.
version 2.92
Redesign the interaction between DNSSEC validation and per-domain
+44 -3
View File
@@ -233,31 +233,55 @@ struct dhcp6_packet create_release_packet(const char* iaid, const char* ip, cons
return result;
}
static void copy_option_string(char *dst, size_t dst_len, const char *src, size_t src_len)
{
if (dst_len == 0)
return;
if (src_len >= dst_len)
src_len = dst_len - 1;
memcpy(dst, src, src_len);
dst[src_len] = '\0';
}
uint16_t parse_iana_suboption(char* buf, size_t len)
{
size_t current_pos = 0;
char option_value[1024];
while (current_pos < len)
{
if (len - current_pos < 2 * sizeof(uint16_t))
return UNSPEC_FAIL;
uint16_t option_type, option_len;
memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
option_type = ntohs(option_type);
option_len = ntohs(option_len);
current_pos += 2 * sizeof(uint16_t);
if (option_len > len - current_pos)
return UNSPEC_FAIL;
if (option_type == STATUS_CODE)
{
uint16_t status;
if (option_len < sizeof(uint16_t))
return UNSPEC_FAIL;
memcpy(&status, buf + current_pos, sizeof(uint16_t));
status = ntohs(status);
if (status != SUCCESS)
{
memcpy(option_value, buf + current_pos + sizeof(uint16_t) , option_len - sizeof(uint16_t));
option_value[option_len-sizeof(uint16_t)] ='\0';
copy_option_string(option_value, sizeof(option_value),
buf + current_pos + sizeof(uint16_t),
option_len - sizeof(uint16_t));
fprintf(stderr, "Error: %s\n", option_value);
}
return status;
}
current_pos += option_len;
}
return -2;
@@ -266,6 +290,9 @@ uint16_t parse_iana_suboption(char* buf, size_t len)
int16_t parse_packet(char* buf, size_t len)
{
int16_t ret = -1;
if (len < 4)
return UNSPEC_FAIL;
uint8_t type = buf[0];
/*skipping tx id. you need it, uncomment following line
uint16_t tx_id = ntohs((buf[1] <<16) + (buf[2] <<8) + buf[3]);
@@ -277,20 +304,31 @@ int16_t parse_packet(char* buf, size_t len)
char option_value[1024];
while (current_pos < len)
{
if (len - current_pos < 2 * sizeof(uint16_t))
return UNSPEC_FAIL;
uint16_t option_type, option_len;
memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
option_type = ntohs(option_type);
option_len = ntohs(option_len);
current_pos += 2 * sizeof(uint16_t);
if (option_len > len - current_pos)
return UNSPEC_FAIL;
if (option_type == STATUS_CODE)
{
uint16_t status;
if (option_len < sizeof(uint16_t))
return UNSPEC_FAIL;
memcpy(&status, buf + current_pos, sizeof(uint16_t));
status = ntohs(status);
if (status != SUCCESS)
{
memcpy(option_value, buf + current_pos +sizeof(uint16_t) , option_len -sizeof(uint16_t));
copy_option_string(option_value, sizeof(option_value),
buf + current_pos + sizeof(uint16_t),
option_len - sizeof(uint16_t));
fprintf(stderr, "Error: %d %s\n", status, option_value);
return status;
}
@@ -301,6 +339,9 @@ int16_t parse_packet(char* buf, size_t len)
if (option_type == IA_NA )
{
if (option_len < 24)
return UNSPEC_FAIL;
uint16_t result = parse_iana_suboption(buf + current_pos +24, option_len -24);
if (result)
return result;
+9 -3
View File
@@ -331,9 +331,15 @@ static char *print_mac(char *buff, unsigned char *mac, int len)
if (len == 0)
sprintf(p, "<null>");
else
for (i = 0; i < len; i++)
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
{
/* Server-controlled lengths (e.g. BOOTREPLY hlen) must not be trusted
to fit the destination buffer; cap at the actual chaddr field size. */
if (len > DHCP_CHADDR_MAX)
len = DHCP_CHADDR_MAX;
for (i = 0; i < len; i++)
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
}
return buff;
}
+3
View File
@@ -154,6 +154,9 @@ allows the log file to be rotated without stopping dnsmasq.
.B --log-debug
Enable extra logging intended for debugging rather than information.
.TP
.B --log-malloc
Enable logging of memory allocation and release, for debugging.
.TP
.B --log-async[=<lines>]
Enable asynchronous logging and optionally set the limit on the
number of lines
+40 -14
View File
@@ -47,24 +47,20 @@ static union all_addr del_addr;
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
int arp_enumerate(void *parm, callback_t callback)
static int arp_enumerate_family(int family, void *parm, callback_t callback)
{
int mib[6];
size_t needed;
char *next;
struct rt_msghdr *rtm;
struct sockaddr_inarp *sin2;
struct sockaddr_dl *sdl;
struct iovec buff;
static struct iovec buff = { NULL, 0 };
int rc;
buff.iov_base = NULL;
buff.iov_len = 0;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = AF_INET;
mib[3] = family;
mib[4] = NET_RT_FLAGS;
#ifdef RTF_LLINFO
mib[5] = RTF_LLINFO;
@@ -72,8 +68,8 @@ int arp_enumerate(void *parm, callback_t callback)
mib[5] = 0;
#endif
if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
return 0;
return 1; /* not a failure: unsupported or empty table */
while (1)
{
if (!expand_buf(&buff, needed))
@@ -83,20 +79,50 @@ int arp_enumerate(void *parm, callback_t callback)
break;
needed += needed / 8;
}
if (rc == -1)
return 0;
for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen)
{
rtm = (struct rt_msghdr *)next;
sin2 = (struct sockaddr_inarp *)(rtm + 1);
sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
if (!callback.af_unspec(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm))
return 0;
if (family == AF_INET)
{
struct sockaddr_inarp *sin2 = (struct sockaddr_inarp *)(rtm + 1);
sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
if (!callback.af_unspec(AF_INET, &sin2->sin_addr,
LLADDR(sdl), sdl->sdl_alen, parm))
return 0;
}
else
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(rtm + 1);
sdl = (struct sockaddr_dl *)((char *)sin6 + SA_SIZE(sin6));
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
{
/* PF_ROUTE sysctl returns raw kernel structures with the interface
index embedded in bytes 2-3 of link-local addresses. Extract it
into sin6_scope_id per the KAME API contract before clearing. */
sin6->sin6_scope_id =
((uint32_t)(sin6->sin6_addr.s6_addr[2]) << 8) | sin6->sin6_addr.s6_addr[3];
sin6->sin6_addr.s6_addr[2] = 0;
sin6->sin6_addr.s6_addr[3] = 0;
}
if (!callback.af_unspec(AF_INET6, &sin6->sin6_addr,
LLADDR(sdl), sdl->sdl_alen, parm))
return 0;
}
}
return 1;
}
static int arp_enumerate(void *parm, callback_t callback)
{
if (!arp_enumerate_family(AF_INET, parm, callback))
return 0;
return arp_enumerate_family(AF_INET6, parm, callback);
}
#endif /* defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) */
+18 -11
View File
@@ -22,6 +22,7 @@ static struct crec *new_chain = NULL;
static int insert_error;
static union bigname *big_free = NULL;
static int bignames_left, hash_size;
struct nameblock {
struct nameblock *next;
unsigned int last, index;
@@ -221,6 +222,9 @@ static char *store_name(unsigned int namelen, unsigned int index)
struct nameblock *block;
char *ret = NULL;
if (namelen > NAMEBLOCK_CHARS)
return NULL;
for (block = hostblocks; block; block = block->next)
if (block->index == index && NAMEBLOCK_CHARS - block->last >= namelen)
break;
@@ -1087,10 +1091,12 @@ int cache_recv_insert(time_t now, int fd)
if (op == PIPE_OP_KILLED)
my_syslog(LOG_INFO, _("TCP process for DNSSEC validation timed out"));
/* There's a tiny chance that the frec may have been freed
and reused before the TCP process returns. Detect that with
the uid field which is unique modulo 2^32 for each use. */
if (uid == forward->uid)
/* There's a chance that the frec may have timed-out and been
freed before the TCP process returns, so check that the
frec is still marked in use. Even if it is in use, there's a tiny chance
that the frec may have been freed and _reused_ before the TCP process returns.
Detect that with the uid field which is unique modulo 2^32 for each use. */
if (forward->sentto && uid == forward->uid)
{
if (op == PIPE_OP_RESULT)
{
@@ -1403,9 +1409,10 @@ static int eatspace(FILE *f)
}
}
static int gettok(FILE *f, char *token)
static int gettok(FILE *f, char *token, size_t buffsz)
{
int c, count = 0;
int c;
unsigned int count = 0;
while (1)
{
@@ -1418,7 +1425,7 @@ static int gettok(FILE *f, char *token)
return eatspace(f);
}
if (count < (MAXDNAME - 1))
if (count < (buffsz - 1))
{
token[count++] = c;
token[count] = 0;
@@ -1443,7 +1450,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
lineno += eatspace(f);
while ((atnl = gettok(f, token)) != -1)
while ((atnl = gettok(f, token, MAXDNAMESTR)) != -1)
{
if (inet_pton(AF_INET, token, &addr) > 0)
{
@@ -1461,7 +1468,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
{
my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno);
while (atnl == 0)
atnl = gettok(f, token);
atnl = gettok(f, token, MAXDNAMESTR);
lineno += atnl;
continue;
}
@@ -1479,7 +1486,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
int fqdn, nomem;
char *canon;
if ((atnl = gettok(f, token)) == -1)
if ((atnl = gettok(f, token, MAXDNAMESTR)) == -1)
break;
fqdn = !!strchr(token, '.');
@@ -1793,7 +1800,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
/* Name in hosts, address doesn't match */
if (fail_crec)
{
inet_ntop(prot, &fail_crec->addr, daemon->namebuff, MAXDNAME);
inet_ntop(prot, &fail_crec->addr, daemon->namebuff, MAXDNAMESTR);
my_syslog(MS_DHCP | LOG_WARNING,
_("not giving name %s to the DHCP lease of %s because "
"the name exists in %s with address %s"),
+1 -1
View File
@@ -43,7 +43,7 @@
#define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */
#define DECLINE_BACKOFF 600 /* disable DECLINEd static addresses for this long */
#define DHCP_PACKET_MAX 16384 /* hard limit on DHCP packet size */
#define SMALLDNAME 50 /* most domain names are smaller than this */
#define SMALLDNAME 75 /* most domain names are smaller than this */
#define CNAME_CHAIN 10 /* chains longer than this atr dropped for loop protection */
#define DNSSEC_MIN_TTL 60 /* DNSKEY and DS records in cache last at least this long */
#define HOSTSFILE "/etc/hosts"
+4 -4
View File
@@ -736,7 +736,7 @@ static void add_dict_entry(DBusMessageIter *container, const char *key, const ch
static void add_dict_int(DBusMessageIter *container, const char *key, const unsigned int val)
{
snprintf(daemon->namebuff, MAXDNAME, "%u", val);
snprintf(daemon->namebuff, MAXDNAMESTR, "%u", val);
add_dict_entry(container, key, daemon->namebuff);
}
@@ -1055,7 +1055,7 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
DBusConnection *connection = (DBusConnection *)daemon->dbus;
DBusMessage* message = NULL;
DBusMessageIter args;
char *action_str, *mac = daemon->namebuff;
char *action_str, *mac;
unsigned char *p;
int i;
@@ -1068,7 +1068,7 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
{
print_mac(mac, lease->clid, lease->clid_len);
mac = print_mac(lease->clid, lease->clid_len);
inet_ntop(AF_INET6, &lease->addr6, daemon->addrbuff, ADDRSTRLEN);
}
else
@@ -1076,7 +1076,7 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
{
p = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len,
lease->hwaddr, lease->clid_len, lease->clid, &i);
print_mac(mac, p, i);
mac = print_mac(p, i);
inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN);
}
+5 -5
View File
@@ -278,9 +278,9 @@ void log_tags(struct dhcp_netid *netid, u32 xid)
if (!n)
{
strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
strncat (s, netid->net, MAXDNAMESTR - strlen(s));
if (netid->next)
strncat (s, ", ", (MAXDNAME-1) - strlen(s));
strncat (s, ", ", MAXDNAMESTR - strlen(s));
}
}
my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), xid, s);
@@ -936,11 +936,11 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len,
trunc = 1;
opt_len = 14;
}
print_mac(buf, val, opt_len);
*buf = 0;
strncat(buf, print_mac(val, opt_len), buf_len);
if (trunc)
strncat(buf, "...", buf_len - strlen(buf));
}
return ot[o].name ? ot[o].name : "";
+8 -7
View File
@@ -925,7 +925,8 @@ void dhcp_read_ethers(void)
{
FILE *f = fopen(ETHERSFILE, "r");
unsigned int flags;
char *buff = daemon->namebuff;
size_t linesz = 0;
char *line = NULL;
char *ip, *cp;
struct in_addr addr;
unsigned char hwaddr[ETHER_ADDR_LEN];
@@ -958,22 +959,22 @@ void dhcp_read_ethers(void)
up = &config->next;
}
while (fgets(buff, MAXDNAME, f))
while (get_line_alloc(f, &line, &linesz))
{
char *host = NULL;
lineno++;
while (strlen(buff) > 0 && isspace((unsigned char)buff[strlen(buff)-1]))
buff[strlen(buff)-1] = 0;
while (strlen(line) > 0 && isspace((unsigned char)line[strlen(line)-1]))
line[strlen(line)-1] = 0;
if ((*buff == '#') || (*buff == '+') || (*buff == 0))
if ((*line == '#') || (*line == '+') || (*line == 0))
continue;
for (ip = buff; *ip && !isspace((unsigned char)*ip); ip++);
for (ip = line; *ip && !isspace((unsigned char)*ip); ip++);
for(; *ip && isspace((unsigned char)*ip); ip++)
*ip = 0;
if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
if (!*ip || parse_hex(line, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
{
my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
continue;
+30 -2
View File
@@ -22,8 +22,34 @@
#define IN6ADDRSZ 16
#define INADDRSZ 4
/* Dnsmasq handles domains names internally as NULL-terminated C strings.
The '.' characters in these strings are significant as label-seperators.
'.' and /0 characters _within_ labels are escaped and take up two
characters to allow labels to contain arbitrary characters.
The maximum length of a domain name in wire format is 255 bytes.
and the maximum length of this when coverted to a <label>.<label> string
is 253 characters. Reasoning: the wire format representation of a
label is one byte length followed by up to length characters so each of
these labels except the last takes the same space since the length is
replaced by the '.' seperator.
The last label doesn't have the "." so it takes one less character
and the terminating zero-length label takes no character either.
When we introduce the dnsmasq escaped format, the maximum length
of this representation is when there are the minimum number of labels,
since that gives us the maximum number of characters that may need
escaping. The minimum number of labels is four, so the overhead
is five bytes (four non-zero lengths plus the zero length terminator)
leaving a total of 250 characters in labels. If every one of these
is escaped and becomes two characters, that gives 500 characters,
plus the three '.' seperators between the four labels, for a total of
503 characters.
*/
#define MAXDNAME 255 /* Maximum size of a domain name in wire format */
#define MAXDNAMESTR 503 /* Maximum size of dnsmasq c-string representaion of a domain name. */
#define PACKETSZ 512 /* maximum packet size */
#define MAXDNAME 1025 /* maximum presentation domain name */
#define RRFIXEDSZ 10 /* #/bytes of fixed data in r record */
#define MAXLABEL 63 /* maximum length of domain label */
@@ -185,10 +211,12 @@ struct dns_header {
#define ADD_RDLEN(header, pp, plen, len) \
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
/* Escape character in our presentation format for names.
/* Escape character in our internal c-string format for names.
Cannot be '.' or /000 and must be !isprint().
Note that escaped chars are stored as
<NAME_ESCAPE> <orig-char+1>
to ensure that the escaped form of /000 doesn't include /000
*/
#define NAME_ESCAPE 1
#define IS_NAME_ESCAPE(x) ((x) == 0 || (x) == '.' || (x) == NAME_ESCAPE)
+2 -7
View File
@@ -135,13 +135,8 @@ int main (int argc, char **argv)
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
/* Note that both /000 and '.' are allowed within labels. These get
represented in presentation format using NAME_ESCAPE as an escape
character. In theory, if all the characters in a name were /000 or
'.' or NAME_ESCAPE then all would have to be escaped, so the
presentation format would be twice as long as the spec. */
daemon->keyname = safe_malloc((MAXDNAME * 2) + 1);
daemon->cname = safe_malloc((MAXDNAME * 2) + 1);
daemon->keyname = safe_malloc(MAXDNAMESTR + 1);
daemon->cname = safe_malloc(MAXDNAMESTR + 1);
/* one char flag per possible RR in answer section (may get extended). */
daemon->rr_status_sz = 64;
daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz);
+11 -10
View File
@@ -486,7 +486,7 @@ struct interface_name {
};
union bigname {
char name[MAXDNAME];
char name[MAXDNAMESTR+1];
union bigname *next; /* freelist */
};
@@ -1271,10 +1271,10 @@ extern struct daemon {
/* globally used stuff for DNS */
char *packet; /* packet buffer */
int packet_buff_sz; /* size of above */
char *namebuff; /* MAXDNAME size buffer */
char *namebuff; /* MAXDNAMESTR+1 size buffer */
char *workspacename;
#ifdef HAVE_DNSSEC
char *keyname, *cname; /* MAXDNAME size buffer */
char *keyname, *cname; /* MAXDNAMESTR+1 size buffer */
unsigned long *rr_status; /* ceiling in TTL from DNSSEC or zero for insecure */
int rr_status_sz;
int dnssec_no_time_check;
@@ -1468,7 +1468,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
/* dnssec.c */
#ifdef HAVE_DNSSEC
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int id, int type);
size_t dnssec_generate_query(struct dns_header *header, size_t outlen, char *name, int class, int id, int type);
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name,
char *keyname, int class, int *validate_count);
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name,
@@ -1500,7 +1500,7 @@ u64 rand64(void);
int rr_on_list(struct rrlist *list, unsigned short rr);
int legal_hostname(char *name);
char *canonicalise(char *in, int *nomem);
unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit);
unsigned char *do_rfc1035_name(unsigned char *p, char *sval, unsigned char *limit);
void *safe_malloc(size_t size);
void safe_strncpy(char *dest, const char *src, size_t size);
void safe_pipe(int *fd, int read_noblock);
@@ -1516,6 +1516,7 @@ void *whine_malloc_real(const char *func, unsigned int line, size_t size);
void *whine_realloc_real(const char *wrapper, const char *func, unsigned int line, void *ptr, size_t size);
int expand_buf_real(const char *func, unsigned int line, struct iovec *iov, size_t size);
int expand_workspace_real(const char *func, unsigned int line, unsigned char ***wkspc, int *szp, int new);
int get_line_alloc(FILE *f, char **buffp, size_t *sizep);
int sa_len(union mysockaddr *addr);
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2);
int sockaddr_isnull(const union mysockaddr *s);
@@ -1537,7 +1538,7 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
unsigned int *wildcard_mask, int *mac_type);
int memcmp_masked(unsigned char *a, unsigned char *b, int len,
unsigned int mask);
char *print_mac(char *buff, unsigned char *mac, int len);
char *print_mac(unsigned char *mac, int len);
int read_write(int fd, unsigned char *packet, int size, int rw);
int read_writev(int fd, struct iovec *iov, int iovcnt, int rw);
void close_fds(long max_fd, int spare1, int spare2, int spare3);
@@ -1938,11 +1939,11 @@ void from_wire(char *name);
/* edns0.c */
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
size_t *len, unsigned char **p, int *is_sign, int *is_last);
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
size_t add_pseudoheader(struct dns_header *header, size_t plen, size_t out_size,
int optno, unsigned char *opt, size_t optlen, int set_do, int replace);
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit);
size_t add_do_bit(struct dns_header *header, size_t plen, size_t outlen);
void edns0_needs_mac(union mysockaddr *addr, time_t now);
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
size_t add_edns0_config(struct dns_header *header, size_t plen, size_t outlen,
union mysockaddr *source, time_t now, int *cacheable);
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
@@ -1965,7 +1966,7 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout);
int filter_servers(int seed, int flags, int *lowout, int *highout);
int is_local_answer(time_t now, int first, char *name);
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header,
char *name, char *limit, int first, int last, int ede);
char *name, size_t limit, int first, int last, int ede);
int server_samegroup(struct server *a, struct server *b);
#ifdef HAVE_DNSSEC
int dnssec_server(struct server *server, char *keyname, int is_ds, int *firstp, int *lastp);
+89 -103
View File
@@ -144,7 +144,7 @@ static int is_check_date(unsigned long curtime)
Init state->ip with the RR, and state->end with the end of same.
Init state->op to NULL.
Init state->desc to RR descriptor.
Init state->buff with a MAXDNAME * 2 buffer.
Init state->buff with a MAXDNAMESTR+1 buffer.
After each call which returns 1, state->op points to the next byte of data.
On returning 0, the end has been reached.
@@ -177,7 +177,7 @@ static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state
if ((state->c = state->end - state->ip) != 0)
{
state->op = state->ip;
state->ip = state->end;;
state->ip = state->end;
}
else
return 0;
@@ -189,24 +189,28 @@ static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state
if (d == (u16)0)
{
/* domain-name, canonicalise */
int len;
/* If the name is malformed, or runs beyond the end of the RR, abort.
under no circumstances should state->ip be beyond state->end since
that will cause the run-out code above to turn the negative
calculted length into a very large postive value for state->c
and overrun the buffer. */
if (!extract_name(header, plen, &state->ip, state->buff, EXTR_NAME_EXTRACT, 0) ||
(len = to_wire(state->buff)) == 0)
continue;
state->c = len;
state->ip > state->end)
return 0;
/* to_wire() always returns length > 0 */
state->c = to_wire(state->buff);
state->op = (unsigned char *)state->buff;
}
else
{
/* plain data preceding a domain-name, don't run off the end of the data */
if ((state->end - state->ip) < d)
d = state->end - state->ip;
if (d == 0)
continue;
/* Plain data preceding a domain-name.
Handle short RR, if there are no bytes left, return finished. */
if ((state->end - state->ip) < d &&
(d = state->end - state->ip) == 0)
return 0;
state->op = state->ip;
state->c = d;
state->ip += d;
@@ -383,7 +387,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
an attacker using signatures made with the key of an unrelated
zone he controls. Note that the root key is always allowed.
Ignore sigs which aren't valid */
if (*daemon->workspacename == 0 || hostname_issubdomain(name, daemon->workspacename) != 0)
if (*daemon->workspacename == 0 || hostname_issubdomain(daemon->workspacename, name) != 0)
{
if (gotkey)
{
@@ -543,10 +547,14 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
*ttl_out = ttl;
}
/* Don't trust rdlen not to be too small and give us a negative sig_len
It has already been checked that it doesn't run us off the end
of the packet. */
if ((sig_len = rdlen - (p - psav)) <= 0)
return STAT_BOGUS;
sig = p;
sig_len = rdlen - (p - psav);
nsigttl = htonl(orig_ttl);
hash->update(ctx, 18, psav);
@@ -1243,15 +1251,23 @@ static int hostname_cmp(const char *a, const char *b)
}
}
static int check_type_bitmap(unsigned char *p, int type)
{
int offset = (type & 0xff) >> 3;
if (p[0] == type >> 8 && p[1] > offset && (p[offset + 2] & (0x80 >> (type & 0x07))))
return 1;
return 0;
}
/* returns 0 on success, or DNSSEC_FAIL_* value on failure. */
static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, unsigned char **labels, int nsec_count,
char *workspace1_in, char *workspace2, char *name, int type, int *nons)
{
int i, rc, rdlen;
unsigned char *p, *psave;
int offset = (type & 0xff) >> 3;
int mask = 0x80 >> (type & 0x07);
if (nons)
*nons = 1;
@@ -1295,17 +1311,6 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
/* rdlen is now length of type map, and p points to it
packet checked to be as long as rdlen implies in prove_non_existence() */
/* check that the first typemap is complete. */
if (rdlen < 2 || rdlen < p[1] + 2)
return DNSSEC_FAIL_BADPACKET;
/* RFC 6672 5.3.4.1. */
#define DNAME_OFFSET (T_DNAME >> 3)
#define DNAME_MASK (0x80 >> (T_DNAME & 0x07))
if (p[0] == 0 && (p[1] >= DNAME_OFFSET + 1) && (p[2 + DNAME_OFFSET] & DNAME_MASK) != 0 &&
hostname_issubdomain(name, workspace1) == 1)
return DNSSEC_FAIL_NONSEC;
rc = hostname_cmp(workspace1, name);
if (rc == 0)
@@ -1314,42 +1319,37 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
if (type == T_NSEC || type == T_RRSIG)
return 0;
/* NSEC with the same name as the RR we're testing, check
that the type in question doesn't appear in the type map */
if (p[0] == 0 && p[1] >= 1)
{
/* If we can prove that there's no NS record, return that information. */
if (nons && (p[2] & (0x80 >> T_NS)) != 0)
*nons = 0;
/* A CNAME answer would also be valid, so if there's a CNAME is should
have been returned. */
if ((p[2] & (0x80 >> T_CNAME)) != 0)
return DNSSEC_FAIL_NONSEC;
/* If the SOA bit is set for a DS record, then we have the
DS from the wrong side of the delegation. For the root DS,
this is expected. */
if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
return DNSSEC_FAIL_NONSEC;
}
while (rdlen > 0)
{
if (rdlen < 2 || rdlen < p[1] + 2)
return DNSSEC_FAIL_BADPACKET;
if (p[0] == type >> 8)
{
/* Does the NSEC say our type exists? */
if (offset < p[1] && (p[offset+2] & mask) != 0)
return DNSSEC_FAIL_NONSEC;
break; /* finished checking */
}
/* If we can prove that there's no NS record, return that information. */
if (nons && check_type_bitmap(p, T_NS))
*nons = 0;
rdlen -= p[1];
p += p[1];
/* NSEC with the same name as the RR we're testing, check
that the type in question doesn't appear in the type map */
if (check_type_bitmap(p, type))
return DNSSEC_FAIL_NONSEC;
/* A CNAME answer would also be valid, so if there's a CNAME it should
have been returned. */
if (check_type_bitmap(p, T_CNAME))
return DNSSEC_FAIL_NONSEC;
/* If the SOA bit is set for a DS record, then we have the
DS from the wrong side of the delegation. For the root DS,
this is expected. */
if (name_labels != 0 && type == T_DS && check_type_bitmap(p, T_SOA))
return DNSSEC_FAIL_NONSEC;
/* RFC 6672 5.3.4.1. */
if (check_type_bitmap(p, T_DNAME) && hostname_issubdomain(name, workspace1) == 1)
return DNSSEC_FAIL_NONSEC;
rdlen -= p[1] + 2;
p += p[1] + 2;
}
return 0;
@@ -1437,6 +1437,7 @@ static int base32_decode(char *in, unsigned char *out)
return p - out;
}
/* return 1 if we can prove record _doesn't_ exist */
static int check_nsec3_coverage(struct dns_header *header, size_t plen, int digest_len, unsigned char *digest, int type,
char *workspace1, char *workspace2, unsigned char **nsecs, int nsec_count, int *nons, int name_labels)
{
@@ -1473,50 +1474,35 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
we just need to check the type map. p points to the RR data for the record.
Note we have packet length up to rdlen bytes checked. */
int offset = (type & 0xff) >> 3;
int mask = 0x80 >> (type & 0x07);
p += hash_len; /* skip next-domain hash */
rdlen -= p - psave;
/* check that the first typemap is complete. */
if (rdlen < 2 || rdlen < p[1] + 2)
return DNSSEC_FAIL_BADPACKET;
if (p[0] == 0 && p[1] >= 1)
{
/* If we can prove that there's no NS record, return that information. */
if (nons && (p[2] & (0x80 >> T_NS)) != 0)
*nons = 0;
/* A CNAME answer would also be valid, so if there's a CNAME is should
have been returned. */
if ((p[2] & (0x80 >> T_CNAME)) != 0)
return 0;
/* If the SOA bit is set for a DS record, then we have the
DS from the wrong side of the delegation. For the root DS,
this is expected. */
if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
return 0;
}
while (rdlen > 0)
{
if (rdlen < 2 || rdlen < p[1] + 2)
return DNSSEC_FAIL_BADPACKET;
return 0;
/* If we can prove that there's no NS record, return that information. */
if (nons && check_type_bitmap(p, T_NS))
*nons = 0;
if (p[0] == type >> 8)
{
/* Does the NSEC3 say our type exists? */
if (offset < p[1] && (p[offset+2] & mask) != 0)
return 0;
break; /* finished checking */
}
rdlen -= p[1];
p += p[1];
/* Does the NSEC3 say our type exists? */
if (check_type_bitmap(p, type))
return 0;
/* A CNAME answer would also be valid, so if there's a CNAME it should
have been returned. */
if (check_type_bitmap(p, T_CNAME))
return 0;
/* If the SOA bit is set for a DS record, then we have the
DS from the wrong side of the delegation. For the root DS,
this is expected. */
if (name_labels != 0 && type == T_DS && check_type_bitmap(p, T_SOA))
return 0;
rdlen -= p[1] + 2;
p += p[1] + 2;
}
return 1;
@@ -2113,8 +2099,8 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
CNAME must be <subdomain>.<dname>
CNAME target must be <subdomain>.<dname_target>
<subdomain>s must match for name and target. */
if (hostname_issubdomain(daemon->cname, name) == 1 &&
hostname_issubdomain(daemon->workspacename, keyname) == 1 &&
if (hostname_issubdomain(name, daemon->cname) == 1 &&
hostname_issubdomain(keyname, daemon->workspacename) == 1 &&
name_prefix_len == strlen(daemon->workspacename) - strlen(keyname))
{
char save = daemon->cname[name_prefix_len];
@@ -2350,7 +2336,7 @@ int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen)
}
}
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name,
size_t dnssec_generate_query(struct dns_header *header, size_t outlen, char *name,
int class, int id, int type)
{
unsigned char *p;
@@ -2374,7 +2360,7 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char
PUTSHORT(type, p);
PUTSHORT(class, p);
return add_do_bit(header, p - (unsigned char *)header, end);
return add_do_bit(header, p - (unsigned char *)header, outlen);
}
int errflags_to_ede(int status)
+4 -3
View File
@@ -408,12 +408,13 @@ int is_local_answer(time_t now, int first, char *name)
return rc;
}
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede)
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, size_t limit, int first, int last, int ede)
{
int trunc = 0, anscount = 0;
unsigned char *p;
int start;
union all_addr addr;
char *out_end = ((char *)header) + limit;
setup_reply(header, flags, ede);
@@ -443,7 +444,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
else
addr.addr4 = srv->addr;
if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr))
if (add_resource_record(header, out_end, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr))
anscount++;
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL, 0);
}
@@ -458,7 +459,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
else
addr.addr6 = srv->addr;
if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr))
if (add_resource_record(header, out_end, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr))
anscount++;
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL, 0);
}
+9 -9
View File
@@ -162,12 +162,12 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
if (c->indexed)
{
unsigned int index = ntohl(addr->addr4.s_addr) - ntohl(c->start.s_addr);
snprintf(name, MAXDNAME, "%s%u", c->prefix ? c->prefix : "", index);
snprintf(name, MAXDNAMESTR, "%s%u", c->prefix ? c->prefix : "", index);
}
else
{
if (c->prefix)
strncpy(name, c->prefix, MAXDNAME - ADDRSTRLEN);
strncpy(name, c->prefix, MAXDNAMESTR - ADDRSTRLEN);
inet_ntop(AF_INET, &addr->addr4, name + strlen(name), ADDRSTRLEN);
for (p = name; *p; p++)
@@ -175,8 +175,8 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
*p = '-';
}
strncat(name, ".", MAXDNAME - strlen(name) - 1);
strncat(name, c->domain, MAXDNAME - strlen(name) - 1);
strncat(name, ".", MAXDNAMESTR - strlen(name) - 1);
strncat(name, c->domain, MAXDNAMESTR - strlen(name) - 1);
return 1;
}
@@ -188,7 +188,7 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
if (c->indexed)
{
u64 index = addr6part(&addr->addr6) - addr6part(&c->start6);
snprintf(name, MAXDNAME, "%s%llu", c->prefix ? c->prefix : "", index);
snprintf(name, MAXDNAMESTR, "%s%llu", c->prefix ? c->prefix : "", index);
}
else
{
@@ -196,17 +196,17 @@ int is_rev_synth(int flag, union all_addr *addr, char *name)
char frag[6];
if (c->prefix)
strncpy(name, c->prefix, MAXDNAME);
strncpy(name, c->prefix, MAXDNAMESTR);
for (i = 0; i < 16; i += 2)
{
sprintf(frag, "%s%02x%02x", i == 0 ? "" : "-", addr->addr6.s6_addr[i], addr->addr6.s6_addr[i+1]);
strncat(name, frag, MAXDNAME - strlen(name) - 1);
strncat(name, frag, MAXDNAMESTR - strlen(name) - 1);
}
}
strncat(name, ".", MAXDNAME - strlen(name) - 1);
strncat(name, c->domain, MAXDNAME - strlen(name) - 1);
strncat(name, ".", MAXDNAMESTR - strlen(name) - 1);
strncat(name, c->domain, MAXDNAMESTR - strlen(name) - 1);
return 1;
}
+25 -22
View File
@@ -100,13 +100,14 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t
replace == 1 ->replace existing or add option
replace == 2 ->relpace existing option only.
*/
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
size_t add_pseudoheader(struct dns_header *header, size_t plen, size_t out_size,
int optno, unsigned char *opt, size_t optlen, int set_do, int replace)
{
unsigned char *lenp = NULL, *datap = NULL, *p, *udp_len, *buff = NULL;
int rdlen = 0, is_sign, is_last;
unsigned short flags = set_do ? 0x8000 : 0, rcode = 0;
unsigned char *limit = ((unsigned char *)header) + out_size;
p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last);
if (is_sign)
@@ -247,9 +248,9 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
return p - (unsigned char *)header;
}
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit)
size_t add_do_bit(struct dns_header *header, size_t plen, size_t outlen)
{
return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1, 0);
return add_pseudoheader(header, plen, outlen, 0, NULL, 0, 1, 0);
}
static unsigned char char64(unsigned char c)
@@ -276,14 +277,16 @@ void edns0_needs_mac(union mysockaddr *addr, time_t now)
/* OPT_ADD_MAC = MAC is added (if available)
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
OPT_STRIP_MAC = MAC is removed */
static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit,
OPT_STRIP_MAC = MAC is removed
Handle only six byte MACs. */
static size_t add_dns_client(struct dns_header *header, size_t plen, size_t outlen,
union mysockaddr *l3, time_t now, int *cacheablep)
{
int replace = 0, maclen = 0;
unsigned char mac[DHCP_CHADDR_MAX];
char encode[18]; /* handle 6 byte MACs ONLY */
char encode[9] = { 0 };
char *encoded = encode;
if ((option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX)) && (maclen = find_mac(l3, mac, 1, now)) == 6)
{
if (option_bool(OPT_STRIP_MAC))
@@ -291,7 +294,7 @@ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned ch
*cacheablep = 0;
if (option_bool(OPT_MAC_HEX))
print_mac(encode, mac, maclen);
encoded = print_mac(mac, maclen);
else
{
encoder(mac, encode);
@@ -303,7 +306,7 @@ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned ch
replace = 2;
if (replace != 0 || maclen == 6)
plen = add_pseudoheader(header, plen, limit, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
plen = add_pseudoheader(header, plen, outlen, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encoded, strlen(encoded), 0, replace);
return plen;
}
@@ -312,7 +315,7 @@ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned ch
/* OPT_ADD_MAC = MAC is added (if available)
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
OPT_STRIP_MAC = MAC is removed */
static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit,
static size_t add_mac(struct dns_header *header, size_t plen, size_t outlen,
union mysockaddr *l3, time_t now, int *cacheablep)
{
int maclen = 0, replace = 0;
@@ -328,7 +331,7 @@ static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *lim
replace = 2;
if (replace != 0 || maclen != 0)
plen = add_pseudoheader(header, plen, limit, EDNS0_OPTION_MAC, mac, maclen, 0, replace);
plen = add_pseudoheader(header, plen, outlen, EDNS0_OPTION_MAC, mac, maclen, 0, replace);
return plen;
}
@@ -409,7 +412,7 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source,
/* OPT_CLIENT_SUBNET = client subnet is added
OPT_CLIENT_SUBNET + OPT_STRIP_ECS = client subnet is replaced
OPT_STRIP_ECS = client subnet is removed */
static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit,
static size_t add_source_addr(struct dns_header *header, size_t plen, size_t outlen,
union mysockaddr *source, int *cacheable)
{
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
@@ -439,7 +442,7 @@ static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned c
return plen;
}
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace);
return add_pseudoheader(header, plen, outlen, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace);
}
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
@@ -516,7 +519,7 @@ struct umbrella_opt {
u8 fields[4 * UMBRELLA_TYPESZ + UMBRELLA_ORGSZ + IN6ADDRSZ + UMBRELLA_DEVICESZ + UMBRELLA_ASSETSZ];
};
static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
static size_t add_umbrella_opt(struct dns_header *header, size_t plen, size_t outlen, union mysockaddr *source, int *cacheable)
{
*cacheable = 0;
@@ -548,28 +551,28 @@ static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned
PUTLONG(daemon->umbrella_asset, u);
}
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1);
return add_pseudoheader(header, plen, outlen, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1);
}
/* Set *check_subnet if we add a client subnet option, which needs to checked
in the reply. Set *cacheable to zero if we add an option which the answer
may depend on. */
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
size_t add_edns0_config(struct dns_header *header, size_t plen, size_t outlen,
union mysockaddr *source, time_t now, int *cacheable)
{
*cacheable = 1;
plen = add_mac(header, plen, limit, source, now, cacheable);
plen = add_dns_client(header, plen, limit, source, now, cacheable);
plen = add_mac(header, plen, outlen, source, now, cacheable);
plen = add_dns_client(header, plen, outlen, source, now, cacheable);
if (daemon->dns_client_id)
plen = add_pseudoheader(header, plen, limit, EDNS0_OPTION_NOMCPEID,
plen = add_pseudoheader(header, plen, outlen, EDNS0_OPTION_NOMCPEID,
(unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
if (option_bool(OPT_UMBRELLA))
plen = add_umbrella_opt(header, plen, limit, source, cacheable);
plen = add_umbrella_opt(header, plen, outlen, source, cacheable);
plen = add_source_addr(header, plen, limit, source, cacheable);
plen = add_source_addr(header, plen, outlen, source, cacheable);
return plen;
}
+22 -22
View File
@@ -375,7 +375,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
plen = add_do_bit(header, plen, ((unsigned char *) header) + daemon->edns_pktsz);
plen = add_do_bit(header, plen, daemon->edns_pktsz);
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
this allows it to select auth servers when one is returning bad data. */
@@ -514,7 +514,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
forwarded = 0;
/* Advertise the size of UDP reply we can accept. */
plen = add_pseudoheader(header, plen, (unsigned char *)(header + daemon->edns_pktsz), 0, NULL, 0, 0, 0);
plen = add_pseudoheader(header, plen, daemon->edns_pktsz, 0, NULL, 0, 0, 0);
/* check for send errors here (no route to host)
if we fail to send to all nameservers, send back an error
@@ -589,7 +589,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
reply:
if (udpfd != -1)
{
if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, (char *)(header + replylimit), first, last, ede)))
if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, replylimit, first, last, ede)))
return;
if (fwd_flags & FREC_HAS_PHEADER)
@@ -597,9 +597,9 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
u16 swap = htons((u16)ede);
if (ede != EDE_UNSET)
plen = add_pseudoheader(header, plen, (unsigned char *)(header + replylimit), EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, 0, 0);
plen = add_pseudoheader(header, plen, replylimit, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, 0, 0);
else
plen = add_pseudoheader(header, plen, (unsigned char *)(header + replylimit), 0, NULL, 0, 0, 0);
plen = add_pseudoheader(header, plen, replylimit, 0, NULL, 0, 0, 0);
}
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
@@ -695,7 +695,7 @@ static struct ipsets *domain_find_sets(struct ipsets *setlist, const char *domai
static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind,
int no_cache, int cache_secure, int bogusanswer, int ad_reqd, int do_bit, int added_pheader,
union mysockaddr *query_source, unsigned char *limit, int ede)
union mysockaddr *query_source, size_t outlen, int ede)
{
unsigned char *pheader, *sizep;
struct ipsets *ipsets = NULL, *nftsets = NULL;
@@ -724,7 +724,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
/* Get extended RCODE. */
rcode |= sizep[2] << 4;
if (option_bool(OPT_CLIENT_SUBNET) && !check_source(header, plen, pheader, query_source))
if (option_bool(OPT_CLIENT_SUBNET) && !check_source(header, n, pheader, query_source))
{
my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
return 0;
@@ -879,7 +879,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (pheader && ede != EDE_UNSET)
{
u16 swap = htons((u16)ede);
n = add_pseudoheader(header, n, limit, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1);
n = add_pseudoheader(header, n, outlen, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1);
}
if (RCODE(header) == NXDOMAIN)
@@ -1027,7 +1027,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
allocation of a new one: third arg of get_new_frec() does that. */
if ((serverind = dnssec_server(forward->sentto, daemon->keyname, STAT_ISEQUAL(status, STAT_NEED_DS), NULL, NULL)) != -1 &&
(server = daemon->serverarray[serverind]) &&
(nn = dnssec_generate_query(header, ((unsigned char *) header) + daemon->edns_pktsz,
(nn = dnssec_generate_query(header, daemon->edns_pktsz,
daemon->keyname, forward->class, get_id(),
STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS)) &&
(fd = allocate_rfd(&rfds, server)) != -1 &&
@@ -1442,7 +1442,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer,
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION,
!(forward->flags & FREC_HAS_PHEADER), &forward->frec_src.source,
((unsigned char *)header) + daemon->edns_pktsz, ede)))
daemon->edns_pktsz, ede)))
{
struct frec_src *src, *prev;
int do_trunc;
@@ -1945,7 +1945,7 @@ void receive_query(struct listener *listen, time_t now)
{
int cacheable;
n = add_edns0_config(header, n, ((unsigned char *)header) + daemon->edns_pktsz, &source_addr, now, &cacheable);
n = add_edns0_config(header, n, daemon->edns_pktsz, &source_addr, now, &cacheable);
saved_question = blockdata_alloc((char *) header, (size_t)n);
if (!cacheable)
@@ -1979,12 +1979,12 @@ void receive_query(struct listener *listen, time_t now)
{
u16 swap = htons(ede);
m = add_pseudoheader(header, m, ((unsigned char *) header) + daemon->edns_pktsz,
EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
m = add_pseudoheader(header, m, daemon->edns_pktsz, EDNS0_OPTION_EDE,
(unsigned char *)&swap, 2, do_bit, 0);
}
else
m = add_pseudoheader(header, m, ((unsigned char *) header) + daemon->edns_pktsz,
0, NULL, 0, do_bit, 0);
m = add_pseudoheader(header, m, daemon->edns_pktsz, 0,
NULL, 0, do_bit, 0);
}
#ifdef HAVE_DUMPFILE
@@ -2326,7 +2326,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
query_header = (struct dns_header *)daemon->packet;
daemon->srv_save = NULL;
m = dnssec_generate_query(query_header, ((unsigned char *)query_header) + daemon->edns_pktsz, keyname, class, 0,
m = dnssec_generate_query(query_header, daemon->edns_pktsz, keyname, class, 0,
STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS);
if ((start = dnssec_server(server, keyname, STAT_ISEQUAL(new_status, STAT_NEED_DS), &first, &last)) == -1 ||
@@ -2479,7 +2479,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
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);
size = add_edns0_config(header, size, daemon->packet_buff_sz, &peer_addr, now, &cacheable);
/* Clear buffer to avoid risk of information disclosure. */
memset(bigbuff->iov_base, 0, bigbuff->iov_len);
@@ -2620,7 +2620,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
size = add_do_bit(header, size, ((unsigned char *) header) + daemon->edns_pktsz);
size = add_do_bit(header, size, daemon->edns_pktsz);
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
this allows it to select auth servers when one is returning bad data. */
@@ -2715,7 +2715,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
m = process_reply(out_header, now, serv, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
ad_reqd, do_bit, !have_pseudoheader, &peer_addr, ((unsigned char *)out_header) + 65536, ede);
ad_reqd, do_bit, !have_pseudoheader, &peer_addr, 65536, ede);
/* process_reply() adds pheader itself */
have_pseudoheader = 0;
@@ -2731,7 +2731,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
if (m == 0)
{
if (!(m = make_local_answer(flags, gotname, size, out_header, daemon->namebuff,
((char *) out_header) + 65536, first, last, ede)))
65536, first, last, ede)))
break;
}
else if (ede == EDE_UNSET)
@@ -2747,9 +2747,9 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
u16 swap = htons((u16)ede);
if (ede != EDE_UNSET)
m = add_pseudoheader(out_header, m, ((unsigned char *) out_header) + 65536, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
m = add_pseudoheader(out_header, m, 65536, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
else
m = add_pseudoheader(out_header, m, ((unsigned char *) out_header) + 65536, 0, NULL, 0, do_bit, 0);
m = add_pseudoheader(out_header, m, 65536, 0, NULL, 0, do_bit, 0);
}
check_log_writer(1);
+9 -15
View File
@@ -253,7 +253,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
}
/* supplied data may just exceed normal buffer (unlikely) */
if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAMESTR &&
!(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
continue;
@@ -261,8 +261,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
data.hostname_len + data.ed_len + data.clid_len, RW_READ))
continue;
/* CLID into packet */
for (p = daemon->packet, i = 0; i < data.clid_len; i++)
/* CLID into packet: limit to 100 bytes to avoid overflowing buffer. */
for (p = daemon->packet, i = 0; i < data.clid_len && i < 100; i++)
{
p += sprintf(p, "%.2x", buf[i]);
if (i != data.clid_len - 1)
@@ -533,18 +533,12 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
close(pipeout[0]);
else
{
while (fgets(daemon->packet, daemon->packet_buff_sz, fp))
{
/* do not include new lines, log will append them */
size_t len = strlen(daemon->packet);
if (len > 0)
{
--len;
if (daemon->packet[len] == '\n')
daemon->packet[len] = 0;
}
send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet);
}
char *line = NULL;
size_t linesz = 0;
while (get_line_alloc(fp, &line, &linesz))
send_event(event_fd, EVENT_SCRIPT_LOG, 0, line);
fclose(fp);
}
}
+1 -1
View File
@@ -35,7 +35,7 @@ static int read_leases(time_t now, FILE *leasestream)
Check various buffers are big enough for the code below */
#if (DHCP_BUFF_SZ < 255) || (MAXDNAME < 64) || (PACKETSZ+MAXDNAME+RRFIXEDSZ < 764)
#if (DHCP_BUFF_SZ < 255) || (MAXDNAMESTR < 64) || (PACKETSZ+MAXDNAME+RRFIXEDSZ < 764)
# error Buffer size breakage in leasefile parsing.
#endif
+5 -5
View File
@@ -1699,19 +1699,19 @@ void check_servers(int no_loop_check)
int reload_servers(char *fname)
{
FILE *f;
char *line;
char *line = NULL;
size_t linesz = 0;
int gotone = 0;
/* buff happens to be MAXDNAME long... */
if (!(f = fopen(fname, "r")))
{
my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno));
return 0;
}
mark_servers(SERV_FROM_RESOLV);
while ((line = fgets(daemon->namebuff, MAXDNAME, f)))
while (get_line_alloc(f, &line, &linesz))
{
union mysockaddr addr, source_addr;
char *token = strtok(line, " \t\n\r");
+41 -38
View File
@@ -5471,9 +5471,20 @@ err:
static void read_file(char *file, FILE *f, int hard_opt, int from_script)
{
volatile int lineno = 0;
char *buff = daemon->namebuff;
char *buff;
size_t buffsz;
/* Memory allocation failure longjmps here if mem_recover == 1 */
if (hard_opt != 0)
{
setjmp(mem_jmp);
mem_recover = 1;
}
while (fgets(buff, MAXDNAME, f))
buff = NULL;
buffsz = 0;
while (get_line_alloc(f, &buff, &buffsz))
{
int white, i;
volatile int option;
@@ -5481,15 +5492,7 @@ static void read_file(char *file, FILE *f, int hard_opt, int from_script)
size_t len;
option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
/* Memory allocation failure longjmps here if mem_recover == 1 */
if (option != 0 || hard_opt == LOPT_REV_SERV)
{
if (setjmp(mem_jmp))
continue;
mem_recover = 1;
}
arg = NULL;
lineno++;
errmess = NULL;
@@ -5948,23 +5951,17 @@ void reread_dhcp(void)
void read_opts(int argc, char **argv, char *compile_opts)
{
size_t argbuf_size = MAXDNAME;
char *argbuf = opt_malloc(argbuf_size);
/* Note that both /000 and '.' are allowed within labels. These get
represented in presentation format using NAME_ESCAPE as an escape
character. In theory, if all the characters in a name were /000 or
'.' or NAME_ESCAPE then all would have to be escaped, so the
presentation format would be twice as long as the spec. */
char *buff = opt_malloc((MAXDNAME * 2) + 1);
size_t argbuf_size = 300;
char *argbuf = safe_malloc(argbuf_size);
int option, testmode = 0;
char *arg, *conffile = NULL;
opterr = 0;
daemon = opt_malloc(sizeof(struct daemon));
memset(daemon, 0, sizeof(struct daemon));
daemon->namebuff = buff;
daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1);
daemon->namebuff = safe_malloc(MAXDNAMESTR+1);
daemon->workspacename = safe_malloc(MAXDNAMESTR+1);
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
/* Set defaults - everything else is zero or NULL */
@@ -6202,9 +6199,9 @@ void read_opts(int argc, char **argv, char *compile_opts)
/* create default, if not specified */
if (daemon->authserver && !daemon->hostmaster)
{
strcpy(buff, "hostmaster.");
strcat(buff, daemon->authserver);
daemon->hostmaster = opt_string_alloc(buff);
strcpy(daemon->namebuff, "hostmaster.");
strncat(daemon->namebuff, daemon->authserver, MAXDNAMESTR - strlen(daemon->namebuff));
daemon->hostmaster = opt_string_alloc(daemon->namebuff);
}
if (!daemon->dhcp_pxe_vendors)
@@ -6219,11 +6216,11 @@ void read_opts(int argc, char **argv, char *compile_opts)
{
struct mx_srv_record *mx;
if (gethostname(buff, MAXDNAME) == -1)
if (gethostname(daemon->namebuff, MAXDNAMESTR) == -1)
die(_("cannot get host-name: %s"), NULL, EC_MISC);
for (mx = daemon->mxnames; mx; mx = mx->next)
if (!mx->issrv && hostname_isequal(mx->name, buff))
if (!mx->issrv && hostname_isequal(mx->name, daemon->namebuff))
break;
if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
@@ -6232,12 +6229,12 @@ void read_opts(int argc, char **argv, char *compile_opts)
mx->next = daemon->mxnames;
mx->issrv = 0;
mx->target = NULL;
mx->name = opt_string_alloc(buff);
mx->name = opt_string_alloc(daemon->namebuff);
daemon->mxnames = mx;
}
if (!daemon->mxtarget)
daemon->mxtarget = opt_string_alloc(buff);
daemon->mxtarget = opt_string_alloc(daemon->namebuff);
for (mx = daemon->mxnames; mx; mx = mx->next)
if (!mx->issrv && !mx->target)
@@ -6252,7 +6249,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
if (option_bool(OPT_RESOLV_DOMAIN))
{
char *line;
char *line = NULL;
size_t linesz = 0;
FILE *f;
if (option_bool(OPT_NO_RESOLV) ||
@@ -6263,7 +6261,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
if (!(f = fopen((daemon->resolv_files)->name, "r")))
die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
while ((line = fgets(buff, MAXDNAME, f)))
while (get_line_alloc(f, &line, &linesz))
{
char *token = strtok(line, " \t\n\r");
@@ -6272,7 +6270,11 @@ void read_opts(int argc, char **argv, char *compile_opts)
if ((token = strtok(NULL, " \t\n\r")) &&
(daemon->domain_suffix = canonicalise_opt(token)))
break;
{
/* We don't call get_line_alloc() until it returns false and frees the automatically */
get_line_alloc(NULL, &line, &linesz);
break;
}
}
fclose(f);
@@ -6283,7 +6285,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
if (daemon->domain_suffix)
{
/* add domain for any srv record without one. */
/* add domain for any srv record without one. */
struct mx_srv_record *srv;
for (srv = daemon->mxnames; srv; srv = srv->next)
@@ -6291,13 +6293,14 @@ void read_opts(int argc, char **argv, char *compile_opts)
strchr(srv->name, '.') &&
strchr(srv->name, '.') == strrchr(srv->name, '.'))
{
if (strlen(srv->name) + 1 + strlen(daemon->domain_suffix) > MAXDNAME)
if (strlen(srv->name) + 1 + strlen(daemon->domain_suffix) > MAXDNAMESTR)
die(_("srv-host name %s too long after domain appended"), srv->name, EC_MISC);
strcpy(buff, srv->name);
strcat(buff, ".");
strcat(buff, daemon->domain_suffix);
strcpy(daemon->namebuff, srv->name);
strcat(daemon->namebuff, ".");
strcat(daemon->namebuff, daemon->domain_suffix);
free(srv->name);
srv->name = opt_string_alloc(buff);
if (!(srv->name = canonicalise_opt(daemon->namebuff)))
die(_("bad srv-host name %s after domain appended"), srv->name, EC_MISC);
}
}
else if (option_bool(OPT_DHCP_FQDN))
+3 -6
View File
@@ -215,11 +215,8 @@ void icmp6_packet(time_t now)
if (opt_sz == 0 || opt_sz > rem)
return; /* Bad packet */
if (p[0] == ICMP6_OPT_SOURCE_MAC && ((opt_sz - 2) * 3 - 1 < MAXDNAME))
{
print_mac(daemon->namebuff, &p[2], opt_sz - 2);
mac = daemon->namebuff;
}
if (p[0] == ICMP6_OPT_SOURCE_MAC)
mac = print_mac(&p[2], opt_sz - 2);
}
if (!option_bool(OPT_QUIET_RA))
@@ -435,7 +432,7 @@ static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_ad
sprintf(daemon->namebuff, "/proc/sys/net/ipv6/conf/%s/mtu", mtu_name ? mtu_name : iface_name);
if ((f = fopen(daemon->namebuff, "r")))
{
if (fgets(daemon->namebuff, MAXDNAME, f))
if (fgets(daemon->namebuff, MAXDNAMESTR, f))
mtu = atoi(daemon->namebuff);
fclose(f);
}
+20 -12
View File
@@ -106,11 +106,16 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
p = l + (unsigned char *)header;
}
else if (label_type == 0x00)
{ /* label_type = 0 -> label. */
namelen += l + 1; /* include period */
else if (label_type == 0x00) /* label_type = 0 -> label. */
{
/* reject wire-format names > MAXDNAME bytes
This also ensures that internal-format
names don't exceed MAXDNAMESTR characters. */
namelen += l + 1;
/* namelen == MAXDNAME means terminator will overflow. */
if (namelen >= MAXDNAME)
return 0;
if (!CHECK_LEN(header, p, plen, l))
return 0;
@@ -119,13 +124,13 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
{
unsigned char c = *p;
if (c == 0 || c == '.' || c == NAME_ESCAPE)
if (IS_NAME_ESCAPE(c))
{
*cp++ = NAME_ESCAPE;
*cp++ = c+1;
}
else
*cp++ = c;
*cp++ = c;
}
else if (flip)
{
@@ -443,7 +448,7 @@ int private_net6(struct in6_addr *a, int ban_localhost)
(IN6_IS_ADDR_LOOPBACK(a) && ban_localhost) || /* RFC 6303 4.3 */
IN6_IS_ADDR_LINKLOCAL(a) || /* RFC 6303 4.5 */
IN6_IS_ADDR_SITELOCAL(a) ||
((unsigned char *)a)[0] == 0xfd || /* RFC 6303 4.4 */
(((unsigned char *)a)[0] & 0xfe) == 0xfc || /* RFC 4196 3.1 */
((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
}
@@ -498,7 +503,7 @@ int do_doctor(struct dns_header *header, size_t qlen, char *namebuff)
header->hb3 &= ~HB3_AA;
#ifdef HAVE_DNSSEC
/* remove validated flag from this RR, since we changed it! */
if (option_bool(OPT_DNSSEC_VALID) && i < ntohs(header->ancount))
if (option_bool(OPT_DNSSEC_VALID) && i < daemon->rr_status_sz && i < ntohs(header->ancount))
daemon->rr_status[i] = 0;
#endif
done = 1;
@@ -540,7 +545,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
for (i = 0; i < ntohs(header->nscount); i++)
{
if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 10))
return 0; /* bad packet */
GETSHORT(qtype, p);
@@ -610,7 +615,9 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
addr.rrblock.datalen += 20;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[i + ntohs(header->ancount)] != 0)
if (option_bool(OPT_DNSSEC_VALID) &&
i + ntohs(header->ancount) < daemon->rr_status_sz &&
daemon->rr_status[i + ntohs(header->ancount)] != 0)
{
secflag = F_DNSSECOK;
@@ -946,7 +953,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
/* Name, extract it then re-encode. */
int len;
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
/* rdlen may lie, and extract_name() advances p1 past where it says the record ends. */
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0) || (p1 > endrr))
{
blockdata_free(addr.rrblock.rrdata);
return 2;
@@ -1461,7 +1469,7 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
else
{
char *name = va_arg(ap, char *);
if (name && !(p = do_rfc1035_name(p, name, limit)))
if (name && !(p = do_rfc1035_name(p, name, (unsigned char *)limit)))
goto truncated;
if (nameoffset < 0)
@@ -1525,7 +1533,7 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
/* get domain-name answer arg and store it in RDATA field */
if (offset)
*offset = p - (unsigned char *)header;
if (!(p = do_rfc1035_name(p, va_arg(ap, char *), limit)))
if (!(p = do_rfc1035_name(p, va_arg(ap, char *), (unsigned char *)limit)))
goto truncated;
CHECK_LIMIT(1);
*p++ = 0;
+13 -11
View File
@@ -413,7 +413,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
struct dhcp_context *context_tmp;
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
{
inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAME);
inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAMESTR);
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
{
inet_ntop(AF_INET, &context_tmp->netmask, daemon->addrbuff, ADDRSTRLEN);
@@ -1313,7 +1313,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
daemon->addrbuff, print_mac(daemon->namebuff, mac, len));
daemon->addrbuff, print_mac(mac, len));
}
else
{
@@ -1537,7 +1537,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
{
inet_ntop(AF_INET, &ltmp->addr, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
print_mac(ltmp->hwaddr, ltmp->hwaddr_len),
daemon->addrbuff);
lease = ltmp;
}
@@ -1918,16 +1918,18 @@ static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
static void log_packet(char *type, void *addr, unsigned char *ext_mac,
int mac_len, char *interface, char *string, char *err, u32 xid)
{
char *mac = "";
if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
return;
daemon->addrbuff[0] = daemon->namebuff[0] = 0;
daemon->addrbuff[0] = 0;
if (addr)
inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN);
if (ext_mac)
print_mac(daemon->namebuff, ext_mac, mac_len);
mac = print_mac(ext_mac, mac_len);
if (option_bool(OPT_LOG_OPTS))
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s%s%s%s",
@@ -1936,7 +1938,7 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
interface,
daemon->addrbuff,
addr ? " " : "",
daemon->namebuff,
mac,
ext_mac ? " " : "",
string ? string : "",
err ? err : "");
@@ -1946,16 +1948,16 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
interface,
daemon->addrbuff,
addr ? " " : "",
daemon->namebuff,
mac,
ext_mac ? " " : "",
string ? string : "",
err ? err : "");
#ifdef HAVE_UBUS
if (!strcmp(type, "DHCPACK"))
ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
ubus_event_bcast("dhcp.ack", mac, addr ? daemon->addrbuff : NULL, string, interface);
else if (!strcmp(type, "DHCPRELEASE"))
ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
ubus_event_bcast("dhcp.release", mac, addr ? daemon->addrbuff : NULL, string, interface);
#endif
}
@@ -1963,7 +1965,7 @@ static void log_options(unsigned char *start, u32 xid)
{
while (*start != OPTION_END)
{
char *optname = option_string(AF_INET, start[0], option_ptr(start, 0), option_len(start), daemon->namebuff, MAXDNAME);
char *optname = option_string(AF_INET, start[0], option_ptr(start, 0), option_len(start), daemon->namebuff, MAXDNAMESTR);
my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
@@ -2642,7 +2644,7 @@ static void do_options(struct dhcp_context *context,
for (i = 0; req_options[i] != OPTION_END; i++)
{
char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
q += snprintf(q, MAXDNAMESTR - (q - daemon->namebuff),
"%d%s%s%s",
req_options[i],
strlen(s) != 0 ? ":" : "",
+52 -30
View File
@@ -41,6 +41,7 @@ static void log6_packet(struct state *state, char *type, struct in6_addr *addr,
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, int minsize);
static void *opt6_first(uint8_t *opt, uint8_t *end);
static void *opt6_user_vendor_first(uint8_t *opt, uint8_t *end);
static unsigned int opt6_uint(unsigned char *opt, int offset, int size);
static void get_context_tag(struct state *state, struct dhcp_context *context);
static int check_ia(struct state *state, void *opt, void **endp, void **ia_option);
@@ -65,9 +66,9 @@ static void calculate_times(struct dhcp_context *context, unsigned int *min_time
#define opt6_next(opt, end) (opt6_first(opt6_ptr((opt), opt6_len((opt))), (end)))
#define opt6_user_vendor_ptr(opt, i) ((void *)&(((uint8_t *)(opt))[2+(i)]))
#define opt6_user_vendor_len(opt) ((int)(opt6_uint(opt, -4, 2)))
#define opt6_user_vendor_next(opt, end) (opt6_next(((uint8_t *) opt) - 2, end))
#define opt6_user_vendor_len(opt) ((int)(opt6_uint((opt), -4, 2)))
#define opt6_user_vendor_next(opt, end) (opt6_user_vendor_first(opt6_user_vendor_ptr((opt), opt6_user_vendor_len((opt))), (end)))
unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int interface, char *iface_name,
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
@@ -414,7 +415,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
}
/* 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 (enc_opt = opt6_user_vendor_first(opt6_ptr(opt, offset), enc_end);
enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end))
for (i = 0; i <= (opt6_user_vendor_len(enc_opt) - vendor->len); i++)
if (memcmp(vendor->data, opt6_user_vendor_ptr(enc_opt, i), vendor->len) == 0)
{
@@ -472,10 +474,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
if (state->mac_len != 0)
{
if (option_bool(OPT_LOG_OPTS))
{
print_mac(daemon->dhcp_buff, state->mac, state->mac_len);
my_syslog(MS_DHCP | LOG_INFO, _("%u client MAC address: %s"), state->xid, daemon->dhcp_buff);
}
my_syslog(MS_DHCP | LOG_INFO, _("%u client MAC address: %s"), state->xid,
print_mac(state->mac, state->mac_len));
for (mac_opt = daemon->dhcp_macs; mac_opt; mac_opt = mac_opt->next)
if ((unsigned)mac_opt->hwaddr_len == state->mac_len &&
@@ -1534,7 +1534,7 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
for (i = 0; i < opt6_len(oro) - 1; i += 2)
{
char *s = option_string(AF_INET6, opt6_uint(oro, i, 2), NULL, 0, NULL, 0);
q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
q += snprintf(q, MAXDNAMESTR - (q - daemon->namebuff),
"%d%s%s%s",
opt6_uint(oro, i, 2),
strlen(s) != 0 ? ":" : "",
@@ -1927,7 +1927,8 @@ 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_user_vendor_next(enc_opt, enc_end))
for (enc_opt = opt6_user_vendor_first(opt6_ptr(opt, 4), enc_end);
enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end))
{
lease->vendorclass_count++;
lease_add_extradata(lease, opt6_user_vendor_ptr(enc_opt, 0), opt6_user_vendor_len(enc_opt), 0);
@@ -1987,7 +1988,8 @@ static void update_leases(struct state *state, struct dhcp_context *context, str
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_user_vendor_next(enc_opt, enc_end))
for (enc_opt = opt6_user_vendor_first(opt6_ptr(opt, 0), enc_end);
enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end))
lease_add_extradata(lease, opt6_user_vendor_ptr(enc_opt, 0), opt6_user_vendor_len(enc_opt), 0);
}
}
@@ -2043,8 +2045,8 @@ static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_op
{
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;
if (slen > (MAXDNAMESTR) - len - 1)
slen = (MAXDNAMESTR) - len - 1;
memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), slen);
daemon->namebuff[len + slen] = 0;
}
@@ -2054,7 +2056,7 @@ static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_op
{
/* account for flag byte on FQDN */
int offset = type == OPTION6_FQDN ? 1 : 0;
optname = option_string(AF_INET6, type, opt6_ptr(opt, offset), opt6_len(opt) - offset, daemon->namebuff, MAXDNAME);
optname = option_string(AF_INET6, type, opt6_ptr(opt, offset), opt6_len(opt) - offset, daemon->namebuff, MAXDNAMESTR);
}
my_syslog(MS_DHCP | LOG_INFO, "%u %s size:%3d option:%3d %s %s",
@@ -2073,14 +2075,6 @@ static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, c
static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string)
{
int clid_len = state->clid_len;
/* avoid buffer overflow */
if (clid_len > 100)
clid_len = 100;
print_mac(daemon->namebuff, state->clid, clid_len);
if (addr)
{
inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, DHCP_BUFF_SZ - 1);
@@ -2095,14 +2089,14 @@ static void log6_packet(struct state *state, char *type, struct in6_addr *addr,
type,
state->iface_name,
daemon->dhcp_buff2,
daemon->namebuff,
print_mac(state->clid, state->clid_len),
string ? string : "");
else
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s %s",
type,
state->iface_name,
daemon->dhcp_buff2,
daemon->namebuff,
print_mac(state->clid, state->clid_len),
string ? string : "");
}
@@ -2130,7 +2124,23 @@ static void *opt6_first(uint8_t *opt, uint8_t *end)
return opt;
}
static void *opt6_user_vendor_first(uint8_t *opt, uint8_t *end)
{
if (!opt)
return NULL;
/* make sure we have length. */
if ((uint8_t *)opt6_user_vendor_ptr(opt, 0) > end)
return NULL;
/* make sure we have bytes promised by length. */
if ((uint8_t *)opt6_user_vendor_ptr(opt, opt6_user_vendor_len(opt)) > end)
return NULL;
return opt;
}
static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
{
/* this worries about unaligned data and byte order */
@@ -2144,12 +2154,12 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
return ret;
}
/* return 0 if we're not configured to relay. */
int relay_upstream6(int iface_index, ssize_t sz,
struct in6_addr *peer_address, u32 scope_id, time_t now)
{
unsigned char *header;
unsigned char *inbuff = daemon->dhcp_packet.iov_base;
int msg_type = *inbuff;
int hopcount, o;
struct in6_addr multicast;
unsigned int maclen, mactype;
@@ -2166,10 +2176,22 @@ int relay_upstream6(int iface_index, ssize_t sz,
inet_pton(AF_INET6, ALL_SERVERS, &multicast);
get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now);
/* We need at least four bytes for valid client/server message
(type, and transaction ID) A relay-originated message needs more. */
if (sz < 4)
return 1;
/* Get hop count from nested relayed message */
if (msg_type == DHCP6RELAYFORW)
hopcount = *((unsigned char *)inbuff+1) + 1;
if (inbuff[0] == DHCP6RELAYFORW)
{
/* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */
if (sz < 38)
return 1;
/* Get hop count from nested relayed message. */
hopcount = inbuff[1] + 1;
}
else
hopcount = 0;
@@ -2235,7 +2257,7 @@ int relay_upstream6(int iface_index, ssize_t sz,
{
inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN);
if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast))
snprintf(daemon->namebuff, MAXDNAME, _("multicast via %s"), relay->interface);
snprintf(daemon->namebuff, MAXDNAMESTR, _("multicast via %s"), relay->interface);
else
inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->namebuff);
+11 -16
View File
@@ -344,14 +344,6 @@ short *rrfilter_desc(int type)
thus generating names in canonical form.
Calling to_wire followed by from_wire is almost an identity,
except that the UC remains mapped to LC.
Note that both /000 and '.' are allowed within labels. These get
represented in presentation format using NAME_ESCAPE as an escape
character. In theory, if all the characters in a name were /000 or
'.' or NAME_ESCAPE then all would have to be escaped, so the
presentation format would be twice as long as the spec (1024).
The buffers are all declared as 2049 (allowing for the trailing zero)
for this reason.
*/
int to_wire(char *name)
{
@@ -397,14 +389,17 @@ void from_wire(char *name)
len = *l;
memmove(l, l+1, len);
for (p = l; p < l + len; p++)
if (*p == '.' || *p == 0 || *p == NAME_ESCAPE)
{
memmove(p+1, p, 1 + last - p);
len++;
*p++ = NAME_ESCAPE;
(*p)++;
}
{
unsigned char c = *p;
if (IS_NAME_ESCAPE(c))
{
memmove(p+1, p, 1 + last - p);
last++;
len++;
*p++ = NAME_ESCAPE;
(*p)++;
}
}
l[len] = '.';
}
+7 -7
View File
@@ -434,17 +434,17 @@ static void tftp_request(char *packet, ssize_t plen, struct listener *listen, ti
{
if (prefix[0] == '/')
daemon->namebuff[0] = 0;
strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
strncat(daemon->namebuff, prefix, MAXDNAMESTR - strlen(daemon->namebuff));
if (prefix[strlen(prefix)-1] != '/')
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
strncat(daemon->namebuff, "/", MAXDNAMESTR - strlen(daemon->namebuff));
if (option_bool(OPT_TFTP_APREF_IP))
{
size_t oldlen = strlen(daemon->namebuff);
struct stat statbuf;
strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
strncat(daemon->namebuff, daemon->addrbuff, MAXDNAMESTR - strlen(daemon->namebuff));
strncat(daemon->namebuff, "/", MAXDNAMESTR - strlen(daemon->namebuff));
/* remove unique-directory if it doesn't exist */
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
@@ -475,7 +475,7 @@ static void tftp_request(char *packet, ssize_t plen, struct listener *listen, ti
size_t oldlen = strlen(daemon->namebuff);
struct stat statbuf;
snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/",
snprintf(daemon->namebuff + oldlen, MAXDNAMESTR - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/",
macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
/* remove unique-directory if it doesn't exist */
@@ -495,7 +495,7 @@ static void tftp_request(char *packet, ssize_t plen, struct listener *listen, ti
}
else if (filename[0] == '/')
daemon->namebuff[0] = 0;
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
strncat(daemon->namebuff, filename, MAXDNAMESTR - strlen(daemon->namebuff));
/* check permissions and open file */
if ((transfer->file = check_tftp_fileperm(packet, &len, prefix, daemon->addrbuff)))
@@ -627,7 +627,7 @@ void check_tftp_listeners(time_t now)
{
/* Use workspace to receive (small) request/ACK, to avoid overwriting precomputed reply */
char *packet = daemon->workspacename;
ssize_t plen = MAXDNAME * 2;
ssize_t plen = MAXDNAMESTR;
struct listener *listener;
struct tftp_transfer *transfer, *tmp, **up;
+97 -24
View File
@@ -136,14 +136,15 @@ static int check_name(char *in)
{
/* remove trailing .
also fail empty string and label > 63 chars */
size_t dotgap = 0, l = strlen(in);
size_t dotgap = 0, wiresize = 0, l = strlen(in);
char c;
int nowhite = 0;
int idn_encode = 0;
int hasuscore = 0;
int hasucase = 0;
if (l == 0 || l > MAXDNAME) return 0;
if (l == 0)
return 0;
if (in[l-1] == '.')
{
@@ -154,7 +155,10 @@ static int check_name(char *in)
for (; (c = *in); in++)
{
if (c == '.')
dotgap = 0;
{
wiresize += dotgap + 1;
dotgap = 0;
}
else if (++dotgap > MAXLABEL)
return 0;
else if (isascii((unsigned char)c) && iscntrl((unsigned char)c))
@@ -196,6 +200,10 @@ static int check_name(char *in)
idn_encode = idn_encode || hasucase;
#endif
/* length of final label and terminaton added */
if (!idn_encode && wiresize + dotgap + 2 > MAXDNAME)
return 0; /* wire representation too long */
return (idn_encode) ? 2 : 1;
}
@@ -283,20 +291,24 @@ char *canonicalise(char *in, int *nomem)
return ret;
}
unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit)
unsigned char *do_rfc1035_name(unsigned char *p, char *sval, unsigned char *limit)
{
int j;
/* Never make a name larger than the RFC limit */
if (!limit || (limit - p > MAXDNAME))
limit = p + MAXDNAME;
while (sval && *sval)
{
unsigned char *cp = p++;
if (limit && p > (unsigned char*)limit)
if (p > limit)
return NULL;
for (j = 0; *sval && (*sval != '.'); sval++, j++)
{
if (limit && p + 1 > (unsigned char*)limit)
if (p + 1 > limit)
return NULL;
if (*sval == NAME_ESCAPE)
@@ -427,10 +439,14 @@ int hostname_issubdomain(char *a, char *b)
for (ap = a; *ap; ap++);
for (bp = b; *bp; bp++);
/* a shorter than b */
if ((ap - a) < (bp - b))
/* anything is a subdomain of the root domain. */
if (ap == a)
return (bp == b) ? 2 : 1;
/* b shorter than a */
if ((bp - b) < (ap - a))
return 0;
do
{
c1 = (unsigned char) *(--ap);
@@ -443,18 +459,17 @@ int hostname_issubdomain(char *a, char *b)
if (c1 != c2)
return 0;
} while (bp != b);
} while (ap != a);
if (ap == a)
if (bp == b)
return 2;
if (*(--ap) == '.')
if (*(--bp) == '.')
return 1;
return 0;
}
time_t dnsmasq_time(void)
{
#ifdef HAVE_BROKEN_RTC
@@ -677,18 +692,24 @@ int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask
return count;
}
char *print_mac(char *buff, unsigned char *mac, int len)
char *print_mac(unsigned char *mac, int len)
{
char *p = buff;
int i;
if (len == 0)
sprintf(p, "<null>");
else
for (i = 0; i < len; i++)
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
static struct iovec buff = { NULL, 0 };
return buff;
/* each byte is two digits plus ':' except that last
which is two digits plus terminator. */
if (len == 0 || !expand_buf(&buff, len*3))
return "<null>";
else
{
char *p = buff.iov_base;
int i;
for (i = 0; i < len; i++)
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
}
return buff.iov_base;
}
/* rc is return from sendto and friends.
@@ -926,7 +947,7 @@ int kernel_version(void)
}
#endif
#define hash_ptr(x) (((unsigned int)(((char *)(x)) - ((char *)NULL))) & 0xffffff)
#define hash_ptr(x) (((uintptr_t)(x)) & 0xffffff)
void *whine_malloc_real(const char *func, unsigned int line, size_t size)
{
@@ -1015,3 +1036,55 @@ void free_real(const char *func, unsigned int line, void *ptr)
free(ptr);
}
}
/* get lines from f, expand buffer as required. Buffer is freed when
EOF reached and we return zero. Buffer also freed if f == NULL. */
int get_line_alloc(FILE *f, char **buffp, size_t *sizep)
{
size_t cnt = 0;
char *buff = *buffp;
while (1) {
int c = f ? getc(f) : EOF;
if (cnt == *sizep)
{
void *new;
if ((new = whine_realloc(buff, cnt + 1024)))
{
buff = *buffp = new;
*sizep = cnt + 1024;
}
else
{
/* allocation failed, ignore line.
Whine_realloc will have complained. */
while (c != '\n' && c != EOF)
c = getc(f);
cnt = 0;
continue;
}
}
buff[cnt] = 0;
if (c == '\n')
return 1;
if (c == EOF)
{
/* handle last line without '\n' */
if (cnt != 0)
return 1;
free(buff);
*buffp = NULL;
*sizep = 0;
return 0;
}
buff[cnt++] = c;
}
}