1 /* $OpenBSD: res_comp.c,v 1.8 1997/07/09 01:08:49 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.8 1997/07/09 01:08:49 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 __P((u_char *exp_dn, u_char *msg, 80 u_char **dnptrs, u_char **lastdnptr)); 81 82 /* 83 * Expand compressed domain name 'comp_dn' to full domain name. 84 * 'msg' is a pointer to the begining of the message, 85 * 'eomorig' points to the first location after the message, 86 * 'exp_dn' is a pointer to a buffer of size 'length' for the result. 87 * Return size of compressed name or -1 if there was an error. 88 */ 89 int 90 dn_expand(msg, eomorig, comp_dn, exp_dn, length) 91 const u_char *msg, *eomorig, *comp_dn; 92 char *exp_dn; 93 int length; 94 { 95 register const u_char *cp; 96 register char *dn; 97 register int n, c; 98 char *eom; 99 int len = -1, checked = 0; 100 101 dn = exp_dn; 102 cp = comp_dn; 103 if (length > MAXHOSTNAMELEN-1) 104 length = MAXHOSTNAMELEN-1; 105 eom = exp_dn + length; 106 /* 107 * fetch next label in domain name 108 */ 109 while ((n = *cp++)) { 110 /* 111 * Check for indirection 112 */ 113 switch (n & INDIR_MASK) { 114 case 0: 115 if (dn != exp_dn) { 116 if (dn >= eom) 117 return (-1); 118 *dn++ = '.'; 119 } 120 if (dn+n >= eom) 121 return (-1); 122 checked += n + 1; 123 while (--n >= 0) { 124 if (((c = *cp++) == '.') || (c == '\\')) { 125 if (dn + n + 2 >= eom) 126 return (-1); 127 *dn++ = '\\'; 128 } 129 *dn++ = c; 130 if (cp >= eomorig) /* out of range */ 131 return (-1); 132 } 133 break; 134 135 case INDIR_MASK: 136 if (len < 0) 137 len = cp - comp_dn + 1; 138 cp = msg + (((n & 0x3f) << 8) | (*cp & 0xff)); 139 if (cp < msg || cp >= eomorig) /* out of range */ 140 return (-1); 141 checked += 2; 142 /* 143 * Check for loops in the compressed name; 144 * if we've looked at the whole message, 145 * there must be a loop. 146 */ 147 if (checked >= eomorig - msg) 148 return (-1); 149 break; 150 151 default: 152 return (-1); /* flag error */ 153 } 154 } 155 *dn = '\0'; 156 if (len < 0) 157 len = cp - comp_dn; 158 return (len); 159 } 160 161 /* 162 * Compress domain name 'exp_dn' into 'comp_dn'. 163 * Return the size of the compressed name or -1. 164 * 'length' is the size of the array pointed to by 'comp_dn'. 165 * 'dnptrs' is a list of pointers to previous compressed names. dnptrs[0] 166 * is a pointer to the beginning of the message. The list ends with NULL. 167 * 'lastdnptr' is a pointer to the end of the arrary pointed to 168 * by 'dnptrs'. Side effect is to update the list of pointers for 169 * labels inserted into the message as we compress the name. 170 * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr' 171 * is NULL, we don't update the list. 172 */ 173 int 174 dn_comp(exp_dn, comp_dn, length, dnptrs, lastdnptr) 175 const char *exp_dn; 176 u_char *comp_dn, **dnptrs, **lastdnptr; 177 int length; 178 { 179 register u_char *cp, *dn; 180 register int c, l; 181 u_char **cpp, **lpp, *sp, *eob; 182 u_char *msg; 183 184 dn = (u_char *)exp_dn; 185 cp = comp_dn; 186 eob = cp + length; 187 lpp = cpp = NULL; 188 if (dnptrs != NULL) { 189 if ((msg = *dnptrs++) != NULL) { 190 for (cpp = dnptrs; *cpp != NULL; cpp++) 191 ; 192 lpp = cpp; /* end of list to search */ 193 } 194 } else 195 msg = NULL; 196 for (c = *dn++; c != '\0'; ) { 197 /* look to see if we can use pointers */ 198 if (msg != NULL) { 199 if ((l = dn_find(dn-1, msg, dnptrs, lpp)) >= 0) { 200 if (cp+1 >= eob) 201 return (-1); 202 *cp++ = (l >> 8) | INDIR_MASK; 203 *cp++ = l % 256; 204 return (cp - comp_dn); 205 } 206 /* not found, save it */ 207 if (lastdnptr != NULL && cpp < lastdnptr-1) { 208 *cpp++ = cp; 209 *cpp = NULL; 210 } 211 } 212 sp = cp++; /* save ptr to length byte */ 213 do { 214 if (c == '.') { 215 c = *dn++; 216 break; 217 } 218 if (c == '\\') { 219 if ((c = *dn++) == '\0') 220 break; 221 } 222 if (cp >= eob) { 223 if (msg != NULL) 224 *lpp = NULL; 225 return (-1); 226 } 227 *cp++ = c; 228 } while ((c = *dn++) != '\0'); 229 /* catch trailing '.'s but not '..' */ 230 if ((l = cp - sp - 1) == 0 && c == '\0') { 231 cp--; 232 break; 233 } 234 if (l <= 0 || l > MAXLABEL) { 235 if (msg != NULL) 236 *lpp = NULL; 237 return (-1); 238 } 239 *sp = l; 240 } 241 if (cp >= eob) { 242 if (msg != NULL) 243 *lpp = NULL; 244 return (-1); 245 } 246 *cp++ = '\0'; 247 return (cp - comp_dn); 248 } 249 250 /* 251 * Skip over a compressed domain name. Return the size or -1. 252 */ 253 int 254 __dn_skipname(comp_dn, eom) 255 const u_char *comp_dn, *eom; 256 { 257 register const u_char *cp; 258 register int n; 259 260 cp = comp_dn; 261 while (cp < eom && (n = *cp++)) { 262 /* 263 * check for indirection 264 */ 265 switch (n & INDIR_MASK) { 266 case 0: /* normal case, n == len */ 267 cp += n; 268 continue; 269 case INDIR_MASK: /* indirection */ 270 cp++; 271 break; 272 default: /* illegal type */ 273 return (-1); 274 } 275 break; 276 } 277 if (cp > eom) 278 return (-1); 279 return (cp - comp_dn); 280 } 281 282 static int 283 mklower(ch) 284 register int ch; 285 { 286 if (isascii(ch) && isupper(ch)) 287 return (tolower(ch)); 288 return (ch); 289 } 290 291 /* 292 * Search for expanded name from a list of previously compressed names. 293 * Return the offset from msg if found or -1. 294 * dnptrs is the pointer to the first name on the list, 295 * not the pointer to the start of the message. 296 */ 297 static int 298 dn_find(exp_dn, msg, dnptrs, lastdnptr) 299 u_char *exp_dn, *msg; 300 u_char **dnptrs, **lastdnptr; 301 { 302 register u_char *dn, *cp, **cpp; 303 register int n; 304 u_char *sp; 305 306 for (cpp = dnptrs; cpp < lastdnptr; cpp++) { 307 dn = exp_dn; 308 sp = cp = *cpp; 309 while ((n = *cp++)) { 310 /* 311 * check for indirection 312 */ 313 switch (n & INDIR_MASK) { 314 case 0: /* normal case, n == len */ 315 while (--n >= 0) { 316 if (*dn == '.') 317 goto next; 318 if (*dn == '\\') 319 dn++; 320 if (mklower(*dn++) != mklower(*cp++)) 321 goto next; 322 } 323 if ((n = *dn++) == '\0' && *cp == '\0') 324 return (sp - msg); 325 if (n == '.') 326 continue; 327 goto next; 328 329 case INDIR_MASK: /* indirection */ 330 cp = msg + (((n & 0x3f) << 8) | *cp); 331 break; 332 333 default: /* illegal type */ 334 return (-1); 335 } 336 } 337 if (*dn == '\0') 338 return (sp - msg); 339 next: ; 340 } 341 return (-1); 342 } 343 344 /* 345 * Verify that a domain name uses an acceptable character set. 346 */ 347 348 /* 349 * Note the conspicuous absence of ctype macros in these definitions. On 350 * non-ASCII hosts, we can't depend on string literals or ctype macros to 351 * tell us anything about network-format data. The rest of the BIND system 352 * is not careful about this, but for some reason, we're doing it right here. 353 */ 354 #define PERIOD 0x2e 355 #define hyphenchar(c) ((c) == 0x2d) 356 #define bslashchar(c) ((c) == 0x5c) 357 #define periodchar(c) ((c) == PERIOD) 358 #define asterchar(c) ((c) == 0x2a) 359 #define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \ 360 || ((c) >= 0x61 && (c) <= 0x7a)) 361 #define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) 362 363 #define borderchar(c) (alphachar(c) || digitchar(c)) 364 #define middlechar(c) (borderchar(c) || hyphenchar(c)) 365 #define domainchar(c) ((c) > 0x20 && (c) < 0x7f) 366 367 int 368 res_hnok(dn) 369 const char *dn; 370 { 371 int pch = PERIOD, ch = *dn++; 372 373 while (ch != '\0') { 374 int nch = *dn++; 375 376 if (periodchar(ch)) { 377 ; 378 } else if (periodchar(pch)) { 379 if (!borderchar(ch)) 380 return (0); 381 } else if (periodchar(nch) || nch == '\0') { 382 if (!borderchar(ch)) 383 return (0); 384 } else { 385 if (!middlechar(ch)) 386 return (0); 387 } 388 pch = ch, ch = nch; 389 } 390 return (1); 391 } 392 393 /* 394 * hostname-like (A, MX, WKS) owners can have "*" as their first label 395 * but must otherwise be as a host name. 396 */ 397 int 398 res_ownok(dn) 399 const char *dn; 400 { 401 if (asterchar(dn[0])) { 402 if (periodchar(dn[1])) 403 return (res_hnok(dn+2)); 404 if (dn[1] == '\0') 405 return (1); 406 } 407 return (res_hnok(dn)); 408 } 409 410 /* 411 * SOA RNAMEs and RP RNAMEs can have any printable character in their first 412 * label, but the rest of the name has to look like a host name. 413 */ 414 int 415 res_mailok(dn) 416 const char *dn; 417 { 418 int ch, escaped = 0; 419 420 /* "." is a valid missing representation */ 421 if (*dn == '\0') 422 return(1); 423 424 /* otherwise <label>.<hostname> */ 425 while ((ch = *dn++) != '\0') { 426 if (!domainchar(ch)) 427 return (0); 428 if (!escaped && periodchar(ch)) 429 break; 430 if (escaped) 431 escaped = 0; 432 else if (bslashchar(ch)) 433 escaped = 1; 434 } 435 if (periodchar(ch)) 436 return (res_hnok(dn)); 437 return(0); 438 } 439 440 /* 441 * This function is quite liberal, since RFC 1034's character sets are only 442 * recommendations. 443 */ 444 int 445 res_dnok(dn) 446 const char *dn; 447 { 448 int ch; 449 450 while ((ch = *dn++) != '\0') 451 if (!domainchar(ch)) 452 return (0); 453 return (1); 454 } 455 456 /* 457 * Routines to insert/extract short/long's. 458 */ 459 460 u_int16_t 461 _getshort(msgp) 462 register const u_char *msgp; 463 { 464 register u_int16_t u; 465 466 GETSHORT(u, msgp); 467 return (u); 468 } 469 470 #ifdef NeXT 471 /* 472 * nExt machines have some funky library conventions, which we must maintain. 473 */ 474 u_int16_t 475 res_getshort(msgp) 476 register const u_char *msgp; 477 { 478 return (_getshort(msgp)); 479 } 480 #endif 481 482 u_int32_t 483 _getlong(msgp) 484 register const u_char *msgp; 485 { 486 register u_int32_t u; 487 488 GETLONG(u, msgp); 489 return (u); 490 } 491 492 void 493 #if defined(__STDC__) || defined(__cplusplus) 494 __putshort(register u_int16_t s, register u_char *msgp) /* must match proto */ 495 #else 496 __putshort(s, msgp) 497 register u_int16_t s; 498 register u_char *msgp; 499 #endif 500 { 501 PUTSHORT(s, msgp); 502 } 503 504 void 505 __putlong(l, msgp) 506 register u_int32_t l; 507 register u_char *msgp; 508 { 509 PUTLONG(l, msgp); 510 } 511