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