--- embedaddon/dnsmasq/src/option.c 2014/06/15 16:31:38 1.1.1.2 +++ embedaddon/dnsmasq/src/option.c 2021/03/17 00:56:46 1.1.1.4 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2021 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 @@ -129,9 +129,6 @@ struct myoption { #define LOPT_AUTHPEER 318 #define LOPT_IPSET 319 #define LOPT_SYNTH 320 -#ifdef OPTION6_PREFIX_CLASS -#define LOPT_PREF_CLSS 321 -#endif #define LOPT_RELAY 323 #define LOPT_RA_PARAM 324 #define LOPT_ADD_SBNET 325 @@ -146,7 +143,32 @@ struct myoption { #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 + #ifdef HAVE_GETOPT_LONG static const struct option opts[] = #else @@ -158,7 +180,7 @@ static const struct myoption opts[] = { "no-poll", 0, 0, 'n' }, { "help", 0, 0, 'w' }, { "no-daemon", 0, 0, 'd' }, - { "log-queries", 0, 0, 'q' }, + { "log-queries", 2, 0, 'q' }, { "user", 2, 0, 'u' }, { "group", 2, 0, 'g' }, { "resolv-file", 2, 0, 'r' }, @@ -180,6 +202,7 @@ static const struct myoption opts[] = { "local-service", 0, 0, LOPT_LOCAL_SERVICE }, { "bogus-priv", 0, 0, 'b' }, { "bogus-nxdomain", 1, 0, 'B' }, + { "ignore-address", 1, 0, LOPT_IGNORE_ADDR }, { "selfmx", 0, 0, 'e' }, { "filterwin2k", 0, 0, 'f' }, { "pid-file", 2, 0, 'x' }, @@ -195,6 +218,7 @@ static const struct myoption opts[] = { "local-ttl", 1, 0, 'T' }, { "no-negcache", 0, 0, 'N' }, { "addn-hosts", 1, 0, 'H' }, + { "hostsdir", 1, 0, LOPT_HOST_INOTIFY }, { "query-port", 1, 0, 'Q' }, { "except-interface", 1, 0, 'I' }, { "no-dhcp-interface", 1, 0, '2' }, @@ -212,8 +236,10 @@ static const struct myoption opts[] = { "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' }, @@ -221,18 +247,23 @@ static const struct myoption opts[] = { "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-unique-root", 0, 0, LOPT_APREF }, + { "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 }, { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK }, { "log-dhcp", 0, 0, LOPT_LOG_OPTS }, @@ -240,22 +271,28 @@ static const struct myoption opts[] = { "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-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 }, @@ -265,8 +302,9 @@ static const struct myoption opts[] = { "dhcp-proxy", 2, 0, LOPT_PROXY }, { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES }, { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND }, - { "add-mac", 0, 0, LOPT_ADD_MAC }, + { "add-mac", 2, 0, LOPT_ADD_MAC }, { "add-subnet", 2, 0, LOPT_ADD_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 }, @@ -287,16 +325,22 @@ static const struct myoption opts[] = { "dnssec", 0, 0, LOPT_SEC_VALID }, { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR }, { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG }, - { "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK }, + { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK }, { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME }, -#ifdef OPTION6_PREFIX_CLASS - { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS }, -#endif + { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP }, { "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 }, { NULL, 0, 0, 0 } }; @@ -329,15 +373,19 @@ static struct { { '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 }, @@ -353,7 +401,7 @@ static struct { { LOPT_FORCE, ARG_DUP, "", gettext_noop("DHCP option sent even if the client does not request it."), 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', OPT_LOG, NULL, gettext_noop("Log DNS queries."), NULL }, + { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL }, { 'Q', ARG_ONE, "", gettext_noop("Force the originating port for upstream DNS queries."), NULL }, { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL }, { 'r', ARG_DUP, "", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE }, @@ -366,12 +414,14 @@ static struct { { '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 }, { '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 for known DHCP options."), 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 }, @@ -381,16 +431,19 @@ static struct { { '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 }, { '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 }, - { '8', ARG_ONE, "|", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), 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 }, @@ -398,12 +451,15 @@ static struct { { 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, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), 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_MAX, ARG_ONE, "", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" }, + { LOPT_TFTP_NO_FAIL, OPT_TFTP_NO_FAIL, NULL, gettext_noop("Do not terminate the service if TFTP directories are inaccessible."), NULL }, + { LOPT_TFTP_MAX, ARG_ONE, "", 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 }, @@ -411,53 +467,64 @@ static struct { { 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_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, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL }, - { LOPT_ADD_SBNET, ARG_ONE, "[,]", gettext_noop("Add requestor's IP subnet to forwarded DNS queries."), NULL }, + { LOPT_ADD_MAC, ARG_DUP, "[=base64|text]", gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL }, + { LOPT_ADD_SBNET, ARG_ONE, "[,]", gettext_noop("Add specified IP subnet to forwarded DNS 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_HOST_REC, ARG_DUP, ",
[,]", gettext_noop("Specify host (A/AAAA and PTR) records"), 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 authoritive zone information"), 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_IPSET, ARG_DUP, "/[/...]/...", gettext_noop("Specify ipsets to which matching domains should be added"), 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, OPT_DNSSEC_NO_SIGN, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), 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 }, -#ifdef OPTION6_PREFIX_CLASS - { LOPT_PREF_CLSS, ARG_DUP, "set:tag,", gettext_noop("Specify DHCPv6 prefix class"), NULL }, -#endif - { LOPT_RA_PARAM, ARG_DUP, ",[high,|low,][,]", gettext_noop("Set priority, resend-interval and router-lifetime"), NULL }, + { LOPT_DNSSEC_STAMP, ARG_ONE, "", gettext_noop("Timestamp file to verify system clock for DNSSEC"), 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_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks"), NULL }, + { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL }, + { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL }, + { LOPT_IGNORE_ADDR, ARG_DUP, "", 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 }, { 0, 0, NULL, NULL, NULL } }; -/* We hide metacharaters in quoted strings by mapping them into the ASCII control +/* 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. @@ -516,14 +583,15 @@ static void *opt_malloc(size_t size) return ret; } -static char *opt_string_alloc(char *cp) +static char *opt_string_alloc(const char *cp) { char *ret = NULL; + size_t len; - if (cp && strlen(cp) != 0) + if (cp && (len = strlen(cp)) != 0) { - ret = opt_malloc(strlen(cp)+1); - strcpy(ret, cp); + ret = opt_malloc(len+1); + memcpy(ret, cp, len+1); /* restore hidden metachars */ unhide_metas(ret); @@ -617,7 +685,8 @@ static int atoi_check8(char *a, int *res) 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)); @@ -630,13 +699,14 @@ static void add_txt(char *name, char *txt, int stat) *(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) { @@ -681,7 +751,7 @@ static void do_usage(void) sprintf(buff, " "); sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc); - printf("%-40.40s", buff); + printf("%-55.55s", buff); if (usage[i].arg) { @@ -696,15 +766,28 @@ static void do_usage(void) } #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, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags) { int source_port = 0, serv_port = NAMESERVER_PORT; char *portno, *source; -#ifdef HAVE_IPV6 + char *interface_opt = NULL; int scope_index = 0; char *scope_id; -#endif if (!arg || strlen(arg) == 0) { @@ -722,10 +805,21 @@ char *parse_server(char *arg, union mysockaddr *addr, !atoi_check16(portno, &serv_port)) return _("bad port"); -#ifdef HAVE_IPV6 scope_id = split_chr(arg, '%'); -#endif + if (source) { + interface_opt = split_chr(source, '@'); + + if (interface_opt) + { +#if defined(SO_BINDTODEVICE) + safe_strncpy(interface, interface_opt, IF_NAMESIZE); +#else + return _("interface binding not supported"); +#endif + } + } + if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0) { addr->in.sin_port = htons(serv_port); @@ -744,15 +838,17 @@ char *parse_server(char *arg, union mysockaddr *addr, if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0)) { #if defined(SO_BINDTODEVICE) + if (interface_opt) + return _("interface can only be specified once"); + source_addr->in.sin_addr.s_addr = INADDR_ANY; - strncpy(interface, source, IF_NAMESIZE - 1); + safe_strncpy(interface, source, IF_NAMESIZE); #else return _("interface binding not supported"); #endif } } } -#ifdef HAVE_IPV6 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0) { if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0) @@ -776,15 +872,17 @@ char *parse_server(char *arg, union mysockaddr *addr, if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0) { #if defined(SO_BINDTODEVICE) - source_addr->in6.sin6_addr = in6addr_any; - strncpy(interface, source, IF_NAMESIZE - 1); + if (interface_opt) + return _("interface can only be specified once"); + + source_addr->in6.sin6_addr = in6addr_any; + safe_strncpy(interface, source, IF_NAMESIZE); #else return _("interface binding not supported"); #endif } } } -#endif else return _("bad address"); @@ -794,20 +892,34 @@ char *parse_server(char *arg, union mysockaddr *addr, static struct server *add_rev4(struct in_addr addr, int msize) { struct server *serv = opt_malloc(sizeof(struct server)); - in_addr_t a = ntohl(addr.s_addr) >> 8; + in_addr_t a = ntohl(addr.s_addr); char *p; memset(serv, 0, sizeof(struct server)); - p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */ + p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */ + + switch (msize) + { + case 32: + p += sprintf(p, "%u.", a & 0xff); + /* fall through */ + case 24: + p += sprintf(p, "%d.", (a >> 8) & 0xff); + /* fall through */ + case 16: + p += sprintf(p, "%d.", (a >> 16) & 0xff); + /* fall through */ + case 8: + p += sprintf(p, "%d.", (a >> 24) & 0xff); + break; + default: + free(serv->domain); + free(serv); + return NULL; + } + + p += sprintf(p, "in-addr.arpa"); - if (msize == 24) - p += sprintf(p, "%d.", a & 0xff); - a = a >> 8; - if (msize != 8) - p += sprintf(p, "%d.", a & 0xff); - a = a >> 8; - p += sprintf(p, "%d.in-addr.arpa", a & 0xff); - serv->flags = SERV_HAS_DOMAIN; serv->next = daemon->servers; daemon->servers = serv; @@ -857,6 +969,116 @@ static char *set_prefix(char *arg) 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; + dhcp_netid_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); + +#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) { @@ -864,7 +1086,6 @@ static int parse_dhcp_opt(char *errstr, char *arg, int char lenchar = 0, *cp; int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots; char *comma = NULL; - struct dhcp_netid *np = NULL; u16 opt_len = 0; int is6 = 0; int option_ok = 0; @@ -951,14 +1172,9 @@ static int parse_dhcp_opt(char *errstr, char *arg, int } else { - new->netid = opt_malloc(sizeof (struct dhcp_netid)); /* allow optional "net:" or "tag:" for consistency */ - if (is_tag_prefix(arg)) - new->netid->net = opt_string_alloc(arg+4); - else - new->netid->net = opt_string_alloc(set_prefix(arg)); - new->netid->next = np; - np = new->netid; + const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg); + new->netid = dhcp_netid_create(name, new->netid); } arg = comma; @@ -968,7 +1184,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int if (is6) { if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE)) - ret_err(_("unsupported encapsulation for IPv6 option")); + goto_err(_("unsupported encapsulation for IPv6 option")); if (opt_len == 0 && !(new->flags & DHOPT_RFC3925)) @@ -982,13 +1198,13 @@ static int parse_dhcp_opt(char *errstr, char *arg, int /* option may be missing with rfc3925 match */ if (!option_ok) - ret_err(_("bad dhcp-option")); + goto_err(_("bad dhcp-option")); if (comma) { /* characterise the value */ char c; - int found_dig = 0; + int found_dig = 0, found_colon = 0; is_addr = is_addr6 = is_hex = is_dec = is_string = 1; addrs = digs = 1; dots = 0; @@ -1002,6 +1218,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int { digs++; is_dec = is_addr = 0; + found_colon = 1; } else if (c == '/') { @@ -1011,7 +1228,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int } else if (c == '.') { - is_addr6 = is_dec = is_hex = 0; + is_dec = is_hex = 0; dots++; } else if (c == '-') @@ -1043,6 +1260,15 @@ static int parse_dhcp_opt(char *errstr, char *arg, int 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) @@ -1050,10 +1276,10 @@ static int parse_dhcp_opt(char *errstr, char *arg, int is_string = is_dec = is_hex = 0; if (!is6 && (!is_addr || dots == 0)) - ret_err(_("bad IP address")); + goto_err(_("bad IP address")); if (is6 && !is_addr6) - ret_err(_("bad IPv6 address")); + goto_err(_("bad IPv6 address")); } /* or names */ else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING)) @@ -1072,7 +1298,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int case 'd': case 'D': fac *= 24; - /* fall though */ + /* fall through */ case 'h': case 'H': fac *= 60; @@ -1145,7 +1371,8 @@ static int parse_dhcp_opt(char *errstr, char *arg, int cp = comma; comma = split(cp); slash = split_chr(cp, '/'); - inet_pton(AF_INET, cp, &in); + if (!inet_pton(AF_INET, cp, &in)) + goto_err(_("bad IPv4 address")); if (!slash) { memcpy(op, &in, INADDRSZ); @@ -1190,8 +1417,8 @@ static int parse_dhcp_opt(char *errstr, char *arg, int op += IN6ADDRSZ; continue; } - - ret_err(_("bad IPv6 address")); + + goto_err(_("bad IPv6 address")); } new->len = op - new->val; } @@ -1218,7 +1445,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int if (strcmp (arg, ".") != 0) { if (!(dom = canonicalise_opt(arg))) - ret_err(_("bad domain in dhcp-option")); + goto_err(_("bad domain in dhcp-option")); domlen = strlen(dom) + 2; } @@ -1265,7 +1492,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int } /* RFC 3361, enc byte is zero for names */ - if (new->opt == OPTION_SIP_SERVER) + if (new->opt == OPTION_SIP_SERVER && m) m[0] = 0; new->len = (int) len + header_size; new->val = m; @@ -1303,8 +1530,9 @@ static int parse_dhcp_opt(char *errstr, char *arg, int } else if (comma && (opt_len & OT_RFC1035_NAME)) { - unsigned char *p = NULL, *newp, *end; + 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); @@ -1312,9 +1540,9 @@ static int parse_dhcp_opt(char *errstr, char *arg, int { char *dom = canonicalise_opt(arg); if (!dom) - ret_err(_("bad domain in dhcp-option")); + goto_err(_("bad domain in dhcp-option")); - newp = opt_malloc(len + strlen(dom) + 2); + newp = opt_malloc(len + header_size + strlen(dom) + 2); if (p) { @@ -1323,8 +1551,14 @@ static int parse_dhcp_opt(char *errstr, char *arg, int } p = newp; - end = do_rfc1035_name(p + len, dom); + 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); @@ -1350,14 +1584,14 @@ static int parse_dhcp_opt(char *errstr, char *arg, int ((new->len > 255) || (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) || (new->len > 250 && (new->flags & DHOPT_RFC3925)))) - ret_err(_("dhcp-option too long")); + goto_err(_("dhcp-option too long")); if (flags == DHOPT_MATCH) { if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) || !new->netid || new->netid->next) - ret_err(_("illegal dhcp-match")); + goto_err(_("illegal dhcp-match")); if (is6) { @@ -1382,26 +1616,33 @@ static int parse_dhcp_opt(char *errstr, char *arg, int } return 1; +on_error: + dhcp_opt_free(new); + return 0; } #endif void set_option_bool(unsigned int opt) { - if (opt < 32) - daemon->options |= 1u << opt; - else - daemon->options2 |= 1u << (opt - 32); + option_var(opt) |= option_val(opt); } void reset_option_bool(unsigned int opt) { - if (opt < 32) - daemon->options &= ~(1u << opt); - else - daemon->options2 &= ~(1u << (opt - 32)); + option_var(opt) &= ~(option_val(opt)); } +static void server_list_free(struct server *list) +{ + while (list) + { + struct server *tmp = list; + list = list->next; + free(tmp); + } +} + static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only) { int i; @@ -1460,9 +1701,9 @@ static int one_opt(int option, char *arg, char *errstr struct dirent *ent; char *directory, *path; struct list { - char *suffix; + char *name; struct list *next; - } *ignore_suffix = NULL, *li; + } *ignore_suffix = NULL, *match_suffix = NULL, *files = NULL, *li; comma = split(arg); if (!(directory = opt_string_alloc(arg))) @@ -1471,12 +1712,31 @@ static int one_opt(int option, char *arg, char *errstr for (arg = comma; arg; arg = comma) { comma = split(arg); - li = opt_malloc(sizeof(struct list)); - li->next = ignore_suffix; - ignore_suffix = li; - /* Have to copy: buffer is overwritten */ - li->suffix = opt_string_alloc(arg); - }; + if (strlen(arg) != 0) + { + li = opt_malloc(sizeof(struct list)); + if (*arg == '*') + { + /* "*" 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); @@ -1493,12 +1753,26 @@ static int one_opt(int option, char *arg, char *errstr 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->suffix); + size_t ls = strlen(li->name); if (len > ls && - strcmp(li->suffix, &ent->d_name[len - ls]) == 0) + strcmp(li->name, &ent->d_name[len - ls]) == 0) break; } if (li) @@ -1515,20 +1789,44 @@ static int one_opt(int option, char *arg, char *errstr /* only reg files allowed. */ if (S_ISREG(buf.st_mode)) - one_file(path, 0); - - free(path); + { + /* 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; + } + } - + + 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->suffix); + 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; } @@ -1536,10 +1834,46 @@ static int one_opt(int option, char *arg, char *errstr set_option_bool(OPT_CLIENT_SUBNET); if (arg) { + char *err, *end; comma = split(arg); - if (!atoi_check(arg, &daemon->addr4_netmask) || - (comma && !atoi_check(comma, &daemon->addr6_netmask))) - ret_err(gen_err); + + struct mysubnet* new = opt_malloc(sizeof(struct mysubnet)); + if ((end = split_chr(arg, '/'))) + { + /* has subnet+len */ + err = parse_mysockaddr(arg, &new->addr); + if (err) + ret_err_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; @@ -1550,7 +1884,15 @@ static int one_opt(int option, char *arg, char *errstr 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) @@ -1571,7 +1913,7 @@ static int one_opt(int option, char *arg, char *errstr #endif } break; - + case 'x': /* --pid-file */ daemon->runfile = opt_string_alloc(arg); break; @@ -1642,6 +1984,14 @@ static int one_opt(int option, char *arg, char *errstr 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); @@ -1650,9 +2000,7 @@ static int one_opt(int option, char *arg, char *errstr /* Sorry about the gross pre-processor abuse */ case '6': /* --dhcp-script */ case LOPT_LUASCRIPT: /* --dhcp-luascript */ -# if defined(NO_FORK) - ret_err(_("cannot run scripts under uClinux")); -# elif !defined(HAVE_SCRIPT) +# if !defined(HAVE_SCRIPT) ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts")); # else if (option == LOPT_LUASCRIPT) @@ -1667,9 +2015,12 @@ static int one_opt(int option, char *arg, char *errstr break; #endif /* HAVE_DHCP */ - case LOPT_DHCP_HOST: /* --dhcp-hostfile */ - case LOPT_DHCP_OPTS: /* --dhcp-optsfile */ - case 'H': /* --addn-hosts */ + case LOPT_DHCP_HOST: /* --dhcp-hostsfile */ + case LOPT_DHCP_OPTS: /* --dhcp-optsfile */ + case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */ + case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */ + case LOPT_HOST_INOTIFY: /* --hostsdir */ + case 'H': /* --addn-hosts */ { struct hostsfile *new = opt_malloc(sizeof(struct hostsfile)); static unsigned int hosts_index = SRC_AH; @@ -1691,50 +2042,57 @@ static int one_opt(int option, char *arg, char *errstr new->next = daemon->dhcp_opts_file; daemon->dhcp_opts_file = new; } + else + { + new->next = daemon->dynamic_dirs; + daemon->dynamic_dirs = new; + if (option == LOPT_DHCP_INOTIFY) + new->flags |= AH_DHCP_HST; + else if (option == LOPT_DHOPT_INOTIFY) + new->flags |= AH_DHCP_OPT; + else if (option == LOPT_HOST_INOTIFY) + new->flags |= AH_HOSTS; + } + break; } - -#ifdef HAVE_AUTH case LOPT_AUTHSERV: /* --auth-server */ - if (!(comma = split(arg))) - ret_err(gen_err); + comma = split(arg); daemon->authserver = opt_string_alloc(arg); - arg = comma; - do { - struct iname *new = opt_malloc(sizeof(struct iname)); - comma = split(arg); - new->name = NULL; - unhide_metas(arg); - if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0) - new->addr.sa.sa_family = AF_INET; -#ifdef HAVE_IPV6 - else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0) - new->addr.sa.sa_family = AF_INET6; -#endif - else - { - char *fam = split_chr(arg, '/'); - new->name = opt_string_alloc(arg); - new->addr.sa.sa_family = 0; - if (fam) - { - if (strcmp(fam, "4") == 0) - new->addr.sa.sa_family = AF_INET; -#ifdef HAVE_IPV6 - else if (strcmp(fam, "6") == 0) - new->addr.sa.sa_family = AF_INET6; -#endif - else - ret_err(gen_err); - } - } - new->next = daemon->authinterface; - daemon->authinterface = new; - - arg = comma; - } while (arg); + + 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; @@ -1762,6 +2120,7 @@ static int one_opt(int option, char *arg, char *errstr new = opt_malloc(sizeof(struct auth_zone)); new->domain = opt_string_alloc(arg); new->subnet = NULL; + new->exclude = NULL; new->interface_names = NULL; new->next = daemon->auth_zones; daemon->auth_zones = new; @@ -1769,9 +2128,10 @@ static int one_opt(int option, char *arg, char *errstr while ((arg = comma)) { int prefixlen = 0; + int is_exclude = 0; char *prefix; struct addrlist *subnet = NULL; - struct all_addr addr; + union all_addr addr; comma = split(arg); prefix = split_chr(arg, '/'); @@ -1779,20 +2139,24 @@ static int one_opt(int option, char *arg, char *errstr if (prefix && !atoi_check(prefix, &prefixlen)) ret_err(gen_err); - if (inet_pton(AF_INET, arg, &addr.addr.addr4)) + 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; } -#ifdef HAVE_IPV6 - else if (inet_pton(AF_INET6, arg, &addr.addr.addr6)) + 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; } -#endif else { struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list)); @@ -1804,10 +2168,8 @@ static int one_opt(int option, char *arg, char *errstr { if (prefixlen == 4) name->flags &= ~AUTH6; -#ifdef HAVE_IPV6 else if (prefixlen == 6) name->flags &= ~AUTH4; -#endif else ret_err(gen_err); } @@ -1816,8 +2178,17 @@ static int one_opt(int option, char *arg, char *errstr if (subnet) { subnet->addr = addr; - subnet->next = new->subnet; - new->subnet = subnet; + + if (is_exclude) + { + subnet->next = new->exclude; + new->exclude = subnet; + } + else + { + subnet->next = new->subnet; + new->subnet = subnet; + } } } break; @@ -1847,17 +2218,12 @@ static int one_opt(int option, char *arg, char *errstr comma = split(arg); daemon->soa_retry = (u32)atoi(arg); if (comma) - { - arg = comma; - comma = split(arg); - daemon->soa_expiry = (u32)atoi(arg); - } + daemon->soa_expiry = (u32)atoi(comma); } } } break; -#endif case 's': /* --domain */ case LOPT_SYNTH: /* --synth-domain */ @@ -1877,7 +2243,8 @@ static int one_opt(int option, char *arg, char *errstr char *netpart; new->prefix = NULL; - + new->indexed = 0; + unhide_metas(comma); if ((netpart = split_chr(comma, '/'))) { @@ -1885,7 +2252,7 @@ static int one_opt(int option, char *arg, char *errstr arg = split(netpart); if (!atoi_check(netpart, &msize)) - ret_err(gen_err); + ret_err_free(gen_err, new); else if (inet_pton(AF_INET, comma, &new->start)) { int mask = (1 << (32 - msize)) - 1; @@ -1898,22 +2265,31 @@ static int one_opt(int option, char *arg, char *errstr { if (!(new->prefix = canonicalise_opt(arg)) || strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN) - ret_err(_("bad prefix")); + ret_err_free(_("bad prefix"), new); } else if (strcmp(arg, "local") != 0 || (msize != 8 && msize != 16 && msize != 24)) - ret_err(gen_err); + ret_err_free(gen_err, new); else { /* generate the equivalent of - local=// local=/xxx.yyy.zzz.in-addr.arpa/ */ struct server *serv = add_rev4(new->start, msize); + if (!serv) + ret_err_free(_("bad prefix"), new); + serv->flags |= SERV_NO_ADDR; + + /* local=// */ + serv = opt_malloc(sizeof(struct server)); + memset(serv, 0, sizeof(struct server)); + serv->domain = d; + serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; + serv->next = daemon->servers; + daemon->servers = serv; } } } -#ifdef HAVE_IPV6 else if (inet_pton(AF_INET6, comma, &new->start6)) { u64 mask = (1LLU << (128 - msize)) - 1LLU; @@ -1929,30 +2305,36 @@ static int one_opt(int option, char *arg, char *errstr setaddr6part(&new->end6, addrpart | mask); if (msize < 64) - ret_err(gen_err); + ret_err_free(gen_err, new); else if (arg) { if (option != 's') { if (!(new->prefix = canonicalise_opt(arg)) || strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN) - ret_err(_("bad prefix")); + ret_err_free(_("bad prefix"), new); } else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0)) - ret_err(gen_err); + ret_err_free(gen_err, new); else { /* generate the equivalent of - local=// local=/xxx.yyy.zzz.ip6.arpa/ */ struct server *serv = add_rev6(&new->start6, msize); serv->flags |= SERV_NO_ADDR; + + /* local=// */ + serv = opt_malloc(sizeof(struct server)); + memset(serv, 0, sizeof(struct server)); + serv->domain = d; + serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; + serv->next = daemon->servers; + daemon->servers = serv; } } } -#endif else - ret_err(gen_err); + ret_err_free(gen_err, new); } else { @@ -1966,26 +2348,24 @@ static int one_opt(int option, char *arg, char *errstr if (!arg) new->end.s_addr = new->start.s_addr; else if (!inet_pton(AF_INET, arg, &new->end)) - ret_err(gen_err); + ret_err_free(gen_err, new); } -#ifdef HAVE_IPV6 else if (inet_pton(AF_INET6, comma, &new->start6)) { new->is6 = 1; if (!arg) memcpy(&new->end6, &new->start6, IN6ADDRSZ); else if (!inet_pton(AF_INET6, arg, &new->end6)) - ret_err(gen_err); + ret_err_free(gen_err, new); } -#endif else - ret_err(gen_err); + ret_err_free(gen_err, new); if (option != 's' && prefstr) { if (!(new->prefix = canonicalise_opt(prefstr)) || strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN) - ret_err(_("bad prefix")); + ret_err_free(_("bad prefix"), new); } } @@ -1997,8 +2377,16 @@ static int one_opt(int option, char *arg, char *errstr } else { + char *star; new->next = daemon->synth_domains; daemon->synth_domains = new; + if (new->prefix && + (star = strrchr(new->prefix, '*')) + && *(star+1) == 0) + { + *star = 0; + new->indexed = 1; + } } } else if (option == 's') @@ -2009,6 +2397,26 @@ static int one_opt(int option, char *arg, char *errstr } break; + case LOPT_CPE_ID: /* --add-dns-client */ + if (arg) + daemon->dns_client_id = opt_string_alloc(arg); + 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; @@ -2070,14 +2478,23 @@ static int one_opt(int option, char *arg, char *errstr break; case 'B': /* --bogus-nxdomain */ - { + case LOPT_IGNORE_ADDR: /* --ignore-address */ + { struct in_addr addr; unhide_metas(arg); if (arg && (inet_pton(AF_INET, arg, &addr) > 0)) { struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr)); - baddr->next = daemon->bogus_addr; - daemon->bogus_addr = baddr; + if (option == 'B') + { + baddr->next = daemon->bogus_addr; + daemon->bogus_addr = baddr; + } + else + { + baddr->next = daemon->ignore_addr; + daemon->ignore_addr = baddr; + } baddr->addr = addr; } else @@ -2099,7 +2516,6 @@ static int one_opt(int option, char *arg, char *errstr new->addr.in.sin_len = sizeof(new->addr.in); #endif } -#ifdef HAVE_IPV6 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0) { new->addr.sa.sa_family = AF_INET6; @@ -2110,9 +2526,8 @@ static int one_opt(int option, char *arg, char *errstr new->addr.in6.sin6_len = sizeof(new->addr.in6); #endif } -#endif else - ret_err(gen_err); + ret_err_free(gen_err, new); new->used = 0; if (option == 'a') @@ -2153,7 +2568,7 @@ static int one_opt(int option, char *arg, char *errstr if (strcmp(arg, "#") == 0) domain = ""; else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg))) - option = '?'; + ret_err(gen_err); serv = opt_malloc(sizeof(struct server)); memset(serv, 0, sizeof(struct server)); serv->next = newlist; @@ -2171,6 +2586,9 @@ static int one_opt(int option, char *arg, char *errstr { newlist = opt_malloc(sizeof(struct server)); memset(newlist, 0, sizeof(struct server)); +#ifdef HAVE_LOOP + newlist->uid = rand32(); +#endif } if (servers_only && option == 'S') @@ -2180,7 +2598,10 @@ static int one_opt(int option, char *arg, char *errstr { newlist->flags |= SERV_LITERAL_ADDRESS; if (!(newlist->flags & SERV_TYPE)) - ret_err(gen_err); + { + server_list_free(newlist); + ret_err(gen_err); + } } else if (option == LOPT_NO_REBIND) newlist->flags |= SERV_NO_REBIND; @@ -2189,27 +2610,24 @@ static int one_opt(int option, char *arg, char *errstr { if (!(newlist->flags & SERV_NO_REBIND)) newlist->flags |= SERV_NO_ADDR; /* no server */ - if (newlist->flags & SERV_LITERAL_ADDRESS) - ret_err(gen_err); } else if (strcmp(arg, "#") == 0) - { - newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */ - if (newlist->flags & SERV_LITERAL_ADDRESS) - ret_err(gen_err); - } + newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */ else { char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags); if (err) - ret_err(err); + { + server_list_free(newlist); + ret_err(err); + } } serv = newlist; while (serv->next) { - serv->next->flags = serv->flags; + serv->next->flags |= serv->flags & ~(SERV_HAS_DOMAIN | SERV_FOR_NODOTS); serv->next->addr = serv->addr; serv->next->source_addr = serv->source_addr; strcpy(serv->next->interface, serv->interface); @@ -2226,20 +2644,25 @@ static int one_opt(int option, char *arg, char *errstr int size; struct server *serv; struct in_addr addr4; -#ifdef HAVE_IPV6 struct in6_addr addr6; -#endif unhide_metas(arg); - if (!arg || !(comma=split(arg)) || !(string = split_chr(arg, '/')) || !atoi_check(string, &size)) + if (!arg) ret_err(gen_err); + + comma=split(arg); + if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size)) + ret_err(gen_err); + if (inet_pton(AF_INET, arg, &addr4)) - serv = add_rev4(addr4, size); -#ifdef HAVE_IPV6 + { + serv = add_rev4(addr4, size); + if (!serv) + ret_err(_("bad prefix")); + } else if (inet_pton(AF_INET6, arg, &addr6)) serv = add_rev6(&addr6, size); -#endif else ret_err(gen_err); @@ -2280,7 +2703,7 @@ static int one_opt(int option, char *arg, char *errstr if (strcmp(arg, "#") == 0 || !*arg) domain = ""; else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg))) - option = '?'; + ret_err(gen_err); ipsets->next = opt_malloc(sizeof(struct ipsets)); ipsets = ipsets->next; memset(ipsets, 0, sizeof(struct ipsets)); @@ -2295,13 +2718,11 @@ static int one_opt(int option, char *arg, char *errstr memset(ipsets, 0, sizeof(struct ipsets)); ipsets->domain = ""; } + if (!arg || !*arg) - { - option = '?'; - break; - } - size = 2; - for (end = arg; *end; ++end) + ret_err(gen_err); + + for (size = 2, end = arg; *end; ++end) if (*end == ',') ++size; @@ -2334,8 +2755,14 @@ static int one_opt(int option, char *arg, char *errstr if (size < 0) size = 0; - else if (size > 10000) - size = 10000; + + /* 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; } @@ -2352,11 +2779,22 @@ static int one_opt(int option, char *arg, char *errstr 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 && strcmp(arg, "extra") == 0) + set_option_bool(OPT_EXTRALOG); + break; + case LOPT_MAX_LOGS: /* --log-async */ daemon->max_logs = LOG_MAX; /* default */ if (arg && !atoi_check(arg, &daemon->max_logs)) @@ -2386,8 +2824,10 @@ static int one_opt(int option, char *arg, char *errstr 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)) @@ -2396,10 +2836,21 @@ static int one_opt(int option, char *arg, char *errstr 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; @@ -2418,6 +2869,11 @@ static int one_opt(int option, char *arg, char *errstr 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) @@ -2446,19 +2902,37 @@ static int one_opt(int option, char *arg, char *errstr } 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 = opt_malloc(sizeof(struct dhcp_bridge)); + struct dhcp_bridge *new; + if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 ) ret_err(_("bad bridge-interface")); - - strcpy(new->iface, arg); - new->alias = NULL; - new->next = daemon->bridges; - daemon->bridges = new; + 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); @@ -2475,6 +2949,44 @@ static int one_opt(int option, char *arg, char *errstr } #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; @@ -2482,14 +2994,7 @@ static int one_opt(int option, char *arg, char *errstr struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context)); memset (new, 0, sizeof(*new)); - new->lease_time = DEFLEASE; - if (!arg) - { - option = '?'; - break; - } - while(1) { for (cp = arg; *cp; cp++) @@ -2502,21 +3007,19 @@ static int one_opt(int option, char *arg, char *errstr { if (is_tag_prefix(arg)) { - struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid)); - tt->net = opt_string_alloc(arg+4); - tt->next = new->filter; /* ignore empty tag */ - if (tt->net) - new->filter = tt; + if (arg[4]) + new->filter = dhcp_netid_create(arg+4, new->filter); } else { if (new->netid.net) - ret_err(_("only one tag allowed")); - else if (strstr(arg, "set:") == arg) - new->netid.net = opt_string_alloc(arg+4); + { + dhcp_context_free(new); + ret_err(_("only one tag allowed")); + } else - new->netid.net = opt_string_alloc(arg); + new->netid.net = opt_string_alloc(set_prefix(arg)); } arg = comma; } @@ -2532,11 +3035,15 @@ static int one_opt(int option, char *arg, char *errstr break; if (k < 2) - ret_err(_("bad dhcp-range")); + { + 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) @@ -2544,7 +3051,10 @@ static int one_opt(int option, char *arg, char *errstr else if (strcmp(a[1], "proxy") == 0) new->flags |= CONTEXT_PROXY; else if (!inet_pton(AF_INET, a[1], &new->end)) - ret_err(_("bad dhcp-range")); + { + dhcp_context_free(new); + ret_err(_("bad dhcp-range")); + } if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr)) { @@ -2559,22 +3069,29 @@ static int one_opt(int option, char *arg, char *errstr new->flags |= CONTEXT_NETMASK; leasepos = 3; if (!is_same_net(new->start, new->end, new->netmask)) - ret_err(_("inconsistent DHCP range")); - } + { + 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; + 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; @@ -2583,11 +3100,15 @@ static int one_opt(int option, char *arg, char *errstr 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_ONLY | CONTEXT_RA; + 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]) @@ -2613,20 +3134,25 @@ static int one_opt(int option, char *arg, char *errstr } } - if (new->prefix != 64) + if (new->prefix > 64) { - if ((new->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS))) - ret_err(_("prefix length must be exactly 64 for RA subnets")); + if (new->flags & CONTEXT_RA) + err=(_("prefix length must be exactly 64 for RA subnets")); else if (new->flags & CONTEXT_TEMPLATE) - ret_err(_("prefix length must be exactly 64 for subnet constructors")); + err=(_("prefix length must be exactly 64 for subnet constructors")); } - - if (new->prefix < 64) - ret_err(_("prefix length must be at least 64")); + else if (new->prefix < 64) + err=(_("prefix length must be at least 64")); - if (!is_same_net6(&new->start6, &new->end6, new->prefix)) - ret_err(_("inconsistent DHCPv6 range")); + 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; @@ -2636,7 +3162,10 @@ static int one_opt(int option, char *arg, char *errstr struct in6_addr zero; memset(&zero, 0, sizeof(zero)); if (!is_same_net6(&zero, &new->start6, new->prefix)) - ret_err(_("prefix must be zero with \"constructor:\" argument")); + { + dhcp_context_free(new); + ret_err(_("prefix must be zero with \"constructor:\" argument")); + } } if (addr6part(&new->start6) > addr6part(&new->end6)) @@ -2648,12 +3177,24 @@ static int one_opt(int option, char *arg, char *errstr } #endif else - ret_err(_("bad dhcp-range")); + { + 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->lease_time = 0xffffffff; + new->flags |= CONTEXT_SETLEASE; + } else if (strcmp(a[leasepos], "deprecated") == 0) new->flags |= CONTEXT_DEPRECATE; else @@ -2670,7 +3211,7 @@ static int one_opt(int option, char *arg, char *errstr case 'd': case 'D': fac *= 24; - /* fall though */ + /* fall through */ case 'h': case 'H': fac *= 60; @@ -2689,9 +3230,10 @@ static int one_opt(int option, char *arg, char *errstr break; if (*cp || (leasepos+1 < k)) - ret_err(_("bad dhcp-range")); + 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) @@ -2699,14 +3241,13 @@ static int one_opt(int option, char *arg, char *errstr } } } + break; } case LOPT_BANK: case 'G': /* --dhcp-host */ { - int j, k = 0; - char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; struct dhcp_config *new; struct in_addr in; @@ -2716,186 +3257,223 @@ static int one_opt(int option, char *arg, char *errstr new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0; new->hwaddr = NULL; new->netid = NULL; - - if ((a[0] = arg)) - for (k = 1; k < 7; k++) - if (!(a[k] = split(a[k-1]))) - break; - - for (j = 0; j < k; j++) - if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */ - { - char *arg = a[j]; - - 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) - - 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 *newtag = opt_malloc(sizeof(struct dhcp_netid)); - struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list)); - newtag->net = opt_malloc(strlen(arg + 4) + 1); - newlist->next = new->netid; - new->netid = newlist; - newlist->list = newtag; - strcpy(newtag->net, arg+4); - unhide_metas(newtag->net); - } - else if (strstr(arg, "tag:") == arg) - ret_err(_("cannot match tags in --dhcp-host")); + new->filter = NULL; + new->clid = NULL; #ifdef HAVE_DHCP6 - else if (arg[0] == '[' && arg[strlen(arg)-1] == ']') - { - arg[strlen(arg)-1] = 0; - arg++; + 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); - if (!inet_pton(AF_INET6, arg, &new->addr6)) - ret_err(_("bad IPv6 address")); +#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")); + } - for (i= 0; i < 8; i++) - if (new->addr6.s6_addr[i] != 0) - break; - - /* set WILDCARD if network part all zeros */ - if (i == 8) - new->flags |= CONFIG_WILDCARD; + new_addr = opt_malloc(sizeof(struct addrlist)); + new_addr->next = new->addr6; + new_addr->flags = 0; + new_addr->addr.addr6 = in6; + new->addr6 = new_addr; + + 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(_("bad IPv6 prefix")); + } + + new_addr->flags |= ADDRLIST_PREFIX; + } - new->flags |= CONFIG_ADDR6; - } + 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->flags |= CONFIG_ADDR6; + } #endif - else - { - struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config)); - if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX, - &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1) - ret_err(_("bad hex constant")); - else + 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) { - - newhw->next = new->hwaddr; - new->hwaddr = newhw; - } - } - } - else if (strchr(a[j], '.') && (inet_pton(AF_INET, a[j], &in) > 0)) - { - struct dhcp_config *configs; - - new->addr = in; - new->flags |= CONFIG_ADDR; + sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in)); + 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 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) + if (*cp) { - sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in)); - return 0; - } - } - else - { - char *cp, *lastp = NULL, last = 0; - int fac = 1, isdig = 0; - - if (strlen(a[j]) > 1) - { - lastp = a[j] + strlen(a[j]) - 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 = a[j]; *cp; cp++) - if (isdigit((unsigned char)*cp)) - isdig = 1; - else if (*cp != ' ') - break; + 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 = 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; + } + } - if (*cp) - { - if (lastp) - *lastp = last; - if (strcmp(a[j], "infinite") == 0) - { - new->lease_time = 0xffffffff; - new->flags |= CONFIG_TIME; - } - else if (strcmp(a[j], "ignore") == 0) - new->flags |= CONFIG_DISABLE; - else - { - if (!(new->hostname = canonicalise_opt(a[j])) || - !legal_hostname(new->hostname)) - ret_err(_("bad DHCP host name")); - - new->flags |= CONFIG_NAME; - new->domain = strip_hostname(new->hostname); - } - } - else if (isdig) - { - new->lease_time = atoi(a[j]) * 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)); @@ -2928,10 +3506,7 @@ static int one_opt(int option, char *arg, char *errstr } else { - struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid)); - newtag->net = opt_malloc(len - 3); - strcpy(newtag->net, arg+4); - unhide_metas(newtag->net); + struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL); if (strstr(arg, "set:") == arg) { @@ -2948,7 +3523,7 @@ static int one_opt(int option, char *arg, char *errstr else { new->set = NULL; - free(newtag); + dhcp_netid_free(newtag); break; } } @@ -2957,7 +3532,11 @@ static int one_opt(int option, char *arg, char *errstr } if (!new->set) - ret_err(_("bad tag-if")); + { + dhcp_netid_free(new->tag); + dhcp_netid_list_free(new->set); + ret_err_free(_("bad tag-if"), new); + } break; } @@ -2971,22 +3550,41 @@ static int one_opt(int option, char *arg, char *errstr option == LOPT_FORCE ? DHOPT_FORCE : (option == LOPT_MATCH ? DHOPT_MATCH : (option == LOPT_OPTS ? DHOPT_BANK : 0))); - - case 'M': /* --dhcp-boot */ + + case LOPT_NAME_MATCH: /* --dhcp-name-match */ { - struct dhcp_netid *id = NULL; - while (is_tag_prefix(arg)) + struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name)); + struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid)); + ssize_t len; + + if (!(comma = split(arg)) || (len = strlen(comma)) == 0) + ret_err(gen_err); + + new->wildcard = 0; + new->netid = id; + id->net = opt_string_alloc(set_prefix(arg)); + + if (comma[len-1] == '*') { - struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid)); - newid->next = id; - id = newid; - comma = split(arg); - newid->net = opt_string_alloc(arg+4); - arg = comma; - }; + 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); + { + ret_err(gen_err); + } else { char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL; @@ -3030,26 +3628,45 @@ static int one_opt(int option, char *arg, char *errstr 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 */ - - while (is_tag_prefix(arg)) - { - struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid)); - comma = split(arg); - nn->next = new->netid; - new->netid = nn; - nn->net = opt_string_alloc(arg+4); - arg = comma; - } + new->netid = dhcp_tags(&arg); if (!arg) - ret_err(gen_err); + { + dhcp_opt_free(new); + ret_err(gen_err); + } else { comma = split(arg); @@ -3058,8 +3675,8 @@ static int one_opt(int option, char *arg, char *errstr new->val = opt_malloc(new->len); memcpy(new->val + 1, arg, new->len - 1); - new->u.vendor_class = (unsigned char *)"PXEClient"; - new->flags = DHOPT_VENDOR; + new->u.vendor_class = NULL; + new->flags = DHOPT_VENDOR | DHOPT_VENDOR_PXE; if (comma && atoi_check(comma, &timeout)) *(new->val) = timeout; @@ -3078,23 +3695,15 @@ static int one_opt(int option, char *arg, char *errstr { struct pxe_service *new = opt_malloc(sizeof(struct pxe_service)); char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client", - "IA32_EFI", "BC_EFI", "Xscale_EFI", "x86-64_EFI", NULL }; + "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); - while (is_tag_prefix(arg)) - { - struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid)); - comma = split(arg); - nn->next = new->netid; - new->netid = nn; - nn->net = opt_string_alloc(arg+4); - arg = comma; - } - if (arg && (comma = split(arg))) { for (i = 0; CSA[i]; i++) @@ -3171,7 +3780,10 @@ static int one_opt(int option, char *arg, char *errstr unhide_metas(comma); new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type); if (new->hwaddr_len == -1) - ret_err(gen_err); + { + free(new->netid.net); + ret_err_free(gen_err, new); + } else { new->next = daemon->dhcp_macs; @@ -3181,24 +3793,6 @@ static int one_opt(int option, char *arg, char *errstr } break; -#ifdef OPTION6_PREFIX_CLASS - case LOPT_PREF_CLSS: /* --dhcp-prefix-class */ - { - struct prefix_class *new = opt_malloc(sizeof(struct prefix_class)); - - if (!(comma = split(arg)) || - !atoi_check16(comma, &new->class)) - ret_err(gen_err); - - new->tag.net = opt_string_alloc(set_prefix(arg)); - new->next = daemon->prefix_classes; - daemon->prefix_classes = new; - - break; - } -#endif - - case 'U': /* --dhcp-vendorclass */ case 'j': /* --dhcp-userclass */ case LOPT_CIRCUIT: /* --dhcp-circuitid */ @@ -3210,7 +3804,7 @@ static int one_opt(int option, char *arg, char *errstr struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor)); if (!(comma = split(arg))) - ret_err(gen_err); + 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, @@ -3220,7 +3814,10 @@ static int one_opt(int option, char *arg, char *errstr if ((comma = split(arg))) { if (option != 'U' || strstr(arg, "enterprise:") != arg) - ret_err(gen_err); + { + free(new->netid.net); + ret_err_free(gen_err, new); + } else new->enterprise = atoi(arg+11); } @@ -3322,14 +3919,8 @@ static int one_opt(int option, char *arg, char *errstr } while (arg) { - struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid)); comma = split(arg); - member->next = list; - list = member; - if (is_tag_prefix(arg)) - member->net = opt_string_alloc(arg+4); - else - member->net = opt_string_alloc(arg); + list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list); arg = comma; } @@ -3343,10 +3934,23 @@ static int one_opt(int option, char *arg, char *errstr struct addr_list *new = opt_malloc(sizeof(struct addr_list)); comma = split(arg); if (!(inet_pton(AF_INET, arg, &new->addr) > 0)) - ret_err(_("bad dhcp-proxy address")); + 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; @@ -3369,7 +3973,10 @@ static int one_opt(int option, char *arg, char *errstr } #endif else - ret_err(_("Bad dhcp-relay")); + { + free(new->interface); + ret_err_free(_("Bad dhcp-relay"), new); + } break; } @@ -3383,7 +3990,21 @@ static int one_opt(int option, char *arg, char *errstr 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') @@ -3395,7 +4016,11 @@ static int one_opt(int option, char *arg, char *errstr arg = split(comma); if (!atoi_check(comma, &new->interval) || (arg && !atoi_check(arg, &new->lifetime))) - ret_err(_("bad RA-params")); + { +err: + free(new->name); + ret_err_free(_("bad RA-params"), new); + } new->next = daemon->ra_interfaces; daemon->ra_interfaces = new; @@ -3436,17 +4061,15 @@ static int one_opt(int option, char *arg, char *errstr if ((k < 2) || (!(inet_pton(AF_INET, a[0], &new->in) > 0)) || - (!(inet_pton(AF_INET, a[1], &new->out) > 0))) - option = '?'; + (!(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 (k == 3) - inet_pton(AF_INET, a[2], &new->mask); - 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(_("invalid alias range")); + ret_err_free(_("invalid alias range"), new); break; } @@ -3476,12 +4099,10 @@ static int one_opt(int option, char *arg, char *errstr { if (strcmp(arg, "4") == 0) new->family = AF_INET; -#ifdef HAVE_IPV6 else if (strcmp(arg, "6") == 0) new->family = AF_INET6; -#endif else - ret_err(gen_err); + ret_err_free(gen_err, new); } new->intr = opt_string_alloc(comma); break; @@ -3490,27 +4111,50 @@ static int one_opt(int option, char *arg, char *errstr case LOPT_CNAME: /* --cname */ { struct cname *new; - char *alias; - char *target; + char *alias, *target, *last, *pen; + int ttl = -1; - if (!(comma = split(arg))) - ret_err(gen_err); - - alias = canonicalise_opt(arg); - target = canonicalise_opt(comma); - - if (!alias || !target) + for (last = pen = NULL, comma = arg; comma; comma = split(comma)) + { + pen = last; + last = comma; + } + + if (!pen) ret_err(_("bad CNAME")); - else + + if (pen != arg && atoi_check(last, &ttl)) + last = pen; + + target = canonicalise_opt(last); + + while (arg != last) { + int arglen = strlen(arg); + alias = canonicalise_opt(arg); + + if (!alias || !target) + { + free(target); + free(alias); + ret_err(_("bad CNAME")); + } + for (new = daemon->cnames; new; new = new->next) - if (hostname_isequal(new->alias, arg)) - ret_err(_("duplicate CNAME")); + 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(*arg); arg++); } break; @@ -3525,7 +4169,11 @@ static int one_opt(int option, char *arg, char *errstr if (!(dom = canonicalise_opt(arg)) || (comma && !(target = canonicalise_opt(comma)))) - ret_err(_("bad PTR record")); + { + free(dom); + free(target); + ret_err(_("bad PTR record")); + } else { new = opt_malloc(sizeof(struct ptr_record)); @@ -3543,7 +4191,7 @@ static int one_opt(int option, char *arg, char *errstr int k = 0; struct naptr *new; int order, pref; - char *name, *replace = NULL; + char *name=NULL, *replace = NULL; if ((a[0] = arg)) for (k = 1; k < 7; k++) @@ -3556,7 +4204,11 @@ static int one_opt(int option, char *arg, char *errstr !atoi_check16(a[1], &order) || !atoi_check16(a[2], &pref) || (k == 7 && !(replace = canonicalise_opt(a[6])))) - ret_err(_("bad NAPTR record")); + { + free(name); + free(replace); + ret_err(_("bad NAPTR record")); + } else { new = opt_malloc(sizeof(struct naptr)); @@ -3576,28 +4228,32 @@ static int one_opt(int option, char *arg, char *errstr case LOPT_RR: /* dns-rr */ { struct txt_record *new; - size_t len = len; + size_t len = 0; char *data; - int val; + int class; comma = split(arg); data = split(comma); new = opt_malloc(sizeof(struct txt_record)); - new->next = daemon->rr; - daemon->rr = new; + new->name = NULL; - if (!atoi_check(comma, &val) || + if (!atoi_check(comma, &class) || !(new->name = canonicalise_opt(arg)) || (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U)) - ret_err(_("bad RR record")); - - new->class = val; + { + 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->txt = opt_malloc(len); new->len = len; memcpy(new->txt, data, len); } @@ -3605,6 +4261,37 @@ static int one_opt(int option, char *arg, char *errstr 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; @@ -3614,14 +4301,14 @@ static int one_opt(int option, char *arg, char *errstr comma = split(arg); new = opt_malloc(sizeof(struct txt_record)); - new->next = daemon->txt; - daemon->txt = new; new->class = C_IN; new->stat = 0; if (!(new->name = canonicalise_opt(arg))) - ret_err(_("bad TXT record")); + 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); @@ -3668,29 +4355,32 @@ static int one_opt(int option, char *arg, char *errstr arg = comma; comma = split(arg); if (!(target = canonicalise_opt(arg))) - ret_err(_("bad SRV target")); + ret_err_free(_("bad SRV target"), name); if (comma) { arg = comma; comma = split(arg); if (!atoi_check16(arg, &port)) - ret_err(_("invalid port number")); + { + free(name); + ret_err_free(_("invalid port number"), target); + } if (comma) { arg = comma; comma = split(arg); if (!atoi_check16(arg, &priority)) - ret_err(_("invalid priority")); - - if (comma) - { - arg = comma; - comma = split(arg); - if (!atoi_check16(arg, &weight)) - ret_err(_("invalid weight")); + { + free(name); + ret_err_free(_("invalid priority"), target); } + if (comma && !atoi_check16(comma, &weight)) + { + free(name); + ret_err_free(_("invalid weight"), target); + } } } } @@ -3709,29 +4399,53 @@ static int one_opt(int option, char *arg, char *errstr case LOPT_HOST_REC: /* --host-record */ { - struct host_record *new = opt_malloc(sizeof(struct host_record)); - memset(new, 0, sizeof(struct 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) { - struct all_addr addr; - if (inet_pton(AF_INET, arg, &addr)) - new->addr = addr.addr.addr4; -#ifdef HAVE_IPV6 - else if (inet_pton(AF_INET6, arg, &addr)) - new->addr6 = addr.addr.addr6; -#endif + 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 { int nomem; char *canon = canonicalise(arg, &nomem); - struct name_list *nl = opt_malloc(sizeof(struct name_list)); + struct name_list *nl; if (!canon) - ret_err(_("Bad name in host-record")); + { + struct name_list *tmp = new->names, *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; @@ -3760,13 +4474,28 @@ static int one_opt(int option, char *arg, char *errstr } #ifdef HAVE_DNSSEC - case LOPT_TRUST_ANCHOR: + 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))) { @@ -3791,7 +4520,7 @@ static int one_opt(int option, char *arg, char *errstr !atoi_check8(algo, &new->algo) || !atoi_check8(digest, &new->digest_type) || !(new->name = canonicalise_opt(arg))) - ret_err(_("bad trust anchor")); + ret_err_free(_("bad trust anchor"), new); /* Upper bound on length */ len = (2*strlen(keyhex))+1; @@ -3805,7 +4534,10 @@ static int one_opt(int option, char *arg, char *errstr else cp++; if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1) - ret_err(_("bad HEX in trust anchor")); + { + free(new->name); + ret_err_free(_("bad HEX in trust anchor"), new); + } new->next = daemon->ds; daemon->ds = new; @@ -3831,7 +4563,7 @@ static void read_file(char *file, FILE *f, int hard_op { int white, i; volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt; - char *errmess, *p, *arg = NULL, *start; + char *errmess, *p, *arg, *start; size_t len; /* Memory allocation failure longjmps here if mem_recover == 1 */ @@ -3842,6 +4574,7 @@ static void read_file(char *file, FILE *f, int hard_op mem_recover = 1; } + arg = NULL; lineno++; errmess = NULL; @@ -3944,7 +4677,7 @@ static void read_file(char *file, FILE *f, int hard_op if (errmess) strcpy(daemon->namebuff, errmess); - if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV)) + if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV)) { sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file); if (hard_opt != 0) @@ -3958,6 +4691,20 @@ static void read_file(char *file, FILE *f, int hard_op fclose(f); } +#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; @@ -4055,7 +4802,7 @@ struct hostsfile *expand_filelist(struct hostsfile *li /* don't read this as a file */ ah->flags |= AH_INACTIVE; - + if (!(dir_stream = opendir(ah->fname))) my_syslog(LOG_ERR, _("cannot access directory %s: %s"), ah->fname, strerror(errno)); @@ -4145,86 +4892,99 @@ void read_servers_file(void) #ifdef HAVE_DHCP -void reread_dhcp(void) +static void clear_dynamic_conf(void) { - struct hostsfile *hf; - - if (daemon->dhcp_hosts_file) - { - struct dhcp_config *configs, *cp, **up; + struct dhcp_config *configs, *cp, **up; - /* remove existing... */ - for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp) + /* remove existing... */ + for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp) + { + cp = configs->next; + + if (configs->flags & CONFIG_BANK) { - cp = configs->next; + struct hwaddr_config *mac, *tmp; + struct dhcp_netid_list *list, *tmplist; - if (configs->flags & CONFIG_BANK) + for (mac = configs->hwaddr; mac; mac = tmp) { - struct hwaddr_config *mac, *tmp; - struct dhcp_netid_list *list, *tmplist; - - for (mac = configs->hwaddr; mac; mac = tmp) - { - tmp = mac->next; - free(mac); - } + tmp = mac->next; + free(mac); + } + + if (configs->flags & CONFIG_CLID) + free(configs->clid); + + for (list = configs->netid; list; list = tmplist) + { + free(list->list); + tmplist = list->next; + free(list); + } + + if (configs->flags & CONFIG_NAME) + free(configs->hostname); + + *up = configs->next; + free(configs); + } + else + up = &configs->next; + } +} - if (configs->flags & CONFIG_CLID) - free(configs->clid); +static void clear_dynamic_opt(void) +{ + struct dhcp_opt *opts, *cp, **up; + struct dhcp_netid *id, *next; - for (list = configs->netid; list; list = tmplist) - { - free(list->list); - tmplist = list->next; - free(list); - } - - if (configs->flags & CONFIG_NAME) - free(configs->hostname); - - *up = configs->next; - free(configs); + for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp) + { + cp = opts->next; + + if (opts->flags & DHOPT_BANK) + { + if ((opts->flags & DHOPT_VENDOR)) + free(opts->u.vendor_class); + free(opts->val); + for (id = opts->netid; id; id = next) + { + next = id->next; + free(id->net); + free(id); } - else - up = &configs->next; + *up = opts->next; + free(opts); } - + else + up = &opts->next; + } +} + +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 (!(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) { - struct dhcp_opt *opts, *cp, **up; - struct dhcp_netid *id, *next; - - for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp) - { - cp = opts->next; - - if (opts->flags & DHOPT_BANK) - { - if ((opts->flags & DHOPT_VENDOR)) - free(opts->u.vendor_class); - free(opts->val); - for (id = opts->netid; id; id = next) - { - next = id->next; - free(id->net); - free(id); - } - *up = opts->next; - free(opts); - } - else - up = &opts->next; - } - 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)) @@ -4233,14 +4993,21 @@ void reread_dhcp(void) 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); char *buff = opt_malloc(MAXDNAME); - int option, conffile_opt = '7', testmode = 0; - char *arg, *conffile = CONFFILE; + int option, testmode = 0; + char *arg, *conffile = NULL; opterr = 0; @@ -4267,6 +5034,10 @@ void read_opts(int argc, char **argv, char *compile_op daemon->soa_refresh = SOA_REFRESH; daemon->soa_retry = SOA_RETRY; daemon->soa_expiry = SOA_EXPIRY; + daemon->max_port = MAX_PORT; + daemon->min_port = MIN_PORT; + +#ifndef NO_ID add_txt("version.bind", "dnsmasq-" VERSION, 0 ); add_txt("authors.bind", "Simon Kelley", 0); add_txt("copyright.bind", COPYRIGHT, 0); @@ -4279,6 +5050,7 @@ void read_opts(int argc, char **argv, char *compile_op add_txt("auth.bind", NULL, TXT_STAT_AUTH); #endif add_txt("servers.bind", NULL, TXT_STAT_SERVERS); +#endif while (1) { @@ -4303,9 +5075,14 @@ void read_opts(int argc, char **argv, char *compile_op /* Copy optarg so that argv doesn't get changed */ if (optarg) { - strncpy(buff, optarg, MAXDNAME); - buff[MAXDNAME-1] = 0; - arg = buff; + 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; @@ -4339,8 +5116,14 @@ void read_opts(int argc, char **argv, char *compile_op } else if (option == 'C') { - conffile_opt = 0; /* file must exist */ - conffile = opt_string_alloc(arg); + if (!conffile) + conffile = opt_string_alloc(arg); + else + { + char *extra = opt_string_alloc(arg); + one_file(extra, 0); + free(extra); + } } else { @@ -4353,8 +5136,15 @@ void read_opts(int argc, char **argv, char *compile_op } } + free(argbuf); + if (conffile) - one_file(conffile, conffile_opt); + { + one_file(conffile, 0); + free(conffile); + } + else + one_file(CONFFILE, '7'); /* port might not be known when the address is parsed - fill in here */ if (daemon->servers) @@ -4365,23 +5155,70 @@ void read_opts(int argc, char **argv, char *compile_op { if (tmp->source_addr.sa.sa_family == AF_INET) tmp->source_addr.in.sin_port = htons(daemon->query_port); -#ifdef HAVE_IPV6 else if (tmp->source_addr.sa.sa_family == AF_INET6) tmp->source_addr.in6.sin6_port = htons(daemon->query_port); -#endif - } - } + } + } + 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); -#ifdef HAVE_IPV6 else if (tmp->addr.sa.sa_family == AF_INET6) tmp->addr.in6.sin6_port = htons(daemon->port); -#endif /* IPv6 */ } /* create default, if not specified */ @@ -4390,6 +5227,13 @@ void read_opts(int argc, char **argv, char *compile_op 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 */