ISC DHCP  4.3.0
A reference DHCPv4 and DHCPv6 implementation
ns_name.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2004,2009 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 #ifndef lint
25 static const char rcsid[] = "$Id: ns_name.c,v 1.2 2009/10/28 04:12:29 sar Exp $";
26 #endif
27 
28 #include <sys/types.h>
29 
30 #include <netinet/in.h>
31 #include <sys/socket.h>
32 
33 #include <errno.h>
34 #include <string.h>
35 #include <ctype.h>
36 
37 #include "minires.h"
38 #include "arpa/nameser.h"
39 
40 /* Data. */
41 
42 static const char digits[] = "0123456789";
43 
44 /* Forward. */
45 
46 static int special(int);
47 static int printable(int);
48 static int dn_find(const u_char *, const u_char *,
49  const u_char * const *,
50  const u_char * const *);
51 
52 /* Public. */
53 
54 /*
55  * MRns_name_ntop(src, dst, dstsiz)
56  * Convert an encoded domain name to printable ascii as per RFC1035.
57  * return:
58  * Number of bytes written to buffer, or -1 (with errno set)
59  * notes:
60  * The root is returned as "."
61  * All other domains are returned in non absolute form
62  */
63 int
64 MRns_name_ntop(const u_char *src, char *dst, size_t dstsiz) {
65  const u_char *cp;
66  char *dn, *eom;
67  u_char c;
68  u_int n;
69 
70  cp = src;
71  dn = dst;
72  eom = dst + dstsiz;
73 
74  while ((n = *cp++) != 0) {
75  if ((n & NS_CMPRSFLGS) != 0) {
76  /* Some kind of compression pointer. */
77  errno = EMSGSIZE;
78  return (-1);
79  }
80  if (dn != dst) {
81  if (dn >= eom) {
82  errno = EMSGSIZE;
83  return (-1);
84  }
85  *dn++ = '.';
86  }
87  if (dn + n >= eom) {
88  errno = EMSGSIZE;
89  return (-1);
90  }
91  for ((void)NULL; n > 0; n--) {
92  c = *cp++;
93  if (special(c)) {
94  if (dn + 1 >= eom) {
95  errno = EMSGSIZE;
96  return (-1);
97  }
98  *dn++ = '\\';
99  *dn++ = (char)c;
100  } else if (!printable(c)) {
101  if (dn + 3 >= eom) {
102  errno = EMSGSIZE;
103  return (-1);
104  }
105  *dn++ = '\\';
106  *dn++ = digits[c / 100];
107  *dn++ = digits[(c % 100) / 10];
108  *dn++ = digits[c % 10];
109  } else {
110  if (dn >= eom) {
111  errno = EMSGSIZE;
112  return (-1);
113  }
114  *dn++ = (char)c;
115  }
116  }
117  }
118  if (dn == dst) {
119  if (dn >= eom) {
120  errno = EMSGSIZE;
121  return (-1);
122  }
123  *dn++ = '.';
124  }
125  if (dn >= eom) {
126  errno = EMSGSIZE;
127  return (-1);
128  }
129  *dn++ = '\0';
130  return (dn - dst);
131 }
132 
133 /*
134  * MRns_name_pton(src, dst, dstsiz)
135  * Convert a ascii string into an encoded domain name as per RFC1035.
136  * return:
137  * -1 if it fails
138  * 1 if string was fully qualified
139  * 0 is string was not fully qualified
140  * notes:
141  * Enforces label and domain length limits.
142  */
143 
144 int
145 MRns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
146  u_char *label, *bp, *eom;
147  int c, n, escaped;
148  char *cp;
149 
150  escaped = 0;
151  bp = dst;
152  eom = dst + dstsiz;
153  label = bp++;
154 
155  while ((c = *src++) != 0) {
156  if (escaped) {
157  if ((cp = strchr(digits, c)) != NULL) {
158  n = (cp - digits) * 100;
159  if ((c = *src++) == 0 ||
160  (cp = strchr(digits, c)) == NULL) {
161  errno = EMSGSIZE;
162  return (-1);
163  }
164  n += (cp - digits) * 10;
165  if ((c = *src++) == 0 ||
166  (cp = strchr(digits, c)) == NULL) {
167  errno = EMSGSIZE;
168  return (-1);
169  }
170  n += (cp - digits);
171  if (n > 255) {
172  errno = EMSGSIZE;
173  return (-1);
174  }
175  c = n;
176  }
177  escaped = 0;
178  } else if (c == '\\') {
179  escaped = 1;
180  continue;
181  } else if (c == '.') {
182  c = (bp - label - 1);
183  if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */
184  errno = EMSGSIZE;
185  return (-1);
186  }
187  if (label >= eom) {
188  errno = EMSGSIZE;
189  return (-1);
190  }
191  *label = c;
192  /* Fully qualified ? */
193  if (*src == '\0') {
194  if (c != 0) {
195  if (bp >= eom) {
196  errno = EMSGSIZE;
197  return (-1);
198  }
199  *bp++ = '\0';
200  }
201  if ((bp - dst) > MAXCDNAME) {
202  errno = EMSGSIZE;
203  return (-1);
204  }
205  return (1);
206  }
207  if (c == 0 || *src == '.') {
208  errno = EMSGSIZE;
209  return (-1);
210  }
211  label = bp++;
212  continue;
213  }
214  if (bp >= eom) {
215  errno = EMSGSIZE;
216  return (-1);
217  }
218  *bp++ = (u_char)c;
219  }
220  c = (bp - label - 1);
221  if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */
222  errno = EMSGSIZE;
223  return (-1);
224  }
225  if (label >= eom) {
226  errno = EMSGSIZE;
227  return (-1);
228  }
229  *label = c;
230  if (c != 0) {
231  if (bp >= eom) {
232  errno = EMSGSIZE;
233  return (-1);
234  }
235  *bp++ = 0;
236  }
237  if ((bp - dst) > MAXCDNAME) { /* src too big */
238  errno = EMSGSIZE;
239  return (-1);
240  }
241  return (0);
242 }
243 
244 /*
245  * MRns_name_ntol(src, dst, dstsiz)
246  * Convert a network strings labels into all lowercase.
247  * return:
248  * Number of bytes written to buffer, or -1 (with errno set)
249  * notes:
250  * Enforces label and domain length limits.
251  */
252 
253 int
254 MRns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) {
255  const u_char *cp;
256  u_char *dn, *eom;
257  u_char c;
258  u_int n;
259 
260  cp = src;
261  dn = dst;
262  eom = dst + dstsiz;
263 
264  if (dn >= eom) {
265  errno = EMSGSIZE;
266  return (-1);
267  }
268  while ((n = *cp++) != 0) {
269  if ((n & NS_CMPRSFLGS) != 0) {
270  /* Some kind of compression pointer. */
271  errno = EMSGSIZE;
272  return (-1);
273  }
274  *dn++ = n;
275  if (dn + n >= eom) {
276  errno = EMSGSIZE;
277  return (-1);
278  }
279  for ((void)NULL; n > 0; n--) {
280  c = *cp++;
281  if (isupper(c))
282  *dn++ = tolower(c);
283  else
284  *dn++ = c;
285  }
286  }
287  *dn++ = '\0';
288  return (dn - dst);
289 }
290 
291 /*
292  * MRns_name_unpack(msg, eom, src, dst, dstsiz)
293  * Unpack a domain name from a message, source may be compressed.
294  * return:
295  * -1 if it fails, or consumed octets if it succeeds.
296  */
297 int
298 MRns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
299  u_char *dst, size_t dstsiz)
300 {
301  const u_char *srcp, *dstlim;
302  u_char *dstp;
303  unsigned n;
304  int len;
305  int checked;
306 
307  len = -1;
308  checked = 0;
309  dstp = dst;
310  srcp = src;
311  dstlim = dst + dstsiz;
312  if (srcp < msg || srcp >= eom) {
313  errno = EMSGSIZE;
314  return (-1);
315  }
316  /* Fetch next label in domain name. */
317  while ((n = *srcp++) != 0) {
318  /* Check for indirection. */
319  switch (n & NS_CMPRSFLGS) {
320  case 0:
321  /* Limit checks. */
322  if (dstp + n + 1 >= dstlim || srcp + n >= eom) {
323  errno = EMSGSIZE;
324  return (-1);
325  }
326  checked += n + 1;
327  *dstp++ = n;
328  memcpy(dstp, srcp, n);
329  dstp += n;
330  srcp += n;
331  break;
332 
333  case NS_CMPRSFLGS:
334  if (srcp >= eom) {
335  errno = EMSGSIZE;
336  return (-1);
337  }
338  if (len < 0)
339  len = srcp - src + 1;
340  srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
341  if (srcp < msg || srcp >= eom) { /* Out of range. */
342  errno = EMSGSIZE;
343  return (-1);
344  }
345  checked += 2;
346  /*
347  * Check for loops in the compressed name;
348  * if we've looked at the whole message,
349  * there must be a loop.
350  */
351  if (checked >= eom - msg) {
352  errno = EMSGSIZE;
353  return (-1);
354  }
355  break;
356 
357  default:
358  errno = EMSGSIZE;
359  return (-1); /* flag error */
360  }
361  }
362  *dstp = '\0';
363  if (len < 0)
364  len = srcp - src;
365  return (len);
366 }
367 
368 /*
369  * MRns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr)
370  * Pack domain name 'domain' into 'comp_dn'.
371  * return:
372  * Size of the compressed name, or -1.
373  * notes:
374  * 'dnptrs' is an array of pointers to previous compressed names.
375  * dnptrs[0] is a pointer to the beginning of the message. The array
376  * ends with NULL.
377  * 'lastdnptr' is a pointer to the end of the array pointed to
378  * by 'dnptrs'.
379  * Side effects:
380  * The list of pointers in dnptrs is updated for labels inserted into
381  * the message as we compress the name. If 'dnptr' is NULL, we don't
382  * try to compress names. If 'lastdnptr' is NULL, we don't update the
383  * list.
384  */
385 int
386 MRns_name_pack(const u_char *src, u_char *dst, unsigned dstsiz,
387  const u_char **dnptrs, const u_char **lastdnptr)
388 {
389  u_char *dstp;
390  const u_char **cpp, **lpp, *eob, *msg;
391  const u_char *srcp;
392  unsigned n;
393  int l;
394 
395  srcp = src;
396  dstp = dst;
397  eob = dstp + dstsiz;
398  lpp = cpp = NULL;
399  if (dnptrs != NULL) {
400  if ((msg = *dnptrs++) != NULL) {
401  for (cpp = dnptrs; *cpp != NULL; cpp++)
402  (void)NULL;
403  lpp = cpp; /* end of list to search */
404  }
405  } else
406  msg = NULL;
407 
408  /* make sure the domain we are about to add is legal */
409  l = 0;
410  do {
411  n = *srcp;
412  if ((n & NS_CMPRSFLGS) != 0) {
413  errno = EMSGSIZE;
414  return (-1);
415  }
416  l += n + 1;
417  if (l > MAXCDNAME) {
418  errno = EMSGSIZE;
419  return (-1);
420  }
421  srcp += n + 1;
422  } while (n != 0);
423 
424  /* from here on we need to reset compression pointer array on error */
425  srcp = src;
426  do {
427  /* Look to see if we can use pointers. */
428  n = *srcp;
429  if (n != 0 && msg != NULL) {
430  l = dn_find(srcp, msg, (const u_char * const *)dnptrs,
431  (const u_char * const *)lpp);
432  if (l >= 0) {
433  if (dstp + 1 >= eob) {
434  goto cleanup;
435  }
436  *dstp++ = (l >> 8) | NS_CMPRSFLGS;
437  *dstp++ = l % 256;
438  return (dstp - dst);
439  }
440  /* Not found, save it. */
441  if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
442  (dstp - msg) < 0x4000) {
443  *cpp++ = dstp;
444  *cpp = NULL;
445  }
446  }
447  /* copy label to buffer */
448  if (n & NS_CMPRSFLGS) { /* Should not happen. */
449  goto cleanup;
450  }
451  if (dstp + 1 + n >= eob) {
452  goto cleanup;
453  }
454  memcpy(dstp, srcp, n + 1);
455  srcp += n + 1;
456  dstp += n + 1;
457  } while (n != 0);
458 
459  if (dstp > eob) {
460 cleanup:
461  if (msg != NULL)
462  *lpp = NULL;
463  errno = EMSGSIZE;
464  return (-1);
465  }
466  return (dstp - dst);
467 }
468 
469 /*
470  * MRns_name_uncompress(msg, eom, src, dst, dstsiz)
471  * Expand compressed domain name to presentation format.
472  * return:
473  * Number of bytes read out of `src', or -1 (with errno set).
474  * note:
475  * Root domain returns as "." not "".
476  */
477 int
478 MRns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
479  char *dst, size_t dstsiz)
480 {
481  u_char tmp[NS_MAXCDNAME];
482  int n;
483 
484  if ((n = MRns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
485  return (-1);
486  if (MRns_name_ntop(tmp, dst, dstsiz) == -1)
487  return (-1);
488  return (n);
489 }
490 
491 /*
492  * MRns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr)
493  * Compress a domain name into wire format, using compression pointers.
494  * return:
495  * Number of bytes consumed in `dst' or -1 (with errno set).
496  * notes:
497  * 'dnptrs' is an array of pointers to previous compressed names.
498  * dnptrs[0] is a pointer to the beginning of the message.
499  * The list ends with NULL. 'lastdnptr' is a pointer to the end of the
500  * array pointed to by 'dnptrs'. Side effect is to update the list of
501  * pointers for labels inserted into the message as we compress the name.
502  * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
503  * is NULL, we don't update the list.
504  */
505 int
506 MRns_name_compress(const char *src, u_char *dst, size_t dstsiz,
507  const u_char **dnptrs, const u_char **lastdnptr)
508 {
509  u_char tmp[NS_MAXCDNAME];
510 
511  if (MRns_name_pton(src, tmp, sizeof tmp) == -1)
512  return (-1);
513  return (MRns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
514 }
515 
516 /*
517  * MRns_name_skip(ptrptr, eom)
518  * Advance *ptrptr to skip over the compressed name it points at.
519  * return:
520  * 0 on success, -1 (with errno set) on failure.
521  */
522 int
523 MRns_name_skip(const u_char **ptrptr, const u_char *eom) {
524  const u_char *cp;
525  u_int n;
526 
527  cp = *ptrptr;
528  while (cp < eom && (n = *cp++) != 0) {
529  /* Check for indirection. */
530  switch (n & NS_CMPRSFLGS) {
531  case 0: /* normal case, n == len */
532  cp += n;
533  continue;
534  case NS_CMPRSFLGS: /* indirection */
535  cp++;
536  break;
537  default: /* illegal type */
538  errno = EMSGSIZE;
539  return (-1);
540  }
541  break;
542  }
543  if (cp > eom) {
544  errno = EMSGSIZE;
545  return (-1);
546  }
547  *ptrptr = cp;
548  return (0);
549 }
550 
551 /* Private. */
552 
553 /*
554  * special(ch)
555  * Thinking in noninternationalized USASCII (per the DNS spec),
556  * is this characted special ("in need of quoting") ?
557  * return:
558  * boolean.
559  */
560 static int
561 special(int ch) {
562  switch (ch) {
563  case 0x22: /* '"' */
564  case 0x2E: /* '.' */
565  case 0x3B: /* ';' */
566  case 0x5C: /* '\\' */
567  /* Special modifiers in zone files. */
568  case 0x40: /* '@' */
569  case 0x24: /* '$' */
570  return (1);
571  default:
572  return (0);
573  }
574 }
575 
576 /*
577  * printable(ch)
578  * Thinking in noninternationalized USASCII (per the DNS spec),
579  * is this character visible and not a space when printed ?
580  * return:
581  * boolean.
582  */
583 static int
584 printable(int ch) {
585  return (ch > 0x20 && ch < 0x7f);
586 }
587 
588 /*
589  * Thinking in noninternationalized USASCII (per the DNS spec),
590  * convert this character to lower case if it's upper case.
591  */
592 static int
593 mklower(int ch) {
594  if (ch >= 0x41 && ch <= 0x5A)
595  return (ch + 0x20);
596  return (ch);
597 }
598 
599 /*
600  * dn_find(domain, msg, dnptrs, lastdnptr)
601  * Search for the counted-label name in an array of compressed names.
602  * return:
603  * offset from msg if found, or -1.
604  * notes:
605  * dnptrs is the pointer to the first name on the list,
606  * not the pointer to the start of the message.
607  */
608 static int
609 dn_find(const u_char *domain, const u_char *msg,
610  const u_char * const *dnptrs,
611  const u_char * const *lastdnptr)
612 {
613  const u_char *dn, *cp, *sp;
614  const u_char * const *cpp;
615  u_int n;
616 
617  for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
618  dn = domain;
619  sp = cp = *cpp;
620  while ((n = *cp++) != 0) {
621  /*
622  * check for indirection
623  */
624  switch (n & NS_CMPRSFLGS) {
625  case 0: /* normal case, n == len */
626  if (n != *dn++)
627  goto next;
628  for ((void)NULL; n > 0; n--)
629  if (mklower(*dn++) != mklower(*cp++))
630  goto next;
631  /* Is next root for both ? */
632  if (*dn == '\0' && *cp == '\0')
633  return (sp - msg);
634  if (*dn)
635  continue;
636  goto next;
637 
638  case NS_CMPRSFLGS: /* indirection */
639  cp = msg + (((n & 0x3f) << 8) | *cp);
640  break;
641 
642  default: /* illegal type */
643  errno = EMSGSIZE;
644  return (-1);
645  }
646  }
647  next: ;
648  }
649  errno = ENOENT;
650  return (-1);
651 }
#define NS_MAXCDNAME
Definition: nameser.h:75
int MRns_name_ntop(const u_char *src, char *dst, size_t dstsiz)
Definition: ns_name.c:64
#define MAXCDNAME
int MRns_name_skip(const u_char **ptrptr, const u_char *eom)
Definition: ns_name.c:523
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:506
#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:254
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:386
void cleanup(void)
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:478
struct interface_info * next
Definition: dhcpd.h:1242
int MRns_name_pton(const char *src, u_char *dst, size_t dstsiz)
Definition: ns_name.c:145
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:298