Compare commits

...
11 Commits
Author SHA1 Message Date
Simon Kelley 5990074ab0 Fix stupid error in allocating 0x20-flip bitmaps. 2025-02-21 14:41:34 +00:00
Simon Kelley dbb69bd192 Merge branch 'master' of onyx:dnsmasq/dnsmasq 2025-02-21 13:08:26 +00:00
Simon Kelley d17581c4c6 Use correct packet length when 0x20 flipping truncated packet.
This makes no difference in practice, since only the query is
operated on, but it is more correct.
2025-02-21 13:02:04 +00:00
Simon Kelley 2c9ed7f425 Fix possible problems with case-encode bigmap array allocation. 2025-02-20 22:59:04 +00:00
Simon Kelley 717ff6adc3 Update plen when getting retried query from stash.
They should be equal, but that depends on untrusted data.
2025-02-10 12:26:15 +00:00
Simon Kelley f9f8d19bf5 Yet another 0x20 fix.
To complement the previous one, which fixed the retry path
when the query is retried from a different id/source address, this
fixes retries from the same id/source address.
2025-02-09 11:06:59 +00:00
Simon Kelley 535be2f5d3 Fix possible SIGSEGV in bpf.c 2025-02-08 22:58:42 +00:00
Simon Kelley bceab45dbe Fix 0x20 problem.
A retry to upstream DNS servers triggered by the following conditions

1) A query asking for the same data as a previous query which has not yet been answered.
2) The second query arrives more than two seconds after the first.
3) Either the source of the second query or the id field differs from the first.

fails to set the case of the retry to the same pattern as the first attempt.

However dnsmasq expects the reply from upstream to have the case
pattern of the first attempt.

If the answer to the retry arrives before the answer to the first
query, dnsmasq will notice the case mismatch, log an error, and
ignore the answer.

The worst case scenario would be the first upstream query or reply is
lost and there would follow a short period where all queries for that
particular domain would fail.

This is a 2.91 development issue, it doesn't apply to previous stable releases.
2025-02-07 19:56:33 +00:00
Helge Deller 368ceff6e0 TFTP off-by-2 bugfix
Some of my PA-RISC UNIX machines boot remotely via tftp, but dnsmasq
randomly fails to deliver (the identical file) to some of the machines.

I traced the issue and basically dnsmasq fails with error "unsupported
request from IP.x.y.z" (line 366 in tftp.c).

Here is an example package which is sent (516 hex bytes):
76 6d 6c 69 6e 75 78 00 6f 63 74 65 74 00 12 74 10 3c 00 00 00 00 00 01
a9 24 00 00 00 00 00 00 1e 38 00 00 00 00 00 00 1c a0 00 00 00 00 00 00
1d 08 00 00 00 00 00 00 1d 28 00 00 00 00 00 00 08 00 00 00 00 00 00 00
03 d8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1d 30 00 00 00 02 ff e0
00 00 00 00 03 60 a8 49 55 93 00 00 00 01 f0 d4 21 e4 00 00 00 00 00 00
1d 78 00 00 00 f0 f0 d8 51 38 00 00 00 f0 f0 d4 21 c0 00 00 00 00 00 00
00 00 00 00 00 00 00 01 aa b8 00 00 00 f0 f0 e9 62 7c 00 00 00 00 00 00
03 01 ff ff ff ff ff ff 03 00 ff ff ff ff ff ff ff ff 00 00 00 00 00 00
00 03 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 04 ff ff ff ff ff ff
ff ff 00 00 00 00 00 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 00 00
00 05 00 00 00 00 00 00 1e 38 00 00 00 00 00 00 00 60 00 00 00 00 00 01
a6 68 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 ff 00 00 00 00 00 00
00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00
00 00 00 00 00 f0 f0 d8 4f 30 00 00 00 00 00 00 00 01 00 00 00 00 00 00
00 00 00 00 00 00 00 01 ae ec 00 00 00 00 00 00 1f 70 00 00 00 00 00 00
1e b8 00 00 03 60 a8 49 55 93 00 00 00 02 18 71 1a 00 00 00 00 00 00 00
00 03 00 00 00 00 00 00 00 03 00 00 00 00 00 00 1e 38 00 00 00 00 00 00
00 07 00 00 00 00 00 00 00 00 00 00 00 f0 f0 d2 f0 70 00 00 00 00 00 00
1f c0 00 00 00 f0 f0 d4 0b e8 00 00 00 00 00 00 00 01 00 00 00 00 00 00
00 60 ff ff ff fc 00 60 18 00 00 00 00 00 00 00 00 00 00 00 00 f0 f0 d8
8f d0 00 00 00 00 00 00 1f f8 00 00 00 00 00 00 00 00 00 00 00 f0 f0 d8
8d b8 00 00 00 00 00 00 1e e8 00 00

