diff options
Diffstat (limited to 'networking/udhcp/domain_codec.c')
-rw-r--r-- | networking/udhcp/domain_codec.c | 73 |
1 files changed, 39 insertions, 34 deletions
diff --git a/networking/udhcp/domain_codec.c b/networking/udhcp/domain_codec.c index 6f051c4..45354e7 100644 --- a/networking/udhcp/domain_codec.c +++ b/networking/udhcp/domain_codec.c @@ -25,16 +25,9 @@ */ char* FAST_FUNC dname_dec(const uint8_t *cstr, int clen, const char *pre) { - const uint8_t *c; - int crtpos, retpos, depth, plen = 0, len = 0; + char *ret = ret; /* for compiler */ char *dst = NULL; - if (!cstr) - return NULL; - - if (pre) - plen = strlen(pre); - /* We make two passes over the cstr string. First, we compute * how long the resulting string would be. Then we allocate a * new buffer of the required length, and fill it in with the @@ -42,59 +35,71 @@ char* FAST_FUNC dname_dec(const uint8_t *cstr, int clen, const char *pre) * having to deal with requiring callers to supply their own * buffer, then having to check if it's sufficiently large, etc. */ - - while (!dst) { - - if (len > 0) { /* second pass? allocate dst buffer and copy pre */ - dst = xmalloc(len + plen); - memcpy(dst, pre, plen); - } + while (1) { + /* note: "return NULL" below are leak-safe since + * dst isn't yet allocated */ + const uint8_t *c; + unsigned crtpos, retpos, depth, len; crtpos = retpos = depth = len = 0; - while (crtpos < clen) { c = cstr + crtpos; - if ((*c & NS_CMPRSFLGS) != 0) { /* pointer */ - if (crtpos + 2 > clen) /* no offset to jump to? abort */ + if (*c & NS_CMPRSFLGS) { + /* pointer */ + if (crtpos + 2 > clen) /* no offset to jump to? abort */ return NULL; - if (retpos == 0) /* toplevel? save return spot */ + if (retpos == 0) /* toplevel? save return spot */ retpos = crtpos + 2; depth++; - crtpos = ((*c & 0x3f) << 8) | (*(c + 1) & 0xff); /* jump */ - } else if (*c) { /* label */ - if (crtpos + *c + 1 > clen) /* label too long? abort */ + crtpos = ((c[0] & 0x3f) << 8) | (c[1] & 0xff); /* jump */ + } else if (*c) { + /* label */ + if (crtpos + *c + 1 > clen) /* label too long? abort */ return NULL; if (dst) - memcpy(dst + plen + len, c + 1, *c); + memcpy(dst + len, c + 1, *c); len += *c + 1; crtpos += *c + 1; if (dst) - *(dst + plen + len - 1) = '.'; - } else { /* null: end of current domain name */ - if (retpos == 0) { /* toplevel? keep going */ + dst[len - 1] = '.'; + } else { + /* null: end of current domain name */ + if (retpos == 0) { + /* toplevel? keep going */ crtpos++; - } else { /* return to toplevel saved spot */ + } else { + /* return to toplevel saved spot */ crtpos = retpos; retpos = depth = 0; } if (dst) - *(dst + plen + len - 1) = ' '; + dst[len - 1] = ' '; } - if (depth > NS_MAXDNSRCH || /* too many jumps? abort, it's a loop */ - len > NS_MAXDNAME * NS_MAXDNSRCH) /* result too long? abort */ + if (depth > NS_MAXDNSRCH /* too many jumps? abort, it's a loop */ + || len > NS_MAXDNAME * NS_MAXDNSRCH /* result too long? abort */ + ) { return NULL; + } } - if (!len) /* expanded string has 0 length? abort */ + if (!len) /* expanded string has 0 length? abort */ return NULL; - if (dst) - *(dst + plen + len - 1) = '\0'; + if (!dst) { /* first pass? */ + /* allocate dst buffer and copy pre */ + unsigned plen = strlen(pre); + ret = dst = xmalloc(plen + len); + memcpy(dst, pre, plen); + dst += plen; + } else { + dst[len - 1] = '\0'; + break; + } } - return dst; + return ret; } /* Convert a domain name (src) from human-readable "foo.blah.com" format into |