2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-22 09:57:20 +00:00

383 lines
10 KiB
C
Raw Normal View History

1997-03-08 02:24:16 +00:00
/* dns.c
Domain Name Service subroutines. */
1997-03-08 02:24:16 +00:00
/*
* Copyright (C) 1992 by Ted Lemon.
* Copyright (c) 1997 The Internet Software Consortium.
* 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.
*
* 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[] =
"$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";
1997-03-08 02:24:16 +00:00
#endif /* not lint */
#include "dhcpd.h"
#include "arpa/nameser.h"
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 *));
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];
1997-03-08 02:24:16 +00:00
/* Initialize the DNS protocol. */
void dns_startup ()
1997-03-08 02:24:16 +00:00
{
struct servent *srv;
1997-03-08 02:24:16 +00:00
struct sockaddr_in from;
/* Only initialize icmp once. */
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). */
srv = getservbyname ("domain", "tcp");
if (srv)
dns_port = srv -> s_port;
else
dns_port = htons (53);
1997-03-08 02:24:16 +00:00
/* Get a socket for the DNS protocol. */
dns_protocol_fd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (dns_protocol_fd < 0)
error ("unable to create dns socket: %m");
1997-03-08 02:24:16 +00:00
pick_name_server ();
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. */
static int addlabel (buf, label)
u_int8_t *buf;
char *label;
1997-03-08 02:24:16 +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. */
static int skipname (label)
u_int8_t *label;
1997-03-08 02:24:16 +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. */
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
{
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
}
/* ns_inaddr_lookup constructs a PTR lookup query for an internet address -
e.g., 1.200.9.192.in-addr.arpa. If the specified timeout period passes
before the query is satisfied, or if the query fails, the callback is
called with a null pointer. Otherwise, the callback is called with the
address of the string returned by the name server. */
struct dns_query *ns_inaddr_lookup (inaddr, wakeup)
struct iaddr inaddr;
struct dns_wakeup *wakeup;
1997-03-08 02:24:16 +00:00
{
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;
c = inaddr.iabuf [i];
if (c > 100) {
++*label;
*s++ = '0' + c / 100;
}
if (c > 10) {
++*label;
*s++ = '0' + ((c / 10) % 10);
}
*s++ = '0' + (c % 10);
1997-03-08 02:24:16 +00:00
}
s += addlabel (s, "in-addr");
s += addlabel (s, "arpa");
*s++ = 0;
1997-03-08 02:24:16 +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);
return ns_query (question, s - question, wakeup);
}
1997-03-08 02:24:16 +00:00
struct dns_query *ns_query (question, len, wakeup)
unsigned char *question;
int len;
struct dns_wakeup *wakeup;
1997-03-08 02:24:16 +00:00
{
HEADER *hdr;
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 -> buf;
memset (hdr, 0, sizeof *hdr);
hdr -> id = query -> id;
hdr -> rd = 1;
hdr -> opcode = QUERY;
hdr -> qdcount = htons (1);
/* Copy the name into the buffer. */
s = (unsigned char *)hdr + 1;
memcpy (s, question, len);
query -> question = s;
query -> question_len = len;
/* 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. */
dns_timeout (query);
/* No answer yet, obviously. */
return (struct dns_query *)0;
1997-03-08 02:24:16 +00:00
}
/* Retransmit a DNS query. */
void dns_timeout (qv)
void *qv;
{
struct dns_query *query = qv;
int status;
/* Choose the server to send to. */
if (!query -> next_server)
query -> next_server = first_name_server ();
/* 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;
/* Look for the next server... */
query -> next_server = query -> next_server -> next;
/* If this is our first time, backoff one second. */
if (!query -> backoff)
query -> backoff = 1;
/* If the send failed, don't advance the backoff. */
else if (status < 0)
;
/* 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;
/* Set up the timeout. */
add_timeout (cur_time + query -> backoff, dns_timeout, query);
}
/* Process a reply from a name server. */
void dns_packet (protocol)
struct protocol *protocol;
1997-03-08 02:24:16 +00:00
{
HEADER *ns_header;
struct sockaddr_in from;
struct dns_wakeup *wakeup;
unsigned char buf [512];
unsigned char *base;
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 ("dns_packet: %m");
return;
}
/* Response is too long? */
if (len > 512) {
warn ("dns_packet: dns message too long (%d)", len);
return;
}
1997-03-08 02:24:16 +00:00
ns_header = (HEADER *)buf;
base = (unsigned char *)(ns_header + 1);
1997-03-08 02:24:16 +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) {
dns_bogus (buf, len);
return;
}
1997-03-08 02:24:16 +00:00
/* Find the start of the name in the query. */
name = dptr;
1997-03-08 02:24:16 +00:00
/* 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;
}
/* 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);
}
1997-03-08 02:24:16 +00:00
}