OSDN Git Service

inet/resolv: add ns_name_pton, ns_name_pack and ns_name_compress
authorDaniel Mack <zonque@gmail.com>
Tue, 12 Jul 2011 22:30:49 +0000 (00:30 +0200)
committerBernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Fri, 26 Aug 2011 07:29:53 +0000 (09:29 +0200)
These are built '#ifdef L_ns_name'

Signed-off-by: Daniel Mack <zonque@gmail.com>
include/arpa/nameser.h
libc/inet/resolv.c

index 917ba19..cdc3df5 100644 (file)
@@ -521,6 +521,7 @@ int         ns_name_ntol (const u_char *, u_char *, size_t) __THROW;
 int            ns_name_ntop (const u_char *, char *, size_t) __THROW;
 libc_hidden_proto(ns_name_ntop)
 int            ns_name_pton (const char *, u_char *, size_t) __THROW;
+libc_hidden_proto(ns_name_pton)
 int            ns_name_unpack (const u_char *, const u_char *,
                                const u_char *, u_char *, size_t) __THROW;
 libc_hidden_proto(ns_name_unpack)
index a347ca5..64e99ba 100644 (file)
@@ -339,6 +339,9 @@ Domain name in a message can be represented as either:
 #define BUFSZ       (80) /* one line */
 #define SBUFSIZE    (BUFSZ + 1 + (sizeof(char *) * MAXALIASES))
 
+#define NS_TYPE_ELT                                    0x40 /*%< EDNS0 extended label type */
+#define DNS_LABELTYPE_BITSTRING                0x41
+
 #undef DEBUG
 /* #define DEBUG */
 
@@ -2792,6 +2795,259 @@ int ns_name_ntop(const u_char *src, char *dst, size_t dstsiz)
 }
 libc_hidden_def(ns_name_ntop)
 
