ISC DHCP  4.3.3
A reference DHCPv4 and DHCPv6 implementation
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ns_name.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2004,2009,2014 by Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (c) 1996-2003 by Internet Software Consortium
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  *
17  * Internet Systems Consortium, Inc.
18  * 950 Charter Street
19  * Redwood City, CA 94063
20  * <info@isc.org>
21  * http://www.isc.org/
22  */
23 
24 #include <sys/types.h>
25 
26 #include <netinet/in.h>
27 #include <sys/socket.h>
28 
29 #include <errno.h>
30 #include <string.h>
31 #include <ctype.h>
32 
33 #include "minires.h"
34 #include "arpa/nameser.h"
35 
36 /* Data. */
37 
38 static const char digits[] = "0123456789";
39 
40 /* Forward. */
41 
42 static int special(int);
43 static int printable(int);
44 static int dn_find(const u_char *, const u_char *,
45  const u_char * const *,
46  const u_char * const *);
47 
48 /* Public. */
49 
50 /*
51  * MRns_name_ntop(src, dst, dstsiz)
52  * Convert an encoded domain name to printable ascii as per RFC1035.
53  * return:
54  * Number of bytes written to buffer, or -1 (with errno set)
55  * notes:
56  * The root is returned as "."
57  * All other domains are returned in non absolute form
58  */
59 int
60 MRns_name_ntop(const u_char *src, char *dst, size_t dstsiz) {
61  const u_char *cp;
62  char *dn, *eom;
63  u_char c;
64  u_int n;
65 
66  cp = src;
67  dn = dst;
68  eom = dst + dstsiz;
69 
70  while ((n = *cp++) != 0) {
71  if ((n & NS_CMPRSFLGS) != 0) {
72  /* Some kind of compression pointer. */
73  errno = EMSGSIZE;
74  return (-1);
75  }
76  if (dn != dst) {
77  if (dn >= eom) {
78  errno = EMSGSIZE;
79  return (-1);
80  }
81  *dn++ = '.';
82  }
83  if (dn + n >= eom) {
84  errno = EMSGSIZE;
85  return (-1);
86  }
87  for ((void)NULL; n > 0; n--) {
88  c = *cp++;
89  if (special(c)) {
90  if (dn + 1 >= eom) {
91  errno = EMSGSIZE;
92  return (-1);
93  }
94  *dn++ = '\\';
95  *dn++ = (char)c;
96  } else if (!printable(c)) {
97  if (dn + 3 >= eom) {
98  errno = EMSGSIZE;
99  return (-1);
100  }
101  *dn++ = '\\';
102  *dn++ = digits[c / 100];
103  *dn++ = digits[(c % 100) / 10];
104  *dn++ = digits[c % 10];
105  } else {
106  if (dn >= eom) {
107  errno = EMSGSIZE;
108  return (-1);
109  }
110  *dn++ = (char)c;
111  }
112  }
113  }
114  if (dn == dst) {
115  if (dn >= eom) {
116  errno = EMSGSIZE;
117  return (-1);
118  }
119  *dn++ = '.';
120  }
121  if (dn >= eom) {
122  errno = EMSGSIZE;
123  return (-1);
124  }
125  *dn++ = '\0';
126  return (dn - dst);
127 }
128 
129 /*
130  * MRns_name_pton(src, dst, dstsiz)
131  * Convert a ascii string into an encoded domain name as per RFC1035.
132  * return:
133  * -1 if it fails
134  * 1 if string was fully qualified
135  * 0 is string was not fully qualified
136  * notes:
137  * Enforces label and domain length limits.
138  */
139 
140 int
141 MRns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
142  u_char *label, *bp, *eom;
143  int c, n, escaped;
144  char *cp;
145 
146  escaped = 0;
147  bp = dst;
148  eom = dst + dstsiz;
149  label = bp++;
150 
151  while ((c = *src++) != 0) {
152  if (escaped) {
153  if ((cp = strchr(digits, c)) != NULL) {
154  n = (cp - digits) * 100;
155  if ((c = *src++) == 0 ||
156  (cp = strchr(digits, c)) == NULL) {
157  errno = EMSGSIZE;
158  return (-1);
159  }
160  n += (cp - digits) * 10;
161  if ((c = *src++) == 0 ||
162  (cp = strchr(digits, c)) == NULL) {
163  errno = EMSGSIZE;
164  return (-1);
165  }
166  n += (cp - digits);
167  if (n > 255) {
168  errno = EMSGSIZE;
169  return (-1);
170  }
171  c = n;
172  }
173  escaped = 0;
174  } else if (c == '\\') {
175  escaped = 1;
176  continue;
177  } else if (c == '.') {
178  c = (bp - label - 1);
179  if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */
180  errno = EMSGSIZE;
181  return (-1);
182  }
183  if (label >= eom) {
184  errno = EMSGSIZE;
185  return (-1);
186  }
187  *label = c;
188  /* Fully qualified ? */
189  if (*src == '\0') {
190  if (c != 0) {
191  if (bp >= eom) {
192  errno = EMSGSIZE;
193  return (-1);
194  }
195  *bp++ = '\0';
196  }
197  if ((bp - dst) > MAXCDNAME) {
198  errno = EMSGSIZE;
199  return (-1);
200  }
201  return (1);
202  }
203  if (c == 0 || *src == '.') {
204  errno = EMSGSIZE;
205  return (-1);
206  }
207  label = bp++;
208  continue;
209  }
210  if (bp >= eom) {
211  errno = EMSGSIZE;
212  return (-1);
213  }
214  *bp++ = (u_char)c;
215  }
216  c = (bp - label - 1);
217  if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */
218  errno = EMSGSIZE;
219  return (-1);
220  }
221  if (label >= eom) {
222  errno = EMSGSIZE;
223  return (-1);
224  }
225  *label = c;
226  if (c != 0) {
227  if (bp >= eom) {
228  errno = EMSGSIZE;
229  return (-1);
230  }
231  *bp++ = 0;
232  }
233  if ((bp - dst) > MAXCDNAME) { /* src too big */
234  errno = EMSGSIZE;
235  return (-1);
236  }
237  return (0);
238 }
239 
240 /*
241  * MRns_name_ntol(src, dst, dstsiz)
242  * Convert a network strings labels into all lowercase.
243  * return:
244  * Number of bytes written to buffer, or -1 (with errno set)
245  * notes:
246  * Enforces label and domain length limits.
247  */
248 
249 int
250 MRns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) {
251  const u_char *cp;
252  u_char *dn, *eom;
253  u_char c;
254  u_int n;
255 
256  cp = src;
257  dn = dst;
258  eom = dst + dstsiz;
259 
260  if (dn >= eom) {
261  errno = EMSGSIZE;
262  return (-1);
263  }
264  while ((n = *cp++) != 0) {
265  if ((n & NS_CMPRSFLGS) != 0) {
266  /* Some kind of compression pointer. */
267  errno = EMSGSIZE;
268  return (-1);
269  }
270  *dn++ = n;
271  if (dn + n >= eom) {
272  errno = EMSGSIZE;
273  return (-1);
274  }
275  for ((void)NULL; n > 0; n--) {
276  c = *cp++;
277  if (isupper(c))
278  *dn++ = tolower(c);
279  else
280  *dn++ = c;
281  }
282  }
283  *dn++ = '\0';
284  return (dn - dst);
285 }
286 
287 /*
288  * MRns_name_unpack(msg, eom, src, dst, dstsiz)
289  * Unpack a domain name from a message, source may be compressed.
290  * return:
291  * -1 if it fails, or consumed octets if it succeeds.
292  */
293 int
294 MRns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
295  u_char *dst, size_t dstsiz)
296 {
297  const u_char *srcp, *dstlim;
298  u_char *dstp;
299  unsigned n;
300  int len;
301  int checked;
302 
303  len = -1;
304  checked = 0;
305  dstp = dst;
306  srcp = src;
307  dstlim = dst + dstsiz;
308  if (srcp < msg || srcp >= eom) {
309  errno = EMSGSIZE;
310  return (-1);
311  }
312  /* Fetch next label in domain name. */
313  while ((n = *srcp++) != 0) {
314  /* Check for indirection. */
315  switch (n & NS_CMPRSFLGS) {
316  case 0:
317  /* Limit checks. */
318  if (dstp + n + 1 >= dstlim || srcp + n >= eom) {
319  errno = EMSGSIZE;
320  return (-1);
321  }
322  checked += n + 1;
323  *dstp++ = n;
324  memcpy(dstp, srcp, n);
325  dstp += n;
326  srcp += n;
327  break;
328 
329  case NS_CMPRSFLGS:
330  if (srcp >= eom) {
331  errno = EMSGSIZE;
332  return (-1);
333  }
334  if (len < 0)
335  len = srcp - src + 1;
336  srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
337  if (srcp < msg || srcp >= eom) { /* Out of range. */
338  errno = EMSGSIZE;
339  return (-1);
340  }
341  checked += 2;
342  /*
343  * Check for loops in the compressed name;
344  * if we've looked at the whole message,
345  * there must be a loop.
346  */
347  if (checked >= eom - msg) {
348  errno = EMSGSIZE;
349  return (-1);
350  }
351  break;
352 
353  default:
354  errno = EMSGSIZE;
355  return (-1); /* flag error */
356  }
357  }
358  *dstp = '\0';
359  if (len < 0)
360  len = srcp - src;
361  return (len);
362 }
363 
364 /*
365  * MRns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr)
366  * Pack domain name 'domain' into 'comp_dn'.
367  * return:
368  * Size of the compressed name, or -1.
369  * notes:
370  * 'dnptrs' is an array of pointers to previous compressed names.
371  * dnptrs[0] is a pointer to the beginning of the message. The array
372  * ends with NULL.
373  * 'lastdnptr' is a pointer to the end of the array pointed to
374  * by 'dnptrs'.
375  * Side effects:
376  * The list of pointers in dnptrs is updated for labels inserted into
377  * the message as we compress the name. If 'dnptr' is NULL, we don't
378  * try to compress names. If 'lastdnptr' is NULL, we don't update the
379  * list.
380  */
381 int
382 MRns_name_pack(const u_char *src, u_char *dst, unsigned dstsiz,
383  const u_char **dnptrs, const u_char **lastdnptr)
384 {
385  u_char *dstp;
386  const u_char **cpp, **lpp, *eob, *msg;
387  const u_char *srcp;
388  unsigned n;
389  int l;
390 
391  srcp = src;
392  dstp = dst;
393  eob = dstp + dstsiz;
394  lpp = cpp = NULL;
395  if (dnptrs != NULL) {
396  if ((msg = *dnptrs++) != NULL) {
397  for (cpp = dnptrs; *cpp != NULL; cpp++)
398  (void)NULL;
399  lpp = cpp; /* end of list to search */
400  }
401  } else
402  msg = NULL;
403 
404  /* make sure the domain we are about to add is legal */
405  l = 0;
406  do {
407  n = *srcp;
408  if ((n & NS_CMPRSFLGS) != 0) {
409  errno = EMSGSIZE;
410  return (-1);
411  }
412  l += n + 1;
413  if (l > MAXCDNAME) {
414  errno = EMSGSIZE;
415  return (-1);
416  }
417  srcp += n + 1;
418  } while (n != 0);
419 
420  /* from here on we need to reset compression pointer array on error */
421  srcp = src;
422  do {
423  /* Look to see if we can use pointers. */
424  n = *srcp;
425  if (n != 0 && msg != NULL) {
426  l = dn_find(srcp, msg, (const u_char * const *)dnptrs,
427  (const u_char * const *)lpp);
428  if (l >= 0) {
429  if (dstp + 1 >= eob) {
430  goto cleanup;
431  }
432  *dstp++ = (l >> 8) | NS_CMPRSFLGS;
433  *dstp++ = l % 256;
434  return (dstp - dst);
435  }
436  /* Not found, save it. */
437  if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
438  (dstp - msg) < 0x4000) {
439  *cpp++ = dstp;
440  *cpp = NULL;
441  }
442  }
443  /* copy label to buffer */
444  if (n & NS_CMPRSFLGS) { /* Should not happen. */
445  goto cleanup;
446  }
447  if (dstp + 1 + n >= eob) {
448  goto cleanup;
449  }
450  memcpy(dstp, srcp, n + 1);
451  srcp += n + 1;
452  dstp += n + 1;
453  } while (n != 0);
454 
455  if (dstp > eob) {
456 cleanup:
457  if (msg != NULL)
458  *lpp = NULL;
459  errno = EMSGSIZE;
460  return (-1);
461  }
462  return (dstp - dst);
463 }
464 
465 /*
466  * MRns_name_uncompress(msg, eom, src, dst, dstsiz)
467  * Expand compressed domain name to presentation format.
468  * return:
469  * Number of bytes read out of `src', or -1 (with errno set).
470  * note:
471  * Root domain returns as "." not "".
472  */
473 int
474 MRns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
475  char *dst, size_t dstsiz)
476 {
477  u_char tmp[NS_MAXCDNAME];
478  int n;
479 
480  if ((n = MRns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
481  return (-1);
482  if (MRns_name_ntop(tmp, dst, dstsiz) == -1)
483  return (-1);
484  return (n);
485 }
486 
487 /*
488  * MRns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr)
489  * Compress a domain name into wire format, using compression pointers.
490  * return:
491  * Number of bytes consumed in `dst' or -1 (with errno set).
492  * notes:
493  * 'dnptrs' is an array of pointers to previous compressed names.
494  * dnptrs[0] is a pointer to the beginning of the message.
495  * The list ends with NULL. 'lastdnptr' is a pointer to the end of the
496  * array pointed to by 'dnptrs'. Side effect is to update the list of
497  * pointers for labels inserted into the message as we compress the name.
498  * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
499  * is NULL, we don't update the list.
500  */
501 int
502 MRns_name_compress(const char *src, u_char *dst, size_t dstsiz,
503  const u_char **dnptrs, const u_char **lastdnptr)
504 {
505  u_char tmp[NS_MAXCDNAME];
506 
507  if (MRns_name_pton(src, tmp, sizeof tmp) == -1)
508  return (-1);
509  return (MRns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
510 }
511 
512 /*
513  * MRns_name_skip(ptrptr, eom)
514  * Advance *ptrptr to skip over the compressed name it points at.
515  * return:
516  * 0 on success, -1 (with errno set) on failure.
517  */
518 int
519 MRns_name_skip(const u_char **ptrptr, const u_char *eom) {
520  const u_char *cp;
521  u_int n;
522 
523  cp = *ptrptr;
524  while (cp < eom && (n = *cp++) != 0) {
525  /* Check for indirection. */
526  switch (n & NS_CMPRSFLGS) {
527  case 0: /* normal case, n == len */
528  cp += n;
529  continue;
530  case NS_CMPRSFLGS: /* indirection */
531  cp++;
532  break;
533  default: /* illegal type */
534  errno = EMSGSIZE;
535  return (-1);
536  }
537  break;
538  }
539  if (cp > eom) {
540  errno = EMSGSIZE;
541  return (-1);
542  }
543  *ptrptr = cp;
544  return (0);
545 }
546 
547 /* Private. */
548 
549 /*
550  * special(ch)
551  * Thinking in noninternationalized USASCII (per the DNS spec),
552  * is this characted special ("in need of quoting") ?
553  * return:
554  * boolean.
555  */
556 static int
557 special(int ch) {
558  switch (ch) {
559  case 0x22: /* '"' */
560  case 0x2E: /* '.' */
561  case 0x3B: /* ';' */
562  case 0x5C: /* '\\' */
563  /* Special modifiers in zone files. */
564  case 0x40: /* '@' */
565  case 0x24: /* '$' */
566  return (1);
567  default:
568  return (0);
569  }
570 }
571 
572 /*
573  * printable(ch)
574  * Thinking in noninternationalized USASCII (per the DNS spec),
575  * is this character visible and not a space when printed ?
576  * return:
577  * boolean.
578  */
579 static int
580 printable(int ch) {
581  return (ch > 0x20 && ch < 0x7f);
582 }
583 
584 /*
585  * Thinking in noninternationalized USASCII (per the DNS spec),
586  * convert this character to lower case if it's upper case.
587  */
588 static int
589 mklower(int ch) {
590  if (ch >= 0x41 && ch <= 0x5A)
591  return (ch + 0x20);
592  return (ch);
593 }
594 
595 /*
596  * dn_find(domain, msg, dnptrs, lastdnptr)
597  * Search for the counted-label name in an array of compressed names.
598  * return:
599  * offset from msg if found, or -1.
600  * notes:
601  * dnptrs is the pointer to the first name on the list,
602  * not the pointer to the start of the message.
603  */
604 static int
605 dn_find(const u_char *domain, const u_char *msg,
606  const u_char * const *dnptrs,
607  const u_char * const *lastdnptr)
608 {
609  const u_char *dn, *cp, *sp;
610  const u_char * const *cpp;
611  u_int n;
612 
613  for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
614  dn = domain;
615  sp = cp = *cpp;
616  while ((n = *cp++) != 0) {
617  /*
618  * check for indirection
619  */
620  switch (n & NS_CMPRSFLGS) {
621  case 0: /* normal case, n == len */
622  if (n != *dn++)
623  goto next;
624  for ((void)NULL; n > 0; n--)
625  if (mklower(*dn++) != mklower(*cp++))
626  goto next;
627  /* Is next root for both ? */
628  if (*dn == '\0' && *cp == '\0')
629  return (sp - msg);
630  if (*dn)
631  continue;
632  goto next;
633 
634  case NS_CMPRSFLGS: /* indirection */
635  cp = msg + (((n & 0x3f) << 8) | *cp);
636  break;
637 
638  default: /* illegal type */
639  errno = EMSGSIZE;
640  return (-1);
641  }
642  }
643  next: ;
644  }
645  errno = ENOENT;
646  return (-1);
647 }
648 
678 int MRns_name_uncompress_list(const unsigned char* buf, int buflen,
679  char* dst_buf, size_t dst_size)
680 {
681  const unsigned char* src = buf;
682  char* dst = dst_buf;
683  int consumed = 1;
684  int dst_remaining = dst_size;
685  int added_len = 0;
686  int first_pass = 1;
687 
688  if (!buf || buflen == 0 || *buf == 0x00) {
689  /* nothing to do */
690  *dst = 0;
691  return (0);
692  }
693 
694  while ((consumed > 0) && (src < (buf + buflen)))
695  {
696  if (dst_remaining <= 0) {
697  errno = EMSGSIZE;
698  return (-1);
699  }
700 
701  if (!first_pass) {
702  *dst++ = ',';
703  *dst = '\0';
704  dst_remaining--;
705  }
706 
707  consumed = MRns_name_uncompress(buf, buf + buflen, src,
708  dst, dst_remaining);
709  if (consumed < 0) {
710  return (-1);
711  }
712 
713  src += consumed;
714  added_len = strlen(dst);
715  dst_remaining -= added_len;
716  dst += added_len;
717  first_pass = 0;
718  }
719  *dst='\0';
720 
721  /* return the length of the uncompressed list string */
722  return (strlen(dst_buf));
723 }
724 
747 int MRns_name_compress_list(const char* buf, int buflen,
748  unsigned char* compbuf, size_t compbuf_size)
749 {
750  char cur_name[NS_MAXCDNAME];
751  const unsigned char *dnptrs[256], **lastdnptr;
752  const char* src;
753  const char* src_end;
754  unsigned clen = 0;
755  int result = 0;
756 
757  memset(compbuf, 0, compbuf_size);
758  memset(dnptrs, 0, sizeof(dnptrs));
759  dnptrs[0] = compbuf;
760  lastdnptr = &dnptrs[255];
761 
762  src = buf;
763  src_end = buf + buflen;
764  while (src < src_end) {
765  char *comma = strchr(src, ',');
766  int copylen = ((comma != NULL) ? comma - src : strlen(src));
767  if (copylen > (sizeof(cur_name) - 1)) {
768  errno = EMSGSIZE;
769  return (-1);
770  }
771 
772  memcpy(cur_name, src, copylen);
773  cur_name[copylen] = '\0';
774  src += copylen + 1;
775 
776  result = MRns_name_compress(cur_name, compbuf + clen,
777  compbuf_size - clen,
778  dnptrs, lastdnptr);
779 
780  if (result < 0) {
781  return (-1);
782  }
783 
784  clen += result;
785  }
786 
787  /* return size of compressed list */
788  return(clen);
789 }
#define NS_MAXCDNAME
Definition: nameser.h:75
int MRns_name_ntop(const u_char *src, char *dst, size_t dstsiz)
Definition: ns_name.c:60
#define MAXCDNAME
int MRns_name_skip(const u_char **ptrptr, const u_char *eom)
Definition: ns_name.c:519
int MRns_name_compress(const char *src, u_char *dst, size_t dstsiz, const u_char **dnptrs, const u_char **lastdnptr)
Definition: ns_name.c:502
#define NS_CMPRSFLGS
Definition: nameser.h:85
int MRns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz)
Definition: ns_name.c:250
int MRns_name_compress_list(const char *buf, int buflen, unsigned char *compbuf, size_t compbuf_size)
Creates a compressed list from a string of comma-separated domain-names.
Definition: ns_name.c:747
int MRns_name_pack(const u_char *src, u_char *dst, unsigned dstsiz, const u_char **dnptrs, const u_char **lastdnptr)
Definition: ns_name.c:382
void cleanup(void)
int MRns_name_uncompress_list(const unsigned char *buf, int buflen, char *dst_buf, size_t dst_size)
Creates a string of comma-separated domain-names from a compressed list.
Definition: ns_name.c:678
int MRns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src, char *dst, size_t dstsiz)
Definition: ns_name.c:474
struct interface_info * next
Definition: dhcpd.h:1326
int MRns_name_pton(const char *src, u_char *dst, size_t dstsiz)
Definition: ns_name.c:141
int MRns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, u_char *dst, size_t dstsiz)
Definition: ns_name.c:294