ns_name.c revision 1.1 1 1.1 christos /* $NetBSD: ns_name.c,v 1.1 2018/04/07 22:34:26 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.1 christos __RCSID("$NetBSD: ns_name.c,v 1.1 2018/04/07 22:34:26 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.1 christos /*
246 1.1 christos * MRns_name_ntol(src, dst, dstsiz)
247 1.1 christos * Convert a network strings labels into all lowercase.
248 1.1 christos * return:
249 1.1 christos * Number of bytes written to buffer, or -1 (with errno set)
250 1.1 christos * notes:
251 1.1 christos * Enforces label and domain length limits.
252 1.1 christos */
253 1.1 christos
254 1.1 christos int
255 1.1 christos MRns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) {
256 1.1 christos const u_char *cp;
257 1.1 christos u_char *dn, *eom;
258 1.1 christos u_char c;
259 1.1 christos u_int n;
260 1.1 christos
261 1.1 christos cp = src;
262 1.1 christos dn = dst;
263 1.1 christos eom = dst + dstsiz;
264 1.1 christos
265 1.1 christos if (dn >= eom) {
266 1.1 christos errno = EMSGSIZE;
267 1.1 christos return (-1);
268 1.1 christos }
269 1.1 christos while ((n = *cp++) != 0) {
270 1.1 christos if ((n & NS_CMPRSFLGS) != 0) {
271 1.1 christos /* Some kind of compression pointer. */
272 1.1 christos errno = EMSGSIZE;
273 1.1 christos return (-1);
274 1.1 christos }
275 1.1 christos *dn++ = n;
276 1.1 christos if (dn + n >= eom) {
277 1.1 christos errno = EMSGSIZE;
278 1.1 christos return (-1);
279 1.1 christos }
280 1.1 christos for ((void)NULL; n > 0; n--) {
281 1.1 christos c = *cp++;
282 1.1 christos if (isupper(c))
283 1.1 christos *dn++ = tolower(c);
284 1.1 christos else
285 1.1 christos *dn++ = c;
286 1.1 christos }
287 1.1 christos }
288 1.1 christos *dn++ = '\0';
289 1.1 christos return (dn - dst);
290 1.1 christos }
291 1.1 christos
292 1.1 christos /*
293 1.1 christos * MRns_name_unpack(msg, eom, src, dst, dstsiz)
294 1.1 christos * Unpack a domain name from a message, source may be compressed.
295 1.1 christos * return:
296 1.1 christos * -1 if it fails, or consumed octets if it succeeds.
297 1.1 christos */
298 1.1 christos int
299 1.1 christos MRns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
300 1.1 christos u_char *dst, size_t dstsiz)
301 1.1 christos {
302 1.1 christos const u_char *srcp, *dstlim;
303 1.1 christos u_char *dstp;
304 1.1 christos unsigned n;
305 1.1 christos int len;
306 1.1 christos int checked;
307 1.1 christos
308 1.1 christos len = -1;
309 1.1 christos checked = 0;
310 1.1 christos dstp = dst;
311 1.1 christos srcp = src;
312 1.1 christos dstlim = dst + dstsiz;
313 1.1 christos if (srcp < msg || srcp >= eom) {
314 1.1 christos errno = EMSGSIZE;
315 1.1 christos return (-1);
316 1.1 christos }
317 1.1 christos /* Fetch next label in domain name. */
318 1.1 christos while ((n = *srcp++) != 0) {
319 1.1 christos /* Check for indirection. */
320 1.1 christos switch (n & NS_CMPRSFLGS) {
321 1.1 christos case 0:
322 1.1 christos /* Limit checks. */
323 1.1 christos if (dstp + n + 1 >= dstlim || srcp + n >= eom) {
324 1.1 christos errno = EMSGSIZE;
325 1.1 christos return (-1);
326 1.1 christos }
327 1.1 christos checked += n + 1;
328 1.1 christos *dstp++ = n;
329 1.1 christos memcpy(dstp, srcp, n);
330 1.1 christos dstp += n;
331 1.1 christos srcp += n;
332 1.1 christos break;
333 1.1 christos
334 1.1 christos case NS_CMPRSFLGS:
335 1.1 christos if (srcp >= eom) {
336 1.1 christos errno = EMSGSIZE;
337 1.1 christos return (-1);
338 1.1 christos }
339 1.1 christos if (len < 0)
340 1.1 christos len = srcp - src + 1;
341 1.1 christos srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
342 1.1 christos if (srcp < msg || srcp >= eom) { /* Out of range. */
343 1.1 christos errno = EMSGSIZE;
344 1.1 christos return (-1);
345 1.1 christos }
346 1.1 christos checked += 2;
347 1.1 christos /*
348 1.1 christos * Check for loops in the compressed name;
349 1.1 christos * if we've looked at the whole message,
350 1.1 christos * there must be a loop.
351 1.1 christos */
352 1.1 christos if (checked >= eom - msg) {
353 1.1 christos errno = EMSGSIZE;
354 1.1 christos return (-1);
355 1.1 christos }
356 1.1 christos break;
357 1.1 christos
358 1.1 christos default:
359 1.1 christos errno = EMSGSIZE;
360 1.1 christos return (-1); /* flag error */
361 1.1 christos }
362 1.1 christos }
363 1.1 christos *dstp = '\0';
364 1.1 christos if (len < 0)
365 1.1 christos len = srcp - src;
366 1.1 christos return (len);
367 1.1 christos }
368 1.1 christos
369 1.1 christos /*
370 1.1 christos * MRns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr)
371 1.1 christos * Pack domain name 'domain' into 'comp_dn'.
372 1.1 christos * return:
373 1.1 christos * Size of the compressed name, or -1.
374 1.1 christos * notes:
375 1.1 christos * 'dnptrs' is an array of pointers to previous compressed names.
376 1.1 christos * dnptrs[0] is a pointer to the beginning of the message. The array
377 1.1 christos * ends with NULL.
378 1.1 christos * 'lastdnptr' is a pointer to the end of the array pointed to
379 1.1 christos * by 'dnptrs'.
380 1.1 christos * Side effects:
381 1.1 christos * The list of pointers in dnptrs is updated for labels inserted into
382 1.1 christos * the message as we compress the name. If 'dnptr' is NULL, we don't
383 1.1 christos * try to compress names. If 'lastdnptr' is NULL, we don't update the
384 1.1 christos * list.
385 1.1 christos */
386 1.1 christos int
387 1.1 christos MRns_name_pack(const u_char *src, u_char *dst, unsigned dstsiz,
388 1.1 christos const u_char **dnptrs, const u_char **lastdnptr)
389 1.1 christos {
390 1.1 christos u_char *dstp;
391 1.1 christos const u_char **cpp, **lpp, *eob, *msg;
392 1.1 christos const u_char *srcp;
393 1.1 christos unsigned n;
394 1.1 christos int l;
395 1.1 christos
396 1.1 christos srcp = src;
397 1.1 christos dstp = dst;
398 1.1 christos eob = dstp + dstsiz;
399 1.1 christos lpp = cpp = NULL;
400 1.1 christos if (dnptrs != NULL) {
401 1.1 christos if ((msg = *dnptrs++) != NULL) {
402 1.1 christos for (cpp = dnptrs; *cpp != NULL; cpp++)
403 1.1 christos (void)NULL;
404 1.1 christos lpp = cpp; /* end of list to search */
405 1.1 christos }
406 1.1 christos } else
407 1.1 christos msg = NULL;
408 1.1 christos
409 1.1 christos /* make sure the domain we are about to add is legal */
410 1.1 christos l = 0;
411 1.1 christos do {
412 1.1 christos n = *srcp;
413 1.1 christos if ((n & NS_CMPRSFLGS) != 0) {
414 1.1 christos errno = EMSGSIZE;
415 1.1 christos return (-1);
416 1.1 christos }
417 1.1 christos l += n + 1;
418 1.1 christos if (l > MAXCDNAME) {
419 1.1 christos errno = EMSGSIZE;
420 1.1 christos return (-1);
421 1.1 christos }
422 1.1 christos srcp += n + 1;
423 1.1 christos } while (n != 0);
424 1.1 christos
425 1.1 christos /* from here on we need to reset compression pointer array on error */
426 1.1 christos srcp = src;
427 1.1 christos do {
428 1.1 christos /* Look to see if we can use pointers. */
429 1.1 christos n = *srcp;
430 1.1 christos if (n != 0 && msg != NULL) {
431 1.1 christos l = dn_find(srcp, msg, (const u_char * const *)dnptrs,
432 1.1 christos (const u_char * const *)lpp);
433 1.1 christos if (l >= 0) {
434 1.1 christos if (dstp + 1 >= eob) {
435 1.1 christos goto cleanup;
436 1.1 christos }
437 1.1 christos *dstp++ = (l >> 8) | NS_CMPRSFLGS;
438 1.1 christos *dstp++ = l % 256;
439 1.1 christos return (dstp - dst);
440 1.1 christos }
441 1.1 christos /* Not found, save it. */
442 1.1 christos if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
443 1.1 christos (dstp - msg) < 0x4000) {
444 1.1 christos *cpp++ = dstp;
445 1.1 christos *cpp = NULL;
446 1.1 christos }
447 1.1 christos }
448 1.1 christos /* copy label to buffer */
449 1.1 christos if (n & NS_CMPRSFLGS) { /* Should not happen. */
450 1.1 christos goto cleanup;
451 1.1 christos }
452 1.1 christos if (dstp + 1 + n >= eob) {
453 1.1 christos goto cleanup;
454 1.1 christos }
455 1.1 christos memcpy(dstp, srcp, n + 1);
456 1.1 christos srcp += n + 1;
457 1.1 christos dstp += n + 1;
458 1.1 christos } while (n != 0);
459 1.1 christos
460 1.1 christos if (dstp > eob) {
461 1.1 christos cleanup:
462 1.1 christos if (msg != NULL)
463 1.1 christos *lpp = NULL;
464 1.1 christos errno = EMSGSIZE;
465 1.1 christos return (-1);
466 1.1 christos }
467 1.1 christos return (dstp - dst);
468 1.1 christos }
469 1.1 christos
470 1.1 christos /*
471 1.1 christos * MRns_name_uncompress(msg, eom, src, dst, dstsiz)
472 1.1 christos * Expand compressed domain name to presentation format.
473 1.1 christos * return:
474 1.1 christos * Number of bytes read out of `src', or -1 (with errno set).
475 1.1 christos * note:
476 1.1 christos * Root domain returns as "." not "".
477 1.1 christos */
478 1.1 christos int
479 1.1 christos MRns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
480 1.1 christos char *dst, size_t dstsiz)
481 1.1 christos {
482 1.1 christos u_char tmp[NS_MAXCDNAME];
483 1.1 christos int n;
484 1.1 christos
485 1.1 christos if ((n = MRns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
486 1.1 christos return (-1);
487 1.1 christos if (MRns_name_ntop(tmp, dst, dstsiz) == -1)
488 1.1 christos return (-1);
489 1.1 christos return (n);
490 1.1 christos }
491 1.1 christos
492 1.1 christos /*
493 1.1 christos * MRns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr)
494 1.1 christos * Compress a domain name into wire format, using compression pointers.
495 1.1 christos * return:
496 1.1 christos * Number of bytes consumed in `dst' or -1 (with errno set).
497 1.1 christos * notes:
498 1.1 christos * 'dnptrs' is an array of pointers to previous compressed names.
499 1.1 christos * dnptrs[0] is a pointer to the beginning of the message.
500 1.1 christos * The list ends with NULL. 'lastdnptr' is a pointer to the end of the
501 1.1 christos * array pointed to by 'dnptrs'. Side effect is to update the list of
502 1.1 christos * pointers for labels inserted into the message as we compress the name.
503 1.1 christos * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
504 1.1 christos * is NULL, we don't update the list.
505 1.1 christos */
506 1.1 christos int
507 1.1 christos MRns_name_compress(const char *src, u_char *dst, size_t dstsiz,
508 1.1 christos const u_char **dnptrs, const u_char **lastdnptr)
509 1.1 christos {
510 1.1 christos u_char tmp[NS_MAXCDNAME];
511 1.1 christos
512 1.1 christos if (MRns_name_pton(src, tmp, sizeof tmp) == -1)
513 1.1 christos return (-1);
514 1.1 christos return (MRns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
515 1.1 christos }
516 1.1 christos
517 1.1 christos /*
518 1.1 christos * MRns_name_skip(ptrptr, eom)
519 1.1 christos * Advance *ptrptr to skip over the compressed name it points at.
520 1.1 christos * return:
521 1.1 christos * 0 on success, -1 (with errno set) on failure.
522 1.1 christos */
523 1.1 christos int
524 1.1 christos MRns_name_skip(const u_char **ptrptr, const u_char *eom) {
525 1.1 christos const u_char *cp;
526 1.1 christos u_int n;
527 1.1 christos
528 1.1 christos cp = *ptrptr;
529 1.1 christos while (cp < eom && (n = *cp++) != 0) {
530 1.1 christos /* Check for indirection. */
531 1.1 christos switch (n & NS_CMPRSFLGS) {
532 1.1 christos case 0: /* normal case, n == len */
533 1.1 christos cp += n;
534 1.1 christos continue;
535 1.1 christos case NS_CMPRSFLGS: /* indirection */
536 1.1 christos cp++;
537 1.1 christos break;
538 1.1 christos default: /* illegal type */
539 1.1 christos errno = EMSGSIZE;
540 1.1 christos return (-1);
541 1.1 christos }
542 1.1 christos break;
543 1.1 christos }
544 1.1 christos if (cp > eom) {
545 1.1 christos errno = EMSGSIZE;
546 1.1 christos return (-1);
547 1.1 christos }
548 1.1 christos *ptrptr = cp;
549 1.1 christos return (0);
550 1.1 christos }
551 1.1 christos
552 1.1 christos /* Private. */
553 1.1 christos
554 1.1 christos /*
555 1.1 christos * special(ch)
556 1.1 christos * Thinking in noninternationalized USASCII (per the DNS spec),
557 1.1 christos * is this characted special ("in need of quoting") ?
558 1.1 christos * return:
559 1.1 christos * boolean.
560 1.1 christos */
561 1.1 christos static int
562 1.1 christos special(int ch) {
563 1.1 christos switch (ch) {
564 1.1 christos case 0x22: /* '"' */
565 1.1 christos case 0x2E: /* '.' */
566 1.1 christos case 0x3B: /* ';' */
567 1.1 christos case 0x5C: /* '\\' */
568 1.1 christos /* Special modifiers in zone files. */
569 1.1 christos case 0x40: /* '@' */
570 1.1 christos case 0x24: /* '$' */
571 1.1 christos return (1);
572 1.1 christos default:
573 1.1 christos return (0);
574 1.1 christos }
575 1.1 christos }
576 1.1 christos
577 1.1 christos /*
578 1.1 christos * printable(ch)
579 1.1 christos * Thinking in noninternationalized USASCII (per the DNS spec),
580 1.1 christos * is this character visible and not a space when printed ?
581 1.1 christos * return:
582 1.1 christos * boolean.
583 1.1 christos */
584 1.1 christos static int
585 1.1 christos printable(int ch) {
586 1.1 christos return (ch > 0x20 && ch < 0x7f);
587 1.1 christos }
588 1.1 christos
589 1.1 christos /*
590 1.1 christos * Thinking in noninternationalized USASCII (per the DNS spec),
591 1.1 christos * convert this character to lower case if it's upper case.
592 1.1 christos */
593 1.1 christos static int
594 1.1 christos mklower(int ch) {
595 1.1 christos if (ch >= 0x41 && ch <= 0x5A)
596 1.1 christos return (ch + 0x20);
597 1.1 christos return (ch);
598 1.1 christos }
599 1.1 christos
600 1.1 christos /*
601 1.1 christos * dn_find(domain, msg, dnptrs, lastdnptr)
602 1.1 christos * Search for the counted-label name in an array of compressed names.
603 1.1 christos * return:
604 1.1 christos * offset from msg if found, or -1.
605 1.1 christos * notes:
606 1.1 christos * dnptrs is the pointer to the first name on the list,
607 1.1 christos * not the pointer to the start of the message.
608 1.1 christos */
609 1.1 christos static int
610 1.1 christos dn_find(const u_char *domain, const u_char *msg,
611 1.1 christos const u_char * const *dnptrs,
612 1.1 christos const u_char * const *lastdnptr)
613 1.1 christos {
614 1.1 christos const u_char *dn, *cp, *sp;
615 1.1 christos const u_char * const *cpp;
616 1.1 christos u_int n;
617 1.1 christos
618 1.1 christos for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
619 1.1 christos dn = domain;
620 1.1 christos sp = cp = *cpp;
621 1.1 christos while ((n = *cp++) != 0) {
622 1.1 christos /*
623 1.1 christos * check for indirection
624 1.1 christos */
625 1.1 christos switch (n & NS_CMPRSFLGS) {
626 1.1 christos case 0: /* normal case, n == len */
627 1.1 christos if (n != *dn++)
628 1.1 christos goto next;
629 1.1 christos for ((void)NULL; n > 0; n--)
630 1.1 christos if (mklower(*dn++) != mklower(*cp++))
631 1.1 christos goto next;
632 1.1 christos /* Is next root for both ? */
633 1.1 christos if (*dn == '\0' && *cp == '\0')
634 1.1 christos return (sp - msg);
635 1.1 christos if (*dn)
636 1.1 christos continue;
637 1.1 christos goto next;
638 1.1 christos
639 1.1 christos case NS_CMPRSFLGS: /* indirection */
640 1.1 christos cp = msg + (((n & 0x3f) << 8) | *cp);
641 1.1 christos break;
642 1.1 christos
643 1.1 christos default: /* illegal type */
644 1.1 christos errno = EMSGSIZE;
645 1.1 christos return (-1);
646 1.1 christos }
647 1.1 christos }
648 1.1 christos next: ;
649 1.1 christos }
650 1.1 christos errno = ENOENT;
651 1.1 christos return (-1);
652 1.1 christos }
653 1.1 christos
654 1.1 christos /*!
655 1.1 christos * \brief Creates a string of comma-separated domain-names from a
656 1.1 christos * compressed list
657 1.1 christos *
658 1.1 christos * Produces a null-terminated string of comma-separated domain-names from
659 1.1 christos * a buffer containing a compressed list of domain-names. The names will
660 1.1 christos * be dotted and without enclosing quotes. For example:
661 1.1 christos * If a compressed list contains the follwoing two domain names:
662 1.1 christos *
663 1.1 christos * a. one.two.com
664 1.1 christos * b. three.four.com
665 1.1 christos *
666 1.1 christos * The compressed data will look like this:
667 1.1 christos *
668 1.1 christos * 03 6f 6e 65 03 74 77 6f 03 63 6f 6d 00 05 74 68
669 1.1 christos * 72 65 65 04 66 6f 75 72 c0 08
670 1.1 christos *
671 1.1 christos * and will decompress into:
672 1.1 christos *
673 1.1 christos * one.two.com,three.four.com
674 1.1 christos *
675 1.1 christos * \param buf - buffer containing the compressed list of domain-names
676 1.1 christos * \param buflen - length of compressed list of domain-names
677 1.1 christos * \param dst_buf - buffer to receive the decompressed list
678 1.1 christos * \param dst_size - size of the destination buffer
679 1.1 christos *
680 1.1 christos * \return the length of the decompressed string when successful, -1 on
681 1.1 christos * error.
682 1.1 christos */
683 1.1 christos int MRns_name_uncompress_list(const unsigned char* buf, int buflen,
684 1.1 christos char* dst_buf, size_t dst_size)
685 1.1 christos {
686 1.1 christos const unsigned char* src = buf;
687 1.1 christos char* dst = dst_buf;
688 1.1 christos int consumed = 1;
689 1.1 christos int dst_remaining = dst_size;
690 1.1 christos int added_len = 0;
691 1.1 christos int first_pass = 1;
692 1.1 christos
693 1.1 christos if (!buf || buflen == 0 || *buf == 0x00) {
694 1.1 christos /* nothing to do */
695 1.1 christos *dst = 0;
696 1.1 christos return (0);
697 1.1 christos }
698 1.1 christos
699 1.1 christos while ((consumed > 0) && (src < (buf + buflen)))
700 1.1 christos {
701 1.1 christos if (dst_remaining <= 0) {
702 1.1 christos errno = EMSGSIZE;
703 1.1 christos return (-1);
704 1.1 christos }
705 1.1 christos
706 1.1 christos if (!first_pass) {
707 1.1 christos *dst++ = ',';
708 1.1 christos *dst = '\0';
709 1.1 christos dst_remaining--;
710 1.1 christos }
711 1.1 christos
712 1.1 christos consumed = MRns_name_uncompress(buf, buf + buflen, src,
713 1.1 christos dst, dst_remaining);
714 1.1 christos if (consumed < 0) {
715 1.1 christos return (-1);
716 1.1 christos }
717 1.1 christos
718 1.1 christos src += consumed;
719 1.1 christos added_len = strlen(dst);
720 1.1 christos dst_remaining -= added_len;
721 1.1 christos dst += added_len;
722 1.1 christos first_pass = 0;
723 1.1 christos }
724 1.1 christos *dst='\0';
725 1.1 christos
726 1.1 christos /* return the length of the uncompressed list string */
727 1.1 christos return (strlen(dst_buf));
728 1.1 christos }
729 1.1 christos
730 1.1 christos /*!
731 1.1 christos * \brief Creates a compressed list from a string of comma-separated
732 1.1 christos * domain-names
733 1.1 christos *
734 1.1 christos * Produces a buffer containing a compressed data version of a list of
735 1.1 christos * domain-names extracted from a comma-separated string. Given a string
736 1.1 christos * containing:
737 1.1 christos *
738 1.1 christos * one.two.com,three.four.com
739 1.1 christos *
740 1.1 christos * It will compress this into:
741 1.1 christos *
742 1.1 christos * 03 6f 6e 65 03 74 77 6f 03 63 6f 6d 00 05 74 68
743 1.1 christos * 72 65 65 04 66 6f 75 72 c0 08
744 1.1 christos *
745 1.1 christos * \param buf - buffer containing the uncompressed string of domain-names
746 1.1 christos * \param buflen - length of uncompressed string of domain-names
747 1.1 christos * \param compbuf - buffer to receive the compressed list
748 1.1 christos * \param compbuf_size - size of the compression buffer
749 1.1 christos *
750 1.1 christos * \return the length of the compressed data when successful, -1 on error.
751 1.1 christos */
752 1.1 christos int MRns_name_compress_list(const char* buf, int buflen,
753 1.1 christos unsigned char* compbuf, size_t compbuf_size)
754 1.1 christos {
755 1.1 christos char cur_name[NS_MAXCDNAME];
756 1.1 christos const unsigned char *dnptrs[256], **lastdnptr;
757 1.1 christos const char* src;
758 1.1 christos const char* src_end;
759 1.1 christos unsigned clen = 0;
760 1.1 christos int result = 0;
761 1.1 christos
762 1.1 christos memset(compbuf, 0, compbuf_size);
763 1.1 christos memset(dnptrs, 0, sizeof(dnptrs));
764 1.1 christos dnptrs[0] = compbuf;
765 1.1 christos lastdnptr = &dnptrs[255];
766 1.1 christos
767 1.1 christos src = buf;
768 1.1 christos src_end = buf + buflen;
769 1.1 christos while (src < src_end) {
770 1.1 christos char *comma = strchr(src, ',');
771 1.1 christos int copylen = ((comma != NULL) ? comma - src : strlen(src));
772 1.1 christos if (copylen > (sizeof(cur_name) - 1)) {
773 1.1 christos errno = EMSGSIZE;
774 1.1 christos return (-1);
775 1.1 christos }
776 1.1 christos
777 1.1 christos memcpy(cur_name, src, copylen);
778 1.1 christos cur_name[copylen] = '\0';
779 1.1 christos src += copylen + 1;
780 1.1 christos
781 1.1 christos result = MRns_name_compress(cur_name, compbuf + clen,
782 1.1 christos compbuf_size - clen,
783 1.1 christos dnptrs, lastdnptr);
784 1.1 christos
785 1.1 christos if (result < 0) {
786 1.1 christos return (-1);
787 1.1 christos }
788 1.1 christos
789 1.1 christos clen += result;
790 1.1 christos }
791 1.1 christos
792 1.1 christos /* return size of compressed list */
793 1.1 christos return(clen);
794 1.1 christos }
795