Please note the last 3 bytes: "e8 00 00".
If the 3rd last byte is "00", then dnsmasq works and it fails it it's "e8".

So, the bug is in line 366 of tftp.c:
   filename = next(&p, end)
Here filename gets the value NULL from next(), because the "end" variable is off-by-2.
The fix is to change line 363 to add an offset of 2:
  end = packet + 2 + len;

Signed-off-by: Helge Deller <deller@gmx.de>
Closes: https://bugzilla.redhat.com/show_bug.cgi?id=2293793
2025-02-06 17:04:06 +00:00
Simon Kelley 77c4e95d4a Fix for case-sensitivity problems in DNS.
Fix a case sensitivity problem which has been lurking for a long while.
When we get example.com and Example.com and combine them, we send whichever
query arrives first upstream and then later answer it, and we also
answer the second with the same answer. That means that if example.com
arrives first, it will get the answer example.com - good - but Example.com
will _also_ get the answer example.com - not so good.

In theory, fixing this is simple without having to keep seperate
copies of all the queries: Just use the bit-vector representation
of case flipping that we have for 0x20-encoding to keep the
differences in case. The complication comes from the fact that
the existing bit-vector code only holds data on the first 32 alpha
letters, because we only flip that up to many for 0x20 encoding.

In practise, the delta between combined queries can almost always
be represented with that data, since almost all queries are
all lower case and we only purturb the first 32 letters with
0x20 encoding. It's therefore worth keeping the existing,
efficient data structure for the 99.9% of the time it works.
For the 0.1% it doesn't, however, one needs an arbitrary-length data
structure with the resource implications of that.

Thanks to Peter Tirsek for the well researched bug report which set me
on to these problems.
2025-02-06 17:02:50 +00:00
Simon Kelley e44165c0f7 Fix bug in 0x20 encoding.
We must only compare case when mapping an answer from upstream
to a forwarding record, not when checking a query to see if it's a
duplicate. Since the saved query name is scrambled, that ensures
that almost all such checks will wrongly fail.

Thanks to Peter Tirsek for an exemplary bug report for this.
2025-02-06 10:36:21 +00:00
6 changed files with 298 additions and 124 deletions

No files matched your search

