Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f20afb1a8 | ||
|
|
f61afcfc70 | ||
|
|
961daf8f92 | ||
|
|
64a16cb376 | ||
|
|
ea6b0b2665 | ||
|
|
89df73ac05 | ||
|
|
7d3f3c9983 | ||
|
|
1bdbea2461 | ||
|
|
dfb1f7ccf1 | ||
|
|
b5d1b20727 | ||
|
|
2a407a76be | ||
|
|
d1640a6338 | ||
|
|
26b5c40d95 | ||
|
|
0b3ecf7432 | ||
|
|
8f9bd61505 | ||
|
|
ea28d0ef8a | ||
|
|
4a8c098840 | ||
|
|
ffa4628faa | ||
|
|
e10a9239e1 | ||
|
|
51f7bc924c | ||
|
|
6c0bf79078 | ||
|
|
4b03170920 | ||
|
|
1de6bbc108 | ||
|
|
023ace8e54 | ||
|
|
74d4fcd756 | ||
|
|
9eaa91bfc3 | ||
|
|
484bd75ce4 | ||
|
|
4c30e9602b | ||
|
|
b260d222af | ||
|
|
6528d62cd2 | ||
|
|
b7cf754f6f | ||
|
|
14e3f6ba19 | ||
|
|
a8c1474562 | ||
|
|
8b8a4148ec | ||
|
|
9e147480ed | ||
|
|
4c0aecc685 | ||
|
|
d556b8a5d5 | ||
|
|
e7c0d7b348 | ||
|
|
17360439dc | ||
|
|
9e169a9bea | ||
|
|
305cb79c57 | ||
|
|
141a26f979 | ||
|
|
cfcafdd27c | ||
|
|
f1204a875e |
86
CHANGELOG
86
CHANGELOG
@@ -1,3 +1,85 @@
|
||||
version 2.85
|
||||
Fix problem with DNS retries in 2.83/2.84.
|
||||
The new logic in 2.83/2.84 which merges distinct requests
|
||||
for the same domain causes problems with clients which do
|
||||
retries as distinct requests (differing IDs and/or source ports.)
|
||||
The retries just get piggy-backed on the first, failed, request.
|
||||
The logic is now changed so that distinct requests for repeated
|
||||
queries still get merged into a single ID/source port, but
|
||||
they now always trigger a re-try upstream.
|
||||
Thanks to Nicholas Mu for his analysis.
|
||||
|
||||
Tweak sort order of tags in get-version. v2.84 sorts
|
||||
before v2.83, but v2.83 sorts before v2.83rc1 and 2.83rc1
|
||||
sorts before v2.83test1. This fixes the problem which lead
|
||||
to 2.84 announcing itself as 2.84rc2.
|
||||
|
||||
Avoid treating a --dhcp-host which has an IPv6 address
|
||||
as eligible for use with DHCPv4 on the grounds that it has
|
||||
no address, and vice-versa. Thanks to Viktor Papp for
|
||||
spotting the problem. (This bug was fixed was back in 2.67, and
|
||||
then regressed in 2.81).
|
||||
|
||||
Add --dynamic-host option: A and AAAA records which take their
|
||||
network part from the network of a local interface. Useful
|
||||
for routers with dynamically prefixes. Thanks
|
||||
to Fred F for the suggestion.
|
||||
|
||||
Teach --bogus-nxdomain and --ignore-address to take an IPv4 subnet.
|
||||
|
||||
Use random source ports where possible if source
|
||||
addresses/interfaces in use.
|
||||
CVE-2021-3448 applies. Thanks to Petr Menšík for spotting this.
|
||||
It's possible to specify the source address or interface to be
|
||||
used when contacting upstream name servers: server=8.8.8.8@1.2.3.4
|
||||
or server=8.8.8.8@1.2.3.4#66 or server=8.8.8.8@eth0, and all of
|
||||
these have, until now, used a single socket, bound to a fixed
|
||||
port. This was originally done to allow an error (non-existent
|
||||
interface, or non-local address) to be detected at start-up. This
|
||||
means that any upstream servers specified in such a way don't use
|
||||
random source ports, and are more susceptible to cache-poisoning
|
||||
attacks.
|
||||
We now use random ports where possible, even when the
|
||||
source is specified, so server=8.8.8.8@1.2.3.4 or
|
||||
server=8.8.8.8@eth0 will use random source
|
||||
ports. server=8.8.8.8@1.2.3.4#66 or any use of --query-port will
|
||||
use the explicitly configured port, and should only be done with
|
||||
understanding of the security implications.
|
||||
Note that this change changes non-existing interface, or non-local
|
||||
source address errors from fatal to run-time. The error will be
|
||||
logged and communication with the server not possible.
|
||||
|
||||
Change the method of allocation of random source ports for DNS.
|
||||
Previously, without min-port or max-port configured, dnsmasq would
|
||||
default to the compiled in defaults for those, which are 1024 and
|
||||
65535. Now, when neither are configured, it defaults instead to
|
||||
the kernel's ephemeral port range, which is typically
|
||||
32768 to 60999 on Linux systems. This change eliminates the
|
||||
possibility that dnsmasq may be using a registered port > 1024
|
||||
when a long-running daemon starts up and wishes to claim it.
|
||||
This change does likely slightly reduce the number of random ports
|
||||
and therefore the protection from reply spoofing. The older
|
||||
behaviour can be restored using the min-port and max-port config
|
||||
switches should that be a concern.
|
||||
|
||||
Scale the size of the DNS random-port pool based on the
|
||||
value of the --dns-forward-max configuration.
|
||||
|
||||
Tweak TFTP code to check sender of all received packets, as
|
||||
specified in RFC 1350 para 4.
|
||||
|
||||
|
||||
version 2.84
|
||||
Fix a problem, introduced in 2.83, which could see DNS replies
|
||||
being sent via the wrong socket. On machines running both
|
||||
IPv4 and IPv6 this could result in sporadic messages of
|
||||
the form "failed to send packet: Network is unreachable" and
|
||||
the lost of the query. Since the error is sporadic and of
|
||||
low probability, the client retry would normally succeed.
|
||||
|
||||
Change HAVE_NETTLEHASH compile-time to HAVE_CRYPTOHASH.
|
||||
|
||||
|
||||
version 2.83
|
||||
Use the values of --min-port and --max-port in outgoing
|
||||
TCP connections to upstream DNS servers.
|
||||
@@ -19,13 +101,13 @@ version 2.83
|
||||
|
||||
Handle multiple identical near simultaneous DNS queries better.
|
||||
Previously, such queries would all be forwarded
|
||||
independently. This is, in theory, inefficent but in practise
|
||||
independently. This is, in theory, inefficient but in practise
|
||||
not a problem, _except_ that is means that an answer for any
|
||||
of the forwarded queries will be accepted and cached.
|
||||
An attacker can send a query multiple times, and for each repeat,
|
||||
another {port, ID} becomes capable of accepting the answer he is
|
||||
sending in the blind, to random IDs and ports. The chance of a
|
||||
succesful attack is therefore multiplied by the number of repeats
|
||||
successful attack is therefore multiplied by the number of repeats
|
||||
of the query. The new behaviour detects repeated queries and
|
||||
merely stores the clients sending repeats so that when the
|
||||
first query completes, the answer can be sent to all the
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
# If we can find one which matches $v[0-9].* then we assume it's
|
||||
# a version-number tag, else we just use the whole string.
|
||||
# If there is more than one v[0-9].* tag, sort them and use the
|
||||
# first. This favours, eg v2.63 over 2.63rc6.
|
||||
# first. The insane arguments to the sort command are to ensure
|
||||
# that, eg v2.64 comes before v2.63, but v2.63 comes before v2.63rc1
|
||||
# and v2.63rc1 comes before v2.63test1
|
||||
|
||||
|
||||
# Change directory to the toplevel source directory.
|
||||
if test -z "$1" || ! test -d "$1" || ! cd "$1"; then
|
||||
@@ -28,7 +31,7 @@ else
|
||||
vers=`cat $1/VERSION | sed 's/[(), ]/,/ g' | tr ',' '\n' | grep ^v[0-9]`
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "${vers}" | sort -r | head -n 1 | sed 's/^v//'
|
||||
echo "${vers}" | sort -k1.2,1.5Vr -k1.6,1.6 -k1.8,1.9Vr -k1.10,1.11Vr | head -n 1 | sed 's/^v//'
|
||||
else
|
||||
cat $1/VERSION
|
||||
fi
|
||||
|
||||
26
debian/changelog
vendored
26
debian/changelog
vendored
@@ -1,3 +1,29 @@
|
||||
dnsmasq (2.85-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
* Includes fix to CVE-2021-3448.
|
||||
* Fix manpage typos. (closes: #986150)
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Sat, 03 Apr 2021 22:17:23 +0100
|
||||
|
||||
dnsmasq (2.84-1.2) unstable; urgency=medium
|
||||
|
||||
* Non-maintainer upload.
|
||||
* Bump old-version in dpkg-maintscript-helper dir_to_symlink calls to also
|
||||
clean up after upgrades to an earlier version in testing.
|
||||
|
||||
-- Andreas Beckmann <anbe@debian.org> Thu, 01 Apr 2021 16:01:51 +0200
|
||||
|
||||
dnsmasq (2.84-1.1) unstable; urgency=medium
|
||||
|
||||
* Non-maintainer upload.
|
||||
* Fix symlink to directory conversion for /usr/share/doc/dnsmasq.
|
||||
This is achieved by directly calling dpkg-maintscript-helper in the preinst,
|
||||
postinst, and postrm scripts, since the package does not use debhelper.
|
||||
(Closes: #985282)
|
||||
|
||||
-- Sébastien Villemot <sebastien@debian.org> Sun, 28 Mar 2021 10:55:07 +0200
|
||||
|
||||
dnsmasq (2.84-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
|
||||
3
debian/postinst
vendored
3
debian/postinst
vendored
@@ -1,6 +1,9 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# /usr/share/doc/dnsmasq was a symlink in versions < 2.81-1 (see #985282)
|
||||
dpkg-maintscript-helper symlink_to_dir /usr/share/doc/dnsmasq dnsmasq-base 2.84-1.2~ dnsmasq -- "$@"
|
||||
|
||||
# Code copied from dh_systemd_enable ----------------------
|
||||
# This will only remove masks created by d-s-h on package removal.
|
||||
deb-systemd-helper unmask dnsmasq.service >/dev/null || true
|
||||
|
||||
3
debian/postrm
vendored
3
debian/postrm
vendored
@@ -1,6 +1,9 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# /usr/share/doc/dnsmasq was a symlink in versions < 2.81-1 (see #985282)
|
||||
dpkg-maintscript-helper symlink_to_dir /usr/share/doc/dnsmasq dnsmasq-base 2.84-1.2~ dnsmasq -- "$@"
|
||||
|
||||
if [ purge = "$1" ]; then
|
||||
update-rc.d dnsmasq remove >/dev/null
|
||||
fi
|
||||
|
||||
5
debian/preinst
vendored
Normal file
5
debian/preinst
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# /usr/share/doc/dnsmasq was a symlink in versions < 2.81-1 (see #985282)
|
||||
dpkg-maintscript-helper symlink_to_dir /usr/share/doc/dnsmasq dnsmasq-base 2.84-1.2~ dnsmasq -- "$@"
|
||||
2
debian/rules
vendored
2
debian/rules
vendored
@@ -176,7 +176,7 @@ binary-indep: checkroot
|
||||
-d debian/trees/daemon/usr/lib/tmpfiles.d \
|
||||
-d debian/trees/daemon/etc/insserv.conf.d
|
||||
install -m 644 debian/conffiles debian/trees/daemon/DEBIAN
|
||||
install -m 755 debian/postinst debian/postrm debian/prerm debian/trees/daemon/DEBIAN
|
||||
install -m 755 debian/postinst debian/postrm debian/preinst debian/prerm debian/trees/daemon/DEBIAN
|
||||
if ! dpkg-vendor --derives-from Ubuntu; then \
|
||||
rm -f debian/dnsmasq.postinst.debhelper debian/dnsmasq.postrm.debhelper; \
|
||||
dh_runit -pdnsmasq -Pdebian/trees/daemon; \
|
||||
|
||||
@@ -135,6 +135,9 @@ running, will go exclusively to the file.) When logging to a file,
|
||||
dnsmasq will close and reopen the file when it receives SIGUSR2. This
|
||||
allows the log file to be rotated without stopping dnsmasq.
|
||||
.TP
|
||||
.B --log-debug
|
||||
Enable extra logging intended for debugging rather than information.
|
||||
.TP
|
||||
.B --log-async[=<lines>]
|
||||
Enable asynchronous logging and optionally set the limit on the
|
||||
number of lines
|
||||
@@ -181,7 +184,7 @@ OS: this was the default behaviour in versions prior to 2.43.
|
||||
.B --min-port=<port>
|
||||
Do not use ports less than that given as source for outbound DNS
|
||||
queries. Dnsmasq picks random ports as source for outbound queries:
|
||||
when this option is given, the ports used will always to larger
|
||||
when this option is given, the ports used will always be larger
|
||||
than that specified. Useful for systems behind firewalls. If not specified,
|
||||
defaults to 1024.
|
||||
.TP
|
||||
@@ -296,7 +299,7 @@ option requires non-standard networking APIs and it is only available
|
||||
under Linux. On other platforms it falls-back to \fB--bind-interfaces\fP mode.
|
||||
.TP
|
||||
.B \-y, --localise-queries
|
||||
Return answers to DNS queries from /etc/hosts and \fB--interface-name\fP which depend on the interface over which the query was
|
||||
Return answers to DNS queries from /etc/hosts and \fB--interface-name\fP and \fB--dynamic-host\fP which depend on the interface over which the query was
|
||||
received. If a name has more than one address associated with
|
||||
it, and at least one of those addresses is on the same subnet as the
|
||||
interface to which the query was sent, then return only the
|
||||
@@ -323,8 +326,8 @@ are re-written. So
|
||||
.B --alias=192.168.0.10-192.168.0.40,10.0.0.0,255.255.255.0
|
||||
maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40
|
||||
.TP
|
||||
.B \-B, --bogus-nxdomain=<ipaddr>
|
||||
Transform replies which contain the IP address given into "No such
|
||||
.B \-B, --bogus-nxdomain=<ipaddr>[/prefix]
|
||||
Transform replies which contain the IP specified address or subnet into "No such
|
||||
domain" replies. This is intended to counteract a devious move made by
|
||||
Verisign in September 2003 when they started returning the address of
|
||||
an advertising web page in response to queries for unregistered names,
|
||||
@@ -332,8 +335,8 @@ instead of the correct NXDOMAIN response. This option tells dnsmasq to
|
||||
fake the correct response when it sees this behaviour. As at Sept 2003
|
||||
the IP address being returned by Verisign is 64.94.110.11
|
||||
.TP
|
||||
.B --ignore-address=<ipaddr>
|
||||
Ignore replies to A-record queries which include the specified address.
|
||||
.B --ignore-address=<ipaddr>[/prefix]
|
||||
Ignore replies to A-record queries which include the specified address or subnet.
|
||||
No error is generated, dnsmasq simply continues to listen for another reply.
|
||||
This is useful to defeat blocking strategies which rely on quickly supplying a
|
||||
forged answer to a DNS request for certain domain, before the correct answer can arrive.
|
||||
@@ -428,7 +431,7 @@ Tells dnsmasq to never forward A or AAAA queries for plain names, without dots
|
||||
or domain parts, to upstream nameservers. If the name is not known
|
||||
from /etc/hosts or DHCP then a "not found" answer is returned.
|
||||
.TP
|
||||
.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>]][@<source-ip>|<interface>[#<port>]]
|
||||
.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>]][@<interface>][@<source-ip>[#<port>]]
|
||||
Specify IP address of upstream servers directly. Setting this flag does
|
||||
not suppress reading of /etc/resolv.conf, use \fB--no-resolv\fP to do that. If one or more
|
||||
optional domains are given, that server is used only for those domains
|
||||
@@ -489,7 +492,7 @@ source address specified but the port may be specified directly as
|
||||
part of the source address. Forcing queries to an interface is not
|
||||
implemented on all platforms supported by dnsmasq.
|
||||
.TP
|
||||
.B --rev-server=<ip-address>/<prefix-len>[,<ipaddr>][#<port>][@<source-ip>|<interface>[#<port>]]
|
||||
.B --rev-server=<ip-address>/<prefix-len>[,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
|
||||
This is functionally the same as
|
||||
.B --server,
|
||||
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
|
||||
@@ -591,6 +594,12 @@ If the time-to-live is given, it overrides the default, which is zero
|
||||
or the value of \fB--local-ttl\fP. The value is a positive integer and gives
|
||||
the time-to-live in seconds.
|
||||
.TP
|
||||
.B --dynamic-host=<name>,[IPv4-address],[IPv6-address],<interface>
|
||||
Add A, AAAA and PTR records to the DNS in the same subnet as the specified interface. The address is derived from the network part of each address associated with the interface, and the host part from the specified address. For example
|
||||
.B --dynamic-host=example.com,0.0.0.8,eth0
|
||||
will, when eth0 has the address 192.168.78.x and netmask 255.255.255.0 give the
|
||||
name example.com an A record for 192.168.78.8. The same principle applies to IPv6 addresses. Note that if an interface has more than one address, more than one A or AAAA record will be created. The TTL of the records is always zero, and any changes to interface addresses will be immediately reflected in them.
|
||||
.TP
|
||||
.B \-Y, --txt-record=<name>[[,<text>],<text>]
|
||||
Return a TXT DNS record. The value of TXT record is a set of strings,
|
||||
so any number may be included, delimited by commas; use quotes to put
|
||||
@@ -1045,6 +1054,19 @@ option, but aliases are possible by using CNAMEs. (See
|
||||
.B --cname
|
||||
).
|
||||
|
||||
More than one
|
||||
.B --dhcp-host
|
||||
can be associated (by name, hardware address or UID) with a host. Which one is used
|
||||
(and therefore which address is allocated by DHCP and appears in the DNS) depends
|
||||
on the subnet on which the host last obtained a DHCP lease:
|
||||
the
|
||||
.B --dhcp-host
|
||||
with an address within the subnet is used. If more than one address is within the subnet,
|
||||
the result is undefined. A corollary to this is that the name associated with a host using
|
||||
.B --dhcp-host
|
||||
does not appear in the DNS until the host obtains a DHCP lease.
|
||||
|
||||
|
||||
The special keyword "ignore"
|
||||
tells dnsmasq to never offer a DHCP lease to a machine. The machine
|
||||
can be specified by hardware address, client ID or hostname, for
|
||||
@@ -1436,7 +1458,7 @@ functions when supported by a suitable DHCP server.
|
||||
This specifies a boot option which may appear in a PXE boot menu. <CSA> is
|
||||
client system type, only services of the correct type will appear in a
|
||||
menu. The known types are x86PC, PC98, IA64_EFI, Alpha, Arc_x86,
|
||||
Intel_Lean_Client, IA32_EFI, X86-64_EFI, Xscale_EFI, BC_EFI, ARM32_EFI and ARM64_EFI; an
|
||||
Intel_Lean_Client, IA32_EFI, x86-64_EFI, Xscale_EFI, BC_EFI, ARM32_EFI and ARM64_EFI; an
|
||||
integer may be used for other types. The
|
||||
parameter after the menu text may be a file name, in which case dnsmasq acts as a
|
||||
boot server and directs the PXE client to download the file by TFTP,
|
||||
@@ -2367,6 +2389,8 @@ IPv4 and IPv6 addresses from /etc/hosts (and
|
||||
.B --host-record
|
||||
and
|
||||
.B --interface-name
|
||||
and
|
||||
.B ---dynamic-host
|
||||
provided the address falls into one of the subnets specified in the
|
||||
.B --auth-zone.
|
||||
.PP
|
||||
|
||||
1043
po/pt_BR.po
1043
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,6 @@
|
||||
#define UDP_TEST_TIME 60 /* How often to reset our idea of max packet size. */
|
||||
#define SERVERS_LOGGED 30 /* Only log this many servers when logging state */
|
||||
#define LOCALS_LOGGED 8 /* Only log this many local addresses when logging state */
|
||||
#define RANDOM_SOCKS 64 /* max simultaneous random ports */
|
||||
#define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
|
||||
#define CACHESIZ 150 /* default cache size */
|
||||
#define TTL_FLOOR_LIMIT 3600 /* don't allow --min-cache-ttl to raise TTL above this under any circumstances */
|
||||
|
||||
32
src/crypto.c
32
src/crypto.c
@@ -18,11 +18,20 @@
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
|
||||
/* Minimal version of nettle */
|
||||
#define MIN_VERSION(major, minor) (NETTLE_VERSION_MAJOR == (major) && NETTLE_VERSION_MINOR >= (minor)) || \
|
||||
(NETTLE_VERSION_MAJOR > (major))
|
||||
|
||||
#include <nettle/rsa.h>
|
||||
#include <nettle/ecdsa.h>
|
||||
#include <nettle/ecc-curve.h>
|
||||
#if !defined(NETTLE_VERSION_MAJOR)
|
||||
#define NETTLE_VERSION_MAJOR 2
|
||||
#endif
|
||||
#if MIN_VERSION(3, 1)
|
||||
#include <nettle/eddsa.h>
|
||||
#if NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR >= 6
|
||||
#endif
|
||||
#if MIN_VERSION(3, 6)
|
||||
# include <nettle/gostdsa.h>
|
||||
#endif
|
||||
#endif
|
||||
@@ -115,10 +124,8 @@ const struct nettle_hash *hash_find(char *name)
|
||||
|
||||
/* libnettle >= 3.4 provides nettle_lookup_hash() which avoids nasty ABI
|
||||
incompatibilities if sizeof(nettle_hashes) changes between library
|
||||
versions. It also #defines nettle_hashes, so use that to tell
|
||||
if we have the new facilities. */
|
||||
|
||||
#ifdef nettle_hashes
|
||||
versions. */
|
||||
#if MIN_VERSION(3, 4)
|
||||
return nettle_lookup_hash(name);
|
||||
#else
|
||||
{
|
||||
@@ -171,7 +178,7 @@ int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **diges
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* defined(HAVE_DNSSEC) || defined(HAVE_CRYPTOHASH) */
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
|
||||
@@ -238,7 +245,7 @@ static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len
|
||||
static struct ecc_point *key_256 = NULL, *key_384 = NULL;
|
||||
static mpz_t x, y;
|
||||
static struct dsa_signature *sig_struct;
|
||||
#if NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR < 4
|
||||
#if !MIN_VERSION(3, 4)
|
||||
#define nettle_get_secp_256r1() (&nettle_secp_256r1)
|
||||
#define nettle_get_secp_384r1() (&nettle_secp_384r1)
|
||||
#endif
|
||||
@@ -301,7 +308,7 @@ static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len
|
||||
return nettle_ecdsa_verify(key, digest_len, digest, sig_struct);
|
||||
}
|
||||
|
||||
#if NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR >= 6
|
||||
#if MIN_VERSION(3, 6)
|
||||
static int dnsmasq_gostdsa_verify(struct blockdata *key_data, unsigned int key_len,
|
||||
unsigned char *sig, size_t sig_len,
|
||||
unsigned char *digest, size_t digest_len, int algo)
|
||||
@@ -342,6 +349,7 @@ static int dnsmasq_gostdsa_verify(struct blockdata *key_data, unsigned int key_l
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MIN_VERSION(3, 1)
|
||||
static int dnsmasq_eddsa_verify(struct blockdata *key_data, unsigned int key_len,
|
||||
unsigned char *sig, size_t sig_len,
|
||||
unsigned char *digest, size_t digest_len, int algo)
|
||||
@@ -368,7 +376,7 @@ static int dnsmasq_eddsa_verify(struct blockdata *key_data, unsigned int key_len
|
||||
((struct null_hash_digest *)digest)->buff,
|
||||
sig);
|
||||
|
||||
#if NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR >= 6
|
||||
#if MIN_VERSION(3, 6)
|
||||
case 16:
|
||||
if (key_len != ED448_KEY_SIZE ||
|
||||
sig_len != ED448_SIGNATURE_SIZE)
|
||||
@@ -384,6 +392,7 @@ static int dnsmasq_eddsa_verify(struct blockdata *key_data, unsigned int key_len
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
|
||||
unsigned char *digest, size_t digest_len, int algo)
|
||||
@@ -399,16 +408,17 @@ static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key
|
||||
case 5: case 7: case 8: case 10:
|
||||
return dnsmasq_rsa_verify;
|
||||
|
||||
#if NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR >= 6
|
||||
#if MIN_VERSION(3, 6)
|
||||
case 12:
|
||||
return dnsmasq_gostdsa_verify;
|
||||
#endif
|
||||
|
||||
case 13: case 14:
|
||||
return dnsmasq_ecdsa_verify;
|
||||
|
||||
#if MIN_VERSION(3, 1)
|
||||
case 15: case 16:
|
||||
return dnsmasq_eddsa_verify;
|
||||
#endif
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -280,31 +280,29 @@ static int is_config_in_context(struct dhcp_context *context, struct dhcp_config
|
||||
{
|
||||
if (!context) /* called via find_config() from lease_update_from_configs() */
|
||||
return 1;
|
||||
|
||||
if (!(config->flags & (CONFIG_ADDR | CONFIG_ADDR6)))
|
||||
return 1;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (context->flags & CONTEXT_V6)
|
||||
{
|
||||
struct addrlist *addr_list;
|
||||
|
||||
if (!(config->flags & CONFIG_ADDR6))
|
||||
return 1;
|
||||
|
||||
for (; context; context = context->current)
|
||||
for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
|
||||
{
|
||||
if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
|
||||
return 1;
|
||||
|
||||
if (is_same_net6(&addr_list->addr.addr6, &context->start6, context->prefix))
|
||||
return 1;
|
||||
}
|
||||
if (config->flags & CONFIG_ADDR6)
|
||||
for (; context; context = context->current)
|
||||
for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
|
||||
{
|
||||
if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
|
||||
return 1;
|
||||
|
||||
if (is_same_net6(&addr_list->addr.addr6, &context->start6, context->prefix))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (!(config->flags & CONFIG_ADDR))
|
||||
return 1;
|
||||
|
||||
for (; context; context = context->current)
|
||||
if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
|
||||
return 1;
|
||||
|
||||
@@ -237,9 +237,16 @@ int main (int argc, char **argv)
|
||||
die(_("Ubus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF);
|
||||
#endif
|
||||
|
||||
/* Handle only one of min_port/max_port being set. */
|
||||
if (daemon->min_port != 0 && daemon->max_port == 0)
|
||||
daemon->max_port = MAX_PORT;
|
||||
|
||||
if (daemon->max_port != 0 && daemon->min_port == 0)
|
||||
daemon->min_port = MIN_PORT;
|
||||
|
||||
if (daemon->max_port < daemon->min_port)
|
||||
die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF);
|
||||
|
||||
|
||||
now = dnsmasq_time();
|
||||
|
||||
if (daemon->auth_zones)
|
||||
@@ -392,6 +399,14 @@ int main (int argc, char **argv)
|
||||
cache_init();
|
||||
blockdata_init();
|
||||
hash_questions_init();
|
||||
|
||||
/* Scale random socket pool by ftabsize, but
|
||||
limit it based on available fds. */
|
||||
daemon->numrrand = daemon->ftabsize/2;
|
||||
if (daemon->numrrand > max_fd/3)
|
||||
daemon->numrrand = max_fd/3;
|
||||
/* safe_malloc returns zero'd memory */
|
||||
daemon->randomsocks = safe_malloc(daemon->numrrand * sizeof(struct randfd));
|
||||
}
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
@@ -980,7 +995,7 @@ int main (int argc, char **argv)
|
||||
a single file will be sent to may clients (the file only needs
|
||||
one fd). */
|
||||
|
||||
max_fd -= 30; /* use other than TFTP */
|
||||
max_fd -= 30 + daemon->numrrand; /* use other than TFTP */
|
||||
|
||||
if (max_fd < 0)
|
||||
max_fd = 5;
|
||||
@@ -1672,6 +1687,7 @@ static int set_dns_listeners(time_t now)
|
||||
{
|
||||
struct serverfd *serverfdp;
|
||||
struct listener *listener;
|
||||
struct randfd_list *rfl;
|
||||
int wait = 0, i;
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
@@ -1692,11 +1708,14 @@ static int set_dns_listeners(time_t now)
|
||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
poll_listen(serverfdp->fd, POLLIN);
|
||||
|
||||
if (daemon->port != 0 && !daemon->osport)
|
||||
for (i = 0; i < RANDOM_SOCKS; i++)
|
||||
if (daemon->randomsocks[i].refcount != 0)
|
||||
poll_listen(daemon->randomsocks[i].fd, POLLIN);
|
||||
|
||||
for (i = 0; i < daemon->numrrand; i++)
|
||||
if (daemon->randomsocks[i].refcount != 0)
|
||||
poll_listen(daemon->randomsocks[i].fd, POLLIN);
|
||||
|
||||
/* Check overflow random sockets too. */
|
||||
for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next)
|
||||
poll_listen(rfl->rfd->fd, POLLIN);
|
||||
|
||||
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||
{
|
||||
/* only listen for queries if we have resources */
|
||||
@@ -1733,18 +1752,23 @@ static void check_dns_listeners(time_t now)
|
||||
{
|
||||
struct serverfd *serverfdp;
|
||||
struct listener *listener;
|
||||
struct randfd_list *rfl;
|
||||
int i;
|
||||
int pipefd[2];
|
||||
|
||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
if (poll_check(serverfdp->fd, POLLIN))
|
||||
reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);
|
||||
reply_query(serverfdp->fd, now);
|
||||
|
||||
if (daemon->port != 0 && !daemon->osport)
|
||||
for (i = 0; i < RANDOM_SOCKS; i++)
|
||||
if (daemon->randomsocks[i].refcount != 0 &&
|
||||
poll_check(daemon->randomsocks[i].fd, POLLIN))
|
||||
reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
|
||||
for (i = 0; i < daemon->numrrand; i++)
|
||||
if (daemon->randomsocks[i].refcount != 0 &&
|
||||
poll_check(daemon->randomsocks[i].fd, POLLIN))
|
||||
reply_query(daemon->randomsocks[i].fd, now);
|
||||
|
||||
/* Check overflow random sockets too. */
|
||||
for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next)
|
||||
if (poll_check(rfl->rfd->fd, POLLIN))
|
||||
reply_query(rfl->rfd->fd, now);
|
||||
|
||||
/* Races. The child process can die before we read all of the data from the
|
||||
pipe, or vice versa. Therefore send tcp_pids to zero when we wait() the
|
||||
|
||||
@@ -95,11 +95,7 @@ typedef unsigned long long u64;
|
||||
#if defined(HAVE_SOLARIS_NETWORK)
|
||||
# include <sys/sockio.h>
|
||||
#endif
|
||||
#if defined(HAVE_POLL_H)
|
||||
# include <poll.h>
|
||||
#else
|
||||
# include <sys/poll.h>
|
||||
#endif
|
||||
#include <poll.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/un.h>
|
||||
@@ -273,7 +269,8 @@ struct event_desc {
|
||||
#define OPT_IGNORE_CLID 59
|
||||
#define OPT_SINGLE_PORT 60
|
||||
#define OPT_LEASE_RENEW 61
|
||||
#define OPT_LAST 62
|
||||
#define OPT_LOG_DEBUG 62
|
||||
#define OPT_LAST 63
|
||||
|
||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||
@@ -281,11 +278,13 @@ struct event_desc {
|
||||
#define option_val(x) ((1u) << ((x) % OPTION_BITS))
|
||||
#define option_bool(x) (option_var(x) & option_val(x))
|
||||
|
||||
/* extra flags for my_syslog, we use a couple of facilities since they are known
|
||||
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
|
||||
/* extra flags for my_syslog, we use facilities since they are known
|
||||
not to occupy the same bits as priorities, no matter how syslog.h is set up.
|
||||
MS_DEBUG messages are suppressed unless --log-debug is set. */
|
||||
#define MS_TFTP LOG_USER
|
||||
#define MS_DHCP LOG_DAEMON
|
||||
#define MS_SCRIPT LOG_MAIL
|
||||
#define MS_DEBUG LOG_NEWS
|
||||
|
||||
/* Note that this is used widely as a container for IPv4/IPv6 addresses,
|
||||
so for that reason, was well as to avoid wasting memory in almost every
|
||||
@@ -326,7 +325,7 @@ union all_addr {
|
||||
|
||||
|
||||
struct bogus_addr {
|
||||
struct in_addr addr;
|
||||
struct in_addr addr, mask;
|
||||
struct bogus_addr *next;
|
||||
};
|
||||
|
||||
@@ -427,10 +426,17 @@ struct host_record {
|
||||
struct host_record *next;
|
||||
};
|
||||
|
||||
#define IN4 1
|
||||
#define IN6 2
|
||||
#define INP4 4
|
||||
#define INP6 8
|
||||
|
||||
struct interface_name {
|
||||
char *name; /* domain name */
|
||||
char *intr; /* interface name */
|
||||
int family; /* AF_INET, AF_INET6 or zero for both */
|
||||
int flags;
|
||||
struct in_addr proto4;
|
||||
struct in6_addr proto6;
|
||||
struct addrlist *addr;
|
||||
struct interface_name *next;
|
||||
};
|
||||
@@ -543,13 +549,20 @@ struct serverfd {
|
||||
};
|
||||
|
||||
struct randfd {
|
||||
struct server *serv;
|
||||
int fd;
|
||||
unsigned short refcount, family;
|
||||
unsigned short refcount; /* refcount == 0xffff means overflow record. */
|
||||
};
|
||||
|
||||
|
||||
struct randfd_list {
|
||||
struct randfd *rfd;
|
||||
struct randfd_list *next;
|
||||
};
|
||||
|
||||
struct server {
|
||||
union mysockaddr addr, source_addr;
|
||||
char interface[IF_NAMESIZE+1];
|
||||
unsigned int ifindex; /* corresponding to interface, above */
|
||||
struct serverfd *sfd;
|
||||
char *domain; /* set if this server only handles a domain. */
|
||||
int flags, tcpfd, edns_pktsz;
|
||||
@@ -673,8 +686,7 @@ struct frec {
|
||||
struct frec_src *next;
|
||||
} frec_src;
|
||||
struct server *sentto; /* NULL means free */
|
||||
struct randfd *rfd4;
|
||||
struct randfd *rfd6;
|
||||
struct randfd_list *rfds;
|
||||
unsigned short new_id;
|
||||
int forwardall, flags;
|
||||
time_t time;
|
||||
@@ -1114,11 +1126,13 @@ extern struct daemon {
|
||||
int forwardcount;
|
||||
struct server *srv_save; /* Used for resend on DoD */
|
||||
size_t packet_len; /* " " */
|
||||
struct randfd *rfd_save; /* " " */
|
||||
int fd_save; /* " " */
|
||||
pid_t tcp_pids[MAX_PROCS];
|
||||
int tcp_pipes[MAX_PROCS];
|
||||
int pipe_to_parent;
|
||||
struct randfd randomsocks[RANDOM_SOCKS];
|
||||
int numrrand;
|
||||
struct randfd *randomsocks;
|
||||
struct randfd_list *rfl_spare, *rfl_poll;
|
||||
int v6pktinfo;
|
||||
struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */
|
||||
int log_id, log_display_id; /* ids of transactions for logging */
|
||||
@@ -1235,8 +1249,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
struct in_addr local_addr, struct in_addr local_netmask,
|
||||
time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
|
||||
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
struct bogus_addr *baddr, time_t now);
|
||||
int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr);
|
||||
time_t now);
|
||||
int check_for_ignored_address(struct dns_header *header, size_t qlen);
|
||||
int check_for_local_domain(char *name, time_t now);
|
||||
size_t resize_packet(struct dns_header *header, size_t plen,
|
||||
unsigned char *pheader, size_t hlen);
|
||||
@@ -1290,7 +1304,7 @@ void safe_strncpy(char *dest, const char *src, size_t size);
|
||||
void safe_pipe(int *fd, int read_noblock);
|
||||
void *whine_malloc(size_t size);
|
||||
int sa_len(union mysockaddr *addr);
|
||||
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
|
||||
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2);
|
||||
int hostname_isequal(const char *a, const char *b);
|
||||
int hostname_issubdomain(char *a, char *b);
|
||||
time_t dnsmasq_time(void);
|
||||
@@ -1341,7 +1355,7 @@ char *parse_server(char *arg, union mysockaddr *addr,
|
||||
int option_read_dynfile(char *file, int flags);
|
||||
|
||||
/* forward.c */
|
||||
void reply_query(int fd, int family, time_t now);
|
||||
void reply_query(int fd, time_t now);
|
||||
void receive_query(struct listener *listen, time_t now);
|
||||
unsigned char *tcp_request(int confd, time_t now,
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
||||
@@ -1351,13 +1365,12 @@ int send_from(int fd, int nowild, char *packet, size_t len,
|
||||
union mysockaddr *to, union all_addr *source,
|
||||
unsigned int iface);
|
||||
void resend_query(void);
|
||||
struct randfd *allocate_rfd(int family);
|
||||
void free_rfd(struct randfd *rfd);
|
||||
int allocate_rfd(struct randfd_list **fdlp, struct server *serv);
|
||||
void free_rfds(struct randfd_list **fdlp);
|
||||
|
||||
/* network.c */
|
||||
int indextoname(int fd, int index, char *name);
|
||||
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp);
|
||||
int random_sock(int family);
|
||||
void pre_allocate_sfds(void);
|
||||
int reload_servers(char *fname);
|
||||
void mark_servers(int flag);
|
||||
|
||||
709
src/forward.c
709
src/forward.c
@@ -16,14 +16,12 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash);
|
||||
static struct frec *lookup_frec_by_sender(unsigned short id,
|
||||
union mysockaddr *addr,
|
||||
void *hash);
|
||||
static struct frec *lookup_frec(unsigned short id, int fd, void *hash);
|
||||
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags);
|
||||
|
||||
static unsigned short get_id(void);
|
||||
static void free_frec(struct frec *f);
|
||||
static void query_full(time_t now);
|
||||
|
||||
/* Send a UDP packet with its source address set as "source"
|
||||
unless nowild is true, when we just send it with the kernel default */
|
||||
@@ -248,6 +246,53 @@ static unsigned int search_servers(time_t now, union all_addr **addrpp, unsigned
|
||||
return flags;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
static void set_outgoing_mark(struct frec *forward, int fd)
|
||||
{
|
||||
/* Copy connection mark of incoming query to outgoing connection. */
|
||||
unsigned int mark;
|
||||
if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark))
|
||||
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void log_query_mysockaddr(unsigned int flags, char *name, union mysockaddr *addr, char *arg)
|
||||
{
|
||||
if (addr->sa.sa_family == AF_INET)
|
||||
log_query(flags | F_IPV4, name, (union all_addr *)&addr->in.sin_addr, arg);
|
||||
else
|
||||
log_query(flags | F_IPV6, name, (union all_addr *)&addr->in6.sin6_addr, arg);
|
||||
}
|
||||
|
||||
static void server_send(struct server *server, int fd,
|
||||
const void *header, size_t plen, int flags)
|
||||
{
|
||||
while (retry_send(sendto(fd, header, plen, flags,
|
||||
&server->addr.sa,
|
||||
sa_len(&server->addr))));
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
static void server_send_log(struct server *server, int fd,
|
||||
const void *header, size_t plen, int dumpflags,
|
||||
unsigned int logflags, char *name, char *arg)
|
||||
{
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(dumpflags, (void *)header, (size_t)plen, NULL, &server->addr);
|
||||
#endif
|
||||
log_query_mysockaddr(logflags, name, &server->addr, arg);
|
||||
server_send(server, fd, header, plen, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int server_test_type(const struct server *server,
|
||||
const char *domain, int type, int extratype)
|
||||
{
|
||||
return (type == (server->flags & (SERV_TYPE | extratype)) &&
|
||||
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, server->domain)) &&
|
||||
!(server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)));
|
||||
}
|
||||
|
||||
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, time_t now,
|
||||
@@ -265,6 +310,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
#endif
|
||||
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
|
||||
unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL);
|
||||
int old_src = 0;
|
||||
|
||||
(void)do_bit;
|
||||
|
||||
if (header->hb4 & HB4_CD)
|
||||
@@ -278,8 +325,62 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
fwd_flags |= FREC_DO_QUESTION;
|
||||
#endif
|
||||
|
||||
/* may be no servers available. */
|
||||
if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash)))
|
||||
/* Check for retry on existing query */
|
||||
if (forward)
|
||||
old_src = 1;
|
||||
else if ((forward = lookup_frec_by_query(hash, fwd_flags)))
|
||||
{
|
||||
struct frec_src *src;
|
||||
|
||||
for (src = &forward->frec_src; src; src = src->next)
|
||||
if (src->orig_id == ntohs(header->id) &&
|
||||
sockaddr_isequal(&src->source, udpaddr))
|
||||
break;
|
||||
|
||||
if (src)
|
||||
old_src = 1;
|
||||
else
|
||||
{
|
||||
/* Existing query, but from new source, just add this
|
||||
client to the list that will get the reply.*/
|
||||
|
||||
/* Note whine_malloc() zeros memory. */
|
||||
if (!daemon->free_frec_src &&
|
||||
daemon->frec_src_count < daemon->ftabsize &&
|
||||
(daemon->free_frec_src = whine_malloc(sizeof(struct frec_src))))
|
||||
{
|
||||
daemon->frec_src_count++;
|
||||
daemon->free_frec_src->next = NULL;
|
||||
}
|
||||
|
||||
/* If we've been spammed with many duplicates, return REFUSED. */
|
||||
if (!daemon->free_frec_src)
|
||||
{
|
||||
query_full(now);
|
||||
goto frec_err;
|
||||
}
|
||||
|
||||
src = daemon->free_frec_src;
|
||||
daemon->free_frec_src = src->next;
|
||||
src->next = forward->frec_src.next;
|
||||
forward->frec_src.next = src;
|
||||
src->orig_id = ntohs(header->id);
|
||||
src->source = *udpaddr;
|
||||
src->dest = *dst_addr;
|
||||
src->log_id = daemon->log_id;
|
||||
src->iface = dst_iface;
|
||||
src->fd = udpfd;
|
||||
|
||||
/* closely spaced identical queries cannot be a try and a retry, so
|
||||
it's safe to wait for the reply from the first without
|
||||
forwarding the second. */
|
||||
if (difftime(now, forward->time) < 2)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* retry existing query */
|
||||
if (forward)
|
||||
{
|
||||
/* If we didn't get an answer advertising a maximal packet in EDNS,
|
||||
fall back to 1280, which should work everywhere on IPv6.
|
||||
@@ -307,34 +408,19 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
|
||||
PUTSHORT(SAFE_PKTSZ, pheader);
|
||||
|
||||
if (forward->sentto->addr.sa.sa_family == AF_INET)
|
||||
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (union all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
|
||||
else
|
||||
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (union all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
|
||||
if ((fd = allocate_rfd(&forward->rfds, forward->sentto)) != -1)
|
||||
server_send_log(forward->sentto, fd, header, plen,
|
||||
DUMP_SEC_QUERY,
|
||||
F_NOEXTRA | F_DNSSEC, "retry", "dnssec");
|
||||
|
||||
|
||||
if (forward->sentto->sfd)
|
||||
fd = forward->sentto->sfd->fd;
|
||||
else
|
||||
{
|
||||
if (forward->sentto->addr.sa.sa_family == AF_INET6)
|
||||
fd = forward->rfd6->fd;
|
||||
else
|
||||
fd = forward->rfd4->fd;
|
||||
}
|
||||
|
||||
while (retry_send(sendto(fd, (char *)header, plen, 0,
|
||||
&forward->sentto->addr.sa,
|
||||
sa_len(&forward->sentto->addr))));
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* retry on existing query, send to all available servers */
|
||||
/* retry on existing query, from original source. Send to all available servers */
|
||||
domain = forward->sentto->domain;
|
||||
forward->sentto->failed_queries++;
|
||||
if (!option_bool(OPT_ORDER))
|
||||
if (!option_bool(OPT_ORDER) && old_src)
|
||||
{
|
||||
forward->forwardall = 1;
|
||||
daemon->last_server = NULL;
|
||||
@@ -350,40 +436,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Query from new source, but the same query may be in progress
|
||||
from another source. If so, just add this client to the
|
||||
list that will get the reply.*/
|
||||
|
||||
if (!option_bool(OPT_ADD_MAC) && !option_bool(OPT_MAC_B64) &&
|
||||
(forward = lookup_frec_by_query(hash, fwd_flags)))
|
||||
{
|
||||
/* Note whine_malloc() zeros memory. */
|
||||
if (!daemon->free_frec_src &&
|
||||
daemon->frec_src_count < daemon->ftabsize &&
|
||||
(daemon->free_frec_src = whine_malloc(sizeof(struct frec_src))))
|
||||
{
|
||||
daemon->frec_src_count++;
|
||||
daemon->free_frec_src->next = NULL;
|
||||
}
|
||||
|
||||
/* If we've been spammed with many duplicates, just drop the query. */
|
||||
if (daemon->free_frec_src)
|
||||
{
|
||||
struct frec_src *new = daemon->free_frec_src;
|
||||
daemon->free_frec_src = new->next;
|
||||
new->next = forward->frec_src.next;
|
||||
forward->frec_src.next = new;
|
||||
new->orig_id = ntohs(header->id);
|
||||
new->source = *udpaddr;
|
||||
new->dest = *dst_addr;
|
||||
new->log_id = daemon->log_id;
|
||||
new->iface = dst_iface;
|
||||
new->fd = udpfd;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* new query */
|
||||
|
||||
if (gotname)
|
||||
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
|
||||
|
||||
@@ -392,6 +446,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
#endif
|
||||
type &= ~SERV_DO_DNSSEC;
|
||||
|
||||
/* may be no servers available. */
|
||||
if (daemon->servers && !flags)
|
||||
forward = get_new_frec(now, NULL, NULL);
|
||||
/* table full - flags == 0, return REFUSED */
|
||||
@@ -502,48 +557,21 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
|
||||
while (1)
|
||||
{
|
||||
int fd;
|
||||
|
||||
/* only send to servers dealing with our domain.
|
||||
domain may be NULL, in which case server->domain
|
||||
must be NULL also. */
|
||||
|
||||
if (type == (start->flags & SERV_TYPE) &&
|
||||
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
|
||||
!(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
|
||||
if (server_test_type(start, domain, type, 0) &&
|
||||
((fd = allocate_rfd(&forward->rfds, start)) != -1))
|
||||
{
|
||||
int fd;
|
||||
|
||||
/* find server socket to use, may need to get random one. */
|
||||
if (start->sfd)
|
||||
fd = start->sfd->fd;
|
||||
else
|
||||
{
|
||||
if (start->addr.sa.sa_family == AF_INET6)
|
||||
{
|
||||
if (!forward->rfd6 &&
|
||||
!(forward->rfd6 = allocate_rfd(AF_INET6)))
|
||||
break;
|
||||
daemon->rfd_save = forward->rfd6;
|
||||
fd = forward->rfd6->fd;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!forward->rfd4 &&
|
||||
!(forward->rfd4 = allocate_rfd(AF_INET)))
|
||||
break;
|
||||
daemon->rfd_save = forward->rfd4;
|
||||
fd = forward->rfd4->fd;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
/* Copy connection mark of incoming query to outgoing connection. */
|
||||
if (option_bool(OPT_CONNTRACK))
|
||||
{
|
||||
unsigned int mark;
|
||||
if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark))
|
||||
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
|
||||
}
|
||||
/* Copy connection mark of incoming query to outgoing connection. */
|
||||
if (option_bool(OPT_CONNTRACK))
|
||||
set_outgoing_mark(forward, fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER))
|
||||
@@ -575,15 +603,12 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
/* Keep info in case we want to re-send this packet */
|
||||
daemon->srv_save = start;
|
||||
daemon->packet_len = plen;
|
||||
daemon->fd_save = fd;
|
||||
|
||||
if (!gotname)
|
||||
strcpy(daemon->namebuff, "query");
|
||||
if (start->addr.sa.sa_family == AF_INET)
|
||||
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
(union all_addr *)&start->addr.in.sin_addr, NULL);
|
||||
else
|
||||
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
|
||||
(union all_addr *)&start->addr.in6.sin6_addr, NULL);
|
||||
log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff,
|
||||
&start->addr, NULL);
|
||||
start->queries++;
|
||||
forwarded = 1;
|
||||
forward->sentto = start;
|
||||
@@ -591,7 +616,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
break;
|
||||
forward->forwardall++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(start = start->next))
|
||||
start = daemon->servers;
|
||||
@@ -609,6 +634,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
}
|
||||
|
||||
/* could not send on, return empty answer or address if known for whole domain */
|
||||
frec_err:
|
||||
if (udpfd != -1)
|
||||
{
|
||||
plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
|
||||
@@ -732,7 +758,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
}
|
||||
|
||||
if (daemon->bogus_addr && rcode != NXDOMAIN &&
|
||||
check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
|
||||
check_for_bogus_wildcard(header, n, daemon->namebuff, now))
|
||||
{
|
||||
munged = 1;
|
||||
SET_RCODE(header, NXDOMAIN);
|
||||
@@ -806,7 +832,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
}
|
||||
|
||||
/* sets new last_server */
|
||||
void reply_query(int fd, int family, time_t now)
|
||||
void reply_query(int fd, time_t now)
|
||||
{
|
||||
/* packet from peer server, extract data for cache, and send to
|
||||
original requester */
|
||||
@@ -821,9 +847,9 @@ void reply_query(int fd, int family, time_t now)
|
||||
|
||||
/* packet buffer overwritten */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
|
||||
/* Determine the address of the server replying so that we can mark that as good */
|
||||
if ((serveraddr.sa.sa_family = family) == AF_INET6)
|
||||
if (serveraddr.sa.sa_family == AF_INET6)
|
||||
serveraddr.in6.sin6_flowinfo = 0;
|
||||
|
||||
header = (struct dns_header *)daemon->packet;
|
||||
@@ -846,7 +872,7 @@ void reply_query(int fd, int family, time_t now)
|
||||
|
||||
hash = hash_questions(header, n, daemon->namebuff);
|
||||
|
||||
if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash)))
|
||||
if (!(forward = lookup_frec(ntohs(header->id), fd, hash)))
|
||||
return;
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
@@ -860,7 +886,7 @@ void reply_query(int fd, int family, time_t now)
|
||||
daemon->log_source_addr = &forward->frec_src.source;
|
||||
|
||||
if (daemon->ignore_addr && RCODE(header) == NOERROR &&
|
||||
check_for_ignored_address(header, n, daemon->ignore_addr))
|
||||
check_for_ignored_address(header, n))
|
||||
return;
|
||||
|
||||
/* Note: if we send extra options in the EDNS0 header, we can't recreate
|
||||
@@ -900,43 +926,10 @@ void reply_query(int fd, int family, time_t now)
|
||||
}
|
||||
|
||||
|
||||
fd = -1;
|
||||
|
||||
if (start->sfd)
|
||||
fd = start->sfd->fd;
|
||||
else
|
||||
{
|
||||
if (start->addr.sa.sa_family == AF_INET6)
|
||||
{
|
||||
/* may have changed family */
|
||||
if (forward->rfd6 || (forward->rfd6 = allocate_rfd(AF_INET6)))
|
||||
fd = forward->rfd6->fd;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* may have changed family */
|
||||
if (forward->rfd4 || (forward->rfd4 = allocate_rfd(AF_INET)))
|
||||
fd = forward->rfd4->fd;
|
||||
}
|
||||
}
|
||||
|
||||
/* Can't get socket. */
|
||||
if (fd == -1)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)plen, NULL, &start->addr);
|
||||
#endif
|
||||
|
||||
while (retry_send(sendto(fd, (char *)header, plen, 0,
|
||||
&start->addr.sa,
|
||||
sa_len(&start->addr))));
|
||||
|
||||
if (start->addr.sa.sa_family == AF_INET)
|
||||
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (union all_addr *)&start->addr.in.sin_addr, "dnssec");
|
||||
else
|
||||
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (union all_addr *)&start->addr.in6.sin6_addr, "dnssec");
|
||||
|
||||
if ((fd = allocate_rfd(&forward->rfds, start)) != -1)
|
||||
server_send_log(start, fd, header, plen,
|
||||
DUMP_SEC_QUERY,
|
||||
F_NOEXTRA | F_DNSSEC, "retry", "dnssec");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -1107,9 +1100,7 @@ void reply_query(int fd, int family, time_t now)
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (type == (start->flags & (SERV_TYPE | SERV_DO_DNSSEC)) &&
|
||||
((type & SERV_TYPE) != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
|
||||
!(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
|
||||
if (server_test_type(start, domain, type, SERV_DO_DNSSEC))
|
||||
{
|
||||
new_server = start;
|
||||
if (server == start)
|
||||
@@ -1130,8 +1121,7 @@ void reply_query(int fd, int family, time_t now)
|
||||
}
|
||||
|
||||
new->sentto = server;
|
||||
new->rfd4 = NULL;
|
||||
new->rfd6 = NULL;
|
||||
new->rfds = NULL;
|
||||
new->frec_src.next = NULL;
|
||||
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA);
|
||||
new->forwardall = 0;
|
||||
@@ -1153,13 +1143,6 @@ void reply_query(int fd, int family, time_t now)
|
||||
nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz,
|
||||
daemon->keyname, forward->class, querytype, server->edns_pktsz);
|
||||
|
||||
if (server->addr.sa.sa_family == AF_INET)
|
||||
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, daemon->keyname, (union all_addr *)&(server->addr.in.sin_addr),
|
||||
querystr("dnssec-query", querytype));
|
||||
else
|
||||
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, daemon->keyname, (union all_addr *)&(server->addr.in6.sin6_addr),
|
||||
querystr("dnssec-query", querytype));
|
||||
|
||||
memcpy(new->hash, hash_questions(header, nn, daemon->namebuff), HASH_SIZE);
|
||||
new->new_id = get_id();
|
||||
header->id = htons(new->new_id);
|
||||
@@ -1170,42 +1153,15 @@ void reply_query(int fd, int family, time_t now)
|
||||
/* Don't resend this. */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
if (server->sfd)
|
||||
fd = server->sfd->fd;
|
||||
else
|
||||
{
|
||||
fd = -1;
|
||||
if (server->addr.sa.sa_family == AF_INET6)
|
||||
{
|
||||
if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
|
||||
fd = new->rfd6->fd;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
|
||||
fd = new->rfd4->fd;
|
||||
}
|
||||
}
|
||||
|
||||
if (fd != -1)
|
||||
if ((fd = allocate_rfd(&new->rfds, server)) != -1)
|
||||
{
|
||||
#ifdef HAVE_CONNTRACK
|
||||
/* Copy connection mark of incoming query to outgoing connection. */
|
||||
if (option_bool(OPT_CONNTRACK))
|
||||
{
|
||||
unsigned int mark;
|
||||
if (get_incoming_mark(&orig->frec_src.source, &orig->frec_src.dest, 0, &mark))
|
||||
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
|
||||
}
|
||||
set_outgoing_mark(orig, fd);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr);
|
||||
#endif
|
||||
|
||||
while (retry_send(sendto(fd, (char *)header, nn, 0,
|
||||
&server->addr.sa,
|
||||
sa_len(&server->addr))));
|
||||
server_send_log(server, fd, header, nn, DUMP_SEC_QUERY,
|
||||
F_NOEXTRA | F_DNSSEC, daemon->keyname,
|
||||
querystr("dnssec-query", querytype));
|
||||
server->queries++;
|
||||
}
|
||||
}
|
||||
@@ -1354,7 +1310,7 @@ void receive_query(struct listener *listen, time_t now)
|
||||
|
||||
/* packet buffer overwritten */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
|
||||
dst_addr_4.s_addr = dst_addr.addr4.s_addr = 0;
|
||||
netmask.s_addr = 0;
|
||||
|
||||
@@ -1564,13 +1520,9 @@ void receive_query(struct listener *listen, time_t now)
|
||||
struct auth_zone *zone;
|
||||
#endif
|
||||
char *types = querystr(auth_dns ? "auth" : "query", type);
|
||||
|
||||
if (family == AF_INET)
|
||||
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
(union all_addr *)&source_addr.in.sin_addr, types);
|
||||
else
|
||||
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
|
||||
(union all_addr *)&source_addr.in6.sin6_addr, types);
|
||||
|
||||
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
|
||||
&source_addr, types);
|
||||
|
||||
#ifdef HAVE_AUTH
|
||||
/* find queries for zones we're authoritative for, and answer them directly */
|
||||
@@ -1732,9 +1684,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
}
|
||||
}
|
||||
|
||||
if (type != (server->flags & (SERV_TYPE | SERV_DO_DNSSEC)) ||
|
||||
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, server->domain)) ||
|
||||
(server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
|
||||
if (!server_test_type(server, domain, type, SERV_DO_DNSSEC))
|
||||
continue;
|
||||
|
||||
retry:
|
||||
@@ -1758,9 +1708,8 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
}
|
||||
|
||||
#ifdef MSG_FASTOPEN
|
||||
while(retry_send(sendto(server->tcpfd, packet, m + sizeof(u16),
|
||||
MSG_FASTOPEN, &server->addr.sa, sa_len(&server->addr))));
|
||||
|
||||
server_send(server, server->tcpfd, packet, m + sizeof(u16), MSG_FASTOPEN);
|
||||
|
||||
if (errno == 0)
|
||||
data_sent = 1;
|
||||
#endif
|
||||
@@ -1791,14 +1740,9 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
continue;
|
||||
}
|
||||
|
||||
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC, keyname, &server->addr,
|
||||
querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
|
||||
|
||||
if (server->addr.sa.sa_family == AF_INET)
|
||||
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, keyname, (union all_addr *)&(server->addr.in.sin_addr),
|
||||
querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
|
||||
else
|
||||
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, keyname, (union all_addr *)&(server->addr.in6.sin6_addr),
|
||||
querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
|
||||
|
||||
server->flags |= SERV_GOT_TCP;
|
||||
|
||||
m = (c1 << 8) | c2;
|
||||
@@ -1936,12 +1880,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
#endif
|
||||
char *types = querystr(auth_dns ? "auth" : "query", qtype);
|
||||
|
||||
if (peer_addr.sa.sa_family == AF_INET)
|
||||
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
(union all_addr *)&peer_addr.in.sin_addr, types);
|
||||
else
|
||||
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
|
||||
(union all_addr *)&peer_addr.in6.sin6_addr, types);
|
||||
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
|
||||
&peer_addr, types);
|
||||
|
||||
#ifdef HAVE_AUTH
|
||||
/* find queries for zones we're authoritative for, and answer them directly */
|
||||
@@ -2056,9 +1996,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
}
|
||||
|
||||
/* server for wrong domain */
|
||||
if (type != (last_server->flags & SERV_TYPE) ||
|
||||
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)) ||
|
||||
(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
|
||||
if (!server_test_type(last_server, domain, type, 0))
|
||||
continue;
|
||||
|
||||
retry:
|
||||
@@ -2083,9 +2021,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
}
|
||||
|
||||
#ifdef MSG_FASTOPEN
|
||||
while(retry_send(sendto(last_server->tcpfd, packet, size + sizeof(u16),
|
||||
MSG_FASTOPEN, &last_server->addr.sa, sa_len(&last_server->addr))));
|
||||
|
||||
server_send(last_server, last_server->tcpfd, packet, size + sizeof(u16), MSG_FASTOPEN);
|
||||
|
||||
if (errno == 0)
|
||||
data_sent = 1;
|
||||
#endif
|
||||
@@ -2123,13 +2060,9 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
last_server->flags |= SERV_GOT_TCP;
|
||||
|
||||
m = (c1 << 8) | c2;
|
||||
|
||||
if (last_server->addr.sa.sa_family == AF_INET)
|
||||
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
|
||||
(union all_addr *)&last_server->addr.in.sin_addr, NULL);
|
||||
else
|
||||
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
|
||||
(union all_addr *)&last_server->addr.in6.sin6_addr, NULL);
|
||||
|
||||
log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff,
|
||||
&last_server->addr, NULL);
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (last_server->flags & SERV_DO_DNSSEC))
|
||||
@@ -2222,9 +2155,8 @@ static struct frec *allocate_frec(time_t now)
|
||||
f->next = daemon->frec_list;
|
||||
f->time = now;
|
||||
f->sentto = NULL;
|
||||
f->rfd4 = NULL;
|
||||
f->rfds = NULL;
|
||||
f->flags = 0;
|
||||
f->rfd6 = NULL;
|
||||
#ifdef HAVE_DNSSEC
|
||||
f->dependent = NULL;
|
||||
f->blocking_query = NULL;
|
||||
@@ -2236,46 +2168,192 @@ static struct frec *allocate_frec(time_t now)
|
||||
return f;
|
||||
}
|
||||
|
||||
struct randfd *allocate_rfd(int family)
|
||||
/* return a UDP socket bound to a random port, have to cope with straying into
|
||||
occupied port nos and reserved ones. */
|
||||
static int random_sock(struct server *s)
|
||||
{
|
||||
static int finger = 0;
|
||||
int i;
|
||||
int fd;
|
||||
|
||||
/* limit the number of sockets we have open to avoid starvation of
|
||||
(eg) TFTP. Once we have a reasonable number, randomness should be OK */
|
||||
|
||||
for (i = 0; i < RANDOM_SOCKS; i++)
|
||||
if (daemon->randomsocks[i].refcount == 0)
|
||||
{
|
||||
if ((daemon->randomsocks[i].fd = random_sock(family)) == -1)
|
||||
break;
|
||||
|
||||
daemon->randomsocks[i].refcount = 1;
|
||||
daemon->randomsocks[i].family = family;
|
||||
return &daemon->randomsocks[i];
|
||||
}
|
||||
|
||||
/* No free ones or cannot get new socket, grab an existing one */
|
||||
for (i = 0; i < RANDOM_SOCKS; i++)
|
||||
if ((fd = socket(s->source_addr.sa.sa_family, SOCK_DGRAM, 0)) != -1)
|
||||
{
|
||||
int j = (i+finger) % RANDOM_SOCKS;
|
||||
if (daemon->randomsocks[j].refcount != 0 &&
|
||||
daemon->randomsocks[j].family == family &&
|
||||
daemon->randomsocks[j].refcount != 0xffff)
|
||||
{
|
||||
finger = j;
|
||||
daemon->randomsocks[j].refcount++;
|
||||
return &daemon->randomsocks[j];
|
||||
}
|
||||
}
|
||||
if (local_bind(fd, &s->source_addr, s->interface, s->ifindex, 0))
|
||||
return fd;
|
||||
|
||||
return NULL; /* doom */
|
||||
if (s->interface[0] == 0)
|
||||
(void)prettyprint_addr(&s->source_addr, daemon->namebuff);
|
||||
else
|
||||
strcpy(daemon->namebuff, s->interface);
|
||||
|
||||
my_syslog(LOG_ERR, _("failed to bind server socket to %s: %s"),
|
||||
daemon->namebuff, strerror(errno));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void free_rfd(struct randfd *rfd)
|
||||
/* compare source addresses and interface, serv2 can be null. */
|
||||
static int server_isequal(const struct server *serv1,
|
||||
const struct server *serv2)
|
||||
{
|
||||
if (rfd && --(rfd->refcount) == 0)
|
||||
close(rfd->fd);
|
||||
return (serv2 &&
|
||||
serv2->ifindex == serv1->ifindex &&
|
||||
sockaddr_isequal(&serv2->source_addr, &serv1->source_addr) &&
|
||||
strncmp(serv2->interface, serv1->interface, IF_NAMESIZE) == 0);
|
||||
}
|
||||
|
||||
/* fdlp points to chain of randomfds already in use by transaction.
|
||||
If there's already a suitable one, return it, else allocate a
|
||||
new one and add it to the list.
|
||||
|
||||
Not leaking any resources in the face of allocation failures
|
||||
is rather convoluted here.
|
||||
|
||||
Note that rfd->serv may be NULL, when a server goes away.
|
||||
*/
|
||||
int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
|
||||
{
|
||||
static int finger = 0;
|
||||
int i, j = 0;
|
||||
struct randfd_list *rfl;
|
||||
struct randfd *rfd = NULL;
|
||||
int fd = 0;
|
||||
|
||||
/* If server has a pre-allocated fd, use that. */
|
||||
if (serv->sfd)
|
||||
return serv->sfd->fd;
|
||||
|
||||
/* existing suitable random port socket linked to this transaction? */
|
||||
for (rfl = *fdlp; rfl; rfl = rfl->next)
|
||||
if (server_isequal(serv, rfl->rfd->serv))
|
||||
return rfl->rfd->fd;
|
||||
|
||||
/* No. need new link. */
|
||||
if ((rfl = daemon->rfl_spare))
|
||||
daemon->rfl_spare = rfl->next;
|
||||
else if (!(rfl = whine_malloc(sizeof(struct randfd_list))))
|
||||
return -1;
|
||||
|
||||
/* limit the number of sockets we have open to avoid starvation of
|
||||
(eg) TFTP. Once we have a reasonable number, randomness should be OK */
|
||||
for (i = 0; i < daemon->numrrand; i++)
|
||||
if (daemon->randomsocks[i].refcount == 0)
|
||||
{
|
||||
if ((fd = random_sock(serv)) != -1)
|
||||
{
|
||||
rfd = &daemon->randomsocks[i];
|
||||
rfd->serv = serv;
|
||||
rfd->fd = fd;
|
||||
rfd->refcount = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* No free ones or cannot get new socket, grab an existing one */
|
||||
if (!rfd)
|
||||
for (j = 0; j < daemon->numrrand; j++)
|
||||
{
|
||||
i = (j + finger) % daemon->numrrand;
|
||||
if (daemon->randomsocks[i].refcount != 0 &&
|
||||
server_isequal(serv, daemon->randomsocks[i].serv) &&
|
||||
daemon->randomsocks[i].refcount != 0xfffe)
|
||||
{
|
||||
finger = i + 1;
|
||||
rfd = &daemon->randomsocks[i];
|
||||
rfd->refcount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j == daemon->numrrand)
|
||||
{
|
||||
struct randfd_list *rfl_poll;
|
||||
|
||||
/* there are no free slots, and non with the same parameters we can piggy-back on.
|
||||
We're going to have to allocate a new temporary record, distinguished by
|
||||
refcount == 0xffff. This will exist in the frec randfd list, never be shared,
|
||||
and be freed when no longer in use. It will also be held on
|
||||
the daemon->rfl_poll list so the poll system can find it. */
|
||||
|
||||
if ((rfl_poll = daemon->rfl_spare))
|
||||
daemon->rfl_spare = rfl_poll->next;
|
||||
else
|
||||
rfl_poll = whine_malloc(sizeof(struct randfd_list));
|
||||
|
||||
if (!rfl_poll ||
|
||||
!(rfd = whine_malloc(sizeof(struct randfd))) ||
|
||||
(fd = random_sock(serv)) == -1)
|
||||
{
|
||||
|
||||
/* Don't leak anything we may already have */
|
||||
rfl->next = daemon->rfl_spare;
|
||||
daemon->rfl_spare = rfl;
|
||||
|
||||
if (rfl_poll)
|
||||
{
|
||||
rfl_poll->next = daemon->rfl_spare;
|
||||
daemon->rfl_spare = rfl_poll;
|
||||
}
|
||||
|
||||
if (rfd)
|
||||
free(rfd);
|
||||
|
||||
return -1; /* doom */
|
||||
}
|
||||
|
||||
/* Note rfd->serv not set here, since it's not reused */
|
||||
rfd->fd = fd;
|
||||
rfd->refcount = 0xffff; /* marker for temp record */
|
||||
|
||||
rfl_poll->rfd = rfd;
|
||||
rfl_poll->next = daemon->rfl_poll;
|
||||
daemon->rfl_poll = rfl_poll;
|
||||
}
|
||||
|
||||
rfl->rfd = rfd;
|
||||
rfl->next = *fdlp;
|
||||
*fdlp = rfl;
|
||||
|
||||
return rfl->rfd->fd;
|
||||
}
|
||||
|
||||
void free_rfds(struct randfd_list **fdlp)
|
||||
{
|
||||
struct randfd_list *tmp, *rfl, *poll, *next, **up;
|
||||
|
||||
for (rfl = *fdlp; rfl; rfl = tmp)
|
||||
{
|
||||
if (rfl->rfd->refcount == 0xffff || --(rfl->rfd->refcount) == 0)
|
||||
close(rfl->rfd->fd);
|
||||
|
||||
/* temporary overflow record */
|
||||
if (rfl->rfd->refcount == 0xffff)
|
||||
{
|
||||
free(rfl->rfd);
|
||||
|
||||
/* go through the link of all these by steam to delete.
|
||||
This list is expected to be almost always empty. */
|
||||
for (poll = daemon->rfl_poll, up = &daemon->rfl_poll; poll; poll = next)
|
||||
{
|
||||
next = poll->next;
|
||||
|
||||
if (poll->rfd == rfl->rfd)
|
||||
{
|
||||
*up = poll->next;
|
||||
poll->next = daemon->rfl_spare;
|
||||
daemon->rfl_spare = poll;
|
||||
}
|
||||
else
|
||||
up = &poll->next;
|
||||
}
|
||||
}
|
||||
|
||||
tmp = rfl->next;
|
||||
rfl->next = daemon->rfl_spare;
|
||||
daemon->rfl_spare = rfl;
|
||||
}
|
||||
|
||||
*fdlp = NULL;
|
||||
}
|
||||
|
||||
static void free_frec(struct frec *f)
|
||||
@@ -2291,12 +2369,9 @@ static void free_frec(struct frec *f)
|
||||
}
|
||||
|
||||
f->frec_src.next = NULL;
|
||||
free_rfd(f->rfd4);
|
||||
f->rfd4 = NULL;
|
||||
free_rfds(&f->rfds);
|
||||
f->sentto = NULL;
|
||||
f->flags = 0;
|
||||
free_rfd(f->rfd6);
|
||||
f->rfd6 = NULL;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (f->stash)
|
||||
@@ -2382,17 +2457,11 @@ struct frec *get_new_frec(time_t now, int *wait, struct frec *force)
|
||||
/* none available, calculate time 'till oldest record expires */
|
||||
if (!force && count > daemon->ftabsize)
|
||||
{
|
||||
static time_t last_log = 0;
|
||||
|
||||
if (oldest && wait)
|
||||
*wait = oldest->time + (time_t)TIMEOUT - now;
|
||||
|
||||
if ((int)difftime(now, last_log) > 5)
|
||||
{
|
||||
last_log = now;
|
||||
my_syslog(LOG_WARNING, _("Maximum number of concurrent DNS queries reached (max: %d)"), daemon->ftabsize);
|
||||
}
|
||||
|
||||
query_full(now);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2403,44 +2472,47 @@ struct frec *get_new_frec(time_t now, int *wait, struct frec *force)
|
||||
return f; /* OK if malloc fails and this is NULL */
|
||||
}
|
||||
|
||||
static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash)
|
||||
static void query_full(time_t now)
|
||||
{
|
||||
static time_t last_log = 0;
|
||||
|
||||
if ((int)difftime(now, last_log) > 5)
|
||||
{
|
||||
last_log = now;
|
||||
my_syslog(LOG_WARNING, _("Maximum number of concurrent DNS queries reached (max: %d)"), daemon->ftabsize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct frec *lookup_frec(unsigned short id, int fd, void *hash)
|
||||
{
|
||||
struct frec *f;
|
||||
|
||||
struct server *s;
|
||||
int type;
|
||||
struct randfd_list *fdl;
|
||||
|
||||
for(f = daemon->frec_list; f; f = f->next)
|
||||
if (f->sentto && f->new_id == id &&
|
||||
(memcmp(hash, f->hash, HASH_SIZE) == 0))
|
||||
{
|
||||
/* sent from random port */
|
||||
if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd)
|
||||
return f;
|
||||
|
||||
if (family == AF_INET6 && f->rfd6 && f->rfd6->fd == fd)
|
||||
return f;
|
||||
|
||||
/* sent to upstream from bound socket. */
|
||||
if (f->sentto->sfd && f->sentto->sfd->fd == fd)
|
||||
for (fdl = f->rfds; fdl; fdl = fdl->next)
|
||||
if (fdl->rfd->fd == fd)
|
||||
return f;
|
||||
|
||||
/* Sent to upstream from socket associated with a server.
|
||||
Note we have to iterate over all the possible servers, since they may
|
||||
have different bound sockets. */
|
||||
type = f->sentto->flags & SERV_TYPE;
|
||||
s = f->sentto;
|
||||
do {
|
||||
if (server_test_type(s, f->sentto->domain, type, 0) &&
|
||||
s->sfd && s->sfd->fd == fd)
|
||||
return f;
|
||||
|
||||
s = s->next ? s->next : daemon->servers;
|
||||
} while (s != f->sentto);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct frec *lookup_frec_by_sender(unsigned short id,
|
||||
union mysockaddr *addr,
|
||||
void *hash)
|
||||
{
|
||||
struct frec *f;
|
||||
struct frec_src *src;
|
||||
|
||||
for (f = daemon->frec_list; f; f = f->next)
|
||||
if (f->sentto &&
|
||||
!(f->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) &&
|
||||
memcmp(hash, f->hash, HASH_SIZE) == 0)
|
||||
for (src = &f->frec_src; src; src = src->next)
|
||||
if (src->orig_id == id &&
|
||||
sockaddr_isequal(&src->source, addr))
|
||||
return f;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -2471,34 +2543,29 @@ static struct frec *lookup_frec_by_query(void *hash, unsigned int flags)
|
||||
void resend_query()
|
||||
{
|
||||
if (daemon->srv_save)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (daemon->srv_save->sfd)
|
||||
fd = daemon->srv_save->sfd->fd;
|
||||
else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
|
||||
fd = daemon->rfd_save->fd;
|
||||
else
|
||||
return;
|
||||
|
||||
while(retry_send(sendto(fd, daemon->packet, daemon->packet_len, 0,
|
||||
&daemon->srv_save->addr.sa,
|
||||
sa_len(&daemon->srv_save->addr))));
|
||||
}
|
||||
server_send(daemon->srv_save, daemon->fd_save,
|
||||
daemon->packet, daemon->packet_len, 0);
|
||||
}
|
||||
|
||||
/* A server record is going away, remove references to it */
|
||||
void server_gone(struct server *server)
|
||||
{
|
||||
struct frec *f;
|
||||
int i;
|
||||
|
||||
for (f = daemon->frec_list; f; f = f->next)
|
||||
if (f->sentto && f->sentto == server)
|
||||
free_frec(f);
|
||||
|
||||
/* If any random socket refers to this server, NULL the reference.
|
||||
No more references to the socket will be created in the future. */
|
||||
for (i = 0; i < daemon->numrrand; i++)
|
||||
if (daemon->randomsocks[i].refcount != 0 && daemon->randomsocks[i].serv == server)
|
||||
daemon->randomsocks[i].serv = NULL;
|
||||
|
||||
if (daemon->last_server == server)
|
||||
daemon->last_server = NULL;
|
||||
|
||||
|
||||
if (daemon->srv_save == server)
|
||||
daemon->srv_save = NULL;
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ void lease_update_file(time_t now)
|
||||
if (next_event == 0 || difftime(next_event, LEASE_RETRY + now) > 0.0)
|
||||
next_event = LEASE_RETRY + now;
|
||||
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("failed to write %s: %s (retry in %us)"),
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("failed to write %s: %s (retry in %u s)"),
|
||||
daemon->lease_file, strerror(err),
|
||||
(unsigned int)difftime(next_event, now));
|
||||
}
|
||||
|
||||
10
src/log.c
10
src/log.c
@@ -273,7 +273,7 @@ static void log_write(void)
|
||||
/* priority is one of LOG_DEBUG, LOG_INFO, LOG_NOTICE, etc. See sys/syslog.h.
|
||||
OR'd to priority can be MS_TFTP, MS_DHCP, ... to be able to do log separation between
|
||||
DNS, DHCP and TFTP services.
|
||||
*/
|
||||
If OR'd with MS_DEBUG, the messages are suppressed unless --log-debug is set. */
|
||||
void my_syslog(int priority, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
@@ -290,7 +290,13 @@ void my_syslog(int priority, const char *format, ...)
|
||||
func = "-dhcp";
|
||||
else if ((LOG_FACMASK & priority) == MS_SCRIPT)
|
||||
func = "-script";
|
||||
|
||||
else if ((LOG_FACMASK & priority) == MS_DEBUG)
|
||||
{
|
||||
if (!option_bool(OPT_LOG_DEBUG))
|
||||
return;
|
||||
func = "-debug";
|
||||
}
|
||||
|
||||
#ifdef LOG_PRI
|
||||
priority = LOG_PRI(priority);
|
||||
#else
|
||||
|
||||
20
src/loop.c
20
src/loop.c
@@ -22,6 +22,7 @@ static ssize_t loop_make_probe(u32 uid);
|
||||
void loop_send_probes()
|
||||
{
|
||||
struct server *serv;
|
||||
struct randfd_list *rfds = NULL;
|
||||
|
||||
if (!option_bool(OPT_LOOP_DETECT))
|
||||
return;
|
||||
@@ -34,29 +35,22 @@ void loop_send_probes()
|
||||
{
|
||||
ssize_t len = loop_make_probe(serv->uid);
|
||||
int fd;
|
||||
struct randfd *rfd = NULL;
|
||||
|
||||
if (serv->sfd)
|
||||
fd = serv->sfd->fd;
|
||||
else
|
||||
{
|
||||
if (!(rfd = allocate_rfd(serv->addr.sa.sa_family)))
|
||||
continue;
|
||||
fd = rfd->fd;
|
||||
}
|
||||
|
||||
if ((fd = allocate_rfd(&rfds, serv)) == -1)
|
||||
continue;
|
||||
|
||||
while (retry_send(sendto(fd, daemon->packet, len, 0,
|
||||
&serv->addr.sa, sa_len(&serv->addr))));
|
||||
|
||||
free_rfd(rfd);
|
||||
}
|
||||
|
||||
free_rfds(&rfds);
|
||||
}
|
||||
|
||||
static ssize_t loop_make_probe(u32 uid)
|
||||
{
|
||||
struct dns_header *header = (struct dns_header *)daemon->packet;
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
|
||||
|
||||
/* packet buffer overwritten */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
|
||||
@@ -41,19 +41,26 @@
|
||||
|
||||
#ifndef NDA_RTA
|
||||
# define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Used to request refresh of addresses or routes just once,
|
||||
* when multiple changes might be announced. */
|
||||
enum async_states {
|
||||
STATE_NEWADDR = (1 << 0),
|
||||
STATE_NEWROUTE = (1 << 1),
|
||||
};
|
||||
|
||||
|
||||
static struct iovec iov;
|
||||
static u32 netlink_pid;
|
||||
|
||||
static void nl_async(struct nlmsghdr *h);
|
||||
static unsigned nl_async(struct nlmsghdr *h, unsigned state);
|
||||
static void nl_multicast_state(unsigned state);
|
||||
|
||||
char *netlink_init(void)
|
||||
{
|
||||
struct sockaddr_nl addr;
|
||||
socklen_t slen = sizeof(addr);
|
||||
int opt = 1;
|
||||
|
||||
addr.nl_family = AF_NETLINK;
|
||||
addr.nl_pad = 0;
|
||||
@@ -92,14 +99,10 @@ char *netlink_init(void)
|
||||
iov.iov_len = 100;
|
||||
iov.iov_base = safe_malloc(iov.iov_len);
|
||||
|
||||
if (daemon->kernel_version >= KERNEL_VERSION(2,6,30) &&
|
||||
setsockopt(daemon->netlinkfd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &opt, sizeof(opt)) == -1)
|
||||
return _("warning: failed to set NETLINK_NO_ENOBUFS on netlink socket");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t netlink_recv(void)
|
||||
static ssize_t netlink_recv(int flags)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct sockaddr_nl nladdr;
|
||||
@@ -115,7 +118,8 @@ static ssize_t netlink_recv(void)
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
while ((rc = recvmsg(daemon->netlinkfd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
|
||||
while ((rc = recvmsg(daemon->netlinkfd, &msg, flags | MSG_PEEK | MSG_TRUNC)) == -1 &&
|
||||
errno == EINTR);
|
||||
|
||||
/* make buffer big enough */
|
||||
if (rc != -1 && (msg.msg_flags & MSG_TRUNC))
|
||||
@@ -132,7 +136,7 @@ static ssize_t netlink_recv(void)
|
||||
|
||||
/* read it for real */
|
||||
msg.msg_flags = 0;
|
||||
while ((rc = recvmsg(daemon->netlinkfd, &msg, 0)) == -1 && errno == EINTR);
|
||||
while ((rc = recvmsg(daemon->netlinkfd, &msg, flags)) == -1 && errno == EINTR);
|
||||
|
||||
/* Make sure this is from the kernel */
|
||||
if (rc == -1 || nladdr.nl_pid == 0)
|
||||
@@ -151,7 +155,9 @@ static ssize_t netlink_recv(void)
|
||||
|
||||
|
||||
/* family = AF_UNSPEC finds ARP table entries.
|
||||
family = AF_LOCAL finds MAC addresses. */
|
||||
family = AF_LOCAL finds MAC addresses.
|
||||
returns 0 on failure, 1 on success, -1 when restart is required
|
||||
*/
|
||||
int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
{
|
||||
struct sockaddr_nl addr;
|
||||
@@ -159,6 +165,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
ssize_t len;
|
||||
static unsigned int seq = 0;
|
||||
int callback_ok = 1;
|
||||
unsigned state = 0;
|
||||
|
||||
struct {
|
||||
struct nlmsghdr nlh;
|
||||
@@ -170,7 +177,6 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
|
||||
addr.nl_family = AF_NETLINK;
|
||||
|
||||
again:
|
||||
if (family == AF_UNSPEC)
|
||||
req.nlh.nlmsg_type = RTM_GETNEIGH;
|
||||
else if (family == AF_LOCAL)
|
||||
@@ -193,12 +199,12 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
|
||||
while (1)
|
||||
{
|
||||
if ((len = netlink_recv()) == -1)
|
||||
if ((len = netlink_recv(0)) == -1)
|
||||
{
|
||||
if (errno == ENOBUFS)
|
||||
{
|
||||
sleep(1);
|
||||
goto again;
|
||||
nl_multicast_state(state);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -207,7 +213,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
if (h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
/* May be multicast arriving async */
|
||||
nl_async(h);
|
||||
state = nl_async(h, state);
|
||||
}
|
||||
else if (h->nlmsg_seq != seq)
|
||||
{
|
||||
@@ -341,26 +347,28 @@ int iface_enumerate(int family, void *parm, int (*callback)())
|
||||
}
|
||||
}
|
||||
|
||||
void netlink_multicast(void)
|
||||
static void nl_multicast_state(unsigned state)
|
||||
{
|
||||
ssize_t len;
|
||||
struct nlmsghdr *h;
|
||||
int flags;
|
||||
|
||||
do {
|
||||
/* don't risk blocking reading netlink messages here. */
|
||||
while ((len = netlink_recv(MSG_DONTWAIT)) != -1)
|
||||
|
||||
/* don't risk blocking reading netlink messages here. */
|
||||
if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||
|
||||
fcntl(daemon->netlinkfd, F_SETFL, flags | O_NONBLOCK) == -1)
|
||||
return;
|
||||
|
||||
if ((len = netlink_recv()) != -1)
|
||||
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
|
||||
nl_async(h);
|
||||
|
||||
/* restore non-blocking status */
|
||||
fcntl(daemon->netlinkfd, F_SETFL, flags);
|
||||
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
|
||||
state = nl_async(h, state);
|
||||
} while (errno == ENOBUFS);
|
||||
}
|
||||
|
||||
static void nl_async(struct nlmsghdr *h)
|
||||
void netlink_multicast(void)
|
||||
{
|
||||
unsigned state = 0;
|
||||
nl_multicast_state(state);
|
||||
}
|
||||
|
||||
|
||||
static unsigned nl_async(struct nlmsghdr *h, unsigned state)
|
||||
{
|
||||
if (h->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
@@ -368,7 +376,8 @@ static void nl_async(struct nlmsghdr *h)
|
||||
if (err->error != 0)
|
||||
my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
|
||||
}
|
||||
else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
|
||||
else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE &&
|
||||
(state & STATE_NEWROUTE)==0)
|
||||
{
|
||||
/* We arrange to receive netlink multicast messages whenever the network route is added.
|
||||
If this happens and we still have a DNS packet in the buffer, we re-send it.
|
||||
@@ -380,10 +389,18 @@ static void nl_async(struct nlmsghdr *h)
|
||||
if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK &&
|
||||
(rtm->rtm_table == RT_TABLE_MAIN ||
|
||||
rtm->rtm_table == RT_TABLE_LOCAL))
|
||||
queue_event(EVENT_NEWROUTE);
|
||||
{
|
||||
queue_event(EVENT_NEWROUTE);
|
||||
state |= STATE_NEWROUTE;
|
||||
}
|
||||
}
|
||||
else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR)
|
||||
queue_event(EVENT_NEWADDR);
|
||||
else if ((h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) &&
|
||||
(state & STATE_NEWADDR)==0)
|
||||
{
|
||||
queue_event(EVENT_NEWADDR);
|
||||
state |= STATE_NEWADDR;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
306
src/network.c
306
src/network.c
@@ -232,7 +232,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
|
||||
union mysockaddr *addr, struct in_addr netmask, int prefixlen, int iface_flags)
|
||||
{
|
||||
struct irec *iface;
|
||||
int mtu = 0, loopback;
|
||||
int loopback;
|
||||
struct ifreq ifr;
|
||||
int tftp_ok = !!option_bool(OPT_TFTP);
|
||||
int dhcp_ok = 1;
|
||||
@@ -253,9 +253,6 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
|
||||
if (loopback)
|
||||
dhcp_ok = 0;
|
||||
|
||||
if (ioctl(param->fd, SIOCGIFMTU, &ifr) != -1)
|
||||
mtu = ifr.ifr_mtu;
|
||||
|
||||
if (!label)
|
||||
label = ifr.ifr_name;
|
||||
else
|
||||
@@ -351,36 +348,109 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
|
||||
/* Update addresses from interface_names. These are a set independent
|
||||
of the set we're listening on. */
|
||||
for (int_name = daemon->int_names; int_name; int_name = int_name->next)
|
||||
if (strncmp(label, int_name->intr, IF_NAMESIZE) == 0 &&
|
||||
(addr->sa.sa_family == int_name->family || int_name->family == 0))
|
||||
if (strncmp(label, int_name->intr, IF_NAMESIZE) == 0)
|
||||
{
|
||||
if (param->spare)
|
||||
struct addrlist *lp;
|
||||
|
||||
al = NULL;
|
||||
|
||||
if (addr->sa.sa_family == AF_INET && (int_name->flags & (IN4 | INP4)))
|
||||
{
|
||||
al = param->spare;
|
||||
param->spare = al->next;
|
||||
struct in_addr newaddr = addr->in.sin_addr;
|
||||
|
||||
if (int_name->flags & INP4)
|
||||
{
|
||||
if (netmask.s_addr == 0xffff)
|
||||
continue;
|
||||
|
||||
newaddr.s_addr = (addr->in.sin_addr.s_addr & netmask.s_addr) |
|
||||
(int_name->proto4.s_addr & ~netmask.s_addr);
|
||||
}
|
||||
|
||||
/* check for duplicates. */
|
||||
for (lp = int_name->addr; lp; lp = lp->next)
|
||||
if (lp->flags == 0 && lp->addr.addr4.s_addr == newaddr.s_addr)
|
||||
break;
|
||||
|
||||
if (!lp)
|
||||
{
|
||||
if (param->spare)
|
||||
{
|
||||
al = param->spare;
|
||||
param->spare = al->next;
|
||||
}
|
||||
else
|
||||
al = whine_malloc(sizeof(struct addrlist));
|
||||
|
||||
if (al)
|
||||
{
|
||||
al->flags = 0;
|
||||
al->addr.addr4 = newaddr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (addr->sa.sa_family == AF_INET6 && (int_name->flags & (IN6 | INP6)))
|
||||
{
|
||||
struct in6_addr newaddr = addr->in6.sin6_addr;
|
||||
|
||||
if (int_name->flags & INP6)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* No sense in doing /128. */
|
||||
if (prefixlen == 128)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
int bits = ((i+1)*8) - prefixlen;
|
||||
|
||||
if (bits >= 8)
|
||||
newaddr.s6_addr[i] = int_name->proto6.s6_addr[i];
|
||||
else if (bits >= 0)
|
||||
{
|
||||
unsigned char mask = 0xff << bits;
|
||||
newaddr.s6_addr[i] =
|
||||
(addr->in6.sin6_addr.s6_addr[i] & mask) |
|
||||
(int_name->proto6.s6_addr[i] & ~mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check for duplicates. */
|
||||
for (lp = int_name->addr; lp; lp = lp->next)
|
||||
if ((lp->flags & ADDRLIST_IPV6) &&
|
||||
IN6_ARE_ADDR_EQUAL(&lp->addr.addr6, &newaddr))
|
||||
break;
|
||||
|
||||
if (!lp)
|
||||
{
|
||||
if (param->spare)
|
||||
{
|
||||
al = param->spare;
|
||||
param->spare = al->next;
|
||||
}
|
||||
else
|
||||
al = whine_malloc(sizeof(struct addrlist));
|
||||
|
||||
if (al)
|
||||
{
|
||||
al->flags = ADDRLIST_IPV6;
|
||||
al->addr.addr6 = newaddr;
|
||||
|
||||
/* Privacy addresses and addresses still undergoing DAD and deprecated addresses
|
||||
don't appear in forward queries, but will in reverse ones. */
|
||||
if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE)))
|
||||
al->flags |= ADDRLIST_REVONLY;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
al = whine_malloc(sizeof(struct addrlist));
|
||||
|
||||
if (al)
|
||||
{
|
||||
al->next = int_name->addr;
|
||||
int_name->addr = al;
|
||||
|
||||
if (addr->sa.sa_family == AF_INET)
|
||||
{
|
||||
al->addr.addr4 = addr->in.sin_addr;
|
||||
al->flags = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
al->addr.addr6 = addr->in6.sin6_addr;
|
||||
al->flags = ADDRLIST_IPV6;
|
||||
/* Privacy addresses and addresses still undergoing DAD and deprecated addresses
|
||||
don't appear in forward queries, but will in reverse ones. */
|
||||
if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE)))
|
||||
al->flags |= ADDRLIST_REVONLY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -458,6 +528,11 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
|
||||
/* add to list */
|
||||
if ((iface = whine_malloc(sizeof(struct irec))))
|
||||
{
|
||||
int mtu = 0;
|
||||
|
||||
if (ioctl(param->fd, SIOCGIFMTU, &ifr) != -1)
|
||||
mtu = ifr.ifr_mtu;
|
||||
|
||||
iface->addr = *addr;
|
||||
iface->netmask = netmask;
|
||||
iface->tftp_ok = tftp_ok;
|
||||
@@ -592,7 +667,7 @@ static int release_listener(struct listener *l)
|
||||
int port;
|
||||
|
||||
port = prettyprint_addr(&l->iface->addr, daemon->addrbuff);
|
||||
my_syslog(LOG_DEBUG, _("stopped listening on %s(#%d): %s port %d"),
|
||||
my_syslog(LOG_DEBUG|MS_DEBUG, _("stopped listening on %s(#%d): %s port %d"),
|
||||
l->iface->name, l->iface->index, daemon->addrbuff, port);
|
||||
/* In case it ever returns */
|
||||
l->iface->done = 0;
|
||||
@@ -621,7 +696,8 @@ int enumerate_interfaces(int reset)
|
||||
#ifdef HAVE_AUTH
|
||||
struct auth_zone *zone;
|
||||
#endif
|
||||
|
||||
struct server *serv;
|
||||
|
||||
/* Do this max once per select cycle - also inhibits netlink socket use
|
||||
in TCP child processes. */
|
||||
|
||||
@@ -638,7 +714,26 @@ int enumerate_interfaces(int reset)
|
||||
|
||||
if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
|
||||
return 0;
|
||||
|
||||
|
||||
/* iface indexes can change when interfaces are created/destroyed.
|
||||
We use them in the main forwarding control path, when the path
|
||||
to a server is specified by an interface, so cache them.
|
||||
Update the cache here. */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (serv->interface[0] != 0)
|
||||
{
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
struct ifreq ifr;
|
||||
|
||||
safe_strncpy(ifr.ifr_name, serv->interface, IF_NAMESIZE);
|
||||
if (ioctl(param.fd, SIOCGIFINDEX, &ifr) != -1)
|
||||
serv->ifindex = ifr.ifr_ifindex;
|
||||
#else
|
||||
serv->ifindex = if_nametoindex(serv->interface);
|
||||
#endif
|
||||
}
|
||||
|
||||
again:
|
||||
/* Mark interfaces for garbage collection */
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
iface->found = 0;
|
||||
@@ -690,9 +785,14 @@ int enumerate_interfaces(int reset)
|
||||
param.spare = spare;
|
||||
|
||||
ret = iface_enumerate(AF_INET6, ¶m, iface_allowed_v6);
|
||||
|
||||
if (ret)
|
||||
ret = iface_enumerate(AF_INET, ¶m, iface_allowed_v4);
|
||||
if (ret < 0)
|
||||
goto again;
|
||||
else if (ret)
|
||||
{
|
||||
ret = iface_enumerate(AF_INET, ¶m, iface_allowed_v4);
|
||||
if (ret < 0)
|
||||
goto again;
|
||||
}
|
||||
|
||||
errsave = errno;
|
||||
close(param.fd);
|
||||
@@ -727,7 +827,7 @@ int enumerate_interfaces(int reset)
|
||||
|
||||
errno = errsave;
|
||||
spare = param.spare;
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -867,10 +967,10 @@ int tcp_interface(int fd, int af)
|
||||
/* use mshdr so that the CMSDG_* macros are available */
|
||||
msg.msg_control = daemon->packet;
|
||||
msg.msg_controllen = len = daemon->packet_buff_sz;
|
||||
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
if (af == AF_INET)
|
||||
{
|
||||
if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 &&
|
||||
@@ -1045,7 +1145,7 @@ void create_bound_listeners(int dienow)
|
||||
if (!dienow)
|
||||
{
|
||||
int port = prettyprint_addr(&iface->addr, daemon->addrbuff);
|
||||
my_syslog(LOG_DEBUG, _("listening on %s(#%d): %s port %d"),
|
||||
my_syslog(LOG_DEBUG|MS_DEBUG, _("listening on %s(#%d): %s port %d"),
|
||||
iface->name, iface->index, daemon->addrbuff, port);
|
||||
}
|
||||
}
|
||||
@@ -1072,7 +1172,7 @@ void create_bound_listeners(int dienow)
|
||||
if (!dienow)
|
||||
{
|
||||
int port = prettyprint_addr(&if_tmp->addr, daemon->addrbuff);
|
||||
my_syslog(LOG_DEBUG, _("listening on %s port %d"), daemon->addrbuff, port);
|
||||
my_syslog(LOG_DEBUG|MS_DEBUG, _("listening on %s port %d"), daemon->addrbuff, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1206,66 +1306,13 @@ void join_multicast(int dienow)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* return a UDP socket bound to a random port, have to cope with straying into
|
||||
occupied port nos and reserved ones. */
|
||||
int random_sock(int family)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if ((fd = socket(family, SOCK_DGRAM, 0)) != -1)
|
||||
{
|
||||
union mysockaddr addr;
|
||||
unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1;
|
||||
int tries = ports_avail < 30 ? 3 * ports_avail : 100;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sa.sa_family = family;
|
||||
|
||||
/* don't loop forever if all ports in use. */
|
||||
|
||||
if (fix_fd(fd))
|
||||
while(tries--)
|
||||
{
|
||||
unsigned short port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
|
||||
|
||||
if (family == AF_INET)
|
||||
{
|
||||
addr.in.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.in.sin_port = port;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr.in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
addr.in6.sin6_addr = in6addr_any;
|
||||
addr.in6.sin6_port = port;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0)
|
||||
return fd;
|
||||
|
||||
if (errno != EADDRINUSE && errno != EACCES)
|
||||
break;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp)
|
||||
{
|
||||
union mysockaddr addr_copy = *addr;
|
||||
unsigned short port;
|
||||
int tries = 1, done = 0;
|
||||
unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1;
|
||||
|
||||
int tries = 1;
|
||||
unsigned short ports_avail = 1;
|
||||
|
||||
if (addr_copy.sa.sa_family == AF_INET)
|
||||
port = addr_copy.in.sin_port;
|
||||
else
|
||||
@@ -1274,35 +1321,43 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
|
||||
/* cannot set source _port_ for TCP connections. */
|
||||
if (is_tcp)
|
||||
port = 0;
|
||||
|
||||
/* Bind a random port within the range given by min-port and max-port */
|
||||
if (port == 0)
|
||||
else if (port == 0 && daemon->max_port != 0)
|
||||
{
|
||||
/* Bind a random port within the range given by min-port and max-port if either
|
||||
or both are set. Otherwise use the OS's random ephemeral port allocation by
|
||||
leaving port == 0 and tries == 1 */
|
||||
ports_avail = daemon->max_port - daemon->min_port + 1;
|
||||
tries = ports_avail < 30 ? 3 * ports_avail : 100;
|
||||
port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
|
||||
port = htons(daemon->min_port + (rand16() % ports_avail));
|
||||
}
|
||||
|
||||
while (tries--)
|
||||
while (1)
|
||||
{
|
||||
/* elide bind() call if it's to port 0, address 0 */
|
||||
if (addr_copy.sa.sa_family == AF_INET)
|
||||
addr_copy.in.sin_port = port;
|
||||
else
|
||||
addr_copy.in6.sin6_port = port;
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) != -1)
|
||||
{
|
||||
done = 1;
|
||||
break;
|
||||
if (port == 0 && addr_copy.in.sin_addr.s_addr == 0)
|
||||
break;
|
||||
addr_copy.in.sin_port = port;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (port == 0 && IN6_IS_ADDR_UNSPECIFIED(&addr_copy.in6.sin6_addr))
|
||||
break;
|
||||
addr_copy.in6.sin6_port = port;
|
||||
}
|
||||
|
||||
if (errno != EADDRINUSE && errno != EACCES)
|
||||
return 0;
|
||||
if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) != -1)
|
||||
break;
|
||||
|
||||
port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
|
||||
}
|
||||
if (errno != EADDRINUSE && errno != EACCES)
|
||||
return 0;
|
||||
|
||||
if (!done)
|
||||
return 0;
|
||||
if (--tries == 0)
|
||||
return 0;
|
||||
|
||||
port = htons(daemon->min_port + (rand16() % ports_avail));
|
||||
}
|
||||
|
||||
if (!is_tcp && ifindex > 0)
|
||||
{
|
||||
@@ -1332,38 +1387,33 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
|
||||
static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname, unsigned int ifindex)
|
||||
{
|
||||
struct serverfd *sfd;
|
||||
unsigned int ifindex = 0;
|
||||
int errsave;
|
||||
int opt = 1;
|
||||
|
||||
/* when using random ports, servers which would otherwise use
|
||||
the INADDR_ANY/port0 socket have sfd set to NULL */
|
||||
if (!daemon->osport && intname[0] == 0)
|
||||
the INADDR_ANY/port0 socket have sfd set to NULL, this is
|
||||
anything without an explictly set source port. */
|
||||
if (!daemon->osport)
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
if (addr->sa.sa_family == AF_INET &&
|
||||
addr->in.sin_addr.s_addr == INADDR_ANY &&
|
||||
addr->in.sin_port == htons(0))
|
||||
return NULL;
|
||||
|
||||
if (addr->sa.sa_family == AF_INET6 &&
|
||||
memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&
|
||||
addr->in6.sin6_port == htons(0))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (intname && strlen(intname) != 0)
|
||||
ifindex = if_nametoindex(intname); /* index == 0 when not binding to an interface */
|
||||
|
||||
/* may have a suitable one already */
|
||||
for (sfd = daemon->sfds; sfd; sfd = sfd->next )
|
||||
if (sockaddr_isequal(&sfd->source_addr, addr) &&
|
||||
strcmp(intname, sfd->interface) == 0 &&
|
||||
ifindex == sfd->ifindex)
|
||||
if (ifindex == sfd->ifindex &&
|
||||
sockaddr_isequal(&sfd->source_addr, addr) &&
|
||||
strcmp(intname, sfd->interface) == 0)
|
||||
return sfd;
|
||||
|
||||
/* need to make a new one. */
|
||||
@@ -1414,7 +1464,7 @@ void pre_allocate_sfds(void)
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr.in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
if ((sfd = allocate_sfd(&addr, "")))
|
||||
if ((sfd = allocate_sfd(&addr, "", 0)))
|
||||
sfd->preallocated = 1;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
@@ -1424,13 +1474,13 @@ void pre_allocate_sfds(void)
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
if ((sfd = allocate_sfd(&addr, "")))
|
||||
if ((sfd = allocate_sfd(&addr, "", 0)))
|
||||
sfd->preallocated = 1;
|
||||
}
|
||||
|
||||
for (srv = daemon->servers; srv; srv = srv->next)
|
||||
if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
|
||||
!allocate_sfd(&srv->source_addr, srv->interface) &&
|
||||
!allocate_sfd(&srv->source_addr, srv->interface, srv->ifindex) &&
|
||||
errno != 0 &&
|
||||
option_bool(OPT_NOWILD))
|
||||
{
|
||||
@@ -1639,7 +1689,7 @@ void check_servers(void)
|
||||
|
||||
/* Do we need a socket set? */
|
||||
if (!serv->sfd &&
|
||||
!(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) &&
|
||||
!(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface, serv->ifindex)) &&
|
||||
errno != 0)
|
||||
{
|
||||
my_syslog(LOG_WARNING,
|
||||
|
||||
94
src/option.c
94
src/option.c
@@ -168,6 +168,8 @@ struct myoption {
|
||||
#define LOPT_SINGLE_PORT 359
|
||||
#define LOPT_SCRIPT_TIME 360
|
||||
#define LOPT_PXE_VENDOR 361
|
||||
#define LOPT_DYNHOST 362
|
||||
#define LOPT_LOG_DEBUG 363
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -341,6 +343,8 @@ static const struct myoption opts[] =
|
||||
{ "dumpfile", 1, 0, LOPT_DUMPFILE },
|
||||
{ "dumpmask", 1, 0, LOPT_DUMPMASK },
|
||||
{ "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID },
|
||||
{ "dynamic-host", 1, 0, LOPT_DYNHOST },
|
||||
{ "log-debug", 0, 0, LOPT_LOG_DEBUG },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
@@ -491,6 +495,7 @@ static struct {
|
||||
{ LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
|
||||
{ LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
|
||||
{ LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
|
||||
{ LOPT_DYNHOST, ARG_DUP, "<name>,[<IPv4>][,<IPv6>],<interface-name>", gettext_noop("Specify host record in interface subnet"), NULL },
|
||||
{ LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },
|
||||
{ LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
|
||||
{ LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
|
||||
@@ -512,6 +517,7 @@ static struct {
|
||||
{ LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
|
||||
{ LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
|
||||
{ LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
|
||||
{ LOPT_LOG_DEBUG, OPT_LOG_DEBUG, NULL, gettext_noop("Log debugging information."), NULL },
|
||||
{ LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
|
||||
{ LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
|
||||
{ LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
|
||||
@@ -813,7 +819,8 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
|
||||
if (interface_opt)
|
||||
{
|
||||
#if defined(SO_BINDTODEVICE)
|
||||
safe_strncpy(interface, interface_opt, IF_NAMESIZE);
|
||||
safe_strncpy(interface, source, IF_NAMESIZE);
|
||||
source = interface_opt;
|
||||
#else
|
||||
return _("interface binding not supported");
|
||||
#endif
|
||||
@@ -2481,8 +2488,14 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
case LOPT_IGNORE_ADDR: /* --ignore-address */
|
||||
{
|
||||
struct in_addr addr;
|
||||
int prefix = 32;
|
||||
unhide_metas(arg);
|
||||
if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
|
||||
|
||||
if (!arg ||
|
||||
((comma = split_chr(arg, '/')) && !atoi_check(comma, &prefix)) ||
|
||||
(inet_pton(AF_INET, arg, &addr) != 1))
|
||||
ret_err(gen_err); /* error */
|
||||
else
|
||||
{
|
||||
struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
|
||||
if (option == 'B')
|
||||
@@ -2495,12 +2508,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
baddr->next = daemon->ignore_addr;
|
||||
daemon->ignore_addr = baddr;
|
||||
}
|
||||
baddr->addr = addr;
|
||||
baddr->mask.s_addr = htonl(~((1 << (32 - prefix)) - 1));
|
||||
baddr->addr.s_addr = addr.s_addr & baddr->mask.s_addr;
|
||||
}
|
||||
else
|
||||
ret_err(gen_err); /* error */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'a': /* --listen-address */
|
||||
case LOPT_AUTHPEER: /* --auth-peer */
|
||||
@@ -4075,36 +4087,66 @@ err:
|
||||
}
|
||||
|
||||
case LOPT_INTNAME: /* --interface-name */
|
||||
case LOPT_DYNHOST: /* --dynamic-host */
|
||||
{
|
||||
struct interface_name *new, **up;
|
||||
char *domain = NULL;
|
||||
|
||||
comma = split(arg);
|
||||
char *domain = arg;
|
||||
|
||||
if (!comma || !(domain = canonicalise_opt(arg)))
|
||||
ret_err(_("bad interface name"));
|
||||
arg = split(arg);
|
||||
|
||||
new = opt_malloc(sizeof(struct interface_name));
|
||||
new->next = NULL;
|
||||
new->addr = NULL;
|
||||
memset(new, 0, sizeof(struct interface_name));
|
||||
new->flags = IN4 | IN6;
|
||||
|
||||
/* Add to the end of the list, so that first name
|
||||
of an interface is used for PTR lookups. */
|
||||
for (up = &daemon->int_names; *up; up = &((*up)->next));
|
||||
*up = new;
|
||||
new->name = domain;
|
||||
new->family = 0;
|
||||
arg = split_chr(comma, '/');
|
||||
if (arg)
|
||||
|
||||
while ((comma = split(arg)))
|
||||
{
|
||||
if (strcmp(arg, "4") == 0)
|
||||
new->family = AF_INET;
|
||||
else if (strcmp(arg, "6") == 0)
|
||||
new->family = AF_INET6;
|
||||
if (inet_pton(AF_INET, arg, &new->proto4))
|
||||
new->flags |= INP4;
|
||||
else if (inet_pton(AF_INET6, arg, &new->proto6))
|
||||
new->flags |= INP6;
|
||||
else
|
||||
break;
|
||||
|
||||
arg = comma;
|
||||
}
|
||||
|
||||
if ((comma = split_chr(arg, '/')))
|
||||
{
|
||||
if (strcmp(comma, "4") == 0)
|
||||
new->flags &= ~IN6;
|
||||
else if (strcmp(comma, "6") == 0)
|
||||
new->flags &= ~IN4;
|
||||
else
|
||||
ret_err_free(gen_err, new);
|
||||
}
|
||||
new->intr = opt_string_alloc(comma);
|
||||
}
|
||||
|
||||
new->intr = opt_string_alloc(arg);
|
||||
|
||||
if (option == LOPT_DYNHOST)
|
||||
{
|
||||
if (!(new->flags & (INP4 | INP6)))
|
||||
ret_err(_("missing address in dynamic host"));
|
||||
|
||||
if (!(new->flags & IN4) || !(new->flags & IN6))
|
||||
arg = NULL; /* provoke error below */
|
||||
|
||||
new->flags &= ~(IN4 | IN6);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (new->flags & (INP4 | INP6))
|
||||
arg = NULL; /* provoke error below */
|
||||
}
|
||||
|
||||
if (!domain || !arg || !(new->name = canonicalise_opt(domain)))
|
||||
ret_err(option == LOPT_DYNHOST ?
|
||||
_("bad dynamic host") : _("bad interface name"));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5034,9 +5076,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
daemon->soa_refresh = SOA_REFRESH;
|
||||
daemon->soa_retry = SOA_RETRY;
|
||||
daemon->soa_expiry = SOA_EXPIRY;
|
||||
daemon->max_port = MAX_PORT;
|
||||
daemon->min_port = MIN_PORT;
|
||||
|
||||
|
||||
#ifndef NO_ID
|
||||
add_txt("version.bind", "dnsmasq-" VERSION, 0 );
|
||||
add_txt("authors.bind", "Simon Kelley", 0);
|
||||
|
||||
@@ -1017,30 +1017,33 @@ int check_for_local_domain(char *name, time_t now)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Is the packet a reply with the answer address equal to addr?
|
||||
If so mung is into an NXDOMAIN reply and also put that information
|
||||
in the cache. */
|
||||
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
struct bogus_addr *baddr, time_t now)
|
||||
static int check_bad_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr, char *name, unsigned long *ttlp)
|
||||
{
|
||||
unsigned char *p;
|
||||
int i, qtype, qclass, rdlen;
|
||||
unsigned long ttl;
|
||||
struct bogus_addr *baddrp;
|
||||
|
||||
struct in_addr addr;
|
||||
|
||||
/* skip over questions */
|
||||
if (!(p = skip_questions(header, qlen)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
for (i = ntohs(header->ancount); i != 0; i--)
|
||||
{
|
||||
if (!extract_name(header, qlen, &p, name, 1, 10))
|
||||
if (name && !extract_name(header, qlen, &p, name, 1, 10))
|
||||
return 0; /* bad packet */
|
||||
|
||||
|
||||
if (!name && !(p = skip_name(p, header, qlen, 10)))
|
||||
return 0;
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
GETLONG(ttl, p);
|
||||
GETSHORT(rdlen, p);
|
||||
|
||||
if (ttlp)
|
||||
*ttlp = ttl;
|
||||
|
||||
if (qclass == C_IN && qtype == T_A)
|
||||
{
|
||||
@@ -1048,16 +1051,12 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
return 0;
|
||||
|
||||
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
|
||||
if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
|
||||
{
|
||||
/* Found a bogus address. Insert that info here, since there no SOA record
|
||||
to get the ttl from in the normal processing */
|
||||
cache_start_insert();
|
||||
cache_insert(name, NULL, C_IN, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN);
|
||||
cache_end_insert();
|
||||
|
||||
{
|
||||
memcpy(&addr, p, INADDRSZ);
|
||||
|
||||
if ((addr.s_addr & baddrp->mask.s_addr) == baddrp->addr.s_addr)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ADD_RDLEN(header, p, qlen, rdlen))
|
||||
@@ -1067,43 +1066,31 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr)
|
||||
/* Is the packet a reply with the answer address equal to addr?
|
||||
If so mung is into an NXDOMAIN reply and also put that information
|
||||
in the cache. */
|
||||
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, time_t now)
|
||||
{
|
||||
unsigned char *p;
|
||||
int i, qtype, qclass, rdlen;
|
||||
struct bogus_addr *baddrp;
|
||||
unsigned long ttl;
|
||||
|
||||
/* skip over questions */
|
||||
if (!(p = skip_questions(header, qlen)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
for (i = ntohs(header->ancount); i != 0; i--)
|
||||
if (check_bad_address(header, qlen, daemon->bogus_addr, name, &ttl))
|
||||
{
|
||||
if (!(p = skip_name(p, header, qlen, 10)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
p += 4; /* TTL */
|
||||
GETSHORT(rdlen, p);
|
||||
|
||||
if (qclass == C_IN && qtype == T_A)
|
||||
{
|
||||
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
|
||||
return 0;
|
||||
|
||||
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
|
||||
if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ADD_RDLEN(header, p, qlen, rdlen))
|
||||
return 0;
|
||||
/* Found a bogus address. Insert that info here, since there no SOA record
|
||||
to get the ttl from in the normal processing */
|
||||
cache_start_insert();
|
||||
cache_insert(name, NULL, C_IN, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN);
|
||||
cache_end_insert();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_for_ignored_address(struct dns_header *header, size_t qlen)
|
||||
{
|
||||
return check_bad_address(header, qlen, daemon->ignore_addr, NULL, NULL);
|
||||
}
|
||||
|
||||
int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp,
|
||||
unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
|
||||
|
||||
@@ -98,7 +98,7 @@ int add_to_ipset(const char *setname, const union all_addr *ipaddr,
|
||||
io.pfrio_size = 1;
|
||||
if (ioctl(dev, DIOCRADDTABLES, &io))
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("IPset: error:%s"), pfr_strerror(errno));
|
||||
my_syslog(LOG_WARNING, _("IPset: error: %s"), pfr_strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
35
src/tftp.c
35
src/tftp.c
@@ -22,7 +22,7 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
||||
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix);
|
||||
static void free_transfer(struct tftp_transfer *transfer);
|
||||
static ssize_t tftp_err(int err, char *packet, char *message, char *file);
|
||||
static ssize_t tftp_err_oops(char *packet, char *file);
|
||||
static ssize_t tftp_err_oops(char *packet, const char *file);
|
||||
static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
|
||||
static char *next(char **p, char *end);
|
||||
static void sanitise(char *buf);
|
||||
@@ -39,6 +39,7 @@ static void sanitise(char *buf);
|
||||
#define ERR_PERM 2
|
||||
#define ERR_FULL 3
|
||||
#define ERR_ILL 4
|
||||
#define ERR_TID 5
|
||||
|
||||
void tftp_request(struct listener *listen, time_t now)
|
||||
{
|
||||
@@ -94,7 +95,7 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
|
||||
if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
|
||||
return;
|
||||
|
||||
|
||||
/* Can always get recvd interface for IPv6 */
|
||||
if (!check_dest)
|
||||
{
|
||||
@@ -583,11 +584,27 @@ void check_tftp_listeners(time_t now)
|
||||
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
|
||||
if (poll_check(transfer->sockfd, POLLIN))
|
||||
{
|
||||
union mysockaddr peer;
|
||||
socklen_t addr_len = sizeof(union mysockaddr);
|
||||
ssize_t len;
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
handle_tftp(now, transfer, recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0));
|
||||
}
|
||||
|
||||
if ((len = recvfrom(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0, &peer.sa, &addr_len)) > 0)
|
||||
{
|
||||
if (sockaddr_isequal(&peer, &transfer->peer))
|
||||
handle_tftp(now, transfer, len);
|
||||
else
|
||||
{
|
||||
/* Wrong source address. See rfc1350 para 4. */
|
||||
prettyprint_addr(&peer, daemon->addrbuff);
|
||||
len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff);
|
||||
sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
|
||||
{
|
||||
tmp = transfer->next;
|
||||
@@ -602,7 +619,7 @@ void check_tftp_listeners(time_t now)
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
|
||||
if ((len = get_block(daemon->packet, transfer)) == -1)
|
||||
{
|
||||
len = tftp_err_oops(daemon->packet, transfer->file->filename);
|
||||
@@ -736,7 +753,8 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
|
||||
char *errstr = strerror(errno);
|
||||
|
||||
memset(packet, 0, daemon->packet_buff_sz);
|
||||
sanitise(file);
|
||||
if (file)
|
||||
sanitise(file);
|
||||
|
||||
mess->op = htons(OP_ERR);
|
||||
mess->err = htons(err);
|
||||
@@ -748,10 +766,11 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t tftp_err_oops(char *packet, char *file)
|
||||
static ssize_t tftp_err_oops(char *packet, const char *file)
|
||||
{
|
||||
/* May have >1 refs to file, so potentially mangle a copy of the name */
|
||||
strcpy(daemon->namebuff, file);
|
||||
if (file != daemon->namebuff)
|
||||
strcpy(daemon->namebuff, file);
|
||||
return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
|
||||
}
|
||||
|
||||
|
||||
@@ -316,7 +316,7 @@ void *whine_malloc(size_t size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
|
||||
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2)
|
||||
{
|
||||
if (s1->sa.sa_family == s2->sa.sa_family)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user