1997-03-08 02:24:16 +00:00
|
|
|
/* dns.c
|
|
|
|
|
1997-03-29 01:25:32 +00:00
|
|
|
Domain Name Service subroutines. */
|
1997-03-08 02:24:16 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (C) 1992 by Ted Lemon.
|
1998-03-15 20:50:53 +00:00
|
|
|
* Copyright (c) 1997, 1998 The Internet Software Consortium.
|
1997-03-08 02:24:16 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. Neither the name of The Internet Software Consortium nor the names
|
|
|
|
* of its contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
|
|
|
|
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
|
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* This file is based on software written in 1992 by Ted Lemon for
|
|
|
|
* a portable network boot loader. That original code base has been
|
|
|
|
* substantially modified for use in the Internet Software Consortium
|
|
|
|
* DHCP suite.
|
|
|
|
*
|
1997-03-29 01:25:32 +00:00
|
|
|
* These later modifications were done on behalf of the Internet
|
|
|
|
* Software Consortium by Ted Lemon <mellon@fugue.com> in cooperation
|
|
|
|
* with Vixie Enterprises. To learn more about the Internet Software
|
|
|
|
* Consortium, see ``http://www.vix.com/isc''. To learn more about
|
|
|
|
* Vixie Enterprises, see ``http://www.vix.com''.
|
1997-03-08 02:24:16 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
static char copyright[] =
|
1998-03-17 06:09:59 +00:00
|
|
|
"$Id: dns.c,v 1.9 1998/03/17 06:09:59 mellon Exp $ Copyright (c) 1997 The Internet Software Consortium. All rights reserved.\n";
|
1997-03-08 02:24:16 +00:00
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
#include "dhcpd.h"
|
|
|
|
#include "arpa/nameser.h"
|
|
|
|
|
1997-03-29 01:25:32 +00:00
|
|
|
int dns_protocol_initialized;
|
|
|
|
int dns_protocol_fd;
|
|
|
|
|
|
|
|
static int addlabel PROTO ((u_int8_t *, char *));
|
|
|
|
static int skipname PROTO ((u_int8_t *));
|
|
|
|
static int copy_out_name PROTO ((u_int8_t *, u_int8_t *, char *));
|
1997-05-09 08:02:33 +00:00
|
|
|
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;
|
1997-03-29 01:25:32 +00:00
|
|
|
|
1998-03-15 20:50:53 +00:00
|
|
|
#define DNS_QUERY_HASH_SIZE 293
|
|
|
|
struct dns_query *dns_query_hash [DNS_QUERY_HASH_SIZE];
|
1998-01-12 01:00:09 +00:00
|
|
|
|
1997-03-08 02:24:16 +00:00
|
|
|
/* Initialize the DNS protocol. */
|
|
|
|
|
1997-03-29 01:25:32 +00:00
|
|
|
void dns_startup ()
|
1997-03-08 02:24:16 +00:00
|
|
|
{
|
1997-03-29 01:25:32 +00:00
|
|
|
struct servent *srv;
|
1997-03-08 02:24:16 +00:00
|
|
|
struct sockaddr_in from;
|
|
|
|
|
|
|
|
/* Only initialize icmp once. */
|
1997-03-29 01:25:32 +00:00
|
|
|
if (dns_protocol_initialized)
|
|
|
|
error ("attempted to reinitialize dns protocol");
|
|
|
|
dns_protocol_initialized = 1;
|
1997-03-08 02:24:16 +00:00
|
|
|
|
|
|
|
/* Get the protocol number (should be 1). */
|
1997-05-09 08:02:33 +00:00
|
|
|
srv = getservbyname ("domain", "tcp");
|
1997-03-29 01:25:32 +00:00
|
|
|
if (srv)
|
1997-05-09 08:02:33 +00:00
|
|
|
dns_port = srv -> s_port;
|
|
|
|
else
|
|
|
|
dns_port = htons (53);
|
1997-03-08 02:24:16 +00:00
|
|
|
|
1997-05-09 08:02:33 +00:00
|
|
|
/* Get a socket for the DNS protocol. */
|
1997-03-29 01:25:32 +00:00
|
|
|
dns_protocol_fd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
1997-11-29 07:51:49 +00:00
|
|
|
if (dns_protocol_fd < 0)
|
1997-03-29 01:25:32 +00:00
|
|
|
error ("unable to create dns socket: %m");
|
1997-03-08 02:24:16 +00:00
|
|
|
|
1998-03-15 20:50:53 +00:00
|
|
|
first_name_server ();
|
1997-05-09 08:02:33 +00:00
|
|
|
|
1997-03-29 01:25:32 +00:00
|
|
|
add_protocol ("dns", dns_protocol_fd, dns_packet, 0);
|
1997-03-08 02:24:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Label manipulation stuff; see RFC1035, page 28 section 4.1.2 and
|
|
|
|
page 30, section 4.1.4. */
|
|
|
|
|
|
|
|
/* addlabel copies a label into the specified buffer, putting the length of
|
|
|
|
the label in the first character, the contents of the label in subsequent
|
|
|
|
characters, and returning the length of the conglomeration. */
|
|
|
|
|
1997-03-29 01:25:32 +00:00
|
|
|
static int addlabel (buf, label)
|
|
|
|
u_int8_t *buf;
|
|
|
|
char *label;
|
1997-03-08 02:24:16 +00:00
|
|
|
{
|
1997-03-29 01:25:32 +00:00
|
|
|
*buf = strlen (label);
|
|
|
|
memcpy (buf + 1, label, *buf);
|
|
|
|
return *buf + 1;
|
1997-03-08 02:24:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* skipname skips over all of the labels in a single domain name,
|
|
|
|
returning the length of the domain name. */
|
|
|
|
|
1997-03-29 01:25:32 +00:00
|
|
|
static int skipname (label)
|
|
|
|
u_int8_t *label;
|
1997-03-08 02:24:16 +00:00
|
|
|
{
|
1997-03-29 01:25:32 +00:00
|
|
|
if (*label & INDIR_MASK)
|
|
|
|
return 2;
|
|
|
|
if (*label == 0)
|
|
|
|
return 1;
|
|
|
|
return *label + 1 + skipname (label + *label + 1);
|
1997-03-08 02:24:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* copy_out_name copies out the name appearing at the specified location
|
|
|
|
into a string, stored as fields seperated by dots rather than lengths
|
|
|
|
and labels. The length of the label-formatted name is returned. */
|
|
|
|
|
1997-03-29 01:25:32 +00:00
|
|
|
static int copy_out_name (base, name, buf)
|
|
|
|
u_int8_t *base;
|
|
|
|
u_int8_t *name;
|
|
|
|
char *buf;
|
1997-03-08 02:24:16 +00:00
|
|
|
{
|
1997-03-29 01:25:32 +00:00
|
|
|
if (*name & INDIR_MASK) {
|
|
|
|
int offset = (*name & ~INDIR_MASK) + (*name + 1);
|
|
|
|
return copy_out_name (base, base + offset, buf);
|
|
|
|
}
|
|
|
|
if (!*name) {
|
|
|
|
*buf = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
memcpy (buf, name + 1, *name);
|
|
|
|
*(buf + *name) = '.';
|
|
|
|
return (*name + 1
|
|
|
|
+ copy_out_name (base, name + *name + 1, buf + *name + 1));
|
1997-03-08 02:24:16 +00:00
|
|
|
}
|
|
|
|
|
1998-03-15 20:50:53 +00:00
|
|
|
/* Compute a hash on the question. */
|
|
|
|
|
1998-03-16 06:17:37 +00:00
|
|
|
static INLINE u_int32_t dns_hash_question (struct dns_question *question)
|
1998-03-15 20:50:53 +00:00
|
|
|
{
|
|
|
|
u_int32_t sum;
|
|
|
|
u_int32_t remainder;
|
|
|
|
u_int32_t *p = (u_int32_t *)question;
|
|
|
|
u_int8_t *s;
|
|
|
|
|
|
|
|
/* First word. */
|
|
|
|
sum = *p++;
|
|
|
|
s = (u_int8_t *)p;
|
|
|
|
|
|
|
|
remainder = 0;
|
|
|
|
while (s [0]) {
|
|
|
|
remainder = s [0];
|
|
|
|
if (s [1]) {
|
|
|
|
remainder = (remainder << 8) + s [1];
|
|
|
|
if (s [2]) {
|
|
|
|
remainder = (remainder << 8) + s [2];
|
|
|
|
if (s [3])
|
|
|
|
remainder = (remainder << 8) + s [3];
|
|
|
|
else
|
|
|
|
goto done;
|
|
|
|
} else
|
|
|
|
goto done;
|
|
|
|
} else {
|
|
|
|
done:
|
|
|
|
sum += remainder;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ((sum & 0x80000000) && (remainder & 0x80000000))
|
|
|
|
++sum;
|
|
|
|
sum += remainder;
|
|
|
|
s += 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (sum > DNS_QUERY_HASH_SIZE) {
|
|
|
|
remainder = sum / DNS_QUERY_HASH_SIZE;
|
|
|
|
sum = sum % DNS_QUERY_HASH_SIZE;
|
|
|
|
while (remainder) {
|
|
|
|
sum += remainder % DNS_QUERY_HASH_SIZE;
|
|
|
|
remainder /= DNS_QUERY_HASH_SIZE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find a query that matches the specified name. If one can't be
|
|
|
|
found, and new is nonzero, allocate one, hash it in, and save the
|
|
|
|
question. Otherwise, if new is nonzero, free() the question.
|
|
|
|
Return the query if one was found or allocated. */
|
|
|
|
|
|
|
|
struct dns_query *find_dns_query (question, new)
|
|
|
|
struct dns_question *question;
|
|
|
|
int new;
|
|
|
|
{
|
|
|
|
int hash = dns_hash_question (question);
|
|
|
|
struct dns_query *q;
|
|
|
|
|
|
|
|
for (q = dns_query_hash [hash]; q; q = q -> next) {
|
|
|
|
if (q -> question -> type == question -> type &&
|
|
|
|
q -> question -> class == question -> class &&
|
1998-03-16 06:17:37 +00:00
|
|
|
!strcmp ((char *)q -> question -> data,
|
|
|
|
(char *)question -> data))
|
1998-03-15 20:50:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (q || !new) {
|
|
|
|
if (new)
|
|
|
|
free (question);
|
|
|
|
return q;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate and zap a new query. */
|
|
|
|
q = (struct dns_query *)malloc (sizeof (struct dns_query));
|
|
|
|
memset (q, 0, sizeof *q);
|
|
|
|
|
|
|
|
/* All we need to set up is the question and the hash. */
|
|
|
|
q -> question = question;
|
|
|
|
q -> next = dns_query_hash [hash];
|
|
|
|
dns_query_hash [hash] = q;
|
|
|
|
q -> hash = hash;
|
|
|
|
return q;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free up all memory associated with a DNS query and remove it from the
|
|
|
|
query hash. */
|
|
|
|
|
|
|
|
void destroy_dns_query (query)
|
|
|
|
struct dns_query *query;
|
|
|
|
{
|
|
|
|
struct dns_query *q;
|
|
|
|
|
|
|
|
/* Free up attached free data. */
|
|
|
|
if (query -> question)
|
|
|
|
free (query -> question);
|
|
|
|
if (query -> answer)
|
|
|
|
free (query -> answer);
|
|
|
|
if (query -> query)
|
|
|
|
free (query -> query);
|
|
|
|
|
|
|
|
/* Remove query from hash table. */
|
|
|
|
if (dns_query_hash [query -> hash] == query)
|
|
|
|
dns_query_hash [query -> hash] = query -> next;
|
|
|
|
else {
|
|
|
|
for (q = dns_query_hash [query -> hash];
|
|
|
|
q -> next && q -> next != query; q = q -> next)
|
|
|
|
;
|
|
|
|
if (q -> next)
|
|
|
|
q -> next = query -> next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free the query structure. */
|
|
|
|
free (query);
|
|
|
|
}
|
|
|
|
|
1997-03-08 02:24:16 +00:00
|
|
|
/* ns_inaddr_lookup constructs a PTR lookup query for an internet address -
|
1998-03-15 20:50:53 +00:00
|
|
|
e.g., 1.200.9.192.in-addr.arpa. It then passes it on to ns_query for
|
|
|
|
completion. */
|
1997-03-08 02:24:16 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
struct dns_query *ns_inaddr_lookup (inaddr, wakeup)
|
1997-03-29 01:25:32 +00:00
|
|
|
struct iaddr inaddr;
|
1998-01-12 01:00:09 +00:00
|
|
|
struct dns_wakeup *wakeup;
|
1997-03-08 02:24:16 +00:00
|
|
|
{
|
1998-03-15 20:50:53 +00:00
|
|
|
unsigned char query [512];
|
1998-01-12 01:00:09 +00:00
|
|
|
unsigned char *s;
|
1997-03-29 01:25:32 +00:00
|
|
|
unsigned char *label;
|
|
|
|
int i;
|
|
|
|
unsigned char c;
|
1998-03-15 20:50:53 +00:00
|
|
|
struct dns_question *question;
|
|
|
|
|
|
|
|
/* First format the query in the internal format. */
|
1998-03-16 06:17:37 +00:00
|
|
|
sprintf ((char *)query, "%d.%d.%d.%d.in-addr.arpa.",
|
1998-03-15 20:50:53 +00:00
|
|
|
inaddr.iabuf [0], inaddr.iabuf [1],
|
|
|
|
inaddr.iabuf [2], inaddr.iabuf [3]);
|
1997-03-29 01:25:32 +00:00
|
|
|
|
1998-03-16 06:17:37 +00:00
|
|
|
question = (struct dns_question *)malloc (strlen ((char *)query) +
|
1998-03-15 20:50:53 +00:00
|
|
|
sizeof *question);
|
|
|
|
if (!question)
|
|
|
|
return (struct dns_query *)-1;
|
|
|
|
question -> type = T_PTR;
|
|
|
|
question -> class = C_IN;
|
1998-03-16 06:17:37 +00:00
|
|
|
strcpy ((char *)question -> data, (char *)query);
|
1998-03-15 20:50:53 +00:00
|
|
|
|
|
|
|
/* Now format the query for the name server. */
|
|
|
|
s = query;
|
1998-01-12 01:00:09 +00:00
|
|
|
|
|
|
|
/* Copy out the digits. */
|
1997-03-29 01:25:32 +00:00
|
|
|
for (i = 3; i >= 0; --i) {
|
|
|
|
label = s++;
|
1998-03-16 06:17:37 +00:00
|
|
|
sprintf ((char *)s, "%d", inaddr.iabuf [i]);
|
|
|
|
*label = strlen ((char *)s);
|
1998-03-15 20:50:53 +00:00
|
|
|
s += *label;
|
1997-03-08 02:24:16 +00:00
|
|
|
}
|
1997-03-29 01:25:32 +00:00
|
|
|
s += addlabel (s, "in-addr");
|
|
|
|
s += addlabel (s, "arpa");
|
|
|
|
*s++ = 0;
|
1997-03-08 02:24:16 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
/* 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);
|
|
|
|
|
1998-03-15 20:50:53 +00:00
|
|
|
return ns_query (question, query, s - query, wakeup);
|
1998-01-12 01:00:09 +00:00
|
|
|
}
|
1997-03-08 02:24:16 +00:00
|
|
|
|
1998-03-15 20:50:53 +00:00
|
|
|
/* Try to satisfy a query out of the local cache. If no answer has
|
|
|
|
been cached, and if there isn't already a query pending on this
|
|
|
|
question, send it. If the query can be immediately satisfied,
|
|
|
|
a pointer to the dns_query structure is returned. If the query
|
|
|
|
can't even be made for some reason, (struct dns_query *)-1 is
|
|
|
|
returned. Otherwise, the null pointer is returned, indicating that
|
|
|
|
a wakeup will be performed later when the answer comes back. */
|
|
|
|
|
|
|
|
struct dns_query *ns_query (question, formatted_query, len, wakeup)
|
|
|
|
struct dns_question *question;
|
|
|
|
unsigned char *formatted_query;
|
1998-01-12 01:00:09 +00:00
|
|
|
int len;
|
|
|
|
struct dns_wakeup *wakeup;
|
1997-03-08 02:24:16 +00:00
|
|
|
{
|
1997-03-29 01:25:32 +00:00
|
|
|
HEADER *hdr;
|
1998-01-12 01:00:09 +00:00
|
|
|
struct dns_query *query;
|
|
|
|
unsigned char *s;
|
1998-03-15 20:50:53 +00:00
|
|
|
unsigned char buf [512];
|
|
|
|
|
|
|
|
/* If the query won't fit, don't bother setting it up. */
|
|
|
|
if (len > 255) {
|
|
|
|
free (question);
|
|
|
|
return (struct dns_query *)-1;
|
|
|
|
}
|
1998-01-12 01:00:09 +00:00
|
|
|
|
|
|
|
/* See if there's already a query for this name, and allocate a
|
|
|
|
query if none exists. */
|
1998-03-15 20:50:53 +00:00
|
|
|
query = find_dns_query (question, 1);
|
1998-01-12 01:00:09 +00:00
|
|
|
|
|
|
|
/* If we can't allocate a query, report that the query failed. */
|
1998-03-15 20:50:53 +00:00
|
|
|
if (!query)
|
1998-01-12 01:00:09 +00:00
|
|
|
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;
|
1997-06-08 04:05:21 +00:00
|
|
|
|
1997-03-29 01:25:32 +00:00
|
|
|
/* Construct a header... */
|
1998-03-15 20:50:53 +00:00
|
|
|
hdr = (HEADER *)buf;
|
1997-05-09 08:02:33 +00:00
|
|
|
memset (hdr, 0, sizeof *hdr);
|
1998-01-12 01:00:09 +00:00
|
|
|
hdr -> id = query -> id;
|
1997-03-29 01:25:32 +00:00
|
|
|
hdr -> rd = 1;
|
|
|
|
hdr -> opcode = QUERY;
|
|
|
|
hdr -> qdcount = htons (1);
|
|
|
|
|
1998-03-15 20:50:53 +00:00
|
|
|
/* Copy the formatted name into the buffer. */
|
1998-01-12 01:00:09 +00:00
|
|
|
s = (unsigned char *)hdr + 1;
|
1998-03-15 20:50:53 +00:00
|
|
|
memcpy (s, formatted_query, len);
|
1997-03-29 01:25:32 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
/* Figure out how long the whole message is */
|
|
|
|
s += len;
|
1998-03-15 20:50:53 +00:00
|
|
|
query -> len = s - buf;
|
|
|
|
|
|
|
|
/* Save the raw query data. */
|
|
|
|
query -> query = malloc (len);
|
|
|
|
if (!query -> query) {
|
|
|
|
destroy_dns_query (query);
|
|
|
|
return (struct dns_query *)-1;
|
|
|
|
}
|
1998-03-17 06:09:59 +00:00
|
|
|
memcpy (query -> query, buf, query -> len);
|
1998-01-12 01:00:09 +00:00
|
|
|
|
|
|
|
/* Flag the query as having been sent. */
|
|
|
|
query -> sent = 1;
|
1997-03-29 01:25:32 +00:00
|
|
|
|
|
|
|
/* Send the query. */
|
1998-01-12 01:00:09 +00:00
|
|
|
dns_timeout (query);
|
1997-03-29 01:25:32 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
/* No answer yet, obviously. */
|
|
|
|
return (struct dns_query *)0;
|
1997-03-08 02:24:16 +00:00
|
|
|
}
|
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
/* Retransmit a DNS query. */
|
1997-05-09 08:02:33 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
void dns_timeout (qv)
|
|
|
|
void *qv;
|
|
|
|
{
|
|
|
|
struct dns_query *query = qv;
|
|
|
|
int status;
|
1997-05-09 08:02:33 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
/* Choose the server to send to. */
|
|
|
|
if (!query -> next_server)
|
|
|
|
query -> next_server = first_name_server ();
|
1997-05-09 08:02:33 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
/* Send the query. */
|
|
|
|
if (query -> next_server)
|
|
|
|
status = sendto (dns_protocol_fd,
|
1998-03-16 06:17:37 +00:00
|
|
|
(char *)query -> query, query -> len, 0,
|
1998-01-12 01:00:09 +00:00
|
|
|
((struct sockaddr *)&query ->
|
|
|
|
next_server -> addr),
|
|
|
|
sizeof query -> next_server -> addr);
|
|
|
|
else
|
|
|
|
status = -1;
|
1997-06-08 04:05:21 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
/* Look for the next server... */
|
|
|
|
query -> next_server = query -> next_server -> next;
|
1997-05-09 08:02:33 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
/* If this is our first time, backoff one second. */
|
|
|
|
if (!query -> backoff)
|
|
|
|
query -> backoff = 1;
|
1997-05-09 08:02:33 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
/* If the send failed, don't advance the backoff. */
|
|
|
|
else if (status < 0)
|
|
|
|
;
|
1997-05-09 08:02:33 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
/* 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;
|
1997-05-09 08:02:33 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
/* Set up the timeout. */
|
|
|
|
add_timeout (cur_time + query -> backoff, dns_timeout, query);
|
1997-05-09 08:02:33 +00:00
|
|
|
}
|
|
|
|
|
1997-03-29 01:25:32 +00:00
|
|
|
/* Process a reply from a name server. */
|
|
|
|
|
1997-05-09 08:02:33 +00:00
|
|
|
void dns_packet (protocol)
|
1997-03-29 01:25:32 +00:00
|
|
|
struct protocol *protocol;
|
1997-03-08 02:24:16 +00:00
|
|
|
{
|
1997-03-29 01:25:32 +00:00
|
|
|
HEADER *ns_header;
|
|
|
|
struct sockaddr_in from;
|
1998-01-12 01:00:09 +00:00
|
|
|
struct dns_wakeup *wakeup;
|
|
|
|
unsigned char buf [512];
|
1998-03-15 20:50:53 +00:00
|
|
|
union {
|
|
|
|
unsigned char u [512];
|
|
|
|
struct dns_question q;
|
|
|
|
} qbuf;
|
1997-03-29 01:25:32 +00:00
|
|
|
unsigned char *base;
|
1998-01-12 01:00:09 +00:00
|
|
|
unsigned char *dptr, *name;
|
1997-05-09 08:02:33 +00:00
|
|
|
u_int16_t type;
|
|
|
|
u_int16_t class;
|
|
|
|
TIME ttl;
|
|
|
|
u_int16_t rdlength;
|
|
|
|
int len, status;
|
|
|
|
int i;
|
1998-01-12 01:00:09 +00:00
|
|
|
struct dns_query *query;
|
1997-03-29 01:25:32 +00:00
|
|
|
|
|
|
|
len = sizeof from;
|
1998-03-16 06:17:37 +00:00
|
|
|
status = recvfrom (protocol -> fd, (char *)buf, sizeof buf, 0,
|
1997-03-29 01:25:32 +00:00
|
|
|
(struct sockaddr *)&from, &len);
|
|
|
|
if (status < 0) {
|
1998-01-12 01:00:09 +00:00
|
|
|
warn ("dns_packet: %m");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Response is too long? */
|
|
|
|
if (len > 512) {
|
|
|
|
warn ("dns_packet: dns message too long (%d)", len);
|
1997-03-29 01:25:32 +00:00
|
|
|
return;
|
|
|
|
}
|
1997-03-08 02:24:16 +00:00
|
|
|
|
1997-03-29 01:25:32 +00:00
|
|
|
ns_header = (HEADER *)buf;
|
|
|
|
base = (unsigned char *)(ns_header + 1);
|
1997-03-08 02:24:16 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
/* 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) {
|
1998-03-15 20:50:53 +00:00
|
|
|
warn ("Bogus DNS answer packet from %s claims %d queries.\n",
|
|
|
|
inet_ntoa (from.sin_addr),
|
|
|
|
ntohs (ns_header -> qdcount));
|
1997-03-29 01:25:32 +00:00
|
|
|
return;
|
|
|
|
}
|
1997-03-08 02:24:16 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
/* Find the start of the name in the query. */
|
|
|
|
name = dptr;
|
1997-03-08 02:24:16 +00:00
|
|
|
|
1998-01-12 01:00:09 +00:00
|
|
|
/* Skip over the name. */
|
1998-03-16 06:17:37 +00:00
|
|
|
dptr += copy_out_name (name, name, (char *)qbuf.q.data);
|
1998-01-12 01:00:09 +00:00
|
|
|
|
|
|
|
/* Skip over the query type and query class. */
|
1998-03-15 20:50:53 +00:00
|
|
|
qbuf.q.type = getUShort (dptr);
|
|
|
|
dptr += sizeof (u_int16_t);
|
|
|
|
qbuf.q.class = getUShort (dptr);
|
|
|
|
dptr += sizeof (u_int16_t);
|
1998-01-12 01:00:09 +00:00
|
|
|
|
|
|
|
/* See if we asked this question. */
|
1998-03-15 20:50:53 +00:00
|
|
|
query = find_dns_query (&qbuf.q, 0);
|
1998-01-12 01:00:09 +00:00
|
|
|
if (!query) {
|
1998-03-15 20:50:53 +00:00
|
|
|
warn ("got answer for question %s from DNS, which we didn't ask.",
|
|
|
|
qbuf.q.data);
|
1998-01-12 01:00:09 +00:00
|
|
|
return;
|
1997-05-09 08:02:33 +00:00
|
|
|
}
|
1997-03-29 01:25:32 +00:00
|
|
|
|
1998-03-15 20:50:53 +00:00
|
|
|
note ("got answer for question %s from DNS", qbuf.q.data);
|
1998-01-12 01:00:09 +00:00
|
|
|
|
|
|
|
/* Wake up everybody who's waiting. */
|
|
|
|
for (wakeup = query -> wakeups; wakeup; wakeup = wakeup -> next) {
|
|
|
|
(*wakeup -> func) (query);
|
1997-03-29 01:25:32 +00:00
|
|
|
}
|
1997-03-08 02:24:16 +00:00
|
|
|
}
|