ares_expand_name.c (c-ares-1.17.1) | : | ares_expand_name.c (c-ares-1.17.2) | ||
---|---|---|---|---|
skipping to change at line 22 | skipping to change at line 22 | |||
* M.I.T. makes no representations about the suitability of | * M.I.T. makes no representations about the suitability of | |||
* this software for any purpose. It is provided "as is" | * this software for any purpose. It is provided "as is" | |||
* without express or implied warranty. | * without express or implied warranty. | |||
*/ | */ | |||
#include "ares_setup.h" | #include "ares_setup.h" | |||
#ifdef HAVE_NETINET_IN_H | #ifdef HAVE_NETINET_IN_H | |||
# include <netinet/in.h> | # include <netinet/in.h> | |||
#endif | #endif | |||
#ifdef HAVE_ARPA_NAMESER_H | ||||
# include <arpa/nameser.h> | #include "ares_nameser.h" | |||
#else | ||||
# include "nameser.h" | ||||
#endif | ||||
#ifdef HAVE_ARPA_NAMESER_COMPAT_H | ||||
# include <arpa/nameser_compat.h> | ||||
#endif | ||||
#include "ares.h" | #include "ares.h" | |||
#include "ares_nowarn.h" | #include "ares_nowarn.h" | |||
#include "ares_private.h" /* for the memdebug */ | #include "ares_private.h" /* for the memdebug */ | |||
/* Maximum number of indirections allowed for a name */ | /* Maximum number of indirections allowed for a name */ | |||
#define MAX_INDIRS 50 | #define MAX_INDIRS 50 | |||
static int name_length(const unsigned char *encoded, const unsigned char *abuf, | static int name_length(const unsigned char *encoded, const unsigned char *abuf, | |||
int alen); | int alen, int is_hostname); | |||
/* Reserved characters for names that need to be escaped */ | ||||
static int is_reservedch(int ch) | ||||
{ | ||||
switch (ch) { | ||||
case '"': | ||||
case '.': | ||||
case ';': | ||||
case '\\': | ||||
case '(': | ||||
case ')': | ||||
case '@': | ||||
case '$': | ||||
return 1; | ||||
default: | ||||
break; | ||||
} | ||||
return 0; | ||||
} | ||||
static int ares__isprint(int ch) | ||||
{ | ||||
if (ch >= 0x20 && ch <= 0x7E) | ||||
return 1; | ||||
return 0; | ||||
} | ||||
/* Character set allowed by hostnames */ | ||||
static int is_hostnamech(int ch) | ||||
{ | ||||
/* [A-Za-z0-9-.] | ||||
* Don't use isalnum() as it is locale-specific | ||||
*/ | ||||
if (ch >= 'A' && ch <= 'Z') | ||||
return 1; | ||||
if (ch >= 'a' && ch <= 'z') | ||||
return 1; | ||||
if (ch >= '0' && ch <= '9') | ||||
return 1; | ||||
if (ch == '-' || ch == '.') | ||||
return 1; | ||||
return 0; | ||||
} | ||||
/* Expand an RFC1035-encoded domain name given by encoded. The | /* Expand an RFC1035-encoded domain name given by encoded. The | |||
* containing message is given by abuf and alen. The result given by | * containing message is given by abuf and alen. The result given by | |||
* *s, which is set to a NUL-terminated allocated buffer. *enclen is | * *s, which is set to a NUL-terminated allocated buffer. *enclen is | |||
* set to the length of the encoded name (not the length of the | * set to the length of the encoded name (not the length of the | |||
* expanded name; the goal is to tell the caller how many bytes to | * expanded name; the goal is to tell the caller how many bytes to | |||
* move forward to get past the encoded name). | * move forward to get past the encoded name). | |||
* | * | |||
* In the simple case, an encoded name is a series of labels, each | * In the simple case, an encoded name is a series of labels, each | |||
* composed of a one-byte length (limited to values between 0 and 63 | * composed of a one-byte length (limited to values between 0 and 63 | |||
skipping to change at line 63 | skipping to change at line 102 | |||
* In the more complicated case, a label may be terminated by an | * In the more complicated case, a label may be terminated by an | |||
* indirection pointer, specified by two bytes with the high bits of | * indirection pointer, specified by two bytes with the high bits of | |||
* the first byte (corresponding to INDIR_MASK) set to 11. With the | * the first byte (corresponding to INDIR_MASK) set to 11. With the | |||
* two high bits of the first byte stripped off, the indirection | * two high bits of the first byte stripped off, the indirection | |||
* pointer gives an offset from the beginning of the containing | * pointer gives an offset from the beginning of the containing | |||
* message with more labels to decode. Indirection can happen an | * message with more labels to decode. Indirection can happen an | |||
* arbitrary number of times, so we have to detect loops. | * arbitrary number of times, so we have to detect loops. | |||
* | * | |||
* Since the expanded name uses '.' as a label separator, we use | * Since the expanded name uses '.' as a label separator, we use | |||
* backslashes to escape periods or backslashes in the expanded name. | * backslashes to escape periods or backslashes in the expanded name. | |||
* | ||||
* If the result is expected to be a hostname, then no escaped data is allowed | ||||
* and will return error. | ||||
*/ | */ | |||
int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf, | int ares__expand_name_validated(const unsigned char *encoded, | |||
int alen, char **s, long *enclen) | const unsigned char *abuf, | |||
int alen, char **s, long *enclen, | ||||
int is_hostname) | ||||
{ | { | |||
int len, indir = 0; | int len, indir = 0; | |||
char *q; | char *q; | |||
const unsigned char *p; | const unsigned char *p; | |||
union { | union { | |||
ares_ssize_t sig; | ares_ssize_t sig; | |||
size_t uns; | size_t uns; | |||
} nlen; | } nlen; | |||
nlen.sig = name_length(encoded, abuf, alen); | nlen.sig = name_length(encoded, abuf, alen, is_hostname); | |||
if (nlen.sig < 0) | if (nlen.sig < 0) | |||
return ARES_EBADNAME; | return ARES_EBADNAME; | |||
*s = ares_malloc(nlen.uns + 1); | *s = ares_malloc(nlen.uns + 1); | |||
if (!*s) | if (!*s) | |||
return ARES_ENOMEM; | return ARES_ENOMEM; | |||
q = *s; | q = *s; | |||
if (nlen.uns == 0) { | if (nlen.uns == 0) { | |||
/* RFC2181 says this should be ".": the root of the DNS tree. | /* RFC2181 says this should be ".": the root of the DNS tree. | |||
skipping to change at line 116 | skipping to change at line 160 | |||
{ | { | |||
if (!indir) | if (!indir) | |||
{ | { | |||
*enclen = aresx_uztosl(p + 2U - encoded); | *enclen = aresx_uztosl(p + 2U - encoded); | |||
indir = 1; | indir = 1; | |||
} | } | |||
p = abuf + ((*p & ~INDIR_MASK) << 8 | *(p + 1)); | p = abuf + ((*p & ~INDIR_MASK) << 8 | *(p + 1)); | |||
} | } | |||
else | else | |||
{ | { | |||
len = *p; | int name_len = *p; | |||
len = name_len; | ||||
p++; | p++; | |||
while (len--) | while (len--) | |||
{ | { | |||
if (*p == '.' || *p == '\\') | /* Output as \DDD for consistency with RFC1035 5.1, except | |||
*q++ = '\\'; | * for the special case of a root name response */ | |||
*q++ = *p; | if (!ares__isprint(*p) && !(name_len == 1 && *p == 0)) | |||
{ | ||||
*q++ = '\\'; | ||||
*q++ = '0' + *p / 100; | ||||
*q++ = '0' + (*p % 100) / 10; | ||||
*q++ = '0' + (*p % 10); | ||||
} | ||||
else if (is_reservedch(*p)) | ||||
{ | ||||
*q++ = '\\'; | ||||
*q++ = *p; | ||||
} | ||||
else | ||||
{ | ||||
*q++ = *p; | ||||
} | ||||
p++; | p++; | |||
} | } | |||
*q++ = '.'; | *q++ = '.'; | |||
} | } | |||
} | } | |||
if (!indir) | if (!indir) | |||
*enclen = aresx_uztosl(p + 1U - encoded); | *enclen = aresx_uztosl(p + 1U - encoded); | |||
/* Nuke the trailing period if we wrote one. */ | /* Nuke the trailing period if we wrote one. */ | |||
if (q > *s) | if (q > *s) | |||
*(q - 1) = 0; | *(q - 1) = 0; | |||
else | else | |||
*q = 0; /* zero terminate; LCOV_EXCL_LINE: empty names exit above */ | *q = 0; /* zero terminate; LCOV_EXCL_LINE: empty names exit above */ | |||
return ARES_SUCCESS; | return ARES_SUCCESS; | |||
} | } | |||
int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf, | ||||
int alen, char **s, long *enclen) | ||||
{ | ||||
return ares__expand_name_validated(encoded, abuf, alen, s, enclen, 0); | ||||
} | ||||
/* Return the length of the expansion of an encoded domain name, or | /* Return the length of the expansion of an encoded domain name, or | |||
* -1 if the encoding is invalid. | * -1 if the encoding is invalid. | |||
*/ | */ | |||
static int name_length(const unsigned char *encoded, const unsigned char *abuf, | static int name_length(const unsigned char *encoded, const unsigned char *abuf, | |||
int alen) | int alen, int is_hostname) | |||
{ | { | |||
int n = 0, offset, indir = 0, top; | int n = 0, offset, indir = 0, top; | |||
/* Allow the caller to pass us abuf + alen and have us check for it. */ | /* Allow the caller to pass us abuf + alen and have us check for it. */ | |||
if (encoded >= abuf + alen) | if (encoded >= abuf + alen) | |||
return -1; | return -1; | |||
while (*encoded) | while (*encoded) | |||
{ | { | |||
top = (*encoded & INDIR_MASK); | top = (*encoded & INDIR_MASK); | |||
skipping to change at line 174 | skipping to change at line 242 | |||
/* If we've seen more indirects than the message length, | /* If we've seen more indirects than the message length, | |||
* then there's a loop. | * then there's a loop. | |||
*/ | */ | |||
++indir; | ++indir; | |||
if (indir > alen || indir > MAX_INDIRS) | if (indir > alen || indir > MAX_INDIRS) | |||
return -1; | return -1; | |||
} | } | |||
else if (top == 0x00) | else if (top == 0x00) | |||
{ | { | |||
offset = *encoded; | int name_len = *encoded; | |||
offset = name_len; | ||||
if (encoded + offset + 1 >= abuf + alen) | if (encoded + offset + 1 >= abuf + alen) | |||
return -1; | return -1; | |||
encoded++; | encoded++; | |||
while (offset--) | while (offset--) | |||
{ | { | |||
n += (*encoded == '.' || *encoded == '\\') ? 2 : 1; | if (!ares__isprint(*encoded) && !(name_len == 1 && *encoded == 0)) | |||
{ | ||||
if (is_hostname) | ||||
return -1; | ||||
n += 4; | ||||
} | ||||
else if (is_reservedch(*encoded)) | ||||
{ | ||||
if (is_hostname) | ||||
return -1; | ||||
n += 2; | ||||
} | ||||
else | ||||
{ | ||||
if (is_hostname && !is_hostnamech(*encoded)) | ||||
return -1; | ||||
n += 1; | ||||
} | ||||
encoded++; | encoded++; | |||
} | } | |||
n++; | n++; | |||
} | } | |||
else | else | |||
{ | { | |||
/* RFC 1035 4.1.4 says other options (01, 10) for top 2 | /* RFC 1035 4.1.4 says other options (01, 10) for top 2 | |||
* bits are reserved. | * bits are reserved. | |||
*/ | */ | |||
return -1; | return -1; | |||
} | } | |||
} | } | |||
/* If there were any labels at all, then the number of dots is one | /* If there were any labels at all, then the number of dots is one | |||
* less than the number of labels, so subtract one. | * less than the number of labels, so subtract one. | |||
*/ | */ | |||
return (n) ? n - 1 : n; | return (n) ? n - 1 : n; | |||
} | } | |||
/* Like ares_expand_name but returns EBADRESP in case of invalid input. */ | /* Like ares_expand_name_validated but returns EBADRESP in case of invalid | |||
* input. */ | ||||
int ares__expand_name_for_response(const unsigned char *encoded, | int ares__expand_name_for_response(const unsigned char *encoded, | |||
const unsigned char *abuf, int alen, | const unsigned char *abuf, int alen, | |||
char **s, long *enclen) | char **s, long *enclen, int is_hostname) | |||
{ | { | |||
int status = ares_expand_name(encoded, abuf, alen, s, enclen); | int status = ares__expand_name_validated(encoded, abuf, alen, s, enclen, | |||
is_hostname); | ||||
if (status == ARES_EBADNAME) | if (status == ARES_EBADNAME) | |||
status = ARES_EBADRESP; | status = ARES_EBADRESP; | |||
return status; | return status; | |||
} | } | |||
End of changes. 18 change blocks. | ||||
23 lines changed or deleted | 113 lines changed or added |