Update to 2.74 45/65045/1 accepted/tizen/common/20160407.132919 accepted/tizen/ivi/20160407.111623 accepted/tizen/mobile/20160407.111534 accepted/tizen/tv/20160407.111550 accepted/tizen/wearable/20160407.111603 submit/tizen/20160407.062043
authorSeonah Moon <seonah1.moon@samsung.com>
Thu, 7 Apr 2016 05:01:07 +0000 (14:01 +0900)
committerSeonah Moon <seonah1.moon@samsung.com>
Thu, 7 Apr 2016 05:01:28 +0000 (14:01 +0900)
Change-Id: Ic7e94612466be7786c3d2b0724d745c7720e01c9
Signed-off-by: Seonah Moon <seonah1.moon@samsung.com>
84 files changed:
CHANGELOG
CMakeLists.txt
FAQ
Makefile
VERSION [new file with mode: 0644]
bld/Android.mk
bld/bloat-o-meter [new file with mode: 0755]
bld/get-version [new file with mode: 0755]
bld/install-man
bld/install-mo
bld/pkg-wrapper
contrib/conntrack/README [new file with mode: 0644]
contrib/dbus-test/dbus-test.py [new file with mode: 0755]
contrib/mactable/macscript [new file with mode: 0755]
contrib/port-forward/dnsmasq-portforward
contrib/reverse-dns/README [new file with mode: 0644]
contrib/reverse-dns/reverse_replace.sh [new file with mode: 0644]
contrib/systemd/README [new file with mode: 0644]
contrib/systemd/dbus_activation [new file with mode: 0644]
contrib/systemd/dnsmasq.service [new file with mode: 0644]
contrib/try-all-ns/dnsmasq-2.68-try-all-ns [new file with mode: 0644]
contrib/wrt/dhcp_lease_time.1 [new file with mode: 0644]
contrib/wrt/dhcp_lease_time.c
contrib/wrt/dhcp_release.1 [new file with mode: 0644]
contrib/wrt/dhcp_release.c
dbus/DBus-interface
dnsmasq.conf.example
doc.html
logo/favicon.ico
man/dnsmasq.8
man/es/dnsmasq.8
man/fr/dnsmasq.8
packaging/dnsmasq.spec
po/de.po
po/es.po
po/fi.po
po/fr.po
po/id.po
po/it.po
po/no.po
po/pl.po
po/pt_BR.po
po/ro.po
setup.html
src/auth.c [new file with mode: 0644]
src/blockdata.c [new file with mode: 0644]
src/bpf.c
src/cache.c
src/config.h
src/conntrack.c [new file with mode: 0644]
src/dbus.c
src/dhcp-common.c [new file with mode: 0644]
src/dhcp-protocol.h [moved from src/dhcp_protocol.h with 94% similarity]
src/dhcp.c
src/dhcp6-protocol.h [new file with mode: 0644]
src/dhcp6.c [new file with mode: 0644]
src/dns-protocol.h [moved from src/dns_protocol.h with 61% similarity]
src/dnsmasq.c
src/dnsmasq.h
src/dnssec.c [new file with mode: 0644]
src/domain.c [new file with mode: 0644]
src/forward.c
src/helper.c
src/inotify.c [new file with mode: 0644]
src/ip6addr.h [new file with mode: 0644]
src/ipset.c [new file with mode: 0644]
src/lease.c
src/log.c
src/loop.c [new file with mode: 0644]
src/netlink.c
src/network.c
src/option.c
src/outpacket.c [new file with mode: 0644]
src/poll.c [new file with mode: 0644]
src/radv-protocol.h [new file with mode: 0644]
src/radv.c [new file with mode: 0644]
src/rfc1035.c
src/rfc2131.c
src/rfc3315.c [new file with mode: 0644]
src/slaac.c [new file with mode: 0644]
src/tables.c [new file with mode: 0644]
src/tftp.c
src/util.c
trust-anchors.conf [new file with mode: 0644]

index fb15e3d..7c621e2 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
+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 
+           Tomas Hozza for spotting this.
+
+           Remove floor of 4096 on advertised EDNS0 packet size when 
+           DNSSEC in use, the original rationale for this has long gone.
+           Thanks to Anders Kaseorg for spotting this.
+
+           Use inotify for checking on updates to /etc/resolv.conf and
+           friends under Linux. This fixes race conditions when the files are 
+           updated rapidly and saves CPU by noy polling. To build
+           a binary that runs on old Linux kernels without inotify,
+           use make COPTS=-DNO_INOTIFY
+
+           Fix breakage of --domain=<domain>,<subnet>,local - only reverse
+           queries were intercepted. THis appears to have been broken 
+           since 2.69. Thanks to Josh Stone for finding the bug.
+
+           Eliminate IPv6 privacy addresses and deprecated addresses from
+           the answers given by --interface-name. Note that reverse queries
+           (ie looking for names, given addresses) are not affected. 
+           Thanks to Michael Gorbach for the suggestion.
+
+           Fix crash in DNSSEC code with long RRs. Thanks to Marco Davids
+           for the bug report.
+           
+           Add --ignore-address option. Ignore replies to A-record 
+           queries which include the specified address. No error is
+           generated, dnsmasq simply continues to listen for another 
+           reply. This is useful to defeat blocking strategies which
+           rely on quickly supplying a forged answer to a DNS 
+           request for certain domains, before the correct answer can
+            arrive. Thanks to Glen Huang for the patch.
+       
+           Revisit the part of DNSSEC validation which determines if an 
+           unsigned answer is legit, or is in some part of the DNS 
+           tree which should be signed. Dnsmasq now works from the 
+           DNS root downward looking for the limit of signed 
+           delegations, rather than working bottom up. This is 
+           both more correct, and less likely to trip over broken 
+           nameservers in the unsigned parts of the DNS tree 
+           which don't respond well to DNSSEC queries.
+
+           Add --log-queries=extra option, which makes logs easier
+           to search automatically.
+
+           Add --min-cache-ttl option. I've resisted this for a long 
+           time, on the grounds that disbelieving TTLs is never a 
+           good idea, but I've been persuaded that there are 
+           sometimes reasons to do it. (Step forward, GFW).
+           To avoid misuse, there's a hard limit on the TTL 
+           floor of one hour. Thansk to RinSatsuki for the patch.
+
+           Cope with multiple interfaces with the same link-local 
+           address. (IPv6 addresses are scoped, so this is allowed.)
+           Thanks to Cory Benfield for help with this.
+
+           Add --dhcp-hostsdir. This allows addition of new host
+           configurations to a running dnsmasq instance much more 
+           cheaply than having dnsmasq re-read all its existing
+           configuration each time. 
+       
+           Don't reply to DHCPv6 SOLICIT messages if we're not 
+           configured to do stateful DHCPv6. Thanks to Win King Wan 
+           for the patch.
+
+           Fix broken DNSSEC validation of ECDSA signatures.
+
+           Add --dnssec-timestamp option, which provides an automatic
+           way to detect when the system time becomes valid after 
+           boot on systems without an RTC, whilst allowing DNS 
+           queries before the clock is valid so that NTP can run. 
+           Thanks to Kevin Darbyshire-Bryant for developing this idea.
+
+           Add --tftp-no-fail option. Thanks to Stefan Tomanek for
+           the patch.
+
+           Fix crash caused by looking up servers.bind, CHAOS text 
+           record, when more than about five --servers= lines are 
+           in the dnsmasq config. This causes memory corruption 
+           which causes a crash later. Thanks to Matt Coddington for 
+           sterling work chasing this down.
+
+           Fix crash on receipt of certain malformed DNS requests.
+           Thanks to Nick Sampanis for spotting the problem.
+           Note that this is could allow the dnsmasq process's
+           memory to be read by an attacker under certain
+           circumstances, so it has a CVE, CVE-2015-3294 
+
+            Fix crash in authoritative DNS code, if a .arpa zone 
+           is declared as authoritative, and then a PTR query which
+           is not to be treated as authoritative arrived. Normally, 
+           directly declaring .arpa zone as authoritative is not 
+           done, so this crash wouldn't be seen. Instead the 
+           relevant .arpa zone should be specified as a subnet
+           in the auth-zone declaration. Thanks to Johnny S. Lee
+           for the bugreport and initial patch.
+
+           Fix authoritative DNS code to correctly reply to NS 
+           and SOA queries for .arpa zones for which we are 
+           declared authoritative by means of a subnet in auth-zone.
+           Previously we provided correct answers to PTR queries
+           in such zones (including NS and SOA) but not direct
+           NS and SOA queries. Thanks to Johnny S. Lee for 
+           pointing out the problem.
+
+           Fix logging of DHCPREPLY which should be suppressed 
+           by quiet-dhcp6. Thanks to J. Pablo Abonia for 
+           spotting the problem.
+
+           Try and handle net connections with broken fragmentation 
+           that lose large UDP packets. If a server times out, 
+            reduce the maximum UDP packet size field in the EDNS0
+           header to 1280 bytes. If it then answers, make that
+           change permanent.
+
+           Check IPv4-mapped IPv6 addresses when --stop-rebind
+           is active. Thanks to Jordan Milne for spotting this.
+
+           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.
+
+           Add support for "ipsets" in *BSD, using pf. Thanks to 
+           Sven Falempim for the patch.
+
+           Fix race condition which could lock up dnsmasq when an 
+           interface goes down and up rapidly. Thanks to Conrad 
+           Kostecki for helping to chase this down.
+
+           Add DBus methods SetFilterWin2KOption and SetBogusPrivOption
+           Thanks to the Smoothwall project for the patch.
+
+           Fix failure to build against Nettle-3.0. Thanks to Steven 
+           Barth for spotting this and finding the fix. 
+           
+           When assigning existing DHCP leases to intefaces by comparing 
+           networks, handle the case that two or more interfaces have the
+           same network part, but different prefix lengths (favour the
+           longer prefix length.) Thanks to Lung-Pin Chang for the 
+           patch.
+           
+           Add a mode which detects and removes DNS forwarding loops, ie 
+           a query sent to an upstream server returns as a new query to 
+           dnsmasq, and would therefore be forwarded again, resulting in 
+           a query which loops many times before being dropped. Upstream
+           servers which loop back are disabled and this event is logged.
+           Thanks to Smoothwall for their sponsorship of this feature.
+
+           Extend --conf-dir to allow filtering of files. So
+           --conf-dir=/etc/dnsmasq.d,\*.conf
+           will load all the files in /etc/dnsmasq.d which end in .conf
+            Fix bug when resulted in NXDOMAIN answers instead of NODATA in
+            some circumstances.
+
+           Fix bug which caused dnsmasq to become unresponsive if it 
+           failed to send packets due to a network interface disappearing.
+           Thanks to Niels Peen for spotting this.
+                   
+            Fix problem with --local-service option on big-endian platforms
+           Thanks to Richard Genoud for the patch.
+
+       
+version 2.71
+            Subtle change to error handling to help DNSSEC validation 
+           when servers fail to provide NODATA answers for 
+           non-existent DS records.
+
+           Tweak code which removes DNSSEC records from answers when
+           not required. Fixes broken answers when additional section
+           has real records in it. Thanks to Marco Davids for the bug 
+           report.
+
+           Fix DNSSEC validation of ANY queries. Thanks to Marco Davids
+           for spotting that too.
+
+           Fix total DNS failure and 100% CPU use if cachesize set to zero,
+           regression introduced in 2.69. Thanks to James Hunt and
+           the Ubuntu crowd for assistance in fixing this.
+
+
+version 2.70
+            Fix crash, introduced in 2.69, on TCP request when dnsmasq
+           compiled with DNSSEC support, but running without DNSSEC
+           enabled. Thanks to Manish Sing for spotting that one.
+
+           Fix regression which broke ipset functionality. Thanks to 
+           Wang Jian for the bug report.
+
+
+version 2.69
+           Implement dynamic interface discovery on *BSD. This allows
+           the contructor: syntax to be used in dhcp-range for DHCPv6
+           on the BSD platform. Thanks to Matthias Andree for
+           valuable research on how to implement this.
+
+           Fix infinite loop associated with some --bogus-nxdomain
+           configs. Thanks fogobogo for the bug report.
+
+           Fix missing RA RDNS option with configuration like
+           --dhcp-option=option6:23,[::] Thanks to Tsachi Kimeldorfer
+           for spotting the problem.
+
+           Add [fd00::] and [fe80::] as special addresses in DHCPv6
+           options, analogous to [::]. [fd00::] is replaced with the
+           actual ULA of the interface on the machine running
+           dnsmasq, [fe80::] with the link-local address. 
+           Thanks to Tsachi Kimeldorfer for championing this.
+
+           DNSSEC validation and caching. Dnsmasq needs to be
+           compiled with this enabled, with 
+           
+           make dnsmasq COPTS=-DHAVE_DNSSEC
+           
+           this add dependencies on the nettle crypto library and the 
+           gmp maths library. It's possible to have these linked
+           statically with
+           
+           make dnsmasq COPTS='-DHAVE_DNSSEC -DHAVE_DNSSEC_STATIC'
+           
+           which bloats the dnsmasq binary, but saves the size of 
+           the shared libraries which are much bigger.
+
+           To enable, DNSSEC, you will need a set of
+           trust-anchors. Now that the TLDs are signed, this can be
+           the keys for the root zone, and for convenience they are
+           included in trust-anchors.conf in the dnsmasq
+           distribution. You should of course check that these are
+           legitimate and up-to-date. So, adding
+           
+           conf-file=/path/to/trust-anchors.conf
+           dnssec
+
+           to your config is all thats needed to get things
+           working. The upstream nameservers have to be DNSSEC-capable
+           too, of course. Many ISP nameservers aren't, but the
+           Google public nameservers (8.8.8.8 and 8.8.4.4) are.
+           When DNSSEC is configured, dnsmasq validates any queries 
+           for domains which are signed. Query results which are 
+           bogus are replaced with SERVFAIL replies, and results 
+           which are correctly signed have the AD bit set. In 
+           addition, and just as importantly, dnsmasq supplies 
+           correct DNSSEC information to clients which are doing 
+           their own validation, and caches DNSKEY, DS and RRSIG
+           records, which significantly improve the performance of 
+           downstream validators. Setting --log-queries will show 
+           DNSSEC in action.
+
+           If a domain is returned from an upstream nameserver without 
+           DNSSEC signature, dnsmasq by default trusts this. This 
+           means that for unsigned zone (still the majority) there 
+           is effectively no cost for having DNSSEC enabled. Of course
+           this allows an attacker to replace a signed record with a 
+           false unsigned record. This is addressed by the 
+           --dnssec-check-unsigned flag, which instructs dnsmasq
+           to prove that an unsigned record is legitimate, by finding  
+           a secure proof that the zone containing the record is not
+           signed. Doing this has costs (typically one or two extra
+           upstream queries). It also has a nasty failure mode if
+           dnsmasq's upstream nameservers are not DNSSEC capable. 
+           Without --dnssec-check-unsigned using such an upstream
+           server will simply result in not queries being validated; 
+           with --dnssec-check-unsigned enabled and a 
+           DNSSEC-ignorant upstream server, _all_ queries will fail.
+
+           Note that DNSSEC requires that the local time is valid and 
+           accurate, if not then DNSSEC validation will fail. NTP 
+           should be running. This presents a problem for routers
+           without a battery-backed clock. To set the time needs NTP 
+           to do DNS lookups, but lookups will fail until NTP has run.
+           To address this, there's a flag, --dnssec-no-timecheck 
+           which disables the time checks (only) in DNSSEC. When dnsmasq
+           is started and the clock is not synced, this flag should
+           be used. As soon as the clock is synced, SIGHUP dnsmasq. 
+           The SIGHUP clears the cache of partially-validated data and
+           resets the no-timecheck flag, so that all DNSSEC checks 
+           henceforward will be complete.
+           
+           The development of DNSSEC in dnsmasq was started by 
+           Giovanni Bajo, to whom huge thanks are owed. It has been
+           supported by Comcast, whose techfund grant has allowed for 
+           an invaluable period of full-time work to get it to 
+           a workable state.
+           Add --rev-server. Thanks to Dave Taht for suggesting this.
+           
+           Add --servers-file. Allows dynamic update of upstream servers 
+           full access to configuration. 
+
+           Add --local-service. Accept DNS queries only from hosts 
+            whose address is on a local subnet, ie a subnet for which 
+            an interface exists on the server. This option
+            only has effect if there are no --interface --except-interface,
+            --listen-address or --auth-server options. It is intended 
+            to be set as a default on installation, to allow
+            unconfigured installations to be useful but also safe from 
+           being used for DNS amplification attacks.
+
+           Fix crashes in cache_get_cname_target() when dangling CNAMEs
+           encountered. Thanks to Andy and the rt-n56u project for
+           find this and helping to chase it down.
+
+           Fix wrong RCODE in authoritative DNS replies to PTR queries. The
+           correct answer was included, but the RCODE was set to NXDOMAIN.
+           Thanks to Craig McQueen for spotting this.
+
+           Make statistics available as DNS queries in the .bind TLD as 
+           well as logging them.
+
+
+version 2.68
+            Use random addresses for DHCPv6 temporary address
+            allocations, instead of algorithmically determined stable
+            addresses.
+
+           Fix bug which meant that the DHCPv6 DUID was not available
+           in DHCP script runs during the lifetime of the dnsmasq
+           process which created the DUID de-novo. Once the DUID was
+           created and stored in the lease file and dnsmasq
+           restarted, this bug disappeared.
+
+           Fix bug introduced in 2.67 which could result in erroneous
+           NXDOMAIN returns to CNAME queries.
+
+           Fix build failures on MacOS X and openBSD.
+
+           Allow subnet specifications in --auth-zone to be interface 
+           names as well as address literals. This makes it possible
+           to configure authoritative DNS when local address ranges
+           are dynamic and works much better than the previous
+           work-around which exempted contructed DHCP ranges from the
+           IP address filtering. As a consequence, that work-around
+           is removed. Under certain circumstances, this change wil
+           break existing configuration: if you're relying on the
+           contructed-range exception, you need to change --auth-zone
+           to specify the same interface as is used to construct your
+           DHCP ranges, probably with a trailing "/6" like this: 
+           --auth-zone=example.com,eth0/6 to limit the addresses to
+           IPv6 addresses of eth0.
+
+           Fix problems when advertising deleted IPv6 prefixes. If
+           the prefix is deleted (rather than replaced), it doesn't
+           get advertised with zero preferred time. Thanks to Tsachi
+           for the bug report. 
+
+           Fix segfault with some locally configured CNAMEs. Thanks
+           to Andrew Childs for spotting the problem.
+
+           Fix memory leak on re-reading /etc/hosts and friends,
+           introduced in 2.67.
+
+           Check the arrival interface of incoming DNS and TFTP
+           requests via IPv6, even in --bind-interfaces mode. This
+           isn't possible for IPv4 and can generate scary warnings,
+           but as it's always possible for IPv6 (the API always
+           exists) then we should do it always. 
+           
+           Tweak the rules on prefix-lengths in --dhcp-range for
+           IPv6. The new rule is that the specified prefix length
+           must be larger than or equal to the prefix length of the
+           corresponding address on the local interface. 
+
+
+version 2.67
+           Fix crash if upstream server returns SERVFAIL when
+           --conntrack in use. Thanks to Giacomo Tazzari for finding
+           this and supplying the patch. 
+
+           Repair regression in 2.64. That release stopped sending
+           lease-time information in the reply to DHCPINFORM
+           requests, on the correct grounds that it was a standards
+           violation. However, this broke the dnsmasq-specific
+           dhcp_lease_time utility. Now, DHCPINFORM returns
+           lease-time only if it's specifically requested
+           (maintaining standards) and the dhcp_lease_time utility
+           has been taught to ask for it (restoring functionality). 
+
+           Fix --dhcp-match, --dhcp-vendorclass and --dhcp-userclass
+           to work with BOOTP and well as DHCP. Thanks to Peter
+           Korsgaard for spotting the problem. 
+
+           Add --synth-domain. Thanks to Vishvananda Ishaya for
+           suggesting this.
+
+           Fix failure to compile ipset.c if old kernel headers are
+           in use. Thanks to Eugene Rudoy for pointing this out.
+
+           Handle IPv4 interface-address labels in Linux. These are
+           often used to emulate the old IP-alias addresses. Before,
+           using --interface=eth0 would service all the addresses of
+           eth0, including ones configured as aliases, which appear
+           in ifconfig as eth0:0. Now, only addresses with the label
+           eth0 are active. This is not backwards compatible: if you
+           want to continue to bind the aliases too, you need to add
+           eg. --interface=eth0:0 to the config. 
+       
+           Fix "failed to set SO_BINDTODEVICE on DHCP socket: Socket 
+           operation on non-socket" error on startup with
+           configurations which have exactly one --interface option
+           and do RA but _not_ DHCPv6. Thanks to Trever Adams for the
+           bug report.
+
+           Generalise --interface-name to cope with IPv6 addresses
+           and multiple addresses per interface per address family.
+
+           Fix option parsing for --dhcp-host, which was generating a
+           spurious error when all seven possible items were
+           included. Thanks to Zhiqiang Wang for the bug report.
+
+           Remove restriction on prefix-length in --auth-zone. Thanks
+           to Toke Hoiland-Jorgensen for suggesting this.
+
+           Log when the maximum number of concurrent DNS queries is
+           reached. Thanks to Marcelo Salhab Brogliato for the patch.
+
+           If wildcards are used in --interface, don't assume that 
+           there will only ever be one available interface for DHCP
+           just because there is one at start-up. More may appear, so
+           we can't use SO_BINDTODEVICE. Thanks to Natrio for the bug
+           report. 
+
+           Increase timeout/number of retries in TFTP to accomodate
+           AudioCodes Voice Gateways doing streaming writes to flash.
+           Thanks to Damian Kaczkowski for spotting the problem.
+
+           Fix crash with empty DHCP string options when adding zero
+           terminator. Thanks to Patrick McLean for the bug report.
+
+           Allow hostnames to start with a number, as allowed in
+           RFC-1123. Thanks to Kyle Mestery for the patch. 
+
+           Fixes to DHCP FQDN option handling: don't terminate FQDN
+           if domain not known and allow a FQDN option with blank
+           name to request that a FQDN option is returned in the
+           reply. Thanks to Roy Marples for the patch.
+
+           Make --clear-on-reload apply to setting upstream servers
+           via DBus too.
+
+           When the address which triggered the construction of an
+           advertised IPv6 prefix disappears, continue to advertise 
+           the prefix for up to 2 hours, with the preferred lifetime
+           set to zero. This satisfies RFC 6204 4.3 L-13 and makes
+           things work better if a prefix disappears without being
+           deprecated first. Thanks to Uwe Schindler for persuasively
+           arguing for this.
+
+           Fix MAC address enumeration on *BSD. Thanks to Brad Smith
+           for the bug report.
+
+           Support RFC-4242 information-refresh-time options in the 
+           reply to DHCPv6 information-request. The lease time of the
+            smallest valid dhcp-range is sent. Thanks to Uwe Schindler 
+           for suggesting this.
+
+           Make --listen-address higher priority than --except-interface
+           in all circumstances. Thanks to Thomas Hood for the bugreport.
+
+           Provide independent control over which interfaces get TFTP 
+           service. If enable-tftp is given a list of interfaces, then TFTP 
+           is provided on those. Without the list, the previous behaviour
+           (provide TFTP to the same interfaces we provide DHCP to) 
+           is retained. Thanks to Lonnie Abelbeck for the suggestion.
+
+           Add --dhcp-relay config option. Many thanks to vtsl.net
+           for sponsoring this development.
+
+           Fix crash with empty tag: in --dhcp-range. Thanks to
+           Kaspar Schleiser for the bug report.
+
+           Add "baseline" and "bloatcheck" makefile targets, for 
+           revealing size changes during development. Thanks to
+           Vladislav Grishenko for the patch. 
+
+           Cope with DHCPv6 clients which send REQUESTs without
+           address options - treat them as SOLICIT with rapid commit.
+
+           Support identification of clients by MAC address in
+           DHCPv6. When using a relay, the relay must support RFC
+           6939 for this to work. It always works for directly
+           connected clients. Thanks to Vladislav Grishenko
+           for prompting this feature.
+           
+           Remove the rule for constructed DHCP ranges that the local
+           address must be either the first or last address in the
+           range. This was originally to avoid SLAAC addresses, but
+           we now explicitly autoconfig and privacy addresses instead.  
+
+           Update Polish translation. Thanks to Jan Psota.
+
+           Fix problem in DHCPv6 vendorclass/userclass matching
+           code. Thanks to Tanguy Bouzeloc for the patch.
+
+           Update Spanish transalation. Thanks to Vicente Soriano.
+
+           Add --ra-param option. Thanks to Vladislav Grishenko for
+           inspiration on this.
+
+           Add --add-subnet configuration, to tell upstream DNS
+           servers where the original client is. Thanks to DNSthingy
+           for sponsoring this feature.
+
+           Add --quiet-dhcp, --quiet-dhcp6 and --quiet-ra. Thanks to
+           Kevin Darbyshire-Bryant for the initial patch.
+
+           Allow A/AAAA records created by --interface-name to be the
+           target of --cname. Thanks to Hadmut Danisch for the
+           suggestion. 
+
+           Avoid treating a --dhcp-host which has an IPv6 address
+           as eligable for use with DHCPv4 on the grounds that it has
+           no address, and vice-versa. Thanks to Yury Konovalov for
+           spotting the problem.
+
+           Do a better job caching dangling CNAMEs. Thanks to Yves
+           Dorfsman for spotting the problem.
+
+version 2.66
+            Add the ability to act as an authoritative DNS
+            server. Dnsmasq can now answer queries from the wider 'net
+            with local data, as long as the correct NS records are set
+            up. Only local data is provided, to avoid creating an open
+            DNS relay. Zone transfer is supported, to allow secondary
+            servers to be configured.
+
+           Add "constructed DHCP ranges" for DHCPv6. This is intended
+           for IPv6 routers which get prefixes dynamically via prefix
+           delegation. With suitable configuration, stateful DHCPv6
+           and RA can happen automatically as prefixes are delegated
+           and then deprecated, without having  to re-write the
+           dnsmasq configuration file or restart the daemon. Thanks to
+           Steven Barth for extensive testing and development work on
+           this idea.
+
+           Fix crash on startup on Solaris 11. Regression probably
+           introduced in 2.61.  Thanks to Geoff Johnstone for the
+           patch.
+
+           Add code to make behaviour for TCP DNS requests that same
+           as for UDP requests, when a request arrives for an allowed 
+           address, but via a banned interface. This change is only
+           active on Linux, since the relevant API is missing (AFAIK)
+           on other platforms. Many thanks to Tomas Hozza for
+           spotting the problem, and doing invaluable discovery of
+           the obscure and undocumented API required for the solution.
+
+           Don't send the default DHCP option advertising dnsmasq as
+           the local DNS server if dnsmasq is configured to not act
+           as DNS server, or it's configured to a non-standard port.
+            Add DNSMASQ_CIRCUIT_ID, DNSMASQ_SUBCRIBER_ID,
+            DNSMASQ_REMOTE_ID variables to the environment of the
+            lease-change script (and the corresponding Lua). These hold
+            information inserted into the DHCP request by a DHCP relay
+            agent. Thanks to Lakefield Communications for providing a
+            bounty for this addition.
+           Fixed crash, introduced in 2.64, whilst handling DHCPv6
+           information-requests with some common configurations.
+           Thanks to Robert M. Albrecht for the bug report and 
+           chasing the problem.
+
+           Add --ipset option. Thanks to Jason A. Donenfeld for the 
+           patch.
+
+           Don't erroneously reject some option names in --dhcp-match
+           options. Thanks to Benedikt Hochstrasser for the bug report.
+           
+           Allow a trailing '*' wildcard in all interface-name
+           configurations. Thanks to Christian Parpart for the patch.
+
+           Handle the situation where libc headers define
+           SO_REUSEPORT, but the kernel in use doesn't, to cope with
+           the introduction of this option to Linux. Thanks to Rich
+           Felker for the bug report.
+
+           Update Polish translation. Thanks to Jan Psota.
+
+           Fix crash if the configured DHCP lease limit is
+           reached. Regression occurred in 2.61. Thanks to Tsachi for
+           the bug report. 
+           
+           Update the French translation. Thanks to Gildas le Nadan.
+
+  
+version 2.65
+           Fix regression which broke forwarding of queries sent via
+           TCP which are not for A and AAAA and which were directed to
+           non-default servers. Thanks to Niax for the bug report.
+
+           Fix failure to build with DHCP support excluded. Thanks to 
+           Gustavo Zacarias for the patch.
+           
+           Fix nasty regression in 2.64 which completely broke cacheing.
+
+
+version 2.64
+            Handle DHCP FQDN options with all flag bits zero and
+            --dhcp-client-update set. Thanks to Bernd Krumbroeck for
+            spotting the problem.
+
+           Finesse the check for /etc/hosts names which conflict with
+           DHCP names. Previously a name/address pair in /etc/hosts
+           which didn't match the name/address of a DHCP lease would
+           generate a warning. Now that only happesn if there is not
+           also a match. This allows multiple addresses for a name in 
+           /etc/hosts with one of them assigned via DHCP.
+
+           Fix broken vendor-option processing for BOOTP. Thanks to
+           Hans-Joachim Baader for the bug report.
+
+           Don't report spurious netlink errors, regression in
+           2.63. Thanks to Vladislav Grishenko for the patch.
+
+           Flag DHCP or DHCPv6 in starup logging. Thanks to 
+           Vladislav Grishenko for the patch.
+
+           Add SetServersEx method in DBus interface. Thanks to Dan
+           Williams for the patch.
+
+           Add SetDomainServers method in DBus interface. Thanks to
+           Roy Marples for the patch.
+
+           Fix build with later Lua libraries. Thansk to Cristian
+           Rodriguez for the patch.
+
+           Add --max-cache-ttl option. Thanks to Dennis Kaarsemaker
+           for the patch.
+
+           Fix breakage of --host-record parsing, resulting in
+           infinte loop at startup. Regression in 2.63. Thanks to
+           Haim Gelfenbeyn for spotting this.
+
+           Set SO_REUSEADDRESS and SO_V6ONLY options on the DHCPv6
+           socket, this allows multiple instances of dnsmasq on a
+           single machine, in the same way as for DHCPv4. Thanks to
+           Gene Czarcinski and Vladislav Grishenko for work on this.
+
+           Fix DHCPv6 to do access control correctly when it's 
+           configured with --listen-address. Thanks to
+           Gene Czarcinski for sorting this out. 
+
+           Add a "wildcard" dhcp-range which works for any IPv6
+           subnet, --dhcp-range=::,static Useful for Stateless 
+           DHCPv6. Thanks to Vladislav Grishenko for the patch.
+
+           Don't include lease-time in DHCPACK replies to DHCPINFORM
+           queries, since RFC-2131 says we shouldn't. Thanks to
+           Wouter Ibens for pointing this out.  
+
+           Makefile tweak to do dependency checking on header files.
+           Thanks to Johan Peeters for the patch.
+
+           Check interface for outgoing unsolicited router 
+           advertisements, rather than relying on interface address 
+           configuration. Thanks to Gene Czarinski for the patch.
+
+           Handle better attempts to transmit on interfaces which are
+           still doing DAD, and specifically do not just transmit
+           without setting source address and interface, since this
+           can cause very puzzling effects when a router
+           advertisement goes astray. Thanks again to Gene Czarinski.
+
+           Get RA timers right when there is more than one
+           dhcp-range on a subnet.
+           
+
+version 2.63
+            Do duplicate dhcp-host address check in --test mode.
+
+           Check that tftp-root directories are accessible before
+           start-up. Thanks to Daniel Veillard for the initial patch.
+
+           Allow more than one --tfp-root flag. The per-interface
+           stuff is pointless without that.
+
+           Add --bind-dynamic. A hybrid mode between the default and
+           --bind-interfaces which copes with dynamically created
+           interfaces. 
+           
+           A couple of fixes to the build system for Android. Thanks
+           to Metin Kaya for the patches.
+
+           Remove the interface:<interface> argument in --dhcp-range, and
+           the interface argument to --enable-tftp. These were a
+           still-born attempt to allow automatic isolated
+           configuration by libvirt, but have never (to my knowledge)
+           been used, had very strange semantics, and have been
+           superceded by other mechanisms. 
+
+           Fixed bug logging filenames when duplicate dhcp-host
+           addresses are found. Thanks to John Hanks for the patch.
+
+           Fix regression in 2.61 which broke caching of CNAME
+           chains. Thanks to Atul Gupta for the bug report.
+
+           Allow the target of a --cname flag to be another --cname.
+
+            Teach DHCPv6 about the RFC 4242 information-refresh-time
+           option, and add parsing if the minutes, hours and days
+           format for options. Thanks to Francois-Xavier Le Bail for
+           the suggestion.
+
+           Allow "w" (for week) as multiplier in lease times, as well
+           as seconds, minutes, hours and days.  Álvaro Gámez Machado 
+           spotted the ommission.
+           Update French translation. Thanks to Gildas Le Nadan.
+
+           Allow a DBus service name to be given with --enable-dbus
+           which overrides the default,
+           uk.org.thekelleys.dnsmasq. Thanks to Mathieu
+           Trudel-Lapierre for the patch. 
+
+           Set the "prefix on-link" bit in Router
+           Advertisements. Thanks to Gui Iribarren for the patch.
+
+
+version 2.62
+            Update German translation. Thanks to Conrad Kostecki.
+
+           Cope with router-solict packets wich don't have a valid 
+           source address. Thanks to Vladislav Grishenko for the patch.
+
+           Fixed bug which caused missing periodic router
+           advertisements with some configurations. Thanks to
+           Vladislav Grishenko for the patch.
+
+           Fixed bug which broke DHCPv6/RA with prefix lengths 
+           which are not divisible by 8. Thanks to Andre Coetzee 
+           for spotting this.
+
+           Fix non-response to router-solicitations when
+           router-advertisement configured, but DHCPv6 not
+           configured. Thanks to Marien Zwart for the patch.
+
+           Add --dns-rr, to allow arbitrary DNS resource records.
+
+           Fixed bug which broke RA scheduling when an interface had
+           two addresses in the same network. Thanks to Jim Bos for
+           his help nailing this.
+
+version 2.61
+           Re-write interface discovery code on *BSD to use
+           getifaddrs. This is more portable, more straightforward,
+           and allows us to find the prefix length for IPv6
+           addresses.
+
+           Add ra-names, ra-stateless and slaac keywords for DHCPv6.
+           Dnsmasq can now synthesise AAAA records for dual-stack 
+            hosts which get IPv6 addresses via SLAAC. It is also now 
+           possible to use SLAAC and stateless DHCPv6, and to 
+           tell clients to use SLAAC addresses as well as DHCP ones.
+           Thanks to Dave Taht for help with this.
+
+           Add --dhcp-duid to allow DUID-EN uids to be used.
+
+           Explicity send DHCPv6 replies to the correct port, instead
+           of relying on clients to send requests with the correct
+           source address, since at least one client in the wild gets
+           this wrong. Thanks to Conrad Kostecki for help tracking
+           this down.
+
+           Send a preference value of 255 in DHCPv6 replies when 
+           --dhcp-authoritative is in effect. This tells clients not
+           to wait around for other DHCP servers.
+
+           Better logging of DHCPv6 options.
+
+           Add --host-record. Thanks to Rob Zwissler for the
+           suggestion.
+
+           Invoke the DHCP script with action "tftp" when a TFTP file
+           transfer completes. The size of the file, address to which
+           it was sent and complete pathname are supplied. Note that
+           version 2.60 introduced some script incompatibilties
+           associated with DHCPv6, and this is a further change. To
+           be safe, scripts should ignore unknown actions, and if
+           not IPv6-aware, should exit if the environment
+           variable DNSMASQ_IAID is set. The use-case for this is
+           to track netboot/install.  Suggestion from Shantanu
+           Gadgil.
+
+           Update contrib/port-forward/dnsmasq-portforward to reflect
+           the above.
+
+           Set the environment variable DNSMASQ_LOG_DHCP when running
+           the script id --log-dhcp is in effect, so that script can
+           taylor their logging verbosity. Suggestion from Malte
+           Forkel.
+           
+           Arrange that addresses specified with --listen-address
+           work even if there is no interface carrying the
+           address. This is chiefly useful for IPv4 loopback
+           addresses, where any address in 127.0.0.0/8 is a valid
+           loopback address, but normally only 127.0.0.1 appears on
+           the lo interface. Thanks to Mathieu Trudel-Lapierre for
+           the idea and initial patch. 
+
+           Fix crash, introduced in 2.60, when a DHCPINFORM is
+           received from a network which has no valid dhcp-range.
+           Thanks to Stephane Glondu for the bug report.
+
+           Add a new DHCP lease time keyword, "deprecated" for
+           --dhcp-range. This is only valid for IPv6, and sets the
+           preffered lease time for both DHCP and RA to zero. The
+           effect is that clients can continue to use the address 
+           for existing connections, but new connections will use
+            other addresses, if they exist. This makes hitless
+           renumbering at least possible.
+
+           Fix bug in address6_available() which caused DHCPv6 lease
+           aquisition to fail if more than one dhcp-range in use.
+
+           Provide RDNSS and DNSSL data in router advertisements,
+           using the settings provided for DHCP options
+           option6:domain-search and option6:dns-server.
+
+           Tweak logo/favicon.ico to add some transparency. Thanks to
+           SamLT for work on this.
+           
+           Don't cache data from non-recursive nameservers, since it
+           may erroneously look like a valid CNAME to a non-exitant
+           name. Thanks to Ben Winslow for finding this.
+
+           Call SO_BINDTODEVICE on the DHCP socket(s) when doing DHCP
+           on exactly one interface and --bind-interfaces is set. This 
+           makes the OpenStack use-case of one dnsmasq per virtual
+           interface work. This is only available on Linux; it's not
+           supported on other platforms. Thanks to Vishvananda Ishaya
+           and the OpenStack team for the suggestion.
+
+           Updated French translation. Thanks to Gildas Le Nadan.
+
+           Give correct from-cache answers to explict CNAME queries.
+           Thanks to Rob Zwissler for spotting this.
+           
+           Add --tftp-lowercase option. Thanks to Oliver Rath for the
+           patch. 
+
+           Ensure that the DBus DhcpLeaseUpdated events are generated
+           when a lease goes through INIT_REBOOT state, even if the
+           dhcp-script is not in use. Thanks to Antoaneta-Ecaterina
+           Ene for the patch.
+
+           Fix failure of TFTP over IPv4 on OpenBSD platform. Thanks
+           to Brad Smith for spotting this.
+           
+
+version 2.60
+            Fix compilation problem in Mac OS X Lion. Thanks to Olaf
+            Flebbe for the patch.
+
+           Fix DHCP when using --listen-address with an IP address
+           which is not the primary address of an interface.
+
+           Add --dhcp-client-update option.
+
+           Add Lua integration. Dnsmasq can now execute a DHCP
+           lease-change script written in Lua. This needs to be
+           enabled at compile time by setting HAVE_LUASCRIPT in 
+           src/config.h or running "make COPTS=-DHAVE_LUASCRIPT"
+           Thanks to Jan-Piet Mens for the idea and proof-of-concept 
+           implementation.
+           
+           Tidied src/config.h to distinguish between
+           platform-dependent compile-time options which are selected
+           automatically, and builder-selectable compile time
+           options. Document the latter better, and describe how to
+           set them from the make command line.
+
+           Tidied up IPPROTO_IP/SOL_IP (and IPv6 equivalent)
+           confusion. IPPROTO_IP works everywhere now.
+           
+           Set TOS on DHCP sockets, this improves things on busy
+           wireless networks. Thanks to Dave Taht for the patch.
+
+           Determine VERSION automatically based on git magic:
+           release tags or hash values.
+
+           Improve start-up speed when reading large hosts files 
+           containing many distinct addresses.
+
+           Fix problem if dnsmasq is started without the stdin,
+           stdout and stderr file descriptors open. This can manifest
+           itself as 100% CPU use. Thanks to Chris Moore for finding
+           this.
+
+           Fix shell-scripting bug in bld/pkg-wrapper. Thanks to 
+           Mark Mitchell for the patch.
+
+           Allow the TFP server or boot server in --pxe-service, to
+           be a domain name instead of an IP address. This allows for
+           round-robin to multiple servers, in the same way as
+           --dhcp-boot. A good suggestion from Cristiano Cumer.
+
+           Support BUILDDIR variable in the Makefile. Allows builds 
+           for multiple archs from the same source tree with eg.
+           make BUILDDIR=linux             (relative to dnsmasq tree)
+           make BUILDDIR=/tmp/openbsd      (absolute path)
+           If BUILDDIR is not set, compilation happens in the src
+           directory, as before. Suggestion from Mark Mitchell.
+
+           Support DHCPv6. Support is there for the sort of things
+           the existing v4 server does, including tags, options, 
+           static addresses and relay support. Missing is prefix 
+           delegation, which is probably not required in the dnsmasq
+           niche, and an easy way to accept prefix delegations from
+           an upstream DHCPv6 server, which is. Future plans include
+           support for DHCPv6 router option and MAC address option
+           (to make selecting clients by MAC address work like IPv4).
+           These will be added as the standards mature.
+           This code has been tested, but this is the first release,
+           so don't bet the farm on it just yet. Many thanks to all 
+           testers who have got it this far.
+
+           Support IPv6 router advertisements. This is a
+           simple-minded implementation, aimed at providing the
+           vestigial RA needed to go alongside IPv6. Is picks up
+           configuration from the DHCPv6 conf, and should just need
+           enabling with --enable-ra.   
+
+           Fix long-standing wrinkle with --localise-queries that
+           could result in wrong answers when DNS packets arrive
+           via an interface other than the expected one. Thanks to 
+           Lorenzo Milesi and John Hanks for spotting this one.
+            Update French translation. Thanks to Gildas Le Nadan.
+
+           Update Polish translation. Thanks to Jan Psota.
+
+
+version 2.59
+            Fix regression in 2.58 which caused failure to start up
+            with some combinations of dnsmasq config and IPv6 kernel
+            network config. Thanks to Brielle Bruns for the bug
+            report.
+
+            Improve dnsmasq's behaviour when network interfaces are
+            still doing duplicate address detection (DAD). Previously,
+            dnsmasq would wait up to 20 seconds at start-up for the
+            DAD state to terminate. This is broken for bridge
+            interfaces on recent Linux kernels, which don't start DAD
+            until the bridge comes up, and so can take arbitrary
+            time. The new behaviour lets dnsmasq poll for an arbitrary
+            time whilst providing service on other interfaces. Thanks
+            to Stephen Hemminger for pointing out the problem.
+
+
+version 2.58
+           Provide a definition of the SA_SIZE macro where it's 
+           missing. Fixes build failure on openBSD.
+
+           Don't include a zero terminator at the end of messages
+           sent to /dev/log when /dev/log is a datagram socket.
+           Thanks to Didier Rabound for spotting the problem.
+
+           Add --dhcp-sequential-ip flag, to force allocation of IP
+           addresses in ascending order. Note that the default
+           pseudo-random mode is in general better but some
+           server-deployment applications need this.
+
+           Fix problem where a server-id of 0.0.0.0 is sent to a
+           client when a dhcp-relay is in use if a client renews a
+           lease after dnsmasq restart and before any clients on the
+           subnet get a new lease. Thanks to Mike Ruiz for assistance
+           in chasing this one down. 
+
+           Don't return NXDOMAIN to an AAAA query if we have CNAME
+           which points to an A record only: NODATA is the correct
+           reply in this case. Thanks to Tom Fernandes for spotting
+           the problem.
+
+           Relax the need to supply a netmask in --dhcp-range for
+           networks which use a DHCP relay. Whilst this is still
+           desireable, in the absence of a netmask dnsmasq will use
+           a default based on the class (A, B, or C) of the address. 
+           This should at least remove a cause of mysterious failure 
+           for people using RFC1918 addresses and relays.
+
+           Add support for Linux conntrack connection marking. If 
+           enabled with --conntrack, the connection mark for incoming
+           DNS queries will be copied  to the outgoing connections
+           used to answer those queries. This allows clever firewall
+           and accounting stuff. Only available if dnsmasq is
+           compiled with HAVE_CONNTRACK and adds a dependency on 
+           libnetfilter-conntrack. Thanks to Ed Wildgoose for the
+           initial idea, testing and sponsorship of this function.
+
+           Provide a sane error message when someone attempts to 
+           match a tag in --dhcp-host.
+
+           Tweak the behaviour of --domain-needed, to avoid problems
+           with recursive nameservers downstream of dnsmasq. The new
+           behaviour only stops A and AAAA queries, and returns
+           NODATA rather than NXDOMAIN replies. 
+
+           Efficiency fix for very large DHCP configurations, thanks
+           to James Gartrell and Mike Ruiz for help with this. 
+
+           Allow the TFTP-server address in --dhcp-boot to be a
+           domain-name which is looked up in /etc/hosts. This can 
+           give multiple IP addresses which are used round-robin,
+           thus doing TFTP server load-balancing. Thanks to Sushil
+           Agrawal for the patch.
+
+           When two tagged dhcp-options for a particular option
+           number are both valid, use the one which is valid without
+           a tag from the dhcp-range. Allows overriding of the value
+           of a DHCP option for a particular host as well as
+           per-network values.  So 
+           --dhcp-range=set:interface1,......
+           --dhcp-host=set:myhost,.....  
+           --dhcp-option=tag:interface1,option:nis-domain,"domain1" 
+           --dhcp-option=tag:myhost,option:nis-domain,"domain2" 
+           will set the NIS-domain to domain1 for hosts in the range, but
+                   override that to domain2 for a particular host.
+
+           Fix bug which resulted in truncated files and timeouts for
+           some TFTP transfers. The bug only occurs with netascii
+           transfers and needs an unfortunate relationship between
+           file size, blocksize and the number of newlines in the
+           last block before it manifests itself. Many thanks to 
+           Alkis Georgopoulos for spotting the problem and providing
+           a comprehensive test-case. 
+
+           Fix regression in TFTP server on *BSD platforms introduced
+           in version 2.56, due to confusion with sockaddr
+           length. Many thanks to Loic Pefferkorn for finding this.
+
+           Support scope-ids in IPv6 addresses of nameservers from
+           /etc/resolv.conf and in --server options. Eg
+           nameserver fe80::202:a412:4512:7bbf%eth0 or
+           server=fe80::202:a412:4512:7bbf%eth0. Thanks to 
+           Michael Stapelberg for the suggestion.
+
+           Update Polish translation, thanks to Jan Psota.
+
+           Update French translation. Thanks to Gildas Le Nadan.
+
+
 version 2.57
            Add patches to allow build under Android.
 
index aca7e76..39869a9 100644 (file)
@@ -1,25 +1,40 @@
 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
 PROJECT(dnsmasq C)
 
-SET(SRCS       src/bpf.c
+SET(SRCS       src/auth.c
+               src/blockdata.c
+               src/bpf.c
                src/cache.c
+               src/conntrack.c
                src/dbus.c
+               src/dhcp6.c
                src/dhcp.c
+               src/dhcp-common.c
                src/dnsmasq.c
+               src/dnssec.c
+               src/domain.c
                src/forward.c
                src/helper.c
+               src/inotify.c
+               src/ipset.c
                src/lease.c
                src/log.c
+               src/loop.c
                src/netlink.c
                src/network.c
                src/option.c
+               src/outpacket.c
+               src/poll.c
+               src/radv.c
                src/rfc1035.c
                src/rfc2131.c
+               src/rfc3315.c
+               src/slaac.c
+               src/tables.c
                src/tftp.c
                src/util.c
    )
 
-SET(VENDOR "samsung")
 SET(PACKAGE ${PROJECT_NAME})
 SET(PKGNAME ${PACKAGE})
 SET(PREFIX ${CMAKE_INSTALL_PREFIX})
@@ -40,12 +55,10 @@ FOREACH(flag ${pkgs_CFLAGS})
 ENDFOREACH(flag)
 SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden")
 
-SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE")
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
 SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
 SET(CMAKE_C_FLAGS_RELEASE "-O2")
-SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -pie")
 
-ADD_DEFINITIONS("-DVENDOR=\"${VENDOR}\"")
 ADD_DEFINITIONS("-DPACKAGE=\"${PACKAGE}\"")
 ADD_DEFINITIONS("-DPACKAGE_NAME=\"${PKGNAME}\"")
 ADD_DEFINITIONS("-DPREFIX=\"${PREFIX}\"")
diff --git a/FAQ b/FAQ
index a930516..ec71691 100644 (file)
--- a/FAQ
+++ b/FAQ
@@ -22,7 +22,7 @@ A: The high ports that dnsmasq opens are for replies from the upstream
    now uses a new, randomly selected, port for each query. The old
    default behaviour (use one port allocated by the OS) is available by
    setting --query-port=0, and setting the query port to a positive
-   value is still works. You should think hard and know what you are
+   value still works. You should think hard and know what you are
    doing before using either of these options.
  
 Q: Why doesn't dnsmasq support DNS queries over TCP? Don't the RFC's specify
@@ -112,7 +112,7 @@ A: Resolver code sometime does strange things when given names without
    hostname  will fix things. (ie "ping myhost" fails, but "ping
    myhost." works. The solution is to make sure that all your hosts
    have a domain set ("domain" in resolv.conf, or set a domain in 
-   your DHCP server, see below fr Windows XP and Mac OS X). 
+   your DHCP server, see below for Windows XP and Mac OS X). 
    Any domain  will do, but "localnet" is traditional. Now when you
    resolve "myhost" the resolver will attempt to look up 
    "myhost.localnet" so you need to have dnsmasq reply to that name. 
@@ -236,53 +236,70 @@ Q: What network types are supported by the DHCP server?
 A: Ethernet (and 802.11 wireless) are supported on all platforms. On
    Linux all network types (including FireWire) are supported.
 
-Q: What is this strange "bind-interface" option?
-
-A: The DNS spec says that the reply to a DNS query must come from the
-   same address it was sent to. The traditional way to write an UDP
-   server to do this is to find all of the addresses belonging to the
-   machine (ie all the interfaces on the machine) and then create a
-   socket for each interface which is bound to the address of the
-   interface. Then when a packet is sent to address A, it is received
-   on the socket bound to address A and when the reply is also sent
-   via that socket, the source address is set to A by the kernel and
-   everything works. This is the how dnsmasq works when
-   "bind-interfaces" is set, with the obvious extension that is misses
-   out creating sockets for some interfaces depending on the
-   --interface, --address and --except-interface flags.  The
-   disadvantage of this approach is that it breaks if interfaces don't
-   exist or are not configured when the daemon starts and does the
-   socket creation step. In a hotplug-aware world this is a real
-   problem.
-
-   The alternative approach is to have only one socket, which is bound
-   to the correct port and the wildcard IP address (0.0.0.0). That
-   socket will receive _all_ packets sent to port 53, no matter what
-   destination address they have. This solves the problem of
-   interfaces which are created or reconfigured after daemon
-   start-up. To make this work is more complicated because of the
-   "reply source address" problem. When a UDP packet is sent by a
-   socket bound to 0.0.0.0 its source address will be set to the
-   address of one of the machine's interfaces, but which one is not
-   determined and can vary depending on the OS being run. To get round
-   this it is neccessary to use a scary advanced API to determine the
-   address to which a query was sent, and force that to be the source
-   address in the reply. For IPv4 this stuff in non-portable and quite
-   often not even available (It's different between FreeBSD 5.x and
-   Linux, for instance, and FreeBSD 4.x, Linux 2.0.x and OpenBSD don't
-   have it at all.) Hence "bind-interfaces" has to always be available
-   as a fall back. For IPv6 the API is standard and universally
-   available.
-
-   It could be argued that if the --interface or --address flags are
-   used then binding interfaces is more appropriate, but using
-   wildcard binding means that dnsmasq will quite happily start up
-   after being told to use interfaces which don't exist, but which are
-   created later. Wildcard binding breaks the scenario when dnsmasq is
-   listening on one interface and another server (most probably BIND)
-   is listening on another. It's not possible for BIND to bind to an
-   (address,port) pair when dnsmasq has bound (wildcard,port), hence
-   the ability to explicitly turn off wildcard binding.
+Q: What are these strange "bind-interface" and "bind-dynamic" options?
+
+A: Dnsmasq from v2.63 can operate in one of three different "networking
+   modes". This is unfortunate as it requires users configuring dnsmasq
+   to take into account some rather bizzare contraints and select the
+   mode which best fits the requirements of a particular installation.
+   The origin of these are deficiencies in the Unix networking
+   model and APIs and each mode has different advantages and
+   problems. Just to add to the confusion, not all modes are available on
+   all platforms (due the to lack of supporting network APIs).To further
+   add to the confusion, the rules for the DHCP subsystem on dnsmasq are
+   different to the rules for the DNS and TFTP subsystems.
+
+   The three modes are "wildcard", "bind-interfaces" and "bind-dynamic".
+
+   In "wildcard" mode, dnsmasq binds the wildcard IP address (0.0.0.0 or
+   ::). This allows it to recieve all the packets sent to the server on
+   the relevant port. Access control (--interface, --except-interface,
+   --listen-address, etc) is implemented by dnsmasq: it queries the
+   kernel to determine the interface on which a packet was recieved and
+   the address to which it was sent, and applies the configured
+   rules. Wildcard mode is the default if neither of the other modes are
+   specified. 
+
+   In "bind-interfaces" mode, dnsmasq runs through all the network
+   interfaces available when it starts, finds the set of IP addresses on
+   those interfaces, filters that set using the access control
+   configuration, and then binds the set of IP addresses. Only packets
+   sent to the allowed addresses are delivered by the kernel to dnsmasq.
+
+   In "bind-dynamic" mode, access control filtering is done both by
+   binding individual IP addresses, as for bind-interfaces, and by
+   inspecting individual packets on arrival as for wildcard mode. In
+   addition, dnsmasq notices when new interfaces appear or new addresses
+   appear on existing interfaces, and the resulting IP addresses are
+   bound automatically without having to restart dnsmasq.
+
+   The mode chosen has four different effects: co-existence with other
+   servers, semantics of --interface access control, effect of new
+   interfaces, and legality of --interface specifications for
+   non-existent inferfaces. We will deal with these in order.
+
+   A dnsmasq instance running in wildcard mode precludes a machine from
+   running a second instance of dnsmasq or any other DNS, TFTP or DHCP
+   server. Attempts to do so will fail with an "address in use" error. 
+   Dnsmasq running in --bind-interfaces or bind-dynamic mode allow other
+   instances of dnsmasq or other servers, as long as no two servers are
+   configured to listen on the same interface address. 
+
+   The semantics of --interface varies subtly between wildcard or
+   bind-dynamic mode and bind-interfaces mode. The situation where this
+   matters is a request which arrives via one interface (A), but with a
+   destination address of a second interface (B) and when dnsmasq is
+   configured to listen only on B. In wildcard or bind-dynamic mode, such
+   a request will be ignored, in bind-interfaces mode, it will be
+   accepted.
+
+   The creation of new network interfaces after dnsmasq starts is ignored
+   by dnsmasq when in --bind-interfaces mode. In wildcard or bind-dynamic
+   mode, such interfaces are handled normally.
+
+   A --interface specification for a non-existent interface is a fatal
+   error at start-up when in --bind-interfaces mode, by just generates a
+   warning in wildcard or bind-dynamic mode.
 
 Q: Why doesn't Kerberos work/why can't I get sensible answers to
    queries for SRV records.
@@ -303,7 +320,7 @@ A: Yes, new releases of dnsmasq are always announced through
 
 Q: What does the dhcp-authoritative option do? 
 
-A: See http://www.isc.org/index.pl?/sw/dhcp/authoritative.php - that's
+A: See http://www.isc.org/files/auth.html - that's
    for the ISC daemon, but the same applies to dnsmasq.
 
 Q: Why does my Gentoo box pause for a minute before getting a new
@@ -381,7 +398,7 @@ A: Probably the nameserver is an authoritative nameserver for a
 Q: Does the dnsmasq DHCP server probe addresses before allocating
    them, as recommended in RFC2131?
 
-A: Yes, dynmaically allocated IP addresses are checked by sending an
+A: Yes, dynamically allocated IP addresses are checked by sending an
    ICMP echo request (ping). If a reply is received, then dnsmasq
    assumes that the address is in use, and attempts to allocate an
    different address. The wait for a reply is between two and three
@@ -467,8 +484,18 @@ A: The DHCP client on windows Vista (and possibly later versions)
    work).
 
   
-
-
+Q: DHCP doesn't work with windows 7 but everything else is fine.
+
+A: There seems to be a problem if Windows 7 doesn't get a value for
+   DHCP option 252 in DHCP packets it gets from the server. The
+   symtoms have beeen variously reported as continual DHCPINFORM
+   requests in an attempt to get an option-252, or even ignoring DHCP
+   offers completely (and failing to get an IP address) if there is no
+   option-252 supplied. DHCP option 252 is for WPAD, WWW Proxy 
+   Auto Detection and if you don't want or need to use that, then 
+   simplest fix seems to be to supply an empty option with:
+
+   dhcp-option=252,"\n"
 
 
 
index 16c69e4..4c87ea9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+# 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
 #  You should have received a copy of the GNU General Public License
 #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-PREFIX = /usr/local
-BINDIR = ${PREFIX}/sbin
-MANDIR = ${PREFIX}/share/man
-LOCALEDIR = ${PREFIX}/share/locale
+# NOTE: Building the i18n targets requires GNU-make 
 
-PKG_CONFIG = pkg-config
-INSTALL = install
-MSGMERGE = msgmerge
-MSGFMT = msgfmt
-XGETTEXT = xgettext
 
-CFLAGS = -Wall -W -O2
+# Variables you may well want to override.
+
+PREFIX        = /usr/local
+BINDIR        = $(PREFIX)/sbin
+MANDIR        = $(PREFIX)/share/man
+LOCALEDIR     = $(PREFIX)/share/locale
+BUILDDIR      = $(SRC)
+DESTDIR       = 
+CFLAGS        = -Wall -W -O2
+LDFLAGS       = 
+COPTS         = 
+RPM_OPT_FLAGS = 
+LIBS          = 
 
 #################################################################
 
+# Variables you might want to override.
+
+PKG_CONFIG = pkg-config
+INSTALL    = install
+MSGMERGE   = msgmerge
+MSGFMT     = msgfmt
+XGETTEXT   = xgettext
+
 SRC = src
-PO = po
+PO  = po
 MAN = man
 
-DNSMASQ_CFLAGS=`echo $(COPTS) | ../bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1` 
-DNSMASQ_LIBS=  `echo $(COPTS) | ../bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1` 
-IDN_CFLAGS=`echo $(COPTS) | ../bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn` 
-IDN_LIBS=  `echo $(COPTS) | ../bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn` 
-SUNOS_LIBS= `if uname | grep SunOS 2>&1 >/dev/null; then echo -lsocket -lnsl -lposix4; fi`
+#################################################################
 
-OBJS = cache.o rfc1035.o util.o option.o forward.o network.o \
+# pmake way. (NB no spaces to keep gmake 3.82 happy)
+top!=pwd
+# GNU make way.
+top?=$(CURDIR)
+
+dbus_cflags =   `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1` 
+dbus_libs =     `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1` 
+idn_cflags =    `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn` 
+idn_libs =      `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn` 
+ct_cflags =     `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --cflags libnetfilter_conntrack`
+ct_libs =       `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack`
+lua_cflags =    `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.1` 
+lua_libs =      `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.1` 
+nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags nettle hogweed`
+nettle_libs =   `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs nettle hogweed`
+gmp_libs =      `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --copy -lgmp`
+sunos_libs =    `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
+version =     -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
+
+sum?=$(shell $(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ')
+sum!=$(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' '
+copts_conf = .copts_$(sum)
+
+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
+       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 poll.o
+
+hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
+       dns-protocol.h radv-protocol.h ip6addr.h
+
+all : $(BUILDDIR)
+       @cd $(BUILDDIR) && $(MAKE) \
+ top="$(top)" \
+ build_cflags="$(version) $(dbus_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
+ build_libs="$(dbus_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs)" \
+ -f $(top)/Makefile dnsmasq 
 
-all :
-       @cd $(SRC) && $(MAKE) \
- BUILD_CFLAGS="$(DNSMASQ_CFLAGS) $(IDN_CFLAGS)" \
- BUILD_LIBS="$(DNSMASQ_LIBS) $(IDN_LIBS) $(SUNOS_LIBS)" \
- -f ../Makefile dnsmasq 
+mostly_clean :
+       rm -f $(BUILDDIR)/*.mo $(BUILDDIR)/*.pot 
+       rm -f $(BUILDDIR)/.copts_* $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq
 
-clean :
-       rm -f *~ $(SRC)/*.mo contrib/*/*~ */*~ $(SRC)/*.pot 
-       rm -f $(SRC)/*.o $(SRC)/dnsmasq.a $(SRC)/dnsmasq core */core
+clean : mostly_clean
+       rm -f $(BUILDDIR)/dnsmasq_baseline
+       rm -f core */core
+       rm -f *~ contrib/*/*~ */*~
 
 install : all install-common
 
 install-common :
        $(INSTALL) -d $(DESTDIR)$(BINDIR) -d $(DESTDIR)$(MANDIR)/man8
        $(INSTALL) -m 644 $(MAN)/dnsmasq.8 $(DESTDIR)$(MANDIR)/man8 
-       $(INSTALL) -m 755 $(SRC)/dnsmasq $(DESTDIR)$(BINDIR)
-
-all-i18n :
-       @cd $(SRC) && $(MAKE) \
I18N=-DLOCALEDIR='\"$(LOCALEDIR)\"' \
BUILD_CFLAGS="$(DNSMASQ_CFLAGS) `$(PKG_CONFIG) --cflags libidn`" \
BUILD_LIBS="$(DNSMASQ_LIBS) $(SUNOS_LIBS) `$(PKG_CONFIG) --libs libidn`"  \
- -f ../Makefile dnsmasq
-       @cd $(PO); for f in *.po; do \
-               cd ../$(SRC) && $(MAKE) \
-f ../Makefile $${f%.po}.mo; \
+       $(INSTALL) -m 755 $(BUILDDIR)/dnsmasq $(DESTDIR)$(BINDIR)
+
+all-i18n : $(BUILDDIR)
+       @cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \
i18n=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \
build_cflags="$(version) $(dbus_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags) `$(PKG_CONFIG) --cflags libidn`" \
+ build_libs="$(dbus_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) `$(PKG_CONFIG) --libs libidn`"  \
+ -f $(top)/Makefile dnsmasq
+       for f in `cd $(PO); echo *.po`; do \
              cd $(top) && cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/Makefile $${f%.po}.mo; \
        done
 
 install-i18n : all-i18n install-common
-       cd $(SRC); ../bld/install-mo $(DESTDIR)$(LOCALEDIR) $(INSTALL)
+       cd $(BUILDDIR); $(top)/bld/install-mo $(DESTDIR)$(LOCALEDIR) $(INSTALL)
        cd $(MAN); ../bld/install-man $(DESTDIR)$(MANDIR) $(INSTALL)
 
-merge :
-       @cd $(SRC) && $(MAKE) -f ../Makefile dnsmasq.pot
-       @cd $(PO); for f in *.po; do \
-               echo -n msgmerge $$f && $(MSGMERGE) --no-wrap -U $$f ../$(SRC)/dnsmasq.pot; \
+merge : 
+       @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
 
+# Cannonicalise .po file.
+%.po : 
+       @cd $(BUILDDIR) && $(MAKE) -f $(top)/Makefile dnsmasq.pot
+       mv $(PO)/$*.po $(PO)/$*.po.orig && $(MSGMERGE) --no-wrap $(PO)/$*.po.orig $(BUILDDIR)/dnsmasq.pot >$(PO)/$*.po; 
 
-# rules below are targets in recusive makes with cwd=$(SRC)
+$(BUILDDIR):
+       mkdir -p $(BUILDDIR)
 
-.c.o:
-       $(CC) $(CFLAGS) $(COPTS) $(I18N) $(BUILD_CFLAGS) $(RPM_OPT_FLAGS) -c $<
+# rules below are helpers for size tracking
+
+baseline : mostly_clean all
+       @cd $(BUILDDIR) && \
+          mv dnsmasq dnsmasq_baseline
+
+bloatcheck : $(BUILDDIR)/dnsmasq_baseline mostly_clean all
+       @cd $(BUILDDIR) && \
+           $(top)/bld/bloat-o-meter dnsmasq_baseline dnsmasq; \
+           size dnsmasq_baseline dnsmasq
+
+# rules below are targets in recusive makes with cwd=$(BUILDDIR)
 
-dnsmasq : $(OBJS)
-       $(CC) $(LDFLAGS) -o $@ $(OBJS) $(BUILD_LIBS) $(LIBS) 
+$(copts_conf): $(hdrs)
+       @rm -f *.o .copts_*
+       @touch $@
+
+$(objs:.o=.c) $(hdrs):
+       ln -s $(top)/$(SRC)/$@ .
+
+$(objs): $(copts_conf) $(hdrs)
+
+.c.o:
+       $(CC) $(CFLAGS) $(COPTS) $(i18n) $(build_cflags) $(RPM_OPT_FLAGS) -c $< 
 
-dnsmasq.pot : $(OBJS:.o=.c) dnsmasq.h config.h
-       $(XGETTEXT) -d dnsmasq --foreign-user --omit-header --keyword=_ -o $@ -i $(OBJS:.o=.c)
+dnsmasq : $(objs)
+       $(CC) $(LDFLAGS) -o $@ $(objs) $(build_libs) $(LIBS) 
 
-%.mo : ../po/%.po dnsmasq.pot
-       $(MSGMERGE) -o - ../po/$*.po dnsmasq.pot | $(MSGFMT) -o $*.mo -
+dnsmasq.pot : $(objs:.o=.c) $(hdrs)
+       $(XGETTEXT) -d dnsmasq --foreign-user --omit-header --keyword=_ -o $@ -i $(objs:.o=.c)
 
+%.mo : $(top)/$(PO)/%.po dnsmasq.pot
+       $(MSGMERGE) -o - $(top)/$(PO)/$*.po dnsmasq.pot | $(MSGFMT) -o $*.mo -
 
-.PHONY : all clean install install-common all-i18n install-i18n merge 
+.PHONY : all clean mostly_clean install install-common all-i18n install-i18n merge baseline bloatcheck
diff --git a/VERSION b/VERSION
new file mode 100644 (file)
index 0000000..4b5d0ac
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+ (HEAD, tag: v2.74, origin/master, origin/HEAD, master)
index 373a783..5364ee7 100644 (file)
@@ -6,7 +6,11 @@ include $(CLEAR_VARS)
 LOCAL_SRC_FILES :=  bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
                     forward.c helper.c lease.c log.c \
                     netlink.c network.c option.c rfc1035.c \
-                   rfc2131.c tftp.c util.c
+                   rfc2131.c tftp.c util.c conntrack.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 poll.c
 
 LOCAL_MODULE := dnsmasq
 
@@ -15,4 +19,6 @@ LOCAL_C_INCLUDES := external/dnsmasq/src
 LOCAL_CFLAGS := -O2 -g -W -Wall -D__ANDROID__ -DNO_IPV6 -DNO_TFTP -DNO_SCRIPT
 LOCAL_SYSTEM_SHARED_LIBRARIES := libc libcutils
 
+LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
+
 include $(BUILD_EXECUTABLE)
diff --git a/bld/bloat-o-meter b/bld/bloat-o-meter
new file mode 100755 (executable)
index 0000000..6db2a5e
--- /dev/null
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+#
+# Copyright 2004 Matt Mackall <mpm@selenic.com>
+#
+# Inspired by perl Bloat-O-Meter (c) 1997 by Andi Kleen
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+import sys, os#, re
+
+def usage():
+    sys.stderr.write("usage: %s [-t] file1 file2\n" % sys.argv[0])
+    sys.exit(-1)
+
+f1, f2 = (None, None)
+flag_timing, dashes = (False, False)
+
+for f in sys.argv[1:]:
+    if f.startswith("-"):
+        if f == "--": # sym_args
+            dashes = True
+            break
+        if f == "-t": # timings
+            flag_timing = True
+    else:
+        if not os.path.exists(f):
+            sys.stderr.write("Error: file '%s' does not exist\n" % f)
+            usage()
+        if f1 is None:
+            f1 = f
+        elif f2 is None:
+            f2 = f
+if flag_timing:
+    import time
+if f1 is None or f2 is None:
+    usage()
+
+sym_args = " ".join(sys.argv[3 + flag_timing + dashes:])
+def getsizes(file):
+    sym, alias, lut = {}, {}, {}
+    for l in os.popen("readelf -W -s %s %s" % (sym_args, file)).readlines():
+        l = l.strip()
+        if not (len(l) and l[0].isdigit() and len(l.split()) == 8):
+            continue
+        num, value, size, typ, bind, vis, ndx, name = l.split()
+        if ndx == "UND": continue # skip undefined
+        if typ in ["SECTION", "FILES"]: continue # skip sections and files
+        if "." in name: name = "static." + name.split(".")[0]
+        value = int(value, 16)
+        size = int(size, 16) if size.startswith('0x') else int(size)
+        if vis != "DEFAULT" and bind != "GLOBAL": # see if it is an alias
+            alias[(value, size)] = {"name" : name}
+        else:
+            sym[name] = {"addr" : value, "size":  size}
+            lut[(value, size)] = 0
+    for addr, sz in iter(alias.keys()):
+        # If the non-GLOBAL sym has an implementation elsewhere then
+        # it's an alias, disregard it.
+        if not (addr, sz) in lut:
+            # If this non-GLOBAL sym does not have an implementation at
+            # another address, then treat it as a normal symbol.
+            sym[alias[(addr, sz)]["name"]] = {"addr" : addr, "size": sz}
+    for l in os.popen("readelf -W -S " + file).readlines():
+        x = l.split()
+        if len(x)<6: continue
+        # Should take these into account too!
+        #if x[1] not in [".text", ".rodata", ".symtab", ".strtab"]: continue
+        if x[1] not in [".rodata"]: continue
+        sym[x[1]] = {"addr" : int(x[3], 16), "size" : int(x[5], 16)}
+    return sym
+
+if flag_timing:
+    start_t1 = int(time.time() * 1e9)
+old = getsizes(f1)
+if flag_timing:
+    end_t1 = int(time.time() * 1e9)
+    start_t2 = int(time.time() * 1e9)
+new = getsizes(f2)
+if flag_timing:
+    end_t2 = int(time.time() * 1e9)
+    start_t3 = int(time.time() * 1e9)
+grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0
+delta, common = [], {}
+
+for name in iter(old.keys()):
+    if name in new:
+        common[name] = 1
+
+for name in old:
+    if name not in common:
+        remove += 1
+        sz = old[name]["size"]
+        down += sz
+        delta.append((-sz, name))
+
+for name in new:
+    if name not in common:
+        add += 1
+        sz = new[name]["size"]
+        up += sz
+        delta.append((sz, name))
+
+for name in common:
+        d = new[name].get("size", 0) - old[name].get("size", 0)
+        if d>0: grow, up = grow+1, up+d
+        elif d<0: shrink, down = shrink+1, down-d
+        else:
+            continue
+        delta.append((d, name))
+
+delta.sort()
+delta.reverse()
+if flag_timing:
+    end_t3 = int(time.time() * 1e9)
+
+print("%-48s %7s %7s %+7s" % ("function", "old", "new", "delta"))
+for d, n in delta:
+    if d:
+        old_sz = old.get(n, {}).get("size", "-")
+        new_sz = new.get(n, {}).get("size", "-")
+        print("%-48s %7s %7s %+7d" % (n, old_sz, new_sz, d))
+print("-"*78)
+total="(add/remove: %s/%s grow/shrink: %s/%s up/down: %s/%s)%%sTotal: %s bytes"\
+    % (add, remove, grow, shrink, up, -down, up-down)
+print(total % (" "*(80-len(total))))
+if flag_timing:
+    print("\n%d/%d; %d Parse origin/new; processing nsecs" %
+        (end_t1-start_t1, end_t2-start_t2, end_t3-start_t3))
+    print("total nsecs: %d" % (end_t3-start_t1))
diff --git a/bld/get-version b/bld/get-version
new file mode 100755 (executable)
index 0000000..5372869
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Determine the version string to build into a binary.
+# When building in the git repository, we can use the output 
+# of "git describe" which gives an unequivocal answer.
+#
+# Failing that, we use the contents of the VERSION file
+# which has a set of references substituted into it by git.
+# If we can find one which matches $v[0-9].* then we assume it's
+# a version-number tag, else we just use the whole string.
+# If there is more than one v[0-9].* tag, sort them and use the
+# first. This favours, eg v2.63 over 2.63rc6.
+
+if which git >/dev/null 2>&1 && \
+    ([ -d $1/.git ] || grep '^gitdir:' $1/.git >/dev/null 2>&1); then 
+    cd $1; git describe | sed 's/^v//'
+elif grep '\$Format:%d\$' $1/VERSION >/dev/null 2>&1; then
+# unsubstituted VERSION, but no git available.
+    echo UNKNOWN
+else
+     vers=`cat $1/VERSION | sed 's/[(), ]/,/ g' | tr ',' '\n' | grep ^v[0-9]`
+
+     if [ $? -eq 0 ]; then
+         echo "${vers}" | sort -r | head -n 1 | sed 's/^v//'
+     else
+         cat $1/VERSION
+     fi
+fi
+
+exit 0
+
index f4cf3dc..420c9b1 100755 (executable)
@@ -4,6 +4,6 @@ for f in *; do
   if [ -d $f ]; then
      $2 -m 755 -d $1/$f/man8 
      $2 -m 644 $f/dnsmasq.8 $1/$f/man8
-     echo installing $1/$f/man8/dnsmasq.8
+     echo installing $f/man8/dnsmasq.8
   fi
 done
index d11fa9f..ab54301 100755 (executable)
@@ -3,7 +3,7 @@
 for f in *.mo; do
   $2 -m 755 -d $1/${f%.mo}/LC_MESSAGES
   $2 -m 644 $f $1/${f%.mo}/LC_MESSAGES/dnsmasq.mo
-  echo installing $1/${f%.mo}/LC_MESSAGES/dnsmasq.mo
+  echo installing ${f%.mo}/LC_MESSAGES/dnsmasq.mo
 done
 
 
index 4f3b76b..0ddb678 100755 (executable)
@@ -2,10 +2,39 @@
 
 search=$1
 shift
+pkg=$1
+shift
+op=$1
+shift
 
-if grep "^\#.*define.*$search" config.h 2>&1 >/dev/null || \
-   grep $search 2>&1 >/dev/null ; then
-  exec $*
-fi
+in=`cat`
 
+if grep "^\#[[:space:]]*define[[:space:]]*$search" config.h >/dev/null 2>&1 || \
+    echo $in | grep $search >/dev/null 2>&1; then
+# Nasty, nasty, in --copy, arg 2 is another config to search for, use with NO_GMP
+    if [ $op = "--copy" ]; then
+       if grep "^\#[[:space:]]*define[[:space:]]*$pkg" config.h >/dev/null 2>&1 || \
+            echo $in | grep $pkg >/dev/null 2>&1; then
+           pkg=""
+       else 
+           pkg="$*"
+       fi
+    elif grep "^\#[[:space:]]*define[[:space:]]*${search}_STATIC" config.h >/dev/null 2>&1 || \
+             echo $in | grep ${search}_STATIC >/dev/null 2>&1; then
+       pkg=`$pkg  --static $op $*`
+    else
+       pkg=`$pkg $op $*`
+    fi
+
+    if grep "^\#[[:space:]]*define[[:space:]]*${search}_STATIC" config.h >/dev/null 2>&1 || \
+       echo $in | grep ${search}_STATIC >/dev/null 2>&1; then
+       if [ $op = "--libs" ] || [ $op = "--copy" ]; then
+           echo "-Wl,-Bstatic $pkg -Wl,-Bdynamic"
+       else
+           echo "$pkg" 
+       fi
+    else
+       echo "$pkg"
+    fi
+fi
 
diff --git a/contrib/conntrack/README b/contrib/conntrack/README
new file mode 100644 (file)
index 0000000..c0d17aa
--- /dev/null
@@ -0,0 +1,54 @@
+Linux iptables includes that ability to mark individual network packets
+with a "firewall mark".  Additionally there is a component called
+"conntrack" which tries to string sequences of related packets together
+into a "connection" (it even relates sequences of UDP and ICMP packets).
+ There is a related mark for a connection called a "connection mark".
+Marks can be copied freely between the firewall and connection marks
+
+Using these two features it become possible to tag all related traffic
+in arbitrary ways, eg authenticated users, traffic from a particular IP,
+port, etc. Unfortunately any kind of "proxy" breaks this relationship
+because network packets go in one side of the proxy and a completely new
+connection comes out of the other side.  However, sometimes, we want to
+maintain that relationship through the proxy and continue the connection
+mark on packets upstream of our proxy
+
+DNSMasq includes such a feature enabled by the --conntrack
+option. This allows, for example, using iptables to mark traffic from
+a particular IP, and that mark to be persisted to requests made *by*
+DNSMasq. Such a feature could be useful for bandwidth accounting,
+captive portals and the like. Note a similar feature has been 
+implemented in Squid 2.2
+
+
+As an example consider the following iptables rules:
+
+
+1) iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
+2) iptables -t mangle -A PREROUTING -m mark --mark 0 -s 192.168.111.137
+-j MARK --set-mark 137
+3) iptables -t mangle -A PREROUTING -j CONNMARK --save-mark
+
+4) iptables -t mangle -A OUTPUT -m mark ! --mark 0 -j CONNMARK --save-mark
+
+1-3) are all applied to the PREROUTING table and affect all packets
+entering the firewall.
+
+1) copies any existing connection mark into the firewall mark. 2) Checks
+the packet not already marked and if not applies an arbitrary mark based
+on IP address. 3) Saves the firewall mark back to the connection mark
+(which will persist it across related packets)
+
+4) is applied to the OUTPUT table, which is where we first see packets
+generated locally. DNSMasq will have already copied the firewall mark
+from the request, across to the new packet, and so all that remains is
+for iptables to copy it to the connection mark so it's persisted across
+packets.
+
+Note: iptables can be quite confusing to the beginner. The following
+diagram is extremely helpful in understanding the flows
+       http://linux-ip.net/nf/nfk-traversal.png
+Additionally the following URL contains a useful "starting guide" on
+linux connection tracking/marking
+       http://home.regit.org/netfilter-en/netfilter-connmark/
+
diff --git a/contrib/dbus-test/dbus-test.py b/contrib/dbus-test/dbus-test.py
new file mode 100755 (executable)
index 0000000..25d8881
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+import dbus
+
+bus = dbus.SystemBus()
+p = bus.get_object("uk.org.thekelleys.dnsmasq", "/uk/org/thekelleys/dnsmasq")
+l = dbus.Interface(p, dbus_interface="uk.org.thekelleys.dnsmasq")
+
+# The new more flexible SetServersEx method
+array = dbus.Array()
+array.append(["1.2.3.5"])
+array.append(["1.2.3.4#664", "foobar.com"])
+array.append(["1003:1234:abcd::1%eth0", "eng.mycorp.com", "lab.mycorp.com"])
+print l.SetServersEx(array)
+
+# Must create a new object for dnsmasq as the introspection gives the wrong
+# signature for SetServers (av) while the code only expects a bunch of arguments
+# instead of an array of variants
+p = bus.get_object("uk.org.thekelleys.dnsmasq", "/uk/org/thekelleys/dnsmasq", introspect=False)
+l = dbus.Interface(p, dbus_interface="uk.org.thekelleys.dnsmasq")
+
+# The previous method; all addresses in machine byte order
+print l.SetServers(dbus.UInt32(16909060), # 1.2.3.5
+                   dbus.UInt32(16909061), # 1.2.3.4
+                   "foobar.com",
+                   dbus.Byte(0x10),       # 1003:1234:abcd::1
+                   dbus.Byte(0x03),
+                   dbus.Byte(0x12),
+                   dbus.Byte(0x34),
+                   dbus.Byte(0xab),
+                   dbus.Byte(0xcd),
+                   dbus.Byte(0x00),
+                   dbus.Byte(0x00),
+                   dbus.Byte(0x00),
+                   dbus.Byte(0x00),
+                   dbus.Byte(0x00),
+                   dbus.Byte(0x00),
+                   dbus.Byte(0x00),
+                   dbus.Byte(0x00),
+                   dbus.Byte(0x00),
+                   dbus.Byte(0x01),
+                   "eng.mycorp.com",
+                   "lab.mycorp.com")
+
diff --git a/contrib/mactable/macscript b/contrib/mactable/macscript
new file mode 100755 (executable)
index 0000000..44a4477
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+STATUS_FILE="/tmp/dnsmasq-ip-mac.status"
+
+# Script for dnsmasq lease-change hook.
+# Maintains the above file with a IP address/MAC address pairs,
+# one lease per line. Works with IPv4 and IPv6 leases, file is
+# atomically updated, so no races for users of the data.
+
+action="$1"
+mac="$2"   # IPv4
+ip="$3"
+
+# ensure it always exists.
+
+if [ ! -f "$STATUS_FILE" ]; then
+  touch "$STATUS_FILE"
+fi
+
+if [  -n "$DNSMASQ_IAID" ]; then
+    mac="$DNSMASQ_MAC"   # IPv6
+fi
+
+# worry about an add or old action when the MAC address is not known:
+# leave any old one in place in that case.
+
+if [ "$action" = "add" -o "$action" = "old" -o "$action" = "del" ]; then
+  if [ -n "$mac" -o "$action" = "del" ]; then
+    sed "/^${ip//./\.} / d" "$STATUS_FILE" > "$STATUS_FILE".new
+  
+    if [ "$action" = "add" -o "$action" = "old" ]; then
+       echo "$ip $mac" >> "$STATUS_FILE".new
+    fi
+    mv  "$STATUS_FILE".new "$STATUS_FILE" # atomic update.
+  fi
+fi
index f9bb857..c2c634f 100755 (executable)
@@ -34,11 +34,21 @@ if [ ${DNSMASQ_OLD_HOSTNAME} ] && [ ${action} = old ] ; then
     hostname=${DNSMASQ_OLD_HOSTNAME}
 fi
 
+# IPv6 leases are not our concern. no NAT there!
+if [ ${DNSMASQ_IAID} ] ; then
+   exit 0
+fi
+
 # action init is not relevant, and will only be seen when leasefile-ro is set.
 if [ ${action} = init ] ; then
     exit 0
 fi
 
+# action tftp is not relevant.
+if [ ${action} = tftp ] ; then
+    exit 0
+fi
+
 if [ ${hostname} ]; then
     ports=$(sed -n -e "/^${hostname}\ .*/ s/^.* //p" ${PORTSFILE})
 
diff --git a/contrib/reverse-dns/README b/contrib/reverse-dns/README
new file mode 100644 (file)
index 0000000..2ec4df1
--- /dev/null
@@ -0,0 +1,18 @@
+The script reads stdin and replaces all IP addresses with names before\r
+outputting it again. IPs from private networks are reverse looked  up\r
+via dns. Other IP adresses are searched for in the dnsmasq query log.\r
+This gives names (CNAMEs if I understand DNS correctly) that are closer\r
+to the name the client originally asked for then the names obtained by\r
+reverse lookup. Just run\r
+\r
+netstat -n -4 | ./reverse_replace.sh \r
+\r
+to see what it does. It needs \r
+\r
+log-queries\r
+log-facility=/var/log/dnsmasq.log\r
+\r
+in the dnsmasq configuration.\r
+\r
+The script runs on debian (with ash installed) and on busybox.\r
+\r
diff --git a/contrib/reverse-dns/reverse_replace.sh b/contrib/reverse-dns/reverse_replace.sh
new file mode 100644 (file)
index 0000000..5b4aebd
--- /dev/null
@@ -0,0 +1,125 @@
+#!/bin/ash
+# $Id: reverse_replace.sh 18 2015-03-01 16:12:35Z jo $
+#
+# Usage e.g.: netstat -n -4 | reverse_replace.sh 
+# Parses stdin for IP4 addresses and replaces them 
+# with names retrieved by parsing the dnsmasq log.
+# This currently only gives CNAMEs. But these 
+# usually tell ou more than the mones from reverse 
+# lookups. 
+#
+# This has been tested on debian and asuswrt. Plese
+# report successful tests on other platforms.
+#
+# Author: Joachim Zobel <jz-2014@heute-morgen.de>
+# License: Consider this MIT style licensed. You can 
+#   do as you ike, but you must not remove my name.
+#
+
+LOG=/var/log/dnsmasq.log
+MAX_LINES=15000
+
+# sed regex do match IPs
+IP_regex='[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'
+# private IP ranges
+IP_private='\(^127\.\)\|\(^192\.168\.\)\|\(^10\.\)\|\(^172\.1[6-9]\.\)\|\(^172\.2[0-9]\.\)\|\(^172\.3[0-1]\.\)'
+
+#######################################################################
+# Find Commands
+  
+HOST=nslookup
+if type host > /dev/null 2>&1; then
+  # echo "No need for nslookup, host is there"
+  HOST=host
+fi
+
+#######################################################################
+# Functions
+
+# Use shell variables for an (IP) lookup table
+create_lookup_table()
+{
+  # Parse log into lookup table
+  local CMDS="$( tail -"$MAX_LINES" "$LOG" | \
+    grep " is $IP_regex" | \
+    sed "s#.* \([^ ]*\) is \($IP_regex\).*#set_val \2 \1;#" )"
+
+  local IFS='
+'
+  for CMD in $CMDS
+  do
+    eval $CMD
+  done
+}
+
+set_val()
+{
+  local _IP=$(echo $1 | tr . _)
+  local KEY="__IP__$_IP"
+  eval "$KEY"=$2
+}
+
+get_val()
+{
+  local _IP=$(echo $1 | tr . _)
+  local KEY="__IP__$_IP"
+  eval echo -n '${'"$KEY"'}'
+}
+
+dns_lookup()
+{
+  local IP=$1
+
+  local RTN="$($HOST $IP | \
+        sed 's#\s\+#\n#g' | \
+        grep -v '^$' | \
+        tail -1 | tr -d '\n' | \
+        sed 's#\.$##')"
+  if echo $RTN | grep -q NXDOMAIN; then
+    echo -n $IP
+  else
+    echo -n "$RTN"
+  fi     
+}
+
+reverse_dns()
+{
+  local IP=$1
+
+  # Skip if it is not an IP
+  if ! echo $IP | grep -q "^$IP_regex$"; then
+    echo -n $IP
+    return 
+  fi
+    
+  # Do a dns lookup, if it is a local IP
+  if echo $IP | grep -q $IP_private; then
+    dns_lookup $IP
+    return
+  fi
+    
+  local NAME="$(get_val $IP)"
+  
+  if [ -z "$NAME" ]; then
+    echo -n $IP
+  else
+    echo -n $NAME
+  fi
+}
+
+#######################################################################
+# Main
+create_lookup_table
+
+while read LINE; do
+  for IP in $(echo "$LINE" | \
+              sed "s#\b\($IP_regex\)\b#\n\1\n#g" | \
+              grep $IP_regex) 
+  do
+    NAME=`reverse_dns $IP `
+    # echo "$NAME $IP"
+    LINE=`echo "$LINE" | sed "s#$IP#$NAME#" ` 
+  done
+  echo $LINE
+done
+
diff --git a/contrib/systemd/README b/contrib/systemd/README
new file mode 100644 (file)
index 0000000..c8046c2
--- /dev/null
@@ -0,0 +1,16 @@
+Hello,
+
+I created a systemd service file for dnsmasq.
+systemd is a sysvinit replacement (see [1] for more information).
+One of the goals of systemd is to encourage standardization between different
+distributions. This means, while I also submitted a ticket in Debian GNU/Linux,
+I would like to ask you to accept this service file as the upstream
+distributor, so that other distributions can use the same service file and
+don’t have to ship their own.
+
+Please include this file in your next release (just like in init script).
+
+
+[1] http://en.wikipedia.org/wiki/Systemd
+
+
diff --git a/contrib/systemd/dbus_activation b/contrib/systemd/dbus_activation
new file mode 100644 (file)
index 0000000..38f0822
--- /dev/null
@@ -0,0 +1,57 @@
+To: dnsmasq-discuss@lists.thekelleys.org.uk\r
+From: Alex Elsayed <eternaleye+usenet@gmail.com>\r
+Date: Tue, 15 May 2012 01:53:54 -0700\r
+Subject: [Dnsmasq-discuss] [PATCH] Support dbus activation\r
+\r
+Introduce dbus service file and turn dbus on in the systemd\r
+unit.\r
+\r
+Note to packagers:\r
+To add support for dbus activation, you must install the dbus\r
+service file (dbus/uk.org.thekelleys.dnsmasq.service) into\r
+$DATADIR/dbus-1/system-services.\r
+\r
+---\r
+ contrib/systemd/dnsmasq.service        |    2 +-\r
+ dbus/uk.org.thekelleys.dnsmasq.service |    7 +++++++\r
+ 2 files changed, 8 insertions(+), 1 deletion(-)\r
+ create mode 100644 dbus/uk.org.thekelleys.dnsmasq.service\r
+\r
+diff --git a/contrib/systemd/dnsmasq.service \r
+b/contrib/systemd/dnsmasq.service\r
+index a27fe6d..4a784d3 100644\r
+--- a/contrib/systemd/dnsmasq.service\r
++++ b/contrib/systemd/dnsmasq.service\r
+@@ -5,7 +5,7 @@ Description=A lightweight DHCP and caching DNS server\r
+ Type=dbus\r
+ BusName=uk.org.thekelleys.dnsmasq\r
+ ExecStartPre=/usr/sbin/dnsmasq --test\r
+-ExecStart=/usr/sbin/dnsmasq -k\r
++ExecStart=/usr/sbin/dnsmasq -k -1\r
+ ExecReload=/bin/kill -HUP $MAINPID\r
\r
+ [Install]\r
+diff --git a/dbus/uk.org.thekelleys.dnsmasq.service \r
+b/dbus/uk.org.thekelleys.dnsmasq.service\r
+new file mode 100644\r
+index 0000000..f5fe98d\r
+--- /dev/null\r
++++ b/dbus/uk.org.thekelleys.dnsmasq.service\r
+@@ -0,0 +1,7 @@\r
++[D-BUS Service]\r
++Name=uk.org.thekelleys.dnsmasq\r
++Exec=/usr/sbin/dnsmasq -k -1\r
++User=root\r
++SystemdService=dnsmasq.service\r
++\r
++\r
+-- \r
+1.7.10.2\r
+\r
+\r
+\r
+_______________________________________________\r
+Dnsmasq-discuss mailing list\r
+Dnsmasq-discuss@lists.thekelleys.org.uk\r
+http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss\r
+\r
diff --git a/contrib/systemd/dnsmasq.service b/contrib/systemd/dnsmasq.service
new file mode 100644 (file)
index 0000000..c70b144
--- /dev/null
@@ -0,0 +1,12 @@
+[Unit]
+Description=dnsmasq - A lightweight DHCP and caching DNS server
+
+[Service]
+Type=dbus
+BusName=uk.org.thekelleys.dnsmasq
+ExecStartPre=/usr/sbin/dnsmasq --test
+ExecStart=/usr/sbin/dnsmasq -k
+ExecReload=/bin/kill -HUP $MAINPID
+
+[Install]
+WantedBy=multi-user.target
diff --git a/contrib/try-all-ns/dnsmasq-2.68-try-all-ns b/contrib/try-all-ns/dnsmasq-2.68-try-all-ns
new file mode 100644 (file)
index 0000000..66a41f6
--- /dev/null
@@ -0,0 +1,29 @@
+From: Jesse Glick <jglick@cloudbees.com>
+To: dnsmasq-discuss@lists.thekelleys.org.uk
+Subject: Re: [Dnsmasq-discuss] Ability to delegate to one server but fall
+ back to another after NXDOMAIN?
+
+
+On Wed, Jan 15, 2014 at 12:30 PM, Simon Kelley <simon@thekelleys.org.uk> wrote:
+> > There's a (very old) patch in contrib/try-all-ns that would make a starting point
+This does not apply against trunk, so I tried to rework it. The
+following appears to do what I expect:
+
+diff --git a/src/forward.c b/src/forward.c
+index 8167229..76070b5 100644
+--- a/src/forward.c
++++ b/src/forward.c
+@@ -610,7 +610,11 @@ void reply_query(int fd, int family, time_t now)
+
+   if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
+       !option_bool(OPT_ORDER) &&
+-      forward->forwardall == 0)
++      forward->forwardall == 0 ||
++      /* try each in turn */
++      RCODE(header) == NXDOMAIN &&
++      option_bool(OPT_ORDER) &&
++      server->next != NULL)
+     /* for broken servers, attempt to send to another one. */
+     {
+       unsigned char *pheader;
+
diff --git a/contrib/wrt/dhcp_lease_time.1 b/contrib/wrt/dhcp_lease_time.1
new file mode 100644 (file)
index 0000000..2fa78d3
--- /dev/null
@@ -0,0 +1,25 @@
+.TH DHCP_LEASE_TIME 1
+.SH NAME
+dhcp_lease_time \- Query remaining time of a lease on a the local dnsmasq DHCP server.
+.SH SYNOPSIS
+.B  dhcp_lease_time <address>
+.SH "DESCRIPTION"
+Send a DHCPINFORM message to a dnsmasq server running on the local host
+and print (to stdout) the time remaining in any lease for the given
+address. The time is given as string printed to stdout.
+
+If an error occurs or no lease exists for the given address, 
+nothing is sent to stdout a message is sent to stderr and a
+non-zero error code is returned.
+
+Requires dnsmasq 2.67 or later and may not work with other DHCP servers.
+
+The address argument is a dotted-quad IP addresses and mandatory.
+.SH LIMITATIONS
+Only works with IPv4 addresses and DHCP leases. 
+.SH SEE ALSO
+.BR dnsmasq (8)
+.SH AUTHOR
+This manual page was written by Simon Kelley <simon@thekelleys.org.uk>.
+
+
index 2866bb5..b438ef7 100644 (file)
@@ -20,7 +20,7 @@
    nothing is sent to stdout a message is sent to stderr and a
    non-zero error code is returned.
 
-   Requires dnsmasq 2.40 or later. 
+   This version requires dnsmasq 2.67 or later. 
 */
 
 #include <sys/types.h> 
@@ -46,6 +46,7 @@
 #define OPTION_LEASE_TIME        51
 #define OPTION_OVERLOAD          52
 #define OPTION_MESSAGE_TYPE      53
+#define OPTION_REQUESTED_OPTIONS 55
 #define OPTION_END               255
 #define DHCPINFORM               8
 #define DHCP_SERVER_PORT         67
@@ -167,6 +168,12 @@ int main(int argc, char **argv)
   *(p++) = 1;
   *(p++) = DHCPINFORM;
 
+  /* Explicity request the lease time, it won't be sent otherwise:
+     this is a dnsmasq extension, not standard. */
+  *(p++) = OPTION_REQUESTED_OPTIONS;
+  *(p++) = 1;
+  *(p++) = OPTION_LEASE_TIME;
+  
   *(p++) = OPTION_END;
  
   dest.sin_family = AF_INET; 
diff --git a/contrib/wrt/dhcp_release.1 b/contrib/wrt/dhcp_release.1
new file mode 100644 (file)
index 0000000..e71aba0
--- /dev/null
@@ -0,0 +1,37 @@
+.TH DHCP_RELEASE 1
+.SH NAME
+dhcp_release \- Release a DHCP lease on a the local dnsmasq DHCP server.
+.SH SYNOPSIS
+.B dhcp_release <interface> <address> <MAC address> <client_id>
+.SH "DESCRIPTION"
+A utility which forces the DHCP server running on this machine to release a 
+DHCP lease.
+.PP
+Send a DHCPRELEASE message via the specified interface to tell the
+local DHCP server to delete a particular lease. 
+
+The interface argument is the interface in which a DHCP
+request _would_ be received if it was coming from the client, 
+rather than being faked up here.
+   
+The address argument is a dotted-quad IP addresses and mandatory. 
+   
+The MAC address is colon separated hex, and is mandatory. It may be 
+prefixed by an address-type byte followed by -, eg
+
+10-11:22:33:44:55:66
+
+but if the address-type byte is missing it is assumed to be 1, the type 
+for ethernet. This encoding is the one used in dnsmasq lease files.
+
+The client-id is optional. If it is "*" then it treated as being missing.
+.SH NOTES
+MUST be run as root - will fail otherwise.
+.SH LIMITATIONS
+Only usable on IPv4 DHCP leases.
+.SH SEE ALSO
+.BR dnsmasq (8)
+.SH AUTHOR
+This manual page was written by Simon Kelley <simon@thekelleys.org.uk>.
+
+
index c66d3a0..a51f04b 100644 (file)
@@ -178,7 +178,7 @@ static int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
   return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
 }
 
-static struct in_addr find_interface(struct in_addr client, int fd, int index)
+static struct in_addr find_interface(struct in_addr client, int fd, unsigned int index)
 {
   struct sockaddr_nl addr;
   struct nlmsghdr *h;
@@ -255,10 +255,6 @@ int main(int argc, char **argv)
   struct ifreq ifr;
   int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
   int nl = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
-  struct iovec iov;
-  iov.iov_len = 200;
-  iov.iov_base = malloc(iov.iov_len);
 
   if (argc < 4 || argc > 5)
     { 
@@ -281,6 +277,11 @@ int main(int argc, char **argv)
       exit(1);
     }
   
+  if (inet_addr(argv[2]) == INADDR_NONE)
+    {
+      perror("invalid ip address");
+      exit(1);
+    }
   
   lease.s_addr = inet_addr(argv[2]);
   server = find_interface(lease, nl, if_nametoindex(argv[1]));
index 8d578ca..2db5c30 100644 (file)
@@ -19,7 +19,8 @@ and avoids startup races with the provider of nameserver information.
 
 
 Dnsmasq provides one service on the DBus: uk.org.thekelleys.dnsmasq
-and a single object: /uk/org/thekelleys/dnsmasq
+and a single object: /uk/org/thekelleys/dnsmasq 
+The name of the service may be changed by giving an argument to --enable-dbus.
 
 1. METHODS
 ----------
@@ -39,6 +40,14 @@ ClearCache
 Returns nothing. Clears the domain name cache and re-reads
 /etc/hosts. The same as sending dnsmasq a HUP signal.
 
+SetFilterWin2KOption
+--------------------
+Takes boolean, sets or resets the --filterwin2k option.
+
+SetBogusPrivOption
+------------------
+Takes boolean, sets or resets the --bogus-priv option.
+
 SetServers
 ----------
 Returns nothing. Takes a set of arguments representing the new
@@ -94,6 +103,148 @@ Each call to SetServers completely replaces the set of servers
 specified by via the DBus, but it leaves any servers specified via the
 command line or /etc/dnsmasq.conf or /etc/resolv.conf alone.
 
+SetServersEx
+------------
+
+This function is more flexible and the SetServers function, in that it can
+handle address scoping, port numbers, and is easier for clients to use.
+
+Returns nothing. Takes a set of arguments representing the new
+upstream DNS servers to be used by dnsmasq. All addresses (both IPv4 and IPv6)
+are represented as STRINGS.  Each server address may be followed by one or more
+STRINGS, which are the domains for which the preceding server should be used.
+
+This function takes an array of STRING arrays, where each inner array represents
+a set of DNS servers and domains for which those servers may be used.  Each
+string represents a list of upstream DNS servers first, and domains second.
+Mixing of domains and servers within a the string array is not allowed.
+
+Examples.
+
+[
+  ["1.2.3.4", "foobar.com"],
+  ["1003:1234:abcd::1%eth0", "eng.mycorp.com", "lab.mycorp.com"]
+]
+
+is equivalent to
+
+--server=/foobar.com/1.2.3.4 \
+  --server=/eng.mycorp.com/lab.mycorp.com/1003:1234:abcd::1%eth0
+
+An IPv4 address of 0.0.0.0 is interpreted as "no address, local only",
+so
+
+[ ["0.0.0.0", "local.domain"] ]
+
+is equivalent to
+
+--local=/local.domain/
+
+
+Each call to SetServersEx completely replaces the set of servers
+specified by via the DBus, but it leaves any servers specified via the
+command line or /etc/dnsmasq.conf or /etc/resolv.conf alone.
+
+
+SetDomainServers
+----------------
+
+Yes another variation for setting DNS servers, with the capability of
+SetServersEx, but without using arrays of arrays, which are not
+sendable with dbus-send. The arguments are an array of strings which
+are identical to the equivalent arguments --server, so the example
+for SetServersEx is represented as
+
+[
+  "/foobar.com/1.2.3.4"
+  "/eng.mycorp.com/lab.mycorp.com/1003:1234:abcd::1%eth0"
+]
+
+GetLoopServers
+--------------
+
+(Only available if dnsmasq compiled with HAVE_LOOP)
+
+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
 ----------
 
index 719dcff..590f217 100644 (file)
@@ -4,6 +4,11 @@
 # as the long options legal on the command line. See
 # "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.
 
+# Listen on this specific port instead of the standard DNS port
+# (53). Setting this to zero completely disables DNS function,
+# leaving only DHCP and/or TFTP.
+#port=5353
+
 # The following two options make you a better netizen, since they
 # tell dnsmasq to filter out queries which the public DNS cannot
 # answer, and which load the servers (especially the root servers)
 # Never forward addresses in the non-routed address spaces.
 #bogus-priv
 
+# Uncomment these to enable DNSSEC validation and caching:
+# (Requires dnsmasq to be built with DNSSEC option.)
+#conf-file=%%PREFIX%%/share/dnsmasq/trust-anchors.conf
+#dnssec
+
+# Replies which are not DNSSEC signed may be legitimate, because the domain
+# is unsigned, or may be forgeries. Setting this option tells dnsmasq to
+# check that an unsigned reply is OK, by finding a secure proof that a DS 
+# record somewhere between the root and the domain does not exist. 
+# The cost of setting this is that even queries in unsigned domains will need
+# one or more extra DNS queries to verify.
+#dnssec-check-unsigned
 
 # Uncomment this to filter useless windows-originated DNS requests
 # which can trigger dial-on-demand links needlessly.
 # --address (and --server) work with IPv6 addresses too.
 #address=/www.thekelleys.org.uk/fe80::20d:60ff:fe36:f83
 
+# Add the IPs of all queries to yahoo.com, google.com, and their
+# subdomains to the vpn and search ipsets:
+#ipset=/yahoo.com/google.com/vpn,search
+
 # You can control how dnsmasq talks to a server: this forces
 # queries to 10.1.2.3 to be routed via eth1
 # server=10.1.2.3@eth1
 # an explicit netmask instead.
 #dhcp-range=192.168.0.0,static
 
+# Enable DHCPv6. Note that the prefix-length does not need to be specified
+# and defaults to 64 if missing/
+#dhcp-range=1234::2, 1234::500, 64, 12h
+
+# Do Router Advertisements, BUT NOT DHCP for this subnet.
+#dhcp-range=1234::, ra-only 
+
+# Do Router Advertisements, BUT NOT DHCP for this subnet, also try and
+# add names to the DNS for the IPv6 address of SLAAC-configured dual-stack 
+# hosts. Use the DHCPv4 lease to derive the name, network segment and 
+# MAC address and assume that the host will also have an
+# IPv6 address calculated using the SLAAC alogrithm.
+#dhcp-range=1234::, ra-names
+
+# Do Router Advertisements, BUT NOT DHCP for this subnet.
+# Set the lifetime to 46 hours. (Note: minimum lifetime is 2 hours.)
+#dhcp-range=1234::, ra-only, 48h
+
+# Do DHCP and Router Advertisements for this subnet. Set the A bit in the RA
+# so that clients can use SLAAC addresses as well as DHCP ones.
+#dhcp-range=1234::2, 1234::500, slaac
+
+# Do Router Advertisements and stateless DHCP for this subnet. Clients will
+# not get addresses from DHCP, but they will get other configuration information.
+# They will use SLAAC for addresses.
+#dhcp-range=1234::, ra-stateless
+
+# Do stateless DHCP, SLAAC, and generate DNS names for SLAAC addresses
+# from DHCPv4 leases.
+#dhcp-range=1234::, ra-stateless, ra-names
+
+# Do router advertisements for all subnets where we're doing DHCPv6
+# Unless overriden by ra-stateless, ra-names, et al, the router 
+# advertisements will have the M and O bits set, so that the clients
+# get addresses and configuration from DHCPv6, and the A bit reset, so the 
+# clients don't use SLAAC addresses.
+#enable-ra
+
 # Supply parameters for specified hosts using DHCP. There are lots
 # of valid alternatives, so we will give examples of each. Note that
 # IP addresses DO NOT have to be in the range given above, they just
 # 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
 # any machine with Ethernet address starting 11:22:33:
 #dhcp-host=11:22:33:*:*:*,set:red
 
-# Ignore any clients which are specified in dhcp-host lines
+# Give a fixed IPv6 address and name to client with 
+# DUID 00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2
+# Note the MAC addresses CANNOT be used to identify DHCPv6 clients.
+# Note also the they [] around the IPv6 address are obilgatory.
+#dhcp-host=id:00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2, fred, [1234::5] 
+
+# Ignore any clients which are not specified in dhcp-host lines
 # or /etc/ethers. Equivalent to ISC "deny unknown-clients".
 # This relies on the special "known" tag which is set when
 # a host is matched.
 # Set the NTP time server addresses to 192.168.0.4 and 10.10.0.5
 #dhcp-option=option:ntp-server,192.168.0.4,10.10.0.5
 
+# Send DHCPv6 option. Note [] around IPv6 addresses.
+#dhcp-option=option6:dns-server,[1234::77],[1234::88]
+
+# Send DHCPv6 option for namservers as the machine running 
+# dnsmasq and another.
+#dhcp-option=option6:dns-server,[::],[1234::88]
+
+# Ask client to poll for option changes every six hours. (RFC4242)
+#dhcp-option=option6:information-refresh-time,6h
+
+# Set option 58 client renewal time (T1). Defaults to half of the
+# lease time if not specified. (RFC2132)
+#dhcp-option=option:T1:1m
+
+# Set option 59 rebinding time (T2). Defaults to 7/8 of the
+# lease time if not specified. (RFC2132)
+#dhcp-option=option:T2:2m
+
 # Set the NTP time server address to be the same machine as
 # is running dnsmasq
 #dhcp-option=42,0.0.0.0
 #dhcp-option=45,0.0.0.0     # netbios datagram distribution server
 #dhcp-option=46,8           # netbios node type
 
+# Send an empty WPAD option. This may be REQUIRED to get windows 7 to behave.
+#dhcp-option=252,"\n"
+
 # Send RFC-3397 DNS domain search DHCP option. WARNING: Your DHCP client
 # probably doesn't support this......
 #dhcp-option=option:domain-search,eng.apple.com,marketing.apple.com
 # external one. (See below for how to enable the TFTP server.)
 #dhcp-boot=pxelinux.0
 
+# The same as above, but use custom tftp-server instead machine running dnsmasq
+#dhcp-boot=pxelinux,server.name,192.168.1.100
+
 # Boot for Etherboot gPXE. The idea is to send two different
 # filenames, the first loads gPXE, and the second tells gPXE what to
 # load. The dhcp-match sets the gpxe tag for requests from gPXE.
 # Set the root directory for files available via FTP.
 #tftp-root=/var/ftpd
 
+# Do not abort if the tftp-root is unavailable
+#tftp-no-fail
+
 # Make the TFTP server more secure: with this set, only files owned by
 # the user dnsmasq is running as will be send over the net.
 #tftp-secure
 #tftp-no-blocksize
 
 # Set the boot file name only when the "red" tag is set.
-#dhcp-boot=net:red,pxelinux.red-net
+#dhcp-boot=tag:red,pxelinux.red-net
 
 # An example of dhcp-boot with an external TFTP server: the name and IP
 # address of the server are given after the filename.
 # Can fail with old PXE ROMS. Overridden by --pxe-service.
 #dhcp-boot=/var/ftpd/pxelinux.0,boothost,192.168.0.3
 
+# If there are multiple external tftp servers having a same name
+# (using /etc/hosts) then that name can be specified as the
+# tftp_servername (the third option to dhcp-boot) and in that
+# case dnsmasq resolves this name and returns the resultant IP
+# addresses in round robin fasion. This facility can be used to
+# load balance the tftp load among a set of servers.
+#dhcp-boot=/var/ftpd/pxelinux.0,boothost,tftp_server_name
+
 # Set the limit on DHCP leases, the default is 150
 #dhcp-lease-max=150
 
 # If you want to disable negative caching, uncomment this.
 #no-negcache
 
-# Normally responses which come form /etc/hosts and the DHCP lease
+# Normally responses which come from /etc/hosts and the DHCP lease
 # file have Time-To-Live set as zero, which conventionally means
 # do not cache further. If you are happy to trade lower load on the
 # server for potentially stale date, you can set a time-to-live (in
 # Log lots of extra information about DHCP transactions.
 #log-dhcp
 
-# Include a another lot of configuration options.
+# Include another lot of configuration options.
 #conf-file=/etc/dnsmasq.more.conf
 #conf-dir=/etc/dnsmasq.d
+
+# Include all the files in a directory except those ending in .bak
+#conf-dir=/etc/dnsmasq.d,.bak
+
+# Include all files in a directory which end in .conf
+#conf-dir=/etc/dnsmasq.d/,*.conf
index 0a73083..54f59bb 100644 (file)
--- a/doc.html
+++ b/doc.html
@@ -1,8 +1,7 @@
 <HTML>
 <HEAD>
-<TITLE> Dnsmasq - a DNS forwarder for NAT firewalls.</TITLE>
-<link rel="icon"
-      href="http://www.thekelleys.org.uk/dnsmasq/images/favicon.ico">
+<TITLE> Dnsmasq - network services for small networks.</TITLE>
+<link rel="icon" href="http://www.thekelleys.org.uk/dnsmasq/images/favicon.ico">
 </HEAD>
 <BODY BGCOLOR="WHITE"> 
 <table width="100%" border="0" cellpadding="0" cellspacing="0">
 <td align="middle" valign="middle"><h1>Dnsmasq</h1></td>
 <td align="right" valign="middle"><img border="0" src="http://www.thekelleys.org.uk/dnsmasq/images/icon.png" /></td></tr>
 </table>
+Dnsmasq provides network infrastructure for small networks: DNS, DHCP, router advertisement and network boot. It is designed to be 
+lightweight and have a small footprint, suitable for resource constrained routers and firewalls. It has also been widely used 
+for tethering on smartphones and portable hotspots, and to support virtual networking in virtualisation frameworks.
+Supported platforms include Linux (with glibc and uclibc), Android, *BSD, and Mac OS X. Dnsmasq is included in most
+Linux distributions and the ports systems of FreeBSD, OpenBSD and NetBSD. Dnsmasq provides full IPv6 support.
 
-Dnsmasq is a lightweight, easy to configure DNS forwarder and DHCP
- server. It is designed to provide DNS and, optionally, DHCP, to a 
- small network. It can serve the names of local machines which are 
- not in the global DNS. The DHCP server integrates with the DNS 
- server and allows machines with DHCP-allocated addresses
- to appear in the DNS with names configured either in each host or
- in a central configuration file. Dnsmasq supports static and dynamic 
- DHCP leases and BOOTP/TFTP/PXE for network booting of diskless machines.
 <P>
- Dnsmasq is targeted at home networks using NAT and 
-connected to the internet via a modem, cable-modem or ADSL
-connection but would be a good choice for any smallish network (up to
-1000 clients is known to work) where low
-resource use and ease of configuration are important. 
-<P>
-Supported platforms include Linux (with glibc and uclibc), *BSD,
-Solaris and Mac OS X.
-Dnsmasq is included in at least the following Linux distributions:
-Gentoo, Debian, Slackware, Suse, Fedora,
-Smoothwall, IP-Cop, floppyfw, Firebox, LEAF, Freesco, fli4l,
-CoyoteLinux, Endian Firewall and
-Clarkconnect. It is also available as FreeBSD, OpenBSD and NetBSD ports and is used in
-Linksys wireless routers (dd-wrt, openwrt and the stock firmware) and the m0n0wall project.
+The DNS subsystem provides a local DNS server for the network, with forwarding of all query types to upstream recursive DNS servers and
+cacheing of common record types (A, AAAA, CNAME and PTR, also DNSKEY and DS when DNSSEC is enabled). 
+<DIR>
+<LI>Local DNS names can be defined by reading /etc/hosts, by importing names from the DHCP subsystem, or by configuration of a wide range of useful record types.</LI>
+<LI>Upstream servers can be configured in a variety of convenient ways, including  dynamic configuration as these change on moving upstream network.
+<LI>Authoritative DNS mode allows local DNS names may be exported to zone in the global DNS. Dnsmasq acts as authoritative server for this zone, and also provides 
+zone transfer to secondaries for the zone, if required.</LI>
+<LI>DNSSEC validation may be performed on DNS replies from upstream nameservers, providing security against spoofing and cache poisoning.</LI>
+<LI>Specified sub-domains can be directed to their own upstream DNS servers, making VPN configuration easy.</LI>
+<LI>Internationalised domain names are supported.
+</DIR>
 <P>
-Dnsmasq provides the following features:
+The DHCP subsystem supports DHCPv4, DHCPv6, BOOTP and PXE.
 <DIR>
-
-<LI> 
-The DNS configuration of machines behind the firewall is simple and
-doesn't depend on the details of the ISP's dns servers
-<LI>
-Clients which try to do DNS lookups while  a modem link to the
-internet is down will time out immediately.
-</LI>
-<LI>
-Dnsmasq will serve names from the /etc/hosts file on the firewall
-machine: If the names of local machines are there, then they can all
-be addressed without having to maintain /etc/hosts on each machine.
-</LI>
-<LI>
-The integrated DHCP server supports static and dynamic DHCP leases and
-multiple networks and IP ranges. It works across BOOTP relays and
-supports DHCP options including RFC3397 DNS search lists.
-Machines which are configured by DHCP have their names automatically 
+<LI> Both static and dynamic DHCP leases are supported, along with stateless mode in DHCPv6.</LI>
+<LI> The PXE system is a full PXE server, supporting netboot menus and multiple architecture support. It
+includes proxy-mode, where the PXE system co-operates with another DHCP server.</LI>
+<LI> There is a built in read-only TFTP server to support netboot.</LI>
+<LI> Machines which are configured by DHCP have their names automatically 
 included in the DNS and the names can specified by each machine or
-centrally by associating a name with a MAC address in the dnsmasq
-config file.
-</LI>
-<LI>
-Dnsmasq caches internet addresses (A records and AAAA records) and address-to-name
-mappings (PTR records), reducing the load on upstream servers and
-improving performance (especially on modem connections). 
-</LI>
-<LI>
-Dnsmasq can be configured to automatically pick up the addresses of
-its upstream nameservers from ppp or dhcp configuration. It will
-automatically reload this information if it changes. This facility
-will be of particular interest to maintainers of Linux firewall
-distributions since it allows dns configuration to be made automatic.
-</LI>
-<LI>
-On IPv6-enabled boxes, dnsmasq can both talk to upstream servers via IPv6 
-and offer DNS service via IPv6. On dual-stack (IPv4 and IPv6) boxes it talks
-both protocols and can even act as IPv6-to-IPv4 or IPv4-to-IPv6 forwarder.
-</LI>
-<LI>
-Dnsmasq can be configured to send queries for certain domains to
-upstream servers handling only those domains. This makes integration
-with private DNS systems easy.
-</LI>
-<LI>
-Dnsmasq supports MX and SRV records and can be configured to return MX records
-for any or all local machines.
-</LI>
+centrally by associating a name with a MAC address or UID in the dnsmasq
+configuration file.</LI>
+</DIR>
+<P>
+The Router Advertisement subsystem provides basic autoconfiguration for IPv6 hosts. It can be used stand-alone or in conjunction with DHCPv6.
+<DIR>
+<LI> The M and O bits are configurable, to control hosts' use of DHCPv6.</LI>
+<LI> Router advertisements can include the RDNSS option.</LI>
+<LI> There is a mode which uses name information from DHCPv4 configuration to provide DNS entries
+ for autoconfigured IPv6 addresses which would otherwise be anonymous.</LI>
 </DIR>
+<P>
+For extra compactness, unused features may be omitted at compile time.
+
 
-<H2>Download.</H2>
+<H2>Get code.</H2>
 
-<A HREF="http://www.thekelleys.org.uk/dnsmasq/"> Download</A> dnsmasq here. 
+<A HREF="http://www.thekelleys.org.uk/dnsmasq/">Download</A> dnsmasq here. 
 The tarball includes this documentation, source, and manpage.
 There is also a <A HREF="CHANGELOG"> CHANGELOG</A> and a <A HREF="FAQ">FAQ</A>.
-Dnsmasq is part of the Debian distribution, it can be downloaded from 
-<A HREF="http://ftp.debian.org/debian/pool/main/d/dnsmasq/"> here</A> or installed using <TT>apt</TT>.
 
-<H2>Links.</H2>
-Damien Raude-Morvan has an article in French at <A HREF="http://www.drazzib.com/docs-dnsmasq.html">http://www.drazzib.com/docs-dnsmasq.html</A>
-There is a good article about dnsmasq at <A
-HREF="http://www.enterprisenetworkingplanet.com/netos/article.php/3377351">http://www.enterprisenetworkingplanet.com/netos/article.php/3377351</A>
-and another at <A
-HREF="http://www.linux.com/articles/149040">http://www.linux.com/articles/149040</A>
-and Ilya Evseev has an article in Russian about dnsmasq to be found at
-<A HREF="http://ilya-evseev.narod.ru/articles/dnsmasq">
-http://ilya-evseev.narod.ru/articles/dnsmasq</A>. Ismael Ull has an
-article about dnsmasq in Spanish at <A HREF="http://www.mey-online.com.ar/blog/index.php/archives/guia-rapida-de-dnsmasq">http://www.mey-online.com.ar/blog/index.php/archives/guia-rapida-de-dnsmasq</A>
+Dnsmasq has a git repository which contains the complete release
+history of version 2 and development history from 2.60. You can 
+<A HREF="http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=summary">browse</A>
+the repo, or get a copy using git protocol with the command
+
+<PRE><TT>git clone git://thekelleys.org.uk/dnsmasq.git </TT></PRE>
+
 <H2>License.</H2>
-Dnsmasq is distributed under the GPL. See the file COPYING in the distribution 
+Dnsmasq is distributed under the GPL, version 2 or version 3 at your discretion. See the files COPYING and COPYING-v3 in the distribution 
 for details.
 
 <H2>Contact.</H2>
 There is a dnsmasq mailing list at <A
 HREF="http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss">
 http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss</A> which should be the
-first location for queries, bugreports, suggestions etc.
-Dnsmasq was written by Simon Kelley. You can contact me at <A
+first location for queries, bugreports, suggestions etc. The list is mirrored, with a
+search facility, at <A HREF="https://www.mail-archive.com/dnsmasq-discuss@lists.thekelleys.org.uk/">
+https://www.mail-archive.com/dnsmasq-discuss@lists.thekelleys.org.uk/</A>.
+You can contact me at <A
 HREF="mailto:simon@thekelleys.org.uk">simon@thekelleys.org.uk</A>.
+
+<H2>Donations.</H2>
+Dnsmasq is mainly written and maintained by Simon Kelley. For most of its life, dnsmasq has been a spare-time project. 
+These days I'm working on it as my main activity. 
+I don't have an employer or anyone who pays me regularly to work on dnsmasq. If you'd like to make 
+a contribution towards my expenses, please use the donation button below.
+<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
+<input type="hidden" name="cmd" value="_s-xclick">
+<input type="hidden" name="hosted_button_id" value="V3X9GVW5GX6DA">
+<input type="image" src="https://www.paypalobjects.com/en_US/GB/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal – The safer, easier way to pay online.">
+<img alt="" border="0" src="https://www.paypalobjects.com/en_GB/i/scr/pixel.gif" width="1" height="1">
+</form>
+
+
 </BODY>
 
index 1c7e8f8..71bc934 100644 (file)
Binary files a/logo/favicon.ico and b/logo/favicon.ico differ
index 511f57f..c8913b5 100644 (file)
@@ -6,24 +6,31 @@ dnsmasq \- A lightweight DHCP and caching DNS server.
 .I [OPTION]...
 .SH "DESCRIPTION"
 .BR dnsmasq
-is a lightweight DNS, TFTP and DHCP server. It is intended to provide 
+is a lightweight DNS, TFTP, PXE, router advertisement and DHCP server. It is intended to provide 
 coupled DNS and DHCP service to a LAN.
 .PP
 Dnsmasq accepts DNS queries and either answers them from a small, local,
 cache or forwards them to a real, recursive, DNS server. It loads the
 contents of /etc/hosts so that local hostnames
 which do not appear in the global DNS can be resolved and also answers
-DNS queries for DHCP configured hosts.
+DNS queries for DHCP configured hosts. It can also act as the
+authoritative DNS server for one or more domains, allowing local names
+to appear in the global DNS. It can be configured to do DNSSEC
+validation.
 .PP
 The dnsmasq DHCP server supports static address assignments and multiple
 networks. It automatically
 sends a sensible default set of DHCP options, and can be configured to
 send any desired set of DHCP options, including vendor-encapsulated
 options. It includes a secure, read-only,
-TFTP server to allow net/PXE boot of DHCP hosts and also supports BOOTP.
+TFTP server to allow net/PXE boot of DHCP hosts and also supports BOOTP. The PXE support is full featured, and includes a proxy mode which supplies PXE information to clients whilst DHCP address allocation is done by another server.
 .PP
-Dnsmasq 
-supports IPv6 for DNS and TFTP, but not DHCP.
+The dnsmasq DHCPv6 server provides the same set of features as the
+DHCPv4 server, and in addition, it includes router advertisements and
+a neat feature which allows nameing for clients which use DHCPv4 and
+stateless autoconfiguration only for IPv6 configuration. There is support for doing address allocation (both DHCPv6 and RA) from subnets which are dynamically delegated via DHCPv6 prefix delegation.
+.PP
+Dnsmasq is coded with small embedded systems in mind. It aims for the smallest possible memory footprint compatible with the supported functions,  and allows uneeded functions to be omitted from the compiled binary.  
 .SH OPTIONS
 Note that in general missing parameters are allowed and switch off
 functions, for instance "--pid-file" disables writing a PID file. On
@@ -43,6 +50,10 @@ Additional hosts file. Read the specified file as well as /etc/hosts. If -h is g
 only the specified file. This option may be repeated for more than one
 additional hosts file. If a directory is given, then read all the files contained in that directory. 
 .TP
+.B --hostsdir=<path>
+Read all the hosts files contained in the directory. New or changed files
+are read automatically. See --dhcp-hostsdir for details.
+.TP
 .B \-E, --expand-hosts
 Add the domain to simple names (without a period) in /etc/hosts
 in the same way as for DHCP-derived names. Note that this does not
@@ -51,7 +62,7 @@ apply to domain names in cnames, PTR records, TXT records etc.
 .B \-T, --local-ttl=<time>
 When replying with information from /etc/hosts or the DHCP leases
 file dnsmasq by default sets the time-to-live field to zero, meaning
-that the requestor should not itself cache the information. This is
+that the requester should not itself cache the information. This is
 the correct thing to do in almost all situations. This option allows a
 time-to-live (in seconds) to be given for these replies. This will
 reduce the load on the server at the expense of clients using stale
@@ -71,6 +82,18 @@ maximum TTL will be given to clients instead of the true TTL value if it is
 lower. The true TTL value is however kept in the cache to avoid flooding 
 the upstream DNS servers.
 .TP
+.B --max-cache-ttl=<time>
+Set a maximum TTL value for entries in the cache.
+.TP
+.B --min-cache-ttl=<time>
+Extend short TTL values to the time given when caching them. Note that
+artificially extending TTL values is in general a bad idea, do not do it 
+unless you have a good reason, and understand what you are doing. 
+Dnsmasq limits the value of this option to one hour, unless recompiled.
+.TP
+.B --auth-ttl=<time>
+Set the TTL value returned in answers from the authoritative server.
+.TP
 .B \-k, --keep-in-foreground
 Do not go into the background at startup but otherwise run as
 normal. This is intended for use when dnsmasq is run under daemontools
@@ -80,10 +103,15 @@ or launchd.
 Debug mode: don't fork to the background, don't write a pid file,
 don't change user id, generate a complete cache dump on receipt on
 SIGUSR1, log to stderr as well as syslog, don't fork new processes
-to handle TCP queries.
+to handle TCP queries. Note that this option is for use in debugging
+only, to stop dnsmasq daemonising in production, use 
+.B -k.
 .TP
 .B \-q, --log-queries
-Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on receipt of SIGUSR1.
+Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on receipt of SIGUSR1. If the argument "extra" is supplied, ie
+.B --log-queries=extra
+then the log has extra information at the start of each line.
+This consists of a serial number which ties together the log lines associated with an individual query, and the IP address of the requestor.
 .TP
 .B \-8, --log-facility=<facility>
 Set the facility to which dnsmasq will send syslog entries, this
@@ -162,7 +190,12 @@ options. IP alias interfaces (eg "eth1:0") cannot be used with
 .B --interface
 or
 .B --except-interface
-options, use --listen-address instead. 
+options, use --listen-address instead. A simple wildcard, consisting
+of a trailing '*', can be used in 
+.B \--interface 
+and
+.B \--except-interface
+options. 
 .TP
 .B \-I, --except-interface=<interface name>
 Do not listen on the specified interface. Note that the order of
@@ -173,6 +206,29 @@ and
 options does not matter and that 
 .B --except-interface
 options always override the others.
+.TP
+.B --auth-server=<domain>,<interface>|<ip-address>
+Enable DNS authoritative mode for queries arriving at an interface or address. Note that the interface or address
+need not be mentioned in 
+.B --interface
+or 
+.B --listen-address
+configuration, indeed
+.B --auth-server
+will overide these and provide a different DNS service on the
+specified interface. The <domain> is the "glue record". It should
+resolve in the global DNS to a A and/or AAAA record which points to
+the address dnsmasq is listening on. When an interface is specified,
+it may be qualified with "/4" or "/6" to specify only the IPv4 or IPv6
+addresses associated with the interface.
+.TP
+.B --local-service
+Accept DNS queries only from hosts whose address is on a local subnet,
+ie a subnet for which an interface exists on the server. This option
+only has effect is there are no --interface --except-interface,
+--listen-address or --auth-server options. It is intended to be set as
+a default on installation, to allow unconfigured installations to be
+useful but also safe from being used for DNS amplification attacks.
 .TP 
 .B \-2, --no-dhcp-interface=<interface name>
 Do not provide DHCP or TFTP on the specified interface, but do provide DNS service.
@@ -204,6 +260,17 @@ running another nameserver (or another instance of dnsmasq) on the
 same machine. Setting this option also enables multiple instances of
 dnsmasq which provide DHCP service to run in the same machine.
 .TP
+.B --bind-dynamic
+Enable a network mode which is a hybrid between 
+.B --bind-interfaces
+and the default. Dnsmasq binds the address of individual interfaces,
+allowing multiple dnsmasq instances, but if new interfaces or
+addresses appear, it automatically listens on those (subject to any
+access-control configuration). This makes dynamically created
+interfaces work in the same way as the default. Implementing this
+option requires non-standard networking APIs and it is only available
+under Linux. On other platforms it falls-back to --bind-interfaces mode.
+.TP
 .B \-y, --localise-queries
 Return answers to DNS queries from /etc/hosts which depend on the interface over which the query was
 received. If a name in /etc/hosts has more than one address associated with
@@ -239,6 +306,12 @@ an advertising web page in response to queries for unregistered names,
 instead of the correct NXDOMAIN response. This option tells dnsmasq to
 fake the correct response when it sees this behaviour. As at Sept 2003
 the IP address being returned by Verisign is 64.94.110.11
+.TP 
+.B \-B, --ignore-address=<ipaddr>
+Ignore replies to A-record queries which include the specified address. 
+No error is generated, dnsmasq simply continues to listen for another reply. 
+This is useful to defeat blocking strategies which rely on quickly supplying a
+forged answer to a DNS request for certain domain, before the correct answer can arrive.
 .TP
 .B \-f, --filterwin2k
 Later versions of windows make periodic DNS requests which don't get sensible answers from
@@ -249,8 +322,8 @@ requested name has underscores, to catch LDAP requests.
 .B \-r, --resolv-file=<file>
 Read the IP addresses of the upstream nameservers from <file>, instead of
 /etc/resolv.conf. For the format of this file see
-.BR resolv.conf (5) 
-the only lines relevant to dnsmasq are nameserver ones. Dnsmasq can
+.BR resolv.conf (5). 
+The only lines relevant to dnsmasq are nameserver ones. Dnsmasq can
 be told to poll more than one resolv.conf file, the first file name  specified
 overrides the default, subsequent ones add to the list. This is only
 allowed when polling; the file with the currently latest modification
@@ -260,11 +333,13 @@ time is the one used.
 Don't read /etc/resolv.conf. Get upstream servers only from the command
 line or the dnsmasq configuration file.
 .TP
-.B \-1, --enable-dbus
+.B \-1, --enable-dbus[=<service-name>]
 Allow dnsmasq configuration to be updated via DBus method calls. The
 configuration which can be changed is upstream DNS servers (and
 corresponding domains) and cache clear. Requires that dnsmasq has
-been built with DBus support.
+been built with DBus support. If the service name is given, dnsmasq
+provides service at that name, rather than the default which is 
+.B uk.org.thekelleys.dnsmasq
 .TP 
 .B \-o, --strict-order
 By default, dnsmasq will send queries to any of the upstream servers
@@ -276,7 +351,17 @@ server strictly in the order they appear in /etc/resolv.conf
 By default, when dnsmasq has more than one upstream server available,
 it will send queries to just one server. Setting this flag forces
 dnsmasq to send all queries to all available servers. The reply from
-the server which answers first will be returned to the original requestor.
+the server which answers first will be returned to the original requester.
+.TP
+.B --dns-loop-detect
+Enable code to detect DNS forwarding loops; ie the situation where a query sent to one 
+of the upstream server eventually returns as a new query to the dnsmasq instance. The
+process works by generating TXT queries of the form <hex>.test and sending them to
+each upstream server. The hex is a UID which encodes the instance of dnsmasq sending the query
+and the upstream server to which it was sent. If the query returns to the server which sent it, then
+the upstream server through which it was sent is disabled and this event is logged. Each time the
+set of upstream servers changes, the test is re-run on all of them, including ones which
+were previously disabled.
 .TP
 .B --stop-dns-rebind
 Reject (and log) addresses from upstream nameservers which are in the
@@ -298,12 +383,13 @@ by '/', like the --server syntax, eg.
 Don't poll /etc/resolv.conf for changes.
 .TP
 .B --clear-on-reload
-Whenever /etc/resolv.conf is re-read, clear the DNS cache.
+Whenever /etc/resolv.conf is re-read or the upstream servers are set
+via DBus, clear the DNS cache.
 This is useful when new nameservers may have different
 data than that held in cache.
 .TP
 .B \-D, --domain-needed
-Tells dnsmasq to never forward queries for plain names, without dots
+Tells dnsmasq to never forward A or AAAA queries for plain names, without dots
 or domain parts, to upstream nameservers. If the name is not known
 from /etc/hosts or DHCP then a "not found" answer is returned.
 .TP
@@ -350,6 +436,9 @@ is a synonym for
 .B server
 to make configuration files clearer in this case.
 
+IPv6 addresses may include a %interface scope-id, eg
+fe80::202:a412:4512:7bbf%eth0.
+
 The optional string after the @ character tells
 dnsmasq how to set the source of the queries to this
 nameserver. It should be an ip-address, which should belong to the machine on which
@@ -363,7 +452,15 @@ source address specified but the port may be specified directly as
 part of the source address. Forcing queries to an interface is not
 implemented on all platforms supported by dnsmasq.
 .TP
-.B \-A, --address=/<domain>/[domain/]<ipaddr>
+.B --rev-server=<ip-address>/<prefix-len>,<ipaddr>[#<port>][@<source-ip>|<interface>[#<port>]]
+This is functionally the same as 
+.B --server, 
+but provides some syntactic sugar to make specifying address-to-name queries easier. For example
+.B --rev-server=1.2.3.0/24,192.168.0.1
+is exactly equivalent to 
+.B --server=/3.2.1.in-addr.arpa/192.168.0.1
+.TP
+.B \-A, --address=/<domain>/[domain/][<ipaddr>]
 Specify an IP address to return for any host in the given domains.
 Queries in the domains are never forwarded and always replied to
 with the specified IP address which may be IPv4 or IPv6. To give
@@ -375,7 +472,16 @@ domain specification works in the same was as for --server, with the
 additional facility that /#/ matches any domain. Thus
 --address=/#/1.2.3.4 will always return 1.2.3.4 for any query not
 answered from /etc/hosts or DHCP and not sent to an upstream
-nameserver by a more specific --server directive.
+nameserver by a more specific --server directive. As for --server,
+one or more domains with no address returns a no-such-domain answer, so
+--address=/example.com/ is equivalent to --server=/example.com/ and returns 
+NXDOMAIN for example.com and all its subdomains.
+.TP
+.B --ipset=/<domain>/[domain/]<ipset>[,<ipset>]
+Places the resolved IP addresses of queries for the specified domains
+in the specified netfilter ip sets. Domains and subdomains are matched
+in the same way as --address. These ip sets must already exist. See
+ipset(8) for more details.
 .TP
 .B \-m, --mx-host=<mx name>[[,<hostname>],<preference>]
 Return an MX record named <mx name> pointing to the given hostname (if
@@ -413,6 +519,24 @@ zone files: the port, weight and priority numbers are in a different
 order. More than one SRV record for a given service/domain is allowed,
 all that match are returned.
 .TP
+.B --host-record=<name>[,<name>....],[<IPv4-address>],[<IPv6-address>]
+Add A, AAAA and PTR records to the DNS. This adds one or more names to
+the DNS with associated IPv4 (A) and IPv6 (AAAA) records. A name may
+appear in more than one 
+.B host-record
+and therefore be assigned more than one address. Only the first
+address creates a PTR record linking the address to the name. This is
+the same rule as is used reading hosts-files. 
+.B host-record
+options are considered to be read before host-files, so a name
+appearing there inhibits PTR-record creation if it appears in
+hosts-file also. Unlike hosts-files, names are not expanded, even when
+.B expand-hosts
+is in effect. Short and long names may appear in the same 
+.B host-record,
+eg. 
+.B --host-record=laptop,laptop.thekelleys.org,192.168.0.1,1234::100
+.TP
 .B \-Y, --txt-record=<name>[[,<text>],<text>]
 Return a TXT DNS record. The value of TXT record is a set of strings,
 so  any number may be included, delimited by commas; use quotes to put
@@ -429,21 +553,47 @@ Return an NAPTR DNS record, as specified in RFC3403.
 Return a CNAME record which indicates that <cname> is really
 <target>. There are significant limitations on the target; it must be a
 DNS name which is known to dnsmasq from /etc/hosts (or additional
-hosts files) or from DHCP. If the target does not satisfy this
+hosts files), from DHCP, from --interface-name or from another 
+.B --cname.
+If the target does not satisfy this
 criteria, the whole cname is ignored. The cname must be unique, but it
 is permissable to have more than one cname pointing to the same target.
 .TP
-.B --interface-name=<name>,<interface>
+.B --dns-rr=<name>,<RR-number>,[<hex data>]
+Return an arbitrary DNS Resource Record. The number is the type of the
+record (which is always in the C_IN class). The value of the record is
+given by the hex data, which may be of the form 01:23:45 or 01 23 45 or
+012345 or any mixture of these.
+.TP
+.B --interface-name=<name>,<interface>[/4|/6]
 Return a DNS record associating the name with the primary address on
-the given interface. This flag specifies an A record for the given
+the given interface. This flag specifies an A or AAAA record for the given
 name in the same way as an /etc/hosts line, except that the address is
-not constant, but taken from the given interface. If the interface is
+not constant, but taken from the given interface. The interface may be
+followed by "/4" or "/6" to specify that only IPv4 or IPv6 addresses
+of the interface should be used. If the interface is
 down, not configured or non-existent, an empty record is returned. The
 matching PTR record is also created, mapping the interface address to
 the name. More than one name may be associated with an interface
 address by repeating the flag; in that case the first instance is used
 for the reverse address-to-name mapping.
 .TP
+.B --synth-domain=<domain>,<address range>[,<prefix>]
+Create artificial A/AAAA and PTR records for an address range. The
+records use the address, with periods (or colons for IPv6) replaced
+with dashes.
+
+An example should make this clearer.
+.B --synth-domain=thekelleys.org.uk,192.168.0.0/24,internal-
+will result in a query for internal-192-168-0-56.thekelleys.org.uk returning
+192.168.0.56 and a reverse query vice versa. The same applies to IPv6,
+but IPv6 addresses may start with '::'
+but DNS labels may not start with '-' so in this case if no prefix is
+configured a zero is added in front of the label. ::1 becomes 0--1.
+
+The address range can be of the form
+<ip address>,<ip address> or <ip address>/<netmask>
+.TP
 .B --add-mac
 Add the MAC address of the requestor to DNS queries which are
 forwarded upstream. This may be used to DNS filtering by the upstream
@@ -451,7 +601,20 @@ server. The MAC address can only be added if the requestor is on the same
 subnet as the dnsmasq server. Note that the mechanism used to achieve this (an EDNS0 option)
 is not yet standardised, so this should be considered
 experimental. Also note that exposing MAC addresses in this way may
-have security and privacy implications. 
+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.
 .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.
@@ -467,21 +630,127 @@ Set the maximum number of concurrent DNS queries. The default value is
 where this needs to be increased is when using web-server log file
 resolvers, which can generate large numbers of concurrent queries.
 .TP
+.B --dnssec
+Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, dnsmasq requests the 
+DNSSEC records needed to validate the replies. The replies are validated and the result returned as 
+the Authenticated Data bit in the DNS packet. In addition the DNSSEC records are stored in the cache, making 
+validation by clients more efficient. Note that validation by clients is the most secure DNSSEC mode, but for
+clients unable to do validation, use of the AD bit set by dnsmasq is useful, provided that the network between 
+the dnsmasq server and the client is trusted. Dnsmasq must be compiled with HAVE_DNSSEC enabled, and DNSSEC
+trust anchors provided, see 
+.B --trust-anchor.
+Because the DNSSEC validation process uses the cache, it is not
+permitted to reduce the cache size below the default when DNSSEC is
+enabled. The nameservers upstream of dnsmasq must be DNSSEC-capable,
+ie capable of returning DNSSEC records with data. If they are not,
+then dnsmasq will not be able to determine the trusted status of
+answers. In the default mode, this menas that all replies will be
+marked as untrusted. If 
+.B --dnssec-check-unsigned
+is set and the upstream servers don't support DNSSEC, then DNS service will be entirely broken.
+.TP
+.B --trust-anchor=[<class>],<domain>,<key-tag>,<algorithm>,<digest-type>,<digest>
+Provide DS records to act a trust anchors for DNSSEC
+validation. Typically these will be the DS record(s) for 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 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
+legitimate: they are assumed to be valid and passed on (without the
+"authentic data" bit set, of course). This does not protect against an
+attacker forging unsigned replies for signed DNS zones, but it is
+fast. If this flag is set, dnsmasq will check the zones of unsigned
+replies, to ensure that unsigned replies are allowed in those
+zones. The cost of this is more upstream queries and slower
+performance. See also the warning about upstream servers in the
+section on 
+.B --dnssec
+.TP
+.B --dnssec-no-timecheck
+DNSSEC signatures are only valid for specified time windows, and should be rejected outside those windows. This generates an
+interesting chicken-and-egg problem for machines which don't have a hardware real time clock. For these machines to determine the correct 
+time typically requires use of NTP and therefore DNS, but validating DNS requires that the correct time is already known. Setting this flag
+removes the time-window checks (but not other DNSSEC validation.) only until the dnsmasq process receives SIGHUP. The intention is
+that dnsmasq should be started with this flag when the platform determines that reliable time is not currently available. As soon as 
+reliable time is established, a SIGHUP should be sent to dnsmasq, which enables time checking, and purges the cache of DNS records
+which have not been throughly checked.
+.TP
+.B --dnssec-timestamp=<path>
+Enables an alternative way of checking the validity of the system time for DNSSEC (see --dnssec-no-timecheck). In this case, the 
+system time is considered to be valid once it becomes later than the timestamp on the specified file. The file is created and 
+its timestamp set automatically by dnsmasq. The file must be stored on a persistent filesystem, so that it and its mtime are carried 
+over system restarts. The timestamp file is created after dnsmasq has dropped root, so it must be in a location writable by the 
+unprivileged user that dnsmasq runs as.
+.TP
 .B --proxy-dnssec
-A resolver on a client machine can do DNSSEC validation in two ways: it
-can perform the cryptograhic operations on the reply it receives, or
-it can rely on the upstream recursive nameserver to do the validation
-and set a bit in the reply if it succeeds. Dnsmasq is not a DNSSEC
-validator, so it cannot perform the validation role of the recursive nameserver,
-but it can pass through the validation results from its own upstream
-nameservers. This option enables this behaviour. You should only do
-this if you trust all the configured upstream nameservers 
-.I and the network between you and them.
-If you use the first DNSSEC mode, validating resolvers in clients,
-this option is not required. Dnsmasq always returns all the data
-needed for a client to do validation itself. 
-.TP
-.B \-F, --dhcp-range=[interface:<interface>,][tag:<tag>[,tag:<tag>],][set:<tag],]<start-addr>,<end-addr>[,<netmask>[,<broadcast>]][,<lease time>]
+Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients and cache it.  This is an 
+alternative to having dnsmasq validate DNSSEC, but it depends on the security of the network between 
+dnsmasq and the upstream servers, and the trustworthiness of the upstream servers.
+.TP
+.B --dnssec-debug
+Set debugging mode for the DNSSEC validation, set the Checking Disabled bit on upstream queries, 
+and don't convert replies which do not validate to responses with
+a return code of SERVFAIL. Note that
+setting this may affect DNS behaviour in bad ways, it is not an
+extra-logging flag and should not be set in production.
+.TP
+.B --auth-zone=<domain>[,<subnet>[/<prefix length>][,<subnet>[/<prefix length>].....]]
+Define a DNS zone for which dnsmasq acts as authoritative server. Locally defined DNS records which are in the domain
+will be served. If subnet(s) are given, A and AAAA records must be in one of the
+specified subnets.
+
+As alternative to directly specifying the subnets, it's possible to
+give the name of an interface, in which case the subnets implied by
+that interface's configured addresses and netmask/prefix-length are
+used; this is useful when using constructed DHCP ranges as the actual
+address is dynamic and not known when configuring dnsmasq. The
+interface addresses may be confined to only IPv6 addresses using
+<interface>/6 or to only IPv4 using <interface>/4. This is useful when
+an interface has dynamically determined global IPv6 addresses which should
+appear in the zone, but RFC1918 IPv4 addresses which should not.
+Interface-name and address-literal subnet specifications may be used
+freely in the same --auth-zone declaration.
+
+The subnet(s) are also used to define in-addr.arpa and
+ip6.arpa domains which are served for reverse-DNS queries. If not
+specified, the prefix length defaults to 24 for IPv4 and 64 for IPv6.
+For IPv4 subnets, the prefix length should be have the value 8, 16 or 24
+unless you are familiar with RFC 2317 and have arranged the
+in-addr.arpa delegation accordingly. Note that if no subnets are
+specified, then no reverse queries are answered.
+.TP
+.B --auth-soa=<serial>[,<hostmaster>[,<refresh>[,<retry>[,<expiry>]]]]
+Specify fields in the SOA record associated with authoritative
+zones. Note that this is optional, all the values are set to sane defaults.
+.TP
+.B --auth-sec-servers=<domain>[,<domain>[,<domain>...]]
+Specify any secondary servers for a zone for which dnsmasq is
+authoritative. These servers must be configured to get zone data from
+dnsmasq by zone transfer, and answer queries for the same
+authoritative zones as dnsmasq.
+.TP
+.B --auth-peer=<ip-address>[,<ip-address>[,<ip-address>...]]
+Specify the addresses of secondary servers which are allowed to
+initiate zone transfer (AXFR) requests for zones for which dnsmasq is
+authoritative. If this option is not given, then AXFR requests will be
+accepted from any secondary. 
+.TP 
+.B --conntrack
+Read the Linux connection track mark associated with incoming DNS
+queries and set the same mark value on upstream traffic used to answer
+those queries. This allows traffic generated by dnsmasq to be
+associated with the queries which cause it, useful for bandwidth
+accounting and firewalling. Dnsmasq must have conntrack support
+compiled in and the kernel must have conntrack support
+included and configured. This option cannot be combined with
+--query-port. 
+.TP
+.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-addr>[,<end-addr>][,<mode>][,<netmask>[,<broadcast>]][,<lease time>]
+.TP
+.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-IPv6addr>[,<end-IPv6addr>|constructor:<interface>][,<mode>][,<prefix-len>][,<lease time>]
+
 Enable the DHCP server. Addresses will be given out from the range
 <start-addr> to <end-addr> and from statically defined addresses given
 in 
@@ -490,29 +759,71 @@ options. If the lease time is given, then leases
 will be given for that length of time. The lease time is in seconds,
 or minutes (eg 45m) or hours (eg 1h) or "infinite". If not given,
 the default lease time is one hour. The
-minimum lease time is two minutes. This
-option may be repeated, with different addresses, to enable DHCP
+minimum lease time is two minutes. For IPv6 ranges, the lease time
+maybe "deprecated"; this sets the preferred lifetime sent in a DHCP
+lease or router advertisement to zero, which causes clients to use
+other addresses, if available, for new connections as a prelude to renumbering.
+
+This option may be repeated, with different addresses, to enable DHCP
 service to more than one network. For directly connected networks (ie,
 networks on which the machine running dnsmasq has an interface) the
-netmask is optional. It is, however, required for networks which
-receive DHCP service via a relay agent. The broadcast address is
+netmask is optional: dnsmasq will determine it from the interface
+configuration. For networks which receive DHCP service via a relay
+agent, dnsmasq cannot determine the netmask itself, so it should be
+specified, otherwise dnsmasq will have to guess, based on the class (A, B or
+C) of the network address. The broadcast address is
 always optional. It is always
 allowed to have more than one dhcp-range in a single subnet. 
 
+For IPv6, the parameters are slightly different: instead of netmask
+and broadcast address, there is an optional prefix length which must
+be equal to or larger then the prefix length on the local interface. If not
+given, this defaults to 64. Unlike the IPv4 case, the prefix length is not
+automatically derived from the interface configuration. The mimimum
+size of the prefix length is 64.
+
+IPv6 (only) supports another type of range. In this, the start address and optional end address contain only the network part (ie ::1) and they are followed by
+.B constructor:<interface>.
+This forms a template which describes how to create ranges, based on the addresses assigned to the interface. For instance
+
+.B --dhcp-range=::1,::400,constructor:eth0
+
+will look for addresses on
+eth0 and then create a range from <network>::1 to <network>::400. If
+the interface is assigned more than one network, then the
+corresponding ranges will be automatically created, and then
+deprecated and finally removed again as the address is deprecated and
+then deleted. The interface name may have a final "*" wildcard. Note
+that just any address on eth0 will not do: it must not be an
+autoconfigured or privacy address, or be deprecated.
+
+If a dhcp-range is only being used for stateless DHCP and/or SLAAC,
+then the address can be simply ::
+
+.B --dhcp-range=::,constructor:eth0
+
+
 The optional 
 .B set:<tag> 
 sets an alphanumeric label which marks this network so that
 dhcp options may be specified on a per-network basis. 
 When it is prefixed with 'tag:' instead, then its meaning changes from setting
-a tag to matching it. Only one tag may be set, but more than one tag may be matched.
-The end address may be replaced by the keyword 
+a tag to matching it. Only one tag may be set, but more than one tag
+may be matched.
+
+The optional <mode> keyword may be 
 .B static
 which tells dnsmasq to enable DHCP for the network specified, but not
 to dynamically allocate IP addresses: only hosts which have static
 addresses given via 
 .B dhcp-host
-or from /etc/ethers will be served. The end address may be replaced by
-the keyword
+or from /etc/ethers will be served. A static-only subnet with address
+all zeros may be used as a "catch-all" address to enable replies to all
+Information-request packets on a subnet which is provided with
+stateless DHCPv6, ie
+.B --dhcp-range=::,static
+
+For IPv4, the <mode> may be 
 .B proxy
 in which case dnsmasq will provide proxy-DHCP on the specified
 subnet. (See 
@@ -521,15 +832,57 @@ and
 .B pxe-service
 for details.)
 
-The interface:<interface name> section is not normally used. See the
-NOTES section for details of this.
+For IPv6, the mode may be some combination of
+.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,
+and not DHCP. 
+
+.B slaac
+tells dnsmasq to offer Router Advertisement on this subnet and to set
+the A bit in the router advertisement, so that the client will use
+SLAAC addresses. When used with a DHCP range or static DHCP address
+this results in the client having both a DHCP-assigned and a SLAAC
+address.
+
+.B ra-stateless
+sends router advertisements with the O and A bits set, and provides a
+stateless DHCP service. The client will use a SLAAC address, and use
+DHCP for other configuration information.
+
+.B ra-names
+enables a mode
+which gives DNS names to dual-stack hosts which do SLAAC for
+IPv6. Dnsmasq uses the host's IPv4 lease to derive the name, network
+segment and MAC address and assumes that the host will also have an
+IPv6 address calculated using the SLAAC algorithm, on the same network
+segment. The address is pinged, and if a reply is received, an AAAA
+record is added to the DNS for this IPv6
+address. Note that this is only happens for directly-connected
+networks, (not one doing DHCP via a relay) and it will not work 
+if a host is using privacy extensions. 
+.B ra-names
+can be combined  with 
+.B ra-stateless
+and
+.B slaac.
+
+.B ra-advrouter
+enables a mode where router address(es) rather than prefix(es) are included in the advertisements.
+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
 with a particular hardware address to be always allocated the same
 hostname, IP address and lease time. A hostname specified like this
 overrides any supplied by the DHCP client on the machine. It is also
-allowable to ommit the hardware address and include the hostname, in
+allowable to omit the hardware address and include the hostname, in
 which case the IP address and lease times will apply to any machine
 claiming that name. For example 
 .B --dhcp-host=00:20:e0:3b:13:af,wap,infinite 
@@ -547,14 +900,28 @@ the same subnet as some valid dhcp-range.  For
 subnets which don't need a pool of dynamically allocated addresses,
 use the "static" keyword in the dhcp-range declaration.
 
-It is allowed to use client identifiers rather than
+It is allowed to use client identifiers (called client
+DUID in IPv6-land rather than
 hardware addresses to identify hosts by prefixing with 'id:'. Thus: 
 .B --dhcp-host=id:01:02:03:04,..... 
 refers to the host with client identifier 01:02:03:04. It is also
 allowed to specify the client ID as text, like this:
 .B --dhcp-host=id:clientidastext,..... 
 
-The special option id:* means "ignore any client-id 
+A single
+.B dhcp-host 
+may contain an IPv4 address or an IPv6 address, or both. IPv6 addresses must be bracketed by square brackets thus:
+.B --dhcp-host=laptop,[1234::56]
+IPv6 addresses may contain only the host-identifier part:
+.B --dhcp-host=laptop,[::56]
+in which case they act as wildcards in constructed dhcp ranges, with
+the appropriate network part inserted. 
+Note that in IPv6 DHCP, the hardware address may not be
+available, though it normally is for direct-connected clients, or
+clients using DHCP relays which support RFC 6939.
+
+
+For DHCPv4, the  special option id:* means "ignore any client-id 
 and use MAC addresses only." This is useful when a client presents a client-id sometimes 
 but not others.
 
@@ -577,7 +944,7 @@ This is
 useful when there is another DHCP server on the network which should
 be used by some machines.
 
-The set:<tag> contruct sets the tag
+The set:<tag> construct sets the tag
 whenever this dhcp-host directive is in use. This can be used to 
 selectively send DHCP options just for this host. More than one tag
 can be set in a dhcp-host directive (but not in other places where
@@ -601,7 +968,7 @@ will only match a
 Token-Ring hardware address, since the ARP-address type for token ring
 is 6. 
 
-As a special case, it is possible to include more than one
+As a special case, in DHCPv4, it is possible to include more than one
 hardware address. eg:
 .B --dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.2
 This allows an IP address to be associated with
@@ -627,6 +994,18 @@ is given, then read all the files contained in that directory. The advantage of
 using this option is the same as for --dhcp-hostsfile: the
 dhcp-optsfile will be re-read when dnsmasq receives SIGHUP. Note that
 it is possible to encode the information in a
+.TP
+.B --dhcp-hostsdir=<path>
+This is equivalent to dhcp-hostsfile, except for the following. The path MUST be a
+directory, and not an individual file. Changed or new files within
+the directory are read automatically, without the need to send SIGHUP.
+If a file is deleted for changed after it has been read by dnsmasq, then the
+host record it contained will remain until dnsmasq recieves a SIGHUP, or 
+is restarted; ie host records are only added dynamically.
+.TP
+.B --dhcp-optsdir=<path>
+This is equivalent to dhcp-optsfile, with the differences noted for --dhcp-hostsdir.
+.TP
 .B --dhcp-boot
 flag as DHCP options, using the options names bootfile-name,
 server-ip-address and tftp-server. This allows these to be included
@@ -639,14 +1018,14 @@ hostname or dotted-quad IP address. When read by dnsmasq these lines
 have exactly the same effect as
 .B --dhcp-host
 options containing the same information. /etc/ethers is re-read when 
-dnsmasq receives SIGHUP.
+dnsmasq receives SIGHUP. IPv6 addresses are NOT read from /etc/ethers.
 .TP
-.B \-O, --dhcp-option=[tag:<tag>,[tag:<tag>,]][encap:<opt>,][vi-encap:<enterprise>,][vendor:[<vendor-class>],][<opt>|option:<opt-name>],[<value>[,<value>]]
+.B \-O, --dhcp-option=[tag:<tag>,[tag:<tag>,]][encap:<opt>,][vi-encap:<enterprise>,][vendor:[<vendor-class>],][<opt>|option:<opt-name>|option6:<opt>|option6:<opt-name>],[<value>[,<value>]]
 Specify different or extra options to DHCP clients. By default,
 dnsmasq sends some standard options to DHCP clients, the netmask and
 broadcast address are set to the same as the host running dnsmasq, and
 the DNS server and default route are set to the address of the machine
-running dnsmasq. If the domain name option has been set, that is sent.
+running dnsmasq. (Equivalent rules apply for IPv6.) If the domain name option has been set, that is sent.
 This configuration allows these defaults to be overridden,
 or other options specified. The option, to be sent may be given as a
 decimal number or as "option:<option-name>" The option numbers are
@@ -662,8 +1041,10 @@ and to set the time-server address to 192.168.0.4, do
 or 
 .B --dhcp-option = option:ntp-server, 192.168.0.4
 The special address 0.0.0.0 is taken to mean "the address of the
-machine running dnsmasq". Data types allowed are comma separated
-dotted-quad IP addresses, a decimal number, colon-separated hex digits
+machine running dnsmasq". 
+
+Data types allowed are comma separated
+dotted-quad IPv4 addresses, []-wrapped IPv6 addresses, a decimal number, colon-separated hex digits
 and a text string. If the optional tags are given then
 this option is only sent when all the tags are matched.
 
@@ -673,6 +1054,16 @@ to option 120 are handled as per RFC 3361. Dotted-quad IP addresses
 which are followed by a slash and then a netmask size are encoded as
 described in RFC 3442.
 
+IPv6 options are specified using the 
+.B option6:
+keyword, followed by the option number or option name. The IPv6 option
+name space is disjoint from the IPv4 option name space. IPv6 addresses
+in options must be bracketed with square brackets, eg. 
+.B --dhcp-option=option6:ntp-server,[1234::56]
+For IPv6, [::] means "the global address of
+the machine running dnsmasq", whilst [fd00::] is replaced with the
+ULA, if it exists, and [fe80::] with the link-local address.
+
 Be careful: no checking is done that the correct type of data for the
 option number is sent, it is quite possible to
 persuade dnsmasq to generate illegal DHCP packets with injudicious use
@@ -688,7 +1079,7 @@ literal string, use quotes. For instance when using option 66 to send
 a literal IP address as TFTP server name, it is necessary to do
 .B --dhcp-option=66,"1.2.3.4"
 
-Encapsulated Vendor-class options may also be specified using
+Encapsulated Vendor-class options may also be specified (IPv4 only) using
 --dhcp-option: for instance 
 .B --dhcp-option=vendor:PXEClient,1,0.0.0.0 
 sends the encapsulated vendor
@@ -700,9 +1091,9 @@ for selecting encapsulated options in preference to any sent by the
 client. It is
 possible to omit the vendorclass completely;
 .B --dhcp-option=vendor:,1,0.0.0.0
-in which case the encapsulated option is always sent.
+in which case the encapsulated option is always sent. 
 
-Options may be encapsulated within other options: for instance
+Options may be encapsulated (IPv4 only) within other options: for instance
 .B --dhcp-option=encap:175, 190, "iscsi-client0"
 will send option 175, within which is the option 190. If multiple
 options are given which are encapsulated with the same option number
@@ -713,8 +1104,9 @@ The final variant on encapsulated options is "Vendor-Identifying
 Vendor Options" as specified by RFC3925. These are denoted like this: 
 .B --dhcp-option=vi-encap:2, 10, "text"
 The number in the vi-encap: section is the IANA enterprise number
-used to identify this option.
-
+used to identify this option. This form of encapsulation is supported
+in IPv6.
 The address 0.0.0.0 is not treated specially in
 encapsulated options.
 .TP
@@ -726,14 +1118,46 @@ not ask for it in the parameter request list. This is sometimes
 needed, for example when sending options to PXELinux.
 .TP
 .B --dhcp-no-override
-Disable re-use of the DHCP servername and filename fields as extra
+(IPv4 only) Disable re-use of the DHCP servername and filename fields as extra
 option space. If it can, dnsmasq moves the boot server and filename
 information (from dhcp-boot) out of their dedicated fields into
 DHCP options. This make extra space available in the DHCP packet for
 options but can, rarely, confuse old or broken clients. This flag
 forces "simple and safe" behaviour to avoid problems in such a case.
 .TP
-.B \-U, --dhcp-vendorclass=set:<tag>,<vendor-class>
+.B --dhcp-relay=<local address>,<server address>[,<interface]
+Configure dnsmasq to do DHCP relay. The local address is an address
+allocated to an interface on the host running dnsmasq. All DHCP
+requests arriving on that interface will we relayed to a remote DHCP
+server at the server address. It is possible to relay from a single local
+address to multiple remote servers by using multiple dhcp-relay
+configs with the same local address and different server
+addresses. A server address must be an IP literal address, not a
+domain name. In the case of DHCPv6, the server address may be the
+ALL_SERVERS multicast address, ff05::1:3. In this case the interface
+must be given, not be wildcard, and is used to direct the multicast to the
+correct interface to reach the DHCP server. 
+
+Access control for DHCP clients has the same rules as for the DHCP
+server, see --interface, --except-interface, etc. The optional
+interface name in the dhcp-relay config has a different function: it
+controls on which interface DHCP replies from the server will be
+accepted. This is intended for configurations which have three
+interfaces: one being relayed from, a second connecting the DHCP
+server, and a third untrusted network, typically the wider
+internet. It avoids the possibility of spoof replies arriving via this
+third interface.
+
+It is allowed to have dnsmasq act as a DHCP server on one set of
+interfaces and relay from a disjoint set of interfaces. Note that
+whilst it is quite possible to write configurations which appear to
+act as a server and a relay on the same interface, this is not
+supported: the relay function will take precedence.
+
+Both DHCPv4 and DHCPv6 relay is supported. It's not possible to relay
+DHCPv4 to a DHCPv6 server or vice-versa.
+.TP
+.B \-U, --dhcp-vendorclass=set:<tag>,[enterprise:<IANA-enterprise number>,]<vendor-class>
 Map from a vendor-class string to a tag. Most DHCP clients provide a 
 "vendor class" which represents, in some sense, the type of host. This option 
 maps vendor classes to tags, so that DHCP options may be selectively delivered
@@ -743,7 +1167,13 @@ will allow options to be set only for HP printers like so:
 .B --dhcp-option=tag:printers,3,192.168.4.4 
 The vendor-class string is
 substring matched against the vendor-class supplied by the client, to
-allow fuzzy matching. The set: prefix is optional but allowed for consistency.
+allow fuzzy matching. The set: prefix is optional but allowed for
+consistency. 
+
+Note that in IPv6 only, vendorclasses are namespaced with an 
+IANA-allocated enterprise number. This is given with enterprise:
+keyword and specifies that only vendorclasses matching the specified
+number should be searched.
 .TP
 .B \-j, --dhcp-userclass=set:<tag>,<user-class>
 Map from a user-class string to a tag (with substring
@@ -765,16 +1195,19 @@ Map from RFC3046 relay agent options to tags. This data may
 be provided by DHCP relay agents. The circuit-id or remote-id is
 normally given as colon-separated hex, but is also allowed to be a
 simple string. If an exact match is achieved between the circuit or
-agent ID and one provided by a relay agent, the tag is set.
+agent ID and one provided by a relay agent, the tag is set. 
+
+.B dhcp-remoteid
+(but not dhcp-circuitid) is supported in IPv6. 
 .TP
 .B --dhcp-subscrid=set:<tag>,<subscriber-id>
-Map from RFC3993 subscriber-id relay agent options to tags.
+(IPv4 and IPv6) Map from RFC3993 subscriber-id relay agent options to tags.
 .TP
 .B --dhcp-proxy[=<ip addr>]......
-A normal DHCP relay agent is only used to forward the initial parts of
+(IPv4 only) A normal DHCP relay agent is only used to forward the initial parts of
 a DHCP interaction to the DHCP server. Once a client is configured, it
 communicates directly with the server. This is undesirable if the
-relay agent is addding extra information to the DHCP packets, such as
+relay agent is adding extra information to the DHCP packets, such as
 that used by
 .B dhcp-circuitid
 and
@@ -791,7 +1224,7 @@ relays at those addresses are affected.
 Without a value, set the tag if the client sends a DHCP
 option of the given number or name. When a value is given, set the tag only if
 the option is sent and matches the value. The value may be of the form
-"01:ff:*:02" in which case the value must match (apart from widcards)
+"01:ff:*:02" in which case the value must match (apart from wildcards)
 but the option sent may have unmatched data past the end of the
 value. The value may also be of the same form as in 
 .B dhcp-option
@@ -804,7 +1237,7 @@ will set the tag "efi-ia32" if the the number 6 appears in the list of
 architectures sent by the client in option 93. (See RFC 4578 for
 details.)  If the value is a string, substring matching is used.
 
-The special form with vi-encap:<enterpise number> matches against
+The special form with vi-encap:<enterprise number> matches against
 vendor-identifying vendor classes for the specified enterprise. Please
 see RFC 3925 for more details of these rare and interesting beasts.
 .TP
@@ -831,22 +1264,22 @@ dhcp-host configuration in dnsmasq and the contents of /etc/hosts and
 /etc/ethers.
 .TP
 .B --dhcp-generate-names=tag:<tag>[,tag:<tag>]
-Generate a name for DHCP clients which do not otherwise have one,
-using the MAC address expressed in hex, seperated by dashes. Note that
+(IPv4 only) Generate a name for DHCP clients which do not otherwise have one,
+using the MAC address expressed in hex, separated by dashes. Note that
 if a host provides a name, it will be used by preference to this,
 unless 
 .B --dhcp-ignore-names 
 is set.
 .TP
 .B --dhcp-broadcast[=tag:<tag>[,tag:<tag>]]
-When all the given tags appear in the tag set, always use broadcast to
+(IPv4 only) When all the given tags appear in the tag set, always use broadcast to
 communicate with the host when it is unconfigured. It is permissible
 to supply no tags, in which case this is unconditional. Most DHCP clients which
 need broadcast replies set a flag in their requests so that this
 happens automatically, some old BOOTP clients do not.
 .TP
-.B \-M, --dhcp-boot=[tag:<tag>,]<filename>,[<servername>[,<server address>]]
-Set BOOTP options to be returned by the DHCP server. Server name and
+.B \-M, --dhcp-boot=[tag:<tag>,]<filename>,[<servername>[,<server address>|<tftp_servername>]]
+(IPv4 only) Set BOOTP options to be returned by the DHCP server. Server name and
 address are optional: if not provided, the name is left empty, and the
 address set to the address of the machine running dnsmasq. If dnsmasq
 is providing a TFTP service (see 
@@ -854,8 +1287,25 @@ is providing a TFTP service (see
 ) then only the filename is required here to enable network booting.
 If the optional tag(s) are given,
 they must match for this configuration to be sent. 
-.TP
-.B --pxe-service=[tag:<tag>,]<CSA>,<menu text>[,<basename>|<bootservicetype>][,<server address>]
+Instead of an IP address, the TFTP server address can be given as a domain
+name which is looked up in /etc/hosts. This name can be associated in
+/etc/hosts with multiple IP addresses, which are used round-robin.
+This facility can be used to load balance the tftp load among a set of servers.
+.TP
+.B --dhcp-sequential-ip
+Dnsmasq is designed to choose IP addresses for DHCP clients using a
+hash of the client's MAC address. This normally allows a client's
+address to remain stable long-term, even if the client  sometimes allows its DHCP
+lease to expire. In this default mode IP addresses are distributed
+pseudo-randomly over the entire available address range. There are
+sometimes circumstances (typically server deployment) where it is more
+convenient to have IP
+addresses allocated sequentially, starting from the lowest available
+address, and setting this flag enables this mode. Note that in the
+sequential mode, clients which allow a lease to expire are much more
+likely to move IP address; for this reason it should not be generally used.
+.TP
+.B --pxe-service=[tag:<tag>,]<CSA>,<menu text>[,<basename>|<bootservicetype>][,<server address>|<server_name>]
 Most uses of PXE boot-ROMS simply allow the PXE
 system to obtain an IP address and then download the file specified by
 .B dhcp-boot
@@ -871,17 +1321,19 @@ parameter after the menu text may be a file name, in which case dnsmasq acts as
 boot server and directs the PXE client to download the file by TFTP,
 either from itself (
 .B enable-tftp 
-must be set for this to work) or another TFTP server if the final IP
-address is given.
+must be set for this to work) or another TFTP server if the final server
+address/name is given.
 Note that the "layer"
 suffix (normally ".0") is supplied by PXE, and should not be added to
 the basename. If an integer boot service type, rather than a basename
 is given, then the PXE client will search for a
 suitable boot service for that type on the network. This search may be done
-by broadcast, or direct to a server if its IP address is provided.  
+by broadcast, or direct to a server if its IP address/name is provided.  
 If no boot service type or filename is provided (or a boot service type of 0 is specified)
 then the menu entry will abort the net boot procedure and
-continue booting from local media.
+continue booting from local media. The server address can be given as a domain
+name which is looked up in /etc/hosts. This name can be associated in
+/etc/hosts with multiple IP addresses, which are used round-robin.
 .TP
 .B --pxe-prompt=[tag:<tag>,]<prompt>[,<timeout>]
 Setting this provides a prompt to be displayed after PXE boot. If the
@@ -890,7 +1342,7 @@ timeout has elapsed with no keyboard input, the first available menu
 option will be automatically executed. If the timeout is zero then the first available menu
 item will be executed immediately. If 
 .B pxe-prompt
-is ommitted the system will wait for user input if there are multiple
+is omitted the system will wait for user input if there are multiple
 items in the menu, but boot immediately if
 there is only one. See
 .B pxe-service 
@@ -915,14 +1367,15 @@ process.
 .TP
 .B \-K, --dhcp-authoritative
 Should be set when dnsmasq is definitely the only DHCP server on a network.
-It changes the behaviour from strict RFC compliance so that DHCP requests on
+For DHCPv4, it changes the behaviour from strict RFC compliance so that DHCP requests on
 unknown leases from unknown hosts are not ignored. This allows new hosts
 to get a lease without a tedious timeout under all circumstances. It also 
 allows dnsmasq to rebuild its lease database without each client needing to 
-reacquire a lease, if the database is lost.
+reacquire a lease, if the database is lost. For DHCPv6 it sets the
+priority in replies to 255 (the maximum) instead of 0 (the minimum).
 .TP
 .B --dhcp-alternate-port[=<server port>[,<client port>]]
-Change the ports used for DHCP from the default. If this option is
+(IPv4 only) Change the ports used for DHCP from the default. If this option is
 given alone, without arguments, it changes the ports used for DHCP
 from 67 and 68 to 1067 and 1068. If a single argument is given, that
 port number is used for the server and the port number plus one used
@@ -930,7 +1383,7 @@ for the client. Finally, two port numbers allows arbitrary
 specification of both server and client ports for DHCP.
 .TP
 .B \-3, --bootp-dynamic[=<network-id>[,<network-id>]]
-Enable dynamic allocation of IP addresses to BOOTP clients. Use this
+(IPv4 only) Enable dynamic allocation of IP addresses to BOOTP clients. Use this
 with care, since each address allocated to a BOOTP client is leased
 forever, and therefore becomes permanently unavailable for re-use by
 other hosts. if this is given without tags, then it unconditionally
@@ -938,7 +1391,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
-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
@@ -948,16 +1401,32 @@ tried. This flag disables this check. Use with caution.
 Extra logging for DHCP: log all the options sent to DHCP clients and
 the tags used to determine them.
 .TP
+.B --quiet-dhcp, --quiet-dhcp6, --quiet-ra
+Suppress logging of the routine operation of these protocols. Errors and
+problems will still be logged. --quiet-dhcp and quiet-dhcp6 are
+over-ridden by --log-dhcp.
+.TP
 .B \-l, --dhcp-leasefile=<path>
 Use the specified file to store DHCP lease information.
 .TP 
+.B --dhcp-duid=<enterprise-id>,<uid>
+(IPv6 only) Specify the server persistent UID which the DHCPv6 server
+will use. This option is not normally required as dnsmasq creates a
+DUID automatically when it is first needed. When given, this option
+provides dnsmasq the data required to create a DUID-EN type DUID. Note
+that once set, the DUID is stored in the lease database, so to change between DUID-EN and
+automatically created DUIDs or vice-versa, the lease database must be
+re-intialised. The enterprise-id is assigned by IANA, and the uid is a
+string of hex octets unique to a particular device.
+.TP
 .B \-6 --dhcp-script=<path>
-Whenever a new DHCP lease is created, or an old one destroyed, the
+Whenever a new DHCP lease is created, or an old one destroyed, or a
+TFTP file transfer completes, the
 executable specified by this option is run.  <path>
 must be an absolute pathname, no PATH search occurs. 
 The arguments to the process
 are "add", "old" or "del", the MAC
-address of the host, the IP address, and the hostname,
+address of the host (or DUID for IPv6) , the IP address, and the hostname,
 if known. "add" means a lease has been created, "del" means it has
 been destroyed, "old" is a notification of an existing lease when
 dnsmasq starts or a change to MAC address or hostname of an existing
@@ -968,21 +1437,17 @@ token ring. The process is run as root (assuming that dnsmasq was originally run
 root) even if dnsmasq is configured to change UID to an unprivileged user.
 
 The environment is inherited from the invoker of dnsmasq, with some or
-all of the following variables added.
+all of the following variables added
 
-DNSMASQ_CLIENT_ID if the host provided a client-id.
+For both IPv4 and IPv6:
 
 DNSMASQ_DOMAIN if the fully-qualified domain name of the host is
 known, this is set to the  domain part. (Note that the hostname passed
 to the script as an argument is never fully-qualified.)
 
-If the client provides vendor-class, hostname or user-class, 
-these are provided in DNSMASQ_VENDOR_CLASS
-DNSMASQ_SUPPLIED_HOSTNAME and 
-DNSMASQ_USER_CLASS0..DNSMASQ_USER_CLASSn variables, but only for
-"add" actions or "old" actions when a host resumes an existing lease,
-since these data are not held in dnsmasq's lease
-database.
+If the client provides a hostname, DNSMASQ_SUPPLIED_HOSTNAME
+
+If the client provides user-classes, DNSMASQ_USER_CLASS0..DNSMASQ_USER_CLASSn 
 
 If dnsmasq was compiled with HAVE_BROKEN_RTC, then
 the length of the lease (in seconds) is stored in
@@ -1006,6 +1471,41 @@ is known.
 DNSMASQ_TAGS contains all the tags set during the
 DHCP transaction, separated by spaces.
 
+DNSMASQ_LOG_DHCP is set if
+.B --log-dhcp
+is in effect.
+
+For IPv4 only:
+
+DNSMASQ_CLIENT_ID if the host provided a client-id.
+
+DNSMASQ_CIRCUIT_ID, DNSMASQ_SUBSCRIBER_ID, DNSMASQ_REMOTE_ID if a
+DHCP relay-agent added any of these options.
+If the client provides vendor-class, DNSMASQ_VENDOR_CLASS.
+
+For IPv6 only:
+
+If the client provides vendor-class, DNSMASQ_VENDOR_CLASS_ID,
+containing the IANA enterprise id for the class, and
+DNSMASQ_VENDOR_CLASS0..DNSMASQ_VENDOR_CLASSn for the data.
+
+DNSMASQ_SERVER_DUID containing the DUID of the server: this is the same for
+every call to the script.
+
+DNSMASQ_IAID containing the IAID for the lease. If the lease is a
+temporary allocation, this is prefixed to 'T'.
+
+DNSMASQ_MAC containing the MAC address of the client, if known.
+
+Note that the supplied hostname, vendorclass and userclass data is
+only  supplied for
+"add" actions or "old" actions when a host resumes an existing lease,
+since these data are not held in dnsmasq's lease
+database.
+
+
+
 All file descriptors are
 closed except stdin, stdout and stderr which are open to /dev/null
 (except in debug mode).
@@ -1024,9 +1524,66 @@ all existing leases as they are read from the lease file. Expired
 leases will be called with "del" and others with "old". When dnsmasq
 receives a HUP signal, the script will be invoked for existing leases
 with an "old " event.
+
+
+There are two further actions which may appear as the first argument
+to the script, "init" and "tftp". More may be added in the future, so
+scripts should be written to ignore unknown actions. "init" is
+described below in 
+.B --leasefile-ro
+The "tftp" action is invoked when a TFTP file transfer completes: the
+arguments are the file size in bytes, the address to which the file
+was sent, and the complete pathname of the file.
+.TP
+.B --dhcp-luascript=<path>
+Specify a script written in Lua, to be run when leases are created,
+destroyed or changed. To use this option, dnsmasq must be compiled
+with the correct support. The Lua interpreter is intialised once, when
+dnsmasq starts, so that global variables persist between lease
+events. The Lua code must define a
+.B lease
+function, and may provide
+.B init
+and
+.B shutdown
+functions, which are called, without arguments when dnsmasq starts up
+and terminates. It may also provide a 
+.B tftp
+function.
+
+The 
+.B lease
+function receives the information detailed in 
+.B --dhcp-script. 
+It gets two arguments, firstly the action, which is a string
+containing, "add", "old" or "del", and secondly a table of tag value
+pairs. The tags mostly correspond to the environment variables
+detailed above, for instance the tag "domain" holds the same data as
+the environment variable DNSMASQ_DOMAIN. There are a few extra tags
+which hold the data supplied as arguments to
+.B --dhcp-script. 
+These are 
+.B mac_address, ip_address
+and 
+.B hostname
+for IPv4, and 
+.B client_duid, ip_address
+and 
+.B hostname
+for IPv6.
+
+The  
+.B tftp
+function is called in the same way as the lease function, and the
+table holds the tags 
+.B destination_address,
+.B file_name
+and 
+.B file_size.
 .TP
 .B --dhcp-scriptuser
-Specify the user as which to run the lease-change script. This defaults to root, but can be changed to another user using this flag. 
+Specify the user as which to run the lease-change script or Lua script. This defaults to root, but can be changed to another user using this flag. 
 .TP 
 .B \-9, --leasefile-ro
 Completely suppress use of the lease database file. The file will not
@@ -1043,10 +1600,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.
+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 
@@ -1089,7 +1649,7 @@ In the default mode, dnsmasq inserts the unqualified names of
 DHCP clients into the DNS. For this reason, the names must be unique,
 even if two clients which have the same name are in different
 domains. If a second DHCP client appears which has the same name as an
-existing client, the name is transfered to the new client. If 
+existing client, the name is transferred to the new client. If 
 .B --dhcp-fqdn
 is set, this behaviour changes: the unqualified name is no longer
 put in the DNS, only the qualified name. Two DHCP clients with the
@@ -1101,12 +1661,52 @@ without an address specified when
 .B --dhcp-fqdn 
 is set.
 .TP
-.B --enable-tftp[=<interface>]
+.B --dhcp-client-update
+Normally, when giving a DHCP lease, dnsmasq sets flags in the FQDN
+option to tell the client not to attempt a DDNS update with its name
+and IP address. This is because the name-IP pair is automatically
+added into dnsmasq's DNS view. This flag suppresses that behaviour,
+this is useful, for instance, to allow Windows clients to update
+Active Directory servers. See RFC 4702 for details. 
+.TP
+.B --enable-ra
+Enable dnsmasq's IPv6 Router Advertisement feature. DHCPv6 doesn't
+handle complete network configuration in the same way as DHCPv4. Router
+discovery and (possibly) prefix discovery for autonomous address
+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
+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.
+.TP
+.B --ra-param=<interface>,[high|low],[[<ra-interval>],<router lifetime>]
+Set non-default values for router advertisements sent via an
+interface. The priority field for the router may be altered from the
+default of medium with eg
+.B --ra-param=eth0,high.
+The interval between router advertisements may be set (in seconds) with 
+.B --ra-param=eth0,60.
+The lifetime of the route may be changed or set to zero, which allows
+a router to advertise prefixes but not a route via itself. 
+.B --ra-parm=eth0,0,0
+(A value of zero for the interval means the default value.) All three parameters may be set at once.
+.B --ra-param=low,60,1200
+The interface field may include a wildcard.
+.TP
+.B --enable-tftp[=<interface>[,<interface>]]
 Enable the TFTP server function. This is deliberately limited to that
 needed to net-boot a client. Only reading is allowed; the tsize and
 blksize extensions are supported (tsize is only supported in octet
-mode). See NOTES section for use of the interface argument.
-
+mode). Without an argument, the TFTP service is provided to the same set of interfaces as DHCP service. 
+If the list of interfaces is provided, that defines which interfaces recieve TFTP service.
 .TP
 .B --tftp-root=<directory>[,<interface>]
 Look for files to transfer using TFTP relative to the given
@@ -1116,6 +1716,9 @@ Absolute paths (starting with /) are allowed, but they must be within
 the tftp-root. If the optional interface argument is given, the
 directory is only used for TFTP requests via that interface.
 .TP
+.B --tftp-no-fail
+Do not abort startup if specified tftp root directories are inaccessible.
+.TP
 .B --tftp-unique-root
 Add the IP address of the TFTP client as a path component on the end
 of the TFTP-root (in standard dotted-quad format). Only valid if a
@@ -1134,6 +1737,12 @@ are accessible. It is not recommended to run dnsmasq as root with TFTP
 enabled, and certainly not without specifying --tftp-root. Doing so
 can expose any world-readable file on the server to any host on the net. 
 .TP
+.B --tftp-lowercase
+Convert filenames in TFTP requests to all lowercase. This is useful
+for requests from Windows machines, which have case-insensitive
+filesystems and tend to play fast-and-loose with case in filenames.
+Note that dnsmasq's tftp server always converts "\\" to "/" in filenames.
+.TP
 .B --tftp-max=<connections>
 Set the maximum number of concurrent TFTP connections allowed. This
 defaults to 50. When serving a large number of TFTP connections,
@@ -1165,12 +1774,23 @@ Specify a different configuration file. The conf-file option is also allowed in
 configuration files, to include multiple configuration files. A
 filename of "-" causes dnsmasq to read configuration from stdin.
 .TP
-.B \-7, --conf-dir=<directory>[,<file-extension>......]
+.B \-7, --conf-dir=<directory>[,<file-extension>......],
 Read all the files in the given directory as configuration
 files. If extension(s) are given, any files which end in those
 extensions are skipped. Any files whose names end in ~ or start with . or start and end
-with # are always skipped. This flag may be given on the command
-line or in a configuration file.
+with # are always skipped. If the extension starts with * then only files 
+which have that extension are loaded. So
+.B --conf-dir=/path/to/dir,*.conf
+loads all files with the suffix .conf in /path/to/dir. This flag may be given on the command
+line or in a configuration file. If giving it on the command line, be sure to
+escape * characters.
+.TP
+.B --servers-file=<file>
+A special case of 
+.B --conf-file
+which differs in two respects. Firstly, only --server and --rev-server are allowed 
+in the configuration file included. Secondly, the file is re-read and the configuration
+therein is updated when dnsmasq recieves SIGHUP.
 .SH CONFIG FILE
 At startup, dnsmasq reads
 .I /etc/dnsmasq.conf,
@@ -1196,7 +1816,8 @@ clears its cache and then re-loads
 .I /etc/hosts
 and 
 .I /etc/ethers 
-and any file given by --dhcp-hostsfile, --dhcp-optsfile or --addn-hosts.
+and any file given by --dhcp-hostsfile, --dhcp-hostsdir, --dhcp-optsfile, 
+--dhcp-optsdir, --addn-hosts or --hostsdir.
 The dhcp lease change script is called for all
 existing DHCP leases. If 
 .B
@@ -1211,12 +1832,22 @@ When it receives a SIGUSR1,
 writes statistics to the system log. It writes the cache size,
 the number of names which have had to removed from the cache before
 they expired in order to make room for new names and the total number
-of names that have been inserted into the cache. For each upstream
+of names that have been inserted into the cache. The number of cache hits and 
+misses and the number of authoritative queries answered are also given. For each upstream
 server it gives the number of queries sent, and the number which
 resulted in an error. In 
 .B --no-daemon
 mode or when full logging is enabled (-q), a complete dump of the
-contents of the cache is made.
+contents of the cache is made. 
+
+The cache statistics are also available in the DNS as answers to 
+queries of class CHAOS and type TXT in domain bind. The domain names are cachesize.bind, insertions.bind, evictions.bind, 
+misses.bind, hits.bind, auth.bind and servers.bind. An example command to query this, using the 
+.B dig
+utility would be
+
+dig +short chaos txt cachesize.bind
+
 .PP 
 When it receives SIGUSR2 and it is logging direct to a file (see
 .B --log-facility
@@ -1312,7 +1943,7 @@ used to allocate the address, one from any matching
 The tag "bootp" is set for BOOTP requests, and a tag whose name is the 
 name of the interface on which the request arrived is also set.
 
-Any configuration lines which includes one or more tag:<tag> contructs
+Any configuration lines which include one or more tag:<tag> constructs
 will only be valid if all that tags are matched in the set derived
 above. Typically this is dhcp-option.
 .B dhcp-option 
@@ -1320,10 +1951,21 @@ which has tags will be used in preference  to an untagged
 .B dhcp-option,
 provided that _all_ the tags match somewhere in the
 set collected as described above. The prefix '!' on a tag means 'not'
-so --dhcp=option=tag:!purple,3,1.2.3.4 sends the option when the
+so --dhcp-option=tag:!purple,3,1.2.3.4 sends the option when the
 tag purple is not in the set of valid tags. (If using this in a
 command line rather than a configuration file, be sure to escape !,
 which is a shell metacharacter)
+
+When selecting dhcp-options, a tag from dhcp-range is second class
+relative to other tags, to make it easy to override options for
+individual hosts, so 
+.B dhcp-range=set:interface1,......
+.B dhcp-host=set:myhost,.....
+.B dhcp-option=tag:interface1,option:nis-domain,"domain1"
+.B dhcp-option=tag:myhost,option:nis-domain,"domain2"
+will set the NIS-domain to domain1 for hosts in the range, but
+override that to domain2 for a particular host.
+
 .PP
 Note that for 
 .B dhcp-range 
@@ -1353,51 +1995,170 @@ parameter in a BOOTP request is used as a tag,
 as is the tag "bootp", allowing some control over the options returned to
 different classes of hosts.
 
-.B dhcp-range
-may have an interface name supplied as
-"interface:<interface-name>". The semantics if this are as follows:
-For DHCP, if any other dhcp-range exists _without_ an interface name,
-then the interface name is ignored and and dnsmasq behaves as if the
-interface parts did not exist, otherwise DHCP is only provided to 
-interfaces mentioned in dhcp-range
-declarations. For DNS, if there are no
-.B --interface
-or 
-.B --listen-address
-flags, behaviour is unchanged by the interface part. If either of
-these flags are present, the interfaces mentioned in
-dhcp-ranges are added to the set which get DNS service.
-
-Similarly, 
-.B enable-tftp
-may take an interface name, which enables TFTP only for a particular
-interface, ignoring 
-.B --interface 
-or
-.B --listen-address
-flags. In addition 
-.B --tftp-secure
-and
-.B --tftp-unique-root
+.SH AUTHORITATIVE CONFIGURATION
+.PP 
+Configuring dnsmasq to act as an authoritative DNS server is
+complicated by the fact that it involves configuration of external DNS
+servers to provide delegation. We will walk through three scenarios of
+increasing complexity. Prerequisites for all of these scenarios
+are a globally accessible IP address, an A or AAAA record pointing to that address,
+and an external DNS server capable of doing delegation of the zone in
+question. For the first part of this explanation, we will call the A (or AAAA) record
+for the globally accessible address server.example.com, and the zone
+for which dnsmasq is authoritative our.zone.com.
+
+The simplest configuration consists of two lines of dnsmasq configuration; something like
+
+.nf
+.B auth-server=server.example.com,eth0
+.B auth-zone=our.zone.com,1.2.3.0/24
+.fi
+
+and two records in the external DNS
+
+.nf
+server.example.com       A    192.0.43.10
+our.zone.com            NS    server.example.com
+.fi
+
+eth0 is the external network interface on which dnsmasq is listening,
+and has (globally accessible) address 192.0.43.10. 
+
+Note that the external IP address may well be dynamic (ie assigned
+from an ISP by DHCP or PPP) If so, the A record must be linked to this
+dynamic assignment by one of the usual dynamic-DNS systems.
+
+A more complex, but practically useful configuration has the address
+record for the globally accessible IP address residing in the
+authoritative zone which dnsmasq is serving, typically at the root. Now
+we have
+
+.nf
+.B auth-server=our.zone.com,eth0
+.B auth-zone=our.zone.com,1.2.3.0/24
+.fi
+
+.nf
+our.zone.com             A    1.2.3.4
+our.zone.com            NS    our.zone.com
+.fi
+
+The A record for our.zone.com has now become a glue record, it solves
+the chicken-and-egg problem of finding the IP address of the
+nameserver for our.zone.com when the A record is within that
+zone. Note that this is the only role of this record: as dnsmasq is
+now authoritative from our.zone.com it too must provide this
+record. If the external address is static, this can be done with an
+.B /etc/hosts 
+entry or 
+.B --host-record.
+
+.nf
+.B auth-server=our.zone.com,eth0
+.B host-record=our.zone.com,1.2.3.4
+.B auth-zone=our.zone.com,1.2.3.0/24
+.fi
+
+If the external address is dynamic, the address
+associated with our.zone.com must be derived from the address of the
+relevant interface. This is done using 
+.B interface-name
+Something like:
+
+.nf
+.B auth-server=our.zone.com,eth0
+.B interface-name=our.zone.com,eth0
+.B auth-zone=our.zone.com,1.2.3.0/24,eth0
+.fi
+
+(The "eth0" argument in auth-zone adds the subnet containing eth0's
+dynamic address to the zone, so that the interface-name returns the
+address in outside queries.)
+
+Our final configuration builds on that above, but also adds a
+secondary DNS server. This is another DNS server which learns the DNS data
+for the zone by doing zones transfer, and acts as a backup should
+the primary server become inaccessible. The configuration of the
+secondary is beyond the scope of this man-page, but the extra
+configuration of dnsmasq is simple:
+
+.nf
+.B auth-sec-servers=secondary.myisp.com
+.fi
+
 and
-.B --tftp-no-blocksize
-are ignored for requests from such interfaces. (A 
-.B --tftp-root
-directive giving a root path and an interface should be 
-provided too.)
-
-These rules may seem odd at first sight, but  they
-allow a single line  of the form "dhcp-range=interface:virt0,192.168.0.4,192.168.0.200"
-to be added to dnsmasq configuration which then supplies
-DHCP and DNS services to that interface, without affecting
-what services are supplied to other interfaces and irrespective of 
-the existance or lack of "interface=<interface>" 
-lines elsewhere in the dnsmasq configuration.
-"enable-tftp=virt0" and "tftp-root=<root>,virt0" do the same job for TFTP.
- The idea is
-that such a line can be added automatically by libvirt
-or equivalent systems, without disturbing any manual
-configuration.
+
+.nf
+our.zone.com           NS    secondary.myisp.com
+.fi
+
+Adding auth-sec-servers enables zone transfer in dnsmasq, to allow the
+secondary to collect the DNS data. If you wish to restrict this data
+to particular hosts then
+
+.nf
+.B auth-peer=<IP address of secondary>
+.fi
+
+will do so.
+
+Dnsmasq acts as an authoritative server for  in-addr.arpa and
+ip6.arpa domains associated with the subnets given in auth-zone
+declarations, so reverse (address to name) lookups can be simply
+configured with a suitable NS record, for instance in this example,
+where we allow 1.2.3.0/24 addresses.
+
+.nf
+ 3.2.1.in-addr.arpa  NS    our.zone.com
+.fi
+
+Note that at present, reverse (in-addr.arpa and ip6.arpa) zones are
+not available in zone transfers, so there is no point arranging
+secondary servers for reverse lookups.
+
+.PP
+When dnsmasq is configured to act as an authoritative server, the
+following data is used to populate the authoritative zone.
+.PP
+.B --mx-host, --srv-host, --dns-rr, --txt-record, --naptr-record
+, as long as the record names are in the authoritative domain.
+.PP
+.B --cname
+as long as the record name is in  the authoritative domain. If the
+target of the CNAME is unqualified, then it  is qualified with the
+authoritative zone name.
+.PP
+IPv4 and IPv6 addresses from /etc/hosts (and 
+.B --addn-hosts
+) and
+.B --host-record
+and 
+.B --interface-name
+provided the address falls into one of the subnets specified in the
+.B --auth-zone.
+.PP
+Addresses of DHCP leases, provided the address falls into one of the subnets specified in the
+.B --auth-zone.
+(If contructed DHCP ranges are is use, which depend on the address dynamically 
+assigned to an interface, then the form of
+.B --auth-zone
+which defines subnets by the dynamic address of an interface should
+be used to ensure this condition is met.)
+.PP 
+In the default mode, where a DHCP lease
+has an unqualified name, and possibly a qualified name constructed
+using 
+.B --domain
+then the name in the authoritative zone is constructed from the
+unqualified name and the zone's domain. This may or may not equal
+that specified by 
+.B --domain.
+If 
+.B --dhcp-fqdn
+is set, then the fully qualified names associated with DHCP leases are
+used, and must match the zone's domain.
+
 
 .SH EXIT CODES
 .PP
index 65e4b72..81c745a 100644 (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
index 44c301b..b4cc16d 100644 (file)
@@ -6,23 +6,40 @@ Dnsmasq \- Un serveur DHCP et cache DNS poids-plume.
 .I [OPTION]...
 .SH "DESCRIPTION"
 .BR dnsmasq
-est un serveur DHCP et DNS à faible empreinte mémoire. Il offre à la fois les
-services DNS et DHCP pour un réseau local (LAN).
+est un serveur à faible empreinte mémoire faisant DNS, TFTP, PXE, annonces de
+routeurs et DHCP. Il offre à la fois les services DNS et DHCP pour un réseau
+local (LAN).
 .PP
 Dnsmasq accepte les requêtes DNS et y réponds soit en utilisant un petit cache
 local, soit en effectuant une requête à un serveur DNS récursif externe (par
 exemple celui de votre fournisseur d'accès internet). Il charge le contenu du
 fichier /etc/hosts afin que les noms locaux n'apparaissant pas dans les DNS
 globaux soient tout de même résolus, et assure également la résolution de nom
-pour les hôtes présents dans le service DHCP.
+pour les hôtes présents dans le service DHCP. Il peut aussi agir en temps que
+serveur DNS faisant autorité pour un ou plusieurs domaines, permettant à des
+noms locaux d'apparaitre dans le DNS global.
 .PP
 Le serveur DHCP Dnsmasq DHCP supporte les définitions d'adresses statiques et les
-réseaux multiples. Il envoie par défaut un jeu raisonnable de paramètres DHCP, et
-peut être configuré pour envoyer n'importe quel option DHCP.
+réseaux multiples. Il fournit par défaut un jeu raisonnable de paramètres DHCP,
+et peut être configuré pour fournir n'importe quelle option DHCP.
 Il inclut un serveur TFTP sécurisé en lecture seule permettant le démarrage via
-le réseau/PXE de clients DHCP et supporte également le protocole BOOTP.
+le réseau/PXE de clients DHCP et supporte également le protocole BOOTP. Le
+support PXE est complet, et comprend un mode proxy permettant de fournir des
+informations PXE aux clients alors que l'allocation DHCP est effectuée par un
+autre serveur.
 .PP
-Dnsmasq supporte IPv6 pour le DNS et TFTP mais pas pour le DHCP.
+Le serveur DHCPv6 de dnsmasq possède non seulement les mêmes fonctionalités
+que le serveur DHCPv4, mais aussi le support des annonces de routeurs ainsi
+qu'une fonctionalité permettant l'addition de ressources AAAA pour des
+clients utilisant DHCPv4 et la configuration IPv6 sans état (stateless
+autoconfiguration).
+Il inclut le support d'allocations d'adresses (à la fois en DHCPv6 et en
+annonces de routeurs - RA) pour des sous-réseaux dynamiquement délégués via
+une délégation de préfixe DHCPv6.
+.PP
+Dnsmasq est developpé pour de petits systèmes embarqués. It tends à avoir
+l'empreinte mémoire la plus faible possible pour les fonctions supportées,
+et permet d'exclure les fonctions inutiles du binaire compilé.
 .SH OPTIONS
 Notes : Il est possible d'utiliser des options sans leur donner de paramètre.
 Dans ce cas, la fonction correspondante sera désactivée. Par exemple
@@ -75,9 +92,16 @@ l'absence d'enregistrement SOA.
 .TP
 .B --max-ttl=<durée>
 Définie la valeur de TTL maximum qui sera fournie aux clients. La valeur maximum
-de TTL spécifiée sera fournie aux clients en remplacement de la vraie valeur de TTL
-si cette dernière est supérieure. La valeur réelle de TTL est cependant conservée dans
-le cache afin d'éviter de saturer les serveurs DNS en amont.
+de TTL spécifiée sera fournie aux clients en remplacement de la vraie valeur de
+TTL si cette dernière est supérieure. La valeur réelle de TTL est cependant
+conservée dans le cache afin d'éviter de saturer les serveurs DNS en amont.
+.TP
+.B --max-cache-ttl=<durée>
+Définie la valeur de TTL maximum pour les entrées dans le cache
+.TP
+.B --auth-ttl=<durée>
+Définie la valeur de TTL retournée pour les réponses du serveur faisant
+autorité.
 .TP
 .B \-k, --keep-in-foreground
 Ne pas aller en tâche de fond au lancement, mais en dehors de cela, fonctionner
@@ -89,7 +113,10 @@ Mode debug (déverminage) : ne pas aller en tâche de fond, ne pas écrire de
 fichier pid, ne pas changer d'identifiant utilisateur, générer un état complet
 du cache lors de la réception d'un signal SIGUSR1, envoyer les logs sur la
 sortie standard d'erreur ("stderr") de même que dans le syslog, ne pas créer de
-processus fils pour traiter les requêtes TCP.
+processus fils pour traiter les requêtes TCP. A noter que cette option est à
+user pour du déverminage seulement : pour empêcher dnsmasq se fonctionner en
+mode démon en production, utiliser
+.B -k.
 .TP
 .B \-q, --log-queries
 Enregistrer les résultats des requêtes DNS traitées par Dnsmasq dans un fichier
@@ -184,7 +211,11 @@ ni
 .B \--except-interface.
 Utiliser l'option 
 .B --listen-address
-à la place. 
+à la place. Un simple joker, consistant d'un '*' final, peut-être utilisé dans
+les options
+.B \--interface 
+et
+.B \--except-interface
 .TP
 .B \-I, --except-interface=<interface name>
 Ne pas écouter sur l'interface spécifiée. Notez que l'ordre dans lesquelles les
@@ -197,6 +228,21 @@ et
 sont fournies n'importe pas, et que l'option 
 .B --except-interface
 l'emporte toujours sur les autres.
+.TP
+.B --auth-server=<domaine>,<interface>|<addresse IP>
+Active le mode DNS faisant autorité pour les requêtes arrivant sur cette
+interface ou sur cette adresse. Noter que l'interface ou l'adresse n'ont
+pas besoin d'être mentionées ni dans
+.B --interface
+ni dans
+.B --listen-address
+En effet,
+.B --auth-server
+va passer outre ceux-ci et fournir un service DNS différent sur l'interface
+spécifiée. La valeur de <domaine> est l'enregistrement de type "colle"
+("glue record"). Il doit correspondre dans le service DNS global avec un
+enregistrement de type A et/ou AAAA pointant sur l'adresse sur laquelle dnsmasq
+écoute pour le mode DNS faisant autorité.
 .TP 
 .B \-2, --no-dhcp-interface=<nom d'interface>
 Ne pas fournir de service DHCP sur l'interface spécifiée, mais fournir tout de
@@ -231,6 +277,18 @@ autre serveur de nom (ou une autre instance de Dnsmasq) tourne sur la même
 machine. Utiliser cette option permet également d'avoir plusieurs instances de
 Dnsmasq fournissant un service DHCP sur la même machine.
 .TP
+.B --bind-dynamic
+Autorise un mode réseau intermédiaire entre
+.B --bind-interfaces
+et le mode par défaut. Dnsmasq s'associe à une seule interface, ce qui permet
+plusieurs instances de dnsmasq, mais si une interface ou adresse apparaissent,
+il se mettra automatiquement à écouter sur celles-ci (les règles de contrôle
+d'accès s'appliquent).
+De fait, les interfaces créées dynamiquement fonctionnent de la même façon que
+dans le comportement par défaut. Ce fonctionnement nécessite des APIs réseau
+non standard et n'est disponible que sous Linux. Sur les autres plateformes,
+le fonctionnement est celui du mode --bind-interfaces.
+.TP
 .B \-y, --localise-queries
 Retourne des réponses aux requêtes DNS dépendantes de l'interface sur laquelle
 la requête a été reçue, à partir du fichier /etc/hosts. Si un nom dans
@@ -299,11 +357,14 @@ Ne pas lire le contenu du fichier /etc/resolv.conf. N'obtenir l'adresse des
 serveurs de nom amont que depuis la ligne de commande ou le fichier de
 configuration de Dnsmasq.
 .TP
-.B \-1, --enable-dbus
+.B \-1, --enable-dbus[=<nom de service>]
 Autoriser la mise à jour de la configuration de Dnsmasq par le biais d'appel de
 méthodes DBus. Il est possible par ce biais de mettre à jour l'adresse de
 serveurs DNS amont (et les domaines correspondants) et de vider le cache. Cette
-option nécessite que Dnsmasq soit compilé avec le support DBus.
+option nécessite que Dnsmasq soit compilé avec le support DBus. Si un nom de
+service est fourni, dnsmasq fourni un service à ce nom, plutôt qu'avec la
+valeur par défaut :
+.B uk.org.thekelleys.dnsmasq
 .TP 
 .B \-o, --strict-order
 Par défaut, Dnsmasq envoie les requêtes à n'importe lequel des serveurs amonts
@@ -342,15 +403,16 @@ noms de domains entourés par des '/', selon une syntaxe similaire à l'option
 Ne pas vérifier régulièrement si le fichier /etc/resolv.conf a été modifié.
 .TP
 .B --clear-on-reload
-Lorsque le fichier /etc/resolv.conf est relu, vider le cache DNS.
+Lorsque le fichier /etc/resolv.conf est relu, ou si les serveurs amonts sont
+configurés via DBus, vider le cache DNS.
 Cela est utile si les nouveaux serveurs sont susceptibles d'avoir des données
 différentes de celles stockées dans le cache.
 .TP
 .B \-D, --domain-needed
-Indique à Dnsmasq de ne jamais transmettre en amont de requêtes pour des noms
-simples, ne comprenant donc ni points ni nom de domaine. Si un nom n'est pas
-dans /etc/hosts ou dans la liste des baux DHCP, alors une réponse de type
-"non trouvé" est renvoyée.
+Indique à Dnsmasq de ne jamais transmettre en amont de requêtes A ou AAAA pour
+des noms simples, c'est à dire ne comprenant ni points ni nom de domaine. Si un
+nom n'est pas dans /etc/hosts ou dans la liste des baux DHCP, alors une réponse
+de type "non trouvé" est renvoyée.
 .TP
 .B \-S, --local, --server=[/[<domaine>]/[domaine/]][<Adresse IP>[#<port>][@<Adresse IP source>|<interface>[#<port>]]]
 Spécifie directement l'adresse IP d'un serveur de nom amont. Cette option ne
@@ -402,6 +464,10 @@ est synonyme de
 ("serveur") afin de rendre plus claire l'utilisation de cette option pour cet
 usage particulier.
 
+Les adresses IPv6 peuvent inclure un identifiant de zone sous la forme
+%interface tel que par exemple
+fe80::202:a412:4512:7bbf%eth0.
+
 La chaîne de caractères optionnelle suivant le caractère @ permet de définir
 la source que Dnsmasq doit utiliser pour les réponses à ce
 serveur de nom. Il doit s'agir d'une des adresses IP appartenant à la machine sur
@@ -438,6 +504,12 @@ n'ayant de réponse ni dans /etc/hosts, ni dans les baux DHCP, et n'étant pas
 transmise à un serveur spécifique par le biais d'une directive
 .B --server.
 .TP
+.B --ipset=/<domaine>/[domaine/]<ipset>[,<ipset>]
+Obtient les adresses IP des domaines spécifiés et les place dans les groupes
+d'IP netfilter (ipset) indiqués. Domaines et sous-domaines sont résolus de la
+même façon que pour --address. Ces groupes d'IP doivent déjà exister. Voir
+ipset(8) pour plus de détails.
+.TP
 .B \-m, --mx-host=<nom de l'hôte>[[,<nom du MX>],<préference>]
 Spécifie un enregistrement de type MX pour <nom de l'hôte> retournant le nom
 donné dans <nom du MX> (s'il est présent), ou sinon le nom spécifié dans
@@ -488,6 +560,27 @@ dans un ordre différents. Pour un service/domaine donné, plus d'un
 enregistrement SRV est autorisé et tous les enregistrements qui coïncident sont
 retournés dans la réponse.
 .TP
+.B --host-record=<nom>[,<nom>....][<adresse IPv4>],[<adresse IPv6>]
+Ajoute des enregistrements A, AAAA et PTR dans le DNS. Ceci permet d'ajouter
+un ou plusieurs noms dans le DNS et de les associer à des enregistrements IPv4
+(A) ou IPv6 (AAAA). Un nom peut apparaître dans plus d'une entrée
+.B host-record
+et de fait être associé à plus d'une adresse. Seule la première entrée créée
+l'enregistrement PTR associée au nom. Ceci correspond à la même règle que celle
+utilisée lors de la lecture du fichier hosts.
+Les options
+.B host-record
+sont considérées lues avant le fichier hosts, ainsi un nom apparaissant dans
+une option host-record et dans le fichier hosts n'aura pas d'enregistrement
+PTR associé à l'entrée dans le fichier hosts. A l'inverse du fichier hosts, les
+noms ne sont pas étendus, même lorsque l'option
+.B expand-hosts
+est activée. Les noms longs et les noms courts peuvent apparaitre dans la même
+entrée 
+.B host-record,
+c-à-d
+.B --host-record=laptop,laptop.thekelleys.org,192.168.0.1,1234::100
+.TP
 .B \-Y, --txt-record=<nom>[[,<texte>],<texte>]
 Définit un enregistrement DNS de type TXT. La valeur de l'enregistrement TXT est
 un ensemble de chaînes de caractères, donc un nombre variable de chaînes de
@@ -504,11 +597,19 @@ Retourne un enregistrement de type NAPTR, tel que spécifié dans le RFC3403.
 .TP
 .B --cname=<cname>,<cible>
 Retourne un enregistrement de type CNAME qui indique que <cname> est en
-réalité <cible>. Il existe des contraintes significatives sur la valeur
-de cible; il doit s'agir d'un nom DNS qui est connu de dnsmasq via /etc/hosts
-(ou un fichier hôtes additionnel) ou via DHCP. Si une cible ne satisfait
-pas ces critères, le CNAME est ignoré. Le CNAME doit être unique, mais
-il est autorisé d'avoir plus d'un CNAME pointant vers la même cible.
+réalité <cible>. Il existe des contraintes importantes sur la valeur
+cible; il doit s'agir d'un nom DNS qui est connu de dnsmasq via /etc/hosts
+(ou un fichier hôtes additionnel), via DHCP, via interface--name ou par un autre
+.B --cname.
+Si une cible ne satisfait pas ces critères, le CNAME est ignoré. Le CNAME
+doit être unique, mais il est autorisé d'avoir plus d'un CNAME pointant
+vers la même cible.
+.TP
+.B --dns-rr=<nom>,<numéro-RR>,[<données hexadécimales>]
+Retourne un enregistrement DNS arbitraire. Le numéro correspond au type
+d'enregistrement (qui est toujours de la classe C_IN). La valeur de
+l'enregistrement est donnée dans les données hexadécimales, qui peuvent
+être de la forme 01:23:45, 01 23 45,+012345 ou n'importe quelle combinaison.
 .TP
 .B --interface-name=<nom>,<interface>
 Définit un entregistrement DNS associant le nom avec l'adresse primaire sur
@@ -522,6 +623,24 @@ Plus d'un nom peut être associé à une interface donnée en répétant cette o
 plusieurs fois; dans ce cas, l'enregistrement inverse pointe vers le nom fourni
 dans la première instance de cette option.
 .TP
+.B --synth-domain=<domaine>,<plage d'adresses>[,<préfixe>]
+Créé des enregistrements A/AAAA ou PTR pour une plage d'adresses. Les
+enregistrements utilisent l'adresse ainsi que les points (ou les deux points
+dans le cas d'IPv6) remplacés par des tirets.
+
+Un exemple devrait rendre cela plus clair :
+La configuration
+.B --synth-domain=thekelleys.org.uk,192.168.0.0/24,internal-
+permet de retourner internal-192-168-0-56.thekelleys.org.uk lors d'une requête
+sur l'adresse 192.168.0.56 et vice-versa pour la requête inverse. La même
+logique s'applique pour IPv6, avec la particularité suivante : les adresses
+IPv6 pouvant commencer par '::', mais les noms DNS ne pouvant pas commencer
+par '-', si aucun préfixe n'est donné, un zéro est ajouté en début de nom.
+Ainsi, ::1 devient 0--1.
+
+La plage d'adresses peut-être de la forme
+<adresse IP>,<adresse IP> ou <adresse IP>/<masque réseau>
+.TP
 .B --add-mac
 Ajoute l'adresse MAC du requêteur aux requêtes DNS transmises aux serveurs
 amonts. Cela peut être utilisé dans un but de filtrage DNS par les serveurs
@@ -530,7 +649,20 @@ même sous-réseau que le serveur dnsmasq. Veuillez noter que le mécanisme
 utilisé pour effectuer cela (une option EDNS0) n'est pas encore standardisée,
 aussi cette fonctionalité doit être considérée comme expérimentale. Notez
 également qu'exposer les adresses MAC de la sorte peut avoir des implications
-en termes de sécurité et de vie privée.
+en termes de sécurité et de vie privée. L'avertissement donné pour --add-subnet
+s'applique également ici.
+.TP
+.B --add-subnet[[=<longueur de préfixe IPv4>],<longueur de préfixe IPv6>]
+Rajoute l'adresse de sous-réseau du requêteur aux requêtes DNS transmises
+aux serveurs amonts. La quantité d'adresses transmises dépend du paramètre
+longueur du préfixe : 32 (ou 128 dans le cas d'IPv6) transmet la totalité
+de l'adresse, 0 n'en transmet aucun mais marque néanmoins la requête ce qui
+fait qu'aucun serveur amont ne rajoutera d'adresse client. La valeur par
+défaut est zéro et pour IPv4 et pour IPv6. A noter que les serveurs amonts
+peuvent-être configurés pour retourner des valeurs différentes en fonction
+de cette information mais que le cache de dnsmasq n'en tient pas compte.
+Si une instance de dnsmasq est configurée de telle maniêre que des valeurs
+différentes pourraient-être rencontrés, alors le cache devrait être désactivé.
 .TP
 .B \-c, --cache-size=<taille>
 Définit la taille du cache de Dnsmasq. La valeur par défaut est de 150 noms.
@@ -565,7 +697,53 @@ Si vous utilisez le premier mode DNSSEC, la validation par le resolveur des
 clients, cette option n'est pas requise. Dnsmasq retourne toujours toutes les
 données nécessaires par un client pour effectuer la validation lui-même.
 .TP
-.B \-F, --dhcp-range=[interface:<interface>,][tag:<label>[,tag:<label>],][set:<label],]<adresse de début>,<adresse de fin>[,<masque de réseau>[,<broadcast>]][,<durée de bail>]
+
+.B --auth-zone=<domaine>[,<sous-réseau>[/<longueur de préfixe>][,<sous-réseau>[/<longueur de préfixe>].....]]
+Définie une zone DNS pour laquelle dnsmasq agit en temps que serveur faisant
+autorité. Les enregistrements DNS définis localement et correspondant à ce
+domaine seront fournis. Les enregistrements A et AAAA doivent se situer dans
+l'un des sous-réseaux définis, ou dans un réseau correspondant à une plage DHCP
+(ce comportement peut-être désactivé par
+.B constructor-noauth:
+). Le ou les sous-réseaux sont également utilisé(s) pour définir les domaines
+in-addr.arpa et ip6.arpa servant à l'interrogation DNS inverse. Si la longueur
+de préfixe n'est pas spécifiée, elle sera par défaut de 24 pour IPv4 et 64 pour
+IPv6. Dans le cas d'IPv4, la longueur du masque de réseau devrait-être de 8, 16
+ou 24, sauf si en cas de mise en place d'une délégation de la zone in-addr.arpa
+conforme au RFC 2317.
+.TP
+.B --auth-soa=<numéro de série>[,<mainteneur de zone (hostmaster)>[,<rafraichissement>[,<nombre de réessais>[,<expiration>]]]]
+Spécifie les champs de l'enregistrement de type SOA (Start Of Authority)
+associé à une zone pour laquelle le serveur fait autorité. A noter que cela est
+optionnel, les valeurs par défaut devant convenir à la majorité des cas.
+.TP
+.B --auth-sec-servers=<domaine>[,<domaine>[,<domaine>...]]
+Spécifie un ou plusieurs serveur de nom secondaires pour une zone pour
+laquelle dnsmasq fait autorité. Ces serveurs doivent-être configurés pour
+récupérer auprès de dnsmasq les informations liées à la zone au travers d'un
+transfert de zone, et répondre aux requêtes pour toutes les zones pour
+lesquelles dnsmasq fait autorité.
+.TP
+.B --auth-peer=<adresse IP>[,<adresse IP>[,<adresse IP>...]]
+Spécifie la ou les adresses de serveurs secondaires autorisés à initier des
+requêtes de transfert de zone (AXFR) pour les zones pour lesquelles
+dnsmasq fait autorité. Si cette option n'est pas fournie, les requêtes AXFR
+seront acceptées pour tous les serveurs secondaires.
+.TP 
+.B --conntrack
+Lis le marquage de suivi de connexion Linux associé aux requêtes DNS entrantes
+et positionne la même marque au trafic amont utilisé pour répondre à ces
+requétes. Cela permet au trafic généré par Dnsmasq d'étre associé aux requêtes
+l'ayant déclenché, ce qui est pratique pour la gestion de la bande passante
+(accounting) et le filtrage (firewall). Dnsmasq doit pour cela être compilé
+avec le support conntrack, le noyau doit également inclure conntrack et être
+configuré pour cela. Cette option ne peut pas être combinée avec
+--query-port.
+.TP
+.B \-F, --dhcp-range=[tag:<label>[,tag:<label>],][set:<label>],]<adresse de début>[,<adresse de fin>][,<mode>][,<masque de réseau>[,<broadcast>]][,<durée de bail>]
+.TP
+.B \-F, --dhcp-range=[tag:<label>[,tag:<label>],][set:<label>],]<addresse IPv6 de début>[,<adresse IPv6 de fin>|constructor:<interface>][,<mode>][,<longueur de préfixe>][,<durée de bail>]
+
 Active le serveur DHCP. Les adresses seront données dans la plage comprise entre
 <adresse de début> et <adresse de fin> et à partir des adresses définies
 statiquement dans l'option
@@ -576,15 +754,72 @@ en heures (exemple : 1h) ou être la chaine de caractère "infinite" pour une
 durée indéterminée. Si aucune valeur n'est donnée, une durée de bail par défaut
 de une heure est appliquée. La valeur minimum pour un bail DHCP est de 2
 minutes.
+
+Pour les plages IPv6, la durée de bail peut-être égale au mot-clef "deprecated"
+(obsolète); Cela positionne la durée de vie préférée envoyée dans les baux DHCP
+ou les annonces routeurs à zéro, ce qui incite les clients à utiliser d'autres
+adresses autant que possible, pour toute nouvelle connexion, en préalable à
+la renumérotation.
+
 Cette option peut être répétée, avec différentes adresses,
 pour activer le service DHCP sur plus d'un réseau. Pour des réseaux directement
 connectés (c'est-à-dire des réseaux dans lesquels la machine sur laquelle tourne
-Dnsmasq possède une interface), le masque de réseau est optionnel. Il est par
-contre requis pour les réseaux pour lesquels le service DHCP se fait via un
-relais DHCP ("relay agent"). L'adresse de broadcast est toujours optionnelle.
+Dnsmasq possède une interface), le masque de réseau est optionnel : Dnsmasq la
+déterminera à partir de la configuration des interfaces.
+
+Pour les réseaux pour lesquels le service DHCP se fait via un relais DHCP
+("relay agent"), Dnsmasq est incapable de déterminer le masque par lui-même,
+aussi il doit être spécifié, faute de quoi Dnsmasq essaiera de le deviner en
+fonction de la classe (A, B ou C) de l'adresse réseau. L'adresse de broadcast
+est toujours optionnelle.
 
 Il est toujours possible d'avoir plus d'une plage DHCP pour un même
 sous-réseau.
+
+Pour IPv6, les paramètres sont légèrement différents : au lieu d'un masque de
+réseau et d'une adresse de broadcast, il existe une longueur de préfixe
+optionnelle. Si elle est omise, la valeur par défaut est 64. À la différence
+d'IPv4, la longueur de préfixe n'est pas automatiquement déduite de la
+configuration de l'interface. La taille minimale pour la longueur de préfixe
+est 64.
+
+Pour IPv6 (et IPv6 uniquement), il est possible de définir les plages d'une
+autre façon. Dans ce cas, l'adresse de départ et l'adresse de fin optionnelle
+contiennent uniquement la partie réseau (par exemple ::1) et sont suivies par
+.B constructor:<interface>.
+Cela forme un modèle décrivant comment construire la plage, à partir des
+adresses assignées à l'interface. Par exemple
+
+.B --dhcp-range=::1,::400,constructor:eth0
+
+provoque la recherche d'adresses de la forme <réseau>::1 sur eth0 et crée une
+plage allant de <réseau>::1 à <réseau>:400. Si une interface est assignée à
+plus d'un réseau, les plages correspondantes seront automatiquement créées,
+rendues obsolètes puis supprimées lorsque l'adress est rendue obsolète puis
+supprimée. Le nom de l'interface peut être spécifié avec un caractère joker '*'
+final.
+
+provoque la recherche d'adresses sur eth0 et crée une plage allant de
+<réseau>::1 à <réseau>:400. Si l'interface est assignée à
+plus d'un réseau, les plages correspondantes seront respectivement
+automatiquement créées, rendues obsolètes et supprimées lorsque l'adresse
+est rendue obsolète et supprimée. Le nom de l'interface peut être spécifié avec
+un caractère joker '*' final. Les adresses autoconfigurées, privées ou
+obsolètes ne conviennent pas.
+
+Si une plage dhcp-range est uniquement utilisée pour du DHCP sans-état
+("stateless") ou de l'autoconfiguration sans état ("SLAAC"), alors l'adresse
+peut-être indiquée sous la forme '::'
+
+.B --dhcp-range=::,constructor:eth0
+
+Il existe une variante de la syntaxe constructor: qui consiste en l'utilisation
+du mot-clef
+.B constructor-noauth.
+Voir
+.B --auth-zone
+pour des explications à ce sujet.
+
 L'identifiant de label optionnel
 .B set:<label>
 fournie une étiquette alphanumérique qui identifie ce réseau, afin de permettre
@@ -593,15 +828,21 @@ Lorsque préfixé par 'tag:', la signification change, et au lieu de définir un
 label, il définit le label pour laquelle la règle s'applique. Un seul label peut-
 être défini mais plusieurs labels peuvent coïncider.
 
-L'adresse de fin peut être remplacée par le mot-clef
+Le mot clef optionnel <mode> peut être égal à
 .B static
-("statique") qui indique à Dnsmasq d'activer le service DHCP pour le réseau
+("statique") ce qui indique à Dnsmasq d'activer le service DHCP pour le réseau
 spécifié, mais de ne pas activer l'allocation dynamique d'adresses IP : Seuls
 les hôtes possédant des adresses IP statiques fournies via 
 .B dhcp-host
-ou présentes dans le fichier /etc/ethers seront alors servis par le DHCP.
+ou présentes dans le fichier /etc/ethers seront alors servis par le DHCP. Il est
+possible d'activer un mode "fourre-tout" en définissant un réseau statique
+comportant uniquement des zéros, c'est à dire :
+.B --dhcp=range=::,static
+Cela permet de retourner des réponses à tous les paquets de type
+Information-request (requête d'information) en mode DHCPv6 sans état sur le
+sous-réseau configuré. 
 
-L'adresse de fin peut-être remplacée par le mot-clef
+Pour IPv4, le <mode> peut est égal à
 .B proxy
 , auquel cas Dnsmasq fournira un service de DHCP proxy pour le sous-réseau
 spécifié. (voir
@@ -610,8 +851,46 @@ et
 .B pxe-service
 pour plus de détails).
 
-La section interface:<nom d'interface> n'est normalement pas utilisée. Se
-référer aux indications de la section NOTES pour plus de détail à ce sujet.
+Pour IPv6, le mode peut-être une combinaison des valeurs
+.B ra-only, slaac, ra-names, ra-stateless, off-link.
+
+.B ra-only
+indique à dnsmasq de n'effectuer que des annonces de routeur (Router
+Advertisement, RA) sur ce sous-réseau, et de ne pas faire de DHCP.
+
+.B slaac
+indique à dnsmasq d'effectuer des annonces de routeur sur ce sous-réseau
+et de positionner dans celles-ci le bit A, afin que les clients utilisent
+des adresses SLAAC. Lorsqu'utilisé conjointement avec une plage DHCP ou des
+affectations statiques d'adresses DHCP, les clients disposeront à la fois
+d'adresses DHCP assignées et d'adresses SLAAC.
+
+.B ra-stateless
+indique à dnsmasq d'effectuer des annonces de routeur avec les bits 0 et A
+positionnés, et de fournir un service DHCP sans état ("stateless"). Les clients
+utiliseront des adresses SLAAC, et utiliseront DHCP pour toutes les autres
+informations de configuration.
+
+.B ra-names
+active un mode qui fourni des noms DNS aux hôtes fonctionnant en double pile
+("dual stack") et configurés pour faire du SLAAC en IPv6. Dnsmasq utilise le
+bail IPv4 de l'hôte afin de dériver le nom, le segment de réseau et l'adresse
+MAC et assume que l'hôte disposera d'une adresse IPv6 calculée via l'algorithme
+SLAAC, sur le même segment de réseau. Un ping est envoyé à l'adresse, et si une
+réponse est obtenue, un enregistrement AAAA est rajouté dans le DNS pour cette
+adresse IPv6. Veuillez-noter que cela n'arrive que pour les réseaux directement
+connectés (et non ceux pour lesquels DHCP se fait via relai), et ne
+fonctionnera pas si un hôte utilise les "extensions de vie privée"
+("privacy extensions").
+.B ra-names
+peut-être combiné avec
+.B ra-stateless
+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
@@ -635,16 +914,31 @@ sous-réseau qu'une plage dhcp-range valide. Pour les sous-réseaux qui n'ont pa
 besoin d'adresses dynamiquement allouées, utiliser le mot-clef "static" dans la
 déclaration de plage d'adresses dhcp-range.
 
-Il est possible
-d'utiliser des identifiants clients plutôt que des adresses matérielles pour
-identifier les hôtes, en préfixant par ceux-ci par 'id:'. Ainsi, 
+Il est possible d'utiliser des identifiants clients (appellé "DUID client" dans
+le monde IPv6) plutôt que des adresses matérielles pour identifier les hôtes,
+en préfixant ceux-ci par 'id:'. Ainsi, 
 .B --dhcp-host=id:01:02:03:04,..... 
 réfère à l'hôte d'identifiant 01:02:03:04. Il est également possible de
 spécifier l'identifiant client sous la forme d'une chaîne de caractères, comme
 ceci :
 .B --dhcp-host=id:identifiantclientsousformedechaine,..... 
 
-L'option spéciale id:* signifie : "ignorer tout identifiant client et n'utiliser
+Un seul
+.B dhcp-host 
+peut contenir une adresse IPv4, une adresse IPv6, ou les deux en même temps.
+Les adresses IPv6 doivent-être mises entre crochets comme suit :
+.B --dhcp-host=laptop,[1234::56]
+Les adresses IPv6 peuvent ne contenir que la partie identifiant de client :
+.B --dhcp-host=laptop,[::56]
+Dans ce cas, lorsque des plages dhcp sont définies automatiquement par le biais
+de constructeurs, la partie réseau correspondante est rajoutée à l'adresse.
+
+A noter que pour le DHCP IPv6, l'adresse matérielle n'est pas toujours
+disponible, bien que ce soit toujours le cas pour des clients directement
+connectés (sur le même domaine de broadcast) ou pour des clients utilisant
+des relais DHCP qui supportent la RFC 6939.
+
+En DHCPv4, l'option spéciale id:* signifie : "ignorer tout identifiant client et n'utiliser
 que l'adresse matérielle". Cela est utile lorsqu'un client présente un
 identifiant client mais pas les autres.
 
@@ -690,7 +984,7 @@ ARP en les précédant du type ARP (en Hexadécimal) et de "-". Ainsi
 coïncidera uniquement avec des adresses matérielles Token-Ring, puisque le type
 ARP pour une adresse Token-Ring est 6.
 
-Un cas spécial correspond à l'inclusion d'une ou plusieurs adresses
+Un cas spécial, pour IPv4, correspond à l'inclusion d'une ou plusieurs adresses
 matérielles, c-à-d :
 .B --dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.2.
 Cela permet à une adresse IP d'être associé à plusieurs adresses
@@ -731,22 +1025,22 @@ une adresse IP sous la forme de 4 chiffres séparés par des points. Lorsque lu
 par Dnsmasq, ces lignes ont exactement le même effet que l'option
 .B --dhcp-host
 contenant les mêmes informations. /etc/ethers est relu à la réception d'un
-signal SIGHUP par Dnsmasq.
+signal SIGHUP par Dnsmasq. Les adresses IPv6 ne sont PAS lues dans /etc/ethers.
 .TP
-.B \-O, --dhcp-option=[tag:<label>,[tag:<label>]][encap:<option>,][vi-encap:<entreprise>,][vendor:[<classe_vendeur>],][<option>|option:<nom d'option>],[<valeur>[,<valeur>]]
+.B \-O, --dhcp-option=[tag:<label>,[tag:<label>]][encap:<option>,][vi-encap:<entreprise>,][vendor:[<classe_vendeur>],][<option>|option:<nom d'option>|option6:<option>|option6:<nom d'option>],[<valeur>[,<valeur>]]
 Spécifie des options différentes ou supplémentaires pour des clients DHCP. Par
 défaut, Dnsmasq envoie un ensemble standard d'options aux clients DHCP : le
 masque de réseau et l'adresse de broadcast sont les mêmes que pour l'hôte
 sur lequel tourne Dnsmasq, et le serveur DNS ainsi que la route par défaut
-prennent comme valeur l'adresse de la machine sur laquelle tourne Dnsmasq. Si
-une option de nom de domaine a été définie, son contenu est transmis. Cette
-option de configuration permet de changer toutes ces valeurs par défaut, ou de
-spécifier d'autres options. L'option DHCP à transmettre peut être fournie sous
-forme d'un nombre décimal ou sous la forme "option:<nom d'option>". Les nombres
-correspondants aux options sont définis dans la RFC2132 et suivants. Les noms
-d'options connus par Dnsmasq peuvent être obtenus via "Dnsmasq --help dhcp".
-Par exemple, pour définir la route par défaut à 192.168.4.4, il est possible de
-faire
+prennent comme valeur l'adresse de la machine sur laquelle tourne Dnsmasq. 
+(Des règles équivalentes s'appliquent en IPv6). Si une option de nom de domaine
+a été définie, son contenu est transmis. Cette option de configuration permet
+de changer toutes ces valeurs par défaut, ou de spécifier d'autres options.
+L'option DHCP à transmettre peut être fournie sous forme d'un nombre décimal 
+ou sous la forme "option:<nom d'option>". Les nombres correspondants aux options
+sont définis dans la RFC2132 et suivants. Les noms d'options connus par Dnsmasq 
+peuvent être obtenus via "Dnsmasq --help dhcp". Par exemple, pour définir la
+route par défaut à 192.168.4.4, il est possible de faire
 .B --dhcp-option=3,192.168.4.4 
 ou
 .B --dhcp-option = option:router, 192.168.4.4
@@ -769,6 +1063,13 @@ de l'option 120 sont traités conforméments à la RFC 3361. Les adresses IP sou
 forme de 4 chiffres séparés par des points suivies par une barre montante "/",
 puis une taille de masque sont encodés conforméments à la RFC 3442.
 
+Les options IPv6 sont fournies en utilisant le mot-clef
+.B option6:
+suivi par le numéro d'option ou le nom d'option. L'espace de nommage des options
+IPv6 est disjint de l'espace de nommage des options IPv4. Les adresses IPv6
+en option doivent être entourées de crochets, comme par exemple :
+.B --dhcp-option=option6:ntp-server,[1234::56]
+
 Attention : aucun test n'étant fait pour vérifier que des données d'un type
 adéquat sont envoyées pour un numéro d'option donné, il est tout à fait possible
 de persuader Dnsmasq de générer des paquets DHCP illégaux par une utilisation
@@ -787,8 +1088,8 @@ d'une chaîne de caractères comme nom de serveur TFTP, il est nécessaire de fa
 comme suit :
 .B --dhcp-option=66,"1.2.3.4"
 
-Les options encapsulées de classes de vendeurs peuvent-être aussi spécifiées en
-utilisant
+Les options encapsulées de classes de vendeurs peuvent-être aussi spécifiées 
+(pour IPv4 seulement) en utilisant
 .B --dhcp-option
 : par exemple
 .B --dhcp-option=vendor:PXEClient,1,0.0.0.0
@@ -804,7 +1105,7 @@ par le client. Il est possible d'omettre complètement une classe de vendeur :
 .B --dhcp-option=vendor:,1,0.0.0.0
 Dans ce cas l'option encapsulée est toujours envoyée.
 
-Les options peuvent-être encapsulées au sein d'autres options :
+En IPv4, les options peuvent-être encapsulées au sein d'autres options :
 par exemple
 .B --dhcp-option=encap:175, 190, "iscsi-client0"
 enverra l'option 175, au sein de laquelle se trouve l'option 190.
@@ -817,7 +1118,8 @@ identifiant le vendeur" ("Vendor-Identifying Vendor Options") telle que
 décrite dans le RFC3925. Celles-ci sont spécifiées comme suit :
 .B --dhcp-option=vi-encap:2, 10, "text"
 Le numéro dans la section vi-encap: est le numéro IANA de l'entreprise servant
-à identifier cette option.
+à identifier cette option. Cette forme d'encapsulation est également supportée
+en IPv6.
 
 L'adresse 0.0.0.0 n'est pas traitée de manière particulière lorsque fournie dans
 une option encapsulée.
@@ -830,8 +1132,8 @@ dans la liste de paramêtres requis. Cela est parfois nécessaire, par exemple l
 de la fourniture d'options à PXELinux.
 .TP
 .B --dhcp-no-override
-Désactive la réutilisation des champs DHCP nom de serveur et nom de
-fichier comme espace supplémentaire pour les options. Si cela est
+(IPv4 seulement) Désactive la réutilisation des champs DHCP nom de serveur et
+nom de fichier comme espace supplémentaire pour les options. Si cela est
 possible, dnsmasq déplace les informations sur le serveur de démarrage
 et le nom de fichier (fournis par 'dhcp-boot') en dehors des champs
 dédiés à cet usage dans les options DHCP. Cet espace supplémentaire est
@@ -840,7 +1142,42 @@ quelques rares cas, perturber des clients vieux ou défectueux. Cette
 option force le comportement à l'utilisation des valeurs "simples et sûres"
 afin d'éviter des problèmes dans de tels cas.
 .TP
-.B \-U, --dhcp-vendorclass=set:<label>,<classe de vendeur>
+.B --dhcp-relay=<adresse locale>,<adresse de serveur>[,<interface]
+Configure dnsmasq en temps que relais DHCP.  L'adresse locale est une
+adresse allouée à l'une interface de la machine sur laquelle tourne dnsmasq.
+Toutes les requêtes DHCP arrivant sur cette interface seront relayées au
+serveur DHCP distant correspondant à l'adresse de serveur indiquée. Il est
+possible de relayer depuis une unique adresse locale vers différents serveurs
+distant en spécifiant plusieurs fois l'option dhcp-relay avec la même adresse
+locale et différentes adresses de serveur. L'adresse de serveur doit-être
+sous forme numérique. Dans le cas de DHCPv6, l'adresse de serveur peut-être
+l'adresse de multicast ff05::1:3 correspondant à tous les serveurs DHCP. Dans
+ce cas, l'interface doit-étre spécifiée et ne peut comporter de caractère
+joker. Elle sera utilisée pour indiquer l'interface à partir de laquelle le
+multicast pourra atteindre le serveur DHCP.
+
+Le contrôle d'accès pour les clients DHCP suivent les mêmes règles que pour
+les serveurs DHCP : voir --interface, --except-interface, etc. Le nom
+d'interface optionel dans l'option dhcp-relay comporte une autre fonction :
+il contrôle l'interface sur laquelle la réponse du serveur sera acceptée. Cela
+sert par exemple dans des configurations à 3 interfaces : une à partir de
+laquelle les requêtes sont relayées, une seconde permettant de se connecter à
+un serveur DHCP, et une troisième reliée à un réseau non-sécurisé tel
+qu'internet. Cela permet d'éviter l'arrivée de requêtes usurpées via cette
+troisième interface.
+
+Il est permis de configurer dnsmasq pour fonctionner comme serveur DHCP sur
+certaines interfaces et en temps que relais sur d'autres. Cependant, même s'il
+est possible de configurer dnsmasq de telle manière qu'il soit à la fois
+serveur et relais pour une même interface, cela n'est pas supporté et la
+fonction de relais prendra le dessus.
+
+Le relais DHCPv4 et le relais DHCPv6 sont tous les deux supportés, mais il
+n'est pas possible de relayer des requêtes DHCPv4 à un serveur DHCPv6 et
+vice-versa.
+.TP
+.B \-U, --dhcp-vendorclass=set:<label>,[enterprise:<numéro IANA d'enterprise>,]<classe de vendeur>
+
 Associe une chaîne de classe de vendeur à un label. La plupart
 des clients DHCP fournissent une "classe de vendeur" ("vendor class") qui
 représente, d'une certaine façon, le type d'hôte. Cette option associe des
@@ -856,6 +1193,11 @@ en temps que sous-chaîne de caractères au sein de la classe de vendeur fournie
 par le client, de façon à permettre la recherche d'un sous-ensemble de la chaîne
 de caractères ("fuzzy matching"). Le préfixe set: est optionnel mais autorisé
 afin de conserver une certaine homogénéité.
+
+Notez qu'en IPv6 (et seulement en IPv6), les noms de classes de vendeurs
+sont dans un espace de nom associé au numéro attribué à l'entreprise par
+l'IANA. Ce numéro est fourni par le biais du mot-clef enterprise: et seules
+les classes de vendeurs associées au numéro spécifié seront cherchées.
 .TP
 .B \-j, --dhcp-userclass=set:<label>,<classe utilisateur>
 Associe une chaîne de classe d'utilisateur à un label (effectue la
@@ -882,16 +1224,19 @@ chaîne de valeurs hexadécimales séparées par des ":", mais il est également
 possible qu'elle le soit sous la forme d'une simple chaîne de caractères. Si
 l'identifiant de circuit ou d'agent correspond exactement à celui fourni par le
 relais DHCP, alors le label est apposé.
+.B dhcp-remoteid
+est supporté en IPv6 (mais non dhcp-circuitid).
 .TP
 .B --dhcp-subscrid=set:<label>,<identifiant d'abonné>
-Associe des options de relais DHCP issues de la RFC3993 à des labels.
+(IPv4 et IPv6) Associe des options de relais DHCP issues de la RFC3993 à des
+labels.
 .TP
 .B --dhcp-proxy[=<adresse ip>]......
-Un agent relai DHCP normal est uniquement utilisé pour faire suivre les
-éléments initiaux de l'interaction avec le serveur DHCP. Une fois que le
-client est configuré, il communique directement avec le serveur. Cela n'est pas
-souhaitable si le relais rajoute des informations supplémentaires aux paquets
-DHCP, telles que celles utilisées dans
+(IPv4 seulement) Un agent relai DHCP normal est uniquement utilisé pour faire
+suivre les éléments initiaux de l'interaction avec le serveur DHCP. Une fois
+que le client est configuré, il communique directement avec le serveur. Cela
+n'est pas souhaitable si le relais rajoute des informations supplémentaires
+aux paquets DHCP, telles que celles utilisées dans
 .B dhcp-circuitid
 et
 .B dhcp-remoteid.
@@ -954,33 +1299,54 @@ configuration dhcp-host de Dnsmasq, ainsi que le contenu des fichiers /etc/hosts
 et /etc/ethers.
 .TP
 .B --dhcp-generate-names=tag:<label>[,tag:<label>]
-Générer un nom pour les clients DHCP qui autrement n'en aurait pas, en
-utilisant l'adresse MAC sous sa forme hexadécimale, séparée par des tirets.
+(IPv4 seulement) Générer un nom pour les clients DHCP qui autrement n'en aurait
+pas, en utilisant l'adresse MAC sous sa forme hexadécimale, séparée par des
+tirets.
 Noter que si un hôte fourni un nom, celui-ci sera utilisé de préférence au nom
 autogénéré, à moins que
 .B --dhcp-ignore-names 
 ne soit positionné.
 .TP
 .B --dhcp-broadcast=[tag:<label>[,tag:<label>]]
-Lorsque tous les labels fournis dans l'option sont présents, toujours utiliser
-le broadcast pour communiquer avec l'hôte lorsque celui-ci n'est
-pas configuré. Il est possible de ne spécifier aucun label, auquel cas cette
-option s'applique inconditionnellement. La plupart des clients DHCP nécessitant une réponse par le biais
-d'un broadcast activent une option dans leur requête, ce qui fait que cela
-se fait automatiquement, mais ce n'est pas la cas de certains vieux clients BOOTP.
-.TP
-.B \-M, --dhcp-boot=[tag:<label>,]<nom de fichier>,[<nom de serveur>[,<adresse de serveur>]]
-Spécifie les options BOOTP devant être retournées par le serveur DHCP. Le nom de
-serveur ainsi que l'adresse sont optionnels : s'ils ne sont pas fournis, le nom
-est laissé vide et l'adresse fournie est celle de la machine sur laquelle
-s'exécute Dnsmasq. Si Dnsmasq founit un service TFTP (voir
+(IPv4 seulement) Lorsque tous les labels fournis dans l'option sont présents,
+toujours utiliser le broadcast pour communiquer avec l'hôte lorsque celui-ci
+n'est pas configuré. Il est possible de ne spécifier aucun label, auquel cas
+cette option s'applique inconditionnellement. La plupart des clients DHCP
+nécessitant une réponse par le biais d'un broadcast activent une option dans
+leur requête, ce qui fait que cela se fait automatiquement, mais ce n'est pas
+le cas de certains vieux clients BOOTP.
+.TP
+.B \-M, --dhcp-boot=[tag:<label>,]<nom de fichier>,[<nom de serveur>[,<adresse de serveur>|<nom du serveur tftp>]]
+(IPv4 seulement) Spécifie les options BOOTP devant être retournées par le
+serveur DHCP. Le nom de serveur ainsi que l'adresse sont optionnels : s'ils
+ne sont pas fournis, le nom est laissé vide et l'adresse fournie est celle de
+la machine sur laquelle s'exécute Dnsmasq. Si Dnsmasq founit un service TFTP (voir
 .B --enable-tftp
 ), alors seul un nom de fichier est requis ici pour permettre un démarrage par
 le réseau.
 Si d'éventuels labels sont fournis, ils doivent coïncider avec
 ceux du client pour que cet élement de configuration lui soit envoyé.
-.TP
-.B --pxe-service=[tag:<label>,]<CSA>,<entrée de menu>[,<nom de fichier>|<type de service de démarrage>][,<adresse de serveur>]
+Une adresse de serveur TFTP peut être spécifiée à la place de l'adresse IP,
+sous la forme d'un nom de domaine qui sera cherché dans le fichier /etc/hosts.
+Ce nom peut être associé dans /etc/hosts avec plusieurs adresses IP, auquel cas
+celles-ci seront utilisées tour à tour (algorithme round-robin).
+Cela peut-être utiliser pour équilibrer la charge tftp sur plusieurs serveurs.
+.TP
+.B --dhcp-sequential-ip
+Dnsmasq est conçu pour choisir l'adresse IP des clients DHCP en utilisant
+un hachage de l'adresse MAC du client. Cela permet en général à l'adresse
+IP du client de rester stable au fil du temps, même lorsque le client laisse
+expirer son bail DHCP de temps en temps. Dans ce mode de fonctionnement par
+défaut, les adresses IP sont distribuées de façon pseudo-aléatoire dans la
+totalité de la plage d'adresses utilisable. Il existe des circonstances (par
+exemples pour du déploiement de serveur) où il est plus pratique d'allouer les
+adresses IP de manière séquentielle, en commençant par la plus petite adresse
+disponible, et c'est ce mode de fonctionnement qui est permis par cette option.
+Veuillez noter que dans ce mode séquentiel, les clients qui laissent expirer
+leur bail ont beaucoup plus de chance de voir leur adresse IP changer, aussi
+cette option ne devrait pas être utilisée dans un cas général.
+.TP
+.B --pxe-service=[tag:<label>,]<CSA>,<entrée de menu>[,<nom de fichier>|<type de service de démarrage>][,<adresse de serveur>|<nom de serveur>]
 La plupart des ROMS de démarrage PXE ne permettent au système PXE que la simple
 obtention d'une adresse IP, le téléchargement du fichier spécifié dans
 .B dhcp-boot
@@ -998,17 +1364,20 @@ client PXE qu'il faut télécharger ce fichier via TFTP, soit depuis ce serveur
 (l'option
 .B enable-tftp 
 doit être spécifiée pour que cela marche), soit depuis un autre serveur TFTP
-si une adresse de serveur est fournie.
+si une adresse ou un nom de serveur est fournie.
 Veuillez noter que le suffixe de "couche" (en principe ".0") est fourni par PXE
 et ne doit pas être rajouté au nom de fichier. Si une valeur numérique entière
 est fournir pour le type de démarrage, en remplacement du nom de fichier, le
 client PXE devra chercher un service de démarrage de ce type sur le réseau.
 Cette recherche peut être faite via broadcast ou directement auprès d'un
-serveur si son adresse IP est fournie dans l'option.
+serveur si son adresse IP ou son nom sont fournis dans l'option.
 Si aucun nom de fichier n'est donné ni aucune valeur de type de service de
 démarrage n'est fournie (ou qu'une valeur de 0 est donnée pour le type de
 service), alors l'entrée de menu provoque l'interruption du démarrage par
-le réseau et la poursuite du démarrage sur un média local.
+le réseau et la poursuite du démarrage sur un média local. L'adresse de serveur
+peut être donnée sous la forme de nom de domaine qui est recherché dans
+/etc/hosts. Ce nom peut-être associé à plusieurs adresses IP, qui dans ce cas
+sont utilisées à tour de rôle (en "round-robin").
 .TP
 .B --pxe-prompt=[tag:<label>,]<invite>[,<délai>]
 Cette option permet d'afficher une invite à la suite du démarrage PXE. Si un
@@ -1041,59 +1410,79 @@ créant des milliers de baux et utilisant beaucoup de mémoire dans le processus
 Dnsmasq.
 .TP
 .B \-K, --dhcp-authoritative
-Cette option doit être donnée lorsque Dnsmasq est le seul serveur DHCP sur le
-réseau. Cela change le comportement par défaut qui est celui d'un strict respect
-des RFC, afin que les requêtes DHCP pour des baux inconnus par des hôtes
-inconnus ne soient pas ignorées. Cela permet à de nouveaux hôtes d'obtenir des
-baux sans tenir compte de fastidieuses temporisations ("timeout"). Cela permet
-également à Dnsmasq de reconstruire sa base de donnée contenant les baux sans
-que les clients n'aient besoin de redemander un bail, si celle-ci est perdue.
+Doit être spécifié lorsque dnsmasq est réellement le seul serveur DHCP
+sur le réseau. Pour DHCPv4, cela change le comportement par défaut qui est
+celui d'un strict respect des RFC, afin que les requêtes DHCP pour des baux
+inconnus par des hôtes inconnus ne soient pas ignorées. Cela permet à de
+nouveaux hôtes d'obtenir des baux sans tenir compte de fastidieuses
+temporisations ("timeout"). Cela permet également à Dnsmasq de reconstruire
+sa base de données contenant les baux sans que les clients n'aient besoin de
+redemander un bail, si celle-ci est perdue.
+Dans le cas de DHCPv6, cela positionne la priorité des réponses à 255 (le
+maximum) au lieu de 0 (le minimum).
 .TP
 .B --dhcp-alternate-port[=<port serveur>[,<port client>]]
-Change les ports utilisés par défaut pour le DHCP. Si cette option est donnée
-toute seule sans arguments, alors change les ports utilisés pour le DHCP
-de 67 et 68 respectivement à 1067 et 1068. Si un seul argument est donné, ce
+(IPv4 seulement) Change les ports utilisés par défaut pour le DHCP. Si cette
+option est donnée seule sans argument, alors change les ports utilisés pour le
+DHCP de 67 et 68 respectivement à 1067 et 1068. Si un seul argument est donné, ce
 numéro est utilisé pour le port serveur et ce numéro plus 1 est utilisé pour le
 port client. Enfin, en fournissant deux numéros de ports, il est possible de
 spécifier arbitrairement 2 ports à la fois pour le serveur et pour le client DHCP.
 .TP
 .B \-3, --bootp-dynamic[=<identifiant de réseau>[,<identifiant de réseau>]]
-Permet l'allocation dynamique d'adresses IP à des clients BOOTP. Utiliser cette
-option avec précaution, une adresse allouée à un client BOOTP étant perpétuelle,
-et de fait n'est plus disponibles pour d'autres hôtes. Si aucun argument n'est
-donné, alors cette option permet une allocation dynamique dans tous les cas. Si
-des arguments sont spécifiés, alors l'allocation ne se fait que lorsque tous
-les identifiants coïncident. Il est possible de répeter cette option avec
-plusieurs jeux d'arguments.
+(IPv4 seulement) Permet l'allocation dynamique d'adresses IP à des clients BOOTP.
+Utiliser cette option avec précaution, une adresse allouée à un client BOOTP
+étant perpétuelle, et de fait n'est plus disponibles pour d'autres hôtes. Si
+aucun argument n'est donné, alors cette option permet une allocation dynamique
+dans tous les cas. Si des arguments sont spécifiés, alors l'allocation ne se
+fait que lorsque tous les identifiants coïncident. Il est possible de répeter
+cette option avec plusieurs jeux d'arguments.
 .TP
 .B \-5, --no-ping
-Par défaut, le serveur DHCP tente de s'assurer qu'une adresse n'est pas utilisée
-avant de l'allouer à un hôte. Cela est fait en envoyant une requête ICMP de type
-"echo request" (aussi connue sous le nom de "ping") à l'adresse en question. Si
-le serveur obtient une réponse, alors l'adresse doit déjà être utilisée et une
-autre est essayée. Cette option permet de supprimer cette vérification. A
-utiliser avec précaution.
+(IPv4 seulement) Par défaut, le serveur DHCP tente de s'assurer qu'une adresse
+n'est pas utilisée avant de l'allouer à un hôte. Cela est fait en envoyant une
+requête ICMP de type "echo request" (aussi connue sous le nom de "ping") à
+l'adresse en question. Si le serveur obtient une réponse, alors l'adresse doit
+déjà être utilisée et une autre est essayée. Cette option permet de supprimer
+cette vérification. A utiliser avec précaution.
 .TP
 .B --log-dhcp
 Traces additionnelles pour le service DHCP : enregistre toutes les options
 envoyées aux clients DHCP et les labels utilisés pour la
 détermination de celles-ci.
 .TP
+.B --quiet-dhcp, --quiet-dhcp6, --quiet-ra
+Supprime les logs des opérations de routine des protocoles concernés. Les
+erreurs et les problèmes seront toujours enregistrés. L'option --log-dhcp
+prends le pas sur --quiet-dhcp et quiet-dhcp6.
+.TP
 .B \-l, --dhcp-leasefile=<chemin de fichier>
 Utilise le fichier dont le chemin est fourni pour stocker les informations de
 baux DHCP.
-.TP 
+.TP
+.B --dhcp-duid=<ID d'entreprise>,<uid>
+(IPv6 seulement) Spécifie le numéro d'UID de serveur persistant que le serveur
+DHCPv6 doit utiliser. Cette option n'est normalement pas requise, Dnsmasq
+créant un DUID automatiquement lorsque cela est nécessaire. Lorsque cette
+option est positionnée, elle fournit à Dnsmasq les données nécessaires à la
+création d'un DUID de type DUID-EN. Veuillez noter qu'une fois créé, le DUID
+est stocké dans la base des baux, aussi changer entre un DUID créé
+automatiquement et un DUID-EN et vice-versa impose de réinitialiser la base de
+baux. Le numéro d'ID d'entreprise est assigné par l'IANA, et l'uid est une
+chaine hexadécimale unique à chaque serveur.
+.TP
 .B \-6 --dhcp-script=<chemin de fichier>
-Lorsqu'un bail DHCP est créé, ou qu'un ancien est supprimé, le fichier dont le
+Lorsqu'un bail DHCP est créé, qu'un ancien est supprimé, ou qu'un transfert
+TFTP est terminé, le fichier dont le
 chemin  est spécifié est exécuté. Le <chemin de fichier> doit être un chemin
 absolu, aucune recherche n'est effectuée via la variable d'environnement PATH.
 Les arguments fournis à celui-ci sont soit
 "add" ("ajouter"), "old" ("ancien") ou "del" ("supprimer"), suivi de l'adresse
-MAC de l'hôte puis l'adresse IP et le nom d'hôte si celui-ci est
-connu."add" signifie qu'un bail a été créé, "del" signifie qu'il a été supprimé,
-"old" notifie que le bail existait au lancement de Dnsmasq, ou un changement
-d'adresse MAC ou de nom d'hôte pour un bail existant (ou, dans le cas où
-leasefile-ro est spécifié, un changement de durée de bail ou d'identifiant
+MAC de l'hôte (ou le DUID pour IPv6) puis l'adresse IP et le nom d'hôte si
+celui-ci est connu."add" signifie qu'un bail a été créé, "del" signifie qu'il a
+été supprimé, "old" notifie que le bail existait au lancement de Dnsmasq, ou un
+changement d'adresse MAC ou de nom d'hôte pour un bail existant (ou, dans le cas
+où leasefile-ro est spécifié, un changement de durée de bail ou d'identifiant
 d'hôte). Si l'adresse Mac est d'un type de réseau autre qu'ethernet, il est
 nécessaire de la préceder du type de réseau, par exemple "06-01:23:45:67:89:ab"
 pour du token ring. Le processus est exécuté en temps que super-utilisateur 
@@ -1103,18 +1492,16 @@ pour changer son UID pour celle d'un utilisateur non-privilégié.
 L'environnement est hérité de celui de l'invocation du processus Dnsmasq,
 auquel se rajoute quelques unes ou toutes les variables décrites ci-dessous :
 
-DNSMASQ_CLIENT_ID, si l'hôte a fourni un identifiant de client.
+Pour IPv4 et IPv6 :
 
 DNSMASQ_DOMAIN si le nom de domaine pleinement qualifié de l'hôte est connu, la
 part relative au domaine y est stockée. (Notez que le nom d'hôte transmis comme
 argument au script n'est jamais pleinement qualifié).
 
-Si le client fournit une information de classe de vendeur, un nom d'hôte, ou
-des classes d'utilisateur, celles-ci sont fournies dans les
-variables DNSMASQ_VENDOR_CLASS et DNSMASQ_USER_CLASS0 à DNSMASQ_USER_CLASSn
-et DNSMASQ_SUPPLIED_HOSTNAME respectivement, mais seulement pour les actions
-"add" et "old" lorsqu'un hôte reprend un bail existant, ces variables n'étant
-pas stockées dans la base de baux de Dnsmasq.
+Si le client fournit un nom d'hôte, DNSMASQ_SUPPLIED_HOSTNAME.
+
+Si le client fournit des classes d'utilisateur, DNSMASQ_USER_CLASS0 à
+DNSMASQ_USER_CLASSn.
 
 Si Dnsmasq a été compilé avec l'option HAVE_BROKEN_RTC ("horloge RTC
 défectueuse"), alors la durée du bail (en secondes) est stockée dans la
@@ -1137,6 +1524,38 @@ relai DHCP pour contacter Dnsmasq, si l'adresse IP du relai est connue.
 DNSMASQ_TAGS contient tous les labels fournis pendant la transaction DHCP,
 séparés par des espaces.
 
+DNSMASQ_LOG_DHCP est positionné si
+.B --log-dhcp
+est activé.
+
+Pour IPv4 seulement :
+
+DNSMASQ_CLIENT_ID, si l'hôte a fourni un identifiant de client.
+
+DNSMASQ_CIRCUIT_ID, DNSMASQ_SUBSCRIBER_ID, DNSMASQ_REMOTE_ID si un relai DHCP a
+rajouté l'une de ces options.
+
+Si le client fournit une information de classe de vendeur, DNSMASQ_VENDOR_CLASS.
+
+Pour IPv6 seulement :
+
+Si le client fournit une classe de vendeur (vendor-class), positionne
+DNSMASQ_VENDOR_CLASS_ID avec comme contenu le numéro IANA de l'entreprise pour
+la classe, et DNSMASQ_VENDOR_CLASS0..DNSMASQ_VENDOR_CLASSn pour les données.
+
+DNSMASQ_SERVER_DUID contient le DUID du serveur : cette valeur est la même
+pour chaque appel au script.
+
+DNSMASQ_IAID contenant l'IAID pour le bail. Si le bail est une allocation
+temporaire, cela est préfixé par le caractère 'T'.
+
+DNSMASQ_MAC contient l'adresse MAC du client, si celle-ci est connue.
+
+A noter que le nom d'hôte fourni, la classe de vendeur ou les données de classe
+d'utilisateur sont uniquement fournies pour les actions "add" ou l'action "old"
+lorsqu'un hôte reprend un bail existant, puisque ces informations ne sont pas
+conservées dans la base de baux de dnsmasq.
+
 Tous les descripteurs de fichiers sont fermés, sauf stdin, stdout et stderr qui
 sont ouverts sur /dev/null (sauf en mode déverminage).
 
@@ -1153,11 +1572,72 @@ Au démarrage de Dnsmasq, le script sera invoqué pour chacun des baux existants
 dans le fichier des baux. Le script sera lancé avec l'action "del" pour les
 baux expirés, et "old" pour les autres. Lorsque Dnsmasq reçoit un signal HUP,
 le script sera invoqué avec une action "old" pour tous les baux existants.
-.TP 
+
+Il existe deux autres actions pouvant apparaître comme argument au script :
+"init" et "tftp". D'autres sont susceptibles d'être rajoutées dans le futur,
+aussi les scripts devraient-être écrits de sorte à ignorer les actions
+inconnues. "init" est décrite ci-dessous dans
+.B --leasefile-ro.
+L'action "tftp" est invoquée lorsqu'un transfert de fichier TFTP s'est
+terminé. Ses arguments sont la taille du fichier en octets, l'adresse à
+laquelle le fichier a été envoyé, ainsi que le chemin complet du fichier.
+
+.TP
+.B --dhcp-luascript=<chemin>
+Spécifie un script écrit en Lua, devant être exécuté lorsque des baux sont
+créés, détruits ou modifiés. Pour utiliser cette option, dnsmasq doit être
+compilé avec avec le support de Lua. L'interpréteur Lua est initialisé une
+seule fois, lorsque dnsmasq démarre, ce qui fait que les variables globales
+persistent entre les évênements liés aux baux. Le code Lua doit définir une
+fonction
+.B lease
+et peut fournir des fonctions
+.B init
+et
+.B shutdown
+qui sont appellées, sans arguments, lorsque dnsmasq démarre ou s'arrête.
+Il peut également fournir une fonction
+.B tftp.
+
+La fonction
+.B lease
+reçoit les informations détaillées dans
+.B --dhcp-script. 
+Il reçoit deux arguments. Le premier spécifie l'action, qui est une chaîne de
+caractères contenant les valeurs "add" (ajout), "old" (réactivation d'un bail
+existant) ou "del" (suppression). Le deuxième est une table contenant des
+paires de valeurs de labels. Les labels correspondent pour l'essentiel aux
+valeurs d'environnement détaillées ci-dessus, ainsi le label "domain" (domaine)
+contient les mêmes données que la variable d'environnement DNSMASQ_DOMAIN. Il
+existe quelques labels supplémentaires contenant les données fournies comme
+arguments à
+.B --dhcp-script. 
+Ces labels sont
+.B mac_address, ip_address
+(pour respectivement l'adresse MAC et l'adresse IP)
+et
+.B hostname
+(le nom d'hôte) dans le cas d'IPv4, et
+.B client_duid, ip_address
+(valeur DUID du client et adresse IP respectivement)
+ainsi que
+.B hostname
+(le nom d'hôte) dans le cas d'IPv6.
+
+La fonction
+.B tftp
+est appelée de la même façon que la fonction "lease", et la table contient les
+labels
+.B destination_address,
+.B file_name
+et
+.B file_size
+(respectivement "adresse de destination", "nom de fichier" et "taille de fichier").
+.TP
 .B --dhcp-scriptuser
-Spécifie l'utilisateur sous lequel le script lease-change doit être exécuté. La
-valeur par défaut correspond à l'utilisateur root mais peut-être changée par le
-biais de cette option.
+Spécifie l'utilisateur sous lequel le script shell lease-change ou le script
+doivent être exécutés. La valeur par défaut correspond à l'utilisateur root
+mais peut-être changée par le biais de cette option.
 .TP
 .B \-9, --leasefile-ro
 Supprimer complètement l'usage du fichier servant de base de donnée pour les
@@ -1178,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.
+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
@@ -1219,7 +1702,7 @@ Si la gamme d'adresse est fournie sous la forme
 qui a pour effect d'ajouter --local-declarations aux requêtes DNS directes et
 inverses. C-à-d
 .B --domain=thekelleys.org.uk,192.168.0.0/24,local
-est indentique à
+est identique à
 .B --domain=thekelleys.org.uk,192.168.0.0/24
 --local=/thekelleys.org.uk/ --local=/0.168.192.in-addr.arpa/
 La taille de réseau doit-être de 8, 16 ou 24 pour être valide.
@@ -1242,12 +1725,61 @@ sans gamme d'adresses de spécifié lorsque l'option
 .B --dhcp-fqdn 
 est configurée.
 .TP
-.B --enable-tftp[=<interface>]
+.B --dhcp-client-update
+Normalement, lorsque dnsmasq fournit un bail DHCP, il positionne un label
+dans l'option FQDN pour indiquer au client qu'il ne doit pas tenter de faire
+une mise à jour DDNS avec son nom et son adresse IP. Ceci parce que la paire
+Nom-IP est rajoutée automatiquement dans la partie DNS de dnsmasq. Cette option
+inhibe ce comportement ce qui est utile, par exemple, pour permettre aux clients
+Windows de la mise à jour de serveurs Active Directory. Voir la RFC 4702 pour
+plus de détails.
+.TP
+.B --enable-ra
+Active la fonctionalité d'annonces routeurs IPv6 ("IPv6 Router Advertisement").
+DHCPv6 ne gère pas la configuration complète du réseau de la même façon que
+DHCPv4. La découverte de routeurs et la découverte (éventuelle) de préfixes pour
+la création autonome d'adresse sont gérées par un protocole différent.
+Lorsque DHCP est utilisé, seul un sous-ensemble de tout ceci est nécessaire et
+dnsmasq est à même de le gérer, en utilisant la configuration DHCP présente pour
+fournir la majorité des données. Lorsque les annonces routeurs (RA pour "Router
+Advertisement") sont activées, dnsmasq va annoncer un préfixe pour chaque
+dhcp-range et, par défaut, fournir comme valeur de routeur et de DNS récursif
+la valeur d'adresse link-local appropriée parmi celles de la machine sur
+laquelle tourne dnsmasq.
+Par défaut, les bits "managed address" sont positionnés, et le bit "use SLAAC"
+("utiliser SLAAC") est réinitialisé. Cela peut-être changé pour des
+sous-réseaux donnés par le biais du mot clef de mode décris dans
+.B --dhcp-range.
+Les paramètres DNS du RFC6106 sont inclus dans les annonces. Par défaut,
+l'adresse link-local appropriée parmi celles de la machine sur laquelle tourne
+dnsmasq est spécifiée comme DNS récursif. Si elles sont fournies, les
+options dns-server et domain-search sont utilisées respectivement pour RDNSS et
+DNSSL.
+.TP
+.B --ra-param=<interface>,[high|low],[[<intervalle d'annonce routeur>],<durée de vie route>]
+Configure pour une interface donnée des valeurs pour les annonces routeurs
+différentes des valeurs par défaut. La valeur par défaut du champ priorité
+pour le routeur peut-être changée de "medium" (moyen) à "high" (haute) ou
+"low" (basse). Par exemple :
+.B --ra-param=eth0,high.
+Un intervalle (en secondes) entre les annonces routeur peut-être fourni par :
+.B --ra-param=eth0,60.
+La durée de vie de la route peut-être changée ou mise à zéro, auquel cas 
+le routeur peut annoncer les préfixes mais pas de route :
+.B --ra-parm=eth0,0,0
+(une valeur de zéro pour l'intervalle signifie qu'il garde la valeur par défaut).
+Ces trois paramètres peuvent-être configurés en une fois :
+.B --ra-param=low,60,1200
+La valeur pour l'interface peut inclure un caractère joker.
+.TP
+.B --enable-tftp[=<interface>[,<interface>]]
 Active la fonction serveur TFTP. Celui-ci est de manière délibérée limité aux
 fonctions nécessaires au démarrage par le réseau ("net-boot") d'un client. Seul
 un accès en lecture est possible; les extensions tsize et blksize sont supportées
-(tsize est seulement supporté en mode octet). Voir dans la section NOTES les
-informations relatives à la spécification de l'interface.
+(tsize est seulement supporté en mode octet). Sans argument optionel, le service
+TFTP est fourni sur les mêmes interfaces que le service DHCP. Si une liste
+d'interfaces est fournie, cela définit les interfaces sur lesquelles le
+service TFTP sera activé.
 .TP
 .B --tftp-root=<répertoire>[,<interface>]
 Les fichiers à fournir dans les transferts TFTP seront cherchés en prenant le
@@ -1285,6 +1817,13 @@ Sans cela, en effet, l'accès de tous les fichiers du serveur pour lequel le
 droit de lecture pour tout le monde est positionné ("world-readable") devient
 possible par n'importe quel hôte sur le réseau.
 .TP
+.B --tftp-lowercase
+Converti les noms de fichiers des requêtes TFTP en minuscules. Cela est utile
+pour les requêtes effectuées depuis les machines Windows, dont les systèmes
+de fichiers sont insensibles à la casse et pour lesquels la détermination
+de la casse est parfois un peu aléatoire. A noter que le serveur tftp de
+dnsmasq converti systématiquement les "\\" en "/" dans les noms de fichiers.
+.TP
 .B --tftp-max=<connexions>
 Définit le nombre maximum de connexions TFTP simultanées autorisées. La valeur
 par défaut est de 50. Lorsqu'un grand nombre de connexions TFTP est spécifié,
@@ -1417,7 +1956,6 @@ Dans le cas de l'utilisation du logiciel logrotate, les options requises sont
 .B create 
 et
 .B delaycompress.
-
  
 .PP
 Dnsmasq est un logiciel de transmission de requêtes DNS : il n'est pas capable
@@ -1509,6 +2047,17 @@ labels définis pour l'hôte considéré. (dans le cas de l'utilisation dans une
 ligne de commande au lieu d'un fichier de configuration, ne pas oublier
 d'échapper le caractère !, qui est un méta-caractère d'interpréteur de commande
 shell).
+
+Lors de la sélection d'une option, une étiquette spécifiée par dhcp-range
+passe après les autres étiquettes, ce qui permet de facilement remplacer des
+option génériques pour des hôtes spécifiques, ainsi :
+.B dhcp-range=set:interface1,......
+.B dhcp-host=set:monhote,.....
+.B dhcp-option=tag:interface1,option:nis-domain,"domaine1"
+.B dhcp-option=tag:monhote,option:nis-domain,"domaine2"
+va positionner l'option NIS-domain à domaine1 pour les hôtes dans la plage
+d'adresse, sauf pour monhote pour lequel cette valeur sera domaine2.
+
 .PP
 Veuillez noter que pour
 .B dhcp-range
@@ -1538,50 +2087,166 @@ supprime la nécessité des associations statiques). Le paramètre
 que le label "bootp", permettant un certain contrôle sur les options retournées
 aux différentes classes d'hôtes.
 
-Il est possible de spécifier un nom d'interface à
-.B dhcp-range
-sous la forme "interface:<nom d'interface>". La sémantique est comme suit :
-Pour le DHCP, s'il existe une autre valeur de dhcp-range pour laquelle
-_aucun_ nom d'interface n'est donné, alors le nom d'interface est ignoré
-et dnsmasq se comporte comme si la partie spécifiant l'interface n'existait
-pas, sinon le service DHCP n'est fourni qu'aux interfaces mentionnées dans
-les déclarations dhcp-range. Pour le DNS, si il n'y a pas d'option
-.B --interface
-ou
-.B --listen-address
-, alors le comportement n'est pas impacté par la spécification d'interface. Si
-l'une ou l'autre de ces options est présente, alors les interfaces mentionnées
-dans les plages d'adresses dhcp-range sont rajoutées à la liste de celles
-où le service DNS est assuré.
-
-De manière similaire,
-.B enable-tftp
-peut prendre un nom d'interface, ce qui active le TFTP pour cette seule
-interface, en ignorant les options
-.B --interface 
-ou
-.B --listen-address
-De plus, 
-.B --tftp-secure
-, 
-.B --tftp-unique-root
+
+.SH CONFIGURATION EN TEMPS QUE SERVEUR FAISANT AUTORITÉ
+.PP 
+Configurer dnsmasq pour agir en temps que serveur DNS faisant autorité est
+compliqué par le fait que cela implique la configuration de serveurs DNS
+externes pour mettre en place la délégation. Seront présentés ci-dessous trois
+scénarios de complexité croissante. Le pré-requis pour chacun de ces scénarios
+est l'existence d'une adresse IP globalement disponible, d'un enregistrement de
+type A ou AAAA pointant vers cette adresse, ainsi que d'un serveur DNS externe
+capable d'effectuer la délégation de la zone en question. Pour la première
+partie de ces explications, nous allons appeller serveur.exemple.com
+l'enregistrement A (ou AAAA) de l'adresse globalement accessible, et
+notre.zone.com la zone pour laquelle dnsmasq fait autorité.
+
+La configuration la plus simple consiste en deux lignes de configuration,
+sous la forme :
+.nf
+.B auth-server=serveur.exemple.com,eth0
+.B auth-zone=notre.zone.com,1.2.3.0/24
+.fi
+
+ainsi que deux enregistrements dans le DNS externe :
+
+.nf
+serveur.exemple.com       A    192.0.43.10
+notre.zone.com            NS    serveur.exemple.com
+.fi
+
+eth0 est l'interface réseau externe sur laquelle dnsmasq écoute, dont l'adresse
+IP (globalement accessible) est 192.0.43.10. 
+
+A noter que l'adresse IP externe peut parfaitement être dynamique (par exemple
+attribuée par un FAI via DHCP ou PPP). Dans ce cas, l'enregistrement de type A
+doit être lié à cet enregistrement dynamique par l'une ou l'autre des techniques
+habituelles de système DNS dynamique.
+
+Un exemple plus complexe mais en pratique plus utile correspond au cas où
+l'adresse IP globalement accessible se trouve dans la zone pour laquelle
+dnsmasq fait autorité, le plus souvent à la racine. Dans ce cas nous avons :
+
+.nf
+.B auth-server=notre.zone.com,eth0
+.B auth-zone=notre.zone.com,1.2.3.0/24
+.fi
+
+.nf
+notre.zone.com             A    1.2.3.4
+notre.zone.com            NS    our.zone.com
+.fi
+
+L'enregistrement A pour notre.zone.com est dorénavant un enregistrement "colle"
+qui résoud le problème de poule et d'oeuf consistant à trouver l'adresse IP
+du serveur de nom pour notre.zone.com lorsque l'enregistrement se trouve dans
+la zone en question. Il s'agit du seul rôle de cet enregistrement : comme dnsmasq
+fait désormais autorité pour notre.zone.com, il doit également fournir cet
+enregistrement. Si l'adresse externe est statique, cela peut-être réalisé par
+le biais d'une entrée dans
+.B /etc/hosts 
+ou via un
+.B --host-record.
+
+.nf
+.B auth-server=notre.zone.com,eth0
+.B host-record=notre.zone.com,1.2.3.4
+.B auth-zone=notre.zone.com,1.2.3.0/24
+.fi
+
+Si l'adresse externe est dynamique, l'adresse associée à notre.zone.com doit
+être dérivée de l'interface correspondante. Cela peut être fait en utilisant
+.B interface-name
+Sous la forme :
+
+.nf
+.B auth-server=notre.zone.com,eth0
+.B interface-name=notre.zone.com,eth0
+.B auth-zone=notre.zone.com,1.2.3.0/24
+.fi
+
+La configuration finale rajoute à cette base un serveur DNS secondaire. Il
+s'agit d'un autre serveur DNS qui apprend les données DNS de la zone en
+effectuant un transfert de zone, et qui joue le rôle de serveur de secours
+au cas où le serveur principal devenait inaccessible. La configuration
+de ce serveur secondaire sort du cadre de cette page de manuel. Les éléments
+de configuration à rajouter dans dnsmasq sont les simples :
+
+.nf
+.B auth-sec-servers=secondaire.monfai.com
+.fi
+
 et
-.B --tftp-no-blocksize
-sont ignorées pour les requêtes sur de telles interfaces. (une directive
-.B --tftp-root
-donnant le chemin de la racine et une interface doit-être fournie).
-
-Ces règles peuvent paraître étrange à première vue, mais elles permettent
-d'ajouter à la configuration de dnsmasq des lignes de configuration de la
-forme "dhcp-range=interface:virt0,192.168.0.4,192.168.0.200" afin de fournir
-un service DHCP et DNS sur cette interface, sans pour autant affecter les
-services fournis sur d'autres interfaces, malgré l'absence de paramètres
-"interface=<interface>" sur les autres lignes de configuration.
-"enable-tftp=virt0" et "tftp-root=<root>,virt0" effectuent la même chose pour
-TFTP.
-L'idée de tout cela est de permettre l'addition de telles lignes
-automatiquement par libvirt ou un système équivalent, sans perturbation
-d'une configuration manuelle existant par ailleurs.
+
+.nf
+notre.zone.com           NS    secondaire.monfai.com
+.fi
+
+L'addition d'une option auth-sec-servers active les transferts de zone dans
+dnsmasq, ce qui permet au serveur secondaire de venir collecter les données
+DNS. Si vous souhaitez restreindre l'accès à ces données à des hôtes
+spécifiques, vous pouvez le faire via :
+
+.nf
+.B auth-peer=<adresse IP du serveur secondaire>
+.fi
+
+Dnsmasq joue le rôle de serveur faisant autorité pour les domaines in-addr.arpa
+et ip6.arpa associés aux sous-réseaux définis dans la déclaration de zone
+auth-zone, ce qui fait que les requêtes DNS inversées (de l'adresse vers
+le nom) peuvent-simplement être configurées avec un enregistrement NS
+adéquat. Par exemple, comme nous définissons plus haut les adresses
+1.2.3.0/24 :
+.nf
+ 3.2.1.in-addr.arpa  NS    notre.zone.com
+.fi
+
+Veuillez noter que pour l'instant, les zones inverses ne sont pas
+disponibles dans les transferts de zone, donc il est inutile de configurer
+de serveur secondaire pour la résolution inverse.
+
+.PP
+Lorsque dnsmasq est configuré en temps que serveur faisant autorité,
+les données suivantes sont utilisées pour peupler la zone considérée :
+.PP
+.B --mx-host, --srv-host, --dns-rr, --txt-record, --naptr-record
+, pour autant que les noms des enregistrements se trouvent dans la zone en
+question.
+.PP
+.B --cname
+pour peu que le nom soit dans le domaine. Si la cible du CNAME n'est
+pas pleinement qualifiée, alors elle est qualifiée avec le nom de la
+zone pour laquelle le serveur fait autorité.
+.PP
+Les adresses IPv4 et IPv6 extraites de /etc/hosts (et
+.B --addn-hosts
+) ainsi que les options
+.B --host-record
+fournissant des adresses situées dans l'un des sous-réseaux spécifiés dans 
+.B --auth-zone.
+.PP
+Adresses spécifiées par
+.B --interface-name.
+Dans ce cas, l'adresse n'est pas limitée à l'un des sous-réseaux donné dans
+.B --auth-zone. 
+
+.PP
+Les adresses de baux DHCP, si l'adresse est située dans l'un des sous-réseaux de
+.B --auth-zone
+OU dans une plage DHCP construite. Dans le mode par défaut, où le bail
+DHCP a un nom non qualifié, et éventuellement pour un nom qualifié construit
+via
+.B --domain
+, alors le nom dans la zone faisant autorité est construit à partir du nom
+non qualifié et du nom de domaine de la zone. Cela peut on non être égal
+celui fourni par
+.B --domain.
+Si l'option
+.B --dhcp-fqdn
+est fournie, alors les noms pleinemenet qualifiés associés aux baux DHCP
+sont utilisés, dès lors qu'ils correspondent au nom de domaine associé
+à la zone.
+
 
 .SH CODES DE SORTIE
 .PP
index 4a237e1..af9d236 100644 (file)
@@ -1,7 +1,7 @@
 Name:       dnsmasq
 Summary:    dnsmasq, DNS forwarder.
-Version:    2.57.1
-Release:    7
+Version:    2.74
+Release:    1
 Group:      System/Network
 License:    GPL-2.0+ or GPL-3.0+
 Source0:    %{name}-%{version}.tar.gz
index 30ecb0e..69d5d35 100644 (file)
--- a/po/de.po
+++ b/po/de.po
 # Simon Kelley <simon@thekelleys.org.uk>, 2005.
 msgid ""
 msgstr ""
-"Project-Id-Version: dnsmasq 2.53rc1\n"
+"Project-Id-Version: dnsmasq 2.74\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2009-06-18 12:24+0100\n"
-"PO-Revision-Date: 2010-05-24 16:29+0200\n"
-"Last-Translator: Matthias Andree <matthias.andree@gmx.de>\n"
+"PO-Revision-Date: 2015-07-22 23:07+0200\n"
+"Last-Translator: Conrad Kostecki <ck@conrad-kostecki.de>\n"
 "Language-Team: German <de@li.org>\n"
+"Language: de\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 1.8.3\n"
+"X-Poedit-SourceCharset: UTF-8\n"
 
-#: cache.c:761
+#: cache.c:523
+msgid "Internal error in cache."
+msgstr "Interner Fehler im Cache."
+
+#: cache.c:941
 #, c-format
 msgid "failed to load names from %s: %s"
 msgstr "Fehler beim Laden der Namen von %s: %s"
 
-#: cache.c:795 dhcp.c:865
+#: cache.c:967 dhcp.c:828
 #, c-format
 msgid "bad address at %s line %d"
 msgstr "Fehlerhafte Adresse in %s Zeile %d"
 
-#: cache.c:853 dhcp.c:881
+#: cache.c:1018 dhcp.c:844
 #, c-format
 msgid "bad name at %s line %d"
 msgstr "Fehlerhafter Name in %s Zeile %d"
 
-#: cache.c:860 dhcp.c:956
+#: cache.c:1027 dhcp.c:919
 #, c-format
 msgid "read %s - %d addresses"
 msgstr "%s gelesen - %d Adressen"
 
-#: cache.c:899
+#: cache.c:1135
 msgid "cleared cache"
 msgstr "Cache geleert"
 
-#: cache.c:960
+#: cache.c:1164
+#, c-format
+msgid "No IPv4 address found for %s"
+msgstr "Keine IPv4-Adresse für %s gefunden"
+
+#: cache.c:1242
 #, c-format
 msgid "%s is a CNAME, not giving it to the DHCP lease of %s"
 msgstr "%s ist ein CNAME, weise es der DHCP-Lease von %s nicht zu"
 
-#: cache.c:966
+#: cache.c:1266
 #, c-format
 msgid "not giving name %s to the DHCP lease of %s because the name exists in %s with address %s"
 msgstr "Name %s wurde dem DHCP-Lease von %s nicht zugewiesen, da der Name in %s bereits mit Adresse %s existiert"
 
-#: cache.c:1039
+#: cache.c:1421
 #, c-format
 msgid "time %lu"
 msgstr "Zeit %lu"
 
-#: cache.c:1040
+#: cache.c:1422
 #, c-format
 msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries."
 msgstr "Cache Größe %d, %d/%d Cache-Einfügungen verwendeten nicht abgelaufene Cache-Einträge wieder."
 
-#: cache.c:1042
+#: cache.c:1424
 #, c-format
 msgid "queries forwarded %u, queries answered locally %u"
 msgstr "%u weitergeleitete Anfragen, %u lokal beantwortete Anfragen"
 
-#: cache.c:1068
+#: cache.c:1427
+#, c-format
+msgid "queries for authoritative zones %u"
+msgstr "Anfragen nach autoritativen Zonen %u"
+
+#: cache.c:1453
 #, c-format
 msgid "server %s#%d: queries sent %u, retried or failed %u"
 msgstr "Server %s#%d: %u Anfragen gesendet, %u erneut versucht oder fehlgeschlagen"
 
-#: util.c:57
+#: util.c:45
 #, c-format
 msgid "failed to seed the random number generator: %s"
 msgstr "Konnte den Zufallszahlengenerator nicht initialisieren: %s"
 
-#: util.c:189
+#: util.c:205
 msgid "failed to allocate memory"
 msgstr "Konnte Speicher nicht belegen"
 
-#: util.c:227 option.c:573
+#: util.c:250 option.c:601
 msgid "could not get memory"
 msgstr "Speicher nicht verfügbar"
 
-#: util.c:237
+#: util.c:260
 #, c-format
 msgid "cannot create pipe: %s"
 msgstr "Konnte Pipe nicht erzeugen: %s"
 
-#: util.c:245
+#: util.c:268
 #, c-format
 msgid "failed to allocate %d bytes"
 msgstr "Konnte %d Bytes nicht belegen"
 
 # @Simon: not perfect but I cannot get nearer right now.
-#: util.c:350
+#: util.c:437
 #, c-format
 msgid "infinite"
 msgstr "unendlich"
 
-#: option.c:244
+#: option.c:332
 msgid "Specify local address(es) to listen on."
 msgstr "Lokale abzuhörende Adresse(n) angeben."
 
-#: option.c:245
+#: option.c:333
 msgid "Return ipaddr for all hosts in specified domains."
-msgstr "IP-Adresse für alle Hosts in angebenen Domänen festlegen."
+msgstr "IP-Adresse für alle Hosts in angegebenen Domänen festlegen."
 
 # FIXME: the English test is not to the point. Just use a shortened description
 # from the manpage instead. -- MA
-#: option.c:246
+#: option.c:334
 msgid "Fake reverse lookups for RFC1918 private address ranges."
 msgstr "Für private Adressbereiche nach RFC1918 \"keine solche Domain\" liefern."
 
-#: option.c:247
+#: option.c:335
 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."
 msgstr "Diese IP-Adresse als NXDOMAIN interpretieren (wehrt \"Suchhilfen\" ab)."
 
-#: option.c:248
+#: option.c:336
 #, c-format
 msgid "Specify the size of the cache in entries (defaults to %s)."
 msgstr "Größe des Caches (Zahl der Einträge) festlegen (Voreinstellung: %s)."
 
-#: option.c:249
+#: option.c:337
 #, c-format
 msgid "Specify configuration file (defaults to %s)."
 msgstr "Konfigurationsdatei festlegen (Voreinstellung: %s)."
 
-#: option.c:250
+#: option.c:338
 msgid "Do NOT fork into the background: run in debug mode."
 msgstr "NICHT in den Hintergrund gehen: Betrieb im Debug-Modus"
 
-#: option.c:251
+#: option.c:339
 msgid "Do NOT forward queries with no domain part."
 msgstr "Anfragen ohne Domänen-Teil NICHT weiterschicken."
 
-#: option.c:252
+#: option.c:340
 msgid "Return self-pointing MX records for local hosts."
 msgstr "Für lokale Einträge MX-Einträge liefern, die auf sich selbst zeigen."
 
-#: option.c:253
+#: option.c:341
 msgid "Expand simple names in /etc/hosts with domain-suffix."
 msgstr "Erweitere einfache Namen in /etc/hosts mit der Domänen-Endung."
 
-#: option.c:254
+#: option.c:342
 msgid "Don't forward spurious DNS requests from Windows hosts."
 msgstr "'unechte' DNS-Anfragen von Windows-Rechnern nicht weiterleiten"
 
 # @Simon: I'm a bit unsure about "spurious"
-#: option.c:255
+#: option.c:343
 msgid "Enable DHCP in the range given with lease duration."
 msgstr "DHCP für angegebenen Bereich und Dauer einschalten"
 
-#: option.c:256
+#: option.c:344
 #, c-format
 msgid "Change to this group after startup (defaults to %s)."
 msgstr "Nach dem Start in diese Benutzergruppe wechseln (Voreinstellung %s)."
 
-#: option.c:257
+#: option.c:345
 msgid "Set address or hostname for a specified machine."
 msgstr "Adresse oder Hostnamen für einen angegebenen Computer setzen."
 
-#: option.c:258
-#, fuzzy
+#: option.c:346
 msgid "Read DHCP host specs from file."
-msgstr "DHCP-Host-Angaben aus Datei lesen"
+msgstr "DHCP-Host-Angaben aus Datei lesen."
 
-#: option.c:259
-#, fuzzy
+#: option.c:347
 msgid "Read DHCP option specs from file."
-msgstr "DHCP-Optionen aus Datei lesen"
+msgstr "DHCP-Optionen aus Datei lesen."
 
-#: option.c:260
-#, fuzzy
+#: option.c:348
+msgid "Read DHCP host specs from a directory."
+msgstr "DHCP-Host-Angaben aus einem Verzeichnis lesen."
+
+#: option.c:349
+msgid "Read DHCP options from a directory."
+msgstr "DHCP-Optionen aus einem Verzeichnis lesen."
+
+#: option.c:350
 msgid "Evaluate conditional tag expression."
-msgstr "Auswertung eines Ausdrucks bedingter Marken"
+msgstr "Auswertung eines Ausdrucks bedingter Marken."
 
-#: option.c:261
+#: option.c:351
 #, c-format
 msgid "Do NOT load %s file."
 msgstr "%s-Datei NICHT laden."
 
-#: option.c:262
+#: option.c:352
 #, c-format
 msgid "Specify a hosts file to be read in addition to %s."
 msgstr "Hosts-Datei festlegen, die zusätzlich zu %s gelesen wird."
 
-#: option.c:263
+#: option.c:353
+msgid "Read hosts files from a directory."
+msgstr "DHCP-Host-Dateien aus einem Verzeichnis lesen."
+
+#: option.c:354
 msgid "Specify interface(s) to listen on."
 msgstr "Schnittstelle(n) zum Empfang festlegen."
 
-#: option.c:264
+#: option.c:355
 msgid "Specify interface(s) NOT to listen on."
 msgstr "Schnittstelle(n) festlegen, die NICHT empfangen sollen."
 
-#: option.c:265
+#: option.c:356
 msgid "Map DHCP user class to tag."
 msgstr "DHCP-Benutzerklasse auf Marke abbilden."
 
-#: option.c:266
+#: option.c:357
 msgid "Map RFC3046 circuit-id to tag."
 msgstr "RFC3046 \"circuit-id\" auf Marke abbilden."
 
-#: option.c:267
+#: option.c:358
 msgid "Map RFC3046 remote-id to tag."
 msgstr "RFC3046 \"remote-id\" auf Marke abbilden."
 
-#: option.c:268
+#: option.c:359
 msgid "Map RFC3993 subscriber-id to tag."
 msgstr "RFC3993 \"subscriber-id\" auf Marke abbilden."
 
-#: option.c:269
+#: option.c:360
 msgid "Don't do DHCP for hosts with tag set."
 msgstr "Kein DHCP für Hosts mit gesetzter Marke verwenden."
 
-#: option.c:270
+#: option.c:361
 msgid "Force broadcast replies for hosts with tag set."
 msgstr "Rundsendung für Hosts mit gesetzter Marke erzwingen."
 
-#: option.c:271
+#: option.c:362
 msgid "Do NOT fork into the background, do NOT run in debug mode."
 msgstr "NICHT in den Hintergrund wechseln, NICHT im Debug-Modus laufen."
 
-#: option.c:272
+#: option.c:363
 msgid "Assume we are the only DHCP server on the local network."
 msgstr "Voraussetzen, dass wir der einzige DHCP-Server im lokalen Netz sind."
 
-#: option.c:273
+#: option.c:364
 #, c-format
 msgid "Specify where to store DHCP leases (defaults to %s)."
 msgstr "Festlegen, wo DHCP-Leases gespeichert werden (Voreinstellung %s)."
 
-#: option.c:274
+#: option.c:365
 msgid "Return MX records for local hosts."
 msgstr "MX-Einträge für lokale Hosts liefern."
 
-#: option.c:275
+#: option.c:366
 msgid "Specify an MX record."
 msgstr "Einen MX-Eintrag festlegen."
 
-#: option.c:276
+#: option.c:367
 msgid "Specify BOOTP options to DHCP server."
 msgstr "BOOTP-Optionen für DHCP-Server festlegen."
 
-#: option.c:277
+#: option.c:368
 #, c-format
 msgid "Do NOT poll %s file, reload only on SIGHUP."
 msgstr "%s-Datei NICHT abfragen, nur bei SIGHUP neu laden."
 
-#: option.c:278
+#: option.c:369
 msgid "Do NOT cache failed search results."
 msgstr "Fehlerhafte Suchergebnisse NICHT zwischenspeichern."
 
-#: option.c:279
+#: option.c:370
 #, c-format
 msgid "Use nameservers strictly in the order given in %s."
 msgstr "Namensserver streng in der in %s angegebenen Reihenfolge verwenden."
 
-#: option.c:280
+#: option.c:371
 msgid "Specify options to be sent to DHCP clients."
 msgstr "Optionen festlegen, die an DHCP-Klienten gesendet werden."
 
-#: option.c:281
+#: option.c:372
 msgid "DHCP option sent even if the client does not request it."
 msgstr "DHCP-Option, die selbst ohne Klientenanfrage gesendet wird."
 
-#: option.c:282
+#: option.c:373
 msgid "Specify port to listen for DNS requests on (defaults to 53)."
 msgstr "Port zum Abhören der DNS-Anfragen festlegen (53 voreingestellt)."
 
-#: option.c:283
+#: option.c:374
 #, c-format
 msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)."
 msgstr "Maximale unterstützte UDP-Paketgröße für EDNS.0 (Voreinstellung %s)."
 
-#: option.c:284
+#: option.c:375
 msgid "Log DNS queries."
 msgstr "DNS-Anfragen protokollieren."
 
-#: option.c:285
+#: option.c:376
 msgid "Force the originating port for upstream DNS queries."
 msgstr "Ausgehenden Port erzwingen für DNS-Anfragen an vorgelagerte Server."
 
-#: option.c:286
+#: option.c:377
 msgid "Do NOT read resolv.conf."
 msgstr "resolv.conf NICHT lesen."
 
-#: option.c:287
+#: option.c:378
 #, c-format
 msgid "Specify path to resolv.conf (defaults to %s)."
 msgstr "Pfad zu resolv.conf festlegen (%s voreingestellt)."
 
-#: option.c:288
+#: option.c:379
+msgid "Specify path to file with server= options"
+msgstr " Dateipfad mit der Option server= angeben"
+
+#: option.c:380
 msgid "Specify address(es) of upstream servers with optional domains."
 msgstr "Adresse(n) vorgelagerter Server festlegen, optional mit Domänen."
 
-#: option.c:289
+#: option.c:381
+msgid "Specify address of upstream servers for reverse address queries"
+msgstr "Adresse(n) vorgelagerter Server festlegen, für reverse Adressanfragen"
+
+#: option.c:382
 msgid "Never forward queries to specified domains."
 msgstr "Anfragen für angegebene Domänen niemals weiterleiten."
 
-#: option.c:290
+#: option.c:383
 msgid "Specify the domain to be assigned in DHCP leases."
 msgstr "Domäne festlegen, die für DHCP-Leases zugewiesen wird."
 
-#: option.c:291
+#: option.c:384
 msgid "Specify default target in an MX record."
 msgstr "Voreingestelltes Ziel für MX-Einträge festlegen."
 
-#: option.c:292
+#: option.c:385
 msgid "Specify time-to-live in seconds for replies from /etc/hosts."
 msgstr "Gültigkeitsdauer für Antworten aus /etc/hosts festlegen."
 
-#: option.c:293
+#: option.c:386
 msgid "Specify time-to-live in seconds for negative caching."
 msgstr "Gültigkeitsdauer in Sekunden für Caching negativer Ergebnisse festlegen."
 
-#: option.c:294
-#, fuzzy
+#: option.c:387
 msgid "Specify time-to-live in seconds for maximum TTL to send to clients."
 msgstr "Gültigkeitsdauer in Sekunden für Caching negativer Ergebnisse festlegen."
 
-#: option.c:295
+#: option.c:388
+msgid "Specify time-to-live ceiling for cache."
+msgstr "Spezifiziere time-to-live ceiling für Cache."
+
+#: option.c:389
+msgid "Specify time-to-live floor for cache."
+msgstr "Spezifiziere time-to-live floor für Cache."
+
+#: option.c:390
 #, c-format
 msgid "Change to this user after startup. (defaults to %s)."
 msgstr "Nach dem Start diese Benutzerrechte annehmen (%s voreingestellt)."
 
-#: option.c:296
+#: option.c:391
 msgid "Map DHCP vendor class to tag."
 msgstr "DHCP-\"vendor class\" auf Marke abbilden."
 
-#: option.c:297
+#: option.c:392
 msgid "Display dnsmasq version and copyright information."
 msgstr "dnsmasq-Version und Urheberrecht anzeigen."
 
-#: option.c:298
+#: option.c:393
 msgid "Translate IPv4 addresses from upstream servers."
 msgstr "IPv4-Adressen von vorgelagerten Servern übersetzen."
 
-#: option.c:299
+#: option.c:394
 msgid "Specify a SRV record."
 msgstr "SRV-Eintrag festlegen."
 
-#: option.c:300
+#: option.c:395
 msgid "Display this message. Use --help dhcp for known DHCP options."
 msgstr "Diese Hilfe anzeigen. Benutzen Sie --help dhcp für bekannte DHCP-Optionen."
 
-#: option.c:301
+#: option.c:396
 #, c-format
 msgid "Specify path of PID file (defaults to %s)."
 msgstr "Dateipfad für Prozesskennung (PID) festlegen (Voreinstellung: %s)."
 
-#: option.c:302
+#: option.c:397
 #, c-format
 msgid "Specify maximum number of DHCP leases (defaults to %s)."
 msgstr "Höchstzahl der DHCP-Leases festlegen (%s voreingestellt)."
 
-#: option.c:303
+#: option.c:398
 msgid "Answer DNS queries based on the interface a query was sent to."
 msgstr "DNS-Anfragen abhängig der Emfpangsschnittstelle beantworten."
 
-#: option.c:304
+#: option.c:399
 msgid "Specify TXT DNS record."
 msgstr "DNS-TXT-Eintrag festlegen."
 
-#: option.c:305
+#: option.c:400
 msgid "Specify PTR DNS record."
 msgstr "DNS-PTR-Eintrag festlegen."
 
-#: option.c:306
+#: option.c:401
 msgid "Give DNS name to IPv4 address of interface."
 msgstr "Schnittstellennamen zur IPv4-Adresse des Interfaces auflösen."
 
-#: option.c:307
+#: option.c:402
 msgid "Bind only to interfaces in use."
 msgstr "Nur an verwendete Schnittstellen binden."
 
-#: option.c:308
+#: option.c:403
 #, c-format
 msgid "Read DHCP static host information from %s."
 msgstr "Statische DHCP-Host-Information aus %s lesen."
 
-#: option.c:309
+#: option.c:404
 msgid "Enable the DBus interface for setting upstream servers, etc."
 msgstr "DBus-Schnittstelle zum Festlegen vorgelagerter Server usw. festlegen."
 
-#: option.c:310
+#: option.c:405
 msgid "Do not provide DHCP on this interface, only provide DNS."
 msgstr "Auf dieser Schnittstelle kein DHCP anbieten, sondern nur DNS."
 
-#: option.c:311
+#: option.c:406
 msgid "Enable dynamic address allocation for bootp."
 msgstr "Dynamische Adressbelegung für bootp einschalten."
 
-#: option.c:312
+#: option.c:407
 msgid "Map MAC address (with wildcards) to option set."
 msgstr "MAC-Adresse (mit Jokerzeichen) auf Netzmarke abbilden."
 
-#: option.c:313
+#: option.c:408
 msgid "Treat DHCP requests on aliases as arriving from interface."
 msgstr "DHCP-Anfragen von Alias-Schnittstellen für die Hauptschnittstelle beantworten."
 
-#: option.c:314
+#: option.c:409
 msgid "Disable ICMP echo address checking in the DHCP server."
 msgstr "ICMP-Echo-Adressprüfung im DHCP-Server abschalten."
 
-#: option.c:315
-msgid "Script to run on DHCP lease creation and destruction."
+#: option.c:410
+msgid "Shell script to run on DHCP lease creation and destruction."
 msgstr "Skript, das bei Erzeugung/Löschung einer DHCP-Lease laufen soll."
 
-#: option.c:316
+#: option.c:411
+msgid "Lua script to run on DHCP lease creation and destruction."
+msgstr "Lua-Skript, welches bei Erzeugung/Löschung eines DHCP-Leases laufen soll."
+
+#: option.c:412
+msgid "Run lease-change scripts as this user."
+msgstr "Lease-Änderungs-Skript mit den Rechten dieses Nutzers ausführen."
+
+#: option.c:413
 msgid "Read configuration from all the files in this directory."
 msgstr "Konfiguration aus allen Dateien in diesem Verzeichnis lesen."
 
-#: option.c:317
+#: option.c:414
 msgid "Log to this syslog facility or file. (defaults to DAEMON)"
 msgstr "Für diese Syslog-Anlage oder in Datei loggen (Voreinstellung DAEMON)."
 
-#: option.c:318
+#: option.c:415
 msgid "Do not use leasefile."
 msgstr "Keine Lease-Datei benützen."
 
-#: option.c:319
+#: option.c:416
 #, c-format
 msgid "Maximum number of concurrent DNS queries. (defaults to %s)"
 msgstr "Höchstzahl nebenläufiger DNS-Anfragen (%s voreingestellt)."
 
-#: option.c:320
+#: option.c:417
 #, c-format
 msgid "Clear DNS cache when reloading %s."
 msgstr "DNS-Cache beim Neuladen von %s löschen."
 
-#: option.c:321
+#: option.c:418
 msgid "Ignore hostnames provided by DHCP clients."
 msgstr "Von DHCP-Clients gelieferte Hostnamen ignorieren."
 
-#: option.c:322
+#: option.c:419
 msgid "Do NOT reuse filename and server fields for extra DHCP options."
 msgstr "Dateinamen und Server-Datenfehler für zusätzliche DHCP-Optionen NICHT wiederverwenden."
 
-#: option.c:323
+#: option.c:420
 msgid "Enable integrated read-only TFTP server."
 msgstr "Eingebauten Nur-Lese-TFTP-Server einschalten."
 
-#: option.c:324
+#: option.c:421
 msgid "Export files by TFTP only from the specified subtree."
 msgstr "Nur vom festgelegten Unterbaum Dateien per TFTP exportieren."
 
-#: option.c:325
+#: option.c:422
 msgid "Add client IP address to tftp-root."
 msgstr "IP-Adresse des Klienten an tftp-root anhängen."
 
-#: option.c:326
+#: option.c:423
 msgid "Allow access only to files owned by the user running dnsmasq."
 msgstr "Zugriff nur auf Dateien gestatten, die dem dnsmasq aufrufenden Benutzer gehören."
 
-#: option.c:327
+#: option.c:424
+msgid "Do not terminate the service if TFTP directories are inaccessible."
+msgstr "Der Dienst sollte nicht beendet werden, wenn die TFTP-Verzeichnisse nicht zugreifbar sind."
+
+#: option.c:425
 #, c-format
 msgid "Maximum number of conncurrent TFTP transfers (defaults to %s)."
 msgstr "Höchstzahl nebenläufiger TFTP-Übertragungen (%s voreingestellt)."
 
-#: option.c:328
+#: option.c:426
 msgid "Disable the TFTP blocksize extension."
 msgstr "TFTP-Blockgrößen-Erweiterung abschalten."
 
-#: option.c:329
+#: option.c:427
+msgid "Convert TFTP filenames to lowercase"
+msgstr "Konvertiere TFTP Dateinamen in Kleinschreibung"
+
+#: option.c:428
 msgid "Ephemeral port range for use by TFTP transfers."
 msgstr "Bereich für vorübergehende Ports für TFTP-Übertragungen."
 
-#: option.c:330
+#: option.c:429
 msgid "Extra logging for DHCP."
 msgstr "Erweiterte DHCP-Protokollierung."
 
-#: option.c:331
+#: option.c:430
 msgid "Enable async. logging; optionally set queue length."
 msgstr "Asynchrone Protokollierung einschalten, opt. Warteschlangenlänge festlegen."
 
-#: option.c:332
+#: option.c:431
 msgid "Stop DNS rebinding. Filter private IP ranges when resolving."
 msgstr "DNS-Rebinding unterbinden, private IP-Bereiche bei der Auflösung ausfiltern."
 
-#: option.c:333
+#: option.c:432
 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers."
 msgstr "Auflösung zu 127.0.0.0/8 erlauben, für RBL-Server."
 
-#: option.c:334
-#, fuzzy
+#: option.c:433
 msgid "Inhibit DNS-rebind protection on this domain."
 msgstr "DNS-Rebind-Schutz für diese Domäne sperren."
 
-#: option.c:335
+#: option.c:434
 msgid "Always perform DNS queries to all servers."
 msgstr "DNS-Anfragen immer an alle Server weiterleiten."
 
-#: option.c:336
+#: option.c:435
 msgid "Set tag if client includes matching option in request."
 msgstr "Marke setzen, wenn Klient eine entsprechende Option anfragt."
 
-#: option.c:337
+#: option.c:436
 msgid "Use alternative ports for DHCP."
 msgstr "Alternative Ports für DHCP verwenden."
 
-#: option.c:338
-msgid "Run lease-change script as this user."
-msgstr "Lease-Änderungs-Skript mit den Rechten dieses Nutzers ausführen."
-
-#: option.c:339
+#: option.c:437
 msgid "Specify NAPTR DNS record."
 msgstr "DNS-NAPTR-Eintrag festlegen."
 
-#: option.c:340
+#: option.c:438
 msgid "Specify lowest port available for DNS query transmission."
 msgstr "Niedrigsten verfügbaren Port für Übertragung von DNS-Anfragen festlegen."
 
-#: option.c:341
+#: option.c:439
 msgid "Use only fully qualified domain names for DHCP clients."
 msgstr "Für DHCP-Klienten nur vollständig bestimmte Domänennamen benutzen."
 
 # FIXME: probably typo in original message. -- MA
-#: option.c:342
-#, fuzzy
+#: option.c:440
 msgid "Generate hostnames based on MAC address for nameless clients."
-msgstr "Für namenlose Klienten die Hostnamen MAC-basiert erzeugen."
+msgstr "Generiere Hostnamen auf Basis der MAC-Adresse für namenlose Klienten."
 
-#: option.c:343
+#: option.c:441
 msgid "Use these DHCP relays as full proxies."
 msgstr "Diese DHCP-Relais als vollwertige Proxies verwenden."
 
-#: option.c:344
+#: option.c:442
+msgid "Relay DHCP requests to a remote server"
+msgstr "Leute DHCP Anfragen an entfernten Server weiter"
+
+#: option.c:443
 msgid "Specify alias name for LOCAL DNS name."
 msgstr "Alias für LOKALEN DNS-Namen festlegen."
 
-#: option.c:345
+#: option.c:444
 msgid "Prompt to send to PXE clients."
 msgstr "Aufforderung, die an PXE-Klienten geschickt wird."
 
-#: option.c:346
+#: option.c:445
 msgid "Boot service for PXE menu."
 msgstr "Boot-Dienst für PXE-Menü."
 
-#: option.c:347
+#: option.c:446
 msgid "Check configuration syntax."
 msgstr "Konfigurationssyntax prüfen."
 
-#: option.c:348
-msgid "Add requestor's MAC address to forwarded DNS queries"
-msgstr ""
+#: option.c:447
+msgid "Add requestor's MAC address to forwarded DNS queries."
+msgstr "Anfragende MAC-Adresse in die weiterleitende DNS-Anfrage einfügen"
 
-#: option.c:349
-#, fuzzy
-msgid "Proxy DNSSEC validation results from upstream nameservers"
-msgstr "IPv4-Adressen von vorgelagerten Servern übersetzen."
+#: option.c:448
+msgid "Add requestor's IP subnet to forwarded DNS queries."
+msgstr "Füge das IP-Subnetz des Anfragenden in die weitergeleiteten DNS-Anfragen hinzu."
+
+#: option.c:449
+msgid "Proxy DNSSEC validation results from upstream nameservers."
+msgstr "Proxy-DNSSEC-Validierung-Ergebnisse von Upstream-Namensservern."
+
+#: option.c:450
+msgid "Attempt to allocate sequential IP addresses to DHCP clients."
+msgstr "Versuche sequenzielle IP-Adressen an DHCP-Klienten zu vergeben."
+
+#: option.c:451
+msgid "Copy connection-track mark from queries to upstream connections."
+msgstr "Kopiere \"connection-track mark\" von Anfragen nach Upstream-Verbindungen."
+
+#: option.c:452
+msgid "Allow DHCP clients to do their own DDNS updates."
+msgstr "Erlaube DHCP-Klienten ihre eigenen DDNS-Updates durchzuführen."
+
+#: option.c:453
+msgid "Send router-advertisements for interfaces doing DHCPv6"
+msgstr "Sende \"Router-Advertisments\" für Netzwerkschnittstellen, welche DHCPv6 nutzen"
+
+#: option.c:454
+msgid "Specify DUID_EN-type DHCPv6 server DUID"
+msgstr "Spezifiziere DUID_EN-type DHCPv6 Server DUID"
+
+#: option.c:455
+msgid "Specify host (A/AAAA and PTR) records"
+msgstr "Spezifiziere Host (A/AAAA und PTR) Einträge"
+
+#: option.c:456
+msgid "Specify arbitrary DNS resource record"
+msgstr "Spezifiziere einen beliebiegen DNS Eintrag"
+
+#: option.c:457
+msgid "Bind to interfaces in use - check for new interfaces"
+msgstr "Bindung zu Schnittstellen in Benutzung - prüfe auf neue Schnittstellen"
+
+#: option.c:458
+msgid "Export local names to global DNS"
+msgstr "Exportiere lokale Namen in das globale DNS"
+
+#: option.c:459
+msgid "Domain to export to global DNS"
+msgstr "Domain für das Exportieren des globalen DNS"
+
+#: option.c:460
+msgid "Set TTL for authoritative replies"
+msgstr "Setzte TTL für autoritative Antworten"
+
+#: option.c:461
+msgid "Set authoritive zone information"
+msgstr "Setze autoritative Zoneninformationen"
 
-#: option.c:638
+#: option.c:462
+msgid "Secondary authoritative nameservers for forward domains"
+msgstr "Sekundärer autoritativer Nameserver für weitergeleitete Domains"
+
+#: option.c:463
+msgid "Peers which are allowed to do zone transfer"
+msgstr "Peers welche einen Zonentransfer durchführen dürfen"
+
+#: option.c:464
+msgid "Specify ipsets to which matching domains should be added"
+msgstr "Spezifiziere IPSets zu welcher passende Domains hinzugefügt werden sollen"
+
+#: option.c:465
+msgid "Specify a domain and address range for synthesised names"
+msgstr "Spezifiziere eine Domain und Adressbereich für synthetisierte Namen"
+
+#: option.c:466
+msgid "Activate DNSSEC validation"
+msgstr "Aktiviere DNSSEC-Validierung"
+
+#: option.c:467
+msgid "Specify trust anchor key digest."
+msgstr "Spezifiziere Vertrauensursprung (Trust Anchor) der Schlüssel-Prüfdaten (Key Digest)."
+
+#: option.c:468
+msgid "Disable upstream checking for DNSSEC debugging."
+msgstr "Deaktiviere die Überprüfung vorgelagerter Server für DNSSEC-Debugging"
+
+#: option.c:469
+msgid "Ensure answers without DNSSEC are in unsigned zones."
+msgstr "Stellt sicher, dass Antworten ohne DNSSEC sich in einer unsignierten Zone befinden."
+
+#: option.c:470
+msgid "Don't check DNSSEC signature timestamps until first cache-reload"
+msgstr "DNSSEC Signatur-Zeitstempel nicht prüfen, bis erstmalig der Cache neugeladen wird"
+
+#: option.c:471
+msgid "Timestamp file to verify system clock for DNSSEC"
+msgstr "Zeitstempel-Datei für die Verifizierung der Systemuhrzeit für DNSSEC"
+
+#: option.c:473
+msgid "Specify DHCPv6 prefix class"
+msgstr "Spezifiziere DHCPv6 Prefix Klasse"
+
+#: option.c:475
+msgid "Set priority, resend-interval and router-lifetime"
+msgstr "Setze Priorität, Intervall des erneuten Sendens und Router Lebenszeit"
+
+#: option.c:476
+msgid "Do not log routine DHCP."
+msgstr "Protokolliere kein DHCP."
+
+#: option.c:477
+msgid "Do not log routine DHCPv6."
+msgstr "Protokolliere kein DHCPv6."
+
+#: option.c:478
+msgid "Do not log RA."
+msgstr "RA nicht protokollieren."
+
+#: option.c:479
+msgid "Accept queries only from directly-connected networks"
+msgstr "Akzeptiere nur Anfragen von direkt verbundenen Netzwerken"
+
+#: option.c:480
+msgid "Detect and remove DNS forwarding loops"
+msgstr "Erkennen und Entfernen von DNS-Weiterleitungsschleifen"
+
+#: option.c:481
+msgid "Ignore DNS responses containing ipaddr."
+msgstr "Ignoriere DNS-Antworten, welche ipaddr enthalten."
+
+#: option.c:683
 #, c-format
 msgid ""
 "Usage: dnsmasq [options]\n"
@@ -564,928 +742,1432 @@ msgstr ""
 "Verwendung: dnsmasq [Optionen]\n"
 "\n"
 
-#: option.c:640
+#: option.c:685
 #, c-format
 msgid "Use short options only on the command line.\n"
 msgstr "Auf der Befehlszeile nur kurze Optionen verwenden!\n"
 
-#: option.c:642
+#: option.c:687
 #, c-format
 msgid "Valid options are:\n"
 msgstr "Gültige Optionen sind:\n"
 
-#: option.c:683
-#, c-format
-msgid "Known DHCP options:\n"
-msgstr "Bekannte DHCP-Optionen:\n"
+#: option.c:744 option.c:748
+msgid "bad port"
+msgstr "unzulässiger Port"
+
+#: option.c:775 option.c:807
+msgid "interface binding not supported"
+msgstr "Schnittstellenbindung nicht unterstützt"
+
+#: option.c:784 option.c:3575
+msgid "bad interface name"
+msgstr "unzulässiger Schnittestellenname"
+
+#: option.c:814
+msgid "bad address"
+msgstr "Fehlerhafte Adresse"
+
+#: option.c:996
+msgid "unsupported encapsulation for IPv6 option"
+msgstr "Nicht unterstützte Verkapselung für eine IPv6-Option"
 
-#: option.c:798
+#: option.c:1010
 msgid "bad dhcp-option"
 msgstr "Fehlerhafte DHCP-Option"
 
-#: option.c:860
+#: option.c:1078
 msgid "bad IP address"
 msgstr "Fehlerhafte IP-Adresse"
 
-#: option.c:968
+#: option.c:1081 option.c:1219 option.c:2893
+msgid "bad IPv6 address"
+msgstr "Fehlerhafte IPv6-Adresse"
+
+#: option.c:1246 option.c:1340
 msgid "bad domain in dhcp-option"
 msgstr "Fehlerhafte Domäne in DHCP-Option"
 
-#: option.c:1034
+#: option.c:1378
 msgid "dhcp-option too long"
 msgstr "DHCP-Option zu lang"
 
-#: option.c:1043
+#: option.c:1385
 msgid "illegal dhcp-match"
 msgstr "Unzulässige dhcp-match-Option"
 
-#: option.c:1087
+#: option.c:1447
 msgid "illegal repeated flag"
 msgstr "unzulässig wiederholte Markierung"
 
-#: option.c:1095
+#: option.c:1455
 msgid "illegal repeated keyword"
 msgstr "unzulässig wiederholtes Schlüsselwort"
 
-#: option.c:1147 option.c:3030
+#: option.c:1520 option.c:4191
 #, c-format
 msgid "cannot access directory %s: %s"
 msgstr "Kann auf Verzeichnis %s nicht zugreifen: %s"
 
-#: option.c:1178 tftp.c:460
+#: option.c:1566 tftp.c:493
 #, c-format
 msgid "cannot access %s: %s"
 msgstr "Kann auf %s nicht zugreifen: %s"
 
-#: option.c:1207
+#: option.c:1618
 msgid "setting log facility is not possible under Android"
-msgstr ""
+msgstr "Die Einstellung Protokolliereinrichtung kann unter Android nicht gesetzt werden"
 
-#: option.c:1216
+#: option.c:1627
 msgid "bad log facility"
-msgstr ""
+msgstr "Falsche Protokolliereinrichtung"
 
-#: option.c:1265
+#: option.c:1680
 msgid "bad MX preference"
 msgstr "unzulässige MX-Präferenz-Angabe"
 
-#: option.c:1270
+#: option.c:1685
 msgid "bad MX name"
 msgstr "unzulässiger MX-Name"
 
-#: option.c:1284
+#: option.c:1699
 msgid "bad MX target"
 msgstr "unzulässiges MX-Ziel"
 
-#: option.c:1294
+#: option.c:1711
 msgid "cannot run scripts under uClinux"
 msgstr "unter uClinux ist die Skriptausführung nicht möglich"
 
-#: option.c:1296
+#: option.c:1713
 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts"
 msgstr "Neuübersetzung mit HAVE_SCRIPT nötig, um Lease-Änderungs-Skripte auszuführen"
 
-#: option.c:1597 option.c:1601
-msgid "bad port"
-msgstr "unzulässiger Port"
+#: option.c:1717
+msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts"
+msgstr "Um Benutzerdefinierte Lua-Scripte zu ermöglichen, muss mit HAVE_LUASCRIPT neu kompiliert werden"
 
-#: option.c:1620 option.c:1645
-msgid "interface binding not supported"
-msgstr "Schnittstellenbindung nicht unterstützt"
+#: option.c:1973 option.c:2018 option.c:2074
+msgid "bad prefix"
+msgstr "unzulässiger Präfix"
+
+#: option.c:2355
+msgid "recompile with HAVE_IPSET defined to enable ipset directives"
+msgstr "Um IPSet-Direktiven zu aktivieren, muss mit HAVE_IPSET neu übersetzt werden"
 
-#: option.c:1791
+#: option.c:2548
 msgid "bad port range"
 msgstr "unzulässiger Portbereich"
 
-#: option.c:1808
+#: option.c:2564
 msgid "bad bridge-interface"
 msgstr "unzulässige Brücken-Schnittstelle"
 
-#: option.c:1850
-msgid "bad dhcp-range"
-msgstr "unzulässiger DHCP-Bereich"
-
-#: option.c:1878
+#: option.c:2624
 msgid "only one tag allowed"
 msgstr "nur eine Marke zulässig"
 
-#: option.c:1925
+#: option.c:2644 option.c:2656 option.c:2764 option.c:2805
+msgid "bad dhcp-range"
+msgstr "unzulässiger DHCP-Bereich"
+
+#: option.c:2671
 msgid "inconsistent DHCP range"
 msgstr "inkonsistenter DHCP-Bereich"
 
-#: option.c:2019 option.c:2045
+#: option.c:2732
+msgid "prefix length must be exactly 64 for RA subnets"
+msgstr "Die Präfixlenge muss genau 64 für RA Subnetze sein"
+
+#: option.c:2734
+msgid "prefix length must be exactly 64 for subnet constructors"
+msgstr "Die Präfixlenge muss genau 64 für Subnet Konstruktoren sein"
+
+#: option.c:2738
+msgid "prefix length must be at least 64"
+msgstr "Die Präfixlänge muss mindestens 64 sein"
+
+#: option.c:2741
+msgid "inconsistent DHCPv6 range"
+msgstr "Inkonsistenter DHCPv6-Bereich"
+
+#: option.c:2752
+msgid "prefix must be zero with \"constructor:\" argument"
+msgstr "Prefix muss mit dem \"constructor:\" Argument Null sein"
+
+#: option.c:2863 option.c:2911
 msgid "bad hex constant"
-msgstr ""
+msgstr "Falscher Hexwert"
 
-#: option.c:2107
+#: option.c:2885
+msgid "cannot match tags in --dhcp-host"
+msgstr "Kann die Tags in --dhcp-host nicht abgleichen"
+
+#: option.c:2933
+#, c-format
+msgid "duplicate dhcp-host IP address %s"
+msgstr "doppelte dhcp-host IP-Adresse %s"
+
+#: option.c:2991
 msgid "bad DHCP host name"
 msgstr "unzulässiger DHCP-Hostname"
 
-#: option.c:2188
+#: option.c:3073
 msgid "bad tag-if"
 msgstr "unzulässige bedingte Marke (tag-if)"
 
-#: option.c:2467 option.c:2752
+#: option.c:3397 option.c:3791
 msgid "invalid port number"
 msgstr "unzulässige Portnummer"
 
-#: option.c:2529
+#: option.c:3459
 msgid "bad dhcp-proxy address"
 msgstr "Fehlerhafte DHCP-Proxy-Adresse"
 
-#: option.c:2569
+#: option.c:3485
+msgid "Bad dhcp-relay"
+msgstr "unzulässiger dhcp-relay"
+
+#: option.c:3511
+msgid "bad RA-params"
+msgstr "unzulässige RA-Parameter"
+
+#: option.c:3520
+msgid "bad DUID"
+msgstr "unzulässige DUID"
+
+#: option.c:3562
 msgid "invalid alias range"
 msgstr "unzulässiger Alias-Bereich"
 
-#: option.c:2582
-msgid "bad interface name"
-msgstr "unzulässiger Schnittestellenname"
-
-#: option.c:2607
+#: option.c:3616
 msgid "bad CNAME"
 msgstr "unzulässiger CNAME"
 
-#: option.c:2612
+#: option.c:3621
 msgid "duplicate CNAME"
 msgstr "doppelter CNAME"
 
-#: option.c:2632
+#: option.c:3641
 msgid "bad PTR record"
 msgstr "unzulässiger PTR-Eintrag"
 
-#: option.c:2663
+#: option.c:3672
 msgid "bad NAPTR record"
 msgstr "unzulässiger NAPTR-Eintrag"
 
-#: option.c:2695
+#: option.c:3706
+msgid "bad RR record"
+msgstr "unzulässiger RR-Eintrag"
+
+#: option.c:3736
 msgid "bad TXT record"
 msgstr "unzulässiger TXT-Eintrag"
 
-#: option.c:2738
+#: option.c:3777
 msgid "bad SRV record"
 msgstr "unzulässiger SRV-Eintrag"
 
-#: option.c:2745
+#: option.c:3784
 msgid "bad SRV target"
 msgstr "unzulässiges SRV-Ziel"
 
-#: option.c:2759
+#: option.c:3798
 msgid "invalid priority"
 msgstr "unzulässige Priorität"
 
-#: option.c:2766
+#: option.c:3805
 msgid "invalid weight"
 msgstr "unzulässige Wichtung"
 
-#: option.c:2785
-msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"
-msgstr "unzulässige Option (prüfen Sie, ob dnsmasq mit DHCP/TFTP/DBus-Unterstützt übersetzt wurde)"
+#: option.c:3829
+msgid "Bad host-record"
+msgstr "unzulässiger host-record"
 
-#: option.c:2849
+#: option.c:3846
+msgid "Bad name in host-record"
+msgstr "Unzulässiger Name in host-record"
+
+#: option.c:3911
+msgid "bad trust anchor"
+msgstr "unzulässiger Vertrauensursprung (Trust Anchor)"
+
+#: option.c:3925
+msgid "bad HEX in trust anchor"
+msgstr "unzulässiger Hexwert in Vertrauensursprung (Trust Anchor)"
+
+#: option.c:3935
+msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"
+msgstr "Nicht unterstützte Option (prüfen Sie, ob DNSMasq mit DHCP/TFTP/DNSSEC/DBus-Unterstützung übersetzt wurde)"
+
+#: option.c:3994
 msgid "missing \""
 msgstr "fehlende \\\""
 
-#: option.c:2908
+#: option.c:4051
 msgid "bad option"
 msgstr "unzulässige Option"
 
-#: option.c:2910
+#: option.c:4053
 msgid "extraneous parameter"
 msgstr "überschüssiger Parameter"
 
-#: option.c:2912
+#: option.c:4055
 msgid "missing parameter"
 msgstr "fehler Parameter"
 
-#: option.c:2916
+#: option.c:4057
+msgid "illegal option"
+msgstr "unzulässige Option"
+
+#: option.c:4064
 msgid "error"
 msgstr "Fehler"
 
-#: option.c:2921
+#: option.c:4066
 #, c-format
-msgid "%s at line %d of %%s"
-msgstr "%s in Zeile %d von %%s"
+msgid " at line %d of %s"
+msgstr " in Zeile %d von %s"
 
-#: option.c:2985 tftp.c:624
-#, c-format
-msgid "cannot read %s: %s"
-msgstr "kann %s nicht lesen: %s"
-
-#: option.c:3151 option.c:3187
+#: option.c:4081 option.c:4328 option.c:4364
 #, c-format
 msgid "read %s"
 msgstr "%s gelesen"
 
-#: option.c:3239
+#: option.c:4144 option.c:4267 tftp.c:667
+#, c-format
+msgid "cannot read %s: %s"
+msgstr "kann %s nicht lesen: %s"
+
+#: option.c:4430
 msgid "junk found in command line"
-msgstr ""
+msgstr "Mist in der Kommandozeile gefunden"
 
-#: option.c:3269
+#: option.c:4465
 #, c-format
 msgid "Dnsmasq version %s  %s\n"
 msgstr "Dnsmasq Version %s  %s\n"
 
-#: option.c:3270
+#: option.c:4466
 #, c-format
 msgid ""
-"Compile time options %s\n"
+"Compile time options: %s\n"
 "\n"
 msgstr ""
-"Übersetzungs-Optionen %s\n"
+"Kompilierungs-Optionen %s\n"
 "\n"
 
-#: option.c:3271
+#: option.c:4467
 #, c-format
 msgid "This software comes with ABSOLUTELY NO WARRANTY.\n"
 msgstr "Für diese Software wird ABSOLUT KEINE GARANTIE gewährt.\n"
 
 # FIXME: this must be one long string! -- MA
-#: option.c:3272
+#: option.c:4468
 #, c-format
 msgid "Dnsmasq is free software, and you are welcome to redistribute it\n"
-msgstr ""
+msgstr "Dnsmasq ist freie Software, und du bist willkommen es weiter zu verteilen\n"
 
-#: option.c:3273
+#: option.c:4469
 #, c-format
 msgid "under the terms of the GNU General Public License, version 2 or 3.\n"
-msgstr ""
+msgstr "unter den Bedingungen der GNU General Public Lizenz, Version 2 oder 3.\n"
 
-#: option.c:3284
+#: option.c:4480
 msgid "try --help"
 msgstr "versuchen Sie --help"
 
-#: option.c:3286
+#: option.c:4482
 msgid "try -w"
 msgstr "versuchen Sie -w"
 
-#: option.c:3289
+#: option.c:4484
 #, c-format
 msgid "bad command line options: %s"
 msgstr "unzulässige Optionen auf der Befehlszeile: %s"
 
-#: option.c:3330
+#: option.c:4544
 #, c-format
 msgid "cannot get host-name: %s"
 msgstr "kann Hostnamen nicht ermitteln: %s"
 
-#: option.c:3358
+#: option.c:4572
 msgid "only one resolv.conf file allowed in no-poll mode."
 msgstr "mit -n/--no-poll ist nur eine resolv.conf-Datei zulässig."
 
-#: option.c:3368
+#: option.c:4582
 msgid "must have exactly one resolv.conf to read domain from."
 msgstr "Um die Domäne zu lesen, muss genau eine resolv.conf-Datei verwendet werden."
 
-#: option.c:3371 network.c:848 dhcp.c:814
+#: option.c:4585 network.c:1507 dhcp.c:777
 #, c-format
 msgid "failed to read %s: %s"
 msgstr "konnte %s nicht lesen: %s"
 
-#: option.c:3388
+#: option.c:4602
 #, c-format
 msgid "no search directive found in %s"
 msgstr "keine \"search\"-Anweisung in %s gefunden"
 
-#: option.c:3409
-#, fuzzy
+#: option.c:4623
 msgid "there must be a default domain when --dhcp-fqdn is set"
-msgstr "für --dhcp-fqdn muss eine Domäne vorausgewählt werden"
+msgstr "Es muss eine standard Domain gesetzt sein, wenn --dhcp-fqdn gesetzt ist"
 
-#: option.c:3413
+#: option.c:4632
 msgid "syntax check OK"
 msgstr "Syntaxprüfung OK"
 
-#: forward.c:461
+#: forward.c:111
+#, c-format
+msgid "failed to send packet: %s"
+msgstr "Fehlgeschlagen, folgendes Paket zu senden: %s"
+
+#: forward.c:591
+msgid "discarding DNS reply: subnet option mismatch"
+msgstr "Verwerfe DNS Antwort: Subnetoption stimmt nicht überrein"
+
+#: forward.c:614
 #, c-format
 msgid "nameserver %s refused to do a recursive query"
 msgstr "Namensserver %s hat eine rekursive Anfrage verweigert"
 
-#: forward.c:489
+#: forward.c:646
 #, c-format
 msgid "possible DNS-rebind attack detected: %s"
 msgstr "möglichen DNS-Rebind-Angriff entdeckt: %s"
 
-#: network.c:171
+#: forward.c:1209 forward.c:1785
+msgid "Ignoring query from non-local network"
+msgstr "Ignoriere Anfragen vom nicht lokalen Netzwerk"
+
+#: forward.c:2256
 #, c-format
-msgid "unknown interface %s in bridge-interface"
-msgstr "unbekannte Schnittstelle %s in bridge-interface"
+msgid "Maximum number of concurrent DNS queries reached (max: %d)"
+msgstr "Maximale Anzahl an nebenläufiger DNS-Anfragen erreicht (Max: %d)"
 
-#: network.c:380
-#, fuzzy, c-format
+#: network.c:715
+#, c-format
 msgid "failed to create listening socket for %s: %s"
-msgstr "Konnte Empfangs-Socket nicht erzeugen: %s"
+msgstr "Konnte Empfangs-Socket für %s: %s nicht erzeugen"
+
+#: network.c:1021
+#, c-format
+msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s"
+msgstr "LOUD WARNING: Das Abhören von %s kann die Anfragen auf der Schnittstelle akzeptieren anders als %s"
+
+#: network.c:1028
+msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)"
+msgstr "LOUD WARNING: Es sollte --bind-dynamic anstatt --bind-interfaces benutzt werden, um DNS-Verstärkungsangriffe auf diesen Schnittstellen zu unterbinden"
+
+#: network.c:1037
+#, c-format
+msgid "warning: no addresses found for interface %s"
+msgstr "Warnung: Keine Adresse für die Schnittstelle %s gefunden"
 
-#: network.c:746
+#: network.c:1095
+#, c-format
+msgid "interface %s failed to join DHCPv6 multicast group: %s"
+msgstr "Schnittstelle %s konnte DHCPv6-Multicast-Gruppe nicht beitreten: %s"
+
+#: network.c:1289
 #, c-format
 msgid "failed to bind server socket for %s: %s"
 msgstr "konnte nicht an Server-Socket für %s binden: %s"
 
-#: network.c:783
+#: network.c:1445
 #, c-format
 msgid "ignoring nameserver %s - local interface"
 msgstr "ignoriere Namensserver %s - lokale Schnittstelle"
 
-#: network.c:794
+#: network.c:1456
 #, c-format
 msgid "ignoring nameserver %s - cannot make/bind socket: %s"
 msgstr "ignoriere Namensserver %s - kann Socket nicht erzeugen/binden: %s"
 
 # FIXME: this isn't translatable - always provide full strings, do not assemble yourself! -- MA
-#: network.c:811
+#: network.c:1469
 msgid "unqualified"
-msgstr ""
+msgstr "unqualifiziert"
 
-#: network.c:811
+#: network.c:1469
 msgid "names"
-msgstr ""
+msgstr "Namen"
 
-#: network.c:813
+#: network.c:1471
 msgid "default"
-msgstr ""
+msgstr "Standard"
 
-#: network.c:815
+#: network.c:1473
 msgid "domain"
-msgstr ""
+msgstr "Domain"
 
-#: network.c:818
+#: network.c:1476
 #, c-format
 msgid "using local addresses only for %s %s"
-msgstr ""
+msgstr "Benutze lokale Adressen nur für %s %s"
 
-#: network.c:820
+#: network.c:1478
 #, c-format
 msgid "using standard nameservers for %s %s"
-msgstr ""
+msgstr "Benutze standard Namensserver für %s %s"
 
-#: network.c:822
+#: network.c:1480
 #, c-format
 msgid "using nameserver %s#%d for %s %s"
-msgstr ""
+msgstr "Benutze Namensserver %s#%d für %s %s"
 
-#: network.c:825
+#: network.c:1484
+#, c-format
+msgid "NOT using nameserver %s#%d - query loop detected"
+msgstr "Benutze Namensserver %s#%d NICHT - Anfragenschleife festgetellt"
+
+#: network.c:1487
 #, c-format
 msgid "using nameserver %s#%d(via %s)"
-msgstr ""
+msgstr "Benutze Namensserver %s#%d(via %s)"
 
-#: network.c:827
+#: network.c:1489
 #, c-format
 msgid "using nameserver %s#%d"
-msgstr ""
+msgstr "Benutze Namensserver %s#%d"
+
+#: dnsmasq.c:163
+msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"
+msgstr "dhcp-hostsdir, dhcp-optsdir und hostsdir sind auf dieser Plattform nicht unterstüzt"
+
+#: dnsmasq.c:170
+msgid "no trust anchors provided for DNSSEC"
+msgstr "Keine Vertrauensursprünge (Trust Anchor) für DNSSEC verfügbar"
+
+#: dnsmasq.c:173
+msgid "cannot reduce cache size from default when DNSSEC enabled"
+msgstr "Kann die Standard Cachegröße nicht verkleinern, wenn DNSSEC aktiviert ist"
 
-#: dnsmasq.c:148
+#: dnsmasq.c:175
+msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h"
+msgstr "DNSSEC nicht verfügbar: setzen Sie HAVE_DNSSEC in src/config.h"
+
+#: dnsmasq.c:181
 msgid "TFTP server not available: set HAVE_TFTP in src/config.h"
 msgstr "TFTP-Server nicht verfügbar, setzen Sie HAVE_TFTP in src/config.h"
 
-#: dnsmasq.c:153
+#: dnsmasq.c:186
+msgid "cannot use --conntrack AND --query-port"
+msgstr "Kann nicht --conntrack UND --query-port einsetzen"
+
+#: dnsmasq.c:189
+msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h"
+msgstr "Conntrack-Unterstützung nicht verfügbar: Aktiviere HAVE_CONNTRACK in src/config.h"
+
+#: dnsmasq.c:194
 msgid "asychronous logging is not available under Solaris"
 msgstr "asynchrone Protokollierung unter Solaris nicht verfügbar"
 
-#: dnsmasq.c:158
-#, fuzzy
+#: dnsmasq.c:199
 msgid "asychronous logging is not available under Android"
-msgstr "asynchrone Protokollierung unter Solaris nicht verfügbar"
+msgstr "Asynchrone Protokollierung unter Android nicht verfügbar"
+
+#: dnsmasq.c:204
+msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h"
+msgstr "Authoritatives DNS nicht verfügbar: Es muss HAVE_AUTH in src/config.h gesetzt sein"
 
-#: dnsmasq.c:177
+#: dnsmasq.c:209
+msgid "loop detection not available: set HAVE_LOOP in src/config.h"
+msgstr "Loop-Erkennung nicht verfügbar, Aktiviere HAVE_LOOP in src/config.h"
+
+#: dnsmasq.c:217
+msgid "zone serial must be configured in --auth-soa"
+msgstr "Zonen Seriennummer muss mit --auth-soa konfiguriert werden"
+
+#: dnsmasq.c:235
+msgid "dhcp-range constructor not available on this platform"
+msgstr "dhcp-range Konstruktor ist auf dieser Plattform nicht verfübar"
+
+#: dnsmasq.c:278
+msgid "cannot set --bind-interfaces and --bind-dynamic"
+msgstr "Kann nicht --bind-interfaces und --bind-dynamic setzen"
+
+#: dnsmasq.c:281
 #, c-format
 msgid "failed to find list of interfaces: %s"
 msgstr "konnte Schnitstellenliste nicht beziehen: %s"
 
-#: dnsmasq.c:185
+#: dnsmasq.c:290
 #, c-format
 msgid "unknown interface %s"
 msgstr "unbekannte Schnittstelle %s"
 
-#: dnsmasq.c:191
-#, c-format
-msgid "no interface with address %s"
-msgstr "keine Schnittstelle mit Adresse %s"
-
-#: dnsmasq.c:207 dnsmasq.c:678
+#: dnsmasq.c:354 dnsmasq.c:1037
 #, c-format
 msgid "DBus error: %s"
 msgstr "DBus-Fehler: %s"
 
-#: dnsmasq.c:210
+#: dnsmasq.c:357
 msgid "DBus not available: set HAVE_DBUS in src/config.h"
 msgstr "DBus nicht verfügbar: setzen Sie HAVE_DBUS in src/config.h"
 
-#: dnsmasq.c:236
+#: dnsmasq.c:385
 #, c-format
 msgid "unknown user or group: %s"
 msgstr "Unbekannter Benutzer oder Gruppe: %s"
 
-#: dnsmasq.c:291
+#: dnsmasq.c:440
 #, c-format
 msgid "cannot chdir to filesystem root: %s"
 msgstr "kann nicht ins Wurzelverzeichnis des Dateisystems wechseln: %s"
 
 # FIXME: this and the next would need commas after the version
-#: dnsmasq.c:455
+#: dnsmasq.c:692
 #, c-format
 msgid "started, version %s DNS disabled"
 msgstr "gestartet, Version %s, DNS abgeschaltet"
 
-#: dnsmasq.c:457
+#: dnsmasq.c:694
 #, c-format
 msgid "started, version %s cachesize %d"
 msgstr "gestartet, Version %s, Cachegröße %d"
 
-#: dnsmasq.c:459
+#: dnsmasq.c:696
 #, c-format
 msgid "started, version %s cache disabled"
-msgstr ""
+msgstr "Gestartet, Version %s Cache deaktiviert"
 
-#: dnsmasq.c:461
+#: dnsmasq.c:698
 #, c-format
 msgid "compile time options: %s"
 msgstr "Übersetzungsoptionen: %s"
 
-#: dnsmasq.c:467
+#: dnsmasq.c:704
 msgid "DBus support enabled: connected to system bus"
 msgstr "DBus-Unterstützung eingeschaltet: mit Systembus verbunden"
 
-#: dnsmasq.c:469
+#: dnsmasq.c:706
 msgid "DBus support enabled: bus connection pending"
 msgstr "DBus-Unterstützung eingeschaltet: warte auf Systembus-Verbindung"
 
-#: dnsmasq.c:474
+#: dnsmasq.c:711
+msgid "DNS service limited to local subnets"
+msgstr "DNS-Dienst auf lokale Subnetze eingeschränkt"
+
+#: dnsmasq.c:727
+msgid "DNSSEC validation enabled"
+msgstr "DNSSEC-Validierung aktiviert"
+
+#: dnsmasq.c:730
+msgid "DNSSEC signature timestamps not checked until first cache reload"
+msgstr "DNSSEC Signatur-Zeitstempel werden erst ab dem ersten Neuladen des Caches überprüft"
+
+#: dnsmasq.c:733
+msgid "DNSSEC signature timestamps not checked until system time valid"
+msgstr "DNSSEC Signatur-Zeitstempel werden erst überprüft, sobald die Systemuhrzeit gültig ist"
+
+#: dnsmasq.c:738
 #, c-format
 msgid "warning: failed to change owner of %s: %s"
 msgstr "Warnung: konnte den Besitzer von %s nicht ändern: %s"
 
-#: dnsmasq.c:478
+#: dnsmasq.c:742
 msgid "setting --bind-interfaces option because of OS limitations"
 msgstr "Aktiviere --bind-interfaces wegen Einschränkungen des Betriebssystems"
 
-#: dnsmasq.c:483
+#: dnsmasq.c:752
 #, c-format
 msgid "warning: interface %s does not currently exist"
 msgstr "Warnung: Schnittstelle %s existiert derzeit nicht"
 
-#: dnsmasq.c:488
+#: dnsmasq.c:757
 msgid "warning: ignoring resolv-file flag because no-resolv is set"
 msgstr "Warnung: Ignoriere \"resolv-file\", weil \"no-resolv\" aktiv ist"
 
-#: dnsmasq.c:491
+#: dnsmasq.c:760
 msgid "warning: no upstream servers configured"
 msgstr "Warnung: keine vorgelagerten (Upstream) Server konfiguriert"
 
-#: dnsmasq.c:495
+#: dnsmasq.c:764
 #, c-format
 msgid "asynchronous logging enabled, queue limit is %d messages"
 msgstr "asynchrone Protokollierung eingeschaltet, Warteschlange fasst %d Nachrichten"
 
-#: dnsmasq.c:508
-#, c-format
-msgid "DHCP, static leases only on %.0s%s, lease time %s"
-msgstr "DHCP, nur statische Leases auf %.0s%s, Lease-Zeit %s"
-
-#: dnsmasq.c:510
-#, c-format
-msgid "DHCP, proxy on subnet %.0s%s%.0s"
-msgstr "DHCP, Proxy im Subnetz %.0s%s%.0s"
+#: dnsmasq.c:785
+msgid "IPv6 router advertisement enabled"
+msgstr "IPv6-Router-Advertisement aktiviert"
 
-#: dnsmasq.c:511
+#: dnsmasq.c:790
 #, c-format
-msgid "DHCP, IP range %s -- %s, lease time %s"
-msgstr "DHCP, IP-Bereich %s - %s, Lease-Zeit %s "
+msgid "DHCP, sockets bound exclusively to interface %s"
+msgstr "DHCP, Sockets exklusiv an das Interface %s gebunden"
 
-#: dnsmasq.c:526
+# FIXME: this and the next few must be full strings to be translatable - do not assemble in code"
+#: dnsmasq.c:808
 msgid "root is "
-msgstr "FIXME: this and the next few must be full strings to be translatable - do not assemble in code"
+msgstr "Wurzel ist "
 
-#: dnsmasq.c:526
+#: dnsmasq.c:808
 msgid "enabled"
-msgstr ""
+msgstr "Aktiviert"
 
-#: dnsmasq.c:528
+#: dnsmasq.c:810
 msgid "secure mode"
-msgstr ""
+msgstr "sicherer Modus"
+
+#: dnsmasq.c:813
+#, c-format
+msgid "warning: %s inaccessible"
+msgstr "Warnung: %s nicht zugreifbar"
 
-#: dnsmasq.c:554
+#: dnsmasq.c:817
+#, c-format
+msgid "warning: TFTP directory %s inaccessible"
+msgstr "Warnung: Das TFTP-Verzeichnis %s ist nicht zugreifbar"
+
+#: dnsmasq.c:843
 #, c-format
 msgid "restricting maximum simultaneous TFTP transfers to %d"
 msgstr "Begrenze gleichzeitige TFTP-Übertragungen auf maximal %d"
 
-#: dnsmasq.c:680
+#: dnsmasq.c:1039
 msgid "connected to system DBus"
 msgstr "Mit System-DBus verbunden"
 
-#: dnsmasq.c:775
+#: dnsmasq.c:1189
 #, c-format
 msgid "cannot fork into background: %s"
 msgstr "kann nicht in den Hintergrund abspalten: %s"
 
-#: dnsmasq.c:778
+#: dnsmasq.c:1192
 #, c-format
 msgid "failed to create helper: %s"
 msgstr "kann Helfer nicht erzeugen: %s"
 
-#: dnsmasq.c:781
+#: dnsmasq.c:1195
 #, c-format
 msgid "setting capabilities failed: %s"
 msgstr "kann \"capabilities\" nicht setzen: %s"
 
-#: dnsmasq.c:785
+#: dnsmasq.c:1198
 #, c-format
 msgid "failed to change user-id to %s: %s"
 msgstr "Kann nicht Benutzerrechte %s annehmen: %s"
 
-#: dnsmasq.c:790
+#: dnsmasq.c:1201
 #, c-format
 msgid "failed to change group-id to %s: %s"
 msgstr "Kann nicht Gruppenrechte %s annehmen: %s"
 
-#: dnsmasq.c:793
+#: dnsmasq.c:1204
 #, c-format
 msgid "failed to open pidfile %s: %s"
 msgstr "kann die Prozessidentifikations-(PID)-Datei %s nicht öffnen: %s"
 
-#: dnsmasq.c:796
+#: dnsmasq.c:1207
+#, c-format
+msgid "cannot open log %s: %s"
+msgstr "Kann Logdatei %s nicht öffnen: %s"
+
+#: dnsmasq.c:1210
 #, c-format
-msgid "cannot open %s: %s"
-msgstr "kann %s nicht öffnen: %s"
+msgid "failed to load Lua script: %s"
+msgstr "Konnte Lua-Script nicht laden: %s"
 
-#: dnsmasq.c:851
+#: dnsmasq.c:1213
 #, c-format
-msgid "child process killed by signal %d"
-msgstr "Tochterprozess durch Signal %d zerstört"
+msgid "TFTP directory %s inaccessible: %s"
+msgstr "Das TFTP-Verzeichnis %s ist nicht zugreifbar: %s"
 
-#: dnsmasq.c:855
+#: dnsmasq.c:1216
 #, c-format
-msgid "child process exited with status %d"
-msgstr "Tochterprozess beendete sich mit Status %d"
+msgid "cannot create timestamp file %s: %s"
+msgstr "Kann keine timestamp-Datei %s erzeugen: %s "
 
-#: dnsmasq.c:859
+#: dnsmasq.c:1237
+msgid "now checking DNSSEC signature timestamps"
+msgstr "Prüfe jetzt DNSSEC Signatur-Zeitstempel"
+
+#: dnsmasq.c:1304
+#, c-format
+msgid "script process killed by signal %d"
+msgstr "Scriptprozess durch Signal %d getötet"
+
+#: dnsmasq.c:1308
+#, c-format
+msgid "script process exited with status %d"
+msgstr "Scriptprozess hat sich mit Status %d beendet"
+
+#: dnsmasq.c:1312
 #, c-format
 msgid "failed to execute %s: %s"
 msgstr "konnte %s nicht ausführen: %s"
 
-#: dnsmasq.c:903
+#: dnsmasq.c:1367
 msgid "exiting on receipt of SIGTERM"
 msgstr "beende nach Empfang von SIGTERM"
 
-#: dnsmasq.c:931
+#: dnsmasq.c:1395
 #, c-format
 msgid "failed to access %s: %s"
 msgstr "konnte auf %s nicht zugreifen: %s"
 
-#: dnsmasq.c:961
+#: dnsmasq.c:1425
 #, c-format
 msgid "reading %s"
 msgstr "lese %s"
 
-#: dnsmasq.c:972
+#: dnsmasq.c:1436
 #, c-format
 msgid "no servers found in %s, will retry"
 msgstr "keine Server in %s gefunden, werde es später neu versuchen"
 
-#: dhcp.c:40
+#: dhcp.c:53
 #, c-format
 msgid "cannot create DHCP socket: %s"
 msgstr "kann DHCP-Socket nicht erzeugen: %s"
 
-#: dhcp.c:52
+#: dhcp.c:68
 #, c-format
 msgid "failed to set options on DHCP socket: %s"
 msgstr "kann Optionen für DHCP-Socket nicht setzen: %s"
 
-#: dhcp.c:65
+#: dhcp.c:89
 #, c-format
 msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"
 msgstr "kann SO_REUSE{ADDR|PORT} für DHCP-Socket nicht aktivieren: %s"
 
-#: dhcp.c:77
+#: dhcp.c:101
 #, c-format
 msgid "failed to bind DHCP server socket: %s"
 msgstr "kann nicht an DHCP-Server-Socket binden: %s"
 
-#: dhcp.c:103
+#: dhcp.c:127
 #, c-format
 msgid "cannot create ICMP raw socket: %s."
 msgstr "kann ICMP-Rohdaten-Socket nicht erzeugen: %s."
 
+#: dhcp.c:241 dhcp6.c:180
+#, c-format
+msgid "unknown interface %s in bridge-interface"
+msgstr "unbekannte Schnittstelle %s in bridge-interface"
+
 #: dhcp.c:281
 #, c-format
 msgid "DHCP packet received on %s which has no address"
 msgstr "DHCP-Paket ohne Adresse an Schnittstelle %s empfangen"
 
-#: dhcp.c:445
+#: dhcp.c:415
+#, c-format
+msgid "ARP-cache injection failed: %s"
+msgstr "APR-Cache Injektion fehlgeschlagen: %s"
+
+#: dhcp.c:514
 #, c-format
 msgid "DHCP range %s -- %s is not consistent with netmask %s"
 msgstr "DHCP-Bereich %s - %s passt nicht zur Netzmaske %s"
 
-#: dhcp.c:852
+#: dhcp.c:815
 #, c-format
 msgid "bad line at %s line %d"
 msgstr "ungültige Zeile %2$d in Datei %1$s"
 
-#: dhcp.c:895
+#: dhcp.c:858
 #, c-format
 msgid "ignoring %s line %d, duplicate name or IP address"
 msgstr "ignoriere %s Zeile %d, doppelter Name oder doppelte IP-Adresse"
 
-#: dhcp.c:978
-#, c-format
-msgid "duplicate IP address %s in dhcp-config directive."
-msgstr "doppelte IP-Adresse %s in \"dhcp-config\"-Anweisung"
-
-#: dhcp.c:981
+#: dhcp.c:1002 rfc3315.c:2135
 #, c-format
-msgid "duplicate IP address %s in %s."
-msgstr "doppelte IP-Adresse %s in %s."
+msgid "DHCP relay %s -> %s"
+msgstr "DHCP Weiterleitung %s -> %s"
 
-#: dhcp.c:1024
-#, c-format
-msgid "%s has more than one address in hostsfile, using %s for DHCP"
-msgstr "%s hat mehr als eine Adresse in hosts-Datei, benutze %s für DHCP"
-
-#: dhcp.c:1029
-#, c-format
-msgid "duplicate IP address %s (%s) in dhcp-config directive"
-msgstr "doppelte IP-Adresse %s (%s) in \"dhcp-config\"-Anweisung"
-
-#: lease.c:67
+#: lease.c:61
 #, c-format
 msgid "cannot open or create lease file %s: %s"
 msgstr "kann Lease-Datei %s nicht öffnen: %s"
 
-#: lease.c:93
+#: lease.c:134
 msgid "too many stored leases"
 msgstr "zu viele Leases gespeichert"
 
-#: lease.c:129
+#: lease.c:165
 #, c-format
 msgid "cannot run lease-init script %s: %s"
 msgstr "kann Lease-Start-Skript %s nicht ausführen: %s"
 
-#: lease.c:135
+#: lease.c:171
 #, c-format
 msgid "lease-init script returned exit code %s"
 msgstr "Lease-Start-Skript beendete sich mit Code %s"
 
 # FIXME: This should be %u s also in English according to NIST and SI rules. -- MA
-#: lease.c:235
+#: lease.c:342
 #, c-format
 msgid "failed to write %s: %s (retry in %us)"
 msgstr "Konnte %s nicht schreiben: %s (Neuversuch in %u s)"
 
+#: lease.c:906
+#, c-format
+msgid "Ignoring domain %s for DHCP host name %s"
+msgstr "Ignoriere Domäne %s für DHCP-Hostnamen %s"
+
 # FIXME: this and the next few are not translatable. Please provide full
 # strings, do not programmatically assemble them.
-#: rfc2131.c:315
+#: rfc2131.c:344
 #, c-format
 msgid "no address range available for DHCP request %s %s"
-msgstr ""
+msgstr "Kein verfügbarer DHCP-Bereich für Anfrage %s %s"
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "with subnet selector"
-msgstr ""
+msgstr "mit Subnetz-Wähler"
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "via"
-msgstr ""
+msgstr "via"
 
-#: rfc2131.c:331
+#: rfc2131.c:357
 #, c-format
 msgid "%u available DHCP subnet: %s/%s"
 msgstr "%u verfügbare(s) DHCP-Subnetz: %s/%s"
 
-#: rfc2131.c:334
+#: rfc2131.c:360 rfc3315.c:300
 #, c-format
 msgid "%u available DHCP range: %s -- %s"
 msgstr "%u verfügbare(r) DHCP-Bereich: %s - %s"
 
+#: rfc2131.c:471
+#, c-format
+msgid "%u vendor class: %s"
+msgstr "%u \"Vendor class\": %s"
+
+#: rfc2131.c:473
+#, c-format
+msgid "%u user class: %s"
+msgstr "%u Benutzerklasse: %s"
+
 # FIXME: do not programmatically assemble strings - untranslatable
-#: rfc2131.c:363
+#: rfc2131.c:500
 msgid "disabled"
-msgstr ""
+msgstr "deaktiviert"
 
-#: rfc2131.c:404 rfc2131.c:916 rfc2131.c:1288
+#: rfc2131.c:541 rfc2131.c:974 rfc2131.c:1380 rfc3315.c:603 rfc3315.c:856
+#: rfc3315.c:1135
 msgid "ignored"
-msgstr ""
+msgstr "ignoriert"
 
-#: rfc2131.c:419 rfc2131.c:1135
+#: rfc2131.c:556 rfc2131.c:1207 rfc3315.c:906
 msgid "address in use"
-msgstr ""
+msgstr "Adresse in Nutzung"
 
-#: rfc2131.c:433 rfc2131.c:970
+#: rfc2131.c:570 rfc2131.c:1028
 msgid "no address available"
-msgstr ""
+msgstr "Keine Adresse verfügbar"
 
-#: rfc2131.c:440 rfc2131.c:1098
+#: rfc2131.c:577 rfc2131.c:1170
 msgid "wrong network"
-msgstr ""
+msgstr "Falsches Netzwerk"
 
-#: rfc2131.c:454
+#: rfc2131.c:592
 msgid "no address configured"
-msgstr ""
+msgstr "Keine Adresse konfiguriert"
 
-#: rfc2131.c:460 rfc2131.c:1148
+#: rfc2131.c:598 rfc2131.c:1220
 msgid "no leases left"
-msgstr ""
+msgstr "Keine Leases übrig"
 
-#: rfc2131.c:545
+#: rfc2131.c:693 rfc3315.c:476
 #, c-format
 msgid "%u client provides name: %s"
 msgstr "%u Klient stellt Name bereit: %s"
 
-#: rfc2131.c:700
-#, c-format
-msgid "%u vendor class: %s"
-msgstr "%u \"Vendor class\": %s"
-
-#: rfc2131.c:702
-#, c-format
-msgid "%u user class: %s"
-msgstr "%u Benutzerklasse: %s"
-
-#: rfc2131.c:761
+#: rfc2131.c:798
 msgid "PXE BIS not supported"
 msgstr "PXE BIS nicht unterstützt"
 
-#: rfc2131.c:886
+#: rfc2131.c:942 rfc3315.c:1229
 #, c-format
 msgid "disabling DHCP static address %s for %s"
 msgstr "schalte statische DHCP-Adresse %s für %s ab"
 
 # FIXME: do not assemble
-#: rfc2131.c:907
+#: rfc2131.c:963
 msgid "unknown lease"
-msgstr ""
+msgstr "Unbekannter Lease"
 
-#: rfc2131.c:939
+#: rfc2131.c:997
 #, c-format
 msgid "not using configured address %s because it is leased to %s"
 msgstr "benutze konfigurierte Adresse %s nicht, weil sie an %s verleast ist"
 
-#: rfc2131.c:949
+#: rfc2131.c:1007
 #, c-format
 msgid "not using configured address %s because it is in use by the server or relay"
 msgstr "benutze konfigurierte Adresse %s nicht, weil sie von Server/Relais verwendet wird"
 
-#: rfc2131.c:952
+#: rfc2131.c:1010
 #, c-format
 msgid "not using configured address %s because it was previously declined"
 msgstr "benutze konfigurierte Adresse %s nicht, weil sie zuvor abgelehnt wurde"
 
 # FIXME: do not assemble
-#: rfc2131.c:968 rfc2131.c:1141
+#: rfc2131.c:1026 rfc2131.c:1213
 msgid "no unique-id"
-msgstr ""
+msgstr "Keine eindeutige ID"
 
-#: rfc2131.c:1037
+#: rfc2131.c:1108
 msgid "wrong server-ID"
-msgstr ""
+msgstr "Falsche Server-ID"
 
-#: rfc2131.c:1055
+#: rfc2131.c:1127
 msgid "wrong address"
-msgstr ""
+msgstr "Falsche Adresse"
 
-#: rfc2131.c:1073
+#: rfc2131.c:1145 rfc3315.c:1002
 msgid "lease not found"
-msgstr ""
+msgstr "Lease nicht gefunden"
 
-#: rfc2131.c:1106
+#: rfc2131.c:1178
 msgid "address not available"
-msgstr ""
+msgstr "Adresse nicht verfügbar"
 
-#: rfc2131.c:1117
+#: rfc2131.c:1189
 msgid "static lease available"
-msgstr ""
+msgstr "Statischer Lease verfügbar"
 
-#: rfc2131.c:1121
+#: rfc2131.c:1193
 msgid "address reserved"
-msgstr ""
+msgstr "Adresse reserviert"
 
-#: rfc2131.c:1129
+#: rfc2131.c:1201
 #, c-format
 msgid "abandoning lease to %s of %s"
 msgstr "Gebe Lease von %2$s an %1$s auf"
 
-#: rfc2131.c:1710
-#, c-format
-msgid "%u tags: %s"
-msgstr "%u Marken: %s"
-
-#: rfc2131.c:1723
+#: rfc2131.c:1707
 #, c-format
 msgid "%u bootfile name: %s"
 msgstr "%u Name der Bootdatei: %s"
 
-#: rfc2131.c:1732
+#: rfc2131.c:1716
 #, c-format
 msgid "%u server name: %s"
 msgstr "%u Servername: %s"
 
-#: rfc2131.c:1746
+#: rfc2131.c:1724
 #, c-format
 msgid "%u next server: %s"
 msgstr "%u nächster Server: %s"
 
-#: rfc2131.c:1749
+#: rfc2131.c:1727
 #, c-format
 msgid "%u broadcast response"
 msgstr "%u Antwort per Rundsendung"
 
-#: rfc2131.c:1812
+#: rfc2131.c:1790
 #, c-format
 msgid "cannot send DHCP/BOOTP option %d: no space left in packet"
 msgstr "kann DHCP/BOOTP-Opition %d nicht setzen: kein Platz mehr im Paket"
 
-#: rfc2131.c:2058
+#: rfc2131.c:2031
 msgid "PXE menu too large"
 msgstr "PXE-Menüeintrag zu groß"
 
-#: rfc2131.c:2171
-#, c-format
-msgid "Ignoring domain %s for DHCP host name %s"
-msgstr "Ignoriere Domäne %s für DHCP-Hostnamen %s"
-
-#: rfc2131.c:2189
+#: rfc2131.c:2170 rfc3315.c:1502
 #, c-format
 msgid "%u requested options: %s"
 msgstr "%u angeforderte Optionen: %s"
 
-#: rfc2131.c:2456
+#: rfc2131.c:2487
 #, c-format
 msgid "cannot send RFC3925 option: too many options for enterprise number %d"
 msgstr "Kann RFC3925-Option nicht senden: zu viele Optionen für Unternehmen Nr. %d"
 
-#: netlink.c:70
+#: netlink.c:77
 #, c-format
 msgid "cannot create netlink socket: %s"
 msgstr "kann Netlink-Socket nicht erzeugen: %s"
 
-#: netlink.c:288
+#: netlink.c:348
 #, c-format
 msgid "netlink returns error: %s"
 msgstr "Netlink liefert Fehler %s"
 
-#: dbus.c:150
+#: dbus.c:186
 msgid "attempt to set an IPv6 server address via DBus - no IPv6 support"
 msgstr "Versuch, via DBus eine IPv6-Serveradresse zu setzen: keine IPv6-Unterstützung"
 
-#: dbus.c:286
+#: dbus.c:439
+#, c-format
+msgid "Enabling --%s option from D-Bus"
+msgstr "Aktiviere --%s Option von D-Bus"
+
+#: dbus.c:444
+#, c-format
+msgid "Disabling --%s option from D-Bus"
+msgstr "Deaktiviere --%s Option von D-Bus"
+
+#: dbus.c:691
 msgid "setting upstream servers from DBus"
 msgstr "vorgelagerte Server von DBus gesetzt"
 
-#: dbus.c:324
+#: dbus.c:738
 msgid "could not register a DBus message handler"
 msgstr "konnte Steuerungsprogramm für DBus-Nachrichten nicht anmelden"
 
-#: bpf.c:217
+#: bpf.c:263
 #, c-format
 msgid "cannot create DHCP BPF socket: %s"
 msgstr "konnte DHCP-BPF-Socket nicht einrichten: %s"
 
-#: bpf.c:245
+#: bpf.c:291
 #, c-format
 msgid "DHCP request for unsupported hardware type (%d) received on %s"
 msgstr "DHCP-Anfrage für nicht unterstützen Hardwaretyp (%d) auf %s empfangen"
 
-#: tftp.c:281
+#: bpf.c:376
+#, c-format
+msgid "cannot create PF_ROUTE socket: %s"
+msgstr "Kann PF_ROUTE socket nicht erzeugen: %s"
+
+#: bpf.c:397
+msgid "Unknown protocol version from route socket"
+msgstr "Unbekannte Protokollversion vom Route Socket"
+
+#: helper.c:153
+msgid "lease() function missing in Lua script"
+msgstr "Die Funktion lease() fehlt im Lua-Script"
+
+#: tftp.c:309
 msgid "unable to get free port for TFTP"
 msgstr "konnte keinen freien Port für TFTP bekommen"
 
-#: tftp.c:296
+#: tftp.c:325
 #, c-format
 msgid "unsupported request from %s"
 msgstr "nicht unterstützte Anfrage von %s"
 
-#: tftp.c:406
+#: tftp.c:439
 #, c-format
 msgid "file %s not found"
 msgstr "Datei %s nicht gefunden"
 
-#: tftp.c:522
+#: tftp.c:548
 #, c-format
 msgid "error %d %s received from %s"
 msgstr "Fehler %d %s von %s empfangen"
 
-#: tftp.c:554
+#: tftp.c:590
 #, c-format
 msgid "failed sending %s to %s"
 msgstr "konnte %s nicht an %s senden"
 
-#: tftp.c:568
+#: tftp.c:590
 #, c-format
 msgid "sent %s to %s"
 msgstr "%s an %s verschickt"
 
-#: log.c:177
+#: log.c:190
 #, c-format
 msgid "overflow: %d log entries lost"
 msgstr "Überlauf: %d Protokolleinträge verloren"
 
-#: log.c:254
+#: log.c:268
 #, c-format
 msgid "log failed: %s"
 msgstr "Protokollierung fehlgeschlagen: %s"
 
-#: log.c:462
+#: log.c:472
 msgid "FAILED to start up"
 msgstr "Start fehlgeschlagen"
 
+#: conntrack.c:65
+#, c-format
+msgid "Conntrack connection mark retrieval failed: %s"
+msgstr "\"Conntrack connection mark\"-Abruf fehlgeschlagen: %s"
+
+#: dhcp6.c:59
+#, c-format
+msgid "cannot create DHCPv6 socket: %s"
+msgstr "Kann DHCPv6-Socket nicht erzeugen: %s"
+
+#: dhcp6.c:80
+#, c-format
+msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"
+msgstr "kann SO_REUSE{ADDR|PORT} für DHCPv6-Socket nicht aktivieren: %s"
+
+#: dhcp6.c:92
+#, c-format
+msgid "failed to bind DHCPv6 server socket: %s"
+msgstr "Kann nicht an DHCPv6-Server-Socket binden: %s"
+
+#: rfc3315.c:157
+#, c-format
+msgid "no address range available for DHCPv6 request from relay at %s"
+msgstr "Kein Adressbereich verfügbar für die DHCPv6-Anfrage vom Relay bei %s"
+
+#: rfc3315.c:166
+#, c-format
+msgid "no address range available for DHCPv6 request via %s"
+msgstr "Kein Adressbereich verfügbar für die DHCPv6-Anfrage via %s"
+
+#: rfc3315.c:297
+#, c-format
+msgid "%u available DHCPv6 subnet: %s/%d"
+msgstr "%u verfügbare(s) DHCPv6-Subnetz: %s/%d"
+
+#: rfc3315.c:380
+#, c-format
+msgid "%u vendor class: %u"
+msgstr "%u Herstellerklasse: %u"
+
+#: rfc3315.c:428
+#, c-format
+msgid "%u client MAC address: %s"
+msgstr "%u Klient MAC-Adresse: %s"
+
+# FIXME: do not assemble
+#: rfc3315.c:660
+#, c-format
+msgid "unknown prefix-class %d"
+msgstr "unbekannte Präfixklasse %d"
+
+#: rfc3315.c:803 rfc3315.c:898
+msgid "address unavailable"
+msgstr "Adresse nicht verfügbar"
+
+#: rfc3315.c:815 rfc3315.c:946 rfc3315.c:1279
+msgid "success"
+msgstr "Erfolg"
+
+#: rfc3315.c:830 rfc3315.c:839 rfc3315.c:954 rfc3315.c:956
+msgid "no addresses available"
+msgstr "Keine Adressen verfügbar"
+
+#: rfc3315.c:933
+msgid "not on link"
+msgstr "nicht on link"
+
+#: rfc3315.c:1006 rfc3315.c:1191 rfc3315.c:1268
+msgid "no binding found"
+msgstr "Keine Bindung gefunden"
+
+#: rfc3315.c:1044
+msgid "deprecated"
+msgstr "veraltet"
+
+#: rfc3315.c:1049
+msgid "address invalid"
+msgstr "Adresse ungültig"
+
+#: rfc3315.c:1096
+msgid "confirm failed"
+msgstr "Bestätigung fehlgeschlagen"
+
+#: rfc3315.c:1112
+msgid "all addresses still on link"
+msgstr "Alle Adressen immer noch on link"
+
+#: rfc3315.c:1200
+msgid "release received"
+msgstr "Freigabe empfangen"
+
+#: rfc3315.c:2126
+msgid "Cannot multicast to DHCPv6 server without correct interface"
+msgstr "Kann nicht zum DHCPv6 Server multicasten ohne korrekte Schnittstelle"
+
+#: dhcp-common.c:145
+#, c-format
+msgid "Ignoring duplicate dhcp-option %d"
+msgstr "Ignoriere doppelt vorhandene DHCP-Option %d"
+
+#: dhcp-common.c:222
+#, c-format
+msgid "%u tags: %s"
+msgstr "%u Marken: %s"
+
+#: dhcp-common.c:407
+#, c-format
+msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgstr "%s hat mehr als eine Adresse in hosts-Datei, benutze %s für DHCP"
+
+#: dhcp-common.c:430
+#, c-format
+msgid "duplicate IP address %s (%s) in dhcp-config directive"
+msgstr "doppelte IP-Adresse %s (%s) in \"dhcp-config\"-Anweisung"
+
+#: dhcp-common.c:494
+#, c-format
+msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s"
+msgstr "kann SO_BINDTODEVICE für DHCP-Socket nicht aktivieren: %s"
+
+#: dhcp-common.c:615
+#, c-format
+msgid "Known DHCP options:\n"
+msgstr "Bekannte DHCP-Optionen:\n"
+
+#: dhcp-common.c:626
+#, c-format
+msgid "Known DHCPv6 options:\n"
+msgstr "Bekannte DHCPv6-Optionen:\n"
+
+#: dhcp-common.c:823
+msgid ", prefix deprecated"
+msgstr ", Prefix veraltet"
+
+#: dhcp-common.c:826
+#, c-format
+msgid ", lease time "
+msgstr ", Lease Zeit"
+
+#: dhcp-common.c:868
+#, c-format
+msgid "%s stateless on %s%.0s%.0s%s"
+msgstr "%s stateless auf %s%.0s%.0s%s"
+
+#: dhcp-common.c:870
+#, c-format
+msgid "%s, static leases only on %.0s%s%s%.0s"
+msgstr "%s, nur statische Leases auf %.0s%s%s%.0s"
+
+#: dhcp-common.c:872
+#, c-format
+msgid "%s, proxy on subnet %.0s%s%.0s%.0s"
+msgstr "%s, Proxy im Subnetz %.0s%s%.0s%.0s"
+
+#: dhcp-common.c:873
+#, c-format
+msgid "%s, IP range %s -- %s%s%.0s"
+msgstr "%s, IP-Bereich %s -- %s%s%.0s"
+
+#: dhcp-common.c:886
+#, c-format
+msgid "DHCPv4-derived IPv6 names on %s%s"
+msgstr "DHCPv4-abgeleitete IPv6 Namen auf %s%s"
+
+#: dhcp-common.c:889
+#, c-format
+msgid "router advertisement on %s%s"
+msgstr "Router-Advertisment auf %s%s"
+
+#: dhcp-common.c:900
+#, c-format
+msgid "DHCP relay from %s to %s via %s"
+msgstr "DHCP Weiterleitung von %s nach %s über %s"
+
+#: dhcp-common.c:902
+#, c-format
+msgid "DHCP relay from %s to %s"
+msgstr "DHCP Weiterleitung von %s nach %s"
+
+#: radv.c:109
+#, c-format
+msgid "cannot create ICMPv6 socket: %s"
+msgstr "Kann ICMPv6-Socket nicht erzeugen: %s"
+
+#: auth.c:448
+#, c-format
+msgid "ignoring zone transfer request from %s"
+msgstr "ignoriere Zonentransfer-Anfrage von %s"
+
+#: ipset.c:95
+#, c-format
+msgid "failed to find kernel version: %s"
+msgstr "konnte Kernelversion nicht finden: %s"
+
+#: ipset.c:114
+#, c-format
+msgid "failed to create IPset control socket: %s"
+msgstr "konnte IPset-Kontroll-Socket nicht erzeugen: %s"
+
+#: dnssec.c:449 dnssec.c:493
+#, c-format
+msgid "failed to update mtime on %s: %s"
+msgstr "kann die mtime nicht auf %s aktualisieren: %s"
+
+#: blockdata.c:58
+#, c-format
+msgid "DNSSEC memory in use %u, max %u, allocated %u"
+msgstr "DNSSEC Speicher in Benutzung %u, Max %u, zugewiesen %u"
+
+#: tables.c:80
+msgid "error: fill_addr missused"
+msgstr "Fehler: fill_addr falsch verwendet"
+
+#: tables.c:109
+#, c-format
+msgid "failed to access pf devices: %s"
+msgstr "konnte auf pf Geräte nicht zugreifen: %s"
+
+#: tables.c:123
+#, c-format
+msgid "warning: no opened pf devices %s"
+msgstr "Warnung: Keine geöffneten pf Geräte %s"
+
+#: tables.c:131
+#, c-format
+msgid "error: cannot use table name %s"
+msgstr "Fehler: Kann Tabellenname %s nicht benutzen"
+
+#: tables.c:139
+#, c-format
+msgid "error: cannot strlcpy table name %s"
+msgstr "Fehler: Kann den Tabellennamen %s nicht strlcpy"
+
+#: tables.c:145
+#, c-format
+msgid "warning: pfr_add_tables: %s(%d)"
+msgstr "Warnung: pfr_add_tables: %s(%d)"
+
+#: tables.c:151
+msgid "info: table created"
+msgstr "Info: Tabelle erstellt"
+
+#: tables.c:162
+#, c-format
+msgid "warning: DIOCR%sADDRS: %s"
+msgstr "Warnung: DIOCR%sADDRS: %s"
+
+#: tables.c:166
+#, c-format
+msgid "%d addresses %s"
+msgstr "%d Adressen %s"
+
+#: inotify.c:46
+#, c-format
+msgid "failed to create inotify: %s"
+msgstr "Kann kein inotify erzeugen: %s"
+
+#: inotify.c:60
+#, c-format
+msgid "cannot cannonicalise resolv-file %s: %s"
+msgstr "Kann die resolv-file %s nicht kanonisieren: %s"
+
+#: inotify.c:72
+#, c-format
+msgid "directory %s for resolv-file is missing, cannot poll"
+msgstr "Verzeichnis %s für resolv-file fehlt, kann nicht pollen"
+
+#: inotify.c:75 inotify.c:112
+#, c-format
+msgid "failed to create inotify for %s: %s"
+msgstr "Konnte inotify für %s: %s nicht erzeugen"
+
+#: inotify.c:97
+#, c-format
+msgid "bad dynamic directory %s: %s"
+msgstr "fehlerhaftes dynamisches Verzeichnis %s: %s"
+
+#: inotify.c:197
+#, c-format
+msgid "inotify, new or changed file %s"
+msgstr "inotify, neue oder geänderte Datei %s"
+
+#~ msgid "no interface with address %s"
+#~ msgstr "keine Schnittstelle mit Adresse %s"
+
+#~ msgid "duplicate IP address %s in dhcp-config directive."
+#~ msgstr "doppelte IP-Adresse %s in \"dhcp-config\"-Anweisung"
+
+#, fuzzy
+#~ msgid "Specify path to Lua script (no default)."
+#~ msgstr "Dateipfad für Prozesskennung (PID) festlegen (Voreinstellung: %s)."
+
 #~ msgid "only one dhcp-hostsfile allowed"
 #~ msgstr "nur eine DHCP-Hostdatei (dhcp-hostsfile) zulässig"
 
@@ -1503,9 +2185,3 @@ msgstr "Start fehlgeschlagen"
 
 #~ msgid "failed to bind listening socket for %s: %s"
 #~ msgstr "konnte Empfangs-Socket nicht an %s binden: %s"
-
-#~ msgid "failed to listen on socket: %s"
-#~ msgstr "konnte Socket nicht zum Empfang einrichten: %s"
-
-#~ msgid "failed to create TFTP socket: %s"
-#~ msgstr "konnte TFTP-Socket nicht erzeugen: %s"
index efdd24e..46d8e42 100644 (file)
--- a/po/es.po
+++ b/po/es.po
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: dnsmasq 2.24\n"
+"Project-Id-Version: dnsmasq 2.67\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2009-06-18 12:24+0100\n"
-"PO-Revision-Date: 2005-10-07 11:04+0100\n"
-"Last-Translator: Christopher Chatham <chrislinux@gmail.com>\n"
-"Language-Team: Spanish <es@li.org>\n"
+"PO-Revision-Date: 2013-10-04 13:24+0100\n"
+"Last-Translator: Vicente Soriano <victek@gmail.com>\n"
+"Language-Team:\n"
+"Language: es\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=ISO-8859-1\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: cache.c:761
+#: cache.c:523
+msgid "Internal error in cache."
+msgstr ""
+
+#: cache.c:941
 #, fuzzy, c-format
 msgid "failed to load names from %s: %s"
 msgstr "no se pudo cargar nombres desde %s: %s"
 
-#: cache.c:795 dhcp.c:865
+#: cache.c:967 dhcp.c:828
 #, fuzzy, c-format
 msgid "bad address at %s line %d"
 msgstr "dirección errónea en %s línea %d"
 
-#: cache.c:853 dhcp.c:881
+#: cache.c:1018 dhcp.c:844
 #, c-format
 msgid "bad name at %s line %d"
 msgstr "nombre erróneo en %s línea %d"
 
-#: cache.c:860 dhcp.c:956
+#: cache.c:1027 dhcp.c:919
 #, c-format
 msgid "read %s - %d addresses"
 msgstr "direcciónes %s - %d leídas"
 
-#: cache.c:899
+#: cache.c:1135
 msgid "cleared cache"
 msgstr "el caché fue liberado"
 
-#: cache.c:960
+#: cache.c:1164
+#, c-format
+msgid "No IPv4 address found for %s"
+msgstr ""
+
+#: cache.c:1242
 #, c-format
 msgid "%s is a CNAME, not giving it to the DHCP lease of %s"
-msgstr "%s es un CNAME, no se le está dando al arriendo DHCP de %s"
+msgstr "%s es un CNAME, no se le está dando concesión DHCP de %s"
 
-#: cache.c:966
+#: cache.c:1266
 #, c-format
 msgid "not giving name %s to the DHCP lease of %s because the name exists in %s with address %s"
-msgstr "no otorgando nombre %s al arriendo DHCP de %s porque el nombre existe en %s con dirección %s"
+msgstr "no otorgando nombre %s a concesión DHCP de %s porque el nombre existe en %s con dirección %s"
 
-#: cache.c:1039
+#: cache.c:1421
 #, c-format
 msgid "time %lu"
 msgstr "tiempo %lu"
 
-#: cache.c:1040
+#: cache.c:1422
 #, fuzzy, c-format
 msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries."
 msgstr "tamaño de caché %d, %d/%d inserciónes de caché reutilizaron objetos no vencidos."
 
-#: cache.c:1042
+#: cache.c:1424
 #, c-format
 msgid "queries forwarded %u, queries answered locally %u"
 msgstr "búsquedas reenviadas %u, búsquedas respondidas localmente %u"
 
-#: cache.c:1068
+#: cache.c:1427
+#, fuzzy, c-format
+msgid "queries for authoritative zones %u"
+msgstr "Fijar TTL para respuestas autoritarias"
+
+#: cache.c:1453
 #, c-format
 msgid "server %s#%d: queries sent %u, retried or failed %u"
 msgstr "servidor %s#%d: búsquedas enviadas %u, reintentadas o fallidas %u"
 
-#: util.c:57
+#: util.c:45
 #, fuzzy, c-format
 msgid "failed to seed the random number generator: %s"
 msgstr "no se pudo crear valor semilla para el generador de números aleatorios: %s"
 
-#: util.c:189
+#: util.c:205
 #, fuzzy
 msgid "failed to allocate memory"
 msgstr "no se pudo asignar memoria"
 
-#: util.c:227 option.c:573
+#: util.c:250 option.c:601
 msgid "could not get memory"
 msgstr "no se pudo adquirir memoria"
 
-#: util.c:237
+#: util.c:260
 #, fuzzy, c-format
 msgid "cannot create pipe: %s"
 msgstr "no se puede crear pipe: %s"
 
-#: util.c:245
+#: util.c:268
 #, fuzzy, c-format
 msgid "failed to allocate %d bytes"
 msgstr "no se pudo asignar %d bytes"
 
-#: util.c:350
+#: util.c:437
 #, c-format
 msgid "infinite"
 msgstr "infinito"
 
-#: option.c:244
+#: option.c:332
 msgid "Specify local address(es) to listen on."
 msgstr "Especificar dirección(es) locales dónde escuchar."
 
-#: option.c:245
+#: option.c:333
 msgid "Return ipaddr for all hosts in specified domains."
 msgstr "Retornar ipaddr (dirección IP) para todos los hosts en los dominios especificados."
 
-#: option.c:246
+#: option.c:334
 msgid "Fake reverse lookups for RFC1918 private address ranges."
 msgstr "Falsificar búsquedas reversas para rangos de dirección privados RFC1918."
 
-#: option.c:247
+#: option.c:335
 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."
 msgstr "Tratar ipaddr (dirección IP) como NXDOMAIN (derrota comodín Verisign)."
 
-#: option.c:248
+#: option.c:336
 #, c-format
 msgid "Specify the size of the cache in entries (defaults to %s)."
 msgstr "Especificar tamaño de caché en cuanto a cantidad de objetos (%s por predeterminado)."
 
-#: option.c:249
+#: option.c:337
 #, c-format
 msgid "Specify configuration file (defaults to %s)."
 msgstr "Especificar archivo de configuración (%s por predeterminado)."
 
-#: option.c:250
+#: option.c:338
 msgid "Do NOT fork into the background: run in debug mode."
 msgstr "NO hacer un fork hacia el fondo: correr en modo debug."
 
-#: option.c:251
+#: option.c:339
 msgid "Do NOT forward queries with no domain part."
 msgstr "NO reenviar búsquedas sin parte de dominio."
 
-#: option.c:252
+#: option.c:340
 msgid "Return self-pointing MX records for local hosts."
 msgstr "Retornar expedientes MX auto-señaladores para hosts locales."
 
-#: option.c:253
+#: option.c:341
 msgid "Expand simple names in /etc/hosts with domain-suffix."
 msgstr "Expandir nombres simples en /etc/hosts con domain-suffix (sufijo de dominio)."
 
-#: option.c:254
+#: option.c:342
 msgid "Don't forward spurious DNS requests from Windows hosts."
 msgstr "No reenviar pedidos DNS falsos desde máquinas Windows."
 
-#: option.c:255
+#: option.c:343
 msgid "Enable DHCP in the range given with lease duration."
-msgstr "Habilitar DHCP dentro del rango brindado con duración del arriendo."
+msgstr "Habilitar DHCP dentro del rango brindado con duración de concesión."
 
-#: option.c:256
+#: option.c:344
 #, c-format
 msgid "Change to this group after startup (defaults to %s)."
 msgstr "Cambiar a este grupo después del inicio (%s por predeterminado)."
 
-#: option.c:257
+#: option.c:345
 msgid "Set address or hostname for a specified machine."
 msgstr "Fijar dirección o nombre de host para una máquina especificada."
 
-#: option.c:258
+#: option.c:346
 #, fuzzy
 msgid "Read DHCP host specs from file."
 msgstr "Leer especificaciones DHCP de host desde archivo"
 
-#: option.c:259
+#: option.c:347
 #, fuzzy
 msgid "Read DHCP option specs from file."
 msgstr "Leer opciones DHCP de host desde archivo"
 
-#: option.c:260
+#: option.c:348
+#, fuzzy
+msgid "Read DHCP host specs from a directory."
+msgstr "Leer especificaciones DHCP de host desde archivo"
+
+#: option.c:349
+#, fuzzy
+msgid "Read DHCP options from a directory."
+msgstr "Leer opciones DHCP de host desde archivo"
+
+#: option.c:350
 msgid "Evaluate conditional tag expression."
 msgstr "Evaluar expresión condicional de etiqueta."
 
-#: option.c:261
+#: option.c:351
 #, c-format
 msgid "Do NOT load %s file."
 msgstr "NO cargar archivo %s."
 
-#: option.c:262
+#: option.c:352
 #, c-format
 msgid "Specify a hosts file to be read in addition to %s."
 msgstr "Especificar un archivo de hosts para ser leído adicionalmente a %s."
 
-#: option.c:263
+#: option.c:353
+#, fuzzy
+msgid "Read hosts files from a directory."
+msgstr "Leer especificaciones DHCP de host desde archivo"
+
+#: option.c:354
 msgid "Specify interface(s) to listen on."
-msgstr "Especificar interface(s) donde escuchar."
+msgstr "Especificar interfase(s) donde escuchar."
 
-#: option.c:264
+#: option.c:355
 msgid "Specify interface(s) NOT to listen on."
-msgstr "Especificar interface(s) donde NO escuchar."
+msgstr "Especificar interfase(s) donde NO escuchar."
 
-#: option.c:265
+#: option.c:356
 #, fuzzy
 msgid "Map DHCP user class to tag."
 msgstr "Trazar clase de usuario DHCP a etiqueta."
 
-#: option.c:266
+#: option.c:357
 msgid "Map RFC3046 circuit-id to tag."
 msgstr "Trazar circuit-id (identificación de circuito) RFC3046 a etiqueta."
 
-#: option.c:267
+#: option.c:358
 msgid "Map RFC3046 remote-id to tag."
 msgstr "Trazar remote-id (identificación remota) RFC3046 a etiqueta."
 
-#: option.c:268
+#: option.c:359
 msgid "Map RFC3993 subscriber-id to tag."
 msgstr "Trazar subscriber-id (identificación de suscritor) RFC3993 a etiqueta."
 
-#: option.c:269
+#: option.c:360
 #, fuzzy
 msgid "Don't do DHCP for hosts with tag set."
 msgstr "No hacer DHCP para hosts con etiqueta fijada."
 
-#: option.c:270
+#: option.c:361
 #, fuzzy
 msgid "Force broadcast replies for hosts with tag set."
 msgstr "Forzar respuestas broadcast para hosts con etiqueta fijada."
 
-#: option.c:271
+#: option.c:362
 msgid "Do NOT fork into the background, do NOT run in debug mode."
 msgstr "NO hacer un fork hacia el fondo, NO correr en modo debug."
 
-#: option.c:272
+#: option.c:363
 msgid "Assume we are the only DHCP server on the local network."
 msgstr "Asumir que somos el único servidor DHCP en la red local."
 
-#: option.c:273
+#: option.c:364
 #, c-format
 msgid "Specify where to store DHCP leases (defaults to %s)."
-msgstr "Especificar donde almacenar arriendos DHCP (%s por predeterminado)."
+msgstr "Especificar donde almacenar concesión DHCP (%s por predeterminado)."
 
-#: option.c:274
+#: option.c:365
 msgid "Return MX records for local hosts."
 msgstr "Retornar expedientes MX para hosts locales."
 
-#: option.c:275
+#: option.c:366
 msgid "Specify an MX record."
 msgstr "Especificar un expediente MX."
 
-#: option.c:276
+#: option.c:367
 msgid "Specify BOOTP options to DHCP server."
 msgstr "Especificar opciones BOOTP a servidor DHCP."
 
-#: option.c:277
+#: option.c:368
 #, c-format
 msgid "Do NOT poll %s file, reload only on SIGHUP."
 msgstr "NO revisar archivo %s periódicamente, recargar solo con SIGHUP."
 
-#: option.c:278
+#: option.c:369
 msgid "Do NOT cache failed search results."
 msgstr "NO almacenar en caché resultados de búsquedas fallidas."
 
-#: option.c:279
+#: option.c:370
 #, c-format
 msgid "Use nameservers strictly in the order given in %s."
 msgstr "Usar servidores DNS estrictamente en el órden brindado en %s."
 
-#: option.c:280
+#: option.c:371
 #, fuzzy
 msgid "Specify options to be sent to DHCP clients."
 msgstr "Especificar opciones para ser enviadas a clientes DHCP."
 
-#: option.c:281
+#: option.c:372
 msgid "DHCP option sent even if the client does not request it."
 msgstr "Opción DHCP enviada aún si el cliente no la pide."
 
-#: option.c:282
+#: option.c:373
 msgid "Specify port to listen for DNS requests on (defaults to 53)."
 msgstr "Especificar puerto donde escuchar por búsquedas DNS (53 por predeterminado)."
 
-#: option.c:283
+#: option.c:374
 #, c-format
 msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)."
 msgstr "Tamaño máximo de paquetes UDP soportado para EDNS.0 (%s por predeterminado)."
 
-#: option.c:284
+#: option.c:375
 #, fuzzy
 msgid "Log DNS queries."
 msgstr "Bitacorear búsquedas DNS."
 
-#: option.c:285
+#: option.c:376
 #, fuzzy
 msgid "Force the originating port for upstream DNS queries."
-msgstr "Enforzar el puerto original para búsquedas DNS upstream."
+msgstr "Enforzar el puerto original para búsquedas DNS subida."
 
-#: option.c:286
+#: option.c:377
 msgid "Do NOT read resolv.conf."
 msgstr "NO leer resolv.conf."
 
-#: option.c:287
+#: option.c:378
 #, c-format
 msgid "Specify path to resolv.conf (defaults to %s)."
 msgstr "Especificar el path hacia resolv.conf (%s por predeterminado)."
 
-#: option.c:288
+#: option.c:379
+#, fuzzy
+msgid "Specify path to file with server= options"
+msgstr "Especificar path de archivo PID (%s por predeterminado)."
+
+#: option.c:380
 msgid "Specify address(es) of upstream servers with optional domains."
-msgstr "Especificar dirección(es) de servidores upstream con dominios opcionales."
+msgstr "Especificar dirección(es) de servidores subida con dominios opcionales."
+
+#: option.c:381
+#, fuzzy
+msgid "Specify address of upstream servers for reverse address queries"
+msgstr "Especificar dirección(es) de servidores subida con dominios opcionales."
 
-#: option.c:289
+#: option.c:382
 msgid "Never forward queries to specified domains."
 msgstr "Nunca reenviar búsquedas a dominios especificados."
 
-#: option.c:290
+#: option.c:383
 msgid "Specify the domain to be assigned in DHCP leases."
-msgstr "Especificar el dominio para ser asignado en arriendos DHCP."
+msgstr "Especificar el dominio para ser asignado en concesión DHCP."
 
-#: option.c:291
+#: option.c:384
 msgid "Specify default target in an MX record."
 msgstr "Especificar destino predeterminado en un expediente MX."
 
-#: option.c:292
+#: option.c:385
 msgid "Specify time-to-live in seconds for replies from /etc/hosts."
 msgstr "Especificar tiempo de vida en segundos para respuestas desde /etc/hosts."
 
-#: option.c:293
+#: option.c:386
 #, fuzzy
 msgid "Specify time-to-live in seconds for negative caching."
 msgstr "Especificar tiempo de vida en segundos para caché negativo."
 
-#: option.c:294
+#: option.c:387
 #, fuzzy
 msgid "Specify time-to-live in seconds for maximum TTL to send to clients."
 msgstr "Especificar tiempo de vida en segundos para respuestas desde /etc/hosts."
 
-#: option.c:295
+#: option.c:388
+#, fuzzy
+msgid "Specify time-to-live ceiling for cache."
+msgstr "Especificar tiempo de vida en segundos para caché negativo."
+
+#: option.c:389
+#, fuzzy
+msgid "Specify time-to-live floor for cache."
+msgstr "Especificar tiempo de vida en segundos para caché negativo."
+
+#: option.c:390
 #, c-format
 msgid "Change to this user after startup. (defaults to %s)."
 msgstr "Cambiar a este usuario despues del inicio (%s por predeterminado)."
 
-#: option.c:296
+#: option.c:391
 #, fuzzy
 msgid "Map DHCP vendor class to tag."
 msgstr "Trazar clase de vendedor DHCP a etiqueta."
 
-#: option.c:297
+#: option.c:392
 msgid "Display dnsmasq version and copyright information."
 msgstr "Mostrar información sobre la versión y copyright de dnsmasq."
 
-#: option.c:298
+#: option.c:393
 msgid "Translate IPv4 addresses from upstream servers."
-msgstr "Traducir direcciones IPv4 desde servidores upstream."
+msgstr "Traducir direcciones IPv4 desde servidores subida."
 
-#: option.c:299
+#: option.c:394
 msgid "Specify a SRV record."
 msgstr "Especificar un expediente SRV."
 
-#: option.c:300
+#: option.c:395
 msgid "Display this message. Use --help dhcp for known DHCP options."
 msgstr "Mostrar este mensaje. Usar --help dhcp para opciones DHCP conocidas."
 
-#: option.c:301
+#: option.c:396
 #, fuzzy, c-format
 msgid "Specify path of PID file (defaults to %s)."
 msgstr "Especificar path de archivo PID (%s por predeterminado)."
 
-#: option.c:302
+#: option.c:397
 #, c-format
 msgid "Specify maximum number of DHCP leases (defaults to %s)."
-msgstr "Especificar número máximo de arriendos DHCP (%s por predeterminado)."
+msgstr "Especificar número máximo de concesión DHCP (%s por predeterminado)."
 
-#: option.c:303
+#: option.c:398
 msgid "Answer DNS queries based on the interface a query was sent to."
-msgstr "Responder a búsquedas DNS en base a la interface a la cuál fueron enviadas."
+msgstr "Responder a búsquedas DNS en base a la interfase a la cuál fueron enviadas."
 
-#: option.c:304
+#: option.c:399
 msgid "Specify TXT DNS record."
 msgstr "Especificar expediente DNS TXT."
 
-#: option.c:305
+#: option.c:400
 #, fuzzy
 msgid "Specify PTR DNS record."
 msgstr "Especificar expediente DNS PTR."
 
-#: option.c:306
+#: option.c:401
 msgid "Give DNS name to IPv4 address of interface."
-msgstr "Otorgar nombre DNS a dirección IPv4 de interface."
+msgstr "Otorgar nombre DNS a dirección IPv4 de interfase."
 
-#: option.c:307
+#: option.c:402
 msgid "Bind only to interfaces in use."
-msgstr "Acoplar solo a interfaces en uso."
+msgstr "Acoplar solo a interfases en uso."
 
-#: option.c:308
+#: option.c:403
 #, c-format
 msgid "Read DHCP static host information from %s."
 msgstr "Leer información sobre hosts DHCP estáticos desde %s."
 
-#: option.c:309
+#: option.c:404
 msgid "Enable the DBus interface for setting upstream servers, etc."
-msgstr "Habilitar la interface DBus para fijar servidores upstream, etc."
+msgstr "Habilitar la interfase DBus para fijar servidores subida, etc."
 
-#: option.c:310
+#: option.c:405
 msgid "Do not provide DHCP on this interface, only provide DNS."
-msgstr "No proveer DHCP en esta interface, sólo proveer DNS."
+msgstr "No proveer DHCP en esta interfase, sólo proveer DNS."
 
-#: option.c:311
+#: option.c:406
 msgid "Enable dynamic address allocation for bootp."
 msgstr "Habilitar alocación dinámica de direcciónes para BOOTP."
 
-#: option.c:312
+#: option.c:407
 #, fuzzy
 msgid "Map MAC address (with wildcards) to option set."
 msgstr "Trazar dirección MAC (con comodínes) a opción fijada."
 
-#: option.c:313
+#: option.c:408
 msgid "Treat DHCP requests on aliases as arriving from interface."
-msgstr "Tratar pedidos DHCP en alias como si llegaran de la interface."
+msgstr "Tratar pedidos DHCP en alias como si llegaran de la interfase."
 
-#: option.c:314
+#: option.c:409
 msgid "Disable ICMP echo address checking in the DHCP server."
 msgstr "Deshabilitar verificación de direcciónes para echo ICMP en el servidor DHCP."
 
-#: option.c:315
-msgid "Script to run on DHCP lease creation and destruction."
-msgstr "Archivo guión para ejecutar cuando se crea o destruye un arriendo DHCP."
+#: option.c:410
+#, fuzzy
+msgid "Shell script to run on DHCP lease creation and destruction."
+msgstr "Archivo guión para ejecutar cuando se crea o destruye una concesión DHCP."
+
+#: option.c:411
+#, fuzzy
+msgid "Lua script to run on DHCP lease creation and destruction."
+msgstr "Archivo guión para ejecutar cuando se crea o destruye una concesión DHCP."
+
+#: option.c:412
+#, fuzzy
+msgid "Run lease-change scripts as this user."
+msgstr "Correr archivo guión de cambio de concesión como este usuario."
 
-#: option.c:316
+#: option.c:413
 msgid "Read configuration from all the files in this directory."
 msgstr "Leer configuración desde todos los archivos en este directorio."
 
-#: option.c:317
+#: option.c:414
 #, fuzzy
 msgid "Log to this syslog facility or file. (defaults to DAEMON)"
 msgstr "Bitacorear a esta facilidad syslog o archivo. (DAEMON por predeterminado)"
 
-#: option.c:318
+#: option.c:415
 msgid "Do not use leasefile."
-msgstr "No usar archivo de arriendos."
+msgstr "No usar archivo de concesión."
 
-#: option.c:319
+#: option.c:416
 #, fuzzy, c-format
 msgid "Maximum number of concurrent DNS queries. (defaults to %s)"
 msgstr "Número máximo de búsquedas DNS simultáneas. (%s por predeterminado)"
 
-#: option.c:320
+#: option.c:417
 #, c-format
 msgid "Clear DNS cache when reloading %s."
 msgstr "Liberar caché DNS al recargar %s."
 
-#: option.c:321
+#: option.c:418
 msgid "Ignore hostnames provided by DHCP clients."
 msgstr "Ignorar nombres de host brindados por clientes DHCP."
 
-#: option.c:322
+#: option.c:419
 msgid "Do NOT reuse filename and server fields for extra DHCP options."
 msgstr "NO reutilizar campos de nombre de archivo y servidor para opciones DHCP extra."
 
-#: option.c:323
+#: option.c:420
 msgid "Enable integrated read-only TFTP server."
 msgstr "Habilitar servidor integrado TFTP solo-lectura."
 
-#: option.c:324
+#: option.c:421
 msgid "Export files by TFTP only from the specified subtree."
 msgstr "Exportar archivos vía TFTP solo del sub-árbol especificado."
 
-#: option.c:325
+#: option.c:422
 msgid "Add client IP address to tftp-root."
 msgstr "Agregar IP de cliente a tftp-root."
 
-#: option.c:326
+#: option.c:423
 msgid "Allow access only to files owned by the user running dnsmasq."
 msgstr "Permitir acceso solo a archivos pertenecientes al usuario que corre dnsmasq."
 
-#: option.c:327
+#: option.c:424
+msgid "Do not terminate the service if TFTP directories are inaccessible."
+msgstr ""
+
+#: option.c:425
 #, fuzzy, c-format
 msgid "Maximum number of conncurrent TFTP transfers (defaults to %s)."
 msgstr "Número máximo de transferencias TFTP simultáneas (%s por predeterminado)."
 
-#: option.c:328
+#: option.c:426
 msgid "Disable the TFTP blocksize extension."
 msgstr "Deshabilitar la extensión TFTP blocksize (tamaño de bloque)."
 
-#: option.c:329
+#: option.c:427
+msgid "Convert TFTP filenames to lowercase"
+msgstr "Convertir a minúsculas los nombres de archivos TFTP"
+
+#: option.c:428
 msgid "Ephemeral port range for use by TFTP transfers."
-msgstr "Rango de puertos efímeros para ser usados por transferencias TFTP."
+msgstr "Rango de puertos efímeros para ser usados en transferencias TFTP."
 
-#: option.c:330
+#: option.c:429
 msgid "Extra logging for DHCP."
-msgstr "Bitacoreo extra para DHCP."
+msgstr "Log extra para DHCP."
 
-#: option.c:331
+#: option.c:430
 msgid "Enable async. logging; optionally set queue length."
-msgstr "Habilitar bitacoreo asincrónico; opcionalmente fijar tamaño de cola."
+msgstr "Habilitar registro asíncrono; opcionalmente fijar tamaño de cola."
 
-#: option.c:332
+#: option.c:431
 msgid "Stop DNS rebinding. Filter private IP ranges when resolving."
 msgstr "Detener revinculación DNS. Filtrar rangos de IP privados al resolver."
 
-#: option.c:333
+#: option.c:432
 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers."
 msgstr "Permitir revinculación de 127.0.0.0/8, para servidores RBL."
 
-#: option.c:334
+#: option.c:433
 msgid "Inhibit DNS-rebind protection on this domain."
 msgstr "Inhibir protección de revinculación DNS en este dominio."
 
-#: option.c:335
+#: option.c:434
 msgid "Always perform DNS queries to all servers."
 msgstr "Siempre realizar búsquedas DNS a todos los servidores."
 
-#: option.c:336
+#: option.c:435
 #, fuzzy
 msgid "Set tag if client includes matching option in request."
 msgstr "Fijar etiqueta si cliente incluye opción coincidente en pedido."
 
-#: option.c:337
+#: option.c:436
 msgid "Use alternative ports for DHCP."
 msgstr "Usar puertos alternativos para DHCP."
 
-#: option.c:338
-msgid "Run lease-change script as this user."
-msgstr "Correr archivo guión de cambio de arriendos como este usuario."
-
-#: option.c:339
+#: option.c:437
 #, fuzzy
 msgid "Specify NAPTR DNS record."
 msgstr "Especificar expediente DNS NAPTR."
 
-#: option.c:340
+#: option.c:438
 msgid "Specify lowest port available for DNS query transmission."
 msgstr "Especificar puerto más bajo disponible para transmisión de búsquedas DNS."
 
-#: option.c:341
+#: option.c:439
 msgid "Use only fully qualified domain names for DHCP clients."
 msgstr "Usar solo nombres de dominio completamente calificados para clientes DHCP."
 
-#: option.c:342
+#: option.c:440
 msgid "Generate hostnames based on MAC address for nameless clients."
 msgstr "Generar hostnames basados en direcciones MAC para clientes sin nombre."
 
-#: option.c:343
+#: option.c:441
 msgid "Use these DHCP relays as full proxies."
 msgstr "Usar estos relays DHCP como proxies completos."
 
-#: option.c:344
+#: option.c:442
+msgid "Relay DHCP requests to a remote server"
+msgstr ""
+
+#: option.c:443
 msgid "Specify alias name for LOCAL DNS name."
 msgstr "Especificar nombre alias para nombre DNS LOCAL."
 
-#: option.c:345
+#: option.c:444
 #, fuzzy
 msgid "Prompt to send to PXE clients."
 msgstr "Aviso a ser enviado a clientes PXE."
 
-#: option.c:346
+#: option.c:445
 msgid "Boot service for PXE menu."
-msgstr "Servico boot para menú PXE."
+msgstr "Servicio de arranque para menú PXE."
 
-#: option.c:347
+#: option.c:446
 msgid "Check configuration syntax."
 msgstr "Revisar sintaxis de configuración."
 
-#: option.c:348
-msgid "Add requestor's MAC address to forwarded DNS queries"
+#: option.c:447
+msgid "Add requestor's MAC address to forwarded DNS queries."
+msgstr "Añadir direcciones MAC de los peticionarios a los filtros DNS enviados"
+
+#: option.c:448
+#, fuzzy
+msgid "Add requestor's IP subnet to forwarded DNS queries."
+msgstr "Añadir direcciones MAC de los peticionarios a los filtros DNS enviados"
+
+#: option.c:449
+#, fuzzy
+msgid "Proxy DNSSEC validation results from upstream nameservers."
+msgstr "Traducir direcciones IPv4 desde servidores subida."
+
+#: option.c:450
+msgid "Attempt to allocate sequential IP addresses to DHCP clients."
+msgstr "Intento de instaurar direcciones IP secuenciales a cliente DHCP"
+
+#: option.c:451
+msgid "Copy connection-track mark from queries to upstream connections."
+msgstr "Copiar la marca de connection-track desde los filtros a las conexiones salientes"
+
+#: option.c:452
+msgid "Allow DHCP clients to do their own DDNS updates."
+msgstr "Permite a clientes DHCP realizar sus propias actualizaciones DDNS"
+
+#: option.c:453
+msgid "Send router-advertisements for interfaces doing DHCPv6"
+msgstr "Enviar anuncios del router a los interfases realizando DHCPv6"
+
+#: option.c:454
+msgid "Specify DUID_EN-type DHCPv6 server DUID"
 msgstr ""
 
-#: option.c:349
+#: option.c:455
+#, fuzzy
+msgid "Specify host (A/AAAA and PTR) records"
+msgstr "Especificar un expediente MX."
+
+#: option.c:456
+#, fuzzy
+msgid "Specify arbitrary DNS resource record"
+msgstr "Especificar expediente DNS TXT."
+
+#: option.c:457
 #, fuzzy
-msgid "Proxy DNSSEC validation results from upstream nameservers"
-msgstr "Traducir direcciones IPv4 desde servidores upstream."
+msgid "Bind to interfaces in use - check for new interfaces"
+msgstr "interfase desconocida %s en bridge-interfase"
+
+#: option.c:458
+msgid "Export local names to global DNS"
+msgstr "Exportar nombres DNS locales a globales"
+
+#: option.c:459
+msgid "Domain to export to global DNS"
+msgstr "Dominio a exportar a DNS global"
+
+#: option.c:460
+msgid "Set TTL for authoritative replies"
+msgstr "Fijar TTL para respuestas autoritarias"
+
+#: option.c:461
+msgid "Set authoritive zone information"
+msgstr "Fijar información de zona autoritaria"
+
+#: option.c:462
+msgid "Secondary authoritative nameservers for forward domains"
+msgstr "Nombres de servidor secundario autoritatorios para dominios enviados"
+
+#: option.c:463
+msgid "Peers which are allowed to do zone transfer"
+msgstr "Colegas autorizados a la zona de transferencia (transfer)"
+
+#: option.c:464
+msgid "Specify ipsets to which matching domains should be added"
+msgstr "Especificar los ipsets coincidentes en dominio que debrían ser añadidos"
 
-#: option.c:638
+#: option.c:465
+#, fuzzy
+msgid "Specify a domain and address range for synthesised names"
+msgstr "Especificar dominio y rango de direcciones para los nombres acrónimos"
+
+#: option.c:466
+msgid "Activate DNSSEC validation"
+msgstr ""
+
+#: option.c:467
+msgid "Specify trust anchor key digest."
+msgstr ""
+
+#: option.c:468
+msgid "Disable upstream checking for DNSSEC debugging."
+msgstr ""
+
+#: option.c:469
+msgid "Ensure answers without DNSSEC are in unsigned zones."
+msgstr ""
+
+#: option.c:470
+msgid "Don't check DNSSEC signature timestamps until first cache-reload"
+msgstr ""
+
+#: option.c:471
+msgid "Timestamp file to verify system clock for DNSSEC"
+msgstr ""
+
+#: option.c:473
+msgid "Specify DHCPv6 prefix class"
+msgstr "Especificar prefijo de clase DHCPv6"
+
+#: option.c:475
+msgid "Set priority, resend-interval and router-lifetime"
+msgstr ""
+
+#: option.c:476
+msgid "Do not log routine DHCP."
+msgstr ""
+
+#: option.c:477
+msgid "Do not log routine DHCPv6."
+msgstr ""
+
+#: option.c:478
+msgid "Do not log RA."
+msgstr ""
+
+#: option.c:479
+msgid "Accept queries only from directly-connected networks"
+msgstr ""
+
+#: option.c:480
+msgid "Detect and remove DNS forwarding loops"
+msgstr ""
+
+#: option.c:481
+msgid "Ignore DNS responses containing ipaddr."
+msgstr ""
+
+#: option.c:683
 #, c-format
 msgid ""
 "Usage: dnsmasq [options]\n"
@@ -566,933 +764,1468 @@ msgstr ""
 "Modo de uso: dnsmasq [opciones]\n"
 "\n"
 
-#: option.c:640
+#: option.c:685
 #, c-format
 msgid "Use short options only on the command line.\n"
 msgstr "Usar opciones cortas solo en la línea de comandos.\n"
 
-#: option.c:642
+#: option.c:687
 #, fuzzy, c-format
 msgid "Valid options are:\n"
 msgstr "Opciones válidas son :\n"
 
-#: option.c:683
-#, c-format
-msgid "Known DHCP options:\n"
-msgstr "Opciones DHCP conocidas:\n"
+#: option.c:744 option.c:748
+msgid "bad port"
+msgstr "puerto erróneo"
+
+#: option.c:775 option.c:807
+msgid "interface binding not supported"
+msgstr "vinculación de interfase no está soportado"
+
+#: option.c:784 option.c:3575
+#, fuzzy
+msgid "bad interface name"
+msgstr "nombre de interfase erróneo"
+
+#: option.c:814
+#, fuzzy
+msgid "bad address"
+msgstr "dirección IP errónea"
+
+#: option.c:996
+msgid "unsupported encapsulation for IPv6 option"
+msgstr "Encapsulación no soportada para opción IPv6"
 
-#: option.c:798
+#: option.c:1010
 msgid "bad dhcp-option"
 msgstr "opción dhcp-option errónea"
 
-#: option.c:860
+#: option.c:1078
 #, fuzzy
 msgid "bad IP address"
 msgstr "dirección IP errónea"
 
-#: option.c:968
+#: option.c:1081 option.c:1219 option.c:2893
+#, fuzzy
+msgid "bad IPv6 address"
+msgstr "dirección IP errónea"
+
+#: option.c:1246 option.c:1340
 msgid "bad domain in dhcp-option"
 msgstr "dominio erróneo en dhcp-option"
 
-#: option.c:1034
+#: option.c:1378
 msgid "dhcp-option too long"
 msgstr "opción dhcp-option demasiado larga"
 
-#: option.c:1043
+#: option.c:1385
 msgid "illegal dhcp-match"
 msgstr "dhcp-match ilegal"
 
-#: option.c:1087
+#: option.c:1447
 msgid "illegal repeated flag"
 msgstr "opción repetida ilegal"
 
-#: option.c:1095
+#: option.c:1455
 msgid "illegal repeated keyword"
 msgstr "palabra clave repetida ilegal"
 
-#: option.c:1147 option.c:3030
+#: option.c:1520 option.c:4191
 #, fuzzy, c-format
 msgid "cannot access directory %s: %s"
-msgstr "no se puede accesar directorio %s: %s"
+msgstr "no se puede acceder a directorio %s: %s"
 
-#: option.c:1178 tftp.c:460
+#: option.c:1566 tftp.c:493
 #, fuzzy, c-format
 msgid "cannot access %s: %s"
-msgstr "no se puede accesar %s: %s"
+msgstr "no se puede acceder %s: %s"
 
-#: option.c:1207
+#: option.c:1618
 msgid "setting log facility is not possible under Android"
-msgstr ""
+msgstr "la creación de un registro no es posible en Android"
 
-#: option.c:1216
+#: option.c:1627
 msgid "bad log facility"
-msgstr ""
+msgstr "ubicación del registro errónea"
 
-#: option.c:1265
+#: option.c:1680
 msgid "bad MX preference"
 msgstr "preferencia MX errónea"
 
-#: option.c:1270
+#: option.c:1685
 msgid "bad MX name"
 msgstr "nombre MX erróneo"
 
-#: option.c:1284
+#: option.c:1699
 msgid "bad MX target"
 msgstr "destino MX erróneo"
 
-#: option.c:1294
+#: option.c:1711
 msgid "cannot run scripts under uClinux"
-msgstr "no se pueden correr archivos guiónes bajo uClinux"
+msgstr "no se pueden correr archivos 'script' bajo uClinux"
 
-#: option.c:1296
+#: option.c:1713
 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts"
-msgstr "recompilar con HAVE_SCRIPT definido para habilitar guiónes de cambio de arriendo"
+msgstr "recompilar con HAVE_SCRIPT definido para habilitar guiónes de cambio de concesión"
 
-#: option.c:1597 option.c:1601
-msgid "bad port"
-msgstr "puerto erróneo"
+#: option.c:1717
+#, fuzzy
+msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts"
+msgstr "recompilar con HAVE_SCRIPT definido para habilitar 'scripts' en Lua"
 
-#: option.c:1620 option.c:1645
-msgid "interface binding not supported"
-msgstr "vinculación de interface no está soportado"
+#: option.c:1973 option.c:2018 option.c:2074
+#, fuzzy
+msgid "bad prefix"
+msgstr "prefijo erróneo"
 
-#: option.c:1791
+#: option.c:2355
+#, fuzzy
+msgid "recompile with HAVE_IPSET defined to enable ipset directives"
+msgstr "recompilar con HAVE_SCRIPT definido para habilitar directivas ipset"
+
+#: option.c:2548
 #, fuzzy
 msgid "bad port range"
 msgstr "rango de puertos erróneo"
 
-#: option.c:1808
+#: option.c:2564
 msgid "bad bridge-interface"
-msgstr "opción bridge-interface (interface puente) errónea"
-
-#: option.c:1850
-msgid "bad dhcp-range"
-msgstr "opción dhcp-range (rango DHCP) errónea"
+msgstr "opción bridge-interface (interfase puente) errónea"
 
-#: option.c:1878
+#: option.c:2624
 msgid "only one tag allowed"
 msgstr "solo una etiqueta permitida"
 
-#: option.c:1925
+#: option.c:2644 option.c:2656 option.c:2764 option.c:2805
+msgid "bad dhcp-range"
+msgstr "opción dhcp-range (rango DHCP) errónea"
+
+#: option.c:2671
 msgid "inconsistent DHCP range"
 msgstr "rango DHCP inconsistente"
 
-#: option.c:2019 option.c:2045
+#: option.c:2732
+msgid "prefix length must be exactly 64 for RA subnets"
+msgstr "la longitud del prefijo debe ser 64 exacto para subredes RA"
+
+#: option.c:2734
+msgid "prefix length must be exactly 64 for subnet constructors"
+msgstr "la longitud del prefijo debe ser 64 exacto para subredes constructoras"
+
+#: option.c:2738
+msgid "prefix length must be at least 64"
+msgstr "la longitud del prefijo debe ser al menos 64"
+
+#: option.c:2741
+#, fuzzy
+msgid "inconsistent DHCPv6 range"
+msgstr "rango DHCP inconsistente"
+
+#: option.c:2752
+msgid "prefix must be zero with \"constructor:\" argument"
+msgstr "prefijo debe ser cero con argumento \"constructor:\""
+
+#: option.c:2863 option.c:2911
 #, fuzzy
 msgid "bad hex constant"
-msgstr "opción dhcp-host errónea"
+msgstr "constante hexadecimal errónea"
 
-#: option.c:2107
+#: option.c:2885
+msgid "cannot match tags in --dhcp-host"
+msgstr "no coinciden etiquetas en --dhcp-host"
+
+#: option.c:2933
+#, fuzzy, c-format
+msgid "duplicate dhcp-host IP address %s"
+msgstr "dirección IP duplicada %s en %s."
+
+#: option.c:2991
 #, fuzzy
 msgid "bad DHCP host name"
 msgstr "nombre de host DHCP erróneo"
 
-#: option.c:2188
+#: option.c:3073
 #, fuzzy
 msgid "bad tag-if"
-msgstr "destino MX erróneo"
+msgstr "etiqueta tag-if errónea"
 
-#: option.c:2467 option.c:2752
+#: option.c:3397 option.c:3791
 msgid "invalid port number"
 msgstr "número de puerto inválido"
 
-#: option.c:2529
+#: option.c:3459
 #, fuzzy
 msgid "bad dhcp-proxy address"
 msgstr "dirección IP errónea"
 
-#: option.c:2569
+#: option.c:3485
 #, fuzzy
-msgid "invalid alias range"
-msgstr "rango alias inválido"
+msgid "Bad dhcp-relay"
+msgstr "opción dhcp-range (rango DHCP) errónea"
 
-#: option.c:2582
+#: option.c:3511
+msgid "bad RA-params"
+msgstr ""
+
+#: option.c:3520
+msgid "bad DUID"
+msgstr "DUID erróneo"
+
+#: option.c:3562
 #, fuzzy
-msgid "bad interface name"
-msgstr "nombre de interface erróneo"
+msgid "invalid alias range"
+msgstr "rango alias inválido"
 
-#: option.c:2607
+#: option.c:3616
 msgid "bad CNAME"
 msgstr "CNAME erróneo"
 
-#: option.c:2612
+#: option.c:3621
 msgid "duplicate CNAME"
 msgstr "CNAME duplicado"
 
-#: option.c:2632
+#: option.c:3641
 #, fuzzy
 msgid "bad PTR record"
-msgstr "expediente PTR erróneo"
+msgstr "registro PTR erróneo"
 
-#: option.c:2663
+#: option.c:3672
 #, fuzzy
 msgid "bad NAPTR record"
-msgstr "expediente NAPTR erróneo"
+msgstr "registro NAPTR erróneo"
+
+#: option.c:3706
+#, fuzzy
+msgid "bad RR record"
+msgstr "registro PTR erróneo"
 
-#: option.c:2695
+#: option.c:3736
 msgid "bad TXT record"
-msgstr "expediente TXT erróneo"
+msgstr "registro TXT erróneo"
 
-#: option.c:2738
+#: option.c:3777
 msgid "bad SRV record"
-msgstr "expediente SRV erróneo"
+msgstr "registro SRV erróneo"
 
-#: option.c:2745
+#: option.c:3784
 msgid "bad SRV target"
 msgstr "destino SRV erróneo"
 
-#: option.c:2759
+#: option.c:3798
 msgid "invalid priority"
 msgstr "prioridad inválida"
 
-#: option.c:2766
+#: option.c:3805
 msgid "invalid weight"
 msgstr "peso inválido"
 
-#: option.c:2785
-msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"
+#: option.c:3829
+#, fuzzy
+msgid "Bad host-record"
+msgstr "registro PTR erróneo"
+
+#: option.c:3846
+#, fuzzy
+msgid "Bad name in host-record"
+msgstr "nombre erróneo en %s"
+
+#: option.c:3911
+#, fuzzy
+msgid "bad trust anchor"
+msgstr "rango de puertos erróneo"
+
+#: option.c:3925
+msgid "bad HEX in trust anchor"
+msgstr ""
+
+#: option.c:3935
+#, fuzzy
+msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"
 msgstr "opción no soportada (verificar que dnsmasq fue compilado con soporte para DHCP/TFTP/DBus)"
 
-#: option.c:2849
+#: option.c:3994
 msgid "missing \""
 msgstr "falta \""
 
-#: option.c:2908
+#: option.c:4051
 msgid "bad option"
 msgstr "opción errónea"
 
-#: option.c:2910
+#: option.c:4053
 msgid "extraneous parameter"
 msgstr "parámetro extraño"
 
-#: option.c:2912
+#: option.c:4055
 msgid "missing parameter"
 msgstr "parámetro ausente"
 
-#: option.c:2916
+#: option.c:4057
+#, fuzzy
+msgid "illegal option"
+msgstr "opción errónea"
+
+#: option.c:4064
 msgid "error"
 msgstr "error"
 
-#: option.c:2921
-#, c-format
-msgid "%s at line %d of %%s"
+#: option.c:4066
+#, fuzzy, c-format
+msgid " at line %d of %s"
 msgstr "%s en línea %d de %%s"
 
-#: option.c:2985 tftp.c:624
+#: option.c:4081 option.c:4328 option.c:4364
+#, fuzzy, c-format
+msgid "read %s"
+msgstr "lee %s"
+
+#: option.c:4144 option.c:4267 tftp.c:667
 #, c-format
 msgid "cannot read %s: %s"
 msgstr "no se puede leer %s: %s"
 
-#: option.c:3151 option.c:3187
-#, fuzzy, c-format
-msgid "read %s"
-msgstr "leyendo %s"
-
-#: option.c:3239
+#: option.c:4430
 msgid "junk found in command line"
-msgstr ""
+msgstr "basura encontrada en linea de comando"
 
-#: option.c:3269
+#: option.c:4465
 #, c-format
 msgid "Dnsmasq version %s  %s\n"
-msgstr "Dnsmasq versión %s  %s\n"
+msgstr "Versión dnsmasq %s  %s\n"
 
-#: option.c:3270
-#, c-format
+#: option.c:4466
+#, fuzzy, c-format
 msgid ""
-"Compile time options %s\n"
+"Compile time options: %s\n"
 "\n"
 msgstr ""
 "Opciones de compilación %s\n"
 "\n"
 
-#: option.c:3271
+#: option.c:4467
 #, c-format
 msgid "This software comes with ABSOLUTELY NO WARRANTY.\n"
 msgstr "Este software viene SIN NINGUNA GARANTIA.\n"
 
-#: option.c:3272
+#: option.c:4468
 #, c-format
 msgid "Dnsmasq is free software, and you are welcome to redistribute it\n"
-msgstr "Dnsmasq es software libre, y usted está bienvenido a redistribuirlo\n"
+msgstr "Dnsmasq es software libre, y usted está autorizado a redistribuirlo\n"
 
-#: option.c:3273
+#: option.c:4469
 #, fuzzy, c-format
 msgid "under the terms of the GNU General Public License, version 2 or 3.\n"
 msgstr "bajo los términos de la GNU General Public License, versión 2 o 3.\n"
 
-#: option.c:3284
+#: option.c:4480
 msgid "try --help"
 msgstr "pruebe --help"
 
-#: option.c:3286
+#: option.c:4482
 msgid "try -w"
 msgstr "pruebe -w"
 
-#: option.c:3289
+#: option.c:4484
 #, fuzzy, c-format
 msgid "bad command line options: %s"
 msgstr "opciones de línea de comandos erróneas: %s"
 
-#: option.c:3330
+#: option.c:4541
 #, c-format
 msgid "cannot get host-name: %s"
 msgstr "no se puede obtener host-name (nombre de host): %s"
 
-#: option.c:3358
+#: option.c:4569
 msgid "only one resolv.conf file allowed in no-poll mode."
-msgstr "solo un archivo resolv.conf permitido en modo no-poll."
+msgstr "solo un archivo resolv.conf está permitido en modo no-poll."
 
-#: option.c:3368
+#: option.c:4579
 msgid "must have exactly one resolv.conf to read domain from."
 msgstr "debe haber exáctamente un resolv.conf desde donde leer dominio."
 
-#: option.c:3371 network.c:848 dhcp.c:814
+#: option.c:4582 network.c:1507 dhcp.c:777
 #, fuzzy, c-format
 msgid "failed to read %s: %s"
 msgstr "no se pudo leer %s: %s"
 
-#: option.c:3388
+#: option.c:4599
 #, c-format
 msgid "no search directive found in %s"
 msgstr "ninguna directiva de búsqueda encontrada en %s"
 
-#: option.c:3409
+#: option.c:4620
 #, fuzzy
 msgid "there must be a default domain when --dhcp-fqdn is set"
 msgstr "debe haber un dominio predeterminado cuando --dhcp-fqdn está fijado"
 
-#: option.c:3413
+#: option.c:4629
 msgid "syntax check OK"
 msgstr "revisión de sintaxis OK"
 
-#: forward.c:461
+#: forward.c:111
+#, fuzzy, c-format
+msgid "failed to send packet: %s"
+msgstr "no se pudo escuchar en socket: %s"
+
+#: forward.c:591
+msgid "discarding DNS reply: subnet option mismatch"
+msgstr ""
+
+#: forward.c:614
 #, c-format
 msgid "nameserver %s refused to do a recursive query"
-msgstr "servidor DNS %s se reusó a hacer una búsqueda recursiva"
+msgstr "servidor DNS %s rechazó realizar una búsqueda recursiva"
 
-#: forward.c:489
+#: forward.c:646
 #, fuzzy, c-format
 msgid "possible DNS-rebind attack detected: %s"
 msgstr "posible ataque de revinculación DNS detectado"
 
-#: network.c:171
+#: forward.c:1209 forward.c:1815
+msgid "Ignoring query from non-local network"
+msgstr ""
+
+#: forward.c:2286
 #, fuzzy, c-format
-msgid "unknown interface %s in bridge-interface"
-msgstr "interface desconocida %s en bridge-interface"
+msgid "Maximum number of concurrent DNS queries reached (max: %d)"
+msgstr "Número máximo de búsquedas DNS simultáneas alcanzado. (%s por predeterminado)"
 
-#: network.c:380
+#: network.c:715
 #, fuzzy, c-format
 msgid "failed to create listening socket for %s: %s"
-msgstr "no se pudo crear un socket escuchador: %s"
+msgstr "no se pudo crear un zócalo de escucha: %s"
+
+#: network.c:1021
+#, c-format
+msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s"
+msgstr ""
 
-#: network.c:746
+#: network.c:1028
+msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)"
+msgstr ""
+
+#: network.c:1037
+#, fuzzy, c-format
+msgid "warning: no addresses found for interface %s"
+msgstr "usando direcciones locales solo para %s %s"
+
+#: network.c:1095
+#, fuzzy, c-format
+msgid "interface %s failed to join DHCPv6 multicast group: %s"
+msgstr "el interfase % falló al unirse al grupo multicast DHCPv6: %s"
+
+#: network.c:1289
 #, fuzzy, c-format
 msgid "failed to bind server socket for %s: %s"
-msgstr "no se pudo acoplar socket escuchador para %s: %s"
+msgstr "no se pudo acoplar al zócalo del servidor para %s: %s"
 
-#: network.c:783
+#: network.c:1445
 #, c-format
 msgid "ignoring nameserver %s - local interface"
-msgstr "ignorando servidor DNS %s - interface local"
+msgstr "ignorando servidor DNS %s - interfase local"
 
-#: network.c:794
+#: network.c:1456
 #, fuzzy, c-format
 msgid "ignoring nameserver %s - cannot make/bind socket: %s"
-msgstr "ignorando servidor DNS %s - no se puede crear/acoplar socket: %s"
+msgstr "ignorando servidor DNS %s - no se puede crear/acoplar zócalo: %s"
 
-#: network.c:811
+#: network.c:1469
 msgid "unqualified"
-msgstr "no calificado"
+msgstr "no cualificado"
 
-#: network.c:811
+#: network.c:1469
 msgid "names"
 msgstr "nombres"
 
-#: network.c:813
+#: network.c:1471
 msgid "default"
 msgstr "predeterminado"
 
-#: network.c:815
+#: network.c:1473
 msgid "domain"
 msgstr "dominio"
 
-#: network.c:818
+#: network.c:1476
 #, c-format
 msgid "using local addresses only for %s %s"
 msgstr "usando direcciones locales solo para %s %s"
 
-#: network.c:820
+#: network.c:1478
 #, fuzzy, c-format
 msgid "using standard nameservers for %s %s"
-msgstr "usando servidor DNS %s#%d para %s %s"
+msgstr "usando nombres estándar %s#%d para %s %s"
 
-#: network.c:822
+#: network.c:1480
 #, c-format
 msgid "using nameserver %s#%d for %s %s"
-msgstr "usando servidor DNS %s#%d para %s %s"
+msgstr "usando nombre de servidor %s#%d para %s %s"
+
+#: network.c:1484
+#, fuzzy, c-format
+msgid "NOT using nameserver %s#%d - query loop detected"
+msgstr "usando nombre de servidor %s#%d para %s %s"
 
-#: network.c:825
+#: network.c:1487
 #, fuzzy, c-format
 msgid "using nameserver %s#%d(via %s)"
-msgstr "usando servidor DNS %s#%d(vía %s)"
+msgstr "usando nombre de servidor %s#%d(vía %s)"
 
-#: network.c:827
+#: network.c:1489
 #, c-format
 msgid "using nameserver %s#%d"
-msgstr "usando servidor DNS %s#%d"
+msgstr "usando nombre de servidor %s#%d"
+
+#: dnsmasq.c:163
+msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"
+msgstr ""
+
+#: dnsmasq.c:170
+msgid "no trust anchors provided for DNSSEC"
+msgstr ""
+
+#: dnsmasq.c:173
+msgid "cannot reduce cache size from default when DNSSEC enabled"
+msgstr ""
+
+#: dnsmasq.c:175
+#, fuzzy
+msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h"
+msgstr "DBus no disponible: fijar HAVE_DBUS en src/config.h"
 
-#: dnsmasq.c:148
+#: dnsmasq.c:181
 #, fuzzy
 msgid "TFTP server not available: set HAVE_TFTP in src/config.h"
 msgstr "servidor TFTP no disponible: fijar HAVE_TFTP en src/config.h"
 
-#: dnsmasq.c:153
+#: dnsmasq.c:186
+#, fuzzy
+msgid "cannot use --conntrack AND --query-port"
+msgstr "No puede usar --conntrack AND --query-port"
+
+#: dnsmasq.c:189
+#, fuzzy
+msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h"
+msgstr "servidor TFTP no disponible: fijar HAVE_TFTP en src/config.h"
+
+#: dnsmasq.c:194
 #, fuzzy
 msgid "asychronous logging is not available under Solaris"
-msgstr "bitacoreo asincrónico no está disponible bajo Solaris"
+msgstr "registro asíncrono no está disponible bajo Solaris"
 
-#: dnsmasq.c:158
+#: dnsmasq.c:199
 #, fuzzy
 msgid "asychronous logging is not available under Android"
-msgstr "bitacoreo asincrónico no está disponible bajo Solaris"
+msgstr "registro asíncrono no está disponible bajo Solaris"
+
+#: dnsmasq.c:204
+#, fuzzy
+msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h"
+msgstr "DBus no disponible: fijar HAVE_DBUS en src/config.h"
 
-#: dnsmasq.c:177
+#: dnsmasq.c:209
+#, fuzzy
+msgid "loop detection not available: set HAVE_LOOP in src/config.h"
+msgstr "servidor TFTP no disponible: fijar HAVE_TFTP en src/config.h"
+
+#: dnsmasq.c:217
+msgid "zone serial must be configured in --auth-soa"
+msgstr "zona serie debe ser configurada en --auth-soa"
+
+#: dnsmasq.c:235
+msgid "dhcp-range constructor not available on this platform"
+msgstr "constructor rango dhcp no disponible en esta plataforma"
+
+#: dnsmasq.c:278
+msgid "cannot set --bind-interfaces and --bind-dynamic"
+msgstr "no puede usar --bind-interfases y --bind-dynamic"
+
+#: dnsmasq.c:281
 #, c-format
 msgid "failed to find list of interfaces: %s"
-msgstr "no se pudo encontrar lista de interfaces: %s"
+msgstr "no se pudo encontrar lista de interfases: %s"
 
-#: dnsmasq.c:185
+#: dnsmasq.c:290
 #, c-format
 msgid "unknown interface %s"
-msgstr "interface desconocida %s"
+msgstr "interfase desconocida %s"
 
-#: dnsmasq.c:191
-#, c-format
-msgid "no interface with address %s"
-msgstr "ninguna interface con dirección %s"
-
-#: dnsmasq.c:207 dnsmasq.c:678
+#: dnsmasq.c:354 dnsmasq.c:997
 #, c-format
 msgid "DBus error: %s"
 msgstr "error DBus: %s"
 
-#: dnsmasq.c:210
+#: dnsmasq.c:357
 msgid "DBus not available: set HAVE_DBUS in src/config.h"
 msgstr "DBus no disponible: fijar HAVE_DBUS en src/config.h"
 
-#: dnsmasq.c:236
+#: dnsmasq.c:385
 #, c-format
 msgid "unknown user or group: %s"
 msgstr "usuario o grupo desconocido: %s"
 
-#: dnsmasq.c:291
+#: dnsmasq.c:440
 #, c-format
 msgid "cannot chdir to filesystem root: %s"
 msgstr "no se puede cambiar directorio a raíz de sistema de archivos: %s"
 
-#: dnsmasq.c:455
+#: dnsmasq.c:692
 #, fuzzy, c-format
 msgid "started, version %s DNS disabled"
 msgstr "iniciado, versión %s DNS deshabilitado"
 
-#: dnsmasq.c:457
+#: dnsmasq.c:694
 #, c-format
 msgid "started, version %s cachesize %d"
 msgstr "iniciado, versión %s tamaño de caché %d"
 
-#: dnsmasq.c:459
+#: dnsmasq.c:696
 #, c-format
 msgid "started, version %s cache disabled"
 msgstr "iniciado, versión %s caché deshabilitado"
 
-#: dnsmasq.c:461
+#: dnsmasq.c:698
 #, c-format
 msgid "compile time options: %s"
 msgstr "opciones de compilación: %s"
 
-#: dnsmasq.c:467
+#: dnsmasq.c:704
 msgid "DBus support enabled: connected to system bus"
 msgstr "soporte DBus habilitado: conectado a bus de sistema"
 
-#: dnsmasq.c:469
+#: dnsmasq.c:706
 msgid "DBus support enabled: bus connection pending"
-msgstr "soporte DBus habilitado: conección a bus pendiente"
+msgstr "soporte DBus habilitado: conexión a bus pendiente"
+
+#: dnsmasq.c:711
+msgid "DNS service limited to local subnets"
+msgstr ""
+
+#: dnsmasq.c:727
+msgid "DNSSEC validation enabled"
+msgstr ""
+
+#: dnsmasq.c:730
+msgid "DNSSEC signature timestamps not checked until first cache reload"
+msgstr ""
+
+#: dnsmasq.c:733
+msgid "DNSSEC signature timestamps not checked until system time valid"
+msgstr ""
 
-#: dnsmasq.c:474
+#: dnsmasq.c:738
 #, fuzzy, c-format
 msgid "warning: failed to change owner of %s: %s"
-msgstr "advertencia: no se pudo cambiar dueño de %s: %s"
+msgstr "advertencia: no se pudo cambiar propietario de %s: %s"
 
-#: dnsmasq.c:478
+#: dnsmasq.c:742
 msgid "setting --bind-interfaces option because of OS limitations"
-msgstr "fijando opción --bind-interfaces debido a limitaciones de sistema operativo"
+msgstr "fijando opción --bind-interfases debido a limitaciones de sistema operativo"
 
-#: dnsmasq.c:483
+#: dnsmasq.c:752
 #, c-format
 msgid "warning: interface %s does not currently exist"
-msgstr "advertencia: interface %s no existe actuálmente"
+msgstr "advertencia: interfase %s no existe actualmente"
 
-#: dnsmasq.c:488
+#: dnsmasq.c:757
 msgid "warning: ignoring resolv-file flag because no-resolv is set"
 msgstr "advertencia: ignorando opción resolv-file porque no-resolv está fijado"
 
-#: dnsmasq.c:491
+#: dnsmasq.c:760
 #, fuzzy
 msgid "warning: no upstream servers configured"
-msgstr "advertencia: ningún servidor upstream configurado"
+msgstr "advertencia: ningún servidor de subida configurado"
 
-#: dnsmasq.c:495
+#: dnsmasq.c:764
 #, c-format
 msgid "asynchronous logging enabled, queue limit is %d messages"
-msgstr "bitacoreo asincrónico habilitado, límite de cola es %d mensajes"
-
-#: dnsmasq.c:508
-#, c-format
-msgid "DHCP, static leases only on %.0s%s, lease time %s"
-msgstr "DHCP, arriendos estáticos solo en %.0s%s, tiempo de arriendo %s"
+msgstr "registro asíncrono habilitado, el límite de la cola es %d mensajes"
 
-#: dnsmasq.c:510
-#, c-format
-msgid "DHCP, proxy on subnet %.0s%s%.0s"
-msgstr "DHCP, proxy en subred %.0s%s%.0s"
+#: dnsmasq.c:785
+msgid "IPv6 router advertisement enabled"
+msgstr "Anuncio de router IPv6 habilitado"
 
-#: dnsmasq.c:511
+#: dnsmasq.c:790
 #, c-format
-msgid "DHCP, IP range %s -- %s, lease time %s"
-msgstr "DHCP, rango de IPs %s -- %s, tiempo de arriendo %s"
+msgid "DHCP, sockets bound exclusively to interface %s"
+msgstr ""
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 msgid "root is "
-msgstr "root es "
+msgstr "root es "
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 #, fuzzy
 msgid "enabled"
 msgstr "habilitado"
 
-#: dnsmasq.c:528
+#: dnsmasq.c:806
 msgid "secure mode"
 msgstr "modo seguro"
 
-#: dnsmasq.c:554
+#: dnsmasq.c:809
+#, c-format
+msgid "warning: %s inaccessible"
+msgstr ""
+
+#: dnsmasq.c:813
+#, fuzzy, c-format
+msgid "warning: TFTP directory %s inaccessible"
+msgstr "directorio TFTP % inaccesible: %s"
+
+#: dnsmasq.c:839
 #, c-format
 msgid "restricting maximum simultaneous TFTP transfers to %d"
 msgstr "limitando número máximo de transferencias TFTP simultáneas a %d"
 
-#: dnsmasq.c:680
+#: dnsmasq.c:999
 msgid "connected to system DBus"
 msgstr "conectado a DBus de sistema"
 
-#: dnsmasq.c:775
+#: dnsmasq.c:1149
 #, c-format
 msgid "cannot fork into background: %s"
-msgstr "no se puede hacer fork hacia el fondo: %s"
+msgstr "no se puede hacer fork en background: %s"
 
-#: dnsmasq.c:778
+#: dnsmasq.c:1152
 #, fuzzy, c-format
 msgid "failed to create helper: %s"
 msgstr "no se pudo crear ayudante: %s"
 
-#: dnsmasq.c:781
+#: dnsmasq.c:1155
 #, fuzzy, c-format
 msgid "setting capabilities failed: %s"
 msgstr "configuración de capacidades ha fallado: %s"
 
-#: dnsmasq.c:785
+#: dnsmasq.c:1158
 #, fuzzy, c-format
 msgid "failed to change user-id to %s: %s"
 msgstr "no se pudo cambiar user-id a %s: %s"
 
-#: dnsmasq.c:790
+#: dnsmasq.c:1161
 #, fuzzy, c-format
 msgid "failed to change group-id to %s: %s"
 msgstr "no se pudo cambiar group-id a %s: %s"
 
-#: dnsmasq.c:793
+#: dnsmasq.c:1164
 #, fuzzy, c-format
 msgid "failed to open pidfile %s: %s"
 msgstr "no se pudo abrir archivo PID %s: %s"
 
-#: dnsmasq.c:796
+#: dnsmasq.c:1167
 #, fuzzy, c-format
-msgid "cannot open %s: %s"
-msgstr "no se puede abrir %s: %s"
+msgid "cannot open log %s: %s"
+msgstr "no se puede abrir registro %s: %s"
 
-#: dnsmasq.c:851
-#, c-format
-msgid "child process killed by signal %d"
-msgstr "proceso hijo eliminado por señal %d"
+#: dnsmasq.c:1170
+#, fuzzy, c-format
+msgid "failed to load Lua script: %s"
+msgstr "no se pudo cargar script Lua %s: %s"
 
-#: dnsmasq.c:855
+#: dnsmasq.c:1173
 #, c-format
-msgid "child process exited with status %d"
-msgstr "proceso hijo hizo exit con estado %d"
+msgid "TFTP directory %s inaccessible: %s"
+msgstr "directorio TFTP % inaccesible: %s"
+
+#: dnsmasq.c:1176
+#, fuzzy, c-format
+msgid "cannot create timestamp file %s: %s"
+msgstr "no se puede abrir o crear archivo de concesión %s: %s"
+
+#: dnsmasq.c:1197
+msgid "now checking DNSSEC signature timestamps"
+msgstr ""
+
+#: dnsmasq.c:1264
+#, fuzzy, c-format
+msgid "script process killed by signal %d"
+msgstr "proceso script eliminado por señal %d"
+
+#: dnsmasq.c:1268
+#, fuzzy, c-format
+msgid "script process exited with status %d"
+msgstr "proceso script salió con con estado %d"
 
-#: dnsmasq.c:859
+#: dnsmasq.c:1272
 #, fuzzy, c-format
 msgid "failed to execute %s: %s"
 msgstr "no se pudo ejecutar %s: %s"
 
-#: dnsmasq.c:903
+#: dnsmasq.c:1327
 msgid "exiting on receipt of SIGTERM"
 msgstr "saliendo al recibir SIGTERM"
 
-#: dnsmasq.c:931
+#: dnsmasq.c:1355
 #, fuzzy, c-format
 msgid "failed to access %s: %s"
-msgstr "no se pudo accesar %s: %s"
+msgstr "no se pudo acceder %s: %s"
 
-#: dnsmasq.c:961
+#: dnsmasq.c:1385
 #, c-format
 msgid "reading %s"
 msgstr "leyendo %s"
 
-#: dnsmasq.c:972
+#: dnsmasq.c:1396
 #, fuzzy, c-format
 msgid "no servers found in %s, will retry"
 msgstr "ningún servidor encontrado en %s, se reintentará"
 
-#: dhcp.c:40
+#: dhcp.c:53
 #, c-format
 msgid "cannot create DHCP socket: %s"
-msgstr "no se puede crear socket DHCP: %s"
+msgstr "no se puede crear zócalo DHCP: %s"
 
-#: dhcp.c:52
+#: dhcp.c:68
 #, c-format
 msgid "failed to set options on DHCP socket: %s"
-msgstr "no se pudo fijar opciones en socket DHCP: %s"
+msgstr "no se pudo fijar opciones en zócalo DHCP: %s"
 
-#: dhcp.c:65
+#: dhcp.c:89
 #, fuzzy, c-format
 msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"
-msgstr "no se pudo fijar SO_REUSE{ADDR|PORT} en socket DHCP: %s"
+msgstr "no se pudo fijar SO_REUSE{ADDR|PORT} en zócalo DHCP: %s"
 
-#: dhcp.c:77
+#: dhcp.c:101
 #, c-format
 msgid "failed to bind DHCP server socket: %s"
-msgstr "no se pudo acoplar socket de servidor DHCP: %s"
+msgstr "no se pudo acoplar zócalo de servidor DHCP: %s"
 
-#: dhcp.c:103
+#: dhcp.c:127
 #, c-format
 msgid "cannot create ICMP raw socket: %s."
-msgstr "no se puede crear socket crudo ICMP: %s."
+msgstr "no se puede crear zócalo puro ICMP: %s."
+
+#: dhcp.c:241 dhcp6.c:180
+#, fuzzy, c-format
+msgid "unknown interface %s in bridge-interface"
+msgstr "interfase desconocida %s en bridge-interface"
 
 #: dhcp.c:281
 #, c-format
 msgid "DHCP packet received on %s which has no address"
 msgstr "Paquete DHCP recibido en %s que no tiene dirección"
 
-#: dhcp.c:445
+#: dhcp.c:415
+#, c-format
+msgid "ARP-cache injection failed: %s"
+msgstr ""
+
+#: dhcp.c:514
 #, c-format
 msgid "DHCP range %s -- %s is not consistent with netmask %s"
 msgstr "rango DHCP %s -- %s no coincide con máscara de subred %s"
 
-#: dhcp.c:852
+#: dhcp.c:815
 #, fuzzy, c-format
 msgid "bad line at %s line %d"
 msgstr "línea errónea en %s línea %d"
 
-#: dhcp.c:895
+#: dhcp.c:858
 #, c-format
 msgid "ignoring %s line %d, duplicate name or IP address"
 msgstr "ignorando %s línea %d, nombre o dirección IP duplicada"
 
-#: dhcp.c:978
-#, c-format
-msgid "duplicate IP address %s in dhcp-config directive."
-msgstr "dirección IP duplicada %s en directiva dhcp-config."
-
-#: dhcp.c:981
-#, fuzzy, c-format
-msgid "duplicate IP address %s in %s."
-msgstr "dirección IP duplicada %s en %s."
-
-#: dhcp.c:1024
-#, c-format
-msgid "%s has more than one address in hostsfile, using %s for DHCP"
-msgstr "%s tiene más de una dirección en hostsfile, usando %s para DHCP"
-
-#: dhcp.c:1029
+#: dhcp.c:1002 rfc3315.c:2135
 #, c-format
-msgid "duplicate IP address %s (%s) in dhcp-config directive"
-msgstr "dirección IP duplicada %s (%s) en directiva dhcp-config"
+msgid "DHCP relay %s -> %s"
+msgstr "DHCP relay %s -> %s"
 
-#: lease.c:67
+#: lease.c:61
 #, fuzzy, c-format
 msgid "cannot open or create lease file %s: %s"
-msgstr "no se puede abrir o crear archivo de arriendos %s: %s"
+msgstr "no se puede abrir o crear archivo de concesión %s: %s"
 
-#: lease.c:93
+#: lease.c:134
 msgid "too many stored leases"
-msgstr "demasiados arriendos almacenados"
+msgstr "demasiadas concesiones almacenadas"
 
-#: lease.c:129
+#: lease.c:165
 #, fuzzy, c-format
 msgid "cannot run lease-init script %s: %s"
-msgstr "no se puede ejecutar archivo guión lease-init %s: %s"
+msgstr "no se puede ejecutar archivo script lease-init %s: %s"
 
-#: lease.c:135
+#: lease.c:171
 #, c-format
 msgid "lease-init script returned exit code %s"
-msgstr "archivo guión lease-init retornó exit code %s"
+msgstr "archivo guión lease-init retornó código de salida %s"
 
-#: lease.c:235
+#: lease.c:342
 #, fuzzy, c-format
 msgid "failed to write %s: %s (retry in %us)"
 msgstr "error al escribir %s: %s (reintentar en %us)"
 
-#: rfc2131.c:315
+#: lease.c:906
+#, c-format
+msgid "Ignoring domain %s for DHCP host name %s"
+msgstr "Ignorando dominio %s para nombre de host DHCP %s"
+
+#: rfc2131.c:344
 #, c-format
 msgid "no address range available for DHCP request %s %s"
 msgstr "ningún rango de direcciónes disponible para pedido DHCP %s %s"
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "with subnet selector"
 msgstr "con selector de subred"
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "via"
 msgstr "vía"
 
-#: rfc2131.c:331
+#: rfc2131.c:357
 #, fuzzy, c-format
 msgid "%u available DHCP subnet: %s/%s"
 msgstr "%u Subred DHCP disponible: %s/%s"
 
-#: rfc2131.c:334
+#: rfc2131.c:360 rfc3315.c:300
 #, fuzzy, c-format
 msgid "%u available DHCP range: %s -- %s"
 msgstr "%u Rango DHCP disponible: %s -- %s"
 
-#: rfc2131.c:363
+#: rfc2131.c:471
+#, fuzzy, c-format
+msgid "%u vendor class: %s"
+msgstr "%u Clase de vendedor: %s"
+
+#: rfc2131.c:473
+#, fuzzy, c-format
+msgid "%u user class: %s"
+msgstr "%u Clase de usuario: %s"
+
+#: rfc2131.c:500
 msgid "disabled"
 msgstr "deshabilitado"
 
-#: rfc2131.c:404 rfc2131.c:916 rfc2131.c:1288
+#: rfc2131.c:541 rfc2131.c:974 rfc2131.c:1380 rfc3315.c:603 rfc3315.c:856
+#: rfc3315.c:1135
 msgid "ignored"
 msgstr "ignorado"
 
-#: rfc2131.c:419 rfc2131.c:1135
+#: rfc2131.c:556 rfc2131.c:1207 rfc3315.c:906
 msgid "address in use"
 msgstr "dirección en uso"
 
-#: rfc2131.c:433 rfc2131.c:970
+#: rfc2131.c:570 rfc2131.c:1028
 msgid "no address available"
 msgstr "ninguna dirección disponible"
 
-#: rfc2131.c:440 rfc2131.c:1098
+#: rfc2131.c:577 rfc2131.c:1170
 msgid "wrong network"
 msgstr "red equivocada"
 
-#: rfc2131.c:454
+#: rfc2131.c:592
 msgid "no address configured"
 msgstr "ninguna dirección configurada"
 
-#: rfc2131.c:460 rfc2131.c:1148
+#: rfc2131.c:598 rfc2131.c:1220
 msgid "no leases left"
-msgstr "no sobra ningún arriendo"
+msgstr "no sobra ninguna concesión"
 
-#: rfc2131.c:545
+#: rfc2131.c:693 rfc3315.c:476
 #, fuzzy, c-format
 msgid "%u client provides name: %s"
 msgstr "%u cliente provee nombre: %s"
 
-#: rfc2131.c:700
-#, fuzzy, c-format
-msgid "%u vendor class: %s"
-msgstr "%u Clase de vendedor: %s"
-
-#: rfc2131.c:702
-#, fuzzy, c-format
-msgid "%u user class: %s"
-msgstr "%u Clase de usuario: %s"
-
-#: rfc2131.c:761
+#: rfc2131.c:798
 msgid "PXE BIS not supported"
 msgstr "no hay soporte para BIS PXE"
 
-#: rfc2131.c:886
+#: rfc2131.c:942 rfc3315.c:1229
 #, fuzzy, c-format
 msgid "disabling DHCP static address %s for %s"
 msgstr "deshabilitando dirección DHCP estática %s para %s"
 
-#: rfc2131.c:907
+#: rfc2131.c:963
 msgid "unknown lease"
-msgstr "arriendo desconocido"
+msgstr "concesión desconocida"
 
-#: rfc2131.c:939
+#: rfc2131.c:997
 #, c-format
 msgid "not using configured address %s because it is leased to %s"
-msgstr "no usando dirección configurada %s porque está arrendada a %s"
+msgstr "no usando dirección configurada %s porque está concedida a %s"
 
-#: rfc2131.c:949
+#: rfc2131.c:1007
 #, fuzzy, c-format
 msgid "not using configured address %s because it is in use by the server or relay"
 msgstr "no usando dirección configurada %s porque está en uso por el servidor o relay"
 
-#: rfc2131.c:952
+#: rfc2131.c:1010
 #, fuzzy, c-format
 msgid "not using configured address %s because it was previously declined"
 msgstr "no usando dirección configurada %s porque fué previamente denegada"
 
-#: rfc2131.c:968 rfc2131.c:1141
+#: rfc2131.c:1026 rfc2131.c:1213
 msgid "no unique-id"
 msgstr "ningún unique-id (identificación única)"
 
-#: rfc2131.c:1037
+#: rfc2131.c:1108
 msgid "wrong server-ID"
 msgstr "ID de servidor equivocada"
 
-#: rfc2131.c:1055
+#: rfc2131.c:1127
 msgid "wrong address"
 msgstr "dirección equivocada"
 
-#: rfc2131.c:1073
+#: rfc2131.c:1145 rfc3315.c:1002
 msgid "lease not found"
-msgstr "arriendo no encontrado"
+msgstr "concesión no encontrada"
 
-#: rfc2131.c:1106
+#: rfc2131.c:1178
 msgid "address not available"
 msgstr "dirección no disponible"
 
-#: rfc2131.c:1117
+#: rfc2131.c:1189
 msgid "static lease available"
-msgstr "arriendo estático disponible"
+msgstr "concesión estática disponible"
 
-#: rfc2131.c:1121
+#: rfc2131.c:1193
 msgid "address reserved"
 msgstr "dirección reservada"
 
-#: rfc2131.c:1129
+#: rfc2131.c:1201
 #, c-format
 msgid "abandoning lease to %s of %s"
-msgstr "abandonando arriendo a %s de %s"
-
-#: rfc2131.c:1710
-#, c-format
-msgid "%u tags: %s"
-msgstr "%u etiquetas: %s"
+msgstr "abandonando concesión a %s de %s"
 
-#: rfc2131.c:1723
+#: rfc2131.c:1707
 #, c-format
 msgid "%u bootfile name: %s"
 msgstr "%u nombre de bootfile: %s"
 
-#: rfc2131.c:1732
+#: rfc2131.c:1716
 #, c-format
 msgid "%u server name: %s"
 msgstr "%u nombre de servidor: %s"
 
-#: rfc2131.c:1746
+#: rfc2131.c:1724
 #, fuzzy, c-format
 msgid "%u next server: %s"
 msgstr "%u siguiente servidor: %s"
 
-#: rfc2131.c:1749
+#: rfc2131.c:1727
 #, c-format
 msgid "%u broadcast response"
-msgstr ""
+msgstr "%u respuesta broadcast"
 
-#: rfc2131.c:1812
+#: rfc2131.c:1790
 #, fuzzy, c-format
 msgid "cannot send DHCP/BOOTP option %d: no space left in packet"
-msgstr "no se puede enviar opción DHCP/BOOTP %d: no queda espacio en paquete"
+msgstr "no se puede enviar opción DHCP/BOOTP %d: no queda espacio en el paquete"
 
-#: rfc2131.c:2058
+#: rfc2131.c:2031
 msgid "PXE menu too large"
-msgstr "menú PXE demasiado grande"
-
-#: rfc2131.c:2171
-#, c-format
-msgid "Ignoring domain %s for DHCP host name %s"
-msgstr "Ignorando dominio %s para nombre de host DHCP %s"
+msgstr "menú PXE demasiado largo"
 
-#: rfc2131.c:2189
+#: rfc2131.c:2170 rfc3315.c:1502
 #, fuzzy, c-format
 msgid "%u requested options: %s"
 msgstr "%u opciones solicitadas: %s"
 
-#: rfc2131.c:2456
+#: rfc2131.c:2487
 #, c-format
 msgid "cannot send RFC3925 option: too many options for enterprise number %d"
 msgstr "no se puede enviar opción RFC3925: demasiadas opciones para número enterprise %d"
 
-#: netlink.c:70
+#: netlink.c:77
 #, fuzzy, c-format
 msgid "cannot create netlink socket: %s"
-msgstr "no se puede crear socket netlink: %s"
+msgstr "no se puede crear zócalo netlink: %s"
 
-#: netlink.c:288
+#: netlink.c:348
 #, fuzzy, c-format
 msgid "netlink returns error: %s"
 msgstr "netlink retorna error: %s"
 
-#: dbus.c:150
+#: dbus.c:186
 msgid "attempt to set an IPv6 server address via DBus - no IPv6 support"
 msgstr "intento de fijar dirección de servidor IPv6 vía DBus - no hay soporte IPv6"
 
-#: dbus.c:286
+#: dbus.c:439
+#, c-format
+msgid "Enabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:444
+#, c-format
+msgid "Disabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:691
 msgid "setting upstream servers from DBus"
-msgstr "fijando servidores upstream desde DBus"
+msgstr "fijando servidores subida desde DBus"
 
-#: dbus.c:324
+#: dbus.c:738
 msgid "could not register a DBus message handler"
 msgstr "no se pudo registrar un manejador de mensajes DBus"
 
-#: bpf.c:217
+#: bpf.c:263
 #, c-format
 msgid "cannot create DHCP BPF socket: %s"
-msgstr "no se puede crear socket BPF DHCP: %s"
+msgstr "no se puede crear zócalo BPF DHCP: %s"
 
-#: bpf.c:245
+#: bpf.c:291
 #, fuzzy, c-format
 msgid "DHCP request for unsupported hardware type (%d) received on %s"
 msgstr "pedido DHCP por tipo de hardware no-soportado (%d) recibido en %s"
 
-#: tftp.c:281
+#: bpf.c:376
+#, fuzzy, c-format
+msgid "cannot create PF_ROUTE socket: %s"
+msgstr "no se puede crear zócalo DHCP: %s"
+
+#: bpf.c:397
+msgid "Unknown protocol version from route socket"
+msgstr ""
+
+#: helper.c:153
+msgid "lease() function missing in Lua script"
+msgstr "la función lease() no se encuentra en el script Lua"
+
+#: tftp.c:309
 msgid "unable to get free port for TFTP"
 msgstr "incapaz de conseguir puerto libre para TFTP"
 
-#: tftp.c:296
+#: tftp.c:325
 #, c-format
 msgid "unsupported request from %s"
 msgstr "pedido no-soportado desde %s"
 
-#: tftp.c:406
+#: tftp.c:439
 #, fuzzy, c-format
 msgid "file %s not found"
 msgstr "archivo %s no encontrado"
 
-#: tftp.c:522
+#: tftp.c:548
 #, fuzzy, c-format
 msgid "error %d %s received from %s"
 msgstr "error TFTP %d %s recibido de %s"
 
-#: tftp.c:554
+#: tftp.c:590
 #, fuzzy, c-format
 msgid "failed sending %s to %s"
 msgstr "TFTP no pudo enviar %s a %s"
 
-#: tftp.c:568
+#: tftp.c:590
 #, fuzzy, c-format
 msgid "sent %s to %s"
 msgstr "TFTP envió %s a %s"
 
-#: log.c:177
+#: log.c:190
 #, c-format
 msgid "overflow: %d log entries lost"
-msgstr "desbordamiento: %d entradas de bitácora perdidas"
+msgstr "desbordamiento: %d entradas de registro perdidas"
 
-#: log.c:254
+#: log.c:268
 #, c-format
 msgid "log failed: %s"
-msgstr "bitácora falló: %s"
+msgstr "registro falló: %s"
 
-#: log.c:462
+#: log.c:469
 msgid "FAILED to start up"
 msgstr "el inicio ha FALLADO"
 
+#: conntrack.c:65
+#, c-format
+msgid "Conntrack connection mark retrieval failed: %s"
+msgstr "Conexión conntrack con marca recuperación falló"
+
+#: dhcp6.c:59
+#, fuzzy, c-format
+msgid "cannot create DHCPv6 socket: %s"
+msgstr "no se puede crear zócalo DHCP: %s"
+
+#: dhcp6.c:80
+#, fuzzy, c-format
+msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"
+msgstr "no se pudo fijar SO_REUSE{ADDR|PORT} en zócalo DHCP: %s"
+
+#: dhcp6.c:92
+#, fuzzy, c-format
+msgid "failed to bind DHCPv6 server socket: %s"
+msgstr "no se pudo acoplar zócalo de servidor DHCP: %s"
+
+#: rfc3315.c:157
+#, fuzzy, c-format
+msgid "no address range available for DHCPv6 request from relay at %s"
+msgstr "ningún rango de direcciónes disponible para pedido DHCP %s %s"
+
+#: rfc3315.c:166
+#, fuzzy, c-format
+msgid "no address range available for DHCPv6 request via %s"
+msgstr "ningún rango de direcciónes disponible para pedido DHCP %s %s"
+
+#: rfc3315.c:297
+#, fuzzy, c-format
+msgid "%u available DHCPv6 subnet: %s/%d"
+msgstr "%u Subred DHCP disponible: %s/%s"
+
+#: rfc3315.c:380
+#, fuzzy, c-format
+msgid "%u vendor class: %u"
+msgstr "%u Clase de vendedor: %s"
+
+#: rfc3315.c:428
+#, fuzzy, c-format
+msgid "%u client MAC address: %s"
+msgstr "%u cliente provee nombre: %s"
+
+#: rfc3315.c:660
+#, fuzzy, c-format
+msgid "unknown prefix-class %d"
+msgstr "clase de prefijo desconocida"
+
+#: rfc3315.c:803 rfc3315.c:898
+#, fuzzy
+msgid "address unavailable"
+msgstr "dirección no disponible"
+
+#: rfc3315.c:815 rfc3315.c:946 rfc3315.c:1279
+msgid "success"
+msgstr ""
+
+#: rfc3315.c:830 rfc3315.c:839 rfc3315.c:954 rfc3315.c:956
+#, fuzzy
+msgid "no addresses available"
+msgstr "ninguna dirección disponible"
+
+#: rfc3315.c:933
+msgid "not on link"
+msgstr "no en el enlace"
+
+#: rfc3315.c:1006 rfc3315.c:1191 rfc3315.c:1268
+msgid "no binding found"
+msgstr "uniones no encontradas"
+
+#: rfc3315.c:1044
+msgid "deprecated"
+msgstr "descartado"
+
+#: rfc3315.c:1049
+#, fuzzy
+msgid "address invalid"
+msgstr "dirección en uso"
+
+#: rfc3315.c:1096
+msgid "confirm failed"
+msgstr "confirmación falló"
+
+#: rfc3315.c:1112
+#, fuzzy
+msgid "all addresses still on link"
+msgstr "dirección errónea en %s línea %d"
+
+#: rfc3315.c:1200
+msgid "release received"
+msgstr "concesión recibida"
+
+#: rfc3315.c:2126
+msgid "Cannot multicast to DHCPv6 server without correct interface"
+msgstr "No puede hacer multicast DHCPv6 sin el interfase correcto"
+
+#: dhcp-common.c:145
+#, c-format
+msgid "Ignoring duplicate dhcp-option %d"
+msgstr "Ignorando opción DHCP duplicada"
+
+#: dhcp-common.c:222
+#, c-format
+msgid "%u tags: %s"
+msgstr "%u etiquetas: %s"
+
+#: dhcp-common.c:407
+#, c-format
+msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgstr "%s tiene más de una dirección en hostsfile, usando %s para DHCP"
+
+#: dhcp-common.c:430
+#, c-format
+msgid "duplicate IP address %s (%s) in dhcp-config directive"
+msgstr "dirección IP duplicada %s (%s) en directiva dhcp-config"
+
+#: dhcp-common.c:494
+#, fuzzy, c-format
+msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s"
+msgstr "no se pudo fijar SO_REUSE{ADDR|PORT} en socket DHCP: %s"
+
+#: dhcp-common.c:615
+#, c-format
+msgid "Known DHCP options:\n"
+msgstr "Opciones DHCP conocidas:\n"
+
+#: dhcp-common.c:626
+#, fuzzy, c-format
+msgid "Known DHCPv6 options:\n"
+msgstr "Opciones DHCP conocidas:\n"
+
+#: dhcp-common.c:823
+msgid ", prefix deprecated"
+msgstr ", prefijo descartado"
+
+#: dhcp-common.c:826
+#, c-format
+msgid ", lease time "
+msgstr ", tiempo de concesión"
+
+#: dhcp-common.c:868
+#, c-format
+msgid "%s stateless on %s%.0s%.0s%s"
+msgstr "%s apátrida en %s%.0s%.0s%s"
+
+#: dhcp-common.c:870
+#, fuzzy, c-format
+msgid "%s, static leases only on %.0s%s%s%.0s"
+msgstr "DHCP, concesión estática solo en %.0s%s, tiempo de concesión %s"
+
+#: dhcp-common.c:872
+#, fuzzy, c-format
+msgid "%s, proxy on subnet %.0s%s%.0s%.0s"
+msgstr "DHCP, proxy en subred %.0s%s%.0s"
+
+#: dhcp-common.c:873
+#, fuzzy, c-format
+msgid "%s, IP range %s -- %s%s%.0s"
+msgstr "DHCP, rango de IPs %s -- %s, tiempo de concesión %s"
+
+#: dhcp-common.c:886
+#, c-format
+msgid "DHCPv4-derived IPv6 names on %s%s"
+msgstr ""
+
+#: dhcp-common.c:889
+#, fuzzy, c-format
+msgid "router advertisement on %s%s"
+msgstr "DHCP, concesión estáticos solo en %.0s%s, tiempo de concesión %s"
+
+#: dhcp-common.c:900
+#, c-format
+msgid "DHCP relay from %s to %s via %s"
+msgstr ""
+
+#: dhcp-common.c:902
+#, c-format
+msgid "DHCP relay from %s to %s"
+msgstr ""
+
+#: radv.c:109
+#, fuzzy, c-format
+msgid "cannot create ICMPv6 socket: %s"
+msgstr "no se puede crear socket DHCP: %s"
+
+#: auth.c:448
+#, fuzzy, c-format
+msgid "ignoring zone transfer request from %s"
+msgstr "pedido no-soportado desde %s"
+
+#: ipset.c:95
+#, fuzzy, c-format
+msgid "failed to find kernel version: %s"
+msgstr "no se pudo acoplar socket de servidor DHCP: %s"
+
+#: ipset.c:114
+#, fuzzy, c-format
+msgid "failed to create IPset control socket: %s"
+msgstr "no se pudo crear socket TFTP: %s"
+
+#: dnssec.c:449 dnssec.c:493
+#, fuzzy, c-format
+msgid "failed to update mtime on %s: %s"
+msgstr "no se pudo abrir archivo PID %s: %s"
+
+#: blockdata.c:58
+#, c-format
+msgid "DNSSEC memory in use %u, max %u, allocated %u"
+msgstr ""
+
+#: tables.c:80
+msgid "error: fill_addr missused"
+msgstr ""
+
+#: tables.c:109
+#, fuzzy, c-format
+msgid "failed to access pf devices: %s"
+msgstr "no se pudo acceder %s: %s"
+
+#: tables.c:123
+#, fuzzy, c-format
+msgid "warning: no opened pf devices %s"
+msgstr "usando direcciones locales solo para %s %s"
+
+#: tables.c:131
+#, fuzzy, c-format
+msgid "error: cannot use table name %s"
+msgstr "no se puede obtener host-name (nombre de host): %s"
+
+#: tables.c:139
+#, c-format
+msgid "error: cannot strlcpy table name %s"
+msgstr ""
+
+#: tables.c:145
+#, c-format
+msgid "warning: pfr_add_tables: %s(%d)"
+msgstr ""
+
+#: tables.c:151
+msgid "info: table created"
+msgstr ""
+
+#: tables.c:162
+#, c-format
+msgid "warning: DIOCR%sADDRS: %s"
+msgstr ""
+
+#: tables.c:166
+#, fuzzy, c-format
+msgid "%d addresses %s"
+msgstr "dirección IP errónea"
+
+#: inotify.c:59
+#, fuzzy, c-format
+msgid "cannot access path %s: %s"
+msgstr "no se puede acceder %s: %s"
+
+#: inotify.c:92
+#, fuzzy, c-format
+msgid "failed to create inotify: %s"
+msgstr "no se pudo crear ayudante: %s"
+
+#: inotify.c:105
+#, c-format
+msgid "too many symlinks following %s"
+msgstr ""
+
+#: inotify.c:121
+#, c-format
+msgid "directory %s for resolv-file is missing, cannot poll"
+msgstr ""
+
+#: inotify.c:125 inotify.c:162
+#, fuzzy, c-format
+msgid "failed to create inotify for %s: %s"
+msgstr "no se pudo crear un zócalo de escucha: %s"
+
+#: inotify.c:147
+#, fuzzy, c-format
+msgid "bad dynamic directory %s: %s"
+msgstr "no se puede acceder a directorio %s: %s"
+
+#: inotify.c:247
+#, c-format
+msgid "inotify, new or changed file %s"
+msgstr ""
+
+#, fuzzy
+#~ msgid "cannot cannonicalise resolv-file %s: %s"
+#~ msgstr "no se puede abrir o crear archivo de concesión %s: %s"
+
+#~ msgid "no interface with address %s"
+#~ msgstr "ninguna interfase con dirección %s"
+
+#~ msgid "duplicate IP address %s in dhcp-config directive."
+#~ msgstr "dirección IP duplicada %s en directiva dhcp-config."
+
+#, fuzzy
+#~ msgid "Specify path to Lua script (no default)."
+#~ msgstr "Especificar path de archivo PID (%s por predeterminado)."
+
 #, fuzzy
 #~ msgid "only one dhcp-hostsfile allowed"
 #~ msgstr "solo un dhcp-hostsfile permitido"
@@ -1513,28 +2246,14 @@ msgstr "el inicio ha FALLADO"
 #~ msgid "failed to bind listening socket for %s: %s"
 #~ msgstr "no se pudo acoplar socket escuchador para %s: %s"
 
-#~ msgid "failed to listen on socket: %s"
-#~ msgstr "no se pudo escuchar en socket: %s"
-
-#, fuzzy
-#~ msgid "failed to create TFTP socket: %s"
-#~ msgstr "no se pudo crear socket TFTP: %s"
-
 #~ msgid "DHCP packet: transaction-id is %u"
 #~ msgstr "paquete DHCP: transaction-id (identificación de transacción) es %u"
 
 #~ msgid "must set exactly one interface on broken systems without IP_RECVIF"
-#~ msgstr "debe fijarse exáctamente una interface en sistemas rotos sin IP_RECVIF"
-
-#, fuzzy
-#~ msgid "failed to load %s: %s"
-#~ msgstr "no se pudo cargar %s: %s"
-
-#~ msgid "bad name in %s"
-#~ msgstr "nombre erróneo en %s"
+#~ msgstr "debe fijarse exáctamente una interfase en sistemas rotos sin IP_RECVIF"
 
 #~ msgid "Ignoring DHCP lease for %s because it has an illegal domain part"
-#~ msgstr "Ignorando arriendo DHCP para %s porque tiene una parte ilegal de dominio"
+#~ msgstr "Ignorando concesión DHCP para %s porque tiene una parte ilegal de dominio"
 
 #~ msgid "ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h"
 #~ msgstr "integración dhcpd ISC no disponible: fijar HAVE_ISC_READER en src/config.h"
@@ -1550,14 +2269,14 @@ msgstr "el inicio ha FALLADO"
 #~ msgstr "corriendo como root"
 
 #~ msgid "Read leases at startup, but never write the lease file."
-#~ msgstr "Leer arriendos al inicio, pero nunca escribir el archivo de arriendos."
+#~ msgstr "Leer concesión al inicio, pero nunca escribir el archivo de concesión."
 
 #, fuzzy
 #~ msgid "read %s - %d hosts"
 #~ msgstr "direcciónes %s - %d leídas"
 
 #~ msgid "Limit of %d leases exceeded."
-#~ msgstr "Límite de %d arriendos excedido."
+#~ msgstr "Límite de %d concesión excedido."
 
 #~ msgid "domains"
 #~ msgstr "dominios"
index ccd0338..cb4428a 100644 (file)
--- a/po/fi.po
+++ b/po/fi.po
@@ -10,1446 +10,2128 @@ msgstr ""
 "PO-Revision-Date: 2005-11-28 22:05+0000\n"
 "Last-Translator: Simon Kelley <simon@thekelleys.org.uk>\n"
 "Language-Team: Finnish <translation-team-fi@lists.sourceforge.net>\n"
+"Language: fi\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=ASCII\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: cache.c:761
+#: cache.c:523
+msgid "Internal error in cache."
+msgstr ""
+
+#: cache.c:941
 #, c-format
 msgid "failed to load names from %s: %s"
 msgstr ""
 
-#: cache.c:795 dhcp.c:865
+#: cache.c:967 dhcp.c:828
 #, c-format
 msgid "bad address at %s line %d"
 msgstr ""
 
-#: cache.c:853 dhcp.c:881
+#: cache.c:1018 dhcp.c:844
 #, c-format
 msgid "bad name at %s line %d"
 msgstr ""
 
-#: cache.c:860 dhcp.c:956
+#: cache.c:1027 dhcp.c:919
 #, c-format
 msgid "read %s - %d addresses"
 msgstr ""
 
-#: cache.c:899
+#: cache.c:1135
 msgid "cleared cache"
 msgstr ""
 
-#: cache.c:960
+#: cache.c:1164
+#, c-format
+msgid "No IPv4 address found for %s"
+msgstr ""
+
+#: cache.c:1242
 #, c-format
 msgid "%s is a CNAME, not giving it to the DHCP lease of %s"
 msgstr ""
 
-#: cache.c:966
+#: cache.c:1266
 #, c-format
 msgid "not giving name %s to the DHCP lease of %s because the name exists in %s with address %s"
 msgstr ""
 
-#: cache.c:1039
+#: cache.c:1421
 #, c-format
 msgid "time %lu"
 msgstr ""
 
-#: cache.c:1040
+#: cache.c:1422
 #, c-format
 msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries."
 msgstr ""
 
-#: cache.c:1042
+#: cache.c:1424
 #, c-format
 msgid "queries forwarded %u, queries answered locally %u"
 msgstr ""
 
-#: cache.c:1068
+#: cache.c:1427
+#, c-format
+msgid "queries for authoritative zones %u"
+msgstr ""
+
+#: cache.c:1453
 #, c-format
 msgid "server %s#%d: queries sent %u, retried or failed %u"
 msgstr ""
 
-#: util.c:57
+#: util.c:45
 #, c-format
 msgid "failed to seed the random number generator: %s"
 msgstr ""
 
-#: util.c:189
+#: util.c:205
 msgid "failed to allocate memory"
 msgstr ""
 
-#: util.c:227 option.c:573
+#: util.c:250 option.c:601
 msgid "could not get memory"
 msgstr ""
 
-#: util.c:237
+#: util.c:260
 #, c-format
 msgid "cannot create pipe: %s"
 msgstr ""
 
-#: util.c:245
+#: util.c:268
 #, c-format
 msgid "failed to allocate %d bytes"
 msgstr ""
 
-#: util.c:350
+#: util.c:437
 #, c-format
 msgid "infinite"
 msgstr ""
 
-#: option.c:244
+#: option.c:332
 msgid "Specify local address(es) to listen on."
 msgstr ""
 
-#: option.c:245
+#: option.c:333
 msgid "Return ipaddr for all hosts in specified domains."
 msgstr ""
 
-#: option.c:246
+#: option.c:334
 msgid "Fake reverse lookups for RFC1918 private address ranges."
 msgstr ""
 
-#: option.c:247
+#: option.c:335
 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."
 msgstr ""
 
-#: option.c:248
+#: option.c:336
 #, c-format
 msgid "Specify the size of the cache in entries (defaults to %s)."
 msgstr ""
 
-#: option.c:249
+#: option.c:337
 #, c-format
 msgid "Specify configuration file (defaults to %s)."
 msgstr ""
 
-#: option.c:250
+#: option.c:338
 msgid "Do NOT fork into the background: run in debug mode."
 msgstr ""
 
-#: option.c:251
+#: option.c:339
 msgid "Do NOT forward queries with no domain part."
 msgstr ""
 
-#: option.c:252
+#: option.c:340
 msgid "Return self-pointing MX records for local hosts."
 msgstr ""
 
-#: option.c:253
+#: option.c:341
 msgid "Expand simple names in /etc/hosts with domain-suffix."
 msgstr ""
 
-#: option.c:254
+#: option.c:342
 msgid "Don't forward spurious DNS requests from Windows hosts."
 msgstr ""
 
-#: option.c:255
+#: option.c:343
 msgid "Enable DHCP in the range given with lease duration."
 msgstr ""
 
-#: option.c:256
+#: option.c:344
 #, c-format
 msgid "Change to this group after startup (defaults to %s)."
 msgstr ""
 
-#: option.c:257
+#: option.c:345
 msgid "Set address or hostname for a specified machine."
 msgstr ""
 
-#: option.c:258
+#: option.c:346
 msgid "Read DHCP host specs from file."
 msgstr ""
 
-#: option.c:259
+#: option.c:347
 msgid "Read DHCP option specs from file."
 msgstr ""
 
-#: option.c:260
+#: option.c:348
+msgid "Read DHCP host specs from a directory."
+msgstr ""
+
+#: option.c:349
+msgid "Read DHCP options from a directory."
+msgstr ""
+
+#: option.c:350
 msgid "Evaluate conditional tag expression."
 msgstr ""
 
-#: option.c:261
+#: option.c:351
 #, c-format
 msgid "Do NOT load %s file."
 msgstr ""
 
-#: option.c:262
+#: option.c:352
 #, c-format
 msgid "Specify a hosts file to be read in addition to %s."
 msgstr ""
 
-#: option.c:263
+#: option.c:353
+msgid "Read hosts files from a directory."
+msgstr ""
+
+#: option.c:354
 msgid "Specify interface(s) to listen on."
 msgstr ""
 
-#: option.c:264
+#: option.c:355
 msgid "Specify interface(s) NOT to listen on."
 msgstr ""
 
-#: option.c:265
+#: option.c:356
 msgid "Map DHCP user class to tag."
 msgstr ""
 
-#: option.c:266
+#: option.c:357
 msgid "Map RFC3046 circuit-id to tag."
 msgstr ""
 
-#: option.c:267
+#: option.c:358
 msgid "Map RFC3046 remote-id to tag."
 msgstr ""
 
-#: option.c:268
+#: option.c:359
 msgid "Map RFC3993 subscriber-id to tag."
 msgstr ""
 
-#: option.c:269
+#: option.c:360
 msgid "Don't do DHCP for hosts with tag set."
 msgstr ""
 
-#: option.c:270
+#: option.c:361
 msgid "Force broadcast replies for hosts with tag set."
 msgstr ""
 
-#: option.c:271
+#: option.c:362
 msgid "Do NOT fork into the background, do NOT run in debug mode."
 msgstr ""
 
-#: option.c:272
+#: option.c:363
 msgid "Assume we are the only DHCP server on the local network."
 msgstr ""
 
-#: option.c:273
+#: option.c:364
 #, c-format
 msgid "Specify where to store DHCP leases (defaults to %s)."
 msgstr ""
 
-#: option.c:274
+#: option.c:365
 msgid "Return MX records for local hosts."
 msgstr ""
 
-#: option.c:275
+#: option.c:366
 msgid "Specify an MX record."
 msgstr ""
 
-#: option.c:276
+#: option.c:367
 msgid "Specify BOOTP options to DHCP server."
 msgstr ""
 
-#: option.c:277
+#: option.c:368
 #, c-format
 msgid "Do NOT poll %s file, reload only on SIGHUP."
 msgstr ""
 
-#: option.c:278
+#: option.c:369
 msgid "Do NOT cache failed search results."
 msgstr ""
 
-#: option.c:279
+#: option.c:370
 #, c-format
 msgid "Use nameservers strictly in the order given in %s."
 msgstr ""
 
-#: option.c:280
+#: option.c:371
 msgid "Specify options to be sent to DHCP clients."
 msgstr ""
 
-#: option.c:281
+#: option.c:372
 msgid "DHCP option sent even if the client does not request it."
 msgstr ""
 
-#: option.c:282
+#: option.c:373
 msgid "Specify port to listen for DNS requests on (defaults to 53)."
 msgstr ""
 
-#: option.c:283
+#: option.c:374
 #, c-format
 msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)."
 msgstr ""
 
-#: option.c:284
+#: option.c:375
 msgid "Log DNS queries."
 msgstr ""
 
-#: option.c:285
+#: option.c:376
 msgid "Force the originating port for upstream DNS queries."
 msgstr ""
 
-#: option.c:286
+#: option.c:377
 msgid "Do NOT read resolv.conf."
 msgstr ""
 
-#: option.c:287
+#: option.c:378
 #, c-format
 msgid "Specify path to resolv.conf (defaults to %s)."
 msgstr ""
 
-#: option.c:288
+#: option.c:379
+msgid "Specify path to file with server= options"
+msgstr ""
+
+#: option.c:380
 msgid "Specify address(es) of upstream servers with optional domains."
 msgstr ""
 
-#: option.c:289
+#: option.c:381
+msgid "Specify address of upstream servers for reverse address queries"
+msgstr ""
+
+#: option.c:382
 msgid "Never forward queries to specified domains."
 msgstr ""
 
-#: option.c:290
+#: option.c:383
 msgid "Specify the domain to be assigned in DHCP leases."
 msgstr ""
 
-#: option.c:291
+#: option.c:384
 msgid "Specify default target in an MX record."
 msgstr ""
 
-#: option.c:292
+#: option.c:385
 msgid "Specify time-to-live in seconds for replies from /etc/hosts."
 msgstr ""
 
-#: option.c:293
+#: option.c:386
 msgid "Specify time-to-live in seconds for negative caching."
 msgstr ""
 
-#: option.c:294
+#: option.c:387
 msgid "Specify time-to-live in seconds for maximum TTL to send to clients."
 msgstr ""
 
-#: option.c:295
+#: option.c:388
+msgid "Specify time-to-live ceiling for cache."
+msgstr ""
+
+#: option.c:389
+msgid "Specify time-to-live floor for cache."
+msgstr ""
+
+#: option.c:390
 #, c-format
 msgid "Change to this user after startup. (defaults to %s)."
 msgstr ""
 
-#: option.c:296
+#: option.c:391
 msgid "Map DHCP vendor class to tag."
 msgstr ""
 
-#: option.c:297
+#: option.c:392
 msgid "Display dnsmasq version and copyright information."
 msgstr ""
 
-#: option.c:298
+#: option.c:393
 msgid "Translate IPv4 addresses from upstream servers."
 msgstr ""
 
-#: option.c:299
+#: option.c:394
 msgid "Specify a SRV record."
 msgstr ""
 
-#: option.c:300
+#: option.c:395
 msgid "Display this message. Use --help dhcp for known DHCP options."
 msgstr ""
 
-#: option.c:301
+#: option.c:396
 #, c-format
 msgid "Specify path of PID file (defaults to %s)."
 msgstr ""
 
-#: option.c:302
+#: option.c:397
 #, c-format
 msgid "Specify maximum number of DHCP leases (defaults to %s)."
 msgstr ""
 
-#: option.c:303
+#: option.c:398
 msgid "Answer DNS queries based on the interface a query was sent to."
 msgstr ""
 
-#: option.c:304
+#: option.c:399
 msgid "Specify TXT DNS record."
 msgstr ""
 
-#: option.c:305
+#: option.c:400
 msgid "Specify PTR DNS record."
 msgstr ""
 
-#: option.c:306
+#: option.c:401
 msgid "Give DNS name to IPv4 address of interface."
 msgstr ""
 
-#: option.c:307
+#: option.c:402
 msgid "Bind only to interfaces in use."
 msgstr ""
 
-#: option.c:308
+#: option.c:403
 #, c-format
 msgid "Read DHCP static host information from %s."
 msgstr ""
 
-#: option.c:309
+#: option.c:404
 msgid "Enable the DBus interface for setting upstream servers, etc."
 msgstr ""
 
-#: option.c:310
+#: option.c:405
 msgid "Do not provide DHCP on this interface, only provide DNS."
 msgstr ""
 
-#: option.c:311
+#: option.c:406
 msgid "Enable dynamic address allocation for bootp."
 msgstr ""
 
-#: option.c:312
+#: option.c:407
 msgid "Map MAC address (with wildcards) to option set."
 msgstr ""
 
-#: option.c:313
+#: option.c:408
 msgid "Treat DHCP requests on aliases as arriving from interface."
 msgstr ""
 
-#: option.c:314
+#: option.c:409
 msgid "Disable ICMP echo address checking in the DHCP server."
 msgstr ""
 
-#: option.c:315
-msgid "Script to run on DHCP lease creation and destruction."
+#: option.c:410
+msgid "Shell script to run on DHCP lease creation and destruction."
 msgstr ""
 
-#: option.c:316
+#: option.c:411
+msgid "Lua script to run on DHCP lease creation and destruction."
+msgstr ""
+
+#: option.c:412
+msgid "Run lease-change scripts as this user."
+msgstr ""
+
+#: option.c:413
 msgid "Read configuration from all the files in this directory."
 msgstr ""
 
-#: option.c:317
+#: option.c:414
 msgid "Log to this syslog facility or file. (defaults to DAEMON)"
 msgstr ""
 
-#: option.c:318
+#: option.c:415
 msgid "Do not use leasefile."
 msgstr ""
 
-#: option.c:319
+#: option.c:416
 #, c-format
 msgid "Maximum number of concurrent DNS queries. (defaults to %s)"
 msgstr ""
 
-#: option.c:320
+#: option.c:417
 #, c-format
 msgid "Clear DNS cache when reloading %s."
 msgstr ""
 
-#: option.c:321
+#: option.c:418
 msgid "Ignore hostnames provided by DHCP clients."
 msgstr ""
 
-#: option.c:322
+#: option.c:419
 msgid "Do NOT reuse filename and server fields for extra DHCP options."
 msgstr ""
 
-#: option.c:323
+#: option.c:420
 msgid "Enable integrated read-only TFTP server."
 msgstr ""
 
-#: option.c:324
+#: option.c:421
 msgid "Export files by TFTP only from the specified subtree."
 msgstr ""
 
-#: option.c:325
+#: option.c:422
 msgid "Add client IP address to tftp-root."
 msgstr ""
 
-#: option.c:326
+#: option.c:423
 msgid "Allow access only to files owned by the user running dnsmasq."
 msgstr ""
 
-#: option.c:327
+#: option.c:424
+msgid "Do not terminate the service if TFTP directories are inaccessible."
+msgstr ""
+
+#: option.c:425
 #, c-format
 msgid "Maximum number of conncurrent TFTP transfers (defaults to %s)."
 msgstr ""
 
-#: option.c:328
+#: option.c:426
 msgid "Disable the TFTP blocksize extension."
 msgstr ""
 
-#: option.c:329
+#: option.c:427
+msgid "Convert TFTP filenames to lowercase"
+msgstr ""
+
+#: option.c:428
 msgid "Ephemeral port range for use by TFTP transfers."
 msgstr ""
 
-#: option.c:330
+#: option.c:429
 msgid "Extra logging for DHCP."
 msgstr ""
 
-#: option.c:331
+#: option.c:430
 msgid "Enable async. logging; optionally set queue length."
 msgstr ""
 
-#: option.c:332
+#: option.c:431
 msgid "Stop DNS rebinding. Filter private IP ranges when resolving."
 msgstr ""
 
-#: option.c:333
+#: option.c:432
 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers."
 msgstr ""
 
-#: option.c:334
+#: option.c:433
 msgid "Inhibit DNS-rebind protection on this domain."
 msgstr ""
 
-#: option.c:335
+#: option.c:434
 msgid "Always perform DNS queries to all servers."
 msgstr ""
 
-#: option.c:336
+#: option.c:435
 msgid "Set tag if client includes matching option in request."
 msgstr ""
 
-#: option.c:337
+#: option.c:436
 msgid "Use alternative ports for DHCP."
 msgstr ""
 
-#: option.c:338
-msgid "Run lease-change script as this user."
-msgstr ""
-
-#: option.c:339
+#: option.c:437
 msgid "Specify NAPTR DNS record."
 msgstr ""
 
-#: option.c:340
+#: option.c:438
 msgid "Specify lowest port available for DNS query transmission."
 msgstr ""
 
-#: option.c:341
+#: option.c:439
 msgid "Use only fully qualified domain names for DHCP clients."
 msgstr ""
 
-#: option.c:342
+#: option.c:440
 msgid "Generate hostnames based on MAC address for nameless clients."
 msgstr ""
 
-#: option.c:343
+#: option.c:441
 msgid "Use these DHCP relays as full proxies."
 msgstr ""
 
-#: option.c:344
+#: option.c:442
+msgid "Relay DHCP requests to a remote server"
+msgstr ""
+
+#: option.c:443
 msgid "Specify alias name for LOCAL DNS name."
 msgstr ""
 
-#: option.c:345
+#: option.c:444
 msgid "Prompt to send to PXE clients."
 msgstr ""
 
-#: option.c:346
+#: option.c:445
 msgid "Boot service for PXE menu."
 msgstr ""
 
-#: option.c:347
+#: option.c:446
 msgid "Check configuration syntax."
 msgstr ""
 
-#: option.c:348
-msgid "Add requestor's MAC address to forwarded DNS queries"
+#: option.c:447
+msgid "Add requestor's MAC address to forwarded DNS queries."
 msgstr ""
 
-#: option.c:349
-msgid "Proxy DNSSEC validation results from upstream nameservers"
+#: option.c:448
+msgid "Add requestor's IP subnet to forwarded DNS queries."
+msgstr ""
+
+#: option.c:449
+msgid "Proxy DNSSEC validation results from upstream nameservers."
+msgstr ""
+
+#: option.c:450
+msgid "Attempt to allocate sequential IP addresses to DHCP clients."
+msgstr ""
+
+#: option.c:451
+msgid "Copy connection-track mark from queries to upstream connections."
+msgstr ""
+
+#: option.c:452
+msgid "Allow DHCP clients to do their own DDNS updates."
+msgstr ""
+
+#: option.c:453
+msgid "Send router-advertisements for interfaces doing DHCPv6"
+msgstr ""
+
+#: option.c:454
+msgid "Specify DUID_EN-type DHCPv6 server DUID"
+msgstr ""
+
+#: option.c:455
+msgid "Specify host (A/AAAA and PTR) records"
+msgstr ""
+
+#: option.c:456
+msgid "Specify arbitrary DNS resource record"
+msgstr ""
+
+#: option.c:457
+msgid "Bind to interfaces in use - check for new interfaces"
+msgstr ""
+
+#: option.c:458
+msgid "Export local names to global DNS"
+msgstr ""
+
+#: option.c:459
+msgid "Domain to export to global DNS"
+msgstr ""
+
+#: option.c:460
+msgid "Set TTL for authoritative replies"
+msgstr ""
+
+#: option.c:461
+msgid "Set authoritive zone information"
+msgstr ""
+
+#: option.c:462
+msgid "Secondary authoritative nameservers for forward domains"
+msgstr ""
+
+#: option.c:463
+msgid "Peers which are allowed to do zone transfer"
+msgstr ""
+
+#: option.c:464
+msgid "Specify ipsets to which matching domains should be added"
+msgstr ""
+
+#: option.c:465
+msgid "Specify a domain and address range for synthesised names"
+msgstr ""
+
+#: option.c:466
+msgid "Activate DNSSEC validation"
+msgstr ""
+
+#: option.c:467
+msgid "Specify trust anchor key digest."
+msgstr ""
+
+#: option.c:468
+msgid "Disable upstream checking for DNSSEC debugging."
+msgstr ""
+
+#: option.c:469
+msgid "Ensure answers without DNSSEC are in unsigned zones."
+msgstr ""
+
+#: option.c:470
+msgid "Don't check DNSSEC signature timestamps until first cache-reload"
+msgstr ""
+
+#: option.c:471
+msgid "Timestamp file to verify system clock for DNSSEC"
+msgstr ""
+
+#: option.c:473
+msgid "Specify DHCPv6 prefix class"
+msgstr ""
+
+#: option.c:475
+msgid "Set priority, resend-interval and router-lifetime"
+msgstr ""
+
+#: option.c:476
+msgid "Do not log routine DHCP."
+msgstr ""
+
+#: option.c:477
+msgid "Do not log routine DHCPv6."
+msgstr ""
+
+#: option.c:478
+msgid "Do not log RA."
 msgstr ""
 
-#: option.c:638
+#: option.c:479
+msgid "Accept queries only from directly-connected networks"
+msgstr ""
+
+#: option.c:480
+msgid "Detect and remove DNS forwarding loops"
+msgstr ""
+
+#: option.c:481
+msgid "Ignore DNS responses containing ipaddr."
+msgstr ""
+
+#: option.c:683
 #, c-format
 msgid ""
 "Usage: dnsmasq [options]\n"
 "\n"
 msgstr ""
 
-#: option.c:640
+#: option.c:685
 #, c-format
 msgid "Use short options only on the command line.\n"
 msgstr ""
 
-#: option.c:642
+#: option.c:687
 #, c-format
 msgid "Valid options are:\n"
 msgstr ""
 
-#: option.c:683
-#, c-format
-msgid "Known DHCP options:\n"
+#: option.c:744 option.c:748
+msgid "bad port"
+msgstr ""
+
+#: option.c:775 option.c:807
+msgid "interface binding not supported"
+msgstr ""
+
+#: option.c:784 option.c:3575
+msgid "bad interface name"
+msgstr ""
+
+#: option.c:814
+msgid "bad address"
+msgstr ""
+
+#: option.c:996
+msgid "unsupported encapsulation for IPv6 option"
 msgstr ""
 
-#: option.c:798
+#: option.c:1010
 msgid "bad dhcp-option"
 msgstr ""
 
-#: option.c:860
+#: option.c:1078
 msgid "bad IP address"
 msgstr ""
 
-#: option.c:968
+#: option.c:1081 option.c:1219 option.c:2893
+msgid "bad IPv6 address"
+msgstr ""
+
+#: option.c:1246 option.c:1340
 msgid "bad domain in dhcp-option"
 msgstr ""
 
-#: option.c:1034
+#: option.c:1378
 msgid "dhcp-option too long"
 msgstr ""
 
-#: option.c:1043
+#: option.c:1385
 msgid "illegal dhcp-match"
 msgstr ""
 
-#: option.c:1087
+#: option.c:1447
 msgid "illegal repeated flag"
 msgstr ""
 
-#: option.c:1095
+#: option.c:1455
 msgid "illegal repeated keyword"
 msgstr ""
 
-#: option.c:1147 option.c:3030
+#: option.c:1520 option.c:4191
 #, c-format
 msgid "cannot access directory %s: %s"
 msgstr ""
 
-#: option.c:1178 tftp.c:460
+#: option.c:1566 tftp.c:493
 #, c-format
 msgid "cannot access %s: %s"
 msgstr ""
 
-#: option.c:1207
+#: option.c:1618
 msgid "setting log facility is not possible under Android"
 msgstr ""
 
-#: option.c:1216
+#: option.c:1627
 msgid "bad log facility"
 msgstr ""
 
-#: option.c:1265
+#: option.c:1680
 msgid "bad MX preference"
 msgstr ""
 
-#: option.c:1270
+#: option.c:1685
 msgid "bad MX name"
 msgstr ""
 
-#: option.c:1284
+#: option.c:1699
 msgid "bad MX target"
 msgstr ""
 
-#: option.c:1294
+#: option.c:1711
 msgid "cannot run scripts under uClinux"
 msgstr ""
 
-#: option.c:1296
+#: option.c:1713
 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts"
 msgstr ""
 
-#: option.c:1597 option.c:1601
-msgid "bad port"
+#: option.c:1717
+msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts"
 msgstr ""
 
-#: option.c:1620 option.c:1645
-msgid "interface binding not supported"
+#: option.c:1973 option.c:2018 option.c:2074
+msgid "bad prefix"
+msgstr ""
+
+#: option.c:2355
+msgid "recompile with HAVE_IPSET defined to enable ipset directives"
 msgstr ""
 
-#: option.c:1791
+#: option.c:2548
 msgid "bad port range"
 msgstr ""
 
-#: option.c:1808
+#: option.c:2564
 msgid "bad bridge-interface"
 msgstr ""
 
-#: option.c:1850
-msgid "bad dhcp-range"
+#: option.c:2624
+msgid "only one tag allowed"
 msgstr ""
 
-#: option.c:1878
-msgid "only one tag allowed"
+#: option.c:2644 option.c:2656 option.c:2764 option.c:2805
+msgid "bad dhcp-range"
 msgstr ""
 
-#: option.c:1925
+#: option.c:2671
 msgid "inconsistent DHCP range"
 msgstr ""
 
-#: option.c:2019 option.c:2045
+#: option.c:2732
+msgid "prefix length must be exactly 64 for RA subnets"
+msgstr ""
+
+#: option.c:2734
+msgid "prefix length must be exactly 64 for subnet constructors"
+msgstr ""
+
+#: option.c:2738
+msgid "prefix length must be at least 64"
+msgstr ""
+
+#: option.c:2741
+msgid "inconsistent DHCPv6 range"
+msgstr ""
+
+#: option.c:2752
+msgid "prefix must be zero with \"constructor:\" argument"
+msgstr ""
+
+#: option.c:2863 option.c:2911
 msgid "bad hex constant"
 msgstr ""
 
-#: option.c:2107
+#: option.c:2885
+msgid "cannot match tags in --dhcp-host"
+msgstr ""
+
+#: option.c:2933
+#, c-format
+msgid "duplicate dhcp-host IP address %s"
+msgstr ""
+
+#: option.c:2991
 msgid "bad DHCP host name"
 msgstr ""
 
-#: option.c:2188
+#: option.c:3073
 msgid "bad tag-if"
 msgstr ""
 
-#: option.c:2467 option.c:2752
+#: option.c:3397 option.c:3791
 msgid "invalid port number"
 msgstr ""
 
-#: option.c:2529
+#: option.c:3459
 msgid "bad dhcp-proxy address"
 msgstr ""
 
-#: option.c:2569
-msgid "invalid alias range"
+#: option.c:3485
+msgid "Bad dhcp-relay"
 msgstr ""
 
-#: option.c:2582
-msgid "bad interface name"
+#: option.c:3511
+msgid "bad RA-params"
+msgstr ""
+
+#: option.c:3520
+msgid "bad DUID"
+msgstr ""
+
+#: option.c:3562
+msgid "invalid alias range"
 msgstr ""
 
-#: option.c:2607
+#: option.c:3616
 msgid "bad CNAME"
 msgstr ""
 
-#: option.c:2612
+#: option.c:3621
 msgid "duplicate CNAME"
 msgstr ""
 
-#: option.c:2632
+#: option.c:3641
 msgid "bad PTR record"
 msgstr ""
 
-#: option.c:2663
+#: option.c:3672
 msgid "bad NAPTR record"
 msgstr ""
 
-#: option.c:2695
+#: option.c:3706
+msgid "bad RR record"
+msgstr ""
+
+#: option.c:3736
 msgid "bad TXT record"
 msgstr ""
 
-#: option.c:2738
+#: option.c:3777
 msgid "bad SRV record"
 msgstr ""
 
-#: option.c:2745
+#: option.c:3784
 msgid "bad SRV target"
 msgstr ""
 
-#: option.c:2759
+#: option.c:3798
 msgid "invalid priority"
 msgstr ""
 
-#: option.c:2766
+#: option.c:3805
 msgid "invalid weight"
 msgstr ""
 
-#: option.c:2785
-msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"
+#: option.c:3829
+msgid "Bad host-record"
+msgstr ""
+
+#: option.c:3846
+msgid "Bad name in host-record"
+msgstr ""
+
+#: option.c:3911
+msgid "bad trust anchor"
+msgstr ""
+
+#: option.c:3925
+msgid "bad HEX in trust anchor"
 msgstr ""
 
-#: option.c:2849
+#: option.c:3935
+msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"
+msgstr ""
+
+#: option.c:3994
 msgid "missing \""
 msgstr ""
 
-#: option.c:2908
+#: option.c:4051
 msgid "bad option"
 msgstr ""
 
-#: option.c:2910
+#: option.c:4053
 msgid "extraneous parameter"
 msgstr ""
 
-#: option.c:2912
+#: option.c:4055
 msgid "missing parameter"
 msgstr ""
 
-#: option.c:2916
+#: option.c:4057
+msgid "illegal option"
+msgstr ""
+
+#: option.c:4064
 msgid "error"
 msgstr ""
 
-#: option.c:2921
+#: option.c:4066
 #, c-format
-msgid "%s at line %d of %%s"
+msgid " at line %d of %s"
 msgstr ""
 
-#: option.c:2985 tftp.c:624
+#: option.c:4081 option.c:4328 option.c:4364
 #, c-format
-msgid "cannot read %s: %s"
+msgid "read %s"
 msgstr ""
 
-#: option.c:3151 option.c:3187
+#: option.c:4144 option.c:4267 tftp.c:667
 #, c-format
-msgid "read %s"
+msgid "cannot read %s: %s"
 msgstr ""
 
-#: option.c:3239
+#: option.c:4430
 msgid "junk found in command line"
 msgstr ""
 
-#: option.c:3269
+#: option.c:4465
 #, c-format
 msgid "Dnsmasq version %s  %s\n"
 msgstr ""
 
-#: option.c:3270
+#: option.c:4466
 #, c-format
 msgid ""
-"Compile time options %s\n"
+"Compile time options: %s\n"
 "\n"
 msgstr ""
 
-#: option.c:3271
+#: option.c:4467
 #, c-format
 msgid "This software comes with ABSOLUTELY NO WARRANTY.\n"
 msgstr ""
 
-#: option.c:3272
+#: option.c:4468
 #, c-format
 msgid "Dnsmasq is free software, and you are welcome to redistribute it\n"
 msgstr ""
 
-#: option.c:3273
+#: option.c:4469
 #, c-format
 msgid "under the terms of the GNU General Public License, version 2 or 3.\n"
 msgstr ""
 
-#: option.c:3284
+#: option.c:4480
 msgid "try --help"
 msgstr ""
 
-#: option.c:3286
+#: option.c:4482
 msgid "try -w"
 msgstr ""
 
-#: option.c:3289
+#: option.c:4484
 #, c-format
 msgid "bad command line options: %s"
 msgstr ""
 
-#: option.c:3330
+#: option.c:4541
 #, c-format
 msgid "cannot get host-name: %s"
 msgstr ""
 
-#: option.c:3358
+#: option.c:4569
 msgid "only one resolv.conf file allowed in no-poll mode."
 msgstr ""
 
-#: option.c:3368
+#: option.c:4579
 msgid "must have exactly one resolv.conf to read domain from."
 msgstr ""
 
-#: option.c:3371 network.c:848 dhcp.c:814
+#: option.c:4582 network.c:1507 dhcp.c:777
 #, c-format
 msgid "failed to read %s: %s"
 msgstr ""
 
-#: option.c:3388
+#: option.c:4599
 #, c-format
 msgid "no search directive found in %s"
 msgstr ""
 
-#: option.c:3409
+#: option.c:4620
 msgid "there must be a default domain when --dhcp-fqdn is set"
 msgstr ""
 
-#: option.c:3413
+#: option.c:4629
 msgid "syntax check OK"
 msgstr ""
 
-#: forward.c:461
+#: forward.c:111
+#, c-format
+msgid "failed to send packet: %s"
+msgstr ""
+
+#: forward.c:591
+msgid "discarding DNS reply: subnet option mismatch"
+msgstr ""
+
+#: forward.c:614
 #, c-format
 msgid "nameserver %s refused to do a recursive query"
 msgstr ""
 
-#: forward.c:489
+#: forward.c:646
 #, c-format
 msgid "possible DNS-rebind attack detected: %s"
 msgstr ""
 
-#: network.c:171
+#: forward.c:1209 forward.c:1815
+msgid "Ignoring query from non-local network"
+msgstr ""
+
+#: forward.c:2286
 #, c-format
-msgid "unknown interface %s in bridge-interface"
+msgid "Maximum number of concurrent DNS queries reached (max: %d)"
 msgstr ""
 
-#: network.c:380
+#: network.c:715
 #, c-format
 msgid "failed to create listening socket for %s: %s"
 msgstr ""
 
-#: network.c:746
+#: network.c:1021
+#, c-format
+msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s"
+msgstr ""
+
+#: network.c:1028
+msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)"
+msgstr ""
+
+#: network.c:1037
+#, c-format
+msgid "warning: no addresses found for interface %s"
+msgstr ""
+
+#: network.c:1095
+#, c-format
+msgid "interface %s failed to join DHCPv6 multicast group: %s"
+msgstr ""
+
+#: network.c:1289
 #, c-format
 msgid "failed to bind server socket for %s: %s"
 msgstr ""
 
-#: network.c:783
+#: network.c:1445
 #, c-format
 msgid "ignoring nameserver %s - local interface"
 msgstr ""
 
-#: network.c:794
+#: network.c:1456
 #, c-format
 msgid "ignoring nameserver %s - cannot make/bind socket: %s"
 msgstr ""
 
-#: network.c:811
+#: network.c:1469
 msgid "unqualified"
 msgstr ""
 
-#: network.c:811
+#: network.c:1469
 msgid "names"
 msgstr ""
 
-#: network.c:813
+#: network.c:1471
 msgid "default"
 msgstr ""
 
-#: network.c:815
+#: network.c:1473
 msgid "domain"
 msgstr ""
 
-#: network.c:818
+#: network.c:1476
 #, c-format
 msgid "using local addresses only for %s %s"
 msgstr ""
 
-#: network.c:820
+#: network.c:1478
 #, c-format
 msgid "using standard nameservers for %s %s"
 msgstr ""
 
-#: network.c:822
+#: network.c:1480
 #, c-format
 msgid "using nameserver %s#%d for %s %s"
 msgstr ""
 
-#: network.c:825
+#: network.c:1484
+#, c-format
+msgid "NOT using nameserver %s#%d - query loop detected"
+msgstr ""
+
+#: network.c:1487
 #, c-format
 msgid "using nameserver %s#%d(via %s)"
 msgstr ""
 
-#: network.c:827
+#: network.c:1489
 #, c-format
 msgid "using nameserver %s#%d"
 msgstr ""
 
-#: dnsmasq.c:148
+#: dnsmasq.c:163
+msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"
+msgstr ""
+
+#: dnsmasq.c:170
+msgid "no trust anchors provided for DNSSEC"
+msgstr ""
+
+#: dnsmasq.c:173
+msgid "cannot reduce cache size from default when DNSSEC enabled"
+msgstr ""
+
+#: dnsmasq.c:175
+msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h"
+msgstr ""
+
+#: dnsmasq.c:181
 msgid "TFTP server not available: set HAVE_TFTP in src/config.h"
 msgstr ""
 
-#: dnsmasq.c:153
+#: dnsmasq.c:186
+msgid "cannot use --conntrack AND --query-port"
+msgstr ""
+
+#: dnsmasq.c:189
+msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h"
+msgstr ""
+
+#: dnsmasq.c:194
 msgid "asychronous logging is not available under Solaris"
 msgstr ""
 
-#: dnsmasq.c:158
+#: dnsmasq.c:199
 msgid "asychronous logging is not available under Android"
 msgstr ""
 
-#: dnsmasq.c:177
-#, c-format
-msgid "failed to find list of interfaces: %s"
+#: dnsmasq.c:204
+msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h"
+msgstr ""
+
+#: dnsmasq.c:209
+msgid "loop detection not available: set HAVE_LOOP in src/config.h"
+msgstr ""
+
+#: dnsmasq.c:217
+msgid "zone serial must be configured in --auth-soa"
 msgstr ""
 
-#: dnsmasq.c:185
+#: dnsmasq.c:235
+msgid "dhcp-range constructor not available on this platform"
+msgstr ""
+
+#: dnsmasq.c:278
+msgid "cannot set --bind-interfaces and --bind-dynamic"
+msgstr ""
+
+#: dnsmasq.c:281
 #, c-format
-msgid "unknown interface %s"
+msgid "failed to find list of interfaces: %s"
 msgstr ""
 
-#: dnsmasq.c:191
+#: dnsmasq.c:290
 #, c-format
-msgid "no interface with address %s"
+msgid "unknown interface %s"
 msgstr ""
 
-#: dnsmasq.c:207 dnsmasq.c:678
+#: dnsmasq.c:354 dnsmasq.c:997
 #, c-format
 msgid "DBus error: %s"
 msgstr ""
 
-#: dnsmasq.c:210
+#: dnsmasq.c:357
 msgid "DBus not available: set HAVE_DBUS in src/config.h"
 msgstr ""
 
-#: dnsmasq.c:236
+#: dnsmasq.c:385
 #, c-format
 msgid "unknown user or group: %s"
 msgstr ""
 
-#: dnsmasq.c:291
+#: dnsmasq.c:440
 #, c-format
 msgid "cannot chdir to filesystem root: %s"
 msgstr ""
 
-#: dnsmasq.c:455
+#: dnsmasq.c:692
 #, c-format
 msgid "started, version %s DNS disabled"
 msgstr ""
 
-#: dnsmasq.c:457
+#: dnsmasq.c:694
 #, c-format
 msgid "started, version %s cachesize %d"
 msgstr ""
 
-#: dnsmasq.c:459
+#: dnsmasq.c:696
 #, c-format
 msgid "started, version %s cache disabled"
 msgstr ""
 
-#: dnsmasq.c:461
+#: dnsmasq.c:698
 #, c-format
 msgid "compile time options: %s"
 msgstr ""
 
-#: dnsmasq.c:467
+#: dnsmasq.c:704
 msgid "DBus support enabled: connected to system bus"
 msgstr ""
 
-#: dnsmasq.c:469
+#: dnsmasq.c:706
 msgid "DBus support enabled: bus connection pending"
 msgstr ""
 
-#: dnsmasq.c:474
+#: dnsmasq.c:711
+msgid "DNS service limited to local subnets"
+msgstr ""
+
+#: dnsmasq.c:727
+msgid "DNSSEC validation enabled"
+msgstr ""
+
+#: dnsmasq.c:730
+msgid "DNSSEC signature timestamps not checked until first cache reload"
+msgstr ""
+
+#: dnsmasq.c:733
+msgid "DNSSEC signature timestamps not checked until system time valid"
+msgstr ""
+
+#: dnsmasq.c:738
 #, c-format
 msgid "warning: failed to change owner of %s: %s"
 msgstr ""
 
-#: dnsmasq.c:478
+#: dnsmasq.c:742
 msgid "setting --bind-interfaces option because of OS limitations"
 msgstr ""
 
-#: dnsmasq.c:483
+#: dnsmasq.c:752
 #, c-format
 msgid "warning: interface %s does not currently exist"
 msgstr ""
 
-#: dnsmasq.c:488
+#: dnsmasq.c:757
 msgid "warning: ignoring resolv-file flag because no-resolv is set"
 msgstr ""
 
-#: dnsmasq.c:491
+#: dnsmasq.c:760
 msgid "warning: no upstream servers configured"
 msgstr ""
 
-#: dnsmasq.c:495
+#: dnsmasq.c:764
 #, c-format
 msgid "asynchronous logging enabled, queue limit is %d messages"
 msgstr ""
 
-#: dnsmasq.c:508
-#, c-format
-msgid "DHCP, static leases only on %.0s%s, lease time %s"
-msgstr ""
-
-#: dnsmasq.c:510
-#, c-format
-msgid "DHCP, proxy on subnet %.0s%s%.0s"
+#: dnsmasq.c:785
+msgid "IPv6 router advertisement enabled"
 msgstr ""
 
-#: dnsmasq.c:511
+#: dnsmasq.c:790
 #, c-format
-msgid "DHCP, IP range %s -- %s, lease time %s"
+msgid "DHCP, sockets bound exclusively to interface %s"
 msgstr ""
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 msgid "root is "
 msgstr ""
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 msgid "enabled"
 msgstr ""
 
-#: dnsmasq.c:528
+#: dnsmasq.c:806
 msgid "secure mode"
 msgstr ""
 
-#: dnsmasq.c:554
+#: dnsmasq.c:809
+#, c-format
+msgid "warning: %s inaccessible"
+msgstr ""
+
+#: dnsmasq.c:813
+#, c-format
+msgid "warning: TFTP directory %s inaccessible"
+msgstr ""
+
+#: dnsmasq.c:839
 #, c-format
 msgid "restricting maximum simultaneous TFTP transfers to %d"
 msgstr ""
 
-#: dnsmasq.c:680
+#: dnsmasq.c:999
 msgid "connected to system DBus"
 msgstr ""
 
-#: dnsmasq.c:775
+#: dnsmasq.c:1149
 #, c-format
 msgid "cannot fork into background: %s"
 msgstr ""
 
-#: dnsmasq.c:778
+#: dnsmasq.c:1152
 #, c-format
 msgid "failed to create helper: %s"
 msgstr ""
 
-#: dnsmasq.c:781
+#: dnsmasq.c:1155
 #, c-format
 msgid "setting capabilities failed: %s"
 msgstr ""
 
-#: dnsmasq.c:785
+#: dnsmasq.c:1158
 #, c-format
 msgid "failed to change user-id to %s: %s"
 msgstr ""
 
-#: dnsmasq.c:790
+#: dnsmasq.c:1161
 #, c-format
 msgid "failed to change group-id to %s: %s"
 msgstr ""
 
-#: dnsmasq.c:793
+#: dnsmasq.c:1164
 #, c-format
 msgid "failed to open pidfile %s: %s"
 msgstr ""
 
-#: dnsmasq.c:796
+#: dnsmasq.c:1167
+#, c-format
+msgid "cannot open log %s: %s"
+msgstr ""
+
+#: dnsmasq.c:1170
+#, c-format
+msgid "failed to load Lua script: %s"
+msgstr ""
+
+#: dnsmasq.c:1173
 #, c-format
-msgid "cannot open %s: %s"
+msgid "TFTP directory %s inaccessible: %s"
+msgstr ""
+
+#: dnsmasq.c:1176
+#, c-format
+msgid "cannot create timestamp file %s: %s"
+msgstr ""
+
+#: dnsmasq.c:1197
+msgid "now checking DNSSEC signature timestamps"
 msgstr ""
 
-#: dnsmasq.c:851
+#: dnsmasq.c:1264
 #, c-format
-msgid "child process killed by signal %d"
+msgid "script process killed by signal %d"
 msgstr ""
 
-#: dnsmasq.c:855
+#: dnsmasq.c:1268
 #, c-format
-msgid "child process exited with status %d"
+msgid "script process exited with status %d"
 msgstr ""
 
-#: dnsmasq.c:859
+#: dnsmasq.c:1272
 #, c-format
 msgid "failed to execute %s: %s"
 msgstr ""
 
-#: dnsmasq.c:903
+#: dnsmasq.c:1327
 msgid "exiting on receipt of SIGTERM"
 msgstr ""
 
-#: dnsmasq.c:931
+#: dnsmasq.c:1355
 #, c-format
 msgid "failed to access %s: %s"
 msgstr ""
 
-#: dnsmasq.c:961
+#: dnsmasq.c:1385
 #, c-format
 msgid "reading %s"
 msgstr ""
 
-#: dnsmasq.c:972
+#: dnsmasq.c:1396
 #, c-format
 msgid "no servers found in %s, will retry"
 msgstr ""
 
-#: dhcp.c:40
+#: dhcp.c:53
 #, c-format
 msgid "cannot create DHCP socket: %s"
 msgstr ""
 
-#: dhcp.c:52
+#: dhcp.c:68
 #, c-format
 msgid "failed to set options on DHCP socket: %s"
 msgstr ""
 
-#: dhcp.c:65
+#: dhcp.c:89
 #, c-format
 msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"
 msgstr ""
 
-#: dhcp.c:77
+#: dhcp.c:101
 #, c-format
 msgid "failed to bind DHCP server socket: %s"
 msgstr ""
 
-#: dhcp.c:103
+#: dhcp.c:127
 #, c-format
 msgid "cannot create ICMP raw socket: %s."
 msgstr ""
 
-#: dhcp.c:281
-#, c-format
-msgid "DHCP packet received on %s which has no address"
-msgstr ""
-
-#: dhcp.c:445
+#: dhcp.c:241 dhcp6.c:180
 #, c-format
-msgid "DHCP range %s -- %s is not consistent with netmask %s"
+msgid "unknown interface %s in bridge-interface"
 msgstr ""
 
-#: dhcp.c:852
+#: dhcp.c:281
 #, c-format
-msgid "bad line at %s line %d"
+msgid "DHCP packet received on %s which has no address"
 msgstr ""
 
-#: dhcp.c:895
+#: dhcp.c:415
 #, c-format
-msgid "ignoring %s line %d, duplicate name or IP address"
+msgid "ARP-cache injection failed: %s"
 msgstr ""
 
-#: dhcp.c:978
+#: dhcp.c:514
 #, c-format
-msgid "duplicate IP address %s in dhcp-config directive."
+msgid "DHCP range %s -- %s is not consistent with netmask %s"
 msgstr ""
 
-#: dhcp.c:981
+#: dhcp.c:815
 #, c-format
-msgid "duplicate IP address %s in %s."
+msgid "bad line at %s line %d"
 msgstr ""
 
-#: dhcp.c:1024
+#: dhcp.c:858
 #, c-format
-msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgid "ignoring %s line %d, duplicate name or IP address"
 msgstr ""
 
-#: dhcp.c:1029
+#: dhcp.c:1002 rfc3315.c:2135
 #, c-format
-msgid "duplicate IP address %s (%s) in dhcp-config directive"
+msgid "DHCP relay %s -> %s"
 msgstr ""
 
-#: lease.c:67
+#: lease.c:61
 #, c-format
 msgid "cannot open or create lease file %s: %s"
 msgstr ""
 
-#: lease.c:93
+#: lease.c:134
 msgid "too many stored leases"
 msgstr ""
 
-#: lease.c:129
+#: lease.c:165
 #, c-format
 msgid "cannot run lease-init script %s: %s"
 msgstr ""
 
-#: lease.c:135
+#: lease.c:171
 #, c-format
 msgid "lease-init script returned exit code %s"
 msgstr ""
 
-#: lease.c:235
+#: lease.c:342
 #, c-format
 msgid "failed to write %s: %s (retry in %us)"
 msgstr ""
 
-#: rfc2131.c:315
+#: lease.c:906
+#, c-format
+msgid "Ignoring domain %s for DHCP host name %s"
+msgstr ""
+
+#: rfc2131.c:344
 #, c-format
 msgid "no address range available for DHCP request %s %s"
 msgstr ""
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "with subnet selector"
 msgstr ""
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "via"
 msgstr ""
 
-#: rfc2131.c:331
+#: rfc2131.c:357
 #, c-format
 msgid "%u available DHCP subnet: %s/%s"
 msgstr ""
 
-#: rfc2131.c:334
+#: rfc2131.c:360 rfc3315.c:300
 #, c-format
 msgid "%u available DHCP range: %s -- %s"
 msgstr ""
 
-#: rfc2131.c:363
+#: rfc2131.c:471
+#, c-format
+msgid "%u vendor class: %s"
+msgstr ""
+
+#: rfc2131.c:473
+#, c-format
+msgid "%u user class: %s"
+msgstr ""
+
+#: rfc2131.c:500
 msgid "disabled"
 msgstr ""
 
-#: rfc2131.c:404 rfc2131.c:916 rfc2131.c:1288
+#: rfc2131.c:541 rfc2131.c:974 rfc2131.c:1380 rfc3315.c:603 rfc3315.c:856
+#: rfc3315.c:1135
 msgid "ignored"
 msgstr ""
 
-#: rfc2131.c:419 rfc2131.c:1135
+#: rfc2131.c:556 rfc2131.c:1207 rfc3315.c:906
 msgid "address in use"
 msgstr ""
 
-#: rfc2131.c:433 rfc2131.c:970
+#: rfc2131.c:570 rfc2131.c:1028
 msgid "no address available"
 msgstr ""
 
-#: rfc2131.c:440 rfc2131.c:1098
+#: rfc2131.c:577 rfc2131.c:1170
 msgid "wrong network"
 msgstr ""
 
-#: rfc2131.c:454
+#: rfc2131.c:592
 msgid "no address configured"
 msgstr ""
 
-#: rfc2131.c:460 rfc2131.c:1148
+#: rfc2131.c:598 rfc2131.c:1220
 msgid "no leases left"
 msgstr ""
 
-#: rfc2131.c:545
+#: rfc2131.c:693 rfc3315.c:476
 #, c-format
 msgid "%u client provides name: %s"
 msgstr ""
 
-#: rfc2131.c:700
-#, c-format
-msgid "%u vendor class: %s"
-msgstr ""
-
-#: rfc2131.c:702
-#, c-format
-msgid "%u user class: %s"
-msgstr ""
-
-#: rfc2131.c:761
+#: rfc2131.c:798
 msgid "PXE BIS not supported"
 msgstr ""
 
-#: rfc2131.c:886
+#: rfc2131.c:942 rfc3315.c:1229
 #, c-format
 msgid "disabling DHCP static address %s for %s"
 msgstr ""
 
-#: rfc2131.c:907
+#: rfc2131.c:963
 msgid "unknown lease"
 msgstr ""
 
-#: rfc2131.c:939
+#: rfc2131.c:997
 #, c-format
 msgid "not using configured address %s because it is leased to %s"
 msgstr ""
 
-#: rfc2131.c:949
+#: rfc2131.c:1007
 #, c-format
 msgid "not using configured address %s because it is in use by the server or relay"
 msgstr ""
 
-#: rfc2131.c:952
+#: rfc2131.c:1010
 #, c-format
 msgid "not using configured address %s because it was previously declined"
 msgstr ""
 
-#: rfc2131.c:968 rfc2131.c:1141
+#: rfc2131.c:1026 rfc2131.c:1213
 msgid "no unique-id"
 msgstr ""
 
-#: rfc2131.c:1037
+#: rfc2131.c:1108
 msgid "wrong server-ID"
 msgstr ""
 
-#: rfc2131.c:1055
+#: rfc2131.c:1127
 msgid "wrong address"
 msgstr ""
 
-#: rfc2131.c:1073
+#: rfc2131.c:1145 rfc3315.c:1002
 msgid "lease not found"
 msgstr ""
 
-#: rfc2131.c:1106
+#: rfc2131.c:1178
 msgid "address not available"
 msgstr ""
 
-#: rfc2131.c:1117
+#: rfc2131.c:1189
 msgid "static lease available"
 msgstr ""
 
-#: rfc2131.c:1121
+#: rfc2131.c:1193
 msgid "address reserved"
 msgstr ""
 
-#: rfc2131.c:1129
+#: rfc2131.c:1201
 #, c-format
 msgid "abandoning lease to %s of %s"
 msgstr ""
 
-#: rfc2131.c:1710
-#, c-format
-msgid "%u tags: %s"
-msgstr ""
-
-#: rfc2131.c:1723
+#: rfc2131.c:1707
 #, c-format
 msgid "%u bootfile name: %s"
 msgstr ""
 
-#: rfc2131.c:1732
+#: rfc2131.c:1716
 #, c-format
 msgid "%u server name: %s"
 msgstr ""
 
-#: rfc2131.c:1746
+#: rfc2131.c:1724
 #, c-format
 msgid "%u next server: %s"
 msgstr ""
 
-#: rfc2131.c:1749
+#: rfc2131.c:1727
 #, c-format
 msgid "%u broadcast response"
 msgstr ""
 
-#: rfc2131.c:1812
+#: rfc2131.c:1790
 #, c-format
 msgid "cannot send DHCP/BOOTP option %d: no space left in packet"
 msgstr ""
 
-#: rfc2131.c:2058
+#: rfc2131.c:2031
 msgid "PXE menu too large"
 msgstr ""
 
-#: rfc2131.c:2171
-#, c-format
-msgid "Ignoring domain %s for DHCP host name %s"
-msgstr ""
-
-#: rfc2131.c:2189
+#: rfc2131.c:2170 rfc3315.c:1502
 #, c-format
 msgid "%u requested options: %s"
 msgstr ""
 
-#: rfc2131.c:2456
+#: rfc2131.c:2487
 #, c-format
 msgid "cannot send RFC3925 option: too many options for enterprise number %d"
 msgstr ""
 
-#: netlink.c:70
+#: netlink.c:77
 #, c-format
 msgid "cannot create netlink socket: %s"
 msgstr ""
 
-#: netlink.c:288
+#: netlink.c:348
 #, c-format
 msgid "netlink returns error: %s"
 msgstr ""
 
-#: dbus.c:150
+#: dbus.c:186
 msgid "attempt to set an IPv6 server address via DBus - no IPv6 support"
 msgstr ""
 
-#: dbus.c:286
+#: dbus.c:439
+#, c-format
+msgid "Enabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:444
+#, c-format
+msgid "Disabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:691
 msgid "setting upstream servers from DBus"
 msgstr ""
 
-#: dbus.c:324
+#: dbus.c:738
 msgid "could not register a DBus message handler"
 msgstr ""
 
-#: bpf.c:217
+#: bpf.c:263
 #, c-format
 msgid "cannot create DHCP BPF socket: %s"
 msgstr ""
 
-#: bpf.c:245
+#: bpf.c:291
 #, c-format
 msgid "DHCP request for unsupported hardware type (%d) received on %s"
 msgstr ""
 
-#: tftp.c:281
+#: bpf.c:376
+#, c-format
+msgid "cannot create PF_ROUTE socket: %s"
+msgstr ""
+
+#: bpf.c:397
+msgid "Unknown protocol version from route socket"
+msgstr ""
+
+#: helper.c:153
+msgid "lease() function missing in Lua script"
+msgstr ""
+
+#: tftp.c:309
 msgid "unable to get free port for TFTP"
 msgstr ""
 
-#: tftp.c:296
+#: tftp.c:325
 #, c-format
 msgid "unsupported request from %s"
 msgstr ""
 
-#: tftp.c:406
+#: tftp.c:439
 #, c-format
 msgid "file %s not found"
 msgstr ""
 
-#: tftp.c:522
+#: tftp.c:548
 #, c-format
 msgid "error %d %s received from %s"
 msgstr ""
 
-#: tftp.c:554
+#: tftp.c:590
 #, c-format
 msgid "failed sending %s to %s"
 msgstr ""
 
-#: tftp.c:568
+#: tftp.c:590
 #, c-format
 msgid "sent %s to %s"
 msgstr ""
 
-#: log.c:177
+#: log.c:190
 #, c-format
 msgid "overflow: %d log entries lost"
 msgstr ""
 
-#: log.c:254
+#: log.c:268
 #, c-format
 msgid "log failed: %s"
 msgstr ""
 
-#: log.c:462
+#: log.c:469
 msgid "FAILED to start up"
 msgstr ""
+
+#: conntrack.c:65
+#, c-format
+msgid "Conntrack connection mark retrieval failed: %s"
+msgstr ""
+
+#: dhcp6.c:59
+#, c-format
+msgid "cannot create DHCPv6 socket: %s"
+msgstr ""
+
+#: dhcp6.c:80
+#, c-format
+msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"
+msgstr ""
+
+#: dhcp6.c:92
+#, c-format
+msgid "failed to bind DHCPv6 server socket: %s"
+msgstr ""
+
+#: rfc3315.c:157
+#, c-format
+msgid "no address range available for DHCPv6 request from relay at %s"
+msgstr ""
+
+#: rfc3315.c:166
+#, c-format
+msgid "no address range available for DHCPv6 request via %s"
+msgstr ""
+
+#: rfc3315.c:297
+#, c-format
+msgid "%u available DHCPv6 subnet: %s/%d"
+msgstr ""
+
+#: rfc3315.c:380
+#, c-format
+msgid "%u vendor class: %u"
+msgstr ""
+
+#: rfc3315.c:428
+#, c-format
+msgid "%u client MAC address: %s"
+msgstr ""
+
+#: rfc3315.c:660
+#, c-format
+msgid "unknown prefix-class %d"
+msgstr ""
+
+#: rfc3315.c:803 rfc3315.c:898
+msgid "address unavailable"
+msgstr ""
+
+#: rfc3315.c:815 rfc3315.c:946 rfc3315.c:1279
+msgid "success"
+msgstr ""
+
+#: rfc3315.c:830 rfc3315.c:839 rfc3315.c:954 rfc3315.c:956
+msgid "no addresses available"
+msgstr ""
+
+#: rfc3315.c:933
+msgid "not on link"
+msgstr ""
+
+#: rfc3315.c:1006 rfc3315.c:1191 rfc3315.c:1268
+msgid "no binding found"
+msgstr ""
+
+#: rfc3315.c:1044
+msgid "deprecated"
+msgstr ""
+
+#: rfc3315.c:1049
+msgid "address invalid"
+msgstr ""
+
+#: rfc3315.c:1096
+msgid "confirm failed"
+msgstr ""
+
+#: rfc3315.c:1112
+msgid "all addresses still on link"
+msgstr ""
+
+#: rfc3315.c:1200
+msgid "release received"
+msgstr ""
+
+#: rfc3315.c:2126
+msgid "Cannot multicast to DHCPv6 server without correct interface"
+msgstr ""
+
+#: dhcp-common.c:145
+#, c-format
+msgid "Ignoring duplicate dhcp-option %d"
+msgstr ""
+
+#: dhcp-common.c:222
+#, c-format
+msgid "%u tags: %s"
+msgstr ""
+
+#: dhcp-common.c:407
+#, c-format
+msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgstr ""
+
+#: dhcp-common.c:430
+#, c-format
+msgid "duplicate IP address %s (%s) in dhcp-config directive"
+msgstr ""
+
+#: dhcp-common.c:494
+#, c-format
+msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s"
+msgstr ""
+
+#: dhcp-common.c:615
+#, c-format
+msgid "Known DHCP options:\n"
+msgstr ""
+
+#: dhcp-common.c:626
+#, c-format
+msgid "Known DHCPv6 options:\n"
+msgstr ""
+
+#: dhcp-common.c:823
+msgid ", prefix deprecated"
+msgstr ""
+
+#: dhcp-common.c:826
+#, c-format
+msgid ", lease time "
+msgstr ""
+
+#: dhcp-common.c:868
+#, c-format
+msgid "%s stateless on %s%.0s%.0s%s"
+msgstr ""
+
+#: dhcp-common.c:870
+#, c-format
+msgid "%s, static leases only on %.0s%s%s%.0s"
+msgstr ""
+
+#: dhcp-common.c:872
+#, c-format
+msgid "%s, proxy on subnet %.0s%s%.0s%.0s"
+msgstr ""
+
+#: dhcp-common.c:873
+#, c-format
+msgid "%s, IP range %s -- %s%s%.0s"
+msgstr ""
+
+#: dhcp-common.c:886
+#, c-format
+msgid "DHCPv4-derived IPv6 names on %s%s"
+msgstr ""
+
+#: dhcp-common.c:889
+#, c-format
+msgid "router advertisement on %s%s"
+msgstr ""
+
+#: dhcp-common.c:900
+#, c-format
+msgid "DHCP relay from %s to %s via %s"
+msgstr ""
+
+#: dhcp-common.c:902
+#, c-format
+msgid "DHCP relay from %s to %s"
+msgstr ""
+
+#: radv.c:109
+#, c-format
+msgid "cannot create ICMPv6 socket: %s"
+msgstr ""
+
+#: auth.c:448
+#, c-format
+msgid "ignoring zone transfer request from %s"
+msgstr ""
+
+#: ipset.c:95
+#, c-format
+msgid "failed to find kernel version: %s"
+msgstr ""
+
+#: ipset.c:114
+#, c-format
+msgid "failed to create IPset control socket: %s"
+msgstr ""
+
+#: dnssec.c:449 dnssec.c:493
+#, c-format
+msgid "failed to update mtime on %s: %s"
+msgstr ""
+
+#: blockdata.c:58
+#, c-format
+msgid "DNSSEC memory in use %u, max %u, allocated %u"
+msgstr ""
+
+#: tables.c:80
+msgid "error: fill_addr missused"
+msgstr ""
+
+#: tables.c:109
+#, c-format
+msgid "failed to access pf devices: %s"
+msgstr ""
+
+#: tables.c:123
+#, c-format
+msgid "warning: no opened pf devices %s"
+msgstr ""
+
+#: tables.c:131
+#, c-format
+msgid "error: cannot use table name %s"
+msgstr ""
+
+#: tables.c:139
+#, c-format
+msgid "error: cannot strlcpy table name %s"
+msgstr ""
+
+#: tables.c:145
+#, c-format
+msgid "warning: pfr_add_tables: %s(%d)"
+msgstr ""
+
+#: tables.c:151
+msgid "info: table created"
+msgstr ""
+
+#: tables.c:162
+#, c-format
+msgid "warning: DIOCR%sADDRS: %s"
+msgstr ""
+
+#: tables.c:166
+#, c-format
+msgid "%d addresses %s"
+msgstr ""
+
+#: inotify.c:59
+#, c-format
+msgid "cannot access path %s: %s"
+msgstr ""
+
+#: inotify.c:92
+#, c-format
+msgid "failed to create inotify: %s"
+msgstr ""
+
+#: inotify.c:105
+#, c-format
+msgid "too many symlinks following %s"
+msgstr ""
+
+#: inotify.c:121
+#, c-format
+msgid "directory %s for resolv-file is missing, cannot poll"
+msgstr ""
+
+#: inotify.c:125 inotify.c:162
+#, c-format
+msgid "failed to create inotify for %s: %s"
+msgstr ""
+
+#: inotify.c:147
+#, c-format
+msgid "bad dynamic directory %s: %s"
+msgstr ""
+
+#: inotify.c:247
+#, c-format
+msgid "inotify, new or changed file %s"
+msgstr ""
index 6863785..6ee92ec 100644 (file)
--- a/po/fr.po
+++ b/po/fr.po
 # Translation completed by Gildas Le Nadan <3ntr0p13@gmail.com>
 msgid ""
 msgstr ""
-"Project-Id-Version: dnsmasq 2.56\n"
+"Project-Id-Version: dnsmasq 2.67\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2009-06-18 12:24+0100\n"
-"PO-Revision-Date: 2011-02-10 20:40+0100\n"
+"PO-Revision-Date: 2013-10-24 19:24+1100\n"
 "Last-Translator:  Gildas Le Nadan <3ntr0p13@gmail.com>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=ISO-8859-1\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 
-#: cache.c:761
+#: cache.c:523
+msgid "Internal error in cache."
+msgstr ""
+
+#: cache.c:941
 #, c-format
 msgid "failed to load names from %s: %s"
 msgstr "Impossible de charger les noms à partir de %s : %s"
 
-#: cache.c:795 dhcp.c:865
+#: cache.c:967 dhcp.c:828
 #, c-format
 msgid "bad address at %s line %d"
 msgstr "mauvaise adresse dans %s ligne %d"
 
-#: cache.c:853 dhcp.c:881
+#: cache.c:1018 dhcp.c:844
 #, c-format
 msgid "bad name at %s line %d"
 msgstr "mauvais nom dans %s ligne %d"
 
-#: cache.c:860 dhcp.c:956
+#: cache.c:1027 dhcp.c:919
 #, c-format
 msgid "read %s - %d addresses"
 msgstr "lecture %s - %d adresses"
 
-#: cache.c:899
+#: cache.c:1135
 msgid "cleared cache"
 msgstr "cache vidé"
 
-#: cache.c:960
+#: cache.c:1164
+#, c-format
+msgid "No IPv4 address found for %s"
+msgstr "Aucune adresse IPv4 trouvée pour %s"
+
+#: cache.c:1242
 #, c-format
 msgid "%s is a CNAME, not giving it to the DHCP lease of %s"
 msgstr "%s est un CNAME, il ne sera pas donné au bail DHCP de %s"
 
-#: cache.c:966
+#: cache.c:1266
 #, c-format
 msgid "not giving name %s to the DHCP lease of %s because the name exists in %s with address %s"
 msgstr "ne donne pas de nom %s au bail DHCP de %s parce-que le nom existe dans %s avec l'adresse %s"
 
-#: cache.c:1039
+#: cache.c:1421
 #, c-format
 msgid "time %lu"
 msgstr "horodatage %lu"
 
-#: cache.c:1040
+#: cache.c:1422
 #, c-format
 msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries."
 msgstr "taille de cache %d, %d/%d insertions dans le cache entrées non-expirées réutilisées"
 
-#: cache.c:1042
+#: cache.c:1424
 #, c-format
 msgid "queries forwarded %u, queries answered locally %u"
 msgstr "requêtes transmises %u, requêtes résolues localement %u"
 
-#: cache.c:1068
+#: cache.c:1427
+#, fuzzy, c-format
+msgid "queries for authoritative zones %u"
+msgstr "Configure la durée de vie (Time To Live) pour les réponses faisant autorité"
+
+#: cache.c:1453
 #, c-format
 msgid "server %s#%d: queries sent %u, retried or failed %u"
 msgstr "serveur %s#%d: requêtes envoyées %u, requêtes réessayées ou échouées %u"
 
-#: util.c:57
+#: util.c:45
 #, c-format
 msgid "failed to seed the random number generator: %s"
 msgstr "impossible d'initialiser le générateur de nombre aléatoire : %s"
 
-#: util.c:189
+#: util.c:205
 msgid "failed to allocate memory"
 msgstr "impossible d'allouer la mémoire"
 
-#: util.c:227 option.c:573
+#: util.c:250 option.c:601
 msgid "could not get memory"
 msgstr "impossible d'allouer de la mémoire"
 
-#: util.c:237
+#: util.c:260
 #, c-format
 msgid "cannot create pipe: %s"
 msgstr "Ne peut pas créer le tube %s : %s"
 
-#: util.c:245
+#: util.c:268
 #, c-format
 msgid "failed to allocate %d bytes"
 msgstr "impossible d'allouer %d octets"
 
-#: util.c:350
+#: util.c:437
 #, c-format
 msgid "infinite"
 msgstr "illimité(e)"
 
-#: option.c:244
+#: option.c:332
 msgid "Specify local address(es) to listen on."
 msgstr "Spécifie la ou les adresse(s) locales où le démon doit se mettre à l'écoute."
 
-#: option.c:245
+#: option.c:333
 msgid "Return ipaddr for all hosts in specified domains."
 msgstr "Retourne les adresses IP pour toutes les machines présentes dans les domaines spécifiés"
 
-#: option.c:246
+#: option.c:334
 msgid "Fake reverse lookups for RFC1918 private address ranges."
 msgstr "Traduction inverse truquée pour la plage d'adresse privée RFC1918"
 
-#: option.c:247
+#: option.c:335
 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."
 msgstr "Traite l'adresse IP comme un domaine inexistant NXDOMAIN (contourne le systeme de redirection de Verisign)"
 
-#: option.c:248
+#: option.c:336
 #, c-format
 msgid "Specify the size of the cache in entries (defaults to %s)."
 msgstr "Spécifie le nombre d'entrées que contiendra le cache (par défaut : %s)."
 
-#: option.c:249
+#: option.c:337
 #, c-format
 msgid "Specify configuration file (defaults to %s)."
 msgstr "Spécifie le nom du fichier de configuration (par défaut : %s)"
 
-#: option.c:250
+#: option.c:338
 msgid "Do NOT fork into the background: run in debug mode."
 msgstr "Ne passe pas en tâche de fond : démarre en mode debug"
 
-#: option.c:251
+#: option.c:339
 msgid "Do NOT forward queries with no domain part."
 msgstr "Ne retransmet pas les requêtes qui n'ont pas de domaine."
 
-#: option.c:252
+#: option.c:340
 msgid "Return self-pointing MX records for local hosts."
 msgstr "Retourne les champs MX pour les machines locales."
 
-#: option.c:253
+#: option.c:341
 msgid "Expand simple names in /etc/hosts with domain-suffix."
 msgstr "Etend les noms uniques des machines dans /etc/hosts avec le suffixe du domaine."
 
-#: option.c:254
+#: option.c:342
 msgid "Don't forward spurious DNS requests from Windows hosts."
 msgstr "Ne retransmet pas les fausses requêtes DNS en provenance des machines Windows."
 
-#: option.c:255
+#: option.c:343
 msgid "Enable DHCP in the range given with lease duration."
 msgstr "Autorise DHCP dans la plage d'adresses donnée sur la durée de validité du bail."
 
-#: option.c:256
+#: option.c:344
 #, c-format
 msgid "Change to this group after startup (defaults to %s)."
 msgstr "On change pour ce groupe après le démarrage (par défaut : %s)."
 
-#: option.c:257
+#: option.c:345
 msgid "Set address or hostname for a specified machine."
 msgstr "On assigne une adresse ou un nom pour une machine spécifiée."
 
-#: option.c:258
+#: option.c:346
 msgid "Read DHCP host specs from file."
 msgstr "Lecture des spécifications d'hôtes DHCP à partir du fichier"
 
-#: option.c:259
+#: option.c:347
 msgid "Read DHCP option specs from file."
 msgstr "Lecture des options DHCP à partir du fichier"
 
-#: option.c:260
+#: option.c:348
+#, fuzzy
+msgid "Read DHCP host specs from a directory."
+msgstr "Lecture des spécifications d'hôtes DHCP à partir du fichier"
+
+#: option.c:349
+#, fuzzy
+msgid "Read DHCP options from a directory."
+msgstr "Lecture des options DHCP à partir du fichier"
+
+#: option.c:350
 msgid "Evaluate conditional tag expression."
 msgstr "Expression d'évaluation conditionnelle d'étiquette"
 
-#: option.c:261
+#: option.c:351
 #, c-format
 msgid "Do NOT load %s file."
 msgstr "Ne charge PAS le fichier %s."
 
-#: option.c:262
+#: option.c:352
 #, c-format
 msgid "Specify a hosts file to be read in addition to %s."
 msgstr "Spécifie un nom de fichier hosts à lire en complément de %s"
 
-#: option.c:263
+#: option.c:353
+#, fuzzy
+msgid "Read hosts files from a directory."
+msgstr "Lecture des spécifications d'hôtes DHCP à partir du fichier"
+
+#: option.c:354
 msgid "Specify interface(s) to listen on."
 msgstr "Spécifie la ou les interface(s) où le démon doit se mettre à l'écoute."
 
-#: option.c:264
+#: option.c:355
 msgid "Specify interface(s) NOT to listen on."
 msgstr "Spécifie la ou les interface(s) que le démon ne doit PAS traiter."
 
 #
-#: option.c:265
+#: option.c:356
 msgid "Map DHCP user class to tag."
 msgstr "Associe les classes d'utilisateurs ('user class') DHCP aux options."
 
-#: option.c:266
+#: option.c:357
 msgid "Map RFC3046 circuit-id to tag."
 msgstr "Associe les identifiants de circuits RFC3046 ('circuit-id') aux options"
 
-#: option.c:267
+#: option.c:358
 msgid "Map RFC3046 remote-id to tag."
 msgstr "Associe les identifiants distants RFC3046 ('remote-id') aux options"
 
-#: option.c:268
+#: option.c:359
 msgid "Map RFC3993 subscriber-id to tag."
 msgstr "Associe les identifiants de souscripteurs RFC3993 ('subscriber-id') aux options"
 
 #
-#: option.c:269
+#: option.c:360
 msgid "Don't do DHCP for hosts with tag set."
 msgstr "Ne pas autoriser DHCP pour les machines énumerées dans les options."
 
 #
-#: option.c:270
+#: option.c:361
 msgid "Force broadcast replies for hosts with tag set."
 msgstr "Forcer les réponses par 'broadcast' pour les machines énumerées dans les options."
 
-#: option.c:271
+#: option.c:362
 msgid "Do NOT fork into the background, do NOT run in debug mode."
 msgstr "Ne passe pas en tâche de fond, ne pas s'exécuter en mode debug."
 
-#: option.c:272
+#: option.c:363
 msgid "Assume we are the only DHCP server on the local network."
 msgstr "On considère que l'on est le seul serveur DHCP sur le réseau local."
 
-#: option.c:273
+#: option.c:364
 #, c-format
 msgid "Specify where to store DHCP leases (defaults to %s)."
 msgstr "Spécifie où il faut sauvegarder les baux DHCP (par défaut : %s)."
 
-#: option.c:274
+#: option.c:365
 msgid "Return MX records for local hosts."
 msgstr "Retourne les champs MX pour les machines locales."
 
-#: option.c:275
+#: option.c:366
 msgid "Specify an MX record."
 msgstr "Spécifie un champ MX."
 
-#: option.c:276
+#: option.c:367
 msgid "Specify BOOTP options to DHCP server."
 msgstr "Spécifie les options BOOTP pour le serveur DHCP."
 
-#: option.c:277
+#: option.c:368
 #, c-format
 msgid "Do NOT poll %s file, reload only on SIGHUP."
 msgstr "Ne pas scruter le fichier %s, ne recharger les modifications que sur réception du signal SIGHUP."
 
-#: option.c:278
+#: option.c:369
 msgid "Do NOT cache failed search results."
 msgstr "Ne place pas en cache le résultat des requêtes qui ont échouées."
 
-#: option.c:279
+#: option.c:370
 #, c-format
 msgid "Use nameservers strictly in the order given in %s."
 msgstr "Utilise les serveurs de noms dans l'ordre donné dans %s."
 
 #
-#: option.c:280
+#: option.c:371
 msgid "Specify options to be sent to DHCP clients."
 msgstr "Options supplémentaires à associer aux clients DHCP."
 
-#: option.c:281
+#: option.c:372
 msgid "DHCP option sent even if the client does not request it."
 msgstr "Option DHCP envoyée même si le client de la demande pas."
 
-#: option.c:282
+#: option.c:373
 msgid "Specify port to listen for DNS requests on (defaults to 53)."
 msgstr "Spécifie le port où il faut écouter les requêtes DNS (par défaut : 53)."
 
-#: option.c:283
+#: option.c:374
 #, c-format
 msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)."
 msgstr "Taille maximale des paquets UDP supportés pour EDNS.0 (par défaut : %s)."
 
 #
-#: option.c:284
+#: option.c:375
 msgid "Log DNS queries."
 msgstr "Enregistre les requêtes DNS dans un journal d'activité."
 
 #
-#: option.c:285
+#: option.c:376
 msgid "Force the originating port for upstream DNS queries."
 msgstr "Force le port d'origine pour les requêtes vers les serveurs amonts."
 
-#: option.c:286
+#: option.c:377
 msgid "Do NOT read resolv.conf."
 msgstr "Ne pas lire le fichier resolv.conf."
 
-#: option.c:287
+#: option.c:378
 #, c-format
 msgid "Specify path to resolv.conf (defaults to %s)."
 msgstr "Spécifie le chemin pour le fichier resolv.conf (par défaut : %s)."
 
-#: option.c:288
+#: option.c:379
+#, fuzzy
+msgid "Specify path to file with server= options"
+msgstr "Spécifie un chemin pour le fichier PID (par défaut : %s)."
+
+#: option.c:380
 msgid "Specify address(es) of upstream servers with optional domains."
 msgstr "Spécifie la ou les adresses des serveurs amonts avec des domaines optionels."
 
-#: option.c:289
+#: option.c:381
+#, fuzzy
+msgid "Specify address of upstream servers for reverse address queries"
+msgstr "Spécifie la ou les adresses des serveurs amonts avec des domaines optionels."
+
+#: option.c:382
 msgid "Never forward queries to specified domains."
 msgstr "Ne jamais retransmettre les requêtes pour les domaines spécifiés."
 
-#: option.c:290
+#: option.c:383
 msgid "Specify the domain to be assigned in DHCP leases."
 msgstr "Spécifie le domaine qui doit etre assigné aux baux DHCP."
 
-#: option.c:291
+#: option.c:384
 msgid "Specify default target in an MX record."
 msgstr "Spécifie la cible par défaut dans un champ MX."
 
-#: option.c:292
+#: option.c:385
 msgid "Specify time-to-live in seconds for replies from /etc/hosts."
 msgstr "Spécifie le TTL en secondes pour les réponses qui utilisent /etc/hosts."
 
 #
-#: option.c:293
+#: option.c:386
 msgid "Specify time-to-live in seconds for negative caching."
 msgstr "Spécifie le TTL en secondes pour les réponses qui utilisent /etc/hosts."
 
-#: option.c:294
+#: option.c:387
 msgid "Specify time-to-live in seconds for maximum TTL to send to clients."
 msgstr "Spécifie, en secondes, la valeur maximum de TTL à renvoyer aux clients."
 
-#: option.c:295
+#
+#: option.c:388
+#, fuzzy
+msgid "Specify time-to-live ceiling for cache."
+msgstr "Spécifie le TTL en secondes pour les réponses qui utilisent /etc/hosts."
+
+#
+#: option.c:389
+#, fuzzy
+msgid "Specify time-to-live floor for cache."
+msgstr "Spécifie le TTL en secondes pour les réponses qui utilisent /etc/hosts."
+
+#: option.c:390
 #, c-format
 msgid "Change to this user after startup. (defaults to %s)."
 msgstr "Change pour cet utilisateur après le démarrage (par défaut : %s)."
 
 #
-#: option.c:296
+#: option.c:391
 msgid "Map DHCP vendor class to tag."
 msgstr "Associe les classes de fournisseurs ('vendor class') DHCP aux options."
 
-#: option.c:297
+#: option.c:392
 msgid "Display dnsmasq version and copyright information."
 msgstr "Affiche la version de Dnsmasq et les informations liées au copyright."
 
-#: option.c:298
+#: option.c:393
 msgid "Translate IPv4 addresses from upstream servers."
 msgstr "Traduit les adresses IPV4 des serveurs amonts."
 
-#: option.c:299
+#: option.c:394
 msgid "Specify a SRV record."
 msgstr "Spécifie un champ SRV."
 
-#: option.c:300
+#: option.c:395
 msgid "Display this message. Use --help dhcp for known DHCP options."
 msgstr "Afficher ce message. Utiliser --help dhcp pour obtenir la liste des options DHCP connues."
 
-#: option.c:301
+#: option.c:396
 #, c-format
 msgid "Specify path of PID file (defaults to %s)."
 msgstr "Spécifie un chemin pour le fichier PID (par défaut : %s)."
 
-#: option.c:302
+#: option.c:397
 #, c-format
 msgid "Specify maximum number of DHCP leases (defaults to %s)."
 msgstr "Spécifie le nombre maximum de baux DHCP (par défaut : %s)."
 
-#: option.c:303
+#: option.c:398
 msgid "Answer DNS queries based on the interface a query was sent to."
 msgstr "Repond aux requêtes DNS en se basant sur l'interface ou a été envoyée la requête."
 
-#: option.c:304
+#: option.c:399
 msgid "Specify TXT DNS record."
 msgstr "Spécifie un champ DNS TXT"
 
 #
-#: option.c:305
+#: option.c:400
 msgid "Specify PTR DNS record."
 msgstr "Spécifie un champ DNS PTR"
 
-#: option.c:306
+#: option.c:401
 msgid "Give DNS name to IPv4 address of interface."
 msgstr "Donne le nom DNS pour l'adresse IPv4 de l'interface."
 
-#: option.c:307
+#: option.c:402
 msgid "Bind only to interfaces in use."
 msgstr "Association uniquement aux interfaces réseau actuellement actives."
 
-#: option.c:308
+#: option.c:403
 #, c-format
 msgid "Read DHCP static host information from %s."
 msgstr "Lecture des informations de DHCP statique à partir de %s."
 
-#: option.c:309
+#: option.c:404
 msgid "Enable the DBus interface for setting upstream servers, etc."
 msgstr "Autorise l'interface DBus pour la configuration des serveurs amonts, etc."
 
-#: option.c:310
+#: option.c:405
 msgid "Do not provide DHCP on this interface, only provide DNS."
 msgstr "Ne pas assurer de fonction DHCP sur cette interface, mais seulement la fonction DNS."
 
-#: option.c:311
+#: option.c:406
 msgid "Enable dynamic address allocation for bootp."
 msgstr "Autorise l'allocation dynamique d'adresse pour bootp."
 
 #
-#: option.c:312
+#: option.c:407
 msgid "Map MAC address (with wildcards) to option set."
 msgstr "Associe l'adresse MAC (avec les jokers) aux options."
 
-#: option.c:313
+#: option.c:408
 msgid "Treat DHCP requests on aliases as arriving from interface."
 msgstr "Traiter les requêtes DHCP sur les alias comme arrivant de l'interface."
 
-#: option.c:314
+#: option.c:409
 msgid "Disable ICMP echo address checking in the DHCP server."
 msgstr "Supprime la vérification d'adresse sur le serveur au moyen de paquets ICMP echo"
 
-#: option.c:315
-msgid "Script to run on DHCP lease creation and destruction."
-msgstr "Script à exécuter lors de la création ou destruction de bail DHCP."
+#: option.c:410
+msgid "Shell script to run on DHCP lease creation and destruction."
+msgstr "Script shell à exécuter lors de la création ou destruction de bail DHCP."
 
-#: option.c:316
+#: option.c:411
+msgid "Lua script to run on DHCP lease creation and destruction."
+msgstr "Script Lua à exécuter lors de la création ou destruction de bail DHCP."
+
+#: option.c:412
+msgid "Run lease-change scripts as this user."
+msgstr "Lancer le script 'lease-change' avec cet utilisateur."
+
+#: option.c:413
 msgid "Read configuration from all the files in this directory."
 msgstr "Lecture de la configuration dans tous les fichiers de ce répertoire."
 
 #
-#: option.c:317
+#: option.c:414
 msgid "Log to this syslog facility or file. (defaults to DAEMON)"
 msgstr "Enregistrer les journaux d'activité dans cette facilité syslog. (défaut : DAEMON)"
 
-#: option.c:318
+#: option.c:415
 msgid "Do not use leasefile."
 msgstr "Ne pas utiliser de fichier de baux."
 
-#: option.c:319
+#: option.c:416
 #, c-format
 msgid "Maximum number of concurrent DNS queries. (defaults to %s)"
 msgstr "Spécifie le nombre maximum de requêtes DHCP concurrentes (par défaut : %s)."
 
-#: option.c:320
+#: option.c:417
 #, c-format
 msgid "Clear DNS cache when reloading %s."
 msgstr "Vider le cache DNS lors du rechargement de %s."
 
-#: option.c:321
+#: option.c:418
 msgid "Ignore hostnames provided by DHCP clients."
 msgstr "Ignorer les noms d'hôtes fournis par les clients DHCP"
 
-#: option.c:322
+#: option.c:419
 msgid "Do NOT reuse filename and server fields for extra DHCP options."
 msgstr "Ne pas réutiliser les champs nom de fichier et serveur dans les options DHCP supplémentaires."
 
-#: option.c:323
+#: option.c:420
 msgid "Enable integrated read-only TFTP server."
 msgstr "Activer le server TFTP intégré (fonctionnant en lecture seulement)"
 
-#: option.c:324
+#: option.c:421
 msgid "Export files by TFTP only from the specified subtree."
 msgstr "N'exporter par TFTP que les fichiers de l'arborescence de fichier spécifiée"
 
-#: option.c:325
+#: option.c:422
 msgid "Add client IP address to tftp-root."
 msgstr "Ajouter les adresses IP clientes à la racine tftp ('tftp-root')."
 
-#: option.c:326
+#: option.c:423
 msgid "Allow access only to files owned by the user running dnsmasq."
 msgstr "Accès aux seuls fichiers appartenants à l'utilisateur sous lequel tourne dnsmasq"
 
-#: option.c:327
+#: option.c:424
+msgid "Do not terminate the service if TFTP directories are inaccessible."
+msgstr ""
+
+#: option.c:425
 #, c-format
 msgid "Maximum number of conncurrent TFTP transfers (defaults to %s)."
 msgstr "Spécifie le nombre maximum de transfert TFTP concurrents (défaut : %s)."
 
-#: option.c:328
+#: option.c:426
 msgid "Disable the TFTP blocksize extension."
 msgstr "Désactivation de l'extension TFTP « taille de bloc »"
 
-#: option.c:329
+#: option.c:427
+msgid "Convert TFTP filenames to lowercase"
+msgstr "Convertis les noms de fichiers TFTP en minuscule"
+
+#: option.c:428
 msgid "Ephemeral port range for use by TFTP transfers."
 msgstr "Gamme de ports dans laquelle seront choisis les ports temporaires utilisés dans les transferts TFTP."
 
-#: option.c:330
+#: option.c:429
 msgid "Extra logging for DHCP."
 msgstr "Traces supplémentaires pour le DHCP."
 
-#: option.c:331
+#: option.c:430
 msgid "Enable async. logging; optionally set queue length."
 msgstr "Active l'écriture de traces en mode asynchrone. Peut prendre en option la valeur de la longueur de la queue."
 
-#: option.c:332
+#: option.c:431
 msgid "Stop DNS rebinding. Filter private IP ranges when resolving."
 msgstr "Stopper la réassociation DNS ('DNS rebinding'). Filtre les gammes d'adresses IP privées lors de la résolution."
 
-#: option.c:333
+#: option.c:432
 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers."
 msgstr "Autorise la réassociation de 127.0.0/8, pour les serveurs RBL (Realtime Blackhole List)"
 
-#: option.c:334
+#: option.c:433
 msgid "Inhibit DNS-rebind protection on this domain."
 msgstr "Désactive la protection contre les réassociation DNS pour ce domaine"
 
-#: option.c:335
+#: option.c:434
 msgid "Always perform DNS queries to all servers."
 msgstr "Toujours effectuer les requêtes DNS à tous les serveurs."
 
 #
-#: option.c:336
+#: option.c:435
 msgid "Set tag if client includes matching option in request."
 msgstr "Spécifie le label si le client inclus l'option dans la requête."
 
-#: option.c:337
+#: option.c:436
 msgid "Use alternative ports for DHCP."
 msgstr "Utiliser des ports alternatifs pour le DHCP."
 
-#: option.c:338
-msgid "Run lease-change script as this user."
-msgstr "Lancer le script 'lease-change' avec cet utilisateur."
-
 #
-#: option.c:339
+#: option.c:437
 msgid "Specify NAPTR DNS record."
 msgstr "Spécifie un champ DNS NAPTR."
 
-#: option.c:340
+#: option.c:438
 msgid "Specify lowest port available for DNS query transmission."
 msgstr "Définie le plus petit port utilisé pour la transmission d'une requête DNS."
 
-#: option.c:341
+#: option.c:439
 msgid "Use only fully qualified domain names for DHCP clients."
 msgstr "Utilise seulement les noms de domaine pleinement qualifiés pour les clients DHCP."
 
-#: option.c:342
+#: option.c:440
 msgid "Generate hostnames based on MAC address for nameless clients."
 msgstr "Génère les noms d'hôtes à partir de l'adresse MAC pour les clients sans nom."
 
-#: option.c:343
+#: option.c:441
 msgid "Use these DHCP relays as full proxies."
 msgstr "Utilise ces relais DHCP en temps que proxy complets."
 
-#: option.c:344
+#: option.c:442
+msgid "Relay DHCP requests to a remote server"
+msgstr "Requêtes de relais DHCP à un serveur distant"
+
+#: option.c:443
 msgid "Specify alias name for LOCAL DNS name."
 msgstr "Spécifie un alias pour un nom DNS local."
 
 #
-#: option.c:345
+#: option.c:444
 msgid "Prompt to send to PXE clients."
 msgstr "Invite à envoyer aux clients PXE."
 
-#: option.c:346
+#: option.c:445
 msgid "Boot service for PXE menu."
 msgstr "Service de démarrage pour menu PXE."
 
-#: option.c:347
+#: option.c:446
 msgid "Check configuration syntax."
 msgstr "vérification de la syntaxe de la configuration."
 
-#: option.c:348
-msgid "Add requestor's MAC address to forwarded DNS queries"
+#: option.c:447
+msgid "Add requestor's MAC address to forwarded DNS queries."
 msgstr "Ajoute l'adresse MAC du requêteur aux requêtes DNS transmises"
 
-#: option.c:349
-msgid "Proxy DNSSEC validation results from upstream nameservers"
-msgstr "Transmet les résultats de validation DNSSEC des serveurs amonts"
+#: option.c:448
+#, fuzzy
+msgid "Add requestor's IP subnet to forwarded DNS queries."
+msgstr "Ajoute l'adresse MAC du requêteur aux requêtes DNS transmises"
+
+#: option.c:449
+msgid "Proxy DNSSEC validation results from upstream nameservers."
+msgstr "Copie dans la réponse DNS le résultat de la validation DNSSEC effectuée par les serveurs DNS amonts."
+
+#: option.c:450
+msgid "Attempt to allocate sequential IP addresses to DHCP clients."
+msgstr "Essaie d'allouer des adresses IP séquentielles aux clients DHCP."
+
+#: option.c:451
+msgid "Copy connection-track mark from queries to upstream connections."
+msgstr "Copie les marques de suivi de connexion pour les requêtes amont."
+
+#: option.c:452
+msgid "Allow DHCP clients to do their own DDNS updates."
+msgstr "Autoriser les clients DHCP à faire leurs propres mises à jour DDNS (Dynamic DNS)"
+
+#: option.c:453
+msgid "Send router-advertisements for interfaces doing DHCPv6"
+msgstr "Envoyer des annonces de routeurs pour toutes les interfaces faisant du DHCPv6"
+
+#: option.c:454
+msgid "Specify DUID_EN-type DHCPv6 server DUID"
+msgstr "Spécifie pour le serveur DHCPv6 un identifiant unique DHCP (DUID) basé sur un numéro unique de vendeur (DUID_EN)"
+
+#: option.c:455
+msgid "Specify host (A/AAAA and PTR) records"
+msgstr "Spécifie les enregistrements (A/AAAA et PTR) d'un hôte."
+
+#: option.c:456
+msgid "Specify arbitrary DNS resource record"
+msgstr "Définie une resource DNS d'un type spécifique"
+
+#: option.c:457
+msgid "Bind to interfaces in use - check for new interfaces"
+msgstr "Se lie aux interfaces préexistantes - vérifie l'apparition de nouvelles interfaces"
+
+#: option.c:458
+msgid "Export local names to global DNS"
+msgstr "Exporte les noms locaux dans le DNS global"
+
+#: option.c:459
+msgid "Domain to export to global DNS"
+msgstr "Domaine à exporter dans le DNS global"
+
+#: option.c:460
+msgid "Set TTL for authoritative replies"
+msgstr "Configure la durée de vie (Time To Live) pour les réponses faisant autorité"
+
+#: option.c:461
+msgid "Set authoritive zone information"
+msgstr "Configure les informations pour une zone de nom faisant autorité"
+
+#: option.c:462
+msgid "Secondary authoritative nameservers for forward domains"
+msgstr "Serveurs de noms secondaires faisant autorité pour les domaines délégués"
+
+#: option.c:463
+msgid "Peers which are allowed to do zone transfer"
+msgstr "Pairs autorisés à faire des transferts de zone"
+
+#: option.c:464
+msgid "Specify ipsets to which matching domains should be added"
+msgstr "Spécifie les ipsets auxquels les domaines correspondants doivent-être ajoutés"
+
+#: option.c:465
+#, fuzzy
+msgid "Specify a domain and address range for synthesised names"
+msgstr "Spécifie un domaine et une plage d'adresses pour les noms auto-générés"
+
+#: option.c:466
+msgid "Activate DNSSEC validation"
+msgstr ""
+
+#: option.c:467
+msgid "Specify trust anchor key digest."
+msgstr ""
+
+#: option.c:468
+msgid "Disable upstream checking for DNSSEC debugging."
+msgstr ""
+
+#: option.c:469
+msgid "Ensure answers without DNSSEC are in unsigned zones."
+msgstr ""
+
+#: option.c:470
+msgid "Don't check DNSSEC signature timestamps until first cache-reload"
+msgstr ""
+
+#: option.c:471
+msgid "Timestamp file to verify system clock for DNSSEC"
+msgstr ""
+
+#: option.c:473
+msgid "Specify DHCPv6 prefix class"
+msgstr "Spécifie le préfixe de classe DHCPv6"
+
+#: option.c:475
+msgid "Set priority, resend-interval and router-lifetime"
+msgstr ""
+
+#: option.c:476
+msgid "Do not log routine DHCP."
+msgstr ""
+
+#: option.c:477
+msgid "Do not log routine DHCPv6."
+msgstr ""
+
+#: option.c:478
+msgid "Do not log RA."
+msgstr ""
+
+#: option.c:479
+msgid "Accept queries only from directly-connected networks"
+msgstr ""
 
-#: option.c:638
+#: option.c:480
+msgid "Detect and remove DNS forwarding loops"
+msgstr ""
+
+#: option.c:481
+msgid "Ignore DNS responses containing ipaddr."
+msgstr ""
+
+#: option.c:683
 #, c-format
 msgid ""
 "Usage: dnsmasq [options]\n"
@@ -560,929 +753,1459 @@ msgstr ""
 "Usage : dnsmasq [options]\n"
 "\n"
 
-#: option.c:640
+#: option.c:685
 #, c-format
 msgid "Use short options only on the command line.\n"
 msgstr "Utilisez les options courtes uniquement sur la ligne de commande.\n"
 
-#: option.c:642
+#: option.c:687
 #, c-format
 msgid "Valid options are:\n"
 msgstr "Les options valides sont :\n"
 
-#: option.c:683
-#, c-format
-msgid "Known DHCP options:\n"
-msgstr "Options DHCP connues :\n"
+#: option.c:744 option.c:748
+msgid "bad port"
+msgstr "numéro de port incorrect"
 
-#: option.c:798
+#: option.c:775 option.c:807
+msgid "interface binding not supported"
+msgstr "association d'interface non supportée"
+
+#
+#: option.c:784 option.c:3575
+msgid "bad interface name"
+msgstr "nom d'interface invalide"
+
+#
+#: option.c:814
+msgid "bad address"
+msgstr "mauvaise adresse"
+
+#: option.c:996
+msgid "unsupported encapsulation for IPv6 option"
+msgstr "encapsulation d'option non supportée pour IPv6"
+
+#: option.c:1010
 msgid "bad dhcp-option"
 msgstr "mauvaise valeur de 'dhcp-option'"
 
 #
-#: option.c:860
+#: option.c:1078
 msgid "bad IP address"
 msgstr "mauvaise adresse IP"
 
-#: option.c:968
+#
+#: option.c:1081 option.c:1219 option.c:2893
+msgid "bad IPv6 address"
+msgstr "mauvaise adresse IPv6"
+
+#: option.c:1246 option.c:1340
 msgid "bad domain in dhcp-option"
 msgstr "mauvais domaine dans dhcp-option"
 
-#: option.c:1034
+#: option.c:1378
 msgid "dhcp-option too long"
 msgstr "dhcp-option trop long"
 
-#: option.c:1043
+#: option.c:1385
 msgid "illegal dhcp-match"
 msgstr "valeur illégale pour 'dhcp-match'"
 
-#: option.c:1087
+#: option.c:1447
 msgid "illegal repeated flag"
 msgstr "Une option ne pouvant être spécifié qu'une seule fois à été donnée plusieurs fois"
 
-#: option.c:1095
+#: option.c:1455
 msgid "illegal repeated keyword"
 msgstr "Mot-clef ne pouvant être répété"
 
-#: option.c:1147 option.c:3030
+#: option.c:1520 option.c:4191
 #, c-format
 msgid "cannot access directory %s: %s"
 msgstr "Ne peut pas lire le répertoire %s : %s"
 
-#: option.c:1178 tftp.c:460
+#: option.c:1566 tftp.c:493
 #, c-format
 msgid "cannot access %s: %s"
 msgstr "Ne peut pas lire %s : %s"
 
-#: option.c:1207
+#: option.c:1618
 msgid "setting log facility is not possible under Android"
-msgstr ""
+msgstr "Sous android, impossible de positionner la cible (facility) pour les traces (logs)."
 
-#: option.c:1216
+#: option.c:1627
 msgid "bad log facility"
-msgstr ""
+msgstr "Mauvaise cible (facility) pour les traces."
 
-#: option.c:1265
+#: option.c:1680
 msgid "bad MX preference"
 msgstr "préference MX incorrecte"
 
-#: option.c:1270
+#: option.c:1685
 msgid "bad MX name"
 msgstr "nom MX incorrect"
 
-#: option.c:1284
+#: option.c:1699
 msgid "bad MX target"
 msgstr "valeur MX cible incorrecte"
 
-#: option.c:1294
+#: option.c:1711
 msgid "cannot run scripts under uClinux"
 msgstr "ne peut exécuter de script sous uClinux"
 
-#: option.c:1296
+#: option.c:1713
 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts"
-msgstr "pour permettre l'exécution de scripts au changement de bail (lease-change), recompiler en définissant HAVE_SCRIPT"
+msgstr "recompiler en définissant HAVE_SCRIPT pour permettre l'exécution de scripts shell au changement de bail (lease-change)"
 
-#: option.c:1597 option.c:1601
-msgid "bad port"
-msgstr "numéro de port incorrect"
+#: option.c:1717
+msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts"
+msgstr "recompiler en définissant HAVE_LUASCRIPT pour permettre l'exécution de scripts LUA au changement de bail (lease-change)"
 
-#: option.c:1620 option.c:1645
-msgid "interface binding not supported"
-msgstr "association d'interface non supportée"
+#: option.c:1973 option.c:2018 option.c:2074
+msgid "bad prefix"
+msgstr "mauvais préfixe"
+
+#: option.c:2355
+msgid "recompile with HAVE_IPSET defined to enable ipset directives"
+msgstr "recompiler en définissant HAVE_IPSET pour permettre l'utilisation de directives de groupes d'IP (IPset)"
 
 #
-#: option.c:1791
+#: option.c:2548
 msgid "bad port range"
 msgstr "gamme de ports incorrecte"
 
-#: option.c:1808
+#: option.c:2564
 msgid "bad bridge-interface"
 msgstr "interface-pont incorrecte"
 
-#: option.c:1850
-msgid "bad dhcp-range"
-msgstr "plage d'adresses DHCP (dhcp-range) incorrecte"
-
-#: option.c:1878
+#: option.c:2624
 msgid "only one tag allowed"
 msgstr "une seule étiquette est autorisée"
 
-#: option.c:1925
+#: option.c:2644 option.c:2656 option.c:2764 option.c:2805
+msgid "bad dhcp-range"
+msgstr "plage d'adresses DHCP (dhcp-range) incorrecte"
+
+#: option.c:2671
 msgid "inconsistent DHCP range"
 msgstr "plage d'adresses DHCP incohérente"
 
-#: option.c:2019 option.c:2045
+#: option.c:2732
+msgid "prefix length must be exactly 64 for RA subnets"
+msgstr "la taille du préfixe doit être exactement 64 pour les sous-réseaux d'annonces de routeurs (RA)"
+
+#: option.c:2734
+msgid "prefix length must be exactly 64 for subnet constructors"
+msgstr "la taille du préfixe doit être exactement 64 pour le constructeur de sous-réseaux"
+
+#: option.c:2738
+msgid "prefix length must be at least 64"
+msgstr "la taille de préfixe doit être au minimum 64"
+
+#: option.c:2741
+msgid "inconsistent DHCPv6 range"
+msgstr "plage d'adresses DHCPv6 incohérente"
+
+#: option.c:2752
+msgid "prefix must be zero with \"constructor:\" argument"
+msgstr "le préfixe doit avoir une taille de 0 lorsque l'argument \"constructor:\" est utilisé"
+
+#: option.c:2863 option.c:2911
 msgid "bad hex constant"
 msgstr "mauvaise constante hexadecimale"
 
+#: option.c:2885
+msgid "cannot match tags in --dhcp-host"
+msgstr "L'utilisation de labels est prohibée dans --dhcp-host"
+
+#: option.c:2933
+#, c-format
+msgid "duplicate dhcp-host IP address %s"
+msgstr "adresse IP dhcp-host dupliquée dans %s."
+
 #
-#: option.c:2107
+#: option.c:2991
 msgid "bad DHCP host name"
 msgstr "nom d'hôte DHCP incorrect"
 
-#: option.c:2188
+#: option.c:3073
 msgid "bad tag-if"
 msgstr "mauvaise étiquette tag-if"
 
-#: option.c:2467 option.c:2752
+#: option.c:3397 option.c:3791
 msgid "invalid port number"
 msgstr "numéro de port invalide"
 
 #
-#: option.c:2529
+#: option.c:3459
 msgid "bad dhcp-proxy address"
 msgstr "adresse dhcp-proxy incorrecte"
 
+#: option.c:3485
+msgid "Bad dhcp-relay"
+msgstr "valeur incorrecte pour le relais DHCP (dhcp-relay)"
+
+#: option.c:3511
+msgid "bad RA-params"
+msgstr ""
+
+#: option.c:3520
+msgid "bad DUID"
+msgstr "mauvais identifiant unique DHCP (DUID)"
+
 #
-#: option.c:2569
+#: option.c:3562
 msgid "invalid alias range"
 msgstr "poids invalide"
 
-#
-#: option.c:2582
-msgid "bad interface name"
-msgstr "nom d'interface invalide"
-
-#: option.c:2607
+#: option.c:3616
 msgid "bad CNAME"
 msgstr "mauvais CNAME"
 
-#: option.c:2612
+#: option.c:3621
 msgid "duplicate CNAME"
 msgstr "ce CNAME existe déja"
 
 #
-#: option.c:2632
+#: option.c:3641
 msgid "bad PTR record"
 msgstr "mauvais champ PTR"
 
 #
-#: option.c:2663
+#: option.c:3672
 msgid "bad NAPTR record"
 msgstr "mauvais champ NAPTR"
 
-#: option.c:2695
+#
+#: option.c:3706
+msgid "bad RR record"
+msgstr "mauvais enregistrement RR"
+
+#: option.c:3736
 msgid "bad TXT record"
 msgstr "champ TXT invalide"
 
-#: option.c:2738
+#: option.c:3777
 msgid "bad SRV record"
 msgstr "champ SRV invalide"
 
-#: option.c:2745
+#: option.c:3784
 msgid "bad SRV target"
 msgstr "cible SRV invalide"
 
-#: option.c:2759
+#: option.c:3798
 msgid "invalid priority"
 msgstr "priorité invalide"
 
-#: option.c:2766
+#: option.c:3805
 msgid "invalid weight"
 msgstr "poids invalide"
 
-#: option.c:2785
-msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"
+#
+#: option.c:3829
+msgid "Bad host-record"
+msgstr "mauvais champ host-record"
+
+#: option.c:3846
+msgid "Bad name in host-record"
+msgstr "mauvais nom dans le champ host-record"
+
+#
+#: option.c:3911
+#, fuzzy
+msgid "bad trust anchor"
+msgstr "gamme de ports incorrecte"
+
+#: option.c:3925
+msgid "bad HEX in trust anchor"
+msgstr ""
+
+#: option.c:3935
+#, fuzzy
+msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"
 msgstr "option non supportée (vérifier que Dnsmasq a été compilé avec le support DHCP/TFTP/DBus)"
 
-#: option.c:2849
+#: option.c:3994
 msgid "missing \""
 msgstr "il manque \""
 
-#: option.c:2908
+#: option.c:4051
 msgid "bad option"
 msgstr "mauvaise option"
 
-#: option.c:2910
+#: option.c:4053
 msgid "extraneous parameter"
 msgstr "paramètre en trop"
 
-#: option.c:2912
+#: option.c:4055
 msgid "missing parameter"
 msgstr "paramètre manquant"
 
-#: option.c:2916
+#: option.c:4057
+#, fuzzy
+msgid "illegal option"
+msgstr "mauvaise option"
+
+#: option.c:4064
 msgid "error"
 msgstr "erreur"
 
-#: option.c:2921
+#: option.c:4066
 #, c-format
-msgid "%s at line %d of %%s"
-msgstr "%s à la ligne %d de %%s"
+msgid " at line %d of %s"
+msgstr "à la ligne %d de %s"
 
-#: option.c:2985 tftp.c:624
-#, c-format
-msgid "cannot read %s: %s"
-msgstr "Ne peut pas lire %s : %s"
-
-#: option.c:3151 option.c:3187
+#: option.c:4081 option.c:4328 option.c:4364
 #, c-format
 msgid "read %s"
 msgstr "Lecture de %s"
 
-#: option.c:3239
+#: option.c:4144 option.c:4267 tftp.c:667
+#, c-format
+msgid "cannot read %s: %s"
+msgstr "Ne peut pas lire %s : %s"
+
+#: option.c:4430
 msgid "junk found in command line"
 msgstr "la ligne de commande contient des éléments indésirables ou incompréhensibles"
 
-#: option.c:3269
+#: option.c:4465
 #, c-format
 msgid "Dnsmasq version %s  %s\n"
 msgstr "Version de Dnsmasq %s  %s\n"
 
-#: option.c:3270
+#: option.c:4466
 #, c-format
 msgid ""
-"Compile time options %s\n"
+"Compile time options: %s\n"
 "\n"
 msgstr ""
 "Options à la compilation %s\n"
 "\n"
 
-#: option.c:3271
+#: option.c:4467
 #, c-format
 msgid "This software comes with ABSOLUTELY NO WARRANTY.\n"
 msgstr "Ce logiciel est fourni sans AUCUNE GARANTIE.\n"
 
-#: option.c:3272
+#: option.c:4468
 #, c-format
 msgid "Dnsmasq is free software, and you are welcome to redistribute it\n"
 msgstr "Dnsmasq est un logiciel libre, il vous est permis de le redistribuer\n"
 
-#: option.c:3273
+#: option.c:4469
 #, c-format
 msgid "under the terms of the GNU General Public License, version 2 or 3.\n"
 msgstr "sous les termes de la licence GPL (GNU General Public License), version 2 ou 3.\n"
 
-#: option.c:3284
+#: option.c:4480
 msgid "try --help"
 msgstr "essayez avec --help"
 
-#: option.c:3286
+#: option.c:4482
 msgid "try -w"
 msgstr "essayez avec -w"
 
-#: option.c:3289
+#: option.c:4484
 #, c-format
 msgid "bad command line options: %s"
 msgstr "mauvaises options en ligne de commande : %s."
 
-#: option.c:3330
+#: option.c:4541
 #, c-format
 msgid "cannot get host-name: %s"
 msgstr "ne peut pas obtenir le nom de la machine : %s"
 
-#: option.c:3358
+#: option.c:4569
 msgid "only one resolv.conf file allowed in no-poll mode."
 msgstr "seul un fichier resolv.conf est autorisé dans le mode no-poll"
 
-#: option.c:3368
+#: option.c:4579
 msgid "must have exactly one resolv.conf to read domain from."
 msgstr "un fichier resolv.conf (et un seul) est nécessaire pour y récuperer le nom de domaine."
 
-#: option.c:3371 network.c:848 dhcp.c:814
+#: option.c:4582 network.c:1507 dhcp.c:777
 #, c-format
 msgid "failed to read %s: %s"
 msgstr "impossible de lire %s : %s"
 
-#: option.c:3388
+#: option.c:4599
 #, c-format
 msgid "no search directive found in %s"
 msgstr "pas de directive de recherche trouvée dans %s"
 
-#: option.c:3409
+#: option.c:4620
 msgid "there must be a default domain when --dhcp-fqdn is set"
 msgstr "un domaine par défaut doit être spécifié lorsque l'option --dhcp-fqdn est utilisée"
 
-#: option.c:3413
+#: option.c:4629
 msgid "syntax check OK"
 msgstr "vérification de syntaxe OK"
 
-#: forward.c:461
+#: forward.c:111
+#, c-format
+msgid "failed to send packet: %s"
+msgstr "impossible d'envoyer le paquet : %s"
+
+#: forward.c:591
+msgid "discarding DNS reply: subnet option mismatch"
+msgstr ""
+
+#: forward.c:614
 #, c-format
 msgid "nameserver %s refused to do a recursive query"
 msgstr "le serveur de nom %s a refusé de faire une recherche récursive"
 
-#: forward.c:489
+#: forward.c:646
 #, c-format
 msgid "possible DNS-rebind attack detected: %s"
 msgstr "détection d'une possible attaque de type DNS-rebind: %s"
 
-#: network.c:171
+#: forward.c:1209 forward.c:1815
+msgid "Ignoring query from non-local network"
+msgstr ""
+
+#: forward.c:2286
 #, c-format
-msgid "unknown interface %s in bridge-interface"
-msgstr "interface %s inconnue spécifiée comme interface de pont"
+msgid "Maximum number of concurrent DNS queries reached (max: %d)"
+msgstr "Nombre maximum de requêtes DNS concurrentes atteint (maximum : %d)."
 
-#: network.c:380
+#: network.c:715
 #, c-format
 msgid "failed to create listening socket for %s: %s"
 msgstr "impossible de créer une socket d'écoute pour %s : %s"
 
-#: network.c:746
+#: network.c:1021
+#, c-format
+msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s"
+msgstr ""
+
+#: network.c:1028
+msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)"
+msgstr ""
+
+#: network.c:1037
+#, fuzzy, c-format
+msgid "warning: no addresses found for interface %s"
+msgstr "utilise les adresses locales seulement pour %s %s"
+
+#: network.c:1095
+#, c-format
+msgid "interface %s failed to join DHCPv6 multicast group: %s"
+msgstr "impossible de faire rejoindre l'interface %s dans le groupe multicast DHCPv6 %s"
+
+#: network.c:1289
 #, c-format
 msgid "failed to bind server socket for %s: %s"
 msgstr "impossible de lier la socket de serveur pour %s : %s"
 
-#: network.c:783
+#: network.c:1445
 #, c-format
 msgid "ignoring nameserver %s - local interface"
 msgstr "ignore le serveur de nom %s - interface locale"
 
-#: network.c:794
+#: network.c:1456
 #, c-format
 msgid "ignoring nameserver %s - cannot make/bind socket: %s"
 msgstr "ignore le serveur de nom %s - ne peut construire/lier la socket : %m"
 
-#: network.c:811
+#: network.c:1469
 msgid "unqualified"
 msgstr "non-qualifié(e)"
 
-#: network.c:811
+#: network.c:1469
 msgid "names"
 msgstr "noms"
 
-#: network.c:813
+#: network.c:1471
 msgid "default"
 msgstr "défaut"
 
-#: network.c:815
+#: network.c:1473
 msgid "domain"
 msgstr "domaine"
 
-#: network.c:818
+#: network.c:1476
 #, c-format
 msgid "using local addresses only for %s %s"
 msgstr "utilise les adresses locales seulement pour %s %s"
 
-#: network.c:820
+#: network.c:1478
 #, c-format
 msgid "using standard nameservers for %s %s"
 msgstr "utilisation des serveurs de nom standards pour %s %s"
 
-#: network.c:822
+#: network.c:1480
 #, c-format
 msgid "using nameserver %s#%d for %s %s"
 msgstr "utilise le serveur de nom %s#%d pour %s %s"
 
-#: network.c:825
+#: network.c:1484
+#, fuzzy, c-format
+msgid "NOT using nameserver %s#%d - query loop detected"
+msgstr "utilise le serveur de nom %s#%d pour %s %s"
+
+#: network.c:1487
 #, c-format
 msgid "using nameserver %s#%d(via %s)"
 msgstr "utilise le serveur de nom %s#%d (via %s)"
 
-#: network.c:827
+#: network.c:1489
 #, c-format
 msgid "using nameserver %s#%d"
 msgstr "utilise le serveur de nom %s#%d"
 
+#: dnsmasq.c:163
+msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"
+msgstr ""
+
+#: dnsmasq.c:170
+msgid "no trust anchors provided for DNSSEC"
+msgstr ""
+
+#: dnsmasq.c:173
+msgid "cannot reduce cache size from default when DNSSEC enabled"
+msgstr ""
+
+#: dnsmasq.c:175
+#, fuzzy
+msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h"
+msgstr "DBus n'est pas disponible : activez HAVE_DBUS dans src/config.h"
+
 #
-#: dnsmasq.c:148
+#: dnsmasq.c:181
 msgid "TFTP server not available: set HAVE_TFTP in src/config.h"
 msgstr "TFTP n'est pas disponible : activez HAVE_TFTP dans src/config.h"
 
-#: dnsmasq.c:153
+#: dnsmasq.c:186
+#, fuzzy
+msgid "cannot use --conntrack AND --query-port"
+msgstr "impossible d'utiliser conjointement --conntrack et --query-port"
+
+#
+#: dnsmasq.c:189
+#, fuzzy
+msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h"
+msgstr "Support de suivi de connexion non disponible : activez HAVE_CONNTRACK dans src/config.h"
+
+#: dnsmasq.c:194
 msgid "asychronous logging is not available under Solaris"
 msgstr "l'écriture de traces en mode asynchrone n'est pas disponible sous Solaris."
 
-#: dnsmasq.c:158
-#, fuzzy
+#: dnsmasq.c:199
 msgid "asychronous logging is not available under Android"
-msgstr "l'écriture de traces en mode asynchrone n'est pas disponible sous Solaris."
+msgstr "l'écriture de traces en mode asynchrone n'est pas disponible sous Android."
+
+#: dnsmasq.c:204
+msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h"
+msgstr "le mode « autorité DNS » n'est pas disponible : activez HAVE_AUTH dans src/config.h"
+
+#
+#: dnsmasq.c:209
+#, fuzzy
+msgid "loop detection not available: set HAVE_LOOP in src/config.h"
+msgstr "TFTP n'est pas disponible : activez HAVE_TFTP dans src/config.h"
+
+#: dnsmasq.c:217
+msgid "zone serial must be configured in --auth-soa"
+msgstr "le numéro de série de la zone doit être configuré dans --auth-soa"
 
-#: dnsmasq.c:177
+#: dnsmasq.c:235
+msgid "dhcp-range constructor not available on this platform"
+msgstr "le constructeur de plage dhcp n'est pas disponible sur cette plate-forme"
+
+#: dnsmasq.c:278
+msgid "cannot set --bind-interfaces and --bind-dynamic"
+msgstr "--bind-interfaces et --bind-dynamic sont mutuellement exclusives"
+
+#: dnsmasq.c:281
 #, c-format
 msgid "failed to find list of interfaces: %s"
 msgstr "impossible de trouver la liste des interfaces : %s"
 
-#: dnsmasq.c:185
+#: dnsmasq.c:290
 #, c-format
 msgid "unknown interface %s"
 msgstr "interface %s inconnue"
 
-#: dnsmasq.c:191
-#, c-format
-msgid "no interface with address %s"
-msgstr "pas d'interface avec l'adresse %s"
-
-#: dnsmasq.c:207 dnsmasq.c:678
+#: dnsmasq.c:354 dnsmasq.c:997
 #, c-format
 msgid "DBus error: %s"
 msgstr "Erreur DBus : %s"
 
-#: dnsmasq.c:210
+#: dnsmasq.c:357
 msgid "DBus not available: set HAVE_DBUS in src/config.h"
 msgstr "DBus n'est pas disponible : activez HAVE_DBUS dans src/config.h"
 
-#: dnsmasq.c:236
+#: dnsmasq.c:385
 #, c-format
 msgid "unknown user or group: %s"
 msgstr "utilisateur ou groupe inconnu : %s"
 
-#: dnsmasq.c:291
+#: dnsmasq.c:440
 #, c-format
 msgid "cannot chdir to filesystem root: %s"
 msgstr "Ne peut effectuer un 'chdir' à la racine du système de fichier : %s"
 
-#: dnsmasq.c:455
+#: dnsmasq.c:692
 #, c-format
 msgid "started, version %s DNS disabled"
 msgstr "démarrage avec le DNS désactivé (version %s)"
 
-#: dnsmasq.c:457
+#: dnsmasq.c:694
 #, c-format
 msgid "started, version %s cachesize %d"
 msgstr "demarré, version %s (taille de cache %d)"
 
-#: dnsmasq.c:459
+#: dnsmasq.c:696
 #, c-format
 msgid "started, version %s cache disabled"
 msgstr "démarrage avec le cache désactivé (version %s)"
 
-#: dnsmasq.c:461
+#: dnsmasq.c:698
 #, c-format
 msgid "compile time options: %s"
 msgstr "options à la compilation : %s"
 
-#: dnsmasq.c:467
+#: dnsmasq.c:704
 msgid "DBus support enabled: connected to system bus"
 msgstr "Support DBus autorisé : connecté au bus système"
 
-#: dnsmasq.c:469
+#: dnsmasq.c:706
 msgid "DBus support enabled: bus connection pending"
 msgstr "Support DBus autorisé : connexion au bus en attente"
 
-#: dnsmasq.c:474
+#: dnsmasq.c:711
+msgid "DNS service limited to local subnets"
+msgstr ""
+
+#: dnsmasq.c:727
+msgid "DNSSEC validation enabled"
+msgstr ""
+
+#: dnsmasq.c:730
+msgid "DNSSEC signature timestamps not checked until first cache reload"
+msgstr ""
+
+#: dnsmasq.c:733
+msgid "DNSSEC signature timestamps not checked until system time valid"
+msgstr ""
+
+#: dnsmasq.c:738
 #, c-format
 msgid "warning: failed to change owner of %s: %s"
 msgstr "Impossible de changer pour l'utilisateur %s : %s"
 
-#: dnsmasq.c:478
+#: dnsmasq.c:742
 msgid "setting --bind-interfaces option because of OS limitations"
 msgstr "active l'option --bind-interfaces à cause de limitations dans le système d'exploitation"
 
-#: dnsmasq.c:483
+#: dnsmasq.c:752
 #, c-format
 msgid "warning: interface %s does not currently exist"
 msgstr "attention : l'interface %s n'existe pas actuellement"
 
-#: dnsmasq.c:488
+#: dnsmasq.c:757
 msgid "warning: ignoring resolv-file flag because no-resolv is set"
 msgstr "attention : l'option « resolv-file » sera ignorée car « no-resolv » a été spécifié"
 
 #
-#: dnsmasq.c:491
+#: dnsmasq.c:760
 msgid "warning: no upstream servers configured"
 msgstr "attention : aucun serveur amont n'est configuré"
 
-#: dnsmasq.c:495
+#: dnsmasq.c:764
 #, c-format
 msgid "asynchronous logging enabled, queue limit is %d messages"
 msgstr "mode asynchrone d'écriture de traces, la taille maximum de la queue est de %d messages."
 
-#: dnsmasq.c:508
-#, c-format
-msgid "DHCP, static leases only on %.0s%s, lease time %s"
-msgstr "baux statiques DHCP seulement sur %.0s%s, durée de validité de bail %s"
-
-#: dnsmasq.c:510
-#, c-format
-msgid "DHCP, proxy on subnet %.0s%s%.0s"
-msgstr "DHCP, proxy sur le sous-réseau %.0s%s%.0s"
+#: dnsmasq.c:785
+msgid "IPv6 router advertisement enabled"
+msgstr "annonces de routeur IPv6 activées"
 
-#: dnsmasq.c:511
+#: dnsmasq.c:790
 #, c-format
-msgid "DHCP, IP range %s -- %s, lease time %s"
-msgstr "DHCP, plage d'adresses %s -- %s, durée de bail %s"
+msgid "DHCP, sockets bound exclusively to interface %s"
+msgstr ""
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 msgid "root is "
 msgstr "root est"
 
 #
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 msgid "enabled"
 msgstr "activé"
 
-#: dnsmasq.c:528
+#: dnsmasq.c:806
 msgid "secure mode"
 msgstr "mode sécurisé"
 
-#: dnsmasq.c:554
+#: dnsmasq.c:809
+#, c-format
+msgid "warning: %s inaccessible"
+msgstr ""
+
+#: dnsmasq.c:813
+#, fuzzy, c-format
+msgid "warning: TFTP directory %s inaccessible"
+msgstr "répertoire TFTP %s inaccessible : %s"
+
+#: dnsmasq.c:839
 #, c-format
 msgid "restricting maximum simultaneous TFTP transfers to %d"
 msgstr "le nombre maximum de transferts TFTP simultanés sera restreint à %d"
 
-#: dnsmasq.c:680
+#: dnsmasq.c:999
 msgid "connected to system DBus"
 msgstr "connecté au systeme DBus"
 
-#: dnsmasq.c:775
+#: dnsmasq.c:1149
 #, c-format
 msgid "cannot fork into background: %s"
 msgstr "Ne peut se lancer en tâche de fond : %s"
 
-#: dnsmasq.c:778
+#: dnsmasq.c:1152
 #, c-format
 msgid "failed to create helper: %s"
 msgstr "impossible de créer le 'helper' : %s"
 
-#: dnsmasq.c:781
+#: dnsmasq.c:1155
 #, c-format
 msgid "setting capabilities failed: %s"
 msgstr "impossible de configurer la capacité %s"
 
-#: dnsmasq.c:785
+#: dnsmasq.c:1158
 #, c-format
 msgid "failed to change user-id to %s: %s"
 msgstr "Impossible de changer l'identifiant utilisateur pour %s : %s"
 
-#: dnsmasq.c:790
+#: dnsmasq.c:1161
 #, c-format
 msgid "failed to change group-id to %s: %s"
 msgstr "Impossible de changer l'identifiant de groupe pour %s : %s"
 
-#: dnsmasq.c:793
+#: dnsmasq.c:1164
 #, c-format
 msgid "failed to open pidfile %s: %s"
 msgstr "impossible de lire le fichier de PID %s : %s"
 
-#: dnsmasq.c:796
+#: dnsmasq.c:1167
 #, c-format
-msgid "cannot open %s: %s"
-msgstr "Ne peut pas lire %s : %s"
+msgid "cannot open log %s: %s"
+msgstr "Ne peut ouvrir le fichier de log %s : %s"
 
-#: dnsmasq.c:851
+#
+#: dnsmasq.c:1170
+#, c-format
+msgid "failed to load Lua script: %s"
+msgstr "impossible de charger le script Lua : %s"
+
+#: dnsmasq.c:1173
 #, c-format
-msgid "child process killed by signal %d"
-msgstr "Le processus fils a été terminé par le signal %d"
+msgid "TFTP directory %s inaccessible: %s"
+msgstr "répertoire TFTP %s inaccessible : %s"
+
+#: dnsmasq.c:1176
+#, fuzzy, c-format
+msgid "cannot create timestamp file %s: %s"
+msgstr "ne peut ouvrir ou créer le fichiers de baux %s : %s"
+
+#: dnsmasq.c:1197
+msgid "now checking DNSSEC signature timestamps"
+msgstr ""
 
-#: dnsmasq.c:855
+#: dnsmasq.c:1264
 #, c-format
-msgid "child process exited with status %d"
-msgstr "Le processus fils s'est terminé avec le statut %d"
+msgid "script process killed by signal %d"
+msgstr "Le script a été terminé par le signal %d"
 
-#: dnsmasq.c:859
+#: dnsmasq.c:1268
+#, c-format
+msgid "script process exited with status %d"
+msgstr "Le script s'est terminé avec le statut %d"
+
+#: dnsmasq.c:1272
 #, c-format
 msgid "failed to execute %s: %s"
 msgstr "impossible d'exécuter à %s : %s"
 
-#: dnsmasq.c:903
+#: dnsmasq.c:1327
 msgid "exiting on receipt of SIGTERM"
 msgstr "sortie sur réception du signal SIGTERM"
 
-#: dnsmasq.c:931
+#: dnsmasq.c:1355
 #, c-format
 msgid "failed to access %s: %s"
 msgstr "impossible d'accéder à %s : %s"
 
-#: dnsmasq.c:961
+#: dnsmasq.c:1385
 #, c-format
 msgid "reading %s"
 msgstr "Lecture de %s"
 
-#: dnsmasq.c:972
+#: dnsmasq.c:1396
 #, c-format
 msgid "no servers found in %s, will retry"
 msgstr "aucun serveur trouvé dans %s, va réessayer"
 
-#: dhcp.c:40
+#: dhcp.c:53
 #, c-format
 msgid "cannot create DHCP socket: %s"
 msgstr "ne peut créer la socket DHCP: %s"
 
-#: dhcp.c:52
+#: dhcp.c:68
 #, c-format
 msgid "failed to set options on DHCP socket: %s"
 msgstr "impossible d'appliquer les options sur la socket DHCP : %s"
 
-#: dhcp.c:65
+#: dhcp.c:89
 #, c-format
 msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"
 msgstr "impossible de déclarer SO_REUSE{ADDR|PORT} sur la socket DHCP : %s"
 
-#: dhcp.c:77
+#: dhcp.c:101
 #, c-format
 msgid "failed to bind DHCP server socket: %s"
 msgstr "impossible de lier la socket serveur DHCP : %s"
 
-#: dhcp.c:103
+#: dhcp.c:127
 #, c-format
 msgid "cannot create ICMP raw socket: %s."
 msgstr "ne peut créer de socket en mode raw pour ICMP : %s."
 
+#: dhcp.c:241 dhcp6.c:180
+#, c-format
+msgid "unknown interface %s in bridge-interface"
+msgstr "interface %s inconnue spécifiée comme interface de pont"
+
 #: dhcp.c:281
 #, c-format
 msgid "DHCP packet received on %s which has no address"
 msgstr "Paquet DHCP reçu sur %s qui n'a pas d'adresse"
 
-#: dhcp.c:445
+#: dhcp.c:415
+#, c-format
+msgid "ARP-cache injection failed: %s"
+msgstr ""
+
+#: dhcp.c:514
 #, c-format
 msgid "DHCP range %s -- %s is not consistent with netmask %s"
 msgstr "La plage d'adresses DHCP %s -- %s n'est pas cohérente avec le masque de réseau %s"
 
-#: dhcp.c:852
+#: dhcp.c:815
 #, c-format
 msgid "bad line at %s line %d"
 msgstr "mauvaise ligne dans %s ligne %d"
 
-#: dhcp.c:895
+#: dhcp.c:858
 #, c-format
 msgid "ignoring %s line %d, duplicate name or IP address"
 msgstr "ignore %s à la ligne %d : duplication de nom ou d'adresse IP"
 
-#: dhcp.c:978
-#, c-format
-msgid "duplicate IP address %s in dhcp-config directive."
-msgstr "adresse IP %s dupliquée dans la directive dhcp-config."
-
-#: dhcp.c:981
-#, c-format
-msgid "duplicate IP address %s in %s."
-msgstr "adresse IP %s dupliquée dans %s."
-
-#: dhcp.c:1024
-#, c-format
-msgid "%s has more than one address in hostsfile, using %s for DHCP"
-msgstr "%s a plus d'une adresse dans le fichier d'hôte, utilisation de %s pour le DHCP."
-
-#: dhcp.c:1029
+#: dhcp.c:1002 rfc3315.c:2135
 #, c-format
-msgid "duplicate IP address %s (%s) in dhcp-config directive"
-msgstr "adresse IP %s (%s) dupliquée dans la directive dhcp-config."
+msgid "DHCP relay %s -> %s"
+msgstr "Relais DHCP %s -> %s"
 
-#: lease.c:67
+#: lease.c:61
 #, c-format
 msgid "cannot open or create lease file %s: %s"
 msgstr "ne peut ouvrir ou créer le fichiers de baux %s : %s"
 
-#: lease.c:93
+#: lease.c:134
 msgid "too many stored leases"
 msgstr "beaucoup trop de baux enregistrés"
 
-#: lease.c:129
+#: lease.c:165
 #, c-format
 msgid "cannot run lease-init script %s: %s"
 msgstr "Ne peut pas exécuter le script lease-init %s : %s"
 
-#: lease.c:135
+#: lease.c:171
 #, c-format
 msgid "lease-init script returned exit code %s"
 msgstr "le script lease-init a retourné le code %s"
 
-#: lease.c:235
+#: lease.c:342
 #, c-format
 msgid "failed to write %s: %s (retry in %us)"
 msgstr "impossible de lire %s : %s (prochain essai dans %us)"
 
-#: rfc2131.c:315
+#: lease.c:906
+#, c-format
+msgid "Ignoring domain %s for DHCP host name %s"
+msgstr "Le domaine %s est ignoré pour l'hôte DHCP %s"
+
+#: rfc2131.c:344
 #, c-format
 msgid "no address range available for DHCP request %s %s"
 msgstr "pas de plage d'adresse disponible pour la requête DHCP %s %s"
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "with subnet selector"
 msgstr "avec sélecteur de sous-reseau"
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "via"
 msgstr "par l'intermédiaire de"
 
-#: rfc2131.c:331
+#: rfc2131.c:357
 #, c-format
 msgid "%u available DHCP subnet: %s/%s"
 msgstr "%u sous-réseaux DHCP disponibles : %s/%s"
 
-#: rfc2131.c:334
+#: rfc2131.c:360 rfc3315.c:300
 #, c-format
 msgid "%u available DHCP range: %s -- %s"
 msgstr "%u la gamme DHCP disponible est : %s -- %s"
 
-#: rfc2131.c:363
+#: rfc2131.c:471
+#, c-format
+msgid "%u vendor class: %s"
+msgstr "%u Classe de vendeur ('Vendor Class') : %s"
+
+#: rfc2131.c:473
+#, c-format
+msgid "%u user class: %s"
+msgstr "%u Classe d'utilisateur : %s"
+
+#: rfc2131.c:500
 msgid "disabled"
 msgstr "désactivé"
 
-#: rfc2131.c:404 rfc2131.c:916 rfc2131.c:1288
+#: rfc2131.c:541 rfc2131.c:974 rfc2131.c:1380 rfc3315.c:603 rfc3315.c:856
+#: rfc3315.c:1135
 msgid "ignored"
 msgstr "ignoré"
 
-#: rfc2131.c:419 rfc2131.c:1135
+#: rfc2131.c:556 rfc2131.c:1207 rfc3315.c:906
 msgid "address in use"
 msgstr "adresse déjà utilisée"
 
-#: rfc2131.c:433 rfc2131.c:970
+#: rfc2131.c:570 rfc2131.c:1028
 msgid "no address available"
 msgstr "pas d'adresse disponible"
 
-#: rfc2131.c:440 rfc2131.c:1098
+#: rfc2131.c:577 rfc2131.c:1170
 msgid "wrong network"
 msgstr "mauvais réseau"
 
-#: rfc2131.c:454
+#: rfc2131.c:592
 msgid "no address configured"
 msgstr "pas d'adresse configurée"
 
-#: rfc2131.c:460 rfc2131.c:1148
+#: rfc2131.c:598 rfc2131.c:1220
 msgid "no leases left"
 msgstr "plus aucun bail disponible"
 
-#: rfc2131.c:545
+#: rfc2131.c:693 rfc3315.c:476
 #, c-format
 msgid "%u client provides name: %s"
 msgstr "le client %u fourni le nom : %s"
 
-#: rfc2131.c:700
-#, c-format
-msgid "%u vendor class: %s"
-msgstr "%u Classe de vendeur ('Vendor Class') : %s"
-
-#: rfc2131.c:702
-#, c-format
-msgid "%u user class: %s"
-msgstr "%u Classe d'utilisateur : %s"
-
-#: rfc2131.c:761
+#: rfc2131.c:798
 msgid "PXE BIS not supported"
 msgstr "Service PXE BIS (Boot Integrity Services) non supporté"
 
-#: rfc2131.c:886
+#: rfc2131.c:942 rfc3315.c:1229
 #, c-format
 msgid "disabling DHCP static address %s for %s"
 msgstr "désactive l'adresse statique DHCP %s pour %s"
 
-#: rfc2131.c:907
+#: rfc2131.c:963
 msgid "unknown lease"
 msgstr "bail inconnu"
 
-#: rfc2131.c:939
+#: rfc2131.c:997
 #, c-format
 msgid "not using configured address %s because it is leased to %s"
 msgstr "L'adresse statique %s ne sera pas utilisée car un bail est déjà attribué à %s"
 
-#: rfc2131.c:949
+#: rfc2131.c:1007
 #, c-format
 msgid "not using configured address %s because it is in use by the server or relay"
 msgstr "L'adresse statique %s ne sera pas utilisée car elle est utilisée par le serveur ou un relai"
 
-#: rfc2131.c:952
+#: rfc2131.c:1010
 #, c-format
 msgid "not using configured address %s because it was previously declined"
 msgstr "L'adresse statique %s ne sera pas utilisée car elle a préalablement été refusée"
 
-#: rfc2131.c:968 rfc2131.c:1141
+#: rfc2131.c:1026 rfc2131.c:1213
 msgid "no unique-id"
 msgstr "pas d'identifiant unique"
 
-#: rfc2131.c:1037
+#: rfc2131.c:1108
 msgid "wrong server-ID"
 msgstr "mauvais identifiant de serveur"
 
-#: rfc2131.c:1055
+#: rfc2131.c:1127
 msgid "wrong address"
 msgstr "mauvaise adresse"
 
-#: rfc2131.c:1073
+#: rfc2131.c:1145 rfc3315.c:1002
 msgid "lease not found"
 msgstr "bail non trouvé"
 
-#: rfc2131.c:1106
+#: rfc2131.c:1178
 msgid "address not available"
 msgstr "adresse non disponible"
 
-#: rfc2131.c:1117
+#: rfc2131.c:1189
 msgid "static lease available"
 msgstr "bail statique disponible"
 
-#: rfc2131.c:1121
+#: rfc2131.c:1193
 msgid "address reserved"
 msgstr "adresse reservée"
 
-#: rfc2131.c:1129
+#: rfc2131.c:1201
 #, c-format
 msgid "abandoning lease to %s of %s"
 msgstr "abandon du bail de %s pour %s"
 
-#: rfc2131.c:1710
-#, c-format
-msgid "%u tags: %s"
-msgstr "%u options: %s"
-
-#: rfc2131.c:1723
+#: rfc2131.c:1707
 #, c-format
 msgid "%u bootfile name: %s"
 msgstr "%u nom de fichier 'bootfile' : %s"
 
-#: rfc2131.c:1732
+#: rfc2131.c:1716
 #, c-format
 msgid "%u server name: %s"
 msgstr "%u nom du serveur : %s"
 
-#: rfc2131.c:1746
+#: rfc2131.c:1724
 #, c-format
 msgid "%u next server: %s"
 msgstr "%u serveur suivant : %s"
 
-#: rfc2131.c:1749
+#: rfc2131.c:1727
 #, c-format
 msgid "%u broadcast response"
 msgstr "%u réponse broadcast"
 
-#: rfc2131.c:1812
+#: rfc2131.c:1790
 #, c-format
 msgid "cannot send DHCP/BOOTP option %d: no space left in packet"
 msgstr "Impossible d'envoyer l'option DHCP/BOOTP %d : pas assez d'espace dans le paquet"
 
-#: rfc2131.c:2058
+#: rfc2131.c:2031
 msgid "PXE menu too large"
 msgstr "menu PXE trop grand"
 
-#: rfc2131.c:2171
-#, c-format
-msgid "Ignoring domain %s for DHCP host name %s"
-msgstr "Le domaine %s est ignoré pour l'hôte DHCP %s"
-
-#: rfc2131.c:2189
+#: rfc2131.c:2170 rfc3315.c:1502
 #, c-format
 msgid "%u requested options: %s"
 msgstr "%u options demandées : %s"
 
-#: rfc2131.c:2456
+#: rfc2131.c:2487
 #, c-format
 msgid "cannot send RFC3925 option: too many options for enterprise number %d"
 msgstr "ne peux envoyer l'option RFC3925 : trop d'options pour le numéro d'entreprise %d"
 
-#: netlink.c:70
+#: netlink.c:77
 #, c-format
 msgid "cannot create netlink socket: %s"
 msgstr "ne peux lier une socket netlink : %s"
 
-#: netlink.c:288
+#: netlink.c:348
 #, c-format
 msgid "netlink returns error: %s"
 msgstr "Erreur netlink : %s"
 
-#: dbus.c:150
+#: dbus.c:186
 msgid "attempt to set an IPv6 server address via DBus - no IPv6 support"
 msgstr "tentative de lier une adresse serveur IPV6 via DBus - pas de support IPV6"
 
-#: dbus.c:286
+#: dbus.c:439
+#, c-format
+msgid "Enabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:444
+#, c-format
+msgid "Disabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:691
 msgid "setting upstream servers from DBus"
 msgstr "configuration des serveurs amonts à partir de DBus"
 
-#: dbus.c:324
+#: dbus.c:738
 msgid "could not register a DBus message handler"
 msgstr "ne peut enregistrer une routine de traitement des messages DBus"
 
-#: bpf.c:217
+#: bpf.c:263
 #, c-format
 msgid "cannot create DHCP BPF socket: %s"
 msgstr "impossible de créer une socket BPF pour DHCP : %s"
 
-#: bpf.c:245
+#: bpf.c:291
 #, c-format
 msgid "DHCP request for unsupported hardware type (%d) received on %s"
 msgstr "requête DHCP pour un type de matériel non supporté (%d) reçue sur %s"
 
-#: tftp.c:281
+#: bpf.c:376
+#, fuzzy, c-format
+msgid "cannot create PF_ROUTE socket: %s"
+msgstr "ne peut créer la socket DHCP: %s"
+
+#: bpf.c:397
+msgid "Unknown protocol version from route socket"
+msgstr ""
+
+#: helper.c:153
+msgid "lease() function missing in Lua script"
+msgstr "la fonction lease() est absente du script Lua"
+
+#: tftp.c:309
 msgid "unable to get free port for TFTP"
 msgstr "impossible d'obtenir un port libre pour TFTP"
 
-#: tftp.c:296
+#: tftp.c:325
 #, c-format
 msgid "unsupported request from %s"
 msgstr "requête de %s non supportée"
 
-#: tftp.c:406
+#: tftp.c:439
 #, c-format
 msgid "file %s not found"
 msgstr "fichier %s non trouvé"
 
-#: tftp.c:522
+#: tftp.c:548
 #, c-format
 msgid "error %d %s received from %s"
 msgstr "erreur %d %s reçu de %s"
 
-#: tftp.c:554
+#: tftp.c:590
 #, c-format
 msgid "failed sending %s to %s"
 msgstr "impossible d'envoyer %s à %s"
 
-#: tftp.c:568
+#: tftp.c:590
 #, c-format
 msgid "sent %s to %s"
 msgstr "envoyé %s à %s"
 
-#: log.c:177
+#: log.c:190
 #, c-format
 msgid "overflow: %d log entries lost"
 msgstr "débordement : %d traces perdues"
 
-#: log.c:254
+#: log.c:268
 #, c-format
 msgid "log failed: %s"
 msgstr "trace perdue : %s"
 
-#: log.c:462
+#: log.c:469
 msgid "FAILED to start up"
 msgstr "IMPOSSIBLE de démarrer"
 
+#: conntrack.c:65
+#, c-format
+msgid "Conntrack connection mark retrieval failed: %s"
+msgstr "La récupération de la marque de suivi de connexion a échoué : %s"
+
+#: dhcp6.c:59
+#, c-format
+msgid "cannot create DHCPv6 socket: %s"
+msgstr "ne peut créer la socket DHCPv6: %s"
+
+#: dhcp6.c:80
+#, c-format
+msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"
+msgstr "impossible de déclarer SO_REUSE{ADDR|PORT} sur la socket DHCPv6 : %s"
+
+#: dhcp6.c:92
+#, c-format
+msgid "failed to bind DHCPv6 server socket: %s"
+msgstr "impossible de lier la socket serveur DHCPv6 : %s"
+
+#: rfc3315.c:157
+#, c-format
+msgid "no address range available for DHCPv6 request from relay at %s"
+msgstr "pas de plage d'adresse disponible pour la requête DHCPv6 transmise via le relai %s"
+
+#: rfc3315.c:166
+#, c-format
+msgid "no address range available for DHCPv6 request via %s"
+msgstr "pas de plage d'adresse disponible pour la requête DHCPv6 via %s"
+
+#: rfc3315.c:297
+#, c-format
+msgid "%u available DHCPv6 subnet: %s/%d"
+msgstr "%u sous-réseaux DHCPv6 disponibles : %s/%d"
+
+#: rfc3315.c:380
+#, c-format
+msgid "%u vendor class: %u"
+msgstr "%u Classe de vendeur ('Vendor Class') : %u"
+
+#: rfc3315.c:428
+#, c-format
+msgid "%u client MAC address: %s"
+msgstr "%u MAC adresse du client : %s"
+
+#: rfc3315.c:660
+#, c-format
+msgid "unknown prefix-class %d"
+msgstr "préfixe de classe inconnu %d"
+
+#: rfc3315.c:803 rfc3315.c:898
+msgid "address unavailable"
+msgstr "adresse non disponible"
+
+#: rfc3315.c:815 rfc3315.c:946 rfc3315.c:1279
+msgid "success"
+msgstr "réussi"
+
+#: rfc3315.c:830 rfc3315.c:839 rfc3315.c:954 rfc3315.c:956
+msgid "no addresses available"
+msgstr "pas d'adresse disponible"
+
+#: rfc3315.c:933
+msgid "not on link"
+msgstr "pas sur ce lien"
+
+#: rfc3315.c:1006 rfc3315.c:1191 rfc3315.c:1268
+msgid "no binding found"
+msgstr "aucune liaison trouvée"
+
+#: rfc3315.c:1044
+msgid "deprecated"
+msgstr "obsolète"
+
+#: rfc3315.c:1049
+msgid "address invalid"
+msgstr "adresse non valide"
+
+#: rfc3315.c:1096
+msgid "confirm failed"
+msgstr "confirmation d'échec"
+
+#: rfc3315.c:1112
+msgid "all addresses still on link"
+msgstr "toutes les adresses sont toujours sur le lien"
+
+#: rfc3315.c:1200
+msgid "release received"
+msgstr "libération reçue"
+
+#: rfc3315.c:2126
+msgid "Cannot multicast to DHCPv6 server without correct interface"
+msgstr "Impossible de faire du multicast au server DHCPv6 sans interface valide"
+
+#: dhcp-common.c:145
+#, c-format
+msgid "Ignoring duplicate dhcp-option %d"
+msgstr "L'option dhcp-option redondante %d sera ignorée"
+
+#: dhcp-common.c:222
+#, c-format
+msgid "%u tags: %s"
+msgstr "%u options: %s"
+
+#: dhcp-common.c:407
+#, c-format
+msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgstr "%s a plus d'une adresse dans le fichier d'hôte, utilisation de %s pour le DHCP."
+
+#: dhcp-common.c:430
+#, c-format
+msgid "duplicate IP address %s (%s) in dhcp-config directive"
+msgstr "adresse IP %s (%s) dupliquée dans la directive dhcp-config."
+
+#: dhcp-common.c:494
+#, c-format
+msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s"
+msgstr "impossible de déclarer SO_BINDTODEVICE sur la socket DHCP : %s"
+
+#: dhcp-common.c:615
+#, c-format
+msgid "Known DHCP options:\n"
+msgstr "Options DHCP connues :\n"
+
+#: dhcp-common.c:626
+#, c-format
+msgid "Known DHCPv6 options:\n"
+msgstr "Options DHCPv6 connues :\n"
+
+#: dhcp-common.c:823
+msgid ", prefix deprecated"
+msgstr ", préfixe obsolète"
+
+#: dhcp-common.c:826
+#, c-format
+msgid ", lease time "
+msgstr ", durée de bail "
+
+#: dhcp-common.c:868
+#, c-format
+msgid "%s stateless on %s%.0s%.0s%s"
+msgstr "%s sans état (stateless) sur %s%.0s%.0s%s"
+
+#: dhcp-common.c:870
+#, c-format
+msgid "%s, static leases only on %.0s%s%s%.0s"
+msgstr "%s, baux statiques seulement sur %.0s%s%s%.0s"
+
+#: dhcp-common.c:872
+#, c-format
+msgid "%s, proxy on subnet %.0s%s%.0s%.0s"
+msgstr "%s, proxy sur le sous-réseau %.0s%s%.0s"
+
+#: dhcp-common.c:873
+#, c-format
+msgid "%s, IP range %s -- %s%s%.0s"
+msgstr "%s, plage d'adresses IP %s -- %s%s%.0s"
+
+#: dhcp-common.c:886
+#, c-format
+msgid "DHCPv4-derived IPv6 names on %s%s"
+msgstr "noms IPv6 dérivés de DHCPv4 sur %s%s"
+
+#: dhcp-common.c:889
+#, c-format
+msgid "router advertisement on %s%s"
+msgstr "annonces de routeurs sur %s%s"
+
+#: dhcp-common.c:900
+#, c-format
+msgid "DHCP relay from %s to %s via %s"
+msgstr "Relais DHCP de %s à %s via %s"
+
+#: dhcp-common.c:902
+#, c-format
+msgid "DHCP relay from %s to %s"
+msgstr "Relais DHCP de %s à %s"
+
+#: radv.c:109
+#, c-format
+msgid "cannot create ICMPv6 socket: %s"
+msgstr "ne peut créer la socket ICMPv6: %s"
+
+#: auth.c:448
+#, c-format
+msgid "ignoring zone transfer request from %s"
+msgstr "la requête de transfert de zone en provenance de %s est ignorée"
+
+#: ipset.c:95
+#, c-format
+msgid "failed to find kernel version: %s"
+msgstr "impossible de trouver la version de noyau : %s"
+
+#: ipset.c:114
+#, c-format
+msgid "failed to create IPset control socket: %s"
+msgstr "impossible de créer une socket de contrôle IPset : %s"
+
+#: dnssec.c:449 dnssec.c:493
+#, fuzzy, c-format
+msgid "failed to update mtime on %s: %s"
+msgstr "impossible de lire le fichier de PID %s : %s"
+
+#: blockdata.c:58
+#, c-format
+msgid "DNSSEC memory in use %u, max %u, allocated %u"
+msgstr ""
+
+#: tables.c:80
+msgid "error: fill_addr missused"
+msgstr ""
+
+#: tables.c:109
+#, fuzzy, c-format
+msgid "failed to access pf devices: %s"
+msgstr "impossible d'accéder à %s : %s"
+
+#: tables.c:123
+#, fuzzy, c-format
+msgid "warning: no opened pf devices %s"
+msgstr "utilise les adresses locales seulement pour %s %s"
+
+#: tables.c:131
+#, fuzzy, c-format
+msgid "error: cannot use table name %s"
+msgstr "ne peut pas obtenir le nom de la machine : %s"
+
+#: tables.c:139
+#, c-format
+msgid "error: cannot strlcpy table name %s"
+msgstr ""
+
+#: tables.c:145
+#, c-format
+msgid "warning: pfr_add_tables: %s(%d)"
+msgstr ""
+
+#: tables.c:151
+msgid "info: table created"
+msgstr ""
+
+#: tables.c:162
+#, c-format
+msgid "warning: DIOCR%sADDRS: %s"
+msgstr ""
+
+#
+#: tables.c:166
+#, fuzzy, c-format
+msgid "%d addresses %s"
+msgstr "mauvaise adresse"
+
+#: inotify.c:59
+#, fuzzy, c-format
+msgid "cannot access path %s: %s"
+msgstr "Ne peut pas lire %s : %s"
+
+#: inotify.c:92
+#, fuzzy, c-format
+msgid "failed to create inotify: %s"
+msgstr "impossible de créer le 'helper' : %s"
+
+#: inotify.c:105
+#, c-format
+msgid "too many symlinks following %s"
+msgstr ""
+
+#: inotify.c:121
+#, c-format
+msgid "directory %s for resolv-file is missing, cannot poll"
+msgstr ""
+
+#: inotify.c:125 inotify.c:162
+#, fuzzy, c-format
+msgid "failed to create inotify for %s: %s"
+msgstr "impossible de créer une socket d'écoute pour %s : %s"
+
+#: inotify.c:147
+#, fuzzy, c-format
+msgid "bad dynamic directory %s: %s"
+msgstr "Ne peut pas lire le répertoire %s : %s"
+
+#: inotify.c:247
+#, c-format
+msgid "inotify, new or changed file %s"
+msgstr ""
+
+#, fuzzy
+#~ msgid "cannot cannonicalise resolv-file %s: %s"
+#~ msgstr "ne peut ouvrir ou créer le fichiers de baux %s : %s"
+
+#~ msgid "Always send frequent router-advertisements"
+#~ msgstr "Envoyer des annonces de routeurs fréquentes"
+
+#~ msgid "no interface with address %s"
+#~ msgstr "pas d'interface avec l'adresse %s"
+
+#~ msgid "duplicate IP address %s in dhcp-config directive."
+#~ msgstr "adresse IP %s dupliquée dans la directive dhcp-config."
+
+#~ msgid "Specify path to Lua script (no default)."
+#~ msgstr "Spécifie un chemin pour le fichier PID (par défaut : %s)."
+
 #
 #~ msgid "only one dhcp-hostsfile allowed"
 #~ msgstr "une seule valeur est autorisée pour 'dhcp-hostsfile'"
@@ -1503,12 +2226,6 @@ msgstr "IMPOSSIBLE de d
 #~ msgid "failed to bind listening socket for %s: %s"
 #~ msgstr "impossible de lier la socket de lecture pour %s : %s"
 
-#~ msgid "failed to listen on socket: %s"
-#~ msgstr "impossible de lire sur la socket : %s"
-
-#~ msgid "failed to create TFTP socket: %s"
-#~ msgstr "impossible de créer une socket TFTP : %s"
-
 #~ msgid "DHCP packet: transaction-id is %u"
 #~ msgstr "paquet DHCP : l'identifiant de transaction ('transaction-id') est %u"
 
@@ -1518,13 +2235,6 @@ msgstr "IMPOSSIBLE de d
 #~ msgid "must set exactly one interface on broken systems without IP_RECVIF"
 #~ msgstr "Une interface et une seule doit être déclarée sur les systèmes sans IP_RECVIF"
 
-#
-#~ msgid "failed to load %s: %s"
-#~ msgstr "impossible de charger %s : %m"
-
-#~ msgid "bad name in %s"
-#~ msgstr "mauvais nom dans %s"
-
 #~ msgid "Ignoring DHCP lease for %s because it has an illegal domain part"
 #~ msgstr "On ignore le bail DHCP pour %s car il possède un nom de domaine illégal"
 
index 749da19..5545aee 100644 (file)
--- a/po/id.po
+++ b/po/id.po
@@ -10,631 +10,834 @@ msgstr ""
 "PO-Revision-Date: 2005-10-07 11:45+0100\n"
 "Last-Translator: Salman AS <sas@salman.or.id>\n"
 "Language-Team: Indonesian <translation-team-id@lists.sourceforge.net>\n"
+"Language: id\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=ASCII\n"
 "Content-Transfer-Encoding: 8bit\n"
 
+#: cache.c:523
+msgid "Internal error in cache."
+msgstr ""
+
 # OK
-#: cache.c:761
+#: cache.c:941
 #, fuzzy, c-format
 msgid "failed to load names from %s: %s"
 msgstr "gagal memuat nama-nama dari %s: %s"
 
 # OK
-#: cache.c:795 dhcp.c:865
+#: cache.c:967 dhcp.c:828
 #, fuzzy, c-format
 msgid "bad address at %s line %d"
 msgstr "kesalahan nama pada %s baris %d"
 
 # OK
-#: cache.c:853 dhcp.c:881
+#: cache.c:1018 dhcp.c:844
 #, c-format
 msgid "bad name at %s line %d"
 msgstr "kesalahan nama pada %s baris %d"
 
 # OK
-#: cache.c:860 dhcp.c:956
+#: cache.c:1027 dhcp.c:919
 #, c-format
 msgid "read %s - %d addresses"
 msgstr "membaca %s - %d alamat"
 
 # OK
-#: cache.c:899
+#: cache.c:1135
 msgid "cleared cache"
 msgstr "cache telah dihapus"
 
-#: cache.c:960
+#: cache.c:1164
+#, c-format
+msgid "No IPv4 address found for %s"
+msgstr ""
+
+#: cache.c:1242
 #, c-format
 msgid "%s is a CNAME, not giving it to the DHCP lease of %s"
 msgstr ""
 
 # OK
-#: cache.c:966
+#: cache.c:1266
 #, c-format
 msgid "not giving name %s to the DHCP lease of %s because the name exists in %s with address %s"
 msgstr "tidak memberikan nama %s kepada lease DHCP %s karena nama telah ada dalam %sdengan alamat %s"
 
-#: cache.c:1039
+#: cache.c:1421
 #, c-format
 msgid "time %lu"
 msgstr ""
 
 # OK
-#: cache.c:1040
+#: cache.c:1422
 #, fuzzy, c-format
 msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries."
 msgstr "ukuran cache %d, %d/%d penyisipan cache menimpa cache yang belum kadaluwarsa"
 
-#: cache.c:1042
+#: cache.c:1424
 #, c-format
 msgid "queries forwarded %u, queries answered locally %u"
 msgstr ""
 
-#: cache.c:1068
+#: cache.c:1427
+#, c-format
+msgid "queries for authoritative zones %u"
+msgstr ""
+
+#: cache.c:1453
 #, c-format
 msgid "server %s#%d: queries sent %u, retried or failed %u"
 msgstr ""
 
 # OK
-#: util.c:57
+#: util.c:45
 #, fuzzy, c-format
 msgid "failed to seed the random number generator: %s"
 msgstr "gagal mendengarkan di socket: %s"
 
 # OK
-#: util.c:189
+#: util.c:205
 #, fuzzy
 msgid "failed to allocate memory"
 msgstr "gagal memuat %S: %m"
 
 # OK
-#: util.c:227 option.c:573
+#: util.c:250 option.c:601
 msgid "could not get memory"
 msgstr "tidak bisa mendapatkan memory"
 
 # OK
-#: util.c:237
+#: util.c:260
 #, fuzzy, c-format
 msgid "cannot create pipe: %s"
 msgstr "tidak bisa membaca %s: %s"
 
 # OK
-#: util.c:245
+#: util.c:268
 #, fuzzy, c-format
 msgid "failed to allocate %d bytes"
 msgstr "gagal memuat %S: %m"
 
 # OK
-#: util.c:350
+#: util.c:437
 #, c-format
 msgid "infinite"
 msgstr "tak terbatas"
 
 # OK
-#: option.c:244
+#: option.c:332
 msgid "Specify local address(es) to listen on."
 msgstr "Tentukan alamat lokal untuk mendengarkan."
 
 # OK
-#: option.c:245
+#: option.c:333
 msgid "Return ipaddr for all hosts in specified domains."
 msgstr "Menghasilkan ipaddr untuk semua host dalam domain yang dipilih."
 
 # OK
-#: option.c:246
+#: option.c:334
 msgid "Fake reverse lookups for RFC1918 private address ranges."
 msgstr "Fake pencarian balik untuk alamat private sesuai dengan RFC1918."
 
 # OK
-#: option.c:247
+#: option.c:335
 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."
 msgstr "Perlakukan ipaddr sebagai NXDOMAIN (mengalahkan wildcard Verisign)."
 
 # OK
-#: option.c:248
+#: option.c:336
 #, c-format
 msgid "Specify the size of the cache in entries (defaults to %s)."
 msgstr "Tentukan ukuran cache, dalam jumlah isian (default %s)."
 
 # OK
-#: option.c:249
+#: option.c:337
 #, c-format
 msgid "Specify configuration file (defaults to %s)."
 msgstr "Tentukan file konfigurasi (default %s)."
 
 # OK
-#: option.c:250
+#: option.c:338
 msgid "Do NOT fork into the background: run in debug mode."
 msgstr "JANGAN berjalan di background: berjalan dalam modus debug."
 
 # OK
-#: option.c:251
+#: option.c:339
 msgid "Do NOT forward queries with no domain part."
 msgstr "JANGAN teruskan permintaan tanpa bagian domain."
 
 # OK
-#: option.c:252
+#: option.c:340
 msgid "Return self-pointing MX records for local hosts."
 msgstr "Mengembalikan record MX untuk diri sendiri host-host lokal."
 
 # OK
-#: option.c:253
+#: option.c:341
 msgid "Expand simple names in /etc/hosts with domain-suffix."
 msgstr "Melengkapi nama-nama di /etc/hosts dengan akhiran domain."
 
 # OK
-#: option.c:254
+#: option.c:342
 msgid "Don't forward spurious DNS requests from Windows hosts."
 msgstr "Jangan meneruskan permintaan DNS spurious dari host-host Windows."
 
 # OK
-#: option.c:255
+#: option.c:343
 msgid "Enable DHCP in the range given with lease duration."
 msgstr "Bolehkan DHCP dalam jangkauan yang diberikan dengan durasi lease."
 
 # OK
-#: option.c:256
+#: option.c:344
 #, c-format
 msgid "Change to this group after startup (defaults to %s)."
 msgstr "Ubah ke group ini setelah mulai (default %s)."
 
 # OK
-#: option.c:257
+#: option.c:345
 msgid "Set address or hostname for a specified machine."
 msgstr "Setel alamat atau nama host untuk mesin yang disebutkan."
 
 # OK
-#: option.c:258
+#: option.c:346
 #, fuzzy
 msgid "Read DHCP host specs from file."
 msgstr "nama MX salah"
 
-#: option.c:259
+#: option.c:347
 msgid "Read DHCP option specs from file."
 msgstr ""
 
-#: option.c:260
+# OK
+#: option.c:348
+#, fuzzy
+msgid "Read DHCP host specs from a directory."
+msgstr "nama MX salah"
+
+# OK
+#: option.c:349
+#, fuzzy
+msgid "Read DHCP options from a directory."
+msgstr "nama MX salah"
+
+#: option.c:350
 msgid "Evaluate conditional tag expression."
 msgstr ""
 
 # OK
-#: option.c:261
+#: option.c:351
 #, c-format
 msgid "Do NOT load %s file."
 msgstr "JANGAN muat file %s."
 
 # OK
-#: option.c:262
+#: option.c:352
 #, c-format
 msgid "Specify a hosts file to be read in addition to %s."
 msgstr "Sebutkan sebuah file hosts yang harus dibaca sebagai tambahan untuk %s."
 
 # OK
-#: option.c:263
+#: option.c:353
+#, fuzzy
+msgid "Read hosts files from a directory."
+msgstr "nama MX salah"
+
+# OK
+#: option.c:354
 msgid "Specify interface(s) to listen on."
 msgstr "Sebutkan antarmuka untuk mendengarkan."
 
 # OK
-#: option.c:264
+#: option.c:355
 msgid "Specify interface(s) NOT to listen on."
 msgstr "Sebutkan antarmuka untuk TIDAK mendengarkan."
 
 # OK
-#: option.c:265
+#: option.c:356
 #, fuzzy
 msgid "Map DHCP user class to tag."
 msgstr "Petakan kelas user DHCP ke setelan yang dipilih."
 
-#: option.c:266
+#: option.c:357
 msgid "Map RFC3046 circuit-id to tag."
 msgstr ""
 
-#: option.c:267
+#: option.c:358
 msgid "Map RFC3046 remote-id to tag."
 msgstr ""
 
-#: option.c:268
+#: option.c:359
 msgid "Map RFC3993 subscriber-id to tag."
 msgstr ""
 
 # OK
-#: option.c:269
+#: option.c:360
 #, fuzzy
 msgid "Don't do DHCP for hosts with tag set."
 msgstr "Jangan menggunakan DHCP untuk host-host yang dipilih."
 
 # OK
-#: option.c:270
+#: option.c:361
 #, fuzzy
 msgid "Force broadcast replies for hosts with tag set."
 msgstr "Jangan menggunakan DHCP untuk host-host yang dipilih."
 
 # OK
-#: option.c:271
+#: option.c:362
 msgid "Do NOT fork into the background, do NOT run in debug mode."
 msgstr "JANGAN berjalan di background, jangan berjalan dalam modus debug."
 
 # OK
-#: option.c:272
+#: option.c:363
 msgid "Assume we are the only DHCP server on the local network."
 msgstr "Berpikir bahwa kita satu-satunya DHCP server dalam jaringan."
 
 # OK
-#: option.c:273
+#: option.c:364
 #, c-format
 msgid "Specify where to store DHCP leases (defaults to %s)."
 msgstr "Sebutkan lokasi untuk menyimpan lease DHCP (default %s)."
 
 # OK
-#: option.c:274
+#: option.c:365
 msgid "Return MX records for local hosts."
 msgstr "Kembalikan rekord MX untuk host-host lokal."
 
 # OK
-#: option.c:275
+#: option.c:366
 msgid "Specify an MX record."
 msgstr "Sebutkan sebuah rekord MX."
 
 # OK
-#: option.c:276
+#: option.c:367
 msgid "Specify BOOTP options to DHCP server."
 msgstr "Sebutkan pilihan-pilihan BOOTP untuk DHCP server."
 
-#: option.c:277
+#: option.c:368
 #, c-format
 msgid "Do NOT poll %s file, reload only on SIGHUP."
 msgstr "Jangan kumpulkan file %s, muat kembali saat SIGHUP."
 
 # OK
-#: option.c:278
+#: option.c:369
 msgid "Do NOT cache failed search results."
 msgstr "JANGAN menyimpan hasil pencarian yang gagal."
 
 # OK
-#: option.c:279
+#: option.c:370
 #, c-format
 msgid "Use nameservers strictly in the order given in %s."
 msgstr "Gunakan secara ketat namaserver yang disebutkan sesuai urutan di %s."
 
 # OK
-#: option.c:280
+#: option.c:371
 #, fuzzy
 msgid "Specify options to be sent to DHCP clients."
 msgstr "Setel pilihan-pilihan tambahan yang akan disetel untuk klien-klien DHCP."
 
-#: option.c:281
+#: option.c:372
 msgid "DHCP option sent even if the client does not request it."
 msgstr ""
 
 # OK
-#: option.c:282
+#: option.c:373
 msgid "Specify port to listen for DNS requests on (defaults to 53)."
 msgstr "Sebutkan port untuk mendengarkan permintaan DNS (default port 53)."
 
 # OK
-#: option.c:283
+#: option.c:374
 #, c-format
 msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)."
 msgstr "Ukuran maksimum paket UDP yang didukung untuk EDNS.0 (default %s)."
 
 # OK
-#: option.c:284
+#: option.c:375
 #, fuzzy
 msgid "Log DNS queries."
 msgstr "Permintaan log."
 
 # OK
-#: option.c:285
+#: option.c:376
 #, fuzzy
 msgid "Force the originating port for upstream DNS queries."
 msgstr "Paksa port asal untuk permintaan ke atas."
 
 # OK
-#: option.c:286
+#: option.c:377
 msgid "Do NOT read resolv.conf."
 msgstr "JANGAN baca resolv.conf."
 
 # OK
-#: option.c:287
+#: option.c:378
 #, c-format
 msgid "Specify path to resolv.conf (defaults to %s)."
 msgstr "Sebutkan path ke resolv.conf (default %s)."
 
 # OK
-#: option.c:288
+#: option.c:379
+#, fuzzy
+msgid "Specify path to file with server= options"
+msgstr "Sebutkan path file PID. (default %s)."
+
+# OK
+#: option.c:380
 msgid "Specify address(es) of upstream servers with optional domains."
 msgstr "Sebutkan alamat-alamat server di atas, boleh dilengkapi dengan nama domain."
 
 # OK
-#: option.c:289
+#: option.c:381
+#, fuzzy
+msgid "Specify address of upstream servers for reverse address queries"
+msgstr "Sebutkan alamat-alamat server di atas, boleh dilengkapi dengan nama domain."
+
+# OK
+#: option.c:382
 msgid "Never forward queries to specified domains."
 msgstr "JANGAN pernah meneruskan permintaan ke domain yang disebutkan."
 
 # OK
-#: option.c:290
+#: option.c:383
 msgid "Specify the domain to be assigned in DHCP leases."
 msgstr "Sebutkan domain yang digunakan dalam lease DHCP."
 
 # OK
-#: option.c:291
+#: option.c:384
 msgid "Specify default target in an MX record."
 msgstr "Sebutkan tujuan default dalam rekord MX."
 
 # OK
-#: option.c:292
+#: option.c:385
 msgid "Specify time-to-live in seconds for replies from /etc/hosts."
 msgstr "Sebutkan time-to-live dalam detik untuk jawaban dari /etc/hosts."
 
 # OK
-#: option.c:293
+#: option.c:386
 #, fuzzy
 msgid "Specify time-to-live in seconds for negative caching."
 msgstr "Sebutkan time-to-live dalam detik untuk jawaban dari /etc/hosts."
 
 # OK
-#: option.c:294
+#: option.c:387
 #, fuzzy
 msgid "Specify time-to-live in seconds for maximum TTL to send to clients."
 msgstr "Sebutkan time-to-live dalam detik untuk jawaban dari /etc/hosts."
 
 # OK
-#: option.c:295
+#: option.c:388
+#, fuzzy
+msgid "Specify time-to-live ceiling for cache."
+msgstr "Sebutkan time-to-live dalam detik untuk jawaban dari /etc/hosts."
+
+# OK
+#: option.c:389
+#, fuzzy
+msgid "Specify time-to-live floor for cache."
+msgstr "Sebutkan time-to-live dalam detik untuk jawaban dari /etc/hosts."
+
+# OK
+#: option.c:390
 #, c-format
 msgid "Change to this user after startup. (defaults to %s)."
 msgstr "Ubah ke user ini setelah mulai. (default %s)."
 
 # OK
-#: option.c:296
+#: option.c:391
 #, fuzzy
 msgid "Map DHCP vendor class to tag."
 msgstr "Memetakan kelas vendor DHCP ke daftar pilihan."
 
 # OK
-#: option.c:297
+#: option.c:392
 msgid "Display dnsmasq version and copyright information."
 msgstr "Menampilkan versi dan informasi hak cipta dnsmasq."
 
 # OK
-#: option.c:298
+#: option.c:393
 msgid "Translate IPv4 addresses from upstream servers."
 msgstr "Terjemahkan alamat-alamat IPv4 dari server-server di atas."
 
 # OK
-#: option.c:299
+#: option.c:394
 msgid "Specify a SRV record."
 msgstr "Sebutkan rekord SRV."
 
-#: option.c:300
+#: option.c:395
 msgid "Display this message. Use --help dhcp for known DHCP options."
 msgstr ""
 
 # OK
-#: option.c:301
+#: option.c:396
 #, fuzzy, c-format
 msgid "Specify path of PID file (defaults to %s)."
 msgstr "Sebutkan path file PID. (default %s)."
 
 # OK
-#: option.c:302
+#: option.c:397
 #, c-format
 msgid "Specify maximum number of DHCP leases (defaults to %s)."
 msgstr "Sebutkan jumlah maksimum lease DHCP (default %s)."
 
 # OK
-#: option.c:303
+#: option.c:398
 msgid "Answer DNS queries based on the interface a query was sent to."
 msgstr "Jawab permintaan DNS berdasarkan antarmuka dimana permintaan dikirimkan."
 
 # OK
-#: option.c:304
+#: option.c:399
 msgid "Specify TXT DNS record."
 msgstr "Sebutkan rekord TXT DNS."
 
 # OK
-#: option.c:305
+#: option.c:400
 #, fuzzy
 msgid "Specify PTR DNS record."
 msgstr "Sebutkan rekord TXT DNS."
 
-#: option.c:306
+#: option.c:401
 msgid "Give DNS name to IPv4 address of interface."
 msgstr ""
 
 # OK
-#: option.c:307
+#: option.c:402
 msgid "Bind only to interfaces in use."
 msgstr "Hanya kaitkan ke antarmuka yang sedang digunakan saja."
 
 # OK
-#: option.c:308
+#: option.c:403
 #, c-format
 msgid "Read DHCP static host information from %s."
 msgstr "Baca informasi statik host DHCP dari %s."
 
 # OK
-#: option.c:309
+#: option.c:404
 msgid "Enable the DBus interface for setting upstream servers, etc."
 msgstr "Mungkinkan antar muka DBus untuk menyetel server-server di atas, dsb."
 
 # OK
-#: option.c:310
+#: option.c:405
 msgid "Do not provide DHCP on this interface, only provide DNS."
 msgstr "JANGAN menyediakan DHCP pada antarmuka ini, hanya menyediakan DNS."
 
 # OK
-#: option.c:311
+#: option.c:406
 msgid "Enable dynamic address allocation for bootp."
 msgstr "Mungkinkan alokasi alamat dinamis untuk bootp."
 
 # OK
-#: option.c:312
+#: option.c:407
 #, fuzzy
 msgid "Map MAC address (with wildcards) to option set."
 msgstr "Memetakan kelas vendor DHCP ke daftar pilihan."
 
-#: option.c:313
+#: option.c:408
 msgid "Treat DHCP requests on aliases as arriving from interface."
 msgstr ""
 
-#: option.c:314
+#: option.c:409
 msgid "Disable ICMP echo address checking in the DHCP server."
 msgstr ""
 
-#: option.c:315
-msgid "Script to run on DHCP lease creation and destruction."
+#: option.c:410
+msgid "Shell script to run on DHCP lease creation and destruction."
+msgstr ""
+
+#: option.c:411
+msgid "Lua script to run on DHCP lease creation and destruction."
 msgstr ""
 
-#: option.c:316
+#: option.c:412
+msgid "Run lease-change scripts as this user."
+msgstr ""
+
+#: option.c:413
 msgid "Read configuration from all the files in this directory."
 msgstr ""
 
 # OK
-#: option.c:317
+#: option.c:414
 #, fuzzy
 msgid "Log to this syslog facility or file. (defaults to DAEMON)"
 msgstr "Ubah ke user ini setelah mulai. (default %s)."
 
-#: option.c:318
+#: option.c:415
 msgid "Do not use leasefile."
 msgstr ""
 
 # OK
-#: option.c:319
+#: option.c:416
 #, fuzzy, c-format
 msgid "Maximum number of concurrent DNS queries. (defaults to %s)"
 msgstr "Sebutkan jumlah maksimum lease DHCP (default %s)."
 
-#: option.c:320
+#: option.c:417
 #, c-format
 msgid "Clear DNS cache when reloading %s."
 msgstr ""
 
-#: option.c:321
+#: option.c:418
 msgid "Ignore hostnames provided by DHCP clients."
 msgstr ""
 
-#: option.c:322
+#: option.c:419
 msgid "Do NOT reuse filename and server fields for extra DHCP options."
 msgstr ""
 
-#: option.c:323
+#: option.c:420
 msgid "Enable integrated read-only TFTP server."
 msgstr ""
 
-#: option.c:324
+#: option.c:421
 msgid "Export files by TFTP only from the specified subtree."
 msgstr ""
 
-#: option.c:325
+#: option.c:422
 msgid "Add client IP address to tftp-root."
 msgstr ""
 
-#: option.c:326
+#: option.c:423
 msgid "Allow access only to files owned by the user running dnsmasq."
 msgstr ""
 
+#: option.c:424
+msgid "Do not terminate the service if TFTP directories are inaccessible."
+msgstr ""
+
 # OK
-#: option.c:327
+#: option.c:425
 #, fuzzy, c-format
 msgid "Maximum number of conncurrent TFTP transfers (defaults to %s)."
 msgstr "Sebutkan jumlah maksimum lease DHCP (default %s)."
 
-#: option.c:328
+#: option.c:426
 msgid "Disable the TFTP blocksize extension."
 msgstr ""
 
-#: option.c:329
+#: option.c:427
+msgid "Convert TFTP filenames to lowercase"
+msgstr ""
+
+#: option.c:428
 msgid "Ephemeral port range for use by TFTP transfers."
 msgstr ""
 
-#: option.c:330
+#: option.c:429
 msgid "Extra logging for DHCP."
 msgstr ""
 
-#: option.c:331
+#: option.c:430
 msgid "Enable async. logging; optionally set queue length."
 msgstr ""
 
-#: option.c:332
+#: option.c:431
 msgid "Stop DNS rebinding. Filter private IP ranges when resolving."
 msgstr ""
 
-#: option.c:333
+#: option.c:432
 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers."
 msgstr ""
 
-#: option.c:334
+#: option.c:433
 msgid "Inhibit DNS-rebind protection on this domain."
 msgstr ""
 
-#: option.c:335
+#: option.c:434
 msgid "Always perform DNS queries to all servers."
 msgstr ""
 
-#: option.c:336
+#: option.c:435
 msgid "Set tag if client includes matching option in request."
 msgstr ""
 
-#: option.c:337
+#: option.c:436
 msgid "Use alternative ports for DHCP."
 msgstr ""
 
-#: option.c:338
-msgid "Run lease-change script as this user."
-msgstr ""
-
 # OK
-#: option.c:339
+#: option.c:437
 #, fuzzy
 msgid "Specify NAPTR DNS record."
 msgstr "Sebutkan rekord TXT DNS."
 
-#: option.c:340
+#: option.c:438
 msgid "Specify lowest port available for DNS query transmission."
 msgstr ""
 
-#: option.c:341
+#: option.c:439
 msgid "Use only fully qualified domain names for DHCP clients."
 msgstr ""
 
-#: option.c:342
+#: option.c:440
 msgid "Generate hostnames based on MAC address for nameless clients."
 msgstr ""
 
-#: option.c:343
+#: option.c:441
 msgid "Use these DHCP relays as full proxies."
 msgstr ""
 
-#: option.c:344
+#: option.c:442
+msgid "Relay DHCP requests to a remote server"
+msgstr ""
+
+#: option.c:443
 msgid "Specify alias name for LOCAL DNS name."
 msgstr ""
 
 # OK
-#: option.c:345
+#: option.c:444
 #, fuzzy
 msgid "Prompt to send to PXE clients."
 msgstr "Setel pilihan-pilihan tambahan yang akan disetel untuk klien-klien DHCP."
 
-#: option.c:346
+#: option.c:445
 msgid "Boot service for PXE menu."
 msgstr ""
 
-#: option.c:347
+#: option.c:446
 msgid "Check configuration syntax."
 msgstr ""
 
-#: option.c:348
-msgid "Add requestor's MAC address to forwarded DNS queries"
+#: option.c:447
+msgid "Add requestor's MAC address to forwarded DNS queries."
+msgstr ""
+
+#: option.c:448
+msgid "Add requestor's IP subnet to forwarded DNS queries."
 msgstr ""
 
 # OK
-#: option.c:349
+#: option.c:449
 #, fuzzy
-msgid "Proxy DNSSEC validation results from upstream nameservers"
+msgid "Proxy DNSSEC validation results from upstream nameservers."
 msgstr "Terjemahkan alamat-alamat IPv4 dari server-server di atas."
 
+#: option.c:450
+msgid "Attempt to allocate sequential IP addresses to DHCP clients."
+msgstr ""
+
+#: option.c:451
+msgid "Copy connection-track mark from queries to upstream connections."
+msgstr ""
+
+#: option.c:452
+msgid "Allow DHCP clients to do their own DDNS updates."
+msgstr ""
+
+#: option.c:453
+msgid "Send router-advertisements for interfaces doing DHCPv6"
+msgstr ""
+
+#: option.c:454
+msgid "Specify DUID_EN-type DHCPv6 server DUID"
+msgstr ""
+
+# OK
+#: option.c:455
+#, fuzzy
+msgid "Specify host (A/AAAA and PTR) records"
+msgstr "Sebutkan sebuah rekord MX."
+
+# OK
+#: option.c:456
+#, fuzzy
+msgid "Specify arbitrary DNS resource record"
+msgstr "Sebutkan rekord TXT DNS."
+
+# OK
+#: option.c:457
+#, fuzzy
+msgid "Bind to interfaces in use - check for new interfaces"
+msgstr "antarmuka tidak dikenal %s"
+
+#: option.c:458
+msgid "Export local names to global DNS"
+msgstr ""
+
+#: option.c:459
+msgid "Domain to export to global DNS"
+msgstr ""
+
+#: option.c:460
+msgid "Set TTL for authoritative replies"
+msgstr ""
+
+#: option.c:461
+msgid "Set authoritive zone information"
+msgstr ""
+
+#: option.c:462
+msgid "Secondary authoritative nameservers for forward domains"
+msgstr ""
+
+#: option.c:463
+msgid "Peers which are allowed to do zone transfer"
+msgstr ""
+
+#: option.c:464
+msgid "Specify ipsets to which matching domains should be added"
+msgstr ""
+
+#: option.c:465
+msgid "Specify a domain and address range for synthesised names"
+msgstr ""
+
+#: option.c:466
+msgid "Activate DNSSEC validation"
+msgstr ""
+
+#: option.c:467
+msgid "Specify trust anchor key digest."
+msgstr ""
+
+#: option.c:468
+msgid "Disable upstream checking for DNSSEC debugging."
+msgstr ""
+
+#: option.c:469
+msgid "Ensure answers without DNSSEC are in unsigned zones."
+msgstr ""
+
+#: option.c:470
+msgid "Don't check DNSSEC signature timestamps until first cache-reload"
+msgstr ""
+
+#: option.c:471
+msgid "Timestamp file to verify system clock for DNSSEC"
+msgstr ""
+
+#: option.c:473
+msgid "Specify DHCPv6 prefix class"
+msgstr ""
+
+#: option.c:475
+msgid "Set priority, resend-interval and router-lifetime"
+msgstr ""
+
+#: option.c:476
+msgid "Do not log routine DHCP."
+msgstr ""
+
+#: option.c:477
+msgid "Do not log routine DHCPv6."
+msgstr ""
+
+#: option.c:478
+msgid "Do not log RA."
+msgstr ""
+
+#: option.c:479
+msgid "Accept queries only from directly-connected networks"
+msgstr ""
+
+#: option.c:480
+msgid "Detect and remove DNS forwarding loops"
+msgstr ""
+
+#: option.c:481
+msgid "Ignore DNS responses containing ipaddr."
+msgstr ""
+
 # OK
-#: option.c:638
+#: option.c:683
 #, c-format
 msgid ""
 "Usage: dnsmasq [options]\n"
@@ -644,1097 +847,1657 @@ msgstr ""
 "\n"
 
 # OK
-#: option.c:640
+#: option.c:685
 #, c-format
 msgid "Use short options only on the command line.\n"
 msgstr "Gunakan pilihan pendek saja pada perintah baris.\n"
 
 # OK
-#: option.c:642
+#: option.c:687
 #, fuzzy, c-format
 msgid "Valid options are:\n"
 msgstr "Pilihan yang boleh adalah:\n"
 
-#: option.c:683
-#, c-format
-msgid "Known DHCP options:\n"
+# OK
+#: option.c:744 option.c:748
+msgid "bad port"
+msgstr "port salah"
+
+#: option.c:775 option.c:807
+msgid "interface binding not supported"
+msgstr ""
+
+# OK
+#: option.c:784 option.c:3575
+#, fuzzy
+msgid "bad interface name"
+msgstr "nama MX salah"
+
+# OK
+#: option.c:814
+#, fuzzy
+msgid "bad address"
+msgstr "membaca %s - %d alamat"
+
+#: option.c:996
+msgid "unsupported encapsulation for IPv6 option"
 msgstr ""
 
 # OK
-#: option.c:798
+#: option.c:1010
 msgid "bad dhcp-option"
 msgstr "dhcp-option salah"
 
 # OK
-#: option.c:860
+#: option.c:1078
 #, fuzzy
 msgid "bad IP address"
 msgstr "membaca %s - %d alamat"
 
 # OK
-#: option.c:968
+#: option.c:1081 option.c:1219 option.c:2893
+#, fuzzy
+msgid "bad IPv6 address"
+msgstr "membaca %s - %d alamat"
+
+# OK
+#: option.c:1246 option.c:1340
 msgid "bad domain in dhcp-option"
 msgstr "domain dalam dhcp-option salah"
 
 # OK
-#: option.c:1034
+#: option.c:1378
 msgid "dhcp-option too long"
 msgstr "dhcp-option terlalu panjang"
 
-#: option.c:1043
+#: option.c:1385
 msgid "illegal dhcp-match"
 msgstr ""
 
-#: option.c:1087
+#: option.c:1447
 msgid "illegal repeated flag"
 msgstr ""
 
-#: option.c:1095
+#: option.c:1455
 msgid "illegal repeated keyword"
 msgstr ""
 
 # OK
-#: option.c:1147 option.c:3030
+#: option.c:1520 option.c:4191
 #, fuzzy, c-format
 msgid "cannot access directory %s: %s"
 msgstr "tidak bisa membaca %s: %s"
 
 # OK
-#: option.c:1178 tftp.c:460
+#: option.c:1566 tftp.c:493
 #, fuzzy, c-format
 msgid "cannot access %s: %s"
 msgstr "tidak bisa membaca %s: %s"
 
-#: option.c:1207
+#: option.c:1618
 msgid "setting log facility is not possible under Android"
 msgstr ""
 
-#: option.c:1216
+#: option.c:1627
 msgid "bad log facility"
 msgstr ""
 
 # OK
-#: option.c:1265
+#: option.c:1680
 msgid "bad MX preference"
 msgstr "kesukaan MX salah"
 
 # OK
-#: option.c:1270
+#: option.c:1685
 msgid "bad MX name"
 msgstr "nama MX salah"
 
 # OK
-#: option.c:1284
+#: option.c:1699
 msgid "bad MX target"
 msgstr "target MX salah"
 
-#: option.c:1294
+#: option.c:1711
 msgid "cannot run scripts under uClinux"
 msgstr ""
 
-#: option.c:1296
+#: option.c:1713
 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts"
 msgstr ""
 
+#: option.c:1717
+msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts"
+msgstr ""
+
 # OK
-#: option.c:1597 option.c:1601
-msgid "bad port"
+#: option.c:1973 option.c:2018 option.c:2074
+#, fuzzy
+msgid "bad prefix"
 msgstr "port salah"
 
-#: option.c:1620 option.c:1645
-msgid "interface binding not supported"
+#: option.c:2355
+msgid "recompile with HAVE_IPSET defined to enable ipset directives"
 msgstr ""
 
 # OK
-#: option.c:1791
+#: option.c:2548
 #, fuzzy
 msgid "bad port range"
 msgstr "port salah"
 
-#: option.c:1808
+#: option.c:2564
 msgid "bad bridge-interface"
 msgstr ""
 
+#: option.c:2624
+msgid "only one tag allowed"
+msgstr ""
+
 # OK
-#: option.c:1850
+#: option.c:2644 option.c:2656 option.c:2764 option.c:2805
 msgid "bad dhcp-range"
 msgstr "dhcp-range salah"
 
-#: option.c:1878
-msgid "only one tag allowed"
+# OK
+#: option.c:2671
+msgid "inconsistent DHCP range"
+msgstr "jangkauan DHCP tidak konsisten"
+
+#: option.c:2732
+msgid "prefix length must be exactly 64 for RA subnets"
+msgstr ""
+
+#: option.c:2734
+msgid "prefix length must be exactly 64 for subnet constructors"
+msgstr ""
+
+#: option.c:2738
+msgid "prefix length must be at least 64"
 msgstr ""
 
 # OK
-#: option.c:1925
-msgid "inconsistent DHCP range"
+#: option.c:2741
+#, fuzzy
+msgid "inconsistent DHCPv6 range"
 msgstr "jangkauan DHCP tidak konsisten"
 
+#: option.c:2752
+msgid "prefix must be zero with \"constructor:\" argument"
+msgstr ""
+
 # OK
-#: option.c:2019 option.c:2045
+#: option.c:2863 option.c:2911
 #, fuzzy
 msgid "bad hex constant"
 msgstr "dhcp-host salah"
 
+#: option.c:2885
+msgid "cannot match tags in --dhcp-host"
+msgstr ""
+
 # OK
-#: option.c:2107
+#: option.c:2933
+#, fuzzy, c-format
+msgid "duplicate dhcp-host IP address %s"
+msgstr "alamat IP kembar %s dalam direktif dhcp-config"
+
+# OK
+#: option.c:2991
 #, fuzzy
 msgid "bad DHCP host name"
 msgstr "nama MX salah"
 
 # OK
-#: option.c:2188
+#: option.c:3073
 #, fuzzy
 msgid "bad tag-if"
 msgstr "target MX salah"
 
 # OK
-#: option.c:2467 option.c:2752
+#: option.c:3397 option.c:3791
 msgid "invalid port number"
 msgstr "nomor port tidak benar"
 
 # OK
-#: option.c:2529
+#: option.c:3459
 #, fuzzy
 msgid "bad dhcp-proxy address"
 msgstr "membaca %s - %d alamat"
 
 # OK
-#: option.c:2569
+#: option.c:3485
 #, fuzzy
-msgid "invalid alias range"
-msgstr "weight tidak benar"
+msgid "Bad dhcp-relay"
+msgstr "dhcp-range salah"
+
+#: option.c:3511
+msgid "bad RA-params"
+msgstr ""
+
+#: option.c:3520
+msgid "bad DUID"
+msgstr ""
 
 # OK
-#: option.c:2582
+#: option.c:3562
 #, fuzzy
-msgid "bad interface name"
-msgstr "nama MX salah"
+msgid "invalid alias range"
+msgstr "weight tidak benar"
 
-#: option.c:2607
+#: option.c:3616
 msgid "bad CNAME"
 msgstr ""
 
-#: option.c:2612
+#: option.c:3621
 msgid "duplicate CNAME"
 msgstr ""
 
 # OK
-#: option.c:2632
+#: option.c:3641
 #, fuzzy
 msgid "bad PTR record"
 msgstr "rekord SRV salah"
 
 # OK
-#: option.c:2663
+#: option.c:3672
 #, fuzzy
 msgid "bad NAPTR record"
 msgstr "rekord SRV salah"
 
 # OK
-#: option.c:2695
+#: option.c:3706
+#, fuzzy
+msgid "bad RR record"
+msgstr "rekord SRV salah"
+
+# OK
+#: option.c:3736
 msgid "bad TXT record"
 msgstr "rekord TXT salah"
 
 # OK
-#: option.c:2738
+#: option.c:3777
 msgid "bad SRV record"
 msgstr "rekord SRV salah"
 
 # OK
-#: option.c:2745
+#: option.c:3784
 msgid "bad SRV target"
 msgstr "target SRV salah"
 
 # OK
-#: option.c:2759
+#: option.c:3798
 msgid "invalid priority"
 msgstr "prioritas tidak benar"
 
 # OK
-#: option.c:2766
+#: option.c:3805
 msgid "invalid weight"
 msgstr "weight tidak benar"
 
-#: option.c:2785
-msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"
+# OK
+#: option.c:3829
+#, fuzzy
+msgid "Bad host-record"
+msgstr "rekord SRV salah"
+
+# OK
+#: option.c:3846
+#, fuzzy
+msgid "Bad name in host-record"
+msgstr "kesalahan nama di %s"
+
+# OK
+#: option.c:3911
+#, fuzzy
+msgid "bad trust anchor"
+msgstr "port salah"
+
+#: option.c:3925
+msgid "bad HEX in trust anchor"
+msgstr ""
+
+#: option.c:3935
+msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"
 msgstr ""
 
 # OK
-#: option.c:2849
+#: option.c:3994
 msgid "missing \""
 msgstr "kurang \""
 
 # OK
-#: option.c:2908
+#: option.c:4051
 msgid "bad option"
 msgstr "pilihan salah"
 
 # OK
-#: option.c:2910
+#: option.c:4053
 msgid "extraneous parameter"
 msgstr "parameter berlebihan"
 
 # OK
-#: option.c:2912
+#: option.c:4055
 msgid "missing parameter"
 msgstr "parameter kurang"
 
 # OK
-#: option.c:2916
+#: option.c:4057
+#, fuzzy
+msgid "illegal option"
+msgstr "pilihan salah"
+
+# OK
+#: option.c:4064
 msgid "error"
 msgstr "kesalahan"
 
 # OK
-#: option.c:2921
-#, c-format
-msgid "%s at line %d of %%s"
+#: option.c:4066
+#, fuzzy, c-format
+msgid " at line %d of %s"
 msgstr "%s pada baris %d dari %%s"
 
 # OK
-#: option.c:2985 tftp.c:624
-#, c-format
-msgid "cannot read %s: %s"
-msgstr "tidak bisa membaca %s: %s"
-
-# OK
-#: option.c:3151 option.c:3187
+#: option.c:4081 option.c:4328 option.c:4364
 #, fuzzy, c-format
 msgid "read %s"
 msgstr "membaca %s"
 
-#: option.c:3239
+# OK
+#: option.c:4144 option.c:4267 tftp.c:667
+#, c-format
+msgid "cannot read %s: %s"
+msgstr "tidak bisa membaca %s: %s"
+
+#: option.c:4430
 msgid "junk found in command line"
 msgstr ""
 
 # OK
-#: option.c:3269
+#: option.c:4465
 #, c-format
 msgid "Dnsmasq version %s  %s\n"
 msgstr "Dnsmasq versi %s  %s\n"
 
 # OK
-#: option.c:3270
-#, c-format
+#: option.c:4466
+#, fuzzy, c-format
 msgid ""
-"Compile time options %s\n"
+"Compile time options: %s\n"
 "\n"
 msgstr ""
 "Pilihan-pilihan saat kompilasi %s\n"
 "\n"
 
 # OK
-#: option.c:3271
+#: option.c:4467
 #, c-format
 msgid "This software comes with ABSOLUTELY NO WARRANTY.\n"
 msgstr "Perangkat lunak ini tersedia TANPA JAMINAN SEDIKITPUN.\n"
 
 # OK
-#: option.c:3272
+#: option.c:4468
 #, c-format
 msgid "Dnsmasq is free software, and you are welcome to redistribute it\n"
 msgstr "Dnsdmasq adalah perangkat lunak bebas, dan Anda dipersilahkan untuk membagikannya\n"
 
 # OK
-#: option.c:3273
+#: option.c:4469
 #, fuzzy, c-format
 msgid "under the terms of the GNU General Public License, version 2 or 3.\n"
 msgstr "dengan aturan GNU General Public License, versi 2.\n"
 
-#: option.c:3284
+#: option.c:4480
 msgid "try --help"
 msgstr ""
 
-#: option.c:3286
+#: option.c:4482
 msgid "try -w"
 msgstr ""
 
 # OK
-#: option.c:3289
+#: option.c:4484
 #, fuzzy, c-format
 msgid "bad command line options: %s"
 msgstr "pilihan baris perintah salah: %s."
 
 # OK
-#: option.c:3330
+#: option.c:4541
 #, c-format
 msgid "cannot get host-name: %s"
 msgstr "tidak bisa mendapatkan host-name: %s"
 
 # OK
-#: option.c:3358
+#: option.c:4569
 msgid "only one resolv.conf file allowed in no-poll mode."
 msgstr "hanya satu file resolv.conf yang diperbolehkan dalam modus no-poll."
 
 # OK
-#: option.c:3368
+#: option.c:4579
 msgid "must have exactly one resolv.conf to read domain from."
 msgstr "harus mempunyai tepat satu resolv.conf untuk mendapatkan nama domain."
 
 # OK
-#: option.c:3371 network.c:848 dhcp.c:814
+#: option.c:4582 network.c:1507 dhcp.c:777
 #, fuzzy, c-format
 msgid "failed to read %s: %s"
 msgstr "gagal membaca %s: %s"
 
 # OK
-#: option.c:3388
+#: option.c:4599
 #, c-format
 msgid "no search directive found in %s"
 msgstr "tidak ditemukan direktif search di %s"
 
-#: option.c:3409
+#: option.c:4620
 msgid "there must be a default domain when --dhcp-fqdn is set"
 msgstr ""
 
-#: option.c:3413
+#: option.c:4629
 msgid "syntax check OK"
 msgstr ""
 
 # OK
-#: forward.c:461
-#, c-format
-msgid "nameserver %s refused to do a recursive query"
-msgstr "nameserver %s menolak melakukan resolusi rekursif"
+#: forward.c:111
+#, fuzzy, c-format
+msgid "failed to send packet: %s"
+msgstr "gagal mendengarkan di socket: %s"
 
-#: forward.c:489
+#: forward.c:591
+msgid "discarding DNS reply: subnet option mismatch"
+msgstr ""
+
+# OK
+#: forward.c:614
+#, c-format
+msgid "nameserver %s refused to do a recursive query"
+msgstr "nameserver %s menolak melakukan resolusi rekursif"
+
+#: forward.c:646
 #, c-format
 msgid "possible DNS-rebind attack detected: %s"
 msgstr ""
 
+#: forward.c:1209 forward.c:1815
+msgid "Ignoring query from non-local network"
+msgstr ""
+
 # OK
-#: network.c:171
+#: forward.c:2286
 #, fuzzy, c-format
-msgid "unknown interface %s in bridge-interface"
-msgstr "antarmuka tidak dikenal %s"
+msgid "Maximum number of concurrent DNS queries reached (max: %d)"
+msgstr "Sebutkan jumlah maksimum lease DHCP (default %s)."
 
 # OK
-#: network.c:380
+#: network.c:715
 #, fuzzy, c-format
 msgid "failed to create listening socket for %s: %s"
 msgstr "gagal membuat socket: %s "
 
-#: network.c:746
+#: network.c:1021
+#, c-format
+msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s"
+msgstr ""
+
+#: network.c:1028
+msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)"
+msgstr ""
+
+# OK
+#: network.c:1037
+#, fuzzy, c-format
+msgid "warning: no addresses found for interface %s"
+msgstr "menggunakan alamat lokal saja untuk %s %s"
+
+# OK
+#: network.c:1095
+#, fuzzy, c-format
+msgid "interface %s failed to join DHCPv6 multicast group: %s"
+msgstr "gagal mem-bind socket server DHCP: %s"
+
+#: network.c:1289
 #, fuzzy, c-format
 msgid "failed to bind server socket for %s: %s"
 msgstr "gagal mem-bind socket untuk mendengarkan %s: %s"
 
 # OK
-#: network.c:783
+#: network.c:1445
 #, c-format
 msgid "ignoring nameserver %s - local interface"
 msgstr "mengabaikan nameserver %s - antarmuka lokal"
 
 # OK
-#: network.c:794
+#: network.c:1456
 #, fuzzy, c-format
 msgid "ignoring nameserver %s - cannot make/bind socket: %s"
 msgstr "mengabaikan nameserver %s - tak dapat membuat/mem-bind socket: %s"
 
 # OK
-#: network.c:811
+#: network.c:1469
 msgid "unqualified"
 msgstr "tidak memenuhi syarat"
 
-#: network.c:811
+#: network.c:1469
 msgid "names"
 msgstr ""
 
-#: network.c:813
+#: network.c:1471
 msgid "default"
 msgstr ""
 
 # OK
-#: network.c:815
+#: network.c:1473
 msgid "domain"
 msgstr "domain"
 
 # OK
-#: network.c:818
+#: network.c:1476
 #, c-format
 msgid "using local addresses only for %s %s"
 msgstr "menggunakan alamat lokal saja untuk %s %s"
 
 # OK
-#: network.c:820
+#: network.c:1478
 #, fuzzy, c-format
 msgid "using standard nameservers for %s %s"
 msgstr "menggunakan nameserver %s#%d untuk %s %s"
 
 # OK
-#: network.c:822
+#: network.c:1480
 #, c-format
 msgid "using nameserver %s#%d for %s %s"
 msgstr "menggunakan nameserver %s#%d untuk %s %s"
 
 # OK
-#: network.c:825
+#: network.c:1484
+#, fuzzy, c-format
+msgid "NOT using nameserver %s#%d - query loop detected"
+msgstr "menggunakan nameserver %s#%d untuk %s %s"
+
+# OK
+#: network.c:1487
 #, fuzzy, c-format
 msgid "using nameserver %s#%d(via %s)"
 msgstr "menggunakan nameserver %s#%d"
 
 # OK
-#: network.c:827
+#: network.c:1489
 #, c-format
 msgid "using nameserver %s#%d"
 msgstr "menggunakan nameserver %s#%d"
 
+#: dnsmasq.c:163
+msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"
+msgstr ""
+
+#: dnsmasq.c:170
+msgid "no trust anchors provided for DNSSEC"
+msgstr ""
+
+#: dnsmasq.c:173
+msgid "cannot reduce cache size from default when DNSSEC enabled"
+msgstr ""
+
+# OK
+#: dnsmasq.c:175
+#, fuzzy
+msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h"
+msgstr "DBus tidak tersedia: setel HAVE_DBUS dalam src/config.h"
+
 # OK
-#: dnsmasq.c:148
+#: dnsmasq.c:181
 #, fuzzy
 msgid "TFTP server not available: set HAVE_TFTP in src/config.h"
 msgstr "DBus tidak tersedia: setel HAVE_DBUS dalam src/config.h"
 
-#: dnsmasq.c:153
+#: dnsmasq.c:186
+msgid "cannot use --conntrack AND --query-port"
+msgstr ""
+
+# OK
+#: dnsmasq.c:189
+#, fuzzy
+msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h"
+msgstr "DBus tidak tersedia: setel HAVE_DBUS dalam src/config.h"
+
+#: dnsmasq.c:194
 msgid "asychronous logging is not available under Solaris"
 msgstr ""
 
-#: dnsmasq.c:158
+#: dnsmasq.c:199
 msgid "asychronous logging is not available under Android"
 msgstr ""
 
 # OK
-#: dnsmasq.c:177
+#: dnsmasq.c:204
+#, fuzzy
+msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h"
+msgstr "DBus tidak tersedia: setel HAVE_DBUS dalam src/config.h"
+
+# OK
+#: dnsmasq.c:209
+#, fuzzy
+msgid "loop detection not available: set HAVE_LOOP in src/config.h"
+msgstr "DBus tidak tersedia: setel HAVE_DBUS dalam src/config.h"
+
+#: dnsmasq.c:217
+msgid "zone serial must be configured in --auth-soa"
+msgstr ""
+
+#: dnsmasq.c:235
+msgid "dhcp-range constructor not available on this platform"
+msgstr ""
+
+#: dnsmasq.c:278
+msgid "cannot set --bind-interfaces and --bind-dynamic"
+msgstr ""
+
+# OK
+#: dnsmasq.c:281
 #, c-format
 msgid "failed to find list of interfaces: %s"
 msgstr "gagal mendapatkan daftar antarmuka: %s"
 
 # OK
-#: dnsmasq.c:185
+#: dnsmasq.c:290
 #, c-format
 msgid "unknown interface %s"
 msgstr "antarmuka tidak dikenal %s"
 
 # OK
-#: dnsmasq.c:191
-#, c-format
-msgid "no interface with address %s"
-msgstr "tidak ada antarmuka dengan alamat %s"
-
-# OK
-#: dnsmasq.c:207 dnsmasq.c:678
+#: dnsmasq.c:354 dnsmasq.c:997
 #, c-format
 msgid "DBus error: %s"
 msgstr "DBus error: %s"
 
 # OK
-#: dnsmasq.c:210
+#: dnsmasq.c:357
 msgid "DBus not available: set HAVE_DBUS in src/config.h"
 msgstr "DBus tidak tersedia: setel HAVE_DBUS dalam src/config.h"
 
-#: dnsmasq.c:236
+#: dnsmasq.c:385
 #, c-format
 msgid "unknown user or group: %s"
 msgstr ""
 
-#: dnsmasq.c:291
+#: dnsmasq.c:440
 #, c-format
 msgid "cannot chdir to filesystem root: %s"
 msgstr ""
 
 # OK
-#: dnsmasq.c:455
+#: dnsmasq.c:692
 #, fuzzy, c-format
 msgid "started, version %s DNS disabled"
 msgstr "dimulai, cache versi %s di disable"
 
 # OK
-#: dnsmasq.c:457
+#: dnsmasq.c:694
 #, c-format
 msgid "started, version %s cachesize %d"
 msgstr "dimulai, versi %s ukuran cache %d"
 
 # OK
-#: dnsmasq.c:459
+#: dnsmasq.c:696
 #, c-format
 msgid "started, version %s cache disabled"
 msgstr "dimulai, cache versi %s di disable"
 
 # OK
-#: dnsmasq.c:461
+#: dnsmasq.c:698
 #, c-format
 msgid "compile time options: %s"
 msgstr "pilihan-pilihan saat kompilasi: %s"
 
 # OK
-#: dnsmasq.c:467
+#: dnsmasq.c:704
 msgid "DBus support enabled: connected to system bus"
 msgstr "dukungan DBus dimungkinkan: terkoneksi pada bus sistem"
 
 # OK
-#: dnsmasq.c:469
+#: dnsmasq.c:706
 msgid "DBus support enabled: bus connection pending"
 msgstr "dukungan DBus dimungkinkan: koneksi bus ditunda"
 
+#: dnsmasq.c:711
+msgid "DNS service limited to local subnets"
+msgstr ""
+
+#: dnsmasq.c:727
+msgid "DNSSEC validation enabled"
+msgstr ""
+
+#: dnsmasq.c:730
+msgid "DNSSEC signature timestamps not checked until first cache reload"
+msgstr ""
+
+#: dnsmasq.c:733
+msgid "DNSSEC signature timestamps not checked until system time valid"
+msgstr ""
+
 # OK
-#: dnsmasq.c:474
+#: dnsmasq.c:738
 #, fuzzy, c-format
 msgid "warning: failed to change owner of %s: %s"
 msgstr "gagal memuat nama-nama dari %s: %s"
 
 # OK
-#: dnsmasq.c:478
+#: dnsmasq.c:742
 msgid "setting --bind-interfaces option because of OS limitations"
 msgstr "setelan opsi --bind-interfaces disebabkan keterbatasan OS"
 
 # OK
-#: dnsmasq.c:483
+#: dnsmasq.c:752
 #, c-format
 msgid "warning: interface %s does not currently exist"
 msgstr "peringatan: antarmuka %s tidak ada"
 
-#: dnsmasq.c:488
+#: dnsmasq.c:757
 msgid "warning: ignoring resolv-file flag because no-resolv is set"
 msgstr ""
 
 # OK
-#: dnsmasq.c:491
+#: dnsmasq.c:760
 #, fuzzy
 msgid "warning: no upstream servers configured"
 msgstr "menyetel server-server di atas dengan DBus"
 
-#: dnsmasq.c:495
+#: dnsmasq.c:764
 #, c-format
 msgid "asynchronous logging enabled, queue limit is %d messages"
 msgstr ""
 
-# OK
-#: dnsmasq.c:508
-#, c-format
-msgid "DHCP, static leases only on %.0s%s, lease time %s"
-msgstr "DHCP, lease static pada %.0s%s, waktu lease %s"
-
-#: dnsmasq.c:510
-#, c-format
-msgid "DHCP, proxy on subnet %.0s%s%.0s"
+#: dnsmasq.c:785
+msgid "IPv6 router advertisement enabled"
 msgstr ""
 
-# OK
-#: dnsmasq.c:511
+#: dnsmasq.c:790
 #, c-format
-msgid "DHCP, IP range %s -- %s, lease time %s"
-msgstr "DHCP, jangkaun IP %s -- %s, waktu lease %s"
+msgid "DHCP, sockets bound exclusively to interface %s"
+msgstr ""
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 msgid "root is "
 msgstr ""
 
 # OK
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 #, fuzzy
 msgid "enabled"
 msgstr "di disable"
 
-#: dnsmasq.c:528
+#: dnsmasq.c:806
 msgid "secure mode"
 msgstr ""
 
-#: dnsmasq.c:554
+#: dnsmasq.c:809
+#, c-format
+msgid "warning: %s inaccessible"
+msgstr ""
+
+#: dnsmasq.c:813
+#, c-format
+msgid "warning: TFTP directory %s inaccessible"
+msgstr ""
+
+#: dnsmasq.c:839
 #, c-format
 msgid "restricting maximum simultaneous TFTP transfers to %d"
 msgstr ""
 
 # OK
-#: dnsmasq.c:680
+#: dnsmasq.c:999
 msgid "connected to system DBus"
 msgstr "terhubung ke sistem DBus"
 
-#: dnsmasq.c:775
+#: dnsmasq.c:1149
 #, c-format
 msgid "cannot fork into background: %s"
 msgstr ""
 
 # OK
-#: dnsmasq.c:778
+#: dnsmasq.c:1152
 #, fuzzy, c-format
 msgid "failed to create helper: %s"
 msgstr "gagal membaca %s: %s"
 
-#: dnsmasq.c:781
+#: dnsmasq.c:1155
 #, c-format
 msgid "setting capabilities failed: %s"
 msgstr ""
 
 # OK
-#: dnsmasq.c:785
+#: dnsmasq.c:1158
 #, fuzzy, c-format
 msgid "failed to change user-id to %s: %s"
 msgstr "gagal memuat nama-nama dari %s: %s"
 
 # OK
-#: dnsmasq.c:790
+#: dnsmasq.c:1161
 #, fuzzy, c-format
 msgid "failed to change group-id to %s: %s"
 msgstr "gagal memuat nama-nama dari %s: %s"
 
 # OK
-#: dnsmasq.c:793
+#: dnsmasq.c:1164
 #, fuzzy, c-format
 msgid "failed to open pidfile %s: %s"
 msgstr "gagal membaca %s: %s"
 
 # OK
-#: dnsmasq.c:796
+#: dnsmasq.c:1167
 #, fuzzy, c-format
-msgid "cannot open %s: %s"
+msgid "cannot open log %s: %s"
 msgstr "tidak bisa membuka %s:%s"
 
-#: dnsmasq.c:851
+# OK
+#: dnsmasq.c:1170
+#, fuzzy, c-format
+msgid "failed to load Lua script: %s"
+msgstr "gagal memuat %S: %s"
+
+#: dnsmasq.c:1173
+#, c-format
+msgid "TFTP directory %s inaccessible: %s"
+msgstr ""
+
+# OK
+#: dnsmasq.c:1176
+#, fuzzy, c-format
+msgid "cannot create timestamp file %s: %s"
+msgstr "tidak dapat membuka atau membuat file lease: %s"
+
+#: dnsmasq.c:1197
+msgid "now checking DNSSEC signature timestamps"
+msgstr ""
+
+#: dnsmasq.c:1264
 #, c-format
-msgid "child process killed by signal %d"
+msgid "script process killed by signal %d"
 msgstr ""
 
-#: dnsmasq.c:855
+#: dnsmasq.c:1268
 #, c-format
-msgid "child process exited with status %d"
+msgid "script process exited with status %d"
 msgstr ""
 
 # OK
-#: dnsmasq.c:859
+#: dnsmasq.c:1272
 #, fuzzy, c-format
 msgid "failed to execute %s: %s"
 msgstr "gagal mengakses %s: %s"
 
-#: dnsmasq.c:903
+#: dnsmasq.c:1327
 msgid "exiting on receipt of SIGTERM"
 msgstr "keluar karena menerima SIGTERM"
 
 # OK
-#: dnsmasq.c:931
+#: dnsmasq.c:1355
 #, fuzzy, c-format
 msgid "failed to access %s: %s"
 msgstr "gagal mengakses %s: %s"
 
 # OK
-#: dnsmasq.c:961
+#: dnsmasq.c:1385
 #, c-format
 msgid "reading %s"
 msgstr "membaca %s"
 
 # OK
-#: dnsmasq.c:972
+#: dnsmasq.c:1396
 #, fuzzy, c-format
 msgid "no servers found in %s, will retry"
 msgstr "tidak ditemukan direktif search di %s"
 
 # OK
-#: dhcp.c:40
+#: dhcp.c:53
 #, c-format
 msgid "cannot create DHCP socket: %s"
 msgstr "tidak bisa membuat socket DHCP: %s"
 
 # OK
-#: dhcp.c:52
+#: dhcp.c:68
 #, c-format
 msgid "failed to set options on DHCP socket: %s"
 msgstr "gagal menyetel opsi pada socket DHCP: %s"
 
 # OK
-#: dhcp.c:65
+#: dhcp.c:89
 #, fuzzy, c-format
 msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"
 msgstr "gagal menyetel SO_REUSEADDR pada socket DHCP: %s"
 
 # OK
-#: dhcp.c:77
+#: dhcp.c:101
 #, c-format
 msgid "failed to bind DHCP server socket: %s"
 msgstr "gagal mem-bind socket server DHCP: %s"
 
 # OK
-#: dhcp.c:103
+#: dhcp.c:127
 #, c-format
 msgid "cannot create ICMP raw socket: %s."
 msgstr "tidak dapat membuat socket ICMP raw: %s"
 
+# OK
+#: dhcp.c:241 dhcp6.c:180
+#, fuzzy, c-format
+msgid "unknown interface %s in bridge-interface"
+msgstr "antarmuka tidak dikenal %s"
+
 #: dhcp.c:281
 #, c-format
 msgid "DHCP packet received on %s which has no address"
 msgstr ""
 
+#: dhcp.c:415
+#, c-format
+msgid "ARP-cache injection failed: %s"
+msgstr ""
+
 # OK
-#: dhcp.c:445
+#: dhcp.c:514
 #, c-format
 msgid "DHCP range %s -- %s is not consistent with netmask %s"
 msgstr "jangkauan DHCP %s -- %s tidak konsisten dengan netmask %s"
 
 # OK
-#: dhcp.c:852
+#: dhcp.c:815
 #, fuzzy, c-format
 msgid "bad line at %s line %d"
 msgstr "kesalahan nama pada %s baris %d"
 
-#: dhcp.c:895
+#: dhcp.c:858
 #, c-format
 msgid "ignoring %s line %d, duplicate name or IP address"
 msgstr ""
 
-# OK
-#: dhcp.c:978
+#: dhcp.c:1002 rfc3315.c:2135
 #, c-format
-msgid "duplicate IP address %s in dhcp-config directive."
-msgstr "alamat IP kembar %s dalam direktif dhcp-config"
-
-# OK
-#: dhcp.c:981
-#, fuzzy, c-format
-msgid "duplicate IP address %s in %s."
-msgstr "alamat IP kembar %s dalam direktif dhcp-config"
-
-#: dhcp.c:1024
-#, c-format
-msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgid "DHCP relay %s -> %s"
 msgstr ""
 
 # OK
-#: dhcp.c:1029
-#, c-format
-msgid "duplicate IP address %s (%s) in dhcp-config directive"
-msgstr "alamat IP kembar %s (%s) dalam direktif dhcp-config"
-
-# OK
-#: lease.c:67
+#: lease.c:61
 #, fuzzy, c-format
 msgid "cannot open or create lease file %s: %s"
 msgstr "tidak dapat membuka atau membuat file lease: %s"
 
 # OK
-#: lease.c:93
+#: lease.c:134
 msgid "too many stored leases"
 msgstr "terlalu banyak lease yang disimpan"
 
 # OK
-#: lease.c:129
+#: lease.c:165
 #, fuzzy, c-format
 msgid "cannot run lease-init script %s: %s"
 msgstr "tidak bisa membaca %s: %s"
 
-#: lease.c:135
+#: lease.c:171
 #, c-format
 msgid "lease-init script returned exit code %s"
 msgstr ""
 
 # OK
-#: lease.c:235
+#: lease.c:342
 #, fuzzy, c-format
 msgid "failed to write %s: %s (retry in %us)"
 msgstr "gagal membaca %s: %s"
 
+#: lease.c:906
+#, c-format
+msgid "Ignoring domain %s for DHCP host name %s"
+msgstr ""
+
 # OK
-#: rfc2131.c:315
+#: rfc2131.c:344
 #, c-format
 msgid "no address range available for DHCP request %s %s"
 msgstr "tidak ada alamat yang bisa dipakai untuk permintaan DHCP %s %s"
 
 # OK
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "with subnet selector"
 msgstr "dengan pemilih subnet"
 
 # OK
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "via"
 msgstr "lewat"
 
 # OK
-#: rfc2131.c:331
+#: rfc2131.c:357
 #, fuzzy, c-format
 msgid "%u available DHCP subnet: %s/%s"
 msgstr "tidak ada alamat yang bisa dipakai untuk permintaan DHCP %s %s"
 
-#: rfc2131.c:334
+#: rfc2131.c:360 rfc3315.c:300
 #, c-format
 msgid "%u available DHCP range: %s -- %s"
 msgstr ""
 
 # OK
-#: rfc2131.c:363
+#: rfc2131.c:471
+#, fuzzy, c-format
+msgid "%u vendor class: %s"
+msgstr "DBus error: %s"
+
+# OK
+#: rfc2131.c:473
+#, fuzzy, c-format
+msgid "%u user class: %s"
+msgstr "DBus error: %s"
+
+# OK
+#: rfc2131.c:500
 msgid "disabled"
 msgstr "di disable"
 
 # OK
-#: rfc2131.c:404 rfc2131.c:916 rfc2131.c:1288
+#: rfc2131.c:541 rfc2131.c:974 rfc2131.c:1380 rfc3315.c:603 rfc3315.c:856
+#: rfc3315.c:1135
 msgid "ignored"
 msgstr "diabaikan"
 
 # OK
-#: rfc2131.c:419 rfc2131.c:1135
+#: rfc2131.c:556 rfc2131.c:1207 rfc3315.c:906
 msgid "address in use"
 msgstr "alamat telah digunakan"
 
 # OK
-#: rfc2131.c:433 rfc2131.c:970
+#: rfc2131.c:570 rfc2131.c:1028
 msgid "no address available"
 msgstr "tak ada alamat yang tersedia"
 
 # OK
-#: rfc2131.c:440 rfc2131.c:1098
+#: rfc2131.c:577 rfc2131.c:1170
 msgid "wrong network"
 msgstr "jaringan yang salah"
 
 # OK
-#: rfc2131.c:454
+#: rfc2131.c:592
 msgid "no address configured"
 msgstr "tak ada alamat yang disetel"
 
 # OK
-#: rfc2131.c:460 rfc2131.c:1148
+#: rfc2131.c:598 rfc2131.c:1220
 msgid "no leases left"
 msgstr "tak ada lease yang tersisa"
 
-#: rfc2131.c:545
+#: rfc2131.c:693 rfc3315.c:476
 #, c-format
 msgid "%u client provides name: %s"
 msgstr ""
 
-# OK
-#: rfc2131.c:700
-#, fuzzy, c-format
-msgid "%u vendor class: %s"
-msgstr "DBus error: %s"
-
-# OK
-#: rfc2131.c:702
-#, fuzzy, c-format
-msgid "%u user class: %s"
-msgstr "DBus error: %s"
-
-#: rfc2131.c:761
+#: rfc2131.c:798
 msgid "PXE BIS not supported"
 msgstr ""
 
 # OK
-#: rfc2131.c:886
+#: rfc2131.c:942 rfc3315.c:1229
 #, fuzzy, c-format
 msgid "disabling DHCP static address %s for %s"
 msgstr "men-disable alamat statik DHCP %s"
 
 # OK
-#: rfc2131.c:907
+#: rfc2131.c:963
 msgid "unknown lease"
 msgstr "lease tidak diketahui"
 
-#: rfc2131.c:939
+#: rfc2131.c:997
 #, c-format
 msgid "not using configured address %s because it is leased to %s"
 msgstr ""
 
-#: rfc2131.c:949
+#: rfc2131.c:1007
 #, c-format
 msgid "not using configured address %s because it is in use by the server or relay"
 msgstr ""
 
-#: rfc2131.c:952
+#: rfc2131.c:1010
 #, c-format
 msgid "not using configured address %s because it was previously declined"
 msgstr ""
 
-#: rfc2131.c:968 rfc2131.c:1141
+#: rfc2131.c:1026 rfc2131.c:1213
 msgid "no unique-id"
 msgstr ""
 
-#: rfc2131.c:1037
+#: rfc2131.c:1108
 msgid "wrong server-ID"
 msgstr ""
 
 # OK
-#: rfc2131.c:1055
+#: rfc2131.c:1127
 msgid "wrong address"
 msgstr "alamat salah"
 
 # OK
-#: rfc2131.c:1073
+#: rfc2131.c:1145 rfc3315.c:1002
 msgid "lease not found"
 msgstr "lease tak ditemukan"
 
 # OK
-#: rfc2131.c:1106
+#: rfc2131.c:1178
 msgid "address not available"
 msgstr "alamat tak tersedia"
 
 # OK
-#: rfc2131.c:1117
+#: rfc2131.c:1189
 msgid "static lease available"
 msgstr "lease statik tak tersedia"
 
 # OK
-#: rfc2131.c:1121
+#: rfc2131.c:1193
 msgid "address reserved"
 msgstr "alamat telah dipesan"
 
-#: rfc2131.c:1129
+#: rfc2131.c:1201
 #, c-format
 msgid "abandoning lease to %s of %s"
 msgstr ""
 
-#: rfc2131.c:1710
-#, c-format
-msgid "%u tags: %s"
-msgstr ""
-
-#: rfc2131.c:1723
+#: rfc2131.c:1707
 #, c-format
 msgid "%u bootfile name: %s"
 msgstr ""
 
 # OK
-#: rfc2131.c:1732
+#: rfc2131.c:1716
 #, fuzzy, c-format
 msgid "%u server name: %s"
 msgstr "DBus error: %s"
 
 # OK
-#: rfc2131.c:1746
+#: rfc2131.c:1724
 #, fuzzy, c-format
 msgid "%u next server: %s"
 msgstr "DBus error: %s"
 
-#: rfc2131.c:1749
+#: rfc2131.c:1727
 #, c-format
 msgid "%u broadcast response"
 msgstr ""
 
-#: rfc2131.c:1812
+#: rfc2131.c:1790
 #, c-format
 msgid "cannot send DHCP/BOOTP option %d: no space left in packet"
 msgstr ""
 
-#: rfc2131.c:2058
+#: rfc2131.c:2031
 msgid "PXE menu too large"
 msgstr ""
 
-#: rfc2131.c:2171
-#, c-format
-msgid "Ignoring domain %s for DHCP host name %s"
-msgstr ""
-
 # OK
-#: rfc2131.c:2189
+#: rfc2131.c:2170 rfc3315.c:1502
 #, fuzzy, c-format
 msgid "%u requested options: %s"
 msgstr "pilihan-pilihan saat kompilasi: %s"
 
-#: rfc2131.c:2456
+#: rfc2131.c:2487
 #, c-format
 msgid "cannot send RFC3925 option: too many options for enterprise number %d"
 msgstr ""
 
 # OK
-#: netlink.c:70
+#: netlink.c:77
 #, fuzzy, c-format
 msgid "cannot create netlink socket: %s"
 msgstr "tidak bisa mem-bind netlink socket: %s"
 
 # OK
-#: netlink.c:288
+#: netlink.c:348
 #, fuzzy, c-format
 msgid "netlink returns error: %s"
 msgstr "DBus error: %s"
 
 # OK
-#: dbus.c:150
+#: dbus.c:186
 msgid "attempt to set an IPv6 server address via DBus - no IPv6 support"
 msgstr "mencoba menyetel sebuah alamat IPv6 server lewat DBus - tidak ada dukungan untuk IPv6"
 
+#: dbus.c:439
+#, c-format
+msgid "Enabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:444
+#, c-format
+msgid "Disabling --%s option from D-Bus"
+msgstr ""
+
 # OK
-#: dbus.c:286
+#: dbus.c:691
 msgid "setting upstream servers from DBus"
 msgstr "menyetel server-server di atas dengan DBus"
 
 # OK
-#: dbus.c:324
+#: dbus.c:738
 msgid "could not register a DBus message handler"
 msgstr "tidak bisa mendaftar sebuah DBus message handler"
 
 # OK
-#: bpf.c:217
+#: bpf.c:263
 #, c-format
 msgid "cannot create DHCP BPF socket: %s"
 msgstr "tidak dapat membuat socket DHCP BPF: %s"
 
 # OK
-#: bpf.c:245
+#: bpf.c:291
 #, fuzzy, c-format
 msgid "DHCP request for unsupported hardware type (%d) received on %s"
 msgstr "permintaan DHCP untuk tipe hardware yang tidak didukung (%d) diterima pada %s"
 
-#: tftp.c:281
+# OK
+#: bpf.c:376
+#, fuzzy, c-format
+msgid "cannot create PF_ROUTE socket: %s"
+msgstr "tidak bisa membuat socket DHCP: %s"
+
+#: bpf.c:397
+msgid "Unknown protocol version from route socket"
+msgstr ""
+
+#: helper.c:153
+msgid "lease() function missing in Lua script"
+msgstr ""
+
+#: tftp.c:309
 msgid "unable to get free port for TFTP"
 msgstr ""
 
-#: tftp.c:296
+#: tftp.c:325
 #, c-format
 msgid "unsupported request from %s"
 msgstr ""
 
 # OK
-#: tftp.c:406
+#: tftp.c:439
 #, fuzzy, c-format
 msgid "file %s not found"
 msgstr "lease tak ditemukan"
 
-#: tftp.c:522
+#: tftp.c:548
 #, c-format
 msgid "error %d %s received from %s"
 msgstr ""
 
 # OK
-#: tftp.c:554
+#: tftp.c:590
 #, fuzzy, c-format
 msgid "failed sending %s to %s"
 msgstr "gagal membaca %s: %s"
 
-#: tftp.c:568
+#: tftp.c:590
 #, c-format
 msgid "sent %s to %s"
 msgstr ""
 
-#: log.c:177
+#: log.c:190
 #, c-format
 msgid "overflow: %d log entries lost"
 msgstr ""
 
-#: log.c:254
+#: log.c:268
 #, c-format
 msgid "log failed: %s"
 msgstr ""
 
 # OK
-#: log.c:462
+#: log.c:469
 msgid "FAILED to start up"
 msgstr "GAGAL untuk memulai"
 
+#: conntrack.c:65
+#, c-format
+msgid "Conntrack connection mark retrieval failed: %s"
+msgstr ""
+
 # OK
-#~ msgid "TXT record string too long"
-#~ msgstr "string rekord TXT terlalu panjang"
+#: dhcp6.c:59
+#, fuzzy, c-format
+msgid "cannot create DHCPv6 socket: %s"
+msgstr "tidak bisa membuat socket DHCP: %s"
 
 # OK
-#~ msgid "failed to set IPV6 options on listening socket: %s"
-#~ msgstr "gagal menyetel IPV6 pada socket: %s"
+#: dhcp6.c:80
+#, fuzzy, c-format
+msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"
+msgstr "gagal menyetel SO_REUSEADDR pada socket DHCP: %s"
 
-#~ msgid "failed to bind listening socket for %s: %s"
-#~ msgstr "gagal mem-bind socket untuk mendengarkan %s: %s"
+# OK
+#: dhcp6.c:92
+#, fuzzy, c-format
+msgid "failed to bind DHCPv6 server socket: %s"
+msgstr "gagal mem-bind socket server DHCP: %s"
+
+# OK
+#: rfc3315.c:157
+#, fuzzy, c-format
+msgid "no address range available for DHCPv6 request from relay at %s"
+msgstr "tidak ada alamat yang bisa dipakai untuk permintaan DHCP %s %s"
+
+# OK
+#: rfc3315.c:166
+#, fuzzy, c-format
+msgid "no address range available for DHCPv6 request via %s"
+msgstr "tidak ada alamat yang bisa dipakai untuk permintaan DHCP %s %s"
+
+# OK
+#: rfc3315.c:297
+#, fuzzy, c-format
+msgid "%u available DHCPv6 subnet: %s/%d"
+msgstr "tidak ada alamat yang bisa dipakai untuk permintaan DHCP %s %s"
+
+# OK
+#: rfc3315.c:380
+#, fuzzy, c-format
+msgid "%u vendor class: %u"
+msgstr "DBus error: %s"
 
 # OK
-#~ msgid "failed to listen on socket: %s"
-#~ msgstr "gagal mendengarkan di socket: %s"
+#: rfc3315.c:428
+#, fuzzy, c-format
+msgid "%u client MAC address: %s"
+msgstr "tidak ada antarmuka dengan alamat %s"
+
+# OK
+#: rfc3315.c:660
+#, fuzzy, c-format
+msgid "unknown prefix-class %d"
+msgstr "lease tidak diketahui"
 
 # OK
+#: rfc3315.c:803 rfc3315.c:898
 #, fuzzy
-#~ msgid "failed to create TFTP socket: %s"
-#~ msgstr "gagal membuat socket: %s "
+msgid "address unavailable"
+msgstr "alamat tak tersedia"
+
+#: rfc3315.c:815 rfc3315.c:946 rfc3315.c:1279
+msgid "success"
+msgstr ""
 
 # OK
-#~ msgid "must set exactly one interface on broken systems without IP_RECVIF"
-#~ msgstr "harus menyetel satu antarmuka saja pada sistem yang tidak benar dengan IP_RECVIF"
+#: rfc3315.c:830 rfc3315.c:839 rfc3315.c:954 rfc3315.c:956
+#, fuzzy
+msgid "no addresses available"
+msgstr "tak ada alamat yang tersedia"
+
+#: rfc3315.c:933
+msgid "not on link"
+msgstr ""
+
+#: rfc3315.c:1006 rfc3315.c:1191 rfc3315.c:1268
+msgid "no binding found"
+msgstr ""
+
+#: rfc3315.c:1044
+msgid "deprecated"
+msgstr ""
+
+# OK
+#: rfc3315.c:1049
+#, fuzzy
+msgid "address invalid"
+msgstr "alamat telah digunakan"
+
+#: rfc3315.c:1096
+msgid "confirm failed"
+msgstr ""
+
+# OK
+#: rfc3315.c:1112
+#, fuzzy
+msgid "all addresses still on link"
+msgstr "kesalahan nama pada %s baris %d"
+
+#: rfc3315.c:1200
+msgid "release received"
+msgstr ""
+
+#: rfc3315.c:2126
+msgid "Cannot multicast to DHCPv6 server without correct interface"
+msgstr ""
+
+#: dhcp-common.c:145
+#, c-format
+msgid "Ignoring duplicate dhcp-option %d"
+msgstr ""
+
+#: dhcp-common.c:222
+#, c-format
+msgid "%u tags: %s"
+msgstr ""
+
+#: dhcp-common.c:407
+#, c-format
+msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgstr ""
+
+# OK
+#: dhcp-common.c:430
+#, c-format
+msgid "duplicate IP address %s (%s) in dhcp-config directive"
+msgstr "alamat IP kembar %s (%s) dalam direktif dhcp-config"
+
+# OK
+#: dhcp-common.c:494
+#, fuzzy, c-format
+msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s"
+msgstr "gagal menyetel SO_REUSEADDR pada socket DHCP: %s"
+
+#: dhcp-common.c:615
+#, c-format
+msgid "Known DHCP options:\n"
+msgstr ""
+
+#: dhcp-common.c:626
+#, c-format
+msgid "Known DHCPv6 options:\n"
+msgstr ""
+
+#: dhcp-common.c:823
+msgid ", prefix deprecated"
+msgstr ""
+
+#: dhcp-common.c:826
+#, c-format
+msgid ", lease time "
+msgstr ""
+
+#: dhcp-common.c:868
+#, c-format
+msgid "%s stateless on %s%.0s%.0s%s"
+msgstr ""
+
+# OK
+#: dhcp-common.c:870
+#, fuzzy, c-format
+msgid "%s, static leases only on %.0s%s%s%.0s"
+msgstr "DHCP, lease static pada %.0s%s, waktu lease %s"
+
+#: dhcp-common.c:872
+#, c-format
+msgid "%s, proxy on subnet %.0s%s%.0s%.0s"
+msgstr ""
+
+# OK
+#: dhcp-common.c:873
+#, fuzzy, c-format
+msgid "%s, IP range %s -- %s%s%.0s"
+msgstr "DHCP, jangkaun IP %s -- %s, waktu lease %s"
+
+#: dhcp-common.c:886
+#, c-format
+msgid "DHCPv4-derived IPv6 names on %s%s"
+msgstr ""
+
+# OK
+#: dhcp-common.c:889
+#, fuzzy, c-format
+msgid "router advertisement on %s%s"
+msgstr "DHCP, lease static pada %.0s%s, waktu lease %s"
+
+#: dhcp-common.c:900
+#, c-format
+msgid "DHCP relay from %s to %s via %s"
+msgstr ""
+
+#: dhcp-common.c:902
+#, c-format
+msgid "DHCP relay from %s to %s"
+msgstr ""
+
+# OK
+#: radv.c:109
+#, fuzzy, c-format
+msgid "cannot create ICMPv6 socket: %s"
+msgstr "tidak bisa membuat socket DHCP: %s"
+
+#: auth.c:448
+#, c-format
+msgid "ignoring zone transfer request from %s"
+msgstr ""
+
+# OK
+#: ipset.c:95
+#, fuzzy, c-format
+msgid "failed to find kernel version: %s"
+msgstr "gagal mem-bind socket server DHCP: %s"
+
+# OK
+#: ipset.c:114
+#, fuzzy, c-format
+msgid "failed to create IPset control socket: %s"
+msgstr "gagal membuat socket: %s "
+
+# OK
+#: dnssec.c:449 dnssec.c:493
+#, fuzzy, c-format
+msgid "failed to update mtime on %s: %s"
+msgstr "gagal membaca %s: %s"
+
+#: blockdata.c:58
+#, c-format
+msgid "DNSSEC memory in use %u, max %u, allocated %u"
+msgstr ""
+
+#: tables.c:80
+msgid "error: fill_addr missused"
+msgstr ""
+
+# OK
+#: tables.c:109
+#, fuzzy, c-format
+msgid "failed to access pf devices: %s"
+msgstr "gagal mengakses %s: %s"
+
+# OK
+#: tables.c:123
+#, fuzzy, c-format
+msgid "warning: no opened pf devices %s"
+msgstr "menggunakan alamat lokal saja untuk %s %s"
+
+# OK
+#: tables.c:131
+#, fuzzy, c-format
+msgid "error: cannot use table name %s"
+msgstr "tidak bisa mendapatkan host-name: %s"
+
+#: tables.c:139
+#, c-format
+msgid "error: cannot strlcpy table name %s"
+msgstr ""
+
+#: tables.c:145
+#, c-format
+msgid "warning: pfr_add_tables: %s(%d)"
+msgstr ""
+
+#: tables.c:151
+msgid "info: table created"
+msgstr ""
+
+#: tables.c:162
+#, c-format
+msgid "warning: DIOCR%sADDRS: %s"
+msgstr ""
+
+# OK
+#: tables.c:166
+#, fuzzy, c-format
+msgid "%d addresses %s"
+msgstr "membaca %s - %d alamat"
+
+# OK
+#: inotify.c:59
+#, fuzzy, c-format
+msgid "cannot access path %s: %s"
+msgstr "tidak bisa membaca %s: %s"
+
+# OK
+#: inotify.c:92
+#, fuzzy, c-format
+msgid "failed to create inotify: %s"
+msgstr "gagal membaca %s: %s"
+
+#: inotify.c:105
+#, c-format
+msgid "too many symlinks following %s"
+msgstr ""
+
+#: inotify.c:121
+#, c-format
+msgid "directory %s for resolv-file is missing, cannot poll"
+msgstr ""
+
+# OK
+#: inotify.c:125 inotify.c:162
+#, fuzzy, c-format
+msgid "failed to create inotify for %s: %s"
+msgstr "gagal membuat socket: %s "
+
+# OK
+#: inotify.c:147
+#, fuzzy, c-format
+msgid "bad dynamic directory %s: %s"
+msgstr "tidak bisa membaca %s: %s"
+
+#: inotify.c:247
+#, c-format
+msgid "inotify, new or changed file %s"
+msgstr ""
+
+# OK
+#, fuzzy
+#~ msgid "cannot cannonicalise resolv-file %s: %s"
+#~ msgstr "tidak dapat membuka atau membuat file lease: %s"
+
+# OK
+#~ msgid "duplicate IP address %s in dhcp-config directive."
+#~ msgstr "alamat IP kembar %s dalam direktif dhcp-config"
 
 # OK
 #, fuzzy
-#~ msgid "failed to load %s: %s"
-#~ msgstr "gagal memuat %S: %s"
+#~ msgid "Specify path to Lua script (no default)."
+#~ msgstr "Sebutkan path file PID. (default %s)."
+
+# OK
+#~ msgid "TXT record string too long"
+#~ msgstr "string rekord TXT terlalu panjang"
+
+# OK
+#~ msgid "failed to set IPV6 options on listening socket: %s"
+#~ msgstr "gagal menyetel IPV6 pada socket: %s"
+
+#~ msgid "failed to bind listening socket for %s: %s"
+#~ msgstr "gagal mem-bind socket untuk mendengarkan %s: %s"
 
 # OK
-#~ msgid "bad name in %s"
-#~ msgstr "kesalahan nama di %s"
+#~ msgid "must set exactly one interface on broken systems without IP_RECVIF"
+#~ msgstr "harus menyetel satu antarmuka saja pada sistem yang tidak benar dengan IP_RECVIF"
 
 # OK
 #~ msgid "Ignoring DHCP lease for %s because it has an illegal domain part"
index 0b42ef1..172fbbe 100644 (file)
--- a/po/it.po
+++ b/po/it.po
@@ -10,1446 +10,2128 @@ msgstr ""
 "PO-Revision-Date: 2006-05-22 11:09+0100\n"
 "Last-Translator: Simon Kelley <simon@thekelleys.org.uk>\n"
 "Language-Team: Italian <tp@lists.linux.it>\n"
+"Language: it\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=ASCII\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: cache.c:761
+#: cache.c:523
+msgid "Internal error in cache."
+msgstr ""
+
+#: cache.c:941
 #, c-format
 msgid "failed to load names from %s: %s"
 msgstr ""
 
-#: cache.c:795 dhcp.c:865
+#: cache.c:967 dhcp.c:828
 #, c-format
 msgid "bad address at %s line %d"
 msgstr ""
 
-#: cache.c:853 dhcp.c:881
+#: cache.c:1018 dhcp.c:844
 #, c-format
 msgid "bad name at %s line %d"
 msgstr ""
 
-#: cache.c:860 dhcp.c:956
+#: cache.c:1027 dhcp.c:919
 #, c-format
 msgid "read %s - %d addresses"
 msgstr ""
 
-#: cache.c:899
+#: cache.c:1135
 msgid "cleared cache"
 msgstr ""
 
-#: cache.c:960
+#: cache.c:1164
+#, c-format
+msgid "No IPv4 address found for %s"
+msgstr ""
+
+#: cache.c:1242
 #, c-format
 msgid "%s is a CNAME, not giving it to the DHCP lease of %s"
 msgstr ""
 
-#: cache.c:966
+#: cache.c:1266
 #, c-format
 msgid "not giving name %s to the DHCP lease of %s because the name exists in %s with address %s"
 msgstr ""
 
-#: cache.c:1039
+#: cache.c:1421
 #, c-format
 msgid "time %lu"
 msgstr ""
 
-#: cache.c:1040
+#: cache.c:1422
 #, c-format
 msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries."
 msgstr ""
 
-#: cache.c:1042
+#: cache.c:1424
 #, c-format
 msgid "queries forwarded %u, queries answered locally %u"
 msgstr ""
 
-#: cache.c:1068
+#: cache.c:1427
+#, c-format
+msgid "queries for authoritative zones %u"
+msgstr ""
+
+#: cache.c:1453
 #, c-format
 msgid "server %s#%d: queries sent %u, retried or failed %u"
 msgstr ""
 
-#: util.c:57
+#: util.c:45
 #, c-format
 msgid "failed to seed the random number generator: %s"
 msgstr ""
 
-#: util.c:189
+#: util.c:205
 msgid "failed to allocate memory"
 msgstr ""
 
-#: util.c:227 option.c:573
+#: util.c:250 option.c:601
 msgid "could not get memory"
 msgstr ""
 
-#: util.c:237
+#: util.c:260
 #, c-format
 msgid "cannot create pipe: %s"
 msgstr ""
 
-#: util.c:245
+#: util.c:268
 #, c-format
 msgid "failed to allocate %d bytes"
 msgstr ""
 
-#: util.c:350
+#: util.c:437
 #, c-format
 msgid "infinite"
 msgstr ""
 
-#: option.c:244
+#: option.c:332
 msgid "Specify local address(es) to listen on."
 msgstr ""
 
-#: option.c:245
+#: option.c:333
 msgid "Return ipaddr for all hosts in specified domains."
 msgstr ""
 
-#: option.c:246
+#: option.c:334
 msgid "Fake reverse lookups for RFC1918 private address ranges."
 msgstr ""
 
-#: option.c:247
+#: option.c:335
 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."
 msgstr ""
 
-#: option.c:248
+#: option.c:336
 #, c-format
 msgid "Specify the size of the cache in entries (defaults to %s)."
 msgstr ""
 
-#: option.c:249
+#: option.c:337
 #, c-format
 msgid "Specify configuration file (defaults to %s)."
 msgstr ""
 
-#: option.c:250
+#: option.c:338
 msgid "Do NOT fork into the background: run in debug mode."
 msgstr ""
 
-#: option.c:251
+#: option.c:339
 msgid "Do NOT forward queries with no domain part."
 msgstr ""
 
-#: option.c:252
+#: option.c:340
 msgid "Return self-pointing MX records for local hosts."
 msgstr ""
 
-#: option.c:253
+#: option.c:341
 msgid "Expand simple names in /etc/hosts with domain-suffix."
 msgstr ""
 
-#: option.c:254
+#: option.c:342
 msgid "Don't forward spurious DNS requests from Windows hosts."
 msgstr ""
 
-#: option.c:255
+#: option.c:343
 msgid "Enable DHCP in the range given with lease duration."
 msgstr ""
 
-#: option.c:256
+#: option.c:344
 #, c-format
 msgid "Change to this group after startup (defaults to %s)."
 msgstr ""
 
-#: option.c:257
+#: option.c:345
 msgid "Set address or hostname for a specified machine."
 msgstr ""
 
-#: option.c:258
+#: option.c:346
 msgid "Read DHCP host specs from file."
 msgstr ""
 
-#: option.c:259
+#: option.c:347
 msgid "Read DHCP option specs from file."
 msgstr ""
 
-#: option.c:260
+#: option.c:348
+msgid "Read DHCP host specs from a directory."
+msgstr ""
+
+#: option.c:349
+msgid "Read DHCP options from a directory."
+msgstr ""
+
+#: option.c:350
 msgid "Evaluate conditional tag expression."
 msgstr ""
 
-#: option.c:261
+#: option.c:351
 #, c-format
 msgid "Do NOT load %s file."
 msgstr ""
 
-#: option.c:262
+#: option.c:352
 #, c-format
 msgid "Specify a hosts file to be read in addition to %s."
 msgstr ""
 
-#: option.c:263
+#: option.c:353
+msgid "Read hosts files from a directory."
+msgstr ""
+
+#: option.c:354
 msgid "Specify interface(s) to listen on."
 msgstr ""
 
-#: option.c:264
+#: option.c:355
 msgid "Specify interface(s) NOT to listen on."
 msgstr ""
 
-#: option.c:265
+#: option.c:356
 msgid "Map DHCP user class to tag."
 msgstr ""
 
-#: option.c:266
+#: option.c:357
 msgid "Map RFC3046 circuit-id to tag."
 msgstr ""
 
-#: option.c:267
+#: option.c:358
 msgid "Map RFC3046 remote-id to tag."
 msgstr ""
 
-#: option.c:268
+#: option.c:359
 msgid "Map RFC3993 subscriber-id to tag."
 msgstr ""
 
-#: option.c:269
+#: option.c:360
 msgid "Don't do DHCP for hosts with tag set."
 msgstr ""
 
-#: option.c:270
+#: option.c:361
 msgid "Force broadcast replies for hosts with tag set."
 msgstr ""
 
-#: option.c:271
+#: option.c:362
 msgid "Do NOT fork into the background, do NOT run in debug mode."
 msgstr ""
 
-#: option.c:272
+#: option.c:363
 msgid "Assume we are the only DHCP server on the local network."
 msgstr ""
 
-#: option.c:273
+#: option.c:364
 #, c-format
 msgid "Specify where to store DHCP leases (defaults to %s)."
 msgstr ""
 
-#: option.c:274
+#: option.c:365
 msgid "Return MX records for local hosts."
 msgstr ""
 
-#: option.c:275
+#: option.c:366
 msgid "Specify an MX record."
 msgstr ""
 
-#: option.c:276
+#: option.c:367
 msgid "Specify BOOTP options to DHCP server."
 msgstr ""
 
-#: option.c:277
+#: option.c:368
 #, c-format
 msgid "Do NOT poll %s file, reload only on SIGHUP."
 msgstr ""
 
-#: option.c:278
+#: option.c:369
 msgid "Do NOT cache failed search results."
 msgstr ""
 
-#: option.c:279
+#: option.c:370
 #, c-format
 msgid "Use nameservers strictly in the order given in %s."
 msgstr ""
 
-#: option.c:280
+#: option.c:371
 msgid "Specify options to be sent to DHCP clients."
 msgstr ""
 
-#: option.c:281
+#: option.c:372
 msgid "DHCP option sent even if the client does not request it."
 msgstr ""
 
-#: option.c:282
+#: option.c:373
 msgid "Specify port to listen for DNS requests on (defaults to 53)."
 msgstr ""
 
-#: option.c:283
+#: option.c:374
 #, c-format
 msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)."
 msgstr ""
 
-#: option.c:284
+#: option.c:375
 msgid "Log DNS queries."
 msgstr ""
 
-#: option.c:285
+#: option.c:376
 msgid "Force the originating port for upstream DNS queries."
 msgstr ""
 
-#: option.c:286
+#: option.c:377
 msgid "Do NOT read resolv.conf."
 msgstr ""
 
-#: option.c:287
+#: option.c:378
 #, c-format
 msgid "Specify path to resolv.conf (defaults to %s)."
 msgstr ""
 
-#: option.c:288
+#: option.c:379
+msgid "Specify path to file with server= options"
+msgstr ""
+
+#: option.c:380
 msgid "Specify address(es) of upstream servers with optional domains."
 msgstr ""
 
-#: option.c:289
+#: option.c:381
+msgid "Specify address of upstream servers for reverse address queries"
+msgstr ""
+
+#: option.c:382
 msgid "Never forward queries to specified domains."
 msgstr ""
 
-#: option.c:290
+#: option.c:383
 msgid "Specify the domain to be assigned in DHCP leases."
 msgstr ""
 
-#: option.c:291
+#: option.c:384
 msgid "Specify default target in an MX record."
 msgstr ""
 
-#: option.c:292
+#: option.c:385
 msgid "Specify time-to-live in seconds for replies from /etc/hosts."
 msgstr ""
 
-#: option.c:293
+#: option.c:386
 msgid "Specify time-to-live in seconds for negative caching."
 msgstr ""
 
-#: option.c:294
+#: option.c:387
 msgid "Specify time-to-live in seconds for maximum TTL to send to clients."
 msgstr ""
 
-#: option.c:295
+#: option.c:388
+msgid "Specify time-to-live ceiling for cache."
+msgstr ""
+
+#: option.c:389
+msgid "Specify time-to-live floor for cache."
+msgstr ""
+
+#: option.c:390
 #, c-format
 msgid "Change to this user after startup. (defaults to %s)."
 msgstr ""
 
-#: option.c:296
+#: option.c:391
 msgid "Map DHCP vendor class to tag."
 msgstr ""
 
-#: option.c:297
+#: option.c:392
 msgid "Display dnsmasq version and copyright information."
 msgstr ""
 
-#: option.c:298
+#: option.c:393
 msgid "Translate IPv4 addresses from upstream servers."
 msgstr ""
 
-#: option.c:299
+#: option.c:394
 msgid "Specify a SRV record."
 msgstr ""
 
-#: option.c:300
+#: option.c:395
 msgid "Display this message. Use --help dhcp for known DHCP options."
 msgstr ""
 
-#: option.c:301
+#: option.c:396
 #, c-format
 msgid "Specify path of PID file (defaults to %s)."
 msgstr ""
 
-#: option.c:302
+#: option.c:397
 #, c-format
 msgid "Specify maximum number of DHCP leases (defaults to %s)."
 msgstr ""
 
-#: option.c:303
+#: option.c:398
 msgid "Answer DNS queries based on the interface a query was sent to."
 msgstr ""
 
-#: option.c:304
+#: option.c:399
 msgid "Specify TXT DNS record."
 msgstr ""
 
-#: option.c:305
+#: option.c:400
 msgid "Specify PTR DNS record."
 msgstr ""
 
-#: option.c:306
+#: option.c:401
 msgid "Give DNS name to IPv4 address of interface."
 msgstr ""
 
-#: option.c:307
+#: option.c:402
 msgid "Bind only to interfaces in use."
 msgstr ""
 
-#: option.c:308
+#: option.c:403
 #, c-format
 msgid "Read DHCP static host information from %s."
 msgstr ""
 
-#: option.c:309
+#: option.c:404
 msgid "Enable the DBus interface for setting upstream servers, etc."
 msgstr ""
 
-#: option.c:310
+#: option.c:405
 msgid "Do not provide DHCP on this interface, only provide DNS."
 msgstr ""
 
-#: option.c:311
+#: option.c:406
 msgid "Enable dynamic address allocation for bootp."
 msgstr ""
 
-#: option.c:312
+#: option.c:407
 msgid "Map MAC address (with wildcards) to option set."
 msgstr ""
 
-#: option.c:313
+#: option.c:408
 msgid "Treat DHCP requests on aliases as arriving from interface."
 msgstr ""
 
-#: option.c:314
+#: option.c:409
 msgid "Disable ICMP echo address checking in the DHCP server."
 msgstr ""
 
-#: option.c:315
-msgid "Script to run on DHCP lease creation and destruction."
+#: option.c:410
+msgid "Shell script to run on DHCP lease creation and destruction."
 msgstr ""
 
-#: option.c:316
+#: option.c:411
+msgid "Lua script to run on DHCP lease creation and destruction."
+msgstr ""
+
+#: option.c:412
+msgid "Run lease-change scripts as this user."
+msgstr ""
+
+#: option.c:413
 msgid "Read configuration from all the files in this directory."
 msgstr ""
 
-#: option.c:317
+#: option.c:414
 msgid "Log to this syslog facility or file. (defaults to DAEMON)"
 msgstr ""
 
-#: option.c:318
+#: option.c:415
 msgid "Do not use leasefile."
 msgstr ""
 
-#: option.c:319
+#: option.c:416
 #, c-format
 msgid "Maximum number of concurrent DNS queries. (defaults to %s)"
 msgstr ""
 
-#: option.c:320
+#: option.c:417
 #, c-format
 msgid "Clear DNS cache when reloading %s."
 msgstr ""
 
-#: option.c:321
+#: option.c:418
 msgid "Ignore hostnames provided by DHCP clients."
 msgstr ""
 
-#: option.c:322
+#: option.c:419
 msgid "Do NOT reuse filename and server fields for extra DHCP options."
 msgstr ""
 
-#: option.c:323
+#: option.c:420
 msgid "Enable integrated read-only TFTP server."
 msgstr ""
 
-#: option.c:324
+#: option.c:421
 msgid "Export files by TFTP only from the specified subtree."
 msgstr ""
 
-#: option.c:325
+#: option.c:422
 msgid "Add client IP address to tftp-root."
 msgstr ""
 
-#: option.c:326
+#: option.c:423
 msgid "Allow access only to files owned by the user running dnsmasq."
 msgstr ""
 
-#: option.c:327
+#: option.c:424
+msgid "Do not terminate the service if TFTP directories are inaccessible."
+msgstr ""
+
+#: option.c:425
 #, c-format
 msgid "Maximum number of conncurrent TFTP transfers (defaults to %s)."
 msgstr ""
 
-#: option.c:328
+#: option.c:426
 msgid "Disable the TFTP blocksize extension."
 msgstr ""
 
-#: option.c:329
+#: option.c:427
+msgid "Convert TFTP filenames to lowercase"
+msgstr ""
+
+#: option.c:428
 msgid "Ephemeral port range for use by TFTP transfers."
 msgstr ""
 
-#: option.c:330
+#: option.c:429
 msgid "Extra logging for DHCP."
 msgstr ""
 
-#: option.c:331
+#: option.c:430
 msgid "Enable async. logging; optionally set queue length."
 msgstr ""
 
-#: option.c:332
+#: option.c:431
 msgid "Stop DNS rebinding. Filter private IP ranges when resolving."
 msgstr ""
 
-#: option.c:333
+#: option.c:432
 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers."
 msgstr ""
 
-#: option.c:334
+#: option.c:433
 msgid "Inhibit DNS-rebind protection on this domain."
 msgstr ""
 
-#: option.c:335
+#: option.c:434
 msgid "Always perform DNS queries to all servers."
 msgstr ""
 
-#: option.c:336
+#: option.c:435
 msgid "Set tag if client includes matching option in request."
 msgstr ""
 
-#: option.c:337
+#: option.c:436
 msgid "Use alternative ports for DHCP."
 msgstr ""
 
-#: option.c:338
-msgid "Run lease-change script as this user."
-msgstr ""
-
-#: option.c:339
+#: option.c:437
 msgid "Specify NAPTR DNS record."
 msgstr ""
 
-#: option.c:340
+#: option.c:438
 msgid "Specify lowest port available for DNS query transmission."
 msgstr ""
 
-#: option.c:341
+#: option.c:439
 msgid "Use only fully qualified domain names for DHCP clients."
 msgstr ""
 
-#: option.c:342
+#: option.c:440
 msgid "Generate hostnames based on MAC address for nameless clients."
 msgstr ""
 
-#: option.c:343
+#: option.c:441
 msgid "Use these DHCP relays as full proxies."
 msgstr ""
 
-#: option.c:344
+#: option.c:442
+msgid "Relay DHCP requests to a remote server"
+msgstr ""
+
+#: option.c:443
 msgid "Specify alias name for LOCAL DNS name."
 msgstr ""
 
-#: option.c:345
+#: option.c:444
 msgid "Prompt to send to PXE clients."
 msgstr ""
 
-#: option.c:346
+#: option.c:445
 msgid "Boot service for PXE menu."
 msgstr ""
 
-#: option.c:347
+#: option.c:446
 msgid "Check configuration syntax."
 msgstr ""
 
-#: option.c:348
-msgid "Add requestor's MAC address to forwarded DNS queries"
+#: option.c:447
+msgid "Add requestor's MAC address to forwarded DNS queries."
 msgstr ""
 
-#: option.c:349
-msgid "Proxy DNSSEC validation results from upstream nameservers"
+#: option.c:448
+msgid "Add requestor's IP subnet to forwarded DNS queries."
+msgstr ""
+
+#: option.c:449
+msgid "Proxy DNSSEC validation results from upstream nameservers."
+msgstr ""
+
+#: option.c:450
+msgid "Attempt to allocate sequential IP addresses to DHCP clients."
+msgstr ""
+
+#: option.c:451
+msgid "Copy connection-track mark from queries to upstream connections."
+msgstr ""
+
+#: option.c:452
+msgid "Allow DHCP clients to do their own DDNS updates."
+msgstr ""
+
+#: option.c:453
+msgid "Send router-advertisements for interfaces doing DHCPv6"
+msgstr ""
+
+#: option.c:454
+msgid "Specify DUID_EN-type DHCPv6 server DUID"
+msgstr ""
+
+#: option.c:455
+msgid "Specify host (A/AAAA and PTR) records"
+msgstr ""
+
+#: option.c:456
+msgid "Specify arbitrary DNS resource record"
+msgstr ""
+
+#: option.c:457
+msgid "Bind to interfaces in use - check for new interfaces"
+msgstr ""
+
+#: option.c:458
+msgid "Export local names to global DNS"
+msgstr ""
+
+#: option.c:459
+msgid "Domain to export to global DNS"
+msgstr ""
+
+#: option.c:460
+msgid "Set TTL for authoritative replies"
+msgstr ""
+
+#: option.c:461
+msgid "Set authoritive zone information"
+msgstr ""
+
+#: option.c:462
+msgid "Secondary authoritative nameservers for forward domains"
+msgstr ""
+
+#: option.c:463
+msgid "Peers which are allowed to do zone transfer"
+msgstr ""
+
+#: option.c:464
+msgid "Specify ipsets to which matching domains should be added"
+msgstr ""
+
+#: option.c:465
+msgid "Specify a domain and address range for synthesised names"
+msgstr ""
+
+#: option.c:466
+msgid "Activate DNSSEC validation"
+msgstr ""
+
+#: option.c:467
+msgid "Specify trust anchor key digest."
+msgstr ""
+
+#: option.c:468
+msgid "Disable upstream checking for DNSSEC debugging."
+msgstr ""
+
+#: option.c:469
+msgid "Ensure answers without DNSSEC are in unsigned zones."
+msgstr ""
+
+#: option.c:470
+msgid "Don't check DNSSEC signature timestamps until first cache-reload"
+msgstr ""
+
+#: option.c:471
+msgid "Timestamp file to verify system clock for DNSSEC"
+msgstr ""
+
+#: option.c:473
+msgid "Specify DHCPv6 prefix class"
+msgstr ""
+
+#: option.c:475
+msgid "Set priority, resend-interval and router-lifetime"
+msgstr ""
+
+#: option.c:476
+msgid "Do not log routine DHCP."
+msgstr ""
+
+#: option.c:477
+msgid "Do not log routine DHCPv6."
+msgstr ""
+
+#: option.c:478
+msgid "Do not log RA."
 msgstr ""
 
-#: option.c:638
+#: option.c:479
+msgid "Accept queries only from directly-connected networks"
+msgstr ""
+
+#: option.c:480
+msgid "Detect and remove DNS forwarding loops"
+msgstr ""
+
+#: option.c:481
+msgid "Ignore DNS responses containing ipaddr."
+msgstr ""
+
+#: option.c:683
 #, c-format
 msgid ""
 "Usage: dnsmasq [options]\n"
 "\n"
 msgstr ""
 
-#: option.c:640
+#: option.c:685
 #, c-format
 msgid "Use short options only on the command line.\n"
 msgstr ""
 
-#: option.c:642
+#: option.c:687
 #, c-format
 msgid "Valid options are:\n"
 msgstr ""
 
-#: option.c:683
-#, c-format
-msgid "Known DHCP options:\n"
+#: option.c:744 option.c:748
+msgid "bad port"
+msgstr ""
+
+#: option.c:775 option.c:807
+msgid "interface binding not supported"
+msgstr ""
+
+#: option.c:784 option.c:3575
+msgid "bad interface name"
+msgstr ""
+
+#: option.c:814
+msgid "bad address"
+msgstr ""
+
+#: option.c:996
+msgid "unsupported encapsulation for IPv6 option"
 msgstr ""
 
-#: option.c:798
+#: option.c:1010
 msgid "bad dhcp-option"
 msgstr ""
 
-#: option.c:860
+#: option.c:1078
 msgid "bad IP address"
 msgstr ""
 
-#: option.c:968
+#: option.c:1081 option.c:1219 option.c:2893
+msgid "bad IPv6 address"
+msgstr ""
+
+#: option.c:1246 option.c:1340
 msgid "bad domain in dhcp-option"
 msgstr ""
 
-#: option.c:1034
+#: option.c:1378
 msgid "dhcp-option too long"
 msgstr ""
 
-#: option.c:1043
+#: option.c:1385
 msgid "illegal dhcp-match"
 msgstr ""
 
-#: option.c:1087
+#: option.c:1447
 msgid "illegal repeated flag"
 msgstr ""
 
-#: option.c:1095
+#: option.c:1455
 msgid "illegal repeated keyword"
 msgstr ""
 
-#: option.c:1147 option.c:3030
+#: option.c:1520 option.c:4191
 #, c-format
 msgid "cannot access directory %s: %s"
 msgstr ""
 
-#: option.c:1178 tftp.c:460
+#: option.c:1566 tftp.c:493
 #, c-format
 msgid "cannot access %s: %s"
 msgstr ""
 
-#: option.c:1207
+#: option.c:1618
 msgid "setting log facility is not possible under Android"
 msgstr ""
 
-#: option.c:1216
+#: option.c:1627
 msgid "bad log facility"
 msgstr ""
 
-#: option.c:1265
+#: option.c:1680
 msgid "bad MX preference"
 msgstr ""
 
-#: option.c:1270
+#: option.c:1685
 msgid "bad MX name"
 msgstr ""
 
-#: option.c:1284
+#: option.c:1699
 msgid "bad MX target"
 msgstr ""
 
-#: option.c:1294
+#: option.c:1711
 msgid "cannot run scripts under uClinux"
 msgstr ""
 
-#: option.c:1296
+#: option.c:1713
 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts"
 msgstr ""
 
-#: option.c:1597 option.c:1601
-msgid "bad port"
+#: option.c:1717
+msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts"
 msgstr ""
 
-#: option.c:1620 option.c:1645
-msgid "interface binding not supported"
+#: option.c:1973 option.c:2018 option.c:2074
+msgid "bad prefix"
+msgstr ""
+
+#: option.c:2355
+msgid "recompile with HAVE_IPSET defined to enable ipset directives"
 msgstr ""
 
-#: option.c:1791
+#: option.c:2548
 msgid "bad port range"
 msgstr ""
 
-#: option.c:1808
+#: option.c:2564
 msgid "bad bridge-interface"
 msgstr ""
 
-#: option.c:1850
-msgid "bad dhcp-range"
+#: option.c:2624
+msgid "only one tag allowed"
 msgstr ""
 
-#: option.c:1878
-msgid "only one tag allowed"
+#: option.c:2644 option.c:2656 option.c:2764 option.c:2805
+msgid "bad dhcp-range"
 msgstr ""
 
-#: option.c:1925
+#: option.c:2671
 msgid "inconsistent DHCP range"
 msgstr ""
 
-#: option.c:2019 option.c:2045
+#: option.c:2732
+msgid "prefix length must be exactly 64 for RA subnets"
+msgstr ""
+
+#: option.c:2734
+msgid "prefix length must be exactly 64 for subnet constructors"
+msgstr ""
+
+#: option.c:2738
+msgid "prefix length must be at least 64"
+msgstr ""
+
+#: option.c:2741
+msgid "inconsistent DHCPv6 range"
+msgstr ""
+
+#: option.c:2752
+msgid "prefix must be zero with \"constructor:\" argument"
+msgstr ""
+
+#: option.c:2863 option.c:2911
 msgid "bad hex constant"
 msgstr ""
 
-#: option.c:2107
+#: option.c:2885
+msgid "cannot match tags in --dhcp-host"
+msgstr ""
+
+#: option.c:2933
+#, c-format
+msgid "duplicate dhcp-host IP address %s"
+msgstr ""
+
+#: option.c:2991
 msgid "bad DHCP host name"
 msgstr ""
 
-#: option.c:2188
+#: option.c:3073
 msgid "bad tag-if"
 msgstr ""
 
-#: option.c:2467 option.c:2752
+#: option.c:3397 option.c:3791
 msgid "invalid port number"
 msgstr ""
 
-#: option.c:2529
+#: option.c:3459
 msgid "bad dhcp-proxy address"
 msgstr ""
 
-#: option.c:2569
-msgid "invalid alias range"
+#: option.c:3485
+msgid "Bad dhcp-relay"
 msgstr ""
 
-#: option.c:2582
-msgid "bad interface name"
+#: option.c:3511
+msgid "bad RA-params"
+msgstr ""
+
+#: option.c:3520
+msgid "bad DUID"
+msgstr ""
+
+#: option.c:3562
+msgid "invalid alias range"
 msgstr ""
 
-#: option.c:2607
+#: option.c:3616
 msgid "bad CNAME"
 msgstr ""
 
-#: option.c:2612
+#: option.c:3621
 msgid "duplicate CNAME"
 msgstr ""
 
-#: option.c:2632
+#: option.c:3641
 msgid "bad PTR record"
 msgstr ""
 
-#: option.c:2663
+#: option.c:3672
 msgid "bad NAPTR record"
 msgstr ""
 
-#: option.c:2695
+#: option.c:3706
+msgid "bad RR record"
+msgstr ""
+
+#: option.c:3736
 msgid "bad TXT record"
 msgstr ""
 
-#: option.c:2738
+#: option.c:3777
 msgid "bad SRV record"
 msgstr ""
 
-#: option.c:2745
+#: option.c:3784
 msgid "bad SRV target"
 msgstr ""
 
-#: option.c:2759
+#: option.c:3798
 msgid "invalid priority"
 msgstr ""
 
-#: option.c:2766
+#: option.c:3805
 msgid "invalid weight"
 msgstr ""
 
-#: option.c:2785
-msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"
+#: option.c:3829
+msgid "Bad host-record"
+msgstr ""
+
+#: option.c:3846
+msgid "Bad name in host-record"
+msgstr ""
+
+#: option.c:3911
+msgid "bad trust anchor"
+msgstr ""
+
+#: option.c:3925
+msgid "bad HEX in trust anchor"
 msgstr ""
 
-#: option.c:2849
+#: option.c:3935
+msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"
+msgstr ""
+
+#: option.c:3994
 msgid "missing \""
 msgstr ""
 
-#: option.c:2908
+#: option.c:4051
 msgid "bad option"
 msgstr ""
 
-#: option.c:2910
+#: option.c:4053
 msgid "extraneous parameter"
 msgstr ""
 
-#: option.c:2912
+#: option.c:4055
 msgid "missing parameter"
 msgstr ""
 
-#: option.c:2916
+#: option.c:4057
+msgid "illegal option"
+msgstr ""
+
+#: option.c:4064
 msgid "error"
 msgstr ""
 
-#: option.c:2921
+#: option.c:4066
 #, c-format
-msgid "%s at line %d of %%s"
+msgid " at line %d of %s"
 msgstr ""
 
-#: option.c:2985 tftp.c:624
+#: option.c:4081 option.c:4328 option.c:4364
 #, c-format
-msgid "cannot read %s: %s"
+msgid "read %s"
 msgstr ""
 
-#: option.c:3151 option.c:3187
+#: option.c:4144 option.c:4267 tftp.c:667
 #, c-format
-msgid "read %s"
+msgid "cannot read %s: %s"
 msgstr ""
 
-#: option.c:3239
+#: option.c:4430
 msgid "junk found in command line"
 msgstr ""
 
-#: option.c:3269
+#: option.c:4465
 #, c-format
 msgid "Dnsmasq version %s  %s\n"
 msgstr ""
 
-#: option.c:3270
+#: option.c:4466
 #, c-format
 msgid ""
-"Compile time options %s\n"
+"Compile time options: %s\n"
 "\n"
 msgstr ""
 
-#: option.c:3271
+#: option.c:4467
 #, c-format
 msgid "This software comes with ABSOLUTELY NO WARRANTY.\n"
 msgstr ""
 
-#: option.c:3272
+#: option.c:4468
 #, c-format
 msgid "Dnsmasq is free software, and you are welcome to redistribute it\n"
 msgstr ""
 
-#: option.c:3273
+#: option.c:4469
 #, c-format
 msgid "under the terms of the GNU General Public License, version 2 or 3.\n"
 msgstr ""
 
-#: option.c:3284
+#: option.c:4480
 msgid "try --help"
 msgstr ""
 
-#: option.c:3286
+#: option.c:4482
 msgid "try -w"
 msgstr ""
 
-#: option.c:3289
+#: option.c:4484
 #, c-format
 msgid "bad command line options: %s"
 msgstr ""
 
-#: option.c:3330
+#: option.c:4541
 #, c-format
 msgid "cannot get host-name: %s"
 msgstr ""
 
-#: option.c:3358
+#: option.c:4569
 msgid "only one resolv.conf file allowed in no-poll mode."
 msgstr ""
 
-#: option.c:3368
+#: option.c:4579
 msgid "must have exactly one resolv.conf to read domain from."
 msgstr ""
 
-#: option.c:3371 network.c:848 dhcp.c:814
+#: option.c:4582 network.c:1507 dhcp.c:777
 #, c-format
 msgid "failed to read %s: %s"
 msgstr ""
 
-#: option.c:3388
+#: option.c:4599
 #, c-format
 msgid "no search directive found in %s"
 msgstr ""
 
-#: option.c:3409
+#: option.c:4620
 msgid "there must be a default domain when --dhcp-fqdn is set"
 msgstr ""
 
-#: option.c:3413
+#: option.c:4629
 msgid "syntax check OK"
 msgstr ""
 
-#: forward.c:461
+#: forward.c:111
+#, c-format
+msgid "failed to send packet: %s"
+msgstr ""
+
+#: forward.c:591
+msgid "discarding DNS reply: subnet option mismatch"
+msgstr ""
+
+#: forward.c:614
 #, c-format
 msgid "nameserver %s refused to do a recursive query"
 msgstr ""
 
-#: forward.c:489
+#: forward.c:646
 #, c-format
 msgid "possible DNS-rebind attack detected: %s"
 msgstr ""
 
-#: network.c:171
+#: forward.c:1209 forward.c:1815
+msgid "Ignoring query from non-local network"
+msgstr ""
+
+#: forward.c:2286
 #, c-format
-msgid "unknown interface %s in bridge-interface"
+msgid "Maximum number of concurrent DNS queries reached (max: %d)"
 msgstr ""
 
-#: network.c:380
+#: network.c:715
 #, c-format
 msgid "failed to create listening socket for %s: %s"
 msgstr ""
 
-#: network.c:746
+#: network.c:1021
+#, c-format
+msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s"
+msgstr ""
+
+#: network.c:1028
+msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)"
+msgstr ""
+
+#: network.c:1037
+#, c-format
+msgid "warning: no addresses found for interface %s"
+msgstr ""
+
+#: network.c:1095
+#, c-format
+msgid "interface %s failed to join DHCPv6 multicast group: %s"
+msgstr ""
+
+#: network.c:1289
 #, c-format
 msgid "failed to bind server socket for %s: %s"
 msgstr ""
 
-#: network.c:783
+#: network.c:1445
 #, c-format
 msgid "ignoring nameserver %s - local interface"
 msgstr ""
 
-#: network.c:794
+#: network.c:1456
 #, c-format
 msgid "ignoring nameserver %s - cannot make/bind socket: %s"
 msgstr ""
 
-#: network.c:811
+#: network.c:1469
 msgid "unqualified"
 msgstr ""
 
-#: network.c:811
+#: network.c:1469
 msgid "names"
 msgstr ""
 
-#: network.c:813
+#: network.c:1471
 msgid "default"
 msgstr ""
 
-#: network.c:815
+#: network.c:1473
 msgid "domain"
 msgstr ""
 
-#: network.c:818
+#: network.c:1476
 #, c-format
 msgid "using local addresses only for %s %s"
 msgstr ""
 
-#: network.c:820
+#: network.c:1478
 #, c-format
 msgid "using standard nameservers for %s %s"
 msgstr ""
 
-#: network.c:822
+#: network.c:1480
 #, c-format
 msgid "using nameserver %s#%d for %s %s"
 msgstr ""
 
-#: network.c:825
+#: network.c:1484
+#, c-format
+msgid "NOT using nameserver %s#%d - query loop detected"
+msgstr ""
+
+#: network.c:1487
 #, c-format
 msgid "using nameserver %s#%d(via %s)"
 msgstr ""
 
-#: network.c:827
+#: network.c:1489
 #, c-format
 msgid "using nameserver %s#%d"
 msgstr ""
 
-#: dnsmasq.c:148
+#: dnsmasq.c:163
+msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"
+msgstr ""
+
+#: dnsmasq.c:170
+msgid "no trust anchors provided for DNSSEC"
+msgstr ""
+
+#: dnsmasq.c:173
+msgid "cannot reduce cache size from default when DNSSEC enabled"
+msgstr ""
+
+#: dnsmasq.c:175
+msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h"
+msgstr ""
+
+#: dnsmasq.c:181
 msgid "TFTP server not available: set HAVE_TFTP in src/config.h"
 msgstr ""
 
-#: dnsmasq.c:153
+#: dnsmasq.c:186
+msgid "cannot use --conntrack AND --query-port"
+msgstr ""
+
+#: dnsmasq.c:189
+msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h"
+msgstr ""
+
+#: dnsmasq.c:194
 msgid "asychronous logging is not available under Solaris"
 msgstr ""
 
-#: dnsmasq.c:158
+#: dnsmasq.c:199
 msgid "asychronous logging is not available under Android"
 msgstr ""
 
-#: dnsmasq.c:177
-#, c-format
-msgid "failed to find list of interfaces: %s"
+#: dnsmasq.c:204
+msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h"
+msgstr ""
+
+#: dnsmasq.c:209
+msgid "loop detection not available: set HAVE_LOOP in src/config.h"
+msgstr ""
+
+#: dnsmasq.c:217
+msgid "zone serial must be configured in --auth-soa"
 msgstr ""
 
-#: dnsmasq.c:185
+#: dnsmasq.c:235
+msgid "dhcp-range constructor not available on this platform"
+msgstr ""
+
+#: dnsmasq.c:278
+msgid "cannot set --bind-interfaces and --bind-dynamic"
+msgstr ""
+
+#: dnsmasq.c:281
 #, c-format
-msgid "unknown interface %s"
+msgid "failed to find list of interfaces: %s"
 msgstr ""
 
-#: dnsmasq.c:191
+#: dnsmasq.c:290
 #, c-format
-msgid "no interface with address %s"
+msgid "unknown interface %s"
 msgstr ""
 
-#: dnsmasq.c:207 dnsmasq.c:678
+#: dnsmasq.c:354 dnsmasq.c:997
 #, c-format
 msgid "DBus error: %s"
 msgstr ""
 
-#: dnsmasq.c:210
+#: dnsmasq.c:357
 msgid "DBus not available: set HAVE_DBUS in src/config.h"
 msgstr ""
 
-#: dnsmasq.c:236
+#: dnsmasq.c:385
 #, c-format
 msgid "unknown user or group: %s"
 msgstr ""
 
-#: dnsmasq.c:291
+#: dnsmasq.c:440
 #, c-format
 msgid "cannot chdir to filesystem root: %s"
 msgstr ""
 
-#: dnsmasq.c:455
+#: dnsmasq.c:692
 #, c-format
 msgid "started, version %s DNS disabled"
 msgstr ""
 
-#: dnsmasq.c:457
+#: dnsmasq.c:694
 #, c-format
 msgid "started, version %s cachesize %d"
 msgstr ""
 
-#: dnsmasq.c:459
+#: dnsmasq.c:696
 #, c-format
 msgid "started, version %s cache disabled"
 msgstr ""
 
-#: dnsmasq.c:461
+#: dnsmasq.c:698
 #, c-format
 msgid "compile time options: %s"
 msgstr ""
 
-#: dnsmasq.c:467
+#: dnsmasq.c:704
 msgid "DBus support enabled: connected to system bus"
 msgstr ""
 
-#: dnsmasq.c:469
+#: dnsmasq.c:706
 msgid "DBus support enabled: bus connection pending"
 msgstr ""
 
-#: dnsmasq.c:474
+#: dnsmasq.c:711
+msgid "DNS service limited to local subnets"
+msgstr ""
+
+#: dnsmasq.c:727
+msgid "DNSSEC validation enabled"
+msgstr ""
+
+#: dnsmasq.c:730
+msgid "DNSSEC signature timestamps not checked until first cache reload"
+msgstr ""
+
+#: dnsmasq.c:733
+msgid "DNSSEC signature timestamps not checked until system time valid"
+msgstr ""
+
+#: dnsmasq.c:738
 #, c-format
 msgid "warning: failed to change owner of %s: %s"
 msgstr ""
 
-#: dnsmasq.c:478
+#: dnsmasq.c:742
 msgid "setting --bind-interfaces option because of OS limitations"
 msgstr ""
 
-#: dnsmasq.c:483
+#: dnsmasq.c:752
 #, c-format
 msgid "warning: interface %s does not currently exist"
 msgstr ""
 
-#: dnsmasq.c:488
+#: dnsmasq.c:757
 msgid "warning: ignoring resolv-file flag because no-resolv is set"
 msgstr ""
 
-#: dnsmasq.c:491
+#: dnsmasq.c:760
 msgid "warning: no upstream servers configured"
 msgstr ""
 
-#: dnsmasq.c:495
+#: dnsmasq.c:764
 #, c-format
 msgid "asynchronous logging enabled, queue limit is %d messages"
 msgstr ""
 
-#: dnsmasq.c:508
-#, c-format
-msgid "DHCP, static leases only on %.0s%s, lease time %s"
-msgstr ""
-
-#: dnsmasq.c:510
-#, c-format
-msgid "DHCP, proxy on subnet %.0s%s%.0s"
+#: dnsmasq.c:785
+msgid "IPv6 router advertisement enabled"
 msgstr ""
 
-#: dnsmasq.c:511
+#: dnsmasq.c:790
 #, c-format
-msgid "DHCP, IP range %s -- %s, lease time %s"
+msgid "DHCP, sockets bound exclusively to interface %s"
 msgstr ""
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 msgid "root is "
 msgstr ""
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 msgid "enabled"
 msgstr ""
 
-#: dnsmasq.c:528
+#: dnsmasq.c:806
 msgid "secure mode"
 msgstr ""
 
-#: dnsmasq.c:554
+#: dnsmasq.c:809
+#, c-format
+msgid "warning: %s inaccessible"
+msgstr ""
+
+#: dnsmasq.c:813
+#, c-format
+msgid "warning: TFTP directory %s inaccessible"
+msgstr ""
+
+#: dnsmasq.c:839
 #, c-format
 msgid "restricting maximum simultaneous TFTP transfers to %d"
 msgstr ""
 
-#: dnsmasq.c:680
+#: dnsmasq.c:999
 msgid "connected to system DBus"
 msgstr ""
 
-#: dnsmasq.c:775
+#: dnsmasq.c:1149
 #, c-format
 msgid "cannot fork into background: %s"
 msgstr ""
 
-#: dnsmasq.c:778
+#: dnsmasq.c:1152
 #, c-format
 msgid "failed to create helper: %s"
 msgstr ""
 
-#: dnsmasq.c:781
+#: dnsmasq.c:1155
 #, c-format
 msgid "setting capabilities failed: %s"
 msgstr ""
 
-#: dnsmasq.c:785
+#: dnsmasq.c:1158
 #, c-format
 msgid "failed to change user-id to %s: %s"
 msgstr ""
 
-#: dnsmasq.c:790
+#: dnsmasq.c:1161
 #, c-format
 msgid "failed to change group-id to %s: %s"
 msgstr ""
 
-#: dnsmasq.c:793
+#: dnsmasq.c:1164
 #, c-format
 msgid "failed to open pidfile %s: %s"
 msgstr ""
 
-#: dnsmasq.c:796
+#: dnsmasq.c:1167
+#, c-format
+msgid "cannot open log %s: %s"
+msgstr ""
+
+#: dnsmasq.c:1170
+#, c-format
+msgid "failed to load Lua script: %s"
+msgstr ""
+
+#: dnsmasq.c:1173
 #, c-format
-msgid "cannot open %s: %s"
+msgid "TFTP directory %s inaccessible: %s"
+msgstr ""
+
+#: dnsmasq.c:1176
+#, c-format
+msgid "cannot create timestamp file %s: %s"
+msgstr ""
+
+#: dnsmasq.c:1197
+msgid "now checking DNSSEC signature timestamps"
 msgstr ""
 
-#: dnsmasq.c:851
+#: dnsmasq.c:1264
 #, c-format
-msgid "child process killed by signal %d"
+msgid "script process killed by signal %d"
 msgstr ""
 
-#: dnsmasq.c:855
+#: dnsmasq.c:1268
 #, c-format
-msgid "child process exited with status %d"
+msgid "script process exited with status %d"
 msgstr ""
 
-#: dnsmasq.c:859
+#: dnsmasq.c:1272
 #, c-format
 msgid "failed to execute %s: %s"
 msgstr ""
 
-#: dnsmasq.c:903
+#: dnsmasq.c:1327
 msgid "exiting on receipt of SIGTERM"
 msgstr ""
 
-#: dnsmasq.c:931
+#: dnsmasq.c:1355
 #, c-format
 msgid "failed to access %s: %s"
 msgstr ""
 
-#: dnsmasq.c:961
+#: dnsmasq.c:1385
 #, c-format
 msgid "reading %s"
 msgstr ""
 
-#: dnsmasq.c:972
+#: dnsmasq.c:1396
 #, c-format
 msgid "no servers found in %s, will retry"
 msgstr ""
 
-#: dhcp.c:40
+#: dhcp.c:53
 #, c-format
 msgid "cannot create DHCP socket: %s"
 msgstr ""
 
-#: dhcp.c:52
+#: dhcp.c:68
 #, c-format
 msgid "failed to set options on DHCP socket: %s"
 msgstr ""
 
-#: dhcp.c:65
+#: dhcp.c:89
 #, c-format
 msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"
 msgstr ""
 
-#: dhcp.c:77
+#: dhcp.c:101
 #, c-format
 msgid "failed to bind DHCP server socket: %s"
 msgstr ""
 
-#: dhcp.c:103
+#: dhcp.c:127
 #, c-format
 msgid "cannot create ICMP raw socket: %s."
 msgstr ""
 
-#: dhcp.c:281
-#, c-format
-msgid "DHCP packet received on %s which has no address"
-msgstr ""
-
-#: dhcp.c:445
+#: dhcp.c:241 dhcp6.c:180
 #, c-format
-msgid "DHCP range %s -- %s is not consistent with netmask %s"
+msgid "unknown interface %s in bridge-interface"
 msgstr ""
 
-#: dhcp.c:852
+#: dhcp.c:281
 #, c-format
-msgid "bad line at %s line %d"
+msgid "DHCP packet received on %s which has no address"
 msgstr ""
 
-#: dhcp.c:895
+#: dhcp.c:415
 #, c-format
-msgid "ignoring %s line %d, duplicate name or IP address"
+msgid "ARP-cache injection failed: %s"
 msgstr ""
 
-#: dhcp.c:978
+#: dhcp.c:514
 #, c-format
-msgid "duplicate IP address %s in dhcp-config directive."
+msgid "DHCP range %s -- %s is not consistent with netmask %s"
 msgstr ""
 
-#: dhcp.c:981
+#: dhcp.c:815
 #, c-format
-msgid "duplicate IP address %s in %s."
+msgid "bad line at %s line %d"
 msgstr ""
 
-#: dhcp.c:1024
+#: dhcp.c:858
 #, c-format
-msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgid "ignoring %s line %d, duplicate name or IP address"
 msgstr ""
 
-#: dhcp.c:1029
+#: dhcp.c:1002 rfc3315.c:2135
 #, c-format
-msgid "duplicate IP address %s (%s) in dhcp-config directive"
+msgid "DHCP relay %s -> %s"
 msgstr ""
 
-#: lease.c:67
+#: lease.c:61
 #, c-format
 msgid "cannot open or create lease file %s: %s"
 msgstr ""
 
-#: lease.c:93
+#: lease.c:134
 msgid "too many stored leases"
 msgstr ""
 
-#: lease.c:129
+#: lease.c:165
 #, c-format
 msgid "cannot run lease-init script %s: %s"
 msgstr ""
 
-#: lease.c:135
+#: lease.c:171
 #, c-format
 msgid "lease-init script returned exit code %s"
 msgstr ""
 
-#: lease.c:235
+#: lease.c:342
 #, c-format
 msgid "failed to write %s: %s (retry in %us)"
 msgstr ""
 
-#: rfc2131.c:315
+#: lease.c:906
+#, c-format
+msgid "Ignoring domain %s for DHCP host name %s"
+msgstr ""
+
+#: rfc2131.c:344
 #, c-format
 msgid "no address range available for DHCP request %s %s"
 msgstr ""
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "with subnet selector"
 msgstr ""
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "via"
 msgstr ""
 
-#: rfc2131.c:331
+#: rfc2131.c:357
 #, c-format
 msgid "%u available DHCP subnet: %s/%s"
 msgstr ""
 
-#: rfc2131.c:334
+#: rfc2131.c:360 rfc3315.c:300
 #, c-format
 msgid "%u available DHCP range: %s -- %s"
 msgstr ""
 
-#: rfc2131.c:363
+#: rfc2131.c:471
+#, c-format
+msgid "%u vendor class: %s"
+msgstr ""
+
+#: rfc2131.c:473
+#, c-format
+msgid "%u user class: %s"
+msgstr ""
+
+#: rfc2131.c:500
 msgid "disabled"
 msgstr ""
 
-#: rfc2131.c:404 rfc2131.c:916 rfc2131.c:1288
+#: rfc2131.c:541 rfc2131.c:974 rfc2131.c:1380 rfc3315.c:603 rfc3315.c:856
+#: rfc3315.c:1135
 msgid "ignored"
 msgstr ""
 
-#: rfc2131.c:419 rfc2131.c:1135
+#: rfc2131.c:556 rfc2131.c:1207 rfc3315.c:906
 msgid "address in use"
 msgstr ""
 
-#: rfc2131.c:433 rfc2131.c:970
+#: rfc2131.c:570 rfc2131.c:1028
 msgid "no address available"
 msgstr ""
 
-#: rfc2131.c:440 rfc2131.c:1098
+#: rfc2131.c:577 rfc2131.c:1170
 msgid "wrong network"
 msgstr ""
 
-#: rfc2131.c:454
+#: rfc2131.c:592
 msgid "no address configured"
 msgstr ""
 
-#: rfc2131.c:460 rfc2131.c:1148
+#: rfc2131.c:598 rfc2131.c:1220
 msgid "no leases left"
 msgstr ""
 
-#: rfc2131.c:545
+#: rfc2131.c:693 rfc3315.c:476
 #, c-format
 msgid "%u client provides name: %s"
 msgstr ""
 
-#: rfc2131.c:700
-#, c-format
-msgid "%u vendor class: %s"
-msgstr ""
-
-#: rfc2131.c:702
-#, c-format
-msgid "%u user class: %s"
-msgstr ""
-
-#: rfc2131.c:761
+#: rfc2131.c:798
 msgid "PXE BIS not supported"
 msgstr ""
 
-#: rfc2131.c:886
+#: rfc2131.c:942 rfc3315.c:1229
 #, c-format
 msgid "disabling DHCP static address %s for %s"
 msgstr ""
 
-#: rfc2131.c:907
+#: rfc2131.c:963
 msgid "unknown lease"
 msgstr ""
 
-#: rfc2131.c:939
+#: rfc2131.c:997
 #, c-format
 msgid "not using configured address %s because it is leased to %s"
 msgstr ""
 
-#: rfc2131.c:949
+#: rfc2131.c:1007
 #, c-format
 msgid "not using configured address %s because it is in use by the server or relay"
 msgstr ""
 
-#: rfc2131.c:952
+#: rfc2131.c:1010
 #, c-format
 msgid "not using configured address %s because it was previously declined"
 msgstr ""
 
-#: rfc2131.c:968 rfc2131.c:1141
+#: rfc2131.c:1026 rfc2131.c:1213
 msgid "no unique-id"
 msgstr ""
 
-#: rfc2131.c:1037
+#: rfc2131.c:1108
 msgid "wrong server-ID"
 msgstr ""
 
-#: rfc2131.c:1055
+#: rfc2131.c:1127
 msgid "wrong address"
 msgstr ""
 
-#: rfc2131.c:1073
+#: rfc2131.c:1145 rfc3315.c:1002
 msgid "lease not found"
 msgstr ""
 
-#: rfc2131.c:1106
+#: rfc2131.c:1178
 msgid "address not available"
 msgstr ""
 
-#: rfc2131.c:1117
+#: rfc2131.c:1189
 msgid "static lease available"
 msgstr ""
 
-#: rfc2131.c:1121
+#: rfc2131.c:1193
 msgid "address reserved"
 msgstr ""
 
-#: rfc2131.c:1129
+#: rfc2131.c:1201
 #, c-format
 msgid "abandoning lease to %s of %s"
 msgstr ""
 
-#: rfc2131.c:1710
-#, c-format
-msgid "%u tags: %s"
-msgstr ""
-
-#: rfc2131.c:1723
+#: rfc2131.c:1707
 #, c-format
 msgid "%u bootfile name: %s"
 msgstr ""
 
-#: rfc2131.c:1732
+#: rfc2131.c:1716
 #, c-format
 msgid "%u server name: %s"
 msgstr ""
 
-#: rfc2131.c:1746
+#: rfc2131.c:1724
 #, c-format
 msgid "%u next server: %s"
 msgstr ""
 
-#: rfc2131.c:1749
+#: rfc2131.c:1727
 #, c-format
 msgid "%u broadcast response"
 msgstr ""
 
-#: rfc2131.c:1812
+#: rfc2131.c:1790
 #, c-format
 msgid "cannot send DHCP/BOOTP option %d: no space left in packet"
 msgstr ""
 
-#: rfc2131.c:2058
+#: rfc2131.c:2031
 msgid "PXE menu too large"
 msgstr ""
 
-#: rfc2131.c:2171
-#, c-format
-msgid "Ignoring domain %s for DHCP host name %s"
-msgstr ""
-
-#: rfc2131.c:2189
+#: rfc2131.c:2170 rfc3315.c:1502
 #, c-format
 msgid "%u requested options: %s"
 msgstr ""
 
-#: rfc2131.c:2456
+#: rfc2131.c:2487
 #, c-format
 msgid "cannot send RFC3925 option: too many options for enterprise number %d"
 msgstr ""
 
-#: netlink.c:70
+#: netlink.c:77
 #, c-format
 msgid "cannot create netlink socket: %s"
 msgstr ""
 
-#: netlink.c:288
+#: netlink.c:348
 #, c-format
 msgid "netlink returns error: %s"
 msgstr ""
 
-#: dbus.c:150
+#: dbus.c:186
 msgid "attempt to set an IPv6 server address via DBus - no IPv6 support"
 msgstr ""
 
-#: dbus.c:286
+#: dbus.c:439
+#, c-format
+msgid "Enabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:444
+#, c-format
+msgid "Disabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:691
 msgid "setting upstream servers from DBus"
 msgstr ""
 
-#: dbus.c:324
+#: dbus.c:738
 msgid "could not register a DBus message handler"
 msgstr ""
 
-#: bpf.c:217
+#: bpf.c:263
 #, c-format
 msgid "cannot create DHCP BPF socket: %s"
 msgstr ""
 
-#: bpf.c:245
+#: bpf.c:291
 #, c-format
 msgid "DHCP request for unsupported hardware type (%d) received on %s"
 msgstr ""
 
-#: tftp.c:281
+#: bpf.c:376
+#, c-format
+msgid "cannot create PF_ROUTE socket: %s"
+msgstr ""
+
+#: bpf.c:397
+msgid "Unknown protocol version from route socket"
+msgstr ""
+
+#: helper.c:153
+msgid "lease() function missing in Lua script"
+msgstr ""
+
+#: tftp.c:309
 msgid "unable to get free port for TFTP"
 msgstr ""
 
-#: tftp.c:296
+#: tftp.c:325
 #, c-format
 msgid "unsupported request from %s"
 msgstr ""
 
-#: tftp.c:406
+#: tftp.c:439
 #, c-format
 msgid "file %s not found"
 msgstr ""
 
-#: tftp.c:522
+#: tftp.c:548
 #, c-format
 msgid "error %d %s received from %s"
 msgstr ""
 
-#: tftp.c:554
+#: tftp.c:590
 #, c-format
 msgid "failed sending %s to %s"
 msgstr ""
 
-#: tftp.c:568
+#: tftp.c:590
 #, c-format
 msgid "sent %s to %s"
 msgstr ""
 
-#: log.c:177
+#: log.c:190
 #, c-format
 msgid "overflow: %d log entries lost"
 msgstr ""
 
-#: log.c:254
+#: log.c:268
 #, c-format
 msgid "log failed: %s"
 msgstr ""
 
-#: log.c:462
+#: log.c:469
 msgid "FAILED to start up"
 msgstr ""
+
+#: conntrack.c:65
+#, c-format
+msgid "Conntrack connection mark retrieval failed: %s"
+msgstr ""
+
+#: dhcp6.c:59
+#, c-format
+msgid "cannot create DHCPv6 socket: %s"
+msgstr ""
+
+#: dhcp6.c:80
+#, c-format
+msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"
+msgstr ""
+
+#: dhcp6.c:92
+#, c-format
+msgid "failed to bind DHCPv6 server socket: %s"
+msgstr ""
+
+#: rfc3315.c:157
+#, c-format
+msgid "no address range available for DHCPv6 request from relay at %s"
+msgstr ""
+
+#: rfc3315.c:166
+#, c-format
+msgid "no address range available for DHCPv6 request via %s"
+msgstr ""
+
+#: rfc3315.c:297
+#, c-format
+msgid "%u available DHCPv6 subnet: %s/%d"
+msgstr ""
+
+#: rfc3315.c:380
+#, c-format
+msgid "%u vendor class: %u"
+msgstr ""
+
+#: rfc3315.c:428
+#, c-format
+msgid "%u client MAC address: %s"
+msgstr ""
+
+#: rfc3315.c:660
+#, c-format
+msgid "unknown prefix-class %d"
+msgstr ""
+
+#: rfc3315.c:803 rfc3315.c:898
+msgid "address unavailable"
+msgstr ""
+
+#: rfc3315.c:815 rfc3315.c:946 rfc3315.c:1279
+msgid "success"
+msgstr ""
+
+#: rfc3315.c:830 rfc3315.c:839 rfc3315.c:954 rfc3315.c:956
+msgid "no addresses available"
+msgstr ""
+
+#: rfc3315.c:933
+msgid "not on link"
+msgstr ""
+
+#: rfc3315.c:1006 rfc3315.c:1191 rfc3315.c:1268
+msgid "no binding found"
+msgstr ""
+
+#: rfc3315.c:1044
+msgid "deprecated"
+msgstr ""
+
+#: rfc3315.c:1049
+msgid "address invalid"
+msgstr ""
+
+#: rfc3315.c:1096
+msgid "confirm failed"
+msgstr ""
+
+#: rfc3315.c:1112
+msgid "all addresses still on link"
+msgstr ""
+
+#: rfc3315.c:1200
+msgid "release received"
+msgstr ""
+
+#: rfc3315.c:2126
+msgid "Cannot multicast to DHCPv6 server without correct interface"
+msgstr ""
+
+#: dhcp-common.c:145
+#, c-format
+msgid "Ignoring duplicate dhcp-option %d"
+msgstr ""
+
+#: dhcp-common.c:222
+#, c-format
+msgid "%u tags: %s"
+msgstr ""
+
+#: dhcp-common.c:407
+#, c-format
+msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgstr ""
+
+#: dhcp-common.c:430
+#, c-format
+msgid "duplicate IP address %s (%s) in dhcp-config directive"
+msgstr ""
+
+#: dhcp-common.c:494
+#, c-format
+msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s"
+msgstr ""
+
+#: dhcp-common.c:615
+#, c-format
+msgid "Known DHCP options:\n"
+msgstr ""
+
+#: dhcp-common.c:626
+#, c-format
+msgid "Known DHCPv6 options:\n"
+msgstr ""
+
+#: dhcp-common.c:823
+msgid ", prefix deprecated"
+msgstr ""
+
+#: dhcp-common.c:826
+#, c-format
+msgid ", lease time "
+msgstr ""
+
+#: dhcp-common.c:868
+#, c-format
+msgid "%s stateless on %s%.0s%.0s%s"
+msgstr ""
+
+#: dhcp-common.c:870
+#, c-format
+msgid "%s, static leases only on %.0s%s%s%.0s"
+msgstr ""
+
+#: dhcp-common.c:872
+#, c-format
+msgid "%s, proxy on subnet %.0s%s%.0s%.0s"
+msgstr ""
+
+#: dhcp-common.c:873
+#, c-format
+msgid "%s, IP range %s -- %s%s%.0s"
+msgstr ""
+
+#: dhcp-common.c:886
+#, c-format
+msgid "DHCPv4-derived IPv6 names on %s%s"
+msgstr ""
+
+#: dhcp-common.c:889
+#, c-format
+msgid "router advertisement on %s%s"
+msgstr ""
+
+#: dhcp-common.c:900
+#, c-format
+msgid "DHCP relay from %s to %s via %s"
+msgstr ""
+
+#: dhcp-common.c:902
+#, c-format
+msgid "DHCP relay from %s to %s"
+msgstr ""
+
+#: radv.c:109
+#, c-format
+msgid "cannot create ICMPv6 socket: %s"
+msgstr ""
+
+#: auth.c:448
+#, c-format
+msgid "ignoring zone transfer request from %s"
+msgstr ""
+
+#: ipset.c:95
+#, c-format
+msgid "failed to find kernel version: %s"
+msgstr ""
+
+#: ipset.c:114
+#, c-format
+msgid "failed to create IPset control socket: %s"
+msgstr ""
+
+#: dnssec.c:449 dnssec.c:493
+#, c-format
+msgid "failed to update mtime on %s: %s"
+msgstr ""
+
+#: blockdata.c:58
+#, c-format
+msgid "DNSSEC memory in use %u, max %u, allocated %u"
+msgstr ""
+
+#: tables.c:80
+msgid "error: fill_addr missused"
+msgstr ""
+
+#: tables.c:109
+#, c-format
+msgid "failed to access pf devices: %s"
+msgstr ""
+
+#: tables.c:123
+#, c-format
+msgid "warning: no opened pf devices %s"
+msgstr ""
+
+#: tables.c:131
+#, c-format
+msgid "error: cannot use table name %s"
+msgstr ""
+
+#: tables.c:139
+#, c-format
+msgid "error: cannot strlcpy table name %s"
+msgstr ""
+
+#: tables.c:145
+#, c-format
+msgid "warning: pfr_add_tables: %s(%d)"
+msgstr ""
+
+#: tables.c:151
+msgid "info: table created"
+msgstr ""
+
+#: tables.c:162
+#, c-format
+msgid "warning: DIOCR%sADDRS: %s"
+msgstr ""
+
+#: tables.c:166
+#, c-format
+msgid "%d addresses %s"
+msgstr ""
+
+#: inotify.c:59
+#, c-format
+msgid "cannot access path %s: %s"
+msgstr ""
+
+#: inotify.c:92
+#, c-format
+msgid "failed to create inotify: %s"
+msgstr ""
+
+#: inotify.c:105
+#, c-format
+msgid "too many symlinks following %s"
+msgstr ""
+
+#: inotify.c:121
+#, c-format
+msgid "directory %s for resolv-file is missing, cannot poll"
+msgstr ""
+
+#: inotify.c:125 inotify.c:162
+#, c-format
+msgid "failed to create inotify for %s: %s"
+msgstr ""
+
+#: inotify.c:147
+#, c-format
+msgid "bad dynamic directory %s: %s"
+msgstr ""
+
+#: inotify.c:247
+#, c-format
+msgid "inotify, new or changed file %s"
+msgstr ""
index 78382cf..5cac04f 100644 (file)
--- a/po/no.po
+++ b/po/no.po
@@ -12,552 +12,745 @@ msgstr ""
 "PO-Revision-Date: 2006-01-11 17:39+0000\n"
 "Last-Translator: Jan Erik Askildt <jeaskildt@gmail.com>\n"
 "Language-Team: Norwegian <i18n-nb@lister.ping.uio.no>\n"
+"Language: no\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=ISO-8859-1\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: cache.c:761
+#: cache.c:523
+msgid "Internal error in cache."
+msgstr ""
+
+#: cache.c:941
 #, fuzzy, c-format
 msgid "failed to load names from %s: %s"
 msgstr "feilet å laste navn fra %s: %s"
 
-#: cache.c:795 dhcp.c:865
+#: cache.c:967 dhcp.c:828
 #, c-format
 msgid "bad address at %s line %d"
 msgstr "dårlig adresse ved %s linje %d"
 
-#: cache.c:853 dhcp.c:881
+#: cache.c:1018 dhcp.c:844
 #, c-format
 msgid "bad name at %s line %d"
 msgstr "dårlig navn ved %s linje %d"
 
-#: cache.c:860 dhcp.c:956
+#: cache.c:1027 dhcp.c:919
 #, c-format
 msgid "read %s - %d addresses"
 msgstr "les %s - %d adresser"
 
-#: cache.c:899
+#: cache.c:1135
 msgid "cleared cache"
 msgstr "mellomlager tømt"
 
-#: cache.c:960
+#: cache.c:1164
+#, c-format
+msgid "No IPv4 address found for %s"
+msgstr ""
+
+#: cache.c:1242
 #, c-format
 msgid "%s is a CNAME, not giving it to the DHCP lease of %s"
 msgstr ""
 
-#: cache.c:966
+#: cache.c:1266
 #, c-format
 msgid "not giving name %s to the DHCP lease of %s because the name exists in %s with address %s"
 msgstr "gir ikke navnet %s til DHCP leien for %s fordi navnet eksisterer i %s med adressen %s"
 
-#: cache.c:1039
+#: cache.c:1421
 #, c-format
 msgid "time %lu"
 msgstr ""
 
-#: cache.c:1040
+#: cache.c:1422
 #, fuzzy, c-format
 msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries."
 msgstr "mellomlager størrelse %d, %d/%d mellomlager innsettinger re-bruker mellomlager plasser som ikke er utløpt"
 
-#: cache.c:1042
+#: cache.c:1424
 #, c-format
 msgid "queries forwarded %u, queries answered locally %u"
 msgstr ""
 
-#: cache.c:1068
+#: cache.c:1427
+#, c-format
+msgid "queries for authoritative zones %u"
+msgstr ""
+
+#: cache.c:1453
 #, c-format
 msgid "server %s#%d: queries sent %u, retried or failed %u"
 msgstr ""
 
-#: util.c:57
+#: util.c:45
 #, fuzzy, c-format
 msgid "failed to seed the random number generator: %s"
 msgstr "feilet å lytte på socket: %s"
 
-#: util.c:189
+#: util.c:205
 #, fuzzy
 msgid "failed to allocate memory"
 msgstr "feilet å laste %d bytes"
 
-#: util.c:227 option.c:573
+#: util.c:250 option.c:601
 msgid "could not get memory"
 msgstr "kunne ikke få minne"
 
-#: util.c:237
+#: util.c:260
 #, fuzzy, c-format
 msgid "cannot create pipe: %s"
 msgstr "kan ikke lese %s: %s"
 
-#: util.c:245
+#: util.c:268
 #, fuzzy, c-format
 msgid "failed to allocate %d bytes"
 msgstr "feilet å laste %d bytes"
 
-#: util.c:350
+#: util.c:437
 #, c-format
 msgid "infinite"
 msgstr "uendelig"
 
-#: option.c:244
+#: option.c:332
 msgid "Specify local address(es) to listen on."
 msgstr "Spesifiser lokal(e) adresse(r) å lytte på."
 
-#: option.c:245
+#: option.c:333
 msgid "Return ipaddr for all hosts in specified domains."
 msgstr "Returner ipaddr for alle verter i det spesifiserte domenet."
 
-#: option.c:246
+#: option.c:334
 msgid "Fake reverse lookups for RFC1918 private address ranges."
 msgstr "Forfalsk revers oppslag for RFC1918 private adresse områder."
 
-#: option.c:247
+#: option.c:335
 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."
 msgstr "Behandle ipaddr som NXDOMAIN (omgår Verisign wildcard)."
 
-#: option.c:248
+#: option.c:336
 #, c-format
 msgid "Specify the size of the cache in entries (defaults to %s)."
 msgstr "Spesifiser størrelsen på mellomlager plassene (standard er %s)."
 
-#: option.c:249
+#: option.c:337
 #, c-format
 msgid "Specify configuration file (defaults to %s)."
 msgstr "Spesifiser konfigurasjonsfil (standard er %s)."
 
-#: option.c:250
+#: option.c:338
 msgid "Do NOT fork into the background: run in debug mode."
 msgstr "IKKE legg (fork) som bakgrunnsprosess: kjør i debug modus."
 
-#: option.c:251
+#: option.c:339
 msgid "Do NOT forward queries with no domain part."
 msgstr "IKKE videresend oppslag som mangler domene del."
 
-#: option.c:252
+#: option.c:340
 msgid "Return self-pointing MX records for local hosts."
 msgstr "Returner selv-pekende MX post for lokale verter."
 
-#: option.c:253
+#: option.c:341
 msgid "Expand simple names in /etc/hosts with domain-suffix."
 msgstr "Utvid enkle navn i /etc/hosts med domene-suffiks."
 
-#: option.c:254
+#: option.c:342
 msgid "Don't forward spurious DNS requests from Windows hosts."
 msgstr "Ikke videresend falske/uekte DNS forespørsler fra Windows verter."
 
-#: option.c:255
+#: option.c:343
 msgid "Enable DHCP in the range given with lease duration."
 msgstr "Aktiver DHCP i det gitte området med leie varighet"
 
-#: option.c:256
+#: option.c:344
 #, c-format
 msgid "Change to this group after startup (defaults to %s)."
 msgstr "Skift til denne gruppen etter oppstart (standard er %s)."
 
-#: option.c:257
+#: option.c:345
 msgid "Set address or hostname for a specified machine."
 msgstr "Sett adresse eller vertsnavn for en spesifikk maskin."
 
-#: option.c:258
+#: option.c:346
 #, fuzzy
 msgid "Read DHCP host specs from file."
 msgstr "dårlig MX navn"
 
-#: option.c:259
+#: option.c:347
 msgid "Read DHCP option specs from file."
 msgstr ""
 
-#: option.c:260
+#: option.c:348
+#, fuzzy
+msgid "Read DHCP host specs from a directory."
+msgstr "dårlig MX navn"
+
+#: option.c:349
+#, fuzzy
+msgid "Read DHCP options from a directory."
+msgstr "dårlig MX navn"
+
+#: option.c:350
 msgid "Evaluate conditional tag expression."
 msgstr ""
 
-#: option.c:261
+#: option.c:351
 #, c-format
 msgid "Do NOT load %s file."
 msgstr "IKKE last %s filen."
 
-#: option.c:262
+#: option.c:352
 #, c-format
 msgid "Specify a hosts file to be read in addition to %s."
 msgstr "Spesifiser en verts (hosts) fil som skal leses i tilleg til %s."
 
-#: option.c:263
+#: option.c:353
+#, fuzzy
+msgid "Read hosts files from a directory."
+msgstr "dårlig MX navn"
+
+#: option.c:354
 msgid "Specify interface(s) to listen on."
 msgstr "Spesifiser nettverkskort det skal lyttes på."
 
-#: option.c:264
+#: option.c:355
 msgid "Specify interface(s) NOT to listen on."
 msgstr "Spesifiser nettverkskort det IKKE skal lyttes på."
 
-#: option.c:265
+#: option.c:356
 #, fuzzy
 msgid "Map DHCP user class to tag."
 msgstr "Map DHCP bruker klasse til opsjon sett."
 
-#: option.c:266
+#: option.c:357
 msgid "Map RFC3046 circuit-id to tag."
 msgstr ""
 
-#: option.c:267
+#: option.c:358
 msgid "Map RFC3046 remote-id to tag."
 msgstr ""
 
-#: option.c:268
+#: option.c:359
 msgid "Map RFC3993 subscriber-id to tag."
 msgstr ""
 
-#: option.c:269
+#: option.c:360
 #, fuzzy
 msgid "Don't do DHCP for hosts with tag set."
 msgstr "Ikke utfør DHCP for klienter i opsjon sett."
 
-#: option.c:270
+#: option.c:361
 #, fuzzy
 msgid "Force broadcast replies for hosts with tag set."
 msgstr "Ikke utfør DHCP for klienter i opsjon sett."
 
-#: option.c:271
+#: option.c:362
 msgid "Do NOT fork into the background, do NOT run in debug mode."
 msgstr "IKKE last (fork) som bakgrunnsprosess, IKKE kjør i debug modus."
 
-#: option.c:272
+#: option.c:363
 msgid "Assume we are the only DHCP server on the local network."
 msgstr "Anta at vi er den eneste DHCP tjeneren på det lokale nettverket."
 
-#: option.c:273
+#: option.c:364
 #, c-format
 msgid "Specify where to store DHCP leases (defaults to %s)."
 msgstr "Spesifiser hvor DHCP leiene skal lagres (standard er %s)."
 
-#: option.c:274
+#: option.c:365
 msgid "Return MX records for local hosts."
 msgstr "Returner MX records for lokale verter."
 
-#: option.c:275
+#: option.c:366
 msgid "Specify an MX record."
 msgstr "Spesifiser en MX post."
 
-#: option.c:276
+#: option.c:367
 msgid "Specify BOOTP options to DHCP server."
 msgstr "Spesifiser BOOTP opsjoner til DHCP tjener."
 
-#: option.c:277
+#: option.c:368
 #, c-format
 msgid "Do NOT poll %s file, reload only on SIGHUP."
 msgstr "IKKE spør (poll) %s fil, les på nytt kun ved SIGHUP"
 
-#: option.c:278
+#: option.c:369
 msgid "Do NOT cache failed search results."
 msgstr "IKKE mellomlagre søkeresultater som feiler."
 
-#: option.c:279
+#: option.c:370
 #, c-format
 msgid "Use nameservers strictly in the order given in %s."
 msgstr "Bruk navnetjenere kun som bestemt i rekkefølgen gitt i %s."
 
-#: option.c:280
+#: option.c:371
 #, fuzzy
 msgid "Specify options to be sent to DHCP clients."
 msgstr "Sett ekstra opsjoner som skal fordeles til DHCP klientene."
 
-#: option.c:281
+#: option.c:372
 msgid "DHCP option sent even if the client does not request it."
 msgstr ""
 
-#: option.c:282
+#: option.c:373
 msgid "Specify port to listen for DNS requests on (defaults to 53)."
 msgstr "Spesifiser lytteport for DNS oppslag (standard er 53)."
 
-#: option.c:283
+#: option.c:374
 #, c-format
 msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)."
 msgstr "Maksimal støttet UDP pakkestørrelse for EDNS.0 (standard er %s)."
 
-#: option.c:284
+#: option.c:375
 #, fuzzy
 msgid "Log DNS queries."
 msgstr "Logg oppslag."
 
-#: option.c:285
+#: option.c:376
 #, fuzzy
 msgid "Force the originating port for upstream DNS queries."
 msgstr "Tving bruk av opprinnelig port for oppstrøms oppslag."
 
-#: option.c:286
+#: option.c:377
 msgid "Do NOT read resolv.conf."
 msgstr "IKKE les resolv.conf."
 
-#: option.c:287
+#: option.c:378
 #, c-format
 msgid "Specify path to resolv.conf (defaults to %s)."
 msgstr "Spesifiser stien til resolv.conf (standard er %s)."
 
-#: option.c:288
+#: option.c:379
+#, fuzzy
+msgid "Specify path to file with server= options"
+msgstr "Spesifiser stien til PID fil. (standard er %s)."
+
+#: option.c:380
 msgid "Specify address(es) of upstream servers with optional domains."
 msgstr "Spesifiser adressen(e) til oppstrøms tjenere med valgfrie domener."
 
-#: option.c:289
+#: option.c:381
+#, fuzzy
+msgid "Specify address of upstream servers for reverse address queries"
+msgstr "Spesifiser adressen(e) til oppstrøms tjenere med valgfrie domener."
+
+#: option.c:382
 msgid "Never forward queries to specified domains."
 msgstr "Aldri videresend oppslag til spesifiserte domener."
 
-#: option.c:290
+#: option.c:383
 msgid "Specify the domain to be assigned in DHCP leases."
 msgstr "Spesifiser domenet som skal tildeles i DHCP leien."
 
-#: option.c:291
+#: option.c:384
 msgid "Specify default target in an MX record."
 msgstr "Spesifiser default mål i en MX post."
 
-#: option.c:292
+#: option.c:385
 msgid "Specify time-to-live in seconds for replies from /etc/hosts."
 msgstr "Spesifiser time-to-live i sekunder for svar fra /etc/hosts."
 
-#: option.c:293
+#: option.c:386
 #, fuzzy
 msgid "Specify time-to-live in seconds for negative caching."
 msgstr "Spesifiser time-to-live i sekunder for svar fra /etc/hosts."
 
-#: option.c:294
+#: option.c:387
 #, fuzzy
 msgid "Specify time-to-live in seconds for maximum TTL to send to clients."
 msgstr "Spesifiser time-to-live i sekunder for svar fra /etc/hosts."
 
-#: option.c:295
+#: option.c:388
+#, fuzzy
+msgid "Specify time-to-live ceiling for cache."
+msgstr "Spesifiser time-to-live i sekunder for svar fra /etc/hosts."
+
+#: option.c:389
+#, fuzzy
+msgid "Specify time-to-live floor for cache."
+msgstr "Spesifiser time-to-live i sekunder for svar fra /etc/hosts."
+
+#: option.c:390
 #, c-format
 msgid "Change to this user after startup. (defaults to %s)."
 msgstr "Skift til denne bruker etter oppstart (standard er %s)."
 
-#: option.c:296
+#: option.c:391
 #, fuzzy
 msgid "Map DHCP vendor class to tag."
 msgstr "Map DHCP produsent klasse til opsjon sett."
 
-#: option.c:297
+#: option.c:392
 msgid "Display dnsmasq version and copyright information."
 msgstr "Vis dnsmasq versjon og copyright informasjon."
 
-#: option.c:298
+#: option.c:393
 msgid "Translate IPv4 addresses from upstream servers."
 msgstr "Oversett IPv4 adresser fra oppstrøms tjenere."
 
-#: option.c:299
+#: option.c:394
 msgid "Specify a SRV record."
 msgstr "Spesifiser en SRV post."
 
-#: option.c:300
+#: option.c:395
 msgid "Display this message. Use --help dhcp for known DHCP options."
 msgstr ""
 
-#: option.c:301
+#: option.c:396
 #, fuzzy, c-format
 msgid "Specify path of PID file (defaults to %s)."
 msgstr "Spesifiser stien til PID fil. (standard er %s)."
 
-#: option.c:302
+#: option.c:397
 #, c-format
 msgid "Specify maximum number of DHCP leases (defaults to %s)."
 msgstr "Spesifiser maksimum antall DHCP leier (standard er %s)"
 
-#: option.c:303
+#: option.c:398
 msgid "Answer DNS queries based on the interface a query was sent to."
 msgstr "Svar DNS oppslag basert på nettverkskortet oppslaget ble sendt til."
 
-#: option.c:304
+#: option.c:399
 msgid "Specify TXT DNS record."
 msgstr "Spesifiser TXT DNS post."
 
-#: option.c:305
+#: option.c:400
 #, fuzzy
 msgid "Specify PTR DNS record."
 msgstr "Spesifiser TXT DNS post."
 
-#: option.c:306
+#: option.c:401
 msgid "Give DNS name to IPv4 address of interface."
 msgstr ""
 
-#: option.c:307
+#: option.c:402
 msgid "Bind only to interfaces in use."
 msgstr "Bind kun til nettverkskort som er i bruk."
 
-#: option.c:308
+#: option.c:403
 #, c-format
 msgid "Read DHCP static host information from %s."
 msgstr "Les DHCP statisk vert informasjon fra %s."
 
-#: option.c:309
+#: option.c:404
 msgid "Enable the DBus interface for setting upstream servers, etc."
 msgstr "Aktiver DBus interface for å sette oppstrøms tjenere, osv."
 
-#: option.c:310
+#: option.c:405
 msgid "Do not provide DHCP on this interface, only provide DNS."
 msgstr "Ikke lever DHCP på dette nettverkskortet, kun lever DNS."
 
-#: option.c:311
+#: option.c:406
 msgid "Enable dynamic address allocation for bootp."
 msgstr "Aktiver dynamisk adresse allokering for bootp."
 
-#: option.c:312
+#: option.c:407
 #, fuzzy
 msgid "Map MAC address (with wildcards) to option set."
 msgstr "Map DHCP produsent klasse til opsjon sett."
 
-#: option.c:313
+#: option.c:408
 msgid "Treat DHCP requests on aliases as arriving from interface."
 msgstr ""
 
-#: option.c:314
+#: option.c:409
 msgid "Disable ICMP echo address checking in the DHCP server."
 msgstr ""
 
-#: option.c:315
-msgid "Script to run on DHCP lease creation and destruction."
+#: option.c:410
+msgid "Shell script to run on DHCP lease creation and destruction."
+msgstr ""
+
+#: option.c:411
+msgid "Lua script to run on DHCP lease creation and destruction."
+msgstr ""
+
+#: option.c:412
+msgid "Run lease-change scripts as this user."
 msgstr ""
 
-#: option.c:316
+#: option.c:413
 msgid "Read configuration from all the files in this directory."
 msgstr ""
 
-#: option.c:317
+#: option.c:414
 #, fuzzy
 msgid "Log to this syslog facility or file. (defaults to DAEMON)"
 msgstr "Skift til denne bruker etter oppstart (standard er %s)."
 
-#: option.c:318
+#: option.c:415
 msgid "Do not use leasefile."
 msgstr ""
 
-#: option.c:319
+#: option.c:416
 #, fuzzy, c-format
 msgid "Maximum number of concurrent DNS queries. (defaults to %s)"
 msgstr "Spesifiser maksimum antall DHCP leier (standard er %s)"
 
-#: option.c:320
+#: option.c:417
 #, c-format
 msgid "Clear DNS cache when reloading %s."
 msgstr ""
 
-#: option.c:321
+#: option.c:418
 msgid "Ignore hostnames provided by DHCP clients."
 msgstr ""
 
-#: option.c:322
+#: option.c:419
 msgid "Do NOT reuse filename and server fields for extra DHCP options."
 msgstr ""
 
-#: option.c:323
+#: option.c:420
 msgid "Enable integrated read-only TFTP server."
 msgstr ""
 
-#: option.c:324
+#: option.c:421
 msgid "Export files by TFTP only from the specified subtree."
 msgstr ""
 
-#: option.c:325
+#: option.c:422
 msgid "Add client IP address to tftp-root."
 msgstr ""
 
-#: option.c:326
+#: option.c:423
 msgid "Allow access only to files owned by the user running dnsmasq."
 msgstr ""
 
-#: option.c:327
+#: option.c:424
+msgid "Do not terminate the service if TFTP directories are inaccessible."
+msgstr ""
+
+#: option.c:425
 #, fuzzy, c-format
 msgid "Maximum number of conncurrent TFTP transfers (defaults to %s)."
 msgstr "Spesifiser maksimum antall DHCP leier (standard er %s)"
 
-#: option.c:328
+#: option.c:426
 msgid "Disable the TFTP blocksize extension."
 msgstr ""
 
-#: option.c:329
+#: option.c:427
+msgid "Convert TFTP filenames to lowercase"
+msgstr ""
+
+#: option.c:428
 msgid "Ephemeral port range for use by TFTP transfers."
 msgstr ""
 
-#: option.c:330
+#: option.c:429
 msgid "Extra logging for DHCP."
 msgstr ""
 
-#: option.c:331
+#: option.c:430
 msgid "Enable async. logging; optionally set queue length."
 msgstr ""
 
-#: option.c:332
+#: option.c:431
 msgid "Stop DNS rebinding. Filter private IP ranges when resolving."
 msgstr ""
 
-#: option.c:333
+#: option.c:432
 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers."
 msgstr ""
 
-#: option.c:334
+#: option.c:433
 msgid "Inhibit DNS-rebind protection on this domain."
 msgstr ""
 
-#: option.c:335
+#: option.c:434
 msgid "Always perform DNS queries to all servers."
 msgstr ""
 
-#: option.c:336
+#: option.c:435
 msgid "Set tag if client includes matching option in request."
 msgstr ""
 
-#: option.c:337
+#: option.c:436
 msgid "Use alternative ports for DHCP."
 msgstr ""
 
-#: option.c:338
-msgid "Run lease-change script as this user."
-msgstr ""
-
-#: option.c:339
+#: option.c:437
 #, fuzzy
 msgid "Specify NAPTR DNS record."
 msgstr "Spesifiser TXT DNS post."
 
-#: option.c:340
+#: option.c:438
 msgid "Specify lowest port available for DNS query transmission."
 msgstr ""
 
-#: option.c:341
+#: option.c:439
 msgid "Use only fully qualified domain names for DHCP clients."
 msgstr ""
 
-#: option.c:342
+#: option.c:440
 msgid "Generate hostnames based on MAC address for nameless clients."
 msgstr ""
 
-#: option.c:343
+#: option.c:441
 msgid "Use these DHCP relays as full proxies."
 msgstr ""
 
-#: option.c:344
+#: option.c:442
+msgid "Relay DHCP requests to a remote server"
+msgstr ""
+
+#: option.c:443
 msgid "Specify alias name for LOCAL DNS name."
 msgstr ""
 
-#: option.c:345
+#: option.c:444
 #, fuzzy
 msgid "Prompt to send to PXE clients."
 msgstr "Sett ekstra opsjoner som skal fordeles til DHCP klientene."
 
-#: option.c:346
+#: option.c:445
 msgid "Boot service for PXE menu."
 msgstr ""
 
-#: option.c:347
+#: option.c:446
 msgid "Check configuration syntax."
 msgstr ""
 
-#: option.c:348
-msgid "Add requestor's MAC address to forwarded DNS queries"
+#: option.c:447
+msgid "Add requestor's MAC address to forwarded DNS queries."
 msgstr ""
 
-#: option.c:349
+#: option.c:448
+msgid "Add requestor's IP subnet to forwarded DNS queries."
+msgstr ""
+
+#: option.c:449
 #, fuzzy
-msgid "Proxy DNSSEC validation results from upstream nameservers"
+msgid "Proxy DNSSEC validation results from upstream nameservers."
 msgstr "Oversett IPv4 adresser fra oppstrøms tjenere."
 
-#: option.c:638
+#: option.c:450
+msgid "Attempt to allocate sequential IP addresses to DHCP clients."
+msgstr ""
+
+#: option.c:451
+msgid "Copy connection-track mark from queries to upstream connections."
+msgstr ""
+
+#: option.c:452
+msgid "Allow DHCP clients to do their own DDNS updates."
+msgstr ""
+
+#: option.c:453
+msgid "Send router-advertisements for interfaces doing DHCPv6"
+msgstr ""
+
+#: option.c:454
+msgid "Specify DUID_EN-type DHCPv6 server DUID"
+msgstr ""
+
+#: option.c:455
+#, fuzzy
+msgid "Specify host (A/AAAA and PTR) records"
+msgstr "Spesifiser en MX post."
+
+#: option.c:456
+#, fuzzy
+msgid "Specify arbitrary DNS resource record"
+msgstr "Spesifiser TXT DNS post."
+
+#: option.c:457
+#, fuzzy
+msgid "Bind to interfaces in use - check for new interfaces"
+msgstr "ukjent tilknytning (interface) %s"
+
+#: option.c:458
+msgid "Export local names to global DNS"
+msgstr ""
+
+#: option.c:459
+msgid "Domain to export to global DNS"
+msgstr ""
+
+#: option.c:460
+msgid "Set TTL for authoritative replies"
+msgstr ""
+
+#: option.c:461
+msgid "Set authoritive zone information"
+msgstr ""
+
+#: option.c:462
+msgid "Secondary authoritative nameservers for forward domains"
+msgstr ""
+
+#: option.c:463
+msgid "Peers which are allowed to do zone transfer"
+msgstr ""
+
+#: option.c:464
+msgid "Specify ipsets to which matching domains should be added"
+msgstr ""
+
+#: option.c:465
+msgid "Specify a domain and address range for synthesised names"
+msgstr ""
+
+#: option.c:466
+msgid "Activate DNSSEC validation"
+msgstr ""
+
+#: option.c:467
+msgid "Specify trust anchor key digest."
+msgstr ""
+
+#: option.c:468
+msgid "Disable upstream checking for DNSSEC debugging."
+msgstr ""
+
+#: option.c:469
+msgid "Ensure answers without DNSSEC are in unsigned zones."
+msgstr ""
+
+#: option.c:470
+msgid "Don't check DNSSEC signature timestamps until first cache-reload"
+msgstr ""
+
+#: option.c:471
+msgid "Timestamp file to verify system clock for DNSSEC"
+msgstr ""
+
+#: option.c:473
+msgid "Specify DHCPv6 prefix class"
+msgstr ""
+
+#: option.c:475
+msgid "Set priority, resend-interval and router-lifetime"
+msgstr ""
+
+#: option.c:476
+msgid "Do not log routine DHCP."
+msgstr ""
+
+#: option.c:477
+msgid "Do not log routine DHCPv6."
+msgstr ""
+
+#: option.c:478
+msgid "Do not log RA."
+msgstr ""
+
+#: option.c:479
+msgid "Accept queries only from directly-connected networks"
+msgstr ""
+
+#: option.c:480
+msgid "Detect and remove DNS forwarding loops"
+msgstr ""
+
+#: option.c:481
+msgid "Ignore DNS responses containing ipaddr."
+msgstr ""
+
+#: option.c:683
 #, c-format
 msgid ""
 "Usage: dnsmasq [options]\n"
@@ -566,930 +759,1458 @@ msgstr ""
 "Bruk: dnsmasq [opsjoner]\n"
 "\n"
 
-#: option.c:640
+#: option.c:685
 #, c-format
 msgid "Use short options only on the command line.\n"
 msgstr "Bruk korte opsjoner kun på kommandolinjen.\n"
 
-#: option.c:642
+#: option.c:687
 #, fuzzy, c-format
 msgid "Valid options are:\n"
 msgstr "Gyldige opsjoner er :\n"
 
-#: option.c:683
-#, c-format
-msgid "Known DHCP options:\n"
+#: option.c:744 option.c:748
+msgid "bad port"
+msgstr "dårlig port"
+
+#: option.c:775 option.c:807
+msgid "interface binding not supported"
+msgstr ""
+
+#: option.c:784 option.c:3575
+#, fuzzy
+msgid "bad interface name"
+msgstr "dårlig MX navn"
+
+#: option.c:814
+#, fuzzy
+msgid "bad address"
+msgstr "les %s - %d adresser"
+
+#: option.c:996
+msgid "unsupported encapsulation for IPv6 option"
 msgstr ""
 
-#: option.c:798
+#: option.c:1010
 msgid "bad dhcp-option"
 msgstr "dårlig dhcp-opsjon"
 
-#: option.c:860
+#: option.c:1078
 #, fuzzy
 msgid "bad IP address"
 msgstr "les %s - %d adresser"
 
-#: option.c:968
+#: option.c:1081 option.c:1219 option.c:2893
+#, fuzzy
+msgid "bad IPv6 address"
+msgstr "les %s - %d adresser"
+
+#: option.c:1246 option.c:1340
 msgid "bad domain in dhcp-option"
 msgstr "dårlig domene i dhcp-opsjon"
 
-#: option.c:1034
+#: option.c:1378
 msgid "dhcp-option too long"
 msgstr "dhcp-opsjon for lang"
 
-#: option.c:1043
+#: option.c:1385
 msgid "illegal dhcp-match"
 msgstr ""
 
-#: option.c:1087
+#: option.c:1447
 msgid "illegal repeated flag"
 msgstr ""
 
-#: option.c:1095
+#: option.c:1455
 msgid "illegal repeated keyword"
 msgstr ""
 
-#: option.c:1147 option.c:3030
+#: option.c:1520 option.c:4191
 #, fuzzy, c-format
 msgid "cannot access directory %s: %s"
 msgstr "kan ikke lese %s: %s"
 
-#: option.c:1178 tftp.c:460
+#: option.c:1566 tftp.c:493
 #, fuzzy, c-format
 msgid "cannot access %s: %s"
 msgstr "kan ikke lese %s: %s"
 
-#: option.c:1207
+#: option.c:1618
 msgid "setting log facility is not possible under Android"
 msgstr ""
 
-#: option.c:1216
+#: option.c:1627
 msgid "bad log facility"
 msgstr ""
 
-#: option.c:1265
+#: option.c:1680
 msgid "bad MX preference"
 msgstr "dårlig MX preferanse"
 
-#: option.c:1270
+#: option.c:1685
 msgid "bad MX name"
 msgstr "dårlig MX navn"
 
-#: option.c:1284
+#: option.c:1699
 msgid "bad MX target"
 msgstr "dårlig MX mål"
 
-#: option.c:1294
+#: option.c:1711
 msgid "cannot run scripts under uClinux"
 msgstr ""
 
-#: option.c:1296
+#: option.c:1713
 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts"
 msgstr ""
 
-#: option.c:1597 option.c:1601
-msgid "bad port"
+#: option.c:1717
+msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts"
+msgstr ""
+
+#: option.c:1973 option.c:2018 option.c:2074
+#, fuzzy
+msgid "bad prefix"
 msgstr "dårlig port"
 
-#: option.c:1620 option.c:1645
-msgid "interface binding not supported"
+#: option.c:2355
+msgid "recompile with HAVE_IPSET defined to enable ipset directives"
 msgstr ""
 
-#: option.c:1791
+#: option.c:2548
 #, fuzzy
 msgid "bad port range"
 msgstr "dårlig port"
 
-#: option.c:1808
+#: option.c:2564
 msgid "bad bridge-interface"
 msgstr ""
 
-#: option.c:1850
+#: option.c:2624
+msgid "only one tag allowed"
+msgstr ""
+
+#: option.c:2644 option.c:2656 option.c:2764 option.c:2805
 msgid "bad dhcp-range"
 msgstr "dårlig dhcp-område"
 
-#: option.c:1878
-msgid "only one tag allowed"
+#: option.c:2671
+msgid "inconsistent DHCP range"
+msgstr "ikke konsistent DHCP område"
+
+#: option.c:2732
+msgid "prefix length must be exactly 64 for RA subnets"
 msgstr ""
 
-#: option.c:1925
-msgid "inconsistent DHCP range"
+#: option.c:2734
+msgid "prefix length must be exactly 64 for subnet constructors"
+msgstr ""
+
+#: option.c:2738
+msgid "prefix length must be at least 64"
+msgstr ""
+
+#: option.c:2741
+#, fuzzy
+msgid "inconsistent DHCPv6 range"
 msgstr "ikke konsistent DHCP område"
 
-#: option.c:2019 option.c:2045
+#: option.c:2752
+msgid "prefix must be zero with \"constructor:\" argument"
+msgstr ""
+
+#: option.c:2863 option.c:2911
 #, fuzzy
 msgid "bad hex constant"
 msgstr "dårlig dhcp-vert"
 
-#: option.c:2107
+#: option.c:2885
+msgid "cannot match tags in --dhcp-host"
+msgstr ""
+
+#: option.c:2933
+#, fuzzy, c-format
+msgid "duplicate dhcp-host IP address %s"
+msgstr "dubliserte IP adresser i %s dhcp-config direktiv."
+
+#: option.c:2991
 #, fuzzy
 msgid "bad DHCP host name"
 msgstr "dårlig MX navn"
 
-#: option.c:2188
+#: option.c:3073
 #, fuzzy
 msgid "bad tag-if"
 msgstr "dårlig MX mål"
 
-#: option.c:2467 option.c:2752
+#: option.c:3397 option.c:3791
 msgid "invalid port number"
 msgstr "ugyldig portnummer"
 
-#: option.c:2529
+#: option.c:3459
 #, fuzzy
 msgid "bad dhcp-proxy address"
 msgstr "les %s - %d adresser"
 
-#: option.c:2569
+#: option.c:3485
 #, fuzzy
-msgid "invalid alias range"
-msgstr "ugyldig vekt"
+msgid "Bad dhcp-relay"
+msgstr "dårlig dhcp-område"
+
+#: option.c:3511
+msgid "bad RA-params"
+msgstr ""
+
+#: option.c:3520
+msgid "bad DUID"
+msgstr ""
 
-#: option.c:2582
+#: option.c:3562
 #, fuzzy
-msgid "bad interface name"
-msgstr "dårlig MX navn"
+msgid "invalid alias range"
+msgstr "ugyldig vekt"
 
-#: option.c:2607
+#: option.c:3616
 msgid "bad CNAME"
 msgstr ""
 
-#: option.c:2612
+#: option.c:3621
 msgid "duplicate CNAME"
 msgstr ""
 
-#: option.c:2632
+#: option.c:3641
 #, fuzzy
 msgid "bad PTR record"
 msgstr "dårlig SRV post"
 
-#: option.c:2663
+#: option.c:3672
 #, fuzzy
 msgid "bad NAPTR record"
 msgstr "dårlig SRV post"
 
-#: option.c:2695
+#: option.c:3706
+#, fuzzy
+msgid "bad RR record"
+msgstr "dårlig SRV post"
+
+#: option.c:3736
 msgid "bad TXT record"
 msgstr "dårlig TXT post"
 
-#: option.c:2738
+#: option.c:3777
 msgid "bad SRV record"
 msgstr "dårlig SRV post"
 
-#: option.c:2745
+#: option.c:3784
 msgid "bad SRV target"
 msgstr "dårlig SRV mål"
 
-#: option.c:2759
+#: option.c:3798
 msgid "invalid priority"
 msgstr "ugyldig prioritet"
 
-#: option.c:2766
+#: option.c:3805
 msgid "invalid weight"
 msgstr "ugyldig vekt"
 
-#: option.c:2785
-msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"
+#: option.c:3829
+#, fuzzy
+msgid "Bad host-record"
+msgstr "dårlig SRV post"
+
+#: option.c:3846
+#, fuzzy
+msgid "Bad name in host-record"
+msgstr "dårlig navn i %s"
+
+#: option.c:3911
+#, fuzzy
+msgid "bad trust anchor"
+msgstr "dårlig port"
+
+#: option.c:3925
+msgid "bad HEX in trust anchor"
+msgstr ""
+
+#: option.c:3935
+msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"
 msgstr ""
 
-#: option.c:2849
+#: option.c:3994
 msgid "missing \""
 msgstr "mangler \""
 
-#: option.c:2908
+#: option.c:4051
 msgid "bad option"
 msgstr "dårlig opsjon"
 
-#: option.c:2910
+#: option.c:4053
 msgid "extraneous parameter"
 msgstr "overflødig parameter"
 
-#: option.c:2912
+#: option.c:4055
 msgid "missing parameter"
 msgstr "mangler parameter"
 
-#: option.c:2916
+#: option.c:4057
+#, fuzzy
+msgid "illegal option"
+msgstr "dårlig opsjon"
+
+#: option.c:4064
 msgid "error"
 msgstr "feil"
 
-#: option.c:2921
-#, c-format
-msgid "%s at line %d of %%s"
+#: option.c:4066
+#, fuzzy, c-format
+msgid " at line %d of %s"
 msgstr "%s på linje %d av %%s"
 
-#: option.c:2985 tftp.c:624
-#, c-format
-msgid "cannot read %s: %s"
-msgstr "kan ikke lese %s: %s"
-
-#: option.c:3151 option.c:3187
+#: option.c:4081 option.c:4328 option.c:4364
 #, fuzzy, c-format
 msgid "read %s"
 msgstr "leser %s"
 
-#: option.c:3239
+#: option.c:4144 option.c:4267 tftp.c:667
+#, c-format
+msgid "cannot read %s: %s"
+msgstr "kan ikke lese %s: %s"
+
+#: option.c:4430
 msgid "junk found in command line"
 msgstr ""
 
-#: option.c:3269
+#: option.c:4465
 #, c-format
 msgid "Dnsmasq version %s  %s\n"
 msgstr "Dnsmasq versjon %s %s\n"
 
-#: option.c:3270
-#, c-format
+#: option.c:4466
+#, fuzzy, c-format
 msgid ""
-"Compile time options %s\n"
+"Compile time options: %s\n"
 "\n"
 msgstr ""
 "Kompileringsopsjoner %s\n"
 "\n"
 
-#: option.c:3271
+#: option.c:4467
 #, c-format
 msgid "This software comes with ABSOLUTELY NO WARRANTY.\n"
 msgstr "Denne programvaren kommer med ABSOLUTT INGEN GARANTI.\n"
 
-#: option.c:3272
+#: option.c:4468
 #, c-format
 msgid "Dnsmasq is free software, and you are welcome to redistribute it\n"
 msgstr "DNsmasq er fri programvare, du er velkommen til å redistribuere den\n"
 
-#: option.c:3273
+#: option.c:4469
 #, fuzzy, c-format
 msgid "under the terms of the GNU General Public License, version 2 or 3.\n"
 msgstr "under vilkårene gitt i GNU General Public License, versjon 2.\n"
 
-#: option.c:3284
+#: option.c:4480
 msgid "try --help"
 msgstr ""
 
-#: option.c:3286
+#: option.c:4482
 msgid "try -w"
 msgstr ""
 
-#: option.c:3289
+#: option.c:4484
 #, fuzzy, c-format
 msgid "bad command line options: %s"
 msgstr "dårlige kommandlinje opsjoner: %s."
 
-#: option.c:3330
+#: option.c:4541
 #, c-format
 msgid "cannot get host-name: %s"
 msgstr "klarer ikke å få vertsnavn: %s"
 
-#: option.c:3358
+#: option.c:4569
 msgid "only one resolv.conf file allowed in no-poll mode."
 msgstr "kun en resolv.conf fil tillat i no-poll modus."
 
-#: option.c:3368
+#: option.c:4579
 msgid "must have exactly one resolv.conf to read domain from."
 msgstr "må ha nøyaktig en resolv.conf å lese domene fra."
 
-#: option.c:3371 network.c:848 dhcp.c:814
+#: option.c:4582 network.c:1507 dhcp.c:777
 #, fuzzy, c-format
 msgid "failed to read %s: %s"
 msgstr "feilet å lese %s: %s"
 
-#: option.c:3388
+#: option.c:4599
 #, c-format
 msgid "no search directive found in %s"
 msgstr "intet søke direktiv funnet i %s"
 
-#: option.c:3409
+#: option.c:4620
 msgid "there must be a default domain when --dhcp-fqdn is set"
 msgstr ""
 
-#: option.c:3413
+#: option.c:4629
 msgid "syntax check OK"
 msgstr ""
 
-#: forward.c:461
+#: forward.c:111
+#, fuzzy, c-format
+msgid "failed to send packet: %s"
+msgstr "feilet å lytte på socket: %s"
+
+#: forward.c:591
+msgid "discarding DNS reply: subnet option mismatch"
+msgstr ""
+
+#: forward.c:614
 #, c-format
 msgid "nameserver %s refused to do a recursive query"
 msgstr "navnetjener %s nektet å gjøre et rekursivt oppslag"
 
-#: forward.c:489
+#: forward.c:646
 #, c-format
 msgid "possible DNS-rebind attack detected: %s"
 msgstr ""
 
-#: network.c:171
+#: forward.c:1209 forward.c:1815
+msgid "Ignoring query from non-local network"
+msgstr ""
+
+#: forward.c:2286
 #, fuzzy, c-format
-msgid "unknown interface %s in bridge-interface"
-msgstr "ukjent tilknytning (interface) %s"
+msgid "Maximum number of concurrent DNS queries reached (max: %d)"
+msgstr "Spesifiser maksimum antall DHCP leier (standard er %s)"
 
-#: network.c:380
+#: network.c:715
 #, fuzzy, c-format
 msgid "failed to create listening socket for %s: %s"
 msgstr "feilet å lage lytte socket: %s"
 
-#: network.c:746
+#: network.c:1021
+#, c-format
+msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s"
+msgstr ""
+
+#: network.c:1028
+msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)"
+msgstr ""
+
+#: network.c:1037
+#, fuzzy, c-format
+msgid "warning: no addresses found for interface %s"
+msgstr "benytter lokale adresser kun for %s %s"
+
+#: network.c:1095
+#, fuzzy, c-format
+msgid "interface %s failed to join DHCPv6 multicast group: %s"
+msgstr "feilet å binde DHCP tjener socket: %s"
+
+#: network.c:1289
 #, fuzzy, c-format
 msgid "failed to bind server socket for %s: %s"
 msgstr "feilet å binde lytte socket for %s: %s"
 
-#: network.c:783
+#: network.c:1445
 #, c-format
 msgid "ignoring nameserver %s - local interface"
 msgstr "ignorerer navnetjener %s - lokal tilknytning"
 
-#: network.c:794
+#: network.c:1456
 #, fuzzy, c-format
 msgid "ignoring nameserver %s - cannot make/bind socket: %s"
 msgstr "ignorerer navnetjener %s - kan ikke lage/dinde socket: %s"
 
-#: network.c:811
+#: network.c:1469
 msgid "unqualified"
 msgstr "ikke kvalifisert"
 
-#: network.c:811
+#: network.c:1469
 msgid "names"
 msgstr ""
 
-#: network.c:813
+#: network.c:1471
 msgid "default"
 msgstr ""
 
-#: network.c:815
+#: network.c:1473
 msgid "domain"
 msgstr "domene"
 
-#: network.c:818
+#: network.c:1476
 #, c-format
 msgid "using local addresses only for %s %s"
 msgstr "benytter lokale adresser kun for %s %s"
 
-#: network.c:820
+#: network.c:1478
 #, fuzzy, c-format
 msgid "using standard nameservers for %s %s"
 msgstr "benytter navnetjener %s#%d for %s %s"
 
-#: network.c:822
+#: network.c:1480
 #, c-format
 msgid "using nameserver %s#%d for %s %s"
 msgstr "benytter navnetjener %s#%d for %s %s"
 
-#: network.c:825
+#: network.c:1484
+#, fuzzy, c-format
+msgid "NOT using nameserver %s#%d - query loop detected"
+msgstr "benytter navnetjener %s#%d for %s %s"
+
+#: network.c:1487
 #, fuzzy, c-format
 msgid "using nameserver %s#%d(via %s)"
 msgstr "benytter navnetjener %s#%d"
 
-#: network.c:827
+#: network.c:1489
 #, c-format
 msgid "using nameserver %s#%d"
 msgstr "benytter navnetjener %s#%d"
 
-#: dnsmasq.c:148
-#, fuzzy
-msgid "TFTP server not available: set HAVE_TFTP in src/config.h"
-msgstr "DBus ikke tilgjengelig: sett HAVE_DBUS i src/config.h"
+#: dnsmasq.c:163
+msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"
+msgstr ""
 
-#: dnsmasq.c:153
+#: dnsmasq.c:170
+msgid "no trust anchors provided for DNSSEC"
+msgstr ""
+
+#: dnsmasq.c:173
+msgid "cannot reduce cache size from default when DNSSEC enabled"
+msgstr ""
+
+#: dnsmasq.c:175
+#, fuzzy
+msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h"
+msgstr "DBus ikke tilgjengelig: sett HAVE_DBUS i src/config.h"
+
+#: dnsmasq.c:181
+#, fuzzy
+msgid "TFTP server not available: set HAVE_TFTP in src/config.h"
+msgstr "DBus ikke tilgjengelig: sett HAVE_DBUS i src/config.h"
+
+#: dnsmasq.c:186
+msgid "cannot use --conntrack AND --query-port"
+msgstr ""
+
+#: dnsmasq.c:189
+#, fuzzy
+msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h"
+msgstr "DBus ikke tilgjengelig: sett HAVE_DBUS i src/config.h"
+
+#: dnsmasq.c:194
 msgid "asychronous logging is not available under Solaris"
 msgstr ""
 
-#: dnsmasq.c:158
+#: dnsmasq.c:199
 msgid "asychronous logging is not available under Android"
 msgstr ""
 
-#: dnsmasq.c:177
+#: dnsmasq.c:204
+#, fuzzy
+msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h"
+msgstr "DBus ikke tilgjengelig: sett HAVE_DBUS i src/config.h"
+
+#: dnsmasq.c:209
+#, fuzzy
+msgid "loop detection not available: set HAVE_LOOP in src/config.h"
+msgstr "DBus ikke tilgjengelig: sett HAVE_DBUS i src/config.h"
+
+#: dnsmasq.c:217
+msgid "zone serial must be configured in --auth-soa"
+msgstr ""
+
+#: dnsmasq.c:235
+msgid "dhcp-range constructor not available on this platform"
+msgstr ""
+
+#: dnsmasq.c:278
+msgid "cannot set --bind-interfaces and --bind-dynamic"
+msgstr ""
+
+#: dnsmasq.c:281
 #, c-format
 msgid "failed to find list of interfaces: %s"
 msgstr "feilet å finne liste av tilknytninger (interfaces): %s"
 
-#: dnsmasq.c:185
+#: dnsmasq.c:290
 #, c-format
 msgid "unknown interface %s"
 msgstr "ukjent tilknytning (interface) %s"
 
-#: dnsmasq.c:191
-#, c-format
-msgid "no interface with address %s"
-msgstr "ingen tilknytning (interface) med adresse %s"
-
-#: dnsmasq.c:207 dnsmasq.c:678
+#: dnsmasq.c:354 dnsmasq.c:997
 #, c-format
 msgid "DBus error: %s"
 msgstr "DBus feil: %s"
 
-#: dnsmasq.c:210
+#: dnsmasq.c:357
 msgid "DBus not available: set HAVE_DBUS in src/config.h"
 msgstr "DBus ikke tilgjengelig: sett HAVE_DBUS i src/config.h"
 
-#: dnsmasq.c:236
+#: dnsmasq.c:385
 #, c-format
 msgid "unknown user or group: %s"
 msgstr ""
 
-#: dnsmasq.c:291
+#: dnsmasq.c:440
 #, c-format
 msgid "cannot chdir to filesystem root: %s"
 msgstr ""
 
-#: dnsmasq.c:455
+#: dnsmasq.c:692
 #, fuzzy, c-format
 msgid "started, version %s DNS disabled"
 msgstr "startet, versjon %s mellomlager deaktivert"
 
-#: dnsmasq.c:457
+#: dnsmasq.c:694
 #, c-format
 msgid "started, version %s cachesize %d"
 msgstr "startet, versjon %s mellomlager størrelse %d"
 
-#: dnsmasq.c:459
+#: dnsmasq.c:696
 #, c-format
 msgid "started, version %s cache disabled"
 msgstr "startet, versjon %s mellomlager deaktivert"
 
-#: dnsmasq.c:461
+#: dnsmasq.c:698
 #, c-format
 msgid "compile time options: %s"
 msgstr "kompilerings opsjoner: %s"
 
-#: dnsmasq.c:467
+#: dnsmasq.c:704
 msgid "DBus support enabled: connected to system bus"
 msgstr "DBus støtte aktivert: koblet til system buss"
 
-#: dnsmasq.c:469
+#: dnsmasq.c:706
 msgid "DBus support enabled: bus connection pending"
 msgstr "DBus støtte aktivert: avventer buss tilkobling"
 
-#: dnsmasq.c:474
+#: dnsmasq.c:711
+msgid "DNS service limited to local subnets"
+msgstr ""
+
+#: dnsmasq.c:727
+msgid "DNSSEC validation enabled"
+msgstr ""
+
+#: dnsmasq.c:730
+msgid "DNSSEC signature timestamps not checked until first cache reload"
+msgstr ""
+
+#: dnsmasq.c:733
+msgid "DNSSEC signature timestamps not checked until system time valid"
+msgstr ""
+
+#: dnsmasq.c:738
 #, fuzzy, c-format
 msgid "warning: failed to change owner of %s: %s"
 msgstr "feilet å laste navn fra %s: %s"
 
-#: dnsmasq.c:478
+#: dnsmasq.c:742
 msgid "setting --bind-interfaces option because of OS limitations"
 msgstr "setter --bind-interfaces opsjon på grunn av OS begrensninger"
 
-#: dnsmasq.c:483
+#: dnsmasq.c:752
 #, c-format
 msgid "warning: interface %s does not currently exist"
 msgstr "advarsel: nettverkskort %s eksisterer ikke for tiden"
 
-#: dnsmasq.c:488
+#: dnsmasq.c:757
 msgid "warning: ignoring resolv-file flag because no-resolv is set"
 msgstr ""
 
-#: dnsmasq.c:491
+#: dnsmasq.c:760
 #, fuzzy
 msgid "warning: no upstream servers configured"
 msgstr "setter oppstrøms tjener fra DBus"
 
-#: dnsmasq.c:495
+#: dnsmasq.c:764
 #, c-format
 msgid "asynchronous logging enabled, queue limit is %d messages"
 msgstr ""
 
-#: dnsmasq.c:508
-#, c-format
-msgid "DHCP, static leases only on %.0s%s, lease time %s"
-msgstr "DHCP, statisk leie kun på %.0s%s, leie tid %s"
-
-#: dnsmasq.c:510
-#, c-format
-msgid "DHCP, proxy on subnet %.0s%s%.0s"
+#: dnsmasq.c:785
+msgid "IPv6 router advertisement enabled"
 msgstr ""
 
-#: dnsmasq.c:511
+#: dnsmasq.c:790
 #, c-format
-msgid "DHCP, IP range %s -- %s, lease time %s"
-msgstr "DHCP, IP område %s -- %s, leie tid %s"
+msgid "DHCP, sockets bound exclusively to interface %s"
+msgstr ""
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 msgid "root is "
 msgstr ""
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 #, fuzzy
 msgid "enabled"
 msgstr "deaktivert"
 
-#: dnsmasq.c:528
+#: dnsmasq.c:806
 msgid "secure mode"
 msgstr ""
 
-#: dnsmasq.c:554
+#: dnsmasq.c:809
+#, c-format
+msgid "warning: %s inaccessible"
+msgstr ""
+
+#: dnsmasq.c:813
+#, c-format
+msgid "warning: TFTP directory %s inaccessible"
+msgstr ""
+
+#: dnsmasq.c:839
 #, c-format
 msgid "restricting maximum simultaneous TFTP transfers to %d"
 msgstr ""
 
-#: dnsmasq.c:680
+#: dnsmasq.c:999
 msgid "connected to system DBus"
 msgstr "tilkoblet til system DBus"
 
-#: dnsmasq.c:775
+#: dnsmasq.c:1149
 #, c-format
 msgid "cannot fork into background: %s"
 msgstr ""
 
-#: dnsmasq.c:778
+#: dnsmasq.c:1152
 #, fuzzy, c-format
 msgid "failed to create helper: %s"
 msgstr "feilet å lese %s: %s"
 
-#: dnsmasq.c:781
+#: dnsmasq.c:1155
 #, c-format
 msgid "setting capabilities failed: %s"
 msgstr ""
 
-#: dnsmasq.c:785
+#: dnsmasq.c:1158
 #, fuzzy, c-format
 msgid "failed to change user-id to %s: %s"
 msgstr "feilet å laste navn fra %s: %s"
 
-#: dnsmasq.c:790
+#: dnsmasq.c:1161
 #, fuzzy, c-format
 msgid "failed to change group-id to %s: %s"
 msgstr "feilet å laste navn fra %s: %s"
 
-#: dnsmasq.c:793
+#: dnsmasq.c:1164
 #, fuzzy, c-format
 msgid "failed to open pidfile %s: %s"
 msgstr "feilet å lese %s: %s"
 
-#: dnsmasq.c:796
+#: dnsmasq.c:1167
 #, fuzzy, c-format
-msgid "cannot open %s: %s"
+msgid "cannot open log %s: %s"
 msgstr "kan ikke åpne %s:%s"
 
-#: dnsmasq.c:851
+#: dnsmasq.c:1170
+#, fuzzy, c-format
+msgid "failed to load Lua script: %s"
+msgstr "feilet å laste %s: %s"
+
+#: dnsmasq.c:1173
 #, c-format
-msgid "child process killed by signal %d"
+msgid "TFTP directory %s inaccessible: %s"
 msgstr ""
 
-#: dnsmasq.c:855
+#: dnsmasq.c:1176
+#, fuzzy, c-format
+msgid "cannot create timestamp file %s: %s"
+msgstr "kan ikke åpne eller lage leie fil: %s"
+
+#: dnsmasq.c:1197
+msgid "now checking DNSSEC signature timestamps"
+msgstr ""
+
+#: dnsmasq.c:1264
+#, c-format
+msgid "script process killed by signal %d"
+msgstr ""
+
+#: dnsmasq.c:1268
 #, c-format
-msgid "child process exited with status %d"
+msgid "script process exited with status %d"
 msgstr ""
 
-#: dnsmasq.c:859
+#: dnsmasq.c:1272
 #, fuzzy, c-format
 msgid "failed to execute %s: %s"
 msgstr "feilet å få tilgang til %s: %s"
 
-#: dnsmasq.c:903
+#: dnsmasq.c:1327
 msgid "exiting on receipt of SIGTERM"
 msgstr "avslutter etter mottak av SIGTERM"
 
-#: dnsmasq.c:931
+#: dnsmasq.c:1355
 #, fuzzy, c-format
 msgid "failed to access %s: %s"
 msgstr "feilet å få tilgang til %s: %s"
 
-#: dnsmasq.c:961
+#: dnsmasq.c:1385
 #, c-format
 msgid "reading %s"
 msgstr "leser %s"
 
-#: dnsmasq.c:972
+#: dnsmasq.c:1396
 #, fuzzy, c-format
 msgid "no servers found in %s, will retry"
 msgstr "intet søke direktiv funnet i %s"
 
-#: dhcp.c:40
+#: dhcp.c:53
 #, c-format
 msgid "cannot create DHCP socket: %s"
 msgstr "kan ikke lage DHCP socket: %s"
 
-#: dhcp.c:52
+#: dhcp.c:68
 #, c-format
 msgid "failed to set options on DHCP socket: %s"
 msgstr "feilet å sette opsjoner på DHCP socket: %s"
 
-#: dhcp.c:65
+#: dhcp.c:89
 #, fuzzy, c-format
 msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"
 msgstr "feilet å sette SO_REUSEADDR på DHCP socket: %s"
 
-#: dhcp.c:77
+#: dhcp.c:101
 #, c-format
 msgid "failed to bind DHCP server socket: %s"
 msgstr "feilet å binde DHCP tjener socket: %s"
 
-#: dhcp.c:103
+#: dhcp.c:127
 #, c-format
 msgid "cannot create ICMP raw socket: %s."
 msgstr "kan ikke lage ICMP raw socket: %s"
 
+#: dhcp.c:241 dhcp6.c:180
+#, fuzzy, c-format
+msgid "unknown interface %s in bridge-interface"
+msgstr "ukjent tilknytning (interface) %s"
+
 #: dhcp.c:281
 #, c-format
 msgid "DHCP packet received on %s which has no address"
 msgstr ""
 
-#: dhcp.c:445
+#: dhcp.c:415
+#, c-format
+msgid "ARP-cache injection failed: %s"
+msgstr ""
+
+#: dhcp.c:514
 #, c-format
 msgid "DHCP range %s -- %s is not consistent with netmask %s"
 msgstr "DHCP område %s -- %s er ikke konsistent med nettmaske %s"
 
-#: dhcp.c:852
+#: dhcp.c:815
 #, c-format
 msgid "bad line at %s line %d"
 msgstr "dårlig linje ved %s linje %d"
 
-#: dhcp.c:895
+#: dhcp.c:858
 #, c-format
 msgid "ignoring %s line %d, duplicate name or IP address"
 msgstr ""
 
-#: dhcp.c:978
-#, c-format
-msgid "duplicate IP address %s in dhcp-config directive."
-msgstr "dubliserte IP adresser i %s dhcp-config direktiv."
-
-#: dhcp.c:981
-#, fuzzy, c-format
-msgid "duplicate IP address %s in %s."
-msgstr "dubliserte IP adresser i %s dhcp-config direktiv."
-
-#: dhcp.c:1024
+#: dhcp.c:1002 rfc3315.c:2135
 #, c-format
-msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgid "DHCP relay %s -> %s"
 msgstr ""
 
-#: dhcp.c:1029
-#, c-format
-msgid "duplicate IP address %s (%s) in dhcp-config directive"
-msgstr "dubliserte IP adresser i %s (%s) i dhcp-config direktiv"
-
-#: lease.c:67
+#: lease.c:61
 #, fuzzy, c-format
 msgid "cannot open or create lease file %s: %s"
 msgstr "kan ikke åpne eller lage leie fil: %s"
 
-#: lease.c:93
+#: lease.c:134
 msgid "too many stored leases"
 msgstr "for mange lagrede leier"
 
-#: lease.c:129
+#: lease.c:165
 #, fuzzy, c-format
 msgid "cannot run lease-init script %s: %s"
 msgstr "kan ikke lese %s: %s"
 
-#: lease.c:135
+#: lease.c:171
 #, c-format
 msgid "lease-init script returned exit code %s"
 msgstr ""
 
-#: lease.c:235
+#: lease.c:342
 #, fuzzy, c-format
 msgid "failed to write %s: %s (retry in %us)"
 msgstr "feilet å lese %s: %s"
 
-#: rfc2131.c:315
+#: lease.c:906
+#, c-format
+msgid "Ignoring domain %s for DHCP host name %s"
+msgstr ""
+
+#: rfc2131.c:344
 #, c-format
 msgid "no address range available for DHCP request %s %s"
 msgstr "ingen adresse område tilgjengelig for DHCP krav %s %s"
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "with subnet selector"
 msgstr "med subnet velger"
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "via"
 msgstr "via"
 
-#: rfc2131.c:331
+#: rfc2131.c:357
 #, fuzzy, c-format
 msgid "%u available DHCP subnet: %s/%s"
 msgstr "ingen adresse område tilgjengelig for DHCP krav %s %s"
 
-#: rfc2131.c:334
+#: rfc2131.c:360 rfc3315.c:300
 #, c-format
 msgid "%u available DHCP range: %s -- %s"
 msgstr ""
 
-#: rfc2131.c:363
+#: rfc2131.c:471
+#, fuzzy, c-format
+msgid "%u vendor class: %s"
+msgstr "DBus feil: %s"
+
+#: rfc2131.c:473
+#, fuzzy, c-format
+msgid "%u user class: %s"
+msgstr "DBus feil: %s"
+
+#: rfc2131.c:500
 msgid "disabled"
 msgstr "deaktivert"
 
-#: rfc2131.c:404 rfc2131.c:916 rfc2131.c:1288
+#: rfc2131.c:541 rfc2131.c:974 rfc2131.c:1380 rfc3315.c:603 rfc3315.c:856
+#: rfc3315.c:1135
 msgid "ignored"
 msgstr "oversett"
 
-#: rfc2131.c:419 rfc2131.c:1135
+#: rfc2131.c:556 rfc2131.c:1207 rfc3315.c:906
 msgid "address in use"
 msgstr "adresse i bruk"
 
-#: rfc2131.c:433 rfc2131.c:970
+#: rfc2131.c:570 rfc2131.c:1028
 msgid "no address available"
 msgstr "ingen adresse tilgjengelig"
 
-#: rfc2131.c:440 rfc2131.c:1098
+#: rfc2131.c:577 rfc2131.c:1170
 msgid "wrong network"
 msgstr "galt nettverk"
 
-#: rfc2131.c:454
+#: rfc2131.c:592
 msgid "no address configured"
 msgstr "ingen adresse konfigurert"
 
-#: rfc2131.c:460 rfc2131.c:1148
+#: rfc2131.c:598 rfc2131.c:1220
 msgid "no leases left"
 msgstr "ingen leier igjen"
 
-#: rfc2131.c:545
+#: rfc2131.c:693 rfc3315.c:476
 #, c-format
 msgid "%u client provides name: %s"
 msgstr ""
 
-#: rfc2131.c:700
-#, fuzzy, c-format
-msgid "%u vendor class: %s"
-msgstr "DBus feil: %s"
-
-#: rfc2131.c:702
-#, fuzzy, c-format
-msgid "%u user class: %s"
-msgstr "DBus feil: %s"
-
-#: rfc2131.c:761
+#: rfc2131.c:798
 msgid "PXE BIS not supported"
 msgstr ""
 
-#: rfc2131.c:886
+#: rfc2131.c:942 rfc3315.c:1229
 #, fuzzy, c-format
 msgid "disabling DHCP static address %s for %s"
 msgstr "deaktiverer DHCP statisk adresse %s"
 
-#: rfc2131.c:907
+#: rfc2131.c:963
 msgid "unknown lease"
 msgstr "ukjent leie"
 
-#: rfc2131.c:939
+#: rfc2131.c:997
 #, c-format
 msgid "not using configured address %s because it is leased to %s"
 msgstr ""
 
-#: rfc2131.c:949
+#: rfc2131.c:1007
 #, c-format
 msgid "not using configured address %s because it is in use by the server or relay"
 msgstr ""
 
-#: rfc2131.c:952
+#: rfc2131.c:1010
 #, c-format
 msgid "not using configured address %s because it was previously declined"
 msgstr ""
 
-#: rfc2131.c:968 rfc2131.c:1141
+#: rfc2131.c:1026 rfc2131.c:1213
 msgid "no unique-id"
 msgstr ""
 
-#: rfc2131.c:1037
+#: rfc2131.c:1108
 msgid "wrong server-ID"
 msgstr ""
 
-#: rfc2131.c:1055
+#: rfc2131.c:1127
 msgid "wrong address"
 msgstr "gal adresse"
 
-#: rfc2131.c:1073
+#: rfc2131.c:1145 rfc3315.c:1002
 msgid "lease not found"
 msgstr "leie ikke funnet"
 
-#: rfc2131.c:1106
+#: rfc2131.c:1178
 msgid "address not available"
 msgstr "adresse ikke tilgjengelig"
 
-#: rfc2131.c:1117
+#: rfc2131.c:1189
 msgid "static lease available"
 msgstr "statisk leie tilgjengelig"
 
-#: rfc2131.c:1121
+#: rfc2131.c:1193
 msgid "address reserved"
 msgstr "adresse reservert"
 
-#: rfc2131.c:1129
+#: rfc2131.c:1201
 #, c-format
 msgid "abandoning lease to %s of %s"
 msgstr ""
 
-#: rfc2131.c:1710
-#, c-format
-msgid "%u tags: %s"
-msgstr ""
-
-#: rfc2131.c:1723
+#: rfc2131.c:1707
 #, c-format
 msgid "%u bootfile name: %s"
 msgstr ""
 
-#: rfc2131.c:1732
+#: rfc2131.c:1716
 #, fuzzy, c-format
 msgid "%u server name: %s"
 msgstr "DBus feil: %s"
 
-#: rfc2131.c:1746
+#: rfc2131.c:1724
 #, fuzzy, c-format
 msgid "%u next server: %s"
 msgstr "DBus feil: %s"
 
-#: rfc2131.c:1749
+#: rfc2131.c:1727
 #, c-format
 msgid "%u broadcast response"
 msgstr ""
 
-#: rfc2131.c:1812
+#: rfc2131.c:1790
 #, fuzzy, c-format
 msgid "cannot send DHCP/BOOTP option %d: no space left in packet"
 msgstr "kan ikke sende DHCP opsjon %d: ikke mer plass i pakken"
 
-#: rfc2131.c:2058
+#: rfc2131.c:2031
 msgid "PXE menu too large"
 msgstr ""
 
-#: rfc2131.c:2171
-#, c-format
-msgid "Ignoring domain %s for DHCP host name %s"
-msgstr ""
-
-#: rfc2131.c:2189
+#: rfc2131.c:2170 rfc3315.c:1502
 #, fuzzy, c-format
 msgid "%u requested options: %s"
 msgstr "kompilerings opsjoner: %s"
 
-#: rfc2131.c:2456
+#: rfc2131.c:2487
 #, c-format
 msgid "cannot send RFC3925 option: too many options for enterprise number %d"
 msgstr ""
 
-#: netlink.c:70
+#: netlink.c:77
 #, fuzzy, c-format
 msgid "cannot create netlink socket: %s"
 msgstr "kan ikke binde netlink socket: %s"
 
-#: netlink.c:288
+#: netlink.c:348
 #, fuzzy, c-format
 msgid "netlink returns error: %s"
 msgstr "DBus feil: %s"
 
-#: dbus.c:150
+#: dbus.c:186
 msgid "attempt to set an IPv6 server address via DBus - no IPv6 support"
 msgstr "forsøk på å sette en IPv6 tjener adresse via DBus - ingen IPv6 støtte"
 
-#: dbus.c:286
+#: dbus.c:439
+#, c-format
+msgid "Enabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:444
+#, c-format
+msgid "Disabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:691
 msgid "setting upstream servers from DBus"
 msgstr "setter oppstrøms tjener fra DBus"
 
-#: dbus.c:324
+#: dbus.c:738
 msgid "could not register a DBus message handler"
 msgstr "kunne ikke registrere en DBus meldingshåndterer"
 
-#: bpf.c:217
+#: bpf.c:263
 #, c-format
 msgid "cannot create DHCP BPF socket: %s"
 msgstr "kan ikke lage DHCP BPF socket: %s"
 
-#: bpf.c:245
+#: bpf.c:291
 #, fuzzy, c-format
 msgid "DHCP request for unsupported hardware type (%d) received on %s"
 msgstr "DHCP krav for ikke støttet maskinvare type (%d) mottatt på %s"
 
-#: tftp.c:281
+#: bpf.c:376
+#, fuzzy, c-format
+msgid "cannot create PF_ROUTE socket: %s"
+msgstr "kan ikke lage DHCP socket: %s"
+
+#: bpf.c:397
+msgid "Unknown protocol version from route socket"
+msgstr ""
+
+#: helper.c:153
+msgid "lease() function missing in Lua script"
+msgstr ""
+
+#: tftp.c:309
 msgid "unable to get free port for TFTP"
 msgstr ""
 
-#: tftp.c:296
+#: tftp.c:325
 #, c-format
 msgid "unsupported request from %s"
 msgstr ""
 
-#: tftp.c:406
+#: tftp.c:439
 #, fuzzy, c-format
 msgid "file %s not found"
 msgstr "leie ikke funnet"
 
-#: tftp.c:522
+#: tftp.c:548
 #, c-format
 msgid "error %d %s received from %s"
 msgstr ""
 
-#: tftp.c:554
+#: tftp.c:590
 #, fuzzy, c-format
 msgid "failed sending %s to %s"
 msgstr "feilet å lese %s: %s"
 
-#: tftp.c:568
+#: tftp.c:590
 #, c-format
 msgid "sent %s to %s"
 msgstr ""
 
-#: log.c:177
+#: log.c:190
 #, c-format
 msgid "overflow: %d log entries lost"
 msgstr ""
 
-#: log.c:254
+#: log.c:268
 #, c-format
 msgid "log failed: %s"
 msgstr ""
 
-#: log.c:462
+#: log.c:469
 msgid "FAILED to start up"
 msgstr "FEILET å starte opp"
 
+#: conntrack.c:65
+#, c-format
+msgid "Conntrack connection mark retrieval failed: %s"
+msgstr ""
+
+#: dhcp6.c:59
+#, fuzzy, c-format
+msgid "cannot create DHCPv6 socket: %s"
+msgstr "kan ikke lage DHCP socket: %s"
+
+#: dhcp6.c:80
+#, fuzzy, c-format
+msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"
+msgstr "feilet å sette SO_REUSEADDR på DHCP socket: %s"
+
+#: dhcp6.c:92
+#, fuzzy, c-format
+msgid "failed to bind DHCPv6 server socket: %s"
+msgstr "feilet å binde DHCP tjener socket: %s"
+
+#: rfc3315.c:157
+#, fuzzy, c-format
+msgid "no address range available for DHCPv6 request from relay at %s"
+msgstr "ingen adresse område tilgjengelig for DHCP krav %s %s"
+
+#: rfc3315.c:166
+#, fuzzy, c-format
+msgid "no address range available for DHCPv6 request via %s"
+msgstr "ingen adresse område tilgjengelig for DHCP krav %s %s"
+
+#: rfc3315.c:297
+#, fuzzy, c-format
+msgid "%u available DHCPv6 subnet: %s/%d"
+msgstr "ingen adresse område tilgjengelig for DHCP krav %s %s"
+
+#: rfc3315.c:380
+#, fuzzy, c-format
+msgid "%u vendor class: %u"
+msgstr "DBus feil: %s"
+
+#: rfc3315.c:428
+#, fuzzy, c-format
+msgid "%u client MAC address: %s"
+msgstr "ingen tilknytning (interface) med adresse %s"
+
+#: rfc3315.c:660
+#, fuzzy, c-format
+msgid "unknown prefix-class %d"
+msgstr "ukjent leie"
+
+#: rfc3315.c:803 rfc3315.c:898
+#, fuzzy
+msgid "address unavailable"
+msgstr "adresse ikke tilgjengelig"
+
+#: rfc3315.c:815 rfc3315.c:946 rfc3315.c:1279
+msgid "success"
+msgstr ""
+
+#: rfc3315.c:830 rfc3315.c:839 rfc3315.c:954 rfc3315.c:956
+#, fuzzy
+msgid "no addresses available"
+msgstr "ingen adresse tilgjengelig"
+
+#: rfc3315.c:933
+msgid "not on link"
+msgstr ""
+
+#: rfc3315.c:1006 rfc3315.c:1191 rfc3315.c:1268
+msgid "no binding found"
+msgstr ""
+
+#: rfc3315.c:1044
+msgid "deprecated"
+msgstr ""
+
+#: rfc3315.c:1049
+#, fuzzy
+msgid "address invalid"
+msgstr "adresse i bruk"
+
+#: rfc3315.c:1096
+msgid "confirm failed"
+msgstr ""
+
+#: rfc3315.c:1112
+#, fuzzy
+msgid "all addresses still on link"
+msgstr "dårlig adresse ved %s linje %d"
+
+#: rfc3315.c:1200
+msgid "release received"
+msgstr ""
+
+#: rfc3315.c:2126
+msgid "Cannot multicast to DHCPv6 server without correct interface"
+msgstr ""
+
+#: dhcp-common.c:145
+#, c-format
+msgid "Ignoring duplicate dhcp-option %d"
+msgstr ""
+
+#: dhcp-common.c:222
+#, c-format
+msgid "%u tags: %s"
+msgstr ""
+
+#: dhcp-common.c:407
+#, c-format
+msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgstr ""
+
+#: dhcp-common.c:430
+#, c-format
+msgid "duplicate IP address %s (%s) in dhcp-config directive"
+msgstr "dubliserte IP adresser i %s (%s) i dhcp-config direktiv"
+
+#: dhcp-common.c:494
+#, fuzzy, c-format
+msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s"
+msgstr "feilet å sette SO_REUSEADDR på DHCP socket: %s"
+
+#: dhcp-common.c:615
+#, c-format
+msgid "Known DHCP options:\n"
+msgstr ""
+
+#: dhcp-common.c:626
+#, c-format
+msgid "Known DHCPv6 options:\n"
+msgstr ""
+
+#: dhcp-common.c:823
+msgid ", prefix deprecated"
+msgstr ""
+
+#: dhcp-common.c:826
+#, c-format
+msgid ", lease time "
+msgstr ""
+
+#: dhcp-common.c:868
+#, c-format
+msgid "%s stateless on %s%.0s%.0s%s"
+msgstr ""
+
+#: dhcp-common.c:870
+#, fuzzy, c-format
+msgid "%s, static leases only on %.0s%s%s%.0s"
+msgstr "DHCP, statisk leie kun på %.0s%s, leie tid %s"
+
+#: dhcp-common.c:872
+#, c-format
+msgid "%s, proxy on subnet %.0s%s%.0s%.0s"
+msgstr ""
+
+#: dhcp-common.c:873
+#, fuzzy, c-format
+msgid "%s, IP range %s -- %s%s%.0s"
+msgstr "DHCP, IP område %s -- %s, leie tid %s"
+
+#: dhcp-common.c:886
+#, c-format
+msgid "DHCPv4-derived IPv6 names on %s%s"
+msgstr ""
+
+#: dhcp-common.c:889
+#, fuzzy, c-format
+msgid "router advertisement on %s%s"
+msgstr "DHCP, statisk leie kun på %.0s%s, leie tid %s"
+
+#: dhcp-common.c:900
+#, c-format
+msgid "DHCP relay from %s to %s via %s"
+msgstr ""
+
+#: dhcp-common.c:902
+#, c-format
+msgid "DHCP relay from %s to %s"
+msgstr ""
+
+#: radv.c:109
+#, fuzzy, c-format
+msgid "cannot create ICMPv6 socket: %s"
+msgstr "kan ikke lage DHCP socket: %s"
+
+#: auth.c:448
+#, c-format
+msgid "ignoring zone transfer request from %s"
+msgstr ""
+
+#: ipset.c:95
+#, fuzzy, c-format
+msgid "failed to find kernel version: %s"
+msgstr "feilet å binde DHCP tjener socket: %s"
+
+#: ipset.c:114
+#, fuzzy, c-format
+msgid "failed to create IPset control socket: %s"
+msgstr "feilet å lage lytte socket: %s"
+
+#: dnssec.c:449 dnssec.c:493
+#, fuzzy, c-format
+msgid "failed to update mtime on %s: %s"
+msgstr "feilet å lese %s: %s"
+
+#: blockdata.c:58
+#, c-format
+msgid "DNSSEC memory in use %u, max %u, allocated %u"
+msgstr ""
+
+#: tables.c:80
+msgid "error: fill_addr missused"
+msgstr ""
+
+#: tables.c:109
+#, fuzzy, c-format
+msgid "failed to access pf devices: %s"
+msgstr "feilet å få tilgang til %s: %s"
+
+#: tables.c:123
+#, fuzzy, c-format
+msgid "warning: no opened pf devices %s"
+msgstr "benytter lokale adresser kun for %s %s"
+
+#: tables.c:131
+#, fuzzy, c-format
+msgid "error: cannot use table name %s"
+msgstr "klarer ikke å få vertsnavn: %s"
+
+#: tables.c:139
+#, c-format
+msgid "error: cannot strlcpy table name %s"
+msgstr ""
+
+#: tables.c:145
+#, c-format
+msgid "warning: pfr_add_tables: %s(%d)"
+msgstr ""
+
+#: tables.c:151
+msgid "info: table created"
+msgstr ""
+
+#: tables.c:162
+#, c-format
+msgid "warning: DIOCR%sADDRS: %s"
+msgstr ""
+
+#: tables.c:166
+#, fuzzy, c-format
+msgid "%d addresses %s"
+msgstr "les %s - %d adresser"
+
+#: inotify.c:59
+#, fuzzy, c-format
+msgid "cannot access path %s: %s"
+msgstr "kan ikke lese %s: %s"
+
+#: inotify.c:92
+#, fuzzy, c-format
+msgid "failed to create inotify: %s"
+msgstr "feilet å lese %s: %s"
+
+#: inotify.c:105
+#, c-format
+msgid "too many symlinks following %s"
+msgstr ""
+
+#: inotify.c:121
+#, c-format
+msgid "directory %s for resolv-file is missing, cannot poll"
+msgstr ""
+
+#: inotify.c:125 inotify.c:162
+#, fuzzy, c-format
+msgid "failed to create inotify for %s: %s"
+msgstr "feilet å lage lytte socket: %s"
+
+#: inotify.c:147
+#, fuzzy, c-format
+msgid "bad dynamic directory %s: %s"
+msgstr "kan ikke lese %s: %s"
+
+#: inotify.c:247
+#, c-format
+msgid "inotify, new or changed file %s"
+msgstr ""
+
+#, fuzzy
+#~ msgid "cannot cannonicalise resolv-file %s: %s"
+#~ msgstr "kan ikke åpne eller lage leie fil: %s"
+
+#~ msgid "duplicate IP address %s in dhcp-config directive."
+#~ msgstr "dubliserte IP adresser i %s dhcp-config direktiv."
+
+#, fuzzy
+#~ msgid "Specify path to Lua script (no default)."
+#~ msgstr "Spesifiser stien til PID fil. (standard er %s)."
+
 #~ msgid "TXT record string too long"
 #~ msgstr "TXT post streng for lang"
 
@@ -1499,23 +2220,9 @@ msgstr "FEILET 
 #~ msgid "failed to bind listening socket for %s: %s"
 #~ msgstr "feilet å binde lytte socket for %s: %s"
 
-#~ msgid "failed to listen on socket: %s"
-#~ msgstr "feilet å lytte på socket: %s"
-
-#, fuzzy
-#~ msgid "failed to create TFTP socket: %s"
-#~ msgstr "feilet å lage lytte socket: %s"
-
 #~ msgid "must set exactly one interface on broken systems without IP_RECVIF"
 #~ msgstr "må sette nøyaktig et interface på ødelagte systemer uten IP_RECVIF"
 
-#, fuzzy
-#~ msgid "failed to load %s: %s"
-#~ msgstr "feilet å laste %s: %s"
-
-#~ msgid "bad name in %s"
-#~ msgstr "dårlig navn i %s"
-
 #~ msgid "Ignoring DHCP lease for %s because it has an illegal domain part"
 #~ msgstr "Ignorerer DHCP leie for %s siden den har en ulovlig domene del"
 
index 945df01..8bcd85f 100644 (file)
--- a/po/pl.po
+++ b/po/pl.po
 # Polish translations for dnsmasq package.
 # This file is put in the public domain.
 #
-# Tomasz Sochañski <nerdhero@gmail.com>, 2005.
-# Jan Psota <jasiu@belsznica.pl>, 2008, 2009, 2010.
-# Jan Psota <jasiupsota@gmail.com>, 2010, 2011.
+# Tomasz Sochański <nerdhero@gmail.com>, 2005.
+# Jan Psota <jasiupsota@gmail.com>, 2008-2015.
+#
 msgid ""
 msgstr ""
 "Project-Id-Version: pl\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2009-06-10 20:57+0100\n"
-"PO-Revision-Date: 2011-01-16 23:56+0100\n"
+"PO-Revision-Date: 2015-07-15 02:08+0100\n"
 "Last-Translator: Jan Psota <jasiupsota@gmail.com>\n"
-"Language-Team: Polish <>\n"
+"Language-Team: polski <>\n"
+"Language: pl\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=iso-8859-2\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-"X-Generator: Lokalize 1.1\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+"X-Generator: Poedit 1.5.5\n"
 "X-Language: pl_PL\n"
 
-#: cache.c:761
+#: cache.c:523
+msgid "Internal error in cache."
+msgstr "Wewnętrzny błąd w pamięci podręcznej."
+
+#: cache.c:941
 #, c-format
 msgid "failed to load names from %s: %s"
-msgstr "nie potrafiê wczytaæ nazw z %s: %s"
+msgstr "nie potrafię wczytać nazw z %s: %s"
 
-#: cache.c:795 dhcp.c:865
+#: cache.c:967 dhcp.c:828
 #, c-format
 msgid "bad address at %s line %d"
-msgstr "b³êdny adres w pliku %s, w linii %d"
+msgstr "błędny adres w pliku %s, w linii %d"
 
-#: cache.c:853 dhcp.c:881
+#: cache.c:1018 dhcp.c:844
 #, c-format
 msgid "bad name at %s line %d"
-msgstr "b³êdna nazwa w pliku %s, w linii %d"
+msgstr "błędna nazwa w pliku %s, w linii %d"
 
-#: cache.c:860 dhcp.c:956
+#: cache.c:1027 dhcp.c:919
 #, c-format
 msgid "read %s - %d addresses"
-msgstr "wczyta³em %s - %d adresów"
+msgstr "wczytałem %s - %d adresów"
 
-#: cache.c:899
+#: cache.c:1135
 msgid "cleared cache"
-msgstr "wyczyszczono pamiêæ podrêczn±"
+msgstr "wyczyszczono pamięć podręczną"
+
+#: cache.c:1164
+#, c-format
+msgid "No IPv4 address found for %s"
+msgstr "Nie znalazłem adresu IPv4 komputera %s"
 
-#: cache.c:960
+#: cache.c:1242
 #, c-format
 msgid "%s is a CNAME, not giving it to the DHCP lease of %s"
-msgstr "%s to nazwa CNAME, nie przypisujê jej dzier¿awie DHCP %s"
+msgstr "%s to nazwa CNAME, nie przypisuję jej dzierżawie DHCP %s"
 
-#: cache.c:966
+#: cache.c:1266
 #, c-format
 msgid "not giving name %s to the DHCP lease of %s because the name exists in %s with address %s"
-msgstr "nazwa %s nie zosta³a nadana dzier¿awie DHCP %s,poniewa¿ nazwa istnieje w %s i ma ju¿ adres %s"
+msgstr "nazwa %s nie została nadana dzierżawie DHCP %s, ponieważ nazwa istnieje w %s i ma już adres %s"
 
-#: cache.c:1039
+#: cache.c:1421
 #, c-format
 msgid "time %lu"
 msgstr "czas %lu"
 
-#: cache.c:1040
+#: cache.c:1422
 #, c-format
 msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries."
-msgstr "wielko¶æ pamiêci podrêcznej: %d; %d z %d miejsc aktualnych wpisów u¿yto ponownie."
+msgstr "wielkość pamięci podręcznej: %d; %d z %d miejsc aktualnych wpisów użyto ponownie."
 
-#: cache.c:1042
+#: cache.c:1424
 #, c-format
 msgid "queries forwarded %u, queries answered locally %u"
-msgstr "%u zapytañ przes³anych dalej, %u odpowiedzi udzielonych samodzielnie"
+msgstr "%u zapytań przesłanych dalej, %u odpowiedzi udzielonych samodzielnie"
+
+#: cache.c:1427
+#, c-format
+msgid "queries for authoritative zones %u"
+msgstr "zapytań do stref autorytatywnych %u"
 
-#: cache.c:1068
+#: cache.c:1453
 #, c-format
 msgid "server %s#%d: queries sent %u, retried or failed %u"
-msgstr "serwer %s#%d: %u zapytañ wys³anych, %u ponowionych lub nieudanych"
+msgstr "serwer %s#%d: %u zapytań wysłanych, %u ponowionych lub nieudanych"
 
-#: util.c:57
+#: util.c:45
 #, c-format
 msgid "failed to seed the random number generator: %s"
-msgstr "brak mo¿liwo¶ci u¿ycia generatora liczb losowych: %s"
+msgstr "brak możliwości użycia generatora liczb losowych: %s"
 
-#: util.c:189
+#: util.c:205
 msgid "failed to allocate memory"
-msgstr "nie uda³o siê przydzieliæ pamiêci"
+msgstr "nie udało się przydzielić pamięci"
 
-#: util.c:227 option.c:573
+#: util.c:250 option.c:601
 msgid "could not get memory"
-msgstr "nie mo¿na dostaæ pamiêci"
+msgstr "nie można dostać pamięci"
 
-#: util.c:237
+#: util.c:260
 #, c-format
 msgid "cannot create pipe: %s"
-msgstr "b³±d podczas próby utworzenia potoku: %s"
+msgstr "błąd podczas próby utworzenia potoku: %s"
 
-#: util.c:245
+#: util.c:268
 #, c-format
 msgid "failed to allocate %d bytes"
-msgstr "niemo¿liwo¶æ przydzielenia %d bajtów pamiêci"
+msgstr "niemożliwość przydzielenia %d bajtów pamięci"
 
-#: util.c:350
+#: util.c:437
 #, c-format
 msgid "infinite"
-msgstr "nieskoñczona"
+msgstr "nieskończona"
 
-#: option.c:244
+#: option.c:332
 msgid "Specify local address(es) to listen on."
-msgstr "Wskazanie adresów, na których nale¿y nas³uchiwaæ."
+msgstr "Wskazanie adresów, na których należy nasłuchiwać."
 
-#: option.c:245
+#: option.c:333
 msgid "Return ipaddr for all hosts in specified domains."
-msgstr "Zwracanie adresu IP dla wszystkich hostów we wskazanych domenach."
+msgstr "Zwracanie adresu IP dla wszystkich hostów we wskazanych domenach."
 
-#: option.c:246
+#: option.c:334
 msgid "Fake reverse lookups for RFC1918 private address ranges."
-msgstr "Wy³±czenie przekazywania zapytañ odwrotnych dla prywatnych zakresów IP."
+msgstr "Wyłączenie przekazywania zapytań odwrotnych dla prywatnych zakresów IP."
 
-#: option.c:247
+#: option.c:335
 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."
-msgstr "Traktowanie adresu IP jako NXDOMAIN (uniewa¿nia ,,Verisign wildcard'')."
+msgstr "Traktowanie adresu IP jako NXDOMAIN (unieważnia ,,Verisign wildcard'')."
 
-#: option.c:248
+#: option.c:336
 #, c-format
 msgid "Specify the size of the cache in entries (defaults to %s)."
-msgstr "Wskazanie wielko¶ci pamiêci podrêcznej (domy¶lnie: %s miejsc)."
+msgstr "Wskazanie wielkości pamięci podręcznej (domyślnie: %s miejsc)."
 
-#: option.c:249
+#: option.c:337
 #, c-format
 msgid "Specify configuration file (defaults to %s)."
-msgstr "Wskazanie pliku konfiguracyjnego (domylnie: %s)."
+msgstr "Wskazanie pliku konfiguracyjnego (domyślnie: %s)."
 
-#: option.c:250
+#: option.c:338
 msgid "Do NOT fork into the background: run in debug mode."
-msgstr "NIE twórz procesu potomnego w tle: dzia³anie w trybie debugowania."
+msgstr "NIE twórz procesu potomnego w tle: działanie w trybie debugowania."
 
-#: option.c:251
+#: option.c:339
 msgid "Do NOT forward queries with no domain part."
-msgstr "Wy³±czenie przekazywania zapytañ bez podanej czê¶ci domenowej."
+msgstr "Wyłączenie przekazywania zapytań bez podanej części domenowej."
 
-#: option.c:252
+#: option.c:340
 msgid "Return self-pointing MX records for local hosts."
-msgstr "Zwracanie samowskazuj±cego rekordu MX dla lokalnych hostów."
+msgstr "Zwracanie samowskazującego rekordu MX dla lokalnych hostów."
 
-#: option.c:253
+#: option.c:341
 msgid "Expand simple names in /etc/hosts with domain-suffix."
 msgstr "Rozwijanie prostych nazw z /etc/hosts przyrostkiem domenowym."
 
-#: option.c:254
+#: option.c:342
 msgid "Don't forward spurious DNS requests from Windows hosts."
-msgstr "Wy³±czenie przekazywania pozornych zapytañ DNS z komputerów dzia³aj±cych pod Windows."
+msgstr "Wyłączenie przekazywania pozornych zapytań DNS z komputerów działających pod Windows."
 
-#: option.c:255
+#: option.c:343
 msgid "Enable DHCP in the range given with lease duration."
-msgstr "W³±czenie serwera DHCP dla wskazanego zakresu adresów."
+msgstr "Włączenie serwera DHCP dla wskazanego zakresu adresów."
 
-#: option.c:256
+#: option.c:344
 #, c-format
 msgid "Change to this group after startup (defaults to %s)."
-msgstr "Po uruchomieniu zmiana grupy procesu na podan± (domy¶lnie: %s)."
+msgstr "Po uruchomieniu zmiana grupy procesu na podaną (domyślnie: %s)."
 
-#: option.c:257
+#: option.c:345
 msgid "Set address or hostname for a specified machine."
 msgstr "Ustawienie adresu lub nazwy dla wskazanego komputera."
 
-#: option.c:258
+#: option.c:346
 msgid "Read DHCP host specs from file."
-msgstr "Wskazanie pliku z wartociami 'dhcp-host='."
+msgstr "Wskazanie pliku z wartościami 'dhcp-host='."
 
-#: option.c:259
+#: option.c:347
 msgid "Read DHCP option specs from file."
-msgstr "Wskazanie pliku z wartociami 'dhcp-option='."
+msgstr "Wskazanie pliku z wartościami 'dhcp-option='."
 
-#: option.c:260
+#: option.c:348
+msgid "Read DHCP host specs from a directory."
+msgstr "Odczyt specyfikacji hostów dla DHCP z katalogu."
+
+#: option.c:349
+msgid "Read DHCP options from a directory."
+msgstr "Odczyt opcji DHCP z katalogu."
+
+#: option.c:350
 msgid "Evaluate conditional tag expression."
-msgstr "Warunkowe ustawianie znaczników."
+msgstr "Warunkowe ustawianie znaczników."
 
-#: option.c:261
+#: option.c:351
 #, c-format
 msgid "Do NOT load %s file."
 msgstr "NIE wczytywanie pliku %s."
 
-#: option.c:262
+#: option.c:352
 #, c-format
 msgid "Specify a hosts file to be read in addition to %s."
-msgstr "Wskazanie dodatkowego pliku 'hosts' oprócz %s."
+msgstr "Wskazanie dodatkowego pliku 'hosts' oprócz %s."
+
+#: option.c:353
+msgid "Read hosts files from a directory."
+msgstr "Odczyt pliku hostów z katalogu."
 
-#: option.c:263
+#: option.c:354
 msgid "Specify interface(s) to listen on."
-msgstr "Interfejsy, na których nas³uchiwaæ."
+msgstr "Interfejsy, na których nasłuchiwać."
 
-#: option.c:264
+#: option.c:355
 msgid "Specify interface(s) NOT to listen on."
-msgstr "Interfejsy, na których NIE nas³uchiwaæ."
+msgstr "Interfejsy, na których NIE nasłuchiwać."
 
-#: option.c:265
+#: option.c:356
 msgid "Map DHCP user class to tag."
-msgstr "Przyporz±dkowanie znacznika w zale¿no¶ci od klasy u¿ytkownika DHCP."
+msgstr "Przyporządkowanie znacznika w zależności od klasy użytkownika DHCP."
 
-#: option.c:266
+#: option.c:357
 msgid "Map RFC3046 circuit-id to tag."
-msgstr "Przyporz±dkowanie znacznika w zale¿no¶ci od numeru obwodu (w rozumieniu RFC3046)."
+msgstr "Przyporządkowanie znacznika w zależności od numeru obwodu (w rozumieniu RFC3046)."
 
-#: option.c:267
+#: option.c:358
 msgid "Map RFC3046 remote-id to tag."
-msgstr "Przyporz±dkowanie znacznika w zale¿no¶ci od numeru agenta (w rozumieniu RFC3046)."
+msgstr "Przyporządkowanie znacznika w zależności od numeru agenta (w rozumieniu RFC3046)."
 
-#: option.c:268
+#: option.c:359
 msgid "Map RFC3993 subscriber-id to tag."
-msgstr "Przyporz±dkowanie znacznika w zale¿no¶ci od numeru subskrybenta (w rozumieniu RFC3993)."
+msgstr "Przyporządkowanie znacznika w zależności od numeru subskrybenta (w rozumieniu RFC3993)."
 
-#: option.c:269
+#: option.c:360
 msgid "Don't do DHCP for hosts with tag set."
-msgstr "Wy³±czenie DHCP dla hostów z okre¶lonym znacznikiem."
+msgstr "Wyłączenie DHCP dla hostów z określonym znacznikiem."
 
-#: option.c:270
+#: option.c:361
 msgid "Force broadcast replies for hosts with tag set."
-msgstr "Wymuszenie odpowiedzi w trybie rozg³oszeniowym dla hostów z okre¶lonym znacznikiem."
+msgstr "Wymuszenie odpowiedzi w trybie rozgłoszeniowym dla hostów z określonym znacznikiem."
 
-#: option.c:271
+#: option.c:362
 msgid "Do NOT fork into the background, do NOT run in debug mode."
-msgstr "NIE twórz procesu potomnego w tle i NIE w³±czaj trybu debugowania."
+msgstr "NIE twórz procesu potomnego w tle i NIE włączaj trybu debugowania."
 
-#: option.c:272
+#: option.c:363
 msgid "Assume we are the only DHCP server on the local network."
-msgstr "Zak³adanie, ¿e jeste¶my jedynym serwerem DHCP w sieci lokalnej."
+msgstr "Zakładanie, że jesteśmy jedynym serwerem DHCP w sieci lokalnej."
 
-#: option.c:273
+#: option.c:364
 #, c-format
 msgid "Specify where to store DHCP leases (defaults to %s)."
-msgstr "¦cie¿ka przechowywania pliku dzier¿aw DHCP (domy¶lnie: %s)."
+msgstr "Ścieżka przechowywania pliku dzierżaw DHCP (domyślnie: %s)."
 
-#: option.c:274
+#: option.c:365
 msgid "Return MX records for local hosts."
-msgstr "W³±czenie zwracania rekordu MX dla hostów lokalnych."
+msgstr "Włączenie zwracania rekordu MX dla hostów lokalnych."
 
-#: option.c:275
+#: option.c:366
 msgid "Specify an MX record."
 msgstr "Specyfikacja rekordu MX."
 
-#: option.c:276
+#: option.c:367
 msgid "Specify BOOTP options to DHCP server."
-msgstr "Okrelenie opcji BOOTP serwera DHCP."
+msgstr "Określenie opcji BOOTP serwera DHCP."
 
-#: option.c:277
+#: option.c:368
 #, c-format
 msgid "Do NOT poll %s file, reload only on SIGHUP."
-msgstr "Wy³±czenie obserwowania pliku %s; ponowne odczytywanie tylko po odebraniu sygna³u SIGHUP."
+msgstr "Wyłączenie obserwowania pliku %s; ponowne odczytywanie tylko po odebraniu sygnału SIGHUP."
 
-#: option.c:278
+#: option.c:369
 msgid "Do NOT cache failed search results."
-msgstr "Wy³±czenie przechowywania w pamiêci podrêcznej wyników nieudanych wyszukiwañ."
+msgstr "Wyłączenie przechowywania w pamięci podręcznej wyników nieudanych wyszukiwań."
 
-#: option.c:279
+#: option.c:370
 #, c-format
 msgid "Use nameservers strictly in the order given in %s."
-msgstr "Odpytywanie serwerów nazw w kolejno¶ci ich wyst±pienia w %s."
+msgstr "Odpytywanie serwerów nazw w kolejności ich wystąpienia w %s."
 
-#: option.c:280
+#: option.c:371
 msgid "Specify options to be sent to DHCP clients."
-msgstr "Specyfikacja opcji wysy³anej do klientów DHCP."
+msgstr "Specyfikacja opcji wysyłanej do klientów DHCP."
 
-#: option.c:281
+#: option.c:372
 msgid "DHCP option sent even if the client does not request it."
-msgstr "Opcja DHCP wysy³ana nawet je¿eli klient o ni± nie prosi."
+msgstr "Opcja DHCP wysyłana nawet jeżeli klient o nią nie prosi."
 
-#: option.c:282
+#: option.c:373
 msgid "Specify port to listen for DNS requests on (defaults to 53)."
-msgstr "Wskazanie portu do nas³uchiwania zapytañ DNS (domy¶lnie: 53)."
+msgstr "Wskazanie portu do nasłuchiwania zapytań DNS (domyślnie: 53)."
 
-#: option.c:283
+#: option.c:374
 #, c-format
 msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)."
-msgstr "Maksymalna obs³ugiwana wielko¶æ pakietu EDNS.0 (domy¶lnie: %s)."
+msgstr "Maksymalna obsługiwana wielkość pakietu EDNS.0 (domyślnie: %s)."
 
-#: option.c:284
+#: option.c:375
 msgid "Log DNS queries."
-msgstr "W³±czenie spisywania zapytañ DNS do logu."
+msgstr "Włączenie spisywania zapytań DNS do logu."
 
-#: option.c:285
+#: option.c:376
 msgid "Force the originating port for upstream DNS queries."
-msgstr "Wymuszenie u¿ycia wskazanego portu UDP do odpytywania nadrzêdnych serwerów DNS i odbierania od nich odpowiedzi."
+msgstr "Wymuszenie użycia wskazanego portu UDP do odpytywania nadrzędnych serwerów DNS i odbierania od nich odpowiedzi."
 
-#: option.c:286
+#: option.c:377
 msgid "Do NOT read resolv.conf."
-msgstr "Wy³±czenie czytania pliku resolv.conf."
+msgstr "Wyłączenie czytania pliku resolv.conf."
 
-#: option.c:287
+#: option.c:378
 #, c-format
 msgid "Specify path to resolv.conf (defaults to %s)."
-msgstr "Wskazanie po³o¿enia pliku resolv.conf (domy¶lnie: %s)."
+msgstr "Wskazanie położenia pliku resolv.conf (domyślnie: %s)."
+
+#: option.c:379
+msgid "Specify path to file with server= options"
+msgstr "Wskazanie położenia pliku z opcjami server="
 
-#: option.c:288
+#: option.c:380
 msgid "Specify address(es) of upstream servers with optional domains."
-msgstr "Wskazywanie adresów serwerów nazw, opcjonalnie z przypisaniem do domeny."
+msgstr "Wskazywanie adresów serwerów nazw, opcjonalnie z przypisaniem do domeny."
 
-#: option.c:289
+#: option.c:381
+msgid "Specify address of upstream servers for reverse address queries"
+msgstr "Wskazanie serwerów nazw do odwrotnej translacji adresów."
+
+#: option.c:382
 msgid "Never forward queries to specified domains."
-msgstr "Wy³±czenie przekazywania zapytañ do wskazanych domen."
+msgstr "Wyłączenie przekazywania zapytań do wskazanych domen."
 
-#: option.c:290
+#: option.c:383
 msgid "Specify the domain to be assigned in DHCP leases."
 msgstr "Wskazanie domeny dla serwera DHCP."
 
-#: option.c:291
+#: option.c:384
 msgid "Specify default target in an MX record."
-msgstr "Okre¶lenie domy¶lnego celu w rekordzie MX."
+msgstr "Określenie domyślnego celu w rekordzie MX."
 
-#: option.c:292
+#: option.c:385
 msgid "Specify time-to-live in seconds for replies from /etc/hosts."
-msgstr "Okre¶lenie (w sekundach) czasu wa¿no¶ci odpowiedzi udzielonych na podstawie /etc/hosts (domy¶lnie 0)."
+msgstr "Określenie (w sekundach) czasu ważności odpowiedzi udzielonych na podstawie /etc/hosts (domyślnie 0)."
 
-#: option.c:293
+#: option.c:386
 msgid "Specify time-to-live in seconds for negative caching."
-msgstr "Okre¶lenie (w sekundach) czasu wa¿no¶ci negatywnych odpowiedzi."
+msgstr "Określenie (w sekundach) czasu ważności negatywnych odpowiedzi."
 
-#: option.c:294
+#: option.c:387
 msgid "Specify time-to-live in seconds for maximum TTL to send to clients."
-msgstr "Ograniczenie maksymalnego czasu wa¿no¶ci odpowiedzi (TTL) podawanego klientom [w sekundach]."
+msgstr "Ograniczenie maksymalnego czasu ważności odpowiedzi (TTL) podawanego klientom [w sekundach]."
+
+#: option.c:388
+msgid "Specify time-to-live ceiling for cache."
+msgstr "Określenie górnej granicy czasu ważności dla wpisów w pamięci podręcznej."
+
+#: option.c:389
+msgid "Specify time-to-live floor for cache."
+msgstr "Określenie dolnej granicy czasu ważności dla wpisów w pamięci podręcznej."
 
-#: option.c:295
+#: option.c:390
 #, c-format
 msgid "Change to this user after startup. (defaults to %s)."
-msgstr "Zmiana u¿ytkownika procesu na wskazanego (po uruchomieniu, domy¶lnie: %s)."
+msgstr "Zmiana użytkownika procesu na wskazanego (po uruchomieniu, domyślnie: %s)."
 
-#: option.c:296
+#: option.c:391
 msgid "Map DHCP vendor class to tag."
-msgstr "Przyporz±dkowanie znacznika w zale¿no¶ci od typu klienta DHCP."
+msgstr "Przyporządkowanie znacznika w zależności od typu klienta DHCP."
 
-#: option.c:297
+#: option.c:392
 msgid "Display dnsmasq version and copyright information."
 msgstr "Wydrukowanie informacji o programie i ochronie praw autorskich."
 
-#: option.c:298
+#: option.c:393
 msgid "Translate IPv4 addresses from upstream servers."
-msgstr "T³umaczenie adresów IPv4 z serwerów nadrzêdnych."
+msgstr "Tłumaczenie adresów IPv4 z serwerów nadrzędnych."
 
-#: option.c:299
+#: option.c:394
 msgid "Specify a SRV record."
-msgstr "Okrelenie rekordu SRV."
+msgstr "Określenie rekordu SRV."
 
-#: option.c:300
+#: option.c:395
 msgid "Display this message. Use --help dhcp for known DHCP options."
-msgstr "Wy¶wietla ten komunikat. U¿yj '--help dhcp' chc±c przejrzeæ listê opcji DHCP (dhcp-option=xxx,...)."
+msgstr "Wyświetla ten komunikat. Użyj '--help dhcp' chcąc przejrzeć listę opcji DHCP (dhcp-option=xxx,...)."
 
-#: option.c:301
+#: option.c:396
 #, c-format
 msgid "Specify path of PID file (defaults to %s)."
-msgstr "Okre¶lenie ¶cie¿ki do pliku PID (domy¶lnie: %s)."
+msgstr "Określenie ścieżki do pliku PID (domyślnie: %s)."
 
-#: option.c:302
+#: option.c:397
 #, c-format
 msgid "Specify maximum number of DHCP leases (defaults to %s)."
-msgstr "Maksymalna liczba dzier¿aw DHCP (domy¶lnie: %s)."
+msgstr "Maksymalna liczba dzierżaw DHCP (domyślnie: %s)."
 
-#: option.c:303
+#: option.c:398
 msgid "Answer DNS queries based on the interface a query was sent to."
-msgstr "Uzale¿nienie odpowiedzi DNS od interfejsu, na którym odebrano zapytanie (wygodne dla serwerów kilku podsieci z ró¿nymi adresami w /etc/hosts)."
+msgstr "Uzależnienie odpowiedzi DNS od interfejsu, na którym odebrano zapytanie (wygodne dla serwerów kilku podsieci z różnymi adresami w /etc/hosts)."
 
-#: option.c:304
+#: option.c:399
 msgid "Specify TXT DNS record."
 msgstr "Specyfikacja rekordu DNS TXT."
 
-#: option.c:305
+#: option.c:400
 msgid "Specify PTR DNS record."
 msgstr "Specyfikacja rekordu DNS PTR."
 
-#: option.c:306
+#: option.c:401
 msgid "Give DNS name to IPv4 address of interface."
-msgstr "Zwraca nazwê domenow± powi±zan± z adresem interfejsu sieciowego."
+msgstr "Zwraca nazwę domenową powiązaną z adresem interfejsu sieciowego."
 
-#: option.c:307
+#: option.c:402
 msgid "Bind only to interfaces in use."
-msgstr "Nas³uchiwanie tylko na wykorzystywanych interfejsach (umo¿liwia uruchomienie osobnych serwerów dla ró¿nych kart)."
+msgstr "Nasłuchiwanie tylko na wykorzystywanych interfejsach (umożliwia uruchomienie osobnych serwerów dla różnych kart)."
 
-#: option.c:308
+#: option.c:403
 #, c-format
 msgid "Read DHCP static host information from %s."
-msgstr "Wczytanie przyporz±dkowañ adresów z %s."
+msgstr "Wczytanie przyporządkowań adresów z %s."
 
-#: option.c:309
+#: option.c:404
 msgid "Enable the DBus interface for setting upstream servers, etc."
-msgstr "W³±czenie u¿ywania interfejsu DBus do informowania o zmianach konfiguracji."
+msgstr "Włączenie używania interfejsu DBus do informowania o zmianach konfiguracji."
 
-#: option.c:310
+#: option.c:405
 msgid "Do not provide DHCP on this interface, only provide DNS."
-msgstr "Uruchomienie na wskazanym interfejsie tylko DNS-a, bez us³ug DHCP i TFTP."
+msgstr "Uruchomienie na wskazanym interfejsie tylko DNS-a, bez usług DHCP i TFTP."
 
-#: option.c:311
+#: option.c:406
 msgid "Enable dynamic address allocation for bootp."
-msgstr "W³±czenie dynamicznego przydzielania adresów dla klientów BOOTP."
+msgstr "Włączenie dynamicznego przydzielania adresów dla klientów BOOTP."
 
-#: option.c:312
+#: option.c:407
 msgid "Map MAC address (with wildcards) to option set."
-msgstr "Przyporz±dkowanie znacznika w zale¿no¶ci od adresu MAC (mo¿na u¿ywaæ uogólnieñ: *)."
+msgstr "Przyporządkowanie znacznika w zależności od adresu MAC (można używać uogólnień: *)."
 
-#: option.c:313
+#: option.c:408
 msgid "Treat DHCP requests on aliases as arriving from interface."
-msgstr "Traktowanie ¿±dañ DHCP odebranych na interfejsach alias, ..., jako odebranych na iface."
+msgstr "Traktowanie żądań DHCP odebranych na interfejsach alias, ..., jako odebranych na iface."
 
-#: option.c:314
+#: option.c:409
 msgid "Disable ICMP echo address checking in the DHCP server."
-msgstr "Pominiêcie sprawdzania za pomoc± ICMP niezajêto¶ci adresu przed jego wydzier¿awieniem."
+msgstr "Pominięcie sprawdzania za pomocą ICMP niezajętości adresu przed jego wydzierżawieniem."
+
+#: option.c:410
+msgid "Shell script to run on DHCP lease creation and destruction."
+msgstr "Skrypt powłoki uruchamiany po przyznaniu lub zwolnieniu adresu."
+
+#: option.c:411
+msgid "Lua script to run on DHCP lease creation and destruction."
+msgstr "Skrypt Lua uruchamiany po przyznaniu lub zwolnieniu adresu."
 
-#: option.c:315
-msgid "Script to run on DHCP lease creation and destruction."
-msgstr "Wskazanie skryptu uruchamianego w przypadku wydzier¿awienia adresu lub wyga¶niêcia dzier¿awy."
+#: option.c:412
+msgid "Run lease-change scripts as this user."
+msgstr "Wskazanie użytkownika z którego uprawnieniami będą uruchamiane skrypty."
 
-#: option.c:316
+#: option.c:413
 msgid "Read configuration from all the files in this directory."
-msgstr "Wczytanie wszystkich plików ze wskazanego katalogu jako konfiguracyjnych."
+msgstr "Wczytanie wszystkich plików ze wskazanego katalogu jako konfiguracyjnych."
 
-#: option.c:317
+#: option.c:414
 msgid "Log to this syslog facility or file. (defaults to DAEMON)"
-msgstr "Wskazanie kana³u syslog-a do którego maj± trafiaæ komunikaty (domy¶lnie: DAEMON)"
+msgstr "Wskazanie kanału syslog-a do którego mają trafiać komunikaty (domyślnie: DAEMON)"
 
-#: option.c:318
+#: option.c:415
 msgid "Do not use leasefile."
-msgstr "Nieu¿ywanie bazy dzier¿aw."
+msgstr "Nieużywanie bazy dzierżaw."
 
-#: option.c:319
+#: option.c:416
 #, c-format
 msgid "Maximum number of concurrent DNS queries. (defaults to %s)"
-msgstr "Maksymalna liczba jednocze¶nie obs³ugiwanych zapytañ DNS (domy¶lnie: %s)"
+msgstr "Maksymalna liczba jednocześnie obsługiwanych zapytań DNS (domyślnie: %s)"
 
-#: option.c:320
+#: option.c:417
 #, c-format
 msgid "Clear DNS cache when reloading %s."
-msgstr "Czyszczenie pamiêci podrêcznej serwera nazw w przypadku ponownego odczytu %s."
+msgstr "Czyszczenie pamięci podręcznej serwera nazw w przypadku ponownego odczytu %s."
 
-#: option.c:321
+#: option.c:418
 msgid "Ignore hostnames provided by DHCP clients."
-msgstr "Nie zwracanie uwagi na nazwê podawan± przez klienta w przypadku dopasowania wszystkich wymienionych znaczników."
+msgstr "Nie zwracanie uwagi na nazwę podawaną przez klienta w przypadku dopasowania wszystkich wymienionych znaczników."
 
-#: option.c:322
+#: option.c:419
 msgid "Do NOT reuse filename and server fields for extra DHCP options."
-msgstr "Wy³±czenie oszczêdzania miejsca w pakiecie DHCP przez przesuwanie pól servername i filename do opcji DHCP. Wymusza prostszy tryb budowy pakietu rozwi±zuj±c problemy z nieprzystosowanymi klientami DHCP."
+msgstr "Wyłączenie oszczędzania miejsca w pakiecie DHCP przez przesuwanie pól servername i filename do opcji DHCP. Wymusza prostszy tryb budowy pakietu rozwiązując problemy z nieprzystosowanymi klientami DHCP."
 
-#: option.c:323
+#: option.c:420
 msgid "Enable integrated read-only TFTP server."
-msgstr "W³±czenie wbudowanego serwera TFTP (tylko do wysy³ania)."
+msgstr "Włączenie wbudowanego serwera TFTP (tylko do wysyłania)."
 
-#: option.c:324
+#: option.c:421
 msgid "Export files by TFTP only from the specified subtree."
-msgstr "Ograniczenie dzia³ania serwera TFTP do wskazanego katalogu i podkatalogów. Nazwy z .. s± odrzucane, / odnosi siê do wskazanego katalogu."
+msgstr "Ograniczenie działania serwera TFTP do wskazanego katalogu i podkatalogów. Nazwy z .. są odrzucane, / odnosi się do wskazanego katalogu."
 
-#: option.c:325
+#: option.c:422
 msgid "Add client IP address to tftp-root."
-msgstr "Doklejanie adresu IP klienta do g³ównego katalogu TFTP. Je¿eli wynikowy katalog nie istnieje, nadal wykorzystuje siê tftp-root."
+msgstr "Doklejanie adresu IP klienta do głównego katalogu TFTP. Jeżeli wynikowy katalog nie istnieje, nadal wykorzystuje się tftp-root."
 
-#: option.c:326
+#: option.c:423
 msgid "Allow access only to files owned by the user running dnsmasq."
-msgstr "Ograniczenie dostêpu do plików przez TFTP do tych, których w³a¶cicielem jest u¿ytkownik uruchamiaj±cy dnsmasq-a."
+msgstr "Ograniczenie dostępu do plików przez TFTP do tych, których właścicielem jest użytkownik uruchamiający dnsmasq-a."
 
-#: option.c:327
+#: option.c:424
+msgid "Do not terminate the service if TFTP directories are inaccessible."
+msgstr "Nieprzerywanie działania serwisu mimo braku dostępu do katalogów TFTP."
+
+#: option.c:425
 #, c-format
 msgid "Maximum number of conncurrent TFTP transfers (defaults to %s)."
-msgstr "Maksymalna liczba jednocze¶nie obs³ugiwanych po³±czeñ TFTP (domy¶lnie %s)."
+msgstr "Maksymalna liczba jednocześnie obsługiwanych połączeń TFTP (domyślnie %s)."
 
-#: option.c:328
+#: option.c:426
 msgid "Disable the TFTP blocksize extension."
-msgstr "Wy³±czenie mo¿liwo¶ci negocjowania wielko¶ci bloku dla przesy³ów przez TFTP."
+msgstr "Wyłączenie możliwości negocjowania wielkości bloku dla przesyłów przez TFTP."
+
+#: option.c:427
+msgid "Convert TFTP filenames to lowercase"
+msgstr "Konwertowanie nazw plików żądanych przez TFTP do małych liter"
 
-#: option.c:329
+#: option.c:428
 msgid "Ephemeral port range for use by TFTP transfers."
-msgstr "Wskazanie zakresu portów do u¿ytku TFTP."
+msgstr "Wskazanie zakresu portów do użytku TFTP."
 
-#: option.c:330
+#: option.c:429
 msgid "Extra logging for DHCP."
-msgstr "W³±czenie spisywania w logu operacji DHCP."
+msgstr "Włączenie spisywania w logu operacji DHCP."
 
-#: option.c:331
+#: option.c:430
 msgid "Enable async. logging; optionally set queue length."
-msgstr "W³±czenie asynchronicznego zapisywania do logu z ewentualnym wskazaniem d³ugo¶ci kolejki."
+msgstr "Włączenie asynchronicznego zapisywania do logu z ewentualnym wskazaniem długości kolejki."
 
-#: option.c:332
+#: option.c:431
 msgid "Stop DNS rebinding. Filter private IP ranges when resolving."
-msgstr "Odfiltrowywanie adresów wskazuj±cych na komputery w sieciach wewnêtrznych spo¶ród odpowiedzi od zewnêtrznych serwerów DNS."
+msgstr "Odfiltrowywanie adresów wskazujących na komputery w sieciach wewnętrznych spośród odpowiedzi od zewnętrznych serwerów DNS."
 
-#: option.c:333
+#: option.c:432
 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers."
-msgstr "Zezwolenie na przekazywanie odpowiedzi w klasie 127.0.0.0/8. Dla serwerów RBL."
+msgstr "Zezwolenie na przekazywanie odpowiedzi w klasie 127.0.0.0/8. Dla serwerów RBL."
 
-#: option.c:334
+#: option.c:433
 msgid "Inhibit DNS-rebind protection on this domain."
 msgstr "Dezaktywacja zabezpieczenia przed atakami DNS-rebind dla wskazanych domen."
 
-#: option.c:335
+#: option.c:434
 msgid "Always perform DNS queries to all servers."
-msgstr "Jednoczesne odpytywanie wszystkich serwerów nadrzêdnych; klientowi przekazywana jest pierwsza odpowied¼."
+msgstr "Jednoczesne odpytywanie wszystkich serwerów nadrzędnych; klientowi przekazywana jest pierwsza odpowiedź."
 
-#: option.c:336
+#: option.c:435
 msgid "Set tag if client includes matching option in request."
-msgstr "Ustawienie znacznika je¿eli w ¿±daniu DHCP pojawi siê wskazana opcja, ewentualnie o konkretnej warto¶ci."
+msgstr "Ustawienie znacznika jeżeli w żądaniu DHCP pojawi się wskazana opcja, ewentualnie o konkretnej wartości."
 
-#: option.c:337
+#: option.c:436
 msgid "Use alternative ports for DHCP."
-msgstr "U¿ycie alternatywnych portów dla us³ugi DHCP."
+msgstr "Użycie alternatywnych portów dla usługi DHCP."
 
-#: option.c:338
-msgid "Run lease-change script as this user."
-msgstr "Uruchamianie skryptu dhcp-script jako wskazany u¿ytkownik."
-
-#: option.c:339
+#: option.c:437
 msgid "Specify NAPTR DNS record."
 msgstr "Specyfikacja rekordu DNS NAPTR."
 
-#: option.c:340
+#: option.c:438
 msgid "Specify lowest port available for DNS query transmission."
-msgstr "Ustawienie dolnej granicy numerów portów do przesy³ania zapytañ DNS."
+msgstr "Ustawienie dolnej granicy numerów portów do przesyłania zapytań DNS."
 
-#: option.c:341
+#: option.c:439
 msgid "Use only fully qualified domain names for DHCP clients."
-msgstr "Przechowywanie w serwerze DNS dnsmasq-a tylko w pe³ni kwalifikowanych nazw zg³aszanych przez klientów DHCP."
+msgstr "Przechowywanie w serwerze DNS dnsmasq-a tylko w pełni kwalifikowanych nazw zgłaszanych przez klientów DHCP."
 
-#: option.c:342
+#: option.c:440
 msgid "Generate hostnames based on MAC address for nameless clients."
-msgstr "Generowanie nazw na podstawie MAC-adresów dla klientów bez nazwy."
+msgstr "Generowanie nazw na podstawie MAC-adresów dla klientów bez nazwy."
 
-#: option.c:343
+#: option.c:441
 msgid "Use these DHCP relays as full proxies."
-msgstr "Traktowanie wskazanych serwerów po¶rednicz±cych DHCP jako dzia³aj±cych w trybie \"pe³nomocnika\" (full-proxy)."
+msgstr "Traktowanie wskazanych serwerów pośredniczących DHCP jako działających w trybie \"pełnomocnika\" (full-proxy)."
 
-#: option.c:344
+#: option.c:442
+msgid "Relay DHCP requests to a remote server"
+msgstr "Przekazywanie żądań DHCP do zdalnego serwera"
+
+#: option.c:443
 msgid "Specify alias name for LOCAL DNS name."
 msgstr "Wskazanie synonimu nazwy komputera lokalnego - znanego z /etc/hosts albo z DHCP."
 
-#: option.c:345
+#: option.c:444
 msgid "Prompt to send to PXE clients."
-msgstr "Zg³oszenie wysy³ane klientom PXE."
+msgstr "Zgłoszenie wysyłane klientom PXE."
 
-#: option.c:346
+#: option.c:445
 msgid "Boot service for PXE menu."
-msgstr "Sk³adnik menu PXE (--> man)."
+msgstr "Składnik menu PXE (--> man)."
 
-#: option.c:347
+#: option.c:446
 msgid "Check configuration syntax."
-msgstr "Sprawdzenie sk³adni."
+msgstr "Sprawdzenie składni."
 
-#: option.c:348
-msgid "Add requestor's MAC address to forwarded DNS queries"
-msgstr "Za³±czanie MAC-adresu komputera pytaj±cego w przekazywanych dalej zapytaniach DNS."
+#: option.c:447
+msgid "Add requestor's MAC address to forwarded DNS queries."
+msgstr "Przekazywanie MAC-adresu komputera pytającego w ruchu wychodzącym DNS."
 
-#: option.c:349
-msgid "Proxy DNSSEC validation results from upstream nameservers"
-msgstr "Przekazywanie wyników weryfikacji DNSSEC z serwerów nadrzêdnych."
+#: option.c:448
+msgid "Add requestor's IP subnet to forwarded DNS queries."
+msgstr "Zamieszczanie adresu IP pytającego w przekazywanych zapytaniach DNS."
+
+#: option.c:449
+msgid "Proxy DNSSEC validation results from upstream nameservers."
+msgstr "Przekazywanie wyników weryfikacji DNSSEC z serwerów nadrzędnych."
+
+#: option.c:450
+msgid "Attempt to allocate sequential IP addresses to DHCP clients."
+msgstr "Zmiana sposobu przydzielania adresów IP na sekwencyjny."
+
+#: option.c:451
+msgid "Copy connection-track mark from queries to upstream connections."
+msgstr "Zachowanie znacznika połączenia z odebranego zapytania DNS w ruchu zewnętrznym."
+
+#: option.c:452
+msgid "Allow DHCP clients to do their own DDNS updates."
+msgstr "Zezwolenie klientom DHCP na uaktualnianie DDNS-ów."
+
+#: option.c:453
+msgid "Send router-advertisements for interfaces doing DHCPv6"
+msgstr "Załączenie anonsowania (RA) na interfejsach serwujących DHCPv6"
+
+#: option.c:454
+msgid "Specify DUID_EN-type DHCPv6 server DUID"
+msgstr "Określenie DHCPv6 DUID"
+
+#: option.c:455
+msgid "Specify host (A/AAAA and PTR) records"
+msgstr "Określenie rekordów A/AAAA i PTR"
+
+#: option.c:456
+msgid "Specify arbitrary DNS resource record"
+msgstr "Określenie rekordu TXT"
+
+#: option.c:457
+msgid "Bind to interfaces in use - check for new interfaces"
+msgstr "Dynamiczne podpinanie do interfejsów sieciowych"
+
+#: option.c:458
+msgid "Export local names to global DNS"
+msgstr "Eksportowanie lokalnych nazw hostów do globalnego DNS-a"
+
+#: option.c:459
+msgid "Domain to export to global DNS"
+msgstr "Domena pod którą będą eksportowane lokalne nazwy"
 
-#: option.c:638
+#: option.c:460
+msgid "Set TTL for authoritative replies"
+msgstr "Określenie TTL dla odpowiedzi autorytatywnych"
+
+#: option.c:461
+msgid "Set authoritive zone information"
+msgstr "Określenie danych strefy autorytatywnej (SOA)"
+
+#: option.c:462
+msgid "Secondary authoritative nameservers for forward domains"
+msgstr "Pomocnicze serwery autorytatywne dla forwardowanych domen"
+
+#: option.c:463
+msgid "Peers which are allowed to do zone transfer"
+msgstr "Wskazanie serwerów uprawnionych do transferu stref"
+
+#: option.c:464
+msgid "Specify ipsets to which matching domains should be added"
+msgstr "Wyszczególnienie ipset-ów, do których będą dopisywane adresy IP leżące we wskazanych domenach"
+
+#: option.c:465
+msgid "Specify a domain and address range for synthesised names"
+msgstr "Wskazanie domeny i zakresu adresów dla generowanych nazw"
+
+#: option.c:466
+msgid "Activate DNSSEC validation"
+msgstr "Uaktywnienie walidacji DNSSEC"
+
+#: option.c:467
+msgid "Specify trust anchor key digest."
+msgstr "Wskazanie punktu zaufania dla uwierzytelniania DNSSEC."
+
+#: option.c:468
+msgid "Disable upstream checking for DNSSEC debugging."
+msgstr "Akceptowanie nieuwiarygodnionych odpowiedzi DNSSEC (ustawienie bitu CD w zapytaniach)."
+
+#: option.c:469
+msgid "Ensure answers without DNSSEC are in unsigned zones."
+msgstr "Upewnianie się, że odpowiedzi bez DNSSEC pochodzą ze stref niepodpisanych."
+
+#: option.c:470
+msgid "Don't check DNSSEC signature timestamps until first cache-reload"
+msgstr "Wyłączenie sprawdzania sygnatur czasowych DNSSEC do pierwszego przeładowania pamięci podręcznej."
+
+#: option.c:471
+msgid "Timestamp file to verify system clock for DNSSEC"
+msgstr "Plik znacznika czasu do weryfikacji zegara systemowego dla potrzeb DNSSEC."
+
+#: option.c:473
+msgid "Specify DHCPv6 prefix class"
+msgstr "Określenie prefiksu klasy DHCPv6"
+
+#: option.c:475
+msgid "Set priority, resend-interval and router-lifetime"
+msgstr "Ustawianie priorytetu, okresu rozsyłania oraz czasu życia rutera (RA)."
+
+#: option.c:476
+msgid "Do not log routine DHCP."
+msgstr "Wyłączenie logowania zwyczajnego DHCP."
+
+#: option.c:477
+msgid "Do not log routine DHCPv6."
+msgstr "Wyłączenie logowania zwyczajnego DHCPv6."
+
+#: option.c:478
+msgid "Do not log RA."
+msgstr "Wyłączenie logowania RA."
+
+#: option.c:479
+msgid "Accept queries only from directly-connected networks"
+msgstr "Akceptowanie zapytań wyłącznie z sieci podpiętych bezpośrednio."
+
+#: option.c:480
+msgid "Detect and remove DNS forwarding loops"
+msgstr "Wykrywanie i usuwanie pętli zapytań DNS."
+
+#: option.c:481
+msgid "Ignore DNS responses containing ipaddr."
+msgstr "Ignorowanie odpowiedzi DNS zawierających ipaddr."
+
+#: option.c:683
 #, c-format
 msgid ""
 "Usage: dnsmasq [options]\n"
 "\n"
 msgstr ""
-"U¿ycie: dnsmasq [opcje]\n"
+"Użycie: dnsmasq [opcje]\n"
 "\n"
 
-#: option.c:640
+#: option.c:685
 #, c-format
 msgid "Use short options only on the command line.\n"
-msgstr "W tym systemie w linii poleceñ mo¿na u¿ywaæ wy³±cznie jednoliterowych opcji.\n"
+msgstr "W tym systemie w linii poleceń można używać wyłącznie jednoliterowych opcji.\n"
 
-#: option.c:642
+#: option.c:687
 #, c-format
 msgid "Valid options are:\n"
-msgstr "Dostêpne opcje:\n"
+msgstr "Dostępne opcje:\n"
 
-#: option.c:683
-#, c-format
-msgid "Known DHCP options:\n"
-msgstr "Znane opcje DHCP:\n"
+#: option.c:744 option.c:748
+msgid "bad port"
+msgstr "nieprawidłowy numer portu"
+
+#: option.c:775 option.c:807
+msgid "interface binding not supported"
+msgstr "nie ma możliwości dowiązywania do interfejsu"
 
-#: option.c:798
+#: option.c:784 option.c:3575
+msgid "bad interface name"
+msgstr "nieprawidłowa nazwa interfejsu"
+
+#: option.c:814
+msgid "bad address"
+msgstr "zły adres"
+
+#: option.c:996
+msgid "unsupported encapsulation for IPv6 option"
+msgstr "nieobsługiwany rodzaj enkapsulacji opcji IPv6"
+
+#: option.c:1010
 msgid "bad dhcp-option"
-msgstr "b³±d w dhcp-option"
+msgstr "błąd w dhcp-option"
 
-#: option.c:860
+#: option.c:1078
 msgid "bad IP address"
-msgstr "z³y adres IP"
+msgstr "zły adres IP"
+
+#: option.c:1081 option.c:1219 option.c:2893
+msgid "bad IPv6 address"
+msgstr "zły adres IPv6"
 
-#: option.c:968
+#: option.c:1246 option.c:1340
 msgid "bad domain in dhcp-option"
-msgstr "nieprawid³owa nazwa domeny w dhcp-option"
+msgstr "nieprawidłowa nazwa domeny w dhcp-option"
 
-#: option.c:1034
+#: option.c:1378
 msgid "dhcp-option too long"
-msgstr "zbyt d³uga dhcp-option (>255 znaków)"
+msgstr "zbyt długa dhcp-option (>255 znaków)"
 
-#: option.c:1043
+#: option.c:1385
 msgid "illegal dhcp-match"
 msgstr "niedopuszczalne dhcp-match"
 
-#: option.c:1087
+#: option.c:1447
 msgid "illegal repeated flag"
-msgstr "wielokrotne u¿ycie opcji niedozwolone (pojawi³a siê wcze¶niej w linii poleceñ)"
+msgstr "wielokrotne użycie opcji niedozwolone (pojawiła się wcześniej w linii poleceń)"
 
-#: option.c:1095
+#: option.c:1455
 msgid "illegal repeated keyword"
-msgstr "wielokrotne u¿ycie opcji niedozwolone (pojawi³a siê wsze¶niej w pliku konfiguracyjnym)"
+msgstr "wielokrotne użycie opcji niedozwolone (pojawiła się wsześniej w pliku konfiguracyjnym)"
 
-#: option.c:1147 option.c:3030
+#: option.c:1520 option.c:4191
 #, c-format
 msgid "cannot access directory %s: %s"
-msgstr "brak dostêpu do katalogu %s: %s"
+msgstr "brak dostępu do katalogu %s: %s"
 
-#: option.c:1178 tftp.c:460
+#: option.c:1566 tftp.c:493
 #, c-format
 msgid "cannot access %s: %s"
-msgstr "brak dostêpu do %s: %s"
+msgstr "brak dostępu do %s: %s"
 
-#: option.c:1207
+#: option.c:1618
 msgid "setting log facility is not possible under Android"
-msgstr ""
+msgstr "zmiana log-facility w systemie Android nie jest możliwa"
 
-#: option.c:1216
+#: option.c:1627
 msgid "bad log facility"
-msgstr ""
+msgstr "nierozpoznany znacznik logów"
 
-#: option.c:1265
+#: option.c:1680
 msgid "bad MX preference"
-msgstr "nieprawid³owa warto¶æ preferencji MX"
+msgstr "nieprawidłowa wartość preferencji MX"
 
-#: option.c:1270
+#: option.c:1685
 msgid "bad MX name"
-msgstr "nieprawid³owa nazwa MX"
+msgstr "nieprawidłowa nazwa MX"
 
-#: option.c:1284
+#: option.c:1699
 msgid "bad MX target"
-msgstr "nieprawid³owa warto¶æ celu MX"
+msgstr "nieprawidłowa wartość celu MX"
 
-#: option.c:1294
+#: option.c:1711
 msgid "cannot run scripts under uClinux"
-msgstr "w uClinuksie nie ma mo¿liwo¶ci uruchamiania skryptów"
+msgstr "w uClinuksie nie ma możliwości uruchamiania skryptów"
 
-#: option.c:1296
+#: option.c:1713
 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts"
-msgstr "¿eby mieæ mo¿liwo¶æ u¿ywania skryptów wywo³ywanych przy zmianie dzier¿awy, przekompiluj dnsmasq-a z w³±czon± flag± HAVE_SCRIPT"
+msgstr "żeby mieć możliwość używania skryptów wywoływanych przy zmianie dzierżawy, przekompiluj dnsmasq-a z włączoną flagą HAVE_SCRIPT"
 
-#: option.c:1597 option.c:1601
-msgid "bad port"
-msgstr "nieprawid³owy numer portu"
+#: option.c:1717
+msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts"
+msgstr "używanie skryptów Lua, wymaga skompilowania dnsmasq-a z flagą HAVE_LUASCRIPT"
 
-#: option.c:1620 option.c:1645
-msgid "interface binding not supported"
-msgstr "nie ma mo¿liwo¶ci dowi±zywania do interfejsu"
+#: option.c:1973 option.c:2018 option.c:2074
+msgid "bad prefix"
+msgstr "zła maska"
+
+#: option.c:2355
+msgid "recompile with HAVE_IPSET defined to enable ipset directives"
+msgstr "chcąc korzystać z ipsets przekompiluj dnsmasq-a z HAVE_IPSET"
 
-#: option.c:1791
+#: option.c:2548
 msgid "bad port range"
-msgstr "nieprawid³owy zakres numerów portów"
+msgstr "nieprawidłowy zakres numerów portów"
 
-#: option.c:1808
+#: option.c:2564
 msgid "bad bridge-interface"
-msgstr "nieprawid³owa nazwa urz±dzenia w bridge-interface"
+msgstr "nieprawidłowa nazwa urządzenia w bridge-interface"
 
-#: option.c:1850
-msgid "bad dhcp-range"
-msgstr "nieprawid³owy zakres dhcp-range"
-
-#: option.c:1878
+#: option.c:2624
 msgid "only one tag allowed"
-msgstr "mo¿na wskazaæ tylko jeden znacznik sieci"
+msgstr "można wskazać tylko jeden znacznik sieci"
+
+#: option.c:2644 option.c:2656 option.c:2764 option.c:2805
+msgid "bad dhcp-range"
+msgstr "nieprawidłowy zakres dhcp-range"
 
-#: option.c:1925
+#: option.c:2671
 msgid "inconsistent DHCP range"
-msgstr "niespójny zakres adresów DHCP"
+msgstr "niespójny zakres adresów DHCP"
+
+#: option.c:2732
+msgid "prefix length must be exactly 64 for RA subnets"
+msgstr "długość prefiksu musi wynosić dokładnie 64 dla podsieci RA"
+
+#: option.c:2734
+msgid "prefix length must be exactly 64 for subnet constructors"
+msgstr "długość prefiksu musi wynosić dokładnie 64 dla konstruktorów podsieci"
 
-#: option.c:2019 option.c:2045
+#: option.c:2738
+msgid "prefix length must be at least 64"
+msgstr "długość prefiksu musi wynosić co najmniej 64"
+
+#: option.c:2741
+msgid "inconsistent DHCPv6 range"
+msgstr "niespójny zakres adresów DHCPv6"
+
+#: option.c:2752
+msgid "prefix must be zero with \"constructor:\" argument"
+msgstr "prefiks musi wynosić zero z argumentem \"constructor:\""
+
+#: option.c:2863 option.c:2911
 msgid "bad hex constant"
 msgstr "zapis niezgodny z formatem szesnastkowym"
 
-#: option.c:2107
+#: option.c:2885
+msgid "cannot match tags in --dhcp-host"
+msgstr "--dhcp-host nie dopuszcza dopasowywania na podstawie znaczników"
+
+#: option.c:2933
+#, c-format
+msgid "duplicate dhcp-host IP address %s"
+msgstr "powtórzony adres IP %s w specyfikacji dhcp-host"
+
+#: option.c:2991
 msgid "bad DHCP host name"
 msgstr "niedopuszczalna nazwa komputera w dhcp-host"
 
-#: option.c:2188
+#: option.c:3073
 msgid "bad tag-if"
-msgstr "nieprawid³owa sk³adnia 'tag-if'"
+msgstr "nieprawidłowa składnia 'tag-if'"
 
-#: option.c:2467 option.c:2752
+#: option.c:3397 option.c:3791
 msgid "invalid port number"
-msgstr "nieprawid³owy numer portu"
+msgstr "nieprawidłowy numer portu"
 
-#: option.c:2529
+#: option.c:3459
 msgid "bad dhcp-proxy address"
-msgstr "z³y adres dhcp-proxy"
+msgstr "zły adres dhcp-proxy"
 
-#: option.c:2569
-msgid "invalid alias range"
-msgstr "nieprawid³owy zakres adresów w --alias"
+#: option.c:3485
+msgid "Bad dhcp-relay"
+msgstr "zły dhcp-relay"
 
-#: option.c:2582
-msgid "bad interface name"
-msgstr "nieprawid³owa nazwa interfejsu"
+#: option.c:3511
+msgid "bad RA-params"
+msgstr "nieprawidłowe argumenty RA"
+
+#: option.c:3520
+msgid "bad DUID"
+msgstr "zły DUID"
+
+#: option.c:3562
+msgid "invalid alias range"
+msgstr "nieprawidłowy zakres adresów w --alias"
 
-#: option.c:2607
+#: option.c:3616
 msgid "bad CNAME"
-msgstr "z³a CNAME"
+msgstr "zła CNAME"
 
-#: option.c:2612
+#: option.c:3621
 msgid "duplicate CNAME"
-msgstr "powtórzona CNAME"
+msgstr "powtórzona CNAME"
 
-#: option.c:2632
+#: option.c:3641
 msgid "bad PTR record"
-msgstr "nieprawid³owy zapis rekordu PTR"
+msgstr "nieprawidłowy zapis rekordu PTR"
 
-#: option.c:2663
+#: option.c:3672
 msgid "bad NAPTR record"
-msgstr "nieprawid³owy zapis rekordu NAPTR"
+msgstr "nieprawidłowy zapis rekordu NAPTR"
+
+#: option.c:3706
+msgid "bad RR record"
+msgstr "nieprawidłowy zapis rekordu RR"
 
-#: option.c:2695
+#: option.c:3736
 msgid "bad TXT record"
-msgstr "nieprawid³owy zapis rekordu TXT"
+msgstr "nieprawidłowy zapis rekordu TXT"
 
-#: option.c:2738
+#: option.c:3777
 msgid "bad SRV record"
-msgstr "nieprawid³owy zapis rekordu SRV"
+msgstr "nieprawidłowy zapis rekordu SRV"
 
-#: option.c:2745
+#: option.c:3784
 msgid "bad SRV target"
-msgstr "nieprawid³owa warto¶æ celu SRV"
+msgstr "nieprawidłowa wartość celu SRV"
 
-#: option.c:2759
+#: option.c:3798
 msgid "invalid priority"
-msgstr "nieprawid³owy priorytet"
+msgstr "nieprawidłowy priorytet"
 
-#: option.c:2766
+#: option.c:3805
 msgid "invalid weight"
-msgstr "nieprawid³owa waga"
+msgstr "nieprawidłowa waga"
+
+#: option.c:3829
+msgid "Bad host-record"
+msgstr "nieprawidłowy zapis host-record"
+
+#: option.c:3846
+msgid "Bad name in host-record"
+msgstr "niedopuszczalna nazwa w host-record"
+
+#: option.c:3911
+msgid "bad trust anchor"
+msgstr "nieprawidłowa specyfikacja punktu zaufania"
 
-#: option.c:2785
-msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"
-msgstr "nieobs³ugiwana opcja (sprawd¼, czy obs³uga DHCP/TFTP/DBus zosta³a wkompilowana)"
+#: option.c:3925
+msgid "bad HEX in trust anchor"
+msgstr "zły zapis szesnastkowy"
 
-#: option.c:2849
+#: option.c:3935
+msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"
+msgstr "nieobsługiwana opcja (sprawdź, czy obsługa DHCP/TFTP/DNSSEC/DBus została wkompilowana)"
+
+#: option.c:3994
 msgid "missing \""
 msgstr "brakuje \""
 
-#: option.c:2908
+#: option.c:4051
 msgid "bad option"
-msgstr "nieprawid³owa opcja"
+msgstr "nieprawidłowa opcja"
 
-#: option.c:2910
+#: option.c:4053
 msgid "extraneous parameter"
-msgstr "nadwy¿kowy parametr"
+msgstr "nadwyżkowy parametr"
 
-#: option.c:2912
+#: option.c:4055
 msgid "missing parameter"
 msgstr "brak parametru"
 
-#: option.c:2916
+#: option.c:4057
+msgid "illegal option"
+msgstr "niedopuszczalna opcja"
+
+#: option.c:4064
 msgid "error"
-msgstr "b³±d"
+msgstr "błąd"
 
-#: option.c:2921
+#: option.c:4066
 #, c-format
-msgid "%s at line %d of %%s"
-msgstr "%s w linii %d pliku %%s"
+msgid " at line %d of %s"
+msgstr " w linii %d pliku %s"
 
-#: option.c:2985 tftp.c:624
+#: option.c:4081 option.c:4328 option.c:4364
 #, c-format
-msgid "cannot read %s: %s"
-msgstr "b³±d odczytu z pliku %s: %s"
+msgid "read %s"
+msgstr "przeczytałem %s"
 
-#: option.c:3151 option.c:3187
+#: option.c:4144 option.c:4267 tftp.c:667
 #, c-format
-msgid "read %s"
-msgstr "przeczyta³em %s"
+msgid "cannot read %s: %s"
+msgstr "błąd odczytu z pliku %s: %s"
 
-#: option.c:3239
+#: option.c:4430
 msgid "junk found in command line"
-msgstr "jakie¶ ¶mieci w linii poleceñ"
+msgstr "jakieś śmieci w linii poleceń"
 
-#: option.c:3269
+#: option.c:4465
 #, c-format
 msgid "Dnsmasq version %s  %s\n"
 msgstr "Dnsmasq, wersja %s  %s\n"
 
-#: option.c:3270
+#: option.c:4466
 #, c-format
 msgid ""
-"Compile time options %s\n"
+"Compile time options: %s\n"
 "\n"
 msgstr ""
 "Wkompilowane opcje %s\n"
 "\n"
 
-#: option.c:3271
+#: option.c:4467
 #, c-format
 msgid "This software comes with ABSOLUTELY NO WARRANTY.\n"
-msgstr "Autor nie daje ¯ADNYCH GWARANCJI egzekwowalnych prawnie.\n"
+msgstr "Autor nie daje ŻADNYCH GWARANCJI egzekwowalnych prawnie.\n"
 
-#: option.c:3272
+#: option.c:4468
 #, c-format
 msgid "Dnsmasq is free software, and you are welcome to redistribute it\n"
-msgstr "Dnsmasq jest wolnym oprogramowaniem, mo¿esz go rozprowadzaæ\n"
+msgstr "Dnsmasq jest wolnym oprogramowaniem, możesz go rozprowadzać\n"
 
-#: option.c:3273
+#: option.c:4469
 #, c-format
 msgid "under the terms of the GNU General Public License, version 2 or 3.\n"
-msgstr "na warunkach okrelonych w GNU General Public Licence, w wersji 2 lub 3.\n"
+msgstr "na warunkach określonych w GNU General Public Licence, w wersji 2 lub 3.\n"
 
-#: option.c:3284
+#: option.c:4480
 msgid "try --help"
-msgstr "spróbuj: --help"
+msgstr "spróbuj: --help"
 
-#: option.c:3286
+#: option.c:4482
 msgid "try -w"
-msgstr "spróbuj: -w"
+msgstr "spróbuj: -w"
 
-#: option.c:3289
+#: option.c:4484
 #, c-format
 msgid "bad command line options: %s"
-msgstr "nieprawid³owa opcja w linii poleceñ %s"
+msgstr "nieprawidłowa opcja w linii poleceń %s"
 
-#: option.c:3330
+#: option.c:4541
 #, c-format
 msgid "cannot get host-name: %s"
-msgstr "nie mo¿na pobraæ nazwy hosta: %s"
+msgstr "nie można pobrać nazwy hosta: %s"
 
-#: option.c:3358
+#: option.c:4569
 msgid "only one resolv.conf file allowed in no-poll mode."
-msgstr "w trybie no-poll mo¿na wskazaæ najwy¿ej jeden plik resolv.conf."
+msgstr "w trybie no-poll można wskazać najwyżej jeden plik resolv.conf."
 
-#: option.c:3368
+#: option.c:4579
 msgid "must have exactly one resolv.conf to read domain from."
-msgstr "musisz mieæ dok³adnie jeden plik resolv.conf do odczytu domen."
+msgstr "musisz mieć dokładnie jeden plik resolv.conf do odczytu domen."
 
-#: option.c:3371 network.c:848 dhcp.c:814
+#: option.c:4582 network.c:1507 dhcp.c:777
 #, c-format
 msgid "failed to read %s: %s"
-msgstr "nie uda³o siê odczytaæ %s: %s"
+msgstr "nie udało się odczytać %s: %s"
 
-#: option.c:3388
+#: option.c:4599
 #, c-format
 msgid "no search directive found in %s"
 msgstr "brak wytycznych wyszukiwania w %s"
 
-#: option.c:3409
-#, fuzzy
+#: option.c:4620
 msgid "there must be a default domain when --dhcp-fqdn is set"
-msgstr "w przypadku u¿ywania --dhcp-fqdn trzeba wskazaæ domy¶ln± domenê"
+msgstr "w przypadku używania --dhcp-fqdn trzeba wskazać domyślną domenę"
 
-#: option.c:3413
+#: option.c:4629
 msgid "syntax check OK"
-msgstr "sk³adnia sprawdzona, jest prawid³owa"
+msgstr "składnia sprawdzona, jest prawidłowa"
+
+#: forward.c:111
+#, c-format
+msgid "failed to send packet: %s"
+msgstr "wysyłanie pakietu nie powiodło się: %s"
+
+#: forward.c:591
+msgid "discarding DNS reply: subnet option mismatch"
+msgstr "odrzucam odpowiedź DNS: nie zgadza się specyfikacja podsieci"
 
-#: forward.c:461
+#: forward.c:614
 #, c-format
 msgid "nameserver %s refused to do a recursive query"
 msgstr "serwer nazw %s odmawia wykonania zapytania rekurencyjnego"
 
-#: forward.c:489
+#: forward.c:646
 #, c-format
 msgid "possible DNS-rebind attack detected: %s"
 msgstr "prawdopodobnie wykryto atak DNS-rebind: %s"
 
-#: network.c:171
+#: forward.c:1209 forward.c:1815
+msgid "Ignoring query from non-local network"
+msgstr "Ignorowanie zapytań z sieci pozalokalnych."
+
+#: forward.c:2286
 #, c-format
-msgid "unknown interface %s in bridge-interface"
-msgstr "nieznany interfejs %s w bridge-u"
+msgid "Maximum number of concurrent DNS queries reached (max: %d)"
+msgstr "Osiągnięto graniczną ilość jednocześnie obsługiwanych zapytań DNS (maks: %d)"
 
-#: network.c:380
+#: network.c:715
 #, c-format
 msgid "failed to create listening socket for %s: %s"
-msgstr "nie uda³o siê otworzyæ gniazda %s: %s"
+msgstr "nie udało się otworzyć gniazda %s: %s"
+
+#: network.c:1021
+#, c-format
+msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s"
+msgstr "UWAGA: nasłuchiwanie na %s może przyjmować żądania przychodzące przez interfejsy inne niż %s"
+
+#: network.c:1028
+msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)"
+msgstr "UWAGA: zastosowanie --bind-dynamic zamiast --bind-interfaces daje ochronę przed atakami wzmocnienia DNS"
 
-#: network.c:746
+#: network.c:1037
+#, c-format
+msgid "warning: no addresses found for interface %s"
+msgstr "uwaga: nie znaleziono adresu interfejsu %s"
+
+#: network.c:1095
+#, c-format
+msgid "interface %s failed to join DHCPv6 multicast group: %s"
+msgstr "interfejs %s nie pozwolił się przyłączyć do grupy rozgłoszeniowej DHCPv6: %s"
+
+#: network.c:1289
 #, c-format
 msgid "failed to bind server socket for %s: %s"
-msgstr "b³±d przy przyznawaniu nazwy gniazdu serwera %s: %s"
+msgstr "błąd przy przyznawaniu nazwy gniazdu serwera %s: %s"
 
-#: network.c:783
+#: network.c:1445
 #, c-format
 msgid "ignoring nameserver %s - local interface"
 msgstr "ignorowanie serwera nazw %s - interfejs lokalny"
 
-#: network.c:794
+#: network.c:1456
 #, c-format
 msgid "ignoring nameserver %s - cannot make/bind socket: %s"
-msgstr "ignorowanie serwera nazw %s - nie mo¿na utworzyæ/dowi±zaæ gniazda: %s"
+msgstr "ignorowanie serwera nazw %s - nie można utworzyć/dowiązać gniazda: %s"
 
-#: network.c:811
+#: network.c:1469
 msgid "unqualified"
 msgstr "niekwalifikowane(-a)"
 
-#: network.c:811
+#: network.c:1469
 msgid "names"
 msgstr "nazwy"
 
-#: network.c:813
+#: network.c:1471
 msgid "default"
-msgstr "domylne"
+msgstr "domyślne"
 
-#: network.c:815
+#: network.c:1473
 msgid "domain"
 msgstr "domeny"
 
-#: network.c:818
+#: network.c:1476
 #, c-format
 msgid "using local addresses only for %s %s"
-msgstr "u¿ywam adresów lokalnych tylko dla %s %s"
+msgstr "używam adresów lokalnych tylko dla %s %s"
 
-#: network.c:820
+#: network.c:1478
 #, c-format
 msgid "using standard nameservers for %s %s"
-msgstr "u¿ywam standardowych serwerów nazw dla %s %s"
+msgstr "używam standardowych serwerów nazw dla %s %s"
 
-#: network.c:822
+#: network.c:1480
 #, c-format
 msgid "using nameserver %s#%d for %s %s"
-msgstr "u¿ywam serwera nazw %s#%d dla %s %s"
+msgstr "używam serwera nazw %s#%d dla %s %s"
+
+#: network.c:1484
+#, c-format
+msgid "NOT using nameserver %s#%d - query loop detected"
+msgstr "NIE używam serwera nazw %s#%d - wykryto pętlę zapytań"
 
-#: network.c:825
+#: network.c:1487
 #, c-format
 msgid "using nameserver %s#%d(via %s)"
-msgstr "u¿ywam serwera nazw %s#%d (przez %s)"
+msgstr "używam serwera nazw %s#%d (przez %s)"
 
-#: network.c:827
+#: network.c:1489
 #, c-format
 msgid "using nameserver %s#%d"
-msgstr "u¿ywam serwera nazw %s#%d"
+msgstr "używam serwera nazw %s#%d"
 
-#: dnsmasq.c:148
+#: dnsmasq.c:163
+msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"
+msgstr "dhcp-hostsdir, dhcp-optsdir i hostsdir nie znajdują zastosowania na tej platformie"
+
+#: dnsmasq.c:170
+msgid "no trust anchors provided for DNSSEC"
+msgstr "nie wskazano punktów zaufania dla DNSSEC"
+
+#: dnsmasq.c:173
+msgid "cannot reduce cache size from default when DNSSEC enabled"
+msgstr "brak możliwości zmniejszenia pamięci podręcznej poniżej wielkości domyślnej w przypadku używania DNSSEC"
+
+#: dnsmasq.c:175
+msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h"
+msgstr "obsługa DNSSEC niedostępna - ustaw HAVE_DNSSEC w src/config.h"
+
+#: dnsmasq.c:181
 msgid "TFTP server not available: set HAVE_TFTP in src/config.h"
-msgstr "Serwer TFTP nie zosta³ wkompilowany -- ustaw HAVE_TFTP w src/config.h"
+msgstr "Serwer TFTP nie został wkompilowany -- ustaw HAVE_TFTP w src/config.h"
+
+#: dnsmasq.c:186
+msgid "cannot use --conntrack AND --query-port"
+msgstr "--conntrack i --query-port wzajemnie się wykluczają"
 
-#: dnsmasq.c:153
+#: dnsmasq.c:189
+msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h"
+msgstr "wsparcie przekazywania znaczników połączeń (conntrack) nie zostało wkompilowane - ustaw HAVE_CONNTRACK w src/config.h"
+
+#: dnsmasq.c:194
 msgid "asychronous logging is not available under Solaris"
-msgstr "zapis do logów w trybie asynchronicznym nie jest dostêpny w Solarisie"
+msgstr "zapis do logów w trybie asynchronicznym nie jest dostępny w Solarisie"
 
-#: dnsmasq.c:158
-#, fuzzy
+#: dnsmasq.c:199
 msgid "asychronous logging is not available under Android"
-msgstr "zapis do logów w trybie asynchronicznym nie jest dostêpny w Solarisie"
+msgstr "zapis do logów w trybie asynchronicznym nie jest dostępny w Androidzie"
+
+#: dnsmasq.c:204
+msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h"
+msgstr "tryb autorytatywny DNS-a niedostępny - ustaw HAVE_AUTH w src/config.h"
+
+#: dnsmasq.c:209
+msgid "loop detection not available: set HAVE_LOOP in src/config.h"
+msgstr "wykrywanie pętli zapytań nie zostało wkompilowane - ustaw HAVE_LOOP w src/config.h"
+
+#: dnsmasq.c:217
+msgid "zone serial must be configured in --auth-soa"
+msgstr "za pomocą --auth-soa musi zostać ustawiony numer seryjny strefy"
+
+#: dnsmasq.c:235
+msgid "dhcp-range constructor not available on this platform"
+msgstr "konstrukcja dhcp-range nie jest dostępna w tym systemie"
 
-#: dnsmasq.c:177
+#: dnsmasq.c:278
+msgid "cannot set --bind-interfaces and --bind-dynamic"
+msgstr "--bind-interfaces i --bind-dynamic wzajemnie się wykluczają"
+
+#: dnsmasq.c:281
 #, c-format
 msgid "failed to find list of interfaces: %s"
-msgstr "b³±d podczas tworzenia listy interfejsów sieciowych: %s"
+msgstr "błąd podczas tworzenia listy interfejsów sieciowych: %s"
 
-#: dnsmasq.c:185
+#: dnsmasq.c:290
 #, c-format
 msgid "unknown interface %s"
 msgstr "nieznany interfejs %s"
 
-#: dnsmasq.c:191
-#, c-format
-msgid "no interface with address %s"
-msgstr "brak interfejsu z adresem %s"
-
-#: dnsmasq.c:207 dnsmasq.c:678
+#: dnsmasq.c:354 dnsmasq.c:997
 #, c-format
 msgid "DBus error: %s"
-msgstr "b³±d DBus: %s"
+msgstr "błąd DBus: %s"
 
-#: dnsmasq.c:210
+#: dnsmasq.c:357
 msgid "DBus not available: set HAVE_DBUS in src/config.h"
-msgstr "Obs³uga DBus nie zosta³a wkompilowana -- ustaw HAVE_DBUS w src/config.h"
+msgstr "Obsługa DBus nie została wkompilowana -- ustaw HAVE_DBUS w src/config.h"
 
-#: dnsmasq.c:236
+#: dnsmasq.c:385
 #, c-format
 msgid "unknown user or group: %s"
-msgstr "nieznany u¿ytkownik lub grupa: %s"
+msgstr "nieznany użytkownik lub grupa: %s"
 
-#: dnsmasq.c:291
+#: dnsmasq.c:440
 #, c-format
 msgid "cannot chdir to filesystem root: %s"
-msgstr "nie potrafiê wej¶æ do g³ównego katalogu: %s"
+msgstr "nie potrafię wejść do głównego katalogu: %s"
 
-#: dnsmasq.c:455
+#: dnsmasq.c:692
 #, c-format
 msgid "started, version %s DNS disabled"
-msgstr "uruchomiony, wersja %s, DNS wy³±czony"
+msgstr "uruchomiony, wersja %s, DNS wyłączony"
 
-#: dnsmasq.c:457
+#: dnsmasq.c:694
 #, c-format
 msgid "started, version %s cachesize %d"
-msgstr "uruchomiony, wersja %s, %d miejsc w pamiêci podrêcznej"
+msgstr "uruchomiony, wersja %s, %d miejsc w pamięci podręcznej"
 
-#: dnsmasq.c:459
+#: dnsmasq.c:696
 #, c-format
 msgid "started, version %s cache disabled"
-msgstr "uruchomiony, wersja %s, pamiêæ podrêczna wy³±czona"
+msgstr "uruchomiony, wersja %s, pamięć podręczna wyłączona"
 
-#: dnsmasq.c:461
+#: dnsmasq.c:698
 #, c-format
 msgid "compile time options: %s"
 msgstr "opcje kompilacji: %s"
 
-#: dnsmasq.c:467
+#: dnsmasq.c:704
 msgid "DBus support enabled: connected to system bus"
-msgstr "obs³uga DBus w³±czona, pod³±czono do serwera DBus"
+msgstr "obsługa DBus włączona, podłączono do serwera DBus"
 
-#: dnsmasq.c:469
+#: dnsmasq.c:706
 msgid "DBus support enabled: bus connection pending"
-msgstr "obs³uga DBus w³±czona, trwa pod³±czanie do serwera DBus"
+msgstr "obsługa DBus włączona, trwa podłączanie do serwera DBus"
+
+#: dnsmasq.c:711
+msgid "DNS service limited to local subnets"
+msgstr "usługa DNS ograniczona do lokalnych podsieci"
+
+#: dnsmasq.c:727
+msgid "DNSSEC validation enabled"
+msgstr "walidacja DNSSEC włączona"
 
-#: dnsmasq.c:474
+#: dnsmasq.c:730
+msgid "DNSSEC signature timestamps not checked until first cache reload"
+msgstr "sprawdzanie sygnatur czasowych DNSSEC wyłączone do czasu przeładowania pamięci podręcznej"
+
+#: dnsmasq.c:733
+msgid "DNSSEC signature timestamps not checked until system time valid"
+msgstr "sprawdzanie sygnatur czasowych DNSSEC wyłączone do czasu zsynchronizowania się zegara systemowego"
+
+#: dnsmasq.c:738
 #, c-format
 msgid "warning: failed to change owner of %s: %s"
-msgstr "UWAGA! Nie uda³o siê zmieniæ u¿ytkownika pliku %s: %s"
+msgstr "uwaga: nie udało się zmienić użytkownika pliku %s: %s"
 
-#: dnsmasq.c:478
+#: dnsmasq.c:742
 msgid "setting --bind-interfaces option because of OS limitations"
-msgstr "ustawiam --bind-interfaces z powodu ograniczeñ systemu operacyjnego"
+msgstr "ustawiam --bind-interfaces z powodu ograniczeń systemu operacyjnego"
 
-#: dnsmasq.c:483
+#: dnsmasq.c:752
 #, c-format
 msgid "warning: interface %s does not currently exist"
-msgstr "uwaga: interfejs %s nie jest w³±czony"
+msgstr "uwaga: interfejs %s nie jest włączony"
 
-#: dnsmasq.c:488
+#: dnsmasq.c:757
 msgid "warning: ignoring resolv-file flag because no-resolv is set"
-msgstr "uwaga: ignorujê opcjê resolv-file, poniewa¿ wybrano tryb no-resolv"
+msgstr "uwaga: ignoruję opcję resolv-file, ponieważ wybrano tryb no-resolv"
 
-#: dnsmasq.c:491
+#: dnsmasq.c:760
 msgid "warning: no upstream servers configured"
-msgstr "uwaga: nie wskazano nadrzêdnych serwerów DNS"
+msgstr "uwaga: nie wskazano nadrzędnych serwerów DNS"
 
-#: dnsmasq.c:495
+#: dnsmasq.c:764
 #, c-format
 msgid "asynchronous logging enabled, queue limit is %d messages"
-msgstr "w³±czono asynchroniczny tryb zapisu do logów z kolejk± na %d komunikatów"
-
-#: dnsmasq.c:508
-#, c-format
-msgid "DHCP, static leases only on %.0s%s, lease time %s"
-msgstr "DHCP: tylko statyczne dzier¿awy na %.0s%s, czas dzier¿awy %s"
+msgstr "włączono asynchroniczny tryb zapisu do logów z kolejką na %d komunikatów"
 
-#: dnsmasq.c:510
-#, c-format
-msgid "DHCP, proxy on subnet %.0s%s%.0s"
-msgstr "DHCP: po¶rednik na podsieci %.0s%s%.0s"
+#: dnsmasq.c:785
+msgid "IPv6 router advertisement enabled"
+msgstr "anonsowanie rutera IPv6 włączone"
 
-#: dnsmasq.c:511
+#: dnsmasq.c:790
 #, c-format
-msgid "DHCP, IP range %s -- %s, lease time %s"
-msgstr "DHCP: zakres IP %s -- %s, czas dzier¿awy %s"
+msgid "DHCP, sockets bound exclusively to interface %s"
+msgstr "DHCP, gniazda dowiązane na wyłączność interfejsowi %s"
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 msgid "root is "
-msgstr "z g³ównym katalogiem w "
+msgstr "z głównym katalogiem w "
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 msgid "enabled"
-msgstr "w³±czony"
+msgstr "włączony"
 
-#: dnsmasq.c:528
+#: dnsmasq.c:806
 msgid "secure mode"
 msgstr "w trybie bezpiecznym"
 
-#: dnsmasq.c:554
+#: dnsmasq.c:809
+#, c-format
+msgid "warning: %s inaccessible"
+msgstr "uwaga: %s niedostępny"
+
+#: dnsmasq.c:813
+#, c-format
+msgid "warning: TFTP directory %s inaccessible"
+msgstr "uwaga: katalog TFTP %s nie jest dostępny"
+
+#: dnsmasq.c:839
 #, c-format
 msgid "restricting maximum simultaneous TFTP transfers to %d"
-msgstr "ograniczam ilo¶æ jednoczesnych przes³añ TFTP do %d"
+msgstr "ograniczam ilość jednoczesnych przesłań TFTP do %d"
 
-#: dnsmasq.c:680
+#: dnsmasq.c:999
 msgid "connected to system DBus"
-msgstr "pod³±czono do DBus-a"
+msgstr "podłączono do DBus-a"
 
-#: dnsmasq.c:775
+#: dnsmasq.c:1149
 #, c-format
 msgid "cannot fork into background: %s"
-msgstr "nie potrafiê prze³±czyæ siê do pracy w tle: %s"
+msgstr "nie potrafię przełączyć się do pracy w tle: %s"
 
-#: dnsmasq.c:778
+#: dnsmasq.c:1152
 #, c-format
 msgid "failed to create helper: %s"
-msgstr "nie uda³o siê utworzyæ procesu pomocniczego: %s"
+msgstr "nie udało się utworzyć procesu pomocniczego: %s"
 
-#: dnsmasq.c:781
+#: dnsmasq.c:1155
 #, c-format
 msgid "setting capabilities failed: %s"
-msgstr "nie powiod³o siê ustawianie ograniczeñ (capabilities): %s"
+msgstr "nie powiodło się ustawianie ograniczeń (capabilities): %s"
 
-#: dnsmasq.c:785
+#: dnsmasq.c:1158
 #, c-format
 msgid "failed to change user-id to %s: %s"
-msgstr "nie uda³o siê zmieniæ u¿ytkownika procesu na %s: %s"
+msgstr "nie udało się zmienić użytkownika procesu na %s: %s"
 
-#: dnsmasq.c:790
+#: dnsmasq.c:1161
 #, c-format
 msgid "failed to change group-id to %s: %s"
-msgstr "nie uda³o siê zmieniæ grupy procesu na %s: %s"
+msgstr "nie udało się zmienić grupy procesu na %s: %s"
 
-#: dnsmasq.c:793
+#: dnsmasq.c:1164
 #, c-format
 msgid "failed to open pidfile %s: %s"
-msgstr "nie uda³o siê otworzyæ pliku z PID-em %s: %s"
+msgstr "nie udało się otworzyć pliku z PID-em %s: %s"
+
+#: dnsmasq.c:1167
+#, c-format
+msgid "cannot open log %s: %s"
+msgstr "nie udało się otworzyć logu %s: %s"
 
-#: dnsmasq.c:796
+#: dnsmasq.c:1170
 #, c-format
-msgid "cannot open %s: %s"
-msgstr "nie mo¿na otworzyæ %s: %s"
+msgid "failed to load Lua script: %s"
+msgstr "nie udało się wczytać skryptu Lua: %s"
 
-#: dnsmasq.c:851
+#: dnsmasq.c:1173
 #, c-format
-msgid "child process killed by signal %d"
-msgstr "proces potomny zabity sygna³em %d"
+msgid "TFTP directory %s inaccessible: %s"
+msgstr "katalog TFTP %s nie jest dostępny: %s"
 
-#: dnsmasq.c:855
+#: dnsmasq.c:1176
 #, c-format
-msgid "child process exited with status %d"
-msgstr "proces potomny zakoñczy³ siê z kodem powrotu %d"
+msgid "cannot create timestamp file %s: %s"
+msgstr "nie potrafię utworzyć pliku znacznika czasu %s: %s"
+
+#: dnsmasq.c:1197
+msgid "now checking DNSSEC signature timestamps"
+msgstr "trwa sprawdzanie sygnatur czasowych podpisów DNSSEC"
 
-#: dnsmasq.c:859
+#: dnsmasq.c:1264
+#, c-format
+msgid "script process killed by signal %d"
+msgstr "skrypt został zabity sygnałem %d"
+
+#: dnsmasq.c:1268
+#, c-format
+msgid "script process exited with status %d"
+msgstr "skrypt zakończył się z kodem powrotu %d"
+
+#: dnsmasq.c:1272
 #, c-format
 msgid "failed to execute %s: %s"
-msgstr "nie uda³o siê uruchomiæ %s: %s"
+msgstr "nie udało się uruchomić %s: %s"
 
-#: dnsmasq.c:903
+#: dnsmasq.c:1327
 msgid "exiting on receipt of SIGTERM"
-msgstr "zakoñczy³em dzia³anie z powodu odebrania SIGTERM"
+msgstr "zakończyłem działanie z powodu odebrania SIGTERM"
 
-#: dnsmasq.c:931
+#: dnsmasq.c:1355
 #, c-format
 msgid "failed to access %s: %s"
-msgstr "brak dostêpu do %s: %s"
+msgstr "brak dostępu do %s: %s"
 
-#: dnsmasq.c:961
+#: dnsmasq.c:1385
 #, c-format
 msgid "reading %s"
 msgstr "czytanie %s"
 
-#: dnsmasq.c:972
+#: dnsmasq.c:1396
 #, c-format
 msgid "no servers found in %s, will retry"
-msgstr "w %s nie znalaz³em serwerów, spróbujê ponownie pó¼niej"
+msgstr "w %s nie znalazłem serwerów, spróbuję ponownie później"
 
-#: dhcp.c:40
+#: dhcp.c:53
 #, c-format
 msgid "cannot create DHCP socket: %s"
-msgstr "nie uda³o siê utworzyæ gniazda dla DHCP: %s"
+msgstr "nie udało się utworzyć gniazda dla DHCP: %s"
 
-#: dhcp.c:52
+#: dhcp.c:68
 #, c-format
 msgid "failed to set options on DHCP socket: %s"
-msgstr "b³±d podczas ustawiania opcji gniazda DHCP: %s"
+msgstr "błąd podczas ustawiania opcji gniazda DHCP: %s"
 
-#: dhcp.c:65
+#: dhcp.c:89
 #, c-format
 msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"
-msgstr "nie uda³o siê ustawiæ SO_REUSE{ADDR|PORT} gniazda DHCP: %s"
+msgstr "nie udało się ustawić SO_REUSE{ADDR|PORT} gniazda DHCP: %s"
 
-#: dhcp.c:77
+#: dhcp.c:101
 #, c-format
 msgid "failed to bind DHCP server socket: %s"
-msgstr "b³±d przy przyznawaniu nazwy gniazdu serwera DHCP: %s"
+msgstr "błąd przy przyznawaniu nazwy gniazdu serwera DHCP: %s"
 
-#: dhcp.c:103
+#: dhcp.c:127
 #, c-format
 msgid "cannot create ICMP raw socket: %s."
-msgstr "nie uda³o siê utworzyæ surowego gniazda ICMP: %s."
+msgstr "nie udało się utworzyć surowego gniazda ICMP: %s."
+
+#: dhcp.c:241 dhcp6.c:180
+#, c-format
+msgid "unknown interface %s in bridge-interface"
+msgstr "nieznany interfejs %s w bridge-u"
 
 #: dhcp.c:281
 #, c-format
 msgid "DHCP packet received on %s which has no address"
-msgstr "¿±danie DHCP odebrano na interfejsie %s, który nie ma adresu"
+msgstr "żądanie DHCP odebrano na interfejsie %s, który nie ma adresu"
+
+#: dhcp.c:415
+#, c-format
+msgid "ARP-cache injection failed: %s"
+msgstr "uzupełnienie pamięci podręcznej ARP nie powiodło się: %s"
 
-#: dhcp.c:445
+#: dhcp.c:514
 #, c-format
 msgid "DHCP range %s -- %s is not consistent with netmask %s"
-msgstr "zakres adresów DHCP %s -- %s jest niespójny z mask± sieci %s"
+msgstr "zakres adresów DHCP %s -- %s jest niespójny z maską sieci %s"
 
-#: dhcp.c:852
+#: dhcp.c:815
 #, c-format
 msgid "bad line at %s line %d"
-msgstr "z³a zawarto¶æ pliku %s, w linii %d"
+msgstr "zła zawartość pliku %s, w linii %d"
 
-#: dhcp.c:895
+#: dhcp.c:858
 #, c-format
 msgid "ignoring %s line %d, duplicate name or IP address"
-msgstr "w %s pomijam liniê %d -- powtórzona nazwa lub adres IP"
-
-#: dhcp.c:978
-#, c-format
-msgid "duplicate IP address %s in dhcp-config directive."
-msgstr "powtórzony adres IP (%s) w parametrze dhcp-config"
-
-#: dhcp.c:981
-#, c-format
-msgid "duplicate IP address %s in %s."
-msgstr "powtórzony adres IP (%s) w pliku %s"
-
-#: dhcp.c:1024
-#, c-format
-msgid "%s has more than one address in hostsfile, using %s for DHCP"
-msgstr "do komputera o nazwie %s pasuje wiêcej ni¿ jeden adres, w odpowiedzi DHCP wysy³am %s"
+msgstr "w %s pomijam linię %d -- powtórzona nazwa lub adres IP"
 
-#: dhcp.c:1029
+#: dhcp.c:1002 rfc3315.c:2135
 #, c-format
-msgid "duplicate IP address %s (%s) in dhcp-config directive"
-msgstr "powtórzenie adresu IP %s (%s) w opcji dhcp-config"
+msgid "DHCP relay %s -> %s"
+msgstr "przekazywanie DHCP %s -> %s"
 
-#: lease.c:67
+#: lease.c:61
 #, c-format
 msgid "cannot open or create lease file %s: %s"
-msgstr "nie potrafiê otworzyæ albo utworzyæ pliku dzier¿aw %s: %s"
+msgstr "nie potrafię otworzyć albo utworzyć pliku dzierżaw %s: %s"
 
-#: lease.c:93
+#: lease.c:134
 msgid "too many stored leases"
-msgstr "zbyt du¿a ilo¶æ zapisanych dzier¿aw"
+msgstr "zbyt duża ilość zapisanych dzierżaw"
 
-#: lease.c:129
+#: lease.c:165
 #, c-format
 msgid "cannot run lease-init script %s: %s"
-msgstr "nie potrafiê uruchomiæ skryptu %s: %s"
+msgstr "nie potrafię uruchomić skryptu %s: %s"
 
-#: lease.c:135
+#: lease.c:171
 #, c-format
 msgid "lease-init script returned exit code %s"
-msgstr "skrypt zakoñczy³ siê z kodem powrotu %s"
+msgstr "skrypt zakończył się z kodem powrotu %s"
 
-#: lease.c:235
+#: lease.c:342
 #, c-format
 msgid "failed to write %s: %s (retry in %us)"
-msgstr "b³±d zapisu do %s: %s (spróbujê ponownie za %us)"
+msgstr "błąd zapisu do %s: %s (spróbuję ponownie za %us)"
+
+#: lease.c:906
+#, c-format
+msgid "Ignoring domain %s for DHCP host name %s"
+msgstr "Nie uwzględniam części domenowej (%s) dla komputera %s"
 
-#: rfc2131.c:315
+#: rfc2131.c:344
 #, c-format
 msgid "no address range available for DHCP request %s %s"
-msgstr "nie zdefiniowano zakresu adresów odpowiedniego dla ¿±dania %s %s"
+msgstr "nie zdefiniowano zakresu adresów odpowiedniego dla żądania %s %s"
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "with subnet selector"
 msgstr "z wyborem podsieci"
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "via"
 msgstr "przez"
 
-#: rfc2131.c:331
+#: rfc2131.c:357
 #, c-format
 msgid "%u available DHCP subnet: %s/%s"
-msgstr "%u dostêpna podsieæ DHCP: %s/%s"
+msgstr "%u dostępna podsieć DHCP: %s/%s"
 
-#: rfc2131.c:334
+#: rfc2131.c:360 rfc3315.c:300
 #, c-format
 msgid "%u available DHCP range: %s -- %s"
-msgstr "%u dostêpny zakres adresów DHCP: %s -- %s"
+msgstr "%u dostępny zakres adresów DHCP: %s -- %s"
+
+#: rfc2131.c:471
+#, c-format
+msgid "%u vendor class: %s"
+msgstr "%u klasa dostawcy: %s"
+
+#: rfc2131.c:473
+#, c-format
+msgid "%u user class: %s"
+msgstr "%u klasa użytkownika: %s"
 
-#: rfc2131.c:363
+#: rfc2131.c:500
 msgid "disabled"
-msgstr "wy³±czony(a)"
+msgstr "wyłączony(a)"
 
-#: rfc2131.c:404 rfc2131.c:916 rfc2131.c:1288
+#: rfc2131.c:541 rfc2131.c:974 rfc2131.c:1380 rfc3315.c:603 rfc3315.c:856
+#: rfc3315.c:1135
 msgid "ignored"
-msgstr "ignorujê"
+msgstr "ignoruję"
 
-#: rfc2131.c:419 rfc2131.c:1135
+#: rfc2131.c:556 rfc2131.c:1207 rfc3315.c:906
 msgid "address in use"
-msgstr "adres jest w u¿yciu"
+msgstr "adres jest w użyciu"
 
-#: rfc2131.c:433 rfc2131.c:970
+#: rfc2131.c:570 rfc2131.c:1028
 msgid "no address available"
-msgstr "brak dostêpnego adresu"
+msgstr "brak dostępnego adresu"
 
-#: rfc2131.c:440 rfc2131.c:1098
+#: rfc2131.c:577 rfc2131.c:1170
 msgid "wrong network"
-msgstr "nieprawid³owa sieæ"
+msgstr "nieprawidłowa sieć"
 
-#: rfc2131.c:454
+#: rfc2131.c:592
 msgid "no address configured"
 msgstr "brak skonfigurowanego adresu"
 
-#: rfc2131.c:460 rfc2131.c:1148
+#: rfc2131.c:598 rfc2131.c:1220
 msgid "no leases left"
-msgstr "brak wolnych dzier¿aw"
+msgstr "brak wolnych dzierżaw"
 
-#: rfc2131.c:545
+#: rfc2131.c:693 rfc3315.c:476
 #, c-format
 msgid "%u client provides name: %s"
-msgstr "klient %u przedstawia siê jako %s"
+msgstr "klient %u przedstawia się jako %s"
 
-#: rfc2131.c:700
-#, c-format
-msgid "%u vendor class: %s"
-msgstr "%u klasa dostawcy: %s"
-
-#: rfc2131.c:702
-#, c-format
-msgid "%u user class: %s"
-msgstr "%u klasa u¿ytkownika: %s"
-
-#: rfc2131.c:761
+#: rfc2131.c:798
 msgid "PXE BIS not supported"
-msgstr "PXE BIS nie jest obs³ugiwane"
+msgstr "PXE BIS nie jest obsługiwane"
 
-#: rfc2131.c:886
+#: rfc2131.c:942 rfc3315.c:1229
 #, c-format
 msgid "disabling DHCP static address %s for %s"
-msgstr "wy³±czam statyczne przypisanie adresu %s dla %s"
+msgstr "wyłączam statyczne przypisanie adresu %s dla %s"
 
-#: rfc2131.c:907
+#: rfc2131.c:963
 msgid "unknown lease"
-msgstr "nieznana dzier¿awa"
+msgstr "nieznana dzierżawa"
 
-#: rfc2131.c:939
+#: rfc2131.c:997
 #, c-format
 msgid "not using configured address %s because it is leased to %s"
-msgstr "nie proponujê zak³adanego w konfiguracji adresu %s, bo jest on ju¿ wydzier¿awiony komputerowi %s"
+msgstr "nie proponuję zakładanego w konfiguracji adresu %s, bo jest on już wydzierżawiony komputerowi %s"
 
-#: rfc2131.c:949
+#: rfc2131.c:1007
 #, c-format
 msgid "not using configured address %s because it is in use by the server or relay"
-msgstr "nie proponujê zak³adanego w konfiguracji adresu %s, bo u¿ywa go który¶ z serwerów"
+msgstr "nie proponuję zakładanego w konfiguracji adresu %s, bo używa go któryś z serwerów"
 
-#: rfc2131.c:952
+#: rfc2131.c:1010
 #, c-format
 msgid "not using configured address %s because it was previously declined"
-msgstr "nie proponujê zak³adanego w konfiguracji adresu %s, bo ju¿ poprzednio zosta³ odrzucony"
+msgstr "nie proponuję zakładanego w konfiguracji adresu %s, bo już poprzednio został odrzucony"
 
-#: rfc2131.c:968 rfc2131.c:1141
+#: rfc2131.c:1026 rfc2131.c:1213
 msgid "no unique-id"
 msgstr "brak unikalnego id"
 
-#: rfc2131.c:1037
+#: rfc2131.c:1108
 msgid "wrong server-ID"
-msgstr "nieprawid³owy identyfikator serwera (server-ID)"
+msgstr "nieprawidłowy identyfikator serwera (server-ID)"
 
-#: rfc2131.c:1055
+#: rfc2131.c:1127
 msgid "wrong address"
-msgstr "b³êdny adres"
+msgstr "błędny adres"
 
-#: rfc2131.c:1073
+#: rfc2131.c:1145 rfc3315.c:1002
 msgid "lease not found"
-msgstr "dzier¿awa nieznaleziona"
+msgstr "dzierżawa nieznaleziona"
 
-#: rfc2131.c:1106
+#: rfc2131.c:1178
 msgid "address not available"
-msgstr "adres niedostêpny"
+msgstr "adres niedostępny"
 
-#: rfc2131.c:1117
+#: rfc2131.c:1189
 msgid "static lease available"
-msgstr "dostêpna statyczna dzier¿awa"
+msgstr "dostępna statyczna dzierżawa"
 
-#: rfc2131.c:1121
+#: rfc2131.c:1193
 msgid "address reserved"
 msgstr "adres zarezerwowany"
 
-#: rfc2131.c:1129
+#: rfc2131.c:1201
 #, c-format
 msgid "abandoning lease to %s of %s"
 msgstr "porzucam przypisanie do %s nazwy %s"
 
-#: rfc2131.c:1710
-#, c-format
-msgid "%u tags: %s"
-msgstr "%u cechy: %s"
-
-#: rfc2131.c:1723
+#: rfc2131.c:1707
 #, c-format
 msgid "%u bootfile name: %s"
 msgstr "%u nazwa pliku bootowania: %s"
 
-#: rfc2131.c:1732
+#: rfc2131.c:1716
 #, c-format
 msgid "%u server name: %s"
 msgstr "%u nazwa serwera: %s"
 
-#: rfc2131.c:1746
+#: rfc2131.c:1724
 #, c-format
 msgid "%u next server: %s"
-msgstr "%u nastêpny serwer: %s"
+msgstr "%u następny serwer: %s"
 
-#: rfc2131.c:1749
+#: rfc2131.c:1727
 #, c-format
 msgid "%u broadcast response"
-msgstr "%u odpowied¼ rozg³oszeniowa"
+msgstr "%u odpowiedź rozgłoszeniowa"
 
-#: rfc2131.c:1812
+#: rfc2131.c:1790
 #, c-format
 msgid "cannot send DHCP/BOOTP option %d: no space left in packet"
-msgstr "nie mam mo¿liwo¶ci wys³ania opcji %d DHCP/BOOTP: niedostateczna ilo¶æ miejsca w pakiecie"
+msgstr "nie mam możliwości wysłania opcji %d DHCP/BOOTP: niedostateczna ilość miejsca w pakiecie"
 
-#: rfc2131.c:2058
+#: rfc2131.c:2031
 msgid "PXE menu too large"
-msgstr "menu PXE zbyt du¿e"
+msgstr "menu PXE zbyt duże"
 
-#: rfc2131.c:2171
-#, c-format
-msgid "Ignoring domain %s for DHCP host name %s"
-msgstr "Nie uwzglêdniam czê¶ci domenowej (%s) dla komputera %s"
-
-#: rfc2131.c:2189
+#: rfc2131.c:2170 rfc3315.c:1502
 #, c-format
 msgid "%u requested options: %s"
-msgstr "%u za¿±dano: %s"
+msgstr "%u zażądano: %s"
 
-#: rfc2131.c:2456
+#: rfc2131.c:2487
 #, c-format
 msgid "cannot send RFC3925 option: too many options for enterprise number %d"
-msgstr "nie mogê wys³aæ opcji RFC3925: za d³ugi ³añcuch opcji przy numerze %d"
+msgstr "nie mogę wysłać opcji RFC3925: za długi łańcuch opcji przy numerze %d"
 
-#: netlink.c:70
+#: netlink.c:77
 #, c-format
 msgid "cannot create netlink socket: %s"
-msgstr "nie potrafiê utworzyæ po³±czenia netlink %s"
+msgstr "nie potrafię utworzyć połączenia netlink %s"
 
-#: netlink.c:288
+#: netlink.c:348
 #, c-format
 msgid "netlink returns error: %s"
-msgstr "wyst±pi³ b³±d w po³±czeniu netlink %s"
+msgstr "wystąpił błąd w połączeniu netlink %s"
 
-#: dbus.c:150
+#: dbus.c:186
 msgid "attempt to set an IPv6 server address via DBus - no IPv6 support"
-msgstr "próba ustawienia adresu IPv6 serwera przez DBus, ale brak obs³ugi IPv6"
+msgstr "próba ustawienia adresu IPv6 serwera przez DBus, ale brak obsługi IPv6"
 
-#: dbus.c:286
+#: dbus.c:439
+#, c-format
+msgid "Enabling --%s option from D-Bus"
+msgstr "opcja --%s została właśnie aktywowana za pomocą D-Bus"
+
+#: dbus.c:444
+#, c-format
+msgid "Disabling --%s option from D-Bus"
+msgstr "opcja --%s została właśnie dezaktywowana za pomocą D-Bus"
+
+#: dbus.c:691
 msgid "setting upstream servers from DBus"
-msgstr "ustawiam adresy serwerów nadrzêdnych na podstawie informacji odebranych z DBus"
+msgstr "ustawiam adresy serwerów nadrzędnych na podstawie informacji odebranych z DBus"
 
-#: dbus.c:324
+#: dbus.c:738
 msgid "could not register a DBus message handler"
-msgstr "nie mo¿na zarejestrowaæ uchwytu DBus"
+msgstr "nie można zarejestrować uchwytu DBus"
 
-#: bpf.c:217
+#: bpf.c:263
 #, c-format
 msgid "cannot create DHCP BPF socket: %s"
-msgstr "nie potrafiê utworzyæ gniazda DHCP BPF: %s"
+msgstr "nie potrafię utworzyć gniazda DHCP BPF: %s"
 
-#: bpf.c:245
+#: bpf.c:291
 #, c-format
 msgid "DHCP request for unsupported hardware type (%d) received on %s"
-msgstr "¿±danie DHCP od urz±dzenia nieobs³ugiwanego typu (%d) odebrano na %s"
+msgstr "żądanie DHCP od urządzenia nieobsługiwanego typu (%d) odebrano na %s"
+
+#: bpf.c:376
+#, c-format
+msgid "cannot create PF_ROUTE socket: %s"
+msgstr "nie udało się utworzyć gniazda PF_ROUTE: %s"
+
+#: bpf.c:397
+msgid "Unknown protocol version from route socket"
+msgstr "Nieznana wersja protokołu."
+
+#: helper.c:153
+msgid "lease() function missing in Lua script"
+msgstr "w skrypcie Lua brak funkcji lease()"
 
-#: tftp.c:281
+#: tftp.c:309
 msgid "unable to get free port for TFTP"
-msgstr "brak wolnego portu dla us³ugi TFTP"
+msgstr "brak wolnego portu dla usługi TFTP"
 
-#: tftp.c:296
+#: tftp.c:325
 #, c-format
 msgid "unsupported request from %s"
-msgstr "nieobs³ugiwane ¿±danie od komputera %s"
+msgstr "nieobsługiwane żądanie od komputera %s"
 
-#: tftp.c:406
+#: tftp.c:439
 #, c-format
 msgid "file %s not found"
-msgstr "plik %s nie zosta³ znaleziony"
+msgstr "plik %s nie został znaleziony"
 
-#: tftp.c:522
+#: tftp.c:548
 #, c-format
 msgid "error %d %s received from %s"
-msgstr "b³±d %d %s odebrano od %s"
+msgstr "błąd %d %s odebrano od %s"
 
-#: tftp.c:554
+#: tftp.c:590
 #, c-format
 msgid "failed sending %s to %s"
-msgstr "b³±d wysy³ania pliku %s do komputera %s"
+msgstr "błąd wysyłania pliku %s do komputera %s"
 
-#: tftp.c:568
+#: tftp.c:590
 #, c-format
 msgid "sent %s to %s"
-msgstr "plik %s przes³ano do %s"
+msgstr "plik %s przesłano do %s"
 
-#: log.c:177
+#: log.c:190
 #, c-format
 msgid "overflow: %d log entries lost"
-msgstr "przepe³nienie: stracono %d wpisów do logów"
+msgstr "przepełnienie: stracono %d wpisów do logów"
 
-#: log.c:254
+#: log.c:268
 #, c-format
 msgid "log failed: %s"
-msgstr "nie uda³o siê zapisaæ komunikatów do %s"
+msgstr "nie udało się zapisać komunikatów do %s"
 
-#: log.c:462
+#: log.c:469
 msgid "FAILED to start up"
-msgstr "B£¡D: nie uda³o siê uruchomiæ dnsmasq-a"
+msgstr "BŁĄD: nie udało się uruchomić dnsmasq-a"
+
+#: conntrack.c:65
+#, c-format
+msgid "Conntrack connection mark retrieval failed: %s"
+msgstr "Nie udało się odcztać znacznika połączenia (conntrack): %s"
+
+#: dhcp6.c:59
+#, c-format
+msgid "cannot create DHCPv6 socket: %s"
+msgstr "nie udało się utworzyć gniazda dla DHCPv6: %s"
+
+#: dhcp6.c:80
+#, c-format
+msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"
+msgstr "nie udało się ustawić SO_REUSE{ADDR|PORT} gniazda DHCPv6: %s"
+
+#: dhcp6.c:92
+#, c-format
+msgid "failed to bind DHCPv6 server socket: %s"
+msgstr "dowiązywanie gniazda serwera DHCPv6 zakończone niepowodzeniem: %s"
+
+#: rfc3315.c:157
+#, c-format
+msgid "no address range available for DHCPv6 request from relay at %s"
+msgstr "nie zdefiniowano zakresu adresów odpowiedniego dla żądania DHCPv6 przekazanego przez %s"
+
+#: rfc3315.c:166
+#, c-format
+msgid "no address range available for DHCPv6 request via %s"
+msgstr "nie zdefiniowano zakresu adresów odpowiedniego dla żądania DHCPv6 od %s"
+
+#: rfc3315.c:297
+#, c-format
+msgid "%u available DHCPv6 subnet: %s/%d"
+msgstr "%u dostępna podsieć DHCPv6: %s/%d"
+
+#: rfc3315.c:380
+#, c-format
+msgid "%u vendor class: %u"
+msgstr "%u klasa dostawcy: %u"
+
+#: rfc3315.c:428
+#, c-format
+msgid "%u client MAC address: %s"
+msgstr "adres MAC klienta %u: %s"
+
+#: rfc3315.c:660
+#, c-format
+msgid "unknown prefix-class %d"
+msgstr "nieznana klasa sieci %d"
+
+#: rfc3315.c:803 rfc3315.c:898
+msgid "address unavailable"
+msgstr "adres niedostępny"
+
+#: rfc3315.c:815 rfc3315.c:946 rfc3315.c:1279
+msgid "success"
+msgstr "udane"
+
+#: rfc3315.c:830 rfc3315.c:839 rfc3315.c:954 rfc3315.c:956
+msgid "no addresses available"
+msgstr "brak wolnych adresów"
+
+#: rfc3315.c:933
+msgid "not on link"
+msgstr "poza zasięgiem"
+
+#: rfc3315.c:1006 rfc3315.c:1191 rfc3315.c:1268
+msgid "no binding found"
+msgstr "brak powiązania"
+
+#: rfc3315.c:1044
+msgid "deprecated"
+msgstr "przestarzały"
+
+#: rfc3315.c:1049
+msgid "address invalid"
+msgstr "niepoprawny adres"
+
+#: rfc3315.c:1096
+msgid "confirm failed"
+msgstr "brak potwierdzenia"
+
+#: rfc3315.c:1112
+msgid "all addresses still on link"
+msgstr "wszystkie adresy ciągle w użyciu"
+
+#: rfc3315.c:1200
+msgid "release received"
+msgstr "adres został zwolniony"
+
+#: rfc3315.c:2126
+msgid "Cannot multicast to DHCPv6 server without correct interface"
+msgstr "Nie mogę rozesłać do serwerów DHCPv6 nie mając prawidłowego interfejsu"
+
+#: dhcp-common.c:145
+#, c-format
+msgid "Ignoring duplicate dhcp-option %d"
+msgstr "Pomijam powtórzoną dhcp-option %d"
+
+#: dhcp-common.c:222
+#, c-format
+msgid "%u tags: %s"
+msgstr "%u cechy: %s"
+
+#: dhcp-common.c:407
+#, c-format
+msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgstr "do komputera o nazwie %s pasuje więcej niż jeden adres, w odpowiedzi DHCP wysyłam %s"
+
+#: dhcp-common.c:430
+#, c-format
+msgid "duplicate IP address %s (%s) in dhcp-config directive"
+msgstr "powtórzenie adresu IP %s (%s) w opcji dhcp-config"
+
+#: dhcp-common.c:494
+#, c-format
+msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s"
+msgstr "nie udało się ustawić SO_BINDTODEVICE gniazda DHCP: %s"
+
+#: dhcp-common.c:615
+#, c-format
+msgid "Known DHCP options:\n"
+msgstr "Znane opcje DHCP:\n"
+
+#: dhcp-common.c:626
+#, c-format
+msgid "Known DHCPv6 options:\n"
+msgstr "Rozpoznawane opcje DHCPv6:\n"
+
+#: dhcp-common.c:823
+msgid ", prefix deprecated"
+msgstr ", przestarzały prefiks"
+
+#: dhcp-common.c:826
+#, c-format
+msgid ", lease time "
+msgstr ", czas dzierżawy "
+
+#: dhcp-common.c:868
+#, c-format
+msgid "%s stateless on %s%.0s%.0s%s"
+msgstr "%s bezstanowy na %s%.0s%.0s%s"
+
+#: dhcp-common.c:870
+#, c-format
+msgid "%s, static leases only on %.0s%s%s%.0s"
+msgstr "%s, wyłącznie statyczne dzierżawy na %.0s%s%s%.0s"
+
+#: dhcp-common.c:872
+#, c-format
+msgid "%s, proxy on subnet %.0s%s%.0s%.0s"
+msgstr "%s, wykryto pośrednika na podsieci %.0s%s%.0s%.0s"
+
+#: dhcp-common.c:873
+#, c-format
+msgid "%s, IP range %s -- %s%s%.0s"
+msgstr "%s, zakres IP %s -- %s%s%.0s"
+
+#: dhcp-common.c:886
+#, c-format
+msgid "DHCPv4-derived IPv6 names on %s%s"
+msgstr "pochodzące z DHCPv4 nazwy IPv6 na %s%s"
+
+#: dhcp-common.c:889
+#, c-format
+msgid "router advertisement on %s%s"
+msgstr "anonsowanie rutera na %s%s"
+
+#: dhcp-common.c:900
+#, c-format
+msgid "DHCP relay from %s to %s via %s"
+msgstr "przekazywanie DHCP z %s do %s za pomocą %s"
+
+#: dhcp-common.c:902
+#, c-format
+msgid "DHCP relay from %s to %s"
+msgstr "przekazywanie DHCP z %s do %s"
+
+#: radv.c:109
+#, c-format
+msgid "cannot create ICMPv6 socket: %s"
+msgstr "nie udało się utworzyć gniazda dla ICMPv6: %s"
+
+#: auth.c:448
+#, c-format
+msgid "ignoring zone transfer request from %s"
+msgstr "ignoruję żądanie transferu strefy od %s"
+
+#: ipset.c:95
+#, c-format
+msgid "failed to find kernel version: %s"
+msgstr "niezgodna wersja jądra: %s"
+
+#: ipset.c:114
+#, c-format
+msgid "failed to create IPset control socket: %s"
+msgstr "nie powiodło się otwieranie gniazda sterującego IPset: %s"
+
+#: dnssec.c:449 dnssec.c:493
+#, c-format
+msgid "failed to update mtime on %s: %s"
+msgstr "nie udało się uaktualnić znacznika czasu pliku %s: %s"
+
+#: blockdata.c:58
+#, c-format
+msgid "DNSSEC memory in use %u, max %u, allocated %u"
+msgstr "DNSSEC: zużycie pamięci %u, maks. %u, przydzielona %u"
+
+#: tables.c:80
+msgid "error: fill_addr missused"
+msgstr "błąd: niepoprawnie użyty fill_addr"
+
+#: tables.c:109
+#, c-format
+msgid "failed to access pf devices: %s"
+msgstr "brak dostępu do /dev/pf (filtra pakietów): %s"
+
+#: tables.c:123
+#, c-format
+msgid "warning: no opened pf devices %s"
+msgstr "uwaga: brak otwartych filtrów pakietów %s"
+
+#: tables.c:131
+#, c-format
+msgid "error: cannot use table name %s"
+msgstr "błąd: nie potrafię użyć nazwy tablicy %s"
+
+#: tables.c:139
+#, c-format
+msgid "error: cannot strlcpy table name %s"
+msgstr "błąd: nie potrafię strlcpy nazwy tablicy %s"
+
+#: tables.c:145
+#, c-format
+msgid "warning: pfr_add_tables: %s(%d)"
+msgstr "uwaga: pfr_add_tables: %s(%d)"
+
+#: tables.c:151
+msgid "info: table created"
+msgstr "info: tablica utworzona"
+
+#: tables.c:162
+#, c-format
+msgid "warning: DIOCR%sADDRS: %s"
+msgstr "uwaga: DIOCR%sADDRS: %s"
+
+#: tables.c:166
+#, c-format
+msgid "%d addresses %s"
+msgstr "%d adresów %s"
+
+#: inotify.c:59
+#, c-format
+msgid "cannot access path %s: %s"
+msgstr "brak dostępu do katalogu %s: %s"
+
+#: inotify.c:92
+#, c-format
+msgid "failed to create inotify: %s"
+msgstr "nie udało się uruchomić powiadamiania inotify: %s"
+
+#: inotify.c:105
+#, c-format
+msgid "too many symlinks following %s"
+msgstr "zbyt wiele odniesień począwszy od %s"
+
+#: inotify.c:121
+#, c-format
+msgid "directory %s for resolv-file is missing, cannot poll"
+msgstr "katalog %s z resolv-file nie istnieje - nie ma czego odpytywać"
+
+#: inotify.c:125 inotify.c:162
+#, c-format
+msgid "failed to create inotify for %s: %s"
+msgstr "nie udało się utworzyć powiadamiania dla %s: %s"
+
+#: inotify.c:147
+#, c-format
+msgid "bad dynamic directory %s: %s"
+msgstr "zły katalog dynamiczny %s: %s"
+
+#: inotify.c:247
+#, c-format
+msgid "inotify, new or changed file %s"
+msgstr "inotify: pojawił się lub uległ zmianie plik %s"
+
+#, fuzzy
+#~ msgid "cannot cannonicalise resolv-file %s: %s"
+#~ msgstr "nie potrafię otworzyć albo utworzyć pliku dzierżaw %s: %s"
+
+#~ msgid "Always send frequent router-advertisements"
+#~ msgstr "Rozsyłanie wielokrotne anonsów rutera (RA)"
+
+#~ msgid "no interface with address %s"
+#~ msgstr "brak interfejsu z adresem %s"
+
+#~ msgid "duplicate IP address %s in dhcp-config directive."
+#~ msgstr "powtórzony adres IP (%s) w parametrze dhcp-config"
+
+#, fuzzy
+#~ msgid "Specify path to Lua script (no default)."
+#~ msgstr "Określenie ścieżki do pliku PID (domyślnie: %s)."
 
 #~ msgid "only one dhcp-hostsfile allowed"
-#~ msgstr "mo¿na wskazaæ tylko jeden plik dhcp-hostsfile"
+#~ msgstr "można wskazać tylko jeden plik dhcp-hostsfile"
 
 #~ msgid "only one dhcp-optsfile allowed"
-#~ msgstr "mo¿na wskazaæ tylko jeden plik dhcp-optsfile"
+#~ msgstr "można wskazać tylko jeden plik dhcp-optsfile"
 
 #~ msgid "files nested too deep in %s"
-#~ msgstr "zbyt du¿e zag³êbienie plików w %s"
+#~ msgstr "zbyt duże zagłębienie plików w %s"
 
 #~ msgid "TXT record string too long"
-#~ msgstr "zbyt d³ugi rekord TXT"
+#~ msgstr "zbyt długi rekord TXT"
 
 #~ msgid "failed to set IPV6 options on listening socket: %s"
-#~ msgstr "b³±d ustawiania opcji IPV6 na nas³uchuj±cym gnie¼dzie: %s"
+#~ msgstr "błąd ustawiania opcji IPV6 na nasłuchującym gnieździe: %s"
 
 #~ msgid "failed to bind listening socket for %s: %s"
-#~ msgstr "b³±d przy przyznawaniu nazwy gniazdu %s: %s"
-
-#~ msgid "failed to listen on socket: %s"
-#~ msgstr "b³±d przy w³±czaniu nas³uchu na gnie¼dzie: %s"
-
-#~ msgid "failed to create TFTP socket: %s"
-#~ msgstr "nie powiod³o siê otwieranie gniazda dla us³ugi TFTP: %s"
+#~ msgstr "błąd przy przyznawaniu nazwy gniazdu %s: %s"
index 5fe209e..74d9eda 100644 (file)
@@ -10,1446 +10,2128 @@ msgstr ""
 "PO-Revision-Date: 2006-01-16 20:42+0000\n"
 "Last-Translator: Simon Kelley <simon@thekelleys.org.uk>\n"
 "Language-Team: Portuguese <ldp-br@bazar.conectiva.com.br>\n"
+"Language: pt\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=ASCII\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 
-#: cache.c:761
+#: cache.c:523
+msgid "Internal error in cache."
+msgstr ""
+
+#: cache.c:941
 #, c-format
 msgid "failed to load names from %s: %s"
 msgstr ""
 
-#: cache.c:795 dhcp.c:865
+#: cache.c:967 dhcp.c:828
 #, c-format
 msgid "bad address at %s line %d"
 msgstr ""
 
-#: cache.c:853 dhcp.c:881
+#: cache.c:1018 dhcp.c:844
 #, c-format
 msgid "bad name at %s line %d"
 msgstr ""
 
-#: cache.c:860 dhcp.c:956
+#: cache.c:1027 dhcp.c:919
 #, c-format
 msgid "read %s - %d addresses"
 msgstr ""
 
-#: cache.c:899
+#: cache.c:1135
 msgid "cleared cache"
 msgstr ""
 
-#: cache.c:960
+#: cache.c:1164
+#, c-format
+msgid "No IPv4 address found for %s"
+msgstr ""
+
+#: cache.c:1242
 #, c-format
 msgid "%s is a CNAME, not giving it to the DHCP lease of %s"
 msgstr ""
 
-#: cache.c:966
+#: cache.c:1266
 #, c-format
 msgid "not giving name %s to the DHCP lease of %s because the name exists in %s with address %s"
 msgstr ""
 
-#: cache.c:1039
+#: cache.c:1421
 #, c-format
 msgid "time %lu"
 msgstr ""
 
-#: cache.c:1040
+#: cache.c:1422
 #, c-format
 msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries."
 msgstr ""
 
-#: cache.c:1042
+#: cache.c:1424
 #, c-format
 msgid "queries forwarded %u, queries answered locally %u"
 msgstr ""
 
-#: cache.c:1068
+#: cache.c:1427
+#, c-format
+msgid "queries for authoritative zones %u"
+msgstr ""
+
+#: cache.c:1453
 #, c-format
 msgid "server %s#%d: queries sent %u, retried or failed %u"
 msgstr ""
 
-#: util.c:57
+#: util.c:45
 #, c-format
 msgid "failed to seed the random number generator: %s"
 msgstr ""
 
-#: util.c:189
+#: util.c:205
 msgid "failed to allocate memory"
 msgstr ""
 
-#: util.c:227 option.c:573
+#: util.c:250 option.c:601
 msgid "could not get memory"
 msgstr ""
 
-#: util.c:237
+#: util.c:260
 #, c-format
 msgid "cannot create pipe: %s"
 msgstr ""
 
-#: util.c:245
+#: util.c:268
 #, c-format
 msgid "failed to allocate %d bytes"
 msgstr ""
 
-#: util.c:350
+#: util.c:437
 #, c-format
 msgid "infinite"
 msgstr ""
 
-#: option.c:244
+#: option.c:332
 msgid "Specify local address(es) to listen on."
 msgstr ""
 
-#: option.c:245
+#: option.c:333
 msgid "Return ipaddr for all hosts in specified domains."
 msgstr ""
 
-#: option.c:246
+#: option.c:334
 msgid "Fake reverse lookups for RFC1918 private address ranges."
 msgstr ""
 
-#: option.c:247
+#: option.c:335
 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."
 msgstr ""
 
-#: option.c:248
+#: option.c:336
 #, c-format
 msgid "Specify the size of the cache in entries (defaults to %s)."
 msgstr ""
 
-#: option.c:249
+#: option.c:337
 #, c-format
 msgid "Specify configuration file (defaults to %s)."
 msgstr ""
 
-#: option.c:250
+#: option.c:338
 msgid "Do NOT fork into the background: run in debug mode."
 msgstr ""
 
-#: option.c:251
+#: option.c:339
 msgid "Do NOT forward queries with no domain part."
 msgstr ""
 
-#: option.c:252
+#: option.c:340
 msgid "Return self-pointing MX records for local hosts."
 msgstr ""
 
-#: option.c:253
+#: option.c:341
 msgid "Expand simple names in /etc/hosts with domain-suffix."
 msgstr ""
 
-#: option.c:254
+#: option.c:342
 msgid "Don't forward spurious DNS requests from Windows hosts."
 msgstr ""
 
-#: option.c:255
+#: option.c:343
 msgid "Enable DHCP in the range given with lease duration."
 msgstr ""
 
-#: option.c:256
+#: option.c:344
 #, c-format
 msgid "Change to this group after startup (defaults to %s)."
 msgstr ""
 
-#: option.c:257
+#: option.c:345
 msgid "Set address or hostname for a specified machine."
 msgstr ""
 
-#: option.c:258
+#: option.c:346
 msgid "Read DHCP host specs from file."
 msgstr ""
 
-#: option.c:259
+#: option.c:347
 msgid "Read DHCP option specs from file."
 msgstr ""
 
-#: option.c:260
+#: option.c:348
+msgid "Read DHCP host specs from a directory."
+msgstr ""
+
+#: option.c:349
+msgid "Read DHCP options from a directory."
+msgstr ""
+
+#: option.c:350
 msgid "Evaluate conditional tag expression."
 msgstr ""
 
-#: option.c:261
+#: option.c:351
 #, c-format
 msgid "Do NOT load %s file."
 msgstr ""
 
-#: option.c:262
+#: option.c:352
 #, c-format
 msgid "Specify a hosts file to be read in addition to %s."
 msgstr ""
 
-#: option.c:263
+#: option.c:353
+msgid "Read hosts files from a directory."
+msgstr ""
+
+#: option.c:354
 msgid "Specify interface(s) to listen on."
 msgstr ""
 
-#: option.c:264
+#: option.c:355
 msgid "Specify interface(s) NOT to listen on."
 msgstr ""
 
-#: option.c:265
+#: option.c:356
 msgid "Map DHCP user class to tag."
 msgstr ""
 
-#: option.c:266
+#: option.c:357
 msgid "Map RFC3046 circuit-id to tag."
 msgstr ""
 
-#: option.c:267
+#: option.c:358
 msgid "Map RFC3046 remote-id to tag."
 msgstr ""
 
-#: option.c:268
+#: option.c:359
 msgid "Map RFC3993 subscriber-id to tag."
 msgstr ""
 
-#: option.c:269
+#: option.c:360
 msgid "Don't do DHCP for hosts with tag set."
 msgstr ""
 
-#: option.c:270
+#: option.c:361
 msgid "Force broadcast replies for hosts with tag set."
 msgstr ""
 
-#: option.c:271
+#: option.c:362
 msgid "Do NOT fork into the background, do NOT run in debug mode."
 msgstr ""
 
-#: option.c:272
+#: option.c:363
 msgid "Assume we are the only DHCP server on the local network."
 msgstr ""
 
-#: option.c:273
+#: option.c:364
 #, c-format
 msgid "Specify where to store DHCP leases (defaults to %s)."
 msgstr ""
 
-#: option.c:274
+#: option.c:365
 msgid "Return MX records for local hosts."
 msgstr ""
 
-#: option.c:275
+#: option.c:366
 msgid "Specify an MX record."
 msgstr ""
 
-#: option.c:276
+#: option.c:367
 msgid "Specify BOOTP options to DHCP server."
 msgstr ""
 
-#: option.c:277
+#: option.c:368
 #, c-format
 msgid "Do NOT poll %s file, reload only on SIGHUP."
 msgstr ""
 
-#: option.c:278
+#: option.c:369
 msgid "Do NOT cache failed search results."
 msgstr ""
 
-#: option.c:279
+#: option.c:370
 #, c-format
 msgid "Use nameservers strictly in the order given in %s."
 msgstr ""
 
-#: option.c:280
+#: option.c:371
 msgid "Specify options to be sent to DHCP clients."
 msgstr ""
 
-#: option.c:281
+#: option.c:372
 msgid "DHCP option sent even if the client does not request it."
 msgstr ""
 
-#: option.c:282
+#: option.c:373
 msgid "Specify port to listen for DNS requests on (defaults to 53)."
 msgstr ""
 
-#: option.c:283
+#: option.c:374
 #, c-format
 msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)."
 msgstr ""
 
-#: option.c:284
+#: option.c:375
 msgid "Log DNS queries."
 msgstr ""
 
-#: option.c:285
+#: option.c:376
 msgid "Force the originating port for upstream DNS queries."
 msgstr ""
 
-#: option.c:286
+#: option.c:377
 msgid "Do NOT read resolv.conf."
 msgstr ""
 
-#: option.c:287
+#: option.c:378
 #, c-format
 msgid "Specify path to resolv.conf (defaults to %s)."
 msgstr ""
 
-#: option.c:288
+#: option.c:379
+msgid "Specify path to file with server= options"
+msgstr ""
+
+#: option.c:380
 msgid "Specify address(es) of upstream servers with optional domains."
 msgstr ""
 
-#: option.c:289
+#: option.c:381
+msgid "Specify address of upstream servers for reverse address queries"
+msgstr ""
+
+#: option.c:382
 msgid "Never forward queries to specified domains."
 msgstr ""
 
-#: option.c:290
+#: option.c:383
 msgid "Specify the domain to be assigned in DHCP leases."
 msgstr ""
 
-#: option.c:291
+#: option.c:384
 msgid "Specify default target in an MX record."
 msgstr ""
 
-#: option.c:292
+#: option.c:385
 msgid "Specify time-to-live in seconds for replies from /etc/hosts."
 msgstr ""
 
-#: option.c:293
+#: option.c:386
 msgid "Specify time-to-live in seconds for negative caching."
 msgstr ""
 
-#: option.c:294
+#: option.c:387
 msgid "Specify time-to-live in seconds for maximum TTL to send to clients."
 msgstr ""
 
-#: option.c:295
+#: option.c:388
+msgid "Specify time-to-live ceiling for cache."
+msgstr ""
+
+#: option.c:389
+msgid "Specify time-to-live floor for cache."
+msgstr ""
+
+#: option.c:390
 #, c-format
 msgid "Change to this user after startup. (defaults to %s)."
 msgstr ""
 
-#: option.c:296
+#: option.c:391
 msgid "Map DHCP vendor class to tag."
 msgstr ""
 
-#: option.c:297
+#: option.c:392
 msgid "Display dnsmasq version and copyright information."
 msgstr ""
 
-#: option.c:298
+#: option.c:393
 msgid "Translate IPv4 addresses from upstream servers."
 msgstr ""
 
-#: option.c:299
+#: option.c:394
 msgid "Specify a SRV record."
 msgstr ""
 
-#: option.c:300
+#: option.c:395
 msgid "Display this message. Use --help dhcp for known DHCP options."
 msgstr ""
 
-#: option.c:301
+#: option.c:396
 #, c-format
 msgid "Specify path of PID file (defaults to %s)."
 msgstr ""
 
-#: option.c:302
+#: option.c:397
 #, c-format
 msgid "Specify maximum number of DHCP leases (defaults to %s)."
 msgstr ""
 
-#: option.c:303
+#: option.c:398
 msgid "Answer DNS queries based on the interface a query was sent to."
 msgstr ""
 
-#: option.c:304
+#: option.c:399
 msgid "Specify TXT DNS record."
 msgstr ""
 
-#: option.c:305
+#: option.c:400
 msgid "Specify PTR DNS record."
 msgstr ""
 
-#: option.c:306
+#: option.c:401
 msgid "Give DNS name to IPv4 address of interface."
 msgstr ""
 
-#: option.c:307
+#: option.c:402
 msgid "Bind only to interfaces in use."
 msgstr ""
 
-#: option.c:308
+#: option.c:403
 #, c-format
 msgid "Read DHCP static host information from %s."
 msgstr ""
 
-#: option.c:309
+#: option.c:404
 msgid "Enable the DBus interface for setting upstream servers, etc."
 msgstr ""
 
-#: option.c:310
+#: option.c:405
 msgid "Do not provide DHCP on this interface, only provide DNS."
 msgstr ""
 
-#: option.c:311
+#: option.c:406
 msgid "Enable dynamic address allocation for bootp."
 msgstr ""
 
-#: option.c:312
+#: option.c:407
 msgid "Map MAC address (with wildcards) to option set."
 msgstr ""
 
-#: option.c:313
+#: option.c:408
 msgid "Treat DHCP requests on aliases as arriving from interface."
 msgstr ""
 
-#: option.c:314
+#: option.c:409
 msgid "Disable ICMP echo address checking in the DHCP server."
 msgstr ""
 
-#: option.c:315
-msgid "Script to run on DHCP lease creation and destruction."
+#: option.c:410
+msgid "Shell script to run on DHCP lease creation and destruction."
 msgstr ""
 
-#: option.c:316
+#: option.c:411
+msgid "Lua script to run on DHCP lease creation and destruction."
+msgstr ""
+
+#: option.c:412
+msgid "Run lease-change scripts as this user."
+msgstr ""
+
+#: option.c:413
 msgid "Read configuration from all the files in this directory."
 msgstr ""
 
-#: option.c:317
+#: option.c:414
 msgid "Log to this syslog facility or file. (defaults to DAEMON)"
 msgstr ""
 
-#: option.c:318
+#: option.c:415
 msgid "Do not use leasefile."
 msgstr ""
 
-#: option.c:319
+#: option.c:416
 #, c-format
 msgid "Maximum number of concurrent DNS queries. (defaults to %s)"
 msgstr ""
 
-#: option.c:320
+#: option.c:417
 #, c-format
 msgid "Clear DNS cache when reloading %s."
 msgstr ""
 
-#: option.c:321
+#: option.c:418
 msgid "Ignore hostnames provided by DHCP clients."
 msgstr ""
 
-#: option.c:322
+#: option.c:419
 msgid "Do NOT reuse filename and server fields for extra DHCP options."
 msgstr ""
 
-#: option.c:323
+#: option.c:420
 msgid "Enable integrated read-only TFTP server."
 msgstr ""
 
-#: option.c:324
+#: option.c:421
 msgid "Export files by TFTP only from the specified subtree."
 msgstr ""
 
-#: option.c:325
+#: option.c:422
 msgid "Add client IP address to tftp-root."
 msgstr ""
 
-#: option.c:326
+#: option.c:423
 msgid "Allow access only to files owned by the user running dnsmasq."
 msgstr ""
 
-#: option.c:327
+#: option.c:424
+msgid "Do not terminate the service if TFTP directories are inaccessible."
+msgstr ""
+
+#: option.c:425
 #, c-format
 msgid "Maximum number of conncurrent TFTP transfers (defaults to %s)."
 msgstr ""
 
-#: option.c:328
+#: option.c:426
 msgid "Disable the TFTP blocksize extension."
 msgstr ""
 
-#: option.c:329
+#: option.c:427
+msgid "Convert TFTP filenames to lowercase"
+msgstr ""
+
+#: option.c:428
 msgid "Ephemeral port range for use by TFTP transfers."
 msgstr ""
 
-#: option.c:330
+#: option.c:429
 msgid "Extra logging for DHCP."
 msgstr ""
 
-#: option.c:331
+#: option.c:430
 msgid "Enable async. logging; optionally set queue length."
 msgstr ""
 
-#: option.c:332
+#: option.c:431
 msgid "Stop DNS rebinding. Filter private IP ranges when resolving."
 msgstr ""
 
-#: option.c:333
+#: option.c:432
 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers."
 msgstr ""
 
-#: option.c:334
+#: option.c:433
 msgid "Inhibit DNS-rebind protection on this domain."
 msgstr ""
 
-#: option.c:335
+#: option.c:434
 msgid "Always perform DNS queries to all servers."
 msgstr ""
 
-#: option.c:336
+#: option.c:435
 msgid "Set tag if client includes matching option in request."
 msgstr ""
 
-#: option.c:337
+#: option.c:436
 msgid "Use alternative ports for DHCP."
 msgstr ""
 
-#: option.c:338
-msgid "Run lease-change script as this user."
-msgstr ""
-
-#: option.c:339
+#: option.c:437
 msgid "Specify NAPTR DNS record."
 msgstr ""
 
-#: option.c:340
+#: option.c:438
 msgid "Specify lowest port available for DNS query transmission."
 msgstr ""
 
-#: option.c:341
+#: option.c:439
 msgid "Use only fully qualified domain names for DHCP clients."
 msgstr ""
 
-#: option.c:342
+#: option.c:440
 msgid "Generate hostnames based on MAC address for nameless clients."
 msgstr ""
 
-#: option.c:343
+#: option.c:441
 msgid "Use these DHCP relays as full proxies."
 msgstr ""
 
-#: option.c:344
+#: option.c:442
+msgid "Relay DHCP requests to a remote server"
+msgstr ""
+
+#: option.c:443
 msgid "Specify alias name for LOCAL DNS name."
 msgstr ""
 
-#: option.c:345
+#: option.c:444
 msgid "Prompt to send to PXE clients."
 msgstr ""
 
-#: option.c:346
+#: option.c:445
 msgid "Boot service for PXE menu."
 msgstr ""
 
-#: option.c:347
+#: option.c:446
 msgid "Check configuration syntax."
 msgstr ""
 
-#: option.c:348
-msgid "Add requestor's MAC address to forwarded DNS queries"
+#: option.c:447
+msgid "Add requestor's MAC address to forwarded DNS queries."
 msgstr ""
 
-#: option.c:349
-msgid "Proxy DNSSEC validation results from upstream nameservers"
+#: option.c:448
+msgid "Add requestor's IP subnet to forwarded DNS queries."
+msgstr ""
+
+#: option.c:449
+msgid "Proxy DNSSEC validation results from upstream nameservers."
+msgstr ""
+
+#: option.c:450
+msgid "Attempt to allocate sequential IP addresses to DHCP clients."
+msgstr ""
+
+#: option.c:451
+msgid "Copy connection-track mark from queries to upstream connections."
+msgstr ""
+
+#: option.c:452
+msgid "Allow DHCP clients to do their own DDNS updates."
+msgstr ""
+
+#: option.c:453
+msgid "Send router-advertisements for interfaces doing DHCPv6"
+msgstr ""
+
+#: option.c:454
+msgid "Specify DUID_EN-type DHCPv6 server DUID"
+msgstr ""
+
+#: option.c:455
+msgid "Specify host (A/AAAA and PTR) records"
+msgstr ""
+
+#: option.c:456
+msgid "Specify arbitrary DNS resource record"
+msgstr ""
+
+#: option.c:457
+msgid "Bind to interfaces in use - check for new interfaces"
+msgstr ""
+
+#: option.c:458
+msgid "Export local names to global DNS"
+msgstr ""
+
+#: option.c:459
+msgid "Domain to export to global DNS"
+msgstr ""
+
+#: option.c:460
+msgid "Set TTL for authoritative replies"
+msgstr ""
+
+#: option.c:461
+msgid "Set authoritive zone information"
+msgstr ""
+
+#: option.c:462
+msgid "Secondary authoritative nameservers for forward domains"
+msgstr ""
+
+#: option.c:463
+msgid "Peers which are allowed to do zone transfer"
+msgstr ""
+
+#: option.c:464
+msgid "Specify ipsets to which matching domains should be added"
+msgstr ""
+
+#: option.c:465
+msgid "Specify a domain and address range for synthesised names"
+msgstr ""
+
+#: option.c:466
+msgid "Activate DNSSEC validation"
+msgstr ""
+
+#: option.c:467
+msgid "Specify trust anchor key digest."
+msgstr ""
+
+#: option.c:468
+msgid "Disable upstream checking for DNSSEC debugging."
+msgstr ""
+
+#: option.c:469
+msgid "Ensure answers without DNSSEC are in unsigned zones."
+msgstr ""
+
+#: option.c:470
+msgid "Don't check DNSSEC signature timestamps until first cache-reload"
+msgstr ""
+
+#: option.c:471
+msgid "Timestamp file to verify system clock for DNSSEC"
+msgstr ""
+
+#: option.c:473
+msgid "Specify DHCPv6 prefix class"
+msgstr ""
+
+#: option.c:475
+msgid "Set priority, resend-interval and router-lifetime"
+msgstr ""
+
+#: option.c:476
+msgid "Do not log routine DHCP."
+msgstr ""
+
+#: option.c:477
+msgid "Do not log routine DHCPv6."
+msgstr ""
+
+#: option.c:478
+msgid "Do not log RA."
 msgstr ""
 
-#: option.c:638
+#: option.c:479
+msgid "Accept queries only from directly-connected networks"
+msgstr ""
+
+#: option.c:480
+msgid "Detect and remove DNS forwarding loops"
+msgstr ""
+
+#: option.c:481
+msgid "Ignore DNS responses containing ipaddr."
+msgstr ""
+
+#: option.c:683
 #, c-format
 msgid ""
 "Usage: dnsmasq [options]\n"
 "\n"
 msgstr ""
 
-#: option.c:640
+#: option.c:685
 #, c-format
 msgid "Use short options only on the command line.\n"
 msgstr ""
 
-#: option.c:642
+#: option.c:687
 #, c-format
 msgid "Valid options are:\n"
 msgstr ""
 
-#: option.c:683
-#, c-format
-msgid "Known DHCP options:\n"
+#: option.c:744 option.c:748
+msgid "bad port"
+msgstr ""
+
+#: option.c:775 option.c:807
+msgid "interface binding not supported"
+msgstr ""
+
+#: option.c:784 option.c:3575
+msgid "bad interface name"
+msgstr ""
+
+#: option.c:814
+msgid "bad address"
+msgstr ""
+
+#: option.c:996
+msgid "unsupported encapsulation for IPv6 option"
 msgstr ""
 
-#: option.c:798
+#: option.c:1010
 msgid "bad dhcp-option"
 msgstr ""
 
-#: option.c:860
+#: option.c:1078
 msgid "bad IP address"
 msgstr ""
 
-#: option.c:968
+#: option.c:1081 option.c:1219 option.c:2893
+msgid "bad IPv6 address"
+msgstr ""
+
+#: option.c:1246 option.c:1340
 msgid "bad domain in dhcp-option"
 msgstr ""
 
-#: option.c:1034
+#: option.c:1378
 msgid "dhcp-option too long"
 msgstr ""
 
-#: option.c:1043
+#: option.c:1385
 msgid "illegal dhcp-match"
 msgstr ""
 
-#: option.c:1087
+#: option.c:1447
 msgid "illegal repeated flag"
 msgstr ""
 
-#: option.c:1095
+#: option.c:1455
 msgid "illegal repeated keyword"
 msgstr ""
 
-#: option.c:1147 option.c:3030
+#: option.c:1520 option.c:4191
 #, c-format
 msgid "cannot access directory %s: %s"
 msgstr ""
 
-#: option.c:1178 tftp.c:460
+#: option.c:1566 tftp.c:493
 #, c-format
 msgid "cannot access %s: %s"
 msgstr ""
 
-#: option.c:1207
+#: option.c:1618
 msgid "setting log facility is not possible under Android"
 msgstr ""
 
-#: option.c:1216
+#: option.c:1627
 msgid "bad log facility"
 msgstr ""
 
-#: option.c:1265
+#: option.c:1680
 msgid "bad MX preference"
 msgstr ""
 
-#: option.c:1270
+#: option.c:1685
 msgid "bad MX name"
 msgstr ""
 
-#: option.c:1284
+#: option.c:1699
 msgid "bad MX target"
 msgstr ""
 
-#: option.c:1294
+#: option.c:1711
 msgid "cannot run scripts under uClinux"
 msgstr ""
 
-#: option.c:1296
+#: option.c:1713
 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts"
 msgstr ""
 
-#: option.c:1597 option.c:1601
-msgid "bad port"
+#: option.c:1717
+msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts"
 msgstr ""
 
-#: option.c:1620 option.c:1645
-msgid "interface binding not supported"
+#: option.c:1973 option.c:2018 option.c:2074
+msgid "bad prefix"
+msgstr ""
+
+#: option.c:2355
+msgid "recompile with HAVE_IPSET defined to enable ipset directives"
 msgstr ""
 
-#: option.c:1791
+#: option.c:2548
 msgid "bad port range"
 msgstr ""
 
-#: option.c:1808
+#: option.c:2564
 msgid "bad bridge-interface"
 msgstr ""
 
-#: option.c:1850
-msgid "bad dhcp-range"
+#: option.c:2624
+msgid "only one tag allowed"
 msgstr ""
 
-#: option.c:1878
-msgid "only one tag allowed"
+#: option.c:2644 option.c:2656 option.c:2764 option.c:2805
+msgid "bad dhcp-range"
 msgstr ""
 
-#: option.c:1925
+#: option.c:2671
 msgid "inconsistent DHCP range"
 msgstr ""
 
-#: option.c:2019 option.c:2045
+#: option.c:2732
+msgid "prefix length must be exactly 64 for RA subnets"
+msgstr ""
+
+#: option.c:2734
+msgid "prefix length must be exactly 64 for subnet constructors"
+msgstr ""
+
+#: option.c:2738
+msgid "prefix length must be at least 64"
+msgstr ""
+
+#: option.c:2741
+msgid "inconsistent DHCPv6 range"
+msgstr ""
+
+#: option.c:2752
+msgid "prefix must be zero with \"constructor:\" argument"
+msgstr ""
+
+#: option.c:2863 option.c:2911
 msgid "bad hex constant"
 msgstr ""
 
-#: option.c:2107
+#: option.c:2885
+msgid "cannot match tags in --dhcp-host"
+msgstr ""
+
+#: option.c:2933
+#, c-format
+msgid "duplicate dhcp-host IP address %s"
+msgstr ""
+
+#: option.c:2991
 msgid "bad DHCP host name"
 msgstr ""
 
-#: option.c:2188
+#: option.c:3073
 msgid "bad tag-if"
 msgstr ""
 
-#: option.c:2467 option.c:2752
+#: option.c:3397 option.c:3791
 msgid "invalid port number"
 msgstr ""
 
-#: option.c:2529
+#: option.c:3459
 msgid "bad dhcp-proxy address"
 msgstr ""
 
-#: option.c:2569
-msgid "invalid alias range"
+#: option.c:3485
+msgid "Bad dhcp-relay"
 msgstr ""
 
-#: option.c:2582
-msgid "bad interface name"
+#: option.c:3511
+msgid "bad RA-params"
+msgstr ""
+
+#: option.c:3520
+msgid "bad DUID"
+msgstr ""
+
+#: option.c:3562
+msgid "invalid alias range"
 msgstr ""
 
-#: option.c:2607
+#: option.c:3616
 msgid "bad CNAME"
 msgstr ""
 
-#: option.c:2612
+#: option.c:3621
 msgid "duplicate CNAME"
 msgstr ""
 
-#: option.c:2632
+#: option.c:3641
 msgid "bad PTR record"
 msgstr ""
 
-#: option.c:2663
+#: option.c:3672
 msgid "bad NAPTR record"
 msgstr ""
 
-#: option.c:2695
+#: option.c:3706
+msgid "bad RR record"
+msgstr ""
+
+#: option.c:3736
 msgid "bad TXT record"
 msgstr ""
 
-#: option.c:2738
+#: option.c:3777
 msgid "bad SRV record"
 msgstr ""
 
-#: option.c:2745
+#: option.c:3784
 msgid "bad SRV target"
 msgstr ""
 
-#: option.c:2759
+#: option.c:3798
 msgid "invalid priority"
 msgstr ""
 
-#: option.c:2766
+#: option.c:3805
 msgid "invalid weight"
 msgstr ""
 
-#: option.c:2785
-msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"
+#: option.c:3829
+msgid "Bad host-record"
+msgstr ""
+
+#: option.c:3846
+msgid "Bad name in host-record"
+msgstr ""
+
+#: option.c:3911
+msgid "bad trust anchor"
+msgstr ""
+
+#: option.c:3925
+msgid "bad HEX in trust anchor"
 msgstr ""
 
-#: option.c:2849
+#: option.c:3935
+msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"
+msgstr ""
+
+#: option.c:3994
 msgid "missing \""
 msgstr ""
 
-#: option.c:2908
+#: option.c:4051
 msgid "bad option"
 msgstr ""
 
-#: option.c:2910
+#: option.c:4053
 msgid "extraneous parameter"
 msgstr ""
 
-#: option.c:2912
+#: option.c:4055
 msgid "missing parameter"
 msgstr ""
 
-#: option.c:2916
+#: option.c:4057
+msgid "illegal option"
+msgstr ""
+
+#: option.c:4064
 msgid "error"
 msgstr ""
 
-#: option.c:2921
+#: option.c:4066
 #, c-format
-msgid "%s at line %d of %%s"
+msgid " at line %d of %s"
 msgstr ""
 
-#: option.c:2985 tftp.c:624
+#: option.c:4081 option.c:4328 option.c:4364
 #, c-format
-msgid "cannot read %s: %s"
+msgid "read %s"
 msgstr ""
 
-#: option.c:3151 option.c:3187
+#: option.c:4144 option.c:4267 tftp.c:667
 #, c-format
-msgid "read %s"
+msgid "cannot read %s: %s"
 msgstr ""
 
-#: option.c:3239
+#: option.c:4430
 msgid "junk found in command line"
 msgstr ""
 
-#: option.c:3269
+#: option.c:4465
 #, c-format
 msgid "Dnsmasq version %s  %s\n"
 msgstr ""
 
-#: option.c:3270
+#: option.c:4466
 #, c-format
 msgid ""
-"Compile time options %s\n"
+"Compile time options: %s\n"
 "\n"
 msgstr ""
 
-#: option.c:3271
+#: option.c:4467
 #, c-format
 msgid "This software comes with ABSOLUTELY NO WARRANTY.\n"
 msgstr ""
 
-#: option.c:3272
+#: option.c:4468
 #, c-format
 msgid "Dnsmasq is free software, and you are welcome to redistribute it\n"
 msgstr ""
 
-#: option.c:3273
+#: option.c:4469
 #, c-format
 msgid "under the terms of the GNU General Public License, version 2 or 3.\n"
 msgstr ""
 
-#: option.c:3284
+#: option.c:4480
 msgid "try --help"
 msgstr ""
 
-#: option.c:3286
+#: option.c:4482
 msgid "try -w"
 msgstr ""
 
-#: option.c:3289
+#: option.c:4484
 #, c-format
 msgid "bad command line options: %s"
 msgstr ""
 
-#: option.c:3330
+#: option.c:4541
 #, c-format
 msgid "cannot get host-name: %s"
 msgstr ""
 
-#: option.c:3358
+#: option.c:4569
 msgid "only one resolv.conf file allowed in no-poll mode."
 msgstr ""
 
-#: option.c:3368
+#: option.c:4579
 msgid "must have exactly one resolv.conf to read domain from."
 msgstr ""
 
-#: option.c:3371 network.c:848 dhcp.c:814
+#: option.c:4582 network.c:1507 dhcp.c:777
 #, c-format
 msgid "failed to read %s: %s"
 msgstr ""
 
-#: option.c:3388
+#: option.c:4599
 #, c-format
 msgid "no search directive found in %s"
 msgstr ""
 
-#: option.c:3409
+#: option.c:4620
 msgid "there must be a default domain when --dhcp-fqdn is set"
 msgstr ""
 
-#: option.c:3413
+#: option.c:4629
 msgid "syntax check OK"
 msgstr ""
 
-#: forward.c:461
+#: forward.c:111
+#, c-format
+msgid "failed to send packet: %s"
+msgstr ""
+
+#: forward.c:591
+msgid "discarding DNS reply: subnet option mismatch"
+msgstr ""
+
+#: forward.c:614
 #, c-format
 msgid "nameserver %s refused to do a recursive query"
 msgstr ""
 
-#: forward.c:489
+#: forward.c:646
 #, c-format
 msgid "possible DNS-rebind attack detected: %s"
 msgstr ""
 
-#: network.c:171
+#: forward.c:1209 forward.c:1815
+msgid "Ignoring query from non-local network"
+msgstr ""
+
+#: forward.c:2286
 #, c-format
-msgid "unknown interface %s in bridge-interface"
+msgid "Maximum number of concurrent DNS queries reached (max: %d)"
 msgstr ""
 
-#: network.c:380
+#: network.c:715
 #, c-format
 msgid "failed to create listening socket for %s: %s"
 msgstr ""
 
-#: network.c:746
+#: network.c:1021
+#, c-format
+msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s"
+msgstr ""
+
+#: network.c:1028
+msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)"
+msgstr ""
+
+#: network.c:1037
+#, c-format
+msgid "warning: no addresses found for interface %s"
+msgstr ""
+
+#: network.c:1095
+#, c-format
+msgid "interface %s failed to join DHCPv6 multicast group: %s"
+msgstr ""
+
+#: network.c:1289
 #, c-format
 msgid "failed to bind server socket for %s: %s"
 msgstr ""
 
-#: network.c:783
+#: network.c:1445
 #, c-format
 msgid "ignoring nameserver %s - local interface"
 msgstr ""
 
-#: network.c:794
+#: network.c:1456
 #, c-format
 msgid "ignoring nameserver %s - cannot make/bind socket: %s"
 msgstr ""
 
-#: network.c:811
+#: network.c:1469
 msgid "unqualified"
 msgstr ""
 
-#: network.c:811
+#: network.c:1469
 msgid "names"
 msgstr ""
 
-#: network.c:813
+#: network.c:1471
 msgid "default"
 msgstr ""
 
-#: network.c:815
+#: network.c:1473
 msgid "domain"
 msgstr ""
 
-#: network.c:818
+#: network.c:1476
 #, c-format
 msgid "using local addresses only for %s %s"
 msgstr ""
 
-#: network.c:820
+#: network.c:1478
 #, c-format
 msgid "using standard nameservers for %s %s"
 msgstr ""
 
-#: network.c:822
+#: network.c:1480
 #, c-format
 msgid "using nameserver %s#%d for %s %s"
 msgstr ""
 
-#: network.c:825
+#: network.c:1484
+#, c-format
+msgid "NOT using nameserver %s#%d - query loop detected"
+msgstr ""
+
+#: network.c:1487
 #, c-format
 msgid "using nameserver %s#%d(via %s)"
 msgstr ""
 
-#: network.c:827
+#: network.c:1489
 #, c-format
 msgid "using nameserver %s#%d"
 msgstr ""
 
-#: dnsmasq.c:148
+#: dnsmasq.c:163
+msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"
+msgstr ""
+
+#: dnsmasq.c:170
+msgid "no trust anchors provided for DNSSEC"
+msgstr ""
+
+#: dnsmasq.c:173
+msgid "cannot reduce cache size from default when DNSSEC enabled"
+msgstr ""
+
+#: dnsmasq.c:175
+msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h"
+msgstr ""
+
+#: dnsmasq.c:181
 msgid "TFTP server not available: set HAVE_TFTP in src/config.h"
 msgstr ""
 
-#: dnsmasq.c:153
+#: dnsmasq.c:186
+msgid "cannot use --conntrack AND --query-port"
+msgstr ""
+
+#: dnsmasq.c:189
+msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h"
+msgstr ""
+
+#: dnsmasq.c:194
 msgid "asychronous logging is not available under Solaris"
 msgstr ""
 
-#: dnsmasq.c:158
+#: dnsmasq.c:199
 msgid "asychronous logging is not available under Android"
 msgstr ""
 
-#: dnsmasq.c:177
-#, c-format
-msgid "failed to find list of interfaces: %s"
+#: dnsmasq.c:204
+msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h"
+msgstr ""
+
+#: dnsmasq.c:209
+msgid "loop detection not available: set HAVE_LOOP in src/config.h"
+msgstr ""
+
+#: dnsmasq.c:217
+msgid "zone serial must be configured in --auth-soa"
 msgstr ""
 
-#: dnsmasq.c:185
+#: dnsmasq.c:235
+msgid "dhcp-range constructor not available on this platform"
+msgstr ""
+
+#: dnsmasq.c:278
+msgid "cannot set --bind-interfaces and --bind-dynamic"
+msgstr ""
+
+#: dnsmasq.c:281
 #, c-format
-msgid "unknown interface %s"
+msgid "failed to find list of interfaces: %s"
 msgstr ""
 
-#: dnsmasq.c:191
+#: dnsmasq.c:290
 #, c-format
-msgid "no interface with address %s"
+msgid "unknown interface %s"
 msgstr ""
 
-#: dnsmasq.c:207 dnsmasq.c:678
+#: dnsmasq.c:354 dnsmasq.c:997
 #, c-format
 msgid "DBus error: %s"
 msgstr ""
 
-#: dnsmasq.c:210
+#: dnsmasq.c:357
 msgid "DBus not available: set HAVE_DBUS in src/config.h"
 msgstr ""
 
-#: dnsmasq.c:236
+#: dnsmasq.c:385
 #, c-format
 msgid "unknown user or group: %s"
 msgstr ""
 
-#: dnsmasq.c:291
+#: dnsmasq.c:440
 #, c-format
 msgid "cannot chdir to filesystem root: %s"
 msgstr ""
 
-#: dnsmasq.c:455
+#: dnsmasq.c:692
 #, c-format
 msgid "started, version %s DNS disabled"
 msgstr ""
 
-#: dnsmasq.c:457
+#: dnsmasq.c:694
 #, c-format
 msgid "started, version %s cachesize %d"
 msgstr ""
 
-#: dnsmasq.c:459
+#: dnsmasq.c:696
 #, c-format
 msgid "started, version %s cache disabled"
 msgstr ""
 
-#: dnsmasq.c:461
+#: dnsmasq.c:698
 #, c-format
 msgid "compile time options: %s"
 msgstr ""
 
-#: dnsmasq.c:467
+#: dnsmasq.c:704
 msgid "DBus support enabled: connected to system bus"
 msgstr ""
 
-#: dnsmasq.c:469
+#: dnsmasq.c:706
 msgid "DBus support enabled: bus connection pending"
 msgstr ""
 
-#: dnsmasq.c:474
+#: dnsmasq.c:711
+msgid "DNS service limited to local subnets"
+msgstr ""
+
+#: dnsmasq.c:727
+msgid "DNSSEC validation enabled"
+msgstr ""
+
+#: dnsmasq.c:730
+msgid "DNSSEC signature timestamps not checked until first cache reload"
+msgstr ""
+
+#: dnsmasq.c:733
+msgid "DNSSEC signature timestamps not checked until system time valid"
+msgstr ""
+
+#: dnsmasq.c:738
 #, c-format
 msgid "warning: failed to change owner of %s: %s"
 msgstr ""
 
-#: dnsmasq.c:478
+#: dnsmasq.c:742
 msgid "setting --bind-interfaces option because of OS limitations"
 msgstr ""
 
-#: dnsmasq.c:483
+#: dnsmasq.c:752
 #, c-format
 msgid "warning: interface %s does not currently exist"
 msgstr ""
 
-#: dnsmasq.c:488
+#: dnsmasq.c:757
 msgid "warning: ignoring resolv-file flag because no-resolv is set"
 msgstr ""
 
-#: dnsmasq.c:491
+#: dnsmasq.c:760
 msgid "warning: no upstream servers configured"
 msgstr ""
 
-#: dnsmasq.c:495
+#: dnsmasq.c:764
 #, c-format
 msgid "asynchronous logging enabled, queue limit is %d messages"
 msgstr ""
 
-#: dnsmasq.c:508
-#, c-format
-msgid "DHCP, static leases only on %.0s%s, lease time %s"
-msgstr ""
-
-#: dnsmasq.c:510
-#, c-format
-msgid "DHCP, proxy on subnet %.0s%s%.0s"
+#: dnsmasq.c:785
+msgid "IPv6 router advertisement enabled"
 msgstr ""
 
-#: dnsmasq.c:511
+#: dnsmasq.c:790
 #, c-format
-msgid "DHCP, IP range %s -- %s, lease time %s"
+msgid "DHCP, sockets bound exclusively to interface %s"
 msgstr ""
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 msgid "root is "
 msgstr ""
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 msgid "enabled"
 msgstr ""
 
-#: dnsmasq.c:528
+#: dnsmasq.c:806
 msgid "secure mode"
 msgstr ""
 
-#: dnsmasq.c:554
+#: dnsmasq.c:809
+#, c-format
+msgid "warning: %s inaccessible"
+msgstr ""
+
+#: dnsmasq.c:813
+#, c-format
+msgid "warning: TFTP directory %s inaccessible"
+msgstr ""
+
+#: dnsmasq.c:839
 #, c-format
 msgid "restricting maximum simultaneous TFTP transfers to %d"
 msgstr ""
 
-#: dnsmasq.c:680
+#: dnsmasq.c:999
 msgid "connected to system DBus"
 msgstr ""
 
-#: dnsmasq.c:775
+#: dnsmasq.c:1149
 #, c-format
 msgid "cannot fork into background: %s"
 msgstr ""
 
-#: dnsmasq.c:778
+#: dnsmasq.c:1152
 #, c-format
 msgid "failed to create helper: %s"
 msgstr ""
 
-#: dnsmasq.c:781
+#: dnsmasq.c:1155
 #, c-format
 msgid "setting capabilities failed: %s"
 msgstr ""
 
-#: dnsmasq.c:785
+#: dnsmasq.c:1158
 #, c-format
 msgid "failed to change user-id to %s: %s"
 msgstr ""
 
-#: dnsmasq.c:790
+#: dnsmasq.c:1161
 #, c-format
 msgid "failed to change group-id to %s: %s"
 msgstr ""
 
-#: dnsmasq.c:793
+#: dnsmasq.c:1164
 #, c-format
 msgid "failed to open pidfile %s: %s"
 msgstr ""
 
-#: dnsmasq.c:796
+#: dnsmasq.c:1167
+#, c-format
+msgid "cannot open log %s: %s"
+msgstr ""
+
+#: dnsmasq.c:1170
+#, c-format
+msgid "failed to load Lua script: %s"
+msgstr ""
+
+#: dnsmasq.c:1173
 #, c-format
-msgid "cannot open %s: %s"
+msgid "TFTP directory %s inaccessible: %s"
+msgstr ""
+
+#: dnsmasq.c:1176
+#, c-format
+msgid "cannot create timestamp file %s: %s"
+msgstr ""
+
+#: dnsmasq.c:1197
+msgid "now checking DNSSEC signature timestamps"
 msgstr ""
 
-#: dnsmasq.c:851
+#: dnsmasq.c:1264
 #, c-format
-msgid "child process killed by signal %d"
+msgid "script process killed by signal %d"
 msgstr ""
 
-#: dnsmasq.c:855
+#: dnsmasq.c:1268
 #, c-format
-msgid "child process exited with status %d"
+msgid "script process exited with status %d"
 msgstr ""
 
-#: dnsmasq.c:859
+#: dnsmasq.c:1272
 #, c-format
 msgid "failed to execute %s: %s"
 msgstr ""
 
-#: dnsmasq.c:903
+#: dnsmasq.c:1327
 msgid "exiting on receipt of SIGTERM"
 msgstr ""
 
-#: dnsmasq.c:931
+#: dnsmasq.c:1355
 #, c-format
 msgid "failed to access %s: %s"
 msgstr ""
 
-#: dnsmasq.c:961
+#: dnsmasq.c:1385
 #, c-format
 msgid "reading %s"
 msgstr ""
 
-#: dnsmasq.c:972
+#: dnsmasq.c:1396
 #, c-format
 msgid "no servers found in %s, will retry"
 msgstr ""
 
-#: dhcp.c:40
+#: dhcp.c:53
 #, c-format
 msgid "cannot create DHCP socket: %s"
 msgstr ""
 
-#: dhcp.c:52
+#: dhcp.c:68
 #, c-format
 msgid "failed to set options on DHCP socket: %s"
 msgstr ""
 
-#: dhcp.c:65
+#: dhcp.c:89
 #, c-format
 msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"
 msgstr ""
 
-#: dhcp.c:77
+#: dhcp.c:101
 #, c-format
 msgid "failed to bind DHCP server socket: %s"
 msgstr ""
 
-#: dhcp.c:103
+#: dhcp.c:127
 #, c-format
 msgid "cannot create ICMP raw socket: %s."
 msgstr ""
 
-#: dhcp.c:281
-#, c-format
-msgid "DHCP packet received on %s which has no address"
-msgstr ""
-
-#: dhcp.c:445
+#: dhcp.c:241 dhcp6.c:180
 #, c-format
-msgid "DHCP range %s -- %s is not consistent with netmask %s"
+msgid "unknown interface %s in bridge-interface"
 msgstr ""
 
-#: dhcp.c:852
+#: dhcp.c:281
 #, c-format
-msgid "bad line at %s line %d"
+msgid "DHCP packet received on %s which has no address"
 msgstr ""
 
-#: dhcp.c:895
+#: dhcp.c:415
 #, c-format
-msgid "ignoring %s line %d, duplicate name or IP address"
+msgid "ARP-cache injection failed: %s"
 msgstr ""
 
-#: dhcp.c:978
+#: dhcp.c:514
 #, c-format
-msgid "duplicate IP address %s in dhcp-config directive."
+msgid "DHCP range %s -- %s is not consistent with netmask %s"
 msgstr ""
 
-#: dhcp.c:981
+#: dhcp.c:815
 #, c-format
-msgid "duplicate IP address %s in %s."
+msgid "bad line at %s line %d"
 msgstr ""
 
-#: dhcp.c:1024
+#: dhcp.c:858
 #, c-format
-msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgid "ignoring %s line %d, duplicate name or IP address"
 msgstr ""
 
-#: dhcp.c:1029
+#: dhcp.c:1002 rfc3315.c:2135
 #, c-format
-msgid "duplicate IP address %s (%s) in dhcp-config directive"
+msgid "DHCP relay %s -> %s"
 msgstr ""
 
-#: lease.c:67
+#: lease.c:61
 #, c-format
 msgid "cannot open or create lease file %s: %s"
 msgstr ""
 
-#: lease.c:93
+#: lease.c:134
 msgid "too many stored leases"
 msgstr ""
 
-#: lease.c:129
+#: lease.c:165
 #, c-format
 msgid "cannot run lease-init script %s: %s"
 msgstr ""
 
-#: lease.c:135
+#: lease.c:171
 #, c-format
 msgid "lease-init script returned exit code %s"
 msgstr ""
 
-#: lease.c:235
+#: lease.c:342
 #, c-format
 msgid "failed to write %s: %s (retry in %us)"
 msgstr ""
 
-#: rfc2131.c:315
+#: lease.c:906
+#, c-format
+msgid "Ignoring domain %s for DHCP host name %s"
+msgstr ""
+
+#: rfc2131.c:344
 #, c-format
 msgid "no address range available for DHCP request %s %s"
 msgstr ""
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "with subnet selector"
 msgstr ""
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "via"
 msgstr ""
 
-#: rfc2131.c:331
+#: rfc2131.c:357
 #, c-format
 msgid "%u available DHCP subnet: %s/%s"
 msgstr ""
 
-#: rfc2131.c:334
+#: rfc2131.c:360 rfc3315.c:300
 #, c-format
 msgid "%u available DHCP range: %s -- %s"
 msgstr ""
 
-#: rfc2131.c:363
+#: rfc2131.c:471
+#, c-format
+msgid "%u vendor class: %s"
+msgstr ""
+
+#: rfc2131.c:473
+#, c-format
+msgid "%u user class: %s"
+msgstr ""
+
+#: rfc2131.c:500
 msgid "disabled"
 msgstr ""
 
-#: rfc2131.c:404 rfc2131.c:916 rfc2131.c:1288
+#: rfc2131.c:541 rfc2131.c:974 rfc2131.c:1380 rfc3315.c:603 rfc3315.c:856
+#: rfc3315.c:1135
 msgid "ignored"
 msgstr ""
 
-#: rfc2131.c:419 rfc2131.c:1135
+#: rfc2131.c:556 rfc2131.c:1207 rfc3315.c:906
 msgid "address in use"
 msgstr ""
 
-#: rfc2131.c:433 rfc2131.c:970
+#: rfc2131.c:570 rfc2131.c:1028
 msgid "no address available"
 msgstr ""
 
-#: rfc2131.c:440 rfc2131.c:1098
+#: rfc2131.c:577 rfc2131.c:1170
 msgid "wrong network"
 msgstr ""
 
-#: rfc2131.c:454
+#: rfc2131.c:592
 msgid "no address configured"
 msgstr ""
 
-#: rfc2131.c:460 rfc2131.c:1148
+#: rfc2131.c:598 rfc2131.c:1220
 msgid "no leases left"
 msgstr ""
 
-#: rfc2131.c:545
+#: rfc2131.c:693 rfc3315.c:476
 #, c-format
 msgid "%u client provides name: %s"
 msgstr ""
 
-#: rfc2131.c:700
-#, c-format
-msgid "%u vendor class: %s"
-msgstr ""
-
-#: rfc2131.c:702
-#, c-format
-msgid "%u user class: %s"
-msgstr ""
-
-#: rfc2131.c:761
+#: rfc2131.c:798
 msgid "PXE BIS not supported"
 msgstr ""
 
-#: rfc2131.c:886
+#: rfc2131.c:942 rfc3315.c:1229
 #, c-format
 msgid "disabling DHCP static address %s for %s"
 msgstr ""
 
-#: rfc2131.c:907
+#: rfc2131.c:963
 msgid "unknown lease"
 msgstr ""
 
-#: rfc2131.c:939
+#: rfc2131.c:997
 #, c-format
 msgid "not using configured address %s because it is leased to %s"
 msgstr ""
 
-#: rfc2131.c:949
+#: rfc2131.c:1007
 #, c-format
 msgid "not using configured address %s because it is in use by the server or relay"
 msgstr ""
 
-#: rfc2131.c:952
+#: rfc2131.c:1010
 #, c-format
 msgid "not using configured address %s because it was previously declined"
 msgstr ""
 
-#: rfc2131.c:968 rfc2131.c:1141
+#: rfc2131.c:1026 rfc2131.c:1213
 msgid "no unique-id"
 msgstr ""
 
-#: rfc2131.c:1037
+#: rfc2131.c:1108
 msgid "wrong server-ID"
 msgstr ""
 
-#: rfc2131.c:1055
+#: rfc2131.c:1127
 msgid "wrong address"
 msgstr ""
 
-#: rfc2131.c:1073
+#: rfc2131.c:1145 rfc3315.c:1002
 msgid "lease not found"
 msgstr ""
 
-#: rfc2131.c:1106
+#: rfc2131.c:1178
 msgid "address not available"
 msgstr ""
 
-#: rfc2131.c:1117
+#: rfc2131.c:1189
 msgid "static lease available"
 msgstr ""
 
-#: rfc2131.c:1121
+#: rfc2131.c:1193
 msgid "address reserved"
 msgstr ""
 
-#: rfc2131.c:1129
+#: rfc2131.c:1201
 #, c-format
 msgid "abandoning lease to %s of %s"
 msgstr ""
 
-#: rfc2131.c:1710
-#, c-format
-msgid "%u tags: %s"
-msgstr ""
-
-#: rfc2131.c:1723
+#: rfc2131.c:1707
 #, c-format
 msgid "%u bootfile name: %s"
 msgstr ""
 
-#: rfc2131.c:1732
+#: rfc2131.c:1716
 #, c-format
 msgid "%u server name: %s"
 msgstr ""
 
-#: rfc2131.c:1746
+#: rfc2131.c:1724
 #, c-format
 msgid "%u next server: %s"
 msgstr ""
 
-#: rfc2131.c:1749
+#: rfc2131.c:1727
 #, c-format
 msgid "%u broadcast response"
 msgstr ""
 
-#: rfc2131.c:1812
+#: rfc2131.c:1790
 #, c-format
 msgid "cannot send DHCP/BOOTP option %d: no space left in packet"
 msgstr ""
 
-#: rfc2131.c:2058
+#: rfc2131.c:2031
 msgid "PXE menu too large"
 msgstr ""
 
-#: rfc2131.c:2171
-#, c-format
-msgid "Ignoring domain %s for DHCP host name %s"
-msgstr ""
-
-#: rfc2131.c:2189
+#: rfc2131.c:2170 rfc3315.c:1502
 #, c-format
 msgid "%u requested options: %s"
 msgstr ""
 
-#: rfc2131.c:2456
+#: rfc2131.c:2487
 #, c-format
 msgid "cannot send RFC3925 option: too many options for enterprise number %d"
 msgstr ""
 
-#: netlink.c:70
+#: netlink.c:77
 #, c-format
 msgid "cannot create netlink socket: %s"
 msgstr ""
 
-#: netlink.c:288
+#: netlink.c:348
 #, c-format
 msgid "netlink returns error: %s"
 msgstr ""
 
-#: dbus.c:150
+#: dbus.c:186
 msgid "attempt to set an IPv6 server address via DBus - no IPv6 support"
 msgstr ""
 
-#: dbus.c:286
+#: dbus.c:439
+#, c-format
+msgid "Enabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:444
+#, c-format
+msgid "Disabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:691
 msgid "setting upstream servers from DBus"
 msgstr ""
 
-#: dbus.c:324
+#: dbus.c:738
 msgid "could not register a DBus message handler"
 msgstr ""
 
-#: bpf.c:217
+#: bpf.c:263
 #, c-format
 msgid "cannot create DHCP BPF socket: %s"
 msgstr ""
 
-#: bpf.c:245
+#: bpf.c:291
 #, c-format
 msgid "DHCP request for unsupported hardware type (%d) received on %s"
 msgstr ""
 
-#: tftp.c:281
+#: bpf.c:376
+#, c-format
+msgid "cannot create PF_ROUTE socket: %s"
+msgstr ""
+
+#: bpf.c:397
+msgid "Unknown protocol version from route socket"
+msgstr ""
+
+#: helper.c:153
+msgid "lease() function missing in Lua script"
+msgstr ""
+
+#: tftp.c:309
 msgid "unable to get free port for TFTP"
 msgstr ""
 
-#: tftp.c:296
+#: tftp.c:325
 #, c-format
 msgid "unsupported request from %s"
 msgstr ""
 
-#: tftp.c:406
+#: tftp.c:439
 #, c-format
 msgid "file %s not found"
 msgstr ""
 
-#: tftp.c:522
+#: tftp.c:548
 #, c-format
 msgid "error %d %s received from %s"
 msgstr ""
 
-#: tftp.c:554
+#: tftp.c:590
 #, c-format
 msgid "failed sending %s to %s"
 msgstr ""
 
-#: tftp.c:568
+#: tftp.c:590
 #, c-format
 msgid "sent %s to %s"
 msgstr ""
 
-#: log.c:177
+#: log.c:190
 #, c-format
 msgid "overflow: %d log entries lost"
 msgstr ""
 
-#: log.c:254
+#: log.c:268
 #, c-format
 msgid "log failed: %s"
 msgstr ""
 
-#: log.c:462
+#: log.c:469
 msgid "FAILED to start up"
 msgstr ""
+
+#: conntrack.c:65
+#, c-format
+msgid "Conntrack connection mark retrieval failed: %s"
+msgstr ""
+
+#: dhcp6.c:59
+#, c-format
+msgid "cannot create DHCPv6 socket: %s"
+msgstr ""
+
+#: dhcp6.c:80
+#, c-format
+msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"
+msgstr ""
+
+#: dhcp6.c:92
+#, c-format
+msgid "failed to bind DHCPv6 server socket: %s"
+msgstr ""
+
+#: rfc3315.c:157
+#, c-format
+msgid "no address range available for DHCPv6 request from relay at %s"
+msgstr ""
+
+#: rfc3315.c:166
+#, c-format
+msgid "no address range available for DHCPv6 request via %s"
+msgstr ""
+
+#: rfc3315.c:297
+#, c-format
+msgid "%u available DHCPv6 subnet: %s/%d"
+msgstr ""
+
+#: rfc3315.c:380
+#, c-format
+msgid "%u vendor class: %u"
+msgstr ""
+
+#: rfc3315.c:428
+#, c-format
+msgid "%u client MAC address: %s"
+msgstr ""
+
+#: rfc3315.c:660
+#, c-format
+msgid "unknown prefix-class %d"
+msgstr ""
+
+#: rfc3315.c:803 rfc3315.c:898
+msgid "address unavailable"
+msgstr ""
+
+#: rfc3315.c:815 rfc3315.c:946 rfc3315.c:1279
+msgid "success"
+msgstr ""
+
+#: rfc3315.c:830 rfc3315.c:839 rfc3315.c:954 rfc3315.c:956
+msgid "no addresses available"
+msgstr ""
+
+#: rfc3315.c:933
+msgid "not on link"
+msgstr ""
+
+#: rfc3315.c:1006 rfc3315.c:1191 rfc3315.c:1268
+msgid "no binding found"
+msgstr ""
+
+#: rfc3315.c:1044
+msgid "deprecated"
+msgstr ""
+
+#: rfc3315.c:1049
+msgid "address invalid"
+msgstr ""
+
+#: rfc3315.c:1096
+msgid "confirm failed"
+msgstr ""
+
+#: rfc3315.c:1112
+msgid "all addresses still on link"
+msgstr ""
+
+#: rfc3315.c:1200
+msgid "release received"
+msgstr ""
+
+#: rfc3315.c:2126
+msgid "Cannot multicast to DHCPv6 server without correct interface"
+msgstr ""
+
+#: dhcp-common.c:145
+#, c-format
+msgid "Ignoring duplicate dhcp-option %d"
+msgstr ""
+
+#: dhcp-common.c:222
+#, c-format
+msgid "%u tags: %s"
+msgstr ""
+
+#: dhcp-common.c:407
+#, c-format
+msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgstr ""
+
+#: dhcp-common.c:430
+#, c-format
+msgid "duplicate IP address %s (%s) in dhcp-config directive"
+msgstr ""
+
+#: dhcp-common.c:494
+#, c-format
+msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s"
+msgstr ""
+
+#: dhcp-common.c:615
+#, c-format
+msgid "Known DHCP options:\n"
+msgstr ""
+
+#: dhcp-common.c:626
+#, c-format
+msgid "Known DHCPv6 options:\n"
+msgstr ""
+
+#: dhcp-common.c:823
+msgid ", prefix deprecated"
+msgstr ""
+
+#: dhcp-common.c:826
+#, c-format
+msgid ", lease time "
+msgstr ""
+
+#: dhcp-common.c:868
+#, c-format
+msgid "%s stateless on %s%.0s%.0s%s"
+msgstr ""
+
+#: dhcp-common.c:870
+#, c-format
+msgid "%s, static leases only on %.0s%s%s%.0s"
+msgstr ""
+
+#: dhcp-common.c:872
+#, c-format
+msgid "%s, proxy on subnet %.0s%s%.0s%.0s"
+msgstr ""
+
+#: dhcp-common.c:873
+#, c-format
+msgid "%s, IP range %s -- %s%s%.0s"
+msgstr ""
+
+#: dhcp-common.c:886
+#, c-format
+msgid "DHCPv4-derived IPv6 names on %s%s"
+msgstr ""
+
+#: dhcp-common.c:889
+#, c-format
+msgid "router advertisement on %s%s"
+msgstr ""
+
+#: dhcp-common.c:900
+#, c-format
+msgid "DHCP relay from %s to %s via %s"
+msgstr ""
+
+#: dhcp-common.c:902
+#, c-format
+msgid "DHCP relay from %s to %s"
+msgstr ""
+
+#: radv.c:109
+#, c-format
+msgid "cannot create ICMPv6 socket: %s"
+msgstr ""
+
+#: auth.c:448
+#, c-format
+msgid "ignoring zone transfer request from %s"
+msgstr ""
+
+#: ipset.c:95
+#, c-format
+msgid "failed to find kernel version: %s"
+msgstr ""
+
+#: ipset.c:114
+#, c-format
+msgid "failed to create IPset control socket: %s"
+msgstr ""
+
+#: dnssec.c:449 dnssec.c:493
+#, c-format
+msgid "failed to update mtime on %s: %s"
+msgstr ""
+
+#: blockdata.c:58
+#, c-format
+msgid "DNSSEC memory in use %u, max %u, allocated %u"
+msgstr ""
+
+#: tables.c:80
+msgid "error: fill_addr missused"
+msgstr ""
+
+#: tables.c:109
+#, c-format
+msgid "failed to access pf devices: %s"
+msgstr ""
+
+#: tables.c:123
+#, c-format
+msgid "warning: no opened pf devices %s"
+msgstr ""
+
+#: tables.c:131
+#, c-format
+msgid "error: cannot use table name %s"
+msgstr ""
+
+#: tables.c:139
+#, c-format
+msgid "error: cannot strlcpy table name %s"
+msgstr ""
+
+#: tables.c:145
+#, c-format
+msgid "warning: pfr_add_tables: %s(%d)"
+msgstr ""
+
+#: tables.c:151
+msgid "info: table created"
+msgstr ""
+
+#: tables.c:162
+#, c-format
+msgid "warning: DIOCR%sADDRS: %s"
+msgstr ""
+
+#: tables.c:166
+#, c-format
+msgid "%d addresses %s"
+msgstr ""
+
+#: inotify.c:59
+#, c-format
+msgid "cannot access path %s: %s"
+msgstr ""
+
+#: inotify.c:92
+#, c-format
+msgid "failed to create inotify: %s"
+msgstr ""
+
+#: inotify.c:105
+#, c-format
+msgid "too many symlinks following %s"
+msgstr ""
+
+#: inotify.c:121
+#, c-format
+msgid "directory %s for resolv-file is missing, cannot poll"
+msgstr ""
+
+#: inotify.c:125 inotify.c:162
+#, c-format
+msgid "failed to create inotify for %s: %s"
+msgstr ""
+
+#: inotify.c:147
+#, c-format
+msgid "bad dynamic directory %s: %s"
+msgstr ""
+
+#: inotify.c:247
+#, c-format
+msgid "inotify, new or changed file %s"
+msgstr ""
index 094da79..8d9ff71 100644 (file)
--- a/po/ro.po
+++ b/po/ro.po
@@ -10,552 +10,745 @@ msgstr ""
 "PO-Revision-Date: 2005-11-22 16:46+0000\n"
 "Last-Translator: Simon Kelley <simon@thekelleys.org.uk>\n"
 "Language-Team: Romanian <translation-team-ro@lists.sourceforge.net>\n"
+"Language: ro\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
+#: cache.c:523
+msgid "Internal error in cache."
+msgstr ""
+
 # for compatibility purposes the letters â, ă, ş, ţ and î can be written as their look-alike correspondent.
-#: cache.c:761
+#: cache.c:941
 #, fuzzy, c-format
 msgid "failed to load names from %s: %s"
 msgstr "încărcarea numelor din %s: %s a eşuat"
 
-#: cache.c:795 dhcp.c:865
+#: cache.c:967 dhcp.c:828
 #, c-format
 msgid "bad address at %s line %d"
 msgstr "adresă greşită în %s, linia %d"
 
-#: cache.c:853 dhcp.c:881
+#: cache.c:1018 dhcp.c:844
 #, c-format
 msgid "bad name at %s line %d"
 msgstr "nume greşit în %s linia %d"
 
-#: cache.c:860 dhcp.c:956
+#: cache.c:1027 dhcp.c:919
 #, c-format
 msgid "read %s - %d addresses"
 msgstr "citesc %s - %d adrese"
 
-#: cache.c:899
+#: cache.c:1135
 msgid "cleared cache"
 msgstr "memoria temporară a fost ştearsă"
 
-#: cache.c:960
+#: cache.c:1164
+#, c-format
+msgid "No IPv4 address found for %s"
+msgstr ""
+
+#: cache.c:1242
 #, c-format
 msgid "%s is a CNAME, not giving it to the DHCP lease of %s"
 msgstr ""
 
-#: cache.c:966
+#: cache.c:1266
 #, c-format
 msgid "not giving name %s to the DHCP lease of %s because the name exists in %s with address %s"
 msgstr "nu pot da numele %s împrumutului de adresă DHCP a lui %s deoarece numeleexistă în %s cu adresa %s"
 
-#: cache.c:1039
+#: cache.c:1421
 #, c-format
 msgid "time %lu"
 msgstr ""
 
-#: cache.c:1040
+#: cache.c:1422
 #, fuzzy, c-format
 msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries."
 msgstr "cantitate de memorie temporară %d, %d/%d stocări temporare aureutilizat locaţii neexpirate."
 
-#: cache.c:1042
+#: cache.c:1424
 #, c-format
 msgid "queries forwarded %u, queries answered locally %u"
 msgstr ""
 
-#: cache.c:1068
+#: cache.c:1427
+#, c-format
+msgid "queries for authoritative zones %u"
+msgstr ""
+
+#: cache.c:1453
 #, c-format
 msgid "server %s#%d: queries sent %u, retried or failed %u"
 msgstr ""
 
-#: util.c:57
+#: util.c:45
 #, fuzzy, c-format
 msgid "failed to seed the random number generator: %s"
 msgstr "ascultarea pe socket a eşuat: %s"
 
-#: util.c:189
+#: util.c:205
 #, fuzzy
 msgid "failed to allocate memory"
 msgstr "nu pot încărca %d bytes"
 
-#: util.c:227 option.c:573
+#: util.c:250 option.c:601
 msgid "could not get memory"
 msgstr "nu am putut aloca memorie"
 
-#: util.c:237
+#: util.c:260
 #, fuzzy, c-format
 msgid "cannot create pipe: %s"
 msgstr "nu pot citi %s: %s"
 
-#: util.c:245
+#: util.c:268
 #, fuzzy, c-format
 msgid "failed to allocate %d bytes"
 msgstr "nu pot încărca %d bytes"
 
-#: util.c:350
+#: util.c:437
 #, c-format
 msgid "infinite"
 msgstr "infinit"
 
-#: option.c:244
+#: option.c:332
 msgid "Specify local address(es) to listen on."
 msgstr "Specificaţi adresele locale deservite."
 
-#: option.c:245
+#: option.c:333
 msgid "Return ipaddr for all hosts in specified domains."
 msgstr "Afişează adresele IP ale maşinilor în domeniul dat."
 
-#: option.c:246
+#: option.c:334
 msgid "Fake reverse lookups for RFC1918 private address ranges."
 msgstr "Simulează căutări după adresă pentru domenii de adresă private (RFC1918)."
 
-#: option.c:247
+#: option.c:335
 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."
 msgstr "Interpretează adresa IP ca NXDOMAIN (împotriva manipulărilor Verisign)"
 
-#: option.c:248
+#: option.c:336
 #, c-format
 msgid "Specify the size of the cache in entries (defaults to %s)."
 msgstr "Specifică mărimea înregistrărilor temporare (implicit e %s)."
 
-#: option.c:249
+#: option.c:337
 #, c-format
 msgid "Specify configuration file (defaults to %s)."
 msgstr "Specifică fişier de configurare (implicit e %s)."
 
-#: option.c:250
+#: option.c:338
 msgid "Do NOT fork into the background: run in debug mode."
 msgstr "NU porneşte în fundal: rulează în modul depanare."
 
-#: option.c:251
+#: option.c:339
 msgid "Do NOT forward queries with no domain part."
 msgstr "NU înainta cererile ce nu conţin domeniu DNS."
 
-#: option.c:252
+#: option.c:340
 msgid "Return self-pointing MX records for local hosts."
 msgstr "Răspunde cu înregistrări MX spre el însuşi pentru maşini locale."
 
-#: option.c:253
+#: option.c:341
 msgid "Expand simple names in /etc/hosts with domain-suffix."
 msgstr "Adaugă numelor simple din /etc/hosts numele domeniului ca sufix."
 
-#: option.c:254
+#: option.c:342
 msgid "Don't forward spurious DNS requests from Windows hosts."
 msgstr "Nu inainta cereri DNS defecte provenite de la maşini Windows."
 
-#: option.c:255
+#: option.c:343
 msgid "Enable DHCP in the range given with lease duration."
 msgstr "Activează DHCP în domeniul dat cu durată limitată de împrumut."
 
-#: option.c:256
+#: option.c:344
 #, c-format
 msgid "Change to this group after startup (defaults to %s)."
 msgstr "Rulează sub acest grup după pornire (implicit e %s)."
 
-#: option.c:257
+#: option.c:345
 msgid "Set address or hostname for a specified machine."
 msgstr "Schimbă adresa sau numele maşinii specificate."
 
-#: option.c:258
+#: option.c:346
 #, fuzzy
 msgid "Read DHCP host specs from file."
 msgstr "nume MX invalid"
 
-#: option.c:259
+#: option.c:347
 msgid "Read DHCP option specs from file."
 msgstr ""
 
-#: option.c:260
+#: option.c:348
+#, fuzzy
+msgid "Read DHCP host specs from a directory."
+msgstr "nume MX invalid"
+
+#: option.c:349
+#, fuzzy
+msgid "Read DHCP options from a directory."
+msgstr "nume MX invalid"
+
+#: option.c:350
 msgid "Evaluate conditional tag expression."
 msgstr ""
 
-#: option.c:261
+#: option.c:351
 #, c-format
 msgid "Do NOT load %s file."
 msgstr "Nu încarcă fişierul %s."
 
-#: option.c:262
+#: option.c:352
 #, c-format
 msgid "Specify a hosts file to be read in addition to %s."
 msgstr "Specifică spre citire un fişier hosts adiţional la %s."
 
-#: option.c:263
+#: option.c:353
+#, fuzzy
+msgid "Read hosts files from a directory."
+msgstr "nume MX invalid"
+
+#: option.c:354
 msgid "Specify interface(s) to listen on."
 msgstr "Specifică interfeţele deservite."
 
-#: option.c:264
+#: option.c:355
 msgid "Specify interface(s) NOT to listen on."
 msgstr "Specifică interfeţele NE-deservite."
 
-#: option.c:265
+#: option.c:356
 #, fuzzy
 msgid "Map DHCP user class to tag."
 msgstr "Leagă clasa de utilizator DHCP cu grup de opţiuni."
 
-#: option.c:266
+#: option.c:357
 msgid "Map RFC3046 circuit-id to tag."
 msgstr ""
 
-#: option.c:267
+#: option.c:358
 msgid "Map RFC3046 remote-id to tag."
 msgstr ""
 
-#: option.c:268
+#: option.c:359
 msgid "Map RFC3993 subscriber-id to tag."
 msgstr ""
 
-#: option.c:269
+#: option.c:360
 #, fuzzy
 msgid "Don't do DHCP for hosts with tag set."
 msgstr "Nu furniza DHCP maşinilor din grupul de opţiuni."
 
-#: option.c:270
+#: option.c:361
 #, fuzzy
 msgid "Force broadcast replies for hosts with tag set."
 msgstr "Nu furniza DHCP maşinilor din grupul de opţiuni."
 
-#: option.c:271
+#: option.c:362
 msgid "Do NOT fork into the background, do NOT run in debug mode."
 msgstr "NU porneşte în fundal, NU rulează în modul depanare."
 
-#: option.c:272
+#: option.c:363
 msgid "Assume we are the only DHCP server on the local network."
 msgstr "Presupune că suntem singurul server DHCP din reţeaua locală."
 
-#: option.c:273
+#: option.c:364
 #, c-format
 msgid "Specify where to store DHCP leases (defaults to %s)."
 msgstr "Specifică fişierul de stocare a împrumuturilor DHCP (implicit e %s)."
 
-#: option.c:274
+#: option.c:365
 msgid "Return MX records for local hosts."
 msgstr "Răspunde cu întregistrări MX pentru maşini locale."
 
-#: option.c:275
+#: option.c:366
 msgid "Specify an MX record."
 msgstr "Specifică o înregistrare MX."
 
-#: option.c:276
+#: option.c:367
 msgid "Specify BOOTP options to DHCP server."
 msgstr "Specifică opţiuni BOOTP serverului DHCP."
 
-#: option.c:277
+#: option.c:368
 #, c-format
 msgid "Do NOT poll %s file, reload only on SIGHUP."
 msgstr "Nu încărca fişierul %s, citeşte-l doar la SIGHUP."
 
-#: option.c:278
+#: option.c:369
 msgid "Do NOT cache failed search results."
 msgstr "NU memora rezultatele de căutare DNS eşuatată."
 
-#: option.c:279
+#: option.c:370
 #, c-format
 msgid "Use nameservers strictly in the order given in %s."
 msgstr "Foloseşte servere DNS strict în ordinea dată în %s."
 
-#: option.c:280
+#: option.c:371
 #, fuzzy
 msgid "Specify options to be sent to DHCP clients."
 msgstr "Configurează opţiuni în plusce trebuie trimise clienţilor DHCP."
 
-#: option.c:281
+#: option.c:372
 msgid "DHCP option sent even if the client does not request it."
 msgstr ""
 
-#: option.c:282
+#: option.c:373
 msgid "Specify port to listen for DNS requests on (defaults to 53)."
 msgstr "Specifică numărul portului pentru cereri DNS (implicit e 53)."
 
-#: option.c:283
+#: option.c:374
 #, c-format
 msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)."
 msgstr "Marimea maximă a pachetului UDP pentru EDNS.0 (implicit e %s)."
 
-#: option.c:284
+#: option.c:375
 #, fuzzy
 msgid "Log DNS queries."
 msgstr "Înregistrează tranzacţiile."
 
-#: option.c:285
+#: option.c:376
 #, fuzzy
 msgid "Force the originating port for upstream DNS queries."
 msgstr "Forţează acest port pentru datele ce pleacă."
 
-#: option.c:286
+#: option.c:377
 msgid "Do NOT read resolv.conf."
 msgstr "NU citi fişierul resolv.conf"
 
-#: option.c:287
+#: option.c:378
 #, c-format
 msgid "Specify path to resolv.conf (defaults to %s)."
 msgstr "Specifică calea către resolv.conf (implicit e %s)."
 
-#: option.c:288
+#: option.c:379
+#, fuzzy
+msgid "Specify path to file with server= options"
+msgstr "Specifică o cale pentru fişierul PID. (implicit %s)."
+
+#: option.c:380
 msgid "Specify address(es) of upstream servers with optional domains."
 msgstr "Specifică adresele server(elor) superioare cu domenii opţionale."
 
-#: option.c:289
+#: option.c:381
+#, fuzzy
+msgid "Specify address of upstream servers for reverse address queries"
+msgstr "Specifică adresele server(elor) superioare cu domenii opţionale."
+
+#: option.c:382
 msgid "Never forward queries to specified domains."
 msgstr "Nu înaintează cererile spre domeniile specificate."
 
-#: option.c:290
+#: option.c:383
 msgid "Specify the domain to be assigned in DHCP leases."
 msgstr "Specifică domeniul de transmis prin DHCP."
 
-#: option.c:291
+#: option.c:384
 msgid "Specify default target in an MX record."
 msgstr "Specifică o ţintă într-o înregistrare MX."
 
-#: option.c:292
+#: option.c:385
 msgid "Specify time-to-live in seconds for replies from /etc/hosts."
 msgstr "Specifică TTL în secunde pentru răspunsurile din /etc/hosts."
 
-#: option.c:293
+#: option.c:386
 #, fuzzy
 msgid "Specify time-to-live in seconds for negative caching."
 msgstr "Specifică TTL în secunde pentru răspunsurile din /etc/hosts."
 
-#: option.c:294
+#: option.c:387
 #, fuzzy
 msgid "Specify time-to-live in seconds for maximum TTL to send to clients."
 msgstr "Specifică TTL în secunde pentru răspunsurile din /etc/hosts."
 
-#: option.c:295
+#: option.c:388
+#, fuzzy
+msgid "Specify time-to-live ceiling for cache."
+msgstr "Specifică TTL în secunde pentru răspunsurile din /etc/hosts."
+
+#: option.c:389
+#, fuzzy
+msgid "Specify time-to-live floor for cache."
+msgstr "Specifică TTL în secunde pentru răspunsurile din /etc/hosts."
+
+#: option.c:390
 #, c-format
 msgid "Change to this user after startup. (defaults to %s)."
 msgstr "Rulează sub acest utilizator după pornire. (implicit e %s)."
 
-#: option.c:296
+#: option.c:391
 #, fuzzy
 msgid "Map DHCP vendor class to tag."
 msgstr "Trimite opţiuni DHCP în funcţie de marca plăcii de reţea."
 
-#: option.c:297
+#: option.c:392
 msgid "Display dnsmasq version and copyright information."
 msgstr "Afişează versiunea dnsmasq şi drepturile de autor."
 
-#: option.c:298
+#: option.c:393
 msgid "Translate IPv4 addresses from upstream servers."
 msgstr "Traduce adresele IPv4 de la serverele DNS superioare."
 
-#: option.c:299
+#: option.c:394
 msgid "Specify a SRV record."
 msgstr "Specifică o înregistrare SRV."
 
-#: option.c:300
+#: option.c:395
 msgid "Display this message. Use --help dhcp for known DHCP options."
 msgstr ""
 
-#: option.c:301
+#: option.c:396
 #, fuzzy, c-format
 msgid "Specify path of PID file (defaults to %s)."
 msgstr "Specifică o cale pentru fişierul PID. (implicit %s)."
 
-#: option.c:302
+#: option.c:397
 #, c-format
 msgid "Specify maximum number of DHCP leases (defaults to %s)."
 msgstr "Specifică numărul maxim de împrumuturi DHCP (implicit %s)."
 
-#: option.c:303
+#: option.c:398
 msgid "Answer DNS queries based on the interface a query was sent to."
 msgstr "Răspunde cererilor DNS în funcţie de interfaţa pe care a venit cererea."
 
-#: option.c:304
+#: option.c:399
 msgid "Specify TXT DNS record."
 msgstr "Specifică o înregistrare TXT."
 
-#: option.c:305
+#: option.c:400
 #, fuzzy
 msgid "Specify PTR DNS record."
 msgstr "Specifică o înregistrare TXT."
 
-#: option.c:306
+#: option.c:401
 msgid "Give DNS name to IPv4 address of interface."
 msgstr ""
 
-#: option.c:307
+#: option.c:402
 msgid "Bind only to interfaces in use."
 msgstr "Ascultă doar pe interfeţele active."
 
-#: option.c:308
+#: option.c:403
 #, c-format
 msgid "Read DHCP static host information from %s."
 msgstr "Citeşte informaţii DHCP statice despre maşină din %s."
 
-#: option.c:309
+#: option.c:404
 msgid "Enable the DBus interface for setting upstream servers, etc."
 msgstr "Activeaza interfaţa DBus pentru configurarea serverelor superioare."
 
-#: option.c:310
+#: option.c:405
 msgid "Do not provide DHCP on this interface, only provide DNS."
 msgstr "Nu activează DHCP ci doar DNS pe această interfaţă."
 
-#: option.c:311
+#: option.c:406
 msgid "Enable dynamic address allocation for bootp."
 msgstr "Activează alocarea dinamică a adreselor pentru BOOTP."
 
-#: option.c:312
+#: option.c:407
 #, fuzzy
 msgid "Map MAC address (with wildcards) to option set."
 msgstr "Trimite opţiuni DHCP în funcţie de marca plăcii de reţea."
 
-#: option.c:313
+#: option.c:408
 msgid "Treat DHCP requests on aliases as arriving from interface."
 msgstr ""
 
-#: option.c:314
+#: option.c:409
 msgid "Disable ICMP echo address checking in the DHCP server."
 msgstr ""
 
-#: option.c:315
-msgid "Script to run on DHCP lease creation and destruction."
+#: option.c:410
+msgid "Shell script to run on DHCP lease creation and destruction."
+msgstr ""
+
+#: option.c:411
+msgid "Lua script to run on DHCP lease creation and destruction."
+msgstr ""
+
+#: option.c:412
+msgid "Run lease-change scripts as this user."
 msgstr ""
 
-#: option.c:316
+#: option.c:413
 msgid "Read configuration from all the files in this directory."
 msgstr ""
 
-#: option.c:317
+#: option.c:414
 #, fuzzy
 msgid "Log to this syslog facility or file. (defaults to DAEMON)"
 msgstr "Rulează sub acest utilizator după pornire. (implicit e %s)."
 
-#: option.c:318
+#: option.c:415
 msgid "Do not use leasefile."
 msgstr ""
 
-#: option.c:319
+#: option.c:416
 #, fuzzy, c-format
 msgid "Maximum number of concurrent DNS queries. (defaults to %s)"
 msgstr "Specifică numărul maxim de împrumuturi DHCP (implicit %s)."
 
-#: option.c:320
+#: option.c:417
 #, c-format
 msgid "Clear DNS cache when reloading %s."
 msgstr ""
 
-#: option.c:321
+#: option.c:418
 msgid "Ignore hostnames provided by DHCP clients."
 msgstr ""
 
-#: option.c:322
+#: option.c:419
 msgid "Do NOT reuse filename and server fields for extra DHCP options."
 msgstr ""
 
-#: option.c:323
+#: option.c:420
 msgid "Enable integrated read-only TFTP server."
 msgstr ""
 
-#: option.c:324
+#: option.c:421
 msgid "Export files by TFTP only from the specified subtree."
 msgstr ""
 
-#: option.c:325
+#: option.c:422
 msgid "Add client IP address to tftp-root."
 msgstr ""
 
-#: option.c:326
+#: option.c:423
 msgid "Allow access only to files owned by the user running dnsmasq."
 msgstr ""
 
-#: option.c:327
+#: option.c:424
+msgid "Do not terminate the service if TFTP directories are inaccessible."
+msgstr ""
+
+#: option.c:425
 #, fuzzy, c-format
 msgid "Maximum number of conncurrent TFTP transfers (defaults to %s)."
 msgstr "Specifică numărul maxim de împrumuturi DHCP (implicit %s)."
 
-#: option.c:328
+#: option.c:426
 msgid "Disable the TFTP blocksize extension."
 msgstr ""
 
-#: option.c:329
+#: option.c:427
+msgid "Convert TFTP filenames to lowercase"
+msgstr ""
+
+#: option.c:428
 msgid "Ephemeral port range for use by TFTP transfers."
 msgstr ""
 
-#: option.c:330
+#: option.c:429
 msgid "Extra logging for DHCP."
 msgstr ""
 
-#: option.c:331
+#: option.c:430
 msgid "Enable async. logging; optionally set queue length."
 msgstr ""
 
-#: option.c:332
+#: option.c:431
 msgid "Stop DNS rebinding. Filter private IP ranges when resolving."
 msgstr ""
 
-#: option.c:333
+#: option.c:432
 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers."
 msgstr ""
 
-#: option.c:334
+#: option.c:433
 msgid "Inhibit DNS-rebind protection on this domain."
 msgstr ""
 
-#: option.c:335
+#: option.c:434
 msgid "Always perform DNS queries to all servers."
 msgstr ""
 
-#: option.c:336
+#: option.c:435
 msgid "Set tag if client includes matching option in request."
 msgstr ""
 
-#: option.c:337
+#: option.c:436
 msgid "Use alternative ports for DHCP."
 msgstr ""
 
-#: option.c:338
-msgid "Run lease-change script as this user."
-msgstr ""
-
-#: option.c:339
+#: option.c:437
 #, fuzzy
 msgid "Specify NAPTR DNS record."
 msgstr "Specifică o înregistrare TXT."
 
-#: option.c:340
+#: option.c:438
 msgid "Specify lowest port available for DNS query transmission."
 msgstr ""
 
-#: option.c:341
+#: option.c:439
 msgid "Use only fully qualified domain names for DHCP clients."
 msgstr ""
 
-#: option.c:342
+#: option.c:440
 msgid "Generate hostnames based on MAC address for nameless clients."
 msgstr ""
 
-#: option.c:343
+#: option.c:441
 msgid "Use these DHCP relays as full proxies."
 msgstr ""
 
-#: option.c:344
+#: option.c:442
+msgid "Relay DHCP requests to a remote server"
+msgstr ""
+
+#: option.c:443
 msgid "Specify alias name for LOCAL DNS name."
 msgstr ""
 
-#: option.c:345
+#: option.c:444
 #, fuzzy
 msgid "Prompt to send to PXE clients."
 msgstr "Configurează opţiuni în plusce trebuie trimise clienţilor DHCP."
 
-#: option.c:346
+#: option.c:445
 msgid "Boot service for PXE menu."
 msgstr ""
 
-#: option.c:347
+#: option.c:446
 msgid "Check configuration syntax."
 msgstr ""
 
-#: option.c:348
-msgid "Add requestor's MAC address to forwarded DNS queries"
+#: option.c:447
+msgid "Add requestor's MAC address to forwarded DNS queries."
 msgstr ""
 
-#: option.c:349
+#: option.c:448
+msgid "Add requestor's IP subnet to forwarded DNS queries."
+msgstr ""
+
+#: option.c:449
 #, fuzzy
-msgid "Proxy DNSSEC validation results from upstream nameservers"
+msgid "Proxy DNSSEC validation results from upstream nameservers."
 msgstr "Traduce adresele IPv4 de la serverele DNS superioare."
 
-#: option.c:638
+#: option.c:450
+msgid "Attempt to allocate sequential IP addresses to DHCP clients."
+msgstr ""
+
+#: option.c:451
+msgid "Copy connection-track mark from queries to upstream connections."
+msgstr ""
+
+#: option.c:452
+msgid "Allow DHCP clients to do their own DDNS updates."
+msgstr ""
+
+#: option.c:453
+msgid "Send router-advertisements for interfaces doing DHCPv6"
+msgstr ""
+
+#: option.c:454
+msgid "Specify DUID_EN-type DHCPv6 server DUID"
+msgstr ""
+
+#: option.c:455
+#, fuzzy
+msgid "Specify host (A/AAAA and PTR) records"
+msgstr "Specifică o înregistrare MX."
+
+#: option.c:456
+#, fuzzy
+msgid "Specify arbitrary DNS resource record"
+msgstr "Specifică o înregistrare TXT."
+
+#: option.c:457
+#, fuzzy
+msgid "Bind to interfaces in use - check for new interfaces"
+msgstr "interfaţă necunoscută %s"
+
+#: option.c:458
+msgid "Export local names to global DNS"
+msgstr ""
+
+#: option.c:459
+msgid "Domain to export to global DNS"
+msgstr ""
+
+#: option.c:460
+msgid "Set TTL for authoritative replies"
+msgstr ""
+
+#: option.c:461
+msgid "Set authoritive zone information"
+msgstr ""
+
+#: option.c:462
+msgid "Secondary authoritative nameservers for forward domains"
+msgstr ""
+
+#: option.c:463
+msgid "Peers which are allowed to do zone transfer"
+msgstr ""
+
+#: option.c:464
+msgid "Specify ipsets to which matching domains should be added"
+msgstr ""
+
+#: option.c:465
+msgid "Specify a domain and address range for synthesised names"
+msgstr ""
+
+#: option.c:466
+msgid "Activate DNSSEC validation"
+msgstr ""
+
+#: option.c:467
+msgid "Specify trust anchor key digest."
+msgstr ""
+
+#: option.c:468
+msgid "Disable upstream checking for DNSSEC debugging."
+msgstr ""
+
+#: option.c:469
+msgid "Ensure answers without DNSSEC are in unsigned zones."
+msgstr ""
+
+#: option.c:470
+msgid "Don't check DNSSEC signature timestamps until first cache-reload"
+msgstr ""
+
+#: option.c:471
+msgid "Timestamp file to verify system clock for DNSSEC"
+msgstr ""
+
+#: option.c:473
+msgid "Specify DHCPv6 prefix class"
+msgstr ""
+
+#: option.c:475
+msgid "Set priority, resend-interval and router-lifetime"
+msgstr ""
+
+#: option.c:476
+msgid "Do not log routine DHCP."
+msgstr ""
+
+#: option.c:477
+msgid "Do not log routine DHCPv6."
+msgstr ""
+
+#: option.c:478
+msgid "Do not log RA."
+msgstr ""
+
+#: option.c:479
+msgid "Accept queries only from directly-connected networks"
+msgstr ""
+
+#: option.c:480
+msgid "Detect and remove DNS forwarding loops"
+msgstr ""
+
+#: option.c:481
+msgid "Ignore DNS responses containing ipaddr."
+msgstr ""
+
+#: option.c:683
 #, c-format
 msgid ""
 "Usage: dnsmasq [options]\n"
@@ -564,933 +757,1461 @@ msgstr ""
 "Utilizare: dnsmasq [opţiuni]\n"
 "\n"
 
-#: option.c:640
+#: option.c:685
 #, c-format
 msgid "Use short options only on the command line.\n"
 msgstr "Folosiţi opţiunile prescurtate doar în linie de comandă.\n"
 
-#: option.c:642
+#: option.c:687
 #, fuzzy, c-format
 msgid "Valid options are:\n"
 msgstr "Opţiunile valide sunt:\n"
 
-#: option.c:683
-#, c-format
-msgid "Known DHCP options:\n"
+#: option.c:744 option.c:748
+msgid "bad port"
+msgstr "port invalid"
+
+#: option.c:775 option.c:807
+msgid "interface binding not supported"
+msgstr ""
+
+#: option.c:784 option.c:3575
+#, fuzzy
+msgid "bad interface name"
+msgstr "nume MX invalid"
+
+#: option.c:814
+#, fuzzy
+msgid "bad address"
+msgstr "citesc %s - %d adrese"
+
+#: option.c:996
+msgid "unsupported encapsulation for IPv6 option"
 msgstr ""
 
-#: option.c:798
+#: option.c:1010
 msgid "bad dhcp-option"
 msgstr "dhcp-option invalid"
 
-#: option.c:860
+#: option.c:1078
 #, fuzzy
 msgid "bad IP address"
 msgstr "citesc %s - %d adrese"
 
-#: option.c:968
+#: option.c:1081 option.c:1219 option.c:2893
+#, fuzzy
+msgid "bad IPv6 address"
+msgstr "citesc %s - %d adrese"
+
+#: option.c:1246 option.c:1340
 msgid "bad domain in dhcp-option"
 msgstr "domeniu DNS invalid în declaraţia dhcp-option"
 
-#: option.c:1034
+#: option.c:1378
 msgid "dhcp-option too long"
 msgstr "declararea dhcp-option este prea lungă"
 
-#: option.c:1043
+#: option.c:1385
 msgid "illegal dhcp-match"
 msgstr ""
 
-#: option.c:1087
+#: option.c:1447
 msgid "illegal repeated flag"
 msgstr ""
 
-#: option.c:1095
+#: option.c:1455
 msgid "illegal repeated keyword"
 msgstr ""
 
-#: option.c:1147 option.c:3030
+#: option.c:1520 option.c:4191
 #, fuzzy, c-format
 msgid "cannot access directory %s: %s"
 msgstr "nu pot citi %s: %s"
 
-#: option.c:1178 tftp.c:460
+#: option.c:1566 tftp.c:493
 #, fuzzy, c-format
 msgid "cannot access %s: %s"
 msgstr "nu pot citi %s: %s"
 
-#: option.c:1207
+#: option.c:1618
 msgid "setting log facility is not possible under Android"
 msgstr ""
 
-#: option.c:1216
+#: option.c:1627
 msgid "bad log facility"
 msgstr ""
 
-#: option.c:1265
+#: option.c:1680
 msgid "bad MX preference"
 msgstr "preferinţă MX invalidă"
 
-#: option.c:1270
+#: option.c:1685
 msgid "bad MX name"
 msgstr "nume MX invalid"
 
-#: option.c:1284
+#: option.c:1699
 msgid "bad MX target"
 msgstr "ţintă MX invalidă"
 
-#: option.c:1294
+#: option.c:1711
 msgid "cannot run scripts under uClinux"
 msgstr ""
 
-#: option.c:1296
+#: option.c:1713
 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts"
 msgstr ""
 
-#: option.c:1597 option.c:1601
-msgid "bad port"
+#: option.c:1717
+msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts"
+msgstr ""
+
+#: option.c:1973 option.c:2018 option.c:2074
+#, fuzzy
+msgid "bad prefix"
 msgstr "port invalid"
 
-#: option.c:1620 option.c:1645
-msgid "interface binding not supported"
+#: option.c:2355
+msgid "recompile with HAVE_IPSET defined to enable ipset directives"
 msgstr ""
 
-#: option.c:1791
+#: option.c:2548
 #, fuzzy
 msgid "bad port range"
 msgstr "port invalid"
 
-#: option.c:1808
+#: option.c:2564
 msgid "bad bridge-interface"
 msgstr ""
 
-#: option.c:1850
+#: option.c:2624
+msgid "only one tag allowed"
+msgstr ""
+
+#: option.c:2644 option.c:2656 option.c:2764 option.c:2805
 msgid "bad dhcp-range"
 msgstr "dhcp-range invalid"
 
-#: option.c:1878
-msgid "only one tag allowed"
+#: option.c:2671
+msgid "inconsistent DHCP range"
+msgstr "domeniu DHCP inconsistent"
+
+#: option.c:2732
+msgid "prefix length must be exactly 64 for RA subnets"
 msgstr ""
 
-#: option.c:1925
-msgid "inconsistent DHCP range"
+#: option.c:2734
+msgid "prefix length must be exactly 64 for subnet constructors"
+msgstr ""
+
+#: option.c:2738
+msgid "prefix length must be at least 64"
+msgstr ""
+
+#: option.c:2741
+#, fuzzy
+msgid "inconsistent DHCPv6 range"
 msgstr "domeniu DHCP inconsistent"
 
-#: option.c:2019 option.c:2045
+#: option.c:2752
+msgid "prefix must be zero with \"constructor:\" argument"
+msgstr ""
+
+#: option.c:2863 option.c:2911
 #, fuzzy
 msgid "bad hex constant"
 msgstr "dhcp-host invalid"
 
-#: option.c:2107
+#: option.c:2885
+msgid "cannot match tags in --dhcp-host"
+msgstr ""
+
+#: option.c:2933
+#, fuzzy, c-format
+msgid "duplicate dhcp-host IP address %s"
+msgstr "adresă IP duplicat %s în declaraţia dhcp-config."
+
+#: option.c:2991
 #, fuzzy
 msgid "bad DHCP host name"
 msgstr "nume MX invalid"
 
-#: option.c:2188
+#: option.c:3073
 #, fuzzy
 msgid "bad tag-if"
 msgstr "ţintă MX invalidă"
 
-#: option.c:2467 option.c:2752
+#: option.c:3397 option.c:3791
 msgid "invalid port number"
 msgstr "număr de port invalid"
 
-#: option.c:2529
+#: option.c:3459
 #, fuzzy
 msgid "bad dhcp-proxy address"
 msgstr "citesc %s - %d adrese"
 
-#: option.c:2569
+#: option.c:3485
 #, fuzzy
-msgid "invalid alias range"
-msgstr "pondere invalidă"
+msgid "Bad dhcp-relay"
+msgstr "dhcp-range invalid"
+
+#: option.c:3511
+msgid "bad RA-params"
+msgstr ""
+
+#: option.c:3520
+msgid "bad DUID"
+msgstr ""
 
-#: option.c:2582
+#: option.c:3562
 #, fuzzy
-msgid "bad interface name"
-msgstr "nume MX invalid"
+msgid "invalid alias range"
+msgstr "pondere invalidă"
 
-#: option.c:2607
+#: option.c:3616
 msgid "bad CNAME"
 msgstr ""
 
-#: option.c:2612
+#: option.c:3621
 msgid "duplicate CNAME"
 msgstr ""
 
-#: option.c:2632
+#: option.c:3641
 #, fuzzy
 msgid "bad PTR record"
 msgstr "înregistrare SRV invalidă"
 
-#: option.c:2663
+#: option.c:3672
 #, fuzzy
 msgid "bad NAPTR record"
 msgstr "înregistrare SRV invalidă"
 
-#: option.c:2695
+#: option.c:3706
+#, fuzzy
+msgid "bad RR record"
+msgstr "înregistrare SRV invalidă"
+
+#: option.c:3736
 msgid "bad TXT record"
 msgstr "înregistrare TXT invalidă"
 
-#: option.c:2738
+#: option.c:3777
 msgid "bad SRV record"
 msgstr "înregistrare SRV invalidă"
 
-#: option.c:2745
+#: option.c:3784
 msgid "bad SRV target"
 msgstr "ţintă SRV invalidă"
 
-#: option.c:2759
+#: option.c:3798
 msgid "invalid priority"
 msgstr "prioritate invalidă"
 
-#: option.c:2766
+#: option.c:3805
 msgid "invalid weight"
 msgstr "pondere invalidă"
 
-#: option.c:2785
-msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"
+#: option.c:3829
+#, fuzzy
+msgid "Bad host-record"
+msgstr "înregistrare SRV invalidă"
+
+#: option.c:3846
+#, fuzzy
+msgid "Bad name in host-record"
+msgstr "nume invalid în %s"
+
+#: option.c:3911
+#, fuzzy
+msgid "bad trust anchor"
+msgstr "port invalid"
+
+#: option.c:3925
+msgid "bad HEX in trust anchor"
+msgstr ""
+
+#: option.c:3935
+msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"
 msgstr ""
 
-#: option.c:2849
+#: option.c:3994
 msgid "missing \""
 msgstr "lipseşte \""
 
-#: option.c:2908
+#: option.c:4051
 msgid "bad option"
 msgstr "opţiune invalidă"
 
-#: option.c:2910
+#: option.c:4053
 msgid "extraneous parameter"
 msgstr "parametru nerecunoscut"
 
-#: option.c:2912
+#: option.c:4055
 msgid "missing parameter"
 msgstr "parametru lipsa"
 
-#: option.c:2916
+#: option.c:4057
+#, fuzzy
+msgid "illegal option"
+msgstr "opţiune invalidă"
+
+#: option.c:4064
 msgid "error"
 msgstr "eroare"
 
-#: option.c:2921
-#, c-format
-msgid "%s at line %d of %%s"
+#: option.c:4066
+#, fuzzy, c-format
+msgid " at line %d of %s"
 msgstr "%s la linia %d din %%s"
 
-#: option.c:2985 tftp.c:624
-#, c-format
-msgid "cannot read %s: %s"
-msgstr "nu pot citi %s: %s"
-
-#: option.c:3151 option.c:3187
+#: option.c:4081 option.c:4328 option.c:4364
 #, fuzzy, c-format
 msgid "read %s"
 msgstr "citesc %s"
 
-#: option.c:3239
+#: option.c:4144 option.c:4267 tftp.c:667
+#, c-format
+msgid "cannot read %s: %s"
+msgstr "nu pot citi %s: %s"
+
+#: option.c:4430
 msgid "junk found in command line"
 msgstr ""
 
-#: option.c:3269
+#: option.c:4465
 #, c-format
 msgid "Dnsmasq version %s  %s\n"
 msgstr "dnsmasq versiunea %s  %s\n"
 
-#: option.c:3270
-#, c-format
+#: option.c:4466
+#, fuzzy, c-format
 msgid ""
-"Compile time options %s\n"
+"Compile time options: %s\n"
 "\n"
 msgstr ""
 "Opţiuni cu care a fost compilat %s\n"
 "\n"
 
-#: option.c:3271
+#: option.c:4467
 #, c-format
 msgid "This software comes with ABSOLUTELY NO WARRANTY.\n"
 msgstr "Acest program vine FĂRĂ NICI O GARANŢIE.\n"
 
-#: option.c:3272
+#: option.c:4468
 #, c-format
 msgid "Dnsmasq is free software, and you are welcome to redistribute it\n"
 msgstr "Dnsmasq este un program gratuit, sunteţi invitaţi să-l redistribuiţi\n"
 
-#: option.c:3273
+#: option.c:4469
 #, fuzzy, c-format
 msgid "under the terms of the GNU General Public License, version 2 or 3.\n"
 msgstr "în termenii Licenţei publice generale GNU, versiunea 2.\n"
 
-#: option.c:3284
+#: option.c:4480
 msgid "try --help"
 msgstr ""
 
-#: option.c:3286
+#: option.c:4482
 msgid "try -w"
 msgstr ""
 
-#: option.c:3289
+#: option.c:4484
 #, fuzzy, c-format
 msgid "bad command line options: %s"
 msgstr "opţiuni în linie de comandă invalide: %s."
 
-#: option.c:3330
+#: option.c:4541
 #, c-format
 msgid "cannot get host-name: %s"
 msgstr "nu pot citi numele maşinii: %s"
 
-#: option.c:3358
+#: option.c:4569
 msgid "only one resolv.conf file allowed in no-poll mode."
 msgstr "se permite un singur fişier resolv.conf în modul no-poll"
 
-#: option.c:3368
+#: option.c:4579
 msgid "must have exactly one resolv.conf to read domain from."
 msgstr "am nevoie de un singur resolv.conf din care să citesc numele domeniului."
 
-#: option.c:3371 network.c:848 dhcp.c:814
+#: option.c:4582 network.c:1507 dhcp.c:777
 #, fuzzy, c-format
 msgid "failed to read %s: %s"
 msgstr "nu pot citi %s: %s"
 
-#: option.c:3388
+#: option.c:4599
 #, c-format
 msgid "no search directive found in %s"
 msgstr "nu s-a găsit nici un criteriu de căutare în %s"
 
-#: option.c:3409
+#: option.c:4620
 msgid "there must be a default domain when --dhcp-fqdn is set"
 msgstr ""
 
-#: option.c:3413
+#: option.c:4629
 msgid "syntax check OK"
 msgstr ""
 
-#: forward.c:461
+#: forward.c:111
+#, fuzzy, c-format
+msgid "failed to send packet: %s"
+msgstr "ascultarea pe socket a eşuat: %s"
+
+#: forward.c:591
+msgid "discarding DNS reply: subnet option mismatch"
+msgstr ""
+
+#: forward.c:614
 #, c-format
 msgid "nameserver %s refused to do a recursive query"
 msgstr "serverul DNS %s refuză interogările recursive"
 
-#: forward.c:489
+#: forward.c:646
 #, c-format
 msgid "possible DNS-rebind attack detected: %s"
 msgstr ""
 
-#: network.c:171
+#: forward.c:1209 forward.c:1815
+msgid "Ignoring query from non-local network"
+msgstr ""
+
+#: forward.c:2286
 #, fuzzy, c-format
-msgid "unknown interface %s in bridge-interface"
-msgstr "interfaţă necunoscută %s"
+msgid "Maximum number of concurrent DNS queries reached (max: %d)"
+msgstr "Specifică numărul maxim de împrumuturi DHCP (implicit %s)."
 
-#: network.c:380
+#: network.c:715
 #, fuzzy, c-format
 msgid "failed to create listening socket for %s: %s"
 msgstr "creearea socket-ului de ascultare a eşuat: %s"
 
-#: network.c:746
+#: network.c:1021
+#, c-format
+msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s"
+msgstr ""
+
+#: network.c:1028
+msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)"
+msgstr ""
+
+#: network.c:1037
+#, fuzzy, c-format
+msgid "warning: no addresses found for interface %s"
+msgstr "folosim adresele locale doar pentru %S %s"
+
+#: network.c:1095
+#, fuzzy, c-format
+msgid "interface %s failed to join DHCPv6 multicast group: %s"
+msgstr "activarea socket-ului server-ului DHCP a eşuat: %s"
+
+#: network.c:1289
 #, fuzzy, c-format
 msgid "failed to bind server socket for %s: %s"
 msgstr "activarea socket-ului de ascultare pentru %s a eşuat: %s"
 
-#: network.c:783
+#: network.c:1445
 #, c-format
 msgid "ignoring nameserver %s - local interface"
 msgstr "ignorăm serverul DNS %s - interfaţă locală"
 
-#: network.c:794
+#: network.c:1456
 #, fuzzy, c-format
 msgid "ignoring nameserver %s - cannot make/bind socket: %s"
 msgstr "ignorăm serverul DNS %s - nu pot creea/activa socket-ul: %s"
 
-#: network.c:811
+#: network.c:1469
 msgid "unqualified"
 msgstr "invalid"
 
-#: network.c:811
+#: network.c:1469
 msgid "names"
 msgstr ""
 
-#: network.c:813
+#: network.c:1471
 msgid "default"
 msgstr ""
 
-#: network.c:815
+#: network.c:1473
 msgid "domain"
 msgstr "domeniu"
 
-#: network.c:818
+#: network.c:1476
 #, c-format
 msgid "using local addresses only for %s %s"
 msgstr "folosim adresele locale doar pentru %S %s"
 
-#: network.c:820
+#: network.c:1478
 #, fuzzy, c-format
 msgid "using standard nameservers for %s %s"
 msgstr "folosim serverul DNS %s#%d pentru %s %s"
 
-#: network.c:822
+#: network.c:1480
 #, c-format
 msgid "using nameserver %s#%d for %s %s"
 msgstr "folosim serverul DNS %s#%d pentru %s %s"
 
-#: network.c:825
+#: network.c:1484
+#, fuzzy, c-format
+msgid "NOT using nameserver %s#%d - query loop detected"
+msgstr "folosim serverul DNS %s#%d pentru %s %s"
+
+#: network.c:1487
 #, fuzzy, c-format
 msgid "using nameserver %s#%d(via %s)"
 msgstr "folosim serverul DNS %s#%d"
 
-#: network.c:827
+#: network.c:1489
 #, c-format
 msgid "using nameserver %s#%d"
 msgstr "folosim serverul DNS %s#%d"
 
-#: dnsmasq.c:148
-#, fuzzy
-msgid "TFTP server not available: set HAVE_TFTP in src/config.h"
-msgstr "DBus nu este disponibil: puneţi HAVE_DBUS in src/config.h"
+#: dnsmasq.c:163
+msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"
+msgstr ""
 
-#: dnsmasq.c:153
+#: dnsmasq.c:170
+msgid "no trust anchors provided for DNSSEC"
+msgstr ""
+
+#: dnsmasq.c:173
+msgid "cannot reduce cache size from default when DNSSEC enabled"
+msgstr ""
+
+#: dnsmasq.c:175
+#, fuzzy
+msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h"
+msgstr "DBus nu este disponibil: puneţi HAVE_DBUS in src/config.h"
+
+#: dnsmasq.c:181
+#, fuzzy
+msgid "TFTP server not available: set HAVE_TFTP in src/config.h"
+msgstr "DBus nu este disponibil: puneţi HAVE_DBUS in src/config.h"
+
+#: dnsmasq.c:186
+msgid "cannot use --conntrack AND --query-port"
+msgstr ""
+
+#: dnsmasq.c:189
+#, fuzzy
+msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h"
+msgstr "DBus nu este disponibil: puneţi HAVE_DBUS in src/config.h"
+
+#: dnsmasq.c:194
 msgid "asychronous logging is not available under Solaris"
 msgstr ""
 
-#: dnsmasq.c:158
+#: dnsmasq.c:199
 msgid "asychronous logging is not available under Android"
 msgstr ""
 
-#: dnsmasq.c:177
+#: dnsmasq.c:204
+#, fuzzy
+msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h"
+msgstr "DBus nu este disponibil: puneţi HAVE_DBUS in src/config.h"
+
+#: dnsmasq.c:209
+#, fuzzy
+msgid "loop detection not available: set HAVE_LOOP in src/config.h"
+msgstr "DBus nu este disponibil: puneţi HAVE_DBUS in src/config.h"
+
+#: dnsmasq.c:217
+msgid "zone serial must be configured in --auth-soa"
+msgstr ""
+
+#: dnsmasq.c:235
+msgid "dhcp-range constructor not available on this platform"
+msgstr ""
+
+#: dnsmasq.c:278
+msgid "cannot set --bind-interfaces and --bind-dynamic"
+msgstr ""
+
+#: dnsmasq.c:281
 #, c-format
 msgid "failed to find list of interfaces: %s"
 msgstr "enumerarea interfeţelor a eşuat: %s"
 
-#: dnsmasq.c:185
+#: dnsmasq.c:290
 #, c-format
 msgid "unknown interface %s"
 msgstr "interfaţă necunoscută %s"
 
-#: dnsmasq.c:191
-#, c-format
-msgid "no interface with address %s"
-msgstr "nu exista interfaţă pentru adresa %s"
-
-#: dnsmasq.c:207 dnsmasq.c:678
+#: dnsmasq.c:354 dnsmasq.c:997
 #, c-format
 msgid "DBus error: %s"
 msgstr "eroare DBus: %s"
 
-#: dnsmasq.c:210
+#: dnsmasq.c:357
 msgid "DBus not available: set HAVE_DBUS in src/config.h"
 msgstr "DBus nu este disponibil: puneţi HAVE_DBUS in src/config.h"
 
-#: dnsmasq.c:236
+#: dnsmasq.c:385
 #, c-format
 msgid "unknown user or group: %s"
 msgstr ""
 
-#: dnsmasq.c:291
+#: dnsmasq.c:440
 #, c-format
 msgid "cannot chdir to filesystem root: %s"
 msgstr ""
 
-#: dnsmasq.c:455
+#: dnsmasq.c:692
 #, fuzzy, c-format
 msgid "started, version %s DNS disabled"
 msgstr "am pornit, versiunea %s memorie temporară dezactivată"
 
-#: dnsmasq.c:457
+#: dnsmasq.c:694
 #, c-format
 msgid "started, version %s cachesize %d"
 msgstr "am ponit, versiunea %s memorie temporară %d"
 
-#: dnsmasq.c:459
+#: dnsmasq.c:696
 #, c-format
 msgid "started, version %s cache disabled"
 msgstr "am pornit, versiunea %s memorie temporară dezactivată"
 
-#: dnsmasq.c:461
+#: dnsmasq.c:698
 #, c-format
 msgid "compile time options: %s"
 msgstr "compilat cu opţiunile: %s"
 
-#: dnsmasq.c:467
+#: dnsmasq.c:704
 msgid "DBus support enabled: connected to system bus"
 msgstr "suportul DBus activ: sunt conectat la magistrala sistem"
 
-#: dnsmasq.c:469
+#: dnsmasq.c:706
 msgid "DBus support enabled: bus connection pending"
 msgstr "suportul DBus activ: aştept conexiunea la magistrală"
 
+#: dnsmasq.c:711
+msgid "DNS service limited to local subnets"
+msgstr ""
+
+#: dnsmasq.c:727
+msgid "DNSSEC validation enabled"
+msgstr ""
+
+#: dnsmasq.c:730
+msgid "DNSSEC signature timestamps not checked until first cache reload"
+msgstr ""
+
+#: dnsmasq.c:733
+msgid "DNSSEC signature timestamps not checked until system time valid"
+msgstr ""
+
 # for compatibility purposes the letters â, ă, ş, ţ and î can be written as their look-alike correspondent.
-#: dnsmasq.c:474
+#: dnsmasq.c:738
 #, fuzzy, c-format
 msgid "warning: failed to change owner of %s: %s"
 msgstr "încărcarea numelor din %s: %s a eşuat"
 
-#: dnsmasq.c:478
+#: dnsmasq.c:742
 msgid "setting --bind-interfaces option because of OS limitations"
 msgstr "specific opţiunea --bind-interfaces din cauza limitărilor SO"
 
-#: dnsmasq.c:483
+#: dnsmasq.c:752
 #, c-format
 msgid "warning: interface %s does not currently exist"
 msgstr "atenţie: interfaţa %s nu există momentan"
 
-#: dnsmasq.c:488
+#: dnsmasq.c:757
 msgid "warning: ignoring resolv-file flag because no-resolv is set"
 msgstr ""
 
-#: dnsmasq.c:491
+#: dnsmasq.c:760
 #, fuzzy
 msgid "warning: no upstream servers configured"
 msgstr "configurăm serverele superioare prin Dbus"
 
-#: dnsmasq.c:495
+#: dnsmasq.c:764
 #, c-format
 msgid "asynchronous logging enabled, queue limit is %d messages"
 msgstr ""
 
-#: dnsmasq.c:508
-#, c-format
-msgid "DHCP, static leases only on %.0s%s, lease time %s"
-msgstr "DHCP, împrumuturi statice doar către  %.0s%s, timpul reînoirii %s"
-
-#: dnsmasq.c:510
-#, c-format
-msgid "DHCP, proxy on subnet %.0s%s%.0s"
+#: dnsmasq.c:785
+msgid "IPv6 router advertisement enabled"
 msgstr ""
 
-#: dnsmasq.c:511
+#: dnsmasq.c:790
 #, c-format
-msgid "DHCP, IP range %s -- %s, lease time %s"
-msgstr "DHCP, domeniu IP %s -- %s, timpul reînoirii %s"
+msgid "DHCP, sockets bound exclusively to interface %s"
+msgstr ""
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 msgid "root is "
 msgstr ""
 
-#: dnsmasq.c:526
+#: dnsmasq.c:804
 #, fuzzy
 msgid "enabled"
 msgstr "dezactivat"
 
-#: dnsmasq.c:528
+#: dnsmasq.c:806
 msgid "secure mode"
 msgstr ""
 
-#: dnsmasq.c:554
+#: dnsmasq.c:809
+#, c-format
+msgid "warning: %s inaccessible"
+msgstr ""
+
+#: dnsmasq.c:813
+#, c-format
+msgid "warning: TFTP directory %s inaccessible"
+msgstr ""
+
+#: dnsmasq.c:839
 #, c-format
 msgid "restricting maximum simultaneous TFTP transfers to %d"
 msgstr ""
 
-#: dnsmasq.c:680
+#: dnsmasq.c:999
 msgid "connected to system DBus"
 msgstr "magistrala sistem Dbus conectată"
 
-#: dnsmasq.c:775
+#: dnsmasq.c:1149
 #, c-format
 msgid "cannot fork into background: %s"
 msgstr ""
 
-#: dnsmasq.c:778
+#: dnsmasq.c:1152
 #, fuzzy, c-format
 msgid "failed to create helper: %s"
 msgstr "nu pot citi %s: %s"
 
-#: dnsmasq.c:781
+#: dnsmasq.c:1155
 #, c-format
 msgid "setting capabilities failed: %s"
 msgstr ""
 
 # for compatibility purposes the letters â, ă, ş, ţ and î can be written as their look-alike correspondent.
-#: dnsmasq.c:785
+#: dnsmasq.c:1158
 #, fuzzy, c-format
 msgid "failed to change user-id to %s: %s"
 msgstr "încărcarea numelor din %s: %s a eşuat"
 
 # for compatibility purposes the letters â, ă, ş, ţ and î can be written as their look-alike correspondent.
-#: dnsmasq.c:790
+#: dnsmasq.c:1161
 #, fuzzy, c-format
 msgid "failed to change group-id to %s: %s"
 msgstr "încărcarea numelor din %s: %s a eşuat"
 
-#: dnsmasq.c:793
+#: dnsmasq.c:1164
 #, fuzzy, c-format
 msgid "failed to open pidfile %s: %s"
 msgstr "nu pot citi %s: %s"
 
-#: dnsmasq.c:796
+#: dnsmasq.c:1167
 #, fuzzy, c-format
-msgid "cannot open %s: %s"
+msgid "cannot open log %s: %s"
 msgstr "nu pot deschide %s:%s"
 
-#: dnsmasq.c:851
+#: dnsmasq.c:1170
+#, fuzzy, c-format
+msgid "failed to load Lua script: %s"
+msgstr "nu pot încărca %s: %s"
+
+#: dnsmasq.c:1173
 #, c-format
-msgid "child process killed by signal %d"
+msgid "TFTP directory %s inaccessible: %s"
 msgstr ""
 
-#: dnsmasq.c:855
+#: dnsmasq.c:1176
+#, fuzzy, c-format
+msgid "cannot create timestamp file %s: %s"
+msgstr "nu pot creea sau deschide fişierul cu împrumuturi: %s"
+
+#: dnsmasq.c:1197
+msgid "now checking DNSSEC signature timestamps"
+msgstr ""
+
+#: dnsmasq.c:1264
+#, c-format
+msgid "script process killed by signal %d"
+msgstr ""
+
+#: dnsmasq.c:1268
 #, c-format
-msgid "child process exited with status %d"
+msgid "script process exited with status %d"
 msgstr ""
 
-#: dnsmasq.c:859
+#: dnsmasq.c:1272
 #, fuzzy, c-format
 msgid "failed to execute %s: %s"
 msgstr "accesarea serverului %s a eşuat: %s"
 
-#: dnsmasq.c:903
+#: dnsmasq.c:1327
 msgid "exiting on receipt of SIGTERM"
 msgstr "am primit SIGTERM, am terminat"
 
-#: dnsmasq.c:931
+#: dnsmasq.c:1355
 #, fuzzy, c-format
 msgid "failed to access %s: %s"
 msgstr "accesarea serverului %s a eşuat: %s"
 
-#: dnsmasq.c:961
+#: dnsmasq.c:1385
 #, c-format
 msgid "reading %s"
 msgstr "citesc %s"
 
-#: dnsmasq.c:972
+#: dnsmasq.c:1396
 #, fuzzy, c-format
 msgid "no servers found in %s, will retry"
 msgstr "nu s-a găsit nici un criteriu de căutare în %s"
 
-#: dhcp.c:40
+#: dhcp.c:53
 #, c-format
 msgid "cannot create DHCP socket: %s"
 msgstr "nu pot creea socket DHCP: %s"
 
-#: dhcp.c:52
+#: dhcp.c:68
 #, c-format
 msgid "failed to set options on DHCP socket: %s"
 msgstr "configurarea opţiunilor socketului DHCP a eşuat: %s"
 
-#: dhcp.c:65
+#: dhcp.c:89
 #, fuzzy, c-format
 msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"
 msgstr "configurarea SO_REUSEADDR pe socket-ul DHCP a eşuat: %s"
 
-#: dhcp.c:77
+#: dhcp.c:101
 #, c-format
 msgid "failed to bind DHCP server socket: %s"
 msgstr "activarea socket-ului server-ului DHCP a eşuat: %s"
 
-#: dhcp.c:103
+#: dhcp.c:127
 #, c-format
 msgid "cannot create ICMP raw socket: %s."
 msgstr "nu pot creea socket ICMP raw: %s."
 
+#: dhcp.c:241 dhcp6.c:180
+#, fuzzy, c-format
+msgid "unknown interface %s in bridge-interface"
+msgstr "interfaţă necunoscută %s"
+
 #: dhcp.c:281
 #, c-format
 msgid "DHCP packet received on %s which has no address"
 msgstr ""
 
-#: dhcp.c:445
+#: dhcp.c:415
+#, c-format
+msgid "ARP-cache injection failed: %s"
+msgstr ""
+
+#: dhcp.c:514
 #, c-format
 msgid "DHCP range %s -- %s is not consistent with netmask %s"
 msgstr "domeniu DHCP %s -- %s nu este consistent cu masca de reţea %s"
 
-#: dhcp.c:852
+#: dhcp.c:815
 #, c-format
 msgid "bad line at %s line %d"
 msgstr "linie invalidă în %s rândul %d"
 
-#: dhcp.c:895
+#: dhcp.c:858
 #, c-format
 msgid "ignoring %s line %d, duplicate name or IP address"
 msgstr ""
 
-#: dhcp.c:978
-#, c-format
-msgid "duplicate IP address %s in dhcp-config directive."
-msgstr "adresă IP duplicat %s în declaraţia dhcp-config."
-
-#: dhcp.c:981
-#, fuzzy, c-format
-msgid "duplicate IP address %s in %s."
-msgstr "adresă IP duplicat %s în declaraţia dhcp-config."
-
-#: dhcp.c:1024
+#: dhcp.c:1002 rfc3315.c:2135
 #, c-format
-msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgid "DHCP relay %s -> %s"
 msgstr ""
 
-#: dhcp.c:1029
-#, c-format
-msgid "duplicate IP address %s (%s) in dhcp-config directive"
-msgstr "adresă IP duplicat %s (%s) în declaraţia dhcp-config."
-
-#: lease.c:67
+#: lease.c:61
 #, fuzzy, c-format
 msgid "cannot open or create lease file %s: %s"
 msgstr "nu pot creea sau deschide fişierul cu împrumuturi: %s"
 
-#: lease.c:93
+#: lease.c:134
 msgid "too many stored leases"
 msgstr "prea multe împrumuturi stocate"
 
-#: lease.c:129
+#: lease.c:165
 #, fuzzy, c-format
 msgid "cannot run lease-init script %s: %s"
 msgstr "nu pot citi %s: %s"
 
-#: lease.c:135
+#: lease.c:171
 #, c-format
 msgid "lease-init script returned exit code %s"
 msgstr ""
 
-#: lease.c:235
+#: lease.c:342
 #, fuzzy, c-format
 msgid "failed to write %s: %s (retry in %us)"
 msgstr "nu pot citi %s: %s"
 
-#: rfc2131.c:315
+#: lease.c:906
+#, c-format
+msgid "Ignoring domain %s for DHCP host name %s"
+msgstr ""
+
+#: rfc2131.c:344
 #, c-format
 msgid "no address range available for DHCP request %s %s"
 msgstr "nici un domeniu de adrese disponibil pentru cererea DHCP %s %s"
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "with subnet selector"
 msgstr "cu selectorul de subreţea"
 
-#: rfc2131.c:316
+#: rfc2131.c:345
 msgid "via"
 msgstr "prin"
 
-#: rfc2131.c:331
+#: rfc2131.c:357
 #, fuzzy, c-format
 msgid "%u available DHCP subnet: %s/%s"
 msgstr "nici un domeniu de adrese disponibil pentru cererea DHCP %s %s"
 
-#: rfc2131.c:334
+#: rfc2131.c:360 rfc3315.c:300
 #, c-format
 msgid "%u available DHCP range: %s -- %s"
 msgstr ""
 
-#: rfc2131.c:363
+#: rfc2131.c:471
+#, fuzzy, c-format
+msgid "%u vendor class: %s"
+msgstr "eroare DBus: %s"
+
+#: rfc2131.c:473
+#, fuzzy, c-format
+msgid "%u user class: %s"
+msgstr "eroare DBus: %s"
+
+#: rfc2131.c:500
 msgid "disabled"
 msgstr "dezactivat"
 
-#: rfc2131.c:404 rfc2131.c:916 rfc2131.c:1288
+#: rfc2131.c:541 rfc2131.c:974 rfc2131.c:1380 rfc3315.c:603 rfc3315.c:856
+#: rfc3315.c:1135
 msgid "ignored"
 msgstr "ignorat"
 
-#: rfc2131.c:419 rfc2131.c:1135
+#: rfc2131.c:556 rfc2131.c:1207 rfc3315.c:906
 msgid "address in use"
 msgstr "adresa este folosită"
 
-#: rfc2131.c:433 rfc2131.c:970
+#: rfc2131.c:570 rfc2131.c:1028
 msgid "no address available"
 msgstr "nici o adresă disponibilă"
 
-#: rfc2131.c:440 rfc2131.c:1098
+#: rfc2131.c:577 rfc2131.c:1170
 msgid "wrong network"
 msgstr "reţea greşită"
 
-#: rfc2131.c:454
+#: rfc2131.c:592
 msgid "no address configured"
 msgstr "adresă lipsă"
 
-#: rfc2131.c:460 rfc2131.c:1148
+#: rfc2131.c:598 rfc2131.c:1220
 msgid "no leases left"
 msgstr "nu mai am de unde să împrumut"
 
-#: rfc2131.c:545
+#: rfc2131.c:693 rfc3315.c:476
 #, c-format
 msgid "%u client provides name: %s"
 msgstr ""
 
-#: rfc2131.c:700
-#, fuzzy, c-format
-msgid "%u vendor class: %s"
-msgstr "eroare DBus: %s"
-
-#: rfc2131.c:702
-#, fuzzy, c-format
-msgid "%u user class: %s"
-msgstr "eroare DBus: %s"
-
-#: rfc2131.c:761
+#: rfc2131.c:798
 msgid "PXE BIS not supported"
 msgstr ""
 
-#: rfc2131.c:886
+#: rfc2131.c:942 rfc3315.c:1229
 #, fuzzy, c-format
 msgid "disabling DHCP static address %s for %s"
 msgstr "dezactivăm adresele DHCP statice %s"
 
-#: rfc2131.c:907
+#: rfc2131.c:963
 msgid "unknown lease"
 msgstr "împrumut necunoscut"
 
-#: rfc2131.c:939
+#: rfc2131.c:997
 #, c-format
 msgid "not using configured address %s because it is leased to %s"
 msgstr ""
 
-#: rfc2131.c:949
+#: rfc2131.c:1007
 #, c-format
 msgid "not using configured address %s because it is in use by the server or relay"
 msgstr ""
 
-#: rfc2131.c:952
+#: rfc2131.c:1010
 #, c-format
 msgid "not using configured address %s because it was previously declined"
 msgstr ""
 
-#: rfc2131.c:968 rfc2131.c:1141
+#: rfc2131.c:1026 rfc2131.c:1213
 msgid "no unique-id"
 msgstr ""
 
-#: rfc2131.c:1037
+#: rfc2131.c:1108
 msgid "wrong server-ID"
 msgstr ""
 
-#: rfc2131.c:1055
+#: rfc2131.c:1127
 msgid "wrong address"
 msgstr "adresă greşită"
 
-#: rfc2131.c:1073
+#: rfc2131.c:1145 rfc3315.c:1002
 msgid "lease not found"
 msgstr "împrumutul nu a fost găsit"
 
-#: rfc2131.c:1106
+#: rfc2131.c:1178
 msgid "address not available"
 msgstr "adresă indisponibilă"
 
-#: rfc2131.c:1117
+#: rfc2131.c:1189
 msgid "static lease available"
 msgstr "împrumut static este disponibil"
 
-#: rfc2131.c:1121
+#: rfc2131.c:1193
 msgid "address reserved"
 msgstr "adresă rezervată"
 
-#: rfc2131.c:1129
+#: rfc2131.c:1201
 #, c-format
 msgid "abandoning lease to %s of %s"
 msgstr ""
 
-#: rfc2131.c:1710
-#, c-format
-msgid "%u tags: %s"
-msgstr ""
-
-#: rfc2131.c:1723
+#: rfc2131.c:1707
 #, c-format
 msgid "%u bootfile name: %s"
 msgstr ""
 
-#: rfc2131.c:1732
+#: rfc2131.c:1716
 #, fuzzy, c-format
 msgid "%u server name: %s"
 msgstr "eroare DBus: %s"
 
-#: rfc2131.c:1746
+#: rfc2131.c:1724
 #, fuzzy, c-format
 msgid "%u next server: %s"
 msgstr "eroare DBus: %s"
 
-#: rfc2131.c:1749
+#: rfc2131.c:1727
 #, c-format
 msgid "%u broadcast response"
 msgstr ""
 
-#: rfc2131.c:1812
+#: rfc2131.c:1790
 #, fuzzy, c-format
 msgid "cannot send DHCP/BOOTP option %d: no space left in packet"
 msgstr "nu pot trimite opţiunea DHCP %d: nu mai este loc în pachet"
 
-#: rfc2131.c:2058
+#: rfc2131.c:2031
 msgid "PXE menu too large"
 msgstr ""
 
-#: rfc2131.c:2171
-#, c-format
-msgid "Ignoring domain %s for DHCP host name %s"
-msgstr ""
-
-#: rfc2131.c:2189
+#: rfc2131.c:2170 rfc3315.c:1502
 #, fuzzy, c-format
 msgid "%u requested options: %s"
 msgstr "compilat cu opţiunile: %s"
 
-#: rfc2131.c:2456
+#: rfc2131.c:2487
 #, c-format
 msgid "cannot send RFC3925 option: too many options for enterprise number %d"
 msgstr ""
 
-#: netlink.c:70
+#: netlink.c:77
 #, fuzzy, c-format
 msgid "cannot create netlink socket: %s"
 msgstr "nu pot să activez socket-ul netlink: %s"
 
-#: netlink.c:288
+#: netlink.c:348
 #, fuzzy, c-format
 msgid "netlink returns error: %s"
 msgstr "eroare DBus: %s"
 
-#: dbus.c:150
+#: dbus.c:186
 msgid "attempt to set an IPv6 server address via DBus - no IPv6 support"
 msgstr "incerc să configurez un server IPv6 prin Dbus - nu este suport IPv6"
 
-#: dbus.c:286
+#: dbus.c:439
+#, c-format
+msgid "Enabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:444
+#, c-format
+msgid "Disabling --%s option from D-Bus"
+msgstr ""
+
+#: dbus.c:691
 msgid "setting upstream servers from DBus"
 msgstr "configurăm serverele superioare prin Dbus"
 
-#: dbus.c:324
+#: dbus.c:738
 msgid "could not register a DBus message handler"
 msgstr "nu pot activa o interfaţă de mesaje DBus"
 
-#: bpf.c:217
+#: bpf.c:263
 #, c-format
 msgid "cannot create DHCP BPF socket: %s"
 msgstr "nu pot creea socket DHCP BPF: %s"
 
-#: bpf.c:245
+#: bpf.c:291
 #, fuzzy, c-format
 msgid "DHCP request for unsupported hardware type (%d) received on %s"
 msgstr "cerere DHCP pentru dispozitiv nesuportat (%d) recepţionată prin %s"
 
-#: tftp.c:281
+#: bpf.c:376
+#, fuzzy, c-format
+msgid "cannot create PF_ROUTE socket: %s"
+msgstr "nu pot creea socket DHCP: %s"
+
+#: bpf.c:397
+msgid "Unknown protocol version from route socket"
+msgstr ""
+
+#: helper.c:153
+msgid "lease() function missing in Lua script"
+msgstr ""
+
+#: tftp.c:309
 msgid "unable to get free port for TFTP"
 msgstr ""
 
-#: tftp.c:296
+#: tftp.c:325
 #, c-format
 msgid "unsupported request from %s"
 msgstr ""
 
-#: tftp.c:406
+#: tftp.c:439
 #, fuzzy, c-format
 msgid "file %s not found"
 msgstr "împrumutul nu a fost găsit"
 
-#: tftp.c:522
+#: tftp.c:548
 #, c-format
 msgid "error %d %s received from %s"
 msgstr ""
 
-#: tftp.c:554
+#: tftp.c:590
 #, fuzzy, c-format
 msgid "failed sending %s to %s"
 msgstr "nu pot citi %s: %s"
 
-#: tftp.c:568
+#: tftp.c:590
 #, c-format
 msgid "sent %s to %s"
 msgstr ""
 
-#: log.c:177
+#: log.c:190
 #, c-format
 msgid "overflow: %d log entries lost"
 msgstr ""
 
-#: log.c:254
+#: log.c:268
 #, c-format
 msgid "log failed: %s"
 msgstr ""
 
-#: log.c:462
+#: log.c:469
 msgid "FAILED to start up"
 msgstr "pornirea A EŞUAT"
 
+#: conntrack.c:65
+#, c-format
+msgid "Conntrack connection mark retrieval failed: %s"
+msgstr ""
+
+#: dhcp6.c:59
+#, fuzzy, c-format
+msgid "cannot create DHCPv6 socket: %s"
+msgstr "nu pot creea socket DHCP: %s"
+
+#: dhcp6.c:80
+#, fuzzy, c-format
+msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"
+msgstr "configurarea SO_REUSEADDR pe socket-ul DHCP a eşuat: %s"
+
+#: dhcp6.c:92
+#, fuzzy, c-format
+msgid "failed to bind DHCPv6 server socket: %s"
+msgstr "activarea socket-ului server-ului DHCP a eşuat: %s"
+
+#: rfc3315.c:157
+#, fuzzy, c-format
+msgid "no address range available for DHCPv6 request from relay at %s"
+msgstr "nici un domeniu de adrese disponibil pentru cererea DHCP %s %s"
+
+#: rfc3315.c:166
+#, fuzzy, c-format
+msgid "no address range available for DHCPv6 request via %s"
+msgstr "nici un domeniu de adrese disponibil pentru cererea DHCP %s %s"
+
+#: rfc3315.c:297
+#, fuzzy, c-format
+msgid "%u available DHCPv6 subnet: %s/%d"
+msgstr "nici un domeniu de adrese disponibil pentru cererea DHCP %s %s"
+
+#: rfc3315.c:380
+#, fuzzy, c-format
+msgid "%u vendor class: %u"
+msgstr "eroare DBus: %s"
+
+#: rfc3315.c:428
+#, fuzzy, c-format
+msgid "%u client MAC address: %s"
+msgstr "nu exista interfaţă pentru adresa %s"
+
+#: rfc3315.c:660
+#, fuzzy, c-format
+msgid "unknown prefix-class %d"
+msgstr "împrumut necunoscut"
+
+#: rfc3315.c:803 rfc3315.c:898
+#, fuzzy
+msgid "address unavailable"
+msgstr "adresă indisponibilă"
+
+#: rfc3315.c:815 rfc3315.c:946 rfc3315.c:1279
+msgid "success"
+msgstr ""
+
+#: rfc3315.c:830 rfc3315.c:839 rfc3315.c:954 rfc3315.c:956
+#, fuzzy
+msgid "no addresses available"
+msgstr "nici o adresă disponibilă"
+
+#: rfc3315.c:933
+msgid "not on link"
+msgstr ""
+
+#: rfc3315.c:1006 rfc3315.c:1191 rfc3315.c:1268
+msgid "no binding found"
+msgstr ""
+
+#: rfc3315.c:1044
+msgid "deprecated"
+msgstr ""
+
+#: rfc3315.c:1049
+#, fuzzy
+msgid "address invalid"
+msgstr "adresa este folosită"
+
+#: rfc3315.c:1096
+msgid "confirm failed"
+msgstr ""
+
+#: rfc3315.c:1112
+#, fuzzy
+msgid "all addresses still on link"
+msgstr "adresă greşită în %s, linia %d"
+
+#: rfc3315.c:1200
+msgid "release received"
+msgstr ""
+
+#: rfc3315.c:2126
+msgid "Cannot multicast to DHCPv6 server without correct interface"
+msgstr ""
+
+#: dhcp-common.c:145
+#, c-format
+msgid "Ignoring duplicate dhcp-option %d"
+msgstr ""
+
+#: dhcp-common.c:222
+#, c-format
+msgid "%u tags: %s"
+msgstr ""
+
+#: dhcp-common.c:407
+#, c-format
+msgid "%s has more than one address in hostsfile, using %s for DHCP"
+msgstr ""
+
+#: dhcp-common.c:430
+#, c-format
+msgid "duplicate IP address %s (%s) in dhcp-config directive"
+msgstr "adresă IP duplicat %s (%s) în declaraţia dhcp-config."
+
+#: dhcp-common.c:494
+#, fuzzy, c-format
+msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s"
+msgstr "configurarea SO_REUSEADDR pe socket-ul DHCP a eşuat: %s"
+
+#: dhcp-common.c:615
+#, c-format
+msgid "Known DHCP options:\n"
+msgstr ""
+
+#: dhcp-common.c:626
+#, c-format
+msgid "Known DHCPv6 options:\n"
+msgstr ""
+
+#: dhcp-common.c:823
+msgid ", prefix deprecated"
+msgstr ""
+
+#: dhcp-common.c:826
+#, c-format
+msgid ", lease time "
+msgstr ""
+
+#: dhcp-common.c:868
+#, c-format
+msgid "%s stateless on %s%.0s%.0s%s"
+msgstr ""
+
+#: dhcp-common.c:870
+#, fuzzy, c-format
+msgid "%s, static leases only on %.0s%s%s%.0s"
+msgstr "DHCP, împrumuturi statice doar către  %.0s%s, timpul reînoirii %s"
+
+#: dhcp-common.c:872
+#, c-format
+msgid "%s, proxy on subnet %.0s%s%.0s%.0s"
+msgstr ""
+
+#: dhcp-common.c:873
+#, fuzzy, c-format
+msgid "%s, IP range %s -- %s%s%.0s"
+msgstr "DHCP, domeniu IP %s -- %s, timpul reînoirii %s"
+
+#: dhcp-common.c:886
+#, c-format
+msgid "DHCPv4-derived IPv6 names on %s%s"
+msgstr ""
+
+#: dhcp-common.c:889
+#, fuzzy, c-format
+msgid "router advertisement on %s%s"
+msgstr "DHCP, împrumuturi statice doar către  %.0s%s, timpul reînoirii %s"
+
+#: dhcp-common.c:900
+#, c-format
+msgid "DHCP relay from %s to %s via %s"
+msgstr ""
+
+#: dhcp-common.c:902
+#, c-format
+msgid "DHCP relay from %s to %s"
+msgstr ""
+
+#: radv.c:109
+#, fuzzy, c-format
+msgid "cannot create ICMPv6 socket: %s"
+msgstr "nu pot creea socket DHCP: %s"
+
+#: auth.c:448
+#, c-format
+msgid "ignoring zone transfer request from %s"
+msgstr ""
+
+#: ipset.c:95
+#, fuzzy, c-format
+msgid "failed to find kernel version: %s"
+msgstr "activarea socket-ului server-ului DHCP a eşuat: %s"
+
+#: ipset.c:114
+#, fuzzy, c-format
+msgid "failed to create IPset control socket: %s"
+msgstr "creearea socket-ului de ascultare a eşuat: %s"
+
+#: dnssec.c:449 dnssec.c:493
+#, fuzzy, c-format
+msgid "failed to update mtime on %s: %s"
+msgstr "nu pot citi %s: %s"
+
+#: blockdata.c:58
+#, c-format
+msgid "DNSSEC memory in use %u, max %u, allocated %u"
+msgstr ""
+
+#: tables.c:80
+msgid "error: fill_addr missused"
+msgstr ""
+
+#: tables.c:109
+#, fuzzy, c-format
+msgid "failed to access pf devices: %s"
+msgstr "accesarea serverului %s a eşuat: %s"
+
+#: tables.c:123
+#, fuzzy, c-format
+msgid "warning: no opened pf devices %s"
+msgstr "folosim adresele locale doar pentru %S %s"
+
+#: tables.c:131
+#, fuzzy, c-format
+msgid "error: cannot use table name %s"
+msgstr "nu pot citi numele maşinii: %s"
+
+#: tables.c:139
+#, c-format
+msgid "error: cannot strlcpy table name %s"
+msgstr ""
+
+#: tables.c:145
+#, c-format
+msgid "warning: pfr_add_tables: %s(%d)"
+msgstr ""
+
+#: tables.c:151
+msgid "info: table created"
+msgstr ""
+
+#: tables.c:162
+#, c-format
+msgid "warning: DIOCR%sADDRS: %s"
+msgstr ""
+
+#: tables.c:166
+#, fuzzy, c-format
+msgid "%d addresses %s"
+msgstr "citesc %s - %d adrese"
+
+#: inotify.c:59
+#, fuzzy, c-format
+msgid "cannot access path %s: %s"
+msgstr "nu pot citi %s: %s"
+
+#: inotify.c:92
+#, fuzzy, c-format
+msgid "failed to create inotify: %s"
+msgstr "nu pot citi %s: %s"
+
+#: inotify.c:105
+#, c-format
+msgid "too many symlinks following %s"
+msgstr ""
+
+#: inotify.c:121
+#, c-format
+msgid "directory %s for resolv-file is missing, cannot poll"
+msgstr ""
+
+#: inotify.c:125 inotify.c:162
+#, fuzzy, c-format
+msgid "failed to create inotify for %s: %s"
+msgstr "creearea socket-ului de ascultare a eşuat: %s"
+
+#: inotify.c:147
+#, fuzzy, c-format
+msgid "bad dynamic directory %s: %s"
+msgstr "nu pot citi %s: %s"
+
+#: inotify.c:247
+#, c-format
+msgid "inotify, new or changed file %s"
+msgstr ""
+
+#, fuzzy
+#~ msgid "cannot cannonicalise resolv-file %s: %s"
+#~ msgstr "nu pot creea sau deschide fişierul cu împrumuturi: %s"
+
+#~ msgid "duplicate IP address %s in dhcp-config directive."
+#~ msgstr "adresă IP duplicat %s în declaraţia dhcp-config."
+
+#, fuzzy
+#~ msgid "Specify path to Lua script (no default)."
+#~ msgstr "Specifică o cale pentru fişierul PID. (implicit %s)."
+
 #~ msgid "TXT record string too long"
 #~ msgstr "şirul de caractere pentru înregistrarea TXT este prea lung"
 
@@ -1500,23 +2221,9 @@ msgstr "pornirea A EŞUAT"
 #~ msgid "failed to bind listening socket for %s: %s"
 #~ msgstr "activarea socket-ului de ascultare pentru %s a eşuat: %s"
 
-#~ msgid "failed to listen on socket: %s"
-#~ msgstr "ascultarea pe socket a eşuat: %s"
-
-#, fuzzy
-#~ msgid "failed to create TFTP socket: %s"
-#~ msgstr "creearea socket-ului de ascultare a eşuat: %s"
-
 #~ msgid "must set exactly one interface on broken systems without IP_RECVIF"
 #~ msgstr "trebuie specificată exact o singură interfaţă pe sistemele defectece nu au IP_RECVIF"
 
-#, fuzzy
-#~ msgid "failed to load %s: %s"
-#~ msgstr "nu pot încărca %s: %s"
-
-#~ msgid "bad name in %s"
-#~ msgstr "nume invalid în %s"
-
 #~ msgid "Ignoring DHCP lease for %s because it has an illegal domain part"
 #~ msgstr "Împrumutul DHCP pentru %s va fi ignorat deoarece are domeniu invalid"
 
index 1183f3b..482ff5d 100644 (file)
@@ -221,7 +221,7 @@ triggering dial-on-demand internet links.
 Sending SIGHUP to the dnsmasq process will cause it to empty its cache and 
 then re-load <TT>/etc/hosts</TT> and <TT>/etc/resolv.conf</TT>.
 <P> Sending SIGUSR1 (killall -10 dnsmasq) to the dnsmasq process will
-cause to to write cache usage statisticss to the log, typically
+cause to write cache usage statisticss to the log, typically
 <TT>/var/log/syslog</TT> or <TT>/var/log/messages</TT>.
 <P> The <TT>log-queries</TT> option tells dnsmasq to verbosely log the queries
 it is handling and causes SIGUSR1 to trigger a complete dump of the
diff --git a/src/auth.c b/src/auth.c
new file mode 100644 (file)
index 0000000..2b0b7d6
--- /dev/null
@@ -0,0 +1,829 @@
+/* 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"
+
+#ifdef HAVE_AUTH
+
+static struct addrlist *find_subnet(struct auth_zone *zone, int flag, struct all_addr *addr_u)
+{
+  struct addrlist *subnet;
+
+  for (subnet = zone->subnet; subnet; subnet = subnet->next)
+    {
+      if (!(subnet->flags & ADDRLIST_IPV6))
+       {
+         struct in_addr netmask, addr = addr_u->addr.addr4;
+
+         if (!(flag & F_IPV4))
+           continue;
+         
+         netmask.s_addr = htonl(~(in_addr_t)0 << (32 - subnet->prefixlen));
+         
+         if  (is_same_net(addr, subnet->addr.addr.addr4, netmask))
+           return subnet;
+       }
+#ifdef HAVE_IPV6
+      else if (is_same_net6(&(addr_u->addr.addr6), &subnet->addr.addr.addr6, subnet->prefixlen))
+       return subnet;
+#endif
+
+    }
+  return NULL;
+}
+
+static int filter_zone(struct auth_zone *zone, int flag, struct all_addr *addr_u)
+{
+  /* No zones specified, no filter */
+  if (!zone->subnet)
+    return 1;
+  
+  return find_subnet(zone, flag, addr_u) != NULL;
+}
+
+int in_zone(struct auth_zone *zone, char *name, char **cut)
+{
+  size_t namelen = strlen(name);
+  size_t domainlen = strlen(zone->domain);
+
+  if (cut)
+    *cut = NULL;
+  
+  if (namelen >= domainlen && 
+      hostname_isequal(zone->domain, &name[namelen - domainlen]))
+    {
+      
+      if (namelen == domainlen)
+       return 1;
+      
+      if (name[namelen - domainlen - 1] == '.')
+       {
+         if (cut)
+           *cut = &name[namelen - domainlen - 1]; 
+         return 1;
+       }
+    }
+
+  return 0;
+}
+
+
+size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, int local_query) 
+{
+  char *name = daemon->namebuff;
+  unsigned char *p, *ansp;
+  int qtype, qclass;
+  int nameoffset, axfroffset = 0;
+  int q, anscount = 0, authcount = 0;
+  struct crec *crecp;
+  int  auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0;
+  struct auth_zone *zone = NULL;
+  struct addrlist *subnet = NULL;
+  char *cut;
+  struct mx_srv_record *rec, *move, **up;
+  struct txt_record *txt;
+  struct interface_name *intr;
+  struct naptr *na;
+  struct all_addr addr;
+  struct cname *a;
+  
+  if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
+    return 0;
+
+  /* determine end of question section (we put answers there) */
+  if (!(ansp = skip_questions(header, qlen)))
+    return 0; /* bad packet */
+  
+  /* now process each question, answers go in RRs after the question */
+  p = (unsigned char *)(header+1);
+
+  for (q = ntohs(header->qdcount); q != 0; q--)
+    {
+      unsigned short flag = 0;
+      int found = 0;
+  
+      /* save pointer to name for copying into answers */
+      nameoffset = p - (unsigned char *)header;
+
+      /* now extract name as .-concatenated string into name */
+      if (!extract_name(header, qlen, &p, name, 1, 4))
+       return 0; /* bad packet */
+      GETSHORT(qtype, p); 
+      GETSHORT(qclass, p);
+      
+      if (qclass != C_IN)
+       {
+         auth = 0;
+         continue;
+       }
+
+      if ((qtype == T_PTR || qtype == T_SOA || qtype == T_NS) &&
+         (flag = in_arpa_name_2_addr(name, &addr)) &&
+         !local_query)
+       {
+         for (zone = daemon->auth_zones; zone; zone = zone->next)
+           if ((subnet = find_subnet(zone, flag, &addr)))
+             break;
+         
+         if (!zone)
+           {
+             auth = 0;
+             continue;
+           }
+         else if (qtype == T_SOA)
+           soa = 1, found = 1;
+         else if (qtype == T_NS)
+           ns = 1, found = 1;
+       }
+
+      if (qtype == T_PTR && flag)
+       {
+         intr = NULL;
+
+         if (flag == F_IPV4)
+           for (intr = daemon->int_names; intr; intr = intr->next)
+             {
+               struct addrlist *addrlist;
+               
+               for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
+                 if (!(addrlist->flags & ADDRLIST_IPV6) && addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr)
+                   break;
+               
+               if (addrlist)
+                 break;
+               else
+                 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
+                   intr = intr->next;
+             }
+#ifdef HAVE_IPV6
+         else if (flag == F_IPV6)
+           for (intr = daemon->int_names; intr; intr = intr->next)
+             {
+               struct addrlist *addrlist;
+               
+               for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
+                 if ((addrlist->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6))
+                   break;
+               
+               if (addrlist)
+                 break;
+               else
+                 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
+                   intr = intr->next;
+             }
+#endif
+         
+         if (intr)
+           {
+             if (local_query || in_zone(zone, intr->name, NULL))
+               {       
+                 found = 1;
+                 log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
+                 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                         daemon->auth_ttl, NULL,
+                                         T_PTR, C_IN, "d", intr->name))
+                   anscount++;
+               }
+           }
+         
+         if ((crecp = cache_find_by_addr(NULL, &addr, now, flag)))
+           do { 
+             strcpy(name, cache_get_name(crecp));
+             
+             if (crecp->flags & F_DHCP && !option_bool(OPT_DHCP_FQDN))
+               {
+                 char *p = strchr(name, '.');
+                 if (p)
+                   *p = 0; /* must be bare name */
+                 
+                 /* add  external domain */
+                 if (zone)
+                   {
+                     strcat(name, ".");
+                     strcat(name, zone->domain);
+                   }
+                 log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid));
+                 found = 1;
+                 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                         daemon->auth_ttl, NULL,
+                                         T_PTR, C_IN, "d", name))
+                   anscount++;
+               }
+             else if (crecp->flags & (F_DHCP | F_HOSTS) && (local_query || in_zone(zone, name, NULL)))
+               {
+                 log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
+                 found = 1;
+                 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                         daemon->auth_ttl, NULL,
+                                         T_PTR, C_IN, "d", name))
+                   anscount++;
+               }
+             else
+               continue;
+                   
+           } while ((crecp = cache_find_by_addr(crecp, &addr, now, flag)));
+
+         if (found)
+           nxdomain = 0;
+         else
+           log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL);
+
+         continue;
+       }
+      
+    cname_restart:
+      if (found)
+       /* NS and SOA .arpa requests have set found above. */
+       cut = NULL;
+      else
+       {
+         for (zone = daemon->auth_zones; zone; zone = zone->next)
+           if (in_zone(zone, name, &cut))
+             break;
+         
+         if (!zone)
+           {
+             auth = 0;
+             continue;
+           }
+       }
+
+      for (rec = daemon->mxnames; rec; rec = rec->next)
+       if (!rec->issrv && hostname_isequal(name, rec->name))
+         {
+           nxdomain = 0;
+                
+           if (qtype == T_MX)
+             {
+               found = 1;
+               log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>"); 
+               if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
+                                       NULL, T_MX, C_IN, "sd", rec->weight, rec->target))
+                 anscount++;
+             }
+         }
+      
+      for (move = NULL, up = &daemon->mxnames, rec = daemon->mxnames; rec; rec = rec->next)
+       if (rec->issrv && hostname_isequal(name, rec->name))
+         {
+           nxdomain = 0;
+           
+           if (qtype == T_SRV)
+             {
+               found = 1;
+               log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>"); 
+               if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
+                                       NULL, T_SRV, C_IN, "sssd", 
+                                       rec->priority, rec->weight, rec->srvport, rec->target))
+
+                 anscount++;
+             } 
+           
+           /* unlink first SRV record found */
+           if (!move)
+             {
+               move = rec;
+               *up = rec->next;
+             }
+           else
+             up = &rec->next;      
+         }
+       else
+         up = &rec->next;
+         
+      /* put first SRV record back at the end. */
+      if (move)
+       {
+         *up = move;
+         move->next = NULL;
+       }
+
+      for (txt = daemon->rr; txt; txt = txt->next)
+       if (hostname_isequal(name, txt->name))
+         {
+           nxdomain = 0;
+           if (txt->class == qtype)
+             {
+               found = 1;
+               log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>"); 
+               if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
+                                       NULL, txt->class, C_IN, "t", txt->len, txt->txt))
+                 anscount++;
+             }
+         }
+      
+      for (txt = daemon->txt; txt; txt = txt->next)
+       if (txt->class == C_IN && hostname_isequal(name, txt->name))
+         {
+           nxdomain = 0;
+           if (qtype == T_TXT)
+             {
+               found = 1;
+               log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>"); 
+               if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
+                                       NULL, T_TXT, C_IN, "t", txt->len, txt->txt))
+                 anscount++;
+             }
+         }
+
+       for (na = daemon->naptr; na; na = na->next)
+        if (hostname_isequal(name, na->name))
+          {
+            nxdomain = 0;
+            if (qtype == T_NAPTR)
+              {
+                found = 1;
+                log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
+                if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl, 
+                                        NULL, T_NAPTR, C_IN, "sszzzd", 
+                                        na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
+                         anscount++;
+              }
+          }
+    
+       if (qtype == T_A)
+        flag = F_IPV4;
+       
+#ifdef HAVE_IPV6
+       if (qtype == T_AAAA)
+        flag = F_IPV6;
+#endif
+       
+       for (intr = daemon->int_names; intr; intr = intr->next)
+        if (hostname_isequal(name, intr->name))
+          {
+            struct addrlist *addrlist;
+            
+            nxdomain = 0;
+            
+            if (flag)
+              for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)  
+                if (((addrlist->flags & ADDRLIST_IPV6)  ? T_AAAA : T_A) == qtype &&
+                    (local_query || filter_zone(zone, flag, &addrlist->addr)))
+                  {
+#ifdef HAVE_IPV6
+                    if (addrlist->flags & ADDRLIST_REVONLY)
+                      continue;
+#endif
+                    found = 1;
+                    log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
+                    if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                            daemon->auth_ttl, NULL, qtype, C_IN, 
+                                            qtype == T_A ? "4" : "6", &addrlist->addr))
+                      anscount++;
+                  }
+            }
+       
+       for (a = daemon->cnames; a; a = a->next)
+        if (hostname_isequal(name, a->alias) )
+          {
+            log_query(F_CONFIG | F_CNAME, name, NULL, NULL);
+            strcpy(name, a->target);
+            if (!strchr(name, '.'))
+              {
+                strcat(name, ".");
+                strcat(name, zone->domain);
+              }
+            found = 1;
+            if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                    daemon->auth_ttl, &nameoffset,
+                                    T_CNAME, C_IN, "d", name))
+              anscount++;
+            
+            goto cname_restart;
+          }
+
+      if (!cut)
+       {
+         nxdomain = 0;
+         
+         if (qtype == T_SOA)
+           {
+             auth = soa = 1; /* inhibits auth section */
+             found = 1;
+             log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>");
+           }
+         else if (qtype == T_AXFR)
+           {
+             struct iname *peers;
+             
+             if (peer_addr->sa.sa_family == AF_INET)
+               peer_addr->in.sin_port = 0;
+#ifdef HAVE_IPV6
+             else
+               {
+                 peer_addr->in6.sin6_port = 0; 
+                 peer_addr->in6.sin6_scope_id = 0;
+               }
+#endif
+             
+             for (peers = daemon->auth_peers; peers; peers = peers->next)
+               if (sockaddr_isequal(peer_addr, &peers->addr))
+                 break;
+             
+             /* Refuse all AXFR unless --auth-sec-servers is set */
+             if ((!peers && daemon->auth_peers) || !daemon->secondary_forward_server)
+               {
+                 if (peer_addr->sa.sa_family == AF_INET)
+                   inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
+#ifdef HAVE_IPV6
+                 else
+                   inet_ntop(AF_INET6, &peer_addr->in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN); 
+#endif
+                 
+                 my_syslog(LOG_WARNING, _("ignoring zone transfer request from %s"), daemon->addrbuff);
+                 return 0;
+               }
+                     
+             auth = 1;
+             soa = 1; /* inhibits auth section */
+             ns = 1; /* ensure we include NS records! */
+             axfr = 1;
+             found = 1;
+             axfroffset = nameoffset;
+             log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>");
+           }
+         else if (qtype == T_NS)
+           {
+             auth = 1;
+             ns = 1; /* inhibits auth section */
+             found = 1;
+             log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>"); 
+           }
+       }
+      
+      if (!option_bool(OPT_DHCP_FQDN) && cut)
+       {         
+         *cut = 0; /* remove domain part */
+         
+         if (!strchr(name, '.') && (crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
+           {
+             if (crecp->flags & F_DHCP)
+               do
+                 { 
+                   nxdomain = 0;
+                   if ((crecp->flags & flag) && 
+                       (local_query || filter_zone(zone, flag, &(crecp->addr.addr))))
+                     {
+                       *cut = '.'; /* restore domain part */
+                       log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
+                       *cut  = 0; /* remove domain part */
+                       found = 1;
+                       if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                               daemon->auth_ttl, NULL, qtype, C_IN, 
+                                               qtype == T_A ? "4" : "6", &crecp->addr))
+                         anscount++;
+                     }
+                 } while ((crecp = cache_find_by_name(crecp, name, now,  F_IPV4 | F_IPV6)));
+           }
+                 
+         *cut = '.'; /* restore domain part */     
+       }
+      
+      if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
+       {
+         if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
+           do
+             { 
+                nxdomain = 0;
+                if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr.addr))))
+                  {
+                    log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
+                    found = 1;
+                    if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                            daemon->auth_ttl, NULL, qtype, C_IN, 
+                                            qtype == T_A ? "4" : "6", &crecp->addr))
+                      anscount++;
+                  }
+             } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
+       }
+      
+      if (!found)
+       log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL);
+      
+    }
+  
+  /* Add auth section */
+  if (auth && zone)
+    {
+      char *authname;
+      int newoffset, offset = 0;
+
+      if (!subnet)
+       authname = zone->domain;
+      else
+       {
+         /* handle NS and SOA for PTR records */
+         
+         authname = name;
+
+         if (!(subnet->flags & ADDRLIST_IPV6))
+           {
+             in_addr_t a = ntohl(subnet->addr.addr.addr4.s_addr) >> 8;
+             char *p = name;
+             
+             if (subnet->prefixlen >= 24)
+               p += sprintf(p, "%d.", a & 0xff);
+             a = a >> 8;
+             if (subnet->prefixlen >= 16 )
+               p += sprintf(p, "%d.", a & 0xff);
+             a = a >> 8;
+             p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
+             
+           }
+#ifdef HAVE_IPV6
+         else
+           {
+             char *p = name;
+             int i;
+             
+             for (i = subnet->prefixlen-1; i >= 0; i -= 4)
+               { 
+                 int dig = ((unsigned char *)&subnet->addr.addr.addr6)[i>>3];
+                 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
+               }
+             p += sprintf(p, "ip6.arpa");
+             
+           }
+#endif
+       }
+      
+      /* handle NS and SOA in auth section or for explicit queries */
+       newoffset = ansp - (unsigned char *)header;
+       if (((anscount == 0 && !ns) || soa) &&
+         add_resource_record(header, limit, &trunc, 0, &ansp, 
+                             daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
+                             authname, daemon->authserver,  daemon->hostmaster,
+                             daemon->soa_sn, daemon->soa_refresh, 
+                             daemon->soa_retry, daemon->soa_expiry, 
+                             daemon->auth_ttl))
+       {
+         offset = newoffset;
+         if (soa)
+           anscount++;
+         else
+           authcount++;
+       }
+      
+      if (anscount != 0 || ns)
+       {
+         struct name_list *secondary;
+         
+         newoffset = ansp - (unsigned char *)header;
+         if (add_resource_record(header, limit, &trunc, -offset, &ansp, 
+                                 daemon->auth_ttl, NULL, T_NS, C_IN, "d", offset == 0 ? authname : NULL, daemon->authserver))
+           {
+             if (offset == 0) 
+               offset = newoffset;
+             if (ns) 
+               anscount++;
+             else
+               authcount++;
+           }
+
+         if (!subnet)
+           for (secondary = daemon->secondary_forward_server; secondary; secondary = secondary->next)
+             if (add_resource_record(header, limit, &trunc, offset, &ansp, 
+                                     daemon->auth_ttl, NULL, T_NS, C_IN, "d", secondary->name))
+               {
+                 if (ns) 
+                   anscount++;
+                 else
+                   authcount++;
+               }
+       }
+      
+      if (axfr)
+       {
+         for (rec = daemon->mxnames; rec; rec = rec->next)
+           if (in_zone(zone, rec->name, &cut))
+             {
+               if (cut)
+                  *cut = 0;
+
+               if (rec->issrv)
+                 {
+                   if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
+                                           NULL, T_SRV, C_IN, "sssd", cut ? rec->name : NULL,
+                                           rec->priority, rec->weight, rec->srvport, rec->target))
+                     
+                     anscount++;
+                 }
+               else
+                 {
+                   if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
+                                           NULL, T_MX, C_IN, "sd", cut ? rec->name : NULL, rec->weight, rec->target))
+                     anscount++;
+                 }
+               
+               /* restore config data */
+               if (cut)
+                 *cut = '.';
+             }
+             
+         for (txt = daemon->rr; txt; txt = txt->next)
+           if (in_zone(zone, txt->name, &cut))
+             {
+               if (cut)
+                 *cut = 0;
+               
+               if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
+                                       NULL, txt->class, C_IN, "t",  cut ? txt->name : NULL, txt->len, txt->txt))
+                 anscount++;
+               
+               /* restore config data */
+               if (cut)
+                 *cut = '.';
+             }
+         
+         for (txt = daemon->txt; txt; txt = txt->next)
+           if (txt->class == C_IN && in_zone(zone, txt->name, &cut))
+             {
+               if (cut)
+                 *cut = 0;
+               
+               if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
+                                       NULL, T_TXT, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
+                 anscount++;
+               
+               /* restore config data */
+               if (cut)
+                 *cut = '.';
+             }
+         
+         for (na = daemon->naptr; na; na = na->next)
+           if (in_zone(zone, na->name, &cut))
+             {
+               if (cut)
+                 *cut = 0;
+               
+               if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, 
+                                       NULL, T_NAPTR, C_IN, "sszzzd", cut ? na->name : NULL,
+                                       na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
+                 anscount++;
+               
+               /* restore config data */
+               if (cut)
+                 *cut = '.'; 
+             }
+         
+         for (intr = daemon->int_names; intr; intr = intr->next)
+           if (in_zone(zone, intr->name, &cut))
+             {
+               struct addrlist *addrlist;
+               
+               if (cut)
+                 *cut = 0;
+               
+               for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) 
+                 if (!(addrlist->flags & ADDRLIST_IPV6) &&
+                     (local_query || filter_zone(zone, F_IPV4, &addrlist->addr)) && 
+                     add_resource_record(header, limit, &trunc, -axfroffset, &ansp, 
+                                         daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addrlist->addr))
+                   anscount++;
+               
+#ifdef HAVE_IPV6
+               for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) 
+                 if ((addrlist->flags & ADDRLIST_IPV6) && 
+                     (local_query || filter_zone(zone, F_IPV6, &addrlist->addr)) &&
+                     add_resource_record(header, limit, &trunc, -axfroffset, &ansp, 
+                                         daemon->auth_ttl, NULL, T_AAAA, C_IN, "6", cut ? intr->name : NULL, &addrlist->addr))
+                   anscount++;
+#endif             
+               
+               /* restore config data */
+               if (cut)
+                 *cut = '.'; 
+             }
+             
+         for (a = daemon->cnames; a; a = a->next)
+           if (in_zone(zone, a->alias, &cut))
+             {
+               strcpy(name, a->target);
+               if (!strchr(name, '.'))
+                 {
+                   strcat(name, ".");
+                   strcat(name, zone->domain);
+                 }
+               
+               if (cut)
+                 *cut = 0;
+               
+               if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, 
+                                       daemon->auth_ttl, NULL,
+                                       T_CNAME, C_IN, "d",  cut ? a->alias : NULL, name))
+                 anscount++;
+             }
+       
+         cache_enumerate(1);
+         while ((crecp = cache_enumerate(0)))
+           {
+             if ((crecp->flags & (F_IPV4 | F_IPV6)) &&
+                 !(crecp->flags & (F_NEG | F_NXDOMAIN)) &&
+                 (crecp->flags & F_FORWARD))
+               {
+                 if ((crecp->flags & F_DHCP) && !option_bool(OPT_DHCP_FQDN))
+                   {
+                     char *cache_name = cache_get_name(crecp);
+                     if (!strchr(cache_name, '.') && 
+                         (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))))
+                       {
+                         qtype = T_A;
+#ifdef HAVE_IPV6
+                         if (crecp->flags & F_IPV6)
+                           qtype = T_AAAA;
+#endif
+                         if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, 
+                                                 daemon->auth_ttl, NULL, qtype, C_IN, 
+                                                 (crecp->flags & F_IPV4) ? "4" : "6", cache_name, &crecp->addr))
+                           anscount++;
+                       }
+                   }
+                 
+                 if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
+                   {
+                     strcpy(name, cache_get_name(crecp));
+                     if (in_zone(zone, name, &cut) && 
+                         (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))))
+                       {
+                         qtype = T_A;
+#ifdef HAVE_IPV6
+                         if (crecp->flags & F_IPV6)
+                           qtype = T_AAAA;
+#endif
+                          if (cut)
+                            *cut = 0;
+
+                          if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, 
+                                                  daemon->auth_ttl, NULL, qtype, C_IN, 
+                                                  (crecp->flags & F_IPV4) ? "4" : "6", cut ? name : NULL, &crecp->addr))
+                            anscount++;
+                       }
+                   }
+               }
+           }
+          
+         /* repeat SOA as last record */
+         if (add_resource_record(header, limit, &trunc, axfroffset, &ansp, 
+                                 daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
+                                 daemon->authserver,  daemon->hostmaster,
+                                 daemon->soa_sn, daemon->soa_refresh, 
+                                 daemon->soa_retry, daemon->soa_expiry, 
+                                 daemon->auth_ttl))
+           anscount++;
+         
+       }
+      
+    }
+  
+  /* done all questions, set up header and return length of result */
+  /* clear authoritative and truncated flags, set QR flag */
+  header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
+
+  if (local_query)
+    {
+      /* set RA flag */
+      header->hb4 |= HB4_RA;
+    }
+  else
+    {
+      /* clear RA flag */
+      header->hb4 &= ~HB4_RA;
+    }
+
+  /* authoritive */
+  if (auth)
+    header->hb3 |= HB3_AA;
+  
+  /* truncation */
+  if (trunc)
+    header->hb3 |= HB3_TC;
+  
+  if ((auth || local_query) && nxdomain)
+    SET_RCODE(header, NXDOMAIN);
+  else
+    SET_RCODE(header, NOERROR); /* no error */
+  header->ancount = htons(anscount);
+  header->nscount = htons(authcount);
+  header->arcount = htons(0);
+  return ansp - (unsigned char *)header;
+}
+  
+#endif  
+  
+
+
diff --git a/src/blockdata.c b/src/blockdata.c
new file mode 100644 (file)
index 0000000..c8f5eae
--- /dev/null
@@ -0,0 +1,151 @@
+/* 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"
+
+#ifdef HAVE_DNSSEC
+
+static struct blockdata *keyblock_free;
+static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced;
+
+static void blockdata_expand(int n)
+{
+  struct blockdata *new = whine_malloc(n * sizeof(struct blockdata));
+  
+  if (n > 0 && new)
+    {
+      int i;
+      
+      new[n-1].next = keyblock_free;
+      keyblock_free = new;
+
+      for (i = 0; i < n - 1; i++)
+       new[i].next = &new[i+1];
+
+      blockdata_alloced += n;
+    }
+}
+
+/* Preallocate some blocks, proportional to cachesize, to reduce heap fragmentation. */
+void blockdata_init(void)
+{
+  keyblock_free = NULL;
+  blockdata_alloced = 0;
+  blockdata_count = 0;
+  blockdata_hwm = 0;
+
+  /* Note that daemon->cachesize is enforced to have non-zero size if OPT_DNSSEC_VALID is set */  
+  if (option_bool(OPT_DNSSEC_VALID))
+    blockdata_expand((daemon->cachesize * 100) / sizeof(struct blockdata));
+}
+
+void blockdata_report(void)
+{
+  if (option_bool(OPT_DNSSEC_VALID))
+    my_syslog(LOG_INFO, _("DNSSEC memory in use %u, max %u, allocated %u"), 
+             blockdata_count * sizeof(struct blockdata),  
+             blockdata_hwm * sizeof(struct blockdata),  
+             blockdata_alloced * sizeof(struct blockdata));
+} 
+
+struct blockdata *blockdata_alloc(char *data, size_t len)
+{
+  struct blockdata *block, *ret = NULL;
+  struct blockdata **prev = &ret;
+  size_t blen;
+
+  while (len > 0)
+    {
+      if (!keyblock_free)
+       blockdata_expand(50);
+      
+      if (keyblock_free)
+       {
+         block = keyblock_free;
+         keyblock_free = block->next;
+         blockdata_count++; 
+       }
+      else
+       {
+         /* failed to alloc, free partial chain */
+         blockdata_free(ret);
+         return NULL;
+       }
+       
+      if (blockdata_hwm < blockdata_count)
+       blockdata_hwm = blockdata_count; 
+      
+      blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
+      memcpy(block->key, data, blen);
+      data += blen;
+      len -= blen;
+      *prev = block;
+      prev = &block->next;
+      block->next = NULL;
+    }
+  
+  return ret;
+}
+
+void blockdata_free(struct blockdata *blocks)
+{
+  struct blockdata *tmp;
+  
+  if (blocks)
+    {
+      for (tmp = blocks; tmp->next; tmp = tmp->next)
+       blockdata_count--;
+      tmp->next = keyblock_free;
+      keyblock_free = blocks; 
+      blockdata_count--;
+    }
+}
+
+/* if data == NULL, return pointer to static block of sufficient size */
+void *blockdata_retrieve(struct blockdata *block, size_t len, void *data)
+{
+  size_t blen;
+  struct  blockdata *b;
+  void *new, *d;
+  
+  static unsigned int buff_len = 0;
+  static unsigned char *buff = NULL;
+   
+  if (!data)
+    {
+      if (len > buff_len)
+       {
+         if (!(new = whine_malloc(len)))
+           return NULL;
+         if (buff)
+           free(buff);
+         buff = new;
+       }
+      data = buff;
+    }
+  
+  for (d = data, b = block; len > 0 && b;  b = b->next)
+    {
+      blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
+      memcpy(d, b->key, blen);
+      d += blen;
+      len -= blen;
+    }
+
+  return data;
+}
+#endif
index 9a77426..a066641 100644 (file)
--- a/src/bpf.c
+++ b/src/bpf.c
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
 #include "dnsmasq.h"
 
 #if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
+#include <ifaddrs.h>
 
-static struct iovec ifconf = {
-  .iov_base = NULL,
-  .iov_len = 0
-};
-
-static struct iovec ifreq = {
-  .iov_base = NULL,
-  .iov_len = 0
-};
-
-#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
-
+#include <sys/param.h>
 #include <sys/sysctl.h>
+#include <net/if.h>
 #include <net/route.h>
 #include <net/if_dl.h>
 #include <netinet/if_ether.h>
+#if defined(__FreeBSD__)
+#  include <net/if_var.h> 
+#endif
+#include <netinet/in_var.h>
+#ifdef HAVE_IPV6
+#  include <netinet6/in6_var.h>
+#endif
+
+#ifndef SA_SIZE
+#define SA_SIZE(sa)                                             \
+    (  (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ?      \
+        sizeof(long)            :                               \
+        1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
+#endif
+
+#ifdef HAVE_BSD_NETWORK
+static int del_family = 0;
+static struct all_addr del_addr;
+#endif
+
+#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
 
 int arp_enumerate(void *parm, int (*callback)())
 {
@@ -43,8 +55,12 @@ int arp_enumerate(void *parm, int (*callback)())
   struct rt_msghdr *rtm;
   struct sockaddr_inarp *sin2;
   struct sockaddr_dl *sdl;
+  struct iovec buff;
   int rc;
-  
+
+  buff.iov_base = NULL;
+  buff.iov_len = 0;
+
   mib[0] = CTL_NET;
   mib[1] = PF_ROUTE;
   mib[2] = 0;
@@ -60,9 +76,9 @@ int arp_enumerate(void *parm, int (*callback)())
 
   while (1) 
     {
-      if (!expand_buf(&ifconf, needed))
+      if (!expand_buf(&buff, needed))
        return 0;
-      if ((rc = sysctl(mib, 6, ifconf.iov_base, &needed, NULL, 0)) == 0 ||
+      if ((rc = sysctl(mib, 6, buff.iov_base, &needed, NULL, 0)) == 0 ||
          errno != ENOMEM)
        break;
       needed += needed / 8;
@@ -70,7 +86,7 @@ int arp_enumerate(void *parm, int (*callback)())
   if (rc == -1)
     return 0;
   
-  for (next = ifconf.iov_base ; next < (char *)ifconf.iov_base + needed; next += rtm->rtm_msglen)
+  for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen)
     {
       rtm = (struct rt_msghdr *)next;
       sin2 = (struct sockaddr_inarp *)(rtm + 1);
@@ -81,19 +97,14 @@ int arp_enumerate(void *parm, int (*callback)())
 
   return 1;
 }
-
-#endif
+#endif /* defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) */
 
 
 int iface_enumerate(int family, void *parm, int (*callback)())
 {
-  char *ptr;
-  struct ifreq *ifr;
-  struct ifconf ifc;
-  int fd, errsav, ret = 0;
-  int lastlen = 0;
-  size_t len = 0;
-  
+  struct ifaddrs *head, *addrs;
+  int errsav, fd = -1, ret = 0;
+
   if (family == AF_UNSPEC)
 #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
     return  arp_enumerate(parm, callback);
@@ -101,87 +112,123 @@ int iface_enumerate(int family, void *parm, int (*callback)())
   return 0; /* need code for Solaris and MacOS*/
 #endif
 
-  if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
+  /* AF_LINK doesn't exist in Linux, so we can't use it in our API */
+  if (family == AF_LOCAL)
+    family = AF_LINK;
+
+  if (getifaddrs(&head) == -1)
     return 0;
+
+#if defined(HAVE_BSD_NETWORK) && defined(HAVE_IPV6)
+  if (family == AF_INET6)
+    fd = socket(PF_INET6, SOCK_DGRAM, 0);
+#endif
   
-  while(1)
-    {
-      len += 10*sizeof(struct ifreq);
-      
-      if (!expand_buf(&ifconf, len))
-       goto err;
-      
-      ifc.ifc_len = len;
-      ifc.ifc_buf = ifconf.iov_base;
-      
-      if (ioctl(fd, SIOCGIFCONF, &ifc) == -1)
-       {
-         if (errno != EINVAL || lastlen != 0)
-           goto err;
-       }
-      else
-       {
-         if (ifc.ifc_len == lastlen)
-           break; /* got a big enough buffer now */
-         lastlen = ifc.ifc_len;
-       }
-    }
-  
-  for (ptr = ifc.ifc_buf; ptr < (char *)(ifc.ifc_buf + ifc.ifc_len); ptr += len)
+  for (addrs = head; addrs; addrs = addrs->ifa_next)
     {
-      /* subsequent entries may not be aligned, so copy into
-        an aligned buffer to avoid nasty complaints about 
-        unaligned accesses. */
-
-      len = sizeof(struct ifreq);
-      
-#ifdef HAVE_SOCKADDR_SA_LEN
-      ifr = (struct ifreq *)ptr;
-      if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
-       len = ifr->ifr_addr.sa_len + offsetof(struct ifreq, ifr_ifru);
-#endif
-      
-      if (!expand_buf(&ifreq, len))
-       goto err;
-
-      ifr = (struct ifreq *)ifreq.iov_base;
-      memcpy(ifr, ptr, len);
-           
-      if (ifr->ifr_addr.sa_family == family)
+      if (addrs->ifa_addr->sa_family == family)
        {
+         int iface_index = if_nametoindex(addrs->ifa_name);
+
+         if (iface_index == 0 || !addrs->ifa_addr || 
+             (!addrs->ifa_netmask && family != AF_LINK))
+           continue;
+
          if (family == AF_INET)
            {
              struct in_addr addr, netmask, broadcast;
-             broadcast.s_addr = 0;
-             addr = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
-             if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
+             addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
+#ifdef HAVE_BSD_NETWORK
+             if (del_family == AF_INET && del_addr.addr.addr4.s_addr == addr.s_addr)
                continue;
-             netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
-             if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1)
-               broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr; 
-             if (!((*callback)(addr, 
-                               (int)if_nametoindex(ifr->ifr_name),
-                               netmask, broadcast, 
-                               parm)))
+#endif
+             netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
+             if (addrs->ifa_broadaddr)
+               broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr; 
+             else 
+               broadcast.s_addr = 0;         
+             if (!((*callback)(addr, iface_index, NULL, netmask, broadcast, parm)))
                goto err;
            }
 #ifdef HAVE_IPV6
          else if (family == AF_INET6)
            {
-             struct in6_addr *addr = &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr;
+             struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
+             unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
+             int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
+             int i, j, prefix = 0;
+             u32 valid = 0xffffffff, preferred = 0xffffffff;
+             int flags = 0;
+#ifdef HAVE_BSD_NETWORK
+             if (del_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&del_addr.addr.addr6, addr))
+               continue;
+#endif
+#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
+             struct in6_ifreq ifr6;
+
+             memset(&ifr6, 0, sizeof(ifr6));
+             strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
+             
+             ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
+             if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
+               {
+                 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
+                   flags |= IFACE_TENTATIVE;
+                 
+                 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
+                   flags |= IFACE_DEPRECATED;
+
+#ifdef IN6_IFF_TEMPORARY
+                 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)))
+                   flags |= IFACE_PERMANENT;
+#endif
+
+#ifdef IN6_IFF_PRIVACY
+                 if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | IN6_IFF_PRIVACY)))
+                   flags |= IFACE_PERMANENT;
+#endif
+               }
+             
+             ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
+             if (fd != -1 && ioctl(fd, SIOCGIFALIFETIME_IN6, &ifr6) != -1)
+               {
+                 valid = ifr6.ifr_ifru.ifru_lifetime.ia6t_vltime;
+                 preferred = ifr6.ifr_ifru.ifru_lifetime.ia6t_pltime;
+               }
+#endif
+                     
+             for (i = 0; i < IN6ADDRSZ; i++, prefix += 8) 
+                if (netmask[i] != 0xff)
+                 break;
+             
+             if (i != IN6ADDRSZ && netmask[i]) 
+                for (j = 7; j > 0; j--, prefix++) 
+                 if ((netmask[i] & (1 << j)) == 0)
+                   break;
+             
              /* voodoo to clear interface field in address */
              if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
                {
                  addr->s6_addr[2] = 0;
                  addr->s6_addr[3] = 0;
-               }
-             if (!((*callback)(addr,
-                               (int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
-                               (int)if_nametoindex(ifr->ifr_name),
-                               parm)))
+               } 
+            
+             if (!((*callback)(addr, prefix, scope_id, iface_index, flags,
+                               (int) preferred, (int)valid, parm)))
+               goto err;             
+           }
+#endif /* HAVE_IPV6 */
+
+#ifdef HAVE_DHCP6      
+         else if (family == AF_LINK)
+           { 
+             /* Assume ethernet again here */
+             struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
+             if (sdl->sdl_alen != 0 && 
+                 !((*callback)(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)))
                goto err;
            }
-#endif
+#endif 
        }
     }
   
@@ -189,12 +236,14 @@ int iface_enumerate(int family, void *parm, int (*callback)())
 
  err:
   errsav = errno;
-  close(fd);  
+  freeifaddrs(head); 
+  if (fd != -1)
+    close(fd);
   errno = errsav;
 
   return ret;
 }
-#endif
+#endif /* defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) */
 
 
 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
@@ -206,13 +255,10 @@ void init_bpf(void)
 
   while (1) 
     {
-      /* useful size which happens to be sufficient */
-      if (expand_buf(&ifreq, sizeof(struct ifreq)))
-       {
-         sprintf(ifreq.iov_base, "/dev/bpf%d", i++);
-         if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1)
-           return;
-       }
+      sprintf(daemon->dhcp_buff, "/dev/bpf%d", i++);
+      if ((daemon->dhcp_raw_fd = open(daemon->dhcp_buff, O_RDWR, 0)) != -1)
+       return;
+
       if (errno != EBUSY)
        die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
     }       
@@ -313,9 +359,90 @@ void send_via_bpf(struct dhcp_packet *mess, size_t len,
   iov[3].iov_base = mess;
   iov[3].iov_len = len;
 
-  while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
+  while (retry_send(writev(daemon->dhcp_raw_fd, iov, 4)));
 }
 
+#endif /* defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP) */
+
+#ifdef HAVE_BSD_NETWORK
+
+void route_init(void)
+{
+  /* AF_UNSPEC: all addr families */
+  daemon->routefd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
+  
+  if (daemon->routefd == -1 || !fix_fd(daemon->routefd))
+    die(_("cannot create PF_ROUTE socket: %s"), NULL, EC_BADNET);
+}
+
+void route_sock(void)
+{
+  struct if_msghdr *msg;
+  int rc = recv(daemon->routefd, daemon->packet, daemon->packet_buff_sz, 0);
+
+  if (rc < 4)
+    return;
+
+  msg = (struct if_msghdr *)daemon->packet;
+  
+  if (rc < msg->ifm_msglen)
+    return;
+
+   if (msg->ifm_version != RTM_VERSION)
+     {
+       static int warned = 0;
+       if (!warned)
+        {
+          my_syslog(LOG_WARNING, _("Unknown protocol version from route socket"));
+          warned = 1;
+        }
+     }
+   else if (msg->ifm_type == RTM_NEWADDR)
+     {
+       del_family = 0;
+       queue_event(EVENT_NEWADDR);
+     }
+   else if (msg->ifm_type == RTM_DELADDR)
+     {
+       /* There's a race in the kernel, such that if we run iface_enumerate() immediately
+         we get a DELADDR event, the deleted address still appears. Here we store the deleted address
+         in a static variable, and omit it from the set returned by iface_enumerate() */
+       int mask = ((struct ifa_msghdr *)msg)->ifam_addrs;
+       int maskvec[] = { RTA_DST, RTA_GATEWAY, RTA_NETMASK, RTA_GENMASK,
+                        RTA_IFP, RTA_IFA, RTA_AUTHOR, RTA_BRD };
+       int of;
+       unsigned int i;
+       
+       for (i = 0,  of = sizeof(struct ifa_msghdr); of < rc && i < sizeof(maskvec)/sizeof(maskvec[0]); i++) 
+        if (mask & maskvec[i]) 
+          {
+            struct sockaddr *sa = (struct sockaddr *)((char *)msg + of);
+            size_t diff = (sa->sa_len != 0) ? sa->sa_len : sizeof(long);
+            
+            if (maskvec[i] == RTA_IFA)
+              {
+                del_family = sa->sa_family;
+                if (del_family == AF_INET)
+                  del_addr.addr.addr4 = ((struct sockaddr_in *)sa)->sin_addr;
+#ifdef HAVE_IPV6
+                else if (del_family == AF_INET6)
+                  del_addr.addr.addr6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
 #endif
+                else
+                  del_family = 0;
+              }
+            
+            of += diff;
+            /* round up as needed */
+            if (diff & (sizeof(long) - 1)) 
+              of += sizeof(long) - (diff & (sizeof(long) - 1));
+          }
+       
+       queue_event(EVENT_NEWADDR);
+     }
+}
+
+#endif /* HAVE_BSD_NETWORK */
 
 
index 77c1972..178d654 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
@@ -24,8 +24,6 @@ static struct crec *new_chain = NULL;
 static int cache_inserted = 0, cache_live_freed = 0, insert_error;
 static union bigname *big_free = NULL;
 static int bignames_left, hash_size;
-static int uid = 0;
-static char *addrbuff = NULL;
 
 /* type->string mapping: this is also used by the name-hash function as a mixing table. */
 static const struct {
@@ -54,7 +52,11 @@ static const struct {
   { 38,  "A6" },
   { 39,  "DNAME" },
   { 41,  "OPT" },
+  { 43,  "DS" },
+  { 46,  "RRSIG" },
+  { 47,  "NSEC" },
   { 48,  "DNSKEY" },
+  { 50,  "NSEC3" },
   { 249, "TKEY" },
   { 250, "TSIG" },
   { 251, "IXFR" },
@@ -70,14 +72,24 @@ static void cache_link(struct crec *crecp);
 static void rehash(int size);
 static void cache_hash(struct crec *crecp);
 
+static unsigned int next_uid(void)
+{
+  static unsigned int uid = 0;
+
+  uid++;
+  
+  /* uid == 0 used to indicate CNAME to interface name. */
+  if (uid == SRC_INTERFACE)
+    uid++;
+  
+  return uid;
+}
+
 void cache_init(void)
 {
   struct crec *crecp;
   int i;
-
-  if (option_bool(OPT_LOG))
-    addrbuff = safe_malloc(ADDRSTRLEN);
-  
   bignames_left = daemon->cachesize/10;
   
   if (daemon->cachesize > 0)
@@ -88,7 +100,7 @@ void cache_init(void)
        {
          cache_link(crecp);
          crecp->flags = 0;
-         crecp->uid = uid++;
+         crecp->uid = next_uid();
        }
     }
   
@@ -172,13 +184,28 @@ static void cache_hash(struct crec *crecp)
   crecp->hash_next = *up;
   *up = crecp;
 }
+
+#ifdef HAVE_DNSSEC
+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);
+    }
+  else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG))
+    blockdata_free(crecp->addr.ds.keydata);
+}
+#endif
+
 static void cache_free(struct crec *crecp)
 {
   crecp->flags &= ~F_FORWARD;
   crecp->flags &= ~F_REVERSE;
-  crecp->uid = uid++; /* invalidate CNAMES pointing to this. */
-  
+  crecp->uid = next_uid(); /* invalidate CNAMES pointing to this. */
+
   if (cache_tail)
     cache_tail->next = crecp;
   else
@@ -194,6 +221,10 @@ static void cache_free(struct crec *crecp)
       big_free = crecp->name.bname;
       crecp->flags &= ~F_BIGNAME;
     }
+
+#ifdef HAVE_DNSSEC
+  cache_blockdata_free(crecp);
+#endif
 }    
 
 /* insert a new cache entry at the head of the list (youngest entry) */
@@ -232,12 +263,49 @@ char *cache_get_name(struct crec *crecp)
   return crecp->name.sname;
 }
 
+char *cache_get_cname_target(struct crec *crecp)
+{
+  if (crecp->addr.cname.uid != SRC_INTERFACE)
+    return cache_get_name(crecp->addr.cname.target.cache);
+
+  return crecp->addr.cname.target.int_name->name;
+}
+
+
+
+struct crec *cache_enumerate(int init)
+{
+  static int bucket;
+  static struct crec *cache;
+
+  if (init)
+    {
+      bucket = 0;
+      cache = NULL;
+    }
+  else if (cache && cache->hash_next)
+    cache = cache->hash_next;
+  else
+    {
+       cache = NULL; 
+       while (bucket < hash_size)
+        if ((cache = hash_table[bucket++]))
+          break;
+    }
+  
+  return cache;
+}
+
 static int is_outdated_cname_pointer(struct crec *crecp)
 {
-  if (!(crecp->flags & F_CNAME))
+  if (!(crecp->flags & F_CNAME) || crecp->addr.cname.uid == SRC_INTERFACE)
     return 0;
   
-  if (crecp->addr.cname.cache && crecp->addr.cname.uid == crecp->addr.cname.cache->uid)
+  /* NB. record may be reused as DS or DNSKEY, where uid is 
+     overloaded for something completely different */
+  if (crecp->addr.cname.target.cache && 
+      (crecp->addr.cname.target.cache->flags & (F_IPV4 | F_IPV6 | F_CNAME)) &&
+      crecp->addr.cname.uid == crecp->addr.cname.target.cache->uid)
     return 0;
   
   return 1;
@@ -254,7 +322,7 @@ static int is_expired(time_t now, struct crec *crecp)
   return 1;
 }
 
-static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
+static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
 {
   /* Scan and remove old entries.
      If (flags & F_FORWARD) then remove any forward entries for name and any expired
@@ -263,8 +331,8 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
      entries in the whole cache.
      If (flags == 0) remove any expired entries in the whole cache. 
 
-     In the flags & F_FORWARD case, the return code is valid, and returns zero if the
-     name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
+     In the flags & F_FORWARD case, the return code is valid, and returns a non-NULL pointer
+     to a cache entry if the name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
 
      We take advantage of the fact that hash chains have stuff in the order <reverse>,<other>,<immortal>
      so that when we hit an entry which isn't reverse and is immortal, we're done. */
@@ -274,27 +342,52 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
   if (flags & F_FORWARD)
     {
       for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
-       if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
-         { 
-           *up = crecp->hash_next;
-           if (!(crecp->flags & (F_HOSTS | F_DHCP)))
-             {
-               cache_unlink(crecp);
-               cache_free(crecp);
-             }
-         } 
-       else if ((crecp->flags & F_FORWARD) && 
-                ((flags & crecp->flags & (F_IPV4 | F_IPV6)) || ((crecp->flags | flags) & F_CNAME)) &&
-                hostname_isequal(cache_get_name(crecp), name))
-         {
-           if (crecp->flags & (F_HOSTS | F_DHCP))
-             return 0;
-           *up = crecp->hash_next;
-           cache_unlink(crecp);
-           cache_free(crecp);
-         }
-       else
+       {
+         if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
+           { 
+             *up = crecp->hash_next;
+             if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
+               {
+                 cache_unlink(crecp);
+                 cache_free(crecp);
+               }
+             continue;
+           } 
+       
+         if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
+           {
+             /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
+             if ((flags & crecp->flags & (F_IPV4 | F_IPV6)) || 
+                 (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
+               {
+                 if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
+                   return crecp;
+                 *up = crecp->hash_next;
+                 cache_unlink(crecp);
+                 cache_free(crecp);
+                 continue;
+               }
+             
+#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))
+               {
+                 if (crecp->flags & F_CONFIG)
+                   return crecp;
+                 *up = crecp->hash_next;
+                 cache_unlink(crecp);
+                 cache_free(crecp);
+                 continue;
+               }
+#endif
+           }
          up = &crecp->hash_next;
+       }
     }
   else
     {
@@ -311,13 +404,13 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
          if (is_expired(now, crecp))
            {
              *up = crecp->hash_next;
-             if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+             if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
                { 
                  cache_unlink(crecp);
                  cache_free(crecp);
                }
            }
-         else if (!(crecp->flags & (F_HOSTS | F_DHCP)) &&
+         else if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) &&
                   (flags & crecp->flags & F_REVERSE) && 
                   (flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
                   memcmp(&crecp->addr.addr, addr, addrlen) == 0)
@@ -330,7 +423,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
            up = &crecp->hash_next;
     }
   
-  return 1;
+  return NULL;
 }
 
 /* Note: The normal calling sequence is
@@ -364,17 +457,42 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
   int freed_all = flags & F_REVERSE;
   int free_avail = 0;
 
-  log_query(flags | F_UPSTREAM, name, addr, NULL);
+  /* Don't log DNSSEC records here, done elsewhere */
+  if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
+    {
+      log_query(flags | F_UPSTREAM, name, addr, NULL);
+      /* Don't mess with TTL for DNSSEC records. */
+      if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
+       ttl = daemon->max_cache_ttl;
+      if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl)
+       ttl = daemon->min_cache_ttl;
+    }
 
   /* if previous insertion failed give up now. */
   if (insert_error)
     return NULL;
-
+  
   /* First remove any expired entries and entries for the name/address we
-     are currently inserting. Fail is we attempt to delete a name from
-     /etc/hosts or DHCP. */
-  if (!cache_scan_free(name, addr, now, flags))
+     are currently inserting. */
+  if ((new = cache_scan_free(name, addr, now, flags)))
     {
+      /* We're trying to insert a record over one from 
+        /etc/hosts or DHCP, or other config. If the 
+        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) && (new->flags & F_IPV4) &&
+             new->addr.addr.addr.addr4.s_addr == addr->addr.addr4.s_addr)
+           return new;
+#ifdef HAVE_IPV6
+         else if ((flags & F_IPV6) && (new->flags & F_IPV6) &&
+                  IN6_ARE_ADDR_EQUAL(&new->addr.addr.addr.addr6, &addr->addr.addr6))
+           return new;
+#endif
+       }
+      
       insert_error = 1;
       return NULL;
     }
@@ -399,14 +517,32 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
           insert. Once in this state, all inserts will probably fail. */
        if (free_avail)
          {
+           static int warned = 0;
+           if (!warned)
+             {
+               my_syslog(LOG_ERR, _("Internal error in cache."));
+               warned = 1;
+             }
            insert_error = 1;
            return NULL;
          }
                
        if (freed_all)
          {
+           struct all_addr free_addr = new->addr.addr;;
+
+#ifdef HAVE_DNSSEC
+           /* For DNSSEC records, addr holds class and type_covered for RRSIG */
+           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;
+             }
+#endif
+           
            free_avail = 1; /* Must be free space now. */
-           cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags);
+           cache_scan_free(cache_get_name(new), &free_addr, now, new->flags);
            cache_live_freed++;
          }
        else
@@ -418,7 +554,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
       }
  
     /* Check if we need to and can allocate extra memory for a long name.
-       If that fails, give up now. */
+       If that fails, give up now, always succeed for DNSSEC records. */
     if (name && (strlen(name) > SMALLDNAME-1))
       {
        if (big_free)
@@ -426,13 +562,13 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
            big_name = big_free;
            big_free = big_free->next;
          }
-       else if (!bignames_left ||
+       else if ((bignames_left == 0 && !(flags & (F_DS | F_DNSKEY))) ||
                 !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
          {
            insert_error = 1;
            return NULL;
          }
-       else
+       else if (bignames_left != 0)
          bignames_left--;
        
       }
@@ -455,10 +591,15 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
     *cache_get_name(new) = 0;
 
   if (addr)
-    new->addr.addr = *addr;
-  else
-    new->addr.cname.cache = NULL;
-  
+    {
+#ifdef HAVE_DNSSEC
+      if (flags & (F_DS | F_DNSKEY))
+       new->uid = addr->addr.dnssec.class;
+      else
+#endif
+       new->addr.addr = *addr; 
+    }
+
   new->ttd = now + (time_t)ttl;
   new->next = new_chain;
   new_chain = new;
@@ -489,10 +630,13 @@ void cache_end_insert(void)
   new_chain = NULL;
 }
 
-struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned short prot)
+struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned int prot)
 {
   struct crec *ans;
+  int no_rr = prot & F_NO_RR;
 
+  prot &= ~F_NO_RR;
+  
   if (crecp) /* iterating */
     ans = crecp->next;
   else
@@ -509,10 +653,13 @@ 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))
                {
-                 if (crecp->flags & (F_HOSTS | F_DHCP))
+                 if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
                    {
                      *chainp = crecp;
                      chainp = &crecp->next;
@@ -537,7 +684,7 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
                    }
                  else
                    {
-                     if (!insert)
+                     if (!insert && !no_rr)
                        {
                          insert = up;
                          ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL);
@@ -553,7 +700,7 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
            {
              /* expired entry, free it */
              *up = crecp->hash_next;
-             if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+             if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
                { 
                  cache_unlink(crecp);
                  cache_free(crecp);
@@ -566,7 +713,10 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
 
   if (ans && 
       (ans->flags & F_FORWARD) &&
-      (ans->flags & prot) &&
+#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;
   
@@ -574,7 +724,7 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
 }
 
 struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr, 
-                               time_t now, unsigned short prot)
+                               time_t now, unsigned int prot)
 {
   struct crec *ans;
 #ifdef HAVE_IPV6
@@ -603,7 +753,7 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
               if ((crecp->flags & prot) &&
                   memcmp(&crecp->addr.addr, addr, addrlen) == 0)
                 {          
-                  if (crecp->flags & (F_HOSTS | F_DHCP))
+                  if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
                     {
                       *chainp = crecp;
                       chainp = &crecp->next;
@@ -619,7 +769,7 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
           else
             {
               *up = crecp->hash_next;
-              if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+              if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
                 {
                   cache_unlink(crecp);
                   cache_free(crecp);
@@ -638,12 +788,31 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
   return NULL;
 }
 
+static void add_hosts_cname(struct crec *target)
+{
+  struct crec *crec;
+  struct cname *a;
+  
+  for (a = daemon->cnames; a; a = a->next)
+    if (hostname_isequal(cache_get_name(target), a->target) &&
+       (crec = whine_malloc(sizeof(struct crec))))
+      {
+       crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME;
+       crec->name.namep = a->alias;
+       crec->addr.cname.target.cache = target;
+       crec->addr.cname.uid = target->uid;
+       crec->uid = next_uid();
+       cache_hash(crec);
+       add_hosts_cname(crec); /* handle chains */
+      }
+}
+  
 static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen, 
-                           unsigned short flags, int index, int addr_dup)
+                           unsigned int index, struct crec **rhash, int hashsz)
 {
-  struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
+  struct crec *lookup = cache_find_by_name(NULL, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6));
   int i, nameexists = 0;
-  struct cname *a;
+  unsigned int j; 
 
   /* Remove duplicates in hosts files. */
   if (lookup && (lookup->flags & F_HOSTS))
@@ -657,47 +826,58 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
     }
   
   /* Ensure there is only one address -> name mapping (first one trumps) 
-     We do this by steam here, first we see if the address is the same as
-     the last one we saw, which eliminates most in the case of an ad-block 
-     file with thousands of entries for the same address.
-     Then we search and bail at the first matching address that came from
-     a HOSTS file. Since the first host entry gets reverse, we know 
-     then that it must exist without searching exhaustively for it. */
+     We do this by steam here, The entries are kept in hash chains, linked
+     by ->next (which is unused at this point) held in hash buckets in
+     the array rhash, hashed on address. Note that rhash and the values
+     in ->next are only valid  whilst reading hosts files: the buckets are
+     then freed, and the ->next pointer used for other things. 
+
+     Only insert each unique address once into this hashing structure.
+
+     This complexity avoids O(n^2) divergent CPU use whilst reading
+     large (10000 entry) hosts files. 
+
+     Note that we only do this process when bulk-reading hosts files, 
+     for incremental reads, rhash is NULL, and we use cache lookups
+     instead.
+  */
   
-  if (addr_dup)
-    flags &= ~F_REVERSE;
+  if (rhash)
+    {
+      /* hash address */
+      for (j = 0, i = 0; i < addrlen; i++)
+       j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
+      
+      for (lookup = rhash[j]; lookup; lookup = lookup->next)
+       if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
+           memcmp(&lookup->addr.addr, addr, addrlen) == 0)
+         {
+           cache->flags &= ~F_REVERSE;
+           break;
+         }
+      
+      /* maintain address hash chain, insert new unique address */
+      if (!lookup)
+       {
+         cache->next = rhash[j];
+         rhash[j] = cache;
+       }
+    }
   else
-    for (i=0; i<hash_size; i++)
-      {
-       for (lookup = hash_table[i]; lookup; lookup = lookup->hash_next)
-         if ((lookup->flags & F_HOSTS) && 
-             (lookup->flags & flags & (F_IPV4 | F_IPV6)) &&
-             memcmp(&lookup->addr.addr, addr, addrlen) == 0)
-           {
-             flags &= ~F_REVERSE;
-             break;
-           }
-       if (lookup)
-         break;
-      }
-  
-  cache->flags = flags;
+    {
+      /* incremental read, lookup in cache */
+      lookup = cache_find_by_addr(NULL, addr, 0, cache->flags & (F_IPV4 | F_IPV6));
+      if (lookup && lookup->flags & F_HOSTS)
+       cache->flags &= ~F_REVERSE;
+    }
+
   cache->uid = index;
-  memcpy(&cache->addr.addr, addr, addrlen);
+  memcpy(&cache->addr.addr, addr, addrlen);  
   cache_hash(cache);
   
   /* don't need to do alias stuff for second and subsequent addresses. */
   if (!nameexists)
-    for (a = daemon->cnames; a; a = a->next)
-      if (hostname_isequal(cache->name.sname, a->target) &&
-         (lookup = whine_malloc(sizeof(struct crec))))
-       {
-         lookup->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_HOSTS | F_CNAME;
-         lookup->name.namep = a->alias;
-         lookup->addr.cname.cache = cache;
-         lookup->addr.cname.uid = index;
-         cache_hash(lookup);
-       }
+    add_hosts_cname(cache);
 }
 
 static int eatspace(FILE *f)
@@ -747,14 +927,14 @@ static int gettok(FILE *f, char *token)
     }
 }
 
-static int read_hostsfile(char *filename, int index, int cache_size)
+int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz)
 {  
   FILE *f = fopen(filename, "r");
   char *token = daemon->namebuff, *domain_suffix = NULL;
   int addr_count = 0, name_count = cache_size, lineno = 0;
-  unsigned short flags = 0, saved_flags = 0;
-  struct all_addr addr, saved_addr;
-  int atnl, addrlen = 0, addr_dup;
+  unsigned short flags = 0;
+  struct all_addr addr;
+  int atnl, addrlen = 0;
 
   if (!f)
     {
@@ -766,28 +946,20 @@ static int read_hostsfile(char *filename, int index, int cache_size)
   
   while ((atnl = gettok(f, token)) != EOF)
     {
-      addr_dup = 0;
       lineno++;
       
-#ifdef HAVE_IPV6      
       if (inet_pton(AF_INET, token, &addr) > 0)
        {
          flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
          addrlen = INADDRSZ;
          domain_suffix = get_domain(addr.addr.addr4);
        }
+#ifdef HAVE_IPV6
       else if (inet_pton(AF_INET6, token, &addr) > 0)
        {
          flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
          addrlen = IN6ADDRSZ;
-         domain_suffix = daemon->domain_suffix;
-       }
-#else 
-      if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1)
-       {
-         flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
-         addrlen = INADDRSZ;
-         domain_suffix = get_domain(addr.addr.addr4);
+         domain_suffix = get_domain6(&addr.addr.addr6);
        }
 #endif
       else
@@ -798,18 +970,10 @@ static int read_hostsfile(char *filename, int index, int cache_size)
          continue;
        }
       
-      if (saved_flags == flags && memcmp(&addr, &saved_addr, addrlen) == 0)
-       addr_dup = 1;
-      else
-       {
-         saved_flags = flags;
-         saved_addr = addr;
-       }
-      
       addr_count++;
       
       /* rehash every 1000 names. */
-      if ((name_count - cache_size) > 1000)
+      if (rhash && ((name_count - cache_size) > 1000))
        {
          rehash(name_count);
          cache_size = name_count;
@@ -836,14 +1000,15 @@ static int read_hostsfile(char *filename, int index, int cache_size)
                  strcpy(cache->name.sname, canon);
                  strcat(cache->name.sname, ".");
                  strcat(cache->name.sname, domain_suffix);
-                 add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
-                 addr_dup = 1;
+                 cache->flags = flags;
+                 add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
                  name_count++;
                }
              if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME)))
                {
                  strcpy(cache->name.sname, canon);
-                 add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
+                 cache->flags = flags;
+                 add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
                  name_count++;
                }
              free(canon);
@@ -855,7 +1020,9 @@ static int read_hostsfile(char *filename, int index, int cache_size)
     } 
 
   fclose(f);
-  rehash(name_count);
+  
+  if (rhash)
+    rehash(name_count); 
   
   my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
   
@@ -865,16 +1032,26 @@ static int read_hostsfile(char *filename, int index, int cache_size)
 void cache_reload(void)
 {
   struct crec *cache, **up, *tmp;
-  int i, total_size = daemon->cachesize;
+  int revhashsz, i, total_size = daemon->cachesize;
   struct hostsfile *ah;
+  struct host_record *hr;
+  struct name_list *nl;
+  struct cname *a;
+  struct interface_name *intr;
+#ifdef HAVE_DNSSEC
+  struct ds_config *ds;
+#endif
 
   cache_inserted = cache_live_freed = 0;
   
   for (i=0; i<hash_size; i++)
     for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
       {
+#ifdef HAVE_DNSSEC
+       cache_blockdata_free(cache);
+#endif
        tmp = cache->hash_next;
-       if (cache->flags & F_HOSTS)
+       if (cache->flags & (F_HOSTS | F_CONFIG))
          {
            *up = cache->hash_next;
            free(cache);
@@ -893,35 +1070,103 @@ void cache_reload(void)
          up = &cache->hash_next;
       }
   
+  /* Add CNAMEs to interface_names to the cache */
+  for (a = daemon->cnames; a; a = a->next)
+    for (intr = daemon->int_names; intr; intr = intr->next)
+      if (hostname_isequal(a->target, intr->name) &&
+         ((cache = whine_malloc(sizeof(struct crec)))))
+       {
+         cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;
+         cache->name.namep = a->alias;
+         cache->addr.cname.target.int_name = intr;
+         cache->addr.cname.uid = SRC_INTERFACE;
+         cache->uid = next_uid();
+         cache_hash(cache);
+         add_hosts_cname(cache); /* handle chains */
+       }
+
+#ifdef HAVE_DNSSEC
+  for (ds = daemon->ds; ds; ds = ds->next)
+    if ((cache = whine_malloc(sizeof(struct crec))) &&
+       (cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
+      {
+       cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
+       cache->name.namep = ds->name;
+       cache->addr.ds.keylen = ds->digestlen;
+       cache->addr.ds.algo = ds->algo;
+       cache->addr.ds.keytag = ds->keytag;
+       cache->addr.ds.digest = ds->digest_type;
+       cache->uid = ds->class;
+       cache_hash(cache);
+      }
+#endif
+  
+  /* borrow the packet buffer for a temporary by-address hash */
+  memset(daemon->packet, 0, daemon->packet_buff_sz);
+  revhashsz = daemon->packet_buff_sz / sizeof(struct crec *);
+  /* we overwrote the buffer... */
+  daemon->srv_save = NULL;
+
+  /* Do host_records in config. */
+  for (hr = daemon->host_records; hr; hr = hr->next)
+    for (nl = hr->names; nl; nl = nl->next)
+      {
+       if (hr->addr.s_addr != 0 &&
+           (cache = whine_malloc(sizeof(struct crec))))
+         {
+           cache->name.namep = nl->name;
+           cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
+           add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
+         }
+#ifdef HAVE_IPV6
+       if (!IN6_IS_ADDR_UNSPECIFIED(&hr->addr6) &&
+           (cache = whine_malloc(sizeof(struct crec))))
+         {
+           cache->name.namep = nl->name;
+           cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
+           add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
+         }
+#endif
+      }
+       
   if (option_bool(OPT_NO_HOSTS) && !daemon->addn_hosts)
     {
       if (daemon->cachesize > 0)
        my_syslog(LOG_INFO, _("cleared cache"));
-      return;
+    }
+  else
+    {
+      if (!option_bool(OPT_NO_HOSTS))
+       total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
+      
+      daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
+      for (ah = daemon->addn_hosts; ah; ah = ah->next)
+       if (!(ah->flags & AH_INACTIVE))
+         total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
     }
 
-  if (!option_bool(OPT_NO_HOSTS))
-    total_size = read_hostsfile(HOSTSFILE, 0, total_size);
-          
-  daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
-  for (ah = daemon->addn_hosts; ah; ah = ah->next)
-    if (!(ah->flags & AH_INACTIVE))
-      total_size = read_hostsfile(ah->fname, ah->index, total_size);
+#ifdef HAVE_INOTIFY
+  set_dynamic_inotify(AH_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
+#endif
+  
 } 
 
-char *get_domain(struct in_addr addr)
+#ifdef HAVE_DHCP
+struct in_addr a_record_from_hosts(char *name, time_t now)
 {
-  struct cond_domain *c;
-
-  for (c = daemon->cond_domain; c; c = c->next)
-    if (ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
-        ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
-      return c->domain;
+  struct crec *crecp = NULL;
+  struct in_addr ret;
+  
+  while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
+    if (crecp->flags & F_HOSTS)
+      return *(struct in_addr *)&crecp->addr;
 
-  return daemon->domain_suffix;
+  my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);
+  
+  ret.s_addr = 0;
+  return ret;
 }
 
-#ifdef HAVE_DHCP
 void cache_unhash_dhcp(void)
 {
   struct crec *cache, **up;
@@ -939,111 +1184,251 @@ void cache_unhash_dhcp(void)
        up = &cache->hash_next;
 }
 
-void cache_add_dhcp_entry(char *host_name, 
-                         struct in_addr *host_address, time_t ttd) 
+static void add_dhcp_cname(struct crec *target, time_t ttd)
 {
-  struct crec *crec = NULL, *aliasc;
-  unsigned short flags =  F_NAMEP | F_DHCP | F_FORWARD | F_IPV4 | F_REVERSE;
-  int in_hosts = 0;
+  struct crec *aliasc;
   struct cname *a;
   
-  while ((crec = cache_find_by_name(crec, host_name, 0, F_IPV4 | F_CNAME)))
+  for (a = daemon->cnames; a; a = a->next)
+    if (hostname_isequal(cache_get_name(target), a->target))
+      {
+       if ((aliasc = dhcp_spare))
+         dhcp_spare = dhcp_spare->next;
+       else /* need new one */
+         aliasc = whine_malloc(sizeof(struct crec));
+       
+       if (aliasc)
+         {
+           aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME | F_CONFIG;
+           if (ttd == 0)
+             aliasc->flags |= F_IMMORTAL;
+           else
+             aliasc->ttd = ttd;
+           aliasc->name.namep = a->alias;
+           aliasc->addr.cname.target.cache = target;
+           aliasc->addr.cname.uid = target->uid;
+           aliasc->uid = next_uid();
+           cache_hash(aliasc);
+           add_dhcp_cname(aliasc, ttd);
+         }
+      }
+}
+
+void cache_add_dhcp_entry(char *host_name, int prot,
+                         struct all_addr *host_address, time_t ttd) 
+{
+  struct crec *crec = NULL, *fail_crec = NULL;
+  unsigned short flags = F_IPV4;
+  int in_hosts = 0;
+  size_t addrlen = sizeof(struct in_addr);
+
+#ifdef HAVE_IPV6
+  if (prot == AF_INET6)
+    {
+      flags = F_IPV6;
+      addrlen = sizeof(struct in6_addr);
+    }
+#endif
+  
+  inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);
+  
+  while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME)))
     {
       /* check all addresses associated with name */
-      if (crec->flags & F_HOSTS)
+      if (crec->flags & (F_HOSTS | F_CONFIG))
        {
-         /* if in hosts, don't need DHCP record */
-         in_hosts = 1;
-         
          if (crec->flags & F_CNAME)
            my_syslog(MS_DHCP | LOG_WARNING, 
                      _("%s is a CNAME, not giving it to the DHCP lease of %s"),
-                     host_name, inet_ntoa(*host_address));
-         else if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr)
-           {
-             strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4));
-             my_syslog(MS_DHCP | LOG_WARNING, 
-                       _("not giving name %s to the DHCP lease of %s because "
-                         "the name exists in %s with address %s"), 
-                       host_name, inet_ntoa(*host_address),
-                       record_source(crec->uid), daemon->namebuff);
-           }     
+                     host_name, daemon->addrbuff);
+         else if (memcmp(&crec->addr.addr, host_address, addrlen) == 0)
+           in_hosts = 1;
+         else
+           fail_crec = crec;
        }
       else if (!(crec->flags & F_DHCP))
        {
-         cache_scan_free(host_name, NULL, 0, crec->flags & (F_IPV4 | F_CNAME | F_FORWARD));
+         cache_scan_free(host_name, NULL, 0, crec->flags & (flags | F_CNAME | F_FORWARD));
          /* scan_free deletes all addresses associated with name */
          break;
        }
     }
   
-   if (in_hosts)
+  /* if in hosts, don't need DHCP record */
+  if (in_hosts)
     return;
-
-   if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, F_IPV4)))
-     {
-       if (crec->flags & F_NEG)
-        cache_scan_free(NULL, (struct all_addr *)host_address, 0, F_IPV4 | F_REVERSE);
-       else
-        /* avoid multiple reverse mappings */
-        flags &= ~F_REVERSE;
-     }
-   
-   if ((crec = dhcp_spare))
+  
+  /* Name in hosts, address doesn't match */
+  if (fail_crec)
+    {
+      inet_ntop(prot, &fail_crec->addr.addr, daemon->namebuff, MAXDNAME);
+      my_syslog(MS_DHCP | LOG_WARNING, 
+               _("not giving name %s to the DHCP lease of %s because "
+                 "the name exists in %s with address %s"), 
+               host_name, daemon->addrbuff,
+               record_source(fail_crec->uid), daemon->namebuff);
+      return;
+    }    
+  
+  if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, flags)))
+    {
+      if (crec->flags & F_NEG)
+       {
+         flags |= F_REVERSE;
+         cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags);
+       }
+    }
+  else
+    flags |= F_REVERSE;
+  
+  if ((crec = dhcp_spare))
     dhcp_spare = dhcp_spare->next;
   else /* need new one */
     crec = whine_malloc(sizeof(struct crec));
   
   if (crec) /* malloc may fail */
     {
-      crec->flags = flags;
+      crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD;
       if (ttd == 0)
        crec->flags |= F_IMMORTAL;
       else
        crec->ttd = ttd;
-      crec->addr.addr.addr.addr4 = *host_address;
+      crec->addr.addr = *host_address;
       crec->name.namep = host_name;
-      crec->uid = uid++;
+      crec->uid = next_uid();
       cache_hash(crec);
 
-      for (a = daemon->cnames; a; a = a->next)
-       if (hostname_isequal(host_name, a->target))
+      add_dhcp_cname(crec, ttd);
+    }
+}
+#endif
+
+int cache_make_stat(struct txt_record *t)
+{ 
+  static char *buff = NULL;
+  static int bufflen = 60;
+  int len;
+  struct server *serv, *serv1;
+  char *p;
+
+  if (!buff && !(buff = whine_malloc(60)))
+    return 0;
+
+  p = buff;
+  
+  switch (t->stat)
+    {
+    case TXT_STAT_CACHESIZE:
+      sprintf(buff+1, "%d", daemon->cachesize);
+      break;
+
+    case TXT_STAT_INSERTS:
+      sprintf(buff+1, "%d", cache_inserted);
+      break;
+
+    case TXT_STAT_EVICTIONS:
+      sprintf(buff+1, "%d", cache_live_freed);
+      break;
+
+    case TXT_STAT_MISSES:
+      sprintf(buff+1, "%u", daemon->queries_forwarded);
+      break;
+
+    case TXT_STAT_HITS:
+      sprintf(buff+1, "%u", daemon->local_answer);
+      break;
+
+#ifdef HAVE_AUTH
+    case TXT_STAT_AUTH:
+      sprintf(buff+1, "%u", daemon->auth_answer);
+      break;
+#endif
+
+    case TXT_STAT_SERVERS:
+      /* sum counts from different records for same server */
+      for (serv = daemon->servers; serv; serv = serv->next)
+       serv->flags &= ~SERV_COUNTED;
+      
+      for (serv = daemon->servers; serv; serv = serv->next)
+       if (!(serv->flags & 
+             (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
          {
-           if ((aliasc = dhcp_spare))
-             dhcp_spare = dhcp_spare->next;
-           else /* need new one */
-             aliasc = whine_malloc(sizeof(struct crec));
-           
-           if (aliasc)
+           char *new, *lenp;
+           int port, newlen, bytes_avail, bytes_needed;
+           unsigned int queries = 0, failed_queries = 0;
+           for (serv1 = serv; serv1; serv1 = serv1->next)
+             if (!(serv1->flags & 
+                   (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) && 
+                 sockaddr_isequal(&serv->addr, &serv1->addr))
+               {
+                 serv1->flags |= SERV_COUNTED;
+                 queries += serv1->queries;
+                 failed_queries += serv1->failed_queries;
+               }
+           port = prettyprint_addr(&serv->addr, daemon->addrbuff);
+           lenp = p++; /* length */
+           bytes_avail = bufflen - (p - buff );
+           bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
+           if (bytes_needed >= bytes_avail)
              {
-               aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME;
-               if (ttd == 0)
-                 aliasc->flags |= F_IMMORTAL;
-               else
-                 aliasc->ttd = ttd;
-               aliasc->name.namep = a->alias;
-               aliasc->addr.cname.cache = crec;
-               aliasc->addr.cname.uid = crec->uid;
-               cache_hash(aliasc);
+               /* expand buffer if necessary */
+               newlen = bytes_needed + 1 + bufflen - bytes_avail;
+               if (!(new = whine_malloc(newlen)))
+                 return 0;
+               memcpy(new, buff, bufflen);
+               free(buff);
+               p = new + (p - buff);
+               lenp = p - 1;
+               buff = new;
+               bufflen = newlen;
+               bytes_avail =  bufflen - (p - buff );
+               bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
              }
+           *lenp = bytes_needed;
+           p += bytes_needed;
          }
+      t->txt = (unsigned char *)buff;
+      t->len = p - buff;
+      return 1;
     }
+  
+  len = strlen(buff+1);
+  t->txt = (unsigned char *)buff;
+  t->len = len + 1;
+  *buff = len;
+  return 1;
+}
+
+/* There can be names in the cache containing control chars, don't 
+   mess up logging or open security holes. */
+static char *sanitise(char *name)
+{
+  unsigned char *r;
+  if (name)
+    for (r = (unsigned char *)name; *r; r++)
+      if (!isprint((int)*r))
+       return "<name unprintable>";
+
+  return name;
 }
-#endif
 
 
 void dump_cache(time_t now)
 {
   struct server *serv, *serv1;
+  char *t = "";
 
   my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now);
   my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."), 
            daemon->cachesize, cache_live_freed, cache_inserted);
   my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"), 
            daemon->queries_forwarded, daemon->local_answer);
-
-  if (!addrbuff && !(addrbuff = whine_malloc(ADDRSTRLEN)))
-    return;
+#ifdef HAVE_AUTH
+  my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->auth_answer);
+#endif
+#ifdef HAVE_DNSSEC
+  blockdata_report();
+#endif
 
   /* sum counts from different records for same server */
   for (serv = daemon->servers; serv; serv = serv->next)
@@ -1064,53 +1449,75 @@ void dump_cache(time_t now)
              queries += serv1->queries;
              failed_queries += serv1->failed_queries;
            }
-       port = prettyprint_addr(&serv->addr, addrbuff);
-       my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), addrbuff, port, queries, failed_queries);
+       port = prettyprint_addr(&serv->addr, daemon->addrbuff);
+       my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);
       }
   
   if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
     {
       struct crec *cache ;
       int i;
-      my_syslog(LOG_INFO, "Host                                     Address                        Flags     Expires");
+      my_syslog(LOG_INFO, "Host                                     Address                        Flags      Expires");
     
       for (i=0; i<hash_size; i++)
        for (cache = hash_table[i]; cache; cache = cache->hash_next)
          {
-           char *a, *p = daemon->namebuff;
-           p += sprintf(p, "%-40.40s ", cache_get_name(cache));
-           if ((cache->flags & F_NEG) && (cache->flags & F_FORWARD))
-             a = ""; 
-           else if (cache->flags & F_CNAME) 
+           char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache);
+           *a = 0;
+           if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
+             n = "<Root>";
+           p += sprintf(p, "%-30.30s ", sanitise(n));
+           if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
+             a = sanitise(cache_get_cname_target(cache));
+#ifdef HAVE_DNSSEC
+           else if (cache->flags & F_DS)
              {
-               a = "";
-               if (!is_outdated_cname_pointer(cache))
-                 a = cache_get_name(cache->addr.cname.cache);
+               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))
+                 sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
+                         cache->addr.ds.algo, cache->addr.ds.digest);
              }
-#ifdef HAVE_IPV6
-           else 
+           else if (cache->flags & F_DNSKEY)
+             sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
+                     cache->addr.key.algo, cache->addr.key.flags);
+#endif
+           else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
              { 
-               a = addrbuff;
+               a = daemon->addrbuff;
                if (cache->flags & F_IPV4)
-                 inet_ntop(AF_INET, &cache->addr.addr, addrbuff, ADDRSTRLEN);
+                 inet_ntop(AF_INET, &cache->addr.addr, a, ADDRSTRLEN);
+#ifdef HAVE_IPV6
                else if (cache->flags & F_IPV6)
-                 inet_ntop(AF_INET6, &cache->addr.addr, addrbuff, ADDRSTRLEN);
+                 inet_ntop(AF_INET6, &cache->addr.addr, a, ADDRSTRLEN);
+#endif
              }
-#else
-            else 
-             a = inet_ntoa(cache->addr.addr.addr.addr4);
+
+           if (cache->flags & F_IPV4)
+             t = "4";
+           else if (cache->flags & F_IPV6)
+             t = "6";
+           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)
+             t = "K";
 #endif
-           p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s  ", a, 
-                        cache->flags & F_IPV4 ? "4" : "",
-                        cache->flags & F_IPV6 ? "6" : "",
-                        cache->flags & F_CNAME ? "C" : "",
+           p += sprintf(p, "%-40.40s %s%s%s%s%s%s%s%s%s  ", a, t,
                         cache->flags & F_FORWARD ? "F" : " ",
                         cache->flags & F_REVERSE ? "R" : " ",
                         cache->flags & F_IMMORTAL ? "I" : " ",
                         cache->flags & F_DHCP ? "D" : " ",
                         cache->flags & F_NEG ? "N" : " ",
                         cache->flags & F_NXDOMAIN ? "X" : " ",
-                        cache->flags & F_HOSTS ? "H" : " ");
+                        cache->flags & F_HOSTS ? "H" : " ",
+                        cache->flags & F_DNSSECOK ? "V" : " ");
 #ifdef HAVE_BROKEN_RTC
            p += sprintf(p, "%lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
 #else
@@ -1123,65 +1530,106 @@ void dump_cache(time_t now)
     }
 }
 
-char *record_source(int index)
+char *record_source(unsigned int index)
 {
   struct hostsfile *ah;
 
-  if (index == 0)
+  if (index == SRC_CONFIG)
+    return "config";
+  else if (index == SRC_HOSTS)
     return HOSTSFILE;
 
   for (ah = daemon->addn_hosts; ah; ah = ah->next)
     if (ah->index == index)
       return ah->fname;
-  
+
+#ifdef HAVE_INOTIFY
+  for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
+     if (ah->index == index)
+       return ah->fname;
+#endif
+
   return "<unknown>";
 }
 
-void querystr(char *str, unsigned short type)
+char *querystr(char *desc, unsigned short type)
 {
   unsigned int i;
-  
-  sprintf(str, "query[type=%d]", type); 
+  int len = 10; /* strlen("type=xxxxx") */
+  const char *types = NULL;
+  static char *buff = NULL;
+  static int bufflen = 0;
+
   for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
     if (typestr[i].type == type)
-      sprintf(str,"query[%s]", typestr[i].name);
+      {
+       types = typestr[i].name;
+       len = strlen(types);
+       break;
+      }
+
+  len += 3; /* braces, terminator */
+  len += strlen(desc);
+
+  if (!buff || bufflen < len)
+    {
+      if (buff)
+       free(buff);
+      else if (len < 20)
+       len = 20;
+      
+      buff = whine_malloc(len);
+      bufflen = len;
+    }
+
+  if (buff)
+    {
+      if (types)
+       sprintf(buff, "%s[%s]", desc, types);
+      else
+       sprintf(buff, "%s[type=%d]", desc, type);
+    }
+
+  return buff ? buff : "";
 }
 
 void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
 {
-  char *source, *dest = addrbuff;
+  char *source, *dest = daemon->addrbuff;
   char *verb = "is";
   
   if (!option_bool(OPT_LOG))
     return;
 
+  name = sanitise(name);
+
   if (addr)
     {
+      if (flags & F_KEYTAG)
+       sprintf(daemon->addrbuff, arg, addr->addr.keytag);
+      else
+       {
 #ifdef HAVE_IPV6
-      inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
-               addr, addrbuff, ADDRSTRLEN);
+         inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
+                   addr, daemon->addrbuff, ADDRSTRLEN);
 #else
-      strncpy(addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);  
+         strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);  
 #endif
+       }
     }
+  else
+    dest = arg;
 
   if (flags & F_REVERSE)
     {
       dest = name;
-      name = addrbuff;
+      name = daemon->addrbuff;
     }
   
   if (flags & F_NEG)
     {
       if (flags & F_NXDOMAIN)
-       {
-         if (flags & F_IPV4)
-           dest = "NXDOMAIN-IPv4";
-         else if (flags & F_IPV6)
-           dest = "NXDOMAIN-IPv6";
-         else
-           dest = "NXDOMAIN";
-       }
+       dest = "NXDOMAIN";
       else
        {      
          if (flags & F_IPV4)
@@ -1205,6 +1653,10 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
     source = arg;
   else if (flags & F_UPSTREAM)
     source = "reply";
+  else if (flags & F_SECSTAT)
+    source = "validation";
+  else if (flags & F_AUTH)
+    source = "auth";
   else if (flags & F_SERVER)
     {
       source = "forwarded";
@@ -1215,12 +1667,34 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
       source = arg;
       verb = "from";
     }
+  else if (flags & F_DNSSEC)
+    {
+      source = arg;
+      verb = "to";
+    }
+  else if (flags & F_IPSET)
+    {
+      source = "ipset add";
+      dest = name;
+      name = arg;
+      verb = daemon->addrbuff;
+    }
   else
     source = "cached";
   
   if (strlen(name) == 0)
     name = ".";
 
-  my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
+  if (option_bool(OPT_EXTRALOG))
+    {
+      int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
+      if (flags & F_NOEXTRA)
+       my_syslog(LOG_INFO, "* %s/%u %s %s %s %s", daemon->addrbuff2, port, source, name, verb, dest);
+      else
+       my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest);
+    }
+  else
+    my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
 }
 
index 0039c3e..71fba89 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#define VERSION "2.57"
+#define VERSION "2.74"
 
 #define FTABSIZ 150 /* max number of outstanding requests (default) */
 #define MAX_PROCS 20 /* max no children for TCP requests */
 #define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
+#define TCP_MAX_QUERIES 100 /* Maximum number of queries per incoming TCP connection */
 #define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
+#define SAFE_PKTSZ 1280 /* "go anywhere" UDP packet size */
+#define KEYBLOCK_LEN 40 /* choose to mininise fragmentation when storing DNSSEC keys */
+#define DNSSEC_WORK 50 /* Max number of queries to validate one question */
 #define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
 #define FORWARD_TEST 50 /* try all servers every 50 queries */
 #define FORWARD_TIME 20 /* or 20 seconds */
 #define RANDOM_SOCKS 64 /* max simultaneous random ports */
 #define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
 #define CACHESIZ 150 /* default cache size */
+#define TTL_FLOOR_LIMIT 3600 /* don't allow --min-cache-ttl to raise TTL above this under any circumstances */
 #define MAXLEASES 1000 /* maximum number of DHCP leases */
 #define PING_WAIT 3 /* wait for ping address-in-use test */
 #define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */
 #define DECLINE_BACKOFF 600 /* disable DECLINEd static addresses for this long */
 #define DHCP_PACKET_MAX 16384 /* hard limit on DHCP packet size */
-#define SMALLDNAME 40 /* most domain names are smaller than this */
+#define SMALLDNAME 50 /* most domain names are smaller than this */
+#define CNAME_CHAIN 10 /* chains longer than this atr dropped for loop protection */
 #define HOSTSFILE "/etc/hosts"
 #define ETHERSFILE "/etc/ethers"
-#ifdef __uClinux__
-#  define RESOLVFILE "/etc/config/resolv.conf"
-#else
-#  define RESOLVFILE "/etc/resolv.conf"
-#endif
-#define RUNFILE "/var/run/dnsmasq.pid"
-
-#ifndef LEASEFILE
-#   if defined(__FreeBSD__) || defined (__OpenBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
-#      define LEASEFILE "/var/db/dnsmasq.leases"
-#   elif defined(__sun__) || defined (__sun)
-#      define LEASEFILE "/var/cache/dnsmasq.leases"
-#   elif defined(__ANDROID__)
-#      define LEASEFILE "/data/misc/dhcp/dnsmasq.leases"
-#   else
-#      define LEASEFILE "/var/lib/misc/dnsmasq.leases"
-#   endif
-#endif
-
-#ifndef CONFFILE
-#   if defined(__FreeBSD__)
-#      define CONFFILE "/usr/local/etc/dnsmasq.conf"
-#   else
-#      define CONFFILE "/etc/dnsmasq.conf"
-#   endif
-#endif
-
 #define DEFLEASE 3600 /* default lease time, 1 hour */
 #define CHUSER "nobody"
 #define CHGRP "dip"
-#define NAMESERVER_PORT 53
-#define DHCP_SERVER_PORT 67
-#define DHCP_CLIENT_PORT 68
-#define DHCP_SERVER_ALTPORT 1067
-#define DHCP_CLIENT_ALTPORT 1068
-#define PXE_PORT 4011
-#define TFTP_PORT 69
 #define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
 #define LOG_MAX 5 /* log-queue length */
 #define RANDFILE "/dev/urandom"
-#define DAD_WAIT 20 /* retry binding IPv6 sockets for this long */
-#define EDNS0_OPTION_MAC 5 /* dyndns.org temporary assignment */
-
-/* DBUS interface specifics */
-#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq"
+#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq" /* Default - may be overridden by config */
 #define DNSMASQ_PATH "/uk/org/thekelleys/dnsmasq"
-
-/* Follows system specific switches. If you run on a 
-   new system, you may want to edit these. 
-   May replace this with Autoconf one day. 
-
-HAVE_LINUX_NETWORK
-HAVE_BSD_NETWORK
-HAVE_SOLARIS_NETWORK
-   define exactly one of these to alter interaction with kernel networking.
+#define AUTH_TTL 600 /* default TTL for auth DNS */
+#define SOA_REFRESH 1200 /* SOA refresh default */
+#define SOA_RETRY 180 /* SOA retry default */
+#define SOA_EXPIRY 1209600 /* SOA expiry default */
+#define LOOP_TEST_DOMAIN "test" /* domain for loop testing, "test" is reserved by RFC 2606 and won't therefore clash */
+#define LOOP_TEST_TYPE T_TXT
+/* compile-time options: uncomment below to enable or do eg.
+   make COPTS=-DHAVE_BROKEN_RTC
 
 HAVE_BROKEN_RTC
    define this on embedded systems which don't have an RTC
@@ -108,20 +76,16 @@ HAVE_TFTP
    define this to get dnsmasq's built-in TFTP server.
 
 HAVE_DHCP
-   define this to get dnsmasq's DHCP server.
+   define this to get dnsmasq's DHCPv4 server.
 
-HAVE_SCRIPT
-   define this to get the ability to call scripts on lease-change
+HAVE_DHCP6
+   define this to get dnsmasq's DHCPv6 server. (implies HAVE_DHCP).
 
-HAVE_GETOPT_LONG
-   define this if you have GNU libc or GNU getopt. 
-
-HAVE_ARC4RANDOM
-   define this if you have arc4random() to get better security from DNS spoofs
-   by using really random ids (OpenBSD) 
+HAVE_SCRIPT
+   define this to get the ability to call scripts on lease-change.
 
-HAVE_SOCKADDR_SA_LEN
-   define this if struct sockaddr has sa_len field (*BSD
+HAVE_LUASCRIPT
+   define this to get the ability to call Lua script on lease-change. (implies HAVE_SCRIPT
 
 HAVE_DBUS
    define this if you want to link against libdbus, and have dnsmasq
@@ -134,56 +98,142 @@ HAVE_IDN
          included when internationalisation support is built, using the 
         *-i18n makefile targets, even if HAVE_IDN is not explicitly set.
 
-NOTES:
-   For Linux you should define 
-      HAVE_LINUX_NETWORK
-      HAVE_GETOPT_LONG
-  you should NOT define 
-      HAVE_ARC4RANDOM
-      HAVE_SOCKADDR_SA_LEN
-
-   For *BSD systems you should define 
-     HAVE_BSD_NETWORK
-     HAVE_SOCKADDR_SA_LEN
-   and you MAY define  
-     HAVE_ARC4RANDOM - OpenBSD and FreeBSD and NetBSD version 2.0 or later
-     HAVE_GETOPT_LONG - NetBSD, later FreeBSD 
-                       (FreeBSD and OpenBSD only if you link GNU getopt) 
+HAVE_CONNTRACK
+   define this to include code which propogates conntrack marks from
+   incoming DNS queries to the corresponding upstream queries. This adds
+   a build-dependency on libnetfilter_conntrack, but the resulting binary will
+   still run happily on a kernel without conntrack support.
+
+HAVE_IPSET
+    define this to include the ability to selectively add resolved ip addresses
+    to given ipsets.
+
+HAVE_AUTH
+   define this to include the facility to act as an authoritative DNS
+   server for one or more zones.
+
+HAVE_DNSSEC
+   include DNSSEC validator.
+
+HAVE_LOOP
+   include functionality to probe for and remove DNS forwarding loops.
+
+HAVE_INOTIFY
+   use the Linux inotify facility to efficiently re-read configuration files.
+
+NO_IPV6
+NO_TFTP
+NO_DHCP
+NO_DHCP6
+NO_SCRIPT
+NO_LARGEFILE
+NO_AUTH
+NO_INOTIFY
+   these are avilable to explictly disable compile time options which would 
+   otherwise be enabled automatically (HAVE_IPV6, >2Gb file sizes) or 
+   which are enabled  by default in the distributed source tree. Building dnsmasq
+   with something like "make COPTS=-DNO_SCRIPT" will do the trick.
+
+NO_NETTLE_ECC
+   Don't include the ECDSA cypher in DNSSEC validation. Needed for older Nettle versions.
+NO_GMP
+   Don't use and link against libgmp, Useful if nettle is built with --enable-mini-gmp.
+
+LEASEFILE
+CONFFILE
+RESOLVFILE
+   the default locations of these files are determined below, but may be overridden 
+   in a build command line using COPTS.
 
 */
 
-/* platform independent options- uncomment to enable */
+/* Defining this builds a binary which handles time differently and works better on a system without a 
+   stable RTC (it uses uptime, not epoch time) and writes the DHCP leases file less often to avoid flash wear. 
+*/
+
+/* #define HAVE_BROKEN_RTC */
+
+/* The default set of options to build. Built with these options, dnsmasq
+   has no library dependencies other than libc */
+
 #define HAVE_DHCP
+#define HAVE_DHCP6 
 #define HAVE_TFTP
 #define HAVE_SCRIPT
-/* #define HAVE_BROKEN_RTC */
-#define HAVE_DBUS
+#define HAVE_AUTH
+#define HAVE_IPSET 
+#define HAVE_LOOP
+
+/* Build options which require external libraries.
+   
+   Defining HAVE_<opt>_STATIC as _well_ as HAVE_<opt> will link the library statically.
+
+   You can use "make COPTS=-DHAVE_<opt>" instead of editing these.
+*/
+
+/* #define HAVE_LUASCRIPT */
+/* #define HAVE_DBUS */
 /* #define HAVE_IDN */
+/* #define HAVE_CONNTRACK */
+/* #define HAVE_DNSSEC */
 
-/* Allow TFTP to be disabled with COPTS=-DNO_TFTP */
-#ifdef NO_TFTP
-#undef HAVE_TFTP
+
+/* Default locations for important system files. */
+
+#ifndef LEASEFILE
+#   if defined(__FreeBSD__) || defined (__OpenBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
+#      define LEASEFILE "/var/db/dnsmasq.leases"
+#   elif defined(__sun__) || defined (__sun)
+#      define LEASEFILE "/var/cache/dnsmasq.leases"
+#   elif defined(__ANDROID__)
+#      define LEASEFILE "/data/misc/dhcp/dnsmasq.leases"
+#   else
+#      define LEASEFILE "/var/lib/misc/dnsmasq.leases"
+#   endif
 #endif
 
-/* Allow DHCP to be disabled with COPTS=-DNO_DHCP */
-#ifdef NO_DHCP
-#undef HAVE_DHCP
+#ifndef CONFFILE
+#   if defined(__FreeBSD__)
+#      define CONFFILE "/usr/local/etc/dnsmasq.conf"
+#   else
+#      define CONFFILE "/etc/dnsmasq.conf"
+#   endif
 #endif
 
-/* Allow scripts to be disabled with COPTS=-DNO_SCRIPT */
-#ifdef NO_SCRIPT
-#undef HAVE_SCRIPT
+#ifndef RESOLVFILE
+#   if defined(__uClinux__)
+#      define RESOLVFILE "/etc/config/resolv.conf"
+#   else
+#      define RESOLVFILE "/etc/resolv.conf"
+#   endif
 #endif
 
+#ifndef RUNFILE
+#   if defined(__ANDROID__)
+#      define RUNFILE "/data/dnsmasq.pid"
+#    else
+#      define RUNFILE "/var/run/dnsmasq.pid"
+#    endif
+#endif
 
+/* platform dependent options: these are determined automatically below
 
-/* platform dependent options. */
+HAVE_LINUX_NETWORK
+HAVE_BSD_NETWORK
+HAVE_SOLARIS_NETWORK
+   define exactly one of these to alter interaction with kernel networking.
+
+HAVE_GETOPT_LONG
+   defined when GNU-style getopt_long available. 
+
+HAVE_SOCKADDR_SA_LEN
+   defined if struct sockaddr has sa_len field (*BSD) 
+*/
 
 /* Must preceed __linux__ since uClinux defines __linux__ too. */
 #if defined(__uClinux__)
 #define HAVE_LINUX_NETWORK
 #define HAVE_GETOPT_LONG
-#undef HAVE_ARC4RANDOM
 #undef HAVE_SOCKADDR_SA_LEN
 /* Never use fork() on uClinux. Note that this is subtly different from the
    --keep-in-foreground option, since it also  suppresses forking new 
@@ -197,7 +247,6 @@ NOTES:
    ((__UCLIBC_MAJOR__==0) && (__UCLIBC_MINOR__==9) && (__UCLIBC_SUBLEVEL__<21))
 #    define HAVE_GETOPT_LONG
 #endif
-#undef HAVE_ARC4RANDOM
 #undef HAVE_SOCKADDR_SA_LEN
 #if !defined(__ARCH_HAS_MMU__) && !defined(__UCLIBC_HAS_MMU__)
 #  define NO_FORK
@@ -212,7 +261,6 @@ NOTES:
 #elif defined(__linux__)
 #define HAVE_LINUX_NETWORK
 #define HAVE_GETOPT_LONG
-#undef HAVE_ARC4RANDOM
 #undef HAVE_SOCKADDR_SA_LEN
 
 #elif defined(__FreeBSD__) || \
@@ -224,57 +272,180 @@ NOTES:
 #if defined(optional_argument) && defined(required_argument)
 #   define HAVE_GETOPT_LONG
 #endif
-#if !defined(__FreeBSD_kernel__)
-#   define HAVE_ARC4RANDOM
-#endif
 #define HAVE_SOCKADDR_SA_LEN
 
 #elif defined(__APPLE__)
 #define HAVE_BSD_NETWORK
 #define HAVE_GETOPT_LONG
-#define HAVE_ARC4RANDOM
 #define HAVE_SOCKADDR_SA_LEN
 /* Define before sys/socket.h is included so we get socklen_t */
 #define _BSD_SOCKLEN_T_
+/* Select the RFC_3542 version of the IPv6 socket API. 
+   Define before netinet6/in6.h is included. */
+#define __APPLE_USE_RFC_3542 
+#define NO_IPSET
+
 #elif defined(__NetBSD__)
 #define HAVE_BSD_NETWORK
 #define HAVE_GETOPT_LONG
-#undef HAVE_ARC4RANDOM
 #define HAVE_SOCKADDR_SA_LEN
 
 #elif defined(__sun) || defined(__sun__)
 #define HAVE_SOLARIS_NETWORK
 #define HAVE_GETOPT_LONG
-#undef HAVE_ARC4RANDOM
 #undef HAVE_SOCKADDR_SA_LEN
 #define ETHER_ADDR_LEN 6 
  
 #endif
 
 /* Decide if we're going to support IPv6 */
-/* IPv6 can be forced off with "make COPTS=-DNO_IPV6" */
 /* We assume that systems which don't have IPv6
    headers don't have ntop and pton either */
 
-#if defined(INET6_ADDRSTRLEN) && defined(IPV6_V6ONLY) && !defined(NO_IPV6)
+#if defined(INET6_ADDRSTRLEN) && defined(IPV6_V6ONLY)
 #  define HAVE_IPV6
 #  define ADDRSTRLEN INET6_ADDRSTRLEN
-#  if defined(SOL_IPV6)
-#    define IPV6_LEVEL SOL_IPV6
-#  else
-#    define IPV6_LEVEL IPPROTO_IPV6
+#else
+#  if !defined(INET_ADDRSTRLEN)
+#      define INET_ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */
 #  endif
-#elif defined(INET_ADDRSTRLEN)
 #  undef HAVE_IPV6
 #  define ADDRSTRLEN INET_ADDRSTRLEN
-#else
-#  undef HAVE_IPV6
-#  define ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */
 #endif
 
-/* Can't do scripts without fork */
-#ifdef NOFORK
-#  undef HAVE_SCRIPT
+
+/* rules to implement compile-time option dependencies and 
+   the NO_XXX flags */
+
+#ifdef NO_IPV6
+#undef HAVE_IPV6
+#endif
+
+#ifdef NO_TFTP
+#undef HAVE_TFTP
+#endif
+
+#ifdef NO_DHCP
+#undef HAVE_DHCP
+#undef HAVE_DHCP6
+#endif
+
+#if defined(NO_DHCP6) || !defined(HAVE_IPV6)
+#undef HAVE_DHCP6
+#endif
+
+/* DHCP6 needs DHCP too */
+#ifdef HAVE_DHCP6
+#define HAVE_DHCP
 #endif
 
+#if defined(NO_SCRIPT) || !defined(HAVE_DHCP) || defined(NO_FORK)
+#undef HAVE_SCRIPT
+#undef HAVE_LUASCRIPT
+#endif
+
+/* Must HAVE_SCRIPT to HAVE_LUASCRIPT */
+#ifdef HAVE_LUASCRIPT
+#define HAVE_SCRIPT
+#endif
+
+#ifdef NO_AUTH
+#undef HAVE_AUTH
+#endif
+
+#if defined(NO_IPSET)
+#undef HAVE_IPSET
+#endif
+
+#ifdef NO_LOOP
+#undef HAVE_LOOP
+#endif
+
+#if defined (HAVE_LINUX_NETWORK) && !defined(NO_INOTIFY)
+#define HAVE_INOTIFY
+#endif
+
+/* Define a string indicating which options are in use.
+   DNSMASQP_COMPILE_OPTS is only defined in dnsmasq.c */
+
+#ifdef DNSMASQ_COMPILE_OPTS
+
+static char *compile_opts = 
+#ifndef HAVE_IPV6
+"no-"
+#endif
+"IPv6 "
+#ifndef HAVE_GETOPT_LONG
+"no-"
+#endif
+"GNU-getopt "
+#ifdef HAVE_BROKEN_RTC
+"no-RTC "
+#endif
+#ifdef NO_FORK
+"no-MMU "
+#endif
+#ifndef HAVE_DBUS
+"no-"
+#endif
+"DBus "
+#ifndef LOCALEDIR
+"no-"
+#endif
+"i18n "
+#if !defined(LOCALEDIR) && !defined(HAVE_IDN)
+"no-"
+#endif 
+"IDN "
+#ifndef HAVE_DHCP
+"no-"
+#endif
+"DHCP "
+#if defined(HAVE_DHCP)
+#  if !defined (HAVE_DHCP6)
+     "no-"
+#  endif  
+     "DHCPv6 "
+#  if !defined(HAVE_SCRIPT)
+     "no-scripts "
+#  else
+#    if !defined(HAVE_LUASCRIPT)
+       "no-"
+#    endif
+     "Lua "
+#  endif
+#endif
+#ifndef HAVE_TFTP
+"no-"
+#endif
+"TFTP "
+#ifndef HAVE_CONNTRACK
+"no-"
+#endif
+"conntrack "
+#ifndef HAVE_IPSET
+"no-"
+#endif
+"ipset "
+#ifndef HAVE_AUTH
+"no-"
+#endif
+"auth "
+#ifndef HAVE_DNSSEC
+"no-"
+#endif
+"DNSSEC "
+#ifndef HAVE_LOOP
+"no-"
+#endif
+"loop-detect "
+#ifndef HAVE_INOTIFY
+"no-"
+#endif
+"inotify";
+
+
+#endif
+
+
+
diff --git a/src/conntrack.c b/src/conntrack.c
new file mode 100644 (file)
index 0000000..0fa2da9
--- /dev/null
@@ -0,0 +1,90 @@
+/* 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"
+
+#ifdef HAVE_CONNTRACK
+
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+static int gotit = 0; /* yuck */
+
+static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data);
+
+int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr, int istcp, unsigned int *markp)
+{
+  struct nf_conntrack *ct;
+  struct nfct_handle *h;
+  
+  gotit = 0;
+  
+  if ((ct = nfct_new())) 
+    {
+      nfct_set_attr_u8(ct, ATTR_L4PROTO, istcp ? IPPROTO_TCP : IPPROTO_UDP);
+      nfct_set_attr_u16(ct, ATTR_PORT_DST, htons(daemon->port));
+      
+#ifdef HAVE_IPV6
+      if (peer_addr->sa.sa_family == AF_INET6)
+       {
+         nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET6);
+         nfct_set_attr(ct, ATTR_IPV6_SRC, peer_addr->in6.sin6_addr.s6_addr);
+         nfct_set_attr_u16(ct, ATTR_PORT_SRC, peer_addr->in6.sin6_port);
+         nfct_set_attr(ct, ATTR_IPV6_DST, local_addr->addr.addr6.s6_addr);
+       }
+      else
+#endif
+       {
+         nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET);
+         nfct_set_attr_u32(ct, ATTR_IPV4_SRC, peer_addr->in.sin_addr.s_addr);
+         nfct_set_attr_u16(ct, ATTR_PORT_SRC, peer_addr->in.sin_port);
+         nfct_set_attr_u32(ct, ATTR_IPV4_DST, local_addr->addr.addr4.s_addr);
+       }
+      
+      
+      if ((h = nfct_open(CONNTRACK, 0))) 
+       {
+         nfct_callback_register(h, NFCT_T_ALL, callback, (void *)markp);  
+         if (nfct_query(h, NFCT_Q_GET, ct) == -1)
+           {
+             static int warned = 0;
+             if (!warned)
+               {
+                 my_syslog(LOG_ERR, _("Conntrack connection mark retrieval failed: %s"), strerror(errno));
+                 warned = 1;
+               }
+           }
+         nfct_close(h);  
+       }
+      nfct_destroy(ct);
+    }
+
+  return gotit;
+}
+
+static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data)
+{
+  unsigned int *ret = (unsigned int *)data;
+  *ret = nfct_get_attr_u32(ct, ATTR_MARK);
+  (void)type; /* eliminate warning */
+  gotit = 1;
+
+  return NFCT_CB_CONTINUE;
+}
+
+#endif
+  
+
+
index 5cb43fb..3555f49 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
@@ -20,7 +20,7 @@
 
 #include <dbus/dbus.h>
 
-const char* introspection_xml =
+const char* introspection_xml_template =
 "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
 "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
 "<node name=\"" DNSMASQ_PATH "\">\n"
@@ -29,15 +29,32 @@ const char* introspection_xml =
 "      <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
 "    </method>\n"
 "  </interface>\n"
-"  <interface name=\"" DNSMASQ_SERVICE "\">\n"
+"  <interface name=\"%s\">\n"
 "    <method name=\"ClearCache\">\n"
 "    </method>\n"
 "    <method name=\"GetVersion\">\n"
 "      <arg name=\"version\" direction=\"out\" type=\"s\"/>\n"
 "    </method>\n"
+#ifdef HAVE_LOOP
+"    <method name=\"GetLoopServers\">\n"
+"      <arg name=\"server\" direction=\"out\" type=\"as\"/>\n"
+"    </method>\n"
+#endif
 "    <method name=\"SetServers\">\n"
 "      <arg name=\"servers\" direction=\"in\" type=\"av\"/>\n"
 "    </method>\n"
+"    <method name=\"SetDomainServers\">\n"
+"      <arg name=\"servers\" direction=\"in\" type=\"as\"/>\n"
+"    </method>\n"
+"    <method name=\"SetServersEx\">\n"
+"      <arg name=\"servers\" direction=\"in\" type=\"aas\"/>\n"
+"    </method>\n"
+"    <method name=\"SetFilterWin2KOption\">\n"
+"      <arg name=\"filterwin2k\" direction=\"in\" type=\"b\"/>\n"
+"    </method>\n"
+"    <method name=\"SetBogusPrivOption\">\n"
+"      <arg name=\"boguspriv\" direction=\"in\" type=\"b\"/>\n"
+"    </method>\n"
 "    <signal name=\"DhcpLeaseAdded\">\n"
 "      <arg name=\"ipaddr\" type=\"s\"/>\n"
 "      <arg name=\"hwaddr\" type=\"s\"/>\n"
@@ -53,9 +70,26 @@ const char* introspection_xml =
 "      <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";
 
+static char *introspection_xml = NULL;
+
 struct watch {
   DBusWatch *watch;      
   struct watch *next;
@@ -83,33 +117,32 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *data)
 
 static void remove_watch(DBusWatch *watch, void *data)
 {
-  struct watch **up, *w;  
+  struct watch **up, *w, *tmp;  
   
-  for (up = &(daemon->watches), w = daemon->watches; w; w = w->next)
-    if (w->watch == watch)
-      {
-        *up = w->next;
-        free(w);
-      }
-    else
-      up = &(w->next);
+  for (up = &(daemon->watches), w = daemon->watches; w; w = tmp)
+    {
+      tmp = w->next;
+      if (w->watch == watch)
+       {
+         *up = tmp;
+         free(w);
+       }
+      else
+       up = &(w->next);
+    }
 
   w = data; /* no warning */
 }
 
 static void dbus_read_servers(DBusMessage *message)
 {
-  struct server *serv, *tmp, **up;
   DBusMessageIter iter;
   union  mysockaddr addr, source_addr;
   char *domain;
   
   dbus_message_iter_init(message, &iter);
-  
-  /* mark everything from DBUS */
-  for (serv = daemon->servers; serv; serv = serv->next)
-    if (serv->flags & SERV_FROM_DBUS)
-      serv->flags |= SERV_MARK;
+
+  mark_servers(SERV_FROM_DBUS);
   
   while (1)
     {
@@ -143,13 +176,16 @@ static void dbus_read_servers(DBusMessage *message)
              dbus_message_iter_get_basic(&iter, &p[i]);
              dbus_message_iter_next (&iter);
              if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BYTE)
-               break;
+               {
+                 i++;
+                 break;
+               }
            }
 
 #ifndef HAVE_IPV6
          my_syslog(LOG_WARNING, _("attempt to set an IPv6 server address via DBus - no IPv6 support"));
 #else
-         if (i == sizeof(struct in6_addr)-1)
+         if (i == sizeof(struct in6_addr))
            {
              memcpy(&addr.in6.sin6_addr, p, sizeof(struct in6_addr));
 #ifdef HAVE_SOCKADDR_SA_LEN
@@ -169,6 +205,7 @@ static void dbus_read_servers(DBusMessage *message)
        /* At the end */
        break;
       
+      /* process each domain */
       do {
        if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING)
          {
@@ -179,123 +216,500 @@ static void dbus_read_servers(DBusMessage *message)
          domain = NULL;
        
        if (!skip)
-         {
-           /* See if this is already there, and unmark */
-           for (serv = daemon->servers; serv; serv = serv->next)
-             if ((serv->flags & SERV_FROM_DBUS) &&
-                 (serv->flags & SERV_MARK))
+         add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain);
+     
+      } while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING); 
+    }
+   
+  /* unlink and free anything still marked. */
+  cleanup_servers();
+}
+
+#ifdef HAVE_LOOP
+static DBusMessage *dbus_reply_server_loop(DBusMessage *message)
+{
+  DBusMessageIter args, args_iter;
+  struct server *serv;
+  DBusMessage *reply = dbus_message_new_method_return(message);
+   
+  dbus_message_iter_init_append (reply, &args);
+  dbus_message_iter_open_container (&args, DBUS_TYPE_ARRAY,DBUS_TYPE_STRING_AS_STRING, &args_iter);
+
+  for (serv = daemon->servers; serv; serv = serv->next)
+    if (serv->flags & SERV_LOOP)
+      {
+       prettyprint_addr(&serv->addr, daemon->addrbuff);
+       dbus_message_iter_append_basic (&args_iter, DBUS_TYPE_STRING, &daemon->addrbuff);
+      }
+  
+  dbus_message_iter_close_container (&args, &args_iter);
+
+  return reply;
+}
+#endif
+
+static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
+{
+  DBusMessageIter iter, array_iter, string_iter;
+  DBusMessage *error = NULL;
+  const char *addr_err;
+  char *dup = NULL;
+  
+  if (!dbus_message_iter_init(message, &iter))
+    {
+      return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+                                    "Failed to initialize dbus message iter");
+    }
+
+  /* check that the message contains an array of arrays */
+  if ((dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) ||
+      (dbus_message_iter_get_element_type(&iter) != (strings ? DBUS_TYPE_STRING : DBUS_TYPE_ARRAY)))
+    {
+      return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+                                    strings ? "Expected array of string" : "Expected array of string arrays");
+     }
+  mark_servers(SERV_FROM_DBUS);
+
+  /* array_iter points to each "as" element in the outer array */
+  dbus_message_iter_recurse(&iter, &array_iter);
+  while (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID)
+    {
+      const char *str = NULL;
+      union  mysockaddr addr, source_addr;
+      int flags = 0;
+      char interface[IF_NAMESIZE];
+      char *str_addr, *str_domain = NULL;
+
+      if (strings)
+       {
+         dbus_message_iter_get_basic(&array_iter, &str);
+         if (!str || !strlen (str))
+           {
+             error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+                                            "Empty string");
+             break;
+           }
+         
+         /* dup the string because it gets modified during parsing */
+         if (dup)
+           free(dup);
+         if (!(dup = str_domain = whine_malloc(strlen(str)+1)))
+           break;
+         
+         strcpy(str_domain, str);
+
+         /* point to address part of old string for error message */
+         if ((str_addr = strrchr(str, '/')))
+           str = str_addr+1;
+         
+         if ((str_addr = strrchr(str_domain, '/')))
+           {
+             if (*str_domain != '/' || str_addr == str_domain)
                {
-                 if (!(serv->flags & SERV_HAS_DOMAIN) && !domain)
-                   {
-                     serv->flags &= ~SERV_MARK;
-                     break;
-                   }
-                 if ((serv->flags & SERV_HAS_DOMAIN) && 
-                     domain &&
-                     hostname_isequal(domain, serv->domain))
-                   {
-                     serv->flags &= ~SERV_MARK;
-                     break;
-                   }
+                 error = dbus_message_new_error_printf(message,
+                                                       DBUS_ERROR_INVALID_ARGS,
+                                                       "No domain terminator '%s'",
+                                                       str);
+                 break;
                }
-           
-           if (!serv && (serv = whine_malloc(sizeof (struct server))))
-             {
-               /* Not found, create a new one. */
-               memset(serv, 0, sizeof(struct server));
-               
-               if (domain)
-                 serv->domain = whine_malloc(strlen(domain)+1);
-               
-               if (domain && !serv->domain)
-                 {
-                   free(serv);
-                   serv = NULL;
-                 }
-               else
-                 {
-                   serv->next = daemon->servers;
-                   daemon->servers = serv;
-                   serv->flags = SERV_FROM_DBUS;
-                   if (domain)
-                     {
-                       strcpy(serv->domain, domain);
-                       serv->flags |= SERV_HAS_DOMAIN;
-                     }
-                 }
-             }
+             *str_addr++ = 0;
+             str_domain++;
+           }
+         else
+           {
+             str_addr = str_domain;
+             str_domain = NULL;
+           }
+
+         
+       }
+      else
+       {
+         /* check the types of the struct and its elements */
+         if ((dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY) ||
+             (dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_STRING))
+           {
+             error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+                                            "Expected inner array of strings");
+             break;
+           }
+         
+         /* string_iter points to each "s" element in the inner array */
+         dbus_message_iter_recurse(&array_iter, &string_iter);
+         if (dbus_message_iter_get_arg_type(&string_iter) != DBUS_TYPE_STRING)
+           {
+             /* no IP address given */
+             error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+                                            "Expected IP address");
+             break;
+           }
+         
+         dbus_message_iter_get_basic(&string_iter, &str);
+         if (!str || !strlen (str))
+           {
+             error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+                                            "Empty IP address");
+             break;
+           }
+         
+         /* dup the string because it gets modified during parsing */
+         if (dup)
+           free(dup);
+         if (!(dup = str_addr = whine_malloc(strlen(str)+1)))
+           break;
+         
+         strcpy(str_addr, str);
+       }
+
+      memset(&addr, 0, sizeof(addr));
+      memset(&source_addr, 0, sizeof(source_addr));
+      memset(&interface, 0, sizeof(interface));
 
-           if (serv)
+      /* parse the IP address */
+      if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
+       {
+          error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
+                                                "Invalid IP address '%s': %s",
+                                                str, addr_err);
+          break;
+        }
+      
+      /* 0.0.0.0 for server address == NULL, for Dbus */
+      if (addr.in.sin_family == AF_INET &&
+          addr.in.sin_addr.s_addr == 0)
+        flags |= SERV_NO_ADDR;
+      
+      if (strings)
+       {
+         char *p;
+         
+         do {
+           if (str_domain)
              {
-               if (source_addr.in.sin_family == AF_INET &&
-                   addr.in.sin_addr.s_addr == 0 &&
-                   serv->domain)
-                 serv->flags |= SERV_NO_ADDR;
-               else
-                 {
-                   serv->flags &= ~SERV_NO_ADDR;
-                   serv->addr = addr;
-                   serv->source_addr = source_addr;
-                 }
+               if ((p = strchr(str_domain, '/')))
+                 *p++ = 0;
              }
-         }
-       } while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
+           else 
+             p = NULL;
+           
+           add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain);
+         } while ((str_domain = p));
+       }
+      else
+       {
+         /* jump past the address to the domain list (if any) */
+         dbus_message_iter_next (&string_iter);
+         
+         /* parse domains and add each server/domain pair to the list */
+         do {
+           str = NULL;
+           if (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING)
+             dbus_message_iter_get_basic(&string_iter, &str);
+           dbus_message_iter_next (&string_iter);
+           
+           add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str);
+         } while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
+       }
+        
+      /* jump to next element in outer array */
+      dbus_message_iter_next(&array_iter);
     }
+
+  cleanup_servers();
+
+  if (dup)
+    free(dup);
+
+  return error;
+}
+
+static DBusMessage *dbus_set_bool(DBusMessage *message, int flag, char *name)
+{
+  DBusMessageIter iter;
+  dbus_bool_t enabled;
+
+  if (!dbus_message_iter_init(message, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
+    return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Expected boolean argument");
   
-  /* unlink and free anything still marked. */
-  for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) 
+  dbus_message_iter_get_basic(&iter, &enabled);
+
+  if (enabled)
+    { 
+      my_syslog(LOG_INFO, _("Enabling --%s option from D-Bus"), name);
+      set_option_bool(flag);
+    }
+  else
     {
-      tmp = serv->next;
-      if (serv->flags & SERV_MARK)
-       {
-         server_gone(serv);
-         *up = serv->next;
-         free(serv);
-       }
-      else 
-       up = &serv->next;
+      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)
 {
   char *method = (char *)dbus_message_get_member(message);
-   
+  DBusMessage *reply = NULL;
+  int clear_cache = 0, new_servers = 0;
+    
   if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
     {
-      DBusMessage *reply = dbus_message_new_method_return(message);
-
-      dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_xml, DBUS_TYPE_INVALID);
-      dbus_connection_send (connection, reply, NULL);
-      dbus_message_unref (reply);
+      /* string length: "%s" provides space for termination zero */
+      if (!introspection_xml && 
+         (introspection_xml = whine_malloc(strlen(introspection_xml_template) + strlen(daemon->dbus_name))))
+       sprintf(introspection_xml, introspection_xml_template, daemon->dbus_name);
+    
+      if (introspection_xml)
+       {
+         reply = dbus_message_new_method_return(message);
+         dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_xml, DBUS_TYPE_INVALID);
+       }
     }
   else if (strcmp(method, "GetVersion") == 0)
     {
       char *v = VERSION;
-      DBusMessage *reply = dbus_message_new_method_return(message);
+      reply = dbus_message_new_method_return(message);
       
       dbus_message_append_args(reply, DBUS_TYPE_STRING, &v, DBUS_TYPE_INVALID);
-      dbus_connection_send (connection, reply, NULL);
-      dbus_message_unref (reply);
     }
+#ifdef HAVE_LOOP
+  else if (strcmp(method, "GetLoopServers") == 0)
+    {
+      reply = dbus_reply_server_loop(message);
+    }
+#endif
   else if (strcmp(method, "SetServers") == 0)
     {
-      my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
       dbus_read_servers(message);
-      check_servers();
+      new_servers = 1;
+    }
+  else if (strcmp(method, "SetServersEx") == 0)
+    {
+      reply = dbus_read_servers_ex(message, 0);
+      new_servers = 1;
+    }
+  else if (strcmp(method, "SetDomainServers") == 0)
+    {
+      reply = dbus_read_servers_ex(message, 1);
+      new_servers = 1;
+    }
+  else if (strcmp(method, "SetFilterWin2KOption") == 0)
+    {
+      reply = dbus_set_bool(message, OPT_FILTER, "filterwin2k");
+    }
+  else if (strcmp(method, "SetBogusPrivOption") == 0)
+    {
+      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_and_reload(dnsmasq_time());
+    clear_cache = 1;
   else
     return (DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+   
+  if (new_servers)
+    {
+      my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
+      check_servers();
+      if (option_bool(OPT_RELOAD))
+       clear_cache = 1;
+    }
+
+  if (clear_cache)
+    clear_cache_and_reload(dnsmasq_time());
   
   method = user_data; /* no warning */
 
+  /* If no reply or no error, return nothing */
+  if (!reply)
+    reply = dbus_message_new_method_return(message);
+
+  if (reply)
+    {
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+    }
+
   return (DBUS_HANDLER_RESULT_HANDLED);
 }
  
 
@@ -315,7 +729,7 @@ char *dbus_init(void)
   dbus_connection_set_watch_functions(connection, add_watch, remove_watch, 
                                      NULL, NULL, NULL);
   dbus_error_init (&dbus_error);
-  dbus_bus_request_name (connection, DNSMASQ_SERVICE, 0, &dbus_error);
+  dbus_bus_request_name (connection, daemon->dbus_name, 0, &dbus_error);
   if (dbus_error_is_set (&dbus_error))
     return (char *)dbus_error.message;
   
@@ -325,7 +739,7 @@ char *dbus_init(void)
   
   daemon->dbus = connection; 
   
-  if ((message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, "Up")))
+  if ((message = dbus_message_new_signal(DNSMASQ_PATH, daemon->dbus_name, "Up")))
     {
       dbus_connection_send(connection, message, NULL);
       dbus_message_unref(message);
@@ -335,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;
   
@@ -346,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;
@@ -369,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)
@@ -396,7 +807,7 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
   DBusConnection *connection = (DBusConnection *)daemon->dbus;
   DBusMessage* message = NULL;
   DBusMessageIter args;
-  char *action_str, *addr, *mac = daemon->namebuff;
+  char *action_str, *mac = daemon->namebuff;
   unsigned char *p;
   int i;
 
@@ -406,29 +817,36 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
   if (!hostname)
     hostname = "";
   
-  p = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len,
-                     lease->hwaddr, lease->clid_len, lease->clid, &i);
-  print_mac(mac, p, i);
-  
+#ifdef HAVE_DHCP6
+   if (lease->flags & (LEASE_TA | LEASE_NA))
+     {
+       print_mac(mac, lease->clid, lease->clid_len);
+       inet_ntop(AF_INET6, &lease->addr6, daemon->addrbuff, ADDRSTRLEN);
+     }
+   else
+#endif
+     {
+       p = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len,
+                          lease->hwaddr, lease->clid_len, lease->clid, &i);
+       print_mac(mac, p, i);
+       inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN);
+     }
+
   if (action == ACTION_DEL)
     action_str = "DhcpLeaseDeleted";
   else if (action == ACTION_ADD)
     action_str = "DhcpLeaseAdded";
   else if (action == ACTION_OLD)
     action_str = "DhcpLeaseUpdated";
-  else if (action == ACTION_CONNECT)
-    action_str = "DhcpConnected";
   else
     return;
 
-  addr = inet_ntoa(lease->addr);
-
-  if (!(message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, action_str)))
+  if (!(message = dbus_message_new_signal(DNSMASQ_PATH, daemon->dbus_name, action_str)))
     return;
   
   dbus_message_iter_init_append(message, &args);
-
-  if (dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &addr) &&
+  
+  if (dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &daemon->addrbuff) &&
       dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &mac) &&
       dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &hostname))
     dbus_connection_send(connection, message, NULL);
diff --git a/src/dhcp-common.c b/src/dhcp-common.c
new file mode 100644 (file)
index 0000000..bc48f41
--- /dev/null
@@ -0,0 +1,905 @@
+/* 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"
+
+#ifdef HAVE_DHCP
+
+void dhcp_common_init(void)
+{
+    /* These each hold a DHCP option max size 255
+       and get a terminating zero added */
+  daemon->dhcp_buff = safe_malloc(256);
+  daemon->dhcp_buff2 = safe_malloc(256); 
+  daemon->dhcp_buff3 = safe_malloc(256);
+  
+  /* dhcp_packet is used by v4 and v6, outpacket only by v6 
+     sizeof(struct dhcp_packet) is as good an initial size as any,
+     even for v6 */
+  expand_buf(&daemon->dhcp_packet, sizeof(struct dhcp_packet));
+#ifdef HAVE_DHCP6
+  if (daemon->dhcp6)
+    expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
+#endif
+}
+
+ssize_t recv_dhcp_packet(int fd, struct msghdr *msg)
+{  
+  ssize_t sz;
+  while (1)
+    {
+      msg->msg_flags = 0;
+      while ((sz = recvmsg(fd, msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
+      
+      if (sz == -1)
+       return -1;
+      
+      if (!(msg->msg_flags & MSG_TRUNC))
+       break;
+
+      /* Very new Linux kernels return the actual size needed, 
+        older ones always return truncated size */
+      if ((size_t)sz == msg->msg_iov->iov_len)
+       {
+         if (!expand_buf(msg->msg_iov, sz + 100))
+           return -1;
+       }
+      else
+       {
+         expand_buf(msg->msg_iov, sz);
+         break;
+       }
+    }
+  
+  while ((sz = recvmsg(fd, msg, 0)) == -1 && errno == EINTR);
+  
+  return (msg->msg_flags & MSG_TRUNC) ? -1 : sz;
+}
+
+struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
+{
+  struct tag_if *exprs;
+  struct dhcp_netid_list *list;
+
+  for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
+    if (match_netid(exprs->tag, tags, 1))
+      for (list = exprs->set; list; list = list->next)
+       {
+         list->list->next = tags;
+         tags = list->list;
+       }
+
+  return tags;
+}
+
+
+struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags, struct dhcp_opt *opts)
+{
+  struct dhcp_netid *tagif = run_tag_if(tags);
+  struct dhcp_opt *opt;
+  struct dhcp_opt *tmp;  
+
+  /* flag options which are valid with the current tag set (sans context tags) */
+  for (opt = opts; opt; opt = opt->next)
+    {
+      opt->flags &= ~DHOPT_TAGOK;
+      if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
+         match_netid(opt->netid, tagif, 0))
+       opt->flags |= DHOPT_TAGOK;
+    }
+
+  /* now flag options which are valid, including the context tags,
+     otherwise valid options are inhibited if we found a higher priority one above */
+  if (context_tags)
+    {
+      struct dhcp_netid *last_tag;
+
+      for (last_tag = context_tags; last_tag->next; last_tag = last_tag->next);
+      last_tag->next = tags;
+      tagif = run_tag_if(context_tags);
+      
+      /* reset stuff with tag:!<tag> which now matches. */
+      for (opt = opts; opt; opt = opt->next)
+       if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
+           (opt->flags & DHOPT_TAGOK) &&
+           !match_netid(opt->netid, tagif, 0))
+         opt->flags &= ~DHOPT_TAGOK;
+
+      for (opt = opts; opt; opt = opt->next)
+       if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
+           match_netid(opt->netid, tagif, 0))
+         {
+           struct dhcp_opt *tmp;  
+           for (tmp = opts; tmp; tmp = tmp->next) 
+             if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK))
+               break;
+           if (!tmp)
+             opt->flags |= DHOPT_TAGOK;
+         }      
+    }
+  
+  /* now flag untagged options which are not overridden by tagged ones */
+  for (opt = opts; opt; opt = opt->next)
+    if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid)
+      {
+       for (tmp = opts; tmp; tmp = tmp->next) 
+         if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK))
+           break;
+       if (!tmp)
+         opt->flags |= DHOPT_TAGOK;
+       else if (!tmp->netid)
+         my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt); 
+      }
+
+  /* Finally, eliminate duplicate options later in the chain, and therefore earlier in the config file. */
+  for (opt = opts; opt; opt = opt->next)
+    if (opt->flags & DHOPT_TAGOK)
+      for (tmp = opt->next; tmp; tmp = tmp->next) 
+       if (tmp->opt == opt->opt)
+         tmp->flags &= ~DHOPT_TAGOK;
+  
+  return tagif;
+}
+       
+/* Is every member of check matched by a member of pool? 
+   If tagnotneeded, untagged is OK */
+int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
+{
+  struct dhcp_netid *tmp1;
+  
+  if (!check && !tagnotneeded)
+    return 0;
+
+  for (; check; check = check->next)
+    {
+      /* '#' for not is for backwards compat. */
+      if (check->net[0] != '!' && check->net[0] != '#')
+       {
+         for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
+           if (strcmp(check->net, tmp1->net) == 0)
+             break;
+         if (!tmp1)
+           return 0;
+       }
+      else
+       for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
+         if (strcmp((check->net)+1, tmp1->net) == 0)
+           return 0;
+    }
+  return 1;
+}
+
+/* return domain or NULL if none. */
+char *strip_hostname(char *hostname)
+{
+  char *dot = strchr(hostname, '.');
+  if (!dot)
+    return NULL;
+  
+  *dot = 0; /* truncate */
+  if (strlen(dot+1) != 0)
+    return dot+1;
+  
+  return NULL;
+}
+
+void log_tags(struct dhcp_netid *netid, u32 xid)
+{
+  if (netid && option_bool(OPT_LOG_OPTS))
+    {
+      char *s = daemon->namebuff;
+      for (*s = 0; netid; netid = netid->next)
+       {
+         /* kill dupes. */
+         struct dhcp_netid *n;
+         
+         for (n = netid->next; n; n = n->next)
+           if (strcmp(netid->net, n->net) == 0)
+             break;
+         
+         if (!n)
+           {
+             strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
+             if (netid->next)
+               strncat (s, ", ", (MAXDNAME-1) - strlen(s));
+           }
+       }
+      my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), xid, s);
+    } 
+}   
+  
+int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
+{
+  int i;
+  
+  if (o->len > len)
+    return 0;
+  
+  if (o->len == 0)
+    return 1;
+     
+  if (o->flags & DHOPT_HEX)
+    { 
+      if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask))
+       return 1;
+    }
+  else 
+    for (i = 0; i <= (len - o->len); ) 
+      {
+       if (memcmp(o->val, p + i, o->len) == 0)
+         return 1;
+           
+       if (o->flags & DHOPT_STRING)
+         i++;
+       else
+         i += o->len;
+      }
+  
+  return 0;
+}
+
+int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
+{
+  struct hwaddr_config *conf_addr;
+  
+  for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
+    if (conf_addr->wildcard_mask == 0 &&
+       conf_addr->hwaddr_len == len &&
+       (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
+       memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
+      return 1;
+  
+  return 0;
+}
+
+static int is_config_in_context(struct dhcp_context *context, struct dhcp_config *config)
+{
+  if (!context) /* called via find_config() from lease_update_from_configs() */
+    return 1; 
+
+  if (!(config->flags & (CONFIG_ADDR | CONFIG_ADDR6)))
+    return 1;
+  
+#ifdef HAVE_DHCP6
+  if ((context->flags & CONTEXT_V6) && (config->flags & CONFIG_WILDCARD))
+    return 1;
+#endif
+
+  for (; context; context = context->current)
+#ifdef HAVE_DHCP6
+    if (context->flags & CONTEXT_V6) 
+      {
+       if ((config->flags & CONFIG_ADDR6) && is_same_net6(&config->addr6, &context->start6, context->prefix))
+         return 1;
+      }
+    else 
+#endif
+      if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
+       return 1;
+
+  return 0;
+}
+
+struct dhcp_config *find_config(struct dhcp_config *configs,
+                               struct dhcp_context *context,
+                               unsigned char *clid, int clid_len,
+                               unsigned char *hwaddr, int hw_len, 
+                               int hw_type, char *hostname)
+{
+  int count, new;
+  struct dhcp_config *config, *candidate; 
+  struct hwaddr_config *conf_addr;
+
+  if (clid)
+    for (config = configs; config; config = config->next)
+      if (config->flags & CONFIG_CLID)
+       {
+         if (config->clid_len == clid_len && 
+             memcmp(config->clid, clid, clid_len) == 0 &&
+             is_config_in_context(context, config))
+           return config;
+         
+         /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
+            cope with that here. This is IPv4 only. context==NULL implies IPv4, 
+            see lease_update_from_configs() */
+         if ((!context || !(context->flags & CONTEXT_V6)) && *clid == 0 && config->clid_len == clid_len-1  &&
+             memcmp(config->clid, clid+1, clid_len-1) == 0 &&
+             is_config_in_context(context, config))
+           return config;
+       }
+  
+
+  if (hwaddr)
+    for (config = configs; config; config = config->next)
+      if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
+         is_config_in_context(context, config))
+       return config;
+  
+  if (hostname && context)
+    for (config = configs; config; config = config->next)
+      if ((config->flags & CONFIG_NAME) && 
+         hostname_isequal(config->hostname, hostname) &&
+         is_config_in_context(context, config))
+       return config;
+
+  
+  if (!hwaddr)
+    return NULL;
+
+  /* use match with fewest wildcard octets */
+  for (candidate = NULL, count = 0, config = configs; config; config = config->next)
+    if (is_config_in_context(context, config))
+      for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
+       if (conf_addr->wildcard_mask != 0 &&
+           conf_addr->hwaddr_len == hw_len &&  
+           (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
+           (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
+         {
+             count = new;
+             candidate = config;
+         }
+  
+  return candidate;
+}
+
+void dhcp_update_configs(struct dhcp_config *configs)
+{
+  /* Some people like to keep all static IP addresses in /etc/hosts.
+     This goes through /etc/hosts and sets static addresses for any DHCP config
+     records which don't have an address and whose name matches. 
+     We take care to maintain the invariant that any IP address can appear
+     in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP, 
+     restore the status-quo ante first. */
+  
+  struct dhcp_config *config, *conf_tmp;
+  struct crec *crec;
+  int prot = AF_INET;
+
+  for (config = configs; config; config = config->next)
+    if (config->flags & CONFIG_ADDR_HOSTS)
+      config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR6 | CONFIG_ADDR_HOSTS);
+
+#ifdef HAVE_DHCP6 
+ again:  
+#endif
+
+  if (daemon->port != 0)
+    for (config = configs; config; config = config->next)
+      {
+       int conflags = CONFIG_ADDR;
+       int cacheflags = F_IPV4;
+
+#ifdef HAVE_DHCP6
+       if (prot == AF_INET6)
+         {
+           conflags = CONFIG_ADDR6;
+           cacheflags = F_IPV6;
+         }
+#endif
+       if (!(config->flags & conflags) &&
+           (config->flags & CONFIG_NAME) && 
+           (crec = cache_find_by_name(NULL, config->hostname, 0, cacheflags)) &&
+           (crec->flags & F_HOSTS))
+         {
+           if (cache_find_by_name(crec, config->hostname, 0, cacheflags))
+             {
+               /* use primary (first) address */
+               while (crec && !(crec->flags & F_REVERSE))
+                 crec = cache_find_by_name(crec, config->hostname, 0, cacheflags);
+               if (!crec)
+                 continue; /* should be never */
+               inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN);
+               my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"), 
+                         config->hostname, daemon->addrbuff);
+             }
+           
+           if (prot == AF_INET && 
+               (!(conf_tmp = config_find_by_address(configs, crec->addr.addr.addr.addr4)) || conf_tmp == config))
+             {
+               config->addr = crec->addr.addr.addr.addr4;
+               config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
+               continue;
+             }
+
+#ifdef HAVE_DHCP6
+           if (prot == AF_INET6 && 
+               (!(conf_tmp = config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 128, 0)) || conf_tmp == config))
+             {
+               memcpy(&config->addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ);
+               config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS;
+               continue;
+             }
+#endif
+
+           inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN);
+           my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"), 
+                     daemon->addrbuff, config->hostname);
+           
+           
+         }
+      }
+
+#ifdef HAVE_DHCP6
+  if (prot == AF_INET)
+    {
+      prot = AF_INET6;
+      goto again;
+    }
+#endif
+
+}
+
+#ifdef HAVE_LINUX_NETWORK 
+char *whichdevice(void)
+{
+  /* If we are doing DHCP on exactly one interface, and running linux, do SO_BINDTODEVICE
+     to that device. This is for the use case of  (eg) OpenStack, which runs a new
+     dnsmasq instance for each VLAN interface it creates. Without the BINDTODEVICE, 
+     individual processes don't always see the packets they should.
+     SO_BINDTODEVICE is only available Linux. 
+
+     Note that if wildcards are used in --interface, or --interface is not used at all,
+     or a configured interface doesn't yet exist, then more interfaces may arrive later, 
+     so we can't safely assert there is only one interface and proceed.
+*/
+  
+  struct irec *iface, *found;
+  struct iname *if_tmp;
+  
+  if (!daemon->if_names)
+    return NULL;
+  
+  for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
+    if (if_tmp->name && (!if_tmp->used || strchr(if_tmp->name, '*')))
+      return NULL;
+
+  for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next)
+    if (iface->dhcp_ok)
+      {
+       if (!found)
+         found = iface;
+       else if (strcmp(found->name, iface->name) != 0) 
+         return NULL; /* more than one. */
+      }
+
+  if (found)
+    return found->name;
+
+  return NULL;
+}
+void  bindtodevice(char *device, int fd)
+{
+  struct ifreq ifr;
+  
+  strcpy(ifr.ifr_name, device);
+  /* only allowed by root. */
+  if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) == -1 &&
+      errno != EPERM)
+    die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
+}
+#endif
+
+static const struct opttab_t {
+  char *name;
+  u16 val, size;
+} opttab[] = {
+  { "netmask", 1, OT_ADDR_LIST },
+  { "time-offset", 2, 4 },
+  { "router", 3, OT_ADDR_LIST  },
+  { "dns-server", 6, OT_ADDR_LIST },
+  { "log-server", 7, OT_ADDR_LIST },
+  { "lpr-server", 9, OT_ADDR_LIST },
+  { "hostname", 12, OT_INTERNAL | OT_NAME },
+  { "boot-file-size", 13, 2 | OT_DEC },
+  { "domain-name", 15, OT_NAME },
+  { "swap-server", 16, OT_ADDR_LIST },
+  { "root-path", 17, OT_NAME },
+  { "extension-path", 18, OT_NAME },
+  { "ip-forward-enable", 19, 1 },
+  { "non-local-source-routing", 20, 1 },
+  { "policy-filter", 21, OT_ADDR_LIST },
+  { "max-datagram-reassembly", 22, 2 | OT_DEC },
+  { "default-ttl", 23, 1 | OT_DEC },
+  { "mtu", 26, 2 | OT_DEC },
+  { "all-subnets-local", 27, 1 },
+  { "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
+  { "router-discovery", 31, 1 },
+  { "router-solicitation", 32, OT_ADDR_LIST },
+  { "static-route", 33, OT_ADDR_LIST },
+  { "trailer-encapsulation", 34, 1 },
+  { "arp-timeout", 35, 4 | OT_DEC },
+  { "ethernet-encap", 36, 1 },
+  { "tcp-ttl", 37, 1 },
+  { "tcp-keepalive", 38, 4 | OT_DEC },
+  { "nis-domain", 40, OT_NAME },
+  { "nis-server", 41, OT_ADDR_LIST },
+  { "ntp-server", 42, OT_ADDR_LIST },
+  { "vendor-encap", 43, OT_INTERNAL },
+  { "netbios-ns", 44, OT_ADDR_LIST },
+  { "netbios-dd", 45, OT_ADDR_LIST },
+  { "netbios-nodetype", 46, 1 },
+  { "netbios-scope", 47, 0 },
+  { "x-windows-fs", 48, OT_ADDR_LIST },
+  { "x-windows-dm", 49, OT_ADDR_LIST },
+  { "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
+  { "lease-time", 51, OT_INTERNAL | OT_TIME },
+  { "option-overload", 52, OT_INTERNAL },
+  { "message-type", 53, OT_INTERNAL | OT_DEC },
+  { "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
+  { "parameter-request", 55, OT_INTERNAL },
+  { "message", 56, OT_INTERNAL },
+  { "max-message-size", 57, OT_INTERNAL },
+  { "T1", 58, OT_TIME},
+  { "T2", 59, OT_TIME},
+  { "vendor-class", 60, 0 },
+  { "client-id", 61, OT_INTERNAL },
+  { "nis+-domain", 64, OT_NAME },
+  { "nis+-server", 65, OT_ADDR_LIST },
+  { "tftp-server", 66, OT_NAME },
+  { "bootfile-name", 67, OT_NAME },
+  { "mobile-ip-home", 68, OT_ADDR_LIST }, 
+  { "smtp-server", 69, OT_ADDR_LIST }, 
+  { "pop3-server", 70, OT_ADDR_LIST }, 
+  { "nntp-server", 71, OT_ADDR_LIST }, 
+  { "irc-server", 74, OT_ADDR_LIST }, 
+  { "user-class", 77, 0 },
+  { "FQDN", 81, OT_INTERNAL },
+  { "agent-id", 82, OT_INTERNAL },
+  { "client-arch", 93, 2 | OT_DEC },
+  { "client-interface-id", 94, 0 },
+  { "client-machine-id", 97, 0 },
+  { "subnet-select", 118, OT_INTERNAL },
+  { "domain-search", 119, OT_RFC1035_NAME },
+  { "sip-server", 120, 0 },
+  { "classless-static-route", 121, 0 },
+  { "vendor-id-encap", 125, 0 },
+  { "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
+  { NULL, 0, 0 }
+};
+
+#ifdef HAVE_DHCP6
+static const struct opttab_t opttab6[] = {
+  { "client-id", 1, OT_INTERNAL },
+  { "server-id", 2, OT_INTERNAL },
+  { "ia-na", 3, OT_INTERNAL },
+  { "ia-ta", 4, OT_INTERNAL },
+  { "iaaddr", 5, OT_INTERNAL },
+  { "oro", 6, OT_INTERNAL },
+  { "preference", 7, OT_INTERNAL | OT_DEC },
+  { "unicast", 12, OT_INTERNAL },
+  { "status", 13, OT_INTERNAL },
+  { "rapid-commit", 14, OT_INTERNAL },
+  { "user-class", 15, OT_INTERNAL | OT_CSTRING },
+  { "vendor-class", 16, OT_INTERNAL | OT_CSTRING },
+  { "vendor-opts", 17, OT_INTERNAL },
+  { "sip-server-domain", 21,  OT_RFC1035_NAME },
+  { "sip-server", 22, OT_ADDR_LIST },
+  { "dns-server", 23, OT_ADDR_LIST },
+  { "domain-search", 24, OT_RFC1035_NAME },
+  { "nis-server", 27, OT_ADDR_LIST },
+  { "nis+-server", 28, OT_ADDR_LIST },
+  { "nis-domain", 29,  OT_RFC1035_NAME },
+  { "nis+-domain", 30, OT_RFC1035_NAME },
+  { "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 },
+  { "bootfile-url", 59, OT_NAME },
+  { "bootfile-param", 60, OT_CSTRING },
+  { NULL, 0, 0 }
+};
+#endif
+
+
+
+void display_opts(void)
+{
+  int i;
+  
+  printf(_("Known DHCP options:\n"));
+  
+  for (i = 0; opttab[i].name; i++)
+    if (!(opttab[i].size & OT_INTERNAL))
+      printf("%3d %s\n", opttab[i].val, opttab[i].name);
+}
+
+#ifdef HAVE_DHCP6
+void display_opts6(void)
+{
+  int i;
+  printf(_("Known DHCPv6 options:\n"));
+  
+  for (i = 0; opttab6[i].name; i++)
+    if (!(opttab6[i].size & OT_INTERNAL))
+      printf("%3d %s\n", opttab6[i].val, opttab6[i].name);
+}
+#endif
+
+int lookup_dhcp_opt(int prot, char *name)
+{
+  const struct opttab_t *t;
+  int i;
+
+  (void)prot;
+
+#ifdef HAVE_DHCP6
+  if (prot == AF_INET6)
+    t = opttab6;
+  else
+#endif
+    t = opttab;
+
+  for (i = 0; t[i].name; i++)
+    if (strcasecmp(t[i].name, name) == 0)
+      return t[i].val;
+  
+  return -1;
+}
+
+int lookup_dhcp_len(int prot, int val)
+{
+  const struct opttab_t *t;
+  int i;
+
+  (void)prot;
+
+#ifdef HAVE_DHCP6
+  if (prot == AF_INET6)
+    t = opttab6;
+  else
+#endif
+    t = opttab;
+
+  for (i = 0; t[i].name; i++)
+    if (val == t[i].val)
+      return t[i].size & ~OT_DEC;
+
+   return 0;
+}
+
+char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
+{
+  int o, i, j, nodecode = 0;
+  const struct opttab_t *ot = opttab;
+
+#ifdef HAVE_DHCP6
+  if (prot == AF_INET6)
+    ot = opttab6;
+#endif
+
+  for (o = 0; ot[o].name; o++)
+    if (ot[o].val == opt)
+      {
+       if (buf)
+         {
+           memset(buf, 0, buf_len);
+           
+           if (ot[o].size & OT_ADDR_LIST) 
+             {
+               struct all_addr addr;
+               int addr_len = INADDRSZ;
+
+#ifdef HAVE_DHCP6
+               if (prot == AF_INET6)
+                 addr_len = IN6ADDRSZ;
+#endif
+               for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len) 
+                 {
+                   if (i != 0)
+                     strncat(buf, ", ", buf_len - strlen(buf));
+                   /* align */
+                   memcpy(&addr, &val[i], addr_len); 
+                   inet_ntop(prot, &val[i], daemon->addrbuff, ADDRSTRLEN);
+                   strncat(buf, daemon->addrbuff, buf_len - strlen(buf));
+                 }
+             }
+           else if (ot[o].size & OT_NAME)
+               for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
+                 {
+                   char c = val[i];
+                   if (isprint((int)c))
+                     buf[j++] = c;
+                 }
+#ifdef HAVE_DHCP6
+           /* We don't handle compressed rfc1035 names, so no good in IPv4 land */
+           else if ((ot[o].size & OT_RFC1035_NAME) && prot == AF_INET6)
+             {
+               i = 0, j = 0;
+               while (i < opt_len && val[i] != 0)
+                 {
+                   int k, l = i + val[i] + 1;
+                   for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++)
+                    {
+                      char c = val[k];
+                      if (isprint((int)c))
+                        buf[j++] = c;
+                    }
+                   i = l;
+                   if (val[i] != 0 && j < buf_len)
+                     buf[j++] = '.';
+                 }
+             }
+           else if ((ot[o].size & OT_CSTRING))
+             {
+               int k, len;
+               unsigned char *p;
+
+               i = 0, j = 0;
+               while (1)
+                 {
+                   p = &val[i];
+                   GETSHORT(len, p);
+                   for (k = 0; k < len && j < buf_len; k++)
+                     {
+                      char c = *p++;
+                      if (isprint((int)c))
+                        buf[j++] = c;
+                    }
+                   i += len +2;
+                   if (i >= opt_len)
+                     break;
+
+                   if (j < buf_len)
+                     buf[j++] = ',';
+                 }
+             }       
+#endif
+           else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
+             {
+               unsigned int dec = 0;
+               
+               for (i = 0; i < opt_len; i++)
+                 dec = (dec << 8) | val[i]; 
+
+               if (ot[o].size & OT_TIME)
+                 prettyprint_time(buf, dec);
+               else
+                 sprintf(buf, "%u", dec);
+             }
+           else
+             nodecode = 1;
+         }
+       break;
+      }
+
+  if (opt_len != 0 && buf && (!ot[o].name || nodecode))
+    {
+      int trunc  = 0;
+      if (opt_len > 14)
+       {
+         trunc = 1;
+         opt_len = 14;
+       }
+      print_mac(buf, val, opt_len);
+      if (trunc)
+       strncat(buf, "...", buf_len - strlen(buf));
+    
+
+    }
+
+  return ot[o].name ? ot[o].name : "";
+
+}
+
+void log_context(int family, struct dhcp_context *context)
+{
+  /* Cannot use dhcp_buff* for RA contexts */
+
+  void *start = &context->start;
+  void *end = &context->end;
+  char *template = "", *p = daemon->namebuff;
+  
+  *p = 0;
+    
+#ifdef HAVE_DHCP6
+  if (family == AF_INET6)
+    {
+      struct in6_addr subnet = context->start6;
+      if (!(context->flags & CONTEXT_TEMPLATE))
+       setaddr6part(&subnet, 0);
+      inet_ntop(AF_INET6, &subnet, daemon->addrbuff, ADDRSTRLEN); 
+      start = &context->start6;
+      end = &context->end6;
+    }
+#endif
+
+  if (family != AF_INET && (context->flags & CONTEXT_DEPRECATE))
+    strcpy(daemon->namebuff, _(", prefix deprecated"));
+  else
+    {
+      p += sprintf(p, _(", lease time "));
+      prettyprint_time(p, context->lease_time);
+      p += strlen(p);
+    }  
+
+#ifdef HAVE_DHCP6
+  if (context->flags & CONTEXT_CONSTRUCTED)
+    {
+      char ifrn_name[IFNAMSIZ];
+      
+      template = p;
+      p += sprintf(p, ", ");
+      
+      if (indextoname(daemon->icmp6fd, context->if_index, ifrn_name))
+       sprintf(p, "%s for %s", (context->flags & CONTEXT_OLD) ? "old prefix" : "constructed", ifrn_name);
+    }
+  else if (context->flags & CONTEXT_TEMPLATE && !(context->flags & CONTEXT_RA_STATELESS))
+    {
+      template = p;
+      p += sprintf(p, ", ");
+      
+      sprintf(p, "template for %s", context->template_interface);  
+    }
+#endif
+     
+  if (!(context->flags & CONTEXT_OLD) &&
+      ((context->flags & CONTEXT_DHCP) || family == AF_INET)) 
+    {
+#ifdef HAVE_DHCP6
+      if (context->flags & CONTEXT_RA_STATELESS)
+       {
+         if (context->flags & CONTEXT_TEMPLATE)
+           strncpy(daemon->dhcp_buff, context->template_interface, 256);
+         else
+           strcpy(daemon->dhcp_buff, daemon->addrbuff);
+       }
+      else 
+#endif
+       inet_ntop(family, start, daemon->dhcp_buff, 256);
+      inet_ntop(family, end, daemon->dhcp_buff3, 256);
+      my_syslog(MS_DHCP | LOG_INFO, 
+               (context->flags & CONTEXT_RA_STATELESS) ? 
+               _("%s stateless on %s%.0s%.0s%s") :
+               (context->flags & CONTEXT_STATIC) ? 
+               _("%s, static leases only on %.0s%s%s%.0s") :
+               (context->flags & CONTEXT_PROXY) ?
+               _("%s, proxy on subnet %.0s%s%.0s%.0s") :
+               _("%s, IP range %s -- %s%s%.0s"),
+               (family != AF_INET) ? "DHCPv6" : "DHCP",
+               daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff, template);
+    }
+  
+#ifdef HAVE_DHCP6
+  if (context->flags & CONTEXT_TEMPLATE)
+    {
+      strcpy(daemon->addrbuff, context->template_interface);
+      template = "";
+    }
+
+  if ((context->flags & CONTEXT_RA_NAME) && !(context->flags & CONTEXT_OLD))
+    my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s%s"), daemon->addrbuff, template);
+  
+  if ((context->flags & CONTEXT_RA) || (option_bool(OPT_RA) && (context->flags & CONTEXT_DHCP) && family == AF_INET6)) 
+    my_syslog(MS_DHCP | LOG_INFO, _("router advertisement on %s%s"), daemon->addrbuff, template);
+#endif
+
+}
+
+void log_relay(int family, struct dhcp_relay *relay)
+{
+  inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN);
+  inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN); 
+
+  if (relay->interface)
+    my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
+  else
+    my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff);
+}
+   
+#endif
similarity index 94%
rename from src/dhcp_protocol.h
rename to src/dhcp-protocol.h
index e09cad8..701b6cb 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
+#define DHCP_SERVER_PORT 67
+#define DHCP_CLIENT_PORT 68
+#define DHCP_SERVER_ALTPORT 1067
+#define DHCP_CLIENT_ALTPORT 1068
+#define PXE_PORT 4011
 
 #define BOOTREQUEST              1
 #define BOOTREPLY                2
index 29ddf24..e6fceb1 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
 #ifdef HAVE_DHCP
 
 struct iface_param {
-  struct in_addr relay, primary;
   struct dhcp_context *current;
+  struct dhcp_relay *relay;
+  struct in_addr relay_local;
   int ind;
 };
 
-static int complete_context(struct in_addr local, int if_index, 
+struct match_param {
+  int ind, matched;
+  struct in_addr netmask, broadcast, addr;
+};
+
+static int complete_context(struct in_addr local, int if_index, char *label,
                            struct in_addr netmask, struct in_addr broadcast, void *vparam);
+static int check_listen_addrs(struct in_addr local, int if_index, char *label,
+                             struct in_addr netmask, struct in_addr broadcast, void *vparam);
+static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index);
+static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);
 
 static int make_fd(int port)
 {
@@ -35,16 +45,22 @@ static int make_fd(int port)
 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
   int mtu = IP_PMTUDISC_DONT;
 #endif
+#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
+  int tos = IPTOS_CLASS_CS6;
+#endif
 
   if (fd == -1)
     die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET);
   
   if (!fix_fd(fd) ||
 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
-      setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
+      setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
+#endif
+#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
+      setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1 ||
 #endif
 #if defined(HAVE_LINUX_NETWORK)
-      setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
+      setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
 #else
       setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
 #endif
@@ -53,14 +69,22 @@ static int make_fd(int port)
   
   /* When bind-interfaces is set, there might be more than one dnmsasq
      instance binding port 67. That's OK if they serve different networks.
-     Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD. */
-  if (option_bool(OPT_NOWILD))
+     Need to set REUSEADDR|REUSEPORT to make this posible.
+     Handle the case that REUSEPORT is defined, but the kernel doesn't 
+     support it. This handles the introduction of REUSEPORT on Linux. */
+  if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
     {
+      int rc = 0;
+
 #ifdef SO_REUSEPORT
-      int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt));
-#else
-      int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
+      if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 && 
+         errno == ENOPROTOOPT)
+       rc = 0;
 #endif
+      
+      if (rc != -1)
+       rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
+      
       if (rc == -1)
        die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
     }
@@ -104,19 +128,16 @@ void dhcp_init(void)
   
   /* Make BPF raw send socket */
   init_bpf();
-#endif
-  
-  check_dhcp_hosts(1);
-    
-  daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet); 
-  daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
+#endif  
 }
-  
+
 void dhcp_packet(time_t now, int pxe_fd)
 {
   int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
   struct dhcp_packet *mess;
   struct dhcp_context *context;
+  struct dhcp_relay *relay;
+  int is_relay_reply = 0;
   struct iname *tmp;
   struct ifreq ifr;
   struct msghdr msg;
@@ -125,7 +146,7 @@ void dhcp_packet(time_t now, int pxe_fd)
   struct iovec iov;
   ssize_t sz; 
   int iface_index = 0, unicast_dest = 0, is_inform = 0;
-  struct in_addr iface_addr, *addrp = NULL;
+  struct in_addr iface_addr;
   struct iface_param parm;
 #ifdef HAVE_LINUX_NETWORK
   struct arpreq arp_req;
@@ -141,56 +162,23 @@ void dhcp_packet(time_t now, int pxe_fd)
     char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
 #endif
   } control_u;
-  
-  msg.msg_control = NULL;
-  msg.msg_controllen = 0;
-  msg.msg_name = NULL;
-  msg.msg_namelen = 0;
-  msg.msg_iov = &daemon->dhcp_packet;
-  msg.msg_iovlen = 1;
-  
-  while (1)
-    {
-      msg.msg_flags = 0;
-      while ((sz = recvmsg(fd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
-      
-      if (sz == -1)
-       return;
-      
-      if (!(msg.msg_flags & MSG_TRUNC))
-       break;
+  struct dhcp_bridge *bridge, *alias;
 
-      /* Very new Linux kernels return the actual size needed, 
-        older ones always return truncated size */
-      if ((size_t)sz == daemon->dhcp_packet.iov_len)
-       {
-         if (!expand_buf(&daemon->dhcp_packet, sz + 100))
-           return;
-       }
-      else
-       {
-         expand_buf(&daemon->dhcp_packet, sz);
-         break;
-       }
-    }
-  
-  /* expand_buf may have moved buffer */
-  mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
   msg.msg_controllen = sizeof(control_u);
   msg.msg_control = control_u.control;
-  msg.msg_flags = 0;
   msg.msg_name = &dest;
   msg.msg_namelen = sizeof(dest);
-
-  while ((sz = recvmsg(fd, &msg, 0)) == -1 && errno == EINTR);
-  if ((msg.msg_flags & MSG_TRUNC) || sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
-    return;
+  msg.msg_iov = &daemon->dhcp_packet;
+  msg.msg_iovlen = 1;
   
-#if defined (HAVE_LINUX_NETWORK)
+  if ((sz = recv_dhcp_packet(fd, &msg)) == -1 || 
+      (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))) 
+    return;
+    
+  #if defined (HAVE_LINUX_NETWORK)
   if (msg.msg_controllen >= sizeof(struct cmsghdr))
     for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
-      if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
+      if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
        {
          union {
            unsigned char *c;
@@ -237,74 +225,120 @@ void dhcp_packet(time_t now, int pxe_fd)
   strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
 #endif 
 
+  /* 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)
+       if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
+         {
+           if (!(iface_index = if_nametoindex(bridge->iface)))
+             {
+               my_syslog(MS_DHCP | LOG_WARNING,
+                         _("unknown interface %s in bridge-interface"),
+                         bridge->iface);
+               return;
+             }
+           else 
+             {
+               strncpy(ifr.ifr_name,  bridge->iface, IF_NAMESIZE);
+               break;
+             }
+         }
+      
+      if (alias)
+       break;
+    }
+
 #ifdef MSG_BCAST
   /* OpenBSD tells us when a packet was broadcast */
   if (!(msg.msg_flags & MSG_BCAST))
     unicast_dest = 1;
 #endif
-
-  ifr.ifr_addr.sa_family = AF_INET;
-  if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
-    {
-      addrp = &iface_addr;
-      iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
-    }
-
-  if (!iface_check(AF_INET, (struct all_addr *)addrp, ifr.ifr_name, &iface_index))
-    return;
   
-  for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
-    if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
-      return;
-
-  /* weird libvirt-inspired access control */
-  for (context = daemon->dhcp; context; context = context->next)
-    if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0)
-      break;
-
-  if (!context)
-    return;
-  
-  /* unlinked contexts are marked by context->current == context */
-  for (context = daemon->dhcp; context; context = context->next)
-    context->current = context;
-  
-  parm.relay = mess->giaddr;
-  parm.primary = iface_addr;
-  parm.current = NULL;
-  parm.ind = iface_index;
-
-    /* interface may have been changed by alias in iface_check, make sure it gets priority in case
-       there is more than one address on the interface in the same subnet */
-  if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1)
+  if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name)))
     {
-      my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
-      return;
+      /* Reply from server, using us as relay. */
+      iface_index = relay->iface_index;
+      if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
+       return;
+      is_relay_reply = 1; 
+      iov.iov_len = sz;
+#ifdef HAVE_LINUX_NETWORK
+      strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
+#endif 
     }
   else
     {
-      iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
-      if (ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) != -1)
+      ifr.ifr_addr.sa_family = AF_INET;
+      if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
+       iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+      else
        {
-         struct in_addr netmask =  ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
-         if (ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) != -1)
-           {
-             struct in_addr broadcast =  ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
-             complete_context(iface_addr, iface_index, netmask, broadcast, &parm);
-           }
+         my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
+         return;
        }
-    } 
+      
+      for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
+       if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
+         return;
+      
+      /* unlinked contexts/relays are marked by context->current == context */
+      for (context = daemon->dhcp; context; context = context->next)
+       context->current = context;
+      
+      for (relay = daemon->relay4; relay; relay = relay->next)
+       relay->current = relay;
+      
+      parm.current = NULL;
+      parm.relay = NULL;
+      parm.relay_local.s_addr = 0;
+      parm.ind = iface_index;
+      
+      if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
+       {
+         /* If we failed to match the primary address of the interface, see if we've got a --listen-address
+            for a secondary */
+         struct match_param match;
+         
+         match.matched = 0;
+         match.ind = iface_index;
+         
+         if (!daemon->if_addrs ||
+             !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
+             !match.matched)
+           return;
+         
+         iface_addr = match.addr;
+         /* make sure secondary address gets priority in case
+            there is more than one address on the interface in the same subnet */
+         complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
+       }    
+      
+      if (!iface_enumerate(AF_INET, &parm, complete_context))
+       return;
 
-  if (!iface_enumerate(AF_INET, &parm, complete_context))
-    return;
-  lease_prune(NULL, now); /* lose any expired leases */
-  iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, 
-                          now, unicast_dest, &is_inform, pxe_fd);
-  lease_update_file(now);
-  lease_update_dns();
-    
-  if (iov.iov_len == 0)
-    return;
+      /* We're relaying this request */
+      if  (parm.relay_local.s_addr != 0 &&
+          relay_upstream4(parm.relay, (struct dhcp_packet *)daemon->dhcp_packet.iov_base, (size_t)sz, iface_index))
+       return;
+
+      /* May have configured relay, but not DHCP server */
+      if (!daemon->dhcp)
+       return;
+
+      lease_prune(NULL, now); /* lose any expired leases */
+      iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, 
+                              now, unicast_dest, &is_inform, pxe_fd, iface_addr);
+      lease_update_file(now);
+      lease_update_dns(0);
+      
+      if (iov.iov_len == 0)
+       return;
+    }
   
   msg.msg_name = &dest;
   msg.msg_namelen = sizeof(dest);
@@ -319,13 +353,13 @@ void dhcp_packet(time_t now, int pxe_fd)
 #ifdef HAVE_SOCKADDR_SA_LEN
   dest.sin_len = sizeof(struct sockaddr_in);
 #endif
-     
+  
   if (pxe_fd)
     { 
       if (mess->ciaddr.s_addr != 0)
        dest.sin_addr = mess->ciaddr;
     }
-  else if (mess->giaddr.s_addr)
+  else if (mess->giaddr.s_addr && !is_relay_reply)
     {
       /* Send to BOOTP relay  */
       dest.sin_port = htons(daemon->dhcp_server_port);
@@ -338,17 +372,16 @@ void dhcp_packet(time_t now, int pxe_fd)
         source port too, and send back to that.  If we're replying 
         to a DHCPINFORM, trust the source address always. */
       if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
-         dest.sin_port == 0 || dest.sin_addr.s_addr == 0)
+         dest.sin_port == 0 || dest.sin_addr.s_addr == 0 || is_relay_reply)
        {
          dest.sin_port = htons(daemon->dhcp_client_port); 
          dest.sin_addr = mess->ciaddr;
        }
     } 
 #if defined(HAVE_LINUX_NETWORK)
-  else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
-          mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
+  else
     {
-      /* broadcast to 255.255.255.255 (or mac address invalid) */
+      /* fill cmsg for outbound interface (both broadcast & unicast) */
       struct in_pktinfo *pkt;
       msg.msg_control = control_u.control;
       msg.msg_controllen = sizeof(control_u);
@@ -357,23 +390,30 @@ void dhcp_packet(time_t now, int pxe_fd)
       pkt->ipi_ifindex = iface_index;
       pkt->ipi_spec_dst.s_addr = 0;
       msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
-      cmptr->cmsg_level = SOL_IP;
-      cmptr->cmsg_type = IP_PKTINFO;  
-      dest.sin_addr.s_addr = INADDR_BROADCAST;
-      dest.sin_port = htons(daemon->dhcp_client_port);
-    }
-  else
-    {
-      /* unicast to unconfigured client. Inject mac address direct into ARP cache. 
-        struct sockaddr limits size to 14 bytes. */
-      dest.sin_addr = mess->yiaddr;
-      dest.sin_port = htons(daemon->dhcp_client_port);
-      memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
-      arp_req.arp_ha.sa_family = mess->htype;
-      memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
-      /* interface name already copied in */
-      arp_req.arp_flags = ATF_COM;
-      ioctl(daemon->dhcpfd, SIOCSARP, &arp_req);
+      cmptr->cmsg_level = IPPROTO_IP;
+      cmptr->cmsg_type = IP_PKTINFO;
+
+      if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
+         mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
+        {
+          /* broadcast to 255.255.255.255 (or mac address invalid) */
+          dest.sin_addr.s_addr = INADDR_BROADCAST;
+          dest.sin_port = htons(daemon->dhcp_client_port);
+        }
+      else
+        {
+          /* unicast to unconfigured client. Inject mac address direct into ARP cache.
+          struct sockaddr limits size to 14 bytes. */
+          dest.sin_addr = mess->yiaddr;
+          dest.sin_port = htons(daemon->dhcp_client_port);
+          memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
+          arp_req.arp_ha.sa_family = mess->htype;
+          memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
+          /* interface name already copied in */
+          arp_req.arp_flags = ATF_COM;
+          if (ioctl(daemon->dhcpfd, SIOCSARP, &arp_req) == -1)
+            my_syslog(MS_DHCP | LOG_ERR, _("ARP-cache injection failed: %s"), strerror(errno));
+        }
     }
 #elif defined(HAVE_SOLARIS_NETWORK)
   else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
@@ -411,9 +451,35 @@ void dhcp_packet(time_t now, int pxe_fd)
   setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
 #endif
   
-  while(sendmsg(fd, &msg, 0) == -1 && retry_send());
+  while(retry_send(sendmsg(fd, &msg, 0)));
 }
  
+/* 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)
+{
+  struct match_param *param = vparam;
+  struct iname *tmp;
+
+  (void) label;
+
+  if (if_index == param->ind)
+    {
+      for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
+       if ( tmp->addr.sa.sa_family == AF_INET &&
+            tmp->addr.in.sin_addr.s_addr == local.s_addr)
+         {
+           param->matched = 1;
+           param->addr = local;
+           param->netmask = netmask;
+           param->broadcast = broadcast;
+           break;
+         }
+    }
+  
+  return 1;
+}
+
 /* This is a complex routine: it gets called with each (address,netmask,broadcast) triple 
    of each interface (and any relay address) and does the  following things:
 
@@ -424,11 +490,14 @@ void dhcp_packet(time_t now, int pxe_fd)
 
    Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
 
-static int complete_context(struct in_addr local, int if_index, 
+static int complete_context(struct in_addr local, int if_index, char *label,
                            struct in_addr netmask, struct in_addr broadcast, void *vparam)
 {
   struct dhcp_context *context;
+  struct dhcp_relay *relay;
   struct iface_param *param = vparam;
+
+  (void)label;
   
   for (context = daemon->dhcp; context; context = context->next)
     {
@@ -448,40 +517,38 @@ static int complete_context(struct in_addr local, int if_index,
        context->netmask = netmask;
       }
       
-      if (context->netmask.s_addr)
+      if (context->netmask.s_addr != 0 &&
+         is_same_net(local, context->start, context->netmask) &&
+         is_same_net(local, context->end, context->netmask))
        {
-         if (is_same_net(local, context->start, context->netmask) &&
-             is_same_net(local, context->end, context->netmask))
+         /* link it onto the current chain if we've not seen it before */
+         if (if_index == param->ind && context->current == context)
            {
-             /* link it onto the current chain if we've not seen it before */
-             if (if_index == param->ind && context->current == context)
-               {
-                 context->router = local;
-                 context->local = local;
-                 context->current = param->current;
-                 param->current = context;
-               }
-             
-             if (!(context->flags & CONTEXT_BRDCAST))
-               {
-                 if (is_same_net(broadcast, context->start, context->netmask))
-                   context->broadcast = broadcast;
-                 else 
-                   context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
-               }
-           }   
-         else if (param->relay.s_addr && is_same_net(param->relay, context->start, context->netmask))
+             context->router = local;
+             context->local = local;
+             context->current = param->current;
+             param->current = context;
+           }
+         
+         if (!(context->flags & CONTEXT_BRDCAST))
            {
-             context->router = param->relay;
-             context->local = param->primary;
-             /* fill in missing broadcast addresses for relayed ranges */
-             if (!(context->flags & CONTEXT_BRDCAST))
+             if (is_same_net(broadcast, context->start, context->netmask))
+               context->broadcast = broadcast;
+             else 
                context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
            }
-
-       }
+       }               
     }
 
+  for (relay = daemon->relay4; relay; relay = relay->next)
+    if (if_index == param->ind && relay->local.addr.addr4.s_addr == local.s_addr && relay->current == relay &&
+       (param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr))
+      {
+       relay->current = param->relay;
+       param->relay = relay;
+       param->relay_local = local;     
+      }
+
   return 1;
 }
          
@@ -505,7 +572,7 @@ struct dhcp_context *address_available(struct dhcp_context *context,
       start = ntohl(tmp->start.s_addr);
       end = ntohl(tmp->end.s_addr);
 
-      if (!(tmp->flags & CONTEXT_STATIC) &&
+      if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
          addr >= start &&
          addr <= end &&
          match_netid(tmp->filter, netids, 1))
@@ -540,7 +607,8 @@ struct dhcp_context *narrow_context(struct dhcp_context *context,
       if (!tmp)
        for (tmp = context; tmp; tmp = tmp->current)
          if (match_netid(tmp->filter, netids, 1) &&
-             is_same_net(taddr, tmp->start, tmp->netmask))
+             is_same_net(taddr, tmp->start, tmp->netmask) &&
+             !(tmp->flags & CONTEXT_PROXY))
            break;
     }
   
@@ -562,50 +630,6 @@ struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct i
   return NULL;
 }
 
-/* Is every member of check matched by a member of pool? 
-   If tagnotneeded, untagged is OK */
-int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
-{
-  struct dhcp_netid *tmp1;
-  
-  if (!check && !tagnotneeded)
-    return 0;
-
-  for (; check; check = check->next)
-    {
-      /* '#' for not is for backwards compat. */
-      if (check->net[0] != '!' && check->net[0] != '#')
-       {
-         for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
-           if (strcmp(check->net, tmp1->net) == 0)
-             break;
-         if (!tmp1)
-           return 0;
-       }
-      else
-       for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
-         if (strcmp((check->net)+1, tmp1->net) == 0)
-           return 0;
-    }
-  return 1;
-}
-
-struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
-{
-  struct tag_if *exprs;
-  struct dhcp_netid_list *list;
-
-  for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
-    if (match_netid(exprs->tag, tags, 1))
-      for (list = exprs->set; list; list = list->next)
-       {
-         list->list->next = tags;
-         tags = list->list;
-       }
-
-  return tags;
-}
-
 int address_allocate(struct dhcp_context *context,
                     struct in_addr *addrp, unsigned char *hwaddr, int hw_len, 
                     struct dhcp_netid *netids, time_t now)   
@@ -626,16 +650,22 @@ int address_allocate(struct dhcp_context *context,
   
   for (pass = 0; pass <= 1; pass++)
     for (c = context; c; c = c->current)
-      if (c->flags & CONTEXT_STATIC)
+      if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
        continue;
       else if (!match_netid(c->filter, netids, pass))
        continue;
       else
        {
-         /* pick a seed based on hwaddr then iterate until we find a free address. */
-         start.s_addr = addr.s_addr = 
-           htonl(ntohl(c->start.s_addr) + 
-                 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
+         if (option_bool(OPT_CONSEC_ADDR))
+           /* seed is largest extant lease addr in this context */
+           start = lease_find_max_addr(c);
+         else
+           /* pick a seed based on hwaddr */
+           start.s_addr = htonl(ntohl(c->start.s_addr) + 
+                                ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
+
+         /* iterate until we find a free address. */
+         addr = start;
          
          do {
            /* eliminate addresses in use by the server. */
@@ -660,9 +690,6 @@ int address_allocate(struct dhcp_context *context,
                
                *addrp = addr;
 
-               if (option_bool(OPT_NO_PING))
-                 return 1;
-               
                /* check if we failed to ping addr sometime in the last
                   PING_CACHE_TIME seconds. If so, assume the same situation still exists.
                   This avoids problems when a stupid client bangs
@@ -672,33 +699,51 @@ int address_allocate(struct dhcp_context *context,
                for (count = 0, r = daemon->ping_results; r; r = r->next)
                  if (difftime(now, r->time) >  (float)PING_CACHE_TIME)
                    victim = r; /* old record */
-                 else if (++count == max || r->addr.s_addr == addr.s_addr)
-                   return 1;
-                   
-               if (icmp_ping(addr))
-                 /* address in use: perturb address selection so that we are
-                    less likely to try this address again. */
-                 c->addr_epoch++;
-               else
+                 else 
+                   {
+                     count++;
+                     if (r->addr.s_addr == addr.s_addr)
+                       {
+                         /* consec-ip mode: we offered this address for another client
+                            (different hash) recently, don't offer it to this one. */
+                         if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
+                           break;
+                         
+                         return 1;
+                       }
+                   }
+
+               if (!r) 
                  {
-                   /* at this point victim may hold an expired record */
-                   if (!victim)
+                   if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
                      {
-                       if ((victim = whine_malloc(sizeof(struct ping_result))))
-                         {
-                           victim->next = daemon->ping_results;
-                           daemon->ping_results = victim;
-                         }
+                       /* address in use: perturb address selection so that we are
+                          less likely to try this address again. */
+                       if (!option_bool(OPT_CONSEC_ADDR))
+                         c->addr_epoch++;
                      }
-                   
-                   /* record that this address is OK for 30s 
-                      without more ping checks */
-                   if (victim)
+                   else
                      {
-                       victim->addr = addr;
-                       victim->time = now;
+                       /* at this point victim may hold an expired record */
+                       if (!victim)
+                         {
+                           if ((victim = whine_malloc(sizeof(struct ping_result))))
+                             {
+                               victim->next = daemon->ping_results;
+                               daemon->ping_results = victim;
+                             }
+                         }
+                       
+                       /* record that this address is OK for 30s 
+                          without more ping checks */
+                       if (victim)
+                         {
+                           victim->addr = addr;
+                           victim->time = now;
+                           victim->hash = j;
+                         }
+                       return 1;
                      }
-                   return 1;
                  }
              }
 
@@ -709,92 +754,10 @@ int address_allocate(struct dhcp_context *context,
            
          } while (addr.s_addr != start.s_addr);
        }
-  return 0;
-}
-
-static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
-{
-  if (!context) /* called via find_config() from lease_update_from_configs() */
-    return 1; 
-  if (!(config->flags & CONFIG_ADDR))
-    return 1;
-  for (; context; context = context->current)
-    if (is_same_net(config->addr, context->start, context->netmask))
-      return 1;
-  
-  return 0;
-}
 
-int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
-{
-  struct hwaddr_config *conf_addr;
-  
-  for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
-    if (conf_addr->wildcard_mask == 0 &&
-       conf_addr->hwaddr_len == len &&
-       (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
-       memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
-      return 1;
-  
   return 0;
 }
 
-struct dhcp_config *find_config(struct dhcp_config *configs,
-                               struct dhcp_context *context,
-                               unsigned char *clid, int clid_len,
-                               unsigned char *hwaddr, int hw_len, 
-                               int hw_type, char *hostname)
-{
-  int count, new;
-  struct dhcp_config *config, *candidate; 
-  struct hwaddr_config *conf_addr;
-
-  if (clid)
-    for (config = configs; config; config = config->next)
-      if (config->flags & CONFIG_CLID)
-       {
-         if (config->clid_len == clid_len && 
-             memcmp(config->clid, clid, clid_len) == 0 &&
-             is_addr_in_context(context, config))
-           return config;
-         
-         /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
-            cope with that here */
-         if (*clid == 0 && config->clid_len == clid_len-1  &&
-             memcmp(config->clid, clid+1, clid_len-1) == 0 &&
-             is_addr_in_context(context, config))
-           return config;
-       }
-  
-
-  for (config = configs; config; config = config->next)
-    if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
-       is_addr_in_context(context, config))
-      return config;
-  
-  if (hostname && context)
-    for (config = configs; config; config = config->next)
-      if ((config->flags & CONFIG_NAME) && 
-         hostname_isequal(config->hostname, hostname) &&
-         is_addr_in_context(context, config))
-       return config;
-
-  /* use match with fewest wildcast octets */
-  for (candidate = NULL, count = 0, config = configs; config; config = config->next)
-    if (is_addr_in_context(context, config))
-      for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
-       if (conf_addr->wildcard_mask != 0 &&
-           conf_addr->hwaddr_len == hw_len &&  
-           (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
-           (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
-         {
-           count = new;
-           candidate = config;
-         }
-
-  return candidate;
-}
-
 void dhcp_read_ethers(void)
 {
   FILE *f = fopen(ETHERSFILE, "r");
@@ -956,85 +919,6 @@ void dhcp_read_ethers(void)
   my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
 }
 
-void check_dhcp_hosts(int fatal)
-{
-  /* If the same IP appears in more than one host config, then DISCOVER
-     for one of the hosts will get the address, but REQUEST will be NAKed,
-     since the address is reserved by the other one -> protocol loop. 
-     Also check that FQDNs match the domain we are using. */
-  
-  struct dhcp_config *configs, *cp;
-  for (configs = daemon->dhcp_conf; configs; configs = configs->next)
-    {
-      char *domain;
-
-      if ((configs->flags & DHOPT_BANK) || fatal)
-       {
-        for (cp = configs->next; cp; cp = cp->next)
-          if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
-            {
-              if (fatal)
-                die(_("duplicate IP address %s in dhcp-config directive."), 
-                    inet_ntoa(cp->addr), EC_BADCONF);
-              else
-                my_syslog(MS_DHCP | LOG_ERR, _("duplicate IP address %s in %s."), 
-                          inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
-              configs->flags &= ~CONFIG_ADDR;
-            }
-        
-        /* split off domain part */
-        if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
-          configs->domain = domain;
-       }
-    }
-}
-
-void dhcp_update_configs(struct dhcp_config *configs)
-{
-  /* Some people like to keep all static IP addresses in /etc/hosts.
-     This goes through /etc/hosts and sets static addresses for any DHCP config
-     records which don't have an address and whose name matches. 
-     We take care to maintain the invariant that any IP address can appear
-     in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP, 
-     restore the status-quo ante first. */
-  
-  struct dhcp_config *config;
-  struct crec *crec;
-
-  for (config = configs; config; config = config->next)
-    if (config->flags & CONFIG_ADDR_HOSTS)
-      config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
-  
-  
-  if (daemon->port != 0)
-    for (config = configs; config; config = config->next)
-      if (!(config->flags & CONFIG_ADDR) &&
-         (config->flags & CONFIG_NAME) && 
-         (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
-         (crec->flags & F_HOSTS))
-       {
-         if (cache_find_by_name(crec, config->hostname, 0, F_IPV4))
-           {
-             /* use primary (first) address */
-             while (crec && !(crec->flags & F_REVERSE))
-               crec = cache_find_by_name(crec, config->hostname, 0, F_IPV4);
-             if (!crec)
-               continue; /* should be never */
-             my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"), 
-                       config->hostname, inet_ntoa(crec->addr.addr.addr.addr4));
-           }
-
-         if (config_find_by_address(configs, crec->addr.addr.addr.addr4))
-           my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"), 
-                     inet_ntoa(crec->addr.addr.addr.addr4), config->hostname);
-         else 
-           {
-             config->addr = crec->addr.addr.addr.addr4;
-             config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
-           }
-       }
-}
 
 /* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
    for this address. If it has a domain part, that must match the set domain and
@@ -1075,20 +959,74 @@ char *host_from_dns(struct in_addr addr)
   return NULL;
 }
 
-/* return domain or NULL if none. */
-char *strip_hostname(char *hostname)
+static int  relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index)
 {
-  char *dot = strchr(hostname, '.');
-  if (!dot)
-    return NULL;
+  /* ->local is same value for all relays on ->current chain */
+  struct all_addr from;
   
-  *dot = 0; /* truncate */
-  if (strlen(dot+1) != 0)
-    return dot+1;
+  if (mess->op != BOOTREQUEST)
+    return 0;
+
+  /* source address == relay address */
+  from.addr.addr4 = relay->local.addr.addr4;
   
-  return NULL;
+  /* already gatewayed ? */
+  if (mess->giaddr.s_addr)
+    {
+      /* if so check if by us, to stomp on loops. */
+      if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)
+       return 1;
+    }
+  else
+    {
+      /* plug in our address */
+      mess->giaddr.s_addr = relay->local.addr.addr4.s_addr;
+    }
+
+  if ((mess->hops++) > 20)
+    return 1;
+
+  for (; relay; relay = relay->current)
+    {
+      union mysockaddr to;
+      
+      to.sa.sa_family = AF_INET;
+      to.in.sin_addr = relay->server.addr.addr4;
+      to.in.sin_port = htons(daemon->dhcp_server_port);
+      
+      send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
+      
+      if (option_bool(OPT_LOG_OPTS))
+       {
+         inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
+         my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, inet_ntoa(relay->server.addr.addr4));
+       }
+      
+      /* Save this for replies */
+      relay->iface_index = iface_index;
+    }
+  
+  return 1;
 }
 
-#endif
 
+static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
+{
+  struct dhcp_relay *relay;
+
+  if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
+    return NULL;
+
+  for (relay = daemon->relay4; relay; relay = relay->next)
+    {
+      if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)
+       {
+         if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
+           return relay->iface_index != 0 ? relay : NULL;
+       }
+    }
+  
+  return NULL;  
+}     
+
+#endif
diff --git a/src/dhcp6-protocol.h b/src/dhcp6-protocol.h
new file mode 100644 (file)
index 0000000..928a2fa
--- /dev/null
@@ -0,0 +1,75 @@
+/* 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/>.
+*/
+
+#define DHCPV6_SERVER_PORT 547
+#define DHCPV6_CLIENT_PORT 546
+
+#define ALL_SERVERS                  "FF05::1:3"
+#define ALL_RELAY_AGENTS_AND_SERVERS "FF02::1:2"
+
+#define DHCP6SOLICIT      1
+#define DHCP6ADVERTISE    2
+#define DHCP6REQUEST      3
+#define DHCP6CONFIRM      4
+#define DHCP6RENEW        5
+#define DHCP6REBIND       6
+#define DHCP6REPLY        7
+#define DHCP6RELEASE      8
+#define DHCP6DECLINE      9
+#define DHCP6RECONFIGURE  10
+#define DHCP6IREQ         11
+#define DHCP6RELAYFORW    12
+#define DHCP6RELAYREPL    13
+
+#define OPTION6_CLIENT_ID       1
+#define OPTION6_SERVER_ID       2
+#define OPTION6_IA_NA           3
+#define OPTION6_IA_TA           4
+#define OPTION6_IAADDR          5
+#define OPTION6_ORO             6
+#define OPTION6_PREFERENCE      7
+#define OPTION6_ELAPSED_TIME    8
+#define OPTION6_RELAY_MSG       9
+#define OPTION6_AUTH            11
+#define OPTION6_UNICAST         12
+#define OPTION6_STATUS_CODE     13
+#define OPTION6_RAPID_COMMIT    14
+#define OPTION6_USER_CLASS      15
+#define OPTION6_VENDOR_CLASS    16
+#define OPTION6_VENDOR_OPTS     17
+#define OPTION6_INTERFACE_ID    18
+#define OPTION6_RECONFIGURE_MSG 19
+#define OPTION6_RECONF_ACCEPT   20
+#define OPTION6_DNS_SERVER      23
+#define OPTION6_DOMAIN_SEARCH   24
+#define OPTION6_REFRESH_TIME    32
+#define OPTION6_REMOTE_ID       37
+#define OPTION6_SUBSCRIBER_ID   38
+#define OPTION6_FQDN            39
+#define OPTION6_CLIENT_MAC      79
+
+/* replace this with the real number when allocated.
+   defining this also enables the relevant code. */ 
+/* #define OPTION6_PREFIX_CLASS    99 */
+
+
+#define DHCP6SUCCESS     0
+#define DHCP6UNSPEC      1
+#define DHCP6NOADDRS     2
+#define DHCP6NOBINDING   3
+#define DHCP6NOTONLINK   4
+#define DHCP6USEMULTI    5
+
diff --git a/src/dhcp6.c b/src/dhcp6.c
new file mode 100644 (file)
index 0000000..8286ff4
--- /dev/null
@@ -0,0 +1,806 @@
+/* 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"
+
+#ifdef HAVE_DHCP6
+
+#include <netinet/icmp6.h>
+
+struct iface_param {
+  struct dhcp_context *current;
+  struct dhcp_relay *relay;
+  struct in6_addr fallback, relay_local, ll_addr, ula_addr;
+  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)
+{
+  int fd;
+  struct sockaddr_in6 saddr;
+#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
+  int class = IPTOS_CLASS_CS6;
+#endif
+  int oneopt = 1;
+
+  if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 ||
+#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
+      setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
+#endif
+      setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &oneopt, sizeof(oneopt)) == -1 ||
+      !fix_fd(fd) ||
+      !set_ipv6pktinfo(fd))
+    die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
+  
+ /* When bind-interfaces is set, there might be more than one dnmsasq
+     instance binding port 547. That's OK if they serve different networks.
+     Need to set REUSEADDR|REUSEPORT to make this posible.
+     Handle the case that REUSEPORT is defined, but the kernel doesn't 
+     support it. This handles the introduction of REUSEPORT on Linux. */
+  if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
+    {
+      int rc = 0;
+
+#ifdef SO_REUSEPORT
+      if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
+         errno == ENOPROTOOPT)
+       rc = 0;
+#endif
+      
+      if (rc != -1)
+       rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
+      
+      if (rc == -1)
+       die(_("failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"), NULL, EC_BADNET);
+    }
+  
+  memset(&saddr, 0, sizeof(saddr));
+#ifdef HAVE_SOCKADDR_SA_LEN
+  saddr.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+  saddr.sin6_family = AF_INET6;
+  saddr.sin6_addr = in6addr_any;
+  saddr.sin6_port = htons(DHCPV6_SERVER_PORT);
+  
+  if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
+    die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
+  
+  daemon->dhcp6fd = fd;
+}
+
+void dhcp6_packet(time_t now)
+{
+  struct dhcp_context *context;
+  struct dhcp_relay *relay;
+  struct iface_param parm;
+  struct cmsghdr *cmptr;
+  struct msghdr msg;
+  int if_index = 0;
+  union {
+    struct cmsghdr align; /* this ensures alignment */
+    char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+  } control_u;
+  struct sockaddr_in6 from;
+  ssize_t sz; 
+  struct ifreq ifr;
+  struct iname *tmp;
+  unsigned short port;
+  struct in6_addr dst_addr;
+
+  memset(&dst_addr, 0, sizeof(dst_addr));
+
+  msg.msg_control = control_u.control6;
+  msg.msg_controllen = sizeof(control_u);
+  msg.msg_flags = 0;
+  msg.msg_name = &from;
+  msg.msg_namelen = sizeof(from);
+  msg.msg_iov =  &daemon->dhcp_packet;
+  msg.msg_iovlen = 1;
+  
+  if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
+    return;
+  
+  for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+    if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
+      {
+       union {
+         unsigned char *c;
+         struct in6_pktinfo *p;
+       } p;
+       p.c = CMSG_DATA(cmptr);
+        
+       if_index = p.p->ipi6_ifindex;
+       dst_addr = p.p->ipi6_addr;
+      }
+
+  if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
+    return;
+
+  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;
+      
+      for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
+       if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
+         return;
+      
+      parm.current = NULL;
+      parm.relay = NULL;
+      memset(&parm.relay_local, 0, IN6ADDRSZ);
+      parm.ind = if_index;
+      parm.addr_match = 0;
+      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)
+         {
+           /* wildcard context for DHCP-stateless only */
+           parm.current = context;
+           context->current = NULL;
+         }
+       else
+         {
+           /* unlinked contexts are marked by context->current == context */
+           context->current = context;
+           memset(&context->local6, 0, IN6ADDRSZ);
+         }
+
+      for (relay = daemon->relay6; relay; relay = relay->next)
+       relay->current = relay;
+      
+      if (!iface_enumerate(AF_INET6, &parm, complete_context6))
+       return;
+
+      if (daemon->if_names || daemon->if_addrs)
+       {
+         
+         for (tmp = daemon->if_names; tmp; tmp = tmp->next)
+           if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
+             break;
+         
+         if (!tmp && !parm.addr_match)
+           return;
+       }
+      
+      if (parm.relay)
+       {
+         /* Ignore requests sent to the ALL_SERVERS multicast address for relay when
+            we're listening there for DHCPv6 server reasons. */
+         struct in6_addr all_servers;
+         
+         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);
+         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, 
+                        &parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);
+      
+      lease_update_file(now);
+      lease_update_dns(0);
+    }
+                         
+  /* The port in the source address of the original request should
+     be correct, but at least once client sends from the server port,
+     so we explicitly send to the client port to a client, and the
+     server port to a relay. */
+  if (port != 0)
+    {
+      from.sin6_port = htons(port);
+      while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, 
+                              save_counter(0), 0, (struct sockaddr *)&from, 
+                              sizeof(from))));
+    }
+}
+
+void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep)
+{
+  /* 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;
+
+  neigh.type = ND_NEIGHBOR_SOLICIT;
+  neigh.code = 0;
+  neigh.reserved = 0;
+  neigh.target = *client;
+  /* RFC4443 section-2.3: checksum has to be zero to be calculated */
+  neigh.checksum = 0;
+   
+  memset(&addr, 0, sizeof(addr));
+#ifdef HAVE_SOCKADDR_SA_LEN
+  addr.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;
+  
+  for (i = 0; i < 5; i++)
+    {
+      struct timespec ts;
+      
+      iface_enumerate(AF_UNSPEC, &mac_param, find_mac);
+      
+      if (mac_param.maclen != 0)
+       break;
+      
+      sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, (struct sockaddr *)&addr, sizeof(addr));
+      
+      ts.tv_sec = 0;
+      ts.tv_nsec = 100000000; /* 100ms */
+      nanosleep(&ts, NULL);
+    }
+
+  *maclenp = mac_param.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)
+{
+  struct dhcp_context *context;
+  struct dhcp_relay *relay;
+  struct iface_param *param = vparam;
+  struct iname *tmp;
+  (void)scope; /* warning */
+  
+  if (if_index == param->ind)
+    {
+      if (IN6_IS_ADDR_LINKLOCAL(local))
+       param->ll_addr = *local;
+      else if (IN6_IS_ADDR_ULA(local))
+       param->ula_addr = *local;
+
+      if (!IN6_IS_ADDR_LOOPBACK(local) &&
+         !IN6_IS_ADDR_LINKLOCAL(local) &&
+         !IN6_IS_ADDR_MULTICAST(local))
+       {
+         /* if we have --listen-address config, see if the 
+            arrival interface has a matching address. */
+         for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
+           if (tmp->addr.sa.sa_family == AF_INET6 &&
+               IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
+             param->addr_match = 1;
+         
+         /* Determine a globally address on the arrival interface, even
+            if we have no matching dhcp-context, because we're only
+            allocating on remote subnets via relays. This
+            is used as a default for the DNS server option. */
+         param->fallback = *local;
+         
+         for (context = daemon->dhcp6; context; context = context->next)
+           {
+             if ((context->flags & CONTEXT_DHCP) &&
+                 !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
+                 prefix <= context->prefix &&
+                 is_same_net6(local, &context->start6, context->prefix) &&
+                 is_same_net6(local, &context->end6, context->prefix))
+               {
+                 
+                 
+                 /* link it onto the current chain if we've not seen it before */
+                 if (context->current == context)
+                   {
+                     struct dhcp_context *tmp, **up;
+                     
+                     /* use interface values only for contructed contexts */
+                     if (!(context->flags & CONTEXT_CONSTRUCTED))
+                       preferred = valid = 0xffffffff;
+                     else if (flags & IFACE_DEPRECATED)
+                       preferred = 0;
+                     
+                     if (context->flags & CONTEXT_DEPRECATE)
+                       preferred = 0;
+                     
+                     /* order chain, longest preferred time first */
+                     for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
+                       if (tmp->preferred <= preferred)
+                         break;
+                       else
+                         up = &tmp->current;
+                     
+                     context->current = *up;
+                     *up = context;
+                     context->local6 = *local;
+                     context->preferred = preferred;
+                     context->valid = valid;
+                   }
+               }
+           }
+       }
+
+      for (relay = daemon->relay6; relay; relay = relay->next)
+       if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr.addr6) && relay->current == relay &&
+           (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
+         {
+           relay->current = param->relay;
+           param->relay = relay;
+           param->relay_local = *local;
+         }
+      
+    }          
+ return 1;
+}
+
+struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
+{
+  struct dhcp_config *config;
+  
+  for (config = configs; config; config = config->next)
+    if ((config->flags & CONFIG_ADDR6) &&
+       is_same_net6(&config->addr6, net, prefix) &&
+       (prefix == 128 || addr6part(&config->addr6) == addr))
+      return config;
+  
+  return NULL;
+}
+
+struct dhcp_context *address6_allocate(struct dhcp_context *context,  unsigned char *clid, int clid_len, int temp_addr,
+                                      int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)   
+{
+  /* Find a free address: exclude anything in use and anything allocated to
+     a particular hwaddr/clientid/hostname in our configuration.
+     Try to return from contexts which match netids first. 
+     
+     Note that we assume the address prefix lengths are 64 or greater, so we can
+     get by with 64 bit arithmetic.
+*/
+
+  u64 start, addr;
+  struct dhcp_context *c, *d;
+  int i, pass;
+  u64 j; 
+
+  /* hash hwaddr: use the SDBM hashing algorithm.  This works
+     for MAC addresses, let's see how it manages with client-ids! 
+     For temporary addresses, we generate a new random one each time. */
+  if (temp_addr)
+    j = rand64();
+  else
+    for (j = iaid, i = 0; i < clid_len; i++)
+      j += clid[i] + (j << 6) + (j << 16) - j;
+  
+  for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
+    for (c = context; c; c = c->current)
+      if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
+       continue;
+      else if (!match_netid(c->filter, netids, pass))
+       continue;
+      else
+       { 
+         if (!temp_addr && option_bool(OPT_CONSEC_ADDR))
+           /* seed is largest extant lease addr in this context */
+           start = lease_find_max_addr6(c) + serial;
+         else
+           start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
+
+         /* iterate until we find a free address. */
+         addr = start;
+         
+         do {
+           /* eliminate addresses in use by the server. */
+           for (d = context; d; d = d->current)
+             if (addr == addr6part(&d->local6))
+               break;
+
+           if (!d &&
+               !lease6_find_by_addr(&c->start6, c->prefix, addr) && 
+               !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
+             {
+               *ans = c->start6;
+               setaddr6part (ans, addr);
+               return c;
+             }
+       
+           addr++;
+           
+           if (addr  == addr6part(&c->end6) + 1)
+             addr = addr6part(&c->start6);
+           
+         } while (addr != start);
+       }
+          
+  return NULL;
+}
+
+/* can dynamically allocate addr */
+struct dhcp_context *address6_available(struct dhcp_context *context, 
+                                       struct in6_addr *taddr,
+                                       struct dhcp_netid *netids,
+                                       int plain_range)
+{
+  u64 start, end, addr = addr6part(taddr);
+  struct dhcp_context *tmp;
+  for (tmp = context; tmp; tmp = tmp->current)
+    {
+      start = addr6part(&tmp->start6);
+      end = addr6part(&tmp->end6);
+
+      if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
+          is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
+         is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
+         addr >= start &&
+          addr <= end &&
+          match_netid(tmp->filter, netids, plain_range))
+        return tmp;
+    }
+
+  return NULL;
+}
+
+/* address OK if configured */
+struct dhcp_context *address6_valid(struct dhcp_context *context, 
+                                   struct in6_addr *taddr,
+                                   struct dhcp_netid *netids,
+                                   int plain_range)
+{
+  struct dhcp_context *tmp;
+  for (tmp = context; tmp; tmp = tmp->current)
+    if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
+       match_netid(tmp->filter, netids, plain_range))
+      return tmp;
+
+  return NULL;
+}
+
+int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
+{
+  if (!config || !(config->flags & CONFIG_ADDR6))
+    return 0;
+
+  if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
+    {
+      *addr = context->start6;
+      setaddr6part(addr, addr6part(&config->addr6));
+      return 1;
+    }
+  
+  if (is_same_net6(&context->start6, &config->addr6, context->prefix))
+    {
+      *addr = config->addr6;
+      return 1;
+    }
+  
+  return 0;
+}
+
+void make_duid(time_t now)
+{
+  (void)now;
+
+  if (daemon->duid_config)
+    {
+      unsigned char *p;
+      
+      daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
+      daemon->duid_len = daemon->duid_config_len + 6;
+      PUTSHORT(2, p); /* DUID_EN */
+      PUTLONG(daemon->duid_enterprise, p);
+      memcpy(p, daemon->duid_config, daemon->duid_config_len);
+    }
+  else
+    {
+      time_t newnow = 0;
+      
+      /* If we have no persistent lease database, or a non-stable RTC, use DUID_LL (newnow == 0) */
+#ifndef HAVE_BROKEN_RTC
+      /* rebase epoch to 1/1/2000 */
+      if (!option_bool(OPT_LEASE_RO) || daemon->lease_change_command)
+       newnow = now - 946684800;
+#endif      
+      
+      iface_enumerate(AF_LOCAL, &newnow, make_duid1);
+      
+      if(!daemon->duid)
+       die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
+    }
+}
+
+static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
+{
+  /* create DUID as specified in RFC3315. We use the MAC of the
+     first interface we find that isn't loopback or P-to-P and
+     has address-type < 256. Address types above 256 are things like 
+     tunnels which don't have usable MAC addresses. */
+  
+  unsigned char *p;
+  (void)index;
+  (void)parm;
+  time_t newnow = *((time_t *)parm);
+  
+  if (type >= 256)
+    return 1;
+
+  if (newnow == 0)
+    {
+      daemon->duid = p = safe_malloc(maclen + 4);
+      daemon->duid_len = maclen + 4;
+      PUTSHORT(3, p); /* DUID_LL */
+      PUTSHORT(type, p); /* address type */
+    }
+  else
+    {
+      daemon->duid = p = safe_malloc(maclen + 8);
+      daemon->duid_len = maclen + 8;
+      PUTSHORT(1, p); /* DUID_LLT */
+      PUTSHORT(type, p); /* address type */
+      PUTLONG(*((time_t *)parm), p); /* time */
+    }
+  
+  memcpy(p, mac, maclen);
+
+  return 0;
+}
+
+struct cparam {
+  time_t now;
+  int newone, newname;
+};
+
+static int construct_worker(struct in6_addr *local, int prefix, 
+                           int scope, int if_index, int flags, 
+                           int preferred, int valid, void *vparam)
+{
+  char ifrn_name[IFNAMSIZ];
+  struct in6_addr start6, end6;
+  struct dhcp_context *template, *context;
+
+  (void)scope;
+  (void)flags;
+  (void)valid;
+  (void)preferred;
+
+  struct cparam *param = vparam;
+
+  if (IN6_IS_ADDR_LOOPBACK(local) ||
+      IN6_IS_ADDR_LINKLOCAL(local) ||
+      IN6_IS_ADDR_MULTICAST(local))
+    return 1;
+
+  if (!(flags & IFACE_PERMANENT))
+    return 1;
+
+  if (flags & IFACE_DEPRECATED)
+    return 1;
+
+  if (!indextoname(daemon->icmp6fd, if_index, ifrn_name))
+    return 0;
+  
+  for (template = daemon->dhcp6; template; template = template->next)
+    if (!(template->flags & CONTEXT_TEMPLATE))
+      {
+       /* non-template entries, just fill in interface and local addresses */
+       if (prefix <= template->prefix &&
+           is_same_net6(local, &template->start6, template->prefix) &&
+           is_same_net6(local, &template->end6, template->prefix))
+         {
+           template->if_index = if_index;
+           template->local6 = *local;
+         }
+       
+      }
+    else if (wildcard_match(template->template_interface, ifrn_name) &&
+            template->prefix >= prefix)
+      {
+       start6 = *local;
+       setaddr6part(&start6, addr6part(&template->start6));
+       end6 = *local;
+       setaddr6part(&end6, addr6part(&template->end6));
+       
+       for (context = daemon->dhcp6; context; context = context->next)
+         if ((context->flags & CONTEXT_CONSTRUCTED) &&
+             IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
+             IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
+           {
+             int flags = context->flags;
+             context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
+             if (flags & CONTEXT_OLD)
+               {
+                 /* address went, now it's back */
+                 log_context(AF_INET6, context); 
+                 /* fast RAs for a while */
+                 ra_start_unsolicted(param->now, context);
+                 param->newone = 1; 
+                 /* Add address to name again */
+                 if (context->flags & CONTEXT_RA_NAME)
+                   param->newname = 1;
+               }
+             break;
+           }
+       
+       if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
+         {
+           *context = *template;
+           context->start6 = start6;
+           context->end6 = end6;
+           context->flags &= ~CONTEXT_TEMPLATE;
+           context->flags |= CONTEXT_CONSTRUCTED;
+           context->if_index = if_index;
+           context->local6 = *local;
+           context->saved_valid = 0;
+           
+           context->next = daemon->dhcp6;
+           daemon->dhcp6 = context;
+
+           ra_start_unsolicted(param->now, context);
+           /* we created a new one, need to call
+              lease_update_file to get periodic functions called */
+           param->newone = 1; 
+
+           /* Will need to add new putative SLAAC addresses to existing leases */
+           if (context->flags & CONTEXT_RA_NAME)
+             param->newname = 1;
+           
+           log_context(AF_INET6, context);
+         } 
+      }
+  
+  return 1;
+}
+
+void dhcp_construct_contexts(time_t now)
+{ 
+  struct dhcp_context *context, *tmp, **up;
+  struct cparam param;
+  param.newone = 0;
+  param.newname = 0;
+  param.now = now;
+
+  for (context = daemon->dhcp6; context; context = context->next)
+    if (context->flags & CONTEXT_CONSTRUCTED)
+      context->flags |= CONTEXT_GC;
+   
+  iface_enumerate(AF_INET6, &param, construct_worker);
+
+  for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
+    {
+      
+      tmp = context->next; 
+     
+      if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
+       {
+         if ((context->flags & CONTEXT_RA) || option_bool(OPT_RA))
+           {
+             /* previously constructed context has gone. advertise it's demise */
+             context->flags |= CONTEXT_OLD;
+             context->address_lost_time = now;
+             /* Apply same ceiling of configured lease time as in radv.c */
+             if (context->saved_valid > context->lease_time)
+               context->saved_valid = context->lease_time;
+             /* maximum time is 2 hours, from RFC */
+             if (context->saved_valid > 7200) /* 2 hours */
+               context->saved_valid = 7200;
+             ra_start_unsolicted(now, context);
+             param.newone = 1; /* include deletion */ 
+             
+             if (context->flags & CONTEXT_RA_NAME)
+               param.newname = 1; 
+                             
+             log_context(AF_INET6, context);
+             
+             up = &context->next;
+           }
+         else
+           {
+             /* we were never doing RA for this, so free now */
+             *up = context->next;
+             free(context);
+           }
+       }
+      else
+        up = &context->next;
+    }
+  
+  if (param.newone)
+    {
+      if (daemon->dhcp || daemon->doing_dhcp6)
+       {
+         if (param.newname)
+           lease_update_slaac(now);
+         lease_update_file(now);
+       }
+      else 
+       /* Not doing DHCP, so no lease system, manage alarms for ra only */
+       send_alarm(periodic_ra(now), now);
+    }
+}
+
+#endif
+
+
similarity index 61%
rename from src/dns_protocol.h
rename to src/dns-protocol.h
index bc18e79..6cf5158 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
@@ -14,6 +14,9 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#define NAMESERVER_PORT 53
+#define TFTP_PORT       69
+
 #define IN6ADDRSZ       16
 #define INADDRSZ        4
 
 
 #define C_IN            1               /* the arpa internet */
 #define C_CHAOS         3               /* for chaos net (MIT) */
+#define C_HESIOD        4               /* hesiod */
 #define C_ANY           255             /* wildcard match */
 
 #define T_A            1
-#define T_NS            2               
+#define T_NS            2
+#define T_MD            3
+#define T_MF            4             
 #define T_CNAME                5
 #define T_SOA          6
+#define T_MB            7
+#define T_MG            8
+#define T_MR            9
 #define T_PTR          12
+#define T_MINFO         14
 #define T_MX           15
 #define T_TXT          16
+#define T_RP            17
+#define T_AFSDB         18
+#define T_RT            21
 #define T_SIG          24
+#define T_PX            26
 #define T_AAAA         28
+#define T_NXT           30
 #define T_SRV          33
 #define T_NAPTR                35
+#define T_KX            36
+#define T_DNAME         39
 #define T_OPT          41
+#define T_DS            43
+#define T_RRSIG         46
+#define T_NSEC          47
+#define T_DNSKEY        48
+#define T_NSEC3         50
 #define        T_TKEY          249             
 #define        T_TSIG          250
+#define T_AXFR          252
 #define T_MAILB                253     
 #define T_ANY          255
 
+#define EDNS0_OPTION_MAC            65001 /* dyndns.org temporary assignment */
+#define EDNS0_OPTION_CLIENT_SUBNET  8     /* IANA */
+
 struct dns_header {
   u16 id;
   u8  hb3,hb4;
   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)
+#define SET_OPCODE(x, code) (x)->hb3 = ((x)->hb3 & ~HB3_OPCODE) | code
+
 #define RCODE(x)           ((x)->hb4 & HB4_RCODE)
 #define SET_RCODE(x, code) (x)->hb4 = ((x)->hb4 & ~HB4_RCODE) | code
   
@@ -109,3 +137,16 @@ struct dns_header {
        (cp) += 4; \
 }
 
+#define CHECK_LEN(header, pp, plen, len) \
+    ((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
+
+#define ADD_RDLEN(header, pp, plen, len) \
+  (!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
+
+/* Escape character in our presentation format for names.
+   Cannot be '.' or /000 and must be !isprint().
+   Note that escaped chars are stored as
+   <NAME_ESCAPE> <orig-char+1>
+   to ensure that the escaped form of /000 doesn't include /000
+*/
+#define NAME_ESCAPE 1
index 827b0dc..04d5758 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+/* Declare static char *compiler_opts  in config.h */
+#define DNSMASQ_COMPILE_OPTS
+
 #include "dnsmasq.h"
 
 struct daemon *daemon;
 
-static char *compile_opts = 
-#ifndef HAVE_IPV6
-"no-"
-#endif
-"IPv6 "
-#ifndef HAVE_GETOPT_LONG
-"no-"
-#endif
-"GNU-getopt "
-#ifdef HAVE_BROKEN_RTC
-"no-RTC "
-#endif
-#ifdef NO_FORK
-"no-MMU "
-#endif
-#ifndef HAVE_DBUS
-"no-"
-#endif
-"DBus "
-#ifndef LOCALEDIR
-"no-"
-#endif
-"I18N "
-#ifndef HAVE_DHCP
-"no-"
-#endif
-"DHCP "
-#if defined(HAVE_DHCP) && !defined(HAVE_SCRIPT)
-"no-scripts "
-#endif
-#ifndef HAVE_TFTP
-"no-"
-#endif
-"TFTP "
-#if !defined(LOCALEDIR) && !defined(HAVE_IDN)
-"no-"
-#endif 
-"IDN";
-
-
 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);
+static void fatal_event(struct event_desc *ev, char *msg);
+static int read_event(int fd, struct event_desc *evp, char **msg);
+static void poll_resolv(int force, int do_reload, time_t now);
 
 int main (int argc, char **argv)
 {
@@ -75,7 +40,7 @@ int main (int argc, char **argv)
   struct iname *if_tmp;
   int piperead, pipefd[2], err_pipe[2];
   struct passwd *ent_pw = NULL;
-#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
+#if defined(HAVE_SCRIPT)
   uid_t script_uid = 0;
   gid_t script_gid = 0;
 #endif
@@ -86,7 +51,16 @@ int main (int argc, char **argv)
 #if defined(HAVE_LINUX_NETWORK)
   cap_user_header_t hdr = NULL;
   cap_user_data_t data = NULL;
+  char *bound_device = NULL;
+  int did_bind = 0;
 #endif 
+#if defined(HAVE_DHCP) || defined(HAVE_DHCP6)
+  struct dhcp_context *context;
+  struct dhcp_relay *relay;
+#endif
+#ifdef HAVE_TFTP
+  int tftp_prefix_missing = 0;
+#endif
 
 #ifdef LOCALEDIR
   setlocale(LC_ALL, "");
@@ -109,45 +83,112 @@ int main (int argc, char **argv)
   sigaction(SIGPIPE, &sigact, NULL);
 
   umask(022); /* known umask, create leases and pid files as 0644 */
-
+  rand_init(); /* Must precede read_opts() */
+  
   read_opts(argc, argv, compile_opts);
-    
   if (daemon->edns_pktsz < PACKETSZ)
     daemon->edns_pktsz = PACKETSZ;
+
   daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ? 
     daemon->edns_pktsz : DNSMASQ_PACKETSZ;
   daemon->packet = safe_malloc(daemon->packet_buff_sz);
+  
+  daemon->addrbuff = safe_malloc(ADDRSTRLEN);
+  if (option_bool(OPT_EXTRALOG))
+    daemon->addrbuff2 = safe_malloc(ADDRSTRLEN);
+  
+#ifdef HAVE_DNSSEC
+  if (option_bool(OPT_DNSSEC_VALID))
+    {
+      /* Note that both /000 and '.' are allowed within labels. These get
+        represented in presentation format using NAME_ESCAPE as an escape
+        character when in DNSSEC mode. 
+        In theory, if all the characters in a name were /000 or
+        '.' or NAME_ESCAPE then all would have to be escaped, so the 
+        presentation format would be twice as long as the spec.
+
+        daemon->namebuff was previously allocated by the option-reading
+        code before we knew if we're in DNSSEC mode, so reallocate here. */
+      free(daemon->namebuff);
+      daemon->namebuff = safe_malloc(MAXDNAME * 2);
+      daemon->keyname = safe_malloc(MAXDNAME * 2);
+      daemon->workspacename = safe_malloc(MAXDNAME * 2);
+    }
+#endif
 
 #ifdef HAVE_DHCP
   if (!daemon->lease_file)
     {
-      if (daemon->dhcp)
+      if (daemon->dhcp || daemon->dhcp6)
        daemon->lease_file = LEASEFILE;
     }
 #endif
   
-  /* Close any file descriptors we inherited apart from std{in|out|err} */
+  /* Close any file descriptors we inherited apart from std{in|out|err} 
+     
+     Ensure that at least stdin, stdout and stderr (fd 0, 1, 2) exist,
+     otherwise file descriptors we create can end up being 0, 1, or 2 
+     and then get accidentally closed later when we make 0, 1, and 2 
+     open to /dev/null. Normally we'll be started with 0, 1 and 2 open, 
+     but it's not guaranteed. By opening /dev/null three times, we 
+     ensure that we're not using those fds for real stuff. */
   for (i = 0; i < max_fd; i++)
     if (i != STDOUT_FILENO && i != STDERR_FILENO && i != STDIN_FILENO)
       close(i);
+    else
+      open("/dev/null", O_RDWR); 
 
-#ifdef HAVE_LINUX_NETWORK
-  netlink_init();
-#elif !(defined(IP_RECVDSTADDR) && \
-       defined(IP_RECVIF) && \
-       defined(IP_SENDSRCADDR))
+#ifndef HAVE_LINUX_NETWORK
+#  if !(defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR))
   if (!option_bool(OPT_NOWILD))
     {
       bind_fallback = 1;
       set_option_bool(OPT_NOWILD);
     }
+#  endif
+  
+  /* -- bind-dynamic not supported on !Linux, fall back to --bind-interfaces */
+  if (option_bool(OPT_CLEVERBIND))
+    {
+      bind_fallback = 1;
+      set_option_bool(OPT_NOWILD);
+      reset_option_bool(OPT_CLEVERBIND);
+    }
+#endif
+
+#ifndef HAVE_INOTIFY
+  if (daemon->dynamic_dirs)
+    die(_("dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"), NULL, EC_BADCONF);
+#endif
+  
+  if (option_bool(OPT_DNSSEC_VALID))
+    {
+#ifdef HAVE_DNSSEC
+      if (!daemon->ds)
+       die(_("no trust anchors provided for DNSSEC"), NULL, EC_BADCONF);
+      
+      if (daemon->cachesize < CACHESIZ)
+       die(_("cannot reduce cache size from default when DNSSEC enabled"), NULL, EC_BADCONF);
+#else 
+      die(_("DNSSEC not available: set HAVE_DNSSEC in src/config.h"), NULL, EC_BADCONF);
 #endif
+    }
 
 #ifndef HAVE_TFTP
-  if (daemon->tftp_unlimited || daemon->tftp_interfaces)
+  if (option_bool(OPT_TFTP))
     die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF);
 #endif
 
+#ifdef HAVE_CONNTRACK
+  if (option_bool(OPT_CONNTRACK) && (daemon->query_port != 0 || daemon->osport))
+    die (_("cannot use --conntrack AND --query-port"), NULL, EC_BADCONF); 
+#else
+  if (option_bool(OPT_CONNTRACK))
+    die(_("conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF);
+#endif
+
 #ifdef HAVE_SOLARIS_NETWORK
   if (daemon->max_logs != 0)
     die(_("asychronous logging is not available under Solaris"), NULL, EC_BADCONF);
@@ -158,45 +199,151 @@ int main (int argc, char **argv)
     die(_("asychronous logging is not available under Android"), NULL, EC_BADCONF);
 #endif
 
-  rand_init();
+#ifndef HAVE_AUTH
+  if (daemon->authserver)
+    die(_("authoritative DNS not available: set HAVE_AUTH in src/config.h"), NULL, EC_BADCONF);
+#endif
+
+#ifndef HAVE_LOOP
+  if (option_bool(OPT_LOOP_DETECT))
+    die(_("loop detection not available: set HAVE_LOOP in src/config.h"), NULL, EC_BADCONF);
+#endif
   
   now = dnsmasq_time();
+
+  /* Create a serial at startup if not configured. */
+  if (daemon->authinterface && daemon->soa_sn == 0)
+#ifdef HAVE_BROKEN_RTC
+    die(_("zone serial must be configured in --auth-soa"), NULL, EC_BADCONF);
+#else
+  daemon->soa_sn = now;
+#endif
+  
+#ifdef HAVE_DHCP6
+  if (daemon->dhcp6)
+    {
+      daemon->doing_ra = option_bool(OPT_RA);
+      
+      for (context = daemon->dhcp6; context; context = context->next)
+       {
+         if (context->flags & CONTEXT_DHCP)
+           daemon->doing_dhcp6 = 1;
+         if (context->flags & CONTEXT_RA)
+           daemon->doing_ra = 1;
+#if !defined(HAVE_LINUX_NETWORK) && !defined(HAVE_BSD_NETWORK)
+         if (context->flags & CONTEXT_TEMPLATE)
+           die (_("dhcp-range constructor not available on this platform"), NULL, EC_BADCONF);
+#endif 
+       }
+    }
+#endif
   
 #ifdef HAVE_DHCP
-  if (daemon->dhcp)
+  /* 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)
     {
-      /* 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. */
-      lease_init(now);
-      dhcp_init();
+      dhcp_common_init();
+      if (daemon->dhcp || daemon->doing_dhcp6)
+       lease_init(now);
     }
+  
+  if (daemon->dhcp || daemon->relay4)
+    dhcp_init();
+  
+#  ifdef HAVE_DHCP6
+  if (daemon->doing_ra || daemon->doing_dhcp6 || daemon->relay6)
+    ra_init(now);
+  
+  if (daemon->doing_dhcp6 || daemon->relay6)
+    dhcp6_init();
+#  endif
+
+#endif
+
+#ifdef HAVE_IPSET
+  if (daemon->ipsets)
+    ipset_init();
 #endif
 
-  if (!enumerate_interfaces())
+#if  defined(HAVE_LINUX_NETWORK)
+  netlink_init();
+#elif defined(HAVE_BSD_NETWORK)
+  route_init();
+#endif
+
+  if (option_bool(OPT_NOWILD) && option_bool(OPT_CLEVERBIND))
+    die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF);
+  
+  if (!enumerate_interfaces(1) || !enumerate_interfaces(0))
     die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
-    
-  if (option_bool(OPT_NOWILD)) 
+  
+  if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND)
     {
-      daemon->listeners = create_bound_listeners();
+      create_bound_listeners(1);
+      
+      if (!option_bool(OPT_CLEVERBIND))
+       for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
+         if (if_tmp->name && !if_tmp->used)
+           die(_("unknown interface %s"), if_tmp->name, EC_BADNET);
+
+#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP)
+      /* after enumerate_interfaces()  */
+      bound_device = whichdevice();
+      
+      if (daemon->dhcp)
+       {
+         if (!daemon->relay4 && bound_device)
+           {
+             bindtodevice(bound_device, daemon->dhcpfd);
+             did_bind = 1;
+           }
+         if (daemon->enable_pxe && bound_device)
+           {
+             bindtodevice(bound_device, daemon->pxefd);
+             did_bind = 1;
+           }
+       }
+#endif
 
-      for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
-       if (if_tmp->name && !if_tmp->used)
-         die(_("unknown interface %s"), if_tmp->name, EC_BADNET);
-  
-      for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next)
-       if (!if_tmp->used)
-         {
-           prettyprint_addr(&if_tmp->addr, daemon->namebuff);
-           die(_("no interface with address %s"), daemon->namebuff, EC_BADNET);
-         }
+#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP6)
+      if (daemon->doing_dhcp6 && !daemon->relay6 && bound_device)
+       {
+         bindtodevice(bound_device, daemon->dhcp6fd);
+         did_bind = 1;
+       }
+#endif
     }
   else 
-    daemon->listeners = create_wildcard_listeners();
+    create_wildcard_listeners();
+#ifdef HAVE_DHCP6
+  /* after enumerate_interfaces() */
+  if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
+    join_multicast(1);
+
+  /* After netlink_init() and before create_helper() */
+  lease_make_duid(now);
+#endif
   
   if (daemon->port != 0)
-    cache_init();
-    
+    {
+      cache_init();
+
+#ifdef HAVE_DNSSEC
+      blockdata_init();
+#endif
+    }
+
+#ifdef HAVE_INOTIFY
+  if (daemon->port != 0 || daemon->dhcp || daemon->doing_dhcp6)
+    inotify_dnsmasq_init();
+  else
+    daemon->inotifyfd = -1;
+#endif
+       
   if (option_bool(OPT_DBUS))
 #ifdef HAVE_DBUS
     {
@@ -209,13 +356,15 @@ int main (int argc, char **argv)
 #else
   die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL, EC_BADCONF);
 #endif
-  
+
   if (daemon->port != 0)
     pre_allocate_sfds();
 
-#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
+#if defined(HAVE_SCRIPT)
   /* Note getpwnam returns static storage */
-  if (daemon->dhcp && daemon->lease_change_command && daemon->scriptuser)
+  if ((daemon->dhcp || daemon->dhcp6) && 
+      daemon->scriptuser && 
+      (daemon->lease_change_command || daemon->luascript))
     {
       if ((ent_pw = getpwnam(daemon->scriptuser)))
        {
@@ -234,7 +383,7 @@ int main (int argc, char **argv)
 
   if (baduser)
     die(_("unknown user or group: %s"), baduser, EC_BADCONF);
-   
+
   /* implement group defaults, "dip" if available, or group associated with uid */
   if (!daemon->group_set && !gp)
     {
@@ -278,7 +427,7 @@ int main (int argc, char **argv)
   piperead = pipefd[0];
   pipewrite = pipefd[1];
   /* prime the pipe to load stuff first time. */
-  send_event(pipewrite, EVENT_RELOAD, 0); 
+  send_event(pipewrite, EVENT_INIT, 0, NULL); 
 
   err_pipe[1] = -1;
   
@@ -301,30 +450,31 @@ int main (int argc, char **argv)
          
          if ((pid = fork()) == -1)
            /* fd == -1 since we've not forked, never returns. */
-           send_event(-1, EVENT_FORK_ERR, errno);
+           send_event(-1, EVENT_FORK_ERR, errno, NULL);
           
          if (pid != 0)
            {
              struct event_desc ev;
-             
+             char *msg;
+
              /* close our copy of write-end */
-             close(err_pipe[1]);
+             while (retry_send(close(err_pipe[1])));
              
              /* check for errors after the fork */
-             if (read_write(err_pipe[0], (unsigned char *)&ev, sizeof(ev), 1))
-               fatal_event(&ev);
+             if (read_event(err_pipe[0], &ev, &msg))
+               fatal_event(&ev, msg);
              
              _exit(EC_GOOD);
            } 
          
-         close(err_pipe[0]);
+         while (retry_send(close(err_pipe[0])));
 
          /* NO calls to die() from here on. */
          
          setsid();
         
          if ((pid = fork()) == -1)
-           send_event(err_pipe[1], EVENT_FORK_ERR, errno);
+           send_event(err_pipe[1], EVENT_FORK_ERR, errno, NULL);
         
          if (pid != 0)
            _exit(0);
@@ -334,17 +484,52 @@ int main (int argc, char **argv)
       /* write pidfile _after_ forking ! */
       if (daemon->runfile)
        {
-         FILE *pidfile;
+         int fd, err = 0;
+
+         sprintf(daemon->namebuff, "%d\n", (int) getpid());
+
+         /* Explanation: Some installations of dnsmasq (eg Debian/Ubuntu) locate the pid-file
+            in a directory which is writable by the non-privileged user that dnsmasq runs as. This
+            allows the daemon to delete the file as part of its shutdown. This is a security hole to the 
+            extent that an attacker running as the unprivileged  user could replace the pidfile with a 
+            symlink, and have the target of that symlink overwritten as root next time dnsmasq starts. 
+
+            The folowing code first deletes any existing file, and then opens it with the O_EXCL flag,
+            ensuring that the open() fails should there be any existing file (because the unlink() failed, 
+            or an attacker exploited the race between unlink() and open()). This ensures that no symlink
+            attack can succeed. 
+
+            Any compromise of the non-privileged user still theoretically allows the pid-file to be
+            replaced whilst dnsmasq is running. The worst that could allow is that the usual 
+            "shutdown dnsmasq" shell command could be tricked into stopping any other process.
+
+            Note that if dnsmasq is started as non-root (eg for testing) it silently ignores 
+            failure to write the pid-file.
+         */
+
+         unlink(daemon->runfile); 
          
-         /* only complain if started as root */
-         if ((pidfile = fopen(daemon->runfile, "w")))
+         if ((fd = open(daemon->runfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)) == -1)
+           {
+             /* only complain if started as root */
+             if (getuid() == 0)
+               err = 1;
+           }
+         else
            {
-             fprintf(pidfile, "%d\n", (int) getpid());
-             fclose(pidfile);
+             if (!read_write(fd, (unsigned char *)daemon->namebuff, strlen(daemon->namebuff), 0))
+               err = 1;
+             else 
+               {
+                 while (retry_send(close(fd)));
+                 if (errno != 0)
+                   err = 1;
+               }
            }
-         else if (getuid() == 0)
+
+         if (err)
            {
-             send_event(err_pipe[1], EVENT_PIDFILE, errno);
+             send_event(err_pipe[1], EVENT_PIDFILE, errno, daemon->runfile);
              _exit(0);
            }
        }
@@ -364,8 +549,8 @@ int main (int argc, char **argv)
    
    /* if we are to run scripts, we need to fork a helper before dropping root. */
   daemon->helperfd = -1;
-#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT) 
-  if (daemon->dhcp && daemon->lease_change_command)
+#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);
 #endif
 
@@ -379,17 +564,24 @@ int main (int argc, char **argv)
          (setgroups(0, &dummy) == -1 ||
           setgid(gp->gr_gid) == -1))
        {
-         send_event(err_pipe[1], EVENT_GROUP_ERR, errno);
+         send_event(err_pipe[1], EVENT_GROUP_ERR, errno, daemon->groupname);
          _exit(0);
        }
   
       if (ent_pw && ent_pw->pw_uid != 0)
        {     
-#if defined(HAVE_LINUX_NETWORK)
+#if defined(HAVE_LINUX_NETWORK)          
          /* On linux, we keep CAP_NETADMIN (for ARP-injection) and
-            CAP_NET_RAW (for icmp) if we're doing dhcp */
-         data->effective = data->permitted = data->inheritable =
-           (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID);
+            CAP_NET_RAW (for icmp) if we're doing dhcp. If we have yet to bind 
+            ports because of DAD, or we're doing it dynamically,
+            we need CAP_NET_BIND_SERVICE too. */
+         if (is_dad_listeners() || option_bool(OPT_CLEVERBIND))
+           data->effective = data->permitted = data->inheritable =
+             (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | 
+             (1 << CAP_SETUID) | (1 << CAP_NET_BIND_SERVICE);
+         else
+           data->effective = data->permitted = data->inheritable =
+             (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID);
          
          /* Tell kernel to not clear capabilities when dropping root */
          if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
@@ -419,26 +611,30 @@ int main (int argc, char **argv)
 
          if (bad_capabilities != 0)
            {
-             send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities);
+             send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities, NULL);
              _exit(0);
            }
          
          /* finally drop root */
          if (setuid(ent_pw->pw_uid) == -1)
            {
-             send_event(err_pipe[1], EVENT_USER_ERR, errno);
+             send_event(err_pipe[1], EVENT_USER_ERR, errno, daemon->username);
              _exit(0);
            }     
 
 #ifdef HAVE_LINUX_NETWORK
-         data->effective = data->permitted = 
-           (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
+         if (is_dad_listeners() || option_bool(OPT_CLEVERBIND))
+          data->effective = data->permitted =
+            (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_NET_BIND_SERVICE);
+        else
+          data->effective = data->permitted = 
+            (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
          data->inheritable = 0;
          
          /* lose the setuid and setgid capbilities */
          if (capset(hdr, data) == -1)
            {
-             send_event(err_pipe[1], EVENT_CAP_ERR, errno);
+             send_event(err_pipe[1], EVENT_CAP_ERR, errno, NULL);
              _exit(0);
            }
 #endif
@@ -447,10 +643,51 @@ int main (int argc, char **argv)
     }
   
 #ifdef HAVE_LINUX_NETWORK
+  free(hdr);
+  free(data);
   if (option_bool(OPT_DEBUG)) 
     prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
 #endif
 
+#ifdef HAVE_TFTP
+  if (option_bool(OPT_TFTP))
+    {
+      DIR *dir;
+      struct tftp_prefix *p;
+      
+      if (daemon->tftp_prefix)
+       {
+         if (!((dir = opendir(daemon->tftp_prefix))))
+           {
+             tftp_prefix_missing = 1;
+             if (!option_bool(OPT_TFTP_NO_FAIL))
+               {
+                 send_event(err_pipe[1], EVENT_TFTP_ERR, errno, daemon->tftp_prefix);
+                 _exit(0);
+               }
+           }
+         else
+           closedir(dir);
+       }
+
+      for (p = daemon->if_prefix; p; p = p->next)
+       {
+         p->missing = 0;
+         if (!((dir = opendir(p->prefix))))
+           {
+             p->missing = 1;
+             if (!option_bool(OPT_TFTP_NO_FAIL))
+               {
+                 send_event(err_pipe[1], EVENT_TFTP_ERR, errno, p->prefix);
+                 _exit(0);
+               }
+           }
+         else
+           closedir(dir);
+       }
+    }
+#endif
+
   if (daemon->port == 0)
     my_syslog(LOG_INFO, _("started, version %s DNS disabled"), VERSION);
   else if (daemon->cachesize != 0)
@@ -470,12 +707,44 @@ int main (int argc, char **argv)
     }
 #endif
 
+  if (option_bool(OPT_LOCAL_SERVICE))
+    my_syslog(LOG_INFO, _("DNS service limited to local subnets"));
+  
+#ifdef HAVE_DNSSEC
+  if (option_bool(OPT_DNSSEC_VALID))
+    {
+      int rc;
+
+      /* Delay creating the timestamp file until here, after we've changed user, so that
+        it has the correct owner to allow updating the mtime later. 
+        This means we have to report fatal errors via the pipe. */
+      if ((rc = setup_timestamp()) == -1)
+       {
+         send_event(err_pipe[1], EVENT_TIME_ERR, errno, daemon->timestamp_file);
+         _exit(0);
+       }
+      
+      my_syslog(LOG_INFO, _("DNSSEC validation enabled"));
+      
+      if (option_bool(OPT_DNSSEC_TIME))
+       my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until first cache reload"));
+      
+      if (rc == 1)
+       my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until system time valid"));
+    }
+#endif
+
   if (log_err != 0)
     my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"), 
              daemon->log_file, strerror(log_err));
-
+  
   if (bind_fallback)
     my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
+
+  if (option_bool(OPT_NOWILD))
+    warn_bound_listeners();
+
+  warn_int_names();
   
   if (!option_bool(OPT_NOWILD)) 
     for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
@@ -493,40 +762,56 @@ int main (int argc, char **argv)
 
   if (daemon->max_logs != 0)
     my_syslog(LOG_INFO, _("asynchronous logging enabled, queue limit is %d messages"), daemon->max_logs);
+  
 
 #ifdef HAVE_DHCP
-  if (daemon->dhcp)
-    {
-      struct dhcp_context *dhcp_tmp;
-      
-      for (dhcp_tmp = daemon->dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
-       {
-         prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time);
-         strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start));
-         my_syslog(MS_DHCP | LOG_INFO, 
-                   (dhcp_tmp->flags & CONTEXT_STATIC) ? 
-                   _("DHCP, static leases only on %.0s%s, lease time %s") :
-                   (dhcp_tmp->flags & CONTEXT_PROXY) ?
-                   _("DHCP, proxy on subnet %.0s%s%.0s") :
-                   _("DHCP, IP range %s -- %s, lease time %s"),
-                   daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), daemon->dhcp_buff2);
-       }
-    }
+  for (context = daemon->dhcp; context; context = context->next)
+    log_context(AF_INET, context);
+
+  for (relay = daemon->relay4; relay; relay = relay->next)
+    log_relay(AF_INET, relay);
+
+#  ifdef HAVE_DHCP6
+  for (context = daemon->dhcp6; context; context = context->next)
+    log_context(AF_INET6, context);
+
+  for (relay = daemon->relay6; relay; relay = relay->next)
+    log_relay(AF_INET6, relay);
+  
+  if (daemon->doing_dhcp6 || daemon->doing_ra)
+    dhcp_construct_contexts(now);
+  
+  if (option_bool(OPT_RA))
+    my_syslog(MS_DHCP | LOG_INFO, _("IPv6 router advertisement enabled"));
+#  endif
+
+#  ifdef HAVE_LINUX_NETWORK
+  if (did_bind)
+    my_syslog(MS_DHCP | LOG_INFO, _("DHCP, sockets bound exclusively to interface %s"), bound_device);
+#  endif
+
+  /* after dhcp_contruct_contexts */
+  if (daemon->dhcp || daemon->doing_dhcp6)
+    lease_find_interfaces(now);
 #endif
 
 #ifdef HAVE_TFTP
-  if (daemon->tftp_unlimited || daemon->tftp_interfaces)
+  if (option_bool(OPT_TFTP))
     {
-#ifdef FD_SETSIZE
-      if (FD_SETSIZE < (unsigned)max_fd)
-       max_fd = FD_SETSIZE;
-#endif
+      struct tftp_prefix *p;
 
       my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s", 
                daemon->tftp_prefix ? _("root is ") : _("enabled"),
                daemon->tftp_prefix ? daemon->tftp_prefix: "",
                option_bool(OPT_TFTP_SECURE) ? _("secure mode") : "");
-      
+
+      if (tftp_prefix_missing)
+       my_syslog(MS_TFTP | LOG_WARNING, _("warning: %s inaccessible"), daemon->tftp_prefix);
+
+      for (p = daemon->if_prefix; p; p = p->next)
+       if (p->missing)
+          my_syslog(MS_TFTP | LOG_WARNING, _("warning: TFTP directory %s inaccessible"), p->prefix);
+
       /* This is a guess, it assumes that for small limits, 
         disjoint files might be served, but for large limits, 
         a single file will be sent to may clients (the file only needs
@@ -559,100 +844,133 @@ int main (int argc, char **argv)
 
   /* finished start-up - release original process */
   if (err_pipe[1] != -1)
-    close(err_pipe[1]);
+    while (retry_send(close(err_pipe[1])));
   
   if (daemon->port != 0)
     check_servers();
   
   pid = getpid();
   
+#ifdef HAVE_INOTIFY
+  /* Using inotify, have to select a resolv file at startup */
+  poll_resolv(1, 0, now);
+#endif
+  
   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())
+       timeout = 1000;
 
 #ifdef HAVE_DBUS
-      set_dbus_listeners(&maxfd, &rset, &wset, &eset);
+      set_dbus_listeners();
 #endif 
   
 #ifdef HAVE_DHCP
-      if (daemon->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_LINUX_NETWORK
-      FD_SET(daemon->netlinkfd, &rset);
-      bump_maxfd(daemon->netlinkfd, &maxfd);
+#ifdef HAVE_DHCP6
+      if (daemon->doing_dhcp6 || daemon->relay6)
+       poll_listen(daemon->dhcp6fd, POLLIN);
+       
+      if (daemon->doing_ra)
+       poll_listen(daemon->icmp6fd, POLLIN); 
+#endif
+    
+#ifdef HAVE_INOTIFY
+      if (daemon->inotifyfd != -1)
+       poll_listen(daemon->inotifyfd, POLLIN);
+#endif
+
+#if defined(HAVE_LINUX_NETWORK)
+      poll_listen(daemon->netlinkfd, POLLIN);
+#elif defined(HAVE_BSD_NETWORK)
+      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_TFTP
+      while (helper_buf_empty() && do_tftp_script_run());
+#    endif
+
       if (!helper_buf_empty())
-       {
-         FD_SET(daemon->helperfd, &wset);
-         bump_maxfd(daemon->helperfd, &maxfd);
-       }
+       poll_listen(daemon->helperfd, POLLOUT);
 #  else
       /* need this for other side-effects */
       while (do_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);
 
-#ifdef HAVE_LINUX_NETWORK
-      if (FD_ISSET(daemon->netlinkfd, &rset))
+      /* prime. */
+      enumerate_interfaces(1);
+
+      /* Check the interfaces to see if any have exited DAD state
+        and if so, bind the address. */
+      if (is_dad_listeners())
+       {
+         enumerate_interfaces(0);
+         /* NB, is_dad_listeners() == 1 --> we're binding interfaces */
+         create_bound_listeners(0);
+         warn_bound_listeners();
+       }
+
+#if defined(HAVE_LINUX_NETWORK)
+      if (poll_check(daemon->netlinkfd, POLLIN))
        netlink_multicast();
+#elif defined(HAVE_BSD_NETWORK)
+      if (poll_check(daemon->routefd, POLLIN))
+       route_sock();
 #endif
 
+#ifdef HAVE_INOTIFY
+      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);
+       }         
+#else
       /* Check for changes to resolv files once per second max. */
       /* Don't go silent for long periods if the clock goes backwards. */
       if (daemon->last_resolv == 0 || 
@@ -665,8 +983,9 @@ int main (int argc, char **argv)
          poll_resolv(0, daemon->last_resolv != 0, now);          
          daemon->last_resolv = now;
        }
-      
-      if (FD_ISSET(piperead, &rset))
+#endif
+
+      if (poll_check(piperead, POLLIN))
        async_event(piperead, now);
       
 #ifdef HAVE_DBUS
@@ -679,26 +998,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)
+      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) && poll_check(daemon->dhcp6fd, POLLIN))
+       dhcp6_packet(now);
+
+      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, POLLIN))
        helper_write();
 #  endif
 #endif
@@ -741,28 +1068,75 @@ static void sig_handler(int sig)
       else
        return;
 
-      send_event(pipewrite, event, 0); 
+      send_event(pipewrite, event, 0, NULL); 
       errno = errsave;
     }
 }
 
-void send_event(int fd, int event, int data)
+/* now == 0 -> queue immediate callback */
+void send_alarm(time_t event, time_t now)
+{
+  if (now == 0 || event != 0)
+    {
+      /* alarm(0) or alarm(-ve) doesn't do what we want.... */
+      if ((now == 0 || difftime(event, now) <= 0.0))
+       send_event(pipewrite, EVENT_ALARM, 0, NULL);
+      else 
+       alarm((unsigned)difftime(event, now)); 
+    }
+}
+
+void queue_event(int event)
+{
+  send_event(pipewrite, event, 0, NULL);
+}
+
+void send_event(int fd, int event, int data, char *msg)
 {
   struct event_desc ev;
-  
+  struct iovec iov[2];
+
   ev.event = event;
   ev.data = data;
+  ev.msg_sz = msg ? strlen(msg) : 0;
+  
+  iov[0].iov_base = &ev;
+  iov[0].iov_len = sizeof(ev);
+  iov[1].iov_base = msg;
+  iov[1].iov_len = ev.msg_sz;
   
   /* error pipe, debug mode. */
   if (fd == -1)
-    fatal_event(&ev);
+    fatal_event(&ev, msg);
   else
     /* pipe is non-blocking and struct event_desc is smaller than
        PIPE_BUF, so this either fails or writes everything */
-    while (write(fd, &ev, sizeof(ev)) == -1 && errno == EINTR);
+    while (writev(fd, iov, msg ? 2 : 1) == -1 && errno == EINTR);
 }
 
-static void fatal_event(struct event_desc *ev)
+/* NOTE: the memory used to return msg is leaked: use msgs in events only
+   to describe fatal errors. */
+static int read_event(int fd, struct event_desc *evp, char **msg)
+{
+  char *buf;
+
+  if (!read_write(fd, (unsigned char *)evp, sizeof(struct event_desc), 1))
+    return 0;
+  
+  *msg = NULL;
+  
+  if (evp->msg_sz != 0 && 
+      (buf = malloc(evp->msg_sz + 1)) &&
+      read_write(fd, (unsigned char *)buf, evp->msg_sz, 1))
+    {
+      buf[evp->msg_sz] = 0;
+      *msg = buf;
+    }
+
+  return 1;
+}
+    
+static void fatal_event(struct event_desc *ev, char *msg)
 {
   errno = ev->data;
   
@@ -781,19 +1155,25 @@ static void fatal_event(struct event_desc *ev)
       die(_("setting capabilities failed: %s"), NULL, EC_MISC);
 
     case EVENT_USER_ERR:
-    case EVENT_HUSER_ERR:
-      die(_("failed to change user-id to %s: %s"), 
-         ev->event == EVENT_USER_ERR ? daemon->username : daemon->scriptuser,
-         EC_MISC);
+      die(_("failed to change user-id to %s: %s"), msg, EC_MISC);
 
     case EVENT_GROUP_ERR:
-      die(_("failed to change group-id to %s: %s"), daemon->groupname, EC_MISC);
+      die(_("failed to change group-id to %s: %s"), msg, EC_MISC);
       
     case EVENT_PIDFILE:
-      die(_("failed to open pidfile %s: %s"), daemon->runfile, EC_FILE);
+      die(_("failed to open pidfile %s: %s"), msg, EC_FILE);
 
     case EVENT_LOG_ERR:
-      die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log", EC_FILE);
+      die(_("cannot open log %s: %s"), msg, EC_FILE);
+    
+    case EVENT_LUA_ERR:
+      die(_("failed to load Lua script: %s"), msg, EC_MISC);
+
+    case EVENT_TFTP_ERR:
+      die(_("TFTP directory %s inaccessible: %s"), msg, EC_FILE);
+    
+    case EVENT_TIME_ERR:
+      die(_("cannot create timestamp file %s: %s" ), msg, EC_BADCONF);
     }
 }      
       
@@ -801,18 +1181,46 @@ static void async_event(int pipe, time_t now)
 {
   pid_t p;
   struct event_desc ev;
-  int i;
-
-  if (read_write(pipe, (unsigned char *)&ev, sizeof(ev), 1))
+  int i, check = 0;
+  char *msg;
+  
+  /* NOTE: the memory used to return msg is leaked: use msgs in events only
+     to describe fatal errors. */
+  
+  if (read_event(pipe, &ev, &msg))
     switch (ev.event)
       {
       case EVENT_RELOAD:
+#ifdef HAVE_DNSSEC
+       if (option_bool(OPT_DNSSEC_VALID) && option_bool(OPT_DNSSEC_TIME))
+         {
+           my_syslog(LOG_INFO, _("now checking DNSSEC signature timestamps"));
+           reset_option_bool(OPT_DNSSEC_TIME);
+         } 
+#endif
+       /* fall through */
+       
+      case EVENT_INIT:
        clear_cache_and_reload(now);
-       if (daemon->port != 0 && daemon->resolv_files && option_bool(OPT_NO_POLL))
+       
+       if (daemon->port != 0)
          {
-           reload_servers(daemon->resolv_files->name);
-           check_servers();
+           if (daemon->resolv_files && option_bool(OPT_NO_POLL))
+             {
+               reload_servers(daemon->resolv_files->name);
+               check = 1;
+             }
+
+           if (daemon->servers_file)
+             {
+               read_servers_file();
+               check = 1;
+             }
+
+           if (check)
+             check_servers();
          }
+
 #ifdef HAVE_DHCP
        rerun_scripts();
 #endif
@@ -825,11 +1233,16 @@ static void async_event(int pipe, time_t now)
        
       case EVENT_ALARM:
 #ifdef HAVE_DHCP
-       if (daemon->dhcp)
+       if (daemon->dhcp || daemon->doing_dhcp6)
          {
            lease_prune(NULL, now);
            lease_update_file(now);
          }
+#ifdef HAVE_DHCP6
+       else if (daemon->doing_ra)
+         /* Not doing DHCP, so no lease system, manage alarms for ra only */
+           send_alarm(periodic_ra(now), now);
+#endif
 #endif
        break;
                
@@ -848,11 +1261,11 @@ static void async_event(int pipe, time_t now)
        break;
        
       case EVENT_KILLED:
-       my_syslog(LOG_WARNING, _("child process killed by signal %d"), ev.data);
+       my_syslog(LOG_WARNING, _("script process killed by signal %d"), ev.data);
        break;
 
       case EVENT_EXITED:
-       my_syslog(LOG_WARNING, _("child process exited with status %d"), ev.data);
+       my_syslog(LOG_WARNING, _("script process exited with status %d"), ev.data);
        break;
 
       case EVENT_EXEC_ERR:
@@ -861,9 +1274,10 @@ static void async_event(int pipe, time_t now)
        break;
 
        /* necessary for fatal errors in helper */
-      case EVENT_HUSER_ERR:
+      case EVENT_USER_ERR:
       case EVENT_DIE:
-       fatal_event(&ev);
+      case EVENT_LUA_ERR:
+       fatal_event(&ev, msg);
        break;
 
       case EVENT_REOPEN:
@@ -873,14 +1287,24 @@ static void async_event(int pipe, time_t now)
        if (daemon->log_file != NULL)
          log_reopen(daemon->log_file);
        break;
-       
+
+      case EVENT_NEWADDR:
+       newaddress(now);
+       break;
+
+      case EVENT_NEWROUTE:
+       resend_query();
+       /* Force re-reading resolv file right now, for luck. */
+       poll_resolv(0, 1, now);
+       break;
+
       case EVENT_TERM:
        /* Knock all our children on the head. */
        for (i = 0; i < MAX_PROCS; i++)
          if (daemon->tcp_pids[i] != 0)
            kill(daemon->tcp_pids[i], SIGALRM);
        
-#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
+#if defined(HAVE_SCRIPT)
        /* handle pending lease transitions */
        if (daemon->helperfd != -1)
          {
@@ -890,13 +1314,22 @@ static void async_event(int pipe, time_t now)
            do {
              helper_write();
            } while (!helper_buf_empty() || do_script_run(now));
-           close(daemon->helperfd);
+           while (retry_send(close(daemon->helperfd)));
          }
 #endif
        
        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);
        
@@ -906,7 +1339,7 @@ static void async_event(int pipe, time_t now)
       }
 }
 
-void poll_resolv(int force, int do_reload, time_t now)
+static void poll_resolv(int force, int do_reload, time_t now)
 {
   struct resolvc *res, *latest;
   struct stat statbuf;
@@ -978,25 +1411,35 @@ void poll_resolv(int force, int do_reload, time_t now)
 
 void clear_cache_and_reload(time_t now)
 {
+  (void)now;
+
   if (daemon->port != 0)
     cache_reload();
   
 #ifdef HAVE_DHCP
-  if (daemon->dhcp)
+  if (daemon->dhcp || daemon->doing_dhcp6)
     {
       if (option_bool(OPT_ETHERS))
        dhcp_read_ethers();
       reread_dhcp();
+#ifdef HAVE_INOTIFY
+      set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
+#endif
       dhcp_update_configs(daemon->dhcp_conf);
-      check_dhcp_hosts(0);
       lease_update_from_configs(); 
       lease_update_file(now); 
-      lease_update_dns();
+      lease_update_dns(1);
     }
+#ifdef HAVE_DHCP6
+  else if (daemon->doing_ra)
+    /* Not doing DHCP, so no lease system, manage 
+       alarms for ra only */
+    send_alarm(periodic_ra(now), now);
+#endif
 #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;
@@ -1008,55 +1451,41 @@ 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
   
   /* will we be able to get memory? */
   if (daemon->port != 0)
-    get_new_frec(now, &wait);
+    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
 
     }
@@ -1064,67 +1493,113 @@ 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;
+         int confd, client_ok = 1;
          struct irec *iface = NULL;
          pid_t p;
-         
-         while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
+         union mysockaddr tcp_addr;
+         socklen_t tcp_len = sizeof(union mysockaddr);
+
+         while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
          
          if (confd == -1)
            continue;
          
+         if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
+           {
+             while (retry_send(close(confd)));
+             continue;
+           }
+         
+         /* Make sure that the interface list is up-to-date.
+            
+            We do this here as we may need the results below, and
+            the DNS code needs them for --interface-name stuff.
+
+            Multiple calls to enumerate_interfaces() per select loop are
+            inhibited, so calls to it in the child process (which doesn't select())
+            have no effect. This avoids two processes reading from the same
+            netlink fd and screwing the pooch entirely.
+         */
+         enumerate_interfaces(0);
+         
          if (option_bool(OPT_NOWILD))
-           iface = listener->iface;
-         else
+           iface = listener->iface; /* May be NULL */
+         else 
            {
-             union mysockaddr tcp_addr;
-             socklen_t tcp_len = sizeof(union mysockaddr);
-             /* Check for allowed interfaces when binding the wildcard address:
-                we do this by looking for an interface with the same address as 
-                the local address of the TCP connection, then looking to see if that's
-                an allowed interface. As a side effect, we get the netmask of the
-                interface too, for localisation. */
+             int if_index;
+             char intr_name[IF_NAMESIZE];
+             
+             /* if we can find the arrival interface, check it's one that's allowed */
+             if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
+                 indextoname(listener->tcpfd, if_index, intr_name))
+               {
+                 struct all_addr addr;
+                 addr.addr.addr4 = tcp_addr.in.sin_addr;
+#ifdef HAVE_IPV6
+                 if (tcp_addr.sa.sa_family == AF_INET6)
+                   addr.addr.addr6 = tcp_addr.in6.sin6_addr;
+#endif
+                 
+                 for (iface = daemon->interfaces; iface; iface = iface->next)
+                   if (iface->index == if_index)
+                     break;
+                 
+                 if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
+                   client_ok = 0;
+               }
              
-             /* interface may be new since startup */
-             if (enumerate_interfaces() &&
-                 getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1)
-               for (iface = daemon->interfaces; iface; iface = iface->next)
-                 if (sockaddr_isequal(&iface->addr, &tcp_addr))
-                   break;
+             if (option_bool(OPT_CLEVERBIND))
+               iface = listener->iface; /* May be NULL */
+             else
+               {
+                 /* Check for allowed interfaces when binding the wildcard address:
+                    we do this by looking for an interface with the same address as 
+                    the local address of the TCP connection, then looking to see if that's
+                    an allowed interface. As a side effect, we get the netmask of the
+                    interface too, for localisation. */
+                 
+                 for (iface = daemon->interfaces; iface; iface = iface->next)
+                   if (sockaddr_isequal(&iface->addr, &tcp_addr))
+                     break;
+                 
+                 if (!iface)
+                   client_ok = 0;
+               }
            }
          
-         if (!iface)
+         if (!client_ok)
            {
              shutdown(confd, SHUT_RDWR);
-             close(confd);
+             while (retry_send(close(confd)));
            }
 #ifndef NO_FORK
          else if (!option_bool(OPT_DEBUG) && (p = fork()) != 0)
@@ -1139,7 +1614,10 @@ static void check_dns_listeners(fd_set *set, time_t now)
                        break;
                      }
                }
-             close(confd);
+             while (retry_send(close(confd)));
+
+             /* The child can use up to TCP_MAX_QUERIES ids, so skip that many. */
+             daemon->log_id += TCP_MAX_QUERIES;
            }
 #endif
          else
@@ -1147,10 +1625,20 @@ static void check_dns_listeners(fd_set *set, time_t now)
              unsigned char *buff;
              struct server *s; 
              int flags;
-             struct in_addr dst_addr_4;
-             
-             dst_addr_4.s_addr = 0;
-             
+             struct in_addr netmask;
+             int auth_dns;
+
+             if (iface)
+               {
+                 netmask = iface->netmask;
+                 auth_dns = iface->dns_auth;
+               }
+             else
+               {
+                 netmask.s_addr = 0;
+                 auth_dns = 0;
+               }
+
 #ifndef NO_FORK
              /* Arrange for SIGALARM after CHILD_LIFETIME seconds to
                 terminate the process. */
@@ -1168,13 +1656,10 @@ static void check_dns_listeners(fd_set *set, time_t now)
              if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
                fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
              
-             if (listener->family == AF_INET)
-               dst_addr_4 = iface->addr.in.sin_addr;
-             
-             buff = tcp_request(confd, now, dst_addr_4, iface->netmask);
+             buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
               
              shutdown(confd, SHUT_RDWR);
-             close(confd);
+             while (retry_send(close(confd)));
              
              if (buff)
                free(buff);
@@ -1183,7 +1668,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
                if (s->tcpfd != -1)
                  {
                    shutdown(s->tcpfd, SHUT_RDWR);
-                   close(s->tcpfd);
+                   while (retry_send(close(s->tcpfd)));
                  }
 #ifndef NO_FORK                   
              if (!option_bool(OPT_DEBUG))
@@ -1225,14 +1710,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;
 
@@ -1261,44 +1754,47 @@ int icmp_ping(struct in_addr addr)
     j = (j & 0xffff) + (j >> 16);  
   packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j;
   
-  while (sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0, 
-               (struct sockaddr *)&saddr, sizeof(saddr)) == -1 &&
-        retry_send());
+  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; 
+      poll_reset();
+      poll_listen(fd, POLLIN);
+      set_dns_listeners(now);
+      set_log_writer();
       
-      FD_ZERO(&rset);
-      FD_ZERO(&wset);
-      FD_SET(fd, &rset);
-      set_dns_listeners(now, &rset, &maxfd);
-      set_log_writer(&wset, &maxfd);
-
-      if (select(maxfd+1, &rset, &wset, NULL, &tv) < 0)
-       {
-         FD_ZERO(&rset);
-         FD_ZERO(&wset);
-       }
+#ifdef HAVE_DHCP6
+      if (daemon->doing_ra)
+       poll_listen(daemon->icmp6fd, POLLIN); 
+#endif
+      
+      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 && 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 &&
@@ -1312,7 +1808,7 @@ int icmp_ping(struct in_addr addr)
     }
   
 #if defined(HAVE_LINUX_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
-  close(fd);
+  while (retry_send(close(fd)));
 #else
   opt = 1;
   setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
index 66c602e..cf1a782 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
@@ -14,7 +14,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#define COPYRIGHT "Copyright (c) 2000-2011 Simon Kelley" 
+#define COPYRIGHT "Copyright (c) 2000-2015 Simon Kelley" 
 
 #ifndef NO_LARGEFILE
 /* Ensure we can use files >2GB (log files may grow this big) */
 /* get these before config.h  for IPv6 stuff... */
 #include <sys/types.h> 
 #include <sys/socket.h>
+
+#ifdef __APPLE__
+/* Define before netinet/in.h to select API. OSX Lion onwards. */
+#  define __APPLE_USE_RFC_3542
+#endif
 #include <netinet/in.h>
 
-/* and this. */
+/* Also needed before config.h. */
 #include <getopt.h>
 
 #include "config.h"
+#include "ip6addr.h"
 
 typedef unsigned char u8;
 typedef unsigned short u16;
 typedef unsigned int u32;
+typedef unsigned long long u64;
 
-#include "dns_protocol.h"
-#include "dhcp_protocol.h"
+#define countof(x)      (long)(sizeof(x) / sizeof(x[0]))
+#define MIN(a,b)        ((a) < (b) ? (a) : (b))
+
+#include "dns-protocol.h"
+#include "dhcp-protocol.h"
+#ifdef HAVE_DHCP6
+#include "dhcp6-protocol.h"
+#include "radv-protocol.h"
+#endif
 
 #define gettext_noop(S) (S)
 #ifndef LOCALEDIR
@@ -68,7 +82,7 @@ typedef unsigned int u32;
 #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>
@@ -103,6 +117,7 @@ typedef unsigned int u32;
 #include <sys/uio.h>
 #include <syslog.h>
 #include <dirent.h>
+#include <utime.h>
 #ifndef HAVE_LINUX_NETWORK
 #  include <net/if_dl.h>
 #endif
@@ -127,7 +142,7 @@ extern int capget(cap_user_header_t header, cap_user_data_t data);
 
 /* Async event queue */
 struct event_desc {
-  int event, data;
+  int event, data, msg_sz;
 };
 
 #define EVENT_RELOAD    1
@@ -148,6 +163,12 @@ struct event_desc {
 #define EVENT_DIE       16
 #define EVENT_LOG_ERR   17
 #define EVENT_FORK_ERR  18
+#define EVENT_LUA_ERR   19
+#define EVENT_TFTP_ERR  20
+#define EVENT_INIT      21
+#define EVENT_NEWADDR   22
+#define EVENT_NEWROUTE  23
+#define EVENT_TIME_ERR  24
 
 /* Exit codes. */
 #define EC_GOOD        0
@@ -201,8 +222,27 @@ struct event_desc {
 #define OPT_NO_OVERRIDE    30
 #define OPT_NO_REBIND      31
 #define OPT_ADD_MAC        32
-#define OPT_DNSSEC         33
-#define OPT_LAST           34
+#define OPT_DNSSEC_PROXY   33
+#define OPT_CONSEC_ADDR    34
+#define OPT_CONNTRACK      35
+#define OPT_FQDN_UPDATE    36
+#define OPT_RA             37
+#define OPT_TFTP_LC        38
+#define OPT_CLEVERBIND     39
+#define OPT_TFTP           40
+#define OPT_CLIENT_SUBNET  41
+#define OPT_QUIET_DHCP     42
+#define OPT_QUIET_DHCP6    43
+#define OPT_QUIET_RA      44
+#define OPT_DNSSEC_VALID   45
+#define OPT_DNSSEC_TIME    46
+#define OPT_DNSSEC_DEBUG   47
+#define OPT_DNSSEC_NO_SIGN 48 
+#define OPT_LOCAL_SERVICE  49
+#define OPT_LOOP_DETECT    50
+#define OPT_EXTRALOG       51
+#define OPT_TFTP_NO_FAIL   52
+#define OPT_LAST           53
 
 /* 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. */
@@ -215,6 +255,12 @@ struct all_addr {
 #ifdef HAVE_IPV6
     struct in6_addr addr6;
 #endif
+    /* for log_query */
+    unsigned int keytag;
+    /* for cache_insert if RRSIG, DNSKEY, DS */
+    struct {
+      unsigned short class, type;
+    } dnssec;      
   } addr;
 };
 
@@ -242,10 +288,19 @@ struct naptr {
   struct naptr *next;
 };
 
+#define TXT_STAT_CACHESIZE     1
+#define TXT_STAT_INSERTS       2
+#define TXT_STAT_EVICTIONS     3
+#define TXT_STAT_MISSES        4
+#define TXT_STAT_HITS          5
+#define TXT_STAT_AUTH          6
+#define TXT_STAT_SERVERS       7
+
 struct txt_record {
   char *name;
   unsigned char *txt;
   unsigned short class, len;
+  int stat;
   struct txt_record *next;
 };
 
@@ -257,11 +312,56 @@ struct ptr_record {
 struct cname {
   char *alias, *target;
   struct cname *next;
+}; 
+
+struct ds_config {
+  char *name, *digest;
+  int digestlen, class, algo, keytag, digest_type;
+  struct ds_config *next;
+};
+
+#define ADDRLIST_LITERAL 1
+#define ADDRLIST_IPV6    2
+#define ADDRLIST_REVONLY 4
+
+struct addrlist {
+  struct all_addr addr;
+  int flags, prefixlen; 
+  struct addrlist *next;
+};
+
+#define AUTH6     1
+#define AUTH4     2
+
+struct auth_zone {
+  char *domain;
+  struct auth_name_list {
+    char *name;
+    int flags;
+    struct auth_name_list *next;
+  } *interface_names;
+  struct addrlist *subnet;
+  struct auth_zone *next;
+};
+
+
+struct host_record {
+  struct name_list {
+    char *name;
+    struct name_list *next;
+  } *names;
+  struct in_addr addr;
+#ifdef HAVE_IPV6
+  struct in6_addr addr6;
+#endif
+  struct host_record *next;
 };
 
 struct interface_name {
   char *name; /* domain name */
   char *intr; /* interface name */
+  int family; /* AF_INET, AF_INET6 or zero for both */
+  struct addrlist *addr;
   struct interface_name *next;
 };
 
@@ -270,17 +370,43 @@ union bigname {
   union bigname *next; /* freelist */
 };
 
+struct blockdata {
+  struct blockdata *next;
+  unsigned char key[KEYBLOCK_LEN];
+};
+
 struct crec { 
   struct crec *next, *prev, *hash_next;
-  time_t ttd; /* time to die */
-  int uid; 
+  /* union is 16 bytes when doing IPv6, 8 bytes on 32 bit machines without IPv6 */
   union {
     struct all_addr addr;
     struct {
-      struct crec *cache;
-      int uid;
+      union {
+       struct crec *cache;
+       struct interface_name *int_name;
+      } target;
+      unsigned int uid; /* 0 if union is interface-name */
     } cname;
+    struct {
+      struct blockdata *keydata;
+      unsigned short keylen, flags, keytag;
+      unsigned char algo;
+    } key; 
+    struct {
+      struct blockdata *keydata;
+      unsigned short keylen, keytag;
+      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 */
+  unsigned int uid; 
   unsigned short flags;
   union {
     char sname[SMALLDNAME];
@@ -301,15 +427,32 @@ struct crec {
 #define F_BIGNAME   (1u<<9)
 #define F_NXDOMAIN  (1u<<10)
 #define F_CNAME     (1u<<11)
-#define F_NOERR     (1u<<12)
+#define F_DNSKEY    (1u<<12)
 #define F_CONFIG    (1u<<13)
+#define F_DS        (1u<<14)
+#define F_DNSSECOK  (1u<<15)
+
 /* below here are only valid as args to log_query: cache
    entries are limited to 16 bits */
 #define F_UPSTREAM  (1u<<16)
 #define F_RRNAME    (1u<<17)
 #define F_SERVER    (1u<<18)
 #define F_QUERY     (1u<<19)
-#define F_NSRR      (1u<<20)
+#define F_NOERR     (1u<<20)
+#define F_AUTH      (1u<<21)
+#define F_DNSSEC    (1u<<22)
+#define F_KEYTAG    (1u<<23)
+#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)
+
+/* Values of uid in crecs with F_CONFIG bit set. */
+#define SRC_INTERFACE 0
+#define SRC_CONFIG    1
+#define SRC_HOSTS     2
+#define SRC_AH        3
 
 
 /* struct sockaddr is not large enough to hold any address,
@@ -323,6 +466,12 @@ union mysockaddr {
 #endif
 };
 
+/* bits in flag param to IPv6 callbacks from iface_enumerate() */
+#define IFACE_TENTATIVE   1
+#define IFACE_DEPRECATED  2
+#define IFACE_PERMANENT   4
+
+
 #define SERV_FROM_RESOLV       1  /* 1 for servers from resolv, 0 for command line. */
 #define SERV_NO_ADDR           2  /* no server, this domain is local only */
 #define SERV_LITERAL_ADDRESS   4  /* addr is the answer, not the server */ 
@@ -336,6 +485,8 @@ union mysockaddr {
 #define SERV_COUNTED         512  /* workspace for log code */
 #define SERV_USE_RESOLV     1024  /* forward this domain in the normal way */
 #define SERV_NO_REBIND      2048  /* inhibit dns-rebind protection */
+#define SERV_FROM_FILE      4096  /* read from --servers-file */
+#define SERV_LOOP           8192  /* server causes forwarding loop */
 
 struct serverfd {
   int fd;
@@ -354,22 +505,31 @@ struct server {
   char interface[IF_NAMESIZE+1];
   struct serverfd *sfd; 
   char *domain; /* set if this server only handles a domain. */ 
-  int flags, tcpfd;
+  int flags, tcpfd, edns_pktsz;
   unsigned int queries, failed_queries;
+#ifdef HAVE_LOOP
+  u32 uid;
+#endif
   struct server *next; 
 };
 
+struct ipsets {
+  char **sets;
+  char *domain;
+  struct ipsets *next;
+};
+
 struct irec {
   union mysockaddr addr;
   struct in_addr netmask; /* only valid for IPv4 */
-  int tftp_ok, mtu;
-  char *name;
+  int tftp_ok, dhcp_ok, mtu, done, warned, dad, dns_auth, index, multicast_done, found;
+  char *name; 
   struct irec *next;
 };
 
 struct listener {
   int fd, tcpfd, tftpfd, family;
-  struct irec *iface; /* only valid for non-wildcard */
+  struct irec *iface; /* only sometimes valid for non-wildcard */
   struct listener *next;
 };
 
@@ -377,7 +537,7 @@ struct listener {
 struct iname {
   char *name;
   union mysockaddr addr;
-  int isloop, used;
+  int used;
   struct iname *next;
 };
 
@@ -387,20 +547,61 @@ struct resolvc {
   int is_default, logged;
   time_t mtime;
   char *name;
+#ifdef HAVE_INOTIFY
+  int wd; /* inotify watch descriptor */
+  char *file; /* pointer to file part if path */
+#endif
 };
 
-/* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile */
+/* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile and dhcp-hostsdir*/
 #define AH_DIR      1
 #define AH_INACTIVE 2
+#define AH_WD_DONE  4
+#define AH_HOSTS    8
+#define AH_DHCP_HST 16
+#define AH_DHCP_OPT 32
 struct hostsfile {
   struct hostsfile *next;
   int flags;
   char *fname;
-  int index; /* matches to cache entries for logging */
+#ifdef HAVE_INOTIFY
+  int wd; /* inotify watch descriptor */
+#endif
+  unsigned int index; /* matches to cache entries for logging */
 };
 
+
+/* DNSSEC status values. */
+#define STAT_SECURE             1
+#define STAT_INSECURE           2
+#define STAT_BOGUS              3
+#define STAT_NEED_DS            4
+#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 FREC_NOREBIND           1
 #define FREC_CHECKING_DISABLED  2
+#define FREC_HAS_SUBNET         4
+#define FREC_DNSKEY_QUERY       8
+#define FREC_DS_QUERY          16
+#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
+
+#ifdef HAVE_DNSSEC
+#define HASH_SIZE 20 /* SHA-1 digest size */
+#else
+#define HASH_SIZE sizeof(int)
+#endif
 
 struct frec {
   union mysockaddr source;
@@ -412,28 +613,52 @@ struct frec {
 #endif
   unsigned int iface;
   unsigned short orig_id, new_id;
-  int fd, forwardall, flags;
-  unsigned int crc;
+  int log_id, fd, forwardall, flags;
   time_t time;
+  unsigned char *hash[HASH_SIZE];
+#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;
+  struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
+  struct frec *blocking_query; /* Query which is blocking us. */
+#endif
   struct frec *next;
 };
 
+/* flags in top of length field for DHCP-option tables */
+#define OT_ADDR_LIST    0x8000
+#define OT_RFC1035_NAME 0x4000
+#define OT_INTERNAL     0x2000
+#define OT_NAME         0x1000
+#define OT_CSTRING      0x0800
+#define OT_DEC          0x0400 
+#define OT_TIME         0x0200
+
 /* actions in the daemon->helper RPC */
 #define ACTION_DEL           1
 #define ACTION_OLD_HOSTNAME  2
 #define ACTION_OLD           3
 #define ACTION_ADD           4
-#define ACTION_CONNECT      5
+#define ACTION_TFTP          5
+
+#define LEASE_NEW            1  /* newly created */
+#define LEASE_CHANGED        2  /* modified */
+#define LEASE_AUX_CHANGED    4  /* CLID or expiry changed */
+#define LEASE_AUTH_NAME      8  /* hostname came from config, not from client */
+#define LEASE_USED          16  /* used this DHCPv6 transaction */
+#define LEASE_NA            32  /* IPv6 no-temporary lease */
+#define LEASE_TA            64  /* IPv6 temporary lease */
+#define LEASE_HAVE_HWADDR  128  /* Have set hwaddress */
 
 struct dhcp_lease {
   int clid_len;          /* length of client identifier */
   unsigned char *clid;   /* clientid */
   char *hostname, *fqdn; /* name from client-hostname option or config */
   char *old_hostname;    /* hostname before it moved to another lease */
-  char auth_name;        /* hostname came from config, not from client */
-  char new;              /* newly created */
-  char changed;          /* modified */
-  char aux_changed;      /* CLID or expiry changed */
+  int flags;
   time_t expires;        /* lease expiry */
 #ifdef HAVE_BROKEN_RTC
   unsigned int length;
@@ -444,6 +669,19 @@ struct dhcp_lease {
   unsigned char *extradata;
   unsigned int extradata_len, extradata_size;
   int last_interface;
+  int new_interface;     /* save possible originated interface */
+  int new_prefixlen;     /* and its prefix length */
+#ifdef HAVE_DHCP6
+  struct in6_addr addr6;
+  int iaid;
+  struct slaac_address {
+    struct in6_addr addr;
+    time_t ping_time;
+    int backoff; /* zero -> confirmed */
+    struct slaac_address *next;
+  } *slaac_address;
+  int vendorclass_count;
+#endif
   struct dhcp_lease *next;
 };
 
@@ -476,6 +714,9 @@ struct dhcp_config {
   unsigned char *clid;   /* clientid */
   char *hostname, *domain;
   struct dhcp_netid_list *netid;
+#ifdef HAVE_DHCP6
+  struct in6_addr addr6;
+#endif
   struct in_addr addr;
   time_t decline_time;
   unsigned int lease_time;
@@ -483,6 +724,8 @@ struct dhcp_config {
   struct dhcp_config *next;
 };
 
+#define have_config(config, mask) ((config) && ((config)->flags & (mask))) 
+
 #define CONFIG_DISABLE           1
 #define CONFIG_CLID              2
 #define CONFIG_TIME              8
@@ -493,6 +736,8 @@ struct dhcp_config {
 #define CONFIG_ADDR_HOSTS      512    /* address added by from /etc/hosts */
 #define CONFIG_DECLINED       1024    /* address declined by client */
 #define CONFIG_BANK           2048    /* from dhcp hosts file */
+#define CONFIG_ADDR6          4096
+#define CONFIG_WILDCARD       8192
 
 struct dhcp_opt {
   int opt, len, flags;
@@ -518,9 +763,11 @@ struct dhcp_opt {
 #define DHOPT_HEX              512
 #define DHOPT_VENDOR_MATCH    1024
 #define DHOPT_RFC3925         2048
+#define DHOPT_TAGOK           4096
+#define DHOPT_ADDR6           8192
 
 struct dhcp_boot {
-  char *file, *sname;
+  char *file, *sname, *tftp_sname;
   struct in_addr next_server;
   struct dhcp_netid *netid;
   struct dhcp_boot *next;
@@ -528,7 +775,7 @@ struct dhcp_boot {
 
 struct pxe_service {
   unsigned short CSA, type; 
-  char *menu, *basename;
+  char *menu, *basename, *sname;
   struct in_addr server;
   struct dhcp_netid *netid;
   struct pxe_service *next;
@@ -542,7 +789,8 @@ struct pxe_service {
 
 /* vendorclass, userclass, remote-id or cicuit-id */
 struct dhcp_vendor {
-  int len, match_type, option;
+  int len, match_type;
+  unsigned int enterprise;
   char *data;
   struct dhcp_netid netid;
   struct dhcp_vendor *next;
@@ -562,9 +810,27 @@ struct dhcp_bridge {
 };
 
 struct cond_domain {
-  char *domain;
+  char *domain, *prefix;
   struct in_addr start, end;
+#ifdef HAVE_IPV6
+  struct in6_addr start6, end6;
+#endif
+  int is6;
   struct cond_domain *next;
+}; 
+
+#ifdef OPTION6_PREFIX_CLASS 
+struct prefix_class {
+  int class;
+  struct dhcp_netid tag;
+  struct prefix_class *next;
+};
+#endif
+
+struct ra_interface {
+  char *name;
+  int interval, lifetime, prio;
+  struct ra_interface *next;
 };
 
 struct dhcp_context {
@@ -572,20 +838,43 @@ struct dhcp_context {
   struct in_addr netmask, broadcast;
   struct in_addr local, router;
   struct in_addr start, end; /* range of available addresses */
+#ifdef HAVE_DHCP6
+  struct in6_addr start6, end6; /* range of available addresses */
+  struct in6_addr local6;
+  int prefix, if_index;
+  unsigned int valid, preferred, saved_valid;
+  time_t ra_time, ra_short_period_start, address_lost_time;
+  char *template_interface;
+#endif
   int flags;
-  char *interface;
   struct dhcp_netid netid, *filter;
   struct dhcp_context *next, *current;
 };
 
-#define CONTEXT_STATIC    1
-#define CONTEXT_NETMASK   2
-#define CONTEXT_BRDCAST   4
-#define CONTEXT_PROXY     8
+#define CONTEXT_STATIC         (1u<<0)
+#define CONTEXT_NETMASK        (1u<<1)
+#define CONTEXT_BRDCAST        (1u<<2)
+#define CONTEXT_PROXY          (1u<<3)
+#define CONTEXT_RA_ROUTER      (1u<<4)
+#define CONTEXT_RA_DONE        (1u<<5)
+#define CONTEXT_RA_NAME        (1u<<6)
+#define CONTEXT_RA_STATELESS   (1u<<7)
+#define CONTEXT_DHCP           (1u<<8)
+#define CONTEXT_DEPRECATE      (1u<<9)
+#define CONTEXT_TEMPLATE       (1u<<10)    /* create contexts using addresses */
+#define CONTEXT_CONSTRUCTED    (1u<<11)
+#define CONTEXT_GC             (1u<<12)
+#define CONTEXT_RA             (1u<<13)
+#define CONTEXT_CONF_USED      (1u<<14)
+#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;
   time_t time;
+  unsigned int hash;
   struct ping_result *next;
 };
 
@@ -614,17 +903,19 @@ struct addr_list {
   struct addr_list *next;
 };
 
-struct interface_list {
-  char *interface;
-  struct interface_list *next;
-};
-
 struct tftp_prefix {
   char *interface;
   char *prefix;
+  int missing;
   struct tftp_prefix *next;
 };
 
+struct dhcp_relay {
+  struct all_addr local, server;
+  char *interface; /* Allowable interface for replies from server, and dest for IPv6 multicast */
+  int iface_index; /* working - interface in which requests arrived, for return */
+  struct dhcp_relay *current, *next;
+};
 
 extern struct daemon {
   /* datastuctures representing the command-line and 
@@ -634,44 +925,57 @@ extern struct daemon {
   unsigned int options, options2;
   struct resolvc default_resolv, *resolv_files;
   time_t last_resolv;
+  char *servers_file;
   struct mx_srv_record *mxnames;
   struct naptr *naptr;
-  struct txt_record *txt;
+  struct txt_record *txt, *rr;
   struct ptr_record *ptr;
+  struct host_record *host_records, *host_records_tail;
   struct cname *cnames;
+  struct auth_zone *auth_zones;
   struct interface_name *int_names;
   char *mxtarget;
+  int addr4_netmask;
+  int addr6_netmask;
   char *lease_file; 
   char *username, *groupname, *scriptuser;
+  char *luascript;
+  char *authserver, *hostmaster;
+  struct iname *authinterface;
+  struct name_list *secondary_forward_server;
   int group_set, osport;
   char *domain_suffix;
-  struct cond_domain *cond_domain;
+  struct cond_domain *cond_domain, *synth_domains;
   char *runfile; 
   char *lease_change_command;
-  struct iname *if_names, *if_addrs, *if_except, *dhcp_except;
-  struct bogus_addr *bogus_addr;
+  struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
+  struct bogus_addr *bogus_addr, *ignore_addr;
   struct server *servers;
+  struct ipsets *ipsets;
   int log_fac; /* log facility */
   char *log_file; /* optional log file */
   int max_logs;  /* queue limit */
   int cachesize, ftabsize;
   int port, query_port, min_port;
-  unsigned long local_ttl, neg_ttl, max_ttl;
+  unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl;
   struct hostsfile *addn_hosts;
-  struct dhcp_context *dhcp;
+  struct dhcp_context *dhcp, *dhcp6;
+  struct ra_interface *ra_interfaces;
   struct dhcp_config *dhcp_conf;
-  struct dhcp_opt *dhcp_opts, *dhcp_match;
+  struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6, *dhcp_match6;
   struct dhcp_vendor *dhcp_vendors;
   struct dhcp_mac *dhcp_macs;
   struct dhcp_boot *boot_config;
   struct pxe_service *pxe_services;
   struct tag_if *tag_if; 
   struct addr_list *override_relays;
+  struct dhcp_relay *relay4, *relay6;
   int override;
   int enable_pxe;
+  int doing_ra, doing_dhcp6;
   struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names; 
   struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
-  struct hostsfile *dhcp_hosts_file, *dhcp_opts_file;
+  struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
   int dhcp_max, tftp_max;
   int dhcp_server_port, dhcp_client_port;
   int start_tftp_port, end_tftp_port; 
@@ -680,14 +984,28 @@ extern struct daemon {
   unsigned short edns_pktsz;
   char *tftp_prefix; 
   struct tftp_prefix *if_prefix; /* per-interface TFTP prefixes */
-  struct interface_list *tftp_interfaces; /* interfaces for limited TFTP service */
-  int tftp_unlimited;
+  unsigned int duid_enterprise, duid_config_len;
+  unsigned char *duid_config;
+  char *dbus_name;
+  unsigned long soa_sn, soa_refresh, soa_retry, soa_expiry;
+#ifdef OPTION6_PREFIX_CLASS 
+  struct prefix_class *prefix_classes;
+#endif
+#ifdef HAVE_DNSSEC
+  struct ds_config *ds;
+  int back_to_the_future;
+  char *timestamp_file;
+#endif
 
   /* globally used stuff for DNS */
   char *packet; /* packet buffer */
   int packet_buff_sz; /* size of above */
   char *namebuff; /* MAXDNAME size buffer */
-  unsigned int local_answer, queries_forwarded;
+#ifdef HAVE_DNSSEC
+  char *keyname; /* MAXDNAME size buffer */
+  char *workspacename; /* ditto */
+#endif
+  unsigned int local_answer, queries_forwarded, auth_answer;
   struct frec *frec_list;
   struct serverfd *sfds;
   struct irec *interfaces;
@@ -701,20 +1019,31 @@ extern struct daemon {
   pid_t tcp_pids[MAX_PROCS];
   struct randfd randomsocks[RANDOM_SOCKS];
   int v6pktinfo; 
+  struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */
+  int log_id, log_display_id; /* ids of transactions for logging */
+  union mysockaddr *log_source_addr;
 
   /* DHCP state */
   int dhcpfd, helperfd, pxefd; 
+#ifdef HAVE_INOTIFY
+  int inotifyfd;
+#endif
 #if defined(HAVE_LINUX_NETWORK)
   int netlinkfd;
 #elif defined(HAVE_BSD_NETWORK)
-  int dhcp_raw_fd, dhcp_icmp_fd;
+  int dhcp_raw_fd, dhcp_icmp_fd, routefd;
 #endif
   struct iovec dhcp_packet;
   char *dhcp_buff, *dhcp_buff2, *dhcp_buff3;
   struct ping_result *ping_results;
   FILE *lease_stream;
   struct dhcp_bridge *bridges;
-
+#ifdef HAVE_DHCP6
+  int duid_len;
+  unsigned char *duid;
+  struct iovec outpacket;
+  int dhcp6fd, icmp6fd;
+#endif
   /* DBus stuff */
   /* void * here to avoid depending on dbus headers outside dbus.c */
   void *dbus;
@@ -723,43 +1052,77 @@ extern struct daemon {
 #endif
 
   /* TFTP stuff */
-  struct tftp_transfer *tftp_trans;
+  struct tftp_transfer *tftp_trans, *tftp_done_trans;
+
+  /* utility string buffer, hold max sized IP address as string */
+  char *addrbuff;
+  char *addrbuff2; /* only allocated when OPT_EXTRALOG */
 
 } *daemon;
 
 /* cache.c */
 void cache_init(void);
 void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg); 
-char *record_source(int index);
-void querystr(char *str, unsigned short type);
+char *record_source(unsigned int index);
+char *querystr(char *desc, unsigned short type);
 struct crec *cache_find_by_addr(struct crec *crecp,
                                struct all_addr *addr, time_t now, 
-                               unsigned short prot);
+                               unsigned int prot);
 struct crec *cache_find_by_name(struct crec *crecp, 
-                               char *name, time_t now, unsigned short  prot);
+                               char *name, time_t now, unsigned int prot);
 void cache_end_insert(void);
 void cache_start_insert(void);
 struct crec *cache_insert(char *name, struct all_addr *addr,
                          time_t now, unsigned long ttl, unsigned short flags);
 void cache_reload(void);
-void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t ttd);
+void cache_add_dhcp_entry(char *host_name, int prot, struct all_addr *host_address, time_t ttd);
+struct in_addr a_record_from_hosts(char *name, time_t now);
 void cache_unhash_dhcp(void);
 void dump_cache(time_t now);
+int cache_make_stat(struct txt_record *t);
 char *cache_get_name(struct crec *crecp);
+char *cache_get_cname_target(struct crec *crecp);
+struct crec *cache_enumerate(int init);
+int read_hostsfile(char *filename, unsigned int index, int cache_size, 
+                  struct crec **rhash, int hashsz);
+
+/* blockdata.c */
+#ifdef HAVE_DNSSEC
+void blockdata_init(void);
+void blockdata_report(void);
+struct blockdata *blockdata_alloc(char *data, size_t len);
+void *blockdata_retrieve(struct blockdata *block, size_t len, void *data);
+void blockdata_free(struct blockdata *blocks);
+#endif
+
+/* domain.c */
 char *get_domain(struct in_addr addr);
+#ifdef HAVE_IPV6
+char *get_domain6(struct in6_addr *addr);
+#endif
+int is_name_synthetic(int flags, char *name, struct all_addr *addr);
+int is_rev_synth(int flag, struct all_addr *addr, char *name);
 
 /* rfc1035.c */
+int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, 
+                 char *name, int isExtract, int extrabytes);
+unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes);
+unsigned char *skip_questions(struct dns_header *header, size_t plen);
+unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen);
 unsigned int extract_request(struct dns_header *header, size_t qlen, 
                               char *name, unsigned short *typep);
 size_t setup_reply(struct dns_header *header, size_t  qlen,
                   struct all_addr *addrp, unsigned int flags,
                   unsigned long local_ttl);
 int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff, 
-                     time_t now, int is_sign, int checkrebind, int checking_disabled);
+                     time_t now, char **ipsets, int is_sign, int checkrebind,
+                     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);
+                     struct in_addr local_addr, struct in_addr local_netmask, 
+                     time_t now, int *ad_reqd, int *do_bit);
 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);
@@ -767,10 +1130,43 @@ 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, ...);
+unsigned char *skip_questions(struct dns_header *header, size_t plen);
+int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, 
+                char *name, int isExtract, int extrabytes);
+int in_arpa_name_2_addr(char *namein, struct all_addr *addrp);
+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);
+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);
+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 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);
+int setup_timestamp(void);
 
 /* util.c */
 void rand_init(void);
 unsigned short rand16(void);
+u32 rand32(void);
+u64 rand64(void);
 int legal_hostname(char *c);
 char *canonicalise(char *s, int *nomem);
 unsigned char *do_rfc1035_name(unsigned char *p, char *sval);
@@ -779,10 +1175,16 @@ void safe_pipe(int *fd, int read_noblock);
 void *whine_malloc(size_t size);
 int sa_len(union mysockaddr *addr);
 int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
-int hostname_isequal(char *a, char *b);
+int hostname_isequal(const char *a, const char *b);
 time_t dnsmasq_time(void);
+int netmask_length(struct in_addr mask);
 int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
-int retry_send(void);
+#ifdef HAVE_IPV6
+int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
+u64 addr6part(struct in6_addr *addr);
+void setaddr6part(struct in6_addr *addr, u64 host);
+#endif
+int retry_send(ssize_t rc);
 void prettyprint_time(char *buf, unsigned int t);
 int prettyprint_addr(union mysockaddr *addr, char *buf);
 int parse_hex(char *in, unsigned char *out, int maxlen, 
@@ -791,32 +1193,46 @@ 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);
+int wildcard_matchn(const char* wildcard, const char* match, int num);
+
 /* log.c */
 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 */
 void read_opts (int argc, char **argv, char *compile_opts);
-char *option_string(unsigned char opt, int *is_ip, int *is_name);
+char *option_string(int prot, unsigned int opt, unsigned char *val, 
+                   int opt_len, char *buf, int buf_len);
 void reread_dhcp(void);
+void read_servers_file(void);
 void set_option_bool(unsigned int opt);
+void reset_option_bool(unsigned int opt);
 struct hostsfile *expand_filelist(struct hostsfile *list);
+char *parse_server(char *arg, union mysockaddr *addr, 
+                  union mysockaddr *source_addr, char *interface, int *flags);
+int option_read_dynfile(char *file, int flags);
 
 /* forward.c */
 void reply_query(int fd, int family, time_t now);
 void receive_query(struct listener *listen, time_t now);
 unsigned char *tcp_request(int confd, time_t now,
-                          struct in_addr local_addr, struct in_addr netmask);
+                          union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
 void server_gone(struct server *server);
-struct frec *get_new_frec(time_t now, int *wait);
+struct frec *get_new_frec(time_t now, int *wait, int force);
+int send_from(int fd, int nowild, char *packet, size_t len, 
+              union mysockaddr *to, struct all_addr *source,
+              unsigned int iface);
+void resend_query();
+struct randfd *allocate_rfd(int family);
+void free_rfd(struct randfd *rfd);
 
 /* network.c */
 int indextoname(int fd, int index, char *name);
@@ -824,13 +1240,35 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp);
 int random_sock(int family);
 void pre_allocate_sfds(void);
 int reload_servers(char *fname);
+void mark_servers(int flag);
+void cleanup_servers(void);
+void add_update_server(int flags,
+                      union mysockaddr *addr,
+                      union mysockaddr *source_addr,
+                      const char *interface,
+                      const char *domain);
 void check_servers(void);
-int enumerate_interfaces();
-struct listener *create_wildcard_listeners(void);
-struct listener *create_bound_listeners(void);
-int iface_check(int family, struct all_addr *addr, char *name, int *indexp);
+int enumerate_interfaces(int reset);
+void create_wildcard_listeners(void);
+void create_bound_listeners(int die);
+void warn_bound_listeners(void);
+void warn_int_names(void);
+int is_dad_listeners(void);
+int iface_check(int family, struct all_addr *addr, char *name, int *auth_dns);
+int loopback_exception(int fd, int family, struct all_addr *addr, char *name);
+int label_exception(int index, int family, struct all_addr *addr);
 int fix_fd(int fd);
-struct in_addr get_ifaddr(char *intr);
+int tcp_interface(int fd, int af);
+#ifdef HAVE_IPV6
+int set_ipv6pktinfo(int fd);
+#endif
+#ifdef HAVE_DHCP6
+void join_multicast(int dienow);
+#endif
+#if defined(HAVE_LINUX_NETWORK) || defined(HAVE_BSD_NETWORK)
+void newaddress(time_t now);
+#endif
+
 
 /* dhcp.c */
 #ifdef HAVE_DHCP
@@ -842,50 +1280,58 @@ struct dhcp_context *address_available(struct dhcp_context *context,
 struct dhcp_context *narrow_context(struct dhcp_context *context, 
                                    struct in_addr taddr,
                                    struct dhcp_netid *netids);
-int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly);
 int address_allocate(struct dhcp_context *context,
                     struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
                     struct dhcp_netid *netids, time_t now);
-struct dhcp_netid *run_tag_if(struct dhcp_netid *input);
-int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type);
-struct dhcp_config *find_config(struct dhcp_config *configs,
-                               struct dhcp_context *context,
-                               unsigned char *clid, int clid_len,
-                               unsigned char *hwaddr, int hw_len, 
-                               int hw_type, char *hostname);
-void dhcp_update_configs(struct dhcp_config *configs);
 void dhcp_read_ethers(void);
-void check_dhcp_hosts(int fatal);
 struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr);
-char *strip_hostname(char *hostname);
 char *host_from_dns(struct in_addr addr);
-char *get_domain(struct in_addr addr);
 #endif
 
 /* lease.c */
 #ifdef HAVE_DHCP
 void lease_update_file(time_t now);
-void lease_update_dns();
+void lease_update_dns(int force);
 void lease_init(time_t now);
-struct dhcp_lease *lease_allocate(struct in_addr addr);
-void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
-                     unsigned char *clid, int hw_len, int hw_type, int clid_len);
-void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth);
+struct dhcp_lease *lease4_allocate(struct in_addr addr);
+#ifdef HAVE_DHCP6
+struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type);
+struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len, 
+                              int lease_type, int iaid, struct in6_addr *addr);
+void lease6_reset(void);
+struct dhcp_lease *lease6_find_by_client(struct dhcp_lease *first, int lease_type, unsigned char *clid, int clid_len, int iaid);
+struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr);
+u64 lease_find_max_addr6(struct dhcp_context *context);
+void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface);
+void lease_update_slaac(time_t now);
+void lease_set_iaid(struct dhcp_lease *lease, int iaid);
+void lease_make_duid(time_t now);
+#endif
+void lease_set_hwaddr(struct dhcp_lease *lease, const unsigned char *hwaddr,
+                     const unsigned char *clid, int hw_len, int hw_type,
+                     int clid_len, time_t now, int force);
+void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, char *domain, char *config_domain);
 void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
-void lease_set_interface(struct dhcp_lease *lease, int interface);
+void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now);
 struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,  
                                        unsigned char *clid, int clid_len);
 struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
+struct in_addr lease_find_max_addr(struct dhcp_context *context);
 void lease_prune(struct dhcp_lease *target, time_t now);
 void lease_update_from_configs(void);
 int do_script_run(time_t now);
 void rerun_scripts(void);
+void lease_find_interfaces(time_t now);
+#ifdef HAVE_SCRIPT
+void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, 
+                        unsigned int len, int delim);
+#endif
 #endif
 
 /* rfc2131.c */
 #ifdef HAVE_DHCP
 size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
-                 size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe_fd);
+                 size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe_fd, struct in_addr fallback);
 unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr, 
                               int clid_len, unsigned char *clid, int *len_out);
 #endif
@@ -895,9 +1341,10 @@ unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
 int make_icmp_sock(void);
 int icmp_ping(struct in_addr addr);
 #endif
-void send_event(int fd, int event, int data);
+void queue_event(int event);
+void send_alarm(time_t event, time_t now);
+void send_event(int fd, int event, int data, char *msg);
 void clear_cache_and_reload(time_t now);
-void poll_resolv(int force, int do_reload, time_t now);
 
 /* netlink.c */
 #ifdef HAVE_LINUX_NETWORK
@@ -910,6 +1357,8 @@ void netlink_multicast(void);
 void init_bpf(void);
 void send_via_bpf(struct dhcp_packet *mess, size_t len,
                  struct in_addr iface_addr, struct ifreq *ifr);
+void route_init(void);
+void route_sock(void);
 #endif
 
 /* bpf.c or netlink.c */
@@ -918,24 +1367,155 @@ 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
 #endif
 
+/* ipset.c */
+#ifdef HAVE_IPSET
+void ipset_init(void);
+int add_to_ipset(const char *setname, const struct all_addr *ipaddr, int flags, int remove);
+#endif
+
 /* helper.c */
-#if defined(HAVE_DHCP) && !defined(NO_FORK)
+#if defined(HAVE_SCRIPT)
 int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
 void helper_write(void);
 void queue_script(int action, struct dhcp_lease *lease, 
                  char *hostname, time_t now);
+#ifdef HAVE_TFTP
+void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer);
+#endif
 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
+
+/* conntrack.c */
+#ifdef HAVE_CONNTRACK
+int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr,
+                     int istcp, unsigned int *markp);
+#endif
+
+/* dhcp6.c */
+#ifdef HAVE_DHCP6
+void dhcp6_init(void);
+void dhcp6_packet(time_t now);
+struct dhcp_context *address6_allocate(struct dhcp_context *context,  unsigned char *clid, int clid_len, int temp_addr,
+                                      int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans);
+int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr);
+struct dhcp_context *address6_available(struct dhcp_context *context, 
+                                       struct in6_addr *taddr,
+                                       struct dhcp_netid *netids,
+                                       int plain_range);
+struct dhcp_context *address6_valid(struct dhcp_context *context, 
+                                   struct in6_addr *taddr,
+                                   struct dhcp_netid *netids,
+                                   int plain_range);
+struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, 
+                                           int prefix, u64 addr);
+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);
+#endif
+  
+/* rfc3315.c */
+#ifdef HAVE_DHCP6
+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);
+
+unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface);
+#endif
+
+/* dhcp-common.c */
+#ifdef HAVE_DHCP
+void dhcp_common_init(void);
+ssize_t recv_dhcp_packet(int fd, struct msghdr *msg);
+struct dhcp_netid *run_tag_if(struct dhcp_netid *input);
+struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags,
+                                struct dhcp_opt *opts);
+int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly);
+char *strip_hostname(char *hostname);
+void log_tags(struct dhcp_netid *netid, u32 xid);
+int match_bytes(struct dhcp_opt *o, unsigned char *p, int len);
+void dhcp_update_configs(struct dhcp_config *configs);
+void display_opts(void);
+int lookup_dhcp_opt(int prot, char *name);
+int lookup_dhcp_len(int prot, int val);
+char *option_string(int prot, unsigned int opt, unsigned char *val, 
+                   int opt_len, char *buf, int buf_len);
+struct dhcp_config *find_config(struct dhcp_config *configs,
+                               struct dhcp_context *context,
+                               unsigned char *clid, int clid_len,
+                               unsigned char *hwaddr, int hw_len, 
+                               int hw_type, char *hostname);
+int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type);
+#ifdef HAVE_LINUX_NETWORK
+char *whichdevice(void);
+void bindtodevice(char *device, int fd);
+#endif
+#  ifdef HAVE_DHCP6
+void display_opts6(void);
+#  endif
+void log_context(int family, struct dhcp_context *context);
+void log_relay(int family, struct dhcp_relay *relay);
+#endif
+
+/* outpacket.c */
+#ifdef HAVE_DHCP6
+void end_opt6(int container);
+int save_counter(int newval);
+void *expand(size_t headroom);
+int new_opt6(int opt);
+void *put_opt6(void *data, size_t len);
+void put_opt6_long(unsigned int val);
+void put_opt6_short(unsigned int val);
+void put_opt6_char(unsigned int val);
+void put_opt6_string(char *s);
+#endif
+
+/* radv.c */
+#ifdef HAVE_DHCP6
+void ra_init(time_t now);
+void icmp6_packet(time_t now);
+time_t periodic_ra(time_t now);
+void ra_start_unsolicted(time_t now, struct dhcp_context *context);
+#endif
+
+/* slaac.c */ 
+#ifdef HAVE_DHCP6
+void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force);
+time_t periodic_slaac(time_t now, struct dhcp_lease *leases);
+void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases);
+#endif
+
+/* loop.c */
+#ifdef HAVE_LOOP
+void loop_send_probes();
+int detect_loop(char *query, int type);
+#endif
+
+/* inotify.c */
+#ifdef HAVE_INOTIFY
+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);
+
diff --git a/src/dnssec.c b/src/dnssec.c
new file mode 100644 (file)
index 0000000..4deda24
--- /dev/null
@@ -0,0 +1,2544 @@
+/* dnssec.c is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
+           and Copyright (c) 2012-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"
+
+#ifdef HAVE_DNSSEC
+
+#include <nettle/rsa.h>
+#include <nettle/dsa.h>
+#ifndef NO_NETTLE_ECC
+#  include <nettle/ecdsa.h>
+#  include <nettle/ecc-curve.h>
+#endif
+#include <nettle/nettle-meta.h>
+#include <nettle/bignum.h>
+
+/* Nettle-3.0 moved to a new API for DSA. We use a name that's defined in the new API
+   to detect Nettle-3, and invoke the backwards compatibility mode. */
+#ifdef dsa_params_init
+#include <nettle/dsa-compat.h>
+#endif
+
+#define SERIAL_UNDEF  -100
+#define SERIAL_EQ        0
+#define SERIAL_LT       -1
+#define SERIAL_GT        1
+
+/* http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
+static char *ds_digest_name(int digest)
+{
+  switch (digest)
+    {
+    case 1: return "sha1";
+    case 2: return "sha256";
+    case 3: return "gosthash94";
+    case 4: return "sha384";
+    default: return NULL;
+    }
+}
+/* http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
+static char *algo_digest_name(int algo)
+{
+  switch (algo)
+    {
+    case 1: return "md5";
+    case 3: return "sha1";
+    case 5: return "sha1";
+    case 6: return "sha1";
+    case 7: return "sha1";
+    case 8: return "sha256";
+    case 10: return "sha512";
+    case 12: return "gosthash94";
+    case 13: return "sha256";
+    case 14: return "sha384";
+    default: return NULL;
+    }
+}
+      
+/* Find pointer to correct hash function in nettle library */
+static const struct nettle_hash *hash_find(char *name)
+{
+  int i;
+  
+  if (!name)
+    return NULL;
+  
+  for (i = 0; nettle_hashes[i]; i++)
+    {
+      if (strcmp(nettle_hashes[i]->name, name) == 0)
+       return nettle_hashes[i];
+    }
+
+  return NULL;
+}
+
+/* expand ctx and digest memory allocations if necessary and init hash function */
+static int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **digestp)
+{
+  static void *ctx = NULL;
+  static unsigned char *digest = NULL;
+  static unsigned int ctx_sz = 0;
+  static unsigned int digest_sz = 0;
+
+  void *new;
+
+  if (ctx_sz < hash->context_size)
+    {
+      if (!(new = whine_malloc(hash->context_size)))
+       return 0;
+      if (ctx)
+       free(ctx);
+      ctx = new;
+      ctx_sz = hash->context_size;
+    }
+  
+  if (digest_sz < hash->digest_size)
+    {
+      if (!(new = whine_malloc(hash->digest_size)))
+       return 0;
+      if (digest)
+       free(digest);
+      digest = new;
+      digest_sz = hash->digest_size;
+    }
+
+  *ctxp = ctx;
+  *digestp = digest;
+
+  hash->init(ctx);
+
+  return 1;
+}
+  
+static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
+                             unsigned char *digest, int algo)
+{
+  unsigned char *p;
+  size_t exp_len;
+  
+  static struct rsa_public_key *key = NULL;
+  static mpz_t sig_mpz;
+  
+  if (key == NULL)
+    {
+      if (!(key = whine_malloc(sizeof(struct rsa_public_key))))
+       return 0;
+      
+      nettle_rsa_public_key_init(key);
+      mpz_init(sig_mpz);
+    }
+  
+  if ((key_len < 3) || !(p = blockdata_retrieve(key_data, key_len, NULL)))
+    return 0;
+  
+  key_len--;
+  if ((exp_len = *p++) == 0)
+    {
+      GETSHORT(exp_len, p);
+      key_len -= 2;
+    }
+  
+  if (exp_len >= key_len)
+    return 0;
+  
+  key->size =  key_len - exp_len;
+  mpz_import(key->e, exp_len, 1, 1, 0, 0, p);
+  mpz_import(key->n, key->size, 1, 1, 0, 0, p + exp_len);
+
+  mpz_import(sig_mpz, sig_len, 1, 1, 0, 0, sig);
+  
+  switch (algo)
+    {
+    case 1:
+      return nettle_rsa_md5_verify_digest(key, digest, sig_mpz);
+    case 5: case 7:
+      return nettle_rsa_sha1_verify_digest(key, digest, sig_mpz);
+    case 8:
+      return nettle_rsa_sha256_verify_digest(key, digest, sig_mpz);
+    case 10:
+      return nettle_rsa_sha512_verify_digest(key, digest, sig_mpz);
+    }
+
+  return 0;
+}  
+
+static int dnsmasq_dsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
+                             unsigned char *digest, int algo)
+{
+  unsigned char *p;
+  unsigned int t;
+  
+  static struct dsa_public_key *key = NULL;
+  static struct dsa_signature *sig_struct;
+  
+  if (key == NULL)
+    {
+      if (!(sig_struct = whine_malloc(sizeof(struct dsa_signature))) || 
+         !(key = whine_malloc(sizeof(struct dsa_public_key)))) 
+       return 0;
+      
+      nettle_dsa_public_key_init(key);
+      nettle_dsa_signature_init(sig_struct);
+    }
+  
+  if ((sig_len < 41) || !(p = blockdata_retrieve(key_data, key_len, NULL)))
+    return 0;
+  
+  t = *p++;
+  
+  if (key_len < (213 + (t * 24)))
+    return 0;
+  
+  mpz_import(key->q, 20, 1, 1, 0, 0, p); p += 20;
+  mpz_import(key->p, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
+  mpz_import(key->g, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
+  mpz_import(key->y, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
+  
+  mpz_import(sig_struct->r, 20, 1, 1, 0, 0, sig+1);
+  mpz_import(sig_struct->s, 20, 1, 1, 0, 0, sig+21);
+  
+  (void)algo;
+  
+  return nettle_dsa_sha1_verify_digest(key, digest, sig_struct);
+} 
+#ifndef NO_NETTLE_ECC
+static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len, 
+                               unsigned char *sig, size_t sig_len,
+                               unsigned char *digest, size_t digest_len, int algo)
+{
+  unsigned char *p;
+  unsigned int t;
+  struct ecc_point *key;
+
+  static struct ecc_point *key_256 = NULL, *key_384 = NULL;
+  static mpz_t x, y;
+  static struct dsa_signature *sig_struct;
+  
+  if (!sig_struct)
+    {
+      if (!(sig_struct = whine_malloc(sizeof(struct dsa_signature))))
+       return 0;
+      
+      nettle_dsa_signature_init(sig_struct);
+      mpz_init(x);
+      mpz_init(y);
+    }
+  
+  switch (algo)
+    {
+    case 13:
+      if (!key_256)
+       {
+         if (!(key_256 = whine_malloc(sizeof(struct ecc_point))))
+           return 0;
+         
+         nettle_ecc_point_init(key_256, &nettle_secp_256r1);
+       }
+      
+      key = key_256;
+      t = 32;
+      break;
+      
+    case 14:
+      if (!key_384)
+       {
+         if (!(key_384 = whine_malloc(sizeof(struct ecc_point))))
+           return 0;
+         
+         nettle_ecc_point_init(key_384, &nettle_secp_384r1);
+       }
+      
+      key = key_384;
+      t = 48;
+      break;
+        
+    default:
+      return 0;
+    }
+  
+  if (sig_len != 2*t || key_len != 2*t ||
+      !(p = blockdata_retrieve(key_data, key_len, NULL)))
+    return 0;
+  
+  mpz_import(x, t , 1, 1, 0, 0, p);
+  mpz_import(y, t , 1, 1, 0, 0, p + t);
+
+  if (!ecc_point_set(key, x, y))
+    return 0;
+  
+  mpz_import(sig_struct->r, t, 1, 1, 0, 0, sig);
+  mpz_import(sig_struct->s, t, 1, 1, 0, 0, sig + t);
+  
+  return nettle_ecdsa_verify(key, digest_len, digest, sig_struct);
+} 
+#endif 
+
+static int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
+                 unsigned char *digest, size_t digest_len, int algo)
+{
+  (void)digest_len;
+
+  switch (algo)
+    {
+    case 1: case 5: case 7: case 8: case 10:
+      return dnsmasq_rsa_verify(key_data, key_len, sig, sig_len, digest, algo);
+      
+    case 3: case 6: 
+      return dnsmasq_dsa_verify(key_data, key_len, sig, sig_len, digest, algo);
+#ifndef NO_NETTLE_ECC   
+    case 13: case 14:
+      return dnsmasq_ecdsa_verify(key_data, key_len, sig, sig_len, digest, digest_len, algo);
+#endif
+    }
+  
+  return 0;
+}
+
+/* Convert from presentation format to wire format, in place.
+   Also map UC -> LC.
+   Note that using extract_name to get presentation format
+   then calling to_wire() removes compression and maps case,
+   thus generating names in canonical form.
+   Calling to_wire followed by from_wire is almost an identity,
+   except that the UC remains mapped to LC. 
+
+   Note that both /000 and '.' are allowed within labels. These get
+   represented in presentation format using NAME_ESCAPE as an escape
+   character. In theory, if all the characters in a name were /000 or
+   '.' or NAME_ESCAPE then all would have to be escaped, so the 
+   presentation format would be twice as long as the spec (1024). 
+   The buffers are all delcared as 2049 (allowing for the trailing zero) 
+   for this reason.
+*/
+static int to_wire(char *name)
+{
+  unsigned char *l, *p, *q, term;
+  int len;
+
+  for (l = (unsigned char*)name; *l != 0; l = p)
+    {
+      for (p = l; *p != '.' && *p != 0; p++)
+       if (*p >= 'A' && *p <= 'Z')
+         *p = *p - 'A' + 'a';
+       else if (*p == NAME_ESCAPE)
+         {
+           for (q = p; *q; q++)
+             *q = *(q+1);
+           (*p)--;
+         }
+      term = *p;
+      
+      if ((len = p - l) != 0)
+       memmove(l+1, l, len);
+      *l = len;
+      
+      p++;
+      
+      if (term == 0)
+       *p = 0;
+    }
+  
+  return l + 1 - (unsigned char *)name;
+}
+
+/* Note: no compression  allowed in input. */
+static void from_wire(char *name)
+{
+  unsigned char *l, *p, *last;
+  int len;
+  
+  for (last = (unsigned char *)name; *last != 0; last += *last+1);
+  
+  for (l = (unsigned char *)name; *l != 0; l += len+1)
+    {
+      len = *l;
+      memmove(l, l+1, len);
+      for (p = l; p < l + len; p++)
+       if (*p == '.' || *p == 0 || *p == NAME_ESCAPE)
+         {
+           memmove(p+1, p, 1 + last - p);
+           len++;
+           *p++ = NAME_ESCAPE; 
+           (*p)++;
+         }
+       
+      l[len] = '.';
+    }
+
+  if ((char *)l != name)
+    *(l-1) = 0;
+}
+
+/* Input in presentation format */
+static int count_labels(char *name)
+{
+  int i;
+
+  if (*name == 0)
+    return 0;
+
+  for (i = 0; *name; name++)
+    if (*name == '.')
+      i++;
+
+  return i+1;
+}
+
+/* Implement RFC1982 wrapped compare for 32-bit numbers */
+static int serial_compare_32(unsigned long s1, unsigned long s2)
+{
+  if (s1 == s2)
+    return SERIAL_EQ;
+
+  if ((s1 < s2 && (s2 - s1) < (1UL<<31)) ||
+      (s1 > s2 && (s1 - s2) > (1UL<<31)))
+    return SERIAL_LT;
+  if ((s1 < s2 && (s2 - s1) > (1UL<<31)) ||
+      (s1 > s2 && (s1 - s2) < (1UL<<31)))
+    return SERIAL_GT;
+  return SERIAL_UNDEF;
+}
+
+/* Called at startup. If the timestamp file is configured and exists, put its mtime on
+   timestamp_time. If it doesn't exist, create it, and set the mtime to 1-1-2015.
+   return -1 -> Cannot create file.
+           0 -> not using timestamp, or timestamp exists and is in past.
+           1 -> timestamp exists and is in future.
+*/
+
+static time_t timestamp_time;
+
+int setup_timestamp(void)
+{
+  struct stat statbuf;
+  
+  daemon->back_to_the_future = 0;
+  
+  if (!daemon->timestamp_file)
+    return 0;
+  
+  if (stat(daemon->timestamp_file, &statbuf) != -1)
+    {
+      timestamp_time = statbuf.st_mtime;
+    check_and_exit:
+      if (difftime(timestamp_time, time(0)) <=  0)
+       {
+         /* time already OK, update timestamp, and do key checking from the start. */
+         if (utime(daemon->timestamp_file, NULL) == -1)
+           my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));
+         daemon->back_to_the_future = 1;
+         return 0;
+       }
+      return 1;
+    }
+  
+  if (errno == ENOENT)
+    {
+      /* NB. for explanation of O_EXCL flag, see comment on pidfile in dnsmasq.c */ 
+      int fd = open(daemon->timestamp_file, O_WRONLY | O_CREAT | O_NONBLOCK | O_EXCL, 0666);
+      if (fd != -1)
+       {
+         struct utimbuf timbuf;
+
+         close(fd);
+         
+         timestamp_time = timbuf.actime = timbuf.modtime = 1420070400; /* 1-1-2015 */
+         if (utime(daemon->timestamp_file, &timbuf) == 0)
+           goto check_and_exit;
+       }
+    }
+
+  return -1;
+}
+
+/* Check whether today/now is between date_start and date_end */
+static int check_date_range(unsigned long date_start, unsigned long date_end)
+{
+  unsigned long curtime = time(0);
+  /* Checking timestamps may be temporarily disabled */
+    
+  /* If the current time if _before_ the timestamp
+     on our persistent timestamp file, then assume the
+     time if not yet correct, and don't check the
+     key timestamps. As soon as the current time is
+     later then the timestamp, update the timestamp
+     and start checking keys */
+  if (daemon->timestamp_file)
+    {
+      if (daemon->back_to_the_future == 0 && difftime(timestamp_time, curtime) <= 0)
+       {
+         if (utime(daemon->timestamp_file, NULL) != 0)
+           my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));
+         
+         daemon->back_to_the_future = 1;
+         set_option_bool(OPT_DNSSEC_TIME);
+         queue_event(EVENT_RELOAD); /* purge cache */
+       } 
+
+      if (daemon->back_to_the_future == 0)
+       return 1;
+    }
+  else if (option_bool(OPT_DNSSEC_TIME))
+    return 1;
+  
+  /* We must explicitly check against wanted values, because of SERIAL_UNDEF */
+  return serial_compare_32(curtime, date_start) == SERIAL_GT
+    && serial_compare_32(curtime, date_end) == SERIAL_LT;
+}
+
+static u16 *get_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;
+}
+
+/* Return bytes of canonicalised rdata, when the return value is zero, the remaining 
+   data, pointed to by *p, should be used raw. */
+static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff, int bufflen,
+                    unsigned char **p, u16 **desc)
+{
+  int d = **desc;
+  
+  /* No more data needs mangling */
+  if (d == (u16)-1)
+    {
+      /* If there's more data than we have space for, just return what fits,
+        we'll get called again for more chunks */
+      if (end - *p > bufflen)
+       {
+         memcpy(buff, *p, bufflen);
+         *p += bufflen;
+         return bufflen;
+       }
+      
+      return 0;
+    }
+  (*desc)++;
+  
+  if (d == 0 && extract_name(header, plen, p, buff, 1, 0))
+    /* domain-name, canonicalise */
+    return to_wire(buff);
+  else
+    { 
+      /* plain data preceding a domain-name, don't run off the end of the data */
+      if ((end - *p) < d)
+       d = end - *p;
+      
+      if (d != 0)
+       {
+         memcpy(buff, *p, d);
+         *p += d;
+       }
+      
+      return d;
+    }
+}
+
+static int expand_workspace(unsigned char ***wkspc, int *sz, int new)
+{
+  unsigned char **p;
+  int new_sz = *sz;
+  
+  if (new_sz > new)
+    return 1;
+
+  if (new >= 100)
+    return 0;
+
+  new_sz += 5;
+  
+  if (!(p = whine_malloc((new_sz) * sizeof(unsigned char **))))
+    return 0;  
+  
+  if (*wkspc)
+    {
+      memcpy(p, *wkspc, *sz * sizeof(unsigned char **));
+      free(*wkspc);
+    }
+  
+  *wkspc = p;
+  *sz = new_sz;
+
+  return 1;
+}
+
+/* Bubble sort the RRset into the canonical order. 
+   Note that the byte-streams from two RRs may get unsynced: consider 
+   RRs which have two domain-names at the start and then other data.
+   The domain-names may have different lengths in each RR, but sort equal
+
+   ------------
+   |abcde|fghi|
+   ------------
+   |abcd|efghi|
+   ------------
+
+   leaving the following bytes as deciding the order. Hence the nasty left1 and left2 variables.
+*/
+
+static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int rrsetidx, 
+                      unsigned char **rrset, char *buff1, char *buff2)
+{
+  int swap, quit, i;
+  
+  do
+    {
+      for (swap = 0, i = 0; i < rrsetidx-1; i++)
+       {
+         int rdlen1, rdlen2, left1, left2, len1, len2, len, rc;
+         u16 *dp1, *dp2;
+         unsigned char *end1, *end2;
+         /* Note that these have been determined to be OK previously,
+            so we don't need to check for NULL return here. */
+         unsigned char *p1 = skip_name(rrset[i], header, plen, 10);
+         unsigned char *p2 = skip_name(rrset[i+1], header, plen, 10);
+         
+         p1 += 8; /* skip class, type, ttl */
+         GETSHORT(rdlen1, p1);
+         end1 = p1 + rdlen1;
+         
+         p2 += 8; /* skip class, type, ttl */
+         GETSHORT(rdlen2, p2);
+         end2 = p2 + rdlen2; 
+         
+         dp1 = dp2 = rr_desc;
+         
+         for (quit = 0, left1 = 0, left2 = 0, len1 = 0, len2 = 0; !quit;)
+           {
+             if (left1 != 0)
+               memmove(buff1, buff1 + len1 - left1, left1);
+             
+             if ((len1 = get_rdata(header, plen, end1, buff1 + left1, (MAXDNAME * 2) - left1, &p1, &dp1)) == 0)
+               {
+                 quit = 1;
+                 len1 = end1 - p1;
+                 memcpy(buff1 + left1, p1, len1);
+               }
+             len1 += left1;
+             
+             if (left2 != 0)
+               memmove(buff2, buff2 + len2 - left2, left2);
+             
+             if ((len2 = get_rdata(header, plen, end2, buff2 + left2, (MAXDNAME *2) - left2, &p2, &dp2)) == 0)
+               {
+                 quit = 1;
+                 len2 = end2 - p2;
+                 memcpy(buff2 + left2, p2, len2);
+               }
+             len2 += left2;
+              
+             if (len1 > len2)
+               left1 = len1 - len2, left2 = 0, len = len2;
+             else
+               left2 = len2 - len1, left1 = 0, len = len1;
+             
+             rc = (len == 0) ? 0 : memcmp(buff1, buff2, len);
+             
+             if (rc > 0 || (rc == 0 && quit && len1 > len2))
+               {
+                 unsigned char *tmp = rrset[i+1];
+                 rrset[i+1] = rrset[i];
+                 rrset[i] = tmp;
+                 swap = quit = 1;
+               }
+             else if (rc < 0)
+               quit = 1;
+           }
+       }
+    } while (swap);
+}
+
+/* Validate a single RRset (class, type, name) in the supplied DNS reply 
+   Return code:
+   STAT_SECURE   if it validates.
+   STAT_SECURE_WILDCARD if it validates and is the result of wildcard expansion.
+   (In this case *wildcard_out points to the "body" of the wildcard within name.) 
+   STAT_NO_SIG no RRsigs found.
+   STAT_INSECURE RRset empty.
+   STAT_BOGUS    signature is wrong, bad packet.
+   STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
+
+   if key is non-NULL, use that key, which has the algo and tag given in the params of those names,
+   otherwise find the key in the cache.
+
+   name is unchanged on exit. keyname is used as workspace and trashed.
+*/
+static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, int type, 
+                         char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen, int algo_in, int keytag_in)
+{
+  static unsigned char **rrset = NULL, **sigs = NULL;
+  static int rrset_sz = 0, sig_sz = 0;
+  
+  unsigned char *p;
+  int rrsetidx, sigidx, res, rdlen, j, name_labels;
+  struct crec *crecp = NULL;
+  int type_covered, algo, labels, orig_ttl, sig_expiration, sig_inception, key_tag;
+  u16 *rr_desc = get_desc(type);
+  if (wildcard_out)
+    *wildcard_out = NULL;
+  
+  if (!(p = skip_questions(header, plen)))
+    return STAT_BOGUS;
+  
+  name_labels = count_labels(name); /* For 4035 5.3.2 check */
+
+  /* look for RRSIGs for this RRset and get pointers to each RR in the set. */
+  for (rrsetidx = 0, sigidx = 0, j = ntohs(header->ancount) + ntohs(header->nscount); 
+       j != 0; j--) 
+    {
+      unsigned char *pstart, *pdata;
+      int stype, sclass;
+
+      pstart = p;
+      
+      if (!(res = extract_name(header, plen, &p, name, 0, 10)))
+       return STAT_BOGUS; /* bad packet */
+      
+      GETSHORT(stype, p);
+      GETSHORT(sclass, p);
+      p += 4; /* TTL */
+      
+      pdata = p;
+
+      GETSHORT(rdlen, p);
+      
+      if (!CHECK_LEN(header, p, plen, rdlen))
+       return STAT_BOGUS; 
+      
+      if (res == 1 && sclass == class)
+       {
+         if (stype == type)
+           {
+             if (!expand_workspace(&rrset, &rrset_sz, rrsetidx))
+               return STAT_BOGUS; 
+             
+             rrset[rrsetidx++] = pstart;
+           }
+         
+         if (stype == T_RRSIG)
+           {
+             if (rdlen < 18)
+               return STAT_BOGUS; /* bad packet */ 
+             
+             GETSHORT(type_covered, p);
+             
+             if (type_covered == type)
+               {
+                 if (!expand_workspace(&sigs, &sig_sz, sigidx))
+                   return STAT_BOGUS; 
+                 
+                 sigs[sigidx++] = pdata;
+               } 
+             
+             p = pdata + 2; /* restore for ADD_RDLEN */
+           }
+       }
+      
+      if (!ADD_RDLEN(header, p, plen, rdlen))
+       return STAT_BOGUS;
+    }
+  
+  /* RRset empty */
+  if (rrsetidx == 0)
+    return STAT_INSECURE; 
+
+  /* no RRSIGs */
+  if (sigidx == 0)
+    return STAT_NO_SIG; 
+  
+  /* Sort RRset records into canonical order. 
+     Note that at this point keyname and daemon->workspacename buffs are
+     unused, and used as workspace by the sort. */
+  sort_rrset(header, plen, rr_desc, rrsetidx, rrset, daemon->workspacename, keyname);
+         
+  /* Now try all the sigs to try and find one which validates */
+  for (j = 0; j <sigidx; j++)
+    {
+      unsigned char *psav, *sig, *digest;
+      int i, wire_len, sig_len;
+      const struct nettle_hash *hash;
+      void *ctx;
+      char *name_start;
+      u32 nsigttl;
+      
+      p = sigs[j];
+      GETSHORT(rdlen, p); /* rdlen >= 18 checked previously */
+      psav = p;
+      
+      p += 2; /* type_covered - already checked */
+      algo = *p++;
+      labels = *p++;
+      GETLONG(orig_ttl, p);
+      GETLONG(sig_expiration, p);
+      GETLONG(sig_inception, p);
+      GETSHORT(key_tag, p);
+      
+      if (!extract_name(header, plen, &p, keyname, 1, 0))
+       return STAT_BOGUS;
+
+      /* RFC 4035 5.3.1 says that the Signer's Name field MUST equal
+        the name of the zone containing the RRset. We can't tell that
+        for certain, but we can check that  the RRset name is equal to
+        or encloses the signers name, which should be enough to stop 
+        an attacker using signatures made with the key of an unrelated 
+        zone he controls. Note that the root key is always allowed. */
+      if (*keyname != 0)
+       {
+         int failed = 0;
+         
+         for (name_start = name; !hostname_isequal(name_start, keyname); )
+           if ((name_start = strchr(name_start, '.')))
+             name_start++; /* chop a label off and try again */
+           else
+             {
+               failed = 1;
+               break;
+             }
+
+         /* Bad sig, try another */
+         if (failed)
+           continue;
+       }
+      
+      /* Other 5.3.1 checks */
+      if (!check_date_range(sig_inception, sig_expiration) ||
+         labels > name_labels ||
+         !(hash = hash_find(algo_digest_name(algo))) ||
+         !hash_init(hash, &ctx, &digest))
+       continue;
+       
+      /* OK, we have the signature record, see if the relevant DNSKEY is in the cache. */
+      if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
+       return STAT_NEED_KEY;
+      
+      sig = p;
+      sig_len = rdlen - (p - psav);
+              
+      nsigttl = htonl(orig_ttl);
+      
+      hash->update(ctx, 18, psav);
+      wire_len = to_wire(keyname);
+      hash->update(ctx, (unsigned int)wire_len, (unsigned char*)keyname);
+      from_wire(keyname);
+      
+      for (i = 0; i < rrsetidx; ++i)
+       {
+         int seg;
+         unsigned char *end, *cp;
+         u16 len, *dp;
+         
+         p = rrset[i];
+         if (!extract_name(header, plen, &p, name, 1, 10)) 
+           return STAT_BOGUS;
+
+         name_start = name;
+         
+         /* if more labels than in RRsig name, hash *.<no labels in rrsig labels field>  4035 5.3.2 */
+         if (labels < name_labels)
+           {
+             int k;
+             for (k = name_labels - labels; k != 0; k--)
+               {
+                 while (*name_start != '.' && *name_start != 0)
+                   name_start++;
+                 if (k != 1 && *name_start == '.')
+                   name_start++;
+               }
+             
+             if (wildcard_out)
+               *wildcard_out = name_start+1;
+
+             name_start--;
+             *name_start = '*';
+           }
+         
+         wire_len = to_wire(name_start);
+         hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name_start);
+         hash->update(ctx, 4, p); /* class and type */
+         hash->update(ctx, 4, (unsigned char *)&nsigttl);
+         
+         p += 8; /* skip class, type, ttl */
+         GETSHORT(rdlen, p);
+         if (!CHECK_LEN(header, p, plen, rdlen))
+           return STAT_BOGUS; 
+         
+         end = p + rdlen;
+         
+         /* canonicalise rdata and calculate length of same, use name buffer as workspace.
+            Note that name buffer is twice MAXDNAME long in DNSSEC mode. */
+         cp = p;
+         dp = rr_desc;
+         for (len = 0; (seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)) != 0; len += seg);
+         len += end - cp;
+         len = htons(len);
+         hash->update(ctx, 2, (unsigned char *)&len); 
+         
+         /* Now canonicalise again and digest. */
+         cp = p;
+         dp = rr_desc;
+         while ((seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)))
+           hash->update(ctx, seg, (unsigned char *)name);
+         if (cp != end)
+           hash->update(ctx, end - cp, cp);
+       }
+     
+      hash->digest(ctx, hash->digest_size, digest);
+      
+      /* namebuff used for workspace above, restore to leave unchanged on exit */
+      p = (unsigned char*)(rrset[0]);
+      extract_name(header, plen, &p, name, 1, 0);
+
+      if (key)
+       {
+         if (algo_in == algo && keytag_in == key_tag &&
+             verify(key, keylen, sig, sig_len, digest, hash->digest_size, algo))
+           return STAT_SECURE;
+       }
+      else
+       {
+         /* iterate through all possible keys 4035 5.3.1 */
+         for (; crecp; crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY))
+           if (crecp->addr.key.algo == algo && 
+               crecp->addr.key.keytag == key_tag &&
+               crecp->uid == (unsigned int)class &&
+               verify(crecp->addr.key.keydata, crecp->addr.key.keylen, sig, sig_len, digest, hash->digest_size, algo))
+             return (labels < name_labels) ? STAT_SECURE_WILDCARD : STAT_SECURE;
+       }
+    }
+
+  return STAT_BOGUS;
+}
+/* The DNS packet is expected to contain the answer to a DNSKEY query.
+   Put all DNSKEYs in the answer which are valid into the cache.
+   return codes:
+         STAT_SECURE   At least one valid DNSKEY found and in cache.
+        STAT_BOGUS    No DNSKEYs found, which  can be validated with DS,
+                      or self-sign for DNSKEY RRset is not valid, bad packet.
+        STAT_NEED_DS  DS records to validate a key not found, name in keyname 
+*/
+int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
+{
+  unsigned char *psave, *p = (unsigned char *)(header+1);
+  struct crec *crecp, *recp1;
+  int rc, j, qtype, qclass, ttl, rdlen, flags, algo, valid, keytag, type_covered;
+  struct blockdata *key;
+  struct all_addr a;
+
+  if (ntohs(header->qdcount) != 1 ||
+      !extract_name(header, plen, &p, name, 1, 4))
+    return STAT_BOGUS;
+
+  GETSHORT(qtype, p);
+  GETSHORT(qclass, p);
+  
+  if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)
+    return STAT_BOGUS;
+
+  /* See if we have cached a DS record which validates this key */
+  if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
+    {
+      strcpy(keyname, name);
+      return STAT_NEED_DS;
+    }
+  
+  /* If we've cached that DS provably doesn't exist, result must be INSECURE */
+  if (crecp->flags & F_NEG)
+    return STAT_INSECURE_DS;
+  
+  /* NOTE, we need to find ONE DNSKEY which matches the DS */
+  for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--) 
+    {
+      /* Ensure we have type, class  TTL and length */
+      if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
+       return STAT_BOGUS; /* bad packet */
+  
+      GETSHORT(qtype, p); 
+      GETSHORT(qclass, p);
+      GETLONG(ttl, p);
+      GETSHORT(rdlen, p);
+      if (!CHECK_LEN(header, p, plen, rdlen) || rdlen < 4)
+       return STAT_BOGUS; /* bad packet */
+      
+      if (qclass != class || qtype != T_DNSKEY || rc == 2)
+       {
+         p += rdlen;
+         continue;
+       }
+            
+      psave = p;
+      
+      GETSHORT(flags, p);
+      if (*p++ != 3)
+       return STAT_BOGUS;
+      algo = *p++;
+      keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
+      key = NULL;
+      
+      /* key must have zone key flag set */
+      if (flags & 0x100)
+       key = blockdata_alloc((char*)p, rdlen - 4);
+      
+      p = psave;
+      
+      if (!ADD_RDLEN(header, p, plen, rdlen))
+       {
+         if (key)
+           blockdata_free(key);
+         return STAT_BOGUS; /* bad packet */
+       }
+
+      /* No zone key flag or malloc failure */
+      if (!key)
+       continue;
+      
+      for (recp1 = crecp; recp1; recp1 = cache_find_by_name(recp1, name, now, F_DS))
+       {
+         void *ctx;
+         unsigned char *digest, *ds_digest;
+         const struct nettle_hash *hash;
+         
+         if (recp1->addr.ds.algo == algo && 
+             recp1->addr.ds.keytag == keytag &&
+             recp1->uid == (unsigned int)class &&
+             (hash = hash_find(ds_digest_name(recp1->addr.ds.digest))) &&
+             hash_init(hash, &ctx, &digest))
+           
+           {
+             int wire_len = to_wire(name);
+             
+             /* Note that digest may be different between DSs, so 
+                we can't move this outside the loop. */
+             hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name);
+             hash->update(ctx, (unsigned int)rdlen, psave);
+             hash->digest(ctx, hash->digest_size, digest);
+             
+             from_wire(name);
+             
+             if (recp1->addr.ds.keylen == (int)hash->digest_size &&
+                 (ds_digest = blockdata_retrieve(recp1->addr.key.keydata, recp1->addr.ds.keylen, NULL)) &&
+                 memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 &&
+                 validate_rrset(now, header, plen, class, T_DNSKEY, name, keyname, NULL, key, rdlen - 4, algo, keytag) == STAT_SECURE)
+               {
+                 valid = 1;
+                 break;
+               }
+           }
+       }
+      blockdata_free(key);
+    }
+
+  if (valid)
+    {
+      /* DNSKEY RRset determined to be OK, now cache it and the RRsigs that sign it. */
+      cache_start_insert();
+      
+      p = skip_questions(header, plen);
+
+      for (j = ntohs(header->ancount); j != 0; j--) 
+       {
+         /* Ensure we have type, class  TTL and length */
+         if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
+           return STAT_INSECURE; /* bad packet */
+         
+         GETSHORT(qtype, p); 
+         GETSHORT(qclass, p);
+         GETLONG(ttl, p);
+         GETSHORT(rdlen, p);
+           
+         if (!CHECK_LEN(header, p, plen, rdlen))
+           return STAT_BOGUS; /* bad packet */
+         
+         if (qclass == class && rc == 1)
+           {
+             psave = p;
+             
+             if (qtype == T_DNSKEY)
+               {
+                 if (rdlen < 4)
+                   return STAT_BOGUS; /* bad packet */
+                 
+                 GETSHORT(flags, p);
+                 if (*p++ != 3)
+                   return STAT_BOGUS;
+                 algo = *p++;
+                 keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
+                 
+                 /* Cache needs to known class for DNSSEC stuff */
+                 a.addr.dnssec.class = class;
+                 
+                 if ((key = blockdata_alloc((char*)p, rdlen - 4)))
+                   {
+                     if (!(recp1 = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DNSSECOK)))
+                       blockdata_free(key);
+                     else
+                       {
+                         a.addr.keytag = keytag;
+                         log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u");
+                         
+                         recp1->addr.key.keylen = rdlen - 4;
+                         recp1->addr.key.keydata = key;
+                         recp1->addr.key.algo = algo;
+                         recp1->addr.key.keytag = keytag;
+                         recp1->addr.key.flags = flags;
+                       }
+                   }
+               }
+             else if (qtype == T_RRSIG)
+               {
+                 /* RRSIG, cache if covers DNSKEY RRset */
+                 if (rdlen < 18)
+                   return STAT_BOGUS; /* bad packet */
+                 
+                 GETSHORT(type_covered, p);
+                 
+                 if (type_covered == T_DNSKEY)
+                   {
+                     a.addr.dnssec.class = class;
+                     a.addr.dnssec.type = type_covered;
+                     
+                     algo = *p++;
+                     p += 13; /* labels, orig_ttl, expiration, inception */
+                     GETSHORT(keytag, p);      
+                     if ((key = blockdata_alloc((char*)psave, rdlen)))
+                       {
+                         if (!(crecp = cache_insert(name, &a, now, ttl,  F_FORWARD | F_DNSKEY | F_DS)))
+                           blockdata_free(key);
+                         else
+                           {
+                             crecp->addr.sig.keydata = key;
+                             crecp->addr.sig.keylen = rdlen;
+                             crecp->addr.sig.keytag = keytag;
+                             crecp->addr.sig.type_covered = type_covered;
+                             crecp->addr.sig.algo = algo;
+                           }
+                       }
+                   }
+               }
+             
+             p = psave;
+           }
+
+         if (!ADD_RDLEN(header, p, plen, rdlen))
+           return STAT_BOGUS; /* bad packet */
+       }
+      
+      /* commit cache insert. */
+      cache_end_insert();
+      return STAT_SECURE;
+    }
+
+  log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
+  return STAT_BOGUS;
+}
+
+/* The DNS packet is expected to contain the answer to a DS query
+   Put all DSs in the answer which are valid into the cache.
+   return codes:
+   STAT_SECURE      At least one valid DS found and in cache.
+   STAT_NO_DS       It's proved there's no DS here.
+   STAT_NO_NS       It's proved there's no DS _or_ NS here.
+   STAT_BOGUS       no DS in reply or not signed, fails validation, bad packet.
+   STAT_NEED_KEY    DNSKEY records to validate a DS not found, name in keyname
+*/
+
+int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
+{
+  unsigned char *p = (unsigned char *)(header+1);
+  int qtype, qclass, val, i, neganswer, nons;
+
+  if (ntohs(header->qdcount) != 1 ||
+      !(p = skip_name(p, header, plen, 4)))
+    return STAT_BOGUS;
+  
+  GETSHORT(qtype, p);
+  GETSHORT(qclass, p);
+
+  if (qtype != T_DS || qclass != class)
+    val = STAT_BOGUS;
+  else
+    val = dnssec_validate_reply(now, header, plen, name, keyname, NULL, &neganswer, &nons);
+  /* Note dnssec_validate_reply() will have cached positive answers */
+  
+  if (val == STAT_INSECURE)
+    val = STAT_BOGUS;
+
+  p = (unsigned char *)(header+1);
+  extract_name(header, plen, &p, name, 1, 4);
+  p += 4; /* qtype, qclass */
+  
+  if (!(p = skip_section(p, ntohs(header->ancount), header, plen)))
+    val = STAT_BOGUS;
+   
+  /* If we return STAT_NO_SIG, name contains the name of the DS query */
+  if (val == STAT_NO_SIG)
+    {
+      *keyname = 0;
+      return val;
+    }  
+
+  /* If the key needed to validate the DS is on the same domain as the DS, we'll
+     loop getting nowhere. Stop that now. This can happen of the DS answer comes
+     from the DS's zone, and not the parent zone. */
+  if (val == STAT_BOGUS ||  (val == STAT_NEED_KEY && hostname_isequal(name, keyname)))
+    {
+      log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");
+      return STAT_BOGUS;
+    }
+
+  /* By here, the answer is proved secure, and a positive answer has been cached. */
+  if (val == STAT_SECURE && neganswer)
+    {
+      int rdlen, flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
+      unsigned long ttl, minttl = ULONG_MAX;
+      struct all_addr a;
+
+      if (RCODE(header) == NXDOMAIN)
+       flags |= F_NXDOMAIN;
+      
+      /* We only cache validated DS records, DNSSECOK flag hijacked 
+        to store presence/absence of NS. */
+      if (nons)
+       flags &= ~F_DNSSECOK;
+      
+      for (i = ntohs(header->nscount); i != 0; i--)
+       {
+         if (!(p = skip_name(p, header, plen, 0)))
+           return STAT_BOGUS;
+         
+         GETSHORT(qtype, p); 
+         GETSHORT(qclass, p);
+         GETLONG(ttl, p);
+         GETSHORT(rdlen, p);
+
+         if (!CHECK_LEN(header, p, plen, rdlen))
+           return STAT_BOGUS; /* bad packet */
+           
+         if (qclass != class || qtype != T_SOA)
+           {
+             p += rdlen;
+             continue;
+           }
+           
+         if (ttl < minttl)
+           minttl = ttl;
+         
+         /* MNAME */
+         if (!(p = skip_name(p, header, plen, 0)))
+           return STAT_BOGUS;
+         /* RNAME */
+         if (!(p = skip_name(p, header, plen, 20)))
+           return STAT_BOGUS;
+         p += 16; /* SERIAL REFRESH RETRY EXPIRE */
+         
+         GETLONG(ttl, p); /* minTTL */
+         if (ttl < minttl)
+           minttl = ttl;
+         
+         break;
+       }
+      
+      if (i != 0)
+       {
+         cache_start_insert();
+         
+         a.addr.dnssec.class = class;
+         cache_insert(name, &a, now, ttl, flags);
+         
+         cache_end_insert();  
+         
+         log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no delegation" : "no DS");
+       }
+
+      return nons ? STAT_NO_NS : STAT_NO_DS; 
+    }
+
+  return val;
+}
+
+/* 4034 6.1 */
+static int hostname_cmp(const char *a, const char *b)
+{
+  char *sa, *ea, *ca, *sb, *eb, *cb;
+  unsigned char ac, bc;
+  
+  sa = ea = (char *)a + strlen(a);
+  sb = eb = (char *)b + strlen(b);
+  while (1)
+    {
+      while (sa != a && *(sa-1) != '.')
+       sa--;
+      
+      while (sb != b && *(sb-1) != '.')
+       sb--;
+
+      ca = sa;
+      cb = sb;
+
+      while (1) 
+       {
+         if (ca == ea)
+           {
+             if (cb == eb)
+               break;
+             
+             return -1;
+           }
+         
+         if (cb == eb)
+           return 1;
+         
+         ac = (unsigned char) *ca++;
+         bc = (unsigned char) *cb++;
+         
+         if (ac >= 'A' && ac <= 'Z')
+           ac += 'a' - 'A';
+         if (bc >= 'A' && bc <= 'Z')
+           bc += 'a' - 'A';
+         
+         if (ac < bc)
+           return -1;
+         else if (ac != bc)
+           return 1;
+       }
+
+     
+      if (sa == a)
+       {
+         if (sb == b)
+           return 0;
+         
+         return -1;
+       }
+      
+      if (sb == b)
+       return 1;
+      
+      ea = sa--;
+      eb = sb--;
+    }
+}
+
+/* Find all the NSEC or NSEC3 records in a reply.
+   return an array of pointers to them. */
+static int find_nsec_records(struct dns_header *header, size_t plen, unsigned char ***nsecsetp, int *nsecsetl, int class_reqd)
+{
+  static unsigned char **nsecset = NULL;
+  static int nsecset_sz = 0;
+  
+  int type_found = 0;
+  unsigned char *p = skip_questions(header, plen);
+  int type, class, rdlen, i, nsecs_found;
+
+  /* Move to NS section */
+  if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
+    return 0;
+  
+  for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
+    {
+      unsigned char *pstart = p;
+      
+      if (!(p = skip_name(p, header, plen, 10)))
+       return 0;
+      
+      GETSHORT(type, p); 
+      GETSHORT(class, p);
+      p += 4; /* TTL */
+      GETSHORT(rdlen, p);
+
+      if (class == class_reqd && (type == T_NSEC || type == T_NSEC3))
+       {
+         /* No mixed NSECing 'round here, thankyouverymuch */
+         if (type_found == T_NSEC && type == T_NSEC3)
+           return 0;
+         if (type_found == T_NSEC3 && type == T_NSEC)
+           return 0;
+
+         type_found = type;
+
+         if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
+           return 0; 
+         
+         nsecset[nsecs_found++] = pstart;
+       }
+      
+      if (!ADD_RDLEN(header, p, plen, rdlen))
+       return 0;
+    }
+  
+  *nsecsetp = nsecset;
+  *nsecsetl = nsecs_found;
+  
+  return type_found;
+}
+
+static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
+                                   char *workspace1, char *workspace2, char *name, int type, int *nons)
+{
+  int i, rc, rdlen;
+  unsigned char *p, *psave;
+  int offset = (type & 0xff) >> 3;
+  int mask = 0x80 >> (type & 0x07);
+
+  if (nons)
+    *nons = 0;
+  
+  /* Find NSEC record that proves name doesn't exist */
+  for (i = 0; i < nsec_count; i++)
+    {
+      p = nsecs[i];
+      if (!extract_name(header, plen, &p, workspace1, 1, 10))
+       return STAT_BOGUS;
+      p += 8; /* class, type, TTL */
+      GETSHORT(rdlen, p);
+      psave = p;
+      if (!extract_name(header, plen, &p, workspace2, 1, 10))
+       return STAT_BOGUS;
+      
+      rc = hostname_cmp(workspace1, name);
+      
+      if (rc == 0)
+       {
+         /* 4035 para 5.4. Last sentence */
+         if (type == T_NSEC || type == T_RRSIG)
+           return STAT_SECURE;
+
+         /* NSEC with the same name as the RR we're testing, check
+            that the type in question doesn't appear in the type map */
+         rdlen -= p - psave;
+         /* rdlen is now length of type map, and p points to it */
+         
+         /* If we can prove that there's no NS record, return that information. */
+         if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) == 0)
+           *nons = 1;
+         
+         while (rdlen >= 2)
+           {
+             if (!CHECK_LEN(header, p, plen, rdlen))
+               return STAT_BOGUS;
+             
+             if (p[0] == type >> 8)
+               {
+                 /* Does the NSEC say our type exists? */
+                 if (offset < p[1] && (p[offset+2] & mask) != 0)
+                   return STAT_BOGUS;
+                 
+                 break; /* finshed checking */
+               }
+             
+             rdlen -= p[1];
+             p +=  p[1];
+           }
+         
+         return STAT_SECURE;
+       }
+      else if (rc == -1)
+       {
+         /* Normal case, name falls between NSEC name and next domain name,
+            wrap around case, name falls between NSEC name (rc == -1) and end */
+         if (hostname_cmp(workspace2, name) >= 0 || hostname_cmp(workspace1, workspace2) >= 0)
+           return STAT_SECURE;
+       }
+      else 
+       {
+         /* wrap around case, name falls between start and next domain name */
+         if (hostname_cmp(workspace1, workspace2) >= 0 && hostname_cmp(workspace2, name) >=0 )
+           return STAT_SECURE;
+       }
+    }
+  
+  return STAT_BOGUS;
+}
+
+/* return digest length, or zero on error */
+static int hash_name(char *in, unsigned char **out, struct nettle_hash const *hash, 
+                    unsigned char *salt, int salt_len, int iterations)
+{
+  void *ctx;
+  unsigned char *digest;
+  int i;
+
+  if (!hash_init(hash, &ctx, &digest))
+    return 0;
+  hash->update(ctx, to_wire(in), (unsigned char *)in);
+  hash->update(ctx, salt_len, salt);
+  hash->digest(ctx, hash->digest_size, digest);
+
+  for(i = 0; i < iterations; i++)
+    {
+      hash->update(ctx, hash->digest_size, digest);
+      hash->update(ctx, salt_len, salt);
+      hash->digest(ctx, hash->digest_size, digest);
+    }
+   
+  from_wire(in);
+
+  *out = digest;
+  return hash->digest_size;
+}
+
+/* Decode base32 to first "." or end of string */
+static int base32_decode(char *in, unsigned char *out)
+{
+  int oc, on, c, mask, i;
+  unsigned char *p = out;
+  for (c = *in, oc = 0, on = 0; c != 0 && c != '.'; c = *++in) 
+    {
+      if (c >= '0' && c <= '9')
+       c -= '0';
+      else if (c >= 'a' && c <= 'v')
+       c -= 'a', c += 10;
+      else if (c >= 'A' && c <= 'V')
+       c -= 'A', c += 10;
+      else
+       return 0;
+      
+      for (mask = 0x10, i = 0; i < 5; i++)
+        {
+         if (c & mask)
+           oc |= 1;
+         mask = mask >> 1;
+         if (((++on) & 7) == 0)
+           *p++ = oc;
+         oc = oc << 1;
+       }
+    }
+  
+  if ((on & 7) != 0)
+    return 0;
+
+  return p - out;
+}
+
+static int check_nsec3_coverage(struct dns_header *header, size_t plen, int digest_len, unsigned char *digest, int type,
+                               char *workspace1, char *workspace2, unsigned char **nsecs, int nsec_count, int *nons)
+{
+  int i, hash_len, salt_len, base32_len, rdlen;
+  unsigned char *p, *psave;
+
+  for (i = 0; i < nsec_count; i++)
+    if ((p = nsecs[i]))
+      {
+               if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
+           !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
+         return 0;
+       
+       p += 8; /* class, type, TTL */
+       GETSHORT(rdlen, p);
+       psave = p;
+       p += 4; /* algo, flags, iterations */
+       salt_len = *p++; /* salt_len */
+       p += salt_len; /* salt */
+       hash_len = *p++; /* p now points to next hashed name */
+       
+       if (!CHECK_LEN(header, p, plen, hash_len))
+         return 0;
+       
+       if (digest_len == base32_len && hash_len == base32_len)
+         {
+           int rc = memcmp(workspace2, digest, digest_len);
+
+           if (rc == 0)
+             {
+               /* We found an NSEC3 whose hashed name exactly matches the query, so
+                  we just need to check the type map. p points to the RR data for the record. */
+               
+               int offset = (type & 0xff) >> 3;
+               int mask = 0x80 >> (type & 0x07);
+               
+               p += hash_len; /* skip next-domain hash */
+               rdlen -= p - psave;
+
+               if (!CHECK_LEN(header, p, plen, rdlen))
+                 return 0;
+               
+               /* If we can prove that there's no NS record, return that information. */
+               if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) == 0)
+                 *nons = 1;
+               
+               while (rdlen >= 2)
+                 {
+                   if (p[0] == type >> 8)
+                     {
+                       /* Does the NSEC3 say our type exists? */
+                       if (offset < p[1] && (p[offset+2] & mask) != 0)
+                         return STAT_BOGUS;
+                       
+                       break; /* finshed checking */
+                     }
+                   
+                   rdlen -= p[1];
+                   p +=  p[1];
+                 }
+
+               return 1;
+             }
+           else if (rc < 0)
+             {
+               /* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
+                  wrap around case, name-hash falls between NSEC3 name-hash and end */
+               if (memcmp(p, digest, digest_len) >= 0 || memcmp(workspace2, p, digest_len) >= 0)
+                 return 1;
+             }
+           else 
+             {
+               /* wrap around case, name falls between start and next domain name */
+               if (memcmp(workspace2, p, digest_len) >= 0 && memcmp(p, digest, digest_len) >= 0)
+                 return 1;
+             }
+         }
+      }
+  return 0;
+}
+
+static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
+                                    char *workspace1, char *workspace2, char *name, int type, char *wildname, int *nons)
+{
+  unsigned char *salt, *p, *digest;
+  int digest_len, i, iterations, salt_len, base32_len, algo = 0;
+  struct nettle_hash const *hash;
+  char *closest_encloser, *next_closest, *wildcard;
+  
+  if (nons)
+    *nons = 0;
+  
+  /* Look though the NSEC3 records to find the first one with 
+     an algorithm we support (currently only algo == 1).
+
+     Take the algo, iterations, and salt of that record
+     as the ones we're going to use, and prune any 
+     that don't match. */
+  
+  for (i = 0; i < nsec_count; i++)
+    {
+      if (!(p = skip_name(nsecs[i], header, plen, 15)))
+       return STAT_BOGUS; /* bad packet */
+      
+      p += 10; /* type, class, TTL, rdlen */
+      algo = *p++;
+      
+      if (algo == 1)
+       break; /* known algo */
+    }
+
+  /* No usable NSEC3s */
+  if (i == nsec_count)
+    return STAT_BOGUS;
+
+  p++; /* flags */
+  GETSHORT (iterations, p);
+  salt_len = *p++;
+  salt = p;
+  if (!CHECK_LEN(header, salt, plen, salt_len))
+    return STAT_BOGUS; /* bad packet */
+    
+  /* Now prune so we only have NSEC3 records with same iterations, salt and algo */
+  for (i = 0; i < nsec_count; i++)
+    {
+      unsigned char *nsec3p = nsecs[i];
+      int this_iter;
+
+      nsecs[i] = NULL; /* Speculative, will be restored if OK. */
+      
+      if (!(p = skip_name(nsec3p, header, plen, 15)))
+       return STAT_BOGUS; /* bad packet */
+      
+      p += 10; /* type, class, TTL, rdlen */
+      
+      if (*p++ != algo)
+       continue;
+      p++; /* flags */
+      
+      GETSHORT(this_iter, p);
+      if (this_iter != iterations)
+       continue;
+
+      if (salt_len != *p++)
+       continue;
+      
+      if (!CHECK_LEN(header, p, plen, salt_len))
+       return STAT_BOGUS; /* bad packet */
+
+      if (memcmp(p, salt, salt_len) != 0)
+       continue;
+
+      /* All match, put the pointer back */
+      nsecs[i] = nsec3p;
+    }
+
+  /* Algo is checked as 1 above */
+  if (!(hash = hash_find("sha1")))
+    return STAT_BOGUS;
+
+  if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
+    return STAT_BOGUS;
+  
+  if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, nons))
+    return STAT_SECURE;
+
+  /* Can't find an NSEC3 which covers the name directly, we need the "closest encloser NSEC3" 
+     or an answer inferred from a wildcard record. */
+  closest_encloser = name;
+  next_closest = NULL;
+
+  do
+    {
+      if (*closest_encloser == '.')
+       closest_encloser++;
+
+      if (wildname && hostname_isequal(closest_encloser, wildname))
+       break;
+
+      if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0)
+       return STAT_BOGUS;
+      
+      for (i = 0; i < nsec_count; i++)
+       if ((p = nsecs[i]))
+         {
+           if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
+               !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
+             return STAT_BOGUS;
+         
+           if (digest_len == base32_len &&
+               memcmp(digest, workspace2, digest_len) == 0)
+             break; /* Gotit */
+         }
+      
+      if (i != nsec_count)
+       break;
+      
+      next_closest = closest_encloser;
+    }
+  while ((closest_encloser = strchr(closest_encloser, '.')));
+  
+  if (!closest_encloser)
+    return STAT_BOGUS;
+  
+  /* Look for NSEC3 that proves the non-existence of the next-closest encloser */
+  if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0)
+    return STAT_BOGUS;
+
+  if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
+    return STAT_BOGUS;
+  
+  /* Finally, check that there's no seat of wildcard synthesis */
+  if (!wildname)
+    {
+      if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
+       return STAT_BOGUS;
+      
+      wildcard--;
+      *wildcard = '*';
+      
+      if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
+       return STAT_BOGUS;
+      
+      if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
+       return STAT_BOGUS;
+    }
+  
+  return STAT_SECURE;
+}
+    
+/* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */
+/* Returns are the same as validate_rrset, plus the class if the missing key is in *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)
+{
+  unsigned char *ans_start, *qname, *p1, *p2, **nsecs;
+  int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype;
+  int i, j, rc, nsec_count, cname_count = CNAME_CHAIN;
+  int nsec_type = 0, have_answer = 0;
+
+  if (neganswer)
+    *neganswer = 0;
+  
+  if (RCODE(header) == SERVFAIL || ntohs(header->qdcount) != 1)
+    return STAT_BOGUS;
+  
+  if (RCODE(header) != NXDOMAIN && RCODE(header) != NOERROR)
+    return STAT_INSECURE;
+
+  qname = p1 = (unsigned char *)(header+1);
+  
+  if (!extract_name(header, plen, &p1, name, 1, 4))
+    return STAT_BOGUS;
+
+  GETSHORT(qtype, p1);
+  GETSHORT(qclass, p1);
+  ans_start = p1;
+
+  if (qtype == T_ANY)
+    have_answer = 1;
+  /* Can't validate an RRISG query */
+  if (qtype == T_RRSIG)
+    return STAT_INSECURE;
+ cname_loop:
+  for (j = ntohs(header->ancount); j != 0; j--) 
+    {
+      /* leave pointer to missing name in qname */
+           
+      if (!(rc = extract_name(header, plen, &p1, name, 0, 10)))
+       return STAT_BOGUS; /* bad packet */
+      
+      GETSHORT(type2, p1); 
+      GETSHORT(class2, p1);
+      p1 += 4; /* TTL */
+      GETSHORT(rdlen2, p1);
+
+      if (rc == 1 && qclass == class2)
+       {
+         /* Do we have an answer for the question? */
+         if (type2 == qtype)
+           {
+             have_answer = 1;
+             break;
+           }
+         else if (type2 == T_CNAME)
+           {
+             qname = p1;
+             
+             /* looped CNAMES */
+             if (!cname_count-- || !extract_name(header, plen, &p1, name, 1, 0))
+               return STAT_BOGUS;
+              
+             p1 = ans_start;
+             goto cname_loop;
+           }
+       } 
+
+      if (!ADD_RDLEN(header, p1, plen, rdlen2))
+       return STAT_BOGUS;
+    }
+   
+  if (neganswer && !have_answer)
+    *neganswer = 1;
+  
+  /* No data, therefore no sigs */
+  if (ntohs(header->ancount) + ntohs(header->nscount) == 0)
+    {
+      *keyname = 0;
+      return STAT_NO_SIG;
+    }
+
+  for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++)
+    {
+      if (!extract_name(header, plen, &p1, name, 1, 10))
+       return STAT_BOGUS; /* bad packet */
+      
+      GETSHORT(type1, p1);
+      GETSHORT(class1, p1);
+      p1 += 4; /* TTL */
+      GETSHORT(rdlen1, p1);
+      
+      /* Don't try and validate RRSIGs! */
+      if (type1 != T_RRSIG)
+       {
+         /* Check if we've done this RRset already */
+         for (p2 = ans_start, j = 0; j < i; j++)
+           {
+             if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
+               return STAT_BOGUS; /* bad packet */
+             
+             GETSHORT(type2, p2);
+             GETSHORT(class2, p2);
+             p2 += 4; /* TTL */
+             GETSHORT(rdlen2, p2);
+             
+             if (type2 == type1 && class2 == class1 && rc == 1)
+               break; /* Done it before: name, type, class all match. */
+             
+             if (!ADD_RDLEN(header, p2, plen, rdlen2))
+               return STAT_BOGUS;
+           }
+         
+         /* Not done, validate now */
+         if (j == i)
+           {
+             int ttl, keytag, algo, digest, type_covered;
+             unsigned char *psave;
+             struct all_addr a;
+             struct blockdata *key;
+             struct crec *crecp;
+             char *wildname;
+             int have_wildcard = 0;
+
+             rc = validate_rrset(now, header, plen, class1, type1, name, keyname, &wildname, NULL, 0, 0, 0);
+             
+             if (rc == STAT_SECURE_WILDCARD)
+               {
+                 have_wildcard = 1;
+
+                 /* An attacker replay a wildcard answer with a different
+                    answer and overlay a genuine RR. To prove this
+                    hasn't happened, the answer must prove that
+                    the gennuine record doesn't exist. Check that here. */
+                 if (!nsec_type && !(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1)))
+                   return STAT_BOGUS; /* No NSECs or bad packet */
+                 
+                 if (nsec_type == T_NSEC)
+                   rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1, NULL);
+                 else
+                   rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, 
+                                                  keyname, name, type1, wildname, NULL);
+                 
+                 if (rc != STAT_SECURE)
+                   return rc;
+               } 
+             else if (rc != STAT_SECURE)
+               {
+                 if (class)
+                   *class = class1; /* Class for DS or DNSKEY */
+
+                 if (rc == STAT_NO_SIG)
+                   {
+                     /* If we dropped off the end of a CNAME chain, return
+                        STAT_NO_SIG and the last name is keyname. This is used for proving non-existence
+                        if DS records in CNAME chains. */
+                     if (cname_count == CNAME_CHAIN || i < ntohs(header->ancount)) 
+                       /* No CNAME chain, or no sig in answer section, return empty name. */
+                       *keyname = 0;
+                     else if (!extract_name(header, plen, &qname, keyname, 1, 0))
+                       return STAT_BOGUS;
+                   }
+                 return rc;
+               }
+             
+             /* Cache RRsigs in answer section, and if we just validated a DS RRset, cache it */
+             cache_start_insert();
+             
+             for (p2 = ans_start, j = 0; j < ntohs(header->ancount); j++)
+               {
+                 if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
+                   return STAT_BOGUS; /* bad packet */
+                     
+                 GETSHORT(type2, p2);
+                 GETSHORT(class2, p2);
+                 GETLONG(ttl, p2);
+                 GETSHORT(rdlen2, p2);
+                      
+                 if (!CHECK_LEN(header, p2, plen, rdlen2))
+                   return STAT_BOGUS; /* bad packet */
+                 
+                 if (class2 == class1 && rc == 1)
+                   { 
+                     psave = p2;
+
+                     if (type1 == T_DS && type2 == T_DS)
+                       {
+                         if (rdlen2 < 4)
+                           return STAT_BOGUS; /* bad packet */
+                         
+                         GETSHORT(keytag, p2);
+                         algo = *p2++;
+                         digest = *p2++;
+                         
+                         /* Cache needs to known class for DNSSEC stuff */
+                         a.addr.dnssec.class = class2;
+                         
+                         if ((key = blockdata_alloc((char*)p2, rdlen2 - 4)))
+                           {
+                             if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DS | F_DNSSECOK)))
+                               blockdata_free(key);
+                             else
+                               {
+                                 a.addr.keytag = keytag;
+                                 log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
+                                 crecp->addr.ds.digest = digest;
+                                 crecp->addr.ds.keydata = key;
+                                 crecp->addr.ds.algo = algo;
+                                 crecp->addr.ds.keytag = keytag;
+                                 crecp->addr.ds.keylen = rdlen2 - 4; 
+                               } 
+                           }
+                       }
+                     else if (type2 == T_RRSIG)
+                       {
+                         if (rdlen2 < 18)
+                           return STAT_BOGUS; /* bad packet */
+                         
+                         GETSHORT(type_covered, p2);
+
+                         if (type_covered == type1 && 
+                             (type_covered == T_A || type_covered == T_AAAA ||
+                              type_covered == T_CNAME || type_covered == T_DS || 
+                              type_covered == T_DNSKEY || type_covered == T_PTR)) 
+                           {
+                             a.addr.dnssec.type = type_covered;
+                             a.addr.dnssec.class = class1;
+                             
+                             algo = *p2++;
+                             p2 += 13; /* labels, orig_ttl, expiration, inception */
+                             GETSHORT(keytag, p2);
+                             
+                             /* We don't cache sigs for wildcard answers, because to reproduce the
+                                answer from the cache will require one or more NSEC/NSEC3 records 
+                                which we don't cache. The lack of the RRSIG ensures that a query for
+                                this RRset asking for a secure answer will always be forwarded. */
+                             if (!have_wildcard && (key = blockdata_alloc((char*)psave, rdlen2)))
+                               {
+                                 if (!(crecp = cache_insert(name, &a, now, ttl,  F_FORWARD | F_DNSKEY | F_DS)))
+                                   blockdata_free(key);
+                                 else
+                                   {
+                                     crecp->addr.sig.keydata = key;
+                                     crecp->addr.sig.keylen = rdlen2;
+                                     crecp->addr.sig.keytag = keytag;
+                                     crecp->addr.sig.type_covered = type_covered;
+                                     crecp->addr.sig.algo = algo;
+                                   }
+                               }
+                           }
+                       }
+                     
+                     p2 = psave;
+                   }
+                 
+                 if (!ADD_RDLEN(header, p2, plen, rdlen2))
+                   return STAT_BOGUS; /* bad packet */
+               }
+                 
+             cache_end_insert();
+           }
+       }
+
+      if (!ADD_RDLEN(header, p1, plen, rdlen1))
+       return STAT_BOGUS;
+    }
+
+  /* OK, all the RRsets validate, now see if we have a NODATA or NXDOMAIN reply */
+  if (have_answer)
+    return STAT_SECURE;
+     
+  /* NXDOMAIN or NODATA reply, prove that (name, class1, type1) can't exist */
+  /* First marshall the NSEC records, if we've not done it previously */
+  if (!nsec_type && !(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, qclass)))
+    {
+      /* No NSEC records. If we dropped off the end of a CNAME chain, return
+        STAT_NO_SIG and the last name is keyname. This is used for proving non-existence
+        if DS records in CNAME chains. */
+      if (cname_count == CNAME_CHAIN) /* No CNAME chain, return empty name. */
+       *keyname = 0;
+      else if (!extract_name(header, plen, &qname, keyname, 1, 0))
+       return STAT_BOGUS;
+      return STAT_NO_SIG; /* No NSECs, this is probably a dangling CNAME pointing into
+                            an unsigned zone. Return STAT_NO_SIG to cause this to be proved. */
+    }
+   
+  /* Get name of missing answer */
+  if (!extract_name(header, plen, &qname, name, 1, 0))
+    return STAT_BOGUS;
+  
+  if (nsec_type == T_NSEC)
+    return prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, nons);
+  else
+    return prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, NULL, nons);
+}
+
+/* Chase the CNAME chain in the packet until the first record which _doesn't validate.
+   Needed for proving answer in unsigned space.
+   Return STAT_NEED_* 
+          STAT_BOGUS - error
+          STAT_INSECURE - name of first non-secure record in name 
+*/
+int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname)
+{
+  unsigned char *p = (unsigned char *)(header+1);
+  int type, class, qclass, rdlen, j, rc;
+  int cname_count = CNAME_CHAIN;
+  char *wildname;
+
+  /* Get question */
+  if (!extract_name(header, plen, &p, name, 1, 4))
+    return STAT_BOGUS;
+  
+  p +=2; /* type */
+  GETSHORT(qclass, p);
+
+  while (1)
+    {
+      for (j = ntohs(header->ancount); j != 0; j--) 
+       {
+         if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
+           return STAT_BOGUS; /* bad packet */
+         
+         GETSHORT(type, p); 
+         GETSHORT(class, p);
+         p += 4; /* TTL */
+         GETSHORT(rdlen, p);
+
+         /* Not target, loop */
+         if (rc == 2 || qclass != class)
+           {
+             if (!ADD_RDLEN(header, p, plen, rdlen))
+               return STAT_BOGUS;
+             continue;
+           }
+         
+         /* Got to end of CNAME chain. */
+         if (type != T_CNAME)
+           return STAT_INSECURE;
+         
+         /* validate CNAME chain, return if insecure or need more data */
+         rc = validate_rrset(now, header, plen, class, type, name, keyname, &wildname, NULL, 0, 0, 0);
+          
+         if (rc == STAT_SECURE_WILDCARD)
+           {
+             int nsec_type, nsec_count, i;
+             unsigned char **nsecs;
+
+             /* An attacker can replay a wildcard answer with a different
+                answer and overlay a genuine RR. To prove this
+                hasn't happened, the answer must prove that
+                the genuine record doesn't exist. Check that here. */
+             if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class)))
+               return STAT_BOGUS; /* No NSECs or bad packet */
+             
+             /* Note that we're called here because something didn't validate in validate_reply,
+                so we can't assume that any NSEC records have been validated. We do them by steam here */
+
+             for (i = 0; i < nsec_count; i++)
+               {
+                 unsigned char *p1 = nsecs[i];
+                 
+                 if (!extract_name(header, plen, &p1, daemon->workspacename, 1, 0))
+                   return STAT_BOGUS;
+
+                 rc = validate_rrset(now, header, plen, class, nsec_type, daemon->workspacename, keyname, NULL, NULL, 0, 0, 0);
+
+                 /* NSECs can't be wildcards. */
+                 if (rc == STAT_SECURE_WILDCARD)
+                   rc = STAT_BOGUS;
+
+                 if (rc != STAT_SECURE)
+                   return rc;
+               }
+
+             if (nsec_type == T_NSEC)
+               rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type, NULL);
+             else
+               rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, 
+                                              keyname, name, type, wildname, NULL);
+             
+             if (rc != STAT_SECURE)
+               return rc;
+           }
+         
+         if (rc != STAT_SECURE)
+           {
+             if (rc == STAT_NO_SIG)
+               rc = STAT_INSECURE;
+             return rc;
+           }
+
+         /* Loop down CNAME chain/ */
+         if (!cname_count-- || 
+             !extract_name(header, plen, &p, name, 1, 0) ||
+             !(p = skip_questions(header, plen)))
+           return STAT_BOGUS;
+         
+         break;
+       }
+
+      /* End of CNAME chain */
+      return STAT_INSECURE;    
+    }
+}
+
+
+/* Compute keytag (checksum to quickly index a key). See RFC4034 */
+int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen)
+{
+  if (alg == 1)
+    {
+      /* Algorithm 1 (RSAMD5) has a different (older) keytag calculation algorithm.
+         See RFC4034, Appendix B.1 */
+      return key[keylen-4] * 256 + key[keylen-3];
+    }
+  else
+    {
+      unsigned long ac = flags + 0x300 + alg;
+      int i;
+
+      for (i = 0; i < keylen; ++i)
+        ac += (i & 1) ? key[i] : key[i] << 8;
+
+      ac += (ac >> 16) & 0xffff;
+      return ac & 0xffff;
+    }
+}
+
+size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, 
+                            int type, union mysockaddr *addr, int edns_pktsz)
+{
+  unsigned char *p;
+  char *types = querystr("dnssec-query", type);
+  size_t ret;
+
+  if (addr->sa.sa_family == AF_INET) 
+    log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, name, (struct all_addr *)&addr->in.sin_addr, types);
+#ifdef HAVE_IPV6
+  else
+    log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, name, (struct all_addr *)&addr->in6.sin6_addr, types);
+#endif
+  
+  header->qdcount = htons(1);
+  header->ancount = htons(0);
+  header->nscount = htons(0);
+  header->arcount = htons(0);
+
+  header->hb3 = HB3_RD; 
+  SET_OPCODE(header, QUERY);
+  /* For debugging, set Checking Disabled, otherwise, have the upstream check too,
+     this allows it to select auth servers when one is returning bad data. */
+  header->hb4 = option_bool(OPT_DNSSEC_DEBUG) ? HB4_CD : 0;
+
+  /* ID filled in later */
+
+  p = (unsigned char *)(header+1);
+       
+  p = do_rfc1035_name(p, name);
+  *p++ = 0;
+  PUTSHORT(type, p);
+  PUTSHORT(class, p);
+
+  ret = add_do_bit(header, p - (unsigned char *)header, end);
+
+  if (find_pseudoheader(header, ret, NULL, &p, NULL))
+    PUTSHORT(edns_pktsz, p);
+
+  return ret;
+}
+
+/* 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, 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 (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
+       {
+         /* 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 = get_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;
+}
+       
+
+size_t filter_rrsigs(struct dns_header *header, size_t plen)
+{
+  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 ((type == T_NSEC || type == T_NSEC3 || type == T_RRSIG) && 
+         (type != qtype || class != qclass))
+       {
+         if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
+           return plen; 
+         
+         rrs[rr_found++] = pstart;
+
+         if (!ADD_RDLEN(header, p, plen, rdlen))
+           return plen;
+         
+         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++;
+       }
+      else if (!ADD_RDLEN(header, p, plen, rdlen))
+       return plen;
+    }
+  
+  /* 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+1)) + 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;
+}
+
+unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name)
+{
+  int q;
+  unsigned int len;
+  unsigned char *p = (unsigned char *)(header+1);
+  const struct nettle_hash *hash;
+  void *ctx;
+  unsigned char *digest;
+  
+  if (!(hash = hash_find("sha1")) || !hash_init(hash, &ctx, &digest))
+    return NULL;
+  
+  for (q = ntohs(header->qdcount); q != 0; q--) 
+    {
+      if (!extract_name(header, plen, &p, name, 1, 4))
+       break; /* bad packet */
+      
+      len = to_wire(name);
+      hash->update(ctx, len, (unsigned char *)name);
+      /* CRC the class and type as well */
+      hash->update(ctx, 4, p);
+
+      p += 4;
+      if (!CHECK_LEN(header, p, plen, 0))
+       break; /* bad packet */
+    }
+  
+  hash->digest(ctx, hash->digest_size, digest);
+  return digest;
+}
+
+#endif /* HAVE_DNSSEC */
diff --git a/src/domain.c b/src/domain.c
new file mode 100644 (file)
index 0000000..278698c
--- /dev/null
@@ -0,0 +1,232 @@
+/* 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"
+
+
+static struct cond_domain *search_domain(struct in_addr addr, struct cond_domain *c);
+#ifdef HAVE_IPV6
+static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c);
+#endif
+
+
+int is_name_synthetic(int flags, char *name, struct all_addr *addr)
+{
+  char *p;
+  struct cond_domain *c = NULL;
+  int prot = AF_INET;
+
+#ifdef HAVE_IPV6
+  if (flags & F_IPV6)
+    prot = AF_INET6;
+#endif
+
+  for (c = daemon->synth_domains; c; c = c->next)
+    {
+      int found = 0;
+      char *tail, *pref;
+      
+      for (tail = name, pref = c->prefix; *tail != 0 && pref && *pref != 0; tail++, pref++)
+       {
+         unsigned int c1 = (unsigned char) *pref;
+         unsigned int c2 = (unsigned char) *tail;
+         
+         if (c1 >= 'A' && c1 <= 'Z')
+           c1 += 'a' - 'A';
+         if (c2 >= 'A' && c2 <= 'Z')
+           c2 += 'a' - 'A';
+         
+         if (c1 != c2)
+           break;
+       }
+      
+      if (pref && *pref != 0)
+       continue; /* prefix match fail */
+      
+      /* NB, must not alter name if we return zero */
+      for (p = tail; *p; p++)
+       {
+         char c = *p;
+         
+         if ((c >='0' && c <= '9') || c == '-')
+           continue;
+         
+#ifdef HAVE_IPV6
+         if (prot == AF_INET6 && ((c >='A' && c <= 'F') || (c >='a' && c <= 'f'))) 
+           continue;
+#endif
+         
+         break;
+       }
+      
+      if (*p != '.')
+       continue;
+      
+      *p = 0;  
+      
+      /* swap . or : for - */
+      for (p = tail; *p; p++)
+       if (*p == '-')
+         {
+           if (prot == AF_INET)
+             *p = '.';
+#ifdef HAVE_IPV6
+           else
+             *p = ':';
+#endif
+         }
+      
+      if (hostname_isequal(c->domain, p+1) && inet_pton(prot, tail, addr))
+       {
+         if (prot == AF_INET)
+           {
+             if (!c->is6 &&
+                 ntohl(addr->addr.addr4.s_addr) >= ntohl(c->start.s_addr) &&
+                 ntohl(addr->addr.addr4.s_addr) <= ntohl(c->end.s_addr))
+               found = 1;
+           }
+#ifdef HAVE_IPV6
+         else
+           {
+             u64 addrpart = addr6part(&addr->addr.addr6);
+             
+             if (c->is6 &&
+                 is_same_net6(&addr->addr.addr6, &c->start6, 64) &&
+                 addrpart >= addr6part(&c->start6) &&
+                 addrpart <= addr6part(&c->end6))
+               found = 1;
+           }
+#endif
+       }
+      
+      /* restore name */
+      for (p = tail; *p; p++)
+       if (*p == '.' || *p == ':')
+         *p = '-';
+      
+      *p = '.';
+
+      if (found)
+       return 1;
+    }
+  
+  return 0;
+}
+
+
+int is_rev_synth(int flag, struct all_addr *addr, char *name)
+{
+   struct cond_domain *c;
+
+   if (flag & F_IPV4 && (c = search_domain(addr->addr.addr4, daemon->synth_domains))) 
+     {
+       char *p;
+       
+       *name = 0;
+       if (c->prefix)
+        strncpy(name, c->prefix, MAXDNAME - ADDRSTRLEN);
+       
+       inet_ntop(AF_INET, &addr->addr.addr4, name + strlen(name), ADDRSTRLEN);
+       for (p = name; *p; p++)
+        if (*p == '.')
+          *p = '-';
+
+       strncat(name, ".", MAXDNAME);
+       strncat(name, c->domain, MAXDNAME);
+
+       return 1;
+     }
+
+#ifdef HAVE_IPV6
+   if (flag & F_IPV6 && (c = search_domain6(&addr->addr.addr6, daemon->synth_domains))) 
+     {
+       char *p;
+       
+       *name = 0;
+       if (c->prefix)
+        strncpy(name, c->prefix, MAXDNAME - ADDRSTRLEN);
+       
+       inet_ntop(AF_INET6, &addr->addr.addr6, name + strlen(name), ADDRSTRLEN);
+
+       /* IPv6 presentation address can start with ":", but valid domain names
+         cannot start with "-" so prepend a zero in that case. */
+       if (!c->prefix && *name == ':')
+        {
+          *name = '0';
+          inet_ntop(AF_INET6, &addr->addr.addr6, name+1, ADDRSTRLEN);
+        }
+
+       for (p = name; *p; p++)
+        if (*p == ':')
+          *p = '-';
+
+       strncat(name, ".", MAXDNAME);
+       strncat(name, c->domain, MAXDNAME);
+       
+       return 1;
+     }
+#endif
+   
+   return 0;
+}
+
+
+static struct cond_domain *search_domain(struct in_addr addr, struct cond_domain *c)
+{
+  for (; c; c = c->next)
+    if (!c->is6 &&
+       ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
+        ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
+      return c;
+
+  return NULL;
+}
+
+char *get_domain(struct in_addr addr)
+{
+  struct cond_domain *c;
+
+  if ((c = search_domain(addr, daemon->cond_domain)))
+    return c->domain;
+
+  return daemon->domain_suffix;
+} 
+
+#ifdef HAVE_IPV6
+static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c)
+{
+  u64 addrpart = addr6part(addr);
+  
+  for (; c; c = c->next)
+    if (c->is6 &&
+       is_same_net6(addr, &c->start6, 64) &&
+       addrpart >= addr6part(&c->start6) &&
+        addrpart <= addr6part(&c->end6))
+      return c;
+  
+  return NULL;
+}
+
+char *get_domain6(struct in6_addr *addr)
+{
+  struct cond_domain *c;
+
+  if (addr && (c = search_domain6(addr, daemon->cond_domain)))
+    return c->domain;
+
+  return daemon->domain_suffix;
+} 
+#endif
index 92bc6b0..2731b90 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
 
 #include "dnsmasq.h"
 
-static struct frec *lookup_frec(unsigned short id, unsigned int crc);
+static struct frec *lookup_frec(unsigned short id, void *hash);
 static struct frec *lookup_frec_by_sender(unsigned short id,
                                          union mysockaddr *addr,
-                                         unsigned int crc);
-static unsigned short get_id(unsigned int crc);
+                                         void *hash);
+static unsigned short get_id(void);
 static void free_frec(struct frec *f);
-static struct randfd *allocate_rfd(int family);
+
+#ifdef HAVE_DNSSEC
+static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n, 
+                          int class, char *name, char *keyname, struct server *server, int *keycount);
+static int do_check_sign(struct frec *forward, int status, time_t now, char *name, char *keyname);
+static int send_check_sign(struct frec *forward, time_t now, struct dns_header *header, size_t plen, 
+                          char *name, char *keyname);
+#endif
+
 
 /* Send a UDP packet with its source address set as "source" 
    unless nowild is true, when we just send it with the kernel default */
-static void send_from(int fd, int nowild, char *packet, size_t len, 
-                     union mysockaddr *to, struct all_addr *source,
-                     unsigned int iface)
+int send_from(int fd, int nowild, char *packet, size_t len, 
+             union mysockaddr *to, struct all_addr *source,
+             unsigned int iface)
 {
   struct msghdr msg;
   struct iovec iov[1]; 
@@ -70,7 +78,7 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
          p.ipi_spec_dst = source->addr.addr4;
          memcpy(CMSG_DATA(cmptr), &p, sizeof(p));
          msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
-         cmptr->cmsg_level = SOL_IP;
+         cmptr->cmsg_level = IPPROTO_IP;
          cmptr->cmsg_type = IP_PKTINFO;
 #elif defined(IP_SENDSRCADDR)
          memcpy(CMSG_DATA(cmptr), &(source->addr.addr4), sizeof(source->addr.addr4));
@@ -88,27 +96,23 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
          memcpy(CMSG_DATA(cmptr), &p, sizeof(p));
          msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
          cmptr->cmsg_type = daemon->v6pktinfo;
-         cmptr->cmsg_level = IPV6_LEVEL;
+         cmptr->cmsg_level = IPPROTO_IPV6;
        }
 #else
-      iface = 0; /* eliminate warning */
+      (void)iface; /* eliminate warning */
 #endif
     }
   
- retry:
-  if (sendmsg(fd, &msg, 0) == -1)
+  while (retry_send(sendmsg(fd, &msg, 0)));
+
+  /* If interface is still in DAD, EINVAL results - ignore that. */
+  if (errno != 0 && errno != EINVAL)
     {
-      /* certain Linux kernels seem to object to setting the source address in the IPv6 stack
-        by returning EINVAL from sendmsg. In that case, try again without setting the
-        source address, since it will nearly alway be correct anyway.  IPv6 stinks. */
-      if (errno == EINVAL && msg.msg_controllen)
-       {
-         msg.msg_controllen = 0;
-         goto retry;
-       }
-      if (retry_send())
-       goto retry;
+      my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno));
+      return 0;
     }
+  
+  return 1;
 }
           
 static unsigned int search_servers(time_t now, struct all_addr **addrpp, 
@@ -207,10 +211,10 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp,
          }
       }
   
-  if (flags == 0 && !(qtype & F_NSRR) && 
+  if (flags == 0 && !(qtype & F_QUERY) && 
       option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
-    /* don't forward simple names, make exception for NS queries and empty name. */
-    flags = F_NXDOMAIN;
+    /* don't forward A or AAAA queries for simple names, except the empty name */
+    flags = F_NOERR;
   
   if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now))
     flags = F_NOERR;
@@ -234,24 +238,83 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp,
 
 static int forward_query(int udpfd, union mysockaddr *udpaddr,
                         struct all_addr *dst_addr, unsigned int dst_iface,
-                        struct dns_header *header, size_t plen, time_t now, struct frec *forward)
+                        struct dns_header *header, size_t plen, time_t now, 
+                        struct frec *forward, int ad_reqd, int do_bit)
 {
   char *domain = NULL;
   int type = 0, norebind = 0;
   struct all_addr *addrp = NULL;
-  unsigned int crc = questions_crc(header, plen, daemon->namebuff);
   unsigned int flags = 0;
-  unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
   struct server *start = NULL;
-    
-  /* RFC 4035: sect 4.6 para 2 */
-  header->hb4 &= ~HB4_AD;
-  
+#ifdef HAVE_DNSSEC
+  void *hash = hash_questions(header, plen, daemon->namebuff);
+#else
+  unsigned int crc = questions_crc(header, plen, daemon->namebuff);
+  void *hash = &crc;
+#endif
+ unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
+ unsigned char *pheader;
+
+ (void)do_bit;
+
   /* may be no servers available. */
   if (!daemon->servers)
     forward = NULL;
-  else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc)))
+  else if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
     {
+      /* If we didn't get an answer advertising a maximal packet in EDNS,
+        fall back to 1280, which should work everywhere on IPv6.
+        If that generates an answer, it will become the new default
+        for this server */
+      forward->flags |= FREC_TEST_PKTSZ;
+      
+#ifdef HAVE_DNSSEC
+      /* If we've already got an answer to this query, but we're awaiting keys for validation,
+        there's no point retrying the query, retry the key query instead...... */
+      if (forward->blocking_query)
+       {
+         int fd;
+         
+         forward->flags &= ~FREC_TEST_PKTSZ;
+         
+         while (forward->blocking_query)
+           forward = forward->blocking_query;
+          
+         forward->flags |= FREC_TEST_PKTSZ;
+         
+         blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
+         plen = forward->stash_len;
+         
+         if (find_pseudoheader(header, plen, NULL, &pheader, NULL))
+           PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : forward->sentto->edns_pktsz, pheader);
+
+         if (forward->sentto->addr.sa.sa_family == AF_INET) 
+           log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
+#ifdef HAVE_IPV6
+         else
+           log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
+#endif
+  
+         if (forward->sentto->sfd)
+           fd = forward->sentto->sfd->fd;
+         else
+           {
+#ifdef HAVE_IPV6
+             if (forward->sentto->addr.sa.sa_family == AF_INET6)
+               fd = forward->rfd6->fd;
+             else
+#endif
+               fd = forward->rfd4->fd;
+           }
+         
+         while (retry_send( sendto(fd, (char *)header, plen, 0,
+                                   &forward->sentto->addr.sa,
+                                   sa_len(&forward->sentto->addr))));
+         
+         return 1;
+       }
+#endif
+
       /* retry on existing query, send to all available servers  */
       domain = forward->sentto->domain;
       forward->sentto->failed_queries++;
@@ -270,7 +333,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
       if (gotname)
        flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
       
-      if (!flags && !(forward = get_new_frec(now, NULL)))
+      if (!flags && !(forward = get_new_frec(now, NULL, 0)))
        /* table full - server failure. */
        flags = F_NEG;
       
@@ -280,15 +343,23 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
          forward->dest = *dst_addr;
          forward->iface = dst_iface;
          forward->orig_id = ntohs(header->id);
-         forward->new_id = get_id(crc);
+         forward->new_id = get_id();
          forward->fd = udpfd;
-         forward->crc = crc;
+         memcpy(forward->hash, hash, HASH_SIZE);
          forward->forwardall = 0;
+         forward->flags = 0;
          if (norebind)
            forward->flags |= FREC_NOREBIND;
          if (header->hb4 & HB4_CD)
            forward->flags |= FREC_CHECKING_DISABLED;
-
+         if (ad_reqd)
+           forward->flags |= FREC_AD_QUESTION;
+#ifdef HAVE_DNSSEC
+         forward->work_counter = DNSSEC_WORK;
+         if (do_bit)
+           forward->flags |= FREC_DO_QUESTION;
+#endif
+         
          header->id = htons(forward->new_id);
          
          /* In strict_order mode, always try servers in the order 
@@ -328,8 +399,38 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
       struct server *firstsentto = start;
       int forwarded = 0;
       
-      if (udpaddr && option_bool(OPT_ADD_MAC))
-       plen = add_mac(header, plen, ((char *) header) + PACKETSZ, udpaddr);
+      /* If a query is retried, use the log_id for the retry when logging the answer. */
+      forward->log_id = daemon->log_id;
+      
+      if (option_bool(OPT_ADD_MAC))
+       plen = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
+      
+      if (option_bool(OPT_CLIENT_SUBNET))
+       {
+         size_t new = add_source_addr(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source); 
+         if (new != plen)
+           {
+             plen = new;
+             forward->flags |= FREC_HAS_SUBNET;
+           }
+       }
+
+#ifdef HAVE_DNSSEC
+      if (option_bool(OPT_DNSSEC_VALID))
+       {
+         size_t new_plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
+        
+         /* For debugging, set Checking Disabled, otherwise, have the upstream check too,
+            this allows it to select auth servers when one is returning bad data. */
+         if (option_bool(OPT_DNSSEC_DEBUG))
+           header->hb4 |= HB4_CD;
+
+         if (new_plen != plen)
+           forward->flags |= FREC_ADDED_PHEADER;
+
+         plen = new_plen;
+       }
+#endif
       
       while (1)
        { 
@@ -339,7 +440,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
          
          if (type == (start->flags & SERV_TYPE) &&
              (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
-             !(start->flags & SERV_LITERAL_ADDRESS))
+             !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
            {
              int fd;
 
@@ -366,16 +467,27 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
                      daemon->rfd_save = forward->rfd4;
                      fd = forward->rfd4->fd;
                    }
+
+#ifdef HAVE_CONNTRACK
+                 /* Copy connection mark of incoming query to outgoing connection. */
+                 if (option_bool(OPT_CONNTRACK))
+                   {
+                     unsigned int mark;
+                     if (get_incoming_mark(&forward->source, &forward->dest, 0, &mark))
+                       setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
+                   }
+#endif
                }
+
+             if (find_pseudoheader(header, plen, NULL, &pheader, NULL))
+               PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : start->edns_pktsz, pheader);
              
-             if (sendto(fd, (char *)header, plen, 0,
-                        &start->addr.sa,
-                        sa_len(&start->addr)) == -1)
-               {
-                 if (retry_send())
-                   continue;
-               }
-             else
+             if (retry_send(sendto(fd, (char *)header, plen, 0,
+                                   &start->addr.sa,
+                                   sa_len(&start->addr))))
+               continue;
+           
+             if (errno == 0)
                {
                  /* Keep info in case we want to re-send this packet */
                  daemon->srv_save = start;
@@ -419,39 +531,80 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
   if (udpfd != -1)
     {
       plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
-      send_from(udpfd, option_bool(OPT_NOWILD), (char *)header, plen, udpaddr, dst_addr, dst_iface);
+      send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface);
     }
 
   return 0;
 }
 
-static size_t process_reply(struct dns_header *header, time_t now, 
-                           struct server *server, size_t n, int check_rebind, int checking_disabled)
+static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind, 
+                           int no_cache, int cache_secure, int bogusanswer, int ad_reqd, int do_bit, int added_pheader, 
+                           int check_subnet, union mysockaddr *query_source)
 {
   unsigned char *pheader, *sizep;
+  char **sets = 0;
   int munged = 0, is_sign;
   size_t plen; 
 
+  (void)ad_reqd;
+  (void)do_bit;
+  (void)bogusanswer;
+
+#ifdef HAVE_IPSET
+  if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL))
+    {
+      /* Similar algorithm to search_servers. */
+      struct ipsets *ipset_pos;
+      unsigned int namelen = strlen(daemon->namebuff);
+      unsigned int matchlen = 0;
+      for (ipset_pos = daemon->ipsets; ipset_pos; ipset_pos = ipset_pos->next) 
+       {
+         unsigned int domainlen = strlen(ipset_pos->domain);
+         char *matchstart = daemon->namebuff + namelen - domainlen;
+         if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) &&
+             (domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) &&
+             domainlen >= matchlen) 
+           {
+             matchlen = domainlen;
+             sets = ipset_pos->sets;
+           }
+       }
+    }
+#endif
+  
   /* If upstream is advertising a larger UDP packet size
      than we allow, trim it so that we don't get overlarge
      requests for the client. We can't do this for signed packets. */
 
-  if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)) && !is_sign)
+  if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)))
     {
       unsigned short udpsz;
       unsigned char *psave = sizep;
       
       GETSHORT(udpsz, sizep);
-      if (udpsz > daemon->edns_pktsz)
+
+      if (!is_sign && udpsz > daemon->edns_pktsz)
        PUTSHORT(daemon->edns_pktsz, psave);
+      
+      if (check_subnet && !check_source(header, plen, pheader, query_source))
+       {
+         my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
+         return 0;
+       }
+      
+      if (added_pheader)
+       {
+         pheader = 0; 
+         header->arcount = htons(0);
+       }
     }
-
+  
   /* RFC 4035 sect 4.6 para 3 */
-  if (!is_sign && !option_bool(OPT_DNSSEC))
+  if (!is_sign && !option_bool(OPT_DNSSEC_PROXY))
      header->hb4 &= ~HB4_AD;
-
+  
   if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN))
-    return n;
+    return resize_packet(header, n, pheader, plen);
   
   /* Complain loudly if the upstream server is non-recursive. */
   if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR && ntohs(header->ancount) == 0 &&
@@ -462,16 +615,19 @@ static size_t process_reply(struct dns_header *header, time_t now,
       if (!option_bool(OPT_LOG))
        server->flags |= SERV_WARNED_RECURSIVE;
     }  
-    
+
   if (daemon->bogus_addr && RCODE(header) != NXDOMAIN &&
       check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
     {
       munged = 1;
       SET_RCODE(header, NXDOMAIN);
       header->hb3 &= ~HB3_AA;
+      cache_secure = 0;
     }
   else 
     {
+      int doctored = 0;
+      
       if (RCODE(header) == NXDOMAIN && 
          extract_request(header, n, daemon->namebuff, NULL) &&
          check_for_local_domain(daemon->namebuff, now))
@@ -482,15 +638,42 @@ static size_t process_reply(struct dns_header *header, time_t now,
          munged = 1;
          header->hb3 |= HB3_AA;
          SET_RCODE(header, NOERROR);
+         cache_secure = 0;
        }
       
-      if (extract_addresses(header, n, daemon->namebuff, now, is_sign, check_rebind, checking_disabled))
+      if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
        {
          my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
          munged = 1;
+         cache_secure = 0;
+       }
+
+      if (doctored)
+       cache_secure = 0;
+    }
+  
+#ifdef HAVE_DNSSEC
+  if (bogusanswer && !(header->hb4 & HB4_CD)) 
+    {
+      if (!option_bool(OPT_DNSSEC_DEBUG))
+       {
+         /* Bogus reply, turn into SERVFAIL */
+         SET_RCODE(header, SERVFAIL);
+         munged = 1;
        }
     }
+
+  if (option_bool(OPT_DNSSEC_VALID))
+    header->hb4 &= ~HB4_AD;
   
+  if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
+    header->hb4 |= HB4_AD;
+
+  /* If the requestor didn't set the DO bit, don't return DNSSEC info. */
+  if (!do_bit)
+    n = filter_rrsigs(header, n);
+#endif
+
   /* do this after extract_addresses. Ensure NODATA reply and remove
      nameserver info. */
   
@@ -499,6 +682,7 @@ static size_t process_reply(struct dns_header *header, time_t now,
       header->ancount = htons(0);
       header->nscount = htons(0);
       header->arcount = htons(0);
+      header->hb3 &= ~HB3_TC;
     }
   
   /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
@@ -516,10 +700,14 @@ void reply_query(int fd, int family, time_t now)
   union mysockaddr serveraddr;
   struct frec *forward;
   socklen_t addrlen = sizeof(serveraddr);
-  ssize_t n = recvfrom(fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
+  ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveraddr.sa, &addrlen);
   size_t nn;
   struct server *server;
-  
+  void *hash;
+#ifndef HAVE_DNSSEC
+  unsigned int crc;
+#endif
+
   /* packet buffer overwritten */
   daemon->srv_save = NULL;
   
@@ -530,22 +718,40 @@ void reply_query(int fd, int family, time_t now)
     serveraddr.in6.sin6_flowinfo = 0;
 #endif
   
+  header = (struct dns_header *)daemon->packet;
+  
+  if (n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR))
+    return;
+  
   /* spoof check: answer must come from known server, */
   for (server = daemon->servers; server; server = server->next)
     if (!(server->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
        sockaddr_isequal(&server->addr, &serveraddr))
       break;
-   
-  header = (struct dns_header *)daemon->packet;
   
-  if (!server ||
-      n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) ||
-      !(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
+  if (!server)
     return;
-   
-  server = forward->sentto;
   
-  if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
+#ifdef HAVE_DNSSEC
+  hash = hash_questions(header, n, daemon->namebuff);
+#else
+  hash = &crc;
+  crc = questions_crc(header, n, daemon->namebuff);
+#endif
+  
+  if (!(forward = lookup_frec(ntohs(header->id), hash)))
+    return;
+  
+  /* log_query gets called indirectly all over the place, so 
+     pass these in global variables - sorry. */
+  daemon->log_display_id = forward->log_id;
+  daemon->log_source_addr = &forward->source;
+  
+  if (daemon->ignore_addr && RCODE(header) == NOERROR &&
+      check_for_ignored_address(header, n, daemon->ignore_addr))
+    return;
+
+  if (RCODE(header) == REFUSED &&
       !option_bool(OPT_ORDER) &&
       forward->forwardall == 0)
     /* for broken servers, attempt to send to another one. */
@@ -563,16 +769,18 @@ void reply_query(int fd, int family, time_t now)
          header->arcount = htons(0);
          if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
            {
-             header->hb3 &= ~(HB3_QR | HB3_TC);
-             forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
+             header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC);
+             header->hb4 &= ~(HB4_RA | HB4_RCODE);
+             forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0, 0);
              return;
            }
        }
     }   
-  
+   
+  server = forward->sentto;
   if ((forward->sentto->flags & SERV_TYPE) == 0)
     {
-      if (RCODE(header) == SERVFAIL || RCODE(header) == REFUSED)
+      if (RCODE(header) == REFUSED)
        server = NULL;
       else
        {
@@ -590,24 +798,307 @@ void reply_query(int fd, int family, time_t now)
       if (!option_bool(OPT_ALL_SERVERS))
        daemon->last_server = server;
     }
+  /* We tried resending to this server with a smaller maximum size and got an answer.
+     Make that permanent. To avoid reduxing the packet size for an single dropped packet,
+     only do this when we get a truncated answer, or one larger than the safe size. */
+  if (server && (forward->flags & FREC_TEST_PKTSZ) && 
+      ((header->hb3 & HB3_TC) || n >= SAFE_PKTSZ))
+    server->edns_pktsz = SAFE_PKTSZ;
   
   /* If the answer is an error, keep the forward record in place in case
      we get a good reply from another server. Kill it when we've
      had replies from all to avoid filling the forwarding table when
      everything is broken */
-  if (forward->forwardall == 0 || --forward->forwardall == 1 || 
-      (RCODE(header) != REFUSED && RCODE(header) != SERVFAIL))
+  if (forward->forwardall == 0 || --forward->forwardall == 1 || RCODE(header) != SERVFAIL)
     {
-      int check_rebind = !(forward->flags & FREC_NOREBIND);
+      int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
+
+      if (option_bool(OPT_NO_REBIND))
+       check_rebind = !(forward->flags & FREC_NOREBIND);
+      
+      /*   Don't cache replies where DNSSEC validation was turned off, either
+          the upstream server told us so, or the original query specified it.  */
+      if ((header->hb4 & HB4_CD) || (forward->flags & FREC_CHECKING_DISABLED))
+       no_cache_dnssec = 1;
+      
+#ifdef HAVE_DNSSEC
+      if (server && option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED))
+       {
+         int status;
+
+         /* We've had a reply already, which we're validating. Ignore this duplicate */
+         if (forward->blocking_query)
+           return;
+
+         if (header->hb3 & HB3_TC)
+           {
+             /* Truncated answer can't be validated.
+                If this is an answer to a DNSSEC-generated query, we still
+                need to get the client to retry over TCP, so return
+                an answer with the TC bit set, even if the actual answer fits.
+             */
+             status = STAT_TRUNCATED;
+           }
+         else if (forward->flags & FREC_DNSKEY_QUERY)
+           status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
+         else if (forward->flags & FREC_DS_QUERY)
+           {
+             status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
+             /* Provably no DS, everything below is insecure, even if signatures are offered */
+             if (status == STAT_NO_DS)
+               /* We only cache sigs when we've validated a reply.
+                  Avoid caching a reply with sigs if there's a vaildated break in the 
+                  DS chain, so we don't return replies from cache missing sigs. */
+               status = STAT_INSECURE_DS;
+             else if (status == STAT_NO_SIG)
+                {
+                  if (option_bool(OPT_DNSSEC_NO_SIGN))
+                    {
+                     status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
+                     if (status == STAT_INSECURE)
+                       status = STAT_INSECURE_DS;
+                   }
+                 else
+                   status = STAT_INSECURE_DS;
+               }
+              else if (status == STAT_NO_NS)
+               status = STAT_BOGUS;
+           }
+         else if (forward->flags & FREC_CHECK_NOSIGN)
+           {
+             status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
+             if (status != STAT_NEED_KEY)
+               status = do_check_sign(forward, status, now, daemon->namebuff, daemon->keyname);
+           }
+         else
+           {
+             status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL, NULL);
+             if (status == STAT_NO_SIG)
+               {
+                 if (option_bool(OPT_DNSSEC_NO_SIGN))
+                   status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
+                 else
+                   status = STAT_INSECURE;
+               }
+           }
+         /* Can't validate, as we're missing key data. Put this
+            answer aside, whilst we get that. */     
+         if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG || status == STAT_NEED_KEY)
+           {
+             struct frec *new, *orig;
+             
+             /* Free any saved query */
+             if (forward->stash)
+               blockdata_free(forward->stash);
+             
+             /* Now save reply pending receipt of key data */
+             if (!(forward->stash = blockdata_alloc((char *)header, n)))
+               return;
+             forward->stash_len = n;
+             
+           anotherkey:       
+             /* Find the original query that started it all.... */
+             for (orig = forward; orig->dependent; orig = orig->dependent);
+
+             if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, 1)))
+               status = STAT_INSECURE;
+             else
+               {
+                 int fd;
+                 struct frec *next = new->next;
+                 *new = *forward; /* copy everything, then overwrite */
+                 new->next = next;
+                 new->blocking_query = NULL;
+                 new->sentto = server;
+                 new->rfd4 = NULL;
+                 new->orig_domain = NULL;
+#ifdef HAVE_IPV6
+                 new->rfd6 = NULL;
+#endif
+                 new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_CHECK_NOSIGN);
+                 
+                 new->dependent = forward; /* to find query awaiting new one. */
+                 forward->blocking_query = new; /* for garbage cleaning */
+                 /* validate routines leave name of required record in daemon->keyname */
+                 if (status == STAT_NEED_KEY)
+                   {
+                     new->flags |= FREC_DNSKEY_QUERY; 
+                     nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz,
+                                                daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz);
+                   }
+                 else 
+                   {
+                     if (status == STAT_NEED_DS_NEG)
+                       new->flags |= FREC_CHECK_NOSIGN;
+                     else
+                       new->flags |= FREC_DS_QUERY;
+                     nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
+                                                daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz);
+                   }
+                 if ((hash = hash_questions(header, nn, daemon->namebuff)))
+                   memcpy(new->hash, hash, HASH_SIZE);
+                 new->new_id = get_id();
+                 header->id = htons(new->new_id);
+                 /* Save query for retransmission */
+                 if (!(new->stash = blockdata_alloc((char *)header, nn)))
+                   return;
+                     
+                 new->stash_len = nn;
+                 
+                 /* Don't resend this. */
+                 daemon->srv_save = NULL;
+                 
+                 if (server->sfd)
+                   fd = server->sfd->fd;
+                 else
+                   {
+                     fd = -1;
+#ifdef HAVE_IPV6
+                     if (server->addr.sa.sa_family == AF_INET6)
+                       {
+                         if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
+                           fd = new->rfd6->fd;
+                       }
+                     else
+#endif
+                       {
+                         if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
+                           fd = new->rfd4->fd;
+                       }
+                   }
+                 
+                 if (fd != -1)
+                   {
+                     while (retry_send(sendto(fd, (char *)header, nn, 0, 
+                                              &server->addr.sa, 
+                                              sa_len(&server->addr)))); 
+                     server->queries++;
+                   }
+                 
+                 return;
+               }
+           }
+         
+         /* Ok, we reached far enough up the chain-of-trust that we can validate something.
+            Now wind back down, pulling back answers which wouldn't previously validate
+            and validate them with the new data. Note that if an answer needs multiple
+            keys to validate, we may find another key is needed, in which case we set off
+            down another branch of the tree. Once we get to the original answer 
+            (FREC_DNSSEC_QUERY not set) and it validates, return it to the original requestor. */
+         while (forward->dependent)
+           {
+             struct frec *prev = forward->dependent;
+             free_frec(forward);
+             forward = prev;
+             forward->blocking_query = NULL; /* already gone */
+             blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
+             n = forward->stash_len;
+             
+             if (status == STAT_SECURE)
+               {
+                 if (forward->flags & FREC_DNSKEY_QUERY)
+                   status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
+                 else if (forward->flags & FREC_DS_QUERY)
+                   {
+                     status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
+                      /* Provably no DS, everything below is insecure, even if signatures are offered */
+                     if (status == STAT_NO_DS)
+                       /* We only cache sigs when we've validated a reply.
+                          Avoid caching a reply with sigs if there's a vaildated break in the 
+                          DS chain, so we don't return replies from cache missing sigs. */
+                       status = STAT_INSECURE_DS;
+                      else if (status == STAT_NO_SIG)
+                        {
+                          if (option_bool(OPT_DNSSEC_NO_SIGN))
+                            {
+                              status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname); 
+                              if (status == STAT_INSECURE)
+                                status = STAT_INSECURE_DS;
+                            }
+                          else
+                            status = STAT_INSECURE_DS;
+                        }
+                      else if (status == STAT_NO_NS)
+                        status = STAT_BOGUS;
+                   }
+                 else if (forward->flags & FREC_CHECK_NOSIGN)
+                   {
+                     status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
+                     if (status != STAT_NEED_KEY)
+                       status = do_check_sign(forward, status, now, daemon->namebuff, daemon->keyname);
+                   }
+                 else
+                   {
+                     status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL, NULL);   
+                     if (status == STAT_NO_SIG)
+                       {
+                         if (option_bool(OPT_DNSSEC_NO_SIGN))
+                           status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
+                         else
+                           status = STAT_INSECURE;
+                       }
+                   }
+              
+                 if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG || status == STAT_NEED_KEY)
+                   goto anotherkey;
+               }
+           }
+         
+         no_cache_dnssec = 0;
+
+         if (status == STAT_INSECURE_DS)
+           {
+             /* We only cache sigs when we've validated a reply.
+                Avoid caching a reply with sigs if there's a vaildated break in the 
+                DS chain, so we don't return replies from cache missing sigs. */
+             status = STAT_INSECURE;
+             no_cache_dnssec = 1;
+           }
+         
+         if (status == STAT_TRUNCATED)
+           header->hb3 |= HB3_TC;
+         else
+           {
+             char *result, *domain = "result";
+             
+             if (forward->work_counter == 0)
+               {
+                 result = "ABANDONED";
+                 status = STAT_BOGUS;
+               }
+             else
+               result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
+             
+             if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL))
+               domain = daemon->namebuff;
 
-      if (!option_bool(OPT_NO_REBIND))
-       check_rebind = 0;
+             log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
+           }
+         
+         if (status == STAT_SECURE)
+           cache_secure = 1;
+         else if (status == STAT_BOGUS)
+           {
+             no_cache_dnssec = 1;
+             bogusanswer = 1;
+           }
+       }
+#endif     
       
-      if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, forward->flags & FREC_CHECKING_DISABLED)))
+      /* restore CD bit to the value in the query */
+      if (forward->flags & FREC_CHECKING_DISABLED)
+       header->hb4 |= HB4_CD;
+      else
+       header->hb4 &= ~HB4_CD;
+      
+      if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer, 
+                             forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, 
+                             forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source)))
        {
          header->id = htons(forward->orig_id);
          header->hb4 |= HB4_RA; /* recursion if available */
-         send_from(forward->fd, option_bool(OPT_NOWILD), daemon->packet, nn, 
+         send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, 
                    &forward->source, &forward->dest, forward->iface);
        }
       free_frec(forward); /* cancel */
@@ -624,7 +1115,10 @@ void receive_query(struct listener *listen, time_t now)
   struct in_addr netmask, dst_addr_4;
   size_t m;
   ssize_t n;
-  int if_index = 0;
+  int if_index = 0, auth_dns = 0;
+#ifdef HAVE_AUTH
+  int local_auth = 0;
+#endif
   struct iovec iov[1];
   struct msghdr msg;
   struct cmsghdr *cmptr;
@@ -643,21 +1137,30 @@ void receive_query(struct listener *listen, time_t now)
                 CMSG_SPACE(sizeof(struct sockaddr_dl))];
 #endif
   } control_u;
-  
+#ifdef HAVE_IPV6
+   /* Can always get recvd interface for IPv6 */
+  int check_dst = !option_bool(OPT_NOWILD) || listen->family == AF_INET6;
+#else
+  int check_dst = !option_bool(OPT_NOWILD);
+#endif
+
   /* packet buffer overwritten */
   daemon->srv_save = NULL;
   
-  if (listen->family == AF_INET && option_bool(OPT_NOWILD))
-    {
-      dst_addr_4 = listen->iface->addr.in.sin_addr;
-      netmask = listen->iface->netmask;
-    }
-  else
+  dst_addr_4.s_addr = dst_addr.addr.addr4.s_addr = 0;
+  netmask.s_addr = 0;
+  
+  if (option_bool(OPT_NOWILD) && listen->iface)
     {
-      dst_addr_4.s_addr = 0;
-      netmask.s_addr = 0;
+      auth_dns = listen->iface->dns_auth;
+     
+      if (listen->family == AF_INET)
+       {
+         dst_addr_4 = dst_addr.addr.addr4 = listen->iface->addr.in.sin_addr;
+         netmask = listen->iface->netmask;
+       }
     }
-
+  
   iov[0].iov_base = daemon->packet;
   iov[0].iov_len = daemon->edns_pktsz;
     
@@ -678,12 +1181,61 @@ void receive_query(struct listener *listen, time_t now)
     return;
   
   source_addr.sa.sa_family = listen->family;
+  
+  if (listen->family == AF_INET)
+    {
+       /* Source-port == 0 is an error, we can't send back to that. 
+         http://www.ietf.org/mail-archive/web/dnsop/current/msg11441.html */
+      if (source_addr.in.sin_port == 0)
+       return;
+    }
+#ifdef HAVE_IPV6
+  else
+    {
+      /* Source-port == 0 is an error, we can't send back to that. */
+      if (source_addr.in6.sin6_port == 0)
+       return;
+      source_addr.in6.sin6_flowinfo = 0;
+    }
+#endif
+  
+  /* We can be configured to only accept queries from at-most-one-hop-away addresses. */
+  if (option_bool(OPT_LOCAL_SERVICE))
+    {
+      struct addrlist *addr;
 #ifdef HAVE_IPV6
-  if (listen->family == AF_INET6)
-    source_addr.in6.sin6_flowinfo = 0;
+      if (listen->family == AF_INET6) 
+       {
+         for (addr = daemon->interface_addrs; addr; addr = addr->next)
+           if ((addr->flags & ADDRLIST_IPV6) &&
+               is_same_net6(&addr->addr.addr.addr6, &source_addr.in6.sin6_addr, addr->prefixlen))
+             break;
+       }
+      else
 #endif
-
-  if (!option_bool(OPT_NOWILD))
+       {
+         struct in_addr netmask;
+         for (addr = daemon->interface_addrs; addr; addr = addr->next)
+           {
+             netmask.s_addr = htonl(~(in_addr_t)0 << (32 - addr->prefixlen));
+             if (!(addr->flags & ADDRLIST_IPV6) &&
+                 is_same_net(addr->addr.addr.addr4, source_addr.in.sin_addr, netmask))
+               break;
+           }
+       }
+      if (!addr)
+       {
+         static int warned = 0;
+         if (!warned)
+           {
+             my_syslog(LOG_WARNING, _("Ignoring query from non-local network"));
+             warned = 1;
+           }
+         return;
+       }
+    }
+               
+  if (check_dst)
     {
       struct ifreq ifr;
 
@@ -693,7 +1245,7 @@ void receive_query(struct listener *listen, time_t now)
 #if defined(HAVE_LINUX_NETWORK)
       if (listen->family == AF_INET)
        for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
-         if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
+         if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
            {
              union {
                unsigned char *c;
@@ -733,7 +1285,7 @@ void receive_query(struct listener *listen, time_t now)
       if (listen->family == AF_INET6)
        {
          for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
-           if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == daemon->v6pktinfo)
+           if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
              {
                union {
                  unsigned char *c;
@@ -749,232 +1301,862 @@ void receive_query(struct listener *listen, time_t now)
       
       /* enforce available interface configuration */
       
-      if (!indextoname(listen->fd, if_index, ifr.ifr_name) ||
-         !iface_check(listen->family, &dst_addr, ifr.ifr_name, &if_index))
-       return;
-      
-      if (listen->family == AF_INET &&
-         option_bool(OPT_LOCALISE) &&
-         ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
+      if (!indextoname(listen->fd, if_index, ifr.ifr_name))
        return;
       
-      netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
-    }
-  
-  if (extract_request(header, (size_t)n, daemon->namebuff, &type))
-    {
-      char types[20];
-
-      querystr(types, type);
+      if (!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns))
+       {
+          if (!option_bool(OPT_CLEVERBIND))
+            enumerate_interfaces(0); 
+          if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name) &&
+              !label_exception(if_index, listen->family, &dst_addr))
+            return;
+       }
 
-      if (listen->family == AF_INET) 
-       log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, 
+      if (listen->family == AF_INET && option_bool(OPT_LOCALISE))
+       {
+         struct irec *iface;
+         
+         /* get the netmask of the interface whch has the address we were sent to.
+            This is no neccessarily the interface we arrived on. */
+         
+         for (iface = daemon->interfaces; iface; iface = iface->next)
+           if (iface->addr.sa.sa_family == AF_INET &&
+               iface->addr.in.sin_addr.s_addr == dst_addr_4.s_addr)
+             break;
+         
+         /* interface may be new */
+         if (!iface && !option_bool(OPT_CLEVERBIND))
+           enumerate_interfaces(0); 
+         
+         for (iface = daemon->interfaces; iface; iface = iface->next)
+           if (iface->addr.sa.sa_family == AF_INET &&
+               iface->addr.in.sin_addr.s_addr == dst_addr_4.s_addr)
+             break;
+         
+         /* If we failed, abandon localisation */
+         if (iface)
+           netmask = iface->netmask;
+         else
+           dst_addr_4.s_addr = 0;
+       }
+    }
+   
+  /* log_query gets called indirectly all over the place, so 
+     pass these in global variables - sorry. */
+  daemon->log_display_id = ++daemon->log_id;
+  daemon->log_source_addr = &source_addr;
+  
+  if (extract_request(header, (size_t)n, daemon->namebuff, &type))
+    {
+#ifdef HAVE_AUTH
+      struct auth_zone *zone;
+#endif
+      char *types = querystr(auth_dns ? "auth" : "query", type);
+      
+      if (listen->family == AF_INET) 
+       log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, 
                  (struct all_addr *)&source_addr.in.sin_addr, types);
 #ifdef HAVE_IPV6
       else
        log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, 
                  (struct all_addr *)&source_addr.in6.sin6_addr, types);
 #endif
-    }
 
-  m = answer_request (header, ((char *) header) + PACKETSZ, (size_t)n, 
-                     dst_addr_4, netmask, now);
-  if (m >= 1)
+#ifdef HAVE_AUTH
+      /* find queries for zones we're authoritative for, and answer them directly */
+      if (!auth_dns)
+       for (zone = daemon->auth_zones; zone; zone = zone->next)
+         if (in_zone(zone, daemon->namebuff, NULL))
+           {
+             auth_dns = 1;
+             local_auth = 1;
+             break;
+           }
+#endif
+      
+#ifdef HAVE_LOOP
+      /* Check for forwarding loop */
+      if (detect_loop(daemon->namebuff, type))
+       return;
+#endif
+    }
+  
+#ifdef HAVE_AUTH
+  if (auth_dns)
     {
-      send_from(listen->fd, option_bool(OPT_NOWILD), (char *)header, 
-               m, &source_addr, &dst_addr, if_index);
-      daemon->local_answer++;
+      m = answer_auth(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n, now, &source_addr, local_auth);
+      if (m >= 1)
+       {
+         send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
+                   (char *)header, m, &source_addr, &dst_addr, if_index);
+         daemon->auth_answer++;
+       }
     }
-  else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
-                        header, (size_t)n, now, NULL))
-    daemon->queries_forwarded++;
   else
-    daemon->local_answer++;
+#endif
+    {
+      int ad_reqd, do_bit;
+      m = answer_request(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n, 
+                        dst_addr_4, netmask, now, &ad_reqd, &do_bit);
+      
+      if (m >= 1)
+       {
+         send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
+                   (char *)header, m, &source_addr, &dst_addr, if_index);
+         daemon->local_answer++;
+       }
+      else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
+                            header, (size_t)n, now, NULL, ad_reqd, do_bit))
+       daemon->queries_forwarded++;
+      else
+       daemon->local_answer++;
+    }
+}
+
+#ifdef HAVE_DNSSEC
+
+/* UDP: we've got an unsigned answer, return STAT_INSECURE if we can prove there's no DS
+   and therefore the answer shouldn't be signed, or STAT_BOGUS if it should be, or 
+   STAT_NEED_DS_NEG and keyname if we need to do the query. */
+static int send_check_sign(struct frec *forward, time_t now, struct dns_header *header, size_t plen, 
+                          char *name, char *keyname)
+{
+  int status = dnssec_chase_cname(now, header, plen, name, keyname);
+  
+  if (status != STAT_INSECURE)
+    return status;
+
+  /* Store the domain we're trying to check. */
+  forward->name_start = strlen(name);
+  forward->name_len = forward->name_start + 1;
+  if (!(forward->orig_domain = blockdata_alloc(name, forward->name_len)))
+    return STAT_BOGUS;
+  
+  return do_check_sign(forward, 0, now, name, keyname);
+}
+/* We either have a a reply (header non-NULL, or we need to start by looking in the cache */ 
+static int do_check_sign(struct frec *forward, int status, time_t now, char *name, char *keyname)
+{
+  /* get domain we're checking back from blockdata store, it's stored on the original query. */
+  while (forward->dependent && !forward->orig_domain)
+    forward = forward->dependent;
+
+  blockdata_retrieve(forward->orig_domain, forward->name_len, name);
+  
+  while (1)
+    {
+      char *p; 
+
+      if (status == 0)
+       {
+         struct crec *crecp;
+
+         /* Haven't received answer, see if in cache */
+         if (!(crecp = cache_find_by_name(NULL, &name[forward->name_start], now, F_DS)))
+           {
+             /* put name of DS record we're missing into keyname */
+             strcpy(keyname, &name[forward->name_start]);
+             /* and wait for reply to arrive */
+             return STAT_NEED_DS_NEG;
+           }
+
+         /* F_DNSSECOK misused in DS cache records to non-existance of NS record */ 
+         if (!(crecp->flags & F_NEG))
+           status = STAT_SECURE;
+         else if (crecp->flags & F_DNSSECOK)
+           status = STAT_NO_DS;
+         else
+           status = STAT_NO_NS;
+       }
+      
+      /* Have entered non-signed part of DNS tree. */ 
+      if (status == STAT_NO_DS)
+       return forward->dependent ? STAT_INSECURE_DS : STAT_INSECURE;
+
+      if (status == STAT_BOGUS)
+       return STAT_BOGUS;
+
+      if (status == STAT_NO_SIG && *keyname != 0)
+       {
+         /* There is a validated CNAME chain that doesn't end in a DS record. Start 
+            the search again in that domain. */
+         blockdata_free(forward->orig_domain);
+         forward->name_start = strlen(keyname);
+         forward->name_len = forward->name_start + 1;
+         if (!(forward->orig_domain = blockdata_alloc(keyname, forward->name_len)))
+           return STAT_BOGUS;
+         
+         strcpy(name, keyname);
+         status = 0; /* force to cache when we iterate. */
+         continue;
+       }
+      
+      /* There's a proven DS record, or we're within a zone, where there doesn't need
+        to be a DS record. Add a name and try again. 
+        If we've already tried the whole name, then fail */
+
+      if (forward->name_start == 0)
+       return STAT_BOGUS;
+      
+      for (p = &name[forward->name_start-2]; (*p != '.') && (p != name); p--);
+      
+      if (p != name)
+       p++;
+      
+      forward->name_start = p - name;
+      status = 0; /* force to cache when we iterate. */
+    }
+}
+
+/* Move down from the root, until we find a signed non-existance of a DS, in which case
+   an unsigned answer is OK, or we find a signed DS, in which case there should be 
+   a signature, and the answer is BOGUS */
+static int  tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, size_t plen, int class, char *name, 
+                                       char *keyname, struct server *server, int *keycount)
+{
+  size_t m;
+  unsigned char *packet, *payload;
+  u16 *length;
+  int status, name_len;
+  struct blockdata *block;
+
+  char *name_start;
+
+  /* Get first insecure entry in CNAME chain */
+  status = tcp_key_recurse(now, STAT_CHASE_CNAME, header, plen, class, name, keyname, server, keycount);
+  if (status == STAT_BOGUS)
+    return STAT_BOGUS;
+  
+  if (!(packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16))))
+    return STAT_BOGUS;
+  
+  payload = &packet[2];
+  header = (struct dns_header *)payload;
+  length = (u16 *)packet;
+
+  /* Stash the name away, since the buffer will be trashed when we recurse */
+  name_len = strlen(name) + 1;
+  name_start = name + name_len - 1;
+  
+  if (!(block = blockdata_alloc(name, name_len)))
+    {
+      free(packet);
+      return STAT_BOGUS;
+    }
+
+  while (1)
+    {
+      unsigned char c1, c2;
+      struct crec *crecp;
+
+      if (--(*keycount) == 0)
+       {
+         free(packet);
+         blockdata_free(block);
+         return STAT_BOGUS;    
+       }
+      
+      while ((crecp = cache_find_by_name(NULL, name_start, now, F_DS)))
+       {      
+         if ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK))
+           {
+             /* Found a secure denial of DS - delegation is indeed insecure */
+             free(packet);
+             blockdata_free(block);
+             return STAT_INSECURE;
+           }
+      
+         /* Here, either there's a secure DS, or no NS and no DS, and therefore no delegation.
+            Add another label and continue. */
+         if (name_start == name)
+           {
+             free(packet);
+             blockdata_free(block);
+             return STAT_BOGUS; /* run out of labels */
+           }
+         
+         name_start -= 2;
+         while (*name_start != '.' && name_start != name) 
+           name_start--;
+         if (name_start != name)
+           name_start++;
+       }
+      
+      /* Can't find it in the cache, have to send a query */
+
+      m = dnssec_generate_query(header, ((char *) header) + 65536, name_start, class, T_DS, &server->addr, server->edns_pktsz);
+      
+      *length = htons(m);
+      
+      if (read_write(server->tcpfd, packet, m + sizeof(u16), 0) &&
+         read_write(server->tcpfd, &c1, 1, 1) &&
+         read_write(server->tcpfd, &c2, 1, 1) &&
+         read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
+       {
+         m = (c1 << 8) | c2;
+         
+         /* Note this trashes all three name workspaces */
+         status = tcp_key_recurse(now, STAT_NEED_DS_NEG, header, m, class, name, keyname, server, keycount);
+         
+         if (status == STAT_NO_DS)
+           {
+             /* Found a secure denial of DS - delegation is indeed insecure */
+             free(packet);
+             blockdata_free(block);
+             return STAT_INSECURE;
+           }
+         
+         if (status == STAT_NO_SIG && *keyname != 0)
+           {
+             /* There is a validated CNAME chain that doesn't end in a DS record. Start 
+                the search again in that domain. */
+             blockdata_free(block);
+             name_len = strlen(keyname) + 1;
+             name_start = name + name_len - 1;
+             
+             if (!(block = blockdata_alloc(keyname, name_len)))
+               return STAT_BOGUS;
+             
+             strcpy(name, keyname);
+             continue;
+           }
+         
+         if (status == STAT_BOGUS)
+           {
+             free(packet);
+             blockdata_free(block);
+             return STAT_BOGUS;
+           }
+         
+         /* Here, either there's a secure DS, or no NS and no DS, and therefore no delegation.
+            Add another label and continue. */
+         
+         /* Get name we're checking back. */
+         blockdata_retrieve(block, name_len, name);
+         
+         if (name_start == name)
+           {
+             free(packet);
+             blockdata_free(block);
+             return STAT_BOGUS; /* run out of labels */
+           }
+         
+         name_start -= 2;
+         while (*name_start != '.' && name_start != name) 
+           name_start--;
+         if (name_start != name)
+           name_start++;
+       }
+      else
+       {
+         /* IO failure */
+         free(packet);
+         blockdata_free(block);
+         return STAT_BOGUS; /* run out of labels */
+       }
+    }
+}
+
+static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n, 
+                          int class, char *name, char *keyname, struct server *server, int *keycount)
+{
+  /* Recurse up the key heirarchy */
+  int new_status;
+
+  /* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
+  if (--(*keycount) == 0)
+    return STAT_INSECURE;
+  
+  if (status == STAT_NEED_KEY)
+    new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
+  else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG)
+    {
+      new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
+      if (status == STAT_NEED_DS)
+       {
+         if (new_status == STAT_NO_DS)
+           new_status = STAT_INSECURE_DS;
+         if (new_status == STAT_NO_SIG)
+          {
+            if (option_bool(OPT_DNSSEC_NO_SIGN))
+              {
+                new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
+                if (new_status == STAT_INSECURE)
+                  new_status = STAT_INSECURE_DS;
+              }
+            else
+              new_status = STAT_INSECURE_DS;
+          }
+         else if (new_status == STAT_NO_NS)
+           new_status = STAT_BOGUS;
+       }
+    }
+  else if (status == STAT_CHASE_CNAME)
+    new_status = dnssec_chase_cname(now, header, n, name, keyname);
+  else 
+    {
+      new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL, NULL);
+      
+      if (new_status == STAT_NO_SIG)
+       {
+         if (option_bool(OPT_DNSSEC_NO_SIGN))
+           new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
+         else
+           new_status = STAT_INSECURE;
+       }
+    }
+
+  /* Can't validate because we need a key/DS whose name now in keyname.
+     Make query for same, and recurse to validate */
+  if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
+    {
+      size_t m; 
+      unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
+      unsigned char *payload = &packet[2];
+      struct dns_header *new_header = (struct dns_header *)payload;
+      u16 *length = (u16 *)packet;
+      unsigned char c1, c2;
+       
+      if (!packet)
+       return STAT_INSECURE;
+
+    another_tcp_key:
+      m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class, 
+                               new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz);
+      
+      *length = htons(m);
+      
+      if (!read_write(server->tcpfd, packet, m + sizeof(u16), 0) ||
+         !read_write(server->tcpfd, &c1, 1, 1) ||
+         !read_write(server->tcpfd, &c2, 1, 1) ||
+         !read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
+       new_status = STAT_INSECURE;
+      else
+       {
+         m = (c1 << 8) | c2;
+         
+         new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount);
+         
+         if (new_status == STAT_SECURE)
+           {
+             /* Reached a validated record, now try again at this level.
+                Note that we may get ANOTHER NEED_* if an answer needs more than one key.
+                If so, go round again. */
+             
+             if (status == STAT_NEED_KEY)
+               new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
+             else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG)
+               {
+                 new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
+                 if (status == STAT_NEED_DS)
+                   {
+                     if (new_status == STAT_NO_DS)
+                       new_status = STAT_INSECURE_DS;
+                     else if (new_status == STAT_NO_SIG)
+                       {
+                         if (option_bool(OPT_DNSSEC_NO_SIGN))
+                           {
+                             new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount); 
+                             if (new_status == STAT_INSECURE)
+                               new_status = STAT_INSECURE_DS;
+                           }
+                         else
+                           new_status = STAT_INSECURE_DS;
+                       }
+                     else if (new_status == STAT_NO_NS)
+                       new_status = STAT_BOGUS;
+                   }
+               }
+             else if (status == STAT_CHASE_CNAME)
+               new_status = dnssec_chase_cname(now, header, n, name, keyname);
+             else 
+               {
+                 new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL, NULL);
+                 
+                 if (new_status == STAT_NO_SIG)
+                   {
+                     if (option_bool(OPT_DNSSEC_NO_SIGN))
+                       new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
+                     else
+                       new_status = STAT_INSECURE;
+                   }
+               }
+             
+             if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
+               goto another_tcp_key;
+           }
+       }
+      
+      free(packet);
+    }
+  return new_status;
 }
+#endif
+
 
 /* The daemon forks before calling this: it should deal with one connection,
    blocking as neccessary, and then return. Note, need to be a bit careful
    about resources for debug mode, when the fork is suppressed: that's
    done by the caller. */
 unsigned char *tcp_request(int confd, time_t now,
-                          struct in_addr local_addr, struct in_addr netmask)
+                          union mysockaddr *local_addr, struct in_addr netmask, int auth_dns)
 {
   size_t size = 0;
   int norebind = 0;
-  int checking_disabled;
+#ifdef HAVE_AUTH
+  int local_auth = 0;
+#endif
+  int checking_disabled, ad_question, do_bit, added_pheader = 0;
+  int check_subnet, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
   size_t m;
-  unsigned short qtype, gotname;
+  unsigned short qtype;
+  unsigned int gotname;
   unsigned char c1, c2;
-  /* Max TCP packet + slop */
-  unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
-  struct dns_header *header;
+  /* Max TCP packet + slop + size */
+  unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
+  unsigned char *payload = &packet[2];
+  /* largest field in header is 16-bits, so this is still sufficiently aligned */
+  struct dns_header *header = (struct dns_header *)payload;
+  u16 *length = (u16 *)packet;
   struct server *last_server;
+  struct in_addr dst_addr_4;
+  union mysockaddr peer_addr;
+  socklen_t peer_len = sizeof(union mysockaddr);
+  int query_count = 0;
+
+  if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
+    return packet;
   
+  /* We can be configured to only accept queries from at-most-one-hop-away addresses. */
+  if (option_bool(OPT_LOCAL_SERVICE))
+    {
+      struct addrlist *addr;
+#ifdef HAVE_IPV6
+      if (peer_addr.sa.sa_family == AF_INET6) 
+       {
+         for (addr = daemon->interface_addrs; addr; addr = addr->next)
+           if ((addr->flags & ADDRLIST_IPV6) &&
+               is_same_net6(&addr->addr.addr.addr6, &peer_addr.in6.sin6_addr, addr->prefixlen))
+             break;
+       }
+      else
+#endif
+       {
+         struct in_addr netmask;
+         for (addr = daemon->interface_addrs; addr; addr = addr->next)
+           {
+             netmask.s_addr = htonl(~(in_addr_t)0 << (32 - addr->prefixlen));
+             if (!(addr->flags & ADDRLIST_IPV6) && 
+                 is_same_net(addr->addr.addr.addr4, peer_addr.in.sin_addr, netmask))
+               break;
+           }
+       }
+      if (!addr)
+       {
+         my_syslog(LOG_WARNING, _("Ignoring query from non-local network"));
+         return packet;
+       }
+    }
+
   while (1)
     {
-      if (!packet ||
+      if (query_count == TCP_MAX_QUERIES ||
+         !packet ||
          !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
          !(size = c1 << 8 | c2) ||
-         !read_write(confd, packet, size, 1))
+         !read_write(confd, payload, size, 1))
                return packet; 
   
       if (size < (int)sizeof(struct dns_header))
        continue;
       
-      header = (struct dns_header *)packet;
+      query_count++;
+
+      /* log_query gets called indirectly all over the place, so 
+        pass these in global variables - sorry. */
+      daemon->log_display_id = ++daemon->log_id;
+      daemon->log_source_addr = &peer_addr;
+      
+      check_subnet = 0;
 
       /* save state of "cd" flag in query */
-      checking_disabled = header->hb4 & HB4_CD;
+      if ((checking_disabled = header->hb4 & HB4_CD))
+       no_cache_dnssec = 1;
        
-      /* RFC 4035: sect 4.6 para 2 */
-      header->hb4 &= ~HB4_AD;
-      
       if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
        {
-         union mysockaddr peer_addr;
-         socklen_t peer_len = sizeof(union mysockaddr);
+#ifdef HAVE_AUTH
+         struct auth_zone *zone;
+#endif
+         char *types = querystr(auth_dns ? "auth" : "query", qtype);
          
-         if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
-           {
-             char types[20];
-
-             querystr(types, qtype);
-
-             if (peer_addr.sa.sa_family == AF_INET) 
-               log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, 
-                         (struct all_addr *)&peer_addr.in.sin_addr, types);
+         if (peer_addr.sa.sa_family == AF_INET) 
+           log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, 
+                     (struct all_addr *)&peer_addr.in.sin_addr, types);
 #ifdef HAVE_IPV6
-             else
-               log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, 
-                         (struct all_addr *)&peer_addr.in6.sin6_addr, types);
+         else
+           log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, 
+                     (struct all_addr *)&peer_addr.in6.sin6_addr, types);
+#endif
+         
+#ifdef HAVE_AUTH
+         /* find queries for zones we're authoritative for, and answer them directly */
+         if (!auth_dns)
+           for (zone = daemon->auth_zones; zone; zone = zone->next)
+             if (in_zone(zone, daemon->namebuff, NULL))
+               {
+                 auth_dns = 1;
+                 local_auth = 1;
+                 break;
+               }
 #endif
-           }
        }
       
-      /* m > 0 if answered from cache */
-      m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, 
-                        local_addr, netmask, now);
-
-      /* Do this by steam now we're not in the select() loop */
-      check_log_writer(NULL); 
+      if (local_addr->sa.sa_family == AF_INET)
+       dst_addr_4 = local_addr->in.sin_addr;
+      else
+       dst_addr_4.s_addr = 0;
       
-      if (m == 0)
+#ifdef HAVE_AUTH
+      if (auth_dns)
+       m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr, local_auth);
+      else
+#endif
        {
-         unsigned int flags = 0;
-         struct all_addr *addrp = NULL;
-         int type = 0;
-         char *domain = NULL;
-          
-         if (option_bool(OPT_ADD_MAC))
+         /* m > 0 if answered from cache */
+         m = answer_request(header, ((char *) header) + 65536, (size_t)size, 
+                            dst_addr_4, netmask, now, &ad_question, &do_bit);
+         
+         /* Do this by steam now we're not in the select() loop */
+         check_log_writer(1); 
+         
+         if (m == 0)
            {
-             union mysockaddr peer_addr;
-             socklen_t peer_len = sizeof(union mysockaddr);
+             unsigned int flags = 0;
+             struct all_addr *addrp = NULL;
+             int type = 0;
+             char *domain = NULL;
              
-             if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
+             if (option_bool(OPT_ADD_MAC))
                size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
-           }
-      
-         if (gotname)
-           flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
-         
-         if (type != 0  || option_bool(OPT_ORDER) || !daemon->last_server)
-           last_server = daemon->servers;
-         else
-           last_server = daemon->last_server;
-      
-         if (!flags && last_server)
-           {
-             struct server *firstsendto = NULL;
-             unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
-
-             /* Loop round available servers until we succeed in connecting to one.
-                Note that this code subtley ensures that consecutive queries on this connection
-                which can go to the same server, do so. */
-             while (1) 
-               {
-                 if (!firstsendto)
-                   firstsendto = last_server;
-                 else
+               
+             if (option_bool(OPT_CLIENT_SUBNET))
+               {
+                 size_t new = add_source_addr(header, size, ((char *) header) + 65536, &peer_addr);
+                 if (size != new)
                    {
-                     if (!(last_server = last_server->next))
-                       last_server = daemon->servers;
-                     
-                     if (last_server == firstsendto)
-                       break;
+                     size = new;
+                     check_subnet = 1;
                    }
+               }
+
+             if (gotname)
+               flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
              
-                 /* server for wrong domain */
-                 if (type != (last_server->flags & SERV_TYPE) ||
-                     (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
-                   continue;
-                 
-                 if ((last_server->tcpfd == -1) &&
-                     (last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
-                     (!local_bind(last_server->tcpfd,  &last_server->source_addr, last_server->interface, 1) ||
-                      connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
+             if (type != 0  || option_bool(OPT_ORDER) || !daemon->last_server)
+               last_server = daemon->servers;
+             else
+               last_server = daemon->last_server;
+             
+             if (!flags && last_server)
+               {
+                 struct server *firstsendto = NULL;
+#ifdef HAVE_DNSSEC
+                 unsigned char *newhash, hash[HASH_SIZE];
+                 if ((newhash = hash_questions(header, (unsigned int)size, daemon->namebuff)))
+                   memcpy(hash, newhash, HASH_SIZE);
+                 else
+                   memset(hash, 0, HASH_SIZE);
+#else
+                 unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
+#endif           
+                 /* Loop round available servers until we succeed in connecting to one.
+                    Note that this code subtley ensures that consecutive queries on this connection
+                    which can go to the same server, do so. */
+                 while (1) 
                    {
-                     close(last_server->tcpfd);
-                     last_server->tcpfd = -1;
-                   }
-                 
-                 if (last_server->tcpfd == -1) 
-                   continue;
+                     if (!firstsendto)
+                       firstsendto = last_server;
+                     else
+                       {
+                         if (!(last_server = last_server->next))
+                           last_server = daemon->servers;
+                         
+                         if (last_server == firstsendto)
+                           break;
+                       }
+                     
+                     /* server for wrong domain */
+                     if (type != (last_server->flags & SERV_TYPE) ||
+                         (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)) ||
+                         (last_server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
+                       continue;
+                     
+                     if (last_server->tcpfd == -1)
+                       {
+                         if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
+                           continue;
+                         
+#ifdef HAVE_CONNTRACK
+                         /* Copy connection mark of incoming query to outgoing connection. */
+                         if (option_bool(OPT_CONNTRACK))
+                           {
+                             unsigned int mark;
+                             struct all_addr local;
+#ifdef HAVE_IPV6                     
+                             if (local_addr->sa.sa_family == AF_INET6)
+                               local.addr.addr6 = local_addr->in6.sin6_addr;
+                             else
+#endif
+                               local.addr.addr4 = local_addr->in.sin_addr;
+                             
+                             if (get_incoming_mark(&peer_addr, &local, 1, &mark))
+                               setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
+                           }
+#endif 
+                     
+                         if ((!local_bind(last_server->tcpfd,  &last_server->source_addr, last_server->interface, 1) ||
+                              connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
+                           {
+                             close(last_server->tcpfd);
+                             last_server->tcpfd = -1;
+                             continue;
+                           }
+                         
+#ifdef HAVE_DNSSEC
+                         if (option_bool(OPT_DNSSEC_VALID))
+                           {
+                             size_t new_size = add_do_bit(header, size, ((char *) header) + 65536);
+                             
+                             /* For debugging, set Checking Disabled, otherwise, have the upstream check too,
+                                this allows it to select auth servers when one is returning bad data. */
+                             if (option_bool(OPT_DNSSEC_DEBUG))
+                               header->hb4 |= HB4_CD;
+                             
+                             if (size != new_size)
+                               added_pheader = 1;
+                             
+                             size = new_size;
+                           }
+#endif
+                       }
+                     
+                     *length = htons(size);
 
-                 c1 = size >> 8;
-                 c2 = size;
-                 
-                 if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
-                     !read_write(last_server->tcpfd, &c2, 1, 0) ||
-                     !read_write(last_server->tcpfd, packet, size, 0) ||
-                     !read_write(last_server->tcpfd, &c1, 1, 1) ||
-                     !read_write(last_server->tcpfd, &c2, 1, 1))
-                   {
-                     close(last_server->tcpfd);
-                     last_server->tcpfd = -1;
-                     continue;
-                   } 
-                 
-                 m = (c1 << 8) | c2;
-                 if (!read_write(last_server->tcpfd, packet, m, 1))
-                   return packet;
-                 
-                 if (!gotname)
-                   strcpy(daemon->namebuff, "query");
-                 if (last_server->addr.sa.sa_family == AF_INET)
-                   log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, 
-                             (struct all_addr *)&last_server->addr.in.sin_addr, NULL); 
+                     /* get query name again for logging - may have been overwritten */
+                     if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
+                       strcpy(daemon->namebuff, "query");
+                     
+                     if (!read_write(last_server->tcpfd, packet, size + sizeof(u16), 0) ||
+                         !read_write(last_server->tcpfd, &c1, 1, 1) ||
+                         !read_write(last_server->tcpfd, &c2, 1, 1) ||
+                         !read_write(last_server->tcpfd, payload, (c1 << 8) | c2, 1))
+                       {
+                         close(last_server->tcpfd);
+                         last_server->tcpfd = -1;
+                         continue;
+                       } 
+                     
+                     m = (c1 << 8) | c2;
+                     
+                     if (last_server->addr.sa.sa_family == AF_INET)
+                       log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, 
+                                 (struct all_addr *)&last_server->addr.in.sin_addr, NULL); 
 #ifdef HAVE_IPV6
-                 else
-                   log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, 
-                             (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
+                     else
+                       log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, 
+                                 (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
 #endif 
-                 
-                 /* There's no point in updating the cache, since this process will exit and
-                    lose the information after a few queries. We make this call for the alias and 
-                    bogus-nxdomain side-effects. */
-                 /* If the crc of the question section doesn't match the crc we sent, then
-                    someone might be attempting to insert bogus values into the cache by 
-                    sending replies containing questions and bogus answers. */
-                 if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
-                   m = process_reply(header, now, last_server, (unsigned int)m, 
-                                     option_bool(OPT_NO_REBIND) && !norebind, checking_disabled);
-                 
-                 break;
+
+#ifdef HAVE_DNSSEC
+                     if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled)
+                       {
+                         int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
+                         int status = tcp_key_recurse(now, STAT_TRUNCATED, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount);
+                         char *result, *domain = "result";
+
+                         if (status == STAT_INSECURE_DS)
+                           {
+                             /* We only cache sigs when we've validated a reply.
+                                Avoid caching a reply with sigs if there's a vaildated break in the 
+                                DS chain, so we don't return replies from cache missing sigs. */
+                             status = STAT_INSECURE;
+                             no_cache_dnssec = 1;
+                           }
+                         
+                         if (keycount == 0)
+                           {
+                             result = "ABANDONED";
+                             status = STAT_BOGUS;
+                           }
+                         else
+                           result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
+                         
+                         if (status == STAT_BOGUS && extract_request(header, m, daemon->namebuff, NULL))
+                           domain = daemon->namebuff;
+
+                         log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
+                         
+                         if (status == STAT_BOGUS)
+                           {
+                             no_cache_dnssec = 1;
+                             bogusanswer = 1;
+                           }
+
+                         if (status == STAT_SECURE)
+                           cache_secure = 1;
+                       }
+#endif
+
+                     /* restore CD bit to the value in the query */
+                     if (checking_disabled)
+                       header->hb4 |= HB4_CD;
+                     else
+                       header->hb4 &= ~HB4_CD;
+                     
+                     /* There's no point in updating the cache, since this process will exit and
+                        lose the information after a few queries. We make this call for the alias and 
+                        bogus-nxdomain side-effects. */
+                     /* If the crc of the question section doesn't match the crc we sent, then
+                        someone might be attempting to insert bogus values into the cache by 
+                        sending replies containing questions and bogus answers. */
+#ifdef HAVE_DNSSEC
+                     newhash = hash_questions(header, (unsigned int)m, daemon->namebuff);
+                     if (!newhash || memcmp(hash, newhash, HASH_SIZE) != 0)
+                       { 
+                         m = 0;
+                         break;
+                       }
+#else                    
+                     if (crc != questions_crc(header, (unsigned int)m, daemon->namebuff))
+                       {
+                         m = 0;
+                         break;
+                       }
+#endif
+
+                     m = process_reply(header, now, last_server, (unsigned int)m, 
+                                       option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
+                                       ad_question, do_bit, added_pheader, check_subnet, &peer_addr); 
+                     
+                     break;
+                   }
                }
+       
+             /* In case of local answer or no connections made. */
+             if (m == 0)
+               m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
            }
-         
-         /* In case of local answer or no connections made. */
-         if (m == 0)
-           m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
        }
-
-      check_log_writer(NULL);
+         
+      check_log_writer(1);
       
-      c1 = m>>8;
-      c2 = m;
-      if (!read_write(confd, &c1, 1, 0) ||
-         !read_write(confd, &c2, 1, 0) || 
-         !read_write(confd, packet, m, 0))
+      *length = htons(m);
+           
+      if (m == 0 || !read_write(confd, packet, m + sizeof(u16), 0))
        return packet;
     }
 }
@@ -993,13 +2175,19 @@ static struct frec *allocate_frec(time_t now)
 #ifdef HAVE_IPV6
       f->rfd6 = NULL;
 #endif
+#ifdef HAVE_DNSSEC
+      f->dependent = NULL;
+      f->blocking_query = NULL;
+      f->stash = NULL;
+      f->orig_domain = NULL;
+#endif
       daemon->frec_list = f;
     }
 
   return f;
 }
 
-static struct randfd *allocate_rfd(int family)
+struct randfd *allocate_rfd(int family)
 {
   static int finger = 0;
   int i;
@@ -1035,28 +2223,52 @@ static struct randfd *allocate_rfd(int family)
   return NULL; /* doom */
 }
 
+void free_rfd(struct randfd *rfd)
+{
+  if (rfd && --(rfd->refcount) == 0)
+    close(rfd->fd);
+}
+
 static void free_frec(struct frec *f)
 {
-  if (f->rfd4 && --(f->rfd4->refcount) == 0)
-    close(f->rfd4->fd);
-    
+  free_rfd(f->rfd4);
   f->rfd4 = NULL;
   f->sentto = NULL;
   f->flags = 0;
   
 #ifdef HAVE_IPV6
-  if (f->rfd6 && --(f->rfd6->refcount) == 0)
-    close(f->rfd6->fd);
-    
+  free_rfd(f->rfd6);
   f->rfd6 = NULL;
 #endif
+
+#ifdef HAVE_DNSSEC
+  if (f->stash)
+    {
+      blockdata_free(f->stash);
+      f->stash = NULL;
+    }
+
+  if (f->orig_domain)
+    {
+      blockdata_free(f->orig_domain);
+      f->orig_domain = NULL;
+    }
+
+  /* Anything we're waiting on is pointless now, too */
+  if (f->blocking_query)
+    free_frec(f->blocking_query);
+  f->blocking_query = NULL;
+  f->dependent = NULL;
+#endif
 }
 
 /* if wait==NULL return a free or older than TIMEOUT record.
    else return *wait zero if one available, or *wait is delay to
    when the oldest in-use record will expire. Impose an absolute
-   limit of 4*TIMEOUT before we wipe things (for random sockets) */
-struct frec *get_new_frec(time_t now, int *wait)
+   limit of 4*TIMEOUT before we wipe things (for random sockets).
+   If force is set, always return a result, even if we have
+   to allocate above the limit. */
+struct frec *get_new_frec(time_t now, int *wait, int force)
 {
   struct frec *f, *oldest, *target;
   int count;
@@ -1105,10 +2317,19 @@ struct frec *get_new_frec(time_t now, int *wait)
     }
   
   /* none available, calculate time 'till oldest record expires */
-  if (count > daemon->ftabsize)
+  if (!force && count > daemon->ftabsize)
     {
+      static time_t last_log = 0;
+      
       if (oldest && wait)
        *wait = oldest->time + (time_t)TIMEOUT - now;
+      
+      if ((int)difftime(now, last_log) > 5)
+       {
+         last_log = now;
+         my_syslog(LOG_WARNING, _("Maximum number of concurrent DNS queries reached (max: %d)"), daemon->ftabsize);
+       }
+
       return NULL;
     }
   
@@ -1120,13 +2341,13 @@ struct frec *get_new_frec(time_t now, int *wait)
 }
  
 /* crc is all-ones if not known. */
-static struct frec *lookup_frec(unsigned short id, unsigned int crc)
+static struct frec *lookup_frec(unsigned short id, void *hash)
 {
   struct frec *f;
 
   for(f = daemon->frec_list; f; f = f->next)
     if (f->sentto && f->new_id == id && 
-       (f->crc == crc || crc == 0xffffffff))
+       (!hash || memcmp(hash, f->hash, HASH_SIZE) == 0))
       return f;
       
   return NULL;
@@ -1134,19 +2355,39 @@ static struct frec *lookup_frec(unsigned short id, unsigned int crc)
 
 static struct frec *lookup_frec_by_sender(unsigned short id,
                                          union mysockaddr *addr,
-                                         unsigned int crc)
+                                         void *hash)
 {
   struct frec *f;
   
   for(f = daemon->frec_list; f; f = f->next)
     if (f->sentto &&
        f->orig_id == id && 
-       f->crc == crc &&
+       memcmp(hash, f->hash, HASH_SIZE) == 0 &&
        sockaddr_isequal(&f->source, addr))
       return f;
    
   return NULL;
 }
+/* Send query packet again, if we can. */
+void resend_query()
+{
+  if (daemon->srv_save)
+    {
+      int fd;
+      
+      if (daemon->srv_save->sfd)
+       fd = daemon->srv_save->sfd->fd;
+      else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
+       fd = daemon->rfd_save->fd;
+      else
+       return;
+      
+      while(retry_send(sendto(fd, daemon->packet, daemon->packet_len, 0,
+                             &daemon->srv_save->addr.sa, 
+                             sa_len(&daemon->srv_save->addr)))); 
+    }
+}
 
 /* A server record is going away, remove references to it */
 void server_gone(struct server *server)
@@ -1165,13 +2406,13 @@ void server_gone(struct server *server)
 }
 
 /* return unique random ids. */
-static unsigned short get_id(unsigned int crc)
+static unsigned short get_id(void)
 {
   unsigned short ret = 0;
   
   do 
     ret = rand16();
-  while (lookup_frec(ret, crc));
+  while (lookup_frec(ret, NULL));
   
   return ret;
 }
index 93f99f0..1fee72d 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
@@ -16,6 +16,8 @@
 
 #include "dnsmasq.h"
 
+#ifdef HAVE_SCRIPT
+
 /* This file has code to fork a helper process which recieves data via a pipe 
    shared with the main process and which is responsible for calling a script when
    DHCP leases change.
    main process.
 */
 
-#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
-
 static void my_setenv(const char *name, const char *value, int *error);
 static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end,  char *env, int *err);
 
+#ifdef HAVE_LUASCRIPT
+#define LUA_COMPAT_ALL
+#include <lua.h>  
+#include <lualib.h>  
+#include <lauxlib.h>  
+
+#ifndef lua_open
+#define lua_open()     luaL_newstate()
+#endif
+
+lua_State *lua;
+
+static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field);
+#endif
+
+
 struct script_data
 {
-  unsigned char action, hwaddr_len, hwaddr_type;
-  unsigned char clid_len, hostname_len, ed_len;
+  int flags;
+  int action, hwaddr_len, hwaddr_type;
+  int clid_len, hostname_len, ed_len;
   struct in_addr addr, giaddr;
   unsigned int remaining_time;
 #ifdef HAVE_BROKEN_RTC
@@ -44,6 +61,15 @@ struct script_data
 #else
   time_t expires;
 #endif
+#ifdef HAVE_TFTP
+  off_t file_len;
+#endif
+#ifdef HAVE_IPV6
+  struct in6_addr addr6;
+#endif
+#ifdef HAVE_DHCP6
+  int iaid, vendorclass_count;
+#endif
   unsigned char hwaddr[DHCP_CHADDR_MAX];
   char interface[IF_NAMESIZE];
 };
@@ -61,7 +87,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
      then fork our process. */
   if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
     {
-      send_event(err_fd, EVENT_PIPE_ERR, errno);
+      send_event(err_fd, EVENT_PIPE_ERR, errno, NULL);
       _exit(0);
     }
 
@@ -88,37 +114,99 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
        {
          if (option_bool(OPT_NO_FORK))
            /* send error to daemon process if no-fork */
-           send_event(event_fd, EVENT_HUSER_ERR, errno);
+           send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
          else
            {
              /* kill daemon */
-             send_event(event_fd, EVENT_DIE, 0);
+             send_event(event_fd, EVENT_DIE, 0, NULL);
              /* return error */
-             send_event(err_fd, EVENT_HUSER_ERR, errno);
+             send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
            }
          _exit(0);
        }
     }
 
-  /* close all the sockets etc, we don't need them here. This closes err_fd, so that
-     main process can return. */
+  /* close all the sockets etc, we don't need them here. 
+     Don't close err_fd, in case the lua-init fails.
+     Note that we have to do this before lua init
+     so we don't close any lua fds. */
   for (max_fd--; max_fd >= 0; max_fd--)
     if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && 
-       max_fd != STDIN_FILENO && max_fd != pipefd[0] && max_fd != event_fd)
+       max_fd != STDIN_FILENO && max_fd != pipefd[0] && 
+       max_fd != event_fd && max_fd != err_fd)
       close(max_fd);
   
+#ifdef HAVE_LUASCRIPT
+  if (daemon->luascript)
+    {
+      const char *lua_err = NULL;
+      lua = lua_open();
+      luaL_openlibs(lua);
+      
+      /* get Lua to load our script file */
+      if (luaL_dofile(lua, daemon->luascript) != 0)
+       lua_err = lua_tostring(lua, -1);
+      else
+       {
+         lua_getglobal(lua, "lease");
+         if (lua_type(lua, -1) != LUA_TFUNCTION) 
+           lua_err = _("lease() function missing in Lua script");
+       }
+      
+      if (lua_err)
+       {
+         if (option_bool(OPT_NO_FORK) || option_bool(OPT_DEBUG))
+           /* send error to daemon process if no-fork */
+           send_event(event_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
+         else
+           {
+             /* kill daemon */
+             send_event(event_fd, EVENT_DIE, 0, NULL);
+             /* return error */
+             send_event(err_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
+           }
+         _exit(0);
+       }
+      
+      lua_pop(lua, 1);  /* remove nil from stack */
+      lua_getglobal(lua, "init");
+      if (lua_type(lua, -1) == LUA_TFUNCTION)
+       lua_call(lua, 0, 0);
+      else
+       lua_pop(lua, 1);  /* remove nil from stack */   
+    }
+#endif
+
+  /* All init done, close our copy of the error pipe, so that main process can return */
+  if (err_fd != -1)
+    close(err_fd);
+    
   /* loop here */
   while(1)
     {
       struct script_data data;
-      char *p, *action_str, *hostname = NULL;
+      char *p, *action_str, *hostname = NULL, *domain = NULL;
       unsigned char *buf = (unsigned char *)daemon->namebuff;
-      unsigned char *end, *alloc_buff = NULL;
-      int err = 0;
+      unsigned char *end, *extradata, *alloc_buff = NULL;
+      int is6, err = 0;
 
+      free(alloc_buff);
+      
       /* we read zero bytes when pipe closed: this is our signal to exit */ 
       if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
-       _exit(0);
+       {
+#ifdef HAVE_LUASCRIPT
+         if (daemon->luascript)
+           {
+             lua_getglobal(lua, "shutdown");
+             if (lua_type(lua, -1) == LUA_TFUNCTION)
+               lua_call(lua, 0, 0);
+           }
+#endif
+         _exit(0);
+       }
+      is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
       
       if (data.action == ACTION_DEL)
        action_str = "del";
@@ -126,55 +214,244 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
        action_str = "add";
       else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
        action_str = "old";
+      else if (data.action == ACTION_TFTP)
+       {
+         action_str = "tftp";
+         is6 = (data.flags != AF_INET);
+       }
       else
        continue;
-       
+
+       
       /* stringify MAC into dhcp_buff */
       p = daemon->dhcp_buff;
       if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0) 
-        p += sprintf(p, "%.2x-", data.hwaddr_type);
+       p += sprintf(p, "%.2x-", data.hwaddr_type);
       for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
-        {
-          p += sprintf(p, "%.2x", data.hwaddr[i]);
-          if (i != data.hwaddr_len - 1)
-            p += sprintf(p, ":");
-        }
+       {
+         p += sprintf(p, "%.2x", data.hwaddr[i]);
+         if (i != data.hwaddr_len - 1)
+           p += sprintf(p, ":");
+       }
       
-      /* and CLID into packet, avoid overwrite from bad data */
-      if ((data.clid_len > daemon->packet_buff_sz) || !read_write(pipefd[0], buf, data.clid_len, 1))
+      /* supplied data may just exceed normal buffer (unlikely) */
+      if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME && 
+         !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
        continue;
+      
+      if (!read_write(pipefd[0], buf, 
+                     data.hostname_len + data.ed_len + data.clid_len, 1))
+       continue;
+
+      /* CLID into packet */
       for (p = daemon->packet, i = 0; i < data.clid_len; i++)
        {
          p += sprintf(p, "%.2x", buf[i]);
          if (i != data.clid_len - 1) 
-           p += sprintf(p, ":");
+             p += sprintf(p, ":");
        }
-      
-      /* and expiry or length into dhcp_buff2 */
-#ifdef HAVE_BROKEN_RTC
-      sprintf(daemon->dhcp_buff2, "%u", data.length);
+
+#ifdef HAVE_DHCP6
+      if (is6)
+       {
+         /* or IAID and server DUID for IPv6 */
+         sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.iaid);     
+         for (p = daemon->dhcp_packet.iov_base, i = 0; i < daemon->duid_len; i++)
+           {
+             p += sprintf(p, "%.2x", daemon->duid[i]);
+             if (i != daemon->duid_len - 1) 
+               p += sprintf(p, ":");
+           }
+
+       }
+#endif
+
+      buf += data.clid_len;
+
+      if (data.hostname_len != 0)
+       {
+         char *dot;
+         hostname = (char *)buf;
+         hostname[data.hostname_len - 1] = 0;
+         if (data.action != ACTION_TFTP)
+           {
+             if (!legal_hostname(hostname))
+               hostname = NULL;
+             else if ((dot = strchr(hostname, '.')))
+               {
+                 domain = dot+1;
+                 *dot = 0;
+               } 
+           }
+       }
+    
+      extradata = buf + data.hostname_len;
+    
+      if (!is6)
+       inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
+#ifdef HAVE_DHCP6
+      else
+       inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN);
+#endif
+
+#ifdef HAVE_TFTP
+      /* file length */
+      if (data.action == ACTION_TFTP)
+       sprintf(is6 ? daemon->packet : daemon->dhcp_buff, "%lu", (unsigned long)data.file_len);
+#endif
+
+#ifdef HAVE_LUASCRIPT
+      if (daemon->luascript)
+       {
+         if (data.action == ACTION_TFTP)
+           {
+             lua_getglobal(lua, "tftp"); 
+             if (lua_type(lua, -1) != LUA_TFUNCTION)
+               lua_pop(lua, 1); /* tftp 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, "destination_address");
+                 lua_pushstring(lua, hostname);
+                 lua_setfield(lua, -2, "file_name"); 
+                 lua_pushstring(lua, is6 ? daemon->packet : daemon->dhcp_buff);
+                 lua_setfield(lua, -2, "file_size");
+                 lua_call(lua, 2, 0);  /* pass 2 values, expect 0 */
+               }
+           }
+         else
+           {
+             lua_getglobal(lua, "lease");     /* function to call */
+             lua_pushstring(lua, action_str); /* arg1 - action */
+             lua_newtable(lua);               /* arg2 - data table */
+             
+             if (is6)
+               {
+                 lua_pushstring(lua, daemon->packet);
+                 lua_setfield(lua, -2, "client_duid");
+                 lua_pushstring(lua, daemon->dhcp_packet.iov_base);
+                 lua_setfield(lua, -2, "server_duid");
+                 lua_pushstring(lua, daemon->dhcp_buff3);
+                 lua_setfield(lua, -2, "iaid");
+               }
+             
+             if (!is6 && data.clid_len != 0)
+               {
+                 lua_pushstring(lua, daemon->packet);
+                 lua_setfield(lua, -2, "client_id");
+               }
+             
+             if (strlen(data.interface) != 0)
+               {
+                 lua_pushstring(lua, data.interface);
+                 lua_setfield(lua, -2, "interface");
+               }
+             
+#ifdef HAVE_BROKEN_RTC 
+             lua_pushnumber(lua, data.length);
+             lua_setfield(lua, -2, "lease_length");
 #else
-      sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
+             lua_pushnumber(lua, data.expires);
+             lua_setfield(lua, -2, "lease_expires");
 #endif
-      
-      /* supplied data may just exceed normal buffer (unlikely) */
-      if ((data.hostname_len + data.ed_len) > daemon->packet_buff_sz && 
-         !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len)))
-       continue;
-      
-      if (!read_write(pipefd[0], buf, 
-                     data.hostname_len + data.ed_len, 1))
+             
+             if (hostname)
+               {
+                 lua_pushstring(lua, hostname);
+                 lua_setfield(lua, -2, "hostname");
+               }
+             
+             if (domain)
+               {
+                 lua_pushstring(lua, domain);
+                 lua_setfield(lua, -2, "domain");
+               }
+             
+             end = extradata + data.ed_len;
+             buf = extradata;
+             
+             if (!is6)
+               buf = grab_extradata_lua(buf, end, "vendor_class");
+#ifdef HAVE_DHCP6
+             else  if (data.vendorclass_count != 0)
+               {
+                 sprintf(daemon->dhcp_buff2, "vendor_class_id");
+                 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
+                 for (i = 0; i < data.vendorclass_count - 1; i++)
+                   {
+                     sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
+                     buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
+                   }
+               }
+#endif
+             
+             buf = grab_extradata_lua(buf, end, "supplied_hostname");
+             
+             if (!is6)
+               {
+                 buf = grab_extradata_lua(buf, end, "cpewan_oui");
+                 buf = grab_extradata_lua(buf, end, "cpewan_serial");   
+                 buf = grab_extradata_lua(buf, end, "cpewan_class");
+                 buf = grab_extradata_lua(buf, end, "circuit_id");
+                 buf = grab_extradata_lua(buf, end, "subscriber_id");
+                 buf = grab_extradata_lua(buf, end, "remote_id");
+               }
+             
+             buf = grab_extradata_lua(buf, end, "tags");
+             
+             if (is6)
+               buf = grab_extradata_lua(buf, end, "relay_address");
+             else if (data.giaddr.s_addr != 0)
+               {
+                 lua_pushstring(lua, inet_ntoa(data.giaddr));
+                 lua_setfield(lua, -2, "relay_address");
+               }
+             
+             for (i = 0; buf; i++)
+               {
+                 sprintf(daemon->dhcp_buff2, "user_class%i", i);
+                 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
+               }
+             
+             if (data.action != ACTION_DEL && data.remaining_time != 0)
+               {
+                 lua_pushnumber(lua, data.remaining_time);
+                 lua_setfield(lua, -2, "time_remaining");
+               }
+             
+             if (data.action == ACTION_OLD_HOSTNAME && hostname)
+               {
+                 lua_pushstring(lua, hostname);
+                 lua_setfield(lua, -2, "old_hostname");
+               }
+             
+             if (!is6 || data.hwaddr_len != 0)
+               {
+                 lua_pushstring(lua, daemon->dhcp_buff);
+                 lua_setfield(lua, -2, "mac_address");
+               }
+             
+             lua_pushstring(lua, daemon->addrbuff);
+             lua_setfield(lua, -2, "ip_address");
+           
+             lua_call(lua, 2, 0);      /* pass 2 values, expect 0 */
+           }
+       }
+#endif
+
+      /* no script, just lua */
+      if (!daemon->lease_change_command)
        continue;
-      
+
       /* possible fork errors are all temporary resource problems */
       while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
        sleep(2);
 
-      free(alloc_buff);
-      
       if (pid == -1)
        continue;
-         
+      
       /* wait for child to complete */
       if (pid != 0)
        {
@@ -188,9 +465,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
                {
                  /* On error send event back to main process for logging */
                  if (WIFSIGNALED(status))
-                   send_event(event_fd, EVENT_KILLED, WTERMSIG(status));
+                   send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
                  else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
-                   send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status));
+                   send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
                  break;
                }
              
@@ -201,62 +478,82 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
          continue;
        }
       
-      if (data.clid_len != 0)
-       my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
-
-      if (strlen(data.interface) != 0)
-       my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
-            
+      if (data.action != ACTION_TFTP)
+       {
+#ifdef HAVE_DHCP6
+         my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
+         my_setenv("DNSMASQ_SERVER_DUID", is6 ? daemon->dhcp_packet.iov_base : NULL, &err); 
+         my_setenv("DNSMASQ_MAC", is6 && data.hwaddr_len != 0 ? daemon->dhcp_buff : NULL, &err);
+#endif
+         
+         my_setenv("DNSMASQ_CLIENT_ID", !is6 && data.clid_len != 0 ? daemon->packet : NULL, &err);
+         my_setenv("DNSMASQ_INTERFACE", strlen(data.interface) != 0 ? data.interface : NULL, &err);
+         
 #ifdef HAVE_BROKEN_RTC
-      my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
+         sprintf(daemon->dhcp_buff2, "%u", data.length);
+         my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
 #else
-      my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err); 
+         sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
+         my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err); 
 #endif
-      
-      if (data.hostname_len != 0)
-       {
-         char *dot;
-         hostname = (char *)buf;
-         hostname[data.hostname_len - 1] = 0;
-         if (!legal_hostname(hostname))
-           hostname = NULL;
-         else if ((dot = strchr(hostname, '.')))
+         
+         my_setenv("DNSMASQ_DOMAIN", domain, &err);
+         
+         end = extradata + data.ed_len;
+         buf = extradata;
+         
+         if (!is6)
+           buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
+#ifdef HAVE_DHCP6
+         else
            {
-             my_setenv("DNSMASQ_DOMAIN", dot+1, &err);
-             *dot = 0;
-           } 
-         buf += data.hostname_len;
-       }
-
-      end = buf + data.ed_len;
-      buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
-      buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
-      buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
-      buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);   
-      buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
-      buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
-      
-      for (i = 0; buf; i++)
-       {
-         sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
-         buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
-       }
-      
-      if (data.giaddr.s_addr != 0)
-       my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err); 
+             if (data.vendorclass_count != 0)
+               {
+                 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
+                 for (i = 0; i < data.vendorclass_count - 1; i++)
+                   {
+                     sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
+                     buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
+                   }
+               }
+           }
+#endif
+         
+         buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
+         
+         if (!is6)
+           {
+             buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
+             buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);   
+             buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
+             buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
+             buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
+             buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
+           }
+         
+         buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
 
-      if (data.action != ACTION_DEL)
-       {
+         if (is6)
+           buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
+         else 
+           my_setenv("DNSMASQ_RELAY_ADDRESS", data.giaddr.s_addr != 0 ? inet_ntoa(data.giaddr) : NULL, &err); 
+         
+         for (i = 0; buf; i++)
+           {
+             sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
+             buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
+           }
+         
          sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
-         my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
-       }
-
-      if (data.action == ACTION_OLD_HOSTNAME && hostname)
-       {
-         my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
-         hostname = NULL;
+         my_setenv("DNSMASQ_TIME_REMAINING", data.action != ACTION_DEL && data.remaining_time != 0 ? daemon->dhcp_buff2 : NULL, &err);
+         
+         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);
+      
       /* 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);
@@ -267,23 +564,61 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
        {
          execl(daemon->lease_change_command, 
                p ? p+1 : daemon->lease_change_command,
-               action_str, daemon->dhcp_buff, inet_ntoa(data.addr), hostname, (char*)NULL);
+               action_str, is6 ? daemon->packet : daemon->dhcp_buff, 
+               daemon->addrbuff, hostname, (char*)NULL);
          err = errno;
        }
       /* failed, send event so the main process logs the problem */
-      send_event(event_fd, EVENT_EXEC_ERR, err);
+      send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
       _exit(0); 
     }
 }
 
 static void my_setenv(const char *name, const char *value, int *error)
 {
-  if (*error == 0 && setenv(name, value, 1) != 0)
-    *error = errno;
+  if (*error == 0)
+    {
+      if (!value)
+       unsetenv(name);
+      else if (setenv(name, value, 1) != 0)
+       *error = errno;
+    }
 }
  
 static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end,  char *env, int *err)
 {
+  unsigned char *next = NULL;
+  char *val = NULL;
+
+  if (buf && (buf != end))
+    {
+      for (next = buf; ; next++)
+       if (next == end)
+         {
+           next = NULL;
+           break;
+         }
+       else if (*next == 0)
+         break;
+
+      if (next && (next != buf))
+       {
+         char *p;
+         /* No "=" in value */
+         if ((p = strchr((char *)buf, '=')))
+           *p = 0;
+         val = (char *)buf;
+       }
+    }
+  
+  my_setenv(env, val, err);
+   
+  return next ? next + 1 : NULL;
+}
+
+#ifdef HAVE_LUASCRIPT
+static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
+{
   unsigned char *next;
 
   if (!buf || (buf == end))
@@ -295,36 +630,16 @@ static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end,  ch
   
   if (next != buf)
     {
-      char *p;
-      /* No "=" in value */
-      if ((p = strchr((char *)buf, '=')))
-       *p = 0;
-      my_setenv(env, (char *)buf, err);
+      lua_pushstring(lua,  (char *)buf);
+      lua_setfield(lua, -2, field);
     }
 
   return next + 1;
 }
+#endif
 
-/* pack up lease data into a buffer */    
-void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
+static void buff_alloc(size_t size)
 {
-  unsigned char *p;
-  size_t size;
-  unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
-  
-  /* no script */
-  if (daemon->helperfd == -1)
-    return;
-
-  if (lease->extradata)
-    ed_len = lease->extradata_len;
-  if (lease->clid)
-    clid_len = lease->clid_len;
-  if (hostname)
-    hostname_len = strlen(hostname) + 1;
-
-  size = sizeof(struct script_data) +  clid_len + ed_len + hostname_len;
-
   if (size > buf_size)
     {
       struct script_data *new;
@@ -340,8 +655,39 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
       buf = new;
       buf_size = size;
     }
+}
+
+/* pack up lease data into a buffer */    
+void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
+{
+  unsigned char *p;
+  unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
+  int fd = daemon->dhcpfd;
+#ifdef HAVE_DHCP6 
+  if (!daemon->dhcp)
+    fd = daemon->dhcp6fd;
+#endif
+
+  /* no script */
+  if (daemon->helperfd == -1)
+    return;
+
+  if (lease->extradata)
+    ed_len = lease->extradata_len;
+  if (lease->clid)
+    clid_len = lease->clid_len;
+  if (hostname)
+    hostname_len = strlen(hostname) + 1;
+
+  buff_alloc(sizeof(struct script_data) +  clid_len + ed_len + hostname_len);
 
   buf->action = action;
+  buf->flags = lease->flags;
+#ifdef HAVE_DHCP6 
+  buf->vendorclass_count = lease->vendorclass_count;
+  buf->addr6 = lease->addr6;
+  buf->iaid = lease->iaid;
+#endif
   buf->hwaddr_len = lease->hwaddr_len;
   buf->hwaddr_type = lease->hwaddr_type;
   buf->clid_len = clid_len;
@@ -349,8 +695,8 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
   buf->hostname_len = hostname_len;
   buf->addr = lease->addr;
   buf->giaddr = lease->giaddr;
-  memcpy(buf->hwaddr, lease->hwaddr, lease->hwaddr_len);
-  if (!indextoname(daemon->dhcpfd, lease->last_interface, buf->interface))
+  memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
+  if (!indextoname(fd, lease->last_interface, buf->interface))
     buf->interface[0] = 0;
   
 #ifdef HAVE_BROKEN_RTC 
@@ -358,7 +704,11 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
 #else
   buf->expires = lease->expires;
 #endif
-  buf->remaining_time = (unsigned int)difftime(lease->expires, now);
+
+  if (lease->expires != 0)
+    buf->remaining_time = (unsigned int)difftime(lease->expires, now);
+  else
+    buf->remaining_time = 0;
 
   p = (unsigned char *)(buf+1);
   if (clid_len != 0)
@@ -379,6 +729,37 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
   bytes_in_buf = p - (unsigned char *)buf;
 }
 
+#ifdef HAVE_TFTP
+/* This nastily re-uses DHCP-fields for TFTP stuff */
+void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
+{
+  unsigned int filename_len;
+
+  /* no script */
+  if (daemon->helperfd == -1)
+    return;
+  
+  filename_len = strlen(filename) + 1;
+  buff_alloc(sizeof(struct script_data) +  filename_len);
+  memset(buf, 0, sizeof(struct script_data));
+
+  buf->action = ACTION_TFTP;
+  buf->hostname_len = filename_len;
+  buf->file_len = file_len;
+
+  if ((buf->flags = peer->sa.sa_family) == AF_INET)
+    buf->addr = peer->in.sin_addr;
+#ifdef HAVE_IPV6
+  else
+    buf->addr6 = peer->in6.sin6_addr;
+#endif
+
+  memcpy((unsigned char *)(buf+1), filename, filename_len);
+  
+  bytes_in_buf = sizeof(struct script_data) +  filename_len;
+}
+#endif
+
 int helper_buf_empty(void)
 {
   return bytes_in_buf == 0;
@@ -408,3 +789,4 @@ void helper_write(void)
 #endif
 
 
+
diff --git a/src/inotify.c b/src/inotify.c
new file mode 100644 (file)
index 0000000..52d412f
--- /dev/null
@@ -0,0 +1,288 @@
+/* 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"
+#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 
+   or moved into the directory.
+   
+   When either of those happen, we look to see if the file involved
+   is actually a resolv-file, and if so, call poll-resolv with
+   the "force" argument, to ensure it's read.
+
+   This adds one new error condition: the directories containing
+   all specified resolv-files must exist at start-up, even if the actual
+   files don't. 
+*/
+
+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);
+  
+  for (res = daemon->resolv_files; res; res = res->next)
+    {
+      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)))
+       {
+         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 */
+         res->wd = inotify_add_watch(daemon->inotifyfd, path, IN_CLOSE_WRITE | IN_MOVED_TO);
+
+         res->file = d+1; /* pointer to filename */
+         *d = '/';
+         
+         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);
+       
+    }
+}
+
+
+/* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
+void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
+{
+  struct hostsfile *ah;
+  
+  for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
+    {
+      DIR *dir_stream = NULL;
+      struct dirent *ent;
+      struct stat buf;
+     
+      if (!(ah->flags & flag))
+       continue;
+      if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
+       {
+         my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"), 
+                   ah->fname, strerror(errno));
+         continue;
+       }
+      
+       if (!(ah->flags & AH_WD_DONE))
+        {
+          ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
+          ah->flags |= AH_WD_DONE;
+        }
+
+       /* Read contents of dir _after_ calling add_watch, in the hope of avoiding
+         a race which misses files being added as we start */
+       if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
+        {
+          my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
+                    ah->fname, strerror(errno));
+          continue;
+        }
+
+       while ((ent = readdir(dir_stream)))
+        {
+          size_t lendir = strlen(ah->fname);
+          size_t lenfile = strlen(ent->d_name);
+          char *path;
+          
+          /* ignore emacs backups and dotfiles */
+          if (lenfile == 0 || 
+              ent->d_name[lenfile - 1] == '~' ||
+              (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
+              ent->d_name[0] == '.')
+            continue;
+          
+          if ((path = whine_malloc(lendir + lenfile + 2)))
+            {
+              strcpy(path, ah->fname);
+              strcat(path, "/");
+              strcat(path, ent->d_name);
+              
+              /* ignore non-regular files */
+              if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
+                {
+                  if (ah->flags & AH_HOSTS)
+                    total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
+#ifdef HAVE_DHCP
+                  else if (ah->flags & (AH_DHCP_HST | AH_DHCP_OPT))
+                    option_read_dynfile(path, ah->flags);
+#endif            
+                }
+
+              free(path);
+            }
+        }
+    }
+}
+
+int inotify_check(time_t now)
+{
+  int hit = 0;
+  struct hostsfile *ah;
+
+  while (1)
+    {
+      int rc;
+      char *p;
+      struct resolvc *res;
+      struct inotify_event *in;
+
+      while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
+      
+      if (rc <= 0)
+       break;
+      
+      for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len) 
+       {
+         in = (struct inotify_event*)p;
+         
+         for (res = daemon->resolv_files; res; res = res->next)
+           if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
+             hit = 1;
+
+         /* ignore emacs backups and dotfiles */
+         if (in->len == 0 || 
+             in->name[in->len - 1] == '~' ||
+             (in->name[0] == '#' && in->name[in->len - 1] == '#') ||
+             in->name[0] == '.')
+           continue;
+         
+         for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
+           if (ah->wd == in->wd)
+             {
+               size_t lendir = strlen(ah->fname);
+               char *path;
+               
+               if ((path = whine_malloc(lendir + in->len + 2)))
+                 {
+                   strcpy(path, ah->fname);
+                   strcat(path, "/");
+                   strcat(path, in->name);
+                    
+                   my_syslog(LOG_INFO, _("inotify, new or changed file %s"), path);
+
+                   if (ah->flags & AH_HOSTS)
+                     {
+                       read_hostsfile(path, ah->index, 0, NULL, 0);
+#ifdef HAVE_DHCP
+                       if (daemon->dhcp || daemon->doing_dhcp6) 
+                         {
+                           /* Propogate the consequences of loading a new dhcp-host */
+                           dhcp_update_configs(daemon->dhcp_conf);
+                           lease_update_from_configs(); 
+                           lease_update_file(now); 
+                           lease_update_dns(1);
+                         }
+#endif
+                     }
+#ifdef HAVE_DHCP
+                   else if (ah->flags & AH_DHCP_HST)
+                     {
+                       if (option_read_dynfile(path, AH_DHCP_HST))
+                         {
+                           /* Propogate the consequences of loading a new dhcp-host */
+                           dhcp_update_configs(daemon->dhcp_conf);
+                           lease_update_from_configs(); 
+                           lease_update_file(now); 
+                           lease_update_dns(1);
+                         }
+                     }
+                   else if (ah->flags & AH_DHCP_OPT)
+                     option_read_dynfile(path, AH_DHCP_OPT);
+#endif
+                   
+                   free(path);
+                 }
+             }
+       }
+    }
+  return hit;
+}
+
+#endif  /* INOTIFY */
+  
diff --git a/src/ip6addr.h b/src/ip6addr.h
new file mode 100644 (file)
index 0000000..f0b7e82
--- /dev/null
@@ -0,0 +1,34 @@
+/* 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/>.
+*/
+
+
+
+#define IN6_IS_ADDR_ULA(a) \
+        ((((__const uint32_t *) (a))[0] & htonl (0xff000000))                 \
+         == htonl (0xfd000000))
+
+#define IN6_IS_ADDR_ULA_ZERO(a) \
+        (((__const uint32_t *) (a))[0] == htonl (0xfd000000)                        \
+         && ((__const uint32_t *) (a))[1] == 0                                \
+         && ((__const uint32_t *) (a))[2] == 0                                \
+         && ((__const uint32_t *) (a))[3] == 0)
+
+#define IN6_IS_ADDR_LINK_LOCAL_ZERO(a) \
+        (((__const uint32_t *) (a))[0] == htonl (0xfe800000)                  \
+         && ((__const uint32_t *) (a))[1] == 0                                \
+         && ((__const uint32_t *) (a))[2] == 0                                \
+         && ((__const uint32_t *) (a))[3] == 0)
+
diff --git a/src/ipset.c b/src/ipset.c
new file mode 100644 (file)
index 0000000..a315e86
--- /dev/null
@@ -0,0 +1,229 @@
+/* ipset.c is Copyright (c) 2013 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+   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"
+
+#if defined(HAVE_IPSET) && defined(HAVE_LINUX_NETWORK)
+
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include <arpa/inet.h>
+#include <linux/version.h>
+#include <linux/netlink.h>
+
+/* We want to be able to compile against old header files
+   Kernel version is handled at run-time. */
+
+#define NFNL_SUBSYS_IPSET 6
+
+#define IPSET_ATTR_DATA 7
+#define IPSET_ATTR_IP 1
+#define IPSET_ATTR_IPADDR_IPV4 1
+#define IPSET_ATTR_IPADDR_IPV6 2
+#define IPSET_ATTR_PROTOCOL 1
+#define IPSET_ATTR_SETNAME 2
+#define IPSET_CMD_ADD 9
+#define IPSET_CMD_DEL 10
+#define IPSET_MAXNAMELEN 32
+#define IPSET_PROTOCOL 6
+
+#ifndef NFNETLINK_V0
+#define NFNETLINK_V0    0
+#endif
+
+#ifndef NLA_F_NESTED
+#define NLA_F_NESTED           (1 << 15)
+#endif
+
+#ifndef NLA_F_NET_BYTEORDER
+#define NLA_F_NET_BYTEORDER    (1 << 14)
+#endif
+
+struct my_nlattr {
+        __u16           nla_len;
+        __u16           nla_type;
+};
+
+struct my_nfgenmsg {
+        __u8  nfgen_family;             /* AF_xxx */
+        __u8  version;          /* nfnetlink version */
+        __be16    res_id;               /* resource id */
+};
+
+
+/* data structure size in here is fixed */
+#define BUFF_SZ 256
+
+#define NL_ALIGN(len) (((len)+3) & ~(3))
+static const struct sockaddr_nl snl = { .nl_family = AF_NETLINK };
+static int ipset_sock, old_kernel;
+static char *buffer;
+
+static inline void add_attr(struct nlmsghdr *nlh, uint16_t type, size_t len, const void *data)
+{
+  struct my_nlattr *attr = (void *)nlh + NL_ALIGN(nlh->nlmsg_len);
+  uint16_t payload_len = NL_ALIGN(sizeof(struct my_nlattr)) + len;
+  attr->nla_type = type;
+  attr->nla_len = payload_len;
+  memcpy((void *)attr + NL_ALIGN(sizeof(struct my_nlattr)), data, len);
+  nlh->nlmsg_len += NL_ALIGN(payload_len);
+}
+
+void ipset_init(void)
+{
+  struct utsname utsname;
+  int version;
+  char *split;
+  
+  if (uname(&utsname) < 0)
+    die(_("failed to find kernel version: %s"), NULL, EC_MISC);
+  
+  split = strtok(utsname.release, ".");
+  version = (split ? atoi(split) : 0);
+  split = strtok(NULL, ".");
+  version = version * 256 + (split ? atoi(split) : 0);
+  split = strtok(NULL, ".");
+  version = version * 256 + (split ? atoi(split) : 0);
+  old_kernel = (version < KERNEL_VERSION(2,6,32));
+  
+  if (old_kernel && (ipset_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) != -1)
+    return;
+  
+  if (!old_kernel && 
+      (buffer = safe_malloc(BUFF_SZ)) &&
+      (ipset_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER)) != -1 &&
+      (bind(ipset_sock, (struct sockaddr *)&snl, sizeof(snl)) != -1))
+    return;
+  
+  die (_("failed to create IPset control socket: %s"), NULL, EC_MISC);
+}
+
+static int new_add_to_ipset(const char *setname, const struct all_addr *ipaddr, int af, int remove)
+{
+  struct nlmsghdr *nlh;
+  struct my_nfgenmsg *nfg;
+  struct my_nlattr *nested[2];
+  uint8_t proto;
+  int addrsz = INADDRSZ;
+
+#ifdef HAVE_IPV6
+  if (af == AF_INET6)
+    addrsz = IN6ADDRSZ;
+#endif
+    
+  if (strlen(setname) >= IPSET_MAXNAMELEN) 
+    {
+      errno = ENAMETOOLONG;
+      return -1;
+    }
+  
+  memset(buffer, 0, BUFF_SZ);
+
+  nlh = (struct nlmsghdr *)buffer;
+  nlh->nlmsg_len = NL_ALIGN(sizeof(struct nlmsghdr));
+  nlh->nlmsg_type = (remove ? IPSET_CMD_DEL : IPSET_CMD_ADD) | (NFNL_SUBSYS_IPSET << 8);
+  nlh->nlmsg_flags = NLM_F_REQUEST;
+  
+  nfg = (struct my_nfgenmsg *)(buffer + nlh->nlmsg_len);
+  nlh->nlmsg_len += NL_ALIGN(sizeof(struct my_nfgenmsg));
+  nfg->nfgen_family = af;
+  nfg->version = NFNETLINK_V0;
+  nfg->res_id = htons(0);
+  
+  proto = IPSET_PROTOCOL;
+  add_attr(nlh, IPSET_ATTR_PROTOCOL, sizeof(proto), &proto);
+  add_attr(nlh, IPSET_ATTR_SETNAME, strlen(setname) + 1, setname);
+  nested[0] = (struct my_nlattr *)(buffer + NL_ALIGN(nlh->nlmsg_len));
+  nlh->nlmsg_len += NL_ALIGN(sizeof(struct my_nlattr));
+  nested[0]->nla_type = NLA_F_NESTED | IPSET_ATTR_DATA;
+  nested[1] = (struct my_nlattr *)(buffer + NL_ALIGN(nlh->nlmsg_len));
+  nlh->nlmsg_len += NL_ALIGN(sizeof(struct my_nlattr));
+  nested[1]->nla_type = NLA_F_NESTED | IPSET_ATTR_IP;
+  add_attr(nlh, 
+          (af == AF_INET ? IPSET_ATTR_IPADDR_IPV4 : IPSET_ATTR_IPADDR_IPV6) | NLA_F_NET_BYTEORDER,
+          addrsz, &ipaddr->addr);
+  nested[1]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[1];
+  nested[0]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[0];
+       
+  while (retry_send(sendto(ipset_sock, buffer, nlh->nlmsg_len, 0,
+                          (struct sockaddr *)&snl, sizeof(snl))));
+                                                                   
+  return errno == 0 ? 0 : -1;
+}
+
+
+static int old_add_to_ipset(const char *setname, const struct all_addr *ipaddr, int remove)
+{
+  socklen_t size;
+  struct ip_set_req_adt_get {
+    unsigned op;
+    unsigned version;
+    union {
+      char name[IPSET_MAXNAMELEN];
+      uint16_t index;
+    } set;
+    char typename[IPSET_MAXNAMELEN];
+  } req_adt_get;
+  struct ip_set_req_adt {
+    unsigned op;
+    uint16_t index;
+    uint32_t ip;
+  } req_adt;
+  
+  if (strlen(setname) >= sizeof(req_adt_get.set.name)) 
+    {
+      errno = ENAMETOOLONG;
+      return -1;
+    }
+  
+  req_adt_get.op = 0x10;
+  req_adt_get.version = 3;
+  strcpy(req_adt_get.set.name, setname);
+  size = sizeof(req_adt_get);
+  if (getsockopt(ipset_sock, SOL_IP, 83, &req_adt_get, &size) < 0)
+    return -1;
+  req_adt.op = remove ? 0x102 : 0x101;
+  req_adt.index = req_adt_get.set.index;
+  req_adt.ip = ntohl(ipaddr->addr.addr4.s_addr);
+  if (setsockopt(ipset_sock, SOL_IP, 83, &req_adt, sizeof(req_adt)) < 0)
+    return -1;
+  
+  return 0;
+}
+
+
+
+int add_to_ipset(const char *setname, const struct all_addr *ipaddr, int flags, int remove)
+{
+  int af = AF_INET;
+
+#ifdef HAVE_IPV6
+  if (flags & F_IPV6)
+    {
+      af = AF_INET6;
+      /* old method only supports IPv4 */
+      if (old_kernel)
+       return -1;
+    }
+#endif
+  
+  return old_kernel ? old_add_to_ipset(setname, ipaddr, remove) : new_add_to_ipset(setname, ipaddr, af, remove);
+}
+
+#endif
index cfa7543..8adb605 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
@@ -24,19 +24,13 @@ static int dns_dirty, file_dirty, leases_left;
 void lease_init(time_t now)
 {
   unsigned long ei;
-  struct in_addr addr;
+  struct all_addr addr;
   struct dhcp_lease *lease;
   int clid_len, hw_len, hw_type;
   FILE *leasestream;
   
-  /* These each hold a DHCP option max size 255
-     and get a terminating zero added */
-  daemon->dhcp_buff = safe_malloc(256);
-  daemon->dhcp_buff2 = safe_malloc(256); 
-  daemon->dhcp_buff3 = safe_malloc(256);
   leases_left = daemon->dhcp_max;
-
+  
   if (option_bool(OPT_LEASE_RO))
     {
       /* run "<lease_change_script> init" once to get the
@@ -73,23 +67,70 @@ void lease_init(time_t now)
   /* client-id max length is 255 which is 255*2 digits + 254 colons 
      borrow DNS packet buffer which is always larger than 1000 bytes */
   if (leasestream)
-    while (fscanf(leasestream, "%lu %255s %16s %255s %764s",
-                 &ei, daemon->dhcp_buff2, daemon->namebuff, 
-                 daemon->dhcp_buff, daemon->packet) == 5)
+    while (fscanf(leasestream, "%255s %255s", daemon->dhcp_buff3, daemon->dhcp_buff2) == 2)
       {
-       hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type);
-       /* For backwards compatibility, no explict MAC address type means ether. */
-       if (hw_type == 0 && hw_len != 0)
-         hw_type = ARPHRD_ETHER;
+#ifdef HAVE_DHCP6
+       if (strcmp(daemon->dhcp_buff3, "duid") == 0)
+         {
+           daemon->duid_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, 130, NULL, NULL);
+           daemon->duid = safe_malloc(daemon->duid_len);
+           memcpy(daemon->duid, daemon->dhcp_buff2, daemon->duid_len);
+           continue;
+         }
+#endif
+
+       ei = atol(daemon->dhcp_buff3);
        
-       addr.s_addr = inet_addr(daemon->namebuff);
+       if (fscanf(leasestream, " %64s %255s %764s",
+                  daemon->namebuff, daemon->dhcp_buff, daemon->packet) != 3)
+         break;
        
-       /* decode hex in place */
        clid_len = 0;
        if (strcmp(daemon->packet, "*") != 0)
          clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
        
-       if (!(lease = lease_allocate(addr)))
+       if (inet_pton(AF_INET, daemon->namebuff, &addr.addr.addr4) &&
+           (lease = lease4_allocate(addr.addr.addr4)))
+         {
+           hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type);
+           /* For backwards compatibility, no explict MAC address type means ether. */
+           if (hw_type == 0 && hw_len != 0)
+             hw_type = ARPHRD_ETHER; 
+
+           lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, 
+                            hw_len, hw_type, clid_len, now, 0);
+           
+           if (strcmp(daemon->dhcp_buff, "*") !=  0)
+             lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain(lease->addr), NULL);
+         }
+#ifdef HAVE_DHCP6
+       else if (inet_pton(AF_INET6, daemon->namebuff, &addr.addr.addr6))
+         {
+           char *s = daemon->dhcp_buff2;
+           int lease_type = LEASE_NA;
+           int iaid;
+
+           if (s[0] == 'T')
+             {
+               lease_type = LEASE_TA;
+               s++;
+             }
+           
+           iaid = strtoul(s, NULL, 10);
+           
+           if ((lease = lease6_allocate(&addr.addr.addr6, lease_type)))
+             {
+               lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, 0, clid_len, now, 0);
+               lease_set_iaid(lease, iaid);
+               if (strcmp(daemon->dhcp_buff, "*") !=  0)
+                 lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr), NULL);
+             }
+         }
+#endif
+       else
+         break;
+
+       if (!lease)
          die (_("too many stored leases"), NULL, EC_MISC);
                
 #ifdef HAVE_BROKEN_RTC
@@ -104,14 +145,9 @@ void lease_init(time_t now)
        lease->expires = (time_t)ei;
 #endif
        
-       lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len);
-       
-       if (strcmp(daemon->dhcp_buff, "*") !=  0)
-         lease_set_hostname(lease, daemon->dhcp_buff, 0);
-
        /* set these correctly: the "old" events are generated later from
           the startup synthesised SIGHUP. */
-       lease->new = lease->changed = 0;
+       lease->flags &= ~(LEASE_NEW | LEASE_CHANGED);
       }
   
 #ifdef HAVE_SCRIPT
@@ -150,17 +186,19 @@ void lease_update_from_configs(void)
   struct dhcp_lease *lease;
   struct dhcp_config *config;
   char *name;
-
+  
   for (lease = leases; lease; lease = lease->next)
-    if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len, 
-                             lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) && 
-       (config->flags & CONFIG_NAME) &&
-       (!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr))
-      lease_set_hostname(lease, config->hostname, 1);
+    if (lease->flags & (LEASE_TA | LEASE_NA))
+      continue;
+    else if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len, 
+                                  lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) && 
+            (config->flags & CONFIG_NAME) &&
+            (!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr))
+      lease_set_hostname(lease, config->hostname, 1, get_domain(lease->addr), NULL);
     else if ((name = host_from_dns(lease->addr)))
-      lease_set_hostname(lease, name, 1); /* updates auth flag only */
+      lease_set_hostname(lease, name, 1, get_domain(lease->addr), NULL); /* updates auth flag only */
 }
-
 static void ourprintf(int *errp, char *format, ...)
 {
   va_list ap;
@@ -186,11 +224,18 @@ void lease_update_file(time_t now)
       
       for (lease = leases; lease; lease = lease->next)
        {
+
+#ifdef HAVE_DHCP6
+         if (lease->flags & (LEASE_TA | LEASE_NA))
+           continue;
+#endif
+
 #ifdef HAVE_BROKEN_RTC
          ourprintf(&err, "%u ", lease->length);
 #else
          ourprintf(&err, "%lu ", (unsigned long)lease->expires);
 #endif
+
          if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0) 
            ourprintf(&err, "%.2x-", lease->hwaddr_type);
          for (i = 0; i < lease->hwaddr_len; i++)
@@ -199,8 +244,10 @@ void lease_update_file(time_t now)
              if (i != lease->hwaddr_len - 1)
                ourprintf(&err, ":");
            }
+         
+         inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN); 
 
-         ourprintf(&err, " %s ", inet_ntoa(lease->addr));
+         ourprintf(&err, " %s ", daemon->addrbuff);
          ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*");
                  
          if (lease->clid && lease->clid_len != 0)
@@ -213,6 +260,44 @@ void lease_update_file(time_t now)
            ourprintf(&err, "*\n");       
        }
       
+#ifdef HAVE_DHCP6  
+      if (daemon->duid)
+       {
+         ourprintf(&err, "duid ");
+         for (i = 0; i < daemon->duid_len - 1; i++)
+           ourprintf(&err, "%.2x:", daemon->duid[i]);
+         ourprintf(&err, "%.2x\n", daemon->duid[i]);
+         
+         for (lease = leases; lease; lease = lease->next)
+           {
+             
+             if (!(lease->flags & (LEASE_TA | LEASE_NA)))
+               continue;
+
+#ifdef HAVE_BROKEN_RTC
+             ourprintf(&err, "%u ", lease->length);
+#else
+             ourprintf(&err, "%lu ", (unsigned long)lease->expires);
+#endif
+    
+             inet_ntop(AF_INET6, &lease->addr6, daemon->addrbuff, ADDRSTRLEN);
+        
+             ourprintf(&err, "%s%u %s ", (lease->flags & LEASE_TA) ? "T" : "",
+                       lease->iaid, daemon->addrbuff);
+             ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*");
+             
+             if (lease->clid && lease->clid_len != 0)
+               {
+                 for (i = 0; i < lease->clid_len - 1; i++)
+                   ourprintf(&err, "%.2x:", lease->clid[i]);
+                 ourprintf(&err, "%.2x\n", lease->clid[i]);
+               }
+             else
+               ourprintf(&err, "*\n");   
+           }
+       }
+#endif      
+         
       if (fflush(daemon->lease_stream) != 0 ||
          fsync(fileno(daemon->lease_stream)) < 0)
        err = errno;
@@ -221,11 +306,33 @@ void lease_update_file(time_t now)
        file_dirty = 0;
     }
   
-  /* Set alarm for when the first lease expires + slop. */
-  for (next_event = 0, lease = leases; lease; lease = lease->next)
+  /* Set alarm for when the first lease expires. */
+  next_event = 0;
+
+#ifdef HAVE_DHCP6
+  /* do timed RAs and determine when the next is, also pings to potential SLAAC addresses */
+  if (daemon->doing_ra)
+    {
+      time_t event;
+      
+      if ((event = periodic_slaac(now, leases)) != 0)
+       {
+         if (next_event == 0 || difftime(next_event, event) > 0.0)
+           next_event = event;
+       }
+      
+      if ((event = periodic_ra(now)) != 0)
+       {
+         if (next_event == 0 || difftime(next_event, event) > 0.0)
+           next_event = event;
+       }
+    }
+#endif
+
+  for (lease = leases; lease; lease = lease->next)
     if (lease->expires != 0 &&
-       (next_event == 0 || difftime(next_event, lease->expires + 10) > 0.0))
-      next_event = lease->expires + 10;
+       (next_event == 0 || difftime(next_event, lease->expires) > 0.0))
+      next_event = lease->expires;
    
   if (err)
     {
@@ -237,25 +344,168 @@ void lease_update_file(time_t now)
                (unsigned int)difftime(next_event, now));
     }
 
-  if (next_event != 0)
-    alarm((unsigned)difftime(next_event, now)); 
+  send_alarm(next_event, now);
+}
+
+
+static int find_interface_v4(struct in_addr local, int if_index, char *label,
+                            struct in_addr netmask, struct in_addr broadcast, void *vparam)
+{
+  struct dhcp_lease *lease;
+  int prefix = netmask_length(netmask);
+
+  (void) label;
+  (void) broadcast;
+  (void) vparam;
+
+  for (lease = leases; lease; lease = lease->next)
+    if (!(lease->flags & (LEASE_TA | LEASE_NA)) &&
+       is_same_net(local, lease->addr, netmask) && 
+       prefix > lease->new_prefixlen) 
+      {
+       lease->new_interface = if_index;
+        lease->new_prefixlen = prefix;
+      }
+
+  return 1;
 }
 
-void lease_update_dns(void)
+#ifdef HAVE_DHCP6
+static int find_interface_v6(struct in6_addr *local,  int prefix,
+                            int scope, int if_index, int flags, 
+                            int preferred, int valid, void *vparam)
 {
   struct dhcp_lease *lease;
+
+  (void)scope;
+  (void)flags;
+  (void)preferred;
+  (void)valid;
+  (void)vparam;
+
+  for (lease = leases; lease; lease = lease->next)
+    if ((lease->flags & (LEASE_TA | LEASE_NA)))
+      if (is_same_net6(local, &lease->addr6, prefix) && prefix > lease->new_prefixlen) {
+        /* save prefix length for comparison, as we might get shorter matching
+         * prefix in upcoming netlink GETADDR responses
+         * */
+        lease->new_interface = if_index;
+        lease->new_prefixlen = prefix;
+      }
+
+  return 1;
+}
+
+void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface)
+{
+  /* We may be doing RA but not DHCPv4, in which case the lease
+     database may not exist and we have nothing to do anyway */
+  if (daemon->dhcp)
+    slaac_ping_reply(sender, packet, interface, leases);
+}
+
+void lease_update_slaac(time_t now)
+{
+  /* Called when we contruct a new RA-names context, to add putative
+     new SLAAC addresses to existing leases. */
+
+  struct dhcp_lease *lease;
   
-  if (daemon->port != 0 && dns_dirty)
+  if (daemon->dhcp)
+    for (lease = leases; lease; lease = lease->next)
+      slaac_add_addrs(lease, now, 0);
+}
+
+#endif
+
+
+/* Find interfaces associated with leases at start-up. This gets updated as
+   we do DHCP transactions, but information about directly-connected subnets
+   is useful from scrips and necessary for determining SLAAC addresses from
+   start-time. */
+void lease_find_interfaces(time_t now)
+{
+  struct dhcp_lease *lease;
+  
+  for (lease = leases; lease; lease = lease->next)
+    lease->new_prefixlen = lease->new_interface = 0;
+
+  iface_enumerate(AF_INET, &now, find_interface_v4);
+#ifdef HAVE_DHCP6
+  iface_enumerate(AF_INET6, &now, find_interface_v6);
+#endif
+
+  for (lease = leases; lease; lease = lease->next)
+    if (lease->new_interface != 0) 
+      lease_set_interface(lease, lease->new_interface, now);
+}
+
+#ifdef HAVE_DHCP6
+void lease_make_duid(time_t now)
+{
+  /* If we're not doing DHCPv6, and there are not v6 leases, don't add the DUID to the database */
+  if (!daemon->duid && daemon->doing_dhcp6)
     {
-      cache_unhash_dhcp();
+      file_dirty = 1;
+      make_duid(now);
+    }
+}
+#endif
+
+
+
+
+void lease_update_dns(int force)
+{
+  struct dhcp_lease *lease;
+
+  if (daemon->port != 0 && (dns_dirty || force))
+    {
+#ifndef HAVE_BROKEN_RTC
+      /* force transfer to authoritative secondaries */
+      daemon->soa_sn++;
+#endif
       
+      cache_unhash_dhcp();
+
       for (lease = leases; lease; lease = lease->next)
        {
+         int prot = AF_INET;
+         
+#ifdef HAVE_DHCP6
+         if (lease->flags & (LEASE_TA | LEASE_NA))
+           prot = AF_INET6;
+         else if (lease->hostname || lease->fqdn)
+           {
+             struct slaac_address *slaac;
+
+             for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
+               if (slaac->backoff == 0)
+                 {
+                   if (lease->fqdn)
+                     cache_add_dhcp_entry(lease->fqdn, AF_INET6, (struct all_addr *)&slaac->addr, lease->expires);
+                   if (!option_bool(OPT_DHCP_FQDN) && lease->hostname)
+                     cache_add_dhcp_entry(lease->hostname, AF_INET6, (struct all_addr *)&slaac->addr, lease->expires);
+                 }
+           }
+         
          if (lease->fqdn)
-           cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
+           cache_add_dhcp_entry(lease->fqdn, prot, 
+                                prot == AF_INET ? (struct all_addr *)&lease->addr : (struct all_addr *)&lease->addr6,
+                                lease->expires);
             
          if (!option_bool(OPT_DHCP_FQDN) && lease->hostname)
-           cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires);
+           cache_add_dhcp_entry(lease->hostname, prot, 
+                                prot == AF_INET ? (struct all_addr *)&lease->addr : (struct all_addr *)&lease->addr6, 
+                                lease->expires);
+       
+#else
+         if (lease->fqdn)
+           cache_add_dhcp_entry(lease->fqdn, prot, (struct all_addr *)&lease->addr, lease->expires);
+         
+         if (!option_bool(OPT_DHCP_FQDN) && lease->hostname)
+           cache_add_dhcp_entry(lease->hostname, prot, (struct all_addr *)&lease->addr, lease->expires);
+#endif
        }
       
       dns_dirty = 0;
@@ -275,7 +525,7 @@ void lease_prune(struct dhcp_lease *target, time_t now)
          if (lease->hostname)
            dns_dirty = 1;
          
-         *up = lease->next; /* unlink */
+         *up = lease->next; /* unlink */
          
          /* Put on old_leases list 'till we
             can run the script */
@@ -297,18 +547,30 @@ struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int h
 
   if (clid)
     for (lease = leases; lease; lease = lease->next)
-      if (lease->clid && clid_len == lease->clid_len &&
-         memcmp(clid, lease->clid, clid_len) == 0)
-       return lease;
+      {
+#ifdef HAVE_DHCP6
+       if (lease->flags & (LEASE_TA | LEASE_NA))
+         continue;
+#endif
+       if (lease->clid && clid_len == lease->clid_len &&
+           memcmp(clid, lease->clid, clid_len) == 0)
+         return lease;
+      }
   
   for (lease = leases; lease; lease = lease->next)     
-    if ((!lease->clid || !clid) && 
-       hw_len != 0 && 
-       lease->hwaddr_len == hw_len &&
-       lease->hwaddr_type == hw_type &&
-       memcmp(hwaddr, lease->hwaddr, hw_len) == 0)
-      return lease;
-  
+    {
+#ifdef HAVE_DHCP6
+      if (lease->flags & (LEASE_TA | LEASE_NA))
+       continue;
+#endif   
+      if ((!lease->clid || !clid) && 
+         hw_len != 0 && 
+         lease->hwaddr_len == hw_len &&
+         lease->hwaddr_type == hw_type &&
+         memcmp(hwaddr, lease->hwaddr, hw_len) == 0)
+       return lease;
+    }
+
   return NULL;
 }
 
@@ -317,27 +579,156 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
   struct dhcp_lease *lease;
 
   for (lease = leases; lease; lease = lease->next)
-    if (lease->addr.s_addr == addr.s_addr)
+    {
+#ifdef HAVE_DHCP6
+      if (lease->flags & (LEASE_TA | LEASE_NA))
+       continue;
+#endif  
+      if (lease->addr.s_addr == addr.s_addr)
+       return lease;
+    }
+
+  return NULL;
+}
+
+#ifdef HAVE_DHCP6
+/* find address for {CLID, IAID, address} */
+struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len, 
+                              int lease_type, int iaid, struct in6_addr *addr)
+{
+  struct dhcp_lease *lease;
+  
+  for (lease = leases; lease; lease = lease->next)
+    {
+      if (!(lease->flags & lease_type) || lease->iaid != iaid)
+       continue;
+
+      if (!IN6_ARE_ADDR_EQUAL(&lease->addr6, addr))
+       continue;
+      
+      if ((clid_len != lease->clid_len ||
+          memcmp(clid, lease->clid, clid_len) != 0))
+       continue;
+      
+      return lease;
+    }
+  
+  return NULL;
+}
+
+/* reset "USED flags */
+void lease6_reset(void)
+{
+  struct dhcp_lease *lease;
+  
+  for (lease = leases; lease; lease = lease->next)
+    lease->flags &= ~LEASE_USED;
+}
+
+/* enumerate all leases belonging to {CLID, IAID} */
+struct dhcp_lease *lease6_find_by_client(struct dhcp_lease *first, int lease_type, unsigned char *clid, int clid_len, int iaid)
+{
+  struct dhcp_lease *lease;
+
+  if (!first)
+    first = leases;
+  else
+    first = first->next;
+
+  for (lease = first; lease; lease = lease->next)
+    {
+      if (lease->flags & LEASE_USED)
+       continue;
+
+      if (!(lease->flags & lease_type) || lease->iaid != iaid)
+       continue;
+      if ((clid_len != lease->clid_len ||
+          memcmp(clid, lease->clid, clid_len) != 0))
+       continue;
+
       return lease;
+    }
+  
+  return NULL;
+}
+
+struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr)
+{
+  struct dhcp_lease *lease;
+    
+  for (lease = leases; lease; lease = lease->next)
+    {
+      if (!(lease->flags & (LEASE_TA | LEASE_NA)))
+       continue;
+      
+      if (is_same_net6(&lease->addr6, net, prefix) &&
+         (prefix == 128 || addr6part(&lease->addr6) == addr))
+       return lease;
+    }
   
   return NULL;
+} 
+
+/* Find largest assigned address in context */
+u64 lease_find_max_addr6(struct dhcp_context *context)
+{
+  struct dhcp_lease *lease;
+  u64 addr = addr6part(&context->start6);
+  
+  if (!(context->flags & (CONTEXT_STATIC | CONTEXT_PROXY)))
+    for (lease = leases; lease; lease = lease->next)
+      {
+       if (!(lease->flags & (LEASE_TA | LEASE_NA)))
+         continue;
+
+       if (is_same_net6(&lease->addr6, &context->start6, 64) &&
+           addr6part(&lease->addr6) > addr6part(&context->start6) &&
+           addr6part(&lease->addr6) <= addr6part(&context->end6) &&
+           addr6part(&lease->addr6) > addr)
+         addr = addr6part(&lease->addr6);
+      }
+  
+  return addr;
 }
 
+#endif
+
+/* Find largest assigned address in context */
+struct in_addr lease_find_max_addr(struct dhcp_context *context)
+{
+  struct dhcp_lease *lease;
+  struct in_addr addr = context->start;
+  
+  if (!(context->flags & (CONTEXT_STATIC | CONTEXT_PROXY)))
+    for (lease = leases; lease; lease = lease->next)
+      {
+#ifdef HAVE_DHCP6
+       if (lease->flags & (LEASE_TA | LEASE_NA))
+         continue;
+#endif
+       if (((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(context->start.s_addr)) &&
+           ((unsigned)ntohl(lease->addr.s_addr)) <= ((unsigned)ntohl(context->end.s_addr)) &&
+           ((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(addr.s_addr)))
+         addr = lease->addr;
+      }
+  
+  return addr;
+}
 
-struct dhcp_lease *lease_allocate(struct in_addr addr)
+static struct dhcp_lease *lease_allocate(void)
 {
   struct dhcp_lease *lease;
   if (!leases_left || !(lease = whine_malloc(sizeof(struct dhcp_lease))))
     return NULL;
 
   memset(lease, 0, sizeof(struct dhcp_lease));
-  lease->new = 1;
-  lease->addr = addr;
-  lease->hwaddr_len = 256; /* illegal value */
+  lease->flags = LEASE_NEW;
   lease->expires = 1;
 #ifdef HAVE_BROKEN_RTC
   lease->length = 0xffffffff; /* illegal value */
 #endif
+  lease->hwaddr_len = 256; /* illegal value */
   lease->next = leases;
   leases = lease;
   
@@ -347,22 +738,57 @@ struct dhcp_lease *lease_allocate(struct in_addr addr)
   return lease;
 }
 
-void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
+struct dhcp_lease *lease4_allocate(struct in_addr addr)
 {
-  time_t exp = now + (time_t)len;
+  struct dhcp_lease *lease = lease_allocate();
+  if (lease)
+    lease->addr = addr;
   
+  return lease;
+}
+
+#ifdef HAVE_DHCP6
+struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type)
+{
+  struct dhcp_lease *lease = lease_allocate();
+
+  if (lease)
+    {
+      lease->addr6 = *addrp;
+      lease->flags |= lease_type;
+      lease->iaid = 0;
+    }
+
+  return lease;
+}
+#endif
+
+void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
+{
+  time_t exp;
+
   if (len == 0xffffffff)
     {
       exp = 0;
       len = 0;
     }
-  
+  else
+    {
+      exp = now + (time_t)len;
+      /* Check for 2038 overflow. Make the lease
+        inifinite in that case, as the least disruptive
+        thing we can do. */
+      if (difftime(exp, now) <= 0.0)
+       exp = 0;
+    }
+
   if (exp != lease->expires)
     {
       dns_dirty = 1;
       lease->expires = exp;
 #ifndef HAVE_BROKEN_RTC
-      lease->aux_changed = file_dirty = 1;
+      lease->flags |= LEASE_AUX_CHANGED;
+      file_dirty = 1;
 #endif
     }
   
@@ -370,22 +796,45 @@ void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
   if (len != lease->length)
     {
       lease->length = len;
-      lease->aux_changed = file_dirty = 1; 
+      lease->flags |= LEASE_AUX_CHANGED;
+      file_dirty = 1; 
     }
 #endif
 } 
 
-void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
-                     unsigned char *clid, int hw_len, int hw_type, int clid_len)
+#ifdef HAVE_DHCP6
+void lease_set_iaid(struct dhcp_lease *lease, int iaid)
+{
+  if (lease->iaid != iaid)
+    {
+      lease->iaid = iaid;
+      lease->flags |= LEASE_CHANGED;
+    }
+}
+#endif
+
+void lease_set_hwaddr(struct dhcp_lease *lease, const unsigned char *hwaddr,
+                     const unsigned char *clid, int hw_len, int hw_type,
+                     int clid_len, time_t now, int force)
 {
+#ifdef HAVE_DHCP6
+  int change = force;
+  lease->flags |= LEASE_HAVE_HWADDR;
+#endif
+
+  (void)force;
+  (void)now;
+
   if (hw_len != lease->hwaddr_len ||
       hw_type != lease->hwaddr_type || 
       (hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0))
     {
-      memcpy(lease->hwaddr, hwaddr, hw_len);
+      if (hw_len != 0)
+       memcpy(lease->hwaddr, hwaddr, hw_len);
       lease->hwaddr_len = hw_len;
       lease->hwaddr_type = hw_type;
-      lease->changed = file_dirty = 1; /* run script on change */
+      lease->flags |= LEASE_CHANGED;
+      file_dirty = 1; /* run script on change */
     }
 
   /* only update clid when one is available, stops packets
@@ -398,18 +847,32 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
 
       if (lease->clid_len != clid_len)
        {
-         lease->aux_changed = file_dirty = 1;
+         lease->flags |= LEASE_AUX_CHANGED;
+         file_dirty = 1;
          free(lease->clid);
          if (!(lease->clid = whine_malloc(clid_len)))
            return;
+#ifdef HAVE_DHCP6
+         change = 1;
+#endif    
        }
       else if (memcmp(lease->clid, clid, clid_len) != 0)
-       lease->aux_changed = file_dirty = 1;
-         
+       {
+         lease->flags |= LEASE_AUX_CHANGED;
+         file_dirty = 1;
+#ifdef HAVE_DHCP6
+         change = 1;
+#endif 
+       }
+      
       lease->clid_len = clid_len;
       memcpy(lease->clid, clid, clid_len);
     }
-
+  
+#ifdef HAVE_DHCP6
+  if (change)
+    slaac_add_addrs(lease, now, force);
+#endif
 }
 
 static void kill_name(struct dhcp_lease *lease)
@@ -421,7 +884,7 @@ static void kill_name(struct dhcp_lease *lease)
   free(lease->old_hostname);
   
   /* If we know the fqdn, pass that. The helper will derive the
-     unqualified name from it, free the unqulaified name here. */
+     unqualified name from it, free the unqualified name here. */
 
   if (lease->fqdn)
     {
@@ -434,14 +897,18 @@ static void kill_name(struct dhcp_lease *lease)
   lease->hostname = lease->fqdn = NULL;
 }
 
-void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
+void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, char *domain, char *config_domain)
 {
   struct dhcp_lease *lease_tmp;
   char *new_name = NULL, *new_fqdn = NULL;
+
+  if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
+    my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, name);
   
   if (lease->hostname && name && hostname_isequal(lease->hostname, name))
     {
-      lease->auth_name = auth;
+      if (auth)
+       lease->flags |= LEASE_AUTH_NAME;
       return;
     }
   
@@ -451,19 +918,21 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
   /* If a machine turns up on a new net without dropping the old lease,
      or two machines claim the same name, then we end up with two interfaces with
      the same name. Check for that here and remove the name from the old lease.
+     Note that IPv6 leases are different. All the leases to the same DUID are 
+     allowed the same name.
+
      Don't allow a name from the client to override a name from dnsmasq config. */
   
   if (name)
     {
       if ((new_name = whine_malloc(strlen(name) + 1)))
        {
-         char *suffix = get_domain(lease->addr);
          strcpy(new_name, name);
-         if (suffix && (new_fqdn = whine_malloc(strlen(new_name) + strlen(suffix) + 2)))
+         if (domain && (new_fqdn = whine_malloc(strlen(new_name) + strlen(domain) + 2)))
            {
              strcpy(new_fqdn, name);
              strcat(new_fqdn, ".");
-             strcat(new_fqdn, suffix);
+             strcat(new_fqdn, domain);
            }
        }
          
@@ -472,7 +941,7 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
        {
          if (option_bool(OPT_DHCP_FQDN))
            {
-             if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn) )
+             if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn))
                continue;
            }
          else
@@ -480,8 +949,22 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
              if (!new_name || !lease_tmp->hostname || !hostname_isequal(lease_tmp->hostname, new_name) )
                continue; 
            }
-         
-         if (lease_tmp->auth_name && !auth)
+
+         if (lease->flags & (LEASE_TA | LEASE_NA))
+           {
+             if (!(lease_tmp->flags & (LEASE_TA | LEASE_NA)))
+               continue;
+
+             /* another lease for the same DUID is OK for IPv6 */
+             if (lease->clid_len == lease_tmp->clid_len &&
+                 lease->clid && lease_tmp->clid &&
+                 memcmp(lease->clid, lease_tmp->clid, lease->clid_len) == 0)
+               continue;             
+           }
+         else if (lease_tmp->flags & (LEASE_TA | LEASE_NA))
+           continue;
+                  
+         if ((lease_tmp->flags & LEASE_AUTH_NAME) && !auth)
            {
              free(new_name);
              free(new_fqdn);
@@ -498,20 +981,28 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
 
   lease->hostname = new_name;
   lease->fqdn = new_fqdn;
-  lease->auth_name = auth;
+  
+  if (auth)
+    lease->flags |= LEASE_AUTH_NAME;
   
   file_dirty = 1;
   dns_dirty = 1; 
-  lease->changed = 1; /* run script on change */
+  lease->flags |= LEASE_CHANGED; /* run script on change */
 }
 
-void lease_set_interface(struct dhcp_lease *lease, int interface)
+void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now)
 {
+  (void)now;
+
   if (lease->last_interface == interface)
     return;
 
   lease->last_interface = interface;
-  lease->changed = 1;
+  lease->flags |= LEASE_CHANGED; 
+
+#ifdef HAVE_DHCP6
+  slaac_add_addrs(lease, now, 0);
+#endif
 }
 
 void rerun_scripts(void)
@@ -519,7 +1010,7 @@ void rerun_scripts(void)
   struct dhcp_lease *lease;
   
   for (lease = leases; lease; lease = lease->next)
-    lease->changed = 1;
+    lease->flags |= LEASE_CHANGED; 
 }
 
 /* deleted leases get transferred to the old_leases list.
@@ -531,6 +1022,8 @@ int do_script_run(time_t now)
 {
   struct dhcp_lease *lease;
 
+  (void)now;
+
 #ifdef HAVE_DBUS
   /* If we're going to be sending DBus signals, but the connection is not yet up,
      delay everything until it is. */
@@ -554,6 +1047,14 @@ int do_script_run(time_t now)
        }
       else 
        {
+#ifdef HAVE_DHCP6
+         struct slaac_address *slaac, *tmp;
+         for (slaac = lease->slaac_address; slaac; slaac = tmp)
+           {
+             tmp = slaac->next;
+             free(slaac);
+           }
+#endif
          kill_name(lease);
 #ifdef HAVE_SCRIPT
          queue_script(ACTION_DEL, lease, lease->old_hostname, now);
@@ -585,18 +1086,18 @@ int do_script_run(time_t now)
       }
   
   for (lease = leases; lease; lease = lease->next)
-    if (lease->new || lease->changed || 
-       (lease->aux_changed && option_bool(OPT_LEASE_RO)))
+    if ((lease->flags & (LEASE_NEW | LEASE_CHANGED)) || 
+       ((lease->flags & LEASE_AUX_CHANGED) && option_bool(OPT_LEASE_RO)))
       {
 #ifdef HAVE_SCRIPT
-       queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, 
+       queue_script((lease->flags & LEASE_NEW) ? ACTION_ADD : ACTION_OLD, lease, 
                     lease->fqdn ? lease->fqdn : lease->hostname, now);
 #endif
 #ifdef HAVE_DBUS
-       emit_dbus_signal(lease->new ? ACTION_ADD : ACTION_OLD, lease,
+       emit_dbus_signal((lease->flags & LEASE_NEW) ? ACTION_ADD : ACTION_OLD, lease,
                         lease->fqdn ? lease->fqdn : lease->hostname);
 #endif
-       lease->new = lease->changed = lease->aux_changed = 0;
+       lease->flags &= ~(LEASE_NEW | LEASE_CHANGED | LEASE_AUX_CHANGED);
        
        /* this is used for the "add" call, then junked, since they're not in the database */
        free(lease->extradata);
@@ -608,6 +1109,44 @@ int do_script_run(time_t now)
   return 0; /* nothing to do */
 }
 
+#ifdef HAVE_SCRIPT
+void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned int len, int delim)
+{
+  unsigned int i;
+  
+  /* check for embeded NULLs */
+  for (i = 0; i < len; i++)
+    if (data[i] == 0)
+      {
+       len = i;
+       break;
+      }
+
+  if ((lease->extradata_size - lease->extradata_len) < (len + 1))
+    {
+      size_t newsz = lease->extradata_len + len + 100;
+      unsigned char *new = whine_malloc(newsz);
+  
+      if (!new)
+       return;
+      
+      if (lease->extradata)
+       {
+         memcpy(new, lease->extradata, lease->extradata_len);
+         free(lease->extradata);
+       }
+
+      lease->extradata = new;
+      lease->extradata_size = newsz;
+    }
+
+  if (len != 0)
+    memcpy(lease->extradata + lease->extradata_len, data, len);
+  lease->extradata[lease->extradata_len + len] = delim;
+  lease->extradata_len += len + 1; 
+}
+#endif
+
 #endif
          
 
index baaae7c..27b2e59 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
@@ -84,7 +84,7 @@ int log_start(struct passwd *ent_pw, int errfd)
 
   if (!log_reopen(daemon->log_file))
     {
-      send_event(errfd, EVENT_LOG_ERR, errno);
+      send_event(errfd, EVENT_LOG_ERR, errno, daemon->log_file ? daemon->log_file : "");
       _exit(0);
     }
 
@@ -154,6 +154,19 @@ static void log_write(void)
    
   while (entries)
     {
+      /* The data in the payoad is written with a terminating zero character 
+        and the length reflects this. For a stream connection we need to 
+        send the zero as a record terminator, but this isn't done for a 
+        datagram connection, so treat the length as one less than reality 
+        to elide the zero. If we're logging to a file, turn the zero into 
+        a newline, and leave the length alone. */
+      int len_adjust = 0;
+
+      if (log_to_file)
+       entries->payload[entries->offset + entries->length - 1] = '\n';
+      else if (connection_type == SOCK_DGRAM)
+       len_adjust = 1;
+
       /* Avoid duplicates over a fork() */
       if (entries->pid != getpid())
        {
@@ -163,11 +176,11 @@ static void log_write(void)
 
       connection_good = 1;
 
-      if ((rc = write(log_fd, entries->payload + entries->offset, entries->length)) != -1)
+      if ((rc = write(log_fd, entries->payload + entries->offset, entries->length - len_adjust)) != -1)
        {
          entries->length -= rc;
          entries->offset += rc;
-         if (entries->length == 0)
+         if (entries->length == len_adjust)
            {
              free_entry();
              if (entries_lost != 0)
@@ -183,7 +196,7 @@ static void log_write(void)
       if (errno == EINTR)
        continue;
 
-      if (errno == EAGAIN)
+      if (errno == EAGAIN || errno == EWOULDBLOCK)
        return; /* syslogd busy, go again when select() or poll() says so */
       
       if (errno == ENOBUFS)
@@ -231,7 +244,8 @@ static void log_write(void)
                  errno == ECONNREFUSED ||
                  errno == EISCONN || 
                  errno == EINTR ||
-                 errno == EAGAIN)
+                 errno == EAGAIN || 
+                 errno == EWOULDBLOCK)
                {
                  /* try again on next syslog() call */
                  connection_good = 0;
@@ -366,10 +380,6 @@ void my_syslog(int priority, const char *format, ...)
       entry->length = len > MAX_MESSAGE ? MAX_MESSAGE : len;
       entry->offset = 0;
       entry->pid = pid;
-
-      /* replace terminator with \n */
-      if (log_to_file)
-       entry->payload[entry->length - 1] = '\n';
     }
   
   /* almost always, logging won't block, so try and write this now,
@@ -411,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();
 }
 
diff --git a/src/loop.c b/src/loop.c
new file mode 100644 (file)
index 0000000..c9ed075
--- /dev/null
@@ -0,0 +1,117 @@
+/* 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"
+
+#ifdef HAVE_LOOP
+static ssize_t loop_make_probe(u32 uid);
+
+void loop_send_probes()
+{
+   struct server *serv;
+   
+   if (!option_bool(OPT_LOOP_DETECT))
+     return;
+
+   /* Loop through all upstream servers not for particular domains, and send a query to that server which is
+      identifiable, via the uid. If we see that query back again, then the server is looping, and we should not use it. */
+   for (serv = daemon->servers; serv; serv = serv->next)
+     if (!(serv->flags & 
+          (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_LOOP)))
+       {
+        ssize_t len = loop_make_probe(serv->uid);
+        int fd;
+        struct randfd *rfd = NULL;
+        
+        if (serv->sfd)
+          fd = serv->sfd->fd;
+        else 
+          {
+            if (!(rfd = allocate_rfd(serv->addr.sa.sa_family)))
+              continue;
+            fd = rfd->fd;
+          }
+
+        while (retry_send(sendto(fd, daemon->packet, len, 0, 
+                                 &serv->addr.sa, sa_len(&serv->addr))));
+        
+        free_rfd(rfd);
+       }
+}
+  
+static ssize_t loop_make_probe(u32 uid)
+{
+  struct dns_header *header = (struct dns_header *)daemon->packet;
+  unsigned char *p = (unsigned char *)(header+1);
+
+  /* packet buffer overwritten */
+  daemon->srv_save = NULL;
+  
+  header->id = rand16();
+  header->ancount = header->nscount = header->arcount = htons(0);
+  header->qdcount = htons(1);
+  header->hb3 = HB3_RD;
+  header->hb4 = 0;
+  SET_OPCODE(header, QUERY);
+
+  *p++ = 8;
+  sprintf((char *)p, "%.8x", uid);
+  p += 8;
+  *p++ = strlen(LOOP_TEST_DOMAIN);
+  strcpy((char *)p, LOOP_TEST_DOMAIN); /* Add terminating zero */
+  p += strlen(LOOP_TEST_DOMAIN) + 1;
+
+  PUTSHORT(LOOP_TEST_TYPE, p);
+  PUTSHORT(C_IN, p);
+
+  return p - (unsigned char *)header;
+}
+  
+
+int detect_loop(char *query, int type)
+{
+  int i;
+  u32 uid;
+  struct server *serv;
+  
+  if (!option_bool(OPT_LOOP_DETECT))
+    return 0;
+
+  if (type != LOOP_TEST_TYPE ||
+      strlen(LOOP_TEST_DOMAIN) + 9 != strlen(query) ||
+      strstr(query, LOOP_TEST_DOMAIN) != query + 9)
+    return 0;
+
+  for (i = 0; i < 8; i++)
+    if (!isxdigit(query[i]))
+      return 0;
+
+  uid = strtol(query, NULL, 16);
+
+  for (serv = daemon->servers; serv; serv = serv->next)
+     if (!(serv->flags & 
+          (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_LOOP)) &&
+        uid == serv->uid)
+       {
+        serv->flags |= SERV_LOOP;
+        check_servers(); /* log new state */
+        return 1;
+       }
+
+  return 0;
+}
+
+#endif
index f6da7db..753784d 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
 #  define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) 
 #endif 
 
+
 static struct iovec iov;
 static u32 netlink_pid;
 
-static void nl_err(struct nlmsghdr *h);
-static void nl_routechange(struct nlmsghdr *h);
+static void nl_async(struct nlmsghdr *h);
 
 void netlink_init(void)
 {
@@ -48,10 +48,17 @@ void netlink_init(void)
   addr.nl_family = AF_NETLINK;
   addr.nl_pad = 0;
   addr.nl_pid = 0; /* autobind */
-#ifdef HAVE_IPV6
-  addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
-#else
   addr.nl_groups = RTMGRP_IPV4_ROUTE;
+  if (option_bool(OPT_CLEVERBIND))
+    addr.nl_groups |= RTMGRP_IPV4_IFADDR;  
+#ifdef HAVE_IPV6
+  addr.nl_groups |= RTMGRP_IPV6_ROUTE;
+  if (option_bool(OPT_CLEVERBIND))
+    addr.nl_groups |= RTMGRP_IPV6_IFADDR;
+#endif
+#ifdef HAVE_DHCP6
+  if (daemon->doing_ra || daemon->doing_dhcp6)
+    addr.nl_groups |= RTMGRP_IPV6_IFADDR;
 #endif
   
   /* May not be able to have permission to set multicast groups don't die in that case */
@@ -127,13 +134,15 @@ static ssize_t netlink_recv(void)
 }
   
 
-/* family = AF_UNSPEC finds ARP table entries. */
+/* family = AF_UNSPEC finds ARP table entries.
+   family = AF_LOCAL finds MAC addresses. */
 int iface_enumerate(int family, void *parm, int (*callback)())
 {
   struct sockaddr_nl addr;
   struct nlmsghdr *h;
   ssize_t len;
   static unsigned int seq = 0;
+  int callback_ok = 1;
 
   struct {
     struct nlmsghdr nlh;
@@ -144,20 +153,26 @@ int iface_enumerate(int family, void *parm, int (*callback)())
   addr.nl_pad = 0;
   addr.nl_groups = 0;
   addr.nl_pid = 0; /* address to kernel */
+ again: 
+  if (family == AF_UNSPEC)
+    req.nlh.nlmsg_type = RTM_GETNEIGH;
+  else if (family == AF_LOCAL)
+    req.nlh.nlmsg_type = RTM_GETLINK;
+  else
+    req.nlh.nlmsg_type = RTM_GETADDR;
 
- again:
   req.nlh.nlmsg_len = sizeof(req);
-  req.nlh.nlmsg_type = family == AF_UNSPEC ? RTM_GETNEIGH : RTM_GETADDR;
   req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK; 
   req.nlh.nlmsg_pid = 0;
   req.nlh.nlmsg_seq = ++seq;
   req.g.rtgen_family = family; 
 
   /* Don't block in recvfrom if send fails */
-  while((len = sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0, 
-                     (struct sockaddr *)&addr, sizeof(addr))) == -1 && retry_send());
-  
-  if (len == -1)
+  while(retry_send(sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0, 
+                         (struct sockaddr *)&addr, sizeof(addr))));
+
+  if (errno != 0)
     return 0;
     
   while (1)
@@ -173,13 +188,14 @@ int iface_enumerate(int family, void *parm, int (*callback)())
        }
 
       for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
-       if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid)
-         nl_routechange(h); /* May be multicast arriving async */
-       else if (h->nlmsg_type == NLMSG_ERROR)
-         nl_err(h);
+       if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
+         {
+           /* May be multicast arriving async */
+           nl_async(h);
+         }
        else if (h->nlmsg_type == NLMSG_DONE)
-         return 1;
-       else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC)
+         return callback_ok;
+       else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)
          {
            struct ifaddrmsg *ifa = NLMSG_DATA(h);  
            struct rtattr *rta = IFA_RTA(ifa);
@@ -190,8 +206,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
                if (ifa->ifa_family == AF_INET)
                  {
                    struct in_addr netmask, addr, broadcast;
-                   
-                   netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
+                   char *label = NULL;
+
+                   netmask.s_addr = htonl(~(in_addr_t)0 << (32 - ifa->ifa_prefixlen));
+
                    addr.s_addr = 0;
                    broadcast.s_addr = 0;
                    
@@ -201,29 +219,50 @@ int iface_enumerate(int family, void *parm, int (*callback)())
                          addr = *((struct in_addr *)(rta+1));
                        else if (rta->rta_type == IFA_BROADCAST)
                          broadcast = *((struct in_addr *)(rta+1));
+                       else if (rta->rta_type == IFA_LABEL)
+                         label = RTA_DATA(rta);
                        
                        rta = RTA_NEXT(rta, len1);
                      }
                    
-                   if (addr.s_addr)
-                     if (!((*callback)(addr, ifa->ifa_index, netmask, broadcast, parm)))
-                       return 0;
+                   if (addr.s_addr && callback_ok)
+                     if (!((*callback)(addr, ifa->ifa_index, label,  netmask, broadcast, parm)))
+                       callback_ok = 0;
                  }
 #ifdef HAVE_IPV6
                else if (ifa->ifa_family == AF_INET6)
                  {
                    struct in6_addr *addrp = NULL;
+                   u32 valid = 0, preferred = 0;
+                   int flags = 0;
+                   
                    while (RTA_OK(rta, len1))
                      {
                        if (rta->rta_type == IFA_ADDRESS)
                          addrp = ((struct in6_addr *)(rta+1)); 
-                       
+                       else if (rta->rta_type == IFA_CACHEINFO)
+                         {
+                           struct ifa_cacheinfo *ifc = (struct ifa_cacheinfo *)(rta+1);
+                           preferred = ifc->ifa_prefered;
+                           valid = ifc->ifa_valid;
+                         }
                        rta = RTA_NEXT(rta, len1);
                      }
                    
-                   if (addrp)
-                     if (!((*callback)(addrp, ifa->ifa_index, ifa->ifa_index, parm)))
-                       return 0;
+                   if (ifa->ifa_flags & IFA_F_TENTATIVE)
+                     flags |= IFACE_TENTATIVE;
+                   
+                   if (ifa->ifa_flags & IFA_F_DEPRECATED)
+                     flags |= IFACE_DEPRECATED;
+                   
+                   if (!(ifa->ifa_flags & IFA_F_TEMPORARY))
+                     flags |= IFACE_PERMANENT;
+                   
+                   if (addrp && callback_ok)
+                     if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope), 
+                                       (int)(ifa->ifa_index), flags, 
+                                       (int) preferred, (int)valid, parm)))
+                       callback_ok = 0;
                  }
 #endif
              }
@@ -249,10 +288,35 @@ int iface_enumerate(int family, void *parm, int (*callback)())
                rta = RTA_NEXT(rta, len1);
              }
 
-           if (inaddr && mac)
+           if (inaddr && mac && callback_ok)
              if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm)))
-               return 0;
-         }     
+               callback_ok = 0;
+         }
+#ifdef HAVE_DHCP6
+       else if (h->nlmsg_type == RTM_NEWLINK && family == AF_LOCAL)
+         {
+           struct ifinfomsg *link =  NLMSG_DATA(h);
+           struct rtattr *rta = IFLA_RTA(link);
+           unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*link));
+           char *mac = NULL;
+           size_t maclen = 0;
+
+           while (RTA_OK(rta, len1))
+             {
+               if (rta->rta_type == IFLA_ADDRESS)
+                 {
+                   maclen = rta->rta_len - sizeof(struct rtattr);
+                   mac = (char *)(rta+1);
+                 }
+               
+               rta = RTA_NEXT(rta, len1);
+             }
+
+           if (mac && callback_ok && !((link->ifi_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) && 
+               !((*callback)((int)link->ifi_index, (unsigned int)link->ifi_type, mac, maclen, parm)))
+             callback_ok = 0;
+         }
+#endif
     }
 }
 
@@ -268,59 +332,36 @@ void netlink_multicast(void)
     return;
   
   if ((len = netlink_recv()) != -1)
-    {
-      for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
-       if (h->nlmsg_type == NLMSG_ERROR)
-         nl_err(h);
-       else
-         nl_routechange(h);
-    }
-
-  /* restore non-blocking status */
-  fcntl(daemon->netlinkfd, F_SETFL, flags); 
-}
-
-static void nl_err(struct nlmsghdr *h)
-{
-  struct nlmsgerr *err = NLMSG_DATA(h);
+    for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
+      nl_async(h);
   
-  if (err->error != 0)
-    my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
+  /* restore non-blocking status */
+  fcntl(daemon->netlinkfd, F_SETFL, flags);
 }
 
-/* We arrange to receive netlink multicast messages whenever the network route is added.
-   If this happens and we still have a DNS packet in the buffer, we re-send it.
-   This helps on DoD links, where frequently the packet which triggers dialling is
-   a DNS query, which then gets lost. By re-sending, we can avoid the lookup
-   failing. Note that we only accept these messages from the kernel (pid == 0) */ 
-static void nl_routechange(struct nlmsghdr *h)
+static void nl_async(struct nlmsghdr *h)
 {
-  if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
+  if (h->nlmsg_type == NLMSG_ERROR)
+    {
+      struct nlmsgerr *err = NLMSG_DATA(h);
+      if (err->error != 0)
+       my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
+    }
+  else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE) 
     {
+      /* We arrange to receive netlink multicast messages whenever the network route is added.
+        If this happens and we still have a DNS packet in the buffer, we re-send it.
+        This helps on DoD links, where frequently the packet which triggers dialling is
+        a DNS query, which then gets lost. By re-sending, we can avoid the lookup
+        failing. */ 
       struct rtmsg *rtm = NLMSG_DATA(h);
-      int fd;
-
-      if (rtm->rtm_type != RTN_UNICAST || rtm->rtm_scope != RT_SCOPE_LINK)
-       return;
-
-      /* Force re-reading resolv file right now, for luck. */
-      daemon->last_resolv = 0;
       
-      if (daemon->srv_save)
-       {
-         if (daemon->srv_save->sfd)
-           fd = daemon->srv_save->sfd->fd;
-         else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
-           fd = daemon->rfd_save->fd;
-         else
-           return;
-         
-         while(sendto(fd, daemon->packet, daemon->packet_len, 0,
-                      &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send()); 
-       }
+      if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK)
+       queue_event(EVENT_NEWROUTE);
     }
+  else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) 
+    queue_event(EVENT_NEWADDR);
 }
-
 #endif
 
       
index 7b2e905..a1d90c8 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
@@ -99,6 +99,8 @@ int indextoname(int fd, int index, char *name)
 
 int indextoname(int fd, int index, char *name)
 { 
+  (void)fd;
+
   if (index == 0 || !if_indextoname(index, name))
     return 0;
 
@@ -107,187 +109,388 @@ int indextoname(int fd, int index, char *name)
 
 #endif
 
-int iface_check(int family, struct all_addr *addr, char *name, int *indexp)
+int iface_check(int family, struct all_addr *addr, char *name, int *auth)
 {
   struct iname *tmp;
-  int ret = 1;
+  int ret = 1, match_addr = 0;
 
   /* Note: have to check all and not bail out early, so that we set the
-     "used" flags. */
+     "used" flags.
+
+     May be called with family == AF_LOCALto check interface by name only. */
   
-  if (daemon->if_names || (addr && daemon->if_addrs))
+  if (auth)
+    *auth = 0;
+  
+  if (daemon->if_names || daemon->if_addrs)
     {
-#ifdef HAVE_DHCP
-      struct dhcp_context *range;
-#endif
-
       ret = 0;
 
-#ifdef HAVE_DHCP
-      for (range = daemon->dhcp; range; range = range->next)
-       if (range->interface && strcmp(range->interface, name) == 0)
-         ret = 1;
-#endif
-
       for (tmp = daemon->if_names; tmp; tmp = tmp->next)
-       if (tmp->name && (strcmp(tmp->name, name) == 0))
+       if (tmp->name && wildcard_match(tmp->name, name))
          ret = tmp->used = 1;
                
-      for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
-       if (addr && tmp->addr.sa.sa_family == family)
-         {
-           if (family == AF_INET &&
-               tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
-             ret = tmp->used = 1;
+      if (addr)
+       for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
+         if (tmp->addr.sa.sa_family == family)
+           {
+             if (family == AF_INET &&
+                 tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
+               ret = match_addr = tmp->used = 1;
 #ifdef HAVE_IPV6
-           else if (family == AF_INET6 &&
-                    IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, 
-                                       &addr->addr.addr6))
-             ret = tmp->used = 1;
+             else if (family == AF_INET6 &&
+                      IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, 
+                                         &addr->addr.addr6))
+               ret = match_addr = tmp->used = 1;
 #endif
-         }          
+           }          
     }
   
-  for (tmp = daemon->if_except; tmp; tmp = tmp->next)
-    if (tmp->name && (strcmp(tmp->name, name) == 0))
-      ret = 0;
-  
-  if (indexp)
+  if (!match_addr)
+    for (tmp = daemon->if_except; tmp; tmp = tmp->next)
+      if (tmp->name && wildcard_match(tmp->name, name))
+       ret = 0;
+    
+
+  for (tmp = daemon->authinterface; tmp; tmp = tmp->next)
+    if (tmp->name)
+      {
+       if (strcmp(tmp->name, name) == 0 &&
+           (tmp->addr.sa.sa_family == 0 || tmp->addr.sa.sa_family == family))
+         break;
+      }
+    else if (addr && tmp->addr.sa.sa_family == AF_INET && family == AF_INET &&
+            tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
+      break;
+#ifdef HAVE_IPV6
+    else if (addr && tmp->addr.sa.sa_family == AF_INET6 && family == AF_INET6 &&
+            IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr.addr6))
+      break;
+#endif      
+
+  if (tmp && auth) 
     {
-      /* 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 */
-      struct dhcp_bridge *bridge, *alias;
-      for (bridge = daemon->bridges; bridge; bridge = bridge->next)
-       {
-         for (alias = bridge->alias; alias; alias = alias->next)
-           if (strncmp(name, alias->iface, IF_NAMESIZE) == 0)
+      *auth = 1;
+      ret = 1;
+    }
+
+  return ret; 
+}
+
+
+/* Fix for problem that the kernel sometimes reports the loopback inerface as the
+   arrival interface when a packet originates locally, even when sent to address of 
+   an interface other than the loopback. Accept packet if it arrived via a loopback 
+   interface, even when we're not accepting packets that way, as long as the destination
+   address is one we're believing. Interface list must be up-to-date before calling. */
+int loopback_exception(int fd, int family, struct all_addr *addr, char *name)    
+{
+  struct ifreq ifr;
+  struct irec *iface;
+
+  strncpy(ifr.ifr_name, name, IF_NAMESIZE);
+  if (ioctl(fd, SIOCGIFFLAGS, &ifr) != -1 &&
+      ifr.ifr_flags & IFF_LOOPBACK)
+    {
+      for (iface = daemon->interfaces; iface; iface = iface->next)
+       if (iface->addr.sa.sa_family == family)
+         {
+           if (family == AF_INET)
              {
-               int newindex;
-               
-               if (!(newindex = if_nametoindex(bridge->iface)))
-                 {
-                   my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), name);
-                   return 0;
-                 }
-               else 
-                 {
-                   *indexp = newindex;
-                   strncpy(name,  bridge->iface, IF_NAMESIZE);
-                   break;
-                 }
+               if (iface->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
+                 return 1;
              }
-         if (alias)
-           break;
-       }
+#ifdef HAVE_IPV6
+           else if (IN6_ARE_ADDR_EQUAL(&iface->addr.in6.sin6_addr, &addr->addr.addr6))
+             return 1;
+#endif
+           
+         }
     }
-  
-  return ret; 
+  return 0;
 }
-      
-static int iface_allowed(struct irec **irecp, int if_index, 
-                        union mysockaddr *addr, struct in_addr netmask) 
+
+/* If we're configured with something like --interface=eth0:0 then we'll listen correctly
+   on the relevant address, but the name of the arrival interface, derived from the
+   index won't match the config. Check that we found an interface address for the arrival 
+   interface: daemon->interfaces must be up-to-date. */
+int label_exception(int index, int family, struct all_addr *addr)
 {
   struct irec *iface;
-  int fd, mtu = 0, loopback;
+
+  /* labels only supported on IPv4 addresses. */
+  if (family != AF_INET)
+    return 0;
+
+  for (iface = daemon->interfaces; iface; iface = iface->next)
+    if (iface->index == index && iface->addr.sa.sa_family == AF_INET &&
+       iface->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
+      return 1;
+
+  return 0;
+}
+
+struct iface_param {
+  struct addrlist *spare;
+  int fd;
+};
+
+static int iface_allowed(struct iface_param *param, int if_index, char *label,
+                        union mysockaddr *addr, struct in_addr netmask, int prefixlen, int iface_flags) 
+{
+  struct irec *iface;
+  int mtu = 0, loopback;
   struct ifreq ifr;
-  int tftp_ok = daemon->tftp_unlimited;
-#ifdef HAVE_DHCP
+  int tftp_ok = !!option_bool(OPT_TFTP);
+  int dhcp_ok = 1;
+  int auth_dns = 0;
+#if defined(HAVE_DHCP) || defined(HAVE_TFTP)
   struct iname *tmp;
 #endif
-  struct interface_list *ir = NULL;
 
-  /* check whether the interface IP has been added already 
-     we call this routine multiple times. */
-  for (iface = *irecp; iface; iface = iface->next) 
-    if (sockaddr_isequal(&iface->addr, addr))
-      return 1;
+  (void)prefixlen;
+
+  if (!indextoname(param->fd, if_index, ifr.ifr_name) ||
+      ioctl(param->fd, SIOCGIFFLAGS, &ifr) == -1)
+    return 0;
+   
+  loopback = ifr.ifr_flags & IFF_LOOPBACK;
+  
+  if (loopback)
+    dhcp_ok = 0;
   
-  if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1 ||
-      !indextoname(fd, if_index, ifr.ifr_name) ||
-      ioctl(fd, SIOCGIFFLAGS, &ifr) == -1)
+  if (ioctl(param->fd, SIOCGIFMTU, &ifr) != -1)
+    mtu = ifr.ifr_mtu;
+  
+  if (!label)
+    label = ifr.ifr_name;
+  /* maintain a list of all addresses on all interfaces for --local-service option */
+  if (option_bool(OPT_LOCAL_SERVICE))
     {
-      if (fd != -1)
+      struct addrlist *al;
+
+      if (param->spare)
        {
-         int errsave = errno;
-         close(fd);
-         errno = errsave;
+         al = param->spare;
+         param->spare = al->next;
+       }
+      else
+       al = whine_malloc(sizeof(struct addrlist));
+      
+      if (al)
+       {
+         al->next = daemon->interface_addrs;
+         daemon->interface_addrs = al;
+         al->prefixlen = prefixlen;
+         
+         if (addr->sa.sa_family == AF_INET)
+           {
+             al->addr.addr.addr4 = addr->in.sin_addr;
+             al->flags = 0;
+           }
+#ifdef HAVE_IPV6
+         else
+           {
+             al->addr.addr.addr6 = addr->in6.sin6_addr;
+             al->flags = ADDRLIST_IPV6;
+           } 
+#endif
        }
-      return 0;
     }
-   
-  loopback = ifr.ifr_flags & IFF_LOOPBACK;
-
-  if (ioctl(fd, SIOCGIFMTU, &ifr) != -1)
-    mtu = ifr.ifr_mtu;
-  
-  close(fd);
   
-  /* If we are restricting the set of interfaces to use, make
+#ifdef HAVE_IPV6
+  if (addr->sa.sa_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr))
+#endif
+    {
+      struct interface_name *int_name;
+      struct addrlist *al;
+#ifdef HAVE_AUTH
+      struct auth_zone *zone;
+      struct auth_name_list *name;
+
+      /* Find subnets in auth_zones */
+      for (zone = daemon->auth_zones; zone; zone = zone->next)
+       for (name = zone->interface_names; name; name = name->next)
+         if (wildcard_match(name->name, label))
+           {
+             if (addr->sa.sa_family == AF_INET && (name->flags & AUTH4))
+               {
+                 if (param->spare)
+                   {
+                     al = param->spare;
+                     param->spare = al->next;
+                   }
+                 else
+                   al = whine_malloc(sizeof(struct addrlist));
+                 
+                 if (al)
+                   {
+                     al->next = zone->subnet;
+                     zone->subnet = al;
+                     al->prefixlen = prefixlen;
+                     al->addr.addr.addr4 = addr->in.sin_addr;
+                     al->flags = 0;
+                   }
+               }
+             
+#ifdef HAVE_IPV6
+             if (addr->sa.sa_family == AF_INET6 && (name->flags & AUTH6))
+               {
+                 if (param->spare)
+                   {
+                     al = param->spare;
+                     param->spare = al->next;
+                   }
+                 else
+                   al = whine_malloc(sizeof(struct addrlist));
+                 
+                 if (al)
+                   {
+                     al->next = zone->subnet;
+                     zone->subnet = al;
+                     al->prefixlen = prefixlen;
+                     al->addr.addr.addr6 = addr->in6.sin6_addr;
+                     al->flags = ADDRLIST_IPV6;
+                   }
+               } 
+#endif
+             
+           }
+#endif
+       
+      /* Update addresses from interface_names. These are a set independent
+        of the set we're listening on. */  
+      for (int_name = daemon->int_names; int_name; int_name = int_name->next)
+       if (strncmp(label, int_name->intr, IF_NAMESIZE) == 0 && 
+           (addr->sa.sa_family == int_name->family || int_name->family == 0))
+         {
+           if (param->spare)
+             {
+               al = param->spare;
+               param->spare = al->next;
+             }
+           else
+             al = whine_malloc(sizeof(struct addrlist));
+           
+           if (al)
+             {
+               al->next = int_name->addr;
+               int_name->addr = al;
+               
+               if (addr->sa.sa_family == AF_INET)
+                 {
+                   al->addr.addr.addr4 = addr->in.sin_addr;
+                   al->flags = 0;
+                 }
+#ifdef HAVE_IPV6
+               else
+                {
+                   al->addr.addr.addr6 = addr->in6.sin6_addr;
+                   al->flags = ADDRLIST_IPV6;
+                   /* Privacy addresses and addresses still undergoing DAD and deprecated addresses
+                      don't appear in forward queries, but will in reverse ones. */
+                   if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE)))
+                     al->flags |= ADDRLIST_REVONLY;
+                } 
+#endif
+             }
+         }
+    }
+  /* check whether the interface IP has been added already 
+     we call this routine multiple times. */
+  for (iface = daemon->interfaces; iface; iface = iface->next) 
+    if (sockaddr_isequal(&iface->addr, addr))
+      {
+       iface->dad = !!(iface_flags & IFACE_TENTATIVE);
+       iface->found = 1; /* for garbage collection */
+       return 1;
+      }
+
+ /* If we are restricting the set of interfaces to use, make
      sure that loopback interfaces are in that set. */
   if (daemon->if_names && loopback)
     {
       struct iname *lo;
       for (lo = daemon->if_names; lo; lo = lo->next)
        if (lo->name && strcmp(lo->name, ifr.ifr_name) == 0)
-         {
-           lo->isloop = 1;
-           break;
-         }
+         break;
       
-      if (!lo && 
-         (lo = whine_malloc(sizeof(struct iname))) &&
-         (lo->name = whine_malloc(strlen(ifr.ifr_name)+1)))
+      if (!lo && (lo = whine_malloc(sizeof(struct iname)))) 
        {
-         strcpy(lo->name, ifr.ifr_name);
-         lo->isloop = lo->used = 1;
-         lo->next = daemon->if_names;
-         daemon->if_names = lo;
+         if ((lo->name = whine_malloc(strlen(ifr.ifr_name)+1)))
+           {
+             strcpy(lo->name, ifr.ifr_name);
+             lo->used = 1;
+             lo->next = daemon->if_names;
+             daemon->if_names = lo;
+           }
+         else
+           free(lo);
        }
     }
   
-#ifdef HAVE_TFTP
-  /* implement wierd TFTP service rules */
-  for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
-    if (strcmp(ir->interface, ifr.ifr_name) == 0)
-      {
-       tftp_ok = 1;
-       break;
-      }
+  if (addr->sa.sa_family == AF_INET &&
+      !iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, label, &auth_dns))
+    return 1;
+
+#ifdef HAVE_IPV6
+  if (addr->sa.sa_family == AF_INET6 &&
+      !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, label, &auth_dns))
+    return 1;
 #endif
-  
-  if (!ir)
-    {
-      if (addr->sa.sa_family == AF_INET &&
-         !iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name, NULL))
-       return 1;
-      
+    
 #ifdef HAVE_DHCP
-      for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
-       if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
+  /* No DHCP where we're doing auth DNS. */
+  if (auth_dns)
+    {
+      tftp_ok = 0;
+      dhcp_ok = 0;
+    }
+  else
+    for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
+      if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
+       {
          tftp_ok = 0;
+         dhcp_ok = 0;
+       }
 #endif
-      
-#ifdef HAVE_IPV6
-      if (addr->sa.sa_family == AF_INET6 &&
-         !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, NULL))
-       return 1;
-#endif
+  
+#ifdef HAVE_TFTP
+  if (daemon->tftp_interfaces)
+    {
+      /* dedicated tftp interface list */
+      tftp_ok = 0;
+      for (tmp = daemon->tftp_interfaces; tmp; tmp = tmp->next)
+       if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
+         tftp_ok = 1;
     }
-
+#endif
+  
   /* add to list */
   if ((iface = whine_malloc(sizeof(struct irec))))
     {
       iface->addr = *addr;
       iface->netmask = netmask;
       iface->tftp_ok = tftp_ok;
+      iface->dhcp_ok = dhcp_ok;
+      iface->dns_auth = auth_dns;
       iface->mtu = mtu;
+      iface->dad = !!(iface_flags & IFACE_TENTATIVE);
+      iface->found = 1;
+      iface->done = iface->multicast_done = iface->warned = 0;
+      iface->index = if_index;
       if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1)))
-       strcpy(iface->name, ifr.ifr_name);
-      iface->next = *irecp;
-      *irecp = iface;
-      return 1;
+       {
+         strcpy(iface->name, ifr.ifr_name);
+         iface->next = daemon->interfaces;
+         daemon->interfaces = iface;
+         return 1;
+       }
+      free(iface);
+
     }
   
   errno = ENOMEM; 
@@ -295,13 +498,17 @@ static int iface_allowed(struct irec **irecp, int if_index,
 }
 
 #ifdef HAVE_IPV6
-static int iface_allowed_v6(struct in6_addr *local, 
-                           int scope, int if_index, void *vparam)
+static int iface_allowed_v6(struct in6_addr *local, int prefix, 
+                           int scope, int if_index, int flags, 
+                           int preferred, int valid, void *vparam)
 {
   union mysockaddr addr;
   struct in_addr netmask; /* dummy */
-  
   netmask.s_addr = 0;
+
+  (void)scope; /* warning */
+  (void)preferred;
+  (void)valid;
   
   memset(&addr, 0, sizeof(addr));
 #ifdef HAVE_SOCKADDR_SA_LEN
@@ -310,16 +517,21 @@ static int iface_allowed_v6(struct in6_addr *local,
   addr.in6.sin6_family = AF_INET6;
   addr.in6.sin6_addr = *local;
   addr.in6.sin6_port = htons(daemon->port);
-  addr.in6.sin6_scope_id = scope;
+  /* FreeBSD insists this is zero for non-linklocal addresses */
+  if (IN6_IS_ADDR_LINKLOCAL(local))
+    addr.in6.sin6_scope_id = if_index;
+  else
+    addr.in6.sin6_scope_id = 0;
   
-  return iface_allowed((struct irec **)vparam, if_index, &addr, netmask);
+  return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, prefix, flags);
 }
 #endif
 
-static int iface_allowed_v4(struct in_addr local, int if_index, 
+static int iface_allowed_v4(struct in_addr local, int if_index, char *label,
                            struct in_addr netmask, struct in_addr broadcast, void *vparam)
 {
   union mysockaddr addr;
+  int prefix, bit;
 
   memset(&addr, 0, sizeof(addr));
 #ifdef HAVE_SOCKADDR_SA_LEN
@@ -330,17 +542,141 @@ static int iface_allowed_v4(struct in_addr local, int if_index,
   addr.in.sin_addr = local;
   addr.in.sin_port = htons(daemon->port);
 
-  return iface_allowed((struct irec **)vparam, if_index, &addr, netmask);
+  /* determine prefix length from netmask */
+  for (prefix = 32, bit = 1; (bit & ntohl(netmask.s_addr)) == 0 && prefix != 0; bit = bit << 1, prefix--);
+
+  return iface_allowed((struct iface_param *)vparam, if_index, label, &addr, netmask, prefix, 0);
 }
    
-int enumerate_interfaces(void)
+int enumerate_interfaces(int reset)
 {
+  static struct addrlist *spare = NULL;
+  static int done = 0;
+  struct iface_param param;
+  int errsave, ret = 1;
+  struct addrlist *addr, *tmp;
+  struct interface_name *intname;
+  struct irec *iface;
+#ifdef HAVE_AUTH
+  struct auth_zone *zone;
+#endif
+
+  /* Do this max once per select cycle  - also inhibits netlink socket use
+   in TCP child processes. */
+
+  if (reset)
+    {
+      done = 0;
+      return 1;
+    }
+
+  if (done)
+    return 1;
+
+  done = 1;
+
+  if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
+    return 0;
+  /* Mark interfaces for garbage collection */
+  for (iface = daemon->interfaces; iface; iface = iface->next) 
+    iface->found = 0;
+
+  /* remove addresses stored against interface_names */
+  for (intname = daemon->int_names; intname; intname = intname->next)
+    {
+      for (addr = intname->addr; addr; addr = tmp)
+       {
+         tmp = addr->next;
+         addr->next = spare;
+         spare = addr;
+       }
+      
+      intname->addr = NULL;
+    }
+
+  /* Remove list of addresses of local interfaces */
+  for (addr = daemon->interface_addrs; addr; addr = tmp)
+    {
+      tmp = addr->next;
+      addr->next = spare;
+      spare = addr;
+    }
+  daemon->interface_addrs = NULL;
+  
+#ifdef HAVE_AUTH
+  /* remove addresses stored against auth_zone subnets, but not 
+   ones configured as address literals */
+  for (zone = daemon->auth_zones; zone; zone = zone->next)
+    if (zone->interface_names)
+      {
+       struct addrlist **up;
+       for (up = &zone->subnet, addr = zone->subnet; addr; addr = tmp)
+         {
+           tmp = addr->next;
+           if (addr->flags & ADDRLIST_LITERAL)
+             up = &addr->next;
+           else
+             {
+               *up = addr->next;
+               addr->next = spare;
+               spare = addr;
+             }
+         }
+      }
+#endif
+
+  param.spare = spare;
+  
 #ifdef HAVE_IPV6
-  if (!iface_enumerate(AF_INET6, &daemon->interfaces, iface_allowed_v6))
-    return 0; 
+  ret = iface_enumerate(AF_INET6, &param, iface_allowed_v6);
 #endif
 
-  return iface_enumerate(AF_INET, &daemon->interfaces, iface_allowed_v4); 
+  if (ret)
+    ret = iface_enumerate(AF_INET, &param, iface_allowed_v4); 
+  errsave = errno;
+  close(param.fd);
+  
+  if (option_bool(OPT_CLEVERBIND))
+    { 
+      /* Garbage-collect listeners listening on addresses that no longer exist.
+        Does nothing when not binding interfaces or for listeners on localhost, 
+        since the ->iface field is NULL. Note that this needs the protections
+        against re-entrancy, hence it's here.  It also means there's a possibility,
+        in OPT_CLEVERBIND mode, that at listener will just disappear after
+        a call to enumerate_interfaces, this is checked OK on all calls. */
+      struct listener *l, *tmp, **up;
+      
+      for (up = &daemon->listeners, l = daemon->listeners; l; l = tmp)
+       {
+         tmp = l->next;
+         
+         if (!l->iface || l->iface->found)
+           up = &l->next;
+         else
+           {
+             *up = l->next;
+             
+             /* In case it ever returns */
+             l->iface->done = 0;
+             
+             if (l->fd != -1)
+               close(l->fd);
+             if (l->tcpfd != -1)
+               close(l->tcpfd);
+             if (l->tftpfd != -1)
+               close(l->tftpfd);
+             
+             free(l);
+           }
+       }
+    }
+  
+  errno = errsave;
+  spare = param.spare;
+    
+  return ret;
 }
 
 /* set NONBLOCK bit on fd: See Stevens 16.6 */
@@ -355,17 +691,15 @@ int fix_fd(int fd)
   return 1;
 }
 
-static int make_sock(union mysockaddr *addr, int type)
+static int make_sock(union mysockaddr *addr, int type, int dienow)
 {
   int family = addr->sa.sa_family;
   int fd, rc, opt = 1;
-#ifdef HAVE_IPV6
-  static int dad_count = 0;
-#endif
   
   if ((fd = socket(family, type, 0)) == -1)
     {
-      int port;
+      int port, errsav;
+      char *s;
 
       /* No error if the kernel just doesn't support this IP flavour */
       if (errno == EPROTONOSUPPORT ||
@@ -374,42 +708,39 @@ static int make_sock(union mysockaddr *addr, int type)
        return -1;
       
     err:
-      port = prettyprint_addr(addr, daemon->namebuff);
-      if (!option_bool(OPT_NOWILD))
-       sprintf(daemon->namebuff, "port %d", port);
-      die(_("failed to create listening socket for %s: %s"), 
-         daemon->namebuff, EC_BADNET);
-    }  
+      errsav = errno;
+      port = prettyprint_addr(addr, daemon->addrbuff);
+      if (!option_bool(OPT_NOWILD) && !option_bool(OPT_CLEVERBIND))
+       sprintf(daemon->addrbuff, "port %d", port);
+      s = _("failed to create listening socket for %s: %s");
+      
+      if (fd != -1)
+       close (fd);
+       
+      errno = errsav;
 
+      if (dienow)
+       {
+         /* failure to bind addresses given by --listen-address at this point
+            is OK if we're doing bind-dynamic */
+         if (!option_bool(OPT_CLEVERBIND))
+           die(s, daemon->addrbuff, EC_BADNET);
+       }
+      else
+       my_syslog(LOG_WARNING, s, daemon->addrbuff, strerror(errno));
+      
+      return -1;
+    }  
+  
   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || !fix_fd(fd))
     goto err;
   
 #ifdef HAVE_IPV6
-  if (family == AF_INET6 && setsockopt(fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
+  if (family == AF_INET6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
     goto err;
 #endif
   
-  while (1)
-    {
-      if ((rc = bind(fd, (struct sockaddr *)addr, sa_len(addr))) != -1)
-       break;
-      
-#ifdef HAVE_IPV6
-      /* An interface may have an IPv6 address which is still undergoing DAD. 
-        If so, the bind will fail until the DAD completes, so we try over 20 seconds
-        before failing. */
-      if (family == AF_INET6 && 
-         (errno == ENODEV || errno == EADDRNOTAVAIL) && 
-         dad_count++ < DAD_WAIT)
-       {
-         sleep(1);
-         continue;
-       }
-#endif
-      break;
-    }
-  
-  if (rc == -1)
+  if ((rc = bind(fd, (struct sockaddr *)addr, sa_len(addr))) == -1)
     goto err;
   
   if (type == SOCK_STREAM)
@@ -417,12 +748,12 @@ static int make_sock(union mysockaddr *addr, int type)
       if (listen(fd, 5) == -1)
        goto err;
     }
-  else if (!option_bool(OPT_NOWILD))
+  else if (family == AF_INET)
     {
-      if (family == AF_INET)
+      if (!option_bool(OPT_NOWILD))
        {
 #if defined(HAVE_LINUX_NETWORK) 
-         if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1)
+         if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1)
            goto err;
 #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
          if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
@@ -430,47 +761,126 @@ static int make_sock(union mysockaddr *addr, int type)
            goto err;
 #endif
        }
+    }
 #ifdef HAVE_IPV6
-      else
-       {
-         /* The API changed around Linux 2.6.14 but the old ABI is still supported:
-            handle all combinations of headers and kernel.
-            OpenWrt note that this fixes the problem addressed by your very broken patch. */
-         daemon->v6pktinfo = IPV6_PKTINFO;
-         
-#  ifdef IPV6_RECVPKTINFO
-#    ifdef IPV6_2292PKTINFO
-         if (setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1)
-           {
-             if (errno == ENOPROTOOPT && setsockopt(fd, IPV6_LEVEL, IPV6_2292PKTINFO, &opt, sizeof(opt)) != -1)
-               daemon->v6pktinfo = IPV6_2292PKTINFO;
-             else
-               goto err;
-           }
-#    else
-         if (setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1)
-           goto err;
-#    endif 
-#  else
-         if (setsockopt(fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1)
-           goto err;
-#  endif
-       }
+  else if (!set_ipv6pktinfo(fd))
+    goto err;
 #endif
+  
+  return fd;
+}
+
+#ifdef HAVE_IPV6  
+int set_ipv6pktinfo(int fd)
+{
+  int opt = 1;
+
+  /* The API changed around Linux 2.6.14 but the old ABI is still supported:
+     handle all combinations of headers and kernel.
+     OpenWrt note that this fixes the problem addressed by your very broken patch. */
+  daemon->v6pktinfo = IPV6_PKTINFO;
+  
+#ifdef IPV6_RECVPKTINFO
+  if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &opt, sizeof(opt)) != -1)
+    return 1;
+# ifdef IPV6_2292PKTINFO
+  else if (errno == ENOPROTOOPT && setsockopt(fd, IPPROTO_IPV6, IPV6_2292PKTINFO, &opt, sizeof(opt)) != -1)
+    {
+      daemon->v6pktinfo = IPV6_2292PKTINFO;
+      return 1;
     }
+# endif 
+#else
+  if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &opt, sizeof(opt)) != -1)
+    return 1;
+#endif
 
-  return fd;
+  return 0;
+}
+#endif
+
+
+/* Find the interface on which a TCP connection arrived, if possible, or zero otherwise. */
+int tcp_interface(int fd, int af)
+{ 
+  int if_index = 0;
+
+#ifdef HAVE_LINUX_NETWORK
+  int opt = 1;
+  struct cmsghdr *cmptr;
+  struct msghdr msg;
+  
+  /* use mshdr do that the CMSDG_* macros are available */
+  msg.msg_control = daemon->packet;
+  msg.msg_controllen = daemon->packet_buff_sz;
+  
+  /* we overwrote the buffer... */
+  daemon->srv_save = NULL;
+  
+  if (af == AF_INET)
+    {
+      if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 &&
+         getsockopt(fd, IPPROTO_IP, IP_PKTOPTIONS, msg.msg_control, (socklen_t *)&msg.msg_controllen) != -1)
+       for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+         if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
+            {
+              union {
+                unsigned char *c;
+                struct in_pktinfo *p;
+              } p;
+             
+             p.c = CMSG_DATA(cmptr);
+             if_index = p.p->ipi_ifindex;
+           }
+    }
+#ifdef HAVE_IPV6
+  else
+    {
+      /* Only the RFC-2292 API has the ability to find the interface for TCP connections,
+        it was removed in RFC-3542 !!!! 
+
+        Fortunately, Linux kept the 2292 ABI when it moved to 3542. The following code always
+        uses the old ABI, and should work with pre- and post-3542 kernel headers */
+
+#ifdef IPV6_2292PKTOPTIONS   
+#  define PKTOPTIONS IPV6_2292PKTOPTIONS
+#else
+#  define PKTOPTIONS IPV6_PKTOPTIONS
+#endif
+
+      if (set_ipv6pktinfo(fd) &&
+         getsockopt(fd, IPPROTO_IPV6, PKTOPTIONS, msg.msg_control, (socklen_t *)&msg.msg_controllen) != -1)
+       {
+          for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+            if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
+              {
+                union {
+                  unsigned char *c;
+                  struct in6_pktinfo *p;
+                } p;
+                p.c = CMSG_DATA(cmptr);
+               
+               if_index = p.p->ipi6_ifindex;
+              }
+       }
+    }
+#endif /* IPV6 */
+#endif /* Linux */
+  return if_index;
 }
       
-static struct listener *create_listeners(union mysockaddr *addr, int do_tftp)
+static struct listener *create_listeners(union mysockaddr *addr, int do_tftp, int dienow)
 {
   struct listener *l = NULL;
   int fd = -1, tcpfd = -1, tftpfd = -1;
 
+  (void)do_tftp;
+
   if (daemon->port != 0)
     {
-      fd = make_sock(addr, SOCK_DGRAM);
-      tcpfd = make_sock(addr, SOCK_STREAM);
+      fd = make_sock(addr, SOCK_DGRAM, dienow);
+      tcpfd = make_sock(addr, SOCK_STREAM, dienow);
     }
   
 #ifdef HAVE_TFTP
@@ -481,7 +891,7 @@ static struct listener *create_listeners(union mysockaddr *addr, int do_tftp)
          /* port must be restored to DNS port for TCP code */
          short save = addr->in.sin_port;
          addr->in.sin_port = htons(TFTP_PORT);
-         tftpfd = make_sock(addr, SOCK_DGRAM);
+         tftpfd = make_sock(addr, SOCK_DGRAM, dienow);
          addr->in.sin_port = save;
        }
 #  ifdef HAVE_IPV6
@@ -489,7 +899,7 @@ static struct listener *create_listeners(union mysockaddr *addr, int do_tftp)
        {
          short save = addr->in6.sin6_port;
          addr->in6.sin6_port = htons(TFTP_PORT);
-         tftpfd = make_sock(addr, SOCK_DGRAM);
+         tftpfd = make_sock(addr, SOCK_DGRAM, dienow);
          addr->in6.sin6_port = save;
        }  
 #  endif
@@ -503,17 +913,17 @@ static struct listener *create_listeners(union mysockaddr *addr, int do_tftp)
       l->family = addr->sa.sa_family;
       l->fd = fd;
       l->tcpfd = tcpfd;
-      l->tftpfd = tftpfd;
+      l->tftpfd = tftpfd;      
+      l->iface = NULL;
     }
 
   return l;
 }
 
-struct listener *create_wildcard_listeners(void)
+void create_wildcard_listeners(void)
 {
   union mysockaddr addr;
-  struct listener *l;
-  int tftp_enabled = daemon->tftp_unlimited || daemon->tftp_interfaces; 
+  struct listener *l, *l6;
 
   memset(&addr, 0, sizeof(addr));
 #ifdef HAVE_SOCKADDR_SA_LEN
@@ -523,7 +933,7 @@ struct listener *create_wildcard_listeners(void)
   addr.in.sin_addr.s_addr = INADDR_ANY;
   addr.in.sin_port = htons(daemon->port);
 
-  l = create_listeners(&addr, tftp_enabled);
+  l = create_listeners(&addr, !!option_bool(OPT_TFTP), 1);
 
 #ifdef HAVE_IPV6
   memset(&addr, 0, sizeof(addr));
@@ -533,32 +943,165 @@ struct listener *create_wildcard_listeners(void)
   addr.in6.sin6_family = AF_INET6;
   addr.in6.sin6_addr = in6addr_any;
   addr.in6.sin6_port = htons(daemon->port);
-  
+  l6 = create_listeners(&addr, !!option_bool(OPT_TFTP), 1);
   if (l) 
-    l->next = create_listeners(&addr, tftp_enabled);
+    l->next = l6;
   else 
-    l = create_listeners(&addr, tftp_enabled);
+    l = l6;
 #endif
 
-  return l;
+  daemon->listeners = l;
 }
 
-struct listener *create_bound_listeners(void)
+void create_bound_listeners(int dienow)
 {
-  struct listener *new, *listeners = NULL;
+  struct listener *new;
   struct irec *iface;
+  struct iname *if_tmp;
 
   for (iface = daemon->interfaces; iface; iface = iface->next)
-    if ((new = create_listeners(&iface->addr, iface->tftp_ok)))
+    if (!iface->done && !iface->dad && iface->found &&
+       (new = create_listeners(&iface->addr, iface->tftp_ok, dienow)))
       {
        new->iface = iface;
-       new->next = listeners;
-       listeners = new;
+       new->next = daemon->listeners;
+       daemon->listeners = new;
+       iface->done = 1;
+      }
+
+  /* Check for --listen-address options that haven't been used because there's
+     no interface with a matching address. These may be valid: eg it's possible
+     to listen on 127.0.1.1 even if the loopback interface is 127.0.0.1
+
+     If the address isn't valid the bind() will fail and we'll die() 
+     (except in bind-dynamic mode, when we'll complain but keep trying.)
+
+     The resulting listeners have the ->iface field NULL, and this has to be
+     handled by the DNS and TFTP code. It disables --localise-queries processing
+     (no netmask) and some MTU login the tftp code. */
+
+  for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next)
+    if (!if_tmp->used && 
+       (new = create_listeners(&if_tmp->addr, !!option_bool(OPT_TFTP), dienow)))
+      {
+       new->next = daemon->listeners;
+       daemon->listeners = new;
+      }
+}
+
+/* In --bind-interfaces, the only access control is the addresses we're listening on. 
+   There's nothing to avoid a query to the address of an internal interface arriving via
+   an external interface where we don't want to accept queries, except that in the usual 
+   case the addresses of internal interfaces are RFC1918. When bind-interfaces in use, 
+   and we listen on an address that looks like it's probably globally routeable, shout.
+
+   The fix is to use --bind-dynamic, which actually checks the arrival interface too.
+   Tough if your platform doesn't support this.
+
+   Note that checking the arrival interface is supported in the standard IPv6 API and
+   always done, so we don't warn about any IPv6 addresses here.
+*/
+
+void warn_bound_listeners(void)
+{
+  struct irec *iface;  
+  int advice = 0;
+
+  for (iface = daemon->interfaces; iface; iface = iface->next)
+    if (!iface->dns_auth)
+      {
+       if (iface->addr.sa.sa_family == AF_INET)
+         {
+           if (!private_net(iface->addr.in.sin_addr, 1))
+             {
+               inet_ntop(AF_INET, &iface->addr.in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
+               iface->warned = advice = 1;
+               my_syslog(LOG_WARNING, 
+                         _("LOUD WARNING: listening on %s may accept requests via interfaces other than %s"),
+                         daemon->addrbuff, iface->name);
+             }
+         }
       }
   
-  return listeners;
+  if (advice)
+    my_syslog(LOG_WARNING, _("LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)")); 
 }
 
+void warn_int_names(void)
+{
+  struct interface_name *intname;
+  for (intname = daemon->int_names; intname; intname = intname->next)
+    if (!intname->addr)
+      my_syslog(LOG_WARNING, _("warning: no addresses found for interface %s"), intname->intr);
+}
+int is_dad_listeners(void)
+{
+  struct irec *iface;
+  
+  if (option_bool(OPT_NOWILD))
+    for (iface = daemon->interfaces; iface; iface = iface->next)
+      if (iface->dad && !iface->done)
+       return 1;
+  
+  return 0;
+}
+
+#ifdef HAVE_DHCP6
+void join_multicast(int dienow)      
+{
+  struct irec *iface, *tmp;
+
+  for (iface = daemon->interfaces; iface; iface = iface->next)
+    if (iface->addr.sa.sa_family == AF_INET6 && iface->dhcp_ok && !iface->multicast_done)
+      {
+       /* There's an irec per address but we only want to join for multicast 
+          once per interface. Weed out duplicates. */
+       for (tmp = daemon->interfaces; tmp; tmp = tmp->next)
+         if (tmp->multicast_done && tmp->index == iface->index)
+           break;
+       
+       iface->multicast_done = 1;
+       
+       if (!tmp)
+         {
+           struct ipv6_mreq mreq;
+           int err = 0;
+
+           mreq.ipv6mr_interface = iface->index;
+           
+           inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
+           
+           if ((daemon->doing_dhcp6 || daemon->relay6) &&
+               setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
+             err = 1;
+           
+           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;
+           
+           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;
+           
+           if (err)
+             {
+               char *s = _("interface %s failed to join DHCPv6 multicast group: %s");
+               if (dienow)
+                 die(s, iface->name, EC_BADNET);
+               else
+                 my_syslog(LOG_ERR, s, iface->name, strerror(errno));
+             }
+         }
+      }
+}
+#endif
 
 /* return a UDP socket bound to a random port, have to cope with straying into
    occupied port nos and reserved ones. */
@@ -748,87 +1291,206 @@ void pre_allocate_sfds(void)
       }  
 }
 
+void mark_servers(int flag)
+{
+  struct server *serv;
+
+  /* mark everything with argument flag */
+  for (serv = daemon->servers; serv; serv = serv->next)
+    {
+      if (serv->flags & flag)
+       serv->flags |= SERV_MARK;
+#ifdef HAVE_LOOP
+      /* Give looped servers another chance */
+      serv->flags &= ~SERV_LOOP;
+#endif
+    }
+}
+
+void cleanup_servers(void)
+{
+  struct server *serv, *tmp, **up;
+
+  /* unlink and free anything still marked. */
+  for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) 
+    {
+      tmp = serv->next;
+      if (serv->flags & SERV_MARK)
+       {
+         server_gone(serv);
+         *up = serv->next;
+         if (serv->domain)
+          free(serv->domain);
+        free(serv);
+       }
+      else 
+       up = &serv->next;
+    }
+
+#ifdef HAVE_LOOP
+  /* Now we have a new set of servers, test for loops. */
+  loop_send_probes();
+#endif
+}
+
+void add_update_server(int flags,
+                      union mysockaddr *addr,
+                      union mysockaddr *source_addr,
+                      const char *interface,
+                      const char *domain)
+{
+  struct server *serv, *next = NULL;
+  char *domain_str = NULL;
+  
+  /* See if there is a suitable candidate, and unmark */
+  for (serv = daemon->servers; serv; serv = serv->next)
+    if (serv->flags & SERV_MARK)
+      {
+       if (domain)
+         {
+           if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain))
+             continue;
+         }
+       else
+         {
+           if (serv->flags & SERV_HAS_DOMAIN)
+             continue;
+         }
+       
+        break;
+      }
+
+  if (serv)
+    {
+      domain_str = serv->domain;
+      next = serv->next;
+    }
+  else if ((serv = whine_malloc(sizeof (struct server))))
+    {
+      /* Not found, create a new one. */
+      if (domain && !(domain_str = whine_malloc(strlen(domain)+1)))
+       {
+         free(serv);
+          serv = NULL;
+        }
+      else
+        {
+         struct server *s;
+         /* Add to the end of the chain, for order */
+         if (!daemon->servers)
+           daemon->servers = serv;
+         else
+           {
+             for (s = daemon->servers; s->next; s = s->next);
+             s->next = serv;
+           }
+         if (domain)
+           strcpy(domain_str, domain);
+       }
+    }
+  
+  if (serv)
+    {
+      memset(serv, 0, sizeof(struct server));
+      serv->flags = flags;
+      serv->domain = domain_str;
+      serv->next = next;
+      serv->queries = serv->failed_queries = 0;
+      serv->edns_pktsz = daemon->edns_pktsz;
+#ifdef HAVE_LOOP
+      serv->uid = rand32();
+#endif      
+
+      if (domain)
+       serv->flags |= SERV_HAS_DOMAIN;
+      
+      if (interface)
+       strcpy(serv->interface, interface);      
+      if (addr)
+       serv->addr = *addr;
+      if (source_addr)
+       serv->source_addr = *source_addr;
+    }
+}
 
 void check_servers(void)
 {
   struct irec *iface;
-  struct server *new, *tmp, *ret = NULL;
+  struct server *serv;
   int port = 0;
 
   /* interface may be new since startup */
   if (!option_bool(OPT_NOWILD))
-    enumerate_interfaces();
+    enumerate_interfaces(0);
   
-  for (new = daemon->servers; new; new = tmp)
+  for (serv = daemon->servers; serv; serv = serv->next)
     {
-      tmp = new->next;
-      
-      if (!(new->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
+       if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
        {
-         port = prettyprint_addr(&new->addr, daemon->namebuff);
+         port = prettyprint_addr(&serv->addr, daemon->namebuff);
 
          /* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
-         if (new->addr.sa.sa_family == AF_INET &&
-             new->addr.in.sin_addr.s_addr == 0)
+         if (serv->addr.sa.sa_family == AF_INET &&
+             serv->addr.in.sin_addr.s_addr == 0)
            {
-             free(new);
+             serv->flags |= SERV_MARK;
              continue;
            }
 
          for (iface = daemon->interfaces; iface; iface = iface->next)
-           if (sockaddr_isequal(&new->addr, &iface->addr))
+           if (sockaddr_isequal(&serv->addr, &iface->addr))
              break;
          if (iface)
            {
              my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff);
-             free(new);
+             serv->flags |= SERV_MARK;
              continue;
            }
          
          /* Do we need a socket set? */
-         if (!new->sfd && 
-             !(new->sfd = allocate_sfd(&new->source_addr, new->interface)) &&
+         if (!serv->sfd && 
+             !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) &&
              errno != 0)
            {
              my_syslog(LOG_WARNING, 
                        _("ignoring nameserver %s - cannot make/bind socket: %s"),
                        daemon->namebuff, strerror(errno));
-             free(new);
+             serv->flags |= SERV_MARK;
              continue;
            }
        }
       
-      /* reverse order - gets it right. */
-      new->next = ret;
-      ret = new;
-      
-      if (!(new->flags & SERV_NO_REBIND))
+      if (!(serv->flags & SERV_NO_REBIND) && !(serv->flags & SERV_LITERAL_ADDRESS))
        {
-         if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
+         if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
            {
              char *s1, *s2;
-             if (!(new->flags & SERV_HAS_DOMAIN))
+             if (!(serv->flags & SERV_HAS_DOMAIN))
                s1 = _("unqualified"), s2 = _("names");
-             else if (strlen(new->domain) == 0)
+             else if (strlen(serv->domain) == 0)
                s1 = _("default"), s2 = "";
              else
-               s1 = _("domain"), s2 = new->domain;
+               s1 = _("domain"), s2 = serv->domain;
              
-             if (new->flags & SERV_NO_ADDR)
+             if (serv->flags & SERV_NO_ADDR)
                my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
-             else if (new->flags & SERV_USE_RESOLV)
+             else if (serv->flags & SERV_USE_RESOLV)
                my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2);
-             else if (!(new->flags & SERV_LITERAL_ADDRESS))
+             else 
                my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);
            }
-         else if (new->interface[0] != 0)
-           my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, new->interface); 
+#ifdef HAVE_LOOP
+         else if (serv->flags & SERV_LOOP)
+           my_syslog(LOG_INFO, _("NOT using nameserver %s#%d - query loop detected"), daemon->namebuff, port); 
+#endif
+         else if (serv->interface[0] != 0)
+           my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface); 
          else
            my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port); 
        }
     }
-  
-  daemon->servers = ret;
+
+  cleanup_servers();
 }
 
 /* Return zero if no servers found, in that case we keep polling.
@@ -837,9 +1499,6 @@ int reload_servers(char *fname)
 {
   FILE *f;
   char *line;
-  struct server *old_servers = NULL;
-  struct server *new_servers = NULL;
-  struct server *serv;
   int gotone = 0;
 
   /* buff happens to be MAXDNAME long... */
@@ -848,28 +1507,9 @@ int reload_servers(char *fname)
       my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno));
       return 0;
     }
-  
-  /* move old servers to free list - we can reuse the memory 
-     and not risk malloc if there are the same or fewer new servers. 
-     Servers which were specced on the command line go to the new list. */
-  for (serv = daemon->servers; serv;)
-    {
-      struct server *tmp = serv->next;
-      if (serv->flags & SERV_FROM_RESOLV)
-       {
-         serv->next = old_servers;
-         old_servers = serv; 
-         /* forward table rules reference servers, so have to blow them away */
-         server_gone(serv);
-       }
-      else
-       {
-         serv->next = new_servers;
-         new_servers = serv;
-       }
-      serv = tmp;
-    }
-  
+   
+  mark_servers(SERV_FROM_RESOLV);
+    
   while ((line = fgets(daemon->namebuff, MAXDNAME, f)))
     {
       union mysockaddr addr, source_addr;
@@ -896,78 +1536,73 @@ int reload_servers(char *fname)
          source_addr.in.sin_port = htons(daemon->query_port);
        }
 #ifdef HAVE_IPV6
-      else if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr) > 0)
-       {
+      else 
+       {       
+         int scope_index = 0;
+         char *scope_id = strchr(token, '%');
+         
+         if (scope_id)
+           {
+             *(scope_id++) = 0;
+             scope_index = if_nametoindex(scope_id);
+           }
+         
+         if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr) > 0)
+           {
 #ifdef HAVE_SOCKADDR_SA_LEN
-         source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6);
+             source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6);
 #endif
-         source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
-         addr.in6.sin6_port = htons(NAMESERVER_PORT);
-         source_addr.in6.sin6_addr = in6addr_any;
-         source_addr.in6.sin6_port = htons(daemon->query_port);
+             source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
+             source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = 0;
+             addr.in6.sin6_port = htons(NAMESERVER_PORT);
+             addr.in6.sin6_scope_id = scope_index;
+             source_addr.in6.sin6_addr = in6addr_any;
+             source_addr.in6.sin6_port = htons(daemon->query_port);
+             source_addr.in6.sin6_scope_id = 0;
+           }
+         else
+           continue;
        }
-#endif /* IPV6 */
+#else /* IPV6 */
       else
        continue;
-      
-      if (old_servers)
-       {
-         serv = old_servers;
-         old_servers = old_servers->next;
-       }
-      else if (!(serv = whine_malloc(sizeof (struct server))))
-       continue;
-      
-      /* this list is reverse ordered: 
-        it gets reversed again in check_servers */
-      serv->next = new_servers;
-      new_servers = serv;
-      serv->addr = addr;
-      serv->source_addr = source_addr;
-      serv->domain = NULL;
-      serv->interface[0] = 0;
-      serv->sfd = NULL;
-      serv->flags = SERV_FROM_RESOLV;
-      serv->queries = serv->failed_queries = 0;
+#endif 
+
+      add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL);
       gotone = 1;
     }
   
-  /* Free any memory not used. */
-  while (old_servers)
-    {
-      struct server *tmp = old_servers->next;
-      free(old_servers);
-      old_servers = tmp;
-    }
-
-  daemon->servers = new_servers;
   fclose(f);
+  cleanup_servers();
 
   return gotone;
 }
 
-
-/* Use an IPv4 listener socket for ioctling */
-struct in_addr get_ifaddr(char *intr)
+/* Called when addresses are added or deleted from an interface */
+void newaddress(time_t now)
 {
-  struct listener *l;
-  struct ifreq ifr;
-  struct sockaddr_in ret;
+  (void)now;
   
-  ret.sin_addr.s_addr = -1;
-
-  for (l = daemon->listeners; 
-       l && (l->family != AF_INET || l->fd == -1);
-       l = l->next);
+  if (option_bool(OPT_CLEVERBIND) || option_bool(OPT_LOCAL_SERVICE) ||
+      daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
+    enumerate_interfaces(0);
+  
+  if (option_bool(OPT_CLEVERBIND))
+    create_bound_listeners(0);
   
-  strncpy(ifr.ifr_name, intr, IF_NAMESIZE);
-  ifr.ifr_addr.sa_family = AF_INET;
+#ifdef HAVE_DHCP6
+  if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
+    join_multicast(0);
   
-  if (l &&  ioctl(l->fd, SIOCGIFADDR, &ifr) != -1)
-    memcpy(&ret, &ifr.ifr_addr, sizeof(ret)); 
+  if (daemon->doing_dhcp6 || daemon->doing_ra)
+    dhcp_construct_contexts(now);
   
-  return ret.sin_addr;
+  if (daemon->doing_dhcp6)
+    lease_find_interfaces(now);
+#endif
 }
 
 
 
+
+
index 4cee0a2..ecc2619 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
@@ -21,7 +21,7 @@
 
 static volatile int mem_recover = 0;
 static jmp_buf mem_jmp;
-static void one_file(char *file, int hard_opt);
+static int one_file(char *file, int hard_opt);
 
 /* Solaris headers don't have facility names. */
 #ifdef HAVE_SOLARIS_NETWORK
@@ -64,52 +64,96 @@ struct myoption {
 #define OPTSTRING "951yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:6:7:8:0:3:"
 
 /* options which don't have a one-char version */
-#define LOPT_RELOAD    256
-#define LOPT_NO_NAMES  257
-#define LOPT_TFTP      258
-#define LOPT_SECURE    259
-#define LOPT_PREFIX    260
-#define LOPT_PTR       261
-#define LOPT_BRIDGE    262
-#define LOPT_TFTP_MAX  263
-#define LOPT_FORCE     264
-#define LOPT_NOBLOCK   265
-#define LOPT_LOG_OPTS  266
-#define LOPT_MAX_LOGS  267
-#define LOPT_CIRCUIT   268
-#define LOPT_REMOTE    269
-#define LOPT_SUBSCR    270
-#define LOPT_INTNAME   271
-#define LOPT_BANK      272
-#define LOPT_DHCP_HOST 273
-#define LOPT_APREF     274
-#define LOPT_OVERRIDE  275
-#define LOPT_TFTPPORTS 276
-#define LOPT_REBIND    277
-#define LOPT_NOLAST    278
-#define LOPT_OPTS      279
-#define LOPT_DHCP_OPTS 280
-#define LOPT_MATCH     281
-#define LOPT_BROADCAST 282
-#define LOPT_NEGTTL    283
-#define LOPT_ALTPORT   284
-#define LOPT_SCRIPTUSR 285
-#define LOPT_LOCAL     286
-#define LOPT_NAPTR     287
-#define LOPT_MINPORT   288
-#define LOPT_DHCP_FQDN 289
-#define LOPT_CNAME     290
-#define LOPT_PXE_PROMT 291
-#define LOPT_PXE_SERV  292
-#define LOPT_TEST      293
-#define LOPT_TAG_IF    294
-#define LOPT_PROXY     295
-#define LOPT_GEN_NAMES 296
-#define LOPT_MAXTTL    297
-#define LOPT_NO_REBIND 298
-#define LOPT_LOC_REBND 299
-#define LOPT_ADD_MAC   300
-#define LOPT_DNSSEC    301
+#define LOPT_RELOAD        256
+#define LOPT_NO_NAMES      257
+#define LOPT_TFTP          258
+#define LOPT_SECURE        259
+#define LOPT_PREFIX        260
+#define LOPT_PTR           261
+#define LOPT_BRIDGE        262
+#define LOPT_TFTP_MAX      263
+#define LOPT_FORCE         264
+#define LOPT_NOBLOCK       265
+#define LOPT_LOG_OPTS      266
+#define LOPT_MAX_LOGS      267
+#define LOPT_CIRCUIT       268
+#define LOPT_REMOTE        269
+#define LOPT_SUBSCR        270
+#define LOPT_INTNAME       271
+#define LOPT_BANK          272
+#define LOPT_DHCP_HOST     273
+#define LOPT_APREF         274
+#define LOPT_OVERRIDE      275
+#define LOPT_TFTPPORTS     276
+#define LOPT_REBIND        277
+#define LOPT_NOLAST        278
+#define LOPT_OPTS          279
+#define LOPT_DHCP_OPTS     280
+#define LOPT_MATCH         281
+#define LOPT_BROADCAST     282
+#define LOPT_NEGTTL        283
+#define LOPT_ALTPORT       284
+#define LOPT_SCRIPTUSR     285
+#define LOPT_LOCAL         286
+#define LOPT_NAPTR         287
+#define LOPT_MINPORT       288
+#define LOPT_DHCP_FQDN     289
+#define LOPT_CNAME         290
+#define LOPT_PXE_PROMT     291
+#define LOPT_PXE_SERV      292
+#define LOPT_TEST          293
+#define LOPT_TAG_IF        294
+#define LOPT_PROXY         295
+#define LOPT_GEN_NAMES     296
+#define LOPT_MAXTTL        297
+#define LOPT_NO_REBIND     298
+#define LOPT_LOC_REBND     299
+#define LOPT_ADD_MAC       300
+#define LOPT_DNSSEC        301
+#define LOPT_INCR_ADDR     302
+#define LOPT_CONNTRACK     303
+#define LOPT_FQDN          304
+#define LOPT_LUASCRIPT     305
+#define LOPT_RA            306
+#define LOPT_DUID          307
+#define LOPT_HOST_REC      308
+#define LOPT_TFTP_LC       309
+#define LOPT_RR            310
+#define LOPT_CLVERBIND     311
+#define LOPT_MAXCTTL       312
+#define LOPT_AUTHZONE      313
+#define LOPT_AUTHSERV      314
+#define LOPT_AUTHTTL       315
+#define LOPT_AUTHSOA       316
+#define LOPT_AUTHSFS       317
+#define LOPT_AUTHPEER      318
+#define LOPT_IPSET         319
+#define LOPT_SYNTH         320
+#ifdef OPTION6_PREFIX_CLASS 
+#define LOPT_PREF_CLSS     321
+#endif
+#define LOPT_RELAY         323
+#define LOPT_RA_PARAM      324
+#define LOPT_ADD_SBNET     325
+#define LOPT_QUIET_DHCP    326
+#define LOPT_QUIET_DHCP6   327
+#define LOPT_QUIET_RA      328
+#define LOPT_SEC_VALID     329
+#define LOPT_TRUST_ANCHOR  330
+#define LOPT_DNSSEC_DEBUG  331
+#define LOPT_REV_SERV      332
+#define LOPT_SERVERS_FILE  333
+#define LOPT_DNSSEC_CHECK  334
+#define LOPT_LOCAL_SERVICE 335
+#define LOPT_DNSSEC_TIME   336
+#define LOPT_LOOP_DETECT   337
+#define LOPT_IGNORE_ADDR   338
+#define LOPT_MINCTTL       339
+#define LOPT_DHCP_INOTIFY  340
+#define LOPT_DHOPT_INOTIFY 341
+#define LOPT_HOST_INOTIFY  342
+#define LOPT_DNSSEC_STAMP  343
+#define LOPT_TFTP_NO_FAIL  344
 
 #ifdef HAVE_GETOPT_LONG
 static const struct option opts[] =  
@@ -122,10 +166,11 @@ static const struct myoption opts[] =
     { "no-poll", 0, 0, 'n' },
     { "help", 0, 0, 'w' },
     { "no-daemon", 0, 0, 'd' },
-    { "log-queries", 0, 0, 'q' },
+    { "log-queries", 2, 0, 'q' },
     { "user", 2, 0, 'u' },
     { "group", 2, 0, 'g' },
     { "resolv-file", 2, 0, 'r' },
+    { "servers-file", 1, 0, LOPT_SERVERS_FILE },
     { "mx-host", 1, 0, 'm' },
     { "mx-target", 1, 0, 't' },
     { "cache-size", 2, 0, 'c' },
@@ -140,13 +185,16 @@ static const struct myoption opts[] =
     { "domain-suffix", 1, 0, 's' },
     { "interface", 1, 0, 'i' },
     { "listen-address", 1, 0, 'a' },
+    { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
     { "bogus-priv", 0, 0, 'b' },
     { "bogus-nxdomain", 1, 0, 'B' },
+    { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
     { "selfmx", 0, 0, 'e' },
     { "filterwin2k", 0, 0, 'f' },
     { "pid-file", 2, 0, 'x' },
     { "strict-order", 0, 0, 'o' },
     { "server", 1, 0, 'S' },
+    { "rev-server", 1, 0, LOPT_REV_SERV },
     { "local", 1, 0, LOPT_LOCAL },
     { "address", 1, 0, 'A' },
     { "conf-file", 2, 0, 'C' },
@@ -156,6 +204,7 @@ static const struct myoption opts[] =
     { "local-ttl", 1, 0, 'T' },
     { "no-negcache", 0, 0, 'N' },
     { "addn-hosts", 1, 0, 'H' },
+    { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
     { "query-port", 1, 0, 'Q' },
     { "except-interface", 1, 0, 'I' },
     { "no-dhcp-interface", 1, 0, '2' },
@@ -173,7 +222,8 @@ static const struct myoption opts[] =
     { "srv-host", 1, 0, 'W' },
     { "localise-queries", 0, 0, 'y' },
     { "txt-record", 1, 0, 'Y' },
-    { "enable-dbus", 0, 0, '1' },
+    { "dns-rr", 1, 0, LOPT_RR },
+    { "enable-dbus", 2, 0, '1' },
     { "bootp-dynamic", 2, 0, '3' },
     { "dhcp-mac", 1, 0, '4' },
     { "no-ping", 0, 0, '5' },
@@ -186,9 +236,11 @@ static const struct myoption opts[] =
     { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
     { "enable-tftp", 2, 0, LOPT_TFTP },
     { "tftp-secure", 0, 0, LOPT_SECURE },
+    { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
     { "tftp-unique-root", 0, 0, LOPT_APREF },
     { "tftp-root", 1, 0, LOPT_PREFIX },
     { "tftp-max", 1, 0, LOPT_TFTP_MAX },
+    { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
     { "ptr-record", 1, 0, LOPT_PTR },
     { "naptr-record", 1, 0, LOPT_NAPTR },
     { "bridge-interface", 1, 0 , LOPT_BRIDGE },
@@ -202,6 +254,8 @@ static const struct myoption opts[] =
     { "interface-name", 1, 0, LOPT_INTNAME },
     { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
     { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
+    { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
+    { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
     { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
     { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
     { "stop-dns-rebind", 0, 0, LOPT_REBIND },
@@ -211,6 +265,8 @@ static const struct myoption opts[] =
     { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
     { "neg-ttl", 1, 0, LOPT_NEGTTL },
     { "max-ttl", 1, 0, LOPT_MAXTTL },
+    { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
+    { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
     { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
     { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
     { "min-port", 1, 0, LOPT_MINPORT },
@@ -224,7 +280,39 @@ static const struct myoption opts[] =
     { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
     { "rebind-localhost-ok", 0, 0,  LOPT_LOC_REBND },
     { "add-mac", 0, 0, LOPT_ADD_MAC },
+    { "add-subnet", 2, 0, LOPT_ADD_SBNET },
     { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
+    { "dhcp-sequential-ip", 0, 0,  LOPT_INCR_ADDR },
+    { "conntrack", 0, 0, LOPT_CONNTRACK },
+    { "dhcp-client-update", 0, 0, LOPT_FQDN },
+    { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
+    { "enable-ra", 0, 0, LOPT_RA },
+    { "dhcp-duid", 1, 0, LOPT_DUID },
+    { "host-record", 1, 0, LOPT_HOST_REC },
+    { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
+    { "auth-zone", 1, 0, LOPT_AUTHZONE },
+    { "auth-server", 1, 0, LOPT_AUTHSERV },
+    { "auth-ttl", 1, 0, LOPT_AUTHTTL },
+    { "auth-soa", 1, 0, LOPT_AUTHSOA },
+    { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
+    { "auth-peer", 1, 0, LOPT_AUTHPEER }, 
+    { "ipset", 1, 0, LOPT_IPSET },
+    { "synth-domain", 1, 0, LOPT_SYNTH },
+    { "dnssec", 0, 0, LOPT_SEC_VALID },
+    { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
+    { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
+    { "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK },
+    { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
+    { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
+#ifdef OPTION6_PREFIX_CLASS 
+    { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
+#endif
+    { "dhcp-relay", 1, 0, LOPT_RELAY },
+    { "ra-param", 1, 0, LOPT_RA_PARAM },
+    { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
+    { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
+    { "quiet-ra", 0, 0, LOPT_QUIET_RA },
+    { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
     { NULL, 0, 0, 0 }
   };
 
@@ -241,27 +329,30 @@ static struct {
   char * const desc;
   char * const arg;
 } usage[] = {
-  { 'a', ARG_DUP, "ipaddr",  gettext_noop("Specify local address(es) to listen on."), NULL },
-  { 'A', ARG_DUP, "/domain/ipaddr", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
+  { 'a', ARG_DUP, "<ipaddr>",  gettext_noop("Specify local address(es) to listen on."), NULL },
+  { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
   { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
-  { 'B', ARG_DUP, "ipaddr", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL }, 
-  { 'c', ARG_ONE, "cachesize", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
-  { 'C', ARG_DUP, "path", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
+  { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL }, 
+  { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
+  { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
   { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
   { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL }, 
   { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
   { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
   { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
-  { 'F', ARG_DUP, "ipaddr,ipaddr,time", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
-  { 'g', ARG_ONE, "groupname", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
+  { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
+  { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
   { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
-  { LOPT_DHCP_HOST, ARG_DUP, "<filename>", gettext_noop("Read DHCP host specs from file."), NULL },
-  { LOPT_DHCP_OPTS, ARG_DUP, "<filename>", gettext_noop("Read DHCP option specs from file."), NULL },
+  { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
+  { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
+  { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL }, 
+  { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL }, 
   { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
   { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
-  { 'H', ARG_DUP, "path", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
-  { 'i', ARG_DUP, "interface", gettext_noop("Specify interface(s) to listen on."), NULL },
-  { 'I', ARG_DUP, "int", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
+  { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
+  { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
+  { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
+  { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
   { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
   { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
   { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
@@ -270,190 +361,127 @@ static struct {
   { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL }, 
   { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
   { 'K', OPT_AUTHORITATIVE, NULL, gettext_noop("Assume we are the only DHCP server on the local network."), NULL },
-  { 'l', ARG_ONE, "path", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
+  { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
   { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
-  { 'm', ARG_DUP, "host_name,target,pref", gettext_noop("Specify an MX record."), NULL },
+  { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
   { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
   { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE }, 
   { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
   { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
   { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
   { LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},
-  { 'p', ARG_ONE, "number", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
-  { 'P', ARG_ONE, "<size>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
-  { 'q', OPT_LOG, NULL, gettext_noop("Log DNS queries."), NULL },
-  { 'Q', ARG_ONE, "number", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
+  { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
+  { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
+  { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
+  { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
   { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
-  { 'r', ARG_DUP, "path", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE }, 
-  { 'S', ARG_DUP, "/domain/ipaddr", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
-  { LOPT_LOCAL, ARG_DUP, "/domain/", gettext_noop("Never forward queries to specified domains."), NULL },
+  { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE }, 
+  { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
+  { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
+  { LOPT_REV_SERV, ARG_DUP, "<addr>/<prefix>,<ipaddr>", gettext_noop("Specify address of upstream servers for reverse address queries"), NULL },
+  { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
   { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
-  { 't', ARG_ONE, "host_name", gettext_noop("Specify default target in an MX record."), NULL },
-  { 'T', ARG_ONE, "time", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
-  { LOPT_NEGTTL, ARG_ONE, "time", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
-  { LOPT_MAXTTL, ARG_ONE, "time", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
-  { 'u', ARG_ONE, "username", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER }, 
+  { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
+  { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
+  { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
+  { LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
+  { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
+  { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
+  { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER }, 
   { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
   { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
-  { 'V', ARG_DUP, "addr,addr,mask", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
-  { 'W', ARG_DUP, "name,target,...", gettext_noop("Specify a SRV record."), NULL },
+  { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
+  { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
   { 'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp for known DHCP options."), NULL },
-  { 'x', ARG_ONE, "path", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
-  { 'X', ARG_ONE, "number", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
+  { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
+  { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
   { 'y', OPT_LOCALISE, NULL, gettext_noop("Answer DNS queries based on the interface a query was sent to."), NULL },
-  { 'Y', ARG_DUP, "name,txt....", gettext_noop("Specify TXT DNS record."), NULL },
-  { LOPT_PTR, ARG_DUP, "name,target", gettext_noop("Specify PTR DNS record."), NULL },
-  { LOPT_INTNAME, ARG_DUP, "name,interface", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
+  { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
+  { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
+  { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
   { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
   { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
-  { '1', OPT_DBUS, NULL, gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
-  { '2', ARG_DUP, "interface", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
+  { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
+  { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
   { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
   { '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
-  { LOPT_BRIDGE, ARG_DUP, "iface,alias,..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
+  { LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
   { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
-  { '6', ARG_ONE, "path", gettext_noop("Script to run on DHCP lease creation and destruction."), NULL },
-  { '7', ARG_DUP, "path", gettext_noop("Read configuration from all the files in this directory."), NULL },
+  { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
+  { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
+  { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
+  { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
   { '8', ARG_ONE, "<facilty>|<file>", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), NULL },
   { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
-  { '0', ARG_ONE, "<queries>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" }, 
+  { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" }, 
   { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
   { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
   { LOPT_OVERRIDE, OPT_NO_OVERRIDE, NULL, gettext_noop("Do NOT reuse filename and server fields for extra DHCP options."), NULL },
-  { LOPT_TFTP, ARG_DUP, "[=<interface>]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
-  { LOPT_PREFIX, ARG_ONE, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
+  { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
+  { LOPT_PREFIX, ARG_DUP, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
   { LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL },
   { LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
-  { LOPT_TFTP_MAX, ARG_ONE, "<connections>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
+  { LOPT_TFTP_NO_FAIL, OPT_TFTP_NO_FAIL, NULL, gettext_noop("Do not terminate the service if TFTP directories are inaccessible."), NULL },
+  { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
   { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
+  { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
   { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
   { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
-  { LOPT_MAX_LOGS, ARG_ONE, "[=<log lines>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
+  { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
   { LOPT_REBIND, OPT_NO_REBIND, NULL, gettext_noop("Stop DNS rebinding. Filter private IP ranges when resolving."), NULL },
   { LOPT_LOC_REBND, OPT_LOCAL_REBIND, NULL, gettext_noop("Allow rebinding of 127.0.0.0/8, for RBL servers."), NULL },
-  { LOPT_NO_REBIND, ARG_DUP, "/domain/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
+  { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
   { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
   { LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
   { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
-  { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change script as this user."), NULL },
   { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
   { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
   { LOPT_DHCP_FQDN, OPT_DHCP_FQDN, NULL, gettext_noop("Use only fully qualified domain names for DHCP clients."), NULL },
-  { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
-  { LOPT_PROXY, ARG_DUP, "[=<ip_address>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
+  { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
+  { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
+  { LOPT_RELAY, ARG_DUP, "<local-addr>,<server>[,<interface>]", gettext_noop("Relay DHCP requests to a remote server"), NULL},
   { LOPT_CNAME, ARG_DUP, "<alias>,<target>", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
   { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
   { 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_DNSSEC, OPT_DNSSEC, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers"), 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_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 },
+  { LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },
+  { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
+  { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
+  { LOPT_HOST_REC, ARG_DUP, "<name>,<address>", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
+  { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
+  { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
+  { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
+  { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
+  { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
+  { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritive zone information"), NULL },
+  { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
+  { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
+  { LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
+  { LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
+  { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
+  { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
+  { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
+  { LOPT_DNSSEC_CHECK, OPT_DNSSEC_NO_SIGN, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
+  { LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
+  { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
+#ifdef OPTION6_PREFIX_CLASS 
+  { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
+#endif
+  { LOPT_RA_PARAM, ARG_DUP, "<interface>,[high,|low,]<interval>[,<lifetime>]", gettext_noop("Set priority, resend-interval and router-lifetime"), NULL },
+  { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
+  { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
+  { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
+  { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks"), NULL },
+  { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops"), NULL },
+  { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL }, 
   { 0, 0, NULL, NULL, NULL }
 }; 
 
-#ifdef HAVE_DHCP
-#define OT_ADDR_LIST 0x80
-#define OT_RFC1035_NAME 0x40
-#define OT_INTERNAL 0x20
-#define OT_NAME 0x10
-
-
-static const struct {
-  char *name;
-  unsigned char val, size;
-} opttab[] = {
-  { "netmask", 1, OT_ADDR_LIST },
-  { "time-offset", 2, 4 },
-  { "router", 3, OT_ADDR_LIST  },
-  { "dns-server", 6, OT_ADDR_LIST },
-  { "log-server", 7, OT_ADDR_LIST },
-  { "lpr-server", 9, OT_ADDR_LIST },
-  { "hostname", 12, OT_INTERNAL | OT_NAME },
-  { "boot-file-size", 13, 2 },
-  { "domain-name", 15, OT_NAME },
-  { "swap-server", 16, OT_ADDR_LIST },
-  { "root-path", 17, OT_NAME },
-  { "extension-path", 18, OT_NAME },
-  { "ip-forward-enable", 19, 1 },
-  { "non-local-source-routing", 20, 1 },
-  { "policy-filter", 21, OT_ADDR_LIST },
-  { "max-datagram-reassembly", 22, 2 },
-  { "default-ttl", 23, 1 },
-  { "mtu", 26, 2 },
-  { "all-subnets-local", 27, 1 },
-  { "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
-  { "router-discovery", 31, 1 },
-  { "router-solicitation", 32, OT_ADDR_LIST },
-  { "static-route", 33, OT_ADDR_LIST },
-  { "trailer-encapsulation", 34, 1 },
-  { "arp-timeout", 35, 4 },
-  { "ethernet-encap", 36, 1 },
-  { "tcp-ttl", 37, 1 },
-  { "tcp-keepalive", 38, 4 },
-  { "nis-domain", 40, OT_NAME },
-  { "nis-server", 41, OT_ADDR_LIST },
-  { "ntp-server", 42, OT_ADDR_LIST },
-  { "vendor-encap", 43, OT_INTERNAL },
-  { "netbios-ns", 44, OT_ADDR_LIST },
-  { "netbios-dd", 45, OT_ADDR_LIST },
-  { "netbios-nodetype", 46, 1 },
-  { "netbios-scope", 47, 0 },
-  { "x-windows-fs", 48, OT_ADDR_LIST },
-  { "x-windows-dm", 49, OT_ADDR_LIST },
-  { "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
-  { "lease-time", 51, OT_INTERNAL },
-  { "option-overload", 52, OT_INTERNAL },
-  { "message-type", 53, OT_INTERNAL, },
-  { "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
-  { "parameter-request", 55, OT_INTERNAL },
-  { "message", 56, OT_INTERNAL },
-  { "max-message-size", 57, OT_INTERNAL },
-  { "T1", 58, OT_INTERNAL },
-  { "T2", 59, OT_INTERNAL },
-  { "vendor-class", 60, 0 },
-  { "client-id", 61,OT_INTERNAL },
-  { "nis+-domain", 64, OT_NAME },
-  { "nis+-server", 65, OT_ADDR_LIST },
-  { "tftp-server", 66, OT_NAME },
-  { "bootfile-name", 67, OT_NAME },
-  { "mobile-ip-home", 68, OT_ADDR_LIST }, 
-  { "smtp-server", 69, OT_ADDR_LIST }, 
-  { "pop3-server", 70, OT_ADDR_LIST }, 
-  { "nntp-server", 71, OT_ADDR_LIST }, 
-  { "irc-server", 74, OT_ADDR_LIST }, 
-  { "user-class", 77, 0 },
-  { "FQDN", 81, OT_INTERNAL },
-  { "agent-id", 82, OT_INTERNAL },
-  { "client-arch", 93, 2 },
-  { "client-interface-id", 94, 0 },
-  { "client-machine-id", 97, 0 },
-  { "subnet-select", 118, OT_INTERNAL },
-  { "domain-search", 119, OT_RFC1035_NAME },
-  { "sip-server", 120, 0 },
-  { "classless-static-route", 121, 0 },
-  { "vendor-id-encap", 125, 0 },
-  { "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
-  { NULL, 0, 0 }
-};
-
-char *option_string(unsigned char opt, int *is_ip, int *is_name)
-{
-  int i;
-
-  for (i = 0; opttab[i].name; i++)
-    if (opttab[i].val == opt)
-      {
-       if (is_ip)
-         *is_ip = !!(opttab[i].size & OT_ADDR_LIST);
-       if (is_name)
-         *is_name = !!(opttab[i].size & OT_NAME);
-       return opttab[i].name;
-      }
-
-  return NULL;
-}
-
-#endif
-
 /* We hide metacharaters in quoted strings by mapping them into the ASCII control
    character space. Note that the \0, \t \b \r \033 and \n characters are carefully placed in the
    following sequence so that they map to themselves: it is therefore possible to call
@@ -602,20 +630,37 @@ static int atoi_check16(char *a, int *res)
 
   return 1;
 }
+
+#ifdef HAVE_DNSSEC
+static int atoi_check8(char *a, int *res)
+{
+  if (!(atoi_check(a, res)) ||
+      *res < 0 ||
+      *res > 0xff)
+    return 0;
+
+  return 1;
+}
+#endif
        
-static void add_txt(char *name, char *txt)
+static void add_txt(char *name, char *txt, int stat)
 {
-  size_t len = strlen(txt);
   struct txt_record *r = opt_malloc(sizeof(struct txt_record));
+
+  if (txt)
+    {
+      size_t len = strlen(txt);
+      r->txt = opt_malloc(len+1);
+      r->len = len+1;
+      *(r->txt) = len;
+      memcpy((r->txt)+1, txt, len);
+    }
   
+  r->stat = stat;
   r->name = opt_string_alloc(name);
   r->next = daemon->txt;
   daemon->txt = r;
   r->class = C_CHAOS;
-  r->txt = opt_malloc(len+1);
-  r->len = len+1;
-  *(r->txt) = len;
-  memcpy((r->txt)+1, txt, len);
 }
 
 static void do_usage(void)
@@ -675,18 +720,152 @@ static void do_usage(void)
     }
 }
 
-#ifdef HAVE_DHCP
-static void display_opts(void)
+#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
+
+char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
+{
+  int source_port = 0, serv_port = NAMESERVER_PORT;
+  char *portno, *source;
+#ifdef HAVE_IPV6
+  int scope_index = 0;
+  char *scope_id;
+#endif
+  
+  if (!arg || strlen(arg) == 0)
+    {
+      *flags |= SERV_NO_ADDR;
+      *interface = 0;
+      return NULL;
+    }
+
+  if ((source = split_chr(arg, '@')) && /* is there a source. */
+      (portno = split_chr(source, '#')) &&
+      !atoi_check16(portno, &source_port))
+    return _("bad port");
+  
+  if ((portno = split_chr(arg, '#')) && /* is there a port no. */
+      !atoi_check16(portno, &serv_port))
+    return _("bad port");
+  
+#ifdef HAVE_IPV6
+  scope_id = split_chr(arg, '%');
+#endif
+  
+  if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
+    {
+      addr->in.sin_port = htons(serv_port);    
+      addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
+#ifdef HAVE_SOCKADDR_SA_LEN
+      source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
+#endif
+      source_addr->in.sin_addr.s_addr = INADDR_ANY;
+      source_addr->in.sin_port = htons(daemon->query_port);
+      
+      if (source)
+       {
+         if (flags)
+           *flags |= SERV_HAS_SOURCE;
+         source_addr->in.sin_port = htons(source_port);
+         if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
+           {
+#if defined(SO_BINDTODEVICE)
+             source_addr->in.sin_addr.s_addr = INADDR_ANY;
+             strncpy(interface, source, IF_NAMESIZE - 1);
+#else
+             return _("interface binding not supported");
+#endif
+           }
+       }
+    }
+#ifdef HAVE_IPV6
+  else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
+    {
+      if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
+       return _("bad interface name");
+      
+      addr->in6.sin6_port = htons(serv_port);
+      addr->in6.sin6_scope_id = scope_index;
+      source_addr->in6.sin6_addr = in6addr_any; 
+      source_addr->in6.sin6_port = htons(daemon->query_port);
+      source_addr->in6.sin6_scope_id = 0;
+      addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
+      addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
+#ifdef HAVE_SOCKADDR_SA_LEN
+      addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
+#endif
+      if (source)
+       {
+         if (flags)
+           *flags |= SERV_HAS_SOURCE;
+         source_addr->in6.sin6_port = htons(source_port);
+         if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
+           {
+#if defined(SO_BINDTODEVICE)
+             source_addr->in6.sin6_addr = in6addr_any; 
+             strncpy(interface, source, IF_NAMESIZE - 1);
+#else
+             return _("interface binding not supported");
+#endif
+           }
+       }
+    }
+#endif
+  else
+    return _("bad address");
+
+  return NULL;
+}
+
+static struct server *add_rev4(struct in_addr addr, int msize)
+{
+  struct server *serv = opt_malloc(sizeof(struct server));
+  in_addr_t  a = ntohl(addr.s_addr) >> 8;
+  char *p;
+
+  memset(serv, 0, sizeof(struct server));
+  p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */
+  
+  if (msize == 24)
+    p += sprintf(p, "%d.", a & 0xff);
+  a = a >> 8;
+  if (msize != 8)
+    p += sprintf(p, "%d.", a & 0xff);
+  a = a >> 8;
+  p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
+  
+  serv->flags = SERV_HAS_DOMAIN;
+  serv->next = daemon->servers;
+  daemon->servers = serv;
+
+  return serv;
+
+}
+
+static struct server *add_rev6(struct in6_addr *addr, int msize)
 {
+  struct server *serv = opt_malloc(sizeof(struct server));
+  char *p;
   int i;
+                                 
+  memset(serv, 0, sizeof(struct server));
+  p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
   
-  printf(_("Known DHCP options:\n"));
+  for (i = msize-1; i >= 0; i -= 4)
+    { 
+      int dig = ((unsigned char *)addr)[i>>3];
+      p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
+    }
+  p += sprintf(p, "ip6.arpa");
   
-  for (i = 0; opttab[i].name; i++)
-    if (!(opttab[i].size & OT_INTERNAL))
-      printf("%3d %s\n", opttab[i].val, opttab[i].name);
+  serv->flags = SERV_HAS_DOMAIN;
+  serv->next = daemon->servers;
+  daemon->servers = serv;
+  
+  return serv;
 }
 
+#ifdef HAVE_DHCP
+
 static int is_tag_prefix(char *arg)
 {
   if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
@@ -704,14 +883,16 @@ static char *set_prefix(char *arg)
 }
 
 /* This is too insanely large to keep in-line in the switch */
-static char *parse_dhcp_opt(char *arg, int flags)
+static int parse_dhcp_opt(char *errstr, char *arg, int flags)
 {
   struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
   char lenchar = 0, *cp;
-  int i, addrs, digs, is_addr, is_hex, is_dec, is_string, dots;
-  char *comma = NULL, *problem = NULL;
+  int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
+  char *comma = NULL;
   struct dhcp_netid *np = NULL;
-  unsigned char opt_len = 0;
+  u16 opt_len = 0;
+  int is6 = 0;
+  int option_ok = 0;
 
   new->len = 0;
   new->flags = flags;
@@ -731,22 +912,48 @@ static char *parse_dhcp_opt(char *arg, int flags)
        {
          new->opt = atoi(arg);
          opt_len = 0;
+         option_ok = 1;
          break;
        }
       
       if (strstr(arg, "option:") == arg)
        {
-         for (i = 0; opttab[i].name; i++)
-           if (!(opttab[i].size & OT_INTERNAL) &&
-               strcasecmp(opttab[i].name, arg+7) == 0)
-             {
-               new->opt = opttab[i].val;
-               opt_len = opttab[i].size;
-               break;
-             }
-         /* option:<optname> must follow tag and vendor string. */
+         if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
+           {
+             opt_len = lookup_dhcp_len(AF_INET, new->opt);
+             /* option:<optname> must follow tag and vendor string. */
+             if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
+               option_ok = 1;
+           }
+         break;
+       }
+#ifdef HAVE_DHCP6
+      else if (strstr(arg, "option6:") == arg)
+       {
+         for (cp = arg+8; *cp; cp++)
+           if (*cp < '0' || *cp > '9')
+             break;
+        
+         if (!*cp)
+           {
+             new->opt = atoi(arg+8);
+             opt_len = 0;
+             option_ok = 1;
+           }
+         else
+           {
+             if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
+               {
+                 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
+                 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
+                   option_ok = 1;
+               }
+           }
+         /* option6:<opt>|<optname> must follow tag and vendor string. */
+         is6 = 1;
          break;
        }
+#endif
       else if (strstr(arg, "vendor:") == arg)
        {
          new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
@@ -763,7 +970,7 @@ static char *parse_dhcp_opt(char *arg, int flags)
          new->flags |= DHOPT_RFC3925;
          if (flags == DHOPT_MATCH)
            {
-             new->opt = 1; /* avoid error below */
+             option_ok = 1;
              break;
            }
        }
@@ -781,27 +988,33 @@ static char *parse_dhcp_opt(char *arg, int flags)
       
       arg = comma; 
     }
-  
-  if (opt_len == 0 &&
-      !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
-    for (i = 0; opttab[i].name; i++)
-      if (new->opt == opttab[i].val)
-       {
-         opt_len = opttab[i].size;
-         if (opt_len & OT_INTERNAL)
-           opt_len = 0;
-         break;
-       }
 
+#ifdef HAVE_DHCP6
+  if (is6)
+    {
+      if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
+       ret_err(_("unsupported encapsulation for IPv6 option"));
+      
+      if (opt_len == 0 &&
+         !(new->flags & DHOPT_RFC3925))
+       opt_len = lookup_dhcp_len(AF_INET6, new->opt);
+    }
+  else
+#endif
+    if (opt_len == 0 &&
+       !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
+      opt_len = lookup_dhcp_len(AF_INET, new->opt);
+  
   /* option may be missing with rfc3925 match */
-  if (new->opt == 0)
-    problem = _("bad dhcp-option");
-  else if (comma)
+  if (!option_ok)
+    ret_err(_("bad dhcp-option"));
+  
+  if (comma)
     {
       /* characterise the value */
       char c;
       int found_dig = 0;
-      is_addr = is_hex = is_dec = is_string = 1;
+      is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
       addrs = digs = 1;
       dots = 0;
       for (cp = comma; (c = *cp); cp++)
@@ -817,17 +1030,17 @@ static char *parse_dhcp_opt(char *arg, int flags)
          }
        else if (c == '/') 
          {
-           is_dec = is_hex = 0;
+           is_addr6 = is_dec = is_hex = 0;
            if (cp == comma) /* leading / means a pathname */
              is_addr = 0;
          } 
        else if (c == '.')      
          {
-           is_dec = is_hex = 0;
+           is_addr6 = is_dec = is_hex = 0;
            dots++;
          }
        else if (c == '-')
-         is_hex = is_addr = 0;
+         is_hex = is_addr = is_addr6 = 0;
        else if (c == ' ')
          is_dec = is_hex = 0;
        else if (!(c >='0' && c <= '9'))
@@ -844,26 +1057,66 @@ static char *parse_dhcp_opt(char *arg, int flags)
            if (!((c >='A' && c <= 'F') ||
                  (c >='a' && c <= 'f') || 
                  (c == '*' && (flags & DHOPT_MATCH))))
-             is_hex = 0;
+             {
+               is_hex = 0;
+               if (c != '[' && c != ']')
+                 is_addr6 = 0;
+             }
          }
        else
          found_dig = 1;
      
       if (!found_dig)
        is_dec = is_addr = 0;
-      
+     
       /* We know that some options take addresses */
       if (opt_len & OT_ADDR_LIST)
        {
          is_string = is_dec = is_hex = 0;
-         if (!is_addr || dots == 0)
-           problem = _("bad IP address");
+         
+         if (!is6 && (!is_addr || dots == 0))
+           ret_err(_("bad IP address"));
+
+          if (is6 && !is_addr6)
+            ret_err(_("bad IPv6 address"));
        }
       /* or names */
-      else if (opt_len & (OT_NAME | OT_RFC1035_NAME))
-       is_addr = is_dec = is_hex = 0;
+      else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
+       is_addr6 = is_addr = is_dec = is_hex = 0;
+      
+      if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
+       {
+         int val, fac = 1;
+
+         switch (comma[strlen(comma) - 1])
+           {
+           case 'w':
+           case 'W':
+             fac *= 7;
+             /* fall through */
+           case 'd':
+           case 'D':
+             fac *= 24;
+             /* fall though */
+           case 'h':
+           case 'H':
+             fac *= 60;
+             /* fall through */
+           case 'm':
+           case 'M':
+             fac *= 60;
+             /* fall through */
+           case 's':
+           case 'S':
+             comma[strlen(comma) - 1] = 0;
+           }
          
-      if (is_hex && digs > 1)
+         new->len = 4;
+         new->val = opt_malloc(4);
+         val = atoi(comma);
+         *((int *)new->val) = htonl(val * fac);          
+       }  
+      else if (is_hex && digs > 1)
        {
          new->len = digs;
          new->val = opt_malloc(new->len);
@@ -896,7 +1149,7 @@ static char *parse_dhcp_opt(char *arg, int flags)
          for (i=0; i<new->len; i++)
            new->val[i] = val>>((new->len - i - 1)*8);
        }
-      else if (is_addr)        
+      else if (is_addr && !is6)        
        {
          struct in_addr in;
          unsigned char *op;
@@ -917,7 +1170,7 @@ static char *parse_dhcp_opt(char *arg, int flags)
              cp = comma;
              comma = split(cp);
              slash = split_chr(cp, '/');
-             in.s_addr = inet_addr(cp);
+             inet_pton(AF_INET, cp, &in);
              if (!slash)
                {
                  memcpy(op, &in, INADDRSZ);
@@ -941,11 +1194,37 @@ static char *parse_dhcp_opt(char *arg, int flags)
            }
          new->len = op - new->val;
        }
+      else if (is_addr6 && is6)
+       {
+         unsigned char *op;
+         new->val = op = opt_malloc(16 * addrs);
+         new->flags |= DHOPT_ADDR6;
+         while (addrs--) 
+           {
+             cp = comma;
+             comma = split(cp);
+             
+             /* check for [1234::7] */
+             if (*cp == '[')
+               cp++;
+             if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
+               cp[strlen(cp)-1] = 0;
+             
+             if (inet_pton(AF_INET6, cp, op))
+               {
+                 op += IN6ADDRSZ;
+                 continue;
+               }
+         
+             ret_err(_("bad IPv6 address"));
+           } 
+         new->len = op - new->val;
+       }
       else if (is_string)
        {
-         /* text arg */
+         /* text arg */
          if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
-             !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
+             !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
            {
              /* dns search, RFC 3397, or SIP, RFC 3361 */
              unsigned char *q, *r, *tail;
@@ -964,10 +1243,8 @@ static char *parse_dhcp_opt(char *arg, int flags)
                  if (strcmp (arg, ".") != 0)
                    {
                      if (!(dom = canonicalise_opt(arg)))
-                       {
-                         problem = _("bad domain in dhcp-option");
-                         break;
-                       }
+                       ret_err(_("bad domain in dhcp-option"));
+                       
                      domlen = strlen(dom) + 2;
                    }
                      
@@ -1018,6 +1295,72 @@ static char *parse_dhcp_opt(char *arg, int flags)
              new->len = (int) len + header_size;
              new->val = m;
            }
+#ifdef HAVE_DHCP6
+         else if (comma && (opt_len & OT_CSTRING))
+           {
+             /* length fields are two bytes so need 16 bits for each string */
+             int i, commas = 1;
+             unsigned char *p, *newp;
+
+             for (i = 0; comma[i]; i++)
+               if (comma[i] == ',')
+                 commas++;
+             
+             newp = opt_malloc(strlen(comma)+(2*commas));        
+             p = newp;
+             arg = comma;
+             comma = split(arg);
+             
+             while (arg && *arg)
+               {
+                 u16 len = strlen(arg);
+                 unhide_metas(arg);
+                 PUTSHORT(len, p);
+                 memcpy(p, arg, len);
+                 p += len; 
+
+                 arg = comma;
+                 comma = split(arg);
+               }
+
+             new->val = newp;
+             new->len = p - newp;
+           }
+         else if (comma && (opt_len & OT_RFC1035_NAME))
+           {
+             unsigned char *p = NULL, *newp, *end;
+             int len = 0;
+             arg = comma;
+             comma = split(arg);
+             
+             while (arg && *arg)
+               {
+                 char *dom = canonicalise_opt(arg);
+                 if (!dom)
+                   ret_err(_("bad domain in dhcp-option"));
+                                 
+                 newp = opt_malloc(len + strlen(dom) + 2);
+                 
+                 if (p)
+                   {
+                     memcpy(newp, p, len);
+                     free(p);
+                   }
+                 
+                 p = newp;
+                 end = do_rfc1035_name(p + len, dom);
+                 *end++ = 0;
+                 len = end - p;
+                 free(dom);
+
+                 arg = comma;
+                 comma = split(arg);
+               }
+             
+             new->val = p;
+             new->len = len;
+           }
+#endif
          else
            {
              new->len = strlen(comma);
@@ -1028,33 +1371,42 @@ static char *parse_dhcp_opt(char *arg, int flags)
        }
     }
 
-  if ((new->len > 255) || 
+  if (!is6 && 
+      ((new->len > 255) || 
       (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
-      (new->len > 250 && (new->flags & DHOPT_RFC3925)))
-    problem = _("dhcp-option too long");
+       (new->len > 250 && (new->flags & DHOPT_RFC3925))))
+    ret_err(_("dhcp-option too long"));
   
-  if (!problem)
+  if (flags == DHOPT_MATCH)
     {
-      if (flags == DHOPT_MATCH)
+      if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
+         !new->netid ||
+         new->netid->next)
+       ret_err(_("illegal dhcp-match"));
+       
+      if (is6)
        {
-         if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
-             !new->netid ||
-             new->netid->next)
-           problem = _("illegal dhcp-match");
-         else
-           {
-             new->next = daemon->dhcp_match;
-             daemon->dhcp_match = new;
-           }
+         new->next = daemon->dhcp_match6;
+         daemon->dhcp_match6 = new;
        }
-      else     
+      else
        {
-         new->next = daemon->dhcp_opts;
-         daemon->dhcp_opts = new;
+         new->next = daemon->dhcp_match;
+         daemon->dhcp_match = new;
        }
     }
-
-  return problem;
+  else if (is6)
+    {
+      new->next = daemon->dhcp_opts6;
+      daemon->dhcp_opts6 = new;
+    }
+  else
+    {
+      new->next = daemon->dhcp_opts;
+      daemon->dhcp_opts = new;
+    }
+    
+  return 1;
 }
 
 #endif
@@ -1067,13 +1419,21 @@ void set_option_bool(unsigned int opt)
     daemon->options2 |= 1u << (opt - 32);
 }
 
-static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
+void reset_option_bool(unsigned int opt)
+{
+  if (opt < 32)
+    daemon->options &= ~(1u << opt);
+  else
+    daemon->options2 &= ~(1u << (opt - 32));
+}
+
+static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
 {      
   int i;
-  char *comma, *problem = NULL;;
+  char *comma;
 
   if (option == '?')
-    return gen_prob;
+    ret_err(gen_err);
   
   for (i=0; usage[i].opt != 0; i++)
     if (usage[i].opt == option)
@@ -1084,7 +1444,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
           {
             /* command line */
             if (rept == ARG_USED_CL)
-              return _("illegal repeated flag");
+              ret_err(_("illegal repeated flag"));
             if (rept == ARG_ONE)
               usage[i].rept = ARG_USED_CL;
           }
@@ -1092,7 +1452,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
           {
             /* allow file to override command line */
             if (rept == ARG_USED_FILE)
-              return _("illegal repeated keyword");
+              ret_err(_("illegal repeated keyword"));
             if (rept == ARG_USED_CL || rept == ARG_ONE)
               usage[i].rept = ARG_USED_FILE;
           }
@@ -1100,7 +1460,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
         if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL) 
           {
             set_option_bool(rept);
-            return NULL;
+            return 1;
           }
        
         break;
@@ -1127,7 +1487,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
        struct list {
          char *suffix;
          struct list *next;
-       } *ignore_suffix = NULL, *li;
+       } *ignore_suffix = NULL, *match_suffix = NULL, *li;
        
        comma = split(arg);
        if (!(directory = opt_string_alloc(arg)))
@@ -1136,12 +1496,25 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
        for (arg = comma; arg; arg = comma) 
          {
            comma = split(arg);
-           li = opt_malloc(sizeof(struct list));
-           li->next = ignore_suffix;
-           ignore_suffix = li;
-           /* Have to copy: buffer is overwritten */
-           li->suffix = opt_string_alloc(arg);
-         };
+           if (strlen(arg) != 0)
+             {
+               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);
+                 }
+               else
+                 {
+                   li->next = ignore_suffix;
+                   ignore_suffix = li;
+                   /* Have to copy: buffer is overwritten */
+                   li->suffix = opt_string_alloc(arg);
+                 }
+             }
+         }
        
        if (!(dir_stream = opendir(directory)))
          die(_("cannot access directory %s: %s"), directory, EC_FILE);
@@ -1158,6 +1531,20 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
                ent->d_name[0] == '.')
              continue;
 
+           if (match_suffix)
+             {
+               for (li = match_suffix; li; li = li->next)
+                 {
+                   /* check for required suffices */
+                   size_t ls = strlen(li->suffix);
+                   if (len > ls &&
+                       strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
+                     break;
+                 }
+               if (!li)
+                 continue;
+             }
+           
            for (li = ignore_suffix; li; li = li->next)
              {
                /* check for proscribed suffices */
@@ -1174,14 +1561,14 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
            strcat(path, "/");
            strcat(path, ent->d_name);
 
+           /* files must be readable */
            if (stat(path, &buf) == -1)
              die(_("cannot access %s: %s"), path, EC_FILE);
+           
            /* only reg files allowed. */
-           if (!S_ISREG(buf.st_mode))
-             continue;
+           if (S_ISREG(buf.st_mode))
+             one_file(path, 0);
            
-           /* files must be readable */
-           one_file(path, 0);
            free(path);
          }
      
@@ -1193,10 +1580,34 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
            free(ignore_suffix->suffix);
            free(ignore_suffix);
          }
-             
+       for(; match_suffix; match_suffix = li)
+         {
+           li = match_suffix->next;
+           free(match_suffix->suffix);
+           free(match_suffix);
+         }    
        break;
       }
 
+    case LOPT_ADD_SBNET: /* --add-subnet */
+      set_option_bool(OPT_CLIENT_SUBNET);
+      if (arg)
+       {
+         comma = split(arg);
+         if (!atoi_check(arg, &daemon->addr4_netmask) || 
+             (comma && !atoi_check(comma, &daemon->addr6_netmask)))
+            ret_err(gen_err);
+       }
+      break;
+
+    case '1': /* --enable-dbus */
+      set_option_bool(OPT_DBUS);
+      if (arg)
+       daemon->dbus_name = opt_string_alloc(arg);
+      else
+       daemon->dbus_name = DNSMASQ_SERVICE;
+      break;
+      
     case '8': /* --log-facility */
       /* may be a filename */
       if (strchr(arg, '/') || strcmp (arg, "-") == 0)
@@ -1204,7 +1615,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
       else
        {         
 #ifdef __ANDROID__
-         problem = _("setting log facility is not possible under Android");
+         ret_err(_("setting log facility is not possible under Android"));
 #else
          for (i = 0; facilitynames[i].c_name; i++)
            if (hostname_isequal((char *)facilitynames[i].c_name, arg))
@@ -1213,7 +1624,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
          if (facilitynames[i].c_name)
            daemon->log_fac = facilitynames[i].c_val;
          else
-           problem = _("bad log facility");
+           ret_err(_("bad log facility"));
 #endif
        }
       break;
@@ -1251,6 +1662,10 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
        daemon->resolv_files = list;
        break;
       }
+
+    case LOPT_SERVERS_FILE:
+      daemon->servers_file = opt_string_alloc(arg);
+      break;
       
     case 'm':  /* --mx-host */
       {
@@ -1262,12 +1677,12 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
          {
            char *prefstr;
            if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
-             problem = _("bad MX preference");
+             ret_err(_("bad MX preference"));
          }
        
        if (!(name = canonicalise_opt(arg)) || 
            (comma && !(target = canonicalise_opt(comma))))
-         problem = _("bad MX name");
+         ret_err(_("bad MX name"));
        
        new = opt_malloc(sizeof(struct mx_srv_record));
        new->next = daemon->mxnames;
@@ -1281,7 +1696,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
       
     case 't': /*  --mx-target */
       if (!(daemon->mxtarget = canonicalise_opt(arg)))
-       problem = _("bad MX target");
+       ret_err(_("bad MX target"));
       break;
 
 #ifdef HAVE_DHCP      
@@ -1289,23 +1704,35 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
       daemon->lease_file = opt_string_alloc(arg);
       break;
       
-    case '6': /* --dhcp-script */
+      /* Sorry about the gross pre-processor abuse */
+    case '6':             /* --dhcp-script */
+    case LOPT_LUASCRIPT:  /* --dhcp-luascript */
 #  if defined(NO_FORK)
-      problem = _("cannot run scripts under uClinux");
+      ret_err(_("cannot run scripts under uClinux"));
 #  elif !defined(HAVE_SCRIPT)
-      problem = _("recompile with HAVE_SCRIPT defined to enable lease-change scripts");
+      ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
 #  else
-      daemon->lease_change_command = opt_string_alloc(arg);
+      if (option == LOPT_LUASCRIPT)
+#    if !defined(HAVE_LUASCRIPT)
+       ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
+#    else
+        daemon->luascript = opt_string_alloc(arg);
+#    endif
+      else
+        daemon->lease_change_command = opt_string_alloc(arg);
 #  endif
       break;
-#endif
-
-    case LOPT_DHCP_HOST: /* --dhcp-hostfile */
-    case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
-    case 'H': /* --addn-hosts */
+#endif /* HAVE_DHCP */
+
+    case LOPT_DHCP_HOST:     /* --dhcp-hostsfile */
+    case LOPT_DHCP_OPTS:     /* --dhcp-optsfile */
+    case LOPT_DHCP_INOTIFY:  /* --dhcp-hostsdir */
+    case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
+    case LOPT_HOST_INOTIFY:  /* --hostsdir */
+    case 'H':                /* --addn-hosts */
       {
        struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
-       static int hosts_index = 1;
+       static unsigned int hosts_index = SRC_AH;
        new->fname = opt_string_alloc(arg);
        new->index = hosts_index++;
        new->flags = 0;
@@ -1319,15 +1746,193 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
            new->next = daemon->dhcp_hosts_file;
            daemon->dhcp_hosts_file = new;
          }
-       else if (option ==  LOPT_DHCP_OPTS)
+       else if (option == LOPT_DHCP_OPTS)
          {
            new->next = daemon->dhcp_opts_file;
            daemon->dhcp_opts_file = new;
          }       
+       else 
+         {
+           new->next = daemon->dynamic_dirs;
+           daemon->dynamic_dirs = new; 
+           if (option == LOPT_DHCP_INOTIFY)
+             new->flags |= AH_DHCP_HST;
+           else if (option == LOPT_DHOPT_INOTIFY)
+             new->flags |= AH_DHCP_OPT;
+           else if (option == LOPT_HOST_INOTIFY)
+             new->flags |= AH_HOSTS;
+         }
+       
+       break;
+      }
+      
+
+#ifdef HAVE_AUTH
+    case LOPT_AUTHSERV: /* --auth-server */
+      if (!(comma = split(arg)))
+       ret_err(gen_err);
+      
+      daemon->authserver = opt_string_alloc(arg);
+      arg = comma;
+      do {
+       struct iname *new = opt_malloc(sizeof(struct iname));
+       comma = split(arg);
+       new->name = NULL;
+       unhide_metas(arg);
+       if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
+         new->addr.sa.sa_family = AF_INET;
+#ifdef HAVE_IPV6
+       else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
+         new->addr.sa.sa_family = AF_INET6;
+#endif
+       else
+         {
+           char *fam = split_chr(arg, '/');
+           new->name = opt_string_alloc(arg);
+           new->addr.sa.sa_family = 0;
+           if (fam)
+             {
+               if (strcmp(fam, "4") == 0)
+                 new->addr.sa.sa_family = AF_INET;
+#ifdef HAVE_IPV6
+               else if (strcmp(fam, "6") == 0)
+                 new->addr.sa.sa_family = AF_INET6;
+#endif
+               else
+                 ret_err(gen_err);
+             } 
+         }
+       new->next = daemon->authinterface;
+       daemon->authinterface = new;
+       
+       arg = comma;
+      } while (arg);
+            
+      break;
+
+    case LOPT_AUTHSFS: /* --auth-sec-servers */
+      {
+       struct name_list *new;
+
+       do {
+         comma = split(arg);
+         new = opt_malloc(sizeof(struct name_list));
+         new->name = opt_string_alloc(arg);
+         new->next = daemon->secondary_forward_server;
+         daemon->secondary_forward_server = new;
+         arg = comma;
+       } while (arg);
+       break;
+      }
+       
+    case LOPT_AUTHZONE: /* --auth-zone */
+      {
+       struct auth_zone *new;
+       
+       comma = split(arg);
+               
+       new = opt_malloc(sizeof(struct auth_zone));
+       new->domain = opt_string_alloc(arg);
+       new->subnet = NULL;
+       new->interface_names = NULL;
+       new->next = daemon->auth_zones;
+       daemon->auth_zones = new;
+
+       while ((arg = comma))
+         {
+           int prefixlen = 0;
+           char *prefix;
+           struct addrlist *subnet =  NULL;
+           struct all_addr addr;
+
+           comma = split(arg);
+           prefix = split_chr(arg, '/');
+           
+           if (prefix && !atoi_check(prefix, &prefixlen))
+             ret_err(gen_err);
+           
+           if (inet_pton(AF_INET, arg, &addr.addr.addr4))
+             {
+               subnet = opt_malloc(sizeof(struct addrlist));
+               subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
+               subnet->flags = ADDRLIST_LITERAL;
+             }
+#ifdef HAVE_IPV6
+           else if (inet_pton(AF_INET6, arg, &addr.addr.addr6))
+             {
+               subnet = opt_malloc(sizeof(struct addrlist));
+               subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
+               subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
+             }
+#endif
+           else 
+             {
+               struct auth_name_list *name =  opt_malloc(sizeof(struct auth_name_list));
+               name->name = opt_string_alloc(arg);
+               name->flags = AUTH4 | AUTH6;
+               name->next = new->interface_names;
+               new->interface_names = name;
+               if (prefix)
+                 {
+                   if (prefixlen == 4)
+                     name->flags &= ~AUTH6;
+#ifdef HAVE_IPV6
+                   else if (prefixlen == 6)
+                     name->flags &= ~AUTH4;
+#endif
+                   else
+                     ret_err(gen_err);
+                 }
+             }
+           
+           if (subnet)
+             {
+               subnet->addr = addr;
+               subnet->next = new->subnet;
+               new->subnet = subnet;
+             }
+         }
        break;
       }
       
-    case 's': /* --domain */
+    case  LOPT_AUTHSOA: /* --auth-soa */
+      comma = split(arg);
+      daemon->soa_sn = (u32)atoi(arg);
+      if (comma)
+       {
+         char *cp;
+         arg = comma;
+         comma = split(arg);
+         daemon->hostmaster = opt_string_alloc(arg);
+         for (cp = daemon->hostmaster; *cp; cp++)
+           if (*cp == '@')
+             *cp = '.';
+
+         if (comma)
+           {
+             arg = comma;
+             comma = split(arg); 
+             daemon->soa_refresh = (u32)atoi(arg);
+             if (comma)
+               {
+                 arg = comma;
+                 comma = split(arg); 
+                 daemon->soa_retry = (u32)atoi(arg);
+                 if (comma)
+                   {
+                     arg = comma;
+                     comma = split(arg); 
+                     daemon->soa_expiry = (u32)atoi(arg);
+                   }
+               }
+           }
+       }
+
+      break;
+#endif
+
+    case 's':         /* --domain */
+    case LOPT_SYNTH:  /* --synth-domain */
       if (strcmp (arg, "#") == 0)
        set_option_bool(OPT_RESOLV_DOMAIN);
       else
@@ -1335,82 +1940,157 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
          char *d;
          comma = split(arg);
          if (!(d = canonicalise_opt(arg)))
-           option = '?';
+           ret_err(gen_err);
          else
            {
              if (comma)
                {
-                 struct cond_domain *new = safe_malloc(sizeof(struct cond_domain));
+                 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
                  char *netpart;
+                 
+                 new->prefix = NULL;
 
                  unhide_metas(comma);
                  if ((netpart = split_chr(comma, '/')))
                    {
-                     int msize, mask;
+                     int msize;
+
                      arg = split(netpart);
-                     if ((new->start.s_addr = inet_addr(comma)) == (in_addr_t)-1 ||
-                         !atoi_check(netpart, &msize))
-                       option = '?';
-                     else
+                     if (!atoi_check(netpart, &msize))
+                       ret_err(gen_err);
+                     else if (inet_pton(AF_INET, comma, &new->start))
                        {
-                         mask = (1 << (32 - msize)) - 1;
+                         int mask = (1 << (32 - msize)) - 1;
+                         new->is6 = 0;                           
                          new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
                          new->end.s_addr = new->start.s_addr | htonl(mask);
                          if (arg)
                            {
-                             /* generate the equivalent of
-                                local=/<domain>/
-                                local=/xxx.yyy.zzz.in-addr.arpa/ */
-
-                             if (strcmp(arg, "local") != 0 || 
-                                 (msize != 8 && msize != 16 && msize != 24))
-                               option = '?';
+                             if (option != 's')
+                               {
+                                 if (!(new->prefix = canonicalise_opt(arg)) ||
+                                     strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
+                                   ret_err(_("bad prefix"));
+                               }
+                             else if (strcmp(arg, "local") != 0 ||
+                                      (msize != 8 && msize != 16 && msize != 24))
+                               ret_err(gen_err);
                              else
                                {
-                                 struct server *serv = opt_malloc(sizeof(struct server));
-                                 in_addr_t a = ntohl(new->start.s_addr) >> 8;
-                                 char *p;
+                                  /* generate the equivalent of
+                                     local=/xxx.yyy.zzz.in-addr.arpa/ */
+                                 struct server *serv = add_rev4(new->start, msize);
+                                 serv->flags |= SERV_NO_ADDR;
 
+                                 /* local=/<domain>/ */
+                                 serv = opt_malloc(sizeof(struct server));
                                  memset(serv, 0, sizeof(struct server));
                                  serv->domain = d;
                                  serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
                                  serv->next = daemon->servers;
                                  daemon->servers = serv;
-
+                               }
+                           }
+                       }
+#ifdef HAVE_IPV6
+                     else if (inet_pton(AF_INET6, comma, &new->start6))
+                       {
+                         u64 mask = (1LLU << (128 - msize)) - 1LLU;
+                         u64 addrpart = addr6part(&new->start6);
+                         new->is6 = 1;
+                         
+                         /* prefix==64 overflows the mask calculation above */
+                         if (msize == 64)
+                           mask = (u64)-1LL;
+                         
+                         new->end6 = new->start6;
+                         setaddr6part(&new->start6, addrpart & ~mask);
+                         setaddr6part(&new->end6, addrpart | mask);
+                         
+                         if (msize < 64)
+                           ret_err(gen_err);
+                         else if (arg)
+                           {
+                             if (option != 's')
+                               {
+                                 if (!(new->prefix = canonicalise_opt(arg)) ||
+                                     strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
+                                   ret_err(_("bad prefix"));
+                               }       
+                             else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
+                               ret_err(gen_err);
+                             else 
+                               {
+                                 /* generate the equivalent of
+                                    local=/xxx.yyy.zzz.ip6.arpa/ */
+                                 struct server *serv = add_rev6(&new->start6, msize);
+                                 serv->flags |= SERV_NO_ADDR;
+                                 
+                                 /* local=/<domain>/ */
                                  serv = opt_malloc(sizeof(struct server));
                                  memset(serv, 0, sizeof(struct server));
-                                 p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */
-                                 
-                                 if (msize == 24)
-                                   p += sprintf(p, "%d.", a & 0xff);
-                                 a = a >> 8;
-                                 if (msize != 8)
-                                   p += sprintf(p, "%d.", a & 0xff);
-                                 a = a >> 8;
-                                 p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
-
+                                 serv->domain = d;
                                  serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
                                  serv->next = daemon->servers;
                                  daemon->servers = serv;
                                }
                            }
                        }
+#endif
+                     else
+                       ret_err(gen_err);
                    }
-                 else if ((arg = split(comma)))
+                 else
                    {
-                     if ((new->start.s_addr = inet_addr(comma)) == (in_addr_t)-1 ||
-                         (new->end.s_addr = inet_addr(arg)) == (in_addr_t)-1)
-                       option = '?';
+                     char *prefstr;
+                     arg = split(comma);
+                     prefstr = split(arg);
+
+                     if (inet_pton(AF_INET, comma, &new->start))
+                       {
+                         new->is6 = 0;
+                         if (!arg)
+                           new->end.s_addr = new->start.s_addr;
+                         else if (!inet_pton(AF_INET, arg, &new->end))
+                           ret_err(gen_err);
+                       }
+#ifdef HAVE_IPV6
+                     else if (inet_pton(AF_INET6, comma, &new->start6))
+                       {
+                         new->is6 = 1;
+                         if (!arg)
+                           memcpy(&new->end6, &new->start6, IN6ADDRSZ);
+                         else if (!inet_pton(AF_INET6, arg, &new->end6))
+                           ret_err(gen_err);
+                       }
+#endif
+                     else 
+                       ret_err(gen_err);
+
+                     if (option != 's' && prefstr)
+                       {
+                         if (!(new->prefix = canonicalise_opt(prefstr)) ||
+                             strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
+                           ret_err(_("bad prefix"));
+                       }
                    }
-                 else if ((new->start.s_addr = new->end.s_addr = inet_addr(comma)) == (in_addr_t)-1)
-                   option = '?';
 
                  new->domain = d;
-                 new->next = daemon->cond_domain;
-                 daemon->cond_domain = new;
+                 if (option  == 's')
+                   {
+                     new->next = daemon->cond_domain;
+                     daemon->cond_domain = new;
+                   }
+                 else
+                   {
+                     new->next = daemon->synth_domains;
+                     daemon->synth_domains = new;
+                   }
                }
-             else
+             else if (option == 's')
                daemon->domain_suffix = d;
+             else 
+               ret_err(gen_err);
            }
        }
       break;
@@ -1439,11 +2119,17 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
        /* new->name may be NULL if someone does
           "interface=" to disable all interfaces except loop. */
        new->name = opt_string_alloc(arg);
-       new->isloop = new->used = 0;
+       new->used = 0;
        arg = comma;
       } while (arg);
       break;
       
+    case LOPT_TFTP: /* --enable-tftp */
+      set_option_bool(OPT_TFTP);
+      if (!arg)
+       break;
+      /* fall through */
+
     case 'I':  /* --except-interface */
     case '2':  /* --no-dhcp-interface */
       do {
@@ -1455,6 +2141,11 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
            new->next = daemon->if_except;
            daemon->if_except = new;
          }
+       else if (option == LOPT_TFTP)
+          {
+           new->next = daemon->tftp_interfaces;
+           daemon->tftp_interfaces = new;
+         }
        else
          {
            new->next = daemon->dhcp_except;
@@ -1465,30 +2156,40 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
       break;
       
     case 'B':  /* --bogus-nxdomain */
-      {
+    case LOPT_IGNORE_ADDR: /* --ignore-address */
+     {
        struct in_addr addr;
        unhide_metas(arg);
-       if (arg && (addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
+       if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
          {
            struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
-           baddr->next = daemon->bogus_addr;
-           daemon->bogus_addr = baddr;
+           if (option == 'B')
+             {
+               baddr->next = daemon->bogus_addr;
+               daemon->bogus_addr = baddr;
+             }
+           else
+             {
+               baddr->next = daemon->ignore_addr;
+               daemon->ignore_addr = baddr;
+             }
            baddr->addr = addr;
          }
        else
-         option = '?'; /* error */
+         ret_err(gen_err); /* error */
        break;  
       }
       
     case 'a':  /* --listen-address */
+    case LOPT_AUTHPEER: /* --auth-peer */
       do {
        struct iname *new = opt_malloc(sizeof(struct iname));
        comma = split(arg);
        unhide_metas(arg);
-       new->next = daemon->if_addrs;
-       if (arg && (new->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
+       if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
          {
            new->addr.sa.sa_family = AF_INET;
+           new->addr.in.sin_port = 0;
 #ifdef HAVE_SOCKADDR_SA_LEN
            new->addr.in.sin_len = sizeof(new->addr.in);
 #endif
@@ -1499,18 +2200,26 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
            new->addr.sa.sa_family = AF_INET6;
            new->addr.in6.sin6_flowinfo = 0;
            new->addr.in6.sin6_scope_id = 0;
+           new->addr.in6.sin6_port = 0;
 #ifdef HAVE_SOCKADDR_SA_LEN
            new->addr.in6.sin6_len = sizeof(new->addr.in6);
 #endif
          }
 #endif
        else
+         ret_err(gen_err);
+
+       new->used = 0;
+       if (option == 'a')
          {
-           option = '?'; /* error */
-           break;
+           new->next = daemon->if_addrs;
+           daemon->if_addrs = new;
          }
-       
-       daemon->if_addrs = new;
+       else
+         {
+           new->next = daemon->auth_peers;
+           daemon->auth_peers = new;
+         } 
        arg = comma;
       } while (arg);
       break;
@@ -1551,23 +2260,25 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
                  break;
              }
            if (!newlist)
-             {
-               option = '?';
-               break;
-             }
-           
+             ret_err(gen_err);
          }
        else
          {
            newlist = opt_malloc(sizeof(struct server));
            memset(newlist, 0, sizeof(struct server));
+#ifdef HAVE_LOOP
+           newlist->uid = rand32();
+#endif
          }
        
+       if (servers_only && option == 'S')
+         newlist->flags |= SERV_FROM_FILE;
+       
        if (option == 'A')
          {
            newlist->flags |= SERV_LITERAL_ADDRESS;
            if (!(newlist->flags & SERV_TYPE))
-             option = '?';
+             ret_err(gen_err);
          }
        else if (option == LOPT_NO_REBIND)
          newlist->flags |= SERV_NO_REBIND;
@@ -1576,83 +2287,19 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
          {
            if (!(newlist->flags & SERV_NO_REBIND))
              newlist->flags |= SERV_NO_ADDR; /* no server */
-           if (newlist->flags & SERV_LITERAL_ADDRESS)
-             option = '?';
          }
 
        else if (strcmp(arg, "#") == 0)
          {
            newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
            if (newlist->flags & SERV_LITERAL_ADDRESS)
-             option = '?';
+             ret_err(gen_err);
          }
        else
          {
-           int source_port = 0, serv_port = NAMESERVER_PORT;
-           char *portno, *source;
-           
-           if ((source = split_chr(arg, '@')) && /* is there a source. */
-               (portno = split_chr(source, '#')) &&
-               !atoi_check16(portno, &source_port))
-             problem = _("bad port");
-                   
-           if ((portno = split_chr(arg, '#')) && /* is there a port no. */
-               !atoi_check16(portno, &serv_port))
-             problem = _("bad port");
-           
-           if ((newlist->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t) -1)
-             {
-               newlist->addr.in.sin_port = htons(serv_port);   
-               newlist->source_addr.in.sin_port = htons(source_port); 
-               newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET;
-#ifdef HAVE_SOCKADDR_SA_LEN
-               newlist->source_addr.in.sin_len = newlist->addr.in.sin_len = sizeof(struct sockaddr_in);
-#endif
-               if (source)
-                 {
-                   newlist->flags |= SERV_HAS_SOURCE;
-                   if ((newlist->source_addr.in.sin_addr.s_addr = inet_addr(source)) == (in_addr_t) -1)
-                     {
-#if defined(SO_BINDTODEVICE)
-                       newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
-                       strncpy(newlist->interface, source, IF_NAMESIZE - 1);
-#else
-                       problem = _("interface binding not supported");
-#endif
-                     }
-                 }
-               else
-                 newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
-             }
-#ifdef HAVE_IPV6
-           else if (inet_pton(AF_INET6, arg, &newlist->addr.in6.sin6_addr) > 0)
-             {
-               newlist->addr.in6.sin6_port = htons(serv_port);
-               newlist->source_addr.in6.sin6_port = htons(source_port);
-               newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET6;
-#ifdef HAVE_SOCKADDR_SA_LEN
-               newlist->addr.in6.sin6_len = newlist->source_addr.in6.sin6_len = sizeof(newlist->addr.in6);
-#endif
-               if (source)
-                 {
-                    newlist->flags |= SERV_HAS_SOURCE;
-                    if (inet_pton(AF_INET6, source, &newlist->source_addr.in6.sin6_addr) == 0)
-                     {
-#if defined(SO_BINDTODEVICE)
-                       newlist->source_addr.in6.sin6_addr = in6addr_any; 
-                       strncpy(newlist->interface, source, IF_NAMESIZE - 1);
-#else
-                       problem = _("interface binding not supported");
-#endif
-                     }
-                 }
-               else
-                 newlist->source_addr.in6.sin6_addr = in6addr_any; 
-             }
-#endif
-           else
-             option = '?'; /* error */
-           
+           char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
+           if (err)
+             ret_err(err);
          }
        
        serv = newlist;
@@ -1661,19 +2308,122 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
            serv->next->flags = serv->flags;
            serv->next->addr = serv->addr;
            serv->next->source_addr = serv->source_addr;
+           strcpy(serv->next->interface, serv->interface);
            serv = serv->next;
          }
        serv->next = daemon->servers;
        daemon->servers = newlist;
        break;
       }
+
+    case LOPT_REV_SERV: /* --rev-server */
+      {
+       char *string;
+       int size;
+       struct server *serv;
+       struct in_addr addr4;
+#ifdef HAVE_IPV6
+       struct in6_addr addr6;
+#endif
+       unhide_metas(arg);
+       if (!arg || !(comma=split(arg)) || !(string = split_chr(arg, '/')) || !atoi_check(string, &size))
+         ret_err(gen_err);
+
+       if (inet_pton(AF_INET, arg, &addr4))
+         serv = add_rev4(addr4, size);
+#ifdef HAVE_IPV6
+       else if (inet_pton(AF_INET6, arg, &addr6))
+         serv = add_rev6(&addr6, size);
+#endif
+       else
+         ret_err(gen_err);
+       string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
+       
+       if (string)
+         ret_err(string);
+       
+       if (servers_only)
+         serv->flags |= SERV_FROM_FILE;
+       
+       break;
+      }
+
+    case LOPT_IPSET: /* --ipset */
+#ifndef HAVE_IPSET
+      ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
+      break;
+#else
+      {
+        struct ipsets ipsets_head;
+        struct ipsets *ipsets = &ipsets_head;
+        int size;
+        char *end;
+        char **sets, **sets_pos;
+        memset(ipsets, 0, sizeof(struct ipsets));
+        unhide_metas(arg);
+        if (arg && *arg == '/') 
+          {
+            arg++;
+            while ((end = split_chr(arg, '/'))) 
+              {
+                char *domain = NULL;
+                /* elide leading dots - they are implied in the search algorithm */
+                while (*arg == '.')
+                  arg++;
+                /* # matches everything and becomes a zero length domain string */
+                if (strcmp(arg, "#") == 0 || !*arg)
+                  domain = "";
+                else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
+                  option = '?';
+                ipsets->next = opt_malloc(sizeof(struct ipsets));
+                ipsets = ipsets->next;
+                memset(ipsets, 0, sizeof(struct ipsets));
+                ipsets->domain = domain;
+                arg = end;
+              }
+          } 
+        else 
+          {
+            ipsets->next = opt_malloc(sizeof(struct ipsets));
+            ipsets = ipsets->next;
+            memset(ipsets, 0, sizeof(struct ipsets));
+            ipsets->domain = "";
+          }
+        if (!arg || !*arg)
+          {
+            option = '?';
+            break;
+          }
+        size = 2;
+        for (end = arg; *end; ++end) 
+          if (*end == ',')
+              ++size;
+     
+        sets = sets_pos = opt_malloc(sizeof(char *) * size);
+        
+        do {
+          end = split(arg);
+          *sets_pos++ = opt_string_alloc(arg);
+          arg = end;
+        } while (end);
+        *sets_pos = 0;
+        for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
+          ipsets->next->sets = sets;
+        ipsets->next = daemon->ipsets;
+        daemon->ipsets = ipsets_head.next;
+        
+        break;
+      }
+#endif
       
     case 'c':  /* --cache-size */
       {
        int size;
        
        if (!atoi_check(arg, &size))
-         option = '?';
+         ret_err(gen_err);
        else
          {
            /* zero is OK, and means no caching. */
@@ -1690,23 +2440,29 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
       
     case 'p':  /* --port */
       if (!atoi_check16(arg, &daemon->port))
-       option = '?';
+       ret_err(gen_err);
       break;
     
     case LOPT_MINPORT:  /* --min-port */
       if (!atoi_check16(arg, &daemon->min_port))
-       option = '?';
+       ret_err(gen_err);
       break;
 
     case '0':  /* --dns-forward-max */
       if (!atoi_check(arg, &daemon->ftabsize))
-       option = '?';
+       ret_err(gen_err);
       break;  
     
+    case 'q': /* --log-queries */
+      set_option_bool(OPT_LOG);
+      if (arg && strcmp(arg, "extra") == 0)
+       set_option_bool(OPT_EXTRALOG);
+      break;
+
     case LOPT_MAX_LOGS:  /* --log-async */
       daemon->max_logs = LOG_MAX; /* default */
       if (arg && !atoi_check(arg, &daemon->max_logs))
-       option = '?';
+       ret_err(gen_err);
       else if (daemon->max_logs > 100)
        daemon->max_logs = 100;
       break;  
@@ -1715,14 +2471,14 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
       {
        int i;
        if (!atoi_check(arg, &i))
-         option = '?';
+         ret_err(gen_err);
        daemon->edns_pktsz = (unsigned short)i; 
        break;
       }
       
     case 'Q':  /* --query-port */
       if (!atoi_check16(arg, &daemon->query_port))
-       option = '?';
+       ret_err(gen_err);
       /* if explicitly set to zero, use single OS ephemeral port
         and disable random ports */
       if (daemon->query_port == 0)
@@ -1732,14 +2488,27 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
     case 'T':         /* --local-ttl */
     case LOPT_NEGTTL: /* --neg-ttl */
     case LOPT_MAXTTL: /* --max-ttl */
+    case LOPT_MINCTTL: /* --min-cache-ttl */
+    case LOPT_MAXCTTL: /* --max-cache-ttl */
+    case LOPT_AUTHTTL: /* --auth-ttl */
       {
        int ttl;
        if (!atoi_check(arg, &ttl))
-         option = '?';
+         ret_err(gen_err);
        else if (option == LOPT_NEGTTL)
          daemon->neg_ttl = (unsigned long)ttl;
        else if (option == LOPT_MAXTTL)
          daemon->max_ttl = (unsigned long)ttl;
+       else if (option == LOPT_MINCTTL)
+         {
+           if (ttl > TTL_FLOOR_LIMIT)
+             ttl = TTL_FLOOR_LIMIT;
+           daemon->min_cache_ttl = (unsigned long)ttl;
+         }
+       else if (option == LOPT_MAXCTTL)
+         daemon->max_cache_ttl = (unsigned long)ttl;
+       else if (option == LOPT_AUTHTTL)
+         daemon->auth_ttl = (unsigned long)ttl;
        else
          daemon->local_ttl = (unsigned long)ttl;
        break;
@@ -1748,26 +2517,14 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
 #ifdef HAVE_DHCP
     case 'X': /* --dhcp-lease-max */
       if (!atoi_check(arg, &daemon->dhcp_max))
-       option = '?';
+       ret_err(gen_err);
       break;
 #endif
       
 #ifdef HAVE_TFTP
-    case LOPT_TFTP:  /* --enable-tftp */
-      if (arg)
-       {
-         struct interface_list *new = opt_malloc(sizeof(struct interface_list));
-         new->interface = opt_string_alloc(arg);
-         new->next = daemon->tftp_interfaces;
-         daemon->tftp_interfaces = new;
-       }
-      else
-       daemon->tftp_unlimited = 1;
-      break;
-
     case LOPT_TFTP_MAX:  /*  --tftp-max */
       if (!atoi_check(arg, &daemon->tftp_max))
-       option = '?';
+       ret_err(gen_err);
       break;  
 
     case LOPT_PREFIX: /* --tftp-prefix */
@@ -1788,7 +2545,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
       if (!(comma = split(arg)) || 
          !atoi_check16(arg, &daemon->start_tftp_port) ||
          !atoi_check16(comma, &daemon->end_tftp_port))
-       problem = _("bad port range");
+       ret_err(_("bad port range"));
       
       if (daemon->start_tftp_port > daemon->end_tftp_port)
        {
@@ -1804,10 +2561,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
       {
        struct dhcp_bridge *new = opt_malloc(sizeof(struct dhcp_bridge));
        if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
-         {
-           problem = _("bad bridge-interface");
-           break;
-         }
+         ret_err(_("bad bridge-interface"));
        
        strcpy(new->iface, arg);
        new->alias = NULL;
@@ -1833,21 +2587,11 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
     case 'F':  /* --dhcp-range */
       {
        int k, leasepos = 2;
-       char *cp, *a[5] = { NULL, NULL, NULL, NULL, NULL };
+       char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
        struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
        
-       new->next = daemon->dhcp;
+       memset (new, 0, sizeof(*new));
        new->lease_time = DEFLEASE;
-       new->addr_epoch = 0;
-       new->netmask.s_addr = 0;
-       new->broadcast.s_addr = 0;
-       new->router.s_addr = 0;
-       new->netid.net = NULL;
-       new->filter = NULL;
-       new->flags = 0;
-       new->interface = NULL;
-
-       gen_prob = _("bad dhcp-range");
        
        if (!arg)
          {
@@ -1858,24 +2602,26 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
        while(1)
          {
            for (cp = arg; *cp; cp++)
-             if (!(*cp == ' ' || *cp == '.' ||  (*cp >='0' && *cp <= '9')))
+             if (!(*cp == ' ' || *cp == '.' || *cp == ':' || 
+                   (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
+                   (*cp >='0' && *cp <= '9')))
                break;
            
            if (*cp != ',' && (comma = split(arg)))
              {
-               if (strstr(arg, "interface:") == arg)
-                 new->interface = opt_string_alloc(arg+10);
-               else if (is_tag_prefix(arg))
+               if (is_tag_prefix(arg))
                  {
                    struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
                    tt->net = opt_string_alloc(arg+4);
                    tt->next = new->filter;
-                   new->filter = tt;
+                   /* ignore empty tag */
+                   if (tt->net)
+                     new->filter = tt;
                  }
                else
                  {
                    if (new->netid.net)
-                     problem = _("only one tag allowed");
+                     ret_err(_("only one tag allowed"));
                    else if (strstr(arg, "set:") == arg)
                      new->netid.net = opt_string_alloc(arg+4);
                    else
@@ -1890,53 +2636,139 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
              }
          }
        
-       for (k = 1; k < 5; k++)
+       for (k = 1; k < 8; k++)
          if (!(a[k] = split(a[k-1])))
            break;
        
-       if ((k < 2) || ((new->start.s_addr = inet_addr(a[0])) == (in_addr_t)-1))
-         option = '?';
-       else if (strcmp(a[1], "static") == 0)
-         {
-           new->end = new->start;
-           new->flags |= CONTEXT_STATIC;
-         }
-       else if (strcmp(a[1], "proxy") == 0)
-         {
-           new->end = new->start;
-           new->flags |= CONTEXT_PROXY;
-         }
-       else if ((new->end.s_addr = inet_addr(a[1])) == (in_addr_t)-1)
-         option = '?';
-       
-       if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
-         {
-           struct in_addr tmp = new->start;
-           new->start = new->end;
-           new->end = tmp;
-         }
+       if (k < 2)
+         ret_err(_("bad dhcp-range"));
        
-       if (option != '?' && k >= 3 && strchr(a[2], '.') &&  
-           ((new->netmask.s_addr = inet_addr(a[2])) != (in_addr_t)-1))
+       if (inet_pton(AF_INET, a[0], &new->start))
          {
-           new->flags |= CONTEXT_NETMASK;
-           leasepos = 3;
-           if (!is_same_net(new->start, new->end, new->netmask))
-             problem = _("inconsistent DHCP range");
+           new->next = daemon->dhcp;
+           daemon->dhcp = new;
+           new->end = new->start;
+           if (strcmp(a[1], "static") == 0)
+             new->flags |= CONTEXT_STATIC;
+           else if (strcmp(a[1], "proxy") == 0)
+             new->flags |= CONTEXT_PROXY;
+           else if (!inet_pton(AF_INET, a[1], &new->end))
+             ret_err(_("bad dhcp-range"));
+           
+           if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
+             {
+               struct in_addr tmp = new->start;
+               new->start = new->end;
+               new->end = tmp;
+             }
+           
+           if (k >= 3 && strchr(a[2], '.') &&  
+               (inet_pton(AF_INET, a[2], &new->netmask) > 0))
+             {
+               new->flags |= CONTEXT_NETMASK;
+               leasepos = 3;
+               if (!is_same_net(new->start, new->end, new->netmask))
+                 ret_err(_("inconsistent DHCP range"));
+             }
+           
+           if (k >= 4 && strchr(a[3], '.') &&  
+               (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
+             {
+               new->flags |= CONTEXT_BRDCAST;
+               leasepos = 4;
+             }
          }
-       daemon->dhcp = new;
-       
-       if (k >= 4 && strchr(a[3], '.') &&  
-           ((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t)-1))
+#ifdef HAVE_DHCP6
+       else if (inet_pton(AF_INET6, a[0], &new->start6))
          {
-           new->flags |= CONTEXT_BRDCAST;
-           leasepos = 4;
+           new->flags |= CONTEXT_V6; 
+           new->prefix = 64; /* default */
+           new->end6 = new->start6;
+           new->next = daemon->dhcp6;
+           daemon->dhcp6 = new;
+
+           for (leasepos = 1; leasepos < k; leasepos++)
+             {
+               if (strcmp(a[leasepos], "static") == 0)
+                 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
+               else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
+                 new->flags |= CONTEXT_RA;
+               else if (strcmp(a[leasepos], "ra-names") == 0)
+                 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
+               else if (strcmp(a[leasepos], "ra-advrouter") == 0)
+                 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])
+                 {
+                   new->template_interface = opt_string_alloc(a[leasepos] + 12);
+                   new->flags |= CONTEXT_TEMPLATE;
+                 }
+               else  
+                 break;
+             }
+                            
+           /* bare integer < 128 is prefix value */
+           if (leasepos < k)
+             {
+               int pref;
+               for (cp = a[leasepos]; *cp; cp++)
+                 if (!(*cp >= '0' && *cp <= '9'))
+                   break;
+               if (!*cp && (pref = atoi(a[leasepos])) <= 128)
+                 {
+                   new->prefix = pref;
+                   leasepos++;
+                 }
+             }
+           
+           if (new->prefix != 64)
+             {
+               if (new->flags & CONTEXT_RA)
+                 ret_err(_("prefix length must be exactly 64 for RA subnets"));
+               else if (new->flags & CONTEXT_TEMPLATE)
+                 ret_err(_("prefix length must be exactly 64 for subnet constructors"));
+             }
+
+           if (new->prefix < 64)
+             ret_err(_("prefix length must be at least 64"));
+           
+           if (!is_same_net6(&new->start6, &new->end6, new->prefix))
+             ret_err(_("inconsistent DHCPv6 range"));
+
+           /* dhcp-range=:: enables DHCP stateless on any interface */
+           if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
+             new->prefix = 0;
+           
+           if (new->flags & CONTEXT_TEMPLATE)
+             {
+               struct in6_addr zero;
+               memset(&zero, 0, sizeof(zero));
+               if (!is_same_net6(&zero, &new->start6, new->prefix))
+                 ret_err(_("prefix must be zero with \"constructor:\" argument"));
+             }
+           
+           if (addr6part(&new->start6) > addr6part(&new->end6))
+             {
+               struct in6_addr tmp = new->start6;
+               new->start6 = new->end6;
+               new->end6 = tmp;
+             }
          }
+#endif
+       else
+         ret_err(_("bad dhcp-range"));
        
-       if (k >= leasepos+1)
+       if (leasepos < k)
          {
            if (strcmp(a[leasepos], "infinite") == 0)
              new->lease_time = 0xffffffff;
+           else if (strcmp(a[leasepos], "deprecated") == 0)
+             new->flags |= CONTEXT_DEPRECATE;
            else
              {
                int fac = 1;
@@ -1944,6 +2776,10 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
                  {
                    switch (a[leasepos][strlen(a[leasepos]) - 1])
                      {
+                     case 'w':
+                     case 'W':
+                       fac *= 7;
+                       /* fall through */
                      case 'd':
                      case 'D':
                        fac *= 24;
@@ -1961,6 +2797,13 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
                        a[leasepos][strlen(a[leasepos]) - 1] = 0;
                      }
                    
+                   for (cp = a[leasepos]; *cp; cp++)
+                     if (!(*cp >= '0' && *cp <= '9'))
+                       break;
+
+                   if (*cp || (leasepos+1 < k))
+                     ret_err(_("bad dhcp-range"));
+                   
                    new->lease_time = atoi(a[leasepos]) * fac;
                    /* Leases of a minute or less confuse
                       some clients, notably Apple's */
@@ -1976,7 +2819,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
     case 'G':  /* --dhcp-host */
       {
        int j, k = 0;
-       char *a[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
+       char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
        struct dhcp_config *new;
        struct in_addr in;
        
@@ -1988,7 +2831,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
        new->netid = NULL;
 
        if ((a[0] = arg))
-         for (k = 1; k < 6; k++)
+         for (k = 1; k < 7; k++)
            if (!(a[k] = split(a[k-1])))
              break;
        
@@ -2016,7 +2859,8 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
                        }
 
                      if (len == -1)
-                       problem = _("bad hex constant");
+
+                       ret_err(_("bad hex constant"));
                      else if ((new->clid = opt_malloc(len)))
                        {
                          new->flags |= CONFIG_CLID;
@@ -2037,12 +2881,34 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
                  strcpy(newtag->net, arg+4);
                  unhide_metas(newtag->net);
                }
-             else 
+             else if (strstr(arg, "tag:") == arg)
+               ret_err(_("cannot match tags in --dhcp-host"));
+#ifdef HAVE_DHCP6
+             else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
+               {
+                 arg[strlen(arg)-1] = 0;
+                 arg++;
+                 
+                 if (!inet_pton(AF_INET6, arg, &new->addr6))
+                   ret_err(_("bad IPv6 address"));
+
+                 for (i= 0; i < 8; i++)
+                   if (new->addr6.s6_addr[i] != 0)
+                     break;
+
+                 /* set WILDCARD if network part all zeros */
+                 if (i == 8)
+                   new->flags |= CONFIG_WILDCARD;
+                 
+                 new->flags |= CONFIG_ADDR6;
+               }
+#endif
+             else
                {
                  struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
                  if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX, 
                                                     &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
-                   problem = _("bad hex constant");
+                   ret_err(_("bad hex constant"));
                  else
                    {
                      
@@ -2051,15 +2917,27 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
                    }               
                }
            }
-         else if (strchr(a[j], '.') && (in.s_addr = inet_addr(a[j])) != (in_addr_t)-1)
+         else if (strchr(a[j], '.') && (inet_pton(AF_INET, a[j], &in) > 0))
            {
+             struct dhcp_config *configs;
+             
              new->addr = in;
              new->flags |= CONFIG_ADDR;
+
+             /* If the same IP appears in more than one host config, then DISCOVER
+                for one of the hosts will get the address, but REQUEST will be NAKed,
+                since the address is reserved by the other one -> protocol loop. */
+             for (configs = daemon->dhcp_conf; configs; configs = configs->next) 
+               if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
+                 {
+                   sprintf(errstr, _("duplicate dhcp-host IP address %s"),  inet_ntoa(in));
+                   return 0;
+                 }           
            }
          else
            {
              char *cp, *lastp = NULL, last = 0;
-             int fac = 1;
+             int fac = 1, isdig = 0;
              
              if (strlen(a[j]) > 1)
                {
@@ -2067,6 +2945,10 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
                  last = *lastp;
                  switch (last)
                    {
+                   case 'w':
+                   case 'W':
+                     fac *= 7;
+                     /* fall through */
                    case 'd':
                    case 'D':
                      fac *= 24;
@@ -2086,9 +2968,11 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
                }
              
              for (cp = a[j]; *cp; cp++)
-               if (!isdigit((unsigned char)*cp) && *cp != ' ')
+               if (isdigit((unsigned char)*cp))
+                 isdig = 1;
+               else if (*cp != ' ')
                  break;
-             
+
              if (*cp)
                {
                  if (lastp)
@@ -2104,13 +2988,13 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
                    {
                      if (!(new->hostname = canonicalise_opt(a[j])) ||
                          !legal_hostname(new->hostname))
-                       problem = _("bad DHCP host name");
-                     else
-                       new->flags |= CONFIG_NAME;
-                     new->domain = NULL;                       
+                       ret_err(_("bad DHCP host name"));
+                    
+                     new->flags |= CONFIG_NAME;
+                     new->domain = strip_hostname(new->hostname);                      
                    }
                }
-             else
+             else if (isdig)
                {
                  new->lease_time = atoi(a[j]) * fac; 
                  /* Leases of a minute or less confuse
@@ -2177,6 +3061,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
                else 
                  {
                    new->set = NULL;
+                   free(newtag);
                    break;
                  }
              }
@@ -2185,7 +3070,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
          }
 
        if (!new->set)
-         problem = _("bad tag-if");
+         ret_err(_("bad tag-if"));
          
        break;
       }
@@ -2195,12 +3080,11 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
     case LOPT_FORCE:    /* --dhcp-option-force */
     case LOPT_OPTS:
     case LOPT_MATCH:    /* --dhcp-match */
-      problem = parse_dhcp_opt(arg, 
-                              option == LOPT_FORCE ? DHOPT_FORCE : 
-                              (option == LOPT_MATCH ? DHOPT_MATCH :
-                              (option == LOPT_OPTS ? DHOPT_BANK : 0)));
-      break;
-      
+      return parse_dhcp_opt(errstr, arg, 
+                           option == LOPT_FORCE ? DHOPT_FORCE : 
+                           (option == LOPT_MATCH ? DHOPT_MATCH :
+                            (option == LOPT_OPTS ? DHOPT_BANK : 0)));
+     
     case 'M': /* --dhcp-boot */
       {
        struct dhcp_netid *id = NULL;
@@ -2215,11 +3099,12 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
          };
        
        if (!arg)
-         option = '?';
+         ret_err(gen_err);
        else 
          {
-           char *dhcp_file, *dhcp_sname = NULL;
+           char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
            struct in_addr dhcp_next_server;
+           struct dhcp_boot *new;
            comma = split(arg);
            dhcp_file = opt_string_alloc(arg);
            dhcp_next_server.s_addr = 0;
@@ -2231,22 +3116,30 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
                if (comma)
                  {
                    unhide_metas(comma);
-                   if ((dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1)
-                     option = '?';
+                   if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
+                     {
+                       /*
+                        * The user may have specified the tftp hostname here.
+                        * save it so that it can be resolved/looked up during
+                        * actual dhcp_reply().
+                        */     
+                       
+                       tftp_sname = opt_string_alloc(comma);
+                       dhcp_next_server.s_addr = 0;
+                     }
                  }
              }
-           if (option != '?')
-             {
-               struct dhcp_boot *new = opt_malloc(sizeof(struct dhcp_boot));
-               new->file = dhcp_file;
-               new->sname = dhcp_sname;
-               new->next_server = dhcp_next_server;
-               new->netid = id;
-               new->next = daemon->boot_config;
-               daemon->boot_config = new;
-             }
+           
+           new = opt_malloc(sizeof(struct dhcp_boot));
+           new->file = dhcp_file;
+           new->sname = dhcp_sname;
+           new->tftp_sname = tftp_sname;
+           new->next_server = dhcp_next_server;
+           new->netid = id;
+           new->next = daemon->boot_config;
+           daemon->boot_config = new;
          }
-       
+      
        break;
       }
 
@@ -2269,7 +3162,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
           }
         
         if (!arg)
-          option = '?';
+          ret_err(gen_err);
         else
           {
             comma = split(arg);
@@ -2302,6 +3195,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
         static int boottype = 32768;
         
         new->netid = NULL;
+        new->sname = NULL;
         new->server.s_addr = 0;
 
         while (is_tag_prefix(arg))
@@ -2348,10 +3242,17 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
                         new->basename = opt_string_alloc(arg);
                       }
                     
-                    if (comma && (new->server.s_addr = inet_addr(comma)) == (in_addr_t)-1)
-                      option = '?';
+                    if (comma)
+                      {
+                        if (!inet_pton(AF_INET, comma, &new->server))
+                          {
+                            new->server.s_addr = 0;
+                            new->sname = opt_string_alloc(comma);
+                          }
+                      
+                      }
                   }
-
+                
                 /* Order matters */
                 new->next = NULL;
                 if (!daemon->pxe_services)
@@ -2369,14 +3270,13 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
               }
           }
         
-        option = '?';
-        break;
+        ret_err(gen_err);
        }
         
     case '4':  /* --dhcp-mac */
       {
        if (!(comma = split(arg)))
-         option = '?';
+         ret_err(gen_err);
        else
          {
            struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
@@ -2384,7 +3284,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
            unhide_metas(comma);
            new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
            if (new->hwaddr_len == -1)
-             option = '?';
+             ret_err(gen_err);
            else
              {
                new->next = daemon->dhcp_macs;
@@ -2393,64 +3293,94 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
          }
       }
       break;
-      
+
+#ifdef OPTION6_PREFIX_CLASS 
+    case LOPT_PREF_CLSS: /* --dhcp-prefix-class */
+      {
+       struct prefix_class *new = opt_malloc(sizeof(struct prefix_class));
+       
+       if (!(comma = split(arg)) ||
+           !atoi_check16(comma, &new->class))
+         ret_err(gen_err);
+       
+       new->tag.net = opt_string_alloc(set_prefix(arg));
+       new->next = daemon->prefix_classes;
+       daemon->prefix_classes = new;
+       
+       break;
+      }
+#endif
+                             
+
     case 'U':           /* --dhcp-vendorclass */
     case 'j':           /* --dhcp-userclass */
     case LOPT_CIRCUIT:  /* --dhcp-circuitid */
     case LOPT_REMOTE:   /* --dhcp-remoteid */
     case LOPT_SUBSCR:   /* --dhcp-subscrid */
       {
-       if (!(comma = split(arg)))
-         option = '?';
-       else
-         {
-           unsigned char *p;
-           int dig = 0;
-           struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
-           new->netid.net = opt_string_alloc(set_prefix(arg));
-           /* check for hex string - must digits may include : must not have nothing else, 
-              only allowed for agent-options. */
-           for (p = (unsigned char *)comma; *p; p++)
-             if (isxdigit(*p))
-               dig = 1;
-             else if (*p != ':')
-               break;
-           unhide_metas(comma);
-           if (option == 'U' || option == 'j' || *p || !dig)
-             {
-               new->len = strlen(comma);  
-               new->data = opt_malloc(new->len);
-               memcpy(new->data, comma, new->len);
-             }
-           else
-             {
-               new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
-               new->data = opt_malloc(new->len);
-               memcpy(new->data, comma, new->len);
-             }
+        unsigned char *p;
+        int dig = 0;
+        struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
+        
+        if (!(comma = split(arg)))
+          ret_err(gen_err);
+       
+        new->netid.net = opt_string_alloc(set_prefix(arg));
+        /* check for hex string - must digits may include : must not have nothing else, 
+           only allowed for agent-options. */
+        
+        arg = comma;
+        if ((comma = split(arg)))
+          {
+            if (option  != 'U' || strstr(arg, "enterprise:") != arg)
+              ret_err(gen_err);
+            else
+              new->enterprise = atoi(arg+11);
+          }
+        else
+          comma = arg;
+        
+        for (p = (unsigned char *)comma; *p; p++)
+          if (isxdigit(*p))
+            dig = 1;
+          else if (*p != ':')
+            break;
+        unhide_metas(comma);
+        if (option == 'U' || option == 'j' || *p || !dig)
+          {
+            new->len = strlen(comma);  
+            new->data = opt_malloc(new->len);
+            memcpy(new->data, comma, new->len);
+          }
+        else
+          {
+            new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
+            new->data = opt_malloc(new->len);
+            memcpy(new->data, comma, new->len);
+          }
+        
+        switch (option)
+          {
+          case 'j':
+            new->match_type = MATCH_USER;
+            break;
+          case 'U':
+            new->match_type = MATCH_VENDOR;
+            break; 
+          case LOPT_CIRCUIT:
+            new->match_type = MATCH_CIRCUIT;
+            break;
+          case LOPT_REMOTE:
+            new->match_type = MATCH_REMOTE;
+            break;
+          case LOPT_SUBSCR:
+            new->match_type = MATCH_SUBSCRIBER;
+            break;
+          }
+        new->next = daemon->dhcp_vendors;
+        daemon->dhcp_vendors = new;
 
-           switch (option)
-             {
-             case 'j':
-               new->match_type = MATCH_USER;
-               break;
-             case 'U':
-               new->match_type = MATCH_VENDOR;
-               break; 
-             case LOPT_CIRCUIT:
-               new->match_type = MATCH_CIRCUIT;
-               break;
-             case LOPT_REMOTE:
-               new->match_type = MATCH_REMOTE;
-               break;
-             case LOPT_SUBSCR:
-               new->match_type = MATCH_SUBSCRIBER;
-               break;
-             }
-           new->next = daemon->dhcp_vendors;
-           daemon->dhcp_vendors = new;
-         }
-       break;
+        break;
       }
       
     case LOPT_ALTPORT:   /* --dhcp-alternate-port */
@@ -2464,7 +3394,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
          comma = split(arg);
          if (!atoi_check16(arg, &daemon->dhcp_server_port) || 
              (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
-           problem = _("invalid port number");
+           ret_err(_("invalid port number"));
          if (!comma)
            daemon->dhcp_client_port = daemon->dhcp_server_port+1; 
        }
@@ -2525,15 +3455,78 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
       while (arg) {
        struct addr_list *new = opt_malloc(sizeof(struct addr_list));
        comma = split(arg);
-       if ((new->addr.s_addr = inet_addr(arg)) == (in_addr_t)-1)
-         problem = _("bad dhcp-proxy address");
+       if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
+         ret_err(_("bad dhcp-proxy address"));
        new->next = daemon->override_relays;
        daemon->override_relays = new;
        arg = comma;
       }
       break;
+
+    case LOPT_RELAY: /* --dhcp-relay */
+      {
+       struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
+       comma = split(arg);
+       new->interface = opt_string_alloc(split(comma));
+       new->iface_index = 0;
+       if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
+         {
+           new->next = daemon->relay4;
+           daemon->relay4 = new;
+         }
+#ifdef HAVE_DHCP6
+       else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
+         {
+           new->next = daemon->relay6;
+           daemon->relay6 = new;
+         }
+#endif
+       else
+         ret_err(_("Bad dhcp-relay"));
+       
+       break;
+      }
+
 #endif
       
+#ifdef HAVE_DHCP6
+    case LOPT_RA_PARAM: /* --ra-param */
+      if ((comma = split(arg)))
+       {
+         struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
+         new->lifetime = -1;
+         new->prio = 0;
+         new->name = opt_string_alloc(arg);
+         if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
+           {
+             if (*comma == 'l' || *comma == 'L')
+               new->prio = 0x18;
+             else
+               new->prio = 0x08;
+             comma = split(comma);
+           }
+          arg = split(comma);
+          if (!atoi_check(comma, &new->interval) || 
+             (arg && !atoi_check(arg, &new->lifetime)))
+           ret_err(_("bad RA-params"));
+         
+         new->next = daemon->ra_interfaces;
+         daemon->ra_interfaces = new;
+       }
+      break;
+      
+    case LOPT_DUID: /* --dhcp-duid */
+      if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
+       ret_err(_("bad DUID"));
+      else
+       {
+         daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
+         daemon->duid_config = opt_malloc(daemon->duid_config_len);
+         memcpy(daemon->duid_config, comma, daemon->duid_config_len);
+       }
+      break;
+#endif
+
     case 'V':  /* --alias */
       {
        char *dash, *a[3] = { NULL, NULL, NULL };
@@ -2555,18 +3548,18 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
        dash = split_chr(a[0], '-');
 
        if ((k < 2) || 
-           ((new->in.s_addr = inet_addr(a[0])) == (in_addr_t)-1) ||
-           ((new->out.s_addr = inet_addr(a[1])) == (in_addr_t)-1))
+           (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
+           (!(inet_pton(AF_INET, a[1], &new->out) > 0)))
          option = '?';
        
        if (k == 3)
-         new->mask.s_addr = inet_addr(a[2]);
+         inet_pton(AF_INET, a[2], &new->mask);
        
        if (dash && 
-           ((new->end.s_addr = inet_addr(dash)) == (in_addr_t)-1 ||
+           (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
             !is_same_net(new->in, new->end, new->mask) ||
             ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
-         problem = _("invalid alias range");
+         ret_err(_("invalid alias range"));
        
        break;
       }
@@ -2579,15 +3572,30 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
        comma = split(arg);
        
        if (!comma || !(domain = canonicalise_opt(arg)))
-         problem = _("bad interface name");
+         ret_err(_("bad interface name"));
        
        new = opt_malloc(sizeof(struct interface_name));
        new->next = NULL;
+       new->addr = NULL;
+       
        /* Add to the end of the list, so that first name
           of an interface is used for PTR lookups. */
        for (up = &daemon->int_names; *up; up = &((*up)->next));
        *up = new;
        new->name = domain;
+       new->family = 0;
+       arg = split_chr(comma, '/');
+       if (arg)
+         {
+           if (strcmp(arg, "4") == 0)
+             new->family = AF_INET;
+#ifdef HAVE_IPV6
+           else if (strcmp(arg, "6") == 0)
+             new->family = AF_INET6;
+#endif
+           else
+             ret_err(gen_err);
+         } 
        new->intr = opt_string_alloc(comma);
        break;
       }
@@ -2595,28 +3603,29 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
     case LOPT_CNAME: /* --cname */
       {
        struct cname *new;
-       
+       char *alias;
+       char *target;
+
        if (!(comma = split(arg)))
-         option = '?';
+         ret_err(gen_err);
+       
+       alias = canonicalise_opt(arg);
+       target = canonicalise_opt(comma);
+           
+       if (!alias || !target)
+         ret_err(_("bad CNAME"));
        else
          {
-           char *alias = canonicalise_opt(arg);
-           char *target = canonicalise_opt(comma);
-           
-           if (!alias || !target)
-             problem = _("bad CNAME");
-           else
-             {
-               for (new = daemon->cnames; new; new = new->next)
-                 if (hostname_isequal(new->alias, arg))
-                   problem = _("duplicate CNAME");
-               new = opt_malloc(sizeof(struct cname));
-               new->next = daemon->cnames;
-               daemon->cnames = new;
-               new->alias = alias;
-               new->target = target;
-             }
+           for (new = daemon->cnames; new; new = new->next)
+             if (hostname_isequal(new->alias, arg))
+               ret_err(_("duplicate CNAME"));
+           new = opt_malloc(sizeof(struct cname));
+           new->next = daemon->cnames;
+           daemon->cnames = new;
+           new->alias = alias;
+           new->target = target;
          }
+      
        break;
       }
 
@@ -2629,7 +3638,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
        
        if (!(dom = canonicalise_opt(arg)) ||
            (comma && !(target = canonicalise_opt(comma))))
-         problem = _("bad PTR record");
+         ret_err(_("bad PTR record"));
        else
          {
            new = opt_malloc(sizeof(struct ptr_record));
@@ -2660,7 +3669,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
            !atoi_check16(a[1], &order) || 
            !atoi_check16(a[2], &pref) ||
            (k == 7 && !(replace = canonicalise_opt(a[6]))))
-         problem = _("bad NAPTR record");
+         ret_err(_("bad NAPTR record"));
        else
          {
            new = opt_malloc(sizeof(struct naptr));
@@ -2676,7 +3685,39 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
          }
        break;
       }
-       
+
+    case LOPT_RR: /* dns-rr */
+      {
+               struct txt_record *new;
+       size_t len = len;
+       char *data;
+       int val;
+
+       comma = split(arg);
+       data = split(comma);
+               
+       new = opt_malloc(sizeof(struct txt_record));
+       new->next = daemon->rr;
+       daemon->rr = new;
+       
+       if (!atoi_check(comma, &val) || 
+           !(new->name = canonicalise_opt(arg)) ||
+           (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
+         ret_err(_("bad RR record"));
+               
+       new->class = val;
+       new->len = 0;
+       
+       if (data)
+         {
+           new->txt=opt_malloc(len);
+           new->len = len;
+           memcpy(new->txt, data, len);
+         }
+       
+       break;
+      }
+
     case 'Y':  /* --txt-record */
       {
        struct txt_record *new;
@@ -2689,13 +3730,11 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
        new->next = daemon->txt;
        daemon->txt = new;
        new->class = C_IN;
-       
-       if (!(new->name = canonicalise_opt(arg)))
-         {
-           problem = _("bad TXT record");
-           break;
-         }
+       new->stat = 0;
 
+       if (!(new->name = canonicalise_opt(arg)))
+         ret_err(_("bad TXT record"));
+       
        len = comma ? strlen(comma) : 0;
        len += (len/255) + 1; /* room for extra counts */
        new->txt = p = opt_malloc(len);
@@ -2735,35 +3774,35 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
        comma = split(arg);
        
        if (!(name = canonicalise_opt(arg)))
-         problem = _("bad SRV record");
-         
+         ret_err(_("bad SRV record"));
+       
        if (comma)
          {
            arg = comma;
            comma = split(arg);
-           if (!(target = canonicalise_opt(arg))
-)            problem = _("bad SRV target");
+           if (!(target = canonicalise_opt(arg)))
+             ret_err(_("bad SRV target"));
                
            if (comma)
              {
                arg = comma;
                comma = split(arg);
                if (!atoi_check16(arg, &port))
-                 problem = _("invalid port number");
+                 ret_err(_("invalid port number"));
                
                if (comma)
                  {
                    arg = comma;
                    comma = split(arg);
                    if (!atoi_check16(arg, &priority))
-                     problem = _("invalid priority");
+                     ret_err(_("invalid priority"));
                        
                    if (comma)
                      {
                        arg = comma;
                        comma = split(arg);
                        if (!atoi_check16(arg, &weight))
-                         problem = _("invalid weight");
+                         ret_err(_("invalid weight"));
                      }
                  }
              }
@@ -2781,18 +3820,123 @@ static char *one_opt(int option, char *arg, char *gen_prob, int command_line)
        break;
       }
       
-    default:
-      return _("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)");
+    case LOPT_HOST_REC: /* --host-record */
+      {
+       struct host_record *new = opt_malloc(sizeof(struct host_record));
+       memset(new, 0, sizeof(struct host_record));
+       
+       if (!arg || !(comma = split(arg)))
+         ret_err(_("Bad host-record"));
+       
+       while (arg)
+         {
+           struct all_addr addr;
+           if (inet_pton(AF_INET, arg, &addr))
+             new->addr = addr.addr.addr4;
+#ifdef HAVE_IPV6
+           else if (inet_pton(AF_INET6, arg, &addr))
+             new->addr6 = addr.addr.addr6;
+#endif
+           else
+             {
+               int nomem;
+               char *canon = canonicalise(arg, &nomem);
+               struct name_list *nl = opt_malloc(sizeof(struct name_list));
+               if (!canon)
+                 ret_err(_("Bad name in host-record"));
+
+               nl->name = canon;
+               /* keep order, so that PTR record goes to first name */
+               nl->next = NULL;
+               if (!new->names)
+                 new->names = nl;
+               else
+                 { 
+                   struct name_list *tmp;
+                   for (tmp = new->names; tmp->next; tmp = tmp->next);
+                   tmp->next = nl;
+                 }
+             }
+           
+           arg = comma;
+           comma = split(arg);
+         }
 
-    }
+       /* Keep list order */
+       if (!daemon->host_records_tail)
+         daemon->host_records = new;
+       else
+         daemon->host_records_tail->next = new;
+       new->next = NULL;
+       daemon->host_records_tail = new;
+       break;
+      }
 
-  if (problem)
-    return problem;
-  
-  if (option == '?')
-    return gen_prob;
+#ifdef HAVE_DNSSEC
+    case LOPT_DNSSEC_STAMP:
+      daemon->timestamp_file = opt_string_alloc(arg); 
+      break;
 
-  return NULL;
+    case LOPT_TRUST_ANCHOR:
+      {
+       struct ds_config *new = opt_malloc(sizeof(struct ds_config));
+       char *cp, *cp1, *keyhex, *digest, *algo = NULL;
+       int len;
+       
+       new->class = C_IN;
+
+       if ((comma = split(arg)) && (algo = split(comma)))
+         {
+           int class = 0;
+           if (strcmp(comma, "IN") == 0)
+             class = C_IN;
+           else if (strcmp(comma, "CH") == 0)
+             class = C_CHAOS;
+           else if (strcmp(comma, "HS") == 0)
+             class = C_HESIOD;
+           
+           if (class != 0)
+             {
+               new->class = class;
+               comma = algo;
+               algo = split(comma);
+             }
+         }
+                 
+               if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
+           !atoi_check16(comma, &new->keytag) || 
+           !atoi_check8(algo, &new->algo) ||
+           !atoi_check8(digest, &new->digest_type) ||
+           !(new->name = canonicalise_opt(arg)))
+         ret_err(_("bad trust anchor"));
+           
+       /* Upper bound on length */
+       len = (2*strlen(keyhex))+1;
+       new->digest = opt_malloc(len);
+       unhide_metas(keyhex);
+       /* 4034: "Whitespace is allowed within digits" */
+       for (cp = keyhex; *cp; )
+         if (isspace(*cp))
+           for (cp1 = cp; *cp1; cp1++)
+             *cp1 = *(cp1+1);
+         else
+           cp++;
+       if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
+         ret_err(_("bad HEX in trust anchor"));
+       
+       new->next = daemon->ds;
+       daemon->ds = new;
+       
+       break;
+      }
+#endif
+               
+    default:
+      ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
+      
+    }
+  
+  return 1;
 }
 
 static void read_file(char *file, FILE *f, int hard_opt)       
@@ -2802,12 +3946,13 @@ static void read_file(char *file, FILE *f, int hard_opt)
   
   while (fgets(buff, MAXDNAME, f))
     {
-      int white, i, option; ;
-      char *errmess, *p, *arg, *start;
+      int white, i;
+      volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
+      char *errmess, *p, *arg = NULL, *start;
       size_t len;
 
       /* Memory allocation failure longjmps here if mem_recover == 1 */ 
-      if (hard_opt)
+      if (option != 0 || hard_opt == LOPT_REV_SERV)
        {
          if (setjmp(mem_jmp))
            continue;
@@ -2881,7 +4026,7 @@ static void read_file(char *file, FILE *f, int hard_opt)
       else
        start[len] = 0;
       
-      if (hard_opt != 0)
+      if (option != 0)
        arg = start;
       else if ((p=strchr(start, '=')))
        {
@@ -2893,9 +4038,7 @@ static void read_file(char *file, FILE *f, int hard_opt)
       else
        arg = NULL;
 
-      if (hard_opt != 0)
-       option = hard_opt;
-      else
+      if (option == 0)
        {
          for (option = 0, i = 0; opts[i].name; i++) 
            if (strcmp(opts[i].name, start) == 0)
@@ -2910,19 +4053,21 @@ static void read_file(char *file, FILE *f, int hard_opt)
            errmess = _("extraneous parameter");
          else if (opts[i].has_arg == 1 && !arg)
            errmess = _("missing parameter");
+         else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
+           errmess = _("illegal option");
        }
-         
-      if (!errmess)
-       errmess = one_opt(option, arg, _("error"), 0);
-      
+
+    oops:
       if (errmess)
+       strcpy(daemon->namebuff, errmess);
+         
+      if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV))
        {
-       oops:
-         sprintf(buff, _("%s at line %d of %%s"), errmess, lineno);
+         sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
          if (hard_opt != 0)
-           my_syslog(LOG_ERR, buff, file);
+           my_syslog(LOG_ERR, "%s", daemon->namebuff);
          else
-           die(buff, file, EC_BADCONF);
+           die("%s", daemon->namebuff, EC_BADCONF);
        }
     }
 
@@ -2930,7 +4075,21 @@ static void read_file(char *file, FILE *f, int hard_opt)
   fclose(f);
 }
 
-static void one_file(char *file, int hard_opt)
+#ifdef HAVE_DHCP
+int option_read_dynfile(char *file, int flags)
+{
+  my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
+  
+  if (flags & AH_DHCP_HST)
+    return one_file(file, LOPT_BANK);
+  else if (flags & AH_DHCP_OPT)
+    return one_file(file, LOPT_OPTS);
+  
+  return 0;
+}
+#endif
+
+static int one_file(char *file, int hard_opt)
 {
   FILE *f;
   int nofile_ok = 0;
@@ -2951,7 +4110,7 @@ static void one_file(char *file, int hard_opt)
   if (hard_opt == 0 && strcmp(file, "-") == 0)
     {
       if (read_stdin == 1)
-       return;
+       return 1;
       read_stdin = 1;
       file = "stdin";
       f = stdin;
@@ -2967,7 +4126,7 @@ static void one_file(char *file, int hard_opt)
          
          for (r = filesread; r; r = r->next)
            if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
-             return;
+             return 1;
          
          r = safe_malloc(sizeof(struct fileread));
          r->next = filesread;
@@ -2979,14 +4138,14 @@ static void one_file(char *file, int hard_opt)
       if (!(f = fopen(file, "r")))
        {   
          if (errno == ENOENT && nofile_ok)
-           return; /* No conffile, all done. */
+           return 1; /* No conffile, all done. */
          else
            {
              char *str = _("cannot read %s: %s");
              if (hard_opt != 0)
                {
                  my_syslog(LOG_ERR, str, file, strerror(errno));
-                 return;
+                 return 0;
                }
              else
                die(str, file, EC_FILE);
@@ -2995,15 +4154,17 @@ static void one_file(char *file, int hard_opt)
     }
   
   read_file(file, f, hard_opt);
+  return 1;
 }
 
 /* expand any name which is a directory */
 struct hostsfile *expand_filelist(struct hostsfile *list)
 {
-  int i;
+  unsigned int i;
   struct hostsfile *ah;
 
-  for (i = 0, ah = list; ah; ah = ah->next)
+  /* find largest used index */
+  for (i = SRC_AH, ah = list; ah; ah = ah->next)
     {
       if (i <= ah->index)
        i = ah->index + 1;
@@ -3025,7 +4186,7 @@ struct hostsfile *expand_filelist(struct hostsfile *list)
            
            /* don't read this as a file */
            ah->flags |= AH_INACTIVE;
-
+           
            if (!(dir_stream = opendir(ah->fname)))
              my_syslog(LOG_ERR, _("cannot access directory %s: %s"), 
                        ah->fname, strerror(errno));
@@ -3097,6 +4258,22 @@ struct hostsfile *expand_filelist(struct hostsfile *list)
   return list;
 }
 
+void read_servers_file(void)
+{
+  FILE *f;
+
+  if (!(f = fopen(daemon->servers_file, "r")))
+    {
+       my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
+       return;
+    }
+  
+  mark_servers(SERV_FROM_FILE);
+  cleanup_servers();
+  
+  read_file(daemon->servers_file, f, LOPT_REV_SERV);
+}
 
 #ifdef HAVE_DHCP
 void reread_dhcp(void)
@@ -3147,8 +4324,8 @@ void reread_dhcp(void)
       for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
         if (!(hf->flags & AH_INACTIVE))
           {
-            one_file(hf->fname, LOPT_BANK);  
-            my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
+            if (one_file(hf->fname, LOPT_BANK))  
+              my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
           }
     }
 
@@ -3183,8 +4360,8 @@ void reread_dhcp(void)
       for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
        if (!(hf->flags & AH_INACTIVE))
          {
-           one_file(hf->fname, LOPT_OPTS);  
-           my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
+           if (one_file(hf->fname, LOPT_OPTS))  
+             my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
          }
     }
 }
@@ -3194,7 +4371,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
 {
   char *buff = opt_malloc(MAXDNAME);
   int option, conffile_opt = '7', testmode = 0;
-  char *errmess, *arg, *conffile = CONFFILE;
+  char *arg, *conffile = CONFFILE;
       
   opterr = 0;
 
@@ -3217,9 +4394,23 @@ void read_opts(int argc, char **argv, char *compile_opts)
   daemon->tftp_max = TFTP_MAX_CONNECTIONS;
   daemon->edns_pktsz = EDNS_PKTSZ;
   daemon->log_fac = -1;
-  add_txt("version.bind", "dnsmasq-" VERSION );
-  add_txt("authors.bind", "Simon Kelley");
-  add_txt("copyright.bind", COPYRIGHT);
+  daemon->auth_ttl = AUTH_TTL; 
+  daemon->soa_refresh = SOA_REFRESH;
+  daemon->soa_retry = SOA_RETRY;
+  daemon->soa_expiry = SOA_EXPIRY;
+
+  add_txt("version.bind", "dnsmasq-" VERSION, 0 );
+  add_txt("authors.bind", "Simon Kelley", 0);
+  add_txt("copyright.bind", COPYRIGHT, 0);
+  add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
+  add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
+  add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
+  add_txt("misses.bind", NULL, TXT_STAT_MISSES);
+  add_txt("hits.bind", NULL, TXT_STAT_HITS);
+#ifdef HAVE_AUTH
+  add_txt("auth.bind", NULL, TXT_STAT_AUTH);
+#endif
+  add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
 
   while (1) 
     {
@@ -3256,18 +4447,23 @@ void read_opts(int argc, char **argv, char *compile_opts)
        testmode = 1;
       else if (option == 'w')
        {
-         if (argc != 3 ||  strcmp(argv[2], "dhcp") != 0)
-           do_usage();
 #ifdef HAVE_DHCP
-         else
+         if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
            display_opts();
+#ifdef HAVE_DHCP6
+         else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
+           display_opts6();
+#endif
+         else
 #endif
+           do_usage();
+
          exit(0);
        }
       else if (option == 'v')
        {
          printf(_("Dnsmasq version %s  %s\n"), VERSION, COPYRIGHT);
-         printf(_("Compile time options %s\n\n"), compile_opts); 
+         printf(_("Compile time options: %s\n\n"), compile_opts); 
          printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
          printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
          printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
@@ -3281,32 +4477,39 @@ void read_opts(int argc, char **argv, char *compile_opts)
       else
        {
 #ifdef HAVE_GETOPT_LONG
-         errmess = one_opt(option, arg, _("try --help"), 1);
+         if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
 #else 
-         errmess = one_opt(option, arg, _("try -w"), 1); 
+           if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0)) 
 #endif  
-         if (errmess)
-           die(_("bad command line options: %s"), errmess, EC_BADCONF);
+           die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
        }
     }
 
   if (conffile)
-    one_file(conffile, conffile_opt);
+    {
+      one_file(conffile, conffile_opt);
+      if (conffile_opt == 0)
+       free(conffile);
+    }
 
   /* port might not be known when the address is parsed - fill in here */
   if (daemon->servers)
     {
       struct server *tmp;
       for (tmp = daemon->servers; tmp; tmp = tmp->next)
-       if (!(tmp->flags & SERV_HAS_SOURCE))
-         {
-           if (tmp->source_addr.sa.sa_family == AF_INET)
-             tmp->source_addr.in.sin_port = htons(daemon->query_port);
+       {
+         tmp->edns_pktsz = daemon->edns_pktsz;
+        
+         if (!(tmp->flags & SERV_HAS_SOURCE))
+           {
+             if (tmp->source_addr.sa.sa_family == AF_INET)
+               tmp->source_addr.in.sin_port = htons(daemon->query_port);
 #ifdef HAVE_IPV6
-           else if (tmp->source_addr.sa.sa_family == AF_INET6)
-             tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
+             else if (tmp->source_addr.sa.sa_family == AF_INET6)
+               tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
 #endif 
-         } 
+           }
+       } 
     }
   
   if (daemon->if_addrs)
@@ -3320,7 +4523,15 @@ void read_opts(int argc, char **argv, char *compile_opts)
          tmp->addr.in6.sin6_port = htons(daemon->port);
 #endif /* IPv6 */
     }
-                     
+       
+  /* create default, if not specified */
+  if (daemon->authserver && !daemon->hostmaster)
+    {
+      strcpy(buff, "hostmaster.");
+      strcat(buff, daemon->authserver);
+      daemon->hostmaster = opt_string_alloc(buff);
+    }
+  
   /* only one of these need be specified: the other defaults to the host-name */
   if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
     {
@@ -3408,6 +4619,11 @@ void read_opts(int argc, char **argv, char *compile_opts)
   else if (option_bool(OPT_DHCP_FQDN))
     die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
 
+  /* If there's access-control config, then ignore --local-service, it's intended
+     as a system default to keep otherwise unconfigured installations safe. */
+  if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
+    reset_option_bool(OPT_LOCAL_SERVICE); 
+
   if (testmode)
     {
       fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
diff --git a/src/outpacket.c b/src/outpacket.c
new file mode 100644 (file)
index 0000000..5b1ff93
--- /dev/null
@@ -0,0 +1,108 @@
+/* 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"
+#ifdef HAVE_DHCP6
+
+static size_t outpacket_counter;
+
+void end_opt6(int container)
+{
+   void *p = daemon->outpacket.iov_base + container + 2;
+   u16 len = outpacket_counter - container - 4 ;
+   
+   PUTSHORT(len, p);
+}
+
+int save_counter(int newval)
+{
+  int ret = outpacket_counter;
+  if (newval != -1)
+    outpacket_counter = newval;
+
+  return ret;
+}
+
+void *expand(size_t headroom)
+{
+  void *ret;
+
+  if (expand_buf(&daemon->outpacket, outpacket_counter + headroom))
+    {
+      ret = daemon->outpacket.iov_base + outpacket_counter;
+      outpacket_counter += headroom;
+      return ret;
+    }
+  
+  return NULL;
+}
+    
+int new_opt6(int opt)
+{
+  int ret = outpacket_counter;
+  void *p;
+
+  if ((p = expand(4)))
+    {
+      PUTSHORT(opt, p);
+      PUTSHORT(0, p);
+    }
+
+  return ret;
+}
+
+void *put_opt6(void *data, size_t len)
+{
+  void *p;
+
+  if ((p = expand(len)) && data)
+    memcpy(p, data, len);   
+  
+  return p;
+}
+  
+void put_opt6_long(unsigned int val)
+{
+  void *p;
+  
+  if ((p = expand(4)))  
+    PUTLONG(val, p);
+}
+
+void put_opt6_short(unsigned int val)
+{
+  void *p;
+
+  if ((p = expand(2)))
+    PUTSHORT(val, p);   
+}
+
+void put_opt6_char(unsigned int val)
+{
+  unsigned char *p;
+
+  if ((p = expand(1)))
+    *p = val;   
+}
+
+void put_opt6_string(char *s)
+{
+  put_opt6(s, strlen(s));
+}
+
+#endif
diff --git a/src/poll.c b/src/poll.c
new file mode 100644 (file)
index 0000000..d71b1b9
--- /dev/null
@@ -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++;
+     }
+}
diff --git a/src/radv-protocol.h b/src/radv-protocol.h
new file mode 100644 (file)
index 0000000..4cc1ea4
--- /dev/null
@@ -0,0 +1,58 @@
+/* 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/>.
+*/
+
+#define ALL_NODES                 "FF02::1"
+#define ALL_ROUTERS               "FF02::2"
+
+struct ping_packet {
+  u8 type, code;
+  u16 checksum;
+  u16 identifier;
+  u16 sequence_no;
+};
+
+struct ra_packet {
+  u8 type, code;
+  u16 checksum;
+  u8 hop_limit, flags;
+  u16 lifetime;
+  u32 reachable_time;
+  u32 retrans_time;
+};
+
+struct neigh_packet {
+  u8 type, code;
+  u16 checksum;
+  u16 reserved;
+  struct in6_addr target;
+};
+
+struct prefix_opt {
+  u8 type, len, prefix_len, flags;
+  u32 valid_lifetime, preferred_lifetime, reserved;
+  struct in6_addr prefix;
+};
+
+#define ICMP6_OPT_SOURCE_MAC   1
+#define ICMP6_OPT_PREFIX       3
+#define ICMP6_OPT_MTU          5
+#define ICMP6_OPT_ADV_INTERVAL 7
+#define ICMP6_OPT_RT_INFO     24
+#define ICMP6_OPT_RDNSS       25
+#define ICMP6_OPT_DNSSL       31
+
+
+
diff --git a/src/radv.c b/src/radv.c
new file mode 100644 (file)
index 0000000..39f1e92
--- /dev/null
@@ -0,0 +1,976 @@
+/* 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/>.
+*/
+
+
+/* NB. This code may be called during a DHCPv4 or transaction which is in ping-wait
+   It therefore cannot use any DHCP buffer resources except outpacket, which is
+   not used by DHCPv4 code. This code may also be called when DHCP 4 or 6 isn't
+   active, so we ensure that outpacket is allocated here too */
+
+#include "dnsmasq.h"
+
+#ifdef HAVE_DHCP6
+
+#include <netinet/icmp6.h>
+
+struct ra_param {
+  time_t now;
+  int ind, managed, other, found_context, first, adv_router;
+  char *if_name;
+  struct dhcp_netid *tags;
+  struct in6_addr link_local, link_global, ula;
+  unsigned int glob_pref_time, link_pref_time, ula_pref_time, adv_interval, prio;
+};
+
+struct search_param {
+  time_t now; int iface;
+  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);
+static int iface_search(struct in6_addr *local,  int prefix,
+                       int scope, int if_index, int flags, 
+                       int prefered, int valid, void *vparam);
+static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm);
+static void new_timeout(struct dhcp_context *context, char *iface_name, time_t now);
+static unsigned int calc_lifetime(struct ra_interface *ra);
+static unsigned int calc_interval(struct ra_interface *ra);
+static unsigned int calc_prio(struct ra_interface *ra);
+static struct ra_interface *find_iface_param(char *iface);
+
+static int hop_limit;
+
+void ra_init(time_t now)
+{
+  struct icmp6_filter filter;
+  int fd;
+#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
+  int class = IPTOS_CLASS_CS6;
+#endif
+  int val = 255; /* radvd uses this value */
+  socklen_t len = sizeof(int);
+  struct dhcp_context *context;
+  
+  /* ensure this is around even if we're not doing DHCPv6 */
+  expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
+  /* See if we're guessing SLAAC addresses, if so we need to recieve ping replies */
+  for (context = daemon->dhcp6; context; context = context->next)
+    if ((context->flags & CONTEXT_RA_NAME))
+      break;
+  
+  /* Need ICMP6 socket for transmission for DHCPv6 even when not doing RA. */
+
+  ICMP6_FILTER_SETBLOCKALL(&filter);
+  if (daemon->doing_ra)
+    {
+      ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
+      if (context)
+       ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
+    }
+  
+  if ((fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1 ||
+      getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hop_limit, &len) ||
+#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
+      setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
+#endif
+      !fix_fd(fd) ||
+      !set_ipv6pktinfo(fd) ||
+      setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)) ||
+      setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) ||
+      setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) == -1)
+    die (_("cannot create ICMPv6 socket: %s"), NULL, EC_BADNET);
+  
+   daemon->icmp6fd = fd;
+   
+   if (daemon->doing_ra)
+     ra_start_unsolicted(now, NULL);
+}
+
+void ra_start_unsolicted(time_t now, struct dhcp_context *context)
+{   
+   /* init timers so that we do ra's for some/all soon. some ra_times will end up zeroed
+     if it's not appropriate to advertise those contexts.
+     This gets re-called on a netlink route-change to re-do the advertisement
+     and pick up new interfaces */
+  
+  if (context)
+    context->ra_short_period_start = context->ra_time = now;
+  else
+    for (context = daemon->dhcp6; context; context = context->next)
+      if (!(context->flags & CONTEXT_TEMPLATE))
+       {
+         context->ra_time = now + (rand16()/13000); /* range 0 - 5 */
+         /* re-do frequently for a minute or so, in case the first gets lost. */
+         context->ra_short_period_start = now;
+       }
+}
+
+void icmp6_packet(time_t now)
+{
+  char interface[IF_NAMESIZE+1];
+  ssize_t sz; 
+  int if_index = 0;
+  struct cmsghdr *cmptr;
+  struct msghdr msg;
+  union {
+    struct cmsghdr align; /* this ensures alignment */
+    char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+  } control_u;
+  struct sockaddr_in6 from;
+  unsigned char *packet;
+  struct iname *tmp;
+
+  /* Note: use outpacket for input buffer */
+  msg.msg_control = control_u.control6;
+  msg.msg_controllen = sizeof(control_u);
+  msg.msg_flags = 0;
+  msg.msg_name = &from;
+  msg.msg_namelen = sizeof(from);
+  msg.msg_iov = &daemon->outpacket;
+  msg.msg_iovlen = 1;
+  
+  if ((sz = recv_dhcp_packet(daemon->icmp6fd, &msg)) == -1 || sz < 8)
+    return;
+   
+  packet = (unsigned char *)daemon->outpacket.iov_base;
+  
+  for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+    if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
+      {
+       union {
+         unsigned char *c;
+         struct in6_pktinfo *p;
+       } p;
+       p.c = CMSG_DATA(cmptr);
+        
+       if_index = p.p->ipi6_ifindex;
+      }
+  
+  if (!indextoname(daemon->icmp6fd, if_index, interface))
+    return;
+    
+  if (!iface_check(AF_LOCAL, NULL, interface, NULL))
+    return;
+  
+  for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
+    if (tmp->name && wildcard_match(tmp->name, interface))
+      return;
+  if (packet[1] != 0)
+    return;
+  
+  if (packet[0] == ICMP6_ECHO_REPLY)
+    lease_ping_reply(&from.sin6_addr, packet, interface); 
+  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)
+       {
+         print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2);
+         mac = daemon->namebuff;
+       }
+         
+      if (!option_bool(OPT_QUIET_RA))
+       my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
+
+      /* 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_alias(time_t now, int iface, char *iface_name, struct in6_addr *dest, int send_iface)
+{
+  struct ra_packet *ra;
+  struct ra_param parm;
+  struct sockaddr_in6 addr;
+  struct dhcp_context *context, *tmp,  **up;
+  struct dhcp_netid iface_id;
+  struct dhcp_opt *opt_cfg;
+  struct ra_interface *ra_param = find_iface_param(iface_name);
+  int done_dns = 0, old_prefix = 0;
+  unsigned int min_pref_time;
+#ifdef HAVE_LINUX_NETWORK
+  FILE *f;
+#endif
+  
+  parm.ind = iface;
+  parm.managed = 0;
+  parm.other = 0;
+  parm.found_context = 0;
+  parm.adv_router = 0;
+  parm.if_name = iface_name;
+  parm.first = 1;
+  parm.now = now;
+  parm.glob_pref_time = parm.link_pref_time = parm.ula_pref_time = 0;
+  parm.adv_interval = calc_interval(ra_param);
+  parm.prio = calc_prio(ra_param);
+  
+  save_counter(0);
+  ra = expand(sizeof(struct ra_packet));
+  
+  ra->type = ND_ROUTER_ADVERT;
+  ra->code = 0;
+  ra->hop_limit = hop_limit;
+  ra->flags = parm.prio;
+  ra->lifetime = htons(calc_lifetime(ra_param));
+  ra->reachable_time = 0;
+  ra->retrans_time = 0;
+
+  /* set tag with name == interface */
+  iface_id.net = iface_name;
+  iface_id.next = NULL;
+  parm.tags = &iface_id; 
+  
+  for (context = daemon->dhcp6; context; context = context->next)
+    {
+      context->flags &= ~CONTEXT_RA_DONE;
+      context->netid.next = &context->netid;
+    }
+
+  if (!iface_enumerate(AF_INET6, &parm, add_prefixes))
+    return;
+
+  /* Find smallest preferred time within address classes,
+     to use as lifetime for options. This is a rather arbitrary choice. */
+  min_pref_time = 0xffffffff;
+  if (parm.glob_pref_time != 0 && parm.glob_pref_time < min_pref_time)
+    min_pref_time = parm.glob_pref_time;
+  
+  if (parm.ula_pref_time != 0 && parm.ula_pref_time < min_pref_time)
+    min_pref_time = parm.ula_pref_time;
+
+  if (parm.link_pref_time != 0 && parm.link_pref_time < min_pref_time)
+    min_pref_time = parm.link_pref_time;
+
+  /* Look for constructed contexts associated with addresses which have gone, 
+     and advertise them with preferred_time == 0  RFC 6204 4.3 L-13 */
+  for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
+    {
+      tmp = context->next;
+
+      if (context->if_index == iface && (context->flags & CONTEXT_OLD))
+       {
+         unsigned int old = difftime(now, context->address_lost_time);
+         
+         if (old > context->saved_valid)
+           {
+             /* We've advertised this enough, time to go */
+             *up = context->next;
+             free(context);
+           }
+         else
+           {
+             struct prefix_opt *opt;
+             struct in6_addr local = context->start6;
+             int do_slaac = 0;
+
+             old_prefix = 1;
+
+             /* zero net part of address */
+             setaddr6part(&local, addr6part(&local) & ~((context->prefix == 64) ? (u64)-1LL : (1LLU << (128 - context->prefix)) - 1LLU));
+            
+             
+             if (context->flags & CONTEXT_RA)
+               {
+                 do_slaac = 1;
+                 if (context->flags & CONTEXT_DHCP)
+                   {
+                     parm.other = 1; 
+                     if (!(context->flags & CONTEXT_RA_STATELESS))
+                       parm.managed = 1;
+                   }
+               }
+             else
+               {
+                 /* don't do RA for non-ra-only unless --enable-ra is set */
+                 if (option_bool(OPT_RA))
+                   {
+                     parm.managed = 1;
+                     parm.other = 1;
+                   }
+               }
+
+             if ((opt = expand(sizeof(struct prefix_opt))))
+               {
+                 opt->type = ICMP6_OPT_PREFIX;
+                 opt->len = 4;
+                 opt->prefix_len = context->prefix;
+                 /* 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; 
+                 opt->prefix = local;
+                 
+                 inet_ntop(AF_INET6, &local, daemon->addrbuff, ADDRSTRLEN);
+                 if (!option_bool(OPT_QUIET_RA))
+                   my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s old prefix", iface_name, daemon->addrbuff);                    
+               }
+          
+             up = &context->next;
+           }
+       }
+      else
+       up = &context->next;
+    }
+    
+  /* If we're advertising only old prefixes, set router lifetime to zero. */
+  if (old_prefix && !parm.found_context)
+    ra->lifetime = htons(0);
+
+  /* No prefixes to advertise. */
+  if (!old_prefix && !parm.found_context)
+    return; 
+  
+  /* If we're sending router address instead of prefix in at least on prefix,
+     include the advertisement interval option. */
+  if (parm.adv_router)
+    {
+      put_opt6_char(ICMP6_OPT_ADV_INTERVAL);
+      put_opt6_char(1);
+      put_opt6_short(0);
+      /* interval value is in milliseconds */
+      put_opt6_long(1000 * calc_interval(find_iface_param(iface_name)));
+    }
+
+#ifdef HAVE_LINUX_NETWORK
+  /* Note that IPv6 MTU is not necessarilly the same as the IPv4 MTU
+     available from SIOCGIFMTU */
+  sprintf(daemon->namebuff, "/proc/sys/net/ipv6/conf/%s/mtu", iface_name);
+  if ((f = fopen(daemon->namebuff, "r")))
+    {
+      if (fgets(daemon->namebuff, MAXDNAME, f))
+       {
+         put_opt6_char(ICMP6_OPT_MTU);
+         put_opt6_char(1);
+         put_opt6_short(0);
+         put_opt6_long(atoi(daemon->namebuff));
+       }
+      fclose(f);
+    }
+#endif
+     
+  iface_enumerate(AF_LOCAL, &send_iface, add_lla);
+  /* RDNSS, RFC 6106, use relevant DHCP6 options */
+  (void)option_filter(parm.tags, NULL, daemon->dhcp_opts6);
+  
+  for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
+    {
+      int i;
+      
+      /* netids match and not encapsulated? */
+      if (!(opt_cfg->flags & DHOPT_TAGOK))
+        continue;
+      
+      if (opt_cfg->opt == OPTION6_DNS_SERVER)
+        {
+         struct in6_addr *a;
+         int len;
+
+         done_dns = 1;
+
+          if (opt_cfg->len == 0)
+           continue;
+         
+         /* reduce len for any addresses we can't substitute */
+         for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, i = 0; 
+              i < opt_cfg->len; i += IN6ADDRSZ, a++)
+           if ((IN6_IS_ADDR_UNSPECIFIED(a) && parm.glob_pref_time == 0) ||
+               (IN6_IS_ADDR_ULA_ZERO(a) && parm.ula_pref_time == 0) ||
+               (IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && parm.link_pref_time == 0))
+             len -= IN6ADDRSZ;
+
+         if (len != 0)
+           {
+             put_opt6_char(ICMP6_OPT_RDNSS);
+             put_opt6_char((len/8) + 1);
+             put_opt6_short(0);
+             put_opt6_long(min_pref_time);
+        
+             for (a = (struct in6_addr *)opt_cfg->val, i = 0; i <  opt_cfg->len; i += IN6ADDRSZ, a++)
+               if (IN6_IS_ADDR_UNSPECIFIED(a))
+                 {
+                   if (parm.glob_pref_time != 0)
+                     put_opt6(&parm.link_global, IN6ADDRSZ);
+                 }
+               else if (IN6_IS_ADDR_ULA_ZERO(a))
+                 {
+                   if (parm.ula_pref_time != 0)
+                   put_opt6(&parm.ula, IN6ADDRSZ);
+                 }
+               else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
+                 {
+                   if (parm.link_pref_time != 0)
+                     put_opt6(&parm.link_local, IN6ADDRSZ);
+                 }
+               else
+                 put_opt6(a, IN6ADDRSZ);
+           }
+       }
+      
+      if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
+       {
+         int len = ((opt_cfg->len+7)/8);
+         
+         put_opt6_char(ICMP6_OPT_DNSSL);
+         put_opt6_char(len + 1);
+         put_opt6_short(0);
+         put_opt6_long(min_pref_time); 
+         put_opt6(opt_cfg->val, opt_cfg->len);
+         
+         /* pad */
+         for (i = opt_cfg->len; i < len * 8; i++)
+           put_opt6_char(0);
+       }
+    }
+       
+  if (daemon->port == NAMESERVER_PORT && !done_dns && parm.link_pref_time != 0)
+    {
+      /* default == us, as long as we are supplying DNS service. */
+      put_opt6_char(ICMP6_OPT_RDNSS);
+      put_opt6_char(3);
+      put_opt6_short(0);
+      put_opt6_long(min_pref_time); 
+      put_opt6(&parm.link_local, IN6ADDRSZ);
+    }
+
+  /* set managed bits unless we're providing only RA on this link */
+  if (parm.managed)
+    ra->flags |= 0x80; /* M flag, managed, */
+   if (parm.other)
+    ra->flags |= 0x40; /* O flag, other */ 
+                       
+  /* decide where we're sending */
+  memset(&addr, 0, sizeof(addr));
+#ifdef HAVE_SOCKADDR_SA_LEN
+  addr.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+  addr.sin6_family = AF_INET6;
+  addr.sin6_port = htons(IPPROTO_ICMPV6);
+  if (dest)
+    {
+      addr.sin6_addr = *dest;
+      if (IN6_IS_ADDR_LINKLOCAL(dest) ||
+         IN6_IS_ADDR_MC_LINKLOCAL(dest))
+       addr.sin6_scope_id = iface;
+    }
+  else
+    {
+      inet_pton(AF_INET6, ALL_NODES, &addr.sin6_addr); 
+      setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &send_iface, sizeof(send_iface));
+    }
+  
+  while (retry_send(sendto(daemon->icmp6fd, daemon->outpacket.iov_base, 
+                          save_counter(0), 0, (struct sockaddr *)&addr, 
+                          sizeof(addr))));
+  
+}
+
+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)
+{
+  struct ra_param *param = vparam;
+
+  (void)scope; /* warning */
+  
+  if (if_index == param->ind)
+    {
+      if (IN6_IS_ADDR_LINKLOCAL(local))
+       {
+         /* Can there be more than one LL address?
+            Select the one with the longest preferred time 
+            if there is. */
+         if (preferred > param->link_pref_time)
+           {
+             param->link_pref_time = preferred;
+             param->link_local = *local;
+           }
+       }
+      else if (!IN6_IS_ADDR_LOOPBACK(local) &&
+              !IN6_IS_ADDR_MULTICAST(local))
+       {
+         int real_prefix = 0;
+         int do_slaac = 0;
+         int deprecate  = 0;
+         int constructed = 0;
+         int adv_router = 0;
+         int off_link = 0;
+         unsigned int time = 0xffffffff;
+         struct dhcp_context *context;
+         
+         for (context = daemon->dhcp6; context; context = context->next)
+           if (!(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
+               prefix <= context->prefix &&
+               is_same_net6(local, &context->start6, context->prefix) &&
+               is_same_net6(local, &context->end6, context->prefix))
+             {
+               context->saved_valid = valid;
+
+               if (context->flags & CONTEXT_RA) 
+                 {
+                   do_slaac = 1;
+                   if (context->flags & CONTEXT_DHCP)
+                     {
+                       param->other = 1; 
+                       if (!(context->flags & CONTEXT_RA_STATELESS))
+                         param->managed = 1;
+                     }
+                 }
+               else
+                 {
+                   /* don't do RA for non-ra-only unless --enable-ra is set */
+                   if (!option_bool(OPT_RA))
+                     continue;
+                   param->managed = 1;
+                   param->other = 1;
+                 }
+
+               /* Configured to advertise router address, not prefix. See RFC 3775 7.2 
+                In this case we do all addresses associated with a context, 
+                hence the real_prefix setting here. */
+               if (context->flags & CONTEXT_RA_ROUTER)
+                 {
+                   adv_router = 1;
+                   param->adv_router = 1;
+                   real_prefix = context->prefix;
+                 }
+
+               /* find floor time, don't reduce below 3 * RA interval. */
+               if (time > context->lease_time)
+                 {
+                   time = context->lease_time;
+                   if (time < ((unsigned int)(3 * param->adv_interval)))
+                     time = 3 * param->adv_interval;
+                 }
+
+               if (context->flags & CONTEXT_DEPRECATE)
+                 deprecate = 1;
+               
+               if (context->flags & CONTEXT_CONSTRUCTED)
+                 constructed = 1;
+
+
+               /* collect dhcp-range tags */
+               if (context->netid.next == &context->netid && context->netid.net)
+                 {
+                   context->netid.next = param->tags;
+                   param->tags = &context->netid;
+                 }
+                 
+               /* subsequent prefixes on the same interface 
+                  and subsequent instances of this prefix don't need timers.
+                  Be careful not to find the same prefix twice with different
+                  addresses unless we're advertising the actual addresses. */
+               if (!(context->flags & CONTEXT_RA_DONE))
+                 {
+                   if (!param->first)
+                     context->ra_time = 0;
+                   context->flags |= CONTEXT_RA_DONE;
+                   real_prefix = context->prefix;
+                    off_link = (context->flags & CONTEXT_RA_OFF_LINK);
+                 }
+
+               param->first = 0;       
+               param->found_context = 1;
+             }
+
+         /* configured time is ceiling */
+         if (!constructed || valid > time)
+           valid = time;
+         
+         if (flags & IFACE_DEPRECATED)
+           preferred = 0;
+         
+         if (deprecate)
+           time = 0;
+         
+         /* configured time is ceiling */
+         if (!constructed || preferred > time)
+           preferred = time;
+         
+         if (IN6_IS_ADDR_ULA(local))
+           {
+             if (preferred > param->ula_pref_time)
+               {
+                 param->ula_pref_time = preferred;
+                 param->ula = *local;
+               }
+           }
+         else 
+           {
+             if (preferred > param->glob_pref_time)
+               {
+                 param->glob_pref_time = preferred;
+                 param->link_global = *local;
+               }
+           }
+         
+         if (real_prefix != 0)
+           {
+             struct prefix_opt *opt;
+                     
+             if ((opt = expand(sizeof(struct prefix_opt))))
+               {
+                 /* zero net part of address */
+                 if (!adv_router)
+                   setaddr6part(local, addr6part(local) & ~((real_prefix == 64) ? (u64)-1LL : (1LLU << (128 - real_prefix)) - 1LLU));
+                 
+                 opt->type = ICMP6_OPT_PREFIX;
+                 opt->len = 4;
+                 opt->prefix_len = real_prefix;
+                 /* 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)
+                   opt->flags |= 0x20;
+                 opt->valid_lifetime = htonl(valid);
+                 opt->preferred_lifetime = htonl(preferred);
+                 opt->reserved = 0; 
+                 opt->prefix = *local;
+                 
+                 inet_ntop(AF_INET6, local, daemon->addrbuff, ADDRSTRLEN);
+                 if (!option_bool(OPT_QUIET_RA))
+                   my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);                   
+               }
+           }
+       }
+    }          
+  return 1;
+}
+
+static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm)
+{
+  (void)type;
+
+  if (index == *((int *)parm))
+    {
+      /* size is in units of 8 octets and includes type and length (2 bytes)
+        add 7 to round up */
+      int len = (maclen + 9) >> 3;
+      unsigned char *p = expand(len << 3);
+      memset(p, 0, len << 3);
+      *p++ = ICMP6_OPT_SOURCE_MAC;
+      *p++ = len;
+      memcpy(p, mac, maclen);
+
+      return 0;
+    }
+
+  return 1;
+}
+
+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;
+
+  while (1)
+    {
+      /* find overdue events, and time of first future event */
+      for (next_event = 0, context = daemon->dhcp6; context; context = context->next)
+       if (context->ra_time != 0)
+         {
+           if (difftime(context->ra_time, now) <= 0.0)
+             break; /* overdue */
+           
+           if (next_event == 0 || difftime(next_event, context->ra_time) > 0.0)
+             next_event = context->ra_time;
+         }
+      
+      /* none overdue */
+      if (!context)
+       break;
+      
+      if ((context->flags & CONTEXT_OLD) && 
+         context->if_index != 0 && 
+         indextoname(daemon->icmp6fd, context->if_index, param.name))
+       {
+         /* A context for an old address. We'll not find the interface by 
+            looking for addresses, but we know it anyway, since the context is
+            constructed */
+         param.iface = context->if_index;
+         new_timeout(context, param.name, now);
+       }
+      else if (iface_enumerate(AF_INET6, &param, iface_search))
+       /* There's a context overdue, but we can't find an interface
+          associated with it, because it's for a subnet we dont 
+          have an interface on. Probably we're doing DHCP on
+          a remote subnet via a relay. Zero the timer, since we won't
+          ever be able to send ra's and satistfy it. */
+       context->ra_time = 0;
+      
+      if (param.iface != 0 &&
+         iface_check(AF_LOCAL, NULL, param.name, NULL))
+       {
+         struct iname *tmp;
+         for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
+           if (tmp->name && wildcard_match(tmp->name, param.name))
+             break;
+         if (!tmp)
+            {
+              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)
+{
+  struct search_param *param = vparam;
+  struct dhcp_context *context;
+
+  (void)scope;
+  (void)preferred;
+  (void)valid;
+  for (context = daemon->dhcp6; context; context = context->next)
+    if (!(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
+       prefix <= context->prefix &&
+       is_same_net6(local, &context->start6, context->prefix) &&
+       is_same_net6(local, &context->end6, context->prefix) &&
+       context->ra_time != 0 && 
+       difftime(context->ra_time, param->now) <= 0.0)
+      {
+       /* found an interface that's overdue for RA determine new 
+          timeout value and arrange for RA to be sent unless interface is
+          still doing DAD.*/
+       
+       if (!(flags & IFACE_TENTATIVE))
+         param->iface = if_index;
+       
+       /* should never fail */
+       if (!indextoname(daemon->icmp6fd, if_index, param->name))
+         {
+           param->iface = 0;
+           return 0;
+         }
+       
+       new_timeout(context, param->name, param->now);
+       
+       /* zero timers for other contexts on the same subnet, so they don't timeout 
+          independently */
+       for (context = context->next; context; context = context->next)
+         if (prefix <= context->prefix &&
+             is_same_net6(local, &context->start6, context->prefix) &&
+             is_same_net6(local, &context->end6, context->prefix))
+           context->ra_time = 0;
+       
+       return 0; /* found, abort */
+      }
+  
+  return 1; /* keep searching */
+}
+static void new_timeout(struct dhcp_context *context, char *iface_name, time_t now)
+{
+  if (difftime(now, context->ra_short_period_start) < 60.0)
+    /* range 5 - 20 */
+    context->ra_time = now + 5 + (rand16()/4400);
+  else
+    {
+      /* range 3/4 - 1 times MaxRtrAdvInterval */
+      unsigned int adv_interval = calc_interval(find_iface_param(iface_name));
+      context->ra_time = now + (3 * adv_interval)/4 + ((adv_interval * (unsigned int)rand16()) >> 18);
+    }
+}
+
+static struct ra_interface *find_iface_param(char *iface)
+{
+  struct ra_interface *ra;
+  
+  for (ra = daemon->ra_interfaces; ra; ra = ra->next)
+    if (wildcard_match(ra->name, iface))
+      return ra;
+
+  return NULL;
+}
+
+static unsigned int calc_interval(struct ra_interface *ra)
+{
+  int interval = 600;
+  
+  if (ra && ra->interval != 0)
+    {
+      interval = ra->interval;
+      if (interval > 1800)
+       interval = 1800;
+      else if (interval < 4)
+       interval = 4;
+    }
+  
+  return (unsigned int)interval;
+}
+
+static unsigned int calc_lifetime(struct ra_interface *ra)
+{
+  int lifetime, interval = (int)calc_interval(ra);
+  
+  if (!ra || ra->lifetime == -1) /* not specified */
+    lifetime = 3 * interval;
+  else
+    {
+      lifetime = ra->lifetime;
+      if (lifetime < interval && lifetime != 0)
+       lifetime = interval;
+      else if (lifetime > 9000)
+       lifetime = 9000;
+    }
+  
+  return (unsigned int)lifetime;
+}
+
+static unsigned int calc_prio(struct ra_interface *ra)
+{
+  if (ra)
+    return ra->prio;
+  
+  return 0;
+}
+
+#endif
index 889c1f0..56647b0 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
 
 #include "dnsmasq.h"
 
-static int add_resource_record(struct dns_header *header, char *limit, int *truncp, 
-                              unsigned int nameoffset, unsigned char **pp, 
-                              unsigned long ttl, unsigned int *offset, unsigned short type, 
-                              unsigned short class, char *format, ...);
-
-#define CHECK_LEN(header, pp, plen, len) \
-    ((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
-
-#define ADD_RDLEN(header, pp, plen, len) \
-    (!CHECK_LEN(header, pp, plen, len) ? 0 : (long)((pp) += (len)), 1)
-
-static int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, 
-                       char *name, int isExtract, int extrabytes)
+int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, 
+                char *name, int isExtract, int extrabytes)
 {
   unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
-  unsigned int j, l, hops = 0;
+  unsigned int j, l, namelen = 0, hops = 0;
   int retvalue = 1;
   
   if (isExtract)
@@ -88,49 +77,10 @@ static int extract_name(struct dns_header *header, size_t plen, unsigned char **
          
          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+9 chars */
-         if (cp - (unsigned char *)name + digs + 9 >= 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. */
-         if (cp - (unsigned char *)name + l + 1 >= MAXDNAME)
+         namelen += l + 1; /* include period */
+         if (namelen >= MAXDNAME)
            return 0;
          if (!CHECK_LEN(header, p, plen, l))
            return 0;
@@ -139,8 +89,21 @@ static int extract_name(struct dns_header *header, size_t plen, unsigned char **
            if (isExtract)
              {
                unsigned char c = *p;
-               if (isascii(c) && !iscntrl(c) && c != '.')
-                 *cp++ = *p;
+#ifdef HAVE_DNSSEC
+               if (option_bool(OPT_DNSSEC_VALID))
+                 {
+                   if (c == 0 || c == '.' || c == NAME_ESCAPE)
+                     {
+                       *cp++ = NAME_ESCAPE;
+                       *cp++ = c+1;
+                     }
+                   else
+                     *cp++ = c; 
+                 }
+               else
+#endif
+               if (c != 0 && c != '.')
+                 *cp++ = c;
                else
                  return 0;
              }
@@ -155,25 +118,32 @@ static int extract_name(struct dns_header *header, size_t plen, unsigned char **
                    cp++;
                    if (c1 >= 'A' && c1 <= 'Z')
                      c1 += 'a' - 'A';
+#ifdef HAVE_DNSSEC
+                   if (option_bool(OPT_DNSSEC_VALID) && c1 == NAME_ESCAPE)
+                     c1 = (*cp++)-1;
+#endif
+                   
                    if (c2 >= 'A' && c2 <= 'Z')
                      c2 += 'a' - 'A';
-                   
+                    
                    if (c1 != c2)
                      retvalue =  2;
                  }
              }
-         
+           
          if (isExtract)
            *cp++ = '.';
          else if (*cp != 0 && *cp++ != '.')
            retvalue = 2;
        }
+      else
+       return 0; /* label types 0x40 and 0x80 not supported */
     }
 }
  
 /* Max size of input string (for IPv6) is 75 chars.) */
 #define MAXARPANAME 75
-static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
+int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
 {
   int j;
   char name[MAXARPANAME+1], *cp1;
@@ -278,7 +248,7 @@ static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
   return 0;
 }
 
-static unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes)
+unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes)
 {
   while(1)
     {
@@ -333,7 +303,7 @@ static unsigned char *skip_name(unsigned char *ansp, struct dns_header *header,
   return ansp;
 }
 
-static unsigned char *skip_questions(struct dns_header *header, size_t plen)
+unsigned char *skip_questions(struct dns_header *header, size_t plen)
 {
   int q;
   unsigned char *ansp = (unsigned char *)(header+1);
@@ -348,7 +318,7 @@ static unsigned char *skip_questions(struct dns_header *header, size_t plen)
   return ansp;
 }
 
-static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)
+unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)
 {
   int i, rdlen;
   
@@ -371,6 +341,7 @@ static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_he
    than CRC the raw bytes, since replies might be compressed differently. 
    We ignore case in the names for the same reason. Return all-ones
    if there is not question section. */
+#ifndef HAVE_DNSSEC
 unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)
 {
   int q;
@@ -411,7 +382,7 @@ unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)
 
   return crc;
 }
-
+#endif
 
 size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen)
 {
@@ -504,7 +475,7 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t
       else if (is_sign && 
               i == arcount - 1 && 
               class == C_ANY && 
-              (type == T_SIG || type == T_TSIG))
+              type == T_TSIG)
        *is_sign = 1;
     }
   
@@ -517,94 +488,117 @@ struct macparm {
   size_t plen;
   union mysockaddr *l3;
 };
-
-static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
-{
-  struct macparm *parm = parmv;
-  int match = 0;
-  unsigned short rdlen;
-  struct dns_header *header = parm->header;
-  unsigned char *lenp, *datap, *p;
-  
-  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 */
+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 (ntohs(header->arcount) == 0)
+  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, parm->plen)) ||
+      if (!(p = skip_questions(header, plen)) ||
          !(p = skip_section(p, 
-                            ntohs(header->ancount) + ntohs(header->nscount), 
-                            header, parm->plen)))
-       return 0;
+                            ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount)
+                            header, plen)))
+       return plen;
       *p++ = 0; /* empty name */
       PUTSHORT(T_OPT, p);
-      PUTSHORT(PACKETSZ, p); /* max packet length - is 512 suitable default for non-EDNS0 resolvers? */
-      PUTLONG(0, p);    /* extended RCODE */
+      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)maclen) > (parm->limit - (p + 4)))
-       return 0; /* Too big */
-      header->arcount = htons(1);
+      if (((ssize_t)optlen) > (limit - (p + 4)))
+       return plen; /* Too big */
+      header->arcount = htons(ntohs(header->arcount) + 1);
       datap = p;
     }
   else
     {
-      int i, is_sign;
-      unsigned short code, len;
+      int i;
+      unsigned short code, len, flags;
       
+      /* Must be at the end, if exists */
       if (ntohs(header->arcount) != 1 ||
-         !(p = find_pseudoheader(header, parm->plen, NULL, NULL, &is_sign)) ||
          is_sign ||
-         (!(p = skip_name(p, header, parm->plen, 10))))
-       return 0;
-      
-      p += 8; /* skip UDP length and RCODE */
+         (!(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, parm->plen, rdlen))
-       return 0; /* bad packet */
+      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 == EDNS0_OPTION_MAC)
-           return 0;
+         if (code == optno)
+           return plen;
          p += len;
        }
       
-      if (((ssize_t)maclen) > (parm->limit - (p + 4)))
-       return 0; /* Too big */
+      if (((ssize_t)optlen) > (limit - (p + 4)))
+       return plen; /* Too big */
     }
   
-  PUTSHORT(EDNS0_OPTION_MAC, p);
-  PUTSHORT(maclen, p);
-  memcpy(p, mac, maclen);
-  p += maclen;  
+  if (optno != 0)
+    {
+      PUTSHORT(optno, p);
+      PUTSHORT(optlen, p);
+      memcpy(p, opt, optlen);
+      p += optlen;  
+    }
 
   PUTSHORT(p - datap, lenp);
-  parm->plen = p - (unsigned char *)header;
+  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;
@@ -625,9 +619,111 @@ size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysock
   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? */ 
-static int private_net(struct in_addr addr, int ban_localhost) 
+int private_net(struct in_addr addr, int ban_localhost) 
 {
   in_addr_t ip_addr = ntohl(addr.s_addr);
 
@@ -639,10 +735,9 @@ static int private_net(struct in_addr addr, int ban_localhost)
     ((ip_addr & 0xFFFF0000) == 0xA9FE0000)  /* 169.254.0.0/16 (zeroconf) */ ;
 }
 
-static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name)
+static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored)
 {
   int i, qtype, qclass, rdlen;
-  unsigned long ttl;
 
   for (i = count; i != 0; i--)
     {
@@ -656,7 +751,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
       
       GETSHORT(qtype, p); 
       GETSHORT(qclass, p);
-      GETLONG(ttl, p);
+      p += 4; /* ttl */
       GETSHORT(rdlen, p);
       
       if (qclass == C_IN && qtype == T_A)
@@ -685,6 +780,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
              addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
              /* Since we munged the data, the server it came from is no longer authoritative */
              header->hb3 &= ~HB3_AA;
+             *doctored = 1;
              memcpy(p, &addr, INADDRSZ);
              break;
            }
@@ -700,15 +796,17 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
              unsigned char *p2 = p1;
              /* make counted string zero-term  and sanitise */
              for (i = 0; i < len; i++)
-               if (isprint(*(p2+1)))
-                 {
-                   *p2 = *(p2+1);
-                   p2++;
-                 }
+               {
+                 if (!isprint((int)*(p2+1)))
+                   break;
+                 
+                 *p2 = *(p2+1);
+                 p2++;
+               }
              *p2 = 0;
              my_syslog(LOG_INFO, "reply %s is %s", name, p1);
              /* restore */
-             memmove(p1 + 1, p1, len);
+             memmove(p1 + 1, p1, i);
              *p1 = len;
              p1 += len+1;
            }
@@ -721,7 +819,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
   return p; 
 }
 
-static int find_soa(struct dns_header *header, size_t qlen, char *name)
+static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doctored)
 {
   unsigned char *p;
   int qtype, qclass, rdlen;
@@ -730,7 +828,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
   
   /* first move to NS section and find TTL from any SOA section */
   if (!(p = skip_questions(header, qlen)) ||
-      !(p = do_doctor(p, ntohs(header->ancount), header, qlen, name)))
+      !(p = do_doctor(p, ntohs(header->ancount), header, qlen, name, doctored)))
     return 0;  /* bad packet */
   
   for (i = ntohs(header->nscount); i != 0; i--)
@@ -765,8 +863,8 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
        return 0; /* bad packet */
     }
   
-  /* rewrite addresses in additioal section too */
-  if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL))
+  /* rewrite addresses in additional section too */
+  if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL, doctored))
     return 0;
   
   if (!found_soa)
@@ -780,20 +878,29 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name)
    expired and cleaned out that way. 
    Return 1 if we reject an address because it look like part of dns-rebinding attack. */
 int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now, 
-                     int is_sign, int check_rebind, int checking_disabled)
+                     char **ipsets, int is_sign, int check_rebind, int no_cache_dnssec, int secure, int *doctored)
 {
   unsigned char *p, *p1, *endrr, *namep;
   int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
   unsigned long ttl = 0;
   struct all_addr addr;
-
+#ifdef HAVE_IPSET
+  char **ipsets_cur;
+#else
+  (void)ipsets; /* unused */
+#endif
+  
   cache_start_insert();
 
   /* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */
-  if (daemon->doctors || option_bool(OPT_LOG))
+  if (daemon->doctors || option_bool(OPT_LOG) || option_bool(OPT_DNSSEC_VALID))
     {
       searched_soa = 1;
-      ttl = find_soa(header, qlen, name);
+      ttl = find_soa(header, qlen, name, doctored);
+#ifdef HAVE_DNSSEC
+      if (*doctored && secure)
+       return 0;
+#endif
     }
   
   /* go through the questions. */
@@ -801,11 +908,12 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
   
   for (i = ntohs(header->qdcount); i != 0; i--)
     {
-      int found = 0, cname_count = 5;
+      int found = 0, cname_count = CNAME_CHAIN;
       struct crec *cpp = NULL;
       int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
+      int secflag = secure ?  F_DNSSECOK : 0;
       unsigned long cttl = ULONG_MAX, attl;
-      
+
       namep = p;
       if (!extract_name(header, qlen, &p, name, 1, 4))
        return 0; /* bad packet */
@@ -861,12 +969,12 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
                      
                      if (aqtype == T_CNAME)
                        {
-                         if (!cname_count--)
-                           return 0; /* looped CNAMES */
+                         if (!cname_count-- || secure)
+                           return 0; /* looped CNAMES, or DNSSEC, which we can't cache. */
                          goto cname_loop;
                        }
                      
-                     cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE);
+                     cache_insert(name, &addr, now, cttl, name_encoding | secflag | F_REVERSE);
                      found = 1; 
                    }
                  
@@ -881,10 +989,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
              if (!searched_soa)
                {
                  searched_soa = 1;
-                 ttl = find_soa(header, qlen, NULL);
+                 ttl = find_soa(header, qlen, NULL, doctored);
                }
              if (ttl)
-               cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags); 
+               cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | secflag);       
            }
        }
       else
@@ -908,78 +1016,106 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
          else 
            continue;
            
-         if (!(flags & F_NXDOMAIN))
+       cname_loop1:
+         if (!(p1 = skip_questions(header, qlen)))
+           return 0;
+         
+         for (j = ntohs(header->ancount); j != 0; j--) 
            {
-           cname_loop1:
-             if (!(p1 = skip_questions(header, qlen)))
-               return 0;
+             if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
+               return 0; /* bad packet */
              
-             for (j = ntohs(header->ancount); j != 0; j--) 
+             GETSHORT(aqtype, p1); 
+             GETSHORT(aqclass, p1);
+             GETLONG(attl, p1);
+             if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
                {
-                 if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
-                   return 0; /* bad packet */
-                 
-                 GETSHORT(aqtype, p1); 
-                 GETSHORT(aqclass, p1);
-                 GETLONG(attl, p1);
-                 if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
-                   {
-                     (p1) -= 4;
-                     PUTLONG(daemon->max_ttl, p1);
-                   }
-                 GETSHORT(ardlen, p1);
-                 endrr = p1+ardlen;
-                 
-                 if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
+                 (p1) -= 4;
+                 PUTLONG(daemon->max_ttl, p1);
+               }
+             GETSHORT(ardlen, p1);
+             endrr = p1+ardlen;
+             
+             if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
+               {
+                 if (aqtype == T_CNAME)
                    {
-                     if (aqtype == T_CNAME)
+                     if (!cname_count--)
+                       return 0; /* looped CNAMES */
+                     newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD | secflag);
+                     if (newc)
                        {
-                         if (!cname_count--)
-                           return 0; /* looped CNAMES */
-                         newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
-                         if (newc && cpp)
+                         newc->addr.cname.target.cache = NULL;
+                         /* anything other than zero, to avoid being mistaken for CNAME to interface-name */ 
+                         newc->addr.cname.uid = 1; 
+                         if (cpp)
                            {
-                             cpp->addr.cname.cache = newc;
+                             cpp->addr.cname.target.cache = newc;
                              cpp->addr.cname.uid = newc->uid;
                            }
-
-                         cpp = newc;
-                         if (attl < cttl)
-                           cttl = attl;
-                         
-                         if (!extract_name(header, qlen, &p1, name, 1, 0))
-                           return 0;
-                         goto cname_loop1;
                        }
-                     else
+                     
+                     cpp = newc;
+                     if (attl < cttl)
+                       cttl = attl;
+                     
+                     if (!extract_name(header, qlen, &p1, name, 1, 0))
+                       return 0;
+                     goto cname_loop1;
+                   }
+                 else if (!(flags & F_NXDOMAIN))
+                   {
+                     found = 1;
+                     
+                     /* copy address into aligned storage */
+                     if (!CHECK_LEN(header, p1, qlen, addrlen))
+                       return 0; /* bad packet */
+                     memcpy(&addr, p1, addrlen);
+                     
+                     /* check for returned address in private space */
+                     if (check_rebind)
                        {
-                         found = 1;
-                         
-                         /* copy address into aligned storage */
-                         if (!CHECK_LEN(header, p1, qlen, addrlen))
-                           return 0; /* bad packet */
-                         memcpy(&addr, p1, addrlen);
-                         
-                         /* check for returned address in private space */
-                         if (check_rebind &&
-                             (flags & F_IPV4) &&
+                         if ((flags & F_IPV4) &&
                              private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
                            return 1;
                          
-                         newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
-                         if (newc && cpp)
+#ifdef HAVE_IPV6
+                         if ((flags & F_IPV6) &&
+                             IN6_IS_ADDR_V4MAPPED(&addr.addr.addr6))
                            {
-                             cpp->addr.cname.cache = newc;
-                             cpp->addr.cname.uid = newc->uid;
+                             struct in_addr v4;
+                             v4.s_addr = ((const uint32_t *) (&addr.addr.addr6))[3];
+                             if (private_net(v4, !option_bool(OPT_LOCAL_REBIND)))
+                               return 1;
+                           }
+#endif
+                       }
+                     
+#ifdef HAVE_IPSET
+                     if (ipsets && (flags & (F_IPV4 | F_IPV6)))
+                       {
+                         ipsets_cur = ipsets;
+                         while (*ipsets_cur)
+                           {
+                             log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, name, &addr, *ipsets_cur);
+                             add_to_ipset(*ipsets_cur++, &addr, flags, 0);
                            }
-                         cpp = NULL;
                        }
+#endif
+                     
+                     newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD | secflag);
+                     if (newc && cpp)
+                       {
+                         cpp->addr.cname.target.cache = newc;
+                         cpp->addr.cname.uid = newc->uid;
+                       }
+                     cpp = NULL;
                    }
-                 
-                 p1 = endrr;
-                 if (!CHECK_LEN(header, p1, qlen, 0))
-                   return 0; /* bad packet */
                }
+             
+             p1 = endrr;
+             if (!CHECK_LEN(header, p1, qlen, 0))
+               return 0; /* bad packet */
            }
          
          if (!found && !option_bool(OPT_NO_NEG))
@@ -987,16 +1123,16 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
              if (!searched_soa)
                {
                  searched_soa = 1;
-                 ttl = find_soa(header, qlen, NULL);
+                 ttl = find_soa(header, qlen, NULL, doctored);
                }
              /* If there's no SOA to get the TTL from, but there is a CNAME 
                 pointing at this, inherit its TTL */
              if (ttl || cpp)
                {
-                 newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);    
+                 newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | secflag);  
                  if (newc && cpp)
                    {
-                     cpp->addr.cname.cache = newc;
+                     cpp->addr.cname.target.cache = newc;
                      cpp->addr.cname.uid = newc->uid;
                    }
                }
@@ -1004,10 +1140,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
        }
     }
   
-  /* Don't put stuff from a truncated packet into the cache,
-     also don't cache replies where DNSSEC validation was turned off, either
-     the upstream server told us so, or the original query specified it. */
-  if (!(header->hb3 & HB3_TC) && !(header->hb4 & HB4_CD) && !checking_disabled)
+  /* Don't put stuff from a truncated packet into the cache.
+     Don't cache replies from non-recursive nameservers, since we may get a 
+     reply containing a CNAME but not its target, even though the target 
+     does exist. */
+  if (!(header->hb3 & HB3_TC) && 
+      !(header->hb4 & HB4_CD) &&
+      (header->hb4 & HB4_RA) &&
+      !no_cache_dnssec)
     cache_end_insert();
 
   return 0;
@@ -1015,7 +1155,6 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
 
 /* If the packet holds exactly one query
    return F_IPV4 or F_IPV6  and leave the name from the query in name */
-
 unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
 {
   unsigned char *p = (unsigned char *)(header+1);
@@ -1044,8 +1183,6 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
        return F_IPV6;
       if (qtype == T_ANY)
        return  F_IPV4 | F_IPV6;
-      if (qtype == T_NS || qtype == T_SOA)
-       return F_QUERY | F_NSRR;
     }
   
   return F_QUERY;
@@ -1055,7 +1192,10 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
 size_t setup_reply(struct dns_header *header, size_t qlen,
                struct all_addr *addrp, unsigned int flags, unsigned long ttl)
 {
-  unsigned char *p = skip_questions(header, qlen);
+  unsigned char *p;
+
+  if (!(p = skip_questions(header, qlen)))
+    return 0;
   
   /* clear authoritative and truncated flags, set QR flag */
   header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
@@ -1071,7 +1211,7 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
     SET_RCODE(header, NOERROR); /* empty domain */
   else if (flags == F_NXDOMAIN)
     SET_RCODE(header, NXDOMAIN);
-  else if (p && flags == F_IPV4)
+  else if (flags == F_IPV4)
     { /* we know the address */
       SET_RCODE(header, NOERROR);
       header->ancount = htons(1);
@@ -1079,7 +1219,7 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
       add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);
     }
 #ifdef HAVE_IPV6
-  else if (p && flags == F_IPV6)
+  else if (flags == F_IPV6)
     {
       SET_RCODE(header, NOERROR);
       header->ancount = htons(1);
@@ -1101,12 +1241,22 @@ int check_for_local_domain(char *name, time_t now)
   struct txt_record *txt;
   struct interface_name *intr;
   struct ptr_record *ptr;
-  
-  if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
-      (crecp->flags & (F_HOSTS | F_DHCP)))
+  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 */
+
+  if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME | F_DS | F_NO_RR | F_NSIGMATCH)) &&
+      (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
     return 1;
   
-  for (mx = daemon->mxnames; mx; mx = mx->next)
+  for (naptr = daemon->naptr; naptr; naptr = naptr->next)
+     if (hostname_isequal(name, naptr->name))
+      return 1;
+
+   for (mx = daemon->mxnames; mx; mx = mx->next)
     if (hostname_isequal(name, mx->name))
       return 1;
 
@@ -1161,7 +1311,7 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
                /* Found a bogus address. Insert that info here, since there no SOA record
                   to get the ttl from in the normal processing */
                cache_start_insert();
-               cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
+               cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN);
                cache_end_insert();
                
                return 1;
@@ -1175,8 +1325,45 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
   return 0;
 }
 
-static int add_resource_record(struct dns_header *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp, 
-                              unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...)
+int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr)
+{
+  unsigned char *p;
+  int i, qtype, qclass, rdlen;
+  struct bogus_addr *baddrp;
+
+  /* skip over questions */
+  if (!(p = skip_questions(header, qlen)))
+    return 0; /* bad packet */
+
+  for (i = ntohs(header->ancount); i != 0; i--)
+    {
+      if (!(p = skip_name(p, header, qlen, 10)))
+       return 0; /* bad packet */
+      
+      GETSHORT(qtype, p); 
+      GETSHORT(qclass, p);
+      p += 4; /* TTL */
+      GETSHORT(rdlen, p);
+      
+      if (qclass == C_IN && qtype == T_A)
+       {
+         if (!CHECK_LEN(header, p, qlen, INADDRSZ))
+           return 0;
+         
+         for (baddrp = baddr; baddrp; baddrp = baddrp->next)
+           if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
+             return 1;
+       }
+      
+      if (!ADD_RDLEN(header, p, qlen, rdlen))
+       return 0;
+    }
+  
+  return 0;
+}
+
+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, ...)
 {
   va_list ap;
   unsigned char *sav, *p = *pp;
@@ -1187,8 +1374,26 @@ static int add_resource_record(struct dns_header *header, char *limit, int *trun
 
   if (truncp && *truncp)
     return 0;
+  va_start(ap, format);   /* make ap point to 1st unamed argument */
+  
+  if (nameoffset > 0)
+    {
+      PUTSHORT(nameoffset | 0xc000, p);
+    }
+  else
+    {
+      char *name = va_arg(ap, char *);
+      if (name)
+       p = do_rfc1035_name(p, name);
+      if (nameoffset < 0)
+       {
+         PUTSHORT(-nameoffset | 0xc000, p);
+       }
+      else
+       *p++ = 0;
+    }
 
-  PUTSHORT(nameoffset | 0xc000, p);
   PUTSHORT(type, p);
   PUTSHORT(class, p);
   PUTLONG(ttl, p);      /* TTL */
@@ -1196,8 +1401,6 @@ static int add_resource_record(struct dns_header *header, char *limit, int *trun
   sav = p;              /* Save pointer to RDLength field */
   PUTSHORT(0, p);       /* Placeholder RDLength */
 
-  va_start(ap, format);   /* make ap point to 1st unamed argument */
-  
   for (; *format; format++)
     switch (*format)
       {
@@ -1215,6 +1418,11 @@ static int add_resource_record(struct dns_header *header, char *limit, int *trun
        p += INADDRSZ;
        break;
        
+      case 'b':
+       usval = va_arg(ap, int);
+       *p++ = usval;
+       break;
+       
       case 's':
        usval = va_arg(ap, int);
        PUTSHORT(usval, p);
@@ -1236,7 +1444,8 @@ static int add_resource_record(struct dns_header *header, char *limit, int *trun
       case 't':
        usval = va_arg(ap, int);
        sval = va_arg(ap, char *);
-       memcpy(p, sval, usval);
+       if (usval != 0)
+         memcpy(p, sval, usval);
        p += usval;
        break;
 
@@ -1286,45 +1495,49 @@ 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) 
+                     struct in_addr local_addr, struct in_addr local_netmask, 
+                     time_t now, int *ad_reqd, int *do_bit) 
 {
   char *name = daemon->namebuff;
   unsigned char *p, *ansp, *pheader;
-  int qtype, qclass;
+  unsigned int qtype, qclass;
   struct all_addr addr;
-  unsigned int nameoffset;
+  int nameoffset;
   unsigned short flag;
   int q, ans, anscount = 0, addncount = 0;
-  int dryrun = 0, sec_reqd = 0;
-  int is_sign;
+  int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
   struct crec *crecp;
-  int nxdomain = 0, auth = 1, trunc = 0;
+  int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
   struct mx_srv_record *rec;
+  size_t len;
  
+  /* 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
      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. */
+     security information, unless we're in DNSSEC validation mode. */
 
-  if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
+  if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
     { 
-      unsigned short udpsz, ext_rcode, flags;
-      unsigned char *psave = pheader;
+      unsigned short flags;
+      
+      have_pseudoheader = 1;
 
-      GETSHORT(udpsz, pheader);
-      GETSHORT(ext_rcode, pheader);
+      pheader += 4; /* udp size, ext_rcode */
       GETSHORT(flags, pheader);
       
-      sec_reqd = flags & 0x8000; /* do bit */ 
-
-      /* If our client is advertising a larger UDP packet size
-        than we allow, trim it so that we don't get an overlarge
-        response from upstream */
-
-      if (!is_sign && (udpsz > daemon->edns_pktsz))
-       PUTSHORT(daemon->edns_pktsz, psave); 
+      if ((sec_reqd = flags & 0x8000))
+       *do_bit = 1;/* do bit */ 
 
+      *ad_reqd = 1;
       dryrun = 1;
     }
 
@@ -1354,6 +1567,11 @@ 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)
@@ -1366,10 +1584,20 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
                  ans = 1;
                  if (!dryrun)
                    {
+                     unsigned long ttl = daemon->local_ttl;
+                     int ok = 1;
                      log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
-                     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
-                                             daemon->local_ttl, NULL,
-                                             T_TXT, t->class, "t", t->len, t->txt))
+                     /* Dynamically generate stat record */
+                     if (t->stat != 0)
+                       {
+                         ttl = 0;
+                         if (!cache_make_stat(t))
+                           ok = 0;
+                       }
+                     
+                     if (ok && add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                                   ttl, NULL,
+                                                   T_TXT, t->class, "t", t->len, t->txt))
                        anscount++;
 
                    }
@@ -1377,8 +1605,116 @@ 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;
+
+         for (t = daemon->rr; t; t = t->next)
+           if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name))
+             {
+               ans = 1;
+               if (!dryrun)
+                 {
+                   log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
+                   if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                           daemon->local_ttl, NULL,
+                                           t->class, C_IN, "t", t->len, t->txt))
+                     anscount ++;
+                 }
+             }
+               
          if (qtype == T_PTR || qtype == T_ANY)
            {
              /* see if it's w.z.y.z.in-addr.arpa format */
@@ -1393,19 +1729,42 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
              if (is_arpa == F_IPV4)
                for (intr = daemon->int_names; intr; intr = intr->next)
                  {
-                   if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr)
+                   struct addrlist *addrlist;
+                   
+                   for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
+                     if (!(addrlist->flags & ADDRLIST_IPV6) && addr.addr.addr4.s_addr == addrlist->addr.addr.addr4.s_addr)
+                       break;
+                   
+                   if (addrlist)
+                     break;
+                   else
+                     while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
+                       intr = intr->next;
+                 }
+#ifdef HAVE_IPV6
+             else if (is_arpa == F_IPV6)
+               for (intr = daemon->int_names; intr; intr = intr->next)
+                 {
+                   struct addrlist *addrlist;
+                   
+                   for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
+                     if ((addrlist->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&addr.addr.addr6, &addrlist->addr.addr.addr6))
+                       break;
+                   
+                   if (addrlist)
                      break;
                    else
                      while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
                        intr = intr->next;
                  }
+#endif
              
              if (intr)
                {
                  ans = 1;
                  if (!dryrun)
                    {
-                     log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
+                     log_query(is_arpa | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
                      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
                                              daemon->local_ttl, NULL,
                                              T_PTR, C_IN, "d", intr->name))
@@ -1428,38 +1787,90 @@ 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)))
-               do 
-                 { 
-                   /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
-                   if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
-                     continue;
-                   
-                   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)
-                     {
-                       ans = 1;
-                       if (!(crecp->flags & (F_HOSTS | F_DHCP)))
-                         auth = 0;
-                       if (!dryrun)
-                         {
-                           log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr, 
-                                     record_source(crecp->uid));
-                           
-                           if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
-                                                   crec_ttl(crecp, now), NULL,
-                                                   T_PTR, C_IN, "d", cache_get_name(crecp)))
+               {
+                 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)
+                   {
+                     do 
+                       { 
+                         /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
+                         if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
+                           continue;
+                         
+                         if (!(crecp->flags & F_DNSSECOK))
+                           sec_data = 0;
+                         
+                         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))
+                           {
+                             ans = 1;
+                             if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+                               auth = 0;
+                             if (!dryrun)
+                               {
+                                 log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr, 
+                                           record_source(crecp->uid));
+                                 
+                                 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                                         crec_ttl(crecp, now), NULL,
+                                                         T_PTR, C_IN, "d", cache_get_name(crecp)))
+                                   anscount++;
+                               }
+                           }
+                       } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
+                   }
+               }
+             else if (is_rev_synth(is_arpa, &addr, name))
+               {
+                 ans = 1;
+                 if (!dryrun)
+                   {
+                     log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL); 
+                     
+                     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                             daemon->local_ttl, NULL,
+                                             T_PTR, C_IN, "d", name))
                              anscount++;
-                         }
-                     }
-                 } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
+                   }
+               }
              else if (is_arpa == F_IPV4 && 
                       option_bool(OPT_BOGUSPRIV) && 
                       private_net(addr.addr.addr4, 1))
@@ -1476,7 +1887,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
          for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
            {
              unsigned short type = T_A;
-             
+             struct interface_name *intr;
+
              if (flag == F_IPV6)
 #ifdef HAVE_IPV6
                type = T_AAAA;
@@ -1525,35 +1937,51 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
                }
 
              /* interface name stuff */
-             if (qtype == T_A)
+           intname_restart:
+             for (intr = daemon->int_names; intr; intr = intr->next)
+               if (hostname_isequal(name, intr->name))
+                 break;
+             
+             if (intr)
                {
-                 struct interface_name *intr;
+                 struct addrlist *addrlist;
+                 int gotit = 0;
 
+                 enumerate_interfaces(0);
+                 
                  for (intr = daemon->int_names; intr; intr = intr->next)
                    if (hostname_isequal(name, intr->name))
-                     break;
-                 
-                 if (intr)
-                   {
-                     ans = 1;
-                     if (!dryrun)
-                       {
-                         if ((addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr == (in_addr_t) -1)
-                           log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, NULL);
-                         else
+                     {
+                       for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
+#ifdef HAVE_IPV6
+                         if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type)
+#endif
                            {
-                             log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
-                             if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
-                                                     daemon->local_ttl, NULL, type, C_IN, "4", &addr))
-                               anscount++;
+#ifdef HAVE_IPV6
+                             if (addrlist->flags & ADDRLIST_REVONLY)
+                               continue;
+#endif 
+                             ans = 1;  
+                             if (!dryrun)
+                               {
+                                 gotit = 1;
+                                 log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
+                                 if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                                         daemon->local_ttl, NULL, type, C_IN, 
+                                                         type == T_A ? "4" : "6", &addrlist->addr))
+                                   anscount++;
+                               }
                            }
-                       }
-                     continue;
-                   }
+                     }
+                 
+                 if (!dryrun && !gotit)
+                   log_query(F_FORWARD | F_CONFIG | flag | F_NEG, name, NULL, NULL);
+                    
+                 continue;
                }
 
            cname_restart:
-             if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME)))
+             if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME | (dryrun ? F_NO_RR : 0))))
                {
                  int localise = 0;
                  
@@ -1572,66 +2000,153 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
                        } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
                      crecp = save;
                    }
-                         
-                 do
-                   { 
-                     /* don't answer wildcard queries with data not from /etc/hosts
-                        or DHCP leases */
-                     if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
-                       break;
-                     
-                     if (crecp->flags & F_CNAME)
+
+                 /* 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)
                        {
-                         if (!dryrun)
-                           {
-                             log_query(crecp->flags, name, NULL, record_source(crecp->uid));
-                             if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
-                                                     crec_ttl(crecp, now), &nameoffset,
-                                                     T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
-                               anscount++;
-                           }
+                         /* 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;
                          
-                         strcpy(name, cache_get_name(crecp->addr.cname.cache));
-                         goto cname_restart;
-                       }
-                     
-                     if (crecp->flags & F_NEG)
-                       {
-                         ans = 1;
-                         auth = 0;
-                         if (crecp->flags & F_NXDOMAIN)
-                           nxdomain = 1;
-                         if (!dryrun)
-                           log_query(crecp->flags, name, NULL, NULL);
-                       }
-                     else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
-                       {
-                         /* If we are returning local answers depending on network,
-                            filter here. */
-                         if (localise && 
-                             (crecp->flags & F_HOSTS) &&
-                             !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
-                           continue;
-       
-                         if (!(crecp->flags & (F_HOSTS | F_DHCP)))
-                           auth = 0;
+                         if (crecp->flags & F_CNAME)
+                           sigtype = T_CNAME;
                          
-                         ans = 1;
-                         if (!dryrun)
+                         while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
                            {
-                             log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
-                                       record_source(crecp->uid));
-                             
-                             if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
-                                                     crec_ttl(crecp, now), NULL, type, C_IN, 
-                                                     type == T_A ? "4" : "6", &crecp->addr))
-                               anscount++;
+                             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;
                        }
-                   } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
+#endif
+                   }            
+
+                 if (crecp)
+                   do
+                     { 
+                       /* don't answer wildcard queries with data not from /etc/hosts
+                          or DHCP leases */
+                       if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
+                         break;
+                       
+                       if (!(crecp->flags & F_DNSSECOK))
+                         sec_data = 0;
+                       
+                       if (crecp->flags & F_CNAME)
+                         {
+                           char *cname_target = cache_get_cname_target(crecp);
+                           
+                           if (!dryrun)
+                             {
+                               log_query(crecp->flags, name, NULL, record_source(crecp->uid));
+                               if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                                       crec_ttl(crecp, now), &nameoffset,
+                                                       T_CNAME, C_IN, "d", cname_target))
+                                 anscount++;
+                             }
+                           
+                           strcpy(name, cname_target);
+                           /* check if target interface_name */
+                           if (crecp->addr.cname.uid == SRC_INTERFACE)
+                             goto intname_restart;
+                           else
+                             goto cname_restart;
+                         }
+                       
+                       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);
+                             }
+                         }
+                       else 
+                         {
+                           /* If we are returning local answers depending on network,
+                              filter here. */
+                           if (localise && 
+                               (crecp->flags & F_HOSTS) &&
+                               !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
+                             continue;
+                           
+                           if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+                             auth = 0;
+                           
+                           ans = 1;
+                           if (!dryrun)
+                             {
+                               log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
+                                         record_source(crecp->uid));
+                               
+                               if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                                       crec_ttl(crecp, now), NULL, type, C_IN, 
+                                                       type == T_A ? "4" : "6", &crecp->addr))
+                                 anscount++;
+                             }
+                         }
+                     } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
+               }
+             else if (is_name_synthetic(flag, name, &addr))
+               {
+                 ans = 1;
+                 if (!dryrun)
+                   {
+                     log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL);
+                     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                             daemon->local_ttl, NULL, type, C_IN, type == T_A ? "4" : "6", &addr))
+                       anscount++;
+                   }
                }
            }
-         
+
+         if (qtype == T_CNAME || qtype == T_ANY)
+           {
+             if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) &&
+                 (qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG  | (dryrun ? F_NO_RR : 0)))))
+               {
+                 if (!(crecp->flags & F_DNSSECOK))
+                   sec_data = 0;
+                 
+                 ans = 1;
+                 if (!dryrun)
+                   {
+                     log_query(crecp->flags, name, NULL, record_source(crecp->uid));
+                     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                             crec_ttl(crecp, now), &nameoffset,
+                                             T_CNAME, C_IN, "d", cache_get_cname_target(crecp)))
+                       anscount++;
+                   }
+               }
+           }
+
          if (qtype == T_MX || qtype == T_ANY)
            {
              int found = 0;
@@ -1641,7 +2156,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
                  ans = found = 1;
                  if (!dryrun)
                    {
-                     unsigned int offset;
+                     int offset;
                      log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
                      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
                                              &offset, T_MX, C_IN, "sd", rec->weight, rec->target))
@@ -1654,7 +2169,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
                  }
              
              if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) && 
-                 cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
+                 cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP | F_NO_RR))
                { 
                  ans = 1;
                  if (!dryrun)
@@ -1679,7 +2194,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
                    found = ans = 1;
                    if (!dryrun)
                      {
-                       unsigned int offset;
+                       int offset;
                        log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
                        if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, 
                                                &offset, T_SRV, C_IN, "sssd", 
@@ -1798,18 +2313,25 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
   /* truncation */
   if (trunc)
     header->hb3 |= HB3_TC;
-
-  if (anscount == 0 && nxdomain)
+  
+  if (nxdomain)
     SET_RCODE(header, NXDOMAIN);
   else
     SET_RCODE(header, NOERROR); /* no error */
   header->ancount = htons(anscount);
   header->nscount = htons(0);
   header->arcount = htons(addncount);
-  return ansp - (unsigned char *)header;
-}
-
-
-
 
+  len = ansp - (unsigned char *)header;
+  
+  if (have_pseudoheader)
+    len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
+  
+  if (*ad_reqd && sec_data)
+    header->hb4 |= HB4_AD;
+  else
+    header->hb4 &= ~HB4_AD;
+  
+  return len;
+}
 
index 3dbfd79..9f69ed5 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
 
 #ifdef HAVE_DHCP
 
-#define have_config(config, mask) ((config) && ((config)->flags & (mask))) 
 #define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
 #define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
 
 #ifdef HAVE_SCRIPT
-static void add_extradata_data(struct dhcp_lease *lease, unsigned char *data, size_t len, int delim);
 static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt);
 #endif
 
-static int match_bytes(struct dhcp_opt *o, unsigned char *p, int len);
 static int sanitise(unsigned char *opt, char *buf);
 static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback);
 static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt);
@@ -35,39 +32,41 @@ static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, in
 static void option_put_string(struct dhcp_packet *mess, unsigned char *end, 
                              int opt, char *string, int null_term);
 static struct in_addr option_addr(unsigned char *opt);
-static struct in_addr option_addr_arr(unsigned char *opt, int offset);
 static unsigned int option_uint(unsigned char *opt, int i, int size);
 static void log_packet(char *type, void *addr, unsigned char *ext_mac, 
-                      int mac_len, char *interface, char *string, u32 xid);
+                      int mac_len, char *interface, char *string, char *err, u32 xid);
 static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
 static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
-static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid,
-                              unsigned char *agent_id, unsigned char *real_end);
+static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end);
 static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
+static int in_list(unsigned char *list, int opt);
 static void do_options(struct dhcp_context *context,
                       struct dhcp_packet *mess,
                       unsigned char *real_end, 
                       unsigned char *req_options,
                       char *hostname, 
-                      char *domain, char *config_domain,
+                      char *config_domain,
                       struct dhcp_netid *netid,
-                      struct in_addr subnet_addr,
+                      struct in_addr subnet_addr, 
                       unsigned char fqdn_flags,
                       int null_term, int pxearch,
                       unsigned char *uuid,
-                      int vendor_class_len);
+                      int vendor_class_len,
+                      time_t now,
+                      unsigned int lease_time,
+                      unsigned short fuzz);
 
 
 static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); 
 static int do_encap_opts(struct dhcp_opt *opts, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);
 static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid);
 static int prune_vendor_opts(struct dhcp_netid *netid);
-static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local);
+static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now);
 struct dhcp_boot *find_boot(struct dhcp_netid *netid);
 
   
 size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
-                 size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe)
+                 size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe, struct in_addr fallback)
 {
   unsigned char *opt, *clid = NULL;
   struct dhcp_lease *ltmp, *lease = NULL;
@@ -85,7 +84,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
   unsigned int time;
   struct dhcp_config *config;
   struct dhcp_netid *netid, *tagif_netid;
-  struct in_addr subnet_addr, fallback, override;
+  struct in_addr subnet_addr, override;
   unsigned short fuzz = 0;
   unsigned int mess_type = 0;
   unsigned char fqdn_flags = 0;
@@ -95,7 +94,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
   struct dhcp_netid known_id, iface_id, cpewan_id;
   struct dhcp_opt *o;
   unsigned char pxe_uuid[17];
-  unsigned char *oui = NULL, *serial = NULL, *class = NULL;
+  unsigned char *oui = NULL, *serial = NULL;
+#ifdef HAVE_SCRIPT
+  unsigned char *class = NULL;
+#endif
 
   subnet_addr.s_addr = override.s_addr = 0;
 
@@ -159,8 +161,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
                  unsigned char *y = option_ptr(opt, offset + elen + 5);
                  oui = option_find1(x, y, 1, 1);
                  serial = option_find1(x, y, 2, 1);
-                 class = option_find1(x, y, 3, 1);
-                 
+#ifdef HAVE_SCRIPT
+                 class = option_find1(x, y, 3, 1);               
+#endif
                  /* If TR069-id is present set the tag "cpewan-id" to facilitate echoing 
                     the gateway id back. Note that the device class is optional */
                  if (oui && serial)
@@ -297,17 +300,43 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
                
       if (!context_new)
        for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
-         if (context_tmp->netmask.s_addr  && 
-             is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
-             is_same_net(addr, context_tmp->end, context_tmp->netmask))
-           {
-             context_tmp->current = context_new;
-             context_new = context_tmp;
-           }
+         {
+           struct in_addr netmask = context_tmp->netmask;
+
+           /* guess the netmask for relayed networks */
+           if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
+             {
+               if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
+                 netmask.s_addr = htonl(0xff000000);
+               else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
+                 netmask.s_addr = htonl(0xffff0000);
+               else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
+                 netmask.s_addr = htonl(0xffffff00); 
+             }
+           
+           /* This section fills in context mainly when a client which is on a remote (relayed)
+              network renews a lease without using the relay, after dnsmasq has restarted. */
+           if (netmask.s_addr != 0  && 
+               is_same_net(addr, context_tmp->start, netmask) &&
+               is_same_net(addr, context_tmp->end, netmask))
+             {
+               context_tmp->netmask = netmask;
+               if (context_tmp->local.s_addr == 0)
+                 context_tmp->local = fallback;
+               if (context_tmp->router.s_addr == 0)
+                 context_tmp->router = mess->giaddr;
+          
+               /* fill in missing broadcast addresses for relayed ranges */
+               if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
+                 context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
+               
+               context_tmp->current = context_new;
+               context_new = context_tmp;
+             }
+         }
       
       if (context_new || force)
-       context = context_new;
-      
+       context = context_new; 
     }
   
   if (!context)
@@ -318,9 +347,6 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
       return 0;
     }
 
-  /* keep _a_ local address available. */
-  fallback = context->local;
-  
   if (option_bool(OPT_LOG_OPTS))
     {
       struct dhcp_context *context_tmp;
@@ -335,6 +361,117 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
                      ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
        }
     }
+  
+  /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
+     Otherwise assume the option is an array, and look for a matching element. 
+     If no data given, existance of the option is enough. This code handles 
+     rfc3925 V-I classes too. */
+  for (o = daemon->dhcp_match; o; o = o->next)
+    {
+      unsigned int len, elen, match = 0;
+      size_t offset, o2;
+
+      if (o->flags & DHOPT_RFC3925)
+       {
+         if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
+           continue;
+         
+         for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
+           {
+             len = option_uint(opt, offset + 4 , 1);
+             /* Need to take care that bad data can't run us off the end of the packet */
+             if ((offset + len + 5 <= (option_len(opt))) &&
+                 (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
+               for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
+                 { 
+                   elen = option_uint(opt, o2, 1);
+                   if ((o2 + elen + 1 <= option_len(opt)) &&
+                       (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
+                     break;
+                 }
+             if (match) 
+               break;
+           }     
+       }
+      else
+       {
+         if (!(opt = option_find(mess, sz, o->opt, 1)))
+           continue;
+         
+         match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
+       } 
+
+      if (match)
+       {
+         o->netid->next = netid;
+         netid = o->netid;
+       }
+    }
+       
+  /* user-class options are, according to RFC3004, supposed to contain
+     a set of counted strings. Here we check that this is so (by seeing
+     if the counts are consistent with the overall option length) and if
+     so zero the counts so that we don't get spurious matches between 
+     the vendor string and the counts. If the lengths don't add up, we
+     assume that the option is a single string and non RFC3004 compliant 
+     and just do the substring match. dhclient provides these broken options.
+     The code, later, which sends user-class data to the lease-change script
+     relies on the transformation done here.
+  */
+
+  if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
+    {
+      unsigned char *ucp = option_ptr(opt, 0);
+      int tmp, j;
+      for (j = 0; j < option_len(opt); j += ucp[j] + 1);
+      if (j == option_len(opt))
+       for (j = 0; j < option_len(opt); j = tmp)
+         {
+           tmp = j + ucp[j] + 1;
+           ucp[j] = 0;
+         }
+    }
+    
+  for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
+    {
+      int mopt;
+      
+      if (vendor->match_type == MATCH_VENDOR)
+       mopt = OPTION_VENDOR_ID;
+      else if (vendor->match_type == MATCH_USER)
+       mopt = OPTION_USER_CLASS; 
+      else
+       continue;
+
+      if ((opt = option_find(mess, sz, mopt, 1)))
+       {
+         int i;
+         for (i = 0; i <= (option_len(opt) - vendor->len); i++)
+           if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
+             {
+               vendor->netid.next = netid;
+               netid = &vendor->netid;
+               break;
+             }
+       }
+    }
+
+  /* mark vendor-encapsulated options which match the client-supplied vendor class,
+     save client-supplied vendor class */
+  if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
+    {
+      memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
+      vendor_class_len = option_len(opt);
+    }
+  match_vendor_opts(opt, daemon->dhcp_opts);
+  
+  if (option_bool(OPT_LOG_OPTS))
+    {
+      if (sanitise(opt, daemon->namebuff))
+       my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
+      if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
+       my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
+    }
 
   mess->op = BOOTREPLY;
   
@@ -441,9 +578,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
          else if (context->netid.net)
            {
              context->netid.next = netid;
-             netid = &context->netid; 
-             tagif_netid = run_tag_if(netid);
+             tagif_netid = run_tag_if(&context->netid);
            }
+
+         log_tags(tagif_netid, ntohl(mess->xid));
            
          if (!message && !nailed)
            {
@@ -456,34 +594,34 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
 
          if (!message && 
              !lease && 
-             (!(lease = lease_allocate(mess->yiaddr))))
+             (!(lease = lease4_allocate(mess->yiaddr))))
            message = _("no leases left");
          
          if (!message)
            {
              logaddr = &mess->yiaddr;
                
-             lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
+             lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now, 1);
              if (hostname)
-               lease_set_hostname(lease, hostname, 1); 
+               lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain); 
              /* infinite lease unless nailed in dhcp-host line. */
              lease_set_expires(lease,  
                                have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff, 
                                now); 
-             lease_set_interface(lease, int_index);
+             lease_set_interface(lease, int_index, now);
              
              clear_packet(mess, end);
              do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), 
-                        domain, tagif_netid, subnet_addr, 0, 0, 0, NULL, 0);
+                        netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0);
            }
        }
       
-      log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);
+      log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid);
       
-      return message ? 0 : dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
+      return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
     }
       
-  if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
+  if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 3)))
     {
       /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
       int len = option_len(opt);
@@ -495,15 +633,25 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
       op += 3;
       pp = op;
       
-      /* Always force update, since the client has no way to do it itself. */
-      if (!(fqdn_flags & 0x01))
-       fqdn_flags |= 0x02;
-      
-      fqdn_flags &= ~0x08;
-      fqdn_flags |= 0x01;
+      /* NB, the following always sets at least one bit */
+      if (option_bool(OPT_FQDN_UPDATE))
+       {
+         if (fqdn_flags & 0x01)
+           {
+             fqdn_flags |= 0x02; /* set O */
+             fqdn_flags &= ~0x01; /* clear S */
+           }
+         fqdn_flags |= 0x08; /* set N */
+       }
+      else 
+       {
+         if (!(fqdn_flags & 0x01))
+           fqdn_flags |= 0x03; /* set S and O */
+         fqdn_flags &= ~0x08; /* clear N */
+       }
       
       if (fqdn_flags & 0x04)
-       while (*op != 0 && ((op + (*op) + 1) - pp) < len)
+       while (*op != 0 && ((op + (*op)) - pp) < len)
          {
            memcpy(pq, op+1, *op);
            pq += *op;
@@ -591,119 +739,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
        }
     }
   
-  /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
-     Otherwise assume the option is an array, and look for a matching element. 
-     If no data given, existance of the option is enough. This code handles 
-     rfc3925 V-I classes too. */
-  for (o = daemon->dhcp_match; o; o = o->next)
-    {
-      unsigned int len, elen, match = 0;
-      size_t offset, o2;
-
-      if (o->flags & DHOPT_RFC3925)
-       {
-         if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
-           continue;
-         
-         for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
-           {
-             len = option_uint(opt, offset + 4 , 1);
-             /* Need to take care that bad data can't run us off the end of the packet */
-             if ((offset + len + 5 <= (option_len(opt))) &&
-                 (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
-               for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
-                 { 
-                   elen = option_uint(opt, o2, 1);
-                   if ((o2 + elen + 1 <= option_len(opt)) &&
-                       (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
-                     break;
-                 }
-             if (match) 
-               break;
-           }     
-       }
-      else
-       {
-         if (!(opt = option_find(mess, sz, o->opt, 1)))
-           continue;
-         
-         match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
-       } 
-
-      if (match)
-       {
-         o->netid->next = netid;
-         netid = o->netid;
-       }
-    }
-       
-  /* user-class options are, according to RFC3004, supposed to contain
-     a set of counted strings. Here we check that this is so (by seeing
-     if the counts are consistent with the overall option length) and if
-     so zero the counts so that we don't get spurious matches between 
-     the vendor string and the counts. If the lengths don't add up, we
-     assume that the option is a single string and non RFC3004 compliant 
-     and just do the substring match. dhclient provides these broken options.
-     The code, later, which sends user-class data to the lease-change script
-     relies on the transformation done here.
-  */
-
-  if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
-    {
-      unsigned char *ucp = option_ptr(opt, 0);
-      int tmp, j;
-      for (j = 0; j < option_len(opt); j += ucp[j] + 1);
-      if (j == option_len(opt))
-       for (j = 0; j < option_len(opt); j = tmp)
-         {
-           tmp = j + ucp[j] + 1;
-           ucp[j] = 0;
-         }
-    }
-    
-  for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
-    {
-      int mopt;
-      
-      if (vendor->match_type == MATCH_VENDOR)
-       mopt = OPTION_VENDOR_ID;
-      else if (vendor->match_type == MATCH_USER)
-       mopt = OPTION_USER_CLASS; 
-      else
-       continue;
-
-      if ((opt = option_find(mess, sz, mopt, 1)))
-       {
-         int i;
-         for (i = 0; i <= (option_len(opt) - vendor->len); i++)
-           if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
-             {
-               vendor->netid.next = netid;
-               netid = &vendor->netid;
-               break;
-             }
-       }
-    }
-
-  /* mark vendor-encapsulated options which match the client-supplied vendor class,
-     save client-supplied vendor class */
-  if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
-    {
-      memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
-      vendor_class_len = option_len(opt);
-    }
-  match_vendor_opts(opt, daemon->dhcp_opts);
-  
-  if (option_bool(OPT_LOG_OPTS))
-    {
-      if (sanitise(opt, daemon->namebuff))
-       my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
-      if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
-       my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
-    }
-
   tagif_netid = run_tag_if(netid);
-
+  
   /* if all the netids in the ignore list are present, ignore this client */
   for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
     if (match_netid(id_list->list, tagif_netid, 0))
@@ -768,14 +805,21 @@ 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;
          mess->ciaddr.s_addr = 0;
-         if (service->server.s_addr != 0)
+         if (service->sname)
+           mess->siaddr = a_record_from_hosts(service->sname, now);
+         else if (service->server.s_addr != 0)
            mess->siaddr = service->server; 
          else
            mess->siaddr = context->local; 
@@ -794,8 +838,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
          opt71.next = daemon->dhcp_opts;
          do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
          
-         log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid);
-         return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);         
+         log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, NULL, mess->xid);
+         log_tags(tagif_netid, ntohl(mess->xid));
+         return dhcp_packet_size(mess, agent_id, real_end);      
        }
       
       if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
@@ -814,8 +859,16 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
              
              if (tmp)
                {
-                 struct dhcp_boot *boot = find_boot(tagif_netid);
-               
+                 struct dhcp_boot *boot;
+                 
+                 if (tmp->netid.net)
+                   {
+                     tmp->netid.next = netid;
+                     tagif_netid = run_tag_if(&tmp->netid);
+                   }
+                 
+                 boot = find_boot(tagif_netid);
+                 
                  mess->yiaddr.s_addr = 0;
                  if  (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
                    {
@@ -829,8 +882,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
                     and set discovery_control = 8 */
                  if (boot)
                    {
-                     if (boot->next_server.s_addr)
+                     if (boot->next_server.s_addr) 
                        mess->siaddr = boot->next_server;
+                     else if (boot->tftp_sname) 
+                       mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
                      
                      if (boot->file)
                        strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
@@ -838,13 +893,14 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
                  
                  option_put(mess, end, OPTION_MESSAGE_TYPE, 1, 
                             mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
-                 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
+                 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr));
                  pxe_misc(mess, end, uuid);
                  prune_vendor_opts(tagif_netid);
-                 do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
+                 do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
                  
-                 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", mess->xid);
-                 return ignore ? 0 : dhcp_packet_size(mess, tagif_netid, agent_id, real_end);    
+                 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
+                 log_tags(tagif_netid, ntohl(mess->xid));
+                 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);         
                }
            }
        }
@@ -874,7 +930,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
       if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
        return 0;
       
-      log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff, mess->xid);
+      log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, NULL, daemon->dhcp_buff, mess->xid);
       
       if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
        lease_prune(lease, now);
@@ -906,13 +962,15 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
       else
        message = _("unknown lease");
 
-      log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
+      log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
        
       return 0;
       
     case DHCPDISCOVER:
       if (ignore || have_config(config, CONFIG_DISABLE))
        {
+         if (option_bool(OPT_QUIET_DHCP))
+           return 0;
          message = _("ignored");
          opt = NULL;
        }
@@ -970,35 +1028,31 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
            message = _("no address available");      
        }
       
-      log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid); 
+      log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, NULL, message, mess->xid); 
 
       if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
        return 0;
 
-      log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
-
       if (context->netid.net)
        {
          context->netid.next = netid;
-         netid = &context->netid;
-         tagif_netid = run_tag_if(netid);
+         tagif_netid = run_tag_if(&context->netid);
        }
-       
+
+      log_tags(tagif_netid, ntohl(mess->xid));
+      
+      log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
+      
       time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
       clear_packet(mess, end);
       option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
       option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
       option_put(mess, end, OPTION_LEASE_TIME, 4, time);
       /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
-      if (time != 0xffffffff)
-       {
-         option_put(mess, end, OPTION_T1, 4, (time/2));
-         option_put(mess, end, OPTION_T2, 4, (time*7)/8);
-       }
       do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr), 
-                domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
+                netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
       
-      return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
+      return dhcp_packet_size(mess, agent_id, real_end);
       
     case DHCPREQUEST:
       if (ignore || have_config(config, CONFIG_DISABLE))
@@ -1029,12 +1083,30 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
                  
                  if (!context)
                    {
-                     /* In auth mode, a REQUEST sent to the wrong server
-                        should be faulted, so that the client establishes 
-                        communication with us, otherwise, silently ignore. */
-                     if (!option_bool(OPT_AUTHORITATIVE))
-                       return 0;
-                     message = _("wrong server-ID");
+                     /* Handle very strange configs where clients have more than one route to the server.
+                        If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
+                        Have to set override to make sure we echo back the correct server-id */
+                     struct irec *intr;
+                     
+                     enumerate_interfaces(0);
+
+                     for (intr = daemon->interfaces; intr; intr = intr->next)
+                       if (intr->addr.sa.sa_family == AF_INET &&
+                           intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
+                           intr->tftp_ok)
+                         break;
+
+                     if (intr)
+                       override = intr->addr.in.sin_addr;
+                     else
+                       {
+                         /* In auth mode, a REQUEST sent to the wrong server
+                            should be faulted, so that the client establishes 
+                            communication with us, otherwise, silently ignore. */
+                         if (!option_bool(OPT_AUTHORITATIVE))
+                           return 0;
+                         message = _("wrong server-ID");
+                       }
                    }
                }
 
@@ -1080,7 +1152,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
          mess->yiaddr = mess->ciaddr;
        }
       
-      log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
+      log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
  
       if (!message)
        {
@@ -1142,7 +1214,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
              
              else if (!lease)
                {            
-                 if ((lease = lease_allocate(mess->yiaddr)))
+                 if ((lease = lease4_allocate(mess->yiaddr)))
                    do_classes = 1;
                  else
                    message = _("no leases left");
@@ -1152,7 +1224,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
 
       if (message)
        {
-         log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid);
+         log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
          
          mess->yiaddr.s_addr = 0;
          clear_packet(mess, end);
@@ -1173,47 +1245,74 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
          if (context->netid.net)
            {
              context->netid.next = netid;
-             netid = &context->netid;
-             tagif_netid = run_tag_if(netid);
+             tagif_netid = run_tag_if( &context->netid);
            }
+
+         log_tags(tagif_netid, ntohl(mess->xid));
          
-#ifdef HAVE_SCRIPT
-         if (do_classes && daemon->lease_change_command)
+         if (do_classes)
            {
-             struct dhcp_netid *n;
-             
-             if (mess->giaddr.s_addr)
-               lease->giaddr = mess->giaddr;
-             
-             lease->changed = 1;
-             free(lease->extradata);
-             lease->extradata = NULL;
-             lease->extradata_size = lease->extradata_len = 0;
-             
-             add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
-             add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
-             add_extradata_opt(lease, oui);
-             add_extradata_opt(lease, serial);
-             add_extradata_opt(lease, class);
-             
-             /* space-concat tag set */
-             if (!tagif_netid)
-               add_extradata_opt(lease, NULL);
-             else
-               for (n = tagif_netid; n; n = n->next)
-                 add_extradata_data(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0); 
-             
-             if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
+             /* pick up INIT-REBOOT events. */
+             lease->flags |= LEASE_CHANGED;
+
+#ifdef HAVE_SCRIPT
+             if (daemon->lease_change_command)
                {
-                 int len = option_len(opt);
-                 unsigned char *ucp = option_ptr(opt, 0);
-                 /* If the user-class option started as counted strings, the first byte will be zero. */
-                 if (len != 0 && ucp[0] == 0)
-                   ucp++, len--;
-                 add_extradata_data(lease, ucp, len, 0);
+                 struct dhcp_netid *n;
+                 
+                 if (mess->giaddr.s_addr)
+                   lease->giaddr = mess->giaddr;
+                 
+                 free(lease->extradata);
+                 lease->extradata = NULL;
+                 lease->extradata_size = lease->extradata_len = 0;
+                 
+                 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
+                 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
+                 add_extradata_opt(lease, oui);
+                 add_extradata_opt(lease, serial);
+                 add_extradata_opt(lease, class);
+
+                 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
+                   {
+                     add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_CIRCUIT_ID, 1));
+                     add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBSCR_ID, 1));
+                     add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, 1));
+                   }
+                 else
+                   {
+                     add_extradata_opt(lease, NULL);
+                     add_extradata_opt(lease, NULL);
+                     add_extradata_opt(lease, NULL);
+                   }
+
+                 /* space-concat tag set */
+                 if (!tagif_netid)
+                   add_extradata_opt(lease, NULL);
+                 else
+                   for (n = tagif_netid; n; n = n->next)
+                     {
+                       struct dhcp_netid *n1;
+                       /* kill dupes */
+                       for (n1 = n->next; n1; n1 = n1->next)
+                         if (strcmp(n->net, n1->net) == 0)
+                           break;
+                       if (!n1)
+                         lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0); 
+                     }
+                 
+                 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
+                   {
+                     int len = option_len(opt);
+                     unsigned char *ucp = option_ptr(opt, 0);
+                     /* If the user-class option started as counted strings, the first byte will be zero. */
+                     if (len != 0 && ucp[0] == 0)
+                       ucp++, len--;
+                     lease_add_extradata(lease, ucp, len, 0);
+                   }
                }
-           }
 #endif
+           }
          
          if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
            {
@@ -1223,7 +1322,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
            }
          
          time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
-         lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
+         lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
          
          /* if all the netids in the ignore_name list are present, ignore client-supplied name */
          if (!hostname_auth)
@@ -1254,41 +1353,33 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
            }
 
          if (hostname)
-           lease_set_hostname(lease, hostname, hostname_auth);
+           lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
          
          lease_set_expires(lease, time, now);
-         lease_set_interface(lease, int_index);
+         lease_set_interface(lease, int_index, now);
 
          if (override.s_addr != 0)
            lease->override = override;
          else
            override = lease->override;
 
-         log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, mess->xid);  
-         emit_dbus_signal(ACTION_CONNECT, lease, hostname);
+         log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);  
          
          clear_packet(mess, end);
          option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
          option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
          option_put(mess, end, OPTION_LEASE_TIME, 4, time);
-         if (time != 0xffffffff)
-           {
-             while (fuzz > (time/16))
-               fuzz = fuzz/2; 
-             option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
-             option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
-           }
          do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), 
-                    domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
+                    netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
        }
 
-      return dhcp_packet_size(mess, tagif_netid, agent_id, real_end); 
+      return dhcp_packet_size(mess, agent_id, real_end); 
       
     case DHCPINFORM:
       if (ignore || have_config(config, CONFIG_DISABLE))
        message = _("ignored");
       
-      log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
+      log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
      
       if (message || mess->ciaddr.s_addr == 0)
        return 0;
@@ -1303,20 +1394,22 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
          lease->hostname)
        hostname = lease->hostname;
       
-      if (!hostname && (hostname = host_from_dns(mess->ciaddr)))
-       domain = get_domain(mess->ciaddr);
-
-      log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
+      if (!hostname)
+       hostname = host_from_dns(mess->ciaddr);
       
       if (context && context->netid.net)
        {
          context->netid.next = netid;
-         netid = &context->netid;
-         tagif_netid = run_tag_if(netid);
+         tagif_netid = run_tag_if(&context->netid);
        }
+
+      log_tags(tagif_netid, ntohl(mess->xid));
+      
+      log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
       
       if (lease)
        {
+         lease_set_interface(lease, int_index, now);
          if (override.s_addr != 0)
            lease->override = override;
          else
@@ -1326,58 +1419,31 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
       clear_packet(mess, end);
       option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
       option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
-      
-      if (lease)
+     
+      /* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but 
+        we supply a utility which makes DHCPINFORM requests to get this information.
+        Only include lease time if OPTION_LEASE_TIME is in the parameter request list,
+        which won't be true for ordinary clients, but will be true for the 
+        dhcp_lease_time utility. */
+      if (lease && in_list(req_options, OPTION_LEASE_TIME))
        {
          if (lease->expires == 0)
            time = 0xffffffff;
          else
            time = (unsigned int)difftime(lease->expires, now);
          option_put(mess, end, OPTION_LEASE_TIME, 4, time);
-         lease_set_interface(lease, int_index);
        }
 
       do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
-                domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
+                netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0);
       
       *is_inform = 1; /* handle reply differently */
-      return dhcp_packet_size(mess, tagif_netid, agent_id, real_end); 
+      return dhcp_packet_size(mess, agent_id, real_end); 
     }
   
   return 0;
 }
 
-static int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
-{
-  int i;
-  
-  if (o->len > len)
-    return 0;
-  
-  if (o->len == 0)
-    return 1;
-     
-  if (o->flags & DHOPT_HEX)
-    { 
-      if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask))
-       return 1;
-    }
-  else 
-    for (i = 0; i <= (len - o->len); ) 
-      {
-       if (memcmp(o->val, p + i, o->len) == 0)
-         return 1;
-           
-       if (o->flags & DHOPT_STRING)
-         i++;
-       else
-         i += o->len;
-      }
-  
-  return 0;
-}
-      
-
 /* find a good value to use as MAC address for logging and address-allocation hashing.
    This is normally just the chaddr field from the DHCP packet,
    but eg Firewire will have hlen == 0 and use the client-id instead. 
@@ -1433,7 +1499,7 @@ static struct in_addr server_id(struct dhcp_context *context, struct in_addr ove
 {
   if (override.s_addr != 0)
     return override;
-  else if (context)
+  else if (context && context->local.s_addr != 0)
     return context->local;
   else
     return fallback;
@@ -1463,59 +1529,23 @@ static int sanitise(unsigned char *opt, char *buf)
 }
 
 #ifdef HAVE_SCRIPT
-static void add_extradata_data(struct dhcp_lease *lease, unsigned char *data, size_t len, int delim)
-{
-  if ((lease->extradata_size - lease->extradata_len) < (len + 1))
-    {
-      size_t newsz = lease->extradata_len + len + 100;
-      unsigned char *new = whine_malloc(newsz);
-  
-      if (!new)
-       return;
-      
-      if (lease->extradata)
-       {
-         memcpy(new, lease->extradata, lease->extradata_len);
-         free(lease->extradata);
-       }
-
-      lease->extradata = new;
-      lease->extradata_size = newsz;
-    }
-
-  if (len != 0)
-    memcpy(lease->extradata + lease->extradata_len, data, len);
-  lease->extradata[lease->extradata_len + len] = delim;
-  lease->extradata_len += len + 1; 
-}
-
 static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
 {
   if (!opt)
-    add_extradata_data(lease, NULL, 0, 0);
+    lease_add_extradata(lease, NULL, 0, 0);
   else
-    {
-      size_t i, len = option_len(opt);
-      unsigned char *ucp = option_ptr(opt, 0);
-      
-      /* check for embeded NULLs */
-      for (i = 0; i < len; i++)
-       if (ucp[i] == 0)
-         {
-           len = i;
-           break;
-         }
-
-      add_extradata_data(lease, ucp, len, 0);
-    }
+    lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0); 
 }
 #endif
 
 static void log_packet(char *type, void *addr, unsigned char *ext_mac, 
-                      int mac_len, char *interface, char *string, u32 xid)
+                      int mac_len, char *interface, char *string, char *err, u32 xid)
 {
   struct in_addr a;
  
+  if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
+    return;
+  
   /* addr may be misaligned */
   if (addr)
     memcpy(&a, addr, sizeof(a));
@@ -1523,52 +1553,34 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
   print_mac(daemon->namebuff, ext_mac, mac_len);
   
   if(option_bool(OPT_LOG_OPTS))
-     my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s",
+     my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
               ntohl(xid), 
               type,
               interface, 
               addr ? inet_ntoa(a) : "",
               addr ? " " : "",
               daemon->namebuff,
-              string ? string : "");
+              string ? string : "",
+              err ? err : "");
   else
-    my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s",
+    my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
              type,
              interface, 
              addr ? inet_ntoa(a) : "",
              addr ? " " : "",
              daemon->namebuff,
-             string ? string : "");
+             string ? string : "",
+             err ? err : "");
 }
 
 static void log_options(unsigned char *start, u32 xid)
 {
   while (*start != OPTION_END)
     {
-      int is_ip, is_name, i;
-      char *text = option_string(start[0], &is_ip, &is_name);
-      unsigned char trunc = option_len(start);
+      char *optname = option_string(AF_INET, start[0], option_ptr(start, 0), option_len(start), daemon->namebuff, MAXDNAME);
       
-      if (is_ip)
-       for (daemon->namebuff[0]= 0, i = 0; i <= trunc - INADDRSZ; i += INADDRSZ) 
-         {
-           if (i != 0)
-             strncat(daemon->namebuff, ", ", 256 - strlen(daemon->namebuff));
-           strncat(daemon->namebuff, inet_ntoa(option_addr_arr(start, i)), 256 - strlen(daemon->namebuff));
-         }
-      else if (!is_name || !sanitise(start, daemon->namebuff))
-       {
-         if (trunc > 13)
-           trunc = 13;
-         print_mac(daemon->namebuff, option_ptr(start, 0), trunc);
-       }
-      
-      my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d%s%s%s%s%s", 
-               ntohl(xid), option_len(start), start[0],
-               text ? ":" : "", text ? text : "",
-               trunc == 0 ? "" : "  ",
-               trunc == 0 ? "" : daemon->namebuff,
-               trunc == option_len(start) ? "" : "...");
+      my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s  %s", 
+               ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
       start += start[1] + 2;
     }
 }
@@ -1623,22 +1635,17 @@ static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt
   return NULL;
 }
 
-static struct in_addr option_addr_arr(unsigned char *opt, int offset)
+static struct in_addr option_addr(unsigned char *opt)
 {
-  /* this worries about unaligned data in the option. */
+   /* this worries about unaligned data in the option. */
   /* struct in_addr is network byte order */
   struct in_addr ret;
 
-  memcpy(&ret, option_ptr(opt, offset), INADDRSZ);
+  memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
 
   return ret;
 }
 
-static struct in_addr option_addr(unsigned char *opt)
-{
-  return option_addr_arr(opt, 0);
-}
-
 static unsigned int option_uint(unsigned char *opt, int offset, int size)
 {
   /* this worries about unaligned data and byte order */
@@ -1673,15 +1680,12 @@ static unsigned char *find_overload(struct dhcp_packet *mess)
   return NULL;
 }
 
-static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid,
-                              unsigned char *agent_id, unsigned char *real_end)
+static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
 {
   unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
   unsigned char *overload;
   size_t ret;
-  struct dhcp_netid_list *id_list;
-  struct dhcp_netid *n;
-
+  
   /* move agent_id back down to the end of the packet */
   if (agent_id)
     {
@@ -1690,27 +1694,6 @@ static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *neti
       memset(p, 0, real_end - p); /* in case of overlap */
     }
   
-  /* We do logging too */
-  if (netid && option_bool(OPT_LOG_OPTS))
-    {
-      char *s = daemon->namebuff;
-      for (*s = 0; netid; netid = netid->next)
-       {
-         /* kill dupes. */
-         for (n = netid->next; n; n = n->next)
-           if (strcmp(netid->net, n->net) == 0)
-             break;
-         
-         if (!n)
-           {
-             strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
-             if (netid->next)
-               strncat (s, ", ", (MAXDNAME-1) - strlen(s));
-           }
-       }
-      my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), ntohl(mess->xid), s);
-    } 
-   
   /* add END options to the regions. */
   overload = find_overload(mess);
   
@@ -1735,12 +1718,6 @@ static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *neti
 
   *p++ = OPTION_END;
   
-  for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
-    if ((!id_list->list) || match_netid(id_list->list, netid, 0))
-      break;
-  if (id_list)
-    mess->flags |= htons(0x8000); /* force broadcast */
-  
   if (option_bool(OPT_LOG_OPTS))
     {
       if (mess->siaddr.s_addr != 0)
@@ -1803,7 +1780,7 @@ static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, i
              if (overload[2] & 2)
                {
                  p = dhcp_skip_opts(mess->sname);
-                 if (p + len + 3 >= mess->sname + sizeof(mess->file))
+                 if (p + len + 3 >= mess->sname + sizeof(mess->sname))
                    p = NULL;
                }
            }
@@ -1870,7 +1847,8 @@ static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *c
            }
        }
       else
-       memcpy(p, opt->val, len);
+       /* empty string may be extended to "\0" by null_term */
+       memcpy(p, opt->val ? opt->val : (unsigned char *)"", len);
     }  
   return len;
 }
@@ -1890,20 +1868,14 @@ static int in_list(unsigned char *list, int opt)
   return 0;
 }
 
-static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
+static struct dhcp_opt *option_find2(int opt)
 {
-  struct dhcp_opt *tmp;  
-  for (tmp = opts; tmp; tmp = tmp->next)
-    if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
-      if (match_netid(tmp->netid, netid, 0))
-       return tmp;
-
-  /* No match, look for one without a netid */
-  for (tmp = opts; tmp; tmp = tmp->next)
-    if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
-      if (match_netid(tmp->netid, netid, 1))
-       return tmp;
-                     
+  struct dhcp_opt *opts;
+  
+  for (opts = daemon->dhcp_opts; opts; opts = opts->next)
+    if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
+      return opts;
+  
   return NULL;
 }
 
@@ -2003,7 +1975,7 @@ static int prune_vendor_opts(struct dhcp_netid *netid)
   return force;
 }
 
-static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local)
+static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now)
 {
 #define NUM_OPTS 4  
 
@@ -2060,8 +2032,9 @@ static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct
            return daemon->dhcp_opts;
          }
        
-       boot_server = service->basename ? local : service->server;
-
+       boot_server = service->basename ? local : 
+         (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
+       
        if (boot_server.s_addr != 0)
          {
            if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
@@ -2152,13 +2125,16 @@ static void do_options(struct dhcp_context *context,
                       unsigned char *end, 
                       unsigned char *req_options,
                       char *hostname, 
-                      char *domain, char *config_domain,
+                      char *domain,
                       struct dhcp_netid *netid,
                       struct in_addr subnet_addr,
                       unsigned char fqdn_flags,
                       int null_term, int pxe_arch,
                       unsigned char *uuid,
-                      int vendor_class_len)
+                      int vendor_class_len,
+                      time_t now,
+                      unsigned int lease_time,
+                      unsigned short fuzz)
 {
   struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
   struct dhcp_boot *boot;
@@ -2167,22 +2143,26 @@ static void do_options(struct dhcp_context *context,
   unsigned char f0 = 0, s0 = 0;
   int done_file = 0, done_server = 0;
   int done_vendor_class = 0;
+  struct dhcp_netid *tagif;
+  struct dhcp_netid_list *id_list;
 
-  if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
-    my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, hostname);
-  
+  /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
+  if (context)
+    context->netid.next = NULL;
+  tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
+       
   /* logging */
   if (option_bool(OPT_LOG_OPTS) && req_options)
     {
       char *q = daemon->namebuff;
       for (i = 0; req_options[i] != OPTION_END; i++)
        {
-         char *s = option_string(req_options[i], NULL, NULL);
+         char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
          q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
                        "%d%s%s%s", 
                        req_options[i],
-                       s ? ":" : "",
-                       s ? s : ""
+                       strlen(s) != 0 ? ":" : "",
+                       s, 
                        req_options[i+1] == OPTION_END ? "" : ", ");
          if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
            {
@@ -2192,6 +2172,12 @@ static void do_options(struct dhcp_context *context,
        }
     }
       
+  for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
+    if ((!id_list->list) || match_netid(id_list->list, netid, 0))
+      break;
+  if (id_list)
+    mess->flags |= htons(0x8000); /* force broadcast */
+  
   if (context)
     mess->siaddr = context->local;
   
@@ -2201,7 +2187,7 @@ static void do_options(struct dhcp_context *context,
      provide an manual option to disable it.
      Some PXE ROMs have bugs (surprise!) and need zero-terminated 
      names, so we always send those.  */
-  if ((boot = find_boot(netid)))
+  if ((boot = find_boot(tagif)))
     {
       if (boot->sname)
        {         
@@ -2223,8 +2209,10 @@ static void do_options(struct dhcp_context *context,
            strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
        }
       
-      if (boot->next_server.s_addr)
+      if (boot->next_server.s_addr) 
        mess->siaddr = boot->next_server;
+      else if (boot->tftp_sname)
+       mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
     }
   else
     /* Use the values of the relevant options if no dhcp-boot given and
@@ -2233,20 +2221,20 @@ static void do_options(struct dhcp_context *context,
        dhcp-optsfile. */
     {
       if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
-         (opt = option_find2(netid, config_opts, OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
+         (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
        {
          strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
          done_file = 1;
        }
       
       if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
-         (opt = option_find2(netid, config_opts, OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
+         (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
        {
          strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
          done_server = 1;
        }
       
-      if ((opt = option_find2(netid, config_opts, OPTION_END)))
+      if ((opt = option_find2(OPTION_END)))
        mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;     
     }
         
@@ -2270,40 +2258,76 @@ static void do_options(struct dhcp_context *context,
   /* rfc3011 says this doesn't need to be in the requested options list. */
   if (subnet_addr.s_addr)
     option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
-  
+   
+  if (lease_time != 0xffffffff)
+    { 
+      unsigned int t1val = lease_time/2; 
+      unsigned int t2val = (lease_time*7)/8;
+      unsigned int hval;
+      
+      /* If set by user, sanity check, so not longer than lease. */
+      if ((opt = option_find2(OPTION_T1)))
+       {
+         hval = ntohl(*((unsigned int *)opt->val));
+         if (hval < lease_time && hval > 2)
+           t1val = hval;
+       }
+
+       if ((opt = option_find2(OPTION_T2)))
+       {
+         hval = ntohl(*((unsigned int *)opt->val));
+         if (hval < lease_time && hval > 2)
+           t2val = hval;
+       }
+                 
+       /* ensure T1 is still < T2 */
+       if (t2val <= t1val)
+        t1val = t2val - 1; 
+
+       while (fuzz > (t1val/8))
+        fuzz = fuzz/2;
+        
+       t1val -= fuzz;
+       t2val -= fuzz;
+       
+       option_put(mess, end, OPTION_T1, 4, t1val);
+       option_put(mess, end, OPTION_T2, 4, t2val);
+    }
+
   /* replies to DHCPINFORM may not have a valid context */
   if (context)
     {
-      if (!option_find2(netid, config_opts, OPTION_NETMASK))
+      if (!option_find2(OPTION_NETMASK))
        option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
   
       /* May not have a "guessed" broadcast address if we got no packets via a relay
         from this net yet (ie just unicast renewals after a restart */
       if (context->broadcast.s_addr &&
-         !option_find2(netid, config_opts, OPTION_BROADCAST))
+         !option_find2(OPTION_BROADCAST))
        option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
       
       /* Same comments as broadcast apply, and also may not be able to get a sensible
         default when using subnet select.  User must configure by steam in that case. */
       if (context->router.s_addr &&
          in_list(req_options, OPTION_ROUTER) &&
-         !option_find2(netid, config_opts, OPTION_ROUTER))
+         !option_find2(OPTION_ROUTER))
        option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
       
-      if (in_list(req_options, OPTION_DNSSERVER) &&
-         !option_find2(netid, config_opts, OPTION_DNSSERVER))
+      if (daemon->port == NAMESERVER_PORT &&
+         in_list(req_options, OPTION_DNSSERVER) &&
+         !option_find2(OPTION_DNSSERVER))
        option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
     }
 
   if (domain && in_list(req_options, OPTION_DOMAINNAME) && 
-      !option_find2(netid, config_opts, OPTION_DOMAINNAME))
+      !option_find2(OPTION_DOMAINNAME))
     option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
  
   /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
   if (hostname)
     {
       if (in_list(req_options, OPTION_HOSTNAME) &&
-         !option_find2(netid, config_opts, OPTION_HOSTNAME))
+         !option_find2(OPTION_HOSTNAME))
        option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
       
       if (fqdn_flags != 0)
@@ -2317,10 +2341,12 @@ static void do_options(struct dhcp_context *context,
 
          if (domain)
            len += strlen(domain) + 1;
-         
+         else if (fqdn_flags & 0x04)
+           len--;
+
          if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
            {
-             *(p++) = fqdn_flags;
+             *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */ 
              *(p++) = 255;
              *(p++) = 255;
 
@@ -2328,8 +2354,10 @@ static void do_options(struct dhcp_context *context,
                {
                  p = do_rfc1035_name(p, hostname);
                  if (domain)
-                   p = do_rfc1035_name(p, domain);
-                 *p++ = 0;
+                   {
+                     p = do_rfc1035_name(p, domain);
+                     *p++ = 0;
+                   }
                }
              else
                {
@@ -2352,16 +2380,22 @@ static void do_options(struct dhcp_context *context,
     {
       int optno = opt->opt;
 
+      /* netids match and not encapsulated? */
+      if (!(opt->flags & DHOPT_TAGOK))
+       continue;
+      
       /* was it asked for, or are we sending it anyway? */
       if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
        continue;
       
-      /* prohibit some used-internally options */
+      /* prohibit some used-internally options. T1 and T2 already handled. */
       if (optno == OPTION_CLIENT_FQDN ||
          optno == OPTION_MAXMESSAGE ||
          optno == OPTION_OVERLOAD ||
          optno == OPTION_PAD ||
-         optno == OPTION_END)
+         optno == OPTION_END ||
+         optno == OPTION_T1 ||
+         optno == OPTION_T2)
        continue;
 
       if (optno == OPTION_SNAME && done_server)
@@ -2370,10 +2404,6 @@ static void do_options(struct dhcp_context *context,
       if (optno == OPTION_FILENAME && done_file)
        continue;
       
-      /* netids match and not encapsulated? */
-      if (opt != option_find2(netid, config_opts, optno))
-       continue;
-      
       /* For the options we have default values on
         dhc-option=<optionno> means "don't include this option"
         not "include a zero-length option" */
@@ -2411,8 +2441,8 @@ static void do_options(struct dhcp_context *context,
 
   /* Now send options to be encapsulated in arbitrary options, 
      eg dhcp-option=encap:172,17,.......
-     Also hand vendor-identifying vendor-encapsulated options,
-     dhcp-option = rfc3925-encap:13,17,.......
+     Also handle vendor-identifying vendor-encapsulated options,
+     dhcp-option = vi-encap:13,17,.......
      The may be more that one "outer" to do, so group
      all the options which match each outer in turn. */
   for (opt = config_opts; opt; opt = opt->next)
@@ -2440,7 +2470,7 @@ static void do_options(struct dhcp_context *context,
                continue;
              
              o->flags |= DHOPT_ENCAP_DONE;
-             if (match_netid(o->netid, netid, 1) &&
+             if (match_netid(o->netid, tagif, 1) &&
                  ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
                {
                  o->flags |= DHOPT_ENCAP_MATCH;
@@ -2474,12 +2504,12 @@ static void do_options(struct dhcp_context *context,
        }
     }      
 
-  force_encap = prune_vendor_opts(netid);
+  force_encap = prune_vendor_opts(tagif);
   
   if (context && pxe_arch != -1)
     {
       pxe_misc(mess, end, uuid);
-      config_opts = pxe_opts(pxe_arch, netid, context->local);
+      config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
     }
 
   if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
diff --git a/src/rfc3315.c b/src/rfc3315.c
new file mode 100644 (file)
index 0000000..2665d0d
--- /dev/null
@@ -0,0 +1,2183 @@
+/* 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"
+
+#ifdef HAVE_DHCP6
+
+struct state {
+  unsigned char *clid;
+  int clid_len, iaid, ia_type, interface, hostname_auth, lease_allocate;
+  char *client_hostname, *hostname, *domain, *send_domain;
+  struct dhcp_context *context;
+  struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr;
+  unsigned int xid, fqdn_flags;
+  char *iface_name;
+  void *packet_options, *end;
+  struct dhcp_netid *tags, *context_tags;
+  unsigned char mac[DHCP_CHADDR_MAX];
+  unsigned int mac_len, mac_type;
+#ifdef OPTION6_PREFIX_CLASS
+  struct prefix_class *send_prefix_class;
+#endif
+};
+
+static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz, 
+                            struct in6_addr *client_addr, int is_unicast, time_t now);
+static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_t sz, int is_unicast, time_t now);
+static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts);
+static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string);
+static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string);
+static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize);
+static void *opt6_next(void *opts, void *end);
+static unsigned int opt6_uint(unsigned char *opt, int offset, int size);
+static void get_context_tag(struct state *state, struct dhcp_context *context);
+static int check_ia(struct state *state, void *opt, void **endp, void **ia_option);
+static int build_ia(struct state *state, int *t1cntr);
+static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz);
+#ifdef OPTION6_PREFIX_CLASS
+static struct prefix_class *prefix_class_from_context(struct dhcp_context *context);
+#endif
+static void mark_context_used(struct state *state, struct in6_addr *addr);
+static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr);
+static int check_address(struct state *state, struct in6_addr *addr);
+static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, 
+                       unsigned int *min_time, struct in6_addr *addr, time_t now);
+static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now);
+static int add_local_addrs(struct dhcp_context *context);
+static struct dhcp_netid *add_options(struct state *state, int do_refresh);
+static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep, 
+                           unsigned int *preferred_timep, unsigned int lease_time);
+
+#define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2)))
+#define opt6_type(opt) (opt6_uint(opt, -4, 2))
+#define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)]))
+
+#define opt6_user_vendor_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2+(i)]))
+#define opt6_user_vendor_len(opt) ((int)(opt6_uint(opt, -4, 2)))
+#define opt6_user_vendor_next(opt, end) (opt6_next(((void *) opt) - 2, end))
+
+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)
+{
+  struct dhcp_vendor *vendor;
+  int msg_type;
+  struct state state;
+  
+  if (sz <= 4)
+    return 0;
+  
+  msg_type = *((unsigned char *)daemon->dhcp_packet.iov_base);
+  
+  /* Mark these so we only match each at most once, to avoid tangled linked lists */
+  for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
+    vendor->netid.next = &vendor->netid;
+  
+  save_counter(0);
+  state.context = context;
+  state.interface = interface;
+  state.iface_name = iface_name;
+  state.fallback = fallback;
+  state.ll_addr = ll_addr;
+  state.ula_addr = ula_addr;
+  state.mac_len = 0;
+  state.tags = NULL;
+  state.link_address = NULL;
+
+  if (dhcp6_maybe_relay(&state, daemon->dhcp_packet.iov_base, sz, client_addr, 
+                       IN6_IS_ADDR_MULTICAST(client_addr), now))
+    return msg_type == DHCP6RELAYFORW ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;
+
+  return 0;
+}
+
+/* This cost me blood to write, it will probably cost you blood to understand - srk. */
+static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz, 
+                            struct in6_addr *client_addr, int is_unicast, time_t now)
+{
+  void *end = inbuff + sz;
+  void *opts = inbuff + 34;
+  int msg_type = *((unsigned char *)inbuff);
+  unsigned char *outmsgtypep;
+  void *opt;
+  struct dhcp_vendor *vendor;
+
+  /* if not an encaplsulated relayed message, just do the stuff */
+  if (msg_type != DHCP6RELAYFORW)
+    {
+      /* if link_address != NULL if points to the link address field of the 
+        innermost nested RELAYFORW message, which is where we find the
+        address of the network on which we can allocate an address.
+        Recalculate the available contexts using that information. 
+
+      link_address == NULL means there's no relay in use, so we try and find the client's 
+      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);
+      else
+       {
+         struct dhcp_context *c;
+         state->context = NULL;
+          
+         if (!IN6_IS_ADDR_LOOPBACK(state->link_address) &&
+             !IN6_IS_ADDR_LINKLOCAL(state->link_address) &&
+             !IN6_IS_ADDR_MULTICAST(state->link_address))
+           for (c = daemon->dhcp6; c; c = c->next)
+             if ((c->flags & CONTEXT_DHCP) &&
+                 !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
+                 is_same_net6(state->link_address, &c->start6, c->prefix) &&
+                 is_same_net6(state->link_address, &c->end6, c->prefix))
+               {
+                 c->preferred = c->valid = 0xffffffff;
+                 c->current = state->context;
+                 state->context = c;
+               }
+         
+         if (!state->context)
+           {
+             inet_ntop(AF_INET6, state->link_address, daemon->addrbuff, ADDRSTRLEN); 
+             my_syslog(MS_DHCP | LOG_WARNING, 
+                       _("no address range available for DHCPv6 request from relay at %s"),
+                       daemon->addrbuff);
+             return 0;
+           }
+       }
+         
+      if (!state->context)
+       {
+         my_syslog(MS_DHCP | LOG_WARNING, 
+                   _("no address range available for DHCPv6 request via %s"), state->iface_name);
+         return 0;
+       }
+
+      return dhcp6_no_relay(state, msg_type, inbuff, sz, is_unicast, now);
+    }
+
+  /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
+     which is               1   +    1   +    16      +     16     + 2 + 2 = 38 */
+  if (sz < 38)
+    return 0;
+  
+  /* copy header stuff into reply message and set type to reply */
+  if (!(outmsgtypep = put_opt6(inbuff, 34)))
+    return 0;
+  *outmsgtypep = DHCP6RELAYREPL;
+
+  /* look for relay options and set tags if found. */
+  for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
+    {
+      int mopt;
+      
+      if (vendor->match_type == MATCH_SUBSCRIBER)
+       mopt = OPTION6_SUBSCRIBER_ID;
+      else if (vendor->match_type == MATCH_REMOTE)
+       mopt = OPTION6_REMOTE_ID; 
+      else
+       continue;
+
+      if ((opt = opt6_find(opts, end, mopt, 1)) &&
+         vendor->len == opt6_len(opt) &&
+         memcmp(vendor->data, opt6_ptr(opt, 0), vendor->len) == 0 &&
+         vendor->netid.next != &vendor->netid)
+       {
+         vendor->netid.next = state->tags;
+         state->tags = &vendor->netid;
+         break;
+       }
+    }
+  
+  /* RFC-6939 */
+  if ((opt = opt6_find(opts, end, OPTION6_CLIENT_MAC, 3)))
+    {
+      state->mac_type = opt6_uint(opt, 0, 2);
+      state->mac_len = opt6_len(opt) - 2;
+      memcpy(&state->mac[0], opt6_ptr(opt, 2), state->mac_len);
+    }
+  
+  for (opt = opts; opt; opt = opt6_next(opt, end))
+    {
+      int o = new_opt6(opt6_type(opt));
+      if (opt6_type(opt) == OPTION6_RELAY_MSG)
+       {
+         struct in6_addr align;
+         /* the packet data is unaligned, copy to aligned storage */
+         memcpy(&align, inbuff + 2, IN6ADDRSZ); 
+         state->link_address = &align;
+         /* zero is_unicast since that is now known to refer to the 
+            relayed packet, not the original sent by the client */
+         if (!dhcp6_maybe_relay(state, opt6_ptr(opt, 0), opt6_len(opt), client_addr, 0, now))
+           return 0;
+       }
+      else if (opt6_type(opt) != OPTION6_CLIENT_MAC)
+       put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
+      end_opt6(o);         
+    }
+  
+  return 1;
+}
+
+static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_t sz, int is_unicast, time_t now)
+{
+  void *opt;
+  int i, o, o1, start_opts;
+  struct dhcp_opt *opt_cfg;
+  struct dhcp_netid *tagif;
+  struct dhcp_config *config = NULL;
+  struct dhcp_netid known_id, iface_id, v6_id;
+  unsigned char *outmsgtypep;
+  struct dhcp_vendor *vendor;
+  struct dhcp_context *context_tmp;
+  struct dhcp_mac *mac_opt;
+  unsigned int ignore = 0;
+#ifdef OPTION6_PREFIX_CLASS
+  struct prefix_class *p;
+  int dump_all_prefix_classes = 0;
+#endif
+
+  state->packet_options = inbuff + 4;
+  state->end = inbuff + sz;
+  state->clid = NULL;
+  state->clid_len = 0;
+  state->lease_allocate = 0;
+  state->context_tags = NULL;
+  state->domain = NULL;
+  state->send_domain = NULL;
+  state->hostname_auth = 0;
+  state->hostname = NULL;
+  state->client_hostname = NULL;
+  state->fqdn_flags = 0x01; /* default to send if we recieve no FQDN option */
+#ifdef OPTION6_PREFIX_CLASS
+  state->send_prefix_class = NULL;
+#endif
+
+  /* set tag with name == interface */
+  iface_id.net = state->iface_name;
+  iface_id.next = state->tags;
+  state->tags = &iface_id; 
+
+  /* set tag "dhcpv6" */
+  v6_id.net = "dhcpv6";
+  v6_id.next = state->tags;
+  state->tags = &v6_id;
+
+  /* copy over transaction-id, and save pointer to message type */
+  if (!(outmsgtypep = put_opt6(inbuff, 4)))
+    return 0;
+  start_opts = save_counter(-1);
+  state->xid = outmsgtypep[3] | outmsgtypep[2] << 8 | outmsgtypep[1] << 16;
+   
+  /* We're going to be linking tags from all context we use. 
+     mark them as unused so we don't link one twice and break the list */
+  for (context_tmp = state->context; context_tmp; context_tmp = context_tmp->current)
+    {
+      context_tmp->netid.next = &context_tmp->netid;
+
+      if (option_bool(OPT_LOG_OPTS))
+       {
+          inet_ntop(AF_INET6, &context_tmp->start6, daemon->dhcp_buff, ADDRSTRLEN); 
+          inet_ntop(AF_INET6, &context_tmp->end6, daemon->dhcp_buff2, ADDRSTRLEN); 
+          if (context_tmp->flags & (CONTEXT_STATIC))
+            my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCPv6 subnet: %s/%d"),
+                      state->xid, daemon->dhcp_buff, context_tmp->prefix);
+          else
+            my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"), 
+                      state->xid, daemon->dhcp_buff, daemon->dhcp_buff2);
+       }
+    }
+
+  if ((opt = opt6_find(state->packet_options, state->end, OPTION6_CLIENT_ID, 1)))
+    {
+      state->clid = opt6_ptr(opt, 0);
+      state->clid_len = opt6_len(opt);
+      o = new_opt6(OPTION6_CLIENT_ID);
+      put_opt6(state->clid, state->clid_len);
+      end_opt6(o);
+    }
+  else if (msg_type != DHCP6IREQ)
+    return 0;
+
+  /* server-id must match except for SOLICIT, CONFIRM and REBIND messages */
+  if (msg_type != DHCP6SOLICIT && msg_type != DHCP6CONFIRM && msg_type != DHCP6IREQ && msg_type != DHCP6REBIND &&
+      (!(opt = opt6_find(state->packet_options, state->end, OPTION6_SERVER_ID, 1)) ||
+       opt6_len(opt) != daemon->duid_len ||
+       memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))
+    return 0;
+  
+  o = new_opt6(OPTION6_SERVER_ID);
+  put_opt6(daemon->duid, daemon->duid_len);
+  end_opt6(o);
+
+  if (is_unicast &&
+      (msg_type == DHCP6REQUEST || msg_type == DHCP6RENEW || msg_type == DHCP6RELEASE || msg_type == DHCP6DECLINE))
+    
+    {  
+      *outmsgtypep = DHCP6REPLY;
+      o1 = new_opt6(OPTION6_STATUS_CODE);
+      put_opt6_short(DHCP6USEMULTI);
+      put_opt6_string("Use multicast");
+      end_opt6(o1);
+      return 1;
+    }
+
+  /* match vendor and user class options */
+  for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
+    {
+      int mopt;
+      
+      if (vendor->match_type == MATCH_VENDOR)
+       mopt = OPTION6_VENDOR_CLASS;
+      else if (vendor->match_type == MATCH_USER)
+       mopt = OPTION6_USER_CLASS; 
+      else
+       continue;
+
+      if ((opt = opt6_find(state->packet_options, state->end, mopt, 2)))
+       {
+         void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt));
+         int offset = 0;
+         
+         if (mopt == OPTION6_VENDOR_CLASS)
+           {
+             if (opt6_len(opt) < 4)
+               continue;
+             
+             if (vendor->enterprise != opt6_uint(opt, 0, 4))
+               continue;
+           
+             offset = 4;
+           }
+         /* Note that format if user/vendor classes is different to DHCP options - no option types. */
+         for (enc_opt = opt6_ptr(opt, offset); enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end))
+           for (i = 0; i <= (opt6_user_vendor_len(enc_opt) - vendor->len); i++)
+             if (memcmp(vendor->data, opt6_user_vendor_ptr(enc_opt, i), vendor->len) == 0)
+               {
+                 vendor->netid.next = state->tags;
+                 state->tags = &vendor->netid;
+                 break;
+               }
+       }
+    }
+
+  if (option_bool(OPT_LOG_OPTS) && (opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4)))
+    my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %u"), state->xid, opt6_uint(opt, 0, 4));
+  
+  /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
+     Otherwise assume the option is an array, and look for a matching element. 
+     If no data given, existance of the option is enough. This code handles 
+     V-I opts too. */
+  for (opt_cfg = daemon->dhcp_match6; opt_cfg; opt_cfg = opt_cfg->next)
+    {
+      int match = 0;
+      
+      if (opt_cfg->flags & DHOPT_RFC3925)
+       {
+         for (opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_OPTS, 4);
+              opt;
+              opt = opt6_find(opt6_next(opt, state->end), state->end, OPTION6_VENDOR_OPTS, 4))
+           {
+             void *vopt;
+             void *vend = opt6_ptr(opt, opt6_len(opt));
+             
+             for (vopt = opt6_find(opt6_ptr(opt, 4), vend, opt_cfg->opt, 0);
+                  vopt;
+                  vopt = opt6_find(opt6_next(vopt, vend), vend, opt_cfg->opt, 0))
+               if ((match = match_bytes(opt_cfg, opt6_ptr(vopt, 0), opt6_len(vopt))))
+                 break;
+           }
+         if (match)
+           break;
+       }
+      else
+       {
+         if (!(opt = opt6_find(state->packet_options, state->end, opt_cfg->opt, 1)))
+           continue;
+         
+         match = match_bytes(opt_cfg, opt6_ptr(opt, 0), opt6_len(opt));
+       } 
+  
+      if (match)
+       {
+         opt_cfg->netid->next = state->tags;
+         state->tags = opt_cfg->netid;
+       }
+    }
+
+  if (state->mac_len != 0)
+    {
+      if (option_bool(OPT_LOG_OPTS))
+       {
+         print_mac(daemon->dhcp_buff, state->mac, state->mac_len);
+         my_syslog(MS_DHCP | LOG_INFO, _("%u client MAC address: %s"), state->xid, daemon->dhcp_buff);
+       }
+
+      for (mac_opt = daemon->dhcp_macs; mac_opt; mac_opt = mac_opt->next)
+       if ((unsigned)mac_opt->hwaddr_len == state->mac_len &&
+           ((unsigned)mac_opt->hwaddr_type == state->mac_type || mac_opt->hwaddr_type == 0) &&
+           memcmp_masked(mac_opt->hwaddr, state->mac, state->mac_len, mac_opt->mask))
+         {
+           mac_opt->netid.next = state->tags;
+           state->tags = &mac_opt->netid;
+         }
+    }
+  
+  if ((opt = opt6_find(state->packet_options, state->end, OPTION6_FQDN, 1)))
+    {
+      /* RFC4704 refers */
+       int len = opt6_len(opt) - 1;
+       
+       state->fqdn_flags = opt6_uint(opt, 0, 1);
+       
+       /* Always force update, since the client has no way to do it itself. */
+       if (!option_bool(OPT_FQDN_UPDATE) && !(state->fqdn_flags & 0x01))
+        state->fqdn_flags |= 0x03;
+       state->fqdn_flags &= ~0x04;
+
+       if (len != 0 && len < 255)
+        {
+          unsigned char *pp, *op = opt6_ptr(opt, 1);
+          char *pq = daemon->dhcp_buff;
+          
+          pp = op;
+          while (*op != 0 && ((op + (*op)) - pp) < len)
+            {
+              memcpy(pq, op+1, *op);
+              pq += *op;
+              op += (*op)+1;
+              *(pq++) = '.';
+            }
+          
+          if (pq != daemon->dhcp_buff)
+            pq--;
+          *pq = 0;
+          
+          if (legal_hostname(daemon->dhcp_buff))
+            {
+              state->client_hostname = daemon->dhcp_buff;
+              if (option_bool(OPT_LOG_OPTS))
+                my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), state->xid, state->client_hostname); 
+            }
+        }
+    }   
+  
+  if (state->clid)
+    {
+      config = find_config(daemon->dhcp_conf, state->context, state->clid, state->clid_len, state->mac, state->mac_len, state->mac_type, NULL);
+      
+      if (have_config(config, CONFIG_NAME))
+       {
+         state->hostname = config->hostname;
+         state->domain = config->domain;
+         state->hostname_auth = 1;
+       }
+      else if (state->client_hostname)
+       {
+         state->domain = strip_hostname(state->client_hostname);
+         
+         if (strlen(state->client_hostname) != 0)
+           {
+             state->hostname = state->client_hostname;
+             if (!config)
+               {
+                 /* Search again now we have a hostname. 
+                    Only accept configs without CLID here, (it won't match)
+                    to avoid impersonation by name. */
+                 struct dhcp_config *new = find_config(daemon->dhcp_conf, state->context, NULL, 0, NULL, 0, 0, state->hostname);
+                 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
+                   config = new;
+               }
+           }
+       }
+    }
+
+  if (config)
+    {
+      struct dhcp_netid_list *list;
+      
+      for (list = config->netid; list; list = list->next)
+        {
+          list->list->next = state->tags;
+          state->tags = list->list;
+        }
+
+      /* set "known" tag for known hosts */
+      known_id.net = "known";
+      known_id.next = state->tags;
+      state->tags = &known_id;
+
+      if (have_config(config, CONFIG_DISABLE))
+       ignore = 1;
+    }
+
+#ifdef OPTION6_PREFIX_CLASS
+  /* OPTION_PREFIX_CLASS in ORO, send addresses in all prefix classes */
+  if (daemon->prefix_classes && (msg_type == DHCP6SOLICIT || msg_type == DHCP6REQUEST))
+    {
+      void *oro;
+      
+      if ((oro = opt6_find(state->packet_options, state->end, OPTION6_ORO, 0)))
+       for (i = 0; i <  opt6_len(oro) - 1; i += 2)
+         if (opt6_uint(oro, i, 2) == OPTION6_PREFIX_CLASS)
+           {
+             dump_all_prefix_classes = 1;
+             break;
+           }
+      
+      if (msg_type != DHCP6SOLICIT || dump_all_prefix_classes)
+       /* Add the tags associated with prefix classes so we can use the DHCP ranges.
+          Not done for SOLICIT as we add them  one-at-time. */
+       for (p = daemon->prefix_classes; p ; p = p->next)
+         {
+           p->tag.next = state->tags;
+           state->tags = &p->tag;
+         }
+    }    
+#endif
+
+  tagif = run_tag_if(state->tags);
+  
+  /* if all the netids in the ignore list are present, ignore this client */
+  if (daemon->dhcp_ignore)
+    {
+      struct dhcp_netid_list *id_list;
+     
+      for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
+       if (match_netid(id_list->list, tagif, 0))
+         ignore = 1;
+    }
+  
+  /* if all the netids in the ignore_name list are present, ignore client-supplied name */
+  if (!state->hostname_auth)
+    {
+       struct dhcp_netid_list *id_list;
+       
+       for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
+        if ((!id_list->list) || match_netid(id_list->list, tagif, 0))
+          break;
+       if (id_list)
+        state->hostname = NULL;
+    }
+  
+
+  switch (msg_type)
+    {
+    default:
+      return 0;
+      
+      
+    case DHCP6SOLICIT:
+      {
+       int address_assigned = 0;
+       /* tags without all prefix-class tags */
+       struct dhcp_netid *solicit_tags;
+       struct dhcp_context *c;
+       
+       *outmsgtypep = DHCP6ADVERTISE;
+       
+       if (opt6_find(state->packet_options, state->end, OPTION6_RAPID_COMMIT, 0))
+         {
+           *outmsgtypep = DHCP6REPLY;
+           state->lease_allocate = 1;
+           o = new_opt6(OPTION6_RAPID_COMMIT);
+           end_opt6(o);
+         }
+       
+       log6_quiet(state, "DHCPSOLICIT", NULL, ignore ? _("ignored") : NULL);
+
+      request_no_address:
+       solicit_tags = tagif;
+       
+       if (ignore)
+         return 0;
+       
+       /* reset USED bits in leases */
+       lease6_reset();
+
+       /* Can use configured address max once per prefix */
+       for (c = state->context; c; c = c->current)
+         c->flags &= ~CONTEXT_CONF_USED;
+
+       for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
+         {   
+           void *ia_option, *ia_end;
+           unsigned int min_time = 0xffffffff;
+           int t1cntr;
+           int ia_counter;
+           /* set unless we're sending a particular prefix-class, when we
+              want only dhcp-ranges with the correct tags set and not those without any tags. */
+           int plain_range = 1;
+           u32 lease_time;
+           struct dhcp_lease *ltmp;
+           struct in6_addr *req_addr;
+           struct in6_addr addr;
+
+           if (!check_ia(state, opt, &ia_end, &ia_option))
+             continue;
+           
+           /* reset USED bits in contexts - one address per prefix per IAID */
+           for (c = state->context; c; c = c->current)
+             c->flags &= ~CONTEXT_USED;
+
+#ifdef OPTION6_PREFIX_CLASS
+           if (daemon->prefix_classes && state->ia_type == OPTION6_IA_NA)
+             {
+               void *prefix_opt;
+               int prefix_class;
+               
+               if (dump_all_prefix_classes)
+                 /* OPTION_PREFIX_CLASS in ORO, send addresses in all prefix classes */
+                 plain_range = 0;
+               else 
+                 { 
+                   if ((prefix_opt = opt6_find(opt6_ptr(opt, 12), ia_end, OPTION6_PREFIX_CLASS, 2)))
+                     {
+                       
+                       prefix_class = opt6_uint(prefix_opt, 0, 2);
+                       
+                       for (p = daemon->prefix_classes; p ; p = p->next)
+                         if (p->class == prefix_class)
+                           break;
+                       
+                       if (!p)
+                         my_syslog(MS_DHCP | LOG_WARNING, _("unknown prefix-class %d"), prefix_class);
+                       else
+                         {
+                           /* add tag to list, and exclude undecorated dhcp-ranges */
+                           p->tag.next = state->tags;
+                           solicit_tags = run_tag_if(&p->tag);
+                           plain_range = 0;
+                           state->send_prefix_class = p;
+                         }
+                     }
+                   else
+                     {
+                       /* client didn't ask for a prefix class, lets see if we can find one. */
+                       for (p = daemon->prefix_classes; p ; p = p->next)
+                         {
+                           p->tag.next = NULL;
+                           if (match_netid(&p->tag, solicit_tags, 1))
+                             break;
+                         }
+                       
+                       if (p)
+                         {
+                           plain_range = 0;
+                           state->send_prefix_class = p;
+                         }
+                     }
+
+                   if (p && option_bool(OPT_LOG_OPTS))
+                     my_syslog(MS_DHCP | LOG_INFO, "%u prefix class %d tag:%s", state->xid, p->class, p->tag.net); 
+                 }
+             }
+#endif
+
+           o = build_ia(state, &t1cntr);
+           if (address_assigned)
+               address_assigned = 2;
+
+           for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
+             {
+               req_addr = opt6_ptr(ia_option, 0);
+                               
+               if ((c = address6_valid(state->context, req_addr, solicit_tags, plain_range)))
+                 {
+                   lease_time = c->lease_time;
+                   /* If the client asks for an address on the same network as a configured address, 
+                      offer the configured address instead, to make moving to newly-configured
+                      addresses automatic. */
+                   if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr) && check_address(state, &addr))
+                     {
+                       req_addr = &addr;
+                       mark_config_used(c, &addr);
+                       if (have_config(config, CONFIG_TIME))
+                         lease_time = config->lease_time;
+                     }
+                   else if (!(c = address6_available(state->context, req_addr, solicit_tags, plain_range)))
+                     continue; /* not an address we're allowed */
+                   else if (!check_address(state, req_addr))
+                     continue; /* address leased elsewhere */
+                   
+                   /* add address to output packet */
+#ifdef OPTION6_PREFIX_CLASS
+                   if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA)
+                     state->send_prefix_class = prefix_class_from_context(c);
+#endif             
+                   add_address(state, c, lease_time, ia_option, &min_time, req_addr, now);
+                   mark_context_used(state, req_addr);
+                   get_context_tag(state, c);
+                   address_assigned = 1;
+                 }
+             }
+           
+           /* Suggest configured address(es) */
+           for (c = state->context; c; c = c->current) 
+             if (!(c->flags & CONTEXT_CONF_USED) &&
+                 match_netid(c->filter, solicit_tags, plain_range) &&
+                 config_valid(config, c, &addr) && 
+                 check_address(state, &addr))
+               {
+                 mark_config_used(state->context, &addr);
+                 if (have_config(config, CONFIG_TIME))
+                   lease_time = config->lease_time;
+                 else
+                   lease_time = c->lease_time;
+                 /* add address to output packet */
+#ifdef OPTION6_PREFIX_CLASS
+                 if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA)
+                   state->send_prefix_class = prefix_class_from_context(c);
+#endif
+                 add_address(state, c, lease_time, NULL, &min_time, &addr, now);
+                 mark_context_used(state, &addr);
+                 get_context_tag(state, c);
+                 address_assigned = 1;
+               }
+           
+           /* return addresses for existing leases */
+           ltmp = NULL;
+           while ((ltmp = lease6_find_by_client(ltmp, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, state->clid, state->clid_len, state->iaid)))
+             {
+               req_addr = &ltmp->addr6;
+               if ((c = address6_available(state->context, req_addr, solicit_tags, plain_range)))
+                 {
+#ifdef OPTION6_PREFIX_CLASS
+                   if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA)
+                     state->send_prefix_class = prefix_class_from_context(c);
+#endif
+                   add_address(state, c, c->lease_time, NULL, &min_time, req_addr, now);
+                   mark_context_used(state, req_addr);
+                   get_context_tag(state, c);
+                   address_assigned = 1;
+                 }
+             }
+                          
+           /* Return addresses for all valid contexts which don't yet have one */
+           while ((c = address6_allocate(state->context, state->clid, state->clid_len, state->ia_type == OPTION6_IA_TA,
+                                         state->iaid, ia_counter, solicit_tags, plain_range, &addr)))
+             {
+#ifdef OPTION6_PREFIX_CLASS
+               if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA)
+                 state->send_prefix_class = prefix_class_from_context(c);
+#endif
+               add_address(state, c, c->lease_time, NULL, &min_time, &addr, now);
+               mark_context_used(state, &addr);
+               get_context_tag(state, c);
+               address_assigned = 1;
+             }
+           
+           if (address_assigned != 1)
+             {
+               /* If the server will not assign any addresses to any IAs in a
+                  subsequent Request from the client, the server MUST send an Advertise
+                  message to the client that doesn't include any IA options. */
+               if (!state->lease_allocate)
+                 {
+                   save_counter(o);
+                   continue;
+                 }
+               
+               /* If the server cannot assign any addresses to an IA in the message
+                  from the client, the server MUST include the IA in the Reply message
+                  with no addresses in the IA and a Status Code option in the IA
+                  containing status code NoAddrsAvail. */
+               o1 = new_opt6(OPTION6_STATUS_CODE);
+               put_opt6_short(DHCP6NOADDRS);
+               put_opt6_string(_("address unavailable"));
+               end_opt6(o1);
+             }
+           
+           end_ia(t1cntr, min_time, 0);
+           end_opt6(o);        
+         }
+
+       if (address_assigned) 
+         {
+           o1 = new_opt6(OPTION6_STATUS_CODE);
+           put_opt6_short(DHCP6SUCCESS);
+           put_opt6_string(_("success"));
+           end_opt6(o1);
+           
+           /* If --dhcp-authoritative is set, we can tell client not to wait for
+              other possible servers */
+           o = new_opt6(OPTION6_PREFERENCE);
+           put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0);
+           end_opt6(o);
+           tagif = add_options(state, 0);
+         }
+       else
+         { 
+           /* no address, return error */
+           o1 = new_opt6(OPTION6_STATUS_CODE);
+           put_opt6_short(DHCP6NOADDRS);
+           put_opt6_string(_("no addresses available"));
+           end_opt6(o1);
+
+           /* Some clients will ask repeatedly when we're not giving
+              out addresses because we're in stateless mode. Avoid spamming
+              the log in that case. */
+           for (c = state->context; c; c = c->current)
+             if (!(c->flags & CONTEXT_RA_STATELESS))
+               {
+                 log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", NULL, _("no addresses available"));
+                 break;
+               }
+         }
+
+       break;
+      }
+      
+    case DHCP6REQUEST:
+      {
+       int address_assigned = 0;
+       int start = save_counter(-1);
+
+       /* set reply message type */
+       *outmsgtypep = DHCP6REPLY;
+       state->lease_allocate = 1;
+
+       log6_quiet(state, "DHCPREQUEST", NULL, ignore ? _("ignored") : NULL);
+       
+       if (ignore)
+         return 0;
+       
+       for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
+         {   
+           void *ia_option, *ia_end;
+           unsigned int min_time = 0xffffffff;
+           int t1cntr;
+           
+            if (!check_ia(state, opt, &ia_end, &ia_option))
+              continue;
+
+            if (!ia_option)
+              {
+                /* If we get a request with a IA_*A without addresses, treat it exactly like
+                   a SOLICT with rapid commit set. */
+                save_counter(start);
+                goto request_no_address; 
+              }
+
+           o = build_ia(state, &t1cntr);
+             
+           for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
+             {
+               struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
+               struct dhcp_context *dynamic, *c;
+               unsigned int lease_time;
+               struct in6_addr addr;
+               int config_ok = 0;
+               
+               if ((c = address6_valid(state->context, req_addr, tagif, 1)))
+                 config_ok = config_valid(config, c, &addr) && IN6_ARE_ADDR_EQUAL(&addr, req_addr);
+               
+               if ((dynamic = address6_available(state->context, req_addr, tagif, 1)) || c)
+                 {
+                   if (!dynamic && !config_ok)
+                     {
+                       /* Static range, not configured. */
+                       o1 = new_opt6(OPTION6_STATUS_CODE);
+                       put_opt6_short(DHCP6NOADDRS);
+                       put_opt6_string(_("address unavailable"));
+                       end_opt6(o1);
+                     }
+                   else if (!check_address(state, req_addr))
+                     {
+                       /* Address leased to another DUID/IAID */
+                       o1 = new_opt6(OPTION6_STATUS_CODE);
+                       put_opt6_short(DHCP6UNSPEC);
+                       put_opt6_string(_("address in use"));
+                       end_opt6(o1);
+                     } 
+                   else 
+                     {
+                       if (!dynamic)
+                         dynamic = c;
+
+                       lease_time = dynamic->lease_time;
+                       
+                       if (config_ok && have_config(config, CONFIG_TIME))
+                         lease_time = config->lease_time;
+
+#ifdef OPTION6_PREFIX_CLASS
+                       if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA)
+                         state->send_prefix_class = prefix_class_from_context(c);
+#endif
+                       add_address(state, dynamic, lease_time, ia_option, &min_time, req_addr, now);
+                       get_context_tag(state, dynamic);
+                       address_assigned = 1;
+                     }
+                 }
+               else 
+                 {
+                   /* requested address not on the correct link */
+                   o1 = new_opt6(OPTION6_STATUS_CODE);
+                   put_opt6_short(DHCP6NOTONLINK);
+                   put_opt6_string(_("not on link"));
+                   end_opt6(o1);
+                 }
+             }
+        
+           end_ia(t1cntr, min_time, 0);
+           end_opt6(o);        
+         }
+
+       if (address_assigned) 
+         {
+           o1 = new_opt6(OPTION6_STATUS_CODE);
+           put_opt6_short(DHCP6SUCCESS);
+           put_opt6_string(_("success"));
+           end_opt6(o1);
+         }
+       else
+         { 
+           /* no address, return error */
+           o1 = new_opt6(OPTION6_STATUS_CODE);
+           put_opt6_short(DHCP6NOADDRS);
+           put_opt6_string(_("no addresses available"));
+           end_opt6(o1);
+           log6_packet(state, "DHCPREPLY", NULL, _("no addresses available"));
+         }
+
+       tagif = add_options(state, 0);
+       break;
+      }
+      
+  
+    case DHCP6RENEW:
+      {
+       /* set reply message type */
+       *outmsgtypep = DHCP6REPLY;
+       
+       log6_quiet(state, "DHCPRENEW", NULL, NULL);
+
+       for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
+         {
+           void *ia_option, *ia_end;
+           unsigned int min_time = 0xffffffff;
+           int t1cntr, iacntr;
+           
+           if (!check_ia(state, opt, &ia_end, &ia_option))
+             continue;
+           
+           o = build_ia(state, &t1cntr);
+           iacntr = save_counter(-1); 
+           
+           for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
+             {
+               struct dhcp_lease *lease = NULL;
+               struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
+               unsigned int preferred_time =  opt6_uint(ia_option, 16, 4);
+               unsigned int valid_time =  opt6_uint(ia_option, 20, 4);
+               char *message = NULL;
+               struct dhcp_context *this_context;
+               
+               if (!(lease = lease6_find(state->clid, state->clid_len,
+                                         state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, 
+                                         state->iaid, req_addr)))
+                 {
+                   /* If the server cannot find a client entry for the IA the server
+                      returns the IA containing no addresses with a Status Code option set
+                      to NoBinding in the Reply message. */
+                   save_counter(iacntr);
+                   t1cntr = 0;
+                   
+                   log6_packet(state, "DHCPREPLY", req_addr, _("lease not found"));
+                   
+                   o1 = new_opt6(OPTION6_STATUS_CODE);
+                   put_opt6_short(DHCP6NOBINDING);
+                   put_opt6_string(_("no binding found"));
+                   end_opt6(o1);
+
+                   preferred_time = valid_time = 0;
+                   break;
+                 }
+               
+               
+               if ((this_context = address6_available(state->context, req_addr, tagif, 1)) ||
+                   (this_context = address6_valid(state->context, req_addr, tagif, 1)))
+                 {
+                   struct in6_addr addr;
+                   unsigned int lease_time;
+
+                   get_context_tag(state, this_context);
+                   
+                   if (config_valid(config, this_context, &addr) && IN6_ARE_ADDR_EQUAL(&addr, req_addr) && have_config(config, CONFIG_TIME))
+                     lease_time = config->lease_time;
+                   else 
+                     lease_time = this_context->lease_time;
+                   
+                   calculate_times(this_context, &min_time, &valid_time, &preferred_time, lease_time); 
+                   
+                   lease_set_expires(lease, valid_time, now);
+                   /* Update MAC record in case it's new information. */
+                   if (state->mac_len != 0)
+                     lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0);
+                   if (state->ia_type == OPTION6_IA_NA && state->hostname)
+                     {
+                       char *addr_domain = get_domain6(req_addr);
+                       if (!state->send_domain)
+                         state->send_domain = addr_domain;
+                       lease_set_hostname(lease, state->hostname, state->hostname_auth, addr_domain, state->domain); 
+                       message = state->hostname;
+                     }
+                   
+                   
+                   if (preferred_time == 0)
+                     message = _("deprecated");
+                 }
+               else
+                 {
+                   preferred_time = valid_time = 0;
+                   message = _("address invalid");
+                 } 
+
+               if (message && (message != state->hostname))
+                 log6_packet(state, "DHCPREPLY", req_addr, message);   
+               else
+                 log6_quiet(state, "DHCPREPLY", req_addr, message);
+       
+               o1 =  new_opt6(OPTION6_IAADDR);
+               put_opt6(req_addr, sizeof(*req_addr));
+               put_opt6_long(preferred_time);
+               put_opt6_long(valid_time);
+               end_opt6(o1);
+             }
+           
+           end_ia(t1cntr, min_time, 1);
+           end_opt6(o);
+         }
+       
+       tagif = add_options(state, 0);
+       break;
+       
+      }
+      
+    case DHCP6CONFIRM:
+      {
+       int good_addr = 0;
+
+       /* set reply message type */
+       *outmsgtypep = DHCP6REPLY;
+       
+       log6_quiet(state, "DHCPCONFIRM", NULL, NULL);
+       
+       for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
+         {
+           void *ia_option, *ia_end;
+           
+           for (check_ia(state, opt, &ia_end, &ia_option);
+                ia_option;
+                ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
+             {
+               struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
+               
+               if (!address6_valid(state->context, req_addr, tagif, 1))
+                 {
+                   o1 = new_opt6(OPTION6_STATUS_CODE);
+                   put_opt6_short(DHCP6NOTONLINK);
+                   put_opt6_string(_("confirm failed"));
+                   end_opt6(o1);
+                   return 1;
+                 }
+
+               good_addr = 1;
+               log6_quiet(state, "DHCPREPLY", req_addr, state->hostname);
+             }
+         }      
+       
+       /* No addresses, no reply: RFC 3315 18.2.2 */
+       if (!good_addr)
+         return 0;
+
+       o1 = new_opt6(OPTION6_STATUS_CODE);
+       put_opt6_short(DHCP6SUCCESS );
+       put_opt6_string(_("all addresses still on link"));
+       end_opt6(o1);
+       break;
+    }
+      
+    case DHCP6IREQ:
+      {
+       /* We can't discriminate contexts based on address, as we don't know it.
+          If there is only one possible context, we can use its tags */
+       if (state->context && state->context->netid.net && !state->context->current)
+         {
+           state->context->netid.next = NULL;
+           state->context_tags =  &state->context->netid;
+         }
+
+       /* Similarly, we can't determine domain from address, but if the FQDN is
+          given in --dhcp-host, we can use that, and failing that we can use the 
+          unqualified configured domain, if any. */
+       if (state->hostname_auth)
+         state->send_domain = state->domain;
+       else
+         state->send_domain = get_domain6(NULL);
+
+       log6_quiet(state, "DHCPINFORMATION-REQUEST", NULL, ignore ? _("ignored") : state->hostname);
+       if (ignore)
+         return 0;
+       *outmsgtypep = DHCP6REPLY;
+       tagif = add_options(state, 1);
+       break;
+      }
+      
+      
+    case DHCP6RELEASE:
+      {
+       /* set reply message type */
+       *outmsgtypep = DHCP6REPLY;
+
+       log6_quiet(state, "DHCPRELEASE", NULL, NULL);
+
+       for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
+         {
+           void *ia_option, *ia_end;
+           int made_ia = 0;
+                   
+           for (check_ia(state, opt, &ia_end, &ia_option);
+                ia_option;
+                ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) 
+             {
+               struct dhcp_lease *lease;
+               
+               if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
+                                        state->iaid, opt6_ptr(ia_option, 0))))
+                 lease_prune(lease, now);
+               else
+                 {
+                   if (!made_ia)
+                     {
+                       o = new_opt6(state->ia_type);
+                       put_opt6_long(state->iaid);
+                       if (state->ia_type == OPTION6_IA_NA)
+                         {
+                           put_opt6_long(0);
+                           put_opt6_long(0); 
+                         }
+                       made_ia = 1;
+                     }
+                   
+                   o1 = new_opt6(OPTION6_IAADDR);
+                   put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);
+                   put_opt6_long(0);
+                   put_opt6_long(0);
+                   end_opt6(o1);
+                 }
+             }
+           
+           if (made_ia)
+             {
+               o1 = new_opt6(OPTION6_STATUS_CODE);
+               put_opt6_short(DHCP6NOBINDING);
+               put_opt6_string(_("no binding found"));
+               end_opt6(o1);
+               
+               end_opt6(o);
+             }
+         }
+       
+       o1 = new_opt6(OPTION6_STATUS_CODE);
+       put_opt6_short(DHCP6SUCCESS);
+       put_opt6_string(_("release received"));
+       end_opt6(o1);
+       
+       break;
+      }
+
+    case DHCP6DECLINE:
+      {
+       /* set reply message type */
+       *outmsgtypep = DHCP6REPLY;
+       
+       log6_quiet(state, "DHCPDECLINE", NULL, NULL);
+
+       for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
+         {
+           void *ia_option, *ia_end;
+           int made_ia = 0;
+                   
+           for (check_ia(state, opt, &ia_end, &ia_option);
+                ia_option;
+                ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) 
+             {
+               struct dhcp_lease *lease;
+               struct in6_addr *addrp = opt6_ptr(ia_option, 0);
+
+               if (have_config(config, CONFIG_ADDR6) && IN6_ARE_ADDR_EQUAL(&config->addr6, addrp))
+                 {
+                   prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF);
+                   inet_ntop(AF_INET6, addrp, daemon->addrbuff, ADDRSTRLEN);
+                   my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), 
+                             daemon->addrbuff, daemon->dhcp_buff3);
+                   config->flags |= CONFIG_DECLINED;
+                   config->decline_time = now;
+                 }
+               else
+                 /* make sure this host gets a different address next time. */
+                 for (context_tmp = state->context; context_tmp; context_tmp = context_tmp->current)
+                   context_tmp->addr_epoch++;
+               
+               if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
+                                        state->iaid, opt6_ptr(ia_option, 0))))
+                 lease_prune(lease, now);
+               else
+                 {
+                   if (!made_ia)
+                     {
+                       o = new_opt6(state->ia_type);
+                       put_opt6_long(state->iaid);
+                       if (state->ia_type == OPTION6_IA_NA)
+                         {
+                           put_opt6_long(0);
+                           put_opt6_long(0); 
+                         }
+                       made_ia = 1;
+                     }
+                   
+                   o1 = new_opt6(OPTION6_IAADDR);
+                   put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);
+                   put_opt6_long(0);
+                   put_opt6_long(0);
+                   end_opt6(o1);
+                 }
+             }
+           
+           if (made_ia)
+             {
+               o1 = new_opt6(OPTION6_STATUS_CODE);
+               put_opt6_short(DHCP6NOBINDING);
+               put_opt6_string(_("no binding found"));
+               end_opt6(o1);
+               
+               end_opt6(o);
+             }
+           
+         }
+
+       /* We must anwser with 'success' in global section anyway */
+       o1 = new_opt6(OPTION6_STATUS_CODE);
+       put_opt6_short(DHCP6SUCCESS);
+       put_opt6_string(_("success"));
+       end_opt6(o1);
+       break;
+      }
+
+    }
+  
+  log_tags(tagif, state->xid);
+  log6_opts(0, state->xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1));
+  
+  return 1;
+
+}
+
+static struct dhcp_netid *add_options(struct state *state, int do_refresh)  
+{
+  void *oro;
+  /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
+  struct dhcp_netid *tagif = option_filter(state->tags, state->context_tags, daemon->dhcp_opts6);
+  struct dhcp_opt *opt_cfg;
+  int done_dns = 0, done_refresh = !do_refresh, do_encap = 0;
+  int i, o, o1;
+
+  oro = opt6_find(state->packet_options, state->end, OPTION6_ORO, 0);
+  
+  for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
+    {
+      /* netids match and not encapsulated? */
+      if (!(opt_cfg->flags & DHOPT_TAGOK))
+       continue;
+      
+      if (!(opt_cfg->flags & DHOPT_FORCE) && oro)
+       {
+         for (i = 0; i <  opt6_len(oro) - 1; i += 2)
+           if (opt6_uint(oro, i, 2) == (unsigned)opt_cfg->opt)
+             break;
+         
+         /* option not requested */
+         if (i >=  opt6_len(oro) - 1)
+           continue;
+       }
+      
+      if (opt_cfg->opt == OPTION6_REFRESH_TIME)
+       done_refresh = 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)) ||
+               (IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ll_addr)))
+             len -= IN6ADDRSZ;
+         
+         if (len != 0)
+           {
+             
+             o = new_opt6(opt_cfg->opt);
+                 
+             for (a = (struct in6_addr *)opt_cfg->val, j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)
+               {
+                 if (IN6_IS_ADDR_UNSPECIFIED(a))
+                   {
+                     if (!add_local_addrs(state->context))
+                       put_opt6(state->fallback, IN6ADDRSZ);
+                   }
+                 else if (IN6_IS_ADDR_ULA_ZERO(a))
+                   {
+                     if (!IN6_IS_ADDR_UNSPECIFIED(state->ula_addr))
+                       put_opt6(state->ula_addr, IN6ADDRSZ);
+                   }
+                 else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
+                   {
+                     if (!IN6_IS_ADDR_UNSPECIFIED(state->ll_addr))
+                       put_opt6(state->ll_addr, IN6ADDRSZ);
+                   }
+                 else
+                   put_opt6(a, IN6ADDRSZ);
+               }
+
+             end_opt6(o);
+           }
+       }
+      else
+       {
+         o = new_opt6(opt_cfg->opt);
+         if (opt_cfg->val)
+           put_opt6(opt_cfg->val, opt_cfg->len);
+         end_opt6(o);
+       }
+    }
+  
+  if (daemon->port == NAMESERVER_PORT && !done_dns)
+    {
+      o = new_opt6(OPTION6_DNS_SERVER);
+      if (!add_local_addrs(state->context))
+       put_opt6(state->fallback, IN6ADDRSZ);
+      end_opt6(o); 
+    }
+
+  if (state->context && !done_refresh)
+    {
+      struct dhcp_context *c;
+      unsigned int lease_time = 0xffffffff;
+      
+      /* Find the smallest lease tie of all contexts,
+        subjext to the RFC-4242 stipulation that this must not 
+        be less than 600. */
+      for (c = state->context; c; c = c->next)
+       if (c->lease_time < lease_time)
+         {
+           if (c->lease_time < 600)
+             lease_time = 600;
+           else
+             lease_time = c->lease_time;
+         }
+
+      o = new_opt6(OPTION6_REFRESH_TIME);
+      put_opt6_long(lease_time);
+      end_opt6(o); 
+    }
+   
+    /* handle vendor-identifying vendor-encapsulated options,
+       dhcp-option = vi-encap:13,17,....... */
+  for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
+    opt_cfg->flags &= ~DHOPT_ENCAP_DONE;
+    
+  if (oro)
+    for (i = 0; i <  opt6_len(oro) - 1; i += 2)
+      if (opt6_uint(oro, i, 2) == OPTION6_VENDOR_OPTS)
+       do_encap = 1;
+  
+  for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
+    { 
+      if (opt_cfg->flags & DHOPT_RFC3925)
+       {
+         int found = 0;
+         struct dhcp_opt *oc;
+         
+         if (opt_cfg->flags & DHOPT_ENCAP_DONE)
+           continue;
+         
+         for (oc = daemon->dhcp_opts6; oc; oc = oc->next)
+           {
+             oc->flags &= ~DHOPT_ENCAP_MATCH;
+             
+             if (!(oc->flags & DHOPT_RFC3925) || opt_cfg->u.encap != oc->u.encap)
+               continue;
+             
+             oc->flags |= DHOPT_ENCAP_DONE;
+             if (match_netid(oc->netid, tagif, 1))
+               {
+                 /* option requested/forced? */
+                 if (!oro || do_encap || (oc->flags & DHOPT_FORCE))
+                   {
+                     oc->flags |= DHOPT_ENCAP_MATCH;
+                     found = 1;
+                   }
+               } 
+           }
+         
+         if (found)
+           { 
+             o = new_opt6(OPTION6_VENDOR_OPTS);              
+             put_opt6_long(opt_cfg->u.encap);  
+            
+             for (oc = daemon->dhcp_opts6; oc; oc = oc->next)
+               if (oc->flags & DHOPT_ENCAP_MATCH)
+                 {
+                   o1 = new_opt6(oc->opt);
+                   put_opt6(oc->val, oc->len);
+                   end_opt6(o1);
+                 }
+             end_opt6(o);
+           }
+       }
+    }      
+
+
+  if (state->hostname)
+    {
+      unsigned char *p;
+      size_t len = strlen(state->hostname);
+      
+      if (state->send_domain)
+       len += strlen(state->send_domain) + 2;
+
+      o = new_opt6(OPTION6_FQDN);
+      if ((p = expand(len + 2)))
+       {
+         *(p++) = state->fqdn_flags;
+         p = do_rfc1035_name(p, state->hostname);
+         if (state->send_domain)
+           {
+             p = do_rfc1035_name(p, state->send_domain);
+             *p = 0;
+           }
+       }
+      end_opt6(o);
+    }
+
+
+  /* logging */
+  if (option_bool(OPT_LOG_OPTS) && oro)
+    {
+      char *q = daemon->namebuff;
+      for (i = 0; i <  opt6_len(oro) - 1; i += 2)
+       {
+         char *s = option_string(AF_INET6, opt6_uint(oro, i, 2), NULL, 0, NULL, 0);
+         q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
+                       "%d%s%s%s", 
+                       opt6_uint(oro, i, 2),
+                       strlen(s) != 0 ? ":" : "",
+                       s, 
+                       (i > opt6_len(oro) - 3) ? "" : ", ");
+         if ( i >  opt6_len(oro) - 3 || (q - daemon->namebuff) > 40)
+           {
+             q = daemon->namebuff;
+             my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), state->xid, daemon->namebuff);
+           }
+       }
+    } 
+
+  return tagif;
+}
+static int add_local_addrs(struct dhcp_context *context)
+{
+  int done = 0;
+  
+  for (; context; context = context->current)
+    if ((context->flags & CONTEXT_USED) && !IN6_IS_ADDR_UNSPECIFIED(&context->local6))
+      {
+       /* squash duplicates */
+       struct dhcp_context *c;
+       for (c = context->current; c; c = c->current)
+         if ((c->flags & CONTEXT_USED) &&
+             IN6_ARE_ADDR_EQUAL(&context->local6, &c->local6))
+           break;
+       
+       if (!c)
+         { 
+           done = 1;
+           put_opt6(&context->local6, IN6ADDRSZ);
+         }
+      }
+
+  return done;
+}
+
+
+static void get_context_tag(struct state *state, struct dhcp_context *context)
+{
+  /* get tags from context if we've not used it before */
+  if (context->netid.next == &context->netid && context->netid.net)
+    {
+      context->netid.next = state->context_tags;
+      state->context_tags = &context->netid;
+      if (!state->hostname_auth)
+       {
+         struct dhcp_netid_list *id_list;
+         
+         for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
+           if ((!id_list->list) || match_netid(id_list->list, &context->netid, 0))
+             break;
+         if (id_list)
+           state->hostname = NULL;
+       }
+    }
+} 
+
+#ifdef OPTION6_PREFIX_CLASS
+static struct prefix_class *prefix_class_from_context(struct dhcp_context *context)
+{
+  struct prefix_class *p;
+  struct dhcp_netid *t;
+  
+  for (p = daemon->prefix_classes; p ; p = p->next)
+    for (t = context->filter; t; t = t->next)
+      if (strcmp(p->tag.net, t->net) == 0)
+       return p;
+  
+ return NULL;
+}
+#endif
+
+static int check_ia(struct state *state, void *opt, void **endp, void **ia_option)
+{
+  state->ia_type = opt6_type(opt);
+  *ia_option = NULL;
+
+  if (state->ia_type != OPTION6_IA_NA && state->ia_type != OPTION6_IA_TA)
+    return 0;
+  
+  if (state->ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
+    return 0;
+           
+  if (state->ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
+    return 0;
+  
+  *endp = opt6_ptr(opt, opt6_len(opt));
+  state->iaid = opt6_uint(opt, 0, 4);
+  *ia_option = opt6_find(opt6_ptr(opt, state->ia_type == OPTION6_IA_NA ? 12 : 4), *endp, OPTION6_IAADDR, 24);
+
+  return 1;
+}
+
+
+static int build_ia(struct state *state, int *t1cntr)
+{
+  int  o = new_opt6(state->ia_type);
+  put_opt6_long(state->iaid);
+  *t1cntr = 0;
+           
+  if (state->ia_type == OPTION6_IA_NA)
+    {
+      /* save pointer */
+      *t1cntr = save_counter(-1);
+      /* so we can fill these in later */
+      put_opt6_long(0);
+      put_opt6_long(0); 
+    }
+
+  return o;
+}
+
+static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz)
+{
+  if (t1cntr != 0)
+    {
+      /* go back an fill in fields in IA_NA option */
+      int sav = save_counter(t1cntr);
+      unsigned int t1, t2, fuzz = 0;
+
+      if (do_fuzz)
+       {
+         fuzz = rand16();
+      
+         while (fuzz > (min_time/16))
+           fuzz = fuzz/2;
+       }
+      
+      t1 = (min_time == 0xffffffff) ? 0xffffffff : min_time/2 - fuzz;
+      t2 = (min_time == 0xffffffff) ? 0xffffffff : ((min_time/8)*7) - fuzz;
+      put_opt6_long(t1);
+      put_opt6_long(t2);
+      save_counter(sav);
+    }  
+}
+
+static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, 
+                       unsigned int *min_time, struct in6_addr *addr, time_t now)
+{
+  unsigned int valid_time = 0, preferred_time = 0;
+  int o = new_opt6(OPTION6_IAADDR);
+  struct dhcp_lease *lease;
+
+  /* get client requested times */
+  if (ia_option)
+    {
+      preferred_time =  opt6_uint(ia_option, 16, 4);
+      valid_time =  opt6_uint(ia_option, 20, 4);
+    }
+
+  calculate_times(context, min_time, &valid_time, &preferred_time, lease_time); 
+  
+  put_opt6(addr, sizeof(*addr));
+  put_opt6_long(preferred_time);
+  put_opt6_long(valid_time);               
+  
+#ifdef OPTION6_PREFIX_CLASS
+  if (state->send_prefix_class)
+    {
+      int o1 = new_opt6(OPTION6_PREFIX_CLASS);
+      put_opt6_short(state->send_prefix_class->class);
+      end_opt6(o1);
+    }
+#endif
+
+  end_opt6(o);
+  
+  if (state->lease_allocate)
+    update_leases(state, context, addr, valid_time, now);
+
+  if ((lease = lease6_find_by_addr(addr, 128, 0)))
+    lease->flags |= LEASE_USED;
+
+  /* get tags from context if we've not used it before */
+  if (context->netid.next == &context->netid && context->netid.net)
+    {
+      context->netid.next = state->context_tags;
+      state->context_tags = &context->netid;
+      
+      if (!state->hostname_auth)
+       {
+         struct dhcp_netid_list *id_list;
+         
+         for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
+           if ((!id_list->list) || match_netid(id_list->list, &context->netid, 0))
+             break;
+         if (id_list)
+           state->hostname = NULL;
+       }
+    }
+
+  log6_quiet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", addr, state->hostname);
+
+}
+
+static void mark_context_used(struct state *state, struct in6_addr *addr)
+{
+  struct dhcp_context *context;
+
+  /* Mark that we have an address for this prefix. */
+#ifdef OPTION6_PREFIX_CLASS
+  for (context = state->context; context; context = context->current)
+    if (is_same_net6(addr, &context->start6, context->prefix) &&
+       (!state->send_prefix_class || state->send_prefix_class == prefix_class_from_context(context)))
+      context->flags |= CONTEXT_USED;
+#else
+  for (context = state->context; context; context = context->current)
+    if (is_same_net6(addr, &context->start6, context->prefix))
+      context->flags |= CONTEXT_USED;
+#endif
+}
+
+static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr)
+{
+  for (; context; context = context->current)
+    if (is_same_net6(addr, &context->start6, context->prefix))
+      context->flags |= CONTEXT_CONF_USED;
+}
+
+/* make sure address not leased to another CLID/IAID */
+static int check_address(struct state *state, struct in6_addr *addr)
+{ 
+  struct dhcp_lease *lease;
+
+  if (!(lease = lease6_find_by_addr(addr, 128, 0)))
+    return 1;
+
+  if (lease->clid_len != state->clid_len || 
+      memcmp(lease->clid, state->clid, state->clid_len) != 0 ||
+      lease->iaid != state->iaid)
+    return 0;
+
+  return 1;
+}
+
+
+/* Calculate valid and preferred times to send in leases/renewals. 
+
+   Inputs are:
+
+   *valid_timep, *preferred_timep - requested times from IAADDR options.
+   context->valid, context->preferred - times associated with subnet address on local interface.
+   context->flags | CONTEXT_DEPRECATE - "deprecated" flag in dhcp-range.
+   lease_time - configured time for context for individual client.
+   *min_time - smallest valid time sent so far.
+
+   Outputs are :
+   
+   *valid_timep, *preferred_timep - times to be send in IAADDR option.
+   *min_time - smallest valid time sent so far, to calculate T1 and T2.
+   
+   */
+static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep, 
+                           unsigned int *preferred_timep, unsigned int lease_time)
+{
+  unsigned int req_preferred = *preferred_timep, req_valid = *valid_timep;
+  unsigned int valid_time = lease_time, preferred_time = lease_time;
+  
+  /* RFC 3315: "A server ignores the lifetimes set
+     by the client if the preferred lifetime is greater than the valid
+     lifetime. */
+  if (req_preferred <= req_valid)
+    {
+      if (req_preferred != 0)
+       {
+         /* 0 == "no preference from client" */
+         if (req_preferred < 120u)
+           req_preferred = 120u; /* sanity */
+         
+         if (req_preferred < preferred_time)
+           preferred_time = req_preferred;
+       }
+      
+      if (req_valid != 0)
+       /* 0 == "no preference from client" */
+       {
+         if (req_valid < 120u)
+           req_valid = 120u; /* sanity */
+         
+         if (req_valid < valid_time)
+           valid_time = req_valid;
+       }
+    }
+
+  /* deprecate (preferred == 0) which configured, or when local address 
+     is deprecated */
+  if ((context->flags & CONTEXT_DEPRECATE) || context->preferred == 0)
+    preferred_time = 0;
+  
+  if (preferred_time != 0 && preferred_time < *min_time)
+    *min_time = preferred_time;
+  
+  if (valid_time != 0 && valid_time < *min_time)
+    *min_time = valid_time;
+  
+  *valid_timep = valid_time;
+  *preferred_timep = preferred_time;
+}
+
+static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now)
+{
+  struct dhcp_lease *lease = lease6_find_by_addr(addr, 128, 0);
+#ifdef HAVE_SCRIPT
+  struct dhcp_netid *tagif = run_tag_if(state->tags);
+#endif
+
+  (void)context;
+
+  if (!lease)
+    lease = lease6_allocate(addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
+  
+  if (lease)
+    {
+      lease_set_expires(lease, lease_time, now);
+      lease_set_iaid(lease, state->iaid); 
+      lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0);
+      lease_set_interface(lease, state->interface, now);
+      if (state->hostname && state->ia_type == OPTION6_IA_NA)
+       {
+         char *addr_domain = get_domain6(addr);
+         if (!state->send_domain)
+           state->send_domain = addr_domain;
+         lease_set_hostname(lease, state->hostname, state->hostname_auth, addr_domain, state->domain);
+       }
+      
+#ifdef HAVE_SCRIPT
+      if (daemon->lease_change_command)
+       {
+         void *class_opt;
+         lease->flags |= LEASE_CHANGED;
+         free(lease->extradata);
+         lease->extradata = NULL;
+         lease->extradata_size = lease->extradata_len = 0;
+         lease->vendorclass_count = 0; 
+         
+         if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4)))
+           {
+             void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));
+             lease->vendorclass_count++;
+             /* send enterprise number first  */
+             sprintf(daemon->dhcp_buff2, "%u", opt6_uint(class_opt, 0, 4));
+             lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0);
+             
+             if (opt6_len(class_opt) >= 6) 
+               for (enc_opt = opt6_ptr(class_opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
+                 {
+                   lease->vendorclass_count++;
+                   lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
+                 }
+           }
+         
+         lease_add_extradata(lease, (unsigned char *)state->client_hostname, 
+                             state->client_hostname ? strlen(state->client_hostname) : 0, 0);                          
+         
+         /* space-concat tag set */
+         if (!tagif && !context->netid.net)
+           lease_add_extradata(lease, NULL, 0, 0);
+         else
+           {
+             if (context->netid.net)
+               lease_add_extradata(lease, (unsigned char *)context->netid.net, strlen(context->netid.net), tagif ? ' ' : 0);
+             
+             if (tagif)
+               {
+                 struct dhcp_netid *n;
+                 for (n = tagif; n; n = n->next)
+                   {
+                     struct dhcp_netid *n1;
+                     /* kill dupes */
+                     for (n1 = n->next; n1; n1 = n1->next)
+                       if (strcmp(n->net, n1->net) == 0)
+                         break;
+                     if (!n1)
+                       lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0); 
+                   }
+               }
+           }
+         
+         if (state->link_address)
+           inet_ntop(AF_INET6, state->link_address, daemon->addrbuff, ADDRSTRLEN);
+         
+         lease_add_extradata(lease, (unsigned char *)daemon->addrbuff, state->link_address ? strlen(daemon->addrbuff) : 0, 0);
+         
+         if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_USER_CLASS, 2)))
+           {
+             void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));
+             for (enc_opt = opt6_ptr(class_opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
+               lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
+           }
+       }
+#endif 
+      
+    }
+}
+                         
+                       
+       
+static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts)
+{
+  void *opt;
+  char *desc = nest ? "nest" : "sent";
+  
+  if (!option_bool(OPT_LOG_OPTS) || start_opts == end_opts)
+    return;
+  
+  for (opt = start_opts; opt; opt = opt6_next(opt, end_opts))
+    {
+      int type = opt6_type(opt);
+      void *ia_options = NULL;
+      char *optname;
+      
+      if (type == OPTION6_IA_NA)
+       {
+         sprintf(daemon->namebuff, "IAID=%u T1=%u T2=%u",
+                 opt6_uint(opt, 0, 4), opt6_uint(opt, 4, 4), opt6_uint(opt, 8, 4));
+         optname = "ia-na";
+         ia_options = opt6_ptr(opt, 12);
+       }
+      else if (type == OPTION6_IA_TA)
+       {
+         sprintf(daemon->namebuff, "IAID=%u", opt6_uint(opt, 0, 4));
+         optname = "ia-ta";
+         ia_options = opt6_ptr(opt, 4);
+       }
+      else if (type == OPTION6_IAADDR)
+       {
+         inet_ntop(AF_INET6, opt6_ptr(opt, 0), daemon->addrbuff, ADDRSTRLEN);
+         sprintf(daemon->namebuff, "%s PL=%u VL=%u", 
+                 daemon->addrbuff, opt6_uint(opt, 16, 4), opt6_uint(opt, 20, 4));
+         optname = "iaaddr";
+         ia_options = opt6_ptr(opt, 24);
+       }
+#ifdef OPTION6_PREFIX_CLASS
+      else if (type == OPTION6_PREFIX_CLASS)
+       {
+         optname = "prefix-class";
+         sprintf(daemon->namebuff, "class=%u", opt6_uint(opt, 0, 2));
+       }
+#endif
+      else if (type == OPTION6_STATUS_CODE)
+       {
+         int len = sprintf(daemon->namebuff, "%u ", opt6_uint(opt, 0, 2));
+         memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), opt6_len(opt)-2);
+         daemon->namebuff[len + opt6_len(opt) - 2] = 0;
+         optname = "status";
+       }
+      else
+       {
+         /* account for flag byte on FQDN */
+         int offset = type == OPTION6_FQDN ? 1 : 0;
+         optname = option_string(AF_INET6, type, opt6_ptr(opt, offset), opt6_len(opt) - offset, daemon->namebuff, MAXDNAME);
+       }
+      
+      my_syslog(MS_DHCP | LOG_INFO, "%u %s size:%3d option:%3d %s  %s", 
+               xid, desc, opt6_len(opt), type, optname, daemon->namebuff);
+      
+      if (ia_options)
+       log6_opts(1, xid, ia_options, opt6_ptr(opt, opt6_len(opt)));
+    }
+}               
+static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string)
+{
+  if (option_bool(OPT_LOG_OPTS) || !option_bool(OPT_QUIET_DHCP6))
+    log6_packet(state, type, addr, string);
+}
+
+static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string)
+{
+  int clid_len = state->clid_len;
+
+  /* avoid buffer overflow */
+  if (clid_len > 100)
+    clid_len = 100;
+  
+  print_mac(daemon->namebuff, state->clid, clid_len);
+
+  if (addr)
+    {
+      inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, 255);
+      strcat(daemon->dhcp_buff2, " ");
+    }
+  else
+    daemon->dhcp_buff2[0] = 0;
+
+  if(option_bool(OPT_LOG_OPTS))
+    my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s %s",
+             state->xid, 
+             type,
+             state->iface_name, 
+             daemon->dhcp_buff2,
+             daemon->namebuff,
+             string ? string : "");
+  else
+    my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s %s",
+             type,
+             state->iface_name, 
+             daemon->dhcp_buff2,
+             daemon->namebuff,
+             string ? string : "");
+}
+
+static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize)
+{
+  u16 opt, opt_len;
+  void *start;
+  
+  if (!opts)
+    return NULL;
+    
+  while (1)
+    {
+      if (end - opts < 4) 
+       return NULL;
+      
+      start = opts;
+      GETSHORT(opt, opts);
+      GETSHORT(opt_len, opts);
+      
+      if (opt_len > (end - opts))
+       return NULL;
+      
+      if (opt == search && (opt_len >= minsize))
+       return start;
+      
+      opts += opt_len;
+    }
+}
+
+static void *opt6_next(void *opts, void *end)
+{
+  u16 opt_len;
+  
+  if (end - opts < 4) 
+    return NULL;
+  
+  opts += 2;
+  GETSHORT(opt_len, opts);
+  
+  if (opt_len >= (end - opts))
+    return NULL;
+  
+  return opts + opt_len;
+}
+
+static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
+{
+  /* this worries about unaligned data and byte order */
+  unsigned int ret = 0;
+  int i;
+  unsigned char *p = opt6_ptr(opt, offset);
+  
+  for (i = 0; i < size; i++)
+    ret = (ret << 8) | *p++;
+  
+  return ret;
+} 
+
+void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id)
+{
+  /* ->local is same value for all relays on ->current chain */
+  
+  struct all_addr from;
+  unsigned char *header;
+  unsigned char *inbuff = daemon->dhcp_packet.iov_base;
+  int msg_type = *inbuff;
+  int hopcount;
+  struct in6_addr multicast;
+  unsigned int maclen, mactype;
+  unsigned char mac[DHCP_CHADDR_MAX];
+
+  inet_pton(AF_INET6, ALL_SERVERS, &multicast);
+  get_client_mac(peer_address, scope_id, mac, &maclen, &mactype);
+
+  /* source address == relay address */
+  from.addr.addr6 = relay->local.addr.addr6;
+    
+  /* Get hop count from nested relayed message */ 
+  if (msg_type == DHCP6RELAYFORW)
+    hopcount = *((unsigned char *)inbuff+1) + 1;
+  else
+    hopcount = 0;
+
+  /* RFC 3315 HOP_COUNT_LIMIT */
+  if (hopcount > 32)
+    return;
+
+  save_counter(0);
+
+  if ((header = put_opt6(NULL, 34)))
+    {
+      int o;
+
+      header[0] = DHCP6RELAYFORW;
+      header[1] = hopcount;
+      memcpy(&header[2],  &relay->local.addr.addr6, IN6ADDRSZ);
+      memcpy(&header[18], peer_address, IN6ADDRSZ);
+      /* RFC-6939 */
+      if (maclen != 0)
+       {
+         o = new_opt6(OPTION6_CLIENT_MAC);
+         put_opt6_short(mactype);
+         put_opt6(mac, maclen);
+         end_opt6(o);
+       }
+      
+      o = new_opt6(OPTION6_RELAY_MSG);
+      put_opt6(inbuff, sz);
+      end_opt6(o);
+      
+      for (; relay; relay = relay->current)
+       {
+         union mysockaddr to;
+         
+         to.sa.sa_family = AF_INET6;
+         to.in6.sin6_addr = relay->server.addr.addr6;
+         to.in6.sin6_port = htons(DHCPV6_SERVER_PORT);
+         to.in6.sin6_flowinfo = 0;
+         to.in6.sin6_scope_id = 0;
+
+         if (IN6_ARE_ADDR_EQUAL(&relay->server.addr.addr6, &multicast))
+           {
+             int multicast_iface;
+             if (!relay->interface || strchr(relay->interface, '*') ||
+                 (multicast_iface = if_nametoindex(relay->interface)) == 0 ||
+                 setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1)
+               my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast to DHCPv6 server without correct interface"));
+           }
+               
+         send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(0), &to, &from, 0);
+         
+         if (option_bool(OPT_LOG_OPTS))
+           {
+             inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN);
+             inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN);
+             my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->namebuff);
+           }
+
+         /* Save this for replies */
+         relay->iface_index = scope_id;
+       }
+    }
+}
+
+unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface)
+{
+  struct dhcp_relay *relay;
+  struct in6_addr link;
+  unsigned char *inbuff = daemon->dhcp_packet.iov_base;
+  
+  /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
+     which is               1   +    1   +    16      +     16     + 2 + 2 = 38 */
+  
+  if (sz < 38 || *inbuff != DHCP6RELAYREPL)
+    return 0;
+  
+  memcpy(&link, &inbuff[2], IN6ADDRSZ); 
+  
+  for (relay = daemon->relay6; relay; relay = relay->next)
+    if (IN6_ARE_ADDR_EQUAL(&link, &relay->local.addr.addr6) &&
+       (!relay->interface || wildcard_match(relay->interface, arrival_interface)))
+      break;
+      
+  save_counter(0);
+
+  if (relay)
+    {
+      void *opt, *opts = inbuff + 34;
+      void *end = inbuff + sz;
+      for (opt = opts; opt; opt = opt6_next(opt, end))
+       if (opt6_type(opt) == OPTION6_RELAY_MSG && opt6_len(opt) > 0)
+         {
+           int encap_type = *((unsigned char *)opt6_ptr(opt, 0));
+           put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
+           memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ); 
+           peer->sin6_scope_id = relay->iface_index;
+           return encap_type == DHCP6RELAYREPL ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;
+         }
+    }
+
+  return 0;
+}
+
+#endif
diff --git a/src/slaac.c b/src/slaac.c
new file mode 100644 (file)
index 0000000..abaad53
--- /dev/null
@@ -0,0 +1,209 @@
+/* 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"
+
+#ifdef HAVE_DHCP6
+
+#include <netinet/icmp6.h>
+
+static int ping_id = 0;
+
+void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
+{
+  struct slaac_address *slaac, *old, **up;
+  struct dhcp_context *context;
+  int dns_dirty = 0;
+  
+  if (!(lease->flags & LEASE_HAVE_HWADDR) || 
+      (lease->flags & (LEASE_TA | LEASE_NA)) ||
+      lease->last_interface == 0 ||
+      !lease->hostname)
+    return ;
+  
+  old = lease->slaac_address;
+  lease->slaac_address = NULL;
+
+  for (context = daemon->dhcp6; context; context = context->next) 
+    if ((context->flags & CONTEXT_RA_NAME) && 
+       !(context->flags & CONTEXT_OLD) &&
+       lease->last_interface == context->if_index)
+      {
+       struct in6_addr addr = context->start6;
+       if (lease->hwaddr_len == 6 &&
+           (lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802))
+         {
+           /* convert MAC address to EUI-64 */
+           memcpy(&addr.s6_addr[8], lease->hwaddr, 3);
+           memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3);
+           addr.s6_addr[11] = 0xff;
+           addr.s6_addr[12] = 0xfe;
+         }
+#if defined(ARPHRD_EUI64)
+       else if (lease->hwaddr_len == 8 &&
+                lease->hwaddr_type == ARPHRD_EUI64)
+         memcpy(&addr.s6_addr[8], lease->hwaddr, 8);
+#endif
+#if defined(ARPHRD_IEEE1394) && defined(ARPHRD_EUI64)
+       else if (lease->clid_len == 9 && 
+                lease->clid[0] ==  ARPHRD_EUI64 &&
+                lease->hwaddr_type == ARPHRD_IEEE1394)
+         /* firewire has EUI-64 identifier as clid */
+         memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
+#endif
+       else
+         continue;
+       
+       addr.s6_addr[8] ^= 0x02;
+       
+       /* check if we already have this one */
+       for (up = &old, slaac = old; slaac; slaac = slaac->next)
+         {
+           if (IN6_ARE_ADDR_EQUAL(&addr, &slaac->addr))
+             {
+               *up = slaac->next;
+               /* recheck when DHCPv4 goes through init-reboot */
+               if (force)
+                 {
+                   slaac->ping_time = now;
+                   slaac->backoff = 1;
+                   dns_dirty = 1;
+                 }
+               break;
+             }
+           up = &slaac->next;
+         }
+           
+       /* No, make new one */
+       if (!slaac && (slaac = whine_malloc(sizeof(struct slaac_address))))
+         {
+           slaac->ping_time = now;
+           slaac->backoff = 1;
+           slaac->addr = addr;
+           /* Do RA's to prod it */
+           ra_start_unsolicted(now, context);
+         }
+       
+       if (slaac)
+         {
+           slaac->next = lease->slaac_address;
+           lease->slaac_address = slaac;
+         }
+      }
+  
+  if (old || dns_dirty)
+    lease_update_dns(1);
+  
+  /* Free any no reused */
+  for (; old; old = slaac)
+    {
+      slaac = old->next;
+      free(old);
+    }
+}
+
+
+time_t periodic_slaac(time_t now, struct dhcp_lease *leases)
+{
+  struct dhcp_context *context;
+  struct dhcp_lease *lease;
+  struct slaac_address *slaac;
+  time_t next_event = 0;
+  
+  for (context = daemon->dhcp6; context; context = context->next)
+    if ((context->flags & CONTEXT_RA_NAME) && !(context->flags & CONTEXT_OLD))
+      break;
+
+  /* nothing configured */
+  if (!context)
+    return 0;
+
+  while (ping_id == 0)
+    ping_id = rand16();
+
+  for (lease = leases; lease; lease = lease->next)
+    for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
+      {
+       /* confirmed or given up? */
+       if (slaac->backoff == 0 || slaac->ping_time == 0)
+         continue;
+       
+       if (difftime(slaac->ping_time, now) <= 0.0)
+         {
+           struct ping_packet *ping;
+           struct sockaddr_in6 addr;
+           save_counter(0);
+           ping = expand(sizeof(struct ping_packet));
+           ping->type = ICMP6_ECHO_REQUEST;
+           ping->code = 0;
+           ping->identifier = ping_id;
+           ping->sequence_no = slaac->backoff;
+           
+           memset(&addr, 0, sizeof(addr));
+#ifdef HAVE_SOCKADDR_SA_LEN
+           addr.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+           addr.sin6_family = AF_INET6;
+           addr.sin6_port = htons(IPPROTO_ICMPV6);
+           addr.sin6_addr = slaac->addr;
+           
+           if (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0,
+                      (struct sockaddr *)&addr,  sizeof(addr)) == -1 &&
+               errno == EHOSTUNREACH)
+             slaac->ping_time = 0; /* Give up */ 
+           else
+             {
+               slaac->ping_time += (1 << (slaac->backoff - 1)) + (rand16()/21785); /* 0 - 3 */
+               if (slaac->backoff > 4)
+                 slaac->ping_time += rand16()/4000; /* 0 - 15 */
+               if (slaac->backoff < 12)
+                 slaac->backoff++;
+             }
+         }
+       
+       if (slaac->ping_time != 0 &&
+           (next_event == 0 || difftime(next_event, slaac->ping_time) >= 0.0))
+         next_event = slaac->ping_time;
+      }
+
+  return next_event;
+}
+
+
+void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases)
+{
+  struct dhcp_lease *lease;
+  struct slaac_address *slaac;
+  struct ping_packet *ping = (struct ping_packet *)packet;
+  int gotone = 0;
+  
+  if (ping->identifier == ping_id)
+    for (lease = leases; lease; lease = lease->next)
+      for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
+       if (slaac->backoff != 0 && IN6_ARE_ADDR_EQUAL(sender, &slaac->addr))
+         {
+           slaac->backoff = 0;
+           gotone = 1;
+           inet_ntop(AF_INET6, sender, daemon->addrbuff, ADDRSTRLEN);
+           if (!option_bool(OPT_QUIET_DHCP6))
+             my_syslog(MS_DHCP | LOG_INFO, "SLAAC-CONFIRM(%s) %s %s", interface, daemon->addrbuff, lease->hostname); 
+         }
+  
+  lease_update_dns(gotone);
+}
+       
+#endif
diff --git a/src/tables.c b/src/tables.c
new file mode 100644 (file)
index 0000000..aae1252
--- /dev/null
@@ -0,0 +1,173 @@
+/* tables.c is Copyright (c) 2014 Sven Falempin  All Rights Reserved.
+
+   Author's email: sfalempin@citypassenger.com 
+
+   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"
+
+#if defined(HAVE_IPSET) && defined(HAVE_BSD_NETWORK)
+
+#ifndef __FreeBSD__
+#include <string.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#define UNUSED(x) (void)(x)
+
+static char *pf_device = "/dev/pf";
+static int dev = -1;
+
+static char *pfr_strerror(int errnum)
+{
+  switch (errnum) 
+    {
+    case ESRCH:
+      return "Table does not exist";
+    case ENOENT:
+      return "Anchor or Ruleset does not exist";
+    default:
+      return strerror(errnum);
+    }
+}
+
+static int pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags)
+{
+  struct pfioc_table io;
+  
+  if (size < 0 || (size && tbl == NULL)) 
+    {
+      errno = EINVAL;
+      return (-1);
+    }
+  bzero(&io, sizeof io);
+  io.pfrio_flags = flags;
+  io.pfrio_buffer = tbl;
+  io.pfrio_esize = sizeof(*tbl);
+  io.pfrio_size = size;
+  if (ioctl(dev, DIOCRADDTABLES, &io))
+    return (-1);
+  if (nadd != NULL)
+    *nadd = io.pfrio_nadd;
+  return (0);
+}
+
+static int fill_addr(const struct all_addr *ipaddr, int flags, struct pfr_addr* addr) {
+  if ( !addr || !ipaddr)
+    {
+      my_syslog(LOG_ERR, _("error: fill_addr missused"));
+      return -1;
+    }
+  bzero(addr, sizeof(*addr));
+#ifdef HAVE_IPV6
+  if (flags & F_IPV6) 
+    {
+      addr->pfra_af = AF_INET6;
+      addr->pfra_net = 0x80;
+      memcpy(&(addr->pfra_ip6addr), &(ipaddr->addr), sizeof(struct in6_addr));
+    } 
+  else 
+#endif
+    {
+      addr->pfra_af = AF_INET;
+      addr->pfra_net = 0x20;
+      addr->pfra_ip4addr.s_addr = ipaddr->addr.addr4.s_addr;
+    }
+  return 1;
+}
+
+/*****************************************************************************/
+
+void ipset_init(void) 
+{
+  dev = open( pf_device, O_RDWR);
+  if (dev == -1)
+    {
+      err(1, "%s", pf_device);
+      die (_("failed to access pf devices: %s"), NULL, EC_MISC);
+    }
+}
+
+int add_to_ipset(const char *setname, const struct all_addr *ipaddr,
+                     int flags, int remove)
+{
+  struct pfr_addr addr;
+  struct pfioc_table io;
+  struct pfr_table table;
+  int n = 0, rc = 0;
+
+  if ( dev == -1 ) 
+    {
+      my_syslog(LOG_ERR, _("warning: no opened pf devices %s"), pf_device);
+      return -1;
+    }
+
+  bzero(&table, sizeof(struct pfr_table));
+  table.pfrt_flags |= PFR_TFLAG_PERSIST;
+  if ( strlen(setname) >= PF_TABLE_NAME_SIZE )
+    {
+      my_syslog(LOG_ERR, _("error: cannot use table name %s"), setname);
+      errno = ENAMETOOLONG;
+      return -1;
+    }
+  
+  if ( strlcpy(table.pfrt_name, setname,
+               sizeof(table.pfrt_name)) >= sizeof(table.pfrt_name)) 
+    {
+      my_syslog(LOG_ERR, _("error: cannot strlcpy table name %s"), setname);
+      return -1;
+    }
+  
+  if ((rc = pfr_add_tables(&table, 1, &n, 0))) 
+    {
+      my_syslog(LOG_WARNING, _("warning: pfr_add_tables: %s(%d)"),
+               pfr_strerror(errno),rc);
+      return -1;
+    }
+  table.pfrt_flags &= ~PFR_TFLAG_PERSIST;
+  if (n)
+    my_syslog(LOG_INFO, _("info: table created"));
+  
+  fill_addr(ipaddr,flags,&addr);
+  bzero(&io, sizeof(io));
+  io.pfrio_flags = 0;
+  io.pfrio_table = table;
+  io.pfrio_buffer = &addr;
+  io.pfrio_esize = sizeof(addr);
+  io.pfrio_size = 1;
+  if (ioctl(dev, ( remove ? DIOCRDELADDRS : DIOCRADDADDRS ), &io)) 
+    {
+      my_syslog(LOG_WARNING, _("warning: DIOCR%sADDRS: %s"), ( remove ? "DEL" : "ADD" ), pfr_strerror(errno));
+      return -1;
+    }
+  
+  my_syslog(LOG_INFO, _("%d addresses %s"),
+            io.pfrio_nadd, ( remove ? "removed" : "added" ));
+  
+  return io.pfrio_nadd;
+}
+
+
+#endif
index 789c444..350a587 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
 
 #ifdef HAVE_TFTP
 
-static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special);
+static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix);
 static void free_transfer(struct tftp_transfer *transfer);
 static ssize_t tftp_err(int err, char *packet, char *mess, char *file);
 static ssize_t tftp_err_oops(char *packet, char *file);
 static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
 static char *next(char **p, char *end);
+static void sanitise(char *buf);
 
 #define OP_RRQ  1
 #define OP_WRQ  2
@@ -47,22 +48,24 @@ void tftp_request(struct listener *listen, time_t now)
   struct msghdr msg;
   struct iovec iov;
   struct ifreq ifr;
-  int is_err = 1, if_index = 0, mtu = 0, special = 0;
-#ifdef HAVE_DHCP
+  int is_err = 1, if_index = 0, mtu = 0;
   struct iname *tmp;
-#endif
   struct tftp_transfer *transfer;
   int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */
 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
   int mtuflag = IP_PMTUDISC_DONT;
 #endif
   char namebuff[IF_NAMESIZE];
-  char pretty_addr[ADDRSTRLEN];
-  char *name;
+  char *name = NULL;
   char *prefix = daemon->tftp_prefix;
   struct tftp_prefix *pref;
-  struct interface_list *ir;
-
+  struct all_addr addra;
+#ifdef HAVE_IPV6
+  /* Can always get recvd interface for IPv6 */
+  int check_dest = !option_bool(OPT_NOWILD) || listen->family == AF_INET6;
+#else
+  int check_dest = !option_bool(OPT_NOWILD);
+#endif
   union {
     struct cmsghdr align; /* this ensures alignment */
 #ifdef HAVE_IPV6
@@ -93,18 +96,28 @@ void tftp_request(struct listener *listen, time_t now)
 
   if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
     return;
-  
-  if (option_bool(OPT_NOWILD))
+
+  /* Can always get recvd interface for IPv6 */
+  if (!check_dest)
     {
-      addr = listen->iface->addr;
-      mtu = listen->iface->mtu;
-      name = listen->iface->name;
+      if (listen->iface)
+       {
+         addr = listen->iface->addr;
+         mtu = listen->iface->mtu;
+         name = listen->iface->name;
+       }
+      else
+       {
+         /* we're listening on an address that doesn't appear on an interface,
+            ask the kernel what the socket is bound to */
+         socklen_t tcp_len = sizeof(union mysockaddr);
+         if (getsockname(listen->tftpfd, (struct sockaddr *)&addr, &tcp_len) == -1)
+           return;
+       }
     }
   else
     {
       struct cmsghdr *cmptr;
-      int check;
-      struct interface_list *ir;
 
       if (msg.msg_controllen < sizeof(struct cmsghdr))
         return;
@@ -114,7 +127,7 @@ void tftp_request(struct listener *listen, time_t now)
 #if defined(HAVE_LINUX_NETWORK)
       if (listen->family == AF_INET)
        for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
-         if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
+         if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
            {
              union {
                unsigned char *c;
@@ -163,7 +176,7 @@ void tftp_request(struct listener *listen, time_t now)
       if (listen->family == AF_INET6)
         {
           for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
-            if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == daemon->v6pktinfo)
+            if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
               {
                 union {
                   unsigned char *c;
@@ -181,28 +194,40 @@ void tftp_request(struct listener *listen, time_t now)
        return;
 
       name = namebuff;
+      
+      addra.addr.addr4 = addr.in.sin_addr;
 
 #ifdef HAVE_IPV6
       if (listen->family == AF_INET6)
-       check = iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name, &if_index);
-      else
+       addra.addr.addr6 = addr.in6.sin6_addr;
 #endif
-        check = iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name, &if_index);
-
-      /* wierd TFTP service override */
-      for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
-       if (strcmp(ir->interface, name) == 0)
-         break;
-       
-      if (!ir)
+
+      if (daemon->tftp_interfaces)
        {
-         if (!daemon->tftp_unlimited || !check)
+         /* dedicated tftp interface list */
+         for (tmp = daemon->tftp_interfaces; tmp; tmp = tmp->next)
+           if (tmp->name && wildcard_match(tmp->name, name))
+             break;
+
+         if (!tmp)
            return;
+       }
+      else
+       {
+         /* Do the same as DHCP */
+         if (!iface_check(listen->family, &addra, name, NULL))
+           {
+             if (!option_bool(OPT_CLEVERBIND))
+               enumerate_interfaces(0); 
+             if (!loopback_exception(listen->tftpfd, listen->family, &addra, name) &&
+                 !label_exception(if_index, listen->family, &addra) )
+               return;
+           }
          
 #ifdef HAVE_DHCP      
          /* allowed interfaces are the same as for DHCP */
          for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
-           if (tmp->name && (strcmp(tmp->name, name) == 0))
+           if (tmp->name && wildcard_match(tmp->name, name))
              return;
 #endif
        }
@@ -211,28 +236,31 @@ void tftp_request(struct listener *listen, time_t now)
       if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
        mtu = ifr.ifr_mtu;      
     }
-  
-  /* check for per-interface prefix */ 
-  for (pref = daemon->if_prefix; pref; pref = pref->next)
-    if (strcmp(pref->interface, name) == 0)
-      prefix = pref->prefix;
 
-  /* wierd TFTP interfaces disable special options. */
-  for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
-    if (strcmp(ir->interface, name) == 0)
-      special = 1;
+  if (name)
+    {
+      /* check for per-interface prefix */ 
+      for (pref = daemon->if_prefix; pref; pref = pref->next)
+       if (strcmp(pref->interface, name) == 0)
+         prefix = pref->prefix;  
+    }
 
+  if (listen->family == AF_INET)
+    {
+      addr.in.sin_port = htons(port);
 #ifdef HAVE_SOCKADDR_SA_LEN
-  addr.sa.sa_len = sa_len(&addr);
+      addr.in.sin_len = sizeof(addr.in);
 #endif
-
-  if (listen->family == AF_INET)
-    addr.in.sin_port = htons(port);
+    }
 #ifdef HAVE_IPV6
   else
     {
       addr.in6.sin6_port = htons(port);
       addr.in6.sin6_flowinfo = 0;
+      addr.in6.sin6_scope_id = 0;
+#ifdef HAVE_SOCKADDR_SA_LEN
+      addr.in6.sin6_len = sizeof(addr.in6);
+#endif
     }
 #endif
 
@@ -255,14 +283,14 @@ void tftp_request(struct listener *listen, time_t now)
   transfer->opt_blocksize = transfer->opt_transize = 0;
   transfer->netascii = transfer->carrylf = 0;
  
-  prettyprint_addr(&peer, pretty_addr);
+  prettyprint_addr(&peer, daemon->addrbuff);
   
   /* if we have a nailed-down range, iterate until we find a free one. */
   while (1)
     {
-      if (bind(transfer->sockfd, &addr.sa, sizeof(addr)) == -1 ||
+      if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
-         setsockopt(transfer->sockfd, SOL_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
+         setsockopt(transfer->sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
 #endif
          !fix_fd(transfer->sockfd))
        {
@@ -293,7 +321,10 @@ void tftp_request(struct listener *listen, time_t now)
       !(filename = next(&p, end)) ||
       !(mode = next(&p, end)) ||
       (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
-    len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), pretty_addr);
+    {
+      len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
+      is_err = 1;
+    }
   else
     {
       if (strcasecmp(mode, "netascii") == 0)
@@ -303,8 +334,7 @@ void tftp_request(struct listener *listen, time_t now)
        {
          if (strcasecmp(opt, "blksize") == 0)
            {
-             if ((opt = next(&p, end)) &&
-                 (special || !option_bool(OPT_TFTP_NOBLOCK)))
+             if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))
                {
                  transfer->blocksize = atoi(opt);
                  if (transfer->blocksize < 1)
@@ -326,9 +356,12 @@ void tftp_request(struct listener *listen, time_t now)
        }
 
       /* cope with backslashes from windows boxen. */
-      while ((p = strchr(filename, '\\')))
-       *p = '/';
-
+      for (p = filename; *p; p++)
+       if (*p == '\\')
+         *p = '/';
+       else if (option_bool(OPT_TFTP_LC))
+         *p = tolower(*p);
+               
       strcpy(daemon->namebuff, "/");
       if (prefix)
        {
@@ -338,12 +371,12 @@ void tftp_request(struct listener *listen, time_t now)
          if (prefix[strlen(prefix)-1] != '/')
            strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
 
-         if (!special && option_bool(OPT_TFTP_APREF))
+         if (option_bool(OPT_TFTP_APREF))
            {
              size_t oldlen = strlen(daemon->namebuff);
              struct stat statbuf;
              
-             strncat(daemon->namebuff, pretty_addr, (MAXDNAME-1) - strlen(daemon->namebuff));
+             strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
              strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
              
              /* remove unique-directory if it doesn't exist */
@@ -365,7 +398,7 @@ void tftp_request(struct listener *listen, time_t now)
       strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
 
       /* check permissions and open file */
-      if ((transfer->file = check_tftp_fileperm(&len, prefix, special)))
+      if ((transfer->file = check_tftp_fileperm(&len, prefix)))
        {
          if ((len = get_block(packet, transfer)) == -1)
            len = tftp_err_oops(packet, daemon->namebuff);
@@ -375,7 +408,7 @@ void tftp_request(struct listener *listen, time_t now)
     }
   
   while (sendto(transfer->sockfd, packet, len, 0, 
-               (struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR);
+               (struct sockaddr *)&peer, sa_len(&peer)) == -1 && errno == EINTR);
   
   if (is_err)
     free_transfer(transfer);
@@ -386,7 +419,7 @@ void tftp_request(struct listener *listen, time_t now)
     }
 }
  
-static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special)
+static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
 {
   char *packet = daemon->packet, *namebuff = daemon->namebuff;
   struct tftp_file *file;
@@ -423,7 +456,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int spe
        goto perm;
     }
   /* in secure mode, must be owned by user running dnsmasq */
-  else if (!special && option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid)
+  else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid)
     goto perm;
       
   /* If we're doing many tranfers from the same file, only 
@@ -469,11 +502,10 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int spe
   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;
-  char pretty_addr[ADDRSTRLEN];
   
   struct ack {
     unsigned short op, block;
@@ -484,12 +516,12 @@ void check_tftp_listeners(fd_set *rset, time_t now)
     {
       tmp = transfer->next;
       
-      if (FD_ISSET(transfer->sockfd, rset))
+      prettyprint_addr(&transfer->peer, daemon->addrbuff);
+     
+      if (poll_check(transfer->sockfd, POLLIN))
        {
          /* we overwrote the buffer... */
          daemon->srv_save = NULL;
-          
-         prettyprint_addr(&transfer->peer, pretty_addr);
          
          if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
            {
@@ -511,17 +543,11 @@ void check_tftp_listeners(fd_set *rset, time_t now)
                  if (!err)
                    err = "";
                  else
-                   {
-                     unsigned char *q, *r;
-                     for (q = r = (unsigned char *)err; *r; r++)
-                       if (isprint(*r))
-                         *(q++) = *r;
-                     *q = 0;
-                   }
-
+                   sanitise(err);
+                 
                  my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
                            (int)ntohs(mess->block), err, 
-                           pretty_addr);       
+                           daemon->addrbuff);  
                  
                  /* Got err, ensure we take abort */
                  transfer->timeout = now;
@@ -545,30 +571,33 @@ void check_tftp_listeners(fd_set *rset, time_t now)
              len = tftp_err_oops(daemon->packet, transfer->file->filename);
              endcon = 1;
            }
-         else if (++transfer->backoff > 5)
+         /* don't complain about timeout when we're awaiting the last
+            ACK, some clients never send it */
+         else if (++transfer->backoff > 7 && len != 0)
            {
-             /* don't complain about timeout when we're awaiting the last
-                ACK, some clients never send it */
-             if (len != 0)
-               {
-                 my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"), 
-                           transfer->file->filename, pretty_addr);
-                 len = 0;
-                 endcon = 1;
-               }
+             endcon = 1;
+             len = 0;
            }
-         
+
          if (len != 0)
            while(sendto(transfer->sockfd, daemon->packet, len, 0, 
-                        (struct sockaddr *)&transfer->peer, sizeof(transfer->peer)) == -1 && errno == EINTR);
+                        (struct sockaddr *)&transfer->peer, sa_len(&transfer->peer)) == -1 && errno == EINTR);
          
          if (endcon || len == 0)
            {
-             if (!endcon)
-               my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), transfer->file->filename, pretty_addr);
+             strcpy(daemon->namebuff, transfer->file->filename);
+             sanitise(daemon->namebuff);
+             my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
              /* unlink */
              *up = tmp;
-             free_transfer(transfer);
+             if (endcon)
+               free_transfer(transfer);
+             else
+               {
+                 /* put on queue to be sent to script and deleted */
+                 transfer->next = daemon->tftp_done_trans;
+                 daemon->tftp_done_trans = transfer;
+               }
              continue;
            }
        }
@@ -602,6 +631,16 @@ static char *next(char **p, char *end)
   return ret;
 }
 
+static void sanitise(char *buf)
+{
+  unsigned char *q, *r;
+  for (q = r = (unsigned char *)buf; *r; r++)
+    if (isprint((int)*r))
+      *(q++) = *r;
+  *q = 0;
+
+}
+
 static ssize_t tftp_err(int err, char *packet, char *message, char *file)
 {
   struct errmess {
@@ -610,7 +649,9 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
   } *mess = (struct errmess *)packet;
   ssize_t ret = 4;
   char *errstr = strerror(errno);
+  
+  sanitise(file);
+
   mess->op = htons(OP_ERR);
   mess->err = htons(err);
   ret += (snprintf(mess->message, 500,  message, file, errstr) + 1);
@@ -621,7 +662,9 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
 
 static ssize_t tftp_err_oops(char *packet, char *file)
 {
-  return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), file);
+  /* May have >1 refs to file, so potentially mangle a copy of the name */
+  strcpy(daemon->namebuff, file);
+  return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
 }
 
 /* return -1 for error, zero for done. */
@@ -685,15 +728,13 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
          for (i = 0, newcarrylf = 0; i < size; i++)
            if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
              {
-               if (size == transfer->blocksize)
-                 {
-                   transfer->expansion++;
-                   if (i == size - 1)
-                     newcarrylf = 1; /* don't expand LF again if it moves to the next block */
-                 }
-               else
+               transfer->expansion++;
+
+               if (size != transfer->blocksize)
                  size++; /* room in this block */
-             
+               else  if (i == size - 1)
+                 newcarrylf = 1; /* don't expand LF again if it moves to the next block */
+                 
                /* make space and insert CR */
                memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
                mess->data[i] = '\r';
@@ -708,4 +749,21 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
     }
 }
 
+
+int do_tftp_script_run(void)
+{
+  struct tftp_transfer *transfer;
+
+  if ((transfer = daemon->tftp_done_trans))
+    {
+      daemon->tftp_done_trans = transfer->next;
+#ifdef HAVE_SCRIPT
+      queue_tftp(transfer->file->size, transfer->file->filename, &transfer->peer);
+#endif
+      free_transfer(transfer);
+      return 1;
+    }
+
+  return 0;
+}
 #endif
index e64f1a6..469eaed 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* 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
 #include <idna.h>
 #endif
 
-#ifdef HAVE_ARC4RANDOM
-void rand_init(void)
-{
-  return;
-}
-
-unsigned short rand16(void)
-{
-   return (unsigned short) (arc4random() >> 15);
-}
-
-#else
-
 /* SURF random number generator */
 
 static u32 seed[32];
 static u32 in[12];
 static u32 out[8];
+static int outleft = 0;
 
 void rand_init()
 {
@@ -83,19 +71,44 @@ static void surf(void)
 
 unsigned short rand16(void)
 {
+  if (!outleft) 
+    {
+      if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
+      surf();
+      outleft = 8;
+    }
+  
+  return (unsigned short) out[--outleft];
+}
+
+u32 rand32(void)
+{
+ if (!outleft) 
+    {
+      if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
+      surf();
+      outleft = 8;
+    }
+  
+  return out[--outleft]; 
+}
+
+u64 rand64(void)
+{
   static int outleft = 0;
 
-  if (!outleft) {
-    if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
-    surf();
-    outleft = 8;
-  }
+  if (outleft < 2)
+    {
+      if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
+      surf();
+      outleft = 8;
+    }
+  
+  outleft -= 2;
 
-  return (unsigned short) out[--outleft];
+  return (u64)out[outleft+1] + (((u64)out[outleft]) << 32);
 }
 
-#endif
-
 static int check_name(char *in)
 {
   /* remove trailing . 
@@ -108,10 +121,10 @@ static int check_name(char *in)
   
   if (in[l-1] == '.')
     {
-      if (l == 1) return 0;
       in[l-1] = 0;
+      nowhite = 1;
     }
-  
+
   for (; (c = *in); in++)
     {
       if (c == '.')
@@ -142,17 +155,20 @@ static int check_name(char *in)
 int legal_hostname(char *name)
 {
   char c;
+  int first;
 
   if (!check_name(name))
     return 0;
 
-  for (; (c = *name); name++)
+  for (first = 1; (c = *name); name++, first = 0)
     /* check for legal char a-z A-Z 0-9 - _ . */
     {
       if ((c >= 'A' && c <= 'Z') ||
          (c >= 'a' && c <= 'z') ||
-         (c >= '0' && c <= '9') ||
-         c == '-' || c == '_')
+         (c >= '0' && c <= '9'))
+       continue;
+
+      if (!first && (c == '-' || c == '_'))
        continue;
       
       /* end of hostname part */
@@ -210,7 +226,14 @@ unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
     {
       unsigned char *cp = p++;
       for (j = 0; *sval && (*sval != '.'); sval++, j++)
-       *p++ = *sval;
+       {
+#ifdef HAVE_DNSSEC
+         if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE)
+           *p++ = (*(++sval))-1;
+         else
+#endif         
+           *p++ = *sval;
+       }
       *cp  = j;
       if (*sval)
        sval++;
@@ -258,6 +281,7 @@ int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
 #ifdef HAVE_IPV6      
       if (s1->sa.sa_family == AF_INET6 &&
          s1->in6.sin6_port == s2->in6.sin6_port &&
+         s1->in6.sin6_scope_id == s2->in6.sin6_scope_id &&
          IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr))
        return 1;
 #endif
@@ -280,7 +304,7 @@ int sa_len(union mysockaddr *addr)
 }
 
 /* don't use strcasecmp and friends here - they may be messed up by LOCALE */
-int hostname_isequal(char *a, char *b)
+int hostname_isequal(const char *a, const char *b)
 {
   unsigned int c1, c2;
   
@@ -315,11 +339,66 @@ time_t dnsmasq_time(void)
 #endif
 }
 
+int netmask_length(struct in_addr mask)
+{
+  int zero_count = 0;
+
+  while (0x0 == (mask.s_addr & 0x1) && zero_count < 32) 
+    {
+      mask.s_addr >>= 1;
+      zero_count++;
+    }
+  
+  return 32 - zero_count;
+}
+
 int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
 {
   return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
 } 
 
+#ifdef HAVE_IPV6
+int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
+{
+  int pfbytes = prefixlen >> 3;
+  int pfbits = prefixlen & 7;
+
+  if (memcmp(&a->s6_addr, &b->s6_addr, pfbytes) != 0)
+    return 0;
+
+  if (pfbits == 0 ||
+      (a->s6_addr[pfbytes] >> (8 - pfbits) == b->s6_addr[pfbytes] >> (8 - pfbits)))
+    return 1;
+
+  return 0;
+}
+
+/* return least signigicant 64 bits if IPv6 address */
+u64 addr6part(struct in6_addr *addr)
+{
+  int i;
+  u64 ret = 0;
+
+  for (i = 8; i < 16; i++)
+    ret = (ret << 8) + addr->s6_addr[i];
+
+  return ret;
+}
+
+void setaddr6part(struct in6_addr *addr, u64 host)
+{
+  int i;
+
+  for (i = 15; i >= 8; i--)
+    {
+      addr->s6_addr[i] = host;
+      host = host >> 8;
+    }
+}
+
+#endif
+
 /* returns port number from address */
 int prettyprint_addr(union mysockaddr *addr, char *buf)
 {
@@ -333,7 +412,15 @@ int prettyprint_addr(union mysockaddr *addr, char *buf)
     }
   else if (addr->sa.sa_family == AF_INET6)
     {
+      char name[IF_NAMESIZE];
       inet_ntop(AF_INET6, &addr->in6.sin6_addr, buf, ADDRSTRLEN);
+      if (addr->in6.sin6_scope_id != 0 &&
+         if_indextoname(addr->in6.sin6_scope_id, name) &&
+         strlen(buf) + strlen(name) + 2 <= ADDRSTRLEN)
+       {
+         strcat(buf, "%");
+         strcat(buf, name);
+       }
       port = ntohs(addr->in6.sin6_port);
     }
 #else
@@ -376,7 +463,7 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
   
   while (maxlen == -1 || i < maxlen)
     {
-      for (r = in; *r != 0 && *r != ':' && *r != '-'; r++)
+      for (r = in; *r != 0 && *r != ':' && *r != '-' && *r != ' '; r++)
        if (*r != '*' && !isxdigit((unsigned char)*r))
          return -1;
       
@@ -394,12 +481,29 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
          else
            {
              *r = 0;
-             mask = mask << 1;
              if (strcmp(in, "*") == 0)
-               mask |= 1;
+               {
+                 mask = (mask << 1) | 1;
+                 i++;
+               }
              else
-               out[i] = strtol(in, NULL, 16);
-             i++;
+               {
+                 int j, bytes = (1 + (r - in))/2;
+                 for (j = 0; j < bytes; j++)
+                   { 
+                     char sav = sav;
+                     if (j < bytes - 1)
+                       {
+                         sav = in[(j+1)*2];
+                         in[(j+1)*2] = 0;
+                       }
+                     out[i] = strtol(&in[j*2], NULL, 16);
+                     mask = mask << 1;
+                     i++;
+                     if (j < bytes - 1)
+                       in[(j+1)*2] = sav;
+                   }
+               }
            }
        }
       in = r+1;
@@ -466,27 +570,41 @@ char *print_mac(char *buff, unsigned char *mac, int len)
   return buff;
 }
 
-void bump_maxfd(int fd, int *max)
+/* rc is return from sendto and friends.
+   Return 1 if we should retry.
+   Set errno to zero if we succeeded. */
+int retry_send(ssize_t rc)
 {
-  if (fd > *max)
-    *max = fd;
-}
+  static int retries = 0;
+  struct timespec waiter;
+  
+  if (rc != -1)
+    {
+      retries = 0;
+      errno = 0;
+      return 0;
+    }
+  
+  /* Linux kernels can return EAGAIN in perpetuity when calling
+     sendmsg() and the relevant interface has gone. Here we loop
+     retrying in EAGAIN for 1 second max, to avoid this hanging 
+     dnsmasq. */
 
-int retry_send(void)
-{
-   struct timespec waiter;
-   if (errno == EAGAIN)
+  if (errno == EAGAIN || errno == EWOULDBLOCK)
      {
        waiter.tv_sec = 0;
        waiter.tv_nsec = 10000;
        nanosleep(&waiter, NULL);
-       return 1;
+       if (retries++ < 1000)
+        return 1;
      }
-   
-   if (errno == EINTR)
-     return 1;
-
-   return 0;
+  
+  retries = 0;
+  
+  if (errno == EINTR)
+    return 1;
+  
+  return 0;
 }
 
 int read_write(int fd, unsigned char *packet, int size, int rw)
@@ -495,22 +613,57 @@ int read_write(int fd, unsigned char *packet, int size, int rw)
   
   for (done = 0; done < size; done += n)
     {
-    retry:
-      if (rw)
-        n = read(fd, &packet[done], (size_t)(size - done));
-      else
-        n = write(fd, &packet[done], (size_t)(size - done));
-
-      if (n == 0)
-        return 0;
-      else if (n == -1)
-        {
-          if (retry_send() || errno == ENOMEM || errno == ENOBUFS)
-            goto retry;
-          else
-            return 0;
-        }
+      do { 
+       if (rw)
+         n = read(fd, &packet[done], (size_t)(size - done));
+       else
+         n = write(fd, &packet[done], (size_t)(size - done));
+       
+       if (n == 0)
+         return 0;
+       
+      } while (retry_send(n) || errno == ENOMEM || errno == ENOBUFS);
+
+      if (errno != 0)
+       return 0;
     }
+     
   return 1;
 }
 
+/* Basically match a string value against a wildcard pattern.  */
+int wildcard_match(const char* wildcard, const char* match)
+{
+  while (*wildcard && *match)
+    {
+      if (*wildcard == '*')
+        return 1;
+
+      if (*wildcard != *match)
+        return 0; 
+
+      ++wildcard;
+      ++match;
+    }
+
+  return *wildcard == *match;
+}
+
+/* The same but comparing a maximum of NUM characters, like strncmp.  */
+int wildcard_matchn(const char* wildcard, const char* match, int num)
+{
+  while (*wildcard && *match && num)
+    {
+      if (*wildcard == '*')
+        return 1;
+
+      if (*wildcard != *match)
+        return 0; 
+
+      ++wildcard;
+      ++match;
+      --num;
+    }
+
+  return (!num) || (*wildcard == *match);
+}
diff --git a/trust-anchors.conf b/trust-anchors.conf
new file mode 100644 (file)
index 0000000..afda518
--- /dev/null
@@ -0,0 +1,9 @@
+# The root DNSSEC trust anchor, valid as at 30/01/2014
+
+# Note that this is a DS record (ie a hash of the root Zone Signing Key) 
+# If was downloaded from https://data.iana.org/root-anchors/root-anchors.xml
+
+trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
+
+
+