+static const char digits[] = "0123456789";
+
+static const char digitvalue[256] = {
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/
+        0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1, /*64*/
+       -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/
+       -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/
+};
+
+static int encode_bitstring(const char **bp, const char *end,
+                                                       unsigned char **labelp,
+                                                       unsigned char ** dst,
+                                                       unsigned const char *eom)
+{
+       int afterslash = 0;
+       const char *cp = *bp;
+       unsigned char *tp;
+       char c;
+       const char *beg_blen;
+       char *end_blen = NULL;
+       int value = 0, count = 0, tbcount = 0, blen = 0;
+
+       beg_blen = end_blen = NULL;
+
+       /* a bitstring must contain at least 2 characters */
+       if (end - cp < 2)
+               return (EINVAL);
+
+       /* XXX: currently, only hex strings are supported */
+       if (*cp++ != 'x')
+               return (EINVAL);
+       if (!isxdigit((*cp) & 0xff)) /*%< reject '\[x/BLEN]' */
+               return (EINVAL);
+
+       for (tp = *dst + 1; cp < end && tp < eom; cp++) {
+               switch((c = *cp)) {
+               case ']':       /*%< end of the bitstring */
+                       if (afterslash) {
+                               if (beg_blen == NULL)
+                                       return (EINVAL);
+                               blen = (int)strtol(beg_blen, &end_blen, 10);
+                               if (*end_blen != ']')
+                                       return (EINVAL);
+                       }
+                       if (count)
+                               *tp++ = ((value << 4) & 0xff);
+                       cp++;   /*%< skip ']' */
+                       goto done;
+               case '/':
+                       afterslash = 1;
+                       break;
+               default:
+                       if (afterslash) {
+                               if (!isdigit(c&0xff))
+                                       return (EINVAL);
+                               if (beg_blen == NULL) {
+
+                                       if (c == '0') {
+                                               /* blen never begings with 0 */
+                                               return (EINVAL);
+                                       }
+                                       beg_blen = cp;
+                               }
+                       } else {
+                               if (!isxdigit(c&0xff))
+                                       return (EINVAL);
+                               value <<= 4;
+                               value += digitvalue[(int)c];
+                               count += 4;
+                               tbcount += 4;
+                               if (tbcount > 256)
+                                       return (EINVAL);
+                               if (count == 8) {
+                                       *tp++ = value;
+                                       count = 0;
+                               }
+                       }
+                       break;
+               }
+       }
+  done:
+       if (cp >= end || tp >= eom)
+               return (EMSGSIZE);
+
+       /*
+        * bit length validation:
+        * If a <length> is present, the number of digits in the <bit-data>
+        * MUST be just sufficient to contain the number of bits specified
+        * by the <length>. If there are insignificant bits in a final
+        * hexadecimal or octal digit, they MUST be zero.
+        * RFC2673, Section 3.2.
+        */
+       if (blen > 0) {
+               int traillen;
+
+               if (((blen + 3) & ~3) != tbcount)
+                       return (EINVAL);
+               traillen = tbcount - blen; /*%< between 0 and 3 */
+               if (((value << (8 - traillen)) & 0xff) != 0)
+                       return (EINVAL);
+       }
+       else
+               blen = tbcount;
+       if (blen == 256)
+               blen = 0;
+
+       /* encode the type and the significant bit fields */
+       **labelp = DNS_LABELTYPE_BITSTRING;
+       **dst = blen;
+
+       *bp = cp;
+       *dst = tp;
+
+       return (0);
+}
+
+int ns_name_pton(const char *src, u_char *dst, size_t dstsiz)
+{
+       u_char *label, *bp, *eom;
+       int c, n, escaped, e = 0;
+       char *cp;
+
+       escaped = 0;
+       bp = dst;
+       eom = dst + dstsiz;
+       label = bp++;
+
+       while ((c = *src++) != 0) {
+               if (escaped) {
+                       if (c == '[') { /*%< start a bit string label */
+                               if ((cp = strchr(src, ']')) == NULL) {
+                                       errno = EINVAL; /*%< ??? */
+                                       return (-1);
+                               }
+                               if ((e = encode_bitstring(&src, cp + 2,
+                                                        &label, &bp, eom))
+                                   != 0) {
+                                       errno = e;
+                                       return (-1);
+                               }
+                               escaped = 0;
+                               label = bp++;
+                               if ((c = *src++) == 0)
+                                       goto done;
+                               else if (c != '.') {
+                                       errno = EINVAL;
+                                       return  (-1);
+                               }
+                               continue;
+                       }
+                       else if ((cp = strchr(digits, c)) != NULL) {
+                               n = (cp - digits) * 100;
+                               if ((c = *src++) == 0 ||
+                                   (cp = strchr(digits, c)) == NULL) {
+                                       errno = EMSGSIZE;
+                                       return (-1);
+                               }
+                               n += (cp - digits) * 10;
+                               if ((c = *src++) == 0 ||
+                                   (cp = strchr(digits, c)) == NULL) {
+                                       errno = EMSGSIZE;
+                                       return (-1);
+                               }
+                               n += (cp - digits);
+                               if (n > 255) {
+                                       errno = EMSGSIZE;
+                                       return (-1);
+                               }
+                               c = n;
+                       }
+                       escaped = 0;
+               } else if (c == '\\') {
+                       escaped = 1;
+                       continue;
+               } else if (c == '.') {
+                       c = (bp - label - 1);
+                       if ((c & NS_CMPRSFLGS) != 0) {  /*%< Label too big. */
+                               errno = EMSGSIZE;
+                               return (-1);
+                       }
+                       if (label >= eom) {
+                               errno = EMSGSIZE;
+                               return (-1);
+                       }
+                       *label = c;
+                       /* Fully qualified ? */
+                       if (*src == '\0') {
+                               if (c != 0) {
+                                       if (bp >= eom) {
+                                               errno = EMSGSIZE;
+                                               return (-1);
+                                       }
+                                       *bp++ = '\0';
+                               }
+                               if ((bp - dst) > MAXCDNAME) {
+                                       errno = EMSGSIZE;
+                                       return (-1);
+                               }
+
+                               return (1);
+                       }
+                       if (c == 0 || *src == '.') {
+                               errno = EMSGSIZE;
+                               return (-1);
+                       }
+                       label = bp++;
+                       continue;
+               }
+               if (bp >= eom) {
+                       errno = EMSGSIZE;
+                       return (-1);
+               }
+               *bp++ = (u_char)c;
+       }
+       c = (bp - label - 1);
+       if ((c & NS_CMPRSFLGS) != 0) {    /*%< Label too big. */
+               errno = EMSGSIZE;
+               return (-1);
+       }
+  done:
+       if (label >= eom) {
+               errno = EMSGSIZE;
+               return (-1);
+       }
+       *label = c;
+       if (c != 0) {
+               if (bp >= eom) {
+                       errno = EMSGSIZE;
+                       return (-1);
+               }
+               *bp++ = 0;
+       }
+       if ((bp - dst) > MAXCDNAME) {   /*%< src too big */
+               errno = EMSGSIZE;
+               return (-1);
+       }
+
+       return (0);
+}
+libc_hidden_def(ns_name_pton)
+
 /*
  * ns_name_unpack(msg, eom, src, dst, dstsiz)
  *      Unpack a domain name from a message, source may be compressed.
@@ -2866,6 +3122,218 @@ int ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
        return len;
 }
 libc_hidden_def(ns_name_unpack)
+
+static int labellen(const unsigned char *lp)
+{
+       int bitlen;
+       unsigned char l = *lp;
+
+       if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+               /* should be avoided by the caller */
+               return -1;
+       }
+
+       if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT) {
+               if (l == DNS_LABELTYPE_BITSTRING) {
+                       if ((bitlen = *(lp + 1)) == 0)
+                               bitlen = 256;
+                       return ((bitlen + 7 ) / 8 + 1);
+               }
+
+               return -1;    /*%< unknwon ELT */
+       }
+
+       return l;
+}
+
+static int mklower(int ch)
+{
+       if (ch >= 0x41 && ch <= 0x5A)
+               return (ch + 0x20);
+
+       return (ch);
+}
+
+static int dn_find(const unsigned char *domain,
+                                  const unsigned char *msg,
+                                  const unsigned char * const *dnptrs,
+                                  const unsigned char * const *lastdnptr)
+{
+       const unsigned char *dn, *cp, *sp;
+       const unsigned char * const *cpp;
+       u_int n;
+
+       for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
+               sp = *cpp;
+               /*
+                * terminate search on:
+                * root label
+                * compression pointer
+                * unusable offset
+                */
+               while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 &&
+                               (sp - msg) < 0x4000) {
+                       dn = domain;
+                       cp = sp;
+
+                       while ((n = *cp++) != 0) {
+                               /*
+                                * check for indirection
+                                */
+                               switch (n & NS_CMPRSFLGS) {
+                               case 0:  /*%< normal case, n == len */
+                                       n = labellen(cp - 1); /*%< XXX */
+                                       if (n != *dn++)
+                                               goto next;
+
+                                       for (; n > 0; n--)
+                                               if (mklower(*dn++) !=
+                                                   mklower(*cp++))
+                                                       goto next;
+                                       /* Is next root for both ? */
+                                       if (*dn == '\0' && *cp == '\0')
+                                               return (sp - msg);
+                                       if (*dn)
+                                               continue;
+                                       goto next;
+                               case NS_CMPRSFLGS:      /*%< indirection */
+                                       cp = msg + (((n & 0x3f) << 8) | *cp);
+                                       break;
+
+                               default:        /*%< illegal type */
+                                       errno = EMSGSIZE;
+                                       return -1;
+                               }
+                       }
+next:
+                       sp += *sp + 1;
+               }
+       }
+
+       errno = ENOENT;
+       return -1;
+}
+
+int ns_name_pack(const unsigned char *src,
+                                unsigned char *dst, int dstsiz,
+                                const unsigned char **dnptrs,
+                                const unsigned char **lastdnptr)
+{
+       unsigned char *dstp;
+       const unsigned char **cpp, **lpp, *eob, *msg;
+       const unsigned char *srcp;
+       int n, l, first = 1;
+
+       srcp = src;
+       dstp = dst;
+       eob = dstp + dstsiz;
+       lpp = cpp = NULL;
+
+       if (dnptrs != NULL) {
+               if ((msg = *dnptrs++) != NULL) {
+                       for (cpp = dnptrs; *cpp != NULL; cpp++)
+                               continue;
+
+                       lpp = cpp;      /*%< end of list to search */
+               }
+       } else {
+               msg = NULL;
+       }
+
+       /* make sure the domain we are about to add is legal */
+       l = 0;
+       do {
+               int l0;
+
+               n = *srcp;
+               if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+                       errno = EMSGSIZE;
+                       return -1;
+               }
+
+               if ((l0 = labellen(srcp)) < 0) {
+                       errno = EINVAL;
+                       return -1;
+               }
+
+               l += l0 + 1;
+               if (l > MAXCDNAME) {
+                       errno = EMSGSIZE;
+                       return -1;
+               }
+
+               srcp += l0 + 1;
+       } while (n != 0);
+
+       /* from here on we need to reset compression pointer array on error */
+       srcp = src;
+
+       do {
+               /* Look to see if we can use pointers. */
+               n = *srcp;
+
+               if (n != 0 && msg != NULL) {
+                       l = dn_find(srcp, msg, (const unsigned char * const *) dnptrs,
+                                               (const unsigned char * const *) lpp);
+                       if (l >= 0) {
+                               if (dstp + 1 >= eob) {
+                                       goto cleanup;
+                               }
+
+                               *dstp++ = ((u_int32_t)l >> 8) | NS_CMPRSFLGS;
+                               *dstp++ = l % 256;
+                               return (dstp - dst);
+                       }
+
+                       /* Not found, save it. */
+                       if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
+                               (dstp - msg) < 0x4000 && first) {
+                               *cpp++ = dstp;
+                               *cpp = NULL;
+                               first = 0;
+                       }
+               }
+
+               /* copy label to buffer */
+               if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+                       /* Should not happen. */
+                       goto cleanup;
+               }
+
+               n = labellen(srcp);
+               if (dstp + 1 + n >= eob) {
+                       goto cleanup;
+               }
+
+               memcpy(dstp, srcp, (size_t)(n + 1));
+               srcp += n + 1;
+               dstp += n + 1;
+       } while (n != 0);
+
+       if (dstp > eob) {
+cleanup:
+               if (msg != NULL)
+                       *lpp = NULL;
+
+                       errno = EMSGSIZE;
+                       return -1;
+       }
+
+       return dstp - dst;
+}
+
+int ns_name_compress(const char *src,
+                                        unsigned char *dst, size_t dstsiz,
+                                        const unsigned char **dnptrs,
+                                        const unsigned char **lastdnptr)
+{
+       unsigned char tmp[NS_MAXCDNAME];
+
+       if (ns_name_pton(src, tmp, sizeof(tmp)) == -1)
+               return -1;
+
+       return ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr);
+}
 #endif /* L_ns_name */