]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Preliminary rewrite of asynchronous DNS code (needs work)
authorTed Lemon <source@isc.org>
Mon, 12 Jan 1998 01:00:09 +0000 (01:00 +0000)
committerTed Lemon <source@isc.org>
Mon, 12 Jan 1998 01:00:09 +0000 (01:00 +0000)
common/dns.c

index cc98e005a82b423df0a45aeaf826bb62edf5d46f..0e82f60399d81690ba2281185e096ce05bee32ee 100644 (file)
@@ -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);
        }
 }