Merge branch 'aws'

This commit is contained in:
Simon Kelley
2022-10-13 15:37:52 +01:00
15 changed files with 757 additions and 161 deletions

View File

@@ -170,7 +170,7 @@ static int domain_no_rebind(char *domain)
static int forward_query(int udpfd, union mysockaddr *udpaddr,
union all_addr *dst_addr, unsigned int dst_iface,
struct dns_header *header, size_t plen, char *limit, time_t now,
struct frec *forward, int ad_reqd, int do_bit)
struct frec *forward, int ad_reqd, int do_bit, int fast_retry)
{
unsigned int flags = 0;
unsigned int fwd_flags = 0;
@@ -313,6 +313,13 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
goto reply;
/* table full - flags == 0, return REFUSED */
/* Keep copy of query if we're doing fast retry. */
if (daemon->fast_retry_time != 0)
{
forward->stash = blockdata_alloc((char *)header, plen);
forward->stash_len = plen;
}
forward->frec_src.log_id = daemon->log_id;
forward->frec_src.source = *udpaddr;
forward->frec_src.orig_id = ntohs(header->id);
@@ -360,14 +367,14 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#ifdef HAVE_DNSSEC
/* If we've already got an answer to this query, but we're awaiting keys for validation,
there's no point retrying the query, retry the key query instead...... */
if (forward->blocking_query)
while (forward->blocking_query)
forward = forward->blocking_query;
if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))
{
int is_sign;
unsigned char *pheader;
while (forward->blocking_query)
forward = forward->blocking_query;
/* log_id should match previous DNSSEC query. */
daemon->log_display_id = forward->frec_src.log_id;
@@ -390,7 +397,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#endif
{
/* retry on existing query, from original source. Send to all available servers */
forward->sentto->failed_queries++;
if (udpfd == -1 && !fast_retry)
forward->sentto->failed_queries++;
else
forward->sentto->retrys++;
if (!filter_servers(forward->sentto->arrayposn, F_SERVER, &first, &last))
goto reply;
@@ -398,13 +408,13 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
master = daemon->serverarray[first];
/* Forward to all available servers on retry of query from same host. */
if (!option_bool(OPT_ORDER) && old_src)
if (!option_bool(OPT_ORDER) && old_src && !fast_retry)
forward->forwardall = 1;
else
{
start = forward->sentto->arrayposn;
if (option_bool(OPT_ORDER))
if (option_bool(OPT_ORDER) && !fast_retry)
{
/* In strict order mode, there must be a server later in the list
left to send to, otherwise without the forwardall mechanism,
@@ -553,7 +563,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
if (forwarded || is_dnssec)
return 1;
{
forward->forward_timestamp = dnsmasq_milliseconds();
return 1;
}
/* could not send on, prepare to return */
header->id = htons(forward->frec_src.orig_id);
@@ -592,6 +605,61 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
return 0;
}
/* Check if any frecs need to do a retry, and action that if so.
Return time in milliseconds until he next retry will be required,
or -1 if none. */
int fast_retry(time_t now)
{
struct frec *f;
int ret = -1;
if (daemon->fast_retry_time != 0)
{
u32 millis = dnsmasq_milliseconds();
for (f = daemon->frec_list; f; f = f->next)
if (f->sentto && f->stash && difftime(now, f->time) < daemon->fast_retry_timeout)
{
#ifdef HAVE_DNSSEC
if (f->blocking_query)
continue;
#endif
/* t is milliseconds since last query sent. */
int to_run, t = (int)(millis - f->forward_timestamp);
if (t < f->forward_delay)
to_run = f->forward_delay - t;
else
{
unsigned char *udpsz;
unsigned short udp_size = PACKETSZ; /* default if no EDNS0 */
struct dns_header *header = (struct dns_header *)daemon->packet;
/* packet buffer overwritten */
daemon->srv_save = NULL;
blockdata_retrieve(f->stash, f->stash_len, (void *)header);
/* UDP size already set in saved query. */
if (find_pseudoheader(header, f->stash_len, NULL, &udpsz, NULL, NULL))
GETSHORT(udp_size, udpsz);
daemon->log_display_id = f->frec_src.log_id;
forward_query(-1, NULL, NULL, 0, header, f->stash_len, ((char *) header) + udp_size, now, f,
f->flags & FREC_AD_QUESTION, f->flags & FREC_DO_QUESTION, 1);
to_run = f->forward_delay = 2 * f->forward_delay;
}
if (ret == -1 || ret > to_run)
ret = to_run;
}
}
return ret;
}
static struct ipsets *domain_find_sets(struct ipsets *setlist, const char *domain) {
/* Similar algorithm to search_servers. */
struct ipsets *ipset_pos, *ret = NULL;
@@ -807,6 +875,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
n = add_pseudoheader(header, n, limit, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1);
}
if (RCODE(header) == NXDOMAIN)
server->nxdomain_replies++;
return n;
}
@@ -949,6 +1020,8 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* Save query for retransmission and de-dup */
new->stash = blockdata_alloc((char *)header, nn);
new->stash_len = nn;
if (daemon->fast_retry_time != 0)
new->forward_timestamp = dnsmasq_milliseconds();
/* Don't resend this. */
daemon->srv_save = NULL;
@@ -1066,7 +1139,7 @@ void reply_query(int fd, time_t now)
if (daemon->ignore_addr && RCODE(header) == NOERROR &&
check_for_ignored_address(header, n))
return;
/* Note: if we send extra options in the EDNS0 header, we can't recreate
the query from the reply. */
if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) &&
@@ -1097,38 +1170,50 @@ void reply_query(int fd, time_t now)
else
#endif
{
/* recreate query from reply */
if ((pheader = find_pseudoheader(header, (size_t)n, &plen, &udpsz, &is_sign, NULL)))
GETSHORT(udp_size, udpsz);
/* If the client provides an EDNS0 UDP size, use that to limit our reply.
(bounded by the maximum configured). If no EDNS0, then it
defaults to 512 */
if (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 */
if (!is_sign &&
(nn = resize_packet(header, (size_t)n, pheader, plen)) &&
(forward->flags & FREC_DO_QUESTION))
add_do_bit(header, nn, (unsigned char *)pheader + plen);
/* in fast retry mode, we have a copy of the query. */
if (daemon->fast_retry_time != 0 && forward->stash)
{
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
nn = forward->stash_len;
/* UDP size already set in saved query. */
if (find_pseudoheader(header, (size_t)n, NULL, &udpsz, NULL, NULL))
GETSHORT(udp_size, udpsz);
}
else
{
/* recreate query from reply */
if ((pheader = find_pseudoheader(header, (size_t)n, &plen, &udpsz, &is_sign, NULL)))
GETSHORT(udp_size, udpsz);
/* If the client provides an EDNS0 UDP size, use that to limit our reply.
(bounded by the maximum configured). If no EDNS0, then it
defaults to 512 */
if (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 */
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC);
header->hb4 &= ~(HB4_RA | HB4_RCODE | HB4_CD | HB4_AD);
if (forward->flags & FREC_CHECKING_DISABLED)
header->hb4 |= HB4_CD;
if (forward->flags & FREC_AD_QUESTION)
header->hb4 |= HB4_AD;
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC);
header->hb4 &= ~(HB4_RA | HB4_RCODE | HB4_CD | HB4_AD);
if (forward->flags & FREC_CHECKING_DISABLED)
header->hb4 |= HB4_CD;
if (forward->flags & FREC_AD_QUESTION)
header->hb4 |= HB4_AD;
if (!is_sign &&
(nn = resize_packet(header, (size_t)n, pheader, plen)) &&
(forward->flags & FREC_DO_QUESTION))
add_do_bit(header, nn, (unsigned char *)pheader + plen);
}
}
if (nn)
{
forward_query(-1, NULL, NULL, 0, header, nn, ((char *) header) + udp_size, now, forward,
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION);
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, 0);
return;
}
}
@@ -1162,6 +1247,15 @@ void reply_query(int fd, time_t now)
answers, to conserve file descriptors, and to save work reading and
discarding answers for other upstreams. */
free_rfds(&forward->rfds);
/* calculate modified moving average of server latency */
if (server->query_latency == 0)
server->mma_latency = (dnsmasq_milliseconds() - forward->forward_timestamp) * 128; /* init */
else
server->mma_latency += dnsmasq_milliseconds() - forward->forward_timestamp - server->query_latency;
/* denominator controls how many queries we average over. */
server->query_latency = server->mma_latency/128;
#ifdef HAVE_DNSSEC
if ((forward->sentto->flags & SERV_DO_DNSSEC) &&
@@ -1268,10 +1362,6 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
{
header->id = htons(src->orig_id);
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source, src->fd);
#endif
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
if (option_bool(OPT_CMARK_ALST_EN))
{
@@ -1282,14 +1372,20 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
}
#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)
if (src->fd != -1)
{
daemon->log_display_id = src->log_id;
daemon->log_source_addr = &src->source;
log_query(F_UPSTREAM, "query", NULL, "duplicate", 0);
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source, src->fd);
#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", 0);
}
}
}
}
@@ -1379,7 +1475,7 @@ void receive_query(struct listener *listen, time_t now)
int family = listen->addr.sa.sa_family;
/* Can always get recvd interface for IPv6 */
int check_dst = !option_bool(OPT_NOWILD) || family == AF_INET6;
/* packet buffer overwritten */
daemon->srv_save = NULL;
@@ -1702,16 +1798,27 @@ void receive_query(struct listener *listen, time_t now)
#endif
else
{
int stale;
int ad_reqd = do_bit;
u16 hb3 = header->hb3, hb4 = header->hb4;
int fd = listen->fd;
/* RFC 6840 5.7 */
if (header->hb4 & HB4_AD)
ad_reqd = 1;
m = answer_request(header, ((char *) header) + udp_size, (size_t)n,
dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale);
if (m >= 1)
{
if (stale && have_pseudoheader)
{
u16 swap = htons(EDE_STALE);
m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz,
EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
}
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_REPLY, daemon->packet, m, NULL, &source_addr, listen->fd);
#endif
@@ -1722,12 +1829,39 @@ 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->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
if (stale)
daemon->metrics[METRIC_DNS_STALE_ANSWERED]++;
}
if (m == 0 || stale)
{
if (m != 0)
{
size_t plen;
/* We answered with stale cache data, so forward the query anyway to
refresh that. Restore the query from the answer packet. */
pheader = find_pseudoheader(header, (size_t)m, &plen, NULL, NULL, NULL);
header->hb3 = hb3;
header->hb4 = hb4;
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
m = resize_packet(header, m, pheader, plen);
/* We've already answered the client, so don't send it the answer
when it comes back. */
fd = -1;
}
if (forward_query(fd, &source_addr, &dst_addr, if_index,
header, (size_t)n, ((char *) header) + udp_size, now, NULL, ad_reqd, do_bit, 0))
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++;
else
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
}
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
header, (size_t)n, ((char *) header) + udp_size, now, NULL, ad_reqd, do_bit))
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++;
else
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
}
}
@@ -1954,8 +2088,9 @@ unsigned char *tcp_request(int confd, time_t now,
unsigned char *pheader;
unsigned int mark = 0;
int have_mark = 0;
int first, last;
int first, last, stale, do_stale = 0;
unsigned int flags = 0;
u16 hb3, hb4;
if (!packet || getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
return packet;
@@ -2010,13 +2145,37 @@ unsigned char *tcp_request(int confd, time_t now,
{
int ede = EDE_UNSET;
if (query_count == TCP_MAX_QUERIES ||
!packet ||
!read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
!(size = c1 << 8 | c2) ||
!read_write(confd, payload, size, 1))
return packet;
if (query_count == TCP_MAX_QUERIES)
return packet;
if (do_stale)
{
size_t plen;
/* We answered the last query with stale data. Now try and get fresh data.
Restore query from answer. */
pheader = find_pseudoheader(header, m, &plen, NULL, NULL, NULL);
header->hb3 = hb3;
header->hb4 = hb4;
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
size = resize_packet(header, m, pheader, plen);
}
else
{
if (!read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
!(size = c1 << 8 | c2) ||
!read_write(confd, payload, size, 1))
return packet;
/* for stale-answer processing. */
hb3 = header->hb3;
hb4 = header->hb4;
}
if (size < (int)sizeof(struct dns_header))
continue;
@@ -2041,24 +2200,27 @@ unsigned char *tcp_request(int confd, time_t now,
struct auth_zone *zone;
#endif
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
&peer_addr, auth_dns ? "auth" : "query", qtype);
#ifdef HAVE_CONNTRACK
is_single_query = 1;
#endif
if (!do_stale)
{
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
&peer_addr, auth_dns ? "auth" : "query", qtype);
#ifdef HAVE_AUTH
/* find queries for zones we're authoritative for, and answer them directly */
if (!auth_dns && !option_bool(OPT_LOCALISE))
for (zone = daemon->auth_zones; zone; zone = zone->next)
if (in_zone(zone, daemon->namebuff, NULL))
{
auth_dns = 1;
local_auth = 1;
break;
}
/* find queries for zones we're authoritative for, and answer them directly */
if (!auth_dns && !option_bool(OPT_LOCALISE))
for (zone = daemon->auth_zones; zone; zone = zone->next)
if (in_zone(zone, daemon->namebuff, NULL))
{
auth_dns = 1;
local_auth = 1;
break;
}
#endif
}
}
norebind = domain_no_rebind(daemon->namebuff);
@@ -2114,11 +2276,14 @@ unsigned char *tcp_request(int confd, time_t now,
/* RFC 6840 5.7 */
if (header->hb4 & HB4_AD)
ad_reqd = 1;
if (do_stale)
m = 0;
else
/* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale);
/* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
/* Do this by steam now we're not in the select() loop */
check_log_writer(1);
@@ -2236,6 +2401,9 @@ unsigned char *tcp_request(int confd, time_t now,
}
}
if (do_stale)
break;
/* In case of local answer or no connections made. */
if (m == 0)
{
@@ -2246,13 +2414,19 @@ unsigned char *tcp_request(int confd, time_t now,
if (have_pseudoheader)
{
u16 swap = htons((u16)ede);
if (ede != EDE_UNSET)
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
else
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
if (ede != EDE_UNSET)
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
else
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
}
}
else if (stale)
{
u16 swap = htons((u16)EDE_STALE);
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
}
check_log_writer(1);
@@ -2267,8 +2441,26 @@ unsigned char *tcp_request(int confd, time_t now,
#endif
if (!read_write(confd, packet, m + sizeof(u16), 0))
break;
/* If we answered with stale data, this process will now try and get fresh data into
the cache then and cannot therefore accept new queries. Close the incoming
connection to signal that to the client. Then set do_stale and loop round
once more to try and get fresh data, after which we exit. */
if (stale)
{
shutdown(confd, SHUT_RDWR);
close(confd);
do_stale = 1;
}
}
/* If we ran once to get fresh data, confd is already closed. */
if (!do_stale)
{
shutdown(confd, SHUT_RDWR);
close(confd);
}
return packet;
}
@@ -2280,16 +2472,36 @@ static int random_sock(struct server *s)
if ((fd = socket(s->source_addr.sa.sa_family, SOCK_DGRAM, 0)) != -1)
{
/* We need to set IPV6ONLY so we can use the same ports
for IPv4 and IPV6, otherwise, in restriced port situations,
we can end up with all our available ports in use for
one address family, and the other address family cannot be used. */
if (s->source_addr.sa.sa_family == AF_INET6)
{
int opt = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
{
close(fd);
return -1;
}
}
if (local_bind(fd, &s->source_addr, s->interface, s->ifindex, 0))
return fd;
if (s->interface[0] == 0)
(void)prettyprint_addr(&s->source_addr, daemon->namebuff);
else
strcpy(daemon->namebuff, s->interface);
my_syslog(LOG_ERR, _("failed to bind server socket to %s: %s"),
daemon->namebuff, strerror(errno));
/* don't log errors due to running out of available ports, we handle those. */
if (!sockaddr_isnull(&s->source_addr) || errno != EADDRINUSE)
{
if (s->interface[0] == 0)
(void)prettyprint_addr(&s->source_addr, daemon->addrbuff);
else
safe_strncpy(daemon->addrbuff, s->interface, ADDRSTRLEN);
my_syslog(LOG_ERR, _("failed to bind server socket to %s: %s"),
daemon->addrbuff, strerror(errno));
}
close(fd);
}
@@ -2319,39 +2531,93 @@ int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
{
static int finger = 0;
int i, j = 0;
struct randfd_list *rfl;
int ports_full = 0;
struct randfd_list **up, *rfl, *found, **found_link;
struct randfd *rfd = NULL;
int fd = 0;
int ports_avail = 0;
/* We can't have more randomsocks for this AF available than ports in our port range,
so check that here, to avoid trying and failing to bind every port
in local_bind(), called from random_sock(). The actual check is below when
ports_avail != 0 */
if (daemon->max_port != 0)
{
ports_avail = daemon->max_port - daemon->min_port + 1;
if (ports_avail >= SMALL_PORT_RANGE)
ports_avail = 0;
}
/* If server has a pre-allocated fd, use that. */
if (serv->sfd)
return serv->sfd->fd;
/* existing suitable random port socket linked to this transaction? */
for (rfl = *fdlp; rfl; rfl = rfl->next)
/* existing suitable random port socket linked to this transaction?
Find the last one in the list and count how many there are. */
for (found = NULL, found_link = NULL, i = 0, up = fdlp, rfl = *fdlp; rfl; up = &rfl->next, rfl = rfl->next)
if (server_isequal(serv, rfl->rfd->serv))
return rfl->rfd->fd;
{
i++;
found = rfl;
found_link = up;
}
/* No. need new link. */
/* We have the maximum number for this query already. Promote
the last one on the list to the head, to circulate them,
and return it. */
if (found && i >= daemon->randport_limit)
{
*found_link = found->next;
found->next = *fdlp;
*fdlp = found;
return found->rfd->fd;
}
/* check for all available ports in use. */
if (ports_avail != 0)
{
int ports_inuse;
for (ports_inuse = 0, i = 0; i < daemon->numrrand; i++)
if (daemon->randomsocks[i].refcount != 0 &&
daemon->randomsocks[i].serv->source_addr.sa.sa_family == serv->source_addr.sa.sa_family &&
++ports_inuse >= ports_avail)
{
ports_full = 1;
break;
}
}
/* limit the number of sockets we have open to avoid starvation of
(eg) TFTP. Once we have a reasonable number, randomness should be OK */
if (!ports_full)
for (i = 0; i < daemon->numrrand; i++)
if (daemon->randomsocks[i].refcount == 0)
{
if ((fd = random_sock(serv)) != -1)
{
rfd = &daemon->randomsocks[i];
rfd->serv = serv;
rfd->fd = fd;
rfd->refcount = 1;
}
break;
}
/* No good existing. Need new link. */
if ((rfl = daemon->rfl_spare))
daemon->rfl_spare = rfl->next;
else if (!(rfl = whine_malloc(sizeof(struct randfd_list))))
return -1;
/* limit the number of sockets we have open to avoid starvation of
(eg) TFTP. Once we have a reasonable number, randomness should be OK */
for (i = 0; i < daemon->numrrand; i++)
if (daemon->randomsocks[i].refcount == 0)
{
if ((fd = random_sock(serv)) != -1)
{
rfd = &daemon->randomsocks[i];
rfd->serv = serv;
rfd->fd = fd;
rfd->refcount = 1;
}
break;
}
{
/* malloc failed, don't leak allocated sock */
if (rfd)
{
close(rfd->fd);
rfd->refcount = 0;
}
return -1;
}
/* No free ones or cannot get new socket, grab an existing one */
if (!rfd)
@@ -2362,10 +2628,19 @@ int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
server_isequal(serv, daemon->randomsocks[i].serv) &&
daemon->randomsocks[i].refcount != 0xfffe)
{
finger = i + 1;
rfd = &daemon->randomsocks[i];
rfd->refcount++;
break;
struct randfd_list *rl;
/* Don't pick one we already have. */
for (rl = *fdlp; rl; rl = rl->next)
if (rl->rfd == &daemon->randomsocks[i])
break;
if (!rl)
{
finger = i + 1;
rfd = &daemon->randomsocks[i];
rfd->refcount++;
break;
}
}
}
@@ -2477,13 +2752,13 @@ static void free_frec(struct frec *f)
f->sentto = NULL;
f->flags = 0;
#ifdef HAVE_DNSSEC
if (f->stash)
{
blockdata_free(f->stash);
f->stash = NULL;
}
#ifdef HAVE_DNSSEC
/* Anything we're waiting on is pointless now, too */
if (f->blocking_query)
{
@@ -2539,6 +2814,7 @@ static struct frec *get_new_frec(time_t now, struct server *master, int force)
{
if (difftime(now, f->time) >= 4*TIMEOUT)
{
daemon->metrics[METRIC_DNS_UNANSWERED_QUERY]++;
free_frec(f);
target = f;
}
@@ -2560,6 +2836,7 @@ static struct frec *get_new_frec(time_t now, struct server *master, int force)
if (!target && oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
{
/* can't find empty one, use oldest if there is one and it's older than timeout */
daemon->metrics[METRIC_DNS_UNANSWERED_QUERY]++;
free_frec(oldest);
target = oldest;
}
@@ -2571,8 +2848,11 @@ static struct frec *get_new_frec(time_t now, struct server *master, int force)
}
if (target)
target->time = now;
{
target->time = now;
target->forward_delay = daemon->fast_retry_time;
}
return target;
}