+8
View File
@@ -116,6 +116,14 @@ version 2.91
will be logged. If this coincides with DNS not functioning, it
is necessary to disable bit 0x20 encoding with --no-0x20-encode.
Fix a long-standing problem when two queries which are identical
in every repect _except_ case, get combined by dnsmasq. If
dnsmasq gets eg, two queries for example.com and Example.com
in quick succession it will get the answer for example.com from
upstream and send that answer to both requestors. This means that
the query for Example.com will get an answer for example.com, and
in the modern DNS, that answer may not be accepted.
version 2.90
Fix reversion in --rev-server introduced in 2.88 which
+88 -90
View File
@@ -126,112 +126,110 @@ int iface_enumerate(int family, void *parm, callback_t callback)
for (addrs = head; addrs; addrs = addrs->ifa_next)
{
if (addrs->ifa_addr->sa_family == family)
int iface_index = if_nametoindex(addrs->ifa_name);
if (iface_index == 0 || !addrs->ifa_addr ||
addrs->ifa_addr->sa_family != family ||
(!addrs->ifa_netmask && family != AF_LINK))
continue;
if (family == AF_INET)
{
int iface_index = if_nametoindex(addrs->ifa_name);
if (iface_index == 0 || !addrs->ifa_addr ||
(!addrs->ifa_netmask && family != AF_LINK))
struct in_addr addr, netmask, broadcast;
addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
#ifdef HAVE_BSD_NETWORK
if (del_family == AF_INET && del_addr.addr4.s_addr == addr.s_addr)
continue;
if (family == AF_INET)
{
struct in_addr addr, netmask, broadcast;
addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
#ifdef HAVE_BSD_NETWORK
if (del_family == AF_INET && del_addr.addr4.s_addr == addr.s_addr)
continue;
#endif
netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
if (addrs->ifa_broadaddr)
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
else
broadcast.s_addr = 0;
if (!callback.af_inet(addr, iface_index, NULL, netmask, broadcast, parm))
goto err;
}
else if (family == AF_INET6)
{
struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
int i, j, prefix = 0;
u32 valid = 0xffffffff, preferred = 0xffffffff;
int flags = 0;
netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
if (addrs->ifa_broadaddr)
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
else
broadcast.s_addr = 0;
if (!callback.af_inet(addr, iface_index, NULL, netmask, broadcast, parm))
goto err;
}
else if (family == AF_INET6)
{
struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
int i, j, prefix = 0;
u32 valid = 0xffffffff, preferred = 0xffffffff;
int flags = 0;
#ifdef HAVE_BSD_NETWORK
if (del_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&del_addr.addr6, addr))
continue;
if (del_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&del_addr.addr6, addr))
continue;
#endif
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
struct in6_ifreq ifr6;
memset(&ifr6, 0, sizeof(ifr6));
safe_strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
struct in6_ifreq ifr6;
memset(&ifr6, 0, sizeof(ifr6));
safe_strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
{
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
flags |= IFACE_TENTATIVE;
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
flags |= IFACE_DEPRECATED;
ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
{
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
flags |= IFACE_TENTATIVE;
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
flags |= IFACE_DEPRECATED;
#ifdef IN6_IFF_TEMPORARY
if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)))
flags |= IFACE_PERMANENT;
if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)))
flags |= IFACE_PERMANENT;
#endif
#ifdef IN6_IFF_PRIVACY
if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_PRIVACY)))
flags |= IFACE_PERMANENT;
if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_PRIVACY)))
flags |= IFACE_PERMANENT;
#endif
}
ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
if (fd != -1 && ioctl(fd, SIOCGIFALIFETIME_IN6, &ifr6) != -1)
{
valid = ifr6.ifr_ifru.ifru_lifetime.ia6t_vltime;
preferred = ifr6.ifr_ifru.ifru_lifetime.ia6t_pltime;
}
}
ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
if (fd != -1 && ioctl(fd, SIOCGIFALIFETIME_IN6, &ifr6) != -1)
{
valid = ifr6.ifr_ifru.ifru_lifetime.ia6t_vltime;
preferred = ifr6.ifr_ifru.ifru_lifetime.ia6t_pltime;
}
#endif
for (i = 0; i < IN6ADDRSZ; i++, prefix += 8)
if (netmask[i] != 0xff)
break;
if (i != IN6ADDRSZ && netmask[i])
for (j = 7; j > 0; j--, prefix++)
if ((netmask[i] & (1 << j)) == 0)
break;
/* voodoo to clear interface field in address */
if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
{
addr->s6_addr[2] = 0;
addr->s6_addr[3] = 0;
}
if (!callback.af_inet6(addr, prefix, scope_id, iface_index, flags,
(unsigned int) preferred, (unsigned int)valid, parm))
goto err;
}
#ifdef HAVE_DHCP6
else if (family == AF_LINK)
{
/* Assume ethernet again here */
struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
if (sdl->sdl_alen != 0 &&
!callback.af_local(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm))
goto err;
}
#endif
for (i = 0; i < IN6ADDRSZ; i++, prefix += 8)
if (netmask[i] != 0xff)
break;
if (i != IN6ADDRSZ && netmask[i])
for (j = 7; j > 0; j--, prefix++)
if ((netmask[i] & (1 << j)) == 0)
break;
/* voodoo to clear interface field in address */
if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
{
addr->s6_addr[2] = 0;
addr->s6_addr[3] = 0;
}
if (!callback.af_inet6(addr, prefix, scope_id, iface_index, flags,
(unsigned int) preferred, (unsigned int)valid, parm))
goto err;
}
#ifdef HAVE_DHCP6
else if (family == AF_LINK)
{
/* Assume ethernet again here */
struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
if (sdl->sdl_alen != 0 &&
!callback.af_local(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm))
goto err;
}
#endif
}
ret = 1;
err:
errsave = errno;
freeifaddrs(head);
+2 -2
View File
@@ -775,12 +775,13 @@ struct dyndir {
#define FREC_DO_QUESTION 64
#define FREC_HAS_PHEADER 128
#define FREC_GONE_TO_TCP 256
#define FREC_ANSWER 512
struct frec {
struct frec_src {
union mysockaddr source;
union all_addr dest;
unsigned int iface, log_id;
unsigned int iface, log_id, encode_bitmap, *encode_bigmap;
int fd;
unsigned short orig_id, udp_pkt_size;
struct frec_src *next;
@@ -791,7 +792,6 @@ struct frec {
int forwardall, flags;
time_t time;
u32 forward_timestamp;
unsigned int encode_bitmap;
int forward_delay;
struct blockdata *stash; /* saved query or saved reply, whilst we validate */
size_t stash_len;
+176 -25
View File
@@ -197,9 +197,18 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE)))
{
struct frec_src *src;
unsigned int casediff = 0;
unsigned int *bitvector = NULL;
unsigned short id = ntohs(header->id); /* Retrieve the id from the new query before we overwrite it. */
/* Get the case-scambled version of the query to resend. This is important because we
may fall through below and forward the query in the packet buffer again and we
want to use the same case scrambling as the first time. */
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
plen = forward->stash_len;
for (src = &forward->frec_src; src; src = src->next)
if (src->orig_id == ntohs(header->id) &&
if (src->orig_id == id &&
sockaddr_isequal(&src->source, udpaddr))
break;
@@ -221,6 +230,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
{
daemon->frec_src_count++;
daemon->free_frec_src->next = NULL;
daemon->free_frec_src->encode_bigmap = NULL;
}
/* If we've been spammed with many duplicates, return REFUSED. */
@@ -237,17 +247,72 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
free_frec(forward);
goto reply;
}
/* Find a bitmap of case differences between the query sent upstream and this one,
so we can reply to each query with the correct case pattern.
Since we need this to get back the exact case pattern of each query when doing
query combining, we have to handle the (rare) case that there are case differences
beyond the first 32 letters.
If that happens we have to allocate memory to save it, and the casediff variable
holds the length of that array.
Mismatches beyond 32 letters are rare because most queries are all lowercase and
we only scramble the first 32 letters for security reasons.
Note the two names are guaranteed to be the same length and differ only in the case
of letters at this point.
The original query we sent is now in packet buffer and the query name in the
new instance is on daemon->namebuff. */
if (extract_request(header, forward->stash_len, daemon->workspacename, NULL))
{
unsigned int i, gobig = 0;
char *s1, *s2;
#define BITS_IN_INT (sizeof(unsigned int) * 8)
big_redo:
for (s1 = daemon->namebuff, s2 = daemon->workspacename, i = 0; *s1; s1++, s2++)
{
char c1 = *s1, c2 = *s2;
if ((c1 >= 'a' && c1 <= 'z') || (c1 >= 'A' && c1 <= 'Z'))
{
if ((c1 & 0x20) ^ (c2 & 0x20))
{
if (bitvector)
bitvector[i/BITS_IN_INT] |= 1<<(i%BITS_IN_INT);
else if (i >= BITS_IN_INT)
gobig = 1; /* More than 32 */
else
casediff |= 1<<i;
}
i++;
}
}
if (gobig && !bitvector)
{
casediff = ((i - 1)/BITS_IN_INT) + 1; /* length of array */
/* whine_malloc() zeros memory */
if ((bitvector = whine_malloc(casediff * sizeof(unsigned int))))
goto big_redo;
}
}
src = daemon->free_frec_src;
daemon->free_frec_src = src->next;
src->next = forward->frec_src.next;
forward->frec_src.next = src;
src->orig_id = ntohs(header->id);
src->orig_id = id;
src->source = *udpaddr;
src->dest = *dst_addr;
src->log_id = daemon->log_id;
src->iface = dst_iface;
src->fd = udpfd;
src->encode_bitmap = casediff;
src->encode_bigmap = bitvector;
src->udp_pkt_size = (unsigned short)replylimit;
/* closely spaced identical queries cannot be a try and a retry, so
@@ -326,9 +391,10 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
forward->new_id = get_id();
header->id = ntohs(forward->new_id);
forward->encode_bitmap = option_bool(OPT_NO_0x20) ? 0 : rand32();
forward->frec_src.encode_bitmap = option_bool(OPT_NO_0x20) ? 0 : rand32();
forward->frec_src.encode_bigmap = NULL;
p = (unsigned char *)(header+1);
if (!extract_name(header, plen, &p, NULL, EXTR_NAME_FLIP, forward->encode_bitmap))
if (!extract_name(header, plen, &p, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
goto reply;
/* Keep copy of query for retries and move to TCP */
@@ -975,7 +1041,8 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
new->flags |= flags;
new->forwardall = 0;
new->encode_bitmap = 0;
new->frec_src.encode_bitmap = 0;
new->frec_src.encode_bigmap = NULL;
forward->next_dependent = NULL;
new->dependent = forward; /* to find query awaiting new one. */
@@ -1098,7 +1165,7 @@ void reply_query(int fd, time_t now)
GETSHORT(rrtype, p);
GETSHORT(class, p);
if (!(forward = lookup_frec(daemon->namebuff, class, rrtype, ntohs(header->id), 0, 0)))
if (!(forward = lookup_frec(daemon->namebuff, class, rrtype, ntohs(header->id), FREC_ANSWER, 0)))
return;
filter_servers(forward->sentto->arrayposn, F_SERVER, &first, &last);
@@ -1205,7 +1272,7 @@ void reply_query(int fd, time_t now)
/* Flip the bits back in the query name. */
p = (unsigned char *)(header+1);
if (!extract_name(header, n, &p, NULL, EXTR_NAME_FLIP, forward->encode_bitmap))
if (!extract_name(header, n, &p, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
return;
#ifdef HAVE_DNSSEC
@@ -1227,6 +1294,51 @@ void reply_query(int fd, time_t now)
return_reply(now, forward, header, n, STAT_OK);
}
static void xor_array(unsigned int *arg1, unsigned int *arg2, unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i++)
arg1[i] ^= arg2[i];
}
/* Call extract_name() to flip case of query in packet according to the XOR of the bit maps help in arg1 and arg2 */
static void flip_queryname(struct dns_header *header, ssize_t len, struct frec_src *arg1, struct frec_src *arg2)
{
unsigned char *p = (unsigned char *)(header+1);
unsigned int *arg1p, *arg2p, arg1len, arg2len, *swapp, swap;
/* Two cases: bitmap is single 32 bit int, or it's arbitrary-length array of 32bit ints.
The two args may be different and of different lengths.
The shorter gets notionally extended with zeros. */
if (arg1->encode_bigmap)
arg1p = arg1->encode_bigmap, arg1len = arg1->encode_bitmap;
else
arg1p = &arg1->encode_bitmap, arg1len = 1;
if (arg2->encode_bigmap)
arg2p = arg2->encode_bigmap, arg2len = arg2->encode_bitmap;
else
arg2p = &arg2->encode_bitmap, arg2len = 1;
/* make arg1 the longer, if they differ. */
if (arg2len > arg1len)
{
swap = arg1len;
swapp = arg1p;
arg1len = arg2len;
arg1p = arg2p;
arg2len = swap;
arg2p = swapp;
}
/* XOR on shorter length, flip on longer, operate on longer */
xor_array(arg1p, arg2p, arg2len);
extract_name(header, len, &p, (char *)arg1p, EXTR_NAME_FLIP, arg1len);
xor_array(arg1p, arg2p, arg2len); /* restore */
}
void return_reply(time_t now, struct frec *forward, struct dns_header *header, ssize_t n, int status)
{
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
@@ -1316,10 +1428,10 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
!(forward->flags & FREC_HAS_PHEADER), &forward->frec_src.source,
((unsigned char *)header) + daemon->edns_pktsz, ede)))
{
struct frec_src *src;
struct frec_src *src, *prev;
int do_trunc;
for (do_trunc = 0, src = &forward->frec_src; src; src = src->next)
for (do_trunc = 0, prev = NULL, src = &forward->frec_src; src; prev = src, src = src->next)
{
header->id = htons(src->orig_id);
@@ -1333,6 +1445,13 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
}
#endif
/* You will have to draw diagrams and scratch your head to convince yourself
that this works. Bear in mind that the flip to upstream state has already been undone,
for the original query so nothing needs to be done, but subsequent queries' flips
were recorded relative to the flipped name sent upstream. */
if (prev)
flip_queryname(header, nn, prev, src);
if (src->fd != -1)
{
/* Only send packets that fit what the requestor allows.
@@ -1356,7 +1475,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
}
}
}
/* The packet is too big for one or more requestors, send them a truncated answer. */
if (do_trunc)
{
@@ -1368,22 +1487,31 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
header->arcount = htons(0);
header->hb3 |= HB3_TC;
new = resize_packet(header, nn, pheader, hlen);
daemon->log_display_id = forward->frec_src.log_id;
daemon->log_source_addr = &forward->frec_src.source;
log_query(F_UPSTREAM, NULL, NULL, "truncated", 0);
for (src = &forward->frec_src; src; src = src->next)
if (src->fd != -1 && nn > src->udp_pkt_size)
{
header->id = htons(src->orig_id);
send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, new,
&src->source, &src->dest, src->iface);
/* This gets the name back to the state it was in when we started. */
flip_queryname(header, new, prev, &forward->frec_src);
for (src = &forward->frec_src, prev = NULL; src; prev = src, src = src->next)
{
/* If you didn't undertand this above, you won't understand it here either. */
if (prev)
flip_queryname(header, new, prev, src);
if (src->fd != -1 && nn > src->udp_pkt_size)
{
header->id = htons(src->orig_id);
send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, new,
&src->source, &src->dest, src->iface);
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_REPLY, daemon->packet, (size_t)new, NULL, &src->source, src->fd);
dump_packet_udp(DUMP_REPLY, daemon->packet, (size_t)new, NULL, &src->source, src->fd);
#endif
}
}
}
}
}
@@ -2916,10 +3044,23 @@ static void free_frec(struct frec *f)
{
struct frec_src *last;
/* add back to freelist if not the record builtin to every frec. */
for (last = f->frec_src.next; last && last->next; last = last->next) ;
/* add back to freelist if not the record builtin to every frec,
also free any bigmaps they've been decorated with. */
for (last = f->frec_src.next; last && last->next; last = last->next)
if (last->encode_bigmap)
{
free(last->encode_bigmap);
last->encode_bigmap = NULL;
}
if (last)
{
/* final link in the chain loses bigmap too. */
if (last->encode_bigmap)
{
free(last->encode_bigmap);
last->encode_bigmap = NULL;
}
last->next = daemon->free_frec_src;
daemon->free_frec_src = f->frec_src.next;
}
@@ -2934,7 +3075,7 @@ static void free_frec(struct frec *f)
blockdata_free(f->stash);
f->stash = NULL;
}
#ifdef HAVE_DNSSEC
/* Anything we're waiting on is pointless now, too */
if (f->blocking_query)
@@ -3057,6 +3198,16 @@ static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int
{
struct frec *f;
struct dns_header *header;
int compare_mode = EXTR_NAME_COMPARE;
/* Only compare case-sensitive when matching frec to a recieved answer,
NOT when looking for a duplicated question. */
if (flags & FREC_ANSWER)
{
flags &= ~FREC_ANSWER;
if (!option_bool(OPT_NO_0x20))
compare_mode = EXTR_NAME_NOCASE;
}
for (f = daemon->frec_list; f; f = f->next)
if (f->sentto &&
@@ -3068,7 +3219,7 @@ static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int
int hclass, hrrtype, rc;
/* Case sensitive compare for DNS-0x20 encoding. */
if ((rc = extract_name(header, f->stash_len, &p, target, option_bool(OPT_NO_0x20) ? EXTR_NAME_COMPARE : EXTR_NAME_NOCASE, 4)))
if ((rc = extract_name(header, f->stash_len, &p, target, compare_mode, 4)))
{
GETSHORT(hrrtype, p);
GETSHORT(hclass, p);
+23 -6
View File
@@ -19,7 +19,11 @@
/* EXTR_NAME_EXTRACT -> extract name
EXTR_NAME_COMPARE -> compare name, case insensitive
EXTR_NAME_NOCASE -> compare name, case sensitive
EXTR_NAME_FLIP -> flip 0x20 bits in packet, controlled by bitmap in parm. name may be NULL
EXTR_NAME_FLIP -> flip 0x20 bits in packet.
For flip, name is an array of ints, whose size
is given in parm, which forms the bitmap. Bits beyond the size
are assumed to be zero.
return = 0 -> error
return = 1 -> extract OK, compare OK, flip OK
@@ -31,14 +35,21 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
{
unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
unsigned int j, l, namelen = 0, hops = 0;
unsigned int bigmap_counter = 0, bigmap_posn = 0, bigmap_size, bitmap;
int retvalue = 1, case_insens = 1, isExtract = 0, flip = 0, extrabytes = (int)parm;
unsigned int *bigmap;
if (func == EXTR_NAME_EXTRACT)
isExtract = 1, *cp = 0;
else if (func == EXTR_NAME_NOCASE)
case_insens = 0;
else if (func == EXTR_NAME_FLIP)
flip = 1, extrabytes = 0;
{
flip = 1, extrabytes = 0;
bigmap = (unsigned int *)name;
name = NULL;
bigmap_size = parm;
}
while (1)
{
@@ -116,12 +127,18 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
{
unsigned char c = *p;
/* parm is unsigned. We only flip up to the first 32 alpha-chars. */
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
{
if (parm & 1)
/* Get the next int of the bitmap */
if (bigmap_posn < bigmap_size && bigmap_counter-- == 0)
{
bitmap = bigmap[bigmap_posn++];
bigmap_counter = (sizeof(unsigned int) * 8) - 1;
}
if (bitmap & 1)
*p ^= 0x20;
parm >>= 1;
bitmap >>= 1;
}
}
else
+1 -1
View File
@@ -360,7 +360,7 @@ void tftp_request(struct listener *listen, time_t now)
}
p = packet + 2;
end = packet + len;
end = packet + 2 + len;
if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
!(filename = next(&p, end)) ||