Diff for /embedaddon/dnsmasq/src/option.c between versions 1.1.1.4 and 1.1.1.5

version 1.1.1.4, 2021/03/17 00:56:46 version 1.1.1.5, 2023/09/27 11:02:07
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
   
    This program is free software; you can redistribute it and/or modify     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     it under the terms of the GNU General Public License as published by
Line 168  struct myoption { Line 168  struct myoption {
 #define LOPT_SINGLE_PORT   359  #define LOPT_SINGLE_PORT   359
 #define LOPT_SCRIPT_TIME   360  #define LOPT_SCRIPT_TIME   360
 #define LOPT_PXE_VENDOR    361  #define LOPT_PXE_VENDOR    361
 #define LOPT_DYNHOST       362
 #define LOPT_LOG_DEBUG     363
 #define LOPT_UMBRELLA      364
 #define LOPT_CMARK_ALST_EN 365
 #define LOPT_CMARK_ALST    366
 #define LOPT_QUIET_TFTP    367
 #define LOPT_NFTSET        368
 #define LOPT_FILTER_A      369
 #define LOPT_FILTER_AAAA   370
 #define LOPT_STRIP_SBNET   371
 #define LOPT_STRIP_MAC     372
 #define LOPT_CONF_OPT      373
 #define LOPT_CONF_SCRIPT   374
 #define LOPT_RANDPORT_LIM  375
 #define LOPT_FAST_RETRY    376
 #define LOPT_STALE_CACHE   377
 #define LOPT_NORR          378
 #define LOPT_NO_IDENT      379
 
 #ifdef HAVE_GETOPT_LONG  #ifdef HAVE_GETOPT_LONG
 static const struct option opts[] =    static const struct option opts[] =  
 #else  #else
Line 205  static const struct myoption opts[] =  Line 223  static const struct myoption opts[] = 
     { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },      { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
     { "selfmx", 0, 0, 'e' },      { "selfmx", 0, 0, 'e' },
     { "filterwin2k", 0, 0, 'f' },      { "filterwin2k", 0, 0, 'f' },
       { "filter-A", 0, 0, LOPT_FILTER_A },
       { "filter-AAAA", 0, 0, LOPT_FILTER_AAAA },
     { "pid-file", 2, 0, 'x' },      { "pid-file", 2, 0, 'x' },
     { "strict-order", 0, 0, 'o' },      { "strict-order", 0, 0, 'o' },
     { "server", 1, 0, 'S' },      { "server", 1, 0, 'S' },
Line 212  static const struct myoption opts[] =  Line 232  static const struct myoption opts[] = 
     { "local", 1, 0, LOPT_LOCAL },      { "local", 1, 0, LOPT_LOCAL },
     { "address", 1, 0, 'A' },      { "address", 1, 0, 'A' },
     { "conf-file", 2, 0, 'C' },      { "conf-file", 2, 0, 'C' },
       { "conf-script", 1, 0, LOPT_CONF_SCRIPT },
     { "no-resolv", 0, 0, 'R' },      { "no-resolv", 0, 0, 'R' },
     { "expand-hosts", 0, 0, 'E' },      { "expand-hosts", 0, 0, 'E' },
     { "localmx", 0, 0, 'L' },      { "localmx", 0, 0, 'L' },
     { "local-ttl", 1, 0, 'T' },      { "local-ttl", 1, 0, 'T' },
     { "no-negcache", 0, 0, 'N' },      { "no-negcache", 0, 0, 'N' },
       { "no-round-robin", 0, 0, LOPT_NORR },
     { "addn-hosts", 1, 0, 'H' },      { "addn-hosts", 1, 0, 'H' },
     { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },      { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
     { "query-port", 1, 0, 'Q' },      { "query-port", 1, 0, 'Q' },
Line 303  static const struct myoption opts[] =  Line 325  static const struct myoption opts[] = 
     { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },      { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
     { "rebind-localhost-ok", 0, 0,  LOPT_LOC_REBND },      { "rebind-localhost-ok", 0, 0,  LOPT_LOC_REBND },
     { "add-mac", 2, 0, LOPT_ADD_MAC },      { "add-mac", 2, 0, LOPT_ADD_MAC },
       { "strip-mac", 0, 0, LOPT_STRIP_MAC },
     { "add-subnet", 2, 0, LOPT_ADD_SBNET },      { "add-subnet", 2, 0, LOPT_ADD_SBNET },
       { "strip-subnet", 0, 0, LOPT_STRIP_SBNET },
     { "add-cpe-id", 1, 0 , LOPT_CPE_ID },      { "add-cpe-id", 1, 0 , LOPT_CPE_ID },
     { "proxy-dnssec", 0, 0, LOPT_DNSSEC },      { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
     { "dhcp-sequential-ip", 0, 0,  LOPT_INCR_ADDR },      { "dhcp-sequential-ip", 0, 0,  LOPT_INCR_ADDR },
Line 321  static const struct myoption opts[] =  Line 345  static const struct myoption opts[] = 
     { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },      { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
     { "auth-peer", 1, 0, LOPT_AUTHPEER },       { "auth-peer", 1, 0, LOPT_AUTHPEER }, 
     { "ipset", 1, 0, LOPT_IPSET },      { "ipset", 1, 0, LOPT_IPSET },
       { "nftset", 1, 0, LOPT_NFTSET },
       { "connmark-allowlist-enable", 2, 0, LOPT_CMARK_ALST_EN },
       { "connmark-allowlist", 1, 0, LOPT_CMARK_ALST },
     { "synth-domain", 1, 0, LOPT_SYNTH },      { "synth-domain", 1, 0, LOPT_SYNTH },
     { "dnssec", 0, 0, LOPT_SEC_VALID },      { "dnssec", 0, 0, LOPT_SEC_VALID },
     { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },      { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
Line 341  static const struct myoption opts[] =  Line 368  static const struct myoption opts[] = 
     { "dumpfile", 1, 0, LOPT_DUMPFILE },      { "dumpfile", 1, 0, LOPT_DUMPFILE },
     { "dumpmask", 1, 0, LOPT_DUMPMASK },      { "dumpmask", 1, 0, LOPT_DUMPMASK },
     { "dhcp-ignore-clid", 0, 0,  LOPT_IGNORE_CLID },      { "dhcp-ignore-clid", 0, 0,  LOPT_IGNORE_CLID },
       { "dynamic-host", 1, 0, LOPT_DYNHOST },
       { "log-debug", 0, 0, LOPT_LOG_DEBUG },
       { "umbrella", 2, 0, LOPT_UMBRELLA },
       { "quiet-tftp", 0, 0, LOPT_QUIET_TFTP },
       { "port-limit", 1, 0, LOPT_RANDPORT_LIM },
       { "fast-dns-retry", 2, 0, LOPT_FAST_RETRY },
       { "use-stale-cache", 2, 0 , LOPT_STALE_CACHE },
       { "no-ident", 0, 0, LOPT_NO_IDENT },
     { NULL, 0, 0, 0 }      { NULL, 0, 0, 0 }
   };    };
   
Line 368  static struct { Line 403  static struct {
   { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },    { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
   { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },    { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
   { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },    { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
     { LOPT_FILTER_A, OPT_FILTER_A, NULL, gettext_noop("Don't include IPv4 addresses in DNS answers."), NULL },
     { LOPT_FILTER_AAAA, OPT_FILTER_AAAA, NULL, gettext_noop("Don't include IPv6 addresses in DNS answers."), NULL },
   { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },    { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
   { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },    { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
   { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },    { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Line 396  static struct { Line 433  static struct {
   { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },    { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
   { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },     { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE }, 
   { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },    { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
     { LOPT_STALE_CACHE, ARG_ONE, "[=<max_expired>]", gettext_noop("Use expired cache data for faster reply."), NULL },
   { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },    { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
   { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },    { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
   { LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},    { LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},
Line 403  static struct { Line 441  static struct {
   { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },    { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
   { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },    { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
   { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },    { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
     { LOPT_RANDPORT_LIM, ARG_ONE, "#ports", gettext_noop("Set maximum number of random originating ports for a query."), NULL },
   { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },    { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
   { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },     { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE }, 
   { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },    { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
Line 416  static struct { Line 455  static struct {
   { LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },    { LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
   { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },    { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
   { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },    { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
     { LOPT_FAST_RETRY, ARG_ONE, "<milliseconds>", gettext_noop("Retry DNS queries after this many milliseconds."), NULL},
   { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },     { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER }, 
   { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },    { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
   { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },    { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Line 443  static struct { Line 483  static struct {
   { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },    { LOPT_SCRIPTUSR, ARG_ONE, "<username>", 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 },    { LOPT_SCRIPT_ARP, OPT_SCRIPT_ARP, NULL, gettext_noop("Call dhcp-script with changes to local ARP table."), NULL },
   { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },    { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
     { LOPT_CONF_SCRIPT, ARG_DUP, "<path>", gettext_noop("Execute file and read configuration from stdin."), NULL },
   { '8', ARG_ONE, "<facility>|<file>", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), NULL },    { '8', ARG_ONE, "<facility>|<file>", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), NULL },
   { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },    { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
   { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },     { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" }, 
Line 481  static struct { Line 522  static struct {
   { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },    { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
   { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },    { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
   { LOPT_ADD_MAC, ARG_DUP, "[=base64|text]", gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },    { LOPT_ADD_MAC, ARG_DUP, "[=base64|text]", gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
     { LOPT_STRIP_MAC, OPT_STRIP_MAC, NULL, gettext_noop("Strip MAC information from queries."), NULL },
   { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL },    { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL },
     { LOPT_STRIP_SBNET, OPT_STRIP_ECS, NULL, gettext_noop("Strip ECS information from queries."), NULL },
   { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },    { LOPT_CPE_ID, ARG_ONE, "<text>", 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_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_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
Line 491  static struct { Line 534  static struct {
   { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },    { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
   { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },    { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
   { LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },    { LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
     { LOPT_DYNHOST, ARG_DUP, "<name>,[<IPv4>][,<IPv6>],<interface-name>", gettext_noop("Specify host record in interface subnet"), NULL },
   { LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },      { LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },  
   { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },    { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
   { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },    { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Line 501  static struct { Line 545  static struct {
   { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },    { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
   { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },    { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
   { LOPT_IPSET, ARG_DUP, "/<domain>[/<domain>...]/<ipset>...", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },    { LOPT_IPSET, ARG_DUP, "/<domain>[/<domain>...]/<ipset>...", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
     { LOPT_NFTSET, ARG_DUP, "/<domain>[/<domain>...]/<nftset>...", gettext_noop("Specify nftables sets to which matching domains should be added"), NULL },
     { LOPT_CMARK_ALST_EN, ARG_ONE, "[=<mask>]", gettext_noop("Enable filtering of DNS queries with connection-track marks."), NULL },
     { LOPT_CMARK_ALST, ARG_DUP, "<connmark>[/<mask>][,<pattern>[/<pattern>...]]", gettext_noop("Set allowed DNS patterns for a connection-track mark."), NULL },
   { LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },    { LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
   { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },    { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
   { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },    { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
Line 512  static struct { Line 559  static struct {
   { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), 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_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_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
     { LOPT_LOG_DEBUG, OPT_LOG_DEBUG, NULL, gettext_noop("Log debugging information."), NULL }, 
   { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },    { LOPT_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_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
   { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },     { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL }, 
Line 521  static struct { Line 569  static struct {
   { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },    { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
   { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },    { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
   { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },    { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
     { LOPT_UMBRELLA, ARG_ONE, "[=<optspec>]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL },
     { LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL },
     { LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL },
     { LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL },
   { 0, 0, NULL, NULL, NULL }    { 0, 0, NULL, NULL, NULL }
 };   }; 
   
Line 635  static char *canonicalise_opt(char *s) Line 687  static char *canonicalise_opt(char *s)
   if (!s)    if (!s)
     return 0;      return 0;
   
     if (strlen(s) == 0)
       return opt_malloc(1); /* Heap-allocated empty string */
   
   unhide_metas(s);    unhide_metas(s);
   if (!(ret = canonicalise(s, &nomem)) && nomem)    if (!(ret = canonicalise(s, &nomem)) && nomem)
     {      {
Line 647  static char *canonicalise_opt(char *s) Line 702  static char *canonicalise_opt(char *s)
   return ret;    return ret;
 }  }
   
static int atoi_check(char *a, int *res)static int numeric_check(char *a)
 {  {
   char *p;    char *p;
   
Line 660  static int atoi_check(char *a, int *res) Line 715  static int atoi_check(char *a, int *res)
      if (*p < '0' || *p > '9')       if (*p < '0' || *p > '9')
        return 0;         return 0;
   
     return 1;
   }
   
   static int atoi_check(char *a, int *res)
   {
     if (!numeric_check(a))
       return 0;
   *res = atoi(a);    *res = atoi(a);
   return 1;    return 1;
 }  }
   
   static int strtoul_check(char *a, u32 *res)
   {
     unsigned long x;
     
     if (!numeric_check(a))
       return 0;
     x = strtoul(a, NULL, 10);
     if (errno || x > UINT32_MAX) {
       errno = 0;
       return 0;
     }
     *res = (u32)x;
     return 1;
   }
   
 static int atoi_check16(char *a, int *res)  static int atoi_check16(char *a, int *res)
 {  {
   if (!(atoi_check(a, res)) ||    if (!(atoi_check(a, res)) ||
Line 755  static void do_usage(void) Line 832  static void do_usage(void)
                             
       if (usage[i].arg)        if (usage[i].arg)
         {          {
          strcpy(buff, usage[i].arg);          safe_strncpy(buff, usage[i].arg, sizeof(buff));
           for (j = 0; tab[j].handle; j++)            for (j = 0; tab[j].handle; j++)
             if (tab[j].handle == *(usage[i].arg))              if (tab[j].handle == *(usage[i].arg))
               sprintf(buff, "%d", tab[j].val);                sprintf(buff, "%d", tab[j].val);
Line 781  static char *parse_mysockaddr(char *arg, union mysocka Line 858  static char *parse_mysockaddr(char *arg, union mysocka
   return NULL;    return NULL;
 }  }
   
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)char *parse_server(char *arg, struct server_details *sdetails)
 {  {
  int source_port = 0, serv_port = NAMESERVER_PORT;  sdetails->serv_port = NAMESERVER_PORT;
  char *portno, *source;  char *portno;
  char *interface_opt = NULL;  int ecode = 0;
  int scope_index = 0;  struct addrinfo hints;
  char *scope_id;
   memset(&hints, 0, sizeof(struct addrinfo));
       
  if (!arg || strlen(arg) == 0)  *sdetails->interface = 0;
   sdetails->addr_type = AF_UNSPEC;
      
   if (strcmp(arg, "#") == 0)
     {      {
      *flags |= SERV_NO_ADDR;      if (sdetails->flags)
      *interface = 0;        *sdetails->flags |= SERV_USE_RESOLV;
       sdetails->addr_type = AF_LOCAL;
       sdetails->valid = 1;
       return NULL;        return NULL;
     }      }
  
  if ((source = split_chr(arg, '@')) && /* is there a source. */  if ((sdetails->source = split_chr(arg, '@')) && /* is there a source. */
      (portno = split_chr(source, '#')) &&      (portno = split_chr(sdetails->source, '#')) &&
      !atoi_check16(portno, &source_port))      !atoi_check16(portno, &sdetails->source_port))
     return _("bad port");      return _("bad port");
       
   if ((portno = split_chr(arg, '#')) && /* is there a port no. */    if ((portno = split_chr(arg, '#')) && /* is there a port no. */
      !atoi_check16(portno, &serv_port))      !atoi_check16(portno, &sdetails->serv_port))
     return _("bad port");      return _("bad port");
       
  scope_id = split_chr(arg, '%');  sdetails->scope_id = split_chr(arg, '%');
       
  if (source) {  if (sdetails->source) {
    interface_opt = split_chr(source, '@');    sdetails->interface_opt = split_chr(sdetails->source, '@');
   
    if (interface_opt)    if (sdetails->interface_opt)
       {        {
 #if defined(SO_BINDTODEVICE)  #if defined(SO_BINDTODEVICE)
        safe_strncpy(interface, interface_opt, IF_NAMESIZE);        safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
         sdetails->source = sdetails->interface_opt;
 #else  #else
         return _("interface binding not supported");          return _("interface binding not supported");
 #endif  #endif
       }        }
   }    }
   
  if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)  if (inet_pton(AF_INET, arg, &sdetails->addr->in.sin_addr) > 0)
       sdetails->addr_type = AF_INET;
   else if (inet_pton(AF_INET6, arg, &sdetails->addr->in6.sin6_addr) > 0)
       sdetails->addr_type = AF_INET6;
   else 
     {      {
      addr->in.sin_port = htons(serv_port);            /* if the argument is neither an IPv4 not an IPv6 address, it might be a
      addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;         hostname and we should try to resolve it to a suitable address. */
       memset(&hints, 0, sizeof(hints));
       /* The AI_ADDRCONFIG flag ensures that then IPv4 addresses are returned in
          the result only if the local system has at least one IPv4 address
          configured, and IPv6 addresses are returned only if the local system
          has at least one IPv6 address configured. The loopback address is not
          considered for this case as valid as a configured address. This flag is
          useful on, for example, IPv4-only systems, to ensure that getaddrinfo()
          does not return IPv6 socket addresses that would always fail in
          subsequent connect() or bind() attempts. */
       hints.ai_flags = AI_ADDRCONFIG;
 #if defined(HAVE_IDN) && defined(AI_IDN)
       /* If the AI_IDN flag is specified and we have glibc 2.3.4 or newer, then
          the node name given in node is converted to IDN format if necessary.
          The source encoding is that of the current locale. */
       hints.ai_flags |= AI_IDN;
 #endif
       /* The value AF_UNSPEC indicates that getaddrinfo() should return socket
          addresses for any address family (either IPv4 or IPv6, for example)
          that can be used with node <arg> and service "domain". */
       hints.ai_family = AF_UNSPEC;
 
       /* Get addresses suitable for sending datagrams. We assume that we can use the
          same addresses for TCP connections. Settting this to zero gets each address
          threes times, for SOCK_STREAM, SOCK_RAW and SOCK_DGRAM, which is not useful. */
       hints.ai_socktype = SOCK_DGRAM;
 
       /* Get address associated with this hostname */
       ecode = getaddrinfo(arg, NULL, &hints, &sdetails->hostinfo);
       if (ecode == 0)
         {
           /* The getaddrinfo() function allocated and initialized a linked list of
              addrinfo structures, one for each network address that matches node
              and service, subject to the restrictions imposed by our <hints>
              above, and returns a pointer to the start of the list in <hostinfo>.
              The items in the linked list are linked by the <ai_next> field. */
           sdetails->valid = 1;
           sdetails->orig_hostinfo = sdetails->hostinfo;
           return NULL;
         }
       else
         {
           /* Lookup failed, return human readable error string */
           if (ecode == EAI_AGAIN)
             return _("Cannot resolve server name");
           else
             return _((char*)gai_strerror(ecode));
         }
     }
   
   sdetails->valid = 1;
   return NULL;
 }
 
 char *parse_server_addr(struct server_details *sdetails)
 {
   if (sdetails->addr_type == AF_INET)
     {
       sdetails->addr->in.sin_port = htons(sdetails->serv_port);
       sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET;
 #ifdef HAVE_SOCKADDR_SA_LEN  #ifdef HAVE_SOCKADDR_SA_LEN
      source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);      sdetails->source_addr->in.sin_len = sdetails->addr->in.sin_len = sizeof(struct sockaddr_in);
 #endif  #endif
      source_addr->in.sin_addr.s_addr = INADDR_ANY;      sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY;
      source_addr->in.sin_port = htons(daemon->query_port);      sdetails->source_addr->in.sin_port = htons(daemon->query_port);
               
      if (source)      if (sdetails->source)
         {          {
          if (flags)          if (sdetails->flags)
            *flags |= SERV_HAS_SOURCE;            *sdetails->flags |= SERV_HAS_SOURCE;
          source_addr->in.sin_port = htons(source_port);          sdetails->source_addr->in.sin_port = htons(sdetails->source_port);
          if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))          if (inet_pton(AF_INET, sdetails->source, &sdetails->source_addr->in.sin_addr) == 0)
             {              {
                 if (inet_pton(AF_INET6, sdetails->source, &sdetails->source_addr->in6.sin6_addr) == 1)
                   {
                     sdetails->source_addr->sa.sa_family = AF_INET6;
                     /* When resolving a server IP by hostname, we can simply skip mismatching
                        server / source IP pairs. Otherwise, when an IP address is given directly,
                        this is a fatal error. */
                     if (!sdetails->orig_hostinfo)
                       return _("cannot use IPv4 server address with IPv6 source address");
                   }
                 else
                   {
 #if defined(SO_BINDTODEVICE)  #if defined(SO_BINDTODEVICE)
              if (interface_opt)                  if (sdetails->interface_opt)
                return _("interface can only be specified once");                    return _("interface can only be specified once");
              
              source_addr->in.sin_addr.s_addr = INADDR_ANY;                  sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY;
              safe_strncpy(interface, source, IF_NAMESIZE);                  safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
 #else  #else
              return _("interface binding not supported");                  return _("interface binding not supported");
 #endif  #endif
                   }
             }              }
         }          }
     }      }
  else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)  else if (sdetails->addr_type == AF_INET6)
     {      {
      if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)      if (sdetails->scope_id && (sdetails->scope_index = if_nametoindex(sdetails->scope_id)) == 0)
         return _("bad interface name");          return _("bad interface name");
      
      addr->in6.sin6_port = htons(serv_port);      sdetails->addr->in6.sin6_port = htons(sdetails->serv_port);
      addr->in6.sin6_scope_id = scope_index;      sdetails->addr->in6.sin6_scope_id = sdetails->scope_index;
      source_addr->in6.sin6_addr = in6addr_any;       sdetails->source_addr->in6.sin6_addr = in6addr_any;
      source_addr->in6.sin6_port = htons(daemon->query_port);      sdetails->source_addr->in6.sin6_port = htons(daemon->query_port);
      source_addr->in6.sin6_scope_id = 0;      sdetails->source_addr->in6.sin6_scope_id = 0;
      addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;      sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET6;
      addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;      sdetails->addr->in6.sin6_flowinfo = sdetails->source_addr->in6.sin6_flowinfo = 0;
 #ifdef HAVE_SOCKADDR_SA_LEN  #ifdef HAVE_SOCKADDR_SA_LEN
      addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);      sdetails->addr->in6.sin6_len = sdetails->source_addr->in6.sin6_len = sizeof(sdetails->addr->in6);
 #endif  #endif
      if (source)      if (sdetails->source)
         {          {
          if (flags)          if (sdetails->flags)
            *flags |= SERV_HAS_SOURCE;            *sdetails->flags |= SERV_HAS_SOURCE;
          source_addr->in6.sin6_port = htons(source_port);          sdetails->source_addr->in6.sin6_port = htons(sdetails->source_port);
          if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)          if (inet_pton(AF_INET6, sdetails->source, &sdetails->source_addr->in6.sin6_addr) == 0)
             {              {
                 if (inet_pton(AF_INET, sdetails->source, &sdetails->source_addr->in.sin_addr) == 1)
                   {
                     sdetails->source_addr->sa.sa_family = AF_INET;
                     /* When resolving a server IP by hostname, we can simply skip mismatching
                        server / source IP pairs. Otherwise, when an IP address is given directly,
                        this is a fatal error. */
                     if(!sdetails->orig_hostinfo)
                       return _("cannot use IPv6 server address with IPv4 source address");
                   }
                 else
                   {
 #if defined(SO_BINDTODEVICE)  #if defined(SO_BINDTODEVICE)
              if (interface_opt)                  if (sdetails->interface_opt)
                return _("interface can only be specified once");                  return _("interface can only be specified once");
              
              source_addr->in6.sin6_addr = in6addr_any;                  sdetails->source_addr->in6.sin6_addr = in6addr_any;
              safe_strncpy(interface, source, IF_NAMESIZE);                  safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
 #else  #else
              return _("interface binding not supported");                  return _("interface binding not supported");
 #endif  #endif
                   }
             }              }
         }          }
     }      }
  else  else if (sdetails->addr_type != AF_LOCAL)
     return _("bad address");      return _("bad address");
  
   return NULL;    return NULL;
 }  }
   
static struct server *add_rev4(struct in_addr addr, int msize)int parse_server_next(struct server_details *sdetails)
 {  {
  struct server *serv = opt_malloc(sizeof(struct server));  /* Looping over resolved addresses? */
  in_addr_t  a = ntohl(addr.s_addr);  if (sdetails->hostinfo)
  char *p;    {
       /* Get address type */
       sdetails->addr_type = sdetails->hostinfo->ai_family;
   
  memset(serv, 0, sizeof(struct server));      /* Get address */
  p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */      if (sdetails->addr_type == AF_INET)
         memcpy(&sdetails->addr->in.sin_addr,
                 &((struct sockaddr_in *) sdetails->hostinfo->ai_addr)->sin_addr,
                 sizeof(sdetails->addr->in.sin_addr));
       else if (sdetails->addr_type == AF_INET6)
         memcpy(&sdetails->addr->in6.sin6_addr,
                 &((struct sockaddr_in6 *) sdetails->hostinfo->ai_addr)->sin6_addr,
                 sizeof(sdetails->addr->in6.sin6_addr));
   
  switch (msize)      /* Iterate to the next available address */
       sdetails->valid = sdetails->hostinfo->ai_next != NULL;
       sdetails->hostinfo = sdetails->hostinfo->ai_next;
       return 1;
     }
   else if (sdetails->valid)
     {      {
    case 32:      /* When using an IP address, we return the address only once */
      p += sprintf(p, "%u.", a & 0xff);      sdetails->valid = 0;
      /* fall through */      return 1;
    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; 
     }      }
     /* Stop iterating here, we used all available addresses */
     return 0;
   }
   
  p += sprintf(p, "in-addr.arpa");static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int size)
 {
   int i, j;
   char *string;
   int msize;
   u16 flags = 0;
   char domain[29]; /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
   union mysockaddr serv_addr, source_addr;
   char interface[IF_NAMESIZE+1];
   int count = 1, rem, addrbytes, addrbits;
   struct server_details sdetails;
 
   memset(&sdetails, 0, sizeof(struct server_details));
   sdetails.addr = &serv_addr;
   sdetails.source_addr = &source_addr;
   sdetails.interface = interface;
   sdetails.flags = &flags;
     
   if (!server)
     flags = SERV_LITERAL_ADDRESS;
   else if ((string = parse_server(server, &sdetails)))
     return string;
       
  serv->flags = SERV_HAS_DOMAIN;  if (from_file)
  serv->next = daemon->servers;    flags |= SERV_FROM_FILE;
  daemon->servers = serv; 
   rem = size & 0x7;
   addrbytes = (32 - size) >> 3;
   addrbits = (32 - size) & 7;
   
   if (size > 32 || size < 1)
     return _("bad IPv4 prefix length");
   
   /* Zero out last address bits according to CIDR mask */
   ((u8 *)addr4)[3-addrbytes] &= ~((1 << addrbits)-1);
   
   size = size & ~0x7;
   
   if (rem != 0)
     count = 1 << (8 - rem);
   
   for (i = 0; i < count; i++)
     {
       *domain = 0;
       string = domain;
       msize = size/8;
       
       for (j = (rem == 0) ? msize-1 : msize; j >= 0; j--)
         { 
           int dig = ((unsigned char *)addr4)[j];
           
           if (j == msize)
             dig += i;
           
           string += sprintf(string, "%d.", dig);
         }
       
       sprintf(string, "in-addr.arpa");
   
  return serv;      if (flags & SERV_LITERAL_ADDRESS)
         {
           if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
             return  _("error");
         }
       else
         {
           /* Always reset server as valid here, so we can add the same upstream
              server address multiple times for each x.y.z.in-addr.arpa  */
           sdetails.valid = 1;
           while (parse_server_next(&sdetails))
             {
               if ((string = parse_server_addr(&sdetails)))
                 return string;
               
               if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
                 return  _("error");
             }
   
             if (sdetails.orig_hostinfo)
               freeaddrinfo(sdetails.orig_hostinfo);
           }
       }
     
     return NULL;
 }  }
   
static struct server *add_rev6(struct in6_addr *addr, int msize)static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, int size)
 {  {
  struct server *serv = opt_malloc(sizeof(struct server));  int i, j;
  char *p;  char *string;
  int i;  int msize;
                                    u16 flags = 0;
  memset(serv, 0, sizeof(struct server));  char domain[73]; /* strlen("32*<n.>ip6.arpa")+1 */
  p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */  union mysockaddr serv_addr, source_addr;
   char interface[IF_NAMESIZE+1];
   int count = 1, rem, addrbytes, addrbits;
   struct server_details sdetails;
       
  for (i = msize-1; i >= 0; i -= 4)  memset(&sdetails, 0, sizeof(struct server_details));
    {   sdetails.addr = &serv_addr;
      int dig = ((unsigned char *)addr)[i>>3];  sdetails.source_addr = &source_addr;
      p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);  sdetails.interface = interface;
    }  sdetails.flags = &flags;
  p += sprintf(p, "ip6.arpa");   
   if (!server)
     flags = SERV_LITERAL_ADDRESS;
   else if ((string = parse_server(server, &sdetails)))
     return string;
 
   if (from_file)
     flags |= SERV_FROM_FILE;
       
  serv->flags = SERV_HAS_DOMAIN;  rem = size & 0x3;
  serv->next = daemon->servers;  addrbytes = (128 - size) >> 3;
  daemon->servers = serv;  addrbits = (128 - size) & 7;
       
  return serv;  if (size > 128 || size < 1)
     return _("bad IPv6 prefix length");
   
   /* Zero out last address bits according to CIDR mask */
   addr6->s6_addr[15-addrbytes] &= ~((1 << addrbits) - 1);
   
   size = size & ~0x3;
   
   if (rem != 0)
     count = 1 << (4 - rem);
       
   for (i = 0; i < count; i++)
     {
       *domain = 0;
       string = domain;
       msize = size/4;
   
       for (j = (rem == 0) ? msize-1 : msize; j >= 0; j--)
         { 
           int dig = ((unsigned char *)addr6)[j>>1];
           
           dig = j & 1 ? dig & 15 : dig >> 4;
           
           if (j == msize)
             dig += i;
           
           string += sprintf(string, "%.1x.", dig);
         }
       
       sprintf(string, "ip6.arpa");
 
       if (flags & SERV_LITERAL_ADDRESS)
         {
           if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
             return  _("error");
         }
       else
         {
           /* Always reset server as valid here, so we can add the same upstream
              server address multiple times for each x.y.z.ip6.arpa  */
           sdetails.valid = 1;
           while (parse_server_next(&sdetails))
             {
               if ((string = parse_server_addr(&sdetails)))
                 return string;
               
               if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
                 return  _("error");
             }
 
           if (sdetails.orig_hostinfo)
             freeaddrinfo(sdetails.orig_hostinfo);
         }
     }
   
   return NULL;
 }  }
   
 #ifdef HAVE_DHCP  #ifdef HAVE_DHCP
Line 1038  static void dhcp_config_free(struct dhcp_config *confi Line 1354  static void dhcp_config_free(struct dhcp_config *confi
               
       if (config->flags & CONFIG_CLID)        if (config->flags & CONFIG_CLID)
         free(config->clid);          free(config->clid);
         if (config->flags & CONFIG_NAME)
           free(config->hostname);
   
 #ifdef HAVE_DHCP6  #ifdef HAVE_DHCP6
       if (config->flags & CONFIG_ADDR6)        if (config->flags & CONFIG_ADDR6)
Line 1154  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1472  static int parse_dhcp_opt(char *errstr, char *arg, int
         {          {
           new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);            new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
           new->flags |= DHOPT_VENDOR;            new->flags |= DHOPT_VENDOR;
             if ((new->flags & DHOPT_ENCAPSULATE) || flags == DHOPT_MATCH)
               goto_err(_("inappropriate vendor:"));
         }          }
       else if (strstr(arg, "encap:") == arg)        else if (strstr(arg, "encap:") == arg)
         {          {
           new->u.encap = atoi(arg+6);            new->u.encap = atoi(arg+6);
           new->flags |= DHOPT_ENCAPSULATE;            new->flags |= DHOPT_ENCAPSULATE;
             if ((new->flags & DHOPT_VENDOR) || flags == DHOPT_MATCH)
               goto_err(_("inappropriate encap:"));
         }          }
       else if (strstr(arg, "vi-encap:") == arg)        else if (strstr(arg, "vi-encap:") == arg)
         {          {
Line 1633  void reset_option_bool(unsigned int opt) Line 1955  void reset_option_bool(unsigned int opt)
   option_var(opt) &= ~(option_val(opt));    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)  static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
 {        {      
   int i;    int i;
Line 1695  static int one_opt(int option, char *arg, char *errstr Line 2007  static int one_opt(int option, char *arg, char *errstr
         break;          break;
       }        }
   
       case LOPT_CONF_SCRIPT: /* --conf-script */
         {
           char *file = opt_string_alloc(arg);
           if (file)
             {
               one_file(file, LOPT_CONF_SCRIPT);
               free(file);
             }
           break;
         }
   
     case '7': /* --conf-dir */              case '7': /* --conf-dir */        
       {        {
         DIR *dir_stream;          DIR *dir_stream;
Line 1801  static int one_opt(int option, char *arg, char *errstr Line 2124  static int one_opt(int option, char *arg, char *errstr
                 new->next = li;                  new->next = li;
                 *up = new;                  *up = new;
               }                }
               else
                 free(path);
   
           }            }
   
Line 1967  static int one_opt(int option, char *arg, char *errstr Line 2292  static int one_opt(int option, char *arg, char *errstr
                   
         if (!(name = canonicalise_opt(arg)) ||           if (!(name = canonicalise_opt(arg)) || 
             (comma && !(target = canonicalise_opt(comma))))              (comma && !(target = canonicalise_opt(comma))))
          ret_err(_("bad MX name"));          {
             free(name);
             free(target);
             ret_err(_("bad MX name"));
           }
                   
         new = opt_malloc(sizeof(struct mx_srv_record));          new = opt_malloc(sizeof(struct mx_srv_record));
         new->next = daemon->mxnames;          new->next = daemon->mxnames;
Line 2017  static int one_opt(int option, char *arg, char *errstr Line 2346  static int one_opt(int option, char *arg, char *errstr
   
     case LOPT_DHCP_HOST:     /* --dhcp-hostsfile */      case LOPT_DHCP_HOST:     /* --dhcp-hostsfile */
     case LOPT_DHCP_OPTS:     /* --dhcp-optsfile */      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 */      case 'H':                /* --addn-hosts */
       {        {
         struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));          struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
         static unsigned int hosts_index = SRC_AH;  
         new->fname = opt_string_alloc(arg);          new->fname = opt_string_alloc(arg);
        new->index = hosts_index++;        new->index = daemon->host_index++;
         new->flags = 0;          new->flags = 0;
         if (option == 'H')          if (option == 'H')
           {            {
Line 2041  static int one_opt(int option, char *arg, char *errstr Line 2366  static int one_opt(int option, char *arg, char *errstr
           {            {
             new->next = daemon->dhcp_opts_file;              new->next = daemon->dhcp_opts_file;
             daemon->dhcp_opts_file = new;              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;          break;
       }        }
   
       case LOPT_DHCP_INOTIFY:  /* --dhcp-hostsdir */
       case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
       case LOPT_HOST_INOTIFY:  /* --hostsdir */
         {
           struct dyndir *new = opt_malloc(sizeof(struct dyndir));
           new->dname = opt_string_alloc(arg);
           new->flags = 0;
           new->next = daemon->dynamic_dirs;
           daemon->dynamic_dirs = new; 
           if (option == LOPT_DHCP_INOTIFY)
           new->flags |= AH_DHCP_HST;
           else if (option == LOPT_DHOPT_INOTIFY)
           new->flags |= AH_DHCP_OPT;
           else if (option == LOPT_HOST_INOTIFY)
           new->flags |= AH_HOSTS;
   
           break;
         }
               
     case LOPT_AUTHSERV: /* --auth-server */      case LOPT_AUTHSERV: /* --auth-server */
       comma = split(arg);        comma = split(arg);
Line 2118  static int one_opt(int option, char *arg, char *errstr Line 2451  static int one_opt(int option, char *arg, char *errstr
         comma = split(arg);          comma = split(arg);
                                   
         new = opt_malloc(sizeof(struct auth_zone));          new = opt_malloc(sizeof(struct auth_zone));
        new->domain = opt_string_alloc(arg);        new->domain = canonicalise_opt(arg);
        new->subnet = NULL;        if (!new->domain)
           ret_err_free(_("invalid auth-zone"), new);
         new->subnet = NULL;
         new->exclude = NULL;          new->exclude = NULL;
         new->interface_names = NULL;          new->interface_names = NULL;
         new->next = daemon->auth_zones;          new->next = daemon->auth_zones;
Line 2203  static int one_opt(int option, char *arg, char *errstr Line 2538  static int one_opt(int option, char *arg, char *errstr
           arg = comma;            arg = comma;
           comma = split(arg);            comma = split(arg);
           daemon->hostmaster = opt_string_alloc(arg);            daemon->hostmaster = opt_string_alloc(arg);
          for (cp = daemon->hostmaster; *cp; cp++)          for (cp = daemon->hostmaster; cp && *cp; cp++)
             if (*cp == '@')              if (*cp == '@')
               *cp = '.';                *cp = '.';
   
Line 2231  static int one_opt(int option, char *arg, char *errstr Line 2566  static int one_opt(int option, char *arg, char *errstr
         set_option_bool(OPT_RESOLV_DOMAIN);          set_option_bool(OPT_RESOLV_DOMAIN);
       else        else
         {          {
          char *d;          char *d, *d_raw = arg;
           comma = split(arg);            comma = split(arg);
          if (!(d = canonicalise_opt(arg)))          if (!(d = canonicalise_opt(d_raw)))
             ret_err(gen_err);              ret_err(gen_err);
           else            else
             {              {
                 free(d); /* allocate this again below. */
               if (comma)                if (comma)
                 {                  {
                   struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));                    struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Line 2244  static int one_opt(int option, char *arg, char *errstr Line 2580  static int one_opt(int option, char *arg, char *errstr
                                       
                   new->prefix = NULL;                    new->prefix = NULL;
                   new->indexed = 0;                    new->indexed = 0;
                     new->prefixlen = 0;
                                       
                   unhide_metas(comma);                    unhide_metas(comma);
                   if ((netpart = split_chr(comma, '/')))                    if ((netpart = split_chr(comma, '/')))
Line 2255  static int one_opt(int option, char *arg, char *errstr Line 2592  static int one_opt(int option, char *arg, char *errstr
                         ret_err_free(gen_err, new);                          ret_err_free(gen_err, new);
                       else if (inet_pton(AF_INET, comma, &new->start))                        else if (inet_pton(AF_INET, comma, &new->start))
                         {                          {
                          int mask = (1 << (32 - msize)) - 1;                          int mask;
 
                           if (msize > 32)
                              ret_err_free(_("bad prefix length"), new);
                           
                           mask = (1 << (32 - msize)) - 1;
                           new->is6 = 0;                                                       new->is6 = 0;                           
                           new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);                            new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
                           new->end.s_addr = new->start.s_addr | htonl(mask);                            new->end.s_addr = new->start.s_addr | htonl(mask);
Line 2267  static int one_opt(int option, char *arg, char *errstr Line 2609  static int one_opt(int option, char *arg, char *errstr
                                       strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)                                        strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
                                     ret_err_free(_("bad prefix"), new);                                      ret_err_free(_("bad prefix"), new);
                                 }                                  }
                              else if (strcmp(arg, "local") != 0 ||                              else if (strcmp(arg, "local") != 0)
                                       (msize != 8 && msize != 16 && msize != 24)) 
                                 ret_err_free(gen_err, new);                                  ret_err_free(gen_err, new);
                               else                                else
                                 {                                  {
                                   /* generate the equivalent of                                  /* local=/xxx.yyy.zzz.in-addr.arpa/ */
                                      local=/xxx.yyy.zzz.in-addr.arpa/ */                                  domain_rev4(0, NULL, &new->start, msize);
                                  struct server *serv = add_rev4(new->start, msize);                                                                  
                                  if (!serv) 
                                    ret_err_free(_("bad prefix"), new); 
 
                                  serv->flags |= SERV_NO_ADDR; 
 
                                   /* local=/<domain>/ */                                    /* local=/<domain>/ */
                                  serv = opt_malloc(sizeof(struct server));                                  /* d_raw can't failed to canonicalise here, checked above. */
                                  memset(serv, 0, sizeof(struct server));                                  add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL);
                                  serv->domain = d; 
                                  serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; 
                                  serv->next = daemon->servers; 
                                  daemon->servers = serv; 
                                 }                                  }
                             }                              }
                         }                          }
                       else if (inet_pton(AF_INET6, comma, &new->start6))                        else if (inet_pton(AF_INET6, comma, &new->start6))
                         {                          {
                          u64 mask = (1LLU << (128 - msize)) - 1LLU;                          u64 mask, addrpart = addr6part(&new->start6);
                          u64 addrpart = addr6part(&new->start6);
                           if (msize > 128)
                             ret_err_free(_("bad prefix length"), new);
 
                           mask = (1LLU << (128 - msize)) - 1LLU;
 
                           new->is6 = 1;                            new->is6 = 1;
                             new->prefixlen = msize;
                                                       
                           /* prefix==64 overflows the mask calculation above */                            /* prefix==64 overflows the mask calculation above */
                          if (msize == 64)                          if (msize <= 64)
                             mask = (u64)-1LL;                              mask = (u64)-1LL;
                                                       
                           new->end6 = new->start6;                            new->end6 = new->start6;
                           setaddr6part(&new->start6, addrpart & ~mask);                            setaddr6part(&new->start6, addrpart & ~mask);
                           setaddr6part(&new->end6, addrpart | mask);                            setaddr6part(&new->end6, addrpart | mask);
                                                       
                          if (msize < 64)                          if (arg)
                            ret_err_free(gen_err, new); 
                          else if (arg) 
                             {                              {
                               if (option != 's')                                if (option != 's')
                                 {                                  {
Line 2314  static int one_opt(int option, char *arg, char *errstr Line 2650  static int one_opt(int option, char *arg, char *errstr
                                       strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)                                        strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
                                     ret_err_free(_("bad prefix"), new);                                      ret_err_free(_("bad prefix"), new);
                                 }                                         }       
                              else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))                              else if (strcmp(arg, "local") != 0)
                                 ret_err_free(gen_err, new);                                  ret_err_free(gen_err, new);
                               else                                 else 
                                 {                                  {
                                   /* generate the equivalent of                                    /* generate the equivalent of
                                      local=/xxx.yyy.zzz.ip6.arpa/ */                                       local=/xxx.yyy.zzz.ip6.arpa/ */
                                  struct server *serv = add_rev6(&new->start6, msize);                                  domain_rev6(0, NULL, &new->start6, msize);
                                  serv->flags |= SERV_NO_ADDR; 
                                                                       
                                   /* local=/<domain>/ */                                    /* local=/<domain>/ */
                                  serv = opt_malloc(sizeof(struct server));                                  /* d_raw can't failed to canonicalise here, checked above. */
                                  memset(serv, 0, sizeof(struct server));                                  add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL);
                                  serv->domain = d; 
                                  serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; 
                                  serv->next = daemon->servers; 
                                  daemon->servers = serv; 
                                 }                                  }
                             }                              }
                         }                          }
Line 2358  static int one_opt(int option, char *arg, char *errstr Line 2689  static int one_opt(int option, char *arg, char *errstr
                           else if (!inet_pton(AF_INET6, arg, &new->end6))                            else if (!inet_pton(AF_INET6, arg, &new->end6))
                             ret_err_free(gen_err, new);                              ret_err_free(gen_err, new);
                         }                          }
                      else                       else if (option == 's')
                         {
                           /* subnet from interface. */
                           new->interface = opt_string_alloc(comma);
                           new->al = NULL;
                         }
                       else
                         ret_err_free(gen_err, new);                          ret_err_free(gen_err, new);
                      
                       if (option != 's' && prefstr)                        if (option != 's' && prefstr)
                         {                          {
                           if (!(new->prefix = canonicalise_opt(prefstr)) ||                            if (!(new->prefix = canonicalise_opt(prefstr)) ||
Line 2369  static int one_opt(int option, char *arg, char *errstr Line 2706  static int one_opt(int option, char *arg, char *errstr
                         }                          }
                     }                      }
   
                  new->domain = d;                  new->domain = canonicalise_opt(d_raw);
                   if (option  == 's')                    if (option  == 's')
                     {                      {
                       new->next = daemon->cond_domain;                        new->next = daemon->cond_domain;
Line 2378  static int one_opt(int option, char *arg, char *errstr Line 2715  static int one_opt(int option, char *arg, char *errstr
                   else                    else
                     {                      {
                       char *star;                        char *star;
                       new->next = daemon->synth_domains;  
                       daemon->synth_domains = new;  
                       if (new->prefix &&                        if (new->prefix &&
                           (star = strrchr(new->prefix, '*'))                            (star = strrchr(new->prefix, '*'))
                           && *(star+1) == 0)                            && *(star+1) == 0)
                         {                          {
                           *star = 0;                            *star = 0;
                           new->indexed = 1;                            new->indexed = 1;
                             if (new->is6 && new->prefixlen < 64)
                               ret_err_free(_("prefix length too small"), new);
                         }                          }
                         new->next = daemon->synth_domains;
                         daemon->synth_domains = new;
                     }                      }
                 }                  }
               else if (option == 's')                else if (option == 's')
                daemon->domain_suffix = d;                daemon->domain_suffix = canonicalise_opt(d_raw);
               else                 else 
                 ret_err(gen_err);                  ret_err(gen_err);
             }              }
Line 2402  static int one_opt(int option, char *arg, char *errstr Line 2741  static int one_opt(int option, char *arg, char *errstr
         daemon->dns_client_id = opt_string_alloc(arg);          daemon->dns_client_id = opt_string_alloc(arg);
       break;        break;
   
       case LOPT_UMBRELLA: /* --umbrella */
         set_option_bool(OPT_UMBRELLA);
         while (arg)
           {
             comma = split(arg);
             if (strstr(arg, "deviceid:"))
               {
                 char *p;
                 u8 *u = daemon->umbrella_device;
                 char word[3];
                 
                 arg += 9;
                 if (strlen(arg) != 16)
                   ret_err(gen_err);
                 
                 for (p = arg; *p; p++)
                   if (!isxdigit((unsigned char)*p))
                     ret_err(gen_err);
                 
                 set_option_bool(OPT_UMBRELLA_DEVID);
                 
                 for (i = 0; i < (int)sizeof(daemon->umbrella_device); i++, arg+=2)
                   {
                     memcpy(word, &(arg[0]), 2);
                     *u++ = strtoul(word, NULL, 16);
                   }
               }
             else if (strstr(arg, "orgid:"))
               {
                 if (!strtoul_check(arg+6, &daemon->umbrella_org))
                   ret_err(gen_err);
               }
             else if (strstr(arg, "assetid:"))
               {
                 if (!strtoul_check(arg+8, &daemon->umbrella_asset))
                   ret_err(gen_err);
               }
             else
               ret_err(gen_err);
             
             arg = comma;
           }
         break;
         
     case LOPT_ADD_MAC: /* --add-mac */      case LOPT_ADD_MAC: /* --add-mac */
       if (!arg)        if (!arg)
         set_option_bool(OPT_ADD_MAC);          set_option_bool(OPT_ADD_MAC);
Line 2480  static int one_opt(int option, char *arg, char *errstr Line 2863  static int one_opt(int option, char *arg, char *errstr
     case 'B':  /* --bogus-nxdomain */      case 'B':  /* --bogus-nxdomain */
     case LOPT_IGNORE_ADDR: /* --ignore-address */      case LOPT_IGNORE_ADDR: /* --ignore-address */
      {       {
        struct in_addr addr;        union all_addr addr;
         int prefix, is6 = 0;
         struct bogus_addr *baddr;
         
         unhide_metas(arg);          unhide_metas(arg);
        if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
         if (!arg ||
             ((comma = split_chr(arg, '/')) && !atoi_check(comma, &prefix)))
           ret_err(gen_err);
 
         if (inet_pton(AF_INET6, arg, &addr.addr6) == 1)
           is6 = 1;
         else if (inet_pton(AF_INET, arg, &addr.addr4) != 1)
           ret_err(gen_err);
 
         if (!comma)
           {            {
            struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));            if (is6)
            if (option == 'B')              prefix = 128;
              { 
                baddr->next = daemon->bogus_addr; 
                daemon->bogus_addr = baddr; 
              } 
             else              else
              {              prefix = 32;
                baddr->next = daemon->ignore_addr; 
                daemon->ignore_addr = baddr; 
              } 
            baddr->addr = addr; 
           }            }
   
           if (prefix > 128 || (!is6 && prefix > 32))
             ret_err(gen_err);
           
           baddr = opt_malloc(sizeof(struct bogus_addr));
           if (option == 'B')
             {
               baddr->next = daemon->bogus_addr;
               daemon->bogus_addr = baddr;
             }
         else          else
          ret_err(gen_err); /* error */          {
        break;              baddr->next = daemon->ignore_addr;
      }            daemon->ignore_addr = baddr;
           }
 
         baddr->prefix = prefix;
         baddr->is6 = is6;
         baddr->addr = addr;
         break;
      }
               
     case 'a':  /* --listen-address */      case 'a':  /* --listen-address */
     case LOPT_AUTHPEER: /* --auth-peer */      case LOPT_AUTHPEER: /* --auth-peer */
Line 2544  static int one_opt(int option, char *arg, char *errstr Line 2949  static int one_opt(int option, char *arg, char *errstr
       } while (arg);        } while (arg);
       break;        break;
               
       case LOPT_NO_REBIND: /*  --rebind-domain-ok */
         {
           struct rebind_domain *new;
   
           unhide_metas(arg);
   
           if (*arg == '/')
             arg++;
           
           do {
             comma = split_chr(arg, '/');
             new = opt_malloc(sizeof(struct  rebind_domain));
             new->domain = canonicalise_opt(arg);
             new->next = daemon->no_rebind;
             daemon->no_rebind = new;
             arg = comma;
           } while (arg && *arg);
   
           break;
         }
         
     case 'S':            /*  --server */      case 'S':            /*  --server */
     case LOPT_LOCAL:     /*  --local */      case LOPT_LOCAL:     /*  --local */
     case 'A':            /*  --address */      case 'A':            /*  --address */
     case LOPT_NO_REBIND: /*  --rebind-domain-ok */  
       {        {
        struct server *serv, *newlist = NULL;        char *lastdomain = NULL, *domain = "", *cur_domain;
                u16 flags = 0;
         char *err;
         union all_addr addr;
         union mysockaddr serv_addr, source_addr;
         char interface[IF_NAMESIZE+1];
         struct server_details sdetails;
 
         memset(&sdetails, 0, sizeof(struct server_details));
         sdetails.addr = &serv_addr;
         sdetails.source_addr = &source_addr;
         sdetails.interface = interface;
         sdetails.flags = &flags;
                         
         unhide_metas(arg);          unhide_metas(arg);
                   
        if (arg && (*arg == '/' || option == LOPT_NO_REBIND))        /* split the domain args, if any and skip to the end of them. */
         if (arg && *arg == '/')
           {            {
            int rebind = !(*arg == '/');            char *last;
            char *end = NULL;
            if (!rebind)            domain = lastdomain = ++arg;
              arg++;            
            while (rebind || (end = split_chr(arg, '/')))            while ((last = split_chr(arg, '/')))
               {                {
                char *domain = NULL;                lastdomain = arg;
                /* elide leading dots - they are implied in the search algorithm */                arg = last;
                while (*arg == '.') arg++; 
                /* # matches everything and becomes a zero length domain string */ 
                if (strcmp(arg, "#") == 0) 
                  domain = ""; 
                else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg))) 
                  ret_err(gen_err); 
                serv = opt_malloc(sizeof(struct server)); 
                memset(serv, 0, sizeof(struct server)); 
                serv->next = newlist; 
                newlist = serv; 
                serv->domain = domain; 
                serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS; 
                arg = end; 
                if (rebind) 
                  break; 
               }                }
             if (!newlist)  
               ret_err(gen_err);  
           }            }
         else  
           {  
             newlist = opt_malloc(sizeof(struct server));  
             memset(newlist, 0, sizeof(struct server));  
 #ifdef HAVE_LOOP  
             newlist->uid = rand32();  
 #endif  
           }  
                   
        if (servers_only && option == 'S')        if (!arg || !*arg)
          newlist->flags |= SERV_FROM_FILE;          flags = SERV_LITERAL_ADDRESS;
                else if (option == 'A')
        if (option == 'A') 
           {            {
            newlist->flags |= SERV_LITERAL_ADDRESS;            /* # as literal address means return zero address for 4 and 6 */
            if (!(newlist->flags & SERV_TYPE))            if (strcmp(arg, "#") == 0)
              {              flags = SERV_ALL_ZEROS | SERV_LITERAL_ADDRESS;
                server_list_free(newlist);            else if (inet_pton(AF_INET, arg, &addr.addr4) > 0)
                ret_err(gen_err);              flags = SERV_4ADDR | SERV_LITERAL_ADDRESS;
              }            else if (inet_pton(AF_INET6, arg, &addr.addr6) > 0)
               flags = SERV_6ADDR | SERV_LITERAL_ADDRESS;
             else
               ret_err(_("Bad address in --address"));
           }            }
        else if (option == LOPT_NO_REBIND)        else
          newlist->flags |= SERV_NO_REBIND; 
         
        if (!arg || !*arg) 
           {            {
            if (!(newlist->flags & SERV_NO_REBIND))            if ((err = parse_server(arg, &sdetails)))
              newlist->flags |= SERV_NO_ADDR; /* no server */              ret_err(err);
           }            }
   
        else if (strcmp(arg, "#") == 0)        if (servers_only && option == 'S')
          newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */          flags |= SERV_FROM_FILE;
        else
         cur_domain = domain;
         while ((flags & SERV_LITERAL_ADDRESS) || parse_server_next(&sdetails))
           {            {
            char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);            cur_domain = domain;
            if (err)
             if (!(flags & SERV_LITERAL_ADDRESS) && (err = parse_server_addr(&sdetails)))
               ret_err(err);
 
             /* When source is set only use DNS records of the same type and skip all others */
             if (flags & SERV_HAS_SOURCE && sdetails.addr_type != sdetails.source_addr->sa.sa_family)
               continue;
 
             while (1)
               {                {
                server_list_free(newlist);                /* server=//1.2.3.4 is special. */
                ret_err(err);                if (lastdomain)
                   {
                     if (strlen(cur_domain) == 0)
                       flags |= SERV_FOR_NODOTS;
                     else
                       flags &= ~SERV_FOR_NODOTS;
                     
                     /* address=/#/ matches the same as without domain */
                     if (option == 'A' && cur_domain[0] == '#' && cur_domain[1] == 0)
                       cur_domain[0] = 0;
                   }
                 
                 if (!add_update_server(flags, sdetails.addr, sdetails.source_addr, sdetails.interface, cur_domain, &addr))
                   ret_err(gen_err);
                 
                 if (!lastdomain || cur_domain == lastdomain)
                   break;
 
                 cur_domain += strlen(cur_domain) + 1;
               }                }
   
               if (flags & SERV_LITERAL_ADDRESS)
                 break;
           }            }
   
           if (sdetails.orig_hostinfo)
             freeaddrinfo(sdetails.orig_hostinfo);
                   
        serv = newlist;        break;
        while (serv->next) 
          { 
            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); 
            serv = serv->next; 
          } 
        serv->next = daemon->servers; 
        daemon->servers = newlist; 
        break; 
       }        }
   
     case LOPT_REV_SERV: /* --rev-server */      case LOPT_REV_SERV: /* --rev-server */
       {        {
         char *string;          char *string;
         int size;          int size;
         struct server *serv;  
         struct in_addr addr4;          struct in_addr addr4;
         struct in6_addr addr6;          struct in6_addr addr6;
         
         unhide_metas(arg);          unhide_metas(arg);
         if (!arg)          if (!arg)
           ret_err(gen_err);            ret_err(gen_err);
                   
         comma=split(arg);          comma=split(arg);
   
         if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))  
           ret_err(gen_err);  
                   
           if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
             size = -1;
   
         if (inet_pton(AF_INET, arg, &addr4))          if (inet_pton(AF_INET, arg, &addr4))
           {            {
            serv = add_rev4(addr4, size);           if (size == -1)
            if (!serv)             size = 32;
              ret_err(_("bad prefix"));
            if ((string = domain_rev4(servers_only, comma, &addr4, size)))
               ret_err(string);
           }            }
         else if (inet_pton(AF_INET6, arg, &addr6))          else if (inet_pton(AF_INET6, arg, &addr6))
          serv = add_rev6(&addr6, size);          {
              if (size == -1)
                size = 128;
 
              if ((string = domain_rev6(servers_only, comma, &addr6, size)))
               ret_err(string);
           }
         else          else
           ret_err(gen_err);            ret_err(gen_err);
    
         string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);  
                   
         if (string)  
           ret_err(string);  
           
         if (servers_only)  
           serv->flags |= SERV_FROM_FILE;  
           
         break;          break;
       }        }
   
     case LOPT_IPSET: /* --ipset */      case LOPT_IPSET: /* --ipset */
       case LOPT_NFTSET: /* --nftset */
 #ifndef HAVE_IPSET  #ifndef HAVE_IPSET
      ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));      if (option == LOPT_IPSET)
      break;        {
#else          ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
           break;
         }
 #endif
 #ifndef HAVE_NFTSET
       if (option == LOPT_NFTSET)
         {
           ret_err(_("recompile with HAVE_NFTSET defined to enable nftset directives"));
           break;
         }
 #endif
 
       {        {
          struct ipsets ipsets_head;           struct ipsets ipsets_head;
          struct ipsets *ipsets = &ipsets_head;           struct ipsets *ipsets = &ipsets_head;
            struct ipsets **daemon_sets =
              (option == LOPT_IPSET) ? &daemon->ipsets : &daemon->nftsets;
          int size;           int size;
          char *end;           char *end;
          char **sets, **sets_pos;           char **sets, **sets_pos;
Line 2729  static int one_opt(int option, char *arg, char *errstr Line 3177  static int one_opt(int option, char *arg, char *errstr
          sets = sets_pos = opt_malloc(sizeof(char *) * size);           sets = sets_pos = opt_malloc(sizeof(char *) * size);
                     
          do {           do {
              char *p;
            end = split(arg);             end = split(arg);
           *sets_pos++ = opt_string_alloc(arg);           *sets_pos = opt_string_alloc(arg);
            /* Use '#' to delimit table and set */
            if (option == LOPT_NFTSET)
              while ((p = strchr(*sets_pos, '#')))
                *p = ' ';
            sets_pos++;
            arg = end;             arg = end;
          } while (end);           } while (end);
          *sets_pos = 0;           *sets_pos = 0;
          for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)           for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
            ipsets->next->sets = sets;             ipsets->next->sets = sets;
         ipsets->next = daemon->ipsets;         ipsets->next = *daemon_sets;
         daemon->ipsets = ipsets_head.next;         *daemon_sets = ipsets_head.next;
                     
          break;           break;
       }        }
         
       case LOPT_CMARK_ALST_EN: /* --connmark-allowlist-enable */
   #ifndef HAVE_CONNTRACK
         ret_err(_("recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives"));
         break;
   #else
         {
           u32 mask = UINT32_MAX;
           
           if (arg)
             if (!strtoul_check(arg, &mask) || mask < 1)
               ret_err(gen_err);
           
           set_option_bool(OPT_CMARK_ALST_EN);
           daemon->allowlist_mask = mask;
           break;
         }
 #endif  #endif
               
       case LOPT_CMARK_ALST: /* --connmark-allowlist */
   #ifndef HAVE_CONNTRACK
           ret_err(_("recompile with HAVE_CONNTRACK defined to enable connmark-allowlist directives"));
           break;
   #else
         {
           struct allowlist *allowlists;
           char **patterns, **patterns_pos;
           u32 mark, mask = UINT32_MAX;
           size_t num_patterns = 0;
           
           char *c, *m = NULL;
           char *separator;
           unhide_metas(arg);
           if (!arg)
             ret_err(gen_err);
           c = arg;
           if (*c < '0' || *c > '9')
             ret_err(gen_err);
           while (*c && *c != ',')
             {
               if (*c == '/')
                 {
                   if (m)
                     ret_err(gen_err);
                   *c = '\0';
                   m = ++c;
                 }
               if (*c < '0' || *c > '9')
                 ret_err(gen_err);
               c++;
             }
           separator = c;
           if (!*separator)
             break;
           while (c && *c)
             {
               char *end = strchr(++c, '/');
               if (end)
                 *end = '\0';
               if (strcmp(c, "*") && !is_valid_dns_name_pattern(c))
                 ret_err(gen_err);
               if (end)
                 *end = '/';
               if (num_patterns >= UINT16_MAX - 1)
                 ret_err(gen_err);
               num_patterns++;
               c = end;
             }
           
           *separator = '\0';
           if (!strtoul_check(arg, &mark) || mark < 1 || mark > UINT32_MAX)
             ret_err(gen_err);
           if (m)
             if (!strtoul_check(m, &mask) || mask < 1 || mask > UINT32_MAX || (mark & ~mask))
               ret_err(gen_err);
           if (num_patterns)
             *separator = ',';
           for (allowlists = daemon->allowlists; allowlists; allowlists = allowlists->next)
             if (allowlists->mark == mark && allowlists->mask == mask)
               ret_err(gen_err);
           
           patterns = opt_malloc((num_patterns + 1) * sizeof(char *));
           if (!patterns)
             goto fail_cmark_allowlist;
           patterns_pos = patterns;
           c = separator;
           while (c && *c)
           {
             char *end = strchr(++c, '/');
             if (end)
               *end = '\0';
             if (!(*patterns_pos++ = opt_string_alloc(c)))
               goto fail_cmark_allowlist;
             if (end)
               *end = '/';
             c = end;
           }
           *patterns_pos++ = NULL;
           
           allowlists = opt_malloc(sizeof(struct allowlist));
           if (!allowlists)
             goto fail_cmark_allowlist;
           memset(allowlists, 0, sizeof(struct allowlist));
           allowlists->mark = mark;
           allowlists->mask = mask;
           allowlists->patterns = patterns;
           allowlists->next = daemon->allowlists;
           daemon->allowlists = allowlists;
           break;
           
         fail_cmark_allowlist:
           if (patterns)
             {
               for (patterns_pos = patterns; *patterns_pos; patterns_pos++)
                 {
                   free(*patterns_pos);
                   *patterns_pos = NULL;
                 }
               free(patterns);
               patterns = NULL;
             }
           if (allowlists)
             {
               free(allowlists);
               allowlists = NULL;
             }
           ret_err(gen_err);
         }
   #endif
         
     case 'c':  /* --cache-size */      case 'c':  /* --cache-size */
       {        {
         int size;          int size;
Line 2820  static int one_opt(int option, char *arg, char *errstr Line 3402  static int one_opt(int option, char *arg, char *errstr
       if (daemon->query_port == 0)        if (daemon->query_port == 0)
         daemon->osport = 1;          daemon->osport = 1;
       break;        break;
   
       case LOPT_RANDPORT_LIM: /* --port-limit */
         if (!atoi_check(arg, &daemon->randport_limit) || (daemon->randport_limit < 1))
           ret_err(gen_err);
         break;
               
     case 'T':         /* --local-ttl */      case 'T':         /* --local-ttl */
     case LOPT_NEGTTL: /* --neg-ttl */      case LOPT_NEGTTL: /* --neg-ttl */
Line 2855  static int one_opt(int option, char *arg, char *errstr Line 3442  static int one_opt(int option, char *arg, char *errstr
           daemon->local_ttl = (unsigned long)ttl;            daemon->local_ttl = (unsigned long)ttl;
         break;          break;
       }        }
   
       case LOPT_FAST_RETRY:
         daemon->fast_retry_timeout = TIMEOUT;
               
         if (!arg)
           daemon->fast_retry_time = DEFAULT_FAST_RETRY;
         else
           {
             int retry;
             
             comma = split(arg);
             if (!atoi_check(arg, &retry) || retry < 50)
               ret_err(gen_err);
             daemon->fast_retry_time = retry;
             
             if (comma)
               {
                 if (!atoi_check(comma, &retry))
                   ret_err(gen_err);
                 daemon->fast_retry_timeout = retry/1000;
               }
           }
         break;
               
 #ifdef HAVE_DHCP  #ifdef HAVE_DHCP
     case 'X': /* --dhcp-lease-max */      case 'X': /* --dhcp-lease-max */
       if (!atoi_check(arg, &daemon->dhcp_max))        if (!atoi_check(arg, &daemon->dhcp_max))
Line 3389  static int one_opt(int option, char *arg, char *errstr Line 3999  static int one_opt(int option, char *arg, char *errstr
                 for (configs = daemon->dhcp_conf; configs; configs = configs->next)                   for (configs = daemon->dhcp_conf; configs; configs = configs->next) 
                   if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)                    if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
                     {                      {
                      sprintf(errstr, _("duplicate dhcp-host IP address %s"),  inet_ntoa(in));                      inet_ntop(AF_INET, &in, daemon->addrbuff, ADDRSTRLEN);
                       sprintf(errstr, _("duplicate dhcp-host IP address %s"),
                               daemon->addrbuff);
                       dhcp_config_free(new);
                       return 0;                        return 0;
                     }                               }         
               }                }
Line 3553  static int one_opt(int option, char *arg, char *errstr Line 4166  static int one_opt(int option, char *arg, char *errstr
   
     case LOPT_NAME_MATCH: /* --dhcp-name-match */      case LOPT_NAME_MATCH: /* --dhcp-name-match */
       {        {
        struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));        struct dhcp_match_name *new;
        struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid)); 
         ssize_t len;          ssize_t len;
                   
         if (!(comma = split(arg)) || (len = strlen(comma)) == 0)          if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
           ret_err(gen_err);            ret_err(gen_err);
   
           new = opt_malloc(sizeof(struct dhcp_match_name));
         new->wildcard = 0;          new->wildcard = 0;
        new->netid = id;        new->netid = opt_malloc(sizeof(struct dhcp_netid));
        id->net = opt_string_alloc(set_prefix(arg));        new->netid->net = opt_string_alloc(set_prefix(arg));
   
         if (comma[len-1] == '*')          if (comma[len-1] == '*')
           {            {
Line 3766  static int one_opt(int option, char *arg, char *errstr Line 4379  static int one_opt(int option, char *arg, char *errstr
                }                 }
            }             }
                     
            dhcp_netid_free(new->netid);
            free(new);
          ret_err(gen_err);           ret_err(gen_err);
        }         }
                     
Line 3800  static int one_opt(int option, char *arg, char *errstr Line 4415  static int one_opt(int option, char *arg, char *errstr
     case LOPT_SUBSCR:   /* --dhcp-subscrid */      case LOPT_SUBSCR:   /* --dhcp-subscrid */
       {        {
          unsigned char *p;           unsigned char *p;
         int dig = 0;         int dig, colon;
          struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));           struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
                     
          if (!(comma = split(arg)))           if (!(comma = split(arg)))
Line 3824  static int one_opt(int option, char *arg, char *errstr Line 4439  static int one_opt(int option, char *arg, char *errstr
          else           else
            comma = arg;             comma = arg;
                     
         for (p = (unsigned char *)comma; *p; p++)         for (dig = 0, colon = 0, p = (unsigned char *)comma; *p; p++)
            if (isxdigit(*p))             if (isxdigit(*p))
              dig = 1;               dig = 1;
           else if (*p != ':')           else if (*p == ':')
              colon = 1;
            else
              break;               break;
            
          unhide_metas(comma);           unhide_metas(comma);
         if (option == 'U' || option == 'j' || *p || !dig)         if (option == 'U' || option == 'j' || *p || !dig || !colon)
            {             {
              new->len = strlen(comma);                 new->len = strlen(comma);  
              new->data = opt_malloc(new->len);               new->data = opt_malloc(new->len);
Line 3953  static int one_opt(int option, char *arg, char *errstr Line 4571  static int one_opt(int option, char *arg, char *errstr
         }          }
       }        }
       break;        break;
      
     case LOPT_RELAY: /* --dhcp-relay */      case LOPT_RELAY: /* --dhcp-relay */
       {        {
         struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));          struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
        comma = split(arg);        char *two = split(arg);
        new->interface = opt_string_alloc(split(comma));        char *three = split(two);
         
         new->iface_index = 0;          new->iface_index = 0;
        if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
         if (two)
           {            {
            new->next = daemon->relay4;            if (inet_pton(AF_INET, arg, &new->local))
            daemon->relay4 = new;              {
          }                char *hash = split_chr(two, '#');
 
                 if (!hash || !atoi_check16(hash, &new->port))
                   new->port = DHCP_SERVER_PORT;
                 
                 if (!inet_pton(AF_INET, two, &new->server))
                   {
                     new->server.addr4.s_addr = 0;
                                     
                     /* Fail for three arg version where there are not two addresses. 
                        Also fail when broadcasting to wildcard address. */
                     if (three || strchr(two, '*'))
                       two = NULL;
                     else
                       three = two;
                   }
                 
                 new->next = daemon->relay4;
                 daemon->relay4 = new;
               }
 #ifdef HAVE_DHCP6  #ifdef HAVE_DHCP6
        else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))            else if (inet_pton(AF_INET6, arg, &new->local))
          {              {
            new->next = daemon->relay6;                char *hash = split_chr(two, '#');
            daemon->relay6 = new;
          }                if (!hash || !atoi_check16(hash, &new->port))
                   new->port = DHCPV6_SERVER_PORT;
 
                 if (!inet_pton(AF_INET6, two, &new->server))
                   {
                     inet_pton(AF_INET6, ALL_SERVERS, &new->server.addr6);
                     /* Fail for three arg version where there are not two addresses.
                        Also fail when multicasting to wildcard address. */
                     if (three || strchr(two, '*'))
                       two = NULL;
                     else
                       three = two;
                   }
                 new->next = daemon->relay6;
                 daemon->relay6 = new;
               }
 #endif  #endif
        else
             new->interface = opt_string_alloc(three);
           }
         
         if (!two)
           {            {
             free(new->interface);              free(new->interface);
             ret_err_free(_("Bad dhcp-relay"), new);              ret_err_free(_("Bad dhcp-relay"), new);
Line 4075  err: Line 4733  err:
       }        }
               
     case LOPT_INTNAME:  /* --interface-name */      case LOPT_INTNAME:  /* --interface-name */
       case LOPT_DYNHOST:  /* --dynamic-host */
       {        {
         struct interface_name *new, **up;          struct interface_name *new, **up;
        char *domain = NULL;        char *domain = arg;
 
        comma = split(arg); 
                   
        if (!comma || !(domain = canonicalise_opt(arg)))        arg = split(arg);
          ret_err(_("bad interface name")); 
                   
         new = opt_malloc(sizeof(struct interface_name));          new = opt_malloc(sizeof(struct interface_name));
        new->next = NULL;        memset(new, 0, sizeof(struct interface_name));
        new->addr = NULL;        new->flags = IN4 | IN6;
                   
         /* Add to the end of the list, so that first name          /* Add to the end of the list, so that first name
            of an interface is used for PTR lookups. */             of an interface is used for PTR lookups. */
         for (up = &daemon->int_names; *up; up = &((*up)->next));          for (up = &daemon->int_names; *up; up = &((*up)->next));
         *up = new;          *up = new;
        new->name = domain;        
        new->family = 0;        while ((comma = split(arg)))
        arg = split_chr(comma, '/'); 
        if (arg) 
           {            {
            if (strcmp(arg, "4") == 0)            if (inet_pton(AF_INET, arg, &new->proto4))
              new->family = AF_INET;              new->flags |= INP4;
            else if (strcmp(arg, "6") == 0)            else if (inet_pton(AF_INET6, arg, &new->proto6))
              new->family = AF_INET6;              new->flags |= INP6;
             else              else
                 break;
               
               arg = comma;
             }
   
           if ((comma = split_chr(arg, '/')))
             {
               if (strcmp(comma, "4") == 0)
                 new->flags &= ~IN6;
               else if (strcmp(comma, "6") == 0)
                 new->flags &= ~IN4;
               else
               ret_err_free(gen_err, new);                ret_err_free(gen_err, new);
          }           }
        new->intr = opt_string_alloc(comma);
         new->intr = opt_string_alloc(arg);
 
         if (option == LOPT_DYNHOST)
           {
             if (!(new->flags & (INP4 | INP6)))
               ret_err(_("missing address in dynamic host"));
 
             if (!(new->flags & IN4) || !(new->flags & IN6))
               arg = NULL; /* provoke error below */
 
             new->flags &= ~(IN4 | IN6);
           }
         else
           {
             if (new->flags & (INP4 | INP6))
               arg = NULL; /* provoke error below */
           }
         
         if (!domain || !arg || !(new->name = canonicalise_opt(domain)))
           ret_err(option == LOPT_DYNHOST ?
                   _("bad dynamic host") : _("bad interface name"));
         
         break;          break;
       }        }
               
     case LOPT_CNAME: /* --cname */      case LOPT_CNAME: /* --cname */
       {        {
         struct cname *new;          struct cname *new;
        char *alias, *target, *last, *pen;        char *alias, *target=NULL, *last, *pen;
         int ttl = -1;          int ttl = -1;
   
         for (last = pen = NULL, comma = arg; comma; comma = split(comma))          for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Line 4126  err: Line 4814  err:
         if (pen != arg && atoi_check(last, &ttl))          if (pen != arg && atoi_check(last, &ttl))
           last = pen;            last = pen;
                                   
         target = canonicalise_opt(last);  
   
         while (arg != last)          while (arg != last)
           {            {
             int arglen = strlen(arg);              int arglen = strlen(arg);
             alias = canonicalise_opt(arg);              alias = canonicalise_opt(arg);
   
               if (!target)
                 target = canonicalise_opt(last);
             if (!alias || !target)              if (!alias || !target)
               {                {
                 free(target);                  free(target);
Line 4154  err: Line 4842  err:
             new->target = target;              new->target = target;
             new->ttl = ttl;              new->ttl = ttl;
   
            for (arg += arglen+1; *arg && isspace(*arg); arg++);            for (arg += arglen+1; *arg && isspace((unsigned char)*arg); arg++);
           }            }
               
         break;          break;
Line 4431  err: Line 5119  err:
               }                }
             else              else
               {                {
                int nomem;                char *canon = canonicalise_opt(arg);
                char *canon = canonicalise(arg, &nomem); 
                 struct name_list *nl;                  struct name_list *nl;
                 if (!canon)                  if (!canon)
                   {                    {
                    struct name_list *tmp = new->names, *next;                    struct name_list *tmp, *next;
                     for (tmp = new->names; tmp; tmp = next)                      for (tmp = new->names; tmp; tmp = next)
                       {                        {
                         next = tmp->next;                          next = tmp->next;
Line 4473  err: Line 5160  err:
         break;          break;
       }        }
   
       case LOPT_STALE_CACHE:
         {
           int max_expiry = STALE_CACHE_EXPIRY;
           if (arg)
             {
               /* Don't accept negative TTLs here, they'd have the counter-intuitive
                  side-effect of evicting cache records before they expire */
               if (!atoi_check(arg, &max_expiry) || max_expiry < 0)
                 ret_err(gen_err);
               /* Store "serve expired forever" as -1 internally, the option isn't
                  active for daemon->cache_max_expiry == 0 */
               if (max_expiry == 0)
                 max_expiry = -1;
             }
           daemon->cache_max_expiry = max_expiry;
           break;
         }
   
 #ifdef HAVE_DNSSEC  #ifdef HAVE_DNSSEC
     case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */      case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
       daemon->timestamp_file = opt_string_alloc(arg);         daemon->timestamp_file = opt_string_alloc(arg); 
Line 4528  err: Line 5233  err:
         unhide_metas(keyhex);          unhide_metas(keyhex);
         /* 4034: "Whitespace is allowed within digits" */          /* 4034: "Whitespace is allowed within digits" */
         for (cp = keyhex; *cp; )          for (cp = keyhex; *cp; )
          if (isspace(*cp))          if (isspace((unsigned char)*cp))
             for (cp1 = cp; *cp1; cp1++)              for (cp1 = cp; *cp1; cp1++)
               *cp1 = *(cp1+1);                *cp1 = *(cp1+1);
           else            else
Line 4554  err: Line 5259  err:
   return 1;    return 1;
 }  }
   
static void read_file(char *file, FILE *f, int hard_opt)       static void read_file(char *file, FILE *f, int hard_opt, int from_script)      
 {  {
   volatile int lineno = 0;    volatile int lineno = 0;
   char *buff = daemon->namebuff;    char *buff = daemon->namebuff;
Line 4562  static void read_file(char *file, FILE *f, int hard_op Line 5267  static void read_file(char *file, FILE *f, int hard_op
   while (fgets(buff, MAXDNAME, f))    while (fgets(buff, MAXDNAME, f))
     {      {
       int white, i;        int white, i;
      volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;      volatile int option;
       char *errmess, *p, *arg, *start;        char *errmess, *p, *arg, *start;
       size_t len;        size_t len;
   
         option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
   
       /* Memory allocation failure longjmps here if mem_recover == 1 */         /* Memory allocation failure longjmps here if mem_recover == 1 */ 
       if (option != 0 || hard_opt == LOPT_REV_SERV)        if (option != 0 || hard_opt == LOPT_REV_SERV)
         {          {
Line 4573  static void read_file(char *file, FILE *f, int hard_op Line 5280  static void read_file(char *file, FILE *f, int hard_op
             continue;              continue;
           mem_recover = 1;            mem_recover = 1;
         }          }
      
       arg = NULL;        arg = NULL;
       lineno++;        lineno++;
       errmess = NULL;        errmess = NULL;
Line 4614  static void read_file(char *file, FILE *f, int hard_op Line 5321  static void read_file(char *file, FILE *f, int hard_op
               memmove(p, p+1, strlen(p+1)+1);                memmove(p, p+1, strlen(p+1)+1);
             }              }
   
          if (isspace(*p))          if (isspace((unsigned char)*p))
             {              {
               *p = ' ';                *p = ' ';
               white = 1;                white = 1;
Line 4679  static void read_file(char *file, FILE *f, int hard_op Line 5386  static void read_file(char *file, FILE *f, int hard_op
                       
       if (errmess || !one_opt(option, arg, daemon->namebuff, _("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 (from_script)
             sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" in output from %s"), file);
           else
             sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
           
           if (hard_opt != 0)            if (hard_opt != 0)
             my_syslog(LOG_ERR, "%s", daemon->namebuff);              my_syslog(LOG_ERR, "%s", daemon->namebuff);
           else            else
Line 4688  static void read_file(char *file, FILE *f, int hard_op Line 5399  static void read_file(char *file, FILE *f, int hard_op
     }      }
   
   mem_recover = 0;    mem_recover = 0;
   fclose(f);  
 }  }
   
 #if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)  #if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Line 4708  int option_read_dynfile(char *file, int flags) Line 5418  int option_read_dynfile(char *file, int flags)
 static int one_file(char *file, int hard_opt)  static int one_file(char *file, int hard_opt)
 {  {
   FILE *f;    FILE *f;
  int nofile_ok = 0;  int nofile_ok = 0, do_popen = 0;
   static int read_stdin = 0;    static int read_stdin = 0;
   static struct fileread {    static struct fileread {
     dev_t dev;      dev_t dev;
Line 4716  static int one_file(char *file, int hard_opt) Line 5426  static int one_file(char *file, int hard_opt)
     struct fileread *next;      struct fileread *next;
   } *filesread = NULL;    } *filesread = NULL;
       
  if (hard_opt == '7')  if (hard_opt == LOPT_CONF_OPT)
     {      {
       /* default conf-file reading */        /* default conf-file reading */
       hard_opt = 0;        hard_opt = 0;
       nofile_ok = 1;        nofile_ok = 1;
     }      }
   
  if (hard_opt == 0 && strcmp(file, "-") == 0)   if (hard_opt == LOPT_CONF_SCRIPT)
      {
        hard_opt = 0;
        do_popen = 1;
      }
    
    if (hard_opt == 0 && !do_popen && strcmp(file, "-") == 0)
     {      {
       if (read_stdin == 1)        if (read_stdin == 1)
         return 1;          return 1;
Line 4750  static int one_file(char *file, int hard_opt) Line 5466  static int one_file(char *file, int hard_opt)
           r->dev = statbuf.st_dev;            r->dev = statbuf.st_dev;
           r->ino = statbuf.st_ino;            r->ino = statbuf.st_ino;
         }          }
      
      if (!(f = fopen(file, "r")))      if (do_popen)
         {
           if (!(f = popen(file, "r")))
             die(_("cannot execute %s: %s"), file, EC_FILE);
         }
       else if (!(f = fopen(file, "r")))
         {             {   
           if (errno == ENOENT && nofile_ok)            if (errno == ENOENT && nofile_ok)
             return 1; /* No conffile, all done. */              return 1; /* No conffile, all done. */
Line 4769  static int one_file(char *file, int hard_opt) Line 5490  static int one_file(char *file, int hard_opt)
         }           } 
     }      }
       
  read_file(file, f, hard_opt);   read_file(file, f, hard_opt, do_popen);
 
   if (do_popen)
     {
       int rc;
 
       if ((rc = pclose(f)) == -1)
         die(_("error executing %s: %s"), file, EC_MISC);
 
       if (rc != 0)
         die(_("%s returns non-zero error code"), file, rc+10);
     }
   else
     fclose(f);
         
   return 1;    return 1;
 }  }
   
   static int file_filter(const struct dirent *ent)
   {
     size_t lenfile = strlen(ent->d_name);
   
     /* ignore emacs backups and dotfiles */
   
     if (lenfile == 0 || 
         ent->d_name[lenfile - 1] == '~' ||
         (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
         ent->d_name[0] == '.')
       return 0;
   
     return 1;
   }
 /* expand any name which is a directory */  /* expand any name which is a directory */
 struct hostsfile *expand_filelist(struct hostsfile *list)  struct hostsfile *expand_filelist(struct hostsfile *list)
 {  {
   unsigned int i;    unsigned int i;
  struct hostsfile *ah;  int entcnt, n;
   struct hostsfile *ah, *last, *next, **up;
   struct dirent **namelist;
   
   /* find largest used index */    /* find largest used index */
   for (i = SRC_AH, ah = list; ah; ah = ah->next)    for (i = SRC_AH, ah = list; ah; ah = ah->next)
     {      {
         last = ah;
         
       if (i <= ah->index)        if (i <= ah->index)
         i = ah->index + 1;          i = ah->index + 1;
   
Line 4797  struct hostsfile *expand_filelist(struct hostsfile *li Line 5550  struct hostsfile *expand_filelist(struct hostsfile *li
         struct stat buf;          struct stat buf;
         if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))          if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
           {            {
             DIR *dir_stream;  
             struct dirent *ent;              struct dirent *ent;
                           
             /* don't read this as a file */              /* don't read this as a file */
             ah->flags |= AH_INACTIVE;              ah->flags |= AH_INACTIVE;
                           
            if (!(dir_stream = opendir(ah->fname)))            entcnt = scandir(ah->fname, &namelist, file_filter, alphasort);
             if (entcnt < 0)
               my_syslog(LOG_ERR, _("cannot access directory %s: %s"),                 my_syslog(LOG_ERR, _("cannot access directory %s: %s"), 
                         ah->fname, strerror(errno));                          ah->fname, strerror(errno));
             else              else
               {                {
                while ((ent = readdir(dir_stream)))                for (n = 0; n < entcnt; n++)
                   {                    {
                       ent = namelist[n];
                     size_t lendir = strlen(ah->fname);                      size_t lendir = strlen(ah->fname);
                     size_t lenfile = strlen(ent->d_name);                      size_t lenfile = strlen(ent->d_name);
                     struct hostsfile *ah1;                      struct hostsfile *ah1;
                     char *path;                      char *path;
                                           
                     /* ignore emacs backups and dotfiles */  
                     if (lenfile == 0 ||   
                         ent->d_name[lenfile - 1] == '~' ||  
                         (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||  
                         ent->d_name[0] == '.')  
                       continue;  
                       
                     /* see if we have an existing record.                      /* see if we have an existing record.
                        dir is ah->fname                          dir is ah->fname 
                        file is ent->d_name                         file is ent->d_name
                        path to match is ah1->fname */                         path to match is ah1->fname */
                                           
                    for (ah1 = list; ah1; ah1 = ah1->next)                    for (up = &list, ah1 = list; ah1; ah1 = next)
                       {                        {
                           next = ah1->next;
   
                         if (lendir < strlen(ah1->fname) &&                          if (lendir < strlen(ah1->fname) &&
                             strstr(ah1->fname, ah->fname) == ah1->fname &&                              strstr(ah1->fname, ah->fname) == ah1->fname &&
                             ah1->fname[lendir] == '/' &&                              ah1->fname[lendir] == '/' &&
                             strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)                              strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
                           {                            {
                             ah1->flags &= ~AH_INACTIVE;                              ah1->flags &= ~AH_INACTIVE;
                               /* If found, remove from list to re-insert at the end.
                                  Unless it's already at the end. */
                               if (last != ah1)
                                 *up = next;
                             break;                              break;
                           }                            }
   
                           up = &ah1->next;
                       }                        }
                                           
                     /* make new record */                      /* make new record */
Line 4857  struct hostsfile *expand_filelist(struct hostsfile *li Line 5612  struct hostsfile *expand_filelist(struct hostsfile *li
                         ah1->fname = path;                          ah1->fname = path;
                         ah1->index = i++;                          ah1->index = i++;
                         ah1->flags = AH_DIR;                          ah1->flags = AH_DIR;
                         ah1->next = list;  
                         list = ah1;  
                       }                        }
   
                       /* Edge case, may be the last in the list anyway */
                       if (last != ah1)
                         last->next = ah1;
                       ah1->next = NULL;
                       last = ah1;
                                           
                     /* inactivate record if not regular file */                      /* inactivate record if not regular file */
                     if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))                      if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
                       ah1->flags |= AH_INACTIVE;                         ah1->flags |= AH_INACTIVE; 
                                           
                   }                    }
                 closedir(dir_stream);  
               }                }
               free(namelist);
           }            }
       }        }
       
Line 4885  void read_servers_file(void) Line 5644  void read_servers_file(void)
     }      }
       
   mark_servers(SERV_FROM_FILE);    mark_servers(SERV_FROM_FILE);
     read_file(daemon->servers_file, f, LOPT_REV_SERV, 0);
     fclose(f);
   cleanup_servers();    cleanup_servers();
    check_servers(0);
  read_file(daemon->servers_file, f, LOPT_REV_SERV); 
 }  }
     
   
Line 4903  static void clear_dynamic_conf(void) Line 5663  static void clear_dynamic_conf(void)
               
       if (configs->flags & CONFIG_BANK)        if (configs->flags & CONFIG_BANK)
         {          {
          struct hwaddr_config *mac, *tmp;          *up = cp;
          struct dhcp_netid_list *list, *tmplist;          dhcp_config_free(configs);
           
          for (mac = configs->hwaddr; mac; mac = tmp) 
            { 
              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        else
         up = &configs->next;          up = &configs->next;
Line 4936  static void clear_dynamic_conf(void) Line 5674  static void clear_dynamic_conf(void)
 static void clear_dynamic_opt(void)  static void clear_dynamic_opt(void)
 {  {
   struct dhcp_opt *opts, *cp, **up;    struct dhcp_opt *opts, *cp, **up;
   struct dhcp_netid *id, *next;  
   
   for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)    for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
     {      {
Line 4944  static void clear_dynamic_opt(void) Line 5681  static void clear_dynamic_opt(void)
               
       if (opts->flags & DHOPT_BANK)        if (opts->flags & DHOPT_BANK)
         {          {
          if ((opts->flags & DHOPT_VENDOR))          *up = cp;
            free(opts->u.vendor_class);          dhcp_opt_free(opts);
          free(opts->val); 
          for (id = opts->netid; id; id = next) 
            { 
              next = id->next; 
              free(id->net); 
              free(id); 
            } 
          *up = opts->next; 
          free(opts); 
         }          }
       else        else
         up = &opts->next;          up = &opts->next;
Line 5014  void read_opts(int argc, char **argv, char *compile_op Line 5742  void read_opts(int argc, char **argv, char *compile_op
   daemon = opt_malloc(sizeof(struct daemon));    daemon = opt_malloc(sizeof(struct daemon));
   memset(daemon, 0, sizeof(struct daemon));    memset(daemon, 0, sizeof(struct daemon));
   daemon->namebuff = buff;    daemon->namebuff = buff;
  daemon->addrbuff = safe_malloc(ADDRSTRLEN);
   
   /* Set defaults - everything else is zero or NULL */    /* Set defaults - everything else is zero or NULL */
   daemon->cachesize = CACHESIZ;    daemon->cachesize = CACHESIZ;
   daemon->ftabsize = FTABSIZ;    daemon->ftabsize = FTABSIZ;
Line 5034  void read_opts(int argc, char **argv, char *compile_op Line 5763  void read_opts(int argc, char **argv, char *compile_op
   daemon->soa_refresh = SOA_REFRESH;    daemon->soa_refresh = SOA_REFRESH;
   daemon->soa_retry = SOA_RETRY;    daemon->soa_retry = SOA_RETRY;
   daemon->soa_expiry = SOA_EXPIRY;    daemon->soa_expiry = SOA_EXPIRY;
  daemon->max_port = MAX_PORT;  daemon->randport_limit = 1;
  daemon->min_port = MIN_PORT;  daemon->host_index = SRC_AH;
  
#ifndef NO_ID  /* See comment above make_servers(). Optimises server-read code. */
  add_txt("version.bind", "dnsmasq-" VERSION, 0 );  mark_servers(0);
  add_txt("authors.bind", "Simon Kelley", 0);  
  add_txt("copyright.bind", COPYRIGHT, 0); 
  add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE); 
  add_txt("insertions.bind", NULL, TXT_STAT_INSERTS); 
  add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS); 
  add_txt("misses.bind", NULL, TXT_STAT_MISSES); 
  add_txt("hits.bind", NULL, TXT_STAT_HITS); 
#ifdef HAVE_AUTH 
  add_txt("auth.bind", NULL, TXT_STAT_AUTH); 
#endif 
  add_txt("servers.bind", NULL, TXT_STAT_SERVERS); 
#endif 
 
   while (1)     while (1) 
     {      {
 #ifdef HAVE_GETOPT_LONG  #ifdef HAVE_GETOPT_LONG
Line 5144  void read_opts(int argc, char **argv, char *compile_op Line 5861  void read_opts(int argc, char **argv, char *compile_op
       free(conffile);        free(conffile);
     }      }
   else    else
    one_file(CONFFILE, '7');    one_file(CONFFILE, LOPT_CONF_OPT);
 
   /* Add TXT records if wanted */
 #ifndef NO_ID
   if (!option_bool(OPT_NO_IDENT))
     {
       add_txt("version.bind", "dnsmasq-" VERSION, 0 );
       add_txt("authors.bind", "Simon Kelley", 0);
       add_txt("copyright.bind", COPYRIGHT, 0);
       add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
       add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
       add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
       add_txt("misses.bind", NULL, TXT_STAT_MISSES);
       add_txt("hits.bind", NULL, TXT_STAT_HITS);
 #ifdef HAVE_AUTH
       add_txt("auth.bind", NULL, TXT_STAT_AUTH);
 #endif
       add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
     }
 #endif
   
   /* port might not be known when the address is parsed - fill in here */    /* port might not be known when the address is parsed - fill in here */
   if (daemon->servers)    if (daemon->servers)

Removed from v.1.1.1.4  
changed lines
  Added in v.1.1.1.5


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>