mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-03 13:38:32 +00:00
Compare commits
11
Commits
v2.91test5
...
v2.91test7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da58455508 | ||
|
|
b915c9a661 | ||
|
|
424aaa0f9d | ||
|
|
c72c895869 | ||
|
|
b7156116c2 | ||
|
|
7d915a0bb9 | ||
|
|
509afcd1d2 | ||
|
|
51343bd9a2 | ||
|
|
b58276a73c | ||
|
|
f162d344c0 | ||
|
|
0003db15cb |
No files matched your search
+1
-1
@@ -479,7 +479,7 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s
|
||||
if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
|
||||
{
|
||||
int rrmatch = 0;
|
||||
if (crecp->flags & flags & F_RR)
|
||||
if (addr && (crecp->flags & flags & F_RR))
|
||||
{
|
||||
unsigned short rrc = (crecp->flags & F_KEYTAG) ? crecp->addr.rrblock.rrtype : crecp->addr.rrdata.rrtype;
|
||||
unsigned short rra = (flags & F_KEYTAG) ? addr->rrblock.rrtype : addr->rrdata.rrtype;
|
||||
|
||||
+278
-222
@@ -32,6 +32,7 @@ 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 do_tcp_connection(struct listener *listener, time_t now, int slot);
|
||||
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);
|
||||
@@ -961,7 +962,14 @@ int main (int argc, char **argv)
|
||||
my_syslog(LOG_WARNING, _("warning: ignoring resolv-file flag because no-resolv is set"));
|
||||
daemon->resolv_files = NULL;
|
||||
if (!daemon->servers)
|
||||
my_syslog(LOG_WARNING, _("warning: no upstream servers configured"));
|
||||
{
|
||||
#ifdef HAVE_DBUS
|
||||
if (option_bool(OPT_DBUS))
|
||||
my_syslog(LOG_INFO, _("no upstream servers configured - please set them from DBus"));
|
||||
else
|
||||
#endif
|
||||
my_syslog(LOG_WARNING, _("warning: no upstream servers configured"));
|
||||
}
|
||||
}
|
||||
|
||||
if (daemon->max_logs != 0)
|
||||
@@ -1476,7 +1484,7 @@ static void async_event(int pipe, time_t now)
|
||||
{
|
||||
pid_t p;
|
||||
struct event_desc ev;
|
||||
int i, check = 0;
|
||||
int wstatus, i, check = 0;
|
||||
char *msg;
|
||||
|
||||
/* NOTE: the memory used to return msg is leaked: use msgs in events only
|
||||
@@ -1539,7 +1547,7 @@ static void async_event(int pipe, time_t now)
|
||||
|
||||
case EVENT_CHILD:
|
||||
/* See Stevens 5.10 */
|
||||
while ((p = waitpid(-1, NULL, WNOHANG)) != 0)
|
||||
while ((p = waitpid(-1, &wstatus, WNOHANG)) != 0)
|
||||
if (p == -1)
|
||||
{
|
||||
if (errno != EINTR)
|
||||
@@ -1550,6 +1558,20 @@ static void async_event(int pipe, time_t now)
|
||||
if (daemon->tcp_pids[i] == p)
|
||||
{
|
||||
daemon->tcp_pids[i] = 0;
|
||||
|
||||
if (!WIFEXITED(wstatus))
|
||||
{
|
||||
/* If a helper process dies, (eg with SIGSEV)
|
||||
log that and attempt to patch things up so that the
|
||||
parent can continue to function. */
|
||||
my_syslog(LOG_WARNING, _("TCP helper process %u died unexpectedly"), (unsigned int)p);
|
||||
if (daemon->tcp_pipes[i] != -1)
|
||||
{
|
||||
close(daemon->tcp_pipes[i]);
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
|
||||
if (daemon->tcp_pipes[i] == -1)
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
|
||||
@@ -1832,247 +1854,281 @@ static void check_dns_listeners(time_t now)
|
||||
struct listener *listener;
|
||||
struct randfd_list *rfl;
|
||||
int i;
|
||||
int pipefd[2];
|
||||
|
||||
/* Note that handling events here can create or destroy fds and
|
||||
render the result of the last poll() call invalid. Once
|
||||
we find an fd that needs service, do it, then return to go around the
|
||||
poll() loop again. This avoid really, really, wierd bugs. */
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
for (i = 0; i < daemon->max_procs; i++)
|
||||
if (daemon->tcp_pipes[i] != -1 &&
|
||||
poll_check(daemon->tcp_pipes[i], POLLIN | POLLHUP))
|
||||
{
|
||||
/* 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
|
||||
process, and tcp_pipes to -1 and close the FD when we read the last
|
||||
of the data - indicated by cache_recv_insert returning zero.
|
||||
The order of these events is indeterminate, and both are needed
|
||||
to free the process slot. Once the child process has gone, poll()
|
||||
returns POLLHUP, not POLLIN, so have to check for both here. */
|
||||
if (!cache_recv_insert(now, daemon->tcp_pipes[i]))
|
||||
{
|
||||
close(daemon->tcp_pipes[i]);
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
|
||||
if (daemon->tcp_pids[i] == 0)
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
if (poll_check(serverfdp->fd, POLLIN))
|
||||
reply_query(serverfdp->fd, now);
|
||||
{
|
||||
reply_query(serverfdp->fd, now);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < daemon->numrrand; i++)
|
||||
if (daemon->randomsocks[i].refcount != 0 &&
|
||||
poll_check(daemon->randomsocks[i].fd, POLLIN))
|
||||
reply_query(daemon->randomsocks[i].fd, now);
|
||||
|
||||
{
|
||||
reply_query(daemon->randomsocks[i].fd, now);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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
|
||||
process, and tcp_pipes to -1 and close the FD when we read the last
|
||||
of the data - indicated by cache_recv_insert returning zero.
|
||||
The order of these events is indeterminate, and both are needed
|
||||
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 < 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]))
|
||||
{
|
||||
close(daemon->tcp_pipes[i]);
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
|
||||
if (daemon->tcp_pids[i] == 0)
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
|
||||
}
|
||||
|
||||
{
|
||||
reply_query(rfl->rfd->fd, now);
|
||||
return;
|
||||
}
|
||||
|
||||
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||
{
|
||||
if (listener->fd != -1 && poll_check(listener->fd, POLLIN))
|
||||
if (listener->fd != -1 && poll_check(listener->fd, POLLIN))
|
||||
{
|
||||
receive_query(listener, now);
|
||||
|
||||
/* 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 = daemon->max_procs - 1; i >= 0; i--)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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 = daemon->max_procs - 1; i >= 0; i--)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
break;
|
||||
|
||||
if (listener->tcpfd != -1 && i >= 0 && poll_check(listener->tcpfd, POLLIN))
|
||||
if (i >= 0)
|
||||
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||
if (listener->tcpfd != -1 && poll_check(listener->tcpfd, POLLIN))
|
||||
{
|
||||
int confd, client_ok = 1;
|
||||
struct irec *iface = NULL;
|
||||
pid_t p;
|
||||
union mysockaddr tcp_addr;
|
||||
socklen_t tcp_len = sizeof(union mysockaddr);
|
||||
do_tcp_connection(listener, now, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
|
||||
|
||||
if (confd == -1)
|
||||
continue;
|
||||
|
||||
if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
|
||||
{
|
||||
close(confd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Make sure that the interface list is up-to-date.
|
||||
|
||||
We do this here as we may need the results below, and
|
||||
the DNS code needs them for --interface-name stuff.
|
||||
static void do_tcp_connection(struct listener *listener, time_t now, int slot)
|
||||
{
|
||||
int confd, client_ok = 1;
|
||||
struct irec *iface = NULL;
|
||||
pid_t p;
|
||||
union mysockaddr tcp_addr;
|
||||
socklen_t tcp_len = sizeof(union mysockaddr);
|
||||
unsigned char a = 0, *buff;
|
||||
struct server *s;
|
||||
int flags, auth_dns;
|
||||
struct in_addr netmask;
|
||||
int pipefd[2];
|
||||
|
||||
Multiple calls to enumerate_interfaces() per select loop are
|
||||
inhibited, so calls to it in the child process (which doesn't select())
|
||||
have no effect. This avoids two processes reading from the same
|
||||
netlink fd and screwing the pooch entirely.
|
||||
*/
|
||||
|
||||
enumerate_interfaces(0);
|
||||
while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
|
||||
|
||||
if (confd == -1)
|
||||
return;
|
||||
|
||||
if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
|
||||
{
|
||||
closeconandreturn:
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make sure that the interface list is up-to-date.
|
||||
|
||||
We do this here as we may need the results below, and
|
||||
the DNS code needs them for --interface-name stuff.
|
||||
|
||||
Multiple calls to enumerate_interfaces() per select loop are
|
||||
inhibited, so calls to it in the child process (which doesn't select())
|
||||
have no effect. This avoids two processes reading from the same
|
||||
netlink fd and screwing the pooch entirely.
|
||||
*/
|
||||
|
||||
enumerate_interfaces(0);
|
||||
|
||||
if (option_bool(OPT_NOWILD))
|
||||
iface = listener->iface; /* May be NULL */
|
||||
else
|
||||
{
|
||||
int if_index;
|
||||
char intr_name[IF_NAMESIZE];
|
||||
|
||||
/* if we can find the arrival interface, check it's one that's allowed */
|
||||
if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
|
||||
indextoname(listener->tcpfd, if_index, intr_name))
|
||||
{
|
||||
union all_addr addr;
|
||||
|
||||
if (option_bool(OPT_NOWILD))
|
||||
iface = listener->iface; /* May be NULL */
|
||||
else
|
||||
{
|
||||
int if_index;
|
||||
char intr_name[IF_NAMESIZE];
|
||||
|
||||
/* if we can find the arrival interface, check it's one that's allowed */
|
||||
if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
|
||||
indextoname(listener->tcpfd, if_index, intr_name))
|
||||
{
|
||||
union all_addr addr;
|
||||
|
||||
if (tcp_addr.sa.sa_family == AF_INET6)
|
||||
addr.addr6 = tcp_addr.in6.sin6_addr;
|
||||
else
|
||||
addr.addr4 = tcp_addr.in.sin_addr;
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (iface->index == if_index &&
|
||||
iface->addr.sa.sa_family == tcp_addr.sa.sa_family)
|
||||
break;
|
||||
|
||||
if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
|
||||
client_ok = 0;
|
||||
}
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
iface = listener->iface; /* May be NULL */
|
||||
else
|
||||
{
|
||||
/* Check for allowed interfaces when binding the wildcard address:
|
||||
we do this by looking for an interface with the same address as
|
||||
the local address of the TCP connection, then looking to see if that's
|
||||
an allowed interface. As a side effect, we get the netmask of the
|
||||
interface too, for localisation. */
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&iface->addr, &tcp_addr))
|
||||
break;
|
||||
|
||||
if (!iface)
|
||||
client_ok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!client_ok)
|
||||
{
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
}
|
||||
else if (!option_bool(OPT_DEBUG) && pipe(pipefd) == 0 && (p = fork()) != 0)
|
||||
{
|
||||
close(pipefd[1]); /* parent needs read pipe end. */
|
||||
if (p == -1)
|
||||
close(pipefd[0]);
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* The child process inherits the netlink socket,
|
||||
which it never uses, but when the parent (us)
|
||||
uses it in the future, the answer may go to the
|
||||
child, resulting in the parent blocking
|
||||
forever awaiting the result. To avoid this
|
||||
the child closes the netlink socket, but there's
|
||||
a nasty race, since the parent may use netlink
|
||||
before the child has done the close.
|
||||
|
||||
To avoid this, the parent blocks here until a
|
||||
single byte comes back up the pipe, which
|
||||
is sent by the child after it has closed the
|
||||
netlink socket. */
|
||||
|
||||
unsigned char a;
|
||||
read_write(pipefd[0], &a, 1, RW_READ);
|
||||
#endif
|
||||
|
||||
/* i holds index of free slot */
|
||||
daemon->tcp_pids[i] = p;
|
||||
daemon->tcp_pipes[i] = pipefd[0];
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]++;
|
||||
if (daemon->metrics[METRIC_TCP_CONNECTIONS] > daemon->max_procs_used)
|
||||
daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS];
|
||||
}
|
||||
close(confd);
|
||||
|
||||
/* The child can use up to TCP_MAX_QUERIES ids, so skip that many. */
|
||||
daemon->log_id += TCP_MAX_QUERIES;
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* It can do more if making DNSSEC queries too. */
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
daemon->log_id += daemon->limit[LIMIT_WORK];
|
||||
#endif
|
||||
}
|
||||
if (tcp_addr.sa.sa_family == AF_INET6)
|
||||
addr.addr6 = tcp_addr.in6.sin6_addr;
|
||||
else
|
||||
{
|
||||
unsigned char *buff;
|
||||
struct server *s;
|
||||
int flags;
|
||||
struct in_addr netmask;
|
||||
int auth_dns;
|
||||
|
||||
if (iface)
|
||||
{
|
||||
netmask = iface->netmask;
|
||||
auth_dns = iface->dns_auth;
|
||||
}
|
||||
else
|
||||
{
|
||||
netmask.s_addr = 0;
|
||||
auth_dns = 0;
|
||||
}
|
||||
|
||||
/* Arrange for SIGALRM after CHILD_LIFETIME seconds to
|
||||
terminate the process. */
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* See comment above re: netlink socket. */
|
||||
unsigned char a = 0;
|
||||
|
||||
close(daemon->netlinkfd);
|
||||
read_write(pipefd[1], &a, 1, RW_WRITE);
|
||||
#endif
|
||||
alarm(CHILD_LIFETIME);
|
||||
close(pipefd[0]); /* close read end in child. */
|
||||
daemon->pipe_to_parent = pipefd[1];
|
||||
}
|
||||
|
||||
/* The connected socket inherits non-blocking
|
||||
attribute from the listening socket.
|
||||
Reset that here. */
|
||||
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
|
||||
while(retry_send(fcntl(confd, F_SETFL, flags & ~O_NONBLOCK)));
|
||||
|
||||
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
|
||||
|
||||
if (buff)
|
||||
free(buff);
|
||||
|
||||
for (s = daemon->servers; s; s = s->next)
|
||||
if (s->tcpfd != -1)
|
||||
{
|
||||
shutdown(s->tcpfd, SHUT_RDWR);
|
||||
close(s->tcpfd);
|
||||
s->tcpfd = -1;
|
||||
}
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
close(daemon->pipe_to_parent);
|
||||
flush_log();
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
addr.addr4 = tcp_addr.in.sin_addr;
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (iface->index == if_index &&
|
||||
iface->addr.sa.sa_family == tcp_addr.sa.sa_family)
|
||||
break;
|
||||
|
||||
if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
|
||||
client_ok = 0;
|
||||
}
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND))
|
||||
iface = listener->iface; /* May be NULL */
|
||||
else
|
||||
{
|
||||
/* Check for allowed interfaces when binding the wildcard address:
|
||||
we do this by looking for an interface with the same address as
|
||||
the local address of the TCP connection, then looking to see if that's
|
||||
an allowed interface. As a side effect, we get the netmask of the
|
||||
interface too, for localisation. */
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&iface->addr, &tcp_addr))
|
||||
break;
|
||||
|
||||
if (!iface)
|
||||
client_ok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!client_ok)
|
||||
goto closeconandreturn;
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
if (pipe(pipefd) == -1)
|
||||
goto closeconandreturn; /* pipe failed */
|
||||
|
||||
if ((p = fork()) == -1)
|
||||
{
|
||||
/* fork failed */
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
goto closeconandreturn;
|
||||
}
|
||||
|
||||
if (p != 0)
|
||||
{
|
||||
/* fork() done: parent side */
|
||||
close(pipefd[1]); /* parent needs read pipe end. */
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* The child process inherits the netlink socket,
|
||||
which it never uses, but when the parent (us)
|
||||
uses it in the future, the answer may go to the
|
||||
child, resulting in the parent blocking
|
||||
forever awaiting the result. To avoid this
|
||||
the child closes the netlink socket, but there's
|
||||
a nasty race, since the parent may use netlink
|
||||
before the child has done the close.
|
||||
|
||||
To avoid this, the parent blocks here until a
|
||||
single byte comes back up the pipe, which
|
||||
is sent by the child after it has closed the
|
||||
netlink socket. */
|
||||
|
||||
read_write(pipefd[0], &a, 1, RW_READ);
|
||||
#endif
|
||||
|
||||
|
||||
daemon->tcp_pids[slot] = p;
|
||||
daemon->tcp_pipes[slot] = pipefd[0];
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]++;
|
||||
if (daemon->metrics[METRIC_TCP_CONNECTIONS] > daemon->max_procs_used)
|
||||
daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS];
|
||||
|
||||
close(confd);
|
||||
|
||||
/* The child can use up to TCP_MAX_QUERIES ids, so skip that many. */
|
||||
daemon->log_id += TCP_MAX_QUERIES;
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* It can do more if making DNSSEC queries too. */
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
daemon->log_id += daemon->limit[LIMIT_WORK];
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (iface)
|
||||
{
|
||||
netmask = iface->netmask;
|
||||
auth_dns = iface->dns_auth;
|
||||
}
|
||||
else
|
||||
{
|
||||
netmask.s_addr = 0;
|
||||
auth_dns = 0;
|
||||
}
|
||||
|
||||
/* Arrange for SIGALRM after CHILD_LIFETIME seconds to
|
||||
terminate the process. */
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* See comment above re: netlink socket. */
|
||||
close(daemon->netlinkfd);
|
||||
read_write(pipefd[1], &a, 1, RW_WRITE);
|
||||
#endif
|
||||
alarm(CHILD_LIFETIME);
|
||||
close(pipefd[0]); /* close read end in child. */
|
||||
daemon->pipe_to_parent = pipefd[1];
|
||||
}
|
||||
|
||||
/* The connected socket inherits non-blocking
|
||||
attribute from the listening socket.
|
||||
Reset that here. */
|
||||
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
|
||||
while(retry_send(fcntl(confd, F_SETFL, flags & ~O_NONBLOCK)));
|
||||
|
||||
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
|
||||
|
||||
if (buff)
|
||||
free(buff);
|
||||
|
||||
for (s = daemon->servers; s; s = s->next)
|
||||
if (s->tcpfd != -1)
|
||||
{
|
||||
shutdown(s->tcpfd, SHUT_RDWR);
|
||||
close(s->tcpfd);
|
||||
s->tcpfd = -1;
|
||||
}
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
close(daemon->pipe_to_parent);
|
||||
flush_log();
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* If a DNSSEC query over UDP returns a truncated answer,
|
||||
we swap to the TCP path. This routine is responsible for forking
|
||||
|
||||
+39
-24
@@ -163,10 +163,10 @@ static int domain_no_rebind(char *domain)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
union all_addr *dst_addr, unsigned int dst_iface,
|
||||
struct dns_header *header, size_t plen, size_t replylimit, time_t now,
|
||||
struct frec *forward, unsigned int fwd_flags, int fast_retry)
|
||||
static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
union all_addr *dst_addr, unsigned int dst_iface,
|
||||
struct dns_header *header, size_t plen, size_t replylimit, time_t now,
|
||||
struct frec *forward, unsigned int fwd_flags, int fast_retry)
|
||||
{
|
||||
unsigned int flags = 0;
|
||||
int is_dnssec = forward && (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY));
|
||||
@@ -190,6 +190,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
{
|
||||
old_src = 1;
|
||||
old_reply = 1;
|
||||
fwd_flags = forward->flags;
|
||||
}
|
||||
else if (gotname && (forward = lookup_frec(daemon->namebuff, C_IN, (int)rrtype, -1, fwd_flags,
|
||||
FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION |
|
||||
@@ -253,7 +254,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
it's safe to wait for the reply from the first without
|
||||
forwarding the second. */
|
||||
if (difftime(now, forward->time) < 2)
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* use our id when resending */
|
||||
@@ -373,6 +374,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
while (forward->blocking_query)
|
||||
forward = forward->blocking_query;
|
||||
|
||||
/* Don't retry if we've already sent it via TCP. */
|
||||
if (forward->flags & FREC_GONE_TO_TCP)
|
||||
return;
|
||||
|
||||
if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))
|
||||
{
|
||||
/* log_id should match previous DNSSEC query. */
|
||||
@@ -385,8 +390,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
|
||||
/* Find suitable servers: should never fail. */
|
||||
if (!filter_servers(forward->sentto->arrayposn, F_DNSSECOK, &first, &last))
|
||||
return 0;
|
||||
|
||||
return;
|
||||
|
||||
is_dnssec = 1;
|
||||
forward->forwardall = 1;
|
||||
}
|
||||
@@ -501,8 +506,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
|
||||
if (forwarded || is_dnssec)
|
||||
{
|
||||
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++;
|
||||
forward->forward_timestamp = dnsmasq_milliseconds();
|
||||
return 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* could not send on, prepare to return */
|
||||
@@ -514,9 +520,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
if (udpfd != -1)
|
||||
{
|
||||
if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, (char *)(header + replylimit), first, last, ede)))
|
||||
return 0;
|
||||
return;
|
||||
|
||||
if (forward->flags & FREC_HAS_PHEADER)
|
||||
if (fwd_flags & FREC_HAS_PHEADER)
|
||||
{
|
||||
u16 swap = htons((u16)ede);
|
||||
|
||||
@@ -542,7 +548,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface);
|
||||
}
|
||||
|
||||
return 0;
|
||||
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if any frecs need to do a retry, and action that if so.
|
||||
@@ -779,10 +786,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
header->ancount = htons(0);
|
||||
header->nscount = htons(0);
|
||||
header->arcount = htons(0);
|
||||
ede = EDE_DNSSEC_BOGUS;
|
||||
}
|
||||
}
|
||||
else if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
|
||||
else if (ad_reqd && cache_secure)
|
||||
header->hb4 |= HB4_AD;
|
||||
|
||||
/* If the requestor didn't set the DO bit, don't return DNSSEC info. */
|
||||
@@ -1243,20 +1249,25 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
|
||||
char *result, *domain = "result";
|
||||
union all_addr a;
|
||||
|
||||
a.log.ede = ede = errflags_to_ede(status);
|
||||
ede = errflags_to_ede(status);
|
||||
|
||||
if (STAT_ISEQUAL(status, STAT_ABANDONED))
|
||||
{
|
||||
result = "ABANDONED";
|
||||
status = STAT_BOGUS;
|
||||
if (ede == EDE_UNSET)
|
||||
ede = EDE_OTHER;
|
||||
}
|
||||
else
|
||||
result = (STAT_ISEQUAL(status, STAT_SECURE) ? "SECURE" : (STAT_ISEQUAL(status, STAT_INSECURE) ? "INSECURE" : "BOGUS"));
|
||||
|
||||
|
||||
if (STAT_ISEQUAL(status, STAT_SECURE))
|
||||
cache_secure = 1;
|
||||
else if (STAT_ISEQUAL(status, STAT_BOGUS))
|
||||
{
|
||||
if (ede == EDE_UNSET)
|
||||
ede = EDE_DNSSEC_BOGUS;
|
||||
no_cache_dnssec = 1;
|
||||
bogusanswer = 1;
|
||||
|
||||
@@ -1264,6 +1275,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
|
||||
domain = daemon->namebuff;
|
||||
}
|
||||
|
||||
a.log.ede = ede;
|
||||
log_query(F_SECSTAT, domain, &a, result, 0);
|
||||
}
|
||||
}
|
||||
@@ -1845,11 +1857,8 @@ void receive_query(struct listener *listen, time_t now)
|
||||
blockdata_free(saved_question);
|
||||
saved_question = NULL;
|
||||
|
||||
if (forward_query(fd, &source_addr, &dst_addr, if_index, header, (size_t)n,
|
||||
udp_size, now, NULL, fwd_flags, 0))
|
||||
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++;
|
||||
else
|
||||
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
|
||||
forward_query(fd, &source_addr, &dst_addr, if_index, header, (size_t)n,
|
||||
udp_size, now, NULL, fwd_flags, 0);
|
||||
}
|
||||
|
||||
blockdata_free(saved_question);
|
||||
@@ -2394,9 +2403,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
size = saved_size;
|
||||
|
||||
/* save state of "cd" flag in query */
|
||||
if ((checking_disabled = header->hb4 & HB4_CD))
|
||||
no_cache_dnssec = 1;
|
||||
|
||||
checking_disabled = header->hb4 & HB4_CD;
|
||||
|
||||
if (lookup_domain(daemon->namebuff, gotname, &first, &last))
|
||||
flags = is_local_answer(now, first, daemon->namebuff);
|
||||
else
|
||||
@@ -2447,7 +2455,9 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
/* Clear this in case we don't call tcp_key_recurse() below */
|
||||
memset(daemon->rr_status, 0, sizeof(*daemon->rr_status) * daemon->rr_status_sz);
|
||||
|
||||
if (!checking_disabled && (master->flags & SERV_DO_DNSSEC))
|
||||
if (checking_disabled || (header->hb4 & HB4_CD))
|
||||
no_cache_dnssec = 1;
|
||||
else if (master->flags & SERV_DO_DNSSEC)
|
||||
{
|
||||
int keycount = daemon->limit[LIMIT_WORK]; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
|
||||
int validatecount = daemon->limit[LIMIT_CRYPTO];
|
||||
@@ -2456,12 +2466,14 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
char *result, *domain = "result";
|
||||
|
||||
union all_addr a;
|
||||
a.log.ede = ede = errflags_to_ede(status);
|
||||
ede = errflags_to_ede(status);
|
||||
|
||||
if (STAT_ISEQUAL(status, STAT_ABANDONED))
|
||||
{
|
||||
result = "ABANDONED";
|
||||
status = STAT_BOGUS;
|
||||
if (ede == EDE_UNSET)
|
||||
ede = EDE_OTHER;
|
||||
}
|
||||
else
|
||||
result = (STAT_ISEQUAL(status, STAT_SECURE) ? "SECURE" : (STAT_ISEQUAL(status, STAT_INSECURE) ? "INSECURE" : "BOGUS"));
|
||||
@@ -2470,6 +2482,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
cache_secure = 1;
|
||||
else if (STAT_ISEQUAL(status, STAT_BOGUS))
|
||||
{
|
||||
if (ede == EDE_UNSET)
|
||||
ede = EDE_DNSSEC_BOGUS;
|
||||
no_cache_dnssec = 1;
|
||||
bogusanswer = 1;
|
||||
|
||||
@@ -2477,6 +2491,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
domain = daemon->namebuff;
|
||||
}
|
||||
|
||||
a.log.ede = ede;
|
||||
log_query(F_SECSTAT, domain, &a, result, 0);
|
||||
|
||||
if ((daemon->limit[LIMIT_CRYPTO] - validatecount) > (int)daemon->metrics[METRIC_CRYPTO_HWM])
|
||||
|
||||
Reference in new issue
Block a user