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>