Merge branch 'master' of ssh://thekelleys.org.uk/var/local/git/dnsmasq

This commit is contained in:
Simon Kelley
2023-11-22 15:29:10 +00:00
10 changed files with 163 additions and 72 deletions

View File

@@ -2254,6 +2254,10 @@ example command to query this, using the
utility would be
dig +short chaos txt cachesize.bind
.TP
.B --max-tcp-connections=<number>
The maximum number of concurrent TCP connections. The application forks to
handle each TCP request. The default maximum is 20.
.SH CONFIG FILE
At startup, dnsmasq reads

View File

@@ -124,6 +124,7 @@ static const struct {
{ 258, "AVC" }, /* Application Visibility and Control [Wolfgang_Riedel] AVC/avc-completed-template 2016-02-26*/
{ 259, "DOA" }, /* Digital Object Architecture [draft-durand-doa-over-dns] DOA/doa-completed-template 2017-08-30*/
{ 260, "AMTRELAY" }, /* Automatic Multicast Tunneling Relay [RFC8777] AMTRELAY/amtrelay-completed-template 2019-02-06*/
{ 261, "RESINFO" }, /* Resolver Information as Key/Value Pairs https://datatracker.ietf.org/doc/draft-ietf-add-resolver-info/06/ */
{ 32768, "TA" }, /* DNSSEC Trust Authorities [Sam_Weiler][http://cameo.library.cmu.edu/][ Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.] 2005-12-13*/
{ 32769, "DLV" }, /* DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431] */
};
@@ -424,18 +425,21 @@ unsigned int cache_remove_uid(const unsigned int uid)
{
int i;
unsigned int removed = 0;
struct crec *crecp, **up;
struct crec *crecp, *tmp, **up;
for (i = 0; i < hash_size; i++)
for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid)
{
*up = crecp->hash_next;
free(crecp);
removed++;
}
else
up = &crecp->hash_next;
for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = tmp)
{
tmp = crecp->hash_next;
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid)
{
*up = tmp;
free(crecp);
removed++;
}
else
up = &crecp->hash_next;
}
return removed;
}
@@ -629,8 +633,8 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
if (insert_error)
return NULL;
/* we don't cache zero-TTL records. */
if (ttl == 0)
/* we don't cache zero-TTL records unless we're doing stale-caching. */
if (daemon->cache_max_expiry == 0 && ttl == 0)
{
insert_error = 1;
return NULL;

View File

@@ -15,7 +15,7 @@
*/
#define FTABSIZ 150 /* max number of outstanding requests (default) */
#define MAX_PROCS 20 /* max no children for TCP requests */
#define MAX_PROCS 20 /* default max no children for TCP requests */
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
#define TCP_MAX_QUERIES 100 /* Maximum number of queries per incoming TCP connection */
#define TCP_BACKLOG 32 /* kernel backlog limit for TCP connections */

View File

@@ -92,7 +92,7 @@ void dhcp6_packet(time_t now)
struct iface_param parm;
struct cmsghdr *cmptr;
struct msghdr msg;
int if_index = 0;
uint32_t if_index = 0;
union {
struct cmsghdr align; /* this ensures alignment */
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
@@ -118,11 +118,6 @@ void dhcp6_packet(time_t now)
if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
return;
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_DHCPV6, (void *)daemon->dhcp_packet.iov_base, sz,
(union mysockaddr *)&from, NULL, daemon->dhcp6fd);
#endif
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{
@@ -138,6 +133,34 @@ void dhcp6_packet(time_t now)
if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
return;
#ifdef HAVE_LINUX_NETWORK
/* This works around a possible Linux kernel bug when using interfaces
enslaved to a VRF. The scope_id in the source address gets set
to the index of the VRF interface, not the slave. Fortunately,
the interface index returned by packetinfo is correct so we use
that instead. Log this once, so if it triggers in other circumstances
we've not anticipated and breaks things, we get some clues. */
if (from.sin6_scope_id != if_index)
{
static int logged = 0;
if (!logged)
{
my_syslog(MS_DHCP | LOG_WARNING,
_("Working around kernel bug: faulty source address scope for VRF slave %s"),
ifr.ifr_name);
logged = 1;
}
from.sin6_scope_id = if_index;
}
#endif
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_DHCPV6, (void *)daemon->dhcp_packet.iov_base, sz,
(union mysockaddr *)&from, NULL, daemon->dhcp6fd);
#endif
if (relay_reply6(&from, sz, ifr.ifr_name))
{

View File

@@ -30,12 +30,14 @@ static volatile pid_t pid = 0;
static volatile int pipewrite;
static void set_dns_listeners(void);
static void set_tftp_listeners(void);
static void check_dns_listeners(time_t now);
static void sig_handler(int sig);
static void async_event(int pipe, time_t now);
static void fatal_event(struct event_desc *ev, char *msg);
static int read_event(int fd, struct event_desc *evp, char **msg);
static void poll_resolv(int force, int do_reload, time_t now);
static void tcp_init(void);
int main (int argc, char **argv)
{
@@ -415,6 +417,8 @@ int main (int argc, char **argv)
daemon->numrrand = max_fd/3;
/* safe_malloc returns zero'd memory */
daemon->randomsocks = safe_malloc(daemon->numrrand * sizeof(struct randfd));
tcp_init();
}
#ifdef HAVE_INOTIFY
@@ -1043,8 +1047,10 @@ int main (int argc, char **argv)
pid = getpid();
daemon->pipe_to_parent = -1;
for (i = 0; i < MAX_PROCS; i++)
daemon->tcp_pipes[i] = -1;
if (daemon->port != 0)
for (i = 0; i < daemon->max_procs; i++)
daemon->tcp_pipes[i] = -1;
#ifdef HAVE_INOTIFY
/* Using inotify, have to select a resolv file at startup */
@@ -1067,7 +1073,12 @@ int main (int argc, char **argv)
(timeout == -1 || timeout > 1000))
timeout = 1000;
set_dns_listeners();
if (daemon->port != 0)
set_dns_listeners();
#ifdef HAVE_TFTP
set_tftp_listeners();
#endif
#ifdef HAVE_DBUS
if (option_bool(OPT_DBUS))
@@ -1252,8 +1263,9 @@ int main (int argc, char **argv)
check_ubus_listeners();
}
#endif
check_dns_listeners(now);
if (daemon->port != 0)
check_dns_listeners(now);
#ifdef HAVE_TFTP
check_tftp_listeners(now);
@@ -1519,8 +1531,8 @@ static void async_event(int pipe, time_t now)
if (errno != EINTR)
break;
}
else
for (i = 0 ; i < MAX_PROCS; i++)
else if (daemon->port != 0)
for (i = 0 ; i < daemon->max_procs; i++)
if (daemon->tcp_pids[i] == p)
daemon->tcp_pids[i] = 0;
break;
@@ -1584,9 +1596,10 @@ static void async_event(int pipe, time_t now)
case EVENT_TERM:
/* Knock all our children on the head. */
for (i = 0; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] != 0)
kill(daemon->tcp_pids[i], SIGALRM);
if (daemon->port != 0)
for (i = 0; i < daemon->max_procs; i++)
if (daemon->tcp_pids[i] != 0)
kill(daemon->tcp_pids[i], SIGALRM);
#if defined(HAVE_SCRIPT) && defined(HAVE_DHCP)
/* handle pending lease transitions */
@@ -1731,23 +1744,33 @@ void clear_cache_and_reload(time_t now)
#endif
}
static void set_dns_listeners(void)
{
struct serverfd *serverfdp;
struct listener *listener;
struct randfd_list *rfl;
int i;
#ifdef HAVE_TFTP
static void set_tftp_listeners(void)
{
int tftp = 0;
struct tftp_transfer *transfer;
struct listener *listener;
if (!option_bool(OPT_SINGLE_PORT))
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
{
tftp++;
poll_listen(transfer->sockfd, POLLIN);
}
for (listener = daemon->listeners; listener; listener = listener->next)
/* tftp == 0 in single-port mode. */
if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
poll_listen(listener->tftpfd, POLLIN);
}
#endif
static void set_dns_listeners(void)
{
struct serverfd *serverfdp;
struct listener *listener;
struct randfd_list *rfl;
int i;
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
poll_listen(serverfdp->fd, POLLIN);
@@ -1761,7 +1784,7 @@ static void set_dns_listeners(void)
poll_listen(rfl->rfd->fd, POLLIN);
/* check to see if we have free tcp process slots. */
for (i = MAX_PROCS - 1; i >= 0; i--)
for (i = daemon->max_procs - 1; i >= 0; i--)
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
break;
@@ -1776,16 +1799,10 @@ static void set_dns_listeners(void)
we'll be called again when a slot becomes available. */
if (listener->tcpfd != -1 && i >= 0)
poll_listen(listener->tcpfd, POLLIN);
#ifdef HAVE_TFTP
/* tftp == 0 in single-port mode. */
if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
poll_listen(listener->tftpfd, POLLIN);
#endif
}
if (!option_bool(OPT_DEBUG))
for (i = 0; i < MAX_PROCS; i++)
for (i = 0; i < daemon->max_procs; i++)
if (daemon->tcp_pipes[i] != -1)
poll_listen(daemon->tcp_pipes[i], POLLIN);
}
@@ -1820,7 +1837,7 @@ static void check_dns_listeners(time_t now)
to free the process slot. Once the child process has gone, poll()
returns POLLHUP, not POLLIN, so have to check for both here. */
if (!option_bool(OPT_DEBUG))
for (i = 0; i < MAX_PROCS; i++)
for (i = 0; i < daemon->max_procs; i++)
if (daemon->tcp_pipes[i] != -1 &&
poll_check(daemon->tcp_pipes[i], POLLIN | POLLHUP) &&
!cache_recv_insert(now, daemon->tcp_pipes[i]))
@@ -1834,17 +1851,12 @@ static void check_dns_listeners(time_t now)
if (listener->fd != -1 && poll_check(listener->fd, POLLIN))
receive_query(listener, now);
#ifdef HAVE_TFTP
if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN))
tftp_request(listener, now);
#endif
/* check to see if we have a free tcp process slot.
Note that we can't assume that because we had
at least one a poll() time, that we still do.
There may be more waiting connections after
poll() returns then free process slots. */
for (i = MAX_PROCS - 1; i >= 0; i--)
for (i = daemon->max_procs - 1; i >= 0; i--)
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
break;
@@ -2135,7 +2147,11 @@ int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
poll_reset();
if (fd != -1)
poll_listen(fd, POLLIN);
set_dns_listeners();
if (daemon->port != 0)
set_dns_listeners();
#ifdef HAVE_TFTP
set_tftp_listeners();
#endif
set_log_writer();
#ifdef HAVE_DHCP6
@@ -2153,7 +2169,8 @@ int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
now = dnsmasq_time();
check_log_writer(0);
check_dns_listeners(now);
if (daemon->port != 0)
check_dns_listeners(now);
#ifdef HAVE_DHCP6
if (daemon->doing_ra && poll_check(daemon->icmp6fd, POLLIN))
@@ -2186,3 +2203,9 @@ int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
return 0;
}
#endif /* HAVE_DHCP */
void tcp_init(void)
{
daemon->tcp_pids = safe_malloc(daemon->max_procs*sizeof(pid_t));
daemon->tcp_pipes = safe_malloc(daemon->max_procs*sizeof(int));
}

