1 /* $OpenBSD: res_comp.c,v 1.9 2002/02/17 19:42:23 millert Exp $ */ 2 3 /* 4 * ++Copyright++ 1985, 1993 5 * - 6 * Copyright (c) 1985, 1993 7 * The Regents of the University of California. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the University of 20 * California, Berkeley and its contributors. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * - 37 * Portions Copyright (c) 1993 by Digital Equipment Corporation. 38 * 39 * Permission to use, copy, modify, and distribute this software for any 40 * purpose with or without fee is hereby granted, provided that the above 41 * copyright notice and this permission notice appear in all copies, and that 42 * the name of Digital Equipment Corporation not be used in advertising or 43 * publicity pertaining to distribution of the document or software without 44 * specific, written prior permission. 45 * 46 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL 47 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES 48 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT 49 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 50 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 51 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 52 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 53 * SOFTWARE. 54 * - 55 * --Copyright-- 56 */ 57 58 #if defined(LIBC_SCCS) && !defined(lint) 59 #if 0 60 static char sccsid[] = "@(#)res_comp.c 8.1 (Berkeley) 6/4/93"; 61 static char rcsid[] = "$From: res_comp.c,v 8.11 1996/12/02 09:17:22 vixie Exp $"; 62 #else 63 static char rcsid[] = "$OpenBSD: res_comp.c,v 1.9 2002/02/17 19:42:23 millert Exp $"; 64 #endif 65 #endif /* LIBC_SCCS and not lint */ 66 67 #include <sys/types.h> 68 #include <sys/param.h> 69 #include <netinet/in.h> 70 #include <arpa/nameser.h> 71 72 #include <stdio.h> 73 #include <resolv.h> 74 #include <ctype.h> 75 76 #include <unistd.h> 77 #include <string.h> 78 79 static int dn_find(u_char *, u_char *, u_char **, u_char **); 80 81 /* 82 * Expand compressed domain name 'comp_dn' to full domain name. 83 * 'msg' is a pointer to the begining of the message, 84 * 'eomorig' points to the first location after the message, 85 * 'exp_dn' is a pointer to a buffer of size 'length' for the result. 86 * Return size of compressed name or -1 if there was an error. 87 */ 88 int 89 dn_expand(msg, eomorig, comp_dn, exp_dn, length) 90 const u_char *msg, *eomorig, *comp_dn; 91 char *exp_dn; 92 int length; 93 { 94 register const u_char *cp; 95 register char *dn; 96 register int n, c; 97 char *eom; 98 int len = -1, checked = 0; 99 100 dn = exp_dn; 101 cp = comp_dn; 102 if (length > MAXHOSTNAMELEN-1) 103 length = MAXHOSTNAMELEN-1; 104 eom = exp_dn + length; 105 /* 106 * fetch next label in domain name 107 */ 108 while ((n = *cp++)) { 109 /* 110 * Check for indirection 111 */ 112 switch (n & INDIR_MASK) { 113 case 0: 114 if (dn != exp_dn) { 115 if (dn >= eom) 116 return (-1); 117 *dn++ = '.'; 118 } 119 if (dn+n >= eom) 120 return (-1); 121 checked += n + 1; 122 while (--n >= 0) { 123 if (((c = *cp++) == '.') || (c == '\\')) { 124 if (dn + n + 2 >= eom) 125 return (-1); 126 *dn++ = '\\'; 127 } 128 *dn++ = c; 129 if (cp >= eomorig) /* out of range */ 130 return (-1); 131 } 132 break; 133 134 case INDIR_MASK: 135 if (len < 0) 136 len = cp - comp_dn + 1; 137 cp = msg + (((n & 0x3f) << 8) | (*cp & 0xff)); 138 if (cp < msg || cp >= eomorig) /* out of range */ 139 return (-1); 140 checked += 2; 141 /* 142 * Check for loops in the compressed name; 143 * if we've looked at the whole message, 144 * there must be a loop. 145 */ 146 if (checked >= eomorig - msg) 147 return (-1); 148 break; 149 150 default: 151 return (-1); /* flag error */ 152 } 153 } 154 *dn = '\0'; 155 if (len < 0) 156 len = cp - comp_dn; 157 return (len); 158 } 159 160 /* 161 * Compress domain name 'exp_dn' into 'comp_dn'. 162 * Return the size of the compressed name or -1. 163 * 'length' is the size of the array pointed to by 'comp_dn'. 164 * 'dnptrs' is a list of pointers to previous compressed names. dnptrs[0] 165 * is a pointer to the beginning of the message. The list ends with NULL. 166 * 'lastdnptr' is a pointer to the end of the arrary pointed to 167 * by 'dnptrs'. Side effect is to update the list of pointers for 168 * labels inserted into the message as we compress the name. 169 * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr' 170 * is NULL, we don't update the list. 171 */ 172 int 173 dn_comp(exp_dn, comp_dn, length, dnptrs, lastdnptr) 174 const char *exp_dn; 175 u_char *comp_dn, **dnptrs, **lastdnptr; 176 int length; 177 { 178 register u_char *cp, *dn; 179 register int c, l; 180 u_char **cpp, **lpp, *sp, *eob; 181 u_char *msg; 182 183 dn = (u_char *)exp_dn; 184 cp = comp_dn; 185 eob = cp + length; 186 lpp = cpp = NULL; 187 if (dnptrs != NULL) { 188 if ((msg = *dnptrs++) != NULL) { 189 for (cpp = dnptrs; *cpp != NULL; cpp++) 190 ; 191 lpp = cpp; /* end of list to search */ 192 } 193 } else 194 msg = NULL; 195 for (c = *dn++; c != '\0'; ) { 196 /* look to see if we can use pointers */ 197 if (msg != NULL) { 198 if ((l = dn_find(dn-1, msg, dnptrs, lpp)) >= 0) { 199 if (cp+1 >= eob) 200 return (-1); 201 *cp++ = (l >> 8) | INDIR_MASK; 202 *cp++ = l % 256; 203 return (cp - comp_dn); 204 } 205 /* not found, save it */ 206 if (lastdnptr != NULL && cpp < lastdnptr-1) { 207 *cpp++ = cp; 208 *cpp = NULL; 209 } 210 } 211 sp = cp++; /* save ptr to length byte */ 212 do { 213 if (c == '.') { 214 c = *dn++; 215 break; 216 } 217 if (c == '\\') { 218 if ((c = *dn++) == '\0') 219 break; 220 } 221 if (cp >= eob) { 222 if (msg != NULL) 223 *lpp = NULL; 224 return (-1); 225 } 226 *cp++ = c; 227 } while ((c = *dn++) != '\0'); 228 /* catch trailing '.'s but not '..' */ 229 if ((l = cp - sp - 1) == 0 && c == '\0') { 230 cp--; 231 break; 232 } 233 if (l <= 0 || l > MAXLABEL) { 234 if (msg != NULL) 235 *lpp = NULL; 236 return (-1); 237 } 238 *sp = l; 239 } 240 if (cp >= eob) { 241 if (msg != NULL) 242 *lpp = NULL; 243 return (-1); 244 } 245 *cp++ = '\0'; 246 return (cp - comp_dn); 247 } 248 249 /* 250 * Skip over a compressed domain name. Return the size or -1. 251 */ 252 int 253 __dn_skipname(comp_dn, eom) 254 const u_char *comp_dn, *eom; 255 { 256 register const u_char *cp; 257 register int n; 258 259 cp = comp_dn; 260 while (cp < eom && (n = *cp++)) { 261 /* 262 * check for indirection 263 */ 264 switch (n & INDIR_MASK) { 265 case 0: /* normal case, n == len */ 266 cp += n; 267 continue; 268 case INDIR_MASK: /* indirection */ 269 cp++; 270 break; 271 default: /* illegal type */ 272 return (-1); 273 } 274 break; 275 } 276 if (cp > eom) 277 return (-1); 278 return (cp - comp_dn); 279 } 280 281 static int 282 mklower(ch) 283 register int ch; 284 { 285 if (isascii(ch) && isupper(ch)) 286 return (tolower(ch)); 287 return (ch); 288 } 289 290 /* 291 * Search for expanded name from a list of previously compressed names. 292 * Return the offset from msg if found or -1. 293 * dnptrs is the pointer to the first name on the list, 294 * not the pointer to the start of the message. 295 */ 296 static int 297 dn_find(exp_dn, msg, dnptrs, lastdnptr) 298 u_char *exp_dn, *msg; 299 u_char **dnptrs, **lastdnptr; 300 { 301 register u_char *dn, *cp, **cpp; 302 register int n; 303 u_char *sp; 304 305 for (cpp = dnptrs; cpp < lastdnptr; cpp++) { 306 dn = exp_dn; 307 sp = cp = *cpp; 308 while ((n = *cp++)) { 309 /* 310 * check for indirection 311 */ 312 switch (n & INDIR_MASK) { 313 case 0: /* normal case, n == len */ 314 while (--n >= 0) { 315 if (*dn == '.') 316 goto next; 317 if (*dn == '\\') 318 dn++; 319 if (mklower(*dn++) != mklower(*cp++)) 320 goto next; 321 } 322 if ((n = *dn++) == '\0' && *cp == '\0') 323 return (sp - msg); 324 if (n == '.') 325 continue; 326 goto next; 327 328 case INDIR_MASK: /* indirection */ 329 cp = msg + (((n & 0x3f) << 8) | *cp); 330 break; 331 332 default: /* illegal type */ 333 return (-1); 334 } 335 } 336 if (*dn == '\0') 337 return (sp - msg); 338 next: ; 339 } 340 return (-1); 341 } 342 343 /* 344 * Verify that a domain name uses an acceptable character set. 345 */ 346 347 /* 348 * Note the conspicuous absence of ctype macros in these definitions. On 349 * non-ASCII hosts, we can't depend on string literals or ctype macros to 350 * tell us anything about network-format data. The rest of the BIND system 351 * is not careful about this, but for some reason, we're doing it right here. 352 */ 353 #define PERIOD 0x2e 354 #define hyphenchar(c) ((c) == 0x2d) 355 #define bslashchar(c) ((c) == 0x5c) 356 #define periodchar(c) ((c) == PERIOD) 357 #define asterchar(c) ((c) == 0x2a) 358 #define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \ 359 || ((c) >= 0x61 && (c) <= 0x7a)) 360 #define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) 361 362 #define borderchar(c) (alphachar(c) || digitchar(c)) 363 #define middlechar(c) (borderchar(c) || hyphenchar(c)) 364 #define domainchar(c) ((c) > 0x20 && (c) < 0x7f) 365 366 int 367 res_hnok(dn) 368 const char *dn; 369 { 370 int pch = PERIOD, ch = *dn++; 371 372 while (ch != '\0') { 373 int nch = *dn++; 374 375 if (periodchar(ch)) { 376 ; 377 } else if (periodchar(pch)) { 378 if (!borderchar(ch)) 379 return (0); 380 } else if (periodchar(nch) || nch == '\0') { 381 if (!borderchar(ch)) 382 return (0); 383 } else { 384 if (!middlechar(ch)) 385 return (0); 386 } 387 pch = ch, ch = nch; 388 } 389 return (1); 390 } 391 392 /* 393 * hostname-like (A, MX, WKS) owners can have "*" as their first label 394 * but must otherwise be as a host name. 395 */ 396 int 397 res_ownok(dn) 398 const char *dn; 399 { 400 if (asterchar(dn[0])) { 401 if (periodchar(dn[1])) 402 return (res_hnok(dn+2)); 403 if (dn[1] == '\0') 404 return (1); 405 } 406 return (res_hnok(dn)); 407 } 408 409 /* 410 * SOA RNAMEs and RP RNAMEs can have any printable character in their first 411 * label, but the rest of the name has to look like a host name. 412 */ 413 int 414 res_mailok(dn) 415 const char *dn; 416 { 417 int ch, escaped = 0; 418 419 /* "." is a valid missing representation */ 420 if (*dn == '\0') 421 return(1); 422 423 /* otherwise <label>.<hostname> */ 424 while ((ch = *dn++) != '\0') { 425 if (!domainchar(ch)) 426 return (0); 427 if (!escaped && periodchar(ch)) 428 break; 429 if (escaped) 430 escaped = 0; 431 else if (bslashchar(ch)) 432 escaped = 1; 433 } 434 if (periodchar(ch)) 435 return (res_hnok(dn)); 436 return(0); 437 } 438 439 /* 440 * This function is quite liberal, since RFC 1034's character sets are only 441 * recommendations. 442 */ 443 int 444 res_dnok(dn) 445 const char *dn; 446 { 447 int ch; 448 449 while ((ch = *dn++) != '\0') 450 if (!domainchar(ch)) 451 return (0); 452 return (1); 453 } 454 455 /* 456 * Routines to insert/extract short/long's. 457 */ 458 459 u_int16_t 460 _getshort(msgp) 461 register const u_char *msgp; 462 { 463 register u_int16_t u; 464 465 GETSHORT(u, msgp); 466 return (u); 467 } 468 469 #ifdef NeXT 470 /* 471 * nExt machines have some funky library conventions, which we must maintain. 472 */ 473 u_int16_t 474 res_getshort(msgp) 475 register const u_char *msgp; 476 { 477 return (_getshort(msgp)); 478 } 479 #endif 480 481 u_int32_t 482 _getlong(msgp) 483 register const u_char *msgp; 484 { 485 register u_int32_t u; 486 487 GETLONG(u, msgp); 488 return (u); 489 } 490 491 void 492 #if defined(__STDC__) || defined(__cplusplus) 493 __putshort(register u_int16_t s, register u_char *msgp) /* must match proto */ 494 #else 495 __putshort(s, msgp) 496 register u_int16_t s; 497 register u_char *msgp; 498 #endif 499 { 500 PUTSHORT(s, msgp); 501 } 502 503 void 504 __putlong(l, msgp) 505 register u_int32_t l; 506 register u_char *msgp; 507 { 508 PUTLONG(l, msgp); 509 } 510