common/ns_name.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
00003  * Copyright (c) 1996-2003 by Internet Software Consortium
00004  *
00005  * Permission to use, copy, modify, and distribute this software for any
00006  * purpose with or without fee is hereby granted, provided that the above
00007  * copyright notice and this permission notice appear in all copies.
00008  *
00009  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
00010  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00011  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
00012  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00013  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00014  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
00015  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00016  *
00017  *   Internet Systems Consortium, Inc.
00018  *   950 Charter Street
00019  *   Redwood City, CA 94063
00020  *   <info@isc.org>
00021  *   http://www.isc.org/
00022  */
00023 
00024 #ifndef lint
00025 static const char rcsid[] = "$Id: ns_name.c,v 1.2 2009/10/28 04:12:29 sar Exp $";
00026 #endif
00027 
00028 #include <sys/types.h>
00029 
00030 #include <netinet/in.h>
00031 #include <sys/socket.h>
00032 
00033 #include <errno.h>
00034 #include <string.h>
00035 #include <ctype.h>
00036 
00037 #include "minires.h"
00038 #include "arpa/nameser.h"
00039 
00040 /* Data. */
00041 
00042 static const char       digits[] = "0123456789";
00043 
00044 /* Forward. */
00045 
00046 static int              special(int);
00047 static int              printable(int);
00048 static int              dn_find(const u_char *, const u_char *,
00049                                 const u_char * const *,
00050                                 const u_char * const *);
00051 
00052 /* Public. */
00053 
00054 /*
00055  * MRns_name_ntop(src, dst, dstsiz)
00056  *      Convert an encoded domain name to printable ascii as per RFC1035.
00057  * return:
00058  *      Number of bytes written to buffer, or -1 (with errno set)
00059  * notes:
00060  *      The root is returned as "."
00061  *      All other domains are returned in non absolute form
00062  */
00063 int
00064 MRns_name_ntop(const u_char *src, char *dst, size_t dstsiz) {
00065         const u_char *cp;
00066         char *dn, *eom;
00067         u_char c;
00068         u_int n;
00069 
00070         cp = src;
00071         dn = dst;
00072         eom = dst + dstsiz;
00073 
00074         while ((n = *cp++) != 0) {
00075                 if ((n & NS_CMPRSFLGS) != 0) {
00076                         /* Some kind of compression pointer. */
00077                         errno = EMSGSIZE;
00078                         return (-1);
00079                 }
00080                 if (dn != dst) {
00081                         if (dn >= eom) {
00082                                 errno = EMSGSIZE;
00083                                 return (-1);
00084                         }
00085                         *dn++ = '.';
00086                 }
00087                 if (dn + n >= eom) {
00088                         errno = EMSGSIZE;
00089                         return (-1);
00090                 }
00091                 for ((void)NULL; n > 0; n--) {
00092                         c = *cp++;
00093                         if (special(c)) {
00094                                 if (dn + 1 >= eom) {
00095                                         errno = EMSGSIZE;
00096                                         return (-1);
00097                                 }
00098                                 *dn++ = '\\';
00099                                 *dn++ = (char)c;
00100                         } else if (!printable(c)) {
00101                                 if (dn + 3 >= eom) {
00102                                         errno = EMSGSIZE;
00103                                         return (-1);
00104                                 }
00105                                 *dn++ = '\\';
00106                                 *dn++ = digits[c / 100];
00107                                 *dn++ = digits[(c % 100) / 10];
00108                                 *dn++ = digits[c % 10];
00109                         } else {
00110                                 if (dn >= eom) {
00111                                         errno = EMSGSIZE;
00112                                         return (-1);
00113                                 }
00114                                 *dn++ = (char)c;
00115                         }
00116                 }
00117         }
00118         if (dn == dst) {
00119                 if (dn >= eom) {
00120                         errno = EMSGSIZE;
00121                         return (-1);
00122                 }
00123                 *dn++ = '.';
00124         }
00125         if (dn >= eom) {
00126                 errno = EMSGSIZE;
00127                 return (-1);
00128         }
00129         *dn++ = '\0';
00130         return (dn - dst);
00131 }
00132 
00133 /*
00134  * MRns_name_pton(src, dst, dstsiz)
00135  *      Convert a ascii string into an encoded domain name as per RFC1035.
00136  * return:
00137  *      -1 if it fails
00138  *      1 if string was fully qualified
00139  *      0 is string was not fully qualified
00140  * notes:
00141  *      Enforces label and domain length limits.
00142  */
00143 
00144 int
00145 MRns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
00146         u_char *label, *bp, *eom;
00147         int c, n, escaped;
00148         char *cp;
00149 
00150         escaped = 0;
00151         bp = dst;
00152         eom = dst + dstsiz;
00153         label = bp++;
00154 
00155         while ((c = *src++) != 0) {
00156                 if (escaped) {
00157                         if ((cp = strchr(digits, c)) != NULL) {
00158                                 n = (cp - digits) * 100;
00159                                 if ((c = *src++) == 0 ||
00160                                     (cp = strchr(digits, c)) == NULL) {
00161                                         errno = EMSGSIZE;
00162                                         return (-1);
00163                                 }
00164                                 n += (cp - digits) * 10;
00165                                 if ((c = *src++) == 0 ||
00166                                     (cp = strchr(digits, c)) == NULL) {
00167                                         errno = EMSGSIZE;
00168                                         return (-1);
00169                                 }
00170                                 n += (cp - digits);
00171                                 if (n > 255) {
00172                                         errno = EMSGSIZE;
00173                                         return (-1);
00174                                 }
00175                                 c = n;
00176                         }
00177                         escaped = 0;
00178                 } else if (c == '\\') {
00179                         escaped = 1;
00180                         continue;
00181                 } else if (c == '.') {
00182                         c = (bp - label - 1);
00183                         if ((c & NS_CMPRSFLGS) != 0) {  /* Label too big. */
00184                                 errno = EMSGSIZE;
00185                                 return (-1);
00186                         }
00187                         if (label >= eom) {
00188                                 errno = EMSGSIZE;
00189                                 return (-1);
00190                         }
00191                         *label = c;
00192                         /* Fully qualified ? */
00193                         if (*src == '\0') {
00194                                 if (c != 0) {
00195                                         if (bp >= eom) {
00196                                                 errno = EMSGSIZE;
00197                                                 return (-1);
00198                                         }
00199                                         *bp++ = '\0';
00200                                 }
00201                                 if ((bp - dst) > MAXCDNAME) {
00202                                         errno = EMSGSIZE;
00203                                         return (-1);
00204                                 }
00205                                 return (1);
00206                         }
00207                         if (c == 0 || *src == '.') {
00208                                 errno = EMSGSIZE;
00209                                 return (-1);
00210                         }
00211                         label = bp++;
00212                         continue;
00213                 }
00214                 if (bp >= eom) {
00215                         errno = EMSGSIZE;
00216                         return (-1);
00217                 }
00218                 *bp++ = (u_char)c;
00219         }
00220         c = (bp - label - 1);
00221         if ((c & NS_CMPRSFLGS) != 0) {          /* Label too big. */
00222                 errno = EMSGSIZE;
00223                 return (-1);
00224         }
00225         if (label >= eom) {
00226                 errno = EMSGSIZE;
00227                 return (-1);
00228         }
00229         *label = c;
00230         if (c != 0) {
00231                 if (bp >= eom) {
00232                         errno = EMSGSIZE;
00233                         return (-1);
00234                 }
00235                 *bp++ = 0;
00236         }
00237         if ((bp - dst) > MAXCDNAME) {   /* src too big */
00238                 errno = EMSGSIZE;
00239                 return (-1);
00240         }
00241         return (0);
00242 }
00243 
00244 /*
00245  * MRns_name_ntol(src, dst, dstsiz)
00246  *      Convert a network strings labels into all lowercase.
00247  * return:
00248  *      Number of bytes written to buffer, or -1 (with errno set)
00249  * notes:
00250  *      Enforces label and domain length limits.
00251  */
00252 
00253 int
00254 MRns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) {
00255         const u_char *cp;
00256         u_char *dn, *eom;
00257         u_char c;
00258         u_int n;
00259 
00260         cp = src;
00261         dn = dst;
00262         eom = dst + dstsiz;
00263 
00264         if (dn >= eom) {
00265                 errno = EMSGSIZE;
00266                 return (-1);
00267         }
00268         while ((n = *cp++) != 0) {
00269                 if ((n & NS_CMPRSFLGS) != 0) {
00270                         /* Some kind of compression pointer. */
00271                         errno = EMSGSIZE;
00272                         return (-1);
00273                 }
00274                 *dn++ = n;
00275                 if (dn + n >= eom) {
00276                         errno = EMSGSIZE;
00277                         return (-1);
00278                 }
00279                 for ((void)NULL; n > 0; n--) {
00280                         c = *cp++;
00281                         if (isupper(c))
00282                                 *dn++ = tolower(c);
00283                         else
00284                                 *dn++ = c;
00285                 }
00286         }
00287         *dn++ = '\0';
00288         return (dn - dst);
00289 }
00290 
00291 /*
00292  * MRns_name_unpack(msg, eom, src, dst, dstsiz)
00293  *      Unpack a domain name from a message, source may be compressed.
00294  * return:
00295  *      -1 if it fails, or consumed octets if it succeeds.
00296  */
00297 int
00298 MRns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
00299                  u_char *dst, size_t dstsiz)
00300 {
00301         const u_char *srcp, *dstlim;
00302         u_char *dstp;
00303         unsigned n;
00304         int len;
00305         int checked;
00306 
00307         len = -1;
00308         checked = 0;
00309         dstp = dst;
00310         srcp = src;
00311         dstlim = dst + dstsiz;
00312         if (srcp < msg || srcp >= eom) {
00313                 errno = EMSGSIZE;
00314                 return (-1);
00315         }
00316         /* Fetch next label in domain name. */
00317         while ((n = *srcp++) != 0) {
00318                 /* Check for indirection. */
00319                 switch (n & NS_CMPRSFLGS) {
00320                 case 0:
00321                         /* Limit checks. */
00322                         if (dstp + n + 1 >= dstlim || srcp + n >= eom) {
00323                                 errno = EMSGSIZE;
00324                                 return (-1);
00325                         }
00326                         checked += n + 1;
00327                         *dstp++ = n;
00328                         memcpy(dstp, srcp, n);
00329                         dstp += n;
00330                         srcp += n;
00331                         break;
00332 
00333                 case NS_CMPRSFLGS:
00334                         if (srcp >= eom) {
00335                                 errno = EMSGSIZE;
00336                                 return (-1);
00337                         }
00338                         if (len < 0)
00339                                 len = srcp - src + 1;
00340                         srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
00341                         if (srcp < msg || srcp >= eom) {  /* Out of range. */
00342                                 errno = EMSGSIZE;
00343                                 return (-1);
00344                         }
00345                         checked += 2;
00346                         /*
00347                          * Check for loops in the compressed name;
00348                          * if we've looked at the whole message,
00349                          * there must be a loop.
00350                          */
00351                         if (checked >= eom - msg) {
00352                                 errno = EMSGSIZE;
00353                                 return (-1);
00354                         }
00355                         break;
00356 
00357                 default:
00358                         errno = EMSGSIZE;
00359                         return (-1);                    /* flag error */
00360                 }
00361         }
00362         *dstp = '\0';
00363         if (len < 0)
00364                 len = srcp - src;
00365         return (len);
00366 }
00367 
00368 /*
00369  * MRns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr)
00370  *      Pack domain name 'domain' into 'comp_dn'.
00371  * return:
00372  *      Size of the compressed name, or -1.
00373  * notes:
00374  *      'dnptrs' is an array of pointers to previous compressed names.
00375  *      dnptrs[0] is a pointer to the beginning of the message. The array
00376  *      ends with NULL.
00377  *      'lastdnptr' is a pointer to the end of the array pointed to
00378  *      by 'dnptrs'.
00379  * Side effects:
00380  *      The list of pointers in dnptrs is updated for labels inserted into
00381  *      the message as we compress the name.  If 'dnptr' is NULL, we don't
00382  *      try to compress names. If 'lastdnptr' is NULL, we don't update the
00383  *      list.
00384  */
00385 int
00386 MRns_name_pack(const u_char *src, u_char *dst, unsigned dstsiz,
00387                const u_char **dnptrs, const u_char **lastdnptr)
00388 {
00389         u_char *dstp;
00390         const u_char **cpp, **lpp, *eob, *msg;
00391         const u_char *srcp;
00392         unsigned n;
00393         int l;
00394 
00395         srcp = src;
00396         dstp = dst;
00397         eob = dstp + dstsiz;
00398         lpp = cpp = NULL;
00399         if (dnptrs != NULL) {
00400                 if ((msg = *dnptrs++) != NULL) {
00401                         for (cpp = dnptrs; *cpp != NULL; cpp++)
00402                                 (void)NULL;
00403                         lpp = cpp;      /* end of list to search */
00404                 }
00405         } else
00406                 msg = NULL;
00407 
00408         /* make sure the domain we are about to add is legal */
00409         l = 0;
00410         do {
00411                 n = *srcp;
00412                 if ((n & NS_CMPRSFLGS) != 0) {
00413                         errno = EMSGSIZE;
00414                         return (-1);
00415                 }
00416                 l += n + 1;
00417                 if (l > MAXCDNAME) {
00418                         errno = EMSGSIZE;
00419                         return (-1);
00420                 }
00421                 srcp += n + 1;
00422         } while (n != 0);
00423 
00424         /* from here on we need to reset compression pointer array on error */
00425         srcp = src;
00426         do {
00427                 /* Look to see if we can use pointers. */
00428                 n = *srcp;
00429                 if (n != 0 && msg != NULL) {
00430                         l = dn_find(srcp, msg, (const u_char * const *)dnptrs,
00431                                     (const u_char * const *)lpp);
00432                         if (l >= 0) {
00433                                 if (dstp + 1 >= eob) {
00434                                         goto cleanup;
00435                                 }
00436                                 *dstp++ = (l >> 8) | NS_CMPRSFLGS;
00437                                 *dstp++ = l % 256;
00438                                 return (dstp - dst);
00439                         }
00440                         /* Not found, save it. */
00441                         if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
00442                             (dstp - msg) < 0x4000) {
00443                                 *cpp++ = dstp;
00444                                 *cpp = NULL;
00445                         }
00446                 }
00447                 /* copy label to buffer */
00448                 if (n & NS_CMPRSFLGS) {         /* Should not happen. */
00449                         goto cleanup;
00450                 }
00451                 if (dstp + 1 + n >= eob) {
00452                         goto cleanup;
00453                 }
00454                 memcpy(dstp, srcp, n + 1);
00455                 srcp += n + 1;
00456                 dstp += n + 1;
00457         } while (n != 0);
00458 
00459         if (dstp > eob) {
00460 cleanup:
00461                 if (msg != NULL)
00462                         *lpp = NULL;
00463                 errno = EMSGSIZE;
00464                 return (-1);
00465         } 
00466         return (dstp - dst);
00467 }
00468 
00469 /*
00470  * MRns_name_uncompress(msg, eom, src, dst, dstsiz)
00471  *      Expand compressed domain name to presentation format.
00472  * return:
00473  *      Number of bytes read out of `src', or -1 (with errno set).
00474  * note:
00475  *      Root domain returns as "." not "".
00476  */
00477 int
00478 MRns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
00479                      char *dst, size_t dstsiz)
00480 {
00481         u_char tmp[NS_MAXCDNAME];
00482         int n;
00483         
00484         if ((n = MRns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
00485                 return (-1);
00486         if (MRns_name_ntop(tmp, dst, dstsiz) == -1)
00487                 return (-1);
00488         return (n);
00489 }
00490 
00491 /*
00492  * MRns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr)
00493  *      Compress a domain name into wire format, using compression pointers.
00494  * return:
00495  *      Number of bytes consumed in `dst' or -1 (with errno set).
00496  * notes:
00497  *      'dnptrs' is an array of pointers to previous compressed names.
00498  *      dnptrs[0] is a pointer to the beginning of the message.
00499  *      The list ends with NULL.  'lastdnptr' is a pointer to the end of the
00500  *      array pointed to by 'dnptrs'. Side effect is to update the list of
00501  *      pointers for labels inserted into the message as we compress the name.
00502  *      If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
00503  *      is NULL, we don't update the list.
00504  */
00505 int
00506 MRns_name_compress(const char *src, u_char *dst, size_t dstsiz,
00507                  const u_char **dnptrs, const u_char **lastdnptr)
00508 {
00509         u_char tmp[NS_MAXCDNAME];
00510 
00511         if (MRns_name_pton(src, tmp, sizeof tmp) == -1)
00512                 return (-1);
00513         return (MRns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
00514 }
00515 
00516 /*
00517  * MRns_name_skip(ptrptr, eom)
00518  *      Advance *ptrptr to skip over the compressed name it points at.
00519  * return:
00520  *      0 on success, -1 (with errno set) on failure.
00521  */
00522 int
00523 MRns_name_skip(const u_char **ptrptr, const u_char *eom) {
00524         const u_char *cp;
00525         u_int n;
00526 
00527         cp = *ptrptr;
00528         while (cp < eom && (n = *cp++) != 0) {
00529                 /* Check for indirection. */
00530                 switch (n & NS_CMPRSFLGS) {
00531                 case 0:                 /* normal case, n == len */
00532                         cp += n;
00533                         continue;
00534                 case NS_CMPRSFLGS:      /* indirection */
00535                         cp++;
00536                         break;
00537                 default:                /* illegal type */
00538                         errno = EMSGSIZE;
00539                         return (-1);
00540                 }
00541                 break;
00542         }
00543         if (cp > eom) {
00544                 errno = EMSGSIZE;
00545                 return (-1);
00546         }
00547         *ptrptr = cp;
00548         return (0);
00549 }
00550 
00551 /* Private. */
00552 
00553 /*
00554  * special(ch)
00555  *      Thinking in noninternationalized USASCII (per the DNS spec),
00556  *      is this characted special ("in need of quoting") ?
00557  * return:
00558  *      boolean.
00559  */
00560 static int
00561 special(int ch) {
00562         switch (ch) {
00563         case 0x22: /* '"' */
00564         case 0x2E: /* '.' */
00565         case 0x3B: /* ';' */
00566         case 0x5C: /* '\\' */
00567         /* Special modifiers in zone files. */
00568         case 0x40: /* '@' */
00569         case 0x24: /* '$' */
00570                 return (1);
00571         default:
00572                 return (0);
00573         }
00574 }
00575 
00576 /*
00577  * printable(ch)
00578  *      Thinking in noninternationalized USASCII (per the DNS spec),
00579  *      is this character visible and not a space when printed ?
00580  * return:
00581  *      boolean.
00582  */
00583 static int
00584 printable(int ch) {
00585         return (ch > 0x20 && ch < 0x7f);
00586 }
00587 
00588 /*
00589  *      Thinking in noninternationalized USASCII (per the DNS spec),
00590  *      convert this character to lower case if it's upper case.
00591  */
00592 static int
00593 mklower(int ch) {
00594         if (ch >= 0x41 && ch <= 0x5A)
00595                 return (ch + 0x20);
00596         return (ch);
00597 }
00598 
00599 /*
00600  * dn_find(domain, msg, dnptrs, lastdnptr)
00601  *      Search for the counted-label name in an array of compressed names.
00602  * return:
00603  *      offset from msg if found, or -1.
00604  * notes:
00605  *      dnptrs is the pointer to the first name on the list,
00606  *      not the pointer to the start of the message.
00607  */
00608 static int
00609 dn_find(const u_char *domain, const u_char *msg,
00610         const u_char * const *dnptrs,
00611         const u_char * const *lastdnptr)
00612 {
00613         const u_char *dn, *cp, *sp;
00614         const u_char * const *cpp;
00615         u_int n;
00616 
00617         for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
00618                 dn = domain;
00619                 sp = cp = *cpp;
00620                 while ((n = *cp++) != 0) {
00621                         /*
00622                          * check for indirection
00623                          */
00624                         switch (n & NS_CMPRSFLGS) {
00625                         case 0:                 /* normal case, n == len */
00626                                 if (n != *dn++)
00627                                         goto next;
00628                                 for ((void)NULL; n > 0; n--)
00629                                         if (mklower(*dn++) != mklower(*cp++))
00630                                                 goto next;
00631                                 /* Is next root for both ? */
00632                                 if (*dn == '\0' && *cp == '\0')
00633                                         return (sp - msg);
00634                                 if (*dn)
00635                                         continue;
00636                                 goto next;
00637 
00638                         case NS_CMPRSFLGS:      /* indirection */
00639                                 cp = msg + (((n & 0x3f) << 8) | *cp);
00640                                 break;
00641 
00642                         default:        /* illegal type */
00643                                 errno = EMSGSIZE;
00644                                 return (-1);
00645                         }
00646                 }
00647  next: ;
00648         }
00649         errno = ENOENT;
00650         return (-1);
00651 }

Generated on 5 Apr 2014 for ISC DHCP by  doxygen 1.6.1