]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ndb/convM2DNS.c
/sys/src/cmd/ndb/dns.h:
[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(sp->ep - p <= n){
241                                 sp->err = "bad name length";
242                                 goto err;
243                         }
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 ushort
305 mstypehack(Scan *sp, ushort type, char *where)
306 {
307         if ((uchar)type == 0 && (type>>8) != 0) {
308                 USED(where);
309 //              dnslog("%s: byte-swapped type field in ptr rr from win2k",
310 //                      where);
311                 if (sp->rcode == Rok)
312                         sp->rcode = Rformat;
313                 type >>= 8;
314         }
315         return type;
316 }
317
318 #define NAME(x)         gname(x, rp, sp)
319 #define SYMBOL(x)       ((x) = gsym(rp, sp))
320 #define STRING(x)       ((x) = gstr(rp, sp))
321 #define USHORT(x)       ((x) = gshort(rp, sp))
322 #define ULONG(x)        ((x) = glong(rp, sp))
323 #define UCHAR(x)        ((x) = gchar(rp, sp))
324 #define V4ADDR(x)       ((x) = gv4addr(rp, sp))
325 #define V6ADDR(x)       ((x) = gv6addr(rp, sp))
326 #define BYTES(x, y)     ((y) = gbytes(rp, sp, &(x), len - (sp->p - data)))
327
328 /*
329  *  convert the next RR from a message
330  */
331 static RR*
332 convM2RR(Scan *sp, char *what)
333 {
334         int type, class, len, left;
335         char dname[Domlen+1];
336         uchar *data;
337         RR *rp;
338         Txt *t, **l;
339
340 retry:
341         rp = nil;
342         NAME(dname);
343         USHORT(type);
344         USHORT(class);
345
346         type = mstypehack(sp, type, "convM2RR");
347         rp = rralloc(type);
348         rp->owner = dnlookup(dname, class, 1);
349         rp->type = type;
350
351         ULONG(rp->ttl);
352         USHORT(len);                    /* length of data following */
353         data = sp->p;
354         assert(data != nil);
355         left = sp->ep - sp->p;
356
357         /*
358          * ms windows generates a lot of badly-formatted hints.
359          * hints are only advisory, so don't log complaints about them.
360          * it also generates answers in which p overshoots ep by exactly
361          * one byte; this seems to be harmless, so don't log them either.
362          */
363         if (len > left &&
364            !(strcmp(what, "hints") == 0 ||
365              sp->p == sp->ep + 1 && strcmp(what, "answers") == 0))
366                 errtoolong(rp, sp, left, len, "convM2RR");
367         if(sp->err || sp->rcode || sp->stop){
368                 rrfree(rp);
369                 return nil;
370         }
371         /* even if we don't log an error message, truncate length to fit data */
372         if (len > left)
373                 len = left;
374
375         switch(type){
376         default:
377                 /* unknown type, just ignore it */
378                 sp->p = data + len;
379                 rrfree(rp);
380                 goto retry;
381         case Thinfo:
382                 SYMBOL(rp->cpu);
383                 SYMBOL(rp->os);
384                 break;
385         case Tcname:
386         case Tmb:
387         case Tmd:
388         case Tmf:
389         case Tns:
390                 rp->host = dnlookup(NAME(dname), Cin, 1);
391                 break;
392         case Tmg:
393         case Tmr:
394                 rp->mb  = dnlookup(NAME(dname), Cin, 1);
395                 break;
396         case Tminfo:
397                 rp->rmb = dnlookup(NAME(dname), Cin, 1);
398                 rp->mb  = dnlookup(NAME(dname), Cin, 1);
399                 break;
400         case Tmx:
401                 USHORT(rp->pref);
402                 rp->host = dnlookup(NAME(dname), Cin, 1);
403                 break;
404         case Ta:
405                 V4ADDR(rp->ip);
406                 break;
407         case Taaaa:
408                 V6ADDR(rp->ip);
409                 break;
410         case Tptr:
411                 rp->ptr = dnlookup(NAME(dname), Cin, 1);
412                 break;
413         case Tsoa:
414                 rp->host = dnlookup(NAME(dname), Cin, 1);
415                 rp->rmb  = dnlookup(NAME(dname), Cin, 1);
416                 ULONG(rp->soa->serial);
417                 ULONG(rp->soa->refresh);
418                 ULONG(rp->soa->retry);
419                 ULONG(rp->soa->expire);
420                 ULONG(rp->soa->minttl);
421                 break;
422         case Tsrv:
423                 USHORT(rp->srv->pri);
424                 USHORT(rp->srv->weight);
425                 USHORT(rp->port);
426                 /*
427                  * rfc2782 sez no name compression but to be
428                  * backward-compatible with rfc2052, we try to expand the name. 
429                  * if the length is under 64 bytes, either interpretation is
430                  * fine; if it's longer, we'll assume it's compressed,
431                  * as recommended by rfc3597.
432                  */
433                 rp->host = dnlookup(NAME(dname), Cin, 1);
434                 break;
435         case Ttxt:
436                 l = &rp->txt;
437                 *l = nil;
438                 while(sp->p - data < len){
439                         STRING(t);
440                         *l = t;
441                         l = &t->next;
442                 }
443                 break;
444         case Tnull:
445                 BYTES(rp->null->data, rp->null->dlen);
446                 break;
447         case Trp:
448                 rp->rmb = dnlookup(NAME(dname), Cin, 1);
449                 rp->rp  = dnlookup(NAME(dname), Cin, 1);
450                 break;
451         case Tdnskey:
452         case Tkey:
453                 USHORT(rp->key->flags);
454                 UCHAR(rp->key->proto);
455                 UCHAR(rp->key->alg);
456                 BYTES(rp->key->data, rp->key->dlen);
457                 break;
458         case Tsig:
459                 USHORT(rp->sig->type);
460                 UCHAR(rp->sig->alg);
461                 UCHAR(rp->sig->labels);
462                 ULONG(rp->sig->ttl);
463                 ULONG(rp->sig->exp);
464                 ULONG(rp->sig->incep);
465                 USHORT(rp->sig->tag);
466                 rp->sig->signer = dnlookup(NAME(dname), Cin, 1);
467                 BYTES(rp->sig->data, rp->sig->dlen);
468                 break;
469         case Tcert:
470                 USHORT(rp->cert->type);
471                 USHORT(rp->cert->tag);
472                 UCHAR(rp->cert->alg);
473                 BYTES(rp->cert->data, rp->cert->dlen);
474                 break;
475         case Tcaa:
476                 UCHAR(rp->caa->flags);
477                 SYMBOL(rp->caa->tag);
478                 BYTES(rp->caa->data, rp->caa->dlen);
479                 break;
480         }
481         if(sp->p - data != len) {
482                 char ptype[64];
483
484                 /*
485                  * ms windows 2000 generates cname queries for reverse lookups
486                  * with this particular error.  don't bother logging it.
487                  *
488                  * server: input error: bad cname RR len (actual 2 != len 0):
489                  * 235.9.104.135.in-addr.arpa cname
490                  *      235.9.104.135.in-addr.arpa from 135.104.9.235
491                  */
492                 if (type == Tcname && sp->p - data == 2 && len == 0)
493                         return rp;
494                 if (len > sp->p - data){
495                         dnslog("bad %s RR len (%d bytes nominal, %zud actual): %R",
496                                 rrname(type, ptype, sizeof ptype), len,
497                                 sp->p - data, rp);
498                         rrfree(rp);
499                         rp = nil;
500                 }
501         }
502         // if(rp) dnslog("convM2RR: got %R", rp);
503         return rp;
504 }
505
506 /*
507  *  convert the next question from a message
508  */
509 static RR*
510 convM2Q(Scan *sp)
511 {
512         char dname[Domlen+1];
513         int type, class;
514         RR *rp;
515
516         rp = nil;
517         NAME(dname);
518         USHORT(type);
519         USHORT(class);
520         if(sp->err || sp->rcode || sp->stop)
521                 return nil;
522
523         type = mstypehack(sp, type, "convM2Q");
524         rp = rralloc(type);
525         rp->owner = dnlookup(dname, class, 1);
526
527         return rp;
528 }
529
530 static RR*
531 rrloop(Scan *sp, char *what, int count, int quest)
532 {
533         int i;
534         RR *first, *rp, **l;
535
536         if(sp->err || sp->rcode || sp->stop)
537                 return nil;
538         l = &first;
539         first = nil;
540         for(i = 0; i < count; i++){
541                 rp = quest? convM2Q(sp): convM2RR(sp, what);
542                 if(rp == nil)
543                         break;
544                 setmalloctag(rp, getcallerpc(&sp));
545                 /*
546                  * it might be better to ignore the bad rr, possibly break out,
547                  * but return the previous rrs, if any.  that way our callers
548                  * would know that they had got a response, however ill-formed.
549                  */
550                 if(sp->err || sp->rcode || sp->stop){
551                         rrfree(rp);
552                         break;
553                 }
554                 *l = rp;
555                 l = &rp->next;
556         }
557 //      if(first)
558 //              setmalloctag(first, getcallerpc(&sp));
559         return first;
560 }
561
562 /*
563  *  convert the next DNS from a message stream.
564  *  if there are formatting errors or the like during parsing of the message,
565  *  set *codep to the outgoing response code (e.g., Rformat), which will
566  *  abort processing and reply immediately with the outgoing response code.
567  */
568 char*
569 convM2DNS(uchar *buf, int len, DNSmsg *m, int *codep)
570 {
571         char *err = nil;
572         RR *rp = nil;
573         Scan scan;
574         Scan *sp;
575
576         assert(len >= 0);
577         assert(buf != nil);
578         sp = &scan;
579         memset(sp, 0, sizeof *sp);
580         sp->base = sp->p = buf;
581         sp->ep = buf + len;
582         sp->err = nil;
583         sp->errbuf[0] = '\0';
584         sp->rcode = Rok;
585
586         memset(m, 0, sizeof *m);
587         USHORT(m->id);
588         USHORT(m->flags);
589         USHORT(m->qdcount);
590         USHORT(m->ancount);
591         USHORT(m->nscount);
592         USHORT(m->arcount);
593
594         m->qd = rrloop(sp, "questions", m->qdcount, 1);
595         m->an = rrloop(sp, "answers",   m->ancount, 0);
596         m->ns = rrloop(sp, "nameservers",m->nscount, 0);
597         if (sp->stop)
598                 sp->err = nil;
599         if (sp->err)
600                 err = strdup(sp->err);          /* live with bad ar's */
601         m->ar = rrloop(sp, "hints",     m->arcount, 0);
602         if (sp->trunc)
603                 m->flags |= Ftrunc;
604         if (sp->stop)
605                 sp->rcode = Rok;
606         if (codep)
607                 *codep = sp->rcode;
608         return err;
609 }