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