File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / trafshow / domain_resolver.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 16:55:18 2012 UTC (12 years, 4 months ago) by misho
Branches: trafshow, MAIN
CVS tags: v5_2_3p0, v5_2_3, HEAD
trafshow

    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>