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