1 Star 0 Fork 20

liuyumeng/dnsmasq

forked from src-openEuler/dnsmasq 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
backport-0010-CVE-2021-3448.patch 34.11 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
From 74d4fcd756a85bc1823232ea74334f7ccfb9d5d2 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Mon, 15 Mar 2021 21:59:51 +0000
Subject: [PATCH] Use random source ports where possible if source
addresses/interfaces in use.
CVE-2021-3448 applies.
It's possible to specify the source address or interface to be
used when contacting upstream nameservers: server=8.8.8.8@1.2.3.4
or server=8.8.8.8@1.2.3.4#66 or server=8.8.8.8@eth0, and all of
these have, until now, used a single socket, bound to a fixed
port. This was originally done to allow an error (non-existent
interface, or non-local address) to be detected at start-up. This
means that any upstream servers specified in such a way don't use
random source ports, and are more susceptible to cache-poisoning
attacks.
We now use random ports where possible, even when the
source is specified, so server=8.8.8.8@1.2.3.4 or
server=8.8.8.8@eth0 will use random source
ports. server=8.8.8.8@1.2.3.4#66 or any use of --query-port will
use the explicitly configured port, and should only be done with
understanding of the security implications.
Note that this change changes non-existing interface, or non-local
source address errors from fatal to run-time. The error will be
logged and communiction with the server not possible.
---
man/dnsmasq.8 | 4 +-
src/dnsmasq.c | 31 +++--
src/dnsmasq.h | 26 ++--
src/forward.c | 392 +++++++++++++++++++++++++++++++++++-----------------------
src/loop.c | 20 ++-
src/network.c | 110 +++++-----------
src/option.c | 3 +-
src/tftp.c | 6 +-
src/util.c | 2 +-
10 files changed, 344 insertions(+), 272 deletions(-)
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 6dee5a4..4910bd2 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -431,7 +431,7 @@ Tells dnsmasq to never forward A or AAAA queries for plain names, without dots
or domain parts, to upstream nameservers. If the name is not known
from /etc/hosts or DHCP then a "not found" answer is returned.
.TP
-.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>]][@<source-ip>|<interface>[#<port>]]
+.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>]][@<interface>][@<source-ip>[#<port>]]
Specify IP address of upstream servers directly. Setting this flag does
not suppress reading of /etc/resolv.conf, use \fB--no-resolv\fP to do that. If one or more
optional domains are given, that server is used only for those domains
@@ -492,7 +492,7 @@ source address specified but the port may be specified directly as
part of the source address. Forcing queries to an interface is not
implemented on all platforms supported by dnsmasq.
.TP
-.B --rev-server=<ip-address>/<prefix-len>[,<ipaddr>][#<port>][@<source-ip>|<interface>[#<port>]]
+.B --rev-server=<ip-address>/<prefix-len>[,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
This is functionally the same as
.B --server,
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index a2e1796..d1806fc 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -1672,6 +1672,7 @@ static int set_dns_listeners(time_t now)
{
struct serverfd *serverfdp;
struct listener *listener;
+ struct randfd_list *rfl;
int wait = 0, i;
#ifdef HAVE_TFTP
@@ -1692,11 +1693,14 @@ static int set_dns_listeners(time_t now)
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
poll_listen(serverfdp->fd, POLLIN);
- if (daemon->port != 0 && !daemon->osport)
- for (i = 0; i < RANDOM_SOCKS; i++)
- if (daemon->randomsocks[i].refcount != 0)
- poll_listen(daemon->randomsocks[i].fd, POLLIN);
-
+ for (i = 0; i < RANDOM_SOCKS; i++)
+ if (daemon->randomsocks[i].refcount != 0)
+ poll_listen(daemon->randomsocks[i].fd, POLLIN);
+
+ /* Check overflow random sockets too. */
+ for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next)
+ poll_listen(rfl->rfd->fd, POLLIN);
+
for (listener = daemon->listeners; listener; listener = listener->next)
{
/* only listen for queries if we have resources */
@@ -1733,18 +1737,23 @@ static void check_dns_listeners(time_t now)
{
struct serverfd *serverfdp;
struct listener *listener;
+ struct randfd_list *rfl;
int i;
int pipefd[2];
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
if (poll_check(serverfdp->fd, POLLIN))
- reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);
+ reply_query(serverfdp->fd, now);
- if (daemon->port != 0 && !daemon->osport)
- for (i = 0; i < RANDOM_SOCKS; i++)
- if (daemon->randomsocks[i].refcount != 0 &&
- poll_check(daemon->randomsocks[i].fd, POLLIN))
- reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
+ for (i = 0; i < RANDOM_SOCKS; i++)
+ if (daemon->randomsocks[i].refcount != 0 &&
+ poll_check(daemon->randomsocks[i].fd, POLLIN))
+ reply_query(daemon->randomsocks[i].fd, now);
+
+ /* Check overflow random sockets too. */
+ for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next)
+ if (poll_check(rfl->rfd->fd, POLLIN))
+ reply_query(rfl->rfd->fd, now);
/* Races. The child process can die before we read all of the data from the
pipe, or vice versa. Therefore send tcp_pids to zero when we wait() the
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index e201b7a..b0080d0 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -549,13 +549,20 @@ struct serverfd {
};
struct randfd {
+ struct server *serv;
int fd;
- unsigned short refcount, family;
+ unsigned short refcount; /* refcount == 0xffff means overflow record. */
};
-
+
+struct randfd_list {
+ struct randfd *rfd;
+ struct randfd_list *next;
+};
+
struct server {
union mysockaddr addr, source_addr;
char interface[IF_NAMESIZE+1];
+ unsigned int ifindex; /* corresponding to interface, above */
struct serverfd *sfd;
char *domain; /* set if this server only handles a domain. */
int flags, tcpfd, edns_pktsz;
@@ -679,8 +686,7 @@ struct frec {
struct frec_src *next;
} frec_src;
struct server *sentto; /* NULL means free */
- struct randfd *rfd4;
- struct randfd *rfd6;
+ struct randfd_list *rfds;
unsigned short new_id;
int forwardall, flags;
time_t time;
@@ -1120,11 +1126,12 @@ extern struct daemon {
int forwardcount;
struct server *srv_save; /* Used for resend on DoD */
size_t packet_len; /* " " */
- struct randfd *rfd_save; /* " " */
+ int fd_save; /* " " */
pid_t tcp_pids[MAX_PROCS];
int tcp_pipes[MAX_PROCS];
int pipe_to_parent;
struct randfd randomsocks[RANDOM_SOCKS];
+ struct randfd_list *rfl_spare, *rfl_poll;
int v6pktinfo;
struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */
int log_id, log_display_id; /* ids of transactions for logging */
@@ -1296,7 +1303,7 @@ void safe_strncpy(char *dest, const char *src, size_t size);
void safe_pipe(int *fd, int read_noblock);
void *whine_malloc(size_t size);
int sa_len(union mysockaddr *addr);
-int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
+int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2);
int hostname_isequal(const char *a, const char *b);
int hostname_issubdomain(char *a, char *b);
time_t dnsmasq_time(void);
@@ -1347,7 +1354,7 @@ char *parse_server(char *arg, union mysockaddr *addr,
int option_read_dynfile(char *file, int flags);
/* forward.c */
-void reply_query(int fd, int family, time_t now);
+void reply_query(int fd, time_t now);
void receive_query(struct listener *listen, time_t now);
unsigned char *tcp_request(int confd, time_t now,
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
@@ -1357,13 +1364,12 @@ int send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, union all_addr *source,
unsigned int iface);
void resend_query(void);
-struct randfd *allocate_rfd(int family);
-void free_rfd(struct randfd *rfd);
+int allocate_rfd(struct randfd_list **fdlp, struct server *serv);
+void free_rfds(struct randfd_list **fdlp);
/* network.c */
int indextoname(int fd, int index, char *name);
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp);
-int random_sock(int family);
void pre_allocate_sfds(void);
int reload_servers(char *fname);
void mark_servers(int flag);
diff --git a/src/forward.c b/src/forward.c
index 6bbf8a4..a15ac10 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -16,7 +16,7 @@
#include "dnsmasq.h"
-static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash);
+static struct frec *lookup_frec(unsigned short id, int fd, void *hash);
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags);
static unsigned short get_id(void);
@@ -344,26 +344,18 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
PUTSHORT(SAFE_PKTSZ, pheader);
- if (forward->sentto->addr.sa.sa_family == AF_INET)
- 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", (union all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
-
-
- if (forward->sentto->sfd)
- fd = forward->sentto->sfd->fd;
- else
+ if ((fd = allocate_rfd(&forward->rfds, forward->sentto)) != -1)
{
- if (forward->sentto->addr.sa.sa_family == AF_INET6)
- fd = forward->rfd6->fd;
+ if (forward->sentto->addr.sa.sa_family == AF_INET)
+ log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (union all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
else
- fd = forward->rfd4->fd;
+ log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (union all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
+
+ 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;
}
#endif
@@ -508,48 +500,27 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
while (1)
{
+ int fd;
+
/* only send to servers dealing with our domain.
domain may be NULL, in which case server->domain
must be NULL also. */
if (type == (start->flags & SERV_TYPE) &&
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
- !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
+ !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)) &&
+ ((fd = allocate_rfd(&forward->rfds, start)) != -1))
{
- int fd;
-
- /* find server socket to use, may need to get random one. */
- if (start->sfd)
- fd = start->sfd->fd;
- else
- {
- if (start->addr.sa.sa_family == AF_INET6)
- {
- if (!forward->rfd6 &&
- !(forward->rfd6 = allocate_rfd(AF_INET6)))
- break;
- daemon->rfd_save = forward->rfd6;
- fd = forward->rfd6->fd;
- }
- else
- {
- if (!forward->rfd4 &&
- !(forward->rfd4 = allocate_rfd(AF_INET)))
- break;
- daemon->rfd_save = forward->rfd4;
- fd = forward->rfd4->fd;
- }
-
+
#ifdef HAVE_CONNTRACK
- /* Copy connection mark of incoming query to outgoing connection. */
- if (option_bool(OPT_CONNTRACK))
- {
- unsigned int mark;
- if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark))
- setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
- }
-#endif
+ /* Copy connection mark of incoming query to outgoing connection. */
+ if (option_bool(OPT_CONNTRACK))
+ {
+ unsigned int mark;
+ if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark))
+ setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
+#endif
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER))
@@ -581,6 +552,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
/* Keep info in case we want to re-send this packet */
daemon->srv_save = start;
daemon->packet_len = plen;
+ daemon->fd_save = fd;
if (!gotname)
strcpy(daemon->namebuff, "query");
@@ -597,7 +569,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
break;
forward->forwardall++;
}
- }
+ }
if (!(start = start->next))
start = daemon->servers;
@@ -812,7 +784,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
}
/* sets new last_server */
-void reply_query(int fd, int family, time_t now)
+void reply_query(int fd, time_t now)
{
/* packet from peer server, extract data for cache, and send to
original requester */
@@ -827,9 +799,9 @@ void reply_query(int fd, int family, time_t now)
/* packet buffer overwritten */
daemon->srv_save = NULL;
-
+
/* Determine the address of the server replying so that we can mark that as good */
- if ((serveraddr.sa.sa_family = family) == AF_INET6)
+ if (serveraddr.sa.sa_family == AF_INET6)
serveraddr.in6.sin6_flowinfo = 0;
header = (struct dns_header *)daemon->packet;
@@ -852,7 +824,7 @@ void reply_query(int fd, int family, time_t now)
hash = hash_questions(header, n, daemon->namebuff);
- if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash)))
+ if (!(forward = lookup_frec(ntohs(header->id), fd, hash)))
return;
#ifdef HAVE_DUMPFILE
@@ -906,28 +878,7 @@ void reply_query(int fd, int family, time_t now)
}
- fd = -1;
-
- if (start->sfd)
- fd = start->sfd->fd;
- else
- {
- if (start->addr.sa.sa_family == AF_INET6)
- {
- /* may have changed family */
- if (forward->rfd6 || (forward->rfd6 = allocate_rfd(AF_INET6)))
- fd = forward->rfd6->fd;
- }
- else
- {
- /* may have changed family */
- if (forward->rfd4 || (forward->rfd4 = allocate_rfd(AF_INET)))
- fd = forward->rfd4->fd;
- }
- }
-
- /* Can't get socket. */
- if (fd == -1)
+ if ((fd = allocate_rfd(&forward->rfds, start)) == -1)
return;
#ifdef HAVE_DUMPFILE
@@ -1136,8 +1087,7 @@ void reply_query(int fd, int family, time_t now)
}
new->sentto = server;
- new->rfd4 = NULL;
- new->rfd6 = NULL;
+ new->rfds = NULL;
new->frec_src.next = NULL;
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA);
new->forwardall = 0;
@@ -1176,24 +1126,7 @@ void reply_query(int fd, int family, time_t now)
/* Don't resend this. */
daemon->srv_save = NULL;
- if (server->sfd)
- fd = server->sfd->fd;
- else
- {
- fd = -1;
- if (server->addr.sa.sa_family == AF_INET6)
- {
- if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
- fd = new->rfd6->fd;
- }
- else
- {
- if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
- fd = new->rfd4->fd;
- }
- }
-
- if (fd != -1)
+ if ((fd = allocate_rfd(&new->rfds, server)) != -1)
{
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
@@ -1360,7 +1293,7 @@ void receive_query(struct listener *listen, time_t now)
/* packet buffer overwritten */
daemon->srv_save = NULL;
-
+
dst_addr_4.s_addr = dst_addr.addr4.s_addr = 0;
netmask.s_addr = 0;
@@ -2228,9 +2161,8 @@ static struct frec *allocate_frec(time_t now)
f->next = daemon->frec_list;
f->time = now;
f->sentto = NULL;
- f->rfd4 = NULL;
+ f->rfds = NULL;
f->flags = 0;
- f->rfd6 = NULL;
#ifdef HAVE_DNSSEC
f->dependent = NULL;
f->blocking_query = NULL;
@@ -2242,46 +2174,192 @@ static struct frec *allocate_frec(time_t now)
return f;
}
-struct randfd *allocate_rfd(int family)
+/* return a UDP socket bound to a random port, have to cope with straying into
+ occupied port nos and reserved ones. */
+static int random_sock(struct server *s)
{
- static int finger = 0;
- int i;
+ int fd;
+
+ if ((fd = socket(s->source_addr.sa.sa_family, SOCK_DGRAM, 0)) != -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));
+ close(fd);
+ }
+
+ return -1;
+}
+
+/* compare source addresses and interface, serv2 can be null. */
+static int server_isequal(const struct server *serv1,
+ const struct server *serv2)
+{
+ return (serv2 &&
+ serv2->ifindex == serv1->ifindex &&
+ sockaddr_isequal(&serv2->source_addr, &serv1->source_addr) &&
+ strncmp(serv2->interface, serv1->interface, IF_NAMESIZE) == 0);
+}
+
+/* fdlp points to chain of randomfds already in use by transaction.
+ If there's already a suitable one, return it, else allocate a
+ new one and add it to the list.
+
+ Not leaking any resources in the face of allocation failures
+ is rather convoluted here.
+
+ Note that rfd->serv may be NULL, when a server goes away.
+*/
+int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
+{
+ static int finger = 0;
+ int i, j = 0;
+ struct randfd_list *rfl;
+ struct randfd *rfd = NULL;
+ int fd = 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)
+ if (server_isequal(serv, rfl->rfd->serv))
+ return rfl->rfd->fd;
+
+ /* No. 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 < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount == 0)
{
- if ((daemon->randomsocks[i].fd = random_sock(family)) == -1)
- break;
-
- daemon->randomsocks[i].refcount = 1;
- daemon->randomsocks[i].family = family;
- return &daemon->randomsocks[i];
+ if ((fd = random_sock(serv)) != -1)
+ {
+ rfd = &daemon->randomsocks[i];
+ rfd->serv = serv;
+ rfd->fd = fd;
+ rfd->refcount = 1;
+ }
+ break;
}
-
+
/* No free ones or cannot get new socket, grab an existing one */
- for (i = 0; i < RANDOM_SOCKS; i++)
+ if (!rfd)
+ for (j = 0; j < RANDOM_SOCKS; j++)
+ {
+ i = (j + finger) % RANDOM_SOCKS;
+ if (daemon->randomsocks[i].refcount != 0 &&
+ server_isequal(serv, daemon->randomsocks[i].serv) &&
+ daemon->randomsocks[i].refcount != 0xfffe)
+ {
+ finger = i + 1;
+ rfd = &daemon->randomsocks[i];
+ rfd->refcount++;
+ break;
+ }
+ }
+
+ if (j == RANDOM_SOCKS)
{
- int j = (i+finger) % RANDOM_SOCKS;
- if (daemon->randomsocks[j].refcount != 0 &&
- daemon->randomsocks[j].family == family &&
- daemon->randomsocks[j].refcount != 0xffff)
+ struct randfd_list *rfl_poll;
+
+ /* there are no free slots, and non with the same parameters we can piggy-back on.
+ We're going to have to allocate a new temporary record, distinguished by
+ refcount == 0xffff. This will exist in the frec randfd list, never be shared,
+ and be freed when no longer in use. It will also be held on
+ the daemon->rfl_poll list so the poll system can find it. */
+
+ if ((rfl_poll = daemon->rfl_spare))
+ daemon->rfl_spare = rfl_poll->next;
+ else
+ rfl_poll = whine_malloc(sizeof(struct randfd_list));
+
+ if (!rfl_poll ||
+ !(rfd = whine_malloc(sizeof(struct randfd))) ||
+ (fd = random_sock(serv)) == -1)
{
- finger = j;
- daemon->randomsocks[j].refcount++;
- return &daemon->randomsocks[j];
+
+ /* Don't leak anything we may already have */
+ rfl->next = daemon->rfl_spare;
+ daemon->rfl_spare = rfl;
+
+ if (rfl_poll)
+ {
+ rfl_poll->next = daemon->rfl_spare;
+ daemon->rfl_spare = rfl_poll;
+ }
+
+ if (rfd)
+ free(rfd);
+
+ return -1; /* doom */
}
- }
- return NULL; /* doom */
+ /* Note rfd->serv not set here, since it's not reused */
+ rfd->fd = fd;
+ rfd->refcount = 0xffff; /* marker for temp record */
+
+ rfl_poll->rfd = rfd;
+ rfl_poll->next = daemon->rfl_poll;
+ daemon->rfl_poll = rfl_poll;
+ }
+
+ rfl->rfd = rfd;
+ rfl->next = *fdlp;
+ *fdlp = rfl;
+
+ return rfl->rfd->fd;
}
-void free_rfd(struct randfd *rfd)
+void free_rfds(struct randfd_list **fdlp)
{
- if (rfd && --(rfd->refcount) == 0)
- close(rfd->fd);
+ struct randfd_list *tmp, *rfl, *poll, *next, **up;
+
+ for (rfl = *fdlp; rfl; rfl = tmp)
+ {
+ if (rfl->rfd->refcount == 0xffff || --(rfl->rfd->refcount) == 0)
+ close(rfl->rfd->fd);
+
+ /* temporary overflow record */
+ if (rfl->rfd->refcount == 0xffff)
+ {
+ free(rfl->rfd);
+
+ /* go through the link of all these by steam to delete.
+ This list is expected to be almost always empty. */
+ for (poll = daemon->rfl_poll, up = &daemon->rfl_poll; poll; poll = next)
+ {
+ next = poll->next;
+
+ if (poll->rfd == rfl->rfd)
+ {
+ *up = poll->next;
+ poll->next = daemon->rfl_spare;
+ daemon->rfl_spare = poll;
+ }
+ else
+ up = &poll->next;
+ }
+ }
+
+ tmp = rfl->next;
+ rfl->next = daemon->rfl_spare;
+ daemon->rfl_spare = rfl;
+ }
+
+ *fdlp = NULL;
}
static void free_frec(struct frec *f)
@@ -2297,12 +2375,9 @@ static void free_frec(struct frec *f)
}
f->frec_src.next = NULL;
- free_rfd(f->rfd4);
- f->rfd4 = NULL;
+ free_rfds(&f->rfds);
f->sentto = NULL;
f->flags = 0;
- free_rfd(f->rfd6);
- f->rfd6 = NULL;
#ifdef HAVE_DNSSEC
if (f->stash)
@@ -2409,26 +2484,39 @@ struct frec *get_new_frec(time_t now, int *wait, struct frec *force)
return f; /* OK if malloc fails and this is NULL */
}
-static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash)
+static struct frec *lookup_frec(unsigned short id, int fd, void *hash)
{
struct frec *f;
-
+ struct server *s;
+ int type;
+ struct randfd_list *fdl;
+
for(f = daemon->frec_list; f; f = f->next)
if (f->sentto && f->new_id == id &&
(memcmp(hash, f->hash, HASH_SIZE) == 0))
{
/* 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)
+ for (fdl = f->rfds; fdl; fdl = fdl->next)
+ if (fdl->rfd->fd == fd)
return f;
+
+ /* Sent to upstream from socket associated with a server.
+ Note we have to iterate over all the possible servers, since they may
+ have different bound sockets. */
+ type = f->sentto->flags & SERV_TYPE;
+ s = f->sentto;
+ do {
+ if ((type == (s->flags & SERV_TYPE)) &&
+ (type != SERV_HAS_DOMAIN ||
+ (s->domain && hostname_isequal(f->sentto->domain, s->domain))) &&
+ !(s->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)) &&
+ s->sfd && s->sfd->fd == fd)
+ return f;
+
+ s = s->next ? s->next : daemon->servers;
+ } while (s != f->sentto);
}
-
+
return NULL;
}
@@ -2458,34 +2546,30 @@ static struct frec *lookup_frec_by_query(void *hash, unsigned int flags)
void resend_query()
{
if (daemon->srv_save)
- {
- int fd;
-
- if (daemon->srv_save->sfd)
- fd = daemon->srv_save->sfd->fd;
- else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
- fd = daemon->rfd_save->fd;
- else
- return;
-
- while(retry_send(sendto(fd, daemon->packet, daemon->packet_len, 0,
- &daemon->srv_save->addr.sa,
- sa_len(&daemon->srv_save->addr))));
- }
+ while(retry_send(sendto(daemon->fd_save, daemon->packet, daemon->packet_len, 0,
+ &daemon->srv_save->addr.sa,
+ sa_len(&daemon->srv_save->addr))));
}
/* A server record is going away, remove references to it */
void server_gone(struct server *server)
{
struct frec *f;
+ int i;
for (f = daemon->frec_list; f; f = f->next)
if (f->sentto && f->sentto == server)
free_frec(f);
+
+ /* If any random socket refers to this server, NULL the reference.
+ No more references to the socket will be created in the future. */
+ for (i = 0; i < RANDOM_SOCKS; i++)
+ if (daemon->randomsocks[i].refcount != 0 && daemon->randomsocks[i].serv == server)
+ daemon->randomsocks[i].serv = NULL;
if (daemon->last_server == server)
daemon->last_server = NULL;
-
+
if (daemon->srv_save == server)
daemon->srv_save = NULL;
}
diff --git a/src/loop.c b/src/loop.c
index 15e34c7..758674b 100644
--- a/src/loop.c
+++ b/src/loop.c
@@ -22,6 +22,7 @@ static ssize_t loop_make_probe(u32 uid);
void loop_send_probes()
{
struct server *serv;
+ struct randfd_list *rfds = NULL;
if (!option_bool(OPT_LOOP_DETECT))
return;
@@ -34,29 +35,22 @@ void loop_send_probes()
{
ssize_t len = loop_make_probe(serv->uid);
int fd;
- struct randfd *rfd = NULL;
- if (serv->sfd)
- fd = serv->sfd->fd;
- else
- {
- if (!(rfd = allocate_rfd(serv->addr.sa.sa_family)))
- continue;
- fd = rfd->fd;
- }
-
+ if ((fd = allocate_rfd(&rfds, serv)) == -1)
+ continue;
+
while (retry_send(sendto(fd, daemon->packet, len, 0,
&serv->addr.sa, sa_len(&serv->addr))));
-
- free_rfd(rfd);
}
+
+ free_rfds(&rfds);
}
static ssize_t loop_make_probe(u32 uid)
{
struct dns_header *header = (struct dns_header *)daemon->packet;
unsigned char *p = (unsigned char *)(header+1);
-
+
/* packet buffer overwritten */
daemon->srv_save = NULL;
diff --git a/src/network.c b/src/network.c
index 5286c61..cca6ff2 100644
--- a/src/network.c
+++ b/src/network.c
@@ -696,7 +696,8 @@ int enumerate_interfaces(int reset)
#ifdef HAVE_AUTH
struct auth_zone *zone;
#endif
-
+ struct server *serv;
+
/* Do this max once per select cycle - also inhibits netlink socket use
in TCP child processes. */
@@ -714,6 +715,20 @@ int enumerate_interfaces(int reset)
if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
return 0;
+ /* iface indexes can change when interfaces are created/destroyed.
+ We use them in the main forwarding control path, when the path
+ to a server is specified by an interface, so cache them.
+ Update the cache here. */
+ for (serv = daemon->servers; serv; serv = serv->next)
+ if (strlen(serv->interface) != 0)
+ {
+ struct ifreq ifr;
+
+ safe_strncpy(ifr.ifr_name, serv->interface, IF_NAMESIZE);
+ if (ioctl(param.fd, SIOCGIFINDEX, &ifr) != -1)
+ serv->ifindex = ifr.ifr_ifindex;
+ }
+
again:
/* Mark interfaces for garbage collection */
for (iface = daemon->interfaces; iface; iface = iface->next)
@@ -808,7 +823,7 @@ again:
errno = errsave;
spare = param.spare;
-
+
return ret;
}
@@ -948,10 +963,10 @@ int tcp_interface(int fd, int af)
/* use mshdr so that the CMSDG_* macros are available */
msg.msg_control = daemon->packet;
msg.msg_controllen = len = daemon->packet_buff_sz;
-
+
/* we overwrote the buffer... */
- daemon->srv_save = NULL;
-
+ daemon->srv_save = NULL;
+
if (af == AF_INET)
{
if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 &&
@@ -1287,59 +1302,6 @@ void join_multicast(int dienow)
}
#endif
-/* return a UDP socket bound to a random port, have to cope with straying into
- occupied port nos and reserved ones. */
-int random_sock(int family)
-{
- int fd;
-
- if ((fd = socket(family, SOCK_DGRAM, 0)) != -1)
- {
- union mysockaddr addr;
- unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1;
- int tries = ports_avail < 30 ? 3 * ports_avail : 100;
-
- memset(&addr, 0, sizeof(addr));
- addr.sa.sa_family = family;
-
- /* don't loop forever if all ports in use. */
-
- if (fix_fd(fd))
- while(tries--)
- {
- unsigned short port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
-
- if (family == AF_INET)
- {
- addr.in.sin_addr.s_addr = INADDR_ANY;
- addr.in.sin_port = port;
-#ifdef HAVE_SOCKADDR_SA_LEN
- addr.in.sin_len = sizeof(struct sockaddr_in);
-#endif
- }
- else
- {
- addr.in6.sin6_addr = in6addr_any;
- addr.in6.sin6_port = port;
-#ifdef HAVE_SOCKADDR_SA_LEN
- addr.in6.sin6_len = sizeof(struct sockaddr_in6);
-#endif
- }
-
- if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0)
- return fd;
-
- if (errno != EADDRINUSE && errno != EACCES)
- break;
- }
-
- close(fd);
- }
-
- return -1;
-}
-
-
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp)
{
union mysockaddr addr_copy = *addr;
@@ -1355,10 +1317,9 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
/* cannot set source _port_ for TCP connections. */
if (is_tcp)
port = 0;
-
- /* Bind a random port within the range given by min-port and max-port */
- if (port == 0)
+ else if (port == 0)
{
+ /* Bind a random port within the range given by min-port and max-port */
tries = ports_avail < 30 ? 3 * ports_avail : 100;
port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
}
@@ -1413,38 +1374,33 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
return 1;
}
-static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
+static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname, unsigned int ifindex)
{
struct serverfd *sfd;
- unsigned int ifindex = 0;
int errsave;
int opt = 1;
/* when using random ports, servers which would otherwise use
- the INADDR_ANY/port0 socket have sfd set to NULL */
- if (!daemon->osport && intname[0] == 0)
+ the INADDR_ANY/port0 socket have sfd set to NULL, this is
+ anything without an explictly set source port. */
+ if (!daemon->osport)
{
errno = 0;
if (addr->sa.sa_family == AF_INET &&
- addr->in.sin_addr.s_addr == INADDR_ANY &&
addr->in.sin_port == htons(0))
return NULL;
if (addr->sa.sa_family == AF_INET6 &&
- memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&
addr->in6.sin6_port == htons(0))
return NULL;
}
- if (intname && strlen(intname) != 0)
- ifindex = if_nametoindex(intname); /* index == 0 when not binding to an interface */
-
/* may have a suitable one already */
for (sfd = daemon->sfds; sfd; sfd = sfd->next )
- if (sockaddr_isequal(&sfd->source_addr, addr) &&
- strcmp(intname, sfd->interface) == 0 &&
- ifindex == sfd->ifindex)
+ if (ifindex == sfd->ifindex &&
+ sockaddr_isequal(&sfd->source_addr, addr) &&
+ strcmp(intname, sfd->interface) == 0)
return sfd;
/* need to make a new one. */
@@ -1495,7 +1451,7 @@ void pre_allocate_sfds(void)
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in.sin_len = sizeof(struct sockaddr_in);
#endif
- if ((sfd = allocate_sfd(&addr, "")))
+ if ((sfd = allocate_sfd(&addr, "", 0)))
sfd->preallocated = 1;
memset(&addr, 0, sizeof(addr));
@@ -1505,13 +1461,13 @@ void pre_allocate_sfds(void)
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
#endif
- if ((sfd = allocate_sfd(&addr, "")))
+ if ((sfd = allocate_sfd(&addr, "", 0)))
sfd->preallocated = 1;
}
for (srv = daemon->servers; srv; srv = srv->next)
if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
- !allocate_sfd(&srv->source_addr, srv->interface) &&
+ !allocate_sfd(&srv->source_addr, srv->interface, srv->ifindex) &&
errno != 0 &&
option_bool(OPT_NOWILD))
{
@@ -1720,7 +1676,7 @@ void check_servers(void)
/* Do we need a socket set? */
if (!serv->sfd &&
- !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) &&
+ !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface, serv->ifindex)) &&
errno != 0)
{
my_syslog(LOG_WARNING,
diff --git a/src/option.c b/src/option.c
index bfda212..159d1d4 100644
--- a/src/option.c
+++ b/src/option.c
@@ -819,7 +819,8 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
if (interface_opt)
{
#if defined(SO_BINDTODEVICE)
- safe_strncpy(interface, interface_opt, IF_NAMESIZE);
+ safe_strncpy(interface, source, IF_NAMESIZE);
+ source = interface_opt;
#else
return _("interface binding not supported");
#endif
diff --git a/src/tftp.c b/src/tftp.c
index 846b32e..ea2aa84 100644
--- a/src/tftp.c
+++ b/src/tftp.c
@@ -94,7 +94,7 @@ void tftp_request(struct listener *listen, time_t now)
if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
return;
-
+
/* Can always get recvd interface for IPv6 */
if (!check_dest)
{
@@ -587,7 +587,7 @@ void check_tftp_listeners(time_t now)
daemon->srv_save = NULL;
handle_tftp(now, transfer, recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0));
}
-
+
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
{
tmp = transfer->next;
@@ -602,7 +602,7 @@ void check_tftp_listeners(time_t now)
/* we overwrote the buffer... */
daemon->srv_save = NULL;
-
+
if ((len = get_block(daemon->packet, transfer)) == -1)
{
len = tftp_err_oops(daemon->packet, transfer->file->filename);
diff --git a/src/util.c b/src/util.c
index 972c60b..6176dc9 100644
--- a/src/util.c
+++ b/src/util.c
@@ -316,7 +316,7 @@ void *whine_malloc(size_t size)
return ret;
}
-int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
+int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2)
{
if (s1->sa.sa_family == s2->sa.sa_family)
{
--
1.8.3.1
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/liuyumeng1/dnsmasq.git
git@gitee.com:liuyumeng1/dnsmasq.git
liuyumeng1
dnsmasq
dnsmasq
master

搜索帮助