mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
import of dnsmasq-2.36.tar.gz
This commit is contained in:
222
src/forward.c
222
src/forward.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user