Annotation of embedaddon/trafshow/domain_resolver.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (c) 1999-2004 Rinet Corp., Novosibirsk, Russia
! 3: *
! 4: * Redistribution and use in source forms, with and without modification,
! 5: * are permitted provided that this entire comment appears intact.
! 6: *
! 7: * THIS SOURCE CODE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES OF ANY KIND.
! 8: */
! 9:
! 10: #ifdef HAVE_CONFIG_H
! 11: #include <config.h>
! 12: #endif
! 13:
! 14: #include <sys/types.h>
! 15: #include <sys/param.h>
! 16: #include <sys/socket.h>
! 17: #include <netinet/in.h>
! 18: #include <arpa/inet.h>
! 19: #include <arpa/nameser.h>
! 20: #include <stdio.h>
! 21: #include <stdlib.h>
! 22: #include <string.h>
! 23: #include <errno.h>
! 24: #ifdef HAVE_PATHS_H
! 25: #include <paths.h>
! 26: #endif
! 27: #ifdef HAVE_RESOLV_H
! 28: #include <resolv.h>
! 29: #endif
! 30:
! 31: #include "domain_resolver.h"
! 32: #include "session.h"
! 33: #include "util.h"
! 34: #include "trafshow.h" /* just for dprintf() */
! 35:
! 36:
! 37: #ifndef _PATH_RESCONF
! 38: #define _PATH_RESCONF "/etc/resolv.conf"
! 39: #endif
! 40: #ifndef NAMESERVER_TOKEN
! 41: #define NAMESERVER_TOKEN "nameserver"
! 42: #endif
! 43: #ifndef NAMESERVER_PORT
! 44: #define NAMESERVER_PORT 53 /* nameserver port */
! 45: #endif
! 46: #ifndef PACKETSZ
! 47: #define PACKETSZ 512 /* maximum packet size */
! 48: #endif
! 49:
! 50: static struct sockaddr_in *primary = 0, *secondary = 0;
! 51:
! 52: /* currently we handle only following types of nameserver requests */
! 53: typedef enum {
! 54: IpAddress, /* get A resource records */
! 55: DomainName, /* get PTR resource records */
! 56: MailExchanger /* get MX resource records */
! 57: } DomainType;
! 58:
! 59: #define MAX_EXPAND_TRIES 3 /* to resolve MX pointing to CNAME */
! 60:
! 61: typedef struct domain_transact_ent {
! 62: /* caller supplied data */
! 63: char *name; /* original requested name (or ip address) */
! 64: SESSION *sd;
! 65: void (*callback)(SESSION *sd, DOMAIN_DATA *dd);
! 66:
! 67: /* request */
! 68: u_short reqid; /* request id */
! 69: u_short expand; /* expand MX pointing to CNAME */
! 70: int retry; /* retry counter */
! 71: char *domain; /* actual domain name requested */
! 72: DomainType type; /* type of request */
! 73:
! 74: /* response */
! 75: int rcode; /* nameserver reply code */
! 76: DOMAIN_DATA *data; /* list of answered data */
! 77:
! 78: struct domain_transact_ent *next;
! 79: } DOMAIN_TRANSACT;
! 80:
! 81: #define TRANSACT(sd) ((DOMAIN_TRANSACT *)session_cookie(sd))
! 82:
! 83: static DOMAIN_TRANSACT *first_transact = 0;
! 84: static DOMAIN_TRANSACT *new_transact();
! 85: static DOMAIN_TRANSACT *find_transact(u_short reqid);
! 86: static void free_transact(DOMAIN_TRANSACT *dt);
! 87: static DOMAIN_TRANSACT *parse_packet(const unsigned char *data, int len);
! 88:
! 89: static void nameserver_error(SESSION *sd, int error);
! 90: static void nameserver_close(SESSION *sd);
! 91: static void nameserver_reply(SESSION *sd, const unsigned char *data, int len);
! 92: static int nameserver_request(const char *domain, DomainType type,
! 93: SESSION *org,
! 94: void (*notify)(SESSION *sd, DOMAIN_DATA *dd));
! 95: static int nameserver_send(SESSION *sd);
! 96: static void discard_request(void *arg); /* (DOMAIN_TRANSACT *) */
! 97: static u_short unique_reqid();
! 98:
! 99: #ifdef HAVE_REPORT_FUNC
! 100: static const char *rcode2text[6] = {
! 101: "No error", /* 0 - NOERROR */
! 102: "Format error", /* 1 - FORMERR */
! 103: "Server failure", /* 2 - SERVFAIL */
! 104: "Non existend domain", /* 3 - NXDOAMIN */
! 105: "Not implemented", /* 4 - NOTIMP */
! 106: "Query refused" /* 5 - REFUSED */
! 107: };
! 108: #endif
! 109:
! 110: #ifdef DEBUG
! 111: void
! 112: dump_reply(dt)
! 113: DOMAIN_TRANSACT *dt;
! 114: {
! 115: DOMAIN_DATA *dd;
! 116: char ipaddr[50];
! 117:
! 118: if (!dt) {
! 119: printf("REPLY: domain transaction is null\n");
! 120: return;
! 121: }
! 122: printf("REPLY: reqid=%d retry=%d domain=\"%s\" type=%d rcode=\"%s\"\n",
! 123: dt->reqid, dt->retry, dt->domain, dt->type, rcode2text[dt->rcode]);
! 124: for (dd = dt->data; dd; dd = dd->next) {
! 125: printf("REPLY:\tttl=%u\tpref=%u\tname=\"%s\"\taddr=%s\n",
! 126: dd->ttl, dd->pref, dd->name, intoa(ipaddr, dd->addr));
! 127:
! 128: }
! 129: }
! 130: #endif
! 131:
! 132: int
! 133: domain_resolver_init()
! 134: {
! 135: FILE *fp;
! 136: int ns_cnt = 0;
! 137: char *cp, buf[1024];
! 138:
! 139: if (!primary) {
! 140: primary = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
! 141: if (!primary) return -1;
! 142: }
! 143: memset(primary, 0, sizeof(struct sockaddr_in));
! 144: primary->sin_family = AF_INET;
! 145: primary->sin_port = htons(NAMESERVER_PORT);
! 146: primary->sin_addr.s_addr = htonl(0x7f000001);/* 127.0.0.1 by default */
! 147:
! 148: if (secondary) {
! 149: free(secondary);
! 150: secondary = 0;
! 151: }
! 152:
! 153: if ((fp = fopen(_PATH_RESCONF, "r")) != 0) {
! 154: while (fgets(buf, sizeof(buf), fp) != 0) {
! 155: buf[sizeof(buf)-1] = '\0';
! 156: for (cp = buf; *cp; cp++) {
! 157: if (*cp == '#' || *cp == '\r' || *cp == '\n') {
! 158: *cp = '\0';
! 159: break;
! 160: }
! 161: if (*cp < ' ') *cp = ' ';
! 162: }
! 163: if (buf[0] == '\0')
! 164: continue; /* skip empty lines and commentary */
! 165:
! 166: if (!strncasecmp(buf, NAMESERVER_TOKEN, sizeof(NAMESERVER_TOKEN)-1)) {
! 167: cp = strip_blanks(buf + sizeof(NAMESERVER_TOKEN)-1);
! 168: if (!ns_cnt++) {
! 169: primary->sin_addr.s_addr = inet_addr(cp);
! 170: } else if (!secondary) {
! 171: secondary = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
! 172: if (secondary) {
! 173: memset(secondary, 0, sizeof(struct sockaddr_in));
! 174: secondary->sin_family = AF_INET;
! 175: secondary->sin_port = htons(NAMESERVER_PORT);
! 176: secondary->sin_addr.s_addr = inet_addr(cp);
! 177: }
! 178: }
! 179: }
! 180: }
! 181: (void)fclose(fp);
! 182: }
! 183: return ns_cnt;
! 184: }
! 185:
! 186: int
! 187: domain_resolve_addr(domain, sd, notify)
! 188: const char *domain;
! 189: SESSION *sd;
! 190: void (*notify)(SESSION *sd, DOMAIN_DATA *dd);
! 191: {
! 192: return nameserver_request(domain, IpAddress, sd, notify);
! 193: }
! 194:
! 195: int
! 196: domain_resolve_mxlist(domain, sd, notify)
! 197: const char *domain;
! 198: SESSION *sd;
! 199: void (*notify)(SESSION *sd, DOMAIN_DATA *dd);
! 200: {
! 201: return nameserver_request(domain, MailExchanger, sd, notify);
! 202: }
! 203:
! 204: int
! 205: domain_resolve_name(ipaddr, sd, notify)
! 206: in_addr_t ipaddr;
! 207: SESSION *sd;
! 208: void (*notify)(SESSION *sd, DOMAIN_DATA *dd);
! 209: {
! 210: return nameserver_request((char *)&ipaddr, DomainName, sd, notify);
! 211: }
! 212:
! 213:
! 214: /*
! 215: * Callback function: catch all errors during nameserver request.
! 216: */
! 217: static void
! 218: nameserver_error(sd, error)
! 219: SESSION *sd;
! 220: int error;
! 221: {
! 222: DOMAIN_TRANSACT *dt = TRANSACT(sd);
! 223: if (sd && dt) {
! 224: if (error != ETIMEDOUT) {
! 225: #ifdef HAVE_REPORT_FUNC
! 226: report(Warn, 0, error, "%lu: domain_resolver: %s (try=%d)",
! 227: sd->sid, peertoa(0, session_peer(sd)),
! 228: dt->retry + 1);
! 229: #endif
! 230: } else if (++dt->retry < NAMESERVER_RETRIES) {
! 231: nameserver_send(sd);
! 232: return;
! 233: }
! 234: }
! 235: nameserver_close(sd);
! 236: }
! 237:
! 238: /*
! 239: * Normal close nameserver request.
! 240: */
! 241: static void
! 242: nameserver_close(sd)
! 243: SESSION *sd;
! 244: {
! 245: DOMAIN_TRANSACT *dt = TRANSACT(sd);
! 246:
! 247: session_free(sd);
! 248: if (dt) {
! 249: if (dt->data) { /* purge unresolved names */
! 250: if (dt->type != DomainName)
! 251: domain_data_free(&dt->data, "");
! 252: else if (dt->data->addr == 0 || dt->data->addr == -1)
! 253: memcpy(&dt->data->addr, dt->name, sizeof(dt->data->addr));
! 254: }
! 255: #ifdef DEBUG
! 256: dump_reply(dt);
! 257: #endif
! 258: if (dt->callback) {
! 259: (*dt->callback)(dt->sd, dt->data);
! 260: dt->data = 0; /* received data dispatched */
! 261: }
! 262: free_transact(dt);
! 263: }
! 264: }
! 265:
! 266: static void
! 267: discard_request(arg)
! 268: void *arg;
! 269: {
! 270: DOMAIN_TRANSACT *dt = (DOMAIN_TRANSACT *)arg;
! 271: if (dt) {
! 272: dt->sd = 0;
! 273: dt->callback = 0;
! 274: }
! 275: }
! 276:
! 277: static u_short
! 278: unique_reqid()
! 279: {
! 280: static u_short reqid = 0;
! 281: if (++reqid == 0) reqid++; /* prevent 0 reqid */
! 282: return reqid;
! 283: }
! 284:
! 285: static DOMAIN_TRANSACT *
! 286: new_transact()
! 287: {
! 288: DOMAIN_TRANSACT *curr;
! 289: if ((curr = (DOMAIN_TRANSACT *)malloc(sizeof(DOMAIN_TRANSACT))) == 0)
! 290: return 0;
! 291: memset(curr, 0, sizeof(DOMAIN_TRANSACT));
! 292:
! 293: if (first_transact) {
! 294: DOMAIN_TRANSACT *prev = first_transact;
! 295: while (prev->next) prev = prev->next;
! 296: prev->next = curr;
! 297: } else first_transact = curr;
! 298:
! 299: return curr;
! 300: }
! 301:
! 302: static DOMAIN_TRANSACT *
! 303: find_transact(reqid)
! 304: u_short reqid;
! 305: {
! 306: DOMAIN_TRANSACT *curr;
! 307: for (curr = first_transact; curr; curr = curr->next) {
! 308: if (curr->reqid && curr->reqid == reqid)
! 309: return curr;
! 310: }
! 311: return 0;
! 312: }
! 313:
! 314: static void
! 315: free_transact(dt)
! 316: DOMAIN_TRANSACT *dt;
! 317: {
! 318: DOMAIN_TRANSACT *curr, *prev, *next;
! 319:
! 320: curr = first_transact;
! 321: prev = 0;
! 322: while (curr) {
! 323: if (!dt || curr == dt) {
! 324: next = curr->next;
! 325: if (prev)
! 326: prev->next = next;
! 327: else first_transact = next;
! 328:
! 329: if (curr->sd)
! 330: session_unbind(curr->sd, discard_request, curr);
! 331: if (curr->name)
! 332: free(curr->name);
! 333: if (curr->domain)
! 334: free(curr->domain);
! 335: domain_data_free(&curr->data, 0);
! 336: free(curr);
! 337:
! 338: curr = next;
! 339: } else {
! 340: prev = curr;
! 341: curr = curr->next;
! 342: }
! 343: }
! 344: }
! 345:
! 346: DOMAIN_DATA *
! 347: domain_data_add(list, name, pref)
! 348: DOMAIN_DATA **list;
! 349: const char *name;
! 350: int pref;
! 351: {
! 352: DOMAIN_DATA *curr, *last, *prev;
! 353: int insert;
! 354: char *cp;
! 355:
! 356: /* sanity check */
! 357: if (!list || !name || !*name) {
! 358: errno = EINVAL;
! 359: return 0;
! 360: }
! 361:
! 362: /* sort it by pref ascending (bigger pref farther) */
! 363: last = prev = 0;
! 364: insert = 0;
! 365: for (curr = *list; curr; curr = curr->next) {
! 366: /* prevent duplicates */
! 367: if (curr->name && !strcasecmp(curr->name, name))
! 368: return curr;
! 369:
! 370: if (!insert && pref < curr->pref) {
! 371: insert++;
! 372: prev = last;
! 373: }
! 374: last = curr;
! 375: }
! 376: if ((curr = (DOMAIN_DATA *)malloc(sizeof(DOMAIN_DATA))) == 0)
! 377: return 0;
! 378: memset(curr, 0, sizeof(DOMAIN_DATA));
! 379:
! 380: if ((curr->name = strdup(name)) == 0) {
! 381: int save_errno = errno;
! 382: free(curr);
! 383: save_errno = errno;
! 384: return 0;
! 385: }
! 386: /* make all lowercase */
! 387: for (cp = curr->name; *cp; cp++) {
! 388: if (*cp >= 'A' && *cp <= 'Z')
! 389: *cp = *cp + 32;
! 390: }
! 391: curr->pref = pref;
! 392:
! 393: if (insert) {
! 394: if (prev) {
! 395: curr->next = prev->next;
! 396: prev->next = curr;
! 397: } else {
! 398: curr->next = *list;
! 399: *list = curr;
! 400: }
! 401: } else if (last) {
! 402: last->next = curr;
! 403: } else {
! 404: *list = curr;
! 405: }
! 406: return curr;
! 407: }
! 408:
! 409: DOMAIN_DATA *
! 410: domain_data_find(list, name)
! 411: DOMAIN_DATA **list;
! 412: const char *name;
! 413: {
! 414: DOMAIN_DATA *curr;
! 415:
! 416: /* sanity check */
! 417: if (!list || !name || !*name)
! 418: return 0;
! 419:
! 420: for (curr = *list; curr; curr = curr->next) {
! 421: if (!strcasecmp(curr->name, name))
! 422: return curr;
! 423: }
! 424: return 0;
! 425: }
! 426:
! 427: void
! 428: domain_data_free(list, name)
! 429: DOMAIN_DATA **list;
! 430: const char *name;
! 431: {
! 432: DOMAIN_DATA *curr, *prev, *next;
! 433:
! 434: /* sanity check */
! 435: if (!list) return;
! 436:
! 437: curr = *list;
! 438: prev = 0;
! 439: while (curr) {
! 440: if (!name || (*name == '\0' && curr->addr == 0) ||
! 441: curr->name == name || !strcasecmp(curr->name, name)) {
! 442: next = curr->next;
! 443: if (prev)
! 444: prev->next = next;
! 445: else *list = next;
! 446:
! 447: if (curr->name) free(curr->name);
! 448: free(curr);
! 449:
! 450: curr = next;
! 451: } else {
! 452: prev = curr;
! 453: curr = curr->next;
! 454: }
! 455: }
! 456: }
! 457:
! 458: static int
! 459: nameserver_request(domain, type, org, notify)
! 460: const char *domain;
! 461: DomainType type;
! 462: SESSION *org;
! 463: void (*notify)(SESSION *sd, DOMAIN_DATA *dd);
! 464: {
! 465: SESSION *sd;
! 466: DOMAIN_TRANSACT *dt;
! 467: char buf[MAXDNAME];
! 468: const u_char *cp;
! 469:
! 470: /* sanity check */
! 471: if (!domain || !*domain) {
! 472: errno = EINVAL;
! 473: return -1;
! 474: }
! 475: if (!primary && domain_resolver_init() < 0)
! 476: return -1;
! 477:
! 478: if ((sd = session_open(-1, (struct sockaddr *)primary, DataSequence)) == 0)
! 479: return -1;
! 480:
! 481: if ((dt = new_transact()) == 0) {
! 482: int save_errno = errno;
! 483: session_free(sd);
! 484: errno = save_errno;
! 485: return -1;
! 486: }
! 487: switch (type) {
! 488: case IpAddress:
! 489: case MailExchanger:
! 490: dt->name = strdup(domain);
! 491: (void)strncpy(buf, domain, sizeof(buf));
! 492: buf[sizeof(buf)-1] = '\0';
! 493: dt->domain = strdup(buf);
! 494: break;
! 495: case DomainName:
! 496: if ((dt->name = (char *)malloc(sizeof(in_addr_t))) == 0)
! 497: break;
! 498: memcpy(dt->name, domain, sizeof(in_addr_t));
! 499: cp = (u_char *)domain;
! 500: snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa",
! 501: cp[3], cp[2], cp[1], cp[0]);
! 502: dt->domain = strdup(buf);
! 503: break;
! 504: }
! 505: if (!dt->name || !dt->domain) {
! 506: int save_errno = errno;
! 507: session_free(sd);
! 508: free_transact(dt);
! 509: errno = save_errno;
! 510: return -1;
! 511: }
! 512: dt->reqid = unique_reqid();
! 513: dt->type = type;
! 514:
! 515: session_setcallback(sd, 0, nameserver_error, nameserver_reply);
! 516: session_setcookie(sd, dt);
! 517: session_settimeout(sd, NAMESERVER_TIMEOUT);
! 518:
! 519: if (nameserver_send(sd) < 0) {
! 520: int save_errno = errno;
! 521: #ifdef HAVE_REPORT_FUNC
! 522: char ipaddr[50];
! 523: report(Warn, 0, errno, "%lu: nameserver_send: %s",
! 524: sd->sid, peertoa(ipaddr, session_peer(sd)));
! 525: #endif
! 526: session_free(sd);
! 527: free_transact(dt);
! 528: errno = save_errno;
! 529: return -1;
! 530: }
! 531: if (org && session_bind(org, discard_request, dt) != -1)
! 532: dt->sd = org;
! 533: dt->callback = notify;
! 534: return 0;
! 535: }
! 536:
! 537: static int
! 538: nameserver_send(sd)
! 539: SESSION *sd;
! 540: {
! 541: DOMAIN_TRANSACT *dt = TRANSACT(sd);
! 542: u_char buf[PACKETSZ];
! 543: HEADER *hp = (HEADER *)buf;
! 544: int len;
! 545: u_char *cp, *dnptrs[50], **dpp, **lastdnptr;
! 546:
! 547: /* sanity check */
! 548: if (!dt) {
! 549: errno = EINVAL;
! 550: return -1;
! 551: }
! 552: memset(hp, 0, HFIXEDSZ);
! 553: hp->id = htons(dt->reqid);
! 554: hp->rd = 1; /* recursion desired */
! 555: hp->qdcount = htons(1); /* we allways utilize one query per packet */
! 556:
! 557: cp = buf + HFIXEDSZ;
! 558: len = PACKETSZ - (HFIXEDSZ + QFIXEDSZ);
! 559: dpp = dnptrs;
! 560: *dpp++ = buf;
! 561: *dpp = 0;
! 562: lastdnptr = dnptrs + sizeof(dnptrs) / sizeof(dnptrs[0]);
! 563:
! 564: if ((len = dn_comp(dt->domain, cp, len, dnptrs, lastdnptr)) < 0)
! 565: return -1;
! 566:
! 567: cp += len;
! 568: /* translate our type into appropriate NS opcode && type */
! 569: switch (dt->type) {
! 570: case IpAddress:
! 571: PUTSHORT(T_A, cp);
! 572: break;
! 573: case DomainName:
! 574: PUTSHORT(T_PTR, cp);
! 575: break;
! 576: case MailExchanger:
! 577: PUTSHORT(T_MX, cp);
! 578: break;
! 579: }
! 580: PUTSHORT(C_IN, cp);
! 581: len = cp - buf;
! 582:
! 583: dprintf(("nameserver_send: \"%s\"", dt->domain));
! 584:
! 585: return session_send(sd, buf, len);
! 586: }
! 587:
! 588: static void
! 589: nameserver_reply(sd, data, len)
! 590: SESSION *sd;
! 591: const unsigned char *data;
! 592: int len;
! 593: {
! 594: DOMAIN_TRANSACT *dt;
! 595:
! 596: /* sanity check */
! 597: if (!sd) return;
! 598:
! 599: if ((dt = parse_packet(data, len)) == 0) {
! 600: #ifdef HAVE_REPORT_FUNC
! 601: char ipaddr[50];
! 602: report(Info, 0, 0, "%lu: nameserver_reply: %s: unexpected packet (len=%d)",
! 603: sd->sid, peertoa(ipaddr, session_peer(sd)), len);
! 604: #endif
! 605: return;
! 606: }
! 607: if (dt->rcode < 0) {
! 608: #ifdef HAVE_REPORT_FUNC
! 609: char ipaddr[50];
! 610: report(Info, 0, 0, "%lu: nameserver_reply: %s: broken packet (len=%d try=%d err=%d)",
! 611: sd->sid, peertoa(ipaddr, session_peer(sd)),
! 612: len, dt->retry + 1, -dt->rcode);
! 613: #endif
! 614: return;
! 615: }
! 616: #ifdef HAVE_REPORT_FUNC
! 617: if (dt->rcode != NOERROR &&
! 618: dt->rcode != SERVFAIL &&
! 619: dt->rcode != NXDOMAIN) {
! 620: char ipaddr[50];
! 621: report(Crit, 0, 0, "%lu: nameserver_reply: %s: %s (try=%d)",
! 622: sd->sid, peertoa(ipaddr, session_peer(sd)),
! 623: rcode2text[dt->rcode], dt->retry + 1);
! 624: }
! 625: #endif
! 626: #ifdef DEBUG
! 627: dump_reply(dt);
! 628: #endif
! 629: if (dt->rcode == NOERROR &&
! 630: (dt->type == MailExchanger ||
! 631: (dt->type == IpAddress && dt->expand && dt->expand < MAX_EXPAND_TRIES))) {
! 632: DOMAIN_DATA *dd;
! 633: for (dd = dt->data; dd; dd = dd->next) {
! 634: /* it was CNAME -- expand it */
! 635: if (dd->name && !dd->addr) {
! 636: if (dt->domain) {
! 637: if (!strcasecmp(dd->name, dt->domain))
! 638: break; /* to prevent looping */
! 639: free(dt->domain);
! 640: }
! 641: if ((dt->domain = strdup(dd->name)) == 0)
! 642: break;
! 643: dt->reqid = unique_reqid();
! 644: dt->expand++;
! 645: dt->retry = 0;
! 646: dt->type = IpAddress;
! 647: if (nameserver_send(sd) < 0)
! 648: break;
! 649: return;
! 650: }
! 651: }
! 652: }
! 653: nameserver_close(sd); /* caller notified inside */
! 654: }
! 655:
! 656: static DOMAIN_TRANSACT *
! 657: parse_packet(data, len)
! 658: const unsigned char *data;
! 659: int len;
! 660: {
! 661: const u_char *pkt = data;
! 662: HEADER *hp = (HEADER *)pkt;
! 663: const u_char *cp = pkt + HFIXEDSZ;
! 664: int qdcount, ancount, nscount, arcount, nb;
! 665: DOMAIN_TRANSACT *dt;
! 666: DOMAIN_DATA *dd;
! 667: u_short type, class, rdlen, pref;
! 668: u_int ttl;
! 669: char name[MAXDNAME+1];
! 670:
! 671: /*
! 672: * first check the response Header.
! 673: */
! 674: if (!hp || len < HFIXEDSZ) {
! 675: dprintf(("parse_packet: undersized packet, len=%d", len));
! 676: return 0;
! 677: }
! 678: if (!hp->qr) {
! 679: dprintf(("parse_packet: not a response"));
! 680: return 0;
! 681: }
! 682: if (hp->opcode) {
! 683: dprintf(("parse_packet: response not a QUERY"));
! 684: return 0;
! 685: }
! 686: if (hp->rcode < NOERROR || hp->rcode > REFUSED) {
! 687: dprintf(("parse_packet: bad reply code %d", (int)hp->rcode));
! 688: return 0;
! 689: }
! 690: if ((dt = find_transact(ntohs(hp->id))) == 0) {
! 691: dprintf(("parse_packet: invalid reqid"));
! 692: return 0;
! 693: }
! 694: dt->rcode = hp->rcode; /* Header is OK; reply code fixed */
! 695:
! 696: qdcount = ntohs(hp->qdcount);
! 697: ancount = ntohs(hp->ancount);
! 698: nscount = ntohs(hp->nscount);
! 699: arcount = ntohs(hp->arcount);
! 700:
! 701: dprintf(("parse_packet: rcode=%d qdcount=%d ancount=%d nscount=%d arcount=%d",
! 702: hp->rcode, qdcount, ancount, nscount, arcount));
! 703:
! 704: /*
! 705: * check Question section.
! 706: */
! 707: while (qdcount-- > 0) {
! 708: if ((nb = dn_expand(pkt, pkt + len, cp, name, sizeof(name))) < 0) {
! 709: dprintf(("parse_packet: dn_expand: unexpected end of question"));
! 710: dt->rcode = -1;
! 711: return dt;
! 712: }
! 713: if (strcasecmp(name, dt->domain)) {
! 714: dprintf(("parse_packet: question name mismatch transaction"));
! 715: dt->rcode = -2;
! 716: return dt;
! 717: }
! 718: cp += nb;
! 719: if (cp + 2 * INT16SZ > pkt + len) {
! 720: dprintf(("parse_packet: unexpected end of question"));
! 721: dt->rcode = -3;
! 722: return dt;
! 723: }
! 724: GETSHORT(type, cp);
! 725: GETSHORT(class, cp);
! 726: if (class != C_IN) {
! 727: dprintf(("parse_packet: question class mismatch transaction"));
! 728: dt->rcode = -4;
! 729: return dt;
! 730: }
! 731: if ((type == T_A && dt->type == IpAddress) ||
! 732: (type == T_PTR && dt->type == DomainName) ||
! 733: (type == T_MX && dt->type == MailExchanger))
! 734: continue;
! 735:
! 736: dprintf(("parse_packet: question type mismatch transaction"));
! 737: dt->rcode = -5;
! 738: return dt;
! 739: }
! 740:
! 741: /*
! 742: * parse Answer section.
! 743: */
! 744: while (ancount-- > 0) {
! 745: if ((nb = dn_expand(pkt, pkt + len, cp, name, sizeof(name))) < 0) {
! 746: dprintf(("parse_packet: dn_expand: unexpected end of answer"));
! 747: dt->rcode = -10;
! 748: return dt;
! 749: }
! 750: dprintf(("parse_packet: answer name \"%s\"", name));
! 751: cp += nb;
! 752: if (cp + 3 * INT16SZ + INT32SZ > pkt + len) {
! 753: dprintf(("parse_packet: unexpected end of answer"));
! 754: dt->rcode = -11;
! 755: return dt;
! 756: }
! 757: GETSHORT(type, cp);
! 758: GETSHORT(class, cp);
! 759: GETLONG(ttl, cp);
! 760: GETSHORT(rdlen, cp);
! 761: if (cp + rdlen > pkt + len) {
! 762: dprintf(("parse_packet: unexpected end of answer"));
! 763: dt->rcode = -12;
! 764: return dt;
! 765: }
! 766: if (class != C_IN) {
! 767: dprintf(("parse_packet: answer class mismatch transaction"));
! 768: dt->rcode = -13;
! 769: return dt;
! 770: }
! 771: dprintf(("parse_packet: answer rdlen=%d", rdlen));
! 772:
! 773: if (type == T_A && dt->type == IpAddress) {
! 774: /* XXX IPv6 incompatible yet */
! 775: if (rdlen % sizeof(in_addr_t)) {
! 776: dprintf(("parse_packet: unexpected rdlen in A RR"));
! 777: dt->rcode = -14;
! 778: return dt;
! 779: }
! 780: while (rdlen > 0) {
! 781: dprintf(("parse_packet: A %d.%d.%d.%d (ttl=%d)",
! 782: cp[0], cp[1], cp[2], cp[3], ttl));
! 783: if ((dd = domain_data_add(&dt->data, name, 0)) != 0) {
! 784: if (!dd->ttl || !ttl || dd->ttl > ttl)
! 785: dd->ttl = ttl;
! 786: if (dd->addr == 0 || dd->addr == -1)
! 787: dd->addr = *((in_addr_t *)cp);
! 788: }
! 789: cp += sizeof(in_addr_t);
! 790: rdlen -= sizeof(in_addr_t);
! 791: }
! 792: continue;
! 793: }
! 794: if (type == T_MX && dt->type == MailExchanger) {
! 795: if (rdlen < INT16SZ) {
! 796: dprintf(("parse_packet: unexpected rdlen in MX RR"));
! 797: dt->rcode = -15;
! 798: return dt;
! 799: }
! 800: GETSHORT(pref, cp);
! 801: rdlen -= INT16SZ;
! 802: while (rdlen > 0) {
! 803: if ((nb = dn_expand(pkt, pkt + len, cp, name, sizeof(name))) < 0) {
! 804: dprintf(("parse_packet: dn_expand: unexpected end of answer"));
! 805: dt->rcode = -16;
! 806: return dt;
! 807: }
! 808: dprintf(("parse_packet: MX %d \"%s\" (ttl=%d)",
! 809: pref, name, ttl));
! 810: if ((dd = domain_data_add(&dt->data, name, pref)) != 0) {
! 811: if (!dd->ttl || !ttl || dd->ttl > ttl)
! 812: dd->ttl = ttl;
! 813: }
! 814: cp += nb;
! 815: rdlen -= nb;
! 816: }
! 817: continue;
! 818: }
! 819: if (type == T_PTR && dt->type == DomainName) {
! 820: while (rdlen > 0) {
! 821: if ((nb = dn_expand(pkt, pkt + len, cp, name, sizeof(name))) < 0) {
! 822: dprintf(("parse_packet: dn_expand: unexpected end of answer"));
! 823: dt->rcode = -17;
! 824: return dt;
! 825: }
! 826: dprintf(("parse_packet: PTR \"%s\" (ttl=%d)",
! 827: name, ttl));
! 828: if ((dd = domain_data_add(&dt->data, name, 0)) != 0) {
! 829: if (!dd->ttl || !ttl || dd->ttl > ttl)
! 830: dd->ttl = ttl;
! 831: }
! 832: cp += nb;
! 833: rdlen -= nb;
! 834: }
! 835: continue;
! 836: }
! 837: if (type == T_CNAME) {
! 838: dprintf(("parse_packet: CNAME \"%s\" removed", name));
! 839: domain_data_free(&dt->data, name);
! 840: cp += rdlen;
! 841: continue;
! 842: }
! 843: /* simply skip it */
! 844: dprintf(("parse_packet: answer name \"%s\" type %d",
! 845: name, type));
! 846: cp += rdlen;
! 847: }
! 848:
! 849: if (dt->type != MailExchanger)
! 850: return dt;
! 851:
! 852: /*
! 853: * skip Authority section.
! 854: */
! 855: while (nscount-- > 0) {
! 856: if ((nb = dn_expand(pkt, pkt + len, cp, name, sizeof(name))) < 0) {
! 857: dprintf(("parse_packet: dn_expand: unexpected end of authority"));
! 858: dt->rcode = -20;
! 859: return dt;
! 860: }
! 861: cp += nb;
! 862: if (cp + 3 * INT16SZ + INT32SZ > pkt + len) {
! 863: dprintf(("parse_packet: unexpected end of authority"));
! 864: dt->rcode = -21;
! 865: return dt;
! 866: }
! 867: GETSHORT(type, cp);
! 868: GETSHORT(class, cp);
! 869: GETLONG(ttl, cp);
! 870: GETSHORT(rdlen, cp);
! 871: if (cp + rdlen > pkt + len) {
! 872: dprintf(("parse_packet: unexpected end of authority"));
! 873: dt->rcode = -22;
! 874: return dt;
! 875: }
! 876: /* simply skip it */
! 877: dprintf(("parse_packet: authority name \"%s\" type %d",
! 878: name, type));
! 879: cp += rdlen;
! 880: }
! 881:
! 882: /*
! 883: * parse Additional section.
! 884: */
! 885: while (arcount-- > 0) {
! 886: if ((nb = dn_expand(pkt, pkt + len, cp, name, sizeof(name))) < 0) {
! 887: dprintf(("parse_packet: dn_expand: unexpected end of answer"));
! 888: dt->rcode = -30;
! 889: return dt;
! 890: }
! 891: dprintf(("parse_packet: additional name \"%s\"", name));
! 892: cp += nb;
! 893: if (cp + 3 * INT16SZ + INT32SZ > pkt + len) {
! 894: dprintf(("parse_packet: unexpected end of additional"));
! 895: dt->rcode = -31;
! 896: return dt;
! 897: }
! 898: GETSHORT(type, cp);
! 899: GETSHORT(class, cp);
! 900: GETLONG(ttl, cp);
! 901: GETSHORT(rdlen, cp);
! 902: if (cp + rdlen > pkt + len) {
! 903: dprintf(("parse_packet: unexpected end of additional"));
! 904: dt->rcode = -32;
! 905: return dt;
! 906: }
! 907: if (class == C_IN && type == T_A) {
! 908: /* XXX IPv6 incompatible yet */
! 909: if (rdlen % sizeof(in_addr_t)) {
! 910: dprintf(("parse_packet: unexpected rdlen in A RR"));
! 911: dt->rcode = -33;
! 912: return dt;
! 913: }
! 914: while (rdlen > 0) {
! 915: dprintf(("parse_packet: A %d.%d.%d.%d (ttl=%d)",
! 916: cp[0], cp[1], cp[2], cp[3], ttl));
! 917: if ((dd = domain_data_find(&dt->data, name)) != 0) {
! 918: if (!dd->ttl || !ttl || dd->ttl > ttl)
! 919: dd->ttl = ttl;
! 920: if (dd->addr == 0 || dd->addr == -1)
! 921: dd->addr = *((in_addr_t *)cp);
! 922: }
! 923: cp += sizeof(in_addr_t);
! 924: rdlen -= sizeof(in_addr_t);
! 925: }
! 926: continue;
! 927: }
! 928: /* simply skip it */
! 929: dprintf(("parse_packet: additional name \"%s\" type %d",
! 930: name, type));
! 931: cp += rdlen;
! 932: }
! 933:
! 934: return dt;
! 935: }
! 936:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>