View File

@@ -1252,8 +1252,8 @@ extern struct daemon {
struct server *srv_save; /* Used for resend on DoD */
size_t packet_len; /* " " */
int fd_save; /* " " */
pid_t tcp_pids[MAX_PROCS];
int tcp_pipes[MAX_PROCS];
pid_t *tcp_pids;
int *tcp_pipes;
int pipe_to_parent;
int numrrand;
struct randfd *randomsocks;
@@ -1313,6 +1313,7 @@ extern struct daemon {
/* file for packet dumps. */
int dumpfd;
#endif
int max_procs;
} *daemon;
struct server_details {

View File

@@ -190,6 +190,7 @@ struct myoption {
#define LOPT_FILTER_RR 381
#define LOPT_NO_DHCP6 382
#define LOPT_NO_DHCP4 383
#define LOPT_MAX_PROCS 384
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -384,6 +385,7 @@ static const struct myoption opts[] =
{ "fast-dns-retry", 2, 0, LOPT_FAST_RETRY },
{ "use-stale-cache", 2, 0 , LOPT_STALE_CACHE },
{ "no-ident", 0, 0, LOPT_NO_IDENT },
{ "max-tcp-connections", 1, 0, LOPT_MAX_PROCS },
{ NULL, 0, 0, 0 }
};
@@ -585,6 +587,7 @@ static struct {
{ LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL },
{ LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL },
{ LOPT_CACHE_RR, ARG_DUP, "<RR-type>", gettext_noop("Cache this DNS resource record type."), NULL },
{ LOPT_MAX_PROCS, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent tcp connections."), NULL },
{ 0, 0, NULL, NULL, NULL }
};
@@ -5313,7 +5316,17 @@ err:
break;
}
#endif
case LOPT_MAX_PROCS: /* --max-tcp-connections */
{
int max_procs;
/* Don't accept numbers less than 1. */
if (!atoi_check(arg, &max_procs) || max_procs < 1)
ret_err(gen_err);
daemon->max_procs = max_procs;
break;
}
default:
ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
@@ -5841,6 +5854,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon->soa_expiry = SOA_EXPIRY;
daemon->randport_limit = 1;
daemon->host_index = SRC_AH;
daemon->max_procs = MAX_PROCS;
/* See comment above make_servers(). Optimises server-read code. */
mark_servers(0);

View File

@@ -847,11 +847,18 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
int len;
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 2;
{
blockdata_free(addr.rrblock.rrdata);
return 2;
}
len = to_wire(name);
if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, name, len))
return 0;
{
blockdata_free(addr.rrblock.rrdata);
return 0;
}
addr.rrblock.datalen += len;
}
else
@@ -859,8 +866,13 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
/* desc is length of a block of data to be used as-is */
if (desc > endrr - p1)
desc = endrr - p1;
if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p1, desc))
return 0;
{
blockdata_free(addr.rrblock.rrdata);
return 0;
}
addr.rrblock.datalen += desc;
p1 += desc;
}
@@ -868,7 +880,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
/* we overwrote the original name, so get it back here. */
if (!extract_name(header, qlen, &tmp, name, 1, 0))
return 2;
{
blockdata_free(addr.rrblock.rrdata);
return 2;
}
}
}
else if (flags & (F_IPV4 | F_IPV6))
@@ -914,6 +929,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
cpp->addr.cname.uid = newc->uid;
}
cpp = NULL;
/* cache insert failed, don't leak blockdata. */
if (!newc && (flags & F_RR) && (flags & F_KEYTAG))
blockdata_free(addr.rrblock.rrdata);
}
if (aqtype == T_TXT)

