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