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

version 1.1.1.1, 2013/07/29 19:37:40 version 1.1.1.5, 2023/09/27 11:02:07
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2013 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 64  struct myoption { Line 64  struct myoption {
 #define OPTSTRING "951yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:6:7:8:0:3:"  #define OPTSTRING "951yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:6:7:8:0:3:"
   
 /* options which don't have a one-char version */  /* options which don't have a one-char version */
#define LOPT_RELOAD    256#define LOPT_RELOAD        256
#define LOPT_NO_NAMES  257#define LOPT_NO_NAMES      257
#define LOPT_TFTP      258#define LOPT_TFTP          258
#define LOPT_SECURE    259#define LOPT_SECURE        259
#define LOPT_PREFIX    260#define LOPT_PREFIX        260
#define LOPT_PTR       261#define LOPT_PTR           261
#define LOPT_BRIDGE    262#define LOPT_BRIDGE        262
#define LOPT_TFTP_MAX  263#define LOPT_TFTP_MAX      263
#define LOPT_FORCE     264#define LOPT_FORCE         264
#define LOPT_NOBLOCK   265#define LOPT_NOBLOCK       265
#define LOPT_LOG_OPTS  266#define LOPT_LOG_OPTS      266
#define LOPT_MAX_LOGS  267#define LOPT_MAX_LOGS      267
#define LOPT_CIRCUIT   268#define LOPT_CIRCUIT       268
#define LOPT_REMOTE    269#define LOPT_REMOTE        269
#define LOPT_SUBSCR    270#define LOPT_SUBSCR        270
#define LOPT_INTNAME   271#define LOPT_INTNAME       271
#define LOPT_BANK      272#define LOPT_BANK          272
#define LOPT_DHCP_HOST 273#define LOPT_DHCP_HOST     273
#define LOPT_APREF     274#define LOPT_APREF         274
#define LOPT_OVERRIDE  275#define LOPT_OVERRIDE      275
#define LOPT_TFTPPORTS 276#define LOPT_TFTPPORTS     276
#define LOPT_REBIND    277#define LOPT_REBIND        277
#define LOPT_NOLAST    278#define LOPT_NOLAST        278
#define LOPT_OPTS      279#define LOPT_OPTS          279
#define LOPT_DHCP_OPTS 280#define LOPT_DHCP_OPTS     280
#define LOPT_MATCH     281#define LOPT_MATCH         281
#define LOPT_BROADCAST 282#define LOPT_BROADCAST     282
#define LOPT_NEGTTL    283#define LOPT_NEGTTL        283
#define LOPT_ALTPORT   284#define LOPT_ALTPORT       284
#define LOPT_SCRIPTUSR 285#define LOPT_SCRIPTUSR     285
#define LOPT_LOCAL     286#define LOPT_LOCAL         286
#define LOPT_NAPTR     287#define LOPT_NAPTR         287
#define LOPT_MINPORT   288#define LOPT_MINPORT       288
#define LOPT_DHCP_FQDN 289#define LOPT_DHCP_FQDN     289
#define LOPT_CNAME     290#define LOPT_CNAME         290
#define LOPT_PXE_PROMT 291#define LOPT_PXE_PROMT     291
#define LOPT_PXE_SERV  292#define LOPT_PXE_SERV      292
#define LOPT_TEST      293#define LOPT_TEST          293
#define LOPT_TAG_IF    294#define LOPT_TAG_IF        294
#define LOPT_PROXY     295#define LOPT_PROXY         295
#define LOPT_GEN_NAMES 296#define LOPT_GEN_NAMES     296
#define LOPT_MAXTTL    297#define LOPT_MAXTTL        297
#define LOPT_NO_REBIND 298#define LOPT_NO_REBIND     298
#define LOPT_LOC_REBND 299#define LOPT_LOC_REBND     299
#define LOPT_ADD_MAC   300#define LOPT_ADD_MAC       300
#define LOPT_DNSSEC    301#define LOPT_DNSSEC        301
#define LOPT_INCR_ADDR 302#define LOPT_INCR_ADDR     302
#define LOPT_CONNTRACK 303#define LOPT_CONNTRACK     303
#define LOPT_FQDN      304#define LOPT_FQDN          304
#define LOPT_LUASCRIPT 305#define LOPT_LUASCRIPT     305
#define LOPT_RA        306#define LOPT_RA            306
#define LOPT_DUID      307#define LOPT_DUID          307
#define LOPT_HOST_REC  308#define LOPT_HOST_REC      308
#define LOPT_TFTP_LC   309#define LOPT_TFTP_LC       309
#define LOPT_RR        310#define LOPT_RR            310
#define LOPT_CLVERBIND 311#define LOPT_CLVERBIND     311
#define LOPT_MAXCTTL   312#define LOPT_MAXCTTL       312
#define LOPT_AUTHZONE  313#define LOPT_AUTHZONE      313
#define LOPT_AUTHSERV  314#define LOPT_AUTHSERV      314
#define LOPT_AUTHTTL   315#define LOPT_AUTHTTL       315
#define LOPT_AUTHSOA   316#define LOPT_AUTHSOA       316
#define LOPT_AUTHSFS   317#define LOPT_AUTHSFS       317
#define LOPT_AUTHPEER  318#define LOPT_AUTHPEER      318
#define LOPT_IPSET     319#define LOPT_IPSET         319
#ifdef OPTION6_PREFIX_CLASS #define LOPT_SYNTH         320
#define LOPT_PREF_CLSS 320#define LOPT_RELAY         323
#endif#define LOPT_RA_PARAM      324
 #define LOPT_ADD_SBNET     325
 #define LOPT_QUIET_DHCP    326
 #define LOPT_QUIET_DHCP6   327
 #define LOPT_QUIET_RA      328
 #define LOPT_SEC_VALID     329
 #define LOPT_TRUST_ANCHOR  330
 #define LOPT_DNSSEC_DEBUG  331
 #define LOPT_REV_SERV      332
 #define LOPT_SERVERS_FILE  333
 #define LOPT_DNSSEC_CHECK  334
 #define LOPT_LOCAL_SERVICE 335
 #define LOPT_DNSSEC_TIME   336
 #define LOPT_LOOP_DETECT   337
 #define LOPT_IGNORE_ADDR   338
 #define LOPT_MINCTTL       339
 #define LOPT_DHCP_INOTIFY  340
 #define LOPT_DHOPT_INOTIFY 341
 #define LOPT_HOST_INOTIFY  342
 #define LOPT_DNSSEC_STAMP  343
 #define LOPT_TFTP_NO_FAIL  344
 #define LOPT_MAXPORT       345
 #define LOPT_CPE_ID        346
 #define LOPT_SCRIPT_ARP    347
 #define LOPT_DHCPTTL       348
 #define LOPT_TFTP_MTU      349
 #define LOPT_REPLY_DELAY   350
 #define LOPT_RAPID_COMMIT  351
 #define LOPT_DUMPFILE      352
 #define LOPT_DUMPMASK      353
 #define LOPT_UBUS          354
 #define LOPT_NAME_MATCH    355
 #define LOPT_CAA           356
 #define LOPT_SHARED_NET    357
 #define LOPT_IGNORE_CLID   358
 #define LOPT_SINGLE_PORT   359
 #define LOPT_SCRIPT_TIME   360
 #define LOPT_PXE_VENDOR    361
 #define LOPT_DYNHOST       362
 #define LOPT_LOG_DEBUG     363
 #define LOPT_UMBRELLA      364
 #define LOPT_CMARK_ALST_EN 365
 #define LOPT_CMARK_ALST    366
 #define LOPT_QUIET_TFTP    367
 #define LOPT_NFTSET        368
 #define LOPT_FILTER_A      369
 #define LOPT_FILTER_AAAA   370
 #define LOPT_STRIP_SBNET   371
 #define LOPT_STRIP_MAC     372
 #define LOPT_CONF_OPT      373
 #define LOPT_CONF_SCRIPT   374
 #define LOPT_RANDPORT_LIM  375
 #define LOPT_FAST_RETRY    376
 #define LOPT_STALE_CACHE   377
 #define LOPT_NORR          378
 #define LOPT_NO_IDENT      379
   
 #ifdef HAVE_GETOPT_LONG  #ifdef HAVE_GETOPT_LONG
 static const struct option opts[] =    static const struct option opts[] =  
Line 143  static const struct myoption opts[] =  Line 198  static const struct myoption opts[] = 
     { "no-poll", 0, 0, 'n' },      { "no-poll", 0, 0, 'n' },
     { "help", 0, 0, 'w' },      { "help", 0, 0, 'w' },
     { "no-daemon", 0, 0, 'd' },      { "no-daemon", 0, 0, 'd' },
    { "log-queries", 0, 0, 'q' },    { "log-queries", 2, 0, 'q' },
     { "user", 2, 0, 'u' },      { "user", 2, 0, 'u' },
     { "group", 2, 0, 'g' },      { "group", 2, 0, 'g' },
     { "resolv-file", 2, 0, 'r' },      { "resolv-file", 2, 0, 'r' },
       { "servers-file", 1, 0, LOPT_SERVERS_FILE },
     { "mx-host", 1, 0, 'm' },      { "mx-host", 1, 0, 'm' },
     { "mx-target", 1, 0, 't' },      { "mx-target", 1, 0, 't' },
     { "cache-size", 2, 0, 'c' },      { "cache-size", 2, 0, 'c' },
Line 161  static const struct myoption opts[] =  Line 217  static const struct myoption opts[] = 
     { "domain-suffix", 1, 0, 's' },      { "domain-suffix", 1, 0, 's' },
     { "interface", 1, 0, 'i' },      { "interface", 1, 0, 'i' },
     { "listen-address", 1, 0, 'a' },      { "listen-address", 1, 0, 'a' },
       { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
     { "bogus-priv", 0, 0, 'b' },      { "bogus-priv", 0, 0, 'b' },
     { "bogus-nxdomain", 1, 0, 'B' },      { "bogus-nxdomain", 1, 0, 'B' },
       { "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' },
       { "rev-server", 1, 0, LOPT_REV_SERV },
     { "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 },
     { "query-port", 1, 0, 'Q' },      { "query-port", 1, 0, 'Q' },
     { "except-interface", 1, 0, 'I' },      { "except-interface", 1, 0, 'I' },
     { "no-dhcp-interface", 1, 0, '2' },      { "no-dhcp-interface", 1, 0, '2' },
Line 194  static const struct myoption opts[] =  Line 258  static const struct myoption opts[] = 
     { "srv-host", 1, 0, 'W' },      { "srv-host", 1, 0, 'W' },
     { "localise-queries", 0, 0, 'y' },      { "localise-queries", 0, 0, 'y' },
     { "txt-record", 1, 0, 'Y' },      { "txt-record", 1, 0, 'Y' },
       { "caa-record", 1, 0 , LOPT_CAA },
     { "dns-rr", 1, 0, LOPT_RR },      { "dns-rr", 1, 0, LOPT_RR },
     { "enable-dbus", 2, 0, '1' },      { "enable-dbus", 2, 0, '1' },
       { "enable-ubus", 2, 0, LOPT_UBUS },
     { "bootp-dynamic", 2, 0, '3' },      { "bootp-dynamic", 2, 0, '3' },
     { "dhcp-mac", 1, 0, '4' },      { "dhcp-mac", 1, 0, '4' },
     { "no-ping", 0, 0, '5' },      { "no-ping", 0, 0, '5' },
Line 203  static const struct myoption opts[] =  Line 269  static const struct myoption opts[] = 
     { "conf-dir", 1, 0, '7' },      { "conf-dir", 1, 0, '7' },
     { "log-facility", 1, 0 ,'8' },      { "log-facility", 1, 0 ,'8' },
     { "leasefile-ro", 0, 0, '9' },      { "leasefile-ro", 0, 0, '9' },
       { "script-on-renewal", 0, 0, LOPT_SCRIPT_TIME},
     { "dns-forward-max", 1, 0, '0' },      { "dns-forward-max", 1, 0, '0' },
     { "clear-on-reload", 0, 0, LOPT_RELOAD },      { "clear-on-reload", 0, 0, LOPT_RELOAD },
     { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },      { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
    { "enable-tftp", 0, 0, LOPT_TFTP },    { "enable-tftp", 2, 0, LOPT_TFTP },
     { "tftp-secure", 0, 0, LOPT_SECURE },      { "tftp-secure", 0, 0, LOPT_SECURE },
    { "tftp-unique-root", 0, 0, LOPT_APREF },    { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
     { "tftp-unique-root", 2, 0, LOPT_APREF },
     { "tftp-root", 1, 0, LOPT_PREFIX },      { "tftp-root", 1, 0, LOPT_PREFIX },
     { "tftp-max", 1, 0, LOPT_TFTP_MAX },      { "tftp-max", 1, 0, LOPT_TFTP_MAX },
       { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
     { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },      { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
       { "tftp-single-port", 0, 0, LOPT_SINGLE_PORT },
     { "ptr-record", 1, 0, LOPT_PTR },      { "ptr-record", 1, 0, LOPT_PTR },
     { "naptr-record", 1, 0, LOPT_NAPTR },      { "naptr-record", 1, 0, LOPT_NAPTR },
     { "bridge-interface", 1, 0 , LOPT_BRIDGE },      { "bridge-interface", 1, 0 , LOPT_BRIDGE },
       { "shared-network", 1, 0, LOPT_SHARED_NET },
     { "dhcp-option-force", 1, 0, LOPT_FORCE },      { "dhcp-option-force", 1, 0, LOPT_FORCE },
     { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },      { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
     { "log-dhcp", 0, 0, LOPT_LOG_OPTS },      { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
Line 222  static const struct myoption opts[] =  Line 293  static const struct myoption opts[] = 
     { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },      { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
     { "dhcp-remoteid", 1, 0, LOPT_REMOTE },      { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
     { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },      { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
       { "dhcp-pxe-vendor", 1, 0, LOPT_PXE_VENDOR },
     { "interface-name", 1, 0, LOPT_INTNAME },      { "interface-name", 1, 0, LOPT_INTNAME },
     { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },      { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
     { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },      { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
       { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
       { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
     { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },      { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
     { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },      { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
     { "stop-dns-rebind", 0, 0, LOPT_REBIND },      { "stop-dns-rebind", 0, 0, LOPT_REBIND },
     { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },      { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
     { "all-servers", 0, 0, LOPT_NOLAST },       { "all-servers", 0, 0, LOPT_NOLAST }, 
    { "dhcp-match", 1, 0, LOPT_MATCH },     { "dhcp-match", 1, 0, LOPT_MATCH },
     { "dhcp-name-match", 1, 0, LOPT_NAME_MATCH },
     { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },      { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
     { "neg-ttl", 1, 0, LOPT_NEGTTL },      { "neg-ttl", 1, 0, LOPT_NEGTTL },
     { "max-ttl", 1, 0, LOPT_MAXTTL },      { "max-ttl", 1, 0, LOPT_MAXTTL },
       { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
     { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },      { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
     { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },      { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
     { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },      { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
     { "min-port", 1, 0, LOPT_MINPORT },      { "min-port", 1, 0, LOPT_MINPORT },
       { "max-port", 1, 0, LOPT_MAXPORT },
     { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },      { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
     { "cname", 1, 0, LOPT_CNAME },      { "cname", 1, 0, LOPT_CNAME },
     { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },      { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
Line 247  static const struct myoption opts[] =  Line 324  static const struct myoption opts[] = 
     { "dhcp-proxy", 2, 0, LOPT_PROXY },      { "dhcp-proxy", 2, 0, LOPT_PROXY },
     { "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", 0, 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 },
     { "strip-subnet", 0, 0, LOPT_STRIP_SBNET },
     { "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 },
     { "conntrack", 0, 0, LOPT_CONNTRACK },      { "conntrack", 0, 0, LOPT_CONNTRACK },
Line 264  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 },
#ifdef OPTION6_PREFIX_CLASS     { "nftset", 1, 0, LOPT_NFTSET },
    { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },    { "connmark-allowlist-enable", 2, 0, LOPT_CMARK_ALST_EN },
#endif    { "connmark-allowlist", 1, 0, LOPT_CMARK_ALST },
     { "synth-domain", 1, 0, LOPT_SYNTH },
     { "dnssec", 0, 0, LOPT_SEC_VALID },
     { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
     { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
     { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
     { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
     { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
     { "dhcp-relay", 1, 0, LOPT_RELAY },
     { "ra-param", 1, 0, LOPT_RA_PARAM },
     { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
     { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
     { "quiet-ra", 0, 0, LOPT_QUIET_RA },
     { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
     { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
     { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
     { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
     { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
     { "dumpfile", 1, 0, LOPT_DUMPFILE },
     { "dumpmask", 1, 0, LOPT_DUMPMASK },
     { "dhcp-ignore-clid", 0, 0,  LOPT_IGNORE_CLID },
     { "dynamic-host", 1, 0, LOPT_DYNHOST },
     { "log-debug", 0, 0, LOPT_LOG_DEBUG },
     { "umbrella", 2, 0, LOPT_UMBRELLA },
     { "quiet-tftp", 0, 0, LOPT_QUIET_TFTP },
     { "port-limit", 1, 0, LOPT_RANDPORT_LIM },
     { "fast-dns-retry", 2, 0, LOPT_FAST_RETRY },
     { "use-stale-cache", 2, 0 , LOPT_STALE_CACHE },
     { "no-ident", 0, 0, LOPT_NO_IDENT },
     { NULL, 0, 0, 0 }      { NULL, 0, 0, 0 }
   };    };
   
Line 294  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 },
   { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },    { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
   { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },    { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
     { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL }, 
     { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL }, 
   { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },    { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
   { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },    { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
   { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },    { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
     { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
   { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },    { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
   { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },    { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
   { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },    { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
   { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },    { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
   { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },    { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
   { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },    { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
     { LOPT_PXE_VENDOR, ARG_DUP, "<vendor>[,...]", gettext_noop("Specify vendor class to match for PXE requests."), NULL },
   { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },    { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
   { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },     { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL }, 
   { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },    { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
Line 318  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},
   { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },    { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
   { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },    { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
  { 'q', OPT_LOG, NULL, gettext_noop("Log DNS queries."), NULL },  { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
   { 'Q', ARG_ONE, "<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 },
   { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },    { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
     { LOPT_REV_SERV, ARG_DUP, "<addr>/<prefix>,<ipaddr>", gettext_noop("Specify address of upstream servers for reverse address queries"), NULL },
   { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },    { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
   { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },    { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
   { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },    { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
   { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },    { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
   { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },    { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
   { LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },    { LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
     { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
     { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
     { 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 },
   { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },    { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
   { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },    { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
  { 'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp for known DHCP options."), NULL },  { 'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp or --help dhcp6 for known DHCP options."), NULL },
   { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },    { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
   { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },    { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
   { 'y', OPT_LOCALISE, NULL, gettext_noop("Answer DNS queries based on the interface a query was sent to."), NULL },    { 'y', OPT_LOCALISE, NULL, gettext_noop("Answer DNS queries based on the interface a query was sent to."), NULL },
Line 349  static struct { Line 471  static struct {
   { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },    { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
   { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },    { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
   { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },    { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
     { LOPT_UBUS, ARG_ONE, "[=<busname>]", gettext_noop("Enable the UBus interface."), NULL },
   { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },    { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
   { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },    { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
   { '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },    { '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
   { LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },    { LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
     { LOPT_SHARED_NET, ARG_DUP, "<iface>|<addr>,<addr>", gettext_noop("Specify extra networks sharing a broadcast domain for DHCP"), NULL},
   { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },    { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
   { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },    { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
   { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },    { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
   { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },    { 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 },
   { '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 },
  { '8', ARG_ONE, "<facilty>|<file>", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), 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 },
   { '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)"), "!" }, 
   { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },    { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
   { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },    { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
   { LOPT_OVERRIDE, OPT_NO_OVERRIDE, NULL, gettext_noop("Do NOT reuse filename and server fields for extra DHCP options."), NULL },    { LOPT_OVERRIDE, OPT_NO_OVERRIDE, NULL, gettext_noop("Do NOT reuse filename and server fields for extra DHCP options."), NULL },
  { LOPT_TFTP, OPT_TFTP, NULL, gettext_noop("Enable integrated read-only TFTP server."), NULL },  { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
   { LOPT_PREFIX, ARG_DUP, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },    { LOPT_PREFIX, ARG_DUP, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
  { LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL },  { LOPT_APREF, ARG_DUP, "[=ip|mac]", gettext_noop("Add client IP or hardware address to tftp-root."), NULL },
   { LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },    { LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
  { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },  { LOPT_TFTP_NO_FAIL, OPT_TFTP_NO_FAIL, NULL, gettext_noop("Do not terminate the service if TFTP directories are inaccessible."), NULL },
   { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" },
   { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
   { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },    { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
   { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },    { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
   { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },    { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
     { LOPT_SINGLE_PORT, OPT_SINGLE_PORT, NULL, gettext_noop("Use only one port for TFTP server."), NULL },
   { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },    { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
   { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },    { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
   { LOPT_REBIND, OPT_NO_REBIND, NULL, gettext_noop("Stop DNS rebinding. Filter private IP ranges when resolving."), NULL },    { LOPT_REBIND, OPT_NO_REBIND, NULL, gettext_noop("Stop DNS rebinding. Filter private IP ranges when resolving."), NULL },
Line 379  static struct { Line 508  static struct {
   { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },    { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
   { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },    { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
   { LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },    { LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
     { LOPT_NAME_MATCH, ARG_DUP, "set:<tag>,<string>[*]", gettext_noop("Set tag if client provides given name."), NULL },
   { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },    { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
   { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },    { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
   { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },    { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
     { LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
   { LOPT_DHCP_FQDN, OPT_DHCP_FQDN, NULL, gettext_noop("Use only fully qualified domain names for DHCP clients."), NULL },    { LOPT_DHCP_FQDN, OPT_DHCP_FQDN, NULL, gettext_noop("Use only fully qualified domain names for DHCP clients."), NULL },
   { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},    { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
   { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },    { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
  { LOPT_CNAME, ARG_DUP, "<alias>,<target>", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },  { LOPT_RELAY, ARG_DUP, "<local-addr>,<server>[,<iface>]", gettext_noop("Relay DHCP requests to a remote server"), NULL},
   { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
   { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },    { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
   { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },    { LOPT_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, OPT_ADD_MAC, NULL, 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_DNSSEC, OPT_DNSSEC, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), 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_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_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 },
     { LOPT_IGNORE_CLID, OPT_IGNORE_CLID, NULL, gettext_noop("Ignore client identifier option sent by DHCP clients."), NULL },
   { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },    { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
   { LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },    { LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },
   { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },    { LOPT_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>", 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_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 },
   { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },    { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
   { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },    { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
   { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },    { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
  { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritive zone information"), NULL },  { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritative zone information"), NULL },
   { 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>/<ipset>[,<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 },
#ifdef OPTION6_PREFIX_CLASS   { LOPT_NFTSET, ARG_DUP, "/<domain>[/<domain>...]/<nftset>...", gettext_noop("Specify nftables sets to which matching domains should be added"), NULL },
  { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },  { LOPT_CMARK_ALST_EN, ARG_ONE, "[=<mask>]", gettext_noop("Enable filtering of DNS queries with connection-track marks."), NULL },
#endif  { 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_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
   { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
   { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
   { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
   { LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
   { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
   { LOPT_RA_PARAM, ARG_DUP, "<iface>,[mtu:<value>|<interface>|off,][<prio>,]<intval>[,<lifetime>]", gettext_noop("Set MTU, priority, resend-interval and router-lifetime"), NULL },
   { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
   { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
   { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
   { LOPT_LOG_DEBUG, OPT_LOG_DEBUG, NULL, gettext_noop("Log debugging information."), NULL }, 
   { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
   { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
   { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL }, 
   { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL }, 
   { LOPT_REPLY_DELAY, ARG_ONE, "<integer>", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL },
   { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
   { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), 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_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 }
 };   }; 
   
/* We hide metacharaters in quoted strings by mapping them into the ASCII control/* We hide metacharacters in quoted strings by mapping them into the ASCII control
    character space. Note that the \0, \t \b \r \033 and \n characters are carefully placed in the     character space. Note that the \0, \t \b \r \033 and \n characters are carefully placed in the
    following sequence so that they map to themselves: it is therefore possible to call     following sequence so that they map to themselves: it is therefore possible to call
    unhide_metas repeatedly on string without breaking things.     unhide_metas repeatedly on string without breaking things.
Line 471  static void *opt_malloc(size_t size) Line 635  static void *opt_malloc(size_t size)
   return ret;    return ret;
 }  }
   
static char *opt_string_alloc(char *cp)static char *opt_string_alloc(const char *cp)
 {  {
   char *ret = NULL;    char *ret = NULL;
     size_t len;
       
  if (cp && strlen(cp) != 0)  if (cp && (len = strlen(cp)) != 0)
     {      {
      ret = opt_malloc(strlen(cp)+1);      ret = opt_malloc(len+1);
      strcpy(ret, cp);       memcpy(ret, cp, len+1); 
               
       /* restore hidden metachars */        /* restore hidden metachars */
       unhide_metas(ret);        unhide_metas(ret);
Line 522  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 534  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 547  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 560  static int atoi_check16(char *a, int *res) Line 750  static int atoi_check16(char *a, int *res)
   
   return 1;    return 1;
 }  }
        
static void add_txt(char *name, char *txt)#ifdef HAVE_DNSSEC
 static int atoi_check8(char *a, int *res)
 {  {
  size_t len = strlen(txt);  if (!(atoi_check(a, res)) ||
       *res < 0 ||
       *res > 0xff)
     return 0;
 
   return 1;
 }
 #endif
 
 #ifndef NO_ID
 static void add_txt(char *name, char *txt, int stat)
 {
   struct txt_record *r = opt_malloc(sizeof(struct txt_record));    struct txt_record *r = opt_malloc(sizeof(struct txt_record));
  
   if (txt)
     {
       size_t len = strlen(txt);
       r->txt = opt_malloc(len+1);
       r->len = len+1;
       *(r->txt) = len;
       memcpy((r->txt)+1, txt, len);
     }
 
   r->stat = stat;
   r->name = opt_string_alloc(name);    r->name = opt_string_alloc(name);
   r->next = daemon->txt;    r->next = daemon->txt;
   daemon->txt = r;    daemon->txt = r;
   r->class = C_CHAOS;    r->class = C_CHAOS;
   r->txt = opt_malloc(len+1);  
   r->len = len+1;  
   *(r->txt) = len;  
   memcpy((r->txt)+1, txt, len);  
 }  }
   #endif
   
 static void do_usage(void)  static void do_usage(void)
 {  {
Line 619  static void do_usage(void) Line 828  static void do_usage(void)
         sprintf(buff, "    ");          sprintf(buff, "    ");
               
       sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);        sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
      printf("%-40.40s", buff);      printf("%-55.55s", buff);
                             
       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 634  static void do_usage(void) Line 843  static void do_usage(void)
 }  }
   
 #define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)  #define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
   #define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0)
   #define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0)
   
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)static char *parse_mysockaddr(char *arg, union mysockaddr *addr
 {  {
  int source_port = 0, serv_port = NAMESERVER_PORT;  if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
  char *portno, *source;    addr->sa.sa_family = AF_INET;
#ifdef HAVE_IPV6  else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
  int scope_index = 0;    addr->sa.sa_family = AF_INET6;
  char *scope_id;  else
#endif    return _("bad address");
    
   return NULL;
 }
 
 char *parse_server(char *arg, struct server_details *sdetails)
 {
   sdetails->serv_port = NAMESERVER_PORT;
   char *portno;
   int ecode = 0;
   struct addrinfo hints;
 
   memset(&hints, 0, sizeof(struct addrinfo));
       
  if ((source = split_chr(arg, '@')) && /* is there a source. */  *sdetails->interface = 0;
      (portno = split_chr(source, '#')) &&  sdetails->addr_type = AF_UNSPEC;
      !atoi_check16(portno, &source_port))     
   if (strcmp(arg, "#") == 0)
     {
       if (sdetails->flags)
         *sdetails->flags |= SERV_USE_RESOLV;
       sdetails->addr_type = AF_LOCAL;
       sdetails->valid = 1;
       return NULL;
     }
   
   if ((sdetails->source = split_chr(arg, '@')) && /* is there a source. */
       (portno = split_chr(sdetails->source, '#')) &&
       !atoi_check16(portno, &sdetails->source_port))
     return _("bad port");      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");
       
#ifdef HAVE_IPV6  sdetails->scope_id = split_chr(arg, '%');
  scope_id = split_chr(arg, '%');  
   if (sdetails->source) {
     sdetails->interface_opt = split_chr(sdetails->source, '@');
 
     if (sdetails->interface_opt)
       {
 #if defined(SO_BINDTODEVICE)
         safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
         sdetails->source = sdetails->interface_opt;
 #else
         return _("interface binding not supported");
 #endif  #endif
         }
     }
   
     if (inet_pton(AF_INET, arg, &sdetails->addr->in.sin_addr) > 0)
         sdetails->addr_type = AF_INET;
     else if (inet_pton(AF_INET6, arg, &sdetails->addr->in6.sin6_addr) > 0)
         sdetails->addr_type = AF_INET6;
     else 
       {
         /* if the argument is neither an IPv4 not an IPv6 address, it might be a
            hostname and we should try to resolve it to a suitable address. */
         memset(&hints, 0, sizeof(hints));
         /* The AI_ADDRCONFIG flag ensures that then IPv4 addresses are returned in
            the result only if the local system has at least one IPv4 address
            configured, and IPv6 addresses are returned only if the local system
            has at least one IPv6 address configured. The loopback address is not
            considered for this case as valid as a configured address. This flag is
            useful on, for example, IPv4-only systems, to ensure that getaddrinfo()
            does not return IPv6 socket addresses that would always fail in
            subsequent connect() or bind() attempts. */
         hints.ai_flags = AI_ADDRCONFIG;
   #if defined(HAVE_IDN) && defined(AI_IDN)
         /* If the AI_IDN flag is specified and we have glibc 2.3.4 or newer, then
            the node name given in node is converted to IDN format if necessary.
            The source encoding is that of the current locale. */
         hints.ai_flags |= AI_IDN;
   #endif
         /* The value AF_UNSPEC indicates that getaddrinfo() should return socket
            addresses for any address family (either IPv4 or IPv6, for example)
            that can be used with node <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));
           }
       }
       
  if ((addr->in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t) -1)  sdetails->valid = 1;
   return NULL;
 }
 
 char *parse_server_addr(struct server_details *sdetails)
 {
   if (sdetails->addr_type == AF_INET)
     {      {
      addr->in.sin_port = htons(serv_port);            sdetails->addr->in.sin_port = htons(sdetails->serv_port);
      addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;      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 ((source_addr->in.sin_addr.s_addr = inet_addr(source)) == (in_addr_t) -1)          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)
              source_addr->in.sin_addr.s_addr = INADDR_ANY;                  if (sdetails->interface_opt)
              strncpy(interface, source, IF_NAMESIZE - 1);                    return _("interface can only be specified once");
 
                   sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY;
                   safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
 #else  #else
              return _("interface binding not supported");                  return _("interface binding not supported");
 #endif  #endif
                   }
             }              }
         }          }
     }      }
#ifdef HAVE_IPV6  else if (sdetails->addr_type == AF_INET6)
  else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0) 
     {      {
      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)
              source_addr->in6.sin6_addr = in6addr_any;                   if (sdetails->interface_opt)
              strncpy(interface, source, IF_NAMESIZE - 1);                  return _("interface can only be specified once");
 
                   sdetails->source_addr->in6.sin6_addr = in6addr_any;
                   safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
 #else  #else
              return _("interface binding not supported");                  return _("interface binding not supported");
 #endif  #endif
                   }
             }              }
         }          }
     }      }
#endif  else if (sdetails->addr_type != AF_LOCAL)
  else 
     return _("bad address");      return _("bad address");
     
     return NULL;
   }
   
   int parse_server_next(struct server_details *sdetails)
   {
     /* Looping over resolved addresses? */
     if (sdetails->hostinfo)
       {
         /* Get address type */
         sdetails->addr_type = sdetails->hostinfo->ai_family;
   
         /* Get address */
         if (sdetails->addr_type == AF_INET)
           memcpy(&sdetails->addr->in.sin_addr,
                   &((struct sockaddr_in *) sdetails->hostinfo->ai_addr)->sin_addr,
                   sizeof(sdetails->addr->in.sin_addr));
         else if (sdetails->addr_type == AF_INET6)
           memcpy(&sdetails->addr->in6.sin6_addr,
                   &((struct sockaddr_in6 *) sdetails->hostinfo->ai_addr)->sin6_addr,
                   sizeof(sdetails->addr->in6.sin6_addr));
   
         /* Iterate to the next available address */
         sdetails->valid = sdetails->hostinfo->ai_next != NULL;
         sdetails->hostinfo = sdetails->hostinfo->ai_next;
         return 1;
       }
     else if (sdetails->valid)
       {
         /* When using an IP address, we return the address only once */
         sdetails->valid = 0;
         return 1;
       }
     /* Stop iterating here, we used all available addresses */
     return 0;
   }
   
   static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int size)
   {
     int i, j;
     char *string;
     int msize;
     u16 flags = 0;
     char domain[29]; /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
     union mysockaddr serv_addr, source_addr;
     char interface[IF_NAMESIZE+1];
     int count = 1, rem, addrbytes, addrbits;
     struct server_details sdetails;
   
     memset(&sdetails, 0, sizeof(struct server_details));
     sdetails.addr = &serv_addr;
     sdetails.source_addr = &source_addr;
     sdetails.interface = interface;
     sdetails.flags = &flags;
       
     if (!server)
       flags = SERV_LITERAL_ADDRESS;
     else if ((string = parse_server(server, &sdetails)))
       return string;
     
     if (from_file)
       flags |= SERV_FROM_FILE;
    
     rem = size & 0x7;
     addrbytes = (32 - size) >> 3;
     addrbits = (32 - size) & 7;
     
     if (size > 32 || size < 1)
       return _("bad IPv4 prefix length");
     
     /* Zero out last address bits according to CIDR mask */
     ((u8 *)addr4)[3-addrbytes] &= ~((1 << addrbits)-1);
     
     size = size & ~0x7;
     
     if (rem != 0)
       count = 1 << (8 - rem);
     
     for (i = 0; i < count; i++)
       {
         *domain = 0;
         string = domain;
         msize = size/8;
         
         for (j = (rem == 0) ? msize-1 : msize; j >= 0; j--)
           { 
             int dig = ((unsigned char *)addr4)[j];
             
             if (j == msize)
               dig += i;
             
             string += sprintf(string, "%d.", dig);
           }
         
         sprintf(string, "in-addr.arpa");
   
         if (flags & SERV_LITERAL_ADDRESS)
           {
             if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
               return  _("error");
           }
         else
           {
             /* Always reset server as valid here, so we can add the same upstream
                server address multiple times for each x.y.z.in-addr.arpa  */
             sdetails.valid = 1;
             while (parse_server_next(&sdetails))
               {
                 if ((string = parse_server_addr(&sdetails)))
                   return string;
                 
                 if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
                   return  _("error");
               }
   
             if (sdetails.orig_hostinfo)
               freeaddrinfo(sdetails.orig_hostinfo);
           }
       }
     
   return NULL;    return NULL;
 }  }
   
   static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, int size)
   {
     int i, j;
     char *string;
     int msize;
     u16 flags = 0;
     char domain[73]; /* strlen("32*<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;
     
     memset(&sdetails, 0, sizeof(struct server_details));
     sdetails.addr = &serv_addr;
     sdetails.source_addr = &source_addr;
     sdetails.interface = interface;
     sdetails.flags = &flags;
      
     if (!server)
       flags = SERV_LITERAL_ADDRESS;
     else if ((string = parse_server(server, &sdetails)))
       return string;
   
     if (from_file)
       flags |= SERV_FROM_FILE;
     
     rem = size & 0x3;
     addrbytes = (128 - size) >> 3;
     addrbits = (128 - size) & 7;
     
     if (size > 128 || size < 1)
       return _("bad IPv6 prefix length");
     
     /* Zero out last address bits according to CIDR mask */
     addr6->s6_addr[15-addrbytes] &= ~((1 << addrbits) - 1);
     
     size = size & ~0x3;
     
     if (rem != 0)
       count = 1 << (4 - rem);
         
     for (i = 0; i < count; i++)
       {
         *domain = 0;
         string = domain;
         msize = size/4;
     
         for (j = (rem == 0) ? msize-1 : msize; j >= 0; j--)
           { 
             int dig = ((unsigned char *)addr6)[j>>1];
             
             dig = j & 1 ? dig & 15 : dig >> 4;
             
             if (j == msize)
               dig += i;
             
             string += sprintf(string, "%.1x.", dig);
           }
         
         sprintf(string, "ip6.arpa");
   
         if (flags & SERV_LITERAL_ADDRESS)
           {
             if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
               return  _("error");
           }
         else
           {
             /* Always reset server as valid here, so we can add the same upstream
                server address multiple times for each x.y.z.ip6.arpa  */
             sdetails.valid = 1;
             while (parse_server_next(&sdetails))
               {
                 if ((string = parse_server_addr(&sdetails)))
                   return string;
                 
                 if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
                   return  _("error");
               }
   
             if (sdetails.orig_hostinfo)
               freeaddrinfo(sdetails.orig_hostinfo);
           }
       }
     
     return NULL;
   }
   
 #ifdef HAVE_DHCP  #ifdef HAVE_DHCP
   
 static int is_tag_prefix(char *arg)  static int is_tag_prefix(char *arg)
Line 740  static char *set_prefix(char *arg) Line 1285  static char *set_prefix(char *arg)
    return arg;     return arg;
 }  }
   
   static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next)
   {
     struct dhcp_netid *tt;
     tt = opt_malloc(sizeof (struct dhcp_netid));
     tt->net = opt_string_alloc(net);
     tt->next = next;
     return tt;
   }
   
   static void dhcp_netid_free(struct dhcp_netid *nid)
   {
     while (nid)
       {
         struct dhcp_netid *tmp = nid;
         nid = nid->next;
         free(tmp->net);
         free(tmp);
       }
   }
   
   /* Parse one or more tag:s before parameters.
    * Moves arg to the end of tags. */
   static struct dhcp_netid * dhcp_tags(char **arg)
   {
     struct dhcp_netid *id = NULL;
   
     while (is_tag_prefix(*arg))
       {
         char *comma = split(*arg);
         id = dhcp_netid_create((*arg)+4, id);
         *arg = comma;
       };
     if (!*arg)
       {
         dhcp_netid_free(id);
         id = NULL;
       }
     return id;
   }
   
   static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
   {
     while (netid)
       {
         struct dhcp_netid_list *tmplist = netid;
         netid = netid->next;
         dhcp_netid_free(tmplist->list);
         free(tmplist);
       }
   }
   
   static void dhcp_config_free(struct dhcp_config *config)
   {
     if (config)
       {
         struct hwaddr_config *hwaddr = config->hwaddr;
         
         while (hwaddr)
           {
             struct hwaddr_config *tmp = hwaddr;
             hwaddr = hwaddr->next;
             free(tmp);
           }
         
         dhcp_netid_list_free(config->netid);
         dhcp_netid_free(config->filter);
         
         if (config->flags & CONFIG_CLID)
           free(config->clid);
         if (config->flags & CONFIG_NAME)
           free(config->hostname);
   
   #ifdef HAVE_DHCP6
         if (config->flags & CONFIG_ADDR6)
           {
             struct addrlist *addr, *tmp;
             
             for (addr = config->addr6; addr; addr = tmp)
               {
                 tmp = addr->next;
                 free(addr);
               }
           }
   #endif
   
         free(config);
       }
   }
   
   static void dhcp_context_free(struct dhcp_context *ctx)
   {
     if (ctx)
       {
         dhcp_netid_free(ctx->filter);
         free(ctx->netid.net);
   #ifdef HAVE_DHCP6
         free(ctx->template_interface);
   #endif
         free(ctx);
       }
   }
   
   static void dhcp_opt_free(struct dhcp_opt *opt)
   {
     if (opt->flags & DHOPT_VENDOR)
       free(opt->u.vendor_class);
     dhcp_netid_free(opt->netid);
     free(opt->val);
     free(opt);
   }
   
   
 /* This is too insanely large to keep in-line in the switch */  /* This is too insanely large to keep in-line in the switch */
 static int parse_dhcp_opt(char *errstr, char *arg, int flags)  static int parse_dhcp_opt(char *errstr, char *arg, int flags)
 {  {
Line 747  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1404  static int parse_dhcp_opt(char *errstr, char *arg, int
   char lenchar = 0, *cp;    char lenchar = 0, *cp;
   int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;    int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
   char *comma = NULL;    char *comma = NULL;
   struct dhcp_netid *np = NULL;  
   u16 opt_len = 0;    u16 opt_len = 0;
   int is6 = 0;    int is6 = 0;
     int option_ok = 0;
   
   new->len = 0;    new->len = 0;
   new->flags = flags;    new->flags = flags;
Line 769  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1426  static int parse_dhcp_opt(char *errstr, char *arg, int
         {          {
           new->opt = atoi(arg);            new->opt = atoi(arg);
           opt_len = 0;            opt_len = 0;
             option_ok = 1;
           break;            break;
         }          }
               
       if (strstr(arg, "option:") == arg)        if (strstr(arg, "option:") == arg)
         {          {
          new->opt = lookup_dhcp_opt(AF_INET, arg+7);          if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
          opt_len = lookup_dhcp_len(AF_INET, new->opt);            {
          /* option:<optname> must follow tag and vendor string. */              opt_len = lookup_dhcp_len(AF_INET, new->opt);
          if ((opt_len & OT_INTERNAL) && flags != DHOPT_MATCH)              /* option:<optname> must follow tag and vendor string. */
            new->opt = 0;              if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
                 option_ok = 1;
             }
           break;            break;
         }          }
 #ifdef HAVE_DHCP6  #ifdef HAVE_DHCP6
Line 792  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1452  static int parse_dhcp_opt(char *errstr, char *arg, int
             {              {
               new->opt = atoi(arg+8);                new->opt = atoi(arg+8);
               opt_len = 0;                opt_len = 0;
                 option_ok = 1;
             }              }
           else            else
             {              {
              new->opt = lookup_dhcp_opt(AF_INET6, arg+8);              if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
              opt_len = lookup_dhcp_len(AF_INET6, new->opt);                {
              if ((opt_len & OT_INTERNAL) && flags != DHOPT_MATCH)                  opt_len = lookup_dhcp_len(AF_INET6, new->opt);
                new->opt = 0;                  if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
                     option_ok = 1;
                 }
             }              }
           /* option6:<opt>|<optname> must follow tag and vendor string. */            /* option6:<opt>|<optname> must follow tag and vendor string. */
           is6 = 1;            is6 = 1;
Line 809  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 821  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1488  static int parse_dhcp_opt(char *errstr, char *arg, int
           new->flags |= DHOPT_RFC3925;            new->flags |= DHOPT_RFC3925;
           if (flags == DHOPT_MATCH)            if (flags == DHOPT_MATCH)
             {              {
              new->opt = 1; /* avoid error below */              option_ok = 1;
               break;                break;
             }              }
         }          }
       else        else
         {          {
           new->netid = opt_malloc(sizeof (struct dhcp_netid));  
           /* allow optional "net:" or "tag:" for consistency */            /* allow optional "net:" or "tag:" for consistency */
          if (is_tag_prefix(arg))          const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg);
            new->netid->net = opt_string_alloc(arg+4);          new->netid = dhcp_netid_create(name, new->netid);
          else 
            new->netid->net = opt_string_alloc(set_prefix(arg)); 
          new->netid->next = np; 
          np = new->netid; 
         }          }
               
       arg = comma;         arg = comma; 
Line 844  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1506  static int parse_dhcp_opt(char *errstr, char *arg, int
   if (is6)    if (is6)
     {      {
       if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))        if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
        ret_err(_("unsupported encapsulation for IPv6 option"));        goto_err(_("unsupported encapsulation for IPv6 option"));
               
       if (opt_len == 0 &&        if (opt_len == 0 &&
           !(new->flags & DHOPT_RFC3925))            !(new->flags & DHOPT_RFC3925))
        opt_len = lookup_dhcp_len(AF_INET6 ,new->opt);        opt_len = lookup_dhcp_len(AF_INET6, new->opt);
     }      }
   else    else
 #endif  #endif
     if (opt_len == 0 &&      if (opt_len == 0 &&
         !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))          !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
      opt_len = lookup_dhcp_len(AF_INET ,new->opt);      opt_len = lookup_dhcp_len(AF_INET, new->opt);
       
   /* option may be missing with rfc3925 match */    /* option may be missing with rfc3925 match */
  if (new->opt == 0)  if (!option_ok)
    ret_err(_("bad dhcp-option"));    goto_err(_("bad dhcp-option"));
       
   if (comma)    if (comma)
     {      {
       /* characterise the value */        /* characterise the value */
       char c;        char c;
      int found_dig = 0;      int found_dig = 0, found_colon = 0;
       is_addr = is_addr6 = is_hex = is_dec = is_string = 1;        is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
       addrs = digs = 1;        addrs = digs = 1;
       dots = 0;        dots = 0;
Line 878  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1540  static int parse_dhcp_opt(char *errstr, char *arg, int
           {            {
             digs++;              digs++;
             is_dec = is_addr = 0;              is_dec = is_addr = 0;
               found_colon = 1;
           }            }
         else if (c == '/')           else if (c == '/') 
           {            {
Line 887  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1550  static int parse_dhcp_opt(char *errstr, char *arg, int
           }             } 
         else if (c == '.')                else if (c == '.')      
           {            {
            is_addr6 = is_dec = is_hex = 0;            is_dec = is_hex = 0;
             dots++;              dots++;
           }            }
         else if (c == '-')          else if (c == '-')
Line 919  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1582  static int parse_dhcp_opt(char *errstr, char *arg, int
             
       if (!found_dig)        if (!found_dig)
         is_dec = is_addr = 0;          is_dec = is_addr = 0;
   
         if (!found_colon)
           is_addr6 = 0;
   
   #ifdef HAVE_DHCP6
         /* NTP server option takes hex, addresses or FQDN */
         if (is6 && new->opt == OPTION6_NTP_SERVER && !is_hex)
           opt_len |= is_addr6 ? OT_ADDR_LIST : OT_RFC1035_NAME;
   #endif
             
       /* We know that some options take addresses */        /* We know that some options take addresses */
       if (opt_len & OT_ADDR_LIST)        if (opt_len & OT_ADDR_LIST)
Line 926  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1598  static int parse_dhcp_opt(char *errstr, char *arg, int
           is_string = is_dec = is_hex = 0;            is_string = is_dec = is_hex = 0;
                       
           if (!is6 && (!is_addr || dots == 0))            if (!is6 && (!is_addr || dots == 0))
            ret_err(_("bad IP address"));            goto_err(_("bad IP address"));
   
            if (is6 && !is_addr6)             if (is6 && !is_addr6)
             ret_err(_("bad IPv6 address"));             goto_err(_("bad IPv6 address"));
         }          }
       /* or names */        /* or names */
       else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))        else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
Line 948  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1620  static int parse_dhcp_opt(char *errstr, char *arg, int
             case 'd':              case 'd':
             case 'D':              case 'D':
               fac *= 24;                fac *= 24;
              /* fall though */              /* fall through */
             case 'h':              case 'h':
             case 'H':              case 'H':
               fac *= 60;                fac *= 60;
Line 1021  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1693  static int parse_dhcp_opt(char *errstr, char *arg, int
               cp = comma;                cp = comma;
               comma = split(cp);                comma = split(cp);
               slash = split_chr(cp, '/');                slash = split_chr(cp, '/');
              in.s_addr = inet_addr(cp);              if (!inet_pton(AF_INET, cp, &in))
                 goto_err(_("bad IPv4 address"));
               if (!slash)                if (!slash)
                 {                  {
                   memcpy(op, &in, INADDRSZ);                    memcpy(op, &in, INADDRSZ);
Line 1066  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1739  static int parse_dhcp_opt(char *errstr, char *arg, int
                   op += IN6ADDRSZ;                    op += IN6ADDRSZ;
                   continue;                    continue;
                 }                  }
          
              ret_err(_("bad IPv6 address"));              goto_err(_("bad IPv6 address"));
             }               } 
           new->len = op - new->val;            new->len = op - new->val;
         }          }
Line 1094  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1767  static int parse_dhcp_opt(char *errstr, char *arg, int
                   if (strcmp (arg, ".") != 0)                    if (strcmp (arg, ".") != 0)
                     {                      {
                       if (!(dom = canonicalise_opt(arg)))                        if (!(dom = canonicalise_opt(arg)))
                        ret_err(_("bad domain in dhcp-option"));                        goto_err(_("bad domain in dhcp-option"));
                                                   
                       domlen = strlen(dom) + 2;                        domlen = strlen(dom) + 2;
                     }                      }
Line 1141  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1814  static int parse_dhcp_opt(char *errstr, char *arg, int
                 }                  }
               
               /* RFC 3361, enc byte is zero for names */                /* RFC 3361, enc byte is zero for names */
              if (new->opt == OPTION_SIP_SERVER)              if (new->opt == OPTION_SIP_SERVER && m)
                 m[0] = 0;                  m[0] = 0;
               new->len = (int) len + header_size;                new->len = (int) len + header_size;
               new->val = m;                new->val = m;
Line 1179  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1852  static int parse_dhcp_opt(char *errstr, char *arg, int
             }              }
           else if (comma && (opt_len & OT_RFC1035_NAME))            else if (comma && (opt_len & OT_RFC1035_NAME))
             {              {
              unsigned char *p = NULL, *newp, *end;              unsigned char *p = NULL, *q, *newp, *end;
               int len = 0;                int len = 0;
                 int header_size = (is6 && new->opt == OPTION6_NTP_SERVER) ? 4 : 0;
               arg = comma;                arg = comma;
               comma = split(arg);                comma = split(arg);
                               
Line 1188  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1862  static int parse_dhcp_opt(char *errstr, char *arg, int
                 {                  {
                   char *dom = canonicalise_opt(arg);                    char *dom = canonicalise_opt(arg);
                   if (!dom)                    if (!dom)
                    ret_err(_("bad domain in dhcp-option"));                    goto_err(_("bad domain in dhcp-option"));
                                                                       
                  newp = opt_malloc(len + strlen(dom) + 2);                  newp = opt_malloc(len + header_size + strlen(dom) + 2);
                                       
                   if (p)                    if (p)
                     {                      {
Line 1199  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1873  static int parse_dhcp_opt(char *errstr, char *arg, int
                     }                      }
                                       
                   p = newp;                    p = newp;
                  end = do_rfc1035_name(p + len, dom);                  q = p + len;
                   end = do_rfc1035_name(q + header_size, dom, NULL);
                   *end++ = 0;                    *end++ = 0;
                     if (is6 && new->opt == OPTION6_NTP_SERVER)
                       {
                         PUTSHORT(NTP_SUBOPTION_SRV_FQDN, q);
                         PUTSHORT(end - q - 2, q);
                       }
                   len = end - p;                    len = end - p;
                   free(dom);                    free(dom);
   
Line 1226  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1906  static int parse_dhcp_opt(char *errstr, char *arg, int
       ((new->len > 255) ||         ((new->len > 255) || 
       (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||        (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
        (new->len > 250 && (new->flags & DHOPT_RFC3925))))         (new->len > 250 && (new->flags & DHOPT_RFC3925))))
    ret_err(_("dhcp-option too long"));    goto_err(_("dhcp-option too long"));
       
   if (flags == DHOPT_MATCH)    if (flags == DHOPT_MATCH)
     {      {
       if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||        if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
           !new->netid ||            !new->netid ||
           new->netid->next)            new->netid->next)
        ret_err(_("illegal dhcp-match"));        goto_err(_("illegal dhcp-match"));
                 
       if (is6)        if (is6)
         {          {
Line 1258  static int parse_dhcp_opt(char *errstr, char *arg, int Line 1938  static int parse_dhcp_opt(char *errstr, char *arg, int
     }      }
           
   return 1;    return 1;
   on_error:
     dhcp_opt_free(new);
     return 0;
 }  }
   
 #endif  #endif
   
 void set_option_bool(unsigned int opt)  void set_option_bool(unsigned int opt)
 {  {
  if (opt < 32)  option_var(opt) |= option_val(opt);
    daemon->options |= 1u << opt; 
  else 
    daemon->options2 |= 1u << (opt - 32); 
 }  }
   
 void reset_option_bool(unsigned int opt)  void reset_option_bool(unsigned int opt)
 {  {
  if (opt < 32)  option_var(opt) &= ~(option_val(opt));
    daemon->options &= ~(1u << opt); 
  else 
    daemon->options2 &= ~(1u << (opt - 32)); 
 }  }
   
static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line)static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
 {        {      
   int i;    int i;
   char *comma;    char *comma;
Line 1330  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;
         struct dirent *ent;          struct dirent *ent;
         char *directory, *path;          char *directory, *path;
         struct list {          struct list {
          char *suffix;          char *name;
           struct list *next;            struct list *next;
        } *ignore_suffix = NULL, *li;        } *ignore_suffix = NULL, *match_suffix = NULL, *files = NULL, *li;
                   
         comma = split(arg);          comma = split(arg);
         if (!(directory = opt_string_alloc(arg)))          if (!(directory = opt_string_alloc(arg)))
Line 1347  static int one_opt(int option, char *arg, char *errstr Line 2035  static int one_opt(int option, char *arg, char *errstr
         for (arg = comma; arg; arg = comma)           for (arg = comma; arg; arg = comma) 
           {            {
             comma = split(arg);              comma = split(arg);
            li = opt_malloc(sizeof(struct list));            if (strlen(arg) != 0)
            li->next = ignore_suffix;              {
            ignore_suffix = li;                li = opt_malloc(sizeof(struct list));
            /* Have to copy: buffer is overwritten */                if (*arg == '*')
            li->suffix = opt_string_alloc(arg);                  {
          };                    /* "*" with no suffix is a no-op */
                     if (arg[1] == 0)
                       free(li);
                     else
                       {
                         li->next = match_suffix;
                         match_suffix = li;
                         /* Have to copy: buffer is overwritten */
                         li->name = opt_string_alloc(arg+1);
                       }
                   }
                 else
                   {
                     li->next = ignore_suffix;
                     ignore_suffix = li;
                     /* Have to copy: buffer is overwritten */
                     li->name = opt_string_alloc(arg);
                   }
               }
           }
                   
         if (!(dir_stream = opendir(directory)))          if (!(dir_stream = opendir(directory)))
           die(_("cannot access directory %s: %s"), directory, EC_FILE);            die(_("cannot access directory %s: %s"), directory, EC_FILE);
Line 1369  static int one_opt(int option, char *arg, char *errstr Line 2076  static int one_opt(int option, char *arg, char *errstr
                 ent->d_name[0] == '.')                  ent->d_name[0] == '.')
               continue;                continue;
   
               if (match_suffix)
                 {
                   for (li = match_suffix; li; li = li->next)
                     {
                       /* check for required suffices */
                       size_t ls = strlen(li->name);
                       if (len > ls &&
                           strcmp(li->name, &ent->d_name[len - ls]) == 0)
                         break;
                     }
                   if (!li)
                     continue;
                 }
               
             for (li = ignore_suffix; li; li = li->next)              for (li = ignore_suffix; li; li = li->next)
               {                {
                 /* check for proscribed suffices */                  /* check for proscribed suffices */
                size_t ls = strlen(li->suffix);                size_t ls = strlen(li->name);
                 if (len > ls &&                  if (len > ls &&
                    strcmp(li->suffix, &ent->d_name[len - ls]) == 0)                    strcmp(li->name, &ent->d_name[len - ls]) == 0)
                   break;                    break;
               }                }
             if (li)              if (li)
Line 1391  static int one_opt(int option, char *arg, char *errstr Line 2112  static int one_opt(int option, char *arg, char *errstr
                           
             /* only reg files allowed. */              /* only reg files allowed. */
             if (S_ISREG(buf.st_mode))              if (S_ISREG(buf.st_mode))
              one_file(path, 0);              {
                            /* sort files into order. */
            free(path);                struct list **up, *new = opt_malloc(sizeof(struct list));
                 new->name = path;
                 
                 for (up = &files, li = files; li; up = &li->next, li = li->next)
                   if (strcmp(li->name, path) >=0)
                     break;
 
                 new->next = li;
                 *up = new;
               }
             else
               free(path);
 
           }            }
     
         for (li = files; li; li = li->next)
           one_file(li->name, 0);
         
         closedir(dir_stream);          closedir(dir_stream);
         free(directory);          free(directory);
         for(; ignore_suffix; ignore_suffix = li)          for(; ignore_suffix; ignore_suffix = li)
           {            {
             li = ignore_suffix->next;              li = ignore_suffix->next;
            free(ignore_suffix->suffix);            free(ignore_suffix->name);
             free(ignore_suffix);              free(ignore_suffix);
           }            }
                      for(; match_suffix; match_suffix = li)
           {
             li = match_suffix->next;
             free(match_suffix->name);
             free(match_suffix);
           }
         for(; files; files = li)
           {
             li = files->next;
             free(files->name);
             free(files);
           }
         break;          break;
       }        }
   
       case LOPT_ADD_SBNET: /* --add-subnet */
         set_option_bool(OPT_CLIENT_SUBNET);
         if (arg)
           {
             char *err, *end;
             comma = split(arg);
   
             struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
             if ((end = split_chr(arg, '/')))
               {
                 /* has subnet+len */
                 err = parse_mysockaddr(arg, &new->addr);
                 if (err)
                   ret_err_free(err, new);
                 if (!atoi_check(end, &new->mask))
                   ret_err_free(gen_err, new);
                 new->addr_used = 1;
               } 
             else if (!atoi_check(arg, &new->mask))
               ret_err_free(gen_err, new);
               
             daemon->add_subnet4 = new;
   
             if (comma)
               {
                 new = opt_malloc(sizeof(struct mysubnet));
                 if ((end = split_chr(comma, '/')))
                   {
                     /* has subnet+len */
                     err = parse_mysockaddr(comma, &new->addr);
                     if (err)
                       ret_err_free(err, new);
                     if (!atoi_check(end, &new->mask))
                       ret_err_free(gen_err, new);
                     new->addr_used = 1;
                   }
                 else
                   {
                     if (!atoi_check(comma, &new->mask))
                       ret_err_free(gen_err, new);
                   }
             
                 daemon->add_subnet6 = new;
               }
           }
         break;
   
     case '1': /* --enable-dbus */      case '1': /* --enable-dbus */
       set_option_bool(OPT_DBUS);        set_option_bool(OPT_DBUS);
       if (arg)        if (arg)
Line 1415  static int one_opt(int option, char *arg, char *errstr Line 2209  static int one_opt(int option, char *arg, char *errstr
       else        else
         daemon->dbus_name = DNSMASQ_SERVICE;          daemon->dbus_name = DNSMASQ_SERVICE;
       break;        break;
      
     case LOPT_UBUS: /* --enable-ubus */
       set_option_bool(OPT_UBUS);
       if (arg)
         daemon->ubus_name = opt_string_alloc(arg);
       else
         daemon->ubus_name = DNSMASQ_UBUS_NAME;
       break;
 
     case '8': /* --log-facility */      case '8': /* --log-facility */
       /* may be a filename */        /* may be a filename */
       if (strchr(arg, '/') || strcmp (arg, "-") == 0)        if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Line 1436  static int one_opt(int option, char *arg, char *errstr Line 2238  static int one_opt(int option, char *arg, char *errstr
 #endif  #endif
         }          }
       break;        break;
      
     case 'x': /* --pid-file */      case 'x': /* --pid-file */
       daemon->runfile = opt_string_alloc(arg);        daemon->runfile = opt_string_alloc(arg);
       break;        break;
Line 1470  static int one_opt(int option, char *arg, char *errstr Line 2272  static int one_opt(int option, char *arg, char *errstr
         daemon->resolv_files = list;          daemon->resolv_files = list;
         break;          break;
       }        }
   
       case LOPT_SERVERS_FILE:
         daemon->servers_file = opt_string_alloc(arg);
         break;
               
     case 'm':  /* --mx-host */      case 'm':  /* --mx-host */
       {        {
Line 1486  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 1503  static int one_opt(int option, char *arg, char *errstr Line 2313  static int one_opt(int option, char *arg, char *errstr
         ret_err(_("bad MX target"));          ret_err(_("bad MX target"));
       break;        break;
   
       case LOPT_DUMPFILE:  /* --dumpfile */
         daemon->dump_file = opt_string_alloc(arg);
         break;
   
       case LOPT_DUMPMASK:  /* --dumpmask */
         daemon->dump_mask = strtol(arg, NULL, 0);
         break;
         
 #ifdef HAVE_DHCP        #ifdef HAVE_DHCP      
     case 'l':  /* --dhcp-leasefile */      case 'l':  /* --dhcp-leasefile */
       daemon->lease_file = opt_string_alloc(arg);        daemon->lease_file = opt_string_alloc(arg);
Line 1511  static int one_opt(int option, char *arg, char *errstr Line 2329  static int one_opt(int option, char *arg, char *errstr
       /* Sorry about the gross pre-processor abuse */        /* Sorry about the gross pre-processor abuse */
     case '6':             /* --dhcp-script */      case '6':             /* --dhcp-script */
     case LOPT_LUASCRIPT:  /* --dhcp-luascript */      case LOPT_LUASCRIPT:  /* --dhcp-luascript */
#  if defined(NO_FORK)#  if !defined(HAVE_SCRIPT)
      ret_err(_("cannot run scripts under uClinux")); 
#  elif !defined(HAVE_SCRIPT) 
       ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));        ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
 #  else  #  else
       if (option == LOPT_LUASCRIPT)        if (option == LOPT_LUASCRIPT)
Line 1528  static int one_opt(int option, char *arg, char *errstr Line 2344  static int one_opt(int option, char *arg, char *errstr
       break;        break;
 #endif /* HAVE_DHCP */  #endif /* HAVE_DHCP */
   
    case LOPT_DHCP_HOST: /* --dhcp-hostfile */    case LOPT_DHCP_HOST:     /* --dhcp-hostsfile */
    case LOPT_DHCP_OPTS: /* --dhcp-optsfile */    case LOPT_DHCP_OPTS:     /* --dhcp-optsfile */
    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 int hosts_index = 1;  
         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 1551  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;
          }                 }
         
         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 */
      if (!(comma = split(arg)))      comma = split(arg);
        ret_err(gen_err); 
               
       daemon->authserver = opt_string_alloc(arg);        daemon->authserver = opt_string_alloc(arg);
      arg = comma;      
      do {      while ((arg = comma))
        struct iname *new = opt_malloc(sizeof(struct iname));        {
        comma = split(arg);          struct iname *new = opt_malloc(sizeof(struct iname));
        new->name = NULL;          comma = split(arg);
        unhide_metas(arg);          new->name = NULL;
        if ((new->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)          unhide_metas(arg);
          new->addr.sa.sa_family = AF_INET;          if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
#ifdef HAVE_IPV6            new->addr.sa.sa_family = AF_INET;
        else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)          else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
          new->addr.sa.sa_family = AF_INET6;            new->addr.sa.sa_family = AF_INET6;
#endif          else
        else            {
          new->name = opt_string_alloc(arg);              char *fam = split_chr(arg, '/');
                      new->name = opt_string_alloc(arg);
        new->next = daemon->authinterface;              new->addr.sa.sa_family = 0;
        daemon->authinterface = new;              if (fam)
                        {
        arg = comma;                  if (strcmp(fam, "4") == 0)
      } while (arg);                    new->addr.sa.sa_family = AF_INET;
                   else if (strcmp(fam, "6") == 0)
                     new->addr.sa.sa_family = AF_INET6;
                   else
                   {
                     free(new->name);
                     ret_err_free(gen_err, new);
                   }
                 } 
             }
           new->next = daemon->authinterface;
           daemon->authinterface = new;
         };
                           
       break;        break;
   
Line 1605  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->interface_names = NULL;
         new->next = daemon->auth_zones;          new->next = daemon->auth_zones;
         daemon->auth_zones = new;          daemon->auth_zones = new;
   
         while ((arg = comma))          while ((arg = comma))
           {            {
             int prefixlen = 0;              int prefixlen = 0;
               int is_exclude = 0;
             char *prefix;              char *prefix;
            struct subnet *subnet =  opt_malloc(sizeof(struct subnet));            struct addrlist *subnet =  NULL;
                        union all_addr addr;
            subnet->next = new->subnet; 
            new->subnet = subnet; 
   
             comma = split(arg);              comma = split(arg);
             prefix = split_chr(arg, '/');              prefix = split_chr(arg, '/');
Line 1625  static int one_opt(int option, char *arg, char *errstr Line 2474  static int one_opt(int option, char *arg, char *errstr
             if (prefix && !atoi_check(prefix, &prefixlen))              if (prefix && !atoi_check(prefix, &prefixlen))
               ret_err(gen_err);                ret_err(gen_err);
                           
            if (inet_pton(AF_INET, arg, &subnet->addr4))            if (strstr(arg, "exclude:") == arg)
               {                {
                if ((prefixlen & 0x07) != 0 || prefixlen > 24)                    is_exclude = 1;
                  ret_err(_("bad prefix"));                    arg = arg+8;
               }
 
             if (inet_pton(AF_INET, arg, &addr.addr4))
               {
                 subnet = opt_malloc(sizeof(struct addrlist));
                 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;                  subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
                subnet->is6 = 0;                subnet->flags = ADDRLIST_LITERAL;
               }                }
#ifdef HAVE_IPV6            else if (inet_pton(AF_INET6, arg, &addr.addr6))
            else if (inet_pton(AF_INET6, arg, &subnet->addr6)) 
               {                {
                   subnet = opt_malloc(sizeof(struct addrlist));
                 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;                  subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
                subnet->is6 = 1;                subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
               }                }
#endif            else 
            else              {
                ret_err(gen_err);                struct auth_name_list *name =  opt_malloc(sizeof(struct auth_name_list));
                 name->name = opt_string_alloc(arg);
                 name->flags = AUTH4 | AUTH6;
                 name->next = new->interface_names;
                 new->interface_names = name;
                 if (prefix)
                   {
                     if (prefixlen == 4)
                       name->flags &= ~AUTH6;
                     else if (prefixlen == 6)
                       name->flags &= ~AUTH4;
                     else
                       ret_err(gen_err);
                   }
               }
             
             if (subnet)
               {
                 subnet->addr = addr;
 
                 if (is_exclude)
                   {
                     subnet->next = new->exclude;
                     new->exclude = subnet;
                   }
                 else
                   {
                     subnet->next = new->subnet;
                     new->subnet = subnet;
                   }
               }
           }            }
         break;          break;
       }        }
      
     case  LOPT_AUTHSOA: /* --auth-soa */      case  LOPT_AUTHSOA: /* --auth-soa */
       comma = split(arg);        comma = split(arg);
      atoi_check(arg, (int *)&daemon->soa_sn);      daemon->soa_sn = (u32)atoi(arg);
       if (comma)        if (comma)
         {          {
           char *cp;            char *cp;
           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 1662  static int one_opt(int option, char *arg, char *errstr Line 2546  static int one_opt(int option, char *arg, char *errstr
             {              {
               arg = comma;                arg = comma;
               comma = split(arg);                 comma = split(arg); 
              atoi_check(arg, (int *)&daemon->soa_refresh);              daemon->soa_refresh = (u32)atoi(arg);
               if (comma)                if (comma)
                 {                  {
                   arg = comma;                    arg = comma;
                   comma = split(arg);                     comma = split(arg); 
                  atoi_check(arg, (int *)&daemon->soa_retry);                  daemon->soa_retry = (u32)atoi(arg);
                   if (comma)                    if (comma)
                    {                    daemon->soa_expiry = (u32)atoi(comma);
                      arg = comma; 
                      comma = split(arg);  
                      atoi_check(arg, (int *)&daemon->soa_expiry); 
                    } 
                 }                  }
             }              }
         }          }
   
       break;        break;
   
    case 's': /* --domain */    case 's':         /* --domain */
     case LOPT_SYNTH:  /* --synth-domain */
       if (strcmp (arg, "#") == 0)        if (strcmp (arg, "#") == 0)
         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));
                   char *netpart;                    char *netpart;
                  
                   new->prefix = NULL;
                   new->indexed = 0;
                   new->prefixlen = 0;
                   
                   unhide_metas(comma);                    unhide_metas(comma);
                   if ((netpart = split_chr(comma, '/')))                    if ((netpart = split_chr(comma, '/')))
                     {                      {
Line 1703  static int one_opt(int option, char *arg, char *errstr Line 2589  static int one_opt(int option, char *arg, char *errstr
   
                       arg = split(netpart);                        arg = split(netpart);
                       if (!atoi_check(netpart, &msize))                        if (!atoi_check(netpart, &msize))
                        ret_err(gen_err);                        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);
                           if (arg)                            if (arg)
                             {                              {
                              /* generate the equivalent of                              if (option != 's')
                                 local=/<domain>/                                {
                                 local=/xxx.yyy.zzz.in-addr.arpa/ */                                  if (!(new->prefix = canonicalise_opt(arg)) ||
                                      strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
                              if (strcmp(arg, "local") != 0 ||                                     ret_err_free(_("bad prefix"), new);
                                  (msize != 8 && msize != 16 && msize != 24))                                }
                                ret_err(gen_err);                              else if (strcmp(arg, "local") != 0)
                                 ret_err_free(gen_err, new);
                               else                                else
                                 {                                  {
                                  struct server *serv = opt_malloc(sizeof(struct server));                                  /* local=/xxx.yyy.zzz.in-addr.arpa/ */
                                  in_addr_t a = ntohl(new->start.s_addr) >> 8;                                  domain_rev4(0, NULL, &new->start, msize);
                                  char *p;                                                                  
                                  /* local=/<domain>/ */
                                  memset(serv, 0, sizeof(struct server));                                  /* d_raw can't failed to canonicalise here, checked above. */
                                  serv->domain = d;                                  add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL);
                                  serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; 
                                  serv->next = daemon->servers; 
                                  daemon->servers = serv; 
 
                                  serv = opt_malloc(sizeof(struct server)); 
                                  memset(serv, 0, sizeof(struct server)); 
                                  p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */ 
                                   
                                  if (msize == 24) 
                                    p += sprintf(p, "%d.", a & 0xff); 
                                  a = a >> 8; 
                                  if (msize != 8) 
                                    p += sprintf(p, "%d.", a & 0xff); 
                                  a = a >> 8; 
                                  p += sprintf(p, "%d.in-addr.arpa", a & 0xff); 
 
                                  serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; 
                                  serv->next = daemon->servers; 
                                  daemon->servers = serv; 
                                 }                                  }
                             }                              }
                         }                          }
 #ifdef HAVE_IPV6  
                       else if (inet_pton(AF_INET6, comma, &new->start6))                        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); 
                          new->is6 = 1; 
   
                             if (msize > 128)
                               ret_err_free(_("bad prefix length"), new);
   
                             mask = (1LLU << (128 - msize)) - 1LLU;
   
                             new->is6 = 1;
                             new->prefixlen = msize;
                             
                           /* prefix==64 overflows the mask calculation above */                            /* 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(gen_err); 
                          else if (arg) 
                             {                              {
                              /* generate the equivalent of                              if (option != 's')
                                 local=/<domain>/                                {
                                 local=/xxx.yyy.zzz.ip6.arpa/ */                                  if (!(new->prefix = canonicalise_opt(arg)) ||
                                      strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
                              if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))                                    ret_err_free(_("bad prefix"), new);
                                ret_err(gen_err);                                }       
                               else if (strcmp(arg, "local") != 0)
                                 ret_err_free(gen_err, new);
                               else                                 else 
                                 {                                  {
                                  struct server *serv = opt_malloc(sizeof(struct server));                                  /* generate the equivalent of
                                  char *p;                                     local=/xxx.yyy.zzz.ip6.arpa/ */
                                   domain_rev6(0, NULL, &new->start6, msize);
                                                                       
                                  memset(serv, 0, sizeof(struct server));                                  /* local=/<domain>/ */
                                  serv->domain = d;                                  /* d_raw can't failed to canonicalise here, checked above. */
                                  serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;                                  add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL);
                                  serv->next = daemon->servers; 
                                  daemon->servers = serv; 
 
                                  serv = opt_malloc(sizeof(struct server)); 
                                  memset(serv, 0, sizeof(struct server)); 
                                  p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */ 
                                   
                                  for (i = msize-1; i >= 0; i -= 4) 
                                    {  
                                      int dig = ((unsigned char *)&new->start6)[i>>3]; 
                                      p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4); 
                                    } 
                                  p += sprintf(p, "ip6.arpa"); 
                                   
                                  serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; 
                                  serv->next = daemon->servers; 
                                  daemon->servers = serv; 
                                 }                                  }
                             }                              }
                         }                          }
 #endif  
                       else                        else
                        ret_err(gen_err);                        ret_err_free(gen_err, new);
                     }                      }
                  else                   else
                     {                      {
                         char *prefstr;
                       arg = split(comma);                        arg = split(comma);
                         prefstr = split(arg);
   
                       if (inet_pton(AF_INET, comma, &new->start))                        if (inet_pton(AF_INET, comma, &new->start))
                         {                          {
                           new->is6 = 0;                            new->is6 = 0;
                           if (!arg)                            if (!arg)
                             new->end.s_addr = new->start.s_addr;                              new->end.s_addr = new->start.s_addr;
                           else if (!inet_pton(AF_INET, arg, &new->end))                            else if (!inet_pton(AF_INET, arg, &new->end))
                            ret_err(gen_err);                            ret_err_free(gen_err, new);
                         }                          }
 #ifdef HAVE_IPV6  
                       else if (inet_pton(AF_INET6, comma, &new->start6))                        else if (inet_pton(AF_INET6, comma, &new->start6))
                         {                          {
                           new->is6 = 1;                            new->is6 = 1;
                           if (!arg)                            if (!arg)
                             memcpy(&new->end6, &new->start6, IN6ADDRSZ);                              memcpy(&new->end6, &new->start6, IN6ADDRSZ);
                           else if (!inet_pton(AF_INET6, arg, &new->end6))                            else if (!inet_pton(AF_INET6, arg, &new->end6))
                            ret_err(gen_err);                            ret_err_free(gen_err, new);
                         }                          }
#endif                      else if (option == 's')
                      else                         {
                        ret_err(gen_err);                          /* subnet from interface. */
                           new->interface = opt_string_alloc(comma);
                           new->al = NULL;
                         }
                       else
                         ret_err_free(gen_err, new);
                       
                       if (option != 's' && prefstr)
                         {
                           if (!(new->prefix = canonicalise_opt(prefstr)) ||
                               strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
                             ret_err_free(_("bad prefix"), new);
                         }
                     }                      }
   
                  new->domain = d;                  new->domain = canonicalise_opt(d_raw);
                  new->next = daemon->cond_domain;                  if (option  == 's')
                  daemon->cond_domain = new;                                     {
                       new->next = daemon->cond_domain;
                       daemon->cond_domain = new;
                     }
                   else
                     {
                       char *star;
                       if (new->prefix &&
                           (star = strrchr(new->prefix, '*'))
                           && *(star+1) == 0)
                         {
                           *star = 0;
                           new->indexed = 1;
                           if (new->is6 && new->prefixlen < 64)
                             ret_err_free(_("prefix length too small"), new);
                         }
                       new->next = daemon->synth_domains;
                       daemon->synth_domains = new;
                     }
                 }                  }
              else              else if (option == 's')
                daemon->domain_suffix = d;                daemon->domain_suffix = canonicalise_opt(d_raw);
               else 
                 ret_err(gen_err);
             }              }
         }          }
       break;        break;
               
       case LOPT_CPE_ID: /* --add-dns-client */
         if (arg)
           daemon->dns_client_id = opt_string_alloc(arg);
         break;
   
       case LOPT_UMBRELLA: /* --umbrella */
         set_option_bool(OPT_UMBRELLA);
         while (arg)
           {
             comma = split(arg);
             if (strstr(arg, "deviceid:"))
               {
                 char *p;
                 u8 *u = daemon->umbrella_device;
                 char word[3];
                 
                 arg += 9;
                 if (strlen(arg) != 16)
                   ret_err(gen_err);
                 
                 for (p = arg; *p; p++)
                   if (!isxdigit((unsigned char)*p))
                     ret_err(gen_err);
                 
                 set_option_bool(OPT_UMBRELLA_DEVID);
                 
                 for (i = 0; i < (int)sizeof(daemon->umbrella_device); i++, arg+=2)
                   {
                     memcpy(word, &(arg[0]), 2);
                     *u++ = strtoul(word, NULL, 16);
                   }
               }
             else if (strstr(arg, "orgid:"))
               {
                 if (!strtoul_check(arg+6, &daemon->umbrella_org))
                   ret_err(gen_err);
               }
             else if (strstr(arg, "assetid:"))
               {
                 if (!strtoul_check(arg+8, &daemon->umbrella_asset))
                   ret_err(gen_err);
               }
             else
               ret_err(gen_err);
             
             arg = comma;
           }
         break;
         
       case LOPT_ADD_MAC: /* --add-mac */
         if (!arg)
           set_option_bool(OPT_ADD_MAC);
         else
           {
             unhide_metas(arg);
             if (strcmp(arg, "base64") == 0)
               set_option_bool(OPT_MAC_B64);
             else if (strcmp(arg, "text") == 0)
               set_option_bool(OPT_MAC_HEX);
             else
               ret_err(gen_err);
           }
         break;
   
     case 'u':  /* --user */      case 'u':  /* --user */
       daemon->username = opt_string_alloc(arg);        daemon->username = opt_string_alloc(arg);
       break;        break;
Line 1870  static int one_opt(int option, char *arg, char *errstr Line 2829  static int one_opt(int option, char *arg, char *errstr
       } while (arg);        } while (arg);
       break;        break;
               
       case LOPT_TFTP: /* --enable-tftp */
         set_option_bool(OPT_TFTP);
         if (!arg)
           break;
         /* fall through */
   
     case 'I':  /* --except-interface */      case 'I':  /* --except-interface */
     case '2':  /* --no-dhcp-interface */      case '2':  /* --no-dhcp-interface */
       do {        do {
Line 1881  static int one_opt(int option, char *arg, char *errstr Line 2846  static int one_opt(int option, char *arg, char *errstr
             new->next = daemon->if_except;              new->next = daemon->if_except;
             daemon->if_except = new;              daemon->if_except = new;
           }            }
           else if (option == LOPT_TFTP)
              {
               new->next = daemon->tftp_interfaces;
               daemon->tftp_interfaces = new;
             }
         else          else
           {            {
             new->next = daemon->dhcp_except;              new->next = daemon->dhcp_except;
Line 1891  static int one_opt(int option, char *arg, char *errstr Line 2861  static int one_opt(int option, char *arg, char *errstr
       break;        break;
               
     case 'B':  /* --bogus-nxdomain */      case 'B':  /* --bogus-nxdomain */
      {    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 && (addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
         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)
               prefix = 128;
             else
               prefix = 32;
           }
 
         if (prefix > 128 || (!is6 && prefix > 32))
           ret_err(gen_err);
         
         baddr = opt_malloc(sizeof(struct bogus_addr));
         if (option == 'B')
           {
             baddr->next = daemon->bogus_addr;              baddr->next = daemon->bogus_addr;
             daemon->bogus_addr = baddr;              daemon->bogus_addr = baddr;
             baddr->addr = addr;  
           }            }
         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 1912  static int one_opt(int option, char *arg, char *errstr Line 2913  static int one_opt(int option, char *arg, char *errstr
         struct iname *new = opt_malloc(sizeof(struct iname));          struct iname *new = opt_malloc(sizeof(struct iname));
         comma = split(arg);          comma = split(arg);
         unhide_metas(arg);          unhide_metas(arg);
        if (arg && (new->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)        if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
           {            {
             new->addr.sa.sa_family = AF_INET;              new->addr.sa.sa_family = AF_INET;
             new->addr.in.sin_port = 0;              new->addr.in.sin_port = 0;
Line 1920  static int one_opt(int option, char *arg, char *errstr Line 2921  static int one_opt(int option, char *arg, char *errstr
             new->addr.in.sin_len = sizeof(new->addr.in);              new->addr.in.sin_len = sizeof(new->addr.in);
 #endif  #endif
           }            }
 #ifdef HAVE_IPV6  
         else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)          else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
           {            {
             new->addr.sa.sa_family = AF_INET6;              new->addr.sa.sa_family = AF_INET6;
Line 1931  static int one_opt(int option, char *arg, char *errstr Line 2931  static int one_opt(int option, char *arg, char *errstr
             new->addr.in6.sin6_len = sizeof(new->addr.in6);              new->addr.in6.sin6_len = sizeof(new->addr.in6);
 #endif  #endif
           }            }
 #endif  
         else          else
          ret_err(gen_err);          ret_err_free(gen_err, new);
   
         new->used = 0;          new->used = 0;
         if (option == 'a')          if (option == 'a')
Line 1950  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))) 
                  option = '?'; 
                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));  
           }  
                   
        if (option == 'A')        if (!arg || !*arg)
           flags = SERV_LITERAL_ADDRESS;
         else 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)
              ret_err(gen_err);              flags = SERV_ALL_ZEROS | SERV_LITERAL_ADDRESS;
             else if (inet_pton(AF_INET, arg, &addr.addr4) > 0)
               flags = SERV_4ADDR | SERV_LITERAL_ADDRESS;
             else if (inet_pton(AF_INET6, arg, &addr.addr6) > 0)
               flags = SERV_6ADDR | SERV_LITERAL_ADDRESS;
             else
               ret_err(_("Bad address in --address"));
           }            }
        else if (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);
            if (newlist->flags & SERV_LITERAL_ADDRESS) 
              ret_err(gen_err); 
           }            }
   
        else if (strcmp(arg, "#") == 0)        if (servers_only && option == 'S')
           flags |= SERV_FROM_FILE;
 
         cur_domain = domain;
         while ((flags & SERV_LITERAL_ADDRESS) || parse_server_next(&sdetails))
           {            {
            newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */            cur_domain = domain;
            if (newlist->flags & SERV_LITERAL_ADDRESS)
              ret_err(gen_err);            if (!(flags & SERV_LITERAL_ADDRESS) && (err = parse_server_addr(&sdetails)))
          } 
        else 
          { 
            char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags); 
            if (err) 
               ret_err(err);                ret_err(err);
   
               /* When source is set only use DNS records of the same type and skip all others */
               if (flags & SERV_HAS_SOURCE && sdetails.addr_type != sdetails.source_addr->sa.sa_family)
                 continue;
   
               while (1)
                 {
                   /* server=//1.2.3.4 is special. */
                   if (lastdomain)
                     {
                       if (strlen(cur_domain) == 0)
                         flags |= SERV_FOR_NODOTS;
                       else
                         flags &= ~SERV_FOR_NODOTS;
                       
                       /* address=/#/ matches the same as without domain */
                       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)      }
 
     case LOPT_REV_SERV: /* --rev-server */
       {
         char *string;
         int size;
         struct in_addr addr4;
         struct in6_addr addr6;
         
         unhide_metas(arg);
         if (!arg)
           ret_err(gen_err);
         
         comma=split(arg);
         
         if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
           size = -1;
 
         if (inet_pton(AF_INET, arg, &addr4))
           {            {
            serv->next->flags = serv->flags;           if (size == -1)
            serv->next->addr = serv->addr;             size = 32;
            serv->next->source_addr = serv->source_addr;
            strcpy(serv->next->interface, serv->interface);           if ((string = domain_rev4(servers_only, comma, &addr4, size)))
            serv = serv->next;              ret_err(string);
           }            }
        serv->next = daemon->servers;        else if (inet_pton(AF_INET6, arg, &addr6))
        daemon->servers = newlist;          {
              if (size == -1)
                size = 128;
 
              if ((string = domain_rev6(servers_only, comma, &addr6, size)))
               ret_err(string);
           }
         else
           ret_err(gen_err);
         
         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 2064  static int one_opt(int option, char *arg, char *errstr Line 3151  static int one_opt(int option, char *arg, char *errstr
                  if (strcmp(arg, "#") == 0 || !*arg)                   if (strcmp(arg, "#") == 0 || !*arg)
                    domain = "";                     domain = "";
                  else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))                   else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
                   option = '?';                   ret_err(gen_err);
                  ipsets->next = opt_malloc(sizeof(struct ipsets));                   ipsets->next = opt_malloc(sizeof(struct ipsets));
                  ipsets = ipsets->next;                   ipsets = ipsets->next;
                  memset(ipsets, 0, sizeof(struct ipsets));                   memset(ipsets, 0, sizeof(struct ipsets));
Line 2079  static int one_opt(int option, char *arg, char *errstr Line 3166  static int one_opt(int option, char *arg, char *errstr
              memset(ipsets, 0, sizeof(struct ipsets));               memset(ipsets, 0, sizeof(struct ipsets));
              ipsets->domain = "";               ipsets->domain = "";
            }             }
            
          if (!arg || !*arg)           if (!arg || !*arg)
           {           ret_err(gen_err);
             option = '?';         
             break;         for (size = 2, end = arg; *end; ++end) 
           } 
         size = 2; 
         for (end = arg; *end; ++end)  
            if (*end == ',')             if (*end == ',')
                ++size;                 ++size;
             
          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 2118  static int one_opt(int option, char *arg, char *errstr Line 3337  static int one_opt(int option, char *arg, char *errstr
                           
             if (size < 0)              if (size < 0)
               size = 0;                size = 0;
            else if (size > 10000)
              size = 10000;            /* Note that for very large cache sizes, the malloc()
                will overflow. For the size of the cache record
                at the time this was noted, the value of "very large"
                was 46684428. Limit to an order of magnitude less than
                that to be safe from changes to the cache record. */
             if (size > 5000000)
               size = 5000000;
                           
             daemon->cachesize = size;              daemon->cachesize = size;
           }            }
Line 2136  static int one_opt(int option, char *arg, char *errstr Line 3361  static int one_opt(int option, char *arg, char *errstr
         ret_err(gen_err);          ret_err(gen_err);
       break;        break;
   
       case LOPT_MAXPORT:  /* --max-port */
         if (!atoi_check16(arg, &daemon->max_port))
           ret_err(gen_err);
         break;
   
     case '0':  /* --dns-forward-max */      case '0':  /* --dns-forward-max */
       if (!atoi_check(arg, &daemon->ftabsize))        if (!atoi_check(arg, &daemon->ftabsize))
         ret_err(gen_err);          ret_err(gen_err);
       break;          break;  
           
       case 'q': /* --log-queries */
         set_option_bool(OPT_LOG);
         if (arg && strcmp(arg, "extra") == 0)
           set_option_bool(OPT_EXTRALOG);
         break;
   
     case LOPT_MAX_LOGS:  /* --log-async */      case LOPT_MAX_LOGS:  /* --log-async */
       daemon->max_logs = LOG_MAX; /* default */        daemon->max_logs = LOG_MAX; /* default */
       if (arg && !atoi_check(arg, &daemon->max_logs))        if (arg && !atoi_check(arg, &daemon->max_logs))
Line 2166  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 */
     case LOPT_MAXTTL: /* --max-ttl */      case LOPT_MAXTTL: /* --max-ttl */
       case LOPT_MINCTTL: /* --min-cache-ttl */
     case LOPT_MAXCTTL: /* --max-cache-ttl */      case LOPT_MAXCTTL: /* --max-cache-ttl */
     case LOPT_AUTHTTL: /* --auth-ttl */      case LOPT_AUTHTTL: /* --auth-ttl */
       case LOPT_DHCPTTL: /* --dhcp-ttl */
       {        {
         int ttl;          int ttl;
         if (!atoi_check(arg, &ttl))          if (!atoi_check(arg, &ttl))
Line 2180  static int one_opt(int option, char *arg, char *errstr Line 3423  static int one_opt(int option, char *arg, char *errstr
           daemon->neg_ttl = (unsigned long)ttl;            daemon->neg_ttl = (unsigned long)ttl;
         else if (option == LOPT_MAXTTL)          else if (option == LOPT_MAXTTL)
           daemon->max_ttl = (unsigned long)ttl;            daemon->max_ttl = (unsigned long)ttl;
           else if (option == LOPT_MINCTTL)
             {
               if (ttl > TTL_FLOOR_LIMIT)
                 ttl = TTL_FLOOR_LIMIT;
               daemon->min_cache_ttl = (unsigned long)ttl;
             }
         else if (option == LOPT_MAXCTTL)          else if (option == LOPT_MAXCTTL)
           daemon->max_cache_ttl = (unsigned long)ttl;            daemon->max_cache_ttl = (unsigned long)ttl;
         else if (option == LOPT_AUTHTTL)          else if (option == LOPT_AUTHTTL)
           daemon->auth_ttl = (unsigned long)ttl;            daemon->auth_ttl = (unsigned long)ttl;
           else if (option == LOPT_DHCPTTL)
             {
               daemon->dhcp_ttl = (unsigned long)ttl;
               daemon->use_dhcp_ttl = 1;
             }
         else          else
           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 2202  static int one_opt(int option, char *arg, char *errstr Line 3479  static int one_opt(int option, char *arg, char *errstr
         ret_err(gen_err);          ret_err(gen_err);
       break;          break;  
   
       case LOPT_TFTP_MTU:  /*  --tftp-mtu */
         if (!atoi_check(arg, &daemon->tftp_mtu))
           ret_err(gen_err);
         break;
   
     case LOPT_PREFIX: /* --tftp-prefix */      case LOPT_PREFIX: /* --tftp-prefix */
       comma = split(arg);        comma = split(arg);
       if (comma)        if (comma)
Line 2230  static int one_opt(int option, char *arg, char *errstr Line 3512  static int one_opt(int option, char *arg, char *errstr
         }           } 
               
       break;        break;
   
       case LOPT_APREF: /* --tftp-unique-root */
         if (!arg || strcasecmp(arg, "ip") == 0)
           set_option_bool(OPT_TFTP_APREF_IP);
         else if (strcasecmp(arg, "mac") == 0)
           set_option_bool(OPT_TFTP_APREF_MAC);
         else
           ret_err(gen_err);
         break;
 #endif  #endif
                               
     case LOPT_BRIDGE:   /* --bridge-interface */      case LOPT_BRIDGE:   /* --bridge-interface */
       {        {
        struct dhcp_bridge *new = opt_malloc(sizeof(struct dhcp_bridge));        struct dhcp_bridge *new;
 
         if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )          if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
           ret_err(_("bad bridge-interface"));            ret_err(_("bad bridge-interface"));
           
         strcpy(new->iface, arg);  
         new->alias = NULL;  
         new->next = daemon->bridges;  
         daemon->bridges = new;  
   
           for (new = daemon->bridges; new; new = new->next)
             if (strcmp(new->iface, arg) == 0)
               break;
   
           if (!new)
             {
                new = opt_malloc(sizeof(struct dhcp_bridge));
                strcpy(new->iface, arg);
                new->alias = NULL;
                new->next = daemon->bridges;
                daemon->bridges = new;
             }
           
         do {          do {
           arg = comma;            arg = comma;
           comma = split(arg);            comma = split(arg);
Line 2259  static int one_opt(int option, char *arg, char *errstr Line 3559  static int one_opt(int option, char *arg, char *errstr
       }        }
   
 #ifdef HAVE_DHCP  #ifdef HAVE_DHCP
       case LOPT_SHARED_NET: /* --shared-network */
         {
           struct shared_network *new = opt_malloc(sizeof(struct shared_network));
   
   #ifdef HAVE_DHCP6
           new->shared_addr.s_addr = 0;
   #endif
           new->if_index = 0;
           
           if (!(comma = split(arg)))
             {
             snerr:
               free(new);
               ret_err(_("bad shared-network"));
             }
           
           if (inet_pton(AF_INET, comma, &new->shared_addr))
             {
               if (!inet_pton(AF_INET, arg, &new->match_addr) &&
                   !(new->if_index = if_nametoindex(arg)))
                 goto snerr;
             }
   #ifdef HAVE_DHCP6
           else if (inet_pton(AF_INET6, comma, &new->shared_addr6))
             {
               if (!inet_pton(AF_INET6, arg, &new->match_addr6) &&
                   !(new->if_index = if_nametoindex(arg)))
                 goto snerr;
             }
   #endif
           else
             goto snerr;
   
           new->next = daemon->shared_networks;
           daemon->shared_networks = new;
           break;
         }
             
     case 'F':  /* --dhcp-range */      case 'F':  /* --dhcp-range */
       {        {
         int k, leasepos = 2;          int k, leasepos = 2;
Line 2266  static int one_opt(int option, char *arg, char *errstr Line 3604  static int one_opt(int option, char *arg, char *errstr
         struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));          struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
                   
         memset (new, 0, sizeof(*new));          memset (new, 0, sizeof(*new));
         new->lease_time = DEFLEASE;  
                   
         if (!arg)  
           {  
             option = '?';  
             break;  
           }  
           
         while(1)          while(1)
           {            {
             for (cp = arg; *cp; cp++)              for (cp = arg; *cp; cp++)
Line 2286  static int one_opt(int option, char *arg, char *errstr Line 3617  static int one_opt(int option, char *arg, char *errstr
               {                {
                 if (is_tag_prefix(arg))                  if (is_tag_prefix(arg))
                   {                    {
                    struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));                    /* ignore empty tag */
                    tt->net = opt_string_alloc(arg+4);                    if (arg[4])
                    tt->next = new->filter;                      new->filter = dhcp_netid_create(arg+4, new->filter);
                    new->filter = tt; 
                   }                    }
                 else                  else
                   {                    {
                     if (new->netid.net)                      if (new->netid.net)
                      ret_err(_("only one tag allowed"));                      {
                    else if (strstr(arg, "set:") == arg)                        dhcp_context_free(new);
                      new->netid.net = opt_string_alloc(arg+4);                        ret_err(_("only one tag allowed"));
                       }
                     else                      else
                      new->netid.net = opt_string_alloc(arg);                      new->netid.net = opt_string_alloc(set_prefix(arg));
                   }                    }
                 arg = comma;                  arg = comma;
               }                }
Line 2314  static int one_opt(int option, char *arg, char *errstr Line 3645  static int one_opt(int option, char *arg, char *errstr
             break;              break;
                   
         if (k < 2)          if (k < 2)
          ret_err(_("bad dhcp-range"));          {
             dhcp_context_free(new);
             ret_err(_("bad dhcp-range"));
           }
                   
         if (inet_pton(AF_INET, a[0], &new->start))          if (inet_pton(AF_INET, a[0], &new->start))
           {            {
             new->next = daemon->dhcp;              new->next = daemon->dhcp;
               new->lease_time = DEFLEASE;
             daemon->dhcp = new;              daemon->dhcp = new;
             new->end = new->start;              new->end = new->start;
             if (strcmp(a[1], "static") == 0)              if (strcmp(a[1], "static") == 0)
Line 2326  static int one_opt(int option, char *arg, char *errstr Line 3661  static int one_opt(int option, char *arg, char *errstr
             else if (strcmp(a[1], "proxy") == 0)              else if (strcmp(a[1], "proxy") == 0)
               new->flags |= CONTEXT_PROXY;                new->flags |= CONTEXT_PROXY;
             else if (!inet_pton(AF_INET, a[1], &new->end))              else if (!inet_pton(AF_INET, a[1], &new->end))
              ret_err(_("bad dhcp-range"));              {
                 dhcp_context_free(new);
                 ret_err(_("bad dhcp-range"));
               }
                           
             if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))              if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
               {                {
Line 2336  static int one_opt(int option, char *arg, char *errstr Line 3674  static int one_opt(int option, char *arg, char *errstr
               }                }
                           
             if (k >= 3 && strchr(a[2], '.') &&                if (k >= 3 && strchr(a[2], '.') &&  
                ((new->netmask.s_addr = inet_addr(a[2])) != (in_addr_t)-1))                (inet_pton(AF_INET, a[2], &new->netmask) > 0))
               {                {
                 new->flags |= CONTEXT_NETMASK;                  new->flags |= CONTEXT_NETMASK;
                 leasepos = 3;                  leasepos = 3;
                 if (!is_same_net(new->start, new->end, new->netmask))                  if (!is_same_net(new->start, new->end, new->netmask))
                  ret_err(_("inconsistent DHCP range"));                  {
              }                    dhcp_context_free(new);
                     ret_err(_("inconsistent DHCP range"));
                   }
                 
                           
            if (k >= 4 && strchr(a[3], '.') &&                  if (k >= 4 && strchr(a[3], '.') &&  
                ((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t)-1))                    (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
              {                  {
                new->flags |= CONTEXT_BRDCAST;                    new->flags |= CONTEXT_BRDCAST;
                leasepos = 4;                    leasepos = 4;
                   }
               }                }
           }            }
 #ifdef HAVE_DHCP6  #ifdef HAVE_DHCP6
         else if (inet_pton(AF_INET6, a[0], &new->start6))          else if (inet_pton(AF_INET6, a[0], &new->start6))
           {            {
               const char *err = NULL;
   
               new->flags |= CONTEXT_V6; 
             new->prefix = 64; /* default */              new->prefix = 64; /* default */
             new->end6 = new->start6;              new->end6 = new->start6;
                        new->lease_time = DEFLEASE6;
            /* dhcp-range=:: enables DHCP stateless on any interface */            new->next = daemon->dhcp6;
            if (IN6_IS_ADDR_UNSPECIFIED(&new->start6))            daemon->dhcp6 = new;
              new->prefix = 0;
             
             for (leasepos = 1; leasepos < k; leasepos++)              for (leasepos = 1; leasepos < k; leasepos++)
               {                {
                 if (strcmp(a[leasepos], "static") == 0)                  if (strcmp(a[leasepos], "static") == 0)
                   new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;                    new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
                 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )                  else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
                  new->flags |= CONTEXT_RA_ONLY | CONTEXT_RA;                  new->flags |= CONTEXT_RA;
                 else if (strcmp(a[leasepos], "ra-names") == 0)                  else if (strcmp(a[leasepos], "ra-names") == 0)
                   new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;                    new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
                   else if (strcmp(a[leasepos], "ra-advrouter") == 0)
                     new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
                 else if (strcmp(a[leasepos], "ra-stateless") == 0)                  else if (strcmp(a[leasepos], "ra-stateless") == 0)
                   new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;                    new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
                   else if (strcmp(a[leasepos], "off-link") == 0)
                     new->flags |= CONTEXT_RA_OFF_LINK;
                 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))                  else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
                   new->flags |= CONTEXT_DHCP;                     new->flags |= CONTEXT_DHCP; 
                 else if (strstr(a[leasepos], "constructor:") == a[leasepos])                  else if (strstr(a[leasepos], "constructor:") == a[leasepos])
Line 2381  static int one_opt(int option, char *arg, char *errstr Line 3729  static int one_opt(int option, char *arg, char *errstr
                 else                    else  
                   break;                    break;
               }                }
                                         
            new->next = daemon->dhcp6; 
            daemon->dhcp6 = new; 
                      
             /* bare integer < 128 is prefix value */              /* bare integer < 128 is prefix value */
             if (leasepos < k)              if (leasepos < k)
               {                {
Line 2396  static int one_opt(int option, char *arg, char *errstr Line 3741  static int one_opt(int option, char *arg, char *errstr
                   {                    {
                     new->prefix = pref;                      new->prefix = pref;
                     leasepos++;                      leasepos++;
                     if (new->prefix != 64)  
                       {  
                         if ((new->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))  
                           ret_err(_("prefix must be exactly 64 for RA subnets"));  
                         else if (new->template_interface)  
                           ret_err(_("prefix must be exactly 64 for subnet constructors"));  
                       }  
                     if (new->prefix < 64)  
                       ret_err(_("prefix must be at least 64"));  
                   }                    }
               }                }
                           
            if (!is_same_net6(&new->start6, &new->end6, new->prefix))            if (new->prefix > 64)
              ret_err(_("inconsistent DHCPv6 range"));              {
                 if (new->flags & CONTEXT_RA)
                   err=(_("prefix length must be exactly 64 for RA subnets"));
                 else if (new->flags & CONTEXT_TEMPLATE)
                   err=(_("prefix length must be exactly 64 for subnet constructors"));
               }
             else if (new->prefix < 64)
               err=(_("prefix length must be at least 64"));
             
             if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
               err=(_("inconsistent DHCPv6 range"));
 
             if (err)
               {
                 dhcp_context_free(new);
                 ret_err(err);
               }
 
             /* dhcp-range=:: enables DHCP stateless on any interface */
             if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
               new->prefix = 0;
             
             if (new->flags & CONTEXT_TEMPLATE)
               {
                 struct in6_addr zero;
                 memset(&zero, 0, sizeof(zero));
                 if (!is_same_net6(&zero, &new->start6, new->prefix))
                   {
                     dhcp_context_free(new);
                     ret_err(_("prefix must be zero with \"constructor:\" argument"));
                   }
               }
             
             if (addr6part(&new->start6) > addr6part(&new->end6))              if (addr6part(&new->start6) > addr6part(&new->end6))
               {                {
                 struct in6_addr tmp = new->start6;                  struct in6_addr tmp = new->start6;
Line 2419  static int one_opt(int option, char *arg, char *errstr Line 3787  static int one_opt(int option, char *arg, char *errstr
           }            }
 #endif  #endif
         else          else
          ret_err(_("bad dhcp-range"));          {
             dhcp_context_free(new);
             ret_err(_("bad dhcp-range"));
           }
                   
         if (leasepos < k)          if (leasepos < k)
           {            {
               if (leasepos != k-1)
                 {
                   dhcp_context_free(new);
                   ret_err(_("bad dhcp-range"));
                 }
               
             if (strcmp(a[leasepos], "infinite") == 0)              if (strcmp(a[leasepos], "infinite") == 0)
              new->lease_time = 0xffffffff;              {
                 new->lease_time = 0xffffffff;
                 new->flags |= CONTEXT_SETLEASE;
               }
             else if (strcmp(a[leasepos], "deprecated") == 0)              else if (strcmp(a[leasepos], "deprecated") == 0)
               new->flags |= CONTEXT_DEPRECATE;                new->flags |= CONTEXT_DEPRECATE;
             else              else
Line 2441  static int one_opt(int option, char *arg, char *errstr Line 3821  static int one_opt(int option, char *arg, char *errstr
                       case 'd':                        case 'd':
                       case 'D':                        case 'D':
                         fac *= 24;                          fac *= 24;
                        /* fall though */                        /* fall through */
                       case 'h':                        case 'h':
                       case 'H':                        case 'H':
                         fac *= 60;                          fac *= 60;
Line 2460  static int one_opt(int option, char *arg, char *errstr Line 3840  static int one_opt(int option, char *arg, char *errstr
                         break;                          break;
   
                     if (*cp || (leasepos+1 < k))                      if (*cp || (leasepos+1 < k))
                      ret_err(_("bad dhcp-range"));                      ret_err_free(_("bad dhcp-range"), new);
                                           
                     new->lease_time = atoi(a[leasepos]) * fac;                      new->lease_time = atoi(a[leasepos]) * fac;
                       new->flags |= CONTEXT_SETLEASE;
                     /* Leases of a minute or less confuse                      /* Leases of a minute or less confuse
                        some clients, notably Apple's */                         some clients, notably Apple's */
                     if (new->lease_time < 120)                      if (new->lease_time < 120)
Line 2470  static int one_opt(int option, char *arg, char *errstr Line 3851  static int one_opt(int option, char *arg, char *errstr
                   }                    }
               }                }
           }            }
   
         break;          break;
       }        }
   
     case LOPT_BANK:      case LOPT_BANK:
     case 'G':  /* --dhcp-host */      case 'G':  /* --dhcp-host */
       {        {
         int j, k = 0;  
         char *a[6] = { NULL, NULL, NULL, NULL, NULL, NULL };  
         struct dhcp_config *new;          struct dhcp_config *new;
         struct in_addr in;          struct in_addr in;
                   
Line 2487  static int one_opt(int option, char *arg, char *errstr Line 3867  static int one_opt(int option, char *arg, char *errstr
         new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;          new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
         new->hwaddr = NULL;          new->hwaddr = NULL;
         new->netid = NULL;          new->netid = NULL;
        new->filter = NULL;
        if ((a[0] = arg))        new->clid = NULL;
          for (k = 1; k < 6; k++) 
            if (!(a[k] = split(a[k-1]))) 
              break; 
         
        for (j = 0; j < k; j++) 
          if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */ 
            { 
              char *arg = a[j]; 
               
              if ((arg[0] == 'i' || arg[0] == 'I') && 
                  (arg[1] == 'd' || arg[1] == 'D') && 
                  arg[2] == ':') 
                { 
                  if (arg[3] == '*') 
                    new->flags |= CONFIG_NOCLID; 
                  else 
                    { 
                      int len; 
                      arg += 3; /* dump id: */ 
                      if (strchr(arg, ':')) 
                        len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL); 
                      else 
                        { 
                          unhide_metas(arg); 
                          len = (int) strlen(arg); 
                        } 
 
                      if (len == -1) 
 
                        ret_err(_("bad hex constant")); 
                      else if ((new->clid = opt_malloc(len))) 
                        { 
                          new->flags |= CONFIG_CLID; 
                          new->clid_len = len; 
                          memcpy(new->clid, arg, len); 
                        } 
                    } 
                } 
              /* dhcp-host has strange backwards-compat needs. */ 
              else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg) 
                { 
                  struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid)); 
                  struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list)); 
                  newtag->net = opt_malloc(strlen(arg + 4) + 1); 
                  newlist->next = new->netid; 
                  new->netid = newlist; 
                  newlist->list = newtag; 
                  strcpy(newtag->net, arg+4); 
                  unhide_metas(newtag->net); 
                } 
              else if (strstr(arg, "tag:") == arg) 
                ret_err(_("cannot match tags in --dhcp-host")); 
 #ifdef HAVE_DHCP6  #ifdef HAVE_DHCP6
              else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')        new->addr6 = NULL;
                {#endif
                  arg[strlen(arg)-1] = 0;
                  arg++;        while (arg)
           {
             comma = split(arg);
             if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
               {
                 if ((arg[0] == 'i' || arg[0] == 'I') &&
                     (arg[1] == 'd' || arg[1] == 'D') &&
                     arg[2] == ':')
                   {
                     if (arg[3] == '*')
                       new->flags |= CONFIG_NOCLID;
                     else
                       {
                         int len;
                         arg += 3; /* dump id: */
                         if (strchr(arg, ':'))
                           len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
                         else
                           {
                             unhide_metas(arg);
                             len = (int) strlen(arg);
                           }
                         
                         if (len == -1)
                           {
                             dhcp_config_free(new);
                             ret_err(_("bad hex constant"));
                           }
                         else if ((new->clid = opt_malloc(len)))
                           {
                             new->flags |= CONFIG_CLID;
                             new->clid_len = len;
                             memcpy(new->clid, arg, len);
                           }
                       }
                   }
                 /* dhcp-host has strange backwards-compat needs. */
                 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
                   {
                     struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
                     newlist->next = new->netid;
                     new->netid = newlist;
                     newlist->list = dhcp_netid_create(arg+4, NULL);
                   }
                 else if (strstr(arg, "tag:") == arg)
                   new->filter = dhcp_netid_create(arg+4, new->filter);
                                       
                  if (!inet_pton(AF_INET6, arg, &new->addr6))#ifdef HAVE_DHCP6
                    ret_err(_("bad IPv6 address"));                else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
                   {
                     char *pref;
                     struct in6_addr in6;
                     struct addrlist *new_addr;
                     
                     arg[strlen(arg)-1] = 0;
                     arg++;
                     pref = split_chr(arg, '/');
                     
                     if (!inet_pton(AF_INET6, arg, &in6))
                       {
                         dhcp_config_free(new);
                         ret_err(_("bad IPv6 address"));
                       }
   
                  for (i= 0; i < 8; i++)                    new_addr = opt_malloc(sizeof(struct addrlist));
                    if (new->addr6.s6_addr[i] != 0)                    new_addr->next = new->addr6;
                      break;                    new_addr->flags = 0;
                    new_addr->addr.addr6 = in6;
                  /* set WILDCARD if network part all zeros */                    new->addr6 = new_addr;
                  if (i == 8)                    
                    new->flags |= CONFIG_WILDCARD;                    if (pref)
                       {
                         u64 addrpart = addr6part(&in6);
                         
                         if (!atoi_check(pref, &new_addr->prefixlen) ||
                             new_addr->prefixlen > 128 ||
                             ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
                           {
                             dhcp_config_free(new);
                             ret_err(_("bad IPv6 prefix"));
                           }
                         
                         new_addr->flags |= ADDRLIST_PREFIX;
                       }
                                       
                  new->flags |= CONFIG_ADDR6;                    for (i= 0; i < 8; i++)
                }                      if (in6.s6_addr[i] != 0)
                         break;
                     
                     /* set WILDCARD if network part all zeros */
                     if (i == 8)
                       new_addr->flags |= ADDRLIST_WILDCARD;
                     
                     new->flags |= CONFIG_ADDR6;
                   }
 #endif  #endif
              else                else
                {                  {
                  struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));                    struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
                  if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX,                     if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX, 
                                                     &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)                                                       &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
                    ret_err(_("bad hex constant"));                      {
                  else                        free(newhw);
                         dhcp_config_free(new);
                         ret_err(_("bad hex constant"));
                       }
                     else
                       {
                         newhw->next = new->hwaddr;
                         new->hwaddr = newhw;
                       }             
                   }
               }
             else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
               {
                 struct dhcp_config *configs;
                 
                 new->addr = in;
                 new->flags |= CONFIG_ADDR;
                 
                 /* If the same IP appears in more than one host config, then DISCOVER
                    for one of the hosts will get the address, but REQUEST will be NAKed,
                    since the address is reserved by the other one -> protocol loop. */
                 for (configs = daemon->dhcp_conf; configs; configs = configs->next) 
                   if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
                     {                      {
                                            inet_ntop(AF_INET, &in, daemon->addrbuff, ADDRSTRLEN);
                      newhw->next = new->hwaddr;                      sprintf(errstr, _("duplicate dhcp-host IP address %s"),
                      new->hwaddr = newhw;                              daemon->addrbuff);
                    }                                     dhcp_config_free(new);
                }                      return 0;
            }                    }         
          else if (strchr(a[j], '.') && (in.s_addr = inet_addr(a[j])) != (in_addr_t)-1)              }
            {            else
              struct dhcp_config *configs;              {
                              char *cp, *lastp = NULL, last = 0;
              new->addr = in;                int fac = 1, isdig = 0;
              new->flags |= CONFIG_ADDR;                
                 if (strlen(arg) > 1)
                   {
                     lastp = arg + strlen(arg) - 1;
                     last = *lastp;
                     switch (last)
                       {
                       case 'w':
                       case 'W':
                         fac *= 7;
                         /* fall through */
                       case 'd':
                       case 'D':
                         fac *= 24;
                         /* fall through */
                       case 'h':
                       case 'H':
                         fac *= 60;
                         /* fall through */
                       case 'm':
                       case 'M':
                         fac *= 60;
                         /* fall through */
                       case 's':
                       case 'S':
                         *lastp = 0;
                       }
                   }
                 
                 for (cp = arg; *cp; cp++)
                   if (isdigit((unsigned char)*cp))
                     isdig = 1;
                   else if (*cp != ' ')
                     break;
   
              /* If the same IP appears in more than one host config, then DISCOVER                if (*cp)
                 for one of the hosts will get the address, but REQUEST will be NAKed, 
                 since the address is reserved by the other one -> protocol loop. */ 
              for (configs = daemon->dhcp_conf; configs; configs = configs->next)  
                if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr) 
                   {                    {
                    sprintf(errstr, _("duplicate dhcp-host IP address %s"),  inet_ntoa(in));                    if (lastp)
                    return 0;                      *lastp = last;
                  }                               if (strcmp(arg, "infinite") == 0)
            }                      {
          else                        new->lease_time = 0xffffffff;
            {                        new->flags |= CONFIG_TIME;
              char *cp, *lastp = NULL, last = 0;                      }
              int fac = 1;                    else if (strcmp(arg, "ignore") == 0)
                                    new->flags |= CONFIG_DISABLE;
              if (strlen(a[j]) > 1)                    else
                {                      {
                  lastp = a[j] + strlen(a[j]) - 1;                        if (!(new->hostname = canonicalise_opt(arg)) ||
                  last = *lastp;                            !legal_hostname(new->hostname))
                  switch (last)                          {
                    {                            dhcp_config_free(new);
                    case 'w':                            ret_err(_("bad DHCP host name"));
                    case 'W':                          }
                      fac *= 7;                        
                      /* fall through */                        new->flags |= CONFIG_NAME;
                    case 'd':                        new->domain = strip_hostname(new->hostname);                    
                    case 'D':                      }
                      fac *= 24;                  }
                      /* fall through */                else if (isdig)
                    case 'h':                  {
                    case 'H':                    new->lease_time = atoi(arg) * fac; 
                      fac *= 60;                    /* Leases of a minute or less confuse
                      /* fall through */                       some clients, notably Apple's */
                    case 'm':                    if (new->lease_time < 120)
                    case 'M':                      new->lease_time = 120;
                      fac *= 60;                    new->flags |= CONFIG_TIME;
                      /* fall through */                  }
                    case 's':              }
                    case 'S':
                      *lastp = 0;            arg = comma;
                    }          }
                }
               
              for (cp = a[j]; *cp; cp++) 
                if (!isdigit((unsigned char)*cp) && *cp != ' ') 
                  break; 
               
              if (*cp) 
                { 
                  if (lastp) 
                    *lastp = last; 
                  if (strcmp(a[j], "infinite") == 0) 
                    { 
                      new->lease_time = 0xffffffff; 
                      new->flags |= CONFIG_TIME; 
                    } 
                  else if (strcmp(a[j], "ignore") == 0) 
                    new->flags |= CONFIG_DISABLE; 
                  else 
                    { 
                      if (!(new->hostname = canonicalise_opt(a[j])) || 
                          !legal_hostname(new->hostname)) 
                        ret_err(_("bad DHCP host name")); 
                      
                      new->flags |= CONFIG_NAME; 
                      new->domain = strip_hostname(new->hostname);                       
                    } 
                } 
              else 
                { 
                  new->lease_time = atoi(a[j]) * fac;  
                  /* Leases of a minute or less confuse 
                     some clients, notably Apple's */ 
                  if (new->lease_time < 120) 
                    new->lease_time = 120; 
                  new->flags |= CONFIG_TIME; 
                } 
            } 
         
         daemon->dhcp_conf = new;          daemon->dhcp_conf = new;
         break;          break;
       }        }
      
     case LOPT_TAG_IF:  /* --tag-if */      case LOPT_TAG_IF:  /* --tag-if */
       {        {
         struct tag_if *new = opt_malloc(sizeof(struct tag_if));          struct tag_if *new = opt_malloc(sizeof(struct tag_if));
Line 2697  static int one_opt(int option, char *arg, char *errstr Line 4119  static int one_opt(int option, char *arg, char *errstr
               }                }
             else              else
               {                {
                struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));                struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
                newtag->net = opt_malloc(len - 3); 
                strcpy(newtag->net, arg+4); 
                unhide_metas(newtag->net); 
   
                 if (strstr(arg, "set:") == arg)                  if (strstr(arg, "set:") == arg)
                   {                    {
Line 2717  static int one_opt(int option, char *arg, char *errstr Line 4136  static int one_opt(int option, char *arg, char *errstr
                 else                   else 
                   {                    {
                     new->set = NULL;                      new->set = NULL;
                    free(newtag);                    dhcp_netid_free(newtag);
                     break;                      break;
                   }                    }
               }                }
Line 2726  static int one_opt(int option, char *arg, char *errstr Line 4145  static int one_opt(int option, char *arg, char *errstr
           }            }
   
         if (!new->set)          if (!new->set)
          ret_err(_("bad tag-if"));          {
             dhcp_netid_free(new->tag);
             dhcp_netid_list_free(new->set);
             ret_err_free(_("bad tag-if"), new);
           }
                       
         break;          break;
       }        }
Line 2740  static int one_opt(int option, char *arg, char *errstr Line 4163  static int one_opt(int option, char *arg, char *errstr
                             option == LOPT_FORCE ? DHOPT_FORCE :                               option == LOPT_FORCE ? DHOPT_FORCE : 
                             (option == LOPT_MATCH ? DHOPT_MATCH :                              (option == LOPT_MATCH ? DHOPT_MATCH :
                              (option == LOPT_OPTS ? DHOPT_BANK : 0)));                               (option == LOPT_OPTS ? DHOPT_BANK : 0)));
     
    case 'M': /* --dhcp-boot */    case LOPT_NAME_MATCH: /* --dhcp-name-match */
       {        {
        struct dhcp_netid *id = NULL;        struct dhcp_match_name *new;
        while (is_tag_prefix(arg))        ssize_t len;
         
         if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
           ret_err(gen_err);
 
         new = opt_malloc(sizeof(struct dhcp_match_name));
         new->wildcard = 0;
         new->netid = opt_malloc(sizeof(struct dhcp_netid));
         new->netid->net = opt_string_alloc(set_prefix(arg));
 
         if (comma[len-1] == '*')
           {            {
            struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));            comma[len-1] = 0;
            newid->next = id;            new->wildcard = 1;
            id = newid;          }
            comma = split(arg);        new->name = opt_string_alloc(comma);
            newid->net = opt_string_alloc(arg+4);
            arg = comma;        new->next = daemon->dhcp_name_match;
          };        daemon->dhcp_name_match = new;
 
         break;
       }
       
     case 'M': /* --dhcp-boot */
       {
         struct dhcp_netid *id = dhcp_tags(&arg);
                   
         if (!arg)          if (!arg)
          ret_err(gen_err);          {
             ret_err(gen_err);
           }
         else           else 
           {            {
             char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;              char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Line 2772  static int one_opt(int option, char *arg, char *errstr Line 4214  static int one_opt(int option, char *arg, char *errstr
                 if (comma)                  if (comma)
                   {                    {
                     unhide_metas(comma);                      unhide_metas(comma);
                    if ((dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1) {                    if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
                      {
                      /*                        /*
                       * The user may have specified the tftp hostname here.                         * The user may have specified the tftp hostname here.
                       * save it so that it can be resolved/looked up during                         * save it so that it can be resolved/looked up during
                       * actual dhcp_reply().                         * actual dhcp_reply().
                       */                                */     
                        
                      tftp_sname = opt_string_alloc(comma);                        tftp_sname = opt_string_alloc(comma);
                      dhcp_next_server.s_addr = 0;                        dhcp_next_server.s_addr = 0;
                    }                      }
                   }                    }
               }                }
                           
Line 2795  static int one_opt(int option, char *arg, char *errstr Line 4237  static int one_opt(int option, char *arg, char *errstr
             new->next = daemon->boot_config;              new->next = daemon->boot_config;
             daemon->boot_config = new;              daemon->boot_config = new;
           }            }
              
         break;          break;
       }        }
   
       case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
         {
           struct dhcp_netid *id = dhcp_tags(&arg);
           
           if (!arg)
             {
               ret_err(gen_err);
             }
           else
             {
               struct delay_config *new;
               int delay;
               if (!atoi_check(arg, &delay))
                 ret_err(gen_err);
               
               new = opt_malloc(sizeof(struct delay_config));
               new->delay = delay;
               new->netid = id;
               new->next = daemon->delay_conf;
               daemon->delay_conf = new;
             }
           
           break;
         }
         
     case LOPT_PXE_PROMT:  /* --pxe-prompt */      case LOPT_PXE_PROMT:  /* --pxe-prompt */
        {         {
          struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));           struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
          int timeout;           int timeout;
         
          new->netid = NULL;           new->netid = NULL;
          new->opt = 10; /* PXE_MENU_PROMPT */           new->opt = 10; /* PXE_MENU_PROMPT */
         new->netid = dhcp_tags(&arg);
         while (is_tag_prefix(arg)) 
          { 
             struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid)); 
             comma = split(arg); 
             nn->next = new->netid; 
             new->netid = nn; 
             nn->net = opt_string_alloc(arg+4); 
             arg = comma; 
           } 
                     
          if (!arg)           if (!arg)
           ret_err(gen_err);           {
              dhcp_opt_free(new);
              ret_err(gen_err);
            }
          else           else
            {             {
              comma = split(arg);               comma = split(arg);
Line 2827  static int one_opt(int option, char *arg, char *errstr Line 4288  static int one_opt(int option, char *arg, char *errstr
              new->val = opt_malloc(new->len);               new->val = opt_malloc(new->len);
              memcpy(new->val + 1, arg, new->len - 1);               memcpy(new->val + 1, arg, new->len - 1);
                             
             new->u.vendor_class = (unsigned char *)"PXEClient";             new->u.vendor_class = NULL;
             new->flags = DHOPT_VENDOR;             new->flags = DHOPT_VENDOR | DHOPT_VENDOR_PXE;
                             
              if (comma && atoi_check(comma, &timeout))               if (comma && atoi_check(comma, &timeout))
                *(new->val) = timeout;                 *(new->val) = timeout;
Line 2847  static int one_opt(int option, char *arg, char *errstr Line 4308  static int one_opt(int option, char *arg, char *errstr
        {         {
          struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));           struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
          char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",           char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
                         "IA32_EFI", "BC_EFI", "Xscale_EFI", "x86-64_EFI", NULL };                           "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
                          "ARM32_EFI", "ARM64_EFI", NULL };  
          static int boottype = 32768;           static int boottype = 32768;
                     
          new->netid = NULL;           new->netid = NULL;
          new->sname = NULL;           new->sname = NULL;
          new->server.s_addr = 0;           new->server.s_addr = 0;
            new->netid = dhcp_tags(&arg);
   
          while (is_tag_prefix(arg))  
            {  
              struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));  
              comma = split(arg);  
              nn->next = new->netid;  
              new->netid = nn;  
              nn->net = opt_string_alloc(arg+4);  
              arg = comma;  
            }  
          
          if (arg && (comma = split(arg)))           if (arg && (comma = split(arg)))
            {             {
              for (i = 0; CSA[i]; i++)               for (i = 0; CSA[i]; i++)
Line 2926  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 2940  static int one_opt(int option, char *arg, char *errstr Line 4395  static int one_opt(int option, char *arg, char *errstr
             unhide_metas(comma);              unhide_metas(comma);
             new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);              new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
             if (new->hwaddr_len == -1)              if (new->hwaddr_len == -1)
              ret_err(gen_err);              {
                 free(new->netid.net);
                 ret_err_free(gen_err, new);
               }
             else              else
               {                {
                 new->next = daemon->dhcp_macs;                  new->next = daemon->dhcp_macs;
Line 2950  static int one_opt(int option, char *arg, char *errstr Line 4408  static int one_opt(int option, char *arg, char *errstr
       }        }
       break;        break;
   
 #ifdef OPTION6_PREFIX_CLASS   
     case LOPT_PREF_CLSS: /* --dhcp-prefix-class */  
       {  
         struct prefix_class *new = opt_malloc(sizeof(struct prefix_class));  
           
         if (!(comma = split(arg)) ||  
             !atoi_check16(comma, &new->class))  
           ret_err(gen_err);  
           
         new->tag.net = opt_string_alloc(set_prefix(arg));  
         new->next = daemon->prefix_classes;  
         daemon->prefix_classes = new;  
           
         break;  
       }  
 #endif  
                                 
   
     case 'U':           /* --dhcp-vendorclass */      case 'U':           /* --dhcp-vendorclass */
     case 'j':           /* --dhcp-userclass */      case 'j':           /* --dhcp-userclass */
     case LOPT_CIRCUIT:  /* --dhcp-circuitid */      case LOPT_CIRCUIT:  /* --dhcp-circuitid */
Line 2975  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)))
           ret_err(gen_err);           ret_err_free(gen_err, new);
                   
          new->netid.net = opt_string_alloc(set_prefix(arg));           new->netid.net = opt_string_alloc(set_prefix(arg));
          /* check for hex string - must digits may include : must not have nothing else,            /* check for hex string - must digits may include : must not have nothing else, 
Line 2989  static int one_opt(int option, char *arg, char *errstr Line 4429  static int one_opt(int option, char *arg, char *errstr
          if ((comma = split(arg)))           if ((comma = split(arg)))
            {             {
              if (option  != 'U' || strstr(arg, "enterprise:") != arg)               if (option  != 'U' || strstr(arg, "enterprise:") != arg)
               ret_err(gen_err);               {
                  free(new->netid.net);
                  ret_err_free(gen_err, new);
                }
              else               else
                new->enterprise = atoi(arg+11);                 new->enterprise = atoi(arg+11);
            }             }
          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 3091  static int one_opt(int option, char *arg, char *errstr Line 4537  static int one_opt(int option, char *arg, char *errstr
           }            }
                   
         while (arg) {          while (arg) {
           struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));  
           comma = split(arg);            comma = split(arg);
          member->next = list;          list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
          list = member; 
          if (is_tag_prefix(arg)) 
            member->net = opt_string_alloc(arg+4); 
          else 
            member->net = opt_string_alloc(arg); 
           arg = comma;            arg = comma;
         }          }
                   
Line 3111  static int one_opt(int option, char *arg, char *errstr Line 4551  static int one_opt(int option, char *arg, char *errstr
       while (arg) {        while (arg) {
         struct addr_list *new = opt_malloc(sizeof(struct addr_list));          struct addr_list *new = opt_malloc(sizeof(struct addr_list));
         comma = split(arg);          comma = split(arg);
        if ((new->addr.s_addr = inet_addr(arg)) == (in_addr_t)-1)        if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
          ret_err(_("bad dhcp-proxy address"));          ret_err_free(_("bad dhcp-proxy address"), new);
         new->next = daemon->override_relays;          new->next = daemon->override_relays;
         daemon->override_relays = new;          daemon->override_relays = new;
         arg = comma;          arg = comma;
           }
             break;
   
       case LOPT_PXE_VENDOR: /* --dhcp-pxe-vendor */
         {
           while (arg) {
             struct dhcp_pxe_vendor *new = opt_malloc(sizeof(struct dhcp_pxe_vendor));
             comma = split(arg);
             new->data = opt_string_alloc(arg);
             new->next = daemon->dhcp_pxe_vendors;
             daemon->dhcp_pxe_vendors = new;
             arg = comma;
           }
       }        }
       break;        break;
         
       case LOPT_RELAY: /* --dhcp-relay */
         {
           struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
           char *two = split(arg);
           char *three = split(two);
           
           new->iface_index = 0;
   
           if (two)
             {
               if (inet_pton(AF_INET, arg, &new->local))
                 {
                   char *hash = split_chr(two, '#');
   
                   if (!hash || !atoi_check16(hash, &new->port))
                     new->port = DHCP_SERVER_PORT;
                   
                   if (!inet_pton(AF_INET, two, &new->server))
                     {
                       new->server.addr4.s_addr = 0;
                                       
                       /* Fail for three arg version where there are not two addresses. 
                          Also fail when broadcasting to wildcard address. */
                       if (three || strchr(two, '*'))
                         two = NULL;
                       else
                         three = two;
                     }
                   
                   new->next = daemon->relay4;
                   daemon->relay4 = new;
                 }
   #ifdef HAVE_DHCP6
               else if (inet_pton(AF_INET6, arg, &new->local))
                 {
                   char *hash = split_chr(two, '#');
   
                   if (!hash || !atoi_check16(hash, &new->port))
                     new->port = DHCPV6_SERVER_PORT;
   
                   if (!inet_pton(AF_INET6, two, &new->server))
                     {
                       inet_pton(AF_INET6, ALL_SERVERS, &new->server.addr6);
                       /* Fail for three arg version where there are not two addresses.
                          Also fail when multicasting to wildcard address. */
                       if (three || strchr(two, '*'))
                         two = NULL;
                       else
                         three = two;
                     }
                   new->next = daemon->relay6;
                   daemon->relay6 = new;
                 }
 #endif  #endif
   
               new->interface = opt_string_alloc(three);
             }
           
           if (!two)
             {
               free(new->interface);
               ret_err_free(_("Bad dhcp-relay"), new);
             }
           
           break;
         }
   
   #endif
               
 #ifdef HAVE_DHCP6  #ifdef HAVE_DHCP6
       case LOPT_RA_PARAM: /* --ra-param */
         if ((comma = split(arg)))
           {
             struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
             new->lifetime = -1;
             new->prio = 0;
             new->mtu = 0;
             new->mtu_name = NULL;
             new->name = opt_string_alloc(arg);
             if (strcasestr(comma, "mtu:") == comma)
               {
                 arg = comma + 4;
                 if (!(comma = split(comma)))
                   goto err;
                 if (!strcasecmp(arg, "off"))
                   new->mtu = -1;
                 else if (!atoi_check(arg, &new->mtu))
                   new->mtu_name = opt_string_alloc(arg);
                 else if (new->mtu < 1280)
                   goto err;
               }
             if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
               {
                 if (*comma == 'l' || *comma == 'L')
                   new->prio = 0x18;
                 else
                   new->prio = 0x08;
                 comma = split(comma);
               }
              arg = split(comma);
              if (!atoi_check(comma, &new->interval) || 
                 (arg && !atoi_check(arg, &new->lifetime)))
                {
   err:
                  free(new->name);
                  ret_err_free(_("bad RA-params"), new);
                }
             
             new->next = daemon->ra_interfaces;
             daemon->ra_interfaces = new;
           }
         break;
         
     case LOPT_DUID: /* --dhcp-duid */      case LOPT_DUID: /* --dhcp-duid */
       if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))        if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
         ret_err(_("bad DUID"));          ret_err(_("bad DUID"));
Line 3154  static int one_opt(int option, char *arg, char *errstr Line 4718  static int one_opt(int option, char *arg, char *errstr
         dash = split_chr(a[0], '-');          dash = split_chr(a[0], '-');
   
         if ((k < 2) ||           if ((k < 2) || 
            ((new->in.s_addr = inet_addr(a[0])) == (in_addr_t)-1) ||            (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
            ((new->out.s_addr = inet_addr(a[1])) == (in_addr_t)-1))            (!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
          option = '?';            (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
           ret_err(_("missing address in alias"));
                   
         if (k == 3)  
           new->mask.s_addr = inet_addr(a[2]);  
           
         if (dash &&           if (dash && 
            ((new->end.s_addr = inet_addr(dash)) == (in_addr_t)-1 ||            (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
              !is_same_net(new->in, new->end, new->mask) ||               !is_same_net(new->in, new->end, new->mask) ||
              ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))               ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
          ret_err(_("invalid alias range"));          ret_err_free(_("invalid alias range"), new);
                   
         break;          break;
       }        }
               
     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->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->intr = opt_string_alloc(comma);        while ((comma = split(arg)))
           {
             if (inet_pton(AF_INET, arg, &new->proto4))
               new->flags |= INP4;
             else if (inet_pton(AF_INET6, arg, &new->proto6))
               new->flags |= INP6;
             else
               break;
             
             arg = comma;
           }
 
         if ((comma = split_chr(arg, '/')))
           {
             if (strcmp(comma, "4") == 0)
               new->flags &= ~IN6;
             else if (strcmp(comma, "6") == 0)
               new->flags &= ~IN4;
             else
               ret_err_free(gen_err, new);
           }
 
         new->intr = opt_string_alloc(arg);
 
         if (option == LOPT_DYNHOST)
           {
             if (!(new->flags & (INP4 | INP6)))
               ret_err(_("missing address in dynamic host"));
 
             if (!(new->flags & IN4) || !(new->flags & IN6))
               arg = NULL; /* provoke error below */
 
             new->flags &= ~(IN4 | IN6);
           }
         else
           {
             if (new->flags & (INP4 | INP6))
               arg = NULL; /* provoke error below */
           }
         
         if (!domain || !arg || !(new->name = canonicalise_opt(domain)))
           ret_err(option == LOPT_DYNHOST ?
                   _("bad dynamic host") : _("bad interface name"));
         
         break;          break;
       }        }
               
     case LOPT_CNAME: /* --cname */      case LOPT_CNAME: /* --cname */
       {        {
         struct cname *new;          struct cname *new;
        char *alias;        char *alias, *target=NULL, *last, *pen;
        char *target;        int ttl = -1;
   
        if (!(comma = split(arg)))        for (last = pen = NULL, comma = arg; comma; comma = split(comma))
          ret_err(gen_err);          {
                    pen = last;
        alias = canonicalise_opt(arg);            last = comma;
        target = canonicalise_opt(comma);          }
            
        if (!alias || !target)        if (!pen)
           ret_err(_("bad CNAME"));            ret_err(_("bad CNAME"));
        else        
         if (pen != arg && atoi_check(last, &ttl))
           last = pen;
                 
         while (arg != last)
           {            {
               int arglen = strlen(arg);
               alias = canonicalise_opt(arg);
   
               if (!target)
                 target = canonicalise_opt(last);
               if (!alias || !target)
                 {
                   free(target);
                   free(alias);
                   ret_err(_("bad CNAME"));
                 }
               
             for (new = daemon->cnames; new; new = new->next)              for (new = daemon->cnames; new; new = new->next)
              if (hostname_isequal(new->alias, arg))              if (hostname_isequal(new->alias, alias))
                ret_err(_("duplicate CNAME"));                {
                   free(target);
                   free(alias);
                   ret_err(_("duplicate CNAME"));
                 }
             new = opt_malloc(sizeof(struct cname));              new = opt_malloc(sizeof(struct cname));
             new->next = daemon->cnames;              new->next = daemon->cnames;
             daemon->cnames = new;              daemon->cnames = new;
             new->alias = alias;              new->alias = alias;
             new->target = target;              new->target = target;
               new->ttl = ttl;
   
               for (arg += arglen+1; *arg && isspace((unsigned char)*arg); arg++);
           }            }
               
         break;          break;
Line 3229  static int one_opt(int option, char *arg, char *errstr Line 4857  static int one_opt(int option, char *arg, char *errstr
                   
         if (!(dom = canonicalise_opt(arg)) ||          if (!(dom = canonicalise_opt(arg)) ||
             (comma && !(target = canonicalise_opt(comma))))              (comma && !(target = canonicalise_opt(comma))))
          ret_err(_("bad PTR record"));          {
             free(dom);
             free(target);
             ret_err(_("bad PTR record"));
           }
         else          else
           {            {
             new = opt_malloc(sizeof(struct ptr_record));              new = opt_malloc(sizeof(struct ptr_record));
Line 3247  static int one_opt(int option, char *arg, char *errstr Line 4879  static int one_opt(int option, char *arg, char *errstr
         int k = 0;          int k = 0;
         struct naptr *new;          struct naptr *new;
         int order, pref;          int order, pref;
        char *name, *replace = NULL;        char *name=NULL, *replace = NULL;
   
         if ((a[0] = arg))          if ((a[0] = arg))
           for (k = 1; k < 7; k++)            for (k = 1; k < 7; k++)
Line 3260  static int one_opt(int option, char *arg, char *errstr Line 4892  static int one_opt(int option, char *arg, char *errstr
             !atoi_check16(a[1], &order) ||               !atoi_check16(a[1], &order) || 
             !atoi_check16(a[2], &pref) ||              !atoi_check16(a[2], &pref) ||
             (k == 7 && !(replace = canonicalise_opt(a[6]))))              (k == 7 && !(replace = canonicalise_opt(a[6]))))
          ret_err(_("bad NAPTR record"));          {
             free(name);
             free(replace);
             ret_err(_("bad NAPTR record"));
           }
         else          else
           {            {
             new = opt_malloc(sizeof(struct naptr));              new = opt_malloc(sizeof(struct naptr));
Line 3280  static int one_opt(int option, char *arg, char *errstr Line 4916  static int one_opt(int option, char *arg, char *errstr
     case LOPT_RR: /* dns-rr */      case LOPT_RR: /* dns-rr */
       {        {
         struct txt_record *new;          struct txt_record *new;
        size_t len;        size_t len = 0;
         char *data;          char *data;
        int val;        int class;
   
         comma = split(arg);          comma = split(arg);
         data = split(comma);          data = split(comma);
                                   
         new = opt_malloc(sizeof(struct txt_record));          new = opt_malloc(sizeof(struct txt_record));
        new->next = daemon->rr;        new->name = NULL;
        daemon->rr = new; 
                   
        if (!atoi_check(comma, &val) ||         if (!atoi_check(comma, &class) || 
             !(new->name = canonicalise_opt(arg)) ||              !(new->name = canonicalise_opt(arg)) ||
             (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))              (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
          ret_err(_("bad RR record"));          {
                            free(new->name);
        new->class = val;            ret_err_free(_("bad RR record"), new);
           }
 
         new->len = 0;          new->len = 0;
           new->class = class;
           new->next = daemon->rr;
           daemon->rr = new;
                   
         if (data)          if (data)
           {            {
            new->txt=opt_malloc(len);            new->txt = opt_malloc(len);
             new->len = len;              new->len = len;
             memcpy(new->txt, data, len);              memcpy(new->txt, data, len);
           }            }
Line 3309  static int one_opt(int option, char *arg, char *errstr Line 4949  static int one_opt(int option, char *arg, char *errstr
         break;          break;
       }        }
   
       case LOPT_CAA: /* --caa-record */
         {
           struct txt_record *new;
           char *tag, *value;
           int flags;
           
           comma = split(arg);
           tag = split(comma);
           value = split(tag);
           
           new = opt_malloc(sizeof(struct txt_record));
           new->next = daemon->rr;
           daemon->rr = new;
   
           if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
             ret_err(_("bad CAA record"));
           
           unhide_metas(tag);
           unhide_metas(value);
   
           new->len = strlen(tag) + strlen(value) + 2;
           new->txt = opt_malloc(new->len);
           new->txt[0] = flags;
           new->txt[1] = strlen(tag);
           memcpy(&new->txt[2], tag, strlen(tag));
           memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
           new->class = T_CAA;
           
           break;
         }
           
     case 'Y':  /* --txt-record */      case 'Y':  /* --txt-record */
       {        {
         struct txt_record *new;          struct txt_record *new;
Line 3318  static int one_opt(int option, char *arg, char *errstr Line 4989  static int one_opt(int option, char *arg, char *errstr
         comma = split(arg);          comma = split(arg);
                                   
         new = opt_malloc(sizeof(struct txt_record));          new = opt_malloc(sizeof(struct txt_record));
         new->next = daemon->txt;  
         daemon->txt = new;  
         new->class = C_IN;          new->class = C_IN;
                new->stat = 0;
 
         if (!(new->name = canonicalise_opt(arg)))          if (!(new->name = canonicalise_opt(arg)))
          ret_err(_("bad TXT record"));          ret_err_free(_("bad TXT record"), new);
                   
           new->next = daemon->txt;
           daemon->txt = new;
         len = comma ? strlen(comma) : 0;          len = comma ? strlen(comma) : 0;
         len += (len/255) + 1; /* room for extra counts */          len += (len/255) + 1; /* room for extra counts */
         new->txt = p = opt_malloc(len);          new->txt = p = opt_malloc(len);
Line 3371  static int one_opt(int option, char *arg, char *errstr Line 5043  static int one_opt(int option, char *arg, char *errstr
             arg = comma;              arg = comma;
             comma = split(arg);              comma = split(arg);
             if (!(target = canonicalise_opt(arg)))              if (!(target = canonicalise_opt(arg)))
              ret_err(_("bad SRV target"));              ret_err_free(_("bad SRV target"), name);
                                   
             if (comma)              if (comma)
               {                {
                 arg = comma;                  arg = comma;
                 comma = split(arg);                  comma = split(arg);
                 if (!atoi_check16(arg, &port))                  if (!atoi_check16(arg, &port))
                  ret_err(_("invalid port number"));                  {
                     free(name);
                     ret_err_free(_("invalid port number"), target);
                   }
                                   
                 if (comma)                  if (comma)
                   {                    {
                     arg = comma;                      arg = comma;
                     comma = split(arg);                      comma = split(arg);
                     if (!atoi_check16(arg, &priority))                      if (!atoi_check16(arg, &priority))
                      ret_err(_("invalid priority"));                      {
                                                free(name);
                    if (comma)                        ret_err_free(_("invalid priority"), target);
                      { 
                        arg = comma; 
                        comma = split(arg); 
                        if (!atoi_check16(arg, &weight)) 
                          ret_err(_("invalid weight")); 
                       }                        }
                       if (comma && !atoi_check16(comma, &weight))
                         {
                           free(name);
                           ret_err_free(_("invalid weight"), target);
                         }
                   }                    }
               }                }
           }            }
Line 3412  static int one_opt(int option, char *arg, char *errstr Line 5087  static int one_opt(int option, char *arg, char *errstr
               
     case LOPT_HOST_REC: /* --host-record */      case LOPT_HOST_REC: /* --host-record */
       {        {
        struct host_record *new = opt_malloc(sizeof(struct host_record));        struct host_record *new;
        memset(new, 0, sizeof(struct host_record));
         
         if (!arg || !(comma = split(arg)))          if (!arg || !(comma = split(arg)))
           ret_err(_("Bad host-record"));            ret_err(_("Bad host-record"));
                   
           new = opt_malloc(sizeof(struct host_record));
           memset(new, 0, sizeof(struct host_record));
           new->ttl = -1;
           new->flags = 0;
   
         while (arg)          while (arg)
           {            {
            struct all_addr addr;            union all_addr addr;
            if (inet_pton(AF_INET, arg, &addr))            char *dig;
              new->addr = addr.addr.addr4;
#ifdef HAVE_IPV6            for (dig = arg; *dig != 0; dig++)
            else if (inet_pton(AF_INET6, arg, &addr))              if (*dig < '0' || *dig > '9')
              new->addr6 = addr.addr.addr6;                break;
#endif            if (*dig == 0)
               new->ttl = atoi(arg);
             else if (inet_pton(AF_INET, arg, &addr.addr4))
               {
                 new->addr = addr.addr4;
                 new->flags |= HR_4;
               }
             else if (inet_pton(AF_INET6, arg, &addr.addr6))
               {
                 new->addr6 = addr.addr6;
                 new->flags |= HR_6;
               }
             else              else
               {                {
                int nomem;                char *canon = canonicalise_opt(arg);
                char *canon = canonicalise(arg, &nomem);                struct name_list *nl;
                struct name_list *nl = opt_malloc(sizeof(struct name_list)); 
                 if (!canon)                  if (!canon)
                  ret_err(_("Bad name in host-record"));                  {
                     struct name_list *tmp, *next;
                     for (tmp = new->names; tmp; tmp = next)
                       {
                         next = tmp->next;
                         free(tmp);
                       }
                     ret_err_free(_("Bad name in host-record"), new);
                   }
   
                   nl = opt_malloc(sizeof(struct name_list));
                 nl->name = canon;                  nl->name = canon;
                 /* keep order, so that PTR record goes to first name */                  /* keep order, so that PTR record goes to first name */
                 nl->next = NULL;                  nl->next = NULL;
Line 3461  static int one_opt(int option, char *arg, char *errstr Line 5159  static int one_opt(int option, char *arg, char *errstr
         daemon->host_records_tail = new;          daemon->host_records_tail = new;
         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
       case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
         daemon->timestamp_file = opt_string_alloc(arg); 
         break;
   
       case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
         if (arg)
           {
             if (strcmp(arg, "no") == 0)
               set_option_bool(OPT_DNSSEC_IGN_NS);
             else
               ret_err(_("bad value for dnssec-check-unsigned"));
           }
         break;
               
       case LOPT_TRUST_ANCHOR: /* --trust-anchor */
         {
           struct ds_config *new = opt_malloc(sizeof(struct ds_config));
           char *cp, *cp1, *keyhex, *digest, *algo = NULL;
           int len;
           
           new->class = C_IN;
           new->name = NULL;
   
           if ((comma = split(arg)) && (algo = split(comma)))
             {
               int class = 0;
               if (strcmp(comma, "IN") == 0)
                 class = C_IN;
               else if (strcmp(comma, "CH") == 0)
                 class = C_CHAOS;
               else if (strcmp(comma, "HS") == 0)
                 class = C_HESIOD;
               
               if (class != 0)
                 {
                   new->class = class;
                   comma = algo;
                   algo = split(comma);
                 }
             }
                     
           if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
               !atoi_check16(comma, &new->keytag) || 
               !atoi_check8(algo, &new->algo) ||
               !atoi_check8(digest, &new->digest_type) ||
               !(new->name = canonicalise_opt(arg)))
             ret_err_free(_("bad trust anchor"), new);
               
           /* Upper bound on length */
           len = (2*strlen(keyhex))+1;
           new->digest = opt_malloc(len);
           unhide_metas(keyhex);
           /* 4034: "Whitespace is allowed within digits" */
           for (cp = keyhex; *cp; )
             if (isspace((unsigned char)*cp))
               for (cp1 = cp; *cp1; cp1++)
                 *cp1 = *(cp1+1);
             else
               cp++;
           if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
             {
               free(new->name);
               ret_err_free(_("bad HEX in trust anchor"), new);
             }
           
           new->next = daemon->ds;
           daemon->ds = new;
           
           break;
         }
   #endif
                   
     default:      default:
      ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"));      ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
               
     }      }
       
   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;
       
   while (fgets(buff, MAXDNAME, f))    while (fgets(buff, MAXDNAME, f))
     {      {
      int white, i, option = hard_opt;      int white, i;
      char *errmess, *p, *arg = NULL, *start;      volatile int option;
       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)      if (option != 0 || hard_opt == LOPT_REV_SERV)
         {          {
           if (setjmp(mem_jmp))            if (setjmp(mem_jmp))
             continue;              continue;
           mem_recover = 1;            mem_recover = 1;
         }          }
      
       arg = NULL;
       lineno++;        lineno++;
       errmess = NULL;        errmess = NULL;
               
Line 3528  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 3583  static void read_file(char *file, FILE *f, int hard_op Line 5376  static void read_file(char *file, FILE *f, int hard_op
             errmess = _("extraneous parameter");              errmess = _("extraneous parameter");
           else if (opts[i].has_arg == 1 && !arg)            else if (opts[i].has_arg == 1 && !arg)
             errmess = _("missing parameter");              errmess = _("missing parameter");
             else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
               errmess = _("illegal option");
         }          }
   
     oops:      oops:
       if (errmess)        if (errmess)
         strcpy(daemon->namebuff, errmess);          strcpy(daemon->namebuff, errmess);
                       
      if (errmess || !one_opt(option, arg, buff, _("error"), 0))      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 3600  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)
   int option_read_dynfile(char *file, int flags)
   {
     my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
     
     if (flags & AH_DHCP_HST)
       return one_file(file, LOPT_BANK);
     else if (flags & AH_DHCP_OPT)
       return one_file(file, LOPT_OPTS);
     
     return 0;
   }
   #endif
   
 static int one_file(char *file, int hard_opt)  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 3614  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 3648  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 3667  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)
 {  {
  int i;  unsigned int i;
  struct hostsfile *ah;  int entcnt, n;
   struct hostsfile *ah, *last, *next, **up;
   struct dirent **namelist;
   
  for (i = 0, ah = list; ah; ah = ah->next)  /* find largest used index */
   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 3694  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 3754  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);
           }            }
       }        }
       
   return list;    return list;
 }  }
   
void read_servers_file(void)
#ifdef HAVE_DHCP 
void reread_dhcp(void) 
 {  {
  struct hostsfile *hf;  FILE *f;
   
  if (daemon->dhcp_hosts_file)  if (!(f = fopen(daemon->servers_file, "r")))
     {      {
      struct dhcp_config *configs, *cp, **up;       my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
        return;
     }
       
      /* remove existing... */  mark_servers(SERV_FROM_FILE);
      for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)  read_file(daemon->servers_file, f, LOPT_REV_SERV, 0);
   fclose(f);
   cleanup_servers();
   check_servers(0);
 }
  
 
 #ifdef HAVE_DHCP
 static void clear_dynamic_conf(void)
 {
   struct dhcp_config *configs, *cp, **up;
   
   /* remove existing... */
   for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
     {
       cp = configs->next;
       
       if (configs->flags & CONFIG_BANK)
         {          {
          cp = configs->next;          *up = cp;
                    dhcp_config_free(configs);
          if (configs->flags & CONFIG_BANK)        }
            {      else
              struct hwaddr_config *mac, *tmp;        up = &configs->next;
              struct dhcp_netid_list *list, *tmplist;    }
              }
              for (mac = configs->hwaddr; mac; mac = tmp) 
                { 
                  tmp = mac->next; 
                  free(mac); 
                } 
   
              if (configs->flags & CONFIG_CLID)static void clear_dynamic_opt(void)
                free(configs->clid);{
   struct dhcp_opt *opts, *cp, **up;
   
              for (list = configs->netid; list; list = tmplist)  for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
                {    {
                  free(list->list);      cp = opts->next;
                  tmplist = list->next; 
                  free(list); 
                } 
               
              if (configs->flags & CONFIG_NAME) 
                free(configs->hostname); 
               
              *up = configs->next; 
              free(configs); 
            } 
          else 
            up = &configs->next; 
        } 
               
         if (opts->flags & DHOPT_BANK)
           {
             *up = cp;
             dhcp_opt_free(opts);
           }
         else
           up = &opts->next;
       }
   }
   
   void reread_dhcp(void)
   {
      struct hostsfile *hf;
   
      /* Do these even if there is no daemon->dhcp_hosts_file or
         daemon->dhcp_opts_file since entries may have been created by the
         inotify dynamic file reading system. */
      
      clear_dynamic_conf();
      clear_dynamic_opt();
   
      if (daemon->dhcp_hosts_file)
       {
       daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);        daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
       for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)        for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
         if (!(hf->flags & AH_INACTIVE))        if (!(hf->flags & AH_INACTIVE))
           {          {
             if (one_file(hf->fname, LOPT_BANK))              if (one_file(hf->fname, LOPT_BANK))  
               my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);              my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
           }          }
     }      }
   
   if (daemon->dhcp_opts_file)    if (daemon->dhcp_opts_file)
     {      {
       struct dhcp_opt *opts, *cp, **up;  
       struct dhcp_netid *id, *next;  
   
       for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)  
         {  
           cp = opts->next;  
             
           if (opts->flags & DHOPT_BANK)  
             {  
               if ((opts->flags & DHOPT_VENDOR))  
                 free(opts->u.vendor_class);  
               free(opts->val);  
               for (id = opts->netid; id; id = next)  
                 {  
                   next = id->next;  
                   free(id->net);  
                   free(id);  
                 }  
               *up = opts->next;  
               free(opts);  
             }  
           else  
             up = &opts->next;  
         }  
         
       daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);        daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
       for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)        for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
         if (!(hf->flags & AH_INACTIVE))          if (!(hf->flags & AH_INACTIVE))
Line 3861  void reread_dhcp(void) Line 5721  void reread_dhcp(void)
               my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);                my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
           }            }
     }      }
   
   #  ifdef HAVE_INOTIFY
     /* Setup notify and read pre-existing files. */
     set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
   #  endif
 }  }
 #endif  #endif
    
 void read_opts(int argc, char **argv, char *compile_opts)  void read_opts(int argc, char **argv, char *compile_opts)
 {  {
     size_t argbuf_size = MAXDNAME;
     char *argbuf = opt_malloc(argbuf_size);
   char *buff = opt_malloc(MAXDNAME);    char *buff = opt_malloc(MAXDNAME);
  int option, conffile_opt = '7', testmode = 0;  int option, testmode = 0;
  char *arg, *conffile = CONFFILE;  char *arg, *conffile = NULL;
               
   opterr = 0;    opterr = 0;
   
   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 3895  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;
  add_txt("version.bind", "dnsmasq-" VERSION );  daemon->randport_limit = 1;
  add_txt("authors.bind", "Simon Kelley");  daemon->host_index = SRC_AH;
  add_txt("copyright.bind", COPYRIGHT);  
  /* See comment above make_servers(). Optimises server-read code. */
   mark_servers(0);
   
   while (1)     while (1) 
     {      {
 #ifdef HAVE_GETOPT_LONG  #ifdef HAVE_GETOPT_LONG
Line 3922  void read_opts(int argc, char **argv, char *compile_op Line 5792  void read_opts(int argc, char **argv, char *compile_op
       /* Copy optarg so that argv doesn't get changed */        /* Copy optarg so that argv doesn't get changed */
       if (optarg)        if (optarg)
         {          {
          strncpy(buff, optarg, MAXDNAME);          if (strlen(optarg) >= argbuf_size)
          buff[MAXDNAME-1] = 0;            {
          arg = buff;              free(argbuf);
               argbuf_size = strlen(optarg) + 1;
               argbuf = opt_malloc(argbuf_size);
             }
           safe_strncpy(argbuf, optarg, argbuf_size);
           arg = argbuf;
         }          }
       else        else
         arg = NULL;          arg = NULL;
Line 3958  void read_opts(int argc, char **argv, char *compile_op Line 5833  void read_opts(int argc, char **argv, char *compile_op
         }          }
       else if (option == 'C')        else if (option == 'C')
         {          {
          conffile_opt = 0; /* file must exist */          if (!conffile)
          conffile = opt_string_alloc(arg);            conffile = opt_string_alloc(arg);
           else
             {
               char *extra = opt_string_alloc(arg);
               one_file(extra, 0);
               free(extra);
             }
         }          }
       else        else
         {          {
 #ifdef HAVE_GETOPT_LONG  #ifdef HAVE_GETOPT_LONG
          if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1))          if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
 #else   #else 
          if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1))             if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0)) 
 #endif    #endif  
             die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);              die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
         }          }
     }      }
   
     free(argbuf);
   
   if (conffile)    if (conffile)
    one_file(conffile, conffile_opt);    {
       one_file(conffile, 0);
       free(conffile);
     }
   else
     one_file(CONFFILE, LOPT_CONF_OPT);
   
     /* Add TXT records if wanted */
   #ifndef NO_ID
     if (!option_bool(OPT_NO_IDENT))
       {
         add_txt("version.bind", "dnsmasq-" VERSION, 0 );
         add_txt("authors.bind", "Simon Kelley", 0);
         add_txt("copyright.bind", COPYRIGHT, 0);
         add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
         add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
         add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
         add_txt("misses.bind", NULL, TXT_STAT_MISSES);
         add_txt("hits.bind", NULL, TXT_STAT_HITS);
   #ifdef HAVE_AUTH
         add_txt("auth.bind", NULL, TXT_STAT_AUTH);
   #endif
         add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
       }
   #endif
   
   /* 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)
     {      {
Line 3984  void read_opts(int argc, char **argv, char *compile_op Line 5891  void read_opts(int argc, char **argv, char *compile_op
           {            {
             if (tmp->source_addr.sa.sa_family == AF_INET)              if (tmp->source_addr.sa.sa_family == AF_INET)
               tmp->source_addr.in.sin_port = htons(daemon->query_port);                tmp->source_addr.in.sin_port = htons(daemon->query_port);
 #ifdef HAVE_IPV6  
             else if (tmp->source_addr.sa.sa_family == AF_INET6)              else if (tmp->source_addr.sa.sa_family == AF_INET6)
               tmp->source_addr.in6.sin6_port = htons(daemon->query_port);                tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
#endif           }
          }     } 
    } 
       
     if (daemon->host_records)
       {
         struct host_record *hr;
         
         for (hr = daemon->host_records; hr; hr = hr->next)
           if (hr->ttl == -1)
             hr->ttl = daemon->local_ttl;
       }
   
     if (daemon->cnames)
       {
         struct cname *cn, *cn2, *cn3;
   
   #define NOLOOP 1
   #define TESTLOOP 2      
   
         /* Fill in TTL for CNAMES now we have local_ttl.
            Also prepare to do loop detection. */
         for (cn = daemon->cnames; cn; cn = cn->next)
           {
             if (cn->ttl == -1)
               cn->ttl = daemon->local_ttl;
             cn->flag = 0;
             cn->targetp = NULL;
             for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
               if (hostname_isequal(cn->target, cn2->alias))
                 {
                   cn->targetp = cn2;
                   break;
                 }
           }
         
         /* Find any CNAME loops.*/
         for (cn = daemon->cnames; cn; cn = cn->next)
           {
             for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
               {
                 if (cn2->flag == NOLOOP)
                   break;
                 
                 if (cn2->flag == TESTLOOP)
                   die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
                 
                 cn2->flag = TESTLOOP;
               }
             
             for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
               cn3->flag = NOLOOP;
           }
       }
   
   if (daemon->if_addrs)    if (daemon->if_addrs)
     {        {  
       struct iname *tmp;        struct iname *tmp;
       for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)        for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
         if (tmp->addr.sa.sa_family == AF_INET)          if (tmp->addr.sa.sa_family == AF_INET)
           tmp->addr.in.sin_port = htons(daemon->port);            tmp->addr.in.sin_port = htons(daemon->port);
 #ifdef HAVE_IPV6  
         else if (tmp->addr.sa.sa_family == AF_INET6)          else if (tmp->addr.sa.sa_family == AF_INET6)
           tmp->addr.in6.sin6_port = htons(daemon->port);            tmp->addr.in6.sin6_port = htons(daemon->port);
 #endif /* IPv6 */  
     }      }
                   
   /* create default, if not specified */    /* create default, if not specified */
Line 4010  void read_opts(int argc, char **argv, char *compile_op Line 5964  void read_opts(int argc, char **argv, char *compile_op
       strcat(buff, daemon->authserver);        strcat(buff, daemon->authserver);
       daemon->hostmaster = opt_string_alloc(buff);        daemon->hostmaster = opt_string_alloc(buff);
     }      }
   
     if (!daemon->dhcp_pxe_vendors)
       {
         daemon->dhcp_pxe_vendors = opt_malloc(sizeof(struct dhcp_pxe_vendor));
         daemon->dhcp_pxe_vendors->data = opt_string_alloc(DHCP_PXE_DEF_VENDOR);
         daemon->dhcp_pxe_vendors->next = NULL;
       }
       
   /* only one of these need be specified: the other defaults to the host-name */    /* only one of these need be specified: the other defaults to the host-name */
   if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)    if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Line 4097  void read_opts(int argc, char **argv, char *compile_op Line 6058  void read_opts(int argc, char **argv, char *compile_op
     }      }
   else if (option_bool(OPT_DHCP_FQDN))    else if (option_bool(OPT_DHCP_FQDN))
     die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);      die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
   
     /* If there's access-control config, then ignore --local-service, it's intended
        as a system default to keep otherwise unconfigured installations safe. */
     if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
       reset_option_bool(OPT_LOCAL_SERVICE); 
   
   if (testmode)    if (testmode)
     {      {

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


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