"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/forward.c" between
dnsmasq-2.82.tar.xz and dnsmasq-2.83.tar.xz

About: Dnsmasq is a lightweight caching DNS forwarder and DHCP server.

forward.c  (dnsmasq-2.82.tar.xz):forward.c  (dnsmasq-2.83.tar.xz)
skipping to change at line 19 skipping to change at line 19
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#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 *has h);
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, union all_addr *source, union mysockaddr *to, union all_addr *source,
unsigned int iface) unsigned int iface)
{ {
struct msghdr msg; struct msghdr msg;
skipping to change at line 258 skipping to change at line 260
static int forward_query(int udpfd, union mysockaddr *udpaddr, static int forward_query(int udpfd, union mysockaddr *udpaddr,
union 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;
union 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); 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), ud paddr, hash)))) if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, ha sh)))
{ {
/* 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.
If that generates an answer, it will become the new default If that generates an answer, it will become the new default
for this server */ for this server */
forward->flags |= FREC_TEST_PKTSZ; forward->flags |= FREC_TEST_PKTSZ;
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
/* If we've already got an answer to this query, but we're awaiting keys f or validation, /* If we've already got an answer to this query, but we're awaiting keys f or validation,
there's no point retrying the query, retry the key query instead...... * / there's no point retrying the query, retry the key query instead...... * /
skipping to change at line 341 skipping to change at line 352
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
do_dnssec = forward->sentto->flags & SERV_DO_DNSSEC; do_dnssec = forward->sentto->flags & SERV_DO_DNSSEC;
#endif #endif
if (!(start = forward->sentto->next)) if (!(start = forward->sentto->next))
start = daemon->servers; /* at end of list, recycle */ start = daemon->servers; /* at end of list, recycle */
header->id = htons(forward->new_id); header->id = htons(forward->new_id);
} }
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;
}
return 1;
}
if (gotname) if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &do main, &norebind); flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &do main, &norebind);
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
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, NULL); 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->new_id = get_id(); forward->new_id = get_id();
forward->fd = udpfd; 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)
forward->flags |= FREC_CHECKING_DISABLED; forward->flags |= FREC_CHECKING_DISABLED;
if (ad_reqd) if (ad_reqd)
forward->flags |= FREC_AD_QUESTION; forward->flags |= FREC_AD_QUESTION;
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
forward->work_counter = DNSSEC_WORK; forward->work_counter = DNSSEC_WORK;
if (do_bit) if (do_bit)
forward->flags |= FREC_DO_QUESTION; forward->flags |= FREC_DO_QUESTION;
skipping to change at line 413 skipping to change at line 458
} }
} }
/* check for send errors here (no route to host) /* check for send errors here (no route to host)
if we fail to send to all nameservers, send back an error if we fail to send to all nameservers, send back an error
packet straight away (helps modem users when offline) */ packet straight away (helps modem users when offline) */
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; unsigned char *pheader;
/* If a query is retried, use the log_id for the retry when logging the an swer. */ /* If a query is retried, use the log_id for the retry when logging the an swer. */
forward->log_id = daemon->log_id; forward->frec_src.log_id = daemon->log_id;
plen = 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 (subnet) if (subnet)
forward->flags |= FREC_HAS_SUBNET; forward->flags |= FREC_HAS_SUBNET;
if (!cacheable)
forward->flags |= FREC_NO_CACHE;
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && do_dnssec) if (option_bool(OPT_DNSSEC_VALID) && do_dnssec)
{ {
plen = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ); plen = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ);
/* For debugging, set Checking Disabled, otherwise, have the upstream c heck too, /* For debugging, set Checking Disabled, otherwise, have the upstream c heck 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))
header->hb4 |= HB4_CD; header->hb4 |= HB4_CD;
skipping to change at line 492 skipping to change at line 540
break; break;
daemon->rfd_save = forward->rfd4; daemon->rfd_save = forward->rfd4;
fd = forward->rfd4->fd; fd = forward->rfd4->fd;
} }
#ifdef HAVE_CONNTRACK #ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connectio n. */ /* Copy connection mark of incoming query to outgoing connectio n. */
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(unsigne d int)); setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigne d int));
} }
#endif #endif
} }
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_P HEADER)) if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_P HEADER))
{ {
/* Difficult one here. If our client didn't send EDNS0, we will have set the UDP /* Difficult one here. If our client didn't send EDNS0, we will have set the UDP
packet size to 512. But that won't provide space for the RRS IGS in many cases. packet size to 512. But that won't provide space for the RRS IGS in many cases.
skipping to change at line 557 skipping to change at line 605
start = daemon->servers; start = daemon->servers;
if (start == firstsentto) if (start == firstsentto)
break; break;
} }
if (forwarded) if (forwarded)
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 */
} }
/* could not send on, return empty answer or address if known for whole domain */ /* could not send on, return empty answer or address if known for whole domain */
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) if (oph)
plen = add_pseudoheader(header, plen, ((unsigned char *) header) + PACKET SZ, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); plen = add_pseudoheader(header, plen, ((unsigned char *) header) + PACKET SZ, 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);
skipping to change at line 771 skipping to change at line 819
/* packet from peer server, extract data for cache, and send to /* packet from peer server, extract data for cache, and send to
original requester */ original requester */
struct dns_header *header; struct dns_header *header;
union mysockaddr serveraddr; union mysockaddr serveraddr;
struct frec *forward; struct frec *forward;
socklen_t addrlen = sizeof(serveraddr); socklen_t addrlen = sizeof(serveraddr);
ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveradd r.sa, &addrlen); ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveradd r.sa, &addrlen);
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 g ood */ /* Determine the address of the server replying so that we can mark that as g ood */
if ((serveraddr.sa.sa_family = family) == AF_INET6) if ((serveraddr.sa.sa_family = family) == AF_INET6)
serveraddr.in6.sin6_flowinfo = 0; serveraddr.in6.sin6_flowinfo = 0;
header = (struct dns_header *)daemon->packet; header = (struct dns_header *)daemon->packet;
skipping to change at line 800 skipping to change at line 845
sockaddr_isequal(&server->addr, &serveraddr)) sockaddr_isequal(&server->addr, &serveraddr))
break; break;
if (!server) if (!server)
return; return;
/* If sufficient time has elapsed, try and expand UDP buffer size again. */ /* If sufficient time has elapsed, try and expand UDP buffer size again. */
if (difftime(now, server->pktsz_reduced) > UDP_TEST_TIME) if (difftime(now, server->pktsz_reduced) > UDP_TEST_TIME)
server->edns_pktsz = daemon->edns_pktsz; server->edns_pktsz = daemon->edns_pktsz;
#ifdef HAVE_DNSSEC
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 #ifdef HAVE_DUMPFILE
dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_ REPLY : DUMP_UP_REPLY, dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_ REPLY : DUMP_UP_REPLY,
(void *)header, n, &serveraddr, NULL); (void *)header, n, &serveraddr, NULL);
#endif #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))
return; return;
/* 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 || RCODE(header) == SERVFAIL) && if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) &&
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. */
{ {
unsigned char *pheader; unsigned char *pheader;
size_t plen; size_t plen;
int is_sign; int is_sign;
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
/* For DNSSEC originated queries, just retry the query to the same server. */
if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))
{ {
struct server *start; struct server *start;
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->forwardall = 2; /* only retry once */ forward->forwardall = 2; /* only retry once */
start = forward->sentto; start = forward->sentto;
skipping to change at line 861 skipping to change at line 900
if (!(start = start->next)) if (!(start = start->next))
start = daemon->servers; start = daemon->servers;
if (start == forward->sentto) if (start == forward->sentto)
break; break;
if ((start->flags & SERV_TYPE) == 0 && if ((start->flags & SERV_TYPE) == 0 &&
(start->flags & SERV_DO_DNSSEC)) (start->flags & SERV_DO_DNSSEC))
break; break;
} }
fd = -1;
if (start->sfd) if (start->sfd)
fd = start->sfd->fd; fd = start->sfd->fd;
else else
{ {
if (start->addr.sa.sa_family == AF_INET6) if (start->addr.sa.sa_family == AF_INET6)
{ {
/* may have changed family */ /* may have changed family */
if (!forward->rfd6) if (forward->rfd6 || (forward->rfd6 = allocate_rfd(AF_INET6)))
forward->rfd6 = allocate_rfd(AF_INET6); fd = forward->rfd6->fd;
fd = forward->rfd6->fd;
} }
else else
{ {
/* may have changed family */ /* may have changed family */
if (!forward->rfd4) if (forward->rfd4 || (forward->rfd4 = allocate_rfd(AF_INET)))
forward->rfd4 = allocate_rfd(AF_INET); fd = forward->rfd4->fd;
fd = forward->rfd4->fd;
} }
} }
/* Can't get socket. */
if (fd == -1)
return;
#ifdef HAVE_DUMPFILE #ifdef HAVE_DUMPFILE
dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)plen, NULL, &start- >addr); dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)plen, NULL, &start- >addr);
#endif #endif
while (retry_send(sendto(fd, (char *)header, plen, 0, while (retry_send(sendto(fd, (char *)header, plen, 0,
&start->addr.sa, &start->addr.sa,
sa_len(&start->addr)))); sa_len(&start->addr))));
if (start->addr.sa.sa_family == AF_INET) 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"); log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (union all_addr *)& start->addr.in.sin_addr, "dnssec");
skipping to change at line 1088 skipping to change at line 1131
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;
new->rfd6 = NULL; new->rfd6 = NULL;
new->frec_src.next = NULL;
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HA S_EXTRADATA); new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HA S_EXTRADATA);
new->forwardall = 0; 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 */
/* validate routines leave name of required record in daemo n->keyname */ /* validate routines leave name of required record in daemo n->keyname */
if (status == STAT_NEED_KEY) if (status == STAT_NEED_KEY)
{ {
new->flags |= FREC_DNSKEY_QUERY; new->flags |= FREC_DNSKEY_QUERY;
querytype = T_DNSKEY; querytype = T_DNSKEY;
skipping to change at line 1115 skipping to change at line 1159
nn = dnssec_generate_query(header,((unsigned char *) header ) + server->edns_pktsz, nn = dnssec_generate_query(header,((unsigned char *) header ) + server->edns_pktsz,
daemon->keyname, forward->class, querytype, server->edns_pktsz); daemon->keyname, forward->class, querytype, server->edns_pktsz);
if (server->addr.sa.sa_family == AF_INET) 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), log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, daemon->keyname, (union all_addr *)&(server->addr.in.sin_addr),
querystr("dnssec-query", querytype)); querystr("dnssec-query", querytype));
else else
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, daemon->keyname, (union all_addr *)&(server->addr.in6.sin6_addr), log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, daemon->keyname, (union all_addr *)&(server->addr.in6.sin6_addr),
querystr("dnssec-query", querytype)); querystr("dnssec-query", querytype));
if ((hash = hash_questions(header, nn, daemon->namebuff))) memcpy(new->hash, hash_questions(header, nn, daemon->namebu
memcpy(new->hash, hash, HASH_SIZE); ff), 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 */
new->stash = blockdata_alloc((char *)header, nn); new->stash = blockdata_alloc((char *)header, nn);
new->stash_len = nn; new->stash_len = nn;
/* Don't resend this. */ /* Don't resend this. */
daemon->srv_save = NULL; daemon->srv_save = NULL;
if (server->sfd) if (server->sfd)
skipping to change at line 1150 skipping to change at line 1193
} }
} }
if (fd != -1) if (fd != -1)
{ {
#ifdef HAVE_CONNTRACK #ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing c onnection. */ /* Copy connection mark of incoming query to outgoing c onnection. */
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 #ifdef HAVE_DUMPFILE
dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr); dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr);
#endif #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,
skipping to change at line 1221 skipping to change at line 1264
} }
#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 E
DSN0 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_reb ind, no_cache_dnssec, cache_secure, bogusanswer, if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_reb ind, 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 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 i t so. The client then retries with TCP. */ header, the answer is still bigger than 512, truncate it and mark i t so. The client then retries with TCP. */
if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEAD ER) && (nn > PACKETSZ)) if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEAD ER) && (nn > PACKETSZ))
{ {
header->ancount = htons(0); header->ancount = htons(0);
header->nscount = htons(0); header->nscount = htons(0);
header->arcount = htons(0); header->arcount = htons(0);
header->hb3 |= HB3_TC; header->hb3 |= HB3_TC;
nn = resize_packet(header, nn, NULL, 0); nn = resize_packet(header, nn, NULL, 0);
} }
#endif #endif
for (src = &forward->frec_src; src; src = src->next)
{
header->id = htons(src->orig_id);
#ifdef HAVE_DUMPFILE #ifdef HAVE_DUMPFILE
dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &forward->sou rce); dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->sou rce);
#endif #endif
send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEV send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_
ERBIND), daemon->packet, nn, CLEVERBIND), daemon->packet, nn,
&forward->source, &forward->dest, forward->iface); &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 */
} }
} }
void receive_query(struct listener *listen, time_t now) void receive_query(struct listener *listen, time_t now)
{ {
struct dns_header *header = (struct dns_header *)daemon->packet; struct dns_header *header = (struct dns_header *)daemon->packet;
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 */
skipping to change at line 1763 skipping to change at line 1826
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,
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns) union mysockaddr *local_addr, struct in_addr netmask, int auth_dns)
{ {
size_t size = 0; size_t size = 0;
int norebind = 0; int norebind = 0;
#ifdef HAVE_AUTH #ifdef HAVE_AUTH
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, bogusanswe r = 0;
size_t m; size_t m;
unsigned short qtype; unsigned short qtype;
unsigned int gotname; unsigned int gotname;
unsigned char c1, c2; unsigned char c1, c2;
/* Max TCP packet + slop + size */ /* Max TCP packet + slop + size */
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16 )); unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16 ));
unsigned char *payload = &packet[2]; unsigned char *payload = &packet[2];
/* largest field in header is 16-bits, so this is still sufficiently aligned * / /* largest field in header is 16-bits, so this is still sufficiently aligned * /
struct dns_header *header = (struct dns_header *)payload; struct dns_header *header = (struct dns_header *)payload;
u16 *length = (u16 *)packet; u16 *length = (u16 *)packet;
skipping to change at line 1934 skipping to change at line 1997
check_log_writer(1); check_log_writer(1);
if (m == 0) if (m == 0)
{ {
unsigned int flags = 0; unsigned int flags = 0;
union 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;
unsigned char *oph = find_pseudoheader(header, size, NULL, NULL, NU LL, NULL); unsigned char *oph = find_pseudoheader(header, size, NULL, NULL, NU LL, NULL);
size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet); size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet, &cacheable);
if (gotname) if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &t ype, &domain, &norebind); flags = search_servers(now, &addrp, gotname, daemon->namebuff, &t ype, &domain, &norebind);
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (type & SERV_DO_DNSSEC)) if (option_bool(OPT_DNSSEC_VALID) && (type & SERV_DO_DNSSEC))
{ {
size = add_do_bit(header, size, ((unsigned char *) header) + 65 536); size = add_do_bit(header, size, ((unsigned char *) header) + 65 536);
/* For debugging, set Checking Disabled, otherwise, have the up stream check too, /* For debugging, set Checking Disabled, otherwise, have the up stream check too,
skipping to change at line 1966 skipping to change at line 2029
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)
last_server = daemon->servers; last_server = daemon->servers;
else else
last_server = daemon->last_server; last_server = daemon->last_server;
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-
if ((newhash = hash_questions(header, (unsigned int)size, daemo >namebuff), HASH_SIZE);
n->namebuff)))
memcpy(hash, newhash, HASH_SIZE);
else
memset(hash, 0, HASH_SIZE);
#else
unsigned int crc = questions_crc(header, (unsigned int)size, da
emon->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 subtly 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; int data_sent = 0;
if (!firstsendto) if (!firstsendto)
firstsendto = last_server; firstsendto = last_server;
else else
skipping to change at line 2113 skipping to change at line 2170
header->hb4 |= HB4_CD; header->hb4 |= HB4_CD;
else else
header->hb4 &= ~HB4_CD; header->hb4 &= ~HB4_CD;
/* There's no point in updating the cache, since this proce ss will exit and /* There's no point in updating the cache, since this proce ss will exit and
lose the information after a few queries. We make this c all for the alias and lose the information after a few queries. We make this c all for the alias and
bogus-nxdomain side-effects. */ bogus-nxdomain side-effects. */
/* 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, da
newhash = hash_questions(header, (unsigned int)m, daemon->n emon->namebuff), HASH_SIZE) != 0)
amebuff);
if (!newhash || memcmp(hash, newhash, HASH_SIZE) != 0)
{ {
m = 0; m = 0;
break; break;
} }
#else
if (crc != questions_crc(header, (unsigned int)m, daemon->n /* Never cache answers which are contingent on the source o
amebuff)) r MAC address EDSN0 option,
{ since the cache is ignorant of such things. */
m = 0; if (!cacheable)
break; no_cache_dnssec = 1;
}
#endif
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_sub net, &peer_addr); ad_reqd, do_bit, added_pheader, check_sub net, &peer_addr);
break; break;
} }
} }
/* In case of local answer or no connections made. */ /* In case of local answer or no connections made. */
skipping to change at line 2222 skipping to change at line 2275
} }
void free_rfd(struct randfd *rfd) void free_rfd(struct randfd *rfd)
{ {
if (rfd && --(rfd->refcount) == 0) if (rfd && --(rfd->refcount) == 0)
close(rfd->fd); close(rfd->fd);
} }
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;
free_rfd(f->rfd6); free_rfd(f->rfd6);
f->rfd6 = NULL; f->rfd6 = NULL;
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (f->stash) if (f->stash)
{ {
skipping to change at line 2331 skipping to change at line 2395
return NULL; return NULL;
} }
if (!(f = allocate_frec(now)) && wait) if (!(f = allocate_frec(now)) && wait)
/* wait one second on malloc failure */ /* wait one second on malloc failure */
*wait = 1; *wait = 1;
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 *has
static struct frec *lookup_frec(unsigned short id, void *hash) h)
{ {
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;
} }
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)
{ {
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_N
O_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()
{ {
if (daemon->srv_save) if (daemon->srv_save)
{ {
skipping to change at line 2400 skipping to change at line 2499
daemon->last_server = NULL; daemon->last_server = NULL;
if (daemon->srv_save == server) if (daemon->srv_save == server)
daemon->srv_save = NULL; daemon->srv_save = NULL;
} }
/* return unique random ids. */ /* return unique random ids. */
static unsigned short get_id(void) static unsigned short get_id(void)
{ {
unsigned short ret = 0; unsigned short ret = 0;
struct frec *f;
while (1)
{
ret = rand16();
do /* ensure id is unique. */
ret = rand16(); for (f = daemon->frec_list; f; f = f->next)
while (lookup_frec(ret, NULL)); if (f->sentto && f->new_id == ret)
break;
return ret; if (!f)
return ret;
}
} }
 End of changes. 50 change blocks. 
79 lines changed or deleted 191 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)