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