version 1.1.1.3, 2016/11/02 09:57:01
|
version 1.1.1.4, 2021/03/17 00:56:46
|
Line 1
|
Line 1
|
/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley | /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley |
|
|
This program is free software; you can redistribute it and/or modify |
This program is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
it under the terms of the GNU General Public License as published by |
Line 16
|
Line 16
|
|
|
#include "dnsmasq.h" |
#include "dnsmasq.h" |
|
|
static struct frec *lookup_frec(unsigned short id, void *hash); | static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash); |
static struct frec *lookup_frec_by_sender(unsigned short id, |
static struct frec *lookup_frec_by_sender(unsigned short id, |
union mysockaddr *addr, |
union mysockaddr *addr, |
void *hash); |
void *hash); |
|
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags); |
|
|
static unsigned short get_id(void); |
static unsigned short get_id(void); |
static void free_frec(struct frec *f); |
static void free_frec(struct frec *f); |
|
|
/* Send a UDP packet with its source address set as "source" |
/* Send a UDP packet with its source address set as "source" |
unless nowild is true, when we just send it with the kernel default */ |
unless nowild is true, when we just send it with the kernel default */ |
int send_from(int fd, int nowild, char *packet, size_t len, |
int send_from(int fd, int nowild, char *packet, size_t len, |
union mysockaddr *to, struct all_addr *source, | union mysockaddr *to, union all_addr *source, |
unsigned int iface) |
unsigned int iface) |
{ |
{ |
struct msghdr msg; |
struct msghdr msg; |
Line 38 int send_from(int fd, int nowild, char *packet, size_t
|
Line 40 int send_from(int fd, int nowild, char *packet, size_t
|
#elif defined(IP_SENDSRCADDR) |
#elif defined(IP_SENDSRCADDR) |
char control[CMSG_SPACE(sizeof(struct in_addr))]; |
char control[CMSG_SPACE(sizeof(struct in_addr))]; |
#endif |
#endif |
#ifdef HAVE_IPV6 |
|
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; |
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; |
#endif |
|
} control_u; |
} control_u; |
|
|
iov[0].iov_base = packet; |
iov[0].iov_base = packet; |
Line 66 int send_from(int fd, int nowild, char *packet, size_t
|
Line 66 int send_from(int fd, int nowild, char *packet, size_t
|
#if defined(HAVE_LINUX_NETWORK) |
#if defined(HAVE_LINUX_NETWORK) |
struct in_pktinfo p; |
struct in_pktinfo p; |
p.ipi_ifindex = 0; |
p.ipi_ifindex = 0; |
p.ipi_spec_dst = source->addr.addr4; | p.ipi_spec_dst = source->addr4; |
| msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); |
memcpy(CMSG_DATA(cmptr), &p, sizeof(p)); |
memcpy(CMSG_DATA(cmptr), &p, sizeof(p)); |
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); | cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); |
cmptr->cmsg_level = IPPROTO_IP; |
cmptr->cmsg_level = IPPROTO_IP; |
cmptr->cmsg_type = IP_PKTINFO; |
cmptr->cmsg_type = IP_PKTINFO; |
#elif defined(IP_SENDSRCADDR) |
#elif defined(IP_SENDSRCADDR) |
memcpy(CMSG_DATA(cmptr), &(source->addr.addr4), sizeof(source->addr.addr4)); | msg.msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); |
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); | memcpy(CMSG_DATA(cmptr), &(source->addr4), sizeof(source->addr4)); |
| cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); |
cmptr->cmsg_level = IPPROTO_IP; |
cmptr->cmsg_level = IPPROTO_IP; |
cmptr->cmsg_type = IP_SENDSRCADDR; |
cmptr->cmsg_type = IP_SENDSRCADDR; |
#endif |
#endif |
} |
} |
else |
else |
#ifdef HAVE_IPV6 |
|
{ |
{ |
struct in6_pktinfo p; |
struct in6_pktinfo p; |
p.ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */ |
p.ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */ |
p.ipi6_addr = source->addr.addr6; | p.ipi6_addr = source->addr6; |
| msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); |
memcpy(CMSG_DATA(cmptr), &p, sizeof(p)); |
memcpy(CMSG_DATA(cmptr), &p, sizeof(p)); |
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); | cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); |
cmptr->cmsg_type = daemon->v6pktinfo; |
cmptr->cmsg_type = daemon->v6pktinfo; |
cmptr->cmsg_level = IPPROTO_IPV6; |
cmptr->cmsg_level = IPPROTO_IPV6; |
} |
} |
#else |
|
(void)iface; /* eliminate warning */ |
|
#endif |
|
} |
} |
|
|
while (retry_send(sendmsg(fd, &msg, 0))); |
while (retry_send(sendmsg(fd, &msg, 0))); |
|
|
/* If interface is still in DAD, EINVAL results - ignore that. */ | if (errno != 0) |
if (errno != 0 && errno != EINVAL) | |
{ |
{ |
my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno)); | #ifdef HAVE_LINUX_NETWORK |
| /* If interface is still in DAD, EINVAL results - ignore that. */ |
| if (errno != EINVAL) |
| my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno)); |
| #endif |
return 0; |
return 0; |
} |
} |
|
|
return 1; |
return 1; |
} |
} |
|
|
static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigned int qtype, | static unsigned int search_servers(time_t now, union all_addr **addrpp, unsigned int qtype, |
char *qdomain, int *type, char **domain, int *norebind) |
char *qdomain, int *type, char **domain, int *norebind) |
|
|
{ |
{ |
Line 118 static unsigned int search_servers(time_t now, struct
|
Line 120 static unsigned int search_servers(time_t now, struct
|
unsigned int matchlen = 0; |
unsigned int matchlen = 0; |
struct server *serv; |
struct server *serv; |
unsigned int flags = 0; |
unsigned int flags = 0; |
|
static union all_addr zero; |
|
|
for (serv = daemon->servers; serv; serv=serv->next) |
for (serv = daemon->servers; serv; serv=serv->next) |
|
if (qtype == F_DNSSECOK && !(serv->flags & SERV_DO_DNSSEC)) |
|
continue; |
/* domain matches take priority over NODOTS matches */ |
/* domain matches take priority over NODOTS matches */ |
if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.') && namelen != 0) | else if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.') && namelen != 0) |
{ |
{ |
unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; |
unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; |
*type = SERV_FOR_NODOTS; |
*type = SERV_FOR_NODOTS; |
if (serv->flags & SERV_NO_ADDR) | if ((serv->flags & SERV_NO_REBIND) && norebind) |
| *norebind = 1; |
| else if (serv->flags & SERV_NO_ADDR) |
flags = F_NXDOMAIN; |
flags = F_NXDOMAIN; |
else if (serv->flags & SERV_LITERAL_ADDRESS) | else if (serv->flags & SERV_LITERAL_ADDRESS) |
{ |
{ |
if (sflag & qtype) | /* literal address = '#' -> return all-zero address for IPv4 and IPv6 */ |
| if ((serv->flags & SERV_USE_RESOLV) && (qtype & (F_IPV6 | F_IPV4))) |
{ |
{ |
|
memset(&zero, 0, sizeof(zero)); |
|
flags = qtype; |
|
*addrpp = &zero; |
|
} |
|
else if (sflag & qtype) |
|
{ |
flags = sflag; |
flags = sflag; |
if (serv->addr.sa.sa_family == AF_INET) |
if (serv->addr.sa.sa_family == AF_INET) |
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr; | *addrpp = (union all_addr *)&serv->addr.in.sin_addr; |
#ifdef HAVE_IPV6 | |
else |
else |
*addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr; | *addrpp = (union all_addr *)&serv->addr.in6.sin6_addr; |
#endif | |
} |
} |
else if (!flags || (flags & F_NXDOMAIN)) |
else if (!flags || (flags & F_NXDOMAIN)) |
flags = F_NOERR; |
flags = F_NOERR; |
Line 182 static unsigned int search_servers(time_t now, struct
|
Line 194 static unsigned int search_servers(time_t now, struct
|
flags = F_NXDOMAIN; |
flags = F_NXDOMAIN; |
else if (serv->flags & SERV_LITERAL_ADDRESS) |
else if (serv->flags & SERV_LITERAL_ADDRESS) |
{ |
{ |
if (sflag & qtype) | /* literal address = '#' -> return all-zero address for IPv4 and IPv6 */ |
| if ((serv->flags & SERV_USE_RESOLV) && (qtype & (F_IPV6 | F_IPV4))) |
| { |
| memset(&zero, 0, sizeof(zero)); |
| flags = qtype; |
| *addrpp = &zero; |
| } |
| else if (sflag & qtype) |
{ |
{ |
flags = sflag; |
flags = sflag; |
if (serv->addr.sa.sa_family == AF_INET) |
if (serv->addr.sa.sa_family == AF_INET) |
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr; | *addrpp = (union all_addr *)&serv->addr.in.sin_addr; |
#ifdef HAVE_IPV6 | |
else |
else |
*addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr; | *addrpp = (union all_addr *)&serv->addr.in6.sin6_addr; |
#endif | |
} |
} |
else if (!flags || (flags & F_NXDOMAIN)) |
else if (!flags || (flags & F_NXDOMAIN)) |
flags = F_NOERR; |
flags = F_NOERR; |
Line 202 static unsigned int search_servers(time_t now, struct
|
Line 219 static unsigned int search_servers(time_t now, struct
|
} |
} |
} |
} |
|
|
if (flags == 0 && !(qtype & F_QUERY) && | if (flags == 0 && !(qtype & (F_QUERY | F_DNSSECOK)) && |
option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0) |
option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0) |
/* don't forward A or AAAA queries for simple names, except the empty name */ |
/* don't forward A or AAAA queries for simple names, except the empty name */ |
flags = F_NOERR; |
flags = F_NOERR; |
Line 212 static unsigned int search_servers(time_t now, struct
|
Line 229 static unsigned int search_servers(time_t now, struct
|
|
|
if (flags) |
if (flags) |
{ |
{ |
int logflags = 0; | if (flags == F_NXDOMAIN || flags == F_NOERR) |
| log_query(flags | qtype | F_NEG | F_CONFIG | F_FORWARD, qdomain, NULL, NULL); |
if (flags == F_NXDOMAIN || flags == F_NOERR) | else |
logflags = F_NEG | qtype; | { |
| /* handle F_IPV4 and F_IPV6 set on ANY query to 0.0.0.0/:: domain. */ |
log_query(logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp, NULL); | if (flags & F_IPV4) |
| log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, qdomain, *addrpp, NULL); |
| if (flags & F_IPV6) |
| log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, qdomain, *addrpp, NULL); |
| } |
} |
} |
else if ((*type) & SERV_USE_RESOLV) |
else if ((*type) & SERV_USE_RESOLV) |
{ |
{ |
Line 228 static unsigned int search_servers(time_t now, struct
|
Line 249 static unsigned int search_servers(time_t now, struct
|
} |
} |
|
|
static int forward_query(int udpfd, union mysockaddr *udpaddr, |
static int forward_query(int udpfd, union mysockaddr *udpaddr, |
struct all_addr *dst_addr, unsigned int dst_iface, | union all_addr *dst_addr, unsigned int dst_iface, |
struct dns_header *header, size_t plen, time_t now, |
struct dns_header *header, size_t plen, time_t now, |
struct frec *forward, int ad_reqd, int do_bit) |
struct frec *forward, int ad_reqd, int do_bit) |
{ |
{ |
char *domain = NULL; |
char *domain = NULL; |
int type = SERV_DO_DNSSEC, norebind = 0; |
int type = SERV_DO_DNSSEC, norebind = 0; |
struct all_addr *addrp = NULL; | union all_addr *addrp = NULL; |
unsigned int flags = 0; |
unsigned int flags = 0; |
|
unsigned int fwd_flags = 0; |
struct server *start = NULL; |
struct server *start = NULL; |
#ifdef HAVE_DNSSEC |
|
void *hash = hash_questions(header, plen, daemon->namebuff); |
void *hash = hash_questions(header, plen, daemon->namebuff); |
|
#ifdef HAVE_DNSSEC |
int do_dnssec = 0; |
int do_dnssec = 0; |
#else |
|
unsigned int crc = questions_crc(header, plen, daemon->namebuff); |
|
void *hash = &crc; |
|
#endif |
#endif |
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); | unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); |
| unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL); |
(void)do_bit; | (void)do_bit; |
| |
| if (header->hb4 & HB4_CD) |
| fwd_flags |= FREC_CHECKING_DISABLED; |
| if (ad_reqd) |
| fwd_flags |= FREC_AD_QUESTION; |
| if (oph) |
| fwd_flags |= FREC_HAS_PHEADER; |
| #ifdef HAVE_DNSSEC |
| if (do_bit) |
| fwd_flags |= FREC_DO_QUESTION; |
| #endif |
| |
/* may be no servers available. */ |
/* may be no servers available. */ |
if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash)))) | if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))) |
{ |
{ |
/* If we didn't get an answer advertising a maximal packet in EDNS, |
/* If we didn't get an answer advertising a maximal packet in EDNS, |
fall back to 1280, which should work everywhere on IPv6. |
fall back to 1280, which should work everywhere on IPv6. |
Line 270 static int forward_query(int udpfd, union mysockaddr *
|
Line 300 static int forward_query(int udpfd, union mysockaddr *
|
while (forward->blocking_query) |
while (forward->blocking_query) |
forward = forward->blocking_query; |
forward = forward->blocking_query; |
|
|
forward->flags |= FREC_TEST_PKTSZ; |
|
|
|
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); |
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); |
plen = forward->stash_len; |
plen = forward->stash_len; |
|
|
|
forward->flags |= FREC_TEST_PKTSZ; |
if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign) |
if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign) |
PUTSHORT(SAFE_PKTSZ, pheader); |
PUTSHORT(SAFE_PKTSZ, pheader); |
| |
if (forward->sentto->addr.sa.sa_family == AF_INET) |
if (forward->sentto->addr.sa.sa_family == AF_INET) |
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec"); | log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (union all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec"); |
#ifdef HAVE_IPV6 | |
else |
else |
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec"); | log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (union all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec"); |
#endif | |
|
|
if (forward->sentto->sfd) |
if (forward->sentto->sfd) |
fd = forward->sentto->sfd->fd; |
fd = forward->sentto->sfd->fd; |
else |
else |
{ |
{ |
#ifdef HAVE_IPV6 |
|
if (forward->sentto->addr.sa.sa_family == AF_INET6) |
if (forward->sentto->addr.sa.sa_family == AF_INET6) |
fd = forward->rfd6->fd; |
fd = forward->rfd6->fd; |
else |
else |
#endif |
|
fd = forward->rfd4->fd; |
fd = forward->rfd4->fd; |
} |
} |
|
|
while (retry_send( sendto(fd, (char *)header, plen, 0, | while (retry_send(sendto(fd, (char *)header, plen, 0, |
&forward->sentto->addr.sa, | &forward->sentto->addr.sa, |
sa_len(&forward->sentto->addr)))); | sa_len(&forward->sentto->addr)))); |
|
|
return 1; |
return 1; |
} |
} |
Line 324 static int forward_query(int udpfd, union mysockaddr *
|
Line 350 static int forward_query(int udpfd, union mysockaddr *
|
} |
} |
else |
else |
{ |
{ |
|
/* Query from new source, but the same query may be in progress |
|
from another source. If so, just add this client to the |
|
list that will get the reply.*/ |
|
|
|
if (!option_bool(OPT_ADD_MAC) && !option_bool(OPT_MAC_B64) && |
|
(forward = lookup_frec_by_query(hash, fwd_flags))) |
|
{ |
|
/* Note whine_malloc() zeros memory. */ |
|
if (!daemon->free_frec_src && |
|
daemon->frec_src_count < daemon->ftabsize && |
|
(daemon->free_frec_src = whine_malloc(sizeof(struct frec_src)))) |
|
{ |
|
daemon->frec_src_count++; |
|
daemon->free_frec_src->next = NULL; |
|
} |
|
|
|
/* If we've been spammed with many duplicates, just drop the query. */ |
|
if (daemon->free_frec_src) |
|
{ |
|
struct frec_src *new = daemon->free_frec_src; |
|
daemon->free_frec_src = new->next; |
|
new->next = forward->frec_src.next; |
|
forward->frec_src.next = new; |
|
new->orig_id = ntohs(header->id); |
|
new->source = *udpaddr; |
|
new->dest = *dst_addr; |
|
new->log_id = daemon->log_id; |
|
new->iface = dst_iface; |
|
new->fd = udpfd; |
|
} |
|
|
|
return 1; |
|
} |
|
|
if (gotname) |
if (gotname) |
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); |
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); |
|
|
Line 331 static int forward_query(int udpfd, union mysockaddr *
|
Line 391 static int forward_query(int udpfd, union mysockaddr *
|
do_dnssec = type & SERV_DO_DNSSEC; |
do_dnssec = type & SERV_DO_DNSSEC; |
#endif |
#endif |
type &= ~SERV_DO_DNSSEC; |
type &= ~SERV_DO_DNSSEC; |
| |
if (daemon->servers && !flags) |
if (daemon->servers && !flags) |
forward = get_new_frec(now, NULL, 0); | forward = get_new_frec(now, NULL, NULL); |
/* table full - flags == 0, return REFUSED */ |
/* table full - flags == 0, return REFUSED */ |
|
|
if (forward) |
if (forward) |
{ |
{ |
forward->source = *udpaddr; | forward->frec_src.source = *udpaddr; |
forward->dest = *dst_addr; | forward->frec_src.orig_id = ntohs(header->id); |
forward->iface = dst_iface; | forward->frec_src.dest = *dst_addr; |
forward->orig_id = ntohs(header->id); | forward->frec_src.iface = dst_iface; |
| forward->frec_src.next = NULL; |
| forward->frec_src.fd = udpfd; |
forward->new_id = get_id(); |
forward->new_id = get_id(); |
forward->fd = udpfd; |
|
memcpy(forward->hash, hash, HASH_SIZE); |
memcpy(forward->hash, hash, HASH_SIZE); |
forward->forwardall = 0; |
forward->forwardall = 0; |
forward->flags = 0; | forward->flags = fwd_flags; |
if (norebind) |
if (norebind) |
forward->flags |= FREC_NOREBIND; |
forward->flags |= FREC_NOREBIND; |
if (header->hb4 & HB4_CD) |
if (header->hb4 & HB4_CD) |
Line 396 static int forward_query(int udpfd, union mysockaddr *
|
Line 457 static int forward_query(int udpfd, union mysockaddr *
|
if (!flags && forward) |
if (!flags && forward) |
{ |
{ |
struct server *firstsentto = start; |
struct server *firstsentto = start; |
int subnet, forwarded = 0; | int subnet, cacheable, forwarded = 0; |
size_t edns0_len; |
size_t edns0_len; |
| unsigned char *pheader; |
| |
/* If a query is retried, use the log_id for the retry when logging the answer. */ |
/* If a query is retried, use the log_id for the retry when logging the answer. */ |
forward->log_id = daemon->log_id; | forward->frec_src.log_id = daemon->log_id; |
|
|
edns0_len = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->source, now, &subnet); | plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->frec_src.source, now, &subnet, &cacheable); |
|
|
if (edns0_len != plen) | if (subnet) |
{ | forward->flags |= FREC_HAS_SUBNET; |
plen = edns0_len; | |
forward->flags |= FREC_ADDED_PHEADER; | if (!cacheable) |
| forward->flags |= FREC_NO_CACHE; |
if (subnet) | |
forward->flags |= FREC_HAS_SUBNET; | |
} | |
| |
#ifdef HAVE_DNSSEC |
#ifdef HAVE_DNSSEC |
if (option_bool(OPT_DNSSEC_VALID) && do_dnssec) |
if (option_bool(OPT_DNSSEC_VALID) && do_dnssec) |
{ |
{ |
size_t new = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ); | plen = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ); |
| |
if (new != plen) | |
forward->flags |= FREC_ADDED_PHEADER; | |
| |
plen = new; | |
| |
/* For debugging, set Checking Disabled, otherwise, have the upstream check too, |
/* For debugging, set Checking Disabled, otherwise, have the upstream check too, |
this allows it to select auth servers when one is returning bad data. */ |
this allows it to select auth servers when one is returning bad data. */ |
if (option_bool(OPT_DNSSEC_DEBUG)) |
if (option_bool(OPT_DNSSEC_DEBUG)) |
Line 431 static int forward_query(int udpfd, union mysockaddr *
|
Line 485 static int forward_query(int udpfd, union mysockaddr *
|
} |
} |
#endif |
#endif |
|
|
/* If we're sending an EDNS0 with any options, we can't recreate the query from a reply. */ | if (find_pseudoheader(header, plen, &edns0_len, &pheader, NULL, NULL)) |
if (find_pseudoheader(header, plen, &edns0_len, NULL, NULL, NULL) && edns0_len > 11) | { |
forward->flags |= FREC_HAS_EXTRADATA; | /* If there wasn't a PH before, and there is now, we added it. */ |
| if (!oph) |
| forward->flags |= FREC_ADDED_PHEADER; |
| |
| /* If we're sending an EDNS0 with any options, we can't recreate the query from a reply. */ |
| if (edns0_len > 11) |
| forward->flags |= FREC_HAS_EXTRADATA; |
| |
| /* Reduce udp size on retransmits. */ |
| if (forward->flags & FREC_TEST_PKTSZ) |
| PUTSHORT(SAFE_PKTSZ, pheader); |
| } |
|
|
while (1) |
while (1) |
{ |
{ |
Line 452 static int forward_query(int udpfd, union mysockaddr *
|
Line 517 static int forward_query(int udpfd, union mysockaddr *
|
fd = start->sfd->fd; |
fd = start->sfd->fd; |
else |
else |
{ |
{ |
#ifdef HAVE_IPV6 |
|
if (start->addr.sa.sa_family == AF_INET6) |
if (start->addr.sa.sa_family == AF_INET6) |
{ |
{ |
if (!forward->rfd6 && |
if (!forward->rfd6 && |
Line 462 static int forward_query(int udpfd, union mysockaddr *
|
Line 526 static int forward_query(int udpfd, union mysockaddr *
|
fd = forward->rfd6->fd; |
fd = forward->rfd6->fd; |
} |
} |
else |
else |
#endif |
|
{ |
{ |
if (!forward->rfd4 && |
if (!forward->rfd4 && |
!(forward->rfd4 = allocate_rfd(AF_INET))) |
!(forward->rfd4 = allocate_rfd(AF_INET))) |
Line 476 static int forward_query(int udpfd, union mysockaddr *
|
Line 539 static int forward_query(int udpfd, union mysockaddr *
|
if (option_bool(OPT_CONNTRACK)) |
if (option_bool(OPT_CONNTRACK)) |
{ |
{ |
unsigned int mark; |
unsigned int mark; |
if (get_incoming_mark(&forward->source, &forward->dest, 0, &mark)) | if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark)) |
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); |
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); |
} |
} |
#endif |
#endif |
Line 505 static int forward_query(int udpfd, union mysockaddr *
|
Line 568 static int forward_query(int udpfd, union mysockaddr *
|
|
|
if (errno == 0) |
if (errno == 0) |
{ |
{ |
|
#ifdef HAVE_DUMPFILE |
|
dump_packet(DUMP_UP_QUERY, (void *)header, plen, NULL, &start->addr); |
|
#endif |
|
|
/* Keep info in case we want to re-send this packet */ |
/* Keep info in case we want to re-send this packet */ |
daemon->srv_save = start; |
daemon->srv_save = start; |
daemon->packet_len = plen; |
daemon->packet_len = plen; |
Line 513 static int forward_query(int udpfd, union mysockaddr *
|
Line 580 static int forward_query(int udpfd, union mysockaddr *
|
strcpy(daemon->namebuff, "query"); |
strcpy(daemon->namebuff, "query"); |
if (start->addr.sa.sa_family == AF_INET) |
if (start->addr.sa.sa_family == AF_INET) |
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, |
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, |
(struct all_addr *)&start->addr.in.sin_addr, NULL); | (union all_addr *)&start->addr.in.sin_addr, NULL); |
#ifdef HAVE_IPV6 | |
else |
else |
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, |
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, |
(struct all_addr *)&start->addr.in6.sin6_addr, NULL); | (union all_addr *)&start->addr.in6.sin6_addr, NULL); |
#endif | |
start->queries++; |
start->queries++; |
forwarded = 1; |
forwarded = 1; |
forward->sentto = start; |
forward->sentto = start; |
Line 539 static int forward_query(int udpfd, union mysockaddr *
|
Line 604 static int forward_query(int udpfd, union mysockaddr *
|
return 1; |
return 1; |
|
|
/* could not send on, prepare to return */ |
/* could not send on, prepare to return */ |
header->id = htons(forward->orig_id); | header->id = htons(forward->frec_src.orig_id); |
free_frec(forward); /* cancel */ |
free_frec(forward); /* cancel */ |
} |
} |
|
|
Line 547 static int forward_query(int udpfd, union mysockaddr *
|
Line 612 static int forward_query(int udpfd, union mysockaddr *
|
if (udpfd != -1) |
if (udpfd != -1) |
{ |
{ |
plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl); |
plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl); |
|
if (oph) |
|
plen = add_pseudoheader(header, plen, ((unsigned char *) header) + PACKETSZ, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); |
send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface); |
send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface); |
} |
} |
|
|
Line 560 static size_t process_reply(struct dns_header *header,
|
Line 627 static size_t process_reply(struct dns_header *header,
|
unsigned char *pheader, *sizep; |
unsigned char *pheader, *sizep; |
char **sets = 0; |
char **sets = 0; |
int munged = 0, is_sign; |
int munged = 0, is_sign; |
|
unsigned int rcode = RCODE(header); |
size_t plen; |
size_t plen; |
| |
(void)ad_reqd; |
(void)ad_reqd; |
(void)do_bit; |
(void)do_bit; |
(void)bogusanswer; |
(void)bogusanswer; |
Line 587 static size_t process_reply(struct dns_header *header,
|
Line 655 static size_t process_reply(struct dns_header *header,
|
} |
} |
} |
} |
#endif |
#endif |
| |
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL))) |
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL))) |
{ |
{ |
|
/* Get extended RCODE. */ |
|
rcode |= sizep[2] << 4; |
|
|
if (check_subnet && !check_source(header, plen, pheader, query_source)) |
if (check_subnet && !check_source(header, plen, pheader, query_source)) |
{ |
{ |
my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch")); |
my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch")); |
Line 638 static size_t process_reply(struct dns_header *header,
|
Line 709 static size_t process_reply(struct dns_header *header,
|
if (!is_sign && !option_bool(OPT_DNSSEC_PROXY)) |
if (!is_sign && !option_bool(OPT_DNSSEC_PROXY)) |
header->hb4 &= ~HB4_AD; |
header->hb4 &= ~HB4_AD; |
|
|
if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN)) | if (OPCODE(header) != QUERY) |
return resize_packet(header, n, pheader, plen); |
return resize_packet(header, n, pheader, plen); |
|
|
|
if (rcode != NOERROR && rcode != NXDOMAIN) |
|
{ |
|
union all_addr a; |
|
a.log.rcode = rcode; |
|
log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL); |
|
|
|
return resize_packet(header, n, pheader, plen); |
|
} |
|
|
/* Complain loudly if the upstream server is non-recursive. */ |
/* Complain loudly if the upstream server is non-recursive. */ |
if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR && | if (!(header->hb4 & HB4_RA) && rcode == NOERROR && |
server && !(server->flags & SERV_WARNED_RECURSIVE)) |
server && !(server->flags & SERV_WARNED_RECURSIVE)) |
{ |
{ |
prettyprint_addr(&server->addr, daemon->namebuff); | (void)prettyprint_addr(&server->addr, daemon->namebuff); |
my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff); |
my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff); |
if (!option_bool(OPT_LOG)) |
if (!option_bool(OPT_LOG)) |
server->flags |= SERV_WARNED_RECURSIVE; |
server->flags |= SERV_WARNED_RECURSIVE; |
} |
} |
|
|
if (daemon->bogus_addr && RCODE(header) != NXDOMAIN && | if (daemon->bogus_addr && rcode != NXDOMAIN && |
check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now)) |
check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now)) |
{ |
{ |
munged = 1; |
munged = 1; |
Line 663 static size_t process_reply(struct dns_header *header,
|
Line 743 static size_t process_reply(struct dns_header *header,
|
{ |
{ |
int doctored = 0; |
int doctored = 0; |
|
|
if (RCODE(header) == NXDOMAIN && | if (rcode == NXDOMAIN && |
extract_request(header, n, daemon->namebuff, NULL) && |
extract_request(header, n, daemon->namebuff, NULL) && |
check_for_local_domain(daemon->namebuff, now)) |
check_for_local_domain(daemon->namebuff, now)) |
{ |
{ |
Line 738 void reply_query(int fd, int family, time_t now)
|
Line 818 void reply_query(int fd, int family, time_t now)
|
size_t nn; |
size_t nn; |
struct server *server; |
struct server *server; |
void *hash; |
void *hash; |
#ifndef HAVE_DNSSEC |
|
unsigned int crc; |
|
#endif |
|
|
|
/* packet buffer overwritten */ |
/* packet buffer overwritten */ |
daemon->srv_save = NULL; |
daemon->srv_save = NULL; |
|
|
/* Determine the address of the server replying so that we can mark that as good */ |
/* Determine the address of the server replying so that we can mark that as good */ |
serveraddr.sa.sa_family = family; | if ((serveraddr.sa.sa_family = family) == AF_INET6) |
#ifdef HAVE_IPV6 | |
if (serveraddr.sa.sa_family == AF_INET6) | |
serveraddr.in6.sin6_flowinfo = 0; |
serveraddr.in6.sin6_flowinfo = 0; |
#endif |
|
|
|
header = (struct dns_header *)daemon->packet; |
header = (struct dns_header *)daemon->packet; |
| |
if (n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR)) |
if (n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR)) |
return; |
return; |
|
|
Line 765 void reply_query(int fd, int family, time_t now)
|
Line 839 void reply_query(int fd, int family, time_t now)
|
|
|
if (!server) |
if (!server) |
return; |
return; |
| |
#ifdef HAVE_DNSSEC | /* If sufficient time has elapsed, try and expand UDP buffer size again. */ |
| if (difftime(now, server->pktsz_reduced) > UDP_TEST_TIME) |
| server->edns_pktsz = daemon->edns_pktsz; |
| |
hash = hash_questions(header, n, daemon->namebuff); |
hash = hash_questions(header, n, daemon->namebuff); |
#else |
|
hash = &crc; |
|
crc = questions_crc(header, n, daemon->namebuff); |
|
#endif |
|
|
|
if (!(forward = lookup_frec(ntohs(header->id), hash))) | if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash))) |
return; |
return; |
|
|
|
#ifdef HAVE_DUMPFILE |
|
dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_REPLY : DUMP_UP_REPLY, |
|
(void *)header, n, &serveraddr, NULL); |
|
#endif |
|
|
/* log_query gets called indirectly all over the place, so |
/* log_query gets called indirectly all over the place, so |
pass these in global variables - sorry. */ |
pass these in global variables - sorry. */ |
daemon->log_display_id = forward->log_id; | daemon->log_display_id = forward->frec_src.log_id; |
daemon->log_source_addr = &forward->source; | daemon->log_source_addr = &forward->frec_src.source; |
|
|
if (daemon->ignore_addr && RCODE(header) == NOERROR && |
if (daemon->ignore_addr && RCODE(header) == NOERROR && |
check_for_ignored_address(header, n, daemon->ignore_addr)) |
check_for_ignored_address(header, n, daemon->ignore_addr)) |
Line 787 void reply_query(int fd, int family, time_t now)
|
Line 865 void reply_query(int fd, int family, time_t now)
|
|
|
/* Note: if we send extra options in the EDNS0 header, we can't recreate |
/* Note: if we send extra options in the EDNS0 header, we can't recreate |
the query from the reply. */ |
the query from the reply. */ |
if (RCODE(header) == REFUSED && | if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) && |
!option_bool(OPT_ORDER) && | |
forward->forwardall == 0 && |
forward->forwardall == 0 && |
!(forward->flags & FREC_HAS_EXTRADATA)) |
!(forward->flags & FREC_HAS_EXTRADATA)) |
/* for broken servers, attempt to send to another one. */ |
/* for broken servers, attempt to send to another one. */ |
Line 796 void reply_query(int fd, int family, time_t now)
|
Line 873 void reply_query(int fd, int family, time_t now)
|
unsigned char *pheader; |
unsigned char *pheader; |
size_t plen; |
size_t plen; |
int is_sign; |
int is_sign; |
|
|
|
#ifdef HAVE_DNSSEC |
|
if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) |
|
{ |
|
struct server *start; |
|
|
|
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); |
|
plen = forward->stash_len; |
|
|
|
forward->forwardall = 2; /* only retry once */ |
|
start = forward->sentto; |
|
|
|
/* for non-domain specific servers, see if we can find another to try. */ |
|
if ((forward->sentto->flags & SERV_TYPE) == 0) |
|
while (1) |
|
{ |
|
if (!(start = start->next)) |
|
start = daemon->servers; |
|
if (start == forward->sentto) |
|
break; |
|
|
|
if ((start->flags & SERV_TYPE) == 0 && |
|
(start->flags & SERV_DO_DNSSEC)) |
|
break; |
|
} |
|
|
|
|
|
fd = -1; |
|
|
|
if (start->sfd) |
|
fd = start->sfd->fd; |
|
else |
|
{ |
|
if (start->addr.sa.sa_family == AF_INET6) |
|
{ |
|
/* may have changed family */ |
|
if (forward->rfd6 || (forward->rfd6 = allocate_rfd(AF_INET6))) |
|
fd = forward->rfd6->fd; |
|
} |
|
else |
|
{ |
|
/* may have changed family */ |
|
if (forward->rfd4 || (forward->rfd4 = allocate_rfd(AF_INET))) |
|
fd = forward->rfd4->fd; |
|
} |
|
} |
|
|
|
/* Can't get socket. */ |
|
if (fd == -1) |
|
return; |
|
|
|
#ifdef HAVE_DUMPFILE |
|
dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)plen, NULL, &start->addr); |
|
#endif |
|
|
|
while (retry_send(sendto(fd, (char *)header, plen, 0, |
|
&start->addr.sa, |
|
sa_len(&start->addr)))); |
|
|
|
if (start->addr.sa.sa_family == AF_INET) |
|
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (union all_addr *)&start->addr.in.sin_addr, "dnssec"); |
|
else |
|
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (union all_addr *)&start->addr.in6.sin6_addr, "dnssec"); |
|
|
|
return; |
|
} |
|
#endif |
|
|
|
/* In strict order mode, there must be a server later in the chain |
|
left to send to, otherwise without the forwardall mechanism, |
|
code further on will cycle around the list forwever if they |
|
all return REFUSED. Note that server is always non-NULL before |
|
this executes. */ |
|
if (option_bool(OPT_ORDER)) |
|
for (server = forward->sentto->next; server; server = server->next) |
|
if (!(server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR | SERV_LOOP))) |
|
break; |
|
|
/* recreate query from reply */ |
/* recreate query from reply */ |
pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign, NULL); |
pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign, NULL); |
if (!is_sign) | if (!is_sign && server) |
{ |
{ |
header->ancount = htons(0); |
header->ancount = htons(0); |
header->nscount = htons(0); |
header->nscount = htons(0); |
Line 843 void reply_query(int fd, int family, time_t now)
|
Line 997 void reply_query(int fd, int family, time_t now)
|
} |
} |
|
|
/* We tried resending to this server with a smaller maximum size and got an answer. |
/* We tried resending to this server with a smaller maximum size and got an answer. |
Make that permanent. To avoid reduxing the packet size for an single dropped packet, | Make that permanent. To avoid reduxing the packet size for a single dropped packet, |
only do this when we get a truncated answer, or one larger than the safe size. */ |
only do this when we get a truncated answer, or one larger than the safe size. */ |
if (server && (forward->flags & FREC_TEST_PKTSZ) && | if (forward->sentto->edns_pktsz > SAFE_PKTSZ && (forward->flags & FREC_TEST_PKTSZ) && |
((header->hb3 & HB3_TC) || n >= SAFE_PKTSZ)) |
((header->hb3 & HB3_TC) || n >= SAFE_PKTSZ)) |
server->edns_pktsz = SAFE_PKTSZ; | { |
| forward->sentto->edns_pktsz = SAFE_PKTSZ; |
| forward->sentto->pktsz_reduced = now; |
| (void)prettyprint_addr(&forward->sentto->addr, daemon->addrbuff); |
| my_syslog(LOG_WARNING, _("reducing DNS packet size for nameserver %s to %d"), daemon->addrbuff, SAFE_PKTSZ); |
| } |
| |
| |
/* If the answer is an error, keep the forward record in place in case |
/* 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 |
we get a good reply from another server. Kill it when we've |
had replies from all to avoid filling the forwarding table when |
had replies from all to avoid filling the forwarding table when |
Line 856 void reply_query(int fd, int family, time_t now)
|
Line 1016 void reply_query(int fd, int family, time_t now)
|
if (forward->forwardall == 0 || --forward->forwardall == 1 || RCODE(header) != REFUSED) |
if (forward->forwardall == 0 || --forward->forwardall == 1 || RCODE(header) != REFUSED) |
{ |
{ |
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; |
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; |
| |
if (option_bool(OPT_NO_REBIND)) |
if (option_bool(OPT_NO_REBIND)) |
check_rebind = !(forward->flags & FREC_NOREBIND); |
check_rebind = !(forward->flags & FREC_NOREBIND); |
|
|
Line 866 void reply_query(int fd, int family, time_t now)
|
Line 1026 void reply_query(int fd, int family, time_t now)
|
no_cache_dnssec = 1; |
no_cache_dnssec = 1; |
|
|
#ifdef HAVE_DNSSEC |
#ifdef HAVE_DNSSEC |
if (server && (server->flags & SERV_DO_DNSSEC) && | if ((forward->sentto->flags & SERV_DO_DNSSEC) && |
option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED)) |
option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED)) |
{ |
{ |
int status = 0; |
int status = 0; |
Line 876 void reply_query(int fd, int family, time_t now)
|
Line 1036 void reply_query(int fd, int family, time_t now)
|
return; |
return; |
|
|
/* Truncated answer can't be validated. |
/* Truncated answer can't be validated. |
If this is an answer to a DNSSEC-generated query, we still | If this is an answer to a DNSSEC-generated query, we still |
need to get the client to retry over TCP, so return | need to get the client to retry over TCP, so return |
an answer with the TC bit set, even if the actual answer fits. | an answer with the TC bit set, even if the actual answer fits. |
*/ | */ |
if (header->hb3 & HB3_TC) |
if (header->hb3 & HB3_TC) |
status = STAT_TRUNCATED; |
status = STAT_TRUNCATED; |
|
|
Line 896 void reply_query(int fd, int family, time_t now)
|
Line 1056 void reply_query(int fd, int family, time_t now)
|
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); |
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); |
else |
else |
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, |
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, |
option_bool(OPT_DNSSEC_NO_SIGN), NULL, NULL); | !option_bool(OPT_DNSSEC_IGN_NS) && (forward->sentto->flags & SERV_DO_DNSSEC), |
| NULL, NULL, NULL); |
| #ifdef HAVE_DUMPFILE |
| if (status == STAT_BOGUS) |
| dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS, |
| header, (size_t)n, &serveraddr, NULL); |
| #endif |
} |
} |
|
|
/* Can't validate, as we're missing key data. Put this |
/* Can't validate, as we're missing key data. Put this |
Line 917 void reply_query(int fd, int family, time_t now)
|
Line 1083 void reply_query(int fd, int family, time_t now)
|
/* Find the original query that started it all.... */ |
/* Find the original query that started it all.... */ |
for (orig = forward; orig->dependent; orig = orig->dependent); |
for (orig = forward; orig->dependent; orig = orig->dependent); |
|
|
if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, 1))) | /* Make sure we don't expire and free the orig frec during the |
| allocation of a new one. */ |
| if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, orig))) |
status = STAT_ABANDONED; |
status = STAT_ABANDONED; |
else |
else |
{ |
{ |
int fd, type = SERV_DO_DNSSEC; | int querytype, fd, type = SERV_DO_DNSSEC; |
struct frec *next = new->next; |
struct frec *next = new->next; |
char *domain; |
char *domain; |
|
|
Line 932 void reply_query(int fd, int family, time_t now)
|
Line 1100 void reply_query(int fd, int family, time_t now)
|
/* Find server to forward to. This will normally be the |
/* Find server to forward to. This will normally be the |
same as for the original query, but may be another if |
same as for the original query, but may be another if |
servers for domains are involved. */ |
servers for domains are involved. */ |
if (search_servers(now, NULL, F_QUERY, daemon->keyname, &type, &domain, NULL) == 0) | if (search_servers(now, NULL, F_DNSSECOK, daemon->keyname, &type, &domain, NULL) == 0) |
{ |
{ |
struct server *start = server, *new_server = NULL; | struct server *start, *new_server = NULL; |
type &= ~SERV_DO_DNSSEC; | start = server = forward->sentto; |
| |
while (1) | while (1) |
{ | { |
if (type == (start->flags & SERV_TYPE) && | if (type == (start->flags & (SERV_TYPE | SERV_DO_DNSSEC)) && |
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) && | ((type & SERV_TYPE) != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) && |
!(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP))) | !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP))) |
{ | { |
new_server = start; | new_server = start; |
if (server == start) | if (server == start) |
{ | { |
new_server = NULL; | new_server = NULL; |
break; | break; |
} | } |
} | } |
| |
if (!(start = start->next)) | if (!(start = start->next)) |
start = daemon->servers; | start = daemon->servers; |
if (start == server) | if (start == server) |
break; | break; |
} | } |
| |
if (new_server) | if (new_server) |
server = new_server; | server = new_server; |
} |
} |
| |
new->sentto = server; |
new->sentto = server; |
|
|
new->rfd4 = NULL; |
new->rfd4 = NULL; |
#ifdef HAVE_IPV6 |
|
new->rfd6 = NULL; |
new->rfd6 = NULL; |
#endif | new->frec_src.next = NULL; |
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY); | new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA); |
| new->forwardall = 0; |
|
|
new->dependent = forward; /* to find query awaiting new one. */ |
new->dependent = forward; /* to find query awaiting new one. */ |
forward->blocking_query = new; /* for garbage cleaning */ |
forward->blocking_query = new; /* for garbage cleaning */ |
Line 975 void reply_query(int fd, int family, time_t now)
|
Line 1142 void reply_query(int fd, int family, time_t now)
|
if (status == STAT_NEED_KEY) |
if (status == STAT_NEED_KEY) |
{ |
{ |
new->flags |= FREC_DNSKEY_QUERY; |
new->flags |= FREC_DNSKEY_QUERY; |
nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz, | querytype = T_DNSKEY; |
daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz); | |
} |
} |
else |
else |
{ |
{ |
new->flags |= FREC_DS_QUERY; |
new->flags |= FREC_DS_QUERY; |
nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz, | querytype = T_DS; |
daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz); | |
} |
} |
if ((hash = hash_questions(header, nn, daemon->namebuff))) | |
memcpy(new->hash, hash, HASH_SIZE); | nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz, |
| daemon->keyname, forward->class, querytype, server->edns_pktsz); |
| |
| if (server->addr.sa.sa_family == AF_INET) |
| log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, daemon->keyname, (union all_addr *)&(server->addr.in.sin_addr), |
| querystr("dnssec-query", querytype)); |
| else |
| log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, daemon->keyname, (union all_addr *)&(server->addr.in6.sin6_addr), |
| querystr("dnssec-query", querytype)); |
| |
| memcpy(new->hash, hash_questions(header, nn, daemon->namebuff), HASH_SIZE); |
new->new_id = get_id(); |
new->new_id = get_id(); |
header->id = htons(new->new_id); |
header->id = htons(new->new_id); |
/* Save query for retransmission */ |
/* Save query for retransmission */ |
Line 1000 void reply_query(int fd, int family, time_t now)
|
Line 1175 void reply_query(int fd, int family, time_t now)
|
else |
else |
{ |
{ |
fd = -1; |
fd = -1; |
#ifdef HAVE_IPV6 |
|
if (server->addr.sa.sa_family == AF_INET6) |
if (server->addr.sa.sa_family == AF_INET6) |
{ |
{ |
if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6))) |
if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6))) |
fd = new->rfd6->fd; |
fd = new->rfd6->fd; |
} |
} |
else |
else |
#endif |
|
{ |
{ |
if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET))) |
if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET))) |
fd = new->rfd4->fd; |
fd = new->rfd4->fd; |
Line 1021 void reply_query(int fd, int family, time_t now)
|
Line 1194 void reply_query(int fd, int family, time_t now)
|
if (option_bool(OPT_CONNTRACK)) |
if (option_bool(OPT_CONNTRACK)) |
{ |
{ |
unsigned int mark; |
unsigned int mark; |
if (get_incoming_mark(&orig->source, &orig->dest, 0, &mark)) | if (get_incoming_mark(&orig->frec_src.source, &orig->frec_src.dest, 0, &mark)) |
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); |
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); |
} |
} |
#endif |
#endif |
|
|
|
#ifdef HAVE_DUMPFILE |
|
dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr); |
|
#endif |
|
|
while (retry_send(sendto(fd, (char *)header, nn, 0, |
while (retry_send(sendto(fd, (char *)header, nn, 0, |
&server->addr.sa, |
&server->addr.sa, |
sa_len(&server->addr)))); |
sa_len(&server->addr)))); |
Line 1038 void reply_query(int fd, int family, time_t now)
|
Line 1216 void reply_query(int fd, int family, time_t now)
|
if (!forward->dependent) |
if (!forward->dependent) |
break; |
break; |
|
|
/* validated subsdiary query, (and cached result) | /* validated subsidiary query, (and cached result) |
pop that and return to the previous query we were working on. */ |
pop that and return to the previous query we were working on. */ |
struct frec *prev = forward->dependent; |
struct frec *prev = forward->dependent; |
free_frec(forward); |
free_frec(forward); |
Line 1068 void reply_query(int fd, int family, time_t now)
|
Line 1246 void reply_query(int fd, int family, time_t now)
|
if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL)) |
if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL)) |
domain = daemon->namebuff; |
domain = daemon->namebuff; |
|
|
log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result); | log_query(F_SECSTAT, domain, NULL, result); |
} |
} |
|
|
if (status == STAT_SECURE) |
if (status == STAT_SECURE) |
Line 1079 void reply_query(int fd, int family, time_t now)
|
Line 1257 void reply_query(int fd, int family, time_t now)
|
bogusanswer = 1; |
bogusanswer = 1; |
} |
} |
} |
} |
#endif | |
| #endif |
| |
/* restore CD bit to the value in the query */ |
/* restore CD bit to the value in the query */ |
if (forward->flags & FREC_CHECKING_DISABLED) |
if (forward->flags & FREC_CHECKING_DISABLED) |
header->hb4 |= HB4_CD; |
header->hb4 |= HB4_CD; |
else |
else |
header->hb4 &= ~HB4_CD; |
header->hb4 &= ~HB4_CD; |
|
|
|
/* Never cache answers which are contingent on the source or MAC address EDSN0 option, |
|
since the cache is ignorant of such things. */ |
|
if (forward->flags & FREC_NO_CACHE) |
|
no_cache_dnssec = 1; |
|
|
if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer, |
if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer, |
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, |
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, |
forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source))) | forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->frec_src.source))) |
{ |
{ |
header->id = htons(forward->orig_id); | struct frec_src *src; |
| |
| header->id = htons(forward->frec_src.orig_id); |
header->hb4 |= HB4_RA; /* recursion if available */ |
header->hb4 |= HB4_RA; /* recursion if available */ |
#ifdef HAVE_DNSSEC |
#ifdef HAVE_DNSSEC |
/* We added an EDNSO header for the purpose of getting DNSSEC RRs, and set the value of the UDP payload size |
/* We added an EDNSO header for the purpose of getting DNSSEC RRs, and set the value of the UDP payload size |
greater than the no-EDNS0-implied 512 to have if space for the RRSIGS. If, having stripped them and the EDNS0 | greater than the no-EDNS0-implied 512 to have space for the RRSIGS. If, having stripped them and the EDNS0 |
header, the answer is still bigger than 512, truncate it and mark it so. The client then retries with TCP. */ |
header, the answer is still bigger than 512, truncate it and mark it so. The client then retries with TCP. */ |
if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER) && (nn > PACKETSZ)) |
if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER) && (nn > PACKETSZ)) |
{ |
{ |
Line 1106 void reply_query(int fd, int family, time_t now)
|
Line 1292 void reply_query(int fd, int family, time_t now)
|
nn = resize_packet(header, nn, NULL, 0); |
nn = resize_packet(header, nn, NULL, 0); |
} |
} |
#endif |
#endif |
send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, | |
&forward->source, &forward->dest, forward->iface); | for (src = &forward->frec_src; src; src = src->next) |
| { |
| header->id = htons(src->orig_id); |
| |
| #ifdef HAVE_DUMPFILE |
| dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source); |
| #endif |
| |
| send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, |
| &src->source, &src->dest, src->iface); |
| |
| if (option_bool(OPT_EXTRALOG) && src != &forward->frec_src) |
| { |
| daemon->log_display_id = src->log_id; |
| daemon->log_source_addr = &src->source; |
| log_query(F_UPSTREAM, "query", NULL, "duplicate"); |
| } |
| } |
} |
} |
|
|
free_frec(forward); /* cancel */ |
free_frec(forward); /* cancel */ |
} |
} |
} |
} |
Line 1120 void receive_query(struct listener *listen, time_t now
|
Line 1324 void receive_query(struct listener *listen, time_t now
|
union mysockaddr source_addr; |
union mysockaddr source_addr; |
unsigned char *pheader; |
unsigned char *pheader; |
unsigned short type, udp_size = PACKETSZ; /* default if no EDNS0 */ |
unsigned short type, udp_size = PACKETSZ; /* default if no EDNS0 */ |
struct all_addr dst_addr; | union all_addr dst_addr; |
struct in_addr netmask, dst_addr_4; |
struct in_addr netmask, dst_addr_4; |
size_t m; |
size_t m; |
ssize_t n; |
ssize_t n; |
Line 1133 void receive_query(struct listener *listen, time_t now
|
Line 1337 void receive_query(struct listener *listen, time_t now
|
struct cmsghdr *cmptr; |
struct cmsghdr *cmptr; |
union { |
union { |
struct cmsghdr align; /* this ensures alignment */ |
struct cmsghdr align; /* this ensures alignment */ |
#ifdef HAVE_IPV6 |
|
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; |
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; |
#endif |
|
#if defined(HAVE_LINUX_NETWORK) |
#if defined(HAVE_LINUX_NETWORK) |
char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
#elif defined(IP_RECVDSTADDR) && defined(HAVE_SOLARIS_NETWORK) |
#elif defined(IP_RECVDSTADDR) && defined(HAVE_SOLARIS_NETWORK) |
Line 1146 void receive_query(struct listener *listen, time_t now
|
Line 1348 void receive_query(struct listener *listen, time_t now
|
CMSG_SPACE(sizeof(struct sockaddr_dl))]; |
CMSG_SPACE(sizeof(struct sockaddr_dl))]; |
#endif |
#endif |
} control_u; |
} control_u; |
#ifdef HAVE_IPV6 | int family = listen->addr.sa.sa_family; |
/* Can always get recvd interface for IPv6 */ |
/* Can always get recvd interface for IPv6 */ |
int check_dst = !option_bool(OPT_NOWILD) || listen->family == AF_INET6; | int check_dst = !option_bool(OPT_NOWILD) || family == AF_INET6; |
#else | |
int check_dst = !option_bool(OPT_NOWILD); | |
#endif | |
|
|
/* packet buffer overwritten */ |
/* packet buffer overwritten */ |
daemon->srv_save = NULL; |
daemon->srv_save = NULL; |
|
|
dst_addr_4.s_addr = dst_addr.addr.addr4.s_addr = 0; | dst_addr_4.s_addr = dst_addr.addr4.s_addr = 0; |
netmask.s_addr = 0; |
netmask.s_addr = 0; |
|
|
if (option_bool(OPT_NOWILD) && listen->iface) |
if (option_bool(OPT_NOWILD) && listen->iface) |
{ |
{ |
auth_dns = listen->iface->dns_auth; |
auth_dns = listen->iface->dns_auth; |
|
|
if (listen->family == AF_INET) | if (family == AF_INET) |
{ |
{ |
dst_addr_4 = dst_addr.addr.addr4 = listen->iface->addr.in.sin_addr; | dst_addr_4 = dst_addr.addr4 = listen->iface->addr.in.sin_addr; |
netmask = listen->iface->netmask; |
netmask = listen->iface->netmask; |
} |
} |
} |
} |
Line 1188 void receive_query(struct listener *listen, time_t now
|
Line 1387 void receive_query(struct listener *listen, time_t now
|
(msg.msg_flags & MSG_TRUNC) || |
(msg.msg_flags & MSG_TRUNC) || |
(header->hb3 & HB3_QR)) |
(header->hb3 & HB3_QR)) |
return; |
return; |
|
|
|
/* Clear buffer beyond request to avoid risk of |
|
information disclosure. */ |
|
memset(daemon->packet + n, 0, daemon->edns_pktsz - n); |
|
|
source_addr.sa.sa_family = listen->family; | source_addr.sa.sa_family = family; |
|
|
if (listen->family == AF_INET) | if (family == AF_INET) |
{ |
{ |
/* Source-port == 0 is an error, we can't send back to that. |
/* Source-port == 0 is an error, we can't send back to that. |
http://www.ietf.org/mail-archive/web/dnsop/current/msg11441.html */ |
http://www.ietf.org/mail-archive/web/dnsop/current/msg11441.html */ |
if (source_addr.in.sin_port == 0) |
if (source_addr.in.sin_port == 0) |
return; |
return; |
} |
} |
#ifdef HAVE_IPV6 |
|
else |
else |
{ |
{ |
/* Source-port == 0 is an error, we can't send back to that. */ |
/* Source-port == 0 is an error, we can't send back to that. */ |
Line 1206 void receive_query(struct listener *listen, time_t now
|
Line 1408 void receive_query(struct listener *listen, time_t now
|
return; |
return; |
source_addr.in6.sin6_flowinfo = 0; |
source_addr.in6.sin6_flowinfo = 0; |
} |
} |
#endif |
|
|
|
/* We can be configured to only accept queries from at-most-one-hop-away addresses. */ |
/* We can be configured to only accept queries from at-most-one-hop-away addresses. */ |
if (option_bool(OPT_LOCAL_SERVICE)) |
if (option_bool(OPT_LOCAL_SERVICE)) |
{ |
{ |
struct addrlist *addr; |
struct addrlist *addr; |
#ifdef HAVE_IPV6 | |
if (listen->family == AF_INET6) | if (family == AF_INET6) |
{ |
{ |
for (addr = daemon->interface_addrs; addr; addr = addr->next) |
for (addr = daemon->interface_addrs; addr; addr = addr->next) |
if ((addr->flags & ADDRLIST_IPV6) && |
if ((addr->flags & ADDRLIST_IPV6) && |
is_same_net6(&addr->addr.addr.addr6, &source_addr.in6.sin6_addr, addr->prefixlen)) | is_same_net6(&addr->addr.addr6, &source_addr.in6.sin6_addr, addr->prefixlen)) |
break; |
break; |
} |
} |
else |
else |
#endif |
|
{ |
{ |
struct in_addr netmask; |
struct in_addr netmask; |
for (addr = daemon->interface_addrs; addr; addr = addr->next) |
for (addr = daemon->interface_addrs; addr; addr = addr->next) |
{ |
{ |
netmask.s_addr = htonl(~(in_addr_t)0 << (32 - addr->prefixlen)); |
netmask.s_addr = htonl(~(in_addr_t)0 << (32 - addr->prefixlen)); |
if (!(addr->flags & ADDRLIST_IPV6) && |
if (!(addr->flags & ADDRLIST_IPV6) && |
is_same_net(addr->addr.addr.addr4, source_addr.in.sin_addr, netmask)) | is_same_net(addr->addr.addr4, source_addr.in.sin_addr, netmask)) |
break; |
break; |
} |
} |
} |
} |
Line 1252 void receive_query(struct listener *listen, time_t now
|
Line 1452 void receive_query(struct listener *listen, time_t now
|
return; |
return; |
|
|
#if defined(HAVE_LINUX_NETWORK) |
#if defined(HAVE_LINUX_NETWORK) |
if (listen->family == AF_INET) | if (family == AF_INET) |
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO) |
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO) |
{ |
{ |
Line 1261 void receive_query(struct listener *listen, time_t now
|
Line 1461 void receive_query(struct listener *listen, time_t now
|
struct in_pktinfo *p; |
struct in_pktinfo *p; |
} p; |
} p; |
p.c = CMSG_DATA(cmptr); |
p.c = CMSG_DATA(cmptr); |
dst_addr_4 = dst_addr.addr.addr4 = p.p->ipi_spec_dst; | dst_addr_4 = dst_addr.addr4 = p.p->ipi_spec_dst; |
if_index = p.p->ipi_ifindex; |
if_index = p.p->ipi_ifindex; |
} |
} |
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) |
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) |
if (listen->family == AF_INET) | if (family == AF_INET) |
{ |
{ |
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
{ |
{ |
Line 1279 void receive_query(struct listener *listen, time_t now
|
Line 1479 void receive_query(struct listener *listen, time_t now
|
} p; |
} p; |
p.c = CMSG_DATA(cmptr); |
p.c = CMSG_DATA(cmptr); |
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) |
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) |
dst_addr_4 = dst_addr.addr.addr4 = *(p.a); | dst_addr_4 = dst_addr.addr4 = *(p.a); |
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) |
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) |
#ifdef HAVE_SOLARIS_NETWORK |
#ifdef HAVE_SOLARIS_NETWORK |
if_index = *(p.i); |
if_index = *(p.i); |
Line 1290 void receive_query(struct listener *listen, time_t now
|
Line 1490 void receive_query(struct listener *listen, time_t now
|
} |
} |
#endif |
#endif |
|
|
#ifdef HAVE_IPV6 | if (family == AF_INET6) |
if (listen->family == AF_INET6) | |
{ |
{ |
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo) |
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo) |
Line 1302 void receive_query(struct listener *listen, time_t now
|
Line 1501 void receive_query(struct listener *listen, time_t now
|
} p; |
} p; |
p.c = CMSG_DATA(cmptr); |
p.c = CMSG_DATA(cmptr); |
|
|
dst_addr.addr.addr6 = p.p->ipi6_addr; | dst_addr.addr6 = p.p->ipi6_addr; |
if_index = p.p->ipi6_ifindex; |
if_index = p.p->ipi6_ifindex; |
} |
} |
} |
} |
#endif |
|
|
|
/* enforce available interface configuration */ |
/* enforce available interface configuration */ |
|
|
if (!indextoname(listen->fd, if_index, ifr.ifr_name)) |
if (!indextoname(listen->fd, if_index, ifr.ifr_name)) |
return; |
return; |
|
|
if (!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns)) | if (!iface_check(family, &dst_addr, ifr.ifr_name, &auth_dns)) |
{ |
{ |
if (!option_bool(OPT_CLEVERBIND)) |
if (!option_bool(OPT_CLEVERBIND)) |
enumerate_interfaces(0); |
enumerate_interfaces(0); |
if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name) && | if (!loopback_exception(listen->fd, family, &dst_addr, ifr.ifr_name) && |
!label_exception(if_index, listen->family, &dst_addr)) | !label_exception(if_index, family, &dst_addr)) |
return; |
return; |
} |
} |
|
|
if (listen->family == AF_INET && option_bool(OPT_LOCALISE)) | if (family == AF_INET && option_bool(OPT_LOCALISE)) |
{ |
{ |
struct irec *iface; |
struct irec *iface; |
|
|
/* get the netmask of the interface whch has the address we were sent to. | /* get the netmask of the interface which has the address we were sent to. |
This is no neccessarily the interface we arrived on. */ | This is no necessarily the interface we arrived on. */ |
|
|
for (iface = daemon->interfaces; iface; iface = iface->next) |
for (iface = daemon->interfaces; iface; iface = iface->next) |
if (iface->addr.sa.sa_family == AF_INET && |
if (iface->addr.sa.sa_family == AF_INET && |
Line 1355 void receive_query(struct listener *listen, time_t now
|
Line 1553 void receive_query(struct listener *listen, time_t now
|
pass these in global variables - sorry. */ |
pass these in global variables - sorry. */ |
daemon->log_display_id = ++daemon->log_id; |
daemon->log_display_id = ++daemon->log_id; |
daemon->log_source_addr = &source_addr; |
daemon->log_source_addr = &source_addr; |
| |
| #ifdef HAVE_DUMPFILE |
| dump_packet(DUMP_QUERY, daemon->packet, (size_t)n, &source_addr, NULL); |
| #endif |
| |
if (extract_request(header, (size_t)n, daemon->namebuff, &type)) |
if (extract_request(header, (size_t)n, daemon->namebuff, &type)) |
{ |
{ |
#ifdef HAVE_AUTH |
#ifdef HAVE_AUTH |
Line 1363 void receive_query(struct listener *listen, time_t now
|
Line 1565 void receive_query(struct listener *listen, time_t now
|
#endif |
#endif |
char *types = querystr(auth_dns ? "auth" : "query", type); |
char *types = querystr(auth_dns ? "auth" : "query", type); |
|
|
if (listen->family == AF_INET) | if (family == AF_INET) |
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, |
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, |
(struct all_addr *)&source_addr.in.sin_addr, types); | (union all_addr *)&source_addr.in.sin_addr, types); |
#ifdef HAVE_IPV6 | |
else |
else |
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, |
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, |
(struct all_addr *)&source_addr.in6.sin6_addr, types); | (union all_addr *)&source_addr.in6.sin6_addr, types); |
#endif | |
|
|
#ifdef HAVE_AUTH |
#ifdef HAVE_AUTH |
/* find queries for zones we're authoritative for, and answer them directly */ |
/* find queries for zones we're authoritative for, and answer them directly */ |
Line 1408 void receive_query(struct listener *listen, time_t now
|
Line 1608 void receive_query(struct listener *listen, time_t now
|
defaults to 512 */ |
defaults to 512 */ |
if (udp_size > daemon->edns_pktsz) |
if (udp_size > daemon->edns_pktsz) |
udp_size = daemon->edns_pktsz; |
udp_size = daemon->edns_pktsz; |
|
else if (udp_size < PACKETSZ) |
|
udp_size = PACKETSZ; /* Sanity check - can't reduce below default. RFC 6891 6.2.3 */ |
} |
} |
|
|
#ifdef HAVE_AUTH |
#ifdef HAVE_AUTH |
Line 1419 void receive_query(struct listener *listen, time_t now
|
Line 1621 void receive_query(struct listener *listen, time_t now
|
{ |
{ |
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), |
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), |
(char *)header, m, &source_addr, &dst_addr, if_index); |
(char *)header, m, &source_addr, &dst_addr, if_index); |
daemon->auth_answer++; | daemon->metrics[METRIC_DNS_AUTH_ANSWERED]++; |
} |
} |
} |
} |
else |
else |
Line 1437 void receive_query(struct listener *listen, time_t now
|
Line 1639 void receive_query(struct listener *listen, time_t now
|
{ |
{ |
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), |
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), |
(char *)header, m, &source_addr, &dst_addr, if_index); |
(char *)header, m, &source_addr, &dst_addr, if_index); |
daemon->local_answer++; | daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++; |
} |
} |
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index, |
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index, |
header, (size_t)n, now, NULL, ad_reqd, do_bit)) |
header, (size_t)n, now, NULL, ad_reqd, do_bit)) |
daemon->queries_forwarded++; | daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++; |
else |
else |
daemon->local_answer++; | daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++; |
} |
} |
} |
} |
|
|
#ifdef HAVE_DNSSEC |
#ifdef HAVE_DNSSEC |
/* Recurse up the key heirarchy */ | /* Recurse up the key hierarchy */ |
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n, |
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n, |
int class, char *name, char *keyname, struct server *server, |
int class, char *name, char *keyname, struct server *server, |
int have_mark, unsigned int mark, int *keycount) |
int have_mark, unsigned int mark, int *keycount) |
Line 1458 static int tcp_key_recurse(time_t now, int status, str
|
Line 1660 static int tcp_key_recurse(time_t now, int status, str
|
unsigned char *payload = NULL; |
unsigned char *payload = NULL; |
struct dns_header *new_header = NULL; |
struct dns_header *new_header = NULL; |
u16 *length = NULL; |
u16 *length = NULL; |
| |
while (1) |
while (1) |
{ |
{ |
int type = SERV_DO_DNSSEC; |
int type = SERV_DO_DNSSEC; |
char *domain; |
char *domain; |
size_t m; |
size_t m; |
unsigned char c1, c2; |
unsigned char c1, c2; |
| struct server *firstsendto = NULL; |
| |
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */ |
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */ |
if (--(*keycount) == 0) |
if (--(*keycount) == 0) |
new_status = STAT_ABANDONED; |
new_status = STAT_ABANDONED; |
Line 1474 static int tcp_key_recurse(time_t now, int status, str
|
Line 1677 static int tcp_key_recurse(time_t now, int status, str
|
else if (status == STAT_NEED_DS) |
else if (status == STAT_NEED_DS) |
new_status = dnssec_validate_ds(now, header, n, name, keyname, class); |
new_status = dnssec_validate_ds(now, header, n, name, keyname, class); |
else |
else |
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, option_bool(OPT_DNSSEC_NO_SIGN), NULL, NULL); | new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, |
| !option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC), |
| NULL, NULL, NULL); |
|
|
if (new_status != STAT_NEED_DS && new_status != STAT_NEED_KEY) |
if (new_status != STAT_NEED_DS && new_status != STAT_NEED_KEY) |
break; |
break; |
Line 1494 static int tcp_key_recurse(time_t now, int status, str
|
Line 1699 static int tcp_key_recurse(time_t now, int status, str
|
new_status = STAT_ABANDONED; |
new_status = STAT_ABANDONED; |
break; |
break; |
} |
} |
| |
m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, |
m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, |
new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz); | new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, server->edns_pktsz); |
|
|
*length = htons(m); |
*length = htons(m); |
|
|
/* Find server to forward to. This will normally be the |
/* Find server to forward to. This will normally be the |
same as for the original query, but may be another if |
same as for the original query, but may be another if |
servers for domains are involved. */ |
servers for domains are involved. */ |
if (search_servers(now, NULL, F_QUERY, keyname, &type, &domain, NULL) == 0) | if (search_servers(now, NULL, F_DNSSECOK, keyname, &type, &domain, NULL) != 0) |
{ |
{ |
struct server *start = server, *new_server = NULL; | new_status = STAT_ABANDONED; |
type &= ~SERV_DO_DNSSEC; | break; |
| } |
while (1) | |
| while (1) |
| { |
| int data_sent = 0; |
| |
| if (!firstsendto) |
| firstsendto = server; |
| else |
{ |
{ |
if (type == (start->flags & SERV_TYPE) && | if (!(server = server->next)) |
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) && | server = daemon->servers; |
!(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP))) | if (server == firstsendto) |
{ |
{ |
new_server = start; | /* can't find server to accept our query. */ |
if (server == start) | new_status = STAT_ABANDONED; |
{ | break; |
new_server = NULL; | |
break; | |
} | |
} |
} |
|
|
if (!(start = start->next)) |
|
start = daemon->servers; |
|
if (start == server) |
|
break; |
|
} |
} |
| |
| if (type != (server->flags & (SERV_TYPE | SERV_DO_DNSSEC)) || |
| (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, server->domain)) || |
| (server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP))) |
| continue; |
|
|
if (new_server) | retry: |
| /* may need to make new connection. */ |
| if (server->tcpfd == -1) |
{ |
{ |
server = new_server; | if ((server->tcpfd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1) |
/* may need to make new connection. */ | continue; /* No good, next server */ |
if (server->tcpfd == -1) | |
{ | |
if ((server->tcpfd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1) | |
{ | |
new_status = STAT_ABANDONED; | |
break; | |
} | |
| |
#ifdef HAVE_CONNTRACK |
#ifdef HAVE_CONNTRACK |
/* Copy connection mark of incoming query to outgoing connection. */ | /* Copy connection mark of incoming query to outgoing connection. */ |
if (have_mark) | if (have_mark) |
setsockopt(server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); | setsockopt(server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); |
#endif |
#endif |
| |
if (!local_bind(server->tcpfd, &server->source_addr, server->interface, 1) || | if (!local_bind(server->tcpfd, &server->source_addr, server->interface, 0, 1)) |
connect(server->tcpfd, &server->addr.sa, sa_len(&server->addr)) == -1) | { |
{ | close(server->tcpfd); |
close(server->tcpfd); | server->tcpfd = -1; |
server->tcpfd = -1; | continue; /* No good, next server */ |
new_status = STAT_ABANDONED; | |
break; | |
} | |
| |
} |
} |
|
|
|
#ifdef MSG_FASTOPEN |
|
while(retry_send(sendto(server->tcpfd, packet, m + sizeof(u16), |
|
MSG_FASTOPEN, &server->addr.sa, sa_len(&server->addr)))); |
|
|
|
if (errno == 0) |
|
data_sent = 1; |
|
#endif |
|
|
|
if (!data_sent && connect(server->tcpfd, &server->addr.sa, sa_len(&server->addr)) == -1) |
|
{ |
|
close(server->tcpfd); |
|
server->tcpfd = -1; |
|
continue; /* No good, next server */ |
|
} |
|
|
|
server->flags &= ~SERV_GOT_TCP; |
} |
} |
} | |
| if ((!data_sent && !read_write(server->tcpfd, packet, m + sizeof(u16), 0)) || |
| !read_write(server->tcpfd, &c1, 1, 1) || |
| !read_write(server->tcpfd, &c2, 1, 1) || |
| !read_write(server->tcpfd, payload, (c1 << 8) | c2, 1)) |
| { |
| close(server->tcpfd); |
| server->tcpfd = -1; |
| /* We get data then EOF, reopen connection to same server, |
| else try next. This avoids DoS from a server which accepts |
| connections and then closes them. */ |
| if (server->flags & SERV_GOT_TCP) |
| goto retry; |
| else |
| continue; |
| } |
|
|
| |
if (!read_write(server->tcpfd, packet, m + sizeof(u16), 0) || | if (server->addr.sa.sa_family == AF_INET) |
!read_write(server->tcpfd, &c1, 1, 1) || | log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, keyname, (union all_addr *)&(server->addr.in.sin_addr), |
!read_write(server->tcpfd, &c2, 1, 1) || | querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS)); |
!read_write(server->tcpfd, payload, (c1 << 8) | c2, 1)) | else |
{ | log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, keyname, (union all_addr *)&(server->addr.in6.sin6_addr), |
new_status = STAT_ABANDONED; | querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS)); |
| |
| server->flags |= SERV_GOT_TCP; |
| |
| m = (c1 << 8) | c2; |
| new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount); |
break; |
break; |
} |
} |
|
|
m = (c1 << 8) | c2; |
|
|
|
new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount); |
|
|
|
if (new_status != STAT_OK) |
if (new_status != STAT_OK) |
break; |
break; |
} |
} |
| |
if (packet) |
if (packet) |
free(packet); |
free(packet); |
|
|
Line 1587 static int tcp_key_recurse(time_t now, int status, str
|
Line 1819 static int tcp_key_recurse(time_t now, int status, str
|
|
|
|
|
/* The daemon forks before calling this: it should deal with one connection, |
/* The daemon forks before calling this: it should deal with one connection, |
blocking as neccessary, and then return. Note, need to be a bit careful | blocking as necessary, and then return. Note, need to be a bit careful |
about resources for debug mode, when the fork is suppressed: that's |
about resources for debug mode, when the fork is suppressed: that's |
done by the caller. */ |
done by the caller. */ |
unsigned char *tcp_request(int confd, time_t now, |
unsigned char *tcp_request(int confd, time_t now, |
Line 1599 unsigned char *tcp_request(int confd, time_t now,
|
Line 1831 unsigned char *tcp_request(int confd, time_t now,
|
int local_auth = 0; |
int local_auth = 0; |
#endif |
#endif |
int checking_disabled, do_bit, added_pheader = 0, have_pseudoheader = 0; |
int checking_disabled, do_bit, added_pheader = 0, have_pseudoheader = 0; |
int check_subnet, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; | int check_subnet, cacheable, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; |
size_t m; |
size_t m; |
unsigned short qtype; |
unsigned short qtype; |
unsigned int gotname; |
unsigned int gotname; |
Line 1629 unsigned char *tcp_request(int confd, time_t now,
|
Line 1861 unsigned char *tcp_request(int confd, time_t now,
|
/* Get connection mark of incoming query to set on outgoing connections. */ |
/* Get connection mark of incoming query to set on outgoing connections. */ |
if (option_bool(OPT_CONNTRACK)) |
if (option_bool(OPT_CONNTRACK)) |
{ |
{ |
struct all_addr local; | union all_addr local; |
#ifdef HAVE_IPV6 | |
if (local_addr->sa.sa_family == AF_INET6) |
if (local_addr->sa.sa_family == AF_INET6) |
local.addr.addr6 = local_addr->in6.sin6_addr; | local.addr6 = local_addr->in6.sin6_addr; |
else |
else |
#endif | local.addr4 = local_addr->in.sin_addr; |
local.addr.addr4 = local_addr->in.sin_addr; | |
|
|
have_mark = get_incoming_mark(&peer_addr, &local, 1, &mark); |
have_mark = get_incoming_mark(&peer_addr, &local, 1, &mark); |
} |
} |
Line 1645 unsigned char *tcp_request(int confd, time_t now,
|
Line 1876 unsigned char *tcp_request(int confd, time_t now,
|
if (option_bool(OPT_LOCAL_SERVICE)) |
if (option_bool(OPT_LOCAL_SERVICE)) |
{ |
{ |
struct addrlist *addr; |
struct addrlist *addr; |
#ifdef HAVE_IPV6 | |
if (peer_addr.sa.sa_family == AF_INET6) |
if (peer_addr.sa.sa_family == AF_INET6) |
{ |
{ |
for (addr = daemon->interface_addrs; addr; addr = addr->next) |
for (addr = daemon->interface_addrs; addr; addr = addr->next) |
if ((addr->flags & ADDRLIST_IPV6) && |
if ((addr->flags & ADDRLIST_IPV6) && |
is_same_net6(&addr->addr.addr.addr6, &peer_addr.in6.sin6_addr, addr->prefixlen)) | is_same_net6(&addr->addr.addr6, &peer_addr.in6.sin6_addr, addr->prefixlen)) |
break; |
break; |
} |
} |
else |
else |
#endif |
|
{ |
{ |
struct in_addr netmask; |
struct in_addr netmask; |
for (addr = daemon->interface_addrs; addr; addr = addr->next) |
for (addr = daemon->interface_addrs; addr; addr = addr->next) |
{ |
{ |
netmask.s_addr = htonl(~(in_addr_t)0 << (32 - addr->prefixlen)); |
netmask.s_addr = htonl(~(in_addr_t)0 << (32 - addr->prefixlen)); |
if (!(addr->flags & ADDRLIST_IPV6) && |
if (!(addr->flags & ADDRLIST_IPV6) && |
is_same_net(addr->addr.addr.addr4, peer_addr.in.sin_addr, netmask)) | is_same_net(addr->addr.addr4, peer_addr.in.sin_addr, netmask)) |
break; |
break; |
} |
} |
} |
} |
Line 1683 unsigned char *tcp_request(int confd, time_t now,
|
Line 1913 unsigned char *tcp_request(int confd, time_t now,
|
|
|
if (size < (int)sizeof(struct dns_header)) |
if (size < (int)sizeof(struct dns_header)) |
continue; |
continue; |
|
|
|
/* Clear buffer beyond request to avoid risk of |
|
information disclosure. */ |
|
memset(payload + size, 0, 65536 - size); |
|
|
query_count++; |
query_count++; |
|
|
Line 1704 unsigned char *tcp_request(int confd, time_t now,
|
Line 1938 unsigned char *tcp_request(int confd, time_t now,
|
|
|
if (peer_addr.sa.sa_family == AF_INET) |
if (peer_addr.sa.sa_family == AF_INET) |
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, |
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, |
(struct all_addr *)&peer_addr.in.sin_addr, types); | (union all_addr *)&peer_addr.in.sin_addr, types); |
#ifdef HAVE_IPV6 | |
else |
else |
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, |
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, |
(struct all_addr *)&peer_addr.in6.sin6_addr, types); | (union all_addr *)&peer_addr.in6.sin6_addr, types); |
#endif | |
|
|
#ifdef HAVE_AUTH |
#ifdef HAVE_AUTH |
/* find queries for zones we're authoritative for, and answer them directly */ |
/* find queries for zones we're authoritative for, and answer them directly */ |
Line 1765 unsigned char *tcp_request(int confd, time_t now,
|
Line 1997 unsigned char *tcp_request(int confd, time_t now,
|
if (m == 0) |
if (m == 0) |
{ |
{ |
unsigned int flags = 0; |
unsigned int flags = 0; |
struct all_addr *addrp = NULL; | union all_addr *addrp = NULL; |
int type = SERV_DO_DNSSEC; |
int type = SERV_DO_DNSSEC; |
char *domain = NULL; |
char *domain = NULL; |
size_t new_size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet); | unsigned char *oph = find_pseudoheader(header, size, NULL, NULL, NULL, NULL); |
|
|
if (size != new_size) | size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet, &cacheable); |
{ | |
added_pheader = 1; | |
size = new_size; | |
} | |
| |
if (gotname) |
if (gotname) |
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); |
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); |
| |
| #ifdef HAVE_DNSSEC |
| if (option_bool(OPT_DNSSEC_VALID) && (type & SERV_DO_DNSSEC)) |
| { |
| size = add_do_bit(header, size, ((unsigned char *) header) + 65536); |
| |
| /* For debugging, set Checking Disabled, otherwise, have the upstream check too, |
| this allows it to select auth servers when one is returning bad data. */ |
| if (option_bool(OPT_DNSSEC_DEBUG)) |
| header->hb4 |= HB4_CD; |
| } |
| #endif |
| |
| /* Check if we added a pheader on forwarding - may need to |
| strip it from the reply. */ |
| if (!oph && find_pseudoheader(header, size, NULL, NULL, NULL, NULL)) |
| added_pheader = 1; |
| |
type &= ~SERV_DO_DNSSEC; |
type &= ~SERV_DO_DNSSEC; |
|
|
if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server) |
if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server) |
Line 1789 unsigned char *tcp_request(int confd, time_t now,
|
Line 2034 unsigned char *tcp_request(int confd, time_t now,
|
if (!flags && last_server) |
if (!flags && last_server) |
{ |
{ |
struct server *firstsendto = NULL; |
struct server *firstsendto = NULL; |
#ifdef HAVE_DNSSEC | unsigned char hash[HASH_SIZE]; |
unsigned char *newhash, hash[HASH_SIZE]; | memcpy(hash, hash_questions(header, (unsigned int)size, daemon->namebuff), HASH_SIZE); |
if ((newhash = hash_questions(header, (unsigned int)size, daemon->namebuff))) | |
memcpy(hash, newhash, HASH_SIZE); | |
else | |
memset(hash, 0, HASH_SIZE); | |
#else | |
unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff); | |
#endif | |
/* Loop round available servers until we succeed in connecting to one. |
/* Loop round available servers until we succeed in connecting to one. |
Note that this code subtley ensures that consecutive queries on this connection | Note that this code subtly ensures that consecutive queries on this connection |
which can go to the same server, do so. */ |
which can go to the same server, do so. */ |
while (1) |
while (1) |
{ |
{ |
|
int data_sent = 0; |
|
|
if (!firstsendto) |
if (!firstsendto) |
firstsendto = last_server; |
firstsendto = last_server; |
else |
else |
Line 1819 unsigned char *tcp_request(int confd, time_t now,
|
Line 2060 unsigned char *tcp_request(int confd, time_t now,
|
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)) || |
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)) || |
(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP))) |
(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP))) |
continue; |
continue; |
| |
| retry: |
| *length = htons(size); |
| |
if (last_server->tcpfd == -1) |
if (last_server->tcpfd == -1) |
{ |
{ |
if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1) |
if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1) |
Line 1829 unsigned char *tcp_request(int confd, time_t now,
|
Line 2073 unsigned char *tcp_request(int confd, time_t now,
|
/* Copy connection mark of incoming query to outgoing connection. */ |
/* Copy connection mark of incoming query to outgoing connection. */ |
if (have_mark) |
if (have_mark) |
setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); |
setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); |
#endif | #endif |
|
|
if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) || | if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 0, 1))) |
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1)) | |
{ |
{ |
close(last_server->tcpfd); |
close(last_server->tcpfd); |
last_server->tcpfd = -1; |
last_server->tcpfd = -1; |
continue; |
continue; |
} |
} |
|
|
#ifdef HAVE_DNSSEC | #ifdef MSG_FASTOPEN |
if (option_bool(OPT_DNSSEC_VALID) && (last_server->flags & SERV_DO_DNSSEC)) | while(retry_send(sendto(last_server->tcpfd, packet, size + sizeof(u16), |
| MSG_FASTOPEN, &last_server->addr.sa, sa_len(&last_server->addr)))); |
| |
| if (errno == 0) |
| data_sent = 1; |
| #endif |
| |
| if (!data_sent && connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1) |
{ |
{ |
new_size = add_do_bit(header, size, ((unsigned char *) header) + 65536); | close(last_server->tcpfd); |
| last_server->tcpfd = -1; |
if (size != new_size) | continue; |
{ | |
added_pheader = 1; | |
size = new_size; | |
} | |
| |
/* For debugging, set Checking Disabled, otherwise, have the upstream check too, | |
this allows it to select auth servers when one is returning bad data. */ | |
if (option_bool(OPT_DNSSEC_DEBUG)) | |
header->hb4 |= HB4_CD; | |
} |
} |
#endif | |
| last_server->flags &= ~SERV_GOT_TCP; |
} |
} |
|
|
*length = htons(size); |
|
|
|
/* get query name again for logging - may have been overwritten */ |
/* get query name again for logging - may have been overwritten */ |
if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype))) |
if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype))) |
strcpy(daemon->namebuff, "query"); |
strcpy(daemon->namebuff, "query"); |
|
|
if (!read_write(last_server->tcpfd, packet, size + sizeof(u16), 0) || | if ((!data_sent && !read_write(last_server->tcpfd, packet, size + sizeof(u16), 0)) || |
!read_write(last_server->tcpfd, &c1, 1, 1) || |
!read_write(last_server->tcpfd, &c1, 1, 1) || |
!read_write(last_server->tcpfd, &c2, 1, 1) || |
!read_write(last_server->tcpfd, &c2, 1, 1) || |
!read_write(last_server->tcpfd, payload, (c1 << 8) | c2, 1)) |
!read_write(last_server->tcpfd, payload, (c1 << 8) | c2, 1)) |
{ |
{ |
close(last_server->tcpfd); |
close(last_server->tcpfd); |
last_server->tcpfd = -1; |
last_server->tcpfd = -1; |
continue; | /* We get data then EOF, reopen connection to same server, |
} | else try next. This avoids DoS from a server which accepts |
| connections and then closes them. */ |
| if (last_server->flags & SERV_GOT_TCP) |
| goto retry; |
| else |
| continue; |
| } |
|
|
|
last_server->flags |= SERV_GOT_TCP; |
|
|
m = (c1 << 8) | c2; |
m = (c1 << 8) | c2; |
|
|
if (last_server->addr.sa.sa_family == AF_INET) |
if (last_server->addr.sa.sa_family == AF_INET) |
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, |
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, |
(struct all_addr *)&last_server->addr.in.sin_addr, NULL); | (union all_addr *)&last_server->addr.in.sin_addr, NULL); |
#ifdef HAVE_IPV6 | |
else |
else |
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, |
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, |
(struct all_addr *)&last_server->addr.in6.sin6_addr, NULL); | (union all_addr *)&last_server->addr.in6.sin6_addr, NULL); |
#endif | |
|
|
#ifdef HAVE_DNSSEC |
#ifdef HAVE_DNSSEC |
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (last_server->flags & SERV_DO_DNSSEC)) |
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (last_server->flags & SERV_DO_DNSSEC)) |
Line 1904 unsigned char *tcp_request(int confd, time_t now,
|
Line 2150 unsigned char *tcp_request(int confd, time_t now,
|
if (status == STAT_BOGUS && extract_request(header, m, daemon->namebuff, NULL)) |
if (status == STAT_BOGUS && extract_request(header, m, daemon->namebuff, NULL)) |
domain = daemon->namebuff; |
domain = daemon->namebuff; |
|
|
log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result); | log_query(F_SECSTAT, domain, NULL, result); |
|
|
if (status == STAT_BOGUS) |
if (status == STAT_BOGUS) |
{ |
{ |
Line 1929 unsigned char *tcp_request(int confd, time_t now,
|
Line 2175 unsigned char *tcp_request(int confd, time_t now,
|
/* If the crc of the question section doesn't match the crc we sent, then |
/* 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 |
someone might be attempting to insert bogus values into the cache by |
sending replies containing questions and bogus answers. */ |
sending replies containing questions and bogus answers. */ |
#ifdef HAVE_DNSSEC | if (memcmp(hash, hash_questions(header, (unsigned int)m, daemon->namebuff), HASH_SIZE) != 0) |
newhash = hash_questions(header, (unsigned int)m, daemon->namebuff); | |
if (!newhash || memcmp(hash, newhash, HASH_SIZE) != 0) | |
{ |
{ |
m = 0; |
m = 0; |
break; |
break; |
} |
} |
#else |
|
if (crc != questions_crc(header, (unsigned int)m, daemon->namebuff)) |
|
{ |
|
m = 0; |
|
break; |
|
} |
|
#endif |
|
|
|
|
/* Never cache answers which are contingent on the source or MAC address EDSN0 option, |
|
since the cache is ignorant of such things. */ |
|
if (!cacheable) |
|
no_cache_dnssec = 1; |
|
|
m = process_reply(header, now, last_server, (unsigned int)m, |
m = process_reply(header, now, last_server, (unsigned int)m, |
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer, |
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer, |
ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr); |
ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr); |
Line 1954 unsigned char *tcp_request(int confd, time_t now,
|
Line 2196 unsigned char *tcp_request(int confd, time_t now,
|
|
|
/* In case of local answer or no connections made. */ |
/* In case of local answer or no connections made. */ |
if (m == 0) |
if (m == 0) |
m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl); | { |
| m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl); |
| if (have_pseudoheader) |
| m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); |
| } |
} |
} |
} |
} |
|
|
Line 1978 static struct frec *allocate_frec(time_t now)
|
Line 2224 static struct frec *allocate_frec(time_t now)
|
f->sentto = NULL; |
f->sentto = NULL; |
f->rfd4 = NULL; |
f->rfd4 = NULL; |
f->flags = 0; |
f->flags = 0; |
#ifdef HAVE_IPV6 |
|
f->rfd6 = NULL; |
f->rfd6 = NULL; |
#endif |
|
#ifdef HAVE_DNSSEC |
#ifdef HAVE_DNSSEC |
f->dependent = NULL; |
f->dependent = NULL; |
f->blocking_query = NULL; |
f->blocking_query = NULL; |
Line 2036 void free_rfd(struct randfd *rfd)
|
Line 2280 void free_rfd(struct randfd *rfd)
|
|
|
static void free_frec(struct frec *f) |
static void free_frec(struct frec *f) |
{ |
{ |
|
struct frec_src *last; |
|
|
|
/* add back to freelist if not the record builtin to every frec. */ |
|
for (last = f->frec_src.next; last && last->next; last = last->next) ; |
|
if (last) |
|
{ |
|
last->next = daemon->free_frec_src; |
|
daemon->free_frec_src = f->frec_src.next; |
|
} |
|
|
|
f->frec_src.next = NULL; |
free_rfd(f->rfd4); |
free_rfd(f->rfd4); |
f->rfd4 = NULL; |
f->rfd4 = NULL; |
f->sentto = NULL; |
f->sentto = NULL; |
f->flags = 0; |
f->flags = 0; |
|
|
#ifdef HAVE_IPV6 |
|
free_rfd(f->rfd6); |
free_rfd(f->rfd6); |
f->rfd6 = NULL; |
f->rfd6 = NULL; |
#endif |
|
|
|
#ifdef HAVE_DNSSEC |
#ifdef HAVE_DNSSEC |
if (f->stash) |
if (f->stash) |
Line 2061 static void free_frec(struct frec *f)
|
Line 2313 static void free_frec(struct frec *f)
|
#endif |
#endif |
} |
} |
|
|
|
|
|
|
/* if wait==NULL return a free or older than TIMEOUT record. |
/* if wait==NULL return a free or older than TIMEOUT record. |
else return *wait zero if one available, or *wait is delay to |
else return *wait zero if one available, or *wait is delay to |
when the oldest in-use record will expire. Impose an absolute |
when the oldest in-use record will expire. Impose an absolute |
limit of 4*TIMEOUT before we wipe things (for random sockets). |
limit of 4*TIMEOUT before we wipe things (for random sockets). |
If force is set, always return a result, even if we have | If force is non-NULL, always return a result, even if we have |
to allocate above the limit. */ | to allocate above the limit, and never free the record pointed |
struct frec *get_new_frec(time_t now, int *wait, int force) | to by the force argument. */ |
| struct frec *get_new_frec(time_t now, int *wait, struct frec *force) |
{ |
{ |
struct frec *f, *oldest, *target; |
struct frec *f, *oldest, *target; |
int count; |
int count; |
Line 2084 struct frec *get_new_frec(time_t now, int *wait, int f
|
Line 2339 struct frec *get_new_frec(time_t now, int *wait, int f
|
/* Don't free DNSSEC sub-queries here, as we may end up with |
/* Don't free DNSSEC sub-queries here, as we may end up with |
dangling references to them. They'll go when their "real" query |
dangling references to them. They'll go when their "real" query |
is freed. */ |
is freed. */ |
if (!f->dependent) | if (!f->dependent && f != force) |
#endif |
#endif |
{ |
{ |
if (difftime(now, f->time) >= 4*TIMEOUT) |
if (difftime(now, f->time) >= 4*TIMEOUT) |
Line 2107 struct frec *get_new_frec(time_t now, int *wait, int f
|
Line 2362 struct frec *get_new_frec(time_t now, int *wait, int f
|
|
|
/* can't find empty one, use oldest if there is one |
/* can't find empty one, use oldest if there is one |
and it's older than timeout */ |
and it's older than timeout */ |
if (oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT) | if (!force && oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT) |
{ |
{ |
/* keep stuff for twice timeout if we can by allocating a new |
/* keep stuff for twice timeout if we can by allocating a new |
record instead */ |
record instead */ |
Line 2147 struct frec *get_new_frec(time_t now, int *wait, int f
|
Line 2402 struct frec *get_new_frec(time_t now, int *wait, int f
|
|
|
return f; /* OK if malloc fails and this is NULL */ |
return f; /* OK if malloc fails and this is NULL */ |
} |
} |
| |
/* crc is all-ones if not known. */ | static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash) |
static struct frec *lookup_frec(unsigned short id, void *hash) | |
{ |
{ |
struct frec *f; |
struct frec *f; |
|
|
for(f = daemon->frec_list; f; f = f->next) |
for(f = daemon->frec_list; f; f = f->next) |
if (f->sentto && f->new_id == id && |
if (f->sentto && f->new_id == id && |
(!hash || memcmp(hash, f->hash, HASH_SIZE) == 0)) | (memcmp(hash, f->hash, HASH_SIZE) == 0)) |
return f; | { |
| /* sent from random port */ |
| if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd) |
| return f; |
| |
| if (family == AF_INET6 && f->rfd6 && f->rfd6->fd == fd) |
| return f; |
| |
| /* sent to upstream from bound socket. */ |
| if (f->sentto->sfd && f->sentto->sfd->fd == fd) |
| return f; |
| } |
|
|
return NULL; |
return NULL; |
} |
} |
Line 2166 static struct frec *lookup_frec_by_sender(unsigned sho
|
Line 2431 static struct frec *lookup_frec_by_sender(unsigned sho
|
void *hash) |
void *hash) |
{ |
{ |
struct frec *f; |
struct frec *f; |
|
struct frec_src *src; |
|
|
|
for (f = daemon->frec_list; f; f = f->next) |
|
if (f->sentto && |
|
!(f->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) && |
|
memcmp(hash, f->hash, HASH_SIZE) == 0) |
|
for (src = &f->frec_src; src; src = src->next) |
|
if (src->orig_id == id && |
|
sockaddr_isequal(&src->source, addr)) |
|
return f; |
|
|
|
return NULL; |
|
} |
|
|
|
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags) |
|
{ |
|
struct frec *f; |
|
|
|
/* FREC_DNSKEY and FREC_DS_QUERY are never set in flags, so the test below |
|
ensures that no frec created for internal DNSSEC query can be returned here. |
|
|
|
Similarly FREC_NO_CACHE is never set in flags, so a query which is |
|
contigent on a particular source address EDNS0 option will never be matched. */ |
|
|
|
#define FLAGMASK (FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION \ |
|
| FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE) |
|
|
for(f = daemon->frec_list; f; f = f->next) |
for(f = daemon->frec_list; f; f = f->next) |
if (f->sentto && |
if (f->sentto && |
f->orig_id == id && | (f->flags & FLAGMASK) == flags && |
memcmp(hash, f->hash, HASH_SIZE) == 0 && | memcmp(hash, f->hash, HASH_SIZE) == 0) |
sockaddr_isequal(&f->source, addr)) | |
return f; |
return f; |
| |
return NULL; |
return NULL; |
} |
} |
| |
/* Send query packet again, if we can. */ |
/* Send query packet again, if we can. */ |
void resend_query() |
void resend_query() |
{ |
{ |
Line 2217 void server_gone(struct server *server)
|
Line 2507 void server_gone(struct server *server)
|
static unsigned short get_id(void) |
static unsigned short get_id(void) |
{ |
{ |
unsigned short ret = 0; |
unsigned short ret = 0; |
|
struct frec *f; |
|
|
do | while (1) |
ret = rand16(); | { |
while (lookup_frec(ret, NULL)); | ret = rand16(); |
| |
return ret; | /* ensure id is unique. */ |
| for (f = daemon->frec_list; f; f = f->next) |
| if (f->sentto && f->new_id == ret) |
| break; |
| |
| if (!f) |
| return ret; |
| } |
} |
} |
|
|
|
|