mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-10-10 17:02:35 +07:00
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.
223 lines
8.2 KiB
C
223 lines
8.2 KiB
C
/* dnsmasq is Copyright (c) 2000-2026 Simon Kelley
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 dated June, 1991, or
|
|
(at your option) version 3 dated 29 June, 2007.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#define NAMESERVER_PORT 53
|
|
#define TFTP_PORT 69
|
|
#define MIN_PORT 1024 /* first non-reserved port */
|
|
#define MAX_PORT 65535u
|
|
|
|
#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 RRFIXEDSZ 10 /* #/bytes of fixed data in r record */
|
|
#define MAXLABEL 63 /* maximum length of domain label */
|
|
|
|
#define NOERROR 0 /* no error */
|
|
#define FORMERR 1 /* format error */
|
|
#define SERVFAIL 2 /* server failure */
|
|
#define NXDOMAIN 3 /* non existent domain */
|
|
#define NOTIMP 4 /* not implemented */
|
|
#define REFUSED 5 /* query refused */
|
|
|
|
#define QUERY 0 /* opcode */
|
|
|
|
#define C_IN 1 /* the arpa internet */
|
|
#define C_CHAOS 3 /* for chaos net (MIT) */
|
|
#define C_HESIOD 4 /* hesiod */
|
|
#define C_ANY 255 /* wildcard match */
|
|
|
|
#define T_A 1
|
|
#define T_NS 2
|
|
#define T_MD 3
|
|
#define T_MF 4
|
|
#define T_CNAME 5
|
|
#define T_SOA 6
|
|
#define T_MB 7
|
|
#define T_MG 8
|
|
#define T_MR 9
|
|
#define T_PTR 12
|
|
#define T_MINFO 14
|
|
#define T_MX 15
|
|
#define T_TXT 16
|
|
#define T_RP 17
|
|
#define T_AFSDB 18
|
|
#define T_RT 21
|
|
#define T_SIG 24
|
|
#define T_PX 26
|
|
#define T_AAAA 28
|
|
#define T_NXT 30
|
|
#define T_SRV 33
|
|
#define T_NAPTR 35
|
|
#define T_KX 36
|
|
#define T_DNAME 39
|
|
#define T_OPT 41
|
|
#define T_DS 43
|
|
#define T_RRSIG 46
|
|
#define T_NSEC 47
|
|
#define T_DNSKEY 48
|
|
#define T_NSEC3 50
|
|
#define T_TKEY 249
|
|
#define T_TSIG 250
|
|
#define T_AXFR 252
|
|
#define T_MAILB 253
|
|
#define T_ANY 255
|
|
#define T_CAA 257
|
|
|
|
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
|
|
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
|
|
#define EDNS0_OPTION_EDE 15 /* IANA - RFC 8914 */
|
|
#define EDNS0_OPTION_NOMDEVICEID 65073 /* Nominum temporary assignment */
|
|
#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */
|
|
#define EDNS0_OPTION_UMBRELLA 20292 /* Cisco Umbrella temporary assignment */
|
|
|
|
/* RFC-8914 extended errors, negative values are our definitions */
|
|
#define EDE_US_SERVFAIL -2 /* SERVFAIL from usptream */
|
|
#define EDE_UNSET -1 /* No extended DNS error available */
|
|
#define EDE_OTHER 0 /* Other */
|
|
#define EDE_USUPDNSKEY 1 /* Unsupported DNSKEY algo */
|
|
#define EDE_USUPDS 2 /* Unsupported DS Digest */
|
|
#define EDE_STALE 3 /* Stale answer */
|
|
#define EDE_FORGED 4 /* Forged answer */
|
|
#define EDE_DNSSEC_IND 5 /* DNSSEC Indeterminate */
|
|
#define EDE_DNSSEC_BOGUS 6 /* DNSSEC Bogus */
|
|
#define EDE_SIG_EXP 7 /* Signature Expired */
|
|
#define EDE_SIG_NYV 8 /* Signature Not Yet Valid */
|
|
#define EDE_NO_DNSKEY 9 /* DNSKEY missing */
|
|
#define EDE_NO_RRSIG 10 /* RRSIGs missing */
|
|
#define EDE_NO_ZONEKEY 11 /* No Zone Key Bit Set */
|
|
#define EDE_NO_NSEC 12 /* NSEC Missing */
|
|
#define EDE_CACHED_ERR 13 /* Cached Error */
|
|
#define EDE_NOT_READY 14 /* Not Ready */
|
|
#define EDE_BLOCKED 15 /* Blocked */
|
|
#define EDE_CENSORED 16 /* Censored */
|
|
#define EDE_FILTERED 17 /* Filtered */
|
|
#define EDE_PROHIBITED 18 /* Prohibited */
|
|
#define EDE_STALE_NXD 19 /* Stale NXDOMAIN */
|
|
#define EDE_NOT_AUTH 20 /* Not Authoritative */
|
|
#define EDE_NOT_SUP 21 /* Not Supported */
|
|
#define EDE_NO_AUTH 22 /* No Reachable Authority */
|
|
#define EDE_NETERR 23 /* Network error */
|
|
#define EDE_INVALID_DATA 24 /* Invalid Data */
|
|
#define EDE_SIG_E_B_V 25 /* Signature Expired before Valid */
|
|
#define EDE_TOO_EARLY 26 /* To Early */
|
|
#define EDE_UNS_NS3_ITER 27 /* Unsupported NSEC3 Iterations Value */
|
|
#define EDE_UNABLE_POLICY 28 /* Unable to conform to policy */
|
|
#define EDE_SYNTHESIZED 29 /* Synthesized */
|
|
|
|
|
|
struct dns_header {
|
|
u16 id;
|
|
u8 hb3,hb4;
|
|
u16 qdcount,ancount,nscount,arcount;
|
|
};
|
|
|
|
#define HB3_QR 0x80 /* Query */
|
|
#define HB3_OPCODE 0x78
|
|
#define HB3_AA 0x04 /* Authoritative Answer */
|
|
#define HB3_TC 0x02 /* TrunCated */
|
|
#define HB3_RD 0x01 /* Recursion Desired */
|
|
|
|
#define HB4_RA 0x80 /* Recursion Available */
|
|
#define HB4_AD 0x20 /* Authenticated Data */
|
|
#define HB4_CD 0x10 /* Checking Disabled */
|
|
#define HB4_RCODE 0x0f
|
|
|
|
#define OPCODE(x) (((x)->hb3 & HB3_OPCODE) >> 3)
|
|
#define SET_OPCODE(x, code) (x)->hb3 = ((x)->hb3 & ~HB3_OPCODE) | code
|
|
|
|
#define RCODE(x) ((x)->hb4 & HB4_RCODE)
|
|
#define SET_RCODE(x, code) (x)->hb4 = ((x)->hb4 & ~HB4_RCODE) | code
|
|
|
|
#define GETSHORT(s, cp) do { \
|
|
unsigned char *t_cp = (unsigned char *)(cp); \
|
|
(s) = ((u16)t_cp[0] << 8) \
|
|
| ((u16)t_cp[1]) \
|
|
; \
|
|
(cp) += 2; \
|
|
} while(0)
|
|
|
|
#define GETLONG(l, cp) do { \
|
|
unsigned char *t_cp = (unsigned char *)(cp); \
|
|
(l) = ((u32)t_cp[0] << 24) \
|
|
| ((u32)t_cp[1] << 16) \
|
|
| ((u32)t_cp[2] << 8) \
|
|
| ((u32)t_cp[3]) \
|
|
; \
|
|
(cp) += 4; \
|
|
} while (0)
|
|
|
|
#define PUTSHORT(s, cp) do { \
|
|
u16 t_s = (u16)(s); \
|
|
unsigned char *t_cp = (unsigned char *)(cp); \
|
|
*t_cp++ = t_s >> 8; \
|
|
*t_cp = t_s; \
|
|
(cp) += 2; \
|
|
} while(0)
|
|
|
|
#define PUTLONG(l, cp) do { \
|
|
u32 t_l = (u32)(l); \
|
|
unsigned char *t_cp = (unsigned char *)(cp); \
|
|
*t_cp++ = t_l >> 24; \
|
|
*t_cp++ = t_l >> 16; \
|
|
*t_cp++ = t_l >> 8; \
|
|
*t_cp = t_l; \
|
|
(cp) += 4; \
|
|
} while (0)
|
|
|
|
#define CHECK_LEN(header, pp, plen, len) \
|
|
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
|
|
|
|
#define ADD_RDLEN(header, pp, plen, len) \
|
|
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
|
|
|
|
/* 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)
|
|
|