dnsmasq-2.91/0000775000175000017500000000000014767032032011301 5ustar srksrkdnsmasq-2.91/Makefile0000664000175000017500000001640414765043257012757 0ustar srksrk# dnsmasq is Copyright (c) 2000-2025 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 . # NOTE: Building the i18n targets requires GNU-make # 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 = LUA = lua ################################################################# # Variables you might want to override. PKG_CONFIG = pkg-config INSTALL = install MSGMERGE = msgmerge MSGFMT = msgfmt XGETTEXT = xgettext SRC = src PO = po MAN = man ################################################################# # 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` ubus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_UBUS "" --copy '-lubox -lubus'` 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` idn2_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LIBIDN2 $(PKG_CONFIG) --cflags libidn2` idn2_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LIBIDN2 $(PKG_CONFIG) --libs libidn2` 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 $(LUA)` lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs $(LUA)` 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` nft_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_NFTSET $(PKG_CONFIG) --cflags libnftables` nft_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_NFTSET $(PKG_CONFIG) --libs libnftables` version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"' sum?=$(shell echo $(CC) -DDNSMASQ_COMPILE_FLAGS="$(CFLAGS)" -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ') sum!=echo $(CC) -DDNSMASQ_COMPILE_FLAGS="$(CFLAGS)" -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ' copts_conf = .copts_$(sum) objs = cache.o rfc1035.o util.o option.o forward.o network.o \ dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \ helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \ dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o pattern.o \ domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \ poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o \ metrics.o domain-match.o nftset.o hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ dns-protocol.h radv-protocol.h ip6addr.h metrics.h all : $(BUILDDIR) @cd $(BUILDDIR) && $(MAKE) \ top="$(top)" \ build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags) $(nft_cflags)" \ build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs) $(nft_libs)" \ -f $(top)/Makefile dnsmasq mostly_clean : rm -f $(BUILDDIR)/*.mo $(BUILDDIR)/*.pot rm -f $(BUILDDIR)/.copts_* $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq clean : mostly_clean rm -f $(BUILDDIR)/dnsmasq_baseline rm -f core */core rm -f *~ contrib/*/*~ */*~ install : all $(INSTALL) -d $(DESTDIR)$(BINDIR) $(INSTALL) -d $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -m 644 $(MAN)/dnsmasq.8 $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -m 755 $(BUILDDIR)/dnsmasq $(DESTDIR)$(BINDIR) all-i18n : $(BUILDDIR) @cd $(BUILDDIR) && $(MAKE) \ top="$(top)" \ i18n=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \ build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags) $(nft_cflags)" \ build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs) $(nft_libs)" \ -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) -d $(DESTDIR)$(BINDIR) $(INSTALL) -d $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -m 644 $(MAN)/dnsmasq.8 $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -m 755 $(BUILDDIR)/dnsmasq $(DESTDIR)$(BINDIR) cd $(BUILDDIR); $(top)/bld/install-mo $(DESTDIR)$(LOCALEDIR) $(INSTALL) cd $(MAN); ../bld/install-man $(DESTDIR)$(MANDIR) $(INSTALL) 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 # Canonicalise .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; $(BUILDDIR): mkdir -p $(BUILDDIR) # 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 recursive makes with cwd=$(BUILDDIR) $(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 : $(objs) $(CC) $(LDFLAGS) -o $@ $(objs) $(build_libs) $(LIBS) 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 mostly_clean install install-common all-i18n install-i18n merge baseline bloatcheck dnsmasq-2.91/CHANGELOG.archive0000664000175000017500000031503314765043257014151 0ustar srksrkrelease 0.4 - initial public release release 0.5 - added caching, removed compiler warning on linux PPC release 0.6 - TCP handling: close socket and return to connect state if we can't read the first byte. This corrects a problem seen very occasionally where dnsmasq would loop using all available CPU. Added a patch from Cris Bailiff to set SO_REUSEADDR on the tcp socket which stops problems when dnsmasq is restarted and old connections still exist. Stopped claiming in doc.html that smail is the default Debian mailer, since it isn't any longer. (Pointed out by David Karlin ) release 0.7 Create a pidfile at /var/run/dnsmasq.pid Extensive armouring against "poison packets" courtesy of Thomas Moestl Set sockaddr.sa_family on outgoing address, patch from David Symonds Patch to clear cache on SIGHUP from Jason L. Wagner Fix bad bug resulting from not initialising value-result address-length parameter to recvfrom() and accept() - it worked by luck before! release 0.95 Major rewrite: remove calls to gethostbyname() and talk directly to the upstream server(s) instead. This has many advantages. (1) Dnsmasq no longer blocks during long lookups. (2) All query types are handled now, (eg MX) not just internet address queries. Addresses are cached, all other queries are forwarded directly. (3) Time-to-live data from upstream server is read and used by dnsmasq to purge entries from the cache. (4) /etc/hosts is still read and its contents served (unless the -h option is given). (5) Dnsmasq can get its upstream servers from a file other than /etc/resolv.conf (-r option) this allows dnsmasq to serve names to the machine it is running on (put nameserver 127.0.0.1 in /etc/resolv.conf and give dnsmasq the option -r /etc/resolv.dnsmasq) (6) Dnsmasq will re-read its servers if the modification time of resolv.conf changes. Along with 4 above this allows nameservers to be set automatically by ppp or dhcp. A really clever NAT-like technique allows the daemon to have lots of queries in progress, but still remain very lightweight. Dnsmasq has a small footprint and normally doesn't allocate any more memory after start-up. The NAT-like forwarding was inspired by a suggestion from Eli Chen release 0.96 Fixed embarrassing thinko in cache linked-list code. release 0.98 Some enhancements and bug-fixes. Thanks to "Denis Carre" and Martin Otte (1) Dnsmasq now always sets the IP source address of its replies correctly. Older versions would not always do this on multi-homed and IP aliased hosts, which violates the RFC. (2) Dnsmasq no longer crashes if a server loop is created (ie dnsmasq is told to use itself as an upstream server.) Now it just logs the problem and doesn't use the bad server address. (3) Dnsmasq should now forward (but not cache) inverse queries and server status queries; this feature has not been tested. (4) Don't write the pid file when in non-daemon mode. (5) Create the pid file mode 644, rather then 666 (!). (6) Generate queries to upstream nameservers with unpredictable ids, to thwart DNS spoofers. (7) Dnsmasq no longer forwards queries when the "recursion desired" bit is not set in the header. (8) Fixed getopt code to work on compilers with unsigned char. release 0.991 Added -b flag: when set causes dnsmasq to always answer reverse queries on the RFC 1918 private IP space itself and never forward them to an upstream server. If the name is not in /etc/hosts, dnsmasq replies with the dotted-quad address. Fixed a bug which stopped dnsmasq working on a box with two or more interfaces with the same IP address. Fixed caching of CNAMEs. Previously, a CNAME which pointed to a name with many A records would not have all the addresses returned when being answered from the cache. Thanks to "Steve Hardy" for his input on these fixes. Fixed race which could cause dnsmasq to miss the second of two closely-spaced updates of resolv.conf (Thanks to Eli Chen for pointing this out.) Fixed a bug which could cause dnsmasq to fail to cache some dns names. release 0.992 Small change to memory allocation so that names in /etc/hosts don't use cache slots. Also make "-c 0" flag meaningfully disable caching completely. release 0.993 Return only the first (canonical) name from an entry in /etc/hosts as reply to reverse query. Handle wildcard queries for names/addresses in /etc/hosts this is mainly to allow reverse lookups by dig to succeed. (Bug reported by Simon J. Rowe" ) Subtle change to the logic which selects which of multiple upstream servers we send queries to. This fixes a problem where dnsmasq continuously sends queries to a server which is returning error codes and ignores one which is working. release 0.994 Fixed bug which broke lookup of names in /etc/hosts which have upper-case letters in them. Thanks for Joao Clemente for spotting that one. Output cache statistics on receipt of SIGUSR1. These go to syslog except in debug (-d) mode, when a complete cache dump goes to stdout. Suggestion from Joao Clemente, code based in John Volpe's. Accept GNU long options on the command line. Code from John Volpe for this. Split source code into multiple files and produced a proper makefile. Included code from John Volpe to parse dhcp.leases file written by ISC dhcpd. The hostnames in the leases file are added to the cache and updated as dhcpd updates the leases file. The code has been heavily re-worked by me, so any bugs are probably mine. release 0.995 Small tidy-ups to signal handling and cache code. release 0.996 Added negative caching: If dnsmasq gets a "no such domain" reply from an upstream nameserver, it will cache that information for a time specified by the SOA RR in the reply. See RFC 2308 for details. This is useful with resolver libraries which append assorted suffices to non-FQDN in an attempt to resolve them, causing useless cache misses. Added -i flag, which restricts dnsmasq to offering name service only on specified interfaces. release 0.997 Deleted INSTALL script and added "install" target to makefile. Stopped distributing binaries in the tarball to avoid libc version clashes. Fixed interface detection code to remove spurious startup errors in rare circumstances. Dnsmasq now changes its uid, irrevocably, to nobody after startup for security reasons. Thanks to Peter Bailey for this patch. Cope with infinite DHCP leases. Patch thanks to Yaacov Akiba Slama. Added rpm control files to .tar.gz distribution. Thanks to Peter Baldwin at ClarkConnect for those. Improved startup script for rpms. Thanks to Yaacov Akiba Slama. release 1.0 Stable release: dnsmasq is now considered feature-complete and stable. release 1.1 Added --user argument to allow user to change to a different userid. Added --mx-target argument to allow mail to be delivered away from the gateway machine running dnsmasq. Fixed highly obscure bug with wildcard queries for DHCP lease derived names. Moved manpage from section 1 to section 8. Added --no-poll option. Added Suse-rpm support. Thanks to Joerg Mayer for the last two. release 1.2 Added IPv6 DNS record support. AAAA records are cached and read from /etc/hosts. Reverse-lookups in the ip6.int and ip6.arpa domains are supported. Dnsmasq can talk to upstream servers via IPv6 if it finds IP6 addresses in /etc/resolv.conf and it offers DNS service automatically if IPv6 support is present in the kernel. Extended negative caching to NODATA replies. Re-vamped CNAME processing to cope with RFC 2317's use of CNAMES to PTR RRs in CIDR. Added config.h and a couple of symbols to aid compilation on non-linux systems. release 1.3 Some versions of the Linux kernel return EINVAL rather then ENPROTONOSUPPORT when IPv6 is not available, causing dnsmasq to bomb out. This release fixes that. Thanks to Steve Davis for pointing this one out. Trivial change to startup logic so that dnsmasq logs its stuff and reads config files straight away on starting, rather than after the first query - principle of least surprise applies here. release 1.4 Fix a bug with DHCP lease parsing which broke in non-UTC timezones. Thanks to Mark Wormgoor for spotting and diagnosing this. Fixed versions in the .spec files this time. Fixed bug in Suse startup script. Thanks to Didi Niklaus for pointing this out. release 1.5 Added --filterwin2k option which stops dnsmasq from forwarding "spam" queries from win2k boxes. This is useful to stop spurious connections over dial-on-demand links. Thanks to Steve Hardy for this code. Clear "truncated" bit in replies we return from upstream. This stops resolvers from switching to TCP, which is pointless since dnsmasq doesn't support TCP. This should solve problems in resolving hotmail.com domains. Don't include getopt.h when Gnu-long-options are disabled - hopefully this will allow compilation on FreeBSD. Added the --listen-address and --pid-file flags. Fixed a bug which caused old entries in the DHCP leases file to be used in preference to current ones under certain circumstances. release 1.6 If a machine gets named via DHCP and the DHCP name doesn't have a domain part and domain suffix is set using the -s flag, then that machine has two names with the same address, with and without the domain suffix. When doing a _reverse_ lookup to get the name, the "without suffix" name used to be returned, now the "with suffix" one gets returned instead. This change suggested by Arnold Schulz. Fixed assorted typos in the documentation. Thanks to David Kimdon. Subtle rearrangement to the downloadable tarball, and stopped distributing .debs, since dnsmasq is now an official Debian package. release 1.7 Fix a problem with cache not clearing properly on receipt of SIGHUP. Bug spotted by Sat Deshpande. In group-id changing code: 1) Drop supplementary groups. 2) Change gid before dropping root (patch from Soewono Effendi.) 3) Change group to "dip" if it exists, to allow access to /etc/ppp/resolv.conf (suggestion from Jorg Sommer.) Update docs to reflect above changes. Other documentation changes from David Miller. Added suggested script fragment for dhcpcd.exe. release 1.8 Fix unsafe use of tolower() macro - allows linking against ulibc. (Patches from Soewono Effendi and Bjorn Andersson.) Fix typo in usage string. Added advice about RedHat PPP configuration to documentation. (Thanks to C. Lee Taylor.) Patches to fix problems on BSD systems from Marc Huber and Can Erkin Acar. These add the options HAVE_ARC4RANDOM and HAVE_SOCKADDR_SA_LEN to config.h. Elaborated config.h - should really use autoconf. Fix time-to-live calculation when chasing CNAMEs. Fix use-after-free and missing initialisation bugs in the cache code. (Thanks to Marc Huber.) Builds on Solaris 9. (Thanks to Marc Huber.) release 1.9 Fixes to rpm .spec files. Don't put expired DHCP entries into the cache only to throw them away again. Put dnsmasq on a severe memory diet: this reduces both the amount of heap space used and the stack size required. The difference is not really visible with bloated libcs like glibc, but should dramatically reduce memory requirements when linked against ulibc for use on embedded routers, and that's the point really. Thanks to Matthew Natalier for prompting this. Changed debug mode (-d) so that all logging appears on stderr as well as going to syslogd. Added HAVE_IPV6 config symbol to allow compilation against a libc which doesn't have IPv6 support. Added a facility to log all queries, enabled with -q flag. Fixed packet size checking bug in address extraction code. Halved default cache size - 300 was way OTT in typical use. Added self-MX function, enabled by -e flag. Thanks to Lyonel Vincent for the patch. Added HAVE_FORK config symbol and stuff to support uClinux. Thanks to Matthew Natalier for uClinux stuff. release 1.10 Log warnings if resolv.conf or dhcp.leases are not accessible for any reason, as suggested by Hinrich Eilts. Fixed wrong address printing in error message about no interface with address. Updated docs and split installation instructions into setup.html. Fix bug in CNAME chasing code: One CNAME pointing to many A records would lose A records after the first. This bug was introduced in version 1.9. Log startup failures at level Critical as well as printing them to standard error. Exit with return code 1 when given bad options. Cleaned up code for no-cache operation. Added -o option which forces dnsmasq to use to upstream servers in the order they appear in /etc/resolv.conf. Added upstream server use logging. Log full cache dump on receipt of SIGUSR1 when query logging is enabled (-q switch). Added -S option to directly specify upstream servers and added ability to direct queries for specific domains to specific servers. Suggested by Jens Vonderheide. Upgraded random ID generation - patch from Rob Funk. Fixed reading of domains in arguments with capital letters or trailing periods. Fixed potential SEGV when given bad options. Read options from /etc/dnsmasq.conf if it exists. Do sensible things with missing parameters, eg "--resolv-file=" turns off reading /etc/resolv.conf. release 1.11 Actually implement the -R flag promised in the 1.10 man page. Improve and rationalise the return codes in answers to queries. In the case that there are no available upstream servers to forward a query to, return REFUSED. This makes sendmail work better on modem connected systems when the modem link is down (Thanks to Roger Plant). Cache and return the NXDOMAIN status of failed queries: this makes the `host` command work when traversing search paths (Thanks to Peter Bailey). Set the "authoritative" bit in replies containing names from /etc/hosts or DHCP. Tolerate MS-DOS style line ending codes in /etc/hosts and /etc/resolv.conf, for people who copy from winsock installations. Allow specification of more than one resolv.conf file. This is intended for laptops which connect via DHCP or PPP. Whichever resolv.conf was updated last is used. Allow -S flags which specify a domain but no server address. This gives local domains which are never forwarded. Add -E flag to automatically add the domain suffix to names in /etc/hosts -suggestion from Phil Harman. Always return a zero time-to-live for names derived from DHCP which stops anything else caching these names. Previously the TTL was derived from the lease time but that is incorrect since a lease can be given up early: dnsmasq would know this but anything with the name cached with long TTL would not be updated. Extended HAVE_IPV6 config flag to allow compilation on old systems which don't have modern library routines like inet_ntop(). Thanks to Phil Harman for the patch. release 1.12 Allow more than one domain in server config lines and make "local" a synonym for "server". This makes things like "local=/localnet/thekelleys.org.uk/" legal. Allow port to specified as part of server address. Allow whole domains to have an IP address specified in /etc/dnsmasq.conf. (/etc/hosts doesn't work domains). address=/doubleclick.net/127.0.0.1 should catch all those nasty banner ads. Inspired by a patch from Daniel Gryniewicz Log the source of each query when logging switched on. Fix bug in script fragment for dhcpcd - thanks to Barry Stewart. Fix bug which meant that strict-order and self-mx were always enabled. Builds with Linux libc5 now - for the Freesco project. Fixed Makefile installation script (patch from Silvan Minghetti) and added CC and CFLAGS variables. Improve resource allocation to reduce vulnerability to DOS attacks - the old version could have all queries blocked by a continuous high-speed stream of queries. Now some queries will succeed, and the excess will be rejected with a server fail error. This change also protects against server-loops; setting up a resolving loop between two instances of dnsmasq is no longer catastrophic. The servers will continue to run, looped queries fail and a warning is logged. Thanks to C. Lee Taylor for help with this. release 1.13 Added support for building rpms suitable for modern Suse systems. (patch from Andi ) Added options --group, --localmx, --local-ttl, --no-negcache, --addn-host. Moved all the various rpm-building bits into /rpm. Fix builds with glibc 2.1 (thanks to Cristian Ionescu-Idbohrn) Preserve case in domain names, as per RFC1035. Fixed ANY queries to domains with --address specification. Fixed FreeBSD build. (thanks to Steven Honson) Added -Q option which allows a specified port to be used to talk to upstream servers. Useful for people who want very paranoid firewalls which open individual UDP port. (thanks to David Coe for the patch) release 1.14 Fixed man page description of -b option which confused /etc/hosts with /etc/resolv.conf. (thanks to Christopher Weimann) Fixed config.h to allow building under MACOS X and glibc 2.0.x. (thanks to Matthew Gregan and Serge Caron) Added --except-interface option. (Suggested by Serge Caron) Added SIGUSR2 facility to re-scan for new interfaces. (Suggested by Serge Caron) Fixed SEGV in option-reading code for invalid options. (Thanks to Klaas Teschauer) Fixed man page to clarify effect of SIGUSR1 on /etc/resolv.conf. (Thanks to Klaas Teschauer) Check that received queries have only rfc1035-legal characters in them. This check is mainly to avoid bad strings being sent to syslog. Fixed &&/& confusion in option.c and added DESTDIR variable for "make install" (Thanks to Osvaldo Marques for the patch.) Fixed /etc/hosts parsing code to cope with MS-DOS line-ends in the file. This was supposed to be done in version 1.11, but something got missed. (Thanks to Doug Copestake for helping to find this.) Squash repeated name/address pairs read from hosts files. Tidied up resource handling in util.c (Thanks to Cristian Ionescu-Idbohrn). Added hashed searching of domain names. People are starting to use dnsmasq with larger loads now, and bigger caches, and large lists of ad-block addresses. This means doing linear searches can start to use lots of CPU so I added hashed searching and seriously optimised the cache code for algorithmic efficiency. Also upped the limit on cache size to 10000. Fixed logging of the source of names from the additional hosts file and from the "bogus private address" option. Fixed spurious re-reading of empty lease files. (Thanks to Lewis Baughman for spotting this.) Fixed building under uclibc (patch from Cristian Ionescu-Idbohrn) Do some socket tweaking to allow dnsmasq to co-exist with BIND. Thanks to Stefan 'Sec' Zehl for the patch. release 1.15 Added --bogus-nxdomain option. Restrict checking of resolv.conf and DHCP leases files to once per second. This is intended to improve performance under heavy loads. Also make a system call to get the current time once per query, rather than four times. Increased number of outstanding queries to 150 in config.h release 1.16 Allow "/" characters in domain names - this fixes caching of RFC 2317 CNAME-PTR records. Fixed brain-fart in -B option when GETOPT_LONG not enabled - thanks to Steven Young and Jason Miller for pointing this out. Generalised bogus-nxdomain code: allow more than one address to check, and deal with replies with multiple answer records. (Based on contribution from Humberto Massa.) Updated the documentation to include information about bogus-nxdomain and the Verisign tragedy. Added libraries needed on Solaris to Makefile. Added facility to set source address in queries to upstream nameservers. This is useful with multihomed hosts, especially when using VPNs. Thanks to Tom Fanning for suggesting this feature. Tweaked logging: log to facility LOCAL0 when in debug/no-daemon mode and changed level of query logging from INFO to DEBUG. Make log options controllable in config.h release 1.17 Fixed crash with DHCP hostnames > 40 characters. Fixed name-comparison routines to not depend on Locale, in theory this versions since 1.15 could lock up or give wrong results when run with locale != 'C'. Fix potential lockup in cache code. (thanks to Henning Glawe for help chasing this down.) Made lease-file reader bullet-proof. Added -D option, suggested by Peter Fichtner. release 1.18 Added round-robin DNS for names which have more than one address. In this case all the addresses will be returned, as before, but the order will change on each query. Remove stray tolower() and isalnum() calls missed in last release to complete LOCALE independence. Allow port numbers in source-address specifications. For hostnames without a domain part which don't get forwarded because -D is in effect, return NXDOMAIN not an empty reply. Add code to return the software version in response to the correct magic query in the same way as BIND. Use "dig version.bind chaos txt" to make the query. Added negative caching for PTR (address to name) records. Ensure that names of the form typically used in PTR queries (ie w.x.yz.in-addr.arpa and IPv6 equivalents) get correct answers when queried as other types. It's unlikely that anyone would do this, but the change makes things pedantically correct. Taught dnsmasq to understand "bitstring" names, as these are used for PTR lookups of IPv6 addresses by some resolvers and lookup tools. Dnsmasq now understands both the ip6.int domain and the ip6.arpa domain and both nibble and bitstring formats so it should work with any client code. Standards for this stuff have flip-flopped over the last few years, leaving many different clients in their wake. See RFC2673 for details of bitstrings. Allow '_' characters in domain names: Legal characters are now [a-z][A-Z].-_ Check names read from hosts files and leases files and reject illegal ones with a message in syslog. Make empty domain names in server and address options have the special meaning "unqualified names". (unqualified names are names without any dots in them). It's now possible to do server=//1.2.3.4 and have unqualified names sent to a special nameserver. release 2.0rc1 Moved source code into src/ directory. Fixes to cure compilation breakage when HAVE_IPV6 not set, thanks to Claas Hilbrecht. BIG CHANGE: added an integrated DHCP server and removed the code to read ISC dhcp.leases. This wins in terms of ease of setup and configuration flexibility and total machine resources consumed. Re-jiged the signal handling code to remove a race condition and to be more portable. release 2.0 Thanks to David Ashworth for feedback which informed many of the fixes below. Allow hosts to be specified by client ID in dhcp-hosts options. These are now one of dhcp-host=,.... dhcp-host=id:,..... dhcp-host=id:,..... Allow dhcp-host options to specify any IP address on the DHCP-served network, not just the range available for dynamic allocation. Allow dhcp-host options for the same host with different IP addresses where the correct one will be selected for the network the host appears on. Fix parsing of --dhcp-option to allow more than one IP address and to allow text-type options. Inhibit use of --dhcp-option to send hostname DHCP options. Update the DNS with DHCP information after re-reading /etc/hosts so that any DHCP derived names which have been shadowed by now-deleted hosts entries become visible. Fix typos in dnsmasq.conf.example Fixes to Makefile(s) to help pkgsrc packaging - patch from "pancake". Add dhcp-boot option to support network boot. Check for duplicate IP addresses in dhcp-hosts lines and refuse to run if found. If allowed to remain these can provoke an infinite loop in the DHCP protocol. Attempted to rationalise the .spec files for rpm building. There are now files for Redhat, Suse and Mandrake. I hope they work OK. Fixed hard-to-reproduce crash involving use of local domains and IPv6 queries. Thanks to Roy Marples for helping to track that one down. release 2.1 Thanks to Matt Swift and Dag Wieers for many suggestions which went into this release. Tweak include files to allow compilation on FreeBSD 5 Fix unaligned access warnings on BSD/Alpha. Allow empty DHCP options, like so: dhcp-option=44 Allow single-byte DHCP options like so: dhcp-option=20,1 Allow comments on the same line as options in /etc/dnsmasq.conf Don't complain when the same name and address is allocated to a host using DHCP and /etc/hosts. Added to the example configuration the dnsmasq equivalent of the ISC dhcpd settings given in http://www.samba.org/samba/ftp/docs/textdocs/DHCP-Server-Configuration.txt Fixed long-existing strangeness in Linux IPv6 interface discovery code. The flags field in /proc/net/if_inet6 is _not_ the interface flags. Fail gracefully when getting an ENODEV error when trying to bind an IPv6 socket, rather than bailing out. Thanks to Jan Ischebeck for feedback on that. Allow the name->address mapping for static DHCP leases to be set by /etc/hosts. It's now possible to have dhcp-host=,wibble or even dhcp-host=wibble and in /etc/hosts have wibble 1.2.3.4 and for the correct thing to happen. Note that some sort of dhcp-host line is still needed, it's not possible for random host to claim an address in /etc/hosts without some explicit configuration. Make 0.0.0.0 in a dhcp-option to mean "the machine running dnsmasq". Fix lease time spec when specified in dhcp-range and not in dhcp-host, previously this was always one hour. Fix problem with setting domains as "local only". - thanks to Chris Schank. Added support for max message size DHCP option. release 2.2 Fix total lack for DHCP functionality on Linux systems with IPv6 enabled. - thanks to Jonathon Hudson for spotting that. Move default config file under FreeBSD - patch from Steven Honson release 2.3 Fix "install" makefile target. (reported by Rob Stevens) Ensure that "local=/domain/" flag is obeyed for all queries on a domain, not just A and AAAA. (Reported by Peter Fichtner.) Handle DHCPDECLINE messages and provide an error message in DHCPNAK messages. Add "domain" setting example to dnsmasq.conf.example. Thanks to K P Kirchdorfer for spotting that it was missing. Subtle change to the DHCPREQUEST handling code to work around a bug in the DHCP client in HP Jetdirect printers. Thanks to Marko Stolle for finding this problem. Return DHCP T1 and T2 times, with "fuzz" to desynchronise lease renewals, as specified in the RFC. Ensure that the END option is always present in DHCP packets , even if the packet is too small to fit all the requested options. Handle larger-than-default DHCP packets if required, up to the ethernet MTU. Fix a couple of places where the return code from malloc() was not checked. Cope with a machine taking a DHCP lease and then moving network so that the lease address is no longer valid. The DHCP server will now work via a BOOTP relay - remote networks are configured with the dhcp-range option the same as directly connected ones, but they need an additional netmask parameter. Eg --dhcp-range=192.168.4.10,192.168.4.50,255.255,255.0 will enable DHCP service via a BOOTP relay on the 192.168.4.0 network. Add a limit on the number of available DHCP leases, otherwise the daemon could be DOSed by a malicious host. The default is 150, but it can be changed by the dhcp-lease-max option. Fixed compilation on OpenBSD (thanks to Frederic Brodbeck for help with that.) Reworked the DHCP network handling code for two good effects: (1) The limit of one network only for DHCP on FreeBSD is now gone, (2) The DHCP server copes with dynamically created interfaces. The one-interface limitation remains for OpenBSD, which is missing extensions to the socket API which have been in Linux since version 2.2 and FreeBSD since version 4.8. Reworked the DNS network code to also cope with dynamically created interfaces. dnsmasq will now listen to the wildcard address and port 53 by default, so if no --interface or --address options are given it will handle dynamically created interfaces. The old behaviour can be restored with --bind-interfaces for people running BIND on one interface and dnsmasq on another. Note that --interface and --address options still work, but the filtering is done by dnsmasq, rather then the kernel. This works on Linux, and FreeBSD>=5.0. On systems which don't support the required API extensions, the old behaviour is used, just as if --bind-interfaces had been set. Allow IPv6 support to be disabled at compile time. To do that, add -DNO_IPV6 to the CFLAGS. Thanks to Oleg I. Vdovikin for the suggestion to do that. Add ability to set DHCP options per network. This is done by giving a network an identifier like this: dhcp-range=red-net,192.168.0.10,192.168.0.50 and then labeling options intended for that network only like this: dhcp-option=red-net,6,1.1.1.1 Thanks to Oleg Vdovikin for arguing that one through. Made errors in the configuration file non-fatal: dnsmasq will now complain bitterly, but continue. Added --read-ethers option, to allow dnsmasq to pull static DHCP information from that file. Thanks to Andi Cambeis for that suggestion. Added HAVE_BROKEN_RTC compilation option to support embedded systems without a stable RTC. Oleg Vdovikin helped work out how to make that work. release 2.4 Fixed inability to start when the lease file doesn't already exist. Thanks to Dag Wieers for reporting that. Fixed problem were dhcp-host configuration options did not play well with entries in /etc/ethers for the same host. Thanks again to Dag Wieers. Tweaked DHCP code to favour moving to a newly-configured static IP address rather than an old lease when doing DHCP allocation. Added --alias configuration option. This provides IPv4 rewrite facilities like Cisco "DNS doctoring". Suggested by Chad Skeeters. Fixed bug in /etc/ethers parsing code triggered by tab characters. Kudos to Dag Wieers for helping to nail that one. Added "bind-interfaces" option correctly. release 2.5 Made "where are we allocating addresses?" code in DHCP server cope with requests via a relay which is on a directly connected network for which there is not a configured netmask. This strange state of affairs occurs with win4lin. Thanks to Alex Melt and Jim Horner for bug reports and testing with this. Fixed trivial-but-irritating missing #include which broke compilation on *BSD. Force --bind-interfaces if IP-aliased interface specifications are used, since the sockets API provides no other sane way to determine which alias of an interface a packet was sent to. Thanks to Javier Kohen for the bug report. release 2.6 Support Token Ring DHCP. Thanks to Dag Wieers for help testing. Note that Token ring support only works on Linux currently. Fix compilation on MacOS X. Thanks to Bernhard Ehlers for the patch. Added new "ignore" keyword for dhcp-host. "dhcp-host=11:22:33:44:55:66,ignore" will cause the DHCP server to ignore any host with the given MAC address, leaving it to other servers on the network. This also works with client-id and hostnames. Suggestion by Alex Melt. Fixed parsing of hex client IDs. Problem spotted by Peter Fichtner. Allow conf-file options in configuration file, to provide an include function. Re-read /etc/ethers on receipt of SIGHUP. Added back the ability to read ISC dhcpd lease files, by popular demand. Note that this is deprecated and for backwards compatibility only. You can get back the 4K of memory that the code occupies by undefining "HAVE_ISC_READER" in src/config.h Added ability to disable "pool" DHCP address allocation whilst leaving static leases working. The syntax is "dhcp-range=192.168.0.0,static" Thanks to Grzegorz Nosek for the suggestion. Generalized dnsmasq-rh.spec file to work on Mandrake too, and removed dnsmasq-mdk.spec. Thanks to Doug Keller. Allow DHCP options which are tied to specific static leases in the same way as to specific networks. Generalised the dhcp-option parser a bit to allow hex strings as parameters. This is now legal: dhcp-option=128,e4:45:74:68:00:00 Inspired by a patch from Joel Nordell. Changed the semantics of argument-less dhcp-options for the default-setting ones, ie 1, 3, 6 and 28. Now, doing eg, dhcp-option=3 stops dnsmasq from sending a default router option at all. Thanks to Scott Emmons for pointing out that this is useful. Fixed dnsmasq.conf parsing bug which interpreted port numbers in server= lines as a comment. To start a comment, a '#' character must now be a the start of a line or preceded by whitespace. Thanks to Christian Haggstrom for the bug report. release 2.7 Allow the dhcp-host specification of id:* which makes dnsmasq ignore any client-id. This is useful to ensure that a dual-boot machine sees the same lease when one OS gives a client-id and the other doesn't. It's also useful when PXE boot DHCP does not use client IDs but the OS it boots does. Thanks to Grzegorz Nosek for suggesting this enhancement. No longer assume that ciaddr is zero in received DHCPDISCOVER messages, just for security against broken clients. Set default of siaddr field to the address of the machine running dnsmasq when not explicitly set using dhcp-boot option. This is the ISC dhcpd behaviour. Send T1 and T2 options in DHCPOFFER packets. This is required by the DHCP client in some JetDirect printers. Thanks to Paul Mattal for work on this. Fixed bug with DHCP on OpenBSD reported by Dominique Jacquel. The code which added loopback interfaces to the list was confusing the DHCP code, which expected one interface only. Solved by adding loopback interfaces to address list instead. Add dhcp-vendorclass option to allow options to be sent only to certain classes of clients. Tweaked option search code so that if a netid-qualified option is used, any unqualified option is ignored. Changed the method of picking new dynamic IP addresses. This used to use the next consecutive address as long it was free, now it uses a hash from the client hardware address. This reduces the amount of address movement for clients which let their lease expire and allows consecutive DHCPOFFERS to the same host to (almost always) be for the same address, without storing state before a lease is granted. Tweaked option handling code to return all possible options rather than none when DHCP "requested options" field is missing. This fixes interoperability with ancient IBM LANMAN DHCP clients. Thanks to Jim Louvau for help with this. release 2.8 Pad DHCP packets to a minimum size of 300 bytes. This fixes interoperability problems with the Linux in-kernel DHCP/BOOTP client. Thanks to Richard Musil for diagnosing this and supplying a patch. Fixed option-parsing bug and potential memory leak. Patch from Richard Musil. Improved vendor class configuration and added user class configuration. Specifically: (1) options are matched on the netids from dhcp-range, dhcp-host, vendor class and user class(es). Multiple net-ids are allowed and options are searched on them all. (2) matches against vendor class and user class are now on a substring, if the given string is a substring of the vendor/user class, then a match occurs. Thanks again to Richard Musil for prompting this. Make "#" match any domain on --address and --server flags. --address=/#/1.2.3.4 will return 1.2.3.4 for _any_ domain not otherwise matched. Of course --server=/#/1.2.3.4 is exactly equivalent to --server=1.2.3.4. Special request from Josh Howlett. Fixed a nasty bug which would cause dnsmasq to lose track of leases for hosts which had a --dhcp-host flag without a name specification. The mechanism for this was that the hostname could get erroneously set as a zero-length string and then written to the leases file as a mal-formed line. Restarting dnsmasq would then lose the lease. Alex Hermann's work helped chase down this problem. Add checks against DHCP clients which return zero-length hostnames. This avoids the potential lease-loss problems referred to above. Also, if a client sends a hostname when it creates a lease but subsequently sends no or a zero-length hostname whilst renewing, continue to use the existing hostname, don't wipe it out. Tweaked option parsing to flag some parameter errors. release 2.9 Fixed interface filter code for two effects: 1) Fixed bug where queries sent via loopback interface but to the address of another interface were ignored unless the loopback interface was explicitly configured. 2) on OpenBSD failure to configure one interface now causes a fatal error on startup rather than a huge stream of log messages. Thanks to Erik Jan Tromp for finding that bug. Changed server selection strategy to improve performance when there are many available servers and some are broken. The new algorithm is to pick as before for the first try, but if a query is retried, to send to all available servers in parallel. The first one to reply then becomes preferred for the next query. This should improve reliability without generating significant extra upstream load. Fixed breakage of special servers/addresses for unqualified domains introduced in version 2.8 Allow fallback to "bind-interfaces" at runtime: Some versions of *BSD seem to have enough stuff in the header files to build but no kernel support. Also now log if "bind-interfaces" is forced on. Log replies from upstream servers which refuse to do recursion - dnsmasq is not a recursive nameserver and relies on upstream servers to do the recursion, this flags a configuration error. Disable client-id matching for hosts whose MAC address is read from /etc/ethers. Patch from Oleg I. Vdovikin. Extended --mx-host flag to allow arbitrary targets for MX records, suggested by Moritz Bunkus. Fixed build under NetBSD 2.0 - thanks to Felix Deichmann for the patch. Deal correctly with repeated addresses in /etc/hosts. The first name found is now returned for reverse lookups, rather than all of them. Add back fatal errors when nonexistent interfaces or interface addresses are given but only in "bind-interfaces" mode. Principle of least surprise applies. Allow # as the argument to --domain, meaning "read the domain from the first search directive in /etc.resolv.conf". Feature suggested by Evan Jones. release 2.10 Allow --query-port to be set to a low port by creating and binding the socket before dropping root. (Suggestion from Jamie Lokier) Support TCP queries. It turned out to be possible to do this with a couple of hundred lines of code, once I knew how. The executable size went up by a few K on i386. There are a few limitations: data obtained via TCP is not cached, and dynamically-created interfaces may break under certain circumstances. Source-address or query-port specifications are ignored for TCP. NAK attempts to renew a DHCP lease where the DHCP range has changed and the lease is no longer in the allowed range. Jamie Lokier pointed out this bug. NAK attempts to renew a pool DHCP lease when a statically allocated address has become available, forcing a host to move to its allocated address. Lots of people have suggested this change and been rebuffed (they know who they are) the straws that broke the camel's back were Tim Cutts and Jamie Lokier. Remove any nameserver records from answers which are modified by --alias flags. If the answer is modified, it cannot any longer be authoritative. Change behaviour of "bogus-priv" option to return NXDOMAIN rather than a PTR record with the dotted-quad address as name. The new behaviour doesn't provoke tcpwrappers like the old behavior did. Added a patch for the Suse rpm. That changes the default group to one suitable for Suse and disables inclusion of the ISC lease-file reader code. Thanks to Andy Cambeis for his ongoing work on Suse packaging. Support forwarding of EDNS.0 The maximum UDP packet size defaults to 1280, but may be changed with the --edns-packet-max option. Detect queries with the do bit set and always forward them, since DNSSEC records are not cached. This behaviour is required to make DNSSECbis work properly though dnsmasq. Thanks to Simon Josefsson for help with this. Move default config file location under OpenBSD from /usr/local/etc/dnsmasq.conf to /etc/dnsmasq.conf. Bug report from Jonathan Weiss. Use a lease with matching MAC address for a host which doesn't present a client-id, even if there was a client ID at some point in the past. This reduces surprises when changing DHCP clients, adding id:* to a host, and from the semantics change of /etc/ethers in 2.9. Thanks to Bernard Sammer for finding that. Added a "contrib" directory and in it the dnslist utility, from Thomas Tuttle. Fixed "fail to start up" problems under Linux with IPv6 enabled. It's not clear that these were an issue in released versions, but they manifested themselves when TCP support was added. Thanks to Michael Hamilton for assistance with this. version 2.11 Fixed DHCP problem which could result in two leases in the database with the same address. This looked much more alarming then it was, since it could only happen when a machine changes MAC address but kept the same name. The old lease would persist until it timed out but things would still work OK. Check that IP addresses in all dhcp-host directives are unique and die horribly if they are not, since otherwise endless protocol loops can occur. Use IPV6_RECVPKTINFO as socket option rather than IPV6_PKTINFO where available. This keeps late-model FreeBSD happy. Set source interface when replying to IPv6 UDP queries. This is needed to cope with link-local addresses. version 2.12 Added extra checks to ensure that DHCP created DNS entries cannot generate multiple DNS address->name entries. Thanks to Stefan Monnier for finding the exact set of configuration options which could create this. Don't set the the filterwin2k option in the example config file and add warnings that is breaks Kerberos. Thanks to Simon Josefsson and Timothy Folks for pointing that out. Log types of incoming queries as well as source and domain. Log NODATA replies generated as a result of the filterwin2k option. version 2.13 Fixed crash with un-named DHCP hosts introduced in 2.12. Thanks to Nicolo Wojewoda and Gregory Gathy for bug reports. version 2.14 Fix DHCP network detection for hosts which talk via a relay. This makes lease renewal for such hosts work correctly. Support RFC3011 subnet selectors in the DHCP server. Fix DHCP code to generate RFC-compliant responses to hosts in the INIT-REBOOT state. In the DHCP server, set the receive buffer size on the transmit-only packet socket to zero, to avoid waste of kernel buffers. Fix DHCP address allocation code to use the whole of the DHCP range, including the start and end addresses. Attempt an ICMP "ping" on new addresses before allocating them to leases, to avoid allocating addresses which are in use. Handle rfc951 BOOTP as well as DHCP for hosts which have MAC address to IP address mapping defined. Fix compilation under MacOS X. Thanks to Chris Tomlinson. Fix compilation under NetBSD. Thanks to Felix Deichmann. Added "keep-in-foreground" option. Thanks to Sean MacLennan for the patch. version 2.15 Fixed NXDOMAIN/NODATA confusion for locally known names. We now return a NODATA response for names which are locally known. Now a query for (eg AAAA or MX) for a name with an IPv4 address in /etc/hosts which fails upstream will generate a NODATA response. Note that the query is still tried upstream, but a NXDOMAIN reply gets converted to NODATA. Thanks to Eric de Thouars, Eric Spakman and Mike Mestnik for bug reports/testing. Allow multiple dhcp-ranges within the same network. The original intention was that there would be a dhcp-range option for each network served, but there's no real reason not to allow discontinuous ranges within a network so this release adds support for that. Check for dhcp-ranges which are inconsistent with their netmask, and generate errors or warnings. Improve error messages when there are problems with configuration. version 2.16 Fixed typo in OpenBSD-only code which stopped compilation under that OS. Chris Weinhaupl gets credit for reporting this. Added dhcp-authoritative option which restores non-RFC compliant but desirable behaviour of pre-2.14 versions and avoids long timeouts while DHCP clients try to renew leases which are unknown to dnsmasq. Thanks to John Mastwijk for help with this. Added support to the DHCP option code to allow RFC-3397 domain search DHCP option (119) to be sent. Set NONBLOCK on all listening sockets to workaround non-POSIX compliance in Linux 2.4 and 2.6. This fixes rare hangs which occurred when corrupted packets were received. Thanks to Joris van Rantwijk for chasing that down. Updated config.h for NetBSD. Thanks to Martin Lambers. Do a better job of distinguishing between retransmissions and new queries when forwarding. This fixes a bug triggered by the polipo web cache which sends A and AAAA queries both with the same transaction-ID. Thanks to Joachim Berdal Haga and Juliusz Chroboczek for help with this. Rewrote cache code to store CNAMES, rather then chasing them before storage. This eliminates bad situations when clients get inconsistent views depending on if data comes from the cache. Allow for more than one --addn-hosts flag. Clarify logged message when a DHCP lease clashes with an /etc/hosts entry. Thanks to Mat Swift for the suggestion. Added dynamic-dnsmasq from Peter Willis to the contrib section. version 2.17 Correctly deduce the size of numeric dhcp-options, rather than making wild guesses. Also cope with negative values. Fixed use of C library reserved symbol "index" which broke under certain combinations of library and compiler. Make bind-interfaces work for IPv6 interfaces too. Warn if an interface is given for listening which doesn't currently exist when not in bind-interfaces mode. (This is already a fatal error when bind-interfaces is set.) Allow the --interface and --except-interface options to take a comma-separated list of interfaces. Tweak --dhcp-userclass matching code to work with the ISC dhclient which violates RFC3004 unless its configuration is very warped. Thanks to Cedric Duval for the bug report. Allow more than one network-id tag in a dhcp-option. All the tags must match to enable the option. Added dhcp-ignore option to disable classes of hosts based on network-id tags. Also allow BOOTP options to be controlled by network tags. Fill in sname, file and siaddr fields in replies to DHCPINFORM messages. Don't send NAK replies to DHCPREQUEST packets for disabled clients. Credit to Cedric Duval for spotting this. Fix rare crash associated with long DNS names and CNAME records. Thanks to Holger Hoffstatte and especially Steve Grecni for help chasing that one down. version 2.18 Reworked the Linux interface discovery code (again) to cope with interfaces which have only IPv6 addresses and interfaces with more than one IPv6 address. Thanks to Martin Pels for help with that. Fix problems which occurred when more than one dhcp-range was specified in the same subnet: sometimes parameters (lease time, network-id tag) from the wrong one would be used. Thanks to Rory Campbell-Lange for the bug report. Reset cache statistics when clearing the cache. Enable long command line options on FreeBSD when the C library supports them. version 2.19 Tweaked the Linux-only interface discovery code to cope with interface-indexes larger than 8 bits in /proc/net/if_inet6. This only affects Linux, obviously. Thanks to Richard Atterer for the bug report. Check for under-length option fields in DHCP packets, a zero length client-id, in particular, could seriously confuse dnsmasq 'till now. Thanks to Will Murname for help with that. If a DHCP-allocated address has an associated name in /etc/hosts, and the client does not provide a hostname parameter and there is no hostname in a matching dhcp-host option, send the /etc/hosts name as the hostname in the DHCP lease. Thanks to Will Murname for the suggestion. version 2.20 Allow more than one instance of dnsmasq to run on a machine, each providing DHCP service on a different interface, provided that --bind-interfaces is set. This configuration used to work, but regressed in version 2.14 Fix compilation on Mac OS X. Thanks to Kevin Bullock. Protect against overlong names and overlong labels in configuration and from DHCP. Fix interesting corner case in CNAME handling. This occurs when a CNAME has a target which "shadowed" by a name in /etc/hosts or from DHCP. Resolving the CNAME would sneak the upstream value of the CNAME's target into the cache, alongside the local value. Now that doesn't happen, though resolving the CNAME still gives the unshadowed value. This is arguably wrong but rather difficult to fix. The main thing is to avoid getting strange results for the target due to the cache pollution when resolving the CNAME. Thanks to Pierre Habouzit for exploring the corner and submitting a very clear bug report. Fix subtle bug in the DNS packet parsing code. It's almost impossible to describe this succinctly, but the one known manifestation is the inability to cache the A record for www.apple.com. Thanks to Bob Alexander for spotting that. Support SRV records. Thanks to Robert Kean for the patches for this. Fixed sign confusion in the vendor-id matching code which could cause crashes sometimes. (Credit to Mark Wiater for help finding this.) Added the ability to match the netid tag in a dhcp-range. Combined with the ability to have multiple ranges in a single subnet, this provides a means to segregate hosts on different address ranges based on vendorclass or userclass. Thanks to Mark Wiater for prompting this enhancement. Added preference values for MX records. Added the --localise-queries option. version 2.21 Improve handling of SERVFAIL and REFUSED errors. Receiving these now initiates search for a new good server, and a server which returns them is not a candidate as a good server. Thanks to Istvan Varadi for pointing out the problem. Tweak the time code in BROKEN_RTC mode. Sanity check lease times in dhcp-range and dhcp-host configurations and force them to be at least two minutes (120s) leases shorter than a minute confuse some clients, notably Apple MacOS X. Rory Campbell-Lange found this problem. Only warn once about an upstream server which is refusing to do recursive queries. Fix DHCP address allocation problem when netid tags are in use. Thanks to Will Murname for the bug report and subsequent testing. Add an additional data section to the reply for MX and SRV queries. Add support for DNS TXT records. Thanks to Robert Kean and John Hampton for prompts and testing of these. Apply address rewriting to records in the additional data section of DNS packets. This makes things like MX records work with the alias function. Thanks to Chad Skeeters for pointing out the need for this. Added support for quoted strings in config file. Detect and defeat cache-poisoning attacks which attempt to send (malicious) answers to questions we didn't send. These are ignored now even if the attacker manages to guess a random query-id. Provide DHCP support for interfaces with multiple IP addresses or aliases. This in only enabled under Linux. See the FAQ entry for details. Revisit the MAC-address and client-id matching code to provide saner behaviour with PXE boots, where some requests have a client-id and some don't. Fixed off-by-one buffer overflow in lease file reading code. Thanks to Rob Holland for the bug report. Added wildcard matching for MAC addresses in dhcp-host options. A sensible suggestion by Nathaniel McCallum. version 2.22 Fixed build problems on (many) systems with older libc headers where is required before . Enabled HAVE_RTNETLINK under uclibc now that this fix is in place. Added support for encapsulated vendor-class-specific DHCP options. Thanks to Eric Shattow for help with this. Fix regression in 2.21 which broke commas in filenames and corrupted argv. Thanks to Eric Scott for the bugreport. Fixed stupid thinko which caused dnsmasq to wedge during startup with certain MX-record options. Another 2.21 regression. Fixed broken-ness when reading /etc/ethers. 2.21 broke this too. Fixed wedge with certain DHCP options. Yet another 2.21 regression. Rob Holland and Roy Marples chased this one down. version 2.23 Added a check to ensure that there cannot be more than one dhcp-host option for any one IP address, even if the addresses are assigned indirectly via a hostname and /etc/hosts. Include a "server identifier" in DHCPNAK replies, as required by RFC2131. Added method support for DBus (http://www.freedesktop.org/Software/dbus) This is a superior way to re-configure dnsmasq on-the-fly with different upstream nameservers, as the host moves between networks. DBus support must be enabled in src/config.h and should be considered experimental at this point. See DBus-interface for the specification of the DBus method calls supported. Added information to the FAQ about setting the DNS domain in windows XP and Mac OS X, thanks to Rick Hull. Added sanity check to resolv.conf polling code to cope with backwards-moving clocks. Thanks to Leonardo Canducci for help with this. Handle so-called "A-for-A" queries, which are queries for the address associated with a name which is already a dotted-quad address. These should be handled by the resolver code, but sometimes aren't and there's no point in forwarding them. Added "no-dhcp-interface" option to disable DHCP service on an interface, whilst still providing DNS. Fix format-string problem - config file names get passed to fprintf as a format string, so % characters could cause crashes. Thanks to Rob Holland for sleuthing that one. Fixed multiple compiler warnings from gcc 4. Thanks to Tim Cutts for the report. Send the hostname option on DHCP offer messages as well as DHCP ack messages. This is required by the Rio Digital Audio Receiver. Thanks to Ron Frederick for the patch. Add 'd' (for day) as a possible time multiplier in lease time specifications. Thanks to Michael Deegan. Make quoting suppress recognition of IP addresses, so dhcp-option=66,1.2.3.4 now means something different to dhcp-option=66,"1.2.3.4", which sets the option to a string value. Thanks to Brian Macauley for the bug report. Fixed the option parsing code to avoid segfaults from some invalid configurations. Thanks to Wookey for spotting that one. Provide information about which compile-time options were selected, both in the log at startup and as part of the output from dnsmasq --version. Thanks to Dirk Schenkewitz for the suggestion. Fix pathological behaviour when a broken client keeps sending DHCPDISCOVER messages repeatedly and fast. Because dealing with each of these takes a few seconds, (because of the ping) then a queue of DHCP packets could build up. Now, the results of a ping test are assumed to be valid for 30 seconds, so repeated waits are not required. Thanks to Luca Landi for finding this. Allow DHCPINFORM requests without hardware address information. These are generated by some browsers, looking for proxy information. Thanks to Stanley Jaddoe for the bug report on that. Add support of the "client FQDN" DHCP option. If present, this is used to allow the client to tell dnsmasq its name, in preference to (mis)using the hostname option. See http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/\ draft-ietf-dhc-fqdn-option-10.txt for details of the draft spec. Added startup scripts for MacOS X Tiger/Panther to the contrib collection. Thanks to Tim Cutts. Tweak DHCP network selection so that clients which turn up on our network in REBINDING state and with a lease for a foreign network will get a NAK response. Thanks to Dan Shechter for work on this and an initial patch and thanks to Gyorgy Farkas for further testing. Fix DNS query forwarding for empty queries and forward queries even when the recursion-desired bit is clear. This allows "dig +trace" to work. Problem report from Uwe Gansert. Added "const" declarations where appropriate, thanks to Andreas Mohr for the patch. Added --bootp-dynamic option and associated functionality. Thanks to Josef Wolf for the suggestion. version 2.24 Updated contrib/openvpn/dnsmasq.patch from Joseph Tate. Tweaked DHCP NAK code, a DHCP NAK is now unicast as a fallback in cases where a broadcast is futile: namely in response to a unicast REQUEST from a non-local network which was not sent via a relay. Slightly changed the semantics of domain matching in --server and --address configs. --server=/domain.com/ still matches domain.com and sub.domain.com but does not now match newdomain.com The semantics of --server=/.domain.com/ are unchanged. Thanks to Chris Blaise for the patch. Added backwards-compatible internationalisation support. The existing make targets, (all, dnsmasq, install) work as before. New ones (all-i18n, and install-i18n) add gettext. The translations live in po/ There are not too many strings, so if anybody can provide translations (and for the manpage....) please send them in. Tweak behaviour on receipt of REFUSED or SERVFAIL rcodes, now the query gets retried on all servers before returning the error to the source of the query. Thanks to Javier Kohen for the report. Added Polish translation - thanks to Tomasz Sochanski. Changed default manpage install location from /usr/man to /usr/share/man Added Spanish translation - thanks to Christopher Chatham. Log a warning when a DHCP packet is truncated due to lack of space. (Thanks to Michael Welle for the prompt to do this.) Added French translation - thanks to Lionel Tricon. Added Indonesian translation - thanks to Salman AS. Tweaked the netlink code to cope with interface broadcast address not set, or set to 0.0.0.0. Fixed problem assigning fixed addresses to hosts when more than one dhcp-range is available. Thanks to Sorin Panca for help chasing this down. Added more explicit error messages to the hosts file and ethers file reading code. Markus Kaiserswerth suffered to make this happen. Ensure that a hostname supplied by a DHCP client can never override one configured on the server. Previously, any host claiming a name would be given it, even if that over-rode a dhcp-host declaration, leading to potentially confusing situations. Added Slackware package-build stuff into contrib/ The i18n effort broke the current scripts, and working ones were needed for testing, so they ended up here rather than make Pat re-invent the wheel. Added Romanian translation, thanks to Sorin Panca for that. version 2.25 Fixed RedHat spec file for FC4 - thanks to Werner Hoelzl and Andrew Bird. Fixed Suse spec file - thanks to Steven Springl. Fixed DHCP bug when two distinct subnets are on the same physical interface. Thanks to Pawel Zawora for finding this and suggesting the fix. Added logging to make it explicit when dnsmasq falls back from using RT-netlink sockets to the old ioctl API for getting information about interfaces. Doing this completely silently made remote debugging hard. Merged uclibc build fixes from the OpenWRT package into src/config.h Added Norwegian translation - thanks to Jan Erik Askildt. version 2.26 Fixed SuSe rpm patch problem - thanks to Steven Springl. Fixed crash when attempting to send a DHCP NAK to a host which believes it has a lease on an unknown network. Thanks to Lutz Pressler for the bug report and patch. version 2.27 Tweaked DHCP behaviour when a client attempts to renew a lease which dnsmasq doesn't know about. Previously that would always result in a DHCPNAK. Now, in dhcp-authoritative mode, the lease will be created, if it's legal. This makes dnsmasq work better if the lease database is lost, for example on an OpenWRT system which reboots. Thanks to Stephen Rose for work on this. Added the ability to support RFC-3442 style destination descriptors in dhcp-options. This makes classless static routes easy to do, eg dhcp-option=121,192.168.1.0/24,1.2.3.4 Added error-checking to the code which writes the lease file. If this fails for any reason, an error is logged, and a retry occurs after one minute. This should improve things eg when a filesystem is full. Thanks to Jens Holze for the bug report. Fixed breakage of the "/#/ matches any domain" facility which happened in 2.24. Thanks to Peter Surda for the bug report. Use "size_t" and "ssize_t" types where appropriate in the code. Fix buggy CNAME handling in mixed IPv4 and IPv6 queries. Thanks to Andreas Pelme for help finding that. Added some code to attempt to re-transmit DNS queries when a network interface comes up. 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. This function is only active when netlink support is compiled in, and therefore only under Linux. Thanks to Jean Wolter for help with this. Tweaked the DHCP tag-matching code to work correctly with NOT-tag conditions. Thanks to Lutz Pressler for finding the bug. Generalised netid-tag matching in dhcp-range statements to allow more than one tag. Added --dhcp-mac to do MAC address matching in the same way as vendorclass and userclass matching. A good suggestion from Lutz Pressler. Add workaround for buggy early Microsoft DHCP clients which need zero-termination in string options. Thanks to Fabiano Pires for help with this. Generalised the DHCP code to cope with any hardware address type, at least on Linux. *BSD is still limited to ethernet only. version 2.28 Eliminated all raw network access when running on Linux. All DHCP network activity now goes through the IP stack. Packet sockets are no longer required. Apart from being a neat hack, this should also allow DHCP over IPsec to work better. On *BSD and OS X, the old method of raw net access through BPF is retained. Simplified build options. Networking is now slimmed down to a choice of "linux" or "other". Netlink is always used under Linux. Since netlink has been available since 2.2 and non-optional in an IPv4-configured kernel since 2.4, and the dnsmasq netlink code is now well tested, this should work out fine. Removed decayed build support for libc5 and Solaris. Removed pselect code: use a pipe for race-free signal handling instead, as this works everywhere. No longer enable the ISC leasefile reading code in the distributed sources. I doubt there are many people left using this 1.x compatibility code. Those that are will have to explicitly enable it in src/config.h. Don't send the "DHCP maximum message size" option, even if requested. RFC2131 says this is a "MUST NOT". Support larger-than-minimum DHCP message. Dnsmasq is now happy to get larger than 576-byte DHCP messages, and will return large messages, if permitted by the "maximum message size" option of the message to which it is replying. There's now an arbitrary sanity limit of 16384 bytes. Added --no-ping option. This fixes an RFC2131 "SHOULD". Building on the 2.27 MAC-address changes, allow clients to provide no MAC address at all, relying on the client-id as a unique identifier. This should make things like DHCP for USB come easier. Fixed regression in netlink code under 2.2.x kernels which occurred in 2.27. Erik Jan Tromp is the vintage kernel fan who found this. P.S. It looks like this "netlink bind: permission denied" problem occurred in kernels at least as late a 2.4.18. Good information from Alain Richoux. Added a warning when it's impossible to give a host its configured address because the address is leased elsewhere. A sensible suggestion from Mircea Bardac. Added minimal support for RFC 3046 DHCP relay agent-id options. The DHCP server now echoes these back to the relay, as required by the RFC. Also, RFC 3527 link selection sub-options are honoured. Set the process "dumpable" flag when running in debug mode: this makes getting core dumps from root processes much easier. Fixed one-byte buffer overflow which seems to only cause problems when dnsmasq is linked with uclibc. Thanks to Eric House and Eric Spakman for help in chasing this down. Tolerate configuration screwups which lead to the DHCP server attempting to allocate its own address to a client; eg setting the whole subnet range as a DHCP range. Addresses in use by the server are now excluded from use by clients. Did some thinking about HAVE_BROKEN_RTC mode, and made it much simpler and better. The key is to just keep lease lengths in the lease file. Since these normally never change, even as the lease is renewed, the lease file never needs to change except when machines arrive on the network or leave. This eliminates the code for timed writes, and reduces the amount of wear on a flash filesystem to the absolute minimum. Also re-did the basic time function in this mode to use the portable times(), rather than parsing /proc/uptime. Believe the source port number when replying to unicast DHCP requests and DHCP requests via a relay, instead of always using the standard ports. This will allow relays on non-standard ports and DHCPINFORM from unprivileged ports to work. The source port sent by unconfigured clients is still ignored, since this may be unreliable. This means that a DHCP client must use the standard port to do full configuration. version 2.29 Fixed compilation on OpenBSD (thanks to Tom Hensel for the report). Fixed false "no interface" errors when --bind-interfaces is set along with --interface=lo or --listen-address. Thanks to Paul Wise for the report. Updated patch for SuSE rpm. Thanks to Steven Springl. It turns out that there are some Linux kernel configurations which make using the capability system impossible. If this situation occurs then continue, running as root, and log a warning. Thanks to Scott Wehrenberg for help tracking this down. version 2.30 Fixed crash when a DHCP client requested a broadcast reply. This problem was introduced in version 2.28. Thanks to Sandra Dekkers for the bug report. version 2.31 Added --dhcp-script option. There have been calls for this for a long time from many good people. Fabio Muzzi gets the prize for finally convincing me. Added example dbus config file and moved dbus stuff into its own directory. Removed horribly outdated Redhat RPM build files. These are obsolete now that dnsmasq in in Fedora extras. Thanks to Patrick "Jima" Laughton, the Fedora package maintainer. Added workaround for Linux kernel bug. This manifests itself as failure of DHCP on kernels with "support for classical IP over ATM" configured. That includes most Debian kernel packages. Many thanks to A. Costa and Benjamin Kudria for their huge efforts in chasing this down. Force-kill child processes when dnsmasq is sent a sigterm, otherwise an unclosed TCP connection could keep dnsmasq hanging round for a few minutes. Tweaked config.h logic for uclibc build. It will now pick up MMU and IPV6 status correctly on every system I tested. version 2.32 Attempt a better job of replacing previous configuration when re-reading /etc/hosts and /etc/ethers. SIGHUP is still not identical to a restart under all circumstances, but it is for the common case of name->MAC address in /etc/ethers and name->IP address in /etc/hosts. Fall back to broadcast for DHCP to an unconfigured client when the MAC address size is greater than 14 bytes. Fix problem in 2.28-onwards releases which breaks DNS on Mac OS X. Thanks to Doug Fields for the bug report and testing. Added fix to allow compilation on c89-only compilers. Thanks to John Mastwijk for the patch. Tweak resolv file polling code to work better if there is a race between updating the mtime and file contents. This is not normally a problem, but it can be on systems which replace nameservers whilst active. The code now continues to read resolv.conf until it gets at least one usable server. Thanks to Holger Mauermann for help with this. If a client DECLINEs an address which is allocated to it via dhcp-host or /etc/hosts, lock that address out of use for ten minutes, instead of forever, and log when it's not being used because of the lock-out. This should provide less surprising behaviour when a configured address can't be used. Thanks to Peter Surda and Heinz Deinhart for input on this. Fixed *BSD DHCP breakage with only some arches/compilers, depending on structure padding rules. Thanks to Jeb Campbell and Tom Hensel for help with this. Added --conf-dir option. Suggestion from Aaron Tygart. Applied patch from Brent Cook which allows netids in dhcp-option configuration lines to be prefixed by "net:". This is not required by the syntax, but it is consistent with other configuration items. Added --log-facility option. Suggestion from Fabio Muzzi. Major update to Spanish translation. Many thanks to Chris Chatham. Fixed gcc-4.1 strict-alias compilation warning. version 2.33 Remove bash-specific shellcode from the Makefile. Fix breakage with some DHCP relay implementations which was introduced in 2.28. Believing the source port in DHCP requests and sending the reply there is sometimes a bad thing to do, so I've reverted to always sending to the relay on port 68. Thanks to Daniel Hamlin and Alex (alde) for bug reports on this. Moved the SuSe packaging files to contrib. I will no longer attempt to maintain this in the source tarball. It will be done externally, in the same way as packaging for other distros. Suse packages are available from ftp://ftp.suse.com/pub/people/ug/ Merged patch from Gentoo to honour $LDFLAGS environment. Fix bug in resolv.conf processing when more than one file is being checked. Add --dns-forward-max option. Warn if --resolv-file flags are ignored because of --no-resolv. Thanks to Martin F Krafft for spotting this one. Add --leasefile-ro option which allows the use of an external lease database. Many thanks to Steve Horbachuk for assistance developing this feature. Provide extra information to lease-change script via its environment. If the host has a client-id, then DNSMASQ_CLIENT_ID will be set. Either the lease length (in DNSMASQ_LEASE_LENGTH) or lease expiry time (in DNSMASQ_LEASE_EXPIRES) will be set, depending on the HAVE_BROKEN_RTC compile-time option. This extra information should make it possible to maintain the lease database in external storage such as LDAP or a relational database. Note that while leasefile-ro is set, the script will be called with "old" events more often, since changes to the client-id and lease length (HAVE_BROKEN_RTC) or lease expiry time (otherwise) are now flagged. Add contrib/wrt/* which is an example implementation of an external persistent lease database for *WRT distros with the nvram command. Add contrib/wrt/dhcp_release.c which is a small utility which removes DHCP leases using DHCPRELEASE operation in the DHCP protocol. version 2.34 Tweak network-determination code for another corner case: in this case a host forced to move between dhcp-ranges on the same physical interface. Thanks to Matthias Andree. Improve handling of high DNS loads by throttling acceptance of new queries when resources are tight. This should be a better response than the "forwarding table full..." message which was logged before. Fixed intermittent infinite loop when re-reading /etc/ethers after SIGHUP. Thanks to Eldon Ziegler for the bug report. Provide extra information to the lease-change script: when a lease loses its hostname (because a new lease comes along and claims the same new), the "old" action is called with the current state of the lease, ie no name. The change is to provide the former name which the lease had in the environment variable DNSMASQ_OLD_HOSTNAME. This helps scripts which do stuff based on hostname, rather than IP address. Also provide vendor-class and user-class information to the lease-change script when a new lease is created in the DNSMASQ_VENDOR_CLASS and DNSMASQ_USER_CLASS environment variables. Suggestion from Francois-Xavier Le Bail. Run the lease change script as root, even when dnsmasq is configured to change UID to an unprivileged user. Since most uses of the lease change script need root, this allows its use whilst keeping the security advantages of running the daemon without privs. The script is invoked via a small helper process which keeps root UID, and validates all data received from the main process. To get root, an attacker would have to break dnsmasq and then break the helper through the restricted comms channel linking the two. Add contrib/port-forward/* which is a script to set up port-forwards using the DHCP lease-change script. It's possible to add a host to a config file by name, and when that host gets a DHCP lease, the script will use iptables to set up port-forwards to configured ports at the address which the host is allocated. The script also handles setting up the port-forward iptables entries after reboot, using the persistent lease database, and removing them when a host leaves and its DHCP lease expires. Fix unaligned access problem which caused wrong log messages with some clients on some architectures. Thanks to Francois-Xavier Le Bail for the bugreport. Fixed problem with DHCPRELEASE and multi-address interfaces. Enhanced contrib/wrt/dhcp_release to cope under these circumstances too. Thanks to Eldon Ziegler for input on this. Updated French translation: thanks to Gildas Le Nadan. Upgraded the name hash function in the DNS cache. Thanks to Oleg Khovayko for good work on this. Added --clear-on-reload flag. Suggestion from Johannes Stezenbach. Treat a nameserver address of 0.0.0.0 as "nothing". Erwin Cabrera spotted that specifying a nameserver as 0.0.0.0 breaks things badly; this is because the network stack treats is as "this host" and an endless loop ensues. Added Webmin module in contrib/webmin. Thanks to Neil Fisher for that. version 2.35 Generate an "old" script event when a client does a DHCPREQUEST in INIT-REBOOT or SELECTING state and the lease already exists. Supply vendor and user class information to these script calls. Added support for Dragonfly BSD to src/config.h Removed "Upgrading to 2.0" document, which is ancient history now. Tweak DHCP networking code for BSD, esp OpenBSD. Added a workaround for a bug in OpenBSD 4.0: there should finally be support for multiple interfaces under OpenBSD now. Note that no version of dnsmasq before 2.35 will work for DHCP under OpenBSD 4.0 because of a kernel bug. Thanks to Claudio Jeker, Jeb Campbell and Cristobal Palmer for help with this. Optimised the cache code for the case of large /etc/hosts. This is mainly to remove the O(n-squared) algorithm which made reading large (50000 lines) files slow, but it also takes into account the size of /etc/hosts when building hash tables, so overall performance should be better. Thanks to "koko" for pointing out the problem. version 2.36 Added --dhcp-ignore-names flag which tells dnsmasq not to use names provided by DHCP clients. Suggestion from Thomas M Steenholdt. Send netmask and broadcast address DHCP options always, even if the client doesn't request them. This makes a few odd clients work better. Added simple TFTP function, optimised for net-boot. It is now possible to net boot hosts using only dnsmasq. The TFTP server is read-only, binary-mode only, and designed to be secure; it adds about 4K to the dnsmasq binary. Support DHCP option 120, SIP servers, (RFC 3361). Both encodings are supported, so both --dhcp-option=120,192.168.2.3 and --dhcp-option=120,sip.example.net will work. Brian Candler pointed out the need for this. Allow spaces in domain names, to support DNS-SD. Add --ptr-record flag, again for DNS-SD. Thanks to Stephan Sokolow for the suggestion. Tolerate leading space on lines in the config file. Thanks to Luigi Rizzo for pointing this out. Fixed netlink.c to cope with headers from the Linux 2.6.19 kernel. Thanks to Philip Wall for the bug report. Added --dhcp-bridge option, but only to the FreeBSD build. This fixes an oddity with a particular bridged network configuration on FreeBSD. Thanks to Luigi Rizzo for the patch. Added FAQ entry about running dnsmasq in a Linux vserver. Thanks to Gildas le Nadan for the information. Fixed problem with option parsing which interpreted "/" as an address and not a string. Thanks to Luigi Rizzo for the patch. Ignore the --domain-needed flag when forwarding NS and SOA queries, since NS queries of TLDs are always legit. Marcus Better pointed out this problem. Take care to forward signed DNS requests bit-perfect, so as not to affect the validity of the signature. This should allow DDNS updates to be forwarded. version 2.37 Add better support for RFC-2855 DHCP-over-firewire and RFC -4390 DHCP-over-InfiniBand. A good suggestion from Karl Svec. Some efficiency tweaks to the cache code for very large /etc/hosts files. Should improve reverse (address->name) lookups and garbage collection. Thanks to Jan 'RedBully' Seiffert for input on this. Fix regression in 2.36 which made bogus-nxdomain and DNS caching unreliable. Thanks to Dennis DeDonatis and Jan Seiffert for bug reports. Make DHCP encapsulated vendor-class options sane. Be warned that some conceivable existing configurations using these may break, but they work in a much simpler and more logical way now. Prepending "vendor:" to an option encapsulates it in option 43, and the option is sent only if the client-supplied vendor-class substring-matches with the given client-id. Thanks to Dennis DeDonatis for help with this. Apply patch from Jan Seiffert to tidy up tftp.c Add support for overloading the filename and servername fields in DHCP packet. This gives extra option-space when these fields are not being used or with a modern client which supports moving them into options. Added a LIMITS section to the man-page, with guidance on maximum numbers of clients, file sizes and tuning. release 2.38 Fix compilation on *BSD. Thanks to Tom Hensel. Don't send length zero DHCP option 43 and cope with encapsulated options whose total length exceeds 255 octets by splitting them into multiple option 43 pieces. Avoid queries being retried forever when --strict-order is set and an upstream server returns a SERVFAIL error. Thanks to Johannes Stezenbach for spotting this. Fix BOOTP support, broken in version 2.37. Add example dhcp-options for Etherboot. Add \e (for ASCII ESCape) to the set of valid escapes in config-file strings. Added --dhcp-option-force flag and examples in the configuration file which use this to control PXELinux. Added --tftp-no-blocksize option. Set netid tag "bootp" when BOOTP (rather than DHCP) is in use. This makes it easy to customise which options are sent to BOOTP clients. (BOOTP allows only 64 octets for options, so it can be necessary to trim things.) Fix rare hang in cache code, a 2.37 regression. This probably needs an infinite DHCP lease and some bad luck to trigger. Thanks to Detlef Reichelt for bug reports and testing. release 2.39 Apply patch from Mike Baker/OpenWRT to ensure that names like "localhost." in /etc/hosts with trailing period are treated as fully-qualified. Tolerate and ignore spaces around commas in the configuration file in all circumstances. Note that this may change the meaning of a few existing config files, for instance txt-record=mydomain.com, string would have a leading space in the string before, and now will not. To get the old behaviour back, use quotes: txt-record=mydomain.com," string" /a is no longer a valid escape in quoted strings. Added symbolic DHCP option names. Instead of dhcp-option = 3, 1.2.3.4 it is now possible to do dhcp-option = option:router, 1.2.3.4 To see the list of known DHCP options, use the command "dnsmasq --help dhcp" Thanks to Luigi Rizzo for a patch and good work on this. Overhauled the log code so that logging can be asynchronous; dnsmasq then no longer blocks waiting for the syslog() library call. This is important on systems where syslog is being used to log over the network (and therefore doing DNS lookups) and syslog is using dnsmasq as its DNS server. Having dnsmasq block awaiting syslog under such circumstances can lead to syslog and dnsmasq deadlocking. The new behaviour is enabled with a new --log-async flag, which can also be used to tune the queue length. Paul Chambers found and diagnosed this trap for the unwary. He also did much testing of the solution along with Carlos Carvalho. --log-facility can now take a file-name instead of a facility name. When this is done, dnsmasq logs to the file and not via syslog. (Failures early in startup, whilst reading configuration, will still go to syslog, and syslog is used as a log-of-last-resort if the file cannot be written.) Added --log-dhcp flag. Suggestion from Carlos Carvalho. Made BINDIR, MANDIR and LOCALEDIR independently over-rideable in the makefile. Suggestion from Thomas Klausner. Added 127.0.0.0/8 and 169.254.0.0/16 to the address ranges affected by --bogus-priv. Thanks to Paul Chambers for the patch. Fixed failure of TFTP server with --listen-address. Thanks to William Dinkel for the bug report. Added --dhcp-circuitid and --dhcp-remoteid for RFC3046 relay agent data matching. Added --dhcp-subscrid for RFC3993 subscriber-id relay agent data matching. Correctly garbage-collect connections when upstream servers go away as a result of DBus transactions. Allow absolute paths for TFTP transfers even when --tftp-root is set, as long as the path matches the root, so /var/ftp/myfile is OK with tftp-root=/var/ftp. Thanks for Thomas Mizzi for the patch. Updated Spanish translation - thanks to Chris Chatham. Updated French translation - thanks to Gildas Le Nadan. Added to example conf file example of routing PTR queries for a subnet to a different nameserver. Suggestion from Jon Nicholson. Added --interface-name option. This provides a facility to add a domain name with a dynamic IP address taken from the address of a local network interface. Useful for networks with dynamic IPs. version 2.40 Make SIGUSR2 close-and-reopen the logfile when logging direct to a file. Thanks to Carlos Carvalho for suggesting this. When a logfile is created, change its ownership to the user dnsmasq will run as, don't leave it owned by root. Set a special tag, "known" for hosts which are matched by a dhcp-host or /etc/ethers line. This is especially useful to be able to do --dhcp-ignore=#known, like ISCs "deny unknown-clients". Explicitly set a umask before creating the leases file, rather than relying on whatever we inherited. The permissions are set to 644. Fix handling of fully-qualified names in --dhcp-host directives and in /etc/ethers. These are now rejected if the domain doesn't match that given by --domain, and used correctly otherwise. Before, putting a FQDN here could cause the whole FQDN to be used as hostname. Thanks to Michael Heimpold for the bug report. Massive but trivial edit to make the "daemon" variable global, instead of copying the same value around as the first argument to half the functions in the program. Updated Spanish manpage and message catalog. Thanks to Chris Chatham. Added patch for support of DNS LOC records in contrib/dns-loc. Thanks to Lorenz Schori. Fixed error in manpage: dhcp-ignore-name -> dhcp-ignore-names. Thanks to Daniel Mentz for spotting this. Use client-id as hash-seed for DHCP address allocation with Firewire and InfiniBand, as these don't supply a MAC address. Tweaked TFTP file-open code to make it behave sensibly when the filesystem changes under its feet. Added DNSMASQ_TIME_REMAINING environment variable to the lease-script. Always send replies to DHCPINFORM requests to the source of the request and not to the address in ciaddr. This allows third-party queries. Return "lease time remaining" in the reply to a DHCPINFORM request if there exists a lease for the host sending the request. Added --dhcp-hostsfile option. This gives a superset of the functionality provided by /etc/ethers. Thanks to Greg Kurtzer for the suggestion. Accept keyword "server" as a synonym for "nameserver" in resolv.conf. Thanks to Andrew Bartlett for the report. Add --tftp-unique-root option. Suggestion from Dermot Bradley. Tweak TFTP retry timer to avoid problems with difficult clients. Thanks to Dermot Bradley for assistance with this. Continue to use unqualified hostnames provided by DHCP clients, even if the domain part is illegal. (The domain is ignored, and an error logged.) Previously in this situation, the whole name would have been rejected. Thanks to Jima for the patch. Handle EINTR returns from wait() correctly and reap our children's children if necessary. This fixes a problem with zombie-creation under *BSD when using --dhcp-script. Escape spaces in hostnames when they are stored in the leases file and passed to the lease-change script. Suggestion from Ben Voigt. Re-run the lease change script with an "old" event for each lease when dnsmasq receives a SIGHUP. Added more useful exit codes, including passing on a non-zero exit code from the lease-script "init" call when --leasefile-ro is set. Log memory allocation failure whilst the daemon is running. Allocation failures during startup are fatal, but lack of memory whilst running is worked around. This used to be silent, but now is logged. Fixed misaligned memory access which caused problems on Blackfin CPUs. Thanks to Alex Landau for the patch. Don't include (useless) script-calling code when NO_FORK is set. Since this tends to be used on very small uclinux systems, it's worth-while to save some code-size. Don't set REUSEADDR on TFTP listening socket. There's no need to do so, and it creates confusing behaviour when inetd is also listening on the same port. Thanks to Erik Brown for spotting the problem. version 2.41 Remove deprecated calls when compiled against libdbus 1.1. Fix "strict-alias" warning in bpf.c Reduce dependency on Gnu-make in build system: dnsmasq now builds with system make under OpenBSD. Port to Solaris. Dnsmasq 1.x used to run under Solaris, and this release does so again, for Solaris 9 or better. Allow the DNS function to be completely disabled, by setting the port to zero "--port=0". The allows dnsmasq to be used as a simple DHCP server, simple TFTP server, or both, but without the DNS server getting in the way. Fix a bug where NXDOMAIN could be returned for a query even if the name's value was known for a different query type. This bug could be prodded with --local=/domain/ --address=/name.domain/1.2.3.4 An IPv6 query for name.domain would return NXDOMAIN, and not the correct NOERROR. Thanks to Lars Nooden for spotting the bug and Jima for diagnosis of the problem. Added per-server stats to the information logged when dnsmasq gets SIGUSR1. Added counts of queries forwarded and queries answered locally (from the cache, /etc/hosts or config). Fixed possible crash bug in DBus IPv6 code. Thanks to Matt Domsch and Jima. Tighten checks for clashes between hosts-file and DHCP-derived names. Multiple addresses associated with a name in hosts-file no longer confuses the check. Add --dhcp-no-override option to fix problems with some combinations of stage zero and stage one bootloaders. Thanks to Steve Alexander for the bug report. Add --tftp-port-range option. Thanks to Daniel Mierswa for the suggestion. Add --stop-dns-rebind option. Thanks to Collin Mulliner for the patch. Added GPL version 3 as a license option. Added --all-servers option. Thanks to Peter Naulls for the patch. Extend source address mechanism so that the interface used to contact an upstream DNS server can be nailed down. Something like "--server=1.2.3.4@eth1" will force the use of eth1 for traffic to DNS-server 1.2.3.4. This facility is only available on Linux and Solaris. Thanks to Peter Naulls for prompting this. Add --dhcp-optsfile option. Thanks to Carlos Carvalho for the suggestion. Fixed failure to set source address for server connections when using TCP. Thanks to Simon Capper for finding this bug. Refuse to give a DHCP client the address it asks for if the address range in question is not available to that particular host. Thanks to Cedric Duval for the bug report. Changed behavior of DHCP server to always return total length of a new lease in DHCPOFFER, even if an existing lease exists. (It used to return the time remaining on the lease when one existed.) This fixes problems with the Sony Ericsson K610i phone. Thanks to Hakon Stordahl for finding and fixing this. Add DNSMASQ_INTERFACE to the environment of the lease-change script. Thanks to Nikos Mavrogiannopoulos for the patch. Fixed broken --alias functionality. Thanks to Michael Meelis for the bug report. Added French translation of the man page. Thank to Gildas Le Nadan for that. Add --dhcp-match flag, to check for arbitrary options in DHCP messages from clients. This enables use of dnsmasq with iPXE. Thanks to Rance Hall for the suggestion. Added --dhcp-broadcast, to force broadcast replies to DHCP clients which need them but are too dumb or too old to ask. Thanks to Bodo Bellut for the suggestion. Disable path-MTU discovery on DHCP and TFTP sockets. This is never needed, and the presence of DF flags in the IP header confuses some broken PXE ROMS. Thanks again to Bodo Bellut for spotting this. Fix problems with addresses which have multiple PTR records - all but one of these could get lost. Fix bug with --address and ANY query type seeing REFUSED return code in replies. Thanks to Mike Wright for spotting the problem. Update Spanish translation. Thanks to Chris Chatham. Add --neg-ttl option. Add warnings about the bad effects of --filterwin2k on SIP, XMPP and Google-talk to the example config file. Fix va_list abuse in log.c. This fixes crashes on powerpc when debug mode is set. Thanks to Cedric Duval for the patch. version 2.42 Define _GNU_SOURCE to avoid problems with later glibc headers. Thanks to Jima for spotting the problem. Add --dhcp-alternate-port option. Thanks to Jan Psota for the suggestion. Fix typo in code which is only used on BSD, when Dbus and IPv6 support is enabled. Thanks to Roy Marples. Updated Polish translations - thank to Jan Psota. Fix OS detection logic to cope with GNU/FreeBSD. Fix uninitialised variable in DBus code - thanks to Roy Marples. Fix network enumeration code to work on later NetBSD - thanks to Roy Marples. Provide --dhcp-bridge on all BSD variants. Define _LARGEFILE_SOURCE which removes an arbitrary 2GB limit on logfiles. Thanks to Paul Chambers for spotting the problem. Fix RFC3046 agent-id echo code, broken for many releases. Thanks to Jeremy Laine for spotting the problem and providing a patch. Added Solaris 10 service manifest from David Connelly in contrib/Solaris10 Add --dhcp-scriptuser option. Support new capability interface on suitable Linux kernels, removes "legacy support in use" messages. Thanks to Jorge Bastos for pointing this out. Fix subtle bug in cache code which could cause dnsmasq to lock spinning CPU in rare circumstances. Thanks to Alex Chekholko for bug reports and help debugging. Support netascii transfer mode for TFTP. dnsmasq-2.91/FAQ0000664000175000017500000006560214765043257011655 0ustar srksrkQ: Why does dnsmasq open UDP ports >1024 as well as port 53. Is this a security problem/trojan/backdoor? A: The high ports that dnsmasq opens are for replies from the upstream nameserver(s). Queries from dnsmasq to upstream nameservers are sent from these ports and replies received to them. The reason for doing this is that most firewall setups block incoming packets _to_ port 53, in order to stop DNS queries from the outside world. If dnsmasq sent its queries from port 53 the replies would be _to_ port 53 and get blocked. This is not a security hole since dnsmasq will only accept replies to that port: queries are dropped. The replies must be to outstanding queries which dnsmasq has forwarded, otherwise they are dropped too. Addendum: dnsmasq now has the option "query-port" (-Q), which allows you to specify the UDP port to be used for this purpose. If not specified, the operating system will select an available port number just as it did before. Second addendum: following the discovery of a security flaw in the DNS protocol, dnsmasq from version 2.43 has changed behavior. It 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 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 that? A: Update: from version 2.10, it does. There are a few limitations: data obtained via TCP is not cached, and source-address or query-port specifications are ignored for TCP. Q: When I send SIGUSR1 to dump the contents of the cache, some entries have no IP address and are for names like mymachine.mydomain.com.mydomain.com. What are these? A: They are negative entries: that's what the N flag means. Dnsmasq asked an upstream nameserver to resolve that address and it replied "doesn't exist, and won't exist for hours" so dnsmasq saved that information so that if _it_ gets asked the same question it can answer directly without having to go back to the upstream server again. The strange repeated domains result from the way resolvers search short names. See "man resolv.conf" for details. Q: Will dnsmasq compile/run on non-Linux systems? A: Yes, there is explicit support for *BSD and MacOS X and Solaris. There are start-up scripts for MacOS X Tiger and Panther in /contrib. Dnsmasq will link with uclibc to provide small binaries suitable for use in embedded systems such as routers. (There's special code to support machines with flash filesystems and no battery-backed RTC.) If you encounter make errors with *BSD, try installing gmake from ports and building dnsmasq with "make MAKE=gmake" For other systems, try altering the settings in config.h. Q: My company's nameserver knows about some names which aren't in the public DNS. Even though I put it first in /etc/resolv.conf, it doesn't work: dnsmasq seems not to use the nameservers in the order given. What am I doing wrong? A: By default, dnsmasq treats all the nameservers it knows about as equal: it picks the one to use using an algorithm designed to avoid nameservers which aren't responding. To make dnsmasq use the servers in order, give it the -o flag. If you want some queries sent to a special server, think about using the -S flag to give the IP address of that server, and telling dnsmasq exactly which domains to use the server for. Q: OK, I've got queries to a private nameserver working, now how about reverse queries for a range of IP addresses? A: Use the standard DNS convention of .in-addr.arpa. For instance to send reverse queries on the range 192.168.0.0 to 192.168.0.255 to a nameserver at 10.0.0.1 do server=/0.168.192.in-addr.arpa/10.0.0.1 Note that the "bogus-priv" option take priority over this option, so the above will not work when the bogus-priv option is set. Q: Dnsmasq fails to start with an error like this: "dnsmasq: bind failed: Cannot assign requested address". What's the problem? A: This has been seen when a system is bringing up a PPP interface at boot time: by the time dnsmasq start the interface has been created, but not brought up and assigned an address. The easiest solution is to use --interface flags to specify which interfaces dnsmasq should listen on. Since you are unlikely to want dnsmasq to listen on a PPP interface and offer DNS service to the world, the problem is solved. Q: I'm running on BSD and dnsmasq won't accept long options on the command line. A: Dnsmasq when built on some BSD systems doesn't use GNU getopt by default. You can either just use the single-letter options or change config.h and the Makefile to use getopt-long. Note that options in /etc/dnsmasq.conf must always be the long form, on all platforms. Q: Names on the internet are working fine, but looking up local names from /etc/hosts or DHCP doesn't seem to work. A: Resolver code sometime does strange things when given names without any dots in. Win2k and WinXP may not use the DNS at all and just try and look up the name using WINS. On unix look at "options ndots:" in "man resolv.conf" for details on this topic. Testing lookups using "nslookup" or "dig" will work, but then attempting to run "ping" will get a lookup failure, appending a dot to the end of the 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 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. The way to do that is to include the domain in each name on /etc/hosts and/or to use the --expand-hosts and --domain options. Q: How do I set the DNS domain in Windows XP or MacOS X (ref: previous question)? A: for XP, Control Panel > Network Connections > { Connection to gateway / DNS } > Properties > { Highlight TCP/IP } > Properties > Advanced > DNS Tab > DNS suffix for this connection: A: for OS X, System Preferences > Network > {Connection to gateway / DNS } > Search domains: Q: Can I get dnsmasq to save the contents of its cache to disk when I shut my machine down and re-load when it starts again? A: No, that facility is not provided. Very few names in the DNS have their time-to-live set for longer than a few hours so most of the cache entries would have expired after a shutdown. For longer-lived names it's much cheaper to just reload them from the upstream server. Note that dnsmasq is not shut down between PPP sessions so go off-line and then on-line again will not lose the contents of the cache. Q: Who are Verisign, what do they have to do with the bogus-nxdomain option in dnsmasq and why should I wory about it? A: [note: this was written in September 2003, things may well change.] Verisign run the .com and .net top-level-domains. They have just changed the configuration of their servers so that unknown .com and .net domains, instead of returning an error code NXDOMAIN, (no such domain) return the address of a host at Verisign which runs a web server showing a search page. Most right-thinking people regard this new behaviour as broken :-). You can test to see if you are suffering Verisign brokenness by run a command like host jlsdajkdalld.com If you get "jlsdajkdalld.com" does not exist, then all is fine, if host returns an IP address, then the DNS is broken. (Try a few different unlikely domains, just in case you picked a weird one which really _is_ registered.) Assuming that your DNS is broken, and you want to fix it, simply note the IP address being returned and pass it to dnsmasq using the --bogus-nxdomain flag. Dnsmasq will check for results returning that address and substitute an NXDOMAIN instead. As of writing, the IP address in question for the .com and .net domains is is 64.94.110.11. Various other, less prominent, registries pull the same stunt; there is a list of them all, and the addresses to block, at http://winware.org/bogus-domains.txt Q: This new DHCP server is well and good, but it doesn't work for me. What's the problem? A: There are a couple of configuration gotchas which have been encountered by people moving from the ISC dhcpd to the dnsmasq integrated DHCP daemon. Both are related to differences in in the way the two daemons bypass the IP stack to do "ground up" IP configuration and can lead to the dnsmasq daemon failing whilst the ISC one works. The first thing to check is the broadcast address set for the ethernet interface. This is normally the address on the connected network with all ones in the host part. For instance if the address of the ethernet interface is 192.168.55.7 and the netmask is 255.255.255.0 then the broadcast address should be 192.168.55.255. Having a broadcast address which is not on the network to which the interface is connected kills things stone dead. The second potential problem relates to firewall rules: since the ISC daemon in some configurations bypasses the kernel firewall rules entirely, the ability to run the ISC daemon does not indicate that the current configuration is OK for the dnsmasq daemon. For the dnsmasq daemon to operate it's vital that UDP packets to and from ports 67 and 68 and broadcast packets with source address 0.0.0.0 and destination address 255.255.255.255 are not dropped by iptables/ipchains. Q: I'm running Debian, and my machines get an address fine with DHCP, but their names are not appearing in the DNS. A: By default, none of the DHCP clients send the host-name when asking for a lease. For most of the clients, you can set the host-name to send with the "hostname" keyword in /etc/network/interfaces. (See "man interfaces" for details.) That doesn't work for dhclient, were you have to add something like "send host-name daisy" to /etc/dhclient.conf [Update: the latest dhcpcd packages _do_ send the hostname by default. Q: I'm network booting my machines, and trying to give them static DHCP-assigned addresses. The machine gets its correct address whilst booting, but then the OS starts and it seems to get allocated a different address. A: What is happening is this: The boot process sends a DHCP request and gets allocated the static address corresponding to its MAC address. The boot loader does not send a client-id. Then the OS starts and repeats the DHCP process, but it it does send a client-id. Dnsmasq cannot assume that the two requests are from the same machine (since the client ID's don't match) and even though the MAC address has a static allocation, that address is still in use by the first incarnation of the machine (the one from the boot, without a client ID.) dnsmasq therefore has to give the machine a dynamic address from its pool. There are three ways to solve this: (1) persuade your DHCP client not to send a client ID, or (2) set up the static assignment to the client ID, not the MAC address. The default client-id will be 01:, so change the dhcp-host line from "dhcp-host=11:22:33:44:55:66,1.2.3.4" to "dhcp-host=id:01:11:22:33:44:55:66,1.2.3.4" or (3) tell dnsmasq to ignore client IDs for a particular MAC address, like this: dhcp-host=11:22:33:44:55:66,id:* 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 are these strange "bind-interfaces" 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 bizarre constraints 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 receive 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 received 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 interfaces. 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. An --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. A: Probably because you have the "filterwin2k" option set. Note that it was on by default in example configuration files included in versions before 2.12, so you might have it set on without realising. Q: Can I get email notification when a new version of dnsmasq is released? A: Yes, new releases of dnsmasq are always announced through freshmeat.net, and they allow you to subscribe to email alerts when new versions of particular projects are released. New releases are also announced in the dnsmasq-discuss mailing list, subscribe at http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss Q: What does the dhcp-authoritative option do? A: The DHCP spec says that when a DHCP server receives a renewal request from a client it has no knowledge of, it should just ignore it. This is because it's supported to have more than one DHCP server on a network, and another DHCP server may be dealing with the client. This has the unfortunate effect that when _no_ DHCP replies to the client, it takes some time for the client to time-out and start to get a new lease. Setting this option makes dnsmasq violate the standard to the extent that it will send a NAK reply to the client, causing it to immediately start to get a new lease. This improves behaviour when machines move networks, and in the case that the DHCP lease database is lost. As long as there are not more tha one DHCP server on the network, it's safe to enable the option. Q: Why does my Gentoo box pause for a minute before getting a new lease? A: Because when a Gentoo box shuts down, it releases its lease with the server but remembers it on the client; this seems to be a Gentoo-specific patch to dhcpcd. On restart it tries to renew a lease which is long gone, as far as dnsmasq is concerned, and dnsmasq ignores it until is times out and restarts the process. To fix this, set the dhcp-authoritative flag in dnsmasq. Q: My laptop has two network interfaces, a wired one and a wireless one. I never use both interfaces at the same time, and I'd like the same IP and configuration to be used irrespective of which interface is in use. How can I do that? A: By default, the identity of a machine is determined by using the MAC address, which is associated with interface hardware. Once an IP is bound to the MAC address of one interface, it cannot be associated with another MAC address until after the DHCP lease expires. The solution to this is to use a client-id as the machine identity rather than the MAC address. If you arrange for the same client-id to sent when either interface is in use, the DHCP server will recognise the same machine, and use the same address. The method for setting the client-id varies with DHCP client software, dhcpcd uses the "-I" flag. Windows uses a registry setting, see http://www.jsiinc.com/SUBF/TIP2800/rh2845.htm Addendum: From version 2.46, dnsmasq has a solution to this which doesn't involve setting client-IDs. It's possible to put more than one MAC address in a --dhcp-host configuration. This tells dnsmasq that it should use the specified IP for any of the specified MAC addresses, and furthermore it gives dnsmasq permission to summarily abandon a lease to one of the MAC addresses if another one comes along. Note that this will work fine only as longer as only one interface is up at any time. There is no way for dnsmasq to enforce this constraint: if you configure multiple MAC addresses and violate this rule, bad things will happen. Addendum-II: The link above is dead, the former contents of the link are: ------------------------------------------------------------------------------ How can I keep the same DHCP client reservation, if the MAC address changes? When you reserve an IP address for a DHCP client, you provide the MAC address of the client's NIC. It is possible to use a custom identifier, which is sent as option 61 in the client's DHCP Discover and Request packet. The DhcpClientIdentifier is a REG_DWORD value that is located at: Windows NT 4.0 SP2+ HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\'X'\Parameters\Tcpip where is the NIC driver name and 'X' is the number of the NIC. Windows 2000 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\TcpIp\Parameters\Interfaces\ where is the GUID of the NIC. The valid range of data is 0x0 - 0xFFFFFFFF. The custom identifier is send as 4 bytes, 8 hexadecimal character, in groups of 2 hexadecimal characters, with the groups being sent in reverse order. If the custom identifier is less than 8 hexadeciaml characters, it is zero padded at the end. Examples: Custom Client Client Reservation Identifier on DHCP Server 12345678 78563412 123456 56341200 1234 34120000 1234567 67452301 12345 45230100 123 23010000 A18F42 428FA100 CF432 32F40C00 C32D1BE BED1320C ------------------------------------------------------------------------------------------------------- Q: Can dnsmasq do DHCP on IP-alias interfaces? A: Yes, from version-2.21. The support is only available running under Linux, on a kernel which provides the RT-netlink facility. All 2.4 and 2.6 kernels provide RT-netlink and it's an option in 2.2 kernels. If a physical interface has more than one IP address or aliases with extra IP addresses, then any dhcp-ranges corresponding to these addresses can be used for address allocation. So if an interface has addresses 192.168.1.0/24 and 192.168.2.0/24 and there are DHCP ranges 192.168.1.100-192.168.1.200 and 192.168.2.100-192.168.2.200 then both ranges would be used for host connected to the physical interface. A more typical use might be to have one of the address-ranges as static-only, and have known hosts allocated addresses on that subnet using dhcp-host options, while anonymous hosts go on the other. Q: Dnsmasq sometimes logs "nameserver xxx.xxx.xxx.xxx refused to do a recursive query" and DNS stops working. What's going on? A: Probably the nameserver is an authoritative nameserver for a particular domain, but is not configured to answer general DNS queries for an arbitrary domain. It is not suitable for use by dnsmasq as an upstream server and should be removed from the configuration. Note that if you have more than one upstream nameserver configured dnsmasq will load-balance across them and it may be some time before dnsmasq gets around to using a particular nameserver. This means that a particular configuration may work for sometime with a broken upstream nameserver configuration. Q: Does the dnsmasq DHCP server probe addresses before allocating them, as recommended in RFC2131? 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 seconds. Because the DHCP server is not re-entrant, it cannot serve other DHCP requests during this time. To avoid dropping requests, the address probe may be skipped when dnsmasq is under heavy load. Q: I'm using dnsmasq on a machine with the Firestarter firewall, and DHCP doesn't work. What's the problem? A: This a variant on the iptables problem. Explicit details on how to proceed can be found at http://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2005q3/000431.html Q: I'm using dnsmasq on a machine with the shorewall firewall, and DHCP doesn't work. What's the problem? A: This a variant on the iptables problem. Explicit details on how to proceed can be found at http://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2007q4/001764.html Q: Dnsmasq fails to start up with a message about capabilities. Why did that happen and what can do to fix it? A: Change your kernel configuration: either deselect CONFIG_SECURITY _or_ select CONFIG_SECURITY_CAPABILITIES. Alternatively, you can remove the need to set capabilities by running dnsmasq as root. Q: Where can I get .rpms Suitable for openSUSE/SLES? A: Dnsmasq is in openSUSE itself, and the latest releases are also available at http://download.opensuse.org/repositories/network/ Q: Can I run dnsmasq in a Linux vserver? A: Yes, as a DNS server, dnsmasq will just work in a vserver. To use dnsmasq's DHCP function you need to give the vserver extra system capabilities. Please note that doing so will lesser the overall security of your system. The capabilities required are NET_ADMIN and NET_RAW. NET_ADMIN is essential, NET_RAW is required to do an ICMP "ping" check on newly allocated addresses. If you don't need this check, you can disable it with --no-ping and omit the NET_RAW capability. Adding the capabilities is done by adding them, one per line, to either /etc/vservers//ccapabilities for a 2.4 kernel or /etc/vservers//bcapabilities for a 2.6 kernel (please refer to the vserver documentation for more information). Q: What's the problem with syslog and dnsmasq? A: In almost all cases: none. If you have the normal arrangement with local daemons logging to a local syslog, which then writes to disk, then there's never a problem. If you use network logging, then there's a potential problem with deadlock: the syslog daemon will do DNS lookups so that it can log the source of log messages, these lookups will (depending on exact configuration) go through dnsmasq, which also sends log messages. With bad timing, you can arrive at a situation where syslog is waiting for dnsmasq, and dnsmasq is waiting for syslog; they will both wait forever. This problem is fixed from dnsmasq-2.39, which introduces asynchronous logging: dnsmasq no longer waits for syslog and the deadlock is broken. There is a remaining problem in 2.39, where "log-queries" is in use. In this case most DNS queries generate two log lines, if these go to a syslog which is doing a DNS lookup for each log line, then those queries will in turn generate two more log lines, and a chain reaction runaway will occur. To avoid this, use syslog-ng and turn on syslog-ng's dns-cache function. Q: DHCP doesn't work with windows Vista, but everything else is fine. A: The DHCP client on windows Vista (and possibly later versions) demands that the DHCP server send replies as broadcasts. Most other clients don't do this. The broadcasts are send to 255.255.255.255. A badly configured firewall which blocks such packets will show exactly these symptoms (Vista fails, others 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 symptoms have been 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" dnsmasq-2.91/logo/0000775000175000017500000000000014765043257012252 5ustar srksrkdnsmasq-2.91/logo/README0000664000175000017500000000043014765043257013127 0ustar srksrkDnsmasq logo, contributed by Justin Clift. The source format is Inkscape SVG vector format, which is scalable and easy to export to other formats. For convenience I've included a 56x31 png export and a 16x16 ico suitable for use as a web favicon. Simon Kelley, 22/10/2010 dnsmasq-2.91/logo/favicon.ico0000664000175000017500000000257614765043257014405 0ustar srksrkh( ^effggcfhh o p n sty|~xx|(,178;z?@A{DAvGGFGIqOLPRRiUWWWa\Z]]__azdbfQyPy^99;?LJSCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC>>>CCCCCC>>>CCCB >>CC>>>CB ,5 >B,= @@"@@:>;@' 9@0,@#&% >C@6+.)$!%(*>CCC>@47132/-48>CCCCCCB>A>>CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCdnsmasq-2.91/logo/icon.svg0000664000175000017500000001375114765043257013732 0ustar srksrk image/svg+xml dnsmasq-2.91/logo/icon.png0000664000175000017500000000550614765043257013716 0ustar srksrkPNG  IHDR8/sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATX͙ypTEǿL&w2Ia0II@@aQPkr]]jK]-Z֋$rHB so׽{3/*k̼_׿7sB1~4TU T鼜0K)YHg ~_Qr ׅNo `)*O>I {7 ZM5BrEdF%WdE&nF Ʊ% PG 9,.*Kfmy%@xOgZ% \ lXt*P%(@ԉ()mM͵y‰n|5`'&L#-"b!4U̙? ӅXAc& ?'(anEe~80+.BC66mѪ]r`>y!3A%,Wum>'ltЬ a*#h܅vZT/oXݿ׃)kI!& [ʦOkA!% %N6c_$ox]Ihl&LHO WfZȫO 6pF\ MzgoGrIq{ * c8^xn&y_=Nr1U<2~LtFt  DH#m 7<]J<&f+VGfRH9׻p^a6q*ahޘ줫9W9< Ύ.zTD %C9]9]``*[B%˵5&ߊvxզ2v鋏e^d9(mZPPY4h5gp%Wutk^yy$T}+xդ~ĴH{li zH)/*s(U3[Bˁ~:B`*gj+[oI_̘ @3:v`Z?܌t3]X?p0 ;oQY}ec;I=lv邝EqB%vJG;`N !R,zN\O #8b 0۱7ÔF,n ]f.8Nu;sʪ5O\ӆMkWEI I߬ތIg (Dmc>$u'/GL8w*yɷչYuDtJ$1$JjCU*1DUM fv>>0^W-Jɩ篪|Y8{jJ߂Ip|w{̬1,Vrҧ >\JMvRwdxgW 6UR}ŌqLњGv%ye8CsKqm wPzs3 P  >5dl\*ͧŹb p۴0J]8  h#}^L!:h;(w/;X;NN_W -;ʮQjNIogcMÚ!<JFwi;3rtQn]yJaIq!$1@x%(w1Ys). ^ qPăGv)rNYO*KV,s7܁@9g݇ ޱbLuW4t Wt^ۨ~VF:Ηe#r˾B`xWcDES(}Ts5|+O&G$X4)qf=,GW"KoATXb1s 0j쩛C4M-rÔ]o37~}ICМnΣ#UDzi׮8gM6G잒Rh^ ̅Xfvw\-jۗǤ֣Q,l.72v|0vejgvusooJ~Qʰ:_UBת 'RDh|u/> wNGݻdd0qz&>ƯhlS6#}`6zz,!;ueX49mA ceY@j GLýؒmnGNMs. n񠀨G ; ۞X!ssv1]5K/:tq7.k ;3jcw}&KtdQjwYOQӧA44^# 42 @kδ,*-ׄDP m" "=QvMZ/˘mL?AGFԷwnfMphܕMh%'ӵӋ4Cjѷ>l1FY9$uR 4"nk:^Sƣh6Vqmk"Ƅ#M5ڤʺPݩ5 ngF{KBʺ;ƪ7}11lrm^c6VbOND=x!)?u&aR6u_6T)JIENDB`dnsmasq-2.91/doc.html0000664000175000017500000001316414765043257012752 0ustar srksrk Dnsmasq - network services for small networks.

Dnsmasq

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.

The DNS subsystem provides a local DNS server for the network, with forwarding of all query types to upstream recursive DNS servers and caching of common record types (A, AAAA, CNAME and PTR, also DNSKEY and DS when DNSSEC is enabled).

  • 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.
  • Upstream servers can be configured in a variety of convenient ways, including dynamic configuration as these change on moving upstream network.
  • 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.
  • DNSSEC validation may be performed on DNS replies from upstream nameservers, providing security against spoofing and cache poisoning.
  • Specified sub-domains can be directed to their own upstream DNS servers, making VPN configuration easy.
  • Internationalised domain names are supported.
  • The DHCP subsystem supports DHCPv4, DHCPv6, BOOTP and PXE.

  • Both static and dynamic DHCP leases are supported, along with stateless mode in DHCPv6.
  • 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.
  • There is a built in read-only TFTP server to support netboot.
  • 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 or UID in the dnsmasq configuration file.
  • The Router Advertisement subsystem provides basic autoconfiguration for IPv6 hosts. It can be used stand-alone or in conjunction with DHCPv6.

  • The M and O bits are configurable, to control hosts' use of DHCPv6.
  • Router advertisements can include the RDNSS option.
  • There is a mode which uses name information from DHCPv4 configuration to provide DNS entries for autoconfigured IPv6 addresses which would otherwise be anonymous.
  • For extra compactness, unused features may be omitted at compile time.

    Get code.

    Download dnsmasq here. The tarball includes this documentation, source, and manpage. There is also a CHANGELOG and a FAQ. Dnsmasq has a git repository which contains the complete release history of version 2 and development history from 2.60. You can browse the repo, or get a copy using git protocol with the command
    git clone git://thekelleys.org.uk/dnsmasq.git 
    or
    git clone http://thekelleys.org.uk/git/dnsmasq.git 

    License.

    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.

    Contact.

    There is a dnsmasq mailing list at http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss which should be the first location for queries, bugreports, suggestions etc. The list is mirrored, with a search facility, at https://www.mail-archive.com/dnsmasq-discuss@lists.thekelleys.org.uk/. You can contact me at simon@thekelleys.org.uk.

    Donations.

    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.
    dnsmasq-2.91/Android.mk0000664000175000017500000000011414765043257013217 0ustar srksrkifneq ($(TARGET_SIMULATOR),true) include $(call all-subdir-makefiles) endif dnsmasq-2.91/COPYING-v30000664000175000017500000010451314765043257012677 0ustar srksrk GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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, either version 3 of the License, or (at your option) any later version. 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . dnsmasq-2.91/src/0000775000175000017500000000000014765043257012101 5ustar srksrkdnsmasq-2.91/src/domain-match.c0000664000175000017500000005223114765043257014611 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" static int order(char *qdomain, size_t qlen, struct server *serv); static int order_qsort(const void *a, const void *b); static int order_servers(struct server *s, struct server *s2); /* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain. */ #define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS) void build_server_array(void) { struct server *serv; int count = 0; for (serv = daemon->servers; serv; serv = serv->next) #ifdef HAVE_LOOP if (!(serv->flags & SERV_LOOP)) #endif { count++; if (serv->flags & SERV_WILDCARD) daemon->server_has_wildcard = 1; } for (serv = daemon->local_domains; serv; serv = serv->next) { count++; if (serv->flags & SERV_WILDCARD) daemon->server_has_wildcard = 1; } daemon->serverarraysz = count; if (count > daemon->serverarrayhwm) { struct server **new; count += 10; /* A few extra without re-allocating. */ if ((new = whine_malloc(count * sizeof(struct server *)))) { if (daemon->serverarray) free(daemon->serverarray); daemon->serverarray = new; daemon->serverarrayhwm = count; } } count = 0; for (serv = daemon->servers; serv; serv = serv->next) #ifdef HAVE_LOOP if (!(serv->flags & SERV_LOOP)) #endif { daemon->serverarray[count] = serv; serv->serial = count; serv->last_server = -1; count++; } for (serv = daemon->local_domains; serv; serv = serv->next, count++) daemon->serverarray[count] = serv; qsort(daemon->serverarray, daemon->serverarraysz, sizeof(struct server *), order_qsort); /* servers need the location in the array to find all the whole set of equivalent servers from a pointer to a single one. */ for (count = 0; count < daemon->serverarraysz; count++) if (!(daemon->serverarray[count]->flags & SERV_IS_LOCAL)) daemon->serverarray[count]->arrayposn = count; } /* we're looking for the server whose domain is the longest exact match to the RH end of qdomain, or a local address if the flags match. Add '.' to the LHS of the query string so server=/.example.com/ works. A flag of F_SERVER returns an upstream server only. A flag of F_DNSSECOK returns a DNSSEC capable server only and also disables NODOTS servers from consideration. A flag of F_DOMAINSRV returns a domain-specific server only. A flag of F_CONFIG returns anything that generates a local reply of IPv4 or IPV6. return 0 if nothing found, 1 otherwise. */ int lookup_domain(char *domain, int flags, int *lowout, int *highout) { int rc, crop_query, nodots; ssize_t qlen; int try, high, low = 0; int nlow = 0, nhigh = 0; char *cp, *qdomain = domain; /* may be no configured servers. */ if (daemon->serverarraysz == 0) return 0; /* find query length and presence of '.' */ for (cp = qdomain, nodots = 1, qlen = 0; *cp; qlen++, cp++) if (*cp == '.') nodots = 0; /* Handle empty name, and searches for DNSSEC queries without diverting to NODOTS servers. */ if (qlen == 0 || flags & F_DNSSECOK) nodots = 0; /* Search shorter and shorter RHS substrings for a match */ while (qlen >= 0) { /* Note that when we chop off a label, all the possible matches MUST be at a larger index than the nearest failing match with one more character, since the array is sorted longest to smallest. Hence we don't reset low to zero here, we can go further below and crop the search string to the size of the largest remaining server when this match fails. */ high = daemon->serverarraysz; crop_query = 1; /* binary search */ while (1) { try = (low + high)/2; if ((rc = order(qdomain, qlen, daemon->serverarray[try])) == 0) break; if (rc < 0) { if (high == try) { /* qdomain is longer or same length as longest domain, and try == 0 crop the query to the longest domain. */ crop_query = qlen - daemon->serverarray[try]->domain_len; break; } high = try; } else { if (low == try) { /* try now points to the last domain that sorts before the query, so we know that a substring of the query shorter than it is required to match, so find the largest domain that's shorter than try. Note that just going to try+1 is not optimal, consider searching bbb in (aaa,ccc,bb). try will point to aaa, since ccc sorts after bbb, but the first domain that has a chance to match is bb. So find the length of the first domain later than try which is is shorter than it. There's a nasty edge case when qdomain sorts before _any_ of the server domains, where try _doesn't point_ to the last domain that sorts before the query, since no such domain exists. In that case, the loop exits via the rc < 0 && high == try path above and this code is not executed. */ ssize_t len, old = daemon->serverarray[try]->domain_len; while (++try != daemon->serverarraysz) { if (old != (len = daemon->serverarray[try]->domain_len)) { crop_query = qlen - len; break; } } break; } low = try; } }; if (rc == 0) { int found = 1; if (daemon->server_has_wildcard) { /* if we have example.com and *example.com we need to check against *example.com, but the binary search may have found either. Use the fact that example.com is sorted before *example.com We favour example.com in the case that both match (ie www.example.com) */ while (try != 0 && order(qdomain, qlen, daemon->serverarray[try-1]) == 0) try--; if (!(qdomain == domain || *qdomain == 0 || *(qdomain-1) == '.')) { while (try < daemon->serverarraysz-1 && order(qdomain, qlen, daemon->serverarray[try+1]) == 0) try++; if (!(daemon->serverarray[try]->flags & SERV_WILDCARD)) found = 0; } } if (found && filter_servers(try, flags, &nlow, &nhigh)) /* We have a match, but it may only be (say) an IPv6 address, and if the query wasn't for an AAAA record, it's no good, and we need to continue generalising */ { /* We've matched a setting which says to use servers without a domain. Continue the search with empty query. We set the F_SERVER flag so that --address=/#/... doesn't match. */ if (daemon->serverarray[nlow]->flags & SERV_USE_RESOLV) { crop_query = qlen; flags |= F_SERVER; } else break; } } /* crop_query must be at least one always. */ if (crop_query == 0) crop_query = 1; /* strip chars off the query based on the largest possible remaining match, then continue to the start of the next label unless we have a wildcard domain somewhere, in which case we have to go one at a time. */ qlen -= crop_query; qdomain += crop_query; if (!daemon->server_has_wildcard) while (qlen > 0 && (*(qdomain-1) != '.')) qlen--, qdomain++; } /* domain has no dots, and we have at least one server configured to handle such, These servers always sort to the very end of the array. A configured server eg server=/lan/ will take precdence. */ if (nodots && (daemon->serverarray[daemon->serverarraysz-1]->flags & SERV_FOR_NODOTS) && (nlow == nhigh || daemon->serverarray[nlow]->domain_len == 0)) filter_servers(daemon->serverarraysz-1, flags, &nlow, &nhigh); if (lowout) *lowout = nlow; if (highout) *highout = nhigh; /* qlen == -1 when we failed to match even an empty query, if there are no default servers. */ if (nlow == nhigh || qlen == -1) return 0; return 1; } /* Return first server in group of equivalent servers; this is the "master" record. */ int server_samegroup(struct server *a, struct server *b) { return order_servers(a, b) == 0; } int filter_servers(int seed, int flags, int *lowout, int *highout) { int nlow = seed, nhigh = seed; int i; /* expand nlow and nhigh to cover all the records with the same domain nlow is the first, nhigh - 1 is the last. nlow=nhigh means no servers, which can happen below. */ while (nlow > 0 && order_servers(daemon->serverarray[nlow-1], daemon->serverarray[nlow]) == 0) nlow--; while (nhigh < daemon->serverarraysz-1 && order_servers(daemon->serverarray[nhigh], daemon->serverarray[nhigh+1]) == 0) nhigh++; nhigh++; #define SERV_LOCAL_ADDRESS (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS) if (flags & F_CONFIG) { /* We're just lookin for any matches that return an RR. */ for (i = nlow; i < nhigh; i++) if (daemon->serverarray[i]->flags & SERV_LOCAL_ADDRESS) break; /* failed, return failure. */ if (i == nhigh) nhigh = nlow; } else { /* Now the servers are on order between low and high, in the order IPv6 addr, IPv4 addr, return zero for both, resolvconf servers, send upstream, no-data return. See which of those match our query in that priority order and narrow (low, high) */ for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++); if (!(flags & F_SERVER) && i != nlow && (flags & F_IPV6)) nhigh = i; else { nlow = i; for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_4ADDR); i++); if (!(flags & F_SERVER) && i != nlow && (flags & F_IPV4)) nhigh = i; else { nlow = i; for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_ALL_ZEROS); i++); if (!(flags & F_SERVER) && i != nlow && (flags & (F_IPV4 | F_IPV6))) nhigh = i; else { nlow = i; /* Short to resolv.conf servers */ for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++); if (i != nlow) nhigh = i; else { /* now look for a server */ for (i = nlow; i < nhigh && !(daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++); if (i != nlow) { /* If we want a server that can do DNSSEC, and this one can't, return nothing, similarly if were looking only for a server for a particular domain. */ if ((flags & F_DNSSECOK) && !(daemon->serverarray[nlow]->flags & SERV_DO_DNSSEC)) nlow = nhigh; else if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0) nlow = nhigh; else nhigh = i; } else { /* --local=/domain/, only return if we don't need a server. */ if (flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER)) nhigh = i; } } } } } } *lowout = nlow; *highout = nhigh; return (nlow != nhigh); } int is_local_answer(time_t now, int first, char *name) { int flags = 0; int rc = 0; if ((flags = daemon->serverarray[first]->flags) & SERV_LITERAL_ADDRESS) { if (flags & SERV_4ADDR) rc = F_IPV4; else if (flags & SERV_6ADDR) rc = F_IPV6; else if (flags & SERV_ALL_ZEROS) rc = F_IPV4 | F_IPV6; else { /* argument first is the first struct server which matches the query type; now roll back to the server which is just the same domain, to check if that provides an answer of a different type. */ for (;first > 0 && order_servers(daemon->serverarray[first-1], daemon->serverarray[first]) == 0; first--); if ((daemon->serverarray[first]->flags & SERV_LOCAL_ADDRESS) || check_for_local_domain(name, now)) rc = F_NOERR; else rc = F_NXDOMAIN; } } return rc; } size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede) { int trunc = 0, anscount = 0; unsigned char *p; int start; union all_addr addr; setup_reply(header, flags, ede); if (flags & (F_NXDOMAIN | F_NOERR)) log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL, 0); if (flags & F_RCODE) { union all_addr a; a.log.rcode = RCODE(header); a.log.ede = ede; log_query(F_UPSTREAM | F_RCODE, "opcode", &a, NULL, 0); } if (!(p = skip_questions(header, size))) return 0; if (flags & gotname & F_IPV4) for (start = first; start != last; start++) { struct serv_addr4 *srv = (struct serv_addr4 *)daemon->serverarray[start]; if (srv->flags & SERV_ALL_ZEROS) memset(&addr, 0, sizeof(addr)); else addr.addr4 = srv->addr; if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr)) anscount++; log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL, 0); } if (flags & gotname & F_IPV6) for (start = first; start != last; start++) { struct serv_addr6 *srv = (struct serv_addr6 *)daemon->serverarray[start]; if (srv->flags & SERV_ALL_ZEROS) memset(&addr, 0, sizeof(addr)); else addr.addr6 = srv->addr; if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr)) anscount++; log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL, 0); } if (trunc) { header->hb3 |= HB3_TC; if (!(p = skip_questions(header, size))) return 0; /* bad packet */ anscount = 0; } header->ancount = htons(anscount); return p - (unsigned char *)header; } #ifdef HAVE_DNSSEC int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp) { int first, last, index; /* Find server to send DNSSEC query to. This will normally be the same as for the original query, but may be another if servers for domains are involved. */ if (!lookup_domain(keyname, F_DNSSECOK, &first, &last)) return -1; for (index = first; index != last; index++) if (daemon->serverarray[index] == server) break; /* No match to server used for original query. Use newly looked up set. */ if (index == last) index = daemon->serverarray[first]->last_server == -1 ? first : daemon->serverarray[first]->last_server; if (firstp) *firstp = first; if (lastp) *lastp = last; return index; } #endif /* order by size, then by dictionary order */ static int order(char *qdomain, size_t qlen, struct server *serv) { size_t dlen = 0; /* servers for dotless names always sort last searched for name is never dotless. */ if (serv->flags & SERV_FOR_NODOTS) return -1; dlen = serv->domain_len; if (qlen < dlen) return 1; if (qlen > dlen) return -1; return hostname_order(qdomain, serv->domain); } static int order_servers(struct server *s1, struct server *s2) { int rc; /* need full comparison of dotless servers in order_qsort() and filter_servers() */ if (s1->flags & SERV_FOR_NODOTS) return (s2->flags & SERV_FOR_NODOTS) ? 0 : 1; if ((rc = order(s1->domain, s1->domain_len, s2)) != 0) return rc; /* For identical domains, sort wildcard ones first */ if (s1->flags & SERV_WILDCARD) return (s2->flags & SERV_WILDCARD) ? 0 : 1; return (s2->flags & SERV_WILDCARD) ? -1 : 0; } static int order_qsort(const void *a, const void *b) { int rc; struct server *s1 = *((struct server **)a); struct server *s2 = *((struct server **)b); rc = order_servers(s1, s2); /* Sort all literal NODATA and local IPV4 or IPV6 responses together, in a very specific order. We flip the SERV_LITERAL_ADDRESS bit so the order is IPv6 literal, IPv4 literal, all-zero literal, unqualified servers, upstream server, NXDOMAIN literal. */ if (rc == 0) rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) - ((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS); /* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */ if (rc == 0) if (!(s1->flags & SERV_IS_LOCAL) && !(s2->flags & SERV_IS_LOCAL)) rc = s1->serial - s2->serial; return rc; } /* When loading large numbers of server=.... lines during startup, there's no possibility that there will be server records that can be reused, but searching a long list for each server added grows as O(n^2) and slows things down. This flag is set only if is known there may be free server records that can be reused. There's a call to mark_servers(0) in read_opts() to reset the flag before main config read. */ static int maybe_free_servers = 0; /* Must be called before add_update_server() to set daemon->servers_tail */ void mark_servers(int flag) { struct server *serv, *next, **up; maybe_free_servers = !!flag; daemon->servers_tail = NULL; /* mark everything with argument flag */ for (serv = daemon->servers; serv; serv = serv->next) { if (serv->flags & flag) serv->flags |= SERV_MARK; else serv->flags &= ~SERV_MARK; daemon->servers_tail = serv; } /* --address etc is different: since they are expected to be 1) numerous and 2) not reloaded often. We just delete and recreate. */ if (flag) for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = next) { next = serv->next; if (serv->flags & flag) { *up = next; free(serv->domain); free(serv); } else up = &serv->next; } } void cleanup_servers(void) { struct server *serv, *tmp, **up; /* unlink and free anything still marked. */ for (serv = daemon->servers, up = &daemon->servers, daemon->servers_tail = NULL; serv; serv = tmp) { tmp = serv->next; if (serv->flags & SERV_MARK) { server_gone(serv); *up = serv->next; free(serv->domain); free(serv); } else { up = &serv->next; daemon->servers_tail = serv; } } } int add_update_server(int flags, union mysockaddr *addr, union mysockaddr *source_addr, const char *interface, const char *domain, union all_addr *local_addr) { struct server *serv = NULL; char *alloc_domain; if (!domain) domain = ""; /* .domain == domain, for historical reasons. */ if (*domain == '.') while (*domain == '.') domain++; else if (*domain == '*') { domain++; if (*domain != 0) flags |= SERV_WILDCARD; } if (*domain == 0) alloc_domain = whine_malloc(1); else alloc_domain = canonicalise((char *)domain, NULL); if (!alloc_domain) return 0; if (flags & SERV_IS_LOCAL) { size_t size; if (flags & SERV_6ADDR) size = sizeof(struct serv_addr6); else if (flags & SERV_4ADDR) size = sizeof(struct serv_addr4); else size = sizeof(struct serv_local); if (!(serv = whine_malloc(size))) { free(alloc_domain); return 0; } serv->next = daemon->local_domains; daemon->local_domains = serv; if (flags & SERV_4ADDR) ((struct serv_addr4*)serv)->addr = local_addr->addr4; if (flags & SERV_6ADDR) ((struct serv_addr6*)serv)->addr = local_addr->addr6; } else { /* Upstream servers. See if there is a suitable candidate, if so unmark and move to the end of the list, for order. The entry found may already be at the end. */ struct server **up, *tmp; serv = NULL; if (maybe_free_servers) for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) { tmp = serv->next; if ((serv->flags & SERV_MARK) && hostname_isequal(alloc_domain, serv->domain)) { /* Need to move down? */ if (serv->next) { *up = serv->next; daemon->servers_tail->next = serv; daemon->servers_tail = serv; serv->next = NULL; } break; } else up = &serv->next; } if (serv) { free(alloc_domain); alloc_domain = serv->domain; } else { if (!(serv = whine_malloc(sizeof(struct server)))) { free(alloc_domain); return 0; } memset(serv, 0, sizeof(struct server)); /* Add to the end of the chain, for order */ if (daemon->servers_tail) daemon->servers_tail->next = serv; else daemon->servers = serv; daemon->servers_tail = serv; } #ifdef HAVE_LOOP serv->uid = rand32(); #endif if (interface) safe_strncpy(serv->interface, interface, sizeof(serv->interface)); if (addr) serv->addr = *addr; if (source_addr) serv->source_addr = *source_addr; serv->tcpfd = -1; } serv->flags = flags; serv->domain = alloc_domain; serv->domain_len = strlen(alloc_domain); return 1; } dnsmasq-2.91/src/ipset.c0000664000175000017500000001422414765043257013374 0ustar srksrk/* ipset.c is Copyright (c) 2013 Jason A. Donenfeld . 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 . */ #include "dnsmasq.h" #if defined(HAVE_IPSET) && defined(HAVE_LINUX_NETWORK) #include #include #include #include #include #include /* 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 = (struct my_nlattr *)((u8 *)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((u8 *)attr + NL_ALIGN(sizeof(struct my_nlattr)), data, len); nlh->nlmsg_len += NL_ALIGN(payload_len); } void ipset_init(void) { old_kernel = (daemon->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 union all_addr *ipaddr, int af, int remove) { struct nlmsghdr *nlh; struct my_nfgenmsg *nfg; struct my_nlattr *nested[2]; uint8_t proto; int addrsz = (af == AF_INET6) ? IN6ADDRSZ : INADDRSZ; 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); nested[1]->nla_len = (u8 *)buffer + NL_ALIGN(nlh->nlmsg_len) - (u8 *)nested[1]; nested[0]->nla_len = (u8 *)buffer + NL_ALIGN(nlh->nlmsg_len) - (u8 *)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 union 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->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 union all_addr *ipaddr, int flags, int remove) { int ret = 0, af = AF_INET; if (flags & F_IPV6) { af = AF_INET6; /* old method only supports IPv4 */ if (old_kernel) { errno = EAFNOSUPPORT ; ret = -1; } } if (ret != -1) ret = old_kernel ? old_add_to_ipset(setname, ipaddr, remove) : new_add_to_ipset(setname, ipaddr, af, remove); if (ret == -1) my_syslog(LOG_ERR, _("failed to update ipset %s: %s"), setname, strerror(errno)); return ret; } #endif dnsmasq-2.91/src/auth.c0000664000175000017500000006066614765043257013224 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_AUTH static struct addrlist *find_addrlist(struct addrlist *list, int flag, union all_addr *addr_u) { do { if (!(list->flags & ADDRLIST_IPV6)) { struct in_addr netmask, addr = addr_u->addr4; if (!(flag & F_IPV4)) continue; netmask.s_addr = htonl(~(in_addr_t)0 << (32 - list->prefixlen)); if (is_same_net(addr, list->addr.addr4, netmask)) return list; } else if (is_same_net6(&(addr_u->addr6), &list->addr.addr6, list->prefixlen)) return list; } while ((list = list->next)); return NULL; } static struct addrlist *find_subnet(struct auth_zone *zone, int flag, union all_addr *addr_u) { if (!zone->subnet) return NULL; return find_addrlist(zone->subnet, flag, addr_u); } static struct addrlist *find_exclude(struct auth_zone *zone, int flag, union all_addr *addr_u) { if (!zone->exclude) return NULL; return find_addrlist(zone->exclude, flag, addr_u); } static int filter_zone(struct auth_zone *zone, int flag, union all_addr *addr_u) { if (find_exclude(zone, flag, addr_u)) return 0; /* No subnets 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, rc; 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, out_of_zone = 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; union all_addr addr; struct cname *a, *candidate; unsigned int wclen; 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 int flag = 0; int found = 0; int cname_wildcard = 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, EXTR_NAME_EXTRACT, 4)) return 0; /* bad packet */ GETSHORT(qtype, p); GETSHORT(qclass, p); if (qclass != C_IN) { auth = 0; out_of_zone = 1; 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) { out_of_zone = 1; 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.addr4.s_addr == addrlist->addr.addr4.s_addr) break; if (addrlist) break; else while (intr->next && strcmp(intr->intr, intr->next->intr) == 0) intr = intr->next; } 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.addr6, &addrlist->addr.addr6)) break; if (addrlist) break; else while (intr->next && strcmp(intr->intr, intr->next->intr) == 0) intr = intr->next; } if (intr) { if (local_query || in_zone(zone, intr->name, NULL)) { found = 1; log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, 0); 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), 0); 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), 0); 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 && is_rev_synth(flag, &addr, name) && (local_query || in_zone(zone, name, NULL))) { log_query(F_CONFIG | F_REVERSE | flag, name, &addr, NULL, 0); found = 1; if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl, NULL, T_PTR, C_IN, "d", name)) anscount++; } if (found) nxdomain = 0; else log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL, 0); 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) { out_of_zone = 1; auth = 0; continue; } } for (rec = daemon->mxnames; rec; rec = rec->next) if (!rec->issrv && (rc = hostname_issubdomain(name, rec->name))) { nxdomain = 0; if (rc == 2 && qtype == T_MX) { found = 1; log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); 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 && (rc = hostname_issubdomain(name, rec->name))) { nxdomain = 0; if (rc == 2 && qtype == T_SRV) { found = 1; log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); 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 ((rc = hostname_issubdomain(name, txt->name))) { nxdomain = 0; if (rc == 2 && txt->class == qtype) { found = 1; log_query(F_CONFIG | F_RRNAME, name, NULL, NULL, txt->class); 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 && (rc = hostname_issubdomain(name, txt->name))) { nxdomain = 0; if (rc == 2 && qtype == T_TXT) { found = 1; log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); 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 ((rc = hostname_issubdomain(name, na->name))) { nxdomain = 0; if (rc == 2 && qtype == T_NAPTR) { found = 1; log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); 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; if (qtype == T_AAAA) flag = F_IPV6; for (intr = daemon->int_names; intr; intr = intr->next) if ((rc = hostname_issubdomain(name, intr->name))) { struct addrlist *addrlist; nxdomain = 0; if (rc == 2 && 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))) { if (addrlist->flags & ADDRLIST_REVONLY) continue; found = 1; log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, 0); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl, NULL, qtype, C_IN, qtype == T_A ? "4" : "6", &addrlist->addr)) anscount++; } } if (!found && is_name_synthetic(flag, name, &addr) ) { nxdomain = 0; log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL, 0); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl, NULL, qtype, C_IN, qtype == T_A ? "4" : "6", &addr)) anscount++; } if (!cut) { nxdomain = 0; if (qtype == T_SOA) { auth = soa = 1; /* inhibits auth section */ log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "", 0); } else if (qtype == T_AXFR) { struct iname *peers; if (peer_addr->sa.sa_family == AF_INET) peer_addr->in.sin_port = 0; else { peer_addr->in6.sin6_port = 0; peer_addr->in6.sin6_scope_id = 0; } for (peers = daemon->auth_peers; peers; peers = peers->next) if (sockaddr_isequal(peer_addr, &peers->addr)) break; /* Refuse all AXFR unless --auth-sec-servers or auth-peers is set */ if ((!daemon->secondary_forward_server && !daemon->auth_peers) || (daemon->auth_peers && !peers)) { if (peer_addr->sa.sa_family == AF_INET) inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN); else inet_ntop(AF_INET6, &peer_addr->in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN); 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; axfroffset = nameoffset; log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "", 0); } else if (qtype == T_NS) { auth = 1; ns = 1; /* inhibits auth section */ log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "", 0); } } 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)))) { *cut = '.'; /* restore domain part */ log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid), 0); *cut = 0; /* remove domain part */ 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)))) { log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr, record_source(crecp->uid), 0); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl, NULL, qtype, C_IN, qtype == T_A ? "4" : "6", &crecp->addr)) anscount++; } } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6))); } /* Only supply CNAME if no record for any type is known. */ if (nxdomain) { /* Check for possible wildcard match against *.domain return length of match, to get longest. Note that if return length of wildcard section, so we match b.simon to _both_ *.simon and b.simon but return a longer (better) match to b.simon. */ for (wclen = 0, candidate = NULL, a = daemon->cnames; a; a = a->next) if (a->alias[0] == '*') { char *test = name; while ((test = strchr(test+1, '.'))) { if (hostname_isequal(test, &(a->alias[1]))) { if (strlen(test) > wclen && !cname_wildcard) { wclen = strlen(test); candidate = a; cname_wildcard = 1; } break; } } } else if (hostname_isequal(a->alias, name) && strlen(a->alias) > wclen) { /* Simple case, no wildcard */ wclen = strlen(a->alias); candidate = a; } if (candidate) { log_query(F_CONFIG | F_CNAME, name, NULL, NULL, 0); strcpy(name, candidate->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; } else if (cache_find_non_terminal(name, now)) nxdomain = 0; log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL, 0); } } /* 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.addr4.s_addr) >> 8; char *p = name; if (subnet->prefixlen >= 24) p += sprintf(p, "%u.", a & 0xff); a = a >> 8; if (subnet->prefixlen >= 16 ) p += sprintf(p, "%u.", a & 0xff); a = a >> 8; sprintf(p, "%u.in-addr.arpa", a & 0xff); } else { char *p = name; int i; for (i = subnet->prefixlen-1; i >= 0; i -= 4) { int dig = ((unsigned char *)&subnet->addr.addr6)[i>>3]; p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4); } sprintf(p, "ip6.arpa"); } } /* 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; /* Only include the machine running dnsmasq if it's acting as an auth server */ if (daemon->authinterface) { 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++; 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++; /* 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))) && add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, NULL, (crecp->flags & F_IPV6) ? T_AAAA : T_A, 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)))) { if (cut) *cut = 0; if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl, NULL, (crecp->flags & F_IPV6) ? T_AAAA : T_A, 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; } /* data is never DNSSEC signed. */ header->hb4 &= ~HB4_AD; /* authoritative */ if (auth) header->hb3 |= HB3_AA; /* truncation */ if (trunc) { header->hb3 |= HB3_TC; if (!(ansp = skip_questions(header, qlen))) return 0; /* bad packet */ anscount = authcount = 0; log_query(F_AUTH, "reply", NULL, "truncated", 0); } 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); if (!local_query && out_of_zone) { SET_RCODE(header, REFUSED); header->ancount = htons(0); header->nscount = htons(0); addr.log.rcode = REFUSED; addr.log.ede = EDE_NOT_AUTH; log_query(F_UPSTREAM | F_RCODE, "error", &addr, NULL, 0); return resize_packet(header, ansp - (unsigned char *)header, NULL, 0); } return ansp - (unsigned char *)header; } #endif dnsmasq-2.91/src/dnsmasq.h0000664000175000017500000016540514765043257013733 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #define COPYRIGHT "Copyright (c) 2000-2025 Simon Kelley" /* We do defines that influence behavior of stdio.h, so complain if included too early. */ #ifdef _STDIO_H # error "Header file stdio.h included too early!" #endif #ifndef NO_LARGEFILE /* Ensure we can use files >2GB (log files may grow this big) */ # define _LARGEFILE_SOURCE 1 # define _FILE_OFFSET_BITS 64 #endif /* Get linux C library versions and define _GNU_SOURCE for kFreeBSD. */ #if defined(__linux__) || defined(__GLIBC__) # ifndef __ANDROID__ # define _GNU_SOURCE # endif # include #endif /* Need these defined early */ #if defined(__sun) || defined(__sun__) # define _XPG4_2 # define __EXTENSIONS__ #endif #if (defined(__GNUC__) && __GNUC__ >= 3) || defined(__clang__) #define ATTRIBUTE_NORETURN __attribute__ ((noreturn)) #else #define ATTRIBUTE_NORETURN #endif /* get these before config.h for IPv6 stuff... */ #include #include #ifdef __APPLE__ /* Define before netinet/in.h to select API. OSX Lion onwards. */ # define __APPLE_USE_RFC_3542 #endif #include /* Also needed before config.h. */ #include #include "config.h" #include "ip6addr.h" #include "metrics.h" typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; #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 # define _(S) (S) #else # include # include # define _(S) gettext(S) #endif #include #include #include #if defined(HAVE_SOLARIS_NETWORK) # include #endif #include #include #include #include #include #include #if defined(HAVE_SOLARIS_NETWORK) && !defined(ifr_mtu) /* Some solaris net/if./h omit this. */ # define ifr_mtu ifr_ifru.ifru_metric #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__sun__) || defined (__sun) || defined (__ANDROID__) # include #else # include #endif #include #include #include #include #include #include #include #include #include #include #ifndef HAVE_LINUX_NETWORK # include #endif #if defined(HAVE_LINUX_NETWORK) #include #include #include /* There doesn't seem to be a universally-available userspace header for these. */ extern int capset(cap_user_header_t header, cap_user_data_t data); extern int capget(cap_user_header_t header, cap_user_data_t data); #define LINUX_CAPABILITY_VERSION_1 0x19980330 #define LINUX_CAPABILITY_VERSION_2 0x20071026 #define LINUX_CAPABILITY_VERSION_3 0x20080522 #include #elif defined(HAVE_SOLARIS_NETWORK) #include #endif #if defined(HAVE_DNSSEC) # include #endif /* daemon is function in the C library.... */ #define daemon dnsmasq_daemon #define ADDRSTRLEN INET6_ADDRSTRLEN /* Async event queue */ struct event_desc { int event, data, msg_sz; }; #define EVENT_RELOAD 1 #define EVENT_DUMP 2 #define EVENT_ALARM 3 #define EVENT_TERM 4 #define EVENT_CHILD 5 #define EVENT_REOPEN 6 #define EVENT_EXITED 7 #define EVENT_KILLED 8 #define EVENT_EXEC_ERR 9 #define EVENT_PIPE_ERR 10 #define EVENT_USER_ERR 11 #define EVENT_CAP_ERR 12 #define EVENT_PIDFILE 13 #define EVENT_HUSER_ERR 14 #define EVENT_GROUP_ERR 15 #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 #define EVENT_SCRIPT_LOG 25 #define EVENT_TIME 26 /* Exit codes. */ #define EC_GOOD 0 #define EC_BADCONF 1 #define EC_BADNET 2 #define EC_FILE 3 #define EC_NOMEM 4 #define EC_MISC 5 #define EC_INIT_OFFSET 10 #define OPT_BOGUSPRIV 0 #define OPT_FILTER 1 #define OPT_LOG 2 #define OPT_SELFMX 3 #define OPT_NO_HOSTS 4 #define OPT_NO_POLL 5 #define OPT_DEBUG 6 #define OPT_ORDER 7 #define OPT_NO_RESOLV 8 #define OPT_EXPAND 9 #define OPT_LOCALMX 10 #define OPT_NO_NEG 11 #define OPT_NODOTS_LOCAL 12 #define OPT_NOWILD 13 #define OPT_ETHERS 14 #define OPT_RESOLV_DOMAIN 15 #define OPT_NO_FORK 16 #define OPT_AUTHORITATIVE 17 #define OPT_LOCALISE 18 #define OPT_DBUS 19 #define OPT_DHCP_FQDN 20 #define OPT_NO_PING 21 #define OPT_LEASE_RO 22 #define OPT_ALL_SERVERS 23 #define OPT_RELOAD 24 #define OPT_LOCAL_REBIND 25 #define OPT_TFTP_SECURE 26 #define OPT_TFTP_NOBLOCK 27 #define OPT_LOG_OPTS 28 #define OPT_TFTP_APREF_IP 29 #define OPT_NO_OVERRIDE 30 #define OPT_NO_REBIND 31 #define OPT_ADD_MAC 32 #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_IGN_NS 48 #define OPT_LOCAL_SERVICE 49 #define OPT_LOOP_DETECT 50 #define OPT_EXTRALOG 51 #define OPT_TFTP_NO_FAIL 52 #define OPT_SCRIPT_ARP 53 #define OPT_MAC_B64 54 #define OPT_MAC_HEX 55 #define OPT_TFTP_APREF_MAC 56 #define OPT_RAPID_COMMIT 57 #define OPT_UBUS 58 #define OPT_IGNORE_CLID 59 #define OPT_SINGLE_PORT 60 #define OPT_LEASE_RENEW 61 #define OPT_LOG_DEBUG 62 #define OPT_UMBRELLA 63 #define OPT_UMBRELLA_DEVID 64 #define OPT_CMARK_ALST_EN 65 #define OPT_QUIET_TFTP 66 #define OPT_STRIP_ECS 67 #define OPT_STRIP_MAC 68 #define OPT_NORR 69 #define OPT_NO_IDENT 70 #define OPT_CACHE_RR 71 #define OPT_LOCALHOST_SERVICE 72 #define OPT_LOG_PROTO 73 #define OPT_NO_0x20 74 #define OPT_DO_0x20 75 #define OPT_LAST 76 #define OPTION_BITS (sizeof(unsigned int)*8) #define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) ) #define option_var(x) (daemon->options[(x) / OPTION_BITS]) #define option_val(x) ((1u) << ((x) % OPTION_BITS)) #define option_bool(x) (option_var(x) & option_val(x)) /* extra flags for my_syslog, we use facilities since they are known not to occupy the same bits as priorities, no matter how syslog.h is set up. MS_DEBUG messages are suppressed unless --log-debug is set. */ #define MS_TFTP LOG_USER #define MS_DHCP LOG_DAEMON #define MS_SCRIPT LOG_MAIL #define MS_DEBUG LOG_NEWS /* Note that this is used widely as a container for IPv4/IPv6 addresses, so for that reason, was well as to avoid wasting memory in almost every cache entry, the other variants should not be larger than sizeof(struct in6_addr) - 16 bytes. */ union all_addr { struct in_addr addr4; struct in6_addr addr6; struct { union { struct crec *cache; char *name; } target; unsigned int uid; int is_name_ptr; /* disciminates target union */ } 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; /* for log_query */ struct { unsigned short keytag, algo, digest, rcode; int ede; } log; /* for arbitrary RR record stored in block */ struct { unsigned short rrtype; unsigned short datalen; struct blockdata *rrdata; } rrblock; /* for arbitrary RR record small enough to go in addr. NOTE: rrblock and rrdata are discriminated by the F_KEYTAG bit in the cache flags. */ struct datablock { unsigned short rrtype; unsigned char datalen; /* also length of SOA in negative records. */ char data[1]; } rrdata; }; #define RR_IMDATALEN (sizeof(union all_addr) - offsetof(struct datablock, data)) struct bogus_addr { int is6, prefix; union all_addr addr; struct bogus_addr *next; }; /* dns doctor param */ struct doctor { struct in_addr in, end, out, mask; struct doctor *next; }; struct mx_srv_record { char *name, *target; int issrv, srvport, priority, weight; unsigned int offset; struct mx_srv_record *next; }; struct naptr { char *name, *replace, *regexp, *services, *flags; unsigned int order, pref; struct naptr *next; }; #ifndef NO_ID #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 #endif struct txt_record { char *name; unsigned char *txt; unsigned short class, len; int stat; struct txt_record *next; }; struct ptr_record { char *name, *ptr; struct ptr_record *next; }; struct cname { int ttl, flag; char *alias, *target; struct cname *next, *targetp; }; 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 #define ADDRLIST_PREFIX 8 #define ADDRLIST_WILDCARD 16 #define ADDRLIST_DECLINED 32 struct addrlist { union all_addr addr; int flags, prefixlen; time_t decline_time; 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 addrlist *exclude; struct auth_zone *next; }; #define HR_6 1 #define HR_4 2 struct host_record { int ttl, flags; struct name_list { char *name; struct name_list *next; } *names; struct in_addr addr; struct in6_addr addr6; struct host_record *next; }; #define IN4 1 #define IN6 2 #define INP4 4 #define INP6 8 #define RW_WRITE 0 #define RW_READ 1 #define RW_WRITE_ONCE 2 #define RW_READ_ONCE 3 struct interface_name { char *name; /* domain name */ char *intr; /* interface name */ int flags; struct in_addr proto4; struct in6_addr proto6; struct addrlist *addr; struct interface_name *next; }; union bigname { char name[MAXDNAME]; union bigname *next; /* freelist */ }; struct blockdata { struct blockdata *next; unsigned char key[KEYBLOCK_LEN]; }; struct crec { struct crec *next, *prev, *hash_next; union all_addr addr; time_t ttd; /* time to die */ /* used as class if DNSKEY/DS, index to source for F_HOSTS */ unsigned int uid; unsigned int flags; union { char sname[SMALLDNAME]; union bigname *bname; char *namep; } name; }; #define SIZEOF_BARE_CREC (sizeof(struct crec) - SMALLDNAME) #define SIZEOF_POINTER_CREC (sizeof(struct crec) + sizeof(char *) - SMALLDNAME) #define F_IMMORTAL (1u<<0) #define F_NAMEP (1u<<1) #define F_REVERSE (1u<<2) #define F_FORWARD (1u<<3) #define F_DHCP (1u<<4) #define F_NEG (1u<<5) #define F_HOSTS (1u<<6) #define F_IPV4 (1u<<7) #define F_IPV6 (1u<<8) #define F_BIGNAME (1u<<9) #define F_NXDOMAIN (1u<<10) #define F_CNAME (1u<<11) #define F_DNSKEY (1u<<12) #define F_CONFIG (1u<<13) #define F_DS (1u<<14) #define F_DNSSECOK (1u<<15) #define F_UPSTREAM (1u<<16) #define F_RRNAME (1u<<17) #define F_SERVER (1u<<18) #define F_QUERY (1u<<19) #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_NOEXTRA (1u<<27) #define F_DOMAINSRV (1u<<28) #define F_RCODE (1u<<29) #define F_RR (1u<<30) #define F_STALE (1u<<31) #define UID_NONE 0 /* Values of uid in crecs with F_CONFIG bit set. */ #define SRC_CONFIG 1 #define SRC_HOSTS 2 #define SRC_AH 3 /* struct sockaddr is not large enough to hold any address, and specifically not big enough to hold an IPv6 address. Blech. Roll our own. */ union mysockaddr { struct sockaddr sa; struct sockaddr_in in; struct sockaddr_in6 in6; }; /* bits in flag param to IPv6 callbacks from iface_enumerate() */ #define IFACE_TENTATIVE 1 #define IFACE_DEPRECATED 2 #define IFACE_PERMANENT 4 /* The actual values here matter, since we sort on them to get records in the order IPv6 addr, IPv4 addr, all zero return, resolvconf servers, upstream server, no-data return */ #define SERV_LITERAL_ADDRESS 1 /* addr is the answer, or NoDATA is the answer, depending on the next four flags */ #define SERV_USE_RESOLV 2 /* forward this domain in the normal way */ #define SERV_ALL_ZEROS 4 /* return all zeros for A and AAAA */ #define SERV_4ADDR 8 /* addr is IPv4 */ #define SERV_6ADDR 16 /* addr is IPv6 */ #define SERV_HAS_SOURCE 32 /* source address defined */ #define SERV_FOR_NODOTS 64 /* server for names with no domain part only */ #define SERV_WARNED_RECURSIVE 128 /* avoid warning spam */ #define SERV_FROM_DBUS 256 /* 1 if source is DBus */ #define SERV_MARK 512 /* for mark-and-delete and log code */ #define SERV_WILDCARD 1024 /* domain has leading '*' */ #define SERV_FROM_RESOLV 2048 /* 1 for servers from resolv, 0 for command line. */ #define SERV_FROM_FILE 4096 /* read from --servers-file */ #define SERV_LOOP 8192 /* server causes forwarding loop */ #define SERV_DO_DNSSEC 16384 /* Validate DNSSEC when using this server */ #define SERV_GOT_TCP 32768 /* Got some data from the TCP connection */ struct serverfd { int fd; union mysockaddr source_addr; char interface[IF_NAMESIZE+1]; unsigned int ifindex, used, preallocated; struct serverfd *next; }; struct randfd { struct server *serv; int fd; unsigned short refcount; /* refcount == 0xffff means overflow record. */ }; struct randfd_list { struct randfd *rfd; struct randfd_list *next; }; struct server { u16 flags, domain_len; char *domain; struct server *next; int serial, arrayposn; int last_server; union mysockaddr addr, source_addr; char interface[IF_NAMESIZE+1]; unsigned int ifindex; /* corresponding to interface, above */ struct serverfd *sfd; int tcpfd; unsigned int queries, failed_queries, nxdomain_replies, retrys; unsigned int query_latency, mma_latency; time_t forwardtime; int forwardcount; #ifdef HAVE_LOOP u32 uid; #endif }; /* First four fields must match struct server in next three definitions.. */ struct serv_addr4 { u16 flags, domain_len; char *domain; struct server *next; struct in_addr addr; }; struct serv_addr6 { u16 flags, domain_len; char *domain; struct server *next; struct in6_addr addr; }; struct serv_local { u16 flags, domain_len; char *domain; struct server *next; }; struct rebind_domain { char *domain; struct rebind_domain *next; }; struct ipsets { char **sets; char *domain; struct ipsets *next; }; struct allowlist { u32 mark, mask; char **patterns; struct allowlist *next; }; struct irec { union mysockaddr addr; struct in_addr netmask; /* only valid for IPv4 */ int tftp_ok, dhcp4_ok, dhcp6_ok, mtu, done, warned, dad; int dns_auth, index, multicast_done, found, label; char *name; struct irec *next; }; struct listener { int fd, tcpfd, tftpfd, used; union mysockaddr addr; struct irec *iface; /* only sometimes valid for non-wildcard */ struct listener *next; }; /* interface and address parms from command line. */ struct iname { char *name; union mysockaddr addr; int flags; struct iname *next; }; #define INAME_USED 1 #define INAME_4 2 #define INAME_6 4 struct rrlist { unsigned short rr; struct rrlist *next; }; /* subnet parameters from command line */ struct mysubnet { union mysockaddr addr; int addr_used; int mask; }; /* resolv-file parms from command-line */ struct resolvc { struct resolvc *next; int is_default, logged; time_t mtime; ino_t ino; 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 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; unsigned int index; /* matches to cache entries for logging */ }; struct dyndir { struct dyndir *next; struct hostsfile *files; int flags; char *dname; #ifdef HAVE_INOTIFY int wd; /* inotify watch descriptor */ #endif }; /* packet-dump flags */ #define DUMP_QUERY 0x0001 #define DUMP_REPLY 0x0002 #define DUMP_UP_QUERY 0x0004 #define DUMP_UP_REPLY 0x0008 #define DUMP_SEC_QUERY 0x0010 #define DUMP_SEC_REPLY 0x0020 #define DUMP_BOGUS 0x0040 #define DUMP_SEC_BOGUS 0x0080 #define DUMP_DHCP 0x1000 #define DUMP_DHCPV6 0x2000 #define DUMP_RA 0x4000 #define DUMP_TFTP 0x8000 /* DNSSEC status values. */ #define STAT_SECURE 0x10000 #define STAT_INSECURE 0x20000 #define STAT_BOGUS 0x30000 #define STAT_NEED_DS 0x40000 #define STAT_NEED_KEY 0x50000 #define STAT_TRUNCATED 0x60000 #define STAT_SECURE_WILDCARD 0x70000 #define STAT_OK 0x80000 #define STAT_ABANDONED 0x90000 #define STAT_ASYNC 0xa0000 #define DNSSEC_FAIL_NYV 0x0001 /* key not yet valid */ #define DNSSEC_FAIL_EXP 0x0002 /* key expired */ #define DNSSEC_FAIL_INDET 0x0004 /* indetermined */ #define DNSSEC_FAIL_NOKEYSUP 0x0008 /* no supported key algo. */ #define DNSSEC_FAIL_NOSIG 0x0010 /* No RRsigs */ #define DNSSEC_FAIL_NOZONE 0x0020 /* No Zone bit set */ #define DNSSEC_FAIL_NONSEC 0x0040 /* No NSEC */ #define DNSSEC_FAIL_NODSSUP 0x0080 /* no supported DS algo. */ #define DNSSEC_FAIL_NOKEY 0x0100 /* no DNSKEY */ #define DNSSEC_FAIL_NSEC3_ITERS 0x0200 /* too many iterations in NSEC3 */ #define DNSSEC_FAIL_BADPACKET 0x0400 /* bad packet */ #define DNSSEC_FAIL_WORK 0x0800 /* too much crypto */ #define STAT_ISEQUAL(a, b) (((a) & 0xffff0000) == (b)) #define FREC_NOREBIND 1 #define FREC_CHECKING_DISABLED 2 #define FREC_NO_CACHE 4 #define FREC_DNSKEY_QUERY 8 #define FREC_DS_QUERY 16 #define FREC_AD_QUESTION 32 #define FREC_DO_QUESTION 64 #define FREC_HAS_PHEADER 128 #define FREC_GONE_TO_TCP 256 #define FREC_ANSWER 512 struct frec { struct frec_src { union mysockaddr source; union all_addr dest; unsigned int iface, log_id, encode_bitmap, *encode_bigmap; int fd; unsigned short orig_id, udp_pkt_size; struct frec_src *next; } frec_src; struct server *sentto; /* NULL means free */ struct randfd_list *rfds; unsigned short new_id; int forwardall, flags; time_t time; u32 forward_timestamp; int forward_delay; struct blockdata *stash; /* saved query or saved reply, whilst we validate */ size_t stash_len; #ifdef HAVE_DNSSEC int uid, class, work_counter, validate_counter; struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */ struct frec *next_dependent; /* list of above. */ 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_TFTP 5 #define ACTION_ARP 6 #define ACTION_ARP_DEL 7 #define ACTION_RELAY_SNOOP 8 #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 */ #define LEASE_EXP_CHANGED 256 /* Lease expiry time changed */ #define LIMIT_SIG_FAIL 0 #define LIMIT_CRYPTO 1 #define LIMIT_WORK 2 #define LIMIT_NSEC3_ITERS 3 #define LIMIT_MAX 4 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 */ int flags; time_t expires; /* lease expiry */ #ifdef HAVE_BROKEN_RTC unsigned int length; #endif int hwaddr_len, hwaddr_type; unsigned char hwaddr[DHCP_CHADDR_MAX]; struct in_addr addr, override, giaddr; 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; unsigned 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; }; struct dhcp_netid { char *net; struct dhcp_netid *next; }; struct dhcp_netid_list { struct dhcp_netid *list; struct dhcp_netid_list *next; }; struct tag_if { struct dhcp_netid_list *set; struct dhcp_netid *tag; struct tag_if *next; }; struct delay_config { int delay; struct dhcp_netid *netid; struct delay_config *next; }; struct hwaddr_config { int hwaddr_len, hwaddr_type; unsigned char hwaddr[DHCP_CHADDR_MAX]; unsigned int wildcard_mask; struct hwaddr_config *next; }; struct dhcp_config { unsigned int flags; int clid_len; /* length of client identifier */ unsigned char *clid; /* clientid */ char *hostname, *domain; struct dhcp_netid_list *netid; struct dhcp_netid *filter; #ifdef HAVE_DHCP6 struct addrlist *addr6; #endif struct in_addr addr; time_t decline_time; unsigned int lease_time; struct hwaddr_config *hwaddr; 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 #define CONFIG_NAME 16 #define CONFIG_ADDR 32 #define CONFIG_NOCLID 128 #define CONFIG_FROM_ETHERS 256 /* entry created by /etc/ethers */ #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_ADDR6_HOSTS 16384 /* address added by from /etc/hosts */ struct dhcp_opt { int opt, len, flags; union { int encap; unsigned int wildcard_mask; unsigned char *vendor_class; } u; unsigned char *val; struct dhcp_netid *netid; struct dhcp_opt *next; }; #define DHOPT_ADDR 1 #define DHOPT_STRING 2 #define DHOPT_ENCAPSULATE 4 #define DHOPT_ENCAP_MATCH 8 #define DHOPT_FORCE 16 #define DHOPT_BANK 32 #define DHOPT_ENCAP_DONE 64 #define DHOPT_MATCH 128 #define DHOPT_VENDOR 256 #define DHOPT_HEX 512 #define DHOPT_VENDOR_MATCH 1024 #define DHOPT_RFC3925 2048 #define DHOPT_TAGOK 4096 #define DHOPT_ADDR6 8192 #define DHOPT_VENDOR_PXE 16384 #define DHOPT_PXE_OPT 32768 struct dhcp_boot { char *file, *sname, *tftp_sname; struct in_addr next_server; struct dhcp_netid *netid; struct dhcp_boot *next; }; struct dhcp_match_name { char *name; int wildcard; struct dhcp_netid *netid; struct dhcp_match_name *next; }; struct pxe_service { unsigned short CSA, type; char *menu, *basename, *sname; struct in_addr server; struct dhcp_netid *netid; struct pxe_service *next; }; #define DHCP_PXE_DEF_VENDOR "PXEClient" #define MATCH_VENDOR 1 #define MATCH_USER 2 #define MATCH_CIRCUIT 3 #define MATCH_REMOTE 4 #define MATCH_SUBSCRIBER 5 /* vendorclass, userclass, remote-id or circuit-id */ struct dhcp_vendor { int len, match_type; unsigned int enterprise; char *data; struct dhcp_netid netid; struct dhcp_vendor *next; }; struct dhcp_pxe_vendor { char *data; struct dhcp_pxe_vendor *next; }; struct dhcp_mac { unsigned int mask; int hwaddr_len, hwaddr_type; unsigned char hwaddr[DHCP_CHADDR_MAX]; struct dhcp_netid netid; struct dhcp_mac *next; }; struct dhcp_bridge { char iface[IF_NAMESIZE]; struct dhcp_bridge *alias, *next; }; struct cond_domain { char *domain, *prefix; /* prefix is text-prefix on domain name */ char *interface; /* These two set when domain comes from interface. */ struct addrlist *al; struct in_addr start, end; struct in6_addr start6, end6; int is6, indexed, prefixlen; struct cond_domain *next; }; struct ra_interface { char *name; char *mtu_name; int interval, lifetime, prio, mtu; struct ra_interface *next; }; struct dhcp_context { unsigned int lease_time, addr_epoch; 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; struct dhcp_netid netid, *filter; struct dhcp_context *next, *current; }; struct shared_network { int if_index; struct in_addr match_addr, shared_addr; #ifdef HAVE_DHCP6 /* shared_addr == 0 for IP6 entries. */ struct in6_addr match_addr6, shared_addr6; #endif struct shared_network *next; }; #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) #define CONTEXT_SETLEASE (1u<<19) struct ping_result { struct in_addr addr; time_t time; unsigned int hash; struct ping_result *next; }; struct tftp_file { int refcount, fd; off_t size; dev_t dev; ino_t inode; char filename[]; }; struct tftp_transfer { int sockfd; time_t timeout; int backoff; unsigned int block, blocksize, expansion; off_t offset; union mysockaddr peer; union all_addr source; int if_index; char opt_blocksize, opt_transize, netascii, carrylf; struct tftp_file *file; struct tftp_transfer *next; }; struct addr_list { struct in_addr addr; struct addr_list *next; }; struct tftp_prefix { char *interface; char *prefix; int missing; struct tftp_prefix *next; }; struct dhcp_relay { union { struct in_addr addr4; struct in6_addr addr6; } 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 */ int port; /* Port of relay we forward to. */ #ifdef HAVE_SCRIPT struct snoop_record { struct in6_addr client, prefix; int prefix_len; struct snoop_record *next; } *snoop_records; #endif struct dhcp_relay *next; }; extern struct daemon { /* datastuctures representing the command-line and config file arguments. All set (including defaults) in option.c */ unsigned int options[OPTION_SIZE]; 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, *rr; struct ptr_record *ptr; struct rrlist *cache_rr, *filter_rr; struct host_record *host_records, *host_records_tail; struct cname *cnames; struct auth_zone *auth_zones; struct interface_name *int_names; char *mxtarget; struct mysubnet *add_subnet4; struct mysubnet *add_subnet6; 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, *synth_domains; char *runfile; char *lease_change_command; struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces; struct bogus_addr *bogus_addr, *ignore_addr; struct server *servers, *servers_tail, *local_domains, **serverarray; struct rebind_domain *no_rebind; int server_has_wildcard; int serverarraysz, serverarrayhwm; struct ipsets *ipsets, *nftsets; u32 allowlist_mask; struct allowlist *allowlists; int log_fac; /* log facility */ char *log_file; /* optional log file */ int max_logs; /* queue limit */ int randport_limit; /* Maximum number of source ports for query. */ int cachesize, ftabsize; int port, query_port, min_port, max_port; unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl, dhcp_ttl, use_dhcp_ttl; char *dns_client_id; u32 umbrella_org; u32 umbrella_asset; u8 umbrella_device[8]; int host_index; struct hostsfile *addn_hosts; struct dhcp_context *dhcp, *dhcp6; struct ra_interface *ra_interfaces; struct dhcp_config *dhcp_conf; struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6, *dhcp_match6; struct dhcp_match_name *dhcp_name_match; struct dhcp_pxe_vendor *dhcp_pxe_vendors; 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; struct delay_config *delay_conf; 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 dyndir *dynamic_dirs; int dhcp_max, tftp_max, tftp_mtu; int dhcp_server_port, dhcp_client_port; int start_tftp_port, end_tftp_port; unsigned int min_leasetime; struct doctor *doctors; unsigned short edns_pktsz; char *tftp_prefix; struct tftp_prefix *if_prefix; /* per-interface TFTP prefixes */ unsigned int duid_enterprise, duid_config_len; unsigned char *duid_config; char *dbus_name; char *ubus_name; char *dump_file; int dump_mask; unsigned long soa_sn, soa_refresh, soa_retry, soa_expiry; u32 metrics[__METRIC_MAX]; int fast_retry_time, fast_retry_timeout; int cache_max_expiry; #ifdef HAVE_DNSSEC struct ds_config *ds; 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 */ char *workspacename; #ifdef HAVE_DNSSEC char *keyname; /* MAXDNAME size buffer */ unsigned long *rr_status; /* ceiling in TTL from DNSSEC or zero for insecure */ int rr_status_sz; int dnssec_no_time_check; int back_to_the_future; int limit[LIMIT_MAX]; #endif struct frec *frec_list; struct frec_src *free_frec_src; int frec_src_count; struct serverfd *sfds; struct irec *interfaces; struct listener *listeners; struct server *srv_save; /* Used for resend on DoD */ size_t packet_len; /* " " */ int fd_save; /* " " */ pid_t *tcp_pids; int *tcp_pipes; int pipe_to_parent; int numrrand; struct randfd *randomsocks; struct randfd_list *rfl_spare, *rfl_poll; int v6pktinfo; struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */ int log_id, log_display_id; /* ids of transactions for logging */ union mysockaddr *log_source_addr; /* DHCP state */ int dhcpfd, helperfd, pxefd; #ifdef HAVE_INOTIFY int inotifyfd; #endif #if defined(HAVE_LINUX_NETWORK) int netlinkfd, kernel_version; #elif defined(HAVE_BSD_NETWORK) 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; struct shared_network *shared_networks; #ifdef HAVE_DHCP6 int duid_len; unsigned char *duid; struct iovec outpacket; int dhcp6fd, icmp6fd; # ifdef HAVE_SCRIPT struct snoop_record *free_snoops; # endif #endif /* DBus stuff */ /* void * here to avoid depending on dbus headers outside dbus.c */ void *dbus; #ifdef HAVE_DBUS struct watch *watches; #endif /* UBus stuff */ #ifdef HAVE_UBUS /* void * here to avoid depending on ubus headers outside ubus.c */ void *ubus; #endif /* TFTP stuff */ 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 */ #ifdef HAVE_DUMPFILE /* file for packet dumps. */ int dumpfd; #endif int max_procs; uint max_procs_used; } *daemon; struct server_details { union mysockaddr *addr, *source_addr; struct addrinfo *hostinfo, *orig_hostinfo; char *interface, *source, *scope_id, *interface_opt; int serv_port, source_port, addr_type, scope_index, valid; u16 *flags; }; /* cache.c */ void cache_init(void); unsigned short rrtype(char *in); void next_uid(struct crec *crecp); void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, unsigned short type); char *record_source(unsigned int index); int cache_find_non_terminal(char *name, time_t now); struct crec *cache_find_by_addr(struct crec *crecp, union all_addr *addr, time_t now, unsigned int prot); struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned int prot); void cache_end_insert(void); void cache_start_insert(void); unsigned int cache_remove_uid(const unsigned int uid); int cache_recv_insert(time_t now, int fd); struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class, time_t now, unsigned long ttl, unsigned int flags); void cache_reload(void); void cache_add_dhcp_entry(char *host_name, int prot, union 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); #ifndef NO_ID int cache_make_stat(struct txt_record *t); #endif 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 */ void blockdata_init(void); void blockdata_report(void); struct blockdata *blockdata_alloc(char *data, size_t len); int blockdata_expand(struct blockdata *block, size_t oldlen, char *data, size_t newlen); void *blockdata_retrieve(struct blockdata *block, size_t len, void *data); struct blockdata *blockdata_read(int fd, size_t len); void blockdata_write(struct blockdata *block, size_t len, int fd); void blockdata_free(struct blockdata *blocks); /* domain.c */ char *get_domain(struct in_addr addr); char *get_domain6(struct in6_addr *addr); int is_name_synthetic(int flags, char *name, union all_addr *addr); int is_rev_synth(int flag, union all_addr *addr, char *name); /* rfc1035.c */ int do_doctor(struct dns_header *header, size_t qlen, char *namebuff); int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, char *name, int func, unsigned int parm); unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes); unsigned char *skip_questions(struct dns_header *header, size_t plen); unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen); unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep); void setup_reply(struct dns_header *header, unsigned int flags, int ede); int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind, int no_cache_dnssec, int secure); #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) void report_addresses(struct dns_header *header, size_t len, u32 mark); #endif size_t answer_request(struct dns_header *header, char *limit, size_t qlen, struct in_addr local_addr, struct in_addr local_netmask, time_t now, int ad_reqd, int do_bit, int no_cache, int *stale, int *filtered); int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, time_t now); int check_for_ignored_address(struct dns_header *header, size_t qlen); int check_for_local_domain(char *name, time_t now); size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen); 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, ...); int in_arpa_name_2_addr(char *namein, union all_addr *addrp); int private_net(struct in_addr addr, int ban_localhost); /* extract_name ops */ #define EXTR_NAME_EXTRACT 1 #define EXTR_NAME_COMPARE 2 #define EXTR_NAME_NOCASE 3 #define EXTR_NAME_FLIP 4 /* auth.c */ #ifdef HAVE_AUTH 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 */ #ifdef HAVE_DNSSEC size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int id, int type); int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class, int *validate_count); int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class, int *validate_count); int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int check_unsigned, int *neganswer, int *nons, int *nsec_ttl, int *validate_count); int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen); size_t filter_rrsigs(struct dns_header *header, size_t plen); int setup_timestamp(void); int errflags_to_ede(int status); #endif /* crypto.c */ const struct nettle_hash *hash_find(char *name); int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **digestp); 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); char *ds_digest_name(int digest); char *algo_digest_name(int algo); char *nsec3_digest_name(int digest); /* util.c */ void rand_init(void); unsigned short rand16(void); u32 rand32(void); u64 rand64(void); int rr_on_list(struct rrlist *list, unsigned short rr); int legal_hostname(char *name); char *canonicalise(char *in, int *nomem); unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit); void *safe_malloc(size_t size); void safe_strncpy(char *dest, const char *src, size_t size); void safe_pipe(int *fd, int read_noblock); void *whine_malloc(size_t size); void *whine_realloc(void *ptr, size_t size); int sa_len(union mysockaddr *addr); int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2); int sockaddr_isnull(const union mysockaddr *s); int hostname_order(const char *a, const char *b); int hostname_isequal(const char *a, const char *b); int hostname_issubdomain(char *a, char *b); time_t dnsmasq_time(void); u32 dnsmasq_milliseconds(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 is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix); 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); 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, unsigned int *wildcard_mask, int *mac_type); 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); int read_write(int fd, unsigned char *packet, int size, int rw); void close_fds(long max_fd, int spare1, int spare2, int spare3); int wildcard_match(const char* wildcard, const char* match); int wildcard_matchn(const char* wildcard, const char* match, int num); #ifdef HAVE_LINUX_NETWORK int kernel_version(void); #endif /* log.c */ void die(char *message, char *arg1, int exit_code) ATTRIBUTE_NORETURN; 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(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(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, struct server_details *sdetails); char *parse_server_addr(struct server_details *sdetails); int parse_server_next(struct server_details *sdetails); int option_read_dynfile(char *file, int flags); /* forward.c */ void reply_query(int fd, time_t now); void receive_query(struct listener *listen, time_t now); void return_reply(time_t now, struct frec *forward, struct dns_header *header, ssize_t n, int status); #ifdef HAVE_DNSSEC void pop_and_retry_query(struct frec *forward, int status, time_t now); int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *n, int class, char *name, char *keyname, struct server *server, int *keycount, int *validatecount); #endif unsigned char *tcp_request(int confd, time_t now, union mysockaddr *local_addr, struct in_addr netmask, int auth_dns); void server_gone(struct server *server); int send_from(int fd, int nowild, char *packet, size_t len, union mysockaddr *to, union all_addr *source, unsigned int iface); void resend_query(void); int allocate_rfd(struct randfd_list **fdlp, struct server *serv); void free_rfds(struct randfd_list **fdlp); int fast_retry(time_t now); /* network.c */ int indextoname(int fd, int index, char *name); int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp); void pre_allocate_sfds(void); int reload_servers(char *fname); void check_servers(int no_loop_call); int enumerate_interfaces(int reset); void create_wildcard_listeners(void); void create_bound_listeners(int dienow); void warn_bound_listeners(void); void warn_wild_labels(void); void warn_int_names(void); int is_dad_listeners(void); int iface_check(int family, union all_addr *addr, char *name, int *auth); int loopback_exception(int fd, int family, union all_addr *addr, char *name); int label_exception(int index, int family, union all_addr *addr); int fix_fd(int fd); int tcp_interface(int fd, int af); int set_ipv6pktinfo(int fd); #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 void dhcp_init(void); void dhcp_packet(time_t now, int pxe_fd); struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr taddr, struct dhcp_netid *netids); struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr, struct dhcp_netid *netids); struct ping_result *do_icmp_ping(time_t now, struct in_addr addr, unsigned int hash, int loopback); int address_allocate(struct dhcp_context *context, struct in_addr *addrp, unsigned char *hwaddr, int hw_len, struct dhcp_netid *netids, time_t now, int loopback); void dhcp_read_ethers(void); struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr); char *host_from_dns(struct in_addr addr); #endif /* lease.c */ #ifdef HAVE_DHCP void lease_update_file(time_t now); void lease_update_dns(int force); void lease_init(time_t now); 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, unsigned 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, unsigned 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, unsigned 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, 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); void lease_calc_fqdns(void); #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 loopback, int *is_inform, int pxe, struct in_addr fallback, time_t recvtime); unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr, int clid_len, unsigned char *clid, int *len_out); #endif /* dnsmasq.c */ #ifdef HAVE_DHCP int make_icmp_sock(void); int icmp_ping(struct in_addr addr); int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id); #endif 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); #ifdef HAVE_DNSSEC int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header *header, ssize_t *plen, int class, struct server *server, int *keycount, int *validatecount); #endif /* netlink.c */ #ifdef HAVE_LINUX_NETWORK char *netlink_init(void); void netlink_multicast(void); #endif /* bpf.c */ #ifdef HAVE_BSD_NETWORK 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 */ typedef union { int (*af_unspec)(int family, void *addrp, char *mac, size_t maclen, void *parmv); int (*af_inet)(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam); int (*af_inet6)(struct in6_addr *local, int prefix, int scope, int if_index, int flags, unsigned int preferred, unsigned int valid, void *vparam); int (*af_local)(int index, unsigned int type, char *mac, size_t maclen, void *parm); } callback_t; int iface_enumerate(int family, void *parm, callback_t callback); /* dbus.c */ #ifdef HAVE_DBUS char *dbus_init(void); 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 /* ubus.c */ #ifdef HAVE_UBUS char *ubus_init(void); void set_ubus_listeners(void); void check_ubus_listeners(void); void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface); # ifdef HAVE_CONNTRACK void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name); void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *pattern, const char *ip, u32 ttl); # endif #endif /* ipset.c */ #ifdef HAVE_IPSET void ipset_init(void); int add_to_ipset(const char *setname, const union all_addr *ipaddr, int flags, int remove); #endif /* nftset.c */ #ifdef HAVE_NFTSET void nftset_init(void); int add_to_nftset(const char *setpath, const union all_addr *ipaddr, int flags, int remove); #endif /* pattern.c */ #ifdef HAVE_CONNTRACK int is_valid_dns_name(const char *value); int is_valid_dns_name_pattern(const char *value); int is_dns_name_matching_pattern(const char *name, const char *pattern); #endif /* helper.c */ #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 void queue_arp(int action, unsigned char *mac, int maclen, int family, union all_addr *addr); int helper_buf_empty(void); #ifdef HAVE_DHCP6 void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *prefix, int prefix_len); #endif #endif /* tftp.c */ #ifdef HAVE_TFTP void tftp_request(struct listener *listen, 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, union 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, unsigned int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans); 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, struct in6_addr *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, time_t now); #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); int relay_upstream6(int iface_index, ssize_t sz, struct in6_addr *peer_address, u32 scope_id, time_t now); int relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface); # ifdef HAVE_SCRIPT int do_snoop_script_run(void); # endif #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 *tags); int pxe_ok(struct dhcp_opt *opt, int pxemode); struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags, struct dhcp_opt *opts, int pxemode); int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded); 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); 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, struct dhcp_netid *filter); int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type); #ifdef HAVE_LINUX_NETWORK char *whichdevice(void); int bind_dhcp_devices(char *bound_device); #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); void reset_counter(void); 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_unsolicited(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(void); int detect_loop(char *query, int type); #endif /* inotify.c */ #ifdef HAVE_INOTIFY void inotify_dnsmasq_init(void); int inotify_check(time_t now); void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz); #endif /* poll.c */ void poll_reset(void); int poll_check(int fd, short event); void poll_listen(int fd, short event); int do_poll(int timeout); /* rrfilter.c */ size_t rrfilter(struct dns_header *header, size_t *plen, int mode); short *rrfilter_desc(int type); int expand_workspace(unsigned char ***wkspc, int *szp, int new); int to_wire(char *name); void from_wire(char *name); /* modes. */ #define RRFILTER_EDNS0 0 #define RRFILTER_DNSSEC 1 #define RRFILTER_CONF 2 /* edns0.c */ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last); size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, int optno, unsigned char *opt, size_t optlen, int set_do, int replace); size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit); size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, time_t now, int *cacheable); int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer); /* arp.c */ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now); int do_arp_script_run(void); /* dump.c */ #ifdef HAVE_DUMPFILE void dump_init(void); void dump_packet_udp(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst, int fd); void dump_packet_icmp(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst); #endif /* domain-match.c */ void build_server_array(void); int lookup_domain(char *qdomain, int flags, int *lowout, int *highout); int filter_servers(int seed, int flags, int *lowout, int *highout); int is_local_answer(time_t now, int first, char *name); size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede); int server_samegroup(struct server *a, struct server *b); #ifdef HAVE_DNSSEC int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp); #endif void mark_servers(int flag); void cleanup_servers(void); int add_update_server(int flags, union mysockaddr *addr, union mysockaddr *source_addr, const char *interface, const char *domain, union all_addr *local_addr); dnsmasq-2.91/src/dns-protocol.h0000664000175000017500000001514114765043257014677 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #define NAMESERVER_PORT 53 #define TFTP_PORT 69 #define MIN_PORT 1024 /* first non-reserved port */ #define MAX_PORT 65535u #define IN6ADDRSZ 16 #define INADDRSZ 4 #define PACKETSZ 512 /* maximum packet size */ #define MAXDNAME 1025 /* maximum presentation domain name */ #define RRFIXEDSZ 10 /* #/bytes of fixed data in r record */ #define MAXLABEL 63 /* maximum length of domain label */ #define NOERROR 0 /* no error */ #define FORMERR 1 /* format error */ #define SERVFAIL 2 /* server failure */ #define NXDOMAIN 3 /* non existent domain */ #define NOTIMP 4 /* not implemented */ #define REFUSED 5 /* query refused */ #define QUERY 0 /* opcode */ #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_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 T_CAA 257 #define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */ #define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */ #define EDNS0_OPTION_EDE 15 /* IANA - RFC 8914 */ #define EDNS0_OPTION_NOMDEVICEID 65073 /* Nominum temporary assignment */ #define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */ #define EDNS0_OPTION_UMBRELLA 20292 /* Cisco Umbrella temporary assignment */ /* RFC-8914 extended errors, negative values are our definitions */ #define EDE_UNSET -1 /* No extended DNS error available */ #define EDE_OTHER 0 /* Other */ #define EDE_USUPDNSKEY 1 /* Unsupported DNSKEY algo */ #define EDE_USUPDS 2 /* Unsupported DS Digest */ #define EDE_STALE 3 /* Stale answer */ #define EDE_FORGED 4 /* Forged answer */ #define EDE_DNSSEC_IND 5 /* DNSSEC Indeterminate */ #define EDE_DNSSEC_BOGUS 6 /* DNSSEC Bogus */ #define EDE_SIG_EXP 7 /* Signature Expired */ #define EDE_SIG_NYV 8 /* Signature Not Yet Valid */ #define EDE_NO_DNSKEY 9 /* DNSKEY missing */ #define EDE_NO_RRSIG 10 /* RRSIGs missing */ #define EDE_NO_ZONEKEY 11 /* No Zone Key Bit Set */ #define EDE_NO_NSEC 12 /* NSEC Missing */ #define EDE_CACHED_ERR 13 /* Cached Error */ #define EDE_NOT_READY 14 /* Not Ready */ #define EDE_BLOCKED 15 /* Blocked */ #define EDE_CENSORED 16 /* Censored */ #define EDE_FILTERED 17 /* Filtered */ #define EDE_PROHIBITED 18 /* Prohibited */ #define EDE_STALE_NXD 19 /* Stale NXDOMAIN */ #define EDE_NOT_AUTH 20 /* Not Authoritative */ #define EDE_NOT_SUP 21 /* Not Supported */ #define EDE_NO_AUTH 22 /* No Reachable Authority */ #define EDE_NETERR 23 /* Network error */ #define EDE_INVALID_DATA 24 /* Invalid Data */ #define EDE_SIG_E_B_V 25 /* Signature Expired before Valid */ #define EDE_TOO_EARLY 26 /* To Early */ #define EDE_UNS_NS3_ITER 27 /* Unsupported NSEC3 Iterations Value */ #define EDE_UNABLE_POLICY 28 /* Unable to conform to policy */ #define EDE_SYNTHESIZED 29 /* Synthesized */ struct dns_header { u16 id; u8 hb3,hb4; u16 qdcount,ancount,nscount,arcount; }; #define HB3_QR 0x80 /* Query */ #define HB3_OPCODE 0x78 #define HB3_AA 0x04 /* Authoritative Answer */ #define HB3_TC 0x02 /* TrunCated */ #define HB3_RD 0x01 /* Recursion Desired */ #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 #define GETSHORT(s, cp) do { \ unsigned char *t_cp = (unsigned char *)(cp); \ (s) = ((u16)t_cp[0] << 8) \ | ((u16)t_cp[1]) \ ; \ (cp) += 2; \ } while(0) #define GETLONG(l, cp) do { \ unsigned char *t_cp = (unsigned char *)(cp); \ (l) = ((u32)t_cp[0] << 24) \ | ((u32)t_cp[1] << 16) \ | ((u32)t_cp[2] << 8) \ | ((u32)t_cp[3]) \ ; \ (cp) += 4; \ } while (0) #define PUTSHORT(s, cp) do { \ u16 t_s = (u16)(s); \ unsigned char *t_cp = (unsigned char *)(cp); \ *t_cp++ = t_s >> 8; \ *t_cp = t_s; \ (cp) += 2; \ } while(0) #define PUTLONG(l, cp) do { \ u32 t_l = (u32)(l); \ unsigned char *t_cp = (unsigned char *)(cp); \ *t_cp++ = t_l >> 24; \ *t_cp++ = t_l >> 16; \ *t_cp++ = t_l >> 8; \ *t_cp = t_l; \ (cp) += 4; \ } while (0) #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 to ensure that the escaped form of /000 doesn't include /000 */ #define NAME_ESCAPE 1 dnsmasq-2.91/src/log.c0000664000175000017500000003216414765043257013034 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef __ANDROID__ # include #endif /* Implement logging to /dev/log asynchronously. If syslogd is making DNS lookups through dnsmasq, and dnsmasq blocks awaiting syslogd, then the two daemons can deadlock. We get around this by not blocking when talking to syslog, instead we queue up to MAX_LOGS messages. If more are queued, they will be dropped, and the drop event itself logged. */ /* The "wire" protocol for logging is defined in RFC 3164 */ /* From RFC 3164 */ #define MAX_MESSAGE 1024 /* defaults in case we die() before we log_start() */ static int log_fac = LOG_DAEMON; static int log_stderr = 0; static int echo_stderr = 0; static int log_fd = -1; static int log_to_file = 0; static int entries_alloced = 0; static int entries_lost = 0; static int connection_good = 1; static int max_logs = 0; static int connection_type = SOCK_DGRAM; struct log_entry { int offset, length; pid_t pid; /* to avoid duplicates over a fork */ struct log_entry *next; char payload[MAX_MESSAGE]; }; static struct log_entry *entries = NULL; static struct log_entry *free_entries = NULL; int log_start(struct passwd *ent_pw, int errfd) { int ret = 0; echo_stderr = option_bool(OPT_DEBUG); if (daemon->log_fac != -1) log_fac = daemon->log_fac; #ifdef LOG_LOCAL0 else if (option_bool(OPT_DEBUG)) log_fac = LOG_LOCAL0; #endif if (daemon->log_file) { log_to_file = 1; daemon->max_logs = 0; if (strcmp(daemon->log_file, "-") == 0) { log_stderr = 1; echo_stderr = 0; log_fd = dup(STDERR_FILENO); } } max_logs = daemon->max_logs; if (!log_reopen(daemon->log_file)) { send_event(errfd, EVENT_LOG_ERR, errno, daemon->log_file ? daemon->log_file : ""); _exit(0); } /* if queuing is inhibited, make sure we allocate the one required buffer now. */ if (max_logs == 0) { free_entries = safe_malloc(sizeof(struct log_entry)); free_entries->next = NULL; entries_alloced = 1; } /* If we're running as root and going to change uid later, change the ownership here so that the file is always owned by the dnsmasq user. Then logrotate can just copy the owner. Failure of the chown call is OK, (for instance when started as non-root). If we've created a file with group-id root, we also make the file group-writable. This gives processes in the root group write access to the file and avoids the problem that on some systems, once the file is owned by the dnsmasq user, it can't be written whilst dnsmasq is running as root during startup. */ if (log_to_file && !log_stderr && ent_pw && ent_pw->pw_uid != 0) { struct stat ls; if (getgid() == 0 && fstat(log_fd, &ls) == 0 && ls.st_gid == 0 && (ls.st_mode & S_IWGRP) == 0) (void)fchmod(log_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); if (fchown(log_fd, ent_pw->pw_uid, -1) != 0) ret = errno; } return ret; } int log_reopen(char *log_file) { if (!log_stderr) { if (log_fd != -1) close(log_fd); /* NOTE: umask is set to 022 by the time this gets called */ if (log_file) log_fd = open(log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP); else { #if defined(HAVE_SOLARIS_NETWORK) || defined(__ANDROID__) /* Solaris logging is "different", /dev/log is not unix-domain socket. Just leave log_fd == -1 and use the vsyslog call for everything.... */ # define _PATH_LOG "" /* dummy */ return 1; #else int flags; log_fd = socket(AF_UNIX, connection_type, 0); /* if max_logs is zero, leave the socket blocking */ if (log_fd != -1 && max_logs != 0 && (flags = fcntl(log_fd, F_GETFL)) != -1) fcntl(log_fd, F_SETFL, flags | O_NONBLOCK); #endif } } return log_fd != -1; } static void free_entry(void) { struct log_entry *tmp = entries; entries = tmp->next; tmp->next = free_entries; free_entries = tmp; } static void log_write(void) { ssize_t rc; while (entries) { /* The data in the payload 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()) { free_entry(); continue; } connection_good = 1; if ((rc = write(log_fd, entries->payload + entries->offset, entries->length - len_adjust)) != -1) { entries->length -= rc; entries->offset += rc; if (entries->length == len_adjust) { free_entry(); if (entries_lost != 0) { int e = entries_lost; entries_lost = 0; /* avoid wild recursion */ my_syslog(LOG_WARNING, _("overflow: %d log entries lost"), e); } } continue; } if (errno == EINTR) continue; if (errno == EAGAIN || errno == EWOULDBLOCK) return; /* syslogd busy, go again when select() or poll() says so */ if (errno == ENOBUFS) { connection_good = 0; return; } /* errors handling after this assumes sockets */ if (!log_to_file) { /* Once a stream socket hits EPIPE, we have to close and re-open (we ignore SIGPIPE) */ if (errno == EPIPE) { if (log_reopen(NULL)) continue; } else if (errno == ECONNREFUSED || errno == ENOTCONN || errno == EDESTADDRREQ || errno == ECONNRESET) { /* socket went (syslogd down?), try and reconnect. If we fail, stop trying until the next call to my_syslog() ECONNREFUSED -> connection went down ENOTCONN -> nobody listening (ECONNRESET, EDESTADDRREQ are *BSD equivalents) */ struct sockaddr_un logaddr; #ifdef HAVE_SOCKADDR_SA_LEN logaddr.sun_len = sizeof(logaddr) - sizeof(logaddr.sun_path) + strlen(_PATH_LOG) + 1; #endif logaddr.sun_family = AF_UNIX; safe_strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path)); /* Got connection back? try again. */ if (connect(log_fd, (struct sockaddr *)&logaddr, sizeof(logaddr)) != -1) continue; /* errors from connect which mean we should keep trying */ if (errno == ENOENT || errno == EALREADY || errno == ECONNREFUSED || errno == EISCONN || errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { /* try again on next syslog() call */ connection_good = 0; return; } /* try the other sort of socket... */ if (errno == EPROTOTYPE) { connection_type = connection_type == SOCK_DGRAM ? SOCK_STREAM : SOCK_DGRAM; if (log_reopen(NULL)) continue; } } } /* give up - fall back to syslog() - this handles out-of-space when logging to a file, for instance. */ log_fd = -1; my_syslog(LOG_CRIT, _("log failed: %s"), strerror(errno)); return; } } /* priority is one of LOG_DEBUG, LOG_INFO, LOG_NOTICE, etc. See sys/syslog.h. OR'd to priority can be MS_TFTP, MS_DHCP, ... to be able to do log separation between DNS, DHCP and TFTP services. If OR'd with MS_DEBUG, the messages are suppressed unless --log-debug is set. */ void my_syslog(int priority, const char *format, ...) { va_list ap; struct log_entry *entry; time_t time_now; char *p; size_t len; pid_t pid = getpid(); char *func = ""; if ((LOG_FACMASK & priority) == MS_TFTP) func = "-tftp"; else if ((LOG_FACMASK & priority) == MS_DHCP) func = "-dhcp"; else if ((LOG_FACMASK & priority) == MS_SCRIPT) func = "-script"; else if ((LOG_FACMASK & priority) == MS_DEBUG) { if (!option_bool(OPT_LOG_DEBUG)) return; func = "-debug"; } #ifdef LOG_PRI priority = LOG_PRI(priority); #else /* Solaris doesn't have LOG_PRI */ priority &= LOG_PRIMASK; #endif if (echo_stderr) { fprintf(stderr, "dnsmasq%s: ", func); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fputc('\n', stderr); } if (log_fd == -1) { #ifdef __ANDROID__ /* do android-specific logging. log_fd is always -1 on Android except when logging to a file. */ int alog_lvl; if (priority <= LOG_ERR) alog_lvl = ANDROID_LOG_ERROR; else if (priority == LOG_WARNING) alog_lvl = ANDROID_LOG_WARN; else if (priority <= LOG_INFO) alog_lvl = ANDROID_LOG_INFO; else alog_lvl = ANDROID_LOG_DEBUG; va_start(ap, format); __android_log_vprint(alog_lvl, "dnsmasq", format, ap); va_end(ap); #else /* fall-back to syslog if we die during startup or fail during running (always on Solaris). */ static int isopen = 0; if (!isopen) { openlog("dnsmasq", LOG_PID, log_fac); isopen = 1; } va_start(ap, format); vsyslog(priority, format, ap); va_end(ap); #endif return; } if ((entry = free_entries)) free_entries = entry->next; else if (entries_alloced < max_logs && (entry = malloc(sizeof(struct log_entry)))) entries_alloced++; if (!entry) entries_lost++; else { /* add to end of list, consumed from the start */ entry->next = NULL; if (!entries) entries = entry; else { struct log_entry *tmp; for (tmp = entries; tmp->next; tmp = tmp->next); tmp->next = entry; } time(&time_now); p = entry->payload; if (!log_to_file) p += sprintf(p, "<%d>", priority | log_fac); /* Omit timestamp for default daemontools situation */ if (!log_stderr || !option_bool(OPT_NO_FORK)) p += sprintf(p, "%.15s ", ctime(&time_now) + 4); p += sprintf(p, "dnsmasq%s[%d]: ", func, (int)pid); len = p - entry->payload; va_start(ap, format); len += vsnprintf(p, MAX_MESSAGE - len, format, ap) + 1; /* include zero-terminator */ va_end(ap); entry->length = len > MAX_MESSAGE ? MAX_MESSAGE : len; entry->offset = 0; entry->pid = pid; } /* almost always, logging won't block, so try and write this now, to save collecting too many log messages during a select loop. */ log_write(); /* Since we're doing things asynchronously, a cache-dump, for instance, can now generate log lines very fast. With a small buffer (desirable), that means it can overflow the log-buffer very quickly, so that the cache dump becomes mainly a count of how many lines overflowed. To avoid this, we delay here, the delay is controlled by queue-occupancy, and grows exponentially. The delay is limited to (2^8)ms. The scaling stuff ensures that when the queue is bigger than 8, the delay only occurs for the last 8 entries. Once the queue is full, we stop delaying to preserve performance. */ if (entries && max_logs != 0) { int d; for (d = 0,entry = entries; entry; entry = entry->next, d++); if (d == max_logs) d = 0; else if (max_logs > 8) d -= max_logs - 8; if (d > 0) { struct timespec waiter; waiter.tv_sec = 0; waiter.tv_nsec = 1000000 << (d - 1); /* 1 ms */ nanosleep(&waiter, NULL); /* Have another go now */ log_write(); } } } void set_log_writer(void) { if (entries && log_fd != -1 && connection_good) poll_listen(log_fd, POLLOUT); } void check_log_writer(int force) { if (log_fd != -1 && (force || poll_check(log_fd, POLLOUT))) log_write(); } void flush_log(void) { /* write until queue empty, but don't loop forever if there's no connection to the syslog in existence */ while (log_fd != -1) { struct timespec waiter; log_write(); if (!entries || !connection_good) { close(log_fd); break; } waiter.tv_sec = 0; waiter.tv_nsec = 1000000; /* 1 ms */ nanosleep(&waiter, NULL); } } void die(char *message, char *arg1, int exit_code) { char *errmess = strerror(errno); if (!arg1) arg1 = errmess; if (!log_stderr) { echo_stderr = 1; /* print as well as log when we die.... */ fputc('\n', stderr); /* prettyfy startup-script message */ } my_syslog(LOG_CRIT, message, arg1, errmess); echo_stderr = 0; my_syslog(LOG_CRIT, _("FAILED to start up")); flush_log(); exit(exit_code); } dnsmasq-2.91/src/cache.c0000664000175000017500000021075214765043257013317 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL; #ifdef HAVE_DHCP static struct crec *dhcp_spare = NULL; #endif static struct crec *new_chain = NULL; static int insert_error; static union bigname *big_free = NULL; static int bignames_left, hash_size; static void make_non_terminals(struct crec *source); static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class, time_t now, unsigned long ttl, unsigned int flags); static void dump_cache_entry(struct crec *cache, time_t now); static char *querystr(char *desc, unsigned short type); /* type->string mapping: this is also used by the name-hash function as a mixing table. */ /* taken from https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml */ static const struct { unsigned int type; const char * const name; } typestr[] = { { 1, "A" }, /* a host address [RFC1035] */ { 2, "NS" }, /* an authoritative name server [RFC1035] */ { 3, "MD" }, /* a mail destination (OBSOLETE - use MX) [RFC1035] */ { 4, "MF" }, /* a mail forwarder (OBSOLETE - use MX) [RFC1035] */ { 5, "CNAME" }, /* the canonical name for an alias [RFC1035] */ { 6, "SOA" }, /* marks the start of a zone of authority [RFC1035] */ { 7, "MB" }, /* a mailbox domain name (EXPERIMENTAL) [RFC1035] */ { 8, "MG" }, /* a mail group member (EXPERIMENTAL) [RFC1035] */ { 9, "MR" }, /* a mail rename domain name (EXPERIMENTAL) [RFC1035] */ { 10, "NULL" }, /* a null RR (EXPERIMENTAL) [RFC1035] */ { 11, "WKS" }, /* a well known service description [RFC1035] */ { 12, "PTR" }, /* a domain name pointer [RFC1035] */ { 13, "HINFO" }, /* host information [RFC1035] */ { 14, "MINFO" }, /* mailbox or mail list information [RFC1035] */ { 15, "MX" }, /* mail exchange [RFC1035] */ { 16, "TXT" }, /* text strings [RFC1035] */ { 17, "RP" }, /* for Responsible Person [RFC1183] */ { 18, "AFSDB" }, /* for AFS Data Base location [RFC1183][RFC5864] */ { 19, "X25" }, /* for X.25 PSDN address [RFC1183] */ { 20, "ISDN" }, /* for ISDN address [RFC1183] */ { 21, "RT" }, /* for Route Through [RFC1183] */ { 22, "NSAP" }, /* for NSAP address, NSAP style A record [RFC1706] */ { 23, "NSAP_PTR" }, /* for domain name pointer, NSAP style [RFC1348][RFC1637][RFC1706] */ { 24, "SIG" }, /* for security signature [RFC2535][RFC2536][RFC2537][RFC2931][RFC3008][RFC3110][RFC3755][RFC4034] */ { 25, "KEY" }, /* for security key [RFC2535][RFC2536][RFC2537][RFC2539][RFC3008][RFC3110][RFC3755][RFC4034] */ { 26, "PX" }, /* X.400 mail mapping information [RFC2163] */ { 27, "GPOS" }, /* Geographical Position [RFC1712] */ { 28, "AAAA" }, /* IP6 Address [RFC3596] */ { 29, "LOC" }, /* Location Information [RFC1876] */ { 30, "NXT" }, /* Next Domain (OBSOLETE) [RFC2535][RFC3755] */ { 31, "EID" }, /* Endpoint Identifier [Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt] 1995-06*/ { 32, "NIMLOC" }, /* Nimrod Locator [1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt] 1995-06*/ { 33, "SRV" }, /* Server Selection [1][RFC2782] */ { 34, "ATMA" }, /* ATM Address [ ATM Forum Technical Committee, "ATM Name System, V2.0", Doc ID: AF-DANS-0152.000, July 2000. Available from and held in escrow by IANA.] */ { 35, "NAPTR" }, /* Naming Authority Pointer [RFC2168][RFC2915][RFC3403] */ { 36, "KX" }, /* Key Exchanger [RFC2230] */ { 37, "CERT" }, /* CERT [RFC4398] */ { 38, "A6" }, /* A6 (OBSOLETE - use AAAA) [RFC2874][RFC3226][RFC6563] */ { 39, "DNAME" }, /* DNAME [RFC6672] */ { 40, "SINK" }, /* SINK [Donald_E_Eastlake][http://tools.ietf.org/html/draft-eastlake-kitchen-sink] 1997-11*/ { 41, "OPT" }, /* OPT [RFC3225][RFC6891] */ { 42, "APL" }, /* APL [RFC3123] */ { 43, "DS" }, /* Delegation Signer [RFC3658][RFC4034] */ { 44, "SSHFP" }, /* SSH Key Fingerprint [RFC4255] */ { 45, "IPSECKEY" }, /* IPSECKEY [RFC4025] */ { 46, "RRSIG" }, /* RRSIG [RFC3755][RFC4034] */ { 47, "NSEC" }, /* NSEC [RFC3755][RFC4034][RFC9077] */ { 48, "DNSKEY" }, /* DNSKEY [RFC3755][RFC4034] */ { 49, "DHCID" }, /* DHCID [RFC4701] */ { 50, "NSEC3" }, /* NSEC3 [RFC5155][RFC9077] */ { 51, "NSEC3PARAM" }, /* NSEC3PARAM [RFC5155] */ { 52, "TLSA" }, /* TLSA [RFC6698] */ { 53, "SMIMEA" }, /* S/MIME cert association [RFC8162] SMIMEA/smimea-completed-template 2015-12-01*/ { 55, "HIP" }, /* Host Identity Protocol [RFC8005] */ { 56, "NINFO" }, /* NINFO [Jim_Reid] NINFO/ninfo-completed-template 2008-01-21*/ { 57, "RKEY" }, /* RKEY [Jim_Reid] RKEY/rkey-completed-template 2008-01-21*/ { 58, "TALINK" }, /* Trust Anchor LINK [Wouter_Wijngaards] TALINK/talink-completed-template 2010-02-17*/ { 59, "CDS" }, /* Child DS [RFC7344] CDS/cds-completed-template 2011-06-06*/ { 60, "CDNSKEY" }, /* DNSKEY(s) the Child wants reflected in DS [RFC7344] 2014-06-16*/ { 61, "OPENPGPKEY" }, /* OpenPGP Key [RFC7929] OPENPGPKEY/openpgpkey-completed-template 2014-08-12*/ { 62, "CSYNC" }, /* Child-To-Parent Synchronization [RFC7477] 2015-01-27*/ { 63, "ZONEMD" }, /* Message Digest Over Zone Data [RFC8976] ZONEMD/zonemd-completed-template 2018-12-12*/ { 64, "SVCB" }, /* Service Binding [draft-ietf-dnsop-svcb-https-00] SVCB/svcb-completed-template 2020-06-30*/ { 65, "HTTPS" }, /* HTTPS Binding [draft-ietf-dnsop-svcb-https-00] HTTPS/https-completed-template 2020-06-30*/ { 66, "DSYNC" }, /* Endpoint discovery for delegation synchronization [draft-ietf-dnsop-generalized-notify-03] DSYNC/dsync-completed-template 2024-12-10 */ { 99, "SPF" }, /* [RFC7208] */ { 100, "UINFO" }, /* [IANA-Reserved] */ { 101, "UID" }, /* [IANA-Reserved] */ { 102, "GID" }, /* [IANA-Reserved] */ { 103, "UNSPEC" }, /* [IANA-Reserved] */ { 104, "NID" }, /* [RFC6742] ILNP/nid-completed-template */ { 105, "L32" }, /* [RFC6742] ILNP/l32-completed-template */ { 106, "L64" }, /* [RFC6742] ILNP/l64-completed-template */ { 107, "LP" }, /* [RFC6742] ILNP/lp-completed-template */ { 108, "EUI48" }, /* an EUI-48 address [RFC7043] EUI48/eui48-completed-template 2013-03-27*/ { 109, "EUI64" }, /* an EUI-64 address [RFC7043] EUI64/eui64-completed-template 2013-03-27*/ { 128, "NXNAME" }, /* NXDOMAIN indicator for Compact Denial of Existence https://www.iana.org/go/draft-ietf-dnsop-compact-denial-of-existence-04 */ { 249, "TKEY" }, /* Transaction Key [RFC2930] */ { 250, "TSIG" }, /* Transaction Signature [RFC8945] */ { 251, "IXFR" }, /* incremental transfer [RFC1995] */ { 252, "AXFR" }, /* transfer of an entire zone [RFC1035][RFC5936] */ { 253, "MAILB" }, /* mailbox-related RRs (MB, MG or MR) [RFC1035] */ { 254, "MAILA" }, /* mail agent RRs (OBSOLETE - see MX) [RFC1035] */ { 255, "ANY" }, /* A request for some or all records the server has available [RFC1035][RFC6895][RFC8482] */ { 256, "URI" }, /* URI [RFC7553] URI/uri-completed-template 2011-02-22*/ { 257, "CAA" }, /* Certification Authority Restriction [RFC8659] CAA/caa-completed-template 2011-04-07*/ { 258, "AVC" }, /* Application Visibility and Control [Wolfgang_Riedel] AVC/avc-completed-template 2016-02-26*/ { 259, "DOA" }, /* Digital Object Architecture [draft-durand-doa-over-dns] DOA/doa-completed-template 2017-08-30*/ { 260, "AMTRELAY" }, /* Automatic Multicast Tunneling Relay [RFC8777] AMTRELAY/amtrelay-completed-template 2019-02-06*/ { 261, "RESINFO" }, /* Resolver Information as Key/Value Pairs https://datatracker.ietf.org/doc/draft-ietf-add-resolver-info/06/ */ { 262, "WALLET" }, /* Public wallet address https://www.iana.org/assignments/dns-parameters/WALLET/wallet-completed-template */ { 263, "CLA" }, /* BP Convergence Layer Adapter https://www.iana.org/go/draft-johnson-dns-ipn-cla-07 */ { 264, "IPN" }, /* BP Node Number https://www.iana.org/go/draft-johnson-dns-ipn-cla-07 */ { 32768, "TA" }, /* DNSSEC Trust Authorities [Sam_Weiler][http://cameo.library.cmu.edu/][ Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.] 2005-12-13*/ { 32769, "DLV" }, /* DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431] */ }; static void cache_free(struct crec *crecp); static void cache_unlink(struct crec *crecp); static void cache_link(struct crec *crecp); static void rehash(int size); static void cache_hash(struct crec *crecp); unsigned short rrtype(char *in) { unsigned int i; for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++) if (strcasecmp(in, typestr[i].name) == 0) return typestr[i].type; return 0; } void next_uid(struct crec *crecp) { static unsigned int uid = 0; if (crecp->uid == UID_NONE) { uid++; /* uid == 0 used to indicate CNAME to interface name. */ if (uid == UID_NONE) uid++; crecp->uid = uid; } } void cache_init(void) { struct crec *crecp; int i; bignames_left = daemon->cachesize/10; if (daemon->cachesize > 0) { crecp = safe_malloc(daemon->cachesize*sizeof(struct crec)); for (i=0; i < daemon->cachesize; i++, crecp++) { cache_link(crecp); crecp->flags = 0; crecp->uid = UID_NONE; } } /* create initial hash table*/ rehash(daemon->cachesize); } /* In most cases, we create the hash table once here by calling this with (hash_table == NULL) but if the hosts file(s) are big (some people have 50000 ad-block entries), the table will be much too small, so the hosts reading code calls rehash every 1000 addresses, to expand the table. */ static void rehash(int size) { struct crec **new, **old, *p, *tmp; int i, new_size, old_size; /* hash_size is a power of two. */ for (new_size = 64; new_size < size/10; new_size = new_size << 1); /* must succeed in getting first instance, failure later is non-fatal */ if (!hash_table) new = safe_malloc(new_size * sizeof(struct crec *)); else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *)))) return; for (i = 0; i < new_size; i++) new[i] = NULL; old = hash_table; old_size = hash_size; hash_table = new; hash_size = new_size; if (old) { for (i = 0; i < old_size; i++) for (p = old[i]; p ; p = tmp) { tmp = p->hash_next; cache_hash(p); } free(old); } } static struct crec **hash_bucket(char *name) { unsigned int c, val = 017465; /* Barker code - minimum self-correlation in cyclic shift */ const unsigned char *mix_tab = (const unsigned char*)typestr; while((c = (unsigned char) *name++)) { /* don't use tolower and friends here - they may be messed up by LOCALE */ if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; val = ((val << 7) | (val >> (32 - 7))) + (mix_tab[(val + c) & 0x3F] ^ c); } /* hash_size is a power of two */ return hash_table + ((val ^ (val >> 16)) & (hash_size - 1)); } static void cache_hash(struct crec *crecp) { /* maintain an invariant that all entries with F_REVERSE set are at the start of the hash-chain and all non-reverse immortal entries are at the end of the hash-chain. This allows reverse searches and garbage collection to be optimised */ char *name = cache_get_name(crecp); struct crec **up = hash_bucket(name); unsigned int flags = crecp->flags & (F_IMMORTAL | F_REVERSE); if (!(flags & F_REVERSE)) { while (*up && ((*up)->flags & F_REVERSE)) up = &((*up)->hash_next); if (flags & F_IMMORTAL) while (*up && !((*up)->flags & F_IMMORTAL)) up = &((*up)->hash_next); } /* Preserve order when inserting the same name multiple times. Do not mess up the flag invariants. */ while (*up && hostname_isequal(cache_get_name(*up), name) && flags == ((*up)->flags & (F_IMMORTAL | F_REVERSE))) up = &((*up)->hash_next); crecp->hash_next = *up; *up = crecp; } static void cache_blockdata_free(struct crec *crecp) { if (!(crecp->flags & F_NEG)) { if ((crecp->flags & F_RR) && (crecp->flags & F_KEYTAG)) blockdata_free(crecp->addr.rrblock.rrdata); #ifdef HAVE_DNSSEC else if (crecp->flags & F_DNSKEY) blockdata_free(crecp->addr.key.keydata); else if (crecp->flags & F_DS) 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_NONE; /* invalidate CNAMES pointing to this. */ if (cache_tail) cache_tail->next = crecp; else cache_head = crecp; crecp->prev = cache_tail; crecp->next = NULL; cache_tail = crecp; /* retrieve big name for further use. */ if (crecp->flags & F_BIGNAME) { crecp->name.bname->next = big_free; big_free = crecp->name.bname; crecp->flags &= ~F_BIGNAME; } cache_blockdata_free(crecp); } /* insert a new cache entry at the head of the list (youngest entry) */ static void cache_link(struct crec *crecp) { if (cache_head) /* check needed for init code */ cache_head->prev = crecp; crecp->next = cache_head; crecp->prev = NULL; cache_head = crecp; if (!cache_tail) cache_tail = crecp; } /* remove an arbitrary cache entry for promotion */ static void cache_unlink (struct crec *crecp) { if (crecp->prev) crecp->prev->next = crecp->next; else cache_head = crecp->next; if (crecp->next) crecp->next->prev = crecp->prev; else cache_tail = crecp->prev; } char *cache_get_name(struct crec *crecp) { if (crecp->flags & F_BIGNAME) return crecp->name.bname->name; else if (crecp->flags & F_NAMEP) return crecp->name.namep; return crecp->name.sname; } char *cache_get_cname_target(struct crec *crecp) { if (crecp->addr.cname.is_name_ptr) return crecp->addr.cname.target.name; else return cache_get_name(crecp->addr.cname.target.cache); } 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) || crecp->addr.cname.is_name_ptr) return 0; /* 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_DNSKEY | F_DS)) && crecp->addr.cname.uid == crecp->addr.cname.target.cache->uid) return 0; return 1; } static int is_expired(time_t now, struct crec *crecp) { /* Don't dump expired entries if they are within the accepted timeout range. The cache becomes approx. LRU. Never use expired DS or DNSKEY entries. Possible values for daemon->cache_max_expiry: -1 == serve cached content regardless how long ago it expired 0 == the option is disabled, expired content isn't served == serve cached content only if it expire less than seconds ago (where n is a positive integer) */ if (daemon->cache_max_expiry != 0 && (daemon->cache_max_expiry == -1 || difftime(now, crecp->ttd) < daemon->cache_max_expiry) && !(crecp->flags & (F_DS | F_DNSKEY))) return 0; if (crecp->flags & F_IMMORTAL) return 0; if (difftime(now, crecp->ttd) < 0) return 0; return 1; } /* Remove entries with a given UID from the cache */ unsigned int cache_remove_uid(const unsigned int uid) { int i; unsigned int removed = 0; struct crec *crecp, *tmp, **up; for (i = 0; i < hash_size; i++) for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = tmp) { tmp = crecp->hash_next; if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid) { *up = tmp; free(crecp); removed++; } else up = &crecp->hash_next; } return removed; } static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned short class, time_t now, unsigned int flags, struct crec **target_crec, unsigned int *target_uid) { /* Scan and remove old entries. If (flags & F_FORWARD) then remove any forward entries for name and any expired entries but only in the same hash bucket as name. If (flags & F_REVERSE) then remove any reverse entries for addr and any expired 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 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 ,, so that when we hit an entry which isn't reverse and is immortal, we're done. If we free a crec which is a CNAME target, return the entry and uid in target_crec and target_uid. This entry will get re-used with the same name, to preserve CNAMEs. */ struct crec *crecp, **up; (void)class; if (flags & F_FORWARD) { for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next) { if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name)) { int rrmatch = 0; if (addr && (crecp->flags & flags & F_RR)) { unsigned short rrc = (crecp->flags & F_KEYTAG) ? crecp->addr.rrblock.rrtype : crecp->addr.rrdata.rrtype; unsigned short rra = (flags & F_KEYTAG) ? addr->rrblock.rrtype : addr->rrdata.rrtype; if (rrc == rra) rrmatch = 1; } /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */ if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_NXDOMAIN)) || (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))) || rrmatch) { if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) return crecp; *up = crecp->hash_next; /* If this record is for the name we're inserting and is the target of a CNAME record. Make the new record for the same name, in the same crec, with the same uid to avoid breaking the existing CNAME. */ if (crecp->uid != UID_NONE) { if (target_crec) *target_crec = crecp; if (target_uid) *target_uid = crecp->uid; } cache_unlink(crecp); cache_free(crecp); continue; } #ifdef HAVE_DNSSEC /* Deletion has to be class-sensitive for DS and DNSKEY */ if ((flags & crecp->flags & (F_DNSKEY | F_DS)) && crecp->uid == class) { if (crecp->flags & F_CONFIG) return crecp; *up = crecp->hash_next; cache_unlink(crecp); cache_free(crecp); continue; } #endif } 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; } up = &crecp->hash_next; } } else { int i; int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ; for (i = 0; i < hash_size; i++) for (crecp = hash_table[i], up = &hash_table[i]; crecp && ((crecp->flags & F_REVERSE) || !(crecp->flags & F_IMMORTAL)); crecp = crecp->hash_next) if (is_expired(now, crecp)) { *up = crecp->hash_next; if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))) { cache_unlink(crecp); cache_free(crecp); } } else if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && (flags & crecp->flags & F_REVERSE) && (flags & crecp->flags & (F_IPV4 | F_IPV6)) && addr && memcmp(&crecp->addr, addr, addrlen) == 0) { *up = crecp->hash_next; cache_unlink(crecp); cache_free(crecp); } else up = &crecp->hash_next; } return NULL; } /* Note: The normal calling sequence is cache_start_insert cache_insert * n cache_end_insert but an abort can cause the cache_end_insert to be missed in which can the next cache_start_insert cleans things up. */ void cache_start_insert(void) { /* Free any entries which didn't get committed during the last insert due to error. */ while (new_chain) { struct crec *tmp = new_chain->next; cache_free(new_chain); new_chain = tmp; } new_chain = NULL; insert_error = 0; } struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class, time_t now, unsigned long ttl, unsigned int flags) { #ifdef HAVE_DNSSEC if (flags & (F_DNSKEY | F_DS)) { /* The DNSSEC validation process works by getting needed records into the cache, then retrying the validation until they are all in place. This can be messed up by very short TTLs, and _really_ messed up by zero TTLs, so we force the TTL to be at least long enough to do a validation. Ideally, we should use some kind of reference counting so that records are locked until the validation that asked for them is complete, but this is much easier, and just as effective. */ if (ttl < DNSSEC_MIN_TTL) ttl = DNSSEC_MIN_TTL; } else #endif { 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; } return really_insert(name, addr, class, now, ttl, flags); } static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class, time_t now, unsigned long ttl, unsigned int flags) { struct crec *new, *target_crec = NULL; union bigname *big_name = NULL; int freed_all = (flags & F_REVERSE); struct crec *free_avail = NULL; unsigned int target_uid; /* if previous insertion failed give up now. */ if (insert_error) return NULL; /* we don't cache zero-TTL records unless we're doing stale-caching. */ if (daemon->cache_max_expiry == 0 && ttl == 0) { insert_error = 1; return NULL; } /* First remove any expired entries and entries for the name/address we are currently inserting. */ if ((new = cache_scan_free(name, addr, class, now, flags, &target_crec, &target_uid))) { /* 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 or CNAME 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) && addr) { if ((flags & F_IPV4) && (new->flags & F_IPV4) && new->addr.addr4.s_addr == addr->addr4.s_addr) return new; else if ((flags & F_IPV6) && (new->flags & F_IPV6) && IN6_ARE_ADDR_EQUAL(&new->addr.addr6, &addr->addr6)) return new; } insert_error = 1; return NULL; } /* Now get a cache entry from the end of the LRU list */ if (!target_crec) while (1) { if (!(new = cache_tail)) /* no entries left - cache is too small, bail */ { insert_error = 1; return NULL; } /* Free entry at end of LRU list, use it. */ if (!(new->flags & (F_FORWARD | F_REVERSE))) break; /* End of LRU list is still in use: if we didn't scan all the hash chains for expired entries do that now. If we already tried that then it's time to start spilling things. */ /* If free_avail set, we believe that an entry has been freed. Bugs have been known to make this not true, resulting in a tight loop here. If that happens, abandon the insert. Once in this state, all inserts will probably fail. */ if (free_avail) { my_syslog(LOG_ERR, _("Internal error in cache.")); /* Log the entry we tried to delete. */ dump_cache_entry(free_avail, now); insert_error = 1; return NULL; } if (freed_all) { /* For DNSSEC records, uid holds class. */ free_avail = new; /* Must be free space now. */ /* condition valid when stale-caching */ if (difftime(now, new->ttd) < 0) daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]++; cache_scan_free(cache_get_name(new), &new->addr, new->uid, now, new->flags, NULL, NULL); } else { cache_scan_free(NULL, NULL, class, now, 0, NULL, NULL); freed_all = 1; } } /* Check if we need to and can allocate extra memory for a long name. If that fails, give up now, always succeed for DNSSEC records. */ if (name && (strlen(name) > SMALLDNAME-1)) { if (big_free) { big_name = big_free; big_free = big_free->next; } 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 if (bignames_left != 0) bignames_left--; } /* If we freed a cache entry for our name which was a CNAME target, use that. and preserve the uid, so that existing CNAMES are not broken. */ if (target_crec) { new = target_crec; new->uid = target_uid; } /* Got the rest: finally grab entry. */ cache_unlink(new); new->flags = flags; if (big_name) { new->name.bname = big_name; new->flags |= F_BIGNAME; } if (name) strcpy(cache_get_name(new), name); else *cache_get_name(new) = 0; #ifdef HAVE_DNSSEC if (flags & (F_DS | F_DNSKEY)) new->uid = class; #endif if (addr) new->addr = *addr; new->ttd = now + (time_t)ttl; new->next = new_chain; new_chain = new; return new; } /* after end of insertion, commit the new entries */ void cache_end_insert(void) { if (insert_error) return; while (new_chain) { struct crec *tmp = new_chain->next; /* drop CNAMEs which didn't find a target. */ if (is_outdated_cname_pointer(new_chain)) cache_free(new_chain); else { cache_hash(new_chain); cache_link(new_chain); daemon->metrics[METRIC_DNS_CACHE_INSERTED]++; /* If we're a child process, send this cache entry up the pipe to the master. The marshalling process is rather nasty. */ if (daemon->pipe_to_parent != -1) { char *name = cache_get_name(new_chain); ssize_t m = strlen(name); unsigned int flags = new_chain->flags; #ifdef HAVE_DNSSEC u16 class = new_chain->uid; #endif read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE); read_write(daemon->pipe_to_parent, (unsigned char *)name, m, RW_WRITE); read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), RW_WRITE); read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), RW_WRITE); read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), RW_WRITE); if (flags & F_RR) { /* A negative RR entry is possible and has no data, obviously. */ if (!(flags & F_NEG) && (flags & F_KEYTAG)) blockdata_write(new_chain->addr.rrblock.rrdata, new_chain->addr.rrblock.datalen, daemon->pipe_to_parent); } #ifdef HAVE_DNSSEC if (flags & F_DNSKEY) { read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), RW_WRITE); blockdata_write(new_chain->addr.key.keydata, new_chain->addr.key.keylen, daemon->pipe_to_parent); } else if (flags & F_DS) { read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), RW_WRITE); /* A negative DS entry is possible and has no data, obviously. */ if (!(flags & F_NEG)) blockdata_write(new_chain->addr.ds.keydata, new_chain->addr.ds.keylen, daemon->pipe_to_parent); } #endif } } new_chain = tmp; } /* signal end of cache insert in master process */ if (daemon->pipe_to_parent != -1) { ssize_t m = -1; read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE); #ifdef HAVE_DNSSEC /* Sneak out possibly updated crypto HWM values. */ m = daemon->metrics[METRIC_CRYPTO_HWM]; read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE); m = daemon->metrics[METRIC_SIG_FAIL_HWM]; read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE); m = daemon->metrics[METRIC_WORK_HWM]; read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE); #endif } new_chain = NULL; } /* A marshalled cache entry arrives on fd, read, unmarshall and insert into cache of master process. */ int cache_recv_insert(time_t now, int fd) { ssize_t m; union all_addr addr; unsigned long ttl; time_t ttd; unsigned int flags; struct crec *crecp = NULL; cache_start_insert(); while (1) { if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ)) return 0; if (m == -1) { #ifdef HAVE_DNSSEC /* Sneak in possibly updated crypto HWM. */ if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ)) return 0; if (m > daemon->metrics[METRIC_CRYPTO_HWM]) daemon->metrics[METRIC_CRYPTO_HWM] = m; if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ)) return 0; if (m > daemon->metrics[METRIC_SIG_FAIL_HWM]) daemon->metrics[METRIC_SIG_FAIL_HWM] = m; if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ)) return 0; if (m > daemon->metrics[METRIC_WORK_HWM]) daemon->metrics[METRIC_WORK_HWM] = m; #endif cache_end_insert(); return 1; } #ifdef HAVE_DNSSEC /* UDP validation moved to TCP to avoid truncation. Restart UDP validation process with the returned result. */ if (m == -2) { int status, uid, keycount, validatecount; int *keycountp, *validatecountp; size_t ret_len; struct frec *forward; if (!read_write(fd, (unsigned char *)&status, sizeof(status), RW_READ)) return 0; if (!read_write(fd, (unsigned char *)&ret_len, sizeof(ret_len), RW_READ)) return 0; if (!read_write(fd, (unsigned char *)daemon->packet, ret_len, RW_READ)) return 0; if (!read_write(fd, (unsigned char *)&forward, sizeof(forward), RW_READ)) return 0; if (!read_write(fd, (unsigned char *)&uid, sizeof(uid), RW_READ)) return 0; if (!read_write(fd, (unsigned char *)&keycount, sizeof(keycount), RW_READ)) return 0; if (!read_write(fd, (unsigned char *)&keycountp, sizeof(keycountp), RW_READ)) return 0; if (!read_write(fd, (unsigned char *)&validatecount, sizeof(validatecount), RW_READ)) return 0; if (!read_write(fd, (unsigned char *)&validatecountp, sizeof(validatecountp), RW_READ)) return 0; /* There's a tiny chance that the frec may have been freed and reused before the TCP process returns. Detect that with the uid field which is unique modulo 2^32 for each use. */ if (uid == forward->uid) { /* repatriate the work counters from the child process. */ *keycountp = keycount; *validatecountp = validatecount; if (!forward->dependent) return_reply(now, forward, (struct dns_header *)daemon->packet, ret_len, status); else pop_and_retry_query(forward, status, now); } return 1; } #endif if (!read_write(fd, (unsigned char *)daemon->namebuff, m, RW_READ) || !read_write(fd, (unsigned char *)&ttd, sizeof(ttd), RW_READ) || !read_write(fd, (unsigned char *)&flags, sizeof(flags), RW_READ) || !read_write(fd, (unsigned char *)&addr, sizeof(addr), RW_READ)) return 0; daemon->namebuff[m] = 0; ttl = difftime(ttd, now); if (flags & F_CNAME) { struct crec *newc = really_insert(daemon->namebuff, NULL, C_IN, now, ttl, flags); /* This relies on the fact that the target of a CNAME immediately precedes it because of the order of extraction in extract_addresses, and the order reversal on the new_chain. */ if (newc) { newc->addr.cname.is_name_ptr = 0; if (!crecp) newc->addr.cname.target.cache = NULL; else { next_uid(crecp); newc->addr.cname.target.cache = crecp; newc->addr.cname.uid = crecp->uid; } } } else { unsigned short class = C_IN; if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG) && !(addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen))) return 0; #ifdef HAVE_DNSSEC if (flags & F_DNSKEY) { if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ) || !(addr.key.keydata = blockdata_read(fd, addr.key.keylen))) return 0; } else if (flags & F_DS) { if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ) || (!(flags & F_NEG) && !(addr.key.keydata = blockdata_read(fd, addr.key.keylen)))) return 0; } #endif crecp = really_insert(daemon->namebuff, &addr, class, now, ttl, flags); } } } int cache_find_non_terminal(char *name, time_t now) { struct crec *crecp; for (crecp = *hash_bucket(name); crecp; crecp = crecp->hash_next) if (!is_outdated_cname_pointer(crecp) && !is_expired(now, crecp) && (crecp->flags & F_FORWARD) && !(crecp->flags & F_NXDOMAIN) && hostname_isequal(name, cache_get_name(crecp))) return 1; return 0; } 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) || option_bool(OPT_NORR); prot &= ~F_NO_RR; if (crecp) /* iterating */ ans = crecp->next; else { /* first search, look for relevant entries and push to top of list also free anything which has expired */ struct crec *next, **up, **insert = NULL, **chainp = &ans; unsigned int ins_flags = 0; for (up = hash_bucket(name), crecp = *up; crecp; crecp = next) { next = crecp->hash_next; if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp)) { if ((crecp->flags & F_FORWARD) && (crecp->flags & prot) && hostname_isequal(cache_get_name(crecp), name)) { if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) { *chainp = crecp; chainp = &crecp->next; } else { cache_unlink(crecp); cache_link(crecp); } /* Move all but the first entry up the hash chain this implements round-robin. Make sure that re-ordering doesn't break the hash-chain order invariants. */ if (insert && (crecp->flags & (F_REVERSE | F_IMMORTAL)) == ins_flags) { *up = crecp->hash_next; crecp->hash_next = *insert; *insert = crecp; insert = &crecp->hash_next; } else { if (!insert && !no_rr) { insert = up; ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL); } up = &crecp->hash_next; } } else /* case : not expired, incorrect entry. */ up = &crecp->hash_next; } else { /* expired entry, free it */ *up = crecp->hash_next; if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))) { cache_unlink(crecp); cache_free(crecp); } } } *chainp = cache_head; } if (ans && (ans->flags & F_FORWARD) && (ans->flags & prot) && hostname_isequal(cache_get_name(ans), name)) return ans; return NULL; } struct crec *cache_find_by_addr(struct crec *crecp, union all_addr *addr, time_t now, unsigned int prot) { struct crec *ans; int addrlen = (prot == F_IPV6) ? IN6ADDRSZ : INADDRSZ; if (crecp) /* iterating */ ans = crecp->next; else { /* first search, look for relevant entries and push to top of list also free anything which has expired. All the reverse entries are at the start of the hash chain, so we can give up when we find the first non-REVERSE one. */ int i; struct crec **up, **chainp = &ans; for (i=0; iflags & F_REVERSE); crecp = crecp->hash_next) if (!is_expired(now, crecp)) { if ((crecp->flags & prot) && memcmp(&crecp->addr, addr, addrlen) == 0) { if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) { *chainp = crecp; chainp = &crecp->next; } else { cache_unlink(crecp); cache_link(crecp); } } up = &crecp->hash_next; } else { *up = crecp->hash_next; if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))) { cache_unlink(crecp); cache_free(crecp); } } *chainp = cache_head; } if (ans && (ans->flags & F_REVERSE) && (ans->flags & prot) && memcmp(&ans->addr, addr, addrlen) == 0) return ans; return NULL; } static void add_hosts_entry(struct crec *cache, union all_addr *addr, int addrlen, unsigned int index, struct crec **rhash, int hashsz) { int i; unsigned int j; struct crec *lookup = NULL; /* Remove duplicates in hosts files. */ while ((lookup = cache_find_by_name(lookup, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6)))) if ((lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0) { free(cache); return; } /* Ensure there is only one address -> name mapping (first one trumps) 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 (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, 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 { /* 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, addrlen); cache_hash(cache); make_non_terminals(cache); } static int eatspace(FILE *f) { int c, nl = 0; while (1) { if ((c = getc(f)) == '#') while (c != '\n' && c != EOF) c = getc(f); if (c == EOF) return 1; if (!isspace(c)) { ungetc(c, f); return nl; } if (c == '\n') nl++; } } static int gettok(FILE *f, char *token) { int c, count = 0; while (1) { if ((c = getc(f)) == EOF) return (count == 0) ? -1 : 1; if (isspace(c) || c == '#') { ungetc(c, f); return eatspace(f); } if (count < (MAXDNAME - 1)) { token[count++] = c; token[count] = 0; } } } 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 names_done = 0, name_count = cache_size, lineno = 1; unsigned int flags = 0; union all_addr addr; int atnl, addrlen = 0; if (!f) { my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno)); return cache_size; } lineno += eatspace(f); while ((atnl = gettok(f, token)) != -1) { 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.addr4); } else if (inet_pton(AF_INET6, token, &addr) > 0) { flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6; addrlen = IN6ADDRSZ; domain_suffix = get_domain6(&addr.addr6); } else { my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno); while (atnl == 0) atnl = gettok(f, token); lineno += atnl; continue; } /* rehash every 1000 names. */ if (rhash && ((name_count - cache_size) > 1000)) { rehash(name_count); cache_size = name_count; } while (atnl == 0) { struct crec *cache; int fqdn, nomem; char *canon; if ((atnl = gettok(f, token)) == -1) break; fqdn = !!strchr(token, '.'); if ((canon = canonicalise(token, &nomem))) { /* If set, add a version of the name with a default domain appended */ if (option_bool(OPT_EXPAND) && domain_suffix && !fqdn && (cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 2 + strlen(domain_suffix)))) { strcpy(cache->name.sname, canon); strcat(cache->name.sname, "."); strcat(cache->name.sname, domain_suffix); cache->flags = flags; cache->ttd = daemon->local_ttl; add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz); name_count++; names_done++; } if ((cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 1))) { strcpy(cache->name.sname, canon); cache->flags = flags; cache->ttd = daemon->local_ttl; add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz); name_count++; names_done++; } free(canon); } else if (!nomem) my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno); } lineno += atnl; } fclose(f); if (rhash) rehash(name_count); my_syslog(LOG_INFO, _("read %s - %d names"), filename, names_done); return name_count; } void cache_reload(void) { struct crec *cache, **up, *tmp; int revhashsz, i, total_size = daemon->cachesize; struct hostsfile *ah; struct host_record *hr; struct name_list *nl; struct cname *a; struct crec lrec; struct mx_srv_record *mx; struct txt_record *txt; struct interface_name *intr; struct ptr_record *ptr; struct naptr *naptr; #ifdef HAVE_DNSSEC struct ds_config *ds; #endif daemon->metrics[METRIC_DNS_CACHE_INSERTED] = 0; daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED] = 0; for (i=0; ihash_next; if (cache->flags & (F_HOSTS | F_CONFIG)) { *up = cache->hash_next; free(cache); } else if (!(cache->flags & F_DHCP)) { *up = cache->hash_next; if (cache->flags & F_BIGNAME) { cache->name.bname->next = big_free; big_free = cache->name.bname; } cache->flags = 0; } else up = &cache->hash_next; } /* Add locally-configured CNAMEs to the cache */ for (a = daemon->cnames; a; a = a->next) if (a->alias[1] != '*' && ((cache = whine_malloc(SIZEOF_POINTER_CREC)))) { cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG; cache->ttd = a->ttl; cache->name.namep = a->alias; cache->addr.cname.target.name = a->target; cache->addr.cname.is_name_ptr = 1; cache->uid = UID_NONE; cache_hash(cache); make_non_terminals(cache); } #ifdef HAVE_DNSSEC for (ds = daemon->ds; ds; ds = ds->next) if ((cache = whine_malloc(SIZEOF_POINTER_CREC)) && (cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen))) { cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP; cache->ttd = daemon->local_ttl; 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); make_non_terminals(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->flags & HR_4) && (cache = whine_malloc(SIZEOF_POINTER_CREC))) { cache->name.namep = nl->name; cache->ttd = hr->ttl; cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG; add_hosts_entry(cache, (union all_addr *)&hr->addr, INADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz); } if ((hr->flags & HR_6) && (cache = whine_malloc(SIZEOF_POINTER_CREC))) { cache->name.namep = nl->name; cache->ttd = hr->ttl; cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG; add_hosts_entry(cache, (union all_addr *)&hr->addr6, IN6ADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz); } } if (option_bool(OPT_NO_HOSTS) && !daemon->addn_hosts) { if (daemon->cachesize > 0) my_syslog(LOG_INFO, _("cleared cache")); } 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); } /* Make non-terminal records for all locally-define RRs */ lrec.flags = F_FORWARD | F_CONFIG | F_NAMEP | F_IMMORTAL; for (txt = daemon->txt; txt; txt = txt->next) { lrec.name.namep = txt->name; make_non_terminals(&lrec); } for (naptr = daemon->naptr; naptr; naptr = naptr->next) { lrec.name.namep = naptr->name; make_non_terminals(&lrec); } for (mx = daemon->mxnames; mx; mx = mx->next) { lrec.name.namep = mx->name; make_non_terminals(&lrec); } for (intr = daemon->int_names; intr; intr = intr->next) { lrec.name.namep = intr->name; make_non_terminals(&lrec); } for (ptr = daemon->ptr; ptr; ptr = ptr->next) { lrec.name.namep = ptr->name; make_non_terminals(&lrec); } #ifdef HAVE_INOTIFY set_dynamic_inotify(AH_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz); #endif } #ifdef HAVE_DHCP struct in_addr a_record_from_hosts(char *name, time_t now) { struct crec *crecp = NULL; struct in_addr ret; /* If no DNS service, cache not initialised. */ if (daemon->port != 0) while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4))) if (crecp->flags & F_HOSTS) return crecp->addr.addr4; my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name); ret.s_addr = 0; return ret; } void cache_unhash_dhcp(void) { struct crec *cache, **up; int i; for (i=0; ihash_next) if (cache->flags & F_DHCP) { *up = cache->hash_next; cache->next = dhcp_spare; dhcp_spare = cache; } else up = &cache->hash_next; } void cache_add_dhcp_entry(char *host_name, int prot, union all_addr *host_address, time_t ttd) { struct crec *crec = NULL, *fail_crec = NULL; unsigned int flags = F_IPV4; int in_hosts = 0; size_t addrlen = sizeof(struct in_addr); if (prot == AF_INET6) { flags = F_IPV6; addrlen = sizeof(struct in6_addr); } 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 | F_CONFIG)) { 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, daemon->addrbuff); else if (memcmp(&crec->addr, host_address, addrlen) == 0) in_hosts = 1; else fail_crec = crec; } else if (!(crec->flags & F_DHCP)) { cache_scan_free(host_name, NULL, C_IN, 0, crec->flags & (flags | F_CNAME | F_FORWARD), NULL, NULL); /* scan_free deletes all addresses associated with name */ break; } } /* if in hosts, don't need DHCP record */ if (in_hosts) return; /* Name in hosts, address doesn't match */ if (fail_crec) { inet_ntop(prot, &fail_crec->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, (union all_addr *)host_address, 0, flags))) { if (crec->flags & F_NEG) { flags |= F_REVERSE; cache_scan_free(NULL, (union all_addr *)host_address, C_IN, 0, flags, NULL, NULL); } } else flags |= F_REVERSE; if ((crec = dhcp_spare)) dhcp_spare = dhcp_spare->next; else /* need new one */ crec = whine_malloc(SIZEOF_POINTER_CREC); if (crec) /* malloc may fail */ { crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD; if (ttd == 0) crec->flags |= F_IMMORTAL; else crec->ttd = ttd; crec->addr = *host_address; crec->name.namep = host_name; crec->uid = UID_NONE; cache_hash(crec); make_non_terminals(crec); } } #endif /* Called when we put a local or DHCP name into the cache. Creates empty cache entries for subnames (ie, for three.two.one, for two.one and one), without F_IPV4 or F_IPV6 or F_CNAME set. These convert NXDOMAIN answers to NoData ones. */ static void make_non_terminals(struct crec *source) { char *name = cache_get_name(source); struct crec *crecp, *tmp, **up; int type = F_HOSTS | F_CONFIG; #ifdef HAVE_DHCP if (source->flags & F_DHCP) type = F_DHCP; #endif /* First delete any empty entries for our new real name. Note that we only delete empty entries deriving from DHCP for a new DHCP-derived entry and vice-versa for HOSTS and CONFIG. This ensures that non-terminals from DHCP go when we reload DHCP and for HOSTS/CONFIG when we re-read. */ for (up = hash_bucket(name), crecp = *up; crecp; crecp = tmp) { tmp = crecp->hash_next; if (!is_outdated_cname_pointer(crecp) && (crecp->flags & F_FORWARD) && (crecp->flags & type) && !(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_DNSKEY | F_DS | F_RR)) && hostname_isequal(name, cache_get_name(crecp))) { *up = crecp->hash_next; #ifdef HAVE_DHCP if (type & F_DHCP) { crecp->next = dhcp_spare; dhcp_spare = crecp; } else #endif free(crecp); break; } else up = &crecp->hash_next; } while ((name = strchr(name, '.'))) { name++; /* Look for one existing, don't need another */ for (crecp = *hash_bucket(name); crecp; crecp = crecp->hash_next) if (!is_outdated_cname_pointer(crecp) && (crecp->flags & F_FORWARD) && (crecp->flags & type) && hostname_isequal(name, cache_get_name(crecp))) break; if (crecp) { /* If the new name expires later, transfer that time to empty non-terminal entry. */ if (!(crecp->flags & F_IMMORTAL)) { if (source->flags & F_IMMORTAL) crecp->flags |= F_IMMORTAL; else if (difftime(crecp->ttd, source->ttd) < 0) crecp->ttd = source->ttd; } continue; } #ifdef HAVE_DHCP if ((source->flags & F_DHCP) && dhcp_spare) { crecp = dhcp_spare; dhcp_spare = dhcp_spare->next; } else #endif crecp = whine_malloc(SIZEOF_POINTER_CREC); if (crecp) { crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_RR | F_DNSKEY | F_DS | F_REVERSE); if (!(crecp->flags & F_IMMORTAL)) crecp->ttd = source->ttd; crecp->name.namep = name; cache_hash(crecp); } } } #ifndef NO_ID 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", daemon->metrics[METRIC_DNS_CACHE_INSERTED]); break; case TXT_STAT_EVICTIONS: sprintf(buff+1, "%d", daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]); break; case TXT_STAT_MISSES: sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]); break; case TXT_STAT_HITS: sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]); break; #ifdef HAVE_AUTH case TXT_STAT_AUTH: sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_AUTH_ANSWERED]); 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_MARK; for (serv = daemon->servers; serv; serv = serv->next) if (!(serv->flags & SERV_MARK)) { 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_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr)) { serv1->flags |= SERV_MARK; 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) { /* expand buffer if necessary */ newlen = bytes_needed + 1 + bufflen - bytes_avail; if (!(new = whine_realloc(buff, newlen))) return 0; 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; } #endif /* There can be names in the cache containing control chars, don't mess up logging or open security holes. Also convert to all-LC so that 0x20-encoding doesn't make logs look like ransom notes made out of letters cut from a newspaper. Overwrites daemon->workspacename */ static char *sanitise(char *name) { unsigned char *r = (unsigned char *)name; if (name) { char *d = name = daemon->workspacename; for (; *r; r++, d++) if (!isprint((int)*r)) return ""; else { unsigned char c = *r; *d = (char)((c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c); } *d = 0; } return name; } static void dump_cache_entry(struct crec *cache, time_t now) { (void)now; static char *buff = NULL; char *p, *t = " "; char *a = daemon->addrbuff, *n = cache_get_name(cache); /* String length is limited below */ if (!buff && !(buff = whine_malloc(150))) return; p = buff; *a = 0; if (cache->flags & F_REVERSE) { if ((cache->flags & F_NEG)) n = ""; } else { if (strlen(n) == 0) n = ""; } p += sprintf(p, "%-30.30s ", sanitise(n)); if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache)) a = sanitise(cache_get_cname_target(cache)); else if (cache->flags & F_RR) { if (cache->flags & F_KEYTAG) sprintf(a, "%s", querystr(NULL, cache->addr.rrblock.rrtype)); else sprintf(a, "%s", querystr(NULL, cache->addr.rrdata.rrtype)); } #ifdef HAVE_DNSSEC else if (cache->flags & F_DS) { if (!(cache->flags & F_NEG)) sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag, cache->addr.ds.algo, cache->addr.ds.digest); } 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 = daemon->addrbuff; if (cache->flags & F_IPV4) inet_ntop(AF_INET, &cache->addr, a, ADDRSTRLEN); else if (cache->flags & F_IPV6) inet_ntop(AF_INET6, &cache->addr, a, ADDRSTRLEN); } if (cache->flags & F_IPV4) t = "4"; else if (cache->flags & F_IPV6) t = "6"; else if (cache->flags & F_CNAME) t = "C"; else if (cache->flags & F_RR) t = "T"; #ifdef HAVE_DNSSEC else if (cache->flags & F_DS) t = "S"; else if (cache->flags & F_DNSKEY) t = "K"; #endif else if (!(cache->flags & F_NXDOMAIN)) /* non-terminal */ t = "!"; p += sprintf(p, "%-40.40s %s%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_CONFIG ? "C" : " ", cache->flags & F_DNSSECOK ? "V" : " "); #ifdef HAVE_BROKEN_RTC p += sprintf(p, "%-24lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now)); #else p += sprintf(p, "%-24.24s", cache->flags & F_IMMORTAL ? "" : ctime(&(cache->ttd))); #endif if(cache->flags & (F_HOSTS | F_CONFIG) && cache->uid > 0) p += sprintf(p, " %-40.40s", record_source(cache->uid)); my_syslog(LOG_INFO, "%s", buff); } void dump_cache(time_t now) { struct server *serv, *serv1; 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, daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED], daemon->metrics[METRIC_DNS_CACHE_INSERTED]); my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"), daemon->metrics[METRIC_DNS_QUERIES_FORWARDED], daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]); if (daemon->cache_max_expiry != 0) my_syslog(LOG_INFO, _("queries answered from stale cache %u"), daemon->metrics[METRIC_DNS_STALE_ANSWERED]); #ifdef HAVE_AUTH my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->metrics[METRIC_DNS_AUTH_ANSWERED]); #endif #ifdef HAVE_DNSSEC my_syslog(LOG_INFO, _("DNSSEC per-query subqueries HWM %u"), daemon->metrics[METRIC_WORK_HWM]); my_syslog(LOG_INFO, _("DNSSEC per-query crypto work HWM %u"), daemon->metrics[METRIC_CRYPTO_HWM]); my_syslog(LOG_INFO, _("DNSSEC per-RRSet signature fails HWM %u"), daemon->metrics[METRIC_SIG_FAIL_HWM]); #endif blockdata_report(); my_syslog(LOG_INFO, _("child processes for TCP requests: in use %zu, highest since last SIGUSR1 %zu, max allowed %zu."), daemon->metrics[METRIC_TCP_CONNECTIONS], daemon->max_procs_used, daemon->max_procs); daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS]; /* sum counts from different records for same server */ for (serv = daemon->servers; serv; serv = serv->next) serv->flags &= ~SERV_MARK; for (serv = daemon->servers; serv; serv = serv->next) if (!(serv->flags & SERV_MARK)) { int port; unsigned int queries = 0, failed_queries = 0, nxdomain_replies = 0, retrys = 0; unsigned int sigma_latency = 0, count_latency = 0; for (serv1 = serv; serv1; serv1 = serv1->next) if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr)) { serv1->flags |= SERV_MARK; queries += serv1->queries; failed_queries += serv1->failed_queries; nxdomain_replies += serv1->nxdomain_replies; retrys += serv1->retrys; sigma_latency += serv1->query_latency; count_latency++; } port = prettyprint_addr(&serv->addr, daemon->addrbuff); my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums"), daemon->addrbuff, port, queries, retrys, failed_queries, nxdomain_replies, sigma_latency/count_latency); } if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG)) { struct crec *cache; int i; my_syslog(LOG_INFO, "Host Address Flags Expires Source"); my_syslog(LOG_INFO, "------------------------------ ---------------------------------------- ---------- ------------------------ ------------"); for (i=0; ihash_next) dump_cache_entry(cache, now); } } char *record_source(unsigned int index) { struct hostsfile *ah; #ifdef HAVE_INOTIFY struct dyndir *dd; #endif 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 /* Dynamic directories contain multiple files */ for (dd = daemon->dynamic_dirs; dd; dd = dd->next) for (ah = dd->files; ah; ah = ah->next) if (ah->index == index) return ah->fname; #endif return ""; } static char *querystr(char *desc, unsigned short type) { unsigned int i; 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) { types = typestr[i].name; len = strlen(types); break; } if (desc) { len += 2; /* braces */ len += strlen(desc); } len++; /* terminator */ if (!buff || bufflen < len) { if (buff) free(buff); else if (len < 20) len = 20; buff = whine_malloc(len); bufflen = len; } if (buff) { if (desc) { if (types) sprintf(buff, "%s[%s]", desc, types); else sprintf(buff, "%s[type=%d]", desc, type); } else { if (types) sprintf(buff, "<%s>", types); else sprintf(buff, "", type); } } return buff ? buff : ""; } static char *edestr(int ede) { switch (ede) { case EDE_OTHER: return "other"; case EDE_USUPDNSKEY: return "unsupported DNSKEY algorithm"; case EDE_USUPDS: return "unsupported DS digest"; case EDE_STALE: return "stale answer"; case EDE_FORGED: return "forged"; case EDE_DNSSEC_IND: return "DNSSEC indeterminate"; case EDE_DNSSEC_BOGUS: return "DNSSEC bogus"; case EDE_SIG_EXP: return "DNSSEC signature expired"; case EDE_SIG_NYV: return "DNSSEC sig not yet valid"; case EDE_NO_DNSKEY: return "DNSKEY missing"; case EDE_NO_RRSIG: return "RRSIG missing"; case EDE_NO_ZONEKEY: return "no zone key bit set"; case EDE_NO_NSEC: return "NSEC(3) missing"; case EDE_CACHED_ERR: return "cached error"; case EDE_NOT_READY: return "not ready"; case EDE_BLOCKED: return "blocked"; case EDE_CENSORED: return "censored"; case EDE_FILTERED: return "filtered"; case EDE_PROHIBITED: return "prohibited"; case EDE_STALE_NXD: return "stale NXDOMAIN"; case EDE_NOT_AUTH: return "not authoritative"; case EDE_NOT_SUP: return "not supported"; case EDE_NO_AUTH: return "no reachable authority"; case EDE_NETERR: return "network error"; case EDE_INVALID_DATA: return "invalid data"; case EDE_SIG_E_B_V: return "signature expired before valid"; case EDE_TOO_EARLY: return "too early"; case EDE_UNS_NS3_ITER: return "unsupported NSEC3 iterations value"; case EDE_UNABLE_POLICY: return "uanble to conform to policy"; case EDE_SYNTHESIZED: return "synthesized"; default: return "unknown"; } } void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, unsigned short type) { char *source, *dest; char *verb = "is"; char *extra = ""; char *gap = " "; char portstring[7]; /* space for # */ if (!option_bool(OPT_LOG)) return; /* build query type string if requested */ if (!(flags & (F_SERVER | F_IPSET)) && type > 0) arg = querystr(arg, type); dest = arg; #ifdef HAVE_DNSSEC if ((flags & F_DNSSECOK) && option_bool(OPT_EXTRALOG)) extra = " (DNSSEC signed)"; #endif name = sanitise(name); if (addr) { dest = daemon->addrbuff; if (flags & F_RR) { if (flags & F_KEYTAG) dest = querystr(NULL, addr->rrblock.rrtype); else dest = querystr(NULL, addr->rrdata.rrtype); } else if (flags & F_KEYTAG) sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest); else if (flags & F_RCODE) { unsigned int rcode = addr->log.rcode; if (rcode == SERVFAIL) dest = "SERVFAIL"; else if (rcode == REFUSED) dest = "REFUSED"; else if (rcode == NOTIMP) dest = "not implemented"; else sprintf(daemon->addrbuff, "%u", rcode); if (addr->log.ede != EDE_UNSET) { extra = daemon->addrbuff; sprintf(extra, " (EDE: %s)", edestr(addr->log.ede)); } } else if (flags & (F_IPV4 | F_IPV6)) { inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6, addr, daemon->addrbuff, ADDRSTRLEN); if ((flags & F_SERVER) && type != NAMESERVER_PORT) { extra = portstring; sprintf(portstring, "#%u", type); } } else dest = arg; } if (flags & F_REVERSE) { dest = name; name = daemon->addrbuff; } if (flags & F_NEG) { if (flags & F_NXDOMAIN) dest = "NXDOMAIN"; else { if (flags & F_IPV4) dest = "NODATA-IPv4"; else if (flags & F_IPV6) dest = "NODATA-IPv6"; else dest = "NODATA"; } } else if (flags & F_CNAME) dest = ""; else if (flags & F_RRNAME) dest = arg; if (flags & F_CONFIG) source = "config"; else if (flags & F_DHCP) source = "DHCP"; else if (flags & F_HOSTS) source = arg; else if (flags & F_UPSTREAM) source = "reply"; else if (flags & F_SECSTAT) { if (addr && addr->log.ede != EDE_UNSET && option_bool(OPT_EXTRALOG)) { extra = daemon->addrbuff; sprintf(extra, " (EDE: %s)", edestr(addr->log.ede)); } source = "validation"; dest = arg; } else if (flags & F_AUTH) source = "auth"; else if (flags & F_DNSSEC) { source = arg; verb = "to"; } else if (flags & F_SERVER) { source = "forwarded"; verb = "to"; } else if (flags & F_QUERY) { source = arg; verb = "from"; } else if (flags & F_IPSET) { source = type ? "ipset add" : "nftset add"; dest = name; name = arg; verb = daemon->addrbuff; } else if (flags & F_STALE) source = "cached-stale"; else source = "cached"; if (!name) gap = name = ""; else if (!name[0]) name = "."; if (option_bool(OPT_EXTRALOG)) { int display_id = daemon->log_display_id; char *proto = ""; if (option_bool(OPT_LOG_PROTO)) proto = (display_id < 0) ? "TCP " : "UDP "; if (display_id < 0) display_id = -display_id; if (flags & F_NOEXTRA || !daemon->log_source_addr) my_syslog(LOG_INFO, "%s%u %s %s%s%s %s%s", proto, display_id, source, name, gap, verb, dest, extra); else { int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2); my_syslog(LOG_INFO, "%s%u %s/%u %s %s%s%s %s%s", proto, display_id, daemon->addrbuff2, port, source, name, gap, verb, dest, extra); } } else my_syslog(LOG_INFO, "%s %s%s%s %s%s", source, name, gap, verb, dest, extra); } dnsmasq-2.91/src/netlink.c0000664000175000017500000002631414765043257013717 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_LINUX_NETWORK #include #include #include /* Blergh. Radv does this, so that's our excuse. */ #ifndef SOL_NETLINK #define SOL_NETLINK 270 #endif #ifndef NETLINK_NO_ENOBUFS #define NETLINK_NO_ENOBUFS 5 #endif /* linux 2.6.19 buggers up the headers, patch it up here. */ #ifndef IFA_RTA # define IFA_RTA(r) \ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) # include #endif #ifndef NDA_RTA # define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) #endif /* Used to request refresh of addresses or routes just once, * when multiple changes might be announced. */ enum async_states { STATE_NEWADDR = (1 << 0), STATE_NEWROUTE = (1 << 1), }; static struct iovec iov; static u32 netlink_pid; static unsigned nl_async(struct nlmsghdr *h, unsigned state); static void nl_multicast_state(unsigned state); char *netlink_init(void) { struct sockaddr_nl addr; socklen_t slen = sizeof(addr); addr.nl_family = AF_NETLINK; addr.nl_pad = 0; addr.nl_pid = 0; /* autobind */ addr.nl_groups = RTMGRP_IPV4_ROUTE; addr.nl_groups |= RTMGRP_IPV4_IFADDR; addr.nl_groups |= RTMGRP_IPV6_ROUTE; addr.nl_groups |= RTMGRP_IPV6_IFADDR; /* May not be able to have permission to set multicast groups don't die in that case */ if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1) { if (bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { addr.nl_groups = 0; if (errno != EPERM || bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) daemon->netlinkfd = -1; } } if (daemon->netlinkfd == -1 || getsockname(daemon->netlinkfd, (struct sockaddr *)&addr, &slen) == -1) die(_("cannot create netlink socket: %s"), NULL, EC_MISC); /* save pid assigned by bind() and retrieved by getsockname() */ netlink_pid = addr.nl_pid; iov.iov_len = 100; iov.iov_base = safe_malloc(iov.iov_len); return NULL; } static ssize_t netlink_recv(int flags) { struct msghdr msg; struct sockaddr_nl nladdr; ssize_t rc; while (1) { msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_name = &nladdr; msg.msg_namelen = sizeof(nladdr); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_flags = 0; while ((rc = recvmsg(daemon->netlinkfd, &msg, flags | MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR); /* make buffer big enough */ if (rc != -1 && (msg.msg_flags & MSG_TRUNC)) { /* Very new Linux kernels return the actual size needed, older ones always return truncated size */ if ((size_t)rc == iov.iov_len) { if (expand_buf(&iov, rc + 100)) continue; } else expand_buf(&iov, rc); } /* read it for real */ msg.msg_flags = 0; while ((rc = recvmsg(daemon->netlinkfd, &msg, flags)) == -1 && errno == EINTR); /* Make sure this is from the kernel */ if (rc == -1 || nladdr.nl_pid == 0) break; } /* discard stuff which is truncated at this point (expand_buf() may fail) */ if (msg.msg_flags & MSG_TRUNC) { rc = -1; errno = ENOMEM; } return rc; } /* family = AF_UNSPEC finds ARP table entries. family = AF_LOCAL finds MAC addresses. returns 0 on failure, 1 on success, -1 when restart is required */ int iface_enumerate(int family, void *parm, callback_t callback) { struct sockaddr_nl addr; struct nlmsghdr *h; ssize_t len; static unsigned int seq = 0; int callback_ok = 1; unsigned state = 0; struct { struct nlmsghdr nlh; struct rtgenmsg g; } req; memset(&req, 0, sizeof(req)); memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; 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; req.nlh.nlmsg_len = sizeof(req); 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(retry_send(sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0, (struct sockaddr *)&addr, sizeof(addr)))); if (errno != 0) return 0; while (1) { if ((len = netlink_recv(0)) == -1) { if (errno == ENOBUFS) { nl_multicast_state(state); return -1; } return 0; } for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) if (h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR) { /* May be multicast arriving async */ state = nl_async(h, state); } else if (h->nlmsg_seq != seq) { /* May be part of incomplete response to previous request after ENOBUFS. Drop it. */ continue; } else if (h->nlmsg_type == NLMSG_DONE) 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); unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)); if (ifa->ifa_family == family) { if (ifa->ifa_family == AF_INET) { struct in_addr netmask, addr, broadcast; char *label = NULL; netmask.s_addr = htonl(~(in_addr_t)0 << (32 - ifa->ifa_prefixlen)); addr.s_addr = 0; broadcast.s_addr = 0; while (RTA_OK(rta, len1)) { if (rta->rta_type == IFA_LOCAL) 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 && callback_ok) if (!callback.af_inet(addr, ifa->ifa_index, label, netmask, broadcast, parm)) callback_ok = 0; } 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)) { /* * Important comment: (from if_addr.h) * IFA_ADDRESS is prefix address, rather than local interface address. * It makes no difference for normally configured broadcast interfaces, * but for point-to-point IFA_ADDRESS is DESTINATION address, * local address is supplied in IFA_LOCAL attribute. */ if (rta->rta_type == IFA_LOCAL) addrp = ((struct in6_addr *)(rta+1)); else if (rta->rta_type == IFA_ADDRESS && !addrp) 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 (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.af_inet6(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope), (int)(ifa->ifa_index), flags, (unsigned int)preferred, (unsigned int)valid, parm)) callback_ok = 0; } } } else if (h->nlmsg_type == RTM_NEWNEIGH && family == AF_UNSPEC) { struct ndmsg *neigh = NLMSG_DATA(h); struct rtattr *rta = NDA_RTA(neigh); unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*neigh)); size_t maclen = 0; char *inaddr = NULL, *mac = NULL; while (RTA_OK(rta, len1)) { if (rta->rta_type == NDA_DST) inaddr = (char *)(rta+1); else if (rta->rta_type == NDA_LLADDR) { maclen = rta->rta_len - sizeof(struct rtattr); mac = (char *)(rta+1); } rta = RTA_NEXT(rta, len1); } if (!(neigh->ndm_state & (NUD_NOARP | NUD_INCOMPLETE | NUD_FAILED)) && inaddr && mac && callback_ok) if (!callback.af_unspec(neigh->ndm_family, inaddr, mac, maclen, parm)) 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.af_local((int)link->ifi_index, (unsigned int)link->ifi_type, mac, maclen, parm)) callback_ok = 0; } #endif } } static void nl_multicast_state(unsigned state) { ssize_t len; struct nlmsghdr *h; do { /* don't risk blocking reading netlink messages here. */ while ((len = netlink_recv(MSG_DONTWAIT)) != -1) for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) state = nl_async(h, state); } while (errno == ENOBUFS); } void netlink_multicast(void) { unsigned state = 0; nl_multicast_state(state); } static unsigned nl_async(struct nlmsghdr *h, unsigned state) { if (h->nlmsg_type == NLMSG_ERROR) { 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 && (state & STATE_NEWROUTE)==0) { /* We arrange to receive netlink multicast messages whenever the network route is added. If this happens and we still have a DNS packet in the buffer, we re-send it. 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); if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK && (rtm->rtm_table == RT_TABLE_MAIN || rtm->rtm_table == RT_TABLE_LOCAL)) { queue_event(EVENT_NEWROUTE); state |= STATE_NEWROUTE; } } else if ((h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) && (state & STATE_NEWADDR)==0) { queue_event(EVENT_NEWADDR); state |= STATE_NEWADDR; } return state; } #endif /* HAVE_LINUX_NETWORK */ dnsmasq-2.91/src/rrfilter.c0000664000175000017500000002520014765043257014075 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ /* Code to safely remove RRs from a DNS answer */ #include "dnsmasq.h" /* Go through a domain name, find "pointers" and fix them up based on how many bytes we've chopped out of the packet, or check they don't point into an elided part. */ static int check_name(unsigned char **namep, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count) { unsigned char *ansp = *namep; while(1) { unsigned int label_type; if (!CHECK_LEN(header, ansp, plen, 1)) return 0; label_type = (*ansp) & 0xc0; if (label_type == 0xc0) { /* pointer for compression. */ unsigned int offset; int i; unsigned char *p; if (!CHECK_LEN(header, ansp, plen, 2)) return 0; offset = ((*ansp++) & 0x3f) << 8; offset |= *ansp++; p = offset + (unsigned char *)header; for (i = 0; i < rr_count; i++) if (p < rrs[i]) break; else if (i & 1) offset -= rrs[i] - rrs[i-1]; /* does the pointer end up in an elided RR? */ if (i & 1) return 0; /* No, scale the pointer */ if (fixup) { ansp -= 2; *ansp++ = (offset >> 8) | 0xc0; *ansp++ = offset & 0xff; } break; } else if (label_type == 0x80) return 0; /* reserved */ else if (label_type == 0x40) { /* Extended label type */ unsigned int count; if (!CHECK_LEN(header, ansp, plen, 2)) return 0; if (((*ansp++) & 0x3f) != 1) return 0; /* we only understand bitstrings */ count = *(ansp++); /* Bits in bitstring */ if (count == 0) /* count == 0 means 256 bits */ ansp += 32; else ansp += ((count-1)>>3)+1; } else { /* label type == 0 Bottom six bits is length */ unsigned int len = (*ansp++) & 0x3f; if (!ADD_RDLEN(header, ansp, plen, len)) return 0; if (len == 0) break; /* zero length label marks the end. */ } } *namep = ansp; return 1; } /* Go through RRs and check or fixup the domain names contained within */ static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count) { int i, j, type, class, rdlen; unsigned char *pp; for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++) { pp = p; if (!(p = skip_name(p, header, plen, 10))) return 0; GETSHORT(type, p); GETSHORT(class, p); p += 4; /* TTL */ GETSHORT(rdlen, p); /* If this RR is to be elided, don't fix up its contents */ for (j = 0; j < rr_count; j += 2) if (rrs[j] == pp) break; if (j >= rr_count) { /* fixup name of RR */ if (!check_name(&pp, header, plen, fixup, rrs, rr_count)) return 0; if (class == C_IN) { short *d; for (pp = p, d = rrfilter_desc(type); *d != -1; d++) { if (*d != 0) pp += *d; else if (!check_name(&pp, header, plen, fixup, rrs, rr_count)) return 0; } } } if (!ADD_RDLEN(header, p, plen, rdlen)) return 0; } return 1; } /* mode may be remove EDNS0 or DNSSEC RRs or remove A or AAAA from answer section. * returns number of modified records. */ size_t rrfilter(struct dns_header *header, size_t *plen, int mode) { static unsigned char **rrs = NULL; static int rr_sz = 0; unsigned char *p = (unsigned char *)(header+1); size_t rr_found = 0; int i, rdlen, qtype, qclass, chop_an, chop_ns, chop_ar; if (mode == RRFILTER_CONF && !daemon->filter_rr) return 0; if (ntohs(header->qdcount) != 1 || !(p = skip_name(p, header, *plen, 4))) return 0; 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 explicitly queried for */ for (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 rr_found; GETSHORT(type, p); GETSHORT(class, p); p += 4; /* TTL */ GETSHORT(rdlen, p); if (!ADD_RDLEN(header, p, *plen, rdlen)) return rr_found; if (mode == RRFILTER_EDNS0) /* EDNS */ { /* EDNS mode, remove T_OPT from additional section only */ if (i < (ntohs(header->nscount) + ntohs(header->ancount)) || type != T_OPT) continue; } else if (mode == RRFILTER_DNSSEC) { if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG) /* DNSSEC mode, remove SIGs and NSECs from all three sections. */ continue; /* Don't remove the answer. */ if (i < ntohs(header->ancount) && type == qtype && class == qclass) continue; } else if (qtype == T_ANY && rr_on_list(daemon->filter_rr, T_ANY)) { /* Filter replies to ANY queries in the spirit of RFC RFC 8482 para 4.3 */ if (class != C_IN || type == T_A || type == T_AAAA || type == T_MX || type == T_CNAME) continue; } else { /* Only looking at answer section now. */ if (i >= ntohs(header->ancount)) break; if (class != C_IN) continue; if (!rr_on_list(daemon->filter_rr, type)) continue; } if (!expand_workspace(&rrs, &rr_sz, rr_found + 1)) return rr_found; rrs[rr_found++] = pstart; rrs[rr_found++] = p; if (i < ntohs(header->ancount)) chop_an++; else if (i < (ntohs(header->nscount) + ntohs(header->ancount))) chop_ns++; else chop_ar++; } /* Nothing to do. */ if (rr_found == 0) return rr_found; /* 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 rr_found; p += 4; /* qclass, qtype */ /* Now answers and NS */ if (!check_rrs(p, header, *plen, 0, rrs, rr_found)) return rr_found; /* Third pass, actually fix up pointers in the 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); /* Fourth pass, elide records */ for (p = rrs[0], i = 1; (unsigned)i < rr_found; i += 2) { unsigned char *start = rrs[i]; unsigned char *end = ((unsigned)i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + *plen; memmove(p, start, end-start); p += end-start; } *plen = p - (unsigned char *)header; header->ancount = htons(ntohs(header->ancount) - chop_an); header->nscount = htons(ntohs(header->nscount) - chop_ns); header->arcount = htons(ntohs(header->arcount) - chop_ar); return rr_found; } /* This is used in the DNSSEC code too, hence it's exported */ short *rrfilter_desc(int type) { /* List of RRtypes which include domains in the data. 0 -> domain integer -> no. of plain bytes -1 -> end zero is not a valid RRtype, so the final entry is returned for anything which needs no mangling. */ static short 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 */ }; short *p = rr_desc; while (*p != type && *p != 0) while (*p++ != -1); return p+1; } int expand_workspace(unsigned char ***wkspc, int *szp, int new) { unsigned char **p; int old = *szp; if (old >= new+1) return 1; new += 5; if (!(p = whine_realloc(*wkspc, new * sizeof(unsigned char *)))) return 0; memset(p+old, 0, new-old); *wkspc = p; *szp = new; return 1; } /* 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 declared as 2049 (allowing for the trailing zero) for this reason. */ 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. */ 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; } dnsmasq-2.91/src/tftp.c0000664000175000017500000006172314765043257013233 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_TFTP static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len); static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client); static void free_transfer(struct tftp_transfer *transfer); static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2); static ssize_t tftp_err_oops(char *packet, const char *file); static ssize_t get_block(char *packet, struct tftp_transfer *transfer); static char *next(char **p, char *end); static void sanitise(char *buf); #define OP_RRQ 1 #define OP_WRQ 2 #define OP_DATA 3 #define OP_ACK 4 #define OP_ERR 5 #define OP_OACK 6 #define ERR_NOTDEF 0 #define ERR_FNF 1 #define ERR_PERM 2 #define ERR_FULL 3 #define ERR_ILL 4 #define ERR_TID 5 void tftp_request(struct listener *listen, time_t now) { ssize_t len; char *packet = daemon->packet; char *filename, *mode, *p, *end, *opt; union mysockaddr addr, peer; struct msghdr msg; struct iovec iov; struct ifreq ifr; int is_err = 1, if_index = 0, mtu = 0; struct iname *tmp; struct tftp_transfer *transfer = NULL, **up; 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 *name = NULL; char *prefix = daemon->tftp_prefix; struct tftp_prefix *pref; union all_addr addra; int family = listen->addr.sa.sa_family; /* Can always get recvd interface for IPv6 */ int check_dest = !option_bool(OPT_NOWILD) || family == AF_INET6; union { struct cmsghdr align; /* this ensures alignment */ char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; #if defined(HAVE_LINUX_NETWORK) char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; #elif defined(HAVE_SOLARIS_NETWORK) char control[CMSG_SPACE(sizeof(struct in_addr)) + CMSG_SPACE(sizeof(unsigned int))]; #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) char control[CMSG_SPACE(sizeof(struct in_addr)) + CMSG_SPACE(sizeof(struct sockaddr_dl))]; #endif } control_u; msg.msg_controllen = sizeof(control_u); msg.msg_control = control_u.control; msg.msg_flags = 0; msg.msg_name = &peer; msg.msg_namelen = sizeof(peer); msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = packet; iov.iov_len = daemon->packet_buff_sz; /* we overwrote the buffer... */ daemon->srv_save = NULL; if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2) return; #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_TFTP, (void *)packet, len, (union mysockaddr *)&peer, NULL, listen->tftpfd); #endif /* Can always get recvd interface for IPv6 */ if (!check_dest) { if (listen->iface) { addr = listen->iface->addr; name = listen->iface->name; mtu = listen->iface->mtu; if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu) mtu = daemon->tftp_mtu; } 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; if (msg.msg_controllen < sizeof(struct cmsghdr)) return; addr.sa.sa_family = family; #if defined(HAVE_LINUX_NETWORK) if (family == AF_INET) 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); addr.in.sin_addr = p.p->ipi_spec_dst; if_index = p.p->ipi_ifindex; } #elif defined(HAVE_SOLARIS_NETWORK) if (family == AF_INET) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) { union { unsigned char *c; struct in_addr *a; unsigned int *i; } p; p.c = CMSG_DATA(cmptr); if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) addr.in.sin_addr = *(p.a); else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) if_index = *(p.i); } #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) if (family == AF_INET) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) { union { unsigned char *c; struct in_addr *a; struct sockaddr_dl *s; } p; p.c = CMSG_DATA(cmptr); if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) addr.in.sin_addr = *(p.a); else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) if_index = p.s->sdl_index; } #endif if (family == AF_INET6) { 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); addr.in6.sin6_addr = p.p->ipi6_addr; if_index = p.p->ipi6_ifindex; } } if (!indextoname(listen->tftpfd, if_index, namebuff)) return; name = namebuff; addra.addr4 = addr.in.sin_addr; if (family == AF_INET6) addra.addr6 = addr.in6.sin6_addr; if (daemon->tftp_interfaces) { /* 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(family, &addra, name, NULL)) { if (!option_bool(OPT_CLEVERBIND)) enumerate_interfaces(0); if (!loopback_exception(listen->tftpfd, family, &addra, name) && !label_exception(if_index, 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 && (tmp->flags & INAME_4) && (tmp->flags & INAME_6) && wildcard_match(tmp->name, name)) return; #endif } safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE); if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1) { mtu = ifr.ifr_mtu; if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu) mtu = daemon->tftp_mtu; } } /* Failed to get interface mtu - can use configured value. */ if (mtu == 0) mtu = daemon->tftp_mtu; /* data transfer via server listening socket */ if (option_bool(OPT_SINGLE_PORT)) { int tftp_cnt; for (tftp_cnt = 0, transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; up = &transfer->next, transfer = transfer->next) { tftp_cnt++; if (sockaddr_isequal(&peer, &transfer->peer)) { if (ntohs(*((unsigned short *)packet)) == OP_RRQ) { /* Handle repeated RRQ or abandoned transfer from same host and port by unlinking and reusing the struct transfer. */ *up = transfer->next; break; } else { handle_tftp(now, transfer, len); return; } } } /* Enforce simultaneous transfer limit. In non-single-port mode this is doene by not listening on the server socket when too many transfers are in progress. */ if (!transfer && tftp_cnt >= daemon->tftp_max) return; } 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 (family == AF_INET) { addr.in.sin_port = htons(port); #ifdef HAVE_SOCKADDR_SA_LEN addr.in.sin_len = sizeof(addr.in); #endif } 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 } /* May reuse struct transfer from abandoned transfer in single port mode. */ if (!transfer && !(transfer = whine_malloc(sizeof(struct tftp_transfer)))) return; if (option_bool(OPT_SINGLE_PORT)) transfer->sockfd = listen->tftpfd; else if ((transfer->sockfd = socket(family, SOCK_DGRAM, 0)) == -1) { free(transfer); return; } transfer->peer = peer; transfer->source = addra; transfer->if_index = if_index; transfer->timeout = now + 2; transfer->backoff = 1; transfer->block = 1; transfer->blocksize = 512; transfer->offset = 0; transfer->file = NULL; transfer->opt_blocksize = transfer->opt_transize = 0; transfer->netascii = transfer->carrylf = 0; (void)prettyprint_addr(&peer, daemon->addrbuff); /* if we have a nailed-down range, iterate until we find a free one. */ while (!option_bool(OPT_SINGLE_PORT)) { if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 || #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) setsockopt(transfer->sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 || #endif !fix_fd(transfer->sockfd)) { if (errno == EADDRINUSE && daemon->start_tftp_port != 0) { if (++port <= daemon->end_tftp_port) { if (family == AF_INET) addr.in.sin_port = htons(port); else addr.in6.sin6_port = htons(port); continue; } my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP")); } free_transfer(transfer); return; } break; } p = packet + 2; end = packet + len; if (ntohs(*((unsigned short *)packet)) != OP_RRQ || !(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"), daemon->addrbuff, NULL); is_err = 1; } else { if (strcasecmp(mode, "netascii") == 0) transfer->netascii = 1; while ((opt = next(&p, end))) { if (strcasecmp(opt, "blksize") == 0) { if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK)) { /* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */ int overhead = (family == AF_INET) ? 32 : 52; transfer->blocksize = atoi(opt); if (transfer->blocksize < 1) transfer->blocksize = 1; if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4) transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4; if (mtu != 0 && transfer->blocksize > (unsigned)mtu - overhead) transfer->blocksize = (unsigned)mtu - overhead; transfer->opt_blocksize = 1; transfer->block = 0; } } else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii) { transfer->opt_transize = 1; transfer->block = 0; } } /* cope with backslashes from windows boxen. */ for (p = filename; *p; p++) if (*p == '\\') *p = '/'; else if (option_bool(OPT_TFTP_LC)) *p = tolower((unsigned char)*p); strcpy(daemon->namebuff, "/"); if (prefix) { if (prefix[0] == '/') daemon->namebuff[0] = 0; strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff)); if (prefix[strlen(prefix)-1] != '/') strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); if (option_bool(OPT_TFTP_APREF_IP)) { size_t oldlen = strlen(daemon->namebuff); struct stat statbuf; 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 */ if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode)) daemon->namebuff[oldlen] = 0; } if (option_bool(OPT_TFTP_APREF_MAC)) { unsigned char *macaddr = NULL; unsigned char macbuf[DHCP_CHADDR_MAX]; #ifdef HAVE_DHCP if (daemon->dhcp && peer.sa.sa_family == AF_INET) { /* Check if the client IP is in our lease database */ struct dhcp_lease *lease = lease_find_by_addr(peer.in.sin_addr); if (lease && lease->hwaddr_type == ARPHRD_ETHER && lease->hwaddr_len == ETHER_ADDR_LEN) macaddr = lease->hwaddr; } #endif /* If no luck, try to find in ARP table. This only works if client is in same (V)LAN */ if (!macaddr && find_mac(&peer, macbuf, 1, now) > 0) macaddr = macbuf; if (macaddr) { size_t oldlen = strlen(daemon->namebuff); struct stat statbuf; snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/", macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]); /* remove unique-directory if it doesn't exist */ if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode)) daemon->namebuff[oldlen] = 0; } } /* Absolute pathnames OK if they match prefix */ if (filename[0] == '/') { if (strstr(filename, daemon->namebuff) == filename) daemon->namebuff[0] = 0; else filename++; } } else if (filename[0] == '/') daemon->namebuff[0] = 0; strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff)); /* check permissions and open file */ if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff))) { if ((len = get_block(packet, transfer)) == -1) len = tftp_err_oops(packet, daemon->namebuff); else is_err = 0; } } send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index); #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd); #endif if (is_err) free_transfer(transfer); else { transfer->next = daemon->tftp_trans; daemon->tftp_trans = transfer; } } static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client) { char *packet = daemon->packet, *namebuff = daemon->namebuff; struct tftp_file *file; struct tftp_transfer *t; uid_t uid = geteuid(); struct stat statbuf; int fd = -1; /* trick to ban moving out of the subtree */ if (prefix && strstr(namebuff, "/../")) goto perm; if ((fd = open(namebuff, O_RDONLY)) == -1) { if (errno == ENOENT) { *len = tftp_err(ERR_FNF, packet, _("file %s not found for %s"), namebuff, client); return NULL; } else if (errno == EACCES) goto perm; else goto oops; } /* stat the file descriptor to avoid stat->open races */ if (fstat(fd, &statbuf) == -1) goto oops; /* running as root, must be world-readable */ if (uid == 0) { if (!(statbuf.st_mode & S_IROTH)) goto perm; } /* in secure mode, must be owned by user running dnsmasq */ else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid) goto perm; /* If we're doing many transfers from the same file, only open it once this saves lots of file descriptors when mass-booting a big cluster, for instance. Be conservative and only share when inode and name match this keeps error messages sane. */ for (t = daemon->tftp_trans; t; t = t->next) if (t->file->dev == statbuf.st_dev && t->file->inode == statbuf.st_ino && strcmp(t->file->filename, namebuff) == 0) { close(fd); t->file->refcount++; return t->file; } if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1))) { errno = ENOMEM; goto oops; } file->fd = fd; file->size = statbuf.st_size; file->dev = statbuf.st_dev; file->inode = statbuf.st_ino; file->refcount = 1; strcpy(file->filename, namebuff); return file; perm: *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff, strerror(EACCES)); if (fd != -1) close(fd); return NULL; oops: *len = tftp_err_oops(packet, namebuff); if (fd != -1) close(fd); return NULL; } void check_tftp_listeners(time_t now) { struct listener *listener; struct tftp_transfer *transfer, *tmp, **up; for (listener = daemon->listeners; listener; listener = listener->next) if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN)) tftp_request(listener, now); /* In single port mode, all packets come via port 69 and tftp_request() */ if (!option_bool(OPT_SINGLE_PORT)) for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next) if (poll_check(transfer->sockfd, POLLIN)) { union mysockaddr peer; socklen_t addr_len = sizeof(union mysockaddr); ssize_t len; /* we overwrote the buffer... */ daemon->srv_save = NULL; if ((len = recvfrom(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0, &peer.sa, &addr_len)) > 0) { if (sockaddr_isequal(&peer, &transfer->peer)) handle_tftp(now, transfer, len); else { /* Wrong source address. See rfc1350 para 4. */ prettyprint_addr(&peer, daemon->addrbuff); len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff, NULL); while(retry_send(sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer)))); #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd); #endif } } } for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp) { tmp = transfer->next; if (difftime(now, transfer->timeout) >= 0.0) { int endcon = 0; ssize_t len; /* timeout, retransmit */ transfer->timeout += 1 + (1<<(transfer->backoff/2)); /* we overwrote the buffer... */ daemon->srv_save = NULL; if ((len = get_block(daemon->packet, transfer)) == -1) { len = tftp_err_oops(daemon->packet, transfer->file->filename); endcon = 1; } else if (++transfer->backoff > 7) { /* don't complain about timeout when we're awaiting the last ACK, some clients never send it */ if ((unsigned)len == transfer->blocksize + 4) endcon = 1; len = 0; } if (len != 0) { send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len, &transfer->peer, &transfer->source, transfer->if_index); #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd); #endif } if (endcon || len == 0) { strcpy(daemon->namebuff, transfer->file->filename); sanitise(daemon->namebuff); (void)prettyprint_addr(&transfer->peer, daemon->addrbuff); my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff); /* unlink */ *up = tmp; 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; } } up = &transfer->next; } } /* packet in daemon->packet as this is called. */ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len) { struct ack { unsigned short op, block; } *mess = (struct ack *)daemon->packet; if (len >= (ssize_t)sizeof(struct ack)) { if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block) { /* Got ack, ensure we take the (re)transmit path */ transfer->timeout = now; transfer->backoff = 0; if (transfer->block++ != 0) transfer->offset += transfer->blocksize - transfer->expansion; } else if (ntohs(mess->op) == OP_ERR) { char *p = daemon->packet + sizeof(struct ack); char *end = daemon->packet + len; char *err = next(&p, end); (void)prettyprint_addr(&transfer->peer, daemon->addrbuff); /* Sanitise error message */ if (!err) err = ""; else sanitise(err); my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"), (int)ntohs(mess->block), err, daemon->addrbuff); /* Got err, ensure we take abort */ transfer->timeout = now; transfer->backoff = 100; } } } static void free_transfer(struct tftp_transfer *transfer) { if (!option_bool(OPT_SINGLE_PORT)) close(transfer->sockfd); if (transfer->file && (--transfer->file->refcount) == 0) { close(transfer->file->fd); free(transfer->file); } free(transfer); } static char *next(char **p, char *end) { char *n, *ret = *p; /* Look for end of string, without running off the end of the packet. */ for (n = *p; n < end && *n != 0; n++); /* ran off the end or zero length string - failed */ if (n == end || n == ret) return NULL; *p = n + 1; 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; } #define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */ static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2) { struct errmess { unsigned short op, err; char message[]; } *mess = (struct errmess *)packet; ssize_t len, ret = 4; memset(packet, 0, daemon->packet_buff_sz); if (file) sanitise(file); mess->op = htons(OP_ERR); mess->err = htons(err); len = snprintf(mess->message, MAXMESSAGE, message, file, arg2); ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */ if (err != ERR_FNF || !option_bool(OPT_QUIET_TFTP)) my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message); return ret; } static ssize_t tftp_err_oops(char *packet, const char *file) { /* May have >1 refs to file, so potentially mangle a copy of the name */ if (file != daemon->namebuff) strcpy(daemon->namebuff, file); return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff, strerror(errno)); } /* return -1 for error, zero for done. */ static ssize_t get_block(char *packet, struct tftp_transfer *transfer) { memset(packet, 0, daemon->packet_buff_sz); if (transfer->block == 0) { /* send OACK */ char *p; struct oackmess { unsigned short op; char data[]; } *mess = (struct oackmess *)packet; p = mess->data; mess->op = htons(OP_OACK); if (transfer->opt_blocksize) { p += (sprintf(p, "blksize") + 1); p += (sprintf(p, "%u", transfer->blocksize) + 1); } if (transfer->opt_transize) { p += (sprintf(p,"tsize") + 1); p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1); } return p - packet; } else { /* send data packet */ struct datamess { unsigned short op, block; unsigned char data[]; } *mess = (struct datamess *)packet; size_t size = transfer->file->size - transfer->offset; if (transfer->offset > transfer->file->size) return 0; /* finished */ if (size > transfer->blocksize) size = transfer->blocksize; mess->op = htons(OP_DATA); mess->block = htons((unsigned short)(transfer->block)); if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 || !read_write(transfer->file->fd, mess->data, size, RW_READ)) return -1; transfer->expansion = 0; /* Map '\n' to CR-LF in netascii mode */ if (transfer->netascii) { size_t i; int newcarrylf; for (i = 0, newcarrylf = 0; i < size; i++) if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf)) { 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'; i++; } transfer->carrylf = newcarrylf; } return size + 4; } } 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 dnsmasq-2.91/src/rfc1035.c0000664000175000017500000020031714765043257013333 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" /* EXTR_NAME_EXTRACT -> extract name EXTR_NAME_COMPARE -> compare name, case insensitive EXTR_NAME_NOCASE -> compare name, case sensitive EXTR_NAME_FLIP -> flip 0x20 bits in packet. For flip, name is an array of ints, whose size is given in parm, which forms the bitmap. Bits beyond the size are assumed to be zero. return = 0 -> error return = 1 -> extract OK, compare OK, flip OK return = 2 -> extract OK, compare failed. return = 3 -> extract OK, compare failed but only on case. */ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, char *name, int func, unsigned int parm) { unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL; unsigned int j, l, namelen = 0, hops = 0; unsigned int bigmap_counter = 0, bigmap_posn = 0, bigmap_size = parm, bitmap = 0; int retvalue = 1, case_insens = 1, isExtract = 0, flip = 0, extrabytes = (int)parm; unsigned int *bigmap = (unsigned int *)name; if (func == EXTR_NAME_EXTRACT) isExtract = 1, *cp = 0; else if (func == EXTR_NAME_NOCASE) case_insens = 0; else if (func == EXTR_NAME_FLIP) { flip = 1, extrabytes = 0; name = NULL; } while (1) { unsigned int label_type; if (!CHECK_LEN(header, p, plen, 1)) return 0; if ((l = *p++) == 0) /* end marker */ { /* check that there are the correct no. of bytes after the name */ if (!CHECK_LEN(header, p1 ? p1 : p, plen, extrabytes)) return 0; if (isExtract) { if (cp != (unsigned char *)name) cp--; *cp = 0; /* terminate: lose final period */ } else if (!flip && *cp != 0) retvalue = 2; if (p1) /* we jumped via compression */ *pp = p1; else *pp = p; return retvalue; } label_type = l & 0xc0; if (label_type == 0xc0) /* pointer */ { if (!CHECK_LEN(header, p, plen, 1)) return 0; /* get offset */ l = (l&0x3f) << 8; l |= *p++; if (!p1) /* first jump, save location to go back to */ p1 = p; hops++; /* break malicious infinite loops */ if (hops > 255) return 0; p = l + (unsigned char *)header; } else if (label_type == 0x00) { /* label_type = 0 -> label. */ namelen += l + 1; /* include period */ if (namelen >= MAXDNAME) return 0; if (!CHECK_LEN(header, p, plen, l)) return 0; for (j=0; j= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { /* Get the next int of the bitmap */ if (bigmap_posn < bigmap_size && bigmap_counter-- == 0) { bitmap = bigmap[bigmap_posn++]; bigmap_counter = (sizeof(unsigned int) * 8) - 1; } if (bitmap & 1) *p ^= 0x20; bitmap >>= 1; } } else { unsigned char c1 = *cp, c2 = *p; if (c1 == 0) retvalue = 2; else { cp++; if (c1 == NAME_ESCAPE) c1 = (*cp++)-1; else if (case_insens && c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A'; if (case_insens && c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A'; if (!case_insens && retvalue != 2 && c1 != c2) { if (c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A'; if (c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A'; if (c1 == c2) retvalue = 3; } if (c1 != c2) retvalue = 2; } } if (isExtract) *cp++ = '.'; else if (!flip && *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 int in_arpa_name_2_addr(char *namein, union all_addr *addrp) { int j; char name[MAXARPANAME+1], *cp1; unsigned char *addr = (unsigned char *)addrp; char *lastchunk = NULL, *penchunk = NULL; if (strlen(namein) > MAXARPANAME) return 0; memset(addrp, 0, sizeof(union all_addr)); /* turn name into a series of asciiz strings */ /* j counts no. of labels */ for(j = 1,cp1 = name; *namein; cp1++, namein++) if (*namein == '.') { penchunk = lastchunk; lastchunk = cp1 + 1; *cp1 = 0; j++; } else *cp1 = *namein; *cp1 = 0; if (j<3) return 0; if (hostname_isequal(lastchunk, "arpa") && hostname_isequal(penchunk, "in-addr")) { /* IP v4 */ /* address arrives as a name of the form www.xxx.yyy.zzz.in-addr.arpa some of the low order address octets might be missing and should be set to zero. */ for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1) { /* check for digits only (weeds out things like 50.0/24.67.28.64.in-addr.arpa which are used as CNAME targets according to RFC 2317 */ char *cp; for (cp = cp1; *cp; cp++) if (!isdigit((unsigned char)*cp)) return 0; addr[3] = addr[2]; addr[2] = addr[1]; addr[1] = addr[0]; addr[0] = atoi(cp1); } return F_IPV4; } else if (hostname_isequal(penchunk, "ip6") && (hostname_isequal(lastchunk, "int") || hostname_isequal(lastchunk, "arpa"))) { /* IP v6: Address arrives as 0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.ip6.[int|arpa] or \[xfedcba9876543210fedcba9876543210/128].ip6.[int|arpa] Note that most of these the various representations are obsolete and left-over from the many DNS-for-IPv6 wars. We support all the formats that we can since there is no reason not to. */ if (*name == '\\' && *(name+1) == '[' && (*(name+2) == 'x' || *(name+2) == 'X')) { for (j = 0, cp1 = name+3; *cp1 && isxdigit((unsigned char) *cp1) && j < 32; cp1++, j++) { char xdig[2]; xdig[0] = *cp1; xdig[1] = 0; if (j%2) addr[j/2] |= strtol(xdig, NULL, 16); else addr[j/2] = strtol(xdig, NULL, 16) << 4; } if (*cp1 == '/' && j == 32) return F_IPV6; } else { for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1) { if (*(cp1+1) || !isxdigit((unsigned char)*cp1)) return 0; for (j = sizeof(struct in6_addr)-1; j>0; j--) addr[j] = (addr[j] >> 4) | (addr[j-1] << 4); addr[0] = (addr[0] >> 4) | (strtol(cp1, NULL, 16) << 4); } return F_IPV6; } } return 0; } unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes) { while(1) { unsigned int label_type; if (!CHECK_LEN(header, ansp, plen, 1)) return NULL; label_type = (*ansp) & 0xc0; if (label_type == 0xc0) { /* pointer for compression. */ ansp += 2; break; } else if (label_type == 0x80) return NULL; /* reserved */ else if (label_type == 0x40) { /* Extended label type */ unsigned int count; if (!CHECK_LEN(header, ansp, plen, 2)) return NULL; if (((*ansp++) & 0x3f) != 1) return NULL; /* 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 NULL; if (len == 0) break; /* zero length label marks the end. */ } } if (!CHECK_LEN(header, ansp, plen, extrabytes)) return NULL; return ansp; } unsigned char *skip_questions(struct dns_header *header, size_t plen) { int q; unsigned char *ansp = (unsigned char *)(header+1); for (q = ntohs(header->qdcount); q != 0; q--) { if (!(ansp = skip_name(ansp, header, plen, 4))) return NULL; ansp += 4; /* class and type */ } return ansp; } unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen) { int i, rdlen; for (i = 0; i < count; i++) { if (!(ansp = skip_name(ansp, header, plen, 10))) return NULL; ansp += 8; /* type, class, TTL */ GETSHORT(rdlen, ansp); if (!ADD_RDLEN(header, ansp, plen, rdlen)) return NULL; } return ansp; } size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen) { unsigned char *ansp = skip_questions(header, plen); /* if packet is malformed, just return as-is. */ if (!ansp) return plen; if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), header, plen))) return plen; /* restore pseudoheader */ if (pheader && ntohs(header->arcount) == 0) { /* must use memmove, may overlap */ memmove(ansp, pheader, hlen); header->arcount = htons(1); ansp += hlen; } return ansp - (unsigned char *)header; } /* is addr in the non-globally-routed IP space? */ int private_net(struct in_addr addr, int ban_localhost) { in_addr_t ip_addr = ntohl(addr.s_addr); return (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ || (((ip_addr & 0xFF000000) == 0x00000000) && ban_localhost) /* RFC 5735 section 3. "here" network */ || ((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ || ((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ || ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ || ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ || ((ip_addr & 0xFFFFFF00) == 0xC0000200) /* 192.0.2.0/24 (test-net) */ || ((ip_addr & 0xFFFFFF00) == 0xC6336400) /* 198.51.100.0/24(test-net) */ || ((ip_addr & 0xFFFFFF00) == 0xCB007100) /* 203.0.113.0/24 (test-net) */ || ((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ; } static int private_net6(struct in6_addr *a, int ban_localhost) { /* Block IPv4-mapped IPv6 addresses in private IPv4 address space */ if (IN6_IS_ADDR_V4MAPPED(a)) { struct in_addr v4; v4.s_addr = ((const uint32_t *) (a))[3]; return private_net(v4, ban_localhost); } return (IN6_IS_ADDR_UNSPECIFIED(a) && ban_localhost) || /* RFC 6303 4.3 */ (IN6_IS_ADDR_LOOPBACK(a) && ban_localhost) || /* RFC 6303 4.3 */ IN6_IS_ADDR_LINKLOCAL(a) || /* RFC 6303 4.5 */ IN6_IS_ADDR_SITELOCAL(a) || ((unsigned char *)a)[0] == 0xfd || /* RFC 6303 4.4 */ ((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */ } int do_doctor(struct dns_header *header, size_t qlen, char *namebuff) { unsigned char *p; int i, qtype, qclass, rdlen; int done = 0; if (!(p = skip_questions(header, qlen))) return done; for (i = 0; i < ntohs(header->ancount) + ntohs(header->arcount); i++) { /* Skip over auth section */ if (i == ntohs(header->ancount) && !(p = skip_section(p, ntohs(header->nscount), header, qlen))) return done; if (!extract_name(header, qlen, &p, namebuff, EXTR_NAME_EXTRACT, 10)) return done; /* bad packet */ GETSHORT(qtype, p); GETSHORT(qclass, p); p += 4; /* ttl */ GETSHORT(rdlen, p); if (qclass == C_IN && qtype == T_A) { struct doctor *doctor; union all_addr addr; if (!CHECK_LEN(header, p, qlen, INADDRSZ)) return done; /* alignment */ memcpy(&addr.addr4, p, INADDRSZ); for (doctor = daemon->doctors; doctor; doctor = doctor->next) { if (doctor->end.s_addr == 0) { if (!is_same_net(doctor->in, addr.addr4, doctor->mask)) continue; } else if (ntohl(doctor->in.s_addr) > ntohl(addr.addr4.s_addr) || ntohl(doctor->end.s_addr) < ntohl(addr.addr4.s_addr)) continue; addr.addr4.s_addr &= ~doctor->mask.s_addr; addr.addr4.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; #ifdef HAVE_DNSSEC /* remove validated flag from this RR, since we changed it! */ if (option_bool(OPT_DNSSEC_VALID) && i < ntohs(header->ancount)) daemon->rr_status[i] = 0; #endif done = 1; memcpy(p, &addr.addr4, INADDRSZ); log_query(F_FORWARD | F_CONFIG | F_IPV4, namebuff, &addr, NULL, 0); break; } } if (!ADD_RDLEN(header, p, qlen, rdlen)) return done; /* bad packet */ } return done; } /* Find SOA RR in auth section to get TTL for negative caching of name. Cache said SOA and return the difference in length between name and the name of the SOA RR so we can look it up again. */ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *substring, unsigned long *ttlp, int no_cache, time_t now) { unsigned char *p, *psave; int qtype, qclass, rdlen; unsigned long ttl, minttl; int i, j; size_t name_len, soa_len, len; union all_addr addr; /* first move to NS section and find TTL from SOA RR */ if (!(p = skip_questions(header, qlen)) || !(p = skip_section(p, ntohs(header->ancount), header, qlen))) return 0; /* bad packet */ name_len = strlen(name); if (substring) *substring = name_len; if (ttlp) *ttlp = daemon->neg_ttl; for (i = 0; i < ntohs(header->nscount); i++) { if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0)) return 0; /* bad packet */ GETSHORT(qtype, p); GETSHORT(qclass, p); GETLONG(ttl, p); GETSHORT(rdlen, p); psave = p; if ((qclass == C_IN) && (qtype == T_SOA)) { soa_len = strlen(daemon->workspacename); /* SOA must be for the name we're interested in. */ if (soa_len <= name_len && memcmp(daemon->workspacename, name + name_len - soa_len, soa_len) == 0) { int prefix = name_len - soa_len; if (!no_cache) { if (!(addr.rrblock.rrdata = blockdata_alloc(NULL, 0))) return 0; addr.rrblock.rrtype = T_SOA; addr.rrblock.datalen = 0; } for (j = 0; j < 2; j++) /* MNAME, RNAME */ { if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0)) { if (!no_cache) blockdata_free(addr.rrblock.rrdata); return 0; } if (!no_cache) { len = to_wire(daemon->workspacename); if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, daemon->workspacename, len)) { blockdata_free(addr.rrblock.rrdata); return 0; } addr.rrblock.datalen += len; } } if (!CHECK_LEN(header, p, qlen, 20)) { if (!no_cache) blockdata_free(addr.rrblock.rrdata); return 0; } /* rest of RR */ if (!no_cache && !blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p, 20)) { blockdata_free(addr.rrblock.rrdata); return 0; } addr.rrblock.datalen += 20; if (!no_cache) { int secflag = 0; #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[i + ntohs(header->ancount)] != 0) { secflag = F_DNSSECOK; /* limit TTL based on signature. */ if (daemon->rr_status[i + ntohs(header->ancount)] < ttl) ttl = daemon->rr_status[i + ntohs(header->ancount)]; } #endif if (!cache_insert(name + prefix, &addr, C_IN, now, ttl, F_FORWARD | F_RR | F_KEYTAG | secflag)) { blockdata_free(addr.rrblock.rrdata); return 0; } } p += 16; /* SERIAL REFRESH RETRY EXPIRE */ GETLONG(minttl, p); /* minTTL */ if (ttl < minttl) minttl = ttl; if (substring) *substring = prefix; if (ttlp) *ttlp = minttl; return 1; } } p = psave; if (!ADD_RDLEN(header, p, qlen, rdlen)) return 0; /* bad packet */ } return 0; } /* Print TXT reply to log */ static int log_txt(char *name, unsigned char *p, const int ardlen, int flag) { unsigned char *p1 = p; /* Loop over TXT payload */ while ((p1 - p) < ardlen) { unsigned int i, len = *p1; unsigned char *p3 = p1; if ((p1 + len - p) >= ardlen) return 0; /* bad packet */ /* make counted string zero-term and sanitise */ for (i = 0; i < len; i++) { if (!isprint((unsigned char)*(p3+1))) break; *p3 = *(p3+1); p3++; } *p3 = 0; log_query(flag, name, NULL, (char*)p1, 0); /* restore */ memmove(p1 + 1, p1, i); *p1 = len; p1 += len+1; } return 1; } /* Note that the following code can create CNAME chains that don't point to a real record, either because of lack of memory, or lack of SOA records. These are treated by the cache code as expired and cleaned out that way. Return 1 if we reject an address because it look like part of dns-rebinding attack. Return 2 if the packet is malformed. */ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind, int no_cache_dnssec, int secure) { unsigned char *p, *p1, *endrr, *namep; int j, qtype, qclass, aqtype, aqclass, ardlen, res; unsigned long ttl = 0; union all_addr addr; #ifdef HAVE_IPSET char **ipsets_cur; #else (void)ipsets; /* unused */ #endif #ifdef HAVE_NFTSET char **nftsets_cur; #else (void)nftsets; /* unused */ #endif int found = 0, cname_count = CNAME_CHAIN; struct crec *cpp = NULL; int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0; #ifdef HAVE_DNSSEC int cname_short = 0; #endif unsigned long cttl = ULONG_MAX, attl; cache_start_insert(); namep = p = (unsigned char *)(header+1); if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, EXTR_NAME_EXTRACT, 4)) return 2; /* bad packet */ GETSHORT(qtype, p); GETSHORT(qclass, p); if (qclass != C_IN) return 0; /* PTRs: we chase CNAMEs here, since we have no way to represent them in the cache. */ if (qtype == T_PTR) { int insert = 1, name_encoding = in_arpa_name_2_addr(name, &addr); if (!(flags & F_NXDOMAIN)) { cname_loop: if (!(p1 = skip_questions(header, qlen))) return 2; for (j = 0; j < ntohs(header->ancount); j++) { int secflag = 0; if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10))) return 2; /* 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; /* TTL of record is minimum of CNAMES and PTR */ if (attl < cttl) cttl = attl; if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR)) { #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID) && j < daemon->rr_status_sz && daemon->rr_status[j] != 0) { /* validated RR anywhere in CNAME chain, don't cache. */ if (cname_short || aqtype == T_CNAME) insert = 0; secflag = F_DNSSECOK; /* limit TTL based on signature. */ if (daemon->rr_status[j] < cttl) cttl = daemon->rr_status[j]; } #endif if (aqtype == T_CNAME) log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0); if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0)) return 2; if (aqtype == T_CNAME) { if (!cname_count--) return 0; /* looped CNAMES, we can't cache. */ #ifdef HAVE_DNSSEC cname_short = 1; #endif goto cname_loop; } found = 1; if (!name_encoding) log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, NULL, aqtype); else { log_query(name_encoding | secflag | F_REVERSE | F_UPSTREAM, name, &addr, NULL, 0); if (insert) cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE); } } p1 = endrr; if (!CHECK_LEN(header, p1, qlen, 0)) return 2; /* bad packet */ } } if (!found && !option_bool(OPT_NO_NEG)) { /* For reverse records, we use the name field to store the SOA name. */ int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now); flags |= F_NEG | (secure ? F_DNSSECOK : 0); if (name_encoding && ttl) { flags |= F_REVERSE | name_encoding; if (!have_soa) flags |= F_NO_RR; /* Marks no SOA found. */ cache_insert(name + substring, &addr, C_IN, now, ttl, flags); } log_query(flags | F_UPSTREAM, name, &addr, NULL, 0); } } else { /* everything other than PTR */ struct crec *newc; int addrlen = 0, insert = 1; if (qtype == T_A) { addrlen = INADDRSZ; flags |= F_IPV4; } else if (qtype == T_AAAA) { addrlen = IN6ADDRSZ; flags |= F_IPV6; } else if (qtype != T_CNAME && (qtype == T_SRV || rr_on_list(daemon->cache_rr, qtype) || rr_on_list(daemon->cache_rr, T_ANY))) flags |= F_RR; else insert = 0; /* NOTE: do not cache data from CNAME queries. */ cname_loop1: if (!(p1 = skip_questions(header, qlen))) return 2; for (j = 0; j < ntohs(header->ancount); j++) { int secflag = 0; if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10))) return 2; /* 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 (!CHECK_LEN(header, endrr, qlen, 0)) return 2; /* bad packet */ /* Not what we're looking for? */ if (aqclass != C_IN || res == 2) { p1 = endrr; continue; } #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID) && j < daemon->rr_status_sz && daemon->rr_status[j] != 0) { secflag = F_DNSSECOK; /* limit TTl based on sig. */ if (daemon->rr_status[j] < attl) attl = daemon->rr_status[j]; } #endif if (aqtype == T_CNAME) { if (!cname_count--) return 0; /* looped CNAMES */ log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0); if (insert) { if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag))) { newc->addr.cname.target.cache = NULL; newc->addr.cname.is_name_ptr = 0; if (cpp) { next_uid(newc); cpp->addr.cname.target.cache = newc; cpp->addr.cname.uid = newc->uid; } } cpp = newc; if (attl < cttl) cttl = attl; } namep = p1; if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0)) return 2; if (qtype != T_CNAME) goto cname_loop1; found = 1; } else if (qtype == T_ANY || aqtype != qtype) { #ifdef HAVE_DNSSEC if (!option_bool(OPT_DNSSEC_VALID) || aqtype != T_RRSIG) #endif log_query(secflag | F_FORWARD | F_UPSTREAM | F_RRNAME, name, NULL, NULL, aqtype); } else if (!(flags & F_NXDOMAIN)) { found = 1; if (flags & F_RR) { short desc, *rrdesc = rrfilter_desc(aqtype); unsigned char *tmp = namep; if (!CHECK_LEN(header, p1, qlen, ardlen)) return 2; /* bad packet */ /* If the data has no names and is small enough, store it in the crec address field rather than allocate a block. */ if (*rrdesc == -1 && ardlen <= (int)RR_IMDATALEN) { addr.rrdata.rrtype = aqtype; addr.rrdata.datalen = (char)ardlen; flags &= ~F_KEYTAG; /* in case of >1 answer, not all the same. */ if (ardlen != 0) memcpy(addr.rrdata.data, p1, ardlen); } else { addr.rrblock.rrtype = aqtype; addr.rrblock.datalen = 0; flags |= F_KEYTAG; /* discriminates between rrdata and rrblock */ /* The RR data may include names, and those names may include compression, which will be rendered meaningless when copied into another packet. Here we go through a description of the packet type to find the names, and extract them to a c-string and then re-encode them to standalone DNS format without compression. */ if (!(addr.rrblock.rrdata = blockdata_alloc(NULL, 0))) return 0; do { desc = *rrdesc++; if (desc == -1) { /* Copy the rest of the RR and end. */ if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p1, endrr - p1)) { blockdata_free(addr.rrblock.rrdata); return 0; } addr.rrblock.datalen += endrr - p1; } else if (desc == 0) { /* Name, extract it then re-encode. */ int len; if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0)) { blockdata_free(addr.rrblock.rrdata); return 2; } len = to_wire(name); if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, name, len)) { blockdata_free(addr.rrblock.rrdata); return 0; } addr.rrblock.datalen += len; } else { /* desc is length of a block of data to be used as-is */ if (desc > endrr - p1) desc = endrr - p1; if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p1, desc)) { blockdata_free(addr.rrblock.rrdata); return 0; } addr.rrblock.datalen += desc; p1 += desc; } } while (desc != -1); /* we overwrote the original name, so get it back here. */ if (!extract_name(header, qlen, &tmp, name, EXTR_NAME_EXTRACT, 0)) { blockdata_free(addr.rrblock.rrdata); return 2; } } } else if (flags & (F_IPV4 | F_IPV6)) { /* copy address into aligned storage */ if (!CHECK_LEN(header, p1, qlen, addrlen)) return 2; /* bad packet */ memcpy(&addr, p1, addrlen); /* check for returned address in private space */ if (check_rebind) { if ((flags & F_IPV4) && private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND))) return 1; if ((flags & F_IPV6) && private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND))) return 1; } #ifdef HAVE_IPSET if (ipsets && (flags & (F_IPV4 | F_IPV6))) for (ipsets_cur = ipsets->sets; *ipsets_cur; ipsets_cur++) if (add_to_ipset(*ipsets_cur, &addr, flags, 0) == 0) log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, ipsets->domain, &addr, *ipsets_cur, 1); #endif #ifdef HAVE_NFTSET if (nftsets && (flags & (F_IPV4 | F_IPV6))) for (nftsets_cur = nftsets->sets; *nftsets_cur; nftsets_cur++) if (add_to_nftset(*nftsets_cur, &addr, flags, 0) == 0) log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, nftsets->domain, &addr, *nftsets_cur, 0); #endif } if (insert) { newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag); if (newc && cpp) { next_uid(newc); cpp->addr.cname.target.cache = newc; cpp->addr.cname.uid = newc->uid; } cpp = NULL; /* cache insert failed, don't leak blockdata. */ if (!newc && (flags & F_RR) && (flags & F_KEYTAG)) blockdata_free(addr.rrblock.rrdata); } /* We're filtering this RRtype. It will be removed from the returned packet in process_reply() but gets cached here anyway and will be filtered again on the way out of the cache. Here, we just need to alter the logging. */ if (qtype != T_ANY && rr_on_list(daemon->filter_rr, qtype)) secflag = F_NEG | F_CONFIG; if (aqtype == T_TXT) log_txt(name, p1, ardlen, flags | F_FORWARD | F_UPSTREAM | secflag); else log_query(flags | F_FORWARD | F_UPSTREAM | secflag, name, &addr, NULL, aqtype); } p1 = endrr; if (!CHECK_LEN(header, p1, qlen, 0)) return 2; /* bad packet */ } if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN))) { if (flags & F_NXDOMAIN) { flags &= ~(F_IPV4 | F_IPV6 | F_RR); /* Can store NXDOMAIN reply for any qtype. */ insert = 1; } log_query(F_UPSTREAM | F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0), name, NULL, NULL, 0); if (insert && !option_bool(OPT_NO_NEG)) { int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now); /* If there's no SOA to get the TTL from, but there is a CNAME pointing at this, inherit its TTL */ if (ttl || cpp) { if (!ttl) ttl = cttl; addr.rrdata.datalen = substring; addr.rrdata.rrtype = qtype; if (!have_soa) flags |= F_NO_RR; /* Marks no SOA found. */ } newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); if (newc && cpp) { next_uid(newc); cpp->addr.cname.target.cache = newc; cpp->addr.cname.uid = newc->uid; } } } } /* 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->hb4 & HB4_CD) && (header->hb4 & HB4_RA) && !no_cache_dnssec) cache_end_insert(); return 0; } #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) /* Don't pass control chars and weird escapes to UBus. */ static int safe_name(char *name) { unsigned char *r; for (r = (unsigned char *)name; *r; r++) if (!isprint((int)*r)) return 0; return 1; } void report_addresses(struct dns_header *header, size_t len, u32 mark) { unsigned char *p, *endrr; int i; unsigned long attl; struct allowlist *allowlists; char **pattern_pos; if (RCODE(header) != NOERROR) return; for (allowlists = daemon->allowlists; allowlists; allowlists = allowlists->next) if (allowlists->mark == (mark & daemon->allowlist_mask & allowlists->mask)) for (pattern_pos = allowlists->patterns; *pattern_pos; pattern_pos++) if (!strcmp(*pattern_pos, "*")) return; if (!(p = skip_questions(header, len))) return; for (i = ntohs(header->ancount); i != 0; i--) { int aqtype, aqclass, ardlen; if (!extract_name(header, len, &p, daemon->namebuff, EXTR_NAME_EXTRACT, 10)) return; if (!CHECK_LEN(header, p, len, 10)) return; GETSHORT(aqtype, p); GETSHORT(aqclass, p); GETLONG(attl, p); GETSHORT(ardlen, p); if (!CHECK_LEN(header, p, len, ardlen)) return; endrr = p+ardlen; if (aqclass == C_IN) { if (aqtype == T_CNAME) { if (!extract_name(header, len, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0)) return; if (safe_name(daemon->namebuff) && safe_name(daemon->workspacename)) ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, daemon->workspacename, attl); } if (aqtype == T_A) { struct in_addr addr; char ip[INET_ADDRSTRLEN]; if (ardlen != INADDRSZ) return; memcpy(&addr, p, ardlen); if (inet_ntop(AF_INET, &addr, ip, sizeof ip) && safe_name(daemon->namebuff)) ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, ip, attl); } else if (aqtype == T_AAAA) { struct in6_addr addr; char ip[INET6_ADDRSTRLEN]; if (ardlen != IN6ADDRSZ) return; memcpy(&addr, p, ardlen); if (inet_ntop(AF_INET6, &addr, ip, sizeof ip) && safe_name(daemon->namebuff)) ubus_event_bcast_connmark_allowlist_resolved(mark, daemon->namebuff, ip, attl); } } p = endrr; } } #endif /* 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); int qtype, qclass; if (typep) *typep = 0; *name = 0; /* return empty name if no query found. */ if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY) return 0; /* must be exactly one query. */ if (!(header->hb3 & HB3_QR) && (ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0)) return 0; /* non-standard query. */ if (!extract_name(header, qlen, &p, name, EXTR_NAME_EXTRACT, 4)) return 0; /* bad packet */ GETSHORT(qtype, p); GETSHORT(qclass, p); if (typep) *typep = qtype; if (qclass == C_IN) { if (qtype == T_A) return F_IPV4; if (qtype == T_AAAA) return F_IPV6; if (qtype == T_ANY) return F_IPV4 | F_IPV6; } #ifdef HAVE_DNSSEC /* F_DNSSECOK as agument to search_servers() inhibits forwarding to servers for domains without a trust anchor. This make the behaviour for DS and DNSKEY queries we forward the same as for DS and DNSKEY queries we originate. */ if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DS || qtype == T_DNSKEY)) return F_DNSSECOK; #endif return F_QUERY; } void setup_reply(struct dns_header *header, unsigned int flags, int ede) { /* clear authoritative and truncated flags, set QR flag */ header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC )) | HB3_QR; /* clear AD flag, set RA flag */ header->hb4 = (header->hb4 & ~HB4_AD) | HB4_RA; header->nscount = htons(0); header->arcount = htons(0); header->ancount = htons(0); /* no answers unless changed below */ if (flags == F_NOERR) SET_RCODE(header, NOERROR); /* empty domain */ else if (flags == F_NXDOMAIN) SET_RCODE(header, NXDOMAIN); else if (flags == F_RCODE) SET_RCODE(header, NOTIMP); else if (flags & ( F_IPV4 | F_IPV6)) { SET_RCODE(header, NOERROR); header->hb3 |= HB3_AA; } else /* nowhere to forward to */ { union all_addr a; a.log.rcode = REFUSED; a.log.ede = ede; log_query(F_CONFIG | F_RCODE, "error", &a, NULL, 0); SET_RCODE(header, REFUSED); } } /* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */ int check_for_local_domain(char *name, time_t now) { struct mx_srv_record *mx; struct txt_record *txt; struct interface_name *intr; struct ptr_record *ptr; struct naptr *naptr; for (naptr = daemon->naptr; naptr; naptr = naptr->next) if (hostname_issubdomain(name, naptr->name)) return 1; for (mx = daemon->mxnames; mx; mx = mx->next) if (hostname_issubdomain(name, mx->name)) return 1; for (txt = daemon->txt; txt; txt = txt->next) if (hostname_issubdomain(name, txt->name)) return 1; for (intr = daemon->int_names; intr; intr = intr->next) if (hostname_issubdomain(name, intr->name)) return 1; for (ptr = daemon->ptr; ptr; ptr = ptr->next) if (hostname_issubdomain(name, ptr->name)) return 1; if (cache_find_non_terminal(name, now)) return 1; if (is_name_synthetic(F_IPV4, name, NULL) || is_name_synthetic(F_IPV6, name, NULL)) return 1; return 0; } static int check_bad_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr, char *name, unsigned long *ttlp) { unsigned char *p; int i, qtype, qclass, rdlen; unsigned long ttl; struct bogus_addr *baddrp; /* skip over questions */ if (!(p = skip_questions(header, qlen))) return 0; /* bad packet */ for (i = ntohs(header->ancount); i != 0; i--) { if (name && !extract_name(header, qlen, &p, name, EXTR_NAME_EXTRACT, 10)) return 0; /* bad packet */ if (!name && !(p = skip_name(p, header, qlen, 10))) return 0; GETSHORT(qtype, p); GETSHORT(qclass, p); GETLONG(ttl, p); GETSHORT(rdlen, p); if (ttlp) *ttlp = ttl; if (qclass == C_IN) { if (qtype == T_A) { struct in_addr addr; if (!CHECK_LEN(header, p, qlen, INADDRSZ)) return 0; memcpy(&addr, p, INADDRSZ); for (baddrp = baddr; baddrp; baddrp = baddrp->next) if (!baddrp->is6 && is_same_net_prefix(addr, baddrp->addr.addr4, baddrp->prefix)) return 1; } else if (qtype == T_AAAA) { struct in6_addr addr; if (!CHECK_LEN(header, p, qlen, IN6ADDRSZ)) return 0; memcpy(&addr, p, IN6ADDRSZ); for (baddrp = baddr; baddrp; baddrp = baddrp->next) if (baddrp->is6 && is_same_net6(&addr, &baddrp->addr.addr6, baddrp->prefix)) return 1; } } if (!ADD_RDLEN(header, p, qlen, rdlen)) return 0; } return 0; } /* Is the packet a reply with the answer address equal to addr? If so mung is into an NXDOMAIN reply and also put that information in the cache. */ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, time_t now) { unsigned long ttl; if (check_bad_address(header, qlen, daemon->bogus_addr, name, &ttl)) { /* Found a bogus address. Insert that info here, since there no SOA record to get the ttl from in the normal processing */ cache_start_insert(); cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | F_NXDOMAIN); cache_end_insert(); log_query(F_CONFIG | F_FORWARD | F_NEG | F_NXDOMAIN, name, NULL, NULL, 0); return 1; } return 0; } int check_for_ignored_address(struct dns_header *header, size_t qlen) { return check_bad_address(header, qlen, daemon->ignore_addr, NULL, NULL); } int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp, unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...) { va_list ap; unsigned char *sav, *p = *pp; int j; unsigned short usval; long lval; char *sval; #define CHECK_LIMIT(size) \ if (limit && p + (size) > (unsigned char*)limit) goto truncated; va_start(ap, format); /* make ap point to 1st unamed argument */ if (truncp && *truncp) goto truncated; if (nameoffset > 0) { CHECK_LIMIT(2); PUTSHORT(nameoffset | 0xc000, p); } else { char *name = va_arg(ap, char *); if (name && !(p = do_rfc1035_name(p, name, limit))) goto truncated; if (nameoffset < 0) { CHECK_LIMIT(2); PUTSHORT(-nameoffset | 0xc000, p); } else { CHECK_LIMIT(1); *p++ = 0; } } /* type (2) + class (2) + ttl (4) + rdlen (2) */ CHECK_LIMIT(10); PUTSHORT(type, p); PUTSHORT(class, p); PUTLONG(ttl, p); /* TTL */ sav = p; /* Save pointer to RDLength field */ PUTSHORT(0, p); /* Placeholder RDLength */ for (; *format; format++) switch (*format) { case '6': CHECK_LIMIT(IN6ADDRSZ); sval = va_arg(ap, char *); memcpy(p, sval, IN6ADDRSZ); p += IN6ADDRSZ; break; case '4': CHECK_LIMIT(INADDRSZ); sval = va_arg(ap, char *); memcpy(p, sval, INADDRSZ); p += INADDRSZ; break; case 'b': CHECK_LIMIT(1); usval = va_arg(ap, int); *p++ = usval; break; case 's': CHECK_LIMIT(2); usval = va_arg(ap, int); PUTSHORT(usval, p); break; case 'l': CHECK_LIMIT(4); lval = va_arg(ap, long); PUTLONG(lval, p); break; case 'd': /* get domain-name answer arg and store it in RDATA field */ if (offset) *offset = p - (unsigned char *)header; if (!(p = do_rfc1035_name(p, va_arg(ap, char *), limit))) goto truncated; CHECK_LIMIT(1); *p++ = 0; break; case 't': usval = va_arg(ap, int); CHECK_LIMIT(usval); sval = va_arg(ap, char *); if (usval != 0) memcpy(p, sval, usval); p += usval; break; case 'z': sval = va_arg(ap, char *); usval = sval ? strlen(sval) : 0; if (usval > 255) usval = 255; CHECK_LIMIT(usval + 1); *p++ = (unsigned char)usval; memcpy(p, sval, usval); p += usval; break; } va_end(ap); /* clean up variable argument pointer */ /* Now, store real RDLength. sav already checked against limit. */ j = p - sav - 2; PUTSHORT(j, sav); *pp = p; return 1; truncated: va_end(ap); if (truncp) *truncp = 1; return 0; #undef CHECK_LIMIT } static int crec_isstale(struct crec *crecp, time_t now) { return (!(crecp->flags & F_IMMORTAL)) && difftime(crecp->ttd, now) < 0; } static unsigned long crec_ttl(struct crec *crecp, time_t now) { signed long ttl = difftime(crecp->ttd, now); /* Return 0 ttl for DHCP entries, which might change before the lease expires, unless configured otherwise. */ if (crecp->flags & F_DHCP) { int conf_ttl = daemon->use_dhcp_ttl ? daemon->dhcp_ttl : daemon->local_ttl; /* Apply ceiling of actual lease length to configured TTL. */ if (!(crecp->flags & F_IMMORTAL) && ttl < conf_ttl) return ttl; return conf_ttl; } /* Immortal entries other than DHCP are local, and hold TTL in TTD field. */ if (crecp->flags & F_IMMORTAL) return crecp->ttd; /* Stale cache entries. */ if (ttl < 0) return 0; /* Return the Max TTL value if it is lower than the actual TTL */ if (daemon->max_ttl == 0 || ((unsigned)ttl < daemon->max_ttl)) return ttl; else return daemon->max_ttl; } static int cache_not_validated(const struct crec *crecp) { return (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)); } /* return zero if we can't answer from cache, or packet size if we can */ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, struct in_addr local_addr, struct in_addr local_netmask, time_t now, int ad_reqd, int do_bit, int no_cache, int *stale, int *filtered) { char *name = daemon->namebuff; unsigned char *p, *ansp; unsigned int qtype, qclass; union all_addr addr; int nameoffset; unsigned short flag; int ans, anscount = 0, nscount = 0, addncount = 0; struct crec *crecp, *soa_lookup = NULL; int nxdomain = 0, notimp = 0, auth = 1, trunc = 0, sec_data = 1; struct mx_srv_record *rec; size_t len; int rd_bit = (header->hb3 & HB3_RD); int count = 255; /* catch loops */ /* Suppress cached answers of no_cache set. */ if (no_cache) rd_bit = 0; if (stale) *stale = 0; if (filtered) *filtered = 0; /* never answer queries with RD unset, to avoid cache snooping. */ if ( ntohs(header->qdcount) != 1 || ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0 || ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY ) return 0; /* Don't return AD set if checking disabled. */ if (header->hb4 & HB4_CD) sec_data = 0; for (rec = daemon->mxnames; rec; rec = rec->next) rec->offset = 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); /* 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, EXTR_NAME_EXTRACT, 4)) return 0; /* bad packet */ GETSHORT(qtype, p); GETSHORT(qclass, p); ans = 0; /* have we answered this question */ if (qclass == C_IN) while (--count != 0 && (crecp = cache_find_by_name(NULL, name, now, F_CNAME | F_NXDOMAIN))) { char *cname_target; int stale_flag = 0; if (crec_isstale(crecp, now)) { if (stale) *stale = 1; stale_flag = F_STALE; } if (crecp->flags & F_NEG) soa_lookup = crecp; if (crecp->flags & F_NXDOMAIN) { if (qtype == T_CNAME) { log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0); auth = 0; nxdomain = 1; ans = 1; } break; } cname_target = cache_get_cname_target(crecp); /* If the client asked for DNSSEC don't use cached data. */ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || (rd_bit && (!do_bit || cache_not_validated(crecp)))) { if (crecp->flags & F_CONFIG || qtype == T_CNAME) ans = 1; if (!(crecp->flags & F_DNSSECOK)) sec_data = 0; log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, crec_ttl(crecp, now), &nameoffset, T_CNAME, C_IN, "d", cname_target)) anscount++; } else return 0; /* give up if any cached CNAME in chain can't be used for DNSSEC reasons. */ if (qtype == T_CNAME) break; strcpy(name, cname_target); } if (qtype == T_TXT || qtype == T_ANY) { struct txt_record *t; for(t = daemon->txt; t ; t = t->next) { if (t->class == qclass && hostname_isequal(name, t->name)) { unsigned long ttl = daemon->local_ttl; int ok = 1; ans = 1, sec_data = 0; #ifndef NO_ID /* Dynamically generate stat record */ if (t->stat != 0) { ttl = 0; if (!cache_make_stat(t)) ok = 0; } #endif if (ok) { log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL, T_TXT, t->class, "t", t->len, t->txt)) anscount++; } } } } if (qclass == C_CHAOS) { /* don't forward *.bind and *.server chaos queries - always reply with NOTIMP */ if (hostname_issubdomain("bind", name) || hostname_issubdomain("server", name)) { if (!ans) { notimp = 1, auth = 0; addr.log.rcode = NOTIMP; log_query(F_CONFIG | F_RCODE, name, &addr, NULL, 0); ans = 1, sec_data = 0; } } } 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; sec_data = 0; log_query(F_CONFIG | F_RRNAME, name, NULL, NULL, t->class); 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 */ int is_arpa = in_arpa_name_2_addr(name, &addr); struct ptr_record *ptr; struct interface_name* intr = NULL; for (ptr = daemon->ptr; ptr; ptr = ptr->next) if (hostname_isequal(name, ptr->name)) break; if (is_arpa == 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.addr4.s_addr == addrlist->addr.addr4.s_addr) break; if (addrlist) break; else if (!(intr->flags & INP4)) while (intr->next && strcmp(intr->intr, intr->next->intr) == 0) intr = intr->next; } 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.addr6, &addrlist->addr.addr6)) break; if (addrlist) break; else if (!(intr->flags & INP6)) while (intr->next && strcmp(intr->intr, intr->next->intr) == 0) intr = intr->next; } if (intr) { sec_data = 0; ans = 1; log_query(is_arpa | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, 0); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, T_PTR, C_IN, "d", intr->name)) anscount++; } else if (ptr) { ans = 1; sec_data = 0; log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); for (ptr = daemon->ptr; ptr; ptr = ptr->next) if (hostname_isequal(name, ptr->name) && add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, T_PTR, C_IN, "d", ptr->ptr)) anscount++; } else if (is_arpa && (crecp = cache_find_by_addr(NULL, &addr, now, is_arpa))) { /* Don't use cache when DNSSEC data required, unless we know that the zone is unsigned, which implies that we're doing validation. */ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || (rd_bit && (!do_bit || cache_not_validated(crecp)) )) { do { int stale_flag = 0; if (crec_isstale(crecp, now)) { if (stale) *stale = 1; stale_flag = F_STALE; } /* 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; ans = 1; if (crecp->flags & F_NEG) { auth = 0; if (crecp->flags & F_NXDOMAIN) nxdomain = 1; log_query(stale_flag | (crecp->flags & ~F_FORWARD), name, &addr, NULL, 0); soa_lookup = crecp; } else { if (!(crecp->flags & (F_HOSTS | F_DHCP))) auth = 0; log_query(stale_flag | (crecp->flags & ~F_FORWARD), cache_get_name(crecp), &addr, record_source(crecp->uid), 0); 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; sec_data = 0; log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL, 0); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, T_PTR, C_IN, "d", name)) anscount++; } else if (option_bool(OPT_BOGUSPRIV) && ((is_arpa == F_IPV6 && private_net6(&addr.addr6, 1)) || (is_arpa == F_IPV4 && private_net(addr.addr4, 1))) && !lookup_domain(name, F_DOMAINSRV, NULL, NULL)) { /* if no configured server, not in cache, enabled and private IPV4 address, return NXDOMAIN */ ans = 1; sec_data = 0; nxdomain = 1; log_query(F_CONFIG | F_REVERSE | is_arpa | F_NEG | F_NXDOMAIN, name, &addr, NULL, 0); } } for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0) { unsigned short type = (flag == F_IPV6) ? T_AAAA : T_A; struct interface_name *intr; if (qtype != type && qtype != T_ANY) continue; /* interface name stuff */ for (intr = daemon->int_names; intr; intr = intr->next) if (hostname_isequal(name, intr->name)) break; if (intr) { struct addrlist *addrlist; int gotit = 0, localise = 0; enumerate_interfaces(0); /* See if a putative address is on the network from which we received the query, is so we'll filter other answers. */ if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && type == T_A) for (intr = daemon->int_names; intr; intr = intr->next) if (hostname_isequal(name, intr->name)) for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) if (!(addrlist->flags & ADDRLIST_IPV6) && is_same_net(addrlist->addr.addr4, local_addr, local_netmask)) { localise = 1; break; } for (intr = daemon->int_names; intr; intr = intr->next) if (hostname_isequal(name, intr->name)) { for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type) { if (localise && !is_same_net(addrlist->addr.addr4, local_addr, local_netmask)) continue; if (addrlist->flags & ADDRLIST_REVONLY) continue; ans = 1; sec_data = 0; gotit = 1; log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, 0); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, type, C_IN, type == T_A ? "4" : "6", &addrlist->addr)) anscount++; } } if (!gotit) log_query(F_FORWARD | F_CONFIG | flag | F_NEG, name, NULL, NULL, 0); continue; } if ((crecp = cache_find_by_name(NULL, name, now, flag))) { int localise = 0; /* See if a putative address is on the network from which we received the query, is so we'll filter other answers. */ if (!(crecp->flags & F_NEG) && local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4) { struct crec *save = crecp; do { if ((crecp->flags & F_HOSTS) && is_same_net(crecp->addr.addr4, local_addr, local_netmask)) { localise = 1; break; } } while ((crecp = cache_find_by_name(crecp, name, now, flag))); crecp = save; } /* If the client asked for DNSSEC don't use cached data. */ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || (rd_bit && (!do_bit || cache_not_validated(crecp)) )) do { int stale_flag = 0; if (crec_isstale(crecp, now)) { if (stale) *stale = 1; stale_flag = F_STALE; } /* 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_HOSTS | F_DHCP))) auth = 0; if (qtype != T_ANY && rr_on_list(daemon->filter_rr, qtype) && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG | F_NEG))) { /* We have a cached answer but we're filtering it. */ ans = 1; sec_data = 0; log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); if (filtered) *filtered = 1; } else if (crecp->flags & F_NEG) { if (qtype != T_ANY) { ans = 1; auth = 0; soa_lookup = crecp; if (crecp->flags & F_NXDOMAIN) nxdomain = 1; log_query(stale_flag | crecp->flags, name, NULL, NULL, 0); } } else { /* If we are returning local answers depending on network, filter here. */ if (localise && (crecp->flags & F_HOSTS) && !is_same_net(crecp->addr.addr4, local_addr, local_netmask)) continue; ans = 1; log_query(stale_flag | (crecp->flags & ~F_REVERSE), name, &crecp->addr, record_source(crecp->uid), 0); 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))); } else if (is_name_synthetic(flag, name, &addr)) { ans = 1, sec_data = 0; log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL, 0); 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_MX || qtype == T_ANY) { int found = 0; for (rec = daemon->mxnames; rec; rec = rec->next) if (!rec->issrv && hostname_isequal(name, rec->name)) { int offset; ans = found = 1; sec_data = 0; log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, &offset, T_MX, C_IN, "sd", rec->weight, rec->target)) { anscount++; if (rec->target) rec->offset = offset; } } if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) && cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP | F_NO_RR)) { ans = 1; sec_data = 0; log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, T_MX, C_IN, "sd", 1, option_bool(OPT_SELFMX) ? name : daemon->mxtarget)) anscount++; } } if (qtype == T_SRV || qtype == T_ANY) { struct mx_srv_record *move = NULL, **up = &daemon->mxnames; for (rec = daemon->mxnames; rec; rec = rec->next) if (rec->issrv && hostname_isequal(name, rec->name)) { int offset; ans = 1; sec_data = 0; log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, &offset, T_SRV, C_IN, "sssd", rec->priority, rec->weight, rec->srvport, rec->target)) { anscount++; if (rec->target) rec->offset = offset; } /* 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; } } if (qtype == T_NAPTR || qtype == T_ANY) { struct naptr *na; for (na = daemon->naptr; na; na = na->next) if (hostname_isequal(name, na->name)) { ans = 1; sec_data = 0; log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, T_NAPTR, C_IN, "sszzzd", na->order, na->pref, na->flags, na->services, na->regexp, na->replace)) anscount++; } } if (qtype == T_MAILB) ans = 1, nxdomain = 1, sec_data = 0; if (qtype == T_SOA && option_bool(OPT_FILTER)) { ans = 1; sec_data = 0; log_query(F_CONFIG | F_NEG, name, &addr, NULL, 0); } if (!ans) { if ((crecp = cache_find_by_name(NULL, name, now, F_RR | F_NXDOMAIN)) && rd_bit) do { int flags = crecp->flags; unsigned short rrtype; if (flags & F_KEYTAG) rrtype = crecp->addr.rrblock.rrtype; else rrtype = crecp->addr.rrdata.rrtype; if (((flags & F_NXDOMAIN) || rrtype == qtype) && (!do_bit || cache_not_validated(crecp))) { char *rrdata = NULL; unsigned short rrlen = 0; if (crec_isstale(crecp, now)) { if (stale) *stale = 1; flags |= F_STALE; } if (!(flags & F_DNSSECOK)) sec_data = 0; if (flags & F_NXDOMAIN) nxdomain = 1; else if (qtype != T_ANY && rr_on_list(daemon->filter_rr, qtype)) flags |= F_NEG | F_CONFIG; auth = 0; ans = 1; if (flags & F_NEG) soa_lookup = crecp; if (!(flags & F_NEG)) { if (flags & F_KEYTAG) { rrlen = crecp->addr.rrblock.datalen; rrdata = blockdata_retrieve(crecp->addr.rrblock.rrdata, crecp->addr.rrblock.datalen, NULL); } else { rrlen = crecp->addr.rrdata.datalen; rrdata = crecp->addr.rrdata.data; } } if (!(flags & F_NEG) && add_resource_record(header, limit, &trunc, nameoffset, &ansp, crec_ttl(crecp, now), NULL, qtype, C_IN, "t", rrlen, rrdata)) anscount++; /* log after cache insertion as log_txt mangles rrdata */ if (qtype == T_TXT && !(flags & F_NEG)) log_txt(name, (unsigned char *)rrdata, rrlen, flags & (F_DNSSECOK | F_STALE)); else log_query(flags, name, &crecp->addr, NULL, 0); } } while ((crecp = cache_find_by_name(crecp, name, now, F_RR))); } if (!ans && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) { ans = 1; sec_data = 0; log_query(F_CONFIG | F_NEG, name, NULL, NULL, 0); } if (qtype != T_ANY && !ans && rr_on_list(daemon->filter_rr, qtype) && !do_bit) { /* We don't have a cached answer and when we get an answer from upstream we're going to filter it anyway. If we have a cached answer for the domain for another RRtype then that may be enough to tell us if the answer should be NODATA and save the round trip. Cached NXDOMAIN has already been handled, so here we look for any record for the domain, since its existence allows us to return a NODATA answer. Note that we never set the AD flag, since we didn't authenticate the record; this doesn't work if we want auth data, so don't use this shortcut in that case. */ if (cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_RR | F_CNAME)) { ans = 1; sec_data = auth = 0; log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); if (filtered) *filtered = 1; } } } if (!ans) return 0; /* failed to answer a question */ /* We found a negative record. See if we have an SOA record to return in the AUTH section. For FORWARD NEG records, the addr.rrdata.datalen field of the othewise empty addr is used to held an offset in to the name which yields the SOA name. For REVERSE NEG records, the otherwise empty name field holds the SOA name. If soa_name has zero length, then no SOA is known. soa_lookup MUST be a neg record here. If the F_NO_RR flag is set, there was no SOA record supplied with the RR. */ if (soa_lookup && !(soa_lookup->flags & F_NO_RR)) { char *soa_name = soa_lookup->flags & F_REVERSE ? cache_get_name(soa_lookup) : name + soa_lookup->addr.rrdata.datalen; crecp = NULL; while ((crecp = cache_find_by_name(crecp, soa_name, now, F_RR))) if (crecp->addr.rrblock.rrtype == T_SOA) { char *rrdata; if (!(crecp->flags & F_NEG) && (rrdata = blockdata_retrieve(crecp->addr.rrblock.rrdata, crecp->addr.rrblock.datalen, NULL)) && add_resource_record(header, limit, &trunc, 0, &ansp, crec_ttl(crecp, now), NULL, T_SOA, C_IN, "t", soa_name, crecp->addr.rrblock.datalen, rrdata)) { nscount++; if (!(crecp->flags & F_DNSSECOK)) sec_data = 0; } break; } } /* create an additional data section, for stuff in SRV and MX record replies. */ for (rec = daemon->mxnames; rec; rec = rec->next) if (rec->offset != 0) { /* squash dupes */ struct mx_srv_record *tmp; for (tmp = rec->next; tmp; tmp = tmp->next) if (tmp->offset != 0 && hostname_isequal(rec->target, tmp->target)) tmp->offset = 0; crecp = NULL; while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6))) { int type = crecp->flags & F_IPV4 ? T_A : T_AAAA; if (crecp->flags & F_NEG) continue; if (add_resource_record(header, limit, NULL, rec->offset, &ansp, crec_ttl(crecp, now), NULL, type, C_IN, crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr)) { addncount++; if (!(crecp->flags & F_DNSSECOK)) sec_data = 0; } } } /* 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; /* set RA flag */ header->hb4 |= HB4_RA; /* authoritative - only hosts and DHCP derived names. */ if (auth) header->hb3 |= HB3_AA; /* truncation */ if (trunc) { header->hb3 |= HB3_TC; if (!(ansp = skip_questions(header, qlen))) return 0; /* bad packet */ anscount = nscount = addncount = 0; log_query(0, "reply", NULL, "truncated", 0); } if (nxdomain) SET_RCODE(header, NXDOMAIN); else if (notimp) SET_RCODE(header, NOTIMP); else SET_RCODE(header, NOERROR); /* no error */ header->ancount = htons(anscount); header->nscount = htons(nscount); header->arcount = htons(addncount); len = ansp - (unsigned char *)header; if (ad_reqd && sec_data) header->hb4 |= HB4_AD; else header->hb4 &= ~HB4_AD; return len; } dnsmasq-2.91/src/radv.c0000664000175000017500000007630514765043257013214 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ /* 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 struct ra_param { time_t now; int ind, managed, other, 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 dhcp_context *found_context; }; 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, unsigned int prefered, unsigned 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 receive 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_unsolicited(now, NULL); } void ra_start_unsolicited(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 = now; /* start after 1 second to get logging right at startup. */ context->ra_time = now + 1; } 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 && (tmp->flags & INAME_6) && 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; ssize_t rem; unsigned char *p; int opt_sz; #ifdef HAVE_DUMPFILE dump_packet_icmp(DUMP_RA, (void *)packet, sz, (union mysockaddr *)&from, NULL); #endif /* look for link-layer address option for logging */ for (rem = sz - 8, p = &packet[8]; rem >= 2; rem -= opt_sz, p += opt_sz) { opt_sz = p[1] * 8; if (opt_sz == 0 || opt_sz > rem) return; /* Bad packet */ if (p[0] == ICMP6_OPT_SOURCE_MAC && ((opt_sz - 2) * 3 - 1 < MAXDNAME)) { print_mac(daemon->namebuff, &p[2], opt_sz - 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, mtu = 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 = NULL; 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); reset_counter(); if (!(ra = expand(sizeof(struct ra_packet)))) return; 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 no link-local address then we can't advertise since source address of advertisement must be link local address: RFC 4861 para 6.1.2. */ if (!iface_enumerate(AF_INET6, &parm, (callback_t){.af_inet6=add_prefixes}) || parm.link_pref_time == 0) 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 */ /* If this context held the timeout, and there's another context in use transfer the timeout there. */ if (context->ra_time != 0 && parm.found_context && parm.found_context->ra_time == 0) new_timeout(parm.found_context, iface_name, now); *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))); } /* Set the MTU from ra_param if any, an MTU of 0 mean automatic for linux, */ /* an MTU of -1 prevents the option from being sent. */ if (ra_param) mtu = ra_param->mtu; #ifdef HAVE_LINUX_NETWORK /* Note that IPv6 MTU is not necessarily the same as the IPv4 MTU available from SIOCGIFMTU */ if (mtu == 0) { char *mtu_name = ra_param ? ra_param->mtu_name : NULL; sprintf(daemon->namebuff, "/proc/sys/net/ipv6/conf/%s/mtu", mtu_name ? mtu_name : iface_name); if ((f = fopen(daemon->namebuff, "r"))) { if (fgets(daemon->namebuff, MAXDNAME, f)) mtu = atoi(daemon->namebuff); fclose(f); } } #endif if (mtu > 0) { put_opt6_char(ICMP6_OPT_MTU); put_opt6_char(1); put_opt6_short(0); put_opt6_long(mtu); } iface_enumerate(AF_LOCAL, &send_iface, (callback_t){.af_local=add_lla}); /* RDNSS, RFC 6106, use relevant DHCP6 options */ (void)option_filter(parm.tags, NULL, daemon->dhcp_opts6, 0); 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)); } #ifdef HAVE_DUMPFILE { struct sockaddr_in6 src; src.sin6_family = AF_INET6; src.sin6_addr = parm.link_local; dump_packet_icmp(DUMP_RA, (void *)daemon->outpacket.iov_base, save_counter(-1), (union mysockaddr *)&src, (union mysockaddr *)&addr); } #endif while (retry_send(sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(-1), 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 the lease time has been left as default, don't use that as a floor. */ if ((context->flags & CONTEXT_SETLEASE) && 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; /* found_context is the _last_ one we found, so if there's more than one, it's not the first. */ param->found_context = context; } /* 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); if (!p) return 1; 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, ¶m, (callback_t){.af_inet6=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 satisfy 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 && (tmp->flags & INAME_6) && 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, (callback_t){.af_local=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, (callback_t){.af_local=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, unsigned int preferred, unsigned int valid, void *vparam) { struct search_param *param = vparam; struct dhcp_context *context; struct iname *tmp; (void)scope; (void)preferred; (void)valid; /* ignore interfaces we're not doing DHCP on. */ if (!indextoname(daemon->icmp6fd, if_index, param->name) || !iface_check(AF_LOCAL, NULL, param->name, NULL)) return 1; for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) if (tmp->name && (tmp->flags & INAME_6) && wildcard_match(tmp->name, param->name)) return 1; 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; 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 dnsmasq-2.91/src/dhcp-common.c0000664000175000017500000007324114765043257014460 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #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(DHCP_BUFF_SZ); daemon->dhcp_buff2 = safe_malloc(DHCP_BUFF_SZ); daemon->dhcp_buff3 = safe_malloc(DHCP_BUFF_SZ); /* 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, new_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 ((new_sz = recvmsg(fd, msg, 0)) == -1 && errno == EINTR); /* Some kernels seem to ignore MSG_PEEK, and dequeue the packet anyway. If that happens we get EAGAIN here because the socket is non-blocking. Use the result of the original testing recvmsg as long as the buffer was big enough. There's a small race here that may lose the odd packet, but it's UDP anyway. */ if (new_sz == -1 && (errno == EWOULDBLOCK || errno == EAGAIN)) new_sz = sz; return (msg->msg_flags & MSG_TRUNC) ? -1 : new_sz; } /* like match_netid() except that the check can have a trailing * for wildcard */ /* started as a direct copy of match_netid() */ int match_netid_wild(struct dhcp_netid *check, struct dhcp_netid *pool) { struct dhcp_netid *tmp1; for (; check; check = check->next) { const int check_len = strlen(check->net); const int is_wc = (check_len > 0 && check->net[check_len - 1] == '*'); /* '#' for not is for backwards compat. */ if (check->net[0] != '!' && check->net[0] != '#') { for (tmp1 = pool; tmp1; tmp1 = tmp1->next) if (is_wc ? (strncmp(check->net, tmp1->net, check_len-1) == 0) : (strcmp(check->net, tmp1->net) == 0)) break; if (!tmp1) return 0; } else for (tmp1 = pool; tmp1; tmp1 = tmp1->next) if (is_wc ? (strncmp((check->net)+1, tmp1->net, check_len-2) == 0) : (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; /* this now uses match_netid_wild() above so that tag_if can * be used to set a 'group of interfaces' tag. */ for (exprs = daemon->tag_if; exprs; exprs = exprs->next) if (match_netid_wild(exprs->tag, tags)) for (list = exprs->set; list; list = list->next) { list->list->next = tags; tags = list->list; } return tags; } /* pxemode == 0 -> don't include dhcp-option-pxe options. pxemode == 1 -> do include dhcp-option-pxe options. pxemode == 2 -> include ONLY dhcp-option-pxe options. */ int pxe_ok(struct dhcp_opt *opt, int pxemode) { if (opt->flags & DHOPT_PXE_OPT) { if (pxemode != 0) return 1; } else { if (pxemode != 2) return 1; } return 0; } struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags, struct dhcp_opt *opts, int pxemode) { 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) && pxe_ok(opt, pxemode)) 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:! 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) && pxe_ok(opt, pxemode)) { 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 && pxe_ok(opt, pxemode)) { 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) { struct addrlist *addr_list; if (config->flags & CONFIG_ADDR6) for (; context; context = context->current) for (addr_list = config->addr6; addr_list; addr_list = addr_list->next) { if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64) return 1; if (is_same_net6(&addr_list->addr.addr6, &context->start6, context->prefix)) return 1; } } else #endif { for (; context; context = context->current) if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask)) return 1; } return 0; } static struct dhcp_config *find_config_match(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, struct dhcp_netid *tags, int tag_not_needed) { 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) && match_netid(config->filter, tags, tag_not_needed)) 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) && match_netid(config->filter, tags, tag_not_needed)) 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) && match_netid(config->filter, tags, tag_not_needed)) 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) && match_netid(config->filter, tags, tag_not_needed)) 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) && match_netid(config->filter, tags, tag_not_needed)) 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; } /* Find tagged configs first. */ 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, struct dhcp_netid *tags) { struct dhcp_config *ret = find_config_match(configs, context, clid, clid_len, hwaddr, hw_len, hw_type, hostname, tags, 0); if (!ret) ret = find_config_match(configs, context, clid, clid_len, hwaddr, hw_len, hw_type, hostname, tags, 1); return ret; } 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_ADDR_HOSTS); #ifdef HAVE_DHCP6 if (config->flags & CONFIG_ADDR6_HOSTS) config->flags &= ~(CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS); #endif } #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, 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.addr4)) || conf_tmp == config)) { config->addr = crec->addr.addr4; config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS; continue; } #ifdef HAVE_DHCP6 if (prot == AF_INET6 && (!(conf_tmp = config_find_by_address6(configs, NULL, 0, &crec->addr.addr6)) || conf_tmp == config)) { /* host must have exactly one address if comming from /etc/hosts. */ if (!config->addr6 && (config->addr6 = whine_malloc(sizeof(struct addrlist)))) { config->addr6->next = NULL; config->addr6->flags = 0; } if (config->addr6 && !config->addr6->next && !(config->addr6->flags & (ADDRLIST_WILDCARD|ADDRLIST_PREFIX))) { memcpy(&config->addr6->addr.addr6, &crec->addr.addr6, IN6ADDRSZ); config->flags |= CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS; } continue; } #endif inet_ntop(prot, &crec->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->flags & INAME_USED) || strchr(if_tmp->name, '*'))) return NULL; for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next) if (iface->dhcp4_ok || iface->dhcp6_ok) { if (!found) found = iface; else if (strcmp(found->name, iface->name) != 0) return NULL; /* more than one. */ } if (found) { char *ret = safe_malloc(strlen(found->name)+1); strcpy(ret, found->name); return ret; } return NULL; } static int bindtodevice(char *device, int fd) { size_t len = strlen(device)+1; if (len > IFNAMSIZ) len = IFNAMSIZ; /* only allowed by root. */ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, len) == -1 && errno != EPERM) return 2; return 1; } int bind_dhcp_devices(char *bound_device) { int ret = 0; if (bound_device) { if (daemon->dhcp) { if (!daemon->relay4) ret |= bindtodevice(bound_device, daemon->dhcpfd); if (daemon->enable_pxe && daemon->pxefd != -1) ret |= bindtodevice(bound_device, daemon->pxefd); } #if defined(HAVE_DHCP6) if (daemon->doing_dhcp6 && !daemon->relay6) ret |= bindtodevice(bound_device, daemon->dhcp6fd); #endif } return ret; } #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 }, { "rapid-commit", 80, 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 }, { "posix-timezone", 100, OT_NAME }, /* RFC 4833, Sec. 2 */ { "tzdb-timezone", 101, OT_NAME }, /* RFC 4833, Sec. 2 */ { "ipv6-only", 108, 4 | OT_DEC }, /* RFC 8925 */ { "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 }, { "tftp-server-address", 150, OT_ADDR_LIST }, { "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 }, { "posix-timezone", 41, OT_NAME }, /* RFC 4833, Sec. 3 */ { "tzdb-timezone", 42, OT_NAME }, /* RFC 4833, Sec. 3 */ { "ntp-server", 56, 0 /* OT_ADDR_LIST | OT_RFC1035_NAME */ }, { "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) { union 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((unsigned char)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((unsigned char)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((unsigned char)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, DHCP_BUFF_SZ); else strcpy(daemon->dhcp_buff, daemon->addrbuff); } else #endif inet_ntop(family, start, daemon->dhcp_buff, DHCP_BUFF_SZ); inet_ntop(family, end, daemon->dhcp_buff3, DHCP_BUFF_SZ); 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) { int broadcast = relay->server.addr4.s_addr == 0; inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN); inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN); if (family == AF_INET && relay->port != DHCP_SERVER_PORT) sprintf(daemon->namebuff + strlen(daemon->namebuff), "#%u", relay->port); #ifdef HAVE_DHCP6 struct in6_addr multicast; inet_pton(AF_INET6, ALL_SERVERS, &multicast); if (family == AF_INET6) { broadcast = IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast); if (relay->port != DHCPV6_SERVER_PORT) sprintf(daemon->namebuff + strlen(daemon->namebuff), "#%u", relay->port); } #endif if (relay->interface) { if (broadcast) my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s via %s"), daemon->addrbuff, relay->interface); else 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 dnsmasq-2.91/src/ubus.c0000664000175000017500000002441314765043257013227 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_UBUS #include static struct blob_buf b; static int error_logged = 0; static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg); #ifdef HAVE_CONNTRACK enum { SET_CONNMARK_ALLOWLIST_MARK, SET_CONNMARK_ALLOWLIST_MASK, SET_CONNMARK_ALLOWLIST_PATTERNS }; static const struct blobmsg_policy set_connmark_allowlist_policy[] = { [SET_CONNMARK_ALLOWLIST_MARK] = { .name = "mark", .type = BLOBMSG_TYPE_INT32 }, [SET_CONNMARK_ALLOWLIST_MASK] = { .name = "mask", .type = BLOBMSG_TYPE_INT32 }, [SET_CONNMARK_ALLOWLIST_PATTERNS] = { .name = "patterns", .type = BLOBMSG_TYPE_ARRAY } }; static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg); #endif static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj); static const struct ubus_method ubus_object_methods[] = { UBUS_METHOD_NOARG("metrics", ubus_handle_metrics), #ifdef HAVE_CONNTRACK UBUS_METHOD("set_connmark_allowlist", ubus_handle_set_connmark_allowlist, set_connmark_allowlist_policy), #endif }; static struct ubus_object_type ubus_object_type = UBUS_OBJECT_TYPE("dnsmasq", ubus_object_methods); static struct ubus_object ubus_object = { .name = NULL, .type = &ubus_object_type, .methods = ubus_object_methods, .n_methods = ARRAY_SIZE(ubus_object_methods), .subscribe_cb = ubus_subscribe_cb, }; static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj) { (void)ctx; my_syslog(LOG_DEBUG, _("UBus subscription callback: %s subscriber(s)"), obj->has_subscribers ? "1" : "0"); } static void ubus_destroy(struct ubus_context *ubus) { ubus_free(ubus); daemon->ubus = NULL; /* Forces re-initialization when we're reusing the same definitions later on. */ ubus_object.id = 0; ubus_object_type.id = 0; } static void ubus_disconnect_cb(struct ubus_context *ubus) { int ret; ret = ubus_reconnect(ubus, NULL); if (ret) { my_syslog(LOG_ERR, _("Cannot reconnect to UBus: %s"), ubus_strerror(ret)); ubus_destroy(ubus); } } char *ubus_init() { struct ubus_context *ubus = NULL; int ret = 0; if (!(ubus = ubus_connect(NULL))) return NULL; ubus_object.name = daemon->ubus_name; ret = ubus_add_object(ubus, &ubus_object); if (ret) { ubus_destroy(ubus); return (char *)ubus_strerror(ret); } ubus->connection_lost = ubus_disconnect_cb; daemon->ubus = ubus; error_logged = 0; return NULL; } void set_ubus_listeners() { struct ubus_context *ubus = (struct ubus_context *)daemon->ubus; if (!ubus) { if (!error_logged) { my_syslog(LOG_ERR, _("Cannot set UBus listeners: no connection")); error_logged = 1; } return; } error_logged = 0; poll_listen(ubus->sock.fd, POLLIN); poll_listen(ubus->sock.fd, POLLERR); poll_listen(ubus->sock.fd, POLLHUP); } void check_ubus_listeners() { struct ubus_context *ubus = (struct ubus_context *)daemon->ubus; if (!ubus) { if (!error_logged) { my_syslog(LOG_ERR, _("Cannot poll UBus listeners: no connection")); error_logged = 1; } return; } error_logged = 0; if (poll_check(ubus->sock.fd, POLLIN)) ubus_handle_event(ubus); if (poll_check(ubus->sock.fd, POLLHUP | POLLERR)) { my_syslog(LOG_INFO, _("Disconnecting from UBus")); ubus_destroy(ubus); } } #define CHECK(stmt) \ do { \ int e = (stmt); \ if (e) \ { \ my_syslog(LOG_ERR, _("UBus command failed: %d (%s)"), e, #stmt); \ return (UBUS_STATUS_UNKNOWN_ERROR); \ } \ } while (0) static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { int i; (void)obj; (void)method; (void)msg; CHECK(blob_buf_init(&b, BLOBMSG_TYPE_TABLE)); for (i=0; i < __METRIC_MAX; i++) CHECK(blobmsg_add_u32(&b, get_metric_name(i), daemon->metrics[i])); CHECK(ubus_send_reply(ctx, req, b.head)); return UBUS_STATUS_OK; } #ifdef HAVE_CONNTRACK static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { const struct blobmsg_policy *policy = set_connmark_allowlist_policy; size_t policy_len = countof(set_connmark_allowlist_policy); struct allowlist *allowlists = NULL, **allowlists_pos; char **patterns = NULL, **patterns_pos; u32 mark, mask = UINT32_MAX; size_t num_patterns = 0; struct blob_attr *tb[policy_len]; struct blob_attr *attr; if (blobmsg_parse(policy, policy_len, tb, blob_data(msg), blob_len(msg))) return UBUS_STATUS_INVALID_ARGUMENT; if (!tb[SET_CONNMARK_ALLOWLIST_MARK]) return UBUS_STATUS_INVALID_ARGUMENT; mark = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MARK]); if (!mark) return UBUS_STATUS_INVALID_ARGUMENT; if (tb[SET_CONNMARK_ALLOWLIST_MASK]) { mask = blobmsg_get_u32(tb[SET_CONNMARK_ALLOWLIST_MASK]); if (!mask || (mark & ~mask)) return UBUS_STATUS_INVALID_ARGUMENT; } if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS]) { struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]); size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]); __blob_for_each_attr(attr, head, len) { char *pattern; if (blob_id(attr) != BLOBMSG_TYPE_STRING) return UBUS_STATUS_INVALID_ARGUMENT; if (!(pattern = blobmsg_get_string(attr))) return UBUS_STATUS_INVALID_ARGUMENT; if (strcmp(pattern, "*") && !is_valid_dns_name_pattern(pattern)) return UBUS_STATUS_INVALID_ARGUMENT; num_patterns++; } } for (allowlists_pos = &daemon->allowlists; *allowlists_pos; allowlists_pos = &(*allowlists_pos)->next) if ((*allowlists_pos)->mark == mark && (*allowlists_pos)->mask == mask) { struct allowlist *allowlists_next = (*allowlists_pos)->next; for (patterns_pos = (*allowlists_pos)->patterns; *patterns_pos; patterns_pos++) { free(*patterns_pos); *patterns_pos = NULL; } free((*allowlists_pos)->patterns); (*allowlists_pos)->patterns = NULL; free(*allowlists_pos); *allowlists_pos = allowlists_next; break; } if (!num_patterns) return UBUS_STATUS_OK; patterns = whine_malloc((num_patterns + 1) * sizeof(char *)); if (!patterns) goto fail; patterns_pos = patterns; if (tb[SET_CONNMARK_ALLOWLIST_PATTERNS]) { struct blob_attr *head = blobmsg_data(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]); size_t len = blobmsg_data_len(tb[SET_CONNMARK_ALLOWLIST_PATTERNS]); __blob_for_each_attr(attr, head, len) { char *pattern; if (!(pattern = blobmsg_get_string(attr))) goto fail; if (!(*patterns_pos = whine_malloc(strlen(pattern) + 1))) goto fail; strcpy(*patterns_pos++, pattern); } } allowlists = whine_malloc(sizeof(struct allowlist)); if (!allowlists) goto fail; memset(allowlists, 0, sizeof(struct allowlist)); allowlists->mark = mark; allowlists->mask = mask; allowlists->patterns = patterns; allowlists->next = daemon->allowlists; daemon->allowlists = allowlists; return UBUS_STATUS_OK; fail: if (patterns) { for (patterns_pos = patterns; *patterns_pos; patterns_pos++) { free(*patterns_pos); *patterns_pos = NULL; } free(patterns); patterns = NULL; } if (allowlists) { free(allowlists); allowlists = NULL; } return UBUS_STATUS_UNKNOWN_ERROR; } #endif #undef CHECK #define CHECK(stmt) \ do { \ int e = (stmt); \ if (e) \ { \ my_syslog(LOG_ERR, _("UBus command failed: %d (%s)"), e, #stmt); \ return; \ } \ } while (0) void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface) { struct ubus_context *ubus = (struct ubus_context *)daemon->ubus; if (!ubus || !ubus_object.has_subscribers) return; CHECK(blob_buf_init(&b, BLOBMSG_TYPE_TABLE)); if (mac) CHECK(blobmsg_add_string(&b, "mac", mac)); if (ip) CHECK(blobmsg_add_string(&b, "ip", ip)); if (name) CHECK(blobmsg_add_string(&b, "name", name)); if (interface) CHECK(blobmsg_add_string(&b, "interface", interface)); CHECK(ubus_notify(ubus, &ubus_object, type, b.head, -1)); } #ifdef HAVE_CONNTRACK void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name) { struct ubus_context *ubus = (struct ubus_context *)daemon->ubus; if (!ubus || !ubus_object.has_subscribers) return; CHECK(blob_buf_init(&b, 0)); CHECK(blobmsg_add_u32(&b, "mark", mark)); CHECK(blobmsg_add_string(&b, "name", name)); CHECK(ubus_notify(ubus, &ubus_object, "connmark-allowlist.refused", b.head, -1)); } void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *name, const char *value, u32 ttl) { struct ubus_context *ubus = (struct ubus_context *)daemon->ubus; if (!ubus || !ubus_object.has_subscribers) return; CHECK(blob_buf_init(&b, 0)); CHECK(blobmsg_add_u32(&b, "mark", mark)); CHECK(blobmsg_add_string(&b, "name", name)); CHECK(blobmsg_add_string(&b, "value", value)); CHECK(blobmsg_add_u32(&b, "ttl", ttl)); /* Set timeout to allow UBus subscriber to configure firewall rules before returning. */ CHECK(ubus_notify(ubus, &ubus_object, "connmark-allowlist.resolved", b.head, /* timeout: */ 1000)); } #endif #undef CHECK #endif /* HAVE_UBUS */ dnsmasq-2.91/src/metrics.h0000664000175000017500000000273714765043257013731 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ /* If you modify this list, please keep the labels in metrics.c in sync. */ enum { METRIC_DNS_CACHE_INSERTED, METRIC_DNS_CACHE_LIVE_FREED, METRIC_DNS_QUERIES_FORWARDED, METRIC_DNS_AUTH_ANSWERED, METRIC_DNS_LOCAL_ANSWERED, METRIC_DNS_STALE_ANSWERED, METRIC_DNS_UNANSWERED_QUERY, METRIC_CRYPTO_HWM, METRIC_SIG_FAIL_HWM, METRIC_WORK_HWM, METRIC_BOOTP, METRIC_PXE, METRIC_DHCPACK, METRIC_DHCPDECLINE, METRIC_DHCPDISCOVER, METRIC_DHCPINFORM, METRIC_DHCPNAK, METRIC_DHCPOFFER, METRIC_DHCPRELEASE, METRIC_DHCPREQUEST, METRIC_NOANSWER, METRIC_LEASES_ALLOCATED_4, METRIC_LEASES_PRUNED_4, METRIC_LEASES_ALLOCATED_6, METRIC_LEASES_PRUNED_6, METRIC_TCP_CONNECTIONS, __METRIC_MAX, }; const char* get_metric_name(int); void clear_metrics(void); dnsmasq-2.91/src/poll.c0000664000175000017500000000506514765043257013221 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #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) { /* Array too small, extend. */ struct pollfd *new; arrsize = (arrsize == 0) ? 64 : arrsize * 2; if (!(new = whine_realloc(pollfds, arrsize * sizeof(struct pollfd)))) return; pollfds = new; } memmove(&pollfds[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd)); pollfds[i].fd = fd; pollfds[i].events = event; nfds++; } } dnsmasq-2.91/src/helper.c0000664000175000017500000006251514765043257013535 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_SCRIPT /* This file has code to fork a helper process which receives data via a pipe shared with the main process and which is responsible for calling a script when DHCP leases change. The helper process is forked before the main process drops root, so it retains root privs to pass on to the script. For this reason it tries to be paranoid about data received from the main process, in case that has been compromised. We don't want the helper to give an attacker root. In particular, the script to be run is not settable via the pipe, once the fork has taken place it is not alterable by the main process. */ 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 #include #include #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 { 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 unsigned int length; #else time_t expires; #endif #ifdef HAVE_TFTP off_t file_len; #endif struct in6_addr addr6; #ifdef HAVE_DHCP6 int vendorclass_count; unsigned int iaid; #endif unsigned char hwaddr[DHCP_CHADDR_MAX]; char interface[IF_NAMESIZE]; }; static struct script_data *buf = NULL; static size_t bytes_in_buf = 0, buf_size = 0; int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) { pid_t pid; int i, pipefd[2]; struct sigaction sigact; unsigned char *alloc_buff = NULL; /* create the pipe through which the main program sends us commands, then fork our process. */ if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1) { send_event(err_fd, EVENT_PIPE_ERR, errno, NULL); _exit(0); } if (pid != 0) { close(pipefd[0]); /* close reader side */ return pipefd[1]; } /* ignore SIGTERM and SIGINT, so that we can clean up when the main process gets hit and SIGALRM so that we can use sleep() */ sigact.sa_handler = SIG_IGN; sigact.sa_flags = 0; sigemptyset(&sigact.sa_mask); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGALRM, &sigact, NULL); sigaction(SIGINT, &sigact, NULL); if (!option_bool(OPT_DEBUG) && uid != 0) { gid_t dummy; if (setgroups(0, &dummy) == -1 || setgid(gid) == -1 || setuid(uid) == -1) { if (option_bool(OPT_NO_FORK)) /* send error to daemon process if no-fork */ send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser); else { /* kill daemon */ send_event(event_fd, EVENT_DIE, 0, NULL); /* return error */ send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser); } _exit(0); } } /* 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. */ close_fds(max_fd, pipefd[0], event_fd, err_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, *domain = NULL; unsigned char *buf = (unsigned char *)daemon->namebuff; unsigned char *end, *extradata; int is6, err = 0; int pipeout[2]; /* Free rarely-allocated memory from previous iteration. */ if (alloc_buff) { free(alloc_buff); alloc_buff = NULL; } /* we read zero bytes when pipe closed: this is our signal to exit */ if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), RW_READ)) { #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"; else if (data.action == ACTION_ADD) 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 if (data.action == ACTION_ARP) { action_str = "arp-add"; is6 = (data.flags != AF_INET); } else if (data.action == ACTION_ARP_DEL) { action_str = "arp-del"; is6 = (data.flags != AF_INET); data.action = ACTION_ARP; } else if (data.action == ACTION_RELAY_SNOOP) { is6 = 1; action_str = "relay-snoop"; } 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); 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, ":"); } /* 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, RW_READ)) 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, ":"); } #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 && data.action != ACTION_RELAY_SNOOP) { 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); else inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN); #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 if (data.action == ACTION_RELAY_SNOOP) { lua_getglobal(lua, "snoop"); 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, "client_address"); lua_pushstring(lua, hostname); lua_setfield(lua, -2, "prefix"); lua_pushstring(lua, data.interface); lua_setfield(lua, -2, "client_interface"); lua_call(lua, 2, 0); /* pass 2 values, expect 0 */ } } else if (data.action == ACTION_ARP) { lua_getglobal(lua, "arp"); if (lua_type(lua, -1) != LUA_TFUNCTION) lua_pop(lua, 1); /* arp function optional */ else { lua_pushstring(lua, action_str); /* arg1 - action */ lua_newtable(lua); /* arg2 - data table */ lua_pushstring(lua, daemon->addrbuff); lua_setfield(lua, -2, "client_address"); lua_pushstring(lua, daemon->dhcp_buff); lua_setfield(lua, -2, "mac_address"); lua_call(lua, 2, 0); /* pass 2 values, expect 0 */ } } else { lua_getglobal(lua, "lease"); /* function to call */ 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 lua_pushnumber(lua, data.expires); lua_setfield(lua, -2, "lease_expires"); #endif 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; lua_pushnumber(lua, data.ed_len == 0 ? 1 : 0); lua_setfield(lua, -2, "data_missing"); 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, "requested_options"); buf = grab_extradata_lua(buf, end, "mud_url"); buf = grab_extradata_lua(buf, end, "tags"); if (is6) buf = grab_extradata_lua(buf, end, "relay_address"); else if (data.giaddr.s_addr != 0) { inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN); lua_pushstring(lua, daemon->dhcp_buff2); 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; /* Pipe to capture stdout and stderr from script */ if (!option_bool(OPT_DEBUG) && pipe(pipeout) == -1) continue; /* possible fork errors are all temporary resource problems */ while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM)) sleep(2); if (pid == -1) { if (!option_bool(OPT_DEBUG)) { close(pipeout[0]); close(pipeout[1]); } continue; } /* wait for child to complete */ if (pid != 0) { if (!option_bool(OPT_DEBUG)) { FILE *fp; close(pipeout[1]); /* Read lines sent to stdout/err by the script and pass them back to be logged */ if (!(fp = fdopen(pipeout[0], "r"))) close(pipeout[0]); else { while (fgets(daemon->packet, daemon->packet_buff_sz, fp)) { /* do not include new lines, log will append them */ size_t len = strlen(daemon->packet); if (len > 0) { --len; if (daemon->packet[len] == '\n') daemon->packet[len] = 0; } send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet); } fclose(fp); } } /* reap our children's children, if necessary */ while (1) { int status; pid_t rc = wait(&status); if (rc == pid) { /* On error send event back to main process for logging */ if (WIFSIGNALED(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), NULL); break; } if (rc == -1 && errno != EINTR) break; } continue; } if (!option_bool(OPT_DEBUG)) { /* map stdout/stderr of script to pipeout */ close(pipeout[0]); dup2(pipeout[1], STDOUT_FILENO); dup2(pipeout[1], STDERR_FILENO); close(pipeout[1]); } if (data.action != ACTION_TFTP && data.action != ACTION_ARP && data.action != ACTION_RELAY_SNOOP) { #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 sprintf(daemon->dhcp_buff2, "%u", data.length); my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err); #else sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires); my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err); #endif my_setenv("DNSMASQ_DOMAIN", domain, &err); end = extradata + data.ed_len; buf = extradata; if (data.ed_len == 0) my_setenv("DNSMASQ_DATA_MISSING", "1", &err); if (!is6) buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err); #ifdef HAVE_DHCP6 else { 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_REQUESTED_OPTIONS", &err); buf = grab_extradata(buf, end, "DNSMASQ_MUD_URL", &err); buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err); if (is6) buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err); else { const char *giaddr = NULL; if (data.giaddr.s_addr != 0) giaddr = inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN); my_setenv("DNSMASQ_RELAY_ADDRESS", giaddr, &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", 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); close(pipefd[0]); if (data.action == ACTION_RELAY_SNOOP) strcpy(daemon->packet, data.interface); p = strrchr(daemon->lease_change_command, '/'); if (err == 0) { execl(daemon->lease_change_command, p ? p+1 : daemon->lease_change_command, action_str, (is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff, daemon->addrbuff, hostname, (char*)NULL); err = errno; } /* failed, send event so the main process logs the problem */ 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) { 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)) return NULL; for (next = buf; *next != 0; next++) if (next == end) return NULL; if (next != buf) { lua_pushstring(lua, (char *)buf); lua_setfield(lua, -2, field); } return next + 1; } #endif static void buff_alloc(size_t size) { if (size > buf_size) { struct script_data *new; /* start with reasonable size, will almost never need extending. */ if (size < sizeof(struct script_data) + 200) size = sizeof(struct script_data) + 200; if (!(new = whine_malloc(size))) return; if (buf) free(buf); 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; buf->ed_len = ed_len; buf->hostname_len = hostname_len; buf->addr = lease->addr; buf->giaddr = lease->giaddr; memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX); if (!indextoname(fd, lease->last_interface, buf->interface)) buf->interface[0] = 0; #ifdef HAVE_BROKEN_RTC buf->length = lease->length; #else buf->expires = lease->expires; #endif 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) { memcpy(p, lease->clid, clid_len); p += clid_len; } if (hostname_len != 0) { memcpy(p, hostname, hostname_len); p += hostname_len; } if (ed_len != 0) { memcpy(p, lease->extradata, ed_len); p += ed_len; } bytes_in_buf = p - (unsigned char *)buf; } #ifdef HAVE_DHCP6 void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *prefix, int prefix_len) { /* no script */ if (daemon->helperfd == -1) return; inet_ntop(AF_INET6, prefix, daemon->addrbuff, ADDRSTRLEN); /* 5 for /nnn and zero on the end of the prefix. */ buff_alloc(sizeof(struct script_data) + ADDRSTRLEN + 5); memset(buf, 0, sizeof(struct script_data)); buf->action = ACTION_RELAY_SNOOP; buf->addr6 = *client; buf->hostname_len = sprintf((char *)(buf+1), "%s/%u", daemon->addrbuff, prefix_len) + 1; indextoname(daemon->dhcp6fd, if_index, buf->interface); bytes_in_buf = sizeof(struct script_data) + buf->hostname_len; } #endif #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; else buf->addr6 = peer->in6.sin6_addr; memcpy((unsigned char *)(buf+1), filename, filename_len); bytes_in_buf = sizeof(struct script_data) + filename_len; } #endif void queue_arp(int action, unsigned char *mac, int maclen, int family, union all_addr *addr) { /* no script */ if (daemon->helperfd == -1) return; buff_alloc(sizeof(struct script_data)); memset(buf, 0, sizeof(struct script_data)); buf->action = action; buf->hwaddr_len = maclen; buf->hwaddr_type = ARPHRD_ETHER; if ((buf->flags = family) == AF_INET) buf->addr = addr->addr4; else buf->addr6 = addr->addr6; memcpy(buf->hwaddr, mac, maclen); bytes_in_buf = sizeof(struct script_data); } int helper_buf_empty(void) { return bytes_in_buf == 0; } void helper_write(void) { ssize_t rc; if (bytes_in_buf == 0) return; if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1) { if (bytes_in_buf != (size_t)rc) memmove(buf, buf + rc, bytes_in_buf - rc); bytes_in_buf -= rc; } else { if (errno == EAGAIN || errno == EINTR) return; bytes_in_buf = 0; } } #endif /* HAVE_SCRIPT */ dnsmasq-2.91/src/slaac.c0000664000175000017500000001337014765043257013334 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_DHCP6 #include 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_unsolicited(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; reset_counter(); if (!(ping = expand(sizeof(struct ping_packet)))) continue; 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(-1), 0, (struct sockaddr *)&addr, sizeof(addr)) == -1 && errno == EHOSTUNREACH && slaac->backoff == 12) 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 dnsmasq-2.91/src/radv-protocol.h0000664000175000017500000000273514765043257015054 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #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 dnsmasq-2.91/src/loop.c0000664000175000017500000000570614765043257013226 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_LOOP static ssize_t loop_make_probe(u32 uid); void loop_send_probes(void) { struct server *serv; struct randfd_list *rfds = NULL; 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 (strlen(serv->domain) == 0 && !(serv->flags & (SERV_FOR_NODOTS))) { ssize_t len = loop_make_probe(serv->uid); int fd; serv->flags &= ~SERV_LOOP; if ((fd = allocate_rfd(&rfds, serv)) == -1) continue; while (retry_send(sendto(fd, daemon->packet, len, 0, &serv->addr.sa, sa_len(&serv->addr)))); } free_rfds(&rfds); } static ssize_t loop_make_probe(u32 uid) { struct dns_header *header = (struct dns_header *)daemon->packet; unsigned char *p = (unsigned char *)(header+1); /* packet buffer overwritten */ daemon->srv_save = NULL; 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((unsigned char)query[i])) return 0; uid = strtol(query, NULL, 16); for (serv = daemon->servers; serv; serv = serv->next) if (strlen(serv->domain) == 0 && !(serv->flags & SERV_LOOP) && uid == serv->uid) { serv->flags |= SERV_LOOP; check_servers(1); /* log new state - don't send more probes. */ return 1; } return 0; } #endif dnsmasq-2.91/src/rfc3315.c0000664000175000017500000020725214765043257013343 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_DHCP6 struct state { unsigned char *clid; int clid_len, 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, iaid; 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; }; static int dhcp6_maybe_relay(struct state *state, unsigned char *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, unsigned char *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 (uint8_t *opts, uint8_t *end, unsigned int search, unsigned int minsize); static void *opt6_next(uint8_t *opts, uint8_t *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); 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 int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state, time_t now); static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, 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 *)&(((uint8_t *)(opt))[4+(i)])) #define opt6_user_vendor_ptr(opt, i) ((void *)&(((uint8_t *)(opt))[2+(i)])) #define opt6_user_vendor_len(opt) ((int)(opt6_uint(opt, -4, 2))) #define opt6_user_vendor_next(opt, end) (opt6_next(((uint8_t *) 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; reset_counter(); 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, unsigned char *inbuff, size_t sz, struct in6_addr *client_addr, int is_unicast, time_t now) { uint8_t *end = inbuff + sz; uint8_t *opts = inbuff + 34; int msg_type = *inbuff; unsigned char *outmsgtypep; uint8_t *opt; struct dhcp_vendor *vendor; /* if not an encapsulated 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, now); else { struct dhcp_context *c; struct shared_network *share = NULL; 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) { for (share = daemon->shared_networks; share; share = share->next) { if (share->shared_addr.s_addr != 0) continue; if (share->if_index != 0 || !IN6_ARE_ADDR_EQUAL(state->link_address, &share->match_addr6)) continue; if ((c->flags & CONTEXT_DHCP) && !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) && is_same_net6(&share->shared_addr6, &c->start6, c->prefix) && is_same_net6(&share->shared_addr6, &c->end6, c->prefix)) break; } if (share || ((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))) { if (opt6_len(opt) - 2 > DHCP_CHADDR_MAX) { return 0; } 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)) { if ((uint8_t *)opt6_ptr(opt, 0) + opt6_len(opt) > end) return 0; /* Don't copy MAC address into reply. */ if (opt6_type(opt) != OPTION6_CLIENT_MAC) { 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 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, unsigned char *inbuff, size_t sz, int is_unicast, time_t now) { void *opt; int i, o, o1, start_opts, start_msg; 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 outmsgtype; struct dhcp_vendor *vendor; struct dhcp_context *context_tmp; struct dhcp_mac *mac_opt; unsigned int ignore = 0; 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 receive no FQDN option */ /* 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; start_msg = save_counter(-1); /* copy over transaction-id */ if (!put_opt6(inbuff, 4)) return 0; start_opts = save_counter(-1); state->xid = inbuff[3] | inbuff[2] << 8 | inbuff[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, which MUST NOT have a server-id. 3315 para 15.x */ opt = opt6_find(state->packet_options, state->end, OPTION6_SERVER_ID, 1); if (msg_type == DHCP6SOLICIT || msg_type == DHCP6CONFIRM || msg_type == DHCP6REBIND) { if (opt) return 0; } else if (msg_type == DHCP6IREQ) { /* If server-id provided, it must match. */ if (opt && (opt6_len(opt) != daemon->duid_len || memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0)) return 0; } else { /* Everything else MUST have a server-id that matches ours. */ if (!opt || 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)) { outmsgtype = DHCP6REPLY; o1 = new_opt6(OPTION6_STATUS_CODE); put_opt6_short(DHCP6USEMULTI); put_opt6_string("Use multicast"); end_opt6(o1); goto done; } /* 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, existence 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; } } else if (option_bool(OPT_LOG_OPTS)) my_syslog(MS_DHCP | LOG_INFO, _("%u cannot determine client MAC address"), state->xid); 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)) { struct dhcp_match_name *m; size_t nl = strlen(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); for (m = daemon->dhcp_name_match; m; m = m->next) { size_t ml = strlen(m->name); char save = 0; if (nl < ml) continue; if (nl > ml) { save = state->client_hostname[ml]; state->client_hostname[ml] = 0; } if (hostname_isequal(state->client_hostname, m->name) && (save == 0 || m->wildcard)) { m->netid->next = state->tags; state->tags = m->netid; } if (save != 0) state->client_hostname[ml] = save; } } } } 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, run_tag_if(state->tags))) && 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, run_tag_if(state->tags)); 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; } else if (state->clid && find_config(daemon->dhcp_conf, NULL, state->clid, state->clid_len, state->mac, state->mac_len, state->mac_type, NULL, run_tag_if(state->tags))) { known_id.net = "known-othernet"; known_id.next = state->tags; state->tags = &known_id; } 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; outmsgtype = DHCP6ADVERTISE; if (opt6_find(state->packet_options, state->end, OPTION6_RAPID_COMMIT, 0)) { outmsgtype = 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, 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; 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)) { /* worry about alignment here. */ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); 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, state, now)) { 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 */ 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, state, now)) { 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 */ 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))) { 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))) { 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 */ outmsgtype = 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 an 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; struct dhcp_context *dynamic, *c; unsigned int lease_time; int config_ok = 0; /* align. */ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); if ((c = address6_valid(state->context, &req_addr, tagif, 1))) config_ok = (config_implies(config, c, &req_addr) != NULL); 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; 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: case DHCP6REBIND: { int address_assigned = 0; /* set reply message type */ outmsgtype = DHCP6REPLY; log6_quiet(state, msg_type == DHCP6RENEW ? "DHCPRENEW" : "DHCPREBIND", 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; 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; memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); if (!(lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, state->iaid, &req_addr))) { if (msg_type == DHCP6REBIND) { /* When rebinding, we can create a lease if it doesn't exist. */ lease = lease6_allocate(&req_addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA); if (lease) lease_set_iaid(lease, state->iaid); else break; } else { /* 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))) { unsigned int lease_time; get_context_tag(state, this_context); if (config_implies(config, this_context, &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"); address_assigned = 1; } 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); } if (!address_assigned && msg_type == DHCP6REBIND) { /* can't create lease for any address, return error */ o1 = new_opt6(OPTION6_STATUS_CODE); put_opt6_short(DHCP6NOADDRS); put_opt6_string(_("no addresses available")); end_opt6(o1); } tagif = add_options(state, 0); break; } case DHCP6CONFIRM: { int good_addr = 0, bad_addr = 0; /* set reply message type */ outmsgtype = 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; /* alignment */ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); if (!address6_valid(state->context, &req_addr, tagif, 1)) { bad_addr = 1; log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed")); } else { good_addr = 1; log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname); } } } /* No addresses, no reply: RFC 3315 18.2.2 */ if (!good_addr && !bad_addr) return 0; o1 = new_opt6(OPTION6_STATUS_CODE); put_opt6_short(bad_addr ? DHCP6NOTONLINK : DHCP6SUCCESS); put_opt6_string(bad_addr ? (_("confirm failed")) : (_("all addresses still on link"))); end_opt6(o1); break; } case DHCP6IREQ: { /* 3315 para 15.12 */ if (opt6_find(state->packet_options, state->end, OPTION6_IA_NA, 1) || opt6_find(state->packet_options, state->end, OPTION6_IA_TA, 1)) return 0; /* 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; outmsgtype = DHCP6REPLY; tagif = add_options(state, 1); break; } case DHCP6RELEASE: { /* set reply message type */ outmsgtype = 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; struct in6_addr addr; /* align */ memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, state->iaid, &addr))) 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(&addr, 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 */ outmsgtype = 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 addr; struct addrlist *addr_list; /* align */ memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); if ((addr_list = config_implies(config, state->context, &addr))) { prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF); inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN); my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), daemon->addrbuff, daemon->dhcp_buff3); addr_list->flags |= ADDRLIST_DECLINED; addr_list->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, &addr))) 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(&addr, 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 answer 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); done: /* Fill in the message type. Note that we store the offset, not a direct pointer, since the packet memory may have been reallocated. */ ((unsigned char *)(daemon->outpacket.iov_base))[start_msg] = outmsgtype; log6_opts(0, state->xid, (uint8_t *)daemon->outpacket.iov_base + start_opts, (uint8_t *)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, 0); 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->opt == OPTION6_DNS_SERVER) done_dns = 1; if (opt_cfg->flags & DHOPT_ADDR6) { int len, j; struct in6_addr *a; 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++) { struct in6_addr *p = NULL; if (IN6_IS_ADDR_UNSPECIFIED(a)) { if (!add_local_addrs(state->context)) p = state->fallback; } else if (IN6_IS_ADDR_ULA_ZERO(a)) { if (!IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) p = state->ula_addr; } else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a)) { if (!IN6_IS_ADDR_UNSPECIFIED(state->ll_addr)) p = state->ll_addr; } else p = a; if (!p) continue; else if (opt_cfg->opt == OPTION6_NTP_SERVER) { if (IN6_IS_ADDR_MULTICAST(p)) o1 = new_opt6(NTP_SUBOPTION_MC_ADDR); else o1 = new_opt6(NTP_SUBOPTION_SRV_ADDR); put_opt6(p, IN6ADDRSZ); end_opt6(o1); } else put_opt6(p, 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, subject 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, NULL); if (state->send_domain) { p = do_rfc1035_name(p, state->send_domain, NULL); *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; } } } 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 and 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); 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. */ for (context = state->context; context; context = context->current) if (is_same_net6(addr, &context->start6, context->prefix)) context->flags |= CONTEXT_USED; } 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; } /* return true of *addr could have been generated from config. */ static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr) { int prefix; struct in6_addr wild_addr; struct addrlist *addr_list; if (!config || !(config->flags & CONFIG_ADDR6)) return NULL; for (addr_list = config->addr6; addr_list; addr_list = addr_list->next) { prefix = (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128; wild_addr = addr_list->addr.addr6; if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64) { wild_addr = context->start6; setaddr6part(&wild_addr, addr6part(&addr_list->addr.addr6)); } else if (!is_same_net6(&context->start6, addr, context->prefix)) continue; if (is_same_net6(&wild_addr, addr, prefix)) return addr_list; } return NULL; } static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state, time_t now) { u64 addrpart, i, addresses; struct addrlist *addr_list; if (!config || !(config->flags & CONFIG_ADDR6)) return 0; for (addr_list = config->addr6; addr_list; addr_list = addr_list->next) if (!(addr_list->flags & ADDRLIST_DECLINED) || difftime(now, addr_list->decline_time) >= (float)DECLINE_BACKOFF) { addrpart = addr6part(&addr_list->addr.addr6); addresses = 1; if (addr_list->flags & ADDRLIST_PREFIX) addresses = (u64)1<<(128-addr_list->prefixlen); if ((addr_list->flags & ADDRLIST_WILDCARD)) { if (context->prefix != 64) continue; *addr = context->start6; } else if (is_same_net6(&context->start6, &addr_list->addr.addr6, context->prefix)) *addr = addr_list->addr.addr6; else continue; for (i = 0 ; i < addresses; i++) { setaddr6part(addr, addrpart+i); if (check_address(state, addr)) return 1; } } return 0; } /* 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 *opt; lease->flags |= LEASE_CHANGED; free(lease->extradata); lease->extradata = NULL; lease->extradata_size = lease->extradata_len = 0; lease->vendorclass_count = 0; if ((opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4))) { void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt)); lease->vendorclass_count++; /* send enterprise number first */ sprintf(daemon->dhcp_buff2, "%u", opt6_uint(opt, 0, 4)); lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0); if (opt6_len(opt) >= 6) for (enc_opt = opt6_ptr(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); /* DNSMASQ_REQUESTED_OPTIONS */ if ((opt = opt6_find(state->packet_options, state->end, OPTION6_ORO, 2))) { int i, len = opt6_len(opt)/2; u16 *rop = opt6_ptr(opt, 0); for (i = 0; i < len; i++) lease_add_extradata(lease, (unsigned char *)daemon->namebuff, sprintf(daemon->namebuff, "%u", ntohs(rop[i])), (i + 1) == len ? 0 : ','); } else lease_add_extradata(lease, NULL, 0, 0); if ((opt = opt6_find(state->packet_options, state->end, OPTION6_MUD_URL, 1))) lease_add_extradata(lease, opt6_ptr(opt, 0), opt6_len(opt), 0); else lease_add_extradata(lease, NULL, 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 ((opt = opt6_find(state->packet_options, state->end, OPTION6_USER_CLASS, 2))) { void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt)); for (enc_opt = opt6_ptr(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) { struct in6_addr addr; /* align */ memcpy(&addr, opt6_ptr(opt, 0), IN6ADDRSZ); inet_ntop(AF_INET6, &addr, 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); } 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, DHCP_BUFF_SZ - 1); 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 (uint8_t *opts, uint8_t *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(uint8_t *opts, uint8_t *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; } int relay_upstream6(int iface_index, ssize_t sz, struct in6_addr *peer_address, u32 scope_id, time_t now) { unsigned char *header; unsigned char *inbuff = daemon->dhcp_packet.iov_base; int msg_type = *inbuff; int hopcount, o; struct in6_addr multicast; unsigned int maclen, mactype; unsigned char mac[DHCP_CHADDR_MAX]; struct dhcp_relay *relay; for (relay = daemon->relay6; relay; relay = relay->next) if (relay->iface_index != 0 && relay->iface_index == iface_index) break; /* No relay config. */ if (!relay) return 0; inet_pton(AF_INET6, ALL_SERVERS, &multicast); get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now); /* Get hop count from nested relayed message */ if (msg_type == DHCP6RELAYFORW) hopcount = *((unsigned char *)inbuff+1) + 1; else hopcount = 0; reset_counter(); /* RFC 3315 HOP_COUNT_LIMIT */ if (hopcount > 32 || !(header = put_opt6(NULL, 34))) return 1; header[0] = DHCP6RELAYFORW; header[1] = hopcount; 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->next) if (relay->iface_index != 0 && relay->iface_index == iface_index) { union mysockaddr to; memcpy(&header[2], &relay->local.addr6, IN6ADDRSZ); to.sa.sa_family = AF_INET6; to.in6.sin6_addr = relay->server.addr6; #ifdef HAVE_SOCKADDR_SA_LEN to.in6.sin6_len = sizeof(struct sockaddr_in6); #endif to.in6.sin6_port = htons(relay->port); to.in6.sin6_flowinfo = 0; to.in6.sin6_scope_id = 0; if (IN6_ARE_ADDR_EQUAL(&relay->server.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 DHCP relay via interface %s"), relay->interface); continue; } } #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), NULL, &to, daemon->dhcp6fd); #endif while (retry_send(sendto(daemon->dhcp6fd, (void *)daemon->outpacket.iov_base, save_counter(-1), 0, (struct sockaddr *)&to, sa_len(&to)))); if (option_bool(OPT_LOG_OPTS)) { inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN); if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast)) snprintf(daemon->namebuff, MAXDNAME, _("multicast via %s"), relay->interface); else inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN); my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->namebuff); } } return 1; } int 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.addr6) && (!relay->interface || wildcard_match(relay->interface, arrival_interface))) break; reset_counter(); 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; if (encap_type == DHCP6RELAYREPL) { peer->sin6_port = ntohs(DHCPV6_SERVER_PORT); return 1; } peer->sin6_port = ntohs(DHCPV6_CLIENT_PORT); #ifdef HAVE_SCRIPT if (daemon->lease_change_command && encap_type == DHCP6REPLY) { /* decapsulate relayed message */ opts = opt6_ptr(opt, 4); end = opt6_ptr(opt, opt6_len(opt)); for (opt = opts; opt; opt = opt6_next(opt, end)) if (opt6_type(opt) == OPTION6_IA_PD && opt6_len(opt) > 12) { void *ia_opts = opt6_ptr(opt, 12); void *ia_end = opt6_ptr(opt, opt6_len(opt)); void *ia_opt; for (ia_opt = ia_opts; ia_opt; ia_opt = opt6_next(ia_opt, ia_end)) /* valid lifetime must not be zero. */ if (opt6_type(ia_opt) == OPTION6_IAPREFIX && opt6_len(ia_opt) >= 25 && opt6_uint(ia_opt, 4, 4) != 0) { if (daemon->free_snoops || (daemon->free_snoops = whine_malloc(sizeof(struct snoop_record)))) { struct snoop_record *snoop = daemon->free_snoops; daemon->free_snoops = snoop->next; snoop->client = peer->sin6_addr; snoop->prefix_len = opt6_uint(ia_opt, 8, 1); memcpy(&snoop->prefix, opt6_ptr(ia_opt, 9), IN6ADDRSZ); snoop->next = relay->snoop_records; relay->snoop_records = snoop; } } } } #endif return 1; } } return 0; } #ifdef HAVE_SCRIPT int do_snoop_script_run(void) { struct dhcp_relay *relay; struct snoop_record *snoop; for (relay = daemon->relay6; relay; relay = relay->next) if ((snoop = relay->snoop_records)) { relay->snoop_records = snoop->next; snoop->next = daemon->free_snoops; daemon->free_snoops = snoop; queue_relay_snoop(&snoop->client, relay->iface_index, &snoop->prefix, snoop->prefix_len); return 1; } return 0; } #endif #endif dnsmasq-2.91/src/conntrack.c0000664000175000017500000000504414765043257014232 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_CONNTRACK #include 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, union 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)); 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->addr6.s6_addr); } else { 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->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 /* HAVE_CONNTRACK */ dnsmasq-2.91/src/dump.c0000664000175000017500000002262514765043257013221 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_DUMPFILE #include static u32 packet_count; static void do_dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst, int port, int proto); /* https://wiki.wireshark.org/Development/LibpcapFileFormat */ struct pcap_hdr_s { u32 magic_number; /* magic number */ u16 version_major; /* major version number */ u16 version_minor; /* minor version number */ u32 thiszone; /* GMT to local correction */ u32 sigfigs; /* accuracy of timestamps */ u32 snaplen; /* max length of captured packets, in octets */ u32 network; /* data link type */ }; struct pcaprec_hdr_s { u32 ts_sec; /* timestamp seconds */ u32 ts_usec; /* timestamp microseconds */ u32 incl_len; /* number of octets of packet saved in file */ u32 orig_len; /* actual length of packet */ }; void dump_init(void) { struct stat buf; struct pcap_hdr_s header; struct pcaprec_hdr_s pcap_header; packet_count = 0; header.magic_number = 0xa1b2c3d4; header.version_major = 2; header.version_minor = 4; header.thiszone = 0; header.sigfigs = 0; header.snaplen = daemon->edns_pktsz + 200; /* slop for IP/UDP headers */ header.network = 101; /* DLT_RAW http://www.tcpdump.org/linktypes.html */ if (stat(daemon->dump_file, &buf) == -1) { /* doesn't exist, create and add header */ if (errno != ENOENT || (daemon->dumpfd = creat(daemon->dump_file, S_IRUSR | S_IWUSR)) == -1 || !read_write(daemon->dumpfd, (void *)&header, sizeof(header), RW_WRITE)) die(_("cannot create %s: %s"), daemon->dump_file, EC_FILE); } else if (S_ISFIFO(buf.st_mode)) { /* File is named pipe (with wireshark on the other end, probably.) Send header. */ if ((daemon->dumpfd = open(daemon->dump_file, O_APPEND | O_RDWR)) == -1 || !read_write(daemon->dumpfd, (void *)&header, sizeof(header), RW_WRITE)) die(_("cannot open pipe %s: %s"), daemon->dump_file, EC_FILE); } else if ((daemon->dumpfd = open(daemon->dump_file, O_APPEND | O_RDWR)) == -1 || !read_write(daemon->dumpfd, (void *)&header, sizeof(header), RW_READ)) die(_("cannot access %s: %s"), daemon->dump_file, EC_FILE); else if (header.magic_number != 0xa1b2c3d4) die(_("bad header in %s"), daemon->dump_file, EC_FILE); else { /* count existing records */ while (read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), RW_READ)) { lseek(daemon->dumpfd, pcap_header.incl_len, SEEK_CUR); packet_count++; } } } void dump_packet_udp(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst, int fd) { union mysockaddr fd_addr; socklen_t addr_len = sizeof(fd_addr); if (daemon->dumpfd != -1 && (mask & daemon->dump_mask)) { /* if fd is negative it carries a port number (negated) which we use as a source or destination when not otherwise specified so wireshark can ID the packet. If both src and dst are specified, set this to -1 to avoid a spurious getsockname() call. */ int port = (fd < 0) ? -fd : -1; /* fd >= 0 is a file descriptor and the address of that file descriptor is used in place of a NULL src or dst. */ if (fd >= 0 && getsockname(fd, (struct sockaddr *)&fd_addr, &addr_len) != -1) { if (!src) src = &fd_addr; if (!dst) dst = &fd_addr; } do_dump_packet(mask, packet, len, src, dst, port, IPPROTO_UDP); } } void dump_packet_icmp(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst) { if (daemon->dumpfd != -1 && (mask & daemon->dump_mask)) do_dump_packet(mask, packet, len, src, dst, -1, IPPROTO_ICMP); } static void do_dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst, int port, int proto) { struct ip ip; struct ip6_hdr ip6; int family; struct udphdr { u16 uh_sport; /* source port */ u16 uh_dport; /* destination port */ u16 uh_ulen; /* udp length */ u16 uh_sum; /* udp checksum */ } udp; struct pcaprec_hdr_s pcap_header; struct timeval time; u32 i, sum; void *iphdr; size_t ipsz; int rc; /* if port != -1 it carries a port number which we use as a source or destination when not otherwise specified so wireshark can ID the packet. If both src and dst are specified, set this to -1 to avoid a spurious getsockname() call. */ udp.uh_sport = udp.uh_dport = htons(port < 0 ? 0 : port); if (src) family = src->sa.sa_family; else family = dst->sa.sa_family; if (family == AF_INET6) { iphdr = &ip6; ipsz = sizeof(ip6); memset(&ip6, 0, sizeof(ip6)); ip6.ip6_vfc = 6 << 4; ip6.ip6_hops = 64; if ((ip6.ip6_nxt = proto) == IPPROTO_UDP) ip6.ip6_plen = htons(sizeof(struct udphdr) + len); else { proto = ip6.ip6_nxt = IPPROTO_ICMPV6; ip6.ip6_plen = htons(len); } if (src) { memcpy(&ip6.ip6_src, &src->in6.sin6_addr, IN6ADDRSZ); udp.uh_sport = src->in6.sin6_port; } if (dst) { memcpy(&ip6.ip6_dst, &dst->in6.sin6_addr, IN6ADDRSZ); udp.uh_dport = dst->in6.sin6_port; } /* start UDP checksum */ for (sum = 0, i = 0; i < IN6ADDRSZ; i+=2) { sum += ntohs((ip6.ip6_src.s6_addr[i] << 8) + (ip6.ip6_src.s6_addr[i+1])) ; sum += ntohs((ip6.ip6_dst.s6_addr[i] << 8) + (ip6.ip6_dst.s6_addr[i+1])) ; } } else { iphdr = &ip; ipsz = sizeof(ip); memset(&ip, 0, sizeof(ip)); ip.ip_v = IPVERSION; ip.ip_hl = sizeof(struct ip) / 4; ip.ip_ttl = IPDEFTTL; if ((ip.ip_p = proto) == IPPROTO_UDP) ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len); else { ip.ip_len = htons(sizeof(struct ip) + len); proto = ip.ip_p = IPPROTO_ICMP; } if (src) { ip.ip_src = src->in.sin_addr; udp.uh_sport = src->in.sin_port; } if (dst) { ip.ip_dst = dst->in.sin_addr; udp.uh_dport = dst->in.sin_port; } ip.ip_sum = 0; for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++) sum += ((u16 *)&ip)[i]; while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); ip.ip_sum = (sum == 0xffff) ? sum : ~sum; /* start UDP/ICMP checksum */ sum = ip.ip_src.s_addr & 0xffff; sum += (ip.ip_src.s_addr >> 16) & 0xffff; sum += ip.ip_dst.s_addr & 0xffff; sum += (ip.ip_dst.s_addr >> 16) & 0xffff; } if (len & 1) ((unsigned char *)packet)[len] = 0; /* for checksum, in case length is odd. */ if (proto == IPPROTO_UDP) { /* Add Remaining part of the pseudoheader. Note that though the IPv6 pseudoheader is very different to the IPv4 one, the net result of this calculation is correct as long as the packet length is less than 65536, which is fine for us. */ sum += htons(IPPROTO_UDP); sum += htons(sizeof(struct udphdr) + len); udp.uh_sum = 0; udp.uh_ulen = htons(sizeof(struct udphdr) + len); for (i = 0; i < sizeof(struct udphdr)/2; i++) sum += ((u16 *)&udp)[i]; for (i = 0; i < (len + 1) / 2; i++) sum += ((u16 *)packet)[i]; while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); udp.uh_sum = (sum == 0xffff) ? sum : ~sum; pcap_header.incl_len = pcap_header.orig_len = ipsz + sizeof(udp) + len; } else { /* ICMP - ICMPv6 packet is a superset of ICMP */ struct icmp6_hdr *icmp = packet; /* See comment in UDP code above. */ sum += htons(proto); sum += htons(len); icmp->icmp6_cksum = 0; for (i = 0; i < (len + 1) / 2; i++) sum += ((u16 *)packet)[i]; while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); icmp->icmp6_cksum = (sum == 0xffff) ? sum : ~sum; pcap_header.incl_len = pcap_header.orig_len = ipsz + len; } rc = gettimeofday(&time, NULL); pcap_header.ts_sec = time.tv_sec; pcap_header.ts_usec = time.tv_usec; if (rc == -1 || !read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), RW_WRITE) || !read_write(daemon->dumpfd, iphdr, ipsz, RW_WRITE) || (proto == IPPROTO_UDP && !read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), RW_WRITE)) || !read_write(daemon->dumpfd, (void *)packet, len, RW_WRITE)) my_syslog(LOG_ERR, _("failed to write packet dump")); else if (option_bool(OPT_EXTRALOG) && (mask & 0x00ff)) my_syslog(LOG_INFO, _("%u dumping packet %u mask 0x%04x"), daemon->log_display_id, ++packet_count, mask); else my_syslog(LOG_INFO, _("dumping packet %u mask 0x%04x"), ++packet_count, mask); } #endif dnsmasq-2.91/src/util.c0000664000175000017500000004473614765043257013240 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ /* The SURF random number generator was taken from djbdns-1.05, by Daniel J Bernstein, which is public domain. */ #include "dnsmasq.h" #ifdef HAVE_BROKEN_RTC #include #endif #if defined(HAVE_LIBIDN2) #include #elif defined(HAVE_IDN) #include #endif #ifdef HAVE_LINUX_NETWORK #include #endif /* SURF random number generator */ static u32 seed[32]; static u32 in[12]; static u32 out[8]; static int outleft = 0; void rand_init(void) { int fd = open(RANDFILE, O_RDONLY); if (fd == -1 || !read_write(fd, (unsigned char *)&seed, sizeof(seed), RW_READ) || !read_write(fd, (unsigned char *)&in, sizeof(in), RW_READ)) die(_("failed to seed the random number generator: %s"), NULL, EC_MISC); close(fd); } #define ROTATE(x,b) (((x) << (b)) | ((x) >> (32 - (b)))) #define MUSH(i,b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x,b)); static void surf(void) { u32 t[12]; u32 x; u32 sum = 0; int r; int i; int loop; for (i = 0;i < 12;++i) t[i] = in[i] ^ seed[12 + i]; for (i = 0;i < 8;++i) out[i] = seed[24 + i]; x = t[11]; for (loop = 0;loop < 2;++loop) { for (r = 0;r < 16;++r) { sum += 0x9e3779b9; MUSH(0,5) MUSH(1,7) MUSH(2,9) MUSH(3,13) MUSH(4,5) MUSH(5,7) MUSH(6,9) MUSH(7,13) MUSH(8,5) MUSH(9,7) MUSH(10,9) MUSH(11,13) } for (i = 0;i < 8;++i) out[i] ^= t[i + 4]; } } 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 < 2) { if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3]; surf(); outleft = 8; } outleft -= 2; return (u64)out[outleft+1] + (((u64)out[outleft]) << 32); } int rr_on_list(struct rrlist *list, unsigned short rr) { while (list) { if (list->rr != 0 && list->rr == rr) return 1; list = list->next; } return 0; } /* returns 1 if name is OK and ascii printable * returns 2 if name should be processed by IDN */ static int check_name(char *in) { /* remove trailing . also fail empty string and label > 63 chars */ size_t dotgap = 0, l = strlen(in); char c; int nowhite = 0; int idn_encode = 0; int hasuscore = 0; int hasucase = 0; if (l == 0 || l > MAXDNAME) return 0; if (in[l-1] == '.') { in[l-1] = 0; nowhite = 1; } for (; (c = *in); in++) { if (c == '.') dotgap = 0; else if (++dotgap > MAXLABEL) return 0; else if (isascii((unsigned char)c) && iscntrl((unsigned char)c)) /* iscntrl only gives expected results for ascii */ return 0; else if (!isascii((unsigned char)c)) #if !defined(HAVE_IDN) && !defined(HAVE_LIBIDN2) return 0; #else idn_encode = 1; #endif else if (c != ' ') { nowhite = 1; #if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003) if (c == '_') hasuscore = 1; #else (void)hasuscore; #endif #if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) if (c >= 'A' && c <= 'Z') hasucase = 1; #else (void)hasucase; #endif } } if (!nowhite) return 0; #if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003) /* Older libidn2 strips underscores, so don't do IDN processing if the name has an underscore unless it also has non-ascii characters. */ idn_encode = idn_encode || (hasucase && !hasuscore); #else idn_encode = idn_encode || hasucase; #endif return (idn_encode) ? 2 : 1; } /* Hostnames have a more limited valid charset than domain names so check for legal char a-z A-Z 0-9 - _ Note that this may receive a FQDN, so only check the first label for the tighter criteria. */ int legal_hostname(char *name) { char c; int first; if (!check_name(name)) return 0; 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')) continue; if (!first && (c == '-' || c == '_')) continue; /* end of hostname part */ if (c == '.') return 1; return 0; } return 1; } char *canonicalise(char *in, int *nomem) { char *ret = NULL; int rc; if (nomem) *nomem = 0; if (!(rc = check_name(in))) return NULL; #if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) if (rc == 2) { # ifdef HAVE_LIBIDN2 rc = idn2_to_ascii_lz(in, &ret, IDN2_NONTRANSITIONAL); # else rc = idna_to_ascii_lz(in, &ret, 0); # endif if (rc != IDNA_SUCCESS) { if (ret) free(ret); if (nomem && (rc == IDNA_MALLOC_ERROR || rc == IDNA_DLOPEN_ERROR)) { my_syslog(LOG_ERR, _("failed to allocate memory")); *nomem = 1; } return NULL; } return ret; } #else (void)rc; #endif if ((ret = whine_malloc(strlen(in)+1))) strcpy(ret, in); else if (nomem) *nomem = 1; return ret; } unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit) { int j; while (sval && *sval) { unsigned char *cp = p++; if (limit && p > (unsigned char*)limit) return NULL; for (j = 0; *sval && (*sval != '.'); sval++, j++) { if (limit && p + 1 > (unsigned char*)limit) return NULL; if (*sval == NAME_ESCAPE) *p++ = (*(++sval))-1; else *p++ = *sval; } *cp = j; if (*sval) sval++; } return p; } /* for use during startup */ void *safe_malloc(size_t size) { void *ret = calloc(1, size); if (!ret) die(_("could not get memory"), NULL, EC_NOMEM); return ret; } /* Ensure limited size string is always terminated. * Can be replaced by (void)strlcpy() on some platforms */ void safe_strncpy(char *dest, const char *src, size_t size) { if (size != 0) { dest[size-1] = '\0'; strncpy(dest, src, size-1); } } void safe_pipe(int *fd, int read_noblock) { if (pipe(fd) == -1 || !fix_fd(fd[1]) || (read_noblock && !fix_fd(fd[0]))) die(_("cannot create pipe: %s"), NULL, EC_MISC); } void *whine_malloc(size_t size) { void *ret = calloc(1, size); if (!ret) my_syslog(LOG_ERR, _("failed to allocate %d bytes"), (int) size); return ret; } void *whine_realloc(void *ptr, size_t size) { void *ret = realloc(ptr, size); if (!ret) my_syslog(LOG_ERR, _("failed to reallocate %d bytes"), (int) size); return ret; } int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2) { if (s1->sa.sa_family == s2->sa.sa_family) { if (s1->sa.sa_family == AF_INET && s1->in.sin_port == s2->in.sin_port && s1->in.sin_addr.s_addr == s2->in.sin_addr.s_addr) return 1; 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; } return 0; } int sockaddr_isnull(const union mysockaddr *s) { if (s->sa.sa_family == AF_INET && s->in.sin_addr.s_addr == 0) return 1; if (s->sa.sa_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&s->in6.sin6_addr)) return 1; return 0; } int sa_len(union mysockaddr *addr) { #ifdef HAVE_SOCKADDR_SA_LEN return addr->sa.sa_len; #else if (addr->sa.sa_family == AF_INET6) return sizeof(addr->in6); else return sizeof(addr->in); #endif } /* don't use strcasecmp and friends here - they may be messed up by LOCALE */ int hostname_order(const char *a, const char *b) { unsigned int c1, c2; do { c1 = (unsigned char) *a++; c2 = (unsigned char) *b++; if (c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A'; if (c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A'; if (c1 < c2) return -1; else if (c1 > c2) return 1; } while (c1); return 0; } int hostname_isequal(const char *a, const char *b) { return hostname_order(a, b) == 0; } /* is b equal to or a subdomain of a return 2 for equal, 1 for subdomain */ int hostname_issubdomain(char *a, char *b) { char *ap, *bp; unsigned int c1, c2; /* move to the end */ for (ap = a; *ap; ap++); for (bp = b; *bp; bp++); /* a shorter than b or a empty. */ if ((bp - b) < (ap - a) || ap == a) return 0; do { c1 = (unsigned char) *(--ap); c2 = (unsigned char) *(--bp); if (c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A'; if (c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A'; if (c1 != c2) return 0; } while (ap != a); if (bp == b) return 2; if (*(--bp) == '.') return 1; return 0; } time_t dnsmasq_time(void) { #ifdef HAVE_BROKEN_RTC struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) die(_("cannot read monotonic clock: %s"), NULL, EC_MISC); return ts.tv_sec; #else return time(NULL); #endif } u32 dnsmasq_milliseconds(void) { struct timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec) * 1000 + (tv.tv_usec / 1000); } 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); } int is_same_net_prefix(struct in_addr a, struct in_addr b, int prefix) { struct in_addr mask; mask.s_addr = htonl(~((1 << (32 - prefix)) - 1)); return is_same_net(a, b, mask); } 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 significant 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; } } /* returns port number from address */ int prettyprint_addr(union mysockaddr *addr, char *buf) { int port = 0; if (addr->sa.sa_family == AF_INET) { inet_ntop(AF_INET, &addr->in.sin_addr, buf, ADDRSTRLEN); port = ntohs(addr->in.sin_port); } 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); } return port; } void prettyprint_time(char *buf, unsigned int t) { if (t == 0xffffffff) sprintf(buf, _("infinite")); else { unsigned int x, p = 0; if ((x = t/86400)) p += sprintf(&buf[p], "%ud", x); if ((x = (t/3600)%24)) p += sprintf(&buf[p], "%uh", x); if ((x = (t/60)%60)) p += sprintf(&buf[p], "%um", x); if ((x = t%60)) sprintf(&buf[p], "%us", x); } } /* in may equal out, when maxlen may be -1 (No max len). Return -1 for extraneous no-hex chars found. */ int parse_hex(char *in, unsigned char *out, int maxlen, unsigned int *wildcard_mask, int *mac_type) { int done = 0, mask = 0, i = 0; char *r; if (mac_type) *mac_type = 0; while (!done && (maxlen == -1 || i < maxlen)) { for (r = in; *r != 0 && *r != ':' && *r != '-' && *r != ' '; r++) if (*r != '*' && !isxdigit((unsigned char)*r)) return -1; if (*r == 0) done = 1; if (r != in ) { if (*r == '-' && i == 0 && mac_type) { *r = 0; *mac_type = strtol(in, NULL, 16); mac_type = NULL; } else { *r = 0; if (strcmp(in, "*") == 0) { mask = (mask << 1) | 1; i++; } else { int j, bytes = (1 + (r - in))/2; for (j = 0; j < bytes; j++) { char sav; if (j < bytes - 1) { sav = in[(j+1)*2]; in[(j+1)*2] = 0; } /* checks above allow mix of hexdigit and *, which is illegal. */ if (strchr(&in[j*2], '*')) return -1; out[i] = strtol(&in[j*2], NULL, 16); mask = mask << 1; if (++i == maxlen) break; if (j < bytes - 1) in[(j+1)*2] = sav; } } } } in = r+1; } if (wildcard_mask) *wildcard_mask = mask; return i; } /* return 0 for no match, or (no matched octets) + 1 */ int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask) { int i, count; for (count = 1, i = len - 1; i >= 0; i--, mask = mask >> 1) if (!(mask & 1)) { if (a[i] == b[i]) count++; else return 0; } return count; } /* _note_ may copy buffer */ int expand_buf(struct iovec *iov, size_t size) { void *new; if (size <= (size_t)iov->iov_len) return 1; if (!(new = whine_malloc(size))) { errno = ENOMEM; return 0; } if (iov->iov_base) { memcpy(new, iov->iov_base, iov->iov_len); free(iov->iov_base); } iov->iov_base = new; iov->iov_len = size; return 1; } char *print_mac(char *buff, unsigned char *mac, int len) { char *p = buff; int i; if (len == 0) sprintf(p, ""); else for (i = 0; i < len; i++) p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":"); return buff; } /* 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) { 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. */ if (errno == EAGAIN || errno == EWOULDBLOCK) { waiter.tv_sec = 0; waiter.tv_nsec = 10000; nanosleep(&waiter, NULL); if (retries++ < 1000) return 1; } retries = 0; if (errno == EINTR) return 1; return 0; } /* rw = 0 -> write rw = 1 -> read rw = 2 -> write once rw = 3 -> read once "once" fails on EAGAIN, as this a timeout. This indicates a timeout of a TCP socket. */ int read_write(int fd, unsigned char *packet, int size, int rw) { ssize_t n, done; for (done = 0; done < size; done += n) { if (rw & 1) n = read(fd, &packet[done], (size_t)(size - done)); else n = write(fd, &packet[done], (size_t)(size - done)); if (n == 0) return 0; if (n == -1) { n = 0; /* don't mess with counter when we loop. */ if (errno == EINTR || errno == ENOMEM || errno == ENOBUFS) continue; if (errno == EAGAIN || errno == EWOULDBLOCK) { /* "once" variant */ if (rw & 2) return 0; continue; } return 0; } } return 1; } /* close all fds except STDIN, STDOUT and STDERR, spare1, spare2 and spare3 */ void close_fds(long max_fd, int spare1, int spare2, int spare3) { /* On Linux, use the /proc/ filesystem to find which files are actually open, rather than iterate over the whole space, for efficiency reasons. On *BSD, the same facility is found at /dev/fd. If this fails we drop back to the dumb code. */ #ifdef HAVE_LINUX_NETWORK #define FDESCFS "/proc/self/fd" #endif #ifdef HAVE_BSD_NETWORK #define FDESCFS "/dev/fd" #endif #ifdef FDESCFS DIR *d; if ((d = opendir(FDESCFS))) { struct dirent *de; while ((de = readdir(d))) { long fd; char *e = NULL; errno = 0; fd = strtol(de->d_name, &e, 10); if (errno != 0 || !e || *e || fd == dirfd(d) || fd == STDOUT_FILENO || fd == STDERR_FILENO || fd == STDIN_FILENO || fd == spare1 || fd == spare2 || fd == spare3) continue; close(fd); } closedir(d); return; } #endif /* fallback, dumb code. */ for (max_fd--; max_fd >= 0; max_fd--) if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && max_fd != STDIN_FILENO && max_fd != spare1 && max_fd != spare2 && max_fd != spare3) close(max_fd); } /* 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); } #ifdef HAVE_LINUX_NETWORK int kernel_version(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, "."); return version * 256 + (split ? atoi(split) : 0); } #endif dnsmasq-2.91/src/dbus.c0000664000175000017500000007656514765043257013225 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_DBUS #include const char* introspection_xml_template = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" #ifdef HAVE_LOOP " \n" " \n" " \n" #endif " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" #ifdef HAVE_DHCP " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" #endif " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n"; static char *introspection_xml = NULL; static int watches_modified = 0; struct watch { DBusWatch *watch; struct watch *next; }; static dbus_bool_t add_watch(DBusWatch *watch, void *data) { struct watch *w; for (w = daemon->watches; w; w = w->next) if (w->watch == watch) return TRUE; if (!(w = whine_malloc(sizeof(struct watch)))) return FALSE; w->watch = watch; w->next = daemon->watches; daemon->watches = w; watches_modified++; (void)data; /* no warning */ return TRUE; } static void remove_watch(DBusWatch *watch, void *data) { struct watch **up, *w, *tmp; for (up = &(daemon->watches), w = daemon->watches; w; w = tmp) { tmp = w->next; if (w->watch == watch) { *up = tmp; free(w); watches_modified++; } else up = &(w->next); } (void)data; /* no warning */ } static DBusMessage* dbus_read_servers(DBusMessage *message) { DBusMessageIter iter; union mysockaddr addr, source_addr; char *domain; if (!dbus_message_iter_init(message, &iter)) { return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Failed to initialize dbus message iter"); } mark_servers(SERV_FROM_DBUS); while (1) { int skip = 0; if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32) { u32 a; dbus_message_iter_get_basic(&iter, &a); dbus_message_iter_next (&iter); #ifdef HAVE_SOCKADDR_SA_LEN source_addr.in.sin_len = addr.in.sin_len = sizeof(struct sockaddr_in); #endif addr.in.sin_addr.s_addr = ntohl(a); source_addr.in.sin_family = addr.in.sin_family = AF_INET; addr.in.sin_port = htons(NAMESERVER_PORT); source_addr.in.sin_addr.s_addr = INADDR_ANY; source_addr.in.sin_port = htons(daemon->query_port); } else if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_BYTE) { unsigned char p[sizeof(struct in6_addr)]; unsigned int i; skip = 1; for(i = 0; i < sizeof(struct in6_addr); i++) { dbus_message_iter_get_basic(&iter, &p[i]); dbus_message_iter_next (&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BYTE) { i++; break; } } if (i == sizeof(struct in6_addr)) { memcpy(&addr.in6.sin6_addr, p, sizeof(struct in6_addr)); #ifdef HAVE_SOCKADDR_SA_LEN source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(struct sockaddr_in6); #endif source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6; addr.in6.sin6_port = htons(NAMESERVER_PORT); source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = 0; source_addr.in6.sin6_scope_id = addr.in6.sin6_scope_id = 0; source_addr.in6.sin6_addr = in6addr_any; source_addr.in6.sin6_port = htons(daemon->query_port); skip = 0; } } else /* At the end */ break; /* process each domain */ do { if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) { dbus_message_iter_get_basic(&iter, &domain); dbus_message_iter_next (&iter); } else domain = NULL; if (!skip) add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain, NULL); } while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING); } /* unlink and free anything still marked. */ cleanup_servers(); return NULL; } #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) { (void)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; u16 flags = 0; char interface[IF_NAMESIZE]; char *str_addr, *str_domain = NULL; struct server_details sdetails = { 0 }; sdetails.addr = &addr; sdetails.source_addr = &source_addr; sdetails.interface = interface; sdetails.flags = &flags; 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) { error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "No domain terminator '%s'", str); break; } *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); } if (strings) { char *p; do { if (str_domain) { if ((p = strchr(str_domain, '/'))) *p++ = 0; } else p = NULL; if (strings && strlen(str_addr) == 0) add_update_server(SERV_LITERAL_ADDRESS | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL); else { if ((addr_err = parse_server(str_addr, &sdetails))) { error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "Invalid IP address '%s': %s", str, addr_err); break; } while (parse_server_next(&sdetails)) { if ((addr_err = parse_server_addr(&sdetails))) { error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "Invalid IP address '%s': %s", str, addr_err); break; } add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL); } } } 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); if ((addr_err = parse_server(str_addr, &sdetails))) { error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "Invalid IP address '%s': %s", str, addr_err); break; } while (parse_server_next(&sdetails)) { if ((addr_err = parse_server_addr(&sdetails))) { 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_LITERAL_ADDRESS; else flags &= ~SERV_LITERAL_ADDRESS; add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL); } } while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING); } if (sdetails.orig_hostinfo) freeaddrinfo(sdetails.orig_hostinfo); /* jump to next element in outer array */ dbus_message_iter_next(&array_iter); } cleanup_servers(); if (dup) free(dup); return error; } static DBusMessage *dbus_get_bool(DBusMessage *message, dbus_bool_t *enabled, char *name) { DBusMessageIter iter; 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"); dbus_message_iter_get_basic(&iter, enabled); if (*enabled) my_syslog(LOG_INFO, _("Enabling --%s option from D-Bus"), name); else my_syslog(LOG_INFO, _("Disabling --%s option from D-Bus"), name); return NULL; } static DBusMessage *dbus_set_bool(DBusMessage *message, int flag, char *name) { dbus_bool_t val; DBusMessage *reply = dbus_get_bool(message, &val, name); if (!reply) { if (val) set_option_bool(flag); else reset_option_bool(flag); } return reply; } #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; union 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.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.addr4))) lease = lease4_allocate(addr.addr4); } #ifdef HAVE_DHCP6 else if (inet_pton(AF_INET6, ipaddr, &addr.addr6)) { if (!(lease = lease6_find_by_addr(&addr.addr6, 128, 0))) lease = lease6_allocate(&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_len < 0) return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "Invalid HW address '%s'", hwaddr); 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; union 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.addr4)) lease = lease_find_by_addr(addr.addr4); #ifdef HAVE_DHCP6 else if (inet_pton(AF_INET6, ipaddr, &addr.addr6)) lease = lease6_find_by_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 static DBusMessage *dbus_get_metrics(DBusMessage* message) { DBusMessage *reply = dbus_message_new_method_return(message); DBusMessageIter array, dict, iter; int i; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{su}", &array); for (i = 0; i < __METRIC_MAX; i++) { const char *key = get_metric_name(i); dbus_uint32_t value = daemon->metrics[i]; dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &dict); dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &key); dbus_message_iter_append_basic(&dict, DBUS_TYPE_UINT32, &value); dbus_message_iter_close_container(&array, &dict); } dbus_message_iter_close_container(&iter, &array); return reply; } static void add_dict_entry(DBusMessageIter *container, const char *key, const char *val) { DBusMessageIter dict; dbus_message_iter_open_container(container, DBUS_TYPE_DICT_ENTRY, NULL, &dict); dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &key); dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &val); dbus_message_iter_close_container(container, &dict); } static void add_dict_int(DBusMessageIter *container, const char *key, const unsigned int val) { snprintf(daemon->namebuff, MAXDNAME, "%u", val); add_dict_entry(container, key, daemon->namebuff); } static DBusMessage *dbus_get_server_metrics(DBusMessage* message) { DBusMessage *reply = dbus_message_new_method_return(message); DBusMessageIter server_array, dict_array, server_iter; struct server *serv; dbus_message_iter_init_append(reply, &server_iter); dbus_message_iter_open_container(&server_iter, DBUS_TYPE_ARRAY, "a{ss}", &server_array); /* sum counts from different records for same server */ for (serv = daemon->servers; serv; serv = serv->next) serv->flags &= ~SERV_MARK; for (serv = daemon->servers; serv; serv = serv->next) if (!(serv->flags & SERV_MARK)) { unsigned int port; unsigned int queries = 0, failed_queries = 0, nxdomain_replies = 0, retrys = 0; unsigned int sigma_latency = 0, count_latency = 0; struct server *serv1; for (serv1 = serv; serv1; serv1 = serv1->next) if (!(serv1->flags & SERV_MARK) && sockaddr_isequal(&serv->addr, &serv1->addr)) { serv1->flags |= SERV_MARK; queries += serv1->queries; failed_queries += serv1->failed_queries; nxdomain_replies += serv1->nxdomain_replies; retrys += serv1->retrys; sigma_latency += serv1->query_latency; count_latency++; } dbus_message_iter_open_container(&server_array, DBUS_TYPE_ARRAY, "{ss}", &dict_array); port = prettyprint_addr(&serv->addr, daemon->namebuff); add_dict_entry(&dict_array, "address", daemon->namebuff); add_dict_int(&dict_array, "port", port); add_dict_int(&dict_array, "queries", serv->queries); add_dict_int(&dict_array, "failed_queries", serv->failed_queries); add_dict_int(&dict_array, "nxdomain", serv->nxdomain_replies); add_dict_int(&dict_array, "retries", serv->retrys); add_dict_int(&dict_array, "latency", sigma_latency/count_latency); dbus_message_iter_close_container(&server_array, &dict_array); } dbus_message_iter_close_container(&server_iter, &server_array); return reply; } 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")) { /* 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; reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_STRING, &v, DBUS_TYPE_INVALID); } #ifdef HAVE_LOOP else if (strcmp(method, "GetLoopServers") == 0) { reply = dbus_reply_server_loop(message); } #endif else if (strcmp(method, "SetServers") == 0) { reply = dbus_read_servers(message); 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, "SetFilterA") == 0) { static int done = 0; static struct rrlist list = { 0, NULL }; dbus_bool_t enabled; if (!(reply = dbus_get_bool(message, &enabled, "filter-A"))) { if (!done) { done = 1; list.next = daemon->filter_rr; daemon->filter_rr = &list; } list.rr = enabled ? T_A : 0; } } else if (strcmp(method, "SetFilterAAAA") == 0) { static int done = 0; static struct rrlist list = { 0, NULL }; dbus_bool_t enabled; if (!(reply = dbus_get_bool(message, &enabled, "filter-AAAA"))) { if (!done) { done = 1; list.next = daemon->filter_rr; daemon->filter_rr = &list; } list.rr = enabled ? T_AAAA : 0; } } else if (strcmp(method, "SetLocaliseQueriesOption") == 0) { reply = dbus_set_bool(message, OPT_LOCALISE, "localise-queries"); } 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, "GetMetrics") == 0) { reply = dbus_get_metrics(message); } else if (strcmp(method, "GetServerMetrics") == 0) { reply = dbus_get_server_metrics(message); } else if (strcmp(method, "ClearMetrics") == 0) { clear_metrics(); } else if (strcmp(method, "ClearCache") == 0) 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(0); if (option_bool(OPT_RELOAD)) clear_cache = 1; } if (clear_cache) clear_cache_and_reload(dnsmasq_time()); (void)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); } /* returns NULL or error message, may fail silently if dbus daemon not yet up. */ char *dbus_init(void) { DBusConnection *connection = NULL; DBusObjectPathVTable dnsmasq_vtable = {NULL, &message_handler, NULL, NULL, NULL, NULL }; DBusError dbus_error; DBusMessage *message; dbus_error_init (&dbus_error); if (!(connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error))) { dbus_error_free(&dbus_error); return NULL; } dbus_connection_set_exit_on_disconnect(connection, FALSE); dbus_connection_set_watch_functions(connection, add_watch, remove_watch, NULL, NULL, NULL); dbus_error_init (&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; if (!dbus_connection_register_object_path(connection, DNSMASQ_PATH, &dnsmasq_vtable, NULL)) return _("could not register a DBus message handler"); daemon->dbus = connection; if ((message = dbus_message_new_signal(DNSMASQ_PATH, daemon->dbus_name, "Up"))) { dbus_connection_send(connection, message, NULL); dbus_message_unref(message); } return NULL; } void set_dbus_listeners(void) { struct watch *w; for (w = daemon->watches; w; w = w->next) if (dbus_watch_get_enabled(w->watch)) { unsigned int flags = dbus_watch_get_flags(w->watch); int fd = dbus_watch_get_unix_fd(w->watch); int poll_flags = POLLERR; if (flags & DBUS_WATCH_READABLE) poll_flags |= POLLIN; if (flags & DBUS_WATCH_WRITABLE) poll_flags |= POLLOUT; poll_listen(fd, poll_flags); } } static int check_dbus_watches() { struct watch *w; watches_modified = 0; for (w = daemon->watches; w; w = w->next) if (dbus_watch_get_enabled(w->watch)) { unsigned int flags = 0; int fd = dbus_watch_get_unix_fd(w->watch); int poll_flags = poll_check(fd, POLLIN|POLLOUT|POLLERR); if ((poll_flags & POLLIN) != 0) flags |= DBUS_WATCH_READABLE; if ((poll_flags & POLLOUT) != 0) flags |= DBUS_WATCH_WRITABLE; if ((poll_flags & POLLERR) != 0) flags |= DBUS_WATCH_ERROR; if (flags != 0) { dbus_watch_handle(w->watch, flags); if (watches_modified) return 0; } } return 1; } void check_dbus_listeners() { DBusConnection *connection = (DBusConnection *)daemon->dbus; while (!check_dbus_watches()) ; if (connection) { dbus_connection_ref (connection); while (dbus_connection_dispatch (connection) == DBUS_DISPATCH_DATA_REMAINS); dbus_connection_unref (connection); } } #ifdef HAVE_DHCP 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, *mac = daemon->namebuff; unsigned char *p; int i; if (!connection) return; if (!hostname) hostname = ""; #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 return; 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, &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); dbus_message_unref(message); } #endif #endif dnsmasq-2.91/src/edns0.c0000664000175000017500000003634514765043257013271 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last) { /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. also return length of pseudoheader in *len and pointer to the UDP size in *p Finally, check to see if a packet is signed. If it is we cannot change a single bit before forwarding. We look for TSIG in the addition section, and TKEY queries (for GSS-TSIG) */ int i, arcount = ntohs(header->arcount); unsigned char *ansp = (unsigned char *)(header+1); unsigned short rdlen, type, class; unsigned char *ret = NULL; if (is_sign) { *is_sign = 0; if (OPCODE(header) == QUERY) { for (i = ntohs(header->qdcount); i != 0; i--) { if (!(ansp = skip_name(ansp, header, plen, 4))) return NULL; GETSHORT(type, ansp); GETSHORT(class, ansp); if (class == C_IN && type == T_TKEY) *is_sign = 1; } } } else { if (!(ansp = skip_questions(header, plen))) return NULL; } if (arcount == 0) return NULL; if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen))) return NULL; for (i = 0; i < arcount; i++) { unsigned char *save, *start = ansp; if (!(ansp = skip_name(ansp, header, plen, 10))) return NULL; GETSHORT(type, ansp); save = ansp; GETSHORT(class, ansp); ansp += 4; /* TTL */ GETSHORT(rdlen, ansp); if (!ADD_RDLEN(header, ansp, plen, rdlen)) return NULL; if (type == T_OPT) { if (len) *len = ansp - start; if (p) *p = save; if (is_last) *is_last = (i == arcount-1); ret = start; } else if (is_sign && i == arcount - 1 && class == C_ANY && type == T_TSIG) *is_sign = 1; } return ret; } /* replace == 0 ->don't replace existing option replace == 1 ->replace existing or add option replace == 2 ->relpace existing option only. */ 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, int replace) { unsigned char *lenp, *datap, *p, *udp_len, *buff = NULL; int rdlen = 0, is_sign, is_last; unsigned short flags = set_do ? 0x8000 : 0, rcode = 0; p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last); if (is_sign) return plen; if (p) { /* Existing header */ int i; unsigned short code, len; p = udp_len; PUTSHORT(daemon->edns_pktsz, p); GETSHORT(rcode, p); GETSHORT(flags, p); if (set_do) { p -= 2; flags |= 0x8000; PUTSHORT(flags, p); } lenp = p; GETSHORT(rdlen, p); if (!CHECK_LEN(header, p, plen, rdlen)) return plen; /* bad packet */ datap = p; /* no option to add */ if (optno == 0) return plen; /* check if option already there */ for (i = 0; i + 4 < rdlen;) { GETSHORT(code, p); GETSHORT(len, p); /* malformed option, delete the whole OPT RR and start again. */ if (i + 4 + len > rdlen) { rdlen = 0; is_last = 0; break; } if (code == optno) { if (replace == 0) return plen; /* delete option if we're to replace it. */ p -= 4; rdlen -= len + 4; memmove(p, p+len+4, rdlen - i); PUTSHORT(rdlen, lenp); lenp -= 2; } else { p += len; i += len + 4; } } /* If we're going to extend the RR, it has to be the last RR in the packet */ if (!is_last) { /* First, take a copy of the options. */ if (rdlen != 0 && (buff = whine_malloc(rdlen))) memcpy(buff, datap, rdlen); /* now, delete OPT RR */ rrfilter(header, &plen, RRFILTER_EDNS0); /* Now, force addition of a new one */ p = NULL; } } if (!p) { /* We are (re)adding the pseudoheader */ if (!(p = skip_questions(header, plen)) || !(p = skip_section(p, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), header, plen)) || p + 11 > limit) { free(buff); return plen; /* bad packet */ } *p++ = 0; /* empty name */ PUTSHORT(T_OPT, p); PUTSHORT(daemon->edns_pktsz, p); /* max packet length, 512 if not given in EDNS0 header */ PUTSHORT(rcode, p); /* extended RCODE and version */ PUTSHORT(flags, p); /* DO flag */ lenp = p; PUTSHORT(rdlen, p); /* RDLEN */ datap = p; /* Copy back any options */ if (buff) { if (p + rdlen > limit) { free(buff); return plen; /* Too big */ } memcpy(p, buff, rdlen); free(buff); p += rdlen; } /* Only bump arcount if RR is going to fit */ if (((ssize_t)optlen) <= (limit - (p + 4))) header->arcount = htons(ntohs(header->arcount) + 1); } if (((ssize_t)optlen) > (limit - (p + 4))) return plen; /* Too big */ /* Add new option */ if (optno != 0 && replace != 2) { if (p + 4 > limit) return plen; /* Too big */ PUTSHORT(optno, p); PUTSHORT(optlen, p); if (p + optlen > limit) return plen; /* Too big */ memcpy(p, opt, optlen); p += optlen; PUTSHORT(p - datap, lenp); } return p - (unsigned char *)header; } size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit) { return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1, 0); } static unsigned char char64(unsigned char c) { return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c & 0x3f]; } static void encoder(unsigned char *in, char *out) { out[0] = char64(in[0]>>2); out[1] = char64((in[0]<<4) | (in[1]>>4)); out[2] = char64((in[1]<<2) | (in[2]>>6)); out[3] = char64(in[2]); } /* OPT_ADD_MAC = MAC is added (if available) OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed OPT_STRIP_MAC = MAC is removed */ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now, int *cacheablep) { int replace = 0, maclen = 0; unsigned char mac[DHCP_CHADDR_MAX]; char encode[18]; /* handle 6 byte MACs ONLY */ if ((option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX)) && (maclen = find_mac(l3, mac, 1, now)) == 6) { if (option_bool(OPT_STRIP_MAC)) replace = 1; *cacheablep = 0; if (option_bool(OPT_MAC_HEX)) print_mac(encode, mac, maclen); else { encoder(mac, encode); encoder(mac+3, encode+4); encode[8] = 0; } } else if (option_bool(OPT_STRIP_MAC)) replace = 2; if (replace != 0 || maclen == 6) plen = add_pseudoheader(header, plen, limit, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace); return plen; } /* OPT_ADD_MAC = MAC is added (if available) OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed OPT_STRIP_MAC = MAC is removed */ static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now, int *cacheablep) { int maclen = 0, replace = 0; unsigned char mac[DHCP_CHADDR_MAX]; if (option_bool(OPT_ADD_MAC) && (maclen = find_mac(l3, mac, 1, now)) != 0) { *cacheablep = 0; if (option_bool(OPT_STRIP_MAC)) replace = 1; } else if (option_bool(OPT_STRIP_MAC)) replace = 2; if (replace != 0 || maclen != 0) plen = add_pseudoheader(header, plen, limit, EDNS0_OPTION_MAC, mac, maclen, 0, replace); return plen; } struct subnet_opt { u16 family; u8 source_netmask, scope_netmask; u8 addr[IN6ADDRSZ]; }; static void *get_addrp(union mysockaddr *addr, const short family) { if (family == AF_INET6) return &addr->in6.sin6_addr; return &addr->in.sin_addr; } static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source, int *cacheablep) { /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */ int len; void *addrp = NULL; int sa_family = source->sa.sa_family; int cacheable = 0; opt->source_netmask = 0; opt->scope_netmask = 0; if (source->sa.sa_family == AF_INET6 && daemon->add_subnet6) { opt->source_netmask = daemon->add_subnet6->mask; if (daemon->add_subnet6->addr_used) { sa_family = daemon->add_subnet6->addr.sa.sa_family; addrp = get_addrp(&daemon->add_subnet6->addr, sa_family); cacheable = 1; } else addrp = &source->in6.sin6_addr; } if (source->sa.sa_family == AF_INET && daemon->add_subnet4) { opt->source_netmask = daemon->add_subnet4->mask; if (daemon->add_subnet4->addr_used) { sa_family = daemon->add_subnet4->addr.sa.sa_family; addrp = get_addrp(&daemon->add_subnet4->addr, sa_family); cacheable = 1; /* Address is constant */ } else addrp = &source->in.sin_addr; } opt->family = htons(sa_family == AF_INET6 ? 2 : 1); if (addrp && 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)); } else { cacheable = 1; /* No address ever supplied. */ len = 0; } if (cacheablep) *cacheablep = cacheable; return len + 4; } /* OPT_CLIENT_SUBNET = client subnet is added OPT_CLIENT_SUBNET + OPT_STRIP_ECS = client subnet is replaced OPT_STRIP_ECS = client subnet is removed */ static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable) { /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */ int replace = 0, len = 0; struct subnet_opt opt; if (option_bool(OPT_CLIENT_SUBNET)) { if (option_bool(OPT_STRIP_ECS)) replace = 1; len = calc_subnet_opt(&opt, source, cacheable); } else if (option_bool(OPT_STRIP_ECS)) replace = 2; else { unsigned char *pheader; /* If we still think the data is cacheable, and we're not messing with EDNS client subnet ourselves, see if the client sent a client subnet. If so, mark the data as uncacheable */ if (*cacheable && (pheader = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL)) && !check_source(header, plen, pheader, NULL)) *cacheable = 0; return plen; } return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace); } int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer) { /* Section 9.2, Check that subnet option (if any) in reply matches. if peer == NULL, this degrades to a check for the existence of and EDNS0 client-subnet option. */ int len, calc_len; struct subnet_opt opt; unsigned char *p; int code, i, rdlen; if (peer) calc_len = calc_subnet_opt(&opt, peer, NULL); 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) { if (peer) { /* make sure this doesn't mismatch. */ opt.scope_netmask = p[3]; if (len != calc_len || memcmp(p, &opt, len) != 0) return 0; } else if (((struct subnet_opt *)p)->source_netmask != 0) return 0; } p += len; } return 1; } /* See https://docs.umbrella.com/umbrella-api/docs/identifying-dns-traffic for * detailed information on packet formating. */ #define UMBRELLA_VERSION 1 #define UMBRELLA_TYPESZ 2 #define UMBRELLA_ASSET 0x0004 #define UMBRELLA_ASSETSZ sizeof(daemon->umbrella_asset) #define UMBRELLA_ORG 0x0008 #define UMBRELLA_ORGSZ sizeof(daemon->umbrella_org) #define UMBRELLA_IPV4 0x0010 #define UMBRELLA_IPV6 0x0020 #define UMBRELLA_DEVICE 0x0040 #define UMBRELLA_DEVICESZ sizeof(daemon->umbrella_device) struct umbrella_opt { u8 magic[4]; u8 version; u8 flags; /* We have 4 possible fields since we'll never send both IPv4 and * IPv6, so using the larger of the two to calculate max buffer size. * Each field also has a type header. So the following accounts for * the type headers and each field size to get a max buffer size. */ u8 fields[4 * UMBRELLA_TYPESZ + UMBRELLA_ORGSZ + IN6ADDRSZ + UMBRELLA_DEVICESZ + UMBRELLA_ASSETSZ]; }; static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable) { *cacheable = 0; struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {0}}; u8 *u = &opt.fields[0]; int family = source->sa.sa_family; int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ; if (daemon->umbrella_org) { PUTSHORT(UMBRELLA_ORG, u); PUTLONG(daemon->umbrella_org, u); } PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u); memcpy(u, get_addrp(source, family), size); u += size; if (option_bool(OPT_UMBRELLA_DEVID)) { PUTSHORT(UMBRELLA_DEVICE, u); memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ); u += UMBRELLA_DEVICESZ; } if (daemon->umbrella_asset) { PUTSHORT(UMBRELLA_ASSET, u); PUTLONG(daemon->umbrella_asset, u); } return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1); } /* Set *check_subnet if we add a client subnet option, which needs to checked in the reply. Set *cacheable to zero if we add an option which the answer may depend on. */ size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, time_t now, int *cacheable) { *cacheable = 1; plen = add_mac(header, plen, limit, source, now, cacheable); plen = add_dns_client(header, plen, limit, source, now, cacheable); if (daemon->dns_client_id) plen = add_pseudoheader(header, plen, limit, EDNS0_OPTION_NOMCPEID, (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1); if (option_bool(OPT_UMBRELLA)) plen = add_umbrella_opt(header, plen, limit, source, cacheable); plen = add_source_addr(header, plen, limit, source, cacheable); return plen; } dnsmasq-2.91/src/lease.c0000664000175000017500000007625114765043257013351 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_DHCP static struct dhcp_lease *leases = NULL, *old_leases = NULL; static int dns_dirty, file_dirty, leases_left; static int read_leases(time_t now, FILE *leasestream) { unsigned long ei; union all_addr addr; struct dhcp_lease *lease; int clid_len, hw_len, hw_type; int items; *daemon->dhcp_buff3 = *daemon->dhcp_buff2 = '\0'; /* client-id max length is 255 which is 255*2 digits + 254 colons borrow DNS packet buffer which is always larger than 1000 bytes Check various buffers are big enough for the code below */ #if (DHCP_BUFF_SZ < 255) || (MAXDNAME < 64) || (PACKETSZ+MAXDNAME+RRFIXEDSZ < 764) # error Buffer size breakage in leasefile parsing. #endif while ((items=fscanf(leasestream, "%255s %255s", daemon->dhcp_buff3, daemon->dhcp_buff2)) == 2) { *daemon->namebuff = *daemon->dhcp_buff = *daemon->packet = '\0'; hw_len = hw_type = clid_len = 0; #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); if (daemon->duid_len < 0) return 0; daemon->duid = safe_malloc(daemon->duid_len); memcpy(daemon->duid, daemon->dhcp_buff2, daemon->duid_len); continue; } #endif if (fscanf(leasestream, " %64s %255s %764s", daemon->namebuff, daemon->dhcp_buff, daemon->packet) != 3) { my_syslog(MS_DHCP | LOG_WARNING, _("ignoring invalid line in lease database: %s %s %s %s ..."), daemon->dhcp_buff3, daemon->dhcp_buff2, daemon->namebuff, daemon->dhcp_buff); continue; } if (inet_pton(AF_INET, daemon->namebuff, &addr.addr4)) { lease = lease4_allocate(addr.addr4); hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type); /* For backwards compatibility, no explicit MAC address type means ether. */ if (hw_type == 0 && hw_len != 0) hw_type = ARPHRD_ETHER; } #ifdef HAVE_DHCP6 else if (inet_pton(AF_INET6, daemon->namebuff, &addr.addr6)) { char *s = daemon->dhcp_buff2; int lease_type = LEASE_NA; if (s[0] == 'T') { lease_type = LEASE_TA; s++; } if ((lease = lease6_allocate(&addr.addr6, lease_type))) lease_set_iaid(lease, strtoul(s, NULL, 10)); } #endif else { my_syslog(MS_DHCP | LOG_WARNING, _("ignoring invalid line in lease database, bad address: %s"), daemon->namebuff); continue; } if (!lease) die (_("too many stored leases"), NULL, EC_MISC); if (strcmp(daemon->packet, "*") != 0) clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL); 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, NULL, NULL); ei = atol(daemon->dhcp_buff3); #ifdef HAVE_BROKEN_RTC if (ei != 0) lease->expires = (time_t)ei + now; else lease->expires = (time_t)0; lease->length = ei; #else /* strictly time_t is opaque, but this hack should work on all sane systems, even when sizeof(time_t) == 8 */ lease->expires = (time_t)ei; #endif /* set these correctly: the "old" events are generated later from the startup synthesised SIGHUP. */ lease->flags &= ~(LEASE_NEW | LEASE_CHANGED); *daemon->dhcp_buff3 = *daemon->dhcp_buff2 = '\0'; } return (items == 0 || items == EOF); } void lease_init(time_t now) { FILE *leasestream; leases_left = daemon->dhcp_max; if (option_bool(OPT_LEASE_RO)) { /* run " init" once to get the initial state of the database. If leasefile-ro is set without a script, we just do without any lease database. */ #ifdef HAVE_SCRIPT if (daemon->lease_change_command) { /* 6 == strlen(" init") plus terminator */ if (strlen(daemon->lease_change_command) + 6 > DHCP_BUFF_SZ) die(_("lease-change script name is too long"), NULL, EC_FILE); strcpy(daemon->dhcp_buff, daemon->lease_change_command); strcat(daemon->dhcp_buff, " init"); leasestream = popen(daemon->dhcp_buff, "r"); } else #endif { file_dirty = dns_dirty = 0; return; } } else { /* NOTE: need a+ mode to create file if it doesn't exist */ leasestream = daemon->lease_stream = fopen(daemon->lease_file, "a+"); if (!leasestream) die(_("cannot open or create lease file %s: %s"), daemon->lease_file, EC_FILE); /* a+ mode leaves pointer at end. */ rewind(leasestream); } if (leasestream) { if (!read_leases(now, leasestream)) my_syslog(MS_DHCP | LOG_ERR, _("failed to parse lease database cleanly")); if (ferror(leasestream)) die(_("failed to read lease file %s: %s"), daemon->lease_file, EC_FILE); } #ifdef HAVE_SCRIPT if (!daemon->lease_stream) { int rc = 0; /* shell returns 127 for "command not found", 126 for bad permissions. */ if (!leasestream || (rc = pclose(leasestream)) == -1 || WEXITSTATUS(rc) == 127 || WEXITSTATUS(rc) == 126) { if (WEXITSTATUS(rc) == 127) errno = ENOENT; else if (WEXITSTATUS(rc) == 126) errno = EACCES; die(_("cannot run lease-init script %s: %s"), daemon->lease_change_command, EC_FILE); } if (WEXITSTATUS(rc) != 0) { sprintf(daemon->dhcp_buff, "%d", WEXITSTATUS(rc)); die(_("lease-init script returned exit code %s"), daemon->dhcp_buff, WEXITSTATUS(rc) + EC_INIT_OFFSET); } } #endif /* Some leases may have expired */ file_dirty = 0; lease_prune(NULL, now); dns_dirty = 1; } void lease_update_from_configs(void) { /* changes to the config may change current leases. */ struct dhcp_lease *lease; struct dhcp_config *config; char *name; for (lease = leases; lease; lease = lease->next) 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, 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, get_domain(lease->addr), NULL); /* updates auth flag only */ } static void ourprintf(int *errp, char *format, ...) { va_list ap; va_start(ap, format); if (!(*errp) && vfprintf(daemon->lease_stream, format, ap) < 0) *errp = errno; va_end(ap); } void lease_update_file(time_t now) { struct dhcp_lease *lease; time_t next_event; int i, err = 0; if (file_dirty != 0 && daemon->lease_stream) { errno = 0; rewind(daemon->lease_stream); if (errno != 0 || ftruncate(fileno(daemon->lease_stream), 0) != 0) err = errno; 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++) { ourprintf(&err, "%.2x", lease->hwaddr[i]); if (i != lease->hwaddr_len - 1) ourprintf(&err, ":"); } inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN); ourprintf(&err, " %s ", 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"); } #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; if (!err) file_dirty = 0; } /* 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) > 0.0)) next_event = lease->expires; if (err) { if (next_event == 0 || difftime(next_event, LEASE_RETRY + now) > 0.0) next_event = LEASE_RETRY + now; my_syslog(MS_DHCP | LOG_ERR, _("failed to write %s: %s (retry in %u s)"), daemon->lease_file, strerror(err), (unsigned int)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; } #ifdef HAVE_DHCP6 static int find_interface_v6(struct in6_addr *local, int prefix, int scope, int if_index, int flags, unsigned int preferred, unsigned 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 construct a new RA-names context, to add putative new SLAAC addresses to existing leases. */ struct dhcp_lease *lease; 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, (callback_t){.af_inet=find_interface_v4}); #ifdef HAVE_DHCP6 iface_enumerate(AF_INET6, &now, (callback_t){.af_inet6=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) { 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, (union all_addr *)&slaac->addr, lease->expires); if (!option_bool(OPT_DHCP_FQDN) && lease->hostname) cache_add_dhcp_entry(lease->hostname, AF_INET6, (union all_addr *)&slaac->addr, lease->expires); } } if (lease->fqdn) cache_add_dhcp_entry(lease->fqdn, prot, prot == AF_INET ? (union all_addr *)&lease->addr : (union all_addr *)&lease->addr6, lease->expires); if (!option_bool(OPT_DHCP_FQDN) && lease->hostname) cache_add_dhcp_entry(lease->hostname, prot, prot == AF_INET ? (union all_addr *)&lease->addr : (union all_addr *)&lease->addr6, lease->expires); #else if (lease->fqdn) cache_add_dhcp_entry(lease->fqdn, prot, (union all_addr *)&lease->addr, lease->expires); if (!option_bool(OPT_DHCP_FQDN) && lease->hostname) cache_add_dhcp_entry(lease->hostname, prot, (union all_addr *)&lease->addr, lease->expires); #endif } dns_dirty = 0; } } void lease_prune(struct dhcp_lease *target, time_t now) { struct dhcp_lease *lease, *tmp, **up; for (lease = leases, up = &leases; lease; lease = tmp) { tmp = lease->next; if ((lease->expires != 0 && difftime(now, lease->expires) >= 0) || lease == target) { file_dirty = 1; if (lease->hostname) dns_dirty = 1; daemon->metrics[lease->addr.s_addr ? METRIC_LEASES_PRUNED_4 : METRIC_LEASES_PRUNED_6]++; *up = lease->next; /* unlink */ /* Put on old_leases list 'till we can run the script */ lease->next = old_leases; old_leases = lease; leases_left++; } else up = &lease->next; } } 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; if (clid) for (lease = leases; lease; lease = lease->next) { #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) { #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; } struct dhcp_lease *lease_find_by_addr(struct in_addr addr) { struct dhcp_lease *lease; for (lease = leases; lease; lease = lease->next) { #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, unsigned 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, unsigned 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; } 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->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; file_dirty = 1; leases_left--; return lease; } struct dhcp_lease *lease4_allocate(struct in_addr addr) { struct dhcp_lease *lease = lease_allocate(); if (lease) { lease->addr = addr; daemon->metrics[METRIC_LEASES_ALLOCATED_4]++; } 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; daemon->metrics[METRIC_LEASES_ALLOCATED_6]++; } 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 infinite 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->flags |= LEASE_AUX_CHANGED | LEASE_EXP_CHANGED; file_dirty = 1; #endif } #ifdef HAVE_BROKEN_RTC if (len != lease->length) { lease->length = len; lease->flags |= LEASE_AUX_CHANGED; file_dirty = 1; } #endif } #ifdef HAVE_DHCP6 void lease_set_iaid(struct dhcp_lease *lease, unsigned 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)) { if (hw_len != 0) memcpy(lease->hwaddr, hwaddr, hw_len); lease->hwaddr_len = hw_len; lease->hwaddr_type = hw_type; lease->flags |= LEASE_CHANGED; file_dirty = 1; /* run script on change */ } /* only update clid when one is available, stops packets without a clid removing the record. Lease init uses clid_len == 0 for no clid. */ if (clid_len != 0 && clid) { if (!lease->clid) lease->clid_len = 0; if (lease->clid_len != clid_len) { 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->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) { /* run script to say we lost our old name */ /* this shouldn't happen unless updates are very quick and the script very slow, we just avoid a memory leak if it does. */ free(lease->old_hostname); /* If we know the fqdn, pass that. The helper will derive the unqualified name from it, free the unqualified name here. */ if (lease->fqdn) { lease->old_hostname = lease->fqdn; free(lease->hostname); } else lease->old_hostname = lease->hostname; lease->hostname = lease->fqdn = NULL; } void lease_calc_fqdns(void) { struct dhcp_lease *lease; for (lease = leases; lease; lease = lease->next) { char *domain; if (lease->hostname) { #ifdef HAVE_DHCP6 if (lease->flags & (LEASE_TA | LEASE_NA)) domain = get_domain6(&lease->addr6); else #endif domain = get_domain(lease->addr); if (domain) { /* This is called only during startup, before forking, hence safe_malloc() */ lease->fqdn = safe_malloc(strlen(lease->hostname) + strlen(domain) + 2); strcpy(lease->fqdn, lease->hostname); strcat(lease->fqdn, "."); strcat(lease->fqdn, domain); } } } } 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)) { if (auth) lease->flags |= LEASE_AUTH_NAME; return; } if (!name && !lease->hostname) return; /* 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))) { strcpy(new_name, name); if (domain && (new_fqdn = whine_malloc(strlen(new_name) + strlen(domain) + 2))) { strcpy(new_fqdn, name); strcat(new_fqdn, "."); strcat(new_fqdn, domain); } } /* Depending on mode, we check either unqualified name or FQDN. */ for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next) { if (option_bool(OPT_DHCP_FQDN)) { if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn)) continue; } else { if (!new_name || !lease_tmp->hostname || !hostname_isequal(lease_tmp->hostname, new_name) ) continue; } 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); return; } kill_name(lease_tmp); lease_tmp->flags |= LEASE_CHANGED; /* run script on change */ break; } } if (lease->hostname) kill_name(lease); lease->hostname = new_name; lease->fqdn = new_fqdn; if (auth) lease->flags |= LEASE_AUTH_NAME; file_dirty = 1; dns_dirty = 1; lease->flags |= LEASE_CHANGED; /* run script on change */ } 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->flags |= LEASE_CHANGED; #ifdef HAVE_DHCP6 slaac_add_addrs(lease, now, 0); #endif } void rerun_scripts(void) { struct dhcp_lease *lease; for (lease = leases; lease; lease = lease->next) lease->flags |= LEASE_CHANGED; } /* deleted leases get transferred to the old_leases list. remove them here, after calling the lease change script. Also run the lease change script on new/modified leases. Return zero if nothing to do. */ 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. */ if (option_bool(OPT_DBUS) && !daemon->dbus) return 0; #endif if (old_leases) { lease = old_leases; /* If the lease still has an old_hostname, do the "old" action on that first */ if (lease->old_hostname) { #ifdef HAVE_SCRIPT queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now); #endif free(lease->old_hostname); lease->old_hostname = NULL; return 1; } 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); #endif #ifdef HAVE_DBUS emit_dbus_signal(ACTION_DEL, lease, lease->old_hostname); #endif old_leases = lease->next; free(lease->old_hostname); free(lease->clid); free(lease->extradata); free(lease); return 1; } } /* make sure we announce the loss of a hostname before its new location. */ for (lease = leases; lease; lease = lease->next) if (lease->old_hostname) { #ifdef HAVE_SCRIPT queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now); #endif free(lease->old_hostname); lease->old_hostname = NULL; return 1; } for (lease = leases; lease; lease = lease->next) if ((lease->flags & (LEASE_NEW | LEASE_CHANGED)) || ((lease->flags & LEASE_AUX_CHANGED) && option_bool(OPT_LEASE_RO)) || ((lease->flags & LEASE_EXP_CHANGED) && option_bool(OPT_LEASE_RENEW))) { #ifdef HAVE_SCRIPT 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->flags & LEASE_NEW) ? ACTION_ADD : ACTION_OLD, lease, lease->fqdn ? lease->fqdn : lease->hostname); #endif lease->flags &= ~(LEASE_NEW | LEASE_CHANGED | LEASE_AUX_CHANGED | LEASE_EXP_CHANGED); /* this is used for the "add" call, then junked, since they're not in the database */ free(lease->extradata); lease->extradata = NULL; return 1; } return 0; /* nothing to do */ } #ifdef HAVE_SCRIPT /* delim == -1 -> delim = 0, but embedded 0s, creating extra records, are OK. */ void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned int len, int delim) { unsigned int i; if (delim == -1) delim = 0; else /* check for embedded 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_realloc(lease->extradata, newsz); if (!new) return; 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 /* HAVE_DHCP */ dnsmasq-2.91/src/dhcp-protocol.h0000664000175000017500000000667014765043257015040 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #define DHCP_SERVER_PORT 67 #define DHCP_CLIENT_PORT 68 #define DHCP_SERVER_ALTPORT 1067 #define DHCP_CLIENT_ALTPORT 1068 #define PXE_PORT 4011 /* These each hold a DHCP option max size 255 and get a terminating zero added */ #define DHCP_BUFF_SZ 256 #define BOOTREQUEST 1 #define BOOTREPLY 2 #define DHCP_COOKIE 0x63825363 /* The Linux in-kernel DHCP client silently ignores any packet smaller than this. Sigh........... */ #define MIN_PACKETSZ 300 #define OPTION_PAD 0 #define OPTION_NETMASK 1 #define OPTION_ROUTER 3 #define OPTION_DNSSERVER 6 #define OPTION_HOSTNAME 12 #define OPTION_DOMAINNAME 15 #define OPTION_BROADCAST 28 #define OPTION_VENDOR_CLASS_OPT 43 #define OPTION_REQUESTED_IP 50 #define OPTION_LEASE_TIME 51 #define OPTION_OVERLOAD 52 #define OPTION_MESSAGE_TYPE 53 #define OPTION_SERVER_IDENTIFIER 54 #define OPTION_REQUESTED_OPTIONS 55 #define OPTION_MESSAGE 56 #define OPTION_MAXMESSAGE 57 #define OPTION_T1 58 #define OPTION_T2 59 #define OPTION_VENDOR_ID 60 #define OPTION_CLIENT_ID 61 #define OPTION_SNAME 66 #define OPTION_FILENAME 67 #define OPTION_USER_CLASS 77 #define OPTION_RAPID_COMMIT 80 #define OPTION_CLIENT_FQDN 81 #define OPTION_AGENT_ID 82 #define OPTION_ARCH 93 #define OPTION_PXE_UUID 97 #define OPTION_SUBNET_SELECT 118 #define OPTION_DOMAIN_SEARCH 119 #define OPTION_SIP_SERVER 120 #define OPTION_VENDOR_IDENT 124 #define OPTION_VENDOR_IDENT_OPT 125 #define OPTION_MUD_URL_V4 161 #define OPTION_END 255 #define SUBOPT_CIRCUIT_ID 1 #define SUBOPT_REMOTE_ID 2 #define SUBOPT_SUBNET_SELECT 5 /* RFC 3527 */ #define SUBOPT_SUBSCR_ID 6 /* RFC 3393 */ #define SUBOPT_SERVER_OR 11 /* RFC 5107 */ #define SUBOPT_PXE_BOOT_ITEM 71 /* PXE standard */ #define SUBOPT_PXE_DISCOVERY 6 #define SUBOPT_PXE_SERVERS 8 #define SUBOPT_PXE_MENU 9 #define SUBOPT_PXE_MENU_PROMPT 10 #define DHCPDISCOVER 1 #define DHCPOFFER 2 #define DHCPREQUEST 3 #define DHCPDECLINE 4 #define DHCPACK 5 #define DHCPNAK 6 #define DHCPRELEASE 7 #define DHCPINFORM 8 #define BRDBAND_FORUM_IANA 3561 /* Broadband forum IANA enterprise */ #define DHCP_CHADDR_MAX 16 struct dhcp_packet { u8 op, htype, hlen, hops; u32 xid; u16 secs, flags; struct in_addr ciaddr, yiaddr, siaddr, giaddr; u8 chaddr[DHCP_CHADDR_MAX], sname[64], file[128]; u8 options[312]; }; dnsmasq-2.91/src/tables.c0000664000175000017500000000675414765043257013533 0ustar srksrk/* 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 . */ #include "dnsmasq.h" #if defined(HAVE_IPSET) && defined(HAVE_BSD_NETWORK) #include #include #include #include #include #include #include #include #include #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); } } 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 union all_addr *ipaddr, int flags, int remove) { struct pfr_addr addr; struct pfioc_table io; struct pfr_table table; 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; } bzero(&io, sizeof io); io.pfrio_flags = 0; io.pfrio_buffer = &table; io.pfrio_esize = sizeof(table); io.pfrio_size = 1; if (ioctl(dev, DIOCRADDTABLES, &io)) { my_syslog(LOG_WARNING, _("IPset: error: %s"), pfr_strerror(errno)); return -1; } table.pfrt_flags &= ~PFR_TFLAG_PERSIST; if (io.pfrio_nadd) my_syslog(LOG_INFO, _("info: table created")); bzero(&addr, sizeof(addr)); if (flags & F_IPV6) { addr.pfra_af = AF_INET6; addr.pfra_net = 0x80; memcpy(&(addr.pfra_ip6addr), ipaddr, sizeof(struct in6_addr)); } else { addr.pfra_af = AF_INET; addr.pfra_net = 0x20; addr.pfra_ip4addr.s_addr = ipaddr->addr4.s_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 dnsmasq-2.91/src/network.c0000664000175000017500000013720614765043257013747 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_LINUX_NETWORK int indextoname(int fd, int index, char *name) { struct ifreq ifr; if (index == 0) return 0; ifr.ifr_ifindex = index; if (ioctl(fd, SIOCGIFNAME, &ifr) == -1) return 0; safe_strncpy(name, ifr.ifr_name, IF_NAMESIZE); return 1; } #elif defined(HAVE_SOLARIS_NETWORK) #include #include #ifndef LIFC_UNDER_IPMP # define LIFC_UNDER_IPMP 0 #endif int indextoname(int fd, int index, char *name) { int64_t lifc_flags; struct lifnum lifn; int numifs, bufsize, i; struct lifconf lifc; struct lifreq *lifrp; if (index == 0) return 0; if (getzoneid() == GLOBAL_ZONEID) { if (!if_indextoname(index, name)) return 0; return 1; } lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES | LIFC_UNDER_IPMP; lifn.lifn_family = AF_UNSPEC; lifn.lifn_flags = lifc_flags; if (ioctl(fd, SIOCGLIFNUM, &lifn) < 0) return 0; numifs = lifn.lifn_count; bufsize = numifs * sizeof(struct lifreq); lifc.lifc_family = AF_UNSPEC; lifc.lifc_flags = lifc_flags; lifc.lifc_len = bufsize; lifc.lifc_buf = alloca(bufsize); if (ioctl(fd, SIOCGLIFCONF, &lifc) < 0) return 0; lifrp = lifc.lifc_req; for (i = lifc.lifc_len / sizeof(struct lifreq); i; i--, lifrp++) { struct lifreq lifr; safe_strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE); if (ioctl(fd, SIOCGLIFINDEX, &lifr) < 0) return 0; if (lifr.lifr_index == index) { safe_strncpy(name, lifr.lifr_name, IF_NAMESIZE); return 1; } } return 0; } #else int indextoname(int fd, int index, char *name) { (void)fd; if (index == 0 || !if_indextoname(index, name)) return 0; return 1; } #endif int iface_check(int family, union all_addr *addr, char *name, int *auth) { struct iname *tmp; int ret = 1, match_addr = 0; /* Note: have to check all and not bail out early, so that we set the "used" flags. May be called with family == AF_LOCAL to check interface by name only. */ if (daemon->if_names || daemon->if_addrs) { ret = 0; for (tmp = daemon->if_names; tmp; tmp = tmp->next) if (tmp->name && wildcard_match(tmp->name, name)) { tmp->flags |= INAME_USED; ret = 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->addr4.s_addr) { tmp->flags |= INAME_USED; ret = match_addr = 1; } else if (family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr6)) { tmp->flags |= INAME_USED; ret = match_addr = 1; } } } if (!match_addr) for (tmp = daemon->if_except; tmp; tmp = tmp->next) if (tmp->name && wildcard_match(tmp->name, name)) ret = 0; if (auth) { *auth = 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->addr4.s_addr) break; else if (addr && tmp->addr.sa.sa_family == AF_INET6 && family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr6)) break; if (tmp) { *auth = 1; ret = 1; } } return ret; } /* Fix for problem that the kernel sometimes reports the loopback interface 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, union all_addr *addr, char *name) { struct ifreq ifr; struct irec *iface; safe_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) { if (iface->addr.in.sin_addr.s_addr == addr->addr4.s_addr) return 1; } else if (IN6_ARE_ADDR_EQUAL(&iface->addr.in6.sin6_addr, &addr->addr6)) return 1; } } return 0; } /* 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, union all_addr *addr) { struct irec *iface; /* 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->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; struct cond_domain *cond; int loopback; struct ifreq ifr; int tftp_ok = !!option_bool(OPT_TFTP); int dhcp4_ok = 1; int dhcp6_ok = 1; int auth_dns = 0; int is_label = 0; #if defined(HAVE_DHCP) || defined(HAVE_TFTP) struct iname *tmp; #endif (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) dhcp4_ok = dhcp6_ok = 0; if (!label) label = ifr.ifr_name; else is_label = strcmp(label, ifr.ifr_name); /* maintain a list of all addresses on all interfaces for --local-service option */ if (option_bool(OPT_LOCAL_SERVICE)) { struct addrlist *al; if (param->spare) { 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.addr4 = addr->in.sin_addr; al->flags = 0; } else { al->addr.addr6 = addr->in6.sin6_addr; al->flags = ADDRLIST_IPV6; } } } if (addr->sa.sa_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr)) { 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.addr4 = addr->in.sin_addr; al->flags = 0; } } 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.addr6 = addr->in6.sin6_addr; al->flags = ADDRLIST_IPV6; } } } #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) { struct addrlist *lp; al = NULL; if (addr->sa.sa_family == AF_INET && (int_name->flags & (IN4 | INP4))) { struct in_addr newaddr = addr->in.sin_addr; if (int_name->flags & INP4) newaddr.s_addr = (addr->in.sin_addr.s_addr & netmask.s_addr) | (int_name->proto4.s_addr & ~netmask.s_addr); /* check for duplicates. */ for (lp = int_name->addr; lp; lp = lp->next) if (lp->flags == 0 && lp->addr.addr4.s_addr == newaddr.s_addr) break; if (!lp) { if (param->spare) { al = param->spare; param->spare = al->next; } else al = whine_malloc(sizeof(struct addrlist)); if (al) { al->flags = 0; al->addr.addr4 = newaddr; } } } if (addr->sa.sa_family == AF_INET6 && (int_name->flags & (IN6 | INP6))) { struct in6_addr newaddr = addr->in6.sin6_addr; if (int_name->flags & INP6) { int i; for (i = 0; i < 16; i++) { int bits = ((i+1)*8) - prefixlen; if (bits >= 8) newaddr.s6_addr[i] = int_name->proto6.s6_addr[i]; else if (bits >= 0) { unsigned char mask = 0xff << bits; newaddr.s6_addr[i] = (addr->in6.sin6_addr.s6_addr[i] & mask) | (int_name->proto6.s6_addr[i] & ~mask); } } } /* check for duplicates. */ for (lp = int_name->addr; lp; lp = lp->next) if ((lp->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&lp->addr.addr6, &newaddr)) break; if (!lp) { if (param->spare) { al = param->spare; param->spare = al->next; } else al = whine_malloc(sizeof(struct addrlist)); if (al) { al->flags = ADDRLIST_IPV6; al->addr.addr6 = newaddr; /* Privacy addresses and addresses still undergoing DAD and deprecated addresses don't appear in forward queries, but will in reverse ones. */ if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE))) al->flags |= ADDRLIST_REVONLY; } } } if (al) { al->next = int_name->addr; int_name->addr = al; } } } /* Update addresses for domain=, */ for (cond = daemon->cond_domain; cond; cond = cond->next) if (cond->interface && strncmp(label, cond->interface, IF_NAMESIZE) == 0) { struct addrlist *al; if (param->spare) { al = param->spare; param->spare = al->next; } else al = whine_malloc(sizeof(struct addrlist)); if (addr->sa.sa_family == AF_INET) { al->addr.addr4 = addr->in.sin_addr; al->flags = 0; } else { al->addr.addr6 = addr->in6.sin6_addr; al->flags = ADDRLIST_IPV6; } al->prefixlen = prefixlen; al->next = cond->al; cond->al = al; } /* 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->index == if_index) { iface->dad = !!(iface_flags & IFACE_TENTATIVE); iface->found = 1; /* for garbage collection */ iface->netmask = netmask; 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) break; if (!lo && (lo = whine_malloc(sizeof(struct iname)))) { if ((lo->name = whine_malloc(strlen(ifr.ifr_name)+1))) { strcpy(lo->name, ifr.ifr_name); lo->flags |= INAME_USED; lo->next = daemon->if_names; daemon->if_names = lo; } else free(lo); } } if (addr->sa.sa_family == AF_INET && !iface_check(AF_INET, (union all_addr *)&addr->in.sin_addr, label, &auth_dns)) return 1; if (addr->sa.sa_family == AF_INET6 && !iface_check(AF_INET6, (union all_addr *)&addr->in6.sin6_addr, label, &auth_dns)) return 1; #ifdef HAVE_DHCP /* No DHCP where we're doing auth DNS. */ if (auth_dns) { tftp_ok = 0; dhcp4_ok = dhcp6_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; if (tmp->flags & INAME_4) dhcp4_ok = 0; if (tmp->flags & INAME_6) dhcp6_ok = 0; } #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)))) { int mtu = 0; if (ioctl(param->fd, SIOCGIFMTU, &ifr) != -1) mtu = ifr.ifr_mtu; iface->addr = *addr; iface->netmask = netmask; iface->tftp_ok = tftp_ok; iface->dhcp4_ok = dhcp4_ok; iface->dhcp6_ok = dhcp6_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; iface->label = is_label; if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1))) { strcpy(iface->name, ifr.ifr_name); iface->next = daemon->interfaces; daemon->interfaces = iface; return 1; } free(iface); } errno = ENOMEM; return 0; } static int iface_allowed_v6(struct in6_addr *local, int prefix, int scope, int if_index, int flags, unsigned int preferred, unsigned 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 addr.in6.sin6_len = sizeof(addr.in6); #endif addr.in6.sin6_family = AF_INET6; addr.in6.sin6_addr = *local; addr.in6.sin6_port = htons(daemon->port); /* 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 iface_param *)vparam, if_index, NULL, &addr, netmask, prefix, flags); } 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; (void)broadcast; /* warning */ memset(&addr, 0, sizeof(addr)); #ifdef HAVE_SOCKADDR_SA_LEN addr.in.sin_len = sizeof(addr.in); #endif addr.in.sin_family = AF_INET; addr.in.sin_addr = local; addr.in.sin_port = htons(daemon->port); /* 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); } /* * Clean old interfaces no longer found. */ static void clean_interfaces(void) { struct irec *iface; struct irec **up = &daemon->interfaces; for (iface = *up; iface; iface = *up) { if (!iface->found && !iface->done) { *up = iface->next; free(iface->name); free(iface); } else { up = &iface->next; } } } /** Release listener if no other interface needs it. * * @return 1 if released, 0 if still required */ static int release_listener(struct listener *l) { if (l->used > 1) { struct irec *iface; for (iface = daemon->interfaces; iface; iface = iface->next) if (iface->done && sockaddr_isequal(&l->addr, &iface->addr)) { if (iface->found) { /* update listener to point to active interface instead */ if (!l->iface->found) l->iface = iface; } else { l->used--; iface->done = 0; } } /* Someone is still using this listener, skip its deletion */ if (l->used > 0) return 0; } if (l->iface->done) { int port; port = prettyprint_addr(&l->iface->addr, daemon->addrbuff); my_syslog(LOG_DEBUG|MS_DEBUG, _("stopped listening on %s(#%d): %s port %d"), l->iface->name, l->iface->index, daemon->addrbuff, port); /* In case it ever returns */ l->iface->done = 0; } if (l->fd != -1) close(l->fd); if (l->tcpfd != -1) close(l->tcpfd); if (l->tftpfd != -1) close(l->tftpfd); free(l); return 1; } 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 cond_domain *cond; struct irec *iface; #ifdef HAVE_AUTH struct auth_zone *zone; #endif struct server *serv; /* Do this max once per select cycle - also inhibits netlink socket use in TCP child processes. */ if (reset) { done = 0; return 1; } if (done) return 1; done = 1; if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return 0; /* iface indexes can change when interfaces are created/destroyed. We use them in the main forwarding control path, when the path to a server is specified by an interface, so cache them. Update the cache here. */ for (serv = daemon->servers; serv; serv = serv->next) if (serv->interface[0] != 0) { #ifdef HAVE_LINUX_NETWORK struct ifreq ifr; safe_strncpy(ifr.ifr_name, serv->interface, IF_NAMESIZE); if (ioctl(param.fd, SIOCGIFINDEX, &ifr) != -1) serv->ifindex = ifr.ifr_ifindex; #else serv->ifindex = if_nametoindex(serv->interface); #endif } again: /* Mark interfaces for garbage collection */ for (iface = daemon->interfaces; iface; iface = iface->next) iface->found = 0; /* 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 addresses stored against cond-domains. */ for (cond = daemon->cond_domain; cond; cond = cond->next) { for (addr = cond->al; addr; addr = tmp) { tmp = addr->next; addr->next = spare; spare = addr; } cond->al = 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; ret = iface_enumerate(AF_INET6, ¶m, (callback_t){.af_inet6=iface_allowed_v6}); if (ret < 0) goto again; else if (ret) { ret = iface_enumerate(AF_INET, ¶m, (callback_t){.af_inet=iface_allowed_v4}); if (ret < 0) goto again; } 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 reentrancy, 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; int freed = 0; for (up = &daemon->listeners, l = daemon->listeners; l; l = tmp) { tmp = l->next; if (!l->iface || l->iface->found) up = &l->next; else if (release_listener(l)) { *up = tmp; freed = 1; } } if (freed) clean_interfaces(); } errno = errsave; spare = param.spare; return ret; } /* set NONBLOCK bit on fd: See Stevens 16.6 */ int fix_fd(int fd) { int flags; if ((flags = fcntl(fd, F_GETFL)) == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) return 0; return 1; } static int make_sock(union mysockaddr *addr, int type, int dienow) { int family = addr->sa.sa_family; int fd, rc, opt = 1; if ((fd = socket(family, type, 0)) == -1) { int port, errsave; char *s; /* No error if the kernel just doesn't support this IP flavour */ if (errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT || errno == EINVAL) return -1; err: errsave = 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 = errsave; /* Failure to bind addresses given by --listen-address at this point because there's no interface with the address is OK if we're doing bind-dynamic. If/when an interface is created with the relevant address we'll notice and attempt to bind it then. This is in the generic error path so we close the socket, but EADDRNOTAVAIL is only a possible error from bind() When a new address is created and we call this code again (dienow == 0) there may still be configured addresses when don't exist, (consider >1 --listen-address, when the first is created, the second will still be missing) so we suppress EADDRNOTAVAIL even in that case to avoid confusing log entries. */ if (!option_bool(OPT_CLEVERBIND) || errno != EADDRNOTAVAIL) { if (dienow) 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; if (family == AF_INET6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1) goto err; if ((rc = bind(fd, (struct sockaddr *)addr, sa_len(addr))) == -1) goto err; if (type == SOCK_STREAM) { #ifdef TCP_FASTOPEN int qlen = 5; setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)); #endif if (listen(fd, TCP_BACKLOG) == -1) goto err; } else if (family == AF_INET) { if (!option_bool(OPT_NOWILD)) { #if defined(HAVE_LINUX_NETWORK) 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 || setsockopt(fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1) goto err; #endif } } else if (!set_ipv6pktinfo(fd)) goto err; return fd; } 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 0; } /* Find the interface on which a TCP connection arrived, if possible, or zero otherwise. */ int tcp_interface(int fd, int af) { (void)fd; /* suppress potential unused warning */ (void)af; /* suppress potential unused warning */ int if_index = 0; #ifdef HAVE_LINUX_NETWORK int opt = 1; struct cmsghdr *cmptr; struct msghdr msg; socklen_t len; /* use mshdr so that the CMSDG_* macros are available */ msg.msg_control = daemon->packet; msg.msg_controllen = len = daemon->packet_buff_sz; /* we overwrote the buffer... */ daemon->srv_save = NULL; if (af == AF_INET) { if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 && getsockopt(fd, IPPROTO_IP, IP_PKTOPTIONS, msg.msg_control, &len) != -1) { msg.msg_controllen = len; 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; } } } 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, &len) != -1) { msg.msg_controllen = len; 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 /* Linux */ return if_index; } 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, dienow); tcpfd = make_sock(addr, SOCK_STREAM, dienow); } #ifdef HAVE_TFTP if (do_tftp) { if (addr->sa.sa_family == AF_INET) { /* 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, dienow); addr->in.sin_port = save; } else { short save = addr->in6.sin6_port; addr->in6.sin6_port = htons(TFTP_PORT); tftpfd = make_sock(addr, SOCK_DGRAM, dienow); addr->in6.sin6_port = save; } } #endif if (fd != -1 || tcpfd != -1 || tftpfd != -1) { l = safe_malloc(sizeof(struct listener)); l->next = NULL; l->fd = fd; l->tcpfd = tcpfd; l->tftpfd = tftpfd; l->addr = *addr; l->used = 1; l->iface = NULL; } return l; } void create_wildcard_listeners(void) { union mysockaddr addr; struct listener *l, *l6; memset(&addr, 0, sizeof(addr)); #ifdef HAVE_SOCKADDR_SA_LEN addr.in.sin_len = sizeof(addr.in); #endif addr.in.sin_family = AF_INET; addr.in.sin_addr.s_addr = INADDR_ANY; addr.in.sin_port = htons(daemon->port); l = create_listeners(&addr, !!option_bool(OPT_TFTP), 1); memset(&addr, 0, sizeof(addr)); #ifdef HAVE_SOCKADDR_SA_LEN addr.in6.sin6_len = sizeof(addr.in6); #endif 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 = l6; else l = l6; daemon->listeners = l; } static struct listener *find_listener(union mysockaddr *addr) { struct listener *l; for (l = daemon->listeners; l; l = l->next) if (sockaddr_isequal(&l->addr, addr)) return l; return NULL; } void create_bound_listeners(int dienow) { struct listener *new; struct irec *iface; struct iname *if_tmp; struct listener *existing; for (iface = daemon->interfaces; iface; iface = iface->next) if (!iface->done && !iface->dad && iface->found) { existing = find_listener(&iface->addr); if (existing) { iface->done = 1; existing->used++; /* increase usage counter */ } else if ((new = create_listeners(&iface->addr, iface->tftp_ok, dienow))) { new->iface = iface; new->next = daemon->listeners; daemon->listeners = new; iface->done = 1; /* Don't log the initial set of listen addresses created at startup, since this is happening before the logging system is initialised and the sign-on printed. */ if (!dienow) { int port = prettyprint_addr(&iface->addr, daemon->addrbuff); my_syslog(LOG_DEBUG|MS_DEBUG, _("listening on %s(#%d): %s port %d"), iface->name, iface->index, daemon->addrbuff, port); } } } /* 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->flags & INAME_USED) && (new = create_listeners(&if_tmp->addr, !!option_bool(OPT_TFTP), dienow))) { new->next = daemon->listeners; daemon->listeners = new; if (!dienow) { int port = prettyprint_addr(&if_tmp->addr, daemon->addrbuff); my_syslog(LOG_DEBUG|MS_DEBUG, _("listening on %s port %d"), daemon->addrbuff, port); } } } /* 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); } } } 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_wild_labels(void) { struct irec *iface; for (iface = daemon->interfaces; iface; iface = iface->next) if (iface->found && iface->name && iface->label) my_syslog(LOG_WARNING, _("warning: using interface %s instead"), iface->name); } 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->dhcp6_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 = errno; inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr); if (daemon->doing_dhcp6 && setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) err = errno; inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr); if (daemon->doing_ra && setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) err = errno; if (err) { char *s = _("interface %s failed to join DHCPv6 multicast group: %s"); errno = err; #ifdef HAVE_LINUX_NETWORK if (errno == ENOMEM) my_syslog(LOG_ERR, _("try increasing /proc/sys/net/core/optmem_max")); #endif if (dienow) die(s, iface->name, EC_BADNET); else my_syslog(LOG_ERR, s, iface->name, strerror(errno)); } } } } #endif int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp) { union mysockaddr addr_copy = *addr; unsigned short port; int tries = 1; unsigned short ports_avail = 1; if (addr_copy.sa.sa_family == AF_INET) port = addr_copy.in.sin_port; else port = addr_copy.in6.sin6_port; /* cannot set source _port_ for TCP connections. */ if (is_tcp) port = 0; else if (port == 0 && daemon->max_port != 0) { /* Bind a random port within the range given by min-port and max-port if either or both are set. Otherwise use the OS's random ephemeral port allocation by leaving port == 0 and tries == 1 */ ports_avail = daemon->max_port - daemon->min_port + 1; tries = (ports_avail < SMALL_PORT_RANGE) ? ports_avail : 100; port = htons(daemon->min_port + (rand16() % ports_avail)); } while (1) { /* elide bind() call if it's to port 0, address 0 */ if (addr_copy.sa.sa_family == AF_INET) { if (port == 0 && addr_copy.in.sin_addr.s_addr == 0) break; addr_copy.in.sin_port = port; } else { if (port == 0 && IN6_IS_ADDR_UNSPECIFIED(&addr_copy.in6.sin6_addr)) break; addr_copy.in6.sin6_port = port; } if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) != -1) break; if (errno != EADDRINUSE && errno != EACCES) return 0; if (--tries == 0) return 0; /* For small ranges, do a systematic search, not a random one. */ if (ports_avail < SMALL_PORT_RANGE) { unsigned short hport = ntohs(port); if (hport++ == daemon->max_port) hport = daemon->min_port; port = htons(hport); } else port = htons(daemon->min_port + (rand16() % ports_avail)); } if (!is_tcp && ifindex > 0) { #if defined(IP_UNICAST_IF) if (addr_copy.sa.sa_family == AF_INET) { uint32_t ifindex_opt = htonl(ifindex); return setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_opt, sizeof(ifindex_opt)) == 0; } #endif #if defined (IPV6_UNICAST_IF) if (addr_copy.sa.sa_family == AF_INET6) { uint32_t ifindex_opt = htonl(ifindex); return setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_opt, sizeof(ifindex_opt)) == 0; } #endif } (void)intname; /* suppress potential unused warning */ #if defined(SO_BINDTODEVICE) if (intname[0] != 0 && setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, IF_NAMESIZE) == -1) return 0; #endif return 1; } static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname, unsigned int ifindex) { struct serverfd *sfd; int errsave; int opt = 1; /* when using random ports, servers which would otherwise use the INADDR_ANY/port0 socket have sfd set to NULL, this is anything without an explictly set source port. */ if (!daemon->osport) { errno = 0; if (addr->sa.sa_family == AF_INET && addr->in.sin_port == htons(0)) return NULL; if (addr->sa.sa_family == AF_INET6 && addr->in6.sin6_port == htons(0)) return NULL; } /* may have a suitable one already */ for (sfd = daemon->sfds; sfd; sfd = sfd->next ) if (ifindex == sfd->ifindex && sockaddr_isequal(&sfd->source_addr, addr) && strcmp(intname, sfd->interface) == 0) return sfd; /* need to make a new one. */ errno = ENOMEM; /* in case malloc fails. */ if (!(sfd = whine_malloc(sizeof(struct serverfd)))) return NULL; if ((sfd->fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0)) == -1) { free(sfd); return NULL; } if ((addr->sa.sa_family == AF_INET6 && setsockopt(sfd->fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1) || !local_bind(sfd->fd, addr, intname, ifindex, 0) || !fix_fd(sfd->fd)) { errsave = errno; /* save error from bind/setsockopt. */ close(sfd->fd); free(sfd); errno = errsave; return NULL; } safe_strncpy(sfd->interface, intname, sizeof(sfd->interface)); sfd->source_addr = *addr; sfd->next = daemon->sfds; sfd->ifindex = ifindex; sfd->preallocated = 0; daemon->sfds = sfd; return sfd; } /* create upstream sockets during startup, before root is dropped which may be needed this allows query_port to be a low port and interface binding */ void pre_allocate_sfds(void) { struct server *srv; struct serverfd *sfd; if (daemon->query_port != 0) { union mysockaddr addr; memset(&addr, 0, sizeof(addr)); addr.in.sin_family = AF_INET; addr.in.sin_addr.s_addr = INADDR_ANY; addr.in.sin_port = htons(daemon->query_port); #ifdef HAVE_SOCKADDR_SA_LEN addr.in.sin_len = sizeof(struct sockaddr_in); #endif if ((sfd = allocate_sfd(&addr, "", 0))) sfd->preallocated = 1; memset(&addr, 0, sizeof(addr)); addr.in6.sin6_family = AF_INET6; addr.in6.sin6_addr = in6addr_any; addr.in6.sin6_port = htons(daemon->query_port); #ifdef HAVE_SOCKADDR_SA_LEN addr.in6.sin6_len = sizeof(struct sockaddr_in6); #endif if ((sfd = allocate_sfd(&addr, "", 0))) sfd->preallocated = 1; } for (srv = daemon->servers; srv; srv = srv->next) if (!allocate_sfd(&srv->source_addr, srv->interface, srv->ifindex) && errno != 0 && option_bool(OPT_NOWILD)) { (void)prettyprint_addr(&srv->source_addr, daemon->namebuff); if (srv->interface[0] != 0) { strcat(daemon->namebuff, " "); strcat(daemon->namebuff, srv->interface); } die(_("failed to bind server socket for %s: %s"), daemon->namebuff, EC_BADNET); } } void check_servers(int no_loop_check) { struct irec *iface; struct server *serv; struct serverfd *sfd, *tmp, **up; int port = 0, count; int locals = 0; #ifdef HAVE_LOOP if (!no_loop_check) loop_send_probes(); #endif /* clear all marks. */ mark_servers(0); /* interface may be new since startup */ if (!option_bool(OPT_NOWILD)) enumerate_interfaces(0); /* don't garbage collect pre-allocated sfds. */ for (sfd = daemon->sfds; sfd; sfd = sfd->next) sfd->used = sfd->preallocated; for (count = 0, serv = daemon->servers; serv; serv = serv->next) { #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID)) { if (!(serv->flags & SERV_FOR_NODOTS)) serv->flags |= SERV_DO_DNSSEC; /* Disable DNSSEC validation when using server=/domain/.... servers unless there's a configured trust anchor. */ if (strlen(serv->domain) != 0) { struct ds_config *ds; char *domain = serv->domain; /* .example.com is valid */ while (*domain == '.') domain++; for (ds = daemon->ds; ds; ds = ds->next) if (ds->name[0] != 0 && hostname_isequal(domain, ds->name)) break; if (!ds) serv->flags &= ~SERV_DO_DNSSEC; } } #endif port = prettyprint_addr(&serv->addr, daemon->namebuff); /* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */ if (serv->addr.sa.sa_family == AF_INET && serv->addr.in.sin_addr.s_addr == 0) { serv->flags |= SERV_MARK; continue; } for (iface = daemon->interfaces; iface; iface = iface->next) if (sockaddr_isequal(&serv->addr, &iface->addr)) break; if (iface) { my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff); serv->flags |= SERV_MARK; continue; } /* Do we need a socket set? */ if (!serv->sfd && !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface, serv->ifindex)) && errno != 0) { my_syslog(LOG_WARNING, _("ignoring nameserver %s - cannot make/bind socket: %s"), daemon->namebuff, strerror(errno)); serv->flags |= SERV_MARK; continue; } if (serv->sfd) serv->sfd->used = 1; if (count == SERVERS_LOGGED) my_syslog(LOG_INFO, _("more servers are defined but not logged")); if (++count > SERVERS_LOGGED) continue; if (strlen(serv->domain) != 0 || (serv->flags & SERV_FOR_NODOTS)) { char *s1, *s2, *s3 = "", *s4 = ""; #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC)) s3 = _("(no DNSSEC)"); #endif if (serv->flags & SERV_FOR_NODOTS) s1 = _("unqualified"), s2 = _("names"); else if (strlen(serv->domain) == 0) s1 = _("default"), s2 = ""; else s1 = _("domain"), s2 = serv->domain, s4 = (serv->flags & SERV_WILDCARD) ? "*" : ""; my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s%s %s"), daemon->namebuff, port, s1, s4, s2, s3); } #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); } for (count = 0, serv = daemon->local_domains; serv; serv = serv->next) { if (++count > SERVERS_LOGGED) continue; if ((serv->flags & SERV_LITERAL_ADDRESS) && !(serv->flags & (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)) && strlen(serv->domain)) { count--; if (++locals <= LOCALS_LOGGED) my_syslog(LOG_INFO, _("using only locally-known addresses for %s"), serv->domain); } else if (serv->flags & SERV_USE_RESOLV) my_syslog(LOG_INFO, _("using standard nameservers for %s"), serv->domain); } if (locals > LOCALS_LOGGED) my_syslog(LOG_INFO, _("using %d more local addresses"), locals - LOCALS_LOGGED); if (count - 1 > SERVERS_LOGGED) my_syslog(LOG_INFO, _("using %d more nameservers"), count - SERVERS_LOGGED - 1); /* Remove unused sfds */ for (sfd = daemon->sfds, up = &daemon->sfds; sfd; sfd = tmp) { tmp = sfd->next; if (!sfd->used) { *up = sfd->next; close(sfd->fd); free(sfd); } else up = &sfd->next; } cleanup_servers(); /* remove servers we just deleted. */ build_server_array(); } /* Return zero if no servers found, in that case we keep polling. This is a protection against an update-time/write race on resolv.conf */ int reload_servers(char *fname) { FILE *f; char *line; int gotone = 0; /* buff happens to be MAXDNAME long... */ if (!(f = fopen(fname, "r"))) { my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno)); return 0; } mark_servers(SERV_FROM_RESOLV); while ((line = fgets(daemon->namebuff, MAXDNAME, f))) { union mysockaddr addr, source_addr; char *token = strtok(line, " \t\n\r"); if (!token) continue; if (strcmp(token, "nameserver") != 0 && strcmp(token, "server") != 0) continue; if (!(token = strtok(NULL, " \t\n\r"))) continue; memset(&addr, 0, sizeof(addr)); memset(&source_addr, 0, sizeof(source_addr)); if (inet_pton(AF_INET, token, &addr.in.sin_addr) > 0) { #ifdef HAVE_SOCKADDR_SA_LEN source_addr.in.sin_len = addr.in.sin_len = sizeof(source_addr.in); #endif source_addr.in.sin_family = addr.in.sin_family = AF_INET; addr.in.sin_port = htons(NAMESERVER_PORT); source_addr.in.sin_addr.s_addr = INADDR_ANY; source_addr.in.sin_port = htons(daemon->query_port); } 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); #endif 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; } add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL, NULL); gotone = 1; } fclose(f); cleanup_servers(); return gotone; } /* Called when addresses are added or deleted from an interface */ void newaddress(time_t now) { struct dhcp_relay *relay; (void)now; 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); #ifdef HAVE_DHCP /* clear cache of subnet->relay index */ for (relay = daemon->relay4; relay; relay = relay->next) relay->iface_index = 0; #endif #ifdef HAVE_DHCP6 if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra) join_multicast(0); if (daemon->doing_dhcp6 || daemon->doing_ra) dhcp_construct_contexts(now); if (daemon->doing_dhcp6) lease_find_interfaces(now); for (relay = daemon->relay6; relay; relay = relay->next) relay->iface_index = 0; #endif } dnsmasq-2.91/src/forward.c0000664000175000017500000030764314765043257013726 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" static struct frec *get_new_frec(time_t now, struct server *serv, int force); static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int flags, int flagmask); #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 have_mark, unsigned int mark, int *keycount, int *validatecount); #endif static unsigned short get_id(void); static void free_frec(struct frec *f); static void query_full(time_t now, char *domain); /* Send a UDP packet with its source address set as "source" unless nowild is true, when we just send it with the kernel default */ int send_from(int fd, int nowild, char *packet, size_t len, union mysockaddr *to, union all_addr *source, unsigned int iface) { struct msghdr msg; struct iovec iov[1]; union { struct cmsghdr align; /* this ensures alignment */ #if defined(HAVE_LINUX_NETWORK) char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; #elif defined(IP_SENDSRCADDR) char control[CMSG_SPACE(sizeof(struct in_addr))]; #endif char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; } control_u; iov[0].iov_base = packet; iov[0].iov_len = len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; msg.msg_name = to; msg.msg_namelen = sa_len(to); msg.msg_iov = iov; msg.msg_iovlen = 1; if (!nowild) { struct cmsghdr *cmptr = msg.msg_control = &control_u.align; /* alignment padding passed to the kernel should not be uninitialised. */ memset(&control_u, 0, sizeof(control_u)); if (to->sa.sa_family == AF_INET) { #if defined(HAVE_LINUX_NETWORK) struct in_pktinfo *p = (struct in_pktinfo *)CMSG_DATA(cmptr);; p->ipi_ifindex = 0; p->ipi_spec_dst = source->addr4; msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); cmptr->cmsg_level = IPPROTO_IP; cmptr->cmsg_type = IP_PKTINFO; #elif defined(IP_SENDSRCADDR) msg.msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); memcpy(CMSG_DATA(cmptr), &(source->addr4), sizeof(source->addr4)); cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); cmptr->cmsg_level = IPPROTO_IP; cmptr->cmsg_type = IP_SENDSRCADDR; #endif } else { struct in6_pktinfo *p = (struct in6_pktinfo *)CMSG_DATA(cmptr); p->ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */ p->ipi6_addr = source->addr6; msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); cmptr->cmsg_type = daemon->v6pktinfo; cmptr->cmsg_level = IPPROTO_IPV6; } } while (retry_send(sendmsg(fd, &msg, 0))); if (errno != 0) { #ifdef HAVE_LINUX_NETWORK /* If interface is still in DAD, EINVAL results - ignore that. */ if (errno != EINVAL) my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno)); #endif return 0; } return 1; } #ifdef HAVE_CONNTRACK static void set_outgoing_mark(struct frec *forward, int fd) { /* Copy connection mark of incoming query to outgoing connection. */ unsigned int mark; if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark)) setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); } #endif static void log_query_mysockaddr(unsigned int flags, char *name, union mysockaddr *addr, char *arg, unsigned short type) { if (addr->sa.sa_family == AF_INET) { if (flags & F_SERVER) type = ntohs(addr->in.sin_port); log_query(flags | F_IPV4, name, (union all_addr *)&addr->in.sin_addr, arg, type); } else { if (flags & F_SERVER) type = ntohs(addr->in6.sin6_port); log_query(flags | F_IPV6, name, (union all_addr *)&addr->in6.sin6_addr, arg, type); } } static void server_send(struct server *server, int fd, const void *header, size_t plen, int flags) { while (retry_send(sendto(fd, header, plen, flags, &server->addr.sa, sa_len(&server->addr)))); } static int domain_no_rebind(char *domain) { struct rebind_domain *rbd; size_t tlen, dlen = strlen(domain); char *dots = strchr(domain, '.'); /* Match whole labels only. Empty domain matches no dots (any single label) */ for (rbd = daemon->no_rebind; rbd; rbd = rbd->next) { if (dlen >= (tlen = strlen(rbd->domain)) && hostname_isequal(rbd->domain, &domain[dlen - tlen]) && (dlen == tlen || domain[dlen - tlen - 1] == '.')) return 1; if (tlen == 0 && !dots) return 1; } return 0; } static void forward_query(int udpfd, union mysockaddr *udpaddr, union all_addr *dst_addr, unsigned int dst_iface, struct dns_header *header, size_t plen, size_t replylimit, time_t now, struct frec *forward, unsigned int fwd_flags, int fast_retry) { unsigned int flags = 0; int is_dnssec = forward && (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)); struct server *master; unsigned int gotname; int old_src = 0, old_reply = 0; int first, last, start = 0; int forwarded = 0; int ede = EDE_UNSET; unsigned short rrtype; gotname = extract_request(header, plen, daemon->namebuff, &rrtype); /* Check for retry on existing query. FREC_DNSKEY and FREC_DS_QUERY are never set in flags, so the test below ensures that no frec created for internal DNSSEC query can be returned here. Similarly FREC_NO_CACHE is never set in flags, so a query which is contigent on a particular source address EDNS0 option will never be matched. */ if (forward) { old_src = 1; old_reply = 1; fwd_flags = forward->flags; } else if (gotname && (forward = lookup_frec(daemon->namebuff, C_IN, (int)rrtype, -1, fwd_flags, FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION | FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE))) { struct frec_src *src; unsigned int casediff = 0; unsigned int *bitvector = NULL; unsigned short id = ntohs(header->id); /* Retrieve the id from the new query before we overwrite it. */ /* Get the case-scambled version of the query to resend. This is important because we may fall through below and forward the query in the packet buffer again and we want to use the same case scrambling as the first time. */ blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); plen = forward->stash_len; for (src = &forward->frec_src; src; src = src->next) if (src->orig_id == id && sockaddr_isequal(&src->source, udpaddr)) break; if (src) { old_src = 1; /* If a query is retried, use the log_id for the retry when logging the answer. */ src->log_id = daemon->log_id; } else { /* Existing query, but from new source, just add this client to the list that will get the reply.*/ /* Note whine_malloc() zeros memory. */ if (!daemon->free_frec_src && daemon->frec_src_count < daemon->ftabsize && (daemon->free_frec_src = whine_malloc(sizeof(struct frec_src)))) { daemon->frec_src_count++; daemon->free_frec_src->next = NULL; daemon->free_frec_src->encode_bigmap = NULL; } /* If we've been spammed with many duplicates, return REFUSED. */ if (!daemon->free_frec_src) { query_full(now, NULL); /* This is tricky; if we're blasted with the same query over and over, we'll end up taking this path each time and never resetting until the frec gets deleted by aging followed by the receipt of a different query. This is a bit of a DoS vuln. Avoid by explicitly deleting the frec once it expires. */ if (difftime(now, forward->time) >= TIMEOUT) free_frec(forward); goto reply; } /* Find a bitmap of case differences between the query sent upstream and this one, so we can reply to each query with the correct case pattern. Since we need this to get back the exact case pattern of each query when doing query combining, we have to handle the (rare) case that there are case differences beyond the first 32 letters. If that happens we have to allocate memory to save it, and the casediff variable holds the length of that array. Mismatches beyond 32 letters are rare because most queries are all lowercase and we only scramble the first 32 letters for security reasons. Note the two names are guaranteed to be the same length and differ only in the case of letters at this point. The original query we sent is now in packet buffer and the query name in the new instance is on daemon->namebuff. */ if (extract_request(header, forward->stash_len, daemon->workspacename, NULL)) { unsigned int i, gobig = 0; char *s1, *s2; #define BITS_IN_INT (sizeof(unsigned int) * 8) big_redo: for (s1 = daemon->namebuff, s2 = daemon->workspacename, i = 0; *s1; s1++, s2++) { char c1 = *s1, c2 = *s2; if ((c1 >= 'a' && c1 <= 'z') || (c1 >= 'A' && c1 <= 'Z')) { if ((c1 & 0x20) ^ (c2 & 0x20)) { if (bitvector) bitvector[i/BITS_IN_INT] |= 1<<(i%BITS_IN_INT); else if (i >= BITS_IN_INT) gobig = 1; /* More than 32 */ else casediff |= 1<free_frec_src; daemon->free_frec_src = src->next; src->next = forward->frec_src.next; forward->frec_src.next = src; src->orig_id = id; src->source = *udpaddr; src->dest = *dst_addr; src->log_id = daemon->log_id; src->iface = dst_iface; src->fd = udpfd; src->encode_bitmap = casediff; src->encode_bigmap = bitvector; src->udp_pkt_size = (unsigned short)replylimit; /* closely spaced identical queries cannot be a try and a retry, so it's safe to wait for the reply from the first without forwarding the second. */ if (difftime(now, forward->time) < 2) return; } /* use our id when resending */ header->id = ntohs(forward->new_id); } /* new query */ if (!forward) { unsigned char *p; if (OPCODE(header) != QUERY) { flags = F_RCODE; goto reply; } else if (!gotname) { /* If the query is malformed, we can't forward it because we can't recognise the answer. */ flags = 0; ede = EDE_INVALID_DATA; goto reply; } if (lookup_domain(daemon->namebuff, gotname, &first, &last)) flags = is_local_answer(now, first, daemon->namebuff); else { /* no available server. */ ede = EDE_NOT_READY; flags = 0; } /* don't forward A or AAAA queries for simple names, except the empty name */ if (!flags && option_bool(OPT_NODOTS_LOCAL) && (gotname & (F_IPV4 | F_IPV6)) && !strchr(daemon->namebuff, '.') && strlen(daemon->namebuff) != 0) flags = check_for_local_domain(daemon->namebuff, now) ? F_NOERR : F_NXDOMAIN; /* Configured answer. */ if (flags || ede == EDE_NOT_READY) goto reply; master = daemon->serverarray[first]; if (!(forward = get_new_frec(now, master, 0))) goto reply; /* table full - flags == 0, return REFUSED */ forward->flags = fwd_flags; #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID) && (master->flags & SERV_DO_DNSSEC)) { plen = add_do_bit(header, plen, ((unsigned char *) header) + daemon->edns_pktsz); /* 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; } #endif /* Do these before saving query. */ forward->frec_src.orig_id = ntohs(header->id); forward->new_id = get_id(); header->id = ntohs(forward->new_id); forward->frec_src.encode_bitmap = (!option_bool(OPT_NO_0x20) && option_bool(OPT_DO_0x20)) ? rand32() : 0; forward->frec_src.encode_bigmap = NULL; p = (unsigned char *)(header+1); if (!extract_name(header, plen, &p, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1)) goto reply; /* Keep copy of query for retries and move to TCP */ if (!(forward->stash = blockdata_alloc((char *)header, plen))) { free_frec(forward); goto reply; /* no mem. return REFUSED */ } forward->stash_len = plen; forward->frec_src.log_id = daemon->log_id; forward->frec_src.source = *udpaddr; forward->frec_src.dest = *dst_addr; forward->frec_src.iface = dst_iface; forward->frec_src.next = NULL; forward->frec_src.fd = udpfd; forward->frec_src.udp_pkt_size = (unsigned short)replylimit; forward->forwardall = 0; if (domain_no_rebind(daemon->namebuff)) forward->flags |= FREC_NOREBIND; #ifdef HAVE_DNSSEC forward->work_counter = daemon->limit[LIMIT_WORK]; forward->validate_counter = daemon->limit[LIMIT_CRYPTO]; #endif start = first; if (option_bool(OPT_ALL_SERVERS)) forward->forwardall = 1; if (!option_bool(OPT_ORDER)) { if (master->forwardcount++ > FORWARD_TEST || difftime(now, master->forwardtime) > FORWARD_TIME || master->last_server == -1) { master->forwardtime = now; master->forwardcount = 0; forward->forwardall = 1; } else start = master->last_server; } } else { #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...... */ while (forward->blocking_query) forward = forward->blocking_query; /* Don't retry if we've already sent it via TCP. */ if (forward->flags & FREC_GONE_TO_TCP) return; if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) { /* log_id should match previous DNSSEC query. */ daemon->log_display_id = forward->frec_src.log_id; blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); plen = forward->stash_len; /* get query for logging. */ gotname = extract_request(header, plen, daemon->namebuff, NULL); /* Find suitable servers: should never fail. */ if (!filter_servers(forward->sentto->arrayposn, F_DNSSECOK, &first, &last)) return; is_dnssec = 1; forward->forwardall = 1; } else #endif { /* retry on existing query, from original source. Send to all available servers */ if (udpfd == -1 && !fast_retry) forward->sentto->failed_queries++; else forward->sentto->retrys++; if (!filter_servers(forward->sentto->arrayposn, F_SERVER, &first, &last)) goto reply; master = daemon->serverarray[first]; /* Forward to all available servers on retry of query from same host. */ if (!option_bool(OPT_ORDER) && old_src && !fast_retry) forward->forwardall = 1; else { start = forward->sentto->arrayposn; if (option_bool(OPT_ORDER) && !fast_retry) { /* In strict order mode, there must be a server later in the list left to send to, otherwise without the forwardall mechanism, code further on will cycle around the list forwever if they all return REFUSED. If at the last, give up. Note that we can get here EITHER because a client retried, or an upstream server returned REFUSED. The above only applied in the later case. For client retries, keep trying the last server.. */ if (++start == last) { if (old_reply) goto reply; else start--; } } } } } if (forward->forwardall) start = first; forwarded = 0; /* Advertise the size of UDP reply we can accept. */ plen = add_pseudoheader(header, plen, (unsigned char *)(header + daemon->edns_pktsz), 0, NULL, 0, 0, 0); /* check for send errors here (no route to host) if we fail to send to all nameservers, send back an error packet straight away (helps modem users when offline) */ while (1) { int fd; struct server *srv = daemon->serverarray[start]; if ((fd = allocate_rfd(&forward->rfds, srv)) != -1) { #ifdef HAVE_CONNTRACK /* Copy connection mark of incoming query to outgoing connection. */ if (option_bool(OPT_CONNTRACK)) set_outgoing_mark(forward, fd); #endif if (retry_send(sendto(fd, (char *)header, plen, 0, &srv->addr.sa, sa_len(&srv->addr)))) continue; if (errno == 0) { #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_UP_QUERY, (void *)header, plen, NULL, &srv->addr, fd); #endif /* Keep info in case we want to re-send this packet */ daemon->srv_save = srv; daemon->packet_len = plen; daemon->fd_save = fd; if (!gotname) strcpy(daemon->namebuff, "query"); if (!(forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))) log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff, &srv->addr, NULL, 0); #ifdef HAVE_DNSSEC else log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, daemon->namebuff, &srv->addr, (forward->flags & FREC_DNSKEY_QUERY) ? "dnssec-retry[DNSKEY]" : "dnssec-retry[DS]", 0); #endif srv->queries++; forwarded = 1; forward->sentto = srv; if (!forward->forwardall) break; forward->forwardall++; } } if (++start == last) break; } if (forwarded || is_dnssec) { daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++; forward->forward_timestamp = dnsmasq_milliseconds(); return; } /* could not send on, prepare to return */ header->id = htons(forward->frec_src.orig_id); free_frec(forward); /* cancel */ ede = EDE_NETERR; reply: if (udpfd != -1) { if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, (char *)(header + replylimit), first, last, ede))) return; if (fwd_flags & FREC_HAS_PHEADER) { u16 swap = htons((u16)ede); if (ede != EDE_UNSET) plen = add_pseudoheader(header, plen, (unsigned char *)(header + replylimit), EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, 0, 0); else plen = add_pseudoheader(header, plen, (unsigned char *)(header + replylimit), 0, NULL, 0, 0, 0); } #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) if (option_bool(OPT_CMARK_ALST_EN)) { unsigned int mark; int have_mark = get_incoming_mark(udpaddr, dst_addr, /* istcp: */ 0, &mark); if (have_mark && ((u32)mark & daemon->allowlist_mask)) report_addresses(header, plen, mark); } #endif #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_REPLY, (void *)header, plen, NULL, udpaddr, udpfd); #endif send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface); } daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++; return; } /* Check if any frecs need to do a retry, and action that if so. Return time in milliseconds until he next retry will be required, or -1 if none. */ int fast_retry(time_t now) { struct frec *f; int ret = -1; if (daemon->fast_retry_time != 0) { u32 millis = dnsmasq_milliseconds(); for (f = daemon->frec_list; f; f = f->next) if (f->sentto && difftime(now, f->time) < daemon->fast_retry_timeout) { #ifdef HAVE_DNSSEC if (f->blocking_query || (f->flags & FREC_GONE_TO_TCP)) continue; #endif /* t is milliseconds since last query sent. */ int to_run, t = (int)(millis - f->forward_timestamp); if (t < f->forward_delay) to_run = f->forward_delay - t; else { struct dns_header *header = (struct dns_header *)daemon->packet; /* packet buffer overwritten */ daemon->srv_save = NULL; blockdata_retrieve(f->stash, f->stash_len, (void *)header); daemon->log_display_id = f->frec_src.log_id; daemon->log_source_addr = NULL; forward_query(-1, NULL, NULL, 0, header, f->stash_len, 0, now, f, 0, 1); to_run = f->forward_delay = 2 * f->forward_delay; } if (ret == -1 || ret > to_run) ret = to_run; } } return ret; } static struct ipsets *domain_find_sets(struct ipsets *setlist, const char *domain) { /* Similar algorithm to search_servers. */ struct ipsets *ipset_pos, *ret = NULL; unsigned int namelen = strlen(domain); unsigned int matchlen = 0; for (ipset_pos = setlist; ipset_pos; ipset_pos = ipset_pos->next) { unsigned int domainlen = strlen(ipset_pos->domain); const char *matchstart = domain + namelen - domainlen; if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) && (domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) && domainlen >= matchlen) { matchlen = domainlen; ret = ipset_pos; } } return ret; } 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, union mysockaddr *query_source, unsigned char *limit, int ede) { unsigned char *pheader, *sizep; struct ipsets *ipsets = NULL, *nftsets = NULL; int is_sign; unsigned int rcode = RCODE(header); size_t plen; (void)ad_reqd; (void)do_bit; #ifdef HAVE_IPSET if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL)) ipsets = domain_find_sets(daemon->ipsets, daemon->namebuff); #endif #ifdef HAVE_NFTSET if (daemon->nftsets && extract_request(header, n, daemon->namebuff, NULL)) nftsets = domain_find_sets(daemon->nftsets, daemon->namebuff); #endif if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL))) { /* Get extended RCODE. */ rcode |= sizep[2] << 4; if (option_bool(OPT_CLIENT_SUBNET) && !check_source(header, plen, pheader, query_source)) { my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch")); return 0; } if (!is_sign) { if (added_pheader) { /* client didn't send EDNS0, we added one, strip it off before returning answer. */ rrfilter(header, &n, RRFILTER_EDNS0); pheader = NULL; } else { /* Advertise our max UDP packet to the client. */ PUTSHORT(daemon->edns_pktsz, sizep); #ifdef HAVE_DNSSEC /* If the client didn't set the do bit, but we did, reset it. */ if (option_bool(OPT_DNSSEC_VALID) && !do_bit) { unsigned short flags; sizep += 2; /* skip RCODE */ GETSHORT(flags, sizep); flags &= ~0x8000; sizep -= 2; PUTSHORT(flags, sizep); } #endif } } } /* RFC 4035 sect 4.6 para 3 */ if (!is_sign && !option_bool(OPT_DNSSEC_PROXY)) header->hb4 &= ~HB4_AD; header->hb4 |= HB4_RA; /* recursion if available */ if (OPCODE(header) != QUERY) return resize_packet(header, n, pheader, plen); if (rcode != NOERROR && rcode != NXDOMAIN) { union all_addr a; a.log.rcode = rcode; a.log.ede = ede; log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL, 0); return resize_packet(header, n, pheader, plen); } /* Complain loudly if the upstream server is non-recursive. */ if (!(header->hb4 & HB4_RA) && rcode == NOERROR && server && !(server->flags & SERV_WARNED_RECURSIVE)) { (void)prettyprint_addr(&server->addr, daemon->namebuff); my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff); if (!option_bool(OPT_LOG)) server->flags |= SERV_WARNED_RECURSIVE; } if (header->hb3 & HB3_TC) log_query(F_UPSTREAM, NULL, NULL, "truncated", 0); else if (!bogusanswer || (header->hb4 & HB4_CD)) { if (rcode == NXDOMAIN && extract_request(header, n, daemon->namebuff, NULL) && (check_for_local_domain(daemon->namebuff, now) || lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL))) { /* if we forwarded a query for a locally known name (because it was for an unknown type) and the answer is NXDOMAIN, convert that to NODATA, since we know that the domain exists, even if upstream doesn't */ header->hb3 |= HB3_AA; SET_RCODE(header, NOERROR); cache_secure = 0; } if (daemon->doctors && do_doctor(header, n, daemon->namebuff)) cache_secure = 0; /* check_for_bogus_wildcard() does it's own caching, so don't call extract_addresses() if it triggers. */ if (daemon->bogus_addr && rcode != NXDOMAIN && check_for_bogus_wildcard(header, n, daemon->namebuff, now)) { header->ancount = htons(0); header->nscount = htons(0); header->arcount = htons(0); SET_RCODE(header, NXDOMAIN); header->hb3 &= ~HB3_AA; cache_secure = 0; ede = EDE_BLOCKED; } else { int rc = extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure); if (rc != 0) { header->ancount = htons(0); header->nscount = htons(0); header->arcount = htons(0); cache_secure = 0; } if (rc == 1) { my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff); ede = EDE_BLOCKED; } if (rc == 2) { /* extract_addresses() found a malformed answer. */ SET_RCODE(header, SERVFAIL); ede = EDE_OTHER; } } if (RCODE(header) == NOERROR && rrfilter(header, &n, RRFILTER_CONF) > 0) ede = EDE_FILTERED; } #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID)) { if (bogusanswer) { if (!(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG)) { /* Bogus reply, turn into SERVFAIL */ SET_RCODE(header, SERVFAIL); header->ancount = htons(0); header->nscount = htons(0); header->arcount = htons(0); } } else if (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) rrfilter(header, &n, RRFILTER_DNSSEC); } #endif /* the code above can elide sections of the packet. Find the new length here and put back pseudoheader if it was removed. */ n = resize_packet(header, n, pheader, plen); if (pheader && ede != EDE_UNSET) { u16 swap = htons((u16)ede); n = add_pseudoheader(header, n, limit, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1); } if (RCODE(header) == NXDOMAIN) server->nxdomain_replies++; return n; } #ifdef HAVE_DNSSEC static void dnssec_validate(struct frec *forward, struct dns_header *header, ssize_t plen, int status, time_t now) { struct frec *orig; int log_resource = 0; daemon->log_display_id = forward->frec_src.log_id; /* Find the original query that started it all.... */ for (orig = forward; orig->dependent; orig = orig->dependent); /* As soon as anything returns BOGUS, we stop and unwind, to do otherwise would invite infinite loops, since the answers to DNSKEY and DS queries will not be cached, so they'll be repeated. */ if (!STAT_ISEQUAL(status, STAT_BOGUS) && !STAT_ISEQUAL(status, STAT_TRUNCATED) && !STAT_ISEQUAL(status, STAT_ABANDONED)) { /* If all replies to a query are REFUSED, give up. */ if (RCODE(header) == REFUSED) status = STAT_ABANDONED; else if (header->hb3 & HB3_TC) { /* Truncated answer can't be validated. If this is an answer to a DNSSEC-generated query, we switch to TCP mode. For downstream queries, if the client didn't ask for DNSSEC RRs, do the query over TCP, and hope that it fits once the DNSSEC RRs have been stripped, otherwise get the client to retry over TCP, so return an answer with the TC bit set. */ if ((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) || !(forward->flags & FREC_DO_QUESTION)) { status = (forward->flags & FREC_DNSKEY_QUERY) ? STAT_NEED_KEY: ((forward->flags & FREC_DS_QUERY) ? STAT_NEED_DS : STAT_OK); /* Get the query we sent by UDP */ blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); if (extract_request(header, forward->stash_len, daemon->namebuff, NULL)) log_query(F_UPSTREAM | F_NOEXTRA, daemon->namebuff, NULL, "truncated", 0); /* Don't count failed UDP attempt AND TCP */ if (status != STAT_OK) orig->work_counter++; /* NOTE: Can't move connection marks from UDP to TCP */ plen = forward->stash_len; status = swap_to_tcp(forward, now, status, header, &plen, forward->class, forward->sentto, &orig->work_counter, &orig->validate_counter); /* We forked a new process. pop_and_retry_query() will be called when is completes. */ if (STAT_ISEQUAL(status, STAT_ASYNC)) { forward->flags |= FREC_GONE_TO_TCP; return; } } else status = STAT_TRUNCATED; } else { /* As soon as anything returns BOGUS, we stop and unwind, to do otherwise would invite infinite loops, since the answers to DNSKEY and DS queries will not be cached, so they'll be repeated. */ if (forward->flags & FREC_DNSKEY_QUERY) status = dnssec_validate_by_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class, &orig->validate_counter); else if (forward->flags & FREC_DS_QUERY) status = dnssec_validate_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class, &orig->validate_counter); else status = dnssec_validate_reply(now, header, plen, daemon->namebuff, daemon->keyname, &forward->class, !option_bool(OPT_DNSSEC_IGN_NS) && (forward->sentto->flags & SERV_DO_DNSSEC), NULL, NULL, NULL, &orig->validate_counter); if (STAT_ISEQUAL(status, STAT_ABANDONED)) log_resource = 1; } } /* Can't validate, as we're missing key data. Put this answer aside, whilst we get that. */ if (STAT_ISEQUAL(status, STAT_NEED_DS) || STAT_ISEQUAL(status, STAT_NEED_KEY)) { struct blockdata *stash; /* Now save reply pending receipt of key data */ if ((stash = blockdata_alloc((char *)header, plen))) { /* validate routines leave name of required record in daemon->keyname */ unsigned int flags = STAT_ISEQUAL(status, STAT_NEED_KEY) ? FREC_DNSKEY_QUERY : FREC_DS_QUERY; struct frec *old; if ((old = lookup_frec(daemon->keyname, forward->class, -1, -1, flags, flags))) { /* This is tricky; it detects loops in the dependency graph for DNSSEC validation, say validating A requires DS B and validating DS B requires DNSKEY C and validating DNSKEY C requires DS B. This should never happen in correctly signed records, but it's likely the case that sufficiently broken ones can cause our validation code requests to exhibit cycles. The result is that the ->blocking_query list can form a cycle, and under certain circumstances that can lock us in an infinite loop. Here we transform the situation into ABANDONED. */ struct frec *f; for (f = old; f; f = f->blocking_query) if (f == forward) break; if (!f) { forward->next_dependent = old->dependent; old->dependent = forward; /* Make consistent, only replace query copy with unvalidated answer when we set ->blocking_query. */ blockdata_free(forward->stash); forward->blocking_query = old; forward->stash_len = plen; forward->stash = stash; return; } } else if (orig->work_counter-- == 0) { my_syslog(LOG_WARNING, _("limit exceeded: per-query subqueries")); log_resource = 1; } else { struct server *server; size_t nn; int serverind, fd; struct randfd_list *rfds = NULL; struct frec *new = NULL; struct blockdata *newstash = NULL; /* Make sure we don't expire and free the orig frec during the allocation of a new one: third arg of get_new_frec() does that. */ if ((serverind = dnssec_server(forward->sentto, daemon->keyname, NULL, NULL)) != -1 && (server = daemon->serverarray[serverind]) && (nn = dnssec_generate_query(header, ((unsigned char *) header) + daemon->edns_pktsz, daemon->keyname, forward->class, get_id(), STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS)) && (fd = allocate_rfd(&rfds, server)) != -1 && (newstash = blockdata_alloc((char *)header, nn)) && (new = get_new_frec(now, server, 1))) { struct frec *next = new->next; *new = *forward; /* copy everything, then overwrite */ new->next = next; new->blocking_query = NULL; new->frec_src.log_id = daemon->log_display_id = ++daemon->log_id; new->sentto = server; new->rfds = rfds; new->frec_src.next = NULL; new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY); new->flags |= flags; new->forwardall = 0; new->frec_src.encode_bitmap = 0; new->frec_src.encode_bigmap = NULL; forward->next_dependent = NULL; new->dependent = forward; /* to find query awaiting new one. */ /* Make consistent, only replace query copy with unvalidated answer when we set ->blocking_query. */ forward->blocking_query = new; blockdata_free(forward->stash); forward->stash_len = plen; forward->stash = stash; new->new_id = ntohs(header->id); /* Save query for retransmission and de-dup */ new->stash = newstash; new->stash_len = nn; if (daemon->fast_retry_time != 0) new->forward_timestamp = dnsmasq_milliseconds(); /* Don't resend this. */ daemon->srv_save = NULL; #ifdef HAVE_CONNTRACK if (option_bool(OPT_CONNTRACK)) set_outgoing_mark(orig, fd); #endif server_send(server, fd, header, nn, 0); server->queries++; #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr, fd); #endif log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, daemon->keyname, &server->addr, STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0); return; } /* error unwind */ free_rfds(&rfds); blockdata_free(newstash); } blockdata_free(stash); /* don't leak this on failure. */ } /* sending DNSSEC query failed or loop detected. */ status = STAT_ABANDONED; } if (log_resource) { /* Log the actual validation that made us barf. */ if (extract_request(header, plen, daemon->namebuff, NULL)) my_syslog(LOG_WARNING, _("validation of %s failed: resource limit exceeded."), daemon->namebuff[0] ? daemon->namebuff : "."); } #ifdef HAVE_DUMPFILE if (STAT_ISEQUAL(status, STAT_BOGUS) || STAT_ISEQUAL(status, STAT_ABANDONED)) dump_packet_udp((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS, header, (size_t)plen, &forward->sentto->addr, NULL, -daemon->port); #endif if (!forward->dependent) /* Validated original answer, all done. */ return_reply(now, forward, header, plen, status); else pop_and_retry_query(forward, status, now); } void pop_and_retry_query(struct frec *forward, int status, time_t now) { /* validated subsidiary query/queries, (and cached result) pop that and return to the previous query/queries we were working on. */ struct frec *prev, *nxt = forward->dependent; struct dns_header *header = (struct dns_header *)daemon->packet; free_frec(forward); while ((prev = nxt)) { /* ->next_dependent will have changed after return from recursive call below. */ nxt = prev->next_dependent; prev->blocking_query = NULL; /* already gone */ blockdata_retrieve(prev->stash, prev->stash_len, (void *)header); dnssec_validate(prev, header, prev->stash_len, status, now); } } #endif /* sets new last_server */ void reply_query(int fd, time_t now) { /* packet from peer server, extract data for cache, and send to original requester */ struct dns_header *header; union mysockaddr serveraddr; struct frec *forward; socklen_t addrlen = sizeof(serveraddr); ssize_t n = recvfrom(fd, daemon->packet, daemon->packet_buff_sz, 0, &serveraddr.sa, &addrlen); struct server *server; int first, last, serv, c, class, rrtype; unsigned char *p; struct randfd_list *fdl; /* packet buffer overwritten */ daemon->srv_save = NULL; /* Determine the address of the server replying so that we can mark that as good */ if (serveraddr.sa.sa_family == AF_INET6) serveraddr.in6.sin6_flowinfo = 0; header = (struct dns_header *)daemon->packet; if (n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) || ntohs(header->qdcount) != 1) return; p = (unsigned char *)(header+1); if (!extract_name(header, n, &p, daemon->namebuff, EXTR_NAME_EXTRACT, 4)) return; /* bad packet */ GETSHORT(rrtype, p); GETSHORT(class, p); if (!(forward = lookup_frec(daemon->namebuff, class, rrtype, ntohs(header->id), FREC_ANSWER, 0))) return; filter_servers(forward->sentto->arrayposn, F_SERVER, &first, &last); /* Check that this arrived on the file descriptor we expected. */ /* sent from random port */ for (fdl = forward->rfds; fdl; fdl = fdl->next) if (fdl->rfd->fd == fd) break; if (!fdl) { /* Sent to upstream from socket associated with a server. Note we have to iterate over all the possible servers, since they may have different bound sockets. */ for (serv = first; serv != last; serv++) { server = daemon->serverarray[serv]; if (server->sfd && server->sfd->fd == fd) break; if (serv == last) return; } } /* spoof check: answer must come from known server, also we may have sent the same query to multiple servers from the same local socket, and would like to know which one has answered. */ for (c = first; c != last; c++) if (sockaddr_isequal(&daemon->serverarray[c]->addr, &serveraddr)) break; if (c == last) return; server = daemon->serverarray[c]; if (RCODE(header) != REFUSED) daemon->serverarray[first]->last_server = c; else if (daemon->serverarray[first]->last_server == c) daemon->serverarray[first]->last_server = -1; /* log_query gets called indirectly all over the place, so pass these in global variables - sorry. */ daemon->log_display_id = forward->frec_src.log_id; daemon->log_source_addr = &forward->frec_src.source; #ifdef HAVE_DUMPFILE dump_packet_udp((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_REPLY : DUMP_UP_REPLY, (void *)header, n, &serveraddr, NULL, fd); #endif if (daemon->ignore_addr && RCODE(header) == NOERROR && check_for_ignored_address(header, n)) return; #ifdef HAVE_DNSSEC /* The query MAY have got a good answer, and be awaiting the results of further queries, in which case the stash contains something else and we don't need to retry anyway. We may also have already got a truncated reply, and be in the process of doing the query by TCP so can ignore further, probably truncated, UDP answers. */ if (forward->blocking_query || (forward->flags & FREC_GONE_TO_TCP)) return; #endif if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) && forward->forwardall == 0) /* for broken servers, attempt to send to another one. */ { /* Get the saved query back. */ blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); forward_query(-1, NULL, NULL, 0, header, forward->stash_len, 0, now, forward, 0, 0); return; } /* 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 */ /* decrement count of replies recieved if we sent to more than one server. */ if (forward->forwardall && (--forward->forwardall > 1) && RCODE(header) == REFUSED) return; forward->sentto = server; /* We have a good answer, and will now validate it or return it. It may be some time before this the validation completes, but we don't need any more answers, so close the socket(s) on which we were expecting answers, to conserve file descriptors, and to save work reading and discarding answers for other upstreams. */ free_rfds(&forward->rfds); /* calculate modified moving average of server latency */ if (server->query_latency == 0) server->mma_latency = (dnsmasq_milliseconds() - forward->forward_timestamp) * 128; /* init */ else server->mma_latency += dnsmasq_milliseconds() - forward->forward_timestamp - server->query_latency; /* denominator controls how many queries we average over. */ server->query_latency = server->mma_latency/128; /* Flip the bits back in the query name. */ p = (unsigned char *)(header+1); if (!extract_name(header, n, &p, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1)) return; #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID)) { if ((forward->sentto->flags & SERV_DO_DNSSEC) && !(forward->flags & FREC_CHECKING_DISABLED)) { dnssec_validate(forward, header, n, STAT_OK, now); return; } /* If dnssec_validate() not called, rr_status{} is not valid Clear it so we don't erroneously mark RRs as secure using stale data from previous queries. */ memset(daemon->rr_status, 0, sizeof(*daemon->rr_status) * daemon->rr_status_sz); } #endif return_reply(now, forward, header, n, STAT_OK); } static void xor_array(unsigned int *arg1, unsigned int *arg2, unsigned int len) { unsigned int i; for (i = 0; i < len; i++) arg1[i] ^= arg2[i]; } /* Call extract_name() to flip case of query in packet according to the XOR of the bit maps help in arg1 and arg2 */ static void flip_queryname(struct dns_header *header, ssize_t len, struct frec_src *arg1, struct frec_src *arg2) { unsigned char *p = (unsigned char *)(header+1); unsigned int *arg1p, *arg2p, arg1len, arg2len, *swapp, swap; /* Two cases: bitmap is single 32 bit int, or it's arbitrary-length array of 32bit ints. The two args may be different and of different lengths. The shorter gets notionally extended with zeros. */ if (arg1->encode_bigmap) arg1p = arg1->encode_bigmap, arg1len = arg1->encode_bitmap; else arg1p = &arg1->encode_bitmap, arg1len = 1; if (arg2->encode_bigmap) arg2p = arg2->encode_bigmap, arg2len = arg2->encode_bitmap; else arg2p = &arg2->encode_bitmap, arg2len = 1; /* make arg1 the longer, if they differ. */ if (arg2len > arg1len) { swap = arg1len; swapp = arg1p; arg1len = arg2len; arg1p = arg2p; arg2len = swap; arg2p = swapp; } /* XOR on shorter length, flip on longer, operate on longer */ xor_array(arg1p, arg2p, arg2len); extract_name(header, len, &p, (char *)arg1p, EXTR_NAME_FLIP, arg1len); xor_array(arg1p, arg2p, arg2len); /* restore */ } void return_reply(time_t now, struct frec *forward, struct dns_header *header, ssize_t n, int status) { int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; size_t nn; int ede = EDE_UNSET; (void)status; daemon->log_display_id = forward->frec_src.log_id; daemon->log_source_addr = (forward->frec_src.fd != -1) ? &forward->frec_src.source : NULL; /* 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 (!STAT_ISEQUAL(status, STAT_OK)) { /* status is STAT_OK when validation not turned on. */ no_cache_dnssec = 0; if (STAT_ISEQUAL(status, STAT_TRUNCATED)) { header->hb3 |= HB3_TC; log_query(F_SECSTAT, "result", NULL, "TRUNCATED", 0); } else { char *result, *domain = "result"; union all_addr a; ede = errflags_to_ede(status); if (STAT_ISEQUAL(status, STAT_ABANDONED)) { result = "ABANDONED"; status = STAT_BOGUS; if (ede == EDE_UNSET) ede = EDE_OTHER; } else result = (STAT_ISEQUAL(status, STAT_SECURE) ? "SECURE" : (STAT_ISEQUAL(status, STAT_INSECURE) ? "INSECURE" : "BOGUS")); if (STAT_ISEQUAL(status, STAT_SECURE)) cache_secure = 1; else if (STAT_ISEQUAL(status, STAT_BOGUS)) { if (ede == EDE_UNSET) ede = EDE_DNSSEC_BOGUS; no_cache_dnssec = 1; bogusanswer = 1; if (extract_request(header, n, daemon->namebuff, NULL)) domain = daemon->namebuff; } a.log.ede = ede; log_query(F_SECSTAT, domain, &a, result, 0); } } if ((daemon->limit[LIMIT_CRYPTO] - forward->validate_counter) > (int)daemon->metrics[METRIC_CRYPTO_HWM]) daemon->metrics[METRIC_CRYPTO_HWM] = daemon->limit[LIMIT_CRYPTO] - forward->validate_counter; if ((daemon->limit[LIMIT_WORK] - forward->work_counter) > (int)daemon->metrics[METRIC_WORK_HWM]) daemon->metrics[METRIC_WORK_HWM] = daemon->limit[LIMIT_WORK] - forward->work_counter; #endif if (option_bool(OPT_NO_REBIND)) check_rebind = !(forward->flags & FREC_NOREBIND); /* restore CD bit to the value in the query */ if (forward->flags & FREC_CHECKING_DISABLED) header->hb4 |= HB4_CD; else header->hb4 &= ~HB4_CD; /* Never cache answers which are contingent on the source or MAC address EDSN0 option, since the cache is ignorant of such things. */ if (forward->flags & FREC_NO_CACHE) no_cache_dnssec = 1; if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, !(forward->flags & FREC_HAS_PHEADER), &forward->frec_src.source, ((unsigned char *)header) + daemon->edns_pktsz, ede))) { struct frec_src *src, *prev; int do_trunc; for (do_trunc = 0, prev = NULL, src = &forward->frec_src; src; prev = src, src = src->next) { header->id = htons(src->orig_id); #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) if (option_bool(OPT_CMARK_ALST_EN)) { unsigned int mark; int have_mark = get_incoming_mark(&src->source, &src->dest, /* istcp: */ 0, &mark); if (have_mark && ((u32)mark & daemon->allowlist_mask)) report_addresses(header, nn, mark); } #endif /* You will have to draw diagrams and scratch your head to convince yourself that this works. Bear in mind that the flip to upstream state has already been undone, for the original query so nothing needs to be done, but subsequent queries' flips were recorded relative to the flipped name sent upstream. */ if (prev) flip_queryname(header, nn, prev, src); if (src->fd != -1) { /* Only send packets that fit what the requestor allows. We'll send a truncated packet to others below. */ if (nn <= src->udp_pkt_size) { send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, &src->source, &src->dest, src->iface); #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source, src->fd); #endif } else do_trunc = 1; if (option_bool(OPT_EXTRALOG) && src != &forward->frec_src) { daemon->log_display_id = src->log_id; daemon->log_source_addr = &src->source; log_query(F_UPSTREAM, "query", NULL, "duplicate", 0); } } } /* The packet is too big for one or more requestors, send them a truncated answer. */ if (do_trunc) { size_t hlen, new; unsigned char *pheader = find_pseudoheader(header, nn, &hlen, NULL, NULL, NULL); header->ancount = htons(0); header->nscount = htons(0); header->arcount = htons(0); header->hb3 |= HB3_TC; new = resize_packet(header, nn, pheader, hlen); daemon->log_display_id = forward->frec_src.log_id; daemon->log_source_addr = &forward->frec_src.source; log_query(F_UPSTREAM, NULL, NULL, "truncated", 0); /* This gets the name back to the state it was in when we started. */ flip_queryname(header, new, prev, &forward->frec_src); for (src = &forward->frec_src, prev = NULL; src; prev = src, src = src->next) { /* If you didn't undertand this above, you won't understand it here either. */ if (prev) flip_queryname(header, new, prev, src); if (src->fd != -1 && nn > src->udp_pkt_size) { header->id = htons(src->orig_id); send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, new, &src->source, &src->dest, src->iface); #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_REPLY, daemon->packet, (size_t)new, NULL, &src->source, src->fd); #endif } } } } free_frec(forward); /* cancel */ } #ifdef HAVE_CONNTRACK static int is_query_allowed_for_mark(u32 mark, const char *name) { int is_allowable_name, did_validate_name = 0; struct allowlist *allowlists; char **patterns_pos; for (allowlists = daemon->allowlists; allowlists; allowlists = allowlists->next) if (allowlists->mark == (mark & daemon->allowlist_mask & allowlists->mask)) for (patterns_pos = allowlists->patterns; *patterns_pos; patterns_pos++) { if (!strcmp(*patterns_pos, "*")) return 1; if (!did_validate_name) { is_allowable_name = name ? is_valid_dns_name(name) : 0; did_validate_name = 1; } if (is_allowable_name && is_dns_name_matching_pattern(name, *patterns_pos)) return 1; } return 0; } static size_t answer_disallowed(struct dns_header *header, size_t qlen, u32 mark, const char *name) { unsigned char *p; (void)name; (void)mark; #ifdef HAVE_UBUS if (name) ubus_event_bcast_connmark_allowlist_refused(mark, name); #endif setup_reply(header, /* flags: */ 0, EDE_BLOCKED); if (!(p = skip_questions(header, qlen))) return 0; return p - (unsigned char *)header; } #endif void receive_query(struct listener *listen, time_t now) { struct dns_header *header = (struct dns_header *)daemon->packet; union mysockaddr source_addr; unsigned char *pheader; unsigned short type, udp_size = PACKETSZ; /* default if no EDNS0 */ union all_addr dst_addr; struct in_addr netmask, dst_addr_4; size_t m; ssize_t n; int if_index = 0, auth_dns = 0, do_bit = 0; unsigned int fwd_flags = 0; int stale = 0, filtered = 0, ede = EDE_UNSET, do_forward = 0; int metric, fd; struct blockdata *saved_question = NULL; #ifdef HAVE_CONNTRACK unsigned int mark = 0; int have_mark = 0; int allowed = 1; # ifdef HAVE_UBUS int report = 0; # endif #endif #ifdef HAVE_AUTH int local_auth = 0; #endif struct iovec iov[1]; struct msghdr msg; struct cmsghdr *cmptr; union { struct cmsghdr align; /* this ensures alignment */ char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; #if defined(HAVE_LINUX_NETWORK) char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; #elif defined(IP_RECVDSTADDR) && defined(HAVE_SOLARIS_NETWORK) char control[CMSG_SPACE(sizeof(struct in_addr)) + CMSG_SPACE(sizeof(unsigned int))]; #elif defined(IP_RECVDSTADDR) char control[CMSG_SPACE(sizeof(struct in_addr)) + CMSG_SPACE(sizeof(struct sockaddr_dl))]; #endif } control_u; int family = listen->addr.sa.sa_family; /* Can always get recvd interface for IPv6 */ int check_dst = !option_bool(OPT_NOWILD) || family == AF_INET6; /* packet buffer overwritten */ daemon->srv_save = NULL; dst_addr_4.s_addr = dst_addr.addr4.s_addr = 0; netmask.s_addr = 0; if (option_bool(OPT_NOWILD) && listen->iface) { auth_dns = listen->iface->dns_auth; if (family == AF_INET) { dst_addr_4 = dst_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; msg.msg_control = control_u.control; msg.msg_controllen = sizeof(control_u); msg.msg_flags = 0; msg.msg_name = &source_addr; msg.msg_namelen = sizeof(source_addr); msg.msg_iov = iov; msg.msg_iovlen = 1; if ((n = recvmsg(listen->fd, &msg, 0)) == -1) return; if (n < (int)sizeof(struct dns_header) || (msg.msg_flags & MSG_TRUNC) || (header->hb3 & HB3_QR)) return; /* Clear buffer beyond request to avoid risk of information disclosure. */ memset(daemon->packet + n, 0, daemon->edns_pktsz - n); source_addr.sa.sa_family = family; if (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; } 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; } /* We can be configured to only accept queries from at-most-one-hop-away addresses. */ if (option_bool(OPT_LOCAL_SERVICE)) { struct addrlist *addr; if (family == AF_INET6) { for (addr = daemon->interface_addrs; addr; addr = addr->next) if ((addr->flags & ADDRLIST_IPV6) && is_same_net6(&addr->addr.addr6, &source_addr.in6.sin6_addr, addr->prefixlen)) break; } else { 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.addr4, source_addr.in.sin_addr, netmask)) break; } } if (!addr) { static int warned = 0; if (!warned) { prettyprint_addr(&source_addr, daemon->addrbuff); my_syslog(LOG_WARNING, _("ignoring query from non-local network %s (logged only once)"), daemon->addrbuff); warned = 1; } return; } } if (check_dst) { struct ifreq ifr; if (msg.msg_controllen < sizeof(struct cmsghdr)) return; #if defined(HAVE_LINUX_NETWORK) if (family == AF_INET) 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); dst_addr_4 = dst_addr.addr4 = p.p->ipi_spec_dst; if_index = p.p->ipi_ifindex; } #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) if (family == AF_INET) { for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) { union { unsigned char *c; unsigned int *i; struct in_addr *a; #ifndef HAVE_SOLARIS_NETWORK struct sockaddr_dl *s; #endif } p; p.c = CMSG_DATA(cmptr); if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) dst_addr_4 = dst_addr.addr4 = *(p.a); else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) #ifdef HAVE_SOLARIS_NETWORK if_index = *(p.i); #else if_index = p.s->sdl_index; #endif } } #endif if (family == AF_INET6) { 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); dst_addr.addr6 = p.p->ipi6_addr; if_index = p.p->ipi6_ifindex; } } /* enforce available interface configuration */ if (!indextoname(listen->fd, if_index, ifr.ifr_name)) return; if (!iface_check(family, &dst_addr, ifr.ifr_name, &auth_dns)) { if (!option_bool(OPT_CLEVERBIND)) enumerate_interfaces(0); if (!loopback_exception(listen->fd, family, &dst_addr, ifr.ifr_name) && !label_exception(if_index, family, &dst_addr)) return; } if (family == AF_INET && option_bool(OPT_LOCALISE)) { struct irec *iface; /* get the netmask of the interface which has the address we were sent to. This is no necessarily 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; #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_QUERY, daemon->packet, (size_t)n, &source_addr, NULL, listen->fd); #endif #ifdef HAVE_CONNTRACK if (option_bool(OPT_CMARK_ALST_EN)) have_mark = get_incoming_mark(&source_addr, &dst_addr, /* istcp: */ 0, &mark); #endif if (OPCODE(header) != QUERY) log_query_mysockaddr(F_QUERY | F_FORWARD, "opcode", &source_addr, "non-query", 0); else if (extract_request(header, (size_t)n, daemon->namebuff, &type)) { #ifdef HAVE_AUTH struct auth_zone *zone; #endif log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff, &source_addr, auth_dns ? "auth" : "query", type); #ifdef HAVE_AUTH /* Find queries for zones we're authoritative for, and answer them directly. The exception to this is DS queries for the zone route. They have to come from the parent zone. Since dnsmasq's auth server can't do DNSSEC, the zone will be unsigned, and anything using dnsmasq as a forwarder and doing validation will be expecting to see the proof of non-existence from the parent. */ if (!auth_dns && !option_bool(OPT_LOCALISE)) for (zone = daemon->auth_zones; zone; zone = zone->next) { char *cut; if (in_zone(zone, daemon->namebuff, &cut)) { if (type != T_DS || cut) { auth_dns = 1; local_auth = 1; } break; } } #endif #ifdef HAVE_LOOP /* Check for forwarding loop */ if (detect_loop(daemon->namebuff, type)) return; #endif } if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL, NULL)) { unsigned short flags; fwd_flags |= FREC_HAS_PHEADER; GETSHORT(udp_size, pheader); pheader += 2; /* ext_rcode */ GETSHORT(flags, pheader); if (flags & 0x8000) do_bit = 1;/* do bit */ /* If the client provides an EDNS0 UDP size, use that to limit our reply. (bounded by the maximum configured). If no EDNS0, then it defaults to 512. We write this value into the query packet too, so that if it's forwarded, we don't specify a maximum size greater than we can handle. */ if (udp_size > daemon->edns_pktsz) udp_size = daemon->edns_pktsz; else if (udp_size < PACKETSZ) udp_size = PACKETSZ; /* Sanity check - can't reduce below default. RFC 6891 6.2.3 */ } /* RFC 6840 5.7 */ if (do_bit || (header->hb4 & HB4_AD)) fwd_flags |= FREC_AD_QUESTION; if (do_bit) fwd_flags |= FREC_DO_QUESTION; if (header->hb4 & HB4_CD) fwd_flags |= FREC_CHECKING_DISABLED; fd = listen->fd; #ifdef HAVE_CONNTRACK #ifdef HAVE_AUTH if (!auth_dns || local_auth) #endif if (option_bool(OPT_CMARK_ALST_EN) && have_mark && ((u32)mark & daemon->allowlist_mask)) allowed = is_query_allowed_for_mark((u32)mark, daemon->namebuff); #endif if (0); #ifdef HAVE_CONNTRACK else if (!allowed) { ede = EDE_BLOCKED; m = answer_disallowed(header, (size_t)n, (u32)mark, daemon->namebuff); metric = METRIC_DNS_LOCAL_ANSWERED; } #endif #ifdef HAVE_AUTH else if (auth_dns) { m = answer_auth(header, ((char *) header) + udp_size, (size_t)n, now, &source_addr, local_auth); metric = METRIC_DNS_AUTH_ANSWERED; #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) if (local_auth) report = 1; #endif } #endif else { int cacheable; n = add_edns0_config(header, n, ((unsigned char *)header) + daemon->edns_pktsz, &source_addr, now, &cacheable); saved_question = blockdata_alloc((char *) header, (size_t)n); if (!cacheable) fwd_flags |= FREC_NO_CACHE; m = answer_request(header, ((char *) header) + udp_size, (size_t)n, dst_addr_4, netmask, now, fwd_flags & FREC_AD_QUESTION, do_bit, !cacheable, &stale, &filtered); metric = stale ? METRIC_DNS_STALE_ANSWERED : METRIC_DNS_LOCAL_ANSWERED; if (m == 0) do_forward = 1; else { #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) report = 1; #endif if (filtered) ede = EDE_FILTERED; else if (stale) ede = EDE_STALE; } } if (m != 0) { if (fwd_flags & FREC_HAS_PHEADER) { if (ede != EDE_UNSET) { u16 swap = htons(ede); m = add_pseudoheader(header, m, ((unsigned char *) header) + daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); } else m = add_pseudoheader(header, m, ((unsigned char *) header) + daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); } #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_REPLY, daemon->packet, m, NULL, &source_addr, listen->fd); #endif #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) if (report) report_addresses(header, m, mark); #endif send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, m, &source_addr, &dst_addr, if_index); daemon->metrics[metric]++; if (stale) { /* We answered with stale cache data, so forward the query anyway to refresh that. */ do_forward = 1; /* Don't mark the query with the source in this case. */ daemon->log_source_addr = NULL; /* We've already answered the client, so don't send it the answer when it comes back. */ fd = -1; } } if (do_forward && saved_question) { /* Get the question back, since it may have been mangled by answer_request() */ blockdata_retrieve(saved_question, (size_t)n, (void *)header); blockdata_free(saved_question); saved_question = NULL; forward_query(fd, &source_addr, &dst_addr, if_index, header, (size_t)n, udp_size, now, NULL, fwd_flags, 0); } blockdata_free(saved_question); } /* Send query in packet, qsize to a server determined by first,last,start and get the reply. return reply size. */ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, size_t qsize, int have_mark, unsigned int mark, struct server **servp) { int firstsendto = -1; u16 *length = (u16 *)packet; unsigned char *payload = &packet[2]; struct dns_header *header = (struct dns_header *)payload; unsigned int rsize; int class, rclass, type, rtype; unsigned char *p; struct blockdata *saved_question; struct timeval tv; (void)mark; (void)have_mark; /* Save the query to make sure we get the answer we expect. */ p = (unsigned char *)(header+1); if (!extract_name(header, qsize, &p, daemon->namebuff, EXTR_NAME_EXTRACT, 4)) return 0; GETSHORT(type, p); GETSHORT(class, p); /* Save question for retry. */ if (!(saved_question = blockdata_alloc((char *)header, (size_t)qsize))) return 0; while (1) { int data_sent = 0, fatal = 0; struct server *serv; if (firstsendto == -1) firstsendto = start; else { start++; if (start == last) start = first; if (start == firstsendto) break; } *servp = serv = daemon->serverarray[start]; retry: blockdata_retrieve(saved_question, qsize, header); *length = htons(qsize); if (serv->tcpfd == -1) { if ((serv->tcpfd = socket(serv->addr.sa.sa_family, SOCK_STREAM, 0)) == -1) continue; #ifdef HAVE_CONNTRACK /* Copy connection mark of incoming query to outgoing connection. */ if (have_mark) setsockopt(serv->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); #endif if ((!local_bind(serv->tcpfd, &serv->source_addr, serv->interface, 0, 1))) { close(serv->tcpfd); serv->tcpfd = -1; continue; } #if defined(SO_SNDTIMEO) && defined(SO_RCVTIMEO) /* TCP connections by default take ages to time out. Set shorter timeouts more appropriate for a DNS server. We set the recieve timeout as twice the send timeout; we want to fail quickly on a non-responsive server, but give it time to get an answer. */ tv.tv_sec = TCP_TIMEOUT; tv.tv_usec = 0; setsockopt(serv->tcpfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); tv.tv_sec += TCP_TIMEOUT; setsockopt(serv->tcpfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); #endif #ifdef MSG_FASTOPEN server_send(serv, serv->tcpfd, packet, qsize + sizeof(u16), MSG_FASTOPEN); if (errno == 0) data_sent = 1; else if (errno == ETIMEDOUT || errno == EHOSTUNREACH || errno == EINPROGRESS || errno == ECONNREFUSED) fatal = 1; #endif /* If fastopen failed due to lack of reply, then there's no point in trying again in non-FASTOPEN mode. */ if (fatal || (!data_sent && connect(serv->tcpfd, &serv->addr.sa, sa_len(&serv->addr)) == -1)) { int port; failed: port = prettyprint_addr(&serv->addr, daemon->addrbuff); my_syslog(LOG_DEBUG|MS_DEBUG, _("TCP connection failed to %s#%d"), daemon->addrbuff, port); close(serv->tcpfd); serv->tcpfd = -1; continue; } daemon->serverarray[first]->last_server = start; serv->flags &= ~SERV_GOT_TCP; } /* We us the _ONCE veriant of read_write() here because we've set a timeout on the tcp socket and wish to abort if the whole data is not read/written within the timeout. */ if ((!data_sent && !read_write(serv->tcpfd, (unsigned char *)packet, qsize + sizeof(u16), RW_WRITE_ONCE)) || !read_write(serv->tcpfd, (unsigned char *)length, sizeof (*length), RW_READ_ONCE) || !read_write(serv->tcpfd, payload, (rsize = ntohs(*length)), RW_READ_ONCE)) { /* We get data then EOF, reopen connection to same server, else try next. This avoids DoS from a server which accepts connections and then closes them. */ if (serv->flags & SERV_GOT_TCP) { close(serv->tcpfd); serv->tcpfd = -1; goto retry; } else goto failed; } /* If the question section of the reply doesn't match the question we sent, then someone might be attempting to insert bogus values into the cache by sending replies containing questions and bogus answers. Try another server, or give up */ p = (unsigned char *)(header+1); if (extract_name(header, rsize, &p, daemon->namebuff, EXTR_NAME_COMPARE, 4) != 1) continue; GETSHORT(rtype, p); GETSHORT(rclass, p); if (type != rtype || class != rclass) continue; serv->flags |= SERV_GOT_TCP; *servp = serv; blockdata_free(saved_question); return rsize; } blockdata_free(saved_question); return 0; } #ifdef HAVE_DNSSEC /* An answer to an downstream query or DNSSEC subquery has returned truncated. (Which type held in status). Resend the query (in header) via TCP */ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *plenp, int class, char *name, char *keyname, struct server *server, int *keycount, int *validatecount) { unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16)); struct dns_header *new_header = (struct dns_header *)&packet[2]; int start, first, last, new_status; ssize_t n = *plenp; int have_req = extract_request(header, n, keyname, NULL); int log_save = daemon->log_display_id; *plenp = 0; if (!packet) return STAT_ABANDONED; memcpy(new_header, header, n); /* Set TCP flag in logs. */ daemon->log_display_id = -daemon->log_display_id; /* send orginal query to same server that generated truncated reply on UDP. */ first = start = server->arrayposn; last = first + 1; if (!STAT_ISEQUAL(status, STAT_OK) && (!have_req || (start = dnssec_server(server, keyname, &first, &last)) == -1)) new_status = STAT_ABANDONED; else if ((n = tcp_talk(first, last, start, packet, n, 0, 0, &server)) == 0) new_status = STAT_ABANDONED; else { if (have_req) { if (STAT_ISEQUAL(status, STAT_OK)) log_query_mysockaddr(F_SERVER | F_FORWARD, keyname, &server->addr, NULL, 0); else log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, keyname, &server->addr, STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0); } new_status = tcp_key_recurse(now, status, new_header, n, class, name, keyname, server, 0, 0, keycount, validatecount); if (STAT_ISEQUAL(status, STAT_OK)) { /* downstream query: strip DNSSSEC RRs and see if it will fit in a UDP reply. */ rrfilter(new_header, (size_t *)&n, RRFILTER_DNSSEC); if (n >= daemon->edns_pktsz) { /* still too bIg, strip optional sections and try again. */ new_header->nscount = htons(0); new_header->arcount = htons(0); n = resize_packet(new_header, n, NULL, 0); if (n >= daemon->edns_pktsz) { /* truncating the packet will break the answers, so remove them too and mark the reply as truncated. */ new_header->ancount = htons(0); n = resize_packet(new_header, n, NULL, 0); new_status = STAT_TRUNCATED; } } /* return the stripped or truncated reply. */ memcpy(header, new_header, n); *plenp = n; } } daemon->log_display_id = log_save; free(packet); return new_status; } /* Recurse down the key hierarchy */ 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 have_mark, unsigned int mark, int *keycount, int *validatecount) { int first, last, start, new_status; unsigned char *packet = NULL; struct dns_header *new_header = NULL; while (1) { size_t m; int log_save; /* limit the amount of work we do, to avoid cycling forever on loops in the DNS */ if (STAT_ISEQUAL(status, STAT_NEED_KEY)) new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class, validatecount); else if (STAT_ISEQUAL(status, STAT_NEED_DS)) new_status = dnssec_validate_ds(now, header, n, name, keyname, class, validatecount); else new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, !option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC), NULL, NULL, NULL, validatecount); if (!STAT_ISEQUAL(new_status, STAT_NEED_DS) && !STAT_ISEQUAL(new_status, STAT_NEED_KEY) && !STAT_ISEQUAL(new_status, STAT_ABANDONED)) break; if ((*keycount)-- == 0) { my_syslog(LOG_WARNING, _("limit exceeded: per-query subqueries")); new_status = STAT_ABANDONED; } if (STAT_ISEQUAL(new_status, STAT_ABANDONED)) { /* Log the actual validation that made us barf. */ if (extract_request(header, n, daemon->namebuff, NULL)) my_syslog(LOG_WARNING, _("validation of %s failed: resource limit exceeded."), daemon->namebuff[0] ? daemon->namebuff : "."); break; } /* Can't validate because we need a key/DS whose name now in keyname. Make query for same, and recurse to validate */ if (!packet) { packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16)); new_header = (struct dns_header *)&packet[2]; } if (!packet) { new_status = STAT_ABANDONED; break; } m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, 0, STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS); if ((start = dnssec_server(server, keyname, &first, &last)) == -1) { new_status = STAT_ABANDONED; break; } if ((m = tcp_talk(first, last, start, packet, m, have_mark, mark, &server)) == 0) { new_status = STAT_ABANDONED; break; } log_save = daemon->log_display_id; daemon->log_display_id = -(++daemon->log_id); log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, keyname, &server->addr, STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0); new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount, validatecount); daemon->log_display_id = log_save; /* If we got STAT_OK from a DS or KEY validation on recursing, loop round and try the failed validation again. */ if (!STAT_ISEQUAL(new_status, STAT_OK)) break; } if (packet) free(packet); return new_status; } #endif /* The daemon forks before calling this: it should deal with one connection, blocking as necessary, 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, union mysockaddr *local_addr, struct in_addr netmask, int auth_dns) { size_t size = 0, saved_size = 0; int norebind = 0; #ifdef HAVE_CONNTRACK int allowed = 1; #endif #ifdef HAVE_AUTH int local_auth = 0; #endif int checking_disabled, do_bit = 0, ad_reqd = 0, have_pseudoheader = 0; struct blockdata *saved_question = NULL; unsigned short qtype; unsigned int gotname = 0; /* Max TCP packet + slop + size */ unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16)); unsigned char *payload = &packet[2]; u16 tcp_len; /* 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 *serv; struct in_addr dst_addr_4; union mysockaddr peer_addr; socklen_t peer_len = sizeof(union mysockaddr); int query_count = 0; unsigned char *pheader; unsigned int mark = 0; int have_mark = 0; int first, last, filtered, do_stale = 0; if (!packet || getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1) return packet; #ifdef HAVE_CONNTRACK /* Get connection mark of incoming query to set on outgoing connections. */ if (option_bool(OPT_CONNTRACK) || option_bool(OPT_CMARK_ALST_EN)) { union all_addr local; if (local_addr->sa.sa_family == AF_INET6) local.addr6 = local_addr->in6.sin6_addr; else local.addr4 = local_addr->in.sin_addr; have_mark = get_incoming_mark(&peer_addr, &local, 1, &mark); } #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; 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.addr6, &peer_addr.in6.sin6_addr, addr->prefixlen)) break; } else { 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.addr4, peer_addr.in.sin_addr, netmask)) break; } } if (!addr) { prettyprint_addr(&peer_addr, daemon->addrbuff); my_syslog(LOG_WARNING, _("ignoring query from non-local network %s"), daemon->addrbuff); return packet; } } while (1) { int cacheable = 1, stale = 0, ede = EDE_UNSET; size_t m = 0; unsigned int flags = 0; #ifdef HAVE_AUTH struct auth_zone *zone; #endif if (!do_stale) { if (query_count >= TCP_MAX_QUERIES) break; if (!read_write(confd, (unsigned char *)&tcp_len, sizeof(tcp_len), RW_READ) || !(size = ntohs(tcp_len)) || !read_write(confd, payload, size, RW_READ)) break; if (size < (int)sizeof(struct dns_header)) continue; /* Clear buffer beyond request to avoid risk of information disclosure. */ memset(payload + size, 0, 65536 - size); query_count++; /* log_query gets called indirectly all over the place, so pass these in global variables - sorry. log_display_id is negative for TCP connections. */ daemon->log_display_id = -(++daemon->log_id); daemon->log_source_addr = &peer_addr; if (OPCODE(header) != QUERY) { log_query_mysockaddr(F_QUERY | F_FORWARD, "opcode", &peer_addr, "non-query", 0); gotname = 0; flags = F_RCODE; } else if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype))) ede = EDE_INVALID_DATA; else { if (saved_question) blockdata_free(saved_question); size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &cacheable); saved_question = blockdata_alloc((char *)header, (size_t)size); saved_size = size; log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff, &peer_addr, auth_dns ? "auth" : "query", qtype); #ifdef HAVE_AUTH /* Find queries for zones we're authoritative for, and answer them directly. The exception to this is DS queries for the zone route. They have to come from the parent zone. Since dnsmasq's auth server can't do DNSSEC, the zone will be unsigned, and anything using dnsmasq as a forwarder and doing validation will be expecting to see the proof of non-existence from the parent. */ if (!auth_dns && !option_bool(OPT_LOCALISE)) for (zone = daemon->auth_zones; zone; zone = zone->next) { char *cut; if (in_zone(zone, daemon->namebuff, &cut)) { if (qtype != T_DS || cut) { auth_dns = 1; local_auth = 1; } break; } } #endif norebind = domain_no_rebind(daemon->namebuff); if (local_addr->sa.sa_family == AF_INET) dst_addr_4 = local_addr->in.sin_addr; else dst_addr_4.s_addr = 0; do_bit = 0; if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL)) { unsigned short ede_flags; have_pseudoheader = 1; pheader += 4; /* udp_size, ext_rcode */ GETSHORT(ede_flags, pheader); if (ede_flags & 0x8000) do_bit = 1; /* do bit */ } ad_reqd = do_bit; /* RFC 6840 5.7 */ if (header->hb4 & HB4_AD) ad_reqd = 1; #ifdef HAVE_CONNTRACK #ifdef HAVE_AUTH if (!auth_dns || local_auth) #endif if (option_bool(OPT_CMARK_ALST_EN) && have_mark && ((u32)mark & daemon->allowlist_mask)) allowed = is_query_allowed_for_mark((u32)mark, daemon->namebuff); #endif if (0); #ifdef HAVE_CONNTRACK else if (!allowed) { ede = EDE_BLOCKED; m = answer_disallowed(header, size, (u32)mark, daemon->namebuff); } #endif #ifdef HAVE_AUTH else if (auth_dns) m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr, local_auth); #endif else m = answer_request(header, ((char *) header) + 65536, (size_t)size, dst_addr_4, netmask, now, ad_reqd, do_bit, !cacheable, &stale, &filtered); } } /* Do this by steam now we're not in the select() loop */ check_log_writer(1); if (m == 0 && ede == EDE_UNSET && saved_question) { struct server *master; int start; int no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; blockdata_retrieve(saved_question, (size_t)saved_size, header); size = saved_size; /* save state of "cd" flag in query */ checking_disabled = header->hb4 & HB4_CD; if (lookup_domain(daemon->namebuff, gotname, &first, &last)) flags = is_local_answer(now, first, daemon->namebuff); else ede = EDE_NOT_READY; if (!flags && ede == EDE_UNSET) { /* don't forward A or AAAA queries for simple names, except the empty name */ if (option_bool(OPT_NODOTS_LOCAL) && (gotname & (F_IPV4 | F_IPV6)) && !strchr(daemon->namebuff, '.') && strlen(daemon->namebuff) != 0) flags = check_for_local_domain(daemon->namebuff, now) ? F_NOERR : F_NXDOMAIN; else { master = daemon->serverarray[first]; if (option_bool(OPT_ORDER) || master->last_server == -1) start = first; else start = master->last_server; #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID) && (master->flags & SERV_DO_DNSSEC)) { size = add_do_bit(header, size, ((unsigned 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; } #endif /* Loop round available servers until we succeed in connecting to one. */ if ((m = tcp_talk(first, last, start, packet, size, have_mark, mark, &serv)) == 0) ede = EDE_NETERR; else { /* 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"); log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff, &serv->addr, NULL, 0); #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID)) { /* Clear this in case we don't call tcp_key_recurse() below */ memset(daemon->rr_status, 0, sizeof(*daemon->rr_status) * daemon->rr_status_sz); if (checking_disabled || (header->hb4 & HB4_CD)) no_cache_dnssec = 1; else if (master->flags & SERV_DO_DNSSEC) { int keycount = daemon->limit[LIMIT_WORK]; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */ int validatecount = daemon->limit[LIMIT_CRYPTO]; int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname, serv, have_mark, mark, &keycount, &validatecount); char *result, *domain = "result"; union all_addr a; ede = errflags_to_ede(status); if (STAT_ISEQUAL(status, STAT_ABANDONED)) { result = "ABANDONED"; status = STAT_BOGUS; if (ede == EDE_UNSET) ede = EDE_OTHER; } else result = (STAT_ISEQUAL(status, STAT_SECURE) ? "SECURE" : (STAT_ISEQUAL(status, STAT_INSECURE) ? "INSECURE" : "BOGUS")); if (STAT_ISEQUAL(status, STAT_SECURE)) cache_secure = 1; else if (STAT_ISEQUAL(status, STAT_BOGUS)) { if (ede == EDE_UNSET) ede = EDE_DNSSEC_BOGUS; no_cache_dnssec = 1; bogusanswer = 1; if (extract_request(header, m, daemon->namebuff, NULL)) domain = daemon->namebuff; } a.log.ede = ede; log_query(F_SECSTAT, domain, &a, result, 0); if ((daemon->limit[LIMIT_CRYPTO] - validatecount) > (int)daemon->metrics[METRIC_CRYPTO_HWM]) daemon->metrics[METRIC_CRYPTO_HWM] = daemon->limit[LIMIT_CRYPTO] - validatecount; if ((daemon->limit[LIMIT_WORK] - keycount) > (int)daemon->metrics[METRIC_WORK_HWM]) daemon->metrics[METRIC_WORK_HWM] = daemon->limit[LIMIT_WORK] - keycount; /* include DNSSEC queries in the limit for a connection. */ query_count += daemon->limit[LIMIT_WORK] - keycount; } } #endif /* restore CD bit to the value in the query */ if (checking_disabled) header->hb4 |= HB4_CD; else header->hb4 &= ~HB4_CD; /* Never cache answers which are contingent on the source or MAC address EDSN0 option, since the cache is ignorant of such things. */ if (!cacheable) no_cache_dnssec = 1; m = process_reply(header, now, serv, (unsigned int)m, option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer, ad_reqd, do_bit, !have_pseudoheader, &peer_addr, ((unsigned char *)header) + 65536, ede); /* process_reply() adds pheader itself */ have_pseudoheader = 0; } } } } if (do_stale) break; /* In case of local answer or no connections made. */ if (m == 0) { if (!(m = make_local_answer(flags, gotname, size, header, daemon->namebuff, ((char *) header) + 65536, first, last, ede))) break; } else if (ede == EDE_UNSET) { if (filtered) ede = EDE_FILTERED; else if (stale) ede = EDE_STALE; } if (have_pseudoheader) { u16 swap = htons((u16)ede); if (ede != EDE_UNSET) m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); else m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, 0, NULL, 0, do_bit, 0); } check_log_writer(1); *length = htons(m); #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) #ifdef HAVE_AUTH if (!auth_dns || local_auth) #endif if (option_bool(OPT_CMARK_ALST_EN) && have_mark && ((u32)mark & daemon->allowlist_mask)) report_addresses(header, m, mark); #endif if (!read_write(confd, packet, m + sizeof(u16), RW_WRITE)) break; /* If we answered with stale data, this process will now try and get fresh data into the cache and cannot therefore accept new queries. Close the incoming connection to signal that to the client. Then set do_stale and loop round once more to try and get fresh data, after which we exit. */ if (stale) { shutdown(confd, SHUT_RDWR); close(confd); do_stale = 1; /* Don't mark the query with the source when refreshing stale data. */ daemon->log_source_addr = NULL; } } /* If we ran once to get fresh data, confd is already closed. */ if (!do_stale) { shutdown(confd, SHUT_RDWR); close(confd); } blockdata_free(saved_question); check_log_writer(1); return packet; } /* return a UDP socket bound to a random port, have to cope with straying into occupied port nos and reserved ones. */ static int random_sock(struct server *s) { int fd; if ((fd = socket(s->source_addr.sa.sa_family, SOCK_DGRAM, 0)) != -1) { /* We need to set IPV6ONLY so we can use the same ports for IPv4 and IPV6, otherwise, in restriced port situations, we can end up with all our available ports in use for one address family, and the other address family cannot be used. */ if (s->source_addr.sa.sa_family == AF_INET6) { int opt = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1) { close(fd); return -1; } } if (local_bind(fd, &s->source_addr, s->interface, s->ifindex, 0)) return fd; /* don't log errors due to running out of available ports, we handle those. */ if (!sockaddr_isnull(&s->source_addr) || errno != EADDRINUSE) { if (s->interface[0] == 0) (void)prettyprint_addr(&s->source_addr, daemon->addrbuff); else safe_strncpy(daemon->addrbuff, s->interface, ADDRSTRLEN); my_syslog(LOG_ERR, _("failed to bind server socket to %s: %s"), daemon->addrbuff, strerror(errno)); } close(fd); } return -1; } /* compare source addresses and interface, serv2 can be null. */ static int server_isequal(const struct server *serv1, const struct server *serv2) { return (serv2 && serv2->ifindex == serv1->ifindex && sockaddr_isequal(&serv2->source_addr, &serv1->source_addr) && strncmp(serv2->interface, serv1->interface, IF_NAMESIZE) == 0); } /* fdlp points to chain of randomfds already in use by transaction. If there's already a suitable one, return it, else allocate a new one and add it to the list. Not leaking any resources in the face of allocation failures is rather convoluted here. Note that rfd->serv may be NULL, when a server goes away. */ int allocate_rfd(struct randfd_list **fdlp, struct server *serv) { static int finger = 0; int i, j = 0; int ports_full = 0; struct randfd_list **up, *rfl, *found, **found_link; struct randfd *rfd = NULL; int fd = 0; int ports_avail = 0; /* We can't have more randomsocks for this AF available than ports in our port range, so check that here, to avoid trying and failing to bind every port in local_bind(), called from random_sock(). The actual check is below when ports_avail != 0 */ if (daemon->max_port != 0) { ports_avail = daemon->max_port - daemon->min_port + 1; if (ports_avail >= SMALL_PORT_RANGE) ports_avail = 0; } /* If server has a pre-allocated fd, use that. */ if (serv->sfd) return serv->sfd->fd; /* existing suitable random port socket linked to this transaction? Find the last one in the list and count how many there are. */ for (found = NULL, found_link = NULL, i = 0, up = fdlp, rfl = *fdlp; rfl; up = &rfl->next, rfl = rfl->next) if (server_isequal(serv, rfl->rfd->serv)) { i++; found = rfl; found_link = up; } /* We have the maximum number for this query already. Promote the last one on the list to the head, to circulate them, and return it. */ if (found && i >= daemon->randport_limit) { *found_link = found->next; found->next = *fdlp; *fdlp = found; return found->rfd->fd; } /* check for all available ports in use. */ if (ports_avail != 0) { int ports_inuse; for (ports_inuse = 0, i = 0; i < daemon->numrrand; i++) if (daemon->randomsocks[i].refcount != 0 && daemon->randomsocks[i].serv->source_addr.sa.sa_family == serv->source_addr.sa.sa_family && ++ports_inuse >= ports_avail) { ports_full = 1; break; } } /* limit the number of sockets we have open to avoid starvation of (eg) TFTP. Once we have a reasonable number, randomness should be OK */ if (!ports_full) for (i = 0; i < daemon->numrrand; i++) if (daemon->randomsocks[i].refcount == 0) { if ((fd = random_sock(serv)) != -1) { rfd = &daemon->randomsocks[i]; rfd->serv = serv; rfd->fd = fd; rfd->refcount = 1; } break; } /* No good existing. Need new link. */ if ((rfl = daemon->rfl_spare)) daemon->rfl_spare = rfl->next; else if (!(rfl = whine_malloc(sizeof(struct randfd_list)))) { /* malloc failed, don't leak allocated sock */ if (rfd) { close(rfd->fd); rfd->refcount = 0; } return -1; } /* No free ones or cannot get new socket, grab an existing one */ if (!rfd) for (j = 0; j < daemon->numrrand; j++) { i = (j + finger) % daemon->numrrand; if (daemon->randomsocks[i].refcount != 0 && server_isequal(serv, daemon->randomsocks[i].serv) && daemon->randomsocks[i].refcount != 0xfffe) { struct randfd_list *rl; /* Don't pick one we already have. */ for (rl = *fdlp; rl; rl = rl->next) if (rl->rfd == &daemon->randomsocks[i]) break; if (!rl) { finger = i + 1; rfd = &daemon->randomsocks[i]; rfd->refcount++; break; } } } if (!rfd) /* should be when j == daemon->numrrand */ { struct randfd_list *rfl_poll; /* there are no free slots, and non with the same parameters we can piggy-back on. We're going to have to allocate a new temporary record, distinguished by refcount == 0xffff. This will exist in the frec randfd list, never be shared, and be freed when no longer in use. It will also be held on the daemon->rfl_poll list so the poll system can find it. */ if ((rfl_poll = daemon->rfl_spare)) daemon->rfl_spare = rfl_poll->next; else rfl_poll = whine_malloc(sizeof(struct randfd_list)); if (!rfl_poll || !(rfd = whine_malloc(sizeof(struct randfd))) || (fd = random_sock(serv)) == -1) { /* Don't leak anything we may already have */ rfl->next = daemon->rfl_spare; daemon->rfl_spare = rfl; if (rfl_poll) { rfl_poll->next = daemon->rfl_spare; daemon->rfl_spare = rfl_poll; } if (rfd) free(rfd); return -1; /* doom */ } /* Note rfd->serv not set here, since it's not reused */ rfd->fd = fd; rfd->refcount = 0xffff; /* marker for temp record */ rfl_poll->rfd = rfd; rfl_poll->next = daemon->rfl_poll; daemon->rfl_poll = rfl_poll; } rfl->rfd = rfd; rfl->next = *fdlp; *fdlp = rfl; return rfl->rfd->fd; } void free_rfds(struct randfd_list **fdlp) { struct randfd_list *tmp, *rfl, *poll, *next, **up; for (rfl = *fdlp; rfl; rfl = tmp) { if (rfl->rfd->refcount == 0xffff || --(rfl->rfd->refcount) == 0) close(rfl->rfd->fd); /* temporary overflow record */ if (rfl->rfd->refcount == 0xffff) { free(rfl->rfd); /* go through the link of all these by steam to delete. This list is expected to be almost always empty. */ for (poll = daemon->rfl_poll, up = &daemon->rfl_poll; poll; poll = next) { next = poll->next; if (poll->rfd == rfl->rfd) { *up = poll->next; poll->next = daemon->rfl_spare; daemon->rfl_spare = poll; } else up = &poll->next; } } tmp = rfl->next; rfl->next = daemon->rfl_spare; daemon->rfl_spare = rfl; } *fdlp = NULL; } static void free_frec(struct frec *f) { struct frec_src *last; /* add back to freelist if not the record builtin to every frec, also free any bigmaps they've been decorated with. */ for (last = f->frec_src.next; last && last->next; last = last->next) if (last->encode_bigmap) { free(last->encode_bigmap); last->encode_bigmap = NULL; } if (last) { /* final link in the chain loses bigmap too. */ if (last->encode_bigmap) { free(last->encode_bigmap); last->encode_bigmap = NULL; } last->next = daemon->free_frec_src; daemon->free_frec_src = f->frec_src.next; } f->frec_src.next = NULL; free_rfds(&f->rfds); f->sentto = NULL; f->flags = 0; if (f->stash) { blockdata_free(f->stash); f->stash = NULL; } #ifdef HAVE_DNSSEC /* Anything we're waiting on is pointless now, too */ if (f->blocking_query) { struct frec *n, **up; /* unlink outselves from the blocking query's dependents list. */ for (n = f->blocking_query->dependent, up = &f->blocking_query->dependent; n; n = n->next_dependent) if (n == f) { *up = n->next_dependent; break; } else up = &n->next_dependent; /* If we were the only/last dependent, free the blocking query too. */ if (!f->blocking_query->dependent) free_frec(f->blocking_query); } f->blocking_query = NULL; f->dependent = NULL; f->next_dependent = NULL; #endif } /* Impose an absolute 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, and don'y free any records. This is set when allocating for DNSSEC to avoid cutting off the branch we are sitting on. */ static struct frec *get_new_frec(time_t now, struct server *master, int force) { struct frec *f, *oldest, *target; int count; #ifdef HAVE_DNSSEC static int next_uid = 0; #endif /* look for free records, garbage collect old records and count number in use by our server-group. */ for (f = daemon->frec_list, oldest = NULL, target = NULL, count = 0; f; f = f->next) { if (!f->sentto) target = f; else { #ifdef HAVE_DNSSEC /* Don't free DNSSEC sub-queries here, as we may end up with dangling references to them. They'll go when their "real" query is freed. */ if (!f->dependent && !force) #endif { if (difftime(now, f->time) >= 4*TIMEOUT) { daemon->metrics[METRIC_DNS_UNANSWERED_QUERY]++; free_frec(f); target = f; } else if (!oldest || difftime(f->time, oldest->time) <= 0) oldest = f; } } if (f->sentto && ((int)difftime(now, f->time)) < TIMEOUT && server_samegroup(f->sentto, master)) count++; } if (!force && count >= daemon->ftabsize) { query_full(now, master->domain); return NULL; } if (!target && oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT) { /* can't find empty one, use oldest if there is one and it's older than timeout */ daemon->metrics[METRIC_DNS_UNANSWERED_QUERY]++; free_frec(oldest); target = oldest; } if (!target && (target = (struct frec *)whine_malloc(sizeof(struct frec)))) { target->next = daemon->frec_list; daemon->frec_list = target; } if (target) { target->time = now; target->forward_delay = daemon->fast_retry_time; #ifdef HAVE_DNSSEC target->uid = next_uid++; #endif } return target; } static void query_full(time_t now, char *domain) { static time_t last_log = 0; if ((int)difftime(now, last_log) > 5) { last_log = now; if (!domain || strlen(domain) == 0) my_syslog(LOG_WARNING, _("Maximum number of concurrent DNS queries reached (max: %d)"), daemon->ftabsize); else my_syslog(LOG_WARNING, _("Maximum number of concurrent DNS queries to %s reached (max: %d)"), domain, daemon->ftabsize); } } static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int flags, int flagmask) { struct frec *f; struct dns_header *header; int compare_mode = EXTR_NAME_COMPARE; /* Only compare case-sensitive when matching frec to a received answer, NOT when looking for a duplicated question. */ if (flags & FREC_ANSWER) { flags &= ~FREC_ANSWER; if (!option_bool(OPT_NO_0x20) && option_bool(OPT_DO_0x20)) compare_mode = EXTR_NAME_NOCASE; } for (f = daemon->frec_list; f; f = f->next) if (f->sentto && (f->flags & flagmask) == flags && (f->new_id == id || id == -1) && (header = blockdata_retrieve(f->stash, f->stash_len, NULL))) { unsigned char *p = (unsigned char *)(header+1); int hclass, hrrtype, rc; /* Case sensitive compare for DNS-0x20 encoding. */ if ((rc = extract_name(header, f->stash_len, &p, target, compare_mode, 4))) { GETSHORT(hrrtype, p); GETSHORT(hclass, p); /* type checked by flags for DNSSEC queries. */ if (rrtype != -1 && rrtype != hrrtype) continue; if (class != hclass) continue; } if (rc != 1) { static int warned = 0; if (rc == 3 && !warned) { my_syslog(LOG_WARNING, _("Case mismatch in DNS reply - check bit 0x20 encoding.")); warned = 1; } continue; } return f; } return NULL; } /* Send query packet again, if we can. */ void resend_query(void) { if (daemon->srv_save) server_send(daemon->srv_save, daemon->fd_save, daemon->packet, daemon->packet_len, 0); } /* A server record is going away, remove references to it */ void server_gone(struct server *server) { struct frec *f; int i; for (f = daemon->frec_list; f; f = f->next) if (f->sentto && f->sentto == server) free_frec(f); /* If any random socket refers to this server, NULL the reference. No more references to the socket will be created in the future. */ for (i = 0; i < daemon->numrrand; i++) if (daemon->randomsocks[i].refcount != 0 && daemon->randomsocks[i].serv == server) daemon->randomsocks[i].serv = NULL; if (daemon->srv_save == server) daemon->srv_save = NULL; } /* return unique random ids. */ static unsigned short get_id(void) { unsigned short ret = 0; struct frec *f; while (1) { ret = rand16(); /* ensure id is unique. */ for (f = daemon->frec_list; f; f = f->next) if (f->sentto && f->new_id == ret) break; if (!f) return ret; } } dnsmasq-2.91/src/outpacket.c0000664000175000017500000000436114765043257014250 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_DHCP6 static size_t outpacket_counter; void end_opt6(int container) { uint8_t *p = (uint8_t *)daemon->outpacket.iov_base + container + 2; u16 len = outpacket_counter - container - 4 ; PUTSHORT(len, p); } void reset_counter(void) { /* Clear out buffer when starting from beginning */ if (daemon->outpacket.iov_base) memset(daemon->outpacket.iov_base, 0, daemon->outpacket.iov_len); save_counter(0); } int save_counter(int newval) { int ret = outpacket_counter; if (newval != -1) outpacket_counter = newval; return ret; } void *expand(size_t headroom) { uint8_t *ret; if (expand_buf(&daemon->outpacket, outpacket_counter + headroom)) { ret = (uint8_t *)daemon->outpacket.iov_base + outpacket_counter; outpacket_counter += headroom; return ret; } return NULL; } int new_opt6(int opt) { int ret = outpacket_counter; unsigned char *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) { unsigned char *p; if ((p = expand(4))) PUTLONG(val, p); } void put_opt6_short(unsigned int val) { uint8_t *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 dnsmasq-2.91/src/ip6addr.h0000664000175000017500000000275114765043257013610 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #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) dnsmasq-2.91/src/crypto.c0000664000175000017500000003117014765043257013567 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #if defined(HAVE_DNSSEC) /* Minimal version of nettle */ /* bignum.h includes version.h and works on earlier releases of nettle which don't have version.h */ #include #if !defined(NETTLE_VERSION_MAJOR) # define NETTLE_VERSION_MAJOR 2 # define NETTLE_VERSION_MINOR 0 #endif #define MIN_VERSION(major, minor) ((NETTLE_VERSION_MAJOR == (major) && NETTLE_VERSION_MINOR >= (minor)) || \ (NETTLE_VERSION_MAJOR > (major))) #include #include #include #if MIN_VERSION(3, 1) #include #endif #if MIN_VERSION(3, 6) # include #endif #if MIN_VERSION(3, 1) /* Implement a "hash-function" to the nettle API, which simply returns the input data, concatenated into a single, statically maintained, buffer. Used for the EdDSA sigs, which operate on the whole message, rather than a digest. */ struct null_hash_digest { uint8_t *buff; size_t len; }; struct null_hash_ctx { size_t len; }; static size_t null_hash_buff_sz = 0; static uint8_t *null_hash_buff = NULL; #define BUFF_INCR 128 static void null_hash_init(void *ctx) { ((struct null_hash_ctx *)ctx)->len = 0; } static void null_hash_update(void *ctxv, size_t length, const uint8_t *src) { struct null_hash_ctx *ctx = ctxv; size_t new_len = ctx->len + length; if (new_len > null_hash_buff_sz) { uint8_t *new; if (!(new = whine_malloc(new_len + BUFF_INCR))) return; if (null_hash_buff) { if (ctx->len != 0) memcpy(new, null_hash_buff, ctx->len); free(null_hash_buff); } null_hash_buff_sz = new_len + BUFF_INCR; null_hash_buff = new; } memcpy(null_hash_buff + ctx->len, src, length); ctx->len += length; } static void null_hash_digest(void *ctx, size_t length, uint8_t *dst) { (void)length; ((struct null_hash_digest *)dst)->buff = null_hash_buff; ((struct null_hash_digest *)dst)->len = ((struct null_hash_ctx *)ctx)->len; } static struct nettle_hash null_hash = { "null_hash", sizeof(struct null_hash_ctx), sizeof(struct null_hash_digest), 0, (nettle_hash_init_func *) null_hash_init, (nettle_hash_update_func *) null_hash_update, (nettle_hash_digest_func *) null_hash_digest }; #endif /* MIN_VERSION(3, 1) */ /* expand ctx and digest memory allocations if necessary and init hash function */ 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, size_t digest_len, int algo) { unsigned char *p; size_t exp_len; static struct rsa_public_key *key = NULL; static mpz_t sig_mpz; (void)digest_len; 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 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_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 !MIN_VERSION(3, 4) #define nettle_get_secp_256r1() (&nettle_secp_256r1) #define nettle_get_secp_384r1() (&nettle_secp_384r1) #endif 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_get_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_get_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); } #if MIN_VERSION(3, 6) static int dnsmasq_gostdsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len, unsigned char *digest, size_t digest_len, int algo) { unsigned char *p; static struct ecc_point *gost_key = NULL; static mpz_t x, y; static struct dsa_signature *sig_struct; if (algo != 12 || sig_len != 64 || key_len != 64 || !(p = blockdata_retrieve(key_data, key_len, NULL))) return 0; if (!sig_struct) { if (!(sig_struct = whine_malloc(sizeof(struct dsa_signature))) || !(gost_key = whine_malloc(sizeof(struct ecc_point)))) return 0; nettle_dsa_signature_init(sig_struct); nettle_ecc_point_init(gost_key, nettle_get_gost_gc256b()); mpz_init(x); mpz_init(y); } mpz_import(x, 32, -1, 1, 0, 0, p); mpz_import(y, 32, -1, 1, 0, 0, p + 32); if (!ecc_point_set(gost_key, x, y)) return 0; mpz_import(sig_struct->s, 32, 1, 1, 0, 0, sig); mpz_import(sig_struct->r, 32, 1, 1, 0, 0, sig + 32); return nettle_gostdsa_verify(gost_key, digest_len, digest, sig_struct); } #endif #if MIN_VERSION(3, 1) static int dnsmasq_eddsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len, unsigned char *digest, size_t digest_len, int algo) { unsigned char *p; if (digest_len != sizeof(struct null_hash_digest) || !(p = blockdata_retrieve(key_data, key_len, NULL))) return 0; /* The "digest" returned by the null_hash function is simply a struct null_hash_digest which has a pointer to the actual data and a length, because the buffer may need to be extended during "hashing". */ switch (algo) { case 15: if (key_len != ED25519_KEY_SIZE || sig_len != ED25519_SIGNATURE_SIZE) return 0; return ed25519_sha512_verify(p, ((struct null_hash_digest *)digest)->len, ((struct null_hash_digest *)digest)->buff, sig); #if MIN_VERSION(3, 6) case 16: if (key_len != ED448_KEY_SIZE || sig_len != ED448_SIGNATURE_SIZE) return 0; return ed448_shake256_verify(p, ((struct null_hash_digest *)digest)->len, ((struct null_hash_digest *)digest)->buff, sig); #endif } return 0; } #endif static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len, unsigned char *digest, size_t digest_len, int algo) { /* Ensure at runtime that we have support for this digest */ if (!hash_find(algo_digest_name(algo))) return NULL; /* This switch defines which sig algorithms we support, can't introspect Nettle for that. */ switch (algo) { case 5: case 7: case 8: case 10: return dnsmasq_rsa_verify; #if MIN_VERSION(3, 6) case 12: return dnsmasq_gostdsa_verify; #endif case 13: case 14: return dnsmasq_ecdsa_verify; #if MIN_VERSION(3, 1) case 15: return dnsmasq_eddsa_verify; #endif #if MIN_VERSION(3, 6) case 16: return dnsmasq_eddsa_verify; #endif } return NULL; } 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) { int (*func)(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len, unsigned char *digest, size_t digest_len, int algo); func = verify_func(algo); if (!func) return 0; return (*func)(key_data, key_len, sig, sig_len, digest, digest_len, algo); } /* Note the ds_digest_name(), algo_digest_name() and nsec3_digest_name() define which algo numbers we support. If algo_digest_name() returns non-NULL for an algorithm number, we assume that algorithm is supported by verify(). */ /* http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */ char *ds_digest_name(int digest) { switch (digest) { case 1: return "sha1"; case 2: return "sha256"; #if MIN_VERSION(3, 6) case 3: return "gosthash94cp"; #endif case 4: return "sha384"; default: return NULL; } } /* http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */ char *algo_digest_name(int algo) { switch (algo) { case 1: return NULL; /* RSA/MD5 - Must Not Implement. RFC 6944 para 2.3. */ case 2: return NULL; /* Diffie-Hellman */ case 3: return NULL; ; /* DSA/SHA1 - Must Not Implement. RFC 8624 section 3.1 */ case 5: return "sha1"; /* RSA/SHA1 */ case 6: return NULL; /* DSA-NSEC3-SHA1 - Must Not Implement. RFC 8624 section 3.1 */ case 7: return "sha1"; /* RSASHA1-NSEC3-SHA1 */ case 8: return "sha256"; /* RSA/SHA-256 */ case 10: return "sha512"; /* RSA/SHA-512 */ #if MIN_VERSION(3, 6) case 12: return "gosthash94cp"; /* ECC-GOST */ #endif case 13: return "sha256"; /* ECDSAP256SHA256 */ case 14: return "sha384"; /* ECDSAP384SHA384 */ #if MIN_VERSION(3, 1) case 15: return "null_hash"; /* ED25519 */ # if MIN_VERSION(3, 6) case 16: return "null_hash"; /* ED448 */ # endif #endif default: return NULL; } } /* http://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml */ char *nsec3_digest_name(int digest) { switch (digest) { case 1: return "sha1"; default: return NULL; } } /* Find pointer to correct hash function in nettle library */ const struct nettle_hash *hash_find(char *name) { if (!name) return NULL; #if MIN_VERSION(3,1) && defined(HAVE_DNSSEC) /* We provide a "null" hash which returns the input data as digest. */ if (strcmp(null_hash.name, name) == 0) return &null_hash; #endif /* libnettle >= 3.4 provides nettle_lookup_hash() which avoids nasty ABI incompatibilities if sizeof(nettle_hashes) changes between library versions. */ #if MIN_VERSION(3, 4) return nettle_lookup_hash(name); #else { int i; for (i = 0; nettle_hashes[i]; i++) if (strcmp(nettle_hashes[i]->name, name) == 0) return nettle_hashes[i]; } return NULL; #endif } #endif /* defined(HAVE_DNSSEC) */ dnsmasq-2.91/src/option.c0000664000175000017500000051520114765043257013561 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ /* define this to get facilitynames */ #define SYSLOG_NAMES #include "dnsmasq.h" #include static volatile int mem_recover = 0; static jmp_buf mem_jmp; static int one_file(char *file, int hard_opt); /* Solaris headers don't have facility names. */ #ifdef HAVE_SOLARIS_NETWORK static const struct { char *c_name; unsigned int c_val; } facilitynames[] = { { "kern", LOG_KERN }, { "user", LOG_USER }, { "mail", LOG_MAIL }, { "daemon", LOG_DAEMON }, { "auth", LOG_AUTH }, { "syslog", LOG_SYSLOG }, { "lpr", LOG_LPR }, { "news", LOG_NEWS }, { "uucp", LOG_UUCP }, { "audit", LOG_AUDIT }, { "cron", LOG_CRON }, { "local0", LOG_LOCAL0 }, { "local1", LOG_LOCAL1 }, { "local2", LOG_LOCAL2 }, { "local3", LOG_LOCAL3 }, { "local4", LOG_LOCAL4 }, { "local5", LOG_LOCAL5 }, { "local6", LOG_LOCAL6 }, { "local7", LOG_LOCAL7 }, { NULL, 0 } }; #endif #ifndef HAVE_GETOPT_LONG struct myoption { const char *name; int has_arg; int *flag; int val; }; #endif #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_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 #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 #define LOPT_MAXPORT 345 #define LOPT_CPE_ID 346 #define LOPT_SCRIPT_ARP 347 #define LOPT_DHCPTTL 348 #define LOPT_TFTP_MTU 349 #define LOPT_REPLY_DELAY 350 #define LOPT_RAPID_COMMIT 351 #define LOPT_DUMPFILE 352 #define LOPT_DUMPMASK 353 #define LOPT_UBUS 354 #define LOPT_NAME_MATCH 355 #define LOPT_CAA 356 #define LOPT_SHARED_NET 357 #define LOPT_IGNORE_CLID 358 #define LOPT_SINGLE_PORT 359 #define LOPT_SCRIPT_TIME 360 #define LOPT_PXE_VENDOR 361 #define LOPT_DYNHOST 362 #define LOPT_LOG_DEBUG 363 #define LOPT_UMBRELLA 364 #define LOPT_CMARK_ALST_EN 365 #define LOPT_CMARK_ALST 366 #define LOPT_QUIET_TFTP 367 #define LOPT_NFTSET 368 #define LOPT_FILTER_A 369 #define LOPT_FILTER_AAAA 370 #define LOPT_STRIP_SBNET 371 #define LOPT_STRIP_MAC 372 #define LOPT_CONF_OPT 373 #define LOPT_CONF_SCRIPT 374 #define LOPT_RANDPORT_LIM 375 #define LOPT_FAST_RETRY 376 #define LOPT_STALE_CACHE 377 #define LOPT_NORR 378 #define LOPT_NO_IDENT 379 #define LOPT_CACHE_RR 380 #define LOPT_FILTER_RR 381 #define LOPT_NO_DHCP6 382 #define LOPT_NO_DHCP4 383 #define LOPT_MAX_PROCS 384 #define LOPT_DNSSEC_LIMITS 385 #define LOPT_PXE_OPT 386 #define LOPT_NO_ENCODE 387 #define LOPT_DO_ENCODE 388 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = #else static const struct myoption opts[] = #endif { { "version", 0, 0, 'v' }, { "no-hosts", 0, 0, 'h' }, { "no-poll", 0, 0, 'n' }, { "help", 0, 0, 'w' }, { "no-daemon", 0, 0, 'd' }, { "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' }, { "port", 1, 0, 'p' }, { "dhcp-leasefile", 2, 0, 'l' }, { "dhcp-lease", 1, 0, 'l' }, { "dhcp-host", 1, 0, 'G' }, { "dhcp-range", 1, 0, 'F' }, { "dhcp-option", 1, 0, 'O' }, { "dhcp-boot", 1, 0, 'M' }, { "domain", 1, 0, 's' }, { "domain-suffix", 1, 0, 's' }, { "interface", 1, 0, 'i' }, { "listen-address", 1, 0, 'a' }, { "local-service", 2, 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' }, { "filter-A", 0, 0, LOPT_FILTER_A }, { "filter-AAAA", 0, 0, LOPT_FILTER_AAAA }, { "filter-rr", 1, 0, LOPT_FILTER_RR }, { "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' }, { "conf-script", 1, 0, LOPT_CONF_SCRIPT }, { "no-resolv", 0, 0, 'R' }, { "expand-hosts", 0, 0, 'E' }, { "localmx", 0, 0, 'L' }, { "local-ttl", 1, 0, 'T' }, { "no-negcache", 0, 0, 'N' }, { "no-round-robin", 0, 0, LOPT_NORR }, { "no-0x20-encode", 0, 0, LOPT_NO_ENCODE }, { "do-0x20-encode", 0, 0, LOPT_DO_ENCODE }, { "cache-rr", 1, 0, LOPT_CACHE_RR }, { "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' }, { "no-dhcpv4-interface", 1, 0, LOPT_NO_DHCP4 }, { "no-dhcpv6-interface", 1, 0, LOPT_NO_DHCP6 }, { "domain-needed", 0, 0, 'D' }, { "dhcp-lease-max", 1, 0, 'X' }, { "bind-interfaces", 0, 0, 'z' }, { "read-ethers", 0, 0, 'Z' }, { "alias", 1, 0, 'V' }, { "dhcp-vendorclass", 1, 0, 'U' }, { "dhcp-userclass", 1, 0, 'j' }, { "dhcp-ignore", 1, 0, 'J' }, { "edns-packet-max", 1, 0, 'P' }, { "keep-in-foreground", 0, 0, 'k' }, { "dhcp-authoritative", 0, 0, 'K' }, { "srv-host", 1, 0, 'W' }, { "localise-queries", 0, 0, 'y' }, { "txt-record", 1, 0, 'Y' }, { "caa-record", 1, 0 , LOPT_CAA }, { "dns-rr", 1, 0, LOPT_RR }, { "enable-dbus", 2, 0, '1' }, { "enable-ubus", 2, 0, LOPT_UBUS }, { "bootp-dynamic", 2, 0, '3' }, { "dhcp-mac", 1, 0, '4' }, { "no-ping", 0, 0, '5' }, { "dhcp-script", 1, 0, '6' }, { "conf-dir", 1, 0, '7' }, { "log-facility", 1, 0 ,'8' }, { "leasefile-ro", 0, 0, '9' }, { "script-on-renewal", 0, 0, LOPT_SCRIPT_TIME}, { "dns-forward-max", 1, 0, '0' }, { "clear-on-reload", 0, 0, LOPT_RELOAD }, { "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", 2, 0, LOPT_APREF }, { "tftp-root", 1, 0, LOPT_PREFIX }, { "tftp-max", 1, 0, LOPT_TFTP_MAX }, { "tftp-mtu", 1, 0, LOPT_TFTP_MTU }, { "tftp-lowercase", 0, 0, LOPT_TFTP_LC }, { "tftp-single-port", 0, 0, LOPT_SINGLE_PORT }, { "ptr-record", 1, 0, LOPT_PTR }, { "naptr-record", 1, 0, LOPT_NAPTR }, { "bridge-interface", 1, 0 , LOPT_BRIDGE }, { "shared-network", 1, 0, LOPT_SHARED_NET }, { "dhcp-option-force", 1, 0, LOPT_FORCE }, { "dhcp-option-pxe", 1, 0, LOPT_PXE_OPT }, { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK }, { "log-dhcp", 0, 0, LOPT_LOG_OPTS }, { "log-async", 2, 0, LOPT_MAX_LOGS }, { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT }, { "dhcp-remoteid", 1, 0, LOPT_REMOTE }, { "dhcp-subscrid", 1, 0, LOPT_SUBSCR }, { "dhcp-pxe-vendor", 1, 0, LOPT_PXE_VENDOR }, { "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 }, { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND }, { "all-servers", 0, 0, LOPT_NOLAST }, { "dhcp-match", 1, 0, LOPT_MATCH }, { "dhcp-name-match", 1, 0, LOPT_NAME_MATCH }, { "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 }, { "max-port", 1, 0, LOPT_MAXPORT }, { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN }, { "cname", 1, 0, LOPT_CNAME }, { "pxe-prompt", 1, 0, LOPT_PXE_PROMT }, { "pxe-service", 1, 0, LOPT_PXE_SERV }, { "test", 0, 0, LOPT_TEST }, { "tag-if", 1, 0, LOPT_TAG_IF }, { "dhcp-proxy", 2, 0, LOPT_PROXY }, { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES }, { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND }, { "add-mac", 2, 0, LOPT_ADD_MAC }, { "strip-mac", 0, 0, LOPT_STRIP_MAC }, { "add-subnet", 2, 0, LOPT_ADD_SBNET }, { "strip-subnet", 0, 0, LOPT_STRIP_SBNET }, { "add-cpe-id", 1, 0 , LOPT_CPE_ID }, { "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 }, { "nftset", 1, 0, LOPT_NFTSET }, { "connmark-allowlist-enable", 2, 0, LOPT_CMARK_ALST_EN }, { "connmark-allowlist", 1, 0, LOPT_CMARK_ALST }, { "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", 2, 0, LOPT_DNSSEC_CHECK }, { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME }, { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP }, { "dnssec-limits", 1, 0, LOPT_DNSSEC_LIMITS }, { "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 }, { "script-arp", 0, 0, LOPT_SCRIPT_ARP }, { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL }, { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY }, { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT }, { "dumpfile", 1, 0, LOPT_DUMPFILE }, { "dumpmask", 1, 0, LOPT_DUMPMASK }, { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID }, { "dynamic-host", 1, 0, LOPT_DYNHOST }, { "log-debug", 0, 0, LOPT_LOG_DEBUG }, { "umbrella", 2, 0, LOPT_UMBRELLA }, { "quiet-tftp", 0, 0, LOPT_QUIET_TFTP }, { "port-limit", 1, 0, LOPT_RANDPORT_LIM }, { "fast-dns-retry", 2, 0, LOPT_FAST_RETRY }, { "use-stale-cache", 2, 0 , LOPT_STALE_CACHE }, { "no-ident", 0, 0, LOPT_NO_IDENT }, { "max-tcp-connections", 1, 0, LOPT_MAX_PROCS }, { NULL, 0, 0, 0 } }; #define ARG_DUP OPT_LAST #define ARG_ONE OPT_LAST + 1 #define ARG_USED_CL OPT_LAST + 2 #define ARG_USED_FILE OPT_LAST + 3 static struct { int opt; unsigned int rept; char * const flagdesc; char * const desc; char * const arg; } usage[] = { { 'a', ARG_DUP, "", gettext_noop("Specify local address(es) to listen on."), NULL }, { 'A', ARG_DUP, "//", 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, "", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL }, { 'c', ARG_ONE, "", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" }, { 'C', ARG_DUP, "", 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 }, { LOPT_FILTER_A, ARG_DUP, NULL, gettext_noop("Don't include IPv4 addresses in DNS answers."), NULL }, { LOPT_FILTER_AAAA, ARG_DUP, NULL, gettext_noop("Don't include IPv6 addresses in DNS answers."), NULL }, { LOPT_FILTER_RR, ARG_DUP, "", gettext_noop("Don't include resource records of the given type in DNS answers."), NULL }, { 'F', ARG_DUP, ",...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL }, { 'g', ARG_ONE, "", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP }, { 'G', ARG_DUP, "", gettext_noop("Set address or hostname for a specified machine."), NULL }, { LOPT_DHCP_HOST, ARG_DUP, "", gettext_noop("Read DHCP host specs from file."), NULL }, { LOPT_DHCP_OPTS, ARG_DUP, "", gettext_noop("Read DHCP option specs from file."), NULL }, { LOPT_DHCP_INOTIFY, ARG_DUP, "", gettext_noop("Read DHCP host specs from a directory."), NULL }, { LOPT_DHOPT_INOTIFY, ARG_DUP, "", 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, "", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE }, { LOPT_HOST_INOTIFY, ARG_DUP, "", gettext_noop("Read hosts files from a directory."), NULL }, { 'i', ARG_DUP, "", gettext_noop("Specify interface(s) to listen on."), NULL }, { 'I', ARG_DUP, "", gettext_noop("Specify interface(s) NOT to listen on.") , NULL }, { 'j', ARG_DUP, "set:,", gettext_noop("Map DHCP user class to tag."), NULL }, { LOPT_CIRCUIT, ARG_DUP, "set:,", gettext_noop("Map RFC3046 circuit-id to tag."), NULL }, { LOPT_REMOTE, ARG_DUP, "set:,", gettext_noop("Map RFC3046 remote-id to tag."), NULL }, { LOPT_SUBSCR, ARG_DUP, "set:,", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL }, { LOPT_PXE_VENDOR, ARG_DUP, "[,...]", gettext_noop("Specify vendor class to match for PXE requests."), NULL }, { 'J', ARG_DUP, "tag:...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL }, { LOPT_BROADCAST, ARG_DUP, "[=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, "", 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, ",,", gettext_noop("Specify an MX record."), NULL }, { 'M', ARG_DUP, "", 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 }, { LOPT_STALE_CACHE, ARG_ONE, "[=]", gettext_noop("Use expired cache data for faster reply."), NULL }, { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE }, { 'O', ARG_DUP, "", gettext_noop("Specify options to be sent to DHCP clients."), NULL }, { LOPT_FORCE, ARG_DUP, "", gettext_noop("DHCP option sent even if the client does not request it."), NULL}, { LOPT_PXE_OPT, ARG_DUP, "", gettext_noop("DHCP option sent only to PXE clients."), NULL}, { 'p', ARG_ONE, "", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL }, { 'P', ARG_ONE, "", 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, "", gettext_noop("Force the originating port for upstream DNS queries."), NULL }, { LOPT_RANDPORT_LIM, ARG_ONE, "#ports", gettext_noop("Set maximum number of random originating ports for a query."), NULL }, { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL }, { 'r', ARG_DUP, "", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE }, { LOPT_SERVERS_FILE, ARG_ONE, "", gettext_noop("Specify path to file with server= options"), NULL }, { 'S', ARG_DUP, "//", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL }, { LOPT_REV_SERV, ARG_DUP, "/,", gettext_noop("Specify address of upstream servers for reverse address queries"), NULL }, { LOPT_LOCAL, ARG_DUP, "//", gettext_noop("Never forward queries to specified domains."), NULL }, { 's', ARG_DUP, "[,]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL }, { 't', ARG_ONE, "", gettext_noop("Specify default target in an MX record."), NULL }, { 'T', ARG_ONE, "", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL }, { LOPT_NEGTTL, ARG_ONE, "", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL }, { LOPT_MAXTTL, ARG_ONE, "", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL }, { LOPT_MAXCTTL, ARG_ONE, "", gettext_noop("Specify time-to-live ceiling for cache."), NULL }, { LOPT_MINCTTL, ARG_ONE, "", gettext_noop("Specify time-to-live floor for cache."), NULL }, { LOPT_FAST_RETRY, ARG_ONE, "", gettext_noop("Retry DNS queries after this many milliseconds."), NULL}, { 'u', ARG_ONE, "", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER }, { 'U', ARG_DUP, "set:,", gettext_noop("Map DHCP vendor class to tag."), NULL }, { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL }, { 'V', ARG_DUP, ",,", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL }, { 'W', ARG_DUP, ",,...", gettext_noop("Specify a SRV record."), NULL }, { 'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp or --help dhcp6 for known DHCP options."), NULL }, { 'x', ARG_ONE, "", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE }, { 'X', ARG_ONE, "", 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, ",[,,", gettext_noop("Specify PTR DNS record."), NULL }, { LOPT_INTNAME, ARG_DUP, ",", 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', ARG_ONE, "[=]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL }, { LOPT_UBUS, ARG_ONE, "[=]", gettext_noop("Enable the UBus interface."), NULL }, { '2', ARG_DUP, "", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL }, { LOPT_NO_DHCP6, ARG_DUP, "", gettext_noop("Do not provide DHCPv6 on this interface."), NULL }, { LOPT_NO_DHCP4, ARG_DUP, "", gettext_noop("Do not provide DHCPv4 on this interface."), NULL }, { '3', ARG_DUP, "[=tag:]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL }, { '4', ARG_DUP, "set:,", gettext_noop("Map MAC address (with wildcards) to option set."), NULL }, { LOPT_BRIDGE, ARG_DUP, ",..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL }, { LOPT_SHARED_NET, ARG_DUP, "|,", gettext_noop("Specify extra networks sharing a broadcast domain for DHCP"), NULL}, { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL }, { '6', ARG_ONE, "", 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, "", gettext_noop("Run lease-change scripts as this user."), NULL }, { LOPT_SCRIPT_ARP, OPT_SCRIPT_ARP, NULL, gettext_noop("Call dhcp-script with changes to local ARP table."), NULL }, { '7', ARG_DUP, "", gettext_noop("Read configuration from all the files in this directory."), NULL }, { LOPT_CONF_SCRIPT, ARG_DUP, "", gettext_noop("Execute file and read configuration from stdin."), NULL }, { '8', ARG_ONE, "|", 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, "", 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:]...", 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, "[=[,]]", gettext_noop("Enable integrated read-only TFTP server."), NULL }, { LOPT_PREFIX, ARG_DUP, "[,]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL }, { LOPT_APREF, ARG_DUP, "[=ip|mac]", gettext_noop("Add client IP or hardware 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_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, "", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" }, { LOPT_TFTP_MTU, ARG_ONE, "", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL }, { 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, ",", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL }, { LOPT_SINGLE_PORT, OPT_SINGLE_PORT, NULL, gettext_noop("Use only one port for TFTP server."), NULL }, { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL }, { LOPT_MAX_LOGS, ARG_ONE, "[=]", 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, "//", 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:,", gettext_noop("Set tag if client includes matching option in request."), NULL }, { LOPT_NAME_MATCH, ARG_DUP, "set:,[*]", gettext_noop("Set tag if client provides given name."), NULL }, { LOPT_ALTPORT, ARG_ONE, "[=]", gettext_noop("Use alternative ports for DHCP."), NULL }, { LOPT_NAPTR, ARG_DUP, ",", gettext_noop("Specify NAPTR DNS record."), NULL }, { LOPT_MINPORT, ARG_ONE, "", gettext_noop("Specify lowest port available for DNS query transmission."), NULL }, { LOPT_MAXPORT, ARG_ONE, "", gettext_noop("Specify highest 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:]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL}, { LOPT_PROXY, ARG_DUP, "[=]...", gettext_noop("Use these DHCP relays as full proxies."), NULL }, { LOPT_RELAY, ARG_DUP, ",[,]", gettext_noop("Relay DHCP requests to a remote server"), NULL}, { LOPT_CNAME, ARG_DUP, ",[,]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL }, { LOPT_PXE_PROMT, ARG_DUP, ",[]", gettext_noop("Prompt to send to PXE clients."), NULL }, { LOPT_PXE_SERV, ARG_DUP, "", gettext_noop("Boot service for PXE menu."), NULL }, { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL }, { LOPT_ADD_MAC, ARG_DUP, "[=base64|text]", gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL }, { LOPT_STRIP_MAC, OPT_STRIP_MAC, NULL, gettext_noop("Strip MAC information from queries."), NULL }, { LOPT_ADD_SBNET, ARG_ONE, "[,]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL }, { LOPT_STRIP_SBNET, OPT_STRIP_ECS, NULL, gettext_noop("Strip ECS information from queries."), NULL }, { LOPT_CPE_ID, ARG_ONE, "", gettext_noop("Add client identification to forwarded DNS queries."), NULL }, { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL }, { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL }, { LOPT_IGNORE_CLID, OPT_IGNORE_CLID, NULL, gettext_noop("Ignore client identifier option sent by 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, ",", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL }, { LOPT_HOST_REC, ARG_DUP, ",
    [,]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL }, { LOPT_DYNHOST, ARG_DUP, ",[][,],", gettext_noop("Specify host record in interface subnet"), NULL }, { LOPT_CAA, ARG_DUP, ",,,", gettext_noop("Specify certification authority authorization record"), NULL }, { LOPT_RR, ARG_DUP, ",,[]", 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, ",", gettext_noop("Export local names to global DNS"), NULL }, { LOPT_AUTHZONE, ARG_DUP, ",[...]", gettext_noop("Domain to export to global DNS"), NULL }, { LOPT_AUTHTTL, ARG_ONE, "", gettext_noop("Set TTL for authoritative replies"), NULL }, { LOPT_AUTHSOA, ARG_ONE, "[,...]", gettext_noop("Set authoritative zone information"), NULL }, { LOPT_AUTHSFS, ARG_DUP, "[,...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL }, { LOPT_AUTHPEER, ARG_DUP, "[,...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL }, { LOPT_IPSET, ARG_DUP, "/[/...]/...", gettext_noop("Specify ipsets to which matching domains should be added"), NULL }, { LOPT_NFTSET, ARG_DUP, "/[/...]/...", gettext_noop("Specify nftables sets to which matching domains should be added"), NULL }, { LOPT_CMARK_ALST_EN, ARG_ONE, "[=]", gettext_noop("Enable filtering of DNS queries with connection-track marks."), NULL }, { LOPT_CMARK_ALST, ARG_DUP, "[/][,[/...]]", gettext_noop("Set allowed DNS patterns for a connection-track mark."), NULL }, { LOPT_SYNTH, ARG_DUP, ",,[]", 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, ",[,]...", gettext_noop("Specify trust anchor key digest."), NULL }, { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL }, { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL }, { LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL }, { LOPT_DNSSEC_STAMP, ARG_ONE, "", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL }, { LOPT_DNSSEC_LIMITS, ARG_ONE, ",..", gettext_noop("Set resource limits for DNSSEC validation"), NULL }, { LOPT_RA_PARAM, ARG_DUP, ",[mtu:||off,][,][,]", gettext_noop("Set MTU, 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_LOG_DEBUG, OPT_LOG_DEBUG, NULL, gettext_noop("Log debugging information."), NULL }, { LOPT_LOCAL_SERVICE, ARG_ONE, 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, "", gettext_noop("Ignore DNS responses containing ipaddr."), NULL }, { LOPT_DHCPTTL, ARG_ONE, "", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL }, { LOPT_REPLY_DELAY, ARG_ONE, "", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL }, { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL }, { LOPT_DUMPFILE, ARG_ONE, "", gettext_noop("Path to debug packet dump file."), NULL }, { LOPT_DUMPMASK, ARG_ONE, "", gettext_noop("Mask which packets to dump."), NULL }, { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL }, { LOPT_UMBRELLA, ARG_ONE, "[=]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL }, { LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL }, { LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL }, { LOPT_NO_ENCODE, OPT_NO_0x20, NULL, gettext_noop("Suppress DNS bit 0x20 encoding."), NULL }, { LOPT_DO_ENCODE, OPT_DO_0x20, NULL, gettext_noop("Enable DNS bit 0x20 encoding."), NULL }, { LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL }, { LOPT_CACHE_RR, ARG_DUP, "", gettext_noop("Cache this DNS resource record type."), NULL }, { LOPT_MAX_PROCS, ARG_ONE, "", gettext_noop("Maximum number of concurrent tcp connections."), NULL }, { 0, 0, NULL, NULL, NULL } }; /* We hide metacharacters 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 unhide_metas repeatedly on string without breaking things. The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a couple of other places. Note that space is included here so that --dhcp-option=3, string has five characters, whilst --dhcp-option=3," string" has six. */ static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,."; static char hide_meta(char c) { unsigned int i; for (i = 0; i < (sizeof(meta) - 1); i++) if (c == meta[i]) return (char)i; return c; } static char unhide_meta(char cr) { unsigned int c = cr; if (c < (sizeof(meta) - 1)) cr = meta[c]; return cr; } static void unhide_metas(char *cp) { if (cp) for(; *cp; cp++) *cp = unhide_meta(*cp); } static void *opt_malloc(size_t size) { void *ret; if (mem_recover) { ret = whine_malloc(size); if (!ret) longjmp(mem_jmp, 1); } else ret = safe_malloc(size); return ret; } static char *opt_string_alloc(const char *cp) { char *ret = NULL; size_t len; if (cp && (len = strlen(cp)) != 0) { ret = opt_malloc(len+1); memcpy(ret, cp, len+1); /* restore hidden metachars */ unhide_metas(ret); } return ret; } /* find next comma, split string with zero and eliminate spaces. return start of string following comma */ static char *split_chr(char *s, char c) { char *comma, *p; if (!s || !(comma = strchr(s, c))) return NULL; p = comma; *comma = ' '; for (; *comma == ' '; comma++); for (; (p >= s) && *p == ' '; p--) *p = 0; return comma; } static char *split(char *s) { return split_chr(s, ','); } static char *canonicalise_opt(char *s) { char *ret; int nomem; if (!s) return 0; if (strlen(s) == 0) return opt_malloc(1); /* Heap-allocated empty string */ unhide_metas(s); if (!(ret = canonicalise(s, &nomem)) && nomem) { if (mem_recover) longjmp(mem_jmp, 1); else die(_("could not get memory"), NULL, EC_NOMEM); } return ret; } static int numeric_check(char *a) { char *p; if (!a) return 0; unhide_metas(a); for (p = a; *p; p++) if (*p < '0' || *p > '9') return 0; return 1; } static int atoi_check(char *a, int *res) { if (!numeric_check(a)) return 0; *res = atoi(a); return 1; } static int strtoul_check(char *a, u32 *res) { unsigned long x; if (!numeric_check(a)) return 0; x = strtoul(a, NULL, 10); if (errno || x > UINT32_MAX) { errno = 0; return 0; } *res = (u32)x; return 1; } static int atoi_check16(char *a, int *res) { if (!(atoi_check(a, res)) || *res < 0 || *res > 0xffff) return 0; 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 #ifndef NO_ID static void add_txt(char *name, char *txt, int stat) { 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; } #endif static void do_usage(void) { char buff[100]; int i, j; struct { char handle; int val; } tab[] = { { '$', CACHESIZ }, { '*', EDNS_PKTSZ }, { '&', MAXLEASES }, { '!', FTABSIZ }, { '#', TFTP_MAX_CONNECTIONS }, { '\0', 0 } }; printf(_("Usage: dnsmasq [options]\n\n")); #ifndef HAVE_GETOPT_LONG printf(_("Use short options only on the command line.\n")); #endif printf(_("Valid options are:\n")); for (i = 0; usage[i].opt != 0; i++) { char *desc = usage[i].flagdesc; char *eq = "="; if (!desc || *desc == '[') eq = ""; if (!desc) desc = ""; for ( j = 0; opts[j].name; j++) if (opts[j].val == usage[i].opt) break; if (usage[i].opt < 256) sprintf(buff, "-%c, ", usage[i].opt); else sprintf(buff, " "); sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc); printf("%-55.55s", buff); if (usage[i].arg) { safe_strncpy(buff, usage[i].arg, sizeof(buff)); for (j = 0; tab[j].handle; j++) if (tab[j].handle == *(usage[i].arg)) sprintf(buff, "%d", tab[j].val); } printf(_(usage[i].desc), buff); printf("\n"); } } #define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0) #define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0) #define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0) static char *parse_mysockaddr(char *arg, union mysockaddr *addr) { if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0) addr->sa.sa_family = AF_INET; else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0) addr->sa.sa_family = AF_INET6; else return _("bad address"); return NULL; } char *parse_server(char *arg, struct server_details *sdetails) { sdetails->serv_port = NAMESERVER_PORT; char *portno; int ecode = 0; struct addrinfo hints; memset(&hints, 0, sizeof(struct addrinfo)); *sdetails->interface = 0; sdetails->addr_type = AF_UNSPEC; if (strcmp(arg, "#") == 0) { if (sdetails->flags) *sdetails->flags |= SERV_USE_RESOLV; sdetails->addr_type = AF_LOCAL; sdetails->valid = 1; return NULL; } if ((sdetails->source = split_chr(arg, '@')) && /* is there a source. */ (portno = split_chr(sdetails->source, '#')) && !atoi_check16(portno, &sdetails->source_port)) return _("bad port"); if ((portno = split_chr(arg, '#')) && /* is there a port no. */ !atoi_check16(portno, &sdetails->serv_port)) return _("bad port"); sdetails->scope_id = split_chr(arg, '%'); if (sdetails->source) { sdetails->interface_opt = split_chr(sdetails->source, '@'); if (sdetails->interface_opt) { #if defined(SO_BINDTODEVICE) safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE); sdetails->source = sdetails->interface_opt; #else return _("interface binding not supported"); #endif } } if (inet_pton(AF_INET, arg, &sdetails->addr->in.sin_addr) > 0) sdetails->addr_type = AF_INET; else if (inet_pton(AF_INET6, arg, &sdetails->addr->in6.sin6_addr) > 0) sdetails->addr_type = AF_INET6; else { /* if the argument is neither an IPv4 not an IPv6 address, it might be a hostname and we should try to resolve it to a suitable address. */ memset(&hints, 0, sizeof(hints)); /* The AI_ADDRCONFIG flag ensures that then IPv4 addresses are returned in the result only if the local system has at least one IPv4 address configured, and IPv6 addresses are returned only if the local system has at least one IPv6 address configured. The loopback address is not considered for this case as valid as a configured address. This flag is useful on, for example, IPv4-only systems, to ensure that getaddrinfo() does not return IPv6 socket addresses that would always fail in subsequent connect() or bind() attempts. */ hints.ai_flags = AI_ADDRCONFIG; #if defined(HAVE_IDN) && defined(AI_IDN) /* If the AI_IDN flag is specified and we have glibc 2.3.4 or newer, then the node name given in node is converted to IDN format if necessary. The source encoding is that of the current locale. */ hints.ai_flags |= AI_IDN; #endif /* The value AF_UNSPEC indicates that getaddrinfo() should return socket addresses for any address family (either IPv4 or IPv6, for example) that can be used with node and service "domain". */ hints.ai_family = AF_UNSPEC; /* Get addresses suitable for sending datagrams. We assume that we can use the same addresses for TCP connections. Settting this to zero gets each address threes times, for SOCK_STREAM, SOCK_RAW and SOCK_DGRAM, which is not useful. */ hints.ai_socktype = SOCK_DGRAM; /* Get address associated with this hostname */ ecode = getaddrinfo(arg, NULL, &hints, &sdetails->hostinfo); if (ecode == 0) { /* The getaddrinfo() function allocated and initialized a linked list of addrinfo structures, one for each network address that matches node and service, subject to the restrictions imposed by our above, and returns a pointer to the start of the list in . The items in the linked list are linked by the field. */ sdetails->valid = 1; sdetails->orig_hostinfo = sdetails->hostinfo; return NULL; } else { /* Lookup failed, return human readable error string */ if (ecode == EAI_AGAIN) return _("Cannot resolve server name"); else return _((char*)gai_strerror(ecode)); } } sdetails->valid = 1; return NULL; } char *parse_server_addr(struct server_details *sdetails) { if (sdetails->addr_type == AF_INET) { sdetails->addr->in.sin_port = htons(sdetails->serv_port); sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET; #ifdef HAVE_SOCKADDR_SA_LEN sdetails->source_addr->in.sin_len = sdetails->addr->in.sin_len = sizeof(struct sockaddr_in); #endif sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY; sdetails->source_addr->in.sin_port = htons(daemon->query_port); if (sdetails->source) { if (sdetails->flags) *sdetails->flags |= SERV_HAS_SOURCE; sdetails->source_addr->in.sin_port = htons(sdetails->source_port); if (inet_pton(AF_INET, sdetails->source, &sdetails->source_addr->in.sin_addr) == 0) { if (inet_pton(AF_INET6, sdetails->source, &sdetails->source_addr->in6.sin6_addr) == 1) { sdetails->source_addr->sa.sa_family = AF_INET6; /* When resolving a server IP by hostname, we can simply skip mismatching server / source IP pairs. Otherwise, when an IP address is given directly, this is a fatal error. */ if (!sdetails->orig_hostinfo) return _("cannot use IPv4 server address with IPv6 source address"); } else { #if defined(SO_BINDTODEVICE) if (sdetails->interface_opt) return _("interface can only be specified once"); sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY; safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE); #else return _("interface binding not supported"); #endif } } } } else if (sdetails->addr_type == AF_INET6) { if (sdetails->scope_id && (sdetails->scope_index = if_nametoindex(sdetails->scope_id)) == 0) return _("bad interface name"); sdetails->addr->in6.sin6_port = htons(sdetails->serv_port); sdetails->addr->in6.sin6_scope_id = sdetails->scope_index; sdetails->source_addr->in6.sin6_addr = in6addr_any; sdetails->source_addr->in6.sin6_port = htons(daemon->query_port); sdetails->source_addr->in6.sin6_scope_id = 0; sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET6; sdetails->addr->in6.sin6_flowinfo = sdetails->source_addr->in6.sin6_flowinfo = 0; #ifdef HAVE_SOCKADDR_SA_LEN sdetails->addr->in6.sin6_len = sdetails->source_addr->in6.sin6_len = sizeof(sdetails->addr->in6); #endif if (sdetails->source) { if (sdetails->flags) *sdetails->flags |= SERV_HAS_SOURCE; sdetails->source_addr->in6.sin6_port = htons(sdetails->source_port); if (inet_pton(AF_INET6, sdetails->source, &sdetails->source_addr->in6.sin6_addr) == 0) { if (inet_pton(AF_INET, sdetails->source, &sdetails->source_addr->in.sin_addr) == 1) { sdetails->source_addr->sa.sa_family = AF_INET; /* When resolving a server IP by hostname, we can simply skip mismatching server / source IP pairs. Otherwise, when an IP address is given directly, this is a fatal error. */ if(!sdetails->orig_hostinfo) return _("cannot use IPv6 server address with IPv4 source address"); } else { #if defined(SO_BINDTODEVICE) if (sdetails->interface_opt) return _("interface can only be specified once"); sdetails->source_addr->in6.sin6_addr = in6addr_any; safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE); #else return _("interface binding not supported"); #endif } } } } else if (sdetails->addr_type != AF_LOCAL) return _("bad address"); return NULL; } int parse_server_next(struct server_details *sdetails) { /* Looping over resolved addresses? */ if (sdetails->hostinfo) { /* Get address type */ sdetails->addr_type = sdetails->hostinfo->ai_family; /* Get address */ if (sdetails->addr_type == AF_INET) memcpy(&sdetails->addr->in.sin_addr, &((struct sockaddr_in *) sdetails->hostinfo->ai_addr)->sin_addr, sizeof(sdetails->addr->in.sin_addr)); else if (sdetails->addr_type == AF_INET6) memcpy(&sdetails->addr->in6.sin6_addr, &((struct sockaddr_in6 *) sdetails->hostinfo->ai_addr)->sin6_addr, sizeof(sdetails->addr->in6.sin6_addr)); /* Iterate to the next available address */ sdetails->valid = sdetails->hostinfo->ai_next != NULL; sdetails->hostinfo = sdetails->hostinfo->ai_next; return 1; } else if (sdetails->valid) { /* When using an IP address, we return the address only once */ sdetails->valid = 0; return 1; } /* Stop iterating here, we used all available addresses */ return 0; } static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int size) { int i, j; char *string; int msize; u16 flags = 0; char domain[29]; /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */ union mysockaddr serv_addr, source_addr; char interface[IF_NAMESIZE+1]; int count = 1, rem, addrbytes, addrbits; struct server_details sdetails; memset(&sdetails, 0, sizeof(struct server_details)); sdetails.addr = &serv_addr; sdetails.source_addr = &source_addr; sdetails.interface = interface; sdetails.flags = &flags; if (!server) flags = SERV_LITERAL_ADDRESS; else if ((string = parse_server(server, &sdetails))) return string; if (from_file) flags |= SERV_FROM_FILE; rem = size & 0x7; addrbytes = (32 - size) >> 3; addrbits = (32 - size) & 7; if (size > 32 || size < 1) return _("bad IPv4 prefix length"); /* Zero out last address bits according to CIDR mask */ ((u8 *)addr4)[3-addrbytes] &= ~((1 << addrbits)-1); size = size & ~0x7; if (rem != 0) count = 1 << (8 - rem); for (i = 0; i < count; i++) { *domain = 0; string = domain; msize = size/8; for (j = (rem == 0) ? msize-1 : msize; j >= 0; j--) { int dig = ((unsigned char *)addr4)[j]; if (j == msize) dig += i; string += sprintf(string, "%d.", dig); } sprintf(string, "in-addr.arpa"); if (flags & SERV_LITERAL_ADDRESS) { if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL)) return _("error"); } else { /* Always reset server as valid here, so we can add the same upstream server address multiple times for each x.y.z.in-addr.arpa */ sdetails.valid = 1; while (parse_server_next(&sdetails)) { if ((string = parse_server_addr(&sdetails))) return string; if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL)) return _("error"); } if (sdetails.orig_hostinfo) freeaddrinfo(sdetails.orig_hostinfo); } } return NULL; } static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, int size) { int i, j; char *string; int msize; u16 flags = 0; char domain[73]; /* strlen("32*ip6.arpa")+1 */ union mysockaddr serv_addr, source_addr; char interface[IF_NAMESIZE+1]; int count = 1, rem, addrbytes, addrbits; struct server_details sdetails; memset(&sdetails, 0, sizeof(struct server_details)); sdetails.addr = &serv_addr; sdetails.source_addr = &source_addr; sdetails.interface = interface; sdetails.flags = &flags; if (!server) flags = SERV_LITERAL_ADDRESS; else if ((string = parse_server(server, &sdetails))) return string; if (from_file) flags |= SERV_FROM_FILE; rem = size & 0x3; addrbytes = (128 - size) >> 3; addrbits = (128 - size) & 7; if (size > 128 || size < 1) return _("bad IPv6 prefix length"); /* Zero out last address bits according to CIDR mask */ addr6->s6_addr[15-addrbytes] &= ~((1 << addrbits) - 1); size = size & ~0x3; if (rem != 0) count = 1 << (4 - rem); for (i = 0; i < count; i++) { *domain = 0; string = domain; msize = size/4; for (j = (rem == 0) ? msize-1 : msize; j >= 0; j--) { int dig = ((unsigned char *)addr6)[j>>1]; dig = j & 1 ? dig & 15 : dig >> 4; if (j == msize) dig += i; string += sprintf(string, "%.1x.", dig); } sprintf(string, "ip6.arpa"); if (flags & SERV_LITERAL_ADDRESS) { if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL)) return _("error"); } else { /* Always reset server as valid here, so we can add the same upstream server address multiple times for each x.y.z.ip6.arpa */ sdetails.valid = 1; while (parse_server_next(&sdetails)) { if ((string = parse_server_addr(&sdetails))) return string; if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL)) return _("error"); } if (sdetails.orig_hostinfo) freeaddrinfo(sdetails.orig_hostinfo); } } return NULL; } static void if_names_add(const char *ifname) { struct iname *new = opt_malloc(sizeof(struct iname)); new->next = daemon->if_names; daemon->if_names = new; /* new->name may be NULL if someone does "interface=" to disable all interfaces except loop. */ new->name = opt_string_alloc(ifname); new->flags = 0; } #ifdef HAVE_DHCP static int is_tag_prefix(char *arg) { if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg)) return 1; return 0; } static char *set_prefix(char *arg) { if (strstr(arg, "set:") == arg) return arg+4; return arg; } static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next) { struct dhcp_netid *tt; tt = opt_malloc(sizeof (struct dhcp_netid)); tt->net = opt_string_alloc(net); tt->next = next; return tt; } static void dhcp_netid_free(struct dhcp_netid *nid) { while (nid) { struct dhcp_netid *tmp = nid; nid = nid->next; free(tmp->net); free(tmp); } } /* Parse one or more tag:s before parameters. * Moves arg to the end of tags. */ static struct dhcp_netid *dhcp_tags(char **arg) { struct dhcp_netid *id = NULL; while (is_tag_prefix(*arg)) { char *comma = split(*arg); id = dhcp_netid_create((*arg)+4, id); *arg = comma; }; if (!*arg) { dhcp_netid_free(id); id = NULL; } return id; } static void dhcp_netid_list_free(struct dhcp_netid_list *netid) { while (netid) { struct dhcp_netid_list *tmplist = netid; netid = netid->next; /* Note: don't use dhcp_netid_free() here, since that frees a list linked on netid->next. Where a netid_list is used that's because the the ->next pointers in the netids are being used to temporarily construct a list of valid tags. */ free(tmplist->list->net); free(tmplist->list); free(tmplist); } } static void dhcp_config_free(struct dhcp_config *config) { if (config) { struct hwaddr_config *hwaddr = config->hwaddr; while (hwaddr) { struct hwaddr_config *tmp = hwaddr; hwaddr = hwaddr->next; free(tmp); } dhcp_netid_list_free(config->netid); dhcp_netid_free(config->filter); if (config->flags & CONFIG_CLID) free(config->clid); if (config->flags & CONFIG_NAME) free(config->hostname); #ifdef HAVE_DHCP6 if (config->flags & CONFIG_ADDR6) { struct addrlist *addr, *tmp; for (addr = config->addr6; addr; addr = tmp) { tmp = addr->next; free(addr); } } #endif free(config); } } static void dhcp_context_free(struct dhcp_context *ctx) { if (ctx) { dhcp_netid_free(ctx->filter); free(ctx->netid.net); #ifdef HAVE_DHCP6 free(ctx->template_interface); #endif free(ctx); } } static void dhcp_opt_free(struct dhcp_opt *opt) { if (opt->flags & DHOPT_VENDOR) free(opt->u.vendor_class); dhcp_netid_free(opt->netid); free(opt->val); free(opt); } /* This is too insanely large to keep in-line in the switch */ 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 addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots; char *comma = NULL; u16 opt_len = 0; int is6 = 0; int option_ok = 0; new->len = 0; new->flags = flags; new->netid = NULL; new->val = NULL; new->opt = 0; while (arg) { comma = split(arg); for (cp = arg; *cp; cp++) if (*cp < '0' || *cp > '9') break; if (!*cp) { new->opt = atoi(arg); opt_len = 0; option_ok = 1; break; } if (strstr(arg, "option:") == arg) { if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1) { opt_len = lookup_dhcp_len(AF_INET, new->opt); /* option: 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:| 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); new->flags |= DHOPT_VENDOR; if ((new->flags & DHOPT_ENCAPSULATE) || flags == DHOPT_MATCH) goto_err(_("inappropriate vendor:")); } else if (strstr(arg, "encap:") == arg) { new->u.encap = atoi(arg+6); new->flags |= DHOPT_ENCAPSULATE; if ((new->flags & DHOPT_VENDOR) || flags == DHOPT_MATCH) goto_err(_("inappropriate encap:")); } else if (strstr(arg, "vi-encap:") == arg) { new->u.encap = atoi(arg+9); new->flags |= DHOPT_RFC3925; if (flags == DHOPT_MATCH) { option_ok = 1; break; } } else { /* allow optional "net:" or "tag:" for consistency */ const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg); new->netid = dhcp_netid_create(name, new->netid); } arg = comma; } #ifdef HAVE_DHCP6 if (is6) { if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE)) goto_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 (!option_ok) goto_err(_("bad dhcp-option")); if (comma) { /* characterise the value */ char c; int found_dig = 0, found_colon = 0; is_addr = is_addr6 = is_hex = is_dec = is_string = 1; addrs = digs = 1; dots = 0; for (cp = comma; (c = *cp); cp++) if (c == ',') { addrs++; is_dec = is_hex = 0; } else if (c == ':') { digs++; is_dec = is_addr = 0; found_colon = 1; } else if (c == '/') { 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; dots++; } else if (c == '-') is_hex = is_addr = is_addr6 = 0; else if (c == ' ') is_dec = is_hex = 0; else if (!(c >='0' && c <= '9')) { is_addr = 0; if (cp[1] == 0 && is_dec && (c == 'b' || c == 's' || c == 'i')) { lenchar = c; *cp = 0; } else is_dec = 0; if (!((c >='A' && c <= 'F') || (c >='a' && c <= 'f') || (c == '*' && (flags & DHOPT_MATCH)))) { is_hex = 0; if (c != '[' && c != ']') is_addr6 = 0; } } else found_dig = 1; if (!found_dig) is_dec = is_addr = 0; if (!found_colon) is_addr6 = 0; #ifdef HAVE_DHCP6 /* NTP server option takes hex, addresses or FQDN */ if (is6 && new->opt == OPTION6_NTP_SERVER && !is_hex) opt_len |= is_addr6 ? OT_ADDR_LIST : OT_RFC1035_NAME; #endif /* We know that some options take addresses */ if (opt_len & OT_ADDR_LIST) { is_string = is_dec = is_hex = 0; if (!is6 && (!is_addr || dots == 0)) goto_err(_("bad IP address")); if (is6 && !is_addr6) goto_err(_("bad IPv6 address")); } /* or names */ 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 through */ 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; } 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); parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL); new->flags |= DHOPT_HEX; } else if (is_dec) { int i, val = atoi(comma); /* assume numeric arg is 1 byte except for options where it is known otherwise. For vendor class option, we have to hack. */ if (opt_len != 0) new->len = opt_len; else if (val & 0xffff0000) new->len = 4; else if (val & 0xff00) new->len = 2; else new->len = 1; if (lenchar == 'b') new->len = 1; else if (lenchar == 's') new->len = 2; else if (lenchar == 'i') new->len = 4; new->val = opt_malloc(new->len); for (i=0; ilen; i++) new->val[i] = val>>((new->len - i - 1)*8); } else if (is_addr && !is6) { struct in_addr in; unsigned char *op; char *slash; /* max length of address/subnet descriptor is five bytes, add one for the option 120 enc byte too */ new->val = op = opt_malloc((5 * addrs) + 1); new->flags |= DHOPT_ADDR; if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) && new->opt == OPTION_SIP_SERVER) { *(op++) = 1; /* RFC 3361 "enc byte" */ new->flags &= ~DHOPT_ADDR; } while (addrs--) { cp = comma; comma = split(cp); slash = split_chr(cp, '/'); if (!inet_pton(AF_INET, cp, &in)) goto_err(_("bad IPv4 address")); if (!slash) { memcpy(op, &in, INADDRSZ); op += INADDRSZ; } else { unsigned char *p = (unsigned char *)∈ int netsize = atoi(slash); *op++ = netsize; if (netsize > 0) *op++ = *p++; if (netsize > 8) *op++ = *p++; if (netsize > 16) *op++ = *p++; if (netsize > 24) *op++ = *p++; new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */ } } 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; } goto_err(_("bad IPv6 address")); } new->len = op - new->val; } else if (is_string) { /* text arg */ if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) && !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925))) { /* dns search, RFC 3397, or SIP, RFC 3361 */ unsigned char *q, *r, *tail; unsigned char *p, *m = NULL, *newp; size_t newlen, len = 0; int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1; arg = comma; comma = split(arg); while (arg && *arg) { char *in, *dom = NULL; size_t domlen = 1; /* Allow "." as an empty domain */ if (strcmp (arg, ".") != 0) { if (!(dom = canonicalise_opt(arg))) goto_err(_("bad domain in dhcp-option")); domlen = strlen(dom) + 2; } newp = opt_malloc(len + domlen + header_size); if (m) { memcpy(newp, m, header_size + len); free(m); } m = newp; p = m + header_size; q = p + len; /* add string on the end in RFC1035 format */ for (in = dom; in && *in;) { unsigned char *cp = q++; int j; for (j = 0; *in && (*in != '.'); in++, j++) *q++ = *in; *cp = j; if (*in) in++; } *q++ = 0; free(dom); /* Now tail-compress using earlier names. */ newlen = q - p; for (tail = p + len; *tail; tail += (*tail) + 1) for (r = p; r - p < (int)len; r += (*r) + 1) if (strcmp((char *)r, (char *)tail) == 0) { PUTSHORT((r - p) | 0xc000, tail); newlen = tail - p; goto end; } end: len = newlen; arg = comma; comma = split(arg); } /* RFC 3361, enc byte is zero for names */ if (new->opt == OPTION_SIP_SERVER && m) m[0] = 0; 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, *q, *newp, *end; int len = 0; int header_size = (is6 && new->opt == OPTION6_NTP_SERVER) ? 4 : 0; arg = comma; comma = split(arg); while (arg && *arg) { char *dom = canonicalise_opt(arg); if (!dom) goto_err(_("bad domain in dhcp-option")); newp = opt_malloc(len + header_size + strlen(dom) + 2); if (p) { memcpy(newp, p, len); free(p); } p = newp; q = p + len; end = do_rfc1035_name(q + header_size, dom, NULL); *end++ = 0; if (is6 && new->opt == OPTION6_NTP_SERVER) { PUTSHORT(NTP_SUBOPTION_SRV_FQDN, q); PUTSHORT(end - q - 2, q); } len = end - p; free(dom); arg = comma; comma = split(arg); } new->val = p; new->len = len; } #endif else { new->len = strlen(comma); /* keep terminating zero on string */ new->val = (unsigned char *)opt_string_alloc(comma); new->flags |= DHOPT_STRING; } } } if (!is6 && ((new->len > 255) || (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) || (new->len > 250 && (new->flags & DHOPT_RFC3925)))) goto_err(_("dhcp-option too long")); if (flags == DHOPT_PXE_OPT && (new->flags & DHOPT_VENDOR)) goto_err(_("No vendor-encap options allowed in dhcp-option-pxe")); if (flags == DHOPT_MATCH) { if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) || !new->netid || new->netid->next) goto_err(_("illegal dhcp-match")); if (is6) { new->next = daemon->dhcp_match6; daemon->dhcp_match6 = new; } else { new->next = daemon->dhcp_match; daemon->dhcp_match = new; } } else if (is6) { new->next = daemon->dhcp_opts6; daemon->dhcp_opts6 = new; } else { new->next = daemon->dhcp_opts; daemon->dhcp_opts = new; } return 1; on_error: dhcp_opt_free(new); return 0; } #endif void set_option_bool(unsigned int opt) { option_var(opt) |= option_val(opt); } void reset_option_bool(unsigned int opt) { option_var(opt) &= ~(option_val(opt)); } static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only) { int i; char *comma; if (option == '?') ret_err(gen_err); for (i=0; usage[i].opt != 0; i++) if (usage[i].opt == option) { int rept = usage[i].rept; if (command_line) { /* command line */ if (rept == ARG_USED_CL) ret_err(_("illegal repeated flag")); if (rept == ARG_ONE) usage[i].rept = ARG_USED_CL; } else { /* allow file to override command line */ if (rept == ARG_USED_FILE) ret_err(_("illegal repeated keyword")); if (rept == ARG_USED_CL || rept == ARG_ONE) usage[i].rept = ARG_USED_FILE; } if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL) { set_option_bool(rept); return 1; } break; } switch (option) { case 'C': /* --conf-file */ { char *file = opt_string_alloc(arg); if (file) { one_file(file, 0); free(file); } break; } case LOPT_CONF_SCRIPT: /* --conf-script */ { char *file = opt_string_alloc(arg); if (file) { one_file(file, LOPT_CONF_SCRIPT); free(file); } break; } case '7': /* --conf-dir */ { DIR *dir_stream; struct dirent *ent; char *directory, *path; struct list { char *name; struct list *next; } *ignore_suffix = NULL, *match_suffix = NULL, *files = NULL, *li; comma = split(arg); if (!(directory = opt_string_alloc(arg))) break; for (arg = comma; arg; arg = comma) { comma = split(arg); if (strlen(arg) != 0) { li = opt_malloc(sizeof(struct list)); if (*arg == '*') { /* "*" with no suffix is a no-op */ if (arg[1] == 0) free(li); else { li->next = match_suffix; match_suffix = li; /* Have to copy: buffer is overwritten */ li->name = opt_string_alloc(arg+1); } } else { li->next = ignore_suffix; ignore_suffix = li; /* Have to copy: buffer is overwritten */ li->name = opt_string_alloc(arg); } } } if (!(dir_stream = opendir(directory))) die(_("cannot access directory %s: %s"), directory, EC_FILE); while ((ent = readdir(dir_stream))) { size_t len = strlen(ent->d_name); struct stat buf; /* ignore emacs backups and dotfiles */ if (len == 0 || ent->d_name[len - 1] == '~' || (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') || 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->name); if (len > ls && strcmp(li->name, &ent->d_name[len - ls]) == 0) break; } if (!li) continue; } for (li = ignore_suffix; li; li = li->next) { /* check for proscribed suffices */ size_t ls = strlen(li->name); if (len > ls && strcmp(li->name, &ent->d_name[len - ls]) == 0) break; } if (li) continue; path = opt_malloc(strlen(directory) + len + 2); strcpy(path, directory); 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)) { /* sort files into order. */ struct list **up, *new = opt_malloc(sizeof(struct list)); new->name = path; for (up = &files, li = files; li; up = &li->next, li = li->next) if (strcmp(li->name, path) >=0) break; new->next = li; *up = new; } else free(path); } for (li = files; li; li = li->next) one_file(li->name, 0); closedir(dir_stream); free(directory); for(; ignore_suffix; ignore_suffix = li) { li = ignore_suffix->next; free(ignore_suffix->name); free(ignore_suffix); } for(; match_suffix; match_suffix = li) { li = match_suffix->next; free(match_suffix->name); free(match_suffix); } for(; files; files = li) { li = files->next; free(files->name); free(files); } break; } case LOPT_ADD_SBNET: /* --add-subnet */ set_option_bool(OPT_CLIENT_SUBNET); if (arg) { char *err, *end; comma = split(arg); struct mysubnet* new = opt_malloc(sizeof(struct mysubnet)); if ((end = split_chr(arg, '/'))) { /* has subnet+len */ err = parse_mysockaddr(arg, &new->addr); if (err) ret_err_free(err, new); if (!atoi_check(end, &new->mask)) ret_err_free(gen_err, new); new->addr_used = 1; } else if (!atoi_check(arg, &new->mask)) ret_err_free(gen_err, new); daemon->add_subnet4 = new; if (comma) { new = opt_malloc(sizeof(struct mysubnet)); if ((end = split_chr(comma, '/'))) { /* has subnet+len */ err = parse_mysockaddr(comma, &new->addr); if (err) ret_err_free(err, new); if (!atoi_check(end, &new->mask)) ret_err_free(gen_err, new); new->addr_used = 1; } else { if (!atoi_check(comma, &new->mask)) ret_err_free(gen_err, new); } daemon->add_subnet6 = new; } } 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 LOPT_UBUS: /* --enable-ubus */ set_option_bool(OPT_UBUS); if (arg) daemon->ubus_name = opt_string_alloc(arg); else daemon->ubus_name = DNSMASQ_UBUS_NAME; break; case '8': /* --log-facility */ /* may be a filename */ if (strchr(arg, '/') || strcmp (arg, "-") == 0) daemon->log_file = opt_string_alloc(arg); else { #ifdef __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)) break; if (facilitynames[i].c_name) daemon->log_fac = facilitynames[i].c_val; else ret_err(_("bad log facility")); #endif } break; case 'x': /* --pid-file */ daemon->runfile = opt_string_alloc(arg); break; case 'r': /* --resolv-file */ { char *name = opt_string_alloc(arg); struct resolvc *new, *list = daemon->resolv_files; if (list && list->is_default) { /* replace default resolv file - possibly with nothing */ if (name) { list->is_default = 0; list->name = name; } else list = NULL; } else if (name) { new = opt_malloc(sizeof(struct resolvc)); new->next = list; new->name = name; new->is_default = 0; new->mtime = 0; new->logged = 0; list = new; } daemon->resolv_files = list; break; } case LOPT_SERVERS_FILE: daemon->servers_file = opt_string_alloc(arg); break; case 'm': /* --mx-host */ { int pref = 1; struct mx_srv_record *new; char *name, *target = NULL; if ((comma = split(arg))) { char *prefstr; if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref)) ret_err(_("bad MX preference")); } if (!(name = canonicalise_opt(arg)) || (comma && !(target = canonicalise_opt(comma)))) { free(name); free(target); ret_err(_("bad MX name")); } new = opt_malloc(sizeof(struct mx_srv_record)); new->next = daemon->mxnames; daemon->mxnames = new; new->issrv = 0; new->name = name; new->target = target; /* may be NULL */ new->weight = pref; break; } case 't': /* --mx-target */ if (!(daemon->mxtarget = canonicalise_opt(arg))) ret_err(_("bad MX target")); break; case LOPT_DUMPFILE: /* --dumpfile */ daemon->dump_file = opt_string_alloc(arg); break; case LOPT_DUMPMASK: /* --dumpmask */ daemon->dump_mask = strtol(arg, NULL, 0); break; #ifdef HAVE_DHCP case 'l': /* --dhcp-leasefile */ daemon->lease_file = opt_string_alloc(arg); break; /* Sorry about the gross pre-processor abuse */ case '6': /* --dhcp-script */ case LOPT_LUASCRIPT: /* --dhcp-luascript */ # if !defined(HAVE_SCRIPT) ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts")); # else 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 /* HAVE_DHCP */ case LOPT_DHCP_HOST: /* --dhcp-hostsfile */ case LOPT_DHCP_OPTS: /* --dhcp-optsfile */ case 'H': /* --addn-hosts */ { struct hostsfile *new = opt_malloc(sizeof(struct hostsfile)); new->fname = opt_string_alloc(arg); new->index = daemon->host_index++; new->flags = 0; if (option == 'H') { new->next = daemon->addn_hosts; daemon->addn_hosts = new; } else if (option == LOPT_DHCP_HOST) { new->next = daemon->dhcp_hosts_file; daemon->dhcp_hosts_file = new; } else if (option == LOPT_DHCP_OPTS) { new->next = daemon->dhcp_opts_file; daemon->dhcp_opts_file = new; } break; } case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */ case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */ case LOPT_HOST_INOTIFY: /* --hostsdir */ { struct dyndir *new = opt_malloc(sizeof(struct dyndir)); new->dname = opt_string_alloc(arg); new->flags = 0; 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; } case LOPT_AUTHSERV: /* --auth-server */ comma = split(arg); daemon->authserver = opt_string_alloc(arg); while ((arg = comma)) { 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; else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0) new->addr.sa.sa_family = AF_INET6; 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; else if (strcmp(fam, "6") == 0) new->addr.sa.sa_family = AF_INET6; else { free(new->name); ret_err_free(gen_err, new); } } } new->next = daemon->authinterface; daemon->authinterface = new; }; 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 = canonicalise_opt(arg); if (!new->domain) ret_err_free(_("invalid auth-zone"), new); new->subnet = NULL; new->exclude = NULL; new->interface_names = NULL; new->next = daemon->auth_zones; daemon->auth_zones = new; while ((arg = comma)) { int prefixlen = 0; int is_exclude = 0; char *prefix; struct addrlist *subnet = NULL; union all_addr addr; comma = split(arg); prefix = split_chr(arg, '/'); if (prefix && !atoi_check(prefix, &prefixlen)) ret_err(gen_err); if (strstr(arg, "exclude:") == arg) { is_exclude = 1; arg = arg+8; } if (inet_pton(AF_INET, arg, &addr.addr4)) { subnet = opt_malloc(sizeof(struct addrlist)); subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen; subnet->flags = ADDRLIST_LITERAL; } else if (inet_pton(AF_INET6, arg, &addr.addr6)) { subnet = opt_malloc(sizeof(struct addrlist)); subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen; subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6; } 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; else if (prefixlen == 6) name->flags &= ~AUTH4; else ret_err(gen_err); } } if (subnet) { subnet->addr = addr; if (is_exclude) { subnet->next = new->exclude; new->exclude = subnet; } else { subnet->next = new->subnet; new->subnet = subnet; } } } break; } 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; 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) daemon->soa_expiry = (u32)atoi(comma); } } } break; case 's': /* --domain */ case LOPT_SYNTH: /* --synth-domain */ { char *d, *d_raw = arg; comma = split(arg); if (!(d = canonicalise_opt(d_raw))) ret_err(gen_err); else { free(d); /* allocate this again below. */ if (comma) { struct cond_domain *new = opt_malloc(sizeof(struct cond_domain)); char *netpart; new->prefix = NULL; new->indexed = 0; new->prefixlen = 0; unhide_metas(comma); if ((netpart = split_chr(comma, '/'))) { int msize; arg = split(netpart); if (!atoi_check(netpart, &msize)) ret_err_free(gen_err, new); else if (inet_pton(AF_INET, comma, &new->start)) { int mask; if (msize > 32) ret_err_free(_("bad prefix length"), new); 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) { if (option != 's') { /* IPv6 address is longest and represented as xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx which is 39 chars */ if (!(new->prefix = canonicalise_opt(arg)) || strlen(new->prefix) > (MAXLABEL - 39)) ret_err_free(_("bad prefix"), new); } else if (strcmp(arg, "local") != 0) ret_err_free(gen_err, new); else { /* local=/xxx.yyy.zzz.in-addr.arpa/ */ domain_rev4(0, NULL, &new->start, msize); /* local=// */ /* d_raw can't failed to canonicalise here, checked above. */ add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL); } } } else if (inet_pton(AF_INET6, comma, &new->start6)) { u64 mask, addrpart = addr6part(&new->start6); if (msize > 128) ret_err_free(_("bad prefix length"), new); mask = (1LLU << (128 - msize)) - 1LLU; new->is6 = 1; new->prefixlen = msize; /* 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 (arg) { if (option != 's') { if (!(new->prefix = canonicalise_opt(arg)) || strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN) ret_err_free(_("bad prefix"), new); } else if (strcmp(arg, "local") != 0) ret_err_free(gen_err, new); else { /* generate the equivalent of local=/xxx.yyy.zzz.ip6.arpa/ */ domain_rev6(0, NULL, &new->start6, msize); /* local=// */ /* d_raw can't failed to canonicalise here, checked above. */ add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL); } } } else ret_err_free(gen_err, new); } else { 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_free(gen_err, new); } 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_free(gen_err, new); } else if (option == 's') { /* subnet from interface. */ new->interface = opt_string_alloc(comma); new->al = NULL; } else ret_err_free(gen_err, new); if (option != 's' && prefstr) { if (!(new->prefix = canonicalise_opt(prefstr)) || strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN) ret_err_free(_("bad prefix"), new); } } new->domain = canonicalise_opt(d_raw); if (option == 's') { new->next = daemon->cond_domain; daemon->cond_domain = new; } else { char *star; if (new->prefix && (star = strrchr(new->prefix, '*')) && *(star+1) == 0) { *star = 0; new->indexed = 1; if (new->is6 && new->prefixlen < 64) ret_err_free(_("prefix length too small"), new); } new->next = daemon->synth_domains; daemon->synth_domains = new; } } else if (option == 's') { if (strcmp (arg, "#") == 0) set_option_bool(OPT_RESOLV_DOMAIN); else daemon->domain_suffix = canonicalise_opt(d_raw); } else ret_err(gen_err); } break; } case LOPT_CPE_ID: /* --add-dns-client */ if (arg) daemon->dns_client_id = opt_string_alloc(arg); break; case LOPT_UMBRELLA: /* --umbrella */ set_option_bool(OPT_UMBRELLA); while (arg) { comma = split(arg); if (strstr(arg, "deviceid:")) { char *p; u8 *u = daemon->umbrella_device; char word[3]; arg += 9; if (strlen(arg) != 16) ret_err(gen_err); for (p = arg; *p; p++) if (!isxdigit((unsigned char)*p)) ret_err(gen_err); set_option_bool(OPT_UMBRELLA_DEVID); for (i = 0; i < (int)sizeof(daemon->umbrella_device); i++, arg+=2) { memcpy(word, &(arg[0]), 2); *u++ = strtoul(word, NULL, 16); } } else if (strstr(arg, "orgid:")) { if (!strtoul_check(arg+6, &daemon->umbrella_org)) ret_err(gen_err); } else if (strstr(arg, "assetid:")) { if (!strtoul_check(arg+8, &daemon->umbrella_asset)) ret_err(gen_err); } else ret_err(gen_err); arg = comma; } break; case LOPT_ADD_MAC: /* --add-mac */ if (!arg) set_option_bool(OPT_ADD_MAC); else { unhide_metas(arg); if (strcmp(arg, "base64") == 0) set_option_bool(OPT_MAC_B64); else if (strcmp(arg, "text") == 0) set_option_bool(OPT_MAC_HEX); else ret_err(gen_err); } break; case 'u': /* --user */ daemon->username = opt_string_alloc(arg); break; case 'g': /* --group */ daemon->groupname = opt_string_alloc(arg); daemon->group_set = 1; break; #ifdef HAVE_DHCP case LOPT_SCRIPTUSR: /* --scriptuser */ daemon->scriptuser = opt_string_alloc(arg); break; #endif case 'i': /* --interface */ do { comma = split(arg); if_names_add(arg); 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 */ case LOPT_NO_DHCP6: /* --no-dhcpv6-interface */ case LOPT_NO_DHCP4: /* --no-dhcpv4-interface */ do { struct iname *new = opt_malloc(sizeof(struct iname)); comma = split(arg); new->name = opt_string_alloc(arg); new->flags = INAME_4 | INAME_6; if (option == 'I') { new->next = daemon->if_except; daemon->if_except = new; } else if (option == LOPT_TFTP) { new->next = daemon->tftp_interfaces; daemon->tftp_interfaces = new; } else { if (option == LOPT_NO_DHCP6) new->flags &= ~INAME_4; if (option == LOPT_NO_DHCP4) new->flags &= ~INAME_6; new->next = daemon->dhcp_except; daemon->dhcp_except = new; } arg = comma; } while (arg); break; case 'B': /* --bogus-nxdomain */ case LOPT_IGNORE_ADDR: /* --ignore-address */ { union all_addr addr; int prefix, is6 = 0; struct bogus_addr *baddr; unhide_metas(arg); if (!arg || ((comma = split_chr(arg, '/')) && !atoi_check(comma, &prefix))) ret_err(gen_err); if (inet_pton(AF_INET6, arg, &addr.addr6) == 1) is6 = 1; else if (inet_pton(AF_INET, arg, &addr.addr4) != 1) ret_err(gen_err); if (!comma) { if (is6) prefix = 128; else prefix = 32; } if (prefix > 128 || (!is6 && prefix > 32)) ret_err(gen_err); baddr = opt_malloc(sizeof(struct bogus_addr)); if (option == 'B') { baddr->next = daemon->bogus_addr; daemon->bogus_addr = baddr; } else { baddr->next = daemon->ignore_addr; daemon->ignore_addr = baddr; } baddr->prefix = prefix; baddr->is6 = is6; baddr->addr = addr; 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); 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 } else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0) { 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 } else ret_err_free(gen_err, new); new->flags = 0; if (option == 'a') { new->next = daemon->if_addrs; daemon->if_addrs = new; } else { new->next = daemon->auth_peers; daemon->auth_peers = new; } arg = comma; } while (arg); break; case LOPT_NO_REBIND: /* --rebind-domain-ok */ { struct rebind_domain *new; unhide_metas(arg); if (*arg == '/') arg++; do { comma = split_chr(arg, '/'); new = opt_malloc(sizeof(struct rebind_domain)); new->domain = canonicalise_opt(arg); new->next = daemon->no_rebind; daemon->no_rebind = new; arg = comma; } while (arg && *arg); break; } case 'S': /* --server */ case LOPT_LOCAL: /* --local */ case 'A': /* --address */ { char *lastdomain = NULL, *domain = "", *cur_domain; u16 flags = 0; char *err; union all_addr addr; union mysockaddr serv_addr, source_addr; char interface[IF_NAMESIZE+1]; struct server_details sdetails; memset(&sdetails, 0, sizeof(struct server_details)); sdetails.addr = &serv_addr; sdetails.source_addr = &source_addr; sdetails.interface = interface; sdetails.flags = &flags; unhide_metas(arg); /* split the domain args, if any and skip to the end of them. */ if (arg && *arg == '/') { char *last; domain = lastdomain = ++arg; while ((last = split_chr(arg, '/'))) { lastdomain = arg; arg = last; } } if (!arg || !*arg) flags = SERV_LITERAL_ADDRESS; else if (option == 'A') { /* # as literal address means return zero address for 4 and 6 */ if (strcmp(arg, "#") == 0) flags = SERV_ALL_ZEROS | SERV_LITERAL_ADDRESS; else if (inet_pton(AF_INET, arg, &addr.addr4) > 0) flags = SERV_4ADDR | SERV_LITERAL_ADDRESS; else if (inet_pton(AF_INET6, arg, &addr.addr6) > 0) flags = SERV_6ADDR | SERV_LITERAL_ADDRESS; else ret_err(_("Bad address in --address")); } else { if ((err = parse_server(arg, &sdetails))) ret_err(err); } if (servers_only && option == 'S') flags |= SERV_FROM_FILE; cur_domain = domain; while ((flags & SERV_LITERAL_ADDRESS) || parse_server_next(&sdetails)) { cur_domain = domain; if (!(flags & SERV_LITERAL_ADDRESS) && (err = parse_server_addr(&sdetails))) ret_err(err); /* When source is set only use DNS records of the same type and skip all others */ if (flags & SERV_HAS_SOURCE && sdetails.addr_type != sdetails.source_addr->sa.sa_family) continue; while (1) { /* server=//1.2.3.4 is special. */ if (lastdomain) { if (strlen(cur_domain) == 0) flags |= SERV_FOR_NODOTS; else flags &= ~SERV_FOR_NODOTS; /* address=/#/ matches the same as without domain, as does server=/#/.... for consistency. */ if (cur_domain[0] == '#' && cur_domain[1] == 0) cur_domain[0] = 0; } if (!add_update_server(flags, sdetails.addr, sdetails.source_addr, sdetails.interface, cur_domain, &addr)) ret_err(gen_err); if (!lastdomain || cur_domain == lastdomain) break; cur_domain += strlen(cur_domain) + 1; } if (flags & SERV_LITERAL_ADDRESS) break; } if (sdetails.orig_hostinfo) freeaddrinfo(sdetails.orig_hostinfo); break; } case LOPT_REV_SERV: /* --rev-server */ { char *string; int size; struct in_addr addr4; struct in6_addr addr6; unhide_metas(arg); if (!arg) ret_err(gen_err); comma=split(arg); if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size)) size = -1; if (inet_pton(AF_INET, arg, &addr4)) { if (size == -1) size = 32; if ((string = domain_rev4(servers_only, comma, &addr4, size))) ret_err(string); } else if (inet_pton(AF_INET6, arg, &addr6)) { if (size == -1) size = 128; if ((string = domain_rev6(servers_only, comma, &addr6, size))) ret_err(string); } else ret_err(gen_err); break; } case LOPT_IPSET: /* --ipset */ case LOPT_NFTSET: /* --nftset */ #ifndef HAVE_IPSET if (option == LOPT_IPSET) { ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives")); break; } #endif #ifndef HAVE_NFTSET if (option == LOPT_NFTSET) { ret_err(_("recompile with HAVE_NFTSET defined to enable nftset directives")); break; } #endif { struct ipsets ipsets_head; struct ipsets *ipsets = &ipsets_head; struct ipsets **daemon_sets = (option == LOPT_IPSET) ? &daemon->ipsets : &daemon->nftsets; 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))) ret_err(gen_err); 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) ret_err(gen_err); for (size = 2, end = arg; *end; ++end) if (*end == ',') ++size; sets = sets_pos = opt_malloc(sizeof(char *) * size); do { char *p; end = split(arg); *sets_pos = opt_string_alloc(arg); /* Use '#' to delimit table and set */ if (option == LOPT_NFTSET) while ((p = strchr(*sets_pos, '#'))) *p = ' '; sets_pos++; arg = end; } while (end); *sets_pos = 0; for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next) ipsets->next->sets = sets; ipsets->next = *daemon_sets; *daemon_sets = ipsets_head.next; break; } case LOPT_CMARK_ALST_EN: /* --connmark-allowlist-enable */ #ifndef HAVE_CONNTRACK ret_err(_("recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives")); break; #else { u32 mask = UINT32_MAX; if (arg) if (!strtoul_check(arg, &mask) || mask < 1) ret_err(gen_err); set_option_bool(OPT_CMARK_ALST_EN); daemon->allowlist_mask = mask; break; } #endif case LOPT_CMARK_ALST: /* --connmark-allowlist */ #ifndef HAVE_CONNTRACK ret_err(_("recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives")); break; #else { struct allowlist *allowlists; char **patterns, **patterns_pos; u32 mark, mask = UINT32_MAX; size_t num_patterns = 0; char *c, *m = NULL; char *separator; unhide_metas(arg); if (!arg) ret_err(gen_err); c = arg; if (*c < '0' || *c > '9') ret_err(gen_err); while (*c && *c != ',') { if (*c == '/') { if (m) ret_err(gen_err); *c = '\0'; m = ++c; } if (*c < '0' || *c > '9') ret_err(gen_err); c++; } separator = c; if (!*separator) break; while (c && *c) { char *end = strchr(++c, '/'); if (end) *end = '\0'; if (strcmp(c, "*") && !is_valid_dns_name_pattern(c)) ret_err(gen_err); if (end) *end = '/'; if (num_patterns >= UINT16_MAX - 1) ret_err(gen_err); num_patterns++; c = end; } *separator = '\0'; if (!strtoul_check(arg, &mark) || mark < 1 || mark > UINT32_MAX) ret_err(gen_err); if (m) if (!strtoul_check(m, &mask) || mask < 1 || mask > UINT32_MAX || (mark & ~mask)) ret_err(gen_err); if (num_patterns) *separator = ','; for (allowlists = daemon->allowlists; allowlists; allowlists = allowlists->next) if (allowlists->mark == mark && allowlists->mask == mask) ret_err(gen_err); patterns = opt_malloc((num_patterns + 1) * sizeof(char *)); if (!patterns) goto fail_cmark_allowlist; patterns_pos = patterns; c = separator; while (c && *c) { char *end = strchr(++c, '/'); if (end) *end = '\0'; if (!(*patterns_pos++ = opt_string_alloc(c))) goto fail_cmark_allowlist; if (end) *end = '/'; c = end; } *patterns_pos++ = NULL; allowlists = opt_malloc(sizeof(struct allowlist)); if (!allowlists) goto fail_cmark_allowlist; memset(allowlists, 0, sizeof(struct allowlist)); allowlists->mark = mark; allowlists->mask = mask; allowlists->patterns = patterns; allowlists->next = daemon->allowlists; daemon->allowlists = allowlists; break; fail_cmark_allowlist: if (patterns) { for (patterns_pos = patterns; *patterns_pos; patterns_pos++) { free(*patterns_pos); *patterns_pos = NULL; } free(patterns); patterns = NULL; } if (allowlists) { free(allowlists); allowlists = NULL; } ret_err(gen_err); } #endif case 'c': /* --cache-size */ { int size; if (!atoi_check(arg, &size)) ret_err(gen_err); else { /* zero is OK, and means no caching. */ if (size < 0) size = 0; /* Note that for very large cache sizes, the malloc() will overflow. For the size of the cache record at the time this was noted, the value of "very large" was 46684428. Limit to an order of magnitude less than that to be safe from changes to the cache record. */ if (size > 5000000) size = 5000000; daemon->cachesize = size; } break; } case 'p': /* --port */ if (!atoi_check16(arg, &daemon->port)) ret_err(gen_err); break; case LOPT_MINPORT: /* --min-port */ if (!atoi_check16(arg, &daemon->min_port)) ret_err(gen_err); break; case LOPT_MAXPORT: /* --max-port */ if (!atoi_check16(arg, &daemon->max_port)) ret_err(gen_err); break; case '0': /* --dns-forward-max */ if (!atoi_check(arg, &daemon->ftabsize)) ret_err(gen_err); break; case 'q': /* --log-queries */ set_option_bool(OPT_LOG); if (arg) { if (strcmp(arg, "extra") == 0) set_option_bool(OPT_EXTRALOG); else if (strcmp(arg, "proto") == 0) { set_option_bool(OPT_EXTRALOG); set_option_bool(OPT_LOG_PROTO); } } break; case LOPT_MAX_LOGS: /* --log-async */ daemon->max_logs = LOG_MAX; /* default */ if (arg && !atoi_check(arg, &daemon->max_logs)) ret_err(gen_err); else if (daemon->max_logs > 100) daemon->max_logs = 100; break; case LOPT_LOCAL_SERVICE: /* --local-service */ if (!arg || !strcmp(arg, "net")) set_option_bool(OPT_LOCAL_SERVICE); else if (!strcmp(arg, "host")) set_option_bool(OPT_LOCALHOST_SERVICE); else ret_err(gen_err); break; case 'P': /* --edns-packet-max */ { int i; if (!atoi_check(arg, &i)) ret_err(gen_err); daemon->edns_pktsz = (unsigned short)i; break; } case 'Q': /* --query-port */ if (!atoi_check16(arg, &daemon->query_port)) ret_err(gen_err); /* if explicitly set to zero, use single OS ephemeral port and disable random ports */ if (daemon->query_port == 0) daemon->osport = 1; break; case LOPT_RANDPORT_LIM: /* --port-limit */ if (!atoi_check(arg, &daemon->randport_limit) || (daemon->randport_limit < 1)) ret_err(gen_err); break; 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 */ case LOPT_DHCPTTL: /* --dhcp-ttl */ { int ttl; if (!atoi_check(arg, &ttl)) 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 if (option == LOPT_DHCPTTL) { daemon->dhcp_ttl = (unsigned long)ttl; daemon->use_dhcp_ttl = 1; } else daemon->local_ttl = (unsigned long)ttl; break; } case LOPT_FAST_RETRY: /* --fast-dns-retry */ daemon->fast_retry_timeout = TIMEOUT; if (!arg) daemon->fast_retry_time = DEFAULT_FAST_RETRY; else { int retry; comma = split(arg); if (!atoi_check(arg, &retry) || retry < 50) ret_err(gen_err); daemon->fast_retry_time = retry; if (comma) { if (!atoi_check(comma, &retry)) ret_err(gen_err); daemon->fast_retry_timeout = retry/1000; } } break; case LOPT_CACHE_RR: /* --cache-rr */ case LOPT_FILTER_RR: /* --filter-rr */ case LOPT_FILTER_A: /* --filter-A */ case LOPT_FILTER_AAAA: /* --filter-AAAA */ while (1) { int type; struct rrlist *new; comma = NULL; if (option == LOPT_FILTER_A) type = T_A; else if (option == LOPT_FILTER_AAAA) type = T_AAAA; else { comma = split(arg); if (!atoi_check(arg, &type) && (type = rrtype(arg)) == 0) ret_err(_("bad RR type")); } new = opt_malloc(sizeof(struct rrlist)); new->rr = type; if (option == LOPT_CACHE_RR) { new->next = daemon->cache_rr; daemon->cache_rr = new; } else { new->next = daemon->filter_rr; daemon->filter_rr = new; } if (!comma) break; arg = comma; } break; #ifdef HAVE_DHCP case 'X': /* --dhcp-lease-max */ if (!atoi_check(arg, &daemon->dhcp_max)) ret_err(gen_err); break; #endif #ifdef HAVE_TFTP case LOPT_TFTP_MAX: /* --tftp-max */ if (!atoi_check(arg, &daemon->tftp_max)) ret_err(gen_err); break; case LOPT_TFTP_MTU: /* --tftp-mtu */ if (!atoi_check(arg, &daemon->tftp_mtu)) ret_err(gen_err); break; case LOPT_PREFIX: /* --tftp-prefix */ comma = split(arg); if (comma) { struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix)); new->interface = opt_string_alloc(comma); new->prefix = opt_string_alloc(arg); new->next = daemon->if_prefix; daemon->if_prefix = new; } else daemon->tftp_prefix = opt_string_alloc(arg); break; case LOPT_TFTPPORTS: /* --tftp-port-range */ if (!(comma = split(arg)) || !atoi_check16(arg, &daemon->start_tftp_port) || !atoi_check16(comma, &daemon->end_tftp_port)) ret_err(_("bad port range")); if (daemon->start_tftp_port > daemon->end_tftp_port) { int tmp = daemon->start_tftp_port; daemon->start_tftp_port = daemon->end_tftp_port; daemon->end_tftp_port = tmp; } break; case LOPT_APREF: /* --tftp-unique-root */ if (!arg || strcasecmp(arg, "ip") == 0) set_option_bool(OPT_TFTP_APREF_IP); else if (strcasecmp(arg, "mac") == 0) set_option_bool(OPT_TFTP_APREF_MAC); else ret_err(gen_err); break; #endif case LOPT_BRIDGE: /* --bridge-interface */ { struct dhcp_bridge *new; if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 ) ret_err(_("bad bridge-interface")); for (new = daemon->bridges; new; new = new->next) if (strcmp(new->iface, arg) == 0) break; if (!new) { new = opt_malloc(sizeof(struct dhcp_bridge)); strcpy(new->iface, arg); new->alias = NULL; new->next = daemon->bridges; daemon->bridges = new; } do { arg = comma; comma = split(arg); if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1) { struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge)); b->next = new->alias; new->alias = b; strcpy(b->iface, arg); } } while (comma); break; } #ifdef HAVE_DHCP case LOPT_SHARED_NET: /* --shared-network */ { struct shared_network *new = opt_malloc(sizeof(struct shared_network)); #ifdef HAVE_DHCP6 new->shared_addr.s_addr = 0; #endif new->if_index = 0; if (!(comma = split(arg))) { snerr: free(new); ret_err(_("bad shared-network")); } if (inet_pton(AF_INET, comma, &new->shared_addr)) { if (!inet_pton(AF_INET, arg, &new->match_addr) && !(new->if_index = if_nametoindex(arg))) goto snerr; } #ifdef HAVE_DHCP6 else if (inet_pton(AF_INET6, comma, &new->shared_addr6)) { if (!inet_pton(AF_INET6, arg, &new->match_addr6) && !(new->if_index = if_nametoindex(arg))) goto snerr; } #endif else goto snerr; new->next = daemon->shared_networks; daemon->shared_networks = new; break; } case 'F': /* --dhcp-range */ { int k, leasepos = 2; char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context)); memset (new, 0, sizeof(*new)); while(1) { for (cp = arg; *cp; cp++) if (!(*cp == ' ' || *cp == '.' || *cp == ':' || (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') || (*cp >='0' && *cp <= '9'))) break; if (*cp != ',' && (comma = split(arg))) { if (is_tag_prefix(arg)) { /* ignore empty tag */ if (arg[4]) new->filter = dhcp_netid_create(arg+4, new->filter); } else { if (new->netid.net) { dhcp_context_free(new); ret_err(_("only one tag allowed")); } else new->netid.net = opt_string_alloc(set_prefix(arg)); } arg = comma; } else { a[0] = arg; break; } } for (k = 1; k < 8; k++) if (!(a[k] = split(a[k-1]))) break; if (k < 2) { dhcp_context_free(new); ret_err(_("bad dhcp-range")); } if (inet_pton(AF_INET, a[0], &new->start)) { new->next = daemon->dhcp; new->lease_time = DEFLEASE; 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)) { dhcp_context_free(new); 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)) { dhcp_context_free(new); 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; } } } #ifdef HAVE_DHCP6 else if (inet_pton(AF_INET6, a[0], &new->start6)) { const char *err = NULL; new->flags |= CONTEXT_V6; new->prefix = 64; /* default */ new->end6 = new->start6; new->lease_time = DEFLEASE6; 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) err=(_("prefix length must be exactly 64 for RA subnets")); else if (new->flags & CONTEXT_TEMPLATE) err=(_("prefix length must be exactly 64 for subnet constructors")); } else if (new->prefix < 64) err=(_("prefix length must be at least 64")); if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix)) err=(_("inconsistent DHCPv6 range")); if (err) { dhcp_context_free(new); ret_err(err); } /* 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)) { dhcp_context_free(new); 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 { dhcp_context_free(new); ret_err(_("bad dhcp-range")); } if (leasepos < k) { if (leasepos != k-1) { dhcp_context_free(new); ret_err(_("bad dhcp-range")); } if (strcmp(a[leasepos], "infinite") == 0) { new->lease_time = 0xffffffff; new->flags |= CONTEXT_SETLEASE; } else if (strcmp(a[leasepos], "deprecated") == 0) new->flags |= CONTEXT_DEPRECATE; else { int fac = 1; if (strlen(a[leasepos]) > 0) { switch (a[leasepos][strlen(a[leasepos]) - 1]) { case 'w': case 'W': fac *= 7; /* fall through */ case 'd': case 'D': fac *= 24; /* fall through */ case 'h': case 'H': fac *= 60; /* fall through */ case 'm': case 'M': fac *= 60; /* fall through */ case 's': case 'S': 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_free(_("bad dhcp-range"), new); new->lease_time = atoi(a[leasepos]) * fac; new->flags |= CONTEXT_SETLEASE; /* Leases of a minute or less confuse some clients, notably Apple's */ if (new->lease_time < 120) new->lease_time = 120; } } } break; } case LOPT_BANK: case 'G': /* --dhcp-host */ { struct dhcp_config *new; struct in_addr in; new = opt_malloc(sizeof(struct dhcp_config)); new->next = daemon->dhcp_conf; new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0; new->hwaddr = NULL; new->netid = NULL; new->filter = NULL; new->clid = NULL; #ifdef HAVE_DHCP6 new->addr6 = NULL; #endif while (arg) { comma = split(arg); if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */ { if ((arg[0] == 'i' || arg[0] == 'I') && (arg[1] == 'd' || arg[1] == 'D') && arg[2] == ':') { if (arg[3] == '*') new->flags |= CONFIG_NOCLID; else { int len; arg += 3; /* dump id: */ if (strchr(arg, ':')) len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL); else { unhide_metas(arg); len = (int) strlen(arg); } if (len == -1) { dhcp_config_free(new); ret_err(_("bad hex constant")); } else if ((new->clid = opt_malloc(len))) { new->flags |= CONFIG_CLID; new->clid_len = len; memcpy(new->clid, arg, len); } } } /* dhcp-host has strange backwards-compat needs. */ else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg) { struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list)); newlist->next = new->netid; new->netid = newlist; newlist->list = dhcp_netid_create(arg+4, NULL); } else if (strstr(arg, "tag:") == arg) new->filter = dhcp_netid_create(arg+4, new->filter); #ifdef HAVE_DHCP6 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']') { char *pref; struct in6_addr in6; struct addrlist *new_addr; arg[strlen(arg)-1] = 0; arg++; pref = split_chr(arg, '/'); if (!inet_pton(AF_INET6, arg, &in6)) { dhcp_config_free(new); ret_err(_("bad IPv6 address")); } new_addr = opt_malloc(sizeof(struct addrlist)); new_addr->flags = 0; new_addr->addr.addr6 = in6; if (pref) { u64 addrpart = addr6part(&in6); if (!atoi_check(pref, &new_addr->prefixlen) || new_addr->prefixlen > 128 || ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0) { dhcp_config_free(new); ret_err_free(_("bad IPv6 prefix"), new_addr); } new_addr->flags |= ADDRLIST_PREFIX; } for (i= 0; i < 8; i++) if (in6.s6_addr[i] != 0) break; /* set WILDCARD if network part all zeros */ if (i == 8) new_addr->flags |= ADDRLIST_WILDCARD; new_addr->next = new->addr6; new->addr6 = new_addr; new->flags |= CONFIG_ADDR6; } #endif else { struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config)); if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX, &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1) { free(newhw); dhcp_config_free(new); ret_err(_("bad hex constant")); } else { newhw->next = new->hwaddr; new->hwaddr = newhw; } } } else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &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) { inet_ntop(AF_INET, &in, daemon->addrbuff, ADDRSTRLEN); sprintf(errstr, _("duplicate dhcp-host IP address %s"), daemon->addrbuff); dhcp_config_free(new); return 0; } } else { char *cp, *lastp = NULL, last = 0; int fac = 1, isdig = 0; if (strlen(arg) > 1) { lastp = arg + strlen(arg) - 1; last = *lastp; switch (last) { case 'w': case 'W': fac *= 7; /* fall through */ case 'd': case 'D': fac *= 24; /* fall through */ case 'h': case 'H': fac *= 60; /* fall through */ case 'm': case 'M': fac *= 60; /* fall through */ case 's': case 'S': *lastp = 0; } } for (cp = arg; *cp; cp++) if (isdigit((unsigned char)*cp)) isdig = 1; else if (*cp != ' ') break; if (*cp) { if (lastp) *lastp = last; if (strcmp(arg, "infinite") == 0) { new->lease_time = 0xffffffff; new->flags |= CONFIG_TIME; } else if (strcmp(arg, "ignore") == 0) new->flags |= CONFIG_DISABLE; else if (new->hostname) { dhcp_config_free(new); ret_err(_("DHCP host has multiple names")); } else { if (!(new->hostname = canonicalise_opt(arg)) || !legal_hostname(new->hostname)) { dhcp_config_free(new); ret_err(_("bad DHCP host name")); } new->flags |= CONFIG_NAME; new->domain = strip_hostname(new->hostname); } } else if (isdig) { new->lease_time = atoi(arg) * fac; /* Leases of a minute or less confuse some clients, notably Apple's */ if (new->lease_time < 120) new->lease_time = 120; new->flags |= CONFIG_TIME; } } arg = comma; } daemon->dhcp_conf = new; break; } case LOPT_TAG_IF: /* --tag-if */ { struct tag_if *new = opt_malloc(sizeof(struct tag_if)); new->tag = NULL; new->set = NULL; new->next = NULL; /* preserve order */ if (!daemon->tag_if) daemon->tag_if = new; else { struct tag_if *tmp; for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next); tmp->next = new; } while (arg) { size_t len; comma = split(arg); len = strlen(arg); if (len < 5) { new->set = NULL; break; } else { struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL); if (strstr(arg, "set:") == arg) { struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list)); newlist->next = new->set; new->set = newlist; newlist->list = newtag; } else if (strstr(arg, "tag:") == arg) { newtag->next = new->tag; new->tag = newtag; } else { new->set = NULL; dhcp_netid_free(newtag); break; } } arg = comma; } if (!new->set) { dhcp_netid_free(new->tag); dhcp_netid_list_free(new->set); ret_err_free(_("bad tag-if"), new); } break; } case 'O': /* --dhcp-option */ case LOPT_FORCE: /* --dhcp-option-force */ case LOPT_PXE_OPT: /* --dhcp-option-pxe */ case LOPT_OPTS: case LOPT_MATCH: /* --dhcp-match */ return parse_dhcp_opt(errstr, arg, option == LOPT_FORCE ? DHOPT_FORCE : (option == LOPT_MATCH ? DHOPT_MATCH : (option == LOPT_OPTS ? DHOPT_BANK : (option == LOPT_PXE_OPT ? DHOPT_PXE_OPT : 0)))); case LOPT_NAME_MATCH: /* --dhcp-name-match */ { struct dhcp_match_name *new; ssize_t len; if (!(comma = split(arg)) || (len = strlen(comma)) == 0) ret_err(gen_err); new = opt_malloc(sizeof(struct dhcp_match_name)); new->wildcard = 0; new->netid = opt_malloc(sizeof(struct dhcp_netid)); new->netid->net = opt_string_alloc(set_prefix(arg)); if (comma[len-1] == '*') { comma[len-1] = 0; new->wildcard = 1; } new->name = opt_string_alloc(comma); new->next = daemon->dhcp_name_match; daemon->dhcp_name_match = new; break; } case 'M': /* --dhcp-boot */ { struct dhcp_netid *id = dhcp_tags(&arg); if (!arg) { ret_err(gen_err); } else { 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; if (comma) { arg = comma; comma = split(arg); dhcp_sname = opt_string_alloc(arg); if (comma) { unhide_metas(comma); 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; } } } 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; } case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */ { struct dhcp_netid *id = dhcp_tags(&arg); if (!arg) { ret_err(gen_err); } else { struct delay_config *new; int delay; if (!atoi_check(arg, &delay)) ret_err(gen_err); new = opt_malloc(sizeof(struct delay_config)); new->delay = delay; new->netid = id; new->next = daemon->delay_conf; daemon->delay_conf = new; } break; } case LOPT_PXE_PROMT: /* --pxe-prompt */ { struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt)); int timeout; new->netid = NULL; new->opt = 10; /* PXE_MENU_PROMPT */ new->netid = dhcp_tags(&arg); if (!arg) { dhcp_opt_free(new); ret_err(gen_err); } else { comma = split(arg); unhide_metas(arg); new->len = strlen(arg) + 1; new->val = opt_malloc(new->len); memcpy(new->val + 1, arg, new->len - 1); new->u.vendor_class = NULL; new->flags = DHOPT_VENDOR | DHOPT_VENDOR_PXE; if (comma && atoi_check(comma, &timeout)) *(new->val) = timeout; else *(new->val) = 255; new->next = daemon->dhcp_opts; daemon->dhcp_opts = new; daemon->enable_pxe = 1; } break; } case LOPT_PXE_SERV: /* --pxe-service */ { struct pxe_service *new = opt_malloc(sizeof(struct pxe_service)); char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client", "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI", "ARM32_EFI", "ARM64_EFI", NULL }; static int boottype = 32768; new->netid = NULL; new->sname = NULL; new->server.s_addr = 0; new->netid = dhcp_tags(&arg); if (arg && (comma = split(arg))) { for (i = 0; CSA[i]; i++) if (strcasecmp(CSA[i], arg) == 0) break; if (CSA[i] || atoi_check(arg, &i)) { arg = comma; comma = split(arg); new->CSA = i; new->menu = opt_string_alloc(arg); if (!comma) { new->type = 0; /* local boot */ new->basename = NULL; } else { arg = comma; comma = split(arg); if (atoi_check(arg, &i)) { new->type = i; new->basename = NULL; } else { new->type = boottype++; new->basename = opt_string_alloc(arg); } 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) daemon->pxe_services = new; else { struct pxe_service *s; for (s = daemon->pxe_services; s->next; s = s->next); s->next = new; } daemon->enable_pxe = 1; break; } } dhcp_netid_free(new->netid); free(new); ret_err(gen_err); } case '4': /* --dhcp-mac */ { if (!(comma = split(arg))) ret_err(gen_err); else { struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac)); new->netid.net = opt_string_alloc(set_prefix(arg)); unhide_metas(comma); new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type); if (new->hwaddr_len == -1) { free(new->netid.net); ret_err_free(gen_err, new); } else { new->next = daemon->dhcp_macs; daemon->dhcp_macs = new; } } } break; case 'U': /* --dhcp-vendorclass */ case 'j': /* --dhcp-userclass */ case LOPT_CIRCUIT: /* --dhcp-circuitid */ case LOPT_REMOTE: /* --dhcp-remoteid */ case LOPT_SUBSCR: /* --dhcp-subscrid */ { unsigned char *p; int dig, colon; struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor)); if (!(comma = split(arg))) ret_err_free(gen_err, new); 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) { free(new->netid.net); ret_err_free(gen_err, new); } else new->enterprise = atoi(arg+11); } else comma = arg; for (dig = 0, colon = 0, p = (unsigned char *)comma; *p; p++) if (isxdigit(*p)) dig = 1; else if (*p == ':') colon = 1; else break; unhide_metas(comma); if (option == 'U' || option == 'j' || *p || !dig || !colon) { 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; break; } case LOPT_ALTPORT: /* --dhcp-alternate-port */ if (!arg) { daemon->dhcp_server_port = DHCP_SERVER_ALTPORT; daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT; } else { comma = split(arg); if (!atoi_check16(arg, &daemon->dhcp_server_port) || (comma && !atoi_check16(comma, &daemon->dhcp_client_port))) ret_err(_("invalid port number")); if (!comma) daemon->dhcp_client_port = daemon->dhcp_server_port+1; } break; case 'J': /* --dhcp-ignore */ case LOPT_NO_NAMES: /* --dhcp-ignore-names */ case LOPT_BROADCAST: /* --dhcp-broadcast */ case '3': /* --bootp-dynamic */ case LOPT_GEN_NAMES: /* --dhcp-generate-names */ { struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list)); struct dhcp_netid *list = NULL; if (option == 'J') { new->next = daemon->dhcp_ignore; daemon->dhcp_ignore = new; } else if (option == LOPT_BROADCAST) { new->next = daemon->force_broadcast; daemon->force_broadcast = new; } else if (option == '3') { new->next = daemon->bootp_dynamic; daemon->bootp_dynamic = new; } else if (option == LOPT_GEN_NAMES) { new->next = daemon->dhcp_gen_names; daemon->dhcp_gen_names = new; } else { new->next = daemon->dhcp_ignore_names; daemon->dhcp_ignore_names = new; } while (arg) { comma = split(arg); list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list); arg = comma; } new->list = list; break; } case LOPT_PROXY: /* --dhcp-proxy */ daemon->override = 1; while (arg) { struct addr_list *new = opt_malloc(sizeof(struct addr_list)); comma = split(arg); if (!(inet_pton(AF_INET, arg, &new->addr) > 0)) ret_err_free(_("bad dhcp-proxy address"), new); new->next = daemon->override_relays; daemon->override_relays = new; arg = comma; } break; case LOPT_PXE_VENDOR: /* --dhcp-pxe-vendor */ { while (arg) { struct dhcp_pxe_vendor *new = opt_malloc(sizeof(struct dhcp_pxe_vendor)); comma = split(arg); new->data = opt_string_alloc(arg); new->next = daemon->dhcp_pxe_vendors; daemon->dhcp_pxe_vendors = new; arg = comma; } } break; case LOPT_RELAY: /* --dhcp-relay */ { struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay)); char *two = split(arg); char *three = split(two); new->iface_index = 0; if (two) { if (inet_pton(AF_INET, arg, &new->local)) { char *hash = split_chr(two, '#'); if (!hash || !atoi_check16(hash, &new->port)) new->port = DHCP_SERVER_PORT; if (!inet_pton(AF_INET, two, &new->server)) { new->server.addr4.s_addr = 0; /* Fail for three arg version where there are not two addresses. Also fail when broadcasting to wildcard address. */ if (three || strchr(two, '*')) two = NULL; else three = two; } new->next = daemon->relay4; daemon->relay4 = new; } #ifdef HAVE_DHCP6 else if (inet_pton(AF_INET6, arg, &new->local)) { char *hash = split_chr(two, '#'); if (!hash || !atoi_check16(hash, &new->port)) new->port = DHCPV6_SERVER_PORT; if (!inet_pton(AF_INET6, two, &new->server)) { inet_pton(AF_INET6, ALL_SERVERS, &new->server.addr6); /* Fail for three arg version where there are not two addresses. Also fail when multicasting to wildcard address. */ if (three || strchr(two, '*')) two = NULL; else three = two; } new->next = daemon->relay6; daemon->relay6 = new; } #endif new->interface = opt_string_alloc(three); } if (!two) { free(new->interface); ret_err_free(_("Bad dhcp-relay"), new); } 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->mtu = 0; new->mtu_name = NULL; new->name = opt_string_alloc(arg); if (strcasestr(comma, "mtu:") == comma) { arg = comma + 4; if (!(comma = split(comma))) goto err; if (!strcasecmp(arg, "off")) new->mtu = -1; else if (!atoi_check(arg, &new->mtu)) new->mtu_name = opt_string_alloc(arg); else if (new->mtu < 1280) goto err; } 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))) { err: free(new->name); ret_err_free(_("bad RA-params"), new); } 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 }; int k = 0; struct doctor *new = opt_malloc(sizeof(struct doctor)); new->next = daemon->doctors; daemon->doctors = new; new->mask.s_addr = 0xffffffff; new->end.s_addr = 0; if ((a[0] = arg)) for (k = 1; k < 3; k++) { if (!(a[k] = split(a[k-1]))) break; unhide_metas(a[k]); } dash = split_chr(a[0], '-'); if ((k < 2) || (!(inet_pton(AF_INET, a[0], &new->in) > 0)) || (!(inet_pton(AF_INET, a[1], &new->out) > 0)) || (k == 3 && !inet_pton(AF_INET, a[2], &new->mask))) ret_err(_("missing address in alias")); if (dash && (!(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))) ret_err_free(_("invalid alias range"), new); break; } case LOPT_INTNAME: /* --interface-name */ case LOPT_DYNHOST: /* --dynamic-host */ { struct interface_name *new, **up; char *domain = arg; arg = split(arg); new = opt_malloc(sizeof(struct interface_name)); memset(new, 0, sizeof(struct interface_name)); new->flags = IN4 | IN6; /* Add to the end of the list, so that first name of an interface is used for PTR lookups. */ for (up = &daemon->int_names; *up; up = &((*up)->next)); *up = new; while ((comma = split(arg))) { if (inet_pton(AF_INET, arg, &new->proto4)) new->flags |= INP4; else if (inet_pton(AF_INET6, arg, &new->proto6)) new->flags |= INP6; else break; arg = comma; } if ((comma = split_chr(arg, '/'))) { if (strcmp(comma, "4") == 0) new->flags &= ~IN6; else if (strcmp(comma, "6") == 0) new->flags &= ~IN4; else ret_err_free(gen_err, new); } new->intr = opt_string_alloc(arg); if (option == LOPT_DYNHOST) { if (!(new->flags & (INP4 | INP6))) ret_err(_("missing address in dynamic host")); if (!(new->flags & IN4) || !(new->flags & IN6)) arg = NULL; /* provoke error below */ new->flags &= ~(IN4 | IN6); } else { if (new->flags & (INP4 | INP6)) arg = NULL; /* provoke error below */ } if (!domain || !arg || !(new->name = canonicalise_opt(domain))) ret_err(option == LOPT_DYNHOST ? _("bad dynamic host") : _("bad interface name")); break; } case LOPT_CNAME: /* --cname */ { struct cname *new; char *alias, *target=NULL, *last, *pen; int ttl = -1; for (last = pen = NULL, comma = arg; comma; comma = split(comma)) { pen = last; last = comma; } if (!pen) ret_err(_("bad CNAME")); if (pen != arg && atoi_check(last, &ttl)) last = pen; while (arg != last) { int arglen = strlen(arg); alias = canonicalise_opt(arg); if (!target) target = canonicalise_opt(last); if (!alias || !target) { free(target); free(alias); ret_err(_("bad CNAME")); } for (new = daemon->cnames; new; new = new->next) if (hostname_isequal(new->alias, alias)) { free(target); free(alias); ret_err(_("duplicate CNAME")); } new = opt_malloc(sizeof(struct cname)); new->next = daemon->cnames; daemon->cnames = new; new->alias = alias; new->target = target; new->ttl = ttl; for (arg += arglen+1; *arg && isspace((unsigned char)*arg); arg++); } break; } case LOPT_PTR: /* --ptr-record */ { struct ptr_record *new; char *dom, *target = NULL; comma = split(arg); if (!(dom = canonicalise_opt(arg)) || (comma && !(target = canonicalise_opt(comma)))) { free(dom); free(target); ret_err(_("bad PTR record")); } else { new = opt_malloc(sizeof(struct ptr_record)); new->next = daemon->ptr; daemon->ptr = new; new->name = dom; new->ptr = target; } break; } case LOPT_NAPTR: /* --naptr-record */ { char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; int k = 0; struct naptr *new; int order, pref; char *name=NULL, *replace = NULL; if ((a[0] = arg)) for (k = 1; k < 7; k++) if (!(a[k] = split(a[k-1]))) break; if (k < 6 || !(name = canonicalise_opt(a[0])) || !atoi_check16(a[1], &order) || !atoi_check16(a[2], &pref) || (k == 7 && !(replace = canonicalise_opt(a[6])))) { free(name); free(replace); ret_err(_("bad NAPTR record")); } else { new = opt_malloc(sizeof(struct naptr)); new->next = daemon->naptr; daemon->naptr = new; new->name = name; new->flags = opt_string_alloc(a[3]); new->services = opt_string_alloc(a[4]); new->regexp = opt_string_alloc(a[5]); new->replace = replace; new->order = order; new->pref = pref; } break; } case LOPT_RR: /* dns-rr */ { struct txt_record *new; size_t len = 0; char *data; int class; comma = split(arg); data = split(comma); new = opt_malloc(sizeof(struct txt_record)); new->name = NULL; if (!atoi_check(comma, &class) || !(new->name = canonicalise_opt(arg)) || (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U)) { free(new->name); ret_err_free(_("bad RR record"), new); } new->len = 0; new->class = class; new->next = daemon->rr; daemon->rr = new; if (data) { new->txt = opt_malloc(len); new->len = len; memcpy(new->txt, data, len); } break; } case LOPT_CAA: /* --caa-record */ { struct txt_record *new; char *tag, *value; int flags; comma = split(arg); tag = split(comma); value = split(tag); new = opt_malloc(sizeof(struct txt_record)); new->next = daemon->rr; daemon->rr = new; if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg))) ret_err(_("bad CAA record")); unhide_metas(tag); unhide_metas(value); new->len = strlen(tag) + strlen(value) + 2; new->txt = opt_malloc(new->len); new->txt[0] = flags; new->txt[1] = strlen(tag); memcpy(&new->txt[2], tag, strlen(tag)); memcpy(&new->txt[2 + strlen(tag)], value, strlen(value)); new->class = T_CAA; break; } case 'Y': /* --txt-record */ { struct txt_record *new; unsigned char *p, *cnt; size_t len; comma = split(arg); new = opt_malloc(sizeof(struct txt_record)); new->class = C_IN; new->stat = 0; if (!(new->name = canonicalise_opt(arg))) ret_err_free(_("bad TXT record"), new); new->next = daemon->txt; daemon->txt = new; len = comma ? strlen(comma) : 0; len += (len/255) + 1; /* room for extra counts */ new->txt = p = opt_malloc(len); cnt = p++; *cnt = 0; while (comma && *comma) { unsigned char c = (unsigned char)*comma++; if (c == ',' || *cnt == 255) { if (c != ',') comma--; cnt = p++; *cnt = 0; } else { *p++ = unhide_meta(c); (*cnt)++; } } new->len = p - new->txt; break; } case 'W': /* --srv-host */ { int port = 1, priority = 0, weight = 0; char *name, *target = NULL; struct mx_srv_record *new; comma = split(arg); if (!(name = canonicalise_opt(arg))) ret_err(_("bad SRV record")); if (comma) { arg = comma; comma = split(arg); if (!(target = canonicalise_opt(arg))) ret_err_free(_("bad SRV target"), name); if (comma) { arg = comma; comma = split(arg); if (!atoi_check16(arg, &port)) { free(name); ret_err_free(_("invalid port number"), target); } if (comma) { arg = comma; comma = split(arg); if (!atoi_check16(arg, &priority)) { free(name); ret_err_free(_("invalid priority"), target); } if (comma && !atoi_check16(comma, &weight)) { free(name); ret_err_free(_("invalid weight"), target); } } } } new = opt_malloc(sizeof(struct mx_srv_record)); new->next = daemon->mxnames; daemon->mxnames = new; new->issrv = 1; new->name = name; new->target = target; new->srvport = port; new->priority = priority; new->weight = weight; break; } case LOPT_HOST_REC: /* --host-record */ { struct host_record *new; if (!arg || !(comma = split(arg))) ret_err(_("Bad host-record")); new = opt_malloc(sizeof(struct host_record)); memset(new, 0, sizeof(struct host_record)); new->ttl = -1; new->flags = 0; while (arg) { union all_addr addr; char *dig; for (dig = arg; *dig != 0; dig++) if (*dig < '0' || *dig > '9') break; if (*dig == 0) new->ttl = atoi(arg); else if (inet_pton(AF_INET, arg, &addr.addr4)) { new->addr = addr.addr4; new->flags |= HR_4; } else if (inet_pton(AF_INET6, arg, &addr.addr6)) { new->addr6 = addr.addr6; new->flags |= HR_6; } else { char *canon = canonicalise_opt(arg); struct name_list *nl; if (!canon) { struct name_list *tmp, *next; for (tmp = new->names; tmp; tmp = next) { next = tmp->next; free(tmp); } ret_err_free(_("Bad name in host-record"), new); } nl = opt_malloc(sizeof(struct name_list)); 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; } case LOPT_STALE_CACHE: /* --use-stale-cache */ { int max_expiry = STALE_CACHE_EXPIRY; if (arg) { /* Don't accept negative TTLs here, they'd have the counter-intuitive side-effect of evicting cache records before they expire */ if (!atoi_check(arg, &max_expiry) || max_expiry < 0) ret_err(gen_err); /* Store "serve expired forever" as -1 internally, the option isn't active for daemon->cache_max_expiry == 0 */ if (max_expiry == 0) max_expiry = -1; } daemon->cache_max_expiry = max_expiry; break; } #ifdef HAVE_DNSSEC case LOPT_DNSSEC_LIMITS: { int lim, val; for (lim = LIMIT_SIG_FAIL; arg && lim < LIMIT_MAX ; lim++, arg = comma) { comma = split(arg); if (!atoi_check(arg, &val)) ret_err(gen_err); if (val != 0) daemon->limit[lim] = val; } break; } case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */ daemon->timestamp_file = opt_string_alloc(arg); break; case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */ if (arg) { if (strcmp(arg, "no") == 0) set_option_bool(OPT_DNSSEC_IGN_NS); else ret_err(_("bad value for dnssec-check-unsigned")); } break; case LOPT_TRUST_ANCHOR: /* --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; new->name = NULL; 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_free(_("bad trust anchor"), new); /* 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((unsigned char)*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) { free(new->name); ret_err_free(_("bad HEX in trust anchor"), new); } new->next = daemon->ds; daemon->ds = new; break; } #endif case LOPT_MAX_PROCS: /* --max-tcp-connections */ { int max_procs; /* Don't accept numbers less than 1. */ if (!atoi_check(arg, &max_procs) || max_procs < 1) ret_err(gen_err); daemon->max_procs = max_procs; break; } 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, int from_script) { volatile int lineno = 0; char *buff = daemon->namebuff; while (fgets(buff, MAXDNAME, f)) { int white, i; volatile int option; char *errmess, *p, *arg, *start; size_t len; option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt; /* Memory allocation failure longjmps here if mem_recover == 1 */ if (option != 0 || hard_opt == LOPT_REV_SERV) { if (setjmp(mem_jmp)) continue; mem_recover = 1; } arg = NULL; lineno++; errmess = NULL; /* Implement quotes, inside quotes we allow \\ \" \n and \t metacharacters get hidden also strip comments */ for (white = 1, p = buff; *p; p++) { if (*p == '"') { memmove(p, p+1, strlen(p+1)+1); for(; *p && *p != '"'; p++) { if (*p == '\\' && strchr("\"tnebr\\", p[1])) { if (p[1] == 't') p[1] = '\t'; else if (p[1] == 'n') p[1] = '\n'; else if (p[1] == 'b') p[1] = '\b'; else if (p[1] == 'r') p[1] = '\r'; else if (p[1] == 'e') /* escape */ p[1] = '\033'; memmove(p, p+1, strlen(p+1)+1); } *p = hide_meta(*p); } if (*p == 0) { errmess = _("missing \""); goto oops; } memmove(p, p+1, strlen(p+1)+1); } if (isspace((unsigned char)*p)) { *p = ' '; white = 1; } else { if (white && *p == '#') { *p = 0; break; } white = 0; } } /* strip leading spaces */ for (start = buff; *start && *start == ' '; start++); /* strip trailing spaces */ for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--); if (len == 0) continue; else start[len] = 0; if (option != 0) arg = start; else if ((p=strchr(start, '='))) { /* allow spaces around "=" */ for (arg = p+1; *arg == ' '; arg++); for (; p >= start && (*p == ' ' || *p == '='); p--) *p = 0; } else arg = NULL; if (option == 0) { for (option = 0, i = 0; opts[i].name; i++) if (strcmp(opts[i].name, start) == 0) { option = opts[i].val; break; } if (!option) errmess = _("bad option"); else if (opts[i].has_arg == 0 && arg) 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"); } oops: if (errmess) strcpy(daemon->namebuff, errmess); if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV)) { if (from_script) sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" in output from %s"), file); else sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file); if (hard_opt != 0) my_syslog(LOG_ERR, "%s", daemon->namebuff); else die("%s", daemon->namebuff, EC_BADCONF); } } mem_recover = 0; } #if defined(HAVE_DHCP) && defined(HAVE_INOTIFY) 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, do_popen = 0; static int read_stdin = 0; static struct fileread { dev_t dev; ino_t ino; struct fileread *next; } *filesread = NULL; if (hard_opt == LOPT_CONF_OPT) { /* default conf-file reading */ hard_opt = 0; nofile_ok = 1; } if (hard_opt == LOPT_CONF_SCRIPT) { hard_opt = 0; do_popen = 1; } if (hard_opt == 0 && !do_popen && strcmp(file, "-") == 0) { if (read_stdin == 1) return 1; read_stdin = 1; file = "stdin"; f = stdin; } else { /* ignore repeated files. */ struct stat statbuf; if (hard_opt == 0 && stat(file, &statbuf) == 0) { struct fileread *r; for (r = filesread; r; r = r->next) if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino) return 1; r = safe_malloc(sizeof(struct fileread)); r->next = filesread; filesread = r; r->dev = statbuf.st_dev; r->ino = statbuf.st_ino; } if (do_popen) { if (!(f = popen(file, "r"))) die(_("cannot execute %s: %s"), file, EC_FILE); } else if (!(f = fopen(file, "r"))) { if (errno == ENOENT && nofile_ok) 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 0; } else die(str, file, EC_FILE); } } } read_file(file, f, hard_opt, do_popen); if (do_popen) { int rc; if ((rc = pclose(f)) == -1) die(_("error executing %s: %s"), file, EC_MISC); if (rc != 0) die(_("%s returns non-zero error code"), file, rc+10); } else fclose(f); return 1; } static int file_filter(const struct dirent *ent) { size_t lenfile = strlen(ent->d_name); /* 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] == '.') return 0; return 1; } /* expand any name which is a directory */ struct hostsfile *expand_filelist(struct hostsfile *list) { unsigned int i; int entcnt, n; struct hostsfile *ah, *last, *next, **up; struct dirent **namelist; /* find largest used index */ for (i = SRC_AH, ah = list; ah; ah = ah->next) { last = ah; if (i <= ah->index) i = ah->index + 1; if (ah->flags & AH_DIR) ah->flags |= AH_INACTIVE; else ah->flags &= ~AH_INACTIVE; } for (ah = list; ah; ah = ah->next) if (!(ah->flags & AH_INACTIVE)) { struct stat buf; if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode)) { struct dirent *ent; /* don't read this as a file */ ah->flags |= AH_INACTIVE; entcnt = scandir(ah->fname, &namelist, file_filter, alphasort); if (entcnt < 0) my_syslog(LOG_ERR, _("cannot access directory %s: %s"), ah->fname, strerror(errno)); else { for (n = 0; n < entcnt; n++) { ent = namelist[n]; size_t lendir = strlen(ah->fname); size_t lenfile = strlen(ent->d_name); struct hostsfile *ah1; char *path; /* see if we have an existing record. dir is ah->fname file is ent->d_name path to match is ah1->fname */ for (up = &list, ah1 = list; ah1; ah1 = next) { next = ah1->next; if (lendir < strlen(ah1->fname) && strstr(ah1->fname, ah->fname) == ah1->fname && ah1->fname[lendir] == '/' && strcmp(ah1->fname + lendir + 1, ent->d_name) == 0) { ah1->flags &= ~AH_INACTIVE; /* If found, remove from list to re-insert at the end. Unless it's already at the end. */ if (last != ah1) *up = next; break; } up = &ah1->next; } /* make new record */ if (!ah1) { if (!(ah1 = whine_malloc(sizeof(struct hostsfile)))) continue; if (!(path = whine_malloc(lendir + lenfile + 2))) { free(ah1); continue; } strcpy(path, ah->fname); strcat(path, "/"); strcat(path, ent->d_name); ah1->fname = path; ah1->index = i++; ah1->flags = AH_DIR; } /* Edge case, may be the last in the list anyway */ if (last != ah1) last->next = ah1; ah1->next = NULL; last = ah1; /* inactivate record if not regular file */ if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode)) ah1->flags |= AH_INACTIVE; } } free(namelist); } } 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); read_file(daemon->servers_file, f, LOPT_REV_SERV, 0); fclose(f); cleanup_servers(); check_servers(0); } #ifdef HAVE_DHCP static void clear_dynamic_conf(void) { struct dhcp_config *configs, *cp, **up; /* remove existing... */ for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp) { cp = configs->next; if (configs->flags & CONFIG_BANK) { *up = cp; dhcp_config_free(configs); } else up = &configs->next; } } static void clear_dhcp_opt(struct dhcp_opt **dhcp_opts) { struct dhcp_opt *opts, *cp, **up; for (up = dhcp_opts, opts = *dhcp_opts; opts; opts = cp) { cp = opts->next; if (opts->flags & DHOPT_BANK) { *up = cp; dhcp_opt_free(opts); } else up = &opts->next; } } static void clear_dynamic_opt(void) { clear_dhcp_opt(&daemon->dhcp_opts); #ifdef HAVE_DHCP6 clear_dhcp_opt(&daemon->dhcp_opts6); #endif } void reread_dhcp(void) { struct hostsfile *hf; /* Do these even if there is no daemon->dhcp_hosts_file or daemon->dhcp_opts_file since entries may have been created by the inotify dynamic file reading system. */ clear_dynamic_conf(); clear_dynamic_opt(); if (daemon->dhcp_hosts_file) { daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file); for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next) if (!(hf->flags & AH_INACTIVE)) { if (one_file(hf->fname, LOPT_BANK)) my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname); } } if (daemon->dhcp_opts_file) { daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file); for (hf = daemon->dhcp_opts_file; hf; hf = hf->next) if (!(hf->flags & AH_INACTIVE)) { if (one_file(hf->fname, LOPT_OPTS)) my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname); } } # ifdef HAVE_INOTIFY /* Setup notify and read pre-existing files. */ set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0); # endif } #endif void read_opts(int argc, char **argv, char *compile_opts) { size_t argbuf_size = MAXDNAME; char *argbuf = opt_malloc(argbuf_size); /* 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. */ char *buff = opt_malloc((MAXDNAME * 2) + 1); int option, testmode = 0; char *arg, *conffile = NULL; opterr = 0; daemon = opt_malloc(sizeof(struct daemon)); memset(daemon, 0, sizeof(struct daemon)); daemon->namebuff = buff; daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1); daemon->addrbuff = safe_malloc(ADDRSTRLEN); /* Set defaults - everything else is zero or NULL */ daemon->cachesize = CACHESIZ; daemon->ftabsize = FTABSIZ; daemon->port = NAMESERVER_PORT; daemon->dhcp_client_port = DHCP_CLIENT_PORT; daemon->dhcp_server_port = DHCP_SERVER_PORT; daemon->default_resolv.is_default = 1; daemon->default_resolv.name = RESOLVFILE; daemon->resolv_files = &daemon->default_resolv; daemon->username = CHUSER; daemon->runfile = RUNFILE; daemon->dhcp_max = MAXLEASES; daemon->tftp_max = TFTP_MAX_CONNECTIONS; daemon->edns_pktsz = EDNS_PKTSZ; daemon->log_fac = -1; daemon->auth_ttl = AUTH_TTL; daemon->soa_refresh = SOA_REFRESH; daemon->soa_retry = SOA_RETRY; daemon->soa_expiry = SOA_EXPIRY; daemon->randport_limit = 1; daemon->host_index = SRC_AH; daemon->max_procs = MAX_PROCS; #ifdef HAVE_DNSSEC daemon->limit[LIMIT_SIG_FAIL] = DNSSEC_LIMIT_SIG_FAIL; daemon->limit[LIMIT_CRYPTO] = DNSSEC_LIMIT_CRYPTO; daemon->limit[LIMIT_WORK] = DNSSEC_LIMIT_WORK; daemon->limit[LIMIT_NSEC3_ITERS] = DNSSEC_LIMIT_NSEC3_ITERS; #endif /* See comment above make_servers(). Optimises server-read code. */ mark_servers(0); while (1) { #ifdef HAVE_GETOPT_LONG option = getopt_long(argc, argv, OPTSTRING, opts, NULL); #else option = getopt(argc, argv, OPTSTRING); #endif if (option == -1) { for (; optind < argc; optind++) { unsigned char *c = (unsigned char *)argv[optind]; for (; *c != 0; c++) if (!isspace(*c)) die(_("junk found in command line"), NULL, EC_BADCONF); } break; } /* Copy optarg so that argv doesn't get changed */ if (optarg) { if (strlen(optarg) >= argbuf_size) { free(argbuf); argbuf_size = strlen(optarg) + 1; argbuf = opt_malloc(argbuf_size); } safe_strncpy(argbuf, optarg, argbuf_size); arg = argbuf; } else arg = NULL; /* command-line only stuff */ if (option == LOPT_TEST) testmode = 1; else if (option == 'w') { #ifdef HAVE_DHCP 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(_("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")); exit(0); } else if (option == 'C') { if (!conffile) conffile = opt_string_alloc(arg); else { char *extra = opt_string_alloc(arg); one_file(extra, 0); free(extra); } } else { #ifdef HAVE_GETOPT_LONG if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0)) #else if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0)) #endif die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF); } } free(argbuf); if (conffile) { one_file(conffile, 0); free(conffile); } else one_file(CONFFILE, LOPT_CONF_OPT); /* Add TXT records if wanted */ #ifndef NO_ID if (!option_bool(OPT_NO_IDENT)) { 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); } #endif #ifdef HAVE_DNSSEC /* Default fast retry on when doing DNSSEC */ if (option_bool(OPT_DNSSEC_VALID) && daemon->fast_retry_time == 0) { daemon->fast_retry_timeout = TIMEOUT; daemon->fast_retry_time = DEFAULT_FAST_RETRY; } #endif /* 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); else if (tmp->source_addr.sa.sa_family == AF_INET6) tmp->source_addr.in6.sin6_port = htons(daemon->query_port); } } if (daemon->host_records) { struct host_record *hr; for (hr = daemon->host_records; hr; hr = hr->next) if (hr->ttl == -1) hr->ttl = daemon->local_ttl; } if (daemon->cnames) { struct cname *cn, *cn2, *cn3; #define NOLOOP 1 #define TESTLOOP 2 /* Fill in TTL for CNAMES now we have local_ttl. Also prepare to do loop detection. */ for (cn = daemon->cnames; cn; cn = cn->next) { if (cn->ttl == -1) cn->ttl = daemon->local_ttl; cn->flag = 0; cn->targetp = NULL; for (cn2 = daemon->cnames; cn2; cn2 = cn2->next) if (hostname_isequal(cn->target, cn2->alias)) { cn->targetp = cn2; break; } } /* Find any CNAME loops.*/ for (cn = daemon->cnames; cn; cn = cn->next) { for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp) { if (cn2->flag == NOLOOP) break; if (cn2->flag == TESTLOOP) die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF); cn2->flag = TESTLOOP; } for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp) cn3->flag = NOLOOP; } } if (daemon->if_addrs) { struct iname *tmp; for(tmp = daemon->if_addrs; tmp; tmp = tmp->next) if (tmp->addr.sa.sa_family == AF_INET) tmp->addr.in.sin_port = htons(daemon->port); else if (tmp->addr.sa.sa_family == AF_INET6) tmp->addr.in6.sin6_port = htons(daemon->port); } /* create default, if not specified */ if (daemon->authserver && !daemon->hostmaster) { strcpy(buff, "hostmaster."); strcat(buff, daemon->authserver); daemon->hostmaster = opt_string_alloc(buff); } if (!daemon->dhcp_pxe_vendors) { daemon->dhcp_pxe_vendors = opt_malloc(sizeof(struct dhcp_pxe_vendor)); daemon->dhcp_pxe_vendors->data = opt_string_alloc(DHCP_PXE_DEF_VENDOR); daemon->dhcp_pxe_vendors->next = NULL; } /* only one of these need be specified: the other defaults to the host-name */ if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget) { struct mx_srv_record *mx; if (gethostname(buff, MAXDNAME) == -1) die(_("cannot get host-name: %s"), NULL, EC_MISC); for (mx = daemon->mxnames; mx; mx = mx->next) if (!mx->issrv && hostname_isequal(mx->name, buff)) break; if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx) { mx = opt_malloc(sizeof(struct mx_srv_record)); mx->next = daemon->mxnames; mx->issrv = 0; mx->target = NULL; mx->name = opt_string_alloc(buff); daemon->mxnames = mx; } if (!daemon->mxtarget) daemon->mxtarget = opt_string_alloc(buff); for (mx = daemon->mxnames; mx; mx = mx->next) if (!mx->issrv && !mx->target) mx->target = daemon->mxtarget; } if (!option_bool(OPT_NO_RESOLV) && daemon->resolv_files && daemon->resolv_files->next && option_bool(OPT_NO_POLL)) die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF); if (option_bool(OPT_RESOLV_DOMAIN)) { char *line; FILE *f; if (option_bool(OPT_NO_RESOLV) || !daemon->resolv_files || (daemon->resolv_files)->next) die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF); if (!(f = fopen((daemon->resolv_files)->name, "r"))) die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE); while ((line = fgets(buff, MAXDNAME, f))) { char *token = strtok(line, " \t\n\r"); if (!token || strcmp(token, "search") != 0) continue; if ((token = strtok(NULL, " \t\n\r")) && (daemon->domain_suffix = canonicalise_opt(token))) break; } fclose(f); if (!daemon->domain_suffix) die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC); } if (daemon->domain_suffix) { /* add domain for any srv record without one. */ struct mx_srv_record *srv; for (srv = daemon->mxnames; srv; srv = srv->next) if (srv->issrv && strchr(srv->name, '.') && strchr(srv->name, '.') == strrchr(srv->name, '.')) { strcpy(buff, srv->name); strcat(buff, "."); strcat(buff, daemon->domain_suffix); free(srv->name); srv->name = opt_string_alloc(buff); } } 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); reset_option_bool(OPT_LOCALHOST_SERVICE); } else if (option_bool(OPT_LOCALHOST_SERVICE) && !option_bool(OPT_LOCAL_SERVICE)) { /* listen only on localhost, emulate --interface=lo --bind-interfaces */ if_names_add(NULL); set_option_bool(OPT_NOWILD); } if (testmode) { fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK")); exit(0); } } dnsmasq-2.91/src/blockdata.c0000664000175000017500000001247414765043257014201 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" static struct blockdata *keyblock_free; static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced; static void add_blocks(int n) { struct blockdata *new = whine_malloc(n * sizeof(struct blockdata)); if (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)) add_blocks(daemon->cachesize); } void blockdata_report(void) { my_syslog(LOG_INFO, _("pool memory in use %zu, max %zu, allocated %zu"), blockdata_count * sizeof(struct blockdata), blockdata_hwm * sizeof(struct blockdata), blockdata_alloced * sizeof(struct blockdata)); } static struct blockdata *new_block(void) { struct blockdata *block; if (!keyblock_free) add_blocks(50); if (keyblock_free) { block = keyblock_free; keyblock_free = block->next; blockdata_count++; if (blockdata_hwm < blockdata_count) blockdata_hwm = blockdata_count; block->next = NULL; return block; } return NULL; } static struct blockdata *blockdata_alloc_real(int fd, char *data, size_t len) { struct blockdata *block, *ret = NULL; struct blockdata **prev = &ret; size_t blen; do { if (!(block = new_block())) { /* failed to alloc, free partial chain */ blockdata_free(ret); return NULL; } if ((blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len) > 0) { if (data) { memcpy(block->key, data, blen); data += blen; } else if (!read_write(fd, block->key, blen, RW_READ)) { /* failed read free partial chain */ blockdata_free(ret); return NULL; } } len -= blen; *prev = block; prev = &block->next; } while (len != 0); return ret; } struct blockdata *blockdata_alloc(char *data, size_t len) { return blockdata_alloc_real(0, data, len); } /* Add data to the end of the block. newlen is length of new data, NOT total new length. Use blockdata_alloc(NULL, 0) to make empty block to add to. */ int blockdata_expand(struct blockdata *block, size_t oldlen, char *data, size_t newlen) { struct blockdata *b; /* find size of current final block */ for (b = block; oldlen > KEYBLOCK_LEN && b; b = b->next, oldlen -= KEYBLOCK_LEN); /* chain to short for length, something is broken */ if (oldlen > KEYBLOCK_LEN) { blockdata_free(block); return 0; } while (1) { struct blockdata *new; size_t blocksize = KEYBLOCK_LEN - oldlen; size_t size = (newlen <= blocksize) ? newlen : blocksize; if (size != 0) { memcpy(&b->key[oldlen], data, size); data += size; newlen -= size; } /* full blocks from now on. */ oldlen = 0; if (newlen == 0) break; if ((new = new_block())) { b->next = new; b = new; } else { /* failed to alloc, free partial chain */ blockdata_free(block); return 0; } } return 1; } 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; uint8_t *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; } void blockdata_write(struct blockdata *block, size_t len, int fd) { for (; len > 0 && block; block = block->next) { size_t blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len; read_write(fd, block->key, blen, RW_WRITE); len -= blen; } } struct blockdata *blockdata_read(int fd, size_t len) { return blockdata_alloc_real(fd, NULL, len); } dnsmasq-2.91/src/dhcp6-protocol.h0000664000175000017500000000476614765043257015132 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #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_IA_PD 25 #define OPTION6_IAPREFIX 26 #define OPTION6_REFRESH_TIME 32 #define OPTION6_REMOTE_ID 37 #define OPTION6_SUBSCRIBER_ID 38 #define OPTION6_FQDN 39 #define OPTION6_NTP_SERVER 56 #define OPTION6_CLIENT_MAC 79 #define OPTION6_MUD_URL 112 #define NTP_SUBOPTION_SRV_ADDR 1 #define NTP_SUBOPTION_MC_ADDR 2 #define NTP_SUBOPTION_SRV_FQDN 3 #define DHCP6SUCCESS 0 #define DHCP6UNSPEC 1 #define DHCP6NOADDRS 2 #define DHCP6NOBINDING 3 #define DHCP6NOTONLINK 4 #define DHCP6USEMULTI 5 dnsmasq-2.91/src/dnsmasq.c0000664000175000017500000020441714765043257013723 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ /* Declare static char *compiler_opts in config.h */ #define DNSMASQ_COMPILE_OPTS /* dnsmasq.h has to be included first as it sources config.h */ #include "dnsmasq.h" #if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) || defined(LOCALEDIR) #include #endif struct daemon *daemon; static volatile pid_t pid = 0; static volatile int pipewrite; static void set_dns_listeners(void); static void set_tftp_listeners(void); static void check_dns_listeners(time_t now); static void do_tcp_connection(struct listener *listener, time_t now, int slot); static void sig_handler(int sig); static void async_event(int pipe, time_t now); static void fatal_event(struct event_desc *ev, char *msg); static int read_event(int fd, struct event_desc *evp, char **msg); static void poll_resolv(int force, int do_reload, time_t now); static void tcp_init(void); int main (int argc, char **argv) { time_t now; struct sigaction sigact; struct iname *if_tmp; int piperead, pipefd[2], err_pipe[2]; struct passwd *ent_pw = NULL; #if defined(HAVE_SCRIPT) uid_t script_uid = 0; gid_t script_gid = 0; #endif struct group *gp = NULL; long i, max_fd = sysconf(_SC_OPEN_MAX); char *baduser = NULL; int log_err; int chown_warn = 0; #if defined(HAVE_LINUX_NETWORK) cap_user_header_t hdr = NULL; cap_user_data_t data = NULL; int need_cap_net_admin = 0; int need_cap_net_raw = 0; int need_cap_net_bind_service = 0; int have_cap_chown = 0; char *bound_device = NULL; int did_bind = 0; struct server *serv; char *netlink_warn; #else int bind_fallback = 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 #if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) || defined(LOCALEDIR) setlocale(LC_ALL, ""); #endif #ifdef LOCALEDIR bindtextdomain("dnsmasq", LOCALEDIR); textdomain("dnsmasq"); #endif sigact.sa_handler = sig_handler; sigact.sa_flags = 0; sigemptyset(&sigact.sa_mask); sigaction(SIGUSR1, &sigact, NULL); sigaction(SIGUSR2, &sigact, NULL); sigaction(SIGHUP, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGALRM, &sigact, NULL); sigaction(SIGCHLD, &sigact, NULL); sigaction(SIGINT, &sigact, NULL); /* ignore SIGPIPE */ sigact.sa_handler = SIG_IGN; 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); #ifdef HAVE_LINUX_NETWORK daemon->kernel_version = kernel_version(); #endif if (daemon->edns_pktsz < PACKETSZ) daemon->edns_pktsz = PACKETSZ; /* Min buffer size: we check after adding each record, so there must be memory for the largest packet, and the largest record so the min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000. This might be increased is EDNS packet size if greater than the minimum. */ daemon->packet_buff_sz = daemon->edns_pktsz + MAXDNAME + RRFIXEDSZ; daemon->packet = safe_malloc(daemon->packet_buff_sz); 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. 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->keyname = safe_malloc((MAXDNAME * 2) + 1); /* one char flag per possible RR in answer section (may get extended). */ daemon->rr_status_sz = 64; daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz); } #endif #ifdef HAVE_DHCP if (!daemon->lease_file) { if (daemon->dhcp || daemon->dhcp6) daemon->lease_file = LEASEFILE; } #endif /* 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 < 3; i++) open("/dev/null", O_RDWR); /* Close any file descriptors we inherited apart from std{in|out|err} */ close_fds(max_fd, -1, -1, -1); #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 struct ds_config *ds; /* Must have at least a root trust anchor, or the DNSSEC code can loop forever. */ for (ds = daemon->ds; ds; ds = ds->next) if (ds->name[0] == 0) break; if (!ds) die(_("no root trust anchor 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 (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)) { if (daemon->query_port != 0 || daemon->osport) die (_("cannot use --conntrack AND --query-port"), NULL, EC_BADCONF); need_cap_net_admin = 1; } #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(_("asynchronous logging is not available under Solaris"), NULL, EC_BADCONF); #endif #ifdef __ANDROID__ if (daemon->max_logs != 0) die(_("asynchronous logging is not available under Android"), NULL, EC_BADCONF); #endif #ifndef HAVE_AUTH if (daemon->auth_zones) 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 #ifndef HAVE_UBUS if (option_bool(OPT_UBUS)) die(_("Ubus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF); #endif /* Handle only one of min_port/max_port being set. */ if (daemon->min_port != 0 && daemon->max_port == 0) daemon->max_port = MAX_PORT; if (daemon->max_port != 0 && daemon->min_port == 0) daemon->min_port = MIN_PORT; if (daemon->max_port < daemon->min_port) die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF); if (daemon->max_port != 0 && daemon->max_port - daemon->min_port + 1 < daemon->randport_limit) die(_("port_limit must not be larger than available port range"), NULL, EC_BADCONF); now = dnsmasq_time(); if (daemon->auth_zones) { if (!daemon->authserver) die(_("--auth-server required when an auth zone is defined."), NULL, EC_BADCONF); /* Create a serial at startup if not configured. */ #ifdef HAVE_BROKEN_RTC if (daemon->soa_sn == 0) die(_("zone serial must be configured in --auth-soa"), NULL, EC_BADCONF); #else if (daemon->soa_sn == 0) 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 /* 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. The script subsystem relies on DHCP buffers, hence the last two conditions below. */ if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || daemon->relay6 || option_bool(OPT_TFTP) || option_bool(OPT_SCRIPT_ARP)) { dhcp_common_init(); if (daemon->dhcp || daemon->doing_dhcp6) lease_init(now); } if (daemon->dhcp || daemon->relay4) { dhcp_init(); # ifdef HAVE_LINUX_NETWORK /* Need NET_RAW to send ping. */ if (!option_bool(OPT_NO_PING)) need_cap_net_raw = 1; /* Need NET_ADMIN to change ARP cache if not always broadcasting. */ if (daemon->force_broadcast == NULL || daemon->force_broadcast->list != NULL) need_cap_net_admin = 1; # endif } # ifdef HAVE_DHCP6 if (daemon->doing_ra || daemon->doing_dhcp6 || daemon->relay6) { ra_init(now); # ifdef HAVE_LINUX_NETWORK need_cap_net_raw = 1; need_cap_net_admin = 1; # endif } if (daemon->doing_dhcp6 || daemon->relay6) dhcp6_init(); # endif #endif #ifdef HAVE_IPSET if (daemon->ipsets) { ipset_init(); # ifdef HAVE_LINUX_NETWORK need_cap_net_admin = 1; # endif } #endif #ifdef HAVE_NFTSET if (daemon->nftsets) { nftset_init(); # ifdef HAVE_LINUX_NETWORK need_cap_net_admin = 1; # endif } #endif #if defined(HAVE_LINUX_NETWORK) netlink_warn = 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); #ifdef HAVE_DHCP /* Determine lease FQDNs after enumerate_interfaces() call, since it needs to call get_domain and that's only valid for some domain configs once we have interface addresses. */ lease_calc_fqdns(); #endif if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND)) { 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->flags & INAME_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 ((did_bind = bind_dhcp_devices(bound_device)) & 2) die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET); #endif } else 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(); blockdata_init(); /* Scale random socket pool by ftabsize, but limit it based on available fds. */ daemon->numrrand = daemon->ftabsize/2; if (daemon->numrrand > max_fd/3) daemon->numrrand = max_fd/3; /* safe_malloc returns zero'd memory */ daemon->randomsocks = safe_malloc(daemon->numrrand * sizeof(struct randfd)); tcp_init(); } #ifdef HAVE_INOTIFY if ((daemon->port != 0 && !option_bool(OPT_NO_RESOLV)) || daemon->dynamic_dirs) inotify_dnsmasq_init(); else daemon->inotifyfd = -1; #endif if (daemon->dump_file) #ifdef HAVE_DUMPFILE dump_init(); else daemon->dumpfd = -1; #else die(_("Packet dumps not available: set HAVE_DUMP in src/config.h"), NULL, EC_BADCONF); #endif if (option_bool(OPT_DBUS)) #ifdef HAVE_DBUS { char *err; if ((err = dbus_init())) die(_("DBus error: %s"), err, EC_MISC); } #else die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL, EC_BADCONF); #endif if (option_bool(OPT_UBUS)) #ifdef HAVE_UBUS { char *err; if ((err = ubus_init())) die(_("UBus error: %s"), err, EC_MISC); } #else die(_("UBus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF); #endif if (daemon->port != 0) pre_allocate_sfds(); #if defined(HAVE_SCRIPT) /* Note getpwnam returns static storage */ if ((daemon->dhcp || daemon->dhcp6) && daemon->scriptuser && (daemon->lease_change_command || daemon->luascript)) { struct passwd *scr_pw; if ((scr_pw = getpwnam(daemon->scriptuser))) { script_uid = scr_pw->pw_uid; script_gid = scr_pw->pw_gid; } else baduser = daemon->scriptuser; } #endif if (daemon->username && !(ent_pw = getpwnam(daemon->username))) baduser = daemon->username; else if (daemon->groupname && !(gp = getgrnam(daemon->groupname))) baduser = daemon->groupname; 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) { if (!(gp = getgrnam(CHGRP)) && ent_pw) gp = getgrgid(ent_pw->pw_gid); /* for error message */ if (gp) daemon->groupname = gp->gr_name; } #if defined(HAVE_LINUX_NETWORK) /* We keep CAP_NETADMIN (for ARP-injection) and 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. */ if ((is_dad_listeners() || option_bool(OPT_CLEVERBIND)) && (option_bool(OPT_TFTP) || (daemon->port != 0 && daemon->port <= 1024))) need_cap_net_bind_service = 1; /* usptream servers which bind to an interface call SO_BINDTODEVICE for each TCP connection, so need CAP_NET_RAW */ for (serv = daemon->servers; serv; serv = serv->next) if (serv->interface[0] != 0) need_cap_net_raw = 1; /* If we're doing Dbus or UBus, the above can be set dynamically, (as can ports) so always (potentially) needed. */ #ifdef HAVE_DBUS if (option_bool(OPT_DBUS)) { need_cap_net_bind_service = 1; need_cap_net_raw = 1; } #endif #ifdef HAVE_UBUS if (option_bool(OPT_UBUS)) { need_cap_net_bind_service = 1; need_cap_net_raw = 1; } #endif /* determine capability API version here, while we can still call safe_malloc */ int capsize = 1; /* for header version 1 */ char *fail = NULL; hdr = safe_malloc(sizeof(*hdr)); /* find version supported by kernel */ memset(hdr, 0, sizeof(*hdr)); capget(hdr, NULL); if (hdr->version != LINUX_CAPABILITY_VERSION_1) { /* if unknown version, use largest supported version (3) */ if (hdr->version != LINUX_CAPABILITY_VERSION_2) hdr->version = LINUX_CAPABILITY_VERSION_3; capsize = 2; } data = safe_malloc(sizeof(*data) * capsize); capget(hdr, data); /* Get current values, for verification */ have_cap_chown = data->permitted & (1 << CAP_CHOWN); if (need_cap_net_admin && !(data->permitted & (1 << CAP_NET_ADMIN))) fail = "NET_ADMIN"; else if (need_cap_net_raw && !(data->permitted & (1 << CAP_NET_RAW))) fail = "NET_RAW"; else if (need_cap_net_bind_service && !(data->permitted & (1 << CAP_NET_BIND_SERVICE))) fail = "NET_BIND_SERVICE"; if (fail) die(_("process is missing required capability %s"), fail, EC_MISC); /* Now set bitmaps to set caps after daemonising */ memset(data, 0, sizeof(*data) * capsize); if (need_cap_net_admin) data->effective |= (1 << CAP_NET_ADMIN); if (need_cap_net_raw) data->effective |= (1 << CAP_NET_RAW); if (need_cap_net_bind_service) data->effective |= (1 << CAP_NET_BIND_SERVICE); data->permitted = data->effective; #endif /* Use a pipe to carry signals and other events back to the event loop in a race-free manner and another to carry errors to daemon-invoking process */ safe_pipe(pipefd, 1); piperead = pipefd[0]; pipewrite = pipefd[1]; /* prime the pipe to load stuff first time. */ send_event(pipewrite, EVENT_INIT, 0, NULL); err_pipe[1] = -1; if (!option_bool(OPT_DEBUG)) { /* The following code "daemonizes" the process. See Stevens section 12.4 */ if (chdir("/") != 0) die(_("cannot chdir to filesystem root: %s"), NULL, EC_MISC); if (!option_bool(OPT_NO_FORK)) { pid_t pid; /* pipe to carry errors back to original process. When startup is complete we close this and the process terminates. */ safe_pipe(err_pipe, 0); if ((pid = fork()) == -1) /* fd == -1 since we've not forked, never returns. */ 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]); /* check for errors after the fork */ if (read_event(err_pipe[0], &ev, &msg)) fatal_event(&ev, msg); _exit(EC_GOOD); } 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, NULL); if (pid != 0) _exit(0); } /* write pidfile _after_ forking ! */ if (daemon->runfile) { 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 following 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); 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 { /* We're still running as root here. Change the ownership of the PID file to the user we will be running as. Note that this is not to allow us to delete the file, since that depends on the permissions of the directory containing the file. That directory will need to by owned by the dnsmasq user, and the ownership of the file has to match, to keep systemd >273 happy. */ if (getuid() == 0 && ent_pw && ent_pw->pw_uid != 0 && fchown(fd, ent_pw->pw_uid, ent_pw->pw_gid) == -1) chown_warn = errno; if (!read_write(fd, (unsigned char *)daemon->namebuff, strlen(daemon->namebuff), RW_WRITE)) err = 1; else { if (close(fd) == -1) err = 1; } } if (err) { send_event(err_pipe[1], EVENT_PIDFILE, errno, daemon->runfile); _exit(0); } } } log_err = log_start(ent_pw, err_pipe[1]); if (!option_bool(OPT_DEBUG)) { /* open stdout etc to /dev/null */ int nullfd = open("/dev/null", O_RDWR); if (nullfd != -1) { dup2(nullfd, STDOUT_FILENO); dup2(nullfd, STDERR_FILENO); dup2(nullfd, STDIN_FILENO); close(nullfd); } } /* if we are to run scripts, we need to fork a helper before dropping root. */ daemon->helperfd = -1; #ifdef HAVE_SCRIPT if ((daemon->dhcp || daemon->dhcp6 || daemon->relay6 || option_bool(OPT_TFTP) || option_bool(OPT_SCRIPT_ARP)) && (daemon->lease_change_command || daemon->luascript)) daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd); #endif if (!option_bool(OPT_DEBUG) && getuid() == 0) { int bad_capabilities = 0; gid_t dummy; /* remove all supplementary groups */ if (gp && (setgroups(0, &dummy) == -1 || setgid(gp->gr_gid) == -1)) { 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) /* Need to be able to drop root. */ data->effective |= (1 << CAP_SETUID); data->permitted |= (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) bad_capabilities = errno; #elif defined(HAVE_SOLARIS_NETWORK) /* http://developers.sun.com/solaris/articles/program_privileges.html */ priv_set_t *priv_set; if (!(priv_set = priv_str_to_set("basic", ",", NULL)) || priv_addset(priv_set, PRIV_NET_ICMPACCESS) == -1 || priv_addset(priv_set, PRIV_SYS_NET_CONFIG) == -1) bad_capabilities = errno; if (priv_set && bad_capabilities == 0) { priv_inverse(priv_set); if (setppriv(PRIV_OFF, PRIV_LIMIT, priv_set) == -1) bad_capabilities = errno; } if (priv_set) priv_freeset(priv_set); #endif if (bad_capabilities != 0) { 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, daemon->username); _exit(0); } #ifdef HAVE_LINUX_NETWORK data->effective &= ~(1 << CAP_SETUID); data->permitted &= ~(1 << CAP_SETUID); /* lose the setuid capability */ if (capset(hdr, data) == -1) { send_event(err_pipe[1], EVENT_CAP_ERR, errno, NULL); _exit(0); } #endif } } #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) { my_syslog(LOG_INFO, _("started, version %s cachesize %d"), VERSION, daemon->cachesize); if (daemon->cachesize > 10000) my_syslog(LOG_WARNING, _("cache size greater than 10000 may cause performance issues, and is unlikely to be useful.")); } else my_syslog(LOG_INFO, _("started, version %s cache disabled"), VERSION); if (option_bool(OPT_LOCAL_SERVICE)) my_syslog(LOG_INFO, _("DNS service limited to local subnets")); else if (option_bool(OPT_LOCALHOST_SERVICE)) my_syslog(LOG_INFO, _("DNS service limited to localhost")); } my_syslog(LOG_INFO, _("compile time options: %s"), compile_opts); if (chown_warn != 0) { #if defined(HAVE_LINUX_NETWORK) if (chown_warn == EPERM && !have_cap_chown) my_syslog(LOG_INFO, "chown of PID file %s failed: please add capability CAP_CHOWN", daemon->runfile); else #endif my_syslog(LOG_WARNING, "chown of PID file %s failed: %s", daemon->runfile, strerror(chown_warn)); } #ifdef HAVE_DBUS if (option_bool(OPT_DBUS)) { if (daemon->dbus) my_syslog(LOG_INFO, _("DBus support enabled: connected to system bus")); else my_syslog(LOG_INFO, _("DBus support enabled: bus connection pending")); } #endif #ifdef HAVE_UBUS if (option_bool(OPT_UBUS)) { if (daemon->ubus) my_syslog(LOG_INFO, _("UBus support enabled: connected to system bus")); else my_syslog(LOG_INFO, _("UBus support enabled: bus connection pending")); } #endif #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID)) { int rc; struct ds_config *ds; /* 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); } if (option_bool(OPT_DNSSEC_IGN_NS)) my_syslog(LOG_INFO, _("DNSSEC validation enabled but all unsigned answers are trusted")); else my_syslog(LOG_INFO, _("DNSSEC validation enabled")); daemon->dnssec_no_time_check = option_bool(OPT_DNSSEC_TIME); if (option_bool(OPT_DNSSEC_TIME) && !daemon->back_to_the_future) my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until receipt of SIGINT")); if (rc == 1) my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until system time valid")); for (ds = daemon->ds; ds; ds = ds->next) my_syslog(LOG_INFO, _("configured with trust anchor for %s keytag %u"), ds->name[0] == 0 ? "" : ds->name, ds->keytag); } #endif if (log_err != 0) my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"), daemon->log_file, strerror(log_err)); #ifndef HAVE_LINUX_NETWORK if (bind_fallback) my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations")); #endif if (option_bool(OPT_NOWILD)) warn_bound_listeners(); else if (!option_bool(OPT_CLEVERBIND)) warn_wild_labels(); warn_int_names(); if (!option_bool(OPT_NOWILD)) for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) if (if_tmp->name && !(if_tmp->flags & INAME_USED)) my_syslog(LOG_WARNING, _("warning: interface %s does not currently exist"), if_tmp->name); if (daemon->port != 0 && option_bool(OPT_NO_RESOLV)) { if (daemon->resolv_files && !daemon->resolv_files->is_default) my_syslog(LOG_WARNING, _("warning: ignoring resolv-file flag because no-resolv is set")); daemon->resolv_files = NULL; if (!daemon->servers) { #ifdef HAVE_DBUS if (option_bool(OPT_DBUS)) my_syslog(LOG_INFO, _("no upstream servers configured - please set them from DBus")); else #endif my_syslog(LOG_WARNING, _("warning: no upstream servers configured")); } } if (daemon->max_logs != 0) my_syslog(LOG_INFO, _("asynchronous logging enabled, queue limit is %d messages"), daemon->max_logs); #ifdef HAVE_DHCP 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); if (netlink_warn) my_syslog(LOG_WARNING, netlink_warn); # endif /* after dhcp_construct_contexts */ if (daemon->dhcp || daemon->doing_dhcp6) lease_find_interfaces(now); #endif #ifdef HAVE_TFTP if (option_bool(OPT_TFTP)) { struct tftp_prefix *p; my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s %s", daemon->tftp_prefix ? _("root is ") : _("enabled"), daemon->tftp_prefix ? daemon->tftp_prefix : "", option_bool(OPT_TFTP_SECURE) ? _("secure mode") : "", option_bool(OPT_SINGLE_PORT) ? _("single port 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 one fd). */ max_fd -= 30 + daemon->numrrand; /* use other than TFTP */ if (max_fd < 0) max_fd = 5; else if (max_fd < 100 && !option_bool(OPT_SINGLE_PORT)) max_fd = max_fd/2; else max_fd = max_fd - 20; /* if we have to use a limited range of ports, that will limit the number of transfers */ if (daemon->start_tftp_port != 0 && daemon->end_tftp_port - daemon->start_tftp_port + 1 < max_fd) max_fd = daemon->end_tftp_port - daemon->start_tftp_port + 1; if (daemon->tftp_max > max_fd) { daemon->tftp_max = max_fd; my_syslog(MS_TFTP | LOG_WARNING, _("restricting maximum simultaneous TFTP transfers to %d"), daemon->tftp_max); } } #endif /* finished start-up - release original process */ if (err_pipe[1] != -1) close(err_pipe[1]); if (daemon->port != 0) check_servers(0); pid = getpid(); daemon->pipe_to_parent = -1; if (daemon->port != 0) for (i = 0; i < daemon->max_procs; i++) daemon->tcp_pipes[i] = -1; #ifdef HAVE_INOTIFY /* Using inotify, have to select a resolv file at startup */ poll_resolv(1, 0, now); #endif while (1) { int timeout = fast_retry(now); poll_reset(); /* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */ if ((daemon->tftp_trans || (option_bool(OPT_DBUS) && !daemon->dbus)) && (timeout == -1 || timeout > 250)) timeout = 250; /* Wake every second whilst waiting for DAD to complete */ else if (is_dad_listeners() && (timeout == -1 || timeout > 1000)) timeout = 1000; if (daemon->port != 0) set_dns_listeners(); #ifdef HAVE_TFTP set_tftp_listeners(); #endif #ifdef HAVE_DBUS if (option_bool(OPT_DBUS)) set_dbus_listeners(); #endif #ifdef HAVE_UBUS if (option_bool(OPT_UBUS)) set_ubus_listeners(); #endif #ifdef HAVE_DHCP # if defined(HAVE_LINUX_NETWORK) if (bind_dhcp_devices(bound_device) & 2) { static int warned = 0; if (!warned) { my_syslog(LOG_ERR, _("error binding DHCP socket to device %s"), bound_device); warned = 1; } } # endif if (daemon->dhcp || daemon->relay4) { poll_listen(daemon->dhcpfd, POLLIN); if (daemon->pxefd != -1) poll_listen(daemon->pxefd, POLLIN); } #endif #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 poll_listen(piperead, POLLIN); #ifdef HAVE_SCRIPT # ifdef HAVE_DHCP while (helper_buf_empty() && do_script_run(now)); # endif /* Refresh cache */ if (option_bool(OPT_SCRIPT_ARP)) find_mac(NULL, NULL, 0, now); while (helper_buf_empty() && do_arp_script_run()); # ifdef HAVE_TFTP while (helper_buf_empty() && do_tftp_script_run()); # endif # ifdef HAVE_DHCP6 while (helper_buf_empty() && do_snoop_script_run()); # endif if (!helper_buf_empty()) poll_listen(daemon->helperfd, POLLOUT); #else /* need this for other side-effects */ # ifdef HAVE_DHCP while (do_script_run(now)); # endif while (do_arp_script_run()); # ifdef HAVE_TFTP while (do_tftp_script_run()); # endif #endif /* must do this just before do_poll(), when we know no more calls to my_syslog() can occur */ set_log_writer(); if (do_poll(timeout) < 0) continue; now = dnsmasq_time(); check_log_writer(0); /* 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 || difftime(now, daemon->last_resolv) > 1.0 || difftime(now, daemon->last_resolv) < -1.0) { /* poll_resolv doesn't need to reload first time through, since that's queued anyway. */ poll_resolv(0, daemon->last_resolv != 0, now); daemon->last_resolv = now; } #endif if (poll_check(piperead, POLLIN)) async_event(piperead, now); #ifdef HAVE_DBUS /* if we didn't create a DBus connection, retry now. */ if (option_bool(OPT_DBUS)) { if (!daemon->dbus) { char *err = dbus_init(); if (daemon->dbus) my_syslog(LOG_INFO, _("connected to system DBus")); else if (err) { my_syslog(LOG_ERR, _("DBus error: %s"), err); reset_option_bool(OPT_DBUS); /* fatal error, stop trying. */ } } check_dbus_listeners(); } #endif #ifdef HAVE_UBUS /* if we didn't create a UBus connection, retry now. */ if (option_bool(OPT_UBUS)) { if (!daemon->ubus) { char *err = ubus_init(); if (daemon->ubus) my_syslog(LOG_INFO, _("connected to system UBus")); else if (err) { my_syslog(LOG_ERR, _("UBus error: %s"), err); reset_option_bool(OPT_UBUS); /* fatal error, stop trying. */ } } check_ubus_listeners(); } #endif if (daemon->port != 0) check_dns_listeners(now); #ifdef HAVE_TFTP check_tftp_listeners(now); #endif #ifdef HAVE_DHCP if (daemon->dhcp || daemon->relay4) { if (poll_check(daemon->dhcpfd, POLLIN)) dhcp_packet(now, 0); 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 && poll_check(daemon->helperfd, POLLOUT)) helper_write(); # endif #endif } } static void sig_handler(int sig) { if (pid == 0) { /* ignore anything other than TERM during startup and in helper proc. (helper ignore TERM too) */ if (sig == SIGTERM || sig == SIGINT) exit(EC_MISC); } else if (pid != getpid()) { /* alarm is used to kill TCP children after a fixed time. */ if (sig == SIGALRM) _exit(0); } else { /* master process */ int event, errsave = errno; if (sig == SIGHUP) event = EVENT_RELOAD; else if (sig == SIGCHLD) event = EVENT_CHILD; else if (sig == SIGALRM) event = EVENT_ALARM; else if (sig == SIGTERM) event = EVENT_TERM; else if (sig == SIGUSR1) event = EVENT_DUMP; else if (sig == SIGUSR2) event = EVENT_REOPEN; else if (sig == SIGINT) { /* Handle SIGINT normally in debug mode, so ctrl-c continues to operate. */ if (option_bool(OPT_DEBUG)) exit(EC_MISC); else event = EVENT_TIME; } else return; send_event(pipewrite, event, 0, NULL); errno = errsave; } } /* 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, msg); else /* pipe is non-blocking and struct event_desc is smaller than PIPE_BUF, so this either fails or writes everything */ while (writev(fd, iov, msg ? 2 : 1) == -1 && errno == EINTR); } /* 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), RW_READ)) return 0; *msg = NULL; if (evp->msg_sz != 0 && (buf = malloc(evp->msg_sz + 1)) && read_write(fd, (unsigned char *)buf, evp->msg_sz, RW_READ)) { buf[evp->msg_sz] = 0; *msg = buf; } return 1; } static void fatal_event(struct event_desc *ev, char *msg) { errno = ev->data; switch (ev->event) { case EVENT_DIE: exit(0); case EVENT_FORK_ERR: die(_("cannot fork into background: %s"), NULL, EC_MISC); /* fall through */ case EVENT_PIPE_ERR: die(_("failed to create helper: %s"), NULL, EC_MISC); /* fall through */ case EVENT_CAP_ERR: die(_("setting capabilities failed: %s"), NULL, EC_MISC); /* fall through */ case EVENT_USER_ERR: die(_("failed to change user-id to %s: %s"), msg, EC_MISC); /* fall through */ case EVENT_GROUP_ERR: die(_("failed to change group-id to %s: %s"), msg, EC_MISC); /* fall through */ case EVENT_PIDFILE: die(_("failed to open pidfile %s: %s"), msg, EC_FILE); /* fall through */ case EVENT_LOG_ERR: die(_("cannot open log %s: %s"), msg, EC_FILE); /* fall through */ case EVENT_LUA_ERR: die(_("failed to load Lua script: %s"), msg, EC_MISC); /* fall through */ case EVENT_TFTP_ERR: die(_("TFTP directory %s inaccessible: %s"), msg, EC_FILE); /* fall through */ case EVENT_TIME_ERR: die(_("cannot create timestamp file %s: %s" ), msg, EC_BADCONF); } } static void async_event(int pipe, time_t now) { pid_t p; struct event_desc ev; int wstatus, 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: daemon->soa_sn++; /* Bump zone serial, as it may have changed. */ /* fall through */ case EVENT_INIT: clear_cache_and_reload(now); if (daemon->port != 0) { 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(0); } #ifdef HAVE_DHCP rerun_scripts(); #endif break; case EVENT_DUMP: if (daemon->port != 0) dump_cache(now); break; case EVENT_ALARM: #ifdef HAVE_DHCP if (daemon->dhcp || daemon->doing_dhcp6) { lease_prune(NULL, now); lease_update_file(now); lease_update_dns(0); } #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; case EVENT_CHILD: /* See Stevens 5.10 */ while ((p = waitpid(-1, &wstatus, WNOHANG)) != 0) if (p == -1) { if (errno != EINTR) break; } else if (daemon->port != 0) for (i = 0 ; i < daemon->max_procs; i++) if (daemon->tcp_pids[i] == p) { daemon->tcp_pids[i] = 0; if (!WIFEXITED(wstatus)) { /* If a helper process dies, (eg with SIGSEV) log that and attempt to patch things up so that the parent can continue to function. */ my_syslog(LOG_WARNING, _("TCP helper process %u died unexpectedly"), (unsigned int)p); if (daemon->tcp_pipes[i] != -1) { close(daemon->tcp_pipes[i]); daemon->tcp_pipes[i] = -1; } } /* tcp_pipes == -1 && tcp_pids == 0 required to free slot */ if (daemon->tcp_pipes[i] == -1) daemon->metrics[METRIC_TCP_CONNECTIONS]--; } break; #if defined(HAVE_SCRIPT) case EVENT_KILLED: my_syslog(LOG_WARNING, _("script process killed by signal %d"), ev.data); break; case EVENT_EXITED: my_syslog(LOG_WARNING, _("script process exited with status %d"), ev.data); break; case EVENT_EXEC_ERR: my_syslog(LOG_ERR, _("failed to execute %s: %s"), daemon->lease_change_command, strerror(ev.data)); break; case EVENT_SCRIPT_LOG: my_syslog(MS_SCRIPT | LOG_DEBUG, "%s", msg ? msg : ""); free(msg); msg = NULL; break; /* necessary for fatal errors in helper */ case EVENT_USER_ERR: case EVENT_DIE: case EVENT_LUA_ERR: fatal_event(&ev, msg); break; #endif case EVENT_REOPEN: /* Note: this may leave TCP-handling processes with the old file still open. Since any such process will die in CHILD_LIFETIME or probably much sooner, we leave them logging to the old file. */ 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_TIME: #ifdef HAVE_DNSSEC if (daemon->dnssec_no_time_check && option_bool(OPT_DNSSEC_VALID) && option_bool(OPT_DNSSEC_TIME)) { my_syslog(LOG_INFO, _("now checking DNSSEC signature timestamps")); daemon->dnssec_no_time_check = 0; clear_cache_and_reload(now); } #endif break; case EVENT_TERM: /* Knock all our children on the head. */ if (daemon->port != 0) for (i = 0; i < daemon->max_procs; i++) if (daemon->tcp_pids[i] != 0) kill(daemon->tcp_pids[i], SIGALRM); #if defined(HAVE_SCRIPT) && defined(HAVE_DHCP) /* handle pending lease transitions */ if (daemon->helperfd != -1) { /* block in writes until all done */ if ((i = fcntl(daemon->helperfd, F_GETFL)) != -1) while(retry_send(fcntl(daemon->helperfd, F_SETFL, i & ~O_NONBLOCK))); do { helper_write(); } while (!helper_buf_empty() || do_script_run(now)); 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 (utimes(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); #ifdef HAVE_DUMPFILE if (daemon->dumpfd != -1) close(daemon->dumpfd); #endif my_syslog(LOG_INFO, _("exiting on receipt of SIGTERM")); flush_log(); exit(EC_GOOD); } } static void poll_resolv(int force, int do_reload, time_t now) { struct resolvc *res, *latest; struct stat statbuf; time_t last_change = 0; /* There may be more than one possible file. Go through and find the one which changed _last_. Warn of any which can't be read. */ if (daemon->port == 0 || option_bool(OPT_NO_POLL)) return; for (latest = NULL, res = daemon->resolv_files; res; res = res->next) if (stat(res->name, &statbuf) == -1) { if (force) { res->mtime = 0; continue; } if (!res->logged) my_syslog(LOG_WARNING, _("failed to access %s: %s"), res->name, strerror(errno)); res->logged = 1; if (res->mtime != 0) { /* existing file evaporated, force selection of the latest file even if its mtime hasn't changed since we last looked */ poll_resolv(1, do_reload, now); return; } } else { res->logged = 0; if (force || (statbuf.st_mtime != res->mtime || statbuf.st_ino != res->ino)) { res->mtime = statbuf.st_mtime; res->ino = statbuf.st_ino; if (difftime(statbuf.st_mtime, last_change) > 0.0) { last_change = statbuf.st_mtime; latest = res; } } } if (latest) { static int warned = 0; if (reload_servers(latest->name)) { my_syslog(LOG_INFO, _("reading %s"), latest->name); warned = 0; check_servers(0); if (option_bool(OPT_RELOAD) && do_reload) clear_cache_and_reload(now); } else { /* If we're delaying things, we don't call check_servers(), but reload_servers() may have deleted some servers, rendering the server_array invalid, so just rebuild that here. Once reload_servers() succeeds, we call check_servers() above, which calls build_server_array itself. */ build_server_array(); latest->mtime = 0; if (!warned) { my_syslog(LOG_WARNING, _("no servers found in %s, will retry"), latest->name); warned = 1; } } } } void clear_cache_and_reload(time_t now) { (void)now; if (daemon->port != 0) cache_reload(); #ifdef HAVE_DHCP if (daemon->dhcp || daemon->doing_dhcp6) { if (option_bool(OPT_ETHERS)) dhcp_read_ethers(); reread_dhcp(); dhcp_update_configs(daemon->dhcp_conf); lease_update_from_configs(); lease_update_file(now); 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 } #ifdef HAVE_TFTP static void set_tftp_listeners(void) { int tftp = 0; struct tftp_transfer *transfer; struct listener *listener; if (!option_bool(OPT_SINGLE_PORT)) for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next) { tftp++; poll_listen(transfer->sockfd, POLLIN); } for (listener = daemon->listeners; listener; listener = listener->next) /* tftp == 0 in single-port mode. */ if (tftp <= daemon->tftp_max && listener->tftpfd != -1) poll_listen(listener->tftpfd, POLLIN); } #endif static void set_dns_listeners(void) { struct serverfd *serverfdp; struct listener *listener; struct randfd_list *rfl; int i; for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) poll_listen(serverfdp->fd, POLLIN); for (i = 0; i < daemon->numrrand; i++) if (daemon->randomsocks[i].refcount != 0) poll_listen(daemon->randomsocks[i].fd, POLLIN); /* Check overflow random sockets too. */ for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next) poll_listen(rfl->rfd->fd, POLLIN); /* check to see if we have free tcp process slots. */ for (i = daemon->max_procs - 1; i >= 0; i--) if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1) break; for (listener = daemon->listeners; listener; listener = listener->next) { if (listener->fd != -1) poll_listen(listener->fd, POLLIN); /* Only listen for TCP connections when a process slot is available. Death of a child goes through the select loop, so we don't need to explicitly arrange to wake up here, we'll be called again when a slot becomes available. */ if (listener->tcpfd != -1 && i >= 0) poll_listen(listener->tcpfd, POLLIN); } if (!option_bool(OPT_DEBUG)) for (i = 0; i < daemon->max_procs; i++) if (daemon->tcp_pipes[i] != -1) poll_listen(daemon->tcp_pipes[i], POLLIN); } static void check_dns_listeners(time_t now) { struct serverfd *serverfdp; struct listener *listener; struct randfd_list *rfl; int i; /* Note that handling events here can create or destroy fds and render the result of the last poll() call invalid. Once we find an fd that needs service, do it, then return to go around the poll() loop again. This avoid really, really, wierd bugs. */ if (!option_bool(OPT_DEBUG)) for (i = 0; i < daemon->max_procs; i++) if (daemon->tcp_pipes[i] != -1 && poll_check(daemon->tcp_pipes[i], POLLIN | POLLHUP)) { /* Races. The child process can die before we read all of the data from the pipe, or vice versa. Therefore send tcp_pids to zero when we wait() the process, and tcp_pipes to -1 and close the FD when we read the last of the data - indicated by cache_recv_insert returning zero. The order of these events is indeterminate, and both are needed to free the process slot. Once the child process has gone, poll() returns POLLHUP, not POLLIN, so have to check for both here. */ if (!cache_recv_insert(now, daemon->tcp_pipes[i])) { close(daemon->tcp_pipes[i]); daemon->tcp_pipes[i] = -1; /* tcp_pipes == -1 && tcp_pids == 0 required to free slot */ if (daemon->tcp_pids[i] == 0) daemon->metrics[METRIC_TCP_CONNECTIONS]--; } return; } for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) if (poll_check(serverfdp->fd, POLLIN)) { reply_query(serverfdp->fd, now); return; } for (i = 0; i < daemon->numrrand; i++) if (daemon->randomsocks[i].refcount != 0 && poll_check(daemon->randomsocks[i].fd, POLLIN)) { reply_query(daemon->randomsocks[i].fd, now); return; } /* Check overflow random sockets too. */ for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next) if (poll_check(rfl->rfd->fd, POLLIN)) { reply_query(rfl->rfd->fd, now); return; } for (listener = daemon->listeners; listener; listener = listener->next) if (listener->fd != -1 && poll_check(listener->fd, POLLIN)) { receive_query(listener, now); return; } /* check to see if we have a free tcp process slot. Note that we can't assume that because we had at least one a poll() time, that we still do. There may be more waiting connections after poll() returns then free process slots. */ for (i = daemon->max_procs - 1; i >= 0; i--) if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1) break; if (i >= 0) for (listener = daemon->listeners; listener; listener = listener->next) if (listener->tcpfd != -1 && poll_check(listener->tcpfd, POLLIN)) { do_tcp_connection(listener, now, i); return; } } static void do_tcp_connection(struct listener *listener, time_t now, int slot) { int confd, client_ok = 1; struct irec *iface = NULL; pid_t p; union mysockaddr tcp_addr; socklen_t tcp_len = sizeof(union mysockaddr); unsigned char a = 0, *buff; struct server *s; int flags, auth_dns; struct in_addr netmask; int pipefd[2]; while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR); if (confd == -1) return; if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1) { closeconandreturn: shutdown(confd, SHUT_RDWR); close(confd); return; } /* Make sure that the interface list is up-to-date. We do this here as we may need the results below, and the DNS code needs them for --interface-name stuff. Multiple calls to enumerate_interfaces() per select loop are inhibited, so calls to it in the child process (which doesn't select()) have no effect. This avoids two processes reading from the same netlink fd and screwing the pooch entirely. */ enumerate_interfaces(0); if (option_bool(OPT_NOWILD)) iface = listener->iface; /* May be NULL */ else { int if_index; char intr_name[IF_NAMESIZE]; /* if we can find the arrival interface, check it's one that's allowed */ if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 && indextoname(listener->tcpfd, if_index, intr_name)) { union all_addr addr; if (tcp_addr.sa.sa_family == AF_INET6) addr.addr6 = tcp_addr.in6.sin6_addr; else addr.addr4 = tcp_addr.in.sin_addr; for (iface = daemon->interfaces; iface; iface = iface->next) if (iface->index == if_index && iface->addr.sa.sa_family == tcp_addr.sa.sa_family) break; if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name)) client_ok = 0; } if (option_bool(OPT_CLEVERBIND)) iface = listener->iface; /* May be NULL */ else { /* Check for allowed interfaces when binding the wildcard address: we do this by looking for an interface with the same address as the local address of the TCP connection, then looking to see if that's an allowed interface. As a side effect, we get the netmask of the interface too, for localisation. */ for (iface = daemon->interfaces; iface; iface = iface->next) if (sockaddr_isequal(&iface->addr, &tcp_addr)) break; if (!iface) client_ok = 0; } } if (!client_ok) goto closeconandreturn; if (!option_bool(OPT_DEBUG)) { if (pipe(pipefd) == -1) goto closeconandreturn; /* pipe failed */ if ((p = fork()) == -1) { /* fork failed */ close(pipefd[0]); close(pipefd[1]); goto closeconandreturn; } if (p != 0) { /* fork() done: parent side */ close(pipefd[1]); /* parent needs read pipe end. */ #ifdef HAVE_LINUX_NETWORK /* The child process inherits the netlink socket, which it never uses, but when the parent (us) uses it in the future, the answer may go to the child, resulting in the parent blocking forever awaiting the result. To avoid this the child closes the netlink socket, but there's a nasty race, since the parent may use netlink before the child has done the close. To avoid this, the parent blocks here until a single byte comes back up the pipe, which is sent by the child after it has closed the netlink socket. */ read_write(pipefd[0], &a, 1, RW_READ); #endif daemon->tcp_pids[slot] = p; daemon->tcp_pipes[slot] = pipefd[0]; daemon->metrics[METRIC_TCP_CONNECTIONS]++; if (daemon->metrics[METRIC_TCP_CONNECTIONS] > daemon->max_procs_used) daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS]; close(confd); /* The child can use up to TCP_MAX_QUERIES ids, so skip that many. */ daemon->log_id += TCP_MAX_QUERIES; #ifdef HAVE_DNSSEC /* It can do more if making DNSSEC queries too. */ if (option_bool(OPT_DNSSEC_VALID)) daemon->log_id += daemon->limit[LIMIT_WORK]; #endif return; } } if (iface) { netmask = iface->netmask; auth_dns = iface->dns_auth; } else { netmask.s_addr = 0; auth_dns = 0; } /* Arrange for SIGALRM after CHILD_LIFETIME seconds to terminate the process. */ if (!option_bool(OPT_DEBUG)) { #ifdef HAVE_LINUX_NETWORK /* See comment above re: netlink socket. */ close(daemon->netlinkfd); read_write(pipefd[1], &a, 1, RW_WRITE); #endif alarm(CHILD_LIFETIME); close(pipefd[0]); /* close read end in child. */ daemon->pipe_to_parent = pipefd[1]; } /* The connected socket inherits non-blocking attribute from the listening socket. Reset that here. */ if ((flags = fcntl(confd, F_GETFL, 0)) != -1) while(retry_send(fcntl(confd, F_SETFL, flags & ~O_NONBLOCK))); buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns); if (buff) free(buff); for (s = daemon->servers; s; s = s->next) if (s->tcpfd != -1) { shutdown(s->tcpfd, SHUT_RDWR); close(s->tcpfd); s->tcpfd = -1; } if (!option_bool(OPT_DEBUG)) { close(daemon->pipe_to_parent); flush_log(); _exit(0); } } #ifdef HAVE_DNSSEC /* If a DNSSEC query over UDP returns a truncated answer, we swap to the TCP path. This routine is responsible for forking the required process, the child then calls tcp_key_recurse() and returns the result of the validation through the pipe to the parent (which has also primed the cache with the relevant DS and DNSKEY records). If we're in debug mode, don't fork and return the result directly, otherwise return STAT_ASYNC. The UDP validation process will restart when cache_recv_insert() calls pop_and_retry_query() after the result arrives via the pipe to the parent. */ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header *header, ssize_t *plen, int class, struct server *server, int *keycount, int *validatecount) { struct server *s; if (!option_bool(OPT_DEBUG)) { pid_t p; int i, pipefd[2]; #ifdef HAVE_LINUX_NETWORK unsigned char a = 0; #endif /* check to see if we have a free tcp process slot. */ for (i = daemon->max_procs - 1; i >= 0; i--) if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1) break; /* No slots or no pipe */ if (i < 0 || pipe(pipefd) != 0) return STAT_ABANDONED; if ((p = fork()) != 0) { close(pipefd[1]); /* parent needs read pipe end. */ if (p == -1) { /* fork() failed */ close(pipefd[0]); return STAT_ABANDONED; } #ifdef HAVE_LINUX_NETWORK /* The child process inherits the netlink socket, which it never uses, but when the parent (us) uses it in the future, the answer may go to the child, resulting in the parent blocking forever awaiting the result. To avoid this the child closes the netlink socket, but there's a nasty race, since the parent may use netlink before the child has done the close. To avoid this, the parent blocks here until a single byte comes back up the pipe, which is sent by the child after it has closed the netlink socket. */ read_write(pipefd[0], &a, 1, RW_READ); #endif /* i holds index of free slot */ daemon->tcp_pids[i] = p; daemon->tcp_pipes[i] = pipefd[0]; daemon->metrics[METRIC_TCP_CONNECTIONS]++; if (daemon->metrics[METRIC_TCP_CONNECTIONS] > daemon->max_procs_used) daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS]; /* child can use a maximum of this many log serials. */ daemon->log_id += daemon->limit[LIMIT_WORK]; /* tell the caller we've forked. */ return STAT_ASYNC; } else { /* child starts here. */ #ifdef HAVE_LINUX_NETWORK /* See comment above re: netlink socket. */ close(daemon->netlinkfd); read_write(pipefd[1], &a, 1, RW_WRITE); #endif close(pipefd[0]); /* close read end in child. */ daemon->pipe_to_parent = pipefd[1]; } } status = tcp_from_udp(now, status, header, plen, class, daemon->namebuff, daemon->keyname, server, keycount, validatecount); /* close upstream connections. */ for (s = daemon->servers; s; s = s->next) if (s->tcpfd != -1) { shutdown(s->tcpfd, SHUT_RDWR); close(s->tcpfd); s->tcpfd = -1; } if (!option_bool(OPT_DEBUG)) { ssize_t m = -2; /* tell our parent we're done, and what the result was then exit. */ read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE); read_write(daemon->pipe_to_parent, (unsigned char *)&status, sizeof(status), RW_WRITE); read_write(daemon->pipe_to_parent, (unsigned char *)plen, sizeof(*plen), RW_WRITE); read_write(daemon->pipe_to_parent, (unsigned char *)header, *plen, RW_WRITE); read_write(daemon->pipe_to_parent, (unsigned char *)&forward, sizeof(forward), RW_WRITE); read_write(daemon->pipe_to_parent, (unsigned char *)&forward->uid, sizeof(forward->uid), RW_WRITE); read_write(daemon->pipe_to_parent, (unsigned char *)keycount, sizeof(*keycount), RW_WRITE); read_write(daemon->pipe_to_parent, (unsigned char *)&keycount, sizeof(keycount), RW_WRITE); read_write(daemon->pipe_to_parent, (unsigned char *)validatecount, sizeof(*validatecount), RW_WRITE); read_write(daemon->pipe_to_parent, (unsigned char *)&validatecount, sizeof(validatecount), RW_WRITE); close(daemon->pipe_to_parent); flush_log(); _exit(0); } /* path for debug mode. */ return status; } #endif #ifdef HAVE_DHCP int make_icmp_sock(void) { int fd; int zeroopt = 0; if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1) { if (!fix_fd(fd) || setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1) { close(fd); fd = -1; } } return fd; } int icmp_ping(struct in_addr addr) { /* Try and get an ICMP echo from a machine. */ int fd; struct sockaddr_in saddr; struct { struct ip ip; struct icmp icmp; } packet; unsigned short id = rand16(); unsigned int i, j; int gotreply = 0; #if defined(HAVE_LINUX_NETWORK) || defined (HAVE_SOLARIS_NETWORK) if ((fd = make_icmp_sock()) == -1) return 0; #else int opt = 2000; fd = daemon->dhcp_icmp_fd; setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)); #endif saddr.sin_family = AF_INET; saddr.sin_port = 0; saddr.sin_addr = addr; #ifdef HAVE_SOCKADDR_SA_LEN saddr.sin_len = sizeof(struct sockaddr_in); #endif memset(&packet.icmp, 0, sizeof(packet.icmp)); packet.icmp.icmp_type = ICMP_ECHO; packet.icmp.icmp_id = id; for (j = 0, i = 0; i < sizeof(struct icmp) / 2; i++) j += ((u16 *)&packet.icmp)[i]; while (j>>16) j = (j & 0xffff) + (j >> 16); packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j; while (retry_send(sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0, (struct sockaddr *)&saddr, sizeof(saddr)))); gotreply = delay_dhcp(dnsmasq_time(), PING_WAIT, fd, addr.s_addr, id); #if defined(HAVE_LINUX_NETWORK) || defined(HAVE_SOLARIS_NETWORK) close(fd); #else opt = 1; setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)); #endif return gotreply; } int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id) { /* Delay processing DHCP packets for "sec" seconds counting from "start". If "fd" is not -1 it will stop waiting if an ICMP echo reply is received from "addr" with ICMP ID "id" and return 1 */ /* Note that whilst waiting, we check for (and service) events on the DNS and TFTP sockets, (so doing that better not use any resources our caller has in use...) but we remain deaf to signals or further DHCP packets. */ /* 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 rc, timeout_count; time_t now; for (now = dnsmasq_time(), timeout_count = 0; (difftime(now, start) <= (float)sec) && (timeout_count < sec * 4);) { poll_reset(); if (fd != -1) poll_listen(fd, POLLIN); if (daemon->port != 0) set_dns_listeners(); #ifdef HAVE_TFTP set_tftp_listeners(); #endif set_log_writer(); #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(0); if (daemon->port != 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(now); #endif if (fd != -1) { struct { struct ip ip; struct icmp icmp; } packet; struct sockaddr_in faddr; socklen_t len = sizeof(faddr); if (poll_check(fd, POLLIN) && recvfrom(fd, &packet, sizeof(packet), 0, (struct sockaddr *)&faddr, &len) == sizeof(packet) && addr == faddr.sin_addr.s_addr && packet.icmp.icmp_type == ICMP_ECHOREPLY && packet.icmp.icmp_seq == 0 && packet.icmp.icmp_id == id) return 1; } } return 0; } #endif /* HAVE_DHCP */ void tcp_init(void) { daemon->tcp_pids = safe_malloc(daemon->max_procs*sizeof(pid_t)); daemon->tcp_pipes = safe_malloc(daemon->max_procs*sizeof(int)); } dnsmasq-2.91/src/inotify.c0000664000175000017500000002241014765043257013725 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_INOTIFY #include #include /* For MAXSYMLINKS */ /* the strategy is to set an 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) { free(buf); return NULL; } else die(_("cannot access path %s: %s"), path, EC_MISC); } else if (rc < size-1) { char *d; buf[rc] = 0; if (buf[0] != '/' && ((d = strrchr(path, '/')))) { /* Add path to relative link */ char *new_buf = safe_malloc((d - path) + strlen(buf) + 2); *(d+1) = 0; strcpy(new_buf, path); strcat(new_buf, buf); free(buf); buf = new_buf; } return buf; } /* Buffer too small, increase and retry */ size += 64; free(buf); } } void inotify_dnsmasq_init() { struct resolvc *res; inotify_buffer = safe_malloc(INOTIFY_SZ); daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); if (daemon->inotifyfd == -1) die(_("failed to create inotify: %s"), NULL, EC_MISC); if (daemon->port == 0 || option_bool(OPT_NO_RESOLV)) return; 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-existent 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); } } static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *path) { /* Check if this file is already known in dd->files */ struct hostsfile *ah = NULL; for(ah = dd->files; ah; ah = ah->next) if(ah && ah->fname && strcmp(path, ah->fname) == 0) return ah; /* Not known, create new hostsfile record for this dyndir */ struct hostsfile *newah = NULL; if(!(newah = whine_malloc(sizeof(struct hostsfile)))) return NULL; /* Add this file to the tip of the linked list */ newah->next = dd->files; dd->files = newah; /* Copy flags, set index and the full file path */ newah->flags = dd->flags; newah->index = daemon->host_index++; newah->fname = path; return newah; } /* 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 dyndir *dd; for (dd = daemon->dynamic_dirs; dd; dd = dd->next) { DIR *dir_stream = NULL; struct dirent *ent; struct stat buf; if (!(dd->flags & flag)) continue; if (stat(dd->dname, &buf) == -1) { my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"), dd->dname, strerror(errno)); continue; } if (!(S_ISDIR(buf.st_mode))) { my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"), dd->dname, _("not a directory")); continue; } if (!(dd->flags & AH_WD_DONE)) { dd->wd = inotify_add_watch(daemon->inotifyfd, dd->dname, IN_CLOSE_WRITE | IN_MOVED_TO | IN_DELETE); dd->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 (dd->wd == -1 || !(dir_stream = opendir(dd->dname))) { my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"), dd->dname, strerror(errno)); continue; } while ((ent = readdir(dir_stream))) { size_t lendir = strlen(dd->dname); 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))) { struct hostsfile *ah; strcpy(path, dd->dname); strcat(path, "/"); strcat(path, ent->d_name); if (!(ah = dyndir_addhosts(dd, path))) { free(path); continue; } /* ignore non-regular files */ if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode)) { if (dd->flags & AH_HOSTS) total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz); #ifdef HAVE_DHCP else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT)) option_read_dynfile(path, dd->flags); #endif } } } closedir(dir_stream); } } int inotify_check(time_t now) { int hit = 0; struct dyndir *dd; 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) { size_t namelen; in = (struct inotify_event*)p; /* ignore emacs backups and dotfiles */ if (in->len == 0 || (namelen = strlen(in->name)) == 0 || in->name[namelen - 1] == '~' || (in->name[0] == '#' && in->name[namelen - 1] == '#') || in->name[0] == '.') continue; for (res = daemon->resolv_files; res; res = res->next) if (res->wd == in->wd && strcmp(res->file, in->name) == 0) hit = 1; for (dd = daemon->dynamic_dirs; dd; dd = dd->next) if (dd->wd == in->wd) { size_t lendir = strlen(dd->dname); char *path; if ((path = whine_malloc(lendir + in->len + 2))) { struct hostsfile *ah = NULL; strcpy(path, dd->dname); strcat(path, "/"); strcat(path, in->name); /* Is this is a deletion event? */ if (in->mask & IN_DELETE) my_syslog(LOG_INFO, _("inotify: %s removed"), path); else my_syslog(LOG_INFO, _("inotify: %s new or modified"), path); if (dd->flags & AH_HOSTS) { if ((ah = dyndir_addhosts(dd, path))) { const unsigned int removed = cache_remove_uid(ah->index); if (removed > 0) my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, path); /* (Re-)load hostsfile only if this event isn't triggered by deletion */ if (!(in->mask & IN_DELETE)) read_hostsfile(path, ah->index, 0, NULL, 0); #ifdef HAVE_DHCP if (daemon->dhcp || daemon->doing_dhcp6) { /* Propagate 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 (dd->flags & AH_DHCP_HST) { if (option_read_dynfile(path, AH_DHCP_HST)) { /* Propagate 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 (dd->flags & AH_DHCP_OPT) option_read_dynfile(path, AH_DHCP_OPT); #endif if (!ah) free(path); } } } } return hit; } #endif /* INOTIFY */ dnsmasq-2.91/src/rfc2131.c0000664000175000017500000025662014765043257013341 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_DHCP #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_opt(struct dhcp_lease *lease, unsigned char *opt); #endif 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); static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val); static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt, const char *string, int null_term); static struct in_addr option_addr(unsigned char *opt); static unsigned int option_uint(unsigned char *opt, int offset, int size); static void log_packet(char *type, void *addr, unsigned char *ext_mac, 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, 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 unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len); static void do_options(struct dhcp_context *context, struct dhcp_packet *mess, unsigned char *end, unsigned char *req_options, char *hostname, 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, time_t now, unsigned int lease_time, unsigned short fuzz, const char *pxevendor); static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); static int do_encap_opts(struct dhcp_opt *opt, 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, const char *pxevendor); 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, time_t now); struct dhcp_boot *find_boot(struct dhcp_netid *netid); static int pxe_uefi_workaround(int pxe_arch, struct dhcp_netid *netid, struct dhcp_packet *mess, struct in_addr local, time_t now, int pxe); static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid); static int is_pxe_client(struct dhcp_packet *mess, size_t sz, const char **pxe_vendor); static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term); static void handle_encap(struct dhcp_packet *mess, unsigned char *end, unsigned char *req_options, int null_term, struct dhcp_netid *tagif, int pxemode); size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, size_t sz, time_t now, int unicast_dest, int loopback, int *is_inform, int pxe, struct in_addr fallback, time_t recvtime) { unsigned char *opt, *clid = NULL; struct dhcp_lease *ltmp, *lease = NULL; struct dhcp_vendor *vendor; struct dhcp_mac *mac; struct dhcp_netid_list *id_list; int clid_len = 0, ignore = 0, do_classes = 0, rapid_commit = 0, selecting = 0, pxearch = -1; const char *pxevendor = NULL; struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base; unsigned char *end = (unsigned char *)(mess + 1); unsigned char *real_end = (unsigned char *)(mess + 1); char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL, *domain = NULL; int hostname_auth = 0, borken_opt = 0; unsigned char *req_options = NULL; char *message = NULL; unsigned int time; struct dhcp_config *config; struct dhcp_netid *netid, *tagif_netid; struct in_addr subnet_addr, override; unsigned short fuzz = 0; unsigned int mess_type = 0; unsigned char fqdn_flags = 0; unsigned char *agent_id = NULL, *uuid = NULL; unsigned char *emac = NULL; int vendor_class_len = 0, emac_len = 0; struct dhcp_netid known_id, iface_id, cpewan_id; struct dhcp_opt *o; unsigned char pxe_uuid[17]; unsigned char *oui = NULL, *serial = NULL; #ifdef HAVE_SCRIPT unsigned char *class = NULL; #endif subnet_addr.s_addr = override.s_addr = 0; /* set tag with name == interface */ iface_id.net = iface_name; iface_id.next = NULL; netid = &iface_id; if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX) return 0; if (mess->htype == 0 && mess->hlen != 0) return 0; /* check for DHCP rather than BOOTP */ if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1))) { u32 cookie = htonl(DHCP_COOKIE); /* only insist on a cookie for DHCP. */ if (memcmp(mess->options, &cookie, sizeof(u32)) != 0) return 0; mess_type = option_uint(opt, 0, 1); /* two things to note here: expand_buf may move the packet, so reassign mess from daemon->packet. Also, the size sent includes the IP and UDP headers, hence the magic "-28" */ if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2))) { size_t size = (size_t)option_uint(opt, 0, 2) - 28; if (size > DHCP_PACKET_MAX) size = DHCP_PACKET_MAX; else if (size < sizeof(struct dhcp_packet)) size = sizeof(struct dhcp_packet); if (expand_buf(&daemon->dhcp_packet, size)) { mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base; real_end = end = ((unsigned char *)mess) + size; } } /* Some buggy clients set ciaddr when they shouldn't, so clear that here since it can affect the context-determination code. */ if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER)) mess->ciaddr.s_addr = 0; /* search for device identity from CPEWAN devices, we pass this through to the script */ if ((opt = option_find(mess, sz, OPTION_VENDOR_IDENT_OPT, 5))) { unsigned int elen, offset, len = option_len(opt); for (offset = 0; offset < (len - 5); offset += elen + 5) { elen = option_uint(opt, offset + 4 , 1); if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA && offset + elen + 5 <= len) { unsigned char *x = option_ptr(opt, offset + 5); unsigned char *y = option_ptr(opt, offset + elen + 5); oui = option_find1(x, y, 1, 1); serial = option_find1(x, y, 2, 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) { cpewan_id.net = "cpewan-id"; cpewan_id.next = netid; netid = &cpewan_id; } break; } } } if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1))) { /* Any agent-id needs to be copied back out, verbatim, as the last option in the packet. Here, we shift it to the very end of the buffer, if it doesn't get overwritten, then it will be shuffled back at the end of processing. Note that the incoming options must not be overwritten here, so there has to be enough free space at the end of the packet to copy the option. */ unsigned char *sopt; unsigned int total = option_len(opt) + 2; unsigned char *last_opt = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + sz, OPTION_END, 0); if (last_opt && last_opt < end - total) { end -= total; agent_id = end; memcpy(agent_id, opt, total); } /* look for RFC3527 Link selection sub-option */ if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBNET_SELECT, INADDRSZ))) subnet_addr = option_addr(sopt); /* look for RFC5107 server-identifier-override */ if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ))) override = option_addr(sopt); /* if a circuit-id or remote-is option is provided, exact-match to options. */ for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) { int search; if (vendor->match_type == MATCH_CIRCUIT) search = SUBOPT_CIRCUIT_ID; else if (vendor->match_type == MATCH_REMOTE) search = SUBOPT_REMOTE_ID; else if (vendor->match_type == MATCH_SUBSCRIBER) search = SUBOPT_SUBSCR_ID; else continue; if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) && vendor->len == option_len(sopt) && memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0) { vendor->netid.next = netid; netid = &vendor->netid; } } } /* Check for RFC3011 subnet selector - only if RFC3527 one not present */ if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ))) subnet_addr = option_addr(opt); /* If there is no client identifier option, use the hardware address */ if (!option_bool(OPT_IGNORE_CLID) && (opt = option_find(mess, sz, OPTION_CLIENT_ID, 1))) { clid_len = option_len(opt); clid = option_ptr(opt, 0); } /* do we have a lease in store? */ lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len); /* If this request is missing a clid, but we've seen one before, use it again for option matching etc. */ if (lease && !clid && lease->clid) { clid_len = lease->clid_len; clid = lease->clid; } /* find mac to use for logging and hashing */ emac = extended_hwaddr(mess->htype, mess->hlen, mess->chaddr, clid_len, clid, &emac_len); } for (mac = daemon->dhcp_macs; mac; mac = mac->next) if (mac->hwaddr_len == mess->hlen && (mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) && memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask)) { mac->netid.next = netid; netid = &mac->netid; } /* Determine network for this packet. Our caller will have already linked all the contexts which match the addresses of the receiving interface but if the machine has an address already, or came via a relay, or we have a subnet selector, we search again. If we don't have have a giaddr or explicit subnet selector, use the ciaddr. This is necessary because a machine which got a lease via a relay won't use the relay to renew. If matching a ciaddr fails but we have a context from the physical network, continue using that to allow correct DHCPNAK generation later. */ if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr) { struct dhcp_context *context_tmp, *context_new = NULL; struct shared_network *share = NULL; struct in_addr addr; int force = 0, via_relay = 0; if (subnet_addr.s_addr) { addr = subnet_addr; force = 1; } else if (mess->giaddr.s_addr) { addr = mess->giaddr; force = 1; via_relay = 1; } else { /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */ addr = mess->ciaddr; for (context_tmp = context; context_tmp; context_tmp = context_tmp->current) 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_new = context; break; } } if (!context_new) { for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next) { 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); } /* check to see is a context is OK because of a shared address on the relayed subnet. */ if (via_relay) for (share = daemon->shared_networks; share; share = share->next) { #ifdef HAVE_DHCP6 if (share->shared_addr.s_addr == 0) continue; #endif if (share->if_index != 0 || share->match_addr.s_addr != mess->giaddr.s_addr) continue; if (netmask.s_addr != 0 && is_same_net(share->shared_addr, context_tmp->start, netmask) && is_same_net(share->shared_addr, context_tmp->end, netmask)) break; } /* 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 (share || (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 && !share) 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; } if (!context) { const char *via; if (subnet_addr.s_addr) { via = _("with subnet selector"); inet_ntop(AF_INET, &subnet_addr, daemon->addrbuff, ADDRSTRLEN); } else { via = _("via"); if (mess->giaddr.s_addr) inet_ntop(AF_INET, &mess->giaddr, daemon->addrbuff, ADDRSTRLEN); else safe_strncpy(daemon->addrbuff, iface_name, ADDRSTRLEN); } my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"), via, daemon->addrbuff); return 0; } if (option_bool(OPT_LOG_OPTS)) { struct dhcp_context *context_tmp; for (context_tmp = context; context_tmp; context_tmp = context_tmp->current) { inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAME); if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) { inet_ntop(AF_INET, &context_tmp->netmask, daemon->addrbuff, ADDRSTRLEN); my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"), ntohl(mess->xid), daemon->namebuff, daemon->addrbuff); } else { inet_ntop(AF_INET, &context_tmp->end, daemon->addrbuff, ADDRSTRLEN); my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"), ntohl(mess->xid), daemon->namebuff, daemon->addrbuff); } } } /* 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, existence 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 <= (unsigned)(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 <= (unsigned)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; config = find_config(daemon->dhcp_conf, context, clid, clid_len, mess->chaddr, mess->hlen, mess->htype, NULL, run_tag_if(netid)); /* set "known" tag for known hosts */ if (config) { known_id.net = "known"; known_id.next = netid; netid = &known_id; } else if (find_config(daemon->dhcp_conf, NULL, clid, clid_len, mess->chaddr, mess->hlen, mess->htype, NULL, run_tag_if(netid))) { known_id.net = "known-othernet"; known_id.next = netid; netid = &known_id; } if (mess_type == 0 && !pxe) { /* BOOTP request */ struct dhcp_netid id, bootp_id; struct in_addr *logaddr = NULL; /* must have a MAC addr for bootp */ if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY)) return 0; if (have_config(config, CONFIG_DISABLE)) message = _("disabled"); end = mess->options + 64; /* BOOTP vend area is only 64 bytes */ if (have_config(config, CONFIG_NAME)) { hostname = config->hostname; domain = config->domain; } if (config) { struct dhcp_netid_list *list; for (list = config->netid; list; list = list->next) { list->list->next = netid; netid = list->list; } } /* Match incoming filename field as a netid. */ if (mess->file[0]) { memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file)); daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */ id.net = (char *)daemon->dhcp_buff2; id.next = netid; netid = &id; } /* Add "bootp" as a tag to allow different options, address ranges etc for BOOTP clients */ bootp_id.net = "bootp"; bootp_id.next = netid; netid = &bootp_id; tagif_netid = run_tag_if(netid); for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next) if (match_netid(id_list->list, tagif_netid, 0)) message = _("ignored"); if (!message) { int nailed = 0; if (have_config(config, CONFIG_ADDR)) { nailed = 1; logaddr = &config->addr; mess->yiaddr = config->addr; if ((lease = lease_find_by_addr(config->addr)) && (lease->hwaddr_len != mess->hlen || lease->hwaddr_type != mess->htype || memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0)) message = _("address in use"); } else { if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) || !address_available(context, lease->addr, tagif_netid)) { if (lease) { /* lease exists, wrong network. */ lease_prune(lease, now); lease = NULL; } if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now, loopback)) message = _("no address available"); } else mess->yiaddr = lease->addr; } if (!message && !(context = narrow_context(context, mess->yiaddr, netid))) message = _("wrong network"); else if (context->netid.net) { context->netid.next = netid; tagif_netid = run_tag_if(&context->netid); } log_tags(tagif_netid, ntohl(mess->xid)); if (!message && !nailed) { for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next) if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0)) break; if (!id_list) message = _("no address configured"); } if (!message && !lease && (!(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, now, 1); if (hostname) 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, now); clear_packet(mess, end); do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL); } } daemon->metrics[METRIC_BOOTP]++; log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid); return message ? 0 : dhcp_packet_size(mess, agent_id, real_end); } 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); char *pq = daemon->dhcp_buff; unsigned char *pp, *op = option_ptr(opt, 0); fqdn_flags = *op; len -= 3; op += 3; pp = op; /* 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)) - pp) < len) { memcpy(pq, op+1, *op); pq += *op; op += (*op)+1; *(pq++) = '.'; } else { memcpy(pq, op, len); if (len > 0 && op[len-1] == 0) borken_opt = 1; pq += len + 1; } if (pq != daemon->dhcp_buff) pq--; *pq = 0; if (legal_hostname(daemon->dhcp_buff)) offer_hostname = client_hostname = daemon->dhcp_buff; } else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1))) { int len = option_len(opt); memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len); /* Microsoft clients are broken, and need zero-terminated strings in options. We detect this state here, and do the same in any options we send */ if (len > 0 && daemon->dhcp_buff[len-1] == 0) borken_opt = 1; else daemon->dhcp_buff[len] = 0; if (legal_hostname(daemon->dhcp_buff)) client_hostname = daemon->dhcp_buff; } if (client_hostname) { struct dhcp_match_name *m; size_t nl = strlen(client_hostname); if (option_bool(OPT_LOG_OPTS)) my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname); for (m = daemon->dhcp_name_match; m; m = m->next) { size_t ml = strlen(m->name); char save = 0; if (nl < ml) continue; if (nl > ml) { save = client_hostname[ml]; client_hostname[ml] = 0; } if (hostname_isequal(client_hostname, m->name) && (save == 0 || m->wildcard)) { m->netid->next = netid; netid = m->netid; } if (save != 0) client_hostname[ml] = save; } } if (have_config(config, CONFIG_NAME)) { hostname = config->hostname; domain = config->domain; hostname_auth = 1; /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */ if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname)) offer_hostname = hostname; } else if (client_hostname) { domain = strip_hostname(client_hostname); if (strlen(client_hostname) != 0) { hostname = client_hostname; if (!config) { /* Search again now we have a hostname. Only accept configs without CLID and HWADDR here, (they won't match) to avoid impersonation by name. */ struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0, mess->chaddr, mess->hlen, mess->htype, hostname, run_tag_if(netid)); if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr) { config = new; /* set "known" tag for known hosts */ known_id.net = "known"; known_id.next = netid; netid = &known_id; } } } } if (config) { struct dhcp_netid_list *list; for (list = config->netid; list; list = list->next) { list->list->next = netid; netid = list->list; } } 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)) ignore = 1; /* If configured, we can override the server-id to be the address of the relay, so that all traffic goes via the relay and can pick up agent-id info. This can be configured for all relays, or by address. */ if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0) { if (!daemon->override_relays) override = mess->giaddr; else { struct addr_list *l; for (l = daemon->override_relays; l; l = l->next) if (l->addr.s_addr == mess->giaddr.s_addr) break; if (l) override = mess->giaddr; } } /* Can have setting to ignore the client ID for a particular MAC address or hostname */ if (have_config(config, CONFIG_NOCLID)) clid = NULL; /* Check if client is PXE client. */ if (daemon->enable_pxe && is_pxe_client(mess, sz, &pxevendor)) { if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17))) { memcpy(pxe_uuid, option_ptr(opt, 0), 17); uuid = pxe_uuid; } /* Check if this is really a PXE bootserver request, and handle specially if so. */ if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) && (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) && (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4))) { struct pxe_service *service; int type = option_uint(opt, 0, 2); int layer = option_uint(opt, 2, 2); unsigned char save71[4]; struct dhcp_opt opt71; if (ignore) return 0; if (layer & 0x8000) { my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported")); return 0; } memcpy(save71, option_ptr(opt, 0), 4); for (service = daemon->pxe_services; service; service = service->next) if (service->type == type) break; 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->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; if (strchr(service->basename, '.')) snprintf((char *)mess->file, sizeof(mess->file), "%s", service->basename); else snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer); option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK); option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr)); pxe_misc(mess, end, uuid, pxevendor); prune_vendor_opts(tagif_netid); opt71.val = save71; opt71.opt = SUBOPT_PXE_BOOT_ITEM; opt71.len = 4; opt71.flags = DHOPT_VENDOR_MATCH; opt71.netid = NULL; opt71.next = daemon->dhcp_opts; do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0); daemon->metrics[METRIC_PXE]++; 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))) { pxearch = option_uint(opt, 0, 2); /* proxy DHCP here. */ if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST))) { struct dhcp_context *tmp; int workaround = 0; for (tmp = context; tmp; tmp = tmp->current) if ((tmp->flags & CONTEXT_PROXY) && match_netid(tmp->filter, tagif_netid, 1)) break; if (tmp) { struct dhcp_boot *boot; int redirect4011 = 0; struct dhcp_opt *option; /* OK only dhcp-option-pxe options. */ tagif_netid = option_filter(netid, tmp->netid.net ? &tmp->netid : NULL, daemon->dhcp_opts, 2); boot = find_boot(tagif_netid); mess->yiaddr.s_addr = 0; if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0) { mess->ciaddr.s_addr = 0; mess->flags |= htons(0x8000); /* broadcast */ } clear_packet(mess, end); /* Redirect EFI clients to port 4011 */ if (pxearch >= 6) { redirect4011 = 1; mess->siaddr = tmp->local; } /* Returns true if only one matching service is available. On port 4011, it also inserts the boot file and server name. */ workaround = pxe_uefi_workaround(pxearch, tagif_netid, mess, tmp->local, now, pxe); if (!workaround && boot) { /* Provide the bootfile here, for iPXE, and in case we have no menu items and set discovery_control = 8 */ 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) safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file)); } option_put(mess, end, OPTION_MESSAGE_TYPE, 1, mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK); option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr)); pxe_misc(mess, end, uuid, pxevendor); prune_vendor_opts(tagif_netid); if ((pxe && !workaround) || !redirect4011) do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0); /* dhcp-option-pxe ONLY */ for (option = daemon->dhcp_opts; option; option = option->next) { int len; unsigned char *p; if (!(option->flags & DHOPT_TAGOK)) continue; len = do_opt(option, NULL, tmp, borken_opt); if ((p = free_space(mess, end, option->opt, len))) do_opt(option, p, tmp, borken_opt); } handle_encap(mess, end, req_options, borken_opt, tagif_netid, 2); daemon->metrics[METRIC_PXE]++; log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid); log_tags(tagif_netid, ntohl(mess->xid)); if (!ignore) apply_delay(mess->xid, recvtime, tagif_netid); return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end); } } } } /* if we're just a proxy server, go no further */ if ((context->flags & CONTEXT_PROXY) || pxe) return 0; if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0))) { req_options = (unsigned char *)daemon->dhcp_buff2; memcpy(req_options, option_ptr(opt, 0), option_len(opt)); req_options[option_len(opt)] = OPTION_END; } switch (mess_type) { case DHCPDECLINE: if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) || option_addr(opt).s_addr != server_id(context, override, fallback).s_addr) return 0; /* sanitise any message. Paranoid? Moi? */ sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff); if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ))) return 0; daemon->metrics[METRIC_DHCPDECLINE]++; 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); if (have_config(config, CONFIG_ADDR) && config->addr.s_addr == option_addr(opt).s_addr) { prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF); inet_ntop(AF_INET, &config->addr, daemon->addrbuff, ADDRSTRLEN); my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), daemon->addrbuff, daemon->dhcp_buff); config->flags |= CONFIG_DECLINED; config->decline_time = now; } else /* make sure this host gets a different address next time. */ for (; context; context = context->current) context->addr_epoch++; return 0; case DHCPRELEASE: if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) || !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) || option_addr(opt).s_addr != server_id(context, override, fallback).s_addr) return 0; if (lease && lease->addr.s_addr == mess->ciaddr.s_addr) lease_prune(lease, now); else message = _("unknown lease"); daemon->metrics[METRIC_DHCPRELEASE]++; 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; } else { struct in_addr addr, conf; addr.s_addr = conf.s_addr = 0; if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ))) addr = option_addr(opt); if (have_config(config, CONFIG_ADDR)) { inet_ntop(AF_INET, &config->addr, daemon->addrbuff, ADDRSTRLEN); if ((ltmp = lease_find_by_addr(config->addr)) && ltmp != lease && !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type)) { int len; unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len, ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len); my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"), daemon->addrbuff, print_mac(daemon->namebuff, mac, len)); } else { struct dhcp_context *tmp; for (tmp = context; tmp; tmp = tmp->current) if (context->router.s_addr == config->addr.s_addr) break; if (tmp) my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), daemon->addrbuff); else if (have_config(config, CONFIG_DECLINED) && difftime(now, config->decline_time) < (float)DECLINE_BACKOFF) my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), daemon->addrbuff); else conf = config->addr; } } if (conf.s_addr) mess->yiaddr = conf; else if (lease && address_available(context, lease->addr, tagif_netid) && !config_find_by_address(daemon->dhcp_conf, lease->addr)) mess->yiaddr = lease->addr; else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) && !config_find_by_address(daemon->dhcp_conf, addr) && do_icmp_ping(now, addr, 0, loopback)) mess->yiaddr = addr; else if (emac_len == 0) message = _("no unique-id"); else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now, loopback)) message = _("no address available"); } daemon->metrics[METRIC_DHCPDISCOVER]++; 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; if (context->netid.net) { context->netid.next = netid; tagif_netid = run_tag_if(&context->netid); } apply_delay(mess->xid, recvtime, tagif_netid); if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0)) { rapid_commit = 1; /* If a lease exists for this host and another address, squash it. */ if (lease && lease->addr.s_addr != mess->yiaddr.s_addr) { lease_prune(lease, now); lease = NULL; } goto rapid_commit; } log_tags(tagif_netid, ntohl(mess->xid)); daemon->metrics[METRIC_DHCPOFFER]++; 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. */ do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr), netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor); return dhcp_packet_size(mess, agent_id, real_end); case DHCPREQUEST: if (ignore || have_config(config, CONFIG_DISABLE)) return 0; if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ))) { /* SELECTING or INIT_REBOOT */ mess->yiaddr = option_addr(opt); /* send vendor and user class info for new or recreated lease */ do_classes = 1; if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ))) { /* SELECTING */ selecting = 1; if (override.s_addr != 0) { if (option_addr(opt).s_addr != override.s_addr) return 0; } else { for (; context; context = context->current) if (context->local.s_addr == option_addr(opt).s_addr) break; if (!context) { /* 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"); } } } /* If a lease exists for this host and another address, squash it. */ if (lease && lease->addr.s_addr != mess->yiaddr.s_addr) { lease_prune(lease, now); lease = NULL; } } else { /* INIT-REBOOT */ if (!lease && !option_bool(OPT_AUTHORITATIVE)) return 0; if (lease && lease->addr.s_addr != mess->yiaddr.s_addr) message = _("wrong address"); } } else { /* RENEWING or REBINDING */ /* Check existing lease for this address. We allow it to be missing if dhcp-authoritative mode as long as we can allocate the lease now - checked below. This makes for a smooth recovery from a lost lease DB */ if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) || (!lease && !option_bool(OPT_AUTHORITATIVE))) { /* A client rebinding will broadcast the request, so we may see it even if the lease is held by another server. Just ignore it in that case. If the request is unicast to us, then somethings wrong, NAK */ if (!unicast_dest) return 0; message = _("lease not found"); /* ensure we broadcast NAK */ unicast_dest = 0; } /* desynchronise renewals */ fuzz = rand16(); mess->yiaddr = mess->ciaddr; } daemon->metrics[METRIC_DHCPREQUEST]++; log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid); rapid_commit: if (!message) { struct dhcp_config *addr_config; struct dhcp_context *tmp = NULL; if (have_config(config, CONFIG_ADDR)) for (tmp = context; tmp; tmp = tmp->current) if (context->router.s_addr == config->addr.s_addr) break; if (!(context = narrow_context(context, mess->yiaddr, tagif_netid))) { /* If a machine moves networks whilst it has a lease, we catch that here. */ message = _("wrong network"); /* ensure we broadcast NAK */ unicast_dest = 0; } /* Check for renewal of a lease which is outside the allowed range. */ else if (!address_available(context, mess->yiaddr, tagif_netid) && (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr)) message = _("address not available"); /* Check if a new static address has been configured. Be very sure that when the client does DISCOVER, it will get the static address, otherwise an endless protocol loop will ensue. */ else if (!tmp && !selecting && have_config(config, CONFIG_ADDR) && (!have_config(config, CONFIG_DECLINED) || difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) && config->addr.s_addr != mess->yiaddr.s_addr && (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease)) message = _("static lease available"); /* Check to see if the address is reserved as a static address for another host */ else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config) message = _("address reserved"); else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr))) { /* If a host is configured with more than one MAC address, it's OK to 'nix a lease from one of it's MACs to give the address to another. */ if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type)) { inet_ntop(AF_INET, <mp->addr, daemon->addrbuff, ADDRSTRLEN); my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"), print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len), daemon->addrbuff); lease = ltmp; } else message = _("address in use"); } if (!message) { if (emac_len == 0) message = _("no unique-id"); else if (!lease) { if ((lease = lease4_allocate(mess->yiaddr))) do_classes = 1; else message = _("no leases left"); } } } if (message) { daemon->metrics[rapid_commit ? METRIC_NOANSWER : METRIC_DHCPNAK]++; log_packet(rapid_commit ? "NOANSWER" : "DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid); /* rapid commit case: lease allocate failed but don't send DHCPNAK */ if (rapid_commit) return 0; mess->yiaddr.s_addr = 0; clear_packet(mess, end); option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK); option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr)); option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt); /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on a distant subnet which unicast a REQ to us won't work. */ if (!unicast_dest || mess->giaddr.s_addr != 0 || mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask)) { mess->flags |= htons(0x8000); /* broadcast */ mess->ciaddr.s_addr = 0; } } else { if (context->netid.net) { context->netid.next = netid; tagif_netid = run_tag_if( &context->netid); } log_tags(tagif_netid, ntohl(mess->xid)); if (do_classes) { /* pick up INIT-REBOOT events. */ lease->flags |= LEASE_CHANGED; #ifdef HAVE_SCRIPT if (daemon->lease_change_command) { 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); } /* DNSMASQ_REQUESTED_OPTIONS */ if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 1))) { int i, len = option_len(opt); unsigned char *rop = option_ptr(opt, 0); for (i = 0; i < len; i++) lease_add_extradata(lease, (unsigned char *)daemon->namebuff, sprintf(daemon->namebuff, "%u", rop[i]), (i + 1) == len ? 0 : ','); } else lease_add_extradata(lease, NULL, 0, 0); add_extradata_opt(lease, option_find(mess, sz, OPTION_MUD_URL_V4, 1)); /* 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, -1); } } #endif } if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr))) { domain = get_domain(mess->yiaddr); hostname = client_hostname; hostname_auth = 1; } 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, now, do_classes); /* if all the netids in the ignore_name list are present, ignore client-supplied name */ if (!hostname_auth) { for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next) if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0)) break; if (id_list) hostname = NULL; } /* Last ditch, if configured, generate hostname from mac address */ if (!hostname && emac_len != 0) { for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next) if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0)) break; if (id_list) { int i; hostname = daemon->dhcp_buff; /* buffer is 256 bytes, 3 bytes per octet */ for (i = 0; (i < emac_len) && (i < 80); i++) hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-"); hostname = daemon->dhcp_buff; } } if (hostname) lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain); lease_set_expires(lease, time, now); lease_set_interface(lease, int_index, now); if (override.s_addr != 0) lease->override = override; else override = lease->override; daemon->metrics[METRIC_DHCPACK]++; 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 (rapid_commit) option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0); do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor); } return dhcp_packet_size(mess, agent_id, real_end); case DHCPINFORM: if (ignore || have_config(config, CONFIG_DISABLE)) message = _("ignored"); daemon->metrics[METRIC_DHCPINFORM]++; log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid); if (message || mess->ciaddr.s_addr == 0) return 0; /* For DHCPINFORM only, cope without a valid context */ context = narrow_context(context, mess->ciaddr, tagif_netid); /* Find a least based on IP address if we didn't get one from MAC address/client-d */ if (!lease && (lease = lease_find_by_addr(mess->ciaddr)) && lease->hostname) hostname = lease->hostname; if (!hostname) hostname = host_from_dns(mess->ciaddr); if (context && context->netid.net) { context->netid.next = netid; tagif_netid = run_tag_if(&context->netid); } log_tags(tagif_netid, ntohl(mess->xid)); daemon->metrics[METRIC_DHCPACK]++; 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 override = lease->override; } 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)); /* 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); } do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr), netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0, pxevendor); *is_inform = 1; /* handle reply differently */ return dhcp_packet_size(mess, agent_id, real_end); } 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. This could be anything, but will normally be EUI64 for Firewire. We assume that if the first byte of the client-id equals the htype byte then the client-id is using the usual encoding and use the rest of the client-id: if not we can use the whole client-id. This should give sane MAC address logs. */ unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr, int clid_len, unsigned char *clid, int *len_out) { if (hwlen == 0 && clid && clid_len > 3) { if (clid[0] == hwtype) { *len_out = clid_len - 1 ; return clid + 1; } #if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394) if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394) { *len_out = clid_len - 1 ; return clid + 1; } #endif *len_out = clid_len; return clid; } *len_out = hwlen; return hwaddr; } static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt) { unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time; if (opt) { unsigned int req_time = option_uint(opt, 0, 4); if (req_time < 120 ) req_time = 120; /* sanity */ if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time)) time = req_time; } return time; } static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback) { if (override.s_addr != 0) return override; else if (context && context->local.s_addr != 0) return context->local; else return fallback; } static int sanitise(unsigned char *opt, char *buf) { char *p; int i; *buf = 0; if (!opt) return 0; p = option_ptr(opt, 0); for (i = option_len(opt); i > 0; i--) { char c = *p++; if (isprint((unsigned char)c)) *buf++ = c; } *buf = 0; /* add terminator */ return 1; } #ifdef HAVE_SCRIPT static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt) { if (!opt) lease_add_extradata(lease, NULL, 0, 0); else 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, char *err, u32 xid) { if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP)) return; daemon->addrbuff[0] = 0; if (addr) inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN); 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%s", ntohl(xid), type, interface, daemon->addrbuff, addr ? " " : "", daemon->namebuff, string ? string : "", err ? err : ""); else my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s", type, interface, daemon->addrbuff, addr ? " " : "", daemon->namebuff, string ? string : "", err ? err : ""); #ifdef HAVE_UBUS if (!strcmp(type, "DHCPACK")) ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface); else if (!strcmp(type, "DHCPRELEASE")) ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface); #endif } static void log_options(unsigned char *start, u32 xid) { while (*start != OPTION_END) { char *optname = option_string(AF_INET, start[0], option_ptr(start, 0), option_len(start), daemon->namebuff, MAXDNAME); 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; } } static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize) { while (1) { if (p >= end) return NULL; else if (*p == OPTION_END) return opt == OPTION_END ? p : NULL; else if (*p == OPTION_PAD) p++; else { int opt_len; if (p > end - 2) return NULL; /* malformed packet */ opt_len = option_len(p); if (p > end - (2 + opt_len)) return NULL; /* malformed packet */ if (*p == opt && opt_len >= minsize) return p; p += opt_len + 2; } } } static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize) { unsigned char *ret, *overload; /* skip over DHCP cookie; */ if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize))) return ret; /* look for overload option. */ if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1))) return NULL; /* Can we look in filename area ? */ if ((overload[2] & 1) && (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize))) return ret; /* finally try sname area */ if ((overload[2] & 2) && (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize))) return ret; return NULL; } static struct in_addr option_addr(unsigned char *opt) { /* this worries about unaligned data in the option. */ /* struct in_addr is network byte order */ struct in_addr ret; memcpy(&ret, option_ptr(opt, 0), INADDRSZ); return ret; } static unsigned int option_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 = option_ptr(opt, offset); for (i = 0; i < size; i++) ret = (ret << 8) | *p++; return ret; } static unsigned char *dhcp_skip_opts(unsigned char *start) { while (*start != 0) start += start[1] + 2; return start; } /* only for use when building packet: doesn't check for bad data. */ static unsigned char *find_overload(struct dhcp_packet *mess) { unsigned char *p = &mess->options[0] + sizeof(u32); while (*p != 0) { if (*p == OPTION_OVERLOAD) return p; p += p[1] + 2; } return NULL; } 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; /* move agent_id back down to the end of the packet */ if (agent_id) { memmove(p, agent_id, real_end - agent_id); p += real_end - agent_id; memset(p, 0, real_end - p); /* in case of overlap */ } /* add END options to the regions. */ overload = find_overload(mess); if (overload && (option_uint(overload, 0, 1) & 1)) { *dhcp_skip_opts(mess->file) = OPTION_END; if (option_bool(OPT_LOG_OPTS)) log_options(mess->file, mess->xid); } else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0) my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file); if (overload && (option_uint(overload, 0, 1) & 2)) { *dhcp_skip_opts(mess->sname) = OPTION_END; if (option_bool(OPT_LOG_OPTS)) log_options(mess->sname, mess->xid); } else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0) my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname); *p++ = OPTION_END; if (option_bool(OPT_LOG_OPTS)) { if (mess->siaddr.s_addr != 0) { inet_ntop(AF_INET, &mess->siaddr, daemon->addrbuff, ADDRSTRLEN); my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), daemon->addrbuff); } if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0) my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid)); log_options(&mess->options[0] + sizeof(u32), mess->xid); } ret = (size_t)(p - (unsigned char *)mess); if (ret < MIN_PACKETSZ) ret = MIN_PACKETSZ; return ret; } static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len) { unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32)); if (p + len + 3 >= end) /* not enough space in options area, try and use overload, if poss */ { unsigned char *overload; if (!(overload = find_overload(mess)) && (mess->file[0] == 0 || mess->sname[0] == 0)) { /* attempt to overload fname and sname areas, we've reserved space for the overflow option previuously. */ overload = p; *(p++) = OPTION_OVERLOAD; *(p++) = 1; } p = NULL; /* using filename field ? */ if (overload) { if (mess->file[0] == 0) overload[2] |= 1; if (overload[2] & 1) { p = dhcp_skip_opts(mess->file); if (p + len + 3 >= mess->file + sizeof(mess->file)) p = NULL; } if (!p) { /* try to bring sname into play (it may be already) */ if (mess->sname[0] == 0) overload[2] |= 2; if (overload[2] & 2) { p = dhcp_skip_opts(mess->sname); if (p + len + 3 >= mess->sname + sizeof(mess->sname)) p = NULL; } } } if (!p) my_syslog(MS_DHCP | LOG_WARNING, _("cannot send DHCP/BOOTP option %d: no space left in packet"), opt); } if (p) { *(p++) = opt; *(p++) = len; } return p; } static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val) { int i; unsigned char *p = free_space(mess, end, opt, len); if (p) for (i = 0; i < len; i++) *(p++) = val >> (8 * (len - (i + 1))); } static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt, const char *string, int null_term) { unsigned char *p; size_t len = strlen(string); if (null_term && len != 255) len++; if ((p = free_space(mess, end, opt, len))) memcpy(p, string, len); } /* return length, note this only does the data part */ static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term) { int len = opt->len; if ((opt->flags & DHOPT_STRING) && null_term && len != 255) len++; if (p && len != 0) { if (context && (opt->flags & DHOPT_ADDR)) { int j; struct in_addr *a = (struct in_addr *)opt->val; for (j = 0; j < opt->len; j+=INADDRSZ, a++) { /* zero means "self" (but not in vendorclass options.) */ if (a->s_addr == 0) memcpy(p, &context->local, INADDRSZ); else memcpy(p, a, INADDRSZ); p += INADDRSZ; } } else /* empty string may be extended to "\0" by null_term */ memcpy(p, opt->val ? opt->val : (unsigned char *)"", len); } return len; } static int in_list(unsigned char *list, int opt) { int i; /* If no requested options, send everything, not nothing. */ if (!list) return 1; for (i = 0; list[i] != OPTION_END; i++) if (opt == list[i]) return 1; return 0; } static struct dhcp_opt *option_find2(int opt) { 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; } /* mark vendor-encapsulated options which match the client-supplied or config-supplied vendor class */ static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt) { for (; dopt; dopt = dopt->next) { dopt->flags &= ~DHOPT_VENDOR_MATCH; if (opt && (dopt->flags & DHOPT_VENDOR)) { const struct dhcp_pxe_vendor *pv; struct dhcp_pxe_vendor dummy_vendor = { .data = (char *)dopt->u.vendor_class, .next = NULL, }; if (dopt->flags & DHOPT_VENDOR_PXE) pv = daemon->dhcp_pxe_vendors; else pv = &dummy_vendor; for (; pv; pv = pv->next) { int i, len = 0, matched = 0; if (pv->data) len = strlen(pv->data); for (i = 0; i <= (option_len(opt) - len); i++) if (len == 0 || memcmp(pv->data, option_ptr(opt, i), len) == 0) { matched = 1; break; } if (matched) { dopt->flags |= DHOPT_VENDOR_MATCH; break; } } } } } static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term) { int len, enc_len, ret = 0; struct dhcp_opt *start; unsigned char *p; /* find size in advance */ for (enc_len = 0, start = opt; opt; opt = opt->next) if (opt->flags & flag) { int new = do_opt(opt, NULL, NULL, null_term) + 2; ret = 1; if (enc_len + new <= 255) enc_len += new; else { p = free_space(mess, end, encap, enc_len); for (; start && start != opt; start = start->next) if (p && (start->flags & flag)) { len = do_opt(start, p + 2, NULL, null_term); *(p++) = start->opt; *(p++) = len; p += len; } enc_len = new; start = opt; } } if (enc_len != 0 && (p = free_space(mess, end, encap, enc_len + 1))) { for (; start; start = start->next) if (start->flags & flag) { len = do_opt(start, p + 2, NULL, null_term); *(p++) = start->opt; *(p++) = len; p += len; } *p = OPTION_END; } return ret; } static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid, const char *pxevendor) { unsigned char *p; if (!pxevendor) pxevendor="PXEClient"; option_put_string(mess, end, OPTION_VENDOR_ID, pxevendor, 0); if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17))) memcpy(p, uuid, 17); } static int prune_vendor_opts(struct dhcp_netid *netid) { int force = 0; struct dhcp_opt *opt; /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */ for (opt = daemon->dhcp_opts; opt; opt = opt->next) if (opt->flags & DHOPT_VENDOR_MATCH) { if (!match_netid(opt->netid, netid, 1)) opt->flags &= ~DHOPT_VENDOR_MATCH; else if (opt->flags & DHOPT_FORCE) force = 1; } return force; } /* Many UEFI PXE implementations have badly broken menu code. If there's exactly one relevant menu item, we abandon the menu system, and jamb the data direct into the DHCP file, siaddr and sname fields. Note that in this case, we have to assume that layer zero would be requested by the client PXE stack. */ static int pxe_uefi_workaround(int pxe_arch, struct dhcp_netid *netid, struct dhcp_packet *mess, struct in_addr local, time_t now, int pxe) { struct pxe_service *service, *found; /* Only workaround UEFI archs. */ if (pxe_arch < 6) return 0; for (found = NULL, service = daemon->pxe_services; service; service = service->next) if (pxe_arch == service->CSA && service->basename && match_netid(service->netid, netid, 1)) { if (found) return 0; /* More than one relevant menu item */ found = service; } if (!found) return 0; /* No relevant menu items. */ if (!pxe) return 1; if (found->sname) { mess->siaddr = a_record_from_hosts(found->sname, now); snprintf((char *)mess->sname, sizeof(mess->sname), "%s", found->sname); } else { if (found->server.s_addr != 0) mess->siaddr = found->server; else mess->siaddr = local; inet_ntop(AF_INET, &mess->siaddr, (char *)mess->sname, INET_ADDRSTRLEN); } if (found->basename) snprintf((char *)mess->file, sizeof(mess->file), strchr(found->basename, '.') ? "%s" : "%s.0", found->basename); return 1; } static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now) { #define NUM_OPTS 4 unsigned char *p, *q; struct pxe_service *service; static struct dhcp_opt *o, *ret; int i, j = NUM_OPTS - 1; struct in_addr boot_server; /* We pass back references to these, hence they are declared static */ static unsigned char discovery_control; static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' }; static struct dhcp_opt *fake_opts = NULL; /* Disable multicast, since we don't support it, and broadcast unless we need it */ discovery_control = 3; ret = daemon->dhcp_opts; if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt)))) return ret; for (i = 0; i < NUM_OPTS; i++) { fake_opts[i].flags = DHOPT_VENDOR_MATCH; fake_opts[i].netid = NULL; fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1]; } /* create the data for the PXE_MENU and PXE_SERVERS options. */ p = (unsigned char *)daemon->dhcp_buff; q = (unsigned char *)daemon->dhcp_buff3; for (i = 0, service = daemon->pxe_services; service; service = service->next) if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1)) { size_t len = strlen(service->menu); /* opt 43 max size is 255. encapsulated option has type and length bytes, so its max size is 253. */ if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253) { *(p++) = service->type >> 8; *(p++) = service->type; *(p++) = len; memcpy(p, service->menu, len); p += len; i++; } else { toobig: my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large")); return daemon->dhcp_opts; } 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) goto toobig; /* Boot service with known address - give it */ *(q++) = service->type >> 8; *(q++) = service->type; *(q++) = 1; /* dest misaligned */ memcpy(q, &boot_server.s_addr, INADDRSZ); q += INADDRSZ; } else if (service->type != 0) /* We don't know the server for a service type, so we'll allow the client to broadcast for it */ discovery_control = 2; } /* if no prompt, wait forever if there's a choice */ fake_prompt[0] = (i > 1) ? 255 : 0; if (i == 0) discovery_control = 8; /* no menu - just use use mess->filename */ else { ret = &fake_opts[j--]; ret->len = p - (unsigned char *)daemon->dhcp_buff; ret->val = (unsigned char *)daemon->dhcp_buff; ret->opt = SUBOPT_PXE_MENU; if (q - (unsigned char *)daemon->dhcp_buff3 != 0) { ret = &fake_opts[j--]; ret->len = q - (unsigned char *)daemon->dhcp_buff3; ret->val = (unsigned char *)daemon->dhcp_buff3; ret->opt = SUBOPT_PXE_SERVERS; } } for (o = daemon->dhcp_opts; o; o = o->next) if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT) break; if (!o) { ret = &fake_opts[j--]; ret->len = sizeof(fake_prompt); ret->val = fake_prompt; ret->opt = SUBOPT_PXE_MENU_PROMPT; } ret = &fake_opts[j--]; ret->len = 1; ret->opt = SUBOPT_PXE_DISCOVERY; ret->val= &discovery_control; return ret; } static void clear_packet(struct dhcp_packet *mess, unsigned char *end) { memset(mess->sname, 0, sizeof(mess->sname)); memset(mess->file, 0, sizeof(mess->file)); memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32))); mess->siaddr.s_addr = 0; } struct dhcp_boot *find_boot(struct dhcp_netid *netid) { struct dhcp_boot *boot; /* decide which dhcp-boot option we're using */ for (boot = daemon->boot_config; boot; boot = boot->next) if (match_netid(boot->netid, netid, 0)) break; if (!boot) /* No match, look for one without a netid */ for (boot = daemon->boot_config; boot; boot = boot->next) if (match_netid(boot->netid, netid, 1)) break; return boot; } static int is_pxe_client(struct dhcp_packet *mess, size_t sz, const char **pxe_vendor) { const unsigned char *opt = NULL; ssize_t conf_len = 0; const struct dhcp_pxe_vendor *conf = daemon->dhcp_pxe_vendors; opt = option_find(mess, sz, OPTION_VENDOR_ID, 0); if (!opt) return 0; for (; conf; conf = conf->next) { conf_len = strlen(conf->data); if (option_len(opt) < conf_len) continue; if (strncmp(option_ptr(opt, 0), conf->data, conf_len) == 0) { if (pxe_vendor) *pxe_vendor = conf->data; return 1; } } return 0; } static void do_options(struct dhcp_context *context, struct dhcp_packet *mess, unsigned char *end, unsigned char *req_options, char *hostname, 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, time_t now, unsigned int lease_time, unsigned short fuzz, const char *pxevendor) { struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts; struct dhcp_boot *boot; unsigned char *p; int i, len, force_encap = 0; 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; /* 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, pxe_arch != -1); /* 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(AF_INET, req_options[i], NULL, 0, NULL, 0); q += snprintf(q, MAXDNAME - (q - daemon->namebuff), "%d%s%s%s", req_options[i], strlen(s) != 0 ? ":" : "", s, req_options[i+1] == OPTION_END ? "" : ", "); if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40) { q = daemon->namebuff; my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff); } } } 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; /* See if we can send the boot stuff as options. To do this we need a requested option list, BOOTP and very old DHCP clients won't have this, we also provide a 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(tagif))) { if (boot->sname) { if (!option_bool(OPT_NO_OVERRIDE) && req_options && in_list(req_options, OPTION_SNAME)) option_put_string(mess, end, OPTION_SNAME, boot->sname, 1); else safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)); } if (boot->file) { if (!option_bool(OPT_NO_OVERRIDE) && req_options && in_list(req_options, OPTION_FILENAME)) option_put_string(mess, end, OPTION_FILENAME, boot->file, 1); else safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file)); } 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 they're not explicitly asked for as options. OPTION_END is used as an internal way to specify siaddr without using dhcp-boot, for use in dhcp-optsfile. */ { if ((!req_options || !in_list(req_options, OPTION_FILENAME)) && (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE)) { safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)); done_file = 1; } if ((!req_options || !in_list(req_options, OPTION_SNAME)) && (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE)) { safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)); done_server = 1; } if ((opt = option_find2(OPTION_END))) mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr; } /* We don't want to do option-overload for BOOTP, so make the file and sname fields look like they are in use, even when they aren't. This gets restored at the end of this function. */ if (!req_options || option_bool(OPT_NO_OVERRIDE)) { f0 = mess->file[0]; mess->file[0] = 1; s0 = mess->sname[0]; mess->sname[0] = 1; } /* At this point, if mess->sname or mess->file are zeroed, they are available for option overload, reserve space for the overload option. */ if (mess->file[0] == 0 || mess->sname[0] == 0) end -= 3; /* 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(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(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(OPTION_ROUTER)) option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr)); 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(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, */ if (hostname) { if (in_list(req_options, OPTION_HOSTNAME) && !option_find2(OPTION_HOSTNAME)) option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term); if (fqdn_flags != 0) { len = strlen(hostname) + 3; if (fqdn_flags & 0x04) len += 2; else if (null_term) len++; 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 & 0x0f; /* MBZ bits to zero */ *(p++) = 255; *(p++) = 255; if (fqdn_flags & 0x04) { p = do_rfc1035_name(p, hostname, NULL); if (domain) { p = do_rfc1035_name(p, domain, NULL); *p++ = 0; } } else { memcpy(p, hostname, strlen(hostname)); p += strlen(hostname); if (domain) { *(p++) = '.'; memcpy(p, domain, strlen(domain)); p += strlen(domain); } if (null_term) *(p++) = 0; } } } } for (opt = config_opts; opt; opt = opt->next) { 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. T1 and T2 already handled. */ if (optno == OPTION_CLIENT_FQDN || optno == OPTION_MAXMESSAGE || optno == OPTION_OVERLOAD || optno == OPTION_PAD || optno == OPTION_END || optno == OPTION_T1 || optno == OPTION_T2) continue; if (optno == OPTION_SNAME && done_server) continue; if (optno == OPTION_FILENAME && done_file) continue; /* For the options we have default values on dhc-option= means "don't include this option" not "include a zero-length option" */ if (opt->len == 0 && (optno == OPTION_NETMASK || optno == OPTION_BROADCAST || optno == OPTION_ROUTER || optno == OPTION_DNSSERVER || optno == OPTION_DOMAINNAME || optno == OPTION_HOSTNAME)) continue; /* vendor-class comes from elsewhere for PXE */ if (pxe_arch != -1 && optno == OPTION_VENDOR_ID) continue; /* always force null-term for filename and servername - buggy PXE again. */ len = do_opt(opt, NULL, context, (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term); if ((p = free_space(mess, end, optno, len))) { do_opt(opt, p, context, (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term); /* If we send a vendor-id, revisit which vendor-ops we consider it appropriate to send. */ if (optno == OPTION_VENDOR_ID) { match_vendor_opts(p - 2, config_opts); done_vendor_class = 1; } } } /* encapsulated options. */ handle_encap(mess, end, req_options, null_term, tagif, pxe_arch != 1); force_encap = prune_vendor_opts(tagif); if (context && pxe_arch != -1) { pxe_misc(mess, end, uuid, pxevendor); if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 0)) config_opts = pxe_opts(pxe_arch, tagif, context->local, now); } if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) && do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) && pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 && (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len))) /* If we send vendor encapsulated options, and haven't already sent option 60, echo back the value we got from the client. */ memcpy(p, daemon->dhcp_buff3, vendor_class_len); /* restore BOOTP anti-overload hack */ if (!req_options || option_bool(OPT_NO_OVERRIDE)) { mess->file[0] = f0; mess->sname[0] = s0; } } static void handle_encap(struct dhcp_packet *mess, unsigned char *end, unsigned char *req_options, int null_term, struct dhcp_netid *tagif, int pxemode) { /* Send options to be encapsulated in arbitrary options, eg dhcp-option=encap:172,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. */ struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts; unsigned char *p; int len; for (opt = config_opts; opt; opt = opt->next) opt->flags &= ~DHOPT_ENCAP_DONE; for (opt = config_opts; opt; opt = opt->next) { int flags; if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925)))) { int found = 0; struct dhcp_opt *o; if (opt->flags & DHOPT_ENCAP_DONE) continue; for (len = 0, o = config_opts; o; o = o->next) { int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT; o->flags &= ~DHOPT_ENCAP_MATCH; if (!(o->flags & flags) || opt->u.encap != o->u.encap) continue; o->flags |= DHOPT_ENCAP_DONE; if (match_netid(o->netid, tagif, 1) && pxe_ok(o, pxemode) && ((o->flags & DHOPT_FORCE) || in_list(req_options, outer))) { o->flags |= DHOPT_ENCAP_MATCH; found = 1; len += do_opt(o, NULL, NULL, 0) + 2; } } if (found) { if (flags & DHOPT_ENCAPSULATE) do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term); else if (len > 250) my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap); else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5))) { int swap_ent = htonl(opt->u.encap); memcpy(p, &swap_ent, 4); p += 4; *(p++) = len; for (o = config_opts; o; o = o->next) if (o->flags & DHOPT_ENCAP_MATCH) { len = do_opt(o, p + 2, NULL, 0); *(p++) = o->opt; *(p++) = len; p += len; } } } } } } static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid) { struct delay_config *delay_conf; /* Decide which delay_config option we're using */ for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next) if (match_netid(delay_conf->netid, netid, 0)) break; if (!delay_conf) /* No match, look for one without a netid */ for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next) if (match_netid(delay_conf->netid, netid, 1)) break; if (delay_conf) { if (!option_bool(OPT_QUIET_DHCP)) my_syslog(MS_DHCP | LOG_INFO, _("%u reply delay: %d"), ntohl(xid), delay_conf->delay); delay_dhcp(recvtime, delay_conf->delay, -1, 0, 0); } } #endif /* HAVE_DHCP */ dnsmasq-2.91/src/dnssec.c0000664000175000017500000020114214765043257013524 0ustar srksrk/* dnssec.c is Copyright (c) 2012 Giovanni Bajo and Copyright (c) 2012-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_DNSSEC #define SERIAL_UNDEF -100 #define SERIAL_EQ 0 #define SERIAL_LT -1 #define SERIAL_GT 1 /* Input in presentation format */ static int count_labels(char *name) { int i; char *p; if (*name == 0) return 0; for (p = name, i = 0; *p; p++) if (*p == '.') i++; /* Don't count empty first label. */ return *name == '.' ? i : i+1; } /* Implement RFC1982 wrapped compare for 32-bit numbers */ static int serial_compare_32(u32 s1, u32 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 (utimes(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 timeval tv[2]; close(fd); timestamp_time = 1420070400; /* 1-1-2015 */ tv[0].tv_sec = tv[1].tv_sec = timestamp_time; tv[0].tv_usec = tv[1].tv_usec = 0; if (utimes(daemon->timestamp_file, tv) == 0) goto check_and_exit; } } return -1; } /* Check whether today/now is between date_start and date_end */ static int is_check_date(unsigned long curtime) { /* 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 (utimes(daemon->timestamp_file, NULL) != 0) my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno)); my_syslog(LOG_INFO, _("system time considered valid, now checking DNSSEC signature timestamps.")); daemon->back_to_the_future = 1; daemon->dnssec_no_time_check = 0; queue_event(EVENT_RELOAD); /* purge cache */ } return daemon->back_to_the_future; } else return !daemon->dnssec_no_time_check; } /* Return bytes of canonicalised rrdata one by one. Init state->ip with the RR, and state->end with the end of same. Init state->op to NULL. Init state->desc to RR descriptor. Init state->buff with a MAXDNAME * 2 buffer. After each call which returns 1, state->op points to the next byte of data. On returning 0, the end has been reached. */ struct rdata_state { short *desc; size_t c; unsigned char *end, *ip, *op; char *buff; }; static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state *state) { int d; if (state->op && state->c != 1) { state->op++; state->c--; return 1; } while (1) { d = *(state->desc); if (d == -1) { /* all the bytes to the end. */ if ((state->c = state->end - state->ip) != 0) { state->op = state->ip; state->ip = state->end;; } else return 0; } else { state->desc++; if (d == (u16)0) { /* domain-name, canonicalise */ int len; if (!extract_name(header, plen, &state->ip, state->buff, EXTR_NAME_EXTRACT, 0) || (len = to_wire(state->buff)) == 0) continue; state->c = len; state->op = (unsigned char *)state->buff; } else { /* plain data preceding a domain-name, don't run off the end of the data */ if ((state->end - state->ip) < d) d = state->end - state->ip; if (d == 0) continue; state->op = state->ip; state->c = d; state->ip += d; } } return 1; } } /* Bubble sort the RRset into the canonical order. */ static int sort_rrset(struct dns_header *header, size_t plen, short *rr_desc, int rrsetidx, unsigned char **rrset, char *buff1, char *buff2) { int swap, i, j; do { for (swap = 0, i = 0; i < rrsetidx-1; i++) { int rdlen1, rdlen2; struct rdata_state state1, state2; /* Note that these have been determined to be OK previously, so we don't need to check for NULL return here. */ state1.ip = skip_name(rrset[i], header, plen, 10); state2.ip = skip_name(rrset[i+1], header, plen, 10); state1.op = state2.op = NULL; state1.buff = buff1; state2.buff = buff2; state1.desc = state2.desc = rr_desc; state1.ip += 8; /* skip class, type, ttl */ GETSHORT(rdlen1, state1.ip); if (!CHECK_LEN(header, state1.ip, plen, rdlen1)) return rrsetidx; /* short packet */ state1.end = state1.ip + rdlen1; state2.ip += 8; /* skip class, type, ttl */ GETSHORT(rdlen2, state2.ip); if (!CHECK_LEN(header, state2.ip, plen, rdlen2)) return rrsetidx; /* short packet */ state2.end = state2.ip + rdlen2; /* If the RR has no names in it then canonicalisation is the identity function and we can compare the RRs directly. If not we compare the canonicalised RRs one byte at a time. */ if (*rr_desc == -1) { int rdmin = rdlen1 > rdlen2 ? rdlen2 : rdlen1; int cmp = memcmp(state1.ip, state2.ip, rdmin); if (cmp > 0 || (cmp == 0 && rdlen1 > rdmin)) { unsigned char *tmp = rrset[i+1]; rrset[i+1] = rrset[i]; rrset[i] = tmp; swap = 1; } else if (cmp == 0 && (rdlen1 == rdlen2)) { /* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */ for (j = i+1; j < rrsetidx-1; j++) rrset[j] = rrset[j+1]; rrsetidx--; i--; } } else /* Comparing canonicalised RRs, byte-at-a-time. */ while (1) { int ok1, ok2; ok1 = get_rdata(header, plen, &state1); ok2 = get_rdata(header, plen, &state2); if (!ok1 && !ok2) { /* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */ for (j = i+1; j < rrsetidx-1; j++) rrset[j] = rrset[j+1]; rrsetidx--; i--; break; } else if (ok1 && (!ok2 || *state1.op > *state2.op)) { unsigned char *tmp = rrset[i+1]; rrset[i+1] = rrset[i]; rrset[i] = tmp; swap = 1; break; } else if (ok2 && (!ok1 || *state2.op > *state1.op)) break; /* arrive here when bytes are equal, go round the loop again and compare the next ones. */ } } } while (swap); return rrsetidx; } static unsigned char **rrset = NULL, **sigs = NULL; /* Get pointers to RRset members and signature(s) for same. Check signatures, and return keyname associated in keyname. */ static int explore_rrset(struct dns_header *header, size_t plen, int class, int type, char *name, char *keyname, int *sigcnt, int *rrcnt) { static int rrset_sz = 0, sig_sz = 0; unsigned char *p; int rrsetidx, sigidx, j, rdlen, res; int gotkey = 0; if (!(p = skip_questions(header, plen))) return 0; /* 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, type_covered; pstart = p; if (!(res = extract_name(header, plen, &p, name, EXTR_NAME_COMPARE, 10))) return 0; /* bad packet */ GETSHORT(stype, p); GETSHORT(sclass, p); pdata = p; p += 4; /* TTL */ GETSHORT(rdlen, p); if (!CHECK_LEN(header, p, plen, rdlen)) return 0; if (res == 1 && sclass == class) { if (stype == type) { if (!expand_workspace(&rrset, &rrset_sz, rrsetidx)) return 0; rrset[rrsetidx++] = pstart; } if (stype == T_RRSIG) { if (rdlen < 18) return 0; /* bad packet */ GETSHORT(type_covered, p); p += 16; /* algo, labels, orig_ttl, sig_expiration, sig_inception, key_tag */ if (gotkey) { /* If there's more than one SIG, ensure they all have same keyname */ if (extract_name(header, plen, &p, keyname, EXTR_NAME_COMPARE, 0) != 1) return 0; } else { gotkey = 1; if (!extract_name(header, plen, &p, keyname, EXTR_NAME_EXTRACT, 0)) return 0; /* RFC 4035 5.3.1 says that the Signer's Name field MUST equal 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) { char *name_start; 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 return 0; } } if (type_covered == type) { if (!expand_workspace(&sigs, &sig_sz, sigidx)) return 0; sigs[sigidx++] = pdata; } p = pdata + 6; /* restore for ADD_RDLEN */ } } if (!ADD_RDLEN(header, p, plen, rdlen)) return 0; } *sigcnt = sigidx; *rrcnt = rrsetidx; return 1; } int dec_counter(int *counter, char *message) { if ((*counter)-- == 0) { my_syslog(LOG_WARNING, "limit exceeded: %s", message ? message : _("per-query crypto work")); return 1; } return 0; } /* 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_BOGUS signature is wrong, bad packet. STAT_ABANDONED validation abandoned do to excess resource usage. STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname) STAT_NEED_DS need DS 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. Call explore_rrset first to find and count RRs and sigs. ttl_out is the floor on TTL, based on TTL and orig_ttl and expiration of sig used to validate. */ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, int type, int sigidx, int rrsetidx, char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen, int algo_in, int keytag_in, unsigned long *ttl_out, int *validate_counter) { unsigned char *p; int rdlen, j, name_labels, algo, labels, key_tag, sig_fail_cnt; struct crec *crecp = NULL; short *rr_desc = rrfilter_desc(type); u32 sig_expiration, sig_inception; int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP; unsigned long curtime = time(0); int time_check = is_check_date(curtime); if (wildcard_out) *wildcard_out = NULL; name_labels = count_labels(name); /* For 4035 5.3.2 check */ /* 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. */ rrsetidx = sort_rrset(header, plen, rr_desc, rrsetidx, rrset, daemon->workspacename, keyname); /* Now try all the sigs to try and find one which validates */ for (sig_fail_cnt = daemon->limit[LIMIT_SIG_FAIL], j = 0; j = 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, EXTR_NAME_EXTRACT, 0)) return STAT_BOGUS; if (!time_check) failflags &= ~(DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP); else { /* We must explicitly check against wanted values, because of SERIAL_UNDEF */ if (serial_compare_32(curtime, sig_inception) == SERIAL_LT) continue; else failflags &= ~DNSSEC_FAIL_NYV; if (serial_compare_32(curtime, sig_expiration) == SERIAL_GT) continue; else failflags &= ~DNSSEC_FAIL_EXP; } if (!(hash = hash_find(algo_digest_name(algo)))) continue; else failflags &= ~DNSSEC_FAIL_NOKEYSUP; if (labels > name_labels || !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; if (ttl_out) { /* 4035 5.3.3 rules on TTLs */ if (orig_ttl < ttl) ttl = orig_ttl; if (time_check && difftime(sig_expiration, curtime) < ttl) ttl = difftime(sig_expiration, curtime); *ttl_out = ttl; } 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); #define RRBUFLEN 128 /* Most RRs are smaller than this. */ for (i = 0; i < rrsetidx; ++i) { int j; struct rdata_state state; u16 len; unsigned char rrbuf[RRBUFLEN]; p = rrset[i]; if (!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 10)) return STAT_BOGUS; name_start = name; /* if more labels than in RRsig name, hash *. 4035 5.3.2 */ if (labels < name_labels) { for (j = name_labels - labels; j != 0; j--) { while (*name_start != '.' && *name_start != 0) name_start++; if (j != 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 type, class, ttl */ GETSHORT(rdlen, p); if (!CHECK_LEN(header, p, plen, rdlen)) return STAT_BOGUS; /* Optimisation for RR types which need no cannonicalisation. This includes DNSKEY DS NSEC and NSEC3, which are also long, so it saves lots of calls to get_rdata, and avoids the pessimal segmented insertion, even with a small rrbuf[]. If canonicalisation is not needed, a simple insertion into the hash works. */ if (*rr_desc == -1) { len = htons(rdlen); hash->update(ctx, 2, (unsigned char *)&len); hash->update(ctx, rdlen, p); } else { /* canonicalise rdata and calculate length of same, use name buffer as workspace for get_rdata. */ state.ip = p; state.op = NULL; state.desc = rr_desc; state.buff = name; state.end = p + rdlen; for (j = 0; get_rdata(header, plen, &state); j++) if (j < RRBUFLEN) rrbuf[j] = *state.op; len = htons((u16)j); hash->update(ctx, 2, (unsigned char *)&len); /* If the RR is shorter than RRBUFLEN (most of them, in practice) then we can just digest it now. If it exceeds RRBUFLEN we have to go back to the start and do it in chunks. */ if (j >= RRBUFLEN) { state.ip = p; state.op = NULL; state.desc = rr_desc; for (j = 0; get_rdata(header, plen, &state); j++) { rrbuf[j] = *state.op; if (j == RRBUFLEN - 1) { hash->update(ctx, RRBUFLEN, rrbuf); j = -1; } } } if (j != 0) hash->update(ctx, j, rrbuf); } } hash->digest(ctx, hash->digest_size, digest); /* namebuff used for workspace above, restore to leave unchanged on exit */ p = (unsigned char*)(rrset[0]); if (!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 0)) return STAT_BOGUS; if (key) { if (algo_in == algo && keytag_in == key_tag) { if (dec_counter(validate_counter, NULL)) return STAT_ABANDONED; if (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) { if (dec_counter(validate_counter, NULL)) return STAT_ABANDONED; if (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; /* An attacker can waste a lot of our CPU by setting up a giant DNSKEY RRSET full of failing keys, all of which we have to try. Since many failing keys is not likely for a legitimate domain, set a limit on how many can fail. */ if ((daemon->limit[LIMIT_SIG_FAIL] - (sig_fail_cnt + 1)) > (int)daemon->metrics[METRIC_SIG_FAIL_HWM]) daemon->metrics[METRIC_SIG_FAIL_HWM] = daemon->limit[LIMIT_SIG_FAIL] - (sig_fail_cnt + 1); if (dec_counter(&sig_fail_cnt, _("per-RRSet signature fails"))) return STAT_ABANDONED; } } } /* If we reach this point, no verifying key was found */ return STAT_BOGUS | failflags | DNSSEC_FAIL_NOKEY; } /* 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_OK Done, key(s) 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_ABANDONED resource exhaustion. 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, int *validate_counter) { unsigned char *psave, *p = (unsigned char *)(header+1), *keyaddr; struct crec *crecp, *recp1; int rc, j, qtype, qclass, rdlen, flags, algo, keytag, sigcnt, rrcnt; unsigned long ttl, sig_ttl; union all_addr a; int failflags = DNSSEC_FAIL_NODSSUP | DNSSEC_FAIL_NOZONE; char valid_digest[255]; static unsigned char **cached_digest; static size_t cached_digest_size = 0; if (ntohs(header->qdcount) != 1 || RCODE(header) != NOERROR || !extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 4)) return STAT_BOGUS | DNSSEC_FAIL_NOKEY; GETSHORT(qtype, p); GETSHORT(qclass, p); if (qtype != T_DNSKEY || qclass != class || !explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) || rrcnt == 0) return STAT_BOGUS | DNSSEC_FAIL_NOKEY; if (sigcnt == 0) return STAT_BOGUS | DNSSEC_FAIL_NOSIG; /* 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; } /* NOTE, we need to find ONE DNSKEY which matches the DS */ for (j = ntohs(header->ancount); j != 0; j--) { /* Ensure we have type, class TTL and length */ if (!(rc = extract_name(header, plen, &p, name, EXTR_NAME_COMPARE, 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)) return STAT_BOGUS; /* bad packet */ if (qclass != class || qtype != T_DNSKEY || rc == 2) { p += rdlen; continue; } if (rdlen < 5) return STAT_BOGUS; /* min 1 byte key! */ psave = p; GETSHORT(flags, p); if (*p++ != 3) { p = psave + rdlen; continue; } algo = *p++; keyaddr = p; keytag = dnskey_keytag(algo, flags, keyaddr, rdlen - 4); p = psave + rdlen; /* key must have zone key flag set */ if (!(flags & 0x100)) continue; failflags &= ~DNSSEC_FAIL_NOZONE; /* clear digest cache. */ memset(valid_digest, 0, sizeof(valid_digest)); 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; int wire_len; if ((recp1->flags & F_NEG) || recp1->addr.ds.algo != algo || recp1->addr.ds.keytag != keytag || recp1->uid != (unsigned int)class) continue; if (!(hash = hash_find(ds_digest_name(recp1->addr.ds.digest)))) continue; failflags &= ~DNSSEC_FAIL_NODSSUP; if (recp1->addr.ds.keylen != (int)hash->digest_size || !(ds_digest = blockdata_retrieve(recp1->addr.ds.keydata, recp1->addr.ds.keylen, NULL))) continue; if (valid_digest[recp1->addr.ds.digest]) digest = cached_digest[recp1->addr.ds.digest]; else { /* computing a hash is a unit of crypto work. */ if (dec_counter(validate_counter, NULL)) return STAT_ABANDONED; if (!hash_init(hash, &ctx, &digest)) continue; wire_len = to_wire(name); /* Note that digest may be different between DSs, so we can't move this outside the loop. We keep copies of each digest we make for this key, so maximum digest work is O(keys x digests_types) rather then O(keys x DSs) */ 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.digest >= cached_digest_size) { unsigned char **new; /* whine_malloc zeros memory */ if ((new = whine_malloc((recp1->addr.ds.digest + 5) * sizeof(unsigned char *)))) { if (cached_digest_size != 0) { memcpy(new, cached_digest, cached_digest_size * sizeof(unsigned char *)); free(cached_digest); } cached_digest_size = recp1->addr.ds.digest + 5; cached_digest = new; } } if (recp1->addr.ds.digest < cached_digest_size) { if (!cached_digest[recp1->addr.ds.digest]) cached_digest[recp1->addr.ds.digest] = whine_malloc(recp1->addr.ds.keylen); if (cached_digest[recp1->addr.ds.digest]) { memcpy(cached_digest[recp1->addr.ds.digest], digest, recp1->addr.ds.keylen); valid_digest[recp1->addr.ds.digest] = 1; } } } if (memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0) { /* Found the key validated by a DS record. Now check the self-sig for the entire key RRset using that key. Note that validate_rrset() will never return STAT_NEED_KEY here, since we supply the key it will use as an argument. */ struct blockdata *key; if (!(key = blockdata_alloc((char *)keyaddr, rdlen - 4))) break; rc = validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname, NULL, key, rdlen - 4, algo, keytag, &sig_ttl, validate_counter); blockdata_free(key); if (STAT_ISEQUAL(rc, STAT_ABANDONED)) return rc; /* can't validate KEY RRset with this key, see if there's another that will, which is validated by another DS. */ if (!STAT_ISEQUAL(rc, STAT_SECURE)) break; /* DNSKEY RRset determined to be OK, now cache 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, EXTR_NAME_COMPARE, 10))) return STAT_BOGUS; /* bad packet */ GETSHORT(qtype, p); GETSHORT(qclass, p); GETLONG(ttl, p); GETSHORT(rdlen, p); /* TTL may be limited by sig. */ if (sig_ttl < ttl) ttl = sig_ttl; if (!CHECK_LEN(header, p, plen, rdlen)) return STAT_BOGUS; /* bad packet */ psave = p; if (qclass == class && rc == 1 && qtype == T_DNSKEY) { if (rdlen < 4) return STAT_BOGUS; /* min 1 byte key! */ GETSHORT(flags, p); if (*p++ == 3) { algo = *p++; keytag = dnskey_keytag(algo, flags, p, rdlen - 4); if (!(key = blockdata_alloc((char*)p, rdlen - 4))) return STAT_BOGUS; a.key.keylen = rdlen - 4; a.key.keydata = key; a.key.algo = algo; a.key.keytag = keytag; a.key.flags = flags; if (!cache_insert(name, &a, class, now, ttl, F_FORWARD | F_DNSKEY | F_DNSSECOK)) { /* cache_insert fails when the cache is too small, so error with STAT_ABANDONED which will log this as a resource exhaustion problem, which it is. */ blockdata_free(key); return STAT_ABANDONED; } a.log.keytag = keytag; a.log.algo = algo; if (algo_digest_name(algo)) log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu", 0); else log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)", 0); } } p = psave + rdlen; } /* commit cache insert. */ cache_end_insert(); return STAT_OK; } } } log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY", 0); return STAT_BOGUS | failflags; } /* The DNS packet is expected to contain the answer to a DS query Put all DSs in the answer which are valid and have hash and signature algos we support into the cache. Also handles replies which prove that there's no DS at this location, either because the zone is unsigned or this isn't a zone cut. These are cached too. If none of the DS's are for supported algos, treat the answer as if it's a proof of no DS at this location. RFC4035 para 5.2. return codes: STAT_OK At least one valid DS found and in cache. 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 STAT_NEED_DS DS record needed. STAT_ABANDONED resource exhaustion. */ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class, int *validate_counter) { unsigned char *p = (unsigned char *)(header+1); int qtype, qclass, rc, i, neganswer = 0, nons = 0, servfail = 0, neg_ttl = 0, found_supported = 0; int aclass, atype, rdlen, flags; unsigned long ttl; union all_addr a; 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) return STAT_BOGUS; /* A SERVFAIL answer has been seen to a DS query not at start of authority, so treat it as such and continue to search for a DS or proof of no existence further down the tree. */ if (RCODE(header) == SERVFAIL) servfail = neganswer = nons = 1; else { rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl, validate_counter); if (STAT_ISEQUAL(rc, STAT_INSECURE)) { my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name); log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0); return STAT_BOGUS | DNSSEC_FAIL_INDET; } p = (unsigned char *)(header+1); if (!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 4)) return STAT_BOGUS; p += 4; /* qtype, qclass */ /* 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 (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname)) { log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS", 0); return STAT_BOGUS; } if (!STAT_ISEQUAL(rc, STAT_SECURE)) return rc; } if (!neganswer) { cache_start_insert(); for (i = 0; i < ntohs(header->ancount); i++) { unsigned char *psave; if (!(rc = extract_name(header, plen, &p, name, EXTR_NAME_COMPARE, 10))) return STAT_BOGUS; /* bad packet */ GETSHORT(atype, p); GETSHORT(aclass, p); GETLONG(ttl, p); GETSHORT(rdlen, p); if (!CHECK_LEN(header, p, plen, rdlen)) return STAT_BOGUS; /* bad packet */ psave = p; if (aclass == class && atype == T_DS && rc == 1) { int algo, digest, keytag; struct blockdata *key; if (rdlen < 5) return STAT_BOGUS; /* min 1 byte digest! */ GETSHORT(keytag, p); algo = *p++; digest = *p++; if (!ds_digest_name(digest) || !algo_digest_name(algo)) { a.log.keytag = keytag; a.log.algo = algo; a.log.digest = digest; log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS for keytag %hu, algo %hu, digest %hu (not supported)", 0); neg_ttl = ttl; } else if ((key = blockdata_alloc((char*)p, rdlen - 4))) { a.ds.digest = digest; a.ds.keydata = key; a.ds.algo = algo; a.ds.keytag = keytag; a.ds.keylen = rdlen - 4; if (!cache_insert(name, &a, class, now, ttl, F_FORWARD | F_DS | F_DNSSECOK)) { /* cache_insert fails when the cache is too small, so error with STAT_ABANDONED which will log this as a resource exhaustion problem, which it is. */ blockdata_free(key); return STAT_ABANDONED; } else { a.log.keytag = keytag; a.log.algo = algo; a.log.digest = digest; log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS for keytag %hu, algo %hu, digest %hu", 0); found_supported = 1; } } } p = psave + rdlen; } cache_end_insert(); /* Fall through if no supported algo DS found. */ if (found_supported) return STAT_OK; } flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK; if (neganswer) { 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; } cache_start_insert(); /* Use TTL from NSEC for negative cache entries */ if (!cache_insert(name, NULL, class, now, neg_ttl, flags)) return STAT_ABANDONED; cache_end_insert(); if (neganswer) log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, servfail ? "SERVFAIL" : (nons ? "no DS/cut" : "no DS"), 0); return STAT_OK; } /* 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; } } /* returns 0 on success, or DNSSEC_FAIL_* value on failure. */ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, unsigned char **labels, int nsec_count, char *workspace1_in, 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 = 1; /* Find NSEC record that proves name doesn't exist */ for (i = 0; i < nsec_count; i++) { char *workspace1 = workspace1_in; int sig_labels, name_labels; p = nsecs[i]; if (!extract_name(header, plen, &p, workspace1, EXTR_NAME_EXTRACT, 10)) return DNSSEC_FAIL_BADPACKET; p += 8; /* class, type, TTL */ GETSHORT(rdlen, p); psave = p; if (!extract_name(header, plen, &p, workspace2, EXTR_NAME_EXTRACT, 0)) return DNSSEC_FAIL_BADPACKET; /* If NSEC comes from wildcard expansion, use original wildcard as name for computation. */ sig_labels = *labels[i]; name_labels = count_labels(workspace1); if (sig_labels < name_labels) { int k; for (k = name_labels - sig_labels; k != 0; k--) { while (*workspace1 != '.' && *workspace1 != 0) workspace1++; if (k != 1 && *workspace1 == '.') workspace1++; } workspace1--; *workspace1 = '*'; } rc = hostname_cmp(workspace1, name); if (rc == 0) { /* 4035 para 5.4. Last sentence */ if (type == T_NSEC || type == T_RRSIG) return 0; /* 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 packet checked to be as long as rdlen implies in prove_non_existence() */ /* 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 = 0; if (rdlen >= 2 && p[0] == 0) { /* A CNAME answer would also be valid, so if there's a CNAME is should have been returned. */ if ((p[2] & (0x80 >> T_CNAME)) != 0) return DNSSEC_FAIL_NONSEC; /* If the SOA bit is set for a DS record, then we have the DS from the wrong side of the delegation. For the root DS, this is expected. */ if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0) return DNSSEC_FAIL_NONSEC; } while (rdlen >= 2) { if (!CHECK_LEN(header, p, plen, rdlen)) return DNSSEC_FAIL_BADPACKET; if (p[0] == type >> 8) { /* Does the NSEC say our type exists? */ if (offset < p[1] && (p[offset+2] & mask) != 0) return DNSSEC_FAIL_NONSEC; break; /* finished checking */ } rdlen -= p[1]; p += p[1]; } return 0; } 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 0; } else { /* wrap around case, name falls between start and next domain name */ if (hostname_cmp(workspace1, workspace2) >= 0 && hostname_cmp(workspace2, name) >=0 ) return 0; } } return DNSSEC_FAIL_NONSEC; } /* 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 name_labels) { int i, hash_len, salt_len, base32_len, rdlen, flags; unsigned char *p, *psave; for (i = 0; i < nsec_count; i++) if ((p = nsecs[i])) { if (!extract_name(header, plen, &p, workspace1, EXTR_NAME_EXTRACT, 10) || !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2))) return 0; p += 8; /* class, type, TTL */ GETSHORT(rdlen, p); psave = p; /* packet checked to be as long as implied by rdlen, salt_len and hash_len in prove_non_existence() */ p++; /* algo */ flags = *p++; /* flags */ p += 2; /* iterations */ salt_len = *p++; /* salt_len */ p += salt_len; /* salt */ hash_len = *p++; /* p now points to next hashed name */ 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. Note we have packet length up to rdlen bytes checked. */ int offset = (type & 0xff) >> 3; int mask = 0x80 >> (type & 0x07); p += hash_len; /* skip next-domain hash */ rdlen -= p - psave; if (rdlen >= 2 && p[0] == 0) { /* If we can prove that there's no NS record, return that information. */ if (nons && (p[2] & (0x80 >> T_NS)) != 0) *nons = 0; /* A CNAME answer would also be valid, so if there's a CNAME is should have been returned. */ if ((p[2] & (0x80 >> T_CNAME)) != 0) return 0; /* If the SOA bit is set for a DS record, then we have the DS from the wrong side of the delegation. For the root DS, this is expected. */ if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0) return 0; } 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 0; break; /* finished 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) { if ((flags & 0x01) && nons) /* opt out */ *nons = 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) { if ((flags & 0x01) && nons) /* opt out */ *nons = 0; return 1; } } } } return 0; } /* returns 0 on success, or DNSSEC_FAIL_* value on failure. */ 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, int *validate_counter) { 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 = 1; /* Look though the NSEC3 records to find the first one with an algorithm we support. 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 DNSSEC_FAIL_BADPACKET; /* bad packet */ p += 10; /* type, class, TTL, rdlen */ algo = *p++; if ((hash = hash_find(nsec3_digest_name(algo)))) break; /* known algo */ } /* No usable NSEC3s */ if (i == nsec_count) return DNSSEC_FAIL_NONSEC; p++; /* flags */ GETSHORT (iterations, p); /* Upper-bound iterations, to avoid DoS. RFC 9276 refers. */ if (iterations > daemon->limit[LIMIT_NSEC3_ITERS]) return DNSSEC_FAIL_NSEC3_ITERS; salt_len = *p++; salt = p; /* 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, flags; nsecs[i] = NULL; /* Speculative, will be restored if OK. */ if (!(p = skip_name(nsec3p, header, plen, 15))) return DNSSEC_FAIL_BADPACKET; /* bad packet */ p += 10; /* type, class, TTL, rdlen */ if (*p++ != algo) continue; flags = *p++; /* flags */ /* 5155 8.2 */ if (flags != 0 && flags != 1) continue; GETSHORT(this_iter, p); if (this_iter != iterations) continue; if (salt_len != *p++) continue; if (memcmp(p, salt, salt_len) != 0) continue; /* All match, put the pointer back */ nsecs[i] = nsec3p; } if (dec_counter(validate_counter, NULL)) return DNSSEC_FAIL_WORK; if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0) return DNSSEC_FAIL_NONSEC; if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, nons, count_labels(name))) return 0; /* 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 (dec_counter(validate_counter, NULL)) return DNSSEC_FAIL_WORK; if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0) return DNSSEC_FAIL_NONSEC; for (i = 0; i < nsec_count; i++) if ((p = nsecs[i])) { if (!extract_name(header, plen, &p, workspace1, EXTR_NAME_EXTRACT, 0)) return DNSSEC_FAIL_BADPACKET; if (!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2))) return DNSSEC_FAIL_NONSEC; 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 || !next_closest) return DNSSEC_FAIL_NONSEC; /* Look for NSEC3 that proves the non-existence of the next-closest encloser */ if (dec_counter(validate_counter, NULL)) return DNSSEC_FAIL_WORK; if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0) return DNSSEC_FAIL_NONSEC; if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL, 1)) return DNSSEC_FAIL_NONSEC; /* Finally, check that there's no seat of wildcard synthesis */ if (!wildname) { if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest) return DNSSEC_FAIL_NONSEC; wildcard--; *wildcard = '*'; if (dec_counter(validate_counter, NULL)) return DNSSEC_FAIL_WORK; if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0) return DNSSEC_FAIL_NONSEC; if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL, 1)) return DNSSEC_FAIL_NONSEC; } return 0; } /* returns 0 on success, or DNSSEC_FAIL_* value on failure. */ static int prove_non_existence(struct dns_header *header, size_t plen, char *keyname, char *name, int qtype, int qclass, char *wildname, int *nons, int *nsec_ttl, int *validate_counter) { static unsigned char **nsecset = NULL, **rrsig_labels = NULL; static int nsecset_sz = 0, rrsig_labels_sz = 0; int type_found = 0; unsigned char *auth_start, *p = skip_questions(header, plen); int type, class, rdlen, i, nsecs_found; unsigned long ttl; /* Move to NS section */ if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen))) return DNSSEC_FAIL_BADPACKET; auth_start = p; for (nsecs_found = 0, i = 0; i < ntohs(header->nscount); i++) { unsigned char *pstart = p; if (!extract_name(header, plen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 10)) return DNSSEC_FAIL_BADPACKET; GETSHORT(type, p); GETSHORT(class, p); GETLONG(ttl, p); GETSHORT(rdlen, p); if (!CHECK_LEN(header, p, plen, rdlen)) return DNSSEC_FAIL_BADPACKET; if (class == qclass && (type == T_NSEC || type == T_NSEC3)) { if (nsec_ttl) { /* Limit TTL with sig TTL */ if (daemon->rr_status[ntohs(header->ancount) + i] < ttl) ttl = daemon->rr_status[ntohs(header->ancount) + i]; *nsec_ttl = ttl; } /* No mixed NSECing 'round here, thankyouverymuch */ if (type_found != 0 && type_found != type) return DNSSEC_FAIL_NONSEC; type_found = type; if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found)) return DNSSEC_FAIL_BADPACKET; if (type == T_NSEC) { /* If we're looking for NSECs, find the corresponding SIGs, to extract the labels value, which we need in case the NSECs are the result of wildcard expansion. Note that the NSEC may not have been validated yet so if there are multiple SIGs, make sure the label value is the same in all, to avoid be duped by a rogue one. If there are no SIGs, that's an error */ unsigned char *p1 = auth_start; int res, j, rdlen1, type1, class1; if (!expand_workspace(&rrsig_labels, &rrsig_labels_sz, nsecs_found)) return DNSSEC_FAIL_BADPACKET; rrsig_labels[nsecs_found] = NULL; for (j = ntohs(header->nscount); j != 0; j--) { unsigned char *psav; if (!(res = extract_name(header, plen, &p1, daemon->workspacename, EXTR_NAME_COMPARE, 10))) return DNSSEC_FAIL_BADPACKET; GETSHORT(type1, p1); GETSHORT(class1, p1); p1 += 4; /* TTL */ GETSHORT(rdlen1, p1); psav = p1; if (!CHECK_LEN(header, p1, plen, rdlen1)) return DNSSEC_FAIL_BADPACKET; if (res == 1 && class1 == qclass && type1 == T_RRSIG) { int type_covered; if (rdlen1 < 18) return DNSSEC_FAIL_BADPACKET; /* bad packet */ GETSHORT(type_covered, p1); if (type_covered == T_NSEC) { p1++; /* algo */ /* labels field must be the same in every SIG we find. */ if (!rrsig_labels[nsecs_found]) rrsig_labels[nsecs_found] = p1; else if (*rrsig_labels[nsecs_found] != *p1) /* algo */ return DNSSEC_FAIL_NONSEC; } } p1 = psav + rdlen1; } /* Must have found at least one sig. */ if (!rrsig_labels[nsecs_found]) return DNSSEC_FAIL_NONSEC; } else if (type == T_NSEC3) { /* Decode the packet structure enough to check that rdlen is big enough to contain everything other than the type bitmap. (packet checked to be long enough to contain rdlen above) We don't need to do any further length checks in check_nes3_coverage() or prove_non_existence_nsec3() */ int salt_len, hash_len; unsigned char *psav = p; if (rdlen < 5) return DNSSEC_FAIL_BADPACKET; p += 4; /* algo, flags, iterations */ salt_len = *p++; /* salt_len */ if (rdlen < (6 + salt_len)) return DNSSEC_FAIL_BADPACKET; /* check up to hash_length */ p += salt_len; /* salt */ hash_len = *p++; if (rdlen < (6 + salt_len + hash_len)) return DNSSEC_FAIL_BADPACKET; /* check to end of next hashed name */ p = psav; } nsecset[nsecs_found++] = pstart; } p += rdlen; } if (type_found == T_NSEC) return prove_non_existence_nsec(header, plen, nsecset, rrsig_labels, nsecs_found, daemon->workspacename, keyname, name, qtype, nons); else if (type_found == T_NSEC3) return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons, validate_counter); else return DNSSEC_FAIL_NONSEC; } /* Check signing status of name. returns: STAT_SECURE zone is signed. STAT_INSECURE zone proved unsigned. STAT_NEED_DS require DS record of name returned in keyname. STAT_NEED_KEY require DNSKEY record of name returned in keyname. name returned unaltered. */ static int zone_status(char *name, int class, char *keyname, time_t now) { int name_start = strlen(name); /* for when TA is root */ struct crec *crecp; char *p; /* First, work towards the root, looking for a trust anchor. This can either be one configured, or one previously cached. We can assume, if we don't find one first, that there is a trust anchor at the root. */ for (p = name; p; p = strchr(p, '.')) { if (*p == '.') p++; if (cache_find_by_name(NULL, p, now, F_DS)) { name_start = p - name; break; } } /* Now work away from the trust anchor */ while (1) { strcpy(keyname, &name[name_start]); if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DS))) return STAT_NEED_DS; /* F_DNSSECOK misused in DS cache records to non-existence of NS record. F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here, but that's because there's no NS record either, ie this isn't the start of a zone. We only prove that the DNS tree below a node is unsigned when we prove that we're at a zone cut AND there's no DS record. */ if (crecp->flags & F_NEG) { if (crecp->flags & F_DNSSECOK) return STAT_INSECURE; /* proved no DS here */ } else { /* If all the DS records have digest and/or sig algos we don't support, then the zone is insecure. Note that if an algo appears in the DS, then RRSIGs for that algo MUST exist for each RRset: 4035 para 2.2 So if we find a DS here with digest and sig we can do, we're entitled to assume we can validate the zone and if we can't later, because an RRSIG is missing we return BOGUS. */ do { if (crecp->uid == (unsigned int)class && ds_digest_name(crecp->addr.ds.digest) && algo_digest_name(crecp->addr.ds.algo)) break; } while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS))); if (!crecp) return STAT_INSECURE; } if (name_start == 0) break; for (p = &name[name_start-2]; (*p != '.') && (p != name); p--); if (p != name) p++; name_start = p - name; } return STAT_SECURE; } /* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) Return code: STAT_SECURE if it validates. STAT_INSECURE at least one RRset not validated, because in unsigned zone. STAT_BOGUS signature is wrong, bad packet, no validation where there should be. STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname, class in *class) STAT_NEED_DS need DS to complete validation (name is returned in keyname) STAT_ABANDONED resource exhaustion. daemon->rr_status points to a char array which corressponds to the RRs in the answer and auth sections. This is set to >1 for each RR which is validated, and 0 for any which aren't. When validating replies to DS records, we're only interested in the NSEC{3} RRs in the auth section. Other RRs in that section missing sigs will not cause am INSECURE reply. We determine this mode if the nons argument is non-NULL. */ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int check_unsigned, int *neganswer, int *nons, int *nsec_ttl, int *validate_counter) { static unsigned char **targets = NULL; static int target_sz = 0; unsigned char *ans_start, *p1, *p2; int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx; int i, j, rc = STAT_INSECURE; int secure = STAT_SECURE; int rc_nsec; /* extend rr_status if necessary */ if (daemon->rr_status_sz < ntohs(header->ancount) + ntohs(header->nscount)) { unsigned long *new = whine_malloc(sizeof(*daemon->rr_status) * (ntohs(header->ancount) + ntohs(header->nscount) + 64)); if (!new) return STAT_BOGUS; free(daemon->rr_status); daemon->rr_status = new; daemon->rr_status_sz = ntohs(header->ancount) + ntohs(header->nscount) + 64; } memset(daemon->rr_status, 0, sizeof(*daemon->rr_status) * daemon->rr_status_sz); 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; p1 = (unsigned char *)(header+1); /* Find all the targets we're looking for answers to. The zeroth array element is for the query, subsequent ones for CNAME targets, unless the query is for a CNAME or ANY. */ if (!expand_workspace(&targets, &target_sz, 0)) return STAT_BOGUS; targets[0] = p1; targetidx = 1; if (!extract_name(header, plen, &p1, name, EXTR_NAME_EXTRACT, 4)) return STAT_BOGUS; GETSHORT(qtype, p1); GETSHORT(qclass, p1); ans_start = p1; /* Can't validate an RRSIG query */ if (qtype == T_RRSIG) return STAT_INSECURE; if (qtype != T_CNAME && qtype != T_ANY) for (j = ntohs(header->ancount); j != 0; j--) { if (!(p1 = skip_name(p1, header, plen, 10))) return STAT_BOGUS; /* bad packet */ GETSHORT(type2, p1); p1 += 6; /* class, TTL */ GETSHORT(rdlen2, p1); if (type2 == T_CNAME) { if (!expand_workspace(&targets, &target_sz, targetidx)) return STAT_BOGUS; targets[targetidx++] = p1; /* pointer to target name */ } if (!ADD_RDLEN(header, p1, plen, rdlen2)) return STAT_BOGUS; } for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++) { if (i != 0 && !ADD_RDLEN(header, p1, plen, rdlen1)) return STAT_BOGUS; if (!extract_name(header, plen, &p1, name, EXTR_NAME_EXTRACT, 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) continue; /* 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, EXTR_NAME_COMPARE, 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; } /* Done already: copy the validation status */ if (j != i) daemon->rr_status[i] = daemon->rr_status[j]; else { /* Not done, validate now */ int sigcnt, rrcnt; char *wildname; if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt)) return STAT_BOGUS; /* No signatures for RRset. We can be configured to assume this is OK and return an INSECURE result. */ if (sigcnt == 0) { /* NSEC and NSEC3 records must be signed. We make this assumption elsewhere. */ if (type1 == T_NSEC || type1 == T_NSEC3) return STAT_BOGUS | DNSSEC_FAIL_NOSIG; else if (nons && i >= ntohs(header->ancount)) /* If we're validating a DS reply, rather than looking for the value of AD bit, we only care that NSEC and NSEC3 RRs in the auth section are signed. Return SECURE even if others (SOA....) are not. */ rc = STAT_SECURE; else { /* unsigned RRsets in auth section are not BOGUS, but do make reply insecure. */ if (check_unsigned && i < ntohs(header->ancount)) { rc = zone_status(name, class1, keyname, now); if (STAT_ISEQUAL(rc, STAT_SECURE)) rc = STAT_BOGUS | DNSSEC_FAIL_NOSIG; if (class) *class = class1; /* Class for NEED_DS or NEED_KEY */ } else rc = STAT_INSECURE; if (!STAT_ISEQUAL(rc, STAT_INSECURE)) return rc; } } else { /* explore_rrset() gives us key name from sigs in keyname. Can't overwrite name here. */ strcpy(daemon->workspacename, keyname); rc = zone_status(daemon->workspacename, class1, keyname, now); if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS)) { if (class) *class = class1; /* Class for NEED_DS or NEED_KEY */ return rc; } /* Zone is insecure, don't need to validate RRset */ if (STAT_ISEQUAL(rc, STAT_SECURE)) { unsigned long sig_ttl; rc = validate_rrset(now, header, plen, class1, type1, sigcnt, rrcnt, name, keyname, &wildname, NULL, 0, 0, 0, &sig_ttl, validate_counter); if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS) || STAT_ISEQUAL(rc, STAT_ABANDONED)) { if (class) *class = class1; /* Class for DS or DNSKEY */ return rc; } /* rc is now STAT_SECURE or STAT_SECURE_WILDCARD */ /* Note that RR is validated */ daemon->rr_status[i] = sig_ttl; /* Note if we've validated either the answer to the question or the target of a CNAME. Any not noted will need NSEC or to be in unsigned space. */ for (j = 0; j > 16) & 0xffff; return ac & 0xffff; } } size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int id, int type) { unsigned char *p; header->qdcount = htons(1); header->ancount = htons(0); header->nscount = htons(0); header->arcount = htons(0); header->id = htons(id); 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; p = (unsigned char *)(header+1); p = do_rfc1035_name(p, name, NULL); *p++ = 0; PUTSHORT(type, p); PUTSHORT(class, p); return add_do_bit(header, p - (unsigned char *)header, end); } int errflags_to_ede(int status) { /* We can end up with more than one flag set for some errors, so this encodes a rough priority so the (eg) No sig is reported before no-unexpired-sig. */ if (status & DNSSEC_FAIL_NYV) return EDE_SIG_NYV; else if (status & DNSSEC_FAIL_EXP) return EDE_SIG_EXP; else if (status & DNSSEC_FAIL_NOKEYSUP) return EDE_USUPDNSKEY; else if (status & DNSSEC_FAIL_NOZONE) return EDE_NO_ZONEKEY; else if (status & DNSSEC_FAIL_NOKEY) return EDE_NO_DNSKEY; else if (status & DNSSEC_FAIL_NODSSUP) return EDE_USUPDS; else if (status & DNSSEC_FAIL_NSEC3_ITERS) return EDE_UNS_NS3_ITER; else if (status & DNSSEC_FAIL_NONSEC) return EDE_NO_NSEC; else if (status & DNSSEC_FAIL_INDET) return EDE_DNSSEC_IND; else if (status & DNSSEC_FAIL_NOSIG) return EDE_NO_RRSIG; else return EDE_UNSET; } #endif /* HAVE_DNSSEC */ dnsmasq-2.91/src/arp.c0000664000175000017500000001265514765043257013040 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" /* Time between forced re-loads from kernel. */ #define INTERVAL 90 #define ARP_MARK 0 #define ARP_FOUND 1 /* Confirmed */ #define ARP_NEW 2 /* Newly created */ #define ARP_EMPTY 3 /* No MAC addr */ struct arp_record { unsigned short hwlen, status; int family; unsigned char hwaddr[DHCP_CHADDR_MAX]; union all_addr addr; struct arp_record *next; }; static struct arp_record *arps = NULL, *old = NULL, *freelist = NULL; static time_t last = 0; static int filter_mac(int family, void *addrp, char *mac, size_t maclen, void *parmv) { struct arp_record *arp; (void)parmv; if (maclen > DHCP_CHADDR_MAX) return 1; /* Look for existing entry */ for (arp = arps; arp; arp = arp->next) { if (family != arp->family || arp->status == ARP_NEW) continue; if (family == AF_INET) { if (arp->addr.addr4.s_addr != ((struct in_addr *)addrp)->s_addr) continue; } else { if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr6, (struct in6_addr *)addrp)) continue; } if (arp->status == ARP_EMPTY) { /* existing address, was negative. */ arp->status = ARP_NEW; arp->hwlen = maclen; memcpy(arp->hwaddr, mac, maclen); } else if (arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0) /* Existing entry matches - confirm. */ arp->status = ARP_FOUND; else continue; break; } if (!arp) { /* New entry */ if (freelist) { arp = freelist; freelist = freelist->next; } else if (!(arp = whine_malloc(sizeof(struct arp_record)))) return 1; arp->next = arps; arps = arp; arp->status = ARP_NEW; arp->hwlen = maclen; arp->family = family; memcpy(arp->hwaddr, mac, maclen); if (family == AF_INET) arp->addr.addr4.s_addr = ((struct in_addr *)addrp)->s_addr; else memcpy(&arp->addr.addr6, addrp, IN6ADDRSZ); } return 1; } /* If in lazy mode, we cache absence of ARP entries. */ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now) { struct arp_record *arp, *tmp, **up; int updated = 0; again: /* If the database is less then INTERVAL old, look in there */ if (difftime(now, last) < INTERVAL) { /* addr == NULL -> just make cache up-to-date */ if (!addr) return 0; for (arp = arps; arp; arp = arp->next) { if (addr->sa.sa_family != arp->family) continue; if (arp->family == AF_INET && arp->addr.addr4.s_addr != addr->in.sin_addr.s_addr) continue; if (arp->family == AF_INET6 && !IN6_ARE_ADDR_EQUAL(&arp->addr.addr6, &addr->in6.sin6_addr)) continue; /* Only accept positive entries unless in lazy mode. */ if (arp->status != ARP_EMPTY || lazy || updated) { if (mac && arp->hwlen != 0) memcpy(mac, arp->hwaddr, arp->hwlen); return arp->hwlen; } } } /* Not found, try the kernel */ if (!updated) { updated = 1; last = now; /* Mark all non-negative entries */ for (arp = arps; arp; arp = arp->next) if (arp->status != ARP_EMPTY) arp->status = ARP_MARK; iface_enumerate(AF_UNSPEC, NULL, (callback_t){.af_unspec=filter_mac}); /* Remove all unconfirmed entries to old list. */ for (arp = arps, up = &arps; arp; arp = tmp) { tmp = arp->next; if (arp->status == ARP_MARK) { *up = arp->next; arp->next = old; old = arp; } else up = &arp->next; } goto again; } /* record failure, so we don't consult the kernel each time we're asked for this address */ if (freelist) { arp = freelist; freelist = freelist->next; } else arp = whine_malloc(sizeof(struct arp_record)); if (arp) { arp->next = arps; arps = arp; arp->status = ARP_EMPTY; arp->family = addr->sa.sa_family; arp->hwlen = 0; if (addr->sa.sa_family == AF_INET) arp->addr.addr4.s_addr = addr->in.sin_addr.s_addr; else memcpy(&arp->addr.addr6, &addr->in6.sin6_addr, IN6ADDRSZ); } return 0; } int do_arp_script_run(void) { struct arp_record *arp; /* Notify any which went, then move to free list */ if (old) { #ifdef HAVE_SCRIPT if (option_bool(OPT_SCRIPT_ARP)) queue_arp(ACTION_ARP_DEL, old->hwaddr, old->hwlen, old->family, &old->addr); #endif arp = old; old = arp->next; arp->next = freelist; freelist = arp; return 1; } for (arp = arps; arp; arp = arp->next) if (arp->status == ARP_NEW) { #ifdef HAVE_SCRIPT if (option_bool(OPT_SCRIPT_ARP)) queue_arp(ACTION_ARP, arp->hwaddr, arp->hwlen, arp->family, &arp->addr); #endif arp->status = ARP_FOUND; return 1; } return 0; } dnsmasq-2.91/src/nftset.c0000664000175000017500000000512714765043257013555 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #if defined (HAVE_NFTSET) && defined (HAVE_LINUX_NETWORK) #include #include #include static struct nft_ctx *ctx = NULL; static const char *cmd_add = "add element %s { %s }"; static const char *cmd_del = "delete element %s { %s }"; void nftset_init() { ctx = nft_ctx_new(NFT_CTX_DEFAULT); if (ctx == NULL) die(_("failed to create nftset context"), NULL, EC_MISC); /* disable libnftables output */ nft_ctx_buffer_error(ctx); } int add_to_nftset(const char *setname, const union all_addr *ipaddr, int flags, int remove) { const char *cmd = remove ? cmd_del : cmd_add; int ret, af = (flags & F_IPV4) ? AF_INET : AF_INET6; size_t new_sz; char *err_str, *new, *nl; const char *err; static char *cmd_buf = NULL; static size_t cmd_buf_sz = 0; inet_ntop(af, ipaddr, daemon->addrbuff, ADDRSTRLEN); if (setname[1] == ' ' && (setname[0] == '4' || setname[0] == '6')) { if (setname[0] == '4' && !(flags & F_IPV4)) return -1; if (setname[0] == '6' && !(flags & F_IPV6)) return -1; setname += 2; } if (cmd_buf_sz == 0) new_sz = 150; /* initial allocation */ else new_sz = snprintf(cmd_buf, cmd_buf_sz, cmd, setname, daemon->addrbuff); if (new_sz > cmd_buf_sz) { if (!(new = whine_malloc(new_sz + 10))) return 0; if (cmd_buf) free(cmd_buf); cmd_buf = new; cmd_buf_sz = new_sz + 10; snprintf(cmd_buf, cmd_buf_sz, cmd, setname, daemon->addrbuff); } ret = nft_run_cmd_from_buffer(ctx, cmd_buf); err = nft_ctx_get_error_buffer(ctx); if (ret != 0) { /* Log only first line of error return. */ if ((err_str = whine_malloc(strlen(err) + 1))) { strcpy(err_str, err); if ((nl = strchr(err_str, '\n'))) *nl = 0; my_syslog(LOG_ERR, "nftset %s %s", setname, err_str); free(err_str); } } return ret; } #endif dnsmasq-2.91/src/dhcp6.c0000664000175000017500000006240714765043257013262 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_DHCP6 #include struct iface_param { struct dhcp_context *current; struct in6_addr fallback, ll_addr, ula_addr; int ind, addr_match; }; 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 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 dnsmasq instance binding port 547. That's OK if they serve different networks. Need to set REUSEADDR|REUSEPORT to make this possible. 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 iface_param parm; struct cmsghdr *cmptr; struct msghdr msg; uint32_t 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; struct in6_addr all_servers; 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; #ifdef HAVE_LINUX_NETWORK /* This works around a possible Linux kernel bug when using interfaces enslaved to a VRF. The scope_id in the source address gets set to the index of the VRF interface, not the slave. Fortunately, the interface index returned by packetinfo is correct so we use that instead. Log this once, so if it triggers in other circumstances we've not anticipated and breaks things, we get some clues. */ if (from.sin6_scope_id != if_index) { static int logged = 0; if (!logged) { my_syslog(MS_DHCP | LOG_WARNING, _("Working around kernel bug: faulty source address scope for VRF slave %s"), ifr.ifr_name); logged = 1; } from.sin6_scope_id = if_index; } #endif #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_DHCPV6, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&from, NULL, daemon->dhcp6fd); #endif if (relay_reply6(&from, sz, ifr.ifr_name)) { #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), NULL, (union mysockaddr *)&from, daemon->dhcp6fd); #endif while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(-1), 0, (struct sockaddr *)&from, sizeof(from)))); } else { 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 && (tmp->flags & INAME_6) && wildcard_match(tmp->name, ifr.ifr_name)) return; parm.current = NULL; 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); } /* Ignore requests sent to the ALL_SERVERS multicast address for relay when we're listening there for DHCPv6 server reasons. */ inet_pton(AF_INET6, ALL_SERVERS, &all_servers); if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) && relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now)) return; if (!iface_enumerate(AF_INET6, &parm, (callback_t){.af_inet6=complete_context6})) return; /* Check for a relay again after iface_enumerate/complete_context has had chance to fill in relay->iface_index fields. This handles first time through and any changes in interface config. */ if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) && relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now)) 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; } /* 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); /* 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); #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), NULL, (union mysockaddr *)&from, daemon->dhcp6fd); #endif while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(-1), 0, (struct sockaddr *)&from, sizeof(from)))); } /* These need to be called _after_ we send DHCPv6 packet, since lease_update_file() may trigger sending an RA packet, which overwrites our buffer. */ lease_update_file(now); lease_update_dns(0); } } void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now) { /* Receiving 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; union mysockaddr addr; int i, maclen; 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.in6.sin6_len = sizeof(struct sockaddr_in6); #endif addr.in6.sin6_family = AF_INET6; addr.in6.sin6_port = htons(IPPROTO_ICMPV6); addr.in6.sin6_addr = *client; addr.in6.sin6_scope_id = iface; for (i = 0; i < 5; i++) { struct timespec ts; if ((maclen = find_mac(&addr, mac, 0, now)) != 0) break; while(retry_send(sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr)))); ts.tv_sec = 0; ts.tv_nsec = 100000000; /* 100ms */ nanosleep(&ts, NULL); } *maclenp = maclen; *mactypep = ARPHRD_ETHER; } 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 shared_network *share; struct dhcp_relay *relay; struct iface_param *param = vparam; struct iname *tmp; int match = !daemon->if_addrs; (void)scope; /* warning */ if (if_index != param->ind) return 1; 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)) return 1; /* 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)) match = 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 && context->current == context) { if (is_same_net6(local, &context->start6, context->prefix) && is_same_net6(local, &context->end6, context->prefix)) { struct dhcp_context *tmp, **up; /* use interface values only for constructed 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 = ¶m->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; } else { for (share = daemon->shared_networks; share; share = share->next) { /* IPv4 shared_address - ignore */ if (share->shared_addr.s_addr != 0) continue; if (share->if_index != 0) { if (share->if_index != if_index) continue; } else { if (!IN6_ARE_ADDR_EQUAL(&share->match_addr6, local)) continue; } if (is_same_net6(&share->shared_addr6, &context->start6, context->prefix) && is_same_net6(&share->shared_addr6, &context->end6, context->prefix)) { context->current = param->current; param->current = context; context->local6 = *local; context->preferred = context->flags & CONTEXT_DEPRECATE ? 0 :0xffffffff; context->valid = 0xffffffff; } } } } if (match) for (relay = daemon->relay6; relay; relay = relay->next) if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6)) relay->iface_index = if_index; return 1; } struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, struct in6_addr *addr) { struct dhcp_config *config; for (config = configs; config; config = config->next) if (config->flags & CONFIG_ADDR6) { struct addrlist *addr_list; for (addr_list = config->addr6; addr_list; addr_list = addr_list->next) if ((!net || is_same_net6(&addr_list->addr.addr6, net, prefix) || ((addr_list->flags & ADDRLIST_WILDCARD) && prefix == 64)) && is_same_net6(&addr_list->addr.addr6, addr, (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128)) return config; } return NULL; } struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len, int temp_addr, unsigned 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, skip addresses equal to the number of addresses rejected by clients. This should avoid the same client being offered the same address after it has rjected it. */ start = lease_find_max_addr6(c) + 1 + serial + c->addr_epoch; if (c->addr_epoch) c->addr_epoch--; } else { u64 range = 1 + addr6part(&c->end6) - addr6part(&c->start6); u64 offset = j + c->addr_epoch; /* don't divide by zero if range is whole 2^64 */ if (range != 0) offset = offset % range; start = addr6part(&c->start6) + offset; } /* 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; *ans = c->start6; setaddr6part (ans, addr); if (!d && !lease6_find_by_addr(&c->start6, c->prefix, addr) && !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, ans)) 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; } 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, (callback_t){.af_local=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, unsigned int preferred, unsigned int valid, void *vparam) { char ifrn_name[IFNAMSIZ]; struct in6_addr start6, end6; struct dhcp_context *template, *context; struct iname *tmp; (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; /* Ignore interfaces where we're not doing RA/DHCP6 */ if (!indextoname(daemon->icmp6fd, if_index, ifrn_name) || !iface_check(AF_LOCAL, NULL, ifrn_name, NULL)) return 1; for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) if (tmp->name && wildcard_match(tmp->name, ifrn_name)) return 1; for (template = daemon->dhcp6; template; template = template->next) if (!(template->flags & (CONTEXT_TEMPLATE | CONTEXT_CONSTRUCTED))) { /* 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)) { /* First time found, do fast RA. */ if (template->if_index == 0) { ra_start_unsolicited(param->now, template); param->newone = 1; } 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_TEMPLATE) && IN6_ARE_ADDR_EQUAL(&start6, &context->start6) && IN6_ARE_ADDR_EQUAL(&end6, &context->end6)) { /* If there's an absolute address context covering this address then don't construct one as well. */ if (!(context->flags & CONTEXT_CONSTRUCTED)) break; if (context->if_index == if_index) { int cflags = context->flags; context->flags &= ~(CONTEXT_GC | CONTEXT_OLD); if (cflags & CONTEXT_OLD) { /* address went, now it's back, and on the same interface */ log_context(AF_INET6, context); /* fast RAs for a while */ ra_start_unsolicited(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_unsolicited(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, ¶m, (callback_t){.af_inet6=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_unsolicited(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 /* HAVE_DHCP6 */ dnsmasq-2.91/src/config.h0000664000175000017500000003220214765043257013516 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #define FTABSIZ 150 /* max number of outstanding requests (default) */ #define MAX_PROCS 20 /* default 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 TCP_TIMEOUT 5 /* timeout waiting to connect to an upstream server - double this for answer */ #define TCP_BACKLOG 32 /* kernel backlog limit for TCP connections */ #define EDNS_PKTSZ 1232 /* default max EDNS.0 UDP packet from from /dnsflagday.net/2020 */ #define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */ #define DNSSEC_LIMIT_WORK 40 /* Max number of queries to validate one question */ #define DNSSEC_LIMIT_SIG_FAIL 20 /* Number of signature that can fail to validate in one answer */ #define DNSSEC_LIMIT_CRYPTO 200 /* max no. of crypto operations to validate one query. */ #define DNSSEC_LIMIT_NSEC3_ITERS 150 /* Max. number if iterations allowed in NSEC3 record. */ #define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */ #define SMALL_PORT_RANGE 30 /* If DNS port range is smaller than this, use different allocation. */ #define FORWARD_TEST 50 /* try all servers every 50 queries */ #define FORWARD_TIME 20 /* or 20 seconds */ #define UDP_TEST_TIME 60 /* How often to reset our idea of max packet size. */ #define SERVERS_LOGGED 30 /* Only log this many servers when logging state */ #define LOCALS_LOGGED 8 /* Only log this many local addresses when logging state */ #define 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 50 /* most domain names are smaller than this */ #define CNAME_CHAIN 10 /* chains longer than this atr dropped for loop protection */ #define DNSSEC_MIN_TTL 60 /* DNSKEY and DS records in cache last at least this long */ #define HOSTSFILE "/etc/hosts" #define ETHERSFILE "/etc/ethers" #define DEFLEASE 3600 /* default DHCPv4 lease time, one hour */ #define DEFLEASE6 (3600*24) /* default lease time for DHCPv6. One day. */ #define CHUSER "nobody" #define CHGRP "dip" #define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */ #define LOG_MAX 5 /* log-queue length */ #define RANDFILE "/dev/urandom" #define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq" /* Default - may be overridden by config */ #define DNSMASQ_PATH "/uk/org/thekelleys/dnsmasq" #define DNSMASQ_UBUS_NAME "dnsmasq" /* Default - may be overridden by config */ #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 #define DEFAULT_FAST_RETRY 1000 /* ms, default delay before fast retry */ #define STALE_CACHE_EXPIRY 86400 /* 1 day in secs, default maximum expiry time for stale cache data */ /* 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 which keeps time over reboots. Causes dnsmasq to use uptime for timing, and keep lease lengths rather than expiry times in its leases file. This also make dnsmasq "flash disk friendly". Normally, dnsmasq tries very hard to keep the on-disk leases file up-to-date: rewriting it after every renewal. When HAVE_BROKEN_RTC is in effect, the lease file is only written when a new lease is created, or an old one destroyed. (Because those are the only times it changes.) This vastly reduces the number of file writes, and makes it viable to keep the lease file on a flash filesystem. NOTE: when enabling or disabling this, be sure to delete any old leases file, otherwise dnsmasq may get very confused. HAVE_TFTP define this to get dnsmasq's built-in TFTP server. HAVE_DHCP define this to get dnsmasq's DHCPv4 server. HAVE_DHCP6 define this to get dnsmasq's DHCPv6 server. (implies HAVE_DHCP). HAVE_SCRIPT define this to get the ability to call scripts on lease-change. 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 support some methods to allow (re)configuration of the upstream DNS servers via DBus. HAVE_UBUS define this if you want to link against libubus HAVE_IDN define this if you want international domain name 2003 support. HAVE_LIBIDN2 define this if you want international domain name 2008 support. HAVE_CONNTRACK define this to include code which propagates 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_NFTSET define this to include the ability to selectively add resolved ip addresses to given nftables sets. 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_DUMPFILE include code to dump packets to a libpcap-format file for debugging. 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_ID Don't report *.bind CHAOS info to clients, forward such requests upstream instead. NO_TFTP NO_DHCP NO_DHCP6 NO_SCRIPT NO_LARGEFILE NO_AUTH NO_DUMPFILE NO_LOOP NO_INOTIFY these are available to explicitly disable compile time options which would otherwise be enabled automatically 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_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. */ /* 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_AUTH #define HAVE_IPSET #define HAVE_LOOP #define HAVE_DUMPFILE /* Build options which require external libraries. Defining HAVE__STATIC as _well_ as HAVE_ will link the library statically. You can use "make COPTS=-DHAVE_" instead of editing these. */ /* #define HAVE_LUASCRIPT */ /* #define HAVE_DBUS */ /* #define HAVE_IDN */ /* #define HAVE_LIBIDN2 */ /* #define HAVE_CONNTRACK */ /* #define HAVE_DNSSEC */ /* #define HAVE_NFTSET */ /* 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 #ifndef CONFFILE # if defined(__FreeBSD__) # define CONFFILE "/usr/local/etc/dnsmasq.conf" # else # define CONFFILE "/etc/dnsmasq.conf" # endif #endif #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 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) */ #if defined(__UCLIBC__) #define HAVE_LINUX_NETWORK #if defined(__UCLIBC_HAS_GNU_GETOPT__) || \ ((__UCLIBC_MAJOR__==0) && (__UCLIBC_MINOR__==9) && (__UCLIBC_SUBLEVEL__<21)) # define HAVE_GETOPT_LONG #endif #undef HAVE_SOCKADDR_SA_LEN #if defined(__UCLIBC_HAS_IPV6__) # ifndef IPV6_V6ONLY # define IPV6_V6ONLY 26 # endif #endif /* This is for glibc 2.x */ #elif defined(__linux__) #define HAVE_LINUX_NETWORK #define HAVE_GETOPT_LONG #undef HAVE_SOCKADDR_SA_LEN #elif defined(__FreeBSD__) || \ defined(__OpenBSD__) || \ defined(__DragonFly__) || \ defined(__FreeBSD_kernel__) #define HAVE_BSD_NETWORK /* Later versions of FreeBSD have getopt_long() */ #if defined(optional_argument) && defined(required_argument) # define HAVE_GETOPT_LONG #endif #define HAVE_SOCKADDR_SA_LEN #elif defined(__APPLE__) #define HAVE_BSD_NETWORK #define HAVE_GETOPT_LONG #define HAVE_SOCKADDR_SA_LEN #define NO_IPSET /* 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 /* Required for Mojave. */ #ifndef SOL_TCP # define SOL_TCP IPPROTO_TCP #endif #define NO_IPSET #elif defined(__NetBSD__) #define HAVE_BSD_NETWORK #define HAVE_GETOPT_LONG #define HAVE_SOCKADDR_SA_LEN #elif defined(__sun) || defined(__sun__) #define HAVE_SOLARIS_NETWORK #define HAVE_GETOPT_LONG #undef HAVE_SOCKADDR_SA_LEN #define ETHER_ADDR_LEN 6 #endif /* rules to implement compile-time option dependencies and the NO_XXX flags */ #ifdef NO_TFTP #undef HAVE_TFTP #endif #ifdef NO_DHCP #undef HAVE_DHCP #undef HAVE_DHCP6 #endif #if defined(NO_DHCP6) #undef HAVE_DHCP6 #endif /* DHCP6 needs DHCP too */ #ifdef HAVE_DHCP6 #define HAVE_DHCP #endif #if defined(NO_SCRIPT) #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 #ifdef NO_DUMPFILE #undef HAVE_DUMPFILE #endif #if defined (HAVE_LINUX_NETWORK) && !defined(NO_INOTIFY) #define HAVE_INOTIFY #endif /* This never compiles code, it's only used by the makefile to fingerprint builds. */ #ifdef DNSMASQ_COMPILE_FLAGS static char *compile_flags = DNSMASQ_COMPILE_FLAGS; #endif /* Define a string indicating which options are in use. DNSMASQ_COMPILE_OPTS is only defined in dnsmasq.c */ #ifdef DNSMASQ_COMPILE_OPTS static char *compile_opts = "IPv6 " #ifndef HAVE_GETOPT_LONG "no-" #endif "GNU-getopt " #ifdef HAVE_BROKEN_RTC "no-RTC " #endif #ifndef HAVE_DBUS "no-" #endif "DBus " #ifndef HAVE_UBUS "no-" #endif "UBus " #ifndef LOCALEDIR "no-" #endif "i18n " #if defined(HAVE_LIBIDN2) "IDN2 " #else #if !defined(HAVE_IDN) "no-" #endif "IDN " #endif #ifndef HAVE_DHCP "no-" #endif "DHCP " #if defined(HAVE_DHCP) # if !defined (HAVE_DHCP6) "no-" # endif "DHCPv6 " #endif #if !defined(HAVE_SCRIPT) "no-scripts " #else # if !defined(HAVE_LUASCRIPT) "no-" # endif "Lua " #endif #ifndef HAVE_TFTP "no-" #endif "TFTP " #ifndef HAVE_CONNTRACK "no-" #endif "conntrack " #ifndef HAVE_IPSET "no-" #endif "ipset " #ifndef HAVE_NFTSET "no-" #endif "nftset " #ifndef HAVE_AUTH "no-" #endif "auth " #ifndef HAVE_DNSSEC "no-" #endif "DNSSEC " #ifdef NO_ID "no-ID " #endif #ifndef HAVE_LOOP "no-" #endif "loop-detect " #ifndef HAVE_INOTIFY "no-" #endif "inotify " #ifndef HAVE_DUMPFILE "no-" #endif "dumpfile"; #endif /* defined(HAVE_DHCP) */ dnsmasq-2.91/src/bpf.c0000664000175000017500000003045714765043257013025 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) #include #include #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) #include #endif #include #include #include #include #if defined(__FreeBSD__) # include #endif #include #include #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 union all_addr del_addr; #endif #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) int arp_enumerate(void *parm, callback_t callback) { int mib[6]; size_t needed; char *next; 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; mib[3] = AF_INET; mib[4] = NET_RT_FLAGS; #ifdef RTF_LLINFO mib[5] = RTF_LLINFO; #else mib[5] = 0; #endif if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0) return 0; while (1) { if (!expand_buf(&buff, needed)) return 0; if ((rc = sysctl(mib, 6, buff.iov_base, &needed, NULL, 0)) == 0 || errno != ENOMEM) break; needed += needed / 8; } if (rc == -1) return 0; 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); sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); if (!callback.af_unspec(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm)) return 0; } return 1; } #endif /* defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) */ int iface_enumerate(int family, void *parm, callback_t callback) { struct ifaddrs *head, *addrs; int errsave, fd = -1, ret = 0; if (family == AF_UNSPEC) #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) return arp_enumerate(parm, callback); #else return 0; /* need code for Solaris and MacOS*/ #endif /* 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) if (family == AF_INET6) fd = socket(PF_INET6, SOCK_DGRAM, 0); #endif for (addrs = head; addrs; addrs = addrs->ifa_next) { int iface_index = if_nametoindex(addrs->ifa_name); if (iface_index == 0 || !addrs->ifa_addr || addrs->ifa_addr->sa_family != family || (!addrs->ifa_netmask && family != AF_LINK)) continue; if (family == AF_INET) { struct in_addr addr, netmask, broadcast; addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr; #ifdef HAVE_BSD_NETWORK if (del_family == AF_INET && del_addr.addr4.s_addr == addr.s_addr) continue; #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.af_inet(addr, iface_index, NULL, netmask, broadcast, parm)) goto err; } else if (family == AF_INET6) { 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.addr6, addr)) continue; #endif #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) struct in6_ifreq ifr6; memset(&ifr6, 0, sizeof(ifr6)); safe_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.af_inet6(addr, prefix, scope_id, iface_index, flags, (unsigned int) preferred, (unsigned int)valid, parm)) goto err; } #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.af_local(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)) goto err; } #endif } ret = 1; err: errsave = errno; freeifaddrs(head); if (fd != -1) close(fd); errno = errsave; return ret; } #endif /* defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) */ #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP) #include void init_bpf(void) { int i = 0; while (1) { 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); } } void send_via_bpf(struct dhcp_packet *mess, size_t len, struct in_addr iface_addr, struct ifreq *ifr) { /* Hairy stuff, packet either has to go to the net broadcast or the destination can't reply to ARP yet, but we do know the physical address. Build the packet by steam, and send directly, bypassing the kernel IP stack */ struct ether_header ether; struct ip ip; struct udphdr { u16 uh_sport; /* source port */ u16 uh_dport; /* destination port */ u16 uh_ulen; /* udp length */ u16 uh_sum; /* udp checksum */ } udp; u32 i, sum; struct iovec iov[4]; /* Only know how to do ethernet on *BSD */ if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN) { my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"), mess->htype, ifr->ifr_name); return; } ifr->ifr_addr.sa_family = AF_LINK; if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0) return; memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN); ether.ether_type = htons(ETHERTYPE_IP); if (ntohs(mess->flags) & 0x8000) { memset(ether.ether_dhost, 255, ETHER_ADDR_LEN); ip.ip_dst.s_addr = INADDR_BROADCAST; } else { memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN); ip.ip_dst.s_addr = mess->yiaddr.s_addr; } ip.ip_p = IPPROTO_UDP; ip.ip_src.s_addr = iface_addr.s_addr; ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len) ; ip.ip_hl = sizeof(struct ip) / 4; ip.ip_v = IPVERSION; ip.ip_tos = 0; ip.ip_id = htons(0); ip.ip_off = htons(0x4000); /* don't fragment */ ip.ip_ttl = IPDEFTTL; ip.ip_sum = 0; for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++) sum += ((u16 *)&ip)[i]; while (sum>>16) sum = (sum & 0xffff) + (sum >> 16); ip.ip_sum = (sum == 0xffff) ? sum : ~sum; udp.uh_sport = htons(daemon->dhcp_server_port); udp.uh_dport = htons(daemon->dhcp_client_port); if (len & 1) ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */ udp.uh_sum = 0; udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len); sum += htons(IPPROTO_UDP); sum += ip.ip_src.s_addr & 0xffff; sum += (ip.ip_src.s_addr >> 16) & 0xffff; sum += ip.ip_dst.s_addr & 0xffff; sum += (ip.ip_dst.s_addr >> 16) & 0xffff; for (i = 0; i < sizeof(struct udphdr)/2; i++) sum += ((u16 *)&udp)[i]; for (i = 0; i < (len + 1) / 2; i++) sum += ((u16 *)mess)[i]; while (sum>>16) sum = (sum & 0xffff) + (sum >> 16); udp.uh_sum = (sum == 0xffff) ? sum : ~sum; ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr); iov[0].iov_base = ðer; iov[0].iov_len = sizeof(ether); iov[1].iov_base = &ip; iov[1].iov_len = sizeof(ip); iov[2].iov_base = &udp; iov[2].iov_len = sizeof(udp); iov[3].iov_base = mess; iov[3].iov_len = len; 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.addr4 = ((struct sockaddr_in *)sa)->sin_addr; else if (del_family == AF_INET6) del_addr.addr6 = ((struct sockaddr_in6 *)sa)->sin6_addr; 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 */ dnsmasq-2.91/src/pattern.c0000664000175000017500000002516314765043257013731 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_CONNTRACK #define LOG(...) \ do { \ my_syslog(LOG_DEBUG, __VA_ARGS__); \ } while (0) #define ASSERT(condition) \ do { \ if (!(condition)) \ my_syslog(LOG_ERR, _("[pattern.c:%d] Assertion failure: %s"), __LINE__, #condition); \ } while (0) /** * Determines whether a given string value matches against a glob pattern * which may contain zero-or-more-character wildcards denoted by '*'. * * Based on "Glob Matching Can Be Simple And Fast Too" by Russ Cox, * See https://research.swtch.com/glob * * @param value A string value. * @param num_value_bytes The number of bytes of the string value. * @param pattern A glob pattern. * @param num_pattern_bytes The number of bytes of the glob pattern. * * @return 1 If the provided value matches against the glob pattern. * @return 0 Otherwise. */ static int is_string_matching_glob_pattern( const char *value, size_t num_value_bytes, const char *pattern, size_t num_pattern_bytes) { ASSERT(value); ASSERT(pattern); size_t value_index = 0; size_t next_value_index = 0; size_t pattern_index = 0; size_t next_pattern_index = 0; while (value_index < num_value_bytes || pattern_index < num_pattern_bytes) { if (pattern_index < num_pattern_bytes) { char pattern_character = pattern[pattern_index]; if ('a' <= pattern_character && pattern_character <= 'z') pattern_character -= 'a' - 'A'; if (pattern_character == '*') { /* zero-or-more-character wildcard */ /* Try to match at value_index, otherwise restart at value_index + 1 next. */ next_pattern_index = pattern_index; pattern_index++; if (value_index < num_value_bytes) next_value_index = value_index + 1; else next_value_index = 0; continue; } else { /* ordinary character */ if (value_index < num_value_bytes) { char value_character = value[value_index]; if ('a' <= value_character && value_character <= 'z') value_character -= 'a' - 'A'; if (value_character == pattern_character) { pattern_index++; value_index++; continue; } } } } if (next_value_index) { pattern_index = next_pattern_index; value_index = next_value_index; continue; } return 0; } return 1; } /** * Determines whether a given string value represents a valid DNS name. * * - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels * delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only * ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen. * * - A valid name must be fully qualified, i.e., consist of at least two labels. * The final label must not be fully numeric, and must not be the "local" pseudo-TLD. * * - Examples: * Valid: "example.com" * Invalid: "ipcamera", "ipcamera.local", "8.8.8.8" * * @param value A string value. * * @return 1 If the provided string value is a valid DNS name. * @return 0 Otherwise. */ int is_valid_dns_name(const char *value) { ASSERT(value); size_t num_bytes = 0; size_t num_labels = 0; const char *c, *label = NULL; int is_label_numeric = 1; for (c = value;; c++) { if (*c && *c != '-' && *c != '.' && (*c < '0' || *c > '9') && (*c < 'A' || *c > 'Z') && (*c < 'a' || *c > 'z')) { LOG(_("Invalid DNS name: Invalid character %c."), *c); return 0; } if (*c) num_bytes++; if (!label) { if (!*c || *c == '.') { LOG(_("Invalid DNS name: Empty label.")); return 0; } if (*c == '-') { LOG(_("Invalid DNS name: Label starts with hyphen.")); return 0; } label = c; } if (*c && *c != '.') { if (*c < '0' || *c > '9') is_label_numeric = 0; } else { if (c[-1] == '-') { LOG(_("Invalid DNS name: Label ends with hyphen.")); return 0; } size_t num_label_bytes = (size_t) (c - label); if (num_label_bytes > 63) { LOG(_("Invalid DNS name: Label is too long (%zu)."), num_label_bytes); return 0; } num_labels++; if (!*c) { if (num_labels < 2) { LOG(_("Invalid DNS name: Not enough labels (%zu)."), num_labels); return 0; } if (is_label_numeric) { LOG(_("Invalid DNS name: Final label is fully numeric.")); return 0; } if (num_label_bytes == 5 && (label[0] == 'l' || label[0] == 'L') && (label[1] == 'o' || label[1] == 'O') && (label[2] == 'c' || label[2] == 'C') && (label[3] == 'a' || label[3] == 'A') && (label[4] == 'l' || label[4] == 'L')) { LOG(_("Invalid DNS name: \"local\" pseudo-TLD.")); return 0; } if (num_bytes < 1 || num_bytes > 253) { LOG(_("DNS name has invalid length (%zu)."), num_bytes); return 0; } return 1; } label = NULL; is_label_numeric = 1; } } } /** * Determines whether a given string value represents a valid DNS name pattern. * * - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels * delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only * ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen. * * - Patterns follow the syntax of DNS names, but additionally allow the wildcard character "*" to be used up to * twice per label to match 0 or more characters within that label. Note that the wildcard never matches a dot * (e.g., "*.example.com" matches "api.example.com" but not "api.us.example.com"). * * - A valid name or pattern must be fully qualified, i.e., consist of at least two labels. * The final label must not be fully numeric, and must not be the "local" pseudo-TLD. * A pattern must end with at least two literal (non-wildcard) labels. * * - Examples: * Valid: "example.com", "*.example.com", "video*.example.com", "api*.*.example.com", "*-prod-*.example.com" * Invalid: "ipcamera", "ipcamera.local", "*", "*.com", "8.8.8.8" * * @param value A string value. * * @return 1 If the provided string value is a valid DNS name pattern. * @return 0 Otherwise. */ int is_valid_dns_name_pattern(const char *value) { ASSERT(value); size_t num_bytes = 0; size_t num_labels = 0; const char *c, *label = NULL; int is_label_numeric = 1; size_t num_wildcards = 0; int previous_label_has_wildcard = 1; for (c = value;; c++) { if (*c && *c != '*' && /* Wildcard. */ *c != '-' && *c != '.' && (*c < '0' || *c > '9') && (*c < 'A' || *c > 'Z') && (*c < 'a' || *c > 'z')) { LOG(_("Invalid DNS name pattern: Invalid character %c."), *c); return 0; } if (*c && *c != '*') num_bytes++; if (!label) { if (!*c || *c == '.') { LOG(_("Invalid DNS name pattern: Empty label.")); return 0; } if (*c == '-') { LOG(_("Invalid DNS name pattern: Label starts with hyphen.")); return 0; } label = c; } if (*c && *c != '.') { if (*c < '0' || *c > '9') is_label_numeric = 0; if (*c == '*') { if (num_wildcards >= 2) { LOG(_("Invalid DNS name pattern: Wildcard character used more than twice per label.")); return 0; } num_wildcards++; } } else { if (c[-1] == '-') { LOG(_("Invalid DNS name pattern: Label ends with hyphen.")); return 0; } size_t num_label_bytes = (size_t) (c - label) - num_wildcards; if (num_label_bytes > 63) { LOG(_("Invalid DNS name pattern: Label is too long (%zu)."), num_label_bytes); return 0; } num_labels++; if (!*c) { if (num_labels < 2) { LOG(_("Invalid DNS name pattern: Not enough labels (%zu)."), num_labels); return 0; } if (num_wildcards != 0 || previous_label_has_wildcard) { LOG(_("Invalid DNS name pattern: Wildcard within final two labels.")); return 0; } if (is_label_numeric) { LOG(_("Invalid DNS name pattern: Final label is fully numeric.")); return 0; } if (num_label_bytes == 5 && (label[0] == 'l' || label[0] == 'L') && (label[1] == 'o' || label[1] == 'O') && (label[2] == 'c' || label[2] == 'C') && (label[3] == 'a' || label[3] == 'A') && (label[4] == 'l' || label[4] == 'L')) { LOG(_("Invalid DNS name pattern: \"local\" pseudo-TLD.")); return 0; } if (num_bytes < 1 || num_bytes > 253) { LOG(_("DNS name pattern has invalid length after removing wildcards (%zu)."), num_bytes); return 0; } return 1; } label = NULL; is_label_numeric = 1; previous_label_has_wildcard = num_wildcards != 0; num_wildcards = 0; } } } /** * Determines whether a given DNS name matches against a DNS name pattern. * * @param name A valid DNS name. * @param pattern A valid DNS name pattern. * * @return 1 If the provided DNS name matches against the DNS name pattern. * @return 0 Otherwise. */ int is_dns_name_matching_pattern(const char *name, const char *pattern) { ASSERT(name); ASSERT(is_valid_dns_name(name)); ASSERT(pattern); ASSERT(is_valid_dns_name_pattern(pattern)); const char *n = name; const char *p = pattern; do { const char *name_label = n; while (*n && *n != '.') n++; const char *pattern_label = p; while (*p && *p != '.') p++; if (!is_string_matching_glob_pattern( name_label, (size_t) (n - name_label), pattern_label, (size_t) (p - pattern_label))) break; if (*n) n++; if (*p) p++; } while (*n && *p); return !*n && !*p; } #endif dnsmasq-2.91/src/metrics.c0000664000175000017500000000336514765043257013722 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" const char * metric_names[] = { "dns_cache_inserted", "dns_cache_live_freed", "dns_queries_forwarded", "dns_auth_answered", "dns_local_answered", "dns_stale_answered", "dns_unanswered", "dnssec_max_crypto_use", "dnssec_max_sig_fail", "dnssec_max_work", "bootp", "pxe", "dhcp_ack", "dhcp_decline", "dhcp_discover", "dhcp_inform", "dhcp_nak", "dhcp_offer", "dhcp_release", "dhcp_request", "noanswer", "leases_allocated_4", "leases_pruned_4", "leases_allocated_6", "leases_pruned_6", "tcp_connections", }; const char* get_metric_name(int i) { return metric_names[i]; } void clear_metrics(void) { int i; struct server *serv; for (i = 0; i < __METRIC_MAX; i++) daemon->metrics[i] = 0; for (serv = daemon->servers; serv; serv = serv->next) { serv->queries = 0; serv->failed_queries = 0; serv->failed_queries = 0; serv->retrys = 0; serv->nxdomain_replies = 0; serv->query_latency = 0; } } dnsmasq-2.91/src/domain.c0000664000175000017500000001523314765043257013520 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" static struct cond_domain *search_domain(struct in_addr addr, struct cond_domain *c); static int match_domain(struct in_addr addr, struct cond_domain *c); static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c); static int match_domain6(struct in6_addr *addr, struct cond_domain *c); int is_name_synthetic(int flags, char *name, union all_addr *addrp) { char *p; struct cond_domain *c = NULL; int prot = (flags & F_IPV6) ? AF_INET6 : AF_INET; union all_addr addr; 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 */ if (c->indexed) { for (p = tail; *p; p++) { char c = *p; if (c < '0' || c > '9') break; } if (*p != '.') continue; *p = 0; if (hostname_isequal(c->domain, p+1)) { if (prot == AF_INET) { unsigned int index = atoi(tail); if (!c->is6 && index <= ntohl(c->end.s_addr) - ntohl(c->start.s_addr)) { addr.addr4.s_addr = htonl(ntohl(c->start.s_addr) + index); found = 1; } } else { u64 index = atoll(tail); if (c->is6 && index <= addr6part(&c->end6) - addr6part(&c->start6)) { u64 start = addr6part(&c->start6); addr.addr6 = c->start6; setaddr6part(&addr.addr6, start + index); found = 1; } } } } else { /* NB, must not alter name if we return zero */ for (p = tail; *p; p++) { char c = *p; if ((c >='0' && c <= '9') || c == '-') continue; if (prot == AF_INET6 && ((c >='A' && c <= 'F') || (c >='a' && c <= 'f'))) continue; break; } if (*p != '.') continue; *p = 0; /* swap . or : for - */ for (p = tail; *p; p++) if (*p == '-') { if (prot == AF_INET) *p = '.'; else *p = ':'; } if (hostname_isequal(c->domain, p+1) && inet_pton(prot, tail, &addr)) found = (prot == AF_INET) ? match_domain(addr.addr4, c) : match_domain6(&addr.addr6, c); } /* restore name */ for (p = tail; *p; p++) if (*p == '.' || *p == ':') *p = '-'; *p = '.'; if (found) { if (addrp) *addrp = addr; return 1; } } return 0; } int is_rev_synth(int flag, union all_addr *addr, char *name) { struct cond_domain *c; if (flag & F_IPV4 && (c = search_domain(addr->addr4, daemon->synth_domains))) { char *p; *name = 0; if (c->indexed) { unsigned int index = ntohl(addr->addr4.s_addr) - ntohl(c->start.s_addr); snprintf(name, MAXDNAME, "%s%u", c->prefix ? c->prefix : "", index); } else { if (c->prefix) strncpy(name, c->prefix, MAXDNAME - ADDRSTRLEN); inet_ntop(AF_INET, &addr->addr4, name + strlen(name), ADDRSTRLEN); for (p = name; *p; p++) if (*p == '.') *p = '-'; } strncat(name, ".", MAXDNAME); strncat(name, c->domain, MAXDNAME); return 1; } if ((flag & F_IPV6) && (c = search_domain6(&addr->addr6, daemon->synth_domains))) { *name = 0; if (c->indexed) { u64 index = addr6part(&addr->addr6) - addr6part(&c->start6); snprintf(name, MAXDNAME, "%s%llu", c->prefix ? c->prefix : "", index); } else { int i; char frag[6]; if (c->prefix) strncpy(name, c->prefix, MAXDNAME); for (i = 0; i < 16; i += 2) { sprintf(frag, "%s%02x%02x", i == 0 ? "" : "-", addr->addr6.s6_addr[i], addr->addr6.s6_addr[i+1]); strncat(name, frag, MAXDNAME); } } strncat(name, ".", MAXDNAME); strncat(name, c->domain, MAXDNAME); return 1; } return 0; } static int match_domain(struct in_addr addr, struct cond_domain *c) { if (c->interface) { struct addrlist *al; for (al = c->al; al; al = al->next) if (!(al->flags & ADDRLIST_IPV6) && is_same_net_prefix(addr, al->addr.addr4, al->prefixlen)) return 1; } else if (!c->is6 && ntohl(addr.s_addr) >= ntohl(c->start.s_addr) && ntohl(addr.s_addr) <= ntohl(c->end.s_addr)) return 1; return 0; } static struct cond_domain *search_domain(struct in_addr addr, struct cond_domain *c) { for (; c; c = c->next) if (match_domain(addr, c)) 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; } static int match_domain6(struct in6_addr *addr, struct cond_domain *c) { /* subnet from interface address. */ if (c->interface) { struct addrlist *al; for (al = c->al; al; al = al->next) if (al->flags & ADDRLIST_IPV6 && is_same_net6(addr, &al->addr.addr6, al->prefixlen)) return 1; } else if (c->is6) { if (c->prefixlen >= 64) { u64 addrpart = addr6part(addr); if (is_same_net6(addr, &c->start6, 64) && addrpart >= addr6part(&c->start6) && addrpart <= addr6part(&c->end6)) return 1; } else if (is_same_net6(addr, &c->start6, c->prefixlen)) return 1; } return 0; } static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c) { for (; c; c = c->next) if (match_domain6(addr, c)) 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; } dnsmasq-2.91/src/dhcp.c0000664000175000017500000010763714765043257013201 0ustar srksrk/* dnsmasq is Copyright (c) 2000-2025 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 . */ #include "dnsmasq.h" #ifdef HAVE_DHCP struct iface_param { struct dhcp_context *current; int ind; }; 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 void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz); static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface); static int make_fd(int port) { int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); struct sockaddr_in saddr; int oneopt = 1; #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, 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, IPPROTO_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 || #else setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 || #endif setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1) die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET); /* When bind-interfaces is set, there might be more than one dnsmasq instance binding port 67. That's OK if they serve different networks. Need to set REUSEADDR|REUSEPORT to make this possible. 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 DHCP socket: %s"), NULL, EC_BADNET); } memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_port = htons(port); saddr.sin_addr.s_addr = INADDR_ANY; #ifdef HAVE_SOCKADDR_SA_LEN saddr.sin_len = sizeof(struct sockaddr_in); #endif if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in))) die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET); return fd; } void dhcp_init(void) { #if defined(HAVE_BSD_NETWORK) int oneopt = 1; #endif daemon->dhcpfd = make_fd(daemon->dhcp_server_port); if (daemon->enable_pxe) daemon->pxefd = make_fd(PXE_PORT); else daemon->pxefd = -1; #if defined(HAVE_BSD_NETWORK) /* When we're not using capabilities, we need to do this here before we drop root. Also, set buffer size small, to avoid wasting kernel buffers */ if (option_bool(OPT_NO_PING)) daemon->dhcp_icmp_fd = -1; else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 || setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 ) die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET); /* Make BPF raw send socket */ init_bpf(); #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; struct sockaddr_in dest; struct cmsghdr *cmptr; struct iovec iov; ssize_t sz; int iface_index = 0, unicast_dest = 0, is_inform = 0, loopback = 0; int rcvd_iface_index; struct in_addr iface_addr; struct iface_param parm; time_t recvtime = now; #ifdef HAVE_LINUX_NETWORK struct arpreq arp_req; struct timeval tv; #endif union { struct cmsghdr align; /* this ensures alignment */ #if defined(HAVE_LINUX_NETWORK) char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; #elif defined(HAVE_SOLARIS_NETWORK) char control[CMSG_SPACE(sizeof(unsigned int))]; #elif defined(HAVE_BSD_NETWORK) char control[CMSG_SPACE(sizeof(struct sockaddr_dl))]; #endif } control_u; struct dhcp_bridge *bridge, *alias; msg.msg_controllen = sizeof(control_u); msg.msg_control = control_u.control; msg.msg_name = &dest; msg.msg_namelen = sizeof(dest); msg.msg_iov = &daemon->dhcp_packet; msg.msg_iovlen = 1; if ((sz = recv_dhcp_packet(fd, &msg)) == -1 || (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))) return; #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_DHCP, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&dest, NULL, fd); #endif #if defined (HAVE_LINUX_NETWORK) if (ioctl(fd, SIOCGSTAMP, &tv) == 0) recvtime = tv.tv_sec; if (msg.msg_controllen >= sizeof(struct cmsghdr)) 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); iface_index = p.p->ipi_ifindex; if (p.p->ipi_addr.s_addr != INADDR_BROADCAST) unicast_dest = 1; } #elif defined(HAVE_BSD_NETWORK) if (msg.msg_controllen >= sizeof(struct cmsghdr)) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) { union { unsigned char *c; struct sockaddr_dl *s; } p; p.c = CMSG_DATA(cmptr); iface_index = p.s->sdl_index; } #elif defined(HAVE_SOLARIS_NETWORK) if (msg.msg_controllen >= sizeof(struct cmsghdr)) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) { union { unsigned char *c; unsigned int *i; } p; p.c = CMSG_DATA(cmptr); iface_index = *(p.i); } #endif if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name) || ioctl(daemon->dhcpfd, SIOCGIFFLAGS, &ifr) != 0) return; mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base; loopback = !mess->giaddr.s_addr && (ifr.ifr_flags & IFF_LOOPBACK); #ifdef HAVE_LINUX_NETWORK /* ARP fiddling uses original interface even if we pretend to use a different one. */ safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev)); #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. */ rcvd_iface_index = iface_index; 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 { safe_strncpy(ifr.ifr_name, bridge->iface, sizeof(ifr.ifr_name)); 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 if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name))) { /* Reply from server, using us as relay. */ rcvd_iface_index = relay->iface_index; if (!indextoname(daemon->dhcpfd, rcvd_iface_index, ifr.ifr_name)) return; is_relay_reply = 1; iov.iov_len = sz; #ifdef HAVE_LINUX_NETWORK safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev)); #endif } else { 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 { if (iface_check(AF_INET, NULL, ifr.ifr_name, NULL)) 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 && (tmp->flags & INAME_4) && 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; parm.current = NULL; parm.ind = iface_index; if (!iface_check(AF_INET, (union 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, (callback_t){.af_inet=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, (callback_t){.af_inet=complete_context})) return; relay_upstream4(iface_index, mess, (size_t)sz); /* 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, loopback, &is_inform, pxe_fd, iface_addr, recvtime); lease_update_file(now); lease_update_dns(0); if (iov.iov_len == 0) return; } msg.msg_name = &dest; msg.msg_namelen = sizeof(dest); msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_iov = &iov; iov.iov_base = daemon->dhcp_packet.iov_base; /* packet buffer may have moved */ mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base; #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 && !is_relay_reply) { /* Send to BOOTP relay */ dest.sin_port = htons(daemon->dhcp_server_port); dest.sin_addr = mess->giaddr; } else if (mess->ciaddr.s_addr) { /* If the client's idea of its own address tallys with the source address in the request packet, we believe the 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 || is_relay_reply) { dest.sin_port = htons(daemon->dhcp_client_port); dest.sin_addr = mess->ciaddr; } } #if defined(HAVE_LINUX_NETWORK) else { /* fill cmsg for outbound interface (both broadcast & unicast) */ struct in_pktinfo *pkt; msg.msg_control = control_u.control; msg.msg_controllen = sizeof(control_u); /* alignment padding passed to the kernel should not be uninitialised. */ memset(&control_u, 0, sizeof(control_u)); cmptr = CMSG_FIRSTHDR(&msg); pkt = (struct in_pktinfo *)CMSG_DATA(cmptr); pkt->ipi_ifindex = rcvd_iface_index; pkt->ipi_spec_dst.s_addr = 0; msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); 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) { /* 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); /* note that we don't specify the interface here: that's done by the IP_BOUND_IF sockopt lower down. */ } else { /* unicast to unconfigured client. Inject mac address direct into ARP cache. Note that this only works for ethernet on solaris, because we use SIOCSARP and not SIOCSXARP, which would be perfect, except that it returns ENXIO mysteriously. Bah. Fall back to broadcast for other net types. */ struct arpreq req; dest.sin_addr = mess->yiaddr; dest.sin_port = htons(daemon->dhcp_client_port); *((struct sockaddr_in *)&req.arp_pa) = dest; req.arp_ha.sa_family = AF_UNSPEC; memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen); req.arp_flags = ATF_COM; ioctl(daemon->dhcpfd, SIOCSARP, &req); } #elif defined(HAVE_BSD_NETWORK) else { #ifdef HAVE_DUMPFILE if (ntohs(mess->flags) & 0x8000) dest.sin_addr.s_addr = INADDR_BROADCAST; else dest.sin_addr = mess->yiaddr; dest.sin_port = htons(daemon->dhcp_client_port); dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL, (union mysockaddr *)&dest, fd); #endif send_via_bpf(mess, iov.iov_len, iface_addr, &ifr); return; } #endif #ifdef HAVE_SOLARIS_NETWORK setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index)); #endif #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL, (union mysockaddr *)&dest, fd); #endif while(retry_send(sendmsg(fd, &msg, 0))); /* This can fail when, eg, iptables DROPS destination 255.255.255.255 */ if (errno != 0) { inet_ntop(AF_INET, &dest.sin_addr, daemon->addrbuff, ADDRSTRLEN); my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"), daemon->addrbuff, strerror(errno)); } } /* check against secondary interface addresses */ static int check_listen_addrs(struct in_addr local, int if_index, char *label, struct in_addr netmask, struct in_addr broadcast, void *vparam) { 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: 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived. 2) Fills in any netmask and broadcast addresses which have not been explicitly configured. 3) Fills in local (this host) and router (this host or relay) addresses. 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current. Note that the current chain may be superseded later for configured hosts or those coming via gateways. */ static void guess_range_netmask(struct in_addr addr, struct in_addr netmask) { struct dhcp_context *context; for (context = daemon->dhcp; context; context = context->next) if (!(context->flags & CONTEXT_NETMASK) && (is_same_net(addr, context->start, netmask) || is_same_net(addr, context->end, netmask))) { if (context->netmask.s_addr != netmask.s_addr && !(is_same_net(addr, context->start, netmask) && is_same_net(addr, context->end, netmask))) { inet_ntop(AF_INET, &context->start, daemon->dhcp_buff, DHCP_BUFF_SZ); inet_ntop(AF_INET, &context->end, daemon->dhcp_buff2, DHCP_BUFF_SZ); inet_ntop(AF_INET, &netmask, daemon->addrbuff, ADDRSTRLEN); my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"), daemon->dhcp_buff, daemon->dhcp_buff2, daemon->addrbuff); } context->netmask = netmask; } } 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; struct shared_network *share; (void)label; for (share = daemon->shared_networks; share; share = share->next) { #ifdef HAVE_DHCP6 if (share->shared_addr.s_addr == 0) continue; #endif if (share->if_index != 0) { if (share->if_index != if_index) continue; } else { if (share->match_addr.s_addr != local.s_addr) continue; } for (context = daemon->dhcp; context; context = context->next) { if (context->netmask.s_addr != 0 && is_same_net(share->shared_addr, context->start, context->netmask) && is_same_net(share->shared_addr, context->end, context->netmask)) { /* link it onto the current chain if we've not seen it before */ if (context->current == context) { /* For a shared network, we have no way to guess what the default route should be. */ context->router.s_addr = 0; context->local = local; /* Use configured address for Server Identifier */ context->current = param->current; param->current = context; } if (!(context->flags & CONTEXT_BRDCAST)) context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr; } } } guess_range_netmask(local, netmask); for (context = daemon->dhcp; context; context = context->next) { if (context->netmask.s_addr != 0 && 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) { 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; } } } for (relay = daemon->relay4; relay; relay = relay->next) if (relay->local.addr4.s_addr == local.s_addr) relay->iface_index = if_index; return 1; } struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr taddr, struct dhcp_netid *netids) { /* Check is an address is OK for this network, check all possible ranges. Make sure that the address isn't in use by the server itself. */ unsigned int start, end, addr = ntohl(taddr.s_addr); struct dhcp_context *tmp; for (tmp = context; tmp; tmp = tmp->current) if (taddr.s_addr == context->router.s_addr) return NULL; for (tmp = context; tmp; tmp = tmp->current) { start = ntohl(tmp->start.s_addr); end = ntohl(tmp->end.s_addr); if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) && addr >= start && addr <= end && match_netid(tmp->filter, netids, 1)) return tmp; } return NULL; } struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr, struct dhcp_netid *netids) { /* We start of with a set of possible contexts, all on the current physical interface. These are chained on ->current. Here we have an address, and return the actual context corresponding to that address. Note that none may fit, if the address came a dhcp-host and is outside any dhcp-range. In that case we return a static range if possible, or failing that, any context on the correct subnet. (If there's more than one, this is a dodgy configuration: maybe there should be a warning.) */ struct dhcp_context *tmp; if (!(tmp = address_available(context, taddr, netids))) { for (tmp = context; tmp; tmp = tmp->current) if (match_netid(tmp->filter, netids, 1) && is_same_net(taddr, tmp->start, tmp->netmask) && (tmp->flags & CONTEXT_STATIC)) break; if (!tmp) for (tmp = context; tmp; tmp = tmp->current) if (match_netid(tmp->filter, netids, 1) && is_same_net(taddr, tmp->start, tmp->netmask) && !(tmp->flags & CONTEXT_PROXY)) break; } /* Only one context allowed now */ if (tmp) tmp->current = NULL; return tmp; } struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr) { struct dhcp_config *config; for (config = configs; config; config = config->next) if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr) return config; return NULL; } /* Check if and address is in use by sending ICMP ping. This wrapper handles a cache and load-limiting. Return is NULL is address in use, or a pointer to a cache entry recording that it isn't. */ struct ping_result *do_icmp_ping(time_t now, struct in_addr addr, unsigned int hash, int loopback) { static struct ping_result dummy; struct ping_result *r, *victim = NULL; int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/ ((float)PING_WAIT))); /* 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 on us repeatedly. As a final check, if we did more than 60% of the possible ping checks in the last PING_CACHE_TIME, we are in high-load mode, so don't do any more. */ 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 { count++; if (r->addr.s_addr == addr.s_addr) return r; } /* didn't find cached entry */ if ((count >= max) || option_bool(OPT_NO_PING) || loopback) { /* overloaded, or configured not to check, loopback interface, return "not in use" */ dummy.hash = hash; return &dummy; } else if (icmp_ping(addr)) return NULL; /* address in use. */ else { /* 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 = hash; } return victim; } } int address_allocate(struct dhcp_context *context, struct in_addr *addrp, unsigned char *hwaddr, int hw_len, struct dhcp_netid *netids, time_t now, int loopback) { /* 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. */ struct in_addr start, addr; struct dhcp_context *c, *d; int i, pass; unsigned int j; /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good dispersal even with similarly-valued "strings". */ for (j = 0, i = 0; i < hw_len; i++) j = hwaddr[i] + (j << 6) + (j << 16) - j; /* j == 0 is marker */ if (j == 0) j = 1; for (pass = 0; pass <= 1; pass++) for (c = context; c; c = c->current) if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) continue; else if (!match_netid(c->filter, netids, pass)) continue; else { 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. */ for (d = context; d; d = d->current) if (addr.s_addr == d->router.s_addr) break; /* Addresses which end in .255 and .0 are broken in Windows even when using supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0 then 192.168.0.255 is a valid IP address, but not for Windows as it's in the class C range. See KB281579. We therefore don't allocate these addresses to avoid hard-to-diagnose problems. Thanks Bill. */ if (!d && !lease_find_by_addr(addr) && !config_find_by_address(daemon->dhcp_conf, addr) && (!IN_CLASSC(ntohl(addr.s_addr)) || ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0)))) { /* in consec-ip mode, skip addresses equal to the number of addresses rejected by clients. This should avoid the same client being offered the same address after it has rjected it. */ if (option_bool(OPT_CONSEC_ADDR) && c->addr_epoch) c->addr_epoch--; else { struct ping_result *r; if ((r = do_icmp_ping(now, addr, j, loopback))) { /* 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) { *addrp = addr; return 1; } } else { /* 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++; } } } addr.s_addr = htonl(ntohl(addr.s_addr) + 1); if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1)) addr = c->start; } while (addr.s_addr != start.s_addr); } return 0; } void dhcp_read_ethers(void) { FILE *f = fopen(ETHERSFILE, "r"); unsigned int flags; char *buff = daemon->namebuff; char *ip, *cp; struct in_addr addr; unsigned char hwaddr[ETHER_ADDR_LEN]; struct dhcp_config **up, *tmp; struct dhcp_config *config; int count = 0, lineno = 0; addr.s_addr = 0; /* eliminate warning */ if (!f) { my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno)); return; } /* This can be called again on SIGHUP, so remove entries created last time round. */ for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp) { tmp = config->next; if (config->flags & CONFIG_FROM_ETHERS) { *up = tmp; /* cannot have a clid */ if (config->flags & CONFIG_NAME) free(config->hostname); free(config->hwaddr); free(config); } else up = &config->next; } while (fgets(buff, MAXDNAME, f)) { char *host = NULL; lineno++; while (strlen(buff) > 0 && isspace((unsigned char)buff[strlen(buff)-1])) buff[strlen(buff)-1] = 0; if ((*buff == '#') || (*buff == '+') || (*buff == 0)) continue; for (ip = buff; *ip && !isspace((unsigned char)*ip); ip++); for(; *ip && isspace((unsigned char)*ip); ip++) *ip = 0; if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN) { my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno); continue; } /* check for name or dotted-quad */ for (cp = ip; *cp; cp++) if (!(*cp == '.' || (*cp >='0' && *cp <= '9'))) break; if (!*cp) { if (inet_pton(AF_INET, ip, &addr.s_addr) < 1) { my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno); continue; } flags = CONFIG_ADDR; for (config = daemon->dhcp_conf; config; config = config->next) if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr) break; } else { int nomem; if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host)) { if (!nomem) my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno); free(host); continue; } flags = CONFIG_NAME; for (config = daemon->dhcp_conf; config; config = config->next) if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host)) break; } if (config && (config->flags & CONFIG_FROM_ETHERS)) { my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno); continue; } if (!config) { for (config = daemon->dhcp_conf; config; config = config->next) { struct hwaddr_config *conf_addr = config->hwaddr; if (conf_addr && conf_addr->next == NULL && conf_addr->wildcard_mask == 0 && conf_addr->hwaddr_len == ETHER_ADDR_LEN && (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) && memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0) break; } if (!config) { if (!(config = whine_malloc(sizeof(struct dhcp_config)))) continue; config->flags = CONFIG_FROM_ETHERS; config->hwaddr = NULL; config->domain = NULL; config->netid = NULL; config->next = daemon->dhcp_conf; daemon->dhcp_conf = config; } config->flags |= flags; if (flags & CONFIG_NAME) { config->hostname = host; host = NULL; } if (flags & CONFIG_ADDR) config->addr = addr; } config->flags |= CONFIG_NOCLID; if (!config->hwaddr) config->hwaddr = whine_malloc(sizeof(struct hwaddr_config)); if (config->hwaddr) { memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN); config->hwaddr->hwaddr_len = ETHER_ADDR_LEN; config->hwaddr->hwaddr_type = ARPHRD_ETHER; config->hwaddr->wildcard_mask = 0; config->hwaddr->next = NULL; } count++; free(host); } fclose(f); my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count); } /* 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 it gets stripped. The set of legal domain names is bigger than the set of legal hostnames so check here that the domain name is legal as a hostname. NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */ char *host_from_dns(struct in_addr addr) { struct crec *lookup; if (daemon->port == 0) return NULL; /* DNS disabled. */ lookup = cache_find_by_addr(NULL, (union all_addr *)&addr, 0, F_IPV4); if (lookup && (lookup->flags & F_HOSTS)) { char *dot, *hostname = cache_get_name(lookup); dot = strchr(hostname, '.'); if (dot && strlen(dot+1) != 0) { char *d2 = get_domain(addr); if (!d2 || !hostname_isequal(dot+1, d2)) return NULL; /* wrong domain */ } if (!legal_hostname(hostname)) return NULL; safe_strncpy(daemon->dhcp_buff, hostname, 256); strip_hostname(daemon->dhcp_buff); return daemon->dhcp_buff; } return NULL; } static void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz) { struct in_addr giaddr = mess->giaddr; u8 hops = mess->hops; struct dhcp_relay *relay; if (mess->op != BOOTREQUEST) return; for (relay = daemon->relay4; relay; relay = relay->next) if (relay->iface_index != 0 && relay->iface_index == iface_index) break; /* No relay config. */ if (!relay) return; for (; relay; relay = relay->next) if (relay->iface_index != 0 && relay->iface_index == iface_index) { union mysockaddr to; union all_addr from; mess->hops = hops; mess->giaddr = giaddr; if ((mess->hops++) > 20) continue; /* source address == relay address */ from.addr4 = relay->local.addr4; /* already gatewayed ? */ if (giaddr.s_addr) { /* if so check if by us, to stomp on loops. */ if (giaddr.s_addr == relay->local.addr4.s_addr) continue; } else { /* plug in our address */ mess->giaddr.s_addr = relay->local.addr4.s_addr; } to.sa.sa_family = AF_INET; to.in.sin_addr = relay->server.addr4; to.in.sin_port = htons(relay->port); #ifdef HAVE_SOCKADDR_SA_LEN to.in.sin_len = sizeof(struct sockaddr_in); #endif /* Broadcasting to server. */ if (relay->server.addr4.s_addr == 0) { struct ifreq ifr; if (relay->interface) safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE); if (!relay->interface || strchr(relay->interface, '*') || ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1) { my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface); continue; } to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; } #ifdef HAVE_DUMPFILE { union mysockaddr fromsock; fromsock.in.sin_port = htons(daemon->dhcp_server_port); fromsock.in.sin_addr = from.addr4; fromsock.sa.sa_family = AF_INET; dump_packet_udp(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, -1); } #endif 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); if (relay->server.addr4.s_addr == 0) snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface); else inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ); my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2); } } /* restore in case of a local reply. */ mess->giaddr = giaddr; } 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.addr4.s_addr) { if (!relay->interface || wildcard_match(relay->interface, arrival_interface)) return relay->iface_index != 0 ? relay : NULL; } } return NULL; } #endif dnsmasq-2.91/po/0000775000175000017500000000000014765043257011730 5ustar srksrkdnsmasq-2.91/po/ro.po0000664000175000017500000020161214765043257012712 0ustar srksrk# Romanian translations for dnsmasq package. # This file is put in the public domain. # Simon Kelley , 2005. # msgid "" msgstr "" "Project-Id-Version: dnsmasq 2.24\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-06-18 12:24+0100\n" "PO-Revision-Date: 2017-07-17 18:30+0100\n" "Last-Translator: Simon Kelley \n" "Language-Team: Romanian \n" "Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: cache.c:652 msgid "Internal error in cache." msgstr "" # for compatibility purposes the letters â, ă, ş, ţ and î can be written as their look-alike correspondent. #: cache.c:1179 #, fuzzy, c-format msgid "failed to load names from %s: %s" msgstr "încărcarea numelor din %s: %s a eşuat" #: cache.c:1201 dhcp.c:943 #, c-format msgid "bad address at %s line %d" msgstr "adresă greşită în %s, linia %d" #: cache.c:1254 dhcp.c:959 #, c-format msgid "bad name at %s line %d" msgstr "nume greşit în %s linia %d" #: cache.c:1265 #, fuzzy, c-format msgid "read %s - %d names" msgstr "citesc %s - %d adrese" #: cache.c:1381 msgid "cleared cache" msgstr "memoria temporară a fost ştearsă" #: cache.c:1445 #, c-format msgid "No IPv4 address found for %s" msgstr "" #: cache.c:1491 #, c-format msgid "%s is a CNAME, not giving it to the DHCP lease of %s" msgstr "" #: cache.c:1515 #, 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:1760 #, c-format msgid "time %lu" msgstr "" #: cache.c:1761 #, 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:1763 #, c-format msgid "queries forwarded %u, queries answered locally %u" msgstr "" #: cache.c:1766 #, c-format msgid "queries answered from stale cache %u" msgstr "" #: cache.c:1768 #, c-format msgid "queries for authoritative zones %u" msgstr "" #: cache.c:1796 #, c-format msgid "server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums" msgstr "" #: util.c:51 #, fuzzy, c-format msgid "failed to seed the random number generator: %s" msgstr "ascultarea pe socket a eşuat: %s" #: util.c:246 #, fuzzy msgid "failed to allocate memory" msgstr "nu pot încărca %d bytes" #: util.c:305 option.c:696 msgid "could not get memory" msgstr "nu am putut aloca memorie" #: util.c:326 #, fuzzy, c-format msgid "cannot create pipe: %s" msgstr "nu pot citi %s: %s" #: util.c:334 #, fuzzy, c-format msgid "failed to allocate %d bytes" msgstr "nu pot încărca %d bytes" #: util.c:344 #, fuzzy, c-format msgid "failed to reallocate %d bytes" msgstr "nu pot încărca %d bytes" #: util.c:465 #, fuzzy, c-format msgid "cannot read monotonic clock: %s" msgstr "nu pot să activez socket-ul netlink: %s" #: util.c:579 #, c-format msgid "infinite" msgstr "infinit" #: util.c:867 #, fuzzy, c-format msgid "failed to find kernel version: %s" msgstr "activarea socket-ului server-ului DHCP a eşuat: %s" #: option.c:393 msgid "Specify local address(es) to listen on." msgstr "Specificaţi adresele locale deservite." #: option.c:394 msgid "Return ipaddr for all hosts in specified domains." msgstr "Afişează adresele IP ale maşinilor în domeniul dat." #: option.c:395 msgid "Fake reverse lookups for RFC1918 private address ranges." msgstr "Simulează căutări după adresă pentru domenii de adresă private (RFC1918)." #: option.c:396 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)." msgstr "Interpretează adresa IP ca NXDOMAIN (împotriva manipulărilor Verisign)" #: option.c:397 #, 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:398 #, c-format msgid "Specify configuration file (defaults to %s)." msgstr "Specifică fişier de configurare (implicit e %s)." #: option.c:399 msgid "Do NOT fork into the background: run in debug mode." msgstr "NU porneşte în fundal: rulează în modul depanare." #: option.c:400 msgid "Do NOT forward queries with no domain part." msgstr "NU înainta cererile ce nu conţin domeniu DNS." #: option.c:401 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:402 msgid "Expand simple names in /etc/hosts with domain-suffix." msgstr "Adaugă numelor simple din /etc/hosts numele domeniului ca sufix." #: option.c:403 msgid "Don't forward spurious DNS requests from Windows hosts." msgstr "Nu inainta cereri DNS defecte provenite de la maşini Windows." #: option.c:404 msgid "Don't include IPv4 addresses in DNS answers." msgstr "" #: option.c:405 msgid "Don't include IPv6 addresses in DNS answers." msgstr "" #: option.c:406 msgid "Enable DHCP in the range given with lease duration." msgstr "Activează DHCP în domeniul dat cu durată limitată de împrumut." #: option.c:407 #, c-format msgid "Change to this group after startup (defaults to %s)." msgstr "Rulează sub acest grup după pornire (implicit e %s)." #: option.c:408 msgid "Set address or hostname for a specified machine." msgstr "Schimbă adresa sau numele maşinii specificate." #: option.c:409 #, fuzzy msgid "Read DHCP host specs from file." msgstr "nume MX invalid" #: option.c:410 msgid "Read DHCP option specs from file." msgstr "" #: option.c:411 #, fuzzy msgid "Read DHCP host specs from a directory." msgstr "nume MX invalid" #: option.c:412 #, fuzzy msgid "Read DHCP options from a directory." msgstr "nume MX invalid" #: option.c:413 msgid "Evaluate conditional tag expression." msgstr "" #: option.c:414 #, c-format msgid "Do NOT load %s file." msgstr "Nu încarcă fişierul %s." #: option.c:415 #, 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:416 #, fuzzy msgid "Read hosts files from a directory." msgstr "nume MX invalid" #: option.c:417 msgid "Specify interface(s) to listen on." msgstr "Specifică interfeţele deservite." #: option.c:418 msgid "Specify interface(s) NOT to listen on." msgstr "Specifică interfeţele NE-deservite." #: option.c:419 #, fuzzy msgid "Map DHCP user class to tag." msgstr "Leagă clasa de utilizator DHCP cu grup de opţiuni." #: option.c:420 msgid "Map RFC3046 circuit-id to tag." msgstr "" #: option.c:421 msgid "Map RFC3046 remote-id to tag." msgstr "" #: option.c:422 msgid "Map RFC3993 subscriber-id to tag." msgstr "" #: option.c:423 msgid "Specify vendor class to match for PXE requests." msgstr "" #: option.c:424 #, fuzzy msgid "Don't do DHCP for hosts with tag set." msgstr "Nu furniza DHCP maşinilor din grupul de opţiuni." #: option.c:425 #, fuzzy msgid "Force broadcast replies for hosts with tag set." msgstr "Nu furniza DHCP maşinilor din grupul de opţiuni." #: option.c:426 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:427 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:428 #, 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:429 msgid "Return MX records for local hosts." msgstr "Răspunde cu întregistrări MX pentru maşini locale." #: option.c:430 msgid "Specify an MX record." msgstr "Specifică o înregistrare MX." #: option.c:431 msgid "Specify BOOTP options to DHCP server." msgstr "Specifică opţiuni BOOTP serverului DHCP." #: option.c:432 #, 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:433 msgid "Do NOT cache failed search results." msgstr "NU memora rezultatele de căutare DNS eşuatată." #: option.c:434 msgid "Use expired cache data for faster reply." msgstr "" #: option.c:435 #, 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:436 #, fuzzy msgid "Specify options to be sent to DHCP clients." msgstr "Configurează opţiuni în plusce trebuie trimise clienţilor DHCP." #: option.c:437 msgid "DHCP option sent even if the client does not request it." msgstr "" #: option.c:438 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:439 #, 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:440 #, fuzzy msgid "Log DNS queries." msgstr "Înregistrează tranzacţiile." #: option.c:441 #, fuzzy msgid "Force the originating port for upstream DNS queries." msgstr "Forţează acest port pentru datele ce pleacă." #: option.c:442 msgid "Set maximum number of random originating ports for a query." msgstr "" #: option.c:443 msgid "Do NOT read resolv.conf." msgstr "NU citi fişierul resolv.conf" #: option.c:444 #, c-format msgid "Specify path to resolv.conf (defaults to %s)." msgstr "Specifică calea către resolv.conf (implicit e %s)." #: option.c:445 #, fuzzy msgid "Specify path to file with server= options" msgstr "Specifică o cale pentru fişierul PID. (implicit %s)." #: option.c:446 msgid "Specify address(es) of upstream servers with optional domains." msgstr "Specifică adresele server(elor) superioare cu domenii opţionale." #: option.c:447 #, fuzzy msgid "Specify address of upstream servers for reverse address queries" msgstr "Specifică adresele server(elor) superioare cu domenii opţionale." #: option.c:448 msgid "Never forward queries to specified domains." msgstr "Nu înaintează cererile spre domeniile specificate." #: option.c:449 msgid "Specify the domain to be assigned in DHCP leases." msgstr "Specifică domeniul de transmis prin DHCP." #: option.c:450 msgid "Specify default target in an MX record." msgstr "Specifică o ţintă într-o înregistrare MX." #: option.c:451 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:452 #, fuzzy msgid "Specify time-to-live in seconds for negative caching." msgstr "Specifică TTL în secunde pentru răspunsurile din /etc/hosts." #: option.c:453 #, 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:454 #, fuzzy msgid "Specify time-to-live ceiling for cache." msgstr "Specifică TTL în secunde pentru răspunsurile din /etc/hosts." #: option.c:455 #, fuzzy msgid "Specify time-to-live floor for cache." msgstr "Specifică TTL în secunde pentru răspunsurile din /etc/hosts." #: option.c:456 msgid "Retry DNS queries after this many milliseconds." msgstr "" #: option.c:457 #, c-format msgid "Change to this user after startup. (defaults to %s)." msgstr "Rulează sub acest utilizator după pornire. (implicit e %s)." #: option.c:458 #, 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:459 msgid "Display dnsmasq version and copyright information." msgstr "Afişează versiunea dnsmasq şi drepturile de autor." #: option.c:460 msgid "Translate IPv4 addresses from upstream servers." msgstr "Traduce adresele IPv4 de la serverele DNS superioare." #: option.c:461 msgid "Specify a SRV record." msgstr "Specifică o înregistrare SRV." #: option.c:462 msgid "Display this message. Use --help dhcp or --help dhcp6 for known DHCP options." msgstr "" #: option.c:463 #, fuzzy, c-format msgid "Specify path of PID file (defaults to %s)." msgstr "Specifică o cale pentru fişierul PID. (implicit %s)." #: option.c:464 #, c-format msgid "Specify maximum number of DHCP leases (defaults to %s)." msgstr "Specifică numărul maxim de împrumuturi DHCP (implicit %s)." #: option.c:465 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:466 msgid "Specify TXT DNS record." msgstr "Specifică o înregistrare TXT." #: option.c:467 #, fuzzy msgid "Specify PTR DNS record." msgstr "Specifică o înregistrare TXT." #: option.c:468 msgid "Give DNS name to IPv4 address of interface." msgstr "" #: option.c:469 msgid "Bind only to interfaces in use." msgstr "Ascultă doar pe interfeţele active." #: option.c:470 #, c-format msgid "Read DHCP static host information from %s." msgstr "Citeşte informaţii DHCP statice despre maşină din %s." #: option.c:471 msgid "Enable the DBus interface for setting upstream servers, etc." msgstr "Activeaza interfaţa DBus pentru configurarea serverelor superioare." #: option.c:472 msgid "Enable the UBus interface." msgstr "" #: option.c:473 msgid "Do not provide DHCP on this interface, only provide DNS." msgstr "Nu activează DHCP ci doar DNS pe această interfaţă." #: option.c:474 msgid "Enable dynamic address allocation for bootp." msgstr "Activează alocarea dinamică a adreselor pentru BOOTP." #: option.c:475 #, 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:476 msgid "Treat DHCP requests on aliases as arriving from interface." msgstr "" #: option.c:477 msgid "Specify extra networks sharing a broadcast domain for DHCP" msgstr "" #: option.c:478 msgid "Disable ICMP echo address checking in the DHCP server." msgstr "" #: option.c:479 msgid "Shell script to run on DHCP lease creation and destruction." msgstr "" #: option.c:480 msgid "Lua script to run on DHCP lease creation and destruction." msgstr "" #: option.c:481 msgid "Run lease-change scripts as this user." msgstr "" #: option.c:482 msgid "Call dhcp-script with changes to local ARP table." msgstr "" #: option.c:483 msgid "Read configuration from all the files in this directory." msgstr "" #: option.c:484 msgid "Execute file and read configuration from stdin." msgstr "" #: option.c:485 #, fuzzy msgid "Log to this syslog facility or file. (defaults to DAEMON)" msgstr "Rulează sub acest utilizator după pornire. (implicit e %s)." #: option.c:486 msgid "Do not use leasefile." msgstr "" #: option.c:487 #, 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:488 #, c-format msgid "Clear DNS cache when reloading %s." msgstr "" #: option.c:489 msgid "Ignore hostnames provided by DHCP clients." msgstr "" #: option.c:490 msgid "Do NOT reuse filename and server fields for extra DHCP options." msgstr "" #: option.c:491 msgid "Enable integrated read-only TFTP server." msgstr "" #: option.c:492 msgid "Export files by TFTP only from the specified subtree." msgstr "" #: option.c:493 msgid "Add client IP or hardware address to tftp-root." msgstr "" #: option.c:494 msgid "Allow access only to files owned by the user running dnsmasq." msgstr "" #: option.c:495 msgid "Do not terminate the service if TFTP directories are inaccessible." msgstr "" #: option.c:496 #, fuzzy, c-format msgid "Maximum number of concurrent TFTP transfers (defaults to %s)." msgstr "Specifică numărul maxim de împrumuturi DHCP (implicit %s)." #: option.c:497 #, fuzzy msgid "Maximum MTU to use for TFTP transfers." msgstr "Specifică numărul maxim de împrumuturi DHCP (implicit %s)." #: option.c:498 msgid "Disable the TFTP blocksize extension." msgstr "" #: option.c:499 msgid "Convert TFTP filenames to lowercase" msgstr "" #: option.c:500 msgid "Ephemeral port range for use by TFTP transfers." msgstr "" #: option.c:501 msgid "Use only one port for TFTP server." msgstr "" #: option.c:502 msgid "Extra logging for DHCP." msgstr "" #: option.c:503 msgid "Enable async. logging; optionally set queue length." msgstr "" #: option.c:504 msgid "Stop DNS rebinding. Filter private IP ranges when resolving." msgstr "" #: option.c:505 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers." msgstr "" #: option.c:506 msgid "Inhibit DNS-rebind protection on this domain." msgstr "" #: option.c:507 msgid "Always perform DNS queries to all servers." msgstr "" #: option.c:508 msgid "Set tag if client includes matching option in request." msgstr "" #: option.c:509 msgid "Set tag if client provides given name." msgstr "" #: option.c:510 msgid "Use alternative ports for DHCP." msgstr "" #: option.c:511 #, fuzzy msgid "Specify NAPTR DNS record." msgstr "Specifică o înregistrare TXT." #: option.c:512 msgid "Specify lowest port available for DNS query transmission." msgstr "" #: option.c:513 msgid "Specify highest port available for DNS query transmission." msgstr "" #: option.c:514 msgid "Use only fully qualified domain names for DHCP clients." msgstr "" #: option.c:515 msgid "Generate hostnames based on MAC address for nameless clients." msgstr "" #: option.c:516 msgid "Use these DHCP relays as full proxies." msgstr "" #: option.c:517 msgid "Relay DHCP requests to a remote server" msgstr "" #: option.c:518 msgid "Specify alias name for LOCAL DNS name." msgstr "" #: option.c:519 #, fuzzy msgid "Prompt to send to PXE clients." msgstr "Configurează opţiuni în plusce trebuie trimise clienţilor DHCP." #: option.c:520 msgid "Boot service for PXE menu." msgstr "" #: option.c:521 msgid "Check configuration syntax." msgstr "" #: option.c:522 msgid "Add requestor's MAC address to forwarded DNS queries." msgstr "" #: option.c:523 msgid "Strip MAC information from queries." msgstr "" #: option.c:524 msgid "Add specified IP subnet to forwarded DNS queries." msgstr "" #: option.c:525 msgid "Strip ECS information from queries." msgstr "" #: option.c:526 #, fuzzy msgid "Add client identification to forwarded DNS queries." msgstr "Forţează acest port pentru datele ce pleacă." #: option.c:527 #, fuzzy msgid "Proxy DNSSEC validation results from upstream nameservers." msgstr "Traduce adresele IPv4 de la serverele DNS superioare." #: option.c:528 msgid "Attempt to allocate sequential IP addresses to DHCP clients." msgstr "" #: option.c:529 #, fuzzy msgid "Ignore client identifier option sent by DHCP clients." msgstr "Configurează opţiuni în plusce trebuie trimise clienţilor DHCP." #: option.c:530 msgid "Copy connection-track mark from queries to upstream connections." msgstr "" #: option.c:531 msgid "Allow DHCP clients to do their own DDNS updates." msgstr "" #: option.c:532 msgid "Send router-advertisements for interfaces doing DHCPv6" msgstr "" #: option.c:533 msgid "Specify DUID_EN-type DHCPv6 server DUID" msgstr "" #: option.c:534 #, fuzzy msgid "Specify host (A/AAAA and PTR) records" msgstr "Specifică o înregistrare MX." #: option.c:535 msgid "Specify host record in interface subnet" msgstr "" #: option.c:536 msgid "Specify certification authority authorization record" msgstr "" #: option.c:537 #, fuzzy msgid "Specify arbitrary DNS resource record" msgstr "Specifică o înregistrare TXT." #: option.c:538 #, fuzzy msgid "Bind to interfaces in use - check for new interfaces" msgstr "interfaţă necunoscută %s" #: option.c:539 msgid "Export local names to global DNS" msgstr "" #: option.c:540 msgid "Domain to export to global DNS" msgstr "" #: option.c:541 msgid "Set TTL for authoritative replies" msgstr "" #: option.c:542 msgid "Set authoritative zone information" msgstr "" #: option.c:543 msgid "Secondary authoritative nameservers for forward domains" msgstr "" #: option.c:544 msgid "Peers which are allowed to do zone transfer" msgstr "" #: option.c:545 msgid "Specify ipsets to which matching domains should be added" msgstr "" #: option.c:546 msgid "Specify nftables sets to which matching domains should be added" msgstr "" #: option.c:547 msgid "Enable filtering of DNS queries with connection-track marks." msgstr "" #: option.c:548 msgid "Set allowed DNS patterns for a connection-track mark." msgstr "" #: option.c:549 msgid "Specify a domain and address range for synthesised names" msgstr "" #: option.c:550 msgid "Activate DNSSEC validation" msgstr "" #: option.c:551 msgid "Specify trust anchor key digest." msgstr "" #: option.c:552 msgid "Disable upstream checking for DNSSEC debugging." msgstr "" #: option.c:553 msgid "Ensure answers without DNSSEC are in unsigned zones." msgstr "" #: option.c:554 msgid "Don't check DNSSEC signature timestamps until first cache-reload" msgstr "" #: option.c:555 msgid "Timestamp file to verify system clock for DNSSEC" msgstr "" #: option.c:556 msgid "Set MTU, priority, resend-interval and router-lifetime" msgstr "" #: option.c:557 msgid "Do not log routine DHCP." msgstr "" #: option.c:558 msgid "Do not log routine DHCPv6." msgstr "" #: option.c:559 msgid "Do not log RA." msgstr "" #: option.c:560 msgid "Log debugging information." msgstr "" #: option.c:561 msgid "Accept queries only from directly-connected networks." msgstr "" #: option.c:562 msgid "Detect and remove DNS forwarding loops." msgstr "" #: option.c:563 msgid "Ignore DNS responses containing ipaddr." msgstr "" #: option.c:564 msgid "Set TTL in DNS responses with DHCP-derived addresses." msgstr "" #: option.c:565 msgid "Delay DHCP replies for at least number of seconds." msgstr "" #: option.c:566 msgid "Enables DHCPv4 Rapid Commit option." msgstr "" #: option.c:567 msgid "Path to debug packet dump file" msgstr "" #: option.c:568 msgid "Mask which packets to dump" msgstr "" #: option.c:569 msgid "Call dhcp-script when lease expiry changes." msgstr "" #: option.c:570 msgid "Send Cisco Umbrella identifiers including remote IP." msgstr "" #: option.c:571 msgid "Do not log routine TFTP." msgstr "" #: option.c:572 msgid "Suppress round-robin ordering of DNS records." msgstr "" #: option.c:802 #, c-format msgid "" "Usage: dnsmasq [options]\n" "\n" msgstr "" "Utilizare: dnsmasq [opţiuni]\n" "\n" #: option.c:804 #, 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:806 #, fuzzy, c-format msgid "Valid options are:\n" msgstr "Opţiunile valide sunt:\n" #: option.c:853 option.c:1055 #, fuzzy msgid "bad address" msgstr "citesc %s - %d adrese" #: option.c:882 option.c:886 msgid "bad port" msgstr "port invalid" #: option.c:899 option.c:1002 option.c:1048 msgid "interface binding not supported" msgstr "" #: option.c:955 msgid "Cannot resolve server name" msgstr "" #: option.c:991 msgid "cannot use IPv4 server address with IPv6 source address" msgstr "" #: option.c:997 option.c:1043 msgid "interface can only be specified once" msgstr "" #: option.c:1011 option.c:4785 #, fuzzy msgid "bad interface name" msgstr "nume MX invalid" #: option.c:1037 msgid "cannot use IPv6 server address with IPv4 source address" msgstr "" #: option.c:1124 #, fuzzy msgid "bad IPv4 prefix length" msgstr "port invalid" #: option.c:1155 option.c:1165 option.c:1240 option.c:1250 option.c:5360 msgid "error" msgstr "eroare" #: option.c:1207 #, fuzzy msgid "bad IPv6 prefix length" msgstr "port invalid" #: option.c:1467 msgid "inappropriate vendor:" msgstr "" #: option.c:1474 msgid "inappropriate encap:" msgstr "" #: option.c:1500 msgid "unsupported encapsulation for IPv6 option" msgstr "" #: option.c:1514 msgid "bad dhcp-option" msgstr "dhcp-option invalid" #: option.c:1592 #, fuzzy msgid "bad IP address" msgstr "citesc %s - %d adrese" #: option.c:1595 option.c:1734 option.c:3928 #, fuzzy msgid "bad IPv6 address" msgstr "citesc %s - %d adrese" #: option.c:1688 #, fuzzy msgid "bad IPv4 address" msgstr "citesc %s - %d adrese" #: option.c:1761 option.c:1856 msgid "bad domain in dhcp-option" msgstr "domeniu DNS invalid în declaraţia dhcp-option" #: option.c:1900 msgid "dhcp-option too long" msgstr "declararea dhcp-option este prea lungă" #: option.c:1907 msgid "illegal dhcp-match" msgstr "" #: option.c:1966 msgid "illegal repeated flag" msgstr "" #: option.c:1974 msgid "illegal repeated keyword" msgstr "" #: option.c:2056 option.c:5533 #, fuzzy, c-format msgid "cannot access directory %s: %s" msgstr "nu pot citi %s: %s" #: option.c:2102 tftp.c:573 dump.c:72 #, fuzzy, c-format msgid "cannot access %s: %s" msgstr "nu pot citi %s: %s" #: option.c:2219 msgid "setting log facility is not possible under Android" msgstr "" #: option.c:2228 msgid "bad log facility" msgstr "" #: option.c:2281 msgid "bad MX preference" msgstr "preferinţă MX invalidă" #: option.c:2289 msgid "bad MX name" msgstr "nume MX invalid" #: option.c:2304 msgid "bad MX target" msgstr "ţintă MX invalidă" #: option.c:2324 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts" msgstr "" #: option.c:2328 msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts" msgstr "" #: option.c:2447 #, fuzzy msgid "invalid auth-zone" msgstr "pondere invalidă" #: option.c:2589 option.c:2621 #, fuzzy msgid "bad prefix length" msgstr "port invalid" #: option.c:2601 option.c:2642 option.c:2696 #, fuzzy msgid "bad prefix" msgstr "port invalid" #: option.c:2716 msgid "prefix length too small" msgstr "" #: option.c:3010 #, fuzzy msgid "Bad address in --address" msgstr "adresa este folosită" #: option.c:3110 msgid "recompile with HAVE_IPSET defined to enable ipset directives" msgstr "" #: option.c:3117 msgid "recompile with HAVE_NFTSET defined to enable nftset directives" msgstr "" #: option.c:3192 option.c:3210 msgid "recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives" msgstr "" #: option.c:3496 #, fuzzy msgid "bad port range" msgstr "port invalid" #: option.c:3522 msgid "bad bridge-interface" msgstr "" #: option.c:3566 msgid "bad shared-network" msgstr "" #: option.c:3620 msgid "only one tag allowed" msgstr "" #: option.c:3641 option.c:3657 option.c:3783 option.c:3791 option.c:3834 msgid "bad dhcp-range" msgstr "dhcp-range invalid" #: option.c:3675 msgid "inconsistent DHCP range" msgstr "domeniu DHCP inconsistent" #: option.c:3741 msgid "prefix length must be exactly 64 for RA subnets" msgstr "" #: option.c:3743 msgid "prefix length must be exactly 64 for subnet constructors" msgstr "" #: option.c:3746 msgid "prefix length must be at least 64" msgstr "" #: option.c:3749 #, fuzzy msgid "inconsistent DHCPv6 range" msgstr "domeniu DHCP inconsistent" #: option.c:3768 msgid "prefix must be zero with \"constructor:\" argument" msgstr "" #: option.c:3893 option.c:3971 #, fuzzy msgid "bad hex constant" msgstr "dhcp-host invalid" #: option.c:3946 #, fuzzy msgid "bad IPv6 prefix" msgstr "port invalid" #: option.c:3994 #, fuzzy, c-format msgid "duplicate dhcp-host IP address %s" msgstr "adresă IP duplicat %s în declaraţia dhcp-config." #: option.c:4056 #, fuzzy msgid "bad DHCP host name" msgstr "nume MX invalid" #: option.c:4142 #, fuzzy msgid "bad tag-if" msgstr "ţintă MX invalidă" #: option.c:4490 option.c:5046 msgid "invalid port number" msgstr "număr de port invalid" #: option.c:4546 #, fuzzy msgid "bad dhcp-proxy address" msgstr "citesc %s - %d adrese" #: option.c:4627 #, fuzzy msgid "Bad dhcp-relay" msgstr "dhcp-range invalid" #: option.c:4671 msgid "bad RA-params" msgstr "" #: option.c:4681 msgid "bad DUID" msgstr "" #: option.c:4715 #, fuzzy msgid "missing address in alias" msgstr "adresa este folosită" #: option.c:4721 #, fuzzy msgid "invalid alias range" msgstr "pondere invalidă" #: option.c:4770 #, fuzzy msgid "missing address in dynamic host" msgstr "adresa este folosită" #: option.c:4785 #, fuzzy msgid "bad dynamic host" msgstr "nu pot citi %s: %s" #: option.c:4803 option.c:4819 msgid "bad CNAME" msgstr "" #: option.c:4827 msgid "duplicate CNAME" msgstr "" #: option.c:4854 #, fuzzy msgid "bad PTR record" msgstr "înregistrare SRV invalidă" #: option.c:4889 #, fuzzy msgid "bad NAPTR record" msgstr "înregistrare SRV invalidă" #: option.c:4925 #, fuzzy msgid "bad RR record" msgstr "înregistrare SRV invalidă" #: option.c:4958 #, fuzzy msgid "bad CAA record" msgstr "înregistrare SRV invalidă" #: option.c:4987 msgid "bad TXT record" msgstr "înregistrare TXT invalidă" #: option.c:5030 msgid "bad SRV record" msgstr "înregistrare SRV invalidă" #: option.c:5037 msgid "bad SRV target" msgstr "ţintă SRV invalidă" #: option.c:5056 msgid "invalid priority" msgstr "prioritate invalidă" #: option.c:5061 msgid "invalid weight" msgstr "pondere invalidă" #: option.c:5084 #, fuzzy msgid "Bad host-record" msgstr "înregistrare SRV invalidă" #: option.c:5123 #, fuzzy msgid "Bad name in host-record" msgstr "nume invalid în %s" #: option.c:5165 msgid "bad value for dnssec-check-unsigned" msgstr "" #: option.c:5201 #, fuzzy msgid "bad trust anchor" msgstr "port invalid" #: option.c:5217 msgid "bad HEX in trust anchor" msgstr "" #: option.c:5228 msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)" msgstr "" #: option.c:5290 msgid "missing \"" msgstr "lipseşte \"" #: option.c:5347 msgid "bad option" msgstr "opţiune invalidă" #: option.c:5349 msgid "extraneous parameter" msgstr "parametru nerecunoscut" #: option.c:5351 msgid "missing parameter" msgstr "parametru lipsa" #: option.c:5353 #, fuzzy msgid "illegal option" msgstr "opţiune invalidă" #: option.c:5363 #, c-format msgid " in output from %s" msgstr "" #: option.c:5365 #, fuzzy, c-format msgid " at line %d of %s" msgstr "%s la linia %d din %%s" #: option.c:5380 option.c:5683 option.c:5694 #, fuzzy, c-format msgid "read %s" msgstr "citesc %s" #: option.c:5446 #, fuzzy, c-format msgid "cannot execute %s: %s" msgstr "nu pot citi %s: %s" #: option.c:5454 option.c:5615 tftp.c:790 #, c-format msgid "cannot read %s: %s" msgstr "nu pot citi %s: %s" #: option.c:5473 #, fuzzy, c-format msgid "error executing %s: %s" msgstr "accesarea serverului %s a eşuat: %s" #: option.c:5476 #, c-format msgid "%s returns non-zero error code" msgstr "" #: option.c:5775 msgid "junk found in command line" msgstr "" #: option.c:5815 #, c-format msgid "Dnsmasq version %s %s\n" msgstr "dnsmasq versiunea %s %s\n" #: option.c:5816 #, fuzzy, c-format msgid "" "Compile time options: %s\n" "\n" msgstr "" "Opţiuni cu care a fost compilat %s\n" "\n" #: option.c:5817 #, c-format msgid "This software comes with ABSOLUTELY NO WARRANTY.\n" msgstr "Acest program vine FĂRĂ NICI O GARANŢIE.\n" #: option.c:5818 #, 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:5819 #, 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:5836 msgid "try --help" msgstr "" #: option.c:5838 msgid "try -w" msgstr "" #: option.c:5840 #, fuzzy, c-format msgid "bad command line options: %s" msgstr "opţiuni în linie de comandă invalide: %s." #: option.c:5909 #, c-format msgid "CNAME loop involving %s" msgstr "" #: option.c:5950 #, c-format msgid "cannot get host-name: %s" msgstr "nu pot citi numele maşinii: %s" #: option.c:5978 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:5988 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:5991 network.c:1727 dhcp.c:892 #, fuzzy, c-format msgid "failed to read %s: %s" msgstr "nu pot citi %s: %s" #: option.c:6008 #, 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:6029 msgid "there must be a default domain when --dhcp-fqdn is set" msgstr "" #: option.c:6038 msgid "syntax check OK" msgstr "" #: forward.c:107 #, fuzzy, c-format msgid "failed to send packet: %s" msgstr "ascultarea pe socket a eşuat: %s" #: forward.c:715 msgid "discarding DNS reply: subnet option mismatch" msgstr "" #: forward.c:780 #, c-format msgid "nameserver %s refused to do a recursive query" msgstr "serverul DNS %s refuză interogările recursive" #: forward.c:826 #, c-format msgid "possible DNS-rebind attack detected: %s" msgstr "" #: forward.c:1239 #, c-format msgid "reducing DNS packet size for nameserver %s to %d" msgstr "" #: forward.c:1565 #, c-format msgid "ignoring query from non-local network %s (logged only once)" msgstr "" #: forward.c:2139 #, c-format msgid "ignoring query from non-local network %s" msgstr "" #: forward.c:2501 #, fuzzy, c-format msgid "failed to bind server socket to %s: %s" msgstr "activarea socket-ului de ascultare pentru %s a eşuat: %s" #: forward.c:2867 #, fuzzy, c-format msgid "Maximum number of concurrent DNS queries reached (max: %d)" msgstr "Specifică numărul maxim de împrumuturi DHCP (implicit %s)." #: forward.c:2869 #, fuzzy, c-format msgid "Maximum number of concurrent DNS queries to %s reached (max: %d)" msgstr "Specifică numărul maxim de împrumuturi DHCP (implicit %s)." #: network.c:700 #, c-format msgid "stopped listening on %s(#%d): %s port %d" msgstr "" #: network.c:911 #, fuzzy, c-format msgid "failed to create listening socket for %s: %s" msgstr "creearea socket-ului de ascultare a eşuat: %s" #: network.c:1192 #, c-format msgid "listening on %s(#%d): %s port %d" msgstr "" #: network.c:1219 #, fuzzy, c-format msgid "listening on %s port %d" msgstr "nu pot citi %s: %s" #: network.c:1252 #, c-format msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s" msgstr "" #: network.c:1259 msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)" msgstr "" #: network.c:1268 #, fuzzy, c-format msgid "warning: using interface %s instead" msgstr "atenţie: interfaţa %s nu există momentan" #: network.c:1277 #, fuzzy, c-format msgid "warning: no addresses found for interface %s" msgstr "folosim adresele locale doar pentru %S %s" #: network.c:1335 #, 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:1340 msgid "try increasing /proc/sys/net/core/optmem_max" msgstr "" #: network.c:1545 #, 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:1622 #, c-format msgid "ignoring nameserver %s - local interface" msgstr "ignorăm serverul DNS %s - interfaţă locală" #: network.c:1633 #, 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:1643 msgid "more servers are defined but not logged" msgstr "" #: network.c:1654 msgid "(no DNSSEC)" msgstr "" #: network.c:1657 msgid "unqualified" msgstr "invalid" #: network.c:1657 msgid "names" msgstr "" #: network.c:1659 msgid "default" msgstr "" #: network.c:1661 msgid "domain" msgstr "domeniu" #: network.c:1663 #, fuzzy, c-format msgid "using nameserver %s#%d for %s %s%s %s" msgstr "folosim serverul DNS %s#%d pentru %s %s" #: network.c:1667 #, fuzzy, c-format msgid "NOT using nameserver %s#%d - query loop detected" msgstr "folosim serverul DNS %s#%d pentru %s %s" #: network.c:1670 #, fuzzy, c-format msgid "using nameserver %s#%d(via %s)" msgstr "folosim serverul DNS %s#%d" #: network.c:1672 #, c-format msgid "using nameserver %s#%d" msgstr "folosim serverul DNS %s#%d" #: network.c:1687 #, fuzzy, c-format msgid "using only locally-known addresses for %s" msgstr "folosim adresele locale doar pentru %S %s" #: network.c:1690 #, fuzzy, c-format msgid "using standard nameservers for %s" msgstr "folosim serverul DNS %s#%d pentru %s %s" #: network.c:1694 #, fuzzy, c-format msgid "using %d more local addresses" msgstr "folosim serverul DNS %s#%d" #: network.c:1696 #, fuzzy, c-format msgid "using %d more nameservers" msgstr "folosim serverul DNS %s#%d" #: dnsmasq.c:192 msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform" msgstr "" #: dnsmasq.c:207 msgid "no root trust anchor provided for DNSSEC" msgstr "" #: dnsmasq.c:210 msgid "cannot reduce cache size from default when DNSSEC enabled" msgstr "" #: dnsmasq.c:212 #, 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:218 #, 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:225 msgid "cannot use --conntrack AND --query-port" msgstr "" #: dnsmasq.c:231 #, 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:236 msgid "asynchronous logging is not available under Solaris" msgstr "" #: dnsmasq.c:241 msgid "asynchronous logging is not available under Android" msgstr "" #: dnsmasq.c:246 #, 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:251 #, 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:256 #, fuzzy msgid "Ubus not available: set HAVE_UBUS in src/config.h" msgstr "DBus nu este disponibil: puneţi HAVE_DBUS in src/config.h" #: dnsmasq.c:267 msgid "max_port cannot be smaller than min_port" msgstr "" #: dnsmasq.c:271 msgid "port_limit must not be larger than available port range" msgstr "" #: dnsmasq.c:278 msgid "--auth-server required when an auth zone is defined." msgstr "" #: dnsmasq.c:283 msgid "zone serial must be configured in --auth-soa" msgstr "" #: dnsmasq.c:303 msgid "dhcp-range constructor not available on this platform" msgstr "" #: dnsmasq.c:377 msgid "cannot set --bind-interfaces and --bind-dynamic" msgstr "" #: dnsmasq.c:380 #, c-format msgid "failed to find list of interfaces: %s" msgstr "enumerarea interfeţelor a eşuat: %s" #: dnsmasq.c:389 #, c-format msgid "unknown interface %s" msgstr "interfaţă necunoscută %s" #: dnsmasq.c:396 #, 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" #: dnsmasq.c:440 #, fuzzy msgid "Packet dumps not available: set HAVE_DUMP in src/config.h" msgstr "DBus nu este disponibil: puneţi HAVE_DBUS in src/config.h" #: dnsmasq.c:448 dnsmasq.c:1232 #, c-format msgid "DBus error: %s" msgstr "eroare DBus: %s" #: dnsmasq.c:451 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:459 dnsmasq.c:1253 #, fuzzy, c-format msgid "UBus error: %s" msgstr "eroare DBus: %s" #: dnsmasq.c:462 #, fuzzy msgid "UBus not available: set HAVE_UBUS in src/config.h" msgstr "DBus nu este disponibil: puneţi HAVE_DBUS in src/config.h" #: dnsmasq.c:492 #, c-format msgid "unknown user or group: %s" msgstr "" #: dnsmasq.c:568 #, c-format msgid "process is missing required capability %s" msgstr "" #: dnsmasq.c:600 #, c-format msgid "cannot chdir to filesystem root: %s" msgstr "" #: dnsmasq.c:852 #, fuzzy, c-format msgid "started, version %s DNS disabled" msgstr "am pornit, versiunea %s memorie temporară dezactivată" #: dnsmasq.c:857 #, c-format msgid "started, version %s cachesize %d" msgstr "am ponit, versiunea %s memorie temporară %d" #: dnsmasq.c:859 msgid "cache size greater than 10000 may cause performance issues, and is unlikely to be useful." msgstr "" #: dnsmasq.c:862 #, c-format msgid "started, version %s cache disabled" msgstr "am pornit, versiunea %s memorie temporară dezactivată" #: dnsmasq.c:865 msgid "DNS service limited to local subnets" msgstr "" #: dnsmasq.c:868 #, c-format msgid "compile time options: %s" msgstr "compilat cu opţiunile: %s" #: dnsmasq.c:877 msgid "DBus support enabled: connected to system bus" msgstr "suportul DBus activ: sunt conectat la magistrala sistem" #: dnsmasq.c:879 msgid "DBus support enabled: bus connection pending" msgstr "suportul DBus activ: aştept conexiunea la magistrală" #: dnsmasq.c:887 #, fuzzy msgid "UBus support enabled: connected to system bus" msgstr "suportul DBus activ: sunt conectat la magistrala sistem" #: dnsmasq.c:889 #, fuzzy msgid "UBus support enabled: bus connection pending" msgstr "suportul DBus activ: aştept conexiunea la magistrală" #: dnsmasq.c:909 msgid "DNSSEC validation enabled but all unsigned answers are trusted" msgstr "" #: dnsmasq.c:911 msgid "DNSSEC validation enabled" msgstr "" #: dnsmasq.c:915 msgid "DNSSEC signature timestamps not checked until receipt of SIGINT" msgstr "" #: dnsmasq.c:918 msgid "DNSSEC signature timestamps not checked until system time valid" msgstr "" #: dnsmasq.c:921 #, c-format msgid "configured with trust anchor for %s keytag %u" msgstr "" # for compatibility purposes the letters â, ă, ş, ţ and î can be written as their look-alike correspondent. #: dnsmasq.c:927 #, fuzzy, c-format msgid "warning: failed to change owner of %s: %s" msgstr "încărcarea numelor din %s: %s a eşuat" #: dnsmasq.c:932 msgid "setting --bind-interfaces option because of OS limitations" msgstr "specific opţiunea --bind-interfaces din cauza limitărilor SO" #: dnsmasq.c:945 #, c-format msgid "warning: interface %s does not currently exist" msgstr "atenţie: interfaţa %s nu există momentan" #: dnsmasq.c:950 msgid "warning: ignoring resolv-file flag because no-resolv is set" msgstr "" #: dnsmasq.c:953 #, fuzzy msgid "warning: no upstream servers configured" msgstr "configurăm serverele superioare prin Dbus" #: dnsmasq.c:957 #, c-format msgid "asynchronous logging enabled, queue limit is %d messages" msgstr "" #: dnsmasq.c:978 msgid "IPv6 router advertisement enabled" msgstr "" #: dnsmasq.c:983 #, c-format msgid "DHCP, sockets bound exclusively to interface %s" msgstr "" #: dnsmasq.c:1000 msgid "root is " msgstr "" #: dnsmasq.c:1000 #, fuzzy msgid "enabled" msgstr "dezactivat" #: dnsmasq.c:1002 msgid "secure mode" msgstr "" #: dnsmasq.c:1003 #, fuzzy msgid "single port mode" msgstr "număr de port invalid" #: dnsmasq.c:1006 #, c-format msgid "warning: %s inaccessible" msgstr "" #: dnsmasq.c:1010 #, c-format msgid "warning: TFTP directory %s inaccessible" msgstr "" #: dnsmasq.c:1036 #, c-format msgid "restricting maximum simultaneous TFTP transfers to %d" msgstr "" #: dnsmasq.c:1095 #, c-format msgid "error binding DHCP socket to device %s" msgstr "" #: dnsmasq.c:1229 msgid "connected to system DBus" msgstr "magistrala sistem Dbus conectată" #: dnsmasq.c:1250 #, fuzzy msgid "connected to system UBus" msgstr "magistrala sistem Dbus conectată" #: dnsmasq.c:1416 #, c-format msgid "cannot fork into background: %s" msgstr "" #: dnsmasq.c:1420 #, fuzzy, c-format msgid "failed to create helper: %s" msgstr "nu pot citi %s: %s" #: dnsmasq.c:1424 #, c-format msgid "setting capabilities failed: %s" msgstr "" # for compatibility purposes the letters â, ă, ş, ţ and î can be written as their look-alike correspondent. #: dnsmasq.c:1428 #, 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:1432 #, fuzzy, c-format msgid "failed to change group-id to %s: %s" msgstr "încărcarea numelor din %s: %s a eşuat" #: dnsmasq.c:1436 #, fuzzy, c-format msgid "failed to open pidfile %s: %s" msgstr "nu pot citi %s: %s" #: dnsmasq.c:1440 #, fuzzy, c-format msgid "cannot open log %s: %s" msgstr "nu pot deschide %s:%s" #: dnsmasq.c:1444 #, fuzzy, c-format msgid "failed to load Lua script: %s" msgstr "nu pot încărca %s: %s" #: dnsmasq.c:1448 #, c-format msgid "TFTP directory %s inaccessible: %s" msgstr "" #: dnsmasq.c:1452 #, fuzzy, c-format msgid "cannot create timestamp file %s: %s" msgstr "nu pot creea sau deschide fişierul cu împrumuturi: %s" #: dnsmasq.c:1536 #, c-format msgid "script process killed by signal %d" msgstr "" #: dnsmasq.c:1540 #, c-format msgid "script process exited with status %d" msgstr "" #: dnsmasq.c:1544 #, fuzzy, c-format msgid "failed to execute %s: %s" msgstr "accesarea serverului %s a eşuat: %s" #: dnsmasq.c:1584 msgid "now checking DNSSEC signature timestamps" msgstr "" #: dnsmasq.c:1619 dnssec.c:160 dnssec.c:204 #, fuzzy, c-format msgid "failed to update mtime on %s: %s" msgstr "nu pot citi %s: %s" #: dnsmasq.c:1631 msgid "exiting on receipt of SIGTERM" msgstr "am primit SIGTERM, am terminat" #: dnsmasq.c:1659 #, fuzzy, c-format msgid "failed to access %s: %s" msgstr "accesarea serverului %s a eşuat: %s" #: dnsmasq.c:1690 #, c-format msgid "reading %s" msgstr "citesc %s" #: dnsmasq.c:1706 #, 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:51 #, c-format msgid "cannot create DHCP socket: %s" msgstr "nu pot creea socket DHCP: %s" #: dhcp.c:66 #, c-format msgid "failed to set options on DHCP socket: %s" msgstr "configurarea opţiunilor socketului DHCP a eşuat: %s" #: dhcp.c:87 #, 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:99 #, c-format msgid "failed to bind DHCP server socket: %s" msgstr "activarea socket-ului server-ului DHCP a eşuat: %s" #: dhcp.c:125 #, c-format msgid "cannot create ICMP raw socket: %s." msgstr "nu pot creea socket ICMP raw: %s." #: dhcp.c:254 dhcp6.c:186 #, fuzzy, c-format msgid "unknown interface %s in bridge-interface" msgstr "interfaţă necunoscută %s" #: dhcp.c:295 #, c-format msgid "DHCP packet received on %s which has no address" msgstr "" #: dhcp.c:429 #, c-format msgid "ARP-cache injection failed: %s" msgstr "" #: dhcp.c:490 #, c-format msgid "Error sending DHCP packet to %s: %s" msgstr "" #: dhcp.c:547 #, 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:930 #, c-format msgid "bad line at %s line %d" msgstr "linie invalidă în %s rândul %d" #: dhcp.c:973 #, c-format msgid "ignoring %s line %d, duplicate name or IP address" msgstr "" #: dhcp.c:1034 #, c-format msgid "read %s - %d addresses" msgstr "citesc %s - %d adrese" #: dhcp.c:1136 #, c-format msgid "Cannot broadcast DHCP relay via interface %s" msgstr "" #: dhcp.c:1160 #, c-format msgid "broadcast via %s" msgstr "" #: dhcp.c:1163 rfc3315.c:2219 #, c-format msgid "DHCP relay at %s -> %s" msgstr "" #: lease.c:64 #, c-format msgid "ignoring invalid line in lease database: %s %s %s %s ..." msgstr "" #: lease.c:101 #, c-format msgid "ignoring invalid line in lease database, bad address: %s" msgstr "" #: lease.c:108 msgid "too many stored leases" msgstr "prea multe împrumuturi stocate" #: lease.c:176 #, 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:185 #, fuzzy msgid "failed to parse lease database cleanly" msgstr "nu pot citi %s: %s" #: lease.c:188 #, fuzzy, c-format msgid "failed to read lease file %s: %s" msgstr "nu pot citi %s: %s" #: lease.c:204 #, fuzzy, c-format msgid "cannot run lease-init script %s: %s" msgstr "nu pot citi %s: %s" #: lease.c:210 #, c-format msgid "lease-init script returned exit code %s" msgstr "" #: lease.c:381 #, fuzzy, c-format msgid "failed to write %s: %s (retry in %u s)" msgstr "nu pot citi %s: %s" #: lease.c:955 #, c-format msgid "Ignoring domain %s for DHCP host name %s" msgstr "" #: rfc2131.c:378 msgid "with subnet selector" msgstr "cu selectorul de subreţea" #: rfc2131.c:383 msgid "via" msgstr "prin" #: rfc2131.c:389 #, 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:403 #, fuzzy, c-format msgid "%u available DHCP subnet: %s/%s" msgstr "nici un domeniu de adrese disponibil pentru cererea DHCP %s %s" #: rfc2131.c:409 rfc3315.c:320 #, c-format msgid "%u available DHCP range: %s -- %s" msgstr "" #: rfc2131.c:521 #, fuzzy, c-format msgid "%u vendor class: %s" msgstr "eroare DBus: %s" #: rfc2131.c:523 #, fuzzy, c-format msgid "%u user class: %s" msgstr "eroare DBus: %s" #: rfc2131.c:557 msgid "disabled" msgstr "dezactivat" #: rfc2131.c:598 rfc2131.c:1087 rfc2131.c:1536 rfc3315.c:633 rfc3315.c:816 #: rfc3315.c:1122 msgid "ignored" msgstr "ignorat" #: rfc2131.c:613 rfc2131.c:1340 rfc3315.c:868 msgid "address in use" msgstr "adresa este folosită" #: rfc2131.c:627 rfc2131.c:1141 msgid "no address available" msgstr "nici o adresă disponibilă" #: rfc2131.c:634 rfc2131.c:1302 msgid "wrong network" msgstr "reţea greşită" #: rfc2131.c:649 msgid "no address configured" msgstr "adresă lipsă" #: rfc2131.c:655 rfc2131.c:1353 msgid "no leases left" msgstr "nu mai am de unde să împrumut" #: rfc2131.c:756 rfc3315.c:500 #, c-format msgid "%u client provides name: %s" msgstr "" #: rfc2131.c:885 msgid "PXE BIS not supported" msgstr "" #: rfc2131.c:1054 rfc3315.c:1223 #, fuzzy, c-format msgid "disabling DHCP static address %s for %s" msgstr "dezactivăm adresele DHCP statice %s" #: rfc2131.c:1075 msgid "unknown lease" msgstr "împrumut necunoscut" #: rfc2131.c:1110 #, c-format msgid "not using configured address %s because it is leased to %s" msgstr "" #: rfc2131.c:1120 #, c-format msgid "not using configured address %s because it is in use by the server or relay" msgstr "" #: rfc2131.c:1123 #, c-format msgid "not using configured address %s because it was previously declined" msgstr "" #: rfc2131.c:1139 rfc2131.c:1346 msgid "no unique-id" msgstr "" #: rfc2131.c:1238 msgid "wrong server-ID" msgstr "" #: rfc2131.c:1257 msgid "wrong address" msgstr "adresă greşită" #: rfc2131.c:1275 rfc3315.c:976 msgid "lease not found" msgstr "împrumutul nu a fost găsit" #: rfc2131.c:1310 msgid "address not available" msgstr "adresă indisponibilă" #: rfc2131.c:1321 msgid "static lease available" msgstr "împrumut static este disponibil" #: rfc2131.c:1325 msgid "address reserved" msgstr "adresă rezervată" #: rfc2131.c:1334 #, c-format msgid "abandoning lease to %s of %s" msgstr "" #: rfc2131.c:1870 #, c-format msgid "%u bootfile name: %s" msgstr "" #: rfc2131.c:1879 #, fuzzy, c-format msgid "%u server name: %s" msgstr "eroare DBus: %s" #: rfc2131.c:1889 #, fuzzy, c-format msgid "%u next server: %s" msgstr "eroare DBus: %s" #: rfc2131.c:1893 #, c-format msgid "%u broadcast response" msgstr "" #: rfc2131.c:1956 #, 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:2267 msgid "PXE menu too large" msgstr "" #: rfc2131.c:2430 rfc3315.c:1517 #, fuzzy, c-format msgid "%u requested options: %s" msgstr "compilat cu opţiunile: %s" #: rfc2131.c:2747 #, c-format msgid "cannot send RFC3925 option: too many options for enterprise number %d" msgstr "" #: rfc2131.c:2810 #, c-format msgid "%u reply delay: %d" msgstr "" #: netlink.c:86 #, fuzzy, c-format msgid "cannot create netlink socket: %s" msgstr "nu pot să activez socket-ul netlink: %s" #: netlink.c:379 #, fuzzy, c-format msgid "netlink returns error: %s" msgstr "eroare DBus: %s" #: dbus.c:491 #, c-format msgid "Enabling --%s option from D-Bus" msgstr "" #: dbus.c:496 #, c-format msgid "Disabling --%s option from D-Bus" msgstr "" #: dbus.c:857 msgid "setting upstream servers from DBus" msgstr "configurăm serverele superioare prin Dbus" #: dbus.c:907 msgid "could not register a DBus message handler" msgstr "nu pot activa o interfaţă de mesaje DBus" #: bpf.c:261 #, c-format msgid "cannot create DHCP BPF socket: %s" msgstr "nu pot creea socket DHCP BPF: %s" #: bpf.c:289 #, 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" #: bpf.c:374 #, fuzzy, c-format msgid "cannot create PF_ROUTE socket: %s" msgstr "nu pot creea socket DHCP: %s" #: bpf.c:395 msgid "Unknown protocol version from route socket" msgstr "" #: helper.c:150 msgid "lease() function missing in Lua script" msgstr "" #: tftp.c:353 msgid "unable to get free port for TFTP" msgstr "" #: tftp.c:369 #, c-format msgid "unsupported request from %s" msgstr "" #: tftp.c:520 #, fuzzy, c-format msgid "file %s not found for %s" msgstr "fișier %s nu a fost găsit pentru %s" #: tftp.c:609 #, c-format msgid "ignoring packet from %s (TID mismatch)" msgstr "" #: tftp.c:662 #, fuzzy, c-format msgid "failed sending %s to %s" msgstr "nu pot citi %s: %s" #: tftp.c:662 #, c-format msgid "sent %s to %s" msgstr "" #: tftp.c:712 #, c-format msgid "error %d %s received from %s" msgstr "" #: log.c:203 #, c-format msgid "overflow: %d log entries lost" msgstr "" #: log.c:281 #, c-format msgid "log failed: %s" msgstr "" #: log.c:490 msgid "FAILED to start up" msgstr "pornirea A EŞUAT" #: conntrack.c:63 #, c-format msgid "Conntrack connection mark retrieval failed: %s" msgstr "" #: dhcp6.c:51 #, fuzzy, c-format msgid "cannot create DHCPv6 socket: %s" msgstr "nu pot creea socket DHCP: %s" #: dhcp6.c:72 #, 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:84 #, fuzzy, c-format msgid "failed to bind DHCPv6 server socket: %s" msgstr "activarea socket-ului server-ului DHCP a eşuat: %s" #: rfc3315.c:173 #, 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:182 #, 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:317 #, fuzzy, c-format msgid "%u available DHCPv6 subnet: %s/%d" msgstr "nici un domeniu de adrese disponibil pentru cererea DHCP %s %s" #: rfc3315.c:400 #, fuzzy, c-format msgid "%u vendor class: %u" msgstr "eroare DBus: %s" #: rfc3315.c:448 #, fuzzy, c-format msgid "%u client MAC address: %s" msgstr "nu exista interfaţă pentru adresa %s" #: rfc3315.c:763 rfc3315.c:860 #, fuzzy msgid "address unavailable" msgstr "adresă indisponibilă" #: rfc3315.c:775 rfc3315.c:904 rfc3315.c:1273 msgid "success" msgstr "" #: rfc3315.c:790 rfc3315.c:799 rfc3315.c:912 rfc3315.c:914 rfc3315.c:1048 #, fuzzy msgid "no addresses available" msgstr "nici o adresă disponibilă" #: rfc3315.c:891 msgid "not on link" msgstr "" #: rfc3315.c:980 rfc3315.c:1181 rfc3315.c:1262 msgid "no binding found" msgstr "" #: rfc3315.c:1017 msgid "deprecated" msgstr "" #: rfc3315.c:1024 #, fuzzy msgid "address invalid" msgstr "adresa este folosită" #: rfc3315.c:1082 rfc3315.c:1084 msgid "confirm failed" msgstr "" #: rfc3315.c:1099 #, fuzzy msgid "all addresses still on link" msgstr "adresă greşită în %s, linia %d" #: rfc3315.c:1190 msgid "release received" msgstr "" #: rfc3315.c:2200 #, c-format msgid "Cannot multicast DHCP relay via interface %s" msgstr "" #: rfc3315.c:2216 #, c-format msgid "multicast via %s" msgstr "" #: dhcp-common.c:187 #, c-format msgid "Ignoring duplicate dhcp-option %d" msgstr "" #: dhcp-common.c:264 #, c-format msgid "%u tags: %s" msgstr "" #: dhcp-common.c:484 #, c-format msgid "%s has more than one address in hostsfile, using %s for DHCP" msgstr "" #: dhcp-common.c:518 #, 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:738 #, c-format msgid "Known DHCP options:\n" msgstr "" #: dhcp-common.c:749 #, c-format msgid "Known DHCPv6 options:\n" msgstr "" #: dhcp-common.c:946 msgid ", prefix deprecated" msgstr "" #: dhcp-common.c:949 #, c-format msgid ", lease time " msgstr "" #: dhcp-common.c:991 #, c-format msgid "%s stateless on %s%.0s%.0s%s" msgstr "" #: dhcp-common.c:993 #, 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:995 #, c-format msgid "%s, proxy on subnet %.0s%s%.0s%.0s" msgstr "" #: dhcp-common.c:996 #, fuzzy, c-format msgid "%s, IP range %s -- %s%s%.0s" msgstr "DHCP, domeniu IP %s -- %s, timpul reînoirii %s" #: dhcp-common.c:1009 #, c-format msgid "DHCPv4-derived IPv6 names on %s%s" msgstr "" #: dhcp-common.c:1012 #, 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:1043 #, c-format msgid "DHCP relay from %s via %s" msgstr "" #: dhcp-common.c:1045 #, c-format msgid "DHCP relay from %s to %s via %s" msgstr "" #: dhcp-common.c:1048 #, c-format msgid "DHCP relay from %s to %s" msgstr "" #: radv.c:110 #, fuzzy, c-format msgid "cannot create ICMPv6 socket: %s" msgstr "nu pot creea socket DHCP: %s" #: auth.c:462 #, c-format msgid "ignoring zone transfer request from %s" msgstr "" #: ipset.c:99 #, fuzzy, c-format msgid "failed to create IPset control socket: %s" msgstr "creearea socket-ului de ascultare a eşuat: %s" #: ipset.c:211 #, fuzzy, c-format msgid "failed to update ipset %s: %s" msgstr "nu pot citi %s: %s" #: pattern.c:29 #, c-format msgid "[pattern.c:%d] Assertion failure: %s" msgstr "" #: pattern.c:142 #, c-format msgid "Invalid DNS name: Invalid character %c." msgstr "" #: pattern.c:151 msgid "Invalid DNS name: Empty label." msgstr "" #: pattern.c:156 msgid "Invalid DNS name: Label starts with hyphen." msgstr "" #: pattern.c:170 msgid "Invalid DNS name: Label ends with hyphen." msgstr "" #: pattern.c:176 #, c-format msgid "Invalid DNS name: Label is too long (%zu)." msgstr "" #: pattern.c:184 #, c-format msgid "Invalid DNS name: Not enough labels (%zu)." msgstr "" #: pattern.c:189 msgid "Invalid DNS name: Final label is fully numeric." msgstr "" #: pattern.c:199 msgid "Invalid DNS name: \"local\" pseudo-TLD." msgstr "" #: pattern.c:204 #, c-format msgid "DNS name has invalid length (%zu)." msgstr "" #: pattern.c:258 #, c-format msgid "Invalid DNS name pattern: Invalid character %c." msgstr "" #: pattern.c:267 msgid "Invalid DNS name pattern: Empty label." msgstr "" #: pattern.c:272 msgid "Invalid DNS name pattern: Label starts with hyphen." msgstr "" #: pattern.c:285 msgid "Invalid DNS name pattern: Wildcard character used more than twice per label." msgstr "" #: pattern.c:295 msgid "Invalid DNS name pattern: Label ends with hyphen." msgstr "" #: pattern.c:301 #, c-format msgid "Invalid DNS name pattern: Label is too long (%zu)." msgstr "" #: pattern.c:309 #, c-format msgid "Invalid DNS name pattern: Not enough labels (%zu)." msgstr "" #: pattern.c:314 msgid "Invalid DNS name pattern: Wildcard within final two labels." msgstr "" #: pattern.c:319 msgid "Invalid DNS name pattern: Final label is fully numeric." msgstr "" #: pattern.c:329 msgid "Invalid DNS name pattern: \"local\" pseudo-TLD." msgstr "" #: pattern.c:334 #, c-format msgid "DNS name pattern has invalid length after removing wildcards (%zu)." msgstr "" #: dnssec.c:206 msgid "system time considered valid, now checking DNSSEC signature timestamps." msgstr "" #: dnssec.c:1018 #, c-format msgid "Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support" msgstr "" #: blockdata.c:55 #, c-format msgid "pool memory in use %zu, max %zu, allocated %zu" msgstr "" #: tables.c:61 #, fuzzy, c-format msgid "failed to access pf devices: %s" msgstr "accesarea serverului %s a eşuat: %s" #: tables.c:74 #, fuzzy, c-format msgid "warning: no opened pf devices %s" msgstr "folosim adresele locale doar pentru %S %s" #: tables.c:82 #, fuzzy, c-format msgid "error: cannot use table name %s" msgstr "nu pot citi numele maşinii: %s" #: tables.c:90 #, c-format msgid "error: cannot strlcpy table name %s" msgstr "" #: tables.c:101 #, fuzzy, c-format msgid "IPset: error: %s" msgstr "eroare DBus: %s" #: tables.c:108 msgid "info: table created" msgstr "" #: tables.c:133 #, c-format msgid "warning: DIOCR%sADDRS: %s" msgstr "" #: tables.c:137 #, fuzzy, c-format msgid "%d addresses %s" msgstr "citesc %s - %d adrese" #: inotify.c:62 #, fuzzy, c-format msgid "cannot access path %s: %s" msgstr "nu pot citi %s: %s" #: inotify.c:95 #, fuzzy, c-format msgid "failed to create inotify: %s" msgstr "nu pot citi %s: %s" #: inotify.c:111 #, c-format msgid "too many symlinks following %s" msgstr "" #: inotify.c:127 #, c-format msgid "directory %s for resolv-file is missing, cannot poll" msgstr "" #: inotify.c:131 inotify.c:200 #, fuzzy, c-format msgid "failed to create inotify for %s: %s" msgstr "creearea socket-ului de ascultare a eşuat: %s" #: inotify.c:178 inotify.c:185 #, fuzzy, c-format msgid "bad dynamic directory %s: %s" msgstr "nu pot citi %s: %s" #: inotify.c:186 #, fuzzy msgid "not a directory" msgstr "nu pot citi %s: %s" #: inotify.c:299 #, c-format msgid "inotify: %s removed" msgstr "" #: inotify.c:301 #, c-format msgid "inotify: %s new or modified" msgstr "" #: inotify.c:309 #, c-format msgid "inotify: flushed %u names read from %s" msgstr "" #: dump.c:68 #, fuzzy, c-format msgid "cannot create %s: %s" msgstr "nu pot citi %s: %s" #: dump.c:74 #, fuzzy, c-format msgid "bad header in %s" msgstr "adresa este folosită" #: dump.c:287 #, fuzzy msgid "failed to write packet dump" msgstr "ascultarea pe socket a eşuat: %s" #: dump.c:289 #, c-format msgid "%u dumping packet %u mask 0x%04x" msgstr "" #: dump.c:291 #, c-format msgid "dumping packet %u mask 0x%04x" msgstr "" #: ubus.c:79 #, c-format msgid "UBus subscription callback: %s subscriber(s)" msgstr "" #: ubus.c:99 #, fuzzy, c-format msgid "Cannot reconnect to UBus: %s" msgstr "nu pot deschide %s:%s" #: ubus.c:135 msgid "Cannot set UBus listeners: no connection" msgstr "" #: ubus.c:155 msgid "Cannot poll UBus listeners: no connection" msgstr "" #: ubus.c:168 msgid "Disconnecting from UBus" msgstr "" #: ubus.c:179 ubus.c:326 #, c-format msgid "UBus command failed: %d (%s)" msgstr "" #: hash-questions.c:40 msgid "Failed to create SHA-256 hash object" msgstr "" #: nftset.c:35 #, fuzzy msgid "failed to create nftset context" msgstr "creearea socket-ului de ascultare a eşuat: %s" #, fuzzy #~ msgid "bad IPv4 prefix" #~ msgstr "port invalid" #, fuzzy #~ msgid "Cannot add object to UBus: %s" #~ msgstr "nu pot deschide %s:%s" #, fuzzy #~ msgid "Failed to send UBus event: %s" #~ msgstr "ascultarea pe socket a eşuat: %s" #~ 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" #, fuzzy #~ msgid "unknown prefix-class %d" #~ msgstr "împrumut necunoscut" #, 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" #~ msgid "failed to set IPV6 options on listening socket: %s" #~ msgstr "configurarea opţiunilor IPv6 a eşuat pe socket-ul de ascultare: %s" #~ msgid "failed to bind listening socket for %s: %s" #~ msgstr "activarea socket-ului de ascultare pentru %s 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" #~ 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" #~ msgid "ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h" #~ msgstr "Integrarea cu ISC dhcpd nu este disponibilă:puneţi HAVE_ISC_HEADER în src/config.h" #, fuzzy #~ msgid "illegal domain %s in dhcp-config directive." #~ msgstr "adresă IP duplicat %s în declaraţia dhcp-config." #~ msgid "running as root" #~ msgstr "rulez ca root" #, fuzzy #~ msgid "read %s - %d hosts" #~ msgstr "citesc %s - %d adrese" #~ msgid "domains" #~ msgstr "domenii" #~ msgid "Ignoring DHCP host name %s because it has an illegal domain part" #~ msgstr "Ignor numele DHCP al maşinii %s deoarece are domeniu DNS ilegal" #~ msgid "Display this message." #~ msgstr "Afişează acest mesaj." #~ msgid "failed to read %s:%m" #~ msgstr "citirea %s:%n a eşuat" #, fuzzy #~ msgid "cannot send encapsulated option %d: no space left in wrapper" #~ msgstr "nu pot trimite opţiunea DHCP %d: nu mai este loc în pachet" #~ msgid "More than one vendor class matches, using %s" #~ msgstr "Se potrivesc mai multe clase de mărci de interfeţe, folosim %s" #~ msgid "forwarding table overflow: check for server loops." #~ msgstr "depăşire de memorie în tabela cu înaintări DNS: verificaţi de bucle." #~ msgid "nested includes not allowed" #~ msgstr "incluziunile locale nu sunt permise" #~ msgid "DHCP, %s will be written every %s" #~ msgstr "DHCP, %s va fi rescris odată la fiecare %s" #~ msgid "cannot create DHCP packet socket: %s. Is CONFIG_PACKET enabled in your kernel?" #~ msgstr "nu pot creea socket DHCP packet: %s. Aveţi activată în nucleulsistemului opţiunea CONFIG_PACKET ?" dnsmasq-2.91/po/de.po0000664000175000017500000023214714765043257012671 0ustar srksrk# German translations for dnsmasq package. # # This revised version is (C) Copyright by # Matthias Andree , 2010 - 2021. # Conrad Kostecki , 2014 - 2024. # It is subject to the GNU General Public License v2, # or at your option, any later version. # # An older version of this file was originally put in the public domain by # Simon Kelley , 2005. msgid "" msgstr "" "Project-Id-Version: dnsmasq 2.91\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-03-20 00:00+0000\n" "PO-Revision-Date: 2024-12-23 22:36+0100\n" "Last-Translator: Conrad Kostecki \n" "Language-Team: German \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 3.5\n" #: cache.c:652 msgid "Internal error in cache." msgstr "Interner Fehler im Cache." #: cache.c:1179 #, c-format msgid "failed to load names from %s: %s" msgstr "Fehler beim Laden der Namen von %s: %s" #: cache.c:1201 dhcp.c:943 #, c-format msgid "bad address at %s line %d" msgstr "Fehlerhafte Adresse in %s Zeile %d" #: cache.c:1254 dhcp.c:959 #, c-format msgid "bad name at %s line %d" msgstr "Fehlerhafter Name in %s Zeile %d" #: cache.c:1265 #, c-format msgid "read %s - %d names" msgstr "%s gelesen - %d Namen" #: cache.c:1381 msgid "cleared cache" msgstr "Cache geleert" #: cache.c:1445 #, c-format msgid "No IPv4 address found for %s" msgstr "Keine IPv4-Adresse für %s gefunden" #: cache.c:1491 #, 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:1515 #, 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:1760 #, c-format msgid "time %lu" msgstr "Zeit %lu" #: cache.c:1761 #, 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:1763 #, c-format msgid "queries forwarded %u, queries answered locally %u" msgstr "weitergeleitete Anfragen %u, lokal beantwortete Anfragen %u" #: cache.c:1766 #, c-format msgid "queries answered from stale cache %u" msgstr "Anfragen beantwortet vom veralteten Cache %u" #: cache.c:1768 #, c-format msgid "queries for authoritative zones %u" msgstr "Anfragen nach autoritativen Zonen %u" #: cache.c:1796 #, c-format msgid "server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums" msgstr "Server %s#%d: Anfragen gesendet %u, erneut versucht %u, fehlgeschlagen %u, nxdomain-Antworten %u, durchschnittliche Latenz %ums" #: util.c:51 #, c-format msgid "failed to seed the random number generator: %s" msgstr "Konnte den Zufallszahlengenerator nicht initialisieren: %s" #: util.c:246 msgid "failed to allocate memory" msgstr "Konnte Speicher nicht belegen" #: util.c:305 option.c:696 msgid "could not get memory" msgstr "Speicher nicht verfügbar" #: util.c:326 #, c-format msgid "cannot create pipe: %s" msgstr "Konnte Pipe nicht erzeugen: %s" #: util.c:334 #, c-format msgid "failed to allocate %d bytes" msgstr "Konnte %d Bytes nicht belegen" #: util.c:344 #, c-format msgid "failed to reallocate %d bytes" msgstr "Konnte %d Bytes nicht allozieren" #: util.c:465 #, c-format msgid "cannot read monotonic clock: %s" msgstr "monotone Uhr kann nicht gelesen werden: %s" # @Simon: not perfect but I cannot get nearer right now. #: util.c:579 #, c-format msgid "infinite" msgstr "unendlich" #: util.c:867 #, c-format msgid "failed to find kernel version: %s" msgstr "konnte Kernelversion nicht finden: %s" #: option.c:393 msgid "Specify local address(es) to listen on." msgstr "Lokale abzuhörende Adresse(n) angeben." #: option.c:394 msgid "Return ipaddr for all hosts in specified domains." msgstr "IP-Adresse für alle Hosts in angegebenen Domänen festlegen." #: option.c:395 msgid "Fake reverse lookups for RFC1918 private address ranges." msgstr "Rückwärtsauflösungen für private Adressbereiche nach RFC1918 falsch erfinden." #: option.c:396 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)." msgstr "Diese IP-Adresse als NXDOMAIN interpretieren (wehrt \"Suchhilfen\" ab)." #: option.c:397 #, 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:398 #, c-format msgid "Specify configuration file (defaults to %s)." msgstr "Konfigurationsdatei festlegen (Voreinstellung: %s)." #: option.c:399 msgid "Do NOT fork into the background: run in debug mode." msgstr "NICHT in den Hintergrund gehen: Betrieb im Debug-Modus." #: option.c:400 msgid "Do NOT forward queries with no domain part." msgstr "Anfragen ohne Domänen-Teil NICHT weiterschicken." #: option.c:401 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:402 msgid "Expand simple names in /etc/hosts with domain-suffix." msgstr "Einfache Namen in /etc/hosts um Domänen-Endung erweitern." #: option.c:403 msgid "Don't forward spurious DNS requests from Windows hosts." msgstr "Unberechtigte DNS-Anfragen von Windows-Rechnern nicht weiterleiten." #: option.c:404 msgid "Don't include IPv4 addresses in DNS answers." msgstr "Keine IPv4-Adressen in DNS-Antworten inkludieren." #: option.c:405 msgid "Don't include IPv6 addresses in DNS answers." msgstr "Keine IPv6-Adressen in DNS-Antworten inkludieren." #: option.c:406 msgid "Enable DHCP in the range given with lease duration." msgstr "DHCP für angegebenen Bereich und Lease-Dauer einschalten." #: option.c:407 #, c-format msgid "Change to this group after startup (defaults to %s)." msgstr "Nach dem Start in diese Benutzergruppe wechseln (Voreinstellung %s)." #: option.c:408 msgid "Set address or hostname for a specified machine." msgstr "Adresse oder Hostnamen für einen angegebenen Computer setzen." #: option.c:409 msgid "Read DHCP host specs from file." msgstr "DHCP-Host-Angaben aus Datei lesen." #: option.c:410 msgid "Read DHCP option specs from file." msgstr "DHCP-Optionen aus Datei lesen." #: option.c:411 msgid "Read DHCP host specs from a directory." msgstr "DHCP-Host-Angaben aus einem Verzeichnis lesen." #: option.c:412 msgid "Read DHCP options from a directory." msgstr "DHCP-Optionen aus einem Verzeichnis lesen." #: option.c:413 msgid "Evaluate conditional tag expression." msgstr "Auswertung eines Ausdrucks bedingter Marken." #: option.c:414 #, c-format msgid "Do NOT load %s file." msgstr "%s-Datei NICHT laden." #: option.c:415 #, 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:416 msgid "Read hosts files from a directory." msgstr "DHCP-Host-Dateien aus einem Verzeichnis lesen." #: option.c:417 msgid "Specify interface(s) to listen on." msgstr "Schnittstelle(n) zum Empfang festlegen." #: option.c:418 msgid "Specify interface(s) NOT to listen on." msgstr "Schnittstelle(n) festlegen, die NICHT empfangen sollen." #: option.c:419 msgid "Map DHCP user class to tag." msgstr "DHCP-Benutzerklasse auf Marke abbilden." #: option.c:420 msgid "Map RFC3046 circuit-id to tag." msgstr "RFC3046 \"circuit-id\" auf Marke abbilden." #: option.c:421 msgid "Map RFC3046 remote-id to tag." msgstr "RFC3046 \"remote-id\" auf Marke abbilden." #: option.c:422 msgid "Map RFC3993 subscriber-id to tag." msgstr "RFC3993 \"subscriber-id\" auf Marke abbilden." #: option.c:423 msgid "Specify vendor class to match for PXE requests." msgstr "Herstellerklasse für Vergleich von PXE-Anforderungen angeben." #: option.c:424 msgid "Don't do DHCP for hosts with tag set." msgstr "Kein DHCP für Hosts mit gesetzter Marke verwenden." #: option.c:425 msgid "Force broadcast replies for hosts with tag set." msgstr "Antwort per Broadcast für Hosts mit gesetzter Marke erzwingen." #: option.c:426 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:427 msgid "Assume we are the only DHCP server on the local network." msgstr "Unterstellen, dass wir der einzige DHCP-Server im lokalen Netz sind." #: option.c:428 #, c-format msgid "Specify where to store DHCP leases (defaults to %s)." msgstr "Festlegen, wo DHCP-Leases gespeichert werden (Voreinstellung %s)." #: option.c:429 msgid "Return MX records for local hosts." msgstr "MX-Einträge für lokale Hosts liefern." #: option.c:430 msgid "Specify an MX record." msgstr "Einen MX-Eintrag festlegen." #: option.c:431 msgid "Specify BOOTP options to DHCP server." msgstr "BOOTP-Optionen für DHCP-Server festlegen." #: option.c:432 #, c-format msgid "Do NOT poll %s file, reload only on SIGHUP." msgstr "%s-Datei NICHT abfragen, nur bei SIGHUP neu laden." #: option.c:433 msgid "Do NOT cache failed search results." msgstr "Fehlerhafte Suchergebnisse NICHT zwischenspeichern." #: option.c:434 msgid "Use expired cache data for faster reply." msgstr "Verwende abgelaufene Cachedaten für schnellere Antwort." #: option.c:435 #, c-format msgid "Use nameservers strictly in the order given in %s." msgstr "Namensserver streng in der in %s angegebenen Reihenfolge verwenden." #: option.c:436 msgid "Specify options to be sent to DHCP clients." msgstr "Optionen festlegen, die an DHCP-Klienten gesendet werden." #: option.c:437 msgid "DHCP option sent even if the client does not request it." msgstr "DHCP-Option, die selbst ohne Klientenanfrage gesendet wird." #: option.c:438 msgid "Specify port to listen for DNS requests on (defaults to 53)." msgstr "Port zum Empfangen der DNS-Anfragen festlegen (53 voreingestellt)." #: option.c:439 #, 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:440 msgid "Log DNS queries." msgstr "DNS-Anfragen protokollieren." #: option.c:441 msgid "Force the originating port for upstream DNS queries." msgstr "Ausgehenden Port für DNS-Anfragen an vorgelagerte Server erzwingen." #: option.c:442 msgid "Set maximum number of random originating ports for a query." msgstr "Setze die maximale Anzahl zufälliger Ursprungsports für eine Abfrage." #: option.c:443 msgid "Do NOT read resolv.conf." msgstr "Die resolv.conf NICHT lesen." #: option.c:444 #, c-format msgid "Specify path to resolv.conf (defaults to %s)." msgstr "Pfad zu resolv.conf festlegen (%s voreingestellt)." #: option.c:445 msgid "Specify path to file with server= options" msgstr "Pfad für Datei mit server=-Optionen angeben" #: option.c:446 msgid "Specify address(es) of upstream servers with optional domains." msgstr "Adresse(n) vorgelagerter Server festlegen, optional mit Domänen." #: option.c:447 msgid "Specify address of upstream servers for reverse address queries" msgstr "Adresse(n) vorgelagerter Server festlegen, für Rückwärtsauflösung" #: option.c:448 msgid "Never forward queries to specified domains." msgstr "Anfragen für angegebene Domänen niemals weiterleiten." #: option.c:449 msgid "Specify the domain to be assigned in DHCP leases." msgstr "Domäne festlegen, die für DHCP-Leases zugewiesen wird." #: option.c:450 msgid "Specify default target in an MX record." msgstr "Voreingestelltes Ziel für MX-Einträge festlegen." #: option.c:451 msgid "Specify time-to-live in seconds for replies from /etc/hosts." msgstr "Gültigkeitsdauer für Antworten aus /etc/hosts festlegen." #: option.c:452 msgid "Specify time-to-live in seconds for negative caching." msgstr "Gültigkeitsdauer in Sekunden für Zwischenspeicher negativer Ergebnisse festlegen." #: option.c:453 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:454 msgid "Specify time-to-live ceiling for cache." msgstr "Spezifiziere obere Gültigkeitsdauergrenze für Zwischenspeicher." #: option.c:455 msgid "Specify time-to-live floor for cache." msgstr "Spezifiziere untere Gültigkeitsdauergrenze für Zwischenspeicher." #: option.c:456 msgid "Retry DNS queries after this many milliseconds." msgstr "DNS-Abfragen nach so vielen Millisekunden wiederholen." #: option.c:457 #, c-format msgid "Change to this user after startup. (defaults to %s)." msgstr "Nach dem Start diese Benutzerrechte annehmen (%s voreingestellt)." #: option.c:458 msgid "Map DHCP vendor class to tag." msgstr "DHCP-\"vendor class\" auf Marke abbilden." #: option.c:459 msgid "Display dnsmasq version and copyright information." msgstr "DNSMasq-Version und Urheberrecht anzeigen." #: option.c:460 msgid "Translate IPv4 addresses from upstream servers." msgstr "IPv4-Adressen von vorgelagerten Servern umsetzen." #: option.c:461 msgid "Specify a SRV record." msgstr "SRV-Eintrag festlegen." #: option.c:462 msgid "Display this message. Use --help dhcp or --help dhcp6 for known DHCP options." msgstr "Diese Hilfe anzeigen. Benutzen Sie --help dhcp oder --help dhcp6 für bekannte DHCP-Optionen." #: option.c:463 #, c-format msgid "Specify path of PID file (defaults to %s)." msgstr "Pfad für Prozesskennungsdatei (PID) festlegen (Voreinstellung: %s)." #: option.c:464 #, c-format msgid "Specify maximum number of DHCP leases (defaults to %s)." msgstr "Höchstzahl der DHCP-Leases festlegen (%s voreingestellt)." #: option.c:465 msgid "Answer DNS queries based on the interface a query was sent to." msgstr "DNS-Anfragen abhängig der Empfangsschnittstelle beantworten." #: option.c:466 msgid "Specify TXT DNS record." msgstr "DNS-TXT-Eintrag festlegen." #: option.c:467 msgid "Specify PTR DNS record." msgstr "DNS-PTR-Eintrag festlegen." #: option.c:468 msgid "Give DNS name to IPv4 address of interface." msgstr "Schnittstellennamen zur IPv4-Adresse der Schnittstelle auflösen." #: option.c:469 msgid "Bind only to interfaces in use." msgstr "Nur an verwendete Schnittstellen binden." #: option.c:470 #, c-format msgid "Read DHCP static host information from %s." msgstr "Statische DHCP-Host-Information aus %s lesen." #: option.c:471 msgid "Enable the DBus interface for setting upstream servers, etc." msgstr "DBus-Schnittstelle zum Festlegen vorgelagerter Server usw. festlegen." #: option.c:472 msgid "Enable the UBus interface." msgstr "UBus-Schnittstelle aktivieren." #: option.c:473 msgid "Do not provide DHCP on this interface, only provide DNS." msgstr "Auf dieser Schnittstelle kein DHCP anbieten, sondern nur DNS." #: option.c:474 msgid "Enable dynamic address allocation for bootp." msgstr "Dynamische Adressbelegung für bootp einschalten." #: option.c:475 msgid "Map MAC address (with wildcards) to option set." msgstr "MAC-Adresse (mit Jokerzeichen) auf Optionenmenge abbilden." #: option.c:476 msgid "Treat DHCP requests on aliases as arriving from interface." msgstr "DHCP-Anfragen von Alias-Schnittstellen für die Hauptschnittstelle beantworten." #: option.c:477 msgid "Specify extra networks sharing a broadcast domain for DHCP" msgstr "Geben Sie zusätzliche Netzwerke an, die eine Broadcast-Domäne für DHCP gemeinsam nutzen" #: option.c:478 msgid "Disable ICMP echo address checking in the DHCP server." msgstr "ICMP-Echo-Adressprüfung im DHCP-Server abschalten." #: option.c:479 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:480 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:481 msgid "Run lease-change scripts as this user." msgstr "Lease-Änderungs-Skript mit den Rechten dieses Nutzers ausführen." #: option.c:482 msgid "Call dhcp-script with changes to local ARP table." msgstr "Rufe dhcp-script mit Änderungen an der lokalen ARP-Tabelle auf." #: option.c:483 msgid "Read configuration from all the files in this directory." msgstr "Konfiguration aus allen Dateien in diesem Verzeichnis lesen." #: option.c:484 msgid "Execute file and read configuration from stdin." msgstr "Führe Datei aus und lese die Konfiguration aus stdin." #: option.c:485 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:486 msgid "Do not use leasefile." msgstr "Keine Lease-Datei benützen." #: option.c:487 #, c-format msgid "Maximum number of concurrent DNS queries. (defaults to %s)" msgstr "Höchstzahl nebenläufiger DNS-Anfragen (%s voreingestellt)." #: option.c:488 #, c-format msgid "Clear DNS cache when reloading %s." msgstr "DNS-Zwischenspeicher beim Neuladen von %s löschen." #: option.c:489 msgid "Ignore hostnames provided by DHCP clients." msgstr "Von DHCP-Klienten gelieferte Hostnamen ignorieren." #: option.c:490 msgid "Do NOT reuse filename and server fields for extra DHCP options." msgstr "Dateinamen und Server-Datenfelder NICHT für zusätzliche DHCP-Optionen wiederverwenden." #: option.c:491 msgid "Enable integrated read-only TFTP server." msgstr "Eingebauten schreibgeschützten TFTP-Server einschalten." #: option.c:492 msgid "Export files by TFTP only from the specified subtree." msgstr "Nur vom festgelegten Unterbaum Dateien per TFTP exportieren." #: option.c:493 msgid "Add client IP or hardware address to tftp-root." msgstr "IP-Adresse oder Hardware-Adresse des Klienten an tftp-root anhängen." #: option.c:494 msgid "Allow access only to files owned by the user running dnsmasq." msgstr "Zugriff nur auf Dateien gestatten, die dem dnsmasq betreibenden Benutzer gehören." #: option.c:495 msgid "Do not terminate the service if TFTP directories are inaccessible." msgstr "Den Dienst nicht beenden, wenn die TFTP-Verzeichnisse unerreichbar sind." #: option.c:496 #, c-format msgid "Maximum number of concurrent TFTP transfers (defaults to %s)." msgstr "Maximale Anzahl gleichzeitiger TFTP-Übertragungen (%s voreingestellt)." #: option.c:497 msgid "Maximum MTU to use for TFTP transfers." msgstr "Maximale MTU für TFTP-Übertragungen erreicht." #: option.c:498 msgid "Disable the TFTP blocksize extension." msgstr "TFTP-Blockgrößen-Erweiterung abschalten." #: option.c:499 msgid "Convert TFTP filenames to lowercase" msgstr "TFTP-Dateinamen in Kleinschreibung umsetzen" #: option.c:500 msgid "Ephemeral port range for use by TFTP transfers." msgstr "Bereich für vorübergehende Ports für TFTP-Übertragungen." #: option.c:501 msgid "Use only one port for TFTP server." msgstr "Bitte nur einen Port für den TFTP-Server nutzen." #: option.c:502 msgid "Extra logging for DHCP." msgstr "Erweiterte DHCP-Protokollierung." #: option.c:503 msgid "Enable async. logging; optionally set queue length." msgstr "Asynchrone Protokollierung einschalten, opt. Warteschlangenlänge festlegen." #: option.c:504 msgid "Stop DNS rebinding. Filter private IP ranges when resolving." msgstr "DNS-Rebinding unterbinden, private IP-Bereiche bei der Auflösung ausfiltern." #: option.c:505 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:506 msgid "Inhibit DNS-rebind protection on this domain." msgstr "DNS-Rebind-Schutz für diese Domäne aufheben." #: option.c:507 msgid "Always perform DNS queries to all servers." msgstr "DNS-Anfragen immer an alle Server weiterleiten." #: option.c:508 msgid "Set tag if client includes matching option in request." msgstr "Marke setzen, wenn Klient eine entsprechende Option anfragt." #: option.c:509 msgid "Set tag if client provides given name." msgstr "Setzt das Tag, wenn der Client diesen Namen anbietet." #: option.c:510 msgid "Use alternative ports for DHCP." msgstr "Alternative Ports für DHCP verwenden." #: option.c:511 msgid "Specify NAPTR DNS record." msgstr "DNS-NAPTR-Eintrag festlegen." #: option.c:512 msgid "Specify lowest port available for DNS query transmission." msgstr "Niedrigsten verfügbaren Port für Übertragung von DNS-Anfragen festlegen." #: option.c:513 msgid "Specify highest port available for DNS query transmission." msgstr "Höchsten verfügbaren Port für Übertragung von DNS-Anfragen festlegen." #: option.c:514 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:515 msgid "Generate hostnames based on MAC address for nameless clients." msgstr "Generiere Hostnamen auf Basis der MAC-Adresse für namenlose Klienten." #: option.c:516 msgid "Use these DHCP relays as full proxies." msgstr "Diese DHCP-Relais als vollwertige Proxies verwenden." #: option.c:517 msgid "Relay DHCP requests to a remote server" msgstr "Leite DHCP-Anfragen an entfernten Server weiter" #: option.c:518 msgid "Specify alias name for LOCAL DNS name." msgstr "Alias für LOKALEN DNS-Namen festlegen." #: option.c:519 msgid "Prompt to send to PXE clients." msgstr "Aufforderung, die an PXE-Klienten geschickt wird." #: option.c:520 msgid "Boot service for PXE menu." msgstr "Boot-Dienst für PXE-Menü." #: option.c:521 msgid "Check configuration syntax." msgstr "Konfigurationssyntax prüfen." #: option.c:522 msgid "Add requestor's MAC address to forwarded DNS queries." msgstr "Anfragende MAC-Adresse in die weiterleitende DNS-Anfrage einfügen." #: option.c:523 msgid "Strip MAC information from queries." msgstr "Entferne die MAC-Information von Abfragen." #: option.c:524 msgid "Add specified IP subnet to forwarded DNS queries." msgstr "Füge spezifiziertes IP-Subnetz an weitergeleiteten DNS-Anfragen hinzu." #: option.c:525 msgid "Strip ECS information from queries." msgstr "Entferne die ECS-Information von Abfragen." #: option.c:526 msgid "Add client identification to forwarded DNS queries." msgstr "Füge Klient Identifikationan weitergeleiteten DNS-Anfragen hinzu." # This is a rather liberal translation to convey the purpose. # something along "authorize upstream nameservers to validate DNSSEC [for us]" #: option.c:527 msgid "Proxy DNSSEC validation results from upstream nameservers." msgstr "Vorgelagerte Namensserver für DNSSEC-Validierung ermächtigen." #: option.c:528 msgid "Attempt to allocate sequential IP addresses to DHCP clients." msgstr "Versuche, sequenzielle IP-Adressen an DHCP-Klienten zu vergeben." #: option.c:529 msgid "Ignore client identifier option sent by DHCP clients." msgstr "Ignorieren Sie die von DHCP-Clients gesendete Client-ID-Option." #: option.c:530 msgid "Copy connection-track mark from queries to upstream connections." msgstr "Kopiere \"connection-track mark\" von Anfragen nach Upstream-Verbindungen." #: option.c:531 msgid "Allow DHCP clients to do their own DDNS updates." msgstr "Erlaube DHCP-Klienten, ihre eigenen DDNS-Updates durchzuführen." #: option.c:532 msgid "Send router-advertisements for interfaces doing DHCPv6" msgstr "Sende \"Router-Advertisments\" für Netzwerkschnittstellen, welche DHCPv6 nutzen" #: option.c:533 msgid "Specify DUID_EN-type DHCPv6 server DUID" msgstr "Spezifiziere DUID_EN-type DHCPv6 Server DUID" #: option.c:534 msgid "Specify host (A/AAAA and PTR) records" msgstr "Spezifiziere Host (A/AAAA und PTR) Einträge" #: option.c:535 msgid "Specify host record in interface subnet" msgstr "Host-Eintrag für das Unternetzwerk der Schnittstelle angeben" #: option.c:536 msgid "Specify certification authority authorization record" msgstr "Autorisierungsdatensatz der Zertifizierungsstelle angeben" #: option.c:537 msgid "Specify arbitrary DNS resource record" msgstr "Spezifiziere einen beliebiegen DNS Eintrag" #: option.c:538 msgid "Bind to interfaces in use - check for new interfaces" msgstr "Bindung an Schnittstellen in Benutzung - prüfe auf neue Schnittstellen" #: option.c:539 msgid "Export local names to global DNS" msgstr "Exportiere lokale Namen in das globale DNS" #: option.c:540 msgid "Domain to export to global DNS" msgstr "Domain für globales DNS ausgeben" #: option.c:541 msgid "Set TTL for authoritative replies" msgstr "Setze Gültigkeitsdauer für autoritative Antworten" #: option.c:542 msgid "Set authoritative zone information" msgstr "Setze autoritative Zoneninformationen" #: option.c:543 msgid "Secondary authoritative nameservers for forward domains" msgstr "Sekundärer autoritativer Nameserver für weitergeleitete Domains" #: option.c:544 msgid "Peers which are allowed to do zone transfer" msgstr "Peers, die einen Zonentransfer durchführen dürfen" #: option.c:545 msgid "Specify ipsets to which matching domains should be added" msgstr "Spezifiziere IPSets, zu denen passende Domains hinzugefügt werden sollen" #: option.c:546 msgid "Specify nftables sets to which matching domains should be added" msgstr "Geben Sie nftables sets an, zu denen passende Domänen hinzugefügt werden sollen" #: option.c:547 msgid "Enable filtering of DNS queries with connection-track marks." msgstr "Aktivieren Sie das Filtern von DNS-Abfragen mit \"connection-track\"-Markierungen." #: option.c:548 msgid "Set allowed DNS patterns for a connection-track mark." msgstr "Legen Sie zulässige DNS-Muster für eine \"connection-track\"-Markierung fest." #: option.c:549 msgid "Specify a domain and address range for synthesised names" msgstr "Spezifiziere eine Domain und Adressbereich für synthetisierte Namen" #: option.c:550 msgid "Activate DNSSEC validation" msgstr "Aktiviere DNSSEC-Validierung" #: option.c:551 msgid "Specify trust anchor key digest." msgstr "Spezifiziere Vertrauensursprung (Trust Anchor) der Schlüssel-Prüfdaten (Key Digest)." # while there is no German manual, mark "not for productive use" #: option.c:552 msgid "Disable upstream checking for DNSSEC debugging." msgstr "Deaktiviere die vorgelagerte Prüfung für die DNS-Fehlersuche. (Nicht für produktiven Einsatz!)" #: option.c:553 msgid "Ensure answers without DNSSEC are in unsigned zones." msgstr "Stellt sicher, dass Antworten ohne DNSSEC sich in einer unsignierten Zone befinden." #: option.c:554 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:555 msgid "Timestamp file to verify system clock for DNSSEC" msgstr "Zeitstempel-Datei für die Verifizierung der Systemuhrzeit für DNSSEC" #: option.c:556 msgid "Set MTU, priority, resend-interval and router-lifetime" msgstr "Setze MTU, Priorität, Sendewiederholintervall und Router-Lebensdauer" #: option.c:557 msgid "Do not log routine DHCP." msgstr "Protokolliere kein Routine-DHCP." #: option.c:558 msgid "Do not log routine DHCPv6." msgstr "Protokolliere kein Routine-DHCPv6." #: option.c:559 msgid "Do not log RA." msgstr "RA nicht protokollieren." #: option.c:560 msgid "Log debugging information." msgstr "Debug-(Fehlersuch-)Information protokollieren." #: option.c:561 msgid "Accept queries only from directly-connected networks." msgstr "Akzeptiere nur Anfragen von direkt verbundenen Netzwerken." #: option.c:562 msgid "Detect and remove DNS forwarding loops." msgstr "Erkennen und Entfernen von DNS-Weiterleitungsschleifen." #: option.c:563 msgid "Ignore DNS responses containing ipaddr." msgstr "Ignoriere DNS-Antworten, die ipaddr enthalten." #: option.c:564 msgid "Set TTL in DNS responses with DHCP-derived addresses." msgstr "Setze TTL in DNS-Antworten mit DHCP-abgeleiteten Adressen." #: option.c:565 msgid "Delay DHCP replies for at least number of seconds." msgstr "Verzögere DHCP-Antworten mindestens für gegebene Anzahl von Sekunden." #: option.c:566 msgid "Enables DHCPv4 Rapid Commit option." msgstr "Aktiviert die DHCPv4-\"Rapid Commit\"-Option." #: option.c:567 msgid "Path to debug packet dump file" msgstr "Pfad zur Paketablagedatei zur Fehlersuche" #: option.c:568 msgid "Mask which packets to dump" msgstr "Maskiere Pakete, welche abgelegt werden sollen" #: option.c:569 msgid "Call dhcp-script when lease expiry changes." msgstr "Rufe dhcp-script auf, wenn der Ablauf des Leases sich ändert." #: option.c:570 msgid "Send Cisco Umbrella identifiers including remote IP." msgstr "Senden Sie Cisco Umbrella-Identifikatoren einschließlich der Remote-IP." #: option.c:571 msgid "Do not log routine TFTP." msgstr "Protokolliere kein Routine-TFTP." #: option.c:572 msgid "Suppress round-robin ordering of DNS records." msgstr "Unterdrückt die Round-Robin-Sortierung von DNS-Einträgen." #: option.c:802 #, c-format msgid "" "Usage: dnsmasq [options]\n" "\n" msgstr "" "Verwendung: dnsmasq [Optionen]\n" "\n" #: option.c:804 #, c-format msgid "Use short options only on the command line.\n" msgstr "Auf der Befehlszeile nur kurze Optionen verwenden!\n" #: option.c:806 #, c-format msgid "Valid options are:\n" msgstr "Gültige Optionen sind:\n" #: option.c:853 option.c:1055 msgid "bad address" msgstr "Fehlerhafte Adresse" #: option.c:882 option.c:886 msgid "bad port" msgstr "Fehlerhafter Port" #: option.c:899 option.c:1002 option.c:1048 msgid "interface binding not supported" msgstr "Schnittstellenbindung nicht unterstützt" #: option.c:955 msgid "Cannot resolve server name" msgstr "Servername kann nicht aufgelöst werden" #: option.c:991 msgid "cannot use IPv4 server address with IPv6 source address" msgstr "IPv4-Serveradresse kann nicht mit IPv6-Quelladresse verwendet werden" #: option.c:997 option.c:1043 msgid "interface can only be specified once" msgstr "Schnittstelle kann nur einmal angegeben werden" #: option.c:1011 option.c:4785 msgid "bad interface name" msgstr "Fehlerhafter Schnittestellenname" #: option.c:1037 msgid "cannot use IPv6 server address with IPv4 source address" msgstr "IPv6-Serveradresse kann nicht mit IPv4-Quelladresse verwendet werden" #: option.c:1124 msgid "bad IPv4 prefix length" msgstr "ungültige IPv4-Präfixlänge" #: option.c:1155 option.c:1165 option.c:1240 option.c:1250 option.c:5360 msgid "error" msgstr "Fehler" #: option.c:1207 msgid "bad IPv6 prefix length" msgstr "ungültige IPv6-Präfixlänge" #: option.c:1467 msgid "inappropriate vendor:" msgstr "Ungeeigneter Anbieter:" #: option.c:1474 msgid "inappropriate encap:" msgstr "Ungeeignete Kapselung:" #: option.c:1500 msgid "unsupported encapsulation for IPv6 option" msgstr "Nicht unterstützte Verkapselung für eine IPv6-Option" #: option.c:1514 msgid "bad dhcp-option" msgstr "Fehlerhafte DHCP-Option" #: option.c:1592 msgid "bad IP address" msgstr "Fehlerhafte IP-Adresse" #: option.c:1595 option.c:1734 option.c:3928 msgid "bad IPv6 address" msgstr "Fehlerhafte IPv6-Adresse" #: option.c:1688 msgid "bad IPv4 address" msgstr "Fehlerhafte IPv4-Adresse" #: option.c:1761 option.c:1856 msgid "bad domain in dhcp-option" msgstr "Fehlerhafte Domäne in DHCP-Option" #: option.c:1900 msgid "dhcp-option too long" msgstr "DHCP-Option zu lang" #: option.c:1907 msgid "illegal dhcp-match" msgstr "Unzulässige dhcp-match-Option" #: option.c:1966 msgid "illegal repeated flag" msgstr "unzulässig wiederholte Markierung" #: option.c:1974 msgid "illegal repeated keyword" msgstr "unzulässig wiederholtes Schlüsselwort" #: option.c:2056 option.c:5533 #, c-format msgid "cannot access directory %s: %s" msgstr "Kann auf Verzeichnis %s nicht zugreifen: %s" #: option.c:2102 tftp.c:573 dump.c:72 #, c-format msgid "cannot access %s: %s" msgstr "Kann auf %s nicht zugreifen: %s" #: option.c:2219 msgid "setting log facility is not possible under Android" msgstr "Die Einstellung der \"log facility\" kann unter Android nicht gesetzt werden" #: option.c:2228 msgid "bad log facility" msgstr "Falsche \"log facility\"" #: option.c:2281 msgid "bad MX preference" msgstr "fehlerhafte MX-Präferenz-Angabe" #: option.c:2289 msgid "bad MX name" msgstr "fehlerhafter MX-Name" #: option.c:2304 msgid "bad MX target" msgstr "fehlerhaftes MX-Ziel" #: option.c:2324 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:2328 msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts" msgstr "Neuübersetzung mit HAVE_LUASCRIPT nötig, um benutzerdefinierte Lua-Skripte auszuführen" #: option.c:2447 msgid "invalid auth-zone" msgstr "unzulässiger Alias-Bereich" #: option.c:2589 option.c:2621 msgid "bad prefix length" msgstr "fehlerhafte Präfixlänge" #: option.c:2601 option.c:2642 option.c:2696 msgid "bad prefix" msgstr "fehlerhaftes Präfix" #: option.c:2716 msgid "prefix length too small" msgstr "Präfixlänge ist zu klein" #: option.c:3010 msgid "Bad address in --address" msgstr "Fehlerhafte Adresse in --address" #: option.c:3110 msgid "recompile with HAVE_IPSET defined to enable ipset directives" msgstr "Neuübersetzung mit HAVE_IPSET nötig, um IPSet-Direktiven zu aktivieren" #: option.c:3117 msgid "recompile with HAVE_NFTSET defined to enable nftset directives" msgstr "Neukompilieren mit definiertem HAVE_NFTSET, um NFTSET-Direktiven zu aktivieren" #: option.c:3192 option.c:3210 msgid "recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives" msgstr "Neukompilierung mit HAVE_CONNTRACK notwendig, um connmark-allowlist-Direktiven zu aktivieren" #: option.c:3496 msgid "bad port range" msgstr "falscher Portbereich" #: option.c:3522 msgid "bad bridge-interface" msgstr "fehlerhafte Brücken-Schnittstelle" #: option.c:3566 msgid "bad shared-network" msgstr "fehlerhaftes geteiltes Netz (shared-network)" #: option.c:3620 msgid "only one tag allowed" msgstr "nur eine Marke zulässig" #: option.c:3641 option.c:3657 option.c:3783 option.c:3791 option.c:3834 msgid "bad dhcp-range" msgstr "fehlerhafter DHCP-Bereich" #: option.c:3675 msgid "inconsistent DHCP range" msgstr "inkonsistenter DHCP-Bereich" #: option.c:3741 msgid "prefix length must be exactly 64 for RA subnets" msgstr "Die Präfixlänge für RA-Subnetze muss genau 64 sein" #: option.c:3743 msgid "prefix length must be exactly 64 for subnet constructors" msgstr "Die Präfixlänge für Subnet-Konstruktor muss genau 64 sein" #: option.c:3746 msgid "prefix length must be at least 64" msgstr "Die Präfixlänge muss mindestens 64 sein" #: option.c:3749 msgid "inconsistent DHCPv6 range" msgstr "Inkonsistenter DHCPv6-Bereich" #: option.c:3768 msgid "prefix must be zero with \"constructor:\" argument" msgstr "Präfix muss in Verbindung mit \"constructor:\" Argument Null sein" #: option.c:3893 option.c:3971 msgid "bad hex constant" msgstr "Fehlerhafte Hex-Konstante" #: option.c:3946 msgid "bad IPv6 prefix" msgstr "fehlerhaftes IPv6-Präfix" #: option.c:3994 #, c-format msgid "duplicate dhcp-host IP address %s" msgstr "doppelte dhcp-host IP-Adresse %s" #: option.c:4056 msgid "bad DHCP host name" msgstr "fehlerhafter DHCP-Hostname" #: option.c:4142 msgid "bad tag-if" msgstr "fehlerhafte bedingte Marke (tag-if)" #: option.c:4490 option.c:5046 msgid "invalid port number" msgstr "unzulässige Portnummer" #: option.c:4546 msgid "bad dhcp-proxy address" msgstr "Fehlerhafte DHCP-Proxy-Adresse" #: option.c:4627 msgid "Bad dhcp-relay" msgstr "Unzulässiges \"dhcp-relay\"" #: option.c:4671 msgid "bad RA-params" msgstr "fehlerhafte RA-Parameter" #: option.c:4681 msgid "bad DUID" msgstr "fehlerhafte DUID" #: option.c:4715 msgid "missing address in alias" msgstr "Adresse fehlt in Alias" #: option.c:4721 msgid "invalid alias range" msgstr "unzulässiger Alias-Bereich" #: option.c:4770 msgid "missing address in dynamic host" msgstr "Adresse fehlt in dynamischem Host" #: option.c:4785 msgid "bad dynamic host" msgstr "fehlerhafter dynamischer Host" #: option.c:4803 option.c:4819 msgid "bad CNAME" msgstr "fehlerhafter CNAME" #: option.c:4827 msgid "duplicate CNAME" msgstr "doppelter CNAME" #: option.c:4854 msgid "bad PTR record" msgstr "fehlerhafter PTR-Eintrag" #: option.c:4889 msgid "bad NAPTR record" msgstr "fehlerhafter NAPTR-Eintrag" #: option.c:4925 msgid "bad RR record" msgstr "fehlerhafter RR-Eintrag" #: option.c:4958 msgid "bad CAA record" msgstr "fehlerhafter CAA-Eintrag" #: option.c:4987 msgid "bad TXT record" msgstr "fehlerhafter TXT-Eintrag" #: option.c:5030 msgid "bad SRV record" msgstr "fehlerhafter SRV-Eintrag" #: option.c:5037 msgid "bad SRV target" msgstr "fehlerhaftes SRV-Ziel" #: option.c:5056 msgid "invalid priority" msgstr "unzulässige Priorität" #: option.c:5061 msgid "invalid weight" msgstr "unzulässige Wichtung" #: option.c:5084 msgid "Bad host-record" msgstr "Fehlerhafter \"host-record\"" #: option.c:5123 msgid "Bad name in host-record" msgstr "Fehlerhafter Name in \"host-record\"" #: option.c:5165 msgid "bad value for dnssec-check-unsigned" msgstr "Fehlerhafter Wert für \"dnssec-check-unsigned\"" #: option.c:5201 msgid "bad trust anchor" msgstr "fehlerhafter Vertrauensursprung (Trust Anchor)" #: option.c:5217 msgid "bad HEX in trust anchor" msgstr "fehlerhafter Hexwert in Vertrauensursprung (Trust Anchor)" #: option.c:5228 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:5290 msgid "missing \"" msgstr "fehlende \\\"" #: option.c:5347 msgid "bad option" msgstr "fehlerhafter Option" #: option.c:5349 msgid "extraneous parameter" msgstr "überschüssiger Parameter" #: option.c:5351 msgid "missing parameter" msgstr "fehlender Parameter" #: option.c:5353 msgid "illegal option" msgstr "unzulässige Option" #: option.c:5363 #, c-format msgid " in output from %s" msgstr " in der Ausgabe von %s" #: option.c:5365 #, c-format msgid " at line %d of %s" msgstr " in Zeile %d von %s" #: option.c:5380 option.c:5683 option.c:5694 #, c-format msgid "read %s" msgstr "%s gelesen" #: option.c:5446 #, c-format msgid "cannot execute %s: %s" msgstr "kann %s nicht ausführen: %s" #: option.c:5454 option.c:5615 tftp.c:790 #, c-format msgid "cannot read %s: %s" msgstr "kann %s nicht lesen: %s" #: option.c:5473 #, c-format msgid "error executing %s: %s" msgstr "Fehler bei der Ausführung von %s: %s" #: option.c:5476 #, c-format msgid "%s returns non-zero error code" msgstr "%s gibt einen Fehlercode ungleich Null zurück" #: option.c:5775 msgid "junk found in command line" msgstr "Müll in der Kommandozeile gefunden" #: option.c:5815 #, c-format msgid "Dnsmasq version %s %s\n" msgstr "Dnsmasq Version %s %s\n" #: option.c:5816 #, c-format msgid "" "Compile time options: %s\n" "\n" msgstr "" "Kompilierungs-Optionen %s\n" "\n" #: option.c:5817 #, 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:5818 #, c-format msgid "Dnsmasq is free software, and you are welcome to redistribute it\n" msgstr "Dnsmasq ist freie Software, und darf unter den Bedingungen der\n" #: option.c:5819 #, c-format msgid "under the terms of the GNU General Public License, version 2 or 3.\n" msgstr "GNU General Public Lizenz, Version 2 oder 3, weiterverteilt werden.\n" #: option.c:5836 msgid "try --help" msgstr "versuchen Sie --help" #: option.c:5838 msgid "try -w" msgstr "versuchen Sie -w" #: option.c:5840 #, c-format msgid "bad command line options: %s" msgstr "fehlerhafte Optionen auf der Befehlszeile: %s" #: option.c:5909 #, c-format msgid "CNAME loop involving %s" msgstr "CNAME-Schleife mit %s" #: option.c:5950 #, c-format msgid "cannot get host-name: %s" msgstr "kann Hostnamen nicht ermitteln: %s" #: option.c:5978 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:5988 msgid "must have exactly one resolv.conf to read domain from." msgstr "muss genau eine resolv.conf-Datei haben, um die Domäne zu lesen." #: option.c:5991 network.c:1727 dhcp.c:892 #, c-format msgid "failed to read %s: %s" msgstr "konnte %s nicht lesen: %s" #: option.c:6008 #, c-format msgid "no search directive found in %s" msgstr "keine \"search\"-Anweisung in %s gefunden" #: option.c:6029 msgid "there must be a default domain when --dhcp-fqdn is set" msgstr "Es muss eine Standard-Domain gesetzt sein, wenn --dhcp-fqdn gesetzt ist" #: option.c:6038 msgid "syntax check OK" msgstr "Syntaxprüfung OK" #: forward.c:107 #, c-format msgid "failed to send packet: %s" msgstr "Paketversand gescheitert: %s" #: forward.c:715 msgid "discarding DNS reply: subnet option mismatch" msgstr "Verwerfe DNS Antwort: Subnetoption stimmt nicht überrein" #: forward.c:780 #, c-format msgid "nameserver %s refused to do a recursive query" msgstr "Namensserver %s hat eine rekursive Anfrage verweigert" #: forward.c:826 #, c-format msgid "possible DNS-rebind attack detected: %s" msgstr "möglichen DNS-Rebind-Angriff entdeckt: %s" #: forward.c:1239 #, c-format msgid "reducing DNS packet size for nameserver %s to %d" msgstr "Reduziere die DNS-Paketgröße für Nameserver %s auf %d" #: forward.c:1565 #, c-format msgid "ignoring query from non-local network %s (logged only once)" msgstr "Ignoriere Abfrage von nicht-lokalen Netzwerk %s (Nur einmal protokolliert)" #: forward.c:2139 #, c-format msgid "ignoring query from non-local network %s" msgstr "Ignoriere Abfrage von nicht-lokalen Netzwerk %s" #: forward.c:2501 #, c-format msgid "failed to bind server socket to %s: %s" msgstr "konnte nicht an Server-Socket für %s binden: %s" #: forward.c:2867 #, c-format msgid "Maximum number of concurrent DNS queries reached (max: %d)" msgstr "Höchstzahl an nebenläufiger DNS-Anfragen erreicht (max. %d)" #: forward.c:2869 #, c-format msgid "Maximum number of concurrent DNS queries to %s reached (max: %d)" msgstr "Maximale Anzahl gleichzeitiger DNS-Abfragen, die erreicht %s (max. %d)" #: network.c:700 #, c-format msgid "stopped listening on %s(#%d): %s port %d" msgstr "Empfang auf %s(#%d) beendet: %s Port %d" #: network.c:911 #, c-format msgid "failed to create listening socket for %s: %s" msgstr "Konnte Empfangs-Socket für %s nicht erzeugen: %s" #: network.c:1192 #, c-format msgid "listening on %s(#%d): %s port %d" msgstr "Empfang auf %s(#%d): %s Port %d" #: network.c:1219 #, c-format msgid "listening on %s port %d" msgstr "Empfang auf %s Port %d" #: network.c:1252 #, c-format msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s" msgstr "LOUD WARNING: Empfang auf %s kann die Anfragen auf anderen Schnittstellen als %s akzeptieren" #: network.c:1259 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:1268 #, c-format msgid "warning: using interface %s instead" msgstr "Warnung: benutze stattdessen Schnittstelle %s" #: network.c:1277 #, c-format msgid "warning: no addresses found for interface %s" msgstr "Warnung: keine Adressen für die Schnittstelle %s gefunden" #: network.c:1335 #, c-format msgid "interface %s failed to join DHCPv6 multicast group: %s" msgstr "Schnittstelle %s konnte DHCPv6-Multicast-Gruppe nicht beitreten: %s" #: network.c:1340 msgid "try increasing /proc/sys/net/core/optmem_max" msgstr "Versuchen Sie, /proc/sys/net/core/optmem_max zu erhöhen" #: network.c:1545 #, c-format msgid "failed to bind server socket for %s: %s" msgstr "konnte nicht an Server-Socket für %s binden: %s" #: network.c:1622 #, c-format msgid "ignoring nameserver %s - local interface" msgstr "ignoriere Namensserver %s - lokale Schnittstelle" #: network.c:1633 #, c-format msgid "ignoring nameserver %s - cannot make/bind socket: %s" msgstr "ignoriere Namensserver %s - kann Socket nicht erzeugen/binden: %s" #: network.c:1643 msgid "more servers are defined but not logged" msgstr "mehr Server sind definiert aber nicht protokolliert" #: network.c:1654 msgid "(no DNSSEC)" msgstr "(kein DNSSEC)" # FIXME: this isn't translatable - always provide full strings, do not assemble yourself! -- MA #: network.c:1657 msgid "unqualified" msgstr "unqualifizierte" #: network.c:1657 msgid "names" msgstr "Namen" #: network.c:1659 msgid "default" msgstr "Standard" #: network.c:1661 msgid "domain" msgstr "Domäne" #: network.c:1663 #, c-format msgid "using nameserver %s#%d for %s %s%s %s" msgstr "benutze Namensserver %s#%d für %s %s%s %s" #: network.c:1667 #, c-format msgid "NOT using nameserver %s#%d - query loop detected" msgstr "Benutze Namensserver %s#%d NICHT - Anfragenschleife festgetellt" #: network.c:1670 #, c-format msgid "using nameserver %s#%d(via %s)" msgstr "Benutze Namensserver %s#%d(via %s)" #: network.c:1672 #, c-format msgid "using nameserver %s#%d" msgstr "Benutze Namensserver %s#%d" #: network.c:1687 #, c-format msgid "using only locally-known addresses for %s" msgstr "benutze nur lokal-bekannte Adressen für %s" #: network.c:1690 #, c-format msgid "using standard nameservers for %s" msgstr "benutze standard Namensserver für %s" #: network.c:1694 #, c-format msgid "using %d more local addresses" msgstr "Benutze weitere %d lokale Adressen" #: network.c:1696 #, c-format msgid "using %d more nameservers" msgstr "Benutze weitere %d Namensserver" #: dnsmasq.c:192 msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform" msgstr "dhcp-hostsdir, dhcp-optsdir und hostsdir werden auf dieser Plattform nicht unterstüzt" #: dnsmasq.c:207 msgid "no root trust anchor provided for DNSSEC" msgstr "Keine Root-Vertrauensanker (Root Trust Anchor) für DNSSEC verfügbar" #: dnsmasq.c:210 msgid "cannot reduce cache size from default when DNSSEC enabled" msgstr "Kann die Standard-Zwischenspeichergröße nicht verkleinern, wenn DNSSEC aktiviert ist" #: dnsmasq.c:212 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:218 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:225 msgid "cannot use --conntrack AND --query-port" msgstr "Kann nicht --conntrack UND --query-port einsetzen" #: dnsmasq.c:231 msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h" msgstr "Conntrack-Unterstützung nicht verfügbar: Aktivieren Sie HAVE_CONNTRACK in src/config.h" #: dnsmasq.c:236 msgid "asynchronous logging is not available under Solaris" msgstr "asynchrone Protokollierung ist unter Solaris nicht verfügbar" #: dnsmasq.c:241 msgid "asynchronous logging is not available under Android" msgstr "Asynchrone Protokollierung ist unter Android nicht verfügbar" #: dnsmasq.c:246 msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h" msgstr "Autoritatives DNS nicht verfügbar: Setzen Sie HAVE_AUTH in src/config.h" #: dnsmasq.c:251 msgid "loop detection not available: set HAVE_LOOP in src/config.h" msgstr "Loop-Erkennung nicht verfügbar, aktivieren Sie HAVE_LOOP in src/config.h" #: dnsmasq.c:256 msgid "Ubus not available: set HAVE_UBUS in src/config.h" msgstr "UBus nicht verfügbar: setzen Sie HAVE_UBUS in src/config.h" #: dnsmasq.c:267 msgid "max_port cannot be smaller than min_port" msgstr "max_port darf nicht kleiner als min_port sein" #: dnsmasq.c:271 msgid "port_limit must not be larger than available port range" msgstr "port_limit darf nicht größer als der verfügbare Portbereich sein" #: dnsmasq.c:278 msgid "--auth-server required when an auth zone is defined." msgstr "--auth-server ist notwendig, wenn eine Auth-Zone definiert ist." #: dnsmasq.c:283 msgid "zone serial must be configured in --auth-soa" msgstr "Seriennummer der Zone muss mit --auth-soa konfiguriert werden" #: dnsmasq.c:303 msgid "dhcp-range constructor not available on this platform" msgstr "dhcp-range-Konstruktor ist auf dieser Plattform nicht verfügbar" #: dnsmasq.c:377 msgid "cannot set --bind-interfaces and --bind-dynamic" msgstr "Kann nicht --bind-interfaces und --bind-dynamic setzen" #: dnsmasq.c:380 #, c-format msgid "failed to find list of interfaces: %s" msgstr "konnte Schnitstellenliste nicht auffinden: %s" #: dnsmasq.c:389 #, c-format msgid "unknown interface %s" msgstr "unbekannte Schnittstelle %s" #: dnsmasq.c:396 #, c-format msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s" msgstr "kann SO_BINDTODEVICE für DHCP-Socket nicht aktivieren: %s" #: dnsmasq.c:440 msgid "Packet dumps not available: set HAVE_DUMP in src/config.h" msgstr "Paketmitschnitt nicht verfügbar: setzen Sie HAVE_DUMP in src/config.h" #: dnsmasq.c:448 dnsmasq.c:1232 #, c-format msgid "DBus error: %s" msgstr "DBus-Fehler: %s" #: dnsmasq.c:451 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:459 dnsmasq.c:1253 #, c-format msgid "UBus error: %s" msgstr "UBus-Fehler: %s" #: dnsmasq.c:462 msgid "UBus not available: set HAVE_UBUS in src/config.h" msgstr "UBus nicht verfügbar: setzen Sie HAVE_UBUS in src/config.h" #: dnsmasq.c:492 #, c-format msgid "unknown user or group: %s" msgstr "Unbekannter Benutzer oder Gruppe: %s" #: dnsmasq.c:568 #, c-format msgid "process is missing required capability %s" msgstr "Prozess benötigt verlangte Fähigkeit %s" #: dnsmasq.c:600 #, 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:852 #, c-format msgid "started, version %s DNS disabled" msgstr "gestartet, Version %s, DNS abgeschaltet" #: dnsmasq.c:857 #, c-format msgid "started, version %s cachesize %d" msgstr "gestartet, Version %s, Zwischenspeichergröße %d" #: dnsmasq.c:859 msgid "cache size greater than 10000 may cause performance issues, and is unlikely to be useful." msgstr "Eine Cachegröße größer als 10000 kann Performanceprobleme verursachen und Nutzen ist wenig wahrscheinlich." #: dnsmasq.c:862 #, c-format msgid "started, version %s cache disabled" msgstr "Gestartet, Version %s, Zwischenspeicher deaktiviert" #: dnsmasq.c:865 msgid "DNS service limited to local subnets" msgstr "DNS-Dienst auf Unternetze eingeschränkt" #: dnsmasq.c:868 #, c-format msgid "compile time options: %s" msgstr "Optionen bei Übersetzung: %s" #: dnsmasq.c:877 msgid "DBus support enabled: connected to system bus" msgstr "DBus-Unterstützung eingeschaltet: mit Systembus verbunden" #: dnsmasq.c:879 msgid "DBus support enabled: bus connection pending" msgstr "DBus-Unterstützung eingeschaltet: warte auf Systembus-Verbindung" #: dnsmasq.c:887 msgid "UBus support enabled: connected to system bus" msgstr "UBus-Unterstützung aktiviert: mit Systembus verbunden" #: dnsmasq.c:889 msgid "UBus support enabled: bus connection pending" msgstr "UBus-Unterstützung aktiviert: Bus-Verbindung wird hergestellt" #: dnsmasq.c:909 msgid "DNSSEC validation enabled but all unsigned answers are trusted" msgstr "DNSSEC-Validierung aktiviert, jedoch wird allen unsignierten Antworten vertraut" #: dnsmasq.c:911 msgid "DNSSEC validation enabled" msgstr "DNSSEC-Validierung aktiviert" #: dnsmasq.c:915 msgid "DNSSEC signature timestamps not checked until receipt of SIGINT" msgstr "DNSSEC-Signatur-Zeitstempel werden erst nach Empfang von SIGINT überprüft" #: dnsmasq.c:918 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:921 #, c-format msgid "configured with trust anchor for %s keytag %u" msgstr "konfiguriert mit Vertrauensanker für %s Schlüsselanhänger %u" #: dnsmasq.c:927 #, c-format msgid "warning: failed to change owner of %s: %s" msgstr "Warnung: konnte den Besitzer von %s nicht ändern: %s" #: dnsmasq.c:932 msgid "setting --bind-interfaces option because of OS limitations" msgstr "Aktiviere --bind-interfaces wegen Einschränkungen des Betriebssystems" #: dnsmasq.c:945 #, c-format msgid "warning: interface %s does not currently exist" msgstr "Warnung: Schnittstelle %s existiert derzeit nicht" #: dnsmasq.c:950 msgid "warning: ignoring resolv-file flag because no-resolv is set" msgstr "Warnung: Ignoriere \"resolv-file\", weil \"no-resolv\" aktiv ist" #: dnsmasq.c:953 msgid "warning: no upstream servers configured" msgstr "Warnung: keine vorgeschalteten Server konfiguriert" #: dnsmasq.c:957 #, c-format msgid "asynchronous logging enabled, queue limit is %d messages" msgstr "asynchrone Protokollierung eingeschaltet, Warteschlange fasst %d Nachrichten" #: dnsmasq.c:978 msgid "IPv6 router advertisement enabled" msgstr "IPv6-Router-Advertisement aktiviert" #: dnsmasq.c:983 #, c-format msgid "DHCP, sockets bound exclusively to interface %s" msgstr "DHCP, Sockets exklusiv an die Schnittstelle %s gebunden" # FIXME: this and the next few must be full strings to be translatable - do not assemble in code" #: dnsmasq.c:1000 msgid "root is " msgstr "Wurzel ist " #: dnsmasq.c:1000 msgid "enabled" msgstr "Aktiviert" #: dnsmasq.c:1002 msgid "secure mode" msgstr "sicherer Modus" #: dnsmasq.c:1003 msgid "single port mode" msgstr "Einzelport-Modus" #: dnsmasq.c:1006 #, c-format msgid "warning: %s inaccessible" msgstr "Warnung: %s unerreichbar" #: dnsmasq.c:1010 #, c-format msgid "warning: TFTP directory %s inaccessible" msgstr "Warnung: Das TFTP-Verzeichnis %s ist unerreichbar" #: dnsmasq.c:1036 #, c-format msgid "restricting maximum simultaneous TFTP transfers to %d" msgstr "Begrenze gleichzeitige TFTP-Übertragungen auf maximal %d" #: dnsmasq.c:1095 #, c-format msgid "error binding DHCP socket to device %s" msgstr "Fehler beim Binden des DHCP-Sockets an Gerät %s" #: dnsmasq.c:1229 msgid "connected to system DBus" msgstr "Mit System-DBus verbunden" #: dnsmasq.c:1250 msgid "connected to system UBus" msgstr "mit System-UBus verbunden" #: dnsmasq.c:1416 #, c-format msgid "cannot fork into background: %s" msgstr "kann nicht in den Hintergrund abspalten: %s" #: dnsmasq.c:1420 #, c-format msgid "failed to create helper: %s" msgstr "kann Helfer nicht erzeugen: %s" #: dnsmasq.c:1424 #, c-format msgid "setting capabilities failed: %s" msgstr "kann \"capabilities\" nicht setzen: %s" #: dnsmasq.c:1428 #, c-format msgid "failed to change user-id to %s: %s" msgstr "Kann nicht Benutzerrechte %s annehmen: %s" #: dnsmasq.c:1432 #, c-format msgid "failed to change group-id to %s: %s" msgstr "Kann nicht Gruppenrechte %s annehmen: %s" #: dnsmasq.c:1436 #, c-format msgid "failed to open pidfile %s: %s" msgstr "kann die Prozessidentifikations-(PID)-Datei %s nicht öffnen: %s" #: dnsmasq.c:1440 #, c-format msgid "cannot open log %s: %s" msgstr "Kann Logdatei %s nicht öffnen: %s" #: dnsmasq.c:1444 #, c-format msgid "failed to load Lua script: %s" msgstr "Konnte Lua-Script nicht laden: %s" #: dnsmasq.c:1448 #, c-format msgid "TFTP directory %s inaccessible: %s" msgstr "Das TFTP-Verzeichnis %s ist unerreichbar: %s" #: dnsmasq.c:1452 #, c-format msgid "cannot create timestamp file %s: %s" msgstr "Kann keine Zeitstempel-Datei %s erzeugen: %s" #: dnsmasq.c:1536 #, c-format msgid "script process killed by signal %d" msgstr "Skriptprozess durch Signal %d beendet" #: dnsmasq.c:1540 #, c-format msgid "script process exited with status %d" msgstr "Scriptprozess hat sich mit Status %d beendet" #: dnsmasq.c:1544 #, c-format msgid "failed to execute %s: %s" msgstr "konnte %s nicht ausführen: %s" #: dnsmasq.c:1584 msgid "now checking DNSSEC signature timestamps" msgstr "Prüfe jetzt Zeitstempel der DNSSEC-Signaturen" #: dnsmasq.c:1619 dnssec.c:160 dnssec.c:204 #, c-format msgid "failed to update mtime on %s: %s" msgstr "kann die mtime nicht auf %s aktualisieren: %s" #: dnsmasq.c:1631 msgid "exiting on receipt of SIGTERM" msgstr "beende nach Empfang von SIGTERM" #: dnsmasq.c:1659 #, c-format msgid "failed to access %s: %s" msgstr "konnte auf %s nicht zugreifen: %s" #: dnsmasq.c:1690 #, c-format msgid "reading %s" msgstr "lese %s" #: dnsmasq.c:1706 #, c-format msgid "no servers found in %s, will retry" msgstr "keine Server in %s gefunden, werde nochmal versuchen" #: dhcp.c:51 #, c-format msgid "cannot create DHCP socket: %s" msgstr "kann DHCP-Socket nicht erzeugen: %s" #: dhcp.c:66 #, c-format msgid "failed to set options on DHCP socket: %s" msgstr "kann Optionen für DHCP-Socket nicht setzen: %s" #: dhcp.c:87 #, 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:99 #, c-format msgid "failed to bind DHCP server socket: %s" msgstr "kann nicht an DHCP-Server-Socket binden: %s" #: dhcp.c:125 #, c-format msgid "cannot create ICMP raw socket: %s." msgstr "kann ICMP-Rohdaten-Socket nicht erzeugen: %s." #: dhcp.c:254 dhcp6.c:186 #, c-format msgid "unknown interface %s in bridge-interface" msgstr "unbekannte Schnittstelle %s in bridge-interface" #: dhcp.c:295 #, c-format msgid "DHCP packet received on %s which has no address" msgstr "DHCP-Paket ohne Adresse an Schnittstelle %s empfangen" #: dhcp.c:429 #, c-format msgid "ARP-cache injection failed: %s" msgstr "Einspeisen in ARP-Zwischenspeicher fehlgeschlagen: %s" #: dhcp.c:490 #, c-format msgid "Error sending DHCP packet to %s: %s" msgstr "Fehler beim Senden des DHCP-Pakets an %s: %s" #: dhcp.c:547 #, 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:930 #, c-format msgid "bad line at %s line %d" msgstr "ungültige Zeile %2$d in Datei %1$s" #: dhcp.c:973 #, 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:1034 #, c-format msgid "read %s - %d addresses" msgstr "%s gelesen - %d Adressen" #: dhcp.c:1136 #, c-format msgid "Cannot broadcast DHCP relay via interface %s" msgstr "DHCP-Relay kann nicht über die Schnittstelle %s gesendet werden" #: dhcp.c:1160 #, c-format msgid "broadcast via %s" msgstr "broadcast via %s" #: dhcp.c:1163 rfc3315.c:2219 #, c-format msgid "DHCP relay at %s -> %s" msgstr "DHCP-Relay bei %s -> %s" #: lease.c:64 #, c-format msgid "ignoring invalid line in lease database: %s %s %s %s ..." msgstr "ignoriere ungültige Zeile in Lease-Datenbank: %s %s %s %s ..." #: lease.c:101 #, c-format msgid "ignoring invalid line in lease database, bad address: %s" msgstr "ignoriere ungültige Zeile in Lease-Datenbank, fehlerhafte Adresse: %s" #: lease.c:108 msgid "too many stored leases" msgstr "zu viele Leases gespeichert" #: lease.c:176 #, c-format msgid "cannot open or create lease file %s: %s" msgstr "kann Lease-Datei %s nicht öffnen oder anlegen: %s" #: lease.c:185 msgid "failed to parse lease database cleanly" msgstr "sauberes Aufgliedern der Lease-Datenbank fehlgeschlagen" #: lease.c:188 #, c-format msgid "failed to read lease file %s: %s" msgstr "konnte Lease-Datei %s nicht lesen: %s" #: lease.c:204 #, c-format msgid "cannot run lease-init script %s: %s" msgstr "kann lease-init-Skript %s nicht ausführen: %s" #: lease.c:210 #, c-format msgid "lease-init script returned exit code %s" msgstr "lease-init-Skript beendete sich mit Code %s" #: lease.c:381 #, c-format msgid "failed to write %s: %s (retry in %u s)" msgstr "Konnte %s nicht schreiben: %s (Neuversuch in %u s)" #: lease.c:955 #, c-format msgid "Ignoring domain %s for DHCP host name %s" msgstr "Ignoriere Domäne %s für DHCP-Hostnamen %s" #: rfc2131.c:378 msgid "with subnet selector" msgstr "mit Subnetz-Wähler" #: rfc2131.c:383 msgid "via" msgstr "via" # FIXME: this and the next few are not translatable. Please provide full # strings, do not programmatically assemble them. #: rfc2131.c:389 #, c-format msgid "no address range available for DHCP request %s %s" msgstr "Kein verfügbarer Adress-Bereich für DHCP-Anfrage %s %s" #: rfc2131.c:403 #, c-format msgid "%u available DHCP subnet: %s/%s" msgstr "%u verfügbares DHCP-Subnetz: %s/%s" #: rfc2131.c:409 rfc3315.c:320 #, c-format msgid "%u available DHCP range: %s -- %s" msgstr "%u verfügbarer DHCP-Bereich: %s - %s" #: rfc2131.c:521 #, c-format msgid "%u vendor class: %s" msgstr "%u \"Vendor class\": %s" #: rfc2131.c:523 #, c-format msgid "%u user class: %s" msgstr "%u Benutzerklasse: %s" # FIXME: do not programmatically assemble strings - untranslatable #: rfc2131.c:557 msgid "disabled" msgstr "deaktiviert" #: rfc2131.c:598 rfc2131.c:1087 rfc2131.c:1536 rfc3315.c:633 rfc3315.c:816 #: rfc3315.c:1122 msgid "ignored" msgstr "ignoriert" #: rfc2131.c:613 rfc2131.c:1340 rfc3315.c:868 msgid "address in use" msgstr "Adresse in Gebrauch" #: rfc2131.c:627 rfc2131.c:1141 msgid "no address available" msgstr "keine Adresse verfügbar" #: rfc2131.c:634 rfc2131.c:1302 msgid "wrong network" msgstr "Falsches Netzwerk" #: rfc2131.c:649 msgid "no address configured" msgstr "keine Adresse konfiguriert" #: rfc2131.c:655 rfc2131.c:1353 msgid "no leases left" msgstr "keine Leases übrig" #: rfc2131.c:756 rfc3315.c:500 #, c-format msgid "%u client provides name: %s" msgstr "%u Klient stellt Name bereit: %s" #: rfc2131.c:885 msgid "PXE BIS not supported" msgstr "PXE BIS nicht unterstützt" #: rfc2131.c:1054 rfc3315.c:1223 #, 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:1075 msgid "unknown lease" msgstr "Unbekannte Lease" #: rfc2131.c:1110 #, 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:1120 #, 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:1123 #, 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:1139 rfc2131.c:1346 msgid "no unique-id" msgstr "keine eindeutige ID" #: rfc2131.c:1238 msgid "wrong server-ID" msgstr "falsche Server-ID" #: rfc2131.c:1257 msgid "wrong address" msgstr "falsche Adresse" #: rfc2131.c:1275 rfc3315.c:976 msgid "lease not found" msgstr "Lease nicht gefunden" #: rfc2131.c:1310 msgid "address not available" msgstr "Adresse nicht verfügbar" #: rfc2131.c:1321 msgid "static lease available" msgstr "Statischer Lease verfügbar" #: rfc2131.c:1325 msgid "address reserved" msgstr "Adresse reserviert" #: rfc2131.c:1334 #, c-format msgid "abandoning lease to %s of %s" msgstr "Gebe Lease von %2$s an %1$s auf" #: rfc2131.c:1870 #, c-format msgid "%u bootfile name: %s" msgstr "%u Name der Bootdatei: %s" #: rfc2131.c:1879 #, c-format msgid "%u server name: %s" msgstr "%u Servername: %s" #: rfc2131.c:1889 #, c-format msgid "%u next server: %s" msgstr "%u nächster Server: %s" #: rfc2131.c:1893 #, c-format msgid "%u broadcast response" msgstr "%u Antwort per Broadcast" #: rfc2131.c:1956 #, 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:2267 msgid "PXE menu too large" msgstr "PXE-Menüeintrag zu groß" #: rfc2131.c:2430 rfc3315.c:1517 #, c-format msgid "%u requested options: %s" msgstr "%u angeforderte Optionen: %s" #: rfc2131.c:2747 #, 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" #: rfc2131.c:2810 #, c-format msgid "%u reply delay: %d" msgstr "%u Antwortverzögerung: %d" #: netlink.c:86 #, c-format msgid "cannot create netlink socket: %s" msgstr "kann Netlink-Socket nicht erzeugen: %s" #: netlink.c:379 #, c-format msgid "netlink returns error: %s" msgstr "Netlink liefert Fehler %s" #: dbus.c:491 #, c-format msgid "Enabling --%s option from D-Bus" msgstr "Aktiviere --%s Option von D-Bus" #: dbus.c:496 #, c-format msgid "Disabling --%s option from D-Bus" msgstr "Deaktiviere --%s Option von D-Bus" #: dbus.c:857 msgid "setting upstream servers from DBus" msgstr "vorgeschaltete Server von DBus festgelegt" #: dbus.c:907 msgid "could not register a DBus message handler" msgstr "konnte Steuerungsprogramm für DBus-Nachrichten nicht anmelden" #: bpf.c:261 #, c-format msgid "cannot create DHCP BPF socket: %s" msgstr "konnte DHCP-BPF-Socket nicht einrichten: %s" #: bpf.c:289 #, 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" #: bpf.c:374 #, c-format msgid "cannot create PF_ROUTE socket: %s" msgstr "Kann PF_ROUTE-Socket nicht erzeugen: %s" #: bpf.c:395 msgid "Unknown protocol version from route socket" msgstr "Unbekannte Protokollversion vom Route-Socket" #: helper.c:150 msgid "lease() function missing in Lua script" msgstr "lease()-Funktion fehlt im Lua-Skript" #: tftp.c:353 msgid "unable to get free port for TFTP" msgstr "konnte keinen freien Port für TFTP bekommen" #: tftp.c:369 #, c-format msgid "unsupported request from %s" msgstr "nicht unterstützte Anfrage von %s" #: tftp.c:520 #, c-format msgid "file %s not found for %s" msgstr "Datei %s nicht gefunden für %s" #: tftp.c:609 #, c-format msgid "ignoring packet from %s (TID mismatch)" msgstr "Paket von %s wird ignoriert (TID-Nichtübereinstimmung)" #: tftp.c:662 #, c-format msgid "failed sending %s to %s" msgstr "konnte %s nicht an %s senden" #: tftp.c:662 #, c-format msgid "sent %s to %s" msgstr "%s an %s verschickt" #: tftp.c:712 #, c-format msgid "error %d %s received from %s" msgstr "Fehler %d %s von %s empfangen" #: log.c:203 #, c-format msgid "overflow: %d log entries lost" msgstr "Überlauf: %d Protokolleinträge verloren" #: log.c:281 #, c-format msgid "log failed: %s" msgstr "Protokollierung fehlgeschlagen: %s" #: log.c:490 msgid "FAILED to start up" msgstr "Start FEHLGESCHLAGEN" #: conntrack.c:63 #, c-format msgid "Conntrack connection mark retrieval failed: %s" msgstr "\"Conntrack connection mark\"-Abruf fehlgeschlagen: %s" #: dhcp6.c:51 #, c-format msgid "cannot create DHCPv6 socket: %s" msgstr "Kann DHCPv6-Socket nicht erzeugen: %s" #: dhcp6.c:72 #, 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:84 #, c-format msgid "failed to bind DHCPv6 server socket: %s" msgstr "Kann nicht an DHCPv6-Server-Socket binden: %s" #: rfc3315.c:173 #, 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 Relais bei %s" #: rfc3315.c:182 #, 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:317 #, c-format msgid "%u available DHCPv6 subnet: %s/%d" msgstr "%u verfügbares DHCPv6-Subnetz: %s/%d" #: rfc3315.c:400 #, c-format msgid "%u vendor class: %u" msgstr "%u Herstellerklasse: %u" #: rfc3315.c:448 #, c-format msgid "%u client MAC address: %s" msgstr "%u Klient MAC-Adresse: %s" #: rfc3315.c:763 rfc3315.c:860 msgid "address unavailable" msgstr "Adresse nicht verfügbar" #: rfc3315.c:775 rfc3315.c:904 rfc3315.c:1273 msgid "success" msgstr "Erfolg" #: rfc3315.c:790 rfc3315.c:799 rfc3315.c:912 rfc3315.c:914 rfc3315.c:1048 msgid "no addresses available" msgstr "keine Adressen verfügbar" #: rfc3315.c:891 msgid "not on link" msgstr "nicht on link" #: rfc3315.c:980 rfc3315.c:1181 rfc3315.c:1262 msgid "no binding found" msgstr "Keine Bindung gefunden" #: rfc3315.c:1017 msgid "deprecated" msgstr "veraltet" #: rfc3315.c:1024 msgid "address invalid" msgstr "Adresse ungültig" #: rfc3315.c:1082 rfc3315.c:1084 msgid "confirm failed" msgstr "Bestätigung fehlgeschlagen" #: rfc3315.c:1099 msgid "all addresses still on link" msgstr "Alle Adressen immer noch in Verbindung" #: rfc3315.c:1190 msgid "release received" msgstr "Freigabe empfangen" #: rfc3315.c:2200 #, c-format msgid "Cannot multicast DHCP relay via interface %s" msgstr "Multicast-DHCP-Relay über Schnittstelle %s nicht möglich" #: rfc3315.c:2216 #, c-format msgid "multicast via %s" msgstr "multicast via %s" #: dhcp-common.c:187 #, c-format msgid "Ignoring duplicate dhcp-option %d" msgstr "Ignoriere doppelt vorhandene DHCP-Option %d" #: dhcp-common.c:264 #, c-format msgid "%u tags: %s" msgstr "%u Marken: %s" #: dhcp-common.c:484 #, 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:518 #, 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:738 #, c-format msgid "Known DHCP options:\n" msgstr "Bekannte DHCP-Optionen:\n" #: dhcp-common.c:749 #, c-format msgid "Known DHCPv6 options:\n" msgstr "Bekannte DHCPv6-Optionen:\n" #: dhcp-common.c:946 msgid ", prefix deprecated" msgstr ", Präfix veraltet" #: dhcp-common.c:949 #, c-format msgid ", lease time " msgstr ", Leasezeit " #: dhcp-common.c:991 #, c-format msgid "%s stateless on %s%.0s%.0s%s" msgstr "%s zustandslos auf %s%.0s%.0s%s" #: dhcp-common.c:993 #, 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:995 #, c-format msgid "%s, proxy on subnet %.0s%s%.0s%.0s" msgstr "%s, Proxy im Subnetz %.0s%s%.0s%.0s" #: dhcp-common.c:996 #, c-format msgid "%s, IP range %s -- %s%s%.0s" msgstr "%s, IP-Bereich %s -- %s%s%.0s" #: dhcp-common.c:1009 #, c-format msgid "DHCPv4-derived IPv6 names on %s%s" msgstr "DHCPv4-abgeleitete IPv6 Namen auf %s%s" #: dhcp-common.c:1012 #, c-format msgid "router advertisement on %s%s" msgstr "Router-Advertisment auf %s%s" #: dhcp-common.c:1043 #, c-format msgid "DHCP relay from %s via %s" msgstr "DHCP-Relay von %s über %s" #: dhcp-common.c:1045 #, c-format msgid "DHCP relay from %s to %s via %s" msgstr "DHCP Weiterleitung von %s nach %s über %s" #: dhcp-common.c:1048 #, c-format msgid "DHCP relay from %s to %s" msgstr "DHCP Weiterleitung von %s nach %s" #: radv.c:110 #, c-format msgid "cannot create ICMPv6 socket: %s" msgstr "Kann ICMPv6-Socket nicht erzeugen: %s" #: auth.c:462 #, c-format msgid "ignoring zone transfer request from %s" msgstr "ignoriere Zonentransfer-Anfrage von %s" #: ipset.c:99 #, c-format msgid "failed to create IPset control socket: %s" msgstr "konnte IPset-Kontroll-Socket nicht erzeugen: %s" #: ipset.c:211 #, c-format msgid "failed to update ipset %s: %s" msgstr "Aktualisierung von ipset %s fehlgeschlagen: %s" #: pattern.c:29 #, c-format msgid "[pattern.c:%d] Assertion failure: %s" msgstr "[pattern.c:%d] Assertion failure: %s" #: pattern.c:142 #, c-format msgid "Invalid DNS name: Invalid character %c." msgstr "Ungültiger DNS-Name: Ungültiges Zeichen %c." #: pattern.c:151 msgid "Invalid DNS name: Empty label." msgstr "Ungültiger DNS-Name: Leeres Label." #: pattern.c:156 msgid "Invalid DNS name: Label starts with hyphen." msgstr "Ungültiger DNS-Name: Label beginnt mit Bindestrich." #: pattern.c:170 msgid "Invalid DNS name: Label ends with hyphen." msgstr "Ungültiger DNS-Name: Bezeichnung endet mit Bindestrich." #: pattern.c:176 #, c-format msgid "Invalid DNS name: Label is too long (%zu)." msgstr "Ungültiger DNS-Name: Label ist zu lang (%zu)." #: pattern.c:184 #, c-format msgid "Invalid DNS name: Not enough labels (%zu)." msgstr "Ungültiger DNS-Name: Nicht genügend Labels (%zu)." #: pattern.c:189 msgid "Invalid DNS name: Final label is fully numeric." msgstr "Ungültiger DNS-Name: Das endgültige Label ist vollständig numerisch." #: pattern.c:199 msgid "Invalid DNS name: \"local\" pseudo-TLD." msgstr "Ungültiger DNS-Name: \"lokale\" Pseudo-TLD." #: pattern.c:204 #, c-format msgid "DNS name has invalid length (%zu)." msgstr "Der DNS-Name hat eine ungültige Länge (%zu)." #: pattern.c:258 #, c-format msgid "Invalid DNS name pattern: Invalid character %c." msgstr "Ungültiges DNS-Namensmuster: Ungültiges Zeichen %c." #: pattern.c:267 msgid "Invalid DNS name pattern: Empty label." msgstr "Ungültiges DNS-Namensmuster: Leeres Label." #: pattern.c:272 msgid "Invalid DNS name pattern: Label starts with hyphen." msgstr "Ungültiges DNS-Namensmuster: Bezeichnung beginnt mit Bindestrich." #: pattern.c:285 msgid "Invalid DNS name pattern: Wildcard character used more than twice per label." msgstr "Ungültiges DNS-Namensmuster: Platzhalterzeichen werden mehr als zweimal pro Label verwendet." #: pattern.c:295 msgid "Invalid DNS name pattern: Label ends with hyphen." msgstr "Ungültiges DNS-Namensmuster: Bezeichnung endet mit Bindestrich." #: pattern.c:301 #, c-format msgid "Invalid DNS name pattern: Label is too long (%zu)." msgstr "Ungültiges DNS-Namensmuster: Label ist zu lang (%zu)." #: pattern.c:309 #, c-format msgid "Invalid DNS name pattern: Not enough labels (%zu)." msgstr "Ungültiges DNS-Namensmuster: Nicht genügend Labels (%zu)." #: pattern.c:314 msgid "Invalid DNS name pattern: Wildcard within final two labels." msgstr "Ungültiges DNS-Namensmuster: Platzhalter innerhalb der letzten beiden Labels." #: pattern.c:319 msgid "Invalid DNS name pattern: Final label is fully numeric." msgstr "Ungültiges DNS-Namensmuster: Das endgültige Label ist vollständig numerisch." #: pattern.c:329 msgid "Invalid DNS name pattern: \"local\" pseudo-TLD." msgstr "Ungültiges DNS-Namensmuster: \"lokale\" Pseudo-TLD." #: pattern.c:334 #, c-format msgid "DNS name pattern has invalid length after removing wildcards (%zu)." msgstr "Das DNS-Namensmuster hat nach dem Entfernen von Platzhaltern (%zu) eine ungültige Länge." #: dnssec.c:206 msgid "system time considered valid, now checking DNSSEC signature timestamps." msgstr "Systemzeit als gültig betrachtet, prüfe jetzt DNSSEC Signatur-Zeitstempel." #: dnssec.c:1018 #, c-format msgid "Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support" msgstr "Unsichere DS-Antwort für %s, bitte Domainkonfiguration und Upstream DNS-Server für DNSSEC-Unterstützung überprüfen" #: blockdata.c:55 #, c-format msgid "pool memory in use %zu, max %zu, allocated %zu" msgstr "Speicherpool in Benutzung %zu, Max %zu, zugewiesen %zu" #: tables.c:61 #, c-format msgid "failed to access pf devices: %s" msgstr "konnte auf pf-Geräte nicht zugreifen: %s" #: tables.c:74 #, c-format msgid "warning: no opened pf devices %s" msgstr "Warnung: Keine geöffneten pf-Geräte %s" #: tables.c:82 #, c-format msgid "error: cannot use table name %s" msgstr "Fehler: Kann Tabellenname %s nicht benutzen" #: tables.c:90 #, c-format msgid "error: cannot strlcpy table name %s" msgstr "Fehler: Kann den Tabellennamen %s nicht mit strlcpy kopieren" #: tables.c:101 #, c-format msgid "IPset: error: %s" msgstr "IPset: Fehler: %s" #: tables.c:108 msgid "info: table created" msgstr "Info: Tabelle erstellt" #: tables.c:133 #, c-format msgid "warning: DIOCR%sADDRS: %s" msgstr "Warnung: DIOCR%sADDRS: %s" #: tables.c:137 #, c-format msgid "%d addresses %s" msgstr "%d Adressen %s" #: inotify.c:62 #, c-format msgid "cannot access path %s: %s" msgstr "Kann auf Pfad %s nicht zugreifen: %s" #: inotify.c:95 #, c-format msgid "failed to create inotify: %s" msgstr "Kann kein \"inotify\" erzeugen: %s" #: inotify.c:111 #, c-format msgid "too many symlinks following %s" msgstr "zu viele Symlinks beim Verfolgen von %s" #: inotify.c:127 #, c-format msgid "directory %s for resolv-file is missing, cannot poll" msgstr "Verzeichnis %s für resolv-file fehlt, kann nicht abfragen" #: inotify.c:131 inotify.c:200 #, c-format msgid "failed to create inotify for %s: %s" msgstr "Konnte \"inotify\" für %s nicht erzeugen: %s" #: inotify.c:178 inotify.c:185 #, c-format msgid "bad dynamic directory %s: %s" msgstr "fehlerhaftes dynamisches Verzeichnis %s: %s" #: inotify.c:186 msgid "not a directory" msgstr "ist kein Verzeichnis" #: inotify.c:299 #, c-format msgid "inotify: %s removed" msgstr "inotify: %s entfernt" #: inotify.c:301 #, c-format msgid "inotify: %s new or modified" msgstr "inotify: %s neu oder modifiziert" #: inotify.c:309 #, c-format msgid "inotify: flushed %u names read from %s" msgstr "inotify: %u Namen gelöscht, aus %s gelesen" #: dump.c:68 #, c-format msgid "cannot create %s: %s" msgstr "kann %s nicht erstellen: %s" #: dump.c:74 #, c-format msgid "bad header in %s" msgstr "Fehlerhafter Kopf in %s" #: dump.c:287 msgid "failed to write packet dump" msgstr "schreiben des Paketmitschnitts fehlgeschlagen" #: dump.c:289 #, c-format msgid "%u dumping packet %u mask 0x%04x" msgstr "%u dumpe Paket %u Maske 0x%04x" #: dump.c:291 #, c-format msgid "dumping packet %u mask 0x%04x" msgstr "Dumpe Packet %u Make 0x%04x" #: ubus.c:79 #, c-format msgid "UBus subscription callback: %s subscriber(s)" msgstr "UBus-Subskription Rückruf: %s Teilnehmer" #: ubus.c:99 #, c-format msgid "Cannot reconnect to UBus: %s" msgstr "Kann mit UBus nicht erneut verbinden: %s" #: ubus.c:135 msgid "Cannot set UBus listeners: no connection" msgstr "Kann UBus-Zuhörer nicht setzen: Keine Verbindung" #: ubus.c:155 msgid "Cannot poll UBus listeners: no connection" msgstr "Kann UBus-Zuhörer nicht abfragen: Keine Verbindung" #: ubus.c:168 msgid "Disconnecting from UBus" msgstr "Von System-UBus trennen" #: ubus.c:179 ubus.c:326 #, c-format msgid "UBus command failed: %d (%s)" msgstr "UBus-Befehl fehlgeschlagen: %d (%s)" #: hash-questions.c:40 msgid "Failed to create SHA-256 hash object" msgstr "Kann SHA-256-Hash-Objekt nicht erstellen" #: nftset.c:35 msgid "failed to create nftset context" msgstr "Fehler beim Erstellen des NFTSET-Kontexts" #~ msgid "bad IPv4 prefix" #~ msgstr "fehlerhaftes IPv4-Präfix" #~ msgid "Cannot initialize UBus: connection failed" #~ msgstr "Kann UBus nicht initialisieren: Verbindung fehlgeschlagen" #~ msgid "Cannot add object to UBus: %s" #~ msgstr "Kann Objekt zu UBus nicht hinzufügen: %s" #~ msgid "Failed to send UBus event: %s" #~ msgstr "Fehlschlag beim Sendes des UBus-Ereignisses: %s" dnsmasq-2.91/po/fi.po0000664000175000017500000014054514765043257012677 0ustar srksrk# Finnish translations for dnsmasq package. # This file is put in the public domain. # Simon Kelley , 2005. # msgid "" msgstr "" "Project-Id-Version: dnsmasq 2.24\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-06-18 12:24+0100\n" "PO-Revision-Date: 2017-07-17 18:30+0100\n" "Last-Translator: Simon Kelley \n" "Language-Team: Finnish \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:652 msgid "Internal error in cache." msgstr "" #: cache.c:1179 #, c-format msgid "failed to load names from %s: %s" msgstr "" #: cache.c:1201 dhcp.c:943 #, c-format msgid "bad address at %s line %d" msgstr "" #: cache.c:1254 dhcp.c:959 #, c-format msgid "bad name at %s line %d" msgstr "" #: cache.c:1265 #, c-format msgid "read %s - %d names" msgstr "" #: cache.c:1381 msgid "cleared cache" msgstr "" #: cache.c:1445 #, c-format msgid "No IPv4 address found for %s" msgstr "" #: cache.c:1491 #, c-format msgid "%s is a CNAME, not giving it to the DHCP lease of %s" msgstr "" #: cache.c:1515 #, 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:1760 #, c-format msgid "time %lu" msgstr "" #: cache.c:1761 #, c-format msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries." msgstr "" #: cache.c:1763 #, c-format msgid "queries forwarded %u, queries answered locally %u" msgstr "" #: cache.c:1766 #, c-format msgid "queries answered from stale cache %u" msgstr "" #: cache.c:1768 #, c-format msgid "queries for authoritative zones %u" msgstr "" #: cache.c:1796 #, c-format msgid "server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums" msgstr "" #: util.c:51 #, c-format msgid "failed to seed the random number generator: %s" msgstr "" #: util.c:246 msgid "failed to allocate memory" msgstr "" #: util.c:305 option.c:696 msgid "could not get memory" msgstr "" #: util.c:326 #, c-format msgid "cannot create pipe: %s" msgstr "" #: util.c:334 #, c-format msgid "failed to allocate %d bytes" msgstr "" #: util.c:344 #, c-format msgid "failed to reallocate %d bytes" msgstr "" #: util.c:465 #, c-format msgid "cannot read monotonic clock: %s" msgstr "" #: util.c:579 #, c-format msgid "infinite" msgstr "" #: util.c:867 #, c-format msgid "failed to find kernel version: %s" msgstr "" #: option.c:393 msgid "Specify local address(es) to listen on." msgstr "" #: option.c:394 msgid "Return ipaddr for all hosts in specified domains." msgstr "" #: option.c:395 msgid "Fake reverse lookups for RFC1918 private address ranges." msgstr "" #: option.c:396 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)." msgstr "" #: option.c:397 #, c-format msgid "Specify the size of the cache in entries (defaults to %s)." msgstr "" #: option.c:398 #, c-format msgid "Specify configuration file (defaults to %s)." msgstr "" #: option.c:399 msgid "Do NOT fork into the background: run in debug mode." msgstr "" #: option.c:400 msgid "Do NOT forward queries with no domain part." msgstr "" #: option.c:401 msgid "Return self-pointing MX records for local hosts." msgstr "" #: option.c:402 msgid "Expand simple names in /etc/hosts with domain-suffix." msgstr "" #: option.c:403 msgid "Don't forward spurious DNS requests from Windows hosts." msgstr "" #: option.c:404 msgid "Don't include IPv4 addresses in DNS answers." msgstr "" #: option.c:405 msgid "Don't include IPv6 addresses in DNS answers." msgstr "" #: option.c:406 msgid "Enable DHCP in the range given with lease duration." msgstr "" #: option.c:407 #, c-format msgid "Change to this group after startup (defaults to %s)." msgstr "" #: option.c:408 msgid "Set address or hostname for a specified machine." msgstr "" #: option.c:409 msgid "Read DHCP host specs from file." msgstr "" #: option.c:410 msgid "Read DHCP option specs from file." msgstr "" #: option.c:411 msgid "Read DHCP host specs from a directory." msgstr "" #: option.c:412 msgid "Read DHCP options from a directory." msgstr "" #: option.c:413 msgid "Evaluate conditional tag expression." msgstr "" #: option.c:414 #, c-format msgid "Do NOT load %s file." msgstr "" #: option.c:415 #, c-format msgid "Specify a hosts file to be read in addition to %s." msgstr "" #: option.c:416 msgid "Read hosts files from a directory." msgstr "" #: option.c:417 msgid "Specify interface(s) to listen on." msgstr "" #: option.c:418 msgid "Specify interface(s) NOT to listen on." msgstr "" #: option.c:419 msgid "Map DHCP user class to tag." msgstr "" #: option.c:420 msgid "Map RFC3046 circuit-id to tag." msgstr "" #: option.c:421 msgid "Map RFC3046 remote-id to tag." msgstr "" #: option.c:422 msgid "Map RFC3993 subscriber-id to tag." msgstr "" #: option.c:423 msgid "Specify vendor class to match for PXE requests." msgstr "" #: option.c:424 msgid "Don't do DHCP for hosts with tag set." msgstr "" #: option.c:425 msgid "Force broadcast replies for hosts with tag set." msgstr "" #: option.c:426 msgid "Do NOT fork into the background, do NOT run in debug mode." msgstr "" #: option.c:427 msgid "Assume we are the only DHCP server on the local network." msgstr "" #: option.c:428 #, c-format msgid "Specify where to store DHCP leases (defaults to %s)." msgstr "" #: option.c:429 msgid "Return MX records for local hosts." msgstr "" #: option.c:430 msgid "Specify an MX record." msgstr "" #: option.c:431 msgid "Specify BOOTP options to DHCP server." msgstr "" #: option.c:432 #, c-format msgid "Do NOT poll %s file, reload only on SIGHUP." msgstr "" #: option.c:433 msgid "Do NOT cache failed search results." msgstr "" #: option.c:434 msgid "Use expired cache data for faster reply." msgstr "" #: option.c:435 #, c-format msgid "Use nameservers strictly in the order given in %s." msgstr "" #: option.c:436 msgid "Specify options to be sent to DHCP clients." msgstr "" #: option.c:437 msgid "DHCP option sent even if the client does not request it." msgstr "" #: option.c:438 msgid "Specify port to listen for DNS requests on (defaults to 53)." msgstr "" #: option.c:439 #, c-format msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)." msgstr "" #: option.c:440 msgid "Log DNS queries." msgstr "" #: option.c:441 msgid "Force the originating port for upstream DNS queries." msgstr "" #: option.c:442 msgid "Set maximum number of random originating ports for a query." msgstr "" #: option.c:443 msgid "Do NOT read resolv.conf." msgstr "" #: option.c:444 #, c-format msgid "Specify path to resolv.conf (defaults to %s)." msgstr "" #: option.c:445 msgid "Specify path to file with server= options" msgstr "" #: option.c:446 msgid "Specify address(es) of upstream servers with optional domains." msgstr "" #: option.c:447 msgid "Specify address of upstream servers for reverse address queries" msgstr "" #: option.c:448 msgid "Never forward queries to specified domains." msgstr "" #: option.c:449 msgid "Specify the domain to be assigned in DHCP leases." msgstr "" #: option.c:450 msgid "Specify default target in an MX record." msgstr "" #: option.c:451 msgid "Specify time-to-live in seconds for replies from /etc/hosts." msgstr "" #: option.c:452 msgid "Specify time-to-live in seconds for negative caching." msgstr "" #: option.c:453 msgid "Specify time-to-live in seconds for maximum TTL to send to clients." msgstr "" #: option.c:454 msgid "Specify time-to-live ceiling for cache." msgstr "" #: option.c:455 msgid "Specify time-to-live floor for cache." msgstr "" #: option.c:456 msgid "Retry DNS queries after this many milliseconds." msgstr "" #: option.c:457 #, c-format msgid "Change to this user after startup. (defaults to %s)." msgstr "" #: option.c:458 msgid "Map DHCP vendor class to tag." msgstr "" #: option.c:459 msgid "Display dnsmasq version and copyright information." msgstr "" #: option.c:460 msgid "Translate IPv4 addresses from upstream servers." msgstr "" #: option.c:461 msgid "Specify a SRV record." msgstr "" #: option.c:462 msgid "Display this message. Use --help dhcp or --help dhcp6 for known DHCP options." msgstr "" #: option.c:463 #, c-format msgid "Specify path of PID file (defaults to %s)." msgstr "" #: option.c:464 #, c-format msgid "Specify maximum number of DHCP leases (defaults to %s)." msgstr "" #: option.c:465 msgid "Answer DNS queries based on the interface a query was sent to." msgstr "" #: option.c:466 msgid "Specify TXT DNS record." msgstr "" #: option.c:467 msgid "Specify PTR DNS record." msgstr "" #: option.c:468 msgid "Give DNS name to IPv4 address of interface." msgstr "" #: option.c:469 msgid "Bind only to interfaces in use." msgstr "" #: option.c:470 #, c-format msgid "Read DHCP static host information from %s." msgstr "" #: option.c:471 msgid "Enable the DBus interface for setting upstream servers, etc." msgstr "" #: option.c:472 msgid "Enable the UBus interface." msgstr "" #: option.c:473 msgid "Do not provide DHCP on this interface, only provide DNS." msgstr "" #: option.c:474 msgid "Enable dynamic address allocation for bootp." msgstr "" #: option.c:475 msgid "Map MAC address (with wildcards) to option set." msgstr "" #: option.c:476 msgid "Treat DHCP requests on aliases as arriving from interface." msgstr "" #: option.c:477 msgid "Specify extra networks sharing a broadcast domain for DHCP" msgstr "" #: option.c:478 msgid "Disable ICMP echo address checking in the DHCP server." msgstr "" #: option.c:479 msgid "Shell script to run on DHCP lease creation and destruction." msgstr "" #: option.c:480 msgid "Lua script to run on DHCP lease creation and destruction." msgstr "" #: option.c:481 msgid "Run lease-change scripts as this user." msgstr "" #: option.c:482 msgid "Call dhcp-script with changes to local ARP table." msgstr "" #: option.c:483 msgid "Read configuration from all the files in this directory." msgstr "" #: option.c:484 msgid "Execute file and read configuration from stdin." msgstr "" #: option.c:485 msgid "Log to this syslog facility or file. (defaults to DAEMON)" msgstr "" #: option.c:486 msgid "Do not use leasefile." msgstr "" #: option.c:487 #, c-format msgid "Maximum number of concurrent DNS queries. (defaults to %s)" msgstr "" #: option.c:488 #, c-format msgid "Clear DNS cache when reloading %s." msgstr "" #: option.c:489 msgid "Ignore hostnames provided by DHCP clients." msgstr "" #: option.c:490 msgid "Do NOT reuse filename and server fields for extra DHCP options." msgstr "" #: option.c:491 msgid "Enable integrated read-only TFTP server." msgstr "" #: option.c:492 msgid "Export files by TFTP only from the specified subtree." msgstr "" #: option.c:493 msgid "Add client IP or hardware address to tftp-root." msgstr "" #: option.c:494 msgid "Allow access only to files owned by the user running dnsmasq." msgstr "" #: option.c:495 msgid "Do not terminate the service if TFTP directories are inaccessible." msgstr "" #: option.c:496 #, c-format msgid "Maximum number of concurrent TFTP transfers (defaults to %s)." msgstr "" #: option.c:497 msgid "Maximum MTU to use for TFTP transfers." msgstr "" #: option.c:498 msgid "Disable the TFTP blocksize extension." msgstr "" #: option.c:499 msgid "Convert TFTP filenames to lowercase" msgstr "" #: option.c:500 msgid "Ephemeral port range for use by TFTP transfers." msgstr "" #: option.c:501 msgid "Use only one port for TFTP server." msgstr "" #: option.c:502 msgid "Extra logging for DHCP." msgstr "" #: option.c:503 msgid "Enable async. logging; optionally set queue length." msgstr "" #: option.c:504 msgid "Stop DNS rebinding. Filter private IP ranges when resolving." msgstr "" #: option.c:505 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers." msgstr "" #: option.c:506 msgid "Inhibit DNS-rebind protection on this domain." msgstr "" #: option.c:507 msgid "Always perform DNS queries to all servers." msgstr "" #: option.c:508 msgid "Set tag if client includes matching option in request." msgstr "" #: option.c:509 msgid "Set tag if client provides given name." msgstr "" #: option.c:510 msgid "Use alternative ports for DHCP." msgstr "" #: option.c:511 msgid "Specify NAPTR DNS record." msgstr "" #: option.c:512 msgid "Specify lowest port available for DNS query transmission." msgstr "" #: option.c:513 msgid "Specify highest port available for DNS query transmission." msgstr "" #: option.c:514 msgid "Use only fully qualified domain names for DHCP clients." msgstr "" #: option.c:515 msgid "Generate hostnames based on MAC address for nameless clients." msgstr "" #: option.c:516 msgid "Use these DHCP relays as full proxies." msgstr "" #: option.c:517 msgid "Relay DHCP requests to a remote server" msgstr "" #: option.c:518 msgid "Specify alias name for LOCAL DNS name." msgstr "" #: option.c:519 msgid "Prompt to send to PXE clients." msgstr "" #: option.c:520 msgid "Boot service for PXE menu." msgstr "" #: option.c:521 msgid "Check configuration syntax." msgstr "" #: option.c:522 msgid "Add requestor's MAC address to forwarded DNS queries." msgstr "" #: option.c:523 msgid "Strip MAC information from queries." msgstr "" #: option.c:524 msgid "Add specified IP subnet to forwarded DNS queries." msgstr "" #: option.c:525 msgid "Strip ECS information from queries." msgstr "" #: option.c:526 msgid "Add client identification to forwarded DNS queries." msgstr "" #: option.c:527 msgid "Proxy DNSSEC validation results from upstream nameservers." msgstr "" #: option.c:528 msgid "Attempt to allocate sequential IP addresses to DHCP clients." msgstr "" #: option.c:529 msgid "Ignore client identifier option sent by DHCP clients." msgstr "" #: option.c:530 msgid "Copy connection-track mark from queries to upstream connections." msgstr "" #: option.c:531 msgid "Allow DHCP clients to do their own DDNS updates." msgstr "" #: option.c:532 msgid "Send router-advertisements for interfaces doing DHCPv6" msgstr "" #: option.c:533 msgid "Specify DUID_EN-type DHCPv6 server DUID" msgstr "" #: option.c:534 msgid "Specify host (A/AAAA and PTR) records" msgstr "" #: option.c:535 msgid "Specify host record in interface subnet" msgstr "" #: option.c:536 msgid "Specify certification authority authorization record" msgstr "" #: option.c:537 msgid "Specify arbitrary DNS resource record" msgstr "" #: option.c:538 msgid "Bind to interfaces in use - check for new interfaces" msgstr "" #: option.c:539 msgid "Export local names to global DNS" msgstr "" #: option.c:540 msgid "Domain to export to global DNS" msgstr "" #: option.c:541 msgid "Set TTL for authoritative replies" msgstr "" #: option.c:542 msgid "Set authoritative zone information" msgstr "" #: option.c:543 msgid "Secondary authoritative nameservers for forward domains" msgstr "" #: option.c:544 msgid "Peers which are allowed to do zone transfer" msgstr "" #: option.c:545 msgid "Specify ipsets to which matching domains should be added" msgstr "" #: option.c:546 msgid "Specify nftables sets to which matching domains should be added" msgstr "" #: option.c:547 msgid "Enable filtering of DNS queries with connection-track marks." msgstr "" #: option.c:548 msgid "Set allowed DNS patterns for a connection-track mark." msgstr "" #: option.c:549 msgid "Specify a domain and address range for synthesised names" msgstr "" #: option.c:550 msgid "Activate DNSSEC validation" msgstr "" #: option.c:551 msgid "Specify trust anchor key digest." msgstr "" #: option.c:552 msgid "Disable upstream checking for DNSSEC debugging." msgstr "" #: option.c:553 msgid "Ensure answers without DNSSEC are in unsigned zones." msgstr "" #: option.c:554 msgid "Don't check DNSSEC signature timestamps until first cache-reload" msgstr "" #: option.c:555 msgid "Timestamp file to verify system clock for DNSSEC" msgstr "" #: option.c:556 msgid "Set MTU, priority, resend-interval and router-lifetime" msgstr "" #: option.c:557 msgid "Do not log routine DHCP." msgstr "" #: option.c:558 msgid "Do not log routine DHCPv6." msgstr "" #: option.c:559 msgid "Do not log RA." msgstr "" #: option.c:560 msgid "Log debugging information." msgstr "" #: option.c:561 msgid "Accept queries only from directly-connected networks." msgstr "" #: option.c:562 msgid "Detect and remove DNS forwarding loops." msgstr "" #: option.c:563 msgid "Ignore DNS responses containing ipaddr." msgstr "" #: option.c:564 msgid "Set TTL in DNS responses with DHCP-derived addresses." msgstr "" #: option.c:565 msgid "Delay DHCP replies for at least number of seconds." msgstr "" #: option.c:566 msgid "Enables DHCPv4 Rapid Commit option." msgstr "" #: option.c:567 msgid "Path to debug packet dump file" msgstr "" #: option.c:568 msgid "Mask which packets to dump" msgstr "" #: option.c:569 msgid "Call dhcp-script when lease expiry changes." msgstr "" #: option.c:570 msgid "Send Cisco Umbrella identifiers including remote IP." msgstr "" #: option.c:571 msgid "Do not log routine TFTP." msgstr "" #: option.c:572 msgid "Suppress round-robin ordering of DNS records." msgstr "" #: option.c:802 #, c-format msgid "" "Usage: dnsmasq [options]\n" "\n" msgstr "" #: option.c:804 #, c-format msgid "Use short options only on the command line.\n" msgstr "" #: option.c:806 #, c-format msgid "Valid options are:\n" msgstr "" #: option.c:853 option.c:1055 msgid "bad address" msgstr "" #: option.c:882 option.c:886 msgid "bad port" msgstr "" #: option.c:899 option.c:1002 option.c:1048 msgid "interface binding not supported" msgstr "" #: option.c:955 msgid "Cannot resolve server name" msgstr "" #: option.c:991 msgid "cannot use IPv4 server address with IPv6 source address" msgstr "" #: option.c:997 option.c:1043 msgid "interface can only be specified once" msgstr "" #: option.c:1011 option.c:4785 msgid "bad interface name" msgstr "" #: option.c:1037 msgid "cannot use IPv6 server address with IPv4 source address" msgstr "" #: option.c:1124 msgid "bad IPv4 prefix length" msgstr "" #: option.c:1155 option.c:1165 option.c:1240 option.c:1250 option.c:5360 msgid "error" msgstr "" #: option.c:1207 msgid "bad IPv6 prefix length" msgstr "" #: option.c:1467 msgid "inappropriate vendor:" msgstr "" #: option.c:1474 msgid "inappropriate encap:" msgstr "" #: option.c:1500 msgid "unsupported encapsulation for IPv6 option" msgstr "" #: option.c:1514 msgid "bad dhcp-option" msgstr "" #: option.c:1592 msgid "bad IP address" msgstr "" #: option.c:1595 option.c:1734 option.c:3928 msgid "bad IPv6 address" msgstr "" #: option.c:1688 msgid "bad IPv4 address" msgstr "" #: option.c:1761 option.c:1856 msgid "bad domain in dhcp-option" msgstr "" #: option.c:1900 msgid "dhcp-option too long" msgstr "" #: option.c:1907 msgid "illegal dhcp-match" msgstr "" #: option.c:1966 msgid "illegal repeated flag" msgstr "" #: option.c:1974 msgid "illegal repeated keyword" msgstr "" #: option.c:2056 option.c:5533 #, c-format msgid "cannot access directory %s: %s" msgstr "" #: option.c:2102 tftp.c:573 dump.c:72 #, c-format msgid "cannot access %s: %s" msgstr "" #: option.c:2219 msgid "setting log facility is not possible under Android" msgstr "" #: option.c:2228 msgid "bad log facility" msgstr "" #: option.c:2281 msgid "bad MX preference" msgstr "" #: option.c:2289 msgid "bad MX name" msgstr "" #: option.c:2304 msgid "bad MX target" msgstr "" #: option.c:2324 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts" msgstr "" #: option.c:2328 msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts" msgstr "" #: option.c:2447 msgid "invalid auth-zone" msgstr "" #: option.c:2589 option.c:2621 msgid "bad prefix length" msgstr "" #: option.c:2601 option.c:2642 option.c:2696 msgid "bad prefix" msgstr "" #: option.c:2716 msgid "prefix length too small" msgstr "" #: option.c:3010 msgid "Bad address in --address" msgstr "" #: option.c:3110 msgid "recompile with HAVE_IPSET defined to enable ipset directives" msgstr "" #: option.c:3117 msgid "recompile with HAVE_NFTSET defined to enable nftset directives" msgstr "" #: option.c:3192 option.c:3210 msgid "recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives" msgstr "" #: option.c:3496 msgid "bad port range" msgstr "" #: option.c:3522 msgid "bad bridge-interface" msgstr "" #: option.c:3566 msgid "bad shared-network" msgstr "" #: option.c:3620 msgid "only one tag allowed" msgstr "" #: option.c:3641 option.c:3657 option.c:3783 option.c:3791 option.c:3834 msgid "bad dhcp-range" msgstr "" #: option.c:3675 msgid "inconsistent DHCP range" msgstr "" #: option.c:3741 msgid "prefix length must be exactly 64 for RA subnets" msgstr "" #: option.c:3743 msgid "prefix length must be exactly 64 for subnet constructors" msgstr "" #: option.c:3746 msgid "prefix length must be at least 64" msgstr "" #: option.c:3749 msgid "inconsistent DHCPv6 range" msgstr "" #: option.c:3768 msgid "prefix must be zero with \"constructor:\" argument" msgstr "" #: option.c:3893 option.c:3971 msgid "bad hex constant" msgstr "" #: option.c:3946 msgid "bad IPv6 prefix" msgstr "" #: option.c:3994 #, c-format msgid "duplicate dhcp-host IP address %s" msgstr "" #: option.c:4056 msgid "bad DHCP host name" msgstr "" #: option.c:4142 msgid "bad tag-if" msgstr "" #: option.c:4490 option.c:5046 msgid "invalid port number" msgstr "" #: option.c:4546 msgid "bad dhcp-proxy address" msgstr "" #: option.c:4627 msgid "Bad dhcp-relay" msgstr "" #: option.c:4671 msgid "bad RA-params" msgstr "" #: option.c:4681 msgid "bad DUID" msgstr "" #: option.c:4715 msgid "missing address in alias" msgstr "" #: option.c:4721 msgid "invalid alias range" msgstr "" #: option.c:4770 msgid "missing address in dynamic host" msgstr "" #: option.c:4785 msgid "bad dynamic host" msgstr "" #: option.c:4803 option.c:4819 msgid "bad CNAME" msgstr "" #: option.c:4827 msgid "duplicate CNAME" msgstr "" #: option.c:4854 msgid "bad PTR record" msgstr "" #: option.c:4889 msgid "bad NAPTR record" msgstr "" #: option.c:4925 msgid "bad RR record" msgstr "" #: option.c:4958 msgid "bad CAA record" msgstr "" #: option.c:4987 msgid "bad TXT record" msgstr "" #: option.c:5030 msgid "bad SRV record" msgstr "" #: option.c:5037 msgid "bad SRV target" msgstr "" #: option.c:5056 msgid "invalid priority" msgstr "" #: option.c:5061 msgid "invalid weight" msgstr "" #: option.c:5084 msgid "Bad host-record" msgstr "" #: option.c:5123 msgid "Bad name in host-record" msgstr "" #: option.c:5165 msgid "bad value for dnssec-check-unsigned" msgstr "" #: option.c:5201 msgid "bad trust anchor" msgstr "" #: option.c:5217 msgid "bad HEX in trust anchor" msgstr "" #: option.c:5228 msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)" msgstr "" #: option.c:5290 msgid "missing \"" msgstr "" #: option.c:5347 msgid "bad option" msgstr "" #: option.c:5349 msgid "extraneous parameter" msgstr "" #: option.c:5351 msgid "missing parameter" msgstr "" #: option.c:5353 msgid "illegal option" msgstr "" #: option.c:5363 #, c-format msgid " in output from %s" msgstr "" #: option.c:5365 #, c-format msgid " at line %d of %s" msgstr "" #: option.c:5380 option.c:5683 option.c:5694 #, c-format msgid "read %s" msgstr "" #: option.c:5446 #, c-format msgid "cannot execute %s: %s" msgstr "" #: option.c:5454 option.c:5615 tftp.c:790 #, c-format msgid "cannot read %s: %s" msgstr "" #: option.c:5473 #, c-format msgid "error executing %s: %s" msgstr "" #: option.c:5476 #, c-format msgid "%s returns non-zero error code" msgstr "" #: option.c:5775 msgid "junk found in command line" msgstr "" #: option.c:5815 #, c-format msgid "Dnsmasq version %s %s\n" msgstr "" #: option.c:5816 #, c-format msgid "" "Compile time options: %s\n" "\n" msgstr "" #: option.c:5817 #, c-format msgid "This software comes with ABSOLUTELY NO WARRANTY.\n" msgstr "" #: option.c:5818 #, c-format msgid "Dnsmasq is free software, and you are welcome to redistribute it\n" msgstr "" #: option.c:5819 #, c-format msgid "under the terms of the GNU General Public License, version 2 or 3.\n" msgstr "" #: option.c:5836 msgid "try --help" msgstr "" #: option.c:5838 msgid "try -w" msgstr "" #: option.c:5840 #, c-format msgid "bad command line options: %s" msgstr "" #: option.c:5909 #, c-format msgid "CNAME loop involving %s" msgstr "" #: option.c:5950 #, c-format msgid "cannot get host-name: %s" msgstr "" #: option.c:5978 msgid "only one resolv.conf file allowed in no-poll mode." msgstr "" #: option.c:5988 msgid "must have exactly one resolv.conf to read domain from." msgstr "" #: option.c:5991 network.c:1727 dhcp.c:892 #, c-format msgid "failed to read %s: %s" msgstr "" #: option.c:6008 #, c-format msgid "no search directive found in %s" msgstr "" #: option.c:6029 msgid "there must be a default domain when --dhcp-fqdn is set" msgstr "" #: option.c:6038 msgid "syntax check OK" msgstr "" #: forward.c:107 #, c-format msgid "failed to send packet: %s" msgstr "" #: forward.c:715 msgid "discarding DNS reply: subnet option mismatch" msgstr "" #: forward.c:780 #, c-format msgid "nameserver %s refused to do a recursive query" msgstr "" #: forward.c:826 #, c-format msgid "possible DNS-rebind attack detected: %s" msgstr "" #: forward.c:1239 #, c-format msgid "reducing DNS packet size for nameserver %s to %d" msgstr "" #: forward.c:1565 #, c-format msgid "ignoring query from non-local network %s (logged only once)" msgstr "" #: forward.c:2139 #, c-format msgid "ignoring query from non-local network %s" msgstr "" #: forward.c:2501 #, c-format msgid "failed to bind server socket to %s: %s" msgstr "" #: forward.c:2867 #, c-format msgid "Maximum number of concurrent DNS queries reached (max: %d)" msgstr "" #: forward.c:2869 #, c-format msgid "Maximum number of concurrent DNS queries to %s reached (max: %d)" msgstr "" #: network.c:700 #, c-format msgid "stopped listening on %s(#%d): %s port %d" msgstr "" #: network.c:911 #, c-format msgid "failed to create listening socket for %s: %s" msgstr "" #: network.c:1192 #, c-format msgid "listening on %s(#%d): %s port %d" msgstr "" #: network.c:1219 #, c-format msgid "listening on %s port %d" msgstr "" #: network.c:1252 #, c-format msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s" msgstr "" #: network.c:1259 msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)" msgstr "" #: network.c:1268 #, c-format msgid "warning: using interface %s instead" msgstr "" #: network.c:1277 #, c-format msgid "warning: no addresses found for interface %s" msgstr "" #: network.c:1335 #, c-format msgid "interface %s failed to join DHCPv6 multicast group: %s" msgstr "" #: network.c:1340 msgid "try increasing /proc/sys/net/core/optmem_max" msgstr "" #: network.c:1545 #, c-format msgid "failed to bind server socket for %s: %s" msgstr "" #: network.c:1622 #, c-format msgid "ignoring nameserver %s - local interface" msgstr "" #: network.c:1633 #, c-format msgid "ignoring nameserver %s - cannot make/bind socket: %s" msgstr "" #: network.c:1643 msgid "more servers are defined but not logged" msgstr "" #: network.c:1654 msgid "(no DNSSEC)" msgstr "" #: network.c:1657 msgid "unqualified" msgstr "" #: network.c:1657 msgid "names" msgstr "" #: network.c:1659 msgid "default" msgstr "" #: network.c:1661 msgid "domain" msgstr "" #: network.c:1663 #, c-format msgid "using nameserver %s#%d for %s %s%s %s" msgstr "" #: network.c:1667 #, c-format msgid "NOT using nameserver %s#%d - query loop detected" msgstr "" #: network.c:1670 #, c-format msgid "using nameserver %s#%d(via %s)" msgstr "" #: network.c:1672 #, c-format msgid "using nameserver %s#%d" msgstr "" #: network.c:1687 #, c-format msgid "using only locally-known addresses for %s" msgstr "" #: network.c:1690 #, c-format msgid "using standard nameservers for %s" msgstr "" #: network.c:1694 #, c-format msgid "using %d more local addresses" msgstr "" #: network.c:1696 #, c-format msgid "using %d more nameservers" msgstr "" #: dnsmasq.c:192 msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform" msgstr "" #: dnsmasq.c:207 msgid "no root trust anchor provided for DNSSEC" msgstr "" #: dnsmasq.c:210 msgid "cannot reduce cache size from default when DNSSEC enabled" msgstr "" #: dnsmasq.c:212 msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h" msgstr "" #: dnsmasq.c:218 msgid "TFTP server not available: set HAVE_TFTP in src/config.h" msgstr "" #: dnsmasq.c:225 msgid "cannot use --conntrack AND --query-port" msgstr "" #: dnsmasq.c:231 msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h" msgstr "" #: dnsmasq.c:236 msgid "asynchronous logging is not available under Solaris" msgstr "" #: dnsmasq.c:241 msgid "asynchronous logging is not available under Android" msgstr "" #: dnsmasq.c:246 msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h" msgstr "" #: dnsmasq.c:251 msgid "loop detection not available: set HAVE_LOOP in src/config.h" msgstr "" #: dnsmasq.c:256 msgid "Ubus not available: set HAVE_UBUS in src/config.h" msgstr "" #: dnsmasq.c:267 msgid "max_port cannot be smaller than min_port" msgstr "" #: dnsmasq.c:271 msgid "port_limit must not be larger than available port range" msgstr "" #: dnsmasq.c:278 msgid "--auth-server required when an auth zone is defined." msgstr "" #: dnsmasq.c:283 msgid "zone serial must be configured in --auth-soa" msgstr "" #: dnsmasq.c:303 msgid "dhcp-range constructor not available on this platform" msgstr "" #: dnsmasq.c:377 msgid "cannot set --bind-interfaces and --bind-dynamic" msgstr "" #: dnsmasq.c:380 #, c-format msgid "failed to find list of interfaces: %s" msgstr "" #: dnsmasq.c:389 #, c-format msgid "unknown interface %s" msgstr "" #: dnsmasq.c:396 #, c-format msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s" msgstr "" #: dnsmasq.c:440 msgid "Packet dumps not available: set HAVE_DUMP in src/config.h" msgstr "" #: dnsmasq.c:448 dnsmasq.c:1232 #, c-format msgid "DBus error: %s" msgstr "" #: dnsmasq.c:451 msgid "DBus not available: set HAVE_DBUS in src/config.h" msgstr "" #: dnsmasq.c:459 dnsmasq.c:1253 #, c-format msgid "UBus error: %s" msgstr "" #: dnsmasq.c:462 msgid "UBus not available: set HAVE_UBUS in src/config.h" msgstr "" #: dnsmasq.c:492 #, c-format msgid "unknown user or group: %s" msgstr "" #: dnsmasq.c:568 #, c-format msgid "process is missing required capability %s" msgstr "" #: dnsmasq.c:600 #, c-format msgid "cannot chdir to filesystem root: %s" msgstr "" #: dnsmasq.c:852 #, c-format msgid "started, version %s DNS disabled" msgstr "" #: dnsmasq.c:857 #, c-format msgid "started, version %s cachesize %d" msgstr "" #: dnsmasq.c:859 msgid "cache size greater than 10000 may cause performance issues, and is unlikely to be useful." msgstr "" #: dnsmasq.c:862 #, c-format msgid "started, version %s cache disabled" msgstr "" #: dnsmasq.c:865 msgid "DNS service limited to local subnets" msgstr "" #: dnsmasq.c:868 #, c-format msgid "compile time options: %s" msgstr "" #: dnsmasq.c:877 msgid "DBus support enabled: connected to system bus" msgstr "" #: dnsmasq.c:879 msgid "DBus support enabled: bus connection pending" msgstr "" #: dnsmasq.c:887 msgid "UBus support enabled: connected to system bus" msgstr "" #: dnsmasq.c:889 msgid "UBus support enabled: bus connection pending" msgstr "" #: dnsmasq.c:909 msgid "DNSSEC validation enabled but all unsigned answers are trusted" msgstr "" #: dnsmasq.c:911 msgid "DNSSEC validation enabled" msgstr "" #: dnsmasq.c:915 msgid "DNSSEC signature timestamps not checked until receipt of SIGINT" msgstr "" #: dnsmasq.c:918 msgid "DNSSEC signature timestamps not checked until system time valid" msgstr "" #: dnsmasq.c:921 #, c-format msgid "configured with trust anchor for %s keytag %u" msgstr "" #: dnsmasq.c:927 #, c-format msgid "warning: failed to change owner of %s: %s" msgstr "" #: dnsmasq.c:932 msgid "setting --bind-interfaces option because of OS limitations" msgstr "" #: dnsmasq.c:945 #, c-format msgid "warning: interface %s does not currently exist" msgstr "" #: dnsmasq.c:950 msgid "warning: ignoring resolv-file flag because no-resolv is set" msgstr "" #: dnsmasq.c:953 msgid "warning: no upstream servers configured" msgstr "" #: dnsmasq.c:957 #, c-format msgid "asynchronous logging enabled, queue limit is %d messages" msgstr "" #: dnsmasq.c:978 msgid "IPv6 router advertisement enabled" msgstr "" #: dnsmasq.c:983 #, c-format msgid "DHCP, sockets bound exclusively to interface %s" msgstr "" #: dnsmasq.c:1000 msgid "root is " msgstr "" #: dnsmasq.c:1000 msgid "enabled" msgstr "" #: dnsmasq.c:1002 msgid "secure mode" msgstr "" #: dnsmasq.c:1003 msgid "single port mode" msgstr "" #: dnsmasq.c:1006 #, c-format msgid "warning: %s inaccessible" msgstr "" #: dnsmasq.c:1010 #, c-format msgid "warning: TFTP directory %s inaccessible" msgstr "" #: dnsmasq.c:1036 #, c-format msgid "restricting maximum simultaneous TFTP transfers to %d" msgstr "" #: dnsmasq.c:1095 #, c-format msgid "error binding DHCP socket to device %s" msgstr "" #: dnsmasq.c:1229 msgid "connected to system DBus" msgstr "" #: dnsmasq.c:1250 msgid "connected to system UBus" msgstr "" #: dnsmasq.c:1416 #, c-format msgid "cannot fork into background: %s" msgstr "" #: dnsmasq.c:1420 #, c-format msgid "failed to create helper: %s" msgstr "" #: dnsmasq.c:1424 #, c-format msgid "setting capabilities failed: %s" msgstr "" #: dnsmasq.c:1428 #, c-format msgid "failed to change user-id to %s: %s" msgstr "" #: dnsmasq.c:1432 #, c-format msgid "failed to change group-id to %s: %s" msgstr "" #: dnsmasq.c:1436 #, c-format msgid "failed to open pidfile %s: %s" msgstr "" #: dnsmasq.c:1440 #, c-format msgid "cannot open log %s: %s" msgstr "" #: dnsmasq.c:1444 #, c-format msgid "failed to load Lua script: %s" msgstr "" #: dnsmasq.c:1448 #, c-format msgid "TFTP directory %s inaccessible: %s" msgstr "" #: dnsmasq.c:1452 #, c-format msgid "cannot create timestamp file %s: %s" msgstr "" #: dnsmasq.c:1536 #, c-format msgid "script process killed by signal %d" msgstr "" #: dnsmasq.c:1540 #, c-format msgid "script process exited with status %d" msgstr "" #: dnsmasq.c:1544 #, c-format msgid "failed to execute %s: %s" msgstr "" #: dnsmasq.c:1584 msgid "now checking DNSSEC signature timestamps" msgstr "" #: dnsmasq.c:1619 dnssec.c:160 dnssec.c:204 #, c-format msgid "failed to update mtime on %s: %s" msgstr "" #: dnsmasq.c:1631 msgid "exiting on receipt of SIGTERM" msgstr "" #: dnsmasq.c:1659 #, c-format msgid "failed to access %s: %s" msgstr "" #: dnsmasq.c:1690 #, c-format msgid "reading %s" msgstr "" #: dnsmasq.c:1706 #, c-format msgid "no servers found in %s, will retry" msgstr "" #: dhcp.c:51 #, c-format msgid "cannot create DHCP socket: %s" msgstr "" #: dhcp.c:66 #, c-format msgid "failed to set options on DHCP socket: %s" msgstr "" #: dhcp.c:87 #, c-format msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s" msgstr "" #: dhcp.c:99 #, c-format msgid "failed to bind DHCP server socket: %s" msgstr "" #: dhcp.c:125 #, c-format msgid "cannot create ICMP raw socket: %s." msgstr "" #: dhcp.c:254 dhcp6.c:186 #, c-format msgid "unknown interface %s in bridge-interface" msgstr "" #: dhcp.c:295 #, c-format msgid "DHCP packet received on %s which has no address" msgstr "" #: dhcp.c:429 #, c-format msgid "ARP-cache injection failed: %s" msgstr "" #: dhcp.c:490 #, c-format msgid "Error sending DHCP packet to %s: %s" msgstr "" #: dhcp.c:547 #, c-format msgid "DHCP range %s -- %s is not consistent with netmask %s" msgstr "" #: dhcp.c:930 #, c-format msgid "bad line at %s line %d" msgstr "" #: dhcp.c:973 #, c-format msgid "ignoring %s line %d, duplicate name or IP address" msgstr "" #: dhcp.c:1034 #, c-format msgid "read %s - %d addresses" msgstr "" #: dhcp.c:1136 #, c-format msgid "Cannot broadcast DHCP relay via interface %s" msgstr "" #: dhcp.c:1160 #, c-format msgid "broadcast via %s" msgstr "" #: dhcp.c:1163 rfc3315.c:2219 #, c-format msgid "DHCP relay at %s -> %s" msgstr "" #: lease.c:64 #, c-format msgid "ignoring invalid line in lease database: %s %s %s %s ..." msgstr "" #: lease.c:101 #, c-format msgid "ignoring invalid line in lease database, bad address: %s" msgstr "" #: lease.c:108 msgid "too many stored leases" msgstr "" #: lease.c:176 #, c-format msgid "cannot open or create lease file %s: %s" msgstr "" #: lease.c:185 msgid "failed to parse lease database cleanly" msgstr "" #: lease.c:188 #, c-format msgid "failed to read lease file %s: %s" msgstr "" #: lease.c:204 #, c-format msgid "cannot run lease-init script %s: %s" msgstr "" #: lease.c:210 #, c-format msgid "lease-init script returned exit code %s" msgstr "" #: lease.c:381 #, c-format msgid "failed to write %s: %s (retry in %u s)" msgstr "" #: lease.c:955 #, c-format msgid "Ignoring domain %s for DHCP host name %s" msgstr "" #: rfc2131.c:378 msgid "with subnet selector" msgstr "" #: rfc2131.c:383 msgid "via" msgstr "" #: rfc2131.c:389 #, c-format msgid "no address range available for DHCP request %s %s" msgstr "" #: rfc2131.c:403 #, c-format msgid "%u available DHCP subnet: %s/%s" msgstr "" #: rfc2131.c:409 rfc3315.c:320 #, c-format msgid "%u available DHCP range: %s -- %s" msgstr "" #: rfc2131.c:521 #, c-format msgid "%u vendor class: %s" msgstr "" #: rfc2131.c:523 #, c-format msgid "%u user class: %s" msgstr "" #: rfc2131.c:557 msgid "disabled" msgstr "" #: rfc2131.c:598 rfc2131.c:1087 rfc2131.c:1536 rfc3315.c:633 rfc3315.c:816 #: rfc3315.c:1122 msgid "ignored" msgstr "" #: rfc2131.c:613 rfc2131.c:1340 rfc3315.c:868 msgid "address in use" msgstr "" #: rfc2131.c:627 rfc2131.c:1141 msgid "no address available" msgstr "" #: rfc2131.c:634 rfc2131.c:1302 msgid "wrong network" msgstr "" #: rfc2131.c:649 msgid "no address configured" msgstr "" #: rfc2131.c:655 rfc2131.c:1353 msgid "no leases left" msgstr "" #: rfc2131.c:756 rfc3315.c:500 #, c-format msgid "%u client provides name: %s" msgstr "" #: rfc2131.c:885 msgid "PXE BIS not supported" msgstr "" #: rfc2131.c:1054 rfc3315.c:1223 #, c-format msgid "disabling DHCP static address %s for %s" msgstr "" #: rfc2131.c:1075 msgid "unknown lease" msgstr "" #: rfc2131.c:1110 #, c-format msgid "not using configured address %s because it is leased to %s" msgstr "" #: rfc2131.c:1120 #, c-format msgid "not using configured address %s because it is in use by the server or relay" msgstr "" #: rfc2131.c:1123 #, c-format msgid "not using configured address %s because it was previously declined" msgstr "" #: rfc2131.c:1139 rfc2131.c:1346 msgid "no unique-id" msgstr "" #: rfc2131.c:1238 msgid "wrong server-ID" msgstr "" #: rfc2131.c:1257 msgid "wrong address" msgstr "" #: rfc2131.c:1275 rfc3315.c:976 msgid "lease not found" msgstr "" #: rfc2131.c:1310 msgid "address not available" msgstr "" #: rfc2131.c:1321 msgid "static lease available" msgstr "" #: rfc2131.c:1325 msgid "address reserved" msgstr "" #: rfc2131.c:1334 #, c-format msgid "abandoning lease to %s of %s" msgstr "" #: rfc2131.c:1870 #, c-format msgid "%u bootfile name: %s" msgstr "" #: rfc2131.c:1879 #, c-format msgid "%u server name: %s" msgstr "" #: rfc2131.c:1889 #, c-format msgid "%u next server: %s" msgstr "" #: rfc2131.c:1893 #, c-format msgid "%u broadcast response" msgstr "" #: rfc2131.c:1956 #, c-format msgid "cannot send DHCP/BOOTP option %d: no space left in packet" msgstr "" #: rfc2131.c:2267 msgid "PXE menu too large" msgstr "" #: rfc2131.c:2430 rfc3315.c:1517 #, c-format msgid "%u requested options: %s" msgstr "" #: rfc2131.c:2747 #, c-format msgid "cannot send RFC3925 option: too many options for enterprise number %d" msgstr "" #: rfc2131.c:2810 #, c-format msgid "%u reply delay: %d" msgstr "" #: netlink.c:86 #, c-format msgid "cannot create netlink socket: %s" msgstr "" #: netlink.c:379 #, c-format msgid "netlink returns error: %s" msgstr "" #: dbus.c:491 #, c-format msgid "Enabling --%s option from D-Bus" msgstr "" #: dbus.c:496 #, c-format msgid "Disabling --%s option from D-Bus" msgstr "" #: dbus.c:857 msgid "setting upstream servers from DBus" msgstr "" #: dbus.c:907 msgid "could not register a DBus message handler" msgstr "" #: bpf.c:261 #, c-format msgid "cannot create DHCP BPF socket: %s" msgstr "" #: bpf.c:289 #, c-format msgid "DHCP request for unsupported hardware type (%d) received on %s" msgstr "" #: bpf.c:374 #, c-format msgid "cannot create PF_ROUTE socket: %s" msgstr "" #: bpf.c:395 msgid "Unknown protocol version from route socket" msgstr "" #: helper.c:150 msgid "lease() function missing in Lua script" msgstr "" #: tftp.c:353 msgid "unable to get free port for TFTP" msgstr "" #: tftp.c:369 #, c-format msgid "unsupported request from %s" msgstr "" #: tftp.c:520 #, c-format msgid "file %s not found for %s" msgstr "" #: tftp.c:609 #, c-format msgid "ignoring packet from %s (TID mismatch)" msgstr "" #: tftp.c:662 #, c-format msgid "failed sending %s to %s" msgstr "" #: tftp.c:662 #, c-format msgid "sent %s to %s" msgstr "" #: tftp.c:712 #, c-format msgid "error %d %s received from %s" msgstr "" #: log.c:203 #, c-format msgid "overflow: %d log entries lost" msgstr "" #: log.c:281 #, c-format msgid "log failed: %s" msgstr "" #: log.c:490 msgid "FAILED to start up" msgstr "" #: conntrack.c:63 #, c-format msgid "Conntrack connection mark retrieval failed: %s" msgstr "" #: dhcp6.c:51 #, c-format msgid "cannot create DHCPv6 socket: %s" msgstr "" #: dhcp6.c:72 #, c-format msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s" msgstr "" #: dhcp6.c:84 #, c-format msgid "failed to bind DHCPv6 server socket: %s" msgstr "" #: rfc3315.c:173 #, c-format msgid "no address range available for DHCPv6 request from relay at %s" msgstr "" #: rfc3315.c:182 #, c-format msgid "no address range available for DHCPv6 request via %s" msgstr "" #: rfc3315.c:317 #, c-format msgid "%u available DHCPv6 subnet: %s/%d" msgstr "" #: rfc3315.c:400 #, c-format msgid "%u vendor class: %u" msgstr "" #: rfc3315.c:448 #, c-format msgid "%u client MAC address: %s" msgstr "" #: rfc3315.c:763 rfc3315.c:860 msgid "address unavailable" msgstr "" #: rfc3315.c:775 rfc3315.c:904 rfc3315.c:1273 msgid "success" msgstr "" #: rfc3315.c:790 rfc3315.c:799 rfc3315.c:912 rfc3315.c:914 rfc3315.c:1048 msgid "no addresses available" msgstr "" #: rfc3315.c:891 msgid "not on link" msgstr "" #: rfc3315.c:980 rfc3315.c:1181 rfc3315.c:1262 msgid "no binding found" msgstr "" #: rfc3315.c:1017 msgid "deprecated" msgstr "" #: rfc3315.c:1024 msgid "address invalid" msgstr "" #: rfc3315.c:1082 rfc3315.c:1084 msgid "confirm failed" msgstr "" #: rfc3315.c:1099 msgid "all addresses still on link" msgstr "" #: rfc3315.c:1190 msgid "release received" msgstr "" #: rfc3315.c:2200 #, c-format msgid "Cannot multicast DHCP relay via interface %s" msgstr "" #: rfc3315.c:2216 #, c-format msgid "multicast via %s" msgstr "" #: dhcp-common.c:187 #, c-format msgid "Ignoring duplicate dhcp-option %d" msgstr "" #: dhcp-common.c:264 #, c-format msgid "%u tags: %s" msgstr "" #: dhcp-common.c:484 #, c-format msgid "%s has more than one address in hostsfile, using %s for DHCP" msgstr "" #: dhcp-common.c:518 #, c-format msgid "duplicate IP address %s (%s) in dhcp-config directive" msgstr "" #: dhcp-common.c:738 #, c-format msgid "Known DHCP options:\n" msgstr "" #: dhcp-common.c:749 #, c-format msgid "Known DHCPv6 options:\n" msgstr "" #: dhcp-common.c:946 msgid ", prefix deprecated" msgstr "" #: dhcp-common.c:949 #, c-format msgid ", lease time " msgstr "" #: dhcp-common.c:991 #, c-format msgid "%s stateless on %s%.0s%.0s%s" msgstr "" #: dhcp-common.c:993 #, c-format msgid "%s, static leases only on %.0s%s%s%.0s" msgstr "" #: dhcp-common.c:995 #, c-format msgid "%s, proxy on subnet %.0s%s%.0s%.0s" msgstr "" #: dhcp-common.c:996 #, c-format msgid "%s, IP range %s -- %s%s%.0s" msgstr "" #: dhcp-common.c:1009 #, c-format msgid "DHCPv4-derived IPv6 names on %s%s" msgstr "" #: dhcp-common.c:1012 #, c-format msgid "router advertisement on %s%s" msgstr "" #: dhcp-common.c:1043 #, c-format msgid "DHCP relay from %s via %s" msgstr "" #: dhcp-common.c:1045 #, c-format msgid "DHCP relay from %s to %s via %s" msgstr "" #: dhcp-common.c:1048 #, c-format msgid "DHCP relay from %s to %s" msgstr "" #: radv.c:110 #, c-format msgid "cannot create ICMPv6 socket: %s" msgstr "" #: auth.c:462 #, c-format msgid "ignoring zone transfer request from %s" msgstr "" #: ipset.c:99 #, c-format msgid "failed to create IPset control socket: %s" msgstr "" #: ipset.c:211 #, c-format msgid "failed to update ipset %s: %s" msgstr "" #: pattern.c:29 #, c-format msgid "[pattern.c:%d] Assertion failure: %s" msgstr "" #: pattern.c:142 #, c-format msgid "Invalid DNS name: Invalid character %c." msgstr "" #: pattern.c:151 msgid "Invalid DNS name: Empty label." msgstr "" #: pattern.c:156 msgid "Invalid DNS name: Label starts with hyphen." msgstr "" #: pattern.c:170 msgid "Invalid DNS name: Label ends with hyphen." msgstr "" #: pattern.c:176 #, c-format msgid "Invalid DNS name: Label is too long (%zu)." msgstr "" #: pattern.c:184 #, c-format msgid "Invalid DNS name: Not enough labels (%zu)." msgstr "" #: pattern.c:189 msgid "Invalid DNS name: Final label is fully numeric." msgstr "" #: pattern.c:199 msgid "Invalid DNS name: \"local\" pseudo-TLD." msgstr "" #: pattern.c:204 #, c-format msgid "DNS name has invalid length (%zu)." msgstr "" #: pattern.c:258 #, c-format msgid "Invalid DNS name pattern: Invalid character %c." msgstr "" #: pattern.c:267 msgid "Invalid DNS name pattern: Empty label." msgstr "" #: pattern.c:272 msgid "Invalid DNS name pattern: Label starts with hyphen." msgstr "" #: pattern.c:285 msgid "Invalid DNS name pattern: Wildcard character used more than twice per label." msgstr "" #: pattern.c:295 msgid "Invalid DNS name pattern: Label ends with hyphen." msgstr "" #: pattern.c:301 #, c-format msgid "Invalid DNS name pattern: Label is too long (%zu)." msgstr "" #: pattern.c:309 #, c-format msgid "Invalid DNS name pattern: Not enough labels (%zu)." msgstr "" #: pattern.c:314 msgid "Invalid DNS name pattern: Wildcard within final two labels." msgstr "" #: pattern.c:319 msgid "Invalid DNS name pattern: Final label is fully numeric." msgstr "" #: pattern.c:329 msgid "Invalid DNS name pattern: \"local\" pseudo-TLD." msgstr "" #: pattern.c:334 #, c-format msgid "DNS name pattern has invalid length after removing wildcards (%zu)." msgstr "" #: dnssec.c:206 msgid "system time considered valid, now checking DNSSEC signature timestamps." msgstr "" #: dnssec.c:1018 #, c-format msgid "Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support" msgstr "" #: blockdata.c:55 #, c-format msgid "pool memory in use %zu, max %zu, allocated %zu" msgstr "" #: tables.c:61 #, c-format msgid "failed to access pf devices: %s" msgstr "" #: tables.c:74 #, c-format msgid "warning: no opened pf devices %s" msgstr "" #: tables.c:82 #, c-format msgid "error: cannot use table name %s" msgstr "" #: tables.c:90 #, c-format msgid "error: cannot strlcpy table name %s" msgstr "" #: tables.c:101 #, c-format msgid "IPset: error: %s" msgstr "" #: tables.c:108 msgid "info: table created" msgstr "" #: tables.c:133 #, c-format msgid "warning: DIOCR%sADDRS: %s" msgstr "" #: tables.c:137 #, c-format msgid "%d addresses %s" msgstr "" #: inotify.c:62 #, c-format msgid "cannot access path %s: %s" msgstr "" #: inotify.c:95 #, c-format msgid "failed to create inotify: %s" msgstr "" #: inotify.c:111 #, c-format msgid "too many symlinks following %s" msgstr "" #: inotify.c:127 #, c-format msgid "directory %s for resolv-file is missing, cannot poll" msgstr "" #: inotify.c:131 inotify.c:200 #, c-format msgid "failed to create inotify for %s: %s" msgstr "" #: inotify.c:178 inotify.c:185 #, c-format msgid "bad dynamic directory %s: %s" msgstr "" #: inotify.c:186 msgid "not a directory" msgstr "" #: inotify.c:299 #, c-format msgid "inotify: %s removed" msgstr "" #: inotify.c:301 #, c-format msgid "inotify: %s new or modified" msgstr "" #: inotify.c:309 #, c-format msgid "inotify: flushed %u names read from %s" msgstr "" #: dump.c:68 #, c-format msgid "cannot create %s: %s" msgstr "" #: dump.c:74 #, c-format msgid "bad header in %s" msgstr "" #: dump.c:287 msgid "failed to write packet dump" msgstr "" #: dump.c:289 #, c-format msgid "%u dumping packet %u mask 0x%04x" msgstr "" #: dump.c:291 #, c-format msgid "dumping packet %u mask 0x%04x" msgstr "" #: ubus.c:79 #, c-format msgid "UBus subscription callback: %s subscriber(s)" msgstr "" #: ubus.c:99 #, c-format msgid "Cannot reconnect to UBus: %s" msgstr "" #: ubus.c:135 msgid "Cannot set UBus listeners: no connection" msgstr "" #: ubus.c:155 msgid "Cannot poll UBus listeners: no connection" msgstr "" #: ubus.c:168 msgid "Disconnecting from UBus" msgstr "" #: ubus.c:179 ubus.c:326 #, c-format msgid "UBus command failed: %d (%s)" msgstr "" #: hash-questions.c:40 msgid "Failed to create SHA-256 hash object" msgstr "" #: nftset.c:35 msgid "failed to create nftset context" msgstr "" dnsmasq-2.91/po/pl.po0000664000175000017500000023141714765043257012713 0ustar srksrk# translation of pl.po to polski # Polish translations for dnsmasq package. # This file is put in the public domain. # # Tomasz Sochański , 2005. # Jan Psota , 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: 2017-07-17 18:30+0100\n" "Last-Translator: Jan Psota \n" "Language-Team: polski <>\n" "Language: pl\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.7\n" "X-Language: pl_PL\n" #: cache.c:652 msgid "Internal error in cache." msgstr "Wewnętrzny błąd w pamięci podręcznej." #: cache.c:1179 #, c-format msgid "failed to load names from %s: %s" msgstr "nie potrafię wczytać nazw z %s: %s" #: cache.c:1201 dhcp.c:943 #, c-format msgid "bad address at %s line %d" msgstr "błędny adres w pliku %s, w linii %d" #: cache.c:1254 dhcp.c:959 #, c-format msgid "bad name at %s line %d" msgstr "błędna nazwa w pliku %s, w linii %d" #: cache.c:1265 #, fuzzy, c-format msgid "read %s - %d names" msgstr "wczytałem %s - %d adresów" #: cache.c:1381 msgid "cleared cache" msgstr "wyczyszczono pamięć podręczną" #: cache.c:1445 #, c-format msgid "No IPv4 address found for %s" msgstr "Nie znalazłem adresu IPv4 komputera %s" #: cache.c:1491 #, 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" #: cache.c:1515 #, 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" #: cache.c:1760 #, c-format msgid "time %lu" msgstr "czas %lu" #: cache.c:1761 #, 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." #: cache.c:1763 #, c-format msgid "queries forwarded %u, queries answered locally %u" msgstr "%u zapytań przesłanych dalej, %u odpowiedzi udzielonych samodzielnie" #: cache.c:1766 #, c-format msgid "queries answered from stale cache %u" msgstr "" #: cache.c:1768 #, c-format msgid "queries for authoritative zones %u" msgstr "zapytań do stref autorytatywnych %u" #: cache.c:1796 #, fuzzy, c-format msgid "server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums" msgstr "serwer %s#%d: %u zapytań wysłanych, %u ponowionych lub nieudanych" #: util.c:51 #, c-format msgid "failed to seed the random number generator: %s" msgstr "brak możliwości użycia generatora liczb losowych: %s" #: util.c:246 msgid "failed to allocate memory" msgstr "nie udało się przydzielić pamięci" #: util.c:305 option.c:696 msgid "could not get memory" msgstr "nie można dostać pamięci" #: util.c:326 #, c-format msgid "cannot create pipe: %s" msgstr "błąd podczas próby utworzenia potoku: %s" #: util.c:334 #, c-format msgid "failed to allocate %d bytes" msgstr "niemożliwość przydzielenia %d bajtów pamięci" #: util.c:344 #, fuzzy, c-format msgid "failed to reallocate %d bytes" msgstr "niemożliwość przydzielenia %d bajtów pamięci" #: util.c:465 #, fuzzy, c-format msgid "cannot read monotonic clock: %s" msgstr "nie potrafię utworzyć połączenia netlink %s" #: util.c:579 #, c-format msgid "infinite" msgstr "nieskończona" #: util.c:867 #, c-format msgid "failed to find kernel version: %s" msgstr "niezgodna wersja jądra: %s" #: option.c:393 msgid "Specify local address(es) to listen on." msgstr "Wskazanie adresów, na których należy nasłuchiwać." #: option.c:394 msgid "Return ipaddr for all hosts in specified domains." msgstr "Zwracanie adresu IP dla wszystkich hostów we wskazanych domenach." #: option.c:395 msgid "Fake reverse lookups for RFC1918 private address ranges." msgstr "Wyłączenie przekazywania zapytań odwrotnych dla prywatnych zakresów IP." #: option.c:396 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)." msgstr "Traktowanie adresu IP jako NXDOMAIN (unieważnia ,,Verisign wildcard'')." #: option.c:397 #, 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)." #: option.c:398 #, c-format msgid "Specify configuration file (defaults to %s)." msgstr "Wskazanie pliku konfiguracyjnego (domyślnie: %s)." #: option.c:399 msgid "Do NOT fork into the background: run in debug mode." msgstr "NIE twórz procesu potomnego w tle: działanie w trybie debugowania." #: option.c:400 msgid "Do NOT forward queries with no domain part." msgstr "Wyłączenie przekazywania zapytań bez podanej części domenowej." #: option.c:401 msgid "Return self-pointing MX records for local hosts." msgstr "Zwracanie samowskazującego rekordu MX dla lokalnych hostów." #: option.c:402 msgid "Expand simple names in /etc/hosts with domain-suffix." msgstr "Rozwijanie prostych nazw z /etc/hosts przyrostkiem domenowym." #: option.c:403 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." #: option.c:404 msgid "Don't include IPv4 addresses in DNS answers." msgstr "" #: option.c:405 msgid "Don't include IPv6 addresses in DNS answers." msgstr "" #: option.c:406 msgid "Enable DHCP in the range given with lease duration." msgstr "Włączenie serwera DHCP dla wskazanego zakresu adresów." #: option.c:407 #, c-format msgid "Change to this group after startup (defaults to %s)." msgstr "Po uruchomieniu zmiana grupy procesu na podaną (domyślnie: %s)." #: option.c:408 msgid "Set address or hostname for a specified machine." msgstr "Ustawienie adresu lub nazwy dla wskazanego komputera." #: option.c:409 msgid "Read DHCP host specs from file." msgstr "Wskazanie pliku z wartościami 'dhcp-host='." #: option.c:410 msgid "Read DHCP option specs from file." msgstr "Wskazanie pliku z wartościami 'dhcp-option='." #: option.c:411 msgid "Read DHCP host specs from a directory." msgstr "Odczyt specyfikacji hostów dla DHCP z katalogu." #: option.c:412 msgid "Read DHCP options from a directory." msgstr "Odczyt opcji DHCP z katalogu." #: option.c:413 msgid "Evaluate conditional tag expression." msgstr "Warunkowe ustawianie znaczników." #: option.c:414 #, c-format msgid "Do NOT load %s file." msgstr "NIE wczytywanie pliku %s." #: option.c:415 #, c-format msgid "Specify a hosts file to be read in addition to %s." msgstr "Wskazanie dodatkowego pliku 'hosts' oprócz %s." #: option.c:416 msgid "Read hosts files from a directory." msgstr "Odczyt pliku hostów z katalogu." #: option.c:417 msgid "Specify interface(s) to listen on." msgstr "Interfejsy, na których nasłuchiwać." #: option.c:418 msgid "Specify interface(s) NOT to listen on." msgstr "Interfejsy, na których NIE nasłuchiwać." #: option.c:419 msgid "Map DHCP user class to tag." msgstr "Przyporządkowanie znacznika w zależności od klasy użytkownika DHCP." #: option.c:420 msgid "Map RFC3046 circuit-id to tag." msgstr "Przyporządkowanie znacznika w zależności od numeru obwodu (w rozumieniu RFC3046)." #: option.c:421 msgid "Map RFC3046 remote-id to tag." msgstr "Przyporządkowanie znacznika w zależności od numeru agenta (w rozumieniu RFC3046)." #: option.c:422 msgid "Map RFC3993 subscriber-id to tag." msgstr "Przyporządkowanie znacznika w zależności od numeru subskrybenta (w rozumieniu RFC3993)." #: option.c:423 msgid "Specify vendor class to match for PXE requests." msgstr "" #: option.c:424 msgid "Don't do DHCP for hosts with tag set." msgstr "Wyłączenie DHCP dla hostów z określonym znacznikiem." #: option.c:425 msgid "Force broadcast replies for hosts with tag set." msgstr "Wymuszenie odpowiedzi w trybie rozgłoszeniowym dla hostów z określonym znacznikiem." #: option.c:426 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." #: option.c:427 msgid "Assume we are the only DHCP server on the local network." msgstr "Zakładanie, że jesteśmy jedynym serwerem DHCP w sieci lokalnej." #: option.c:428 #, c-format msgid "Specify where to store DHCP leases (defaults to %s)." msgstr "Ścieżka przechowywania pliku dzierżaw DHCP (domyślnie: %s)." #: option.c:429 msgid "Return MX records for local hosts." msgstr "Włączenie zwracania rekordu MX dla hostów lokalnych." #: option.c:430 msgid "Specify an MX record." msgstr "Specyfikacja rekordu MX." #: option.c:431 msgid "Specify BOOTP options to DHCP server." msgstr "Określenie opcji BOOTP serwera DHCP." #: option.c:432 #, 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." #: option.c:433 msgid "Do NOT cache failed search results." msgstr "Wyłączenie przechowywania w pamięci podręcznej wyników nieudanych wyszukiwań." #: option.c:434 msgid "Use expired cache data for faster reply." msgstr "" #: option.c:435 #, 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." #: option.c:436 msgid "Specify options to be sent to DHCP clients." msgstr "Specyfikacja opcji wysyłanej do klientów DHCP." #: option.c:437 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." #: option.c:438 msgid "Specify port to listen for DNS requests on (defaults to 53)." msgstr "Wskazanie portu do nasłuchiwania zapytań DNS (domyślnie: 53)." #: option.c:439 #, 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)." #: option.c:440 msgid "Log DNS queries." msgstr "Włączenie spisywania zapytań DNS do logu." #: option.c:441 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." #: option.c:442 msgid "Set maximum number of random originating ports for a query." msgstr "" #: option.c:443 msgid "Do NOT read resolv.conf." msgstr "Wyłączenie czytania pliku resolv.conf." #: option.c:444 #, c-format msgid "Specify path to resolv.conf (defaults to %s)." msgstr "Wskazanie położenia pliku resolv.conf (domyślnie: %s)." #: option.c:445 msgid "Specify path to file with server= options" msgstr "Wskazanie położenia pliku z opcjami server=" #: option.c:446 msgid "Specify address(es) of upstream servers with optional domains." msgstr "Wskazywanie adresów serwerów nazw, opcjonalnie z przypisaniem do domeny." #: option.c:447 msgid "Specify address of upstream servers for reverse address queries" msgstr "Wskazanie serwerów nazw do odwrotnej translacji adresów." #: option.c:448 msgid "Never forward queries to specified domains." msgstr "Wyłączenie przekazywania zapytań do wskazanych domen." #: option.c:449 msgid "Specify the domain to be assigned in DHCP leases." msgstr "Wskazanie domeny dla serwera DHCP." #: option.c:450 msgid "Specify default target in an MX record." msgstr "Określenie domyślnego celu w rekordzie MX." #: option.c:451 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)." #: option.c:452 msgid "Specify time-to-live in seconds for negative caching." msgstr "Określenie (w sekundach) czasu ważności negatywnych odpowiedzi." #: option.c:453 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]." #: option.c:454 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:455 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:456 msgid "Retry DNS queries after this many milliseconds." msgstr "" #: option.c:457 #, c-format msgid "Change to this user after startup. (defaults to %s)." msgstr "Zmiana użytkownika procesu na wskazanego (po uruchomieniu, domyślnie: %s)." #: option.c:458 msgid "Map DHCP vendor class to tag." msgstr "Przyporządkowanie znacznika w zależności od typu klienta DHCP." #: option.c:459 msgid "Display dnsmasq version and copyright information." msgstr "Wydrukowanie informacji o programie i ochronie praw autorskich." #: option.c:460 msgid "Translate IPv4 addresses from upstream servers." msgstr "Tłumaczenie adresów IPv4 z serwerów nadrzędnych." #: option.c:461 msgid "Specify a SRV record." msgstr "Określenie rekordu SRV." #: option.c:462 msgid "Display this message. Use --help dhcp or --help dhcp6 for known DHCP options." msgstr "Wyświetla ten komunikat. Chcąc przejrzeć listę dostępnych opcji DHCP użyj '--help dhcp' lub '--help dhcp6' ." #: option.c:463 #, c-format msgid "Specify path of PID file (defaults to %s)." msgstr "Określenie ścieżki do pliku PID (domyślnie: %s)." #: option.c:464 #, c-format msgid "Specify maximum number of DHCP leases (defaults to %s)." msgstr "Maksymalna liczba dzierżaw DHCP (domyślnie: %s)." #: option.c:465 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)." #: option.c:466 msgid "Specify TXT DNS record." msgstr "Specyfikacja rekordu DNS TXT." #: option.c:467 msgid "Specify PTR DNS record." msgstr "Specyfikacja rekordu DNS PTR." #: option.c:468 msgid "Give DNS name to IPv4 address of interface." msgstr "Zwraca nazwę domenową powiązaną z adresem interfejsu sieciowego." #: option.c:469 msgid "Bind only to interfaces in use." msgstr "Nasłuchiwanie tylko na wykorzystywanych interfejsach (umożliwia uruchomienie osobnych serwerów dla różnych kart)." #: option.c:470 #, c-format msgid "Read DHCP static host information from %s." msgstr "Wczytanie przyporządkowań adresów z %s." #: option.c:471 msgid "Enable the DBus interface for setting upstream servers, etc." msgstr "Włączenie używania interfejsu DBus do informowania o zmianach konfiguracji." #: option.c:472 msgid "Enable the UBus interface." msgstr "" #: option.c:473 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." #: option.c:474 msgid "Enable dynamic address allocation for bootp." msgstr "Włączenie dynamicznego przydzielania adresów dla klientów BOOTP." #: option.c:475 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ń: *)." #: option.c:476 msgid "Treat DHCP requests on aliases as arriving from interface." msgstr "Traktowanie żądań DHCP odebranych na interfejsach alias, ..., jako odebranych na iface." #: option.c:477 msgid "Specify extra networks sharing a broadcast domain for DHCP" msgstr "" #: option.c:478 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." #: option.c:479 msgid "Shell script to run on DHCP lease creation and destruction." msgstr "Skrypt powłoki uruchamiany po przyznaniu lub zwolnieniu adresu." #: option.c:480 msgid "Lua script to run on DHCP lease creation and destruction." msgstr "Skrypt Lua uruchamiany po przyznaniu lub zwolnieniu adresu." #: option.c:481 msgid "Run lease-change scripts as this user." msgstr "Wskazanie użytkownika z którego uprawnieniami będą uruchamiane skrypty." #: option.c:482 msgid "Call dhcp-script with changes to local ARP table." msgstr "Wywoływanie dhcp-script w reakcji na zmiany w tablicy ARP." #: option.c:483 msgid "Read configuration from all the files in this directory." msgstr "Wczytanie wszystkich plików ze wskazanego katalogu jako konfiguracyjnych." #: option.c:484 msgid "Execute file and read configuration from stdin." msgstr "" #: option.c:485 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)" #: option.c:486 msgid "Do not use leasefile." msgstr "Nieużywanie bazy dzierżaw." #: option.c:487 #, c-format msgid "Maximum number of concurrent DNS queries. (defaults to %s)" msgstr "Maksymalna liczba jednocześnie obsługiwanych zapytań DNS (domyślnie: %s)" #: option.c:488 #, c-format msgid "Clear DNS cache when reloading %s." msgstr "Czyszczenie pamięci podręcznej serwera nazw w przypadku ponownego odczytu %s." #: option.c:489 msgid "Ignore hostnames provided by DHCP clients." msgstr "Nie zwracanie uwagi na nazwę podawaną przez klienta w przypadku dopasowania wszystkich wymienionych znaczników." #: option.c:490 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." #: option.c:491 msgid "Enable integrated read-only TFTP server." msgstr "Włączenie wbudowanego serwera TFTP (tylko do wysyłania)." #: option.c:492 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." #: option.c:493 #, fuzzy msgid "Add client IP or hardware 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." #: option.c:494 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." #: option.c:495 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:496 #, fuzzy, c-format msgid "Maximum number of concurrent TFTP transfers (defaults to %s)." msgstr "Maksymalna liczba jednocześnie obsługiwanych połączeń TFTP (domyślnie %s)." #: option.c:497 msgid "Maximum MTU to use for TFTP transfers." msgstr "Ograniczenie MTU w komunikacji TFTP." #: option.c:498 msgid "Disable the TFTP blocksize extension." msgstr "Wyłączenie możliwości negocjowania wielkości bloku dla przesyłów przez TFTP." #: option.c:499 msgid "Convert TFTP filenames to lowercase" msgstr "Konwertowanie nazw plików żądanych przez TFTP do małych liter" #: option.c:500 msgid "Ephemeral port range for use by TFTP transfers." msgstr "Wskazanie zakresu portów do użytku TFTP." #: option.c:501 msgid "Use only one port for TFTP server." msgstr "" #: option.c:502 msgid "Extra logging for DHCP." msgstr "Włączenie spisywania w logu operacji DHCP." #: option.c:503 msgid "Enable async. logging; optionally set queue length." msgstr "Włączenie asynchronicznego zapisywania do logu z ewentualnym wskazaniem długości kolejki." #: option.c:504 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." #: option.c:505 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." #: option.c:506 msgid "Inhibit DNS-rebind protection on this domain." msgstr "Dezaktywacja zabezpieczenia przed atakami DNS-rebind dla wskazanych domen." #: option.c:507 msgid "Always perform DNS queries to all servers." msgstr "Jednoczesne odpytywanie wszystkich serwerów nadrzędnych; klientowi przekazywana jest pierwsza odpowiedź." #: option.c:508 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." #: option.c:509 #, fuzzy msgid "Set tag if client provides given name." msgstr "Ustawienie znacznika jeżeli w żądaniu DHCP pojawi się wskazana opcja, ewentualnie o konkretnej wartości." #: option.c:510 msgid "Use alternative ports for DHCP." msgstr "Użycie alternatywnych portów dla usługi DHCP." #: option.c:511 msgid "Specify NAPTR DNS record." msgstr "Specyfikacja rekordu DNS NAPTR." #: option.c:512 msgid "Specify lowest port available for DNS query transmission." msgstr "Ustawienie dolnej granicy numerów portów do przesyłania zapytań DNS." #: option.c:513 msgid "Specify highest port available for DNS query transmission." msgstr "Ograniczenie najwyższego numeru portu dla transmisji zapytań DNS." #: option.c:514 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." #: option.c:515 msgid "Generate hostnames based on MAC address for nameless clients." msgstr "Generowanie nazw na podstawie MAC-adresów dla klientów bez nazwy." #: option.c:516 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)." #: option.c:517 msgid "Relay DHCP requests to a remote server" msgstr "Przekazywanie żądań DHCP do zdalnego serwera" #: option.c:518 msgid "Specify alias name for LOCAL DNS name." msgstr "Wskazanie synonimu nazwy komputera lokalnego - znanego z /etc/hosts albo z DHCP." #: option.c:519 msgid "Prompt to send to PXE clients." msgstr "Zgłoszenie wysyłane klientom PXE." #: option.c:520 msgid "Boot service for PXE menu." msgstr "Składnik menu PXE (--> man)." #: option.c:521 msgid "Check configuration syntax." msgstr "Sprawdzenie składni." #: option.c:522 msgid "Add requestor's MAC address to forwarded DNS queries." msgstr "Przekazywanie MAC-adresu komputera pytającego w ruchu wychodzącym DNS." #: option.c:523 msgid "Strip MAC information from queries." msgstr "" #: option.c:524 msgid "Add specified IP subnet to forwarded DNS queries." msgstr "Zamieszczanie wskazanego adresu podsieci w przekazywanych zapytaniach DNS." #: option.c:525 msgid "Strip ECS information from queries." msgstr "" #: option.c:526 msgid "Add client identification to forwarded DNS queries." msgstr "Zamieszczanie identyfikacji pytającego w przekazywanych zapytaniach DNS." #: option.c:527 msgid "Proxy DNSSEC validation results from upstream nameservers." msgstr "Przekazywanie wyników weryfikacji DNSSEC z serwerów nadrzędnych." #: option.c:528 msgid "Attempt to allocate sequential IP addresses to DHCP clients." msgstr "Zmiana sposobu przydzielania adresów IP na sekwencyjny." #: option.c:529 #, fuzzy msgid "Ignore client identifier option sent by DHCP clients." msgstr "Nie zwracanie uwagi na nazwę podawaną przez klienta w przypadku dopasowania wszystkich wymienionych znaczników." #: option.c:530 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:531 msgid "Allow DHCP clients to do their own DDNS updates." msgstr "Zezwolenie klientom DHCP na uaktualnianie DDNS-ów." #: option.c:532 msgid "Send router-advertisements for interfaces doing DHCPv6" msgstr "Załączenie anonsowania (RA) na interfejsach serwujących DHCPv6" #: option.c:533 msgid "Specify DUID_EN-type DHCPv6 server DUID" msgstr "Określenie DHCPv6 DUID" #: option.c:534 msgid "Specify host (A/AAAA and PTR) records" msgstr "Określenie rekordów A/AAAA i PTR" #: option.c:535 msgid "Specify host record in interface subnet" msgstr "" #: option.c:536 msgid "Specify certification authority authorization record" msgstr "" #: option.c:537 msgid "Specify arbitrary DNS resource record" msgstr "Określenie rekordu TXT" #: option.c:538 msgid "Bind to interfaces in use - check for new interfaces" msgstr "Dynamiczne podpinanie do interfejsów sieciowych" #: option.c:539 msgid "Export local names to global DNS" msgstr "Eksportowanie lokalnych nazw hostów do globalnego DNS-a" #: option.c:540 msgid "Domain to export to global DNS" msgstr "Domena pod którą będą eksportowane lokalne nazwy" #: option.c:541 msgid "Set TTL for authoritative replies" msgstr "Określenie TTL dla odpowiedzi autorytatywnych" #: option.c:542 #, fuzzy msgid "Set authoritative zone information" msgstr "Określenie danych strefy autorytatywnej (SOA)" #: option.c:543 msgid "Secondary authoritative nameservers for forward domains" msgstr "Pomocnicze serwery autorytatywne dla forwardowanych domen" #: option.c:544 msgid "Peers which are allowed to do zone transfer" msgstr "Wskazanie serwerów uprawnionych do transferu stref" #: option.c:545 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:546 #, fuzzy msgid "Specify nftables sets 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:547 msgid "Enable filtering of DNS queries with connection-track marks." msgstr "" #: option.c:548 msgid "Set allowed DNS patterns for a connection-track mark." msgstr "" #: option.c:549 msgid "Specify a domain and address range for synthesised names" msgstr "Wskazanie domeny i zakresu adresów dla generowanych nazw" #: option.c:550 msgid "Activate DNSSEC validation" msgstr "Uaktywnienie walidacji DNSSEC" #: option.c:551 msgid "Specify trust anchor key digest." msgstr "Wskazanie punktu zaufania dla uwierzytelniania DNSSEC." #: option.c:552 msgid "Disable upstream checking for DNSSEC debugging." msgstr "Akceptowanie nieuwiarygodnionych odpowiedzi DNSSEC (ustawienie bitu CD w zapytaniach)." #: option.c:553 msgid "Ensure answers without DNSSEC are in unsigned zones." msgstr "Upewnianie się, że odpowiedzi bez DNSSEC pochodzą ze stref niepodpisanych." #: option.c:554 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:555 msgid "Timestamp file to verify system clock for DNSSEC" msgstr "Plik znacznika czasu do weryfikacji zegara systemowego dla potrzeb DNSSEC." #: option.c:556 #, fuzzy msgid "Set MTU, priority, resend-interval and router-lifetime" msgstr "Ustawianie priorytetu, okresu rozsyłania oraz czasu życia rutera (RA)." #: option.c:557 msgid "Do not log routine DHCP." msgstr "Wyłączenie logowania zwyczajnego DHCP." #: option.c:558 msgid "Do not log routine DHCPv6." msgstr "Wyłączenie logowania zwyczajnego DHCPv6." #: option.c:559 msgid "Do not log RA." msgstr "Wyłączenie logowania RA." #: option.c:560 msgid "Log debugging information." msgstr "" #: option.c:561 msgid "Accept queries only from directly-connected networks." msgstr "Akceptowanie zapytań wyłącznie z sieci podpiętych bezpośrednio." #: option.c:562 msgid "Detect and remove DNS forwarding loops." msgstr "Wykrywanie i usuwanie pętli zapytań DNS." #: option.c:563 msgid "Ignore DNS responses containing ipaddr." msgstr "Ignorowanie odpowiedzi DNS zawierających ipaddr." #: option.c:564 msgid "Set TTL in DNS responses with DHCP-derived addresses." msgstr "Ustawienie TTL w odpowiedziach DNS dla adresów przydzielonych przez DHCP." #: option.c:565 msgid "Delay DHCP replies for at least number of seconds." msgstr "" #: option.c:566 msgid "Enables DHCPv4 Rapid Commit option." msgstr "" #: option.c:567 msgid "Path to debug packet dump file" msgstr "" #: option.c:568 msgid "Mask which packets to dump" msgstr "" #: option.c:569 #, fuzzy msgid "Call dhcp-script when lease expiry changes." msgstr "Wywoływanie dhcp-script w reakcji na zmiany w tablicy ARP." #: option.c:570 msgid "Send Cisco Umbrella identifiers including remote IP." msgstr "" #: option.c:571 #, fuzzy msgid "Do not log routine TFTP." msgstr "Wyłączenie logowania zwyczajnego DHCP." #: option.c:572 msgid "Suppress round-robin ordering of DNS records." msgstr "" #: option.c:802 #, c-format msgid "" "Usage: dnsmasq [options]\n" "\n" msgstr "" "Użycie: dnsmasq [opcje]\n" "\n" #: option.c:804 #, 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" #: option.c:806 #, c-format msgid "Valid options are:\n" msgstr "Dostępne opcje:\n" #: option.c:853 option.c:1055 msgid "bad address" msgstr "zły adres" #: option.c:882 option.c:886 msgid "bad port" msgstr "nieprawidłowy numer portu" #: option.c:899 option.c:1002 option.c:1048 msgid "interface binding not supported" msgstr "nie ma możliwości dowiązywania do interfejsu" #: option.c:955 msgid "Cannot resolve server name" msgstr "" #: option.c:991 msgid "cannot use IPv4 server address with IPv6 source address" msgstr "" #: option.c:997 option.c:1043 msgid "interface can only be specified once" msgstr "" #: option.c:1011 option.c:4785 msgid "bad interface name" msgstr "nieprawidłowa nazwa interfejsu" #: option.c:1037 msgid "cannot use IPv6 server address with IPv4 source address" msgstr "" #: option.c:1124 #, fuzzy msgid "bad IPv4 prefix length" msgstr "zła maska" #: option.c:1155 option.c:1165 option.c:1240 option.c:1250 option.c:5360 msgid "error" msgstr "błąd" #: option.c:1207 #, fuzzy msgid "bad IPv6 prefix length" msgstr "zła maska" #: option.c:1467 msgid "inappropriate vendor:" msgstr "" #: option.c:1474 msgid "inappropriate encap:" msgstr "" #: option.c:1500 msgid "unsupported encapsulation for IPv6 option" msgstr "nieobsługiwany rodzaj enkapsulacji opcji IPv6" #: option.c:1514 msgid "bad dhcp-option" msgstr "błąd w dhcp-option" #: option.c:1592 msgid "bad IP address" msgstr "zły adres IP" #: option.c:1595 option.c:1734 option.c:3928 msgid "bad IPv6 address" msgstr "zły adres IPv6" #: option.c:1688 msgid "bad IPv4 address" msgstr "nieprawidłowy adres IPv4" #: option.c:1761 option.c:1856 msgid "bad domain in dhcp-option" msgstr "nieprawidłowa nazwa domeny w dhcp-option" #: option.c:1900 msgid "dhcp-option too long" msgstr "zbyt długa dhcp-option (>255 znaków)" #: option.c:1907 msgid "illegal dhcp-match" msgstr "niedopuszczalne dhcp-match" #: option.c:1966 msgid "illegal repeated flag" msgstr "wielokrotne użycie opcji niedozwolone (pojawiła się wcześniej w linii poleceń)" #: option.c:1974 msgid "illegal repeated keyword" msgstr "wielokrotne użycie opcji niedozwolone (pojawiła się wsześniej w pliku konfiguracyjnym)" #: option.c:2056 option.c:5533 #, c-format msgid "cannot access directory %s: %s" msgstr "brak dostępu do katalogu %s: %s" #: option.c:2102 tftp.c:573 dump.c:72 #, c-format msgid "cannot access %s: %s" msgstr "brak dostępu do %s: %s" #: option.c:2219 msgid "setting log facility is not possible under Android" msgstr "zmiana log-facility w systemie Android nie jest możliwa" #: option.c:2228 msgid "bad log facility" msgstr "nierozpoznany znacznik logów" #: option.c:2281 msgid "bad MX preference" msgstr "nieprawidłowa wartość preferencji MX" #: option.c:2289 msgid "bad MX name" msgstr "nieprawidłowa nazwa MX" #: option.c:2304 msgid "bad MX target" msgstr "nieprawidłowa wartość celu MX" #: option.c:2324 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" #: option.c:2328 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:2447 #, fuzzy msgid "invalid auth-zone" msgstr "nieprawidłowy zakres adresów w --alias" #: option.c:2589 option.c:2621 #, fuzzy msgid "bad prefix length" msgstr "zła maska" #: option.c:2601 option.c:2642 option.c:2696 msgid "bad prefix" msgstr "zła maska" #: option.c:2716 #, fuzzy msgid "prefix length too small" msgstr "długość prefiksu musi wynosić co najmniej 64" #: option.c:3010 #, fuzzy msgid "Bad address in --address" msgstr "adres jest w użyciu" #: option.c:3110 msgid "recompile with HAVE_IPSET defined to enable ipset directives" msgstr "chcąc korzystać z ipsets przekompiluj dnsmasq-a z HAVE_IPSET" #: option.c:3117 #, fuzzy msgid "recompile with HAVE_NFTSET defined to enable nftset directives" msgstr "chcąc korzystać z ipsets przekompiluj dnsmasq-a z HAVE_IPSET" #: option.c:3192 option.c:3210 #, fuzzy msgid "recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives" msgstr "chcąc korzystać z ipsets przekompiluj dnsmasq-a z HAVE_IPSET" #: option.c:3496 msgid "bad port range" msgstr "nieprawidłowy zakres numerów portów" #: option.c:3522 msgid "bad bridge-interface" msgstr "nieprawidłowa nazwa urządzenia w bridge-interface" #: option.c:3566 msgid "bad shared-network" msgstr "" #: option.c:3620 msgid "only one tag allowed" msgstr "można wskazać tylko jeden znacznik sieci" #: option.c:3641 option.c:3657 option.c:3783 option.c:3791 option.c:3834 msgid "bad dhcp-range" msgstr "nieprawidłowy zakres dhcp-range" #: option.c:3675 msgid "inconsistent DHCP range" msgstr "niespójny zakres adresów DHCP" #: option.c:3741 msgid "prefix length must be exactly 64 for RA subnets" msgstr "długość prefiksu musi wynosić dokładnie 64 dla podsieci RA" #: option.c:3743 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:3746 msgid "prefix length must be at least 64" msgstr "długość prefiksu musi wynosić co najmniej 64" #: option.c:3749 msgid "inconsistent DHCPv6 range" msgstr "niespójny zakres adresów DHCPv6" #: option.c:3768 msgid "prefix must be zero with \"constructor:\" argument" msgstr "prefiks musi wynosić zero z argumentem \"constructor:\"" #: option.c:3893 option.c:3971 msgid "bad hex constant" msgstr "zapis niezgodny z formatem szesnastkowym" #: option.c:3946 #, fuzzy msgid "bad IPv6 prefix" msgstr "zła maska" #: option.c:3994 #, c-format msgid "duplicate dhcp-host IP address %s" msgstr "powtórzony adres IP %s w specyfikacji dhcp-host" #: option.c:4056 msgid "bad DHCP host name" msgstr "niedopuszczalna nazwa komputera w dhcp-host" #: option.c:4142 msgid "bad tag-if" msgstr "nieprawidłowa składnia 'tag-if'" #: option.c:4490 option.c:5046 msgid "invalid port number" msgstr "nieprawidłowy numer portu" #: option.c:4546 msgid "bad dhcp-proxy address" msgstr "zły adres dhcp-proxy" #: option.c:4627 msgid "Bad dhcp-relay" msgstr "zły dhcp-relay" #: option.c:4671 msgid "bad RA-params" msgstr "nieprawidłowe argumenty RA" #: option.c:4681 msgid "bad DUID" msgstr "zły DUID" #: option.c:4715 #, fuzzy msgid "missing address in alias" msgstr "niepoprawny adres" #: option.c:4721 msgid "invalid alias range" msgstr "nieprawidłowy zakres adresów w --alias" #: option.c:4770 #, fuzzy msgid "missing address in dynamic host" msgstr "niepoprawny adres" #: option.c:4785 #, fuzzy msgid "bad dynamic host" msgstr "zły katalog dynamiczny %s: %s" #: option.c:4803 option.c:4819 msgid "bad CNAME" msgstr "zła CNAME" #: option.c:4827 msgid "duplicate CNAME" msgstr "powtórzona CNAME" #: option.c:4854 msgid "bad PTR record" msgstr "nieprawidłowy zapis rekordu PTR" #: option.c:4889 msgid "bad NAPTR record" msgstr "nieprawidłowy zapis rekordu NAPTR" #: option.c:4925 msgid "bad RR record" msgstr "nieprawidłowy zapis rekordu RR" #: option.c:4958 #, fuzzy msgid "bad CAA record" msgstr "nieprawidłowy zapis rekordu RR" #: option.c:4987 msgid "bad TXT record" msgstr "nieprawidłowy zapis rekordu TXT" #: option.c:5030 msgid "bad SRV record" msgstr "nieprawidłowy zapis rekordu SRV" #: option.c:5037 msgid "bad SRV target" msgstr "nieprawidłowa wartość celu SRV" #: option.c:5056 msgid "invalid priority" msgstr "nieprawidłowy priorytet" #: option.c:5061 msgid "invalid weight" msgstr "nieprawidłowa waga" #: option.c:5084 msgid "Bad host-record" msgstr "nieprawidłowy zapis host-record" #: option.c:5123 msgid "Bad name in host-record" msgstr "niedopuszczalna nazwa w host-record" #: option.c:5165 msgid "bad value for dnssec-check-unsigned" msgstr "" #: option.c:5201 msgid "bad trust anchor" msgstr "nieprawidłowa specyfikacja punktu zaufania" #: option.c:5217 msgid "bad HEX in trust anchor" msgstr "zły zapis szesnastkowy" #: option.c:5228 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:5290 msgid "missing \"" msgstr "brakuje \"" #: option.c:5347 msgid "bad option" msgstr "nieprawidłowa opcja" #: option.c:5349 msgid "extraneous parameter" msgstr "nadwyżkowy parametr" #: option.c:5351 msgid "missing parameter" msgstr "brak parametru" #: option.c:5353 msgid "illegal option" msgstr "niedopuszczalna opcja" #: option.c:5363 #, c-format msgid " in output from %s" msgstr "" #: option.c:5365 #, c-format msgid " at line %d of %s" msgstr " w linii %d pliku %s" #: option.c:5380 option.c:5683 option.c:5694 #, c-format msgid "read %s" msgstr "przeczytałem %s" #: option.c:5446 #, fuzzy, c-format msgid "cannot execute %s: %s" msgstr "błąd odczytu z pliku %s: %s" #: option.c:5454 option.c:5615 tftp.c:790 #, c-format msgid "cannot read %s: %s" msgstr "błąd odczytu z pliku %s: %s" #: option.c:5473 #, fuzzy, c-format msgid "error executing %s: %s" msgstr "nie udało się uruchomić %s: %s" #: option.c:5476 #, c-format msgid "%s returns non-zero error code" msgstr "" #: option.c:5775 msgid "junk found in command line" msgstr "jakieś śmieci w linii poleceń" #: option.c:5815 #, c-format msgid "Dnsmasq version %s %s\n" msgstr "Dnsmasq, wersja %s %s\n" #: option.c:5816 #, c-format msgid "" "Compile time options: %s\n" "\n" msgstr "" "Wkompilowane opcje %s\n" "\n" #: option.c:5817 #, c-format msgid "This software comes with ABSOLUTELY NO WARRANTY.\n" msgstr "Autor nie daje ŻADNYCH GWARANCJI egzekwowalnych prawnie.\n" #: option.c:5818 #, 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" #: option.c:5819 #, c-format msgid "under the terms of the GNU General Public License, version 2 or 3.\n" msgstr "na warunkach określonych w GNU General Public Licence, w wersji 2 lub 3.\n" #: option.c:5836 msgid "try --help" msgstr "spróbuj: --help" #: option.c:5838 msgid "try -w" msgstr "spróbuj: -w" #: option.c:5840 #, c-format msgid "bad command line options: %s" msgstr "nieprawidłowa opcja w linii poleceń %s" #: option.c:5909 #, c-format msgid "CNAME loop involving %s" msgstr "" #: option.c:5950 #, c-format msgid "cannot get host-name: %s" msgstr "nie można pobrać nazwy hosta: %s" #: option.c:5978 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." #: option.c:5988 msgid "must have exactly one resolv.conf to read domain from." msgstr "musisz mieć dokładnie jeden plik resolv.conf do odczytu domen." #: option.c:5991 network.c:1727 dhcp.c:892 #, c-format msgid "failed to read %s: %s" msgstr "nie udało się odczytać %s: %s" #: option.c:6008 #, c-format msgid "no search directive found in %s" msgstr "brak wytycznych wyszukiwania w %s" #: option.c:6029 msgid "there must be a default domain when --dhcp-fqdn is set" msgstr "w przypadku używania --dhcp-fqdn trzeba wskazać domyślną domenę" #: option.c:6038 msgid "syntax check OK" msgstr "składnia sprawdzona, jest prawidłowa" #: forward.c:107 #, c-format msgid "failed to send packet: %s" msgstr "wysyłanie pakietu nie powiodło się: %s" #: forward.c:715 msgid "discarding DNS reply: subnet option mismatch" msgstr "odrzucam odpowiedź DNS: nie zgadza się specyfikacja podsieci" #: forward.c:780 #, c-format msgid "nameserver %s refused to do a recursive query" msgstr "serwer nazw %s odmawia wykonania zapytania rekurencyjnego" #: forward.c:826 #, c-format msgid "possible DNS-rebind attack detected: %s" msgstr "prawdopodobnie wykryto atak DNS-rebind: %s" #: forward.c:1239 #, c-format msgid "reducing DNS packet size for nameserver %s to %d" msgstr "" #: forward.c:1565 #, fuzzy, c-format msgid "ignoring query from non-local network %s (logged only once)" msgstr "Ignorowanie zapytań z sieci pozalokalnych." #: forward.c:2139 #, fuzzy, c-format msgid "ignoring query from non-local network %s" msgstr "Ignorowanie zapytań z sieci pozalokalnych." #: forward.c:2501 #, fuzzy, c-format msgid "failed to bind server socket to %s: %s" msgstr "błąd przy przyznawaniu nazwy gniazdu serwera %s: %s" #: forward.c:2867 #, c-format msgid "Maximum number of concurrent DNS queries reached (max: %d)" msgstr "Osiągnięto graniczną ilość jednocześnie obsługiwanych zapytań DNS (maks: %d)" #: forward.c:2869 #, fuzzy, c-format msgid "Maximum number of concurrent DNS queries to %s reached (max: %d)" msgstr "Osiągnięto graniczną ilość jednocześnie obsługiwanych zapytań DNS (maks: %d)" #: network.c:700 #, c-format msgid "stopped listening on %s(#%d): %s port %d" msgstr "" #: network.c:911 #, c-format msgid "failed to create listening socket for %s: %s" msgstr "nie udało się otworzyć gniazda %s: %s" #: network.c:1192 #, c-format msgid "listening on %s(#%d): %s port %d" msgstr "" #: network.c:1219 #, fuzzy, c-format msgid "listening on %s port %d" msgstr "błąd wysyłania pliku %s do komputera %s" #: network.c:1252 #, 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:1259 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:1268 #, fuzzy, c-format msgid "warning: using interface %s instead" msgstr "uwaga: %s niedostępny" #: network.c:1277 #, c-format msgid "warning: no addresses found for interface %s" msgstr "uwaga: nie znaleziono adresu interfejsu %s" #: network.c:1335 #, 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:1340 msgid "try increasing /proc/sys/net/core/optmem_max" msgstr "spróbuj podwyższyć /proc/sys/net/core/optmem_max" #: network.c:1545 #, c-format msgid "failed to bind server socket for %s: %s" msgstr "błąd przy przyznawaniu nazwy gniazdu serwera %s: %s" #: network.c:1622 #, c-format msgid "ignoring nameserver %s - local interface" msgstr "ignorowanie serwera nazw %s - interfejs lokalny" #: network.c:1633 #, c-format msgid "ignoring nameserver %s - cannot make/bind socket: %s" msgstr "ignorowanie serwera nazw %s - nie można utworzyć/dowiązać gniazda: %s" #: network.c:1643 msgid "more servers are defined but not logged" msgstr "" #: network.c:1654 msgid "(no DNSSEC)" msgstr "(brak obsługi DNSSEC)" #: network.c:1657 msgid "unqualified" msgstr "niekwalifikowane(-a)" #: network.c:1657 msgid "names" msgstr "nazwy" #: network.c:1659 msgid "default" msgstr "domyślne" #: network.c:1661 msgid "domain" msgstr "domeny" #: network.c:1663 #, fuzzy, c-format msgid "using nameserver %s#%d for %s %s%s %s" msgstr "używam serwera nazw %s#%d dla %s %s %s" #: network.c:1667 #, 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:1670 #, c-format msgid "using nameserver %s#%d(via %s)" msgstr "używam serwera nazw %s#%d (przez %s)" #: network.c:1672 #, c-format msgid "using nameserver %s#%d" msgstr "używam serwera nazw %s#%d" #: network.c:1687 #, fuzzy, c-format msgid "using only locally-known addresses for %s" msgstr "używam adresów lokalnych tylko dla %s %s" #: network.c:1690 #, fuzzy, c-format msgid "using standard nameservers for %s" msgstr "używam standardowych serwerów nazw dla %s %s" #: network.c:1694 #, fuzzy, c-format msgid "using %d more local addresses" msgstr "używam o %d serwerów nazw więcej" #: network.c:1696 #, c-format msgid "using %d more nameservers" msgstr "używam o %d serwerów nazw więcej" #: dnsmasq.c:192 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:207 msgid "no root trust anchor provided for DNSSEC" msgstr "nie wskazano punktów zaufania dla DNSSEC" #: dnsmasq.c:210 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:212 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:218 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" #: dnsmasq.c:225 msgid "cannot use --conntrack AND --query-port" msgstr "--conntrack i --query-port wzajemnie się wykluczają" #: dnsmasq.c:231 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:236 msgid "asynchronous logging is not available under Solaris" msgstr "zapis do logów w trybie asynchronicznym nie jest dostępny w Solarisie" #: dnsmasq.c:241 msgid "asynchronous logging is not available under Android" msgstr "zapis do logów w trybie asynchronicznym nie jest dostępny w Androidzie" #: dnsmasq.c:246 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:251 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:256 #, fuzzy msgid "Ubus not available: set HAVE_UBUS in src/config.h" msgstr "Obsługa DBus nie została wkompilowana -- ustaw HAVE_DBUS w src/config.h" #: dnsmasq.c:267 msgid "max_port cannot be smaller than min_port" msgstr "max_port nie może być niższy niż min_port" #: dnsmasq.c:271 msgid "port_limit must not be larger than available port range" msgstr "" #: dnsmasq.c:278 msgid "--auth-server required when an auth zone is defined." msgstr "" #: dnsmasq.c:283 msgid "zone serial must be configured in --auth-soa" msgstr "za pomocą --auth-soa musi zostać ustawiony numer seryjny strefy" #: dnsmasq.c:303 msgid "dhcp-range constructor not available on this platform" msgstr "konstrukcja dhcp-range nie jest dostępna w tym systemie" #: dnsmasq.c:377 msgid "cannot set --bind-interfaces and --bind-dynamic" msgstr "--bind-interfaces i --bind-dynamic wzajemnie się wykluczają" #: dnsmasq.c:380 #, c-format msgid "failed to find list of interfaces: %s" msgstr "błąd podczas tworzenia listy interfejsów sieciowych: %s" #: dnsmasq.c:389 #, c-format msgid "unknown interface %s" msgstr "nieznany interfejs %s" #: dnsmasq.c:396 #, c-format msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s" msgstr "nie udało się ustawić SO_BINDTODEVICE gniazda DHCP: %s" #: dnsmasq.c:440 #, fuzzy msgid "Packet dumps not available: set HAVE_DUMP in src/config.h" msgstr "Obsługa DBus nie została wkompilowana -- ustaw HAVE_DBUS w src/config.h" #: dnsmasq.c:448 dnsmasq.c:1232 #, c-format msgid "DBus error: %s" msgstr "błąd DBus: %s" #: dnsmasq.c:451 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" #: dnsmasq.c:459 dnsmasq.c:1253 #, fuzzy, c-format msgid "UBus error: %s" msgstr "błąd DBus: %s" #: dnsmasq.c:462 #, fuzzy msgid "UBus not available: set HAVE_UBUS in src/config.h" msgstr "Obsługa DBus nie została wkompilowana -- ustaw HAVE_DBUS w src/config.h" #: dnsmasq.c:492 #, c-format msgid "unknown user or group: %s" msgstr "nieznany użytkownik lub grupa: %s" #: dnsmasq.c:568 #, c-format msgid "process is missing required capability %s" msgstr "" #: dnsmasq.c:600 #, c-format msgid "cannot chdir to filesystem root: %s" msgstr "nie potrafię wejść do głównego katalogu: %s" #: dnsmasq.c:852 #, c-format msgid "started, version %s DNS disabled" msgstr "uruchomiony, wersja %s, DNS wyłączony" #: dnsmasq.c:857 #, c-format msgid "started, version %s cachesize %d" msgstr "uruchomiony, wersja %s, %d miejsc w pamięci podręcznej" #: dnsmasq.c:859 msgid "cache size greater than 10000 may cause performance issues, and is unlikely to be useful." msgstr "" #: dnsmasq.c:862 #, c-format msgid "started, version %s cache disabled" msgstr "uruchomiony, wersja %s, pamięć podręczna wyłączona" #: dnsmasq.c:865 msgid "DNS service limited to local subnets" msgstr "usługa DNS ograniczona do lokalnych podsieci" #: dnsmasq.c:868 #, c-format msgid "compile time options: %s" msgstr "opcje kompilacji: %s" #: dnsmasq.c:877 msgid "DBus support enabled: connected to system bus" msgstr "obsługa DBus włączona, podłączono do serwera DBus" #: dnsmasq.c:879 msgid "DBus support enabled: bus connection pending" msgstr "obsługa DBus włączona, trwa podłączanie do serwera DBus" #: dnsmasq.c:887 #, fuzzy msgid "UBus support enabled: connected to system bus" msgstr "obsługa DBus włączona, podłączono do serwera DBus" #: dnsmasq.c:889 #, fuzzy msgid "UBus support enabled: bus connection pending" msgstr "obsługa DBus włączona, trwa podłączanie do serwera DBus" #: dnsmasq.c:909 msgid "DNSSEC validation enabled but all unsigned answers are trusted" msgstr "" #: dnsmasq.c:911 msgid "DNSSEC validation enabled" msgstr "walidacja DNSSEC włączona" #: dnsmasq.c:915 #, fuzzy msgid "DNSSEC signature timestamps not checked until receipt of SIGINT" msgstr "sprawdzanie sygnatur czasowych DNSSEC wyłączone do czasu przeładowania pamięci podręcznej" #: dnsmasq.c:918 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:921 #, c-format msgid "configured with trust anchor for %s keytag %u" msgstr "" #: dnsmasq.c:927 #, c-format msgid "warning: failed to change owner of %s: %s" msgstr "uwaga: nie udało się zmienić użytkownika pliku %s: %s" #: dnsmasq.c:932 msgid "setting --bind-interfaces option because of OS limitations" msgstr "ustawiam --bind-interfaces z powodu ograniczeń systemu operacyjnego" #: dnsmasq.c:945 #, c-format msgid "warning: interface %s does not currently exist" msgstr "uwaga: interfejs %s nie jest włączony" #: dnsmasq.c:950 msgid "warning: ignoring resolv-file flag because no-resolv is set" msgstr "uwaga: ignoruję opcję resolv-file, ponieważ wybrano tryb no-resolv" #: dnsmasq.c:953 msgid "warning: no upstream servers configured" msgstr "uwaga: nie wskazano nadrzędnych serwerów DNS" #: dnsmasq.c:957 #, 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:978 msgid "IPv6 router advertisement enabled" msgstr "anonsowanie rutera IPv6 włączone" #: dnsmasq.c:983 #, c-format msgid "DHCP, sockets bound exclusively to interface %s" msgstr "DHCP, gniazda dowiązane na wyłączność interfejsowi %s" #: dnsmasq.c:1000 msgid "root is " msgstr "z głównym katalogiem w " #: dnsmasq.c:1000 msgid "enabled" msgstr "włączony" #: dnsmasq.c:1002 msgid "secure mode" msgstr "w trybie bezpiecznym" #: dnsmasq.c:1003 #, fuzzy msgid "single port mode" msgstr "nieprawidłowy numer portu" #: dnsmasq.c:1006 #, c-format msgid "warning: %s inaccessible" msgstr "uwaga: %s niedostępny" #: dnsmasq.c:1010 #, c-format msgid "warning: TFTP directory %s inaccessible" msgstr "uwaga: katalog TFTP %s nie jest dostępny" #: dnsmasq.c:1036 #, c-format msgid "restricting maximum simultaneous TFTP transfers to %d" msgstr "ograniczam ilość jednoczesnych przesłań TFTP do %d" #: dnsmasq.c:1095 #, fuzzy, c-format msgid "error binding DHCP socket to device %s" msgstr "Błąd wysyłania pakietu DHCP do %s: %s" #: dnsmasq.c:1229 msgid "connected to system DBus" msgstr "podłączono do DBus-a" #: dnsmasq.c:1250 #, fuzzy msgid "connected to system UBus" msgstr "podłączono do DBus-a" #: dnsmasq.c:1416 #, c-format msgid "cannot fork into background: %s" msgstr "nie potrafię przełączyć się do pracy w tle: %s" #: dnsmasq.c:1420 #, c-format msgid "failed to create helper: %s" msgstr "nie udało się utworzyć procesu pomocniczego: %s" #: dnsmasq.c:1424 #, c-format msgid "setting capabilities failed: %s" msgstr "nie powiodło się ustawianie ograniczeń (capabilities): %s" #: dnsmasq.c:1428 #, c-format msgid "failed to change user-id to %s: %s" msgstr "nie udało się zmienić użytkownika procesu na %s: %s" #: dnsmasq.c:1432 #, c-format msgid "failed to change group-id to %s: %s" msgstr "nie udało się zmienić grupy procesu na %s: %s" #: dnsmasq.c:1436 #, c-format msgid "failed to open pidfile %s: %s" msgstr "nie udało się otworzyć pliku z PID-em %s: %s" #: dnsmasq.c:1440 #, c-format msgid "cannot open log %s: %s" msgstr "nie udało się otworzyć logu %s: %s" #: dnsmasq.c:1444 #, c-format msgid "failed to load Lua script: %s" msgstr "nie udało się wczytać skryptu Lua: %s" #: dnsmasq.c:1448 #, c-format msgid "TFTP directory %s inaccessible: %s" msgstr "katalog TFTP %s nie jest dostępny: %s" #: dnsmasq.c:1452 #, c-format msgid "cannot create timestamp file %s: %s" msgstr "nie potrafię utworzyć pliku znacznika czasu %s: %s" #: dnsmasq.c:1536 #, c-format msgid "script process killed by signal %d" msgstr "skrypt został zabity sygnałem %d" #: dnsmasq.c:1540 #, c-format msgid "script process exited with status %d" msgstr "skrypt zakończył się z kodem powrotu %d" #: dnsmasq.c:1544 #, c-format msgid "failed to execute %s: %s" msgstr "nie udało się uruchomić %s: %s" #: dnsmasq.c:1584 msgid "now checking DNSSEC signature timestamps" msgstr "trwa sprawdzanie sygnatur czasowych podpisów DNSSEC" #: dnsmasq.c:1619 dnssec.c:160 dnssec.c:204 #, c-format msgid "failed to update mtime on %s: %s" msgstr "nie udało się uaktualnić znacznika czasu pliku %s: %s" #: dnsmasq.c:1631 msgid "exiting on receipt of SIGTERM" msgstr "zakończyłem działanie z powodu odebrania SIGTERM" #: dnsmasq.c:1659 #, c-format msgid "failed to access %s: %s" msgstr "brak dostępu do %s: %s" #: dnsmasq.c:1690 #, c-format msgid "reading %s" msgstr "czytanie %s" #: dnsmasq.c:1706 #, c-format msgid "no servers found in %s, will retry" msgstr "w %s nie znalazłem serwerów, spróbuję ponownie później" #: dhcp.c:51 #, c-format msgid "cannot create DHCP socket: %s" msgstr "nie udało się utworzyć gniazda dla DHCP: %s" #: dhcp.c:66 #, c-format msgid "failed to set options on DHCP socket: %s" msgstr "błąd podczas ustawiania opcji gniazda DHCP: %s" #: dhcp.c:87 #, 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" #: dhcp.c:99 #, c-format msgid "failed to bind DHCP server socket: %s" msgstr "błąd przy przyznawaniu nazwy gniazdu serwera DHCP: %s" #: dhcp.c:125 #, c-format msgid "cannot create ICMP raw socket: %s." msgstr "nie udało się utworzyć surowego gniazda ICMP: %s." #: dhcp.c:254 dhcp6.c:186 #, c-format msgid "unknown interface %s in bridge-interface" msgstr "nieznany interfejs %s w bridge-u" #: dhcp.c:295 #, c-format msgid "DHCP packet received on %s which has no address" msgstr "żądanie DHCP odebrano na interfejsie %s, który nie ma adresu" #: dhcp.c:429 #, c-format msgid "ARP-cache injection failed: %s" msgstr "uzupełnienie pamięci podręcznej ARP nie powiodło się: %s" #: dhcp.c:490 #, c-format msgid "Error sending DHCP packet to %s: %s" msgstr "Błąd wysyłania pakietu DHCP do %s: %s" #: dhcp.c:547 #, 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" #: dhcp.c:930 #, c-format msgid "bad line at %s line %d" msgstr "zła zawartość pliku %s, w linii %d" #: dhcp.c:973 #, 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:1034 #, c-format msgid "read %s - %d addresses" msgstr "wczytałem %s - %d adresów" #: dhcp.c:1136 #, fuzzy, c-format msgid "Cannot broadcast DHCP relay via interface %s" msgstr "Nie mogę rozesłać do serwerów DHCPv6 nie mając prawidłowego interfejsu" #: dhcp.c:1160 #, c-format msgid "broadcast via %s" msgstr "" #: dhcp.c:1163 rfc3315.c:2219 #, fuzzy, c-format msgid "DHCP relay at %s -> %s" msgstr "przekazywanie DHCP %s -> %s" #: lease.c:64 #, c-format msgid "ignoring invalid line in lease database: %s %s %s %s ..." msgstr "" #: lease.c:101 #, c-format msgid "ignoring invalid line in lease database, bad address: %s" msgstr "" #: lease.c:108 msgid "too many stored leases" msgstr "zbyt duża ilość zapisanych dzierżaw" #: lease.c:176 #, c-format msgid "cannot open or create lease file %s: %s" msgstr "nie potrafię otworzyć albo utworzyć pliku dzierżaw %s: %s" #: lease.c:185 #, fuzzy msgid "failed to parse lease database cleanly" msgstr "nie udało się odczytać %s: %s" #: lease.c:188 #, fuzzy, c-format msgid "failed to read lease file %s: %s" msgstr "nie udało się odczytać %s: %s" #: lease.c:204 #, c-format msgid "cannot run lease-init script %s: %s" msgstr "nie potrafię uruchomić skryptu %s: %s" #: lease.c:210 #, c-format msgid "lease-init script returned exit code %s" msgstr "skrypt zakończył się z kodem powrotu %s" #: lease.c:381 #, fuzzy, c-format msgid "failed to write %s: %s (retry in %u s)" msgstr "błąd zapisu do %s: %s (spróbuję ponownie za %us)" #: lease.c:955 #, c-format msgid "Ignoring domain %s for DHCP host name %s" msgstr "Nie uwzględniam części domenowej (%s) dla komputera %s" #: rfc2131.c:378 msgid "with subnet selector" msgstr "z wyborem podsieci" #: rfc2131.c:383 msgid "via" msgstr "przez" #: rfc2131.c:389 #, c-format msgid "no address range available for DHCP request %s %s" msgstr "nie zdefiniowano zakresu adresów odpowiedniego dla żądania %s %s" #: rfc2131.c:403 #, c-format msgid "%u available DHCP subnet: %s/%s" msgstr "%u dostępna podsieć DHCP: %s/%s" #: rfc2131.c:409 rfc3315.c:320 #, c-format msgid "%u available DHCP range: %s -- %s" msgstr "%u dostępny zakres adresów DHCP: %s -- %s" #: rfc2131.c:521 #, c-format msgid "%u vendor class: %s" msgstr "%u klasa dostawcy: %s" #: rfc2131.c:523 #, c-format msgid "%u user class: %s" msgstr "%u klasa użytkownika: %s" #: rfc2131.c:557 msgid "disabled" msgstr "wyłączony(a)" #: rfc2131.c:598 rfc2131.c:1087 rfc2131.c:1536 rfc3315.c:633 rfc3315.c:816 #: rfc3315.c:1122 msgid "ignored" msgstr "ignoruję" #: rfc2131.c:613 rfc2131.c:1340 rfc3315.c:868 msgid "address in use" msgstr "adres jest w użyciu" #: rfc2131.c:627 rfc2131.c:1141 msgid "no address available" msgstr "brak dostępnego adresu" #: rfc2131.c:634 rfc2131.c:1302 msgid "wrong network" msgstr "nieprawidłowa sieć" #: rfc2131.c:649 msgid "no address configured" msgstr "brak skonfigurowanego adresu" #: rfc2131.c:655 rfc2131.c:1353 msgid "no leases left" msgstr "brak wolnych dzierżaw" #: rfc2131.c:756 rfc3315.c:500 #, c-format msgid "%u client provides name: %s" msgstr "klient %u przedstawia się jako %s" #: rfc2131.c:885 msgid "PXE BIS not supported" msgstr "PXE BIS nie jest obsługiwane" #: rfc2131.c:1054 rfc3315.c:1223 #, c-format msgid "disabling DHCP static address %s for %s" msgstr "wyłączam statyczne przypisanie adresu %s dla %s" #: rfc2131.c:1075 msgid "unknown lease" msgstr "nieznana dzierżawa" #: rfc2131.c:1110 #, 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" #: rfc2131.c:1120 #, 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" #: rfc2131.c:1123 #, 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" #: rfc2131.c:1139 rfc2131.c:1346 msgid "no unique-id" msgstr "brak unikalnego id" #: rfc2131.c:1238 msgid "wrong server-ID" msgstr "nieprawidłowy identyfikator serwera (server-ID)" #: rfc2131.c:1257 msgid "wrong address" msgstr "błędny adres" #: rfc2131.c:1275 rfc3315.c:976 msgid "lease not found" msgstr "dzierżawa nieznaleziona" #: rfc2131.c:1310 msgid "address not available" msgstr "adres niedostępny" #: rfc2131.c:1321 msgid "static lease available" msgstr "dostępna statyczna dzierżawa" #: rfc2131.c:1325 msgid "address reserved" msgstr "adres zarezerwowany" #: rfc2131.c:1334 #, c-format msgid "abandoning lease to %s of %s" msgstr "porzucam przypisanie do %s nazwy %s" #: rfc2131.c:1870 #, c-format msgid "%u bootfile name: %s" msgstr "%u nazwa pliku bootowania: %s" #: rfc2131.c:1879 #, c-format msgid "%u server name: %s" msgstr "%u nazwa serwera: %s" #: rfc2131.c:1889 #, c-format msgid "%u next server: %s" msgstr "%u następny serwer: %s" #: rfc2131.c:1893 #, c-format msgid "%u broadcast response" msgstr "%u odpowiedź rozgłoszeniowa" #: rfc2131.c:1956 #, 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" #: rfc2131.c:2267 msgid "PXE menu too large" msgstr "menu PXE zbyt duże" #: rfc2131.c:2430 rfc3315.c:1517 #, c-format msgid "%u requested options: %s" msgstr "%u zażądano: %s" #: rfc2131.c:2747 #, 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" #: rfc2131.c:2810 #, c-format msgid "%u reply delay: %d" msgstr "" #: netlink.c:86 #, c-format msgid "cannot create netlink socket: %s" msgstr "nie potrafię utworzyć połączenia netlink %s" #: netlink.c:379 #, c-format msgid "netlink returns error: %s" msgstr "wystąpił błąd w połączeniu netlink %s" #: dbus.c:491 #, c-format msgid "Enabling --%s option from D-Bus" msgstr "opcja --%s została właśnie aktywowana za pomocą D-Bus" #: dbus.c:496 #, c-format msgid "Disabling --%s option from D-Bus" msgstr "opcja --%s została właśnie dezaktywowana za pomocą D-Bus" #: dbus.c:857 msgid "setting upstream servers from DBus" msgstr "ustawiam adresy serwerów nadrzędnych na podstawie informacji odebranych z DBus" #: dbus.c:907 msgid "could not register a DBus message handler" msgstr "nie można zarejestrować uchwytu DBus" #: bpf.c:261 #, c-format msgid "cannot create DHCP BPF socket: %s" msgstr "nie potrafię utworzyć gniazda DHCP BPF: %s" #: bpf.c:289 #, 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" #: bpf.c:374 #, c-format msgid "cannot create PF_ROUTE socket: %s" msgstr "nie udało się utworzyć gniazda PF_ROUTE: %s" #: bpf.c:395 msgid "Unknown protocol version from route socket" msgstr "Nieznana wersja protokołu." #: helper.c:150 msgid "lease() function missing in Lua script" msgstr "w skrypcie Lua brak funkcji lease()" #: tftp.c:353 msgid "unable to get free port for TFTP" msgstr "brak wolnego portu dla usługi TFTP" #: tftp.c:369 #, c-format msgid "unsupported request from %s" msgstr "nieobsługiwane żądanie od komputera %s" #: tftp.c:520 #, c-format msgid "file %s not found for %s" msgstr "plik %s nie został znaleziony dla %s" #: tftp.c:609 #, c-format msgid "ignoring packet from %s (TID mismatch)" msgstr "" #: tftp.c:662 #, c-format msgid "failed sending %s to %s" msgstr "błąd wysyłania pliku %s do komputera %s" #: tftp.c:662 #, c-format msgid "sent %s to %s" msgstr "plik %s przesłano do %s" #: tftp.c:712 #, c-format msgid "error %d %s received from %s" msgstr "błąd %d %s odebrano od %s" #: log.c:203 #, c-format msgid "overflow: %d log entries lost" msgstr "przepełnienie: stracono %d wpisów do logów" #: log.c:281 #, c-format msgid "log failed: %s" msgstr "nie udało się zapisać komunikatów do %s" #: log.c:490 msgid "FAILED to start up" msgstr "BŁĄD: nie udało się uruchomić dnsmasq-a" #: conntrack.c:63 #, c-format msgid "Conntrack connection mark retrieval failed: %s" msgstr "Nie udało się odcztać znacznika połączenia (conntrack): %s" #: dhcp6.c:51 #, c-format msgid "cannot create DHCPv6 socket: %s" msgstr "nie udało się utworzyć gniazda dla DHCPv6: %s" #: dhcp6.c:72 #, 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:84 #, c-format msgid "failed to bind DHCPv6 server socket: %s" msgstr "dowiązywanie gniazda serwera DHCPv6 zakończone niepowodzeniem: %s" #: rfc3315.c:173 #, 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:182 #, 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:317 #, c-format msgid "%u available DHCPv6 subnet: %s/%d" msgstr "%u dostępna podsieć DHCPv6: %s/%d" #: rfc3315.c:400 #, c-format msgid "%u vendor class: %u" msgstr "%u klasa dostawcy: %u" #: rfc3315.c:448 #, c-format msgid "%u client MAC address: %s" msgstr "adres MAC klienta %u: %s" #: rfc3315.c:763 rfc3315.c:860 msgid "address unavailable" msgstr "adres niedostępny" #: rfc3315.c:775 rfc3315.c:904 rfc3315.c:1273 msgid "success" msgstr "udane" #: rfc3315.c:790 rfc3315.c:799 rfc3315.c:912 rfc3315.c:914 rfc3315.c:1048 msgid "no addresses available" msgstr "brak wolnych adresów" #: rfc3315.c:891 msgid "not on link" msgstr "poza zasięgiem" #: rfc3315.c:980 rfc3315.c:1181 rfc3315.c:1262 msgid "no binding found" msgstr "brak powiązania" #: rfc3315.c:1017 msgid "deprecated" msgstr "przestarzały" #: rfc3315.c:1024 msgid "address invalid" msgstr "niepoprawny adres" #: rfc3315.c:1082 rfc3315.c:1084 msgid "confirm failed" msgstr "brak potwierdzenia" #: rfc3315.c:1099 msgid "all addresses still on link" msgstr "wszystkie adresy ciągle w użyciu" #: rfc3315.c:1190 msgid "release received" msgstr "adres został zwolniony" #: rfc3315.c:2200 #, fuzzy, c-format msgid "Cannot multicast DHCP relay via interface %s" msgstr "Nie mogę rozesłać do serwerów DHCPv6 nie mając prawidłowego interfejsu" #: rfc3315.c:2216 #, c-format msgid "multicast via %s" msgstr "" #: dhcp-common.c:187 #, c-format msgid "Ignoring duplicate dhcp-option %d" msgstr "Pomijam powtórzoną dhcp-option %d" #: dhcp-common.c:264 #, c-format msgid "%u tags: %s" msgstr "%u cechy: %s" #: dhcp-common.c:484 #, 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:518 #, 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:738 #, c-format msgid "Known DHCP options:\n" msgstr "Znane opcje DHCP:\n" #: dhcp-common.c:749 #, c-format msgid "Known DHCPv6 options:\n" msgstr "Rozpoznawane opcje DHCPv6:\n" #: dhcp-common.c:946 msgid ", prefix deprecated" msgstr ", przestarzały prefiks" #: dhcp-common.c:949 #, c-format msgid ", lease time " msgstr ", czas dzierżawy " #: dhcp-common.c:991 #, c-format msgid "%s stateless on %s%.0s%.0s%s" msgstr "%s bezstanowy na %s%.0s%.0s%s" #: dhcp-common.c:993 #, 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:995 #, 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:996 #, c-format msgid "%s, IP range %s -- %s%s%.0s" msgstr "%s, zakres IP %s -- %s%s%.0s" #: dhcp-common.c:1009 #, c-format msgid "DHCPv4-derived IPv6 names on %s%s" msgstr "pochodzące z DHCPv4 nazwy IPv6 na %s%s" #: dhcp-common.c:1012 #, c-format msgid "router advertisement on %s%s" msgstr "anonsowanie rutera na %s%s" #: dhcp-common.c:1043 #, fuzzy, c-format msgid "DHCP relay from %s via %s" msgstr "przekazywanie DHCP z %s do %s" #: dhcp-common.c:1045 #, c-format msgid "DHCP relay from %s to %s via %s" msgstr "przekazywanie DHCP z %s do %s za pomocą %s" #: dhcp-common.c:1048 #, c-format msgid "DHCP relay from %s to %s" msgstr "przekazywanie DHCP z %s do %s" #: radv.c:110 #, c-format msgid "cannot create ICMPv6 socket: %s" msgstr "nie udało się utworzyć gniazda dla ICMPv6: %s" #: auth.c:462 #, c-format msgid "ignoring zone transfer request from %s" msgstr "ignoruję żądanie transferu strefy od %s" #: ipset.c:99 #, c-format msgid "failed to create IPset control socket: %s" msgstr "nie powiodło się otwieranie gniazda sterującego IPset: %s" #: ipset.c:211 #, fuzzy, c-format msgid "failed to update ipset %s: %s" msgstr "nie udało się uaktualnić znacznika czasu pliku %s: %s" #: pattern.c:29 #, c-format msgid "[pattern.c:%d] Assertion failure: %s" msgstr "" #: pattern.c:142 #, c-format msgid "Invalid DNS name: Invalid character %c." msgstr "" #: pattern.c:151 msgid "Invalid DNS name: Empty label." msgstr "" #: pattern.c:156 msgid "Invalid DNS name: Label starts with hyphen." msgstr "" #: pattern.c:170 msgid "Invalid DNS name: Label ends with hyphen." msgstr "" #: pattern.c:176 #, c-format msgid "Invalid DNS name: Label is too long (%zu)." msgstr "" #: pattern.c:184 #, c-format msgid "Invalid DNS name: Not enough labels (%zu)." msgstr "" #: pattern.c:189 msgid "Invalid DNS name: Final label is fully numeric." msgstr "" #: pattern.c:199 msgid "Invalid DNS name: \"local\" pseudo-TLD." msgstr "" #: pattern.c:204 #, c-format msgid "DNS name has invalid length (%zu)." msgstr "" #: pattern.c:258 #, c-format msgid "Invalid DNS name pattern: Invalid character %c." msgstr "" #: pattern.c:267 msgid "Invalid DNS name pattern: Empty label." msgstr "" #: pattern.c:272 msgid "Invalid DNS name pattern: Label starts with hyphen." msgstr "" #: pattern.c:285 msgid "Invalid DNS name pattern: Wildcard character used more than twice per label." msgstr "" #: pattern.c:295 msgid "Invalid DNS name pattern: Label ends with hyphen." msgstr "" #: pattern.c:301 #, c-format msgid "Invalid DNS name pattern: Label is too long (%zu)." msgstr "" #: pattern.c:309 #, c-format msgid "Invalid DNS name pattern: Not enough labels (%zu)." msgstr "" #: pattern.c:314 msgid "Invalid DNS name pattern: Wildcard within final two labels." msgstr "" #: pattern.c:319 msgid "Invalid DNS name pattern: Final label is fully numeric." msgstr "" #: pattern.c:329 msgid "Invalid DNS name pattern: \"local\" pseudo-TLD." msgstr "" #: pattern.c:334 #, c-format msgid "DNS name pattern has invalid length after removing wildcards (%zu)." msgstr "" #: dnssec.c:206 #, fuzzy msgid "system time considered valid, now checking DNSSEC signature timestamps." msgstr "trwa sprawdzanie sygnatur czasowych podpisów DNSSEC" #: dnssec.c:1018 #, c-format msgid "Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support" msgstr "" #: blockdata.c:55 #, fuzzy, c-format msgid "pool memory in use %zu, max %zu, allocated %zu" msgstr "DNSSEC: zużycie pamięci %u, maks. %u, przydzielona %u" #: tables.c:61 #, c-format msgid "failed to access pf devices: %s" msgstr "brak dostępu do /dev/pf (filtra pakietów): %s" #: tables.c:74 #, c-format msgid "warning: no opened pf devices %s" msgstr "uwaga: brak otwartych filtrów pakietów %s" #: tables.c:82 #, c-format msgid "error: cannot use table name %s" msgstr "błąd: nie potrafię użyć nazwy tablicy %s" #: tables.c:90 #, c-format msgid "error: cannot strlcpy table name %s" msgstr "błąd: nie potrafię strlcpy nazwy tablicy %s" #: tables.c:101 #, fuzzy, c-format msgid "IPset: error: %s" msgstr "błąd DBus: %s" #: tables.c:108 msgid "info: table created" msgstr "info: tablica utworzona" #: tables.c:133 #, c-format msgid "warning: DIOCR%sADDRS: %s" msgstr "uwaga: DIOCR%sADDRS: %s" #: tables.c:137 #, c-format msgid "%d addresses %s" msgstr "%d adresów %s" #: inotify.c:62 #, c-format msgid "cannot access path %s: %s" msgstr "brak dostępu do katalogu %s: %s" #: inotify.c:95 #, c-format msgid "failed to create inotify: %s" msgstr "nie udało się uruchomić powiadamiania inotify: %s" #: inotify.c:111 #, c-format msgid "too many symlinks following %s" msgstr "zbyt wiele odniesień począwszy od %s" #: inotify.c:127 #, 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:131 inotify.c:200 #, c-format msgid "failed to create inotify for %s: %s" msgstr "nie udało się utworzyć powiadamiania dla %s: %s" #: inotify.c:178 inotify.c:185 #, c-format msgid "bad dynamic directory %s: %s" msgstr "zły katalog dynamiczny %s: %s" #: inotify.c:186 #, fuzzy msgid "not a directory" msgstr "brak dostępu do katalogu %s: %s" #: inotify.c:299 #, c-format msgid "inotify: %s removed" msgstr "" #: inotify.c:301 #, fuzzy, c-format msgid "inotify: %s new or modified" msgstr "inotify: pojawił się lub uległ zmianie plik %s" #: inotify.c:309 #, c-format msgid "inotify: flushed %u names read from %s" msgstr "" #: dump.c:68 #, fuzzy, c-format msgid "cannot create %s: %s" msgstr "błąd odczytu z pliku %s: %s" #: dump.c:74 #, fuzzy, c-format msgid "bad header in %s" msgstr "adres jest w użyciu" #: dump.c:287 #, fuzzy msgid "failed to write packet dump" msgstr "wysyłanie pakietu nie powiodło się: %s" #: dump.c:289 #, c-format msgid "%u dumping packet %u mask 0x%04x" msgstr "" #: dump.c:291 #, c-format msgid "dumping packet %u mask 0x%04x" msgstr "" #: ubus.c:79 #, c-format msgid "UBus subscription callback: %s subscriber(s)" msgstr "" #: ubus.c:99 #, fuzzy, c-format msgid "Cannot reconnect to UBus: %s" msgstr "nie udało się otworzyć logu %s: %s" #: ubus.c:135 msgid "Cannot set UBus listeners: no connection" msgstr "" #: ubus.c:155 msgid "Cannot poll UBus listeners: no connection" msgstr "" #: ubus.c:168 msgid "Disconnecting from UBus" msgstr "" #: ubus.c:179 ubus.c:326 #, c-format msgid "UBus command failed: %d (%s)" msgstr "" #: hash-questions.c:40 msgid "Failed to create SHA-256 hash object" msgstr "" #: nftset.c:35 #, fuzzy msgid "failed to create nftset context" msgstr "nie powiodło się otwieranie gniazda sterującego IPset: %s" #, fuzzy #~ msgid "bad IPv4 prefix" #~ msgstr "zła maska" #, fuzzy #~ msgid "Cannot add object to UBus: %s" #~ msgstr "nie udało się otworzyć logu %s: %s" #, fuzzy #~ msgid "Failed to send UBus event: %s" #~ msgstr "wysyłanie pakietu nie powiodło się: %s" #~ msgid "Specify DHCPv6 prefix class" #~ msgstr "Określenie prefiksu klasy DHCPv6" #~ msgid "cannot run scripts under uClinux" #~ msgstr "w uClinuksie nie ma możliwości uruchamiania skryptów" #~ msgid "cannot match tags in --dhcp-host" #~ msgstr "--dhcp-host nie dopuszcza dopasowywania na podstawie znaczników" #~ 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" #~ msgid "unknown prefix-class %d" #~ msgstr "nieznana klasa sieci %d" #~ msgid "bad TTL" #~ msgstr "zły TTL" #~ msgid "error: fill_addr missused" #~ msgstr "błąd: niepoprawnie użyty fill_addr" #~ msgid "warning: pfr_add_tables: %s(%d)" #~ msgstr "uwaga: pfr_add_tables: %s(%d)" #, 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" #~ msgid "only one dhcp-optsfile allowed" #~ 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" #~ msgid "TXT record string too long" #~ 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" #~ msgid "failed to bind listening socket for %s: %s" #~ msgstr "błąd przy przyznawaniu nazwy gniazdu %s: %s" dnsmasq-2.91/po/fr.po0000664000175000017500000022572514765043257012714 0ustar srksrk# French translations for dnsmasq package. # This file is put in the public domain. # Lionel Tricon , 2005. # Translation completed by Gildas Le Nadan <3ntr0p13@gmail.com> msgid "" msgstr "" "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: 2017-07-17 18:30+0100\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:652 msgid "Internal error in cache." msgstr "" #: cache.c:1179 #, c-format msgid "failed to load names from %s: %s" msgstr "Impossible de charger les noms partir de %s : %s" #: cache.c:1201 dhcp.c:943 #, c-format msgid "bad address at %s line %d" msgstr "mauvaise adresse dans %s ligne %d" #: cache.c:1254 dhcp.c:959 #, c-format msgid "bad name at %s line %d" msgstr "mauvais nom dans %s ligne %d" #: cache.c:1265 #, fuzzy, c-format msgid "read %s - %d names" msgstr "lecture %s - %d adresses" #: cache.c:1381 msgid "cleared cache" msgstr "cache vid" #: cache.c:1445 #, c-format msgid "No IPv4 address found for %s" msgstr "Aucune adresse IPv4 trouve pour %s" #: cache.c:1491 #, 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:1515 #, 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:1760 #, c-format msgid "time %lu" msgstr "horodatage %lu" #: cache.c:1761 #, 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 entres non-expires rutilises" #: cache.c:1763 #, c-format msgid "queries forwarded %u, queries answered locally %u" msgstr "requtes transmises %u, requtes rsolues localement %u" #: cache.c:1766 #, c-format msgid "queries answered from stale cache %u" msgstr "" #: cache.c:1768 #, fuzzy, c-format msgid "queries for authoritative zones %u" msgstr "Configure la dure de vie (Time To Live) pour les rponses faisant autorit" #: cache.c:1796 #, fuzzy, c-format msgid "server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums" msgstr "serveur %s#%d: requtes envoyes %u, requtes ressayes ou choues %u" #: util.c:51 #, c-format msgid "failed to seed the random number generator: %s" msgstr "impossible d'initialiser le gnrateur de nombre alatoire : %s" #: util.c:246 msgid "failed to allocate memory" msgstr "impossible d'allouer la mmoire" #: util.c:305 option.c:696 msgid "could not get memory" msgstr "impossible d'allouer de la mmoire" #: util.c:326 #, c-format msgid "cannot create pipe: %s" msgstr "Ne peut pas crer le tube %s : %s" #: util.c:334 #, c-format msgid "failed to allocate %d bytes" msgstr "impossible d'allouer %d octets" #: util.c:344 #, fuzzy, c-format msgid "failed to reallocate %d bytes" msgstr "impossible d'allouer %d octets" #: util.c:465 #, fuzzy, c-format msgid "cannot read monotonic clock: %s" msgstr "ne peux lier une socket netlink : %s" #: util.c:579 #, c-format msgid "infinite" msgstr "illimit(e)" #: util.c:867 #, c-format msgid "failed to find kernel version: %s" msgstr "impossible de trouver la version de noyau : %s" #: option.c:393 msgid "Specify local address(es) to listen on." msgstr "Spcifie la ou les adresse(s) locales o le dmon doit se mettre l'coute." #: option.c:394 msgid "Return ipaddr for all hosts in specified domains." msgstr "Retourne les adresses IP pour toutes les machines prsentes dans les domaines spcifis" #: option.c:395 msgid "Fake reverse lookups for RFC1918 private address ranges." msgstr "Traduction inverse truque pour la plage d'adresse prive RFC1918" #: option.c:396 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:397 #, c-format msgid "Specify the size of the cache in entries (defaults to %s)." msgstr "Spcifie le nombre d'entres que contiendra le cache (par dfaut : %s)." #: option.c:398 #, c-format msgid "Specify configuration file (defaults to %s)." msgstr "Spcifie le nom du fichier de configuration (par dfaut : %s)" #: option.c:399 msgid "Do NOT fork into the background: run in debug mode." msgstr "Ne passe pas en tche de fond : dmarre en mode debug" #: option.c:400 msgid "Do NOT forward queries with no domain part." msgstr "Ne retransmet pas les requtes qui n'ont pas de domaine." #: option.c:401 msgid "Return self-pointing MX records for local hosts." msgstr "Retourne les champs MX pour les machines locales." #: option.c:402 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:403 msgid "Don't forward spurious DNS requests from Windows hosts." msgstr "Ne retransmet pas les fausses requtes DNS en provenance des machines Windows." #: option.c:404 msgid "Don't include IPv4 addresses in DNS answers." msgstr "" #: option.c:405 msgid "Don't include IPv6 addresses in DNS answers." msgstr "" #: option.c:406 msgid "Enable DHCP in the range given with lease duration." msgstr "Autorise DHCP dans la plage d'adresses donne sur la dure de validit du bail." #: option.c:407 #, c-format msgid "Change to this group after startup (defaults to %s)." msgstr "On change pour ce groupe aprs le dmarrage (par dfaut : %s)." #: option.c:408 msgid "Set address or hostname for a specified machine." msgstr "On assigne une adresse ou un nom pour une machine spcifie." #: option.c:409 msgid "Read DHCP host specs from file." msgstr "Lecture des spcifications d'htes DHCP partir du fichier" #: option.c:410 msgid "Read DHCP option specs from file." msgstr "Lecture des options DHCP partir du fichier" #: option.c:411 #, fuzzy msgid "Read DHCP host specs from a directory." msgstr "Lecture des spcifications d'htes DHCP partir du fichier" #: option.c:412 #, fuzzy msgid "Read DHCP options from a directory." msgstr "Lecture des options DHCP partir du fichier" #: option.c:413 msgid "Evaluate conditional tag expression." msgstr "Expression d'valuation conditionnelle d'tiquette" #: option.c:414 #, c-format msgid "Do NOT load %s file." msgstr "Ne charge PAS le fichier %s." #: option.c:415 #, c-format msgid "Specify a hosts file to be read in addition to %s." msgstr "Spcifie un nom de fichier hosts lire en complment de %s" #: option.c:416 #, fuzzy msgid "Read hosts files from a directory." msgstr "Lecture des spcifications d'htes DHCP partir du fichier" #: option.c:417 msgid "Specify interface(s) to listen on." msgstr "Spcifie la ou les interface(s) o le dmon doit se mettre l'coute." #: option.c:418 msgid "Specify interface(s) NOT to listen on." msgstr "Spcifie la ou les interface(s) que le dmon ne doit PAS traiter." # #: option.c:419 msgid "Map DHCP user class to tag." msgstr "Associe les classes d'utilisateurs ('user class') DHCP aux options." #: option.c:420 msgid "Map RFC3046 circuit-id to tag." msgstr "Associe les identifiants de circuits RFC3046 ('circuit-id') aux options" #: option.c:421 msgid "Map RFC3046 remote-id to tag." msgstr "Associe les identifiants distants RFC3046 ('remote-id') aux options" #: option.c:422 msgid "Map RFC3993 subscriber-id to tag." msgstr "Associe les identifiants de souscripteurs RFC3993 ('subscriber-id') aux options" #: option.c:423 msgid "Specify vendor class to match for PXE requests." msgstr "" # #: option.c:424 msgid "Don't do DHCP for hosts with tag set." msgstr "Ne pas autoriser DHCP pour les machines numeres dans les options." # #: option.c:425 msgid "Force broadcast replies for hosts with tag set." msgstr "Forcer les rponses par 'broadcast' pour les machines numeres dans les options." #: option.c:426 msgid "Do NOT fork into the background, do NOT run in debug mode." msgstr "Ne passe pas en tche de fond, ne pas s'excuter en mode debug." #: option.c:427 msgid "Assume we are the only DHCP server on the local network." msgstr "On considre que l'on est le seul serveur DHCP sur le rseau local." #: option.c:428 #, c-format msgid "Specify where to store DHCP leases (defaults to %s)." msgstr "Spcifie o il faut sauvegarder les baux DHCP (par dfaut : %s)." #: option.c:429 msgid "Return MX records for local hosts." msgstr "Retourne les champs MX pour les machines locales." #: option.c:430 msgid "Specify an MX record." msgstr "Spcifie un champ MX." #: option.c:431 msgid "Specify BOOTP options to DHCP server." msgstr "Spcifie les options BOOTP pour le serveur DHCP." #: option.c:432 #, 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 rception du signal SIGHUP." #: option.c:433 msgid "Do NOT cache failed search results." msgstr "Ne place pas en cache le rsultat des requtes qui ont choues." #: option.c:434 msgid "Use expired cache data for faster reply." msgstr "" #: option.c:435 #, 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:436 msgid "Specify options to be sent to DHCP clients." msgstr "Options supplmentaires associer aux clients DHCP." #: option.c:437 msgid "DHCP option sent even if the client does not request it." msgstr "Option DHCP envoye mme si le client de la demande pas." #: option.c:438 msgid "Specify port to listen for DNS requests on (defaults to 53)." msgstr "Spcifie le port o il faut couter les requtes DNS (par dfaut : 53)." #: option.c:439 #, c-format msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)." msgstr "Taille maximale des paquets UDP supports pour EDNS.0 (par dfaut : %s)." # #: option.c:440 msgid "Log DNS queries." msgstr "Enregistre les requtes DNS dans un journal d'activit." # #: option.c:441 msgid "Force the originating port for upstream DNS queries." msgstr "Force le port d'origine pour les requtes vers les serveurs amonts." #: option.c:442 msgid "Set maximum number of random originating ports for a query." msgstr "" #: option.c:443 msgid "Do NOT read resolv.conf." msgstr "Ne pas lire le fichier resolv.conf." #: option.c:444 #, c-format msgid "Specify path to resolv.conf (defaults to %s)." msgstr "Spcifie le chemin pour le fichier resolv.conf (par dfaut : %s)." #: option.c:445 #, fuzzy msgid "Specify path to file with server= options" msgstr "Spcifie un chemin pour le fichier PID (par dfaut : %s)." #: option.c:446 msgid "Specify address(es) of upstream servers with optional domains." msgstr "Spcifie la ou les adresses des serveurs amonts avec des domaines optionels." #: option.c:447 #, fuzzy msgid "Specify address of upstream servers for reverse address queries" msgstr "Spcifie la ou les adresses des serveurs amonts avec des domaines optionels." #: option.c:448 msgid "Never forward queries to specified domains." msgstr "Ne jamais retransmettre les requtes pour les domaines spcifis." #: option.c:449 msgid "Specify the domain to be assigned in DHCP leases." msgstr "Spcifie le domaine qui doit etre assign aux baux DHCP." #: option.c:450 msgid "Specify default target in an MX record." msgstr "Spcifie la cible par dfaut dans un champ MX." #: option.c:451 msgid "Specify time-to-live in seconds for replies from /etc/hosts." msgstr "Spcifie le TTL en secondes pour les rponses qui utilisent /etc/hosts." # #: option.c:452 msgid "Specify time-to-live in seconds for negative caching." msgstr "Spcifie le TTL en secondes pour les rponses qui utilisent /etc/hosts." #: option.c:453 msgid "Specify time-to-live in seconds for maximum TTL to send to clients." msgstr "Spcifie, en secondes, la valeur maximum de TTL renvoyer aux clients." # #: option.c:454 #, fuzzy msgid "Specify time-to-live ceiling for cache." msgstr "Spcifie le TTL en secondes pour les rponses qui utilisent /etc/hosts." # #: option.c:455 #, fuzzy msgid "Specify time-to-live floor for cache." msgstr "Spcifie le TTL en secondes pour les rponses qui utilisent /etc/hosts." #: option.c:456 msgid "Retry DNS queries after this many milliseconds." msgstr "" #: option.c:457 #, c-format msgid "Change to this user after startup. (defaults to %s)." msgstr "Change pour cet utilisateur aprs le dmarrage (par dfaut : %s)." # #: option.c:458 msgid "Map DHCP vendor class to tag." msgstr "Associe les classes de fournisseurs ('vendor class') DHCP aux options." #: option.c:459 msgid "Display dnsmasq version and copyright information." msgstr "Affiche la version de Dnsmasq et les informations lies au copyright." #: option.c:460 msgid "Translate IPv4 addresses from upstream servers." msgstr "Traduit les adresses IPV4 des serveurs amonts." #: option.c:461 msgid "Specify a SRV record." msgstr "Spcifie un champ SRV." #: option.c:462 #, fuzzy msgid "Display this message. Use --help dhcp or --help dhcp6 for known DHCP options." msgstr "Afficher ce message. Utiliser --help dhcp pour obtenir la liste des options DHCP connues." #: option.c:463 #, c-format msgid "Specify path of PID file (defaults to %s)." msgstr "Spcifie un chemin pour le fichier PID (par dfaut : %s)." #: option.c:464 #, c-format msgid "Specify maximum number of DHCP leases (defaults to %s)." msgstr "Spcifie le nombre maximum de baux DHCP (par dfaut : %s)." #: option.c:465 msgid "Answer DNS queries based on the interface a query was sent to." msgstr "Repond aux requtes DNS en se basant sur l'interface ou a t envoye la requte." #: option.c:466 msgid "Specify TXT DNS record." msgstr "Spcifie un champ DNS TXT" # #: option.c:467 msgid "Specify PTR DNS record." msgstr "Spcifie un champ DNS PTR" #: option.c:468 msgid "Give DNS name to IPv4 address of interface." msgstr "Donne le nom DNS pour l'adresse IPv4 de l'interface." #: option.c:469 msgid "Bind only to interfaces in use." msgstr "Association uniquement aux interfaces rseau actuellement actives." #: option.c:470 #, c-format msgid "Read DHCP static host information from %s." msgstr "Lecture des informations de DHCP statique partir de %s." #: option.c:471 msgid "Enable the DBus interface for setting upstream servers, etc." msgstr "Autorise l'interface DBus pour la configuration des serveurs amonts, etc." #: option.c:472 msgid "Enable the UBus interface." msgstr "" #: option.c:473 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:474 msgid "Enable dynamic address allocation for bootp." msgstr "Autorise l'allocation dynamique d'adresse pour bootp." # #: option.c:475 msgid "Map MAC address (with wildcards) to option set." msgstr "Associe l'adresse MAC (avec les jokers) aux options." #: option.c:476 msgid "Treat DHCP requests on aliases as arriving from interface." msgstr "Traiter les requtes DHCP sur les alias comme arrivant de l'interface." #: option.c:477 msgid "Specify extra networks sharing a broadcast domain for DHCP" msgstr "" #: option.c:478 msgid "Disable ICMP echo address checking in the DHCP server." msgstr "Supprime la vrification d'adresse sur le serveur au moyen de paquets ICMP echo" #: option.c:479 msgid "Shell script to run on DHCP lease creation and destruction." msgstr "Script shell excuter lors de la cration ou destruction de bail DHCP." #: option.c:480 msgid "Lua script to run on DHCP lease creation and destruction." msgstr "Script Lua excuter lors de la cration ou destruction de bail DHCP." #: option.c:481 msgid "Run lease-change scripts as this user." msgstr "Lancer le script 'lease-change' avec cet utilisateur." #: option.c:482 msgid "Call dhcp-script with changes to local ARP table." msgstr "" #: option.c:483 msgid "Read configuration from all the files in this directory." msgstr "Lecture de la configuration dans tous les fichiers de ce rpertoire." #: option.c:484 msgid "Execute file and read configuration from stdin." msgstr "" # #: option.c:485 msgid "Log to this syslog facility or file. (defaults to DAEMON)" msgstr "Enregistrer les journaux d'activit dans cette facilit syslog. (dfaut : DAEMON)" #: option.c:486 msgid "Do not use leasefile." msgstr "Ne pas utiliser de fichier de baux." #: option.c:487 #, c-format msgid "Maximum number of concurrent DNS queries. (defaults to %s)" msgstr "Spcifie le nombre maximum de requtes DHCP concurrentes (par dfaut : %s)." #: option.c:488 #, c-format msgid "Clear DNS cache when reloading %s." msgstr "Vider le cache DNS lors du rechargement de %s." #: option.c:489 msgid "Ignore hostnames provided by DHCP clients." msgstr "Ignorer les noms d'htes fournis par les clients DHCP" #: option.c:490 msgid "Do NOT reuse filename and server fields for extra DHCP options." msgstr "Ne pas rutiliser les champs nom de fichier et serveur dans les options DHCP supplmentaires." #: option.c:491 msgid "Enable integrated read-only TFTP server." msgstr "Activer le server TFTP intgr (fonctionnant en lecture seulement)" #: option.c:492 msgid "Export files by TFTP only from the specified subtree." msgstr "N'exporter par TFTP que les fichiers de l'arborescence de fichier spcifie" #: option.c:493 #, fuzzy msgid "Add client IP or hardware address to tftp-root." msgstr "Ajouter les adresses IP clientes la racine tftp ('tftp-root')." #: option.c:494 msgid "Allow access only to files owned by the user running dnsmasq." msgstr "Accs aux seuls fichiers appartenants l'utilisateur sous lequel tourne dnsmasq" #: option.c:495 msgid "Do not terminate the service if TFTP directories are inaccessible." msgstr "" #: option.c:496 #, fuzzy, c-format msgid "Maximum number of concurrent TFTP transfers (defaults to %s)." msgstr "Spcifie le nombre maximum de transfert TFTP concurrents (dfaut : %s)." #: option.c:497 #, fuzzy msgid "Maximum MTU to use for TFTP transfers." msgstr "Spcifie le nombre maximum de transfert TFTP concurrents (dfaut : %s)." #: option.c:498 msgid "Disable the TFTP blocksize extension." msgstr "Dsactivation de l'extension TFTP taille de bloc " #: option.c:499 msgid "Convert TFTP filenames to lowercase" msgstr "Convertis les noms de fichiers TFTP en minuscule" #: option.c:500 msgid "Ephemeral port range for use by TFTP transfers." msgstr "Gamme de ports dans laquelle seront choisis les ports temporaires utiliss dans les transferts TFTP." #: option.c:501 msgid "Use only one port for TFTP server." msgstr "" #: option.c:502 msgid "Extra logging for DHCP." msgstr "Traces supplmentaires pour le DHCP." #: option.c:503 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:504 msgid "Stop DNS rebinding. Filter private IP ranges when resolving." msgstr "Stopper la rassociation DNS ('DNS rebinding'). Filtre les gammes d'adresses IP prives lors de la rsolution." #: option.c:505 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers." msgstr "Autorise la rassociation de 127.0.0/8, pour les serveurs RBL (Realtime Blackhole List)" #: option.c:506 msgid "Inhibit DNS-rebind protection on this domain." msgstr "Dsactive la protection contre les rassociation DNS pour ce domaine" #: option.c:507 msgid "Always perform DNS queries to all servers." msgstr "Toujours effectuer les requtes DNS tous les serveurs." # #: option.c:508 msgid "Set tag if client includes matching option in request." msgstr "Spcifie le label si le client inclus l'option dans la requte." # #: option.c:509 #, fuzzy msgid "Set tag if client provides given name." msgstr "Spcifie le label si le client inclus l'option dans la requte." #: option.c:510 msgid "Use alternative ports for DHCP." msgstr "Utiliser des ports alternatifs pour le DHCP." # #: option.c:511 msgid "Specify NAPTR DNS record." msgstr "Spcifie un champ DNS NAPTR." #: option.c:512 msgid "Specify lowest port available for DNS query transmission." msgstr "Dfinie le plus petit port utilis pour la transmission d'une requte DNS." #: option.c:513 #, fuzzy msgid "Specify highest port available for DNS query transmission." msgstr "Dfinie le plus petit port utilis pour la transmission d'une requte DNS." #: option.c:514 msgid "Use only fully qualified domain names for DHCP clients." msgstr "Utilise seulement les noms de domaine pleinement qualifis pour les clients DHCP." #: option.c:515 msgid "Generate hostnames based on MAC address for nameless clients." msgstr "Gnre les noms d'htes partir de l'adresse MAC pour les clients sans nom." #: option.c:516 msgid "Use these DHCP relays as full proxies." msgstr "Utilise ces relais DHCP en temps que proxy complets." #: option.c:517 msgid "Relay DHCP requests to a remote server" msgstr "Requtes de relais DHCP un serveur distant" #: option.c:518 msgid "Specify alias name for LOCAL DNS name." msgstr "Spcifie un alias pour un nom DNS local." # #: option.c:519 msgid "Prompt to send to PXE clients." msgstr "Invite envoyer aux clients PXE." #: option.c:520 msgid "Boot service for PXE menu." msgstr "Service de dmarrage pour menu PXE." #: option.c:521 msgid "Check configuration syntax." msgstr "vrification de la syntaxe de la configuration." #: option.c:522 msgid "Add requestor's MAC address to forwarded DNS queries." msgstr "Ajoute l'adresse MAC du requteur aux requtes DNS transmises" #: option.c:523 msgid "Strip MAC information from queries." msgstr "" #: option.c:524 #, fuzzy msgid "Add specified IP subnet to forwarded DNS queries." msgstr "Ajoute l'adresse MAC du requteur aux requtes DNS transmises" #: option.c:525 msgid "Strip ECS information from queries." msgstr "" #: option.c:526 #, fuzzy msgid "Add client identification to forwarded DNS queries." msgstr "Ajoute l'adresse MAC du requteur aux requtes DNS transmises" #: option.c:527 msgid "Proxy DNSSEC validation results from upstream nameservers." msgstr "Copie dans la rponse DNS le rsultat de la validation DNSSEC effectue par les serveurs DNS amonts." #: option.c:528 msgid "Attempt to allocate sequential IP addresses to DHCP clients." msgstr "Essaie d'allouer des adresses IP squentielles aux clients DHCP." #: option.c:529 #, fuzzy msgid "Ignore client identifier option sent by DHCP clients." msgstr "Ignorer les noms d'htes fournis par les clients DHCP" #: option.c:530 msgid "Copy connection-track mark from queries to upstream connections." msgstr "Copie les marques de suivi de connexion pour les requtes amont." #: option.c:531 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:532 msgid "Send router-advertisements for interfaces doing DHCPv6" msgstr "Envoyer des annonces de routeurs pour toutes les interfaces faisant du DHCPv6" #: option.c:533 msgid "Specify DUID_EN-type DHCPv6 server DUID" msgstr "Spcifie pour le serveur DHCPv6 un identifiant unique DHCP (DUID) bas sur un numro unique de vendeur (DUID_EN)" #: option.c:534 msgid "Specify host (A/AAAA and PTR) records" msgstr "Spcifie les enregistrements (A/AAAA et PTR) d'un hte." #: option.c:535 msgid "Specify host record in interface subnet" msgstr "" #: option.c:536 msgid "Specify certification authority authorization record" msgstr "" #: option.c:537 msgid "Specify arbitrary DNS resource record" msgstr "Dfinie une resource DNS d'un type spcifique" #: option.c:538 msgid "Bind to interfaces in use - check for new interfaces" msgstr "Se lie aux interfaces prexistantes - vrifie l'apparition de nouvelles interfaces" #: option.c:539 msgid "Export local names to global DNS" msgstr "Exporte les noms locaux dans le DNS global" #: option.c:540 msgid "Domain to export to global DNS" msgstr "Domaine exporter dans le DNS global" #: option.c:541 msgid "Set TTL for authoritative replies" msgstr "Configure la dure de vie (Time To Live) pour les rponses faisant autorit" #: option.c:542 #, fuzzy msgid "Set authoritative zone information" msgstr "Configure les informations pour une zone de nom faisant autorit" #: option.c:543 msgid "Secondary authoritative nameservers for forward domains" msgstr "Serveurs de noms secondaires faisant autorit pour les domaines dlgus" #: option.c:544 msgid "Peers which are allowed to do zone transfer" msgstr "Pairs autoriss faire des transferts de zone" #: option.c:545 msgid "Specify ipsets to which matching domains should be added" msgstr "Spcifie les ipsets auxquels les domaines correspondants doivent-tre ajouts" #: option.c:546 #, fuzzy msgid "Specify nftables sets to which matching domains should be added" msgstr "Spcifie les ipsets auxquels les domaines correspondants doivent-tre ajouts" #: option.c:547 msgid "Enable filtering of DNS queries with connection-track marks." msgstr "" #: option.c:548 msgid "Set allowed DNS patterns for a connection-track mark." msgstr "" #: option.c:549 #, fuzzy msgid "Specify a domain and address range for synthesised names" msgstr "Spcifie un domaine et une plage d'adresses pour les noms auto-gnrs" #: option.c:550 msgid "Activate DNSSEC validation" msgstr "" #: option.c:551 msgid "Specify trust anchor key digest." msgstr "" #: option.c:552 msgid "Disable upstream checking for DNSSEC debugging." msgstr "" #: option.c:553 msgid "Ensure answers without DNSSEC are in unsigned zones." msgstr "" #: option.c:554 msgid "Don't check DNSSEC signature timestamps until first cache-reload" msgstr "" #: option.c:555 msgid "Timestamp file to verify system clock for DNSSEC" msgstr "" #: option.c:556 msgid "Set MTU, priority, resend-interval and router-lifetime" msgstr "" #: option.c:557 msgid "Do not log routine DHCP." msgstr "" #: option.c:558 msgid "Do not log routine DHCPv6." msgstr "" #: option.c:559 msgid "Do not log RA." msgstr "" #: option.c:560 msgid "Log debugging information." msgstr "" #: option.c:561 msgid "Accept queries only from directly-connected networks." msgstr "" #: option.c:562 msgid "Detect and remove DNS forwarding loops." msgstr "" #: option.c:563 msgid "Ignore DNS responses containing ipaddr." msgstr "" #: option.c:564 msgid "Set TTL in DNS responses with DHCP-derived addresses." msgstr "" #: option.c:565 msgid "Delay DHCP replies for at least number of seconds." msgstr "" #: option.c:566 msgid "Enables DHCPv4 Rapid Commit option." msgstr "" #: option.c:567 msgid "Path to debug packet dump file" msgstr "" #: option.c:568 msgid "Mask which packets to dump" msgstr "" #: option.c:569 msgid "Call dhcp-script when lease expiry changes." msgstr "" #: option.c:570 msgid "Send Cisco Umbrella identifiers including remote IP." msgstr "" #: option.c:571 msgid "Do not log routine TFTP." msgstr "" #: option.c:572 msgid "Suppress round-robin ordering of DNS records." msgstr "" #: option.c:802 #, c-format msgid "" "Usage: dnsmasq [options]\n" "\n" msgstr "" "Usage : dnsmasq [options]\n" "\n" #: option.c:804 #, 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:806 #, c-format msgid "Valid options are:\n" msgstr "Les options valides sont :\n" # #: option.c:853 option.c:1055 msgid "bad address" msgstr "mauvaise adresse" #: option.c:882 option.c:886 msgid "bad port" msgstr "numro de port incorrect" #: option.c:899 option.c:1002 option.c:1048 msgid "interface binding not supported" msgstr "association d'interface non supporte" #: option.c:955 msgid "Cannot resolve server name" msgstr "" #: option.c:991 msgid "cannot use IPv4 server address with IPv6 source address" msgstr "" #: option.c:997 option.c:1043 msgid "interface can only be specified once" msgstr "" # #: option.c:1011 option.c:4785 msgid "bad interface name" msgstr "nom d'interface invalide" #: option.c:1037 msgid "cannot use IPv6 server address with IPv4 source address" msgstr "" #: option.c:1124 #, fuzzy msgid "bad IPv4 prefix length" msgstr "mauvais prfixe" #: option.c:1155 option.c:1165 option.c:1240 option.c:1250 option.c:5360 msgid "error" msgstr "erreur" #: option.c:1207 #, fuzzy msgid "bad IPv6 prefix length" msgstr "mauvais prfixe" #: option.c:1467 msgid "inappropriate vendor:" msgstr "" #: option.c:1474 msgid "inappropriate encap:" msgstr "" #: option.c:1500 msgid "unsupported encapsulation for IPv6 option" msgstr "encapsulation d'option non supporte pour IPv6" #: option.c:1514 msgid "bad dhcp-option" msgstr "mauvaise valeur de 'dhcp-option'" # #: option.c:1592 msgid "bad IP address" msgstr "mauvaise adresse IP" # #: option.c:1595 option.c:1734 option.c:3928 msgid "bad IPv6 address" msgstr "mauvaise adresse IPv6" # #: option.c:1688 #, fuzzy msgid "bad IPv4 address" msgstr "mauvaise adresse IPv6" #: option.c:1761 option.c:1856 msgid "bad domain in dhcp-option" msgstr "mauvais domaine dans dhcp-option" #: option.c:1900 msgid "dhcp-option too long" msgstr "dhcp-option trop long" #: option.c:1907 msgid "illegal dhcp-match" msgstr "valeur illgale pour 'dhcp-match'" #: option.c:1966 msgid "illegal repeated flag" msgstr "Une option ne pouvant tre spcifi qu'une seule fois t donne plusieurs fois" #: option.c:1974 msgid "illegal repeated keyword" msgstr "Mot-clef ne pouvant tre rpt" #: option.c:2056 option.c:5533 #, c-format msgid "cannot access directory %s: %s" msgstr "Ne peut pas lire le rpertoire %s : %s" #: option.c:2102 tftp.c:573 dump.c:72 #, c-format msgid "cannot access %s: %s" msgstr "Ne peut pas lire %s : %s" #: option.c:2219 msgid "setting log facility is not possible under Android" msgstr "Sous android, impossible de positionner la cible (facility) pour les traces (logs)." #: option.c:2228 msgid "bad log facility" msgstr "Mauvaise cible (facility) pour les traces." #: option.c:2281 msgid "bad MX preference" msgstr "prference MX incorrecte" #: option.c:2289 msgid "bad MX name" msgstr "nom MX incorrect" #: option.c:2304 msgid "bad MX target" msgstr "valeur MX cible incorrecte" #: option.c:2324 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts" msgstr "recompiler en dfinissant HAVE_SCRIPT pour permettre l'excution de scripts shell au changement de bail (lease-change)" #: option.c:2328 msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts" msgstr "recompiler en dfinissant HAVE_LUASCRIPT pour permettre l'excution de scripts LUA au changement de bail (lease-change)" # #: option.c:2447 #, fuzzy msgid "invalid auth-zone" msgstr "poids invalide" #: option.c:2589 option.c:2621 #, fuzzy msgid "bad prefix length" msgstr "mauvais prfixe" #: option.c:2601 option.c:2642 option.c:2696 msgid "bad prefix" msgstr "mauvais prfixe" #: option.c:2716 #, fuzzy msgid "prefix length too small" msgstr "la taille de prfixe doit tre au minimum 64" #: option.c:3010 #, fuzzy msgid "Bad address in --address" msgstr "adresse dj utilise" #: option.c:3110 msgid "recompile with HAVE_IPSET defined to enable ipset directives" msgstr "recompiler en dfinissant HAVE_IPSET pour permettre l'utilisation de directives de groupes d'IP (IPset)" #: option.c:3117 #, fuzzy msgid "recompile with HAVE_NFTSET defined to enable nftset directives" msgstr "recompiler en dfinissant HAVE_IPSET pour permettre l'utilisation de directives de groupes d'IP (IPset)" #: option.c:3192 option.c:3210 #, fuzzy msgid "recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives" msgstr "recompiler en dfinissant HAVE_IPSET pour permettre l'utilisation de directives de groupes d'IP (IPset)" # #: option.c:3496 msgid "bad port range" msgstr "gamme de ports incorrecte" #: option.c:3522 msgid "bad bridge-interface" msgstr "interface-pont incorrecte" #: option.c:3566 msgid "bad shared-network" msgstr "" #: option.c:3620 msgid "only one tag allowed" msgstr "une seule tiquette est autorise" #: option.c:3641 option.c:3657 option.c:3783 option.c:3791 option.c:3834 msgid "bad dhcp-range" msgstr "plage d'adresses DHCP (dhcp-range) incorrecte" #: option.c:3675 msgid "inconsistent DHCP range" msgstr "plage d'adresses DHCP incohrente" #: option.c:3741 msgid "prefix length must be exactly 64 for RA subnets" msgstr "la taille du prfixe doit tre exactement 64 pour les sous-rseaux d'annonces de routeurs (RA)" #: option.c:3743 msgid "prefix length must be exactly 64 for subnet constructors" msgstr "la taille du prfixe doit tre exactement 64 pour le constructeur de sous-rseaux" #: option.c:3746 msgid "prefix length must be at least 64" msgstr "la taille de prfixe doit tre au minimum 64" #: option.c:3749 msgid "inconsistent DHCPv6 range" msgstr "plage d'adresses DHCPv6 incohrente" #: option.c:3768 msgid "prefix must be zero with \"constructor:\" argument" msgstr "le prfixe doit avoir une taille de 0 lorsque l'argument \"constructor:\" est utilis" #: option.c:3893 option.c:3971 msgid "bad hex constant" msgstr "mauvaise constante hexadecimale" #: option.c:3946 #, fuzzy msgid "bad IPv6 prefix" msgstr "mauvais prfixe" #: option.c:3994 #, c-format msgid "duplicate dhcp-host IP address %s" msgstr "adresse IP dhcp-host duplique dans %s." # #: option.c:4056 msgid "bad DHCP host name" msgstr "nom d'hte DHCP incorrect" #: option.c:4142 msgid "bad tag-if" msgstr "mauvaise tiquette tag-if" #: option.c:4490 option.c:5046 msgid "invalid port number" msgstr "numro de port invalide" # #: option.c:4546 msgid "bad dhcp-proxy address" msgstr "adresse dhcp-proxy incorrecte" #: option.c:4627 msgid "Bad dhcp-relay" msgstr "valeur incorrecte pour le relais DHCP (dhcp-relay)" #: option.c:4671 msgid "bad RA-params" msgstr "" #: option.c:4681 msgid "bad DUID" msgstr "mauvais identifiant unique DHCP (DUID)" #: option.c:4715 #, fuzzy msgid "missing address in alias" msgstr "adresse non valide" # #: option.c:4721 msgid "invalid alias range" msgstr "poids invalide" #: option.c:4770 #, fuzzy msgid "missing address in dynamic host" msgstr "adresse non valide" #: option.c:4785 #, fuzzy msgid "bad dynamic host" msgstr "Ne peut pas lire le rpertoire %s : %s" #: option.c:4803 option.c:4819 msgid "bad CNAME" msgstr "mauvais CNAME" #: option.c:4827 msgid "duplicate CNAME" msgstr "ce CNAME existe dja" # #: option.c:4854 msgid "bad PTR record" msgstr "mauvais champ PTR" # #: option.c:4889 msgid "bad NAPTR record" msgstr "mauvais champ NAPTR" # #: option.c:4925 msgid "bad RR record" msgstr "mauvais enregistrement RR" # #: option.c:4958 #, fuzzy msgid "bad CAA record" msgstr "mauvais enregistrement RR" #: option.c:4987 msgid "bad TXT record" msgstr "champ TXT invalide" #: option.c:5030 msgid "bad SRV record" msgstr "champ SRV invalide" #: option.c:5037 msgid "bad SRV target" msgstr "cible SRV invalide" #: option.c:5056 msgid "invalid priority" msgstr "priorit invalide" #: option.c:5061 msgid "invalid weight" msgstr "poids invalide" # #: option.c:5084 msgid "Bad host-record" msgstr "mauvais champ host-record" #: option.c:5123 msgid "Bad name in host-record" msgstr "mauvais nom dans le champ host-record" #: option.c:5165 msgid "bad value for dnssec-check-unsigned" msgstr "" # #: option.c:5201 #, fuzzy msgid "bad trust anchor" msgstr "gamme de ports incorrecte" #: option.c:5217 msgid "bad HEX in trust anchor" msgstr "" #: option.c:5228 #, fuzzy msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)" msgstr "option non supporte (vrifier que Dnsmasq a t compil avec le support DHCP/TFTP/DBus)" #: option.c:5290 msgid "missing \"" msgstr "il manque \"" #: option.c:5347 msgid "bad option" msgstr "mauvaise option" #: option.c:5349 msgid "extraneous parameter" msgstr "paramtre en trop" #: option.c:5351 msgid "missing parameter" msgstr "paramtre manquant" #: option.c:5353 #, fuzzy msgid "illegal option" msgstr "mauvaise option" #: option.c:5363 #, c-format msgid " in output from %s" msgstr "" #: option.c:5365 #, c-format msgid " at line %d of %s" msgstr " la ligne %d de %s" #: option.c:5380 option.c:5683 option.c:5694 #, c-format msgid "read %s" msgstr "Lecture de %s" #: option.c:5446 #, fuzzy, c-format msgid "cannot execute %s: %s" msgstr "Ne peut pas lire %s : %s" #: option.c:5454 option.c:5615 tftp.c:790 #, c-format msgid "cannot read %s: %s" msgstr "Ne peut pas lire %s : %s" #: option.c:5473 #, fuzzy, c-format msgid "error executing %s: %s" msgstr "impossible d'excuter %s : %s" #: option.c:5476 #, c-format msgid "%s returns non-zero error code" msgstr "" #: option.c:5775 msgid "junk found in command line" msgstr "la ligne de commande contient des lments indsirables ou incomprhensibles" #: option.c:5815 #, c-format msgid "Dnsmasq version %s %s\n" msgstr "Version de Dnsmasq %s %s\n" #: option.c:5816 #, c-format msgid "" "Compile time options: %s\n" "\n" msgstr "" "Options la compilation %s\n" "\n" #: option.c:5817 #, c-format msgid "This software comes with ABSOLUTELY NO WARRANTY.\n" msgstr "Ce logiciel est fourni sans AUCUNE GARANTIE.\n" #: option.c:5818 #, 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:5819 #, 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:5836 msgid "try --help" msgstr "essayez avec --help" #: option.c:5838 msgid "try -w" msgstr "essayez avec -w" #: option.c:5840 #, c-format msgid "bad command line options: %s" msgstr "mauvaises options en ligne de commande : %s." #: option.c:5909 #, c-format msgid "CNAME loop involving %s" msgstr "" #: option.c:5950 #, c-format msgid "cannot get host-name: %s" msgstr "ne peut pas obtenir le nom de la machine : %s" #: option.c:5978 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:5988 msgid "must have exactly one resolv.conf to read domain from." msgstr "un fichier resolv.conf (et un seul) est ncessaire pour y rcuperer le nom de domaine." #: option.c:5991 network.c:1727 dhcp.c:892 #, c-format msgid "failed to read %s: %s" msgstr "impossible de lire %s : %s" #: option.c:6008 #, c-format msgid "no search directive found in %s" msgstr "pas de directive de recherche trouve dans %s" #: option.c:6029 msgid "there must be a default domain when --dhcp-fqdn is set" msgstr "un domaine par dfaut doit tre spcifi lorsque l'option --dhcp-fqdn est utilise" #: option.c:6038 msgid "syntax check OK" msgstr "vrification de syntaxe OK" #: forward.c:107 #, c-format msgid "failed to send packet: %s" msgstr "impossible d'envoyer le paquet : %s" #: forward.c:715 msgid "discarding DNS reply: subnet option mismatch" msgstr "" #: forward.c:780 #, c-format msgid "nameserver %s refused to do a recursive query" msgstr "le serveur de nom %s a refus de faire une recherche rcursive" #: forward.c:826 #, c-format msgid "possible DNS-rebind attack detected: %s" msgstr "dtection d'une possible attaque de type DNS-rebind: %s" #: forward.c:1239 #, c-format msgid "reducing DNS packet size for nameserver %s to %d" msgstr "" #: forward.c:1565 #, c-format msgid "ignoring query from non-local network %s (logged only once)" msgstr "" #: forward.c:2139 #, c-format msgid "ignoring query from non-local network %s" msgstr "" #: forward.c:2501 #, fuzzy, c-format msgid "failed to bind server socket to %s: %s" msgstr "impossible de lier la socket de serveur pour %s : %s" #: forward.c:2867 #, c-format msgid "Maximum number of concurrent DNS queries reached (max: %d)" msgstr "Nombre maximum de requtes DNS concurrentes atteint (maximum : %d)." #: forward.c:2869 #, fuzzy, c-format msgid "Maximum number of concurrent DNS queries to %s reached (max: %d)" msgstr "Nombre maximum de requtes DNS concurrentes atteint (maximum : %d)." #: network.c:700 #, c-format msgid "stopped listening on %s(#%d): %s port %d" msgstr "" #: network.c:911 #, c-format msgid "failed to create listening socket for %s: %s" msgstr "impossible de crer une socket d'coute pour %s : %s" #: network.c:1192 #, c-format msgid "listening on %s(#%d): %s port %d" msgstr "" #: network.c:1219 #, fuzzy, c-format msgid "listening on %s port %d" msgstr "impossible d'envoyer %s %s" #: network.c:1252 #, c-format msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s" msgstr "" #: network.c:1259 msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)" msgstr "" #: network.c:1268 #, fuzzy, c-format msgid "warning: using interface %s instead" msgstr "attention : l'interface %s n'existe pas actuellement" #: network.c:1277 #, fuzzy, c-format msgid "warning: no addresses found for interface %s" msgstr "utilise les adresses locales seulement pour %s %s" #: network.c:1335 #, 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:1340 msgid "try increasing /proc/sys/net/core/optmem_max" msgstr "" #: network.c:1545 #, c-format msgid "failed to bind server socket for %s: %s" msgstr "impossible de lier la socket de serveur pour %s : %s" #: network.c:1622 #, c-format msgid "ignoring nameserver %s - local interface" msgstr "ignore le serveur de nom %s - interface locale" #: network.c:1633 #, 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:1643 msgid "more servers are defined but not logged" msgstr "" #: network.c:1654 msgid "(no DNSSEC)" msgstr "" #: network.c:1657 msgid "unqualified" msgstr "non-qualifi(e)" #: network.c:1657 msgid "names" msgstr "noms" #: network.c:1659 msgid "default" msgstr "dfaut" #: network.c:1661 msgid "domain" msgstr "domaine" #: network.c:1663 #, fuzzy, c-format msgid "using nameserver %s#%d for %s %s%s %s" msgstr "utilise le serveur de nom %s#%d pour %s %s" #: network.c:1667 #, 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:1670 #, c-format msgid "using nameserver %s#%d(via %s)" msgstr "utilise le serveur de nom %s#%d (via %s)" #: network.c:1672 #, c-format msgid "using nameserver %s#%d" msgstr "utilise le serveur de nom %s#%d" #: network.c:1687 #, fuzzy, c-format msgid "using only locally-known addresses for %s" msgstr "utilise les adresses locales seulement pour %s %s" #: network.c:1690 #, fuzzy, c-format msgid "using standard nameservers for %s" msgstr "utilisation des serveurs de nom standards pour %s %s" #: network.c:1694 #, fuzzy, c-format msgid "using %d more local addresses" msgstr "utilise le serveur de nom %s#%d" #: network.c:1696 #, fuzzy, c-format msgid "using %d more nameservers" msgstr "utilise le serveur de nom %s#%d" #: dnsmasq.c:192 msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform" msgstr "" #: dnsmasq.c:207 msgid "no root trust anchor provided for DNSSEC" msgstr "" #: dnsmasq.c:210 msgid "cannot reduce cache size from default when DNSSEC enabled" msgstr "" #: dnsmasq.c:212 #, 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:218 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:225 #, fuzzy msgid "cannot use --conntrack AND --query-port" msgstr "impossible d'utiliser conjointement --conntrack et --query-port" # #: dnsmasq.c:231 #, 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:236 msgid "asynchronous logging is not available under Solaris" msgstr "l'criture de traces en mode asynchrone n'est pas disponible sous Solaris." #: dnsmasq.c:241 msgid "asynchronous logging is not available under Android" msgstr "l'criture de traces en mode asynchrone n'est pas disponible sous Android." #: dnsmasq.c:246 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:251 #, 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:256 #, fuzzy msgid "Ubus not available: set HAVE_UBUS in src/config.h" msgstr "DBus n'est pas disponible : activez HAVE_DBUS dans src/config.h" #: dnsmasq.c:267 msgid "max_port cannot be smaller than min_port" msgstr "" #: dnsmasq.c:271 msgid "port_limit must not be larger than available port range" msgstr "" #: dnsmasq.c:278 msgid "--auth-server required when an auth zone is defined." msgstr "" #: dnsmasq.c:283 msgid "zone serial must be configured in --auth-soa" msgstr "le numro de srie de la zone doit tre configur dans --auth-soa" #: dnsmasq.c:303 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:377 msgid "cannot set --bind-interfaces and --bind-dynamic" msgstr "--bind-interfaces et --bind-dynamic sont mutuellement exclusives" #: dnsmasq.c:380 #, c-format msgid "failed to find list of interfaces: %s" msgstr "impossible de trouver la liste des interfaces : %s" #: dnsmasq.c:389 #, c-format msgid "unknown interface %s" msgstr "interface %s inconnue" #: dnsmasq.c:396 #, c-format msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s" msgstr "impossible de dclarer SO_BINDTODEVICE sur la socket DHCP : %s" #: dnsmasq.c:440 #, fuzzy msgid "Packet dumps not available: set HAVE_DUMP in src/config.h" msgstr "DBus n'est pas disponible : activez HAVE_DBUS dans src/config.h" #: dnsmasq.c:448 dnsmasq.c:1232 #, c-format msgid "DBus error: %s" msgstr "Erreur DBus : %s" #: dnsmasq.c:451 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:459 dnsmasq.c:1253 #, fuzzy, c-format msgid "UBus error: %s" msgstr "Erreur DBus : %s" #: dnsmasq.c:462 #, fuzzy msgid "UBus not available: set HAVE_UBUS in src/config.h" msgstr "DBus n'est pas disponible : activez HAVE_DBUS dans src/config.h" #: dnsmasq.c:492 #, c-format msgid "unknown user or group: %s" msgstr "utilisateur ou groupe inconnu : %s" #: dnsmasq.c:568 #, c-format msgid "process is missing required capability %s" msgstr "" #: dnsmasq.c:600 #, c-format msgid "cannot chdir to filesystem root: %s" msgstr "Ne peut effectuer un 'chdir' la racine du systme de fichier : %s" #: dnsmasq.c:852 #, c-format msgid "started, version %s DNS disabled" msgstr "dmarrage avec le DNS dsactiv (version %s)" #: dnsmasq.c:857 #, c-format msgid "started, version %s cachesize %d" msgstr "demarr, version %s (taille de cache %d)" #: dnsmasq.c:859 msgid "cache size greater than 10000 may cause performance issues, and is unlikely to be useful." msgstr "" #: dnsmasq.c:862 #, c-format msgid "started, version %s cache disabled" msgstr "dmarrage avec le cache dsactiv (version %s)" #: dnsmasq.c:865 msgid "DNS service limited to local subnets" msgstr "" #: dnsmasq.c:868 #, c-format msgid "compile time options: %s" msgstr "options la compilation : %s" #: dnsmasq.c:877 msgid "DBus support enabled: connected to system bus" msgstr "Support DBus autoris : connect au bus systme" #: dnsmasq.c:879 msgid "DBus support enabled: bus connection pending" msgstr "Support DBus autoris : connexion au bus en attente" #: dnsmasq.c:887 #, fuzzy msgid "UBus support enabled: connected to system bus" msgstr "Support DBus autoris : connect au bus systme" #: dnsmasq.c:889 #, fuzzy msgid "UBus support enabled: bus connection pending" msgstr "Support DBus autoris : connexion au bus en attente" #: dnsmasq.c:909 msgid "DNSSEC validation enabled but all unsigned answers are trusted" msgstr "" #: dnsmasq.c:911 msgid "DNSSEC validation enabled" msgstr "" #: dnsmasq.c:915 msgid "DNSSEC signature timestamps not checked until receipt of SIGINT" msgstr "" #: dnsmasq.c:918 msgid "DNSSEC signature timestamps not checked until system time valid" msgstr "" #: dnsmasq.c:921 #, c-format msgid "configured with trust anchor for %s keytag %u" msgstr "" #: dnsmasq.c:927 #, c-format msgid "warning: failed to change owner of %s: %s" msgstr "Impossible de changer pour l'utilisateur %s : %s" #: dnsmasq.c:932 msgid "setting --bind-interfaces option because of OS limitations" msgstr "active l'option --bind-interfaces cause de limitations dans le systme d'exploitation" #: dnsmasq.c:945 #, c-format msgid "warning: interface %s does not currently exist" msgstr "attention : l'interface %s n'existe pas actuellement" #: dnsmasq.c:950 msgid "warning: ignoring resolv-file flag because no-resolv is set" msgstr "attention : l'option resolv-file sera ignore car no-resolv a t spcifi" # #: dnsmasq.c:953 msgid "warning: no upstream servers configured" msgstr "attention : aucun serveur amont n'est configur" #: dnsmasq.c:957 #, 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:978 msgid "IPv6 router advertisement enabled" msgstr "annonces de routeur IPv6 actives" #: dnsmasq.c:983 #, c-format msgid "DHCP, sockets bound exclusively to interface %s" msgstr "" #: dnsmasq.c:1000 msgid "root is " msgstr "root est" # #: dnsmasq.c:1000 msgid "enabled" msgstr "activ" #: dnsmasq.c:1002 msgid "secure mode" msgstr "mode scuris" #: dnsmasq.c:1003 #, fuzzy msgid "single port mode" msgstr "numro de port invalide" #: dnsmasq.c:1006 #, c-format msgid "warning: %s inaccessible" msgstr "" #: dnsmasq.c:1010 #, fuzzy, c-format msgid "warning: TFTP directory %s inaccessible" msgstr "rpertoire TFTP %s inaccessible : %s" #: dnsmasq.c:1036 #, c-format msgid "restricting maximum simultaneous TFTP transfers to %d" msgstr "le nombre maximum de transferts TFTP simultans sera restreint %d" #: dnsmasq.c:1095 #, c-format msgid "error binding DHCP socket to device %s" msgstr "" #: dnsmasq.c:1229 msgid "connected to system DBus" msgstr "connect au systeme DBus" #: dnsmasq.c:1250 #, fuzzy msgid "connected to system UBus" msgstr "connect au systeme DBus" #: dnsmasq.c:1416 #, c-format msgid "cannot fork into background: %s" msgstr "Ne peut se lancer en tche de fond : %s" #: dnsmasq.c:1420 #, c-format msgid "failed to create helper: %s" msgstr "impossible de crer le 'helper' : %s" #: dnsmasq.c:1424 #, c-format msgid "setting capabilities failed: %s" msgstr "impossible de configurer la capacit %s" #: dnsmasq.c:1428 #, c-format msgid "failed to change user-id to %s: %s" msgstr "Impossible de changer l'identifiant utilisateur pour %s : %s" #: dnsmasq.c:1432 #, c-format msgid "failed to change group-id to %s: %s" msgstr "Impossible de changer l'identifiant de groupe pour %s : %s" #: dnsmasq.c:1436 #, c-format msgid "failed to open pidfile %s: %s" msgstr "impossible de lire le fichier de PID %s : %s" #: dnsmasq.c:1440 #, c-format msgid "cannot open log %s: %s" msgstr "Ne peut ouvrir le fichier de log %s : %s" # #: dnsmasq.c:1444 #, c-format msgid "failed to load Lua script: %s" msgstr "impossible de charger le script Lua : %s" #: dnsmasq.c:1448 #, c-format msgid "TFTP directory %s inaccessible: %s" msgstr "rpertoire TFTP %s inaccessible : %s" #: dnsmasq.c:1452 #, fuzzy, c-format msgid "cannot create timestamp file %s: %s" msgstr "ne peut ouvrir ou crer le fichiers de baux %s : %s" #: dnsmasq.c:1536 #, c-format msgid "script process killed by signal %d" msgstr "Le script a t termin par le signal %d" #: dnsmasq.c:1540 #, c-format msgid "script process exited with status %d" msgstr "Le script s'est termin avec le statut %d" #: dnsmasq.c:1544 #, c-format msgid "failed to execute %s: %s" msgstr "impossible d'excuter %s : %s" #: dnsmasq.c:1584 msgid "now checking DNSSEC signature timestamps" msgstr "" #: dnsmasq.c:1619 dnssec.c:160 dnssec.c:204 #, fuzzy, c-format msgid "failed to update mtime on %s: %s" msgstr "impossible de lire le fichier de PID %s : %s" #: dnsmasq.c:1631 msgid "exiting on receipt of SIGTERM" msgstr "sortie sur rception du signal SIGTERM" #: dnsmasq.c:1659 #, c-format msgid "failed to access %s: %s" msgstr "impossible d'accder %s : %s" #: dnsmasq.c:1690 #, c-format msgid "reading %s" msgstr "Lecture de %s" #: dnsmasq.c:1706 #, c-format msgid "no servers found in %s, will retry" msgstr "aucun serveur trouv dans %s, va ressayer" #: dhcp.c:51 #, c-format msgid "cannot create DHCP socket: %s" msgstr "ne peut crer la socket DHCP: %s" #: dhcp.c:66 #, c-format msgid "failed to set options on DHCP socket: %s" msgstr "impossible d'appliquer les options sur la socket DHCP : %s" #: dhcp.c:87 #, c-format msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s" msgstr "impossible de dclarer SO_REUSE{ADDR|PORT} sur la socket DHCP : %s" #: dhcp.c:99 #, c-format msgid "failed to bind DHCP server socket: %s" msgstr "impossible de lier la socket serveur DHCP : %s" #: dhcp.c:125 #, c-format msgid "cannot create ICMP raw socket: %s." msgstr "ne peut crer de socket en mode raw pour ICMP : %s." #: dhcp.c:254 dhcp6.c:186 #, c-format msgid "unknown interface %s in bridge-interface" msgstr "interface %s inconnue spcifie comme interface de pont" #: dhcp.c:295 #, c-format msgid "DHCP packet received on %s which has no address" msgstr "Paquet DHCP reu sur %s qui n'a pas d'adresse" #: dhcp.c:429 #, c-format msgid "ARP-cache injection failed: %s" msgstr "" #: dhcp.c:490 #, c-format msgid "Error sending DHCP packet to %s: %s" msgstr "" #: dhcp.c:547 #, c-format msgid "DHCP range %s -- %s is not consistent with netmask %s" msgstr "La plage d'adresses DHCP %s -- %s n'est pas cohrente avec le masque de rseau %s" #: dhcp.c:930 #, c-format msgid "bad line at %s line %d" msgstr "mauvaise ligne dans %s ligne %d" #: dhcp.c:973 #, 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:1034 #, c-format msgid "read %s - %d addresses" msgstr "lecture %s - %d adresses" #: dhcp.c:1136 #, fuzzy, c-format msgid "Cannot broadcast DHCP relay via interface %s" msgstr "Impossible de faire du multicast au server DHCPv6 sans interface valide" #: dhcp.c:1160 #, c-format msgid "broadcast via %s" msgstr "" #: dhcp.c:1163 rfc3315.c:2219 #, fuzzy, c-format msgid "DHCP relay at %s -> %s" msgstr "Relais DHCP %s -> %s" #: lease.c:64 #, c-format msgid "ignoring invalid line in lease database: %s %s %s %s ..." msgstr "" #: lease.c:101 #, c-format msgid "ignoring invalid line in lease database, bad address: %s" msgstr "" #: lease.c:108 msgid "too many stored leases" msgstr "beaucoup trop de baux enregistrs" #: lease.c:176 #, c-format msgid "cannot open or create lease file %s: %s" msgstr "ne peut ouvrir ou crer le fichiers de baux %s : %s" #: lease.c:185 #, fuzzy msgid "failed to parse lease database cleanly" msgstr "impossible de lire %s : %s" #: lease.c:188 #, fuzzy, c-format msgid "failed to read lease file %s: %s" msgstr "impossible de lire %s : %s" #: lease.c:204 #, c-format msgid "cannot run lease-init script %s: %s" msgstr "Ne peut pas excuter le script lease-init %s : %s" #: lease.c:210 #, c-format msgid "lease-init script returned exit code %s" msgstr "le script lease-init a retourn le code %s" #: lease.c:381 #, fuzzy, c-format msgid "failed to write %s: %s (retry in %u s)" msgstr "impossible de lire %s : %s (prochain essai dans %us)" #: lease.c:955 #, c-format msgid "Ignoring domain %s for DHCP host name %s" msgstr "Le domaine %s est ignor pour l'hte DHCP %s" #: rfc2131.c:378 msgid "with subnet selector" msgstr "avec slecteur de sous-reseau" #: rfc2131.c:383 msgid "via" msgstr "par l'intermdiaire de" #: rfc2131.c:389 #, c-format msgid "no address range available for DHCP request %s %s" msgstr "pas de plage d'adresse disponible pour la requte DHCP %s %s" #: rfc2131.c:403 #, c-format msgid "%u available DHCP subnet: %s/%s" msgstr "%u sous-rseaux DHCP disponibles : %s/%s" #: rfc2131.c:409 rfc3315.c:320 #, c-format msgid "%u available DHCP range: %s -- %s" msgstr "%u la gamme DHCP disponible est : %s -- %s" #: rfc2131.c:521 #, c-format msgid "%u vendor class: %s" msgstr "%u Classe de vendeur ('Vendor Class') : %s" #: rfc2131.c:523 #, c-format msgid "%u user class: %s" msgstr "%u Classe d'utilisateur : %s" #: rfc2131.c:557 msgid "disabled" msgstr "dsactiv" #: rfc2131.c:598 rfc2131.c:1087 rfc2131.c:1536 rfc3315.c:633 rfc3315.c:816 #: rfc3315.c:1122 msgid "ignored" msgstr "ignor" #: rfc2131.c:613 rfc2131.c:1340 rfc3315.c:868 msgid "address in use" msgstr "adresse dj utilise" #: rfc2131.c:627 rfc2131.c:1141 msgid "no address available" msgstr "pas d'adresse disponible" #: rfc2131.c:634 rfc2131.c:1302 msgid "wrong network" msgstr "mauvais rseau" #: rfc2131.c:649 msgid "no address configured" msgstr "pas d'adresse configure" #: rfc2131.c:655 rfc2131.c:1353 msgid "no leases left" msgstr "plus aucun bail disponible" #: rfc2131.c:756 rfc3315.c:500 #, c-format msgid "%u client provides name: %s" msgstr "le client %u fourni le nom : %s" #: rfc2131.c:885 msgid "PXE BIS not supported" msgstr "Service PXE BIS (Boot Integrity Services) non support" #: rfc2131.c:1054 rfc3315.c:1223 #, c-format msgid "disabling DHCP static address %s for %s" msgstr "dsactive l'adresse statique DHCP %s pour %s" #: rfc2131.c:1075 msgid "unknown lease" msgstr "bail inconnu" #: rfc2131.c:1110 #, c-format msgid "not using configured address %s because it is leased to %s" msgstr "L'adresse statique %s ne sera pas utilise car un bail est dj attribu %s" #: rfc2131.c:1120 #, 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 utilise car elle est utilise par le serveur ou un relai" #: rfc2131.c:1123 #, c-format msgid "not using configured address %s because it was previously declined" msgstr "L'adresse statique %s ne sera pas utilise car elle a pralablement t refuse" #: rfc2131.c:1139 rfc2131.c:1346 msgid "no unique-id" msgstr "pas d'identifiant unique" #: rfc2131.c:1238 msgid "wrong server-ID" msgstr "mauvais identifiant de serveur" #: rfc2131.c:1257 msgid "wrong address" msgstr "mauvaise adresse" #: rfc2131.c:1275 rfc3315.c:976 msgid "lease not found" msgstr "bail non trouv" #: rfc2131.c:1310 msgid "address not available" msgstr "adresse non disponible" #: rfc2131.c:1321 msgid "static lease available" msgstr "bail statique disponible" #: rfc2131.c:1325 msgid "address reserved" msgstr "adresse reserve" #: rfc2131.c:1334 #, c-format msgid "abandoning lease to %s of %s" msgstr "abandon du bail de %s pour %s" #: rfc2131.c:1870 #, c-format msgid "%u bootfile name: %s" msgstr "%u nom de fichier 'bootfile' : %s" #: rfc2131.c:1879 #, c-format msgid "%u server name: %s" msgstr "%u nom du serveur : %s" #: rfc2131.c:1889 #, c-format msgid "%u next server: %s" msgstr "%u serveur suivant : %s" #: rfc2131.c:1893 #, c-format msgid "%u broadcast response" msgstr "%u rponse broadcast" #: rfc2131.c:1956 #, 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:2267 msgid "PXE menu too large" msgstr "menu PXE trop grand" #: rfc2131.c:2430 rfc3315.c:1517 #, c-format msgid "%u requested options: %s" msgstr "%u options demandes : %s" #: rfc2131.c:2747 #, 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 numro d'entreprise %d" #: rfc2131.c:2810 #, c-format msgid "%u reply delay: %d" msgstr "" #: netlink.c:86 #, c-format msgid "cannot create netlink socket: %s" msgstr "ne peux lier une socket netlink : %s" #: netlink.c:379 #, c-format msgid "netlink returns error: %s" msgstr "Erreur netlink : %s" #: dbus.c:491 #, c-format msgid "Enabling --%s option from D-Bus" msgstr "" #: dbus.c:496 #, c-format msgid "Disabling --%s option from D-Bus" msgstr "" #: dbus.c:857 msgid "setting upstream servers from DBus" msgstr "configuration des serveurs amonts partir de DBus" #: dbus.c:907 msgid "could not register a DBus message handler" msgstr "ne peut enregistrer une routine de traitement des messages DBus" #: bpf.c:261 #, c-format msgid "cannot create DHCP BPF socket: %s" msgstr "impossible de crer une socket BPF pour DHCP : %s" #: bpf.c:289 #, c-format msgid "DHCP request for unsupported hardware type (%d) received on %s" msgstr "requte DHCP pour un type de matriel non support (%d) reue sur %s" #: bpf.c:374 #, fuzzy, c-format msgid "cannot create PF_ROUTE socket: %s" msgstr "ne peut crer la socket DHCP: %s" #: bpf.c:395 msgid "Unknown protocol version from route socket" msgstr "" #: helper.c:150 msgid "lease() function missing in Lua script" msgstr "la fonction lease() est absente du script Lua" #: tftp.c:353 msgid "unable to get free port for TFTP" msgstr "impossible d'obtenir un port libre pour TFTP" #: tftp.c:369 #, c-format msgid "unsupported request from %s" msgstr "requte de %s non supporte" #: tftp.c:520 #, c-format msgid "file %s not found for %s" msgstr "fichier %s non trouv pour %s" #: tftp.c:609 #, c-format msgid "ignoring packet from %s (TID mismatch)" msgstr "" #: tftp.c:662 #, c-format msgid "failed sending %s to %s" msgstr "impossible d'envoyer %s %s" #: tftp.c:662 #, c-format msgid "sent %s to %s" msgstr "envoy %s %s" #: tftp.c:712 #, c-format msgid "error %d %s received from %s" msgstr "erreur %d %s reu de %s" #: log.c:203 #, c-format msgid "overflow: %d log entries lost" msgstr "dbordement : %d traces perdues" #: log.c:281 #, c-format msgid "log failed: %s" msgstr "trace perdue : %s" #: log.c:490 msgid "FAILED to start up" msgstr "IMPOSSIBLE de dmarrer" #: conntrack.c:63 #, c-format msgid "Conntrack connection mark retrieval failed: %s" msgstr "La rcupration de la marque de suivi de connexion a chou : %s" #: dhcp6.c:51 #, c-format msgid "cannot create DHCPv6 socket: %s" msgstr "ne peut crer la socket DHCPv6: %s" #: dhcp6.c:72 #, c-format msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s" msgstr "impossible de dclarer SO_REUSE{ADDR|PORT} sur la socket DHCPv6 : %s" #: dhcp6.c:84 #, c-format msgid "failed to bind DHCPv6 server socket: %s" msgstr "impossible de lier la socket serveur DHCPv6 : %s" #: rfc3315.c:173 #, c-format msgid "no address range available for DHCPv6 request from relay at %s" msgstr "pas de plage d'adresse disponible pour la requte DHCPv6 transmise via le relai %s" #: rfc3315.c:182 #, c-format msgid "no address range available for DHCPv6 request via %s" msgstr "pas de plage d'adresse disponible pour la requte DHCPv6 via %s" #: rfc3315.c:317 #, c-format msgid "%u available DHCPv6 subnet: %s/%d" msgstr "%u sous-rseaux DHCPv6 disponibles : %s/%d" #: rfc3315.c:400 #, c-format msgid "%u vendor class: %u" msgstr "%u Classe de vendeur ('Vendor Class') : %u" #: rfc3315.c:448 #, c-format msgid "%u client MAC address: %s" msgstr "%u MAC adresse du client : %s" #: rfc3315.c:763 rfc3315.c:860 msgid "address unavailable" msgstr "adresse non disponible" #: rfc3315.c:775 rfc3315.c:904 rfc3315.c:1273 msgid "success" msgstr "russi" #: rfc3315.c:790 rfc3315.c:799 rfc3315.c:912 rfc3315.c:914 rfc3315.c:1048 msgid "no addresses available" msgstr "pas d'adresse disponible" #: rfc3315.c:891 msgid "not on link" msgstr "pas sur ce lien" #: rfc3315.c:980 rfc3315.c:1181 rfc3315.c:1262 msgid "no binding found" msgstr "aucune liaison trouve" #: rfc3315.c:1017 msgid "deprecated" msgstr "obsolte" #: rfc3315.c:1024 msgid "address invalid" msgstr "adresse non valide" #: rfc3315.c:1082 rfc3315.c:1084 msgid "confirm failed" msgstr "confirmation d'chec" #: rfc3315.c:1099 msgid "all addresses still on link" msgstr "toutes les adresses sont toujours sur le lien" #: rfc3315.c:1190 msgid "release received" msgstr "libration reue" #: rfc3315.c:2200 #, fuzzy, c-format msgid "Cannot multicast DHCP relay via interface %s" msgstr "Impossible de faire du multicast au server DHCPv6 sans interface valide" #: rfc3315.c:2216 #, c-format msgid "multicast via %s" msgstr "" #: dhcp-common.c:187 #, c-format msgid "Ignoring duplicate dhcp-option %d" msgstr "L'option dhcp-option redondante %d sera ignore" #: dhcp-common.c:264 #, c-format msgid "%u tags: %s" msgstr "%u options: %s" #: dhcp-common.c:484 #, 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'hte, utilisation de %s pour le DHCP." #: dhcp-common.c:518 #, c-format msgid "duplicate IP address %s (%s) in dhcp-config directive" msgstr "adresse IP %s (%s) duplique dans la directive dhcp-config." #: dhcp-common.c:738 #, c-format msgid "Known DHCP options:\n" msgstr "Options DHCP connues :\n" #: dhcp-common.c:749 #, c-format msgid "Known DHCPv6 options:\n" msgstr "Options DHCPv6 connues :\n" #: dhcp-common.c:946 msgid ", prefix deprecated" msgstr ", prfixe obsolte" #: dhcp-common.c:949 #, c-format msgid ", lease time " msgstr ", dure de bail " #: dhcp-common.c:991 #, c-format msgid "%s stateless on %s%.0s%.0s%s" msgstr "%s sans tat (stateless) sur %s%.0s%.0s%s" #: dhcp-common.c:993 #, 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:995 #, c-format msgid "%s, proxy on subnet %.0s%s%.0s%.0s" msgstr "%s, proxy sur le sous-rseau %.0s%s%.0s" #: dhcp-common.c:996 #, c-format msgid "%s, IP range %s -- %s%s%.0s" msgstr "%s, plage d'adresses IP %s -- %s%s%.0s" #: dhcp-common.c:1009 #, c-format msgid "DHCPv4-derived IPv6 names on %s%s" msgstr "noms IPv6 drivs de DHCPv4 sur %s%s" #: dhcp-common.c:1012 #, c-format msgid "router advertisement on %s%s" msgstr "annonces de routeurs sur %s%s" #: dhcp-common.c:1043 #, fuzzy, c-format msgid "DHCP relay from %s via %s" msgstr "Relais DHCP de %s %s" #: dhcp-common.c:1045 #, c-format msgid "DHCP relay from %s to %s via %s" msgstr "Relais DHCP de %s %s via %s" #: dhcp-common.c:1048 #, c-format msgid "DHCP relay from %s to %s" msgstr "Relais DHCP de %s %s" #: radv.c:110 #, c-format msgid "cannot create ICMPv6 socket: %s" msgstr "ne peut crer la socket ICMPv6: %s" #: auth.c:462 #, c-format msgid "ignoring zone transfer request from %s" msgstr "la requte de transfert de zone en provenance de %s est ignore" #: ipset.c:99 #, c-format msgid "failed to create IPset control socket: %s" msgstr "impossible de crer une socket de contrle IPset : %s" #: ipset.c:211 #, fuzzy, c-format msgid "failed to update ipset %s: %s" msgstr "impossible de lire le fichier de PID %s : %s" #: pattern.c:29 #, c-format msgid "[pattern.c:%d] Assertion failure: %s" msgstr "" #: pattern.c:142 #, c-format msgid "Invalid DNS name: Invalid character %c." msgstr "" #: pattern.c:151 msgid "Invalid DNS name: Empty label." msgstr "" #: pattern.c:156 msgid "Invalid DNS name: Label starts with hyphen." msgstr "" #: pattern.c:170 msgid "Invalid DNS name: Label ends with hyphen." msgstr "" #: pattern.c:176 #, c-format msgid "Invalid DNS name: Label is too long (%zu)." msgstr "" #: pattern.c:184 #, c-format msgid "Invalid DNS name: Not enough labels (%zu)." msgstr "" #: pattern.c:189 msgid "Invalid DNS name: Final label is fully numeric." msgstr "" #: pattern.c:199 msgid "Invalid DNS name: \"local\" pseudo-TLD." msgstr "" #: pattern.c:204 #, c-format msgid "DNS name has invalid length (%zu)." msgstr "" #: pattern.c:258 #, c-format msgid "Invalid DNS name pattern: Invalid character %c." msgstr "" #: pattern.c:267 msgid "Invalid DNS name pattern: Empty label." msgstr "" #: pattern.c:272 msgid "Invalid DNS name pattern: Label starts with hyphen." msgstr "" #: pattern.c:285 msgid "Invalid DNS name pattern: Wildcard character used more than twice per label." msgstr "" #: pattern.c:295 msgid "Invalid DNS name pattern: Label ends with hyphen." msgstr "" #: pattern.c:301 #, c-format msgid "Invalid DNS name pattern: Label is too long (%zu)." msgstr "" #: pattern.c:309 #, c-format msgid "Invalid DNS name pattern: Not enough labels (%zu)." msgstr "" #: pattern.c:314 msgid "Invalid DNS name pattern: Wildcard within final two labels." msgstr "" #: pattern.c:319 msgid "Invalid DNS name pattern: Final label is fully numeric." msgstr "" #: pattern.c:329 msgid "Invalid DNS name pattern: \"local\" pseudo-TLD." msgstr "" #: pattern.c:334 #, c-format msgid "DNS name pattern has invalid length after removing wildcards (%zu)." msgstr "" #: dnssec.c:206 msgid "system time considered valid, now checking DNSSEC signature timestamps." msgstr "" #: dnssec.c:1018 #, c-format msgid "Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support" msgstr "" #: blockdata.c:55 #, c-format msgid "pool memory in use %zu, max %zu, allocated %zu" msgstr "" #: tables.c:61 #, fuzzy, c-format msgid "failed to access pf devices: %s" msgstr "impossible d'accder %s : %s" #: tables.c:74 #, fuzzy, c-format msgid "warning: no opened pf devices %s" msgstr "utilise les adresses locales seulement pour %s %s" #: tables.c:82 #, fuzzy, c-format msgid "error: cannot use table name %s" msgstr "ne peut pas obtenir le nom de la machine : %s" #: tables.c:90 #, c-format msgid "error: cannot strlcpy table name %s" msgstr "" #: tables.c:101 #, fuzzy, c-format msgid "IPset: error: %s" msgstr "Erreur DBus : %s" #: tables.c:108 msgid "info: table created" msgstr "" #: tables.c:133 #, c-format msgid "warning: DIOCR%sADDRS: %s" msgstr "" # #: tables.c:137 #, fuzzy, c-format msgid "%d addresses %s" msgstr "mauvaise adresse" #: inotify.c:62 #, fuzzy, c-format msgid "cannot access path %s: %s" msgstr "Ne peut pas lire %s : %s" #: inotify.c:95 #, fuzzy, c-format msgid "failed to create inotify: %s" msgstr "impossible de crer le 'helper' : %s" #: inotify.c:111 #, c-format msgid "too many symlinks following %s" msgstr "" #: inotify.c:127 #, c-format msgid "directory %s for resolv-file is missing, cannot poll" msgstr "" #: inotify.c:131 inotify.c:200 #, fuzzy, c-format msgid "failed to create inotify for %s: %s" msgstr "impossible de crer une socket d'coute pour %s : %s" #: inotify.c:178 inotify.c:185 #, fuzzy, c-format msgid "bad dynamic directory %s: %s" msgstr "Ne peut pas lire le rpertoire %s : %s" #: inotify.c:186 #, fuzzy msgid "not a directory" msgstr "Ne peut pas lire le rpertoire %s : %s" #: inotify.c:299 #, c-format msgid "inotify: %s removed" msgstr "" #: inotify.c:301 #, c-format msgid "inotify: %s new or modified" msgstr "" #: inotify.c:309 #, c-format msgid "inotify: flushed %u names read from %s" msgstr "" #: dump.c:68 #, fuzzy, c-format msgid "cannot create %s: %s" msgstr "Ne peut pas lire %s : %s" #: dump.c:74 #, fuzzy, c-format msgid "bad header in %s" msgstr "adresse dj utilise" #: dump.c:287 #, fuzzy msgid "failed to write packet dump" msgstr "impossible d'envoyer le paquet : %s" #: dump.c:289 #, c-format msgid "%u dumping packet %u mask 0x%04x" msgstr "" #: dump.c:291 #, c-format msgid "dumping packet %u mask 0x%04x" msgstr "" #: ubus.c:79 #, c-format msgid "UBus subscription callback: %s subscriber(s)" msgstr "" #: ubus.c:99 #, fuzzy, c-format msgid "Cannot reconnect to UBus: %s" msgstr "Ne peut ouvrir le fichier de log %s : %s" #: ubus.c:135 msgid "Cannot set UBus listeners: no connection" msgstr "" #: ubus.c:155 msgid "Cannot poll UBus listeners: no connection" msgstr "" #: ubus.c:168 msgid "Disconnecting from UBus" msgstr "" #: ubus.c:179 ubus.c:326 #, c-format msgid "UBus command failed: %d (%s)" msgstr "" #: hash-questions.c:40 msgid "Failed to create SHA-256 hash object" msgstr "" #: nftset.c:35 #, fuzzy msgid "failed to create nftset context" msgstr "impossible de crer une socket de contrle IPset : %s" #, fuzzy #~ msgid "bad IPv4 prefix" #~ msgstr "mauvais prfixe" #, fuzzy #~ msgid "Cannot add object to UBus: %s" #~ msgstr "Ne peut ouvrir le fichier de log %s : %s" #, fuzzy #~ msgid "Failed to send UBus event: %s" #~ msgstr "impossible d'envoyer le paquet : %s" #~ msgid "Specify DHCPv6 prefix class" #~ msgstr "Spcifie le prfixe de classe DHCPv6" #~ msgid "cannot run scripts under uClinux" #~ msgstr "ne peut excuter de script sous uClinux" #~ msgid "cannot match tags in --dhcp-host" #~ msgstr "L'utilisation de labels est prohibe dans --dhcp-host" #~ 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" #~ msgid "unknown prefix-class %d" #~ msgstr "prfixe de classe inconnu %d" #, fuzzy #~ msgid "cannot cannonicalise resolv-file %s: %s" #~ msgstr "ne peut ouvrir ou crer le fichiers de baux %s : %s" #~ msgid "Always send frequent router-advertisements" #~ msgstr "Envoyer des annonces de routeurs frquentes" #~ 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 duplique dans la directive dhcp-config." #~ msgid "Specify path to Lua script (no default)." #~ msgstr "Spcifie un chemin pour le fichier PID (par dfaut : %s)." # #~ msgid "only one dhcp-hostsfile allowed" #~ msgstr "une seule valeur est autorise pour 'dhcp-hostsfile'" # #~ msgid "only one dhcp-optsfile allowed" #~ msgstr "une seule valeur est autorise pour 'dhcp-optsfile'" #~ msgid "files nested too deep in %s" #~ msgstr "trop de niveaux de rcursion pour les fichiers dans %s" #~ msgid "TXT record string too long" #~ msgstr "chane du champ TXT trop longue" #~ msgid "failed to set IPV6 options on listening socket: %s" #~ msgstr "impossible d'activer les options IPV6 sur la socket de lecture : %s" #~ msgid "failed to bind listening socket for %s: %s" #~ msgstr "impossible de lier la socket de lecture pour %s : %s" #~ msgid "DHCP packet: transaction-id is %u" #~ msgstr "paquet DHCP : l'identifiant de transaction ('transaction-id') est %u" #~ msgid "failed to read %s:%s" #~ msgstr "impossible de lire %s : %s" #~ msgid "must set exactly one interface on broken systems without IP_RECVIF" #~ msgstr "Une interface et une seule doit tre dclare sur les systmes sans IP_RECVIF" #~ msgid "Ignoring DHCP lease for %s because it has an illegal domain part" #~ msgstr "On ignore le bail DHCP pour %s car il possde un nom de domaine illgal" #~ msgid "ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h" #~ msgstr "L'intgration DHCP ISC n'est pas disponible : activez HAVE_ISC_READER dans src/config.h" # #~ msgid "illegal domain %s in dhcp-config directive." #~ msgstr "domaine %s dupliqu dans la directive dhcp-config." #~ msgid "illegal domain %s in %s." #~ msgstr "domaine %s illgal dans %s." #~ msgid "running as root" #~ msgstr "execut en temps que root" #~ msgid "Read leases at startup, but never write the lease file." #~ msgstr "Lecture des baux au dmarrage, mais aucune criture de fichier de baux" # #~ msgid "read %s - %d hosts" #~ msgstr "lecture %s - %d htes" #~ msgid "domains" #~ msgstr "domaines" #~ msgid "Ignoring DHCP host name %s because it has an illegal domain part" #~ msgstr "Le nom de machine DHCP %s sera ignor parce qu'il possde un nom de domaine illgal" #~ msgid "Display this message." #~ msgstr "Affiche ce message." #~ msgid "failed to read %s: %m" #~ msgstr "impossible de lire %s : %m" #~ msgid "failed to read %s:%m" #~ msgstr "impossible de lire %s : %m" # #~ msgid "cannot send encapsulated option %d: no space left in wrapper" #~ msgstr "Impossible d'envoyer l'option DHCP %d : pas assez d'espace dans le paquet" #~ msgid "More than one vendor class matches, using %s" #~ msgstr "Plusieurs classes de fournisseurs correspondent, %s sera utilis" dnsmasq-2.91/po/es.po0000664000175000017500000022121114765043257012676 0ustar srksrk# Spanish translations for dnsmasq package. # This file is put in the public domain. # Christopher Chatham , 2005. # msgid "" msgstr "" "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: 2017-07-17 18:30+0100\n" "Last-Translator: Vicente Soriano \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:652 msgid "Internal error in cache." msgstr "" #: cache.c:1179 #, fuzzy, c-format msgid "failed to load names from %s: %s" msgstr "no se pudo cargar nombres desde %s: %s" #: cache.c:1201 dhcp.c:943 #, fuzzy, c-format msgid "bad address at %s line %d" msgstr "direccin errnea en %s lnea %d" #: cache.c:1254 dhcp.c:959 #, c-format msgid "bad name at %s line %d" msgstr "nombre errneo en %s lnea %d" #: cache.c:1265 #, fuzzy, c-format msgid "read %s - %d names" msgstr "direccines %s - %d ledas" #: cache.c:1381 msgid "cleared cache" msgstr "el cach fue liberado" #: cache.c:1445 #, c-format msgid "No IPv4 address found for %s" msgstr "" #: cache.c:1491 #, 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 concesin DHCP de %s" #: cache.c:1515 #, 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 a concesin DHCP de %s porque el nombre existe en %s con direccin %s" #: cache.c:1760 #, c-format msgid "time %lu" msgstr "tiempo %lu" #: cache.c:1761 #, fuzzy, c-format msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries." msgstr "tamao de cach %d, %d/%d insercines de cach reutilizaron objetos no vencidos." #: cache.c:1763 #, c-format msgid "queries forwarded %u, queries answered locally %u" msgstr "bsquedas reenviadas %u, bsquedas respondidas localmente %u" #: cache.c:1766 #, c-format msgid "queries answered from stale cache %u" msgstr "" #: cache.c:1768 #, fuzzy, c-format msgid "queries for authoritative zones %u" msgstr "Fijar TTL para respuestas autoritarias" #: cache.c:1796 #, fuzzy, c-format msgid "server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums" msgstr "servidor %s#%d: bsquedas enviadas %u, reintentadas o fallidas %u" #: util.c:51 #, fuzzy, c-format msgid "failed to seed the random number generator: %s" msgstr "no se pudo crear valor semilla para el generador de nmeros aleatorios: %s" #: util.c:246 #, fuzzy msgid "failed to allocate memory" msgstr "no se pudo asignar memoria" #: util.c:305 option.c:696 msgid "could not get memory" msgstr "no se pudo adquirir memoria" #: util.c:326 #, fuzzy, c-format msgid "cannot create pipe: %s" msgstr "no se puede crear pipe: %s" #: util.c:334 #, fuzzy, c-format msgid "failed to allocate %d bytes" msgstr "no se pudo asignar %d bytes" #: util.c:344 #, fuzzy, c-format msgid "failed to reallocate %d bytes" msgstr "no se pudo asignar %d bytes" #: util.c:465 #, fuzzy, c-format msgid "cannot read monotonic clock: %s" msgstr "no se puede crear zcalo netlink: %s" #: util.c:579 #, c-format msgid "infinite" msgstr "infinito" #: util.c:867 #, fuzzy, c-format msgid "failed to find kernel version: %s" msgstr "no se pudo acoplar socket de servidor DHCP: %s" #: option.c:393 msgid "Specify local address(es) to listen on." msgstr "Especificar direccin(es) locales dnde escuchar." #: option.c:394 msgid "Return ipaddr for all hosts in specified domains." msgstr "Retornar ipaddr (direccin IP) para todos los hosts en los dominios especificados." #: option.c:395 msgid "Fake reverse lookups for RFC1918 private address ranges." msgstr "Falsificar bsquedas reversas para rangos de direccin privados RFC1918." #: option.c:396 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)." msgstr "Tratar ipaddr (direccin IP) como NXDOMAIN (derrota comodn Verisign)." #: option.c:397 #, c-format msgid "Specify the size of the cache in entries (defaults to %s)." msgstr "Especificar tamao de cach en cuanto a cantidad de objetos (%s por predeterminado)." #: option.c:398 #, c-format msgid "Specify configuration file (defaults to %s)." msgstr "Especificar archivo de configuracin (%s por predeterminado)." #: option.c:399 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:400 msgid "Do NOT forward queries with no domain part." msgstr "NO reenviar bsquedas sin parte de dominio." #: option.c:401 msgid "Return self-pointing MX records for local hosts." msgstr "Retornar expedientes MX auto-sealadores para hosts locales." #: option.c:402 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:403 msgid "Don't forward spurious DNS requests from Windows hosts." msgstr "No reenviar pedidos DNS falsos desde mquinas Windows." #: option.c:404 msgid "Don't include IPv4 addresses in DNS answers." msgstr "" #: option.c:405 msgid "Don't include IPv6 addresses in DNS answers." msgstr "" #: option.c:406 msgid "Enable DHCP in the range given with lease duration." msgstr "Habilitar DHCP dentro del rango brindado con duracin de concesin." #: option.c:407 #, c-format msgid "Change to this group after startup (defaults to %s)." msgstr "Cambiar a este grupo despus del inicio (%s por predeterminado)." #: option.c:408 msgid "Set address or hostname for a specified machine." msgstr "Fijar direccin o nombre de host para una mquina especificada." #: option.c:409 #, fuzzy msgid "Read DHCP host specs from file." msgstr "Leer especificaciones DHCP de host desde archivo" #: option.c:410 #, fuzzy msgid "Read DHCP option specs from file." msgstr "Leer opciones DHCP de host desde archivo" #: option.c:411 #, fuzzy msgid "Read DHCP host specs from a directory." msgstr "Leer especificaciones DHCP de host desde archivo" #: option.c:412 #, fuzzy msgid "Read DHCP options from a directory." msgstr "Leer opciones DHCP de host desde archivo" #: option.c:413 msgid "Evaluate conditional tag expression." msgstr "Evaluar expresin condicional de etiqueta." #: option.c:414 #, c-format msgid "Do NOT load %s file." msgstr "NO cargar archivo %s." #: option.c:415 #, c-format msgid "Specify a hosts file to be read in addition to %s." msgstr "Especificar un archivo de hosts para ser ledo adicionalmente a %s." #: option.c:416 #, fuzzy msgid "Read hosts files from a directory." msgstr "Leer especificaciones DHCP de host desde archivo" #: option.c:417 msgid "Specify interface(s) to listen on." msgstr "Especificar interfase(s) donde escuchar." #: option.c:418 msgid "Specify interface(s) NOT to listen on." msgstr "Especificar interfase(s) donde NO escuchar." #: option.c:419 #, fuzzy msgid "Map DHCP user class to tag." msgstr "Trazar clase de usuario DHCP a etiqueta." #: option.c:420 msgid "Map RFC3046 circuit-id to tag." msgstr "Trazar circuit-id (identificacin de circuito) RFC3046 a etiqueta." #: option.c:421 msgid "Map RFC3046 remote-id to tag." msgstr "Trazar remote-id (identificacin remota) RFC3046 a etiqueta." #: option.c:422 msgid "Map RFC3993 subscriber-id to tag." msgstr "Trazar subscriber-id (identificacin de suscritor) RFC3993 a etiqueta." #: option.c:423 msgid "Specify vendor class to match for PXE requests." msgstr "" #: option.c:424 #, fuzzy msgid "Don't do DHCP for hosts with tag set." msgstr "No hacer DHCP para hosts con etiqueta fijada." #: option.c:425 #, fuzzy msgid "Force broadcast replies for hosts with tag set." msgstr "Forzar respuestas broadcast para hosts con etiqueta fijada." #: option.c:426 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:427 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:428 #, c-format msgid "Specify where to store DHCP leases (defaults to %s)." msgstr "Especificar donde almacenar concesin DHCP (%s por predeterminado)." #: option.c:429 msgid "Return MX records for local hosts." msgstr "Retornar expedientes MX para hosts locales." #: option.c:430 msgid "Specify an MX record." msgstr "Especificar un expediente MX." #: option.c:431 msgid "Specify BOOTP options to DHCP server." msgstr "Especificar opciones BOOTP a servidor DHCP." #: option.c:432 #, c-format msgid "Do NOT poll %s file, reload only on SIGHUP." msgstr "NO revisar archivo %s peridicamente, recargar solo con SIGHUP." #: option.c:433 msgid "Do NOT cache failed search results." msgstr "NO almacenar en cach resultados de bsquedas fallidas." #: option.c:434 msgid "Use expired cache data for faster reply." msgstr "" #: option.c:435 #, 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:436 #, fuzzy msgid "Specify options to be sent to DHCP clients." msgstr "Especificar opciones para ser enviadas a clientes DHCP." #: option.c:437 msgid "DHCP option sent even if the client does not request it." msgstr "Opcin DHCP enviada an si el cliente no la pide." #: option.c:438 msgid "Specify port to listen for DNS requests on (defaults to 53)." msgstr "Especificar puerto donde escuchar por bsquedas DNS (53 por predeterminado)." #: option.c:439 #, c-format msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)." msgstr "Tamao mximo de paquetes UDP soportado para EDNS.0 (%s por predeterminado)." #: option.c:440 #, fuzzy msgid "Log DNS queries." msgstr "Bitacorear bsquedas DNS." #: option.c:441 #, fuzzy msgid "Force the originating port for upstream DNS queries." msgstr "Enforzar el puerto original para bsquedas DNS subida." #: option.c:442 msgid "Set maximum number of random originating ports for a query." msgstr "" #: option.c:443 msgid "Do NOT read resolv.conf." msgstr "NO leer resolv.conf." #: option.c:444 #, c-format msgid "Specify path to resolv.conf (defaults to %s)." msgstr "Especificar el path hacia resolv.conf (%s por predeterminado)." #: option.c:445 #, fuzzy msgid "Specify path to file with server= options" msgstr "Especificar path de archivo PID (%s por predeterminado)." #: option.c:446 msgid "Specify address(es) of upstream servers with optional domains." msgstr "Especificar direccin(es) de servidores subida con dominios opcionales." #: option.c:447 #, fuzzy msgid "Specify address of upstream servers for reverse address queries" msgstr "Especificar direccin(es) de servidores subida con dominios opcionales." #: option.c:448 msgid "Never forward queries to specified domains." msgstr "Nunca reenviar bsquedas a dominios especificados." #: option.c:449 msgid "Specify the domain to be assigned in DHCP leases." msgstr "Especificar el dominio para ser asignado en concesin DHCP." #: option.c:450 msgid "Specify default target in an MX record." msgstr "Especificar destino predeterminado en un expediente MX." #: option.c:451 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:452 #, fuzzy msgid "Specify time-to-live in seconds for negative caching." msgstr "Especificar tiempo de vida en segundos para cach negativo." #: option.c:453 #, 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:454 #, fuzzy msgid "Specify time-to-live ceiling for cache." msgstr "Especificar tiempo de vida en segundos para cach negativo." #: option.c:455 #, fuzzy msgid "Specify time-to-live floor for cache." msgstr "Especificar tiempo de vida en segundos para cach negativo." #: option.c:456 msgid "Retry DNS queries after this many milliseconds." msgstr "" #: option.c:457 #, 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:458 #, fuzzy msgid "Map DHCP vendor class to tag." msgstr "Trazar clase de vendedor DHCP a etiqueta." #: option.c:459 msgid "Display dnsmasq version and copyright information." msgstr "Mostrar informacin sobre la versin y copyright de dnsmasq." #: option.c:460 msgid "Translate IPv4 addresses from upstream servers." msgstr "Traducir direcciones IPv4 desde servidores subida." #: option.c:461 msgid "Specify a SRV record." msgstr "Especificar un expediente SRV." #: option.c:462 #, fuzzy msgid "Display this message. Use --help dhcp or --help dhcp6 for known DHCP options." msgstr "Mostrar este mensaje. Usar --help dhcp para opciones DHCP conocidas." #: option.c:463 #, fuzzy, c-format msgid "Specify path of PID file (defaults to %s)." msgstr "Especificar path de archivo PID (%s por predeterminado)." #: option.c:464 #, c-format msgid "Specify maximum number of DHCP leases (defaults to %s)." msgstr "Especificar nmero mximo de concesin DHCP (%s por predeterminado)." #: option.c:465 msgid "Answer DNS queries based on the interface a query was sent to." msgstr "Responder a bsquedas DNS en base a la interfase a la cul fueron enviadas." #: option.c:466 msgid "Specify TXT DNS record." msgstr "Especificar expediente DNS TXT." #: option.c:467 #, fuzzy msgid "Specify PTR DNS record." msgstr "Especificar expediente DNS PTR." #: option.c:468 msgid "Give DNS name to IPv4 address of interface." msgstr "Otorgar nombre DNS a direccin IPv4 de interfase." #: option.c:469 msgid "Bind only to interfaces in use." msgstr "Acoplar solo a interfases en uso." #: option.c:470 #, c-format msgid "Read DHCP static host information from %s." msgstr "Leer informacin sobre hosts DHCP estticos desde %s." #: option.c:471 msgid "Enable the DBus interface for setting upstream servers, etc." msgstr "Habilitar la interfase DBus para fijar servidores subida, etc." #: option.c:472 msgid "Enable the UBus interface." msgstr "" #: option.c:473 msgid "Do not provide DHCP on this interface, only provide DNS." msgstr "No proveer DHCP en esta interfase, slo proveer DNS." #: option.c:474 msgid "Enable dynamic address allocation for bootp." msgstr "Habilitar alocacin dinmica de direccines para BOOTP." #: option.c:475 #, fuzzy msgid "Map MAC address (with wildcards) to option set." msgstr "Trazar direccin MAC (con comodnes) a opcin fijada." #: option.c:476 msgid "Treat DHCP requests on aliases as arriving from interface." msgstr "Tratar pedidos DHCP en alias como si llegaran de la interfase." #: option.c:477 msgid "Specify extra networks sharing a broadcast domain for DHCP" msgstr "" #: option.c:478 msgid "Disable ICMP echo address checking in the DHCP server." msgstr "Deshabilitar verificacin de direccines para echo ICMP en el servidor DHCP." #: option.c:479 #, fuzzy msgid "Shell script to run on DHCP lease creation and destruction." msgstr "Archivo guin para ejecutar cuando se crea o destruye una concesin DHCP." #: option.c:480 #, fuzzy msgid "Lua script to run on DHCP lease creation and destruction." msgstr "Archivo guin para ejecutar cuando se crea o destruye una concesin DHCP." #: option.c:481 #, fuzzy msgid "Run lease-change scripts as this user." msgstr "Correr archivo guin de cambio de concesin como este usuario." #: option.c:482 msgid "Call dhcp-script with changes to local ARP table." msgstr "" #: option.c:483 msgid "Read configuration from all the files in this directory." msgstr "Leer configuracin desde todos los archivos en este directorio." #: option.c:484 msgid "Execute file and read configuration from stdin." msgstr "" #: option.c:485 #, 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:486 msgid "Do not use leasefile." msgstr "No usar archivo de concesin." #: option.c:487 #, fuzzy, c-format msgid "Maximum number of concurrent DNS queries. (defaults to %s)" msgstr "Nmero mximo de bsquedas DNS simultneas. (%s por predeterminado)" #: option.c:488 #, c-format msgid "Clear DNS cache when reloading %s." msgstr "Liberar cach DNS al recargar %s." #: option.c:489 msgid "Ignore hostnames provided by DHCP clients." msgstr "Ignorar nombres de host brindados por clientes DHCP." #: option.c:490 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:491 msgid "Enable integrated read-only TFTP server." msgstr "Habilitar servidor integrado TFTP solo-lectura." #: option.c:492 msgid "Export files by TFTP only from the specified subtree." msgstr "Exportar archivos va TFTP solo del sub-rbol especificado." #: option.c:493 #, fuzzy msgid "Add client IP or hardware address to tftp-root." msgstr "Agregar IP de cliente a tftp-root." #: option.c:494 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:495 msgid "Do not terminate the service if TFTP directories are inaccessible." msgstr "" #: option.c:496 #, fuzzy, c-format msgid "Maximum number of concurrent TFTP transfers (defaults to %s)." msgstr "Nmero mximo de transferencias TFTP simultneas (%s por predeterminado)." #: option.c:497 #, fuzzy msgid "Maximum MTU to use for TFTP transfers." msgstr "Nmero mximo de transferencias TFTP simultneas (%s por predeterminado)." #: option.c:498 msgid "Disable the TFTP blocksize extension." msgstr "Deshabilitar la extensin TFTP blocksize (tamao de bloque)." #: option.c:499 msgid "Convert TFTP filenames to lowercase" msgstr "Convertir a minsculas los nombres de archivos TFTP" #: option.c:500 msgid "Ephemeral port range for use by TFTP transfers." msgstr "Rango de puertos efmeros para ser usados en transferencias TFTP." #: option.c:501 msgid "Use only one port for TFTP server." msgstr "" #: option.c:502 msgid "Extra logging for DHCP." msgstr "Log extra para DHCP." #: option.c:503 msgid "Enable async. logging; optionally set queue length." msgstr "Habilitar registro asncrono; opcionalmente fijar tamao de cola." #: option.c:504 msgid "Stop DNS rebinding. Filter private IP ranges when resolving." msgstr "Detener revinculacin DNS. Filtrar rangos de IP privados al resolver." #: option.c:505 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers." msgstr "Permitir revinculacin de 127.0.0.0/8, para servidores RBL." #: option.c:506 msgid "Inhibit DNS-rebind protection on this domain." msgstr "Inhibir proteccin de revinculacin DNS en este dominio." #: option.c:507 msgid "Always perform DNS queries to all servers." msgstr "Siempre realizar bsquedas DNS a todos los servidores." #: option.c:508 #, fuzzy msgid "Set tag if client includes matching option in request." msgstr "Fijar etiqueta si cliente incluye opcin coincidente en pedido." #: option.c:509 #, fuzzy msgid "Set tag if client provides given name." msgstr "Fijar etiqueta si cliente incluye opcin coincidente en pedido." #: option.c:510 msgid "Use alternative ports for DHCP." msgstr "Usar puertos alternativos para DHCP." #: option.c:511 #, fuzzy msgid "Specify NAPTR DNS record." msgstr "Especificar expediente DNS NAPTR." #: option.c:512 msgid "Specify lowest port available for DNS query transmission." msgstr "Especificar puerto ms bajo disponible para transmisin de bsquedas DNS." #: option.c:513 #, fuzzy msgid "Specify highest port available for DNS query transmission." msgstr "Especificar puerto ms bajo disponible para transmisin de bsquedas DNS." #: option.c:514 msgid "Use only fully qualified domain names for DHCP clients." msgstr "Usar solo nombres de dominio completamente calificados para clientes DHCP." #: option.c:515 msgid "Generate hostnames based on MAC address for nameless clients." msgstr "Generar hostnames basados en direcciones MAC para clientes sin nombre." #: option.c:516 msgid "Use these DHCP relays as full proxies." msgstr "Usar estos relays DHCP como proxies completos." #: option.c:517 msgid "Relay DHCP requests to a remote server" msgstr "" #: option.c:518 msgid "Specify alias name for LOCAL DNS name." msgstr "Especificar nombre alias para nombre DNS LOCAL." #: option.c:519 #, fuzzy msgid "Prompt to send to PXE clients." msgstr "Aviso a ser enviado a clientes PXE." #: option.c:520 msgid "Boot service for PXE menu." msgstr "Servicio de arranque para men PXE." #: option.c:521 msgid "Check configuration syntax." msgstr "Revisar sintaxis de configuracin." #: option.c:522 msgid "Add requestor's MAC address to forwarded DNS queries." msgstr "Aadir direcciones MAC de los peticionarios a los filtros DNS enviados" #: option.c:523 msgid "Strip MAC information from queries." msgstr "" #: option.c:524 #, fuzzy msgid "Add specified IP subnet to forwarded DNS queries." msgstr "Aadir direcciones MAC de los peticionarios a los filtros DNS enviados" #: option.c:525 msgid "Strip ECS information from queries." msgstr "" #: option.c:526 #, fuzzy msgid "Add client identification to forwarded DNS queries." msgstr "Aadir direcciones MAC de los peticionarios a los filtros DNS enviados" #: option.c:527 #, fuzzy msgid "Proxy DNSSEC validation results from upstream nameservers." msgstr "Traducir direcciones IPv4 desde servidores subida." #: option.c:528 msgid "Attempt to allocate sequential IP addresses to DHCP clients." msgstr "Intento de instaurar direcciones IP secuenciales a cliente DHCP" #: option.c:529 #, fuzzy msgid "Ignore client identifier option sent by DHCP clients." msgstr "Ignorar nombres de host brindados por clientes DHCP." #: option.c:530 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:531 msgid "Allow DHCP clients to do their own DDNS updates." msgstr "Permite a clientes DHCP realizar sus propias actualizaciones DDNS" #: option.c:532 msgid "Send router-advertisements for interfaces doing DHCPv6" msgstr "Enviar anuncios del router a los interfases realizando DHCPv6" #: option.c:533 msgid "Specify DUID_EN-type DHCPv6 server DUID" msgstr "" #: option.c:534 #, fuzzy msgid "Specify host (A/AAAA and PTR) records" msgstr "Especificar un expediente MX." #: option.c:535 msgid "Specify host record in interface subnet" msgstr "" #: option.c:536 msgid "Specify certification authority authorization record" msgstr "" #: option.c:537 #, fuzzy msgid "Specify arbitrary DNS resource record" msgstr "Especificar expediente DNS TXT." #: option.c:538 #, fuzzy msgid "Bind to interfaces in use - check for new interfaces" msgstr "interfase desconocida %s en bridge-interfase" #: option.c:539 msgid "Export local names to global DNS" msgstr "Exportar nombres DNS locales a globales" #: option.c:540 msgid "Domain to export to global DNS" msgstr "Dominio a exportar a DNS global" #: option.c:541 msgid "Set TTL for authoritative replies" msgstr "Fijar TTL para respuestas autoritarias" #: option.c:542 #, fuzzy msgid "Set authoritative zone information" msgstr "Fijar informacin de zona autoritaria" #: option.c:543 msgid "Secondary authoritative nameservers for forward domains" msgstr "Nombres de servidor secundario autoritatorios para dominios enviados" #: option.c:544 msgid "Peers which are allowed to do zone transfer" msgstr "Colegas autorizados a la zona de transferencia (transfer)" #: option.c:545 msgid "Specify ipsets to which matching domains should be added" msgstr "Especificar los ipsets coincidentes en dominio que debran ser aadidos" #: option.c:546 #, fuzzy msgid "Specify nftables sets to which matching domains should be added" msgstr "Especificar los ipsets coincidentes en dominio que debran ser aadidos" #: option.c:547 msgid "Enable filtering of DNS queries with connection-track marks." msgstr "" #: option.c:548 msgid "Set allowed DNS patterns for a connection-track mark." msgstr "" #: option.c:549 #, fuzzy msgid "Specify a domain and address range for synthesised names" msgstr "Especificar dominio y rango de direcciones para los nombres acrnimos" #: option.c:550 msgid "Activate DNSSEC validation" msgstr "" #: option.c:551 msgid "Specify trust anchor key digest." msgstr "" #: option.c:552 msgid "Disable upstream checking for DNSSEC debugging." msgstr "" #: option.c:553 msgid "Ensure answers without DNSSEC are in unsigned zones." msgstr "" #: option.c:554 msgid "Don't check DNSSEC signature timestamps until first cache-reload" msgstr "" #: option.c:555 msgid "Timestamp file to verify system clock for DNSSEC" msgstr "" #: option.c:556 msgid "Set MTU, priority, resend-interval and router-lifetime" msgstr "" #: option.c:557 msgid "Do not log routine DHCP." msgstr "" #: option.c:558 msgid "Do not log routine DHCPv6." msgstr "" #: option.c:559 msgid "Do not log RA." msgstr "" #: option.c:560 msgid "Log debugging information." msgstr "" #: option.c:561 msgid "Accept queries only from directly-connected networks." msgstr "" #: option.c:562 msgid "Detect and remove DNS forwarding loops." msgstr "" #: option.c:563 msgid "Ignore DNS responses containing ipaddr." msgstr "" #: option.c:564 msgid "Set TTL in DNS responses with DHCP-derived addresses." msgstr "" #: option.c:565 msgid "Delay DHCP replies for at least number of seconds." msgstr "" #: option.c:566 msgid "Enables DHCPv4 Rapid Commit option." msgstr "" #: option.c:567 msgid "Path to debug packet dump file" msgstr "" #: option.c:568 msgid "Mask which packets to dump" msgstr "" #: option.c:569 msgid "Call dhcp-script when lease expiry changes." msgstr "" #: option.c:570 msgid "Send Cisco Umbrella identifiers including remote IP." msgstr "" #: option.c:571 msgid "Do not log routine TFTP." msgstr "" #: option.c:572 msgid "Suppress round-robin ordering of DNS records." msgstr "" #: option.c:802 #, c-format msgid "" "Usage: dnsmasq [options]\n" "\n" msgstr "" "Modo de uso: dnsmasq [opciones]\n" "\n" #: option.c:804 #, c-format msgid "Use short options only on the command line.\n" msgstr "Usar opciones cortas solo en la lnea de comandos.\n" #: option.c:806 #, fuzzy, c-format msgid "Valid options are:\n" msgstr "Opciones vlidas son :\n" #: option.c:853 option.c:1055 #, fuzzy msgid "bad address" msgstr "direccin IP errnea" #: option.c:882 option.c:886 msgid "bad port" msgstr "puerto errneo" #: option.c:899 option.c:1002 option.c:1048 msgid "interface binding not supported" msgstr "vinculacin de interfase no est soportado" #: option.c:955 msgid "Cannot resolve server name" msgstr "" #: option.c:991 msgid "cannot use IPv4 server address with IPv6 source address" msgstr "" #: option.c:997 option.c:1043 msgid "interface can only be specified once" msgstr "" #: option.c:1011 option.c:4785 #, fuzzy msgid "bad interface name" msgstr "nombre de interfase errneo" #: option.c:1037 msgid "cannot use IPv6 server address with IPv4 source address" msgstr "" #: option.c:1124 #, fuzzy msgid "bad IPv4 prefix length" msgstr "prefijo errneo" #: option.c:1155 option.c:1165 option.c:1240 option.c:1250 option.c:5360 msgid "error" msgstr "error" #: option.c:1207 #, fuzzy msgid "bad IPv6 prefix length" msgstr "prefijo errneo" #: option.c:1467 msgid "inappropriate vendor:" msgstr "" #: option.c:1474 msgid "inappropriate encap:" msgstr "" #: option.c:1500 msgid "unsupported encapsulation for IPv6 option" msgstr "Encapsulacin no soportada para opcin IPv6" #: option.c:1514 msgid "bad dhcp-option" msgstr "opcin dhcp-option errnea" #: option.c:1592 #, fuzzy msgid "bad IP address" msgstr "direccin IP errnea" #: option.c:1595 option.c:1734 option.c:3928 #, fuzzy msgid "bad IPv6 address" msgstr "direccin IP errnea" #: option.c:1688 #, fuzzy msgid "bad IPv4 address" msgstr "direccin IP errnea" #: option.c:1761 option.c:1856 msgid "bad domain in dhcp-option" msgstr "dominio errneo en dhcp-option" #: option.c:1900 msgid "dhcp-option too long" msgstr "opcin dhcp-option demasiado larga" #: option.c:1907 msgid "illegal dhcp-match" msgstr "dhcp-match ilegal" #: option.c:1966 msgid "illegal repeated flag" msgstr "opcin repetida ilegal" #: option.c:1974 msgid "illegal repeated keyword" msgstr "palabra clave repetida ilegal" #: option.c:2056 option.c:5533 #, fuzzy, c-format msgid "cannot access directory %s: %s" msgstr "no se puede acceder a directorio %s: %s" #: option.c:2102 tftp.c:573 dump.c:72 #, fuzzy, c-format msgid "cannot access %s: %s" msgstr "no se puede acceder %s: %s" #: option.c:2219 msgid "setting log facility is not possible under Android" msgstr "la creacin de un registro no es posible en Android" #: option.c:2228 msgid "bad log facility" msgstr "ubicacin del registro errnea" #: option.c:2281 msgid "bad MX preference" msgstr "preferencia MX errnea" #: option.c:2289 msgid "bad MX name" msgstr "nombre MX errneo" #: option.c:2304 msgid "bad MX target" msgstr "destino MX errneo" #: option.c:2324 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts" msgstr "recompilar con HAVE_SCRIPT definido para habilitar guines de cambio de concesin" #: option.c:2328 #, fuzzy msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts" msgstr "recompilar con HAVE_SCRIPT definido para habilitar 'scripts' en Lua" #: option.c:2447 #, fuzzy msgid "invalid auth-zone" msgstr "rango alias invlido" #: option.c:2589 option.c:2621 #, fuzzy msgid "bad prefix length" msgstr "prefijo errneo" #: option.c:2601 option.c:2642 option.c:2696 #, fuzzy msgid "bad prefix" msgstr "prefijo errneo" #: option.c:2716 #, fuzzy msgid "prefix length too small" msgstr "la longitud del prefijo debe ser al menos 64" #: option.c:3010 #, fuzzy msgid "Bad address in --address" msgstr "direccin en uso" #: option.c:3110 #, fuzzy msgid "recompile with HAVE_IPSET defined to enable ipset directives" msgstr "recompilar con HAVE_SCRIPT definido para habilitar directivas ipset" #: option.c:3117 #, fuzzy msgid "recompile with HAVE_NFTSET defined to enable nftset directives" msgstr "recompilar con HAVE_SCRIPT definido para habilitar directivas ipset" #: option.c:3192 option.c:3210 #, fuzzy msgid "recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives" msgstr "recompilar con HAVE_SCRIPT definido para habilitar directivas ipset" #: option.c:3496 #, fuzzy msgid "bad port range" msgstr "rango de puertos errneo" #: option.c:3522 msgid "bad bridge-interface" msgstr "opcin bridge-interface (interfase puente) errnea" #: option.c:3566 msgid "bad shared-network" msgstr "" #: option.c:3620 msgid "only one tag allowed" msgstr "solo una etiqueta permitida" #: option.c:3641 option.c:3657 option.c:3783 option.c:3791 option.c:3834 msgid "bad dhcp-range" msgstr "opcin dhcp-range (rango DHCP) errnea" #: option.c:3675 msgid "inconsistent DHCP range" msgstr "rango DHCP inconsistente" #: option.c:3741 msgid "prefix length must be exactly 64 for RA subnets" msgstr "la longitud del prefijo debe ser 64 exacto para subredes RA" #: option.c:3743 msgid "prefix length must be exactly 64 for subnet constructors" msgstr "la longitud del prefijo debe ser 64 exacto para subredes constructoras" #: option.c:3746 msgid "prefix length must be at least 64" msgstr "la longitud del prefijo debe ser al menos 64" #: option.c:3749 #, fuzzy msgid "inconsistent DHCPv6 range" msgstr "rango DHCP inconsistente" #: option.c:3768 msgid "prefix must be zero with \"constructor:\" argument" msgstr "prefijo debe ser cero con argumento \"constructor:\"" #: option.c:3893 option.c:3971 #, fuzzy msgid "bad hex constant" msgstr "constante hexadecimal errnea" #: option.c:3946 #, fuzzy msgid "bad IPv6 prefix" msgstr "prefijo errneo" #: option.c:3994 #, fuzzy, c-format msgid "duplicate dhcp-host IP address %s" msgstr "direccin IP duplicada %s en %s." #: option.c:4056 #, fuzzy msgid "bad DHCP host name" msgstr "nombre de host DHCP errneo" #: option.c:4142 #, fuzzy msgid "bad tag-if" msgstr "etiqueta tag-if errnea" #: option.c:4490 option.c:5046 msgid "invalid port number" msgstr "nmero de puerto invlido" #: option.c:4546 #, fuzzy msgid "bad dhcp-proxy address" msgstr "direccin IP errnea" #: option.c:4627 #, fuzzy msgid "Bad dhcp-relay" msgstr "opcin dhcp-range (rango DHCP) errnea" #: option.c:4671 msgid "bad RA-params" msgstr "" #: option.c:4681 msgid "bad DUID" msgstr "DUID errneo" #: option.c:4715 #, fuzzy msgid "missing address in alias" msgstr "direccin en uso" #: option.c:4721 #, fuzzy msgid "invalid alias range" msgstr "rango alias invlido" #: option.c:4770 #, fuzzy msgid "missing address in dynamic host" msgstr "direccin en uso" #: option.c:4785 #, fuzzy msgid "bad dynamic host" msgstr "no se puede acceder a directorio %s: %s" #: option.c:4803 option.c:4819 msgid "bad CNAME" msgstr "CNAME errneo" #: option.c:4827 msgid "duplicate CNAME" msgstr "CNAME duplicado" #: option.c:4854 #, fuzzy msgid "bad PTR record" msgstr "registro PTR errneo" #: option.c:4889 #, fuzzy msgid "bad NAPTR record" msgstr "registro NAPTR errneo" #: option.c:4925 #, fuzzy msgid "bad RR record" msgstr "registro PTR errneo" #: option.c:4958 #, fuzzy msgid "bad CAA record" msgstr "registro PTR errneo" #: option.c:4987 msgid "bad TXT record" msgstr "registro TXT errneo" #: option.c:5030 msgid "bad SRV record" msgstr "registro SRV errneo" #: option.c:5037 msgid "bad SRV target" msgstr "destino SRV errneo" #: option.c:5056 msgid "invalid priority" msgstr "prioridad invlida" #: option.c:5061 msgid "invalid weight" msgstr "peso invlido" #: option.c:5084 #, fuzzy msgid "Bad host-record" msgstr "registro PTR errneo" #: option.c:5123 #, fuzzy msgid "Bad name in host-record" msgstr "nombre errneo en %s" #: option.c:5165 msgid "bad value for dnssec-check-unsigned" msgstr "" #: option.c:5201 #, fuzzy msgid "bad trust anchor" msgstr "rango de puertos errneo" #: option.c:5217 msgid "bad HEX in trust anchor" msgstr "" #: option.c:5228 #, fuzzy msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)" msgstr "opcin no soportada (verificar que dnsmasq fue compilado con soporte para DHCP/TFTP/DBus)" #: option.c:5290 msgid "missing \"" msgstr "falta \"" #: option.c:5347 msgid "bad option" msgstr "opcin errnea" #: option.c:5349 msgid "extraneous parameter" msgstr "parmetro extrao" #: option.c:5351 msgid "missing parameter" msgstr "parmetro ausente" #: option.c:5353 #, fuzzy msgid "illegal option" msgstr "opcin errnea" #: option.c:5363 #, c-format msgid " in output from %s" msgstr "" #: option.c:5365 #, fuzzy, c-format msgid " at line %d of %s" msgstr "%s en lnea %d de %%s" #: option.c:5380 option.c:5683 option.c:5694 #, fuzzy, c-format msgid "read %s" msgstr "lee %s" #: option.c:5446 #, fuzzy, c-format msgid "cannot execute %s: %s" msgstr "no se puede leer %s: %s" #: option.c:5454 option.c:5615 tftp.c:790 #, c-format msgid "cannot read %s: %s" msgstr "no se puede leer %s: %s" #: option.c:5473 #, fuzzy, c-format msgid "error executing %s: %s" msgstr "no se pudo ejecutar %s: %s" #: option.c:5476 #, c-format msgid "%s returns non-zero error code" msgstr "" #: option.c:5775 msgid "junk found in command line" msgstr "basura encontrada en linea de comando" #: option.c:5815 #, c-format msgid "Dnsmasq version %s %s\n" msgstr "Versin dnsmasq %s %s\n" #: option.c:5816 #, fuzzy, c-format msgid "" "Compile time options: %s\n" "\n" msgstr "" "Opciones de compilacin %s\n" "\n" #: option.c:5817 #, c-format msgid "This software comes with ABSOLUTELY NO WARRANTY.\n" msgstr "Este software viene SIN NINGUNA GARANTIA.\n" #: option.c:5818 #, c-format msgid "Dnsmasq is free software, and you are welcome to redistribute it\n" msgstr "Dnsmasq es software libre, y usted est autorizado a redistribuirlo\n" #: option.c:5819 #, fuzzy, c-format msgid "under the terms of the GNU General Public License, version 2 or 3.\n" msgstr "bajo los trminos de la GNU General Public License, versin 2 o 3.\n" #: option.c:5836 msgid "try --help" msgstr "pruebe --help" #: option.c:5838 msgid "try -w" msgstr "pruebe -w" #: option.c:5840 #, fuzzy, c-format msgid "bad command line options: %s" msgstr "opciones de lnea de comandos errneas: %s" #: option.c:5909 #, c-format msgid "CNAME loop involving %s" msgstr "" #: option.c:5950 #, c-format msgid "cannot get host-name: %s" msgstr "no se puede obtener host-name (nombre de host): %s" #: option.c:5978 msgid "only one resolv.conf file allowed in no-poll mode." msgstr "solo un archivo resolv.conf est permitido en modo no-poll." #: option.c:5988 msgid "must have exactly one resolv.conf to read domain from." msgstr "debe haber exctamente un resolv.conf desde donde leer dominio." #: option.c:5991 network.c:1727 dhcp.c:892 #, fuzzy, c-format msgid "failed to read %s: %s" msgstr "no se pudo leer %s: %s" #: option.c:6008 #, c-format msgid "no search directive found in %s" msgstr "ninguna directiva de bsqueda encontrada en %s" #: option.c:6029 #, 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:6038 msgid "syntax check OK" msgstr "revisin de sintaxis OK" #: forward.c:107 #, fuzzy, c-format msgid "failed to send packet: %s" msgstr "no se pudo escuchar en socket: %s" #: forward.c:715 msgid "discarding DNS reply: subnet option mismatch" msgstr "" #: forward.c:780 #, c-format msgid "nameserver %s refused to do a recursive query" msgstr "servidor DNS %s rechaz realizar una bsqueda recursiva" #: forward.c:826 #, fuzzy, c-format msgid "possible DNS-rebind attack detected: %s" msgstr "posible ataque de revinculacin DNS detectado" #: forward.c:1239 #, c-format msgid "reducing DNS packet size for nameserver %s to %d" msgstr "" #: forward.c:1565 #, c-format msgid "ignoring query from non-local network %s (logged only once)" msgstr "" #: forward.c:2139 #, c-format msgid "ignoring query from non-local network %s" msgstr "" #: forward.c:2501 #, fuzzy, c-format msgid "failed to bind server socket to %s: %s" msgstr "no se pudo acoplar al zcalo del servidor para %s: %s" #: forward.c:2867 #, fuzzy, c-format msgid "Maximum number of concurrent DNS queries reached (max: %d)" msgstr "Nmero mximo de bsquedas DNS simultneas alcanzado. (%s por predeterminado)" #: forward.c:2869 #, fuzzy, c-format msgid "Maximum number of concurrent DNS queries to %s reached (max: %d)" msgstr "Nmero mximo de bsquedas DNS simultneas alcanzado. (%s por predeterminado)" #: network.c:700 #, c-format msgid "stopped listening on %s(#%d): %s port %d" msgstr "" #: network.c:911 #, fuzzy, c-format msgid "failed to create listening socket for %s: %s" msgstr "no se pudo crear un zcalo de escucha: %s" #: network.c:1192 #, c-format msgid "listening on %s(#%d): %s port %d" msgstr "" #: network.c:1219 #, fuzzy, c-format msgid "listening on %s port %d" msgstr "TFTP no pudo enviar %s a %s" #: network.c:1252 #, c-format msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s" msgstr "" #: network.c:1259 msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)" msgstr "" #: network.c:1268 #, fuzzy, c-format msgid "warning: using interface %s instead" msgstr "advertencia: interfase %s no existe actualmente" #: network.c:1277 #, fuzzy, c-format msgid "warning: no addresses found for interface %s" msgstr "usando direcciones locales solo para %s %s" #: network.c:1335 #, 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:1340 msgid "try increasing /proc/sys/net/core/optmem_max" msgstr "" #: network.c:1545 #, fuzzy, c-format msgid "failed to bind server socket for %s: %s" msgstr "no se pudo acoplar al zcalo del servidor para %s: %s" #: network.c:1622 #, c-format msgid "ignoring nameserver %s - local interface" msgstr "ignorando servidor DNS %s - interfase local" #: network.c:1633 #, fuzzy, c-format msgid "ignoring nameserver %s - cannot make/bind socket: %s" msgstr "ignorando servidor DNS %s - no se puede crear/acoplar zcalo: %s" #: network.c:1643 msgid "more servers are defined but not logged" msgstr "" #: network.c:1654 msgid "(no DNSSEC)" msgstr "" #: network.c:1657 msgid "unqualified" msgstr "no cualificado" #: network.c:1657 msgid "names" msgstr "nombres" #: network.c:1659 msgid "default" msgstr "predeterminado" #: network.c:1661 msgid "domain" msgstr "dominio" #: network.c:1663 #, fuzzy, c-format msgid "using nameserver %s#%d for %s %s%s %s" msgstr "usando nombre de servidor %s#%d para %s %s" #: network.c:1667 #, fuzzy, c-format msgid "NOT using nameserver %s#%d - query loop detected" msgstr "usando nombre de servidor %s#%d para %s %s" #: network.c:1670 #, fuzzy, c-format msgid "using nameserver %s#%d(via %s)" msgstr "usando nombre de servidor %s#%d(va %s)" #: network.c:1672 #, c-format msgid "using nameserver %s#%d" msgstr "usando nombre de servidor %s#%d" #: network.c:1687 #, fuzzy, c-format msgid "using only locally-known addresses for %s" msgstr "usando direcciones locales solo para %s %s" #: network.c:1690 #, fuzzy, c-format msgid "using standard nameservers for %s" msgstr "usando nombres estndar %s#%d para %s %s" #: network.c:1694 #, fuzzy, c-format msgid "using %d more local addresses" msgstr "usando nombre de servidor %s#%d" #: network.c:1696 #, fuzzy, c-format msgid "using %d more nameservers" msgstr "usando nombre de servidor %s#%d" #: dnsmasq.c:192 msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform" msgstr "" #: dnsmasq.c:207 msgid "no root trust anchor provided for DNSSEC" msgstr "" #: dnsmasq.c:210 msgid "cannot reduce cache size from default when DNSSEC enabled" msgstr "" #: dnsmasq.c:212 #, 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:218 #, 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:225 #, fuzzy msgid "cannot use --conntrack AND --query-port" msgstr "No puede usar --conntrack AND --query-port" #: dnsmasq.c:231 #, 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:236 #, fuzzy msgid "asynchronous logging is not available under Solaris" msgstr "registro asncrono no est disponible bajo Solaris" #: dnsmasq.c:241 #, fuzzy msgid "asynchronous logging is not available under Android" msgstr "registro asncrono no est disponible bajo Solaris" #: dnsmasq.c:246 #, 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:251 #, 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:256 #, fuzzy msgid "Ubus not available: set HAVE_UBUS in src/config.h" msgstr "DBus no disponible: fijar HAVE_DBUS en src/config.h" #: dnsmasq.c:267 msgid "max_port cannot be smaller than min_port" msgstr "" #: dnsmasq.c:271 msgid "port_limit must not be larger than available port range" msgstr "" #: dnsmasq.c:278 msgid "--auth-server required when an auth zone is defined." msgstr "" #: dnsmasq.c:283 msgid "zone serial must be configured in --auth-soa" msgstr "zona serie debe ser configurada en --auth-soa" #: dnsmasq.c:303 msgid "dhcp-range constructor not available on this platform" msgstr "constructor rango dhcp no disponible en esta plataforma" #: dnsmasq.c:377 msgid "cannot set --bind-interfaces and --bind-dynamic" msgstr "no puede usar --bind-interfases y --bind-dynamic" #: dnsmasq.c:380 #, c-format msgid "failed to find list of interfaces: %s" msgstr "no se pudo encontrar lista de interfases: %s" #: dnsmasq.c:389 #, c-format msgid "unknown interface %s" msgstr "interfase desconocida %s" #: dnsmasq.c:396 #, 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" #: dnsmasq.c:440 #, fuzzy msgid "Packet dumps not available: set HAVE_DUMP in src/config.h" msgstr "DBus no disponible: fijar HAVE_DBUS en src/config.h" #: dnsmasq.c:448 dnsmasq.c:1232 #, c-format msgid "DBus error: %s" msgstr "error DBus: %s" #: dnsmasq.c:451 msgid "DBus not available: set HAVE_DBUS in src/config.h" msgstr "DBus no disponible: fijar HAVE_DBUS en src/config.h" #: dnsmasq.c:459 dnsmasq.c:1253 #, fuzzy, c-format msgid "UBus error: %s" msgstr "error DBus: %s" #: dnsmasq.c:462 #, fuzzy msgid "UBus not available: set HAVE_UBUS in src/config.h" msgstr "DBus no disponible: fijar HAVE_DBUS en src/config.h" #: dnsmasq.c:492 #, c-format msgid "unknown user or group: %s" msgstr "usuario o grupo desconocido: %s" #: dnsmasq.c:568 #, c-format msgid "process is missing required capability %s" msgstr "" #: dnsmasq.c:600 #, c-format msgid "cannot chdir to filesystem root: %s" msgstr "no se puede cambiar directorio a raz de sistema de archivos: %s" #: dnsmasq.c:852 #, fuzzy, c-format msgid "started, version %s DNS disabled" msgstr "iniciado, versin %s DNS deshabilitado" #: dnsmasq.c:857 #, c-format msgid "started, version %s cachesize %d" msgstr "iniciado, versin %s tamao de cach %d" #: dnsmasq.c:859 msgid "cache size greater than 10000 may cause performance issues, and is unlikely to be useful." msgstr "" #: dnsmasq.c:862 #, c-format msgid "started, version %s cache disabled" msgstr "iniciado, versin %s cach deshabilitado" #: dnsmasq.c:865 msgid "DNS service limited to local subnets" msgstr "" #: dnsmasq.c:868 #, c-format msgid "compile time options: %s" msgstr "opciones de compilacin: %s" #: dnsmasq.c:877 msgid "DBus support enabled: connected to system bus" msgstr "soporte DBus habilitado: conectado a bus de sistema" #: dnsmasq.c:879 msgid "DBus support enabled: bus connection pending" msgstr "soporte DBus habilitado: conexin a bus pendiente" #: dnsmasq.c:887 #, fuzzy msgid "UBus support enabled: connected to system bus" msgstr "soporte DBus habilitado: conectado a bus de sistema" #: dnsmasq.c:889 #, fuzzy msgid "UBus support enabled: bus connection pending" msgstr "soporte DBus habilitado: conexin a bus pendiente" #: dnsmasq.c:909 msgid "DNSSEC validation enabled but all unsigned answers are trusted" msgstr "" #: dnsmasq.c:911 msgid "DNSSEC validation enabled" msgstr "" #: dnsmasq.c:915 msgid "DNSSEC signature timestamps not checked until receipt of SIGINT" msgstr "" #: dnsmasq.c:918 msgid "DNSSEC signature timestamps not checked until system time valid" msgstr "" #: dnsmasq.c:921 #, c-format msgid "configured with trust anchor for %s keytag %u" msgstr "" #: dnsmasq.c:927 #, fuzzy, c-format msgid "warning: failed to change owner of %s: %s" msgstr "advertencia: no se pudo cambiar propietario de %s: %s" #: dnsmasq.c:932 msgid "setting --bind-interfaces option because of OS limitations" msgstr "fijando opcin --bind-interfases debido a limitaciones de sistema operativo" #: dnsmasq.c:945 #, c-format msgid "warning: interface %s does not currently exist" msgstr "advertencia: interfase %s no existe actualmente" #: dnsmasq.c:950 msgid "warning: ignoring resolv-file flag because no-resolv is set" msgstr "advertencia: ignorando opcin resolv-file porque no-resolv est fijado" #: dnsmasq.c:953 #, fuzzy msgid "warning: no upstream servers configured" msgstr "advertencia: ningn servidor de subida configurado" #: dnsmasq.c:957 #, c-format msgid "asynchronous logging enabled, queue limit is %d messages" msgstr "registro asncrono habilitado, el lmite de la cola es %d mensajes" #: dnsmasq.c:978 msgid "IPv6 router advertisement enabled" msgstr "Anuncio de router IPv6 habilitado" #: dnsmasq.c:983 #, c-format msgid "DHCP, sockets bound exclusively to interface %s" msgstr "" #: dnsmasq.c:1000 msgid "root is " msgstr "root est " #: dnsmasq.c:1000 #, fuzzy msgid "enabled" msgstr "habilitado" #: dnsmasq.c:1002 msgid "secure mode" msgstr "modo seguro" #: dnsmasq.c:1003 #, fuzzy msgid "single port mode" msgstr "nmero de puerto invlido" #: dnsmasq.c:1006 #, c-format msgid "warning: %s inaccessible" msgstr "" #: dnsmasq.c:1010 #, fuzzy, c-format msgid "warning: TFTP directory %s inaccessible" msgstr "directorio TFTP % inaccesible: %s" #: dnsmasq.c:1036 #, c-format msgid "restricting maximum simultaneous TFTP transfers to %d" msgstr "limitando nmero mximo de transferencias TFTP simultneas a %d" #: dnsmasq.c:1095 #, c-format msgid "error binding DHCP socket to device %s" msgstr "" #: dnsmasq.c:1229 msgid "connected to system DBus" msgstr "conectado a DBus de sistema" #: dnsmasq.c:1250 #, fuzzy msgid "connected to system UBus" msgstr "conectado a DBus de sistema" #: dnsmasq.c:1416 #, c-format msgid "cannot fork into background: %s" msgstr "no se puede hacer fork en background: %s" #: dnsmasq.c:1420 #, fuzzy, c-format msgid "failed to create helper: %s" msgstr "no se pudo crear ayudante: %s" #: dnsmasq.c:1424 #, fuzzy, c-format msgid "setting capabilities failed: %s" msgstr "configuracin de capacidades ha fallado: %s" #: dnsmasq.c:1428 #, fuzzy, c-format msgid "failed to change user-id to %s: %s" msgstr "no se pudo cambiar user-id a %s: %s" #: dnsmasq.c:1432 #, fuzzy, c-format msgid "failed to change group-id to %s: %s" msgstr "no se pudo cambiar group-id a %s: %s" #: dnsmasq.c:1436 #, fuzzy, c-format msgid "failed to open pidfile %s: %s" msgstr "no se pudo abrir archivo PID %s: %s" #: dnsmasq.c:1440 #, fuzzy, c-format msgid "cannot open log %s: %s" msgstr "no se puede abrir registro %s: %s" #: dnsmasq.c:1444 #, fuzzy, c-format msgid "failed to load Lua script: %s" msgstr "no se pudo cargar script Lua %s: %s" #: dnsmasq.c:1448 #, c-format msgid "TFTP directory %s inaccessible: %s" msgstr "directorio TFTP % inaccesible: %s" #: dnsmasq.c:1452 #, fuzzy, c-format msgid "cannot create timestamp file %s: %s" msgstr "no se puede abrir o crear archivo de concesin %s: %s" #: dnsmasq.c:1536 #, fuzzy, c-format msgid "script process killed by signal %d" msgstr "proceso script eliminado por seal %d" #: dnsmasq.c:1540 #, fuzzy, c-format msgid "script process exited with status %d" msgstr "proceso script sali con con estado %d" #: dnsmasq.c:1544 #, fuzzy, c-format msgid "failed to execute %s: %s" msgstr "no se pudo ejecutar %s: %s" #: dnsmasq.c:1584 msgid "now checking DNSSEC signature timestamps" msgstr "" #: dnsmasq.c:1619 dnssec.c:160 dnssec.c:204 #, fuzzy, c-format msgid "failed to update mtime on %s: %s" msgstr "no se pudo abrir archivo PID %s: %s" #: dnsmasq.c:1631 msgid "exiting on receipt of SIGTERM" msgstr "saliendo al recibir SIGTERM" #: dnsmasq.c:1659 #, fuzzy, c-format msgid "failed to access %s: %s" msgstr "no se pudo acceder %s: %s" #: dnsmasq.c:1690 #, c-format msgid "reading %s" msgstr "leyendo %s" #: dnsmasq.c:1706 #, fuzzy, c-format msgid "no servers found in %s, will retry" msgstr "ningn servidor encontrado en %s, se reintentar" #: dhcp.c:51 #, c-format msgid "cannot create DHCP socket: %s" msgstr "no se puede crear zcalo DHCP: %s" #: dhcp.c:66 #, c-format msgid "failed to set options on DHCP socket: %s" msgstr "no se pudo fijar opciones en zcalo DHCP: %s" #: dhcp.c:87 #, 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 zcalo DHCP: %s" #: dhcp.c:99 #, c-format msgid "failed to bind DHCP server socket: %s" msgstr "no se pudo acoplar zcalo de servidor DHCP: %s" #: dhcp.c:125 #, c-format msgid "cannot create ICMP raw socket: %s." msgstr "no se puede crear zcalo puro ICMP: %s." #: dhcp.c:254 dhcp6.c:186 #, fuzzy, c-format msgid "unknown interface %s in bridge-interface" msgstr "interfase desconocida %s en bridge-interface" #: dhcp.c:295 #, c-format msgid "DHCP packet received on %s which has no address" msgstr "Paquete DHCP recibido en %s que no tiene direccin" #: dhcp.c:429 #, c-format msgid "ARP-cache injection failed: %s" msgstr "" #: dhcp.c:490 #, c-format msgid "Error sending DHCP packet to %s: %s" msgstr "" #: dhcp.c:547 #, c-format msgid "DHCP range %s -- %s is not consistent with netmask %s" msgstr "rango DHCP %s -- %s no coincide con mscara de subred %s" #: dhcp.c:930 #, fuzzy, c-format msgid "bad line at %s line %d" msgstr "lnea errnea en %s lnea %d" #: dhcp.c:973 #, c-format msgid "ignoring %s line %d, duplicate name or IP address" msgstr "ignorando %s lnea %d, nombre o direccin IP duplicada" #: dhcp.c:1034 #, c-format msgid "read %s - %d addresses" msgstr "direccines %s - %d ledas" #: dhcp.c:1136 #, fuzzy, c-format msgid "Cannot broadcast DHCP relay via interface %s" msgstr "No puede hacer multicast DHCPv6 sin el interfase correcto" #: dhcp.c:1160 #, c-format msgid "broadcast via %s" msgstr "" #: dhcp.c:1163 rfc3315.c:2219 #, fuzzy, c-format msgid "DHCP relay at %s -> %s" msgstr "DHCP relay %s -> %s" #: lease.c:64 #, c-format msgid "ignoring invalid line in lease database: %s %s %s %s ..." msgstr "" #: lease.c:101 #, c-format msgid "ignoring invalid line in lease database, bad address: %s" msgstr "" #: lease.c:108 msgid "too many stored leases" msgstr "demasiadas concesiones almacenadas" #: lease.c:176 #, fuzzy, c-format msgid "cannot open or create lease file %s: %s" msgstr "no se puede abrir o crear archivo de concesin %s: %s" #: lease.c:185 #, fuzzy msgid "failed to parse lease database cleanly" msgstr "no se pudo leer %s: %s" #: lease.c:188 #, fuzzy, c-format msgid "failed to read lease file %s: %s" msgstr "no se pudo leer %s: %s" #: lease.c:204 #, fuzzy, c-format msgid "cannot run lease-init script %s: %s" msgstr "no se puede ejecutar archivo script lease-init %s: %s" #: lease.c:210 #, c-format msgid "lease-init script returned exit code %s" msgstr "archivo guin lease-init retorn cdigo de salida %s" #: lease.c:381 #, fuzzy, c-format msgid "failed to write %s: %s (retry in %u s)" msgstr "error al escribir %s: %s (reintentar en %us)" #: lease.c:955 #, c-format msgid "Ignoring domain %s for DHCP host name %s" msgstr "Ignorando dominio %s para nombre de host DHCP %s" #: rfc2131.c:378 msgid "with subnet selector" msgstr "con selector de subred" #: rfc2131.c:383 msgid "via" msgstr "va" #: rfc2131.c:389 #, c-format msgid "no address range available for DHCP request %s %s" msgstr "ningn rango de direccines disponible para pedido DHCP %s %s" #: rfc2131.c:403 #, fuzzy, c-format msgid "%u available DHCP subnet: %s/%s" msgstr "%u Subred DHCP disponible: %s/%s" #: rfc2131.c:409 rfc3315.c:320 #, fuzzy, c-format msgid "%u available DHCP range: %s -- %s" msgstr "%u Rango DHCP disponible: %s -- %s" #: rfc2131.c:521 #, fuzzy, c-format msgid "%u vendor class: %s" msgstr "%u Clase de vendedor: %s" #: rfc2131.c:523 #, fuzzy, c-format msgid "%u user class: %s" msgstr "%u Clase de usuario: %s" #: rfc2131.c:557 msgid "disabled" msgstr "deshabilitado" #: rfc2131.c:598 rfc2131.c:1087 rfc2131.c:1536 rfc3315.c:633 rfc3315.c:816 #: rfc3315.c:1122 msgid "ignored" msgstr "ignorado" #: rfc2131.c:613 rfc2131.c:1340 rfc3315.c:868 msgid "address in use" msgstr "direccin en uso" #: rfc2131.c:627 rfc2131.c:1141 msgid "no address available" msgstr "ninguna direccin disponible" #: rfc2131.c:634 rfc2131.c:1302 msgid "wrong network" msgstr "red equivocada" #: rfc2131.c:649 msgid "no address configured" msgstr "ninguna direccin configurada" #: rfc2131.c:655 rfc2131.c:1353 msgid "no leases left" msgstr "no sobra ninguna concesin" #: rfc2131.c:756 rfc3315.c:500 #, fuzzy, c-format msgid "%u client provides name: %s" msgstr "%u cliente provee nombre: %s" #: rfc2131.c:885 msgid "PXE BIS not supported" msgstr "no hay soporte para BIS PXE" #: rfc2131.c:1054 rfc3315.c:1223 #, fuzzy, c-format msgid "disabling DHCP static address %s for %s" msgstr "deshabilitando direccin DHCP esttica %s para %s" #: rfc2131.c:1075 msgid "unknown lease" msgstr "concesin desconocida" #: rfc2131.c:1110 #, c-format msgid "not using configured address %s because it is leased to %s" msgstr "no usando direccin configurada %s porque est concedida a %s" #: rfc2131.c:1120 #, fuzzy, c-format msgid "not using configured address %s because it is in use by the server or relay" msgstr "no usando direccin configurada %s porque est en uso por el servidor o relay" #: rfc2131.c:1123 #, fuzzy, c-format msgid "not using configured address %s because it was previously declined" msgstr "no usando direccin configurada %s porque fu previamente denegada" #: rfc2131.c:1139 rfc2131.c:1346 msgid "no unique-id" msgstr "ningn unique-id (identificacin nica)" #: rfc2131.c:1238 msgid "wrong server-ID" msgstr "ID de servidor equivocada" #: rfc2131.c:1257 msgid "wrong address" msgstr "direccin equivocada" #: rfc2131.c:1275 rfc3315.c:976 msgid "lease not found" msgstr "concesin no encontrada" #: rfc2131.c:1310 msgid "address not available" msgstr "direccin no disponible" #: rfc2131.c:1321 msgid "static lease available" msgstr "concesin esttica disponible" #: rfc2131.c:1325 msgid "address reserved" msgstr "direccin reservada" #: rfc2131.c:1334 #, c-format msgid "abandoning lease to %s of %s" msgstr "abandonando concesin a %s de %s" #: rfc2131.c:1870 #, c-format msgid "%u bootfile name: %s" msgstr "%u nombre de bootfile: %s" #: rfc2131.c:1879 #, c-format msgid "%u server name: %s" msgstr "%u nombre de servidor: %s" #: rfc2131.c:1889 #, fuzzy, c-format msgid "%u next server: %s" msgstr "%u siguiente servidor: %s" #: rfc2131.c:1893 #, c-format msgid "%u broadcast response" msgstr "%u respuesta broadcast" #: rfc2131.c:1956 #, fuzzy, c-format msgid "cannot send DHCP/BOOTP option %d: no space left in packet" msgstr "no se puede enviar opcin DHCP/BOOTP %d: no queda espacio en el paquete" #: rfc2131.c:2267 msgid "PXE menu too large" msgstr "men PXE demasiado largo" #: rfc2131.c:2430 rfc3315.c:1517 #, fuzzy, c-format msgid "%u requested options: %s" msgstr "%u opciones solicitadas: %s" #: rfc2131.c:2747 #, c-format msgid "cannot send RFC3925 option: too many options for enterprise number %d" msgstr "no se puede enviar opcin RFC3925: demasiadas opciones para nmero enterprise %d" #: rfc2131.c:2810 #, c-format msgid "%u reply delay: %d" msgstr "" #: netlink.c:86 #, fuzzy, c-format msgid "cannot create netlink socket: %s" msgstr "no se puede crear zcalo netlink: %s" #: netlink.c:379 #, fuzzy, c-format msgid "netlink returns error: %s" msgstr "netlink retorna error: %s" #: dbus.c:491 #, c-format msgid "Enabling --%s option from D-Bus" msgstr "" #: dbus.c:496 #, c-format msgid "Disabling --%s option from D-Bus" msgstr "" #: dbus.c:857 msgid "setting upstream servers from DBus" msgstr "fijando servidores subida desde DBus" #: dbus.c:907 msgid "could not register a DBus message handler" msgstr "no se pudo registrar un manejador de mensajes DBus" #: bpf.c:261 #, c-format msgid "cannot create DHCP BPF socket: %s" msgstr "no se puede crear zcalo BPF DHCP: %s" #: bpf.c:289 #, 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" #: bpf.c:374 #, fuzzy, c-format msgid "cannot create PF_ROUTE socket: %s" msgstr "no se puede crear zcalo DHCP: %s" #: bpf.c:395 msgid "Unknown protocol version from route socket" msgstr "" #: helper.c:150 msgid "lease() function missing in Lua script" msgstr "la funcin lease() no se encuentra en el script Lua" #: tftp.c:353 msgid "unable to get free port for TFTP" msgstr "incapaz de conseguir puerto libre para TFTP" #: tftp.c:369 #, c-format msgid "unsupported request from %s" msgstr "pedido no-soportado desde %s" #: tftp.c:520 #, fuzzy, c-format msgid "file %s not found for %s" msgstr "archivo %s no encontrado por %s" #: tftp.c:609 #, c-format msgid "ignoring packet from %s (TID mismatch)" msgstr "" #: tftp.c:662 #, fuzzy, c-format msgid "failed sending %s to %s" msgstr "TFTP no pudo enviar %s a %s" #: tftp.c:662 #, fuzzy, c-format msgid "sent %s to %s" msgstr "TFTP envi %s a %s" #: tftp.c:712 #, fuzzy, c-format msgid "error %d %s received from %s" msgstr "error TFTP %d %s recibido de %s" #: log.c:203 #, c-format msgid "overflow: %d log entries lost" msgstr "desbordamiento: %d entradas de registro perdidas" #: log.c:281 #, c-format msgid "log failed: %s" msgstr "registro fall: %s" #: log.c:490 msgid "FAILED to start up" msgstr "el inicio ha FALLADO" #: conntrack.c:63 #, c-format msgid "Conntrack connection mark retrieval failed: %s" msgstr "Conexin conntrack con marca recuperacin fall" #: dhcp6.c:51 #, fuzzy, c-format msgid "cannot create DHCPv6 socket: %s" msgstr "no se puede crear zcalo DHCP: %s" #: dhcp6.c:72 #, 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 zcalo DHCP: %s" #: dhcp6.c:84 #, fuzzy, c-format msgid "failed to bind DHCPv6 server socket: %s" msgstr "no se pudo acoplar zcalo de servidor DHCP: %s" #: rfc3315.c:173 #, fuzzy, c-format msgid "no address range available for DHCPv6 request from relay at %s" msgstr "ningn rango de direccines disponible para pedido DHCP %s %s" #: rfc3315.c:182 #, fuzzy, c-format msgid "no address range available for DHCPv6 request via %s" msgstr "ningn rango de direccines disponible para pedido DHCP %s %s" #: rfc3315.c:317 #, fuzzy, c-format msgid "%u available DHCPv6 subnet: %s/%d" msgstr "%u Subred DHCP disponible: %s/%s" #: rfc3315.c:400 #, fuzzy, c-format msgid "%u vendor class: %u" msgstr "%u Clase de vendedor: %s" #: rfc3315.c:448 #, fuzzy, c-format msgid "%u client MAC address: %s" msgstr "%u cliente provee nombre: %s" #: rfc3315.c:763 rfc3315.c:860 #, fuzzy msgid "address unavailable" msgstr "direccin no disponible" #: rfc3315.c:775 rfc3315.c:904 rfc3315.c:1273 msgid "success" msgstr "" #: rfc3315.c:790 rfc3315.c:799 rfc3315.c:912 rfc3315.c:914 rfc3315.c:1048 #, fuzzy msgid "no addresses available" msgstr "ninguna direccin disponible" #: rfc3315.c:891 msgid "not on link" msgstr "no en el enlace" #: rfc3315.c:980 rfc3315.c:1181 rfc3315.c:1262 msgid "no binding found" msgstr "uniones no encontradas" #: rfc3315.c:1017 msgid "deprecated" msgstr "descartado" #: rfc3315.c:1024 #, fuzzy msgid "address invalid" msgstr "direccin en uso" #: rfc3315.c:1082 rfc3315.c:1084 msgid "confirm failed" msgstr "confirmacin fall" #: rfc3315.c:1099 #, fuzzy msgid "all addresses still on link" msgstr "direccin errnea en %s lnea %d" #: rfc3315.c:1190 msgid "release received" msgstr "concesin recibida" #: rfc3315.c:2200 #, fuzzy, c-format msgid "Cannot multicast DHCP relay via interface %s" msgstr "No puede hacer multicast DHCPv6 sin el interfase correcto" #: rfc3315.c:2216 #, c-format msgid "multicast via %s" msgstr "" #: dhcp-common.c:187 #, c-format msgid "Ignoring duplicate dhcp-option %d" msgstr "Ignorando opcin DHCP duplicada" #: dhcp-common.c:264 #, c-format msgid "%u tags: %s" msgstr "%u etiquetas: %s" #: dhcp-common.c:484 #, c-format msgid "%s has more than one address in hostsfile, using %s for DHCP" msgstr "%s tiene ms de una direccin en hostsfile, usando %s para DHCP" #: dhcp-common.c:518 #, c-format msgid "duplicate IP address %s (%s) in dhcp-config directive" msgstr "direccin IP duplicada %s (%s) en directiva dhcp-config" #: dhcp-common.c:738 #, c-format msgid "Known DHCP options:\n" msgstr "Opciones DHCP conocidas:\n" #: dhcp-common.c:749 #, fuzzy, c-format msgid "Known DHCPv6 options:\n" msgstr "Opciones DHCP conocidas:\n" #: dhcp-common.c:946 msgid ", prefix deprecated" msgstr ", prefijo descartado" #: dhcp-common.c:949 #, c-format msgid ", lease time " msgstr ", tiempo de concesin" #: dhcp-common.c:991 #, c-format msgid "%s stateless on %s%.0s%.0s%s" msgstr "%s aptrida en %s%.0s%.0s%s" #: dhcp-common.c:993 #, fuzzy, c-format msgid "%s, static leases only on %.0s%s%s%.0s" msgstr "DHCP, concesin esttica solo en %.0s%s, tiempo de concesin %s" #: dhcp-common.c:995 #, fuzzy, c-format msgid "%s, proxy on subnet %.0s%s%.0s%.0s" msgstr "DHCP, proxy en subred %.0s%s%.0s" #: dhcp-common.c:996 #, fuzzy, c-format msgid "%s, IP range %s -- %s%s%.0s" msgstr "DHCP, rango de IPs %s -- %s, tiempo de concesin %s" #: dhcp-common.c:1009 #, c-format msgid "DHCPv4-derived IPv6 names on %s%s" msgstr "" #: dhcp-common.c:1012 #, fuzzy, c-format msgid "router advertisement on %s%s" msgstr "DHCP, concesin estticos solo en %.0s%s, tiempo de concesin %s" #: dhcp-common.c:1043 #, fuzzy, c-format msgid "DHCP relay from %s via %s" msgstr "DHCP relay %s -> %s" #: dhcp-common.c:1045 #, c-format msgid "DHCP relay from %s to %s via %s" msgstr "" #: dhcp-common.c:1048 #, c-format msgid "DHCP relay from %s to %s" msgstr "" #: radv.c:110 #, fuzzy, c-format msgid "cannot create ICMPv6 socket: %s" msgstr "no se puede crear socket DHCP: %s" #: auth.c:462 #, fuzzy, c-format msgid "ignoring zone transfer request from %s" msgstr "pedido no-soportado desde %s" #: ipset.c:99 #, fuzzy, c-format msgid "failed to create IPset control socket: %s" msgstr "no se pudo crear socket TFTP: %s" #: ipset.c:211 #, fuzzy, c-format msgid "failed to update ipset %s: %s" msgstr "no se pudo abrir archivo PID %s: %s" #: pattern.c:29 #, c-format msgid "[pattern.c:%d] Assertion failure: %s" msgstr "" #: pattern.c:142 #, c-format msgid "Invalid DNS name: Invalid character %c." msgstr "" #: pattern.c:151 msgid "Invalid DNS name: Empty label." msgstr "" #: pattern.c:156 msgid "Invalid DNS name: Label starts with hyphen." msgstr "" #: pattern.c:170 msgid "Invalid DNS name: Label ends with hyphen." msgstr "" #: pattern.c:176 #, c-format msgid "Invalid DNS name: Label is too long (%zu)." msgstr "" #: pattern.c:184 #, c-format msgid "Invalid DNS name: Not enough labels (%zu)." msgstr "" #: pattern.c:189 msgid "Invalid DNS name: Final label is fully numeric." msgstr "" #: pattern.c:199 msgid "Invalid DNS name: \"local\" pseudo-TLD." msgstr "" #: pattern.c:204 #, c-format msgid "DNS name has invalid length (%zu)." msgstr "" #: pattern.c:258 #, c-format msgid "Invalid DNS name pattern: Invalid character %c." msgstr "" #: pattern.c:267 msgid "Invalid DNS name pattern: Empty label." msgstr "" #: pattern.c:272 msgid "Invalid DNS name pattern: Label starts with hyphen." msgstr "" #: pattern.c:285 msgid "Invalid DNS name pattern: Wildcard character used more than twice per label." msgstr "" #: pattern.c:295 msgid "Invalid DNS name pattern: Label ends with hyphen." msgstr "" #: pattern.c:301 #, c-format msgid "Invalid DNS name pattern: Label is too long (%zu)." msgstr "" #: pattern.c:309 #, c-format msgid "Invalid DNS name pattern: Not enough labels (%zu)." msgstr "" #: pattern.c:314 msgid "Invalid DNS name pattern: Wildcard within final two labels." msgstr "" #: pattern.c:319 msgid "Invalid DNS name pattern: Final label is fully numeric." msgstr "" #: pattern.c:329 msgid "Invalid DNS name pattern: \"local\" pseudo-TLD." msgstr "" #: pattern.c:334 #, c-format msgid "DNS name pattern has invalid length after removing wildcards (%zu)." msgstr "" #: dnssec.c:206 msgid "system time considered valid, now checking DNSSEC signature timestamps." msgstr "" #: dnssec.c:1018 #, c-format msgid "Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support" msgstr "" #: blockdata.c:55 #, c-format msgid "pool memory in use %zu, max %zu, allocated %zu" msgstr "" #: tables.c:61 #, fuzzy, c-format msgid "failed to access pf devices: %s" msgstr "no se pudo acceder %s: %s" #: tables.c:74 #, fuzzy, c-format msgid "warning: no opened pf devices %s" msgstr "usando direcciones locales solo para %s %s" #: tables.c:82 #, fuzzy, c-format msgid "error: cannot use table name %s" msgstr "no se puede obtener host-name (nombre de host): %s" #: tables.c:90 #, c-format msgid "error: cannot strlcpy table name %s" msgstr "" #: tables.c:101 #, fuzzy, c-format msgid "IPset: error: %s" msgstr "error DBus: %s" #: tables.c:108 msgid "info: table created" msgstr "" #: tables.c:133 #, c-format msgid "warning: DIOCR%sADDRS: %s" msgstr "" #: tables.c:137 #, fuzzy, c-format msgid "%d addresses %s" msgstr "direccin IP errnea" #: inotify.c:62 #, fuzzy, c-format msgid "cannot access path %s: %s" msgstr "no se puede acceder %s: %s" #: inotify.c:95 #, fuzzy, c-format msgid "failed to create inotify: %s" msgstr "no se pudo crear ayudante: %s" #: inotify.c:111 #, c-format msgid "too many symlinks following %s" msgstr "" #: inotify.c:127 #, c-format msgid "directory %s for resolv-file is missing, cannot poll" msgstr "" #: inotify.c:131 inotify.c:200 #, fuzzy, c-format msgid "failed to create inotify for %s: %s" msgstr "no se pudo crear un zcalo de escucha: %s" #: inotify.c:178 inotify.c:185 #, fuzzy, c-format msgid "bad dynamic directory %s: %s" msgstr "no se puede acceder a directorio %s: %s" #: inotify.c:186 #, fuzzy msgid "not a directory" msgstr "no se puede acceder a directorio %s: %s" #: inotify.c:299 #, c-format msgid "inotify: %s removed" msgstr "" #: inotify.c:301 #, c-format msgid "inotify: %s new or modified" msgstr "" #: inotify.c:309 #, c-format msgid "inotify: flushed %u names read from %s" msgstr "" #: dump.c:68 #, fuzzy, c-format msgid "cannot create %s: %s" msgstr "no se puede leer %s: %s" #: dump.c:74 #, fuzzy, c-format msgid "bad header in %s" msgstr "direccin en uso" #: dump.c:287 #, fuzzy msgid "failed to write packet dump" msgstr "no se pudo escuchar en socket: %s" #: dump.c:289 #, c-format msgid "%u dumping packet %u mask 0x%04x" msgstr "" #: dump.c:291 #, c-format msgid "dumping packet %u mask 0x%04x" msgstr "" #: ubus.c:79 #, c-format msgid "UBus subscription callback: %s subscriber(s)" msgstr "" #: ubus.c:99 #, fuzzy, c-format msgid "Cannot reconnect to UBus: %s" msgstr "no se puede abrir registro %s: %s" #: ubus.c:135 msgid "Cannot set UBus listeners: no connection" msgstr "" #: ubus.c:155 msgid "Cannot poll UBus listeners: no connection" msgstr "" #: ubus.c:168 msgid "Disconnecting from UBus" msgstr "" #: ubus.c:179 ubus.c:326 #, c-format msgid "UBus command failed: %d (%s)" msgstr "" #: hash-questions.c:40 msgid "Failed to create SHA-256 hash object" msgstr "" #: nftset.c:35 #, fuzzy msgid "failed to create nftset context" msgstr "no se pudo crear socket TFTP: %s" #, fuzzy #~ msgid "bad IPv4 prefix" #~ msgstr "prefijo errneo" #, fuzzy #~ msgid "Cannot add object to UBus: %s" #~ msgstr "no se puede abrir registro %s: %s" #, fuzzy #~ msgid "Failed to send UBus event: %s" #~ msgstr "no se pudo escuchar en socket: %s" #~ msgid "Specify DHCPv6 prefix class" #~ msgstr "Especificar prefijo de clase DHCPv6" #~ msgid "cannot run scripts under uClinux" #~ msgstr "no se pueden correr archivos 'script' bajo uClinux" #~ msgid "cannot match tags in --dhcp-host" #~ msgstr "no coinciden etiquetas en --dhcp-host" #~ msgid "attempt to set an IPv6 server address via DBus - no IPv6 support" #~ msgstr "intento de fijar direccin de servidor IPv6 va DBus - no hay soporte IPv6" #, fuzzy #~ msgid "unknown prefix-class %d" #~ msgstr "clase de prefijo desconocida" #, fuzzy #~ msgid "cannot cannonicalise resolv-file %s: %s" #~ msgstr "no se puede abrir o crear archivo de concesin %s: %s" #~ msgid "no interface with address %s" #~ msgstr "ninguna interfase con direccin %s" #~ msgid "duplicate IP address %s in dhcp-config directive." #~ msgstr "direccin 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" #, fuzzy #~ msgid "only one dhcp-optsfile allowed" #~ msgstr "solo un dhcp-optsfile permitido" #~ msgid "files nested too deep in %s" #~ msgstr "archivos jerarquizados demasiado profundo en %s" #~ msgid "TXT record string too long" #~ msgstr "expediente TXT demasiado largo" #~ msgid "failed to set IPV6 options on listening socket: %s" #~ msgstr "no se pudo fijar opciones IPv6 sobre socket escuchador: %s" #~ msgid "failed to bind listening socket for %s: %s" #~ msgstr "no se pudo acoplar socket escuchador para %s: %s" #~ msgid "DHCP packet: transaction-id is %u" #~ msgstr "paquete DHCP: transaction-id (identificacin de transaccin) es %u" #~ msgid "must set exactly one interface on broken systems without IP_RECVIF" #~ msgstr "debe fijarse exctamente una interfase en sistemas rotos sin IP_RECVIF" #~ msgid "Ignoring DHCP lease for %s because it has an illegal domain part" #~ msgstr "Ignorando concesin 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 "integracin dhcpd ISC no disponible: fijar HAVE_ISC_READER en src/config.h" #, fuzzy #~ msgid "illegal domain %s in dhcp-config directive." #~ msgstr "dominio ilegal %s en directiva dhcp-config." #~ msgid "illegal domain %s in %s." #~ msgstr "dominio ilegal %s en %s." #~ msgid "running as root" #~ msgstr "corriendo como root" #~ msgid "Read leases at startup, but never write the lease file." #~ msgstr "Leer concesin al inicio, pero nunca escribir el archivo de concesin." #, fuzzy #~ msgid "read %s - %d hosts" #~ msgstr "direccines %s - %d ledas" #~ msgid "Limit of %d leases exceeded." #~ msgstr "Lmite de %d concesin excedido." #~ msgid "domains" #~ msgstr "dominios" #~ msgid "Ignoring DHCP host name %s because it has an illegal domain part" #~ msgstr "Ignorando nombre de host DHCP %s porque contiene una parte ilegal de dominio" #~ msgid "Display this message." #~ msgstr "Mostrar este mensaje." #~ msgid "failed to read %s: %m" #~ msgstr "no se pudo leer %s: %m" #~ msgid "failed to read %s:%m" #~ msgstr "no se pudo leer %s:%m" dnsmasq-2.91/po/it.po0000664000175000017500000014051514765043257012712 0ustar srksrk# Italian translations for dnsmasq package. # This file is put in the public domain. # Simon Kelley , 2006. # msgid "" msgstr "" "Project-Id-Version: dnsmasq 2.32\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-06-18 12:24+0100\n" "PO-Revision-Date: 2017-07-17 18:30+0100\n" "Last-Translator: Simon Kelley \n" "Language-Team: Italian \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:652 msgid "Internal error in cache." msgstr "" #: cache.c:1179 #, c-format msgid "failed to load names from %s: %s" msgstr "" #: cache.c:1201 dhcp.c:943 #, c-format msgid "bad address at %s line %d" msgstr "" #: cache.c:1254 dhcp.c:959 #, c-format msgid "bad name at %s line %d" msgstr "" #: cache.c:1265 #, c-format msgid "read %s - %d names" msgstr "" #: cache.c:1381 msgid "cleared cache" msgstr "" #: cache.c:1445 #, c-format msgid "No IPv4 address found for %s" msgstr "" #: cache.c:1491 #, c-format msgid "%s is a CNAME, not giving it to the DHCP lease of %s" msgstr "" #: cache.c:1515 #, 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:1760 #, c-format msgid "time %lu" msgstr "" #: cache.c:1761 #, c-format msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries." msgstr "" #: cache.c:1763 #, c-format msgid "queries forwarded %u, queries answered locally %u" msgstr "" #: cache.c:1766 #, c-format msgid "queries answered from stale cache %u" msgstr "" #: cache.c:1768 #, c-format msgid "queries for authoritative zones %u" msgstr "" #: cache.c:1796 #, c-format msgid "server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums" msgstr "" #: util.c:51 #, c-format msgid "failed to seed the random number generator: %s" msgstr "" #: util.c:246 msgid "failed to allocate memory" msgstr "" #: util.c:305 option.c:696 msgid "could not get memory" msgstr "" #: util.c:326 #, c-format msgid "cannot create pipe: %s" msgstr "" #: util.c:334 #, c-format msgid "failed to allocate %d bytes" msgstr "" #: util.c:344 #, c-format msgid "failed to reallocate %d bytes" msgstr "" #: util.c:465 #, c-format msgid "cannot read monotonic clock: %s" msgstr "" #: util.c:579 #, c-format msgid "infinite" msgstr "" #: util.c:867 #, c-format msgid "failed to find kernel version: %s" msgstr "" #: option.c:393 msgid "Specify local address(es) to listen on." msgstr "" #: option.c:394 msgid "Return ipaddr for all hosts in specified domains." msgstr "" #: option.c:395 msgid "Fake reverse lookups for RFC1918 private address ranges." msgstr "" #: option.c:396 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)." msgstr "" #: option.c:397 #, c-format msgid "Specify the size of the cache in entries (defaults to %s)." msgstr "" #: option.c:398 #, c-format msgid "Specify configuration file (defaults to %s)." msgstr "" #: option.c:399 msgid "Do NOT fork into the background: run in debug mode." msgstr "" #: option.c:400 msgid "Do NOT forward queries with no domain part." msgstr "" #: option.c:401 msgid "Return self-pointing MX records for local hosts." msgstr "" #: option.c:402 msgid "Expand simple names in /etc/hosts with domain-suffix." msgstr "" #: option.c:403 msgid "Don't forward spurious DNS requests from Windows hosts." msgstr "" #: option.c:404 msgid "Don't include IPv4 addresses in DNS answers." msgstr "" #: option.c:405 msgid "Don't include IPv6 addresses in DNS answers." msgstr "" #: option.c:406 msgid "Enable DHCP in the range given with lease duration." msgstr "" #: option.c:407 #, c-format msgid "Change to this group after startup (defaults to %s)." msgstr "" #: option.c:408 msgid "Set address or hostname for a specified machine." msgstr "" #: option.c:409 msgid "Read DHCP host specs from file." msgstr "" #: option.c:410 msgid "Read DHCP option specs from file." msgstr "" #: option.c:411 msgid "Read DHCP host specs from a directory." msgstr "" #: option.c:412 msgid "Read DHCP options from a directory." msgstr "" #: option.c:413 msgid "Evaluate conditional tag expression." msgstr "" #: option.c:414 #, c-format msgid "Do NOT load %s file." msgstr "" #: option.c:415 #, c-format msgid "Specify a hosts file to be read in addition to %s." msgstr "" #: option.c:416 msgid "Read hosts files from a directory." msgstr "" #: option.c:417 msgid "Specify interface(s) to listen on." msgstr "" #: option.c:418 msgid "Specify interface(s) NOT to listen on." msgstr "" #: option.c:419 msgid "Map DHCP user class to tag." msgstr "" #: option.c:420 msgid "Map RFC3046 circuit-id to tag." msgstr "" #: option.c:421 msgid "Map RFC3046 remote-id to tag." msgstr "" #: option.c:422 msgid "Map RFC3993 subscriber-id to tag." msgstr "" #: option.c:423 msgid "Specify vendor class to match for PXE requests." msgstr "" #: option.c:424 msgid "Don't do DHCP for hosts with tag set." msgstr "" #: option.c:425 msgid "Force broadcast replies for hosts with tag set." msgstr "" #: option.c:426 msgid "Do NOT fork into the background, do NOT run in debug mode." msgstr "" #: option.c:427 msgid "Assume we are the only DHCP server on the local network." msgstr "" #: option.c:428 #, c-format msgid "Specify where to store DHCP leases (defaults to %s)." msgstr "" #: option.c:429 msgid "Return MX records for local hosts." msgstr "" #: option.c:430 msgid "Specify an MX record." msgstr "" #: option.c:431 msgid "Specify BOOTP options to DHCP server." msgstr "" #: option.c:432 #, c-format msgid "Do NOT poll %s file, reload only on SIGHUP." msgstr "" #: option.c:433 msgid "Do NOT cache failed search results." msgstr "" #: option.c:434 msgid "Use expired cache data for faster reply." msgstr "" #: option.c:435 #, c-format msgid "Use nameservers strictly in the order given in %s." msgstr "" #: option.c:436 msgid "Specify options to be sent to DHCP clients." msgstr "" #: option.c:437 msgid "DHCP option sent even if the client does not request it." msgstr "" #: option.c:438 msgid "Specify port to listen for DNS requests on (defaults to 53)." msgstr "" #: option.c:439 #, c-format msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)." msgstr "" #: option.c:440 msgid "Log DNS queries." msgstr "" #: option.c:441 msgid "Force the originating port for upstream DNS queries." msgstr "" #: option.c:442 msgid "Set maximum number of random originating ports for a query." msgstr "" #: option.c:443 msgid "Do NOT read resolv.conf." msgstr "" #: option.c:444 #, c-format msgid "Specify path to resolv.conf (defaults to %s)." msgstr "" #: option.c:445 msgid "Specify path to file with server= options" msgstr "" #: option.c:446 msgid "Specify address(es) of upstream servers with optional domains." msgstr "" #: option.c:447 msgid "Specify address of upstream servers for reverse address queries" msgstr "" #: option.c:448 msgid "Never forward queries to specified domains." msgstr "" #: option.c:449 msgid "Specify the domain to be assigned in DHCP leases." msgstr "" #: option.c:450 msgid "Specify default target in an MX record." msgstr "" #: option.c:451 msgid "Specify time-to-live in seconds for replies from /etc/hosts." msgstr "" #: option.c:452 msgid "Specify time-to-live in seconds for negative caching." msgstr "" #: option.c:453 msgid "Specify time-to-live in seconds for maximum TTL to send to clients." msgstr "" #: option.c:454 msgid "Specify time-to-live ceiling for cache." msgstr "" #: option.c:455 msgid "Specify time-to-live floor for cache." msgstr "" #: option.c:456 msgid "Retry DNS queries after this many milliseconds." msgstr "" #: option.c:457 #, c-format msgid "Change to this user after startup. (defaults to %s)." msgstr "" #: option.c:458 msgid "Map DHCP vendor class to tag." msgstr "" #: option.c:459 msgid "Display dnsmasq version and copyright information." msgstr "" #: option.c:460 msgid "Translate IPv4 addresses from upstream servers." msgstr "" #: option.c:461 msgid "Specify a SRV record." msgstr "" #: option.c:462 msgid "Display this message. Use --help dhcp or --help dhcp6 for known DHCP options." msgstr "" #: option.c:463 #, c-format msgid "Specify path of PID file (defaults to %s)." msgstr "" #: option.c:464 #, c-format msgid "Specify maximum number of DHCP leases (defaults to %s)." msgstr "" #: option.c:465 msgid "Answer DNS queries based on the interface a query was sent to." msgstr "" #: option.c:466 msgid "Specify TXT DNS record." msgstr "" #: option.c:467 msgid "Specify PTR DNS record." msgstr "" #: option.c:468 msgid "Give DNS name to IPv4 address of interface." msgstr "" #: option.c:469 msgid "Bind only to interfaces in use." msgstr "" #: option.c:470 #, c-format msgid "Read DHCP static host information from %s." msgstr "" #: option.c:471 msgid "Enable the DBus interface for setting upstream servers, etc." msgstr "" #: option.c:472 msgid "Enable the UBus interface." msgstr "" #: option.c:473 msgid "Do not provide DHCP on this interface, only provide DNS." msgstr "" #: option.c:474 msgid "Enable dynamic address allocation for bootp." msgstr "" #: option.c:475 msgid "Map MAC address (with wildcards) to option set." msgstr "" #: option.c:476 msgid "Treat DHCP requests on aliases as arriving from interface." msgstr "" #: option.c:477 msgid "Specify extra networks sharing a broadcast domain for DHCP" msgstr "" #: option.c:478 msgid "Disable ICMP echo address checking in the DHCP server." msgstr "" #: option.c:479 msgid "Shell script to run on DHCP lease creation and destruction." msgstr "" #: option.c:480 msgid "Lua script to run on DHCP lease creation and destruction." msgstr "" #: option.c:481 msgid "Run lease-change scripts as this user." msgstr "" #: option.c:482 msgid "Call dhcp-script with changes to local ARP table." msgstr "" #: option.c:483 msgid "Read configuration from all the files in this directory." msgstr "" #: option.c:484 msgid "Execute file and read configuration from stdin." msgstr "" #: option.c:485 msgid "Log to this syslog facility or file. (defaults to DAEMON)" msgstr "" #: option.c:486 msgid "Do not use leasefile." msgstr "" #: option.c:487 #, c-format msgid "Maximum number of concurrent DNS queries. (defaults to %s)" msgstr "" #: option.c:488 #, c-format msgid "Clear DNS cache when reloading %s." msgstr "" #: option.c:489 msgid "Ignore hostnames provided by DHCP clients." msgstr "" #: option.c:490 msgid "Do NOT reuse filename and server fields for extra DHCP options." msgstr "" #: option.c:491 msgid "Enable integrated read-only TFTP server." msgstr "" #: option.c:492 msgid "Export files by TFTP only from the specified subtree." msgstr "" #: option.c:493 msgid "Add client IP or hardware address to tftp-root." msgstr "" #: option.c:494 msgid "Allow access only to files owned by the user running dnsmasq." msgstr "" #: option.c:495 msgid "Do not terminate the service if TFTP directories are inaccessible." msgstr "" #: option.c:496 #, c-format msgid "Maximum number of concurrent TFTP transfers (defaults to %s)." msgstr "" #: option.c:497 msgid "Maximum MTU to use for TFTP transfers." msgstr "" #: option.c:498 msgid "Disable the TFTP blocksize extension." msgstr "" #: option.c:499 msgid "Convert TFTP filenames to lowercase" msgstr "" #: option.c:500 msgid "Ephemeral port range for use by TFTP transfers." msgstr "" #: option.c:501 msgid "Use only one port for TFTP server." msgstr "" #: option.c:502 msgid "Extra logging for DHCP." msgstr "" #: option.c:503 msgid "Enable async. logging; optionally set queue length." msgstr "" #: option.c:504 msgid "Stop DNS rebinding. Filter private IP ranges when resolving." msgstr "" #: option.c:505 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers." msgstr "" #: option.c:506 msgid "Inhibit DNS-rebind protection on this domain." msgstr "" #: option.c:507 msgid "Always perform DNS queries to all servers." msgstr "" #: option.c:508 msgid "Set tag if client includes matching option in request." msgstr "" #: option.c:509 msgid "Set tag if client provides given name." msgstr "" #: option.c:510 msgid "Use alternative ports for DHCP." msgstr "" #: option.c:511 msgid "Specify NAPTR DNS record." msgstr "" #: option.c:512 msgid "Specify lowest port available for DNS query transmission." msgstr "" #: option.c:513 msgid "Specify highest port available for DNS query transmission." msgstr "" #: option.c:514 msgid "Use only fully qualified domain names for DHCP clients." msgstr "" #: option.c:515 msgid "Generate hostnames based on MAC address for nameless clients." msgstr "" #: option.c:516 msgid "Use these DHCP relays as full proxies." msgstr "" #: option.c:517 msgid "Relay DHCP requests to a remote server" msgstr "" #: option.c:518 msgid "Specify alias name for LOCAL DNS name." msgstr "" #: option.c:519 msgid "Prompt to send to PXE clients." msgstr "" #: option.c:520 msgid "Boot service for PXE menu." msgstr "" #: option.c:521 msgid "Check configuration syntax." msgstr "" #: option.c:522 msgid "Add requestor's MAC address to forwarded DNS queries." msgstr "" #: option.c:523 msgid "Strip MAC information from queries." msgstr "" #: option.c:524 msgid "Add specified IP subnet to forwarded DNS queries." msgstr "" #: option.c:525 msgid "Strip ECS information from queries." msgstr "" #: option.c:526 msgid "Add client identification to forwarded DNS queries." msgstr "" #: option.c:527 msgid "Proxy DNSSEC validation results from upstream nameservers." msgstr "" #: option.c:528 msgid "Attempt to allocate sequential IP addresses to DHCP clients." msgstr "" #: option.c:529 msgid "Ignore client identifier option sent by DHCP clients." msgstr "" #: option.c:530 msgid "Copy connection-track mark from queries to upstream connections." msgstr "" #: option.c:531 msgid "Allow DHCP clients to do their own DDNS updates." msgstr "" #: option.c:532 msgid "Send router-advertisements for interfaces doing DHCPv6" msgstr "" #: option.c:533 msgid "Specify DUID_EN-type DHCPv6 server DUID" msgstr "" #: option.c:534 msgid "Specify host (A/AAAA and PTR) records" msgstr "" #: option.c:535 msgid "Specify host record in interface subnet" msgstr "" #: option.c:536 msgid "Specify certification authority authorization record" msgstr "" #: option.c:537 msgid "Specify arbitrary DNS resource record" msgstr "" #: option.c:538 msgid "Bind to interfaces in use - check for new interfaces" msgstr "" #: option.c:539 msgid "Export local names to global DNS" msgstr "" #: option.c:540 msgid "Domain to export to global DNS" msgstr "" #: option.c:541 msgid "Set TTL for authoritative replies" msgstr "" #: option.c:542 msgid "Set authoritative zone information" msgstr "" #: option.c:543 msgid "Secondary authoritative nameservers for forward domains" msgstr "" #: option.c:544 msgid "Peers which are allowed to do zone transfer" msgstr "" #: option.c:545 msgid "Specify ipsets to which matching domains should be added" msgstr "" #: option.c:546 msgid "Specify nftables sets to which matching domains should be added" msgstr "" #: option.c:547 msgid "Enable filtering of DNS queries with connection-track marks." msgstr "" #: option.c:548 msgid "Set allowed DNS patterns for a connection-track mark." msgstr "" #: option.c:549 msgid "Specify a domain and address range for synthesised names" msgstr "" #: option.c:550 msgid "Activate DNSSEC validation" msgstr "" #: option.c:551 msgid "Specify trust anchor key digest." msgstr "" #: option.c:552 msgid "Disable upstream checking for DNSSEC debugging." msgstr "" #: option.c:553 msgid "Ensure answers without DNSSEC are in unsigned zones." msgstr "" #: option.c:554 msgid "Don't check DNSSEC signature timestamps until first cache-reload" msgstr "" #: option.c:555 msgid "Timestamp file to verify system clock for DNSSEC" msgstr "" #: option.c:556 msgid "Set MTU, priority, resend-interval and router-lifetime" msgstr "" #: option.c:557 msgid "Do not log routine DHCP." msgstr "" #: option.c:558 msgid "Do not log routine DHCPv6." msgstr "" #: option.c:559 msgid "Do not log RA." msgstr "" #: option.c:560 msgid "Log debugging information." msgstr "" #: option.c:561 msgid "Accept queries only from directly-connected networks." msgstr "" #: option.c:562 msgid "Detect and remove DNS forwarding loops." msgstr "" #: option.c:563 msgid "Ignore DNS responses containing ipaddr." msgstr "" #: option.c:564 msgid "Set TTL in DNS responses with DHCP-derived addresses." msgstr "" #: option.c:565 msgid "Delay DHCP replies for at least number of seconds." msgstr "" #: option.c:566 msgid "Enables DHCPv4 Rapid Commit option." msgstr "" #: option.c:567 msgid "Path to debug packet dump file" msgstr "" #: option.c:568 msgid "Mask which packets to dump" msgstr "" #: option.c:569 msgid "Call dhcp-script when lease expiry changes." msgstr "" #: option.c:570 msgid "Send Cisco Umbrella identifiers including remote IP." msgstr "" #: option.c:571 msgid "Do not log routine TFTP." msgstr "" #: option.c:572 msgid "Suppress round-robin ordering of DNS records." msgstr "" #: option.c:802 #, c-format msgid "" "Usage: dnsmasq [options]\n" "\n" msgstr "" #: option.c:804 #, c-format msgid "Use short options only on the command line.\n" msgstr "" #: option.c:806 #, c-format msgid "Valid options are:\n" msgstr "" #: option.c:853 option.c:1055 msgid "bad address" msgstr "" #: option.c:882 option.c:886 msgid "bad port" msgstr "" #: option.c:899 option.c:1002 option.c:1048 msgid "interface binding not supported" msgstr "" #: option.c:955 msgid "Cannot resolve server name" msgstr "" #: option.c:991 msgid "cannot use IPv4 server address with IPv6 source address" msgstr "" #: option.c:997 option.c:1043 msgid "interface can only be specified once" msgstr "" #: option.c:1011 option.c:4785 msgid "bad interface name" msgstr "" #: option.c:1037 msgid "cannot use IPv6 server address with IPv4 source address" msgstr "" #: option.c:1124 msgid "bad IPv4 prefix length" msgstr "" #: option.c:1155 option.c:1165 option.c:1240 option.c:1250 option.c:5360 msgid "error" msgstr "" #: option.c:1207 msgid "bad IPv6 prefix length" msgstr "" #: option.c:1467 msgid "inappropriate vendor:" msgstr "" #: option.c:1474 msgid "inappropriate encap:" msgstr "" #: option.c:1500 msgid "unsupported encapsulation for IPv6 option" msgstr "" #: option.c:1514 msgid "bad dhcp-option" msgstr "" #: option.c:1592 msgid "bad IP address" msgstr "" #: option.c:1595 option.c:1734 option.c:3928 msgid "bad IPv6 address" msgstr "" #: option.c:1688 msgid "bad IPv4 address" msgstr "" #: option.c:1761 option.c:1856 msgid "bad domain in dhcp-option" msgstr "" #: option.c:1900 msgid "dhcp-option too long" msgstr "" #: option.c:1907 msgid "illegal dhcp-match" msgstr "" #: option.c:1966 msgid "illegal repeated flag" msgstr "" #: option.c:1974 msgid "illegal repeated keyword" msgstr "" #: option.c:2056 option.c:5533 #, c-format msgid "cannot access directory %s: %s" msgstr "" #: option.c:2102 tftp.c:573 dump.c:72 #, c-format msgid "cannot access %s: %s" msgstr "" #: option.c:2219 msgid "setting log facility is not possible under Android" msgstr "" #: option.c:2228 msgid "bad log facility" msgstr "" #: option.c:2281 msgid "bad MX preference" msgstr "" #: option.c:2289 msgid "bad MX name" msgstr "" #: option.c:2304 msgid "bad MX target" msgstr "" #: option.c:2324 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts" msgstr "" #: option.c:2328 msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts" msgstr "" #: option.c:2447 msgid "invalid auth-zone" msgstr "" #: option.c:2589 option.c:2621 msgid "bad prefix length" msgstr "" #: option.c:2601 option.c:2642 option.c:2696 msgid "bad prefix" msgstr "" #: option.c:2716 msgid "prefix length too small" msgstr "" #: option.c:3010 msgid "Bad address in --address" msgstr "" #: option.c:3110 msgid "recompile with HAVE_IPSET defined to enable ipset directives" msgstr "" #: option.c:3117 msgid "recompile with HAVE_NFTSET defined to enable nftset directives" msgstr "" #: option.c:3192 option.c:3210 msgid "recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives" msgstr "" #: option.c:3496 msgid "bad port range" msgstr "" #: option.c:3522 msgid "bad bridge-interface" msgstr "" #: option.c:3566 msgid "bad shared-network" msgstr "" #: option.c:3620 msgid "only one tag allowed" msgstr "" #: option.c:3641 option.c:3657 option.c:3783 option.c:3791 option.c:3834 msgid "bad dhcp-range" msgstr "" #: option.c:3675 msgid "inconsistent DHCP range" msgstr "" #: option.c:3741 msgid "prefix length must be exactly 64 for RA subnets" msgstr "" #: option.c:3743 msgid "prefix length must be exactly 64 for subnet constructors" msgstr "" #: option.c:3746 msgid "prefix length must be at least 64" msgstr "" #: option.c:3749 msgid "inconsistent DHCPv6 range" msgstr "" #: option.c:3768 msgid "prefix must be zero with \"constructor:\" argument" msgstr "" #: option.c:3893 option.c:3971 msgid "bad hex constant" msgstr "" #: option.c:3946 msgid "bad IPv6 prefix" msgstr "" #: option.c:3994 #, c-format msgid "duplicate dhcp-host IP address %s" msgstr "" #: option.c:4056 msgid "bad DHCP host name" msgstr "" #: option.c:4142 msgid "bad tag-if" msgstr "" #: option.c:4490 option.c:5046 msgid "invalid port number" msgstr "" #: option.c:4546 msgid "bad dhcp-proxy address" msgstr "" #: option.c:4627 msgid "Bad dhcp-relay" msgstr "" #: option.c:4671 msgid "bad RA-params" msgstr "" #: option.c:4681 msgid "bad DUID" msgstr "" #: option.c:4715 msgid "missing address in alias" msgstr "" #: option.c:4721 msgid "invalid alias range" msgstr "" #: option.c:4770 msgid "missing address in dynamic host" msgstr "" #: option.c:4785 msgid "bad dynamic host" msgstr "" #: option.c:4803 option.c:4819 msgid "bad CNAME" msgstr "" #: option.c:4827 msgid "duplicate CNAME" msgstr "" #: option.c:4854 msgid "bad PTR record" msgstr "" #: option.c:4889 msgid "bad NAPTR record" msgstr "" #: option.c:4925 msgid "bad RR record" msgstr "" #: option.c:4958 msgid "bad CAA record" msgstr "" #: option.c:4987 msgid "bad TXT record" msgstr "" #: option.c:5030 msgid "bad SRV record" msgstr "" #: option.c:5037 msgid "bad SRV target" msgstr "" #: option.c:5056 msgid "invalid priority" msgstr "" #: option.c:5061 msgid "invalid weight" msgstr "" #: option.c:5084 msgid "Bad host-record" msgstr "" #: option.c:5123 msgid "Bad name in host-record" msgstr "" #: option.c:5165 msgid "bad value for dnssec-check-unsigned" msgstr "" #: option.c:5201 msgid "bad trust anchor" msgstr "" #: option.c:5217 msgid "bad HEX in trust anchor" msgstr "" #: option.c:5228 msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)" msgstr "" #: option.c:5290 msgid "missing \"" msgstr "" #: option.c:5347 msgid "bad option" msgstr "" #: option.c:5349 msgid "extraneous parameter" msgstr "" #: option.c:5351 msgid "missing parameter" msgstr "" #: option.c:5353 msgid "illegal option" msgstr "" #: option.c:5363 #, c-format msgid " in output from %s" msgstr "" #: option.c:5365 #, c-format msgid " at line %d of %s" msgstr "" #: option.c:5380 option.c:5683 option.c:5694 #, c-format msgid "read %s" msgstr "" #: option.c:5446 #, c-format msgid "cannot execute %s: %s" msgstr "" #: option.c:5454 option.c:5615 tftp.c:790 #, c-format msgid "cannot read %s: %s" msgstr "" #: option.c:5473 #, c-format msgid "error executing %s: %s" msgstr "" #: option.c:5476 #, c-format msgid "%s returns non-zero error code" msgstr "" #: option.c:5775 msgid "junk found in command line" msgstr "" #: option.c:5815 #, c-format msgid "Dnsmasq version %s %s\n" msgstr "" #: option.c:5816 #, c-format msgid "" "Compile time options: %s\n" "\n" msgstr "" #: option.c:5817 #, c-format msgid "This software comes with ABSOLUTELY NO WARRANTY.\n" msgstr "" #: option.c:5818 #, c-format msgid "Dnsmasq is free software, and you are welcome to redistribute it\n" msgstr "" #: option.c:5819 #, c-format msgid "under the terms of the GNU General Public License, version 2 or 3.\n" msgstr "" #: option.c:5836 msgid "try --help" msgstr "" #: option.c:5838 msgid "try -w" msgstr "" #: option.c:5840 #, c-format msgid "bad command line options: %s" msgstr "" #: option.c:5909 #, c-format msgid "CNAME loop involving %s" msgstr "" #: option.c:5950 #, c-format msgid "cannot get host-name: %s" msgstr "" #: option.c:5978 msgid "only one resolv.conf file allowed in no-poll mode." msgstr "" #: option.c:5988 msgid "must have exactly one resolv.conf to read domain from." msgstr "" #: option.c:5991 network.c:1727 dhcp.c:892 #, c-format msgid "failed to read %s: %s" msgstr "" #: option.c:6008 #, c-format msgid "no search directive found in %s" msgstr "" #: option.c:6029 msgid "there must be a default domain when --dhcp-fqdn is set" msgstr "" #: option.c:6038 msgid "syntax check OK" msgstr "" #: forward.c:107 #, c-format msgid "failed to send packet: %s" msgstr "" #: forward.c:715 msgid "discarding DNS reply: subnet option mismatch" msgstr "" #: forward.c:780 #, c-format msgid "nameserver %s refused to do a recursive query" msgstr "" #: forward.c:826 #, c-format msgid "possible DNS-rebind attack detected: %s" msgstr "" #: forward.c:1239 #, c-format msgid "reducing DNS packet size for nameserver %s to %d" msgstr "" #: forward.c:1565 #, c-format msgid "ignoring query from non-local network %s (logged only once)" msgstr "" #: forward.c:2139 #, c-format msgid "ignoring query from non-local network %s" msgstr "" #: forward.c:2501 #, c-format msgid "failed to bind server socket to %s: %s" msgstr "" #: forward.c:2867 #, c-format msgid "Maximum number of concurrent DNS queries reached (max: %d)" msgstr "" #: forward.c:2869 #, c-format msgid "Maximum number of concurrent DNS queries to %s reached (max: %d)" msgstr "" #: network.c:700 #, c-format msgid "stopped listening on %s(#%d): %s port %d" msgstr "" #: network.c:911 #, c-format msgid "failed to create listening socket for %s: %s" msgstr "" #: network.c:1192 #, c-format msgid "listening on %s(#%d): %s port %d" msgstr "" #: network.c:1219 #, c-format msgid "listening on %s port %d" msgstr "" #: network.c:1252 #, c-format msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s" msgstr "" #: network.c:1259 msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)" msgstr "" #: network.c:1268 #, c-format msgid "warning: using interface %s instead" msgstr "" #: network.c:1277 #, c-format msgid "warning: no addresses found for interface %s" msgstr "" #: network.c:1335 #, c-format msgid "interface %s failed to join DHCPv6 multicast group: %s" msgstr "" #: network.c:1340 msgid "try increasing /proc/sys/net/core/optmem_max" msgstr "" #: network.c:1545 #, c-format msgid "failed to bind server socket for %s: %s" msgstr "" #: network.c:1622 #, c-format msgid "ignoring nameserver %s - local interface" msgstr "" #: network.c:1633 #, c-format msgid "ignoring nameserver %s - cannot make/bind socket: %s" msgstr "" #: network.c:1643 msgid "more servers are defined but not logged" msgstr "" #: network.c:1654 msgid "(no DNSSEC)" msgstr "" #: network.c:1657 msgid "unqualified" msgstr "" #: network.c:1657 msgid "names" msgstr "" #: network.c:1659 msgid "default" msgstr "" #: network.c:1661 msgid "domain" msgstr "" #: network.c:1663 #, c-format msgid "using nameserver %s#%d for %s %s%s %s" msgstr "" #: network.c:1667 #, c-format msgid "NOT using nameserver %s#%d - query loop detected" msgstr "" #: network.c:1670 #, c-format msgid "using nameserver %s#%d(via %s)" msgstr "" #: network.c:1672 #, c-format msgid "using nameserver %s#%d" msgstr "" #: network.c:1687 #, c-format msgid "using only locally-known addresses for %s" msgstr "" #: network.c:1690 #, c-format msgid "using standard nameservers for %s" msgstr "" #: network.c:1694 #, c-format msgid "using %d more local addresses" msgstr "" #: network.c:1696 #, c-format msgid "using %d more nameservers" msgstr "" #: dnsmasq.c:192 msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform" msgstr "" #: dnsmasq.c:207 msgid "no root trust anchor provided for DNSSEC" msgstr "" #: dnsmasq.c:210 msgid "cannot reduce cache size from default when DNSSEC enabled" msgstr "" #: dnsmasq.c:212 msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h" msgstr "" #: dnsmasq.c:218 msgid "TFTP server not available: set HAVE_TFTP in src/config.h" msgstr "" #: dnsmasq.c:225 msgid "cannot use --conntrack AND --query-port" msgstr "" #: dnsmasq.c:231 msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h" msgstr "" #: dnsmasq.c:236 msgid "asynchronous logging is not available under Solaris" msgstr "" #: dnsmasq.c:241 msgid "asynchronous logging is not available under Android" msgstr "" #: dnsmasq.c:246 msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h" msgstr "" #: dnsmasq.c:251 msgid "loop detection not available: set HAVE_LOOP in src/config.h" msgstr "" #: dnsmasq.c:256 msgid "Ubus not available: set HAVE_UBUS in src/config.h" msgstr "" #: dnsmasq.c:267 msgid "max_port cannot be smaller than min_port" msgstr "" #: dnsmasq.c:271 msgid "port_limit must not be larger than available port range" msgstr "" #: dnsmasq.c:278 msgid "--auth-server required when an auth zone is defined." msgstr "" #: dnsmasq.c:283 msgid "zone serial must be configured in --auth-soa" msgstr "" #: dnsmasq.c:303 msgid "dhcp-range constructor not available on this platform" msgstr "" #: dnsmasq.c:377 msgid "cannot set --bind-interfaces and --bind-dynamic" msgstr "" #: dnsmasq.c:380 #, c-format msgid "failed to find list of interfaces: %s" msgstr "" #: dnsmasq.c:389 #, c-format msgid "unknown interface %s" msgstr "" #: dnsmasq.c:396 #, c-format msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s" msgstr "" #: dnsmasq.c:440 msgid "Packet dumps not available: set HAVE_DUMP in src/config.h" msgstr "" #: dnsmasq.c:448 dnsmasq.c:1232 #, c-format msgid "DBus error: %s" msgstr "" #: dnsmasq.c:451 msgid "DBus not available: set HAVE_DBUS in src/config.h" msgstr "" #: dnsmasq.c:459 dnsmasq.c:1253 #, c-format msgid "UBus error: %s" msgstr "" #: dnsmasq.c:462 msgid "UBus not available: set HAVE_UBUS in src/config.h" msgstr "" #: dnsmasq.c:492 #, c-format msgid "unknown user or group: %s" msgstr "" #: dnsmasq.c:568 #, c-format msgid "process is missing required capability %s" msgstr "" #: dnsmasq.c:600 #, c-format msgid "cannot chdir to filesystem root: %s" msgstr "" #: dnsmasq.c:852 #, c-format msgid "started, version %s DNS disabled" msgstr "" #: dnsmasq.c:857 #, c-format msgid "started, version %s cachesize %d" msgstr "" #: dnsmasq.c:859 msgid "cache size greater than 10000 may cause performance issues, and is unlikely to be useful." msgstr "" #: dnsmasq.c:862 #, c-format msgid "started, version %s cache disabled" msgstr "" #: dnsmasq.c:865 msgid "DNS service limited to local subnets" msgstr "" #: dnsmasq.c:868 #, c-format msgid "compile time options: %s" msgstr "" #: dnsmasq.c:877 msgid "DBus support enabled: connected to system bus" msgstr "" #: dnsmasq.c:879 msgid "DBus support enabled: bus connection pending" msgstr "" #: dnsmasq.c:887 msgid "UBus support enabled: connected to system bus" msgstr "" #: dnsmasq.c:889 msgid "UBus support enabled: bus connection pending" msgstr "" #: dnsmasq.c:909 msgid "DNSSEC validation enabled but all unsigned answers are trusted" msgstr "" #: dnsmasq.c:911 msgid "DNSSEC validation enabled" msgstr "" #: dnsmasq.c:915 msgid "DNSSEC signature timestamps not checked until receipt of SIGINT" msgstr "" #: dnsmasq.c:918 msgid "DNSSEC signature timestamps not checked until system time valid" msgstr "" #: dnsmasq.c:921 #, c-format msgid "configured with trust anchor for %s keytag %u" msgstr "" #: dnsmasq.c:927 #, c-format msgid "warning: failed to change owner of %s: %s" msgstr "" #: dnsmasq.c:932 msgid "setting --bind-interfaces option because of OS limitations" msgstr "" #: dnsmasq.c:945 #, c-format msgid "warning: interface %s does not currently exist" msgstr "" #: dnsmasq.c:950 msgid "warning: ignoring resolv-file flag because no-resolv is set" msgstr "" #: dnsmasq.c:953 msgid "warning: no upstream servers configured" msgstr "" #: dnsmasq.c:957 #, c-format msgid "asynchronous logging enabled, queue limit is %d messages" msgstr "" #: dnsmasq.c:978 msgid "IPv6 router advertisement enabled" msgstr "" #: dnsmasq.c:983 #, c-format msgid "DHCP, sockets bound exclusively to interface %s" msgstr "" #: dnsmasq.c:1000 msgid "root is " msgstr "" #: dnsmasq.c:1000 msgid "enabled" msgstr "" #: dnsmasq.c:1002 msgid "secure mode" msgstr "" #: dnsmasq.c:1003 msgid "single port mode" msgstr "" #: dnsmasq.c:1006 #, c-format msgid "warning: %s inaccessible" msgstr "" #: dnsmasq.c:1010 #, c-format msgid "warning: TFTP directory %s inaccessible" msgstr "" #: dnsmasq.c:1036 #, c-format msgid "restricting maximum simultaneous TFTP transfers to %d" msgstr "" #: dnsmasq.c:1095 #, c-format msgid "error binding DHCP socket to device %s" msgstr "" #: dnsmasq.c:1229 msgid "connected to system DBus" msgstr "" #: dnsmasq.c:1250 msgid "connected to system UBus" msgstr "" #: dnsmasq.c:1416 #, c-format msgid "cannot fork into background: %s" msgstr "" #: dnsmasq.c:1420 #, c-format msgid "failed to create helper: %s" msgstr "" #: dnsmasq.c:1424 #, c-format msgid "setting capabilities failed: %s" msgstr "" #: dnsmasq.c:1428 #, c-format msgid "failed to change user-id to %s: %s" msgstr "" #: dnsmasq.c:1432 #, c-format msgid "failed to change group-id to %s: %s" msgstr "" #: dnsmasq.c:1436 #, c-format msgid "failed to open pidfile %s: %s" msgstr "" #: dnsmasq.c:1440 #, c-format msgid "cannot open log %s: %s" msgstr "" #: dnsmasq.c:1444 #, c-format msgid "failed to load Lua script: %s" msgstr "" #: dnsmasq.c:1448 #, c-format msgid "TFTP directory %s inaccessible: %s" msgstr "" #: dnsmasq.c:1452 #, c-format msgid "cannot create timestamp file %s: %s" msgstr "" #: dnsmasq.c:1536 #, c-format msgid "script process killed by signal %d" msgstr "" #: dnsmasq.c:1540 #, c-format msgid "script process exited with status %d" msgstr "" #: dnsmasq.c:1544 #, c-format msgid "failed to execute %s: %s" msgstr "" #: dnsmasq.c:1584 msgid "now checking DNSSEC signature timestamps" msgstr "" #: dnsmasq.c:1619 dnssec.c:160 dnssec.c:204 #, c-format msgid "failed to update mtime on %s: %s" msgstr "" #: dnsmasq.c:1631 msgid "exiting on receipt of SIGTERM" msgstr "" #: dnsmasq.c:1659 #, c-format msgid "failed to access %s: %s" msgstr "" #: dnsmasq.c:1690 #, c-format msgid "reading %s" msgstr "" #: dnsmasq.c:1706 #, c-format msgid "no servers found in %s, will retry" msgstr "" #: dhcp.c:51 #, c-format msgid "cannot create DHCP socket: %s" msgstr "" #: dhcp.c:66 #, c-format msgid "failed to set options on DHCP socket: %s" msgstr "" #: dhcp.c:87 #, c-format msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s" msgstr "" #: dhcp.c:99 #, c-format msgid "failed to bind DHCP server socket: %s" msgstr "" #: dhcp.c:125 #, c-format msgid "cannot create ICMP raw socket: %s." msgstr "" #: dhcp.c:254 dhcp6.c:186 #, c-format msgid "unknown interface %s in bridge-interface" msgstr "" #: dhcp.c:295 #, c-format msgid "DHCP packet received on %s which has no address" msgstr "" #: dhcp.c:429 #, c-format msgid "ARP-cache injection failed: %s" msgstr "" #: dhcp.c:490 #, c-format msgid "Error sending DHCP packet to %s: %s" msgstr "" #: dhcp.c:547 #, c-format msgid "DHCP range %s -- %s is not consistent with netmask %s" msgstr "" #: dhcp.c:930 #, c-format msgid "bad line at %s line %d" msgstr "" #: dhcp.c:973 #, c-format msgid "ignoring %s line %d, duplicate name or IP address" msgstr "" #: dhcp.c:1034 #, c-format msgid "read %s - %d addresses" msgstr "" #: dhcp.c:1136 #, c-format msgid "Cannot broadcast DHCP relay via interface %s" msgstr "" #: dhcp.c:1160 #, c-format msgid "broadcast via %s" msgstr "" #: dhcp.c:1163 rfc3315.c:2219 #, c-format msgid "DHCP relay at %s -> %s" msgstr "" #: lease.c:64 #, c-format msgid "ignoring invalid line in lease database: %s %s %s %s ..." msgstr "" #: lease.c:101 #, c-format msgid "ignoring invalid line in lease database, bad address: %s" msgstr "" #: lease.c:108 msgid "too many stored leases" msgstr "" #: lease.c:176 #, c-format msgid "cannot open or create lease file %s: %s" msgstr "" #: lease.c:185 msgid "failed to parse lease database cleanly" msgstr "" #: lease.c:188 #, c-format msgid "failed to read lease file %s: %s" msgstr "" #: lease.c:204 #, c-format msgid "cannot run lease-init script %s: %s" msgstr "" #: lease.c:210 #, c-format msgid "lease-init script returned exit code %s" msgstr "" #: lease.c:381 #, c-format msgid "failed to write %s: %s (retry in %u s)" msgstr "" #: lease.c:955 #, c-format msgid "Ignoring domain %s for DHCP host name %s" msgstr "" #: rfc2131.c:378 msgid "with subnet selector" msgstr "" #: rfc2131.c:383 msgid "via" msgstr "" #: rfc2131.c:389 #, c-format msgid "no address range available for DHCP request %s %s" msgstr "" #: rfc2131.c:403 #, c-format msgid "%u available DHCP subnet: %s/%s" msgstr "" #: rfc2131.c:409 rfc3315.c:320 #, c-format msgid "%u available DHCP range: %s -- %s" msgstr "" #: rfc2131.c:521 #, c-format msgid "%u vendor class: %s" msgstr "" #: rfc2131.c:523 #, c-format msgid "%u user class: %s" msgstr "" #: rfc2131.c:557 msgid "disabled" msgstr "" #: rfc2131.c:598 rfc2131.c:1087 rfc2131.c:1536 rfc3315.c:633 rfc3315.c:816 #: rfc3315.c:1122 msgid "ignored" msgstr "" #: rfc2131.c:613 rfc2131.c:1340 rfc3315.c:868 msgid "address in use" msgstr "" #: rfc2131.c:627 rfc2131.c:1141 msgid "no address available" msgstr "" #: rfc2131.c:634 rfc2131.c:1302 msgid "wrong network" msgstr "" #: rfc2131.c:649 msgid "no address configured" msgstr "" #: rfc2131.c:655 rfc2131.c:1353 msgid "no leases left" msgstr "" #: rfc2131.c:756 rfc3315.c:500 #, c-format msgid "%u client provides name: %s" msgstr "" #: rfc2131.c:885 msgid "PXE BIS not supported" msgstr "" #: rfc2131.c:1054 rfc3315.c:1223 #, c-format msgid "disabling DHCP static address %s for %s" msgstr "" #: rfc2131.c:1075 msgid "unknown lease" msgstr "" #: rfc2131.c:1110 #, c-format msgid "not using configured address %s because it is leased to %s" msgstr "" #: rfc2131.c:1120 #, c-format msgid "not using configured address %s because it is in use by the server or relay" msgstr "" #: rfc2131.c:1123 #, c-format msgid "not using configured address %s because it was previously declined" msgstr "" #: rfc2131.c:1139 rfc2131.c:1346 msgid "no unique-id" msgstr "" #: rfc2131.c:1238 msgid "wrong server-ID" msgstr "" #: rfc2131.c:1257 msgid "wrong address" msgstr "" #: rfc2131.c:1275 rfc3315.c:976 msgid "lease not found" msgstr "" #: rfc2131.c:1310 msgid "address not available" msgstr "" #: rfc2131.c:1321 msgid "static lease available" msgstr "" #: rfc2131.c:1325 msgid "address reserved" msgstr "" #: rfc2131.c:1334 #, c-format msgid "abandoning lease to %s of %s" msgstr "" #: rfc2131.c:1870 #, c-format msgid "%u bootfile name: %s" msgstr "" #: rfc2131.c:1879 #, c-format msgid "%u server name: %s" msgstr "" #: rfc2131.c:1889 #, c-format msgid "%u next server: %s" msgstr "" #: rfc2131.c:1893 #, c-format msgid "%u broadcast response" msgstr "" #: rfc2131.c:1956 #, c-format msgid "cannot send DHCP/BOOTP option %d: no space left in packet" msgstr "" #: rfc2131.c:2267 msgid "PXE menu too large" msgstr "" #: rfc2131.c:2430 rfc3315.c:1517 #, c-format msgid "%u requested options: %s" msgstr "" #: rfc2131.c:2747 #, c-format msgid "cannot send RFC3925 option: too many options for enterprise number %d" msgstr "" #: rfc2131.c:2810 #, c-format msgid "%u reply delay: %d" msgstr "" #: netlink.c:86 #, c-format msgid "cannot create netlink socket: %s" msgstr "" #: netlink.c:379 #, c-format msgid "netlink returns error: %s" msgstr "" #: dbus.c:491 #, c-format msgid "Enabling --%s option from D-Bus" msgstr "" #: dbus.c:496 #, c-format msgid "Disabling --%s option from D-Bus" msgstr "" #: dbus.c:857 msgid "setting upstream servers from DBus" msgstr "" #: dbus.c:907 msgid "could not register a DBus message handler" msgstr "" #: bpf.c:261 #, c-format msgid "cannot create DHCP BPF socket: %s" msgstr "" #: bpf.c:289 #, c-format msgid "DHCP request for unsupported hardware type (%d) received on %s" msgstr "" #: bpf.c:374 #, c-format msgid "cannot create PF_ROUTE socket: %s" msgstr "" #: bpf.c:395 msgid "Unknown protocol version from route socket" msgstr "" #: helper.c:150 msgid "lease() function missing in Lua script" msgstr "" #: tftp.c:353 msgid "unable to get free port for TFTP" msgstr "" #: tftp.c:369 #, c-format msgid "unsupported request from %s" msgstr "" #: tftp.c:520 #, c-format msgid "file %s not found for %s" msgstr "" #: tftp.c:609 #, c-format msgid "ignoring packet from %s (TID mismatch)" msgstr "" #: tftp.c:662 #, c-format msgid "failed sending %s to %s" msgstr "" #: tftp.c:662 #, c-format msgid "sent %s to %s" msgstr "" #: tftp.c:712 #, c-format msgid "error %d %s received from %s" msgstr "" #: log.c:203 #, c-format msgid "overflow: %d log entries lost" msgstr "" #: log.c:281 #, c-format msgid "log failed: %s" msgstr "" #: log.c:490 msgid "FAILED to start up" msgstr "" #: conntrack.c:63 #, c-format msgid "Conntrack connection mark retrieval failed: %s" msgstr "" #: dhcp6.c:51 #, c-format msgid "cannot create DHCPv6 socket: %s" msgstr "" #: dhcp6.c:72 #, c-format msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s" msgstr "" #: dhcp6.c:84 #, c-format msgid "failed to bind DHCPv6 server socket: %s" msgstr "" #: rfc3315.c:173 #, c-format msgid "no address range available for DHCPv6 request from relay at %s" msgstr "" #: rfc3315.c:182 #, c-format msgid "no address range available for DHCPv6 request via %s" msgstr "" #: rfc3315.c:317 #, c-format msgid "%u available DHCPv6 subnet: %s/%d" msgstr "" #: rfc3315.c:400 #, c-format msgid "%u vendor class: %u" msgstr "" #: rfc3315.c:448 #, c-format msgid "%u client MAC address: %s" msgstr "" #: rfc3315.c:763 rfc3315.c:860 msgid "address unavailable" msgstr "" #: rfc3315.c:775 rfc3315.c:904 rfc3315.c:1273 msgid "success" msgstr "" #: rfc3315.c:790 rfc3315.c:799 rfc3315.c:912 rfc3315.c:914 rfc3315.c:1048 msgid "no addresses available" msgstr "" #: rfc3315.c:891 msgid "not on link" msgstr "" #: rfc3315.c:980 rfc3315.c:1181 rfc3315.c:1262 msgid "no binding found" msgstr "" #: rfc3315.c:1017 msgid "deprecated" msgstr "" #: rfc3315.c:1024 msgid "address invalid" msgstr "" #: rfc3315.c:1082 rfc3315.c:1084 msgid "confirm failed" msgstr "" #: rfc3315.c:1099 msgid "all addresses still on link" msgstr "" #: rfc3315.c:1190 msgid "release received" msgstr "" #: rfc3315.c:2200 #, c-format msgid "Cannot multicast DHCP relay via interface %s" msgstr "" #: rfc3315.c:2216 #, c-format msgid "multicast via %s" msgstr "" #: dhcp-common.c:187 #, c-format msgid "Ignoring duplicate dhcp-option %d" msgstr "" #: dhcp-common.c:264 #, c-format msgid "%u tags: %s" msgstr "" #: dhcp-common.c:484 #, c-format msgid "%s has more than one address in hostsfile, using %s for DHCP" msgstr "" #: dhcp-common.c:518 #, c-format msgid "duplicate IP address %s (%s) in dhcp-config directive" msgstr "" #: dhcp-common.c:738 #, c-format msgid "Known DHCP options:\n" msgstr "" #: dhcp-common.c:749 #, c-format msgid "Known DHCPv6 options:\n" msgstr "" #: dhcp-common.c:946 msgid ", prefix deprecated" msgstr "" #: dhcp-common.c:949 #, c-format msgid ", lease time " msgstr "" #: dhcp-common.c:991 #, c-format msgid "%s stateless on %s%.0s%.0s%s" msgstr "" #: dhcp-common.c:993 #, c-format msgid "%s, static leases only on %.0s%s%s%.0s" msgstr "" #: dhcp-common.c:995 #, c-format msgid "%s, proxy on subnet %.0s%s%.0s%.0s" msgstr "" #: dhcp-common.c:996 #, c-format msgid "%s, IP range %s -- %s%s%.0s" msgstr "" #: dhcp-common.c:1009 #, c-format msgid "DHCPv4-derived IPv6 names on %s%s" msgstr "" #: dhcp-common.c:1012 #, c-format msgid "router advertisement on %s%s" msgstr "" #: dhcp-common.c:1043 #, c-format msgid "DHCP relay from %s via %s" msgstr "" #: dhcp-common.c:1045 #, c-format msgid "DHCP relay from %s to %s via %s" msgstr "" #: dhcp-common.c:1048 #, c-format msgid "DHCP relay from %s to %s" msgstr "" #: radv.c:110 #, c-format msgid "cannot create ICMPv6 socket: %s" msgstr "" #: auth.c:462 #, c-format msgid "ignoring zone transfer request from %s" msgstr "" #: ipset.c:99 #, c-format msgid "failed to create IPset control socket: %s" msgstr "" #: ipset.c:211 #, c-format msgid "failed to update ipset %s: %s" msgstr "" #: pattern.c:29 #, c-format msgid "[pattern.c:%d] Assertion failure: %s" msgstr "" #: pattern.c:142 #, c-format msgid "Invalid DNS name: Invalid character %c." msgstr "" #: pattern.c:151 msgid "Invalid DNS name: Empty label." msgstr "" #: pattern.c:156 msgid "Invalid DNS name: Label starts with hyphen." msgstr "" #: pattern.c:170 msgid "Invalid DNS name: Label ends with hyphen." msgstr "" #: pattern.c:176 #, c-format msgid "Invalid DNS name: Label is too long (%zu)." msgstr "" #: pattern.c:184 #, c-format msgid "Invalid DNS name: Not enough labels (%zu)." msgstr "" #: pattern.c:189 msgid "Invalid DNS name: Final label is fully numeric." msgstr "" #: pattern.c:199 msgid "Invalid DNS name: \"local\" pseudo-TLD." msgstr "" #: pattern.c:204 #, c-format msgid "DNS name has invalid length (%zu)." msgstr "" #: pattern.c:258 #, c-format msgid "Invalid DNS name pattern: Invalid character %c." msgstr "" #: pattern.c:267 msgid "Invalid DNS name pattern: Empty label." msgstr "" #: pattern.c:272 msgid "Invalid DNS name pattern: Label starts with hyphen." msgstr "" #: pattern.c:285 msgid "Invalid DNS name pattern: Wildcard character used more than twice per label." msgstr "" #: pattern.c:295 msgid "Invalid DNS name pattern: Label ends with hyphen." msgstr "" #: pattern.c:301 #, c-format msgid "Invalid DNS name pattern: Label is too long (%zu)." msgstr "" #: pattern.c:309 #, c-format msgid "Invalid DNS name pattern: Not enough labels (%zu)." msgstr "" #: pattern.c:314 msgid "Invalid DNS name pattern: Wildcard within final two labels." msgstr "" #: pattern.c:319 msgid "Invalid DNS name pattern: Final label is fully numeric." msgstr "" #: pattern.c:329 msgid "Invalid DNS name pattern: \"local\" pseudo-TLD." msgstr "" #: pattern.c:334 #, c-format msgid "DNS name pattern has invalid length after removing wildcards (%zu)." msgstr "" #: dnssec.c:206 msgid "system time considered valid, now checking DNSSEC signature timestamps." msgstr "" #: dnssec.c:1018 #, c-format msgid "Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support" msgstr "" #: blockdata.c:55 #, c-format msgid "pool memory in use %zu, max %zu, allocated %zu" msgstr "" #: tables.c:61 #, c-format msgid "failed to access pf devices: %s" msgstr "" #: tables.c:74 #, c-format msgid "warning: no opened pf devices %s" msgstr "" #: tables.c:82 #, c-format msgid "error: cannot use table name %s" msgstr "" #: tables.c:90 #, c-format msgid "error: cannot strlcpy table name %s" msgstr "" #: tables.c:101 #, c-format msgid "IPset: error: %s" msgstr "" #: tables.c:108 msgid "info: table created" msgstr "" #: tables.c:133 #, c-format msgid "warning: DIOCR%sADDRS: %s" msgstr "" #: tables.c:137 #, c-format msgid "%d addresses %s" msgstr "" #: inotify.c:62 #, c-format msgid "cannot access path %s: %s" msgstr "" #: inotify.c:95 #, c-format msgid "failed to create inotify: %s" msgstr "" #: inotify.c:111 #, c-format msgid "too many symlinks following %s" msgstr "" #: inotify.c:127 #, c-format msgid "directory %s for resolv-file is missing, cannot poll" msgstr "" #: inotify.c:131 inotify.c:200 #, c-format msgid "failed to create inotify for %s: %s" msgstr "" #: inotify.c:178 inotify.c:185 #, c-format msgid "bad dynamic directory %s: %s" msgstr "" #: inotify.c:186 msgid "not a directory" msgstr "" #: inotify.c:299 #, c-format msgid "inotify: %s removed" msgstr "" #: inotify.c:301 #, c-format msgid "inotify: %s new or modified" msgstr "" #: inotify.c:309 #, c-format msgid "inotify: flushed %u names read from %s" msgstr "" #: dump.c:68 #, c-format msgid "cannot create %s: %s" msgstr "" #: dump.c:74 #, c-format msgid "bad header in %s" msgstr "" #: dump.c:287 msgid "failed to write packet dump" msgstr "" #: dump.c:289 #, c-format msgid "%u dumping packet %u mask 0x%04x" msgstr "" #: dump.c:291 #, c-format msgid "dumping packet %u mask 0x%04x" msgstr "" #: ubus.c:79 #, c-format msgid "UBus subscription callback: %s subscriber(s)" msgstr "" #: ubus.c:99 #, c-format msgid "Cannot reconnect to UBus: %s" msgstr "" #: ubus.c:135 msgid "Cannot set UBus listeners: no connection" msgstr "" #: ubus.c:155 msgid "Cannot poll UBus listeners: no connection" msgstr "" #: ubus.c:168 msgid "Disconnecting from UBus" msgstr "" #: ubus.c:179 ubus.c:326 #, c-format msgid "UBus command failed: %d (%s)" msgstr "" #: hash-questions.c:40 msgid "Failed to create SHA-256 hash object" msgstr "" #: nftset.c:35 msgid "failed to create nftset context" msgstr "" dnsmasq-2.91/po/pt_BR.po0000664000175000017500000014053614765043257013307 0ustar srksrk# Portuguese translations for dnsmasq package. # This file is put in the public domain. # Simon Kelley , 2006. # msgid "" msgstr "" "Project-Id-Version: dnsmasq 2.26\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-06-18 12:24+0100\n" "PO-Revision-Date: 2017-07-17 18:30+0100\n" "Last-Translator: Simon Kelley \n" "Language-Team: Portuguese \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:652 msgid "Internal error in cache." msgstr "" #: cache.c:1179 #, c-format msgid "failed to load names from %s: %s" msgstr "" #: cache.c:1201 dhcp.c:943 #, c-format msgid "bad address at %s line %d" msgstr "" #: cache.c:1254 dhcp.c:959 #, c-format msgid "bad name at %s line %d" msgstr "" #: cache.c:1265 #, c-format msgid "read %s - %d names" msgstr "" #: cache.c:1381 msgid "cleared cache" msgstr "" #: cache.c:1445 #, c-format msgid "No IPv4 address found for %s" msgstr "" #: cache.c:1491 #, c-format msgid "%s is a CNAME, not giving it to the DHCP lease of %s" msgstr "" #: cache.c:1515 #, 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:1760 #, c-format msgid "time %lu" msgstr "" #: cache.c:1761 #, c-format msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries." msgstr "" #: cache.c:1763 #, c-format msgid "queries forwarded %u, queries answered locally %u" msgstr "" #: cache.c:1766 #, c-format msgid "queries answered from stale cache %u" msgstr "" #: cache.c:1768 #, c-format msgid "queries for authoritative zones %u" msgstr "" #: cache.c:1796 #, c-format msgid "server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums" msgstr "" #: util.c:51 #, c-format msgid "failed to seed the random number generator: %s" msgstr "" #: util.c:246 msgid "failed to allocate memory" msgstr "" #: util.c:305 option.c:696 msgid "could not get memory" msgstr "" #: util.c:326 #, c-format msgid "cannot create pipe: %s" msgstr "" #: util.c:334 #, c-format msgid "failed to allocate %d bytes" msgstr "" #: util.c:344 #, c-format msgid "failed to reallocate %d bytes" msgstr "" #: util.c:465 #, c-format msgid "cannot read monotonic clock: %s" msgstr "" #: util.c:579 #, c-format msgid "infinite" msgstr "" #: util.c:867 #, c-format msgid "failed to find kernel version: %s" msgstr "" #: option.c:393 msgid "Specify local address(es) to listen on." msgstr "" #: option.c:394 msgid "Return ipaddr for all hosts in specified domains." msgstr "" #: option.c:395 msgid "Fake reverse lookups for RFC1918 private address ranges." msgstr "" #: option.c:396 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)." msgstr "" #: option.c:397 #, c-format msgid "Specify the size of the cache in entries (defaults to %s)." msgstr "" #: option.c:398 #, c-format msgid "Specify configuration file (defaults to %s)." msgstr "" #: option.c:399 msgid "Do NOT fork into the background: run in debug mode." msgstr "" #: option.c:400 msgid "Do NOT forward queries with no domain part." msgstr "" #: option.c:401 msgid "Return self-pointing MX records for local hosts." msgstr "" #: option.c:402 msgid "Expand simple names in /etc/hosts with domain-suffix." msgstr "" #: option.c:403 msgid "Don't forward spurious DNS requests from Windows hosts." msgstr "" #: option.c:404 msgid "Don't include IPv4 addresses in DNS answers." msgstr "" #: option.c:405 msgid "Don't include IPv6 addresses in DNS answers." msgstr "" #: option.c:406 msgid "Enable DHCP in the range given with lease duration." msgstr "" #: option.c:407 #, c-format msgid "Change to this group after startup (defaults to %s)." msgstr "" #: option.c:408 msgid "Set address or hostname for a specified machine." msgstr "" #: option.c:409 msgid "Read DHCP host specs from file." msgstr "" #: option.c:410 msgid "Read DHCP option specs from file." msgstr "" #: option.c:411 msgid "Read DHCP host specs from a directory." msgstr "" #: option.c:412 msgid "Read DHCP options from a directory." msgstr "" #: option.c:413 msgid "Evaluate conditional tag expression." msgstr "" #: option.c:414 #, c-format msgid "Do NOT load %s file." msgstr "" #: option.c:415 #, c-format msgid "Specify a hosts file to be read in addition to %s." msgstr "" #: option.c:416 msgid "Read hosts files from a directory." msgstr "" #: option.c:417 msgid "Specify interface(s) to listen on." msgstr "" #: option.c:418 msgid "Specify interface(s) NOT to listen on." msgstr "" #: option.c:419 msgid "Map DHCP user class to tag." msgstr "" #: option.c:420 msgid "Map RFC3046 circuit-id to tag." msgstr "" #: option.c:421 msgid "Map RFC3046 remote-id to tag." msgstr "" #: option.c:422 msgid "Map RFC3993 subscriber-id to tag." msgstr "" #: option.c:423 msgid "Specify vendor class to match for PXE requests." msgstr "" #: option.c:424 msgid "Don't do DHCP for hosts with tag set." msgstr "" #: option.c:425 msgid "Force broadcast replies for hosts with tag set." msgstr "" #: option.c:426 msgid "Do NOT fork into the background, do NOT run in debug mode." msgstr "" #: option.c:427 msgid "Assume we are the only DHCP server on the local network." msgstr "" #: option.c:428 #, c-format msgid "Specify where to store DHCP leases (defaults to %s)." msgstr "" #: option.c:429 msgid "Return MX records for local hosts." msgstr "" #: option.c:430 msgid "Specify an MX record." msgstr "" #: option.c:431 msgid "Specify BOOTP options to DHCP server." msgstr "" #: option.c:432 #, c-format msgid "Do NOT poll %s file, reload only on SIGHUP." msgstr "" #: option.c:433 msgid "Do NOT cache failed search results." msgstr "" #: option.c:434 msgid "Use expired cache data for faster reply." msgstr "" #: option.c:435 #, c-format msgid "Use nameservers strictly in the order given in %s." msgstr "" #: option.c:436 msgid "Specify options to be sent to DHCP clients." msgstr "" #: option.c:437 msgid "DHCP option sent even if the client does not request it." msgstr "" #: option.c:438 msgid "Specify port to listen for DNS requests on (defaults to 53)." msgstr "" #: option.c:439 #, c-format msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)." msgstr "" #: option.c:440 msgid "Log DNS queries." msgstr "" #: option.c:441 msgid "Force the originating port for upstream DNS queries." msgstr "" #: option.c:442 msgid "Set maximum number of random originating ports for a query." msgstr "" #: option.c:443 msgid "Do NOT read resolv.conf." msgstr "" #: option.c:444 #, c-format msgid "Specify path to resolv.conf (defaults to %s)." msgstr "" #: option.c:445 msgid "Specify path to file with server= options" msgstr "" #: option.c:446 msgid "Specify address(es) of upstream servers with optional domains." msgstr "" #: option.c:447 msgid "Specify address of upstream servers for reverse address queries" msgstr "" #: option.c:448 msgid "Never forward queries to specified domains." msgstr "" #: option.c:449 msgid "Specify the domain to be assigned in DHCP leases." msgstr "" #: option.c:450 msgid "Specify default target in an MX record." msgstr "" #: option.c:451 msgid "Specify time-to-live in seconds for replies from /etc/hosts." msgstr "" #: option.c:452 msgid "Specify time-to-live in seconds for negative caching." msgstr "" #: option.c:453 msgid "Specify time-to-live in seconds for maximum TTL to send to clients." msgstr "" #: option.c:454 msgid "Specify time-to-live ceiling for cache." msgstr "" #: option.c:455 msgid "Specify time-to-live floor for cache." msgstr "" #: option.c:456 msgid "Retry DNS queries after this many milliseconds." msgstr "" #: option.c:457 #, c-format msgid "Change to this user after startup. (defaults to %s)." msgstr "" #: option.c:458 msgid "Map DHCP vendor class to tag." msgstr "" #: option.c:459 msgid "Display dnsmasq version and copyright information." msgstr "" #: option.c:460 msgid "Translate IPv4 addresses from upstream servers." msgstr "" #: option.c:461 msgid "Specify a SRV record." msgstr "" #: option.c:462 msgid "Display this message. Use --help dhcp or --help dhcp6 for known DHCP options." msgstr "" #: option.c:463 #, c-format msgid "Specify path of PID file (defaults to %s)." msgstr "" #: option.c:464 #, c-format msgid "Specify maximum number of DHCP leases (defaults to %s)." msgstr "" #: option.c:465 msgid "Answer DNS queries based on the interface a query was sent to." msgstr "" #: option.c:466 msgid "Specify TXT DNS record." msgstr "" #: option.c:467 msgid "Specify PTR DNS record." msgstr "" #: option.c:468 msgid "Give DNS name to IPv4 address of interface." msgstr "" #: option.c:469 msgid "Bind only to interfaces in use." msgstr "" #: option.c:470 #, c-format msgid "Read DHCP static host information from %s." msgstr "" #: option.c:471 msgid "Enable the DBus interface for setting upstream servers, etc." msgstr "" #: option.c:472 msgid "Enable the UBus interface." msgstr "" #: option.c:473 msgid "Do not provide DHCP on this interface, only provide DNS." msgstr "" #: option.c:474 msgid "Enable dynamic address allocation for bootp." msgstr "" #: option.c:475 msgid "Map MAC address (with wildcards) to option set." msgstr "" #: option.c:476 msgid "Treat DHCP requests on aliases as arriving from interface." msgstr "" #: option.c:477 msgid "Specify extra networks sharing a broadcast domain for DHCP" msgstr "" #: option.c:478 msgid "Disable ICMP echo address checking in the DHCP server." msgstr "" #: option.c:479 msgid "Shell script to run on DHCP lease creation and destruction." msgstr "" #: option.c:480 msgid "Lua script to run on DHCP lease creation and destruction." msgstr "" #: option.c:481 msgid "Run lease-change scripts as this user." msgstr "" #: option.c:482 msgid "Call dhcp-script with changes to local ARP table." msgstr "" #: option.c:483 msgid "Read configuration from all the files in this directory." msgstr "" #: option.c:484 msgid "Execute file and read configuration from stdin." msgstr "" #: option.c:485 msgid "Log to this syslog facility or file. (defaults to DAEMON)" msgstr "" #: option.c:486 msgid "Do not use leasefile." msgstr "" #: option.c:487 #, c-format msgid "Maximum number of concurrent DNS queries. (defaults to %s)" msgstr "" #: option.c:488 #, c-format msgid "Clear DNS cache when reloading %s." msgstr "" #: option.c:489 msgid "Ignore hostnames provided by DHCP clients." msgstr "" #: option.c:490 msgid "Do NOT reuse filename and server fields for extra DHCP options." msgstr "" #: option.c:491 msgid "Enable integrated read-only TFTP server." msgstr "" #: option.c:492 msgid "Export files by TFTP only from the specified subtree." msgstr "" #: option.c:493 msgid "Add client IP or hardware address to tftp-root." msgstr "" #: option.c:494 msgid "Allow access only to files owned by the user running dnsmasq." msgstr "" #: option.c:495 msgid "Do not terminate the service if TFTP directories are inaccessible." msgstr "" #: option.c:496 #, c-format msgid "Maximum number of concurrent TFTP transfers (defaults to %s)." msgstr "" #: option.c:497 msgid "Maximum MTU to use for TFTP transfers." msgstr "" #: option.c:498 msgid "Disable the TFTP blocksize extension." msgstr "" #: option.c:499 msgid "Convert TFTP filenames to lowercase" msgstr "" #: option.c:500 msgid "Ephemeral port range for use by TFTP transfers." msgstr "" #: option.c:501 msgid "Use only one port for TFTP server." msgstr "" #: option.c:502 msgid "Extra logging for DHCP." msgstr "" #: option.c:503 msgid "Enable async. logging; optionally set queue length." msgstr "" #: option.c:504 msgid "Stop DNS rebinding. Filter private IP ranges when resolving." msgstr "" #: option.c:505 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers." msgstr "" #: option.c:506 msgid "Inhibit DNS-rebind protection on this domain." msgstr "" #: option.c:507 msgid "Always perform DNS queries to all servers." msgstr "" #: option.c:508 msgid "Set tag if client includes matching option in request." msgstr "" #: option.c:509 msgid "Set tag if client provides given name." msgstr "" #: option.c:510 msgid "Use alternative ports for DHCP." msgstr "" #: option.c:511 msgid "Specify NAPTR DNS record." msgstr "" #: option.c:512 msgid "Specify lowest port available for DNS query transmission." msgstr "" #: option.c:513 msgid "Specify highest port available for DNS query transmission." msgstr "" #: option.c:514 msgid "Use only fully qualified domain names for DHCP clients." msgstr "" #: option.c:515 msgid "Generate hostnames based on MAC address for nameless clients." msgstr "" #: option.c:516 msgid "Use these DHCP relays as full proxies." msgstr "" #: option.c:517 msgid "Relay DHCP requests to a remote server" msgstr "" #: option.c:518 msgid "Specify alias name for LOCAL DNS name." msgstr "" #: option.c:519 msgid "Prompt to send to PXE clients." msgstr "" #: option.c:520 msgid "Boot service for PXE menu." msgstr "" #: option.c:521 msgid "Check configuration syntax." msgstr "" #: option.c:522 msgid "Add requestor's MAC address to forwarded DNS queries." msgstr "" #: option.c:523 msgid "Strip MAC information from queries." msgstr "" #: option.c:524 msgid "Add specified IP subnet to forwarded DNS queries." msgstr "" #: option.c:525 msgid "Strip ECS information from queries." msgstr "" #: option.c:526 msgid "Add client identification to forwarded DNS queries." msgstr "" #: option.c:527 msgid "Proxy DNSSEC validation results from upstream nameservers." msgstr "" #: option.c:528 msgid "Attempt to allocate sequential IP addresses to DHCP clients." msgstr "" #: option.c:529 msgid "Ignore client identifier option sent by DHCP clients." msgstr "" #: option.c:530 msgid "Copy connection-track mark from queries to upstream connections." msgstr "" #: option.c:531 msgid "Allow DHCP clients to do their own DDNS updates." msgstr "" #: option.c:532 msgid "Send router-advertisements for interfaces doing DHCPv6" msgstr "" #: option.c:533 msgid "Specify DUID_EN-type DHCPv6 server DUID" msgstr "" #: option.c:534 msgid "Specify host (A/AAAA and PTR) records" msgstr "" #: option.c:535 msgid "Specify host record in interface subnet" msgstr "" #: option.c:536 msgid "Specify certification authority authorization record" msgstr "" #: option.c:537 msgid "Specify arbitrary DNS resource record" msgstr "" #: option.c:538 msgid "Bind to interfaces in use - check for new interfaces" msgstr "" #: option.c:539 msgid "Export local names to global DNS" msgstr "" #: option.c:540 msgid "Domain to export to global DNS" msgstr "" #: option.c:541 msgid "Set TTL for authoritative replies" msgstr "" #: option.c:542 msgid "Set authoritative zone information" msgstr "" #: option.c:543 msgid "Secondary authoritative nameservers for forward domains" msgstr "" #: option.c:544 msgid "Peers which are allowed to do zone transfer" msgstr "" #: option.c:545 msgid "Specify ipsets to which matching domains should be added" msgstr "" #: option.c:546 msgid "Specify nftables sets to which matching domains should be added" msgstr "" #: option.c:547 msgid "Enable filtering of DNS queries with connection-track marks." msgstr "" #: option.c:548 msgid "Set allowed DNS patterns for a connection-track mark." msgstr "" #: option.c:549 msgid "Specify a domain and address range for synthesised names" msgstr "" #: option.c:550 msgid "Activate DNSSEC validation" msgstr "" #: option.c:551 msgid "Specify trust anchor key digest." msgstr "" #: option.c:552 msgid "Disable upstream checking for DNSSEC debugging." msgstr "" #: option.c:553 msgid "Ensure answers without DNSSEC are in unsigned zones." msgstr "" #: option.c:554 msgid "Don't check DNSSEC signature timestamps until first cache-reload" msgstr "" #: option.c:555 msgid "Timestamp file to verify system clock for DNSSEC" msgstr "" #: option.c:556 msgid "Set MTU, priority, resend-interval and router-lifetime" msgstr "" #: option.c:557 msgid "Do not log routine DHCP." msgstr "" #: option.c:558 msgid "Do not log routine DHCPv6." msgstr "" #: option.c:559 msgid "Do not log RA." msgstr "" #: option.c:560 msgid "Log debugging information." msgstr "" #: option.c:561 msgid "Accept queries only from directly-connected networks." msgstr "" #: option.c:562 msgid "Detect and remove DNS forwarding loops." msgstr "" #: option.c:563 msgid "Ignore DNS responses containing ipaddr." msgstr "" #: option.c:564 msgid "Set TTL in DNS responses with DHCP-derived addresses." msgstr "" #: option.c:565 msgid "Delay DHCP replies for at least number of seconds." msgstr "" #: option.c:566 msgid "Enables DHCPv4 Rapid Commit option." msgstr "" #: option.c:567 msgid "Path to debug packet dump file" msgstr "" #: option.c:568 msgid "Mask which packets to dump" msgstr "" #: option.c:569 msgid "Call dhcp-script when lease expiry changes." msgstr "" #: option.c:570 msgid "Send Cisco Umbrella identifiers including remote IP." msgstr "" #: option.c:571 msgid "Do not log routine TFTP." msgstr "" #: option.c:572 msgid "Suppress round-robin ordering of DNS records." msgstr "" #: option.c:802 #, c-format msgid "" "Usage: dnsmasq [options]\n" "\n" msgstr "" #: option.c:804 #, c-format msgid "Use short options only on the command line.\n" msgstr "" #: option.c:806 #, c-format msgid "Valid options are:\n" msgstr "" #: option.c:853 option.c:1055 msgid "bad address" msgstr "" #: option.c:882 option.c:886 msgid "bad port" msgstr "" #: option.c:899 option.c:1002 option.c:1048 msgid "interface binding not supported" msgstr "" #: option.c:955 msgid "Cannot resolve server name" msgstr "" #: option.c:991 msgid "cannot use IPv4 server address with IPv6 source address" msgstr "" #: option.c:997 option.c:1043 msgid "interface can only be specified once" msgstr "" #: option.c:1011 option.c:4785 msgid "bad interface name" msgstr "" #: option.c:1037 msgid "cannot use IPv6 server address with IPv4 source address" msgstr "" #: option.c:1124 msgid "bad IPv4 prefix length" msgstr "" #: option.c:1155 option.c:1165 option.c:1240 option.c:1250 option.c:5360 msgid "error" msgstr "" #: option.c:1207 msgid "bad IPv6 prefix length" msgstr "" #: option.c:1467 msgid "inappropriate vendor:" msgstr "" #: option.c:1474 msgid "inappropriate encap:" msgstr "" #: option.c:1500 msgid "unsupported encapsulation for IPv6 option" msgstr "" #: option.c:1514 msgid "bad dhcp-option" msgstr "" #: option.c:1592 msgid "bad IP address" msgstr "" #: option.c:1595 option.c:1734 option.c:3928 msgid "bad IPv6 address" msgstr "" #: option.c:1688 msgid "bad IPv4 address" msgstr "" #: option.c:1761 option.c:1856 msgid "bad domain in dhcp-option" msgstr "" #: option.c:1900 msgid "dhcp-option too long" msgstr "" #: option.c:1907 msgid "illegal dhcp-match" msgstr "" #: option.c:1966 msgid "illegal repeated flag" msgstr "" #: option.c:1974 msgid "illegal repeated keyword" msgstr "" #: option.c:2056 option.c:5533 #, c-format msgid "cannot access directory %s: %s" msgstr "" #: option.c:2102 tftp.c:573 dump.c:72 #, c-format msgid "cannot access %s: %s" msgstr "" #: option.c:2219 msgid "setting log facility is not possible under Android" msgstr "" #: option.c:2228 msgid "bad log facility" msgstr "" #: option.c:2281 msgid "bad MX preference" msgstr "" #: option.c:2289 msgid "bad MX name" msgstr "" #: option.c:2304 msgid "bad MX target" msgstr "" #: option.c:2324 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts" msgstr "" #: option.c:2328 msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts" msgstr "" #: option.c:2447 msgid "invalid auth-zone" msgstr "" #: option.c:2589 option.c:2621 msgid "bad prefix length" msgstr "" #: option.c:2601 option.c:2642 option.c:2696 msgid "bad prefix" msgstr "" #: option.c:2716 msgid "prefix length too small" msgstr "" #: option.c:3010 msgid "Bad address in --address" msgstr "" #: option.c:3110 msgid "recompile with HAVE_IPSET defined to enable ipset directives" msgstr "" #: option.c:3117 msgid "recompile with HAVE_NFTSET defined to enable nftset directives" msgstr "" #: option.c:3192 option.c:3210 msgid "recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives" msgstr "" #: option.c:3496 msgid "bad port range" msgstr "" #: option.c:3522 msgid "bad bridge-interface" msgstr "" #: option.c:3566 msgid "bad shared-network" msgstr "" #: option.c:3620 msgid "only one tag allowed" msgstr "" #: option.c:3641 option.c:3657 option.c:3783 option.c:3791 option.c:3834 msgid "bad dhcp-range" msgstr "" #: option.c:3675 msgid "inconsistent DHCP range" msgstr "" #: option.c:3741 msgid "prefix length must be exactly 64 for RA subnets" msgstr "" #: option.c:3743 msgid "prefix length must be exactly 64 for subnet constructors" msgstr "" #: option.c:3746 msgid "prefix length must be at least 64" msgstr "" #: option.c:3749 msgid "inconsistent DHCPv6 range" msgstr "" #: option.c:3768 msgid "prefix must be zero with \"constructor:\" argument" msgstr "" #: option.c:3893 option.c:3971 msgid "bad hex constant" msgstr "" #: option.c:3946 msgid "bad IPv6 prefix" msgstr "" #: option.c:3994 #, c-format msgid "duplicate dhcp-host IP address %s" msgstr "" #: option.c:4056 msgid "bad DHCP host name" msgstr "" #: option.c:4142 msgid "bad tag-if" msgstr "" #: option.c:4490 option.c:5046 msgid "invalid port number" msgstr "" #: option.c:4546 msgid "bad dhcp-proxy address" msgstr "" #: option.c:4627 msgid "Bad dhcp-relay" msgstr "" #: option.c:4671 msgid "bad RA-params" msgstr "" #: option.c:4681 msgid "bad DUID" msgstr "" #: option.c:4715 msgid "missing address in alias" msgstr "" #: option.c:4721 msgid "invalid alias range" msgstr "" #: option.c:4770 msgid "missing address in dynamic host" msgstr "" #: option.c:4785 msgid "bad dynamic host" msgstr "" #: option.c:4803 option.c:4819 msgid "bad CNAME" msgstr "" #: option.c:4827 msgid "duplicate CNAME" msgstr "" #: option.c:4854 msgid "bad PTR record" msgstr "" #: option.c:4889 msgid "bad NAPTR record" msgstr "" #: option.c:4925 msgid "bad RR record" msgstr "" #: option.c:4958 msgid "bad CAA record" msgstr "" #: option.c:4987 msgid "bad TXT record" msgstr "" #: option.c:5030 msgid "bad SRV record" msgstr "" #: option.c:5037 msgid "bad SRV target" msgstr "" #: option.c:5056 msgid "invalid priority" msgstr "" #: option.c:5061 msgid "invalid weight" msgstr "" #: option.c:5084 msgid "Bad host-record" msgstr "" #: option.c:5123 msgid "Bad name in host-record" msgstr "" #: option.c:5165 msgid "bad value for dnssec-check-unsigned" msgstr "" #: option.c:5201 msgid "bad trust anchor" msgstr "" #: option.c:5217 msgid "bad HEX in trust anchor" msgstr "" #: option.c:5228 msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)" msgstr "" #: option.c:5290 msgid "missing \"" msgstr "" #: option.c:5347 msgid "bad option" msgstr "" #: option.c:5349 msgid "extraneous parameter" msgstr "" #: option.c:5351 msgid "missing parameter" msgstr "" #: option.c:5353 msgid "illegal option" msgstr "" #: option.c:5363 #, c-format msgid " in output from %s" msgstr "" #: option.c:5365 #, c-format msgid " at line %d of %s" msgstr "" #: option.c:5380 option.c:5683 option.c:5694 #, c-format msgid "read %s" msgstr "" #: option.c:5446 #, c-format msgid "cannot execute %s: %s" msgstr "" #: option.c:5454 option.c:5615 tftp.c:790 #, c-format msgid "cannot read %s: %s" msgstr "" #: option.c:5473 #, c-format msgid "error executing %s: %s" msgstr "" #: option.c:5476 #, c-format msgid "%s returns non-zero error code" msgstr "" #: option.c:5775 msgid "junk found in command line" msgstr "" #: option.c:5815 #, c-format msgid "Dnsmasq version %s %s\n" msgstr "" #: option.c:5816 #, c-format msgid "" "Compile time options: %s\n" "\n" msgstr "" #: option.c:5817 #, c-format msgid "This software comes with ABSOLUTELY NO WARRANTY.\n" msgstr "" #: option.c:5818 #, c-format msgid "Dnsmasq is free software, and you are welcome to redistribute it\n" msgstr "" #: option.c:5819 #, c-format msgid "under the terms of the GNU General Public License, version 2 or 3.\n" msgstr "" #: option.c:5836 msgid "try --help" msgstr "" #: option.c:5838 msgid "try -w" msgstr "" #: option.c:5840 #, c-format msgid "bad command line options: %s" msgstr "" #: option.c:5909 #, c-format msgid "CNAME loop involving %s" msgstr "" #: option.c:5950 #, c-format msgid "cannot get host-name: %s" msgstr "" #: option.c:5978 msgid "only one resolv.conf file allowed in no-poll mode." msgstr "" #: option.c:5988 msgid "must have exactly one resolv.conf to read domain from." msgstr "" #: option.c:5991 network.c:1727 dhcp.c:892 #, c-format msgid "failed to read %s: %s" msgstr "" #: option.c:6008 #, c-format msgid "no search directive found in %s" msgstr "" #: option.c:6029 msgid "there must be a default domain when --dhcp-fqdn is set" msgstr "" #: option.c:6038 msgid "syntax check OK" msgstr "" #: forward.c:107 #, c-format msgid "failed to send packet: %s" msgstr "" #: forward.c:715 msgid "discarding DNS reply: subnet option mismatch" msgstr "" #: forward.c:780 #, c-format msgid "nameserver %s refused to do a recursive query" msgstr "" #: forward.c:826 #, c-format msgid "possible DNS-rebind attack detected: %s" msgstr "" #: forward.c:1239 #, c-format msgid "reducing DNS packet size for nameserver %s to %d" msgstr "" #: forward.c:1565 #, c-format msgid "ignoring query from non-local network %s (logged only once)" msgstr "" #: forward.c:2139 #, c-format msgid "ignoring query from non-local network %s" msgstr "" #: forward.c:2501 #, c-format msgid "failed to bind server socket to %s: %s" msgstr "" #: forward.c:2867 #, c-format msgid "Maximum number of concurrent DNS queries reached (max: %d)" msgstr "" #: forward.c:2869 #, c-format msgid "Maximum number of concurrent DNS queries to %s reached (max: %d)" msgstr "" #: network.c:700 #, c-format msgid "stopped listening on %s(#%d): %s port %d" msgstr "" #: network.c:911 #, c-format msgid "failed to create listening socket for %s: %s" msgstr "" #: network.c:1192 #, c-format msgid "listening on %s(#%d): %s port %d" msgstr "" #: network.c:1219 #, c-format msgid "listening on %s port %d" msgstr "" #: network.c:1252 #, c-format msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s" msgstr "" #: network.c:1259 msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)" msgstr "" #: network.c:1268 #, c-format msgid "warning: using interface %s instead" msgstr "" #: network.c:1277 #, c-format msgid "warning: no addresses found for interface %s" msgstr "" #: network.c:1335 #, c-format msgid "interface %s failed to join DHCPv6 multicast group: %s" msgstr "" #: network.c:1340 msgid "try increasing /proc/sys/net/core/optmem_max" msgstr "" #: network.c:1545 #, c-format msgid "failed to bind server socket for %s: %s" msgstr "" #: network.c:1622 #, c-format msgid "ignoring nameserver %s - local interface" msgstr "" #: network.c:1633 #, c-format msgid "ignoring nameserver %s - cannot make/bind socket: %s" msgstr "" #: network.c:1643 msgid "more servers are defined but not logged" msgstr "" #: network.c:1654 msgid "(no DNSSEC)" msgstr "" #: network.c:1657 msgid "unqualified" msgstr "" #: network.c:1657 msgid "names" msgstr "" #: network.c:1659 msgid "default" msgstr "" #: network.c:1661 msgid "domain" msgstr "" #: network.c:1663 #, c-format msgid "using nameserver %s#%d for %s %s%s %s" msgstr "" #: network.c:1667 #, c-format msgid "NOT using nameserver %s#%d - query loop detected" msgstr "" #: network.c:1670 #, c-format msgid "using nameserver %s#%d(via %s)" msgstr "" #: network.c:1672 #, c-format msgid "using nameserver %s#%d" msgstr "" #: network.c:1687 #, c-format msgid "using only locally-known addresses for %s" msgstr "" #: network.c:1690 #, c-format msgid "using standard nameservers for %s" msgstr "" #: network.c:1694 #, c-format msgid "using %d more local addresses" msgstr "" #: network.c:1696 #, c-format msgid "using %d more nameservers" msgstr "" #: dnsmasq.c:192 msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform" msgstr "" #: dnsmasq.c:207 msgid "no root trust anchor provided for DNSSEC" msgstr "" #: dnsmasq.c:210 msgid "cannot reduce cache size from default when DNSSEC enabled" msgstr "" #: dnsmasq.c:212 msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h" msgstr "" #: dnsmasq.c:218 msgid "TFTP server not available: set HAVE_TFTP in src/config.h" msgstr "" #: dnsmasq.c:225 msgid "cannot use --conntrack AND --query-port" msgstr "" #: dnsmasq.c:231 msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h" msgstr "" #: dnsmasq.c:236 msgid "asynchronous logging is not available under Solaris" msgstr "" #: dnsmasq.c:241 msgid "asynchronous logging is not available under Android" msgstr "" #: dnsmasq.c:246 msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h" msgstr "" #: dnsmasq.c:251 msgid "loop detection not available: set HAVE_LOOP in src/config.h" msgstr "" #: dnsmasq.c:256 msgid "Ubus not available: set HAVE_UBUS in src/config.h" msgstr "" #: dnsmasq.c:267 msgid "max_port cannot be smaller than min_port" msgstr "" #: dnsmasq.c:271 msgid "port_limit must not be larger than available port range" msgstr "" #: dnsmasq.c:278 msgid "--auth-server required when an auth zone is defined." msgstr "" #: dnsmasq.c:283 msgid "zone serial must be configured in --auth-soa" msgstr "" #: dnsmasq.c:303 msgid "dhcp-range constructor not available on this platform" msgstr "" #: dnsmasq.c:377 msgid "cannot set --bind-interfaces and --bind-dynamic" msgstr "" #: dnsmasq.c:380 #, c-format msgid "failed to find list of interfaces: %s" msgstr "" #: dnsmasq.c:389 #, c-format msgid "unknown interface %s" msgstr "" #: dnsmasq.c:396 #, c-format msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s" msgstr "" #: dnsmasq.c:440 msgid "Packet dumps not available: set HAVE_DUMP in src/config.h" msgstr "" #: dnsmasq.c:448 dnsmasq.c:1232 #, c-format msgid "DBus error: %s" msgstr "" #: dnsmasq.c:451 msgid "DBus not available: set HAVE_DBUS in src/config.h" msgstr "" #: dnsmasq.c:459 dnsmasq.c:1253 #, c-format msgid "UBus error: %s" msgstr "" #: dnsmasq.c:462 msgid "UBus not available: set HAVE_UBUS in src/config.h" msgstr "" #: dnsmasq.c:492 #, c-format msgid "unknown user or group: %s" msgstr "" #: dnsmasq.c:568 #, c-format msgid "process is missing required capability %s" msgstr "" #: dnsmasq.c:600 #, c-format msgid "cannot chdir to filesystem root: %s" msgstr "" #: dnsmasq.c:852 #, c-format msgid "started, version %s DNS disabled" msgstr "" #: dnsmasq.c:857 #, c-format msgid "started, version %s cachesize %d" msgstr "" #: dnsmasq.c:859 msgid "cache size greater than 10000 may cause performance issues, and is unlikely to be useful." msgstr "" #: dnsmasq.c:862 #, c-format msgid "started, version %s cache disabled" msgstr "" #: dnsmasq.c:865 msgid "DNS service limited to local subnets" msgstr "" #: dnsmasq.c:868 #, c-format msgid "compile time options: %s" msgstr "" #: dnsmasq.c:877 msgid "DBus support enabled: connected to system bus" msgstr "" #: dnsmasq.c:879 msgid "DBus support enabled: bus connection pending" msgstr "" #: dnsmasq.c:887 msgid "UBus support enabled: connected to system bus" msgstr "" #: dnsmasq.c:889 msgid "UBus support enabled: bus connection pending" msgstr "" #: dnsmasq.c:909 msgid "DNSSEC validation enabled but all unsigned answers are trusted" msgstr "" #: dnsmasq.c:911 msgid "DNSSEC validation enabled" msgstr "" #: dnsmasq.c:915 msgid "DNSSEC signature timestamps not checked until receipt of SIGINT" msgstr "" #: dnsmasq.c:918 msgid "DNSSEC signature timestamps not checked until system time valid" msgstr "" #: dnsmasq.c:921 #, c-format msgid "configured with trust anchor for %s keytag %u" msgstr "" #: dnsmasq.c:927 #, c-format msgid "warning: failed to change owner of %s: %s" msgstr "" #: dnsmasq.c:932 msgid "setting --bind-interfaces option because of OS limitations" msgstr "" #: dnsmasq.c:945 #, c-format msgid "warning: interface %s does not currently exist" msgstr "" #: dnsmasq.c:950 msgid "warning: ignoring resolv-file flag because no-resolv is set" msgstr "" #: dnsmasq.c:953 msgid "warning: no upstream servers configured" msgstr "" #: dnsmasq.c:957 #, c-format msgid "asynchronous logging enabled, queue limit is %d messages" msgstr "" #: dnsmasq.c:978 msgid "IPv6 router advertisement enabled" msgstr "" #: dnsmasq.c:983 #, c-format msgid "DHCP, sockets bound exclusively to interface %s" msgstr "" #: dnsmasq.c:1000 msgid "root is " msgstr "" #: dnsmasq.c:1000 msgid "enabled" msgstr "" #: dnsmasq.c:1002 msgid "secure mode" msgstr "" #: dnsmasq.c:1003 msgid "single port mode" msgstr "" #: dnsmasq.c:1006 #, c-format msgid "warning: %s inaccessible" msgstr "" #: dnsmasq.c:1010 #, c-format msgid "warning: TFTP directory %s inaccessible" msgstr "" #: dnsmasq.c:1036 #, c-format msgid "restricting maximum simultaneous TFTP transfers to %d" msgstr "" #: dnsmasq.c:1095 #, c-format msgid "error binding DHCP socket to device %s" msgstr "" #: dnsmasq.c:1229 msgid "connected to system DBus" msgstr "" #: dnsmasq.c:1250 msgid "connected to system UBus" msgstr "" #: dnsmasq.c:1416 #, c-format msgid "cannot fork into background: %s" msgstr "" #: dnsmasq.c:1420 #, c-format msgid "failed to create helper: %s" msgstr "" #: dnsmasq.c:1424 #, c-format msgid "setting capabilities failed: %s" msgstr "" #: dnsmasq.c:1428 #, c-format msgid "failed to change user-id to %s: %s" msgstr "" #: dnsmasq.c:1432 #, c-format msgid "failed to change group-id to %s: %s" msgstr "" #: dnsmasq.c:1436 #, c-format msgid "failed to open pidfile %s: %s" msgstr "" #: dnsmasq.c:1440 #, c-format msgid "cannot open log %s: %s" msgstr "" #: dnsmasq.c:1444 #, c-format msgid "failed to load Lua script: %s" msgstr "" #: dnsmasq.c:1448 #, c-format msgid "TFTP directory %s inaccessible: %s" msgstr "" #: dnsmasq.c:1452 #, c-format msgid "cannot create timestamp file %s: %s" msgstr "" #: dnsmasq.c:1536 #, c-format msgid "script process killed by signal %d" msgstr "" #: dnsmasq.c:1540 #, c-format msgid "script process exited with status %d" msgstr "" #: dnsmasq.c:1544 #, c-format msgid "failed to execute %s: %s" msgstr "" #: dnsmasq.c:1584 msgid "now checking DNSSEC signature timestamps" msgstr "" #: dnsmasq.c:1619 dnssec.c:160 dnssec.c:204 #, c-format msgid "failed to update mtime on %s: %s" msgstr "" #: dnsmasq.c:1631 msgid "exiting on receipt of SIGTERM" msgstr "" #: dnsmasq.c:1659 #, c-format msgid "failed to access %s: %s" msgstr "" #: dnsmasq.c:1690 #, c-format msgid "reading %s" msgstr "" #: dnsmasq.c:1706 #, c-format msgid "no servers found in %s, will retry" msgstr "" #: dhcp.c:51 #, c-format msgid "cannot create DHCP socket: %s" msgstr "" #: dhcp.c:66 #, c-format msgid "failed to set options on DHCP socket: %s" msgstr "" #: dhcp.c:87 #, c-format msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s" msgstr "" #: dhcp.c:99 #, c-format msgid "failed to bind DHCP server socket: %s" msgstr "" #: dhcp.c:125 #, c-format msgid "cannot create ICMP raw socket: %s." msgstr "" #: dhcp.c:254 dhcp6.c:186 #, c-format msgid "unknown interface %s in bridge-interface" msgstr "" #: dhcp.c:295 #, c-format msgid "DHCP packet received on %s which has no address" msgstr "" #: dhcp.c:429 #, c-format msgid "ARP-cache injection failed: %s" msgstr "" #: dhcp.c:490 #, c-format msgid "Error sending DHCP packet to %s: %s" msgstr "" #: dhcp.c:547 #, c-format msgid "DHCP range %s -- %s is not consistent with netmask %s" msgstr "" #: dhcp.c:930 #, c-format msgid "bad line at %s line %d" msgstr "" #: dhcp.c:973 #, c-format msgid "ignoring %s line %d, duplicate name or IP address" msgstr "" #: dhcp.c:1034 #, c-format msgid "read %s - %d addresses" msgstr "" #: dhcp.c:1136 #, c-format msgid "Cannot broadcast DHCP relay via interface %s" msgstr "" #: dhcp.c:1160 #, c-format msgid "broadcast via %s" msgstr "" #: dhcp.c:1163 rfc3315.c:2219 #, c-format msgid "DHCP relay at %s -> %s" msgstr "" #: lease.c:64 #, c-format msgid "ignoring invalid line in lease database: %s %s %s %s ..." msgstr "" #: lease.c:101 #, c-format msgid "ignoring invalid line in lease database, bad address: %s" msgstr "" #: lease.c:108 msgid "too many stored leases" msgstr "" #: lease.c:176 #, c-format msgid "cannot open or create lease file %s: %s" msgstr "" #: lease.c:185 msgid "failed to parse lease database cleanly" msgstr "" #: lease.c:188 #, c-format msgid "failed to read lease file %s: %s" msgstr "" #: lease.c:204 #, c-format msgid "cannot run lease-init script %s: %s" msgstr "" #: lease.c:210 #, c-format msgid "lease-init script returned exit code %s" msgstr "" #: lease.c:381 #, c-format msgid "failed to write %s: %s (retry in %u s)" msgstr "" #: lease.c:955 #, c-format msgid "Ignoring domain %s for DHCP host name %s" msgstr "" #: rfc2131.c:378 msgid "with subnet selector" msgstr "" #: rfc2131.c:383 msgid "via" msgstr "" #: rfc2131.c:389 #, c-format msgid "no address range available for DHCP request %s %s" msgstr "" #: rfc2131.c:403 #, c-format msgid "%u available DHCP subnet: %s/%s" msgstr "" #: rfc2131.c:409 rfc3315.c:320 #, c-format msgid "%u available DHCP range: %s -- %s" msgstr "" #: rfc2131.c:521 #, c-format msgid "%u vendor class: %s" msgstr "" #: rfc2131.c:523 #, c-format msgid "%u user class: %s" msgstr "" #: rfc2131.c:557 msgid "disabled" msgstr "" #: rfc2131.c:598 rfc2131.c:1087 rfc2131.c:1536 rfc3315.c:633 rfc3315.c:816 #: rfc3315.c:1122 msgid "ignored" msgstr "" #: rfc2131.c:613 rfc2131.c:1340 rfc3315.c:868 msgid "address in use" msgstr "" #: rfc2131.c:627 rfc2131.c:1141 msgid "no address available" msgstr "" #: rfc2131.c:634 rfc2131.c:1302 msgid "wrong network" msgstr "" #: rfc2131.c:649 msgid "no address configured" msgstr "" #: rfc2131.c:655 rfc2131.c:1353 msgid "no leases left" msgstr "" #: rfc2131.c:756 rfc3315.c:500 #, c-format msgid "%u client provides name: %s" msgstr "" #: rfc2131.c:885 msgid "PXE BIS not supported" msgstr "" #: rfc2131.c:1054 rfc3315.c:1223 #, c-format msgid "disabling DHCP static address %s for %s" msgstr "" #: rfc2131.c:1075 msgid "unknown lease" msgstr "" #: rfc2131.c:1110 #, c-format msgid "not using configured address %s because it is leased to %s" msgstr "" #: rfc2131.c:1120 #, c-format msgid "not using configured address %s because it is in use by the server or relay" msgstr "" #: rfc2131.c:1123 #, c-format msgid "not using configured address %s because it was previously declined" msgstr "" #: rfc2131.c:1139 rfc2131.c:1346 msgid "no unique-id" msgstr "" #: rfc2131.c:1238 msgid "wrong server-ID" msgstr "" #: rfc2131.c:1257 msgid "wrong address" msgstr "" #: rfc2131.c:1275 rfc3315.c:976 msgid "lease not found" msgstr "" #: rfc2131.c:1310 msgid "address not available" msgstr "" #: rfc2131.c:1321 msgid "static lease available" msgstr "" #: rfc2131.c:1325 msgid "address reserved" msgstr "" #: rfc2131.c:1334 #, c-format msgid "abandoning lease to %s of %s" msgstr "" #: rfc2131.c:1870 #, c-format msgid "%u bootfile name: %s" msgstr "" #: rfc2131.c:1879 #, c-format msgid "%u server name: %s" msgstr "" #: rfc2131.c:1889 #, c-format msgid "%u next server: %s" msgstr "" #: rfc2131.c:1893 #, c-format msgid "%u broadcast response" msgstr "" #: rfc2131.c:1956 #, c-format msgid "cannot send DHCP/BOOTP option %d: no space left in packet" msgstr "" #: rfc2131.c:2267 msgid "PXE menu too large" msgstr "" #: rfc2131.c:2430 rfc3315.c:1517 #, c-format msgid "%u requested options: %s" msgstr "" #: rfc2131.c:2747 #, c-format msgid "cannot send RFC3925 option: too many options for enterprise number %d" msgstr "" #: rfc2131.c:2810 #, c-format msgid "%u reply delay: %d" msgstr "" #: netlink.c:86 #, c-format msgid "cannot create netlink socket: %s" msgstr "" #: netlink.c:379 #, c-format msgid "netlink returns error: %s" msgstr "" #: dbus.c:491 #, c-format msgid "Enabling --%s option from D-Bus" msgstr "" #: dbus.c:496 #, c-format msgid "Disabling --%s option from D-Bus" msgstr "" #: dbus.c:857 msgid "setting upstream servers from DBus" msgstr "" #: dbus.c:907 msgid "could not register a DBus message handler" msgstr "" #: bpf.c:261 #, c-format msgid "cannot create DHCP BPF socket: %s" msgstr "" #: bpf.c:289 #, c-format msgid "DHCP request for unsupported hardware type (%d) received on %s" msgstr "" #: bpf.c:374 #, c-format msgid "cannot create PF_ROUTE socket: %s" msgstr "" #: bpf.c:395 msgid "Unknown protocol version from route socket" msgstr "" #: helper.c:150 msgid "lease() function missing in Lua script" msgstr "" #: tftp.c:353 msgid "unable to get free port for TFTP" msgstr "" #: tftp.c:369 #, c-format msgid "unsupported request from %s" msgstr "" #: tftp.c:520 #, c-format msgid "file %s not found for %s" msgstr "" #: tftp.c:609 #, c-format msgid "ignoring packet from %s (TID mismatch)" msgstr "" #: tftp.c:662 #, c-format msgid "failed sending %s to %s" msgstr "" #: tftp.c:662 #, c-format msgid "sent %s to %s" msgstr "" #: tftp.c:712 #, c-format msgid "error %d %s received from %s" msgstr "" #: log.c:203 #, c-format msgid "overflow: %d log entries lost" msgstr "" #: log.c:281 #, c-format msgid "log failed: %s" msgstr "" #: log.c:490 msgid "FAILED to start up" msgstr "" #: conntrack.c:63 #, c-format msgid "Conntrack connection mark retrieval failed: %s" msgstr "" #: dhcp6.c:51 #, c-format msgid "cannot create DHCPv6 socket: %s" msgstr "" #: dhcp6.c:72 #, c-format msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s" msgstr "" #: dhcp6.c:84 #, c-format msgid "failed to bind DHCPv6 server socket: %s" msgstr "" #: rfc3315.c:173 #, c-format msgid "no address range available for DHCPv6 request from relay at %s" msgstr "" #: rfc3315.c:182 #, c-format msgid "no address range available for DHCPv6 request via %s" msgstr "" #: rfc3315.c:317 #, c-format msgid "%u available DHCPv6 subnet: %s/%d" msgstr "" #: rfc3315.c:400 #, c-format msgid "%u vendor class: %u" msgstr "" #: rfc3315.c:448 #, c-format msgid "%u client MAC address: %s" msgstr "" #: rfc3315.c:763 rfc3315.c:860 msgid "address unavailable" msgstr "" #: rfc3315.c:775 rfc3315.c:904 rfc3315.c:1273 msgid "success" msgstr "" #: rfc3315.c:790 rfc3315.c:799 rfc3315.c:912 rfc3315.c:914 rfc3315.c:1048 msgid "no addresses available" msgstr "" #: rfc3315.c:891 msgid "not on link" msgstr "" #: rfc3315.c:980 rfc3315.c:1181 rfc3315.c:1262 msgid "no binding found" msgstr "" #: rfc3315.c:1017 msgid "deprecated" msgstr "" #: rfc3315.c:1024 msgid "address invalid" msgstr "" #: rfc3315.c:1082 rfc3315.c:1084 msgid "confirm failed" msgstr "" #: rfc3315.c:1099 msgid "all addresses still on link" msgstr "" #: rfc3315.c:1190 msgid "release received" msgstr "" #: rfc3315.c:2200 #, c-format msgid "Cannot multicast DHCP relay via interface %s" msgstr "" #: rfc3315.c:2216 #, c-format msgid "multicast via %s" msgstr "" #: dhcp-common.c:187 #, c-format msgid "Ignoring duplicate dhcp-option %d" msgstr "" #: dhcp-common.c:264 #, c-format msgid "%u tags: %s" msgstr "" #: dhcp-common.c:484 #, c-format msgid "%s has more than one address in hostsfile, using %s for DHCP" msgstr "" #: dhcp-common.c:518 #, c-format msgid "duplicate IP address %s (%s) in dhcp-config directive" msgstr "" #: dhcp-common.c:738 #, c-format msgid "Known DHCP options:\n" msgstr "" #: dhcp-common.c:749 #, c-format msgid "Known DHCPv6 options:\n" msgstr "" #: dhcp-common.c:946 msgid ", prefix deprecated" msgstr "" #: dhcp-common.c:949 #, c-format msgid ", lease time " msgstr "" #: dhcp-common.c:991 #, c-format msgid "%s stateless on %s%.0s%.0s%s" msgstr "" #: dhcp-common.c:993 #, c-format msgid "%s, static leases only on %.0s%s%s%.0s" msgstr "" #: dhcp-common.c:995 #, c-format msgid "%s, proxy on subnet %.0s%s%.0s%.0s" msgstr "" #: dhcp-common.c:996 #, c-format msgid "%s, IP range %s -- %s%s%.0s" msgstr "" #: dhcp-common.c:1009 #, c-format msgid "DHCPv4-derived IPv6 names on %s%s" msgstr "" #: dhcp-common.c:1012 #, c-format msgid "router advertisement on %s%s" msgstr "" #: dhcp-common.c:1043 #, c-format msgid "DHCP relay from %s via %s" msgstr "" #: dhcp-common.c:1045 #, c-format msgid "DHCP relay from %s to %s via %s" msgstr "" #: dhcp-common.c:1048 #, c-format msgid "DHCP relay from %s to %s" msgstr "" #: radv.c:110 #, c-format msgid "cannot create ICMPv6 socket: %s" msgstr "" #: auth.c:462 #, c-format msgid "ignoring zone transfer request from %s" msgstr "" #: ipset.c:99 #, c-format msgid "failed to create IPset control socket: %s" msgstr "" #: ipset.c:211 #, c-format msgid "failed to update ipset %s: %s" msgstr "" #: pattern.c:29 #, c-format msgid "[pattern.c:%d] Assertion failure: %s" msgstr "" #: pattern.c:142 #, c-format msgid "Invalid DNS name: Invalid character %c." msgstr "" #: pattern.c:151 msgid "Invalid DNS name: Empty label." msgstr "" #: pattern.c:156 msgid "Invalid DNS name: Label starts with hyphen." msgstr "" #: pattern.c:170 msgid "Invalid DNS name: Label ends with hyphen." msgstr "" #: pattern.c:176 #, c-format msgid "Invalid DNS name: Label is too long (%zu)." msgstr "" #: pattern.c:184 #, c-format msgid "Invalid DNS name: Not enough labels (%zu)." msgstr "" #: pattern.c:189 msgid "Invalid DNS name: Final label is fully numeric." msgstr "" #: pattern.c:199 msgid "Invalid DNS name: \"local\" pseudo-TLD." msgstr "" #: pattern.c:204 #, c-format msgid "DNS name has invalid length (%zu)." msgstr "" #: pattern.c:258 #, c-format msgid "Invalid DNS name pattern: Invalid character %c." msgstr "" #: pattern.c:267 msgid "Invalid DNS name pattern: Empty label." msgstr "" #: pattern.c:272 msgid "Invalid DNS name pattern: Label starts with hyphen." msgstr "" #: pattern.c:285 msgid "Invalid DNS name pattern: Wildcard character used more than twice per label." msgstr "" #: pattern.c:295 msgid "Invalid DNS name pattern: Label ends with hyphen." msgstr "" #: pattern.c:301 #, c-format msgid "Invalid DNS name pattern: Label is too long (%zu)." msgstr "" #: pattern.c:309 #, c-format msgid "Invalid DNS name pattern: Not enough labels (%zu)." msgstr "" #: pattern.c:314 msgid "Invalid DNS name pattern: Wildcard within final two labels." msgstr "" #: pattern.c:319 msgid "Invalid DNS name pattern: Final label is fully numeric." msgstr "" #: pattern.c:329 msgid "Invalid DNS name pattern: \"local\" pseudo-TLD." msgstr "" #: pattern.c:334 #, c-format msgid "DNS name pattern has invalid length after removing wildcards (%zu)." msgstr "" #: dnssec.c:206 msgid "system time considered valid, now checking DNSSEC signature timestamps." msgstr "" #: dnssec.c:1018 #, c-format msgid "Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support" msgstr "" #: blockdata.c:55 #, c-format msgid "pool memory in use %zu, max %zu, allocated %zu" msgstr "" #: tables.c:61 #, c-format msgid "failed to access pf devices: %s" msgstr "" #: tables.c:74 #, c-format msgid "warning: no opened pf devices %s" msgstr "" #: tables.c:82 #, c-format msgid "error: cannot use table name %s" msgstr "" #: tables.c:90 #, c-format msgid "error: cannot strlcpy table name %s" msgstr "" #: tables.c:101 #, c-format msgid "IPset: error: %s" msgstr "" #: tables.c:108 msgid "info: table created" msgstr "" #: tables.c:133 #, c-format msgid "warning: DIOCR%sADDRS: %s" msgstr "" #: tables.c:137 #, c-format msgid "%d addresses %s" msgstr "" #: inotify.c:62 #, c-format msgid "cannot access path %s: %s" msgstr "" #: inotify.c:95 #, c-format msgid "failed to create inotify: %s" msgstr "" #: inotify.c:111 #, c-format msgid "too many symlinks following %s" msgstr "" #: inotify.c:127 #, c-format msgid "directory %s for resolv-file is missing, cannot poll" msgstr "" #: inotify.c:131 inotify.c:200 #, c-format msgid "failed to create inotify for %s: %s" msgstr "" #: inotify.c:178 inotify.c:185 #, c-format msgid "bad dynamic directory %s: %s" msgstr "" #: inotify.c:186 msgid "not a directory" msgstr "" #: inotify.c:299 #, c-format msgid "inotify: %s removed" msgstr "" #: inotify.c:301 #, c-format msgid "inotify: %s new or modified" msgstr "" #: inotify.c:309 #, c-format msgid "inotify: flushed %u names read from %s" msgstr "" #: dump.c:68 #, c-format msgid "cannot create %s: %s" msgstr "" #: dump.c:74 #, c-format msgid "bad header in %s" msgstr "" #: dump.c:287 msgid "failed to write packet dump" msgstr "" #: dump.c:289 #, c-format msgid "%u dumping packet %u mask 0x%04x" msgstr "" #: dump.c:291 #, c-format msgid "dumping packet %u mask 0x%04x" msgstr "" #: ubus.c:79 #, c-format msgid "UBus subscription callback: %s subscriber(s)" msgstr "" #: ubus.c:99 #, c-format msgid "Cannot reconnect to UBus: %s" msgstr "" #: ubus.c:135 msgid "Cannot set UBus listeners: no connection" msgstr "" #: ubus.c:155 msgid "Cannot poll UBus listeners: no connection" msgstr "" #: ubus.c:168 msgid "Disconnecting from UBus" msgstr "" #: ubus.c:179 ubus.c:326 #, c-format msgid "UBus command failed: %d (%s)" msgstr "" #: hash-questions.c:40 msgid "Failed to create SHA-256 hash object" msgstr "" #: nftset.c:35 msgid "failed to create nftset context" msgstr "" dnsmasq-2.91/po/ka.po0000664000175000017500000033630114765043257012671 0ustar srksrk# Georgian translation for dnsmasq # This file is distributed under the same license as the dnsmasq package. # Temuri Doghonadze , 2022. msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: Temuri Doghonadze \n" "Language-Team: \n" "Language: ka\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 3.1.1\n" #: cache.c:652 msgid "Internal error in cache." msgstr "შიდა შეცდომა ქეშში." #: cache.c:1179 #, c-format msgid "failed to load names from %s: %s" msgstr "შეცდომა სახელების %s-დან წაკითხვისას: %s" #: cache.c:1201 dhcp.c:943 #, c-format msgid "bad address at %s line %d" msgstr "ცუდი მისამართი %s ხაზი %d" #: cache.c:1254 dhcp.c:959 #, c-format msgid "bad name at %s line %d" msgstr "ცუდი სახელი %s ხაზზე %d" #: cache.c:1265 #, fuzzy, c-format msgid "read %s - %d names" msgstr "წავიკითხე %s - %d მისამართი" #: cache.c:1381 msgid "cleared cache" msgstr "ქეში გასუფთავდა" #: cache.c:1445 #, c-format msgid "No IPv4 address found for %s" msgstr "%s-სთვის IPv4 მისამართი ვერ ვიპოვე" #: cache.c:1491 #, c-format msgid "%s is a CNAME, not giving it to the DHCP lease of %s" msgstr "%s CNAME-ა. %s-ის DHCP იჯარა არ გადაეცემა" #: cache.c:1515 #, c-format msgid "not giving name %s to the DHCP lease of %s because the name exists in %s with address %s" msgstr "სახელი %s DHCP იჯარა %s-ზე მინიჭებული არ იქნება იმიტომ, რომ სახელი %s-ში მისამართით %s უკვე არსებობს" #: cache.c:1760 #, c-format msgid "time %lu" msgstr "დრო %lu" #: cache.c:1761 #, c-format msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries." msgstr "ქეშის ზომა %d, %d;%d ქეშში ჩამატებებმა ქეშის ვადაგაუსვლელი ჩანაწერები თავიდან გამოიყენეს." #: cache.c:1763 #, c-format msgid "queries forwarded %u, queries answered locally %u" msgstr "მოთხოვნები გადაგზავნილია %u, ლოკალურად ნაპასუხები %u" #: cache.c:1766 #, c-format msgid "queries answered from stale cache %u" msgstr "" #: cache.c:1768 #, c-format msgid "queries for authoritative zones %u" msgstr "მოთხოვნები ავტორიტეტული ზონებისთვის %u" #: cache.c:1796 #, fuzzy, c-format msgid "server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums" msgstr "სერვერი %s#%d: %u გაგზავნილი მოთხოვნა, %u თავიდან ნაცადი ან შეცდომით დასრულებული" #: util.c:51 #, c-format msgid "failed to seed the random number generator: %s" msgstr "შემთხვევითი რიცხვების გენერაციისათვის თესლის გადაცემის შეცდომა: %s" #: util.c:246 msgid "failed to allocate memory" msgstr "მეხსიერების გამოყოფის შეცდომა" #: util.c:305 option.c:696 msgid "could not get memory" msgstr "მეხსიერების მიღების შეცდომა" #: util.c:326 #, c-format msgid "cannot create pipe: %s" msgstr "ფაიფის შექმნის შეცდომა: %s" #: util.c:334 #, c-format msgid "failed to allocate %d bytes" msgstr "%d ბაიტის გამოყოფის შეცდომა" #: util.c:344 #, c-format msgid "failed to reallocate %d bytes" msgstr "%d ბაიტის თავიდან გამოყოფის შეცდომა" #: util.c:465 #, c-format msgid "cannot read monotonic clock: %s" msgstr "მონოტონური საათის წაკითხვის შეცდომა: %s" #: util.c:579 #, c-format msgid "infinite" msgstr "უსასრულო" #: util.c:867 #, c-format msgid "failed to find kernel version: %s" msgstr "ბირთვის ვერსიის პოვნის შეცდომა: %s" #: option.c:393 msgid "Specify local address(es) to listen on." msgstr "მიუთითეთ მოსასმენი ლოკალური მისამართები." #: option.c:394 msgid "Return ipaddr for all hosts in specified domains." msgstr "მითითებულ დომენებში ყველა ჰოსტისთვის ipaddr-ის დაბრუნება." #: option.c:395 msgid "Fake reverse lookups for RFC1918 private address ranges." msgstr "ყალბი უკუღმა-ძებნები RFC1918 მისამართების პირადი დიაპაზონებისტვის." #: option.c:396 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)." msgstr "\"ipaddr\"-ის განხილვა, როგორც NXDOMAIN-ის (სჯობნის Verisign ვაილდ-კარდს)." #: option.c:397 #, c-format msgid "Specify the size of the cache in entries (defaults to %s)." msgstr "მიუთითეთ ჩანაწერებში ქეშის ზომა (ნაგულისხმები %s)." #: option.c:398 #, c-format msgid "Specify configuration file (defaults to %s)." msgstr "კონფიგურაციის ფაილის მითითება (ნაგულისხმები %s)." #: option.c:399 msgid "Do NOT fork into the background: run in debug mode." msgstr "*არ გადახვიდე* ფონში: გამართვის რეჟიმში გაშვება." #: option.c:400 msgid "Do NOT forward queries with no domain part." msgstr "მოთხოვნები დომენის ნაწილის გარეშე *არ* გადააგზავნო." #: option.c:401 msgid "Return self-pointing MX records for local hosts." msgstr "ლოკალური ჰოსტებისთვის თავის-თავზე მაჩვენებელი MX ჩანაწერების დაბრუნება." #: option.c:402 msgid "Expand simple names in /etc/hosts with domain-suffix." msgstr "/etc/hosts-ის მარტივი სახელებისთვის დომენის სუფიქსის მიწერა." #: option.c:403 msgid "Don't forward spurious DNS requests from Windows hosts." msgstr "Windows-ის ჰოსტებიდან მოსული ყალბი DNS მოთხოვნები არ გადაიგზავნება." #: option.c:404 msgid "Don't include IPv4 addresses in DNS answers." msgstr "DNS-ის პასუხებში IPv4 მისამართები ჩასმული არ იქნება." #: option.c:405 msgid "Don't include IPv6 addresses in DNS answers." msgstr "DNS-ის პასუხებში IPv6 მისამართები ჩასმული არ იქნება." #: option.c:406 msgid "Enable DHCP in the range given with lease duration." msgstr "DHCP-ის იჯარის ხანგრძლივობაში მითითებული დიაპაზონით შექმნა." #: option.c:407 #, c-format msgid "Change to this group after startup (defaults to %s)." msgstr "გაშვების შემდეგ ამ ჯგუფზე გადასვლა (ნაგულისხმები %s)" #: option.c:408 msgid "Set address or hostname for a specified machine." msgstr "მითითებული მანქანისთვის მისამართის ან ჰოსტის სახელის დაყენება." #: option.c:409 msgid "Read DHCP host specs from file." msgstr "DHCP-ის ჰოსტის სპეციფიკაციის ფაილიდან წაკითხვა." #: option.c:410 msgid "Read DHCP option specs from file." msgstr "DHCP-ის პარამეტრების სპეციფიკაციის ფაილიდან წაკითხვა." #: option.c:411 msgid "Read DHCP host specs from a directory." msgstr "DHCP-ის ჰოსტის სპეციფიკაციის საქაღალდიდან წაკითხვა." #: option.c:412 msgid "Read DHCP options from a directory." msgstr "DHCP-ის პარამეტრების სპეციფიკაციის საქაღალდიდან წაკითხვა." #: option.c:413 msgid "Evaluate conditional tag expression." msgstr "ჭდის პირობითი გამოსახულების შემოწმება." #: option.c:414 #, c-format msgid "Do NOT load %s file." msgstr "არ ჩატვირთო %s ფაილი." #: option.c:415 #, c-format msgid "Specify a hosts file to be read in addition to %s." msgstr "მიუთითეთ %s-სთან ერთად, დამატებით წასაკითხი hosts ფაილი." #: option.c:416 msgid "Read hosts files from a directory." msgstr "წავიკითხე hosts ფაილი საქაღალდიდან." #: option.c:417 msgid "Specify interface(s) to listen on." msgstr "მიუთითეთ მოსასმენი ინტერფეისები." #: option.c:418 msgid "Specify interface(s) NOT to listen on." msgstr "მიუთითეთ ინტერფეისები, რომელზეც არ მოვუსმენ." #: option.c:419 msgid "Map DHCP user class to tag." msgstr "DHCP-ის მომხმარებლის კლასის ჭდეზე მიბმა." #: option.c:420 msgid "Map RFC3046 circuit-id to tag." msgstr "RFC3046 circuit-id-ის ჭდეზე მიბმა." #: option.c:421 msgid "Map RFC3046 remote-id to tag." msgstr "RFC3046 remote-id-ის ჭდეზე მიბმა." #: option.c:422 msgid "Map RFC3993 subscriber-id to tag." msgstr "RFC3046 subscriber-id-ის ჭდეზე მიბმა." #: option.c:423 msgid "Specify vendor class to match for PXE requests." msgstr "მიუთითეთ PXE მოთხოვნებში დასამთხვევი მომწოდებლის კლასი." #: option.c:424 msgid "Don't do DHCP for hosts with tag set." msgstr "ჭდე-დაყენებული ჰოსტებისთვის DHCP არ იმუშავებს." #: option.c:425 msgid "Force broadcast replies for hosts with tag set." msgstr "ჭდედაყენებული ჰოსტებისთვის მაუწყებლობის პაკეტების გაგზავნა." #: option.c:426 msgid "Do NOT fork into the background, do NOT run in debug mode." msgstr "*არ* გადახვიდე ფონში. *არ* გაეშვა გამართვის რეჟიში." #: option.c:427 msgid "Assume we are the only DHCP server on the local network." msgstr "ჩავთვალოთ, რომ ჩვენ ლოკალურ ქსელში ერთადერთი DHCP სერვერი ვართ." #: option.c:428 #, c-format msgid "Specify where to store DHCP leases (defaults to %s)." msgstr "მიუთითეთ, სად უნდა შევინახოთ DHCP იჯარები (ნაგულისხმებია %s)." #: option.c:429 msgid "Return MX records for local hosts." msgstr "ლოკალური ჰოსტებისთვის MX ჩანაწერების დაბრუნება." #: option.c:430 msgid "Specify an MX record." msgstr "მიუთითეთ MX ჩანაწერი." #: option.c:431 msgid "Specify BOOTP options to DHCP server." msgstr "DHCP სერვერისთვის საჭიროა BOOTP-ის პარამეტრების მითითება." #: option.c:432 #, c-format msgid "Do NOT poll %s file, reload only on SIGHUP." msgstr "" #: option.c:433 msgid "Do NOT cache failed search results." msgstr "შეცდომით დასრულებული შედეგები *არ* დაიქეშება." #: option.c:434 msgid "Use expired cache data for faster reply." msgstr "" #: option.c:435 #, c-format msgid "Use nameservers strictly in the order given in %s." msgstr "DNS სერვერების მხოლოდ %s-ში მითითებული მიმდევრობით გამოყენება." #: option.c:436 msgid "Specify options to be sent to DHCP clients." msgstr "მიუთითეთ DHCP კლიენტებისთვის გასაგზავნი პარამეტრები." #: option.c:437 msgid "DHCP option sent even if the client does not request it." msgstr "DHCP პარამეტრები იგზავნება მაშინაც კი, როცა კლიენტი მათ არ ითხოვს." #: option.c:438 msgid "Specify port to listen for DNS requests on (defaults to 53)." msgstr "მიუთითეთ პორტი DNS მოთხოვნების მოსასმენად (ნაგულისხმებია 53)." #: option.c:439 #, c-format msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)." msgstr "UDP პაკეტის მაქსიმალური მხარდაჭერილი ზომა EDNS.0-სთვის (ნაგულისხმებია %s)." #: option.c:440 msgid "Log DNS queries." msgstr "DNS მოთხოვნების ჟურნალში ჩაწერა." #: option.c:441 msgid "Force the originating port for upstream DNS queries." msgstr "აღმავალი DNS მოთხოვნების საწყისი პორტის ძალით დაყენება." #: option.c:442 msgid "Set maximum number of random originating ports for a query." msgstr "" #: option.c:443 msgid "Do NOT read resolv.conf." msgstr "არ წაიკითხო resolv.conf." #: option.c:444 #, c-format msgid "Specify path to resolv.conf (defaults to %s)." msgstr "მიუთითეთ ბილიკი resolv.conf-მდე (ნაგულისხმებია %s)." #: option.c:445 msgid "Specify path to file with server= options" msgstr "პარამეტრისთვის server= ფაილის ბილიკის მითითება აუცილებელია" #: option.c:446 msgid "Specify address(es) of upstream servers with optional domains." msgstr "არასავალდებული დომენების მქონე აღმავალი სერვერების მისამართების მითითება." #: option.c:447 msgid "Specify address of upstream servers for reverse address queries" msgstr "რევერსული მისამართების მოთხოვნებისთვის აღმავალი სერვერების მისამართების მითითება" #: option.c:448 msgid "Never forward queries to specified domains." msgstr "მითითებული დომენების მოთხოვნები არასდროს გადაიგზავნება." #: option.c:449 msgid "Specify the domain to be assigned in DHCP leases." msgstr "მიუთითეთ DHCP -ის იჯარებში მისანიჭებელი დომენი." #: option.c:450 msgid "Specify default target in an MX record." msgstr "მიუთითეთ MX ჩანაწერის ნაგულისხმები სამიზნე." #: option.c:451 msgid "Specify time-to-live in seconds for replies from /etc/hosts." msgstr "მიუთითეთ /etc/hosts-დან პასუხების სიცოცხლის-დრო, წამებში." #: option.c:452 msgid "Specify time-to-live in seconds for negative caching." msgstr "მიუთითეთ უარყოფითი ქეშის სიცოცხლის-დრო, წამებში." #: option.c:453 msgid "Specify time-to-live in seconds for maximum TTL to send to clients." msgstr "კლიენტებისთვის გასაგზავნი სიცოცხლის-დროის მითითება წამებში, მაქსიმალური TTL-ისთვის." #: option.c:454 msgid "Specify time-to-live ceiling for cache." msgstr "ქეშის სიცოცხლის-დროის მაქსიმუმის მითითება." #: option.c:455 msgid "Specify time-to-live floor for cache." msgstr "ქეშის სიცოცხლის-დროის მინიმუმის მითითება." #: option.c:456 msgid "Retry DNS queries after this many milliseconds." msgstr "" #: option.c:457 #, c-format msgid "Change to this user after startup. (defaults to %s)." msgstr "გაშვების შემდეგ ამ მომხმარებელზე გადასვლა (ნაგულისხმები %s)" #: option.c:458 msgid "Map DHCP vendor class to tag." msgstr "DHCP-ის მომწოდებლის კლასის ჭდეზე მიბმა." #: option.c:459 msgid "Display dnsmasq version and copyright information." msgstr "Dnsmasq-ის ვერსიის და ლიცენზირების ინფორმაციის გამოტანა." #: option.c:460 msgid "Translate IPv4 addresses from upstream servers." msgstr "აღმავალი სერვერებიდან IPv4 მისამართების თარგმნა." #: option.c:461 msgid "Specify a SRV record." msgstr "მიუთითეთ SRV ჩანაწერი." #: option.c:462 msgid "Display this message. Use --help dhcp or --help dhcp6 for known DHCP options." msgstr "ამ შეტყობინების გამოტანა. ცნობილი DHCP პარამეტრების სანახავად გამოიყენეთ --help dhcp ან --help dhcp6 ." #: option.c:463 #, c-format msgid "Specify path of PID file (defaults to %s)." msgstr "მიუთითეთ ბილიკი PID ფაილამდე (ნაგულისხმებია %s)." #: option.c:464 #, c-format msgid "Specify maximum number of DHCP leases (defaults to %s)." msgstr "DHCP იჯარების მაქსიმალური რაოდენობის მითითება (ნაგულისხმებია %s)." #: option.c:465 msgid "Answer DNS queries based on the interface a query was sent to." msgstr "DNS სერვერების პასუხი იმის მიხედვით, რომელი ინტერფეისიდან მოვიდა მოთხოვნა." #: option.c:466 msgid "Specify TXT DNS record." msgstr "მიუთითეთ TXT DNS ჩანაწერი." #: option.c:467 msgid "Specify PTR DNS record." msgstr "მიუთითეთ PTR DNS ჩანაწერი." #: option.c:468 msgid "Give DNS name to IPv4 address of interface." msgstr "საჭიროა ინტერფეისის IPv4 მისამართისთვის DNS სახელის მინიჭება." #: option.c:469 msgid "Bind only to interfaces in use." msgstr "მხოლოდ ჩართულ ინტერფეისებზე მიბმა." #: option.c:470 #, c-format msgid "Read DHCP static host information from %s." msgstr "DHCP სტატიკური ჰოსტის ინფორმაციის %s-დან წაკითხვა." #: option.c:471 msgid "Enable the DBus interface for setting upstream servers, etc." msgstr "აღმავალი სერვერების დასაყენებლად და სხვა ფუნქციონალისთვის DBus-ის ინტერფეისი ჩართეთ." #: option.c:472 msgid "Enable the UBus interface." msgstr "UBus ინტერფეისის ჩართვა." #: option.c:473 msgid "Do not provide DHCP on this interface, only provide DNS." msgstr "ამ ინტერფეისზე DHCP მიწოდებული არ იქნება. მხოლოდ DNS." #: option.c:474 msgid "Enable dynamic address allocation for bootp." msgstr "\"bootp\"-სთვის დინამიკური მისამართების გამოყოფის ჩაართვა." #: option.c:475 msgid "Map MAC address (with wildcards) to option set." msgstr "MAC მისამართის (ზოგადობის ნიშნებით) პარამეტრების ნაკრებისთვის მიბმა." #: option.c:476 msgid "Treat DHCP requests on aliases as arriving from interface." msgstr "მეტსახელებიდან მოსული DHCP მოთხოვნების ისე დამუშავება, თითქოს ისინი ინტერფეისიდან მოვიდნენ." #: option.c:477 msgid "Specify extra networks sharing a broadcast domain for DHCP" msgstr "DHCP-სთვის სამაუწყებლო დომენის გამზიარებელი დამატებითი ქსელების მითითება" #: option.c:478 msgid "Disable ICMP echo address checking in the DHCP server." msgstr "DHCP სერვერში ICMP echo მისამართის შემოწმების გამორთვა." #: option.c:479 msgid "Shell script to run on DHCP lease creation and destruction." msgstr "DHCP იჯარის შესაქმნელად ან წასაშლელად გასაშვები გარსის სკრიპტი." #: option.c:480 msgid "Lua script to run on DHCP lease creation and destruction." msgstr "DHCP იჯარის შესაქმნელად ან წასაშლელად გასაშვები Lua სკრიპტი." #: option.c:481 msgid "Run lease-change scripts as this user." msgstr "\"lease-change\" სკრიპტები ამ მომხმარებლით გაეშვება." #: option.c:482 msgid "Call dhcp-script with changes to local ARP table." msgstr "\"dhcp-script\"-ის ლოკალურ ARP ცხრილში ცვლილებებით გამოძახება." #: option.c:483 msgid "Read configuration from all the files in this directory." msgstr "კონფიგურაციის ამ საქაღალდეში მყოფი ყველა ფაილიდან წაკითხვა." #: option.c:484 msgid "Execute file and read configuration from stdin." msgstr "ფაილის შესრულება და კონფიგურაციის stdin-დან წაკითხვა." #: option.c:485 msgid "Log to this syslog facility or file. (defaults to DAEMON)" msgstr "ჟურნალის syslog-ში ან ფაილში ჩაწერა (ნაგულისხმებია DAEMON)" #: option.c:486 msgid "Do not use leasefile." msgstr "იჯარების ფაილი გამოყენებული არ იქნება." #: option.c:487 #, c-format msgid "Maximum number of concurrent DNS queries. (defaults to %s)" msgstr "ერთდროული DNS მოთხოვნების მაქსიმალური რაოდენობა (ნაგულისხმებია %s)" #: option.c:488 #, c-format msgid "Clear DNS cache when reloading %s." msgstr "%s-ის გადატვირთვისას DNS ქეშის გასუფთავება." #: option.c:489 msgid "Ignore hostnames provided by DHCP clients." msgstr "DHCP კლიენტების მიერ მოწოდებული ჰოსტი სახელების იგორირება." #: option.c:490 msgid "Do NOT reuse filename and server fields for extra DHCP options." msgstr "ფაილის სახელისა და სერვერის ველები DHCP პარამეტრებისთვის თავიდან გამოყენებული *არ* იქნება." #: option.c:491 msgid "Enable integrated read-only TFTP server." msgstr "ჩაშენებული მხოლოდ-წაკითხვადი TFTP სერვერის ჩართვა." #: option.c:492 msgid "Export files by TFTP only from the specified subtree." msgstr "TFTP-ით ფაილების მხოლოდ მითითებული ქვეხიდან გატანა." #: option.c:493 msgid "Add client IP or hardware address to tftp-root." msgstr "\"tftp-root\" -ს კლიენტის ან აპარატურული მისამართი დაამატეთ." #: option.c:494 msgid "Allow access only to files owned by the user running dnsmasq." msgstr "წვდომის მხოლოდ იმ ფაილებზე მინიჭება, რომლებიც იმ მომხმარებელს მიეკუთვნებიან, რომლითაც dnsmasq-ია გაშვებული." #: option.c:495 msgid "Do not terminate the service if TFTP directories are inaccessible." msgstr "სერვისი იმ შემთხვევაშიც კი არ შეჩერდება, თუ TFTP-ის საქაღალდეები მიუწვდომელია." #: option.c:496 #, c-format msgid "Maximum number of concurrent TFTP transfers (defaults to %s)." msgstr "ერთდროული TFTP გადაცემების მაქსიმალური რაოდენობა (ნაგულისხმებია %s)." #: option.c:497 msgid "Maximum MTU to use for TFTP transfers." msgstr "TFTP გადაცემისთვის გამოყენებული მაქსიმალური MTU." #: option.c:498 msgid "Disable the TFTP blocksize extension." msgstr "TFTP-ის blocksize გაფართოების გამორთვა." #: option.c:499 msgid "Convert TFTP filenames to lowercase" msgstr "TFTP ფაილების სახელების დაბალ რეგისტრში გადაყვანა" #: option.c:500 msgid "Ephemeral port range for use by TFTP transfers." msgstr "TFTP გადაცემების მიერ გამოყენებული ეფემერული პორტის დიაპაზონი." #: option.c:501 msgid "Use only one port for TFTP server." msgstr "TFTP სერვერისთვის მხოლოდ ერთი პორტის გამოყენება." #: option.c:502 msgid "Extra logging for DHCP." msgstr "ჟურნალის დამატებითი ჩანაწერები DHCP-სთვის." #: option.c:503 msgid "Enable async. logging; optionally set queue length." msgstr "ასინქრონული ჟურნალის გამოყენება. შეგიძლიათ ასევე დააყენოთ რიგის სიგრძეც." #: option.c:504 msgid "Stop DNS rebinding. Filter private IP ranges when resolving." msgstr "DNS-ის თავიდან მიბმის შეწყვეტა. ამოხნისას პირადი IP დიაპაზონების გაფილტვრა." #: option.c:505 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers." msgstr "127.0.0.0/8-ის თავიდან მიბმის დაშვება, RBL სერვერებისთვის." #: option.c:506 msgid "Inhibit DNS-rebind protection on this domain." msgstr "ამ დომენზე DNS-ის თავიდან მიბმის დაცვის მემკვიდრეობით მიღება." #: option.c:507 msgid "Always perform DNS queries to all servers." msgstr "DNS მოთხოვნების ყველა სერვერისთვის ყოველთვის ჩატარება." #: option.c:508 msgid "Set tag if client includes matching option in request." msgstr "ჭდის დაყენება, თუ კლიენტი მოთხოვნაში შესაბამის პარამეტრს შეიცავს." #: option.c:509 msgid "Set tag if client provides given name." msgstr "თუ კლიენტი მოგვაწოდებს მითითებულ სახელს, ჭდის დაყენება." #: option.c:510 msgid "Use alternative ports for DHCP." msgstr "DHCP-სთვის ალტერნატიული პორტების გამოყენება." #: option.c:511 msgid "Specify NAPTR DNS record." msgstr "მიუთითეთ NAPTR DNS ჩანაწერი." #: option.c:512 msgid "Specify lowest port available for DNS query transmission." msgstr "DNS მოთხოვნის გადაცემისთვის ხელმისაწვდომი პორტის უმცირესი ნომრის მითითება." #: option.c:513 msgid "Specify highest port available for DNS query transmission." msgstr "DNS მოთხოვნის გადაცემისთვის ხელმისაწვდომი პორტის უდიდესი ნომრის მითითება." #: option.c:514 msgid "Use only fully qualified domain names for DHCP clients." msgstr "DHCP კლიენტებისთვის მხოლოდ სრული დომენური სახელების გამოყენება." #: option.c:515 msgid "Generate hostnames based on MAC address for nameless clients." msgstr "უსახელო კლიენტებისთვის MAC მისამართზე დაფუძნებული ჰოსტის სახელების გენერაცია." #: option.c:516 msgid "Use these DHCP relays as full proxies." msgstr "ამ DHCP გადამგზავნების სრული პროქსის სახით გამოყენება." #: option.c:517 msgid "Relay DHCP requests to a remote server" msgstr "DHCP მოთხოვნების სხვა სერვერზე გადაგზავნა" #: option.c:518 msgid "Specify alias name for LOCAL DNS name." msgstr "მიუთითეთ მეტსახელი LOCAL DNS სახელისთვის." #: option.c:519 msgid "Prompt to send to PXE clients." msgstr "PXE კლიენტებისთვის გასაგზავნი ტექსტი." #: option.c:520 msgid "Boot service for PXE menu." msgstr "ჩატვირთვის სერვისი PXE მენიუსთვის." #: option.c:521 msgid "Check configuration syntax." msgstr "შეამოწმეთ კონფიგურაციის სინტაქსი." #: option.c:522 msgid "Add requestor's MAC address to forwarded DNS queries." msgstr "გადაგზავნილი DNS მოთხოვნებისთვის მომთხოვნის MAC მისამართის დამატება." #: option.c:523 msgid "Strip MAC information from queries." msgstr "მოთხოვნებიდან MAC ინფორმაციის მოცილება." #: option.c:524 msgid "Add specified IP subnet to forwarded DNS queries." msgstr "გადაგზავნილი DNS მოთხოვნებისთვის მითითებული IP ქვექსელის დამატება." #: option.c:525 msgid "Strip ECS information from queries." msgstr "მოთხოვნებიდან ECS ინფორამციის მოცილება." #: option.c:526 msgid "Add client identification to forwarded DNS queries." msgstr "გადაგზავნილი DNS მოთხოვნებისთვის კლიენტის იდენტიფიკატორის დამატება." #: option.c:527 msgid "Proxy DNSSEC validation results from upstream nameservers." msgstr "აღმავალი DNS სერვერებიდან DNSSEC გადამოწმების შედეგების გადმოგზავნა." #: option.c:528 msgid "Attempt to allocate sequential IP addresses to DHCP clients." msgstr "DHCP კლიენტებისთვის IP მისამართების მიმდევრობით გამოყოფის მცდელობა." #: option.c:529 msgid "Ignore client identifier option sent by DHCP clients." msgstr "DHCP კლიენტების მიერ გამოგზავნილი კლიენტის იდენტიფიკატორის იგნორირება." #: option.c:530 msgid "Copy connection-track mark from queries to upstream connections." msgstr "აღმავალი შეერთებებისთვის მოთხოვნებიდან Connection-mark ნიშნის კოპირება." #: option.c:531 msgid "Allow DHCP clients to do their own DDNS updates." msgstr "DHCP კლიენტებისთვის საკუთარი DDNS განახლებების გაკეთების ნების დართვა." #: option.c:532 msgid "Send router-advertisements for interfaces doing DHCPv6" msgstr "ინტერფეისიდან, რომელზეც DHCPv6-ია მიბმული, router-advertisements პაკეტის გაგზავნა" #: option.c:533 msgid "Specify DUID_EN-type DHCPv6 server DUID" msgstr "მიუთითეთ DUID_EN-ტიპის DHCPv6 სერვერის DUID" #: option.c:534 msgid "Specify host (A/AAAA and PTR) records" msgstr "მიუთითეთ ჰოსტის (A/AAAA და PTR) ჩანაწერები" #: option.c:535 msgid "Specify host record in interface subnet" msgstr "ინტერფეისის ქვექსელში ჰოსტის ჩანაწერის მითითება აუცილებელია" #: option.c:536 msgid "Specify certification authority authorization record" msgstr "სერტიფიკატის ორგანის ავტორიზაციის ჩანაწერის მითითება" #: option.c:537 msgid "Specify arbitrary DNS resource record" msgstr "DNS-ის ტექსტური ჩანაწერის მითითება" #: option.c:538 msgid "Bind to interfaces in use - check for new interfaces" msgstr "გამოყენებულ ინტერფეისზე მიბმა - ახალი ინტერფეისებს შემოწმება" #: option.c:539 msgid "Export local names to global DNS" msgstr "ლოკალური სახელების გლობალურ DNS-ში გატანა" #: option.c:540 msgid "Domain to export to global DNS" msgstr "გლობალურ DNS-ში გასატანი დომენი" #: option.c:541 msgid "Set TTL for authoritative replies" msgstr "ავტორიტეტული პასუხებისთვის TTL-ის დაყენება" #: option.c:542 msgid "Set authoritative zone information" msgstr "ავტორიტეტული ზონის ინფორმაციის დაყენება" #: option.c:543 msgid "Secondary authoritative nameservers for forward domains" msgstr "მეორადი ავტორიტეტული DNS სერვერები დომენების გადასაგზავნად" #: option.c:544 msgid "Peers which are allowed to do zone transfer" msgstr "პარტნიორები, რომლებსაც უფლება აქვთ, ზონის გადაცემა შეასრულონ" #: option.c:545 msgid "Specify ipsets to which matching domains should be added" msgstr "მიუთითეთ ipset-ები, რომელშიც შესაბამისი დომენები უნდა დაემატოს" #: option.c:546 msgid "Specify nftables sets to which matching domains should be added" msgstr "მიუთითეთ nfstables-ის სეტი, რომელშიც შესაბამისი დომენები დაემატება" #: option.c:547 msgid "Enable filtering of DNS queries with connection-track marks." msgstr "Connection-track ნიშნების მქონე DNS მოთხოვნების ფილტრაციის ჩართვა." #: option.c:548 msgid "Set allowed DNS patterns for a connection-track mark." msgstr "Connection track ნიშნებისთვის DNS-ის ნებადართული შაბლონების დაყენება." #: option.c:549 msgid "Specify a domain and address range for synthesised names" msgstr "აწყობილი სახელებისთვის დომენის და მისამართების დიაპაზონის მითითება" #: option.c:550 msgid "Activate DNSSEC validation" msgstr "DNSSEC გადამოწმების აქტივაცია" #: option.c:551 msgid "Specify trust anchor key digest." msgstr "" #: option.c:552 msgid "Disable upstream checking for DNSSEC debugging." msgstr "DNSSEC-ის გასამართად აღმავლის შემოწმების გამორთვა." #: option.c:553 msgid "Ensure answers without DNSSEC are in unsigned zones." msgstr "დარწმუნდით, რომ DNSSEC-ის გარეშე პასუხები ხელმოუწერელ ზონებშია." #: option.c:554 msgid "Don't check DNSSEC signature timestamps until first cache-reload" msgstr "პირველ cache-reload-მდე DNSSEC-ის ხელმოწერის დროის შტამპები არ შემოწმდება" #: option.c:555 msgid "Timestamp file to verify system clock for DNSSEC" msgstr "DNSSEC-ისთვის სისტემის საათის შესამოწმებელი დროის შტამპის ფაილი" #: option.c:556 msgid "Set MTU, priority, resend-interval and router-lifetime" msgstr "დააყენეთ MTU, პრიორიტეტი, resend-interval და router-lifetime" #: option.c:557 msgid "Do not log routine DHCP." msgstr "DHCP-ის ფუნქციები ჟურნალში არ ჩაიწერება." #: option.c:558 msgid "Do not log routine DHCPv6." msgstr "DHCPv6-ის ფუნქციები ჟურნალში არ ჩაიწერება." #: option.c:559 msgid "Do not log RA." msgstr "RA ჟურნალში არ ჩაიწერება." #: option.c:560 msgid "Log debugging information." msgstr "გამართვის ინფორმაციის ჟურნალში ჩაწერა." #: option.c:561 msgid "Accept queries only from directly-connected networks." msgstr "მოთხოვნების მხოლოდ პირდაპირ-დაერთებული ქსელებიდან მიღება." #: option.c:562 msgid "Detect and remove DNS forwarding loops." msgstr "DNS-ის გადაგზავნის მარყუჟების აღმოჩენა და მოცილება." #: option.c:563 msgid "Ignore DNS responses containing ipaddr." msgstr "IP მისამართის შემცველი DNS პასუხების იგნორი." #: option.c:564 msgid "Set TTL in DNS responses with DHCP-derived addresses." msgstr "DHCP-ით დაცემულ მისამართებთან ერთად DNS პასუხებში TTL-ის დაყენება." #: option.c:565 msgid "Delay DHCP replies for at least number of seconds." msgstr "DHCP პასუხების გადაგზავნის მითითებული წამების რაოდენობით დაყოვნება." #: option.c:566 msgid "Enables DHCPv4 Rapid Commit option." msgstr "DHCPv4-ის Rapid Commit პარამეტრის ჩართვა." #: option.c:567 msgid "Path to debug packet dump file" msgstr "ბილიკი გამართვის პაკეტის ჩასაწერ ფაილამდე" #: option.c:568 msgid "Mask which packets to dump" msgstr "ჩასაწერი პაკეტების შენიღბვა" #: option.c:569 msgid "Call dhcp-script when lease expiry changes." msgstr "იჯარის ვადის ცვლილებსას dhcp-script -ის გამოძახება." #: option.c:570 msgid "Send Cisco Umbrella identifiers including remote IP." msgstr "Cisco Umbrella იდენტიფიკატორების, დაშორებული IP-ის ჩათვლით, გაგზავნა." #: option.c:571 msgid "Do not log routine TFTP." msgstr "\"TFTP\" რუტინები ჟურნალში არ ჩაიწერება." #: option.c:572 msgid "Suppress round-robin ordering of DNS records." msgstr "" #: option.c:802 #, c-format msgid "" "Usage: dnsmasq [options]\n" "\n" msgstr "" "გამოყენება: dnsmasq [პარამეტრები]\n" "\n" #: option.c:804 #, c-format msgid "Use short options only on the command line.\n" msgstr "მოკლე პარამეტრების მხოლოდ ბრძანებების სტრიქონში გამოყენება.\n" #: option.c:806 #, c-format msgid "Valid options are:\n" msgstr "სწორი პარამეტრებია:\n" #: option.c:853 option.c:1055 msgid "bad address" msgstr "ცუდი მისამართი" #: option.c:882 option.c:886 msgid "bad port" msgstr "არასწორი პორტი" #: option.c:899 option.c:1002 option.c:1048 msgid "interface binding not supported" msgstr "ინტერფეისზე მიბმა მხარდაუჭერელია" #: option.c:955 msgid "Cannot resolve server name" msgstr "" #: option.c:991 msgid "cannot use IPv4 server address with IPv6 source address" msgstr "" #: option.c:997 option.c:1043 msgid "interface can only be specified once" msgstr "ინტერფეისი მხოლოდ ერთხელ შეგიძლიათ მიუთითოთ" #: option.c:1011 option.c:4785 msgid "bad interface name" msgstr "ინტერფეისის არასწორი სახელი" #: option.c:1037 msgid "cannot use IPv6 server address with IPv4 source address" msgstr "" #: option.c:1124 msgid "bad IPv4 prefix length" msgstr "არასწორი IPv4 პრეფიქსის სიგრძე" #: option.c:1155 option.c:1165 option.c:1240 option.c:1250 option.c:5360 msgid "error" msgstr "შეცდომა" #: option.c:1207 msgid "bad IPv6 prefix length" msgstr "არასწორი IPv6 პრეფიქსის სიგრძე" #: option.c:1467 msgid "inappropriate vendor:" msgstr "შეუფერებელი მომწოდებელი:" #: option.c:1474 msgid "inappropriate encap:" msgstr "შეუფერებელი ენკაფსულაცია:" #: option.c:1500 msgid "unsupported encapsulation for IPv6 option" msgstr "მხარდაუჭერელი ენკაფსულაცია IPv6 პარამეტრისთვის" #: option.c:1514 msgid "bad dhcp-option" msgstr "არასწორი dhcp-option" #: option.c:1592 msgid "bad IP address" msgstr "არასწორი IP მისამართი" #: option.c:1595 option.c:1734 option.c:3928 msgid "bad IPv6 address" msgstr "არასწორი IPv6 მისამართი" #: option.c:1688 msgid "bad IPv4 address" msgstr "არასწორი IPv4 მისამართ" #: option.c:1761 option.c:1856 msgid "bad domain in dhcp-option" msgstr "dhcp-option-ში მითითებული დომენი არასწორია" #: option.c:1900 msgid "dhcp-option too long" msgstr "dhcp-option ძალიან გრძელია" #: option.c:1907 msgid "illegal dhcp-match" msgstr "არასწორი dhcp-match" #: option.c:1966 msgid "illegal repeated flag" msgstr "არასწორი გამეორებული ალამი" #: option.c:1974 msgid "illegal repeated keyword" msgstr "არასწორი გამეორებული საკვანზო სიტყვა" #: option.c:2056 option.c:5533 #, c-format msgid "cannot access directory %s: %s" msgstr "%s საქაღალდესთან წვდომის შეცდომა: %s" #: option.c:2102 tftp.c:573 dump.c:72 #, c-format msgid "cannot access %s: %s" msgstr "%s-სთან წვდომის შეცდომა: %s" #: option.c:2219 msgid "setting log facility is not possible under Android" msgstr "ჟურნალის დონეების მითითება Android_ის ქვეშ შეუძლებელია" #: option.c:2228 msgid "bad log facility" msgstr "ჟურნალის არასწორი სამიზნე" #: option.c:2281 msgid "bad MX preference" msgstr "არასწორი MX პრიორიტეტი" #: option.c:2289 msgid "bad MX name" msgstr "არასწორი MX სახელი" #: option.c:2304 msgid "bad MX target" msgstr "არასწორი MX სამიზნე" #: option.c:2324 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts" msgstr "lease-change სკრიპტების ჩასართავად კონფიგურაციაში ჩართეთ HAVE_SCRIPT და ააგეთ პროგრამა თავიდან" #: option.c:2328 msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts" msgstr "lua სკრიპტების ჩასართავად კონფიგურაციაში ჩართეთ HAVE_LUASCRIPT და ააგეთ პროგრამა თავიდან" #: option.c:2447 msgid "invalid auth-zone" msgstr "auth-zone არასწორია" #: option.c:2589 option.c:2621 msgid "bad prefix length" msgstr "არასწორი პრეფიქსის სიგრძე" #: option.c:2601 option.c:2642 option.c:2696 msgid "bad prefix" msgstr "არასწორი პრეფიქსი" #: option.c:2716 msgid "prefix length too small" msgstr "პრეფიქსის სიგრძე ძალიან პატარაა" #: option.c:3010 msgid "Bad address in --address" msgstr "არასწორი მისამართი --address -ში" #: option.c:3110 msgid "recompile with HAVE_IPSET defined to enable ipset directives" msgstr "ipset-ის დირექტივების ჩასართავად კონფიგურაციაში ჩართეთ HAVE_IPSET და ააგეთ პროგრამა თავიდან" #: option.c:3117 msgid "recompile with HAVE_NFTSET defined to enable nftset directives" msgstr "nftset-ის დირექტივების ჩასართავად კონფიგურაციაში ჩართეთ HAVE_NFTSET და ააგეთ პროგრამა თავიდან" #: option.c:3192 option.c:3210 msgid "recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives" msgstr "connmark-allowlist-ის დირექტივების ჩასართავად კონფიგურაციაში ჩართეთ HAVE_CONNTRACK და ააგეთ პროგრამა თავიდან" #: option.c:3496 msgid "bad port range" msgstr "არასწორი პორტის დიაპაზონი" #: option.c:3522 msgid "bad bridge-interface" msgstr "არასწორი bridge-interface" #: option.c:3566 msgid "bad shared-network" msgstr "არასწორი shared-network" #: option.c:3620 msgid "only one tag allowed" msgstr "დაშვებულია მხოლოდ ერთი ჭდე" #: option.c:3641 option.c:3657 option.c:3783 option.c:3791 option.c:3834 msgid "bad dhcp-range" msgstr "არასწორი dhcp-range" #: option.c:3675 msgid "inconsistent DHCP range" msgstr "არამდგრადი DHCP დიაპაზონი" #: option.c:3741 msgid "prefix length must be exactly 64 for RA subnets" msgstr "\"RA\" ქვექსელებისთვის პრეფიქსის სიგრძე ზუსტად 64-ის ტოლი უნდა იყოს" #: option.c:3743 msgid "prefix length must be exactly 64 for subnet constructors" msgstr "ქვექსელის კონსტრუქტორებისთვის პრეფიქსის სიგრძე ზუსტად 64-ის ტოლი უნდა იყოს" #: option.c:3746 msgid "prefix length must be at least 64" msgstr "პრეფიქსის სიგრძე მინიმუმ 64-ის ტოლი უნდა იყოს" #: option.c:3749 msgid "inconsistent DHCPv6 range" msgstr "არამდგრადი DHCPv6 დიაპაზონი" #: option.c:3768 msgid "prefix must be zero with \"constructor:\" argument" msgstr "პრეფიქსი \"constructor:\" არგუმენტთან ერთად ნულის ტოლი უნდა იყოს" #: option.c:3893 option.c:3971 msgid "bad hex constant" msgstr "არასწორი თექვს, კონსტანტა" #: option.c:3946 msgid "bad IPv6 prefix" msgstr "არასწორი IPv6 პრეფიქსი" #: option.c:3994 #, c-format msgid "duplicate dhcp-host IP address %s" msgstr "dhcp-host-ის IP მისამართი %s ორჯერაა მითითებული" #: option.c:4056 msgid "bad DHCP host name" msgstr "არასწორი DHCP ჰოსტის სახელი" #: option.c:4142 msgid "bad tag-if" msgstr "არასწორი tag-if" #: option.c:4490 option.c:5046 msgid "invalid port number" msgstr "პორტის არასწორი ნომერი" #: option.c:4546 msgid "bad dhcp-proxy address" msgstr "არასწორი dhcp-proxy მისამართი" #: option.c:4627 msgid "Bad dhcp-relay" msgstr "არასწორი dhcp-relay" #: option.c:4671 msgid "bad RA-params" msgstr "არასწორი RA-params" #: option.c:4681 msgid "bad DUID" msgstr "არასწორი DUID" #: option.c:4715 msgid "missing address in alias" msgstr "მეტსახელში მისამართი მითითებული არაა" #: option.c:4721 msgid "invalid alias range" msgstr "მეტსახელების არასწორი დიაპაზონი" #: option.c:4770 msgid "missing address in dynamic host" msgstr "დინამიკურ ჰოსტში მისამართი მითითებული არაა" #: option.c:4785 msgid "bad dynamic host" msgstr "ცუდი დინამიკური ჰოსტი" #: option.c:4803 option.c:4819 msgid "bad CNAME" msgstr "არასწორი CNAME" #: option.c:4827 msgid "duplicate CNAME" msgstr "დუბლირებული CNAME" #: option.c:4854 msgid "bad PTR record" msgstr "ცუდი PTR ჩანაწერი" #: option.c:4889 msgid "bad NAPTR record" msgstr "არასწორი NAPTR ჩანაწერი" #: option.c:4925 msgid "bad RR record" msgstr "ცუდი RR ჩანაწერი" #: option.c:4958 msgid "bad CAA record" msgstr "ცუდი CAA ჩანაწერი" #: option.c:4987 msgid "bad TXT record" msgstr "ცუდი TXT ჩანაწერი" #: option.c:5030 msgid "bad SRV record" msgstr "ცუდი SRV ჩანაწერი" #: option.c:5037 msgid "bad SRV target" msgstr "ცუდი SRV სამიზნე" #: option.c:5056 msgid "invalid priority" msgstr "არასწორი პრიორიტეტი" #: option.c:5061 msgid "invalid weight" msgstr "არასწორი წონა" #: option.c:5084 msgid "Bad host-record" msgstr "არასწორი host-record" #: option.c:5123 msgid "Bad name in host-record" msgstr "არასწორი სახელი host-record -ში" #: option.c:5165 msgid "bad value for dnssec-check-unsigned" msgstr "dnssec-check-unsigned-ის არასწორი მნიშვნელობა" #: option.c:5201 msgid "bad trust anchor" msgstr "" #: option.c:5217 msgid "bad HEX in trust anchor" msgstr "" #: option.c:5228 msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)" msgstr "მხარდაუჭერელი პარამეტრი (შეამოწმეთ, dnsmasq აგებულია, თუ არა DHCP/TFTP/DNSSEC/DBus-ის მხარდაჭერით)" #: option.c:5290 msgid "missing \"" msgstr "აკლია \"" #: option.c:5347 msgid "bad option" msgstr "არასწორი პარამეტრი" #: option.c:5349 msgid "extraneous parameter" msgstr "დამატებითი პარამეტრი" #: option.c:5351 msgid "missing parameter" msgstr "ნაკლული პარამეტრი" #: option.c:5353 msgid "illegal option" msgstr "დაუშვებელი პარამეტრი" #: option.c:5363 #, c-format msgid " in output from %s" msgstr " %s-ის გამოტანილში" #: option.c:5365 #, c-format msgid " at line %d of %s" msgstr " %2$s-ის %1$d -ე ხაზზე" #: option.c:5380 option.c:5683 option.c:5694 #, c-format msgid "read %s" msgstr "%s წავიკითხე" #: option.c:5446 #, c-format msgid "cannot execute %s: %s" msgstr "%s-ის შესრულების შეცდომა: %s" #: option.c:5454 option.c:5615 tftp.c:790 #, c-format msgid "cannot read %s: %s" msgstr "%s -ის წაკითხვის შეცდომა: %s" #: option.c:5473 #, c-format msgid "error executing %s: %s" msgstr "შესრულების შეცდომა %s: %s" #: option.c:5476 #, c-format msgid "%s returns non-zero error code" msgstr "%s არანულოვან შეცდომის კოდს აბრუნებს" #: option.c:5775 msgid "junk found in command line" msgstr "ბრძანების სტრიქონში ნაპოვნია ნაგავი" #: option.c:5815 #, c-format msgid "Dnsmasq version %s %s\n" msgstr "Dnsmasq -ის ვერსია %s %s\n" #: option.c:5816 #, c-format msgid "" "Compile time options: %s\n" "\n" msgstr "" "აგების პარამეტრები: %s\n" "\n" #: option.c:5817 #, c-format msgid "This software comes with ABSOLUTELY NO WARRANTY.\n" msgstr "ეს პროგრამა ყველანაირი გარანტიის გარეშეა.\n" #: option.c:5818 #, c-format msgid "Dnsmasq is free software, and you are welcome to redistribute it\n" msgstr "Dnsmasq უფასო პროგრამაა და თავისუფლად შეგიძლიათ, გაავრცელოთ ის\n" #: option.c:5819 #, c-format msgid "under the terms of the GNU General Public License, version 2 or 3.\n" msgstr "ვრცელდება GNU საჯარო ლიცენზიით, ვერსია 2 ან 3.\n" #: option.c:5836 msgid "try --help" msgstr "სცადეთ --help" #: option.c:5838 msgid "try -w" msgstr "სცადეთ -w" #: option.c:5840 #, c-format msgid "bad command line options: %s" msgstr "ბრძანების სტრიქონის არასწორი პარამეტრები: %s" #: option.c:5909 #, c-format msgid "CNAME loop involving %s" msgstr "CNAME-ის მარყუჟი %s -ის მონაწილეობით" #: option.c:5950 #, c-format msgid "cannot get host-name: %s" msgstr "host-name -ის მიღების შეცდომა: %s" #: option.c:5978 msgid "only one resolv.conf file allowed in no-poll mode." msgstr "no-poll რეჟიმში მხოლოდ ერთი resolv.conf-ის მითითებაა შესაძლებელი." #: option.c:5988 msgid "must have exactly one resolv.conf to read domain from." msgstr "დომენის წასაკითხად საჭიროა ზუსტად ერთი resolv.conf-ის ქონა." #: option.c:5991 network.c:1727 dhcp.c:892 #, c-format msgid "failed to read %s: %s" msgstr "%s-ის წაკითხვის შეცდომა: %s" #: option.c:6008 #, c-format msgid "no search directive found in %s" msgstr "%s-ში ძებნის დირექტივა ნაპოვნი არა" #: option.c:6029 msgid "there must be a default domain when --dhcp-fqdn is set" msgstr "როცა --dhcp-fqdn დაყენებულია, ნაგულისხმები დომენის არსებობა აუცილებელია" #: option.c:6038 msgid "syntax check OK" msgstr "სინტაქსის გადამოწმება წარმატებით დასრულდა" #: forward.c:107 #, c-format msgid "failed to send packet: %s" msgstr "პაკეტის გაგზავნის შეცდომა: %s" #: forward.c:715 msgid "discarding DNS reply: subnet option mismatch" msgstr "\"DNS\" პასუხის გაუქმება: ქვექსელის პარამეტრი არ ემთხვევა" #: forward.c:780 #, c-format msgid "nameserver %s refused to do a recursive query" msgstr "სერვერი %s რეკურსიული მოთხოვნის შესრულებაზე უარს აცხადებს" #: forward.c:826 #, c-format msgid "possible DNS-rebind attack detected: %s" msgstr "აღმოჩენილია შესაძლო DNS-rebind შეტევა: %s" #: forward.c:1239 #, c-format msgid "reducing DNS packet size for nameserver %s to %d" msgstr "სერვერისთვის %s DNS პაკეტის ზომის %d-მდე შემცირება" #: forward.c:1565 #, c-format msgid "ignoring query from non-local network %s (logged only once)" msgstr "არა-ლოკალური ქსელიდან (%s) მოსული მოთხოვნის იგნორირება (ჟურნალში იწერება მხოლოდ ერთხელ)" #: forward.c:2139 #, c-format msgid "ignoring query from non-local network %s" msgstr "არალოკალური ქსელიდან (%s) მოსული მოთხოვნის იგნორირება" #: forward.c:2501 #, c-format msgid "failed to bind server socket to %s: %s" msgstr "სერვერის სოკეტის %s-ზე მიბმის შეცდომა: %s" #: forward.c:2867 #, c-format msgid "Maximum number of concurrent DNS queries reached (max: %d)" msgstr "მიღწეულია ერთდრული DNS მოთხოვნების ლიმიტი (მაქს: %d)" #: forward.c:2869 #, c-format msgid "Maximum number of concurrent DNS queries to %s reached (max: %d)" msgstr "მიღწეულია %s-მდე ერთდროული DNS მოთხოვნების ლიმიტი (მაქს: %d)" #: network.c:700 #, c-format msgid "stopped listening on %s(#%d): %s port %d" msgstr "მოსმენა შეწყდა: %s(#%d): %s პორტი %d" #: network.c:911 #, c-format msgid "failed to create listening socket for %s: %s" msgstr "%s-სთვის მოსასმენი სოკეტის შექმნის შეცდომა: %s" #: network.c:1192 #, c-format msgid "listening on %s(#%d): %s port %d" msgstr "ვუსმენ %s(#%d)-ზე: %s პორტი %d" #: network.c:1219 #, c-format msgid "listening on %s port %d" msgstr "ვუსმენ %s -ზე, პორტი %d" #: network.c:1252 #, c-format msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s" msgstr "ხმამაღალი გაფრთხილება: %s-ზე მოსმენამ მოთხოვნების %s-ის გარდა სხვა ინტერფეისებიდანაც მიღებაც შეიძლება გამოიწვიოს" #: network.c:1259 msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)" msgstr "ხმამაღალი გაფრთხილება: ამ ინტერფეისებიდან DNS-ით გაძლიერებული შეტევების თავიდან ასაცილებლად --bind-dynamic -ის მაგიერ --bind-interfaces გამოიყენეთ" #: network.c:1268 #, c-format msgid "warning: using interface %s instead" msgstr "გაფრთხილება: სამაგიეროდ გამოიყენება ინტერფეისი %s" #: network.c:1277 #, c-format msgid "warning: no addresses found for interface %s" msgstr "გაფრთხილება: ინტერფეისისთვის (%s) მისამართის პოვნა შეუძლებელია" #: network.c:1335 #, c-format msgid "interface %s failed to join DHCPv6 multicast group: %s" msgstr "ინტერფეისის (%s) შეცდომა DHCPv6-ის მულტიკასტის ჯგუფში გაწევრებისას: %s" #: network.c:1340 msgid "try increasing /proc/sys/net/core/optmem_max" msgstr "სცადეთ გაზარდოთ /proc/sys/net/core/optmem_max" #: network.c:1545 #, c-format msgid "failed to bind server socket for %s: %s" msgstr "%s-სთვის სერვერის სოკეტის მიბმის შეცდომა: %s" #: network.c:1622 #, c-format msgid "ignoring nameserver %s - local interface" msgstr "\"DNS\" სერვერის (%s) იგნორი - ლოკალური ინტერფეისი" #: network.c:1633 #, c-format msgid "ignoring nameserver %s - cannot make/bind socket: %s" msgstr "\"DNS\" სერვერის (%s) იგნორი - სოკეტის შექმნის/მიბმის შეცდომა: %s" #: network.c:1643 msgid "more servers are defined but not logged" msgstr "აღწერილი მეტი სერვერია, მაგრამ ჟურნალში არ იწერება" #: network.c:1654 msgid "(no DNSSEC)" msgstr "(DNSSEC-ის გარეშე)" #: network.c:1657 msgid "unqualified" msgstr "არაკვალიფიციური" #: network.c:1657 msgid "names" msgstr "სახელები" #: network.c:1659 msgid "default" msgstr "ნაგულისხმები" #: network.c:1661 msgid "domain" msgstr "დომენი" #: network.c:1663 #, c-format msgid "using nameserver %s#%d for %s %s%s %s" msgstr "გამოიყენება DNS სერვერი %s#%d -ი %s %s%s %s -სთვის" #: network.c:1667 #, c-format msgid "NOT using nameserver %s#%d - query loop detected" msgstr "DNS სერვერი გამოყენებული *არ* იქნება %s#%d - აღმოჩენილია მოთხოვნის მარყუჟი" #: network.c:1670 #, c-format msgid "using nameserver %s#%d(via %s)" msgstr "გამოიყენება DNS სერვერ %s#%d(%s-ის გავლით)" #: network.c:1672 #, c-format msgid "using nameserver %s#%d" msgstr "გამოიყენება DNS სერვერი %s#%d" #: network.c:1687 #, c-format msgid "using only locally-known addresses for %s" msgstr "%s-სთვის მხოლოდ ლოკალურად-ცნობილი მისამართების დაყენება" #: network.c:1690 #, c-format msgid "using standard nameservers for %s" msgstr "%s-სთვის სტანდარტული DNS სერვერები გამოიყენება" #: network.c:1694 #, c-format msgid "using %d more local addresses" msgstr "გამოიყენება კიდევ %d ლოკალური მისამართი" #: network.c:1696 #, c-format msgid "using %d more nameservers" msgstr "გამოიყენება კიდევ %d DNS სერვერი" #: dnsmasq.c:192 msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform" msgstr "dhcp-hostsdir, dhcp-optsdir და hostsdir ამ პლატფორმაზე მხარდაჭერილი არაა" #: dnsmasq.c:207 msgid "no root trust anchor provided for DNSSEC" msgstr "" #: dnsmasq.c:210 msgid "cannot reduce cache size from default when DNSSEC enabled" msgstr "ნაგულისხმები ქეშის ზომის შეცვლა მაშინ, როცა DNSSEC ჩართულია, შეუძლებელია" #: dnsmasq.c:212 msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h" msgstr "DNSSEC მიუწვდომელია: დააყენეთ HAVE_DNSSEC src/config.h -ში" #: dnsmasq.c:218 msgid "TFTP server not available: set HAVE_TFTP in src/config.h" msgstr "TFTP სერვერი მიუწვდომელია: დააყენეთ HAVE_TFTP src/config.h -ში" #: dnsmasq.c:225 msgid "cannot use --conntrack AND --query-port" msgstr "--conntrack -ისა და --query-port -ის ერთად გამოყენება შეუძლებელია" #: dnsmasq.c:231 msgid "conntrack support not available: set HAVE_CONNTRACK in src/config.h" msgstr "conntrack-ის მხარდაჭერა მიუწვდომელია: დააყენეთ HAVE_CONNTRACK ფაილში src/config.h" #: dnsmasq.c:236 msgid "asynchronous logging is not available under Solaris" msgstr "solaris-ში ასინქრონული ჟურნალი ხელმიუწვდომელია" #: dnsmasq.c:241 msgid "asynchronous logging is not available under Android" msgstr "android-ში ასინქრონული ჟურნალი ხელმიუწვდომელია" #: dnsmasq.c:246 msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h" msgstr "ავტორიტეტული DNS მიუწვდომელია: დააყენეთ HAVE_AUTH src/confg.h-ში" #: dnsmasq.c:251 msgid "loop detection not available: set HAVE_LOOP in src/config.h" msgstr "მარყუჟის აღმოჩენა ხელმიუწვდომელია: დააყენეთ HAVE_LOOP src/config.h -ში" #: dnsmasq.c:256 msgid "Ubus not available: set HAVE_UBUS in src/config.h" msgstr "Ubus-ის მხარდაჭერა მიუწვდომელია: დააყენეთ HAVE_UBUS ფაილში src/config.h" #: dnsmasq.c:267 msgid "max_port cannot be smaller than min_port" msgstr "max_port-ის მნიშვნელობა min_port-ზე მცირე ვერ იქნება" #: dnsmasq.c:271 msgid "port_limit must not be larger than available port range" msgstr "" #: dnsmasq.c:278 msgid "--auth-server required when an auth zone is defined." msgstr "--auth-server აუცილებელია, როცა ავთენტიკაციის ზონა აღწერილია." #: dnsmasq.c:283 msgid "zone serial must be configured in --auth-soa" msgstr "ზონის სერიული აუცილებლად უნდა მიუთითოთ --auth-soa -ში" #: dnsmasq.c:303 msgid "dhcp-range constructor not available on this platform" msgstr "dhcp-range -ის კონსტრუქტორი ამ პლატფორმაზე ხელმიუწვდომელია" #: dnsmasq.c:377 msgid "cannot set --bind-interfaces and --bind-dynamic" msgstr "--bind-interfaces -ისა და --bind-dyanamic -ის ერთად გამოყენება შეუძლებელია" #: dnsmasq.c:380 #, c-format msgid "failed to find list of interfaces: %s" msgstr "ინტერფეისების სიის პოვნა შეუძლებელია: %s" #: dnsmasq.c:389 #, c-format msgid "unknown interface %s" msgstr "უცნობი ინტერფეისი %s" #: dnsmasq.c:396 #, c-format msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s" msgstr "შეცდომა DHCP სოკეტზე SO_BINDTODEVICE-ის დაყენებისას: %s" #: dnsmasq.c:440 msgid "Packet dumps not available: set HAVE_DUMP in src/config.h" msgstr "პაკეტების ჩაწერა ხელმიუწვდომელია. დააყენეთ HAVE_DUMP src/config.h-ში" #: dnsmasq.c:448 dnsmasq.c:1232 #, c-format msgid "DBus error: %s" msgstr "DBus-ის შეცდომა: %s" #: dnsmasq.c:451 msgid "DBus not available: set HAVE_DBUS in src/config.h" msgstr "DBUS ხელმიუწვდომელია: დააყენეთ HAVE_DBUS src/config.h-ში" #: dnsmasq.c:459 dnsmasq.c:1253 #, c-format msgid "UBus error: %s" msgstr "UBus -ის შეცდომა: %s" #: dnsmasq.c:462 msgid "UBus not available: set HAVE_UBUS in src/config.h" msgstr "UBUS ხელმიუწვდომელია: დააყენეთ HAVE_UBUS src/config.h-ში" #: dnsmasq.c:492 #, c-format msgid "unknown user or group: %s" msgstr "უცნობი მომხმარებელი ან ჯგუფი: %s" #: dnsmasq.c:568 #, c-format msgid "process is missing required capability %s" msgstr "პროცესს აუცილებელი შესაძლებლობა აკლია: %s" #: dnsmasq.c:600 #, c-format msgid "cannot chdir to filesystem root: %s" msgstr "ფაილური სისტემის საწყის საქაღალდეში chdir-ის შეცდომა: %s" #: dnsmasq.c:852 #, c-format msgid "started, version %s DNS disabled" msgstr "გაეშვა, ვერსია %s DNS გამორთულია" #: dnsmasq.c:857 #, c-format msgid "started, version %s cachesize %d" msgstr "გაეშვა, ვერსია %s ქეშის ზომაა %d" #: dnsmasq.c:859 msgid "cache size greater than 10000 may cause performance issues, and is unlikely to be useful." msgstr "თუ ქეშის ზომა 10000-ზე დიდია, მას წარმადობის პრობლემები შეუძლია გამოიწვიოს, სარგებლობა კი ნაკლებად." #: dnsmasq.c:862 #, c-format msgid "started, version %s cache disabled" msgstr "გაეშვა, ვერსია %s ქეში გამორთულია" #: dnsmasq.c:865 msgid "DNS service limited to local subnets" msgstr "DNS სერვისი შეზღუდულია ლოკალურ ქვექსელებამდე" #: dnsmasq.c:868 #, c-format msgid "compile time options: %s" msgstr "აგების პარამეტრები: %s" #: dnsmasq.c:877 msgid "DBus support enabled: connected to system bus" msgstr "DBUS-ის მხარდაჭერა ჩართულია: სისტემურ მატარებელზე დაერთება წარმატებულია" #: dnsmasq.c:879 msgid "DBus support enabled: bus connection pending" msgstr "DBUS-ის მხარდაჭერა ჩართულია: სისტემურ მატარებელზე დაერთების მოლოდინი" #: dnsmasq.c:887 msgid "UBus support enabled: connected to system bus" msgstr "UBUS-ის მხარდაჭერა ჩართულია: სისტემურ მატარებელზე დაერთება წარმატებულია" #: dnsmasq.c:889 msgid "UBus support enabled: bus connection pending" msgstr "UBUS-ის მხარდაჭერა ჩართულია: სისტემურ მატარებელზე დაერთების მოლოდინი" #: dnsmasq.c:909 msgid "DNSSEC validation enabled but all unsigned answers are trusted" msgstr "DNSSEC გადამოწმება ჩართულია, მაგრამ ყველა ხელმოუწერელი პასუხი, როგორც სანდოა გამოცხადებული" #: dnsmasq.c:911 msgid "DNSSEC validation enabled" msgstr "DNSSEC -ის გადამოწმება ჩართულია" #: dnsmasq.c:915 msgid "DNSSEC signature timestamps not checked until receipt of SIGINT" msgstr "DNSSEC-ის ხელმოწერის დროის შტამპები SIGINT-ის მიღებამდე არ მოწმდება" #: dnsmasq.c:918 msgid "DNSSEC signature timestamps not checked until system time valid" msgstr "DNSSEC-ის ხელმოწერის დროის შტამპები არ მოწმდება, სანამ სისტემური დრო სწორი არაა" #: dnsmasq.c:921 #, c-format msgid "configured with trust anchor for %s keytag %u" msgstr "" #: dnsmasq.c:927 #, c-format msgid "warning: failed to change owner of %s: %s" msgstr "გაფრთხილება: %s-ის მფლობელის შეცვლის შეცდომა: %s" #: dnsmasq.c:932 msgid "setting --bind-interfaces option because of OS limitations" msgstr "--bind-interfaces პარამეტრი აირთო OS-ის შეზღუდვების გამო" #: dnsmasq.c:945 #, c-format msgid "warning: interface %s does not currently exist" msgstr "გაფრთხილება: ინტერფეისი %s ამჟამად არ არსებობს" #: dnsmasq.c:950 msgid "warning: ignoring resolv-file flag because no-resolv is set" msgstr "გაფრთხილება: იმის გამო, რომ no-resolv დაყენებულია, resolv-file ალამი იგნორირებული იქნება" #: dnsmasq.c:953 msgid "warning: no upstream servers configured" msgstr "გაფრთხილება: აღმავალი სერვერები მორგებული არაა" #: dnsmasq.c:957 #, c-format msgid "asynchronous logging enabled, queue limit is %d messages" msgstr "ასინქრონული ჟურნალი ჩართულია. რიგის ზღვარი %d შეტყობინებაა" #: dnsmasq.c:978 msgid "IPv6 router advertisement enabled" msgstr "IPv6 რაუტერის გამოცხადება ჩარტულია" #: dnsmasq.c:983 #, c-format msgid "DHCP, sockets bound exclusively to interface %s" msgstr "DHCP და სოკეტები ექსკლუზიურადაა მიბმული ინტერფეისზე %s" #: dnsmasq.c:1000 msgid "root is " msgstr "საწყისი საქაღალდეა " #: dnsmasq.c:1000 msgid "enabled" msgstr "ჩართულია" #: dnsmasq.c:1002 msgid "secure mode" msgstr "დაცული რეჟიმი" #: dnsmasq.c:1003 msgid "single port mode" msgstr "ერთი პორტის რეჟიმი" #: dnsmasq.c:1006 #, c-format msgid "warning: %s inaccessible" msgstr "გაფრთხილება: %s მიუწვდომელია" #: dnsmasq.c:1010 #, c-format msgid "warning: TFTP directory %s inaccessible" msgstr "გაფრთხილება: TFTP საქაღალდე %s მუწვდომელია" #: dnsmasq.c:1036 #, c-format msgid "restricting maximum simultaneous TFTP transfers to %d" msgstr "მაქსიმალურად ერთდროული TFTP მიმოცვლების %d-მდე შეზღუდვა" #: dnsmasq.c:1095 #, c-format msgid "error binding DHCP socket to device %s" msgstr "შეცდომა DHCP სოკეტის %s-ზე მიბმისას" #: dnsmasq.c:1229 msgid "connected to system DBus" msgstr "სისტემურ DBus-თან დაკავშირება ვერ მოხერხდა" #: dnsmasq.c:1250 msgid "connected to system UBus" msgstr "სისტემურ UBus-თან დაკავშირება ვერ მოხერხდა" #: dnsmasq.c:1416 #, c-format msgid "cannot fork into background: %s" msgstr "ფონში გაშვების პრობლემა: %s" #: dnsmasq.c:1420 #, c-format msgid "failed to create helper: %s" msgstr "დამხმარე პროცესის შექმნის შეცდომა: %s" #: dnsmasq.c:1424 #, c-format msgid "setting capabilities failed: %s" msgstr "შესაძლებლობების დაყენების შეცდომა: %s" #: dnsmasq.c:1428 #, c-format msgid "failed to change user-id to %s: %s" msgstr "user-id-ის %s-ზე შეცვლის შეცდომა: %s" #: dnsmasq.c:1432 #, c-format msgid "failed to change group-id to %s: %s" msgstr "group-id-ის %s-ზე შეცვლის შეცდომა: %s" #: dnsmasq.c:1436 #, c-format msgid "failed to open pidfile %s: %s" msgstr "pid-ის ფაილის, %s-ის გახსნის შეცდომა: %s" #: dnsmasq.c:1440 #, c-format msgid "cannot open log %s: %s" msgstr "ჟურნალის (%s) გახსნის შეცდომა: %s" #: dnsmasq.c:1444 #, c-format msgid "failed to load Lua script: %s" msgstr "lua-ის სკრიპტის ჩატვირთვის შეცდომა: %s" #: dnsmasq.c:1448 #, c-format msgid "TFTP directory %s inaccessible: %s" msgstr "TFTP საქაღალდე %s მიუწვდომელია: %s" #: dnsmasq.c:1452 #, c-format msgid "cannot create timestamp file %s: %s" msgstr "დროის შტამპის ფაილის (%s) შექმნის შეცდომა: %s" #: dnsmasq.c:1536 #, c-format msgid "script process killed by signal %d" msgstr "სკრიპტის პროცესი მოკვდა სიგნალით %d" #: dnsmasq.c:1540 #, c-format msgid "script process exited with status %d" msgstr "სკრიპტის პროცესმა მუშაობა დაასრულა სტატუსით %d" #: dnsmasq.c:1544 #, c-format msgid "failed to execute %s: %s" msgstr "%s-ის შესრულების შეცდომა: %s" #: dnsmasq.c:1584 msgid "now checking DNSSEC signature timestamps" msgstr "ახლა DNSSEC-ის ხელმოწერის დროის შტამპები შემოწმდება" #: dnsmasq.c:1619 dnssec.c:160 dnssec.c:204 #, c-format msgid "failed to update mtime on %s: %s" msgstr "%s-ზე mtime-ის განახლების შეცდომა: %s" #: dnsmasq.c:1631 msgid "exiting on receipt of SIGTERM" msgstr "მუშაობის დასრულება SIGTERM-ის გამო" #: dnsmasq.c:1659 #, c-format msgid "failed to access %s: %s" msgstr "\"%s\"-სთან წვდომის შეცდომა: %s" #: dnsmasq.c:1690 #, c-format msgid "reading %s" msgstr "%s-ის წაკითხვა" #: dnsmasq.c:1706 #, c-format msgid "no servers found in %s, will retry" msgstr "%s-ში სერვერები ვერ ვიპოვე. კიდევ ვცდი" #: dhcp.c:51 #, c-format msgid "cannot create DHCP socket: %s" msgstr "შეცდომა DHCP სოკეტის შექმნისას: %s" #: dhcp.c:66 #, c-format msgid "failed to set options on DHCP socket: %s" msgstr "შეცდომა DHCP სოკეტის პარამეტრების დაყენებისას: %s" #: dhcp.c:87 #, c-format msgid "failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s" msgstr "შეცდომა SO_REUSE{ADDR|PORT}-ის DHCP სოკეტზე დაყენებისას: %s" #: dhcp.c:99 #, c-format msgid "failed to bind DHCP server socket: %s" msgstr "შეცდომა DHCP სერვერის სოკეტზე მიბმისას: %s" #: dhcp.c:125 #, c-format msgid "cannot create ICMP raw socket: %s." msgstr "\"ICMP\" დაუმუშავებელი სოკეტის შექმნის შეცდომა: %s." #: dhcp.c:254 dhcp6.c:186 #, c-format msgid "unknown interface %s in bridge-interface" msgstr "bride-interface-ის უცნობი ინტერფეისი: %s" #: dhcp.c:295 #, c-format msgid "DHCP packet received on %s which has no address" msgstr "DHCP პაკეტი მიღებულია %s-დან, რომელსაც მისამართი არ გააჩნია" #: dhcp.c:429 #, c-format msgid "ARP-cache injection failed: %s" msgstr "ARP-ქეშში ჩაწერის შეცდომა: %s" #: dhcp.c:490 #, c-format msgid "Error sending DHCP packet to %s: %s" msgstr "%s-სთვის DHCP პაკეტის გაგზავნის შეცდომა: %s" #: dhcp.c:547 #, c-format msgid "DHCP range %s -- %s is not consistent with netmask %s" msgstr "DHCP დიაპაზონი %s -- %s ქსელის ნიღაბს %s არ შეესაბამება" #: dhcp.c:930 #, c-format msgid "bad line at %s line %d" msgstr "არასწორი ხაზი მისამართზე %s ხაზი %d" #: dhcp.c:973 #, c-format msgid "ignoring %s line %d, duplicate name or IP address" msgstr "%s-ის ხაზი %d დაიგნორდება. სახელი ან IP მისამართი დუბლირებულია" #: dhcp.c:1034 #, c-format msgid "read %s - %d addresses" msgstr "წავიკითხე %s - %d მისამართი" #: dhcp.c:1136 #, c-format msgid "Cannot broadcast DHCP relay via interface %s" msgstr "DHCP გადაგზავნის მაუწყებლობა ინტერფეისით %s შეუძლებელია" #: dhcp.c:1160 #, c-format msgid "broadcast via %s" msgstr "მაუწყებლობა %s-ის გავლით" #: dhcp.c:1163 rfc3315.c:2219 #, c-format msgid "DHCP relay at %s -> %s" msgstr "DHCP გადაგზავნა %s -> %s" #: lease.c:64 #, c-format msgid "ignoring invalid line in lease database: %s %s %s %s ..." msgstr "იჯარების ბაზის არასწორი ხაზი იგნორირებულია: %s %s %s %s ..." #: lease.c:101 #, c-format msgid "ignoring invalid line in lease database, bad address: %s" msgstr "იჯარების ბაზის არასწორი ხაზი. არასწორი მისამართი: %s" #: lease.c:108 msgid "too many stored leases" msgstr "მეტისმეტად ბევრი დამახსოვრებული იჯარა" #: lease.c:176 #, c-format msgid "cannot open or create lease file %s: %s" msgstr "იჯარების ფაილის (%s) გახსნის ან შექმნის შეცდომა: %s" #: lease.c:185 msgid "failed to parse lease database cleanly" msgstr "იჯარების ბაზის სუფთად დამუშავების შეცდომა" #: lease.c:188 #, c-format msgid "failed to read lease file %s: %s" msgstr "იჯარის ფაილის (%s) წაკითხვის შეცდომა: %s" #: lease.c:204 #, c-format msgid "cannot run lease-init script %s: %s" msgstr "lease-init სკრიპტის (%s) გაშვების შეცდომა: %s" #: lease.c:210 #, c-format msgid "lease-init script returned exit code %s" msgstr "lease-init-ის სკრიპტის დაბრუნებული გამოსვლის კოდია: %s" #: lease.c:381 #, c-format msgid "failed to write %s: %s (retry in %u s)" msgstr "%s-ში ჩაწერის შეცდომა: %s (თავიდან ვცდი %u წამში)" #: lease.c:955 #, c-format msgid "Ignoring domain %s for DHCP host name %s" msgstr "დომენის (%s) იგნორირება DHCP ჰოსტის სახელისთვის %s" #: rfc2131.c:378 msgid "with subnet selector" msgstr "ქვექსელის გადამრთველით" #: rfc2131.c:383 msgid "via" msgstr "გავლით" #: rfc2131.c:389 #, c-format msgid "no address range available for DHCP request %s %s" msgstr "მისამართების დიაპაზონი მიუწვდომელია DHCP მოთხოვნისთვის %s %s" #: rfc2131.c:403 #, c-format msgid "%u available DHCP subnet: %s/%s" msgstr "%u ხელმისაწვდომი DHCP ქვექსელი: %s/%s" #: rfc2131.c:409 rfc3315.c:320 #, c-format msgid "%u available DHCP range: %s -- %s" msgstr "%u ხელმისაწვდომი DHCP დიაპაზონი: %s -- %s" #: rfc2131.c:521 #, c-format msgid "%u vendor class: %s" msgstr "%u მომწოდებლის კლასი: %s" #: rfc2131.c:523 #, c-format msgid "%u user class: %s" msgstr "%u მომხმარებლის კლასი: %s" #: rfc2131.c:557 msgid "disabled" msgstr "გამორთულია" #: rfc2131.c:598 rfc2131.c:1087 rfc2131.c:1536 rfc3315.c:633 rfc3315.c:816 #: rfc3315.c:1122 msgid "ignored" msgstr "იგნორირებულია" #: rfc2131.c:613 rfc2131.c:1340 rfc3315.c:868 msgid "address in use" msgstr "მისამართი გამოიყენება" #: rfc2131.c:627 rfc2131.c:1141 msgid "no address available" msgstr "მისამართი მიუწვდომელია" #: rfc2131.c:634 rfc2131.c:1302 msgid "wrong network" msgstr "არასწორი ქსელი" #: rfc2131.c:649 msgid "no address configured" msgstr "მორგებული მისამართის გარეშე" #: rfc2131.c:655 rfc2131.c:1353 msgid "no leases left" msgstr "იჯარები აღარ დარჩა" #: rfc2131.c:756 rfc3315.c:500 #, c-format msgid "%u client provides name: %s" msgstr "%u კლიენტის მომწოდებლის სახელი: %s" #: rfc2131.c:885 msgid "PXE BIS not supported" msgstr "PXE BIS მხარდაუჭერელია" #: rfc2131.c:1054 rfc3315.c:1223 #, c-format msgid "disabling DHCP static address %s for %s" msgstr "\"DHCP\" სტატიკური მისამართის (%s) გათიშვა %s-სთვის" #: rfc2131.c:1075 msgid "unknown lease" msgstr "უცნობი იჯარა" #: rfc2131.c:1110 #, c-format msgid "not using configured address %s because it is leased to %s" msgstr "მითითებული მისამართი (%s) არ გამოიყენება. ის უკვეა იჯარით გაცემული %s-ზე" #: rfc2131.c:1120 #, c-format msgid "not using configured address %s because it is in use by the server or relay" msgstr "მითითებული მისამართი %s გამოყენებული არ იქნება, რადგან ის სერვერის ან გადამგზავნის მიერ უკვე გამოიყენება" #: rfc2131.c:1123 #, c-format msgid "not using configured address %s because it was previously declined" msgstr "მითითებული მისამართი %s გამოყენებული არ იქნება, რადგან ის წარსულში უკვე უარყოფილი იყო" #: rfc2131.c:1139 rfc2131.c:1346 msgid "no unique-id" msgstr "unique-id -ის გარეშე" #: rfc2131.c:1238 msgid "wrong server-ID" msgstr "არასწორი server-ID" #: rfc2131.c:1257 msgid "wrong address" msgstr "არასწორი მისამართი" #: rfc2131.c:1275 rfc3315.c:976 msgid "lease not found" msgstr "იჯარა ვერ ვიპოვე" #: rfc2131.c:1310 msgid "address not available" msgstr "მისამართი მიუწვდომელია" #: rfc2131.c:1321 msgid "static lease available" msgstr "ხელმისაწვდომია სტატიკური იჯარა" #: rfc2131.c:1325 msgid "address reserved" msgstr "მისამართი დარეზერვებულია" #: rfc2131.c:1334 #, c-format msgid "abandoning lease to %s of %s" msgstr "%2$s-ის იჯარის მოცილება: %1$s" #: rfc2131.c:1870 #, c-format msgid "%u bootfile name: %s" msgstr "%u ჩასატვირთი ფაილის სახელი: %s" #: rfc2131.c:1879 #, c-format msgid "%u server name: %s" msgstr "%u სერვერის სახელი: %s" #: rfc2131.c:1889 #, c-format msgid "%u next server: %s" msgstr "%u შემდეგი სერვერი: %s" #: rfc2131.c:1893 #, c-format msgid "%u broadcast response" msgstr "%u სამაუწყებლო პასუხი" #: rfc2131.c:1956 #, c-format msgid "cannot send DHCP/BOOTP option %d: no space left in packet" msgstr "შეცდომა DHCP/BOOTP პარამეტრის %d გაგზავნისას: პაკეტში ადგილი აღარაა" #: rfc2131.c:2267 msgid "PXE menu too large" msgstr "PXE მენიუ ძალიან დიდია" #: rfc2131.c:2430 rfc3315.c:1517 #, c-format msgid "%u requested options: %s" msgstr "%u მოთხოვნილ პარამეტრები: %s" #: rfc2131.c:2747 #, c-format msgid "cannot send RFC3925 option: too many options for enterprise number %d" msgstr "შეცდომა RFC3825 პარამეტრის გაგზავნისას: საწარმოო რიცხვისთვის %d მეტისმეტად ბევრი პარამეტრი" #: rfc2131.c:2810 #, c-format msgid "%u reply delay: %d" msgstr "%u პასუხის დაყოვნება: %d" #: netlink.c:86 #, c-format msgid "cannot create netlink socket: %s" msgstr "netlink სოკეტის შექმნის შეცდომა: %s" #: netlink.c:379 #, c-format msgid "netlink returns error: %s" msgstr "netlink -ის დაბრუნებული შეცდომა: %s" #: dbus.c:491 #, c-format msgid "Enabling --%s option from D-Bus" msgstr "ჩართვა --%s პარამეტრი D-Bus -დან" #: dbus.c:496 #, c-format msgid "Disabling --%s option from D-Bus" msgstr "გამორთვა --%s პარამეტრი D-Bus -დან" #: dbus.c:857 msgid "setting upstream servers from DBus" msgstr "აღმავალი სერვერების DBus-დან დაყენება" #: dbus.c:907 msgid "could not register a DBus message handler" msgstr "\"DBus\"-ის შეტყობინების დამმუშავებლის რეგისტრაციის შეცდომა" #: bpf.c:261 #, c-format msgid "cannot create DHCP BPF socket: %s" msgstr "\"DHCP BPF სოკეტის\" შექმნის შეცდომა: %s" #: bpf.c:289 #, c-format msgid "DHCP request for unsupported hardware type (%d) received on %s" msgstr "%2$s-ზე მიღებულია DHCP მოთხოვნა მხარდაუჭერელი აპარატურის ტიპისთვის (%1$d)" #: bpf.c:374 #, c-format msgid "cannot create PF_ROUTE socket: %s" msgstr "შეცდომა PF_ROUTE სოკეტის შექმნისას: %s" #: bpf.c:395 msgid "Unknown protocol version from route socket" msgstr "უცნობი პროტოკოლი რაუტის სოკეტიდან" #: helper.c:150 msgid "lease() function missing in Lua script" msgstr "lua-ის სკრიპტს lease() ფუნქცია აკლია" #: tftp.c:353 msgid "unable to get free port for TFTP" msgstr "შეცდომა TFTP-სთვის თავისუფალი პორტის მიღებისას" #: tftp.c:369 #, c-format msgid "unsupported request from %s" msgstr "მხარდაუჭერელი მოთხოვნა %s-დან" #: tftp.c:520 #, c-format msgid "file %s not found for %s" msgstr "ფაილი %s ვერ ვიპოვე %s_სთვის" #: tftp.c:609 #, c-format msgid "ignoring packet from %s (TID mismatch)" msgstr "%s-დან მოსული პაკეტის იგნორირება (TID არ ემთხვევა)" #: tftp.c:662 #, c-format msgid "failed sending %s to %s" msgstr "%s-ის %s-სთვის გაგზავნის შეცდომა" #: tftp.c:662 #, c-format msgid "sent %s to %s" msgstr "%s გაგზავნილია %s-სთან" #: tftp.c:712 #, c-format msgid "error %d %s received from %s" msgstr "შეცდომა %d %s მიღებულია %s -გან" #: log.c:203 #, c-format msgid "overflow: %d log entries lost" msgstr "გადავსება: დაკარგულია %d ჟურნალის ჩანაწერი" #: log.c:281 #, c-format msgid "log failed: %s" msgstr "ჟურნალის შეცდომა: %s" #: log.c:490 msgid "FAILED to start up" msgstr "გაშვების შეცდომა" #: conntrack.c:63 #, c-format msgid "Conntrack connection mark retrieval failed: %s" msgstr "Conntrack-ის შეერთების ნიშნის მიღების შეცდომა: %s" #: dhcp6.c:51 #, c-format msgid "cannot create DHCPv6 socket: %s" msgstr "შეცდომა DHCPv6 სოკეტის შექმნისას: %s" #: dhcp6.c:72 #, c-format msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s" msgstr "შეცდომა SO_REUSE{ADDR|PORT}-ის DHCPv6 სოკეტზე დაყენებისას: %s" #: dhcp6.c:84 #, c-format msgid "failed to bind DHCPv6 server socket: %s" msgstr "შეცდომა DHCPv6 სერვერის სოკეტზე მიბმისას: %s" #: rfc3315.c:173 #, c-format msgid "no address range available for DHCPv6 request from relay at %s" msgstr "გადამგზავნელიდან (%s) DHCPv6 მოთხოვნისთვის მისამართის დიაპაზონი ხელმისაწვდომი არაა" #: rfc3315.c:182 #, c-format msgid "no address range available for DHCPv6 request via %s" msgstr "%s-ის გავლით DHCPv6 მოთხოვნისთვის მისამართის დიაპაზონი ხელმისაწვდომი არაა" #: rfc3315.c:317 #, c-format msgid "%u available DHCPv6 subnet: %s/%d" msgstr "%u ხელმისაწვდომი DHCPv6 ქვექსელი: %s/%d" #: rfc3315.c:400 #, c-format msgid "%u vendor class: %u" msgstr "%u მომწოდებლის კლასი: %u" #: rfc3315.c:448 #, c-format msgid "%u client MAC address: %s" msgstr "%u კლიენტის MAC მისამართი: %s" #: rfc3315.c:763 rfc3315.c:860 msgid "address unavailable" msgstr "მისამართი ხელმიუწვდომელია" #: rfc3315.c:775 rfc3315.c:904 rfc3315.c:1273 msgid "success" msgstr "წარმატება" #: rfc3315.c:790 rfc3315.c:799 rfc3315.c:912 rfc3315.c:914 rfc3315.c:1048 msgid "no addresses available" msgstr "მისამართები მიუწვდომელია" #: rfc3315.c:891 msgid "not on link" msgstr "შეერთებული არაა" #: rfc3315.c:980 rfc3315.c:1181 rfc3315.c:1262 msgid "no binding found" msgstr "მიბმა ვერ ვპოვე" #: rfc3315.c:1017 msgid "deprecated" msgstr "მოძველებული" #: rfc3315.c:1024 msgid "address invalid" msgstr "მისამართი არასწორია" #: rfc3315.c:1082 rfc3315.c:1084 msgid "confirm failed" msgstr "დადასტურების შეცდომა" #: rfc3315.c:1099 msgid "all addresses still on link" msgstr "ყველა მისამართი ჯერ კიდევ მიერთებულია" #: rfc3315.c:1190 msgid "release received" msgstr "გათავისუფლება მიღებულია" #: rfc3315.c:2200 #, c-format msgid "Cannot multicast DHCP relay via interface %s" msgstr "DHCP გადაგზავნის მაუწყებლობა შეუძლებელია ინტერფეისის გავლით: %s" #: rfc3315.c:2216 #, c-format msgid "multicast via %s" msgstr "მულტიკასტი %s-ის გავლით" #: dhcp-common.c:187 #, c-format msgid "Ignoring duplicate dhcp-option %d" msgstr "ორჯერ აღწერილი dhcp-option %d იგნორირებულია" #: dhcp-common.c:264 #, c-format msgid "%u tags: %s" msgstr "%u ჭდე: %s" #: dhcp-common.c:484 #, c-format msgid "%s has more than one address in hostsfile, using %s for DHCP" msgstr "%s -ს hosts ფაილში ერთზე მეტი მისამართი აქვს მითითებული. DHCP-სთვის გამოყენებული იქნება %s" #: dhcp-common.c:518 #, c-format msgid "duplicate IP address %s (%s) in dhcp-config directive" msgstr "დუბლირებული IP მისამართი %s (%s) dhcp-config-დირექტივაში" #: dhcp-common.c:738 #, c-format msgid "Known DHCP options:\n" msgstr "ცნობილი DHCP პარამეტრები:\n" #: dhcp-common.c:749 #, c-format msgid "Known DHCPv6 options:\n" msgstr "ცნობილი DHCPv6 პარამეტრები:\n" #: dhcp-common.c:946 msgid ", prefix deprecated" msgstr ", პრეფიქსი მოძველებულია" #: dhcp-common.c:949 #, c-format msgid ", lease time " msgstr ", იჯარის დრო " #: dhcp-common.c:991 #, c-format msgid "%s stateless on %s%.0s%.0s%s" msgstr "" #: dhcp-common.c:993 #, c-format msgid "%s, static leases only on %.0s%s%s%.0s" msgstr "%s, სტატიკური იჯარები მხოლოდ %.0s%s%s%.0s" #: dhcp-common.c:995 #, c-format msgid "%s, proxy on subnet %.0s%s%.0s%.0s" msgstr "%s, პროქსი ქვექსელზე %.0s%s%.0s%.0s" #: dhcp-common.c:996 #, c-format msgid "%s, IP range %s -- %s%s%.0s" msgstr "%s, IP დიაპაზონი %s -- %s%s%.0s" #: dhcp-common.c:1009 #, c-format msgid "DHCPv4-derived IPv6 names on %s%s" msgstr "DHCPv4-დან მიღებული IPv6 სახელები %s%s -ზე" #: dhcp-common.c:1012 #, c-format msgid "router advertisement on %s%s" msgstr "რაუტერის გამოცხადება %s%s-ზე" #: dhcp-common.c:1043 #, c-format msgid "DHCP relay from %s via %s" msgstr "DHCP გადაგზავნა %s-დან %s-ის გავლით" #: dhcp-common.c:1045 #, c-format msgid "DHCP relay from %s to %s via %s" msgstr "DHCP გადაგზავნა %s-დან %s-მდე %s-ის ჩათვლით" #: dhcp-common.c:1048 #, c-format msgid "DHCP relay from %s to %s" msgstr "DHCP გადაგზავნა %s -დან %s -მდე" #: radv.c:110 #, c-format msgid "cannot create ICMPv6 socket: %s" msgstr "შეცდომა ICMPv6 სოკეტის შექმნისას: %s" #: auth.c:462 #, c-format msgid "ignoring zone transfer request from %s" msgstr "%s-დან ზონის გადაცემის მოთხოვნის იგნორირება" #: ipset.c:99 #, c-format msgid "failed to create IPset control socket: %s" msgstr "ipset-ის საკონტროლო სოკეტის შექმნის შეცდომა: %s" #: ipset.c:211 #, c-format msgid "failed to update ipset %s: %s" msgstr "ipset-ის (%s) განახლების შეცდომა: %s" #: pattern.c:29 #, c-format msgid "[pattern.c:%d] Assertion failure: %s" msgstr "[pattern.c:%d] მტკიცების შეცდომა: %s" #: pattern.c:142 #, c-format msgid "Invalid DNS name: Invalid character %c." msgstr "არასწორი DNS სახელი: არასწორი სიმბოლო %c." #: pattern.c:151 msgid "Invalid DNS name: Empty label." msgstr "არასწორი DNS სახელი: ჭდე ცარიელია." #: pattern.c:156 msgid "Invalid DNS name: Label starts with hyphen." msgstr "არასწორი DNS სახელი: ჭდე გამოტოვებით იწყება." #: pattern.c:170 msgid "Invalid DNS name: Label ends with hyphen." msgstr "არასწორი DNS სახელი: ჭდე გამოტოვებით მთავრდება." #: pattern.c:176 #, c-format msgid "Invalid DNS name: Label is too long (%zu)." msgstr "არასწორი DNS სახელი: ჭდე ძალიან გრძელია (%zu)" #: pattern.c:184 #, c-format msgid "Invalid DNS name: Not enough labels (%zu)." msgstr "არასწორი DNS სახელი: ჭდეების არასაკმარისი რაოდენობა (%zu)" #: pattern.c:189 msgid "Invalid DNS name: Final label is fully numeric." msgstr "არასწორი DNS სახელი: საბოლოო ჭდე სრულად რიცხვითია." #: pattern.c:199 msgid "Invalid DNS name: \"local\" pseudo-TLD." msgstr "არასწორი DNS სახელი: ფსევდო-TLD \"local\"." #: pattern.c:204 #, c-format msgid "DNS name has invalid length (%zu)." msgstr "DNS სახელის არასწორი სიგრძე (%zu)." #: pattern.c:258 #, c-format msgid "Invalid DNS name pattern: Invalid character %c." msgstr "DNS სახელის არასწორი შაბლონი: არასწორი სიმბოლო %c." #: pattern.c:267 msgid "Invalid DNS name pattern: Empty label." msgstr "DNS სახელის არასწორი შაბლონი: ჭდე ცარიელია." #: pattern.c:272 msgid "Invalid DNS name pattern: Label starts with hyphen." msgstr "DNS სახელის არასწორი შაბლონი: ჭდე გამოტოვებით იწყება." #: pattern.c:285 msgid "Invalid DNS name pattern: Wildcard character used more than twice per label." msgstr "DNS სახელის არასწორი შაბლონი: შაბლონის სახელში ვაილდკარდი ერთზე მეტჯერ გამოიყენება." #: pattern.c:295 msgid "Invalid DNS name pattern: Label ends with hyphen." msgstr "DNS სახელის არასწორი შაბლონი: ჭდე გამოტოვებით მთავრდება." #: pattern.c:301 #, c-format msgid "Invalid DNS name pattern: Label is too long (%zu)." msgstr "DNS სახელის არასწორი შაბლონი: ჭდე ძალიან გრძელია (%zu)." #: pattern.c:309 #, c-format msgid "Invalid DNS name pattern: Not enough labels (%zu)." msgstr "DNS სახელის არასწორი შაბლონი: ჭდეების არასაკმარისი რაოდენობა (%zu)." #: pattern.c:314 msgid "Invalid DNS name pattern: Wildcard within final two labels." msgstr "DNS სახელის არასწორი შაბლონი: ვაილდკარდი ბოლო ორ ჭდეში." #: pattern.c:319 msgid "Invalid DNS name pattern: Final label is fully numeric." msgstr "DNS სახელის არასწორი შაბლონი: საბოლოო ჭდე სრულად რიცხვითია." #: pattern.c:329 msgid "Invalid DNS name pattern: \"local\" pseudo-TLD." msgstr "DNS სახელის არასწორი შაბლონი: ფსევდო-TLD \"local\"." #: pattern.c:334 #, c-format msgid "DNS name pattern has invalid length after removing wildcards (%zu)." msgstr "DNS სახელის შაბლონის არასწორი სიგრძე * და ?-ის მოცილების შემდეგ (%zu)" #: dnssec.c:206 msgid "system time considered valid, now checking DNSSEC signature timestamps." msgstr "სისტემური დრო ჩათვლილია, როგორც სწორი. ახლა DNSSEC ხელმოწერის დროის შტამპები შემოწმებული იქნება." #: dnssec.c:1018 #, c-format msgid "Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support" msgstr "" #: blockdata.c:55 #, c-format msgid "pool memory in use %zu, max %zu, allocated %zu" msgstr "პულისთვის გამოყოფილი მეხსიერება %zu, მაქს %zu, გამოყოფილი %zu" #: tables.c:61 #, c-format msgid "failed to access pf devices: %s" msgstr "pf მოწყობილობებთან წვდომის შეცდომა: %s" #: tables.c:74 #, c-format msgid "warning: no opened pf devices %s" msgstr "გაფრთხილება: ღია pf მოწყობილობის გარეშე: %s" #: tables.c:82 #, c-format msgid "error: cannot use table name %s" msgstr "შეცდომა: ცხრილის ამ სახელს ვერ გამოიყენებთ: %s" #: tables.c:90 #, c-format msgid "error: cannot strlcpy table name %s" msgstr "შეცდომა: ცხრილის სახელის strlcpy()-ის შეცდომა: %s" #: tables.c:101 #, c-format msgid "IPset: error: %s" msgstr "IPset: შეცდომა: %s" #: tables.c:108 msgid "info: table created" msgstr "ინფორმაცია: ცხრილი შეიქმნა" #: tables.c:133 #, c-format msgid "warning: DIOCR%sADDRS: %s" msgstr "გაფრთხილება: DIOCR%sADDRS: %s" #: tables.c:137 #, c-format msgid "%d addresses %s" msgstr "%d მისამართი %s" #: inotify.c:62 #, c-format msgid "cannot access path %s: %s" msgstr "ბილიკთან წვდომის შეცდომა %s: %s" #: inotify.c:95 #, c-format msgid "failed to create inotify: %s" msgstr "inotify-ის შექმნის შეცდომა: %s" #: inotify.c:111 #, c-format msgid "too many symlinks following %s" msgstr "მეტისმეტად ბევრი სიმბმული მისდევს %s-ს" #: inotify.c:127 #, c-format msgid "directory %s for resolv-file is missing, cannot poll" msgstr "საქაღალდე %s resolv-file-სთვის არ არსებობს.პოლინგი შეუძლებელია" #: inotify.c:131 inotify.c:200 #, c-format msgid "failed to create inotify for %s: %s" msgstr "%s -თვის inotify-ის შექმნის შეცდომა: %s" #: inotify.c:178 inotify.c:185 #, c-format msgid "bad dynamic directory %s: %s" msgstr "%s არასწორი დინამიკური საქაღალდეა: %s" #: inotify.c:186 msgid "not a directory" msgstr "არ წარმოადგეს საქაღალდეს" #: inotify.c:299 #, c-format msgid "inotify: %s removed" msgstr "" #: inotify.c:301 #, fuzzy, c-format msgid "inotify: %s new or modified" msgstr "inotify, ახალი ან შეცვლილი ფაილი %s" #: inotify.c:309 #, c-format msgid "inotify: flushed %u names read from %s" msgstr "" #: dump.c:68 #, c-format msgid "cannot create %s: %s" msgstr "%s-ის შექმნის შეცდომა: %s" #: dump.c:74 #, c-format msgid "bad header in %s" msgstr "არასწორი თავსართი %s-ში" #: dump.c:287 msgid "failed to write packet dump" msgstr "პაკეტების ჩაწერის შეცდომა" #: dump.c:289 #, c-format msgid "%u dumping packet %u mask 0x%04x" msgstr "%u პაკეტის ჩაწერა %u ნიღაბი 0x%04x" #: dump.c:291 #, c-format msgid "dumping packet %u mask 0x%04x" msgstr "იწერება პაკეტი %u ნიღბით 0x%04x" #: ubus.c:79 #, c-format msgid "UBus subscription callback: %s subscriber(s)" msgstr "Ubus-ის გამოწერის უკუგამოძახება: %s გამომწერი" #: ubus.c:99 #, c-format msgid "Cannot reconnect to UBus: %s" msgstr "UBus -თან თავიდან მიერთების შეცდომა: %s" #: ubus.c:135 msgid "Cannot set UBus listeners: no connection" msgstr "UBus-ის მომსმენების დაყენების შეცდომა: შეერთების გარეშე" #: ubus.c:155 msgid "Cannot poll UBus listeners: no connection" msgstr "Ubus-ის მომსმენებიდან პოლინგის შეცდომა: შეერთების გარეშე" #: ubus.c:168 msgid "Disconnecting from UBus" msgstr "UBus– დან გათიშვა" #: ubus.c:179 ubus.c:326 #, c-format msgid "UBus command failed: %d (%s)" msgstr "UBus -ის ბრძანების შეცდომა: %d (%s)" #: hash-questions.c:40 msgid "Failed to create SHA-256 hash object" msgstr "SHA-256 ჰეშის ობიექტის შექმნის შეცდომა" #: nftset.c:35 msgid "failed to create nftset context" msgstr "nftset-ის კონტექსტის შექნის შეცდომა" dnsmasq-2.91/po/no.po0000664000175000017500000017624214765043257012720 0ustar srksrk# Norwegian translations for dnsmasq package. # This file is put in the public domain. # Simon Kelley , 2006. # # Current translator: Jan Erik Askildt , 2006 # msgid "" msgstr "" "Project-Id-Version: dnsmasq 2.25\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-06-18 12:24+0100\n" "PO-Revision-Date: 2017-07-17 18:30+0100\n" "Last-Translator: Jan Erik Askildt \n" "Language-Team: Norwegian \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:652 msgid "Internal error in cache." msgstr "" #: cache.c:1179 #, fuzzy, c-format msgid "failed to load names from %s: %s" msgstr "feilet laste navn fra %s: %s" #: cache.c:1201 dhcp.c:943 #, c-format msgid "bad address at %s line %d" msgstr "drlig adresse ved %s linje %d" #: cache.c:1254 dhcp.c:959 #, c-format msgid "bad name at %s line %d" msgstr "drlig navn ved %s linje %d" #: cache.c:1265 #, fuzzy, c-format msgid "read %s - %d names" msgstr "les %s - %d adresser" #: cache.c:1381 msgid "cleared cache" msgstr "mellomlager tmt" #: cache.c:1445 #, c-format msgid "No IPv4 address found for %s" msgstr "" #: cache.c:1491 #, c-format msgid "%s is a CNAME, not giving it to the DHCP lease of %s" msgstr "" #: cache.c:1515 #, 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:1760 #, c-format msgid "time %lu" msgstr "" #: cache.c:1761 #, fuzzy, c-format msgid "cache size %d, %d/%d cache insertions re-used unexpired cache entries." msgstr "mellomlager strrelse %d, %d/%d mellomlager innsettinger re-bruker mellomlager plasser som ikke er utlpt" #: cache.c:1763 #, c-format msgid "queries forwarded %u, queries answered locally %u" msgstr "" #: cache.c:1766 #, c-format msgid "queries answered from stale cache %u" msgstr "" #: cache.c:1768 #, c-format msgid "queries for authoritative zones %u" msgstr "" #: cache.c:1796 #, c-format msgid "server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums" msgstr "" #: util.c:51 #, fuzzy, c-format msgid "failed to seed the random number generator: %s" msgstr "feilet lytte p socket: %s" #: util.c:246 #, fuzzy msgid "failed to allocate memory" msgstr "feilet laste %d bytes" #: util.c:305 option.c:696 msgid "could not get memory" msgstr "kunne ikke f minne" #: util.c:326 #, fuzzy, c-format msgid "cannot create pipe: %s" msgstr "kan ikke lese %s: %s" #: util.c:334 #, fuzzy, c-format msgid "failed to allocate %d bytes" msgstr "feilet laste %d bytes" #: util.c:344 #, fuzzy, c-format msgid "failed to reallocate %d bytes" msgstr "feilet laste %d bytes" #: util.c:465 #, fuzzy, c-format msgid "cannot read monotonic clock: %s" msgstr "kan ikke binde netlink socket: %s" #: util.c:579 #, c-format msgid "infinite" msgstr "uendelig" #: util.c:867 #, fuzzy, c-format msgid "failed to find kernel version: %s" msgstr "feilet binde DHCP tjener socket: %s" #: option.c:393 msgid "Specify local address(es) to listen on." msgstr "Spesifiser lokal(e) adresse(r) lytte p." #: option.c:394 msgid "Return ipaddr for all hosts in specified domains." msgstr "Returner ipaddr for alle verter i det spesifiserte domenet." #: option.c:395 msgid "Fake reverse lookups for RFC1918 private address ranges." msgstr "Forfalsk revers oppslag for RFC1918 private adresse omrder." #: option.c:396 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)." msgstr "Behandle ipaddr som NXDOMAIN (omgr Verisign wildcard)." #: option.c:397 #, c-format msgid "Specify the size of the cache in entries (defaults to %s)." msgstr "Spesifiser strrelsen p mellomlager plassene (standard er %s)." #: option.c:398 #, c-format msgid "Specify configuration file (defaults to %s)." msgstr "Spesifiser konfigurasjonsfil (standard er %s)." #: option.c:399 msgid "Do NOT fork into the background: run in debug mode." msgstr "IKKE legg (fork) som bakgrunnsprosess: kjr i debug modus." #: option.c:400 msgid "Do NOT forward queries with no domain part." msgstr "IKKE videresend oppslag som mangler domene del." #: option.c:401 msgid "Return self-pointing MX records for local hosts." msgstr "Returner selv-pekende MX post for lokale verter." #: option.c:402 msgid "Expand simple names in /etc/hosts with domain-suffix." msgstr "Utvid enkle navn i /etc/hosts med domene-suffiks." #: option.c:403 msgid "Don't forward spurious DNS requests from Windows hosts." msgstr "Ikke videresend falske/uekte DNS foresprsler fra Windows verter." #: option.c:404 msgid "Don't include IPv4 addresses in DNS answers." msgstr "" #: option.c:405 msgid "Don't include IPv6 addresses in DNS answers." msgstr "" #: option.c:406 msgid "Enable DHCP in the range given with lease duration." msgstr "Aktiver DHCP i det gitte omrdet med leie varighet" #: option.c:407 #, c-format msgid "Change to this group after startup (defaults to %s)." msgstr "Skift til denne gruppen etter oppstart (standard er %s)." #: option.c:408 msgid "Set address or hostname for a specified machine." msgstr "Sett adresse eller vertsnavn for en spesifikk maskin." #: option.c:409 #, fuzzy msgid "Read DHCP host specs from file." msgstr "drlig MX navn" #: option.c:410 msgid "Read DHCP option specs from file." msgstr "" #: option.c:411 #, fuzzy msgid "Read DHCP host specs from a directory." msgstr "drlig MX navn" #: option.c:412 #, fuzzy msgid "Read DHCP options from a directory." msgstr "drlig MX navn" #: option.c:413 msgid "Evaluate conditional tag expression." msgstr "" #: option.c:414 #, c-format msgid "Do NOT load %s file." msgstr "IKKE last %s filen." #: option.c:415 #, 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:416 #, fuzzy msgid "Read hosts files from a directory." msgstr "drlig MX navn" #: option.c:417 msgid "Specify interface(s) to listen on." msgstr "Spesifiser nettverkskort det skal lyttes p." #: option.c:418 msgid "Specify interface(s) NOT to listen on." msgstr "Spesifiser nettverkskort det IKKE skal lyttes p." #: option.c:419 #, fuzzy msgid "Map DHCP user class to tag." msgstr "Map DHCP bruker klasse til opsjon sett." #: option.c:420 msgid "Map RFC3046 circuit-id to tag." msgstr "" #: option.c:421 msgid "Map RFC3046 remote-id to tag." msgstr "" #: option.c:422 msgid "Map RFC3993 subscriber-id to tag." msgstr "" #: option.c:423 msgid "Specify vendor class to match for PXE requests." msgstr "" #: option.c:424 #, fuzzy msgid "Don't do DHCP for hosts with tag set." msgstr "Ikke utfr DHCP for klienter i opsjon sett." #: option.c:425 #, fuzzy msgid "Force broadcast replies for hosts with tag set." msgstr "Ikke utfr DHCP for klienter i opsjon sett." #: option.c:426 msgid "Do NOT fork into the background, do NOT run in debug mode." msgstr "IKKE last (fork) som bakgrunnsprosess, IKKE kjr i debug modus." #: option.c:427 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:428 #, c-format msgid "Specify where to store DHCP leases (defaults to %s)." msgstr "Spesifiser hvor DHCP leiene skal lagres (standard er %s)." #: option.c:429 msgid "Return MX records for local hosts." msgstr "Returner MX records for lokale verter." #: option.c:430 msgid "Specify an MX record." msgstr "Spesifiser en MX post." #: option.c:431 msgid "Specify BOOTP options to DHCP server." msgstr "Spesifiser BOOTP opsjoner til DHCP tjener." #: option.c:432 #, c-format msgid "Do NOT poll %s file, reload only on SIGHUP." msgstr "IKKE spr (poll) %s fil, les p nytt kun ved SIGHUP" #: option.c:433 msgid "Do NOT cache failed search results." msgstr "IKKE mellomlagre skeresultater som feiler." #: option.c:434 msgid "Use expired cache data for faster reply." msgstr "" #: option.c:435 #, c-format msgid "Use nameservers strictly in the order given in %s." msgstr "Bruk navnetjenere kun som bestemt i rekkeflgen gitt i %s." #: option.c:436 #, fuzzy msgid "Specify options to be sent to DHCP clients." msgstr "Sett ekstra opsjoner som skal fordeles til DHCP klientene." #: option.c:437 msgid "DHCP option sent even if the client does not request it." msgstr "" #: option.c:438 msgid "Specify port to listen for DNS requests on (defaults to 53)." msgstr "Spesifiser lytteport for DNS oppslag (standard er 53)." #: option.c:439 #, c-format msgid "Maximum supported UDP packet size for EDNS.0 (defaults to %s)." msgstr "Maksimal stttet UDP pakkestrrelse for EDNS.0 (standard er %s)." #: option.c:440 #, fuzzy msgid "Log DNS queries." msgstr "Logg oppslag." #: option.c:441 #, fuzzy msgid "Force the originating port for upstream DNS queries." msgstr "Tving bruk av opprinnelig port for oppstrms oppslag." #: option.c:442 msgid "Set maximum number of random originating ports for a query." msgstr "" #: option.c:443 msgid "Do NOT read resolv.conf." msgstr "IKKE les resolv.conf." #: option.c:444 #, c-format msgid "Specify path to resolv.conf (defaults to %s)." msgstr "Spesifiser stien til resolv.conf (standard er %s)." #: option.c:445 #, fuzzy msgid "Specify path to file with server= options" msgstr "Spesifiser stien til PID fil. (standard er %s)." #: option.c:446 msgid "Specify address(es) of upstream servers with optional domains." msgstr "Spesifiser adressen(e) til oppstrms tjenere med valgfrie domener." #: option.c:447 #, fuzzy msgid "Specify address of upstream servers for reverse address queries" msgstr "Spesifiser adressen(e) til oppstrms tjenere med valgfrie domener." #: option.c:448 msgid "Never forward queries to specified domains." msgstr "Aldri videresend oppslag til spesifiserte domener." #: option.c:449 msgid "Specify the domain to be assigned in DHCP leases." msgstr "Spesifiser domenet som skal tildeles i DHCP leien." #: option.c:450 msgid "Specify default target in an MX record." msgstr "Spesifiser default ml i en MX post." #: option.c:451 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:452 #, 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:453 #, 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:454 #, fuzzy msgid "Specify time-to-live ceiling for cache." msgstr "Spesifiser time-to-live i sekunder for svar fra /etc/hosts." #: option.c:455 #, fuzzy msgid "Specify time-to-live floor for cache." msgstr "Spesifiser time-to-live i sekunder for svar fra /etc/hosts." #: option.c:456 msgid "Retry DNS queries after this many milliseconds." msgstr "" #: option.c:457 #, c-format msgid "Change to this user after startup. (defaults to %s)." msgstr "Skift til denne bruker etter oppstart (standard er %s)." #: option.c:458 #, fuzzy msgid "Map DHCP vendor class to tag." msgstr "Map DHCP produsent klasse til opsjon sett." #: option.c:459 msgid "Display dnsmasq version and copyright information." msgstr "Vis dnsmasq versjon og copyright informasjon." #: option.c:460 msgid "Translate IPv4 addresses from upstream servers." msgstr "Oversett IPv4 adresser fra oppstrms tjenere." #: option.c:461 msgid "Specify a SRV record." msgstr "Spesifiser en SRV post." #: option.c:462 msgid "Display this message. Use --help dhcp or --help dhcp6 for known DHCP options." msgstr "" #: option.c:463 #, fuzzy, c-format msgid "Specify path of PID file (defaults to %s)." msgstr "Spesifiser stien til PID fil. (standard er %s)." #: option.c:464 #, c-format msgid "Specify maximum number of DHCP leases (defaults to %s)." msgstr "Spesifiser maksimum antall DHCP leier (standard er %s)" #: option.c:465 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:466 msgid "Specify TXT DNS record." msgstr "Spesifiser TXT DNS post." #: option.c:467 #, fuzzy msgid "Specify PTR DNS record." msgstr "Spesifiser TXT DNS post." #: option.c:468 msgid "Give DNS name to IPv4 address of interface." msgstr "" #: option.c:469 msgid "Bind only to interfaces in use." msgstr "Bind kun til nettverkskort som er i bruk." #: option.c:470 #, c-format msgid "Read DHCP static host information from %s." msgstr "Les DHCP statisk vert informasjon fra %s." #: option.c:471 msgid "Enable the DBus interface for setting upstream servers, etc." msgstr "Aktiver DBus interface for sette oppstrms tjenere, osv." #: option.c:472 msgid "Enable the UBus interface." msgstr "" #: option.c:473 msgid "Do not provide DHCP on this interface, only provide DNS." msgstr "Ikke lever DHCP p dette nettverkskortet, kun lever DNS." #: option.c:474 msgid "Enable dynamic address allocation for bootp." msgstr "Aktiver dynamisk adresse allokering for bootp." #: option.c:475 #, fuzzy msgid "Map MAC address (with wildcards) to option set." msgstr "Map DHCP produsent klasse til opsjon sett." #: option.c:476 msgid "Treat DHCP requests on aliases as arriving from interface." msgstr "" #: option.c:477 msgid "Specify extra networks sharing a broadcast domain for DHCP" msgstr "" #: option.c:478 msgid "Disable ICMP echo address checking in the DHCP server." msgstr "" #: option.c:479 msgid "Shell script to run on DHCP lease creation and destruction." msgstr "" #: option.c:480 msgid "Lua script to run on DHCP lease creation and destruction." msgstr "" #: option.c:481 msgid "Run lease-change scripts as this user." msgstr "" #: option.c:482 msgid "Call dhcp-script with changes to local ARP table." msgstr "" #: option.c:483 msgid "Read configuration from all the files in this directory." msgstr "" #: option.c:484 msgid "Execute file and read configuration from stdin." msgstr "" #: option.c:485 #, fuzzy msgid "Log to this syslog facility or file. (defaults to DAEMON)" msgstr "Skift til denne bruker etter oppstart (standard er %s)." #: option.c:486 msgid "Do not use leasefile." msgstr "" #: option.c:487 #, fuzzy, c-format msgid "Maximum number of concurrent DNS queries. (defaults to %s)" msgstr "Spesifiser maksimum antall DHCP leier (standard er %s)" #: option.c:488 #, c-format msgid "Clear DNS cache when reloading %s." msgstr "" #: option.c:489 msgid "Ignore hostnames provided by DHCP clients." msgstr "" #: option.c:490 msgid "Do NOT reuse filename and server fields for extra DHCP options." msgstr "" #: option.c:491 msgid "Enable integrated read-only TFTP server." msgstr "" #: option.c:492 msgid "Export files by TFTP only from the specified subtree." msgstr "" #: option.c:493 msgid "Add client IP or hardware address to tftp-root." msgstr "" #: option.c:494 msgid "Allow access only to files owned by the user running dnsmasq." msgstr "" #: option.c:495 msgid "Do not terminate the service if TFTP directories are inaccessible." msgstr "" #: option.c:496 #, fuzzy, c-format msgid "Maximum number of concurrent TFTP transfers (defaults to %s)." msgstr "Spesifiser maksimum antall DHCP leier (standard er %s)" #: option.c:497 #, fuzzy msgid "Maximum MTU to use for TFTP transfers." msgstr "Spesifiser maksimum antall DHCP leier (standard er %s)" #: option.c:498 msgid "Disable the TFTP blocksize extension." msgstr "" #: option.c:499 msgid "Convert TFTP filenames to lowercase" msgstr "" #: option.c:500 msgid "Ephemeral port range for use by TFTP transfers." msgstr "" #: option.c:501 msgid "Use only one port for TFTP server." msgstr "" #: option.c:502 msgid "Extra logging for DHCP." msgstr "" #: option.c:503 msgid "Enable async. logging; optionally set queue length." msgstr "" #: option.c:504 msgid "Stop DNS rebinding. Filter private IP ranges when resolving." msgstr "" #: option.c:505 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers." msgstr "" #: option.c:506 msgid "Inhibit DNS-rebind protection on this domain." msgstr "" #: option.c:507 msgid "Always perform DNS queries to all servers." msgstr "" #: option.c:508 msgid "Set tag if client includes matching option in request." msgstr "" #: option.c:509 msgid "Set tag if client provides given name." msgstr "" #: option.c:510 msgid "Use alternative ports for DHCP." msgstr "" #: option.c:511 #, fuzzy msgid "Specify NAPTR DNS record." msgstr "Spesifiser TXT DNS post." #: option.c:512 msgid "Specify lowest port available for DNS query transmission." msgstr "" #: option.c:513 msgid "Specify highest port available for DNS query transmission." msgstr "" #: option.c:514 msgid "Use only fully qualified domain names for DHCP clients." msgstr "" #: option.c:515 msgid "Generate hostnames based on MAC address for nameless clients." msgstr "" #: option.c:516 msgid "Use these DHCP relays as full proxies." msgstr "" #: option.c:517 msgid "Relay DHCP requests to a remote server" msgstr "" #: option.c:518 msgid "Specify alias name for LOCAL DNS name." msgstr "" #: option.c:519 #, fuzzy msgid "Prompt to send to PXE clients." msgstr "Sett ekstra opsjoner som skal fordeles til DHCP klientene." #: option.c:520 msgid "Boot service for PXE menu." msgstr "" #: option.c:521 msgid "Check configuration syntax." msgstr "" #: option.c:522 msgid "Add requestor's MAC address to forwarded DNS queries." msgstr "" #: option.c:523 msgid "Strip MAC information from queries." msgstr "" #: option.c:524 msgid "Add specified IP subnet to forwarded DNS queries." msgstr "" #: option.c:525 msgid "Strip ECS information from queries." msgstr "" #: option.c:526 #, fuzzy msgid "Add client identification to forwarded DNS queries." msgstr "Tving bruk av opprinnelig port for oppstrms oppslag." #: option.c:527 #, fuzzy msgid "Proxy DNSSEC validation results from upstream nameservers." msgstr "Oversett IPv4 adresser fra oppstrms tjenere." #: option.c:528 msgid "Attempt to allocate sequential IP addresses to DHCP clients." msgstr "" #: option.c:529 #, fuzzy msgid "Ignore client identifier option sent by DHCP clients." msgstr "Sett ekstra opsjoner som skal fordeles til DHCP klientene." #: option.c:530 msgid "Copy connection-track mark from queries to upstream connections." msgstr "" #: option.c:531 msgid "Allow DHCP clients to do their own DDNS updates." msgstr "" #: option.c:532 msgid "Send router-advertisements for interfaces doing DHCPv6" msgstr "" #: option.c:533 msgid "Specify DUID_EN-type DHCPv6 server DUID" msgstr "" #: option.c:534 #, fuzzy msgid "Specify host (A/AAAA and PTR) records" msgstr "Spesifiser en MX post." #: option.c:535 msgid "Specify host record in interface subnet" msgstr "" #: option.c:536 msgid "Specify certification authority authorization record" msgstr "" #: option.c:537 #, fuzzy msgid "Specify arbitrary DNS resource record" msgstr "Spesifiser TXT DNS post." #: option.c:538 #, fuzzy msgid "Bind to interfaces in use - check for new interfaces" msgstr "ukjent tilknytning (interface) %s" #: option.c:539 msgid "Export local names to global DNS" msgstr "" #: option.c:540 msgid "Domain to export to global DNS" msgstr "" #: option.c:541 msgid "Set TTL for authoritative replies" msgstr "" #: option.c:542 msgid "Set authoritative zone information" msgstr "" #: option.c:543 msgid "Secondary authoritative nameservers for forward domains" msgstr "" #: option.c:544 msgid "Peers which are allowed to do zone transfer" msgstr "" #: option.c:545 msgid "Specify ipsets to which matching domains should be added" msgstr "" #: option.c:546 msgid "Specify nftables sets to which matching domains should be added" msgstr "" #: option.c:547 msgid "Enable filtering of DNS queries with connection-track marks." msgstr "" #: option.c:548 msgid "Set allowed DNS patterns for a connection-track mark." msgstr "" #: option.c:549 msgid "Specify a domain and address range for synthesised names" msgstr "" #: option.c:550 msgid "Activate DNSSEC validation" msgstr "" #: option.c:551 msgid "Specify trust anchor key digest." msgstr "" #: option.c:552 msgid "Disable upstream checking for DNSSEC debugging." msgstr "" #: option.c:553 msgid "Ensure answers without DNSSEC are in unsigned zones." msgstr "" #: option.c:554 msgid "Don't check DNSSEC signature timestamps until first cache-reload" msgstr "" #: option.c:555 msgid "Timestamp file to verify system clock for DNSSEC" msgstr "" #: option.c:556 msgid "Set MTU, priority, resend-interval and router-lifetime" msgstr "" #: option.c:557 msgid "Do not log routine DHCP." msgstr "" #: option.c:558 msgid "Do not log routine DHCPv6." msgstr "" #: option.c:559 msgid "Do not log RA." msgstr "" #: option.c:560 msgid "Log debugging information." msgstr "" #: option.c:561 msgid "Accept queries only from directly-connected networks." msgstr "" #: option.c:562 msgid "Detect and remove DNS forwarding loops." msgstr "" #: option.c:563 msgid "Ignore DNS responses containing ipaddr." msgstr "" #: option.c:564 msgid "Set TTL in DNS responses with DHCP-derived addresses." msgstr "" #: option.c:565 msgid "Delay DHCP replies for at least number of seconds." msgstr "" #: option.c:566 msgid "Enables DHCPv4 Rapid Commit option." msgstr "" #: option.c:567 msgid "Path to debug packet dump file" msgstr "" #: option.c:568 msgid "Mask which packets to dump" msgstr "" #: option.c:569 msgid "Call dhcp-script when lease expiry changes." msgstr "" #: option.c:570 msgid "Send Cisco Umbrella identifiers including remote IP." msgstr "" #: option.c:571 msgid "Do not log routine TFTP." msgstr "" #: option.c:572 msgid "Suppress round-robin ordering of DNS records." msgstr "" #: option.c:802 #, c-format msgid "" "Usage: dnsmasq [options]\n" "\n" msgstr "" "Bruk: dnsmasq [opsjoner]\n" "\n" #: option.c:804 #, c-format msgid "Use short options only on the command line.\n" msgstr "Bruk korte opsjoner kun p kommandolinjen.\n" #: option.c:806 #, fuzzy, c-format msgid "Valid options are:\n" msgstr "Gyldige opsjoner er :\n" #: option.c:853 option.c:1055 #, fuzzy msgid "bad address" msgstr "les %s - %d adresser" #: option.c:882 option.c:886 msgid "bad port" msgstr "drlig port" #: option.c:899 option.c:1002 option.c:1048 msgid "interface binding not supported" msgstr "" #: option.c:955 msgid "Cannot resolve server name" msgstr "" #: option.c:991 msgid "cannot use IPv4 server address with IPv6 source address" msgstr "" #: option.c:997 option.c:1043 msgid "interface can only be specified once" msgstr "" #: option.c:1011 option.c:4785 #, fuzzy msgid "bad interface name" msgstr "drlig MX navn" #: option.c:1037 msgid "cannot use IPv6 server address with IPv4 source address" msgstr "" #: option.c:1124 #, fuzzy msgid "bad IPv4 prefix length" msgstr "drlig port" #: option.c:1155 option.c:1165 option.c:1240 option.c:1250 option.c:5360 msgid "error" msgstr "feil" #: option.c:1207 #, fuzzy msgid "bad IPv6 prefix length" msgstr "drlig port" #: option.c:1467 msgid "inappropriate vendor:" msgstr "" #: option.c:1474 msgid "inappropriate encap:" msgstr "" #: option.c:1500 msgid "unsupported encapsulation for IPv6 option" msgstr "" #: option.c:1514 msgid "bad dhcp-option" msgstr "drlig dhcp-opsjon" #: option.c:1592 #, fuzzy msgid "bad IP address" msgstr "les %s - %d adresser" #: option.c:1595 option.c:1734 option.c:3928 #, fuzzy msgid "bad IPv6 address" msgstr "les %s - %d adresser" #: option.c:1688 #, fuzzy msgid "bad IPv4 address" msgstr "les %s - %d adresser" #: option.c:1761 option.c:1856 msgid "bad domain in dhcp-option" msgstr "drlig domene i dhcp-opsjon" #: option.c:1900 msgid "dhcp-option too long" msgstr "dhcp-opsjon for lang" #: option.c:1907 msgid "illegal dhcp-match" msgstr "" #: option.c:1966 msgid "illegal repeated flag" msgstr "" #: option.c:1974 msgid "illegal repeated keyword" msgstr "" #: option.c:2056 option.c:5533 #, fuzzy, c-format msgid "cannot access directory %s: %s" msgstr "kan ikke lese %s: %s" #: option.c:2102 tftp.c:573 dump.c:72 #, fuzzy, c-format msgid "cannot access %s: %s" msgstr "kan ikke lese %s: %s" #: option.c:2219 msgid "setting log facility is not possible under Android" msgstr "" #: option.c:2228 msgid "bad log facility" msgstr "" #: option.c:2281 msgid "bad MX preference" msgstr "drlig MX preferanse" #: option.c:2289 msgid "bad MX name" msgstr "drlig MX navn" #: option.c:2304 msgid "bad MX target" msgstr "drlig MX ml" #: option.c:2324 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts" msgstr "" #: option.c:2328 msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts" msgstr "" #: option.c:2447 #, fuzzy msgid "invalid auth-zone" msgstr "ugyldig vekt" #: option.c:2589 option.c:2621 #, fuzzy msgid "bad prefix length" msgstr "drlig port" #: option.c:2601 option.c:2642 option.c:2696 #, fuzzy msgid "bad prefix" msgstr "drlig port" #: option.c:2716 msgid "prefix length too small" msgstr "" #: option.c:3010 #, fuzzy msgid "Bad address in --address" msgstr "adresse i bruk" #: option.c:3110 msgid "recompile with HAVE_IPSET defined to enable ipset directives" msgstr "" #: option.c:3117 msgid "recompile with HAVE_NFTSET defined to enable nftset directives" msgstr "" #: option.c:3192 option.c:3210 msgid "recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives" msgstr "" #: option.c:3496 #, fuzzy msgid "bad port range" msgstr "drlig port" #: option.c:3522 msgid "bad bridge-interface" msgstr "" #: option.c:3566 msgid "bad shared-network" msgstr "" #: option.c:3620 msgid "only one tag allowed" msgstr "" #: option.c:3641 option.c:3657 option.c:3783 option.c:3791 option.c:3834 msgid "bad dhcp-range" msgstr "drlig dhcp-omrde" #: option.c:3675 msgid "inconsistent DHCP range" msgstr "ikke konsistent DHCP omrde" #: option.c:3741 msgid "prefix length must be exactly 64 for RA subnets" msgstr "" #: option.c:3743 msgid "prefix length must be exactly 64 for subnet constructors" msgstr "" #: option.c:3746 msgid "prefix length must be at least 64" msgstr "" #: option.c:3749 #, fuzzy msgid "inconsistent DHCPv6 range" msgstr "ikke konsistent DHCP omrde" #: option.c:3768 msgid "prefix must be zero with \"constructor:\" argument" msgstr "" #: option.c:3893 option.c:3971 #, fuzzy msgid "bad hex constant" msgstr "drlig dhcp-vert" #: option.c:3946 #, fuzzy msgid "bad IPv6 prefix" msgstr "drlig port" #: option.c:3994 #, fuzzy, c-format msgid "duplicate dhcp-host IP address %s" msgstr "dubliserte IP adresser i %s dhcp-config direktiv." #: option.c:4056 #, fuzzy msgid "bad DHCP host name" msgstr "drlig MX navn" #: option.c:4142 #, fuzzy msgid "bad tag-if" msgstr "drlig MX ml" #: option.c:4490 option.c:5046 msgid "invalid port number" msgstr "ugyldig portnummer" #: option.c:4546 #, fuzzy msgid "bad dhcp-proxy address" msgstr "les %s - %d adresser" #: option.c:4627 #, fuzzy msgid "Bad dhcp-relay" msgstr "drlig dhcp-omrde" #: option.c:4671 msgid "bad RA-params" msgstr "" #: option.c:4681 msgid "bad DUID" msgstr "" #: option.c:4715 #, fuzzy msgid "missing address in alias" msgstr "adresse i bruk" #: option.c:4721 #, fuzzy msgid "invalid alias range" msgstr "ugyldig vekt" #: option.c:4770 #, fuzzy msgid "missing address in dynamic host" msgstr "adresse i bruk" #: option.c:4785 #, fuzzy msgid "bad dynamic host" msgstr "kan ikke lese %s: %s" #: option.c:4803 option.c:4819 msgid "bad CNAME" msgstr "" #: option.c:4827 msgid "duplicate CNAME" msgstr "" #: option.c:4854 #, fuzzy msgid "bad PTR record" msgstr "drlig SRV post" #: option.c:4889 #, fuzzy msgid "bad NAPTR record" msgstr "drlig SRV post" #: option.c:4925 #, fuzzy msgid "bad RR record" msgstr "drlig SRV post" #: option.c:4958 #, fuzzy msgid "bad CAA record" msgstr "drlig SRV post" #: option.c:4987 msgid "bad TXT record" msgstr "drlig TXT post" #: option.c:5030 msgid "bad SRV record" msgstr "drlig SRV post" #: option.c:5037 msgid "bad SRV target" msgstr "drlig SRV ml" #: option.c:5056 msgid "invalid priority" msgstr "ugyldig prioritet" #: option.c:5061 msgid "invalid weight" msgstr "ugyldig vekt" #: option.c:5084 #, fuzzy msgid "Bad host-record" msgstr "drlig SRV post" #: option.c:5123 #, fuzzy msgid "Bad name in host-record" msgstr "drlig navn i %s" #: option.c:5165 msgid "bad value for dnssec-check-unsigned" msgstr "" #: option.c:5201 #, fuzzy msgid "bad trust anchor" msgstr "drlig port" #: option.c:5217 msgid "bad HEX in trust anchor" msgstr "" #: option.c:5228 msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)" msgstr "" #: option.c:5290 msgid "missing \"" msgstr "mangler \"" #: option.c:5347 msgid "bad option" msgstr "drlig opsjon" #: option.c:5349 msgid "extraneous parameter" msgstr "overfldig parameter" #: option.c:5351 msgid "missing parameter" msgstr "mangler parameter" #: option.c:5353 #, fuzzy msgid "illegal option" msgstr "drlig opsjon" #: option.c:5363 #, c-format msgid " in output from %s" msgstr "" #: option.c:5365 #, fuzzy, c-format msgid " at line %d of %s" msgstr "%s p linje %d av %%s" #: option.c:5380 option.c:5683 option.c:5694 #, fuzzy, c-format msgid "read %s" msgstr "leser %s" #: option.c:5446 #, fuzzy, c-format msgid "cannot execute %s: %s" msgstr "kan ikke lese %s: %s" #: option.c:5454 option.c:5615 tftp.c:790 #, c-format msgid "cannot read %s: %s" msgstr "kan ikke lese %s: %s" #: option.c:5473 #, fuzzy, c-format msgid "error executing %s: %s" msgstr "feilet f tilgang til %s: %s" #: option.c:5476 #, c-format msgid "%s returns non-zero error code" msgstr "" #: option.c:5775 msgid "junk found in command line" msgstr "" #: option.c:5815 #, c-format msgid "Dnsmasq version %s %s\n" msgstr "Dnsmasq versjon %s %s\n" #: option.c:5816 #, fuzzy, c-format msgid "" "Compile time options: %s\n" "\n" msgstr "" "Kompileringsopsjoner %s\n" "\n" #: option.c:5817 #, c-format msgid "This software comes with ABSOLUTELY NO WARRANTY.\n" msgstr "Denne programvaren kommer med ABSOLUTT INGEN GARANTI.\n" #: option.c:5818 #, 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:5819 #, fuzzy, c-format msgid "under the terms of the GNU General Public License, version 2 or 3.\n" msgstr "under vilkrene gitt i GNU General Public License, versjon 2.\n" #: option.c:5836 msgid "try --help" msgstr "" #: option.c:5838 msgid "try -w" msgstr "" #: option.c:5840 #, fuzzy, c-format msgid "bad command line options: %s" msgstr "drlige kommandlinje opsjoner: %s." #: option.c:5909 #, c-format msgid "CNAME loop involving %s" msgstr "" #: option.c:5950 #, c-format msgid "cannot get host-name: %s" msgstr "klarer ikke f vertsnavn: %s" #: option.c:5978 msgid "only one resolv.conf file allowed in no-poll mode." msgstr "kun en resolv.conf fil tillat i no-poll modus." #: option.c:5988 msgid "must have exactly one resolv.conf to read domain from." msgstr "m ha nyaktig en resolv.conf lese domene fra." #: option.c:5991 network.c:1727 dhcp.c:892 #, fuzzy, c-format msgid "failed to read %s: %s" msgstr "feilet lese %s: %s" #: option.c:6008 #, c-format msgid "no search directive found in %s" msgstr "intet ske direktiv funnet i %s" #: option.c:6029 msgid "there must be a default domain when --dhcp-fqdn is set" msgstr "" #: option.c:6038 msgid "syntax check OK" msgstr "" #: forward.c:107 #, fuzzy, c-format msgid "failed to send packet: %s" msgstr "feilet lytte p socket: %s" #: forward.c:715 msgid "discarding DNS reply: subnet option mismatch" msgstr "" #: forward.c:780 #, c-format msgid "nameserver %s refused to do a recursive query" msgstr "navnetjener %s nektet gjre et rekursivt oppslag" #: forward.c:826 #, c-format msgid "possible DNS-rebind attack detected: %s" msgstr "" #: forward.c:1239 #, c-format msgid "reducing DNS packet size for nameserver %s to %d" msgstr "" #: forward.c:1565 #, c-format msgid "ignoring query from non-local network %s (logged only once)" msgstr "" #: forward.c:2139 #, c-format msgid "ignoring query from non-local network %s" msgstr "" #: forward.c:2501 #, fuzzy, c-format msgid "failed to bind server socket to %s: %s" msgstr "feilet binde lytte socket for %s: %s" #: forward.c:2867 #, fuzzy, c-format msgid "Maximum number of concurrent DNS queries reached (max: %d)" msgstr "Spesifiser maksimum antall DHCP leier (standard er %s)" #: forward.c:2869 #, fuzzy, c-format msgid "Maximum number of concurrent DNS queries to %s reached (max: %d)" msgstr "Spesifiser maksimum antall DHCP leier (standard er %s)" #: network.c:700 #, c-format msgid "stopped listening on %s(#%d): %s port %d" msgstr "" #: network.c:911 #, fuzzy, c-format msgid "failed to create listening socket for %s: %s" msgstr "feilet lage lytte socket: %s" #: network.c:1192 #, c-format msgid "listening on %s(#%d): %s port %d" msgstr "" #: network.c:1219 #, fuzzy, c-format msgid "listening on %s port %d" msgstr "feilet lese %s: %s" #: network.c:1252 #, c-format msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s" msgstr "" #: network.c:1259 msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)" msgstr "" #: network.c:1268 #, fuzzy, c-format msgid "warning: using interface %s instead" msgstr "advarsel: nettverkskort %s eksisterer ikke for tiden" #: network.c:1277 #, fuzzy, c-format msgid "warning: no addresses found for interface %s" msgstr "benytter lokale adresser kun for %s %s" #: network.c:1335 #, fuzzy, c-format msgid "interface %s failed to join DHCPv6 multicast group: %s" msgstr "feilet binde DHCP tjener socket: %s" #: network.c:1340 msgid "try increasing /proc/sys/net/core/optmem_max" msgstr "" #: network.c:1545 #, fuzzy, c-format msgid "failed to bind server socket for %s: %s" msgstr "feilet binde lytte socket for %s: %s" #: network.c:1622 #, c-format msgid "ignoring nameserver %s - local interface" msgstr "ignorerer navnetjener %s - lokal tilknytning" #: network.c:1633 #, fuzzy, c-format msgid "ignoring nameserver %s - cannot make/bind socket: %s" msgstr "ignorerer navnetjener %s - kan ikke lage/dinde socket: %s" #: network.c:1643 msgid "more servers are defined but not logged" msgstr "" #: network.c:1654 msgid "(no DNSSEC)" msgstr "" #: network.c:1657 msgid "unqualified" msgstr "ikke kvalifisert" #: network.c:1657 msgid "names" msgstr "" #: network.c:1659 msgid "default" msgstr "" #: network.c:1661 msgid "domain" msgstr "domene" #: network.c:1663 #, fuzzy, c-format msgid "using nameserver %s#%d for %s %s%s %s" msgstr "benytter navnetjener %s#%d for %s %s" #: network.c:1667 #, fuzzy, c-format msgid "NOT using nameserver %s#%d - query loop detected" msgstr "benytter navnetjener %s#%d for %s %s" #: network.c:1670 #, fuzzy, c-format msgid "using nameserver %s#%d(via %s)" msgstr "benytter navnetjener %s#%d" #: network.c:1672 #, c-format msgid "using nameserver %s#%d" msgstr "benytter navnetjener %s#%d" #: network.c:1687 #, fuzzy, c-format msgid "using only locally-known addresses for %s" msgstr "benytter lokale adresser kun for %s %s" #: network.c:1690 #, fuzzy, c-format msgid "using standard nameservers for %s" msgstr "benytter navnetjener %s#%d for %s %s" #: network.c:1694 #, fuzzy, c-format msgid "using %d more local addresses" msgstr "benytter navnetjener %s#%d" #: network.c:1696 #, fuzzy, c-format msgid "using %d more nameservers" msgstr "benytter navnetjener %s#%d" #: dnsmasq.c:192 msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform" msgstr "" #: dnsmasq.c:207 msgid "no root trust anchor provided for DNSSEC" msgstr "" #: dnsmasq.c:210 msgid "cannot reduce cache size from default when DNSSEC enabled" msgstr "" #: dnsmasq.c:212 #, 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:218 #, 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:225 msgid "cannot use --conntrack AND --query-port" msgstr "" #: dnsmasq.c:231 #, 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:236 msgid "asynchronous logging is not available under Solaris" msgstr "" #: dnsmasq.c:241 msgid "asynchronous logging is not available under Android" msgstr "" #: dnsmasq.c:246 #, 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:251 #, 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:256 #, fuzzy msgid "Ubus not available: set HAVE_UBUS in src/config.h" msgstr "DBus ikke tilgjengelig: sett HAVE_DBUS i src/config.h" #: dnsmasq.c:267 msgid "max_port cannot be smaller than min_port" msgstr "" #: dnsmasq.c:271 msgid "port_limit must not be larger than available port range" msgstr "" #: dnsmasq.c:278 msgid "--auth-server required when an auth zone is defined." msgstr "" #: dnsmasq.c:283 msgid "zone serial must be configured in --auth-soa" msgstr "" #: dnsmasq.c:303 msgid "dhcp-range constructor not available on this platform" msgstr "" #: dnsmasq.c:377 msgid "cannot set --bind-interfaces and --bind-dynamic" msgstr "" #: dnsmasq.c:380 #, c-format msgid "failed to find list of interfaces: %s" msgstr "feilet finne liste av tilknytninger (interfaces): %s" #: dnsmasq.c:389 #, c-format msgid "unknown interface %s" msgstr "ukjent tilknytning (interface) %s" #: dnsmasq.c:396 #, fuzzy, c-format msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s" msgstr "feilet sette SO_REUSEADDR p DHCP socket: %s" #: dnsmasq.c:440 #, fuzzy msgid "Packet dumps not available: set HAVE_DUMP in src/config.h" msgstr "DBus ikke tilgjengelig: sett HAVE_DBUS i src/config.h" #: dnsmasq.c:448 dnsmasq.c:1232 #, c-format msgid "DBus error: %s" msgstr "DBus feil: %s" #: dnsmasq.c:451 msgid "DBus not available: set HAVE_DBUS in src/config.h" msgstr "DBus ikke tilgjengelig: sett HAVE_DBUS i src/config.h" #: dnsmasq.c:459 dnsmasq.c:1253 #, fuzzy, c-format msgid "UBus error: %s" msgstr "DBus feil: %s" #: dnsmasq.c:462 #, fuzzy msgid "UBus not available: set HAVE_UBUS in src/config.h" msgstr "DBus ikke tilgjengelig: sett HAVE_DBUS i src/config.h" #: dnsmasq.c:492 #, c-format msgid "unknown user or group: %s" msgstr "" #: dnsmasq.c:568 #, c-format msgid "process is missing required capability %s" msgstr "" #: dnsmasq.c:600 #, c-format msgid "cannot chdir to filesystem root: %s" msgstr "" #: dnsmasq.c:852 #, fuzzy, c-format msgid "started, version %s DNS disabled" msgstr "startet, versjon %s mellomlager deaktivert" #: dnsmasq.c:857 #, c-format msgid "started, version %s cachesize %d" msgstr "startet, versjon %s mellomlager strrelse %d" #: dnsmasq.c:859 msgid "cache size greater than 10000 may cause performance issues, and is unlikely to be useful." msgstr "" #: dnsmasq.c:862 #, c-format msgid "started, version %s cache disabled" msgstr "startet, versjon %s mellomlager deaktivert" #: dnsmasq.c:865 msgid "DNS service limited to local subnets" msgstr "" #: dnsmasq.c:868 #, c-format msgid "compile time options: %s" msgstr "kompilerings opsjoner: %s" #: dnsmasq.c:877 msgid "DBus support enabled: connected to system bus" msgstr "DBus sttte aktivert: koblet til system buss" #: dnsmasq.c:879 msgid "DBus support enabled: bus connection pending" msgstr "DBus sttte aktivert: avventer buss tilkobling" #: dnsmasq.c:887 #, fuzzy msgid "UBus support enabled: connected to system bus" msgstr "DBus sttte aktivert: koblet til system buss" #: dnsmasq.c:889 #, fuzzy msgid "UBus support enabled: bus connection pending" msgstr "DBus sttte aktivert: avventer buss tilkobling" #: dnsmasq.c:909 msgid "DNSSEC validation enabled but all unsigned answers are trusted" msgstr "" #: dnsmasq.c:911 msgid "DNSSEC validation enabled" msgstr "" #: dnsmasq.c:915 msgid "DNSSEC signature timestamps not checked until receipt of SIGINT" msgstr "" #: dnsmasq.c:918 msgid "DNSSEC signature timestamps not checked until system time valid" msgstr "" #: dnsmasq.c:921 #, c-format msgid "configured with trust anchor for %s keytag %u" msgstr "" #: dnsmasq.c:927 #, fuzzy, c-format msgid "warning: failed to change owner of %s: %s" msgstr "feilet laste navn fra %s: %s" #: dnsmasq.c:932 msgid "setting --bind-interfaces option because of OS limitations" msgstr "setter --bind-interfaces opsjon p grunn av OS begrensninger" #: dnsmasq.c:945 #, c-format msgid "warning: interface %s does not currently exist" msgstr "advarsel: nettverkskort %s eksisterer ikke for tiden" #: dnsmasq.c:950 msgid "warning: ignoring resolv-file flag because no-resolv is set" msgstr "" #: dnsmasq.c:953 #, fuzzy msgid "warning: no upstream servers configured" msgstr "setter oppstrms tjener fra DBus" #: dnsmasq.c:957 #, c-format msgid "asynchronous logging enabled, queue limit is %d messages" msgstr "" #: dnsmasq.c:978 msgid "IPv6 router advertisement enabled" msgstr "" #: dnsmasq.c:983 #, c-format msgid "DHCP, sockets bound exclusively to interface %s" msgstr "" #: dnsmasq.c:1000 msgid "root is " msgstr "" #: dnsmasq.c:1000 #, fuzzy msgid "enabled" msgstr "deaktivert" #: dnsmasq.c:1002 msgid "secure mode" msgstr "" #: dnsmasq.c:1003 #, fuzzy msgid "single port mode" msgstr "ugyldig portnummer" #: dnsmasq.c:1006 #, c-format msgid "warning: %s inaccessible" msgstr "" #: dnsmasq.c:1010 #, c-format msgid "warning: TFTP directory %s inaccessible" msgstr "" #: dnsmasq.c:1036 #, c-format msgid "restricting maximum simultaneous TFTP transfers to %d" msgstr "" #: dnsmasq.c:1095 #, c-format msgid "error binding DHCP socket to device %s" msgstr "" #: dnsmasq.c:1229 msgid "connected to system DBus" msgstr "tilkoblet til system DBus" #: dnsmasq.c:1250 #, fuzzy msgid "connected to system UBus" msgstr "tilkoblet til system DBus" #: dnsmasq.c:1416 #, c-format msgid "cannot fork into background: %s" msgstr "" #: dnsmasq.c:1420 #, fuzzy, c-format msgid "failed to create helper: %s" msgstr "feilet lese %s: %s" #: dnsmasq.c:1424 #, c-format msgid "setting capabilities failed: %s" msgstr "" #: dnsmasq.c:1428 #, fuzzy, c-format msgid "failed to change user-id to %s: %s" msgstr "feilet laste navn fra %s: %s" #: dnsmasq.c:1432 #, fuzzy, c-format msgid "failed to change group-id to %s: %s" msgstr "feilet laste navn fra %s: %s" #: dnsmasq.c:1436 #, fuzzy, c-format msgid "failed to open pidfile %s: %s" msgstr "feilet lese %s: %s" #: dnsmasq.c:1440 #, fuzzy, c-format msgid "cannot open log %s: %s" msgstr "kan ikke pne %s:%s" #: dnsmasq.c:1444 #, fuzzy, c-format msgid "failed to load Lua script: %s" msgstr "feilet laste %s: %s" #: dnsmasq.c:1448 #, c-format msgid "TFTP directory %s inaccessible: %s" msgstr "" #: dnsmasq.c:1452 #, fuzzy, c-format msgid "cannot create timestamp file %s: %s" msgstr "kan ikke pne eller lage leie fil: %s" #: dnsmasq.c:1536 #, c-format msgid "script process killed by signal %d" msgstr "" #: dnsmasq.c:1540 #, c-format msgid "script process exited with status %d" msgstr "" #: dnsmasq.c:1544 #, fuzzy, c-format msgid "failed to execute %s: %s" msgstr "feilet f tilgang til %s: %s" #: dnsmasq.c:1584 msgid "now checking DNSSEC signature timestamps" msgstr "" #: dnsmasq.c:1619 dnssec.c:160 dnssec.c:204 #, fuzzy, c-format msgid "failed to update mtime on %s: %s" msgstr "feilet lese %s: %s" #: dnsmasq.c:1631 msgid "exiting on receipt of SIGTERM" msgstr "avslutter etter mottak av SIGTERM" #: dnsmasq.c:1659 #, fuzzy, c-format msgid "failed to access %s: %s" msgstr "feilet f tilgang til %s: %s" #: dnsmasq.c:1690 #, c-format msgid "reading %s" msgstr "leser %s" #: dnsmasq.c:1706 #, fuzzy, c-format msgid "no servers found in %s, will retry" msgstr "intet ske direktiv funnet i %s" #: dhcp.c:51 #, c-format msgid "cannot create DHCP socket: %s" msgstr "kan ikke lage DHCP socket: %s" #: dhcp.c:66 #, c-format msgid "failed to set options on DHCP socket: %s" msgstr "feilet sette opsjoner p DHCP socket: %s" #: dhcp.c:87 #, 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:99 #, c-format msgid "failed to bind DHCP server socket: %s" msgstr "feilet binde DHCP tjener socket: %s" #: dhcp.c:125 #, c-format msgid "cannot create ICMP raw socket: %s." msgstr "kan ikke lage ICMP raw socket: %s" #: dhcp.c:254 dhcp6.c:186 #, fuzzy, c-format msgid "unknown interface %s in bridge-interface" msgstr "ukjent tilknytning (interface) %s" #: dhcp.c:295 #, c-format msgid "DHCP packet received on %s which has no address" msgstr "" #: dhcp.c:429 #, c-format msgid "ARP-cache injection failed: %s" msgstr "" #: dhcp.c:490 #, c-format msgid "Error sending DHCP packet to %s: %s" msgstr "" #: dhcp.c:547 #, c-format msgid "DHCP range %s -- %s is not consistent with netmask %s" msgstr "DHCP omrde %s -- %s er ikke konsistent med nettmaske %s" #: dhcp.c:930 #, c-format msgid "bad line at %s line %d" msgstr "drlig linje ved %s linje %d" #: dhcp.c:973 #, c-format msgid "ignoring %s line %d, duplicate name or IP address" msgstr "" #: dhcp.c:1034 #, c-format msgid "read %s - %d addresses" msgstr "les %s - %d adresser" #: dhcp.c:1136 #, c-format msgid "Cannot broadcast DHCP relay via interface %s" msgstr "" #: dhcp.c:1160 #, c-format msgid "broadcast via %s" msgstr "" #: dhcp.c:1163 rfc3315.c:2219 #, c-format msgid "DHCP relay at %s -> %s" msgstr "" #: lease.c:64 #, c-format msgid "ignoring invalid line in lease database: %s %s %s %s ..." msgstr "" #: lease.c:101 #, c-format msgid "ignoring invalid line in lease database, bad address: %s" msgstr "" #: lease.c:108 msgid "too many stored leases" msgstr "for mange lagrede leier" #: lease.c:176 #, fuzzy, c-format msgid "cannot open or create lease file %s: %s" msgstr "kan ikke pne eller lage leie fil: %s" #: lease.c:185 #, fuzzy msgid "failed to parse lease database cleanly" msgstr "feilet lese %s: %s" #: lease.c:188 #, fuzzy, c-format msgid "failed to read lease file %s: %s" msgstr "feilet lese %s: %s" #: lease.c:204 #, fuzzy, c-format msgid "cannot run lease-init script %s: %s" msgstr "kan ikke lese %s: %s" #: lease.c:210 #, c-format msgid "lease-init script returned exit code %s" msgstr "" #: lease.c:381 #, fuzzy, c-format msgid "failed to write %s: %s (retry in %u s)" msgstr "feilet lese %s: %s" #: lease.c:955 #, c-format msgid "Ignoring domain %s for DHCP host name %s" msgstr "" #: rfc2131.c:378 msgid "with subnet selector" msgstr "med subnet velger" #: rfc2131.c:383 msgid "via" msgstr "via" #: rfc2131.c:389 #, c-format msgid "no address range available for DHCP request %s %s" msgstr "ingen adresse omrde tilgjengelig for DHCP krav %s %s" #: rfc2131.c:403 #, fuzzy, c-format msgid "%u available DHCP subnet: %s/%s" msgstr "ingen adresse omrde tilgjengelig for DHCP krav %s %s" #: rfc2131.c:409 rfc3315.c:320 #, c-format msgid "%u available DHCP range: %s -- %s" msgstr "" #: rfc2131.c:521 #, fuzzy, c-format msgid "%u vendor class: %s" msgstr "DBus feil: %s" #: rfc2131.c:523 #, fuzzy, c-format msgid "%u user class: %s" msgstr "DBus feil: %s" #: rfc2131.c:557 msgid "disabled" msgstr "deaktivert" #: rfc2131.c:598 rfc2131.c:1087 rfc2131.c:1536 rfc3315.c:633 rfc3315.c:816 #: rfc3315.c:1122 msgid "ignored" msgstr "oversett" #: rfc2131.c:613 rfc2131.c:1340 rfc3315.c:868 msgid "address in use" msgstr "adresse i bruk" #: rfc2131.c:627 rfc2131.c:1141 msgid "no address available" msgstr "ingen adresse tilgjengelig" #: rfc2131.c:634 rfc2131.c:1302 msgid "wrong network" msgstr "galt nettverk" #: rfc2131.c:649 msgid "no address configured" msgstr "ingen adresse konfigurert" #: rfc2131.c:655 rfc2131.c:1353 msgid "no leases left" msgstr "ingen leier igjen" #: rfc2131.c:756 rfc3315.c:500 #, c-format msgid "%u client provides name: %s" msgstr "" #: rfc2131.c:885 msgid "PXE BIS not supported" msgstr "" #: rfc2131.c:1054 rfc3315.c:1223 #, fuzzy, c-format msgid "disabling DHCP static address %s for %s" msgstr "deaktiverer DHCP statisk adresse %s" #: rfc2131.c:1075 msgid "unknown lease" msgstr "ukjent leie" #: rfc2131.c:1110 #, c-format msgid "not using configured address %s because it is leased to %s" msgstr "" #: rfc2131.c:1120 #, c-format msgid "not using configured address %s because it is in use by the server or relay" msgstr "" #: rfc2131.c:1123 #, c-format msgid "not using configured address %s because it was previously declined" msgstr "" #: rfc2131.c:1139 rfc2131.c:1346 msgid "no unique-id" msgstr "" #: rfc2131.c:1238 msgid "wrong server-ID" msgstr "" #: rfc2131.c:1257 msgid "wrong address" msgstr "gal adresse" #: rfc2131.c:1275 rfc3315.c:976 msgid "lease not found" msgstr "leie ikke funnet" #: rfc2131.c:1310 msgid "address not available" msgstr "adresse ikke tilgjengelig" #: rfc2131.c:1321 msgid "static lease available" msgstr "statisk leie tilgjengelig" #: rfc2131.c:1325 msgid "address reserved" msgstr "adresse reservert" #: rfc2131.c:1334 #, c-format msgid "abandoning lease to %s of %s" msgstr "" #: rfc2131.c:1870 #, c-format msgid "%u bootfile name: %s" msgstr "" #: rfc2131.c:1879 #, fuzzy, c-format msgid "%u server name: %s" msgstr "DBus feil: %s" #: rfc2131.c:1889 #, fuzzy, c-format msgid "%u next server: %s" msgstr "DBus feil: %s" #: rfc2131.c:1893 #, c-format msgid "%u broadcast response" msgstr "" #: rfc2131.c:1956 #, 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:2267 msgid "PXE menu too large" msgstr "" #: rfc2131.c:2430 rfc3315.c:1517 #, fuzzy, c-format msgid "%u requested options: %s" msgstr "kompilerings opsjoner: %s" #: rfc2131.c:2747 #, c-format msgid "cannot send RFC3925 option: too many options for enterprise number %d" msgstr "" #: rfc2131.c:2810 #, c-format msgid "%u reply delay: %d" msgstr "" #: netlink.c:86 #, fuzzy, c-format msgid "cannot create netlink socket: %s" msgstr "kan ikke binde netlink socket: %s" #: netlink.c:379 #, fuzzy, c-format msgid "netlink returns error: %s" msgstr "DBus feil: %s" #: dbus.c:491 #, c-format msgid "Enabling --%s option from D-Bus" msgstr "" #: dbus.c:496 #, c-format msgid "Disabling --%s option from D-Bus" msgstr "" #: dbus.c:857 msgid "setting upstream servers from DBus" msgstr "setter oppstrms tjener fra DBus" #: dbus.c:907 msgid "could not register a DBus message handler" msgstr "kunne ikke registrere en DBus meldingshndterer" #: bpf.c:261 #, c-format msgid "cannot create DHCP BPF socket: %s" msgstr "kan ikke lage DHCP BPF socket: %s" #: bpf.c:289 #, fuzzy, c-format msgid "DHCP request for unsupported hardware type (%d) received on %s" msgstr "DHCP krav for ikke stttet maskinvare type (%d) mottatt p %s" #: bpf.c:374 #, fuzzy, c-format msgid "cannot create PF_ROUTE socket: %s" msgstr "kan ikke lage DHCP socket: %s" #: bpf.c:395 msgid "Unknown protocol version from route socket" msgstr "" #: helper.c:150 msgid "lease() function missing in Lua script" msgstr "" #: tftp.c:353 msgid "unable to get free port for TFTP" msgstr "" #: tftp.c:369 #, c-format msgid "unsupported request from %s" msgstr "" #: tftp.c:520 #, fuzzy, c-format msgid "file %s not found for %s" msgstr "fil %s ikke funnet for %s" #: tftp.c:609 #, c-format msgid "ignoring packet from %s (TID mismatch)" msgstr "" #: tftp.c:662 #, fuzzy, c-format msgid "failed sending %s to %s" msgstr "feilet lese %s: %s" #: tftp.c:662 #, c-format msgid "sent %s to %s" msgstr "" #: tftp.c:712 #, c-format msgid "error %d %s received from %s" msgstr "" #: log.c:203 #, c-format msgid "overflow: %d log entries lost" msgstr "" #: log.c:281 #, c-format msgid "log failed: %s" msgstr "" #: log.c:490 msgid "FAILED to start up" msgstr "FEILET starte opp" #: conntrack.c:63 #, c-format msgid "Conntrack connection mark retrieval failed: %s" msgstr "" #: dhcp6.c:51 #, fuzzy, c-format msgid "cannot create DHCPv6 socket: %s" msgstr "kan ikke lage DHCP socket: %s" #: dhcp6.c:72 #, 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:84 #, fuzzy, c-format msgid "failed to bind DHCPv6 server socket: %s" msgstr "feilet binde DHCP tjener socket: %s" #: rfc3315.c:173 #, fuzzy, c-format msgid "no address range available for DHCPv6 request from relay at %s" msgstr "ingen adresse omrde tilgjengelig for DHCP krav %s %s" #: rfc3315.c:182 #, fuzzy, c-format msgid "no address range available for DHCPv6 request via %s" msgstr "ingen adresse omrde tilgjengelig for DHCP krav %s %s" #: rfc3315.c:317 #, fuzzy, c-format msgid "%u available DHCPv6 subnet: %s/%d" msgstr "ingen adresse omrde tilgjengelig for DHCP krav %s %s" #: rfc3315.c:400 #, fuzzy, c-format msgid "%u vendor class: %u" msgstr "DBus feil: %s" #: rfc3315.c:448 #, fuzzy, c-format msgid "%u client MAC address: %s" msgstr "ingen tilknytning (interface) med adresse %s" #: rfc3315.c:763 rfc3315.c:860 #, fuzzy msgid "address unavailable" msgstr "adresse ikke tilgjengelig" #: rfc3315.c:775 rfc3315.c:904 rfc3315.c:1273 msgid "success" msgstr "" #: rfc3315.c:790 rfc3315.c:799 rfc3315.c:912 rfc3315.c:914 rfc3315.c:1048 #, fuzzy msgid "no addresses available" msgstr "ingen adresse tilgjengelig" #: rfc3315.c:891 msgid "not on link" msgstr "" #: rfc3315.c:980 rfc3315.c:1181 rfc3315.c:1262 msgid "no binding found" msgstr "" #: rfc3315.c:1017 msgid "deprecated" msgstr "" #: rfc3315.c:1024 #, fuzzy msgid "address invalid" msgstr "adresse i bruk" #: rfc3315.c:1082 rfc3315.c:1084 msgid "confirm failed" msgstr "" #: rfc3315.c:1099 #, fuzzy msgid "all addresses still on link" msgstr "drlig adresse ved %s linje %d" #: rfc3315.c:1190 msgid "release received" msgstr "" #: rfc3315.c:2200 #, c-format msgid "Cannot multicast DHCP relay via interface %s" msgstr "" #: rfc3315.c:2216 #, c-format msgid "multicast via %s" msgstr "" #: dhcp-common.c:187 #, c-format msgid "Ignoring duplicate dhcp-option %d" msgstr "" #: dhcp-common.c:264 #, c-format msgid "%u tags: %s" msgstr "" #: dhcp-common.c:484 #, c-format msgid "%s has more than one address in hostsfile, using %s for DHCP" msgstr "" #: dhcp-common.c:518 #, 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:738 #, c-format msgid "Known DHCP options:\n" msgstr "" #: dhcp-common.c:749 #, c-format msgid "Known DHCPv6 options:\n" msgstr "" #: dhcp-common.c:946 msgid ", prefix deprecated" msgstr "" #: dhcp-common.c:949 #, c-format msgid ", lease time " msgstr "" #: dhcp-common.c:991 #, c-format msgid "%s stateless on %s%.0s%.0s%s" msgstr "" #: dhcp-common.c:993 #, 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:995 #, c-format msgid "%s, proxy on subnet %.0s%s%.0s%.0s" msgstr "" #: dhcp-common.c:996 #, fuzzy, c-format msgid "%s, IP range %s -- %s%s%.0s" msgstr "DHCP, IP omrde %s -- %s, leie tid %s" #: dhcp-common.c:1009 #, c-format msgid "DHCPv4-derived IPv6 names on %s%s" msgstr "" #: dhcp-common.c:1012 #, fuzzy, c-format msgid "router advertisement on %s%s" msgstr "DHCP, statisk leie kun p %.0s%s, leie tid %s" #: dhcp-common.c:1043 #, c-format msgid "DHCP relay from %s via %s" msgstr "" #: dhcp-common.c:1045 #, c-format msgid "DHCP relay from %s to %s via %s" msgstr "" #: dhcp-common.c:1048 #, c-format msgid "DHCP relay from %s to %s" msgstr "" #: radv.c:110 #, fuzzy, c-format msgid "cannot create ICMPv6 socket: %s" msgstr "kan ikke lage DHCP socket: %s" #: auth.c:462 #, c-format msgid "ignoring zone transfer request from %s" msgstr "" #: ipset.c:99 #, fuzzy, c-format msgid "failed to create IPset control socket: %s" msgstr "feilet lage lytte socket: %s" #: ipset.c:211 #, fuzzy, c-format msgid "failed to update ipset %s: %s" msgstr "feilet lese %s: %s" #: pattern.c:29 #, c-format msgid "[pattern.c:%d] Assertion failure: %s" msgstr "" #: pattern.c:142 #, c-format msgid "Invalid DNS name: Invalid character %c." msgstr "" #: pattern.c:151 msgid "Invalid DNS name: Empty label." msgstr "" #: pattern.c:156 msgid "Invalid DNS name: Label starts with hyphen." msgstr "" #: pattern.c:170 msgid "Invalid DNS name: Label ends with hyphen." msgstr "" #: pattern.c:176 #, c-format msgid "Invalid DNS name: Label is too long (%zu)." msgstr "" #: pattern.c:184 #, c-format msgid "Invalid DNS name: Not enough labels (%zu)." msgstr "" #: pattern.c:189 msgid "Invalid DNS name: Final label is fully numeric." msgstr "" #: pattern.c:199 msgid "Invalid DNS name: \"local\" pseudo-TLD." msgstr "" #: pattern.c:204 #, c-format msgid "DNS name has invalid length (%zu)." msgstr "" #: pattern.c:258 #, c-format msgid "Invalid DNS name pattern: Invalid character %c." msgstr "" #: pattern.c:267 msgid "Invalid DNS name pattern: Empty label." msgstr "" #: pattern.c:272 msgid "Invalid DNS name pattern: Label starts with hyphen." msgstr "" #: pattern.c:285 msgid "Invalid DNS name pattern: Wildcard character used more than twice per label." msgstr "" #: pattern.c:295 msgid "Invalid DNS name pattern: Label ends with hyphen." msgstr "" #: pattern.c:301 #, c-format msgid "Invalid DNS name pattern: Label is too long (%zu)." msgstr "" #: pattern.c:309 #, c-format msgid "Invalid DNS name pattern: Not enough labels (%zu)." msgstr "" #: pattern.c:314 msgid "Invalid DNS name pattern: Wildcard within final two labels." msgstr "" #: pattern.c:319 msgid "Invalid DNS name pattern: Final label is fully numeric." msgstr "" #: pattern.c:329 msgid "Invalid DNS name pattern: \"local\" pseudo-TLD." msgstr "" #: pattern.c:334 #, c-format msgid "DNS name pattern has invalid length after removing wildcards (%zu)." msgstr "" #: dnssec.c:206 msgid "system time considered valid, now checking DNSSEC signature timestamps." msgstr "" #: dnssec.c:1018 #, c-format msgid "Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support" msgstr "" #: blockdata.c:55 #, c-format msgid "pool memory in use %zu, max %zu, allocated %zu" msgstr "" #: tables.c:61 #, fuzzy, c-format msgid "failed to access pf devices: %s" msgstr "feilet f tilgang til %s: %s" #: tables.c:74 #, fuzzy, c-format msgid "warning: no opened pf devices %s" msgstr "benytter lokale adresser kun for %s %s" #: tables.c:82 #, fuzzy, c-format msgid "error: cannot use table name %s" msgstr "klarer ikke f vertsnavn: %s" #: tables.c:90 #, c-format msgid "error: cannot strlcpy table name %s" msgstr "" #: tables.c:101 #, fuzzy, c-format msgid "IPset: error: %s" msgstr "DBus feil: %s" #: tables.c:108 msgid "info: table created" msgstr "" #: tables.c:133 #, c-format msgid "warning: DIOCR%sADDRS: %s" msgstr "" #: tables.c:137 #, fuzzy, c-format msgid "%d addresses %s" msgstr "les %s - %d adresser" #: inotify.c:62 #, fuzzy, c-format msgid "cannot access path %s: %s" msgstr "kan ikke lese %s: %s" #: inotify.c:95 #, fuzzy, c-format msgid "failed to create inotify: %s" msgstr "feilet lese %s: %s" #: inotify.c:111 #, c-format msgid "too many symlinks following %s" msgstr "" #: inotify.c:127 #, c-format msgid "directory %s for resolv-file is missing, cannot poll" msgstr "" #: inotify.c:131 inotify.c:200 #, fuzzy, c-format msgid "failed to create inotify for %s: %s" msgstr "feilet lage lytte socket: %s" #: inotify.c:178 inotify.c:185 #, fuzzy, c-format msgid "bad dynamic directory %s: %s" msgstr "kan ikke lese %s: %s" #: inotify.c:186 #, fuzzy msgid "not a directory" msgstr "kan ikke lese %s: %s" #: inotify.c:299 #, c-format msgid "inotify: %s removed" msgstr "" #: inotify.c:301 #, c-format msgid "inotify: %s new or modified" msgstr "" #: inotify.c:309 #, c-format msgid "inotify: flushed %u names read from %s" msgstr "" #: dump.c:68 #, fuzzy, c-format msgid "cannot create %s: %s" msgstr "kan ikke lese %s: %s" #: dump.c:74 #, fuzzy, c-format msgid "bad header in %s" msgstr "adresse i bruk" #: dump.c:287 #, fuzzy msgid "failed to write packet dump" msgstr "feilet lytte p socket: %s" #: dump.c:289 #, c-format msgid "%u dumping packet %u mask 0x%04x" msgstr "" #: dump.c:291 #, c-format msgid "dumping packet %u mask 0x%04x" msgstr "" #: ubus.c:79 #, c-format msgid "UBus subscription callback: %s subscriber(s)" msgstr "" #: ubus.c:99 #, fuzzy, c-format msgid "Cannot reconnect to UBus: %s" msgstr "kan ikke pne %s:%s" #: ubus.c:135 msgid "Cannot set UBus listeners: no connection" msgstr "" #: ubus.c:155 msgid "Cannot poll UBus listeners: no connection" msgstr "" #: ubus.c:168 msgid "Disconnecting from UBus" msgstr "" #: ubus.c:179 ubus.c:326 #, c-format msgid "UBus command failed: %d (%s)" msgstr "" #: hash-questions.c:40 msgid "Failed to create SHA-256 hash object" msgstr "" #: nftset.c:35 #, fuzzy msgid "failed to create nftset context" msgstr "feilet lage lytte socket: %s" #, fuzzy #~ msgid "bad IPv4 prefix" #~ msgstr "drlig port" #, fuzzy #~ msgid "Cannot add object to UBus: %s" #~ msgstr "kan ikke pne %s:%s" #, fuzzy #~ msgid "Failed to send UBus event: %s" #~ msgstr "feilet lytte p socket: %s" #~ msgid "attempt to set an IPv6 server address via DBus - no IPv6 support" #~ msgstr "forsk p sette en IPv6 tjener adresse via DBus - ingen IPv6 sttte" #, fuzzy #~ msgid "unknown prefix-class %d" #~ msgstr "ukjent leie" #, 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" #~ msgid "failed to set IPV6 options on listening socket: %s" #~ msgstr "feilet sette IPv6 opsjoner p lytte socket: %s" #~ msgid "failed to bind listening socket for %s: %s" #~ msgstr "feilet binde lytte socket for %s: %s" #~ msgid "must set exactly one interface on broken systems without IP_RECVIF" #~ msgstr "m sette nyaktig et interface p delagte systemer uten IP_RECVIF" #~ 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" #~ msgid "ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h" #~ msgstr "ISC dhcpf integrasjon ikke tilgjengelig: sett HAVE_ISC_READER i src/config.h" #, fuzzy #~ msgid "illegal domain %s in dhcp-config directive." #~ msgstr "dubliserte IP adresser i %s dhcp-config direktiv." #~ msgid "running as root" #~ msgstr "kjrer som rot (root)" #, fuzzy #~ msgid "read %s - %d hosts" #~ msgstr "les %s - %d adresser" #~ msgid "domains" #~ msgstr "domener" #~ msgid "Ignoring DHCP host name %s because it has an illegal domain part" #~ msgstr "Ignorerer DHCP verts navn %s p grunn av ulovlig domene del" #~ msgid "Display this message." #~ msgstr "Vis denne meldingen." #~ msgid "failed to read %s: %m" #~ msgstr "feilet lese %s: %m" #~ msgid "failed to read %s:%m" #~ msgstr "feilet lese %s:%m" #, fuzzy #~ msgid "cannot send encapsulated option %d: no space left in wrapper" #~ msgstr "kan ikke sende DHCP opsjon %d: ikke mer plass i pakken" #~ msgid "More than one vendor class matches, using %s" #~ msgstr "Mer enn en produsent klasse som passer, bruker %s" #~ msgid "forwarding table overflow: check for server loops." #~ msgstr "fremsendelse (forwarding) tabell overflyt: sjekk etter tjener lkker." #~ msgid "nested includes not allowed" #~ msgstr "nstede inkluderinger er ikke tillatt" #~ msgid "DHCP, %s will be written every %s" #~ msgstr "DHCP, %s vil bli skrevet hver %s" #~ msgid "cannot create DHCP packet socket: %s. Is CONFIG_PACKET enabled in your kernel?" #~ msgstr "kan ikke lage DHCP pakke socket: %s. Er CONFIG_PACKET aktivert i din kjerne?" #~ msgid "Cannot use RTnetlink socket, falling back to ioctl API" #~ msgstr "Kan ikke benytte RTnetlink socket, faller tilbake til ioctl API" dnsmasq-2.91/po/id.po0000664000175000017500000020211114765043257012661 0ustar srksrk# Indonesian translations for dnsmasq package. # This file is put in the public domain. # Salman AS , 2005. # msgid "" msgstr "" "Project-Id-Version: dnsmasq 2.24\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-06-18 12:24+0100\n" "PO-Revision-Date: 2017-07-17 18:30+0100\n" "Last-Translator: Salman AS \n" "Language-Team: Indonesian \n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ASCII\n" "Content-Transfer-Encoding: 8bit\n" #: cache.c:652 msgid "Internal error in cache." msgstr "" # OK #: cache.c:1179 #, fuzzy, c-format msgid "failed to load names from %s: %s" msgstr "gagal memuat nama-nama dari %s: %s" # OK #: cache.c:1201 dhcp.c:943 #, fuzzy, c-format msgid "bad address at %s line %d" msgstr "kesalahan nama pada %s baris %d" # OK #: cache.c:1254 dhcp.c:959 #, c-format msgid "bad name at %s line %d" msgstr "kesalahan nama pada %s baris %d" # OK #: cache.c:1265 #, fuzzy, c-format msgid "read %s - %d names" msgstr "membaca %s - %d alamat" # OK #: cache.c:1381 msgid "cleared cache" msgstr "cache telah dihapus" #: cache.c:1445 #, c-format msgid "No IPv4 address found for %s" msgstr "" #: cache.c:1491 #, c-format msgid "%s is a CNAME, not giving it to the DHCP lease of %s" msgstr "" # OK #: cache.c:1515 #, 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:1760 #, c-format msgid "time %lu" msgstr "" # OK #: cache.c:1761 #, 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:1763 #, c-format msgid "queries forwarded %u, queries answered locally %u" msgstr "" #: cache.c:1766 #, c-format msgid "queries answered from stale cache %u" msgstr "" #: cache.c:1768 #, c-format msgid "queries for authoritative zones %u" msgstr "" #: cache.c:1796 #, c-format msgid "server %s#%d: queries sent %u, retried %u, failed %u, nxdomain replies %u, avg. latency %ums" msgstr "" # OK #: util.c:51 #, fuzzy, c-format msgid "failed to seed the random number generator: %s" msgstr "gagal mendengarkan di socket: %s" # OK #: util.c:246 #, fuzzy msgid "failed to allocate memory" msgstr "gagal memuat %S: %m" # OK #: util.c:305 option.c:696 msgid "could not get memory" msgstr "tidak bisa mendapatkan memory" # OK #: util.c:326 #, fuzzy, c-format msgid "cannot create pipe: %s" msgstr "tidak bisa membaca %s: %s" # OK #: util.c:334 #, fuzzy, c-format msgid "failed to allocate %d bytes" msgstr "gagal memuat %S: %m" # OK #: util.c:344 #, fuzzy, c-format msgid "failed to reallocate %d bytes" msgstr "gagal memuat %S: %m" # OK #: util.c:465 #, fuzzy, c-format msgid "cannot read monotonic clock: %s" msgstr "tidak bisa mem-bind netlink socket: %s" # OK #: util.c:579 #, c-format msgid "infinite" msgstr "tak terbatas" # OK #: util.c:867 #, fuzzy, c-format msgid "failed to find kernel version: %s" msgstr "gagal mem-bind socket server DHCP: %s" # OK #: option.c:393 msgid "Specify local address(es) to listen on." msgstr "Tentukan alamat lokal untuk mendengarkan." # OK #: option.c:394 msgid "Return ipaddr for all hosts in specified domains." msgstr "Menghasilkan ipaddr untuk semua host dalam domain yang dipilih." # OK #: option.c:395 msgid "Fake reverse lookups for RFC1918 private address ranges." msgstr "Fake pencarian balik untuk alamat private sesuai dengan RFC1918." # OK #: option.c:396 msgid "Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)." msgstr "Perlakukan ipaddr sebagai NXDOMAIN (mengalahkan wildcard Verisign)." # OK #: option.c:397 #, 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:398 #, c-format msgid "Specify configuration file (defaults to %s)." msgstr "Tentukan file konfigurasi (default %s)." # OK #: option.c:399 msgid "Do NOT fork into the background: run in debug mode." msgstr "JANGAN berjalan di background: berjalan dalam modus debug." # OK #: option.c:400 msgid "Do NOT forward queries with no domain part." msgstr "JANGAN teruskan permintaan tanpa bagian domain." # OK #: option.c:401 msgid "Return self-pointing MX records for local hosts." msgstr "Mengembalikan record MX untuk diri sendiri host-host lokal." # OK #: option.c:402 msgid "Expand simple names in /etc/hosts with domain-suffix." msgstr "Melengkapi nama-nama di /etc/hosts dengan akhiran domain." # OK #: option.c:403 msgid "Don't forward spurious DNS requests from Windows hosts." msgstr "Jangan meneruskan permintaan DNS spurious dari host-host Windows." #: option.c:404 msgid "Don't include IPv4 addresses in DNS answers." msgstr "" #: option.c:405 msgid "Don't include IPv6 addresses in DNS answers." msgstr "" # OK #: option.c:406 msgid "Enable DHCP in the range given with lease duration." msgstr "Bolehkan DHCP dalam jangkauan yang diberikan dengan durasi lease." # OK #: option.c:407 #, c-format msgid "Change to this group after startup (defaults to %s)." msgstr "Ubah ke group ini setelah mulai (default %s)." # OK #: option.c:408 msgid "Set address or hostname for a specified machine." msgstr "Setel alamat atau nama host untuk mesin yang disebutkan." # OK #: option.c:409 #, fuzzy msgid "Read DHCP host specs from file." msgstr "nama MX salah" #: option.c:410 msgid "Read DHCP option specs from file." msgstr "" # OK #: option.c:411 #, fuzzy msgid "Read DHCP host specs from a directory." msgstr "nama MX salah" # OK #: option.c:412 #, fuzzy msgid "Read DHCP options from a directory." msgstr "nama MX salah" #: option.c:413 msgid "Evaluate conditional tag expression." msgstr "" # OK #: option.c:414 #, c-format msgid "Do NOT load %s file." msgstr "JANGAN muat file %s." # OK #: option.c:415 #, 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:416 #, fuzzy msgid "Read hosts files from a directory." msgstr "nama MX salah" # OK #: option.c:417 msgid "Specify interface(s) to listen on." msgstr "Sebutkan antarmuka untuk mendengarkan." # OK #: option.c:418 msgid "Specify interface(s) NOT to listen on." msgstr "Sebutkan antarmuka untuk TIDAK mendengarkan." # OK #: option.c:419 #, fuzzy msgid "Map DHCP user class to tag." msgstr "Petakan kelas user DHCP ke setelan yang dipilih." #: option.c:420 msgid "Map RFC3046 circuit-id to tag." msgstr "" #: option.c:421 msgid "Map RFC3046 remote-id to tag." msgstr "" #: option.c:422 msgid "Map RFC3993 subscriber-id to tag." msgstr "" #: option.c:423 msgid "Specify vendor class to match for PXE requests." msgstr "" # OK #: option.c:424 #, fuzzy msgid "Don't do DHCP for hosts with tag set." msgstr "Jangan menggunakan DHCP untuk host-host yang dipilih." # OK #: option.c:425 #, fuzzy msgid "Force broadcast replies for hosts with tag set." msgstr "Jangan menggunakan DHCP untuk host-host yang dipilih." # OK #: option.c:426 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:427 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:428 #, c-format msgid "Specify where to store DHCP leases (defaults to %s)." msgstr "Sebutkan lokasi untuk menyimpan lease DHCP (default %s)." # OK #: option.c:429 msgid "Return MX records for local hosts." msgstr "Kembalikan rekord MX untuk host-host lokal." # OK #: option.c:430 msgid "Specify an MX record." msgstr "Sebutkan sebuah rekord MX." # OK #: option.c:431 msgid "Specify BOOTP options to DHCP server." msgstr "Sebutkan pilihan-pilihan BOOTP untuk DHCP server." #: option.c:432 #, c-format msgid "Do NOT poll %s file, reload only on SIGHUP." msgstr "Jangan kumpulkan file %s, muat kembali saat SIGHUP." # OK #: option.c:433 msgid "Do NOT cache failed search results." msgstr "JANGAN menyimpan hasil pencarian yang gagal." #: option.c:434 msgid "Use expired cache data for faster reply." msgstr "" # OK #: option.c:435 #, 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:436 #, fuzzy msgid "Specify options to be sent to DHCP clients." msgstr "Setel pilihan-pilihan tambahan yang akan disetel untuk klien-klien DHCP." #: option.c:437 msgid "DHCP option sent even if the client does not request it." msgstr "" # OK #: option.c:438 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:439 #, 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:440 #, fuzzy msgid "Log DNS queries." msgstr "Permintaan log." # OK #: option.c:441 #, fuzzy msgid "Force the originating port for upstream DNS queries." msgstr "Paksa port asal untuk permintaan ke atas." #: option.c:442 msgid "Set maximum number of random originating ports for a query." msgstr "" # OK #: option.c:443 msgid "Do NOT read resolv.conf." msgstr "JANGAN baca resolv.conf." # OK #: option.c:444 #, c-format msgid "Specify path to resolv.conf (defaults to %s)." msgstr "Sebutkan path ke resolv.conf (default %s)." # OK #: option.c:445 #, fuzzy msgid "Specify path to file with server= options" msgstr "Sebutkan path file PID. (default %s)." # OK #: option.c:446 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:447 #, 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:448 msgid "Never forward queries to specified domains." msgstr "JANGAN pernah meneruskan permintaan ke domain yang disebutkan." # OK #: option.c:449 msgid "Specify the domain to be assigned in DHCP leases." msgstr "Sebutkan domain yang digunakan dalam lease DHCP." # OK #: option.c:450 msgid "Specify default target in an MX record." msgstr "Sebutkan tujuan default dalam rekord MX." # OK #: option.c:451 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:452 #, 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:453 #, 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:454 #, fuzzy msgid "Specify time-to-live ceiling for cache." msgstr "Sebutkan time-to-live dalam detik untuk jawaban dari /etc/hosts." # OK #: option.c:455 #, fuzzy msgid "Specify time-to-live floor for cache." msgstr "Sebutkan time-to-live dalam detik untuk jawaban dari /etc/hosts." #: option.c:456 msgid "Retry DNS queries after this many milliseconds." msgstr "" # OK #: option.c:457 #, c-format msgid "Change to this user after startup. (defaults to %s)." msgstr "Ubah ke user ini setelah mulai. (default %s)." # OK #: option.c:458 #, fuzzy msgid "Map DHCP vendor class to tag." msgstr "Memetakan kelas vendor DHCP ke daftar pilihan." # OK #: option.c:459 msgid "Display dnsmasq version and copyright information." msgstr "Menampilkan versi dan informasi hak cipta dnsmasq." # OK #: option.c:460 msgid "Translate IPv4 addresses from upstream servers." msgstr "Terjemahkan alamat-alamat IPv4 dari server-server di atas." # OK #: option.c:461 msgid "Specify a SRV record." msgstr "Sebutkan rekord SRV." #: option.c:462 msgid "Display this message. Use --help dhcp or --help dhcp6 for known DHCP options." msgstr "" # OK #: option.c:463 #, fuzzy, c-format msgid "Specify path of PID file (defaults to %s)." msgstr "Sebutkan path file PID. (default %s)." # OK #: option.c:464 #, c-format msgid "Specify maximum number of DHCP leases (defaults to %s)." msgstr "Sebutkan jumlah maksimum lease DHCP (default %s)." # OK #: option.c:465 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:466 msgid "Specify TXT DNS record." msgstr "Sebutkan rekord TXT DNS." # OK #: option.c:467 #, fuzzy msgid "Specify PTR DNS record." msgstr "Sebutkan rekord TXT DNS." #: option.c:468 msgid "Give DNS name to IPv4 address of interface." msgstr "" # OK #: option.c:469 msgid "Bind only to interfaces in use." msgstr "Hanya kaitkan ke antarmuka yang sedang digunakan saja." # OK #: option.c:470 #, c-format msgid "Read DHCP static host information from %s." msgstr "Baca informasi statik host DHCP dari %s." # OK #: option.c:471 msgid "Enable the DBus interface for setting upstream servers, etc." msgstr "Mungkinkan antar muka DBus untuk menyetel server-server di atas, dsb." #: option.c:472 msgid "Enable the UBus interface." msgstr "" # OK #: option.c:473 msgid "Do not provide DHCP on this interface, only provide DNS." msgstr "JANGAN menyediakan DHCP pada antarmuka ini, hanya menyediakan DNS." # OK #: option.c:474 msgid "Enable dynamic address allocation for bootp." msgstr "Mungkinkan alokasi alamat dinamis untuk bootp." # OK #: option.c:475 #, fuzzy msgid "Map MAC address (with wildcards) to option set." msgstr "Memetakan kelas vendor DHCP ke daftar pilihan." #: option.c:476 msgid "Treat DHCP requests on aliases as arriving from interface." msgstr "" #: option.c:477 msgid "Specify extra networks sharing a broadcast domain for DHCP" msgstr "" #: option.c:478 msgid "Disable ICMP echo address checking in the DHCP server." msgstr "" #: option.c:479 msgid "Shell script to run on DHCP lease creation and destruction." msgstr "" #: option.c:480 msgid "Lua script to run on DHCP lease creation and destruction." msgstr "" #: option.c:481 msgid "Run lease-change scripts as this user." msgstr "" #: option.c:482 msgid "Call dhcp-script with changes to local ARP table." msgstr "" #: option.c:483 msgid "Read configuration from all the files in this directory." msgstr "" #: option.c:484 msgid "Execute file and read configuration from stdin." msgstr "" # OK #: option.c:485 #, fuzzy msgid "Log to this syslog facility or file. (defaults to DAEMON)" msgstr "Ubah ke user ini setelah mulai. (default %s)." #: option.c:486 msgid "Do not use leasefile." msgstr "" # OK #: option.c:487 #, fuzzy, c-format msgid "Maximum number of concurrent DNS queries. (defaults to %s)" msgstr "Sebutkan jumlah maksimum lease DHCP (default %s)." #: option.c:488 #, c-format msgid "Clear DNS cache when reloading %s." msgstr "" #: option.c:489 msgid "Ignore hostnames provided by DHCP clients." msgstr "" #: option.c:490 msgid "Do NOT reuse filename and server fields for extra DHCP options." msgstr "" #: option.c:491 msgid "Enable integrated read-only TFTP server." msgstr "" #: option.c:492 msgid "Export files by TFTP only from the specified subtree." msgstr "" #: option.c:493 msgid "Add client IP or hardware address to tftp-root." msgstr "" #: option.c:494 msgid "Allow access only to files owned by the user running dnsmasq." msgstr "" #: option.c:495 msgid "Do not terminate the service if TFTP directories are inaccessible." msgstr "" # OK #: option.c:496 #, fuzzy, c-format msgid "Maximum number of concurrent TFTP transfers (defaults to %s)." msgstr "Sebutkan jumlah maksimum lease DHCP (default %s)." # OK #: option.c:497 #, fuzzy msgid "Maximum MTU to use for TFTP transfers." msgstr "Sebutkan jumlah maksimum lease DHCP (default %s)." #: option.c:498 msgid "Disable the TFTP blocksize extension." msgstr "" #: option.c:499 msgid "Convert TFTP filenames to lowercase" msgstr "" #: option.c:500 msgid "Ephemeral port range for use by TFTP transfers." msgstr "" #: option.c:501 msgid "Use only one port for TFTP server." msgstr "" #: option.c:502 msgid "Extra logging for DHCP." msgstr "" #: option.c:503 msgid "Enable async. logging; optionally set queue length." msgstr "" #: option.c:504 msgid "Stop DNS rebinding. Filter private IP ranges when resolving." msgstr "" #: option.c:505 msgid "Allow rebinding of 127.0.0.0/8, for RBL servers." msgstr "" #: option.c:506 msgid "Inhibit DNS-rebind protection on this domain." msgstr "" #: option.c:507 msgid "Always perform DNS queries to all servers." msgstr "" #: option.c:508 msgid "Set tag if client includes matching option in request." msgstr "" #: option.c:509 msgid "Set tag if client provides given name." msgstr "" #: option.c:510 msgid "Use alternative ports for DHCP." msgstr "" # OK #: option.c:511 #, fuzzy msgid "Specify NAPTR DNS record." msgstr "Sebutkan rekord TXT DNS." #: option.c:512 msgid "Specify lowest port available for DNS query transmission." msgstr "" #: option.c:513 msgid "Specify highest port available for DNS query transmission." msgstr "" #: option.c:514 msgid "Use only fully qualified domain names for DHCP clients." msgstr "" #: option.c:515 msgid "Generate hostnames based on MAC address for nameless clients." msgstr "" #: option.c:516 msgid "Use these DHCP relays as full proxies." msgstr "" #: option.c:517 msgid "Relay DHCP requests to a remote server" msgstr "" #: option.c:518 msgid "Specify alias name for LOCAL DNS name." msgstr "" # OK #: option.c:519 #, fuzzy msgid "Prompt to send to PXE clients." msgstr "Setel pilihan-pilihan tambahan yang akan disetel untuk klien-klien DHCP." #: option.c:520 msgid "Boot service for PXE menu." msgstr "" #: option.c:521 msgid "Check configuration syntax." msgstr "" #: option.c:522 msgid "Add requestor's MAC address to forwarded DNS queries." msgstr "" #: option.c:523 msgid "Strip MAC information from queries." msgstr "" #: option.c:524 msgid "Add specified IP subnet to forwarded DNS queries." msgstr "" #: option.c:525 msgid "Strip ECS information from queries." msgstr "" # OK #: option.c:526 #, fuzzy msgid "Add client identification to forwarded DNS queries." msgstr "Paksa port asal untuk permintaan ke atas." # OK #: option.c:527 #, fuzzy msgid "Proxy DNSSEC validation results from upstream nameservers." msgstr "Terjemahkan alamat-alamat IPv4 dari server-server di atas." #: option.c:528 msgid "Attempt to allocate sequential IP addresses to DHCP clients." msgstr "" # OK #: option.c:529 #, fuzzy msgid "Ignore client identifier option sent by DHCP clients." msgstr "Setel pilihan-pilihan tambahan yang akan disetel untuk klien-klien DHCP." #: option.c:530 msgid "Copy connection-track mark from queries to upstream connections." msgstr "" #: option.c:531 msgid "Allow DHCP clients to do their own DDNS updates." msgstr "" #: option.c:532 msgid "Send router-advertisements for interfaces doing DHCPv6" msgstr "" #: option.c:533 msgid "Specify DUID_EN-type DHCPv6 server DUID" msgstr "" # OK #: option.c:534 #, fuzzy msgid "Specify host (A/AAAA and PTR) records" msgstr "Sebutkan sebuah rekord MX." #: option.c:535 msgid "Specify host record in interface subnet" msgstr "" #: option.c:536 msgid "Specify certification authority authorization record" msgstr "" # OK #: option.c:537 #, fuzzy msgid "Specify arbitrary DNS resource record" msgstr "Sebutkan rekord TXT DNS." # OK #: option.c:538 #, fuzzy msgid "Bind to interfaces in use - check for new interfaces" msgstr "antarmuka tidak dikenal %s" #: option.c:539 msgid "Export local names to global DNS" msgstr "" #: option.c:540 msgid "Domain to export to global DNS" msgstr "" #: option.c:541 msgid "Set TTL for authoritative replies" msgstr "" #: option.c:542 msgid "Set authoritative zone information" msgstr "" #: option.c:543 msgid "Secondary authoritative nameservers for forward domains" msgstr "" #: option.c:544 msgid "Peers which are allowed to do zone transfer" msgstr "" #: option.c:545 msgid "Specify ipsets to which matching domains should be added" msgstr "" #: option.c:546 msgid "Specify nftables sets to which matching domains should be added" msgstr "" #: option.c:547 msgid "Enable filtering of DNS queries with connection-track marks." msgstr "" #: option.c:548 msgid "Set allowed DNS patterns for a connection-track mark." msgstr "" #: option.c:549 msgid "Specify a domain and address range for synthesised names" msgstr "" #: option.c:550 msgid "Activate DNSSEC validation" msgstr "" #: option.c:551 msgid "Specify trust anchor key digest." msgstr "" #: option.c:552 msgid "Disable upstream checking for DNSSEC debugging." msgstr "" #: option.c:553 msgid "Ensure answers without DNSSEC are in unsigned zones." msgstr "" #: option.c:554 msgid "Don't check DNSSEC signature timestamps until first cache-reload" msgstr "" #: option.c:555 msgid "Timestamp file to verify system clock for DNSSEC" msgstr "" #: option.c:556 msgid "Set MTU, priority, resend-interval and router-lifetime" msgstr "" #: option.c:557 msgid "Do not log routine DHCP." msgstr "" #: option.c:558 msgid "Do not log routine DHCPv6." msgstr "" #: option.c:559 msgid "Do not log RA." msgstr "" #: option.c:560 msgid "Log debugging information." msgstr "" #: option.c:561 msgid "Accept queries only from directly-connected networks." msgstr "" #: option.c:562 msgid "Detect and remove DNS forwarding loops." msgstr "" #: option.c:563 msgid "Ignore DNS responses containing ipaddr." msgstr "" #: option.c:564 msgid "Set TTL in DNS responses with DHCP-derived addresses." msgstr "" #: option.c:565 msgid "Delay DHCP replies for at least number of seconds." msgstr "" #: option.c:566 msgid "Enables DHCPv4 Rapid Commit option." msgstr "" #: option.c:567 msgid "Path to debug packet dump file" msgstr "" #: option.c:568 msgid "Mask which packets to dump" msgstr "" #: option.c:569 msgid "Call dhcp-script when lease expiry changes." msgstr "" #: option.c:570 msgid "Send Cisco Umbrella identifiers including remote IP." msgstr "" #: option.c:571 msgid "Do not log routine TFTP." msgstr "" #: option.c:572 msgid "Suppress round-robin ordering of DNS records." msgstr "" # OK #: option.c:802 #, c-format msgid "" "Usage: dnsmasq [options]\n" "\n" msgstr "" "Penggunaan: dnsmasq [pilihan]\n" "\n" # OK #: option.c:804 #, c-format msgid "Use short options only on the command line.\n" msgstr "Gunakan pilihan pendek saja pada perintah baris.\n" # OK #: option.c:806 #, fuzzy, c-format msgid "Valid options are:\n" msgstr "Pilihan yang boleh adalah:\n" # OK #: option.c:853 option.c:1055 #, fuzzy msgid "bad address" msgstr "membaca %s - %d alamat" # OK #: option.c:882 option.c:886 msgid "bad port" msgstr "port salah" #: option.c:899 option.c:1002 option.c:1048 msgid "interface binding not supported" msgstr "" #: option.c:955 msgid "Cannot resolve server name" msgstr "" #: option.c:991 msgid "cannot use IPv4 server address with IPv6 source address" msgstr "" #: option.c:997 option.c:1043 msgid "interface can only be specified once" msgstr "" # OK #: option.c:1011 option.c:4785 #, fuzzy msgid "bad interface name" msgstr "nama MX salah" #: option.c:1037 msgid "cannot use IPv6 server address with IPv4 source address" msgstr "" # OK #: option.c:1124 #, fuzzy msgid "bad IPv4 prefix length" msgstr "port salah" # OK #: option.c:1155 option.c:1165 option.c:1240 option.c:1250 option.c:5360 msgid "error" msgstr "kesalahan" # OK #: option.c:1207 #, fuzzy msgid "bad IPv6 prefix length" msgstr "port salah" #: option.c:1467 msgid "inappropriate vendor:" msgstr "" #: option.c:1474 msgid "inappropriate encap:" msgstr "" #: option.c:1500 msgid "unsupported encapsulation for IPv6 option" msgstr "" # OK #: option.c:1514 msgid "bad dhcp-option" msgstr "dhcp-option salah" # OK #: option.c:1592 #, fuzzy msgid "bad IP address" msgstr "membaca %s - %d alamat" # OK #: option.c:1595 option.c:1734 option.c:3928 #, fuzzy msgid "bad IPv6 address" msgstr "membaca %s - %d alamat" # OK #: option.c:1688 #, fuzzy msgid "bad IPv4 address" msgstr "membaca %s - %d alamat" # OK #: option.c:1761 option.c:1856 msgid "bad domain in dhcp-option" msgstr "domain dalam dhcp-option salah" # OK #: option.c:1900 msgid "dhcp-option too long" msgstr "dhcp-option terlalu panjang" #: option.c:1907 msgid "illegal dhcp-match" msgstr "" #: option.c:1966 msgid "illegal repeated flag" msgstr "" #: option.c:1974 msgid "illegal repeated keyword" msgstr "" # OK #: option.c:2056 option.c:5533 #, fuzzy, c-format msgid "cannot access directory %s: %s" msgstr "tidak bisa membaca %s: %s" # OK #: option.c:2102 tftp.c:573 dump.c:72 #, fuzzy, c-format msgid "cannot access %s: %s" msgstr "tidak bisa membaca %s: %s" #: option.c:2219 msgid "setting log facility is not possible under Android" msgstr "" #: option.c:2228 msgid "bad log facility" msgstr "" # OK #: option.c:2281 msgid "bad MX preference" msgstr "kesukaan MX salah" # OK #: option.c:2289 msgid "bad MX name" msgstr "nama MX salah" # OK #: option.c:2304 msgid "bad MX target" msgstr "target MX salah" #: option.c:2324 msgid "recompile with HAVE_SCRIPT defined to enable lease-change scripts" msgstr "" #: option.c:2328 msgid "recompile with HAVE_LUASCRIPT defined to enable Lua scripts" msgstr "" # OK #: option.c:2447 #, fuzzy msgid "invalid auth-zone" msgstr "weight tidak benar" # OK #: option.c:2589 option.c:2621 #, fuzzy msgid "bad prefix length" msgstr "port salah" # OK #: option.c:2601 option.c:2642 option.c:2696 #, fuzzy msgid "bad prefix" msgstr "port salah" #: option.c:2716 msgid "prefix length too small" msgstr "" # OK #: option.c:3010 #, fuzzy msgid "Bad address in --address" msgstr "alamat telah digunakan" #: option.c:3110 msgid "recompile with HAVE_IPSET defined to enable ipset directives" msgstr "" #: option.c:3117 msgid "recompile with HAVE_NFTSET defined to enable nftset directives" msgstr "" #: option.c:3192 option.c:3210 msgid "recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives" msgstr "" # OK #: option.c:3496 #, fuzzy msgid "bad port range" msgstr "port salah" #: option.c:3522 msgid "bad bridge-interface" msgstr "" #: option.c:3566 msgid "bad shared-network" msgstr "" #: option.c:3620 msgid "only one tag allowed" msgstr "" # OK #: option.c:3641 option.c:3657 option.c:3783 option.c:3791 option.c:3834 msgid "bad dhcp-range" msgstr "dhcp-range salah" # OK #: option.c:3675 msgid "inconsistent DHCP range" msgstr "jangkauan DHCP tidak konsisten" #: option.c:3741 msgid "prefix length must be exactly 64 for RA subnets" msgstr "" #: option.c:3743 msgid "prefix length must be exactly 64 for subnet constructors" msgstr "" #: option.c:3746 msgid "prefix length must be at least 64" msgstr "" # OK #: option.c:3749 #, fuzzy msgid "inconsistent DHCPv6 range" msgstr "jangkauan DHCP tidak konsisten" #: option.c:3768 msgid "prefix must be zero with \"constructor:\" argument" msgstr "" # OK #: option.c:3893 option.c:3971 #, fuzzy msgid "bad hex constant" msgstr "dhcp-host salah" # OK #: option.c:3946 #, fuzzy msgid "bad IPv6 prefix" msgstr "port salah" # OK #: option.c:3994 #, fuzzy, c-format msgid "duplicate dhcp-host IP address %s" msgstr "alamat IP kembar %s dalam direktif dhcp-config" # OK #: option.c:4056 #, fuzzy msgid "bad DHCP host name" msgstr "nama MX salah" # OK #: option.c:4142 #, fuzzy msgid "bad tag-if" msgstr "target MX salah" # OK #: option.c:4490 option.c:5046 msgid "invalid port number" msgstr "nomor port tidak benar" # OK #: option.c:4546 #, fuzzy msgid "bad dhcp-proxy address" msgstr "membaca %s - %d alamat" # OK #: option.c:4627 #, fuzzy msgid "Bad dhcp-relay" msgstr "dhcp-range salah" #: option.c:4671 msgid "bad RA-params" msgstr "" #: option.c:4681 msgid "bad DUID" msgstr "" # OK #: option.c:4715 #, fuzzy msgid "missing address in alias" msgstr "alamat telah digunakan" # OK #: option.c:4721 #, fuzzy msgid "invalid alias range" msgstr "weight tidak benar" # OK #: option.c:4770 #, fuzzy msgid "missing address in dynamic host" msgstr "alamat telah digunakan" # OK #: option.c:4785 #, fuzzy msgid "bad dynamic host" msgstr "tidak bisa membaca %s: %s" #: option.c:4803 option.c:4819 msgid "bad CNAME" msgstr "" #: option.c:4827 msgid "duplicate CNAME" msgstr "" # OK #: option.c:4854 #, fuzzy msgid "bad PTR record" msgstr "rekord SRV salah" # OK #: option.c:4889 #, fuzzy msgid "bad NAPTR record" msgstr "rekord SRV salah" # OK #: option.c:4925 #, fuzzy msgid "bad RR record" msgstr "rekord SRV salah" # OK #: option.c:4958 #, fuzzy msgid "bad CAA record" msgstr "rekord SRV salah" # OK #: option.c:4987 msgid "bad TXT record" msgstr "rekord TXT salah" # OK #: option.c:5030 msgid "bad SRV record" msgstr "rekord SRV salah" # OK #: option.c:5037 msgid "bad SRV target" msgstr "target SRV salah" # OK #: option.c:5056 msgid "invalid priority" msgstr "prioritas tidak benar" # OK #: option.c:5061 msgid "invalid weight" msgstr "weight tidak benar" # OK #: option.c:5084 #, fuzzy msgid "Bad host-record" msgstr "rekord SRV salah" # OK #: option.c:5123 #, fuzzy msgid "Bad name in host-record" msgstr "kesalahan nama di %s" #: option.c:5165 msgid "bad value for dnssec-check-unsigned" msgstr "" # OK #: option.c:5201 #, fuzzy msgid "bad trust anchor" msgstr "port salah" #: option.c:5217 msgid "bad HEX in trust anchor" msgstr "" #: option.c:5228 msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)" msgstr "" # OK #: option.c:5290 msgid "missing \"" msgstr "kurang \"" # OK #: option.c:5347 msgid "bad option" msgstr "pilihan salah" # OK #: option.c:5349 msgid "extraneous parameter" msgstr "parameter berlebihan" # OK #: option.c:5351 msgid "missing parameter" msgstr "parameter kurang" # OK #: option.c:5353 #, fuzzy msgid "illegal option" msgstr "pilihan salah" #: option.c:5363 #, c-format msgid " in output from %s" msgstr "" # OK #: option.c:5365 #, fuzzy, c-format msgid " at line %d of %s" msgstr "%s pada baris %d dari %%s" # OK #: option.c:5380 option.c:5683 option.c:5694 #, fuzzy, c-format msgid "read %s" msgstr "membaca %s" # OK #: option.c:5446 #, fuzzy, c-format msgid "cannot execute %s: %s" msgstr "tidak bisa membaca %s: %s" # OK #: option.c:5454 option.c:5615 tftp.c:790 #, c-format msgid "cannot read %s: %s" msgstr "tidak bisa membaca %s: %s" # OK #: option.c:5473 #, fuzzy, c-format msgid "error executing %s: %s" msgstr "gagal mengakses %s: %s" #: option.c:5476 #, c-format msgid "%s returns non-zero error code" msgstr "" #: option.c:5775 msgid "junk found in command line" msgstr "" # OK #: option.c:5815 #, c-format msgid "Dnsmasq version %s %s\n" msgstr "Dnsmasq versi %s %s\n" # OK #: option.c:5816 #, fuzzy, c-format msgid "" "Compile time options: %s\n" "\n" msgstr "" "Pilihan-pilihan saat kompilasi %s\n" "\n" # OK #: option.c:5817 #, c-format msgid "This software comes with ABSOLUTELY NO WARRANTY.\n" msgstr "Perangkat lunak ini tersedia TANPA JAMINAN SEDIKITPUN.\n" # OK #: option.c:5818 #, 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:5819 #, 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:5836 msgid "try --help" msgstr "" #: option.c:5838 msgid "try -w" msgstr "" # OK #: option.c:5840 #, fuzzy, c-format msgid "bad command line options: %s" msgstr "pilihan baris perintah salah: %s." #: option.c:5909 #, c-format msgid "CNAME loop involving %s" msgstr "" # OK #: option.c:5950 #, c-format msgid "cannot get host-name: %s" msgstr "tidak bisa mendapatkan host-name: %s" # OK #: option.c:5978 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:5988 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:5991 network.c:1727 dhcp.c:892 #, fuzzy, c-format msgid "failed to read %s: %s" msgstr "gagal membaca %s: %s" # OK #: option.c:6008 #, c-format msgid "no search directive found in %s" msgstr "tidak ditemukan direktif search di %s" #: option.c:6029 msgid "there must be a default domain when --dhcp-fqdn is set" msgstr "" #: option.c:6038 msgid "syntax check OK" msgstr "" # OK #: forward.c:107 #, fuzzy, c-format msgid "failed to send packet: %s" msgstr "gagal mendengarkan di socket: %s" #: forward.c:715 msgid "discarding DNS reply: subnet option mismatch" msgstr "" # OK #: forward.c:780 #, c-format msgid "nameserver %s refused to do a recursive query" msgstr "nameserver %s menolak melakukan resolusi rekursif" #: forward.c:826 #, c-format msgid "possible DNS-rebind attack detected: %s" msgstr "" #: forward.c:1239 #, c-format msgid "reducing DNS packet size for nameserver %s to %d" msgstr "" #: forward.c:1565 #, c-format msgid "ignoring query from non-local network %s (logged only once)" msgstr "" #: forward.c:2139 #, c-format msgid "ignoring query from non-local network %s" msgstr "" #: forward.c:2501 #, fuzzy, c-format msgid "failed to bind server socket to %s: %s" msgstr "gagal mem-bind socket untuk mendengarkan %s: %s" # OK #: forward.c:2867 #, fuzzy, c-format msgid "Maximum number of concurrent DNS queries reached (max: %d)" msgstr "Sebutkan jumlah maksimum lease DHCP (default %s)." # OK #: forward.c:2869 #, fuzzy, c-format msgid "Maximum number of concurrent DNS queries to %s reached (max: %d)" msgstr "Sebutkan jumlah maksimum lease DHCP (default %s)." #: network.c:700 #, c-format msgid "stopped listening on %s(#%d): %s port %d" msgstr "" # OK #: network.c:911 #, fuzzy, c-format msgid "failed to create listening socket for %s: %s" msgstr "gagal membuat socket: %s " #: network.c:1192 #, c-format msgid "listening on %s(#%d): %s port %d" msgstr "" # OK #: network.c:1219 #, fuzzy, c-format msgid "listening on %s port %d" msgstr "gagal membaca %s: %s" #: network.c:1252 #, c-format msgid "LOUD WARNING: listening on %s may accept requests via interfaces other than %s" msgstr "" #: network.c:1259 msgid "LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)" msgstr "" # OK #: network.c:1268 #, fuzzy, c-format msgid "warning: using interface %s instead" msgstr "peringatan: antarmuka %s tidak ada" # OK #: network.c:1277 #, fuzzy, c-format msgid "warning: no addresses found for interface %s" msgstr "menggunakan alamat lokal saja untuk %s %s" # OK #: network.c:1335 #, fuzzy, c-format msgid "interface %s failed to join DHCPv6 multicast group: %s" msgstr "gagal mem-bind socket server DHCP: %s" #: network.c:1340 msgid "try increasing /proc/sys/net/core/optmem_max" msgstr "" #: network.c:1545 #, fuzzy, c-format msgid "failed to bind server socket for %s: %s" msgstr "gagal mem-bind socket untuk mendengarkan %s: %s" # OK #: network.c:1622 #, c-format msgid "ignoring nameserver %s - local interface" msgstr "mengabaikan nameserver %s - antarmuka lokal" # OK #: network.c:1633 #, fuzzy, c-format msgid "ignoring nameserver %s - cannot make/bind socket: %s" msgstr "mengabaikan nameserver %s - tak dapat membuat/mem-bind socket: %s" #: network.c:1643 msgid "more servers are defined but not logged" msgstr "" #: network.c:1654 msgid "(no DNSSEC)" msgstr "" # OK #: network.c:1657 msgid "unqualified" msgstr "tidak memenuhi syarat" #: network.c:1657 msgid "names" msgstr "" #: network.c:1659 msgid "default" msgstr "" # OK #: network.c:1661 msgid "domain" msgstr "domain" # OK #: network.c:1663 #, fuzzy, c-format msgid "using nameserver %s#%d for %s %s%s %s" msgstr "menggunakan nameserver %s#%d untuk %s %s" # OK #: network.c:1667 #, fuzzy, c-format msgid "NOT using nameserver %s#%d - query loop detected" msgstr "menggunakan nameserver %s#%d untuk %s %s" # OK #: network.c:1670 #, fuzzy, c-format msgid "using nameserver %s#%d(via %s)" msgstr "menggunakan nameserver %s#%d" # OK #: network.c:1672 #, c-format msgid "using nameserver %s#%d" msgstr "menggunakan nameserver %s#%d" # OK #: network.c:1687 #, fuzzy, c-format msgid "using only locally-known addresses for %s" msgstr "menggunakan alamat lokal saja untuk %s %s" # OK #: network.c:1690 #, fuzzy, c-format msgid "using standard nameservers for %s" msgstr "menggunakan nameserver %s#%d untuk %s %s" # OK #: network.c:1694 #, fuzzy, c-format msgid "using %d more local addresses" msgstr "menggunakan nameserver %s#%d" # OK #: network.c:1696 #, fuzzy, c-format msgid "using %d more nameservers" msgstr "menggunakan nameserver %s#%d" #: dnsmasq.c:192 msgid "dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform" msgstr "" #: dnsmasq.c:207 msgid "no root trust anchor provided for DNSSEC" msgstr "" #: dnsmasq.c:210 msgid "cannot reduce cache size from default when DNSSEC enabled" msgstr "" # OK #: dnsmasq.c:212 #, 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:218 #, 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:225 msgid "cannot use --conntrack AND --query-port" msgstr "" # OK #: dnsmasq.c:231 #, 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:236 msgid "asynchronous logging is not available under Solaris" msgstr "" #: dnsmasq.c:241 msgid "asynchronous logging is not available under Android" msgstr "" # OK #: dnsmasq.c:246 #, 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:251 #, fuzzy msgid "loop detection not available: set HAVE_LOOP in src/config.h" msgstr "DBus tidak tersedia: setel HAVE_DBUS dalam src/config.h" # OK #: dnsmasq.c:256 #, fuzzy msgid "Ubus not available: set HAVE_UBUS in src/config.h" msgstr "DBus tidak tersedia: setel HAVE_DBUS dalam src/config.h" #: dnsmasq.c:267 msgid "max_port cannot be smaller than min_port" msgstr "" #: dnsmasq.c:271 msgid "port_limit must not be larger than available port range" msgstr "" #: dnsmasq.c:278 msgid "--auth-server required when an auth zone is defined." msgstr "" #: dnsmasq.c:283 msgid "zone serial must be configured in --auth-soa" msgstr "" #: dnsmasq.c:303 msgid "dhcp-range constructor not available on this platform" msgstr "" #: dnsmasq.c:377 msgid "cannot set --bind-interfaces and --bind-dynamic" msgstr "" # OK #: dnsmasq.c:380 #, c-format msgid "failed to find list of interfaces: %s" msgstr "gagal mendapatkan daftar antarmuka: %s" # OK #: dnsmasq.c:389 #, c-format msgid "unknown interface %s" msgstr "antarmuka tidak dikenal %s" # OK #: dnsmasq.c:396 #, fuzzy, c-format msgid "failed to set SO_BINDTODEVICE on DHCP socket: %s" msgstr "gagal menyetel SO_REUSEADDR pada socket DHCP: %s" # OK #: dnsmasq.c:440 #, fuzzy msgid "Packet dumps not available: set HAVE_DUMP in src/config.h" msgstr "DBus tidak tersedia: setel HAVE_DBUS dalam src/config.h" # OK #: dnsmasq.c:448 dnsmasq.c:1232 #, c-format msgid "DBus error: %s" msgstr "DBus error: %s" # OK #: dnsmasq.c:451 msgid "DBus not available: set HAVE_DBUS in src/config.h" msgstr "DBus tidak tersedia: setel HAVE_DBUS dalam src/config.h" # OK #: dnsmasq.c:459 dnsmasq.c:1253 #, fuzzy, c-format msgid "UBus error: %s" msgstr "DBus error: %s" # OK #: dnsmasq.c:462 #, fuzzy msgid "UBus not available: set HAVE_UBUS in src/config.h" msgstr "DBus tidak tersedia: setel HAVE_DBUS dalam src/config.h" #: dnsmasq.c:492 #, c-format msgid "unknown user or group: %s" msgstr "" #: dnsmasq.c:568 #, c-format msgid "process is missing required capability %s" msgstr "" #: dnsmasq.c:600 #, c-format msgid "cannot chdir to filesystem root: %s" msgstr "" # OK #: dnsmasq.c:852 #, fuzzy, c-format msgid "started, version %s DNS disabled" msgstr "dimulai, cache versi %s di disable" # OK #: dnsmasq.c:857 #, c-format msgid "started, version %s cachesize %d" msgstr "dimulai, versi %s ukuran cache %d" #: dnsmasq.c:859 msgid "cache size greater than 10000 may cause performance issues, and is unlikely to be useful." msgstr "" # OK #: dnsmasq.c:862 #, c-format msgid "started, version %s cache disabled" msgstr "dimulai, cache versi %s di disable" #: dnsmasq.c:865 msgid "DNS service limited to local subnets" msgstr "" # OK #: dnsmasq.c:868 #, c-format msgid "compile time options: %s" msgstr "pilihan-pilihan saat kompilasi: %s" # OK #: dnsmasq.c:877 msgid "DBus support enabled: connected to system bus" msgstr "dukungan DBus dimungkinkan: terkoneksi pada bus sistem" # OK #: dnsmasq.c:879 msgid "DBus support enabled: bus connection pending" msgstr "dukungan DBus dimungkinkan: koneksi bus ditunda" # OK #: dnsmasq.c:887 #, fuzzy msgid "UBus support enabled: connected to system bus" msgstr "dukungan DBus dimungkinkan: terkoneksi pada bus sistem" # OK #: dnsmasq.c:889 #, fuzzy msgid "UBus support enabled: bus connection pending" msgstr "dukungan DBus dimungkinkan: koneksi bus ditunda" #: dnsmasq.c:909 msgid "DNSSEC validation enabled but all unsigned answers are trusted" msgstr "" #: dnsmasq.c:911 msgid "DNSSEC validation enabled" msgstr "" #: dnsmasq.c:915 msgid "DNSSEC signature timestamps not checked until receipt of SIGINT" msgstr "" #: dnsmasq.c:918 msgid "DNSSEC signature timestamps not checked until system time valid" msgstr "" #: dnsmasq.c:921 #, c-format msgid "configured with trust anchor for %s keytag %u" msgstr "" # OK #: dnsmasq.c:927 #, fuzzy, c-format msgid "warning: failed to change owner of %s: %s" msgstr "gagal memuat nama-nama dari %s: %s" # OK #: dnsmasq.c:932 msgid "setting --bind-interfaces option because of OS limitations" msgstr "setelan opsi --bind-interfaces disebabkan keterbatasan OS" # OK #: dnsmasq.c:945 #, c-format msgid "warning: interface %s does not currently exist" msgstr "peringatan: antarmuka %s tidak ada" #: dnsmasq.c:950 msgid "warning: ignoring resolv-file flag because no-resolv is set" msgstr "" # OK #: dnsmasq.c:953 #, fuzzy msgid "warning: no upstream servers configured" msgstr "menyetel server-server di atas dengan DBus" #: dnsmasq.c:957 #, c-format msgid "asynchronous logging enabled, queue limit is %d messages" msgstr "" #: dnsmasq.c:978 msgid "IPv6 router advertisement enabled" msgstr "" #: dnsmasq.c:983 #, c-format msgid "DHCP, sockets bound exclusively to interface %s" msgstr "" #: dnsmasq.c:1000 msgid "root is " msgstr "" # OK #: dnsmasq.c:1000 #, fuzzy msgid "enabled" msgstr "di disable" #: dnsmasq.c:1002 msgid "secure mode" msgstr "" # OK #: dnsmasq.c:1003 #, fuzzy msgid "single port mode" msgstr "nomor port tidak benar" #: dnsmasq.c:1006 #, c-format msgid "warning: %s inaccessible" msgstr "" #: dnsmasq.c:1010 #, c-format msgid "warning: TFTP directory %s inaccessible" msgstr "" #: dnsmasq.c:1036 #, c-format msgid "restricting maximum simultaneous TFTP transfers to %d" msgstr "" #: dnsmasq.c:1095 #, c-format msgid "error binding DHCP socket to device %s" msgstr "" # OK #: dnsmasq.c:1229 msgid "connected to system DBus" msgstr "terhubung ke sistem DBus" # OK #: dnsmasq.c:1250 #, fuzzy msgid "connected to system UBus" msgstr "terhubung ke sistem DBus" #: dnsmasq.c:1416 #, c-format msgid "cannot fork into background: %s" msgstr "" # OK #: dnsmasq.c:1420 #, fuzzy, c-format msgid "failed to create helper: %s" msgstr "gagal membaca %s: %s" #: dnsmasq.c:1424 #, c-format msgid "setting capabilities failed: %s" msgstr "" # OK #: dnsmasq.c:1428 #, fuzzy, c-format msgid "failed to change user-id to %s: %s" msgstr "gagal memuat nama-nama dari %s: %s" # OK #: dnsmasq.c:1432 #, fuzzy, c-format msgid "failed to change group-id to %s: %s" msgstr "gagal memuat nama-nama dari %s: %s" # OK #: dnsmasq.c:1436 #, fuzzy, c-format msgid "failed to open pidfile %s: %s" msgstr "gagal membaca %s: %s" # OK #: dnsmasq.c:1440 #, fuzzy, c-format msgid "cannot open log %s: %s" msgstr "tidak bisa membuka %s:%s" # OK #: dnsmasq.c:1444 #, fuzzy, c-format msgid "failed to load Lua script: %s" msgstr "gagal memuat %S: %s" #: dnsmasq.c:1448 #, c-format msgid "TFTP directory %s inaccessible: %s" msgstr "" # OK #: dnsmasq.c:1452 #, fuzzy, c-format msgid "cannot create timestamp file %s: %s" msgstr "tidak dapat membuka atau membuat file lease: %s" #: dnsmasq.c:1536 #, c-format msgid "script process killed by signal %d" msgstr "" #: dnsmasq.c:1540 #, c-format msgid "script process exited with status %d" msgstr "" # OK #: dnsmasq.c:1544 #, fuzzy, c-format msgid "failed to execute %s: %s" msgstr "gagal mengakses %s: %s" #: dnsmasq.c:1584 msgid "now checking DNSSEC signature timestamps" msgstr "" # OK #: dnsmasq.c:1619 dnssec.c:160 dnssec.c:204 #, fuzzy, c-format msgid "failed to update mtime on %s: %s" msgstr "gagal membaca %s: %s" #: dnsmasq.c:1631 msgid "exiting on receipt of SIGTERM" msgstr "keluar karena menerima SIGTERM" # OK #: dnsmasq.c:1659 #, fuzzy, c-format msgid "failed to access %s: %s" msgstr "gagal mengakses %s: %s" # OK #: dnsmasq.c:1690 #, c-format msgid "reading %s" msgstr "membaca %s" # OK #: dnsmasq.c:1706 #, fuzzy, c-format msgid "no servers found in %s, will retry" msgstr "tidak ditemukan direktif search di %s" # OK #: dhcp.c:51 #, c-format msgid "cannot create DHCP socket: %s" msgstr "tidak bisa membuat socket DHCP: %s" # OK #: dhcp.c:66 #, c-format msgid "failed to set options on DHCP socket: %s" msgstr "gagal menyetel opsi pada socket DHCP: %s" # OK #: dhcp.c:87 #, 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:99 #, c-format msgid "failed to bind DHCP server socket: %s" msgstr "gagal mem-bind socket server DHCP: %s" # OK #: dhcp.c:125 #, c-format msgid "cannot create ICMP raw socket: %s." msgstr "tidak dapat membuat socket ICMP raw: %s" # OK #: dhcp.c:254 dhcp6.c:186 #, fuzzy, c-format msgid "unknown interface %s in bridge-interface" msgstr "antarmuka tidak dikenal %s" #: dhcp.c:295 #, c-format msgid "DHCP packet received on %s which has no address" msgstr "" #: dhcp.c:429 #, c-format msgid "ARP-cache injection failed: %s" msgstr "" #: dhcp.c:490 #, c-format msgid "Error sending DHCP packet to %s: %s" msgstr "" # OK #: dhcp.c:547 #, 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:930 #, fuzzy, c-format msgid "bad line at %s line %d" msgstr "kesalahan nama pada %s baris %d" #: dhcp.c:973 #, c-format msgid "ignoring %s line %d, duplicate name or IP address" msgstr "" # OK #: dhcp.c:1034 #, c-format msgid "read %s - %d addresses" msgstr "membaca %s - %d alamat" #: dhcp.c:1136 #, c-format msgid "Cannot broadcast DHCP relay via interface %s" msgstr "" #: dhcp.c:1160 #, c-format msgid "broadcast via %s" msgstr "" #: dhcp.c:1163 rfc3315.c:2219 #, c-format msgid "DHCP relay at %s -> %s" msgstr "" #: lease.c:64 #, c-format msgid "ignoring invalid line in lease database: %s %s %s %s ..." msgstr "" #: lease.c:101 #, c-format msgid "ignoring invalid line in lease database, bad address: %s" msgstr "" # OK #: lease.c:108 msgid "too many stored leases" msgstr "terlalu banyak lease yang disimpan" # OK #: lease.c:176 #, fuzzy, c-format msgid "cannot open or create lease file %s: %s" msgstr "tidak dapat membuka atau membuat file lease: %s" # OK #: lease.c:185 #, fuzzy msgid "failed to parse lease database cleanly" msgstr "gagal membaca %s: %s" # OK #: lease.c:188 #, fuzzy, c-format msgid "failed to read lease file %s: %s" msgstr "gagal membaca %s: %s" # OK #: lease.c:204 #, fuzzy, c-format msgid "cannot run lease-init script %s: %s" msgstr "tidak bisa membaca %s: %s" #: lease.c:210 #, c-format msgid "lease-init script returned exit code %s" msgstr "" # OK #: lease.c:381 #, fuzzy, c-format msgid "failed to write %s: %s (retry in %u s)" msgstr "gagal membaca %s: %s" #: lease.c:955 #, c-format msgid "Ignoring domain %s for DHCP host name %s" msgstr "" # OK #: rfc2131.c:378 msgid "with subnet selector" msgstr "dengan pemilih subnet" # OK #: rfc2131.c:383 msgid "via" msgstr "lewat" # OK #: rfc2131.c:389 #, 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:403 #, fuzzy, c-format msgid "%u available DHCP subnet: %s/%s" msgstr "tidak ada alamat yang bisa dipakai untuk permintaan DHCP %s %s" #: rfc2131.c:409 rfc3315.c:320 #, c-format msgid "%u available DHCP range: %s -- %s" msgstr "" # OK #: rfc2131.c:521 #, fuzzy, c-format msgid "%u vendor class: %s" msgstr "DBus error: %s" # OK #: rfc2131.c:523 #, fuzzy, c-format msgid "%u user class: %s" msgstr "DBus error: %s" # OK #: rfc2131.c:557 msgid "disabled" msgstr "di disable" # OK #: rfc2131.c:598 rfc2131.c:1087 rfc2131.c:1536 rfc3315.c:633 rfc3315.c:816 #: rfc3315.c:1122 msgid "ignored" msgstr "diabaikan" # OK #: rfc2131.c:613 rfc2131.c:1340 rfc3315.c:868 msgid "address in use" msgstr "alamat telah digunakan" # OK #: rfc2131.c:627 rfc2131.c:1141 msgid "no address available" msgstr "tak ada alamat yang tersedia" # OK #: rfc2131.c:634 rfc2131.c:1302 msgid "wrong network" msgstr "jaringan yang salah" # OK #: rfc2131.c:649 msgid "no address configured" msgstr "tak ada alamat yang disetel" # OK #: rfc2131.c:655 rfc2131.c:1353 msgid "no leases left" msgstr "tak ada lease yang tersisa" #: rfc2131.c:756 rfc3315.c:500 #, c-format msgid "%u client provides name: %s" msgstr "" #: rfc2131.c:885 msgid "PXE BIS not supported" msgstr "" # OK #: rfc2131.c:1054 rfc3315.c:1223 #, fuzzy, c-format msgid "disabling DHCP static address %s for %s" msgstr "men-disable alamat statik DHCP %s" # OK #: rfc2131.c:1075 msgid "unknown lease" msgstr "lease tidak diketahui" #: rfc2131.c:1110 #, c-format msgid "not using configured address %s because it is leased to %s" msgstr "" #: rfc2131.c:1120 #, c-format msgid "not using configured address %s because it is in use by the server or relay" msgstr "" #: rfc2131.c:1123 #, c-format msgid "not using configured address %s because it was previously declined" msgstr "" #: rfc2131.c:1139 rfc2131.c:1346 msgid "no unique-id" msgstr "" #: rfc2131.c:1238 msgid "wrong server-ID" msgstr "" # OK #: rfc2131.c:1257 msgid "wrong address" msgstr "alamat salah" # OK #: rfc2131.c:1275 rfc3315.c:976 msgid "lease not found" msgstr "lease tak ditemukan" # OK #: rfc2131.c:1310 msgid "address not available" msgstr "alamat tak tersedia" # OK #: rfc2131.c:1321 msgid "static lease available" msgstr "lease statik tak tersedia" # OK #: rfc2131.c:1325 msgid "address reserved" msgstr "alamat telah dipesan" #: rfc2131.c:1334 #, c-format msgid "abandoning lease to %s of %s" msgstr "" #: rfc2131.c:1870 #, c-format msgid "%u bootfile name: %s" msgstr "" # OK #: rfc2131.c:1879 #, fuzzy, c-format msgid "%u server name: %s" msgstr "DBus error: %s" # OK #: rfc2131.c:1889 #, fuzzy, c-format msgid "%u next server: %s" msgstr "DBus error: %s" #: rfc2131.c:1893 #, c-format msgid "%u broadcast response" msgstr "" #: rfc2131.c:1956 #, c-format msgid "cannot send DHCP/BOOTP option %d: no space left in packet" msgstr "" #: rfc2131.c:2267 msgid "PXE menu too large" msgstr "" # OK #: rfc2131.c:2430 rfc3315.c:1517 #, fuzzy, c-format msgid "%u requested options: %s" msgstr "pilihan-pilihan saat kompilasi: %s" #: rfc2131.c:2747 #, c-format msgid "cannot send RFC3925 option: too many options for enterprise number %d" msgstr "" #: rfc2131.c:2810 #, c-format msgid "%u reply delay: %d" msgstr "" # OK #: netlink.c:86 #, fuzzy, c-format msgid "cannot create netlink socket: %s" msgstr "tidak bisa mem-bind netlink socket: %s" # OK #: netlink.c:379 #, fuzzy, c-format msgid "netlink returns error: %s" msgstr "DBus error: %s" #: dbus.c:491 #, c-format msgid "Enabling --%s option from D-Bus" msgstr "" #: dbus.c:496 #, c-format msgid "Disabling --%s option from D-Bus" msgstr "" # OK #: dbus.c:857 msgid "setting upstream servers from DBus" msgstr "menyetel server-server di atas dengan DBus" # OK #: dbus.c:907 msgid "could not register a DBus message handler" msgstr "tidak bisa mendaftar sebuah DBus message handler" # OK #: bpf.c:261 #, c-format msgid "cannot create DHCP BPF socket: %s" msgstr "tidak dapat membuat socket DHCP BPF: %s" # OK #: bpf.c:289 #, 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" # OK #: bpf.c:374 #, fuzzy, c-format msgid "cannot create PF_ROUTE socket: %s" msgstr "tidak bisa membuat socket DHCP: %s" #: bpf.c:395 msgid "Unknown protocol version from route socket" msgstr "" #: helper.c:150 msgid "lease() function missing in Lua script" msgstr "" #: tftp.c:353 msgid "unable to get free port for TFTP" msgstr "" #: tftp.c:369 #, c-format msgid "unsupported request from %s" msgstr "" # OK #: tftp.c:520 #, fuzzy, c-format msgid "file %s not found for %s" msgstr "file %s tidak ditemukan untuk %s" #: tftp.c:609 #, c-format msgid "ignoring packet from %s (TID mismatch)" msgstr "" # OK #: tftp.c:662 #, fuzzy, c-format msgid "failed sending %s to %s" msgstr "gagal membaca %s: %s" #: tftp.c:662 #, c-format msgid "sent %s to %s" msgstr "" #: tftp.c:712 #, c-format msgid "error %d %s received from %s" msgstr "" #: log.c:203 #, c-format msgid "overflow: %d log entries lost" msgstr "" #: log.c:281 #, c-format msgid "log failed: %s" msgstr "" # OK #: log.c:490 msgid "FAILED to start up" msgstr "GAGAL untuk memulai" #: conntrack.c:63 #, c-format msgid "Conntrack connection mark retrieval failed: %s" msgstr "" # OK #: dhcp6.c:51 #, fuzzy, c-format msgid "cannot create DHCPv6 socket: %s" msgstr "tidak bisa membuat socket DHCP: %s" # OK #: dhcp6.c:72 #, fuzzy, c-format msgid "failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s" msgstr "gagal menyetel SO_REUSEADDR pada socket DHCP: %s" # OK #: dhcp6.c:84 #, fuzzy, c-format msgid "failed to bind DHCPv6 server socket: %s" msgstr "gagal mem-bind socket server DHCP: %s" # OK #: rfc3315.c:173 #, 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:182 #, 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:317 #, 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:400 #, fuzzy, c-format msgid "%u vendor class: %u" msgstr "DBus error: %s" # OK #: rfc3315.c:448 #, fuzzy, c-format msgid "%u client MAC address: %s" msgstr "tidak ada antarmuka dengan alamat %s" # OK #: rfc3315.c:763 rfc3315.c:860 #, fuzzy msgid "address unavailable" msgstr "alamat tak tersedia" #: rfc3315.c:775 rfc3315.c:904 rfc3315.c:1273 msgid "success" msgstr "" # OK #: rfc3315.c:790 rfc3315.c:799 rfc3315.c:912 rfc3315.c:914 rfc3315.c:1048 #, fuzzy msgid "no addresses available" msgstr "tak ada alamat yang tersedia" #: rfc3315.c:891 msgid "not on link" msgstr "" #: rfc3315.c:980 rfc3315.c:1181 rfc3315.c:1262 msgid "no binding found" msgstr "" #: rfc3315.c:1017 msgid "deprecated" msgstr "" # OK #: rfc3315.c:1024 #, fuzzy msgid "address invalid" msgstr "alamat telah digunakan" #: rfc3315.c:1082 rfc3315.c:1084 msgid "confirm failed" msgstr "" # OK #: rfc3315.c:1099 #, fuzzy msgid "all addresses still on link" msgstr "kesalahan nama pada %s baris %d" #: rfc3315.c:1190 msgid "release received" msgstr "" #: rfc3315.c:2200 #, c-format msgid "Cannot multicast DHCP relay via interface %s" msgstr "" #: rfc3315.c:2216 #, c-format msgid "multicast via %s" msgstr "" #: dhcp-common.c:187 #, c-format msgid "Ignoring duplicate dhcp-option %d" msgstr "" #: dhcp-common.c:264 #, c-format msgid "%u tags: %s" msgstr "" #: dhcp-common.c:484 #, c-format msgid "%s has more than one address in hostsfile, using %s for DHCP" msgstr "" # OK #: dhcp-common.c:518 #, c-format msgid "duplicate IP address %s (%s) in dhcp-config directive" msgstr "alamat IP kembar %s (%s) dalam direktif dhcp-config" #: dhcp-common.c:738 #, c-format msgid "Known DHCP options:\n" msgstr "" #: dhcp-common.c:749 #, c-format msgid "Known DHCPv6 options:\n" msgstr "" #: dhcp-common.c:946 msgid ", prefix deprecated" msgstr "" #: dhcp-common.c:949 #, c-format msgid ", lease time " msgstr "" #: dhcp-common.c:991 #, c-format msgid "%s stateless on %s%.0s%.0s%s" msgstr "" # OK #: dhcp-common.c:993 #, 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:995 #, c-format msgid "%s, proxy on subnet %.0s%s%.0s%.0s" msgstr "" # OK #: dhcp-common.c:996 #, fuzzy, c-format msgid "%s, IP range %s -- %s%s%.0s" msgstr "DHCP, jangkaun IP %s -- %s, waktu lease %s" #: dhcp-common.c:1009 #, c-format msgid "DHCPv4-derived IPv6 names on %s%s" msgstr "" # OK #: dhcp-common.c:1012 #, fuzzy, c-format msgid "router advertisement on %s%s" msgstr "DHCP, lease static pada %.0s%s, waktu lease %s" #: dhcp-common.c:1043 #, c-format msgid "DHCP relay from %s via %s" msgstr "" #: dhcp-common.c:1045 #, c-format msgid "DHCP relay from %s to %s via %s" msgstr "" #: dhcp-common.c:1048 #, c-format msgid "DHCP relay from %s to %s" msgstr "" # OK #: radv.c:110 #, fuzzy, c-format msgid "cannot create ICMPv6 socket: %s" msgstr "tidak bisa membuat socket DHCP: %s" #: auth.c:462 #, c-format msgid "ignoring zone transfer request from %s" msgstr "" # OK #: ipset.c:99 #, fuzzy, c-format msgid "failed to create IPset control socket: %s" msgstr "gagal membuat socket: %s " # OK #: ipset.c:211 #, fuzzy, c-format msgid "failed to update ipset %s: %s" msgstr "gagal membaca %s: %s" #: pattern.c:29 #, c-format msgid "[pattern.c:%d] Assertion failure: %s" msgstr "" #: pattern.c:142 #, c-format msgid "Invalid DNS name: Invalid character %c." msgstr "" #: pattern.c:151 msgid "Invalid DNS name: Empty label." msgstr "" #: pattern.c:156 msgid "Invalid DNS name: Label starts with hyphen." msgstr "" #: pattern.c:170 msgid "Invalid DNS name: Label ends with hyphen." msgstr "" #: pattern.c:176 #, c-format msgid "Invalid DNS name: Label is too long (%zu)." msgstr "" #: pattern.c:184 #, c-format msgid "Invalid DNS name: Not enough labels (%zu)." msgstr "" #: pattern.c:189 msgid "Invalid DNS name: Final label is fully numeric." msgstr "" #: pattern.c:199 msgid "Invalid DNS name: \"local\" pseudo-TLD." msgstr "" #: pattern.c:204 #, c-format msgid "DNS name has invalid length (%zu)." msgstr "" #: pattern.c:258 #, c-format msgid "Invalid DNS name pattern: Invalid character %c." msgstr "" #: pattern.c:267 msgid "Invalid DNS name pattern: Empty label." msgstr "" #: pattern.c:272 msgid "Invalid DNS name pattern: Label starts with hyphen." msgstr "" #: pattern.c:285 msgid "Invalid DNS name pattern: Wildcard character used more than twice per label." msgstr "" #: pattern.c:295 msgid "Invalid DNS name pattern: Label ends with hyphen." msgstr "" #: pattern.c:301 #, c-format msgid "Invalid DNS name pattern: Label is too long (%zu)." msgstr "" #: pattern.c:309 #, c-format msgid "Invalid DNS name pattern: Not enough labels (%zu)." msgstr "" #: pattern.c:314 msgid "Invalid DNS name pattern: Wildcard within final two labels." msgstr "" #: pattern.c:319 msgid "Invalid DNS name pattern: Final label is fully numeric." msgstr "" #: pattern.c:329 msgid "Invalid DNS name pattern: \"local\" pseudo-TLD." msgstr "" #: pattern.c:334 #, c-format msgid "DNS name pattern has invalid length after removing wildcards (%zu)." msgstr "" #: dnssec.c:206 msgid "system time considered valid, now checking DNSSEC signature timestamps." msgstr "" #: dnssec.c:1018 #, c-format msgid "Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support" msgstr "" #: blockdata.c:55 #, c-format msgid "pool memory in use %zu, max %zu, allocated %zu" msgstr "" # OK #: tables.c:61 #, fuzzy, c-format msgid "failed to access pf devices: %s" msgstr "gagal mengakses %s: %s" # OK #: tables.c:74 #, fuzzy, c-format msgid "warning: no opened pf devices %s" msgstr "menggunakan alamat lokal saja untuk %s %s" # OK #: tables.c:82 #, fuzzy, c-format msgid "error: cannot use table name %s" msgstr "tidak bisa mendapatkan host-name: %s" #: tables.c:90 #, c-format msgid "error: cannot strlcpy table name %s" msgstr "" # OK #: tables.c:101 #, fuzzy, c-format msgid "IPset: error: %s" msgstr "DBus error: %s" #: tables.c:108 msgid "info: table created" msgstr "" #: tables.c:133 #, c-format msgid "warning: DIOCR%sADDRS: %s" msgstr "" # OK #: tables.c:137 #, fuzzy, c-format msgid "%d addresses %s" msgstr "membaca %s - %d alamat" # OK #: inotify.c:62 #, fuzzy, c-format msgid "cannot access path %s: %s" msgstr "tidak bisa membaca %s: %s" # OK #: inotify.c:95 #, fuzzy, c-format msgid "failed to create inotify: %s" msgstr "gagal membaca %s: %s" #: inotify.c:111 #, c-format msgid "too many symlinks following %s" msgstr "" #: inotify.c:127 #, c-format msgid "directory %s for resolv-file is missing, cannot poll" msgstr "" # OK #: inotify.c:131 inotify.c:200 #, fuzzy, c-format msgid "failed to create inotify for %s: %s" msgstr "gagal membuat socket: %s " # OK #: inotify.c:178 inotify.c:185 #, fuzzy, c-format msgid "bad dynamic directory %s: %s" msgstr "tidak bisa membaca %s: %s" # OK #: inotify.c:186 #, fuzzy msgid "not a directory" msgstr "tidak bisa membaca %s: %s" #: inotify.c:299 #, c-format msgid "inotify: %s removed" msgstr "" #: inotify.c:301 #, c-format msgid "inotify: %s new or modified" msgstr "" #: inotify.c:309 #, c-format msgid "inotify: flushed %u names read from %s" msgstr "" # OK #: dump.c:68 #, fuzzy, c-format msgid "cannot create %s: %s" msgstr "tidak bisa membaca %s: %s" # OK #: dump.c:74 #, fuzzy, c-format msgid "bad header in %s" msgstr "alamat telah digunakan" # OK #: dump.c:287 #, fuzzy msgid "failed to write packet dump" msgstr "gagal mendengarkan di socket: %s" #: dump.c:289 #, c-format msgid "%u dumping packet %u mask 0x%04x" msgstr "" #: dump.c:291 #, c-format msgid "dumping packet %u mask 0x%04x" msgstr "" #: ubus.c:79 #, c-format msgid "UBus subscription callback: %s subscriber(s)" msgstr "" # OK #: ubus.c:99 #, fuzzy, c-format msgid "Cannot reconnect to UBus: %s" msgstr "tidak bisa membuka %s:%s" #: ubus.c:135 msgid "Cannot set UBus listeners: no connection" msgstr "" #: ubus.c:155 msgid "Cannot poll UBus listeners: no connection" msgstr "" #: ubus.c:168 msgid "Disconnecting from UBus" msgstr "" #: ubus.c:179 ubus.c:326 #, c-format msgid "UBus command failed: %d (%s)" msgstr "" #: hash-questions.c:40 msgid "Failed to create SHA-256 hash object" msgstr "" # OK #: nftset.c:35 #, fuzzy msgid "failed to create nftset context" msgstr "gagal membuat socket: %s " # OK #, fuzzy #~ msgid "bad IPv4 prefix" #~ msgstr "port salah" # OK #, fuzzy #~ msgid "Cannot add object to UBus: %s" #~ msgstr "tidak bisa membuka %s:%s" # OK #, fuzzy #~ msgid "Failed to send UBus event: %s" #~ msgstr "gagal mendengarkan di socket: %s" # OK #~ 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" # OK #, fuzzy #~ msgid "unknown prefix-class %d" #~ msgstr "lease tidak diketahui" # 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 "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 "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" #~ msgstr "Mengabaikan lease DHCP untuk %s sebab terdapat bagian domain yang tidak sah" # OK #~ msgid "ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h" #~ msgstr "Integrasi dengan dhcpd ISC tidak tersedia: atur HAVE_ISC_READER dalam src/config.h" # OK #, fuzzy #~ msgid "illegal domain %s in dhcp-config directive." #~ msgstr "alamat IP kembar %s dalam direktif dhcp-config" # OK #~ msgid "running as root" #~ msgstr "berjalan menggunakan root" # OK #, fuzzy #~ msgid "read %s - %d hosts" #~ msgstr "membaca %s - %d alamat" # OK #~ msgid "domains" #~ msgstr "domain-domain" # OK #~ msgid "Ignoring DHCP host name %s because it has an illegal domain part" #~ msgstr "Mengabaikan nama host DHCP %s sebab memiliki bagian domain yang tidak sah" # OK #~ msgid "Display this message." #~ msgstr "Menampilkan pesan ini." # OK #~ msgid "failed to read %s: %m" #~ msgstr "gagal membaca %s: %m" # OK #~ msgid "failed to read %s:%m" #~ msgstr "gagal membaca %s:%m" # OK #~ msgid "More than one vendor class matches, using %s" #~ msgstr "Lebih dari satu kelas vendor yang sesuai, menggunakan %s" # OK #~ msgid "forwarding table overflow: check for server loops." #~ msgstr "meneruskan tabel overflow: memeriksa apakah terjadi loop server." # OK #~ msgid "nested includes not allowed" #~ msgstr "includes bersarang tidak diijinkan" # OK #~ msgid "DHCP, %s will be written every %s" #~ msgstr "DHCP, %s akan ditulis setiap %s" # OK #~ msgid "cannot create DHCP packet socket: %s. Is CONFIG_PACKET enabled in your kernel?" #~ msgstr "tidak dapat membuat socket packet DHCP: %s. Apakah CONFIG_PACKET dimungkinkan pada kernel?" dnsmasq-2.91/COPYING0000664000175000017500000004325414765043257012355 0ustar srksrk GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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; either version 2 of the License, or (at your option) any later version. 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. dnsmasq-2.91/man/0000775000175000017500000000000014765043257012065 5ustar srksrkdnsmasq-2.91/man/dnsmasq.80000664000175000017500000041504614765043257013636 0ustar srksrk.TH DNSMASQ 8 2025-02-05 .SH NAME dnsmasq \- A lightweight DHCP and caching DNS server. .SH SYNOPSIS .B dnsmasq .I [OPTION]... .SH "DESCRIPTION" .BR dnsmasq 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. 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. 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 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 naming 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 unneeded 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 BSD, unless the GNU getopt library is linked, the long form of the options does not work on the command line; it is still recognised in the configuration file. .TP .B --test Read and syntax check configuration file(s). Exit with code 0 if all is OK, or a non-zero code otherwise. Do not start up dnsmasq. .TP .B \-w, --help Display all command-line options. .B --help dhcp will display known DHCPv4 configuration options, and .B --help dhcp6 will display DHCPv6 options. .TP .B \-h, --no-hosts Don't read the hostnames in /etc/hosts. .TP .B \-H, --addn-hosts= Additional hosts file. Read the specified file as well as /etc/hosts. If \fB--no-hosts\fP is given, read 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 in alphabetical order. .TP .B --hostsdir= Read all the hosts files contained in the directory. New or changed files are read automatically and modified and deleted files have removed records automatically deleted. .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 apply to domain names in cnames, PTR records, TXT records etc. .TP .B \-T, --local-ttl=