Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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
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*
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*
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*
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*
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
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*
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
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*
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*
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*
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*
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
}