xref: /plan9/sys/src/cmd/ndb/convM2DNS.c (revision 98813beef1db23409911a4b339e6bb9c03d0a5c0)
1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include "dns.h"
5 
6 typedef struct Scan	Scan;
7 struct Scan
8 {
9 	uchar	*base;		/* input buffer */
10 	uchar	*p;		/* current position */
11 	uchar	*ep;		/* byte after the end */
12 
13 	char	*err;
14 	char	errbuf[256];	/* hold a formatted error sometimes */
15 	int	rcode;		/* outgoing response codes (reply flags) */
16 	int	stop;		/* flag: stop processing */
17 	int	trunc;		/* flag: input truncated */
18 };
19 
20 static int
errneg(RR * rp,Scan * sp,int actual)21 errneg(RR *rp, Scan *sp, int actual)
22 {
23 	snprint(sp->errbuf, sizeof sp->errbuf, "negative len %d: %R",
24 		actual, rp);
25 	sp->err = sp->errbuf;
26 	return 0;
27 }
28 
29 static int
errtoolong(RR * rp,Scan * sp,int remain,int need,char * where)30 errtoolong(RR *rp, Scan *sp, int remain, int need, char *where)
31 {
32 	char *p, *ep;
33 	char ptype[64];
34 
35 	p =  sp->errbuf;
36 	ep = sp->errbuf + sizeof sp->errbuf - 1;
37 	if (where)
38 		p = seprint(p, ep, "%s: ", where);
39 	if (rp)
40 		p = seprint(p, ep, "type %s RR: ",
41 			rrname(rp->type, ptype, sizeof ptype));
42 	p = seprint(p, ep, "%d bytes needed; %d remain", need, remain);
43 	if (rp)
44 		p = seprint(p, ep, ": %R", rp);
45 	/*
46 	 * hack to cope with servers that don't set Ftrunc when they should:
47 	 * if the (udp) packet is full-sized, if must be truncated because
48 	 * it is incomplete.  otherwise, it's just garbled.
49 	 */
50 	if (sp->ep - sp->base >= Maxpayload) {
51 		sp->trunc = 1;
52 		seprint(p, ep, " (truncated)");
53 	}
54 	if (debug && rp)
55 		dnslog("malformed rr: %R", rp);
56 	sp->err = sp->errbuf;
57 	return 0;
58 }
59 
60 /*
61  *  get a ushort/ulong
62  */
63 static ushort
gchar(RR * rp,Scan * sp)64 gchar(RR *rp, Scan *sp)
65 {
66 	ushort x;
67 
68 	if(sp->err)
69 		return 0;
70 	if(sp->ep - sp->p < 1)
71 		return errtoolong(rp, sp, sp->ep - sp->p, 1, "gchar");
72 	x = sp->p[0];
73 	sp->p += 1;
74 	return x;
75 }
76 static ushort
gshort(RR * rp,Scan * sp)77 gshort(RR *rp, Scan *sp)
78 {
79 	ushort x;
80 
81 	if(sp->err)
82 		return 0;
83 	if(sp->ep - sp->p < 2)
84 		return errtoolong(rp, sp, sp->ep - sp->p, 2, "gshort");
85 	x = sp->p[0]<<8 | sp->p[1];
86 	sp->p += 2;
87 	return x;
88 }
89 static ulong
glong(RR * rp,Scan * sp)90 glong(RR *rp, Scan *sp)
91 {
92 	ulong x;
93 
94 	if(sp->err)
95 		return 0;
96 	if(sp->ep - sp->p < 4)
97 		return errtoolong(rp, sp, sp->ep - sp->p, 4, "glong");
98 	x = sp->p[0]<<24 | sp->p[1]<<16 | sp->p[2]<<8 | sp->p[3];
99 	sp->p += 4;
100 	return x;
101 }
102 
103 /*
104  *  get an ip address
105  */
106 static DN*
gv4addr(RR * rp,Scan * sp)107 gv4addr(RR *rp, Scan *sp)
108 {
109 	char addr[32];
110 
111 	if(sp->err)
112 		return 0;
113 	if(sp->ep - sp->p < 4)
114 		return (DN*)errtoolong(rp, sp, sp->ep - sp->p, 4, "gv4addr");
115 	snprint(addr, sizeof addr, "%V", sp->p);
116 	sp->p += 4;
117 
118 	return dnlookup(addr, Cin, 1);
119 }
120 static DN*
gv6addr(RR * rp,Scan * sp)121 gv6addr(RR *rp, Scan *sp)
122 {
123 	char addr[64];
124 
125 	if(sp->err)
126 		return 0;
127 	if(sp->ep - sp->p < IPaddrlen)
128 		return (DN*)errtoolong(rp, sp, sp->ep - sp->p, IPaddrlen,
129 			"gv6addr");
130 	snprint(addr, sizeof addr, "%I", sp->p);
131 	sp->p += IPaddrlen;
132 
133 	return dnlookup(addr, Cin, 1);
134 }
135 
136 /*
137  *  get a string.  make it an internal symbol.
138  */
139 static DN*
gsym(RR * rp,Scan * sp)140 gsym(RR *rp, Scan *sp)
141 {
142 	int n;
143 	char sym[Strlen+1];
144 
145 	if(sp->err)
146 		return 0;
147 	n = 0;
148 	if (sp->p < sp->ep)
149 		n = *(sp->p++);
150 	if(sp->ep - sp->p < n)
151 		return (DN*)errtoolong(rp, sp, sp->ep - sp->p, n, "gsym");
152 
153 	if(n > Strlen){
154 		sp->err = "illegal string (symbol)";
155 		return 0;
156 	}
157 	strncpy(sym, (char*)sp->p, n);
158 	sym[n] = 0;
159 	if (strlen(sym) != n)
160 		sp->err = "symbol shorter than declared length";
161 	sp->p += n;
162 
163 	return dnlookup(sym, Csym, 1);
164 }
165 
166 /*
167  *  get a string.  don't make it an internal symbol.
168  */
169 static Txt*
gstr(RR * rp,Scan * sp)170 gstr(RR *rp, Scan *sp)
171 {
172 	int n;
173 	char sym[Strlen+1];
174 	Txt *t;
175 
176 	if(sp->err)
177 		return 0;
178 	n = 0;
179 	if (sp->p < sp->ep)
180 		n = *(sp->p++);
181 	if(sp->ep - sp->p < n)
182 		return (Txt*)errtoolong(rp, sp, sp->ep - sp->p, n, "gstr");
183 
184 	if(n > Strlen){
185 		sp->err = "illegal string";
186 		return 0;
187 	}
188 	strncpy(sym, (char*)sp->p, n);
189 	sym[n] = 0;
190 	if (strlen(sym) != n)
191 		sp->err = "string shorter than declared length";
192 	sp->p += n;
193 
194 	t = emalloc(sizeof(*t));
195 	t->next = nil;
196 	t->p = estrdup(sym);
197 	return t;
198 }
199 
200 /*
201  *  get a sequence of bytes
202  */
203 static int
gbytes(RR * rp,Scan * sp,uchar ** p,int n)204 gbytes(RR *rp, Scan *sp, uchar **p, int n)
205 {
206 	*p = nil;			/* i think this is a good idea */
207 	if(sp->err)
208 		return 0;
209 	if(n < 0)
210 		return errneg(rp, sp, n);
211 	if(sp->ep - sp->p < n)
212 		return errtoolong(rp, sp, sp->ep - sp->p, n, "gbytes");
213 	*p = emalloc(n);
214 	memmove(*p, sp->p, n);
215 	sp->p += n;
216 
217 	return n;
218 }
219 
220 /*
221  *  get a domain name.  'to' must point to a buffer at least Domlen+1 long.
222  */
223 static char*
gname(char * to,RR * rp,Scan * sp)224 gname(char *to, RR *rp, Scan *sp)
225 {
226 	int len, off, pointer, n;
227 	char *tostart, *toend;
228 	uchar *p;
229 
230 	tostart = to;
231 	if(sp->err || sp->stop)
232 		goto err;
233 	pointer = 0;
234 	p = sp->p;
235 	if (p == nil) {
236 		dnslog("gname: %R: nil sp->p", rp);
237 		goto err;
238 	}
239 	toend = to + Domlen;
240 	for(len = 0; *p && p < sp->ep; len += (pointer? 0: n+1)) {
241 		n = 0;
242 		switch (*p & 0300) {
243 		case 0:			/* normal label */
244 			if (p < sp->ep)
245 				n = *p++ & 077;		/* pick up length */
246 			if(len + n < Domlen - 1){
247 				if(n > toend - to){
248 					errtoolong(rp, sp, toend - to, n,
249 						"name too long");
250 					goto err;
251 				}
252 				memmove(to, p, n);
253 				to += n;
254 			}
255 			p += n;
256 			if(*p){
257 				if(to >= toend){
258 					errtoolong(rp, sp, toend - to, 2,
259 				     "more name components but no bytes left");
260 					goto err;
261 				}
262 				*to++ = '.';
263 			}
264 			break;
265 		case 0100:		/* edns extended label type, rfc 6891 */
266 			/*
267 			 * treat it like an EOF for now; it seems to be at
268 			 * the end of a long tcp reply.
269 			 */
270 			dnslog("edns label; first byte 0%o = '%c'", *p, *p);
271 			sp->stop = 1;
272 			goto err;
273 		case 0200:		/* reserved */
274 			sp->err = "reserved-use label present";
275 			goto err;
276 		case 0300:		/* pointer to other spot in message */
277 			if(pointer++ > 10){
278 				sp->err = "pointer loop";
279 				goto err;
280 			}
281 			off = (p[0] & 077)<<8 | p[1];
282 			p = sp->base + off;
283 			if(p >= sp->ep){
284 				sp->err = "bad pointer";
285 				goto err;
286 			}
287 			n = 0;
288 			break;
289 		}
290 	}
291 	*to = 0;
292 	if(pointer)
293 		sp->p += len + 2;	/* + 2 for pointer */
294 	else
295 		sp->p += len + 1;	/* + 1 for the null domain */
296 	return tostart;
297 err:
298 	*tostart = 0;
299 	return tostart;
300 }
301 
302 /*
303  * ms windows 2000 seems to get the bytes backward in the type field
304  * of ptr records, so return a format error as feedback.
305  */
306 static ushort
mstypehack(Scan * sp,ushort type,char * where)307 mstypehack(Scan *sp, ushort type, char *where)
308 {
309 	if ((uchar)type == 0 && (type>>8) != 0) {
310 		USED(where);
311 //		dnslog("%s: byte-swapped type field in ptr rr from win2k",
312 //			where);
313 		if (sp->rcode == Rok)
314 			sp->rcode = Rformat;
315 		type >>= 8;
316 	}
317 	return type;
318 }
319 
320 #define NAME(x)		gname(x, rp, sp)
321 #define SYMBOL(x)	((x) = gsym(rp, sp))
322 #define STRING(x)	((x) = gstr(rp, sp))
323 #define USHORT(x)	((x) = gshort(rp, sp))
324 #define ULONG(x)	((x) = glong(rp, sp))
325 #define UCHAR(x)	((x) = gchar(rp, sp))
326 #define V4ADDR(x)	((x) = gv4addr(rp, sp))
327 #define V6ADDR(x)	((x) = gv6addr(rp, sp))
328 #define BYTES(x, y)	((y) = gbytes(rp, sp, &(x), len - (sp->p - data)))
329 
330 /*
331  *  convert the next RR from a message
332  */
333 static RR*
convM2RR(Scan * sp,char * what)334 convM2RR(Scan *sp, char *what)
335 {
336 	int type, class, len, left;
337 	char *dn;
338 	char dname[Domlen+1];
339 	uchar *data;
340 	RR *rp;
341 	Txt *t, **l;
342 
343 retry:
344 	rp = nil;
345 	NAME(dname);
346 	USHORT(type);
347 	USHORT(class);
348 
349 	type = mstypehack(sp, type, "convM2RR");
350 	rp = rralloc(type);
351 	rp->owner = dnlookup(dname, class, 1);
352 	rp->type = type;
353 
354 	ULONG(rp->ttl);
355 	rp->ttl += now;
356 	USHORT(len);			/* length of data following */
357 	data = sp->p;
358 	assert(data != nil);
359 	left = sp->ep - sp->p;
360 
361 	/*
362 	 * ms windows generates a lot of badly-formatted hints.
363 	 * hints are only advisory, so don't log complaints about them.
364 	 * it also generates answers in which p overshoots ep by exactly
365 	 * one byte; this seems to be harmless, so don't log them either.
366 	 */
367 	if (len > left &&
368 	   !(strcmp(what, "hints") == 0 ||
369 	     sp->p == sp->ep + 1 && strcmp(what, "answers") == 0))
370 		errtoolong(rp, sp, left, len, "convM2RR");
371 	if(sp->err || sp->rcode || sp->stop){
372 		rrfree(rp);
373 		return nil;
374 	}
375 	/* even if we don't log an error message, truncate length to fit data */
376 	if (len > left)
377 		len = left;
378 
379 	switch(type){
380 	default:
381 		/* unknown type, just ignore it */
382 		sp->p = data + len;
383 		rrfree(rp);
384 		goto retry;
385 	case Thinfo:
386 		SYMBOL(rp->cpu);
387 		SYMBOL(rp->os);
388 		break;
389 	case Tcname:
390 	case Tmb:
391 	case Tmd:
392 	case Tmf:
393 	case Tns:
394 		rp->host = dnlookup(NAME(dname), Cin, 1);
395 		break;
396 	case Tmg:
397 	case Tmr:
398 		rp->mb  = dnlookup(NAME(dname), Cin, 1);
399 		break;
400 	case Tminfo:
401 		rp->rmb = dnlookup(NAME(dname), Cin, 1);
402 		rp->mb  = dnlookup(NAME(dname), Cin, 1);
403 		break;
404 	case Tmx:
405 		USHORT(rp->pref);
406 		dn = NAME(dname);
407 		rp->host = dnlookup(dn, Cin, 1);
408 		if(strchr((char *)rp->host, '\n') != nil) {
409 			dnslog("newline in mx text for %s", dn);
410 			sp->trunc = 1;		/* try again via tcp */
411 		}
412 		break;
413 	case Ta:
414 		V4ADDR(rp->ip);
415 		break;
416 	case Taaaa:
417 		V6ADDR(rp->ip);
418 		break;
419 	case Tptr:
420 		rp->ptr = dnlookup(NAME(dname), Cin, 1);
421 		break;
422 	case Tsoa:
423 		rp->host = dnlookup(NAME(dname), Cin, 1);
424 		rp->rmb  = dnlookup(NAME(dname), Cin, 1);
425 		ULONG(rp->soa->serial);
426 		ULONG(rp->soa->refresh);
427 		ULONG(rp->soa->retry);
428 		ULONG(rp->soa->expire);
429 		ULONG(rp->soa->minttl);
430 		break;
431 	case Tsrv:
432 		USHORT(rp->srv->pri);
433 		USHORT(rp->srv->weight);
434 		USHORT(rp->port);
435 		/*
436 		 * rfc2782 sez no name compression but to be
437 		 * backward-compatible with rfc2052, we try to expand the name.
438 		 * if the length is under 64 bytes, either interpretation is
439 		 * fine; if it's longer, we'll assume it's compressed,
440 		 * as recommended by rfc3597.
441 		 */
442 		rp->host = dnlookup(NAME(dname), Cin, 1);
443 		break;
444 	case Ttxt:
445 		l = &rp->txt;
446 		*l = nil;
447 		while(sp->p - data < len){
448 			STRING(t);
449 			*l = t;
450 			l = &t->next;
451 		}
452 		break;
453 	case Tnull:
454 		BYTES(rp->null->data, rp->null->dlen);
455 		break;
456 	case Trp:
457 		rp->rmb = dnlookup(NAME(dname), Cin, 1);
458 		rp->rp  = dnlookup(NAME(dname), Cin, 1);
459 		break;
460 	case Tkey:
461 		USHORT(rp->key->flags);
462 		UCHAR(rp->key->proto);
463 		UCHAR(rp->key->alg);
464 		BYTES(rp->key->data, rp->key->dlen);
465 		break;
466 	case Tsig:
467 		USHORT(rp->sig->type);
468 		UCHAR(rp->sig->alg);
469 		UCHAR(rp->sig->labels);
470 		ULONG(rp->sig->ttl);
471 		ULONG(rp->sig->exp);
472 		ULONG(rp->sig->incep);
473 		USHORT(rp->sig->tag);
474 		rp->sig->signer = dnlookup(NAME(dname), Cin, 1);
475 		BYTES(rp->sig->data, rp->sig->dlen);
476 		break;
477 	case Tcert:
478 		USHORT(rp->cert->type);
479 		USHORT(rp->cert->tag);
480 		UCHAR(rp->cert->alg);
481 		BYTES(rp->cert->data, rp->cert->dlen);
482 		break;
483 	}
484 	if(sp->p - data != len) {
485 		char ptype[64];
486 
487 		/*
488 		 * ms windows 2000 generates cname queries for reverse lookups
489 		 * with this particular error.  don't bother logging it.
490 		 *
491 		 * server: input error: bad cname RR len (actual 2 != len 0):
492 		 * 235.9.104.135.in-addr.arpa cname
493 		 *	235.9.104.135.in-addr.arpa from 135.104.9.235
494 		 */
495 		if (type == Tcname && sp->p - data == 2 && len == 0)
496 			return rp;
497 		if (len > sp->p - data){
498 			dnslog("bad %s RR len (%d bytes nominal, %lud actual): %R",
499 				rrname(type, ptype, sizeof ptype), len,
500 				sp->p - data, rp);
501 			rrfree(rp);
502 			rp = nil;
503 		}
504 	}
505 	// if(rp) dnslog("convM2RR: got %R", rp);
506 	return rp;
507 }
508 
509 /*
510  *  convert the next question from a message
511  */
512 static RR*
convM2Q(Scan * sp)513 convM2Q(Scan *sp)
514 {
515 	char dname[Domlen+1];
516 	int type, class;
517 	RR *rp;
518 
519 	rp = nil;
520 	NAME(dname);
521 	USHORT(type);
522 	USHORT(class);
523 	if(sp->err || sp->rcode || sp->stop)
524 		return nil;
525 
526 	type = mstypehack(sp, type, "convM2Q");
527 	rp = rralloc(type);
528 	rp->owner = dnlookup(dname, class, 1);
529 
530 	return rp;
531 }
532 
533 static RR*
rrloop(Scan * sp,char * what,int count,int quest)534 rrloop(Scan *sp, char *what, int count, int quest)
535 {
536 	int i;
537 	RR *first, *rp, **l;
538 
539 	if(sp->err || sp->rcode || sp->stop)
540 		return nil;
541 	l = &first;
542 	first = nil;
543 	for(i = 0; i < count; i++){
544 		rp = quest? convM2Q(sp): convM2RR(sp, what);
545 		if(rp == nil)
546 			break;
547 		setmalloctag(rp, getcallerpc(&sp));
548 		/*
549 		 * it might be better to ignore the bad rr, possibly break out,
550 		 * but return the previous rrs, if any.  that way our callers
551 		 * would know that they had got a response, however ill-formed.
552 		 */
553 		if(sp->err || sp->rcode || sp->stop){
554 			rrfree(rp);
555 			break;
556 		}
557 		*l = rp;
558 		l = &rp->next;
559 	}
560 //	if(first)
561 //		setmalloctag(first, getcallerpc(&sp));
562 	return first;
563 }
564 
565 /*
566  *  convert the next DNS from a message stream.
567  *  if there are formatting errors or the like during parsing of the message,
568  *  set *codep to the outgoing response code (e.g., Rformat), which will
569  *  abort processing and reply immediately with the outgoing response code.
570  *
571  *  ideally would note if len == Maxpayload && query was via UDP, for errtoolong.
572  */
573 char*
convM2DNS(uchar * buf,int len,DNSmsg * m,int * codep)574 convM2DNS(uchar *buf, int len, DNSmsg *m, int *codep)
575 {
576 	char *err = nil;
577 	RR *rp = nil;
578 	Scan scan;
579 	Scan *sp;
580 
581 	assert(len >= 0);
582 	assert(buf != nil);
583 	sp = &scan;
584 	memset(sp, 0, sizeof *sp);
585 	sp->base = sp->p = buf;
586 	sp->ep = buf + len;
587 	sp->err = nil;
588 	sp->errbuf[0] = '\0';
589 	sp->rcode = Rok;
590 
591 	memset(m, 0, sizeof *m);
592 	USHORT(m->id);
593 	USHORT(m->flags);
594 	USHORT(m->qdcount);
595 	USHORT(m->ancount);
596 	USHORT(m->nscount);
597 	USHORT(m->arcount);
598 
599 	m->qd = rrloop(sp, "questions",	m->qdcount, 1);
600 	m->an = rrloop(sp, "answers",	m->ancount, 0);
601 	m->ns = rrloop(sp, "nameservers",m->nscount, 0);
602 	if (sp->stop)
603 		sp->err = nil;
604 	if (sp->err)
605 		err = strdup(sp->err);		/* live with bad ar's */
606 	m->ar = rrloop(sp, "hints",	m->arcount, 0);
607 	if (sp->trunc)
608 		m->flags |= Ftrunc;
609 	if (sp->stop)
610 		sp->rcode = Rok;
611 	if (codep)
612 		*codep = sp->rcode;
613 	return err;
614 }
615