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