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