From eb0b66697bec5e5d01692ca12eeb29f9e891619b Mon Sep 17 00:00:00 2001 From: Ted Lemon Date: Mon, 12 Jan 1998 01:00:09 +0000 Subject: [PATCH] Preliminary rewrite of asynchronous DNS code (needs work) --- common/dns.c | 315 ++++++++++++++++++++++++--------------------------- 1 file changed, 147 insertions(+), 168 deletions(-) diff --git a/common/dns.c b/common/dns.c index cc98e005a..0e82f6039 100644 --- a/common/dns.c +++ b/common/dns.c @@ -48,7 +48,7 @@ #ifndef lint static char copyright[] = -"$Id: dns.c,v 1.5 1997/11/29 07:51:49 mellon Exp $ Copyright (c) 1997 The Internet Software Consortium. All rights reserved.\n"; +"$Id: dns.c,v 1.6 1998/01/12 01:00:09 mellon Exp $ Copyright (c) 1997 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -64,6 +64,8 @@ static int nslookup PROTO ((u_int8_t, char *, int, u_int16_t, u_int16_t)); static int zonelookup PROTO ((u_int8_t, char *, int, u_int16_t)); u_int16_t dns_port; +struct dns_query *queries [65536]; + /* Initialize the DNS protocol. */ void dns_startup () @@ -151,16 +153,19 @@ static int copy_out_name (base, name, buf) called with a null pointer. Otherwise, the callback is called with the address of the string returned by the name server. */ -int ns_inaddr_lookup (id, inaddr) - u_int16_t id; +struct dns_query *ns_inaddr_lookup (inaddr, wakeup) struct iaddr inaddr; + struct dns_wakeup *wakeup; { - unsigned char namebuf [512]; - unsigned char *s = namebuf; + unsigned char question [512]; + unsigned char *s; unsigned char *label; int i; unsigned char c; + s = question; + + /* Copy out the digits. */ for (i = 3; i >= 0; --i) { label = s++; *label = 1; @@ -178,140 +183,123 @@ int ns_inaddr_lookup (id, inaddr) s += addlabel (s, "in-addr"); s += addlabel (s, "arpa"); *s++ = 0; -/* return nslookup (id, namebuf, s - namebuf, T_PTR, C_IN); */ - return zonelookup (id, namebuf, s - namebuf, C_IN); -} -/* Construct and transmit a name server query. */ + /* Set the query type. */ + putUShort (s, T_PTR); + s += sizeof (u_int16_t); + + /* Set the query class. */ + putUShort (s, C_IN); + s += sizeof (u_int16_t); + + return ns_query (question, s - question, wakeup); +} -static int nslookup (id, qname, namelen, qtype, qclass) - u_int8_t id; - char *qname; - int namelen; - u_int16_t qtype; - u_int16_t qclass; +struct dns_query *ns_query (question, len, wakeup) + unsigned char *question; + int len; + struct dns_wakeup *wakeup; { HEADER *hdr; - unsigned char query [512]; - u_int8_t *s; - int len; - int i, status; - struct sockaddr_in *server = pick_name_server (); - - if (!server) - return 0; + struct dns_query *query; + unsigned char *s; + + /* See if there's already a query for this name, and allocate a + query if none exists. */ + query = find_dns_query (question, len, 1); + + /* If we can't allocate a query, report that the query failed. */ + if (!query) { + return (struct dns_query *)-1; + } + + /* If the query has already been answered, return it. */ + if (query -> expiry > cur_time) + return query; + + /* The query hasn't yet been answered, so we have to wait, one + way or another. Put the wakeup on the list. */ + if (wakeup) { + wakeup -> next = query -> wakeups; + query -> wakeups = wakeup; + } + + /* If the query has already been sent, but we don't yet have + an answer, we're done. */ + if (query -> sent) + return (struct dns_query *)0; /* Construct a header... */ - hdr = (HEADER *)query; + hdr = (HEADER *)query -> buf; memset (hdr, 0, sizeof *hdr); - hdr -> id = htons (id); + hdr -> id = query -> id; hdr -> rd = 1; hdr -> opcode = QUERY; hdr -> qdcount = htons (1); - /* Copy in the name we're looking up. */ - s = (u_int8_t *)(hdr + 1); - memcpy (s, qname, namelen); - s += namelen; - - /* Set the query type. */ - putUShort (s, qtype); - s += sizeof (u_int16_t); + /* Copy the name into the buffer. */ + s = (unsigned char *)hdr + 1; + memcpy (s, question, len); + query -> question = s; + query -> question_len = len; - /* Set the query class. */ - putUShort (s, qclass); - s += sizeof (u_int16_t); + /* Figure out how long the whole message is */ + s += len; + query -> len = s - query -> buf; + + /* Flag the query as having been sent. */ + query -> sent = 1; /* Send the query. */ - status = sendto (dns_protocol_fd, query, s - query, 0, - (struct sockaddr *)server, sizeof *server); + dns_timeout (query); - /* If the send failed, report the failure. */ - if (status < 0) - return 0; - return 1; + /* No answer yet, obviously. */ + return (struct dns_query *)0; } -/* Construct a query for the SOA for a specified name. - Try every possible SOA name starting from the name specified and going - to the root name - e.g., for +/* Retransmit a DNS query. */ - 215.5.5.192.in-addr.arpa, look for SOAs matching: +void dns_timeout (qv) + void *qv; +{ + struct dns_query *query = qv; + int status; - 215.5.5.5.192.in-addr.arpa - 5.5.192.in-addr.arpa - 5.192.in-addr.arpa - 192.in-addr.arpa - in-addr.arpa - arpa */ + /* Choose the server to send to. */ + if (!query -> next_server) + query -> next_server = first_name_server (); -static int zonelookup (id, qname, namelen, qclass) - u_int8_t id; - char *qname; - int namelen; - u_int16_t qclass; -{ - HEADER *hdr; - unsigned char query [512]; - u_int8_t *s, *nptr; - int len; - int i, status, count; - struct sockaddr_in *server = pick_name_server (); - - if (!server) - return 0; + /* Send the query. */ + if (query -> next_server) + status = sendto (dns_protocol_fd, + query -> buf, query -> len, 0, + ((struct sockaddr *)&query -> + next_server -> addr), + sizeof query -> next_server -> addr); + else + status = -1; - /* Construct a header... */ - hdr = (HEADER *)query; - memset (hdr, 0, sizeof *hdr); - hdr -> id = htons (id); - hdr -> rd = 1; - hdr -> opcode = QUERY; + /* Look for the next server... */ + query -> next_server = query -> next_server -> next; - /* Copy in the name we're looking up. */ - s = (u_int8_t *)(hdr + 1); - memcpy (s, qname, namelen); - s += namelen; - - /* Set the query type. */ - putUShort (s, T_SOA); - s += sizeof (u_int16_t); + /* If this is our first time, backoff one second. */ + if (!query -> backoff) + query -> backoff = 1; - /* Set the query class. */ - putUShort (s, qclass); - s += sizeof (u_int16_t); - count = 1; - - /* Now query up the hierarchy. */ - nptr = (u_int8_t *)(hdr + 1); - while (*(nptr += *nptr + 1)) { - /* Store a compressed reference from the full name. */ - putUShort (s, ntohs (htons (0xC000) | - htons (nptr - &query [0]))); - s += sizeof (u_int16_t); - - /* Store the query type. */ - putUShort (s, T_SOA); - s += sizeof (u_int16_t); - - putUShort (s, qclass); - s += sizeof (u_int16_t); - - /* Increment the query count... */ - ++count; -break; - } - hdr -> qdcount = htons (count); + /* If the send failed, don't advance the backoff. */ + else if (status < 0) + ; -dump_raw (query, s - query); - /* Send the query. */ - status = sendto (dns_protocol_fd, query, s - query, 0, - (struct sockaddr *)server, sizeof *server); + /* If we haven't run out of servers to try, don't backoff. */ + else if (query -> next_server) + ; + + /* If we haven't backed off enough yet, back off some more. */ + else if (query -> backoff < 30) + query -> backoff += random() % query -> backoff; - /* If the send failed, report the failure. */ - if (status < 0) - return 0; - return 1; + /* Set up the timeout. */ + add_timeout (cur_time + query -> backoff, dns_timeout, query); } /* Process a reply from a name server. */ @@ -321,83 +309,74 @@ void dns_packet (protocol) { HEADER *ns_header; struct sockaddr_in from; - int fl; - unsigned char buf [4096]; - unsigned char nbuf [512]; + struct dns_wakeup *wakeup; + unsigned char buf [512]; unsigned char *base; - unsigned char *dptr; + unsigned char *dptr, *name; u_int16_t type; u_int16_t class; TIME ttl; u_int16_t rdlength; int len, status; int i; + struct dns_query *query; len = sizeof from; status = recvfrom (protocol -> fd, buf, sizeof buf, 0, (struct sockaddr *)&from, &len); if (status < 0) { - warn ("icmp_echoreply: %m"); + warn ("dns_packet: %m"); + return; + } + + /* Response is too long? */ + if (len > 512) { + warn ("dns_packet: dns message too long (%d)", len); return; } ns_header = (HEADER *)buf; base = (unsigned char *)(ns_header + 1); -#if 0 - /* Ignore invalid packets... */ - if (ntohs (ns_header -> id) > ns_query_max) { - printf ("Out-of-range NS message; id = %d\n", - ntohs (ns_header -> id)); + /* Parse the response... */ + dptr = base; + + /* If this is a response to a query from us, there should have + been only one query. */ + if (ntohs (ns_header -> qdcount) != 1) { + dns_bogus (buf, len); return; } -#endif - /* Parse the response... */ - dptr = base; + /* Find the start of the name in the query. */ + name = dptr; - /* Skip over the queries... */ - for (i = 0; i < ntohs (ns_header -> qdcount); i++) { - dptr += skipname (dptr); - /* Skip over the query type and query class. */ - dptr += 2 * sizeof (u_int16_t); + /* Skip over the name. */ + dptr += skipname (dptr); + + /* Skip over the query type and query class. */ + dptr += 2 * sizeof (u_int16_t); + + /* See if we asked this question. */ + query = find_dns_query (name, dptr - name, 0); + if (!query) { + dns_bogus (buf, len); + return; } - /* Process the answers... */ - for (i = 0; i < ntohs (ns_header -> ancount); i++) { - /* Skip over the name we looked up. */ - dptr += skipname (dptr); - - /* Get the type. */ - type = getUShort (dptr); - dptr += sizeof type; - - /* Get the class. */ - class = getUShort (dptr); - dptr += sizeof class; - - /* Get the time-to-live. */ - ttl = getULong (dptr); - dptr += sizeof ttl; - - /* Get the length of the reply. */ - rdlength = getUShort (dptr); - dptr += sizeof rdlength; - - switch (type) { - case T_A: - note ("A record; value is %d.%d.%d.%d", - dptr [0], dptr [1], dptr [2], dptr [3]); - break; - - case T_CNAME: - case T_PTR: - copy_out_name (base, dptr, nbuf); - note ("Domain name; value is %s\n", nbuf); - return; - - default: - note ("unhandled type: %x", type); - } + /* Save the response. */ + memcpy (buf, query -> buf, len); + + /* Remember where the question is and how long it is. */ + query -> question = name; + query -> question_len = dptr - name; + + /* Remember where the answer is and how long it is. */ + query -> answer = dptr; + query -> answer_len = len - (dptr - buf); + + /* Wake up everybody who's waiting. */ + for (wakeup = query -> wakeups; wakeup; wakeup = wakeup -> next) { + (*wakeup -> func) (query); } } -- 2.47.3