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