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