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