summaryrefslogtreecommitdiff
path: root/src/forward.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/forward.c')
-rw-r--r--src/forward.c540
1 files changed, 342 insertions, 198 deletions
diff --git a/src/forward.c b/src/forward.c
index cdd11d3..9c2b2c6 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley
+/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
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
@@ -26,7 +26,7 @@ static void free_frec(struct frec *f);
/* Send a UDP packet with its source address set as "source"
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,
- union mysockaddr *to, struct all_addr *source,
+ union mysockaddr *to, union all_addr *source,
unsigned int iface)
{
struct msghdr msg;
@@ -38,9 +38,7 @@ int send_from(int fd, int nowild, char *packet, size_t len,
#elif defined(IP_SENDSRCADDR)
char control[CMSG_SPACE(sizeof(struct in_addr))];
#endif
-#ifdef HAVE_IPV6
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
-#endif
} control_u;
iov[0].iov_base = packet;
@@ -66,47 +64,49 @@ int send_from(int fd, int nowild, char *packet, size_t len,
#if defined(HAVE_LINUX_NETWORK)
struct in_pktinfo p;
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));
- 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_type = IP_PKTINFO;
#elif defined(IP_SENDSRCADDR)
- memcpy(CMSG_DATA(cmptr), &(source->addr.addr4), sizeof(source->addr.addr4));
- msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
+ msg.msg_controllen = CMSG_SPACE(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_type = IP_SENDSRCADDR;
#endif
}
else
-#ifdef HAVE_IPV6
{
struct in6_pktinfo p;
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));
- 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_level = IPPROTO_IPV6;
}
-#else
- (void)iface; /* eliminate warning */
-#endif
}
while (retry_send(sendmsg(fd, &msg, 0)));
- /* If interface is still in DAD, EINVAL results - ignore that. */
- if (errno != 0 && errno != EINVAL)
+ if (errno != 0)
{
- 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 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)
{
@@ -118,6 +118,7 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
unsigned int matchlen = 0;
struct server *serv;
unsigned int flags = 0;
+ static union all_addr zero;
for (serv = daemon->servers; serv; serv=serv->next)
if (qtype == F_DNSSECOK && !(serv->flags & SERV_DO_DNSSEC))
@@ -127,19 +128,26 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
{
unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
*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;
- 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;
if (serv->addr.sa.sa_family == AF_INET)
- *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
-#ifdef HAVE_IPV6
+ *addrpp = (union all_addr *)&serv->addr.in.sin_addr;
else
- *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
-#endif
+ *addrpp = (union all_addr *)&serv->addr.in6.sin6_addr;
}
else if (!flags || (flags & F_NXDOMAIN))
flags = F_NOERR;
@@ -184,15 +192,20 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
flags = F_NXDOMAIN;
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;
if (serv->addr.sa.sa_family == AF_INET)
- *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
-#ifdef HAVE_IPV6
+ *addrpp = (union all_addr *)&serv->addr.in.sin_addr;
else
- *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
-#endif
+ *addrpp = (union all_addr *)&serv->addr.in6.sin6_addr;
}
else if (!flags || (flags & F_NXDOMAIN))
flags = F_NOERR;
@@ -214,12 +227,16 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
if (flags)
{
- int logflags = 0;
-
- if (flags == F_NXDOMAIN || flags == F_NOERR)
- logflags = F_NEG | qtype;
-
- log_query(logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp, NULL);
+ if (flags == F_NXDOMAIN || flags == F_NOERR)
+ log_query(flags | qtype | F_NEG | F_CONFIG | F_FORWARD, qdomain, NULL, NULL);
+ else
+ {
+ /* handle F_IPV4 and F_IPV6 set on ANY query to 0.0.0.0/:: domain. */
+ 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)
{
@@ -230,13 +247,13 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
}
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 frec *forward, int ad_reqd, int do_bit)
{
char *domain = NULL;
int type = SERV_DO_DNSSEC, norebind = 0;
- struct all_addr *addrp = NULL;
+ union all_addr *addrp = NULL;
unsigned int flags = 0;
struct server *start = NULL;
#ifdef HAVE_DNSSEC
@@ -246,9 +263,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
void *hash = &crc;
#endif
- unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
-
- (void)do_bit;
+ unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
+ unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL);
+ (void)do_bit;
/* may be no servers available. */
if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
@@ -280,27 +297,24 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
PUTSHORT(SAFE_PKTSZ, pheader);
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");
-#ifdef HAVE_IPV6
+ log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (union all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
else
- log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
-#endif
+ log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (union all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
+
if (forward->sentto->sfd)
fd = forward->sentto->sfd->fd;
else
{
-#ifdef HAVE_IPV6
if (forward->sentto->addr.sa.sa_family == AF_INET6)
fd = forward->rfd6->fd;
else
-#endif
fd = forward->rfd4->fd;
}
- while (retry_send( sendto(fd, (char *)header, plen, 0,
- &forward->sentto->addr.sa,
- sa_len(&forward->sentto->addr))));
+ while (retry_send(sendto(fd, (char *)header, plen, 0,
+ &forward->sentto->addr.sa,
+ sa_len(&forward->sentto->addr))));
return 1;
}
@@ -334,7 +348,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
type &= ~SERV_DO_DNSSEC;
if (daemon->servers && !flags)
- forward = get_new_frec(now, NULL, 0);
+ forward = get_new_frec(now, NULL, NULL);
/* table full - flags == 0, return REFUSED */
if (forward)
@@ -399,7 +413,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
struct server *firstsentto = start;
int subnet, forwarded = 0;
size_t edns0_len;
- unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL);
unsigned char *pheader;
/* If a query is retried, use the log_id for the retry when logging the answer. */
@@ -455,7 +468,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
fd = start->sfd->fd;
else
{
-#ifdef HAVE_IPV6
if (start->addr.sa.sa_family == AF_INET6)
{
if (!forward->rfd6 &&
@@ -465,7 +477,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
fd = forward->rfd6->fd;
}
else
-#endif
{
if (!forward->rfd4 &&
!(forward->rfd4 = allocate_rfd(AF_INET)))
@@ -508,6 +519,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
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 */
daemon->srv_save = start;
daemon->packet_len = plen;
@@ -516,12 +531,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
strcpy(daemon->namebuff, "query");
if (start->addr.sa.sa_family == AF_INET)
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&start->addr.in.sin_addr, NULL);
-#ifdef HAVE_IPV6
+ (union all_addr *)&start->addr.in.sin_addr, NULL);
else
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&start->addr.in6.sin6_addr, NULL);
-#endif
+ (union all_addr *)&start->addr.in6.sin6_addr, NULL);
start->queries++;
forwarded = 1;
forward->sentto = start;
@@ -550,6 +563,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (udpfd != -1)
{
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);
}
@@ -563,6 +578,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
unsigned char *pheader, *sizep;
char **sets = 0;
int munged = 0, is_sign;
+ unsigned int rcode = RCODE(header);
size_t plen;
(void)ad_reqd;
@@ -593,6 +609,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
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))
{
my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
@@ -641,20 +660,29 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (!is_sign && !option_bool(OPT_DNSSEC_PROXY))
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);
+
+ 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. */
- if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR &&
+ if (!(header->hb4 & HB4_RA) && rcode == NOERROR &&
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);
if (!option_bool(OPT_LOG))
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))
{
munged = 1;
@@ -666,7 +694,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
{
int doctored = 0;
- if (RCODE(header) == NXDOMAIN &&
+ if (rcode == NXDOMAIN &&
extract_request(header, n, daemon->namebuff, NULL) &&
check_for_local_domain(daemon->namebuff, now))
{
@@ -749,14 +777,11 @@ void reply_query(int fd, int family, time_t now)
daemon->srv_save = NULL;
/* Determine the address of the server replying so that we can mark that as good */
- serveraddr.sa.sa_family = family;
-#ifdef HAVE_IPV6
- if (serveraddr.sa.sa_family == AF_INET6)
+ if ((serveraddr.sa.sa_family = family) == AF_INET6)
serveraddr.in6.sin6_flowinfo = 0;
-#endif
header = (struct dns_header *)daemon->packet;
-
+
if (n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR))
return;
@@ -783,6 +808,11 @@ void reply_query(int fd, int family, time_t now)
if (!(forward = lookup_frec(ntohs(header->id), hash)))
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
pass these in global variables - sorry. */
daemon->log_display_id = forward->log_id;
@@ -794,7 +824,7 @@ void reply_query(int fd, int family, time_t now)
/* Note: if we send extra options in the EDNS0 header, we can't recreate
the query from the reply. */
- if (RCODE(header) == REFUSED &&
+ if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) &&
forward->forwardall == 0 &&
!(forward->flags & FREC_HAS_EXTRADATA))
/* for broken servers, attempt to send to another one. */
@@ -803,6 +833,70 @@ void reply_query(int fd, int family, time_t now)
size_t plen;
int is_sign;
+#ifdef HAVE_DNSSEC
+ /* For DNSSEC originated queries, just retry the query to the same server. */
+ 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;
+ }
+
+
+ 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;
+ }
+ }
+
+#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
@@ -861,12 +955,12 @@ 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.
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. */
- if (server && server->edns_pktsz > SAFE_PKTSZ && (forward->flags & FREC_TEST_PKTSZ) &&
+ if (forward->sentto->edns_pktsz > SAFE_PKTSZ && (forward->flags & FREC_TEST_PKTSZ) &&
((header->hb3 & HB3_TC) || n >= SAFE_PKTSZ))
{
- server->edns_pktsz = SAFE_PKTSZ;
- server->pktsz_reduced = now;
- prettyprint_addr(&server->addr, daemon->addrbuff);
+ 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);
}
@@ -875,8 +969,7 @@ void reply_query(int fd, int family, time_t now)
we get a good reply from another server. Kill it when we've
had replies from all to avoid filling the forwarding table when
everything is broken */
- if (forward->forwardall == 0 || --forward->forwardall == 1 ||
- (RCODE(header) != REFUSED && RCODE(header) != SERVFAIL))
+ if (forward->forwardall == 0 || --forward->forwardall == 1 || RCODE(header) != REFUSED)
{
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
@@ -889,7 +982,7 @@ void reply_query(int fd, int family, time_t now)
no_cache_dnssec = 1;
#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))
{
int status = 0;
@@ -919,8 +1012,13 @@ void reply_query(int fd, int family, time_t now)
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
else
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class,
- option_bool(OPT_DNSSEC_NO_SIGN) && (server->flags & SERV_DO_DNSSEC),
- 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
@@ -941,11 +1039,13 @@ void reply_query(int fd, int family, time_t now)
/* Find the original query that started it all.... */
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;
else
{
- int fd, type = SERV_DO_DNSSEC;
+ int querytype, fd, type = SERV_DO_DNSSEC;
struct frec *next = new->next;
char *domain;
@@ -958,12 +1058,13 @@ void reply_query(int fd, int family, time_t now)
servers for domains are involved. */
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;
+ start = server = forward->sentto;
while (1)
{
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)))
{
new_server = start;
@@ -986,10 +1087,9 @@ void reply_query(int fd, int family, time_t now)
new->sentto = server;
new->rfd4 = NULL;
-#ifdef HAVE_IPV6
new->rfd6 = NULL;
-#endif
- 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. */
forward->blocking_query = new; /* for garbage cleaning */
@@ -997,15 +1097,24 @@ void reply_query(int fd, int family, time_t now)
if (status == STAT_NEED_KEY)
{
new->flags |= FREC_DNSKEY_QUERY;
- nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz,
- daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz);
+ querytype = T_DNSKEY;
}
else
{
new->flags |= FREC_DS_QUERY;
- nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz,
- daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz);
+ querytype = T_DS;
}
+
+ 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));
+
if ((hash = hash_questions(header, nn, daemon->namebuff)))
memcpy(new->hash, hash, HASH_SIZE);
new->new_id = get_id();
@@ -1022,14 +1131,12 @@ void reply_query(int fd, int family, time_t now)
else
{
fd = -1;
-#ifdef HAVE_IPV6
if (server->addr.sa.sa_family == AF_INET6)
{
if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
fd = new->rfd6->fd;
}
else
-#endif
{
if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
fd = new->rfd4->fd;
@@ -1047,6 +1154,11 @@ void reply_query(int fd, int family, time_t now)
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#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,
&server->addr.sa,
sa_len(&server->addr))));
@@ -1090,7 +1202,7 @@ void reply_query(int fd, int family, time_t now)
if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL))
domain = daemon->namebuff;
- log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
+ log_query(F_SECSTAT, domain, NULL, result);
}
if (status == STAT_SECURE)
@@ -1101,8 +1213,9 @@ void reply_query(int fd, int family, time_t now)
bogusanswer = 1;
}
}
-#endif
-
+
+#endif
+
/* restore CD bit to the value in the query */
if (forward->flags & FREC_CHECKING_DISABLED)
header->hb4 |= HB4_CD;
@@ -1128,6 +1241,11 @@ void reply_query(int fd, int family, time_t now)
nn = resize_packet(header, nn, NULL, 0);
}
#endif
+
+#ifdef HAVE_DUMPFILE
+ dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &forward->source);
+#endif
+
send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn,
&forward->source, &forward->dest, forward->iface);
}
@@ -1142,7 +1260,7 @@ void receive_query(struct listener *listen, time_t now)
union mysockaddr source_addr;
unsigned char *pheader;
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;
size_t m;
ssize_t n;
@@ -1155,9 +1273,7 @@ void receive_query(struct listener *listen, time_t now)
struct cmsghdr *cmptr;
union {
struct cmsghdr align; /* this ensures alignment */
-#ifdef HAVE_IPV6
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
-#endif
#if defined(HAVE_LINUX_NETWORK)
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(IP_RECVDSTADDR) && defined(HAVE_SOLARIS_NETWORK)
@@ -1168,26 +1284,23 @@ void receive_query(struct listener *listen, time_t now)
CMSG_SPACE(sizeof(struct sockaddr_dl))];
#endif
} control_u;
-#ifdef HAVE_IPV6
+ int family = listen->addr.sa.sa_family;
/* Can always get recvd interface for IPv6 */
- int check_dst = !option_bool(OPT_NOWILD) || listen->family == AF_INET6;
-#else
- int check_dst = !option_bool(OPT_NOWILD);
-#endif
+ int check_dst = !option_bool(OPT_NOWILD) || family == AF_INET6;
/* packet buffer overwritten */
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;
if (option_bool(OPT_NOWILD) && listen->iface)
{
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;
}
}
@@ -1215,16 +1328,15 @@ void receive_query(struct listener *listen, time_t now)
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.
http://www.ietf.org/mail-archive/web/dnsop/current/msg11441.html */
if (source_addr.in.sin_port == 0)
return;
}
-#ifdef HAVE_IPV6
else
{
/* Source-port == 0 is an error, we can't send back to that. */
@@ -1232,29 +1344,27 @@ void receive_query(struct listener *listen, time_t now)
return;
source_addr.in6.sin6_flowinfo = 0;
}
-#endif
/* We can be configured to only accept queries from at-most-one-hop-away addresses. */
if (option_bool(OPT_LOCAL_SERVICE))
{
struct addrlist *addr;
-#ifdef HAVE_IPV6
- if (listen->family == AF_INET6)
+
+ if (family == AF_INET6)
{
for (addr = daemon->interface_addrs; addr; addr = addr->next)
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;
}
else
-#endif
{
struct in_addr netmask;
for (addr = daemon->interface_addrs; addr; addr = addr->next)
{
netmask.s_addr = htonl(~(in_addr_t)0 << (32 - addr->prefixlen));
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;
}
}
@@ -1278,7 +1388,7 @@ void receive_query(struct listener *listen, time_t now)
return;
#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))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
{
@@ -1287,11 +1397,11 @@ void receive_query(struct listener *listen, time_t now)
struct in_pktinfo *p;
} p;
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;
}
#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))
{
@@ -1305,7 +1415,7 @@ void receive_query(struct listener *listen, time_t now)
} p;
p.c = CMSG_DATA(cmptr);
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)
#ifdef HAVE_SOLARIS_NETWORK
if_index = *(p.i);
@@ -1316,8 +1426,7 @@ void receive_query(struct listener *listen, time_t now)
}
#endif
-#ifdef HAVE_IPV6
- if (listen->family == AF_INET6)
+ if (family == AF_INET6)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
@@ -1328,27 +1437,26 @@ void receive_query(struct listener *listen, time_t now)
} p;
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;
}
}
-#endif
/* enforce available interface configuration */
if (!indextoname(listen->fd, if_index, ifr.ifr_name))
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))
enumerate_interfaces(0);
- if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name) &&
- !label_exception(if_index, listen->family, &dst_addr))
+ if (!loopback_exception(listen->fd, family, &dst_addr, ifr.ifr_name) &&
+ !label_exception(if_index, family, &dst_addr))
return;
}
- if (listen->family == AF_INET && option_bool(OPT_LOCALISE))
+ if (family == AF_INET && option_bool(OPT_LOCALISE))
{
struct irec *iface;
@@ -1381,7 +1489,11 @@ void receive_query(struct listener *listen, time_t now)
pass these in global variables - sorry. */
daemon->log_display_id = ++daemon->log_id;
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))
{
#ifdef HAVE_AUTH
@@ -1389,14 +1501,12 @@ void receive_query(struct listener *listen, time_t now)
#endif
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,
- (struct all_addr *)&source_addr.in.sin_addr, types);
-#ifdef HAVE_IPV6
+ (union all_addr *)&source_addr.in.sin_addr, types);
else
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&source_addr.in6.sin6_addr, types);
-#endif
+ (union all_addr *)&source_addr.in6.sin6_addr, types);
#ifdef HAVE_AUTH
/* find queries for zones we're authoritative for, and answer them directly */
@@ -1447,7 +1557,7 @@ void receive_query(struct listener *listen, time_t now)
{
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
(char *)header, m, &source_addr, &dst_addr, if_index);
- daemon->auth_answer++;
+ daemon->metrics[METRIC_DNS_AUTH_ANSWERED]++;
}
}
else
@@ -1465,13 +1575,13 @@ void receive_query(struct listener *listen, time_t now)
{
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
(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,
header, (size_t)n, now, NULL, ad_reqd, do_bit))
- daemon->queries_forwarded++;
+ daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++;
else
- daemon->local_answer++;
+ daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
}
}
@@ -1504,8 +1614,8 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
else
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class,
- option_bool(OPT_DNSSEC_NO_SIGN) && (server->flags & SERV_DO_DNSSEC),
- NULL, NULL);
+ !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)
break;
@@ -1525,9 +1635,9 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
new_status = STAT_ABANDONED;
break;
}
-
+
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);
@@ -1542,6 +1652,8 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
while (1)
{
+ int data_sent = 0;
+
if (!firstsendto)
firstsendto = server;
else
@@ -1560,32 +1672,46 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, server->domain)) ||
(server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
continue;
-
- retry:
- /* may need to make new connection. */
- if (server->tcpfd == -1)
- {
- if ((server->tcpfd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
- continue; /* No good, next server */
-
+
+ retry:
+ /* may need to make new connection. */
+ if (server->tcpfd == -1)
+ {
+ if ((server->tcpfd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
+ continue; /* No good, next server */
+
#ifdef HAVE_CONNTRACK
- /* Copy connection mark of incoming query to outgoing connection. */
- if (have_mark)
- setsockopt(server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
+ /* Copy connection mark of incoming query to outgoing connection. */
+ if (have_mark)
+ setsockopt(server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
#endif
-
- 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);
- server->tcpfd = -1;
- continue; /* No good, next server */
- }
-
- server->flags &= ~SERV_GOT_TCP;
- }
+
+ if (!local_bind(server->tcpfd, &server->source_addr, server->interface, 0, 1))
+ {
+ close(server->tcpfd);
+ server->tcpfd = -1;
+ continue; /* No good, next server */
+ }
+
+#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 (!read_write(server->tcpfd, packet, m + sizeof(u16), 0) ||
+ 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))
@@ -1600,6 +1726,14 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
else
continue;
}
+
+
+ if (server->addr.sa.sa_family == AF_INET)
+ log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, keyname, (union all_addr *)&(server->addr.in.sin_addr),
+ querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
+ else
+ log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, keyname, (union all_addr *)&(server->addr.in6.sin6_addr),
+ querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
server->flags |= SERV_GOT_TCP;
@@ -1663,13 +1797,12 @@ unsigned char *tcp_request(int confd, time_t now,
/* Get connection mark of incoming query to set on outgoing connections. */
if (option_bool(OPT_CONNTRACK))
{
- struct all_addr local;
-#ifdef HAVE_IPV6
+ union all_addr local;
+
if (local_addr->sa.sa_family == AF_INET6)
- local.addr.addr6 = local_addr->in6.sin6_addr;
+ local.addr6 = local_addr->in6.sin6_addr;
else
-#endif
- local.addr.addr4 = local_addr->in.sin_addr;
+ local.addr4 = local_addr->in.sin_addr;
have_mark = get_incoming_mark(&peer_addr, &local, 1, &mark);
}
@@ -1679,23 +1812,22 @@ unsigned char *tcp_request(int confd, time_t now,
if (option_bool(OPT_LOCAL_SERVICE))
{
struct addrlist *addr;
-#ifdef HAVE_IPV6
+
if (peer_addr.sa.sa_family == AF_INET6)
{
for (addr = daemon->interface_addrs; addr; addr = addr->next)
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;
}
else
-#endif
{
struct in_addr netmask;
for (addr = daemon->interface_addrs; addr; addr = addr->next)
{
netmask.s_addr = htonl(~(in_addr_t)0 << (32 - addr->prefixlen));
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;
}
}
@@ -1742,12 +1874,10 @@ unsigned char *tcp_request(int confd, time_t now,
if (peer_addr.sa.sa_family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&peer_addr.in.sin_addr, types);
-#ifdef HAVE_IPV6
+ (union all_addr *)&peer_addr.in.sin_addr, types);
else
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&peer_addr.in6.sin6_addr, types);
-#endif
+ (union all_addr *)&peer_addr.in6.sin6_addr, types);
#ifdef HAVE_AUTH
/* find queries for zones we're authoritative for, and answer them directly */
@@ -1803,7 +1933,7 @@ unsigned char *tcp_request(int confd, time_t now,
if (m == 0)
{
unsigned int flags = 0;
- struct all_addr *addrp = NULL;
+ union all_addr *addrp = NULL;
int type = SERV_DO_DNSSEC;
char *domain = NULL;
unsigned char *oph = find_pseudoheader(header, size, NULL, NULL, NULL, NULL);
@@ -1854,6 +1984,8 @@ unsigned char *tcp_request(int confd, time_t now,
which can go to the same server, do so. */
while (1)
{
+ int data_sent = 0;
+
if (!firstsendto)
firstsendto = last_server;
else
@@ -1872,6 +2004,8 @@ unsigned char *tcp_request(int confd, time_t now,
continue;
retry:
+ *length = htons(size);
+
if (last_server->tcpfd == -1)
{
if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
@@ -1881,10 +2015,24 @@ unsigned char *tcp_request(int confd, time_t now,
/* Copy connection mark of incoming query to outgoing connection. */
if (have_mark)
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, 0, 1) ||
- connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
+ if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 0, 1)))
+ {
+ close(last_server->tcpfd);
+ last_server->tcpfd = -1;
+ continue;
+ }
+
+#ifdef MSG_FASTOPEN
+ 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)
{
close(last_server->tcpfd);
last_server->tcpfd = -1;
@@ -1894,13 +2042,11 @@ unsigned char *tcp_request(int confd, time_t now,
last_server->flags &= ~SERV_GOT_TCP;
}
- *length = htons(size);
-
/* get query name again for logging - may have been overwritten */
if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
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, &c2, 1, 1) ||
!read_write(last_server->tcpfd, payload, (c1 << 8) | c2, 1))
@@ -1922,12 +2068,10 @@ unsigned char *tcp_request(int confd, time_t now,
if (last_server->addr.sa.sa_family == AF_INET)
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&last_server->addr.in.sin_addr, NULL);
-#ifdef HAVE_IPV6
+ (union all_addr *)&last_server->addr.in.sin_addr, NULL);
else
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
-#endif
+ (union all_addr *)&last_server->addr.in6.sin6_addr, NULL);
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (last_server->flags & SERV_DO_DNSSEC))
@@ -1948,7 +2092,7 @@ unsigned char *tcp_request(int confd, time_t now,
if (status == STAT_BOGUS && extract_request(header, m, daemon->namebuff, NULL))
domain = daemon->namebuff;
- log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
+ log_query(F_SECSTAT, domain, NULL, result);
if (status == STAT_BOGUS)
{
@@ -1998,7 +2142,11 @@ unsigned char *tcp_request(int confd, time_t now,
/* In case of local answer or no connections made. */
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);
+ }
}
}
@@ -2022,9 +2170,7 @@ static struct frec *allocate_frec(time_t now)
f->sentto = NULL;
f->rfd4 = NULL;
f->flags = 0;
-#ifdef HAVE_IPV6
f->rfd6 = NULL;
-#endif
#ifdef HAVE_DNSSEC
f->dependent = NULL;
f->blocking_query = NULL;
@@ -2084,11 +2230,8 @@ static void free_frec(struct frec *f)
f->rfd4 = NULL;
f->sentto = NULL;
f->flags = 0;
-
-#ifdef HAVE_IPV6
free_rfd(f->rfd6);
f->rfd6 = NULL;
-#endif
#ifdef HAVE_DNSSEC
if (f->stash)
@@ -2111,9 +2254,10 @@ static void free_frec(struct frec *f)
else return *wait zero if one available, or *wait is delay to
when the oldest in-use record will expire. Impose an absolute
limit of 4*TIMEOUT before we wipe things (for random sockets).
- If force is set, always return a result, even if we have
- to allocate above the limit. */
-struct frec *get_new_frec(time_t now, int *wait, int force)
+ If force is non-NULL, always return a result, even if we have
+ to allocate above the limit, and never free the record pointed
+ to by the force argument. */
+struct frec *get_new_frec(time_t now, int *wait, struct frec *force)
{
struct frec *f, *oldest, *target;
int count;
@@ -2130,7 +2274,7 @@ struct frec *get_new_frec(time_t now, int *wait, int force)
/* Don't free DNSSEC sub-queries here, as we may end up with
dangling references to them. They'll go when their "real" query
is freed. */
- if (!f->dependent)
+ if (!f->dependent && f != force)
#endif
{
if (difftime(now, f->time) >= 4*TIMEOUT)