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"
{
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
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))
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);
/* 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 */
}
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;
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. */
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
{
/* 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
#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
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 */
}
}
}
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;
+ ifr.ifr_ifindex = if_index;
+ if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1)
+ return;
#else
- if (!if_indextoname(if_index, ifr.ifr_name))
- return;
+ 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))
+
+ 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))
#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;
}
{
f->next = frec_list;
f->time = now;
- f->new_id = 0;
+ f->sentto = NULL;
frec_list = f;
}
*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;
if (!wait)
{
- oldest->new_id = 0;
+ oldest->sentto = 0;
oldest->time = now;
}
return oldest;
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;
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))
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;
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;
}