mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
Merge branch 'random-port'
This commit is contained in:
22
CHANGELOG
22
CHANGELOG
@@ -27,6 +27,28 @@ version 2.85
|
|||||||
|
|
||||||
Teach --bogus-nxdomain and --ignore-address to take an IPv4 subnet.
|
Teach --bogus-nxdomain and --ignore-address to take an IPv4 subnet.
|
||||||
|
|
||||||
|
Use random source ports where possible if source
|
||||||
|
addresses/interfaces in use.
|
||||||
|
CVE-2021-3448 applies. Thanks to Petr Menšík for spotting this.
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
version 2.84
|
version 2.84
|
||||||
Fix a problem, introduced in 2.83, which could see DNS replies
|
Fix a problem, introduced in 2.83, which could see DNS replies
|
||||||
|
|||||||
@@ -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
|
or domain parts, to upstream nameservers. If the name is not known
|
||||||
from /etc/hosts or DHCP then a "not found" answer is returned.
|
from /etc/hosts or DHCP then a "not found" answer is returned.
|
||||||
.TP
|
.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
|
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
|
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
|
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
|
part of the source address. Forcing queries to an interface is not
|
||||||
implemented on all platforms supported by dnsmasq.
|
implemented on all platforms supported by dnsmasq.
|
||||||
.TP
|
.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
|
This is functionally the same as
|
||||||
.B --server,
|
.B --server,
|
||||||
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
|
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
|
||||||
|
|||||||
@@ -1672,6 +1672,7 @@ static int set_dns_listeners(time_t now)
|
|||||||
{
|
{
|
||||||
struct serverfd *serverfdp;
|
struct serverfd *serverfdp;
|
||||||
struct listener *listener;
|
struct listener *listener;
|
||||||
|
struct randfd_list *rfl;
|
||||||
int wait = 0, i;
|
int wait = 0, i;
|
||||||
|
|
||||||
#ifdef HAVE_TFTP
|
#ifdef HAVE_TFTP
|
||||||
@@ -1692,10 +1693,13 @@ static int set_dns_listeners(time_t now)
|
|||||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||||
poll_listen(serverfdp->fd, POLLIN);
|
poll_listen(serverfdp->fd, POLLIN);
|
||||||
|
|
||||||
if (daemon->port != 0 && !daemon->osport)
|
for (i = 0; i < RANDOM_SOCKS; i++)
|
||||||
for (i = 0; i < RANDOM_SOCKS; i++)
|
if (daemon->randomsocks[i].refcount != 0)
|
||||||
if (daemon->randomsocks[i].refcount != 0)
|
poll_listen(daemon->randomsocks[i].fd, POLLIN);
|
||||||
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)
|
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||||
{
|
{
|
||||||
@@ -1733,18 +1737,23 @@ static void check_dns_listeners(time_t now)
|
|||||||
{
|
{
|
||||||
struct serverfd *serverfdp;
|
struct serverfd *serverfdp;
|
||||||
struct listener *listener;
|
struct listener *listener;
|
||||||
|
struct randfd_list *rfl;
|
||||||
int i;
|
int i;
|
||||||
int pipefd[2];
|
int pipefd[2];
|
||||||
|
|
||||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||||
if (poll_check(serverfdp->fd, POLLIN))
|
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++)
|
||||||
for (i = 0; i < RANDOM_SOCKS; i++)
|
if (daemon->randomsocks[i].refcount != 0 &&
|
||||||
if (daemon->randomsocks[i].refcount != 0 &&
|
poll_check(daemon->randomsocks[i].fd, POLLIN))
|
||||||
poll_check(daemon->randomsocks[i].fd, POLLIN))
|
reply_query(daemon->randomsocks[i].fd, now);
|
||||||
reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, 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
|
/* 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
|
pipe, or vice versa. Therefore send tcp_pids to zero when we wait() the
|
||||||
|
|||||||
@@ -549,13 +549,20 @@ struct serverfd {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct randfd {
|
struct randfd {
|
||||||
|
struct server *serv;
|
||||||
int fd;
|
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 {
|
struct server {
|
||||||
union mysockaddr addr, source_addr;
|
union mysockaddr addr, source_addr;
|
||||||
char interface[IF_NAMESIZE+1];
|
char interface[IF_NAMESIZE+1];
|
||||||
|
unsigned int ifindex; /* corresponding to interface, above */
|
||||||
struct serverfd *sfd;
|
struct serverfd *sfd;
|
||||||
char *domain; /* set if this server only handles a domain. */
|
char *domain; /* set if this server only handles a domain. */
|
||||||
int flags, tcpfd, edns_pktsz;
|
int flags, tcpfd, edns_pktsz;
|
||||||
@@ -679,8 +686,7 @@ struct frec {
|
|||||||
struct frec_src *next;
|
struct frec_src *next;
|
||||||
} frec_src;
|
} frec_src;
|
||||||
struct server *sentto; /* NULL means free */
|
struct server *sentto; /* NULL means free */
|
||||||
struct randfd *rfd4;
|
struct randfd_list *rfds;
|
||||||
struct randfd *rfd6;
|
|
||||||
unsigned short new_id;
|
unsigned short new_id;
|
||||||
int forwardall, flags;
|
int forwardall, flags;
|
||||||
time_t time;
|
time_t time;
|
||||||
@@ -1120,11 +1126,12 @@ extern struct daemon {
|
|||||||
int forwardcount;
|
int forwardcount;
|
||||||
struct server *srv_save; /* Used for resend on DoD */
|
struct server *srv_save; /* Used for resend on DoD */
|
||||||
size_t packet_len; /* " " */
|
size_t packet_len; /* " " */
|
||||||
struct randfd *rfd_save; /* " " */
|
int fd_save; /* " " */
|
||||||
pid_t tcp_pids[MAX_PROCS];
|
pid_t tcp_pids[MAX_PROCS];
|
||||||
int tcp_pipes[MAX_PROCS];
|
int tcp_pipes[MAX_PROCS];
|
||||||
int pipe_to_parent;
|
int pipe_to_parent;
|
||||||
struct randfd randomsocks[RANDOM_SOCKS];
|
struct randfd randomsocks[RANDOM_SOCKS];
|
||||||
|
struct randfd_list *rfl_spare, *rfl_poll;
|
||||||
int v6pktinfo;
|
int v6pktinfo;
|
||||||
struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */
|
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 */
|
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 safe_pipe(int *fd, int read_noblock);
|
||||||
void *whine_malloc(size_t size);
|
void *whine_malloc(size_t size);
|
||||||
int sa_len(union mysockaddr *addr);
|
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_isequal(const char *a, const char *b);
|
||||||
int hostname_issubdomain(char *a, char *b);
|
int hostname_issubdomain(char *a, char *b);
|
||||||
time_t dnsmasq_time(void);
|
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);
|
int option_read_dynfile(char *file, int flags);
|
||||||
|
|
||||||
/* forward.c */
|
/* 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);
|
void receive_query(struct listener *listen, time_t now);
|
||||||
unsigned char *tcp_request(int confd, time_t now,
|
unsigned char *tcp_request(int confd, time_t now,
|
||||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
||||||
@@ -1357,13 +1364,12 @@ int send_from(int fd, int nowild, char *packet, size_t len,
|
|||||||
union mysockaddr *to, union all_addr *source,
|
union mysockaddr *to, union all_addr *source,
|
||||||
unsigned int iface);
|
unsigned int iface);
|
||||||
void resend_query(void);
|
void resend_query(void);
|
||||||
struct randfd *allocate_rfd(int family);
|
int allocate_rfd(struct randfd_list **fdlp, struct server *serv);
|
||||||
void free_rfd(struct randfd *rfd);
|
void free_rfds(struct randfd_list **fdlp);
|
||||||
|
|
||||||
/* network.c */
|
/* network.c */
|
||||||
int indextoname(int fd, int index, char *name);
|
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 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);
|
void pre_allocate_sfds(void);
|
||||||
int reload_servers(char *fname);
|
int reload_servers(char *fname);
|
||||||
void mark_servers(int flag);
|
void mark_servers(int flag);
|
||||||
|
|||||||
368
src/forward.c
368
src/forward.c
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
#include "dnsmasq.h"
|
#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 struct frec *lookup_frec_by_query(void *hash, unsigned int flags);
|
||||||
|
|
||||||
static unsigned short get_id(void);
|
static unsigned short get_id(void);
|
||||||
@@ -344,25 +344,17 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
|
if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
|
||||||
PUTSHORT(SAFE_PKTSZ, pheader);
|
PUTSHORT(SAFE_PKTSZ, pheader);
|
||||||
|
|
||||||
if (forward->sentto->addr.sa.sa_family == AF_INET)
|
if ((fd = allocate_rfd(&forward->rfds, forward->sentto)) != -1)
|
||||||
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 (forward->sentto->addr.sa.sa_family == AF_INET6)
|
if (forward->sentto->addr.sa.sa_family == AF_INET)
|
||||||
fd = forward->rfd6->fd;
|
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (union all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
|
||||||
else
|
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,
|
while (retry_send(sendto(fd, (char *)header, plen, 0,
|
||||||
&forward->sentto->addr.sa,
|
&forward->sentto->addr.sa,
|
||||||
sa_len(&forward->sentto->addr))));
|
sa_len(&forward->sentto->addr))));
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -508,48 +500,27 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
/* only send to servers dealing with our domain.
|
/* only send to servers dealing with our domain.
|
||||||
domain may be NULL, in which case server->domain
|
domain may be NULL, in which case server->domain
|
||||||
must be NULL also. */
|
must be NULL also. */
|
||||||
|
|
||||||
if (type == (start->flags & SERV_TYPE) &&
|
if (type == (start->flags & SERV_TYPE) &&
|
||||||
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
|
(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
|
#ifdef HAVE_CONNTRACK
|
||||||
/* Copy connection mark of incoming query to outgoing connection. */
|
/* Copy connection mark of incoming query to outgoing connection. */
|
||||||
if (option_bool(OPT_CONNTRACK))
|
if (option_bool(OPT_CONNTRACK))
|
||||||
{
|
{
|
||||||
unsigned int mark;
|
unsigned int mark;
|
||||||
if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark))
|
if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark))
|
||||||
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
|
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER))
|
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 */
|
/* Keep info in case we want to re-send this packet */
|
||||||
daemon->srv_save = start;
|
daemon->srv_save = start;
|
||||||
daemon->packet_len = plen;
|
daemon->packet_len = plen;
|
||||||
|
daemon->fd_save = fd;
|
||||||
|
|
||||||
if (!gotname)
|
if (!gotname)
|
||||||
strcpy(daemon->namebuff, "query");
|
strcpy(daemon->namebuff, "query");
|
||||||
@@ -812,7 +784,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* sets new last_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
|
/* packet from peer server, extract data for cache, and send to
|
||||||
original requester */
|
original requester */
|
||||||
@@ -829,7 +801,7 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
daemon->srv_save = NULL;
|
daemon->srv_save = NULL;
|
||||||
|
|
||||||
/* Determine the address of the server replying so that we can mark that as good */
|
/* 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;
|
serveraddr.in6.sin6_flowinfo = 0;
|
||||||
|
|
||||||
header = (struct dns_header *)daemon->packet;
|
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);
|
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;
|
return;
|
||||||
|
|
||||||
#ifdef HAVE_DUMPFILE
|
#ifdef HAVE_DUMPFILE
|
||||||
@@ -906,28 +878,7 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fd = -1;
|
if ((fd = allocate_rfd(&forward->rfds, start)) == -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)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef HAVE_DUMPFILE
|
#ifdef HAVE_DUMPFILE
|
||||||
@@ -1136,8 +1087,7 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
}
|
}
|
||||||
|
|
||||||
new->sentto = server;
|
new->sentto = server;
|
||||||
new->rfd4 = NULL;
|
new->rfds = NULL;
|
||||||
new->rfd6 = NULL;
|
|
||||||
new->frec_src.next = NULL;
|
new->frec_src.next = NULL;
|
||||||
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA);
|
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA);
|
||||||
new->forwardall = 0;
|
new->forwardall = 0;
|
||||||
@@ -1176,24 +1126,7 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
/* Don't resend this. */
|
/* Don't resend this. */
|
||||||
daemon->srv_save = NULL;
|
daemon->srv_save = NULL;
|
||||||
|
|
||||||
if (server->sfd)
|
if ((fd = allocate_rfd(&new->rfds, server)) != -1)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
#ifdef HAVE_CONNTRACK
|
#ifdef HAVE_CONNTRACK
|
||||||
/* Copy connection mark of incoming query to outgoing connection. */
|
/* Copy connection mark of incoming query to outgoing connection. */
|
||||||
@@ -2228,9 +2161,8 @@ static struct frec *allocate_frec(time_t now)
|
|||||||
f->next = daemon->frec_list;
|
f->next = daemon->frec_list;
|
||||||
f->time = now;
|
f->time = now;
|
||||||
f->sentto = NULL;
|
f->sentto = NULL;
|
||||||
f->rfd4 = NULL;
|
f->rfds = NULL;
|
||||||
f->flags = 0;
|
f->flags = 0;
|
||||||
f->rfd6 = NULL;
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
f->dependent = NULL;
|
f->dependent = NULL;
|
||||||
f->blocking_query = NULL;
|
f->blocking_query = NULL;
|
||||||
@@ -2242,46 +2174,192 @@ static struct frec *allocate_frec(time_t now)
|
|||||||
return f;
|
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)
|
||||||
|
{
|
||||||
|
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;
|
static int finger = 0;
|
||||||
int i;
|
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
|
/* limit the number of sockets we have open to avoid starvation of
|
||||||
(eg) TFTP. Once we have a reasonable number, randomness should be OK */
|
(eg) TFTP. Once we have a reasonable number, randomness should be OK */
|
||||||
|
|
||||||
for (i = 0; i < RANDOM_SOCKS; i++)
|
for (i = 0; i < RANDOM_SOCKS; i++)
|
||||||
if (daemon->randomsocks[i].refcount == 0)
|
if (daemon->randomsocks[i].refcount == 0)
|
||||||
{
|
{
|
||||||
if ((daemon->randomsocks[i].fd = random_sock(family)) == -1)
|
if ((fd = random_sock(serv)) != -1)
|
||||||
break;
|
{
|
||||||
|
rfd = &daemon->randomsocks[i];
|
||||||
daemon->randomsocks[i].refcount = 1;
|
rfd->serv = serv;
|
||||||
daemon->randomsocks[i].family = family;
|
rfd->fd = fd;
|
||||||
return &daemon->randomsocks[i];
|
rfd->refcount = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* No free ones or cannot get new socket, grab an existing one */
|
/* 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;
|
struct randfd_list *rfl_poll;
|
||||||
if (daemon->randomsocks[j].refcount != 0 &&
|
|
||||||
daemon->randomsocks[j].family == family &&
|
/* there are no free slots, and non with the same parameters we can piggy-back on.
|
||||||
daemon->randomsocks[j].refcount != 0xffff)
|
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++;
|
/* Don't leak anything we may already have */
|
||||||
return &daemon->randomsocks[j];
|
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 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL; /* doom */
|
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)
|
struct randfd_list *tmp, *rfl, *poll, *next, **up;
|
||||||
close(rfd->fd);
|
|
||||||
|
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)
|
static void free_frec(struct frec *f)
|
||||||
@@ -2297,12 +2375,9 @@ static void free_frec(struct frec *f)
|
|||||||
}
|
}
|
||||||
|
|
||||||
f->frec_src.next = NULL;
|
f->frec_src.next = NULL;
|
||||||
free_rfd(f->rfd4);
|
free_rfds(&f->rfds);
|
||||||
f->rfd4 = NULL;
|
|
||||||
f->sentto = NULL;
|
f->sentto = NULL;
|
||||||
f->flags = 0;
|
f->flags = 0;
|
||||||
free_rfd(f->rfd6);
|
|
||||||
f->rfd6 = NULL;
|
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
if (f->stash)
|
if (f->stash)
|
||||||
@@ -2409,24 +2484,37 @@ struct frec *get_new_frec(time_t now, int *wait, struct frec *force)
|
|||||||
return f; /* OK if malloc fails and this is NULL */
|
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 frec *f;
|
||||||
|
struct server *s;
|
||||||
|
int type;
|
||||||
|
struct randfd_list *fdl;
|
||||||
|
|
||||||
for(f = daemon->frec_list; f; f = f->next)
|
for(f = daemon->frec_list; f; f = f->next)
|
||||||
if (f->sentto && f->new_id == id &&
|
if (f->sentto && f->new_id == id &&
|
||||||
(memcmp(hash, f->hash, HASH_SIZE) == 0))
|
(memcmp(hash, f->hash, HASH_SIZE) == 0))
|
||||||
{
|
{
|
||||||
/* sent from random port */
|
/* sent from random port */
|
||||||
if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd)
|
for (fdl = f->rfds; fdl; fdl = fdl->next)
|
||||||
|
if (fdl->rfd->fd == fd)
|
||||||
return f;
|
return f;
|
||||||
|
|
||||||
if (family == AF_INET6 && f->rfd6 && f->rfd6->fd == fd)
|
/* Sent to upstream from socket associated with a server.
|
||||||
return f;
|
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;
|
||||||
|
|
||||||
/* sent to upstream from bound socket. */
|
s = s->next ? s->next : daemon->servers;
|
||||||
if (f->sentto->sfd && f->sentto->sfd->fd == fd)
|
} while (s != f->sentto);
|
||||||
return f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -2458,31 +2546,27 @@ static struct frec *lookup_frec_by_query(void *hash, unsigned int flags)
|
|||||||
void resend_query()
|
void resend_query()
|
||||||
{
|
{
|
||||||
if (daemon->srv_save)
|
if (daemon->srv_save)
|
||||||
{
|
while(retry_send(sendto(daemon->fd_save, daemon->packet, daemon->packet_len, 0,
|
||||||
int fd;
|
&daemon->srv_save->addr.sa,
|
||||||
|
sa_len(&daemon->srv_save->addr))));
|
||||||
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))));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A server record is going away, remove references to it */
|
/* A server record is going away, remove references to it */
|
||||||
void server_gone(struct server *server)
|
void server_gone(struct server *server)
|
||||||
{
|
{
|
||||||
struct frec *f;
|
struct frec *f;
|
||||||
|
int i;
|
||||||
|
|
||||||
for (f = daemon->frec_list; f; f = f->next)
|
for (f = daemon->frec_list; f; f = f->next)
|
||||||
if (f->sentto && f->sentto == server)
|
if (f->sentto && f->sentto == server)
|
||||||
free_frec(f);
|
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)
|
if (daemon->last_server == server)
|
||||||
daemon->last_server = NULL;
|
daemon->last_server = NULL;
|
||||||
|
|
||||||
|
|||||||
16
src/loop.c
16
src/loop.c
@@ -22,6 +22,7 @@ static ssize_t loop_make_probe(u32 uid);
|
|||||||
void loop_send_probes()
|
void loop_send_probes()
|
||||||
{
|
{
|
||||||
struct server *serv;
|
struct server *serv;
|
||||||
|
struct randfd_list *rfds = NULL;
|
||||||
|
|
||||||
if (!option_bool(OPT_LOOP_DETECT))
|
if (!option_bool(OPT_LOOP_DETECT))
|
||||||
return;
|
return;
|
||||||
@@ -34,22 +35,15 @@ void loop_send_probes()
|
|||||||
{
|
{
|
||||||
ssize_t len = loop_make_probe(serv->uid);
|
ssize_t len = loop_make_probe(serv->uid);
|
||||||
int fd;
|
int fd;
|
||||||
struct randfd *rfd = NULL;
|
|
||||||
|
|
||||||
if (serv->sfd)
|
if ((fd = allocate_rfd(&rfds, serv)) == -1)
|
||||||
fd = serv->sfd->fd;
|
continue;
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!(rfd = allocate_rfd(serv->addr.sa.sa_family)))
|
|
||||||
continue;
|
|
||||||
fd = rfd->fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (retry_send(sendto(fd, daemon->packet, len, 0,
|
while (retry_send(sendto(fd, daemon->packet, len, 0,
|
||||||
&serv->addr.sa, sa_len(&serv->addr))));
|
&serv->addr.sa, sa_len(&serv->addr))));
|
||||||
|
|
||||||
free_rfd(rfd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free_rfds(&rfds);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t loop_make_probe(u32 uid)
|
static ssize_t loop_make_probe(u32 uid)
|
||||||
|
|||||||
100
src/network.c
100
src/network.c
@@ -696,6 +696,7 @@ int enumerate_interfaces(int reset)
|
|||||||
#ifdef HAVE_AUTH
|
#ifdef HAVE_AUTH
|
||||||
struct auth_zone *zone;
|
struct auth_zone *zone;
|
||||||
#endif
|
#endif
|
||||||
|
struct server *serv;
|
||||||
|
|
||||||
/* Do this max once per select cycle - also inhibits netlink socket use
|
/* Do this max once per select cycle - also inhibits netlink socket use
|
||||||
in TCP child processes. */
|
in TCP child processes. */
|
||||||
@@ -714,6 +715,20 @@ int enumerate_interfaces(int reset)
|
|||||||
if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
|
if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
|
||||||
return 0;
|
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:
|
again:
|
||||||
/* Mark interfaces for garbage collection */
|
/* Mark interfaces for garbage collection */
|
||||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||||
@@ -1287,59 +1302,6 @@ void join_multicast(int dienow)
|
|||||||
}
|
}
|
||||||
#endif
|
#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)
|
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp)
|
||||||
{
|
{
|
||||||
union mysockaddr addr_copy = *addr;
|
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. */
|
/* cannot set source _port_ for TCP connections. */
|
||||||
if (is_tcp)
|
if (is_tcp)
|
||||||
port = 0;
|
port = 0;
|
||||||
|
else if (port == 0)
|
||||||
/* Bind a random port within the range given by min-port and max-port */
|
|
||||||
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;
|
tries = ports_avail < 30 ? 3 * ports_avail : 100;
|
||||||
port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
|
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;
|
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;
|
struct serverfd *sfd;
|
||||||
unsigned int ifindex = 0;
|
|
||||||
int errsave;
|
int errsave;
|
||||||
int opt = 1;
|
int opt = 1;
|
||||||
|
|
||||||
/* when using random ports, servers which would otherwise use
|
/* when using random ports, servers which would otherwise use
|
||||||
the INADDR_ANY/port0 socket have sfd set to NULL */
|
the INADDR_ANY/port0 socket have sfd set to NULL, this is
|
||||||
if (!daemon->osport && intname[0] == 0)
|
anything without an explictly set source port. */
|
||||||
|
if (!daemon->osport)
|
||||||
{
|
{
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
|
||||||
if (addr->sa.sa_family == AF_INET &&
|
if (addr->sa.sa_family == AF_INET &&
|
||||||
addr->in.sin_addr.s_addr == INADDR_ANY &&
|
|
||||||
addr->in.sin_port == htons(0))
|
addr->in.sin_port == htons(0))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (addr->sa.sa_family == AF_INET6 &&
|
if (addr->sa.sa_family == AF_INET6 &&
|
||||||
memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&
|
|
||||||
addr->in6.sin6_port == htons(0))
|
addr->in6.sin6_port == htons(0))
|
||||||
return NULL;
|
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 */
|
/* may have a suitable one already */
|
||||||
for (sfd = daemon->sfds; sfd; sfd = sfd->next )
|
for (sfd = daemon->sfds; sfd; sfd = sfd->next )
|
||||||
if (sockaddr_isequal(&sfd->source_addr, addr) &&
|
if (ifindex == sfd->ifindex &&
|
||||||
strcmp(intname, sfd->interface) == 0 &&
|
sockaddr_isequal(&sfd->source_addr, addr) &&
|
||||||
ifindex == sfd->ifindex)
|
strcmp(intname, sfd->interface) == 0)
|
||||||
return sfd;
|
return sfd;
|
||||||
|
|
||||||
/* need to make a new one. */
|
/* need to make a new one. */
|
||||||
@@ -1495,7 +1451,7 @@ void pre_allocate_sfds(void)
|
|||||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||||
addr.in.sin_len = sizeof(struct sockaddr_in);
|
addr.in.sin_len = sizeof(struct sockaddr_in);
|
||||||
#endif
|
#endif
|
||||||
if ((sfd = allocate_sfd(&addr, "")))
|
if ((sfd = allocate_sfd(&addr, "", 0)))
|
||||||
sfd->preallocated = 1;
|
sfd->preallocated = 1;
|
||||||
|
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
@@ -1505,13 +1461,13 @@ void pre_allocate_sfds(void)
|
|||||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||||
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
|
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
|
||||||
#endif
|
#endif
|
||||||
if ((sfd = allocate_sfd(&addr, "")))
|
if ((sfd = allocate_sfd(&addr, "", 0)))
|
||||||
sfd->preallocated = 1;
|
sfd->preallocated = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (srv = daemon->servers; srv; srv = srv->next)
|
for (srv = daemon->servers; srv; srv = srv->next)
|
||||||
if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
|
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 &&
|
errno != 0 &&
|
||||||
option_bool(OPT_NOWILD))
|
option_bool(OPT_NOWILD))
|
||||||
{
|
{
|
||||||
@@ -1720,7 +1676,7 @@ void check_servers(void)
|
|||||||
|
|
||||||
/* Do we need a socket set? */
|
/* Do we need a socket set? */
|
||||||
if (!serv->sfd &&
|
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)
|
errno != 0)
|
||||||
{
|
{
|
||||||
my_syslog(LOG_WARNING,
|
my_syslog(LOG_WARNING,
|
||||||
|
|||||||
@@ -819,7 +819,8 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
|
|||||||
if (interface_opt)
|
if (interface_opt)
|
||||||
{
|
{
|
||||||
#if defined(SO_BINDTODEVICE)
|
#if defined(SO_BINDTODEVICE)
|
||||||
safe_strncpy(interface, interface_opt, IF_NAMESIZE);
|
safe_strncpy(interface, source, IF_NAMESIZE);
|
||||||
|
source = interface_opt;
|
||||||
#else
|
#else
|
||||||
return _("interface binding not supported");
|
return _("interface binding not supported");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ void *whine_malloc(size_t size)
|
|||||||
return ret;
|
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)
|
if (s1->sa.sa_family == s2->sa.sa_family)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user