Compare commits

...
83 Commits
Author SHA1 Message Date
Simon Kelley d917275e48 Fix botch in new arp-cache linked-list code resulting in 100% CPU spin. 2016-01-04 17:17:41 +00:00
Simon Kelley cc7cb0b893 Fix datatype-sixe botch which broke DNSSEC sig timestamps when far in the future. 2016-01-04 16:04:51 +00:00
Simon Kelley ec0628c4b2 Trivial code tweak. 2015-12-31 20:55:39 +00:00
Simon Kelley 97b1d25764 Correct logic for when to start helper. 2015-12-31 18:52:38 +00:00
Simon Kelley 33702ab1f8 First complete version of DNS-client-id EDNS0 and ARP tracking code. 2015-12-28 23:17:15 +00:00
Simon Kelley 11867dc28c Cache access to the kernel's ARP table. 2015-12-23 16:15:58 +00:00
Simon Kelley d3a8b39c7d More EDNS0 packet-size tweaks. 2015-12-23 12:27:37 +00:00
Simon Kelley 15379ea1f2 Log signature algo with DNSKEY and DS, also digest with DS. 2015-12-21 18:31:55 +00:00
Simon Kelley efef497b89 Fix build failure when DNSSEC code omitted. 2015-12-21 17:30:44 +00:00
Simon Kelley 5aa5f0ff2f Truncate DNS replies >512 bytes that the client isn't expecting. 2015-12-21 17:20:35 +00:00
Simon Kelley 5bb88f0963 Handle extending EDNS0 OPT RR. 2015-12-21 16:23:47 +00:00
Simon Kelley 1d03016bbc Split EDNS0 stuff into its own source file. 2015-12-21 14:17:06 +00:00
Simon Kelley ce5732e84f NSEC3 check: RFC5155 para 8.2 2015-12-20 21:39:19 +00:00
Simon Kelley a86fdf437e Minor tweak to previous commit. 2015-12-20 21:19:20 +00:00
Simon Kelley 3e86d316c4 Nasty, rare and obscure off-by-one in DNSSEC hostname_cmp(). 2015-12-20 20:50:05 +00:00
Simon Kelley d67ecac59d More tweaks in handling unknown DNSSEC algorithms. 2015-12-20 20:44:23 +00:00
Simon Kelley fa14bec83b Major tidy up of EDNS0 handling and computation/use of udp packet size. 2015-12-20 17:12:16 +00:00
Simon Kelley 14a4ae883d Do a better job of determining which DNSSEC sig algos are supported. 2015-12-17 17:29:57 +00:00
Simon Kelley 3b799c826d Fix brace botch in dnssec_validate_ds()
Thanks to Michał Kępień for spotting this.
2015-12-17 16:58:04 +00:00
Simon Kelley b40f26c019 Tidy up DNSSEC non-existence code. Check zone status is NSEC proof bad. 2015-12-17 11:57:26 +00:00
Simon Kelley dd4ad9ac7e Tweaks to EDNS0 handling in DNS replies. 2015-12-17 10:44:58 +00:00
Simon Kelley 2dbba34b2c DNSSEC validation tweak.
A zone which has at least one key with an algorithm we don't
support should be considered as insecure.
2015-12-16 13:41:58 +00:00
Simon Kelley c2bcd1e183 Generalise RR-filtering code, for use with EDNS0. 2015-12-15 17:25:21 +00:00
Simon Kelley d64c81fff7 Move code which caches DS records to a more logical place. 2015-12-15 16:11:06 +00:00
Simon Kelley 93be5b1e02 Abandon caching RRSIGs and returning them from cache.
The list of exceptions to being able to locally answer
cached data for validated records when DNSSEC data is requested
was getting too long, so don't ever do that. This means
that the cache no longer has to hold RRSIGS and allows
us to lose lots of code. Note that cached validated
answers are still returned as long as do=0
2015-12-15 12:04:40 +00:00
Simon Kelley 9a31b68b59 Major rationalisation of DNSSEC validation.
Much gnarly special-case code removed and replaced with correct
general implementaion. Checking of zone-status moved to DNSSEC code,
where it should be, vastly simplifying query-forwarding code.
2015-12-15 10:20:39 +00:00
Simon Kelley 0007ee9064 Fix crash at start up with conf-dir=/path,*
Thanks to Brian Carpenter and American Fuzzy Lop for finding the bug.
2015-11-21 21:47:41 +00:00
Simon Kelley 67ab3285b5 Handle unknown DS hash algos correctly.
When we can validate a DS RRset, but don't speak the hash algo it
contains, treat that the same as an NSEC/3 proving that the DS
doesn't exist. 4025 5.2
2015-11-20 23:20:47 +00:00
Edwin Török 41a8d9e99b Fix crash when empty address from DNS overlays A record from hosts. 2015-11-14 17:55:41 +00:00
Simon Kelley 90477fb794 Update list of subnet for --bogus-priv
RFC6303 specifies & recommends following zones not be forwarded
to globally facing servers.
+------------------------------+-----------------------+
| Zone                         | Description           |
+------------------------------+-----------------------+
| 0.IN-ADDR.ARPA               | IPv4 "THIS" NETWORK   |
| 127.IN-ADDR.ARPA             | IPv4 Loopback NETWORK |
| 254.169.IN-ADDR.ARPA         | IPv4 LINK LOCAL       |
| 2.0.192.IN-ADDR.ARPA         | IPv4 TEST-NET-1       |
| 100.51.198.IN-ADDR.ARPA      | IPv4 TEST-NET-2       |
| 113.0.203.IN-ADDR.ARPA       | IPv4 TEST-NET-3       |
| 255.255.255.255.IN-ADDR.ARPA | IPv4 BROADCAST        |
+------------------------------+-----------------------+

Signed-off-by: Kevin Darbyshire-Bryant <kevin@darbyshire-bryant.me.uk>
2015-10-20 21:21:32 +01:00
Simon Kelley 98079ea898 Catch errors from sendmsg in DHCP code.
Logs, eg,  iptables DROPS of dest 255.255.255.255
2015-10-13 20:32:21 +01:00
Simon Kelley 4790115455 Use /run/dnsmasq instead of /var/run/dnsmasq in Debian package. 2015-09-29 22:54:41 +01:00
Simon Kelley 27b78d990b Rationalise 5e3e464ac4 2015-09-26 21:40:45 +01:00
Simon Kelley 77607cbea0 Respect the --no-resolv flag in inotify code. 2015-09-10 23:08:43 +01:00
Simon Kelley 102208df69 DHCPv6 option 56 does not hold an address list. (RFC 5908). 2015-09-10 21:50:00 +01:00
Simon Kelley 6de81f1250 Handle signed dangling CNAME replies to DS queries. 2015-09-09 22:51:13 +01:00
Simon Kelley 20fd11e11a Clarify man page on RDNSS set in router advertisement. 2015-08-26 22:48:13 +01:00
Simon Kelley 9cdcfe9f19 Suggest solution to ENOMEM error with IPv6 multicast. 2015-08-26 22:38:08 +01:00
Simon Kelley 5e3e464ac4 Fix behaviour of empty dhcp-option=option6:dns-server, which should inhibit sending option. 2015-08-25 23:08:39 +01:00
Simon Kelley 3a3965ac21 Don't answer non-auth queries for auth zones locally when --localise-queries set. 2015-08-09 17:45:06 +01:00
Ed Bardsley a7369bef8a Enhance --add-subnet to allow arbitary subnet addresses. 2015-08-05 21:17:18 +01:00
Simon Kelley d2aa7dfbb6 Include 0.0.0.0/8 in DNS rebind checks. 2015-08-03 21:52:12 +01:00
Simon Kelley 63ec5d1264 Fix new poll() code for helper pipe. Removed CPU-spin. 2015-07-30 20:59:07 +01:00
Simon Kelley f6381cf482 Declare utime(). 2015-07-27 19:48:43 +01:00
Kevin Darbyshire-Bryant 34b5d19488 Update DNSSEC timestamp file on process TERM. 2015-07-27 19:34:23 +01:00
Conrad Kostecki d6cb7e4815 Update german translation 2015-07-27 19:22:49 +01:00
Simon Kelley d389e0191b DNSSEC fix: correct logic for signed records in unsigned DNS space. 2015-07-27 18:56:43 +01:00
Simon Kelley d3699bb6bc Small tweak to DNSSEC fix. 2015-07-16 22:37:37 +01:00
Simon Kelley 13480e8c2a DNSSEC fix, signed wildcard CNAME to unsigned domain. 2015-07-16 22:23:13 +01:00
Jan Psota 5b3b93f80a Update Polish translation. 2015-07-15 19:57:47 +01:00
Simon Kelley b69e845b1c Close Lauchpad bug in Debian changelog. 2015-07-15 19:54:50 +01:00
Simon Kelley 90c3822bfa Grow pollfds array exponentially. 2015-07-13 12:47:52 +01:00
Simon Kelley c895a0626d Merge messages for translations. 2015-07-12 21:27:40 +01:00
Simon Kelley b842bc97bb Use poll() instead of select() to remove limits on open file descriptors. 2015-07-12 21:09:11 +01:00
Simon Kelley 0f38fa05a6 Log message typo. 2015-07-08 22:42:14 +01:00
Simon Kelley 45c5cb1f8f Fix compilation warning. 2015-07-08 22:40:57 +01:00
Simon Kelley f6d6956261 Test for overflowing platform FD_SET size. 2015-07-08 22:38:13 +01:00
Simon Kelley 60176c7bf4 Bump version in Debian changelog. 2015-07-07 21:54:55 +01:00
Simon Kelley 362c9303da Fix inotify code to handle dangling symlinks better. 2015-07-06 21:48:49 +01:00
Simon Kelley 5e95a552ee Avoid hanngs in DHCP ping code when system time goes backwards. 2015-07-05 22:31:30 +01:00
Simon Kelley 90cb222551 --conf-file should read no file, not try and read the default file. 2015-07-05 21:59:10 +01:00
Ján Sáreník 850163288d Manpage typo fix. 2015-07-05 21:23:27 +01:00
Simon Kelley e3ec6f0bd7 Handle CNAMEs to DS records when confirming absence of DS for DNSSEC. 2015-06-12 21:39:11 +01:00
Simon Kelley f7bfbdc872 Merge messages and fix makefile process to do this. 2015-06-10 22:31:02 +01:00
Neil Jerram 4918bd5505 Documenation updates for --bridge-interface and "off-link". 2015-06-10 22:23:20 +01:00
Neil Jerram 9bad339af8 Apply --bridge-interfaces to unsolicited router advertisements. 2015-06-10 22:16:35 +01:00
Neil Jerram ba4fc0f996 Upply --bridge-interface aliasing to solicited router advertisements. 2015-06-10 22:14:49 +01:00
Neil Jerram 2fd5bc952d Allow router advertisements to have the "off-link" bit set. 2015-06-10 22:13:06 +01:00
Neil Jerram 0ddb8769bb Extend --bridge-interface aliasing to DHCPv6. 2015-06-10 22:11:06 +01:00
Neil Jerram 654f59e762 Fix logging of unknown interface in --bridge-interface, DHPCv4. 2015-06-10 22:06:33 +01:00
Simon Kelley d91b1fd402 Add a couple of missed logging strings to the catalogue. 2015-06-09 20:45:07 +01:00
Nicolas Cavallari c6d82c9ba6 Add Dbus methods to create and delete DHCP leases. 2015-06-09 20:42:20 +01:00
Simon Kelley 4d25cf89d5 Handle corner cases in NSEC coverage checks. 2015-06-06 23:13:57 +01:00
Simon Kelley 24e9207e13 More reproducibility fixes for Debian package. 2015-06-04 22:32:43 +01:00
Simon Kelley 89130d91d6 DHCPv6: DHCPCONFIRM should be OK for any address on link, not just dynamic addresses. 2015-06-03 22:34:14 +01:00
Simon Kelley d644b2a17d Close Debian bug for bug fixed upstream. 2015-06-01 21:00:16 +01:00
swigger bd7bfa21c4 Correctly sanitise DNS header bits in answer when recreating query for retry. 2015-06-01 20:54:59 +01:00
Simon Kelley 403de05925 Merge branch 'master' of ssh://thekelleys.org.uk/var/cache/git/dnsmasq 2015-05-26 22:12:01 +01:00
John Hanks 46c89f2bd0 Add infiniband to example config file. 2015-05-26 22:07:57 +01:00
Christian Demsar 23facf0d77 Man page typo. 2015-05-20 20:26:23 +01:00
Simon Kelley 549b1a478c Tweak immediately previous patch. 2015-05-20 20:20:24 +01:00
Simon Kelley 7f8565b94c Select correct DHCP context when in PXE bootserver mode. 2015-05-19 23:01:27 +01:00
Simon Kelley 06568c6636 Remove support for DNS Extended Label Types.
The support was only partial, and the whole concept is
now deprecated in the standards.
2015-05-15 20:43:48 +01:00
54 changed files with 7601 additions and 6590 deletions

No files matched your search

