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