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