+53
View File
@@ -1,3 +1,42 @@
version 2.76
Include 0.0.0.0/8 in DNS rebind checks. This range
translates to hosts on the local network, or, at
least, 0.0.0.0 accesses the local host, so could
be targets for DNS rebinding. See RFC 5735 section 3
for details. Thanks to Stephen Röttger for the bug report.
Enhance --add-subnet to allow arbitrary subnet addresses.
Thanks to Ed Barsley for the patch.
Respect the --no-resolv flag in inotify code. Fixes bug
which caused dnsmasq to fail to start if a resolv-file
was a dangling symbolic link, even of --no-resolv set.
Thanks to Alexander Kurtz for spotting the problem.
Fix crash when an A or AAAA record is defined locally,
in a hosts file, and an upstream server sends a reply
that the same name is empty. Thanks to Edwin Török for
the patch.
version 2.75
Fix reversion on 2.74 which caused 100% CPU use when a
dhcp-script is configured. Thanks to Adrian Davey for
reporting the bug and testing the fix.
version 2.74
Fix reversion in 2.73 where --conf-file would attempt to
read the default file, rather than no file.
Fix inotify code to handle dangling symlinks better and
not SEGV in some circumstances.
DNSSEC fix. In the case of a signed CNAME generated by a
wildcard which pointed to an unsigned domain, the wrong
status would be logged, and some necessary checks omitted.
version 2.73
Fix crash at startup when an empty suffix is supplied to
--conf-dir, also trivial memory leak. Thanks to
@@ -121,6 +160,20 @@ version 2.73
Allow DHCPv4 options T1 and T2 to be set using --dhcp-option.
Thanks to Kevin Benton for patches and work on this.
Fix code for DHCPCONFIRM DHCPv6 messages to confirm addresses
in the correct subnet, even of not in dynamic address
allocation range. Thanks to Steve Hirsch for spotting
the problem.
Add AddDhcpLease and DeleteDhcpLease DBus methods. Thanks
to Nicolas Cavallari for the patch.
Allow configuration of router advertisements without the
"on-link" bit set. Thanks to Neil Jerram for the patch.
Extend --bridge-interface to DHCPv6 and router
advertisements. Thanks to Neil Jerram for the patch.
version 2.72
Add ra-advrouter mode, for RFC-3775 mobile IPv6 support.
+3 -2
View File
@@ -73,7 +73,8 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
poll.o rrfilter.o edns0.o arp.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h ip6addr.h
@@ -117,7 +118,7 @@ install-i18n : all-i18n install-common
cd $(MAN); ../bld/install-man $(DESTDIR)$(MANDIR) $(INSTALL)
merge :
@cd $(BUILDDIR) && $(MAKE) -f $(top)/Makefile dnsmasq.pot
@cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/Makefile dnsmasq.pot
for f in `cd $(PO); echo *.po`; do \
echo -n msgmerge $(PO)/$$f && $(MSGMERGE) --no-wrap -U $(PO)/$$f $(BUILDDIR)/dnsmasq.pot; \
done
+1 -1
View File
@@ -10,7 +10,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
radv.c slaac.c auth.c ipset.c domain.c \
dnssec.c dnssec-openssl.c blockdata.c tables.c \
loop.c inotify.c
loop.c inotify.c poll.c rrfilter.c edns0.c arp.c
LOCAL_MODULE := dnsmasq
+74
View File
@@ -169,6 +169,80 @@ Return an array of strings, each string is the IP address of an upstream
server which has been found to loop queries back to this dnsmasq instance, and
it therefore not being used.
AddDhcpLease
------------
Returns nothing. Adds or updates a DHCP or DHCPv6 lease to the internal lease
database, as if a client requested and obtained a lease.
If a lease for the IPv4 or IPv6 address already exist, it is overwritten.
Note that this function will trigger the DhcpLeaseAdded or DhcpLeaseUpdated
D-Bus signal and will run the configured DHCP lease script accordingly.
This function takes many arguments which are the lease parameters:
- A string with the textual representation of the IPv4 or IPv6 address of the
client.
Examples:
"192.168.1.115"
"1003:1234:abcd::1%eth0"
"2001:db8:abcd::1"
- A string representing the hardware address of the client, using the same
format as the one used in the lease database.
Examples:
"00:23:45:67:89:ab"
"06-00:20:e0:3b:13:af" (token ring)
- The hostname of the client, as an array of bytes (so there is no problem
with non-ASCII character encoding). May be empty.
Example (for "hostname.or.fqdn"):
[104, 111, 115, 116, 110, 97, 109, 101, 46, 111, 114, 46, 102, 113, 100, 110]
- The client identifier (IPv4) or DUID (IPv6) as an array of bytes. May be
empty.
Examples:
DHCPv6 DUID:
[0, 3, 0, 1, 0, 35, 69, 103, 137, 171]
DHCPv4 client identifier:
[255, 12, 34, 56, 78, 0, 1, 0, 1, 29, 9, 99, 190, 35, 69, 103, 137, 171]
- The duration of the lease, in seconds. If the lease is updated, then
the duration replaces the previous duration.
Example:
7200
- The IAID (Identity association identifier) of the DHCPv6 lease, as a network
byte-order unsigned integer. For DHCPv4 leases, this must be set to 0.
Example (for IPv6):
203569230
- A boolean which, if true, indicates that the DHCPv6 lease is for a temporary
address (IA_TA). If false, the DHCPv6 lease is for a non-temporary address
(IA_NA). For DHCPv4 leases, this must be set to false.
RemoveDhcpLease
---------------
Returns nothing. Removes a DHCP or DHCPv6 lease to the internal lease
database, as if a client sent a release message to abandon a lease.
This function takes only one parameter: the text representation of the
IPv4 or IPv6 address of the lease to remove.
Note that this function will trigger the DhcpLeaseRemoved signal and the
configured DHCP lease script will be run with the "del" action.
2. SIGNALS
+28 -2
View File
@@ -1,13 +1,39 @@
dnsmasq (2.76-1) unstable; urgency=low
* New upstream. (closes: #798586)
* Use /run/dnsmasq directly, rather than relying on link from /var/run
to avoid problems before /var is mounted. (closes: #800351)
-- Simon Kelley <simon@thekelleys.org.uk> Thur, 10 Sep 2015 23:07:21 +0000
dnsmasq (2.75-1) unstable; urgency=low
* New upstream. (closes: #794095)
-- Simon Kelley <simon@thekelleys.org.uk> Thur, 30 Jul 2015 20:58:31 +0000
dnsmasq (2.74-1) unstable; urgency=low
* New upstream. (LP: #1468611)
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 15 Jul 2015 21:54:11 +0000
dnsmasq (2.73-2) unstable; urgency=low
* Fix behaviour of empty --conf-file (closes: #790341)
-- Simon Kelley <simon@thekelleys.org.uk> Thu, 7 Jul 2015 21:46:42 +0000
dnsmasq (2.73-1) unstable; urgency=low
* New upstream.
* New upstream. (closes: #786996)
* Tweak field width in cache dump to avoid truncating IPv6
addresses. (closes: #771557)
* Add newline at the end of example config file. (LP: #1416895)
* Make Debian package build reproducible. (closes: #777323)
* Add Requires=network.target to systemd unit.
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 11 Feb 2015 21:59:42 +0000
-- Simon Kelley <simon@thekelleys.org.uk> Thu, 4 Jun 2015 22:31:42 +0000
dnsmasq (2.72-3) unstable; urgency=medium
+3 -3
View File
@@ -17,8 +17,8 @@ if [ "$1" = "configure" ]; then
# dnsmasq-base, but it's much easier to create it here so that
# we don't have synchronisation issues with the creation of the
# dnsmasq user.
if [ ! -d /var/run/dnsmasq ]; then
mkdir /var/run/dnsmasq
chown dnsmasq:nogroup /var/run/dnsmasq
if [ ! -d /run/dnsmasq ]; then
mkdir /run/dnsmasq
chown dnsmasq:nogroup /run/dnsmasq
fi
fi
+1 -1
View File
@@ -7,5 +7,5 @@ if [ purge = "$1" ]; then
else
echo >&2 "not removing dnsmasq system account because deluser command was not found"
fi
rm -rf /var/run/dnsmasq
rm -rf /run/dnsmasq
fi
Vendored
+19 -19
View File
@@ -81,7 +81,7 @@ if [ ! "$RESOLV_CONF" ] &&
[ "$IGNORE_RESOLVCONF" != "yes" ] &&
[ -x /sbin/resolvconf ]
then
RESOLV_CONF=/var/run/dnsmasq/resolv.conf
RESOLV_CONF=/run/dnsmasq/resolv.conf
fi
for INTERFACE in $DNSMASQ_INTERFACE; do
@@ -121,16 +121,16 @@ start()
# 1 if daemon was already running
# 2 if daemon could not be started
# /var/run may be volatile, so we need to ensure that
# /var/run/dnsmasq exists here as well as in postinst
if [ ! -d /var/run/dnsmasq ]; then
mkdir /var/run/dnsmasq || return 2
chown dnsmasq:nogroup /var/run/dnsmasq || return 2
# /run may be volatile, so we need to ensure that
# /run/dnsmasq exists here as well as in postinst
if [ ! -d /run/dnsmasq ]; then
mkdir /run/dnsmasq || return 2
chown dnsmasq:nogroup /run/dnsmasq || return 2
fi
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON --test > /dev/null || return 1
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON -- \
-x /var/run/dnsmasq/$NAME.pid \
start-stop-daemon --start --quiet --pidfile /run/dnsmasq/$NAME.pid --exec $DAEMON --test > /dev/null || return 1
start-stop-daemon --start --quiet --pidfile /run/dnsmasq/$NAME.pid --exec $DAEMON -- \
-x /run/dnsmasq/$NAME.pid \
${MAILHOSTNAME:+ -m $MAILHOSTNAME} \
${MAILTARGET:+ -t $MAILTARGET} \
${DNSMASQ_USER:+ -u $DNSMASQ_USER} \
@@ -167,7 +167,7 @@ stop()
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile /var/run/dnsmasq/$NAME.pid --name $NAME
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile /run/dnsmasq/$NAME.pid --name $NAME
}
stop_resolvconf()
@@ -185,9 +185,9 @@ status()
# 1 if daemon is dead and pid file exists
# 3 if daemon is not running
# 4 if daemon status is unknown
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON --test > /dev/null
start-stop-daemon --start --quiet --pidfile /run/dnsmasq/$NAME.pid --exec $DAEMON --test > /dev/null
case "$?" in
0) [ -e "/var/run/dnsmasq/$NAME.pid" ] && return 1 ; return 3 ;;
0) [ -e "/run/dnsmasq/$NAME.pid" ] && return 1 ; return 3 ;;
1) return 0 ;;
*) return 4 ;;
esac
@@ -278,7 +278,7 @@ case "$1" in
esac
;;
dump-stats)
kill -s USR1 `cat /var/run/dnsmasq/$NAME.pid`
kill -s USR1 `cat /run/dnsmasq/$NAME.pid`
;;
systemd-start-resolvconf)
start_resolvconf
@@ -287,13 +287,13 @@ case "$1" in
stop_resolvconf
;;
systemd-exec)
# /var/run may be volatile, so we need to ensure that
# /var/run/dnsmasq exists here as well as in postinst
if [ ! -d /var/run/dnsmasq ]; then
mkdir /var/run/dnsmasq || return 2
chown dnsmasq:nogroup /var/run/dnsmasq || return 2
# /run may be volatile, so we need to ensure that
# /run/dnsmasq exists here as well as in postinst
if [ ! -d /run/dnsmasq ]; then
mkdir /run/dnsmasq || return 2
chown dnsmasq:nogroup /run/dnsmasq || return 2
fi
exec $DAEMON -x /var/run/dnsmasq/$NAME.pid \
exec $DAEMON -x /run/dnsmasq/$NAME.pid \
${MAILHOSTNAME:+ -m $MAILHOSTNAME} \
${MAILTARGET:+ -t $MAILTARGET} \
${DNSMASQ_USER:+ -u $DNSMASQ_USER} \
+1 -1
View File
@@ -21,7 +21,7 @@ if [ -x /etc/init.d/dnsmasq ]; then
update-rc.d dnsmasq defaults 15 85 >/dev/null
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ]; then
if [ -e /var/run/dnsmasq/dnsmasq.pid ]; then
if [ -e /run/dnsmasq/dnsmasq.pid ]; then
ACTION=restart
else
ACTION=start
+1 -1
View File
@@ -13,7 +13,7 @@
set -e
RUN_DIR="/var/run/dnsmasq"
RUN_DIR="/run/dnsmasq"
RSLVRLIST_FILE="${RUN_DIR}/resolv.conf"
TMP_FILE="${RSLVRLIST_FILE}_new.$$"
MY_NAME_FOR_RESOLVCONF="dnsmasq"
+7 -4
View File
@@ -24,6 +24,7 @@ DEB_COPTS = $(COPTS)
TARGET = install-i18n
DEB_HOST_ARCH_OS := $(shell dpkg-architecture -qDEB_HOST_ARCH_OS)
BUILD_DATE := $(shell dpkg-parsechangelog --show-field Date)
# Force package version based on git tags.
ifneq (,$(filter gitversion,$(DEB_BUILD_OPTIONS)))
@@ -118,8 +119,9 @@ binary-indep: checkroot
install -m 644 debian/systemd.service debian/daemon/lib/systemd/system/dnsmasq.service
install -m 644 debian/insserv debian/daemon/etc/insserv.conf.d/dnsmasq
ln -s $(package) debian/daemon/usr/share/doc/dnsmasq
cd debian/daemon && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
cd debian/daemon && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-gencontrol $(PACKAGE_VERSION) -T -pdnsmasq -Pdebian/daemon
find debian/daemon -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)'
chown -R root.root debian/daemon
chmod -R g-ws debian/daemon
dpkg --build debian/daemon ..
@@ -132,7 +134,6 @@ binary-arch: checkroot
-d debian/base/etc/dbus-1/system.d \
-d debian/base/usr/share/doc/$(package) \
-d debian/base/usr/share/doc/$(package)/examples \
-d debian/base/var/run \
-d debian/base/usr/share/$(package) \
-d debian/base/var/lib/misc
make $(TARGET) PREFIX=/usr DESTDIR=`pwd`/debian/base CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=gcc
@@ -169,9 +170,10 @@ endif
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
strip -R .note -R .comment debian/base/usr/sbin/dnsmasq
endif
cd debian/base && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
cd debian/base && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps --warnings=1 debian/base/usr/sbin/dnsmasq
dpkg-gencontrol $(PACKAGE_VERSION) -pdnsmasq-base -Pdebian/base
find debian/base -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)'
chown -R root.root debian/base
chmod -R g-ws debian/base
dpkg --build debian/base ..
@@ -196,9 +198,10 @@ ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
strip -R .note -R .comment debian/utils/usr/bin/dhcp_release
strip -R .note -R .comment debian/utils/usr/bin/dhcp_lease_time
endif
cd debian/utils && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
cd debian/utils && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps -Tdebian/utils-substvars debian/utils/usr/bin/dhcp_release debian/utils/usr/bin/dhcp_lease_time
dpkg-gencontrol $(PACKAGE_VERSION) -Tdebian/utils-substvars -pdnsmasq-utils -Pdebian/utils
find debian/utils -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)'
chown -R root.root debian/utils
chmod -R g-ws debian/utils
dpkg --build debian/utils ..
+1 -1
View File
@@ -4,7 +4,7 @@ Requires=network.target
[Service]
Type=forking
PIDFile=/var/run/dnsmasq/dnsmasq.pid
PIDFile=/run/dnsmasq/dnsmasq.pid
# Test the config file and refuse starting if it is not valid.
ExecStartPre=/usr/sbin/dnsmasq --test
+7
View File
@@ -251,6 +251,13 @@
# the IP address 192.168.0.60
#dhcp-host=id:01:02:02:04,192.168.0.60
# Always give the Infiniband interface with hardware address
# 80:00:00:48:fe:80:00:00:00:00:00:00:f4:52:14:03:00:28:05:81 the
# ip address 192.168.0.61. The client id is derived from the prefix
# ff:00:00:00:00:00:02:00:00:02:c9:00 and the last 8 pairs of
# hex digits of the hardware address.
#dhcp-host=id:ff:00:00:00:00:00:02:00:00:02:c9:00:f4:52:14:03:00:28:05:81,192.168.0.61
# Always give the host with client identifier "marjorie"
# the IP address 192.168.0.60
#dhcp-host=id:marjorie,192.168.0.60
+37 -22
View File
@@ -604,17 +604,27 @@ experimental. Also note that exposing MAC addresses in this way may
have security and privacy implications. The warning about caching
given for --add-subnet applies to --add-mac too.
.TP
.B --add-subnet[[=<IPv4 prefix length>],<IPv6 prefix length>]
Add the subnet address of the requestor to the DNS queries which are
forwarded upstream. The amount of the address forwarded depends on the
prefix length parameter: 32 (128 for IPv6) forwards the whole address,
zero forwards none of it but still marks the request so that no
upstream nameserver will add client address information either. The
default is zero for both IPv4 and IPv6. Note that upstream nameservers
may be configured to return different results based on this
information, but the dnsmasq cache does not take account. If a dnsmasq
instance is configured such that different results may be encountered,
caching should be disabled.
.B --add-subnet[[=[<IPv4 address>/]<IPv4 prefix length>][,[<IPv6 address>/]<IPv6 prefix length>]]
Add a subnet address to the DNS queries which are forwarded
upstream. If an address is specified in the flag, it will be used,
otherwise, the address of the requestor will be used. The amount of
the address forwarded depends on the prefix length parameter: 32 (128
for IPv6) forwards the whole address, zero forwards none of it but
still marks the request so that no upstream nameserver will add client
address information either. The default is zero for both IPv4 and
IPv6. Note that upstream nameservers may be configured to return
different results based on this information, but the dnsmasq cache
does not take account. If a dnsmasq instance is configured such that
different results may be encountered, caching should be disabled.
For example,
.B --add-subnet=24,96
will add the /24 and /96 subnets of the requestor for IPv4 and IPv6 requestors, respectively.
.B --add-subnet=1.2.3.4/24
will add 1.2.3.0/24 for IPv4 requestors and ::/0 for IPv6 requestors.
.B --add-subnet=1.2.3.4/24,1.2.3.4/24
will add 1.2.3.0/24 for both IPv4 and IPv6 requestors.
.TP
.B \-c, --cache-size=<cachesize>
Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching.
@@ -654,7 +664,7 @@ Provide DS records to act a trust anchors for DNSSEC
validation. Typically these will be the DS record(s) for Zone Signing
key(s) of the root zone,
but trust anchors for limited domains are also possible. The current
root-zone trust anchors may be donwloaded from https://data.iana.org/root-anchors/root-anchors.xml
root-zone trust anchors may be downloaded from https://data.iana.org/root-anchors/root-anchors.xml
.TP
.B --dnssec-check-unsigned
As a default, dnsmasq does not check that unsigned DNS replies are
@@ -833,7 +843,7 @@ and
for details.)
For IPv6, the mode may be some combination of
.B ra-only, slaac, ra-names, ra-stateless, ra-advrouter.
.B ra-only, slaac, ra-names, ra-stateless, ra-advrouter, off-link.
.B ra-only
tells dnsmasq to offer Router Advertisement only on this subnet,
@@ -873,6 +883,9 @@ enables a mode where router address(es) rather than prefix(es) are included in t
This is described in RFC-3775 section 7.2 and is used in mobile IPv6. In this mode the interval option
is also included, as described in RFC-3775 section 7.3.
.B off-link
tells dnsmasq to advertise the prefix without the on-link (aka L) bit set.
.TP
.B \-G, --dhcp-host=[<hwaddr>][,id:<client_id>|*][,set:<tag>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
Specify per host parameters for the DHCP server. This allows a machine
@@ -1388,7 +1401,7 @@ enables dynamic allocation. With tags, only when the tags are all
set. It may be repeated with different tag sets.
.TP
.B \-5, --no-ping
(IPv4 only) By default, the DHCP server will attempt to ensure that an address in
(IPv4 only) By default, the DHCP server will attempt to ensure that an address is
not in use before allocating it to a host. It does this by sending an
ICMP echo request (aka "ping") to the address in question. If it gets
a reply, then the address must already be in use, and another is
@@ -1597,11 +1610,13 @@ option also forces the leasechange script to be called on changes
to the client-id and lease length and expiry time.
.TP
.B --bridge-interface=<interface>,<alias>[,<alias>]
Treat DHCP request packets arriving at any of the <alias> interfaces
as if they had arrived at <interface>. This option is necessary when
using "old style" bridging on BSD platforms, since
packets arrive at tap interfaces which don't have an IP address.
A trailing '*' wildcard can be used in each <alias>.
Treat DHCP (v4 and v6) request and IPv6 Router Solicit packets
arriving at any of the <alias> interfaces as if they had arrived at
<interface>. This option allows dnsmasq to provide DHCP and RA
service over unaddressed and unbridged Ethernet interfaces, e.g. on an
OpenStack compute host where each such interface is a TAP interface to
a VM, or as in "old style bridging" on BSD platforms. A trailing '*'
wildcard can be used in each <alias>.
.TP
.B \-s, --domain=<domain>[,<address range>[,local]]
Specifies DNS domains for the DHCP server. Domains may be be given
@@ -1672,15 +1687,15 @@ creation are handled by a different protocol. When DHCP is in use,
only a subset of this is needed, and dnsmasq can handle it, using
existing DHCP configuration to provide most data. When RA is enabled,
dnsmasq will advertise a prefix for each dhcp-range, with default
router and recursive DNS server as the relevant link-local address on
the machine running dnsmasq. By default, he "managed address" bits are set, and
router as the relevant link-local address on
the machine running dnsmasq. By default, the "managed address" bits are set, and
the "use SLAAC" bit is reset. This can be changed for individual
subnets with the mode keywords described in
.B --dhcp-range.
RFC6106 DNS parameters are included in the advertisements. By default,
the relevant link-local address of the machine running dnsmasq is sent
as recursive DNS server. If provided, the DHCPv6 options dns-server and
domain-search are used for RDNSS and DNSSL.
domain-search are used for the DNS server (RDNSS) and the domain serach list (DNSSL).
.TP
.B --ra-param=<interface>,[high|low],[[<ra-interval>],<router lifetime>]
Set non-default values for router advertisements sent via an
+8 -4
View File
@@ -1062,10 +1062,14 @@ esta opci
cuando hay cambios hechos a el client-id y tiempos de arriendo y vencimiento.
.TP
.B --bridge-interface=<nombre de interface>,<alias>[,<alias>]
Tratar paquetes de pedidos DHCP que llegan a cualquiera de las interfaces <alias>
como si hubieran llegado a la interface <nombre de interface>. Esta opción
es necesaria al usar bridging estilo viejo en plataformas BSD, dado a que
los paquetes llegan a interfaces tap que no tienen una dirección IP.
Tratar paquetes de pedidos DHCP (v4 y v6) y de IPv6 Router Solicit que
llegan a cualquiera de las interfaces <alias> como si hubieran llegado
a la interface <nombre de interface>. Esta opción permite que dnsmasq
puede proporcionar los servicios DHCP y RA a través de interfaces
ethernet sin dirección y sin puente; por ejemplo en un nodo de cálculo
de OpenStack, donde cada una de esas interfaces es una interfaz TAP
para una máquina virtual, o al usar bridging estilo viejo en
plataformas BSD.
.TP
.B \-s, --domain=<dominio>[,<rango de IPs>]
Especifica los dominios DNS para el servidor DHCP. Dominios pueden ser
+12 -6
View File
@@ -852,7 +852,7 @@ et
pour plus de détails).
Pour IPv6, le mode peut-être une combinaison des valeurs
.B ra-only, slaac, ra-names, ra-stateless.
.B ra-only, slaac, ra-names, ra-stateless, off-link.
.B ra-only
indique à dnsmasq de n'effectuer que des annonces de routeur (Router
@@ -888,6 +888,9 @@ peut-être combiné avec
et
.B slaac.
.B off-link
indique à dnsmasq d'annoncer le préfixe sans le bit L (sur lien).
.TP
.B \-G, --dhcp-host=[<adresse matérielle>][,id:<identifiant client>|*][,set:<label>][,<adresse IP>][,<nom d'hôte>][,<durée de bail>][,ignore]
Spécifie les paramètres DHCP relatifs à un hôte. Cela permet à une machine
@@ -1655,11 +1658,14 @@ changement d'état de bail à chaque changement de l'identifiant de client, de
longueur de bail ou de date d'expiration.
.TP
.B --bridge-interface=<interface>,<alias>[,<alias>]
Traiter les requêtes DHCP arrivant sur n'importe laquelle des interfaces <alias>
comme si elles arrivaient de l'interface <interface>. Cette option est
nécessaire lors de l'utilisation de pont ethernet "ancien mode" sur plate-forme
BSD, puisque dans ce cas les paquets arrivent sur des interfaces "tap" n'ont
pas d'adresse IP. Chaque <alias> peut finir avec un simple '*' joker.
Traiter les requêtes DHCP (v4 et v6) et IPv6 Router Solicit arrivant
sur n'importe laquelle des interfaces <alias> comme si elles
arrivaient de l'interface <interface>. Cette option permet à dnsmasq
de fournir les service DHCP et RA sur les interfaces ethernet non
adressés et non pontés; par exemple sur un hôte de calcul d'OpenStack
où chaque telle interface est une interface TAP à une machine
virtuelle, ou lors de l'utilisation de pont ethernet "ancien mode" sur
plate-forme BSD. Chaque <alias> peut finir avec un simple '*' joker.
.TP
.B \-s, --domain=<domaine>[,<gamme d'adresses>[,local]]
Spécifie le domaine du serveur DHCP. Le domaine peut être donné de manière
+441 -428
View File
File diff suppressed because it is too large. Load diff
+433 -400
View File
File diff suppressed because it is too large. Load diff
+426 -397
View File
File diff suppressed because it is too large. Load diff
+426 -393
View File
File diff suppressed because it is too large. Load diff
+449 -415
View File
File diff suppressed because it is too large. Load diff
+426 -397
View File
File diff suppressed because it is too large. Load diff
+443 -410
View File
File diff suppressed because it is too large. Load diff
+469 -447
View File
File diff suppressed because it is too large. Load diff
+426 -397
View File
File diff suppressed because it is too large. Load diff
+443 -410
View File
File diff suppressed because it is too large. Load diff
+235
View File
@@ -0,0 +1,235 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
/* Time between forced re-loads from kernel. */
#define INTERVAL 90
#define ARP_MARK 0
#define ARP_FOUND 1 /* Confirmed */
#define ARP_NEW 2 /* Newly created */
#define ARP_EMPTY 3 /* No MAC addr */
struct arp_record {
unsigned short hwlen, status;
int family;
unsigned char hwaddr[DHCP_CHADDR_MAX];
struct all_addr addr;
struct arp_record *next;
};
static struct arp_record *arps = NULL, *old = NULL, *freelist = NULL;
static time_t last = 0;
static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
{
struct arp_record *arp;
(void)parmv;
if (maclen > DHCP_CHADDR_MAX)
return 1;
/* Look for existing entry */
for (arp = arps; arp; arp = arp->next)
{
if (family != arp->family || arp->status == ARP_NEW)
continue;
if (family == AF_INET)
{
if (arp->addr.addr.addr4.s_addr != ((struct in_addr *)addrp)->s_addr)
continue;
}
#ifdef HAVE_IPV6
else
{
if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, (struct in6_addr *)addrp))
continue;
}
#endif
if (arp->status == ARP_EMPTY)
{
/* existing address, was negative. */
arp->status = ARP_NEW;
arp->hwlen = maclen;
memcpy(arp->hwaddr, mac, maclen);
}
else if (arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0)
/* Existing entry matches - confirm. */
arp->status = ARP_FOUND;
else
continue;
break;
}
if (!arp)
{
/* New entry */
if (freelist)
{
arp = freelist;
freelist = freelist->next;
}
else if (!(arp = whine_malloc(sizeof(struct arp_record))))
return 1;
arp->next = arps;
arps = arp;
arp->status = ARP_NEW;
arp->hwlen = maclen;
arp->family = family;
memcpy(arp->hwaddr, mac, maclen);
if (family == AF_INET)
arp->addr.addr.addr4.s_addr = ((struct in_addr *)addrp)->s_addr;
#ifdef HAVE_IPV6
else
memcpy(&arp->addr.addr.addr6, addrp, IN6ADDRSZ);
#endif
}
return 1;
}
/* If in lazy mode, we cache absence of ARP entries. */
int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now)
{
struct arp_record *arp, *tmp, **up;
int updated = 0;
again:
/* If the database is less then INTERVAL old, look in there */
if (difftime(now, last) < INTERVAL)
for (arp = arps; arp; arp = arp->next)
{
if (addr->sa.sa_family == arp->family)
{
if (arp->addr.addr.addr4.s_addr != addr->in.sin_addr.s_addr)
continue;
}
#ifdef HAVE_IPV6
else
{
if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, &addr->in6.sin6_addr))
continue;
}
#endif
/* Only accept poitive entries unless in lazy mode. */
if (arp->status != ARP_EMPTY || lazy || updated)
{
if (mac && arp->hwlen != 0)
memcpy(mac, arp->hwaddr, arp->hwlen);
return arp->hwlen;
}
}
/* Not found, try the kernel */
if (!updated)
{
updated = 1;
last = now;
/* Mark all non-negative entries */
for (arp = arps, up = &arps; arp; arp = arp->next)
if (arp->status != ARP_EMPTY)
arp->status = ARP_MARK;
iface_enumerate(AF_UNSPEC, NULL, filter_mac);
/* Remove all unconfirmed entries to old list. */
for (arp = arps, up = &arps; arp; arp = tmp)
{
tmp = arp->next;
if (arp->status == ARP_MARK)
{
*up = arp->next;
arp->next = old;
old = arp;
}
else
up = &arp->next;
}
goto again;
}
/* record failure, so we don't consult the kernel each time
we're asked for this address */
if (freelist)
{
arp = freelist;
freelist = freelist->next;
}
else
arp = whine_malloc(sizeof(struct arp_record));
if (arp)
{
arp->next = arps;
arps = arp;
arp->status = ARP_EMPTY;
arp->family = addr->sa.sa_family;
if (addr->sa.sa_family == AF_INET)
arp->addr.addr.addr4.s_addr = addr->in.sin_addr.s_addr;
#ifdef HAVE_IPV6
else
memcpy(&arp->addr.addr.addr6, &addr->in6.sin6_addr, IN6ADDRSZ);
#endif
}
return 0;
}
int do_arp_script_run(void)
{
struct arp_record *arp;
/* Notify any which went, then move to free list */
if (old)
{
#ifdef HAVE_SCRIPT
if (option_bool(OPT_DNS_CLIENT))
queue_arp(ACTION_ARP_OLD, old->hwaddr, old->hwlen, old->family, &old->addr);
#endif
arp = old;
old = arp->next;
arp->next = freelist;
freelist = arp;
return 1;
}
for (arp = arps; arp; arp = arp->next)
if (arp->status == ARP_NEW)
{
#ifdef HAVE_SCRIPT
if (option_bool(OPT_DNS_CLIENT))
queue_arp(ACTION_ARP, arp->hwaddr, arp->hwlen, arp->family, &arp->addr);
#endif
arp->status = ARP_FOUND;
return 1;
}
return 0;
}
+7 -1
View File
@@ -81,7 +81,8 @@ int in_zone(struct auth_zone *zone, char *name, char **cut)
}
size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, int local_query)
size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr,
int local_query, int do_bit, int have_pseudoheader)
{
char *name = daemon->namebuff;
unsigned char *p, *ansp;
@@ -820,6 +821,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
header->ancount = htons(anscount);
header->nscount = htons(authcount);
header->arcount = htons(0);
/* Advertise our packet size limit in our reply */
if (have_pseudoheader)
return add_pseudoheader(header, ansp - (unsigned char *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit);
return ansp - (unsigned char *)header;
}
+8 -34
View File
@@ -189,12 +189,7 @@ static void cache_hash(struct crec *crecp)
static void cache_blockdata_free(struct crec *crecp)
{
if (crecp->flags & F_DNSKEY)
{
if (crecp->flags & F_DS)
blockdata_free(crecp->addr.sig.keydata);
else
blockdata_free(crecp->addr.key.keydata);
}
blockdata_free(crecp->addr.key.keydata);
else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG))
blockdata_free(crecp->addr.ds.keydata);
}
@@ -369,13 +364,8 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
}
#ifdef HAVE_DNSSEC
/* Deletion has to be class-sensitive for DS, DNSKEY, RRSIG, also
type-covered sensitive for RRSIG */
if ((flags & (F_DNSKEY | F_DS)) &&
(flags & (F_DNSKEY | F_DS)) == (crecp->flags & (F_DNSKEY | F_DS)) &&
crecp->uid == addr->addr.dnssec.class &&
(!((flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY)) ||
crecp->addr.sig.type_covered == addr->addr.dnssec.type))
/* Deletion has to be class-sensitive for DS and DNSKEY */
if ((flags & crecp->flags & (F_DNSKEY | F_DS)) && crecp->uid == addr->addr.dnssec.class)
{
if (crecp->flags & F_CONFIG)
return crecp;
@@ -481,7 +471,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
existing record is for an A or AAAA and
the record we're trying to insert is the same,
just drop the insert, but don't error the whole process. */
if ((flags & (F_IPV4 | F_IPV6)) && (flags & F_FORWARD))
if ((flags & (F_IPV4 | F_IPV6)) && (flags & F_FORWARD) && addr)
{
if ((flags & F_IPV4) && (new->flags & F_IPV4) &&
new->addr.addr.addr.addr4.s_addr == addr->addr.addr4.s_addr)
@@ -532,13 +522,9 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
struct all_addr free_addr = new->addr.addr;;
#ifdef HAVE_DNSSEC
/* For DNSSEC records, addr holds class and type_covered for RRSIG */
/* For DNSSEC records, addr holds class. */
if (new->flags & (F_DS | F_DNSKEY))
{
free_addr.addr.dnssec.class = new->uid;
if ((new->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
free_addr.addr.dnssec.type = new->addr.sig.type_covered;
}
free_addr.addr.dnssec.class = new->uid;
#endif
free_avail = 1; /* Must be free space now. */
@@ -653,9 +639,6 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
{
if ((crecp->flags & F_FORWARD) &&
#ifdef HAVE_DNSSEC
(((crecp->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) || (prot & F_NSIGMATCH)) &&
#endif
(crecp->flags & prot) &&
hostname_isequal(cache_get_name(crecp), name))
{
@@ -713,9 +696,6 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
if (ans &&
(ans->flags & F_FORWARD) &&
#ifdef HAVE_DNSSEC
(((ans->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) || (prot & F_NSIGMATCH)) &&
#endif
(ans->flags & prot) &&
hostname_isequal(cache_get_name(ans), name))
return ans;
@@ -1472,11 +1452,7 @@ void dump_cache(time_t now)
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DS)
{
if (cache->flags & F_DNSKEY)
/* RRSIG */
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
else if (!(cache->flags & F_NEG))
if (!(cache->flags & F_NEG))
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
}
@@ -1502,8 +1478,6 @@ void dump_cache(time_t now)
else if (cache->flags & F_CNAME)
t = "C";
#ifdef HAVE_DNSSEC
else if ((cache->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
t = "G"; /* DNSKEY and DS set -> RRISG */
else if (cache->flags & F_DS)
t = "S";
else if (cache->flags & F_DNSKEY)
@@ -1606,7 +1580,7 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
if (addr)
{
if (flags & F_KEYTAG)
sprintf(daemon->addrbuff, arg, addr->addr.keytag);
sprintf(daemon->addrbuff, arg, addr->addr.log.keytag, addr->addr.log.algo, addr->addr.log.digest);
else
{
#ifdef HAVE_IPV6
+1 -1
View File
@@ -337,7 +337,7 @@ HAVE_SOCKADDR_SA_LEN
#define HAVE_DHCP
#endif
#if defined(NO_SCRIPT) || !defined(HAVE_DHCP) || defined(NO_FORK)
#if defined(NO_SCRIPT) || defined(NO_FORK)
#undef HAVE_SCRIPT
#undef HAVE_LUASCRIPT
#endif
+201 -13
View File
@@ -70,6 +70,21 @@ const char* introspection_xml_template =
" <arg name=\"hwaddr\" type=\"s\"/>\n"
" <arg name=\"hostname\" type=\"s\"/>\n"
" </signal>\n"
#ifdef HAVE_DHCP
" <method name=\"AddDhcpLease\">\n"
" <arg name=\"ipaddr\" type=\"s\"/>\n"
" <arg name=\"hwaddr\" type=\"s\"/>\n"
" <arg name=\"hostname\" type=\"ay\"/>\n"
" <arg name=\"clid\" type=\"ay\"/>\n"
" <arg name=\"lease_duration\" type=\"u\"/>\n"
" <arg name=\"ia_id\" type=\"u\"/>\n"
" <arg name=\"is_temporary\" type=\"b\"/>\n"
" </method>\n"
" <method name=\"DeleteDhcpLease\">\n"
" <arg name=\"ipaddr\" type=\"s\"/>\n"
" <arg name=\"success\" type=\"b\" direction=\"out\"/>\n"
" </method>\n"
#endif
" </interface>\n"
"</node>\n";
@@ -421,18 +436,184 @@ static DBusMessage *dbus_set_bool(DBusMessage *message, int flag, char *name)
if (enabled)
{
my_syslog(LOG_INFO, "Enabling --%s option from D-Bus", name);
my_syslog(LOG_INFO, _("Enabling --%s option from D-Bus"), name);
set_option_bool(flag);
}
else
{
my_syslog(LOG_INFO, "Disabling --%s option from D-Bus", name);
my_syslog(LOG_INFO, _("Disabling --%s option from D-Bus"), name);
reset_option_bool(flag);
}
return NULL;
}
#ifdef HAVE_DHCP
static DBusMessage *dbus_add_lease(DBusMessage* message)
{
struct dhcp_lease *lease;
const char *ipaddr, *hwaddr, *hostname, *tmp;
const unsigned char* clid;
int clid_len, hostname_len, hw_len, hw_type;
dbus_uint32_t expires, ia_id;
dbus_bool_t is_temporary;
struct all_addr addr;
time_t now = dnsmasq_time();
unsigned char dhcp_chaddr[DHCP_CHADDR_MAX];
DBusMessageIter iter, array_iter;
if (!dbus_message_iter_init(message, &iter))
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Failed to initialize dbus message iter");
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected string as first argument");
dbus_message_iter_get_basic(&iter, &ipaddr);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected string as second argument");
dbus_message_iter_get_basic(&iter, &hwaddr);
dbus_message_iter_next(&iter);
if ((dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) ||
(dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE))
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected byte array as third argument");
dbus_message_iter_recurse(&iter, &array_iter);
dbus_message_iter_get_fixed_array(&array_iter, &hostname, &hostname_len);
tmp = memchr(hostname, '\0', hostname_len);
if (tmp)
{
if (tmp == &hostname[hostname_len - 1])
hostname_len--;
else
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Hostname contains an embedded NUL character");
}
dbus_message_iter_next(&iter);
if ((dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) ||
(dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE))
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected byte array as fourth argument");
dbus_message_iter_recurse(&iter, &array_iter);
dbus_message_iter_get_fixed_array(&array_iter, &clid, &clid_len);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected uint32 as fifth argument");
dbus_message_iter_get_basic(&iter, &expires);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected uint32 as sixth argument");
dbus_message_iter_get_basic(&iter, &ia_id);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected uint32 as sixth argument");
dbus_message_iter_get_basic(&iter, &is_temporary);
if (inet_pton(AF_INET, ipaddr, &addr.addr.addr4))
{
if (ia_id != 0 || is_temporary)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"ia_id and is_temporary must be zero for IPv4 lease");
if (!(lease = lease_find_by_addr(addr.addr.addr4)))
lease = lease4_allocate(addr.addr.addr4);
}
#ifdef HAVE_DHCP6
else if (inet_pton(AF_INET6, ipaddr, &addr.addr.addr6))
{
if (!(lease = lease6_find_by_addr(&addr.addr.addr6, 128, 0)))
lease = lease6_allocate(&addr.addr.addr6,
is_temporary ? LEASE_TA : LEASE_NA);
lease_set_iaid(lease, ia_id);
}
#endif
else
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s'", ipaddr);
hw_len = parse_hex((char*)hwaddr, dhcp_chaddr, DHCP_CHADDR_MAX, NULL,
&hw_type);
if (hw_type == 0 && hw_len != 0)
hw_type = ARPHRD_ETHER;
lease_set_hwaddr(lease, dhcp_chaddr, clid, hw_len, hw_type,
clid_len, now, 0);
lease_set_expires(lease, expires, now);
if (hostname_len != 0)
lease_set_hostname(lease, hostname, 0, get_domain(lease->addr), NULL);
lease_update_file(now);
lease_update_dns(0);
return NULL;
}
static DBusMessage *dbus_del_lease(DBusMessage* message)
{
struct dhcp_lease *lease;
DBusMessageIter iter;
const char *ipaddr;
DBusMessage *reply;
struct all_addr addr;
dbus_bool_t ret = 1;
time_t now = dnsmasq_time();
if (!dbus_message_iter_init(message, &iter))
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Failed to initialize dbus message iter");
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected string as first argument");
dbus_message_iter_get_basic(&iter, &ipaddr);
if (inet_pton(AF_INET, ipaddr, &addr.addr.addr4))
lease = lease_find_by_addr(addr.addr.addr4);
#ifdef HAVE_DHCP6
else if (inet_pton(AF_INET6, ipaddr, &addr.addr.addr6))
lease = lease6_find_by_addr(&addr.addr.addr6, 128, 0);
#endif
else
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s'", ipaddr);
if (lease)
{
lease_prune(lease, now);
lease_update_file(now);
lease_update_dns(0);
}
else
ret = 0;
if ((reply = dbus_message_new_method_return(message)))
dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &ret,
DBUS_TYPE_INVALID);
return reply;
}
#endif
DBusHandlerResult message_handler(DBusConnection *connection,
DBusMessage *message,
void *user_data)
@@ -490,6 +671,16 @@ DBusHandlerResult message_handler(DBusConnection *connection,
{
reply = dbus_set_bool(message, OPT_BOGUSPRIV, "bogus-priv");
}
#ifdef HAVE_DHCP
else if (strcmp(method, "AddDhcpLease") == 0)
{
reply = dbus_add_lease(message);
}
else if (strcmp(method, "DeleteDhcpLease") == 0)
{
reply = dbus_del_lease(message);
}
#endif
else if (strcmp(method, "ClearCache") == 0)
clear_cache = 1;
else
@@ -558,8 +749,7 @@ char *dbus_init(void)
}
void set_dbus_listeners(int *maxfdp,
fd_set *rset, fd_set *wset, fd_set *eset)
void set_dbus_listeners(void)
{
struct watch *w;
@@ -569,19 +759,17 @@ void set_dbus_listeners(int *maxfdp,
unsigned int flags = dbus_watch_get_flags(w->watch);
int fd = dbus_watch_get_unix_fd(w->watch);
bump_maxfd(fd, maxfdp);
if (flags & DBUS_WATCH_READABLE)
FD_SET(fd, rset);
poll_listen(fd, POLLIN);
if (flags & DBUS_WATCH_WRITABLE)
FD_SET(fd, wset);
poll_listen(fd, POLLOUT);
FD_SET(fd, eset);
poll_listen(fd, POLLERR);
}
}
void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset)
void check_dbus_listeners()
{
DBusConnection *connection = (DBusConnection *)daemon->dbus;
struct watch *w;
@@ -592,13 +780,13 @@ void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset)
unsigned int flags = 0;
int fd = dbus_watch_get_unix_fd(w->watch);
if (FD_ISSET(fd, rset))
if (poll_check(fd, POLLIN))
flags |= DBUS_WATCH_READABLE;
if (FD_ISSET(fd, wset))
if (poll_check(fd, POLLOUT))
flags |= DBUS_WATCH_WRITABLE;
if (FD_ISSET(fd, eset))
if (poll_check(fd, POLLERR))
flags |= DBUS_WATCH_ERROR;
if (flags != 0)
+1 -1
View File
@@ -599,7 +599,7 @@ static const struct opttab_t opttab6[] = {
{ "sntp-server", 31, OT_ADDR_LIST },
{ "information-refresh-time", 32, OT_TIME },
{ "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
{ "ntp-server", 56, OT_ADDR_LIST },
{ "ntp-server", 56, 0 },
{ "bootfile-url", 59, OT_NAME },
{ "bootfile-param", 60, OT_CSTRING },
{ NULL, 0, 0 }
+14 -6
View File
@@ -225,10 +225,11 @@ void dhcp_packet(time_t now, int pxe_fd)
strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
#endif
/* One form of bridging on BSD has the property that packets
can be recieved on bridge interfaces which do not have an IP address.
We allow these to be treated as aliases of another interface which does have
an IP address with --dhcp-bridge=interface,alias,alias */
/* If the interface on which the DHCP request was received is an
alias of some other interface (as specified by the
--bridge-interface option), change ifr.ifr_name so that we look
for DHCP contexts associated with the aliased interface instead
of with the aliasing one. */
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
for (alias = bridge->alias; alias; alias = alias->next)
@@ -236,7 +237,9 @@ void dhcp_packet(time_t now, int pxe_fd)
{
if (!(iface_index = if_nametoindex(bridge->iface)))
{
my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr.ifr_name);
my_syslog(MS_DHCP | LOG_WARNING,
_("unknown interface %s in bridge-interface"),
bridge->iface);
return;
}
else
@@ -449,8 +452,13 @@ void dhcp_packet(time_t now, int pxe_fd)
#endif
while(retry_send(sendmsg(fd, &msg, 0)));
/* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
if (errno != 0)
my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
inet_ntoa(dest.sin_addr), strerror(errno));
}
/* check against secondary interface addresses */
static int check_listen_addrs(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam)
+40 -46
View File
@@ -27,17 +27,10 @@ struct iface_param {
int ind, addr_match;
};
struct mac_param {
struct in6_addr *target;
unsigned char *mac;
unsigned int maclen;
};
static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, int flags,
unsigned int preferred, unsigned int valid, void *vparam);
static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv);
static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
void dhcp6_init(void)
@@ -144,6 +137,8 @@ void dhcp6_packet(time_t now)
if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0)
{
struct dhcp_bridge *bridge, *alias;
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
return;
@@ -160,6 +155,30 @@ void dhcp6_packet(time_t now)
memset(&parm.fallback, 0, IN6ADDRSZ);
memset(&parm.ll_addr, 0, IN6ADDRSZ);
memset(&parm.ula_addr, 0, IN6ADDRSZ);
/* If the interface on which the DHCPv6 request was received is
an alias of some other interface (as specified by the
--bridge-interface option), change parm.ind so that we look
for DHCPv6 contexts associated with the aliased interface
instead of with the aliasing one. */
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
for (alias = bridge->alias; alias; alias = alias->next)
if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
{
parm.ind = if_nametoindex(bridge->iface);
if (!parm.ind)
{
my_syslog(MS_DHCP | LOG_WARNING,
_("unknown interface %s in bridge-interface"),
bridge->iface);
return;
}
break;
}
if (alias)
break;
}
for (context = daemon->dhcp6; context; context = context->next)
if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
@@ -201,14 +220,14 @@ void dhcp6_packet(time_t now)
inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id);
relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id, now);
return;
}
/* May have configured relay, but not DHCP server */
if (!daemon->doing_dhcp6)
return;
lease_prune(NULL, now); /* lose any expired leases */
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
@@ -231,16 +250,15 @@ void dhcp6_packet(time_t now)
}
}
void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep)
void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now)
{
/* Recieving a packet from a host does not populate the neighbour
cache, so we send a neighbour discovery request if we can't
find the sender. Repeat a few times in case of packet loss. */
struct neigh_packet neigh;
struct sockaddr_in6 addr;
struct mac_param mac_param;
int i;
union mysockaddr addr;
int i, maclen;
neigh.type = ND_NEIGHBOR_SOLICIT;
neigh.code = 0;
@@ -251,55 +269,31 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi
memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SOCKADDR_SA_LEN
addr.sin6_len = sizeof(struct sockaddr_in6);
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
#endif
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(IPPROTO_ICMPV6);
addr.sin6_addr = *client;
addr.sin6_scope_id = iface;
mac_param.target = client;
mac_param.maclen = 0;
mac_param.mac = mac;
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_port = htons(IPPROTO_ICMPV6);
addr.in6.sin6_addr = *client;
addr.in6.sin6_scope_id = iface;
for (i = 0; i < 5; i++)
{
struct timespec ts;
iface_enumerate(AF_UNSPEC, &mac_param, find_mac);
if (mac_param.maclen != 0)
if ((maclen = find_mac(&addr, mac, 0, now)) != 0)
break;
sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, (struct sockaddr *)&addr, sizeof(addr));
sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr));
ts.tv_sec = 0;
ts.tv_nsec = 100000000; /* 100ms */
nanosleep(&ts, NULL);
}
*maclenp = mac_param.maclen;
*maclenp = maclen;
*mactypep = ARPHRD_ETHER;
}
static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
{
struct mac_param *parm = parmv;
if (family == AF_INET6 && IN6_ARE_ADDR_EQUAL(parm->target, (struct in6_addr *)addrp))
{
if (maclen <= DHCP_CHADDR_MAX)
{
parm->maclen = maclen;
memcpy(parm->mac, mac, maclen);
}
return 0; /* found, abort */
}
return 1;
}
static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, int flags, unsigned int preferred,
unsigned int valid, void *vparam)
+9 -7
View File
@@ -77,6 +77,8 @@
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
#define EDNS0_OPTION_NOMDEVICEID 65073 /* Nominum temporary assignment */
#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */
struct dns_header {
u16 id;
@@ -84,15 +86,15 @@ struct dns_header {
u16 qdcount,ancount,nscount,arcount;
};
#define HB3_QR 0x80
#define HB3_QR 0x80 /* Query */
#define HB3_OPCODE 0x78
#define HB3_AA 0x04
#define HB3_TC 0x02
#define HB3_RD 0x01
#define HB3_AA 0x04 /* Authoritative Answer */
#define HB3_TC 0x02 /* TrunCated */
#define HB3_RD 0x01 /* Recursion Desired */
#define HB4_RA 0x80
#define HB4_AD 0x20
#define HB4_CD 0x10
#define HB4_RA 0x80 /* Recursion Available */
#define HB4_AD 0x20 /* Authenticated Data */
#define HB4_CD 0x10 /* Checking Disabled */
#define HB4_RCODE 0x0f
#define OPCODE(x) (((x)->hb3 & HB3_OPCODE) >> 3)
+109 -147
View File
@@ -24,8 +24,8 @@ struct daemon *daemon;
static volatile pid_t pid = 0;
static volatile int pipewrite;
static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp);
static void check_dns_listeners(fd_set *set, time_t now);
static int set_dns_listeners(time_t now);
static void check_dns_listeners(time_t now);
static void sig_handler(int sig);
static void async_event(int pipe, time_t now);
static void fatal_event(struct event_desc *ev, char *msg);
@@ -91,8 +91,11 @@ int main (int argc, char **argv)
if (daemon->edns_pktsz < PACKETSZ)
daemon->edns_pktsz = PACKETSZ;
daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ?
daemon->edns_pktsz : DNSMASQ_PACKETSZ;
/* Min buffer size: we check after adding each record, so there must be
memory for the largest packet, and the largest record so the
min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000.
This might be increased is EDNS packet size if greater than the minimum. */
daemon->packet_buff_sz = daemon->edns_pktsz + MAXDNAME + RRFIXEDSZ;
daemon->packet = safe_malloc(daemon->packet_buff_sz);
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
@@ -242,8 +245,11 @@ int main (int argc, char **argv)
/* Note that order matters here, we must call lease_init before
creating any file descriptors which shouldn't be leaked
to the lease-script init process. We need to call common_init
before lease_init to allocate buffers it uses.*/
if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || daemon->relay6)
before lease_init to allocate buffers it uses.
The script subsystrm relies on DHCP buffers, hence the last two
conditions below. */
if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 ||
daemon->relay6 || option_bool(OPT_TFTP) || option_bool(OPT_DNS_CLIENT))
{
dhcp_common_init();
if (daemon->dhcp || daemon->doing_dhcp6)
@@ -550,8 +556,9 @@ int main (int argc, char **argv)
/* if we are to run scripts, we need to fork a helper before dropping root. */
daemon->helperfd = -1;
#ifdef HAVE_SCRIPT
if ((daemon->dhcp || daemon->dhcp6) && (daemon->lease_change_command || daemon->luascript))
daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_DNS_CLIENT)) &&
(daemon->lease_change_command || daemon->luascript))
daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
#endif
if (!option_bool(OPT_DEBUG) && getuid() == 0)
@@ -799,10 +806,6 @@ int main (int argc, char **argv)
if (option_bool(OPT_TFTP))
{
struct tftp_prefix *p;
#ifdef FD_SETSIZE
if (FD_SETSIZE < (unsigned)max_fd)
max_fd = FD_SETSIZE;
#endif
my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s",
daemon->tftp_prefix ? _("root is ") : _("enabled"),
@@ -862,126 +865,91 @@ int main (int argc, char **argv)
while (1)
{
int maxfd = -1;
struct timeval t, *tp = NULL;
fd_set rset, wset, eset;
int t, timeout = -1;
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&eset);
poll_reset();
/* if we are out of resources, find how long we have to wait
for some to come free, we'll loop around then and restart
listening for queries */
if ((t.tv_sec = set_dns_listeners(now, &rset, &maxfd)) != 0)
{
t.tv_usec = 0;
tp = &t;
}
if ((t = set_dns_listeners(now)) != 0)
timeout = t * 1000;
/* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
if (daemon->tftp_trans ||
(option_bool(OPT_DBUS) && !daemon->dbus))
{
t.tv_sec = 0;
t.tv_usec = 250000;
tp = &t;
}
timeout = 250;
/* Wake every second whilst waiting for DAD to complete */
else if (is_dad_listeners())
{
t.tv_sec = 1;
t.tv_usec = 0;
tp = &t;
}
timeout = 1000;
#ifdef HAVE_DBUS
set_dbus_listeners(&maxfd, &rset, &wset, &eset);
set_dbus_listeners();
#endif
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->relay4)
{
FD_SET(daemon->dhcpfd, &rset);
bump_maxfd(daemon->dhcpfd, &maxfd);
poll_listen(daemon->dhcpfd, POLLIN);
if (daemon->pxefd != -1)
{
FD_SET(daemon->pxefd, &rset);
bump_maxfd(daemon->pxefd, &maxfd);
}
poll_listen(daemon->pxefd, POLLIN);
}
#endif
#ifdef HAVE_DHCP6
if (daemon->doing_dhcp6 || daemon->relay6)
{
FD_SET(daemon->dhcp6fd, &rset);
bump_maxfd(daemon->dhcp6fd, &maxfd);
}
poll_listen(daemon->dhcp6fd, POLLIN);
if (daemon->doing_ra)
{
FD_SET(daemon->icmp6fd, &rset);
bump_maxfd(daemon->icmp6fd, &maxfd);
}
poll_listen(daemon->icmp6fd, POLLIN);
#endif
#ifdef HAVE_INOTIFY
if (daemon->inotifyfd != -1)
{
FD_SET(daemon->inotifyfd, &rset);
bump_maxfd(daemon->inotifyfd, &maxfd);
}
poll_listen(daemon->inotifyfd, POLLIN);
#endif
#if defined(HAVE_LINUX_NETWORK)
FD_SET(daemon->netlinkfd, &rset);
bump_maxfd(daemon->netlinkfd, &maxfd);
poll_listen(daemon->netlinkfd, POLLIN);
#elif defined(HAVE_BSD_NETWORK)
FD_SET(daemon->routefd, &rset);
bump_maxfd(daemon->routefd, &maxfd);
poll_listen(daemon->routefd, POLLIN);
#endif
FD_SET(piperead, &rset);
bump_maxfd(piperead, &maxfd);
poll_listen(piperead, POLLIN);
#ifdef HAVE_DHCP
# ifdef HAVE_SCRIPT
while (helper_buf_empty() && do_script_run(now));
#ifdef HAVE_SCRIPT
while (helper_buf_empty() && do_script_run(now));
while (helper_buf_empty() && do_arp_script_run());
# ifdef HAVE_TFTP
while (helper_buf_empty() && do_tftp_script_run());
# endif
if (!helper_buf_empty())
{
FD_SET(daemon->helperfd, &wset);
bump_maxfd(daemon->helperfd, &maxfd);
}
# else
poll_listen(daemon->helperfd, POLLOUT);
#else
/* need this for other side-effects */
while (do_script_run(now));
while (do_arp_script_run(now));
# ifdef HAVE_TFTP
while (do_tftp_script_run());
# endif
# endif
#endif
/* must do this just before select(), when we know no
more calls to my_syslog() can occur */
set_log_writer(&wset, &maxfd);
set_log_writer();
if (do_poll(timeout) < 0)
continue;
if (select(maxfd+1, &rset, &wset, &eset, tp) < 0)
{
/* otherwise undefined after error */
FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset);
}
now = dnsmasq_time();
check_log_writer(&wset);
check_log_writer(0);
/* prime. */
enumerate_interfaces(1);
@@ -997,15 +965,15 @@ int main (int argc, char **argv)
}
#if defined(HAVE_LINUX_NETWORK)
if (FD_ISSET(daemon->netlinkfd, &rset))
if (poll_check(daemon->netlinkfd, POLLIN))
netlink_multicast();
#elif defined(HAVE_BSD_NETWORK)
if (FD_ISSET(daemon->routefd, &rset))
if (poll_check(daemon->routefd, POLLIN))
route_sock();
#endif
#ifdef HAVE_INOTIFY
if (daemon->inotifyfd != -1 && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check(now))
if (daemon->inotifyfd != -1 && poll_check(daemon->inotifyfd, POLLIN) && inotify_check(now))
{
if (daemon->port != 0 && !option_bool(OPT_NO_POLL))
poll_resolv(1, 1, now);
@@ -1025,7 +993,7 @@ int main (int argc, char **argv)
}
#endif
if (FD_ISSET(piperead, &rset))
if (poll_check(piperead, POLLIN))
async_event(piperead, now);
#ifdef HAVE_DBUS
@@ -1038,34 +1006,34 @@ int main (int argc, char **argv)
if (daemon->dbus)
my_syslog(LOG_INFO, _("connected to system DBus"));
}
check_dbus_listeners(&rset, &wset, &eset);
check_dbus_listeners();
#endif
check_dns_listeners(&rset, now);
check_dns_listeners(now);
#ifdef HAVE_TFTP
check_tftp_listeners(&rset, now);
check_tftp_listeners(now);
#endif
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->relay4)
{
if (FD_ISSET(daemon->dhcpfd, &rset))
if (poll_check(daemon->dhcpfd, POLLIN))
dhcp_packet(now, 0);
if (daemon->pxefd != -1 && FD_ISSET(daemon->pxefd, &rset))
if (daemon->pxefd != -1 && poll_check(daemon->pxefd, POLLIN))
dhcp_packet(now, 1);
}
#ifdef HAVE_DHCP6
if ((daemon->doing_dhcp6 || daemon->relay6) && FD_ISSET(daemon->dhcp6fd, &rset))
if ((daemon->doing_dhcp6 || daemon->relay6) && poll_check(daemon->dhcp6fd, POLLIN))
dhcp6_packet(now);
if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset))
if (daemon->doing_ra && poll_check(daemon->icmp6fd, POLLIN))
icmp6_packet(now);
#endif
# ifdef HAVE_SCRIPT
if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset))
if (daemon->helperfd != -1 && poll_check(daemon->helperfd, POLLOUT))
helper_write();
# endif
#endif
@@ -1361,6 +1329,15 @@ static void async_event(int pipe, time_t now)
if (daemon->lease_stream)
fclose(daemon->lease_stream);
#ifdef HAVE_DNSSEC
/* update timestamp file on TERM if time is considered valid */
if (daemon->back_to_the_future)
{
if (utime(daemon->timestamp_file, NULL) == -1)
my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));
}
#endif
if (daemon->runfile)
unlink(daemon->runfile);
@@ -1470,7 +1447,7 @@ void clear_cache_and_reload(time_t now)
#endif
}
static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
static int set_dns_listeners(time_t now)
{
struct serverfd *serverfdp;
struct listener *listener;
@@ -1482,8 +1459,7 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
{
tftp++;
FD_SET(transfer->sockfd, set);
bump_maxfd(transfer->sockfd, maxfdp);
poll_listen(transfer->sockfd, POLLIN);
}
#endif
@@ -1492,45 +1468,32 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
get_new_frec(now, &wait, 0);
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
{
FD_SET(serverfdp->fd, set);
bump_maxfd(serverfdp->fd, maxfdp);
}
poll_listen(serverfdp->fd, POLLIN);
if (daemon->port != 0 && !daemon->osport)
for (i = 0; i < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount != 0)
{
FD_SET(daemon->randomsocks[i].fd, set);
bump_maxfd(daemon->randomsocks[i].fd, maxfdp);
}
poll_listen(daemon->randomsocks[i].fd, POLLIN);
for (listener = daemon->listeners; listener; listener = listener->next)
{
/* only listen for queries if we have resources */
if (listener->fd != -1 && wait == 0)
{
FD_SET(listener->fd, set);
bump_maxfd(listener->fd, maxfdp);
}
poll_listen(listener->fd, POLLIN);
/* death of a child goes through the select loop, so
we don't need to explicitly arrange to wake up here */
if (listener->tcpfd != -1)
for (i = 0; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] == 0)
{
FD_SET(listener->tcpfd, set);
bump_maxfd(listener->tcpfd, maxfdp);
poll_listen(listener->tcpfd, POLLIN);
break;
}
#ifdef HAVE_TFTP
if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
{
FD_SET(listener->tftpfd, set);
bump_maxfd(listener->tftpfd, maxfdp);
}
poll_listen(listener->tftpfd, POLLIN);
#endif
}
@@ -1538,33 +1501,33 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
return wait;
}
static void check_dns_listeners(fd_set *set, time_t now)
static void check_dns_listeners(time_t now)
{
struct serverfd *serverfdp;
struct listener *listener;
int i;
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
if (FD_ISSET(serverfdp->fd, set))
if (poll_check(serverfdp->fd, POLLIN))
reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);
if (daemon->port != 0 && !daemon->osport)
for (i = 0; i < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount != 0 &&
FD_ISSET(daemon->randomsocks[i].fd, set))
poll_check(daemon->randomsocks[i].fd, POLLIN))
reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
for (listener = daemon->listeners; listener; listener = listener->next)
{
if (listener->fd != -1 && FD_ISSET(listener->fd, set))
if (listener->fd != -1 && poll_check(listener->fd, POLLIN))
receive_query(listener, now);
#ifdef HAVE_TFTP
if (listener->tftpfd != -1 && FD_ISSET(listener->tftpfd, set))
if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN))
tftp_request(listener, now);
#endif
if (listener->tcpfd != -1 && FD_ISSET(listener->tcpfd, set))
if (listener->tcpfd != -1 && poll_check(listener->tcpfd, POLLIN))
{
int confd, client_ok = 1;
struct irec *iface = NULL;
@@ -1755,14 +1718,22 @@ int icmp_ping(struct in_addr addr)
better not use any resources our caller has in use...)
but we remain deaf to signals or further DHCP packets. */
int fd;
/* There can be a problem using dnsmasq_time() to end the loop, since
it's not monotonic, and can go backwards if the system clock is
tweaked, leading to the code getting stuck in this loop and
ignoring DHCP requests. To fix this, we check to see if select returned
as a result of a timeout rather than a socket becoming available. We
only allow this to happen as many times as it takes to get to the wait time
in quarter-second chunks. This provides a fallback way to end loop. */
int fd, rc;
struct sockaddr_in saddr;
struct {
struct ip ip;
struct icmp icmp;
} packet;
unsigned short id = rand16();
unsigned int i, j;
unsigned int i, j, timeout_count;
int gotreply = 0;
time_t start, now;
@@ -1794,53 +1765,44 @@ int icmp_ping(struct in_addr addr)
while (retry_send(sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0,
(struct sockaddr *)&saddr, sizeof(saddr))));
for (now = start = dnsmasq_time();
difftime(now, start) < (float)PING_WAIT;)
for (now = start = dnsmasq_time(), timeout_count = 0;
(difftime(now, start) < (float)PING_WAIT) && (timeout_count < PING_WAIT * 4);)
{
struct timeval tv;
fd_set rset, wset;
struct sockaddr_in faddr;
int maxfd = fd;
socklen_t len = sizeof(faddr);
tv.tv_usec = 250000;
tv.tv_sec = 0;
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_SET(fd, &rset);
set_dns_listeners(now, &rset, &maxfd);
set_log_writer(&wset, &maxfd);
poll_reset();
poll_listen(fd, POLLIN);
set_dns_listeners(now);
set_log_writer();
#ifdef HAVE_DHCP6
if (daemon->doing_ra)
{
FD_SET(daemon->icmp6fd, &rset);
bump_maxfd(daemon->icmp6fd, &maxfd);
}
poll_listen(daemon->icmp6fd, POLLIN);
#endif
if (select(maxfd+1, &rset, &wset, NULL, &tv) < 0)
{
FD_ZERO(&rset);
FD_ZERO(&wset);
}
rc = do_poll(250);
if (rc < 0)
continue;
else if (rc == 0)
timeout_count++;
now = dnsmasq_time();
check_log_writer(&wset);
check_dns_listeners(&rset, now);
check_log_writer(0);
check_dns_listeners(now);
#ifdef HAVE_DHCP6
if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset))
if (daemon->doing_ra && poll_check(daemon->icmp6fd, POLLIN))
icmp6_packet(now);
#endif
#ifdef HAVE_TFTP
check_tftp_listeners(&rset, now);
check_tftp_listeners(now);
#endif
if (FD_ISSET(fd, &rset) &&
if (poll_check(fd, POLLIN) &&
recvfrom(fd, &packet, sizeof(packet), 0,
(struct sockaddr *)&faddr, &len) == sizeof(packet) &&
saddr.sin_addr.s_addr == faddr.sin_addr.s_addr &&
+71 -54
View File
@@ -82,7 +82,7 @@ typedef unsigned long long u64;
#if defined(HAVE_SOLARIS_NETWORK)
# include <sys/sockio.h>
#endif
#include <sys/select.h>
#include <sys/poll.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/un.h>
@@ -117,6 +117,7 @@ typedef unsigned long long u64;
#include <sys/uio.h>
#include <syslog.h>
#include <dirent.h>
#include <utime.h>
#ifndef HAVE_LINUX_NETWORK
# include <net/if_dl.h>
#endif
@@ -178,13 +179,6 @@ struct event_desc {
#define EC_MISC 5
#define EC_INIT_OFFSET 10
/* Min buffer size: we check after adding each record, so there must be
memory for the largest packet, and the largest record so the
min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000.
This might be increased is EDNS packet size if greater than the minimum.
*/
#define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ
/* Trust the compiler dead-code eliminator.... */
#define option_bool(x) (((x) < 32) ? daemon->options & (1u << (x)) : daemon->options2 & (1u << ((x) - 32)))
@@ -241,7 +235,8 @@ struct event_desc {
#define OPT_LOOP_DETECT 50
#define OPT_EXTRALOG 51
#define OPT_TFTP_NO_FAIL 52
#define OPT_LAST 53
#define OPT_DNS_CLIENT 53
#define OPT_LAST 54
/* 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. */
@@ -255,8 +250,10 @@ struct all_addr {
struct in6_addr addr6;
#endif
/* for log_query */
unsigned int keytag;
/* for cache_insert if RRSIG, DNSKEY, DS */
struct {
unsigned short keytag, algo, digest;
} log;
/* for cache_insert of DNSKEY, DS */
struct {
unsigned short class, type;
} dnssec;
@@ -397,14 +394,9 @@ struct crec {
unsigned char algo;
unsigned char digest;
} ds;
struct {
struct blockdata *keydata;
unsigned short keylen, type_covered, keytag;
char algo;
} sig;
} addr;
time_t ttd; /* time to die */
/* used as class if DNSKEY/DS/RRSIG, index to source for F_HOSTS */
/* used as class if DNSKEY/DS, index to source for F_HOSTS */
unsigned int uid;
unsigned short flags;
union {
@@ -444,8 +436,7 @@ struct crec {
#define F_SECSTAT (1u<<24)
#define F_NO_RR (1u<<25)
#define F_IPSET (1u<<26)
#define F_NSIGMATCH (1u<<27)
#define F_NOEXTRA (1u<<28)
#define F_NOEXTRA (1u<<27)
/* Values of uid in crecs with F_CONFIG bit set. */
#define SRC_INTERFACE 0
@@ -540,6 +531,13 @@ struct iname {
struct iname *next;
};
/* subnet parameters from command line */
struct mysubnet {
union mysockaddr addr;
int addr_used;
int mask;
};
/* resolv-file parms from command-line */
struct resolvc {
struct resolvc *next;
@@ -578,12 +576,8 @@ struct hostsfile {
#define STAT_NEED_KEY 5
#define STAT_TRUNCATED 6
#define STAT_SECURE_WILDCARD 7
#define STAT_NO_SIG 8
#define STAT_NO_DS 9
#define STAT_NO_NS 10
#define STAT_NEED_DS_NEG 11
#define STAT_CHASE_CNAME 12
#define STAT_INSECURE_DS 13
#define STAT_OK 8
#define STAT_ABANDONED 9
#define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2
@@ -593,8 +587,8 @@ struct hostsfile {
#define FREC_AD_QUESTION 32
#define FREC_DO_QUESTION 64
#define FREC_ADDED_PHEADER 128
#define FREC_CHECK_NOSIGN 256
#define FREC_TEST_PKTSZ 512
#define FREC_TEST_PKTSZ 256
#define FREC_HAS_EXTRADATA 512
#ifdef HAVE_DNSSEC
#define HASH_SIZE 20 /* SHA-1 digest size */
@@ -618,9 +612,7 @@ struct frec {
#ifdef HAVE_DNSSEC
int class, work_counter;
struct blockdata *stash; /* Saved reply, whilst we validate */
struct blockdata *orig_domain; /* domain of original query, whilst
we're seeing is if in unsigned domain */
size_t stash_len, name_start, name_len;
size_t stash_len;
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
struct frec *blocking_query; /* Query which is blocking us. */
#endif
@@ -642,6 +634,8 @@ struct frec {
#define ACTION_OLD 3
#define ACTION_ADD 4
#define ACTION_TFTP 5
#define ACTION_ARP 6
#define ACTION_ARP_OLD 7
#define LEASE_NEW 1 /* newly created */
#define LEASE_CHANGED 2 /* modified */
@@ -868,6 +862,7 @@ struct dhcp_context {
#define CONTEXT_USED (1u<<15)
#define CONTEXT_OLD (1u<<16)
#define CONTEXT_V6 (1u<<17)
#define CONTEXT_RA_OFF_LINK (1u<<18)
struct ping_result {
struct in_addr addr;
@@ -933,9 +928,9 @@ extern struct daemon {
struct auth_zone *auth_zones;
struct interface_name *int_names;
char *mxtarget;
int addr4_netmask;
int addr6_netmask;
char *lease_file;
struct mysubnet *add_subnet4;
struct mysubnet *add_subnet6;
char *lease_file;
char *username, *groupname, *scriptuser;
char *luascript;
char *authserver, *hostmaster;
@@ -956,6 +951,7 @@ extern struct daemon {
int cachesize, ftabsize;
int port, query_port, min_port;
unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl;
char *dns_client_id;
struct hostsfile *addn_hosts;
struct dhcp_context *dhcp, *dhcp6;
struct ra_interface *ra_interfaces;
@@ -991,6 +987,7 @@ extern struct daemon {
#endif
#ifdef HAVE_DNSSEC
struct ds_config *ds;
int back_to_the_future;
char *timestamp_file;
#endif
@@ -1116,22 +1113,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
int no_cache, int secure, int *doctored);
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);
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 *addr, time_t now);
int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr);
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
size_t *len, unsigned char **p, int *is_sign);
int check_for_local_domain(char *name, time_t now);
unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff);
size_t resize_packet(struct dns_header *header, size_t plen,
unsigned char *pheader, size_t hlen);
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3);
size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source);
#ifdef HAVE_DNSSEC
size_t add_do_bit(struct dns_header *header, size_t plen, char *limit);
#endif
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
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, ...);
@@ -1144,16 +1133,17 @@ int private_net(struct in_addr addr, int ban_localhost);
/* auth.c */
#ifdef HAVE_AUTH
size_t answer_auth(struct dns_header *header, char *limit, size_t qlen,
time_t now, union mysockaddr *peer_addr, int local_query);
time_t now, union mysockaddr *peer_addr, int local_query,
int do_bit, int have_pseudoheader);
int in_zone(struct auth_zone *zone, char *name, char **cut);
#endif
/* dnssec.c */
size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz);
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz);
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer, int *nons);
int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class,
int check_unsigned, int *neganswer, int *nons);
int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
size_t filter_rrsigs(struct dns_header *header, size_t plen);
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
@@ -1190,7 +1180,6 @@ int memcmp_masked(unsigned char *a, unsigned char *b, int len,
unsigned int mask);
int expand_buf(struct iovec *iov, size_t size);
char *print_mac(char *buff, unsigned char *mac, int len);
void bump_maxfd(int fd, int *max);
int read_write(int fd, unsigned char *packet, int size, int rw);
int wildcard_match(const char* wildcard, const char* match);
@@ -1201,8 +1190,8 @@ void die(char *message, char *arg1, int exit_code);
int log_start(struct passwd *ent_pw, int errfd);
int log_reopen(char *log_file);
void my_syslog(int priority, const char *format, ...);
void set_log_writer(fd_set *set, int *maxfdp);
void check_log_writer(fd_set *set);
void set_log_writer(void);
void check_log_writer(int force);
void flush_log(void);
/* option.c */
@@ -1365,8 +1354,8 @@ int iface_enumerate(int family, void *parm, int (callback)());
/* dbus.c */
#ifdef HAVE_DBUS
char *dbus_init(void);
void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset);
void set_dbus_listeners(int *maxfdp, fd_set *rset, fd_set *wset, fd_set *eset);
void check_dbus_listeners(void);
void set_dbus_listeners(void);
# ifdef HAVE_DHCP
void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
# endif
@@ -1387,13 +1376,15 @@ void queue_script(int action, struct dhcp_lease *lease,
#ifdef HAVE_TFTP
void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer);
#endif
void queue_arp(int action, unsigned char *mac, int maclen,
int family, struct all_addr *addr);
int helper_buf_empty(void);
#endif
/* tftp.c */
#ifdef HAVE_TFTP
void tftp_request(struct listener *listen, time_t now);
void check_tftp_listeners(fd_set *rset, time_t now);
void check_tftp_listeners(time_t now);
int do_tftp_script_run(void);
#endif
@@ -1423,7 +1414,7 @@ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct
void make_duid(time_t now);
void dhcp_construct_contexts(time_t now);
void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
unsigned int *maclenp, unsigned int *mactypep);
unsigned int *maclenp, unsigned int *mactypep, time_t now);
#endif
/* rfc3315.c */
@@ -1431,7 +1422,8 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
size_t sz, struct in6_addr *client_addr, time_t now);
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id);
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address,
u32 scope_id, time_t now);
unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface);
#endif
@@ -1510,3 +1502,28 @@ void inotify_dnsmasq_init();
int inotify_check(time_t now);
void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz);
#endif
/* poll.c */
void poll_reset(void);
int poll_check(int fd, short event);
void poll_listen(int fd, short event);
int do_poll(int timeout);
/* rrfilter.c */
size_t rrfilter(struct dns_header *header, size_t plen, int mode);
u16 *rrfilter_desc(int type);
int expand_workspace(unsigned char ***wkspc, int *szp, int new);
/* edns0.c */
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
size_t *len, unsigned char **p, int *is_sign, int *is_last);
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do);
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit);
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
union mysockaddr *source, time_t now, int *check_subnet);
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
/* arp.c */
int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now);
int do_arp_script_run(void);
+622 -844
View File
File diff suppressed because it is too large. Load diff
+394
View File
@@ -0,0 +1,394 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last)
{
/* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
also return length of pseudoheader in *len and pointer to the UDP size in *p
Finally, check to see if a packet is signed. If it is we cannot change a single bit before
forwarding. We look for TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
int i, arcount = ntohs(header->arcount);
unsigned char *ansp = (unsigned char *)(header+1);
unsigned short rdlen, type, class;
unsigned char *ret = NULL;
if (is_sign)
{
*is_sign = 0;
if (OPCODE(header) == QUERY)
{
for (i = ntohs(header->qdcount); i != 0; i--)
{
if (!(ansp = skip_name(ansp, header, plen, 4)))
return NULL;
GETSHORT(type, ansp);
GETSHORT(class, ansp);
if (class == C_IN && type == T_TKEY)
*is_sign = 1;
}
}
}
else
{
if (!(ansp = skip_questions(header, plen)))
return NULL;
}
if (arcount == 0)
return NULL;
if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
return NULL;
for (i = 0; i < arcount; i++)
{
unsigned char *save, *start = ansp;
if (!(ansp = skip_name(ansp, header, plen, 10)))
return NULL;
GETSHORT(type, ansp);
save = ansp;
GETSHORT(class, ansp);
ansp += 4; /* TTL */
GETSHORT(rdlen, ansp);
if (!ADD_RDLEN(header, ansp, plen, rdlen))
return NULL;
if (type == T_OPT)
{
if (len)
*len = ansp - start;
if (p)
*p = save;
if (is_last)
*is_last = (i == arcount-1);
ret = start;
}
else if (is_sign &&
i == arcount - 1 &&
class == C_ANY &&
type == T_TSIG)
*is_sign = 1;
}
return ret;
}
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do)
{
unsigned char *lenp, *datap, *p, *udp_len, *buff = NULL;
int rdlen = 0, is_sign, is_last;
unsigned short flags = set_do ? 0x8000 : 0, rcode = 0;
p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last);
if (is_sign)
return plen;
if (p)
{
/* Existing header */
int i;
unsigned short code, len;
p = udp_len;
GETSHORT(udp_sz, p);
GETSHORT(rcode, p);
GETSHORT(flags, p);
if (set_do)
{
p -=2;
flags |= 0x8000;
PUTSHORT(flags, p);
}
lenp = p;
GETSHORT(rdlen, p);
if (!CHECK_LEN(header, p, plen, rdlen))
return plen; /* bad packet */
datap = p;
/* no option to add */
if (optno == 0)
return plen;
/* check if option already there */
for (i = 0; i + 4 < rdlen; i += len + 4)
{
GETSHORT(code, p);
GETSHORT(len, p);
if (code == optno)
return plen;
p += len;
}
/* If we're going to extend the RR, it has to be the last RR in the packet */
if (!is_last)
{
/* First, take a copy of the options. */
if (rdlen != 0 && (buff = whine_malloc(rdlen)))
memcpy(buff, datap, rdlen);
/* now, delete OPT RR */
plen = rrfilter(header, plen, 0);
/* Now, force addition of a new one */
p = NULL;
}
}
if (!p)
{
/* We are (re)adding the pseudoheader */
if (!(p = skip_questions(header, plen)) ||
!(p = skip_section(p,
ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
header, plen)))
return plen;
*p++ = 0; /* empty name */
PUTSHORT(T_OPT, p);
PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
PUTSHORT(rcode, p); /* extended RCODE and version */
PUTSHORT(flags, p); /* DO flag */
lenp = p;
PUTSHORT(rdlen, p); /* RDLEN */
datap = p;
/* Copy back any options */
if (buff)
{
memcpy(p, buff, rdlen);
free(buff);
p += rdlen;
}
header->arcount = htons(ntohs(header->arcount) + 1);
}
if (((ssize_t)optlen) > (limit - (p + 4)))
return plen; /* Too big */
/* Add new option */
if (optno != 0)
{
PUTSHORT(optno, p);
PUTSHORT(optlen, p);
memcpy(p, opt, optlen);
p += optlen;
PUTSHORT(p - datap, lenp);
}
return p - (unsigned char *)header;
}
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit)
{
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1);
}
static unsigned char char64(unsigned char c)
{
return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c & 0x3f];
}
static void encoder(unsigned char *in, char *out)
{
out[0] = char64(in[0]>>2);
out[1] = char64((in[0]<<4) | (in[1]>>4));
out[2] = char64((in[1]<<2) | (in[2]>>6));
out[3] = char64(in[2]);
}
static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
{
int maclen;
unsigned char mac[DHCP_CHADDR_MAX];
char encode[8]; /* handle 6 byte MACs */
if ((maclen = find_mac(l3, mac, 1, now)) == 6)
{
encoder(mac, encode);
encoder(mac+3, encode+4);
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, 8, 0);
}
if (daemon->dns_client_id)
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID,
(unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0);
return plen;
}
static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
{
int maclen;
unsigned char mac[DHCP_CHADDR_MAX];
if ((maclen = find_mac(l3, mac, 1, now)) != 0)
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0);
return plen;
}
struct subnet_opt {
u16 family;
u8 source_netmask, scope_netmask;
#ifdef HAVE_IPV6
u8 addr[IN6ADDRSZ];
#else
u8 addr[INADDRSZ];
#endif
};
static void *get_addrp(union mysockaddr *addr, const short family)
{
#ifdef HAVE_IPV6
if (family == AF_INET6)
return &addr->in6.sin6_addr;
#endif
return &addr->in.sin_addr;
}
static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
{
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
int len;
void *addrp;
int sa_family = source->sa.sa_family;
#ifdef HAVE_IPV6
if (source->sa.sa_family == AF_INET6)
{
opt->source_netmask = daemon->add_subnet6->mask;
if (daemon->add_subnet6->addr_used)
{
sa_family = daemon->add_subnet6->addr.sa.sa_family;
addrp = get_addrp(&daemon->add_subnet6->addr, sa_family);
}
else
addrp = &source->in6.sin6_addr;
}
else
#endif
{
opt->source_netmask = daemon->add_subnet4->mask;
if (daemon->add_subnet4->addr_used)
{
sa_family = daemon->add_subnet4->addr.sa.sa_family;
addrp = get_addrp(&daemon->add_subnet4->addr, sa_family);
}
else
addrp = &source->in.sin_addr;
}
opt->scope_netmask = 0;
len = 0;
if (opt->source_netmask != 0)
{
#ifdef HAVE_IPV6
opt->family = htons(sa_family == AF_INET6 ? 2 : 1);
#else
opt->family = htons(1);
#endif
len = ((opt->source_netmask - 1) >> 3) + 1;
memcpy(opt->addr, addrp, len);
if (opt->source_netmask & 7)
opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));
}
return len + 4;
}
static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source)
{
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
int len;
struct subnet_opt opt;
len = calc_subnet_opt(&opt, source);
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
}
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
{
/* Section 9.2, Check that subnet option in reply matches. */
int len, calc_len;
struct subnet_opt opt;
unsigned char *p;
int code, i, rdlen;
calc_len = calc_subnet_opt(&opt, peer);
if (!(p = skip_name(pseudoheader, header, plen, 10)))
return 1;
p += 8; /* skip UDP length and RCODE */
GETSHORT(rdlen, p);
if (!CHECK_LEN(header, p, plen, rdlen))
return 1; /* bad packet */
/* check if option there */
for (i = 0; i + 4 < rdlen; i += len + 4)
{
GETSHORT(code, p);
GETSHORT(len, p);
if (code == EDNS0_OPTION_CLIENT_SUBNET)
{
/* make sure this doesn't mismatch. */
opt.scope_netmask = p[3];
if (len != calc_len || memcmp(p, &opt, len) != 0)
return 0;
}
p += len;
}
return 1;
}
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
union mysockaddr *source, time_t now, int *check_subnet)
{
*check_subnet = 0;
if (option_bool(OPT_ADD_MAC))
plen = add_mac(header, plen, limit, source, now);
if (option_bool(OPT_DNS_CLIENT))
plen = add_dns_client(header, plen, limit, source, now);
if (option_bool(OPT_CLIENT_SUBNET))
{
plen = add_source_addr(header, plen, limit, source);
*check_subnet = 1;
}
return plen;
}
+360 -608
View File
File diff suppressed because it is too large. Load diff
+58 -8
View File
@@ -219,7 +219,18 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
action_str = "tftp";
is6 = (data.flags != AF_INET);
}
else
else if (data.action == ACTION_ARP)
{
action_str = "arp";
is6 = (data.flags != AF_INET);
}
else if (data.action == ACTION_ARP_OLD)
{
action_str = "arp-old";
is6 = (data.flags != AF_INET);
data.action = ACTION_ARP;
}
else
continue;
@@ -321,6 +332,22 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
}
}
else if (data.action == ACTION_ARP)
{
lua_getglobal(lua, "arp");
if (lua_type(lua, -1) != LUA_TFUNCTION)
lua_pop(lua, 1); /* arp function optional */
else
{
lua_pushstring(lua, action_str); /* arg1 - action */
lua_newtable(lua); /* arg2 - data table */
lua_pushstring(lua, daemon->addrbuff);
lua_setfield(lua, -2, "client_address");
lua_pushstring(lua, daemon->dhcp_buff);
lua_setfield(lua, -2, "mac_address");
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
}
}
else
{
lua_getglobal(lua, "lease"); /* function to call */
@@ -478,7 +505,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
continue;
}
if (data.action != ACTION_TFTP)
if (data.action != ACTION_TFTP && data.action != ACTION_ARP)
{
#ifdef HAVE_DHCP6
my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
@@ -550,10 +577,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err);
if (data.action == ACTION_OLD_HOSTNAME)
hostname = NULL;
}
my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
}
/* we need to have the event_fd around if exec fails */
if ((i = fcntl(event_fd, F_GETFD)) != -1)
fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
@@ -563,8 +589,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
if (err == 0)
{
execl(daemon->lease_change_command,
p ? p+1 : daemon->lease_change_command,
action_str, is6 ? daemon->packet : daemon->dhcp_buff,
p ? p+1 : daemon->lease_change_command, action_str,
(is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff,
daemon->addrbuff, hostname, (char*)NULL);
err = errno;
}
@@ -760,6 +786,30 @@ void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
}
#endif
void queue_arp(int action, unsigned char *mac, int maclen, int family, struct all_addr *addr)
{
/* no script */
if (daemon->helperfd == -1)
return;
buff_alloc(sizeof(struct script_data));
memset(buf, 0, sizeof(struct script_data));
buf->action = action;
buf->hwaddr_len = maclen;
buf->hwaddr_type = ARPHRD_ETHER;
if ((buf->flags = family) == AF_INET)
buf->addr = addr->addr.addr4;
#ifdef HAVE_IPV6
else
buf->addr6 = addr->addr.addr6;
#endif
memcpy(buf->hwaddr, mac, maclen);
bytes_in_buf = sizeof(struct script_data);
}
int helper_buf_empty(void)
{
return bytes_in_buf == 0;
+69 -16
View File
@@ -18,6 +18,7 @@
#ifdef HAVE_INOTIFY
#include <sys/inotify.h>
#include <sys/param.h> /* For MAXSYMLINKS */
/* the strategy is to set a inotify on the directories containing
resolv files, for any files in the directory which are close-write
@@ -35,31 +36,82 @@
static char *inotify_buffer;
#define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1)
/* If path is a symbolic link, return the path it
points to, made absolute if relative.
If path doesn't exist or is not a symlink, return NULL.
Return value is malloc'ed */
static char *my_readlink(char *path)
{
ssize_t rc, size = 64;
char *buf;
while (1)
{
buf = safe_malloc(size);
rc = readlink(path, buf, (size_t)size);
if (rc == -1)
{
/* Not link or doesn't exist. */
if (errno == EINVAL || errno == ENOENT)
return NULL;
else
die(_("cannot access path %s: %s"), path, EC_MISC);
}
else if (rc < size-1)
{
char *d;
buf[rc] = 0;
if (buf[0] != '/' && ((d = strrchr(path, '/'))))
{
/* Add path to relative link */
char *new_buf = safe_malloc((d - path) + strlen(buf) + 2);
*(d+1) = 0;
strcpy(new_buf, path);
strcat(new_buf, buf);
free(buf);
buf = new_buf;
}
return buf;
}
/* Buffer too small, increase and retry */
size += 64;
free(buf);
}
}
void inotify_dnsmasq_init()
{
struct resolvc *res;
inotify_buffer = safe_malloc(INOTIFY_SZ);
daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (daemon->inotifyfd == -1)
die(_("failed to create inotify: %s"), NULL, EC_MISC);
if (option_bool(OPT_NO_RESOLV))
return;
for (res = daemon->resolv_files; res; res = res->next)
{
char *d = NULL, *path;
if (!(path = realpath(res->name, NULL)))
char *d, *new_path, *path = safe_malloc(strlen(res->name) + 1);
int links = MAXSYMLINKS;
strcpy(path, res->name);
/* Follow symlinks until we reach a non-symlink, or a non-existant file. */
while ((new_path = my_readlink(path)))
{
/* realpath will fail if the file doesn't exist, but
dnsmasq copes with missing files, so fall back
and assume that symlinks are not in use in that case. */
if (errno == ENOENT)
path = res->name;
else
die(_("cannot cannonicalise resolv-file %s: %s"), res->name, EC_MISC);
if (links-- == 0)
die(_("too many symlinks following %s"), res->name, EC_MISC);
free(path);
path = new_path;
}
res->wd = -1;
if ((d = strrchr(path, '/')))
{
*d = 0; /* make path just directory */
@@ -70,10 +122,11 @@ void inotify_dnsmasq_init()
if (res->wd == -1 && errno == ENOENT)
die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC);
if (res->wd == -1)
die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
}
}
if (res->wd == -1)
die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
}
}
+4 -7
View File
@@ -421,18 +421,15 @@ void my_syslog(int priority, const char *format, ...)
}
}
void set_log_writer(fd_set *set, int *maxfdp)
void set_log_writer(void)
{
if (entries && log_fd != -1 && connection_good)
{
FD_SET(log_fd, set);
bump_maxfd(log_fd, maxfdp);
}
poll_listen(log_fd, POLLOUT);
}
void check_log_writer(fd_set *set)
void check_log_writer(int force)
{
if (log_fd != -1 && (!set || FD_ISSET(log_fd, set)))
if (log_fd != -1 && (force || poll_check(log_fd, POLLOUT)))
log_write();
}
+2 -1
View File
@@ -288,7 +288,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
rta = RTA_NEXT(rta, len1);
}
if (inaddr && mac && callback_ok)
if (!(neigh->ndm_state & (NUD_NOARP | NUD_INCOMPLETE | NUD_FAILED)) &&
inaddr && mac && callback_ok)
if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm)))
callback_ok = 0;
}
+10 -3
View File
@@ -1076,23 +1076,30 @@ void join_multicast(int dienow)
if ((daemon->doing_dhcp6 || daemon->relay6) &&
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
err = 1;
err = errno;
inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
if (daemon->doing_dhcp6 &&
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
err = 1;
err = errno;
inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);
if (daemon->doing_ra &&
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
err = 1;
err = errno;
if (err)
{
char *s = _("interface %s failed to join DHCPv6 multicast group: %s");
errno = err;
#ifdef HAVE_LINUX_NETWORK
if (errno == ENOMEM)
my_syslog(LOG_ERR, _("try increasing /proc/sys/net/core/optmem_max"));
#endif
if (dienow)
die(s, iface->name, EC_BADNET);
else
+78 -15
View File
@@ -154,6 +154,7 @@ struct myoption {
#define LOPT_HOST_INOTIFY 342
#define LOPT_DNSSEC_STAMP 343
#define LOPT_TFTP_NO_FAIL 344
#define LOPT_DNS_CLIENT_ID 355
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -281,6 +282,7 @@ static const struct myoption opts[] =
{ "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
{ "add-mac", 0, 0, LOPT_ADD_MAC },
{ "add-subnet", 2, 0, LOPT_ADD_SBNET },
{ "add-dns-client", 2, 0 , LOPT_DNS_CLIENT_ID },
{ "proxy-dnssec", 0, 0, LOPT_DNSSEC },
{ "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
{ "conntrack", 0, 0, LOPT_CONNTRACK },
@@ -445,7 +447,8 @@ static struct {
{ LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
{ LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
{ LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
{ LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add requestor's IP subnet to forwarded DNS queries."), NULL },
{ LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL },
{ LOPT_DNS_CLIENT_ID, ARG_ONE, "<proxyname>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
{ LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
{ LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
{ LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
@@ -722,6 +725,20 @@ static void do_usage(void)
#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
{
if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
addr->sa.sa_family = AF_INET;
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
addr->sa.sa_family = AF_INET6;
#endif
else
return _("bad address");
return NULL;
}
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
{
int source_port = 0, serv_port = NAMESERVER_PORT;
@@ -1501,10 +1518,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
li = opt_malloc(sizeof(struct list));
if (*arg == '*')
{
li->next = match_suffix;
match_suffix = li;
/* Have to copy: buffer is overwritten */
li->suffix = opt_string_alloc(arg+1);
/* "*" with no suffix is a no-op */
if (arg[1] == 0)
free(li);
else
{
li->next = match_suffix;
match_suffix = li;
/* Have to copy: buffer is overwritten */
li->suffix = opt_string_alloc(arg+1);
}
}
else
{
@@ -1585,7 +1608,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
li = match_suffix->next;
free(match_suffix->suffix);
free(match_suffix);
}
}
break;
}
@@ -1593,10 +1616,45 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
set_option_bool(OPT_CLIENT_SUBNET);
if (arg)
{
char *err, *end;
comma = split(arg);
if (!atoi_check(arg, &daemon->addr4_netmask) ||
(comma && !atoi_check(comma, &daemon->addr6_netmask)))
ret_err(gen_err);
struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
if ((end = split_chr(arg, '/')))
{
/* has subnet+len */
err = parse_mysockaddr(arg, &new->addr);
if (err)
ret_err(err);
if (!atoi_check(end, &new->mask))
ret_err(gen_err);
new->addr_used = 1;
}
else if (!atoi_check(arg, &new->mask))
ret_err(gen_err);
daemon->add_subnet4 = new;
new = opt_malloc(sizeof(struct mysubnet));
if (comma)
{
if ((end = split_chr(comma, '/')))
{
/* has subnet+len */
err = parse_mysockaddr(comma, &new->addr);
if (err)
ret_err(err);
if (!atoi_check(end, &new->mask))
ret_err(gen_err);
new->addr_used = 1;
}
else
{
if (!atoi_check(comma, &new->mask))
ret_err(gen_err);
}
}
daemon->add_subnet6 = new;
}
break;
@@ -2095,6 +2153,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
break;
case LOPT_DNS_CLIENT_ID: /* --add-dns-client */
set_option_bool(OPT_DNS_CLIENT);
if (arg)
daemon->dns_client_id = opt_string_alloc(arg);
break;
case 'u': /* --user */
daemon->username = opt_string_alloc(arg);
break;
@@ -2699,6 +2763,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
else if (strcmp(a[leasepos], "ra-stateless") == 0)
new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
else if (strcmp(a[leasepos], "off-link") == 0)
new->flags |= CONTEXT_RA_OFF_LINK;
else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
new->flags |= CONTEXT_DHCP;
else if (strstr(a[leasepos], "constructor:") == a[leasepos])
@@ -4369,7 +4435,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
{
char *buff = opt_malloc(MAXDNAME);
int option, conffile_opt = '7', testmode = 0;
char *arg, *conffile = NULL;
char *arg, *conffile = CONFFILE;
opterr = 0;
@@ -4486,11 +4552,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
if (conffile)
{
one_file(conffile, conffile_opt);
free(conffile);
}
else
{
one_file(CONFFILE, conffile_opt);
if (conffile_opt == 0)
free(conffile);
}
/* port might not be known when the address is parsed - fill in here */
+125
View File
@@ -0,0 +1,125 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
/* Wrapper for poll(). Allocates and extends array of struct pollfds,
keeps them in fd order so that we can set and test conditions on
fd using a simple but efficient binary chop. */
/* poll_reset()
poll_listen(fd, event)
.
.
poll_listen(fd, event);
hits = do_poll(timeout);
if (poll_check(fd, event)
.
.
if (poll_check(fd, event)
.
.
event is OR of POLLIN, POLLOUT, POLLERR, etc
*/
static struct pollfd *pollfds = NULL;
static nfds_t nfds, arrsize = 0;
/* Binary search. Returns either the pollfd with fd, or
if the fd doesn't match, or return equals nfds, the entry
to the left of which a new record should be inserted. */
static nfds_t fd_search(int fd)
{
nfds_t left, right, mid;
if ((right = nfds) == 0)
return 0;
left = 0;
while (1)
{
if (right == left + 1)
return (pollfds[left].fd >= fd) ? left : right;
mid = (left + right)/2;
if (pollfds[mid].fd > fd)
right = mid;
else
left = mid;
}
}
void poll_reset(void)
{
nfds = 0;
}
int do_poll(int timeout)
{
return poll(pollfds, nfds, timeout);
}
int poll_check(int fd, short event)
{
nfds_t i = fd_search(fd);
if (i < nfds && pollfds[i].fd == fd)
return pollfds[i].revents & event;
return 0;
}
void poll_listen(int fd, short event)
{
nfds_t i = fd_search(fd);
if (i < nfds && pollfds[i].fd == fd)
pollfds[i].events |= event;
else
{
if (arrsize != nfds)
memmove(&pollfds[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd));
else
{
/* Array too small, extend. */
struct pollfd *new;
arrsize = (arrsize == 0) ? 64 : arrsize * 2;
if (!(new = whine_malloc(arrsize * sizeof(struct pollfd))))
return;
if (pollfds)
{
memcpy(new, pollfds, i * sizeof(struct pollfd));
memcpy(&new[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd));
free(pollfds);
}
pollfds = new;
}
pollfds[i].fd = fd;
pollfds[i].events = event;
nfds++;
}
}
+133 -11
View File
@@ -40,7 +40,18 @@ struct search_param {
char name[IF_NAMESIZE+1];
};
struct alias_param {
int iface;
struct dhcp_bridge *bridge;
int num_alias_ifs;
int max_alias_ifs;
int *alias_ifs;
};
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest);
static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_addr *dest,
int send_iface);
static int send_ra_to_aliases(int index, unsigned int type, char *mac, size_t maclen, void *parm);
static int add_prefixes(struct in6_addr *local, int prefix,
int scope, int if_index, int flags,
unsigned int preferred, unsigned int valid, void *vparam);
@@ -181,6 +192,7 @@ void icmp6_packet(time_t now)
else if (packet[0] == ND_ROUTER_SOLICIT)
{
char *mac = "";
struct dhcp_bridge *bridge, *alias;
/* look for link-layer address option for logging */
if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
@@ -191,12 +203,37 @@ void icmp6_packet(time_t now)
if (!option_bool(OPT_QUIET_RA))
my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
/* source address may not be valid in solicit request. */
send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL);
/* If the incoming interface is an alias of some other one (as
specified by the --bridge-interface option), send an RA using
the context of the aliased interface. */
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
int bridge_index = if_nametoindex(bridge->iface);
if (bridge_index)
{
for (alias = bridge->alias; alias; alias = alias->next)
if (wildcard_matchn(alias->iface, interface, IF_NAMESIZE))
{
/* Send an RA on if_index with information from
bridge_index. */
send_ra_alias(now, bridge_index, bridge->iface, NULL, if_index);
break;
}
if (alias)
break;
}
}
/* If the incoming interface wasn't an alias, send an RA using
the context of the incoming interface. */
if (!bridge)
/* source address may not be valid in solicit request. */
send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL);
}
}
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest)
static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_addr *dest, int send_iface)
{
struct ra_packet *ra;
struct ra_param parm;
@@ -313,8 +350,10 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
opt->type = ICMP6_OPT_PREFIX;
opt->len = 4;
opt->prefix_len = context->prefix;
/* autonomous only if we're not doing dhcp, always set "on-link" */
opt->flags = do_slaac ? 0xC0 : 0x80;
/* autonomous only if we're not doing dhcp, set
"on-link" unless "off-link" was specified */
opt->flags = (do_slaac ? 0x40 : 0) |
((context->flags & CONTEXT_RA_OFF_LINK) ? 0 : 0x80);
opt->valid_lifetime = htonl(context->saved_valid - old);
opt->preferred_lifetime = htonl(0);
opt->reserved = 0;
@@ -368,7 +407,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
}
#endif
iface_enumerate(AF_LOCAL, &iface, add_lla);
iface_enumerate(AF_LOCAL, &send_iface, add_lla);
/* RDNSS, RFC 6106, use relevant DHCP6 options */
(void)option_filter(parm.tags, NULL, daemon->dhcp_opts6);
@@ -476,7 +515,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
else
{
inet_pton(AF_INET6, ALL_NODES, &addr.sin6_addr);
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &iface, sizeof(iface));
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &send_iface, sizeof(send_iface));
}
while (retry_send(sendto(daemon->icmp6fd, daemon->outpacket.iov_base,
@@ -485,6 +524,13 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
}
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest)
{
/* Send an RA on the same interface that the RA content is based
on. */
send_ra_alias(now, iface, iface_name, dest, iface);
}
static int add_prefixes(struct in6_addr *local, int prefix,
int scope, int if_index, int flags,
unsigned int preferred, unsigned int valid, void *vparam)
@@ -514,6 +560,7 @@ static int add_prefixes(struct in6_addr *local, int prefix,
int deprecate = 0;
int constructed = 0;
int adv_router = 0;
int off_link = 0;
unsigned int time = 0xffffffff;
struct dhcp_context *context;
@@ -586,6 +633,7 @@ static int add_prefixes(struct in6_addr *local, int prefix,
context->ra_time = 0;
context->flags |= CONTEXT_RA_DONE;
real_prefix = context->prefix;
off_link = (context->flags & CONTEXT_RA_OFF_LINK);
}
param->first = 0;
@@ -636,8 +684,9 @@ static int add_prefixes(struct in6_addr *local, int prefix,
opt->type = ICMP6_OPT_PREFIX;
opt->len = 4;
opt->prefix_len = real_prefix;
/* autonomous only if we're not doing dhcp, always set "on-link" */
opt->flags = 0x80;
/* autonomous only if we're not doing dhcp, set
"on-link" unless "off-link" was specified */
opt->flags = (off_link ? 0 : 0x80);
if (do_slaac)
opt->flags |= 0x40;
if (adv_router)
@@ -683,6 +732,7 @@ time_t periodic_ra(time_t now)
struct search_param param;
struct dhcp_context *context;
time_t next_event;
struct alias_param aparam;
param.now = now;
param.iface = 0;
@@ -730,12 +780,84 @@ time_t periodic_ra(time_t now)
if (tmp->name && wildcard_match(tmp->name, param.name))
break;
if (!tmp)
send_ra(now, param.iface, param.name, NULL);
{
send_ra(now, param.iface, param.name, NULL);
/* Also send on all interfaces that are aliases of this
one. */
for (aparam.bridge = daemon->bridges;
aparam.bridge;
aparam.bridge = aparam.bridge->next)
if ((int)if_nametoindex(aparam.bridge->iface) == param.iface)
{
/* Count the number of alias interfaces for this
'bridge', by calling iface_enumerate with
send_ra_to_aliases and NULL alias_ifs. */
aparam.iface = param.iface;
aparam.alias_ifs = NULL;
aparam.num_alias_ifs = 0;
iface_enumerate(AF_LOCAL, &aparam, send_ra_to_aliases);
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s => %d alias(es)",
param.name, daemon->addrbuff, aparam.num_alias_ifs);
/* Allocate memory to store the alias interface
indices. */
aparam.alias_ifs = (int *)whine_malloc(aparam.num_alias_ifs *
sizeof(int));
if (aparam.alias_ifs)
{
/* Use iface_enumerate again to get the alias
interface indices, then send on each of
those. */
aparam.max_alias_ifs = aparam.num_alias_ifs;
aparam.num_alias_ifs = 0;
iface_enumerate(AF_LOCAL, &aparam, send_ra_to_aliases);
for (; aparam.num_alias_ifs; aparam.num_alias_ifs--)
{
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s => i/f %d",
param.name, daemon->addrbuff,
aparam.alias_ifs[aparam.num_alias_ifs - 1]);
send_ra_alias(now,
param.iface,
param.name,
NULL,
aparam.alias_ifs[aparam.num_alias_ifs - 1]);
}
free(aparam.alias_ifs);
}
/* The source interface can only appear in at most
one --bridge-interface. */
break;
}
}
}
}
return next_event;
}
static int send_ra_to_aliases(int index, unsigned int type, char *mac, size_t maclen, void *parm)
{
struct alias_param *aparam = (struct alias_param *)parm;
char ifrn_name[IFNAMSIZ];
struct dhcp_bridge *alias;
(void)type;
(void)mac;
(void)maclen;
if (if_indextoname(index, ifrn_name))
for (alias = aparam->bridge->alias; alias; alias = alias->next)
if (wildcard_matchn(alias->iface, ifrn_name, IFNAMSIZ))
{
if (aparam->alias_ifs && (aparam->num_alias_ifs < aparam->max_alias_ifs))
aparam->alias_ifs[aparam->num_alias_ifs] = index;
aparam->num_alias_ifs++;
}
return 1;
}
static int iface_search(struct in6_addr *local, int prefix,
int scope, int if_index, int flags,
int preferred, int valid, void *vparam)
+56 -589
View File
@@ -77,53 +77,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
p = l + (unsigned char *)header;
}
else if (label_type == 0x80)
return 0; /* reserved */
else if (label_type == 0x40)
{ /* ELT */
unsigned int count, digs;
if ((l & 0x3f) != 1)
return 0; /* we only understand bitstrings */
if (!isExtract)
return 0; /* Cannot compare bitsrings */
count = *p++;
if (count == 0)
count = 256;
digs = ((count-1)>>2)+1;
/* output is \[x<hex>/siz]. which is digs+7/8/9 chars */
namelen += digs+7;
if (count > 9)
namelen++;
if (count > 99)
namelen++;
if (namelen+1 >= MAXDNAME)
return 0;
if (!CHECK_LEN(header, p, plen, (count-1)>>3))
return 0;
*cp++ = '\\';
*cp++ = '[';
*cp++ = 'x';
for (j=0; j<digs; j++)
{
unsigned int dig;
if (j%2 == 0)
dig = *p >> 4;
else
dig = *p++ & 0x0f;
*cp++ = dig < 10 ? dig + '0' : dig + 'A' - 10;
}
cp += sprintf((char *)cp, "/%d]", count);
/* do this here to overwrite the zero char from sprintf */
*cp++ = '.';
}
else
else if (label_type == 0x00)
{ /* label_type = 0 -> label. */
namelen += l + 1; /* include period */
if (namelen >= MAXDNAME)
@@ -176,12 +130,14 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
retvalue = 2;
}
}
if (isExtract)
*cp++ = '.';
else if (*cp != 0 && *cp++ != '.')
retvalue = 2;
}
else
return 0; /* label types 0x40 and 0x80 not supported */
}
}
@@ -452,331 +408,22 @@ size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *phea
return ansp - (unsigned char *)header;
}
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign)
{
/* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
also return length of pseudoheader in *len and pointer to the UDP size in *p
Finally, check to see if a packet is signed. If it is we cannot change a single bit before
forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
int i, arcount = ntohs(header->arcount);
unsigned char *ansp = (unsigned char *)(header+1);
unsigned short rdlen, type, class;
unsigned char *ret = NULL;
if (is_sign)
{
*is_sign = 0;
if (OPCODE(header) == QUERY)
{
for (i = ntohs(header->qdcount); i != 0; i--)
{
if (!(ansp = skip_name(ansp, header, plen, 4)))
return NULL;
GETSHORT(type, ansp);
GETSHORT(class, ansp);
if (class == C_IN && type == T_TKEY)
*is_sign = 1;
}
}
}
else
{
if (!(ansp = skip_questions(header, plen)))
return NULL;
}
if (arcount == 0)
return NULL;
if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
return NULL;
for (i = 0; i < arcount; i++)
{
unsigned char *save, *start = ansp;
if (!(ansp = skip_name(ansp, header, plen, 10)))
return NULL;
GETSHORT(type, ansp);
save = ansp;
GETSHORT(class, ansp);
ansp += 4; /* TTL */
GETSHORT(rdlen, ansp);
if (!ADD_RDLEN(header, ansp, plen, rdlen))
return NULL;
if (type == T_OPT)
{
if (len)
*len = ansp - start;
if (p)
*p = save;
ret = start;
}
else if (is_sign &&
i == arcount - 1 &&
class == C_ANY &&
type == T_TSIG)
*is_sign = 1;
}
return ret;
}
struct macparm {
unsigned char *limit;
struct dns_header *header;
size_t plen;
union mysockaddr *l3;
};
static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
int optno, unsigned char *opt, size_t optlen, int set_do)
{
unsigned char *lenp, *datap, *p;
int rdlen, is_sign;
if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)))
{
if (is_sign)
return plen;
/* We are adding the pseudoheader */
if (!(p = skip_questions(header, plen)) ||
!(p = skip_section(p,
ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
header, plen)))
return plen;
*p++ = 0; /* empty name */
PUTSHORT(T_OPT, p);
PUTSHORT(SAFE_PKTSZ, p); /* max packet length, this will be overwritten */
PUTSHORT(0, p); /* extended RCODE and version */
PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */
lenp = p;
PUTSHORT(0, p); /* RDLEN */
rdlen = 0;
if (((ssize_t)optlen) > (limit - (p + 4)))
return plen; /* Too big */
header->arcount = htons(ntohs(header->arcount) + 1);
datap = p;
}
else
{
int i;
unsigned short code, len, flags;
/* Must be at the end, if exists */
if (ntohs(header->arcount) != 1 ||
is_sign ||
(!(p = skip_name(p, header, plen, 10))))
return plen;
p += 6; /* skip UDP length and RCODE */
GETSHORT(flags, p);
if (set_do)
{
p -=2;
PUTSHORT(flags | 0x8000, p);
}
lenp = p;
GETSHORT(rdlen, p);
if (!CHECK_LEN(header, p, plen, rdlen))
return plen; /* bad packet */
datap = p;
/* no option to add */
if (optno == 0)
return plen;
/* check if option already there */
for (i = 0; i + 4 < rdlen; i += len + 4)
{
GETSHORT(code, p);
GETSHORT(len, p);
if (code == optno)
return plen;
p += len;
}
if (((ssize_t)optlen) > (limit - (p + 4)))
return plen; /* Too big */
}
if (optno != 0)
{
PUTSHORT(optno, p);
PUTSHORT(optlen, p);
memcpy(p, opt, optlen);
p += optlen;
}
PUTSHORT(p - datap, lenp);
return p - (unsigned char *)header;
}
static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
{
struct macparm *parm = parmv;
int match = 0;
if (family == parm->l3->sa.sa_family)
{
if (family == AF_INET && memcmp(&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
match = 1;
#ifdef HAVE_IPV6
else
if (family == AF_INET6 && memcmp(&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
match = 1;
#endif
}
if (!match)
return 1; /* continue */
parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
return 0; /* done */
}
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3)
{
struct macparm parm;
/* Must have an existing pseudoheader as the only ar-record,
or have no ar-records. Must also not be signed */
if (ntohs(header->arcount) > 1)
return plen;
parm.header = header;
parm.limit = (unsigned char *)limit;
parm.plen = plen;
parm.l3 = l3;
iface_enumerate(AF_UNSPEC, &parm, filter_mac);
return parm.plen;
}
struct subnet_opt {
u16 family;
u8 source_netmask, scope_netmask;
#ifdef HAVE_IPV6
u8 addr[IN6ADDRSZ];
#else
u8 addr[INADDRSZ];
#endif
};
static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
{
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
int len;
void *addrp;
#ifdef HAVE_IPV6
if (source->sa.sa_family == AF_INET6)
{
opt->family = htons(2);
opt->source_netmask = daemon->addr6_netmask;
addrp = &source->in6.sin6_addr;
}
else
#endif
{
opt->family = htons(1);
opt->source_netmask = daemon->addr4_netmask;
addrp = &source->in.sin_addr;
}
opt->scope_netmask = 0;
len = 0;
if (opt->source_netmask != 0)
{
len = ((opt->source_netmask - 1) >> 3) + 1;
memcpy(opt->addr, addrp, len);
if (opt->source_netmask & 7)
opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));
}
return len + 4;
}
size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source)
{
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
int len;
struct subnet_opt opt;
len = calc_subnet_opt(&opt, source);
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
}
#ifdef HAVE_DNSSEC
size_t add_do_bit(struct dns_header *header, size_t plen, char *limit)
{
return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1);
}
#endif
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
{
/* Section 9.2, Check that subnet option in reply matches. */
int len, calc_len;
struct subnet_opt opt;
unsigned char *p;
int code, i, rdlen;
calc_len = calc_subnet_opt(&opt, peer);
if (!(p = skip_name(pseudoheader, header, plen, 10)))
return 1;
p += 8; /* skip UDP length and RCODE */
GETSHORT(rdlen, p);
if (!CHECK_LEN(header, p, plen, rdlen))
return 1; /* bad packet */
/* check if option there */
for (i = 0; i + 4 < rdlen; i += len + 4)
{
GETSHORT(code, p);
GETSHORT(len, p);
if (code == EDNS0_OPTION_CLIENT_SUBNET)
{
/* make sure this doesn't mismatch. */
opt.scope_netmask = p[3];
if (len != calc_len || memcmp(p, &opt, len) != 0)
return 0;
}
p += len;
}
return 1;
}
/* is addr in the non-globally-routed IP space? */
int private_net(struct in_addr addr, int ban_localhost)
{
in_addr_t ip_addr = ntohl(addr.s_addr);
return
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
((ip_addr & 0xFF000000) == 0x00000000) /* RFC 5735 section 3. "here" network */ ||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ||
((ip_addr & 0xFFFFFF00) == 0xC0000200) /* 192.0.2.0/24 (test-net) */ ||
((ip_addr & 0xFFFFFF00) == 0xC6336400) /* 198.51.100.0/24(test-net) */ ||
((ip_addr & 0xFFFFFF00) == 0xCB007100) /* 203.0.113.0/24 (test-net) */ ||
((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ;
}
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored)
@@ -1288,11 +935,9 @@ int check_for_local_domain(char *name, time_t now)
struct naptr *naptr;
/* Note: the call to cache_find_by_name is intended to find any record which matches
ie A, AAAA, CNAME, DS. Because RRSIG records are marked by setting both F_DS and F_DNSKEY,
cache_find_by name ordinarily only returns records with an exact match on those bits (ie
for the call below, only DS records). The F_NSIGMATCH bit changes this behaviour */
ie A, AAAA, CNAME. */
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME | F_DS | F_NO_RR | F_NSIGMATCH)) &&
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME |F_NO_RR)) &&
(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
return 1;
@@ -1540,54 +1185,37 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
/* return zero if we can't answer from cache, or packet size if we can */
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)
time_t now, int ad_reqd, int do_bit, int have_pseudoheader)
{
char *name = daemon->namebuff;
unsigned char *p, *ansp, *pheader;
unsigned char *p, *ansp;
unsigned int qtype, qclass;
struct all_addr addr;
int nameoffset;
unsigned short flag;
int q, ans, anscount = 0, addncount = 0;
int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
int dryrun = 0;
struct crec *crecp;
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
struct mx_srv_record *rec;
size_t len;
if (ntohs(header->ancount) != 0 ||
ntohs(header->nscount) != 0 ||
ntohs(header->qdcount) == 0 ||
OPCODE(header) != QUERY )
return 0;
/* Don't return AD set if checking disabled. */
if (header->hb4 & HB4_CD)
sec_data = 0;
/* RFC 6840 5.7 */
*ad_reqd = header->hb4 & HB4_AD;
*do_bit = 0;
/* If there is an RFC2671 pseudoheader then it will be overwritten by
/* If there is an additional data section then it will be overwritten by
partial replies, so we have to do a dry run to see if we can answer
the query. We check to see if the do bit is set, if so we always
forward rather than answering from the cache, which doesn't include
security information, unless we're in DNSSEC validation mode. */
the query. */
if (ntohs(header->arcount) != 0)
dryrun = 1;
if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
{
unsigned short flags;
have_pseudoheader = 1;
pheader += 4; /* udp size, ext_rcode */
GETSHORT(flags, pheader);
if ((sec_reqd = flags & 0x8000))
*do_bit = 1;/* do bit */
*ad_reqd = 1;
dryrun = 1;
}
if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
return 0;
for (rec = daemon->mxnames; rec; rec = rec->next)
rec->offset = 0;
@@ -1611,11 +1239,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
GETSHORT(qtype, p);
GETSHORT(qclass, p);
/* Don't filter RRSIGS from answers to ANY queries, even if do-bit
not set. */
if (qtype == T_ANY)
*do_bit = 1;
ans = 0; /* have we answered this question */
if (qtype == T_TXT || qtype == T_ANY)
@@ -1649,98 +1272,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
}
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DNSKEY || qtype == T_DS))
{
int gotone = 0;
struct blockdata *keydata;
/* Do we have RRSIG? Can't do DS or DNSKEY otherwise. */
if (sec_reqd)
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype)
break;
}
if (!sec_reqd || crecp)
{
if (qtype == T_DS)
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
if (crecp->uid == qclass)
{
gotone = 1;
if (!dryrun)
{
if (crecp->flags & F_NEG)
{
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
log_query(F_UPSTREAM, name, NULL, "no DS");
}
else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
{
struct all_addr a;
a.addr.keytag = crecp->addr.ds.keytag;
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_DS, qclass, "sbbt",
crecp->addr.ds.keytag, crecp->addr.ds.algo,
crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
anscount++;
}
}
}
}
else /* DNSKEY */
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY)))
if (crecp->uid == qclass)
{
gotone = 1;
if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL)))
{
struct all_addr a;
a.addr.keytag = crecp->addr.key.keytag;
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DNSKEY keytag %u");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_DNSKEY, qclass, "sbbt",
crecp->addr.key.flags, 3, crecp->addr.key.algo, crecp->addr.key.keylen, keydata))
anscount++;
}
}
}
}
/* Now do RRSIGs */
if (gotone)
{
ans = 1;
auth = 0;
if (!dryrun && sec_reqd)
{
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype &&
(keydata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL)))
{
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_RRSIG, qclass, "t", crecp->addr.sig.keylen, keydata);
anscount++;
}
}
}
}
#endif
if (qclass == C_IN)
{
struct txt_record *t;
@@ -1749,6 +1280,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name))
{
ans = 1;
sec_data = 0;
if (!dryrun)
{
log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
@@ -1805,6 +1337,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (intr)
{
sec_data = 0;
ans = 1;
if (!dryrun)
{
@@ -1818,6 +1351,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
else if (ptr)
{
ans = 1;
sec_data = 0;
if (!dryrun)
{
log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>");
@@ -1832,38 +1366,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
}
else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
{
if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
{
if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
crecp = NULL;
#ifdef HAVE_DNSSEC
else if (crecp->flags & F_DNSSECOK)
{
int gotsig = 0;
struct crec *rr_crec = NULL;
while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
{
if (rr_crec->addr.sig.type_covered == T_PTR && rr_crec->uid == C_IN)
{
char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
gotsig = 1;
if (!dryrun &&
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
rr_crec->ttd - now, &nameoffset,
T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata))
anscount++;
}
}
if (!gotsig)
crecp = NULL;
}
#endif
}
if (crecp)
/* Don't use cache when DNSSEC data required, unless we know that
the zone is unsigned, which implies that we're doing
validation. */
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
!do_bit ||
(option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))
{
do
{
@@ -1873,19 +1381,19 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!(crecp->flags & F_DNSSECOK))
sec_data = 0;
ans = 1;
if (crecp->flags & F_NEG)
{
ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
}
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))
else
{
ans = 1;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
auth = 0;
if (!dryrun)
@@ -1905,6 +1413,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
else if (is_rev_synth(is_arpa, &addr, name))
{
ans = 1;
sec_data = 0;
if (!dryrun)
{
log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL);
@@ -1921,6 +1430,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
{
/* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
ans = 1;
sec_data = 0;
nxdomain = 1;
if (!dryrun)
log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN,
@@ -1968,6 +1478,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (i == 4)
{
ans = 1;
sec_data = 0;
if (!dryrun)
{
addr.addr.addr4.s_addr = htonl(a);
@@ -2006,6 +1517,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
continue;
#endif
ans = 1;
sec_data = 0;
if (!dryrun)
{
gotit = 1;
@@ -2045,48 +1557,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
crecp = save;
}
/* If the client asked for DNSSEC and we can't provide RRSIGs, either
because we've not doing DNSSEC or the cached answer is signed by negative,
don't answer from the cache, forward instead. */
if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
{
if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
crecp = NULL;
#ifdef HAVE_DNSSEC
else if (crecp->flags & F_DNSSECOK)
{
/* We're returning validated data, need to return the RRSIG too. */
struct crec *rr_crec = NULL;
int sigtype = type;
/* The signature may have expired even though the data is still in cache,
forward instead of answering from cache if so. */
int gotsig = 0;
if (crecp->flags & F_CNAME)
sigtype = T_CNAME;
while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
{
if (rr_crec->addr.sig.type_covered == sigtype && rr_crec->uid == C_IN)
{
char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
gotsig = 1;
if (!dryrun &&
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
rr_crec->ttd - now, &nameoffset,
T_RRSIG, C_IN, "t", rr_crec->addr.sig.keylen, sigdata))
anscount++;
}
}
if (!gotsig)
crecp = NULL;
}
#endif
}
if (crecp)
/* If the client asked for DNSSEC don't use cached data. */
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK))
do
{
/* don't answer wildcard queries with data not from /etc/hosts
@@ -2120,17 +1592,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (crecp->flags & F_NEG)
{
/* We don't cache NSEC records, so if a DNSSEC-validated negative answer
is cached and the client wants DNSSEC, forward rather than answering from the cache */
if (!sec_reqd || !(crecp->flags & F_DNSSECOK))
{
ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags, name, NULL, NULL);
}
ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags, name, NULL, NULL);
}
else
{
@@ -2368,14 +1835,14 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
len = ansp - (unsigned char *)header;
/* Advertise our packet size limit in our reply */
if (have_pseudoheader)
len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
len = add_pseudoheader(header, len, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit);
if (*ad_reqd && sec_data)
if (ad_reqd && sec_data)
header->hb4 |= HB4_AD;
else
header->hb4 &= ~HB4_AD;
return len;
}
+7 -2
View File
@@ -805,9 +805,14 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (service->type == type)
break;
if (!service || !service->basename)
return 0;
for (; context; context = context->current)
if (match_netid(context->filter, tagif_netid, 1) &&
is_same_net(mess->ciaddr, context->start, context->netmask))
break;
if (!service || !service->basename || !context)
return 0;
clear_packet(mess, end);
mess->yiaddr = mess->ciaddr;
+8 -7
View File
@@ -130,7 +130,7 @@ static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
MAC address from the local ND cache. */
if (!state->link_address)
get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type);
get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type, now);
else
{
struct dhcp_context *c;
@@ -1089,7 +1089,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
{
struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
if (!address6_available(state->context, req_addr, tagif, 1))
if (!address6_valid(state->context, req_addr, tagif, 1))
{
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOTONLINK);
@@ -1320,15 +1320,15 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
if (opt_cfg->opt == OPTION6_REFRESH_TIME)
done_refresh = 1;
if (opt_cfg->opt == OPTION6_DNS_SERVER)
done_dns = 1;
if (opt_cfg->flags & DHOPT_ADDR6)
{
int len, j;
struct in6_addr *a;
if (opt_cfg->opt == OPTION6_DNS_SERVER)
done_dns = 1;
for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, j = 0;
j < opt_cfg->len; j += IN6ADDRSZ, a++)
if ((IN6_IS_ADDR_ULA_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) ||
@@ -2054,7 +2054,8 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
return ret;
}
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id)
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz,
struct in6_addr *peer_address, u32 scope_id, time_t now)
{
/* ->local is same value for all relays on ->current chain */
@@ -2068,7 +2069,7 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer
unsigned char mac[DHCP_CHADDR_MAX];
inet_pton(AF_INET6, ALL_SERVERS, &multicast);
get_client_mac(peer_address, scope_id, mac, &maclen, &mactype);
get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now);
/* source address == relay address */
from.addr.addr6 = relay->local.addr.addr6;
+339
View File
@@ -0,0 +1,339 @@
/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Code to safely remove RRs from an DNS answer */
#include "dnsmasq.h"
/* Go through a domain name, find "pointers" and fix them up based on how many bytes
we've chopped out of the packet, or check they don't point into an elided part. */
static int check_name(unsigned char **namep, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
{
unsigned char *ansp = *namep;
while(1)
{
unsigned int label_type;
if (!CHECK_LEN(header, ansp, plen, 1))
return 0;
label_type = (*ansp) & 0xc0;
if (label_type == 0xc0)
{
/* pointer for compression. */
unsigned int offset;
int i;
unsigned char *p;
if (!CHECK_LEN(header, ansp, plen, 2))
return 0;
offset = ((*ansp++) & 0x3f) << 8;
offset |= *ansp++;
p = offset + (unsigned char *)header;
for (i = 0; i < rr_count; i++)
if (p < rrs[i])
break;
else
if (i & 1)
offset -= rrs[i] - rrs[i-1];
/* does the pointer end up in an elided RR? */
if (i & 1)
return 0;
/* No, scale the pointer */
if (fixup)
{
ansp -= 2;
*ansp++ = (offset >> 8) | 0xc0;
*ansp++ = offset & 0xff;
}
break;
}
else if (label_type == 0x80)
return 0; /* reserved */
else if (label_type == 0x40)
{
/* Extended label type */
unsigned int count;
if (!CHECK_LEN(header, ansp, plen, 2))
return 0;
if (((*ansp++) & 0x3f) != 1)
return 0; /* we only understand bitstrings */
count = *(ansp++); /* Bits in bitstring */
if (count == 0) /* count == 0 means 256 bits */
ansp += 32;
else
ansp += ((count-1)>>3)+1;
}
else
{ /* label type == 0 Bottom six bits is length */
unsigned int len = (*ansp++) & 0x3f;
if (!ADD_RDLEN(header, ansp, plen, len))
return 0;
if (len == 0)
break; /* zero length label marks the end. */
}
}
*namep = ansp;
return 1;
}
/* Go through RRs and check or fixup the domain names contained within */
static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
{
int i, j, type, class, rdlen;
unsigned char *pp;
for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++)
{
pp = p;
if (!(p = skip_name(p, header, plen, 10)))
return 0;
GETSHORT(type, p);
GETSHORT(class, p);
p += 4; /* TTL */
GETSHORT(rdlen, p);
/* If this RR is to be elided, don't fix up its contents */
for (j = 0; j < rr_count; j += 2)
if (rrs[j] == pp)
break;
if (j >= rr_count)
{
/* fixup name of RR */
if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
return 0;
if (class == C_IN)
{
u16 *d;
for (pp = p, d = rrfilter_desc(type); *d != (u16)-1; d++)
{
if (*d != 0)
pp += *d;
else if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
return 0;
}
}
}
if (!ADD_RDLEN(header, p, plen, rdlen))
return 0;
}
return 1;
}
/* mode is 0 to remove EDNS0, 1 to filter DNSSEC RRs */
size_t rrfilter(struct dns_header *header, size_t plen, int mode)
{
static unsigned char **rrs;
static int rr_sz = 0;
unsigned char *p = (unsigned char *)(header+1);
int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar;
if (ntohs(header->qdcount) != 1 ||
!(p = skip_name(p, header, plen, 4)))
return plen;
GETSHORT(qtype, p);
GETSHORT(qclass, p);
/* First pass, find pointers to start and end of all the records we wish to elide:
records added for DNSSEC, unless explicity queried for */
for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0;
i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount);
i++)
{
unsigned char *pstart = p;
int type, class;
if (!(p = skip_name(p, header, plen, 10)))
return plen;
GETSHORT(type, p);
GETSHORT(class, p);
p += 4; /* TTL */
GETSHORT(rdlen, p);
if (!ADD_RDLEN(header, p, plen, rdlen))
return plen;
/* Don't remove the answer. */
if (i < ntohs(header->ancount) && type == qtype && class == qclass)
continue;
if (mode == 0) /* EDNS */
{
/* EDNS mode, remove T_OPT from additional section only */
if (i < (ntohs(header->nscount) + ntohs(header->ancount)) || type != T_OPT)
continue;
}
else if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
/* DNSSEC mode, remove SIGs and NSECs from all three sections. */
continue;
if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
return plen;
rrs[rr_found++] = pstart;
rrs[rr_found++] = p;
if (i < ntohs(header->ancount))
chop_an++;
else if (i < (ntohs(header->nscount) + ntohs(header->ancount)))
chop_ns++;
else
chop_ar++;
}
/* Nothing to do. */
if (rr_found == 0)
return plen;
/* Second pass, look for pointers in names in the records we're keeping and make sure they don't
point to records we're going to elide. This is theoretically possible, but unlikely. If
it happens, we give up and leave the answer unchanged. */
p = (unsigned char *)(header+1);
/* question first */
if (!check_name(&p, header, plen, 0, rrs, rr_found))
return plen;
p += 4; /* qclass, qtype */
/* Now answers and NS */
if (!check_rrs(p, header, plen, 0, rrs, rr_found))
return plen;
/* Third pass, elide records */
for (p = rrs[0], i = 1; i < rr_found; i += 2)
{
unsigned char *start = rrs[i];
unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + plen;
memmove(p, start, end-start);
p += end-start;
}
plen = p - (unsigned char *)header;
header->ancount = htons(ntohs(header->ancount) - chop_an);
header->nscount = htons(ntohs(header->nscount) - chop_ns);
header->arcount = htons(ntohs(header->arcount) - chop_ar);
/* Fourth pass, fix up pointers in the remaining records */
p = (unsigned char *)(header+1);
check_name(&p, header, plen, 1, rrs, rr_found);
p += 4; /* qclass, qtype */
check_rrs(p, header, plen, 1, rrs, rr_found);
return plen;
}
/* This is used in the DNSSEC code too, hence it's exported */
u16 *rrfilter_desc(int type)
{
/* List of RRtypes which include domains in the data.
0 -> domain
integer -> no of plain bytes
-1 -> end
zero is not a valid RRtype, so the final entry is returned for
anything which needs no mangling.
*/
static u16 rr_desc[] =
{
T_NS, 0, -1,
T_MD, 0, -1,
T_MF, 0, -1,
T_CNAME, 0, -1,
T_SOA, 0, 0, -1,
T_MB, 0, -1,
T_MG, 0, -1,
T_MR, 0, -1,
T_PTR, 0, -1,
T_MINFO, 0, 0, -1,
T_MX, 2, 0, -1,
T_RP, 0, 0, -1,
T_AFSDB, 2, 0, -1,
T_RT, 2, 0, -1,
T_SIG, 18, 0, -1,
T_PX, 2, 0, 0, -1,
T_NXT, 0, -1,
T_KX, 2, 0, -1,
T_SRV, 6, 0, -1,
T_DNAME, 0, -1,
0, -1 /* wildcard/catchall */
};
u16 *p = rr_desc;
while (*p != type && *p != 0)
while (*p++ != (u16)-1);
return p+1;
}
int expand_workspace(unsigned char ***wkspc, int *szp, int new)
{
unsigned char **p;
int old = *szp;
if (old >= new+1)
return 1;
if (new >= 100)
return 0;
new += 5;
if (!(p = whine_malloc(new * sizeof(unsigned char **))))
return 0;
if (old != 0 && *wkspc)
{
memcpy(p, *wkspc, old * sizeof(unsigned char **));
free(*wkspc);
}
*wkspc = p;
*szp = new;
return 1;
}
+2 -2
View File
@@ -502,7 +502,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
return NULL;
}
void check_tftp_listeners(fd_set *rset, time_t now)
void check_tftp_listeners(time_t now)
{
struct tftp_transfer *transfer, *tmp, **up;
ssize_t len;
@@ -518,7 +518,7 @@ void check_tftp_listeners(fd_set *rset, time_t now)
prettyprint_addr(&transfer->peer, daemon->addrbuff);
if (FD_ISSET(transfer->sockfd, rset))
if (poll_check(transfer->sockfd, POLLIN))
{
/* we overwrote the buffer... */
daemon->srv_save = NULL;
-6
View File
@@ -570,12 +570,6 @@ char *print_mac(char *buff, unsigned char *mac, int len)
return buff;
}
void bump_maxfd(int fd, int *max)
{
if (fd > *max)
*max = fd;
}
/* rc is return from sendto and friends.
Return 1 if we should retry.
Set errno to zero if we succeeded. */