View File

@@ -1074,7 +1074,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
case DHCP6CONFIRM:
{
int good_addr = 0;
int good_addr = 0, bad_addr = 0;
/* set reply message type */
outmsgtype = DHCP6REPLY;
@@ -1096,26 +1096,24 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
if (!address6_valid(state->context, &req_addr, tagif, 1))
{
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOTONLINK);
put_opt6_string(_("confirm failed"));
end_opt6(o1);
bad_addr = 1;
log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed"));
return 1;
}
good_addr = 1;
log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname);
else
{
good_addr = 1;
log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname);
}
}
}
/* No addresses, no reply: RFC 3315 18.2.2 */
if (!good_addr)
if (!good_addr && !bad_addr)
return 0;
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6SUCCESS );
put_opt6_string(_("all addresses still on link"));
put_opt6_short(bad_addr ? DHCP6NOTONLINK : DHCP6SUCCESS);
put_opt6_string(bad_addr ? (_("confirm failed")) : (_("all addresses still on link")));
end_opt6(o1);
break;
}

View File

@@ -585,8 +585,13 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *c
void check_tftp_listeners(time_t now)
{
struct listener *listener;
struct tftp_transfer *transfer, *tmp, **up;
for (listener = daemon->listeners; listener; listener = listener->next)
if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN))
tftp_request(listener, now);
/* In single port mode, all packets come via port 69 and tftp_request() */
if (!option_bool(OPT_SINGLE_PORT))
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)