Large refactor of EDNS0 UDP packet size handling.

This was kinda strange before, with a lot of cargo-cult copied code,
and no clear strategy.

Now it works like this:

When talking upstream we always add a pseudoheader, and set the
UDP packet size to --edns-packet-max unless we've had problems
talking to a server, when it's reduced to 1280 if that fixes things.

Answering queries from downstream, we get the answer (either from
upstream or local data) If local data won't fit the advertised size
(or 512 if there's not pseudoheader) return truncated. If upstream
returns truncated, do likewise. If upstream is OK, but the answer is
too big for downstream, truncate the answer.
This commit is contained in:
Simon Kelley
2024-11-23 22:38:41 +00:00
parent e778a28eee
commit e5e8c14d87
6 changed files with 157 additions and 184 deletions

View File

@@ -96,7 +96,12 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t
}
/* replace == 2 ->delete existing option only. */
/* replace == 0 ->don't replace existing option
replace == 1 ->replace existing or add option
replace == 2 ->relpace existing option only.
udp_sz == 0 -> leave unchanged in existing EDNS0 or set to deamon->edns_pksz in a new one.
*/
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do, int replace)
{
@@ -114,9 +119,14 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
/* Existing header */
int i;
unsigned short code, len;
p = udp_len;
GETSHORT(udp_sz, p);
if (udp_sz == 0)
GETSHORT(udp_sz, p);
else
PUTSHORT(udp_sz, p);
GETSHORT(rcode, p);
GETSHORT(flags, p);
@@ -197,12 +207,15 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
free(buff);
return plen; /* bad packet */
}
if (udp_sz == 0)
udp_sz = daemon->edns_pktsz;
*p++ = 0; /* empty name */
PUTSHORT(T_OPT, p);
PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
PUTSHORT(rcode, p); /* extended RCODE and version */
PUTSHORT(flags, p); /* DO flag */
PUTSHORT(rcode, p); /* extended RCODE and version */
PUTSHORT(flags, p); /* DO flag */
lenp = p;
PUTSHORT(rdlen, p); /* RDLEN */
datap = p;
@@ -245,7 +258,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit)
{
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1, 0);
return add_pseudoheader(header, plen, (unsigned char *)limit, 0, 0, NULL, 0, 1, 0);
}
static unsigned char char64(unsigned char c)
@@ -290,7 +303,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, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
plen = add_pseudoheader(header, plen, limit, 0, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
return plen;
}
@@ -315,7 +328,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, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, replace);
plen = add_pseudoheader(header, plen, limit, 0, EDNS0_OPTION_MAC, mac, maclen, 0, replace);
return plen;
}
@@ -415,7 +428,7 @@ static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned c
else
return plen;
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace);
return add_pseudoheader(header, plen, (unsigned char *)limit, 0, 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)
@@ -515,7 +528,7 @@ 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, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1);
return add_pseudoheader(header, plen, (unsigned char *)limit, 0, 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
@@ -530,7 +543,7 @@ size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *l
plen = add_dns_client(header, plen, limit, source, now, cacheable);
if (daemon->dns_client_id)
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID,
plen = add_pseudoheader(header, plen, limit, 0, EDNS0_OPTION_NOMCPEID,
(unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
if (option_bool(OPT_UMBRELLA))