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 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 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 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 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* 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* 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* 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* 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 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* 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 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* 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* 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* 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* 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