import of dnsmasq-2.36.tar.gz

This commit is contained in:
Simon Kelley
2007-01-21 20:01:28 +00:00
parent 4011c4e05e
commit 832af0bafb
31 changed files with 5064 additions and 2851 deletions

View File

@@ -14,11 +14,11 @@
static struct frec *frec_list = NULL;
static struct frec *lookup_frec(unsigned short id);
static struct frec *lookup_frec(unsigned short id, unsigned int crc);
static struct frec *lookup_frec_by_sender(unsigned short id,
union mysockaddr *addr,
unsigned int crc);
static unsigned short get_id(void);
static unsigned short get_id(int force, unsigned short force_id, unsigned int crc);
/* Send a UDP packet with it's source address set as "source"
@@ -163,7 +163,7 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
{
if ((sflag | F_QUERY ) & qtype)
{
flags = qtype;
flags = qtype & ~F_BIGNAME;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
@@ -184,7 +184,9 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
else
log_query(F_CONFIG | F_FORWARD | flags, qdomain, *addrpp, 0, NULL, 0);
}
else if (qtype && (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
else if (qtype && !(qtype & F_BIGNAME) &&
(daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
/* don't forward simple names, make exception from NS queries and empty name. */
flags = F_NXDOMAIN;
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon))
@@ -237,12 +239,16 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
if (forward)
{
/* force unchanging id for signed packets */
int is_sign;
find_pseudoheader(header, plen, NULL, NULL, &is_sign);
forward->source = *udpaddr;
forward->dest = *dst_addr;
forward->iface = dst_iface;
forward->new_id = get_id();
forward->fd = udpfd;
forward->orig_id = ntohs(header->id);
forward->new_id = get_id(is_sign, forward->orig_id, crc);
forward->fd = udpfd;
forward->crc = crc;
forward->forwardall = 0;
header->id = htons(forward->new_id);
@@ -325,7 +331,7 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
/* could not send on, prepare to return */
header->id = htons(forward->orig_id);
forward->new_id = 0; /* cancel */
forward->sentto = NULL; /* cancel */
}
/* could not send on, return empty answer or address if known for whole domain */
@@ -339,17 +345,17 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
}
static size_t process_reply(struct daemon *daemon, HEADER *header, time_t now,
unsigned int query_crc, struct server *server, size_t n)
struct server *server, size_t n)
{
unsigned char *pheader, *sizep;
int munged = 0;
int munged = 0, is_sign;
size_t plen;
/* If upstream is advertising a larger UDP packet size
than we allow, trim it so that we don't get overlarge
requests for the client. */
requests for the client. We can't do this for signed packets. */
if ((pheader = find_pseudoheader(header, n, &plen, &sizep)))
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)) && !is_sign)
{
unsigned short udpsz;
unsigned char *psave = sizep;
@@ -359,7 +365,7 @@ static size_t process_reply(struct daemon *daemon, HEADER *header, time_t now,
PUTSHORT(daemon->edns_pktsz, psave);
}
if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
if (is_sign || header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
return n;
/* Complain loudly if the upstream server is non-recursive. */
@@ -392,12 +398,8 @@ static size_t process_reply(struct daemon *daemon, HEADER *header, time_t now,
header->aa = 1;
header->rcode = NOERROR;
}
/* If the crc of the question section doesn't match the crc we sent, then
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
if (query_crc == questions_crc(header, n, daemon->namebuff))
extract_addresses(header, n, daemon->namebuff, now, daemon);
extract_addresses(header, n, daemon->namebuff, now, daemon);
}
/* do this after extract_addresses. Ensure NODATA reply and remove
@@ -421,16 +423,16 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
{
/* packet from peer server, extract data for cache, and send to
original requester */
struct frec *forward;
HEADER *header;
union mysockaddr serveraddr;
struct frec *forward;
socklen_t addrlen = sizeof(serveraddr);
ssize_t n = recvfrom(sfd->fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
size_t nn;
/* packet buffer overwritten */
daemon->srv_save = NULL;
/* Determine the address of the server replying so that we can mark that as good */
serveraddr.sa.sa_family = sfd->source_addr.sa.sa_family;
#ifdef HAVE_IPV6
@@ -439,51 +441,55 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
#endif
header = (HEADER *)daemon->packet;
forward = lookup_frec(ntohs(header->id));
if (n >= (int)sizeof(HEADER) && header->qr && forward)
if (n >= (int)sizeof(HEADER) && header->qr &&
(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
{
struct server *server = forward->sentto;
if ((header->rcode == SERVFAIL || header->rcode == REFUSED) && forward->forwardall == 0)
/* for broken servers, attempt to send to another one. */
{
unsigned char *pheader;
size_t plen;
/* recreate query from reply */
pheader = find_pseudoheader(header, (size_t)n, &plen, NULL);
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{
header->qr = 0;
header->tc = 0;
forward_query(daemon, -1, NULL, NULL, 0, header, nn, now, forward);
return;
}
}
if ((forward->sentto->flags & SERV_TYPE) == 0)
{
if (header->rcode == SERVFAIL || header->rcode == REFUSED)
server = NULL;
else
{
struct server *last_server;
/* find good server by address if possible, otherwise assume the last one we sent to */
for (last_server = daemon->servers; last_server; last_server = last_server->next)
if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
sockaddr_isequal(&last_server->addr, &serveraddr))
{
server = last_server;
break;
}
}
daemon->last_server = server;
}
struct server *server = forward->sentto;
if ((header->rcode == SERVFAIL || header->rcode == REFUSED) && forward->forwardall == 0)
/* for broken servers, attempt to send to another one. */
{
unsigned char *pheader;
size_t plen;
int is_sign;
/* recreate query from reply */
pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign);
if (!is_sign)
{
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{
header->qr = 0;
header->tc = 0;
forward_query(daemon, -1, NULL, NULL, 0, header, nn, now, forward);
return;
}
}
}
if ((forward->sentto->flags & SERV_TYPE) == 0)
{
if (header->rcode == SERVFAIL || header->rcode == REFUSED)
server = NULL;
else
{
struct server *last_server;
/* find good server by address if possible, otherwise assume the last one we sent to */
for (last_server = daemon->servers; last_server; last_server = last_server->next)
if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
sockaddr_isequal(&last_server->addr, &serveraddr))
{
server = last_server;
break;
}
}
daemon->last_server = server;
}
/* If the answer is an error, keep the forward record in place in case
we get a good reply from another server. Kill it when we've
had replies from all to avoid filling the forwarding table when
@@ -491,14 +497,14 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
if (forward->forwardall == 0 || --forward->forwardall == 1 ||
(header->rcode != REFUSED && header->rcode != SERVFAIL))
{
if ((nn = process_reply(daemon, header, now, forward->crc, server, (size_t)n)))
if ((nn = process_reply(daemon, header, now, server, (size_t)n)))
{
header->id = htons(forward->orig_id);
header->ra = 1; /* recursion if available */
send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, nn,
&forward->source, &forward->dest, forward->iface);
}
forward->new_id = 0; /* cancel */
forward->sentto = NULL; /* cancel */
}
}
}
@@ -611,27 +617,24 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
if (if_index == 0)
return;
if (daemon->if_except || daemon->if_names || (daemon->options & OPT_LOCALISE))
{
#ifdef SIOCGIFNAME
ifr.ifr_ifindex = if_index;
if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1)
return;
#else
if (!if_indextoname(if_index, ifr.ifr_name))
return;
#endif
if (listen->family == AF_INET &&
(daemon->options & OPT_LOCALISE) &&
ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
return;
netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
if (!iface_check(daemon, listen->family, &dst_addr, ifr.ifr_name))
ifr.ifr_ifindex = if_index;
if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1)
return;
#else
if (!if_indextoname(if_index, ifr.ifr_name))
return;
#endif
if (!iface_check(daemon, listen->family, &dst_addr, &ifr, &if_index))
return;
if (listen->family == AF_INET &&
(daemon->options & OPT_LOCALISE) &&
ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
return;
netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
if (extract_request(header, (size_t)n, daemon->namebuff, &type))
@@ -788,9 +791,13 @@ unsigned char *tcp_request(struct daemon *daemon, int confd, time_t now,
#endif
/* There's no point in updating the cache, since this process will exit and
lose the information after one query. We make this call for the alias and
lose the information after a few queries. We make this call for the alias and
bogus-nxdomain side-effects. */
m = process_reply(daemon, header, now, crc, last_server, (unsigned int)m);
/* If the crc of the question section doesn't match the crc we sent, then
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
m = process_reply(daemon, header, now, last_server, (unsigned int)m);
break;
}
@@ -818,7 +825,7 @@ static struct frec *allocate_frec(time_t now)
{
f->next = frec_list;
f->time = now;
f->new_id = 0;
f->sentto = NULL;
frec_list = f;
}
@@ -837,7 +844,7 @@ struct frec *get_new_frec(struct daemon *daemon, time_t now, int *wait)
*wait = 0;
for (f = frec_list, oldest = NULL, count = 0; f; f = f->next, count++)
if (f->new_id == 0)
if (!f->sentto)
{
f->time = now;
return f;
@@ -858,7 +865,7 @@ struct frec *get_new_frec(struct daemon *daemon, time_t now, int *wait)
if (!wait)
{
oldest->new_id = 0;
oldest->sentto = 0;
oldest->time = now;
}
return oldest;
@@ -879,12 +886,14 @@ struct frec *get_new_frec(struct daemon *daemon, time_t now, int *wait)
return f; /* OK if malloc fails and this is NULL */
}
static struct frec *lookup_frec(unsigned short id)
/* crc is all-ones if not known. */
static struct frec *lookup_frec(unsigned short id, unsigned int crc)
{
struct frec *f;
for(f = frec_list; f; f = f->next)
if (f->new_id == id)
if (f->sentto && f->new_id == id &&
(f->crc == crc || crc == 0xffffffff))
return f;
return NULL;
@@ -897,7 +906,7 @@ static struct frec *lookup_frec_by_sender(unsigned short id,
struct frec *f;
for(f = frec_list; f; f = f->next)
if (f->new_id &&
if (f->sentto &&
f->orig_id == id &&
f->crc == crc &&
sockaddr_isequal(&f->source, addr))
@@ -912,8 +921,8 @@ void server_gone(struct daemon *daemon, struct server *server)
struct frec *f;
for (f = frec_list; f; f = f->next)
if (f->new_id != 0 && f->sentto == server)
f->new_id = 0;
if (f->sentto && f->sentto == server)
f->sentto = NULL;
if (daemon->last_server == server)
daemon->last_server = NULL;
@@ -922,20 +931,25 @@ void server_gone(struct daemon *daemon, struct server *server)
daemon->srv_save = NULL;
}
/* return unique random ids between 1 and 65535 */
static unsigned short get_id(void)
/* return unique random ids.
For signed packets we can't change the ID without breaking the
signing, so we keep the same one. In this case force is set, and this
routine degenerates into killing any conflicting forward record. */
static unsigned short get_id(int force, unsigned short force_id, unsigned int crc)
{
unsigned short ret = 0;
while (ret == 0)
if (force)
{
ret = rand16();
/* scrap ids already in use */
if ((ret != 0) && lookup_frec(ret))
ret = 0;
struct frec *f = lookup_frec(force_id, crc);
if (f)
f->sentto = NULL; /* free */
ret = force_id;
}
else do
ret = rand16();
while (lookup_frec(ret, crc));
return ret;
}