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.
This commit is contained in:
Simon Kelley committed 2026-05-10 21:49:39 +01:00
1 parent 29f8712f02
commit 014e909f78
22 files changed
+136 -92

No files matched your search

+11 -1
View File
@@ -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
+6 -2
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;
@@ -1418,7 +1422,7 @@ static int gettok(FILE *f, char *token)
return eatspace(f);
}
if (count < (MAXDNAME - 1))
if (count < (MAXDNAMESTR - 1))
{
token[count++] = c;
token[count] = 0;
@@ -1793,7 +1797,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"
+1 -1
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);
}
+2 -2
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);
+1 -1
View File
@@ -958,7 +958,7 @@ void dhcp_read_ethers(void)
up = &config->next;
}
while (fgets(buff, MAXDNAME, f))
while (fgets(buff, MAXDNAMESTR, f))
{
char *host = NULL;
+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);
+4 -4
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;
@@ -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);
+1 -1
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.
+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;
}
+1 -1
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;
+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
+2 -2
View File
@@ -1702,7 +1702,7 @@ int reload_servers(char *fname)
char *line;
int gotone = 0;
/* buff happens to be MAXDNAME long... */
/* buff happens to be MAXDNAMESTR long... */
if (!(f = fopen(fname, "r")))
{
my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno));
@@ -1711,7 +1711,7 @@ int reload_servers(char *fname)
mark_servers(SERV_FROM_RESOLV);
while ((line = fgets(daemon->namebuff, MAXDNAME, f)))
while ((line = fgets(daemon->namebuff, MAXDNAMESTR, f)))
{
union mysockaddr addr, source_addr;
char *token = strtok(line, " \t\n\r");
+9 -13
View File
@@ -5473,7 +5473,7 @@ static void read_file(char *file, FILE *f, int hard_opt, int from_script)
volatile int lineno = 0;
char *buff = daemon->namebuff;
while (fgets(buff, MAXDNAME, f))
while (fgets(buff, MAXDNAMESTR, f))
{
int white, i;
volatile int option;
@@ -5948,14 +5948,9 @@ void reread_dhcp(void)
void read_opts(int argc, char **argv, char *compile_opts)
{
size_t argbuf_size = MAXDNAME;
size_t argbuf_size = 300;
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);
char *buff = opt_malloc(MAXDNAMESTR+1);
int option, testmode = 0;
char *arg, *conffile = NULL;
@@ -5964,7 +5959,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon = opt_malloc(sizeof(struct daemon));
memset(daemon, 0, sizeof(struct daemon));
daemon->namebuff = buff;
daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1);
daemon->workspacename = safe_malloc(MAXDNAMESTR+1);
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
/* Set defaults - everything else is zero or NULL */
@@ -6219,7 +6214,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
{
struct mx_srv_record *mx;
if (gethostname(buff, MAXDNAME) == -1)
if (gethostname(buff, MAXDNAMESTR) == -1)
die(_("cannot get host-name: %s"), NULL, EC_MISC);
for (mx = daemon->mxnames; mx; mx = mx->next)
@@ -6263,7 +6258,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 ((line = fgets(buff, MAXDNAMESTR, f)))
{
char *token = strtok(line, " \t\n\r");
@@ -6291,13 +6286,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);
free(srv->name);
srv->name = opt_string_alloc(buff);
if (!(srv->name = canonicalise_opt(buff)))
die(_("bad srv-host name %s after domain appended"), srv->name, EC_MISC);
}
}
else if (option_bool(OPT_DHCP_FQDN))
+2 -2
View File
@@ -215,7 +215,7 @@ 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))
if (p[0] == ICMP6_OPT_SOURCE_MAC && ((opt_sz - 2) * 3 - 1 < MAXDNAMESTR))
{
print_mac(daemon->namebuff, &p[2], opt_sz - 2);
mac = daemon->namebuff;
@@ -435,7 +435,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);
}
+12 -7
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)
{
@@ -1463,7 +1468,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)
@@ -1527,7 +1532,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;
+3 -3
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);
@@ -1963,7 +1963,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 +2642,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 ? ":" : "",
+3 -3
View File
@@ -1536,7 +1536,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 ? ":" : "",
@@ -2058,7 +2058,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",
@@ -2255,7 +2255,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);
+10 -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,16 @@ 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);
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;
+18 -6
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)