xref: /dragonfly/lib/libc/resolv/res_mkupdate.c (revision 4bab7bf3)
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  * $Id: res_mkupdate.c,v 1.8 2005/10/14 05:44:26 marka Exp $
18  */
19 
20 /*! \file
21  * \brief
22  * Based on the Dynamic DNS reference implementation by Viraj Bais
23  * <viraj_bais@ccm.fm.intel.com>
24  */
25 
26 #include "port_before.h"
27 
28 #include <sys/types.h>
29 #include <sys/param.h>
30 
31 #include <netinet/in.h>
32 #include <arpa/nameser.h>
33 #include <arpa/inet.h>
34 
35 #include <errno.h>
36 #include <limits.h>
37 #include <netdb.h>
38 #include <resolv.h>
39 #include <res_update.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <ctype.h>
45 
46 #ifdef _LIBC
47 #include "isc/list.h"
48 #endif
49 #include "port_after.h"
50 
51 /* Options.  Leave them on. */
52 #define DEBUG
53 #define MAXPORT 1024
54 
55 static int getnum_str(u_char **, u_char *);
56 static int gethexnum_str(u_char **, u_char *);
57 static int getword_str(char *, int, u_char **, u_char *);
58 static int getstr_str(char *, int, u_char **, u_char *);
59 
60 #define ShrinkBuffer(x)  if ((buflen -= x) < 0) return (-2);
61 
62 /* Forward. */
63 #ifdef _LIBC
64 static
65 #endif
66 int res_protocolnumber(const char *);
67 #ifdef _LIBC
68 static
69 #endif
70 int res_servicenumber(const char *);
71 
72 /*%
73  * Form update packets.
74  * Returns the size of the resulting packet if no error
75  *
76  * On error,
77  *	returns
78  *\li              -1 if error in reading a word/number in rdata
79  *		   portion for update packets
80  *\li		-2 if length of buffer passed is insufficient
81  *\li		-3 if zone section is not the first section in
82  *		   the linked list, or section order has a problem
83  *\li		-4 on a number overflow
84  *\li		-5 unknown operation or no records
85  */
86 int
87 res_nmkupdate(res_state statp, ns_updrec *rrecp_in, u_char *buf, int buflen) {
88 	ns_updrec *rrecp_start = rrecp_in;
89 	HEADER *hp;
90 	u_char *cp, *sp2, *startp, *endp;
91 	int n, i, soanum, multiline;
92 	ns_updrec *rrecp;
93 	struct in_addr ina;
94 	struct in6_addr in6a;
95         char buf2[MAXDNAME];
96 	u_char buf3[MAXDNAME];
97 	int section, numrrs = 0, counts[ns_s_max];
98 	u_int16_t rtype, rclass;
99 	u_int32_t n1, rttl;
100 	u_char *dnptrs[20], **dpp, **lastdnptr;
101 #ifndef _LIBC
102 	int siglen;
103 #endif
104 	int keylen, certlen;
105 
106 	/*
107 	 * Initialize header fields.
108 	 */
109 	if ((buf == NULL) || (buflen < HFIXEDSZ))
110 		return (-1);
111 	memset(buf, 0, HFIXEDSZ);
112 	hp = (HEADER *) buf;
113 	hp->id = htons(++statp->id);
114 	hp->opcode = ns_o_update;
115 	hp->rcode = NOERROR;
116 	cp = buf + HFIXEDSZ;
117 	buflen -= HFIXEDSZ;
118 	dpp = dnptrs;
119 	*dpp++ = buf;
120 	*dpp++ = NULL;
121 	lastdnptr = dnptrs + NELEM(dnptrs);
122 
123 	if (rrecp_start == NULL)
124 		return (-5);
125 	else if (rrecp_start->r_section != S_ZONE)
126 		return (-3);
127 
128 	memset(counts, 0, sizeof counts);
129 	for (rrecp = rrecp_start; rrecp; rrecp = NEXT(rrecp, r_glink)) {
130 		numrrs++;
131                 section = rrecp->r_section;
132 		if (section < 0 || section >= ns_s_max)
133 			return (-1);
134 		counts[section]++;
135 		for (i = section + 1; i < ns_s_max; i++)
136 			if (counts[i])
137 				return (-3);
138 		rtype = rrecp->r_type;
139 		rclass = rrecp->r_class;
140 		rttl = rrecp->r_ttl;
141 		/* overload class and type */
142 		if (section == S_PREREQ) {
143 			rttl = 0;
144 			switch (rrecp->r_opcode) {
145 			case YXDOMAIN:
146 				rclass = C_ANY;
147 				rtype = T_ANY;
148 				rrecp->r_size = 0;
149 				break;
150 			case NXDOMAIN:
151 				rclass = C_NONE;
152 				rtype = T_ANY;
153 				rrecp->r_size = 0;
154 				break;
155 			case NXRRSET:
156 				rclass = C_NONE;
157 				rrecp->r_size = 0;
158 				break;
159 			case YXRRSET:
160 				if (rrecp->r_size == 0)
161 					rclass = C_ANY;
162 				break;
163 			default:
164 				fprintf(stderr,
165 					"res_mkupdate: incorrect opcode: %d\n",
166 					rrecp->r_opcode);
167 				fflush(stderr);
168 				return (-1);
169 			}
170 		} else if (section == S_UPDATE) {
171 			switch (rrecp->r_opcode) {
172 			case DELETE:
173 				rclass = rrecp->r_size == 0 ? C_ANY : C_NONE;
174 				break;
175 			case ADD:
176 				break;
177 			default:
178 				fprintf(stderr,
179 					"res_mkupdate: incorrect opcode: %d\n",
180 					rrecp->r_opcode);
181 				fflush(stderr);
182 				return (-1);
183 			}
184 		}
185 
186 		/*
187 		 * XXX	appending default domain to owner name is omitted,
188 		 *	fqdn must be provided
189 		 */
190 		if ((n = dn_comp(rrecp->r_dname, cp, buflen, dnptrs,
191 				 lastdnptr)) < 0)
192 			return (-1);
193 		cp += n;
194 		ShrinkBuffer(n + 2*INT16SZ);
195 		PUTSHORT(rtype, cp);
196 		PUTSHORT(rclass, cp);
197 		if (section == S_ZONE) {
198 			if (numrrs != 1 || rrecp->r_type != T_SOA)
199 				return (-3);
200 			continue;
201 		}
202 		ShrinkBuffer(INT32SZ + INT16SZ);
203 		PUTLONG(rttl, cp);
204 		sp2 = cp;  /*%< save pointer to length byte */
205 		cp += INT16SZ;
206 		if (rrecp->r_size == 0) {
207 			if (section == S_UPDATE && rclass != C_ANY)
208 				return (-1);
209 			else {
210 				PUTSHORT(0, sp2);
211 				continue;
212 			}
213 		}
214 		startp = rrecp->r_data;
215 		endp = startp + rrecp->r_size - 1;
216 		/* XXX this should be done centrally. */
217 		switch (rrecp->r_type) {
218 		case T_A:
219 			if (!getword_str(buf2, sizeof buf2, &startp, endp))
220 				return (-1);
221 			if (!inet_aton(buf2, &ina))
222 				return (-1);
223 			n1 = ntohl(ina.s_addr);
224 			ShrinkBuffer(INT32SZ);
225 			PUTLONG(n1, cp);
226 			break;
227 		case T_CNAME:
228 		case T_MB:
229 		case T_MG:
230 		case T_MR:
231 		case T_NS:
232 		case T_PTR:
233 		case ns_t_dname:
234 			if (!getword_str(buf2, sizeof buf2, &startp, endp))
235 				return (-1);
236 			n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
237 			if (n < 0)
238 				return (-1);
239 			cp += n;
240 			ShrinkBuffer(n);
241 			break;
242 		case T_MINFO:
243 		case T_SOA:
244 		case T_RP:
245 			for (i = 0; i < 2; i++) {
246 				if (!getword_str(buf2, sizeof buf2, &startp,
247 						 endp))
248 					return (-1);
249 				n = dn_comp(buf2, cp, buflen,
250 					    dnptrs, lastdnptr);
251 				if (n < 0)
252 					return (-1);
253 				cp += n;
254 				ShrinkBuffer(n);
255 			}
256 			if (rrecp->r_type == T_SOA) {
257 				ShrinkBuffer(5 * INT32SZ);
258 				while (isspace(*startp) || !*startp)
259 					startp++;
260 				if (*startp == '(') {
261 					multiline = 1;
262 					startp++;
263 				} else
264 					multiline = 0;
265 				/* serial, refresh, retry, expire, minimum */
266 				for (i = 0; i < 5; i++) {
267 					soanum = getnum_str(&startp, endp);
268 					if (soanum < 0)
269 						return (-1);
270 					PUTLONG(soanum, cp);
271 				}
272 				if (multiline) {
273 					while (isspace(*startp) || !*startp)
274 						startp++;
275 					if (*startp != ')')
276 						return (-1);
277 				}
278 			}
279 			break;
280 		case T_MX:
281 		case T_AFSDB:
282 		case T_RT:
283 			n = getnum_str(&startp, endp);
284 			if (n < 0)
285 				return (-1);
286 			ShrinkBuffer(INT16SZ);
287 			PUTSHORT(n, cp);
288 			if (!getword_str(buf2, sizeof buf2, &startp, endp))
289 				return (-1);
290 			n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
291 			if (n < 0)
292 				return (-1);
293 			cp += n;
294 			ShrinkBuffer(n);
295 			break;
296 		case T_SRV:
297 			n = getnum_str(&startp, endp);
298 			if (n < 0)
299 				return (-1);
300 			ShrinkBuffer(INT16SZ);
301 			PUTSHORT(n, cp);
302 
303 			n = getnum_str(&startp, endp);
304 			if (n < 0)
305 				return (-1);
306 			ShrinkBuffer(INT16SZ);
307 			PUTSHORT(n, cp);
308 
309 			n = getnum_str(&startp, endp);
310 			if (n < 0)
311 				return (-1);
312 			ShrinkBuffer(INT16SZ);
313 			PUTSHORT(n, cp);
314 
315 			if (!getword_str(buf2, sizeof buf2, &startp, endp))
316 				return (-1);
317 			n = dn_comp(buf2, cp, buflen, NULL, NULL);
318 			if (n < 0)
319 				return (-1);
320 			cp += n;
321 			ShrinkBuffer(n);
322 			break;
323 		case T_PX:
324 			n = getnum_str(&startp, endp);
325 			if (n < 0)
326 				return (-1);
327 			PUTSHORT(n, cp);
328 			ShrinkBuffer(INT16SZ);
329 			for (i = 0; i < 2; i++) {
330 				if (!getword_str(buf2, sizeof buf2, &startp,
331 						 endp))
332 					return (-1);
333 				n = dn_comp(buf2, cp, buflen, dnptrs,
334 					    lastdnptr);
335 				if (n < 0)
336 					return (-1);
337 				cp += n;
338 				ShrinkBuffer(n);
339 			}
340 			break;
341 		case T_WKS: {
342 			char bm[MAXPORT/8];
343 			unsigned int maxbm = 0;
344 
345 			if (!getword_str(buf2, sizeof buf2, &startp, endp))
346 				return (-1);
347 			if (!inet_aton(buf2, &ina))
348 				return (-1);
349 			n1 = ntohl(ina.s_addr);
350 			ShrinkBuffer(INT32SZ);
351 			PUTLONG(n1, cp);
352 
353 			if (!getword_str(buf2, sizeof buf2, &startp, endp))
354 				return (-1);
355 			if ((i = res_protocolnumber(buf2)) < 0)
356 				return (-1);
357 			ShrinkBuffer(1);
358 			*cp++ = i & 0xff;
359 
360 			for (i = 0; i < MAXPORT/8 ; i++)
361 				bm[i] = 0;
362 
363 			while (getword_str(buf2, sizeof buf2, &startp, endp)) {
364 				if ((n = res_servicenumber(buf2)) <= 0)
365 					return (-1);
366 
367 				if (n < MAXPORT) {
368 					bm[n/8] |= (0x80>>(n%8));
369 					if ((unsigned)n > maxbm)
370 						maxbm = n;
371 				} else
372 					return (-1);
373 			}
374 			maxbm = maxbm/8 + 1;
375 			ShrinkBuffer(maxbm);
376 			memcpy(cp, bm, maxbm);
377 			cp += maxbm;
378 			break;
379 		}
380 		case T_HINFO:
381 			for (i = 0; i < 2; i++) {
382 				if ((n = getstr_str(buf2, sizeof buf2,
383 						&startp, endp)) < 0)
384 					return (-1);
385 				if (n > 255)
386 					return (-1);
387 				ShrinkBuffer(n+1);
388 				*cp++ = n;
389 				memcpy(cp, buf2, n);
390 				cp += n;
391 			}
392 			break;
393 		case T_TXT:
394 			for (;;) {
395 				if ((n = getstr_str(buf2, sizeof buf2,
396 						&startp, endp)) < 0) {
397 					if (cp != (sp2 + INT16SZ))
398 						break;
399 					return (-1);
400 				}
401 				if (n > 255)
402 					return (-1);
403 				ShrinkBuffer(n+1);
404 				*cp++ = n;
405 				memcpy(cp, buf2, n);
406 				cp += n;
407 			}
408 			break;
409 		case T_X25:
410 			/* RFC1183 */
411 			if ((n = getstr_str(buf2, sizeof buf2, &startp,
412 					 endp)) < 0)
413 				return (-1);
414 			if (n > 255)
415 				return (-1);
416 			ShrinkBuffer(n+1);
417 			*cp++ = n;
418 			memcpy(cp, buf2, n);
419 			cp += n;
420 			break;
421 		case T_ISDN:
422 			/* RFC1183 */
423 			if ((n = getstr_str(buf2, sizeof buf2, &startp,
424 					 endp)) < 0)
425 				return (-1);
426 			if ((n > 255) || (n == 0))
427 				return (-1);
428 			ShrinkBuffer(n+1);
429 			*cp++ = n;
430 			memcpy(cp, buf2, n);
431 			cp += n;
432 			if ((n = getstr_str(buf2, sizeof buf2, &startp,
433 					 endp)) < 0)
434 				n = 0;
435 			if (n > 255)
436 				return (-1);
437 			ShrinkBuffer(n+1);
438 			*cp++ = n;
439 			memcpy(cp, buf2, n);
440 			cp += n;
441 			break;
442 		case T_NSAP:
443 			if ((n = inet_nsap_addr((char *)startp, (u_char *)buf2, sizeof(buf2))) != 0) {
444 				ShrinkBuffer(n);
445 				memcpy(cp, buf2, n);
446 				cp += n;
447 			} else {
448 				return (-1);
449 			}
450 			break;
451 		case T_LOC:
452 			if ((n = loc_aton((char *)startp, (u_char *)buf2)) != 0) {
453 				ShrinkBuffer(n);
454 				memcpy(cp, buf2, n);
455 				cp += n;
456 			} else
457 				return (-1);
458 			break;
459 		case ns_t_sig:
460 #ifdef _LIBC
461 			return (-1);
462 #else
463 		    {
464 			int sig_type, success, dateerror;
465 			u_int32_t exptime, timesigned;
466 
467 			/* type */
468 			if ((n = getword_str(buf2, sizeof buf2,
469 					     &startp, endp)) < 0)
470 				return (-1);
471 			sig_type = sym_ston(__p_type_syms, buf2, &success);
472 			if (!success || sig_type == ns_t_any)
473 				return (-1);
474 			ShrinkBuffer(INT16SZ);
475 			PUTSHORT(sig_type, cp);
476 			/* alg */
477 			n = getnum_str(&startp, endp);
478 			if (n < 0)
479 				return (-1);
480 			ShrinkBuffer(1);
481 			*cp++ = n;
482 			/* labels */
483 			n = getnum_str(&startp, endp);
484 			if (n <= 0 || n > 255)
485 				return (-1);
486 			ShrinkBuffer(1);
487 			*cp++ = n;
488 			/* ottl  & expire */
489 			if (!getword_str(buf2, sizeof buf2, &startp, endp))
490 				return (-1);
491 			exptime = ns_datetosecs(buf2, &dateerror);
492 			if (!dateerror) {
493 				ShrinkBuffer(INT32SZ);
494 				PUTLONG(rttl, cp);
495 			}
496 			else {
497 				char *ulendp;
498 				u_int32_t ottl;
499 
500 				errno = 0;
501 				ottl = strtoul(buf2, &ulendp, 10);
502 				if (errno != 0 ||
503 				    (ulendp != NULL && *ulendp != '\0'))
504 					return (-1);
505 				ShrinkBuffer(INT32SZ);
506 				PUTLONG(ottl, cp);
507 				if (!getword_str(buf2, sizeof buf2, &startp,
508 						 endp))
509 					return (-1);
510 				exptime = ns_datetosecs(buf2, &dateerror);
511 				if (dateerror)
512 					return (-1);
513 			}
514 			/* expire */
515 			ShrinkBuffer(INT32SZ);
516 			PUTLONG(exptime, cp);
517 			/* timesigned */
518 			if (!getword_str(buf2, sizeof buf2, &startp, endp))
519 				return (-1);
520 			timesigned = ns_datetosecs(buf2, &dateerror);
521 			if (!dateerror) {
522 				ShrinkBuffer(INT32SZ);
523 				PUTLONG(timesigned, cp);
524 			}
525 			else
526 				return (-1);
527 			/* footprint */
528 			n = getnum_str(&startp, endp);
529 			if (n < 0)
530 				return (-1);
531 			ShrinkBuffer(INT16SZ);
532 			PUTSHORT(n, cp);
533 			/* signer name */
534 			if (!getword_str(buf2, sizeof buf2, &startp, endp))
535 				return (-1);
536 			n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
537 			if (n < 0)
538 				return (-1);
539 			cp += n;
540 			ShrinkBuffer(n);
541 			/* sig */
542 			if ((n = getword_str(buf2, sizeof buf2,
543 					     &startp, endp)) < 0)
544 				return (-1);
545 			siglen = b64_pton(buf2, buf3, sizeof(buf3));
546 			if (siglen < 0)
547 				return (-1);
548 			ShrinkBuffer(siglen);
549 			memcpy(cp, buf3, siglen);
550 			cp += siglen;
551 			break;
552 		    }
553 #endif
554 		case ns_t_key:
555 			/* flags */
556 			n = gethexnum_str(&startp, endp);
557 			if (n < 0)
558 				return (-1);
559 			ShrinkBuffer(INT16SZ);
560 			PUTSHORT(n, cp);
561 			/* proto */
562 			n = getnum_str(&startp, endp);
563 			if (n < 0)
564 				return (-1);
565 			ShrinkBuffer(1);
566 			*cp++ = n;
567 			/* alg */
568 			n = getnum_str(&startp, endp);
569 			if (n < 0)
570 				return (-1);
571 			ShrinkBuffer(1);
572 			*cp++ = n;
573 			/* key */
574 			if ((n = getword_str(buf2, sizeof buf2,
575 					     &startp, endp)) < 0)
576 				return (-1);
577 			keylen = b64_pton(buf2, buf3, sizeof(buf3));
578 			if (keylen < 0)
579 				return (-1);
580 			ShrinkBuffer(keylen);
581 			memcpy(cp, buf3, keylen);
582 			cp += keylen;
583 			break;
584 		case ns_t_nxt:
585 		    {
586 			int success, nxt_type;
587 			u_char data[32];
588 			int maxtype;
589 
590 			/* next name */
591 			if (!getword_str(buf2, sizeof buf2, &startp, endp))
592 				return (-1);
593 			n = dn_comp(buf2, cp, buflen, NULL, NULL);
594 			if (n < 0)
595 				return (-1);
596 			cp += n;
597 			ShrinkBuffer(n);
598 			maxtype = 0;
599 			memset(data, 0, sizeof data);
600 			for (;;) {
601 				if (!getword_str(buf2, sizeof buf2, &startp,
602 						 endp))
603 					break;
604 				nxt_type = sym_ston(__p_type_syms, buf2,
605 						    &success);
606 				if (!success || !ns_t_rr_p(nxt_type))
607 					return (-1);
608 				NS_NXT_BIT_SET(nxt_type, data);
609 				if (nxt_type > maxtype)
610 					maxtype = nxt_type;
611 			}
612 			n = maxtype/NS_NXT_BITS+1;
613 			ShrinkBuffer(n);
614 			memcpy(cp, data, n);
615 			cp += n;
616 			break;
617 		    }
618 		case ns_t_cert:
619 			/* type */
620 			n = getnum_str(&startp, endp);
621 			if (n < 0)
622 				return (-1);
623 			ShrinkBuffer(INT16SZ);
624 			PUTSHORT(n, cp);
625 			/* key tag */
626 			n = getnum_str(&startp, endp);
627 			if (n < 0)
628 				return (-1);
629 			ShrinkBuffer(INT16SZ);
630 			PUTSHORT(n, cp);
631 			/* alg */
632 			n = getnum_str(&startp, endp);
633 			if (n < 0)
634 				return (-1);
635 			ShrinkBuffer(1);
636 			*cp++ = n;
637 			/* cert */
638 			if ((n = getword_str(buf2, sizeof buf2,
639 					     &startp, endp)) < 0)
640 				return (-1);
641 			certlen = b64_pton(buf2, buf3, sizeof(buf3));
642 			if (certlen < 0)
643 				return (-1);
644 			ShrinkBuffer(certlen);
645 			memcpy(cp, buf3, certlen);
646 			cp += certlen;
647 			break;
648 		case ns_t_aaaa:
649 			if (!getword_str(buf2, sizeof buf2, &startp, endp))
650 				return (-1);
651 			if (inet_pton(AF_INET6, buf2, &in6a) <= 0)
652 				return (-1);
653 			ShrinkBuffer(NS_IN6ADDRSZ);
654 			memcpy(cp, &in6a, NS_IN6ADDRSZ);
655 			cp += NS_IN6ADDRSZ;
656 			break;
657 		case ns_t_naptr:
658 			/* Order Preference Flags Service Replacement Regexp */
659 			/* Order */
660 			n = getnum_str(&startp, endp);
661 			if (n < 0 || n > 65535)
662 				return (-1);
663 			ShrinkBuffer(INT16SZ);
664 			PUTSHORT(n, cp);
665 			/* Preference */
666 			n = getnum_str(&startp, endp);
667 			if (n < 0 || n > 65535)
668 				return (-1);
669 			ShrinkBuffer(INT16SZ);
670 			PUTSHORT(n, cp);
671 			/* Flags */
672 			if ((n = getstr_str(buf2, sizeof buf2,
673 					&startp, endp)) < 0) {
674 				return (-1);
675 			}
676 			if (n > 255)
677 				return (-1);
678 			ShrinkBuffer(n+1);
679 			*cp++ = n;
680 			memcpy(cp, buf2, n);
681 			cp += n;
682 			/* Service Classes */
683 			if ((n = getstr_str(buf2, sizeof buf2,
684 					&startp, endp)) < 0) {
685 				return (-1);
686 			}
687 			if (n > 255)
688 				return (-1);
689 			ShrinkBuffer(n+1);
690 			*cp++ = n;
691 			memcpy(cp, buf2, n);
692 			cp += n;
693 			/* Pattern */
694 			if ((n = getstr_str(buf2, sizeof buf2,
695 					&startp, endp)) < 0) {
696 				return (-1);
697 			}
698 			if (n > 255)
699 				return (-1);
700 			ShrinkBuffer(n+1);
701 			*cp++ = n;
702 			memcpy(cp, buf2, n);
703 			cp += n;
704 			/* Replacement */
705 			if (!getword_str(buf2, sizeof buf2, &startp, endp))
706 				return (-1);
707 			n = dn_comp(buf2, cp, buflen, NULL, NULL);
708 			if (n < 0)
709 				return (-1);
710 			cp += n;
711 			ShrinkBuffer(n);
712 			break;
713 		default:
714 			return (-1);
715 		} /*switch*/
716 		n = (u_int16_t)((cp - sp2) - INT16SZ);
717 		PUTSHORT(n, sp2);
718 	} /*for*/
719 
720 	hp->qdcount = htons(counts[0]);
721 	hp->ancount = htons(counts[1]);
722 	hp->nscount = htons(counts[2]);
723 	hp->arcount = htons(counts[3]);
724 	return (cp - buf);
725 }
726 
727 /*%
728  * Get a whitespace delimited word from a string (not file)
729  * into buf. modify the start pointer to point after the
730  * word in the string.
731  */
732 static int
733 getword_str(char *buf, int size, u_char **startpp, u_char *endp) {
734         char *cp;
735         int c;
736 
737         for (cp = buf; *startpp <= endp; ) {
738                 c = **startpp;
739                 if (isspace(c) || c == '\0') {
740                         if (cp != buf) /*%< trailing whitespace */
741                                 break;
742                         else { /*%< leading whitespace */
743                                 (*startpp)++;
744                                 continue;
745                         }
746                 }
747                 (*startpp)++;
748                 if (cp >= buf+size-1)
749                         break;
750                 *cp++ = (u_char)c;
751         }
752         *cp = '\0';
753         return (cp != buf);
754 }
755 
756 /*%
757  * get a white spae delimited string from memory.  Process quoted strings
758  * and \\DDD escapes.  Return length or -1 on error.  Returned string may
759  * contain nulls.
760  */
761 static char digits[] = "0123456789";
762 static int
763 getstr_str(char *buf, int size, u_char **startpp, u_char *endp) {
764         char *cp;
765         int c, c1 = 0;
766 	int inquote = 0;
767 	int seen_quote = 0;
768 	int escape = 0;
769 	int dig = 0;
770 
771 	for (cp = buf; *startpp <= endp; ) {
772                 if ((c = **startpp) == '\0')
773 			break;
774 		/* leading white space */
775 		if ((cp == buf) && !seen_quote && isspace(c)) {
776 			(*startpp)++;
777 			continue;
778 		}
779 
780 		switch (c) {
781 		case '\\':
782 			if (!escape)  {
783 				escape = 1;
784 				dig = 0;
785 				c1 = 0;
786 				(*startpp)++;
787 				continue;
788 			}
789 			goto do_escape;
790 		case '"':
791 			if (!escape) {
792 				inquote = !inquote;
793 				seen_quote = 1;
794 				(*startpp)++;
795 				continue;
796 			}
797 			/* fall through */
798 		default:
799 		do_escape:
800 			if (escape) {
801 				switch (c) {
802 				case '0':
803 				case '1':
804 				case '2':
805 				case '3':
806 				case '4':
807 				case '5':
808 				case '6':
809 				case '7':
810 				case '8':
811 				case '9':
812 					c1 = c1 * 10 +
813 						(strchr(digits, c) - digits);
814 
815 					if (++dig == 3) {
816 						c = c1 &0xff;
817 						break;
818 					}
819 					(*startpp)++;
820 					continue;
821 				}
822 				escape = 0;
823 			} else if (!inquote && isspace(c))
824 				goto done;
825 			if (cp >= buf+size-1)
826 				goto done;
827 			*cp++ = (u_char)c;
828 			(*startpp)++;
829 		}
830 	}
831  done:
832 	*cp = '\0';
833 	return ((cp == buf)?  (seen_quote? 0: -1): (cp - buf));
834 }
835 
836 /*%
837  * Get a whitespace delimited base 16 number from a string (not file) into buf
838  * update the start pointer to point after the number in the string.
839  */
840 static int
841 gethexnum_str(u_char **startpp, u_char *endp) {
842         int c, n;
843         int seendigit = 0;
844         int m = 0;
845 
846 	if (*startpp + 2 >= endp || strncasecmp((char *)*startpp, "0x", 2) != 0)
847 		return getnum_str(startpp, endp);
848 	(*startpp)+=2;
849         for (n = 0; *startpp <= endp; ) {
850                 c = **startpp;
851                 if (isspace(c) || c == '\0') {
852                         if (seendigit) /*%< trailing whitespace */
853                                 break;
854                         else { /*%< leading whitespace */
855                                 (*startpp)++;
856                                 continue;
857                         }
858                 }
859                 if (c == ';') {
860                         while ((*startpp <= endp) &&
861 			       ((c = **startpp) != '\n'))
862 					(*startpp)++;
863                         if (seendigit)
864                                 break;
865                         continue;
866                 }
867                 if (!isxdigit(c)) {
868                         if (c == ')' && seendigit) {
869                                 (*startpp)--;
870                                 break;
871                         }
872 			return (-1);
873                 }
874                 (*startpp)++;
875 		if (isdigit(c))
876 	                n = n * 16 + (c - '0');
877 		else
878 			n = n * 16 + (tolower(c) - 'a' + 10);
879                 seendigit = 1;
880         }
881         return (n + m);
882 }
883 
884 /*%
885  * Get a whitespace delimited base 10 number from a string (not file) into buf
886  * update the start pointer to point after the number in the string.
887  */
888 static int
889 getnum_str(u_char **startpp, u_char *endp) {
890         int c, n;
891         int seendigit = 0;
892         int m = 0;
893 
894         for (n = 0; *startpp <= endp; ) {
895                 c = **startpp;
896                 if (isspace(c) || c == '\0') {
897                         if (seendigit) /*%< trailing whitespace */
898                                 break;
899                         else { /*%< leading whitespace */
900                                 (*startpp)++;
901                                 continue;
902                         }
903                 }
904                 if (c == ';') {
905                         while ((*startpp <= endp) &&
906 			       ((c = **startpp) != '\n'))
907 					(*startpp)++;
908                         if (seendigit)
909                                 break;
910                         continue;
911                 }
912                 if (!isdigit(c)) {
913                         if (c == ')' && seendigit) {
914                                 (*startpp)--;
915                                 break;
916                         }
917 			return (-1);
918                 }
919                 (*startpp)++;
920                 n = n * 10 + (c - '0');
921                 seendigit = 1;
922         }
923         return (n + m);
924 }
925 
926 /*%
927  * Allocate a resource record buffer & save rr info.
928  */
929 ns_updrec *
930 res_mkupdrec(int section, const char *dname,
931 	     u_int class, u_int type, u_long ttl) {
932 	ns_updrec *rrecp = (ns_updrec *)calloc(1, sizeof(ns_updrec));
933 
934 	if (!rrecp || !(rrecp->r_dname = strdup(dname))) {
935 		if (rrecp)
936 			free((char *)rrecp);
937 		return (NULL);
938 	}
939 	INIT_LINK(rrecp, r_link);
940 	INIT_LINK(rrecp, r_glink);
941  	rrecp->r_class = (ns_class)class;
942 	rrecp->r_type = (ns_type)type;
943 	rrecp->r_ttl = ttl;
944 	rrecp->r_section = (ns_sect)section;
945 	return (rrecp);
946 }
947 
948 /*%
949  * Free a resource record buffer created by res_mkupdrec.
950  */
951 void
952 res_freeupdrec(ns_updrec *rrecp) {
953 	/* Note: freeing r_dp is the caller's responsibility. */
954 	if (rrecp->r_dname != NULL)
955 		free(rrecp->r_dname);
956 	free(rrecp);
957 }
958 
959 struct valuelist {
960 	struct valuelist *	next;
961 	struct valuelist *	prev;
962 	char *			name;
963 	char *			proto;
964 	int			port;
965 };
966 static struct valuelist *servicelist, *protolist;
967 
968 static void
969 res_buildservicelist(void) {
970 	struct servent *sp;
971 	struct valuelist *slp;
972 
973 #ifdef MAYBE_HESIOD
974 	setservent(0);
975 #else
976 	setservent(1);
977 #endif
978 	while ((sp = getservent()) != NULL) {
979 		slp = (struct valuelist *)malloc(sizeof(struct valuelist));
980 		if (!slp)
981 			break;
982 		slp->name = strdup(sp->s_name);
983 		slp->proto = strdup(sp->s_proto);
984 		if ((slp->name == NULL) || (slp->proto == NULL)) {
985 			if (slp->name) free(slp->name);
986 			if (slp->proto) free(slp->proto);
987 			free(slp);
988 			break;
989 		}
990 		slp->port = ntohs((u_int16_t)sp->s_port);  /*%< host byt order */
991 		slp->next = servicelist;
992 		slp->prev = NULL;
993 		if (servicelist)
994 			servicelist->prev = slp;
995 		servicelist = slp;
996 	}
997 	endservent();
998 }
999 
1000 #ifndef _LIBC
1001 void
1002 res_destroyservicelist() {
1003 	struct valuelist *slp, *slp_next;
1004 
1005 	for (slp = servicelist; slp != NULL; slp = slp_next) {
1006 		slp_next = slp->next;
1007 		free(slp->name);
1008 		free(slp->proto);
1009 		free(slp);
1010 	}
1011 	servicelist = (struct valuelist *)0;
1012 }
1013 #endif
1014 
1015 void
1016 res_buildprotolist(void) {
1017 	struct protoent *pp;
1018 	struct valuelist *slp;
1019 
1020 #ifdef MAYBE_HESIOD
1021 	setprotoent(0);
1022 #else
1023 	setprotoent(1);
1024 #endif
1025 	while ((pp = getprotoent()) != NULL) {
1026 		slp = (struct valuelist *)malloc(sizeof(struct valuelist));
1027 		if (!slp)
1028 			break;
1029 		slp->name = strdup(pp->p_name);
1030 		if (slp->name == NULL) {
1031 			free(slp);
1032 			break;
1033 		}
1034 		slp->port = pp->p_proto;	/*%< host byte order */
1035 		slp->next = protolist;
1036 		slp->prev = NULL;
1037 		if (protolist)
1038 			protolist->prev = slp;
1039 		protolist = slp;
1040 	}
1041 	endprotoent();
1042 }
1043 
1044 #ifndef _LIBC
1045 void
1046 res_destroyprotolist(void) {
1047 	struct valuelist *plp, *plp_next;
1048 
1049 	for (plp = protolist; plp != NULL; plp = plp_next) {
1050 		plp_next = plp->next;
1051 		free(plp->name);
1052 		free(plp);
1053 	}
1054 	protolist = (struct valuelist *)0;
1055 }
1056 #endif
1057 
1058 static int
1059 findservice(const char *s, struct valuelist **list) {
1060 	struct valuelist *lp = *list;
1061 	int n;
1062 
1063 	for (; lp != NULL; lp = lp->next)
1064 		if (strcasecmp(lp->name, s) == 0) {
1065 			if (lp != *list) {
1066 				lp->prev->next = lp->next;
1067 				if (lp->next)
1068 					lp->next->prev = lp->prev;
1069 				(*list)->prev = lp;
1070 				lp->next = *list;
1071 				*list = lp;
1072 			}
1073 			return (lp->port);	/*%< host byte order */
1074 		}
1075 	if (sscanf(s, "%d", &n) != 1 || n <= 0)
1076 		n = -1;
1077 	return (n);
1078 }
1079 
1080 /*%
1081  * Convert service name or (ascii) number to int.
1082  */
1083 #ifdef _LIBC
1084 static
1085 #endif
1086 int
1087 res_servicenumber(const char *p) {
1088 	if (servicelist == (struct valuelist *)0)
1089 		res_buildservicelist();
1090 	return (findservice(p, &servicelist));
1091 }
1092 
1093 /*%
1094  * Convert protocol name or (ascii) number to int.
1095  */
1096 #ifdef _LIBC
1097 static
1098 #endif
1099 int
1100 res_protocolnumber(const char *p) {
1101 	if (protolist == (struct valuelist *)0)
1102 		res_buildprotolist();
1103 	return (findservice(p, &protolist));
1104 }
1105 
1106 #ifndef _LIBC
1107 static struct servent *
1108 cgetservbyport(u_int16_t port, const char *proto) {	/*%< Host byte order. */
1109 	struct valuelist **list = &servicelist;
1110 	struct valuelist *lp = *list;
1111 	static struct servent serv;
1112 
1113 	port = ntohs(port);
1114 	for (; lp != NULL; lp = lp->next) {
1115 		if (port != (u_int16_t)lp->port)	/*%< Host byte order. */
1116 			continue;
1117 		if (strcasecmp(lp->proto, proto) == 0) {
1118 			if (lp != *list) {
1119 				lp->prev->next = lp->next;
1120 				if (lp->next)
1121 					lp->next->prev = lp->prev;
1122 				(*list)->prev = lp;
1123 				lp->next = *list;
1124 				*list = lp;
1125 			}
1126 			serv.s_name = lp->name;
1127 			serv.s_port = htons((u_int16_t)lp->port);
1128 			serv.s_proto = lp->proto;
1129 			return (&serv);
1130 		}
1131 	}
1132 	return (0);
1133 }
1134 
1135 static struct protoent *
1136 cgetprotobynumber(int proto) {				/*%< Host byte order. */
1137 	struct valuelist **list = &protolist;
1138 	struct valuelist *lp = *list;
1139 	static struct protoent prot;
1140 
1141 	for (; lp != NULL; lp = lp->next)
1142 		if (lp->port == proto) {		/*%< Host byte order. */
1143 			if (lp != *list) {
1144 				lp->prev->next = lp->next;
1145 				if (lp->next)
1146 					lp->next->prev = lp->prev;
1147 				(*list)->prev = lp;
1148 				lp->next = *list;
1149 				*list = lp;
1150 			}
1151 			prot.p_name = lp->name;
1152 			prot.p_proto = lp->port;	/*%< Host byte order. */
1153 			return (&prot);
1154 		}
1155 	return (0);
1156 }
1157 
1158 const char *
1159 res_protocolname(int num) {
1160 	static char number[8];
1161 	struct protoent *pp;
1162 
1163 	if (protolist == (struct valuelist *)0)
1164 		res_buildprotolist();
1165 	pp = cgetprotobynumber(num);
1166 	if (pp == NULL)  {
1167 		(void) sprintf(number, "%d", num);
1168 		return (number);
1169 	}
1170 	return (pp->p_name);
1171 }
1172 
1173 const char *
1174 res_servicename(u_int16_t port, const char *proto) {	/*%< Host byte order. */
1175 	static char number[8];
1176 	struct servent *ss;
1177 
1178 	if (servicelist == (struct valuelist *)0)
1179 		res_buildservicelist();
1180 	ss = cgetservbyport(htons(port), proto);
1181 	if (ss == NULL)  {
1182 		(void) sprintf(number, "%d", port);
1183 		return (number);
1184 	}
1185 	return (ss->s_name);
1186 }
1187 #endif /* !_LIBC */
1188