mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-04 13:48:30 +00:00
Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4569c22cc | ||
|
|
199e65c4d9 | ||
|
|
bb8811d472 | ||
|
|
995a16ca0c | ||
|
|
65f9c1aca1 | ||
|
|
b72ecb3a59 | ||
|
|
c221030f89 | ||
|
|
5bbea085d0 | ||
|
|
622cf03ab9 | ||
|
|
8ce27433f8 | ||
|
|
5d894620b4 | ||
|
|
71766c0c35 | ||
|
|
da58455508 | ||
|
|
b915c9a661 | ||
|
|
424aaa0f9d | ||
|
|
c72c895869 | ||
|
|
b7156116c2 | ||
|
|
7d915a0bb9 | ||
|
|
509afcd1d2 | ||
|
|
51343bd9a2 | ||
|
|
b58276a73c | ||
|
|
f162d344c0 |
No files matched your search
@@ -82,6 +82,35 @@ version 2.91
|
||||
to queries which arrive a dnsmasq already carrying an EDNS client
|
||||
subnet.
|
||||
|
||||
Handle DS queries to auth zones. When dnsmasq is configured to
|
||||
act as an authoritative server and has an authoritative zone
|
||||
configured, and recieves a query for that zone _as_forwarder_
|
||||
it answers the query directly rather than forwarding it. This
|
||||
doesn't affect the answer, but it saves dnsmasq forwarding the
|
||||
query to the recusor upstream, whch then bounces it back to dnsmasq
|
||||
in auth mode. The exception should be when the query is for the root
|
||||
of zone, for a DS RR. The answer to that has to come from the parent,
|
||||
via the recursor, and will typically be a proof-of-nonexistence
|
||||
since dnsmasq doesn't support signed zones. This patch suppresses
|
||||
local answers and forces forwarding to the upstream recursor for such
|
||||
queries. It stops breakage when a DNSSEC validating client makes
|
||||
queries to dnsmasq acting as forwarder for a zone for which it is
|
||||
authoritative.
|
||||
|
||||
Implement "DNS-0x20 encoding", for extra protection against
|
||||
reply-spoof attacks. Since DNS queries are case-insensitive,
|
||||
it's possible to randomly flip the case of letters in a query
|
||||
and still get the correct answer back.
|
||||
This adds an extra dimension for a cache-poisoning attacker
|
||||
to guess when sending replies in-the-blind since it's expected
|
||||
that the legitimate answer will have the same pattern of upper
|
||||
and lower case as the query, so any replies which don't can be
|
||||
ignored as malicious. The amount of extra entropy clearly depends
|
||||
on the number of a-z and A-Z characters in the query, and this
|
||||
implementation puts a hard limit of 32 bits to make rescource
|
||||
allocation easy. This about doubles entropy over the standard
|
||||
random ID and random port combination.
|
||||
|
||||
|
||||
version 2.90
|
||||
Fix reversion in --rev-server introduced in 2.88 which
|
||||
|
||||
@@ -71,8 +71,8 @@ nft_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_NFTSET $(PKG_CONFIG
|
||||
nft_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_NFTSET $(PKG_CONFIG) --libs libnftables`
|
||||
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
|
||||
|
||||
sum?=$(shell $(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ')
|
||||
sum!=$(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' '
|
||||
sum?=$(shell echo $(CC) -DDNSMASQ_COMPILE_FLAGS="$(CFLAGS)" -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ')
|
||||
sum!=echo $(CC) -DDNSMASQ_COMPILE_FLAGS="$(CFLAGS)" -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' '
|
||||
copts_conf = .copts_$(sum)
|
||||
|
||||
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
|
||||
|
||||
+1
-1
@@ -882,7 +882,7 @@ ie capable of returning DNSSEC records with data. If they are not,
|
||||
then dnsmasq will not be able to determine the trusted status of
|
||||
answers and this means that DNS service will be entirely broken.
|
||||
.TP
|
||||
.B --trust-anchor=[<class>],<domain>,<key-tag>,<algorithm>,<digest-type>,<digest>
|
||||
.B --trust-anchor=<domain>,[<class>,]<key-tag>,<algorithm>,<digest-type>,<digest>
|
||||
Provide DS records to act a trust anchors for DNSSEC
|
||||
validation. Typically these will be the DS record(s) for Key Signing
|
||||
key(s) (KSK) of the root zone,
|
||||
|
||||
+2
-2
@@ -137,7 +137,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
nameoffset = p - (unsigned char *)header;
|
||||
|
||||
/* now extract name as .-concatenated string into name */
|
||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
||||
if (!extract_name(header, qlen, &p, name, EXTR_NAME_EXTRACT, 4))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -513,7 +513,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
nxdomain = 0;
|
||||
if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr))))
|
||||
{
|
||||
log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid), 0);
|
||||
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr, record_source(crecp->uid), 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||
qtype == T_A ? "4" : "6", &crecp->addr))
|
||||
|
||||
+23
-7
@@ -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;
|
||||
@@ -1852,15 +1852,31 @@ int cache_make_stat(struct txt_record *t)
|
||||
#endif
|
||||
|
||||
/* There can be names in the cache containing control chars, don't
|
||||
mess up logging or open security holes. */
|
||||
mess up logging or open security holes. Also convert to all-LC
|
||||
so that 0x20-encoding doesn't make logs look like ransom notes
|
||||
made out of letters cut from a newspaper.
|
||||
Overwrites daemon->workspacename */
|
||||
static char *sanitise(char *name)
|
||||
{
|
||||
unsigned char *r;
|
||||
unsigned char *r = (unsigned char *)name;
|
||||
|
||||
if (name)
|
||||
for (r = (unsigned char *)name; *r; r++)
|
||||
if (!isprint((int)*r))
|
||||
return "<name unprintable>";
|
||||
|
||||
{
|
||||
char *d = name = daemon->workspacename;
|
||||
|
||||
for (; *r; r++, d++)
|
||||
if (!isprint((int)*r))
|
||||
return "<name unprintable>";
|
||||
else
|
||||
{
|
||||
unsigned char c = *r;
|
||||
|
||||
*d = (char)((c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c);
|
||||
}
|
||||
|
||||
*d = 0;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
@@ -362,6 +362,11 @@ HAVE_SOCKADDR_SA_LEN
|
||||
#define HAVE_INOTIFY
|
||||
#endif
|
||||
|
||||
/* This never compiles code, it's only used by the makefile to fingerprint builds. */
|
||||
#ifdef DNSMASQ_COMPILE_FLAGS
|
||||
static char *compile_flags = DNSMASQ_COMPILE_FLAGS;
|
||||
#endif
|
||||
|
||||
/* Define a string indicating which options are in use.
|
||||
DNSMASQ_COMPILE_OPTS is only defined in dnsmasq.c */
|
||||
|
||||
|
||||
+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
|
||||
|
||||
+7
-1
@@ -790,6 +790,7 @@ struct frec {
|
||||
int forwardall, flags;
|
||||
time_t time;
|
||||
u32 forward_timestamp;
|
||||
unsigned int encode_bitmap;
|
||||
int forward_delay;
|
||||
struct blockdata *stash; /* saved query or saved reply, whilst we validate */
|
||||
size_t stash_len;
|
||||
@@ -1386,7 +1387,7 @@ int is_rev_synth(int flag, union all_addr *addr, char *name);
|
||||
/* rfc1035.c */
|
||||
int do_doctor(struct dns_header *header, size_t qlen, char *namebuff);
|
||||
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
char *name, int isExtract, int extrabytes);
|
||||
char *name, int func, unsigned int parm);
|
||||
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes);
|
||||
unsigned char *skip_questions(struct dns_header *header, size_t plen);
|
||||
unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen);
|
||||
@@ -1413,6 +1414,11 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp,
|
||||
int *offset, unsigned short type, unsigned short class, char *format, ...);
|
||||
int in_arpa_name_2_addr(char *namein, union all_addr *addrp);
|
||||
int private_net(struct in_addr addr, int ban_localhost);
|
||||
/* extract_name ops */
|
||||
#define EXTR_NAME_EXTRACT 1
|
||||
#define EXTR_NAME_COMPARE 2
|
||||
#define EXTR_NAME_NOCASE 3
|
||||
#define EXTR_NAME_FLIP 4
|
||||
|
||||
/* auth.c */
|
||||
#ifdef HAVE_AUTH
|
||||
|
||||
+23
-23
@@ -191,7 +191,7 @@ static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state
|
||||
/* domain-name, canonicalise */
|
||||
int len;
|
||||
|
||||
if (!extract_name(header, plen, &state->ip, state->buff, 1, 0) ||
|
||||
if (!extract_name(header, plen, &state->ip, state->buff, EXTR_NAME_EXTRACT, 0) ||
|
||||
(len = to_wire(state->buff)) == 0)
|
||||
continue;
|
||||
|
||||
@@ -339,7 +339,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
|
||||
|
||||
pstart = p;
|
||||
|
||||
if (!(res = extract_name(header, plen, &p, name, 0, 10)))
|
||||
if (!(res = extract_name(header, plen, &p, name, EXTR_NAME_COMPARE, 10)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(stype, p);
|
||||
@@ -374,14 +374,14 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
|
||||
if (gotkey)
|
||||
{
|
||||
/* If there's more than one SIG, ensure they all have same keyname */
|
||||
if (extract_name(header, plen, &p, keyname, 0, 0) != 1)
|
||||
if (extract_name(header, plen, &p, keyname, EXTR_NAME_COMPARE, 0) != 1)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
gotkey = 1;
|
||||
|
||||
if (!extract_name(header, plen, &p, keyname, 1, 0))
|
||||
if (!extract_name(header, plen, &p, keyname, EXTR_NAME_EXTRACT, 0))
|
||||
return 0;
|
||||
|
||||
/* RFC 4035 5.3.1 says that the Signer's Name field MUST equal
|
||||
@@ -503,7 +503,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
GETLONG(sig_inception, p);
|
||||
GETSHORT(key_tag, p);
|
||||
|
||||
if (!extract_name(header, plen, &p, keyname, 1, 0))
|
||||
if (!extract_name(header, plen, &p, keyname, EXTR_NAME_EXTRACT, 0))
|
||||
return STAT_BOGUS;
|
||||
|
||||
if (!time_check)
|
||||
@@ -568,7 +568,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
|
||||
p = rrset[i];
|
||||
|
||||
if (!extract_name(header, plen, &p, name, 1, 10))
|
||||
if (!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 10))
|
||||
return STAT_BOGUS;
|
||||
|
||||
name_start = name;
|
||||
@@ -661,7 +661,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
|
||||
/* namebuff used for workspace above, restore to leave unchanged on exit */
|
||||
p = (unsigned char*)(rrset[0]);
|
||||
if (!extract_name(header, plen, &p, name, 1, 0))
|
||||
if (!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 0))
|
||||
return STAT_BOGUS;
|
||||
|
||||
if (key)
|
||||
@@ -727,7 +727,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
static unsigned char **cached_digest;
|
||||
static size_t cached_digest_size = 0;
|
||||
|
||||
if (ntohs(header->qdcount) != 1 || RCODE(header) != NOERROR || !extract_name(header, plen, &p, name, 1, 4))
|
||||
if (ntohs(header->qdcount) != 1 || RCODE(header) != NOERROR || !extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 4))
|
||||
return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -752,7 +752,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
for (j = ntohs(header->ancount); j != 0; j--)
|
||||
{
|
||||
/* Ensure we have type, class TTL and length */
|
||||
if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
|
||||
if (!(rc = extract_name(header, plen, &p, name, EXTR_NAME_COMPARE, 10)))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -904,7 +904,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
for (j = ntohs(header->ancount); j != 0; j--)
|
||||
{
|
||||
/* Ensure we have type, class TTL and length */
|
||||
if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
|
||||
if (!(rc = extract_name(header, plen, &p, name, EXTR_NAME_COMPARE, 10)))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -1024,7 +1024,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
}
|
||||
|
||||
p = (unsigned char *)(header+1);
|
||||
if (!extract_name(header, plen, &p, name, 1, 4))
|
||||
if (!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 4))
|
||||
return STAT_BOGUS;
|
||||
|
||||
p += 4; /* qtype, qclass */
|
||||
@@ -1050,7 +1050,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
{
|
||||
unsigned char *psave;
|
||||
|
||||
if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
|
||||
if (!(rc = extract_name(header, plen, &p, name, EXTR_NAME_COMPARE, 10)))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
GETSHORT(atype, p);
|
||||
@@ -1231,12 +1231,12 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
|
||||
int sig_labels, name_labels;
|
||||
|
||||
p = nsecs[i];
|
||||
if (!extract_name(header, plen, &p, workspace1, 1, 10))
|
||||
if (!extract_name(header, plen, &p, workspace1, EXTR_NAME_EXTRACT, 10))
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
p += 8; /* class, type, TTL */
|
||||
GETSHORT(rdlen, p);
|
||||
psave = p;
|
||||
if (!extract_name(header, plen, &p, workspace2, 1, 0))
|
||||
if (!extract_name(header, plen, &p, workspace2, EXTR_NAME_EXTRACT, 0))
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
/* If NSEC comes from wildcard expansion, use original wildcard
|
||||
@@ -1400,7 +1400,7 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
|
||||
for (i = 0; i < nsec_count; i++)
|
||||
if ((p = nsecs[i]))
|
||||
{
|
||||
if (!extract_name(header, plen, &p, workspace1, 1, 10) ||
|
||||
if (!extract_name(header, plen, &p, workspace1, EXTR_NAME_EXTRACT, 10) ||
|
||||
!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
|
||||
return 0;
|
||||
|
||||
@@ -1609,7 +1609,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
|
||||
for (i = 0; i < nsec_count; i++)
|
||||
if ((p = nsecs[i]))
|
||||
{
|
||||
if (!extract_name(header, plen, &p, workspace1, 1, 0))
|
||||
if (!extract_name(header, plen, &p, workspace1, EXTR_NAME_EXTRACT, 0))
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
if (!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
|
||||
@@ -1684,7 +1684,7 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
|
||||
{
|
||||
unsigned char *pstart = p;
|
||||
|
||||
if (!extract_name(header, plen, &p, daemon->workspacename, 1, 10))
|
||||
if (!extract_name(header, plen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 10))
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
GETSHORT(type, p);
|
||||
@@ -1735,7 +1735,7 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
|
||||
{
|
||||
unsigned char *psav;
|
||||
|
||||
if (!(res = extract_name(header, plen, &p1, daemon->workspacename, 0, 10)))
|
||||
if (!(res = extract_name(header, plen, &p1, daemon->workspacename, EXTR_NAME_COMPARE, 10)))
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
GETSHORT(type1, p1);
|
||||
@@ -1965,7 +1965,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
targets[0] = p1;
|
||||
targetidx = 1;
|
||||
|
||||
if (!extract_name(header, plen, &p1, name, 1, 4))
|
||||
if (!extract_name(header, plen, &p1, name, EXTR_NAME_EXTRACT, 4))
|
||||
return STAT_BOGUS;
|
||||
|
||||
GETSHORT(qtype, p1);
|
||||
@@ -2003,7 +2003,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
if (i != 0 && !ADD_RDLEN(header, p1, plen, rdlen1))
|
||||
return STAT_BOGUS;
|
||||
|
||||
if (!extract_name(header, plen, &p1, name, 1, 10))
|
||||
if (!extract_name(header, plen, &p1, name, EXTR_NAME_EXTRACT, 10))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
GETSHORT(type1, p1);
|
||||
@@ -2018,7 +2018,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
/* Check if we've done this RRset already */
|
||||
for (p2 = ans_start, j = 0; j < i; j++)
|
||||
{
|
||||
if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
|
||||
if (!(rc = extract_name(header, plen, &p2, name, EXTR_NAME_COMPARE, 10)))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
GETSHORT(type2, p2);
|
||||
@@ -2115,7 +2115,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
if ((p2 = targets[j]))
|
||||
{
|
||||
int rc1;
|
||||
if (!(rc1 = extract_name(header, plen, &p2, name, 0, 10)))
|
||||
if (!(rc1 = extract_name(header, plen, &p2, name, EXTR_NAME_COMPARE, 10)))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
if (class1 == qclass && rc1 == 1 && (type1 == T_CNAME || type1 == qtype || qtype == T_ANY ))
|
||||
@@ -2149,7 +2149,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
if (neganswer)
|
||||
*neganswer = 1;
|
||||
|
||||
if (!extract_name(header, plen, &p2, name, 1, 10))
|
||||
if (!extract_name(header, plen, &p2, name, EXTR_NAME_EXTRACT, 10))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
|
||||
/* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */
|
||||
|
||||
+105
-56
@@ -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));
|
||||
@@ -254,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 */
|
||||
@@ -264,6 +264,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
/* new query */
|
||||
if (!forward)
|
||||
{
|
||||
unsigned char *p;
|
||||
|
||||
if (OPCODE(header) != QUERY)
|
||||
{
|
||||
flags = F_RCODE;
|
||||
@@ -323,7 +325,12 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
forward->frec_src.orig_id = ntohs(header->id);
|
||||
forward->new_id = get_id();
|
||||
header->id = ntohs(forward->new_id);
|
||||
|
||||
|
||||
forward->encode_bitmap = rand32();
|
||||
p = (unsigned char *)(header+1);
|
||||
if (!extract_name(header, plen, &p, NULL, EXTR_NAME_FLIP, forward->encode_bitmap))
|
||||
goto reply;
|
||||
|
||||
/* Keep copy of query for retries and move to TCP */
|
||||
if (!(forward->stash = blockdata_alloc((char *)header, plen)))
|
||||
{
|
||||
@@ -374,6 +381,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. */
|
||||
@@ -386,8 +397,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;
|
||||
}
|
||||
@@ -502,8 +513,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 */
|
||||
@@ -515,7 +527,7 @@ 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 (fwd_flags & FREC_HAS_PHEADER)
|
||||
{
|
||||
@@ -543,7 +555,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.
|
||||
@@ -780,10 +793,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. */
|
||||
@@ -817,10 +829,6 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
||||
|
||||
daemon->log_display_id = forward->frec_src.log_id;
|
||||
|
||||
/* We've had a reply already, which we're validating. Ignore this duplicate */
|
||||
if (forward->blocking_query || (forward->flags & FREC_GONE_TO_TCP))
|
||||
return;
|
||||
|
||||
/* Find the original query that started it all.... */
|
||||
for (orig = forward; orig->dependent; orig = orig->dependent);
|
||||
|
||||
@@ -967,7 +975,8 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
||||
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
|
||||
new->flags |= flags;
|
||||
new->forwardall = 0;
|
||||
|
||||
new->encode_bitmap = 0;
|
||||
|
||||
forward->next_dependent = NULL;
|
||||
new->dependent = forward; /* to find query awaiting new one. */
|
||||
|
||||
@@ -1084,7 +1093,7 @@ void reply_query(int fd, time_t now)
|
||||
return;
|
||||
|
||||
p = (unsigned char *)(header+1);
|
||||
if (!extract_name(header, n, &p, daemon->namebuff, 1, 4))
|
||||
if (!extract_name(header, n, &p, daemon->namebuff, EXTR_NAME_EXTRACT, 4))
|
||||
return; /* bad packet */
|
||||
GETSHORT(rrtype, p);
|
||||
GETSHORT(class, p);
|
||||
@@ -1147,18 +1156,20 @@ void reply_query(int fd, time_t now)
|
||||
if (daemon->ignore_addr && RCODE(header) == NOERROR &&
|
||||
check_for_ignored_address(header, n))
|
||||
return;
|
||||
|
||||
if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) && forward->forwardall == 0)
|
||||
/* for broken servers, attempt to send to another one. */
|
||||
{
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* The query MAY have got a good answer, and be awaiting
|
||||
the results of further queries, in which case
|
||||
The Stash contains something else and we don't need to retry anyway. */
|
||||
the stash contains something else and we don't need to retry anyway.
|
||||
We may also have already got a truncated reply, and be in the process
|
||||
of doing the query by TCP so can ignore further, probably truncated, UDP answers. */
|
||||
if (forward->blocking_query || (forward->flags & FREC_GONE_TO_TCP))
|
||||
return;
|
||||
#endif
|
||||
|
||||
if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) && forward->forwardall == 0)
|
||||
/* for broken servers, attempt to send to another one. */
|
||||
{
|
||||
/* Get the saved query back. */
|
||||
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
||||
|
||||
@@ -1192,7 +1203,11 @@ void reply_query(int fd, time_t now)
|
||||
/* denominator controls how many queries we average over. */
|
||||
server->query_latency = server->mma_latency/128;
|
||||
|
||||
|
||||
/* Flip the bits back in the query name. */
|
||||
p = (unsigned char *)(header+1);
|
||||
if (!extract_name(header, n, &p, NULL, EXTR_NAME_FLIP, forward->encode_bitmap))
|
||||
return;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
@@ -1244,20 +1259,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;
|
||||
|
||||
@@ -1265,6 +1285,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);
|
||||
}
|
||||
}
|
||||
@@ -1682,15 +1703,27 @@ void receive_query(struct listener *listen, time_t now)
|
||||
&source_addr, auth_dns ? "auth" : "query", type);
|
||||
|
||||
#ifdef HAVE_AUTH
|
||||
/* find queries for zones we're authoritative for, and answer them directly */
|
||||
/* Find queries for zones we're authoritative for, and answer them directly.
|
||||
The exception to this is DS queries for the zone route. They
|
||||
have to come from the parent zone. Since dnsmasq's auth server
|
||||
can't do DNSSEC, the zone will be unsigned, and anything using
|
||||
dnsmasq as a forwarder and doing validation will be expecting to
|
||||
see the proof of non-existence from the parent. */
|
||||
if (!auth_dns && !option_bool(OPT_LOCALISE))
|
||||
for (zone = daemon->auth_zones; zone; zone = zone->next)
|
||||
if (in_zone(zone, daemon->namebuff, NULL))
|
||||
{
|
||||
auth_dns = 1;
|
||||
local_auth = 1;
|
||||
break;
|
||||
}
|
||||
{
|
||||
char *cut;
|
||||
|
||||
if (in_zone(zone, daemon->namebuff, &cut))
|
||||
{
|
||||
if (type != T_DS || cut)
|
||||
{
|
||||
auth_dns = 1;
|
||||
local_auth = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LOOP
|
||||
@@ -1846,11 +1879,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);
|
||||
@@ -1877,7 +1907,7 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
|
||||
/* Save the query to make sure we get the answer we expect. */
|
||||
p = (unsigned char *)(header+1);
|
||||
if (!extract_name(header, qsize, &p, daemon->namebuff, 1, 4))
|
||||
if (!extract_name(header, qsize, &p, daemon->namebuff, EXTR_NAME_EXTRACT, 4))
|
||||
return 0;
|
||||
GETSHORT(type, p);
|
||||
GETSHORT(class, p);
|
||||
@@ -1981,12 +2011,12 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If the question section of the reply doesn't match the crc we sent, then
|
||||
/* If the question section of the reply doesn't match the question we sent, then
|
||||
someone might be attempting to insert bogus values into the cache by
|
||||
sending replies containing questions and bogus answers.
|
||||
sending replies containing questions and bogus answers.
|
||||
Try another server, or give up */
|
||||
p = (unsigned char *)(header+1);
|
||||
if (extract_name(header, rsize, &p, daemon->namebuff, 0, 4) != 1)
|
||||
if (extract_name(header, rsize, &p, daemon->namebuff, EXTR_NAME_NOCASE, 4) != 1)
|
||||
continue;
|
||||
GETSHORT(rtype, p);
|
||||
GETSHORT(rclass, p);
|
||||
@@ -2319,15 +2349,27 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
&peer_addr, auth_dns ? "auth" : "query", qtype);
|
||||
|
||||
#ifdef HAVE_AUTH
|
||||
/* find queries for zones we're authoritative for, and answer them directly */
|
||||
/* Find queries for zones we're authoritative for, and answer them directly.
|
||||
The exception to this is DS queries for the zone route. They
|
||||
have to come from the parent zone. Since dnsmasq's auth server
|
||||
can't do DNSSEC, the zone will be unsigned, and anything using
|
||||
dnsmasq as a forwarder and doing validation will be expecting to
|
||||
see the proof of non-existence from the parent. */
|
||||
if (!auth_dns && !option_bool(OPT_LOCALISE))
|
||||
for (zone = daemon->auth_zones; zone; zone = zone->next)
|
||||
if (in_zone(zone, daemon->namebuff, NULL))
|
||||
{
|
||||
auth_dns = 1;
|
||||
local_auth = 1;
|
||||
break;
|
||||
}
|
||||
{
|
||||
char *cut;
|
||||
|
||||
if (in_zone(zone, daemon->namebuff, &cut))
|
||||
{
|
||||
if (qtype != T_DS || cut)
|
||||
{
|
||||
auth_dns = 1;
|
||||
local_auth = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
norebind = domain_no_rebind(daemon->namebuff);
|
||||
@@ -2395,9 +2437,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
|
||||
@@ -2448,7 +2489,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];
|
||||
@@ -2457,12 +2500,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"));
|
||||
@@ -2471,6 +2516,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;
|
||||
|
||||
@@ -2478,6 +2525,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])
|
||||
@@ -3009,8 +3057,9 @@ static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int
|
||||
{
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
int hclass, hrrtype;
|
||||
|
||||
if (extract_name(header, f->stash_len, &p, target, 0, 4) != 1)
|
||||
|
||||
/* Case sensitive compare for DNS-0x20 encoding. */
|
||||
if (extract_name(header, f->stash_len, &p, target, EXTR_NAME_NOCASE, 4) != 1)
|
||||
continue;
|
||||
|
||||
GETSHORT(hrrtype, p);
|
||||
|
||||
+1
-1
@@ -568,7 +568,7 @@ static struct {
|
||||
{ LOPT_CMARK_ALST, ARG_DUP, "<connmark>[/<mask>][,<pattern>[/<pattern>...]]", gettext_noop("Set allowed DNS patterns for a connection-track mark."), NULL },
|
||||
{ LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
|
||||
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
|
||||
{ LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
|
||||
{ LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>,]...", gettext_noop("Specify trust anchor key digest."), NULL },
|
||||
{ LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
|
||||
{ LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
|
||||
{ LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
|
||||
|
||||
+63
-37
@@ -16,16 +16,29 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
/* EXTR_NAME_EXTRACT -> extract name
|
||||
EXTR_NAME_COMPARE -> compare name, case insensitive
|
||||
EXTR_NAME_NOCASE -> compare name, case sensitive
|
||||
EXTR_NAME_FLIP -> flip 0x20 bits in packet, controlled by bitmap in parm. name may be NULL
|
||||
|
||||
return = 0 -> error
|
||||
return = 1 -> extract OK, compare OK, flip OK
|
||||
return = 2 -> extract OK, compare failed.
|
||||
*/
|
||||
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
char *name, int isExtract, int extrabytes)
|
||||
char *name, int func, unsigned int parm)
|
||||
{
|
||||
unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
|
||||
unsigned int j, l, namelen = 0, hops = 0;
|
||||
int retvalue = 1;
|
||||
int retvalue = 1, case_insens = 1, isExtract = 0, flip = 0, extrabytes = (int)parm;
|
||||
|
||||
if (func == EXTR_NAME_EXTRACT)
|
||||
isExtract = 1, *cp = 0;
|
||||
else if (func == EXTR_NAME_NOCASE)
|
||||
case_insens = 0;
|
||||
else if (func == EXTR_NAME_FLIP)
|
||||
flip = 1, extrabytes = 0;
|
||||
|
||||
if (isExtract)
|
||||
*cp = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
unsigned int label_type;
|
||||
@@ -46,7 +59,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
cp--;
|
||||
*cp = 0; /* terminate: lose final period */
|
||||
}
|
||||
else if (*cp != 0)
|
||||
else if (!flip && *cp != 0)
|
||||
retvalue = 2;
|
||||
|
||||
if (p1) /* we jumped via compression */
|
||||
@@ -85,7 +98,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
if (!CHECK_LEN(header, p, plen, l))
|
||||
return 0;
|
||||
|
||||
for(j=0; j<l; j++, p++)
|
||||
for (j=0; j<l; j++, p++)
|
||||
if (isExtract)
|
||||
{
|
||||
unsigned char c = *p;
|
||||
@@ -98,6 +111,18 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
else
|
||||
*cp++ = c;
|
||||
}
|
||||
else if (flip)
|
||||
{
|
||||
unsigned char c = *p;
|
||||
|
||||
/* parm is unsigned. We only flip up to the first 32 alpha-chars. */
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
|
||||
{
|
||||
if (parm & 1)
|
||||
*p ^= 0x20;
|
||||
parm >>= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char c1 = *cp, c2 = *p;
|
||||
@@ -107,15 +132,15 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
else
|
||||
{
|
||||
cp++;
|
||||
if (c1 >= 'A' && c1 <= 'Z')
|
||||
c1 += 'a' - 'A';
|
||||
|
||||
if (c1 == NAME_ESCAPE)
|
||||
c1 = (*cp++)-1;
|
||||
else if (case_insens && c1 >= 'A' && c1 <= 'Z')
|
||||
c1 += 'a' - 'A';
|
||||
|
||||
if (c2 >= 'A' && c2 <= 'Z')
|
||||
if (case_insens && c2 >= 'A' && c2 <= 'Z')
|
||||
c2 += 'a' - 'A';
|
||||
|
||||
|
||||
if (c1 != c2)
|
||||
retvalue = 2;
|
||||
}
|
||||
@@ -123,7 +148,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
|
||||
if (isExtract)
|
||||
*cp++ = '.';
|
||||
else if (*cp != 0 && *cp++ != '.')
|
||||
else if (!flip && *cp != 0 && *cp++ != '.')
|
||||
retvalue = 2;
|
||||
}
|
||||
else
|
||||
@@ -399,7 +424,7 @@ int do_doctor(struct dns_header *header, size_t qlen, char *namebuff)
|
||||
if (i == ntohs(header->ancount) && !(p = skip_section(p, ntohs(header->nscount), header, qlen)))
|
||||
return done;
|
||||
|
||||
if (!extract_name(header, qlen, &p, namebuff, 1, 10))
|
||||
if (!extract_name(header, qlen, &p, namebuff, EXTR_NAME_EXTRACT, 10))
|
||||
return done; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -480,7 +505,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
|
||||
for (i = 0; i < ntohs(header->nscount); i++)
|
||||
{
|
||||
if (!extract_name(header, qlen, &p, daemon->workspacename, 1, 0))
|
||||
if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -509,7 +534,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
|
||||
for (j = 0; j < 2; j++) /* MNAME, RNAME */
|
||||
{
|
||||
if (!extract_name(header, qlen, &p, daemon->workspacename, 1, 0))
|
||||
if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
|
||||
{
|
||||
if (!no_cache)
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
@@ -660,7 +685,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
namep = p = (unsigned char *)(header+1);
|
||||
|
||||
if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4))
|
||||
if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, EXTR_NAME_EXTRACT, 4))
|
||||
return 2; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -684,7 +709,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
int secflag = 0;
|
||||
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||
if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10)))
|
||||
return 2; /* bad packet */
|
||||
|
||||
GETSHORT(aqtype, p1);
|
||||
@@ -722,7 +747,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
if (aqtype == T_CNAME)
|
||||
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
|
||||
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
|
||||
return 2;
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
@@ -800,7 +825,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
{
|
||||
int secflag = 0;
|
||||
|
||||
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||
if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10)))
|
||||
return 2; /* bad packet */
|
||||
|
||||
GETSHORT(aqtype, p1);
|
||||
@@ -862,7 +887,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
}
|
||||
|
||||
namep = p1;
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
|
||||
return 2;
|
||||
|
||||
if (qtype != T_CNAME)
|
||||
@@ -932,7 +957,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
/* Name, extract it then re-encode. */
|
||||
int len;
|
||||
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
|
||||
{
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
return 2;
|
||||
@@ -965,7 +990,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
} while (desc != -1);
|
||||
|
||||
/* we overwrote the original name, so get it back here. */
|
||||
if (!extract_name(header, qlen, &tmp, name, 1, 0))
|
||||
if (!extract_name(header, qlen, &tmp, name, EXTR_NAME_EXTRACT, 0))
|
||||
{
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
return 2;
|
||||
@@ -1127,7 +1152,7 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark)
|
||||
{
|
||||
int aqtype, aqclass, ardlen;
|
||||
|
||||
if (!extract_name(header, len, &p, daemon->namebuff, 1, 10))
|
||||
if (!extract_name(header, len, &p, daemon->namebuff, EXTR_NAME_EXTRACT, 10))
|
||||
return;
|
||||
|
||||
if (!CHECK_LEN(header, p, len, 10))
|
||||
@@ -1145,7 +1170,7 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark)
|
||||
{
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
if (!extract_name(header, len, &p, daemon->workspacename, 1, 0))
|
||||
if (!extract_name(header, len, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
|
||||
return;
|
||||
if (safe_name(daemon->namebuff) && safe_name(daemon->workspacename))
|
||||
ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, daemon->workspacename, attl);
|
||||
@@ -1195,7 +1220,7 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
||||
if (!(header->hb3 & HB3_QR) && (ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0))
|
||||
return 0; /* non-standard query. */
|
||||
|
||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
||||
if (!extract_name(header, qlen, &p, name, EXTR_NAME_EXTRACT, 4))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -1309,7 +1334,7 @@ static int check_bad_address(struct dns_header *header, size_t qlen, struct bogu
|
||||
|
||||
for (i = ntohs(header->ancount); i != 0; i--)
|
||||
{
|
||||
if (name && !extract_name(header, qlen, &p, name, 1, 10))
|
||||
if (name && !extract_name(header, qlen, &p, name, EXTR_NAME_EXTRACT, 10))
|
||||
return 0; /* bad packet */
|
||||
|
||||
if (!name && !(p = skip_name(p, header, qlen, 10)))
|
||||
@@ -1559,7 +1584,7 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
|
||||
return daemon->max_ttl;
|
||||
}
|
||||
|
||||
static int cache_validated(const struct crec *crecp)
|
||||
static int cache_not_validated(const struct crec *crecp)
|
||||
{
|
||||
return (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK));
|
||||
}
|
||||
@@ -1619,7 +1644,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
nameoffset = p - (unsigned char *)header;
|
||||
|
||||
/* now extract name as .-concatenated string into name */
|
||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
||||
if (!extract_name(header, qlen, &p, name, EXTR_NAME_EXTRACT, 4))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -1660,7 +1685,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
|
||||
/* If the client asked for DNSSEC don't use cached data. */
|
||||
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
|
||||
(rd_bit && (!do_bit || cache_validated(crecp))))
|
||||
(rd_bit && (!do_bit || cache_not_validated(crecp))))
|
||||
{
|
||||
if (crecp->flags & F_CONFIG || qtype == T_CNAME)
|
||||
ans = 1;
|
||||
@@ -1819,7 +1844,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
the zone is unsigned, which implies that we're doing
|
||||
validation. */
|
||||
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
|
||||
(rd_bit && (!do_bit || cache_validated(crecp)) ))
|
||||
(rd_bit && (!do_bit || cache_not_validated(crecp)) ))
|
||||
{
|
||||
do
|
||||
{
|
||||
@@ -1975,7 +2000,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
|
||||
/* If the client asked for DNSSEC don't use cached data. */
|
||||
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
|
||||
(rd_bit && (!do_bit || cache_validated(crecp)) ))
|
||||
(rd_bit && (!do_bit || cache_not_validated(crecp)) ))
|
||||
do
|
||||
{
|
||||
int stale_flag = 0;
|
||||
@@ -2157,19 +2182,19 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
|
||||
if (!ans)
|
||||
{
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_RR | F_NXDOMAIN)) &&
|
||||
rd_bit && (!do_bit || cache_validated(crecp)))
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_RR | F_NXDOMAIN)) && rd_bit)
|
||||
do
|
||||
{
|
||||
int flags = crecp->flags;
|
||||
unsigned short rrtype;
|
||||
|
||||
|
||||
if (flags & F_KEYTAG)
|
||||
rrtype = crecp->addr.rrblock.rrtype;
|
||||
else
|
||||
rrtype = crecp->addr.rrdata.rrtype;
|
||||
|
||||
if ((flags & F_NXDOMAIN) || rrtype == qtype)
|
||||
if (((flags & F_NXDOMAIN) || rrtype == qtype) &&
|
||||
(!do_bit || cache_not_validated(crecp)))
|
||||
{
|
||||
char *rrdata = NULL;
|
||||
unsigned short rrlen = 0;
|
||||
@@ -2232,14 +2257,15 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
}
|
||||
|
||||
|
||||
if (qtype != T_ANY && !ans && rr_on_list(daemon->filter_rr, qtype))
|
||||
if (qtype != T_ANY && !ans && rr_on_list(daemon->filter_rr, qtype) && !do_bit)
|
||||
{
|
||||
/* We don't have a cached answer and when we get an answer from upstream we're going to
|
||||
filter it anyway. If we have a cached answer for the domain for another RRtype then
|
||||
that may be enough to tell us if the answer should be NODATA and save the round trip.
|
||||
Cached NXDOMAIN has already been handled, so here we look for any record for the domain,
|
||||
since its existence allows us to return a NODATA answer. Note that we never set the AD flag,
|
||||
since we didn't authenticate the record. */
|
||||
since we didn't authenticate the record; this doesn't work if we want auth data, so
|
||||
don't use this shortcut in that case. */
|
||||
|
||||
if (cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_RR | F_CNAME))
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user