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