xref: /plan9-contrib/sys/src/cmd/ndb/convM2DNS.c (revision 82f6abee43a55f41b2fe672a88aaa23f21f003ab)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <ip.h>
43e12c5d1SDavid du Colombier #include "dns.h"
53e12c5d1SDavid du Colombier 
63e12c5d1SDavid du Colombier typedef struct Scan	Scan;
73e12c5d1SDavid du Colombier struct Scan
83e12c5d1SDavid du Colombier {
9*82f6abeeSDavid du Colombier 	uchar	*base;		/* input buffer */
10*82f6abeeSDavid du Colombier 	uchar	*p;		/* current position */
11*82f6abeeSDavid du Colombier 	uchar	*ep;		/* byte after the end */
124f8f669cSDavid du Colombier 
133e12c5d1SDavid du Colombier 	char	*err;
144f8f669cSDavid du Colombier 	char	errbuf[256];	/* hold a formatted error sometimes */
154f8f669cSDavid du Colombier 	int	rcode;		/* outgoing response codes (reply flags) */
1676783259SDavid du Colombier 	int	stop;		/* flag: stop processing */
17a41547ffSDavid du Colombier 	int	trunc;		/* flag: input truncated */
183e12c5d1SDavid du Colombier };
193e12c5d1SDavid du Colombier 
204f8f669cSDavid du Colombier static int
214f8f669cSDavid du Colombier errneg(RR *rp, Scan *sp, int actual)
224f8f669cSDavid du Colombier {
234f8f669cSDavid du Colombier 	snprint(sp->errbuf, sizeof sp->errbuf, "negative len %d: %R",
244f8f669cSDavid du Colombier 		actual, rp);
254f8f669cSDavid du Colombier 	sp->err = sp->errbuf;
264f8f669cSDavid du Colombier 	return 0;
274f8f669cSDavid du Colombier }
284f8f669cSDavid du Colombier 
294f8f669cSDavid du Colombier static int
3076783259SDavid du Colombier errtoolong(RR *rp, Scan *sp, int remain, int need, char *where)
314f8f669cSDavid du Colombier {
324f8f669cSDavid du Colombier 	char *p, *ep;
334f8f669cSDavid du Colombier 	char ptype[64];
344f8f669cSDavid du Colombier 
354f8f669cSDavid du Colombier 	p =  sp->errbuf;
364f8f669cSDavid du Colombier 	ep = sp->errbuf + sizeof sp->errbuf - 1;
374f8f669cSDavid du Colombier 	if (where)
384f8f669cSDavid du Colombier 		p = seprint(p, ep, "%s: ", where);
394f8f669cSDavid du Colombier 	if (rp)
404f8f669cSDavid du Colombier 		p = seprint(p, ep, "type %s RR: ",
414f8f669cSDavid du Colombier 			rrname(rp->type, ptype, sizeof ptype));
4276783259SDavid du Colombier 	p = seprint(p, ep, "%d bytes needed; %d remain", need, remain);
434f8f669cSDavid du Colombier 	if (rp)
444f8f669cSDavid du Colombier 		seprint(p, ep, ": %R", rp);
454f8f669cSDavid du Colombier 	sp->err = sp->errbuf;
46a41547ffSDavid du Colombier 	/* hack to cope with servers that don't set Ftrunc when they should */
47a41547ffSDavid du Colombier 	if (remain < Maxudp && need > Maxudp)
48a41547ffSDavid du Colombier 		sp->trunc = 1;
49*82f6abeeSDavid du Colombier 	if (debug && rp)
50b8b25780SDavid du Colombier 		dnslog("malformed rr: %R", rp);
514f8f669cSDavid du Colombier 	return 0;
524f8f669cSDavid du Colombier }
533e12c5d1SDavid du Colombier 
543e12c5d1SDavid du Colombier /*
553e12c5d1SDavid du Colombier  *  get a ushort/ulong
563e12c5d1SDavid du Colombier  */
573e12c5d1SDavid du Colombier static ushort
584f8f669cSDavid du Colombier gchar(RR *rp, Scan *sp)
597dd7cddfSDavid du Colombier {
607dd7cddfSDavid du Colombier 	ushort x;
617dd7cddfSDavid du Colombier 
627dd7cddfSDavid du Colombier 	if(sp->err)
637dd7cddfSDavid du Colombier 		return 0;
644f8f669cSDavid du Colombier 	if(sp->ep - sp->p < 1)
654f8f669cSDavid du Colombier 		return errtoolong(rp, sp, sp->ep - sp->p, 1, "gchar");
667dd7cddfSDavid du Colombier 	x = sp->p[0];
677dd7cddfSDavid du Colombier 	sp->p += 1;
687dd7cddfSDavid du Colombier 	return x;
697dd7cddfSDavid du Colombier }
707dd7cddfSDavid du Colombier static ushort
714f8f669cSDavid du Colombier gshort(RR *rp, Scan *sp)
723e12c5d1SDavid du Colombier {
733e12c5d1SDavid du Colombier 	ushort x;
743e12c5d1SDavid du Colombier 
753e12c5d1SDavid du Colombier 	if(sp->err)
763e12c5d1SDavid du Colombier 		return 0;
774f8f669cSDavid du Colombier 	if(sp->ep - sp->p < 2)
784f8f669cSDavid du Colombier 		return errtoolong(rp, sp, sp->ep - sp->p, 2, "gshort");
794f8f669cSDavid du Colombier 	x = sp->p[0]<<8 | sp->p[1];
803e12c5d1SDavid du Colombier 	sp->p += 2;
813e12c5d1SDavid du Colombier 	return x;
823e12c5d1SDavid du Colombier }
833e12c5d1SDavid du Colombier static ulong
844f8f669cSDavid du Colombier glong(RR *rp, Scan *sp)
853e12c5d1SDavid du Colombier {
863e12c5d1SDavid du Colombier 	ulong x;
873e12c5d1SDavid du Colombier 
883e12c5d1SDavid du Colombier 	if(sp->err)
893e12c5d1SDavid du Colombier 		return 0;
904f8f669cSDavid du Colombier 	if(sp->ep - sp->p < 4)
914f8f669cSDavid du Colombier 		return errtoolong(rp, sp, sp->ep - sp->p, 4, "glong");
924f8f669cSDavid du Colombier 	x = sp->p[0]<<24 | sp->p[1]<<16 | sp->p[2]<<8 | sp->p[3];
933e12c5d1SDavid du Colombier 	sp->p += 4;
943e12c5d1SDavid du Colombier 	return x;
953e12c5d1SDavid du Colombier }
963e12c5d1SDavid du Colombier 
973e12c5d1SDavid du Colombier /*
983e12c5d1SDavid du Colombier  *  get an ip address
993e12c5d1SDavid du Colombier  */
1003e12c5d1SDavid du Colombier static DN*
1014f8f669cSDavid du Colombier gv4addr(RR *rp, Scan *sp)
1023e12c5d1SDavid du Colombier {
1033e12c5d1SDavid du Colombier 	char addr[32];
1043e12c5d1SDavid du Colombier 
1053e12c5d1SDavid du Colombier 	if(sp->err)
1063e12c5d1SDavid du Colombier 		return 0;
1074f8f669cSDavid du Colombier 	if(sp->ep - sp->p < 4)
1084f8f669cSDavid du Colombier 		return (DN*)errtoolong(rp, sp, sp->ep - sp->p, 4, "gv4addr");
1094f8f669cSDavid du Colombier 	snprint(addr, sizeof addr, "%V", sp->p);
1103e12c5d1SDavid du Colombier 	sp->p += 4;
1113e12c5d1SDavid du Colombier 
1123e12c5d1SDavid du Colombier 	return dnlookup(addr, Cin, 1);
1133e12c5d1SDavid du Colombier }
1145d459b5aSDavid du Colombier static DN*
1154f8f669cSDavid du Colombier gv6addr(RR *rp, Scan *sp)
1165d459b5aSDavid du Colombier {
1175d459b5aSDavid du Colombier 	char addr[64];
1185d459b5aSDavid du Colombier 
1195d459b5aSDavid du Colombier 	if(sp->err)
1205d459b5aSDavid du Colombier 		return 0;
1214f8f669cSDavid du Colombier 	if(sp->ep - sp->p < IPaddrlen)
1224f8f669cSDavid du Colombier 		return (DN*)errtoolong(rp, sp, sp->ep - sp->p, IPaddrlen,
1234f8f669cSDavid du Colombier 			"gv6addr");
1244f8f669cSDavid du Colombier 	snprint(addr, sizeof addr, "%I", sp->p);
1255d459b5aSDavid du Colombier 	sp->p += IPaddrlen;
1265d459b5aSDavid du Colombier 
1275d459b5aSDavid du Colombier 	return dnlookup(addr, Cin, 1);
1285d459b5aSDavid du Colombier }
1293e12c5d1SDavid du Colombier 
1303e12c5d1SDavid du Colombier /*
1313e12c5d1SDavid du Colombier  *  get a string.  make it an internal symbol.
1323e12c5d1SDavid du Colombier  */
1333e12c5d1SDavid du Colombier static DN*
1344f8f669cSDavid du Colombier gsym(RR *rp, Scan *sp)
1353e12c5d1SDavid du Colombier {
1363e12c5d1SDavid du Colombier 	int n;
1373e12c5d1SDavid du Colombier 	char sym[Strlen+1];
1383e12c5d1SDavid du Colombier 
1393e12c5d1SDavid du Colombier 	if(sp->err)
1403e12c5d1SDavid du Colombier 		return 0;
1414f8f669cSDavid du Colombier 	n = 0;
1424f8f669cSDavid du Colombier 	if (sp->p < sp->ep)
1433e12c5d1SDavid du Colombier 		n = *(sp->p++);
1444f8f669cSDavid du Colombier 	if(sp->ep - sp->p < n)
1454f8f669cSDavid du Colombier 		return (DN*)errtoolong(rp, sp, sp->ep - sp->p, n, "gsym");
1463e12c5d1SDavid du Colombier 
1473e12c5d1SDavid du Colombier 	if(n > Strlen){
1484f8f669cSDavid du Colombier 		sp->err = "illegal string (symbol)";
1493e12c5d1SDavid du Colombier 		return 0;
1503e12c5d1SDavid du Colombier 	}
1513e12c5d1SDavid du Colombier 	strncpy(sym, (char*)sp->p, n);
1523e12c5d1SDavid du Colombier 	sym[n] = 0;
1534f8f669cSDavid du Colombier 	if (strlen(sym) != n)
1544f8f669cSDavid du Colombier 		sp->err = "symbol shorter than declared length";
1553e12c5d1SDavid du Colombier 	sp->p += n;
1563e12c5d1SDavid du Colombier 
1573e12c5d1SDavid du Colombier 	return dnlookup(sym, Csym, 1);
1583e12c5d1SDavid du Colombier }
1593e12c5d1SDavid du Colombier 
1603e12c5d1SDavid du Colombier /*
16160845620SDavid du Colombier  *  get a string.  don't make it an internal symbol.
16260845620SDavid du Colombier  */
16360845620SDavid du Colombier static Txt*
1644f8f669cSDavid du Colombier gstr(RR *rp, Scan *sp)
16560845620SDavid du Colombier {
16660845620SDavid du Colombier 	int n;
16760845620SDavid du Colombier 	char sym[Strlen+1];
16860845620SDavid du Colombier 	Txt *t;
16960845620SDavid du Colombier 
17060845620SDavid du Colombier 	if(sp->err)
17160845620SDavid du Colombier 		return 0;
1724f8f669cSDavid du Colombier 	n = 0;
1734f8f669cSDavid du Colombier 	if (sp->p < sp->ep)
17460845620SDavid du Colombier 		n = *(sp->p++);
1754f8f669cSDavid du Colombier 	if(sp->ep - sp->p < n)
1764f8f669cSDavid du Colombier 		return (Txt*)errtoolong(rp, sp, sp->ep - sp->p, n, "gstr");
17760845620SDavid du Colombier 
17860845620SDavid du Colombier 	if(n > Strlen){
17960845620SDavid du Colombier 		sp->err = "illegal string";
18060845620SDavid du Colombier 		return 0;
18160845620SDavid du Colombier 	}
18260845620SDavid du Colombier 	strncpy(sym, (char*)sp->p, n);
18360845620SDavid du Colombier 	sym[n] = 0;
1844f8f669cSDavid du Colombier 	if (strlen(sym) != n)
1854f8f669cSDavid du Colombier 		sp->err = "string shorter than declared length";
18660845620SDavid du Colombier 	sp->p += n;
18760845620SDavid du Colombier 
18860845620SDavid du Colombier 	t = emalloc(sizeof(*t));
18960845620SDavid du Colombier 	t->next = nil;
19060845620SDavid du Colombier 	t->p = estrdup(sym);
19160845620SDavid du Colombier 	return t;
19260845620SDavid du Colombier }
19360845620SDavid du Colombier 
19460845620SDavid du Colombier /*
1957dd7cddfSDavid du Colombier  *  get a sequence of bytes
1967dd7cddfSDavid du Colombier  */
1977dd7cddfSDavid du Colombier static int
1984f8f669cSDavid du Colombier gbytes(RR *rp, Scan *sp, uchar **p, int n)
1997dd7cddfSDavid du Colombier {
2004f8f669cSDavid du Colombier 	*p = nil;			/* i think this is a good idea */
2017dd7cddfSDavid du Colombier 	if(sp->err)
2027dd7cddfSDavid du Colombier 		return 0;
2034f8f669cSDavid du Colombier 	if(n < 0)
2044f8f669cSDavid du Colombier 		return errneg(rp, sp, n);
2054f8f669cSDavid du Colombier 	if(sp->ep - sp->p < n)
2064f8f669cSDavid du Colombier 		return errtoolong(rp, sp, sp->ep - sp->p, n, "gbytes");
2079a747e4fSDavid du Colombier 	*p = emalloc(n);
2087dd7cddfSDavid du Colombier 	memmove(*p, sp->p, n);
2097dd7cddfSDavid du Colombier 	sp->p += n;
2107dd7cddfSDavid du Colombier 
2117dd7cddfSDavid du Colombier 	return n;
2127dd7cddfSDavid du Colombier }
2137dd7cddfSDavid du Colombier 
2147dd7cddfSDavid du Colombier /*
2153e12c5d1SDavid du Colombier  *  get a domain name.  'to' must point to a buffer at least Domlen+1 long.
2163e12c5d1SDavid du Colombier  */
2173e12c5d1SDavid du Colombier static char*
2184f8f669cSDavid du Colombier gname(char *to, RR *rp, Scan *sp)
2193e12c5d1SDavid du Colombier {
2204f8f669cSDavid du Colombier 	int len, off, pointer, n;
2214f8f669cSDavid du Colombier 	char *tostart, *toend;
2223e12c5d1SDavid du Colombier 	uchar *p;
2233e12c5d1SDavid du Colombier 
2243e12c5d1SDavid du Colombier 	tostart = to;
22576783259SDavid du Colombier 	if(sp->err || sp->stop)
2263e12c5d1SDavid du Colombier 		goto err;
2273e12c5d1SDavid du Colombier 	pointer = 0;
2283e12c5d1SDavid du Colombier 	p = sp->p;
229*82f6abeeSDavid du Colombier 	if (p == nil) {
230*82f6abeeSDavid du Colombier 		dnslog("gname: %R: nil sp->p", rp);
231*82f6abeeSDavid du Colombier 		goto err;
232*82f6abeeSDavid du Colombier 	}
2333e12c5d1SDavid du Colombier 	toend = to + Domlen;
23476783259SDavid du Colombier 	for(len = 0; *p && p < sp->ep; len += (pointer? 0: n+1)) {
23576783259SDavid du Colombier 		n = 0;
23676783259SDavid du Colombier 		switch (*p & 0300) {
23776783259SDavid du Colombier 		case 0:			/* normal label */
23876783259SDavid du Colombier 			if (p < sp->ep)
23976783259SDavid du Colombier 				n = *p++ & 077;		/* pick up length */
24076783259SDavid du Colombier 			if(len + n < Domlen - 1){
24176783259SDavid du Colombier 				if(n > toend - to){
24276783259SDavid du Colombier 					errtoolong(rp, sp, toend - to, n,
24376783259SDavid du Colombier 						"name too long");
24476783259SDavid du Colombier 					goto err;
24576783259SDavid du Colombier 				}
24676783259SDavid du Colombier 				memmove(to, p, n);
24776783259SDavid du Colombier 				to += n;
24876783259SDavid du Colombier 			}
24976783259SDavid du Colombier 			p += n;
25076783259SDavid du Colombier 			if(*p){
25176783259SDavid du Colombier 				if(to >= toend){
25276783259SDavid du Colombier 					errtoolong(rp, sp, toend - to, 2,
25376783259SDavid du Colombier 				     "more name components but no bytes left");
25476783259SDavid du Colombier 					goto err;
25576783259SDavid du Colombier 				}
25676783259SDavid du Colombier 				*to++ = '.';
25776783259SDavid du Colombier 			}
25876783259SDavid du Colombier 			break;
25976783259SDavid du Colombier 		case 0100:		/* edns extended label type, rfc 2671 */
26076783259SDavid du Colombier 			/*
26176783259SDavid du Colombier 			 * treat it like an EOF for now; it seems to be at
26276783259SDavid du Colombier 			 * the end of a long tcp reply.
26376783259SDavid du Colombier 			 */
2645d34ce99SDavid du Colombier 			dnslog("edns label; first byte 0%o = '%c'", *p, *p);
26576783259SDavid du Colombier 			sp->stop = 1;
26676783259SDavid du Colombier 			goto err;
26776783259SDavid du Colombier 		case 0200:		/* reserved */
26876783259SDavid du Colombier 			sp->err = "reserved-use label present";
26976783259SDavid du Colombier 			goto err;
27076783259SDavid du Colombier 		case 0300:		/* pointer to other spot in message */
2713e12c5d1SDavid du Colombier 			if(pointer++ > 10){
2723e12c5d1SDavid du Colombier 				sp->err = "pointer loop";
2733e12c5d1SDavid du Colombier 				goto err;
2743e12c5d1SDavid du Colombier 			}
2755d34ce99SDavid du Colombier 			off = (p[0] & 077)<<8 | p[1];
2763e12c5d1SDavid du Colombier 			p = sp->base + off;
2773e12c5d1SDavid du Colombier 			if(p >= sp->ep){
2783e12c5d1SDavid du Colombier 				sp->err = "bad pointer";
2793e12c5d1SDavid du Colombier 				goto err;
2803e12c5d1SDavid du Colombier 			}
2813e12c5d1SDavid du Colombier 			n = 0;
28276783259SDavid du Colombier 			break;
2833e12c5d1SDavid du Colombier 		}
2843e12c5d1SDavid du Colombier 	}
2853e12c5d1SDavid du Colombier 	*to = 0;
2863e12c5d1SDavid du Colombier 	if(pointer)
2873e12c5d1SDavid du Colombier 		sp->p += len + 2;	/* + 2 for pointer */
2883e12c5d1SDavid du Colombier 	else
2893e12c5d1SDavid du Colombier 		sp->p += len + 1;	/* + 1 for the null domain */
2903e12c5d1SDavid du Colombier 	return tostart;
2913e12c5d1SDavid du Colombier err:
2923e12c5d1SDavid du Colombier 	*tostart = 0;
2933e12c5d1SDavid du Colombier 	return tostart;
2943e12c5d1SDavid du Colombier }
2953e12c5d1SDavid du Colombier 
2963e12c5d1SDavid du Colombier /*
2974f8f669cSDavid du Colombier  * ms windows 2000 seems to get the bytes backward in the type field
2984f8f669cSDavid du Colombier  * of ptr records, so return a format error as feedback.
2994f8f669cSDavid du Colombier  */
300d9924332SDavid du Colombier static ushort
301d9924332SDavid du Colombier mstypehack(Scan *sp, ushort type, char *where)
3024f8f669cSDavid du Colombier {
303d9924332SDavid du Colombier 	if ((uchar)type == 0 && (type>>8) != 0) {
3044f8f669cSDavid du Colombier 		USED(where);
3054f8f669cSDavid du Colombier //		dnslog("%s: byte-swapped type field in ptr rr from win2k",
3064f8f669cSDavid du Colombier //			where);
307d9924332SDavid du Colombier 		if (sp->rcode == Rok)
3084f8f669cSDavid du Colombier 			sp->rcode = Rformat;
309*82f6abeeSDavid du Colombier 		type >>= 8;
3104f8f669cSDavid du Colombier 	}
311d9924332SDavid du Colombier 	return type;
3124f8f669cSDavid du Colombier }
3134f8f669cSDavid du Colombier 
314*82f6abeeSDavid du Colombier #define NAME(x)		gname(x, rp, sp)
315*82f6abeeSDavid du Colombier #define SYMBOL(x)	((x) = gsym(rp, sp))
316*82f6abeeSDavid du Colombier #define STRING(x)	((x) = gstr(rp, sp))
317*82f6abeeSDavid du Colombier #define USHORT(x)	((x) = gshort(rp, sp))
318*82f6abeeSDavid du Colombier #define ULONG(x)	((x) = glong(rp, sp))
319*82f6abeeSDavid du Colombier #define UCHAR(x)	((x) = gchar(rp, sp))
320*82f6abeeSDavid du Colombier #define V4ADDR(x)	((x) = gv4addr(rp, sp))
321*82f6abeeSDavid du Colombier #define V6ADDR(x)	((x) = gv6addr(rp, sp))
322*82f6abeeSDavid du Colombier #define BYTES(x, y)	((y) = gbytes(rp, sp, &(x), len - (sp->p - data)))
323*82f6abeeSDavid du Colombier 
3244f8f669cSDavid du Colombier /*
3253e12c5d1SDavid du Colombier  *  convert the next RR from a message
3263e12c5d1SDavid du Colombier  */
3273e12c5d1SDavid du Colombier static RR*
3284f8f669cSDavid du Colombier convM2RR(Scan *sp, char *what)
3293e12c5d1SDavid du Colombier {
330*82f6abeeSDavid du Colombier 	int type, class, len, left;
3313e12c5d1SDavid du Colombier 	char dname[Domlen+1];
3324f8f669cSDavid du Colombier 	uchar *data;
333*82f6abeeSDavid du Colombier 	RR *rp;
33460845620SDavid du Colombier 	Txt *t, **l;
3353e12c5d1SDavid du Colombier 
3367dd7cddfSDavid du Colombier retry:
337*82f6abeeSDavid du Colombier 	rp = nil;
3383e12c5d1SDavid du Colombier 	NAME(dname);
3393e12c5d1SDavid du Colombier 	USHORT(type);
3403e12c5d1SDavid du Colombier 	USHORT(class);
3413e12c5d1SDavid du Colombier 
342d9924332SDavid du Colombier 	type = mstypehack(sp, type, "convM2RR");
3433e12c5d1SDavid du Colombier 	rp = rralloc(type);
3443e12c5d1SDavid du Colombier 	rp->owner = dnlookup(dname, class, 1);
3453e12c5d1SDavid du Colombier 	rp->type = type;
3463e12c5d1SDavid du Colombier 
3473e12c5d1SDavid du Colombier 	ULONG(rp->ttl);
3487dd7cddfSDavid du Colombier 	rp->ttl += now;
349*82f6abeeSDavid du Colombier 	USHORT(len);			/* length of data following */
3503e12c5d1SDavid du Colombier 	data = sp->p;
351*82f6abeeSDavid du Colombier 	assert(data != nil);
352*82f6abeeSDavid du Colombier 	left = sp->ep - sp->p;
3537dd7cddfSDavid du Colombier 
3544f8f669cSDavid du Colombier 	/*
3554f8f669cSDavid du Colombier 	 * ms windows generates a lot of badly-formatted hints.
3564f8f669cSDavid du Colombier 	 * hints are only advisory, so don't log complaints about them.
3574f8f669cSDavid du Colombier 	 * it also generates answers in which p overshoots ep by exactly
3584f8f669cSDavid du Colombier 	 * one byte; this seems to be harmless, so don't log them either.
3594f8f669cSDavid du Colombier 	 */
360*82f6abeeSDavid du Colombier 	if (len > left &&
3614f8f669cSDavid du Colombier 	   !(strcmp(what, "hints") == 0 ||
36276783259SDavid du Colombier 	     sp->p == sp->ep + 1 && strcmp(what, "answers") == 0))
363*82f6abeeSDavid du Colombier 		errtoolong(rp, sp, left, len, "convM2RR");
36476783259SDavid du Colombier 	if(sp->err || sp->rcode || sp->stop){
3657dd7cddfSDavid du Colombier 		rrfree(rp);
3665d34ce99SDavid du Colombier 		return nil;
3677dd7cddfSDavid du Colombier 	}
368*82f6abeeSDavid du Colombier 	/* even if we don't log an error message, truncate length to fit data */
369*82f6abeeSDavid du Colombier 	if (len > left)
370*82f6abeeSDavid du Colombier 		len = left;
3717dd7cddfSDavid du Colombier 
3723e12c5d1SDavid du Colombier 	switch(type){
3737dd7cddfSDavid du Colombier 	default:
3747dd7cddfSDavid du Colombier 		/* unknown type, just ignore it */
3757dd7cddfSDavid du Colombier 		sp->p = data + len;
3767dd7cddfSDavid du Colombier 		rrfree(rp);
3777dd7cddfSDavid du Colombier 		goto retry;
3783e12c5d1SDavid du Colombier 	case Thinfo:
37960845620SDavid du Colombier 		SYMBOL(rp->cpu);
38060845620SDavid du Colombier 		SYMBOL(rp->os);
3813e12c5d1SDavid du Colombier 		break;
3823e12c5d1SDavid du Colombier 	case Tcname:
3833e12c5d1SDavid du Colombier 	case Tmb:
3843e12c5d1SDavid du Colombier 	case Tmd:
3853e12c5d1SDavid du Colombier 	case Tmf:
3863e12c5d1SDavid du Colombier 	case Tns:
3873e12c5d1SDavid du Colombier 		rp->host = dnlookup(NAME(dname), Cin, 1);
3883e12c5d1SDavid du Colombier 		break;
3893e12c5d1SDavid du Colombier 	case Tmg:
3903e12c5d1SDavid du Colombier 	case Tmr:
3913e12c5d1SDavid du Colombier 		rp->mb  = dnlookup(NAME(dname), Cin, 1);
3923e12c5d1SDavid du Colombier 		break;
3933e12c5d1SDavid du Colombier 	case Tminfo:
3943e12c5d1SDavid du Colombier 		rp->rmb = dnlookup(NAME(dname), Cin, 1);
3953e12c5d1SDavid du Colombier 		rp->mb  = dnlookup(NAME(dname), Cin, 1);
3963e12c5d1SDavid du Colombier 		break;
3973e12c5d1SDavid du Colombier 	case Tmx:
3983e12c5d1SDavid du Colombier 		USHORT(rp->pref);
3993e12c5d1SDavid du Colombier 		rp->host = dnlookup(NAME(dname), Cin, 1);
4003e12c5d1SDavid du Colombier 		break;
4013e12c5d1SDavid du Colombier 	case Ta:
4025d459b5aSDavid du Colombier 		V4ADDR(rp->ip);
4035d459b5aSDavid du Colombier 		break;
4045d459b5aSDavid du Colombier 	case Taaaa:
4055d459b5aSDavid du Colombier 		V6ADDR(rp->ip);
4063e12c5d1SDavid du Colombier 		break;
4073e12c5d1SDavid du Colombier 	case Tptr:
4083e12c5d1SDavid du Colombier 		rp->ptr = dnlookup(NAME(dname), Cin, 1);
4093e12c5d1SDavid du Colombier 		break;
4103e12c5d1SDavid du Colombier 	case Tsoa:
4113e12c5d1SDavid du Colombier 		rp->host = dnlookup(NAME(dname), Cin, 1);
4123e12c5d1SDavid du Colombier 		rp->rmb  = dnlookup(NAME(dname), Cin, 1);
4133e12c5d1SDavid du Colombier 		ULONG(rp->soa->serial);
4143e12c5d1SDavid du Colombier 		ULONG(rp->soa->refresh);
4153e12c5d1SDavid du Colombier 		ULONG(rp->soa->retry);
4163e12c5d1SDavid du Colombier 		ULONG(rp->soa->expire);
4173e12c5d1SDavid du Colombier 		ULONG(rp->soa->minttl);
4183e12c5d1SDavid du Colombier 		break;
4194f8f669cSDavid du Colombier 	case Tsrv:
4204f8f669cSDavid du Colombier 		USHORT(rp->srv->pri);
4214f8f669cSDavid du Colombier 		USHORT(rp->srv->weight);
422225077b0SDavid du Colombier 		USHORT(rp->port);
423225077b0SDavid du Colombier 		/*
424225077b0SDavid du Colombier 		 * rfc2782 sez no name compression but to be
425225077b0SDavid du Colombier 		 * backward-compatible with rfc2052, we try to expand the name.
426225077b0SDavid du Colombier 		 * if the length is under 64 bytes, either interpretation is
427225077b0SDavid du Colombier 		 * fine; if it's longer, we'll assume it's compressed,
428225077b0SDavid du Colombier 		 * as recommended by rfc3597.
429225077b0SDavid du Colombier 		 */
430225077b0SDavid du Colombier 		rp->host = dnlookup(NAME(dname), Cin, 1);
4314f8f669cSDavid du Colombier 		break;
4327dd7cddfSDavid du Colombier 	case Ttxt:
43360845620SDavid du Colombier 		l = &rp->txt;
43460845620SDavid du Colombier 		*l = nil;
43560845620SDavid du Colombier 		while(sp->p - data < len){
43660845620SDavid du Colombier 			STRING(t);
43760845620SDavid du Colombier 			*l = t;
43860845620SDavid du Colombier 			l = &t->next;
43960845620SDavid du Colombier 		}
4407dd7cddfSDavid du Colombier 		break;
4419a747e4fSDavid du Colombier 	case Tnull:
4429a747e4fSDavid du Colombier 		BYTES(rp->null->data, rp->null->dlen);
4439a747e4fSDavid du Colombier 		break;
4447dd7cddfSDavid du Colombier 	case Trp:
4457dd7cddfSDavid du Colombier 		rp->rmb = dnlookup(NAME(dname), Cin, 1);
44660845620SDavid du Colombier 		rp->rp  = dnlookup(NAME(dname), Cin, 1);
4477dd7cddfSDavid du Colombier 		break;
4487dd7cddfSDavid du Colombier 	case Tkey:
4497dd7cddfSDavid du Colombier 		USHORT(rp->key->flags);
4507dd7cddfSDavid du Colombier 		UCHAR(rp->key->proto);
4517dd7cddfSDavid du Colombier 		UCHAR(rp->key->alg);
4527dd7cddfSDavid du Colombier 		BYTES(rp->key->data, rp->key->dlen);
4537dd7cddfSDavid du Colombier 		break;
4547dd7cddfSDavid du Colombier 	case Tsig:
4557dd7cddfSDavid du Colombier 		USHORT(rp->sig->type);
4567dd7cddfSDavid du Colombier 		UCHAR(rp->sig->alg);
4577dd7cddfSDavid du Colombier 		UCHAR(rp->sig->labels);
4587dd7cddfSDavid du Colombier 		ULONG(rp->sig->ttl);
4597dd7cddfSDavid du Colombier 		ULONG(rp->sig->exp);
4607dd7cddfSDavid du Colombier 		ULONG(rp->sig->incep);
4617dd7cddfSDavid du Colombier 		USHORT(rp->sig->tag);
4627dd7cddfSDavid du Colombier 		rp->sig->signer = dnlookup(NAME(dname), Cin, 1);
4637dd7cddfSDavid du Colombier 		BYTES(rp->sig->data, rp->sig->dlen);
4647dd7cddfSDavid du Colombier 		break;
4657dd7cddfSDavid du Colombier 	case Tcert:
4667dd7cddfSDavid du Colombier 		USHORT(rp->cert->type);
4677dd7cddfSDavid du Colombier 		USHORT(rp->cert->tag);
4687dd7cddfSDavid du Colombier 		UCHAR(rp->cert->alg);
4697dd7cddfSDavid du Colombier 		BYTES(rp->cert->data, rp->cert->dlen);
4707dd7cddfSDavid du Colombier 		break;
4713e12c5d1SDavid du Colombier 	}
4724f8f669cSDavid du Colombier 	if(sp->p - data != len) {
4734f8f669cSDavid du Colombier 		char ptype[64];
4744f8f669cSDavid du Colombier 
4754f8f669cSDavid du Colombier 		/*
4764f8f669cSDavid du Colombier 		 * ms windows 2000 generates cname queries for reverse lookups
4774f8f669cSDavid du Colombier 		 * with this particular error.  don't bother logging it.
4784f8f669cSDavid du Colombier 		 *
4794f8f669cSDavid du Colombier 		 * server: input error: bad cname RR len (actual 2 != len 0):
4804f8f669cSDavid du Colombier 		 * 235.9.104.135.in-addr.arpa cname
4814f8f669cSDavid du Colombier 		 *	235.9.104.135.in-addr.arpa from 135.104.9.235
4824f8f669cSDavid du Colombier 		 */
483b8b25780SDavid du Colombier 		if (type == Tcname && sp->p - data == 2 && len == 0)
4844f8f669cSDavid du Colombier 			return rp;
485225077b0SDavid du Colombier 		if (len > sp->p - data){
48676783259SDavid du Colombier 			dnslog("bad %s RR len (%d bytes nominal, %lud actual): %R",
48776783259SDavid du Colombier 				rrname(type, ptype, sizeof ptype), len,
48876783259SDavid du Colombier 				sp->p - data, rp);
489225077b0SDavid du Colombier 			rrfree(rp);
490225077b0SDavid du Colombier 			rp = nil;
49176783259SDavid du Colombier 		}
492225077b0SDavid du Colombier 	}
493225077b0SDavid du Colombier 	// if(rp) dnslog("convM2RR: got %R", rp);
4943e12c5d1SDavid du Colombier 	return rp;
4953e12c5d1SDavid du Colombier }
4963e12c5d1SDavid du Colombier 
4973e12c5d1SDavid du Colombier /*
4983e12c5d1SDavid du Colombier  *  convert the next question from a message
4993e12c5d1SDavid du Colombier  */
5003e12c5d1SDavid du Colombier static RR*
5013e12c5d1SDavid du Colombier convM2Q(Scan *sp)
5023e12c5d1SDavid du Colombier {
5033e12c5d1SDavid du Colombier 	char dname[Domlen+1];
5044f8f669cSDavid du Colombier 	int type, class;
505*82f6abeeSDavid du Colombier 	RR *rp;
5063e12c5d1SDavid du Colombier 
507*82f6abeeSDavid du Colombier 	rp = nil;
5083e12c5d1SDavid du Colombier 	NAME(dname);
5093e12c5d1SDavid du Colombier 	USHORT(type);
5103e12c5d1SDavid du Colombier 	USHORT(class);
51176783259SDavid du Colombier 	if(sp->err || sp->rcode || sp->stop)
5124f8f669cSDavid du Colombier 		return nil;
5133e12c5d1SDavid du Colombier 
514d9924332SDavid du Colombier 	type = mstypehack(sp, type, "convM2Q");
5153e12c5d1SDavid du Colombier 	rp = rralloc(type);
5163e12c5d1SDavid du Colombier 	rp->owner = dnlookup(dname, class, 1);
5173e12c5d1SDavid du Colombier 
5183e12c5d1SDavid du Colombier 	return rp;
5193e12c5d1SDavid du Colombier }
5203e12c5d1SDavid du Colombier 
5213e12c5d1SDavid du Colombier static RR*
5224f8f669cSDavid du Colombier rrloop(Scan *sp, char *what, int count, int quest)
5233e12c5d1SDavid du Colombier {
5243e12c5d1SDavid du Colombier 	int i;
5253e12c5d1SDavid du Colombier 	RR *first, *rp, **l;
5263e12c5d1SDavid du Colombier 
52776783259SDavid du Colombier 	if(sp->err || sp->rcode || sp->stop)
5284f8f669cSDavid du Colombier 		return nil;
5293e12c5d1SDavid du Colombier 	l = &first;
5304f8f669cSDavid du Colombier 	first = nil;
5313e12c5d1SDavid du Colombier 	for(i = 0; i < count; i++){
5324f8f669cSDavid du Colombier 		rp = quest? convM2Q(sp): convM2RR(sp, what);
5334f8f669cSDavid du Colombier 		if(rp == nil)
534bd389b36SDavid du Colombier 			break;
5355d34ce99SDavid du Colombier 		setmalloctag(rp, getcallerpc(&sp));
536b8b25780SDavid du Colombier 		/*
537b8b25780SDavid du Colombier 		 * it might be better to ignore the bad rr, possibly break out,
538b8b25780SDavid du Colombier 		 * but return the previous rrs, if any.  that way our callers
539b8b25780SDavid du Colombier 		 * would know that they had got a response, however ill-formed.
540b8b25780SDavid du Colombier 		 */
54176783259SDavid du Colombier 		if(sp->err || sp->rcode || sp->stop){
5423e12c5d1SDavid du Colombier 			rrfree(rp);
5433e12c5d1SDavid du Colombier 			break;
5443e12c5d1SDavid du Colombier 		}
5453e12c5d1SDavid du Colombier 		*l = rp;
5463e12c5d1SDavid du Colombier 		l = &rp->next;
5473e12c5d1SDavid du Colombier 	}
548*82f6abeeSDavid du Colombier //	if(first)
5495d34ce99SDavid du Colombier //		setmalloctag(first, getcallerpc(&sp));
5503e12c5d1SDavid du Colombier 	return first;
5513e12c5d1SDavid du Colombier }
5523e12c5d1SDavid du Colombier 
5533e12c5d1SDavid du Colombier /*
5544f8f669cSDavid du Colombier  *  convert the next DNS from a message stream.
5554f8f669cSDavid du Colombier  *  if there are formatting errors or the like during parsing of the message,
5564f8f669cSDavid du Colombier  *  set *codep to the outgoing response code (e.g., Rformat), which will
5574f8f669cSDavid du Colombier  *  abort processing and reply immediately with the outgoing response code.
5583e12c5d1SDavid du Colombier  */
5593e12c5d1SDavid du Colombier char*
5604f8f669cSDavid du Colombier convM2DNS(uchar *buf, int len, DNSmsg *m, int *codep)
5613e12c5d1SDavid du Colombier {
562186d659cSDavid du Colombier 	char *err = nil;
5634f8f669cSDavid du Colombier 	RR *rp = nil;
5640319257bSDavid du Colombier 	Scan scan;
5650319257bSDavid du Colombier 	Scan *sp;
5663e12c5d1SDavid du Colombier 
5674f8f669cSDavid du Colombier 	assert(len >= 0);
568*82f6abeeSDavid du Colombier 	assert(buf != nil);
5693e12c5d1SDavid du Colombier 	sp = &scan;
5700319257bSDavid du Colombier 	memset(sp, 0, sizeof *sp);
5710319257bSDavid du Colombier 	sp->base = sp->p = buf;
5720319257bSDavid du Colombier 	sp->ep = buf + len;
5730319257bSDavid du Colombier 	sp->err = nil;
5740319257bSDavid du Colombier 	sp->errbuf[0] = '\0';
575*82f6abeeSDavid du Colombier 	sp->rcode = Rok;
5764f8f669cSDavid du Colombier 
5774f8f669cSDavid du Colombier 	memset(m, 0, sizeof *m);
5783e12c5d1SDavid du Colombier 	USHORT(m->id);
5793e12c5d1SDavid du Colombier 	USHORT(m->flags);
5803e12c5d1SDavid du Colombier 	USHORT(m->qdcount);
5813e12c5d1SDavid du Colombier 	USHORT(m->ancount);
5823e12c5d1SDavid du Colombier 	USHORT(m->nscount);
5833e12c5d1SDavid du Colombier 	USHORT(m->arcount);
5844f8f669cSDavid du Colombier 
5854f8f669cSDavid du Colombier 	m->qd = rrloop(sp, "questions",	m->qdcount, 1);
5864f8f669cSDavid du Colombier 	m->an = rrloop(sp, "answers",	m->ancount, 0);
5874f8f669cSDavid du Colombier 	m->ns = rrloop(sp, "nameservers",m->nscount, 0);
5880319257bSDavid du Colombier 	if (sp->stop)
5890319257bSDavid du Colombier 		sp->err = nil;
5900319257bSDavid du Colombier 	if (sp->err)
5910319257bSDavid du Colombier 		err = strdup(sp->err);		/* live with bad ar's */
5924f8f669cSDavid du Colombier 	m->ar = rrloop(sp, "hints",	m->arcount, 0);
5930319257bSDavid du Colombier 	if (sp->trunc)
594a41547ffSDavid du Colombier 		m->flags |= Ftrunc;
5950319257bSDavid du Colombier 	if (sp->stop)
596d9924332SDavid du Colombier 		sp->rcode = Rok;
5974f8f669cSDavid du Colombier 	if (codep)
5980319257bSDavid du Colombier 		*codep = sp->rcode;
5997dd7cddfSDavid du Colombier 	return err;
6003e12c5d1SDavid du Colombier }
601