Annotation of embedaddon/dnsmasq/src/option.c, revision 1.1
1.1 ! misho 1: /* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
! 2:
! 3: This program is free software; you can redistribute it and/or modify
! 4: it under the terms of the GNU General Public License as published by
! 5: the Free Software Foundation; version 2 dated June, 1991, or
! 6: (at your option) version 3 dated 29 June, 2007.
! 7:
! 8: This program is distributed in the hope that it will be useful,
! 9: but WITHOUT ANY WARRANTY; without even the implied warranty of
! 10: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 11: GNU General Public License for more details.
! 12:
! 13: You should have received a copy of the GNU General Public License
! 14: along with this program. If not, see <http://www.gnu.org/licenses/>.
! 15: */
! 16:
! 17: /* define this to get facilitynames */
! 18: #define SYSLOG_NAMES
! 19: #include "dnsmasq.h"
! 20: #include <setjmp.h>
! 21:
! 22: static volatile int mem_recover = 0;
! 23: static jmp_buf mem_jmp;
! 24: static int one_file(char *file, int hard_opt);
! 25:
! 26: /* Solaris headers don't have facility names. */
! 27: #ifdef HAVE_SOLARIS_NETWORK
! 28: static const struct {
! 29: char *c_name;
! 30: unsigned int c_val;
! 31: } facilitynames[] = {
! 32: { "kern", LOG_KERN },
! 33: { "user", LOG_USER },
! 34: { "mail", LOG_MAIL },
! 35: { "daemon", LOG_DAEMON },
! 36: { "auth", LOG_AUTH },
! 37: { "syslog", LOG_SYSLOG },
! 38: { "lpr", LOG_LPR },
! 39: { "news", LOG_NEWS },
! 40: { "uucp", LOG_UUCP },
! 41: { "audit", LOG_AUDIT },
! 42: { "cron", LOG_CRON },
! 43: { "local0", LOG_LOCAL0 },
! 44: { "local1", LOG_LOCAL1 },
! 45: { "local2", LOG_LOCAL2 },
! 46: { "local3", LOG_LOCAL3 },
! 47: { "local4", LOG_LOCAL4 },
! 48: { "local5", LOG_LOCAL5 },
! 49: { "local6", LOG_LOCAL6 },
! 50: { "local7", LOG_LOCAL7 },
! 51: { NULL, 0 }
! 52: };
! 53: #endif
! 54:
! 55: #ifndef HAVE_GETOPT_LONG
! 56: struct myoption {
! 57: const char *name;
! 58: int has_arg;
! 59: int *flag;
! 60: int val;
! 61: };
! 62: #endif
! 63:
! 64: #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:"
! 65:
! 66: /* options which don't have a one-char version */
! 67: #define LOPT_RELOAD 256
! 68: #define LOPT_NO_NAMES 257
! 69: #define LOPT_TFTP 258
! 70: #define LOPT_SECURE 259
! 71: #define LOPT_PREFIX 260
! 72: #define LOPT_PTR 261
! 73: #define LOPT_BRIDGE 262
! 74: #define LOPT_TFTP_MAX 263
! 75: #define LOPT_FORCE 264
! 76: #define LOPT_NOBLOCK 265
! 77: #define LOPT_LOG_OPTS 266
! 78: #define LOPT_MAX_LOGS 267
! 79: #define LOPT_CIRCUIT 268
! 80: #define LOPT_REMOTE 269
! 81: #define LOPT_SUBSCR 270
! 82: #define LOPT_INTNAME 271
! 83: #define LOPT_BANK 272
! 84: #define LOPT_DHCP_HOST 273
! 85: #define LOPT_APREF 274
! 86: #define LOPT_OVERRIDE 275
! 87: #define LOPT_TFTPPORTS 276
! 88: #define LOPT_REBIND 277
! 89: #define LOPT_NOLAST 278
! 90: #define LOPT_OPTS 279
! 91: #define LOPT_DHCP_OPTS 280
! 92: #define LOPT_MATCH 281
! 93: #define LOPT_BROADCAST 282
! 94: #define LOPT_NEGTTL 283
! 95: #define LOPT_ALTPORT 284
! 96: #define LOPT_SCRIPTUSR 285
! 97: #define LOPT_LOCAL 286
! 98: #define LOPT_NAPTR 287
! 99: #define LOPT_MINPORT 288
! 100: #define LOPT_DHCP_FQDN 289
! 101: #define LOPT_CNAME 290
! 102: #define LOPT_PXE_PROMT 291
! 103: #define LOPT_PXE_SERV 292
! 104: #define LOPT_TEST 293
! 105: #define LOPT_TAG_IF 294
! 106: #define LOPT_PROXY 295
! 107: #define LOPT_GEN_NAMES 296
! 108: #define LOPT_MAXTTL 297
! 109: #define LOPT_NO_REBIND 298
! 110: #define LOPT_LOC_REBND 299
! 111: #define LOPT_ADD_MAC 300
! 112: #define LOPT_DNSSEC 301
! 113: #define LOPT_INCR_ADDR 302
! 114: #define LOPT_CONNTRACK 303
! 115: #define LOPT_FQDN 304
! 116: #define LOPT_LUASCRIPT 305
! 117: #define LOPT_RA 306
! 118: #define LOPT_DUID 307
! 119: #define LOPT_HOST_REC 308
! 120: #define LOPT_TFTP_LC 309
! 121: #define LOPT_RR 310
! 122: #define LOPT_CLVERBIND 311
! 123: #define LOPT_MAXCTTL 312
! 124: #define LOPT_AUTHZONE 313
! 125: #define LOPT_AUTHSERV 314
! 126: #define LOPT_AUTHTTL 315
! 127: #define LOPT_AUTHSOA 316
! 128: #define LOPT_AUTHSFS 317
! 129: #define LOPT_AUTHPEER 318
! 130: #define LOPT_IPSET 319
! 131: #ifdef OPTION6_PREFIX_CLASS
! 132: #define LOPT_PREF_CLSS 320
! 133: #endif
! 134:
! 135: #ifdef HAVE_GETOPT_LONG
! 136: static const struct option opts[] =
! 137: #else
! 138: static const struct myoption opts[] =
! 139: #endif
! 140: {
! 141: { "version", 0, 0, 'v' },
! 142: { "no-hosts", 0, 0, 'h' },
! 143: { "no-poll", 0, 0, 'n' },
! 144: { "help", 0, 0, 'w' },
! 145: { "no-daemon", 0, 0, 'd' },
! 146: { "log-queries", 0, 0, 'q' },
! 147: { "user", 2, 0, 'u' },
! 148: { "group", 2, 0, 'g' },
! 149: { "resolv-file", 2, 0, 'r' },
! 150: { "mx-host", 1, 0, 'm' },
! 151: { "mx-target", 1, 0, 't' },
! 152: { "cache-size", 2, 0, 'c' },
! 153: { "port", 1, 0, 'p' },
! 154: { "dhcp-leasefile", 2, 0, 'l' },
! 155: { "dhcp-lease", 1, 0, 'l' },
! 156: { "dhcp-host", 1, 0, 'G' },
! 157: { "dhcp-range", 1, 0, 'F' },
! 158: { "dhcp-option", 1, 0, 'O' },
! 159: { "dhcp-boot", 1, 0, 'M' },
! 160: { "domain", 1, 0, 's' },
! 161: { "domain-suffix", 1, 0, 's' },
! 162: { "interface", 1, 0, 'i' },
! 163: { "listen-address", 1, 0, 'a' },
! 164: { "bogus-priv", 0, 0, 'b' },
! 165: { "bogus-nxdomain", 1, 0, 'B' },
! 166: { "selfmx", 0, 0, 'e' },
! 167: { "filterwin2k", 0, 0, 'f' },
! 168: { "pid-file", 2, 0, 'x' },
! 169: { "strict-order", 0, 0, 'o' },
! 170: { "server", 1, 0, 'S' },
! 171: { "local", 1, 0, LOPT_LOCAL },
! 172: { "address", 1, 0, 'A' },
! 173: { "conf-file", 2, 0, 'C' },
! 174: { "no-resolv", 0, 0, 'R' },
! 175: { "expand-hosts", 0, 0, 'E' },
! 176: { "localmx", 0, 0, 'L' },
! 177: { "local-ttl", 1, 0, 'T' },
! 178: { "no-negcache", 0, 0, 'N' },
! 179: { "addn-hosts", 1, 0, 'H' },
! 180: { "query-port", 1, 0, 'Q' },
! 181: { "except-interface", 1, 0, 'I' },
! 182: { "no-dhcp-interface", 1, 0, '2' },
! 183: { "domain-needed", 0, 0, 'D' },
! 184: { "dhcp-lease-max", 1, 0, 'X' },
! 185: { "bind-interfaces", 0, 0, 'z' },
! 186: { "read-ethers", 0, 0, 'Z' },
! 187: { "alias", 1, 0, 'V' },
! 188: { "dhcp-vendorclass", 1, 0, 'U' },
! 189: { "dhcp-userclass", 1, 0, 'j' },
! 190: { "dhcp-ignore", 1, 0, 'J' },
! 191: { "edns-packet-max", 1, 0, 'P' },
! 192: { "keep-in-foreground", 0, 0, 'k' },
! 193: { "dhcp-authoritative", 0, 0, 'K' },
! 194: { "srv-host", 1, 0, 'W' },
! 195: { "localise-queries", 0, 0, 'y' },
! 196: { "txt-record", 1, 0, 'Y' },
! 197: { "dns-rr", 1, 0, LOPT_RR },
! 198: { "enable-dbus", 2, 0, '1' },
! 199: { "bootp-dynamic", 2, 0, '3' },
! 200: { "dhcp-mac", 1, 0, '4' },
! 201: { "no-ping", 0, 0, '5' },
! 202: { "dhcp-script", 1, 0, '6' },
! 203: { "conf-dir", 1, 0, '7' },
! 204: { "log-facility", 1, 0 ,'8' },
! 205: { "leasefile-ro", 0, 0, '9' },
! 206: { "dns-forward-max", 1, 0, '0' },
! 207: { "clear-on-reload", 0, 0, LOPT_RELOAD },
! 208: { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
! 209: { "enable-tftp", 0, 0, LOPT_TFTP },
! 210: { "tftp-secure", 0, 0, LOPT_SECURE },
! 211: { "tftp-unique-root", 0, 0, LOPT_APREF },
! 212: { "tftp-root", 1, 0, LOPT_PREFIX },
! 213: { "tftp-max", 1, 0, LOPT_TFTP_MAX },
! 214: { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
! 215: { "ptr-record", 1, 0, LOPT_PTR },
! 216: { "naptr-record", 1, 0, LOPT_NAPTR },
! 217: { "bridge-interface", 1, 0 , LOPT_BRIDGE },
! 218: { "dhcp-option-force", 1, 0, LOPT_FORCE },
! 219: { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
! 220: { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
! 221: { "log-async", 2, 0, LOPT_MAX_LOGS },
! 222: { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
! 223: { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
! 224: { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
! 225: { "interface-name", 1, 0, LOPT_INTNAME },
! 226: { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
! 227: { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
! 228: { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
! 229: { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
! 230: { "stop-dns-rebind", 0, 0, LOPT_REBIND },
! 231: { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
! 232: { "all-servers", 0, 0, LOPT_NOLAST },
! 233: { "dhcp-match", 1, 0, LOPT_MATCH },
! 234: { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
! 235: { "neg-ttl", 1, 0, LOPT_NEGTTL },
! 236: { "max-ttl", 1, 0, LOPT_MAXTTL },
! 237: { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
! 238: { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
! 239: { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
! 240: { "min-port", 1, 0, LOPT_MINPORT },
! 241: { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
! 242: { "cname", 1, 0, LOPT_CNAME },
! 243: { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
! 244: { "pxe-service", 1, 0, LOPT_PXE_SERV },
! 245: { "test", 0, 0, LOPT_TEST },
! 246: { "tag-if", 1, 0, LOPT_TAG_IF },
! 247: { "dhcp-proxy", 2, 0, LOPT_PROXY },
! 248: { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
! 249: { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
! 250: { "add-mac", 0, 0, LOPT_ADD_MAC },
! 251: { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
! 252: { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
! 253: { "conntrack", 0, 0, LOPT_CONNTRACK },
! 254: { "dhcp-client-update", 0, 0, LOPT_FQDN },
! 255: { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
! 256: { "enable-ra", 0, 0, LOPT_RA },
! 257: { "dhcp-duid", 1, 0, LOPT_DUID },
! 258: { "host-record", 1, 0, LOPT_HOST_REC },
! 259: { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
! 260: { "auth-zone", 1, 0, LOPT_AUTHZONE },
! 261: { "auth-server", 1, 0, LOPT_AUTHSERV },
! 262: { "auth-ttl", 1, 0, LOPT_AUTHTTL },
! 263: { "auth-soa", 1, 0, LOPT_AUTHSOA },
! 264: { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
! 265: { "auth-peer", 1, 0, LOPT_AUTHPEER },
! 266: { "ipset", 1, 0, LOPT_IPSET },
! 267: #ifdef OPTION6_PREFIX_CLASS
! 268: { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
! 269: #endif
! 270: { NULL, 0, 0, 0 }
! 271: };
! 272:
! 273:
! 274: #define ARG_DUP OPT_LAST
! 275: #define ARG_ONE OPT_LAST + 1
! 276: #define ARG_USED_CL OPT_LAST + 2
! 277: #define ARG_USED_FILE OPT_LAST + 3
! 278:
! 279: static struct {
! 280: int opt;
! 281: unsigned int rept;
! 282: char * const flagdesc;
! 283: char * const desc;
! 284: char * const arg;
! 285: } usage[] = {
! 286: { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
! 287: { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
! 288: { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
! 289: { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
! 290: { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
! 291: { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
! 292: { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
! 293: { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
! 294: { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
! 295: { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
! 296: { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
! 297: { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
! 298: { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
! 299: { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
! 300: { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
! 301: { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
! 302: { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
! 303: { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
! 304: { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
! 305: { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
! 306: { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
! 307: { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
! 308: { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
! 309: { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
! 310: { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
! 311: { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
! 312: { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
! 313: { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
! 314: { 'K', OPT_AUTHORITATIVE, NULL, gettext_noop("Assume we are the only DHCP server on the local network."), NULL },
! 315: { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
! 316: { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
! 317: { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
! 318: { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
! 319: { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
! 320: { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
! 321: { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
! 322: { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
! 323: { LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},
! 324: { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
! 325: { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
! 326: { 'q', OPT_LOG, NULL, gettext_noop("Log DNS queries."), NULL },
! 327: { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
! 328: { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
! 329: { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
! 330: { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
! 331: { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
! 332: { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
! 333: { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
! 334: { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
! 335: { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
! 336: { LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
! 337: { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
! 338: { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
! 339: { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
! 340: { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
! 341: { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
! 342: { 'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp for known DHCP options."), NULL },
! 343: { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
! 344: { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
! 345: { 'y', OPT_LOCALISE, NULL, gettext_noop("Answer DNS queries based on the interface a query was sent to."), NULL },
! 346: { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
! 347: { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
! 348: { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
! 349: { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
! 350: { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
! 351: { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
! 352: { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
! 353: { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
! 354: { '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
! 355: { LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
! 356: { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
! 357: { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
! 358: { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
! 359: { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
! 360: { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
! 361: { '8', ARG_ONE, "<facilty>|<file>", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), NULL },
! 362: { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
! 363: { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
! 364: { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
! 365: { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
! 366: { LOPT_OVERRIDE, OPT_NO_OVERRIDE, NULL, gettext_noop("Do NOT reuse filename and server fields for extra DHCP options."), NULL },
! 367: { LOPT_TFTP, OPT_TFTP, NULL, gettext_noop("Enable integrated read-only TFTP server."), NULL },
! 368: { LOPT_PREFIX, ARG_DUP, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
! 369: { LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL },
! 370: { LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
! 371: { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
! 372: { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
! 373: { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
! 374: { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
! 375: { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
! 376: { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
! 377: { LOPT_REBIND, OPT_NO_REBIND, NULL, gettext_noop("Stop DNS rebinding. Filter private IP ranges when resolving."), NULL },
! 378: { LOPT_LOC_REBND, OPT_LOCAL_REBIND, NULL, gettext_noop("Allow rebinding of 127.0.0.0/8, for RBL servers."), NULL },
! 379: { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
! 380: { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
! 381: { LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
! 382: { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
! 383: { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
! 384: { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
! 385: { LOPT_DHCP_FQDN, OPT_DHCP_FQDN, NULL, gettext_noop("Use only fully qualified domain names for DHCP clients."), NULL },
! 386: { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
! 387: { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
! 388: { LOPT_CNAME, ARG_DUP, "<alias>,<target>", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
! 389: { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
! 390: { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
! 391: { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
! 392: { LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
! 393: { LOPT_DNSSEC, OPT_DNSSEC, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
! 394: { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
! 395: { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
! 396: { LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },
! 397: { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
! 398: { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
! 399: { LOPT_HOST_REC, ARG_DUP, "<name>,<address>", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
! 400: { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
! 401: { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
! 402: { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
! 403: { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
! 404: { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
! 405: { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritive zone information"), NULL },
! 406: { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
! 407: { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
! 408: { LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
! 409: #ifdef OPTION6_PREFIX_CLASS
! 410: { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
! 411: #endif
! 412: { 0, 0, NULL, NULL, NULL }
! 413: };
! 414:
! 415: /* We hide metacharaters in quoted strings by mapping them into the ASCII control
! 416: character space. Note that the \0, \t \b \r \033 and \n characters are carefully placed in the
! 417: following sequence so that they map to themselves: it is therefore possible to call
! 418: unhide_metas repeatedly on string without breaking things.
! 419: The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
! 420: couple of other places.
! 421: Note that space is included here so that
! 422: --dhcp-option=3, string
! 423: has five characters, whilst
! 424: --dhcp-option=3," string"
! 425: has six.
! 426: */
! 427:
! 428: static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
! 429:
! 430: static char hide_meta(char c)
! 431: {
! 432: unsigned int i;
! 433:
! 434: for (i = 0; i < (sizeof(meta) - 1); i++)
! 435: if (c == meta[i])
! 436: return (char)i;
! 437:
! 438: return c;
! 439: }
! 440:
! 441: static char unhide_meta(char cr)
! 442: {
! 443: unsigned int c = cr;
! 444:
! 445: if (c < (sizeof(meta) - 1))
! 446: cr = meta[c];
! 447:
! 448: return cr;
! 449: }
! 450:
! 451: static void unhide_metas(char *cp)
! 452: {
! 453: if (cp)
! 454: for(; *cp; cp++)
! 455: *cp = unhide_meta(*cp);
! 456: }
! 457:
! 458: static void *opt_malloc(size_t size)
! 459: {
! 460: void *ret;
! 461:
! 462: if (mem_recover)
! 463: {
! 464: ret = whine_malloc(size);
! 465: if (!ret)
! 466: longjmp(mem_jmp, 1);
! 467: }
! 468: else
! 469: ret = safe_malloc(size);
! 470:
! 471: return ret;
! 472: }
! 473:
! 474: static char *opt_string_alloc(char *cp)
! 475: {
! 476: char *ret = NULL;
! 477:
! 478: if (cp && strlen(cp) != 0)
! 479: {
! 480: ret = opt_malloc(strlen(cp)+1);
! 481: strcpy(ret, cp);
! 482:
! 483: /* restore hidden metachars */
! 484: unhide_metas(ret);
! 485: }
! 486:
! 487: return ret;
! 488: }
! 489:
! 490:
! 491: /* find next comma, split string with zero and eliminate spaces.
! 492: return start of string following comma */
! 493:
! 494: static char *split_chr(char *s, char c)
! 495: {
! 496: char *comma, *p;
! 497:
! 498: if (!s || !(comma = strchr(s, c)))
! 499: return NULL;
! 500:
! 501: p = comma;
! 502: *comma = ' ';
! 503:
! 504: for (; *comma == ' '; comma++);
! 505:
! 506: for (; (p >= s) && *p == ' '; p--)
! 507: *p = 0;
! 508:
! 509: return comma;
! 510: }
! 511:
! 512: static char *split(char *s)
! 513: {
! 514: return split_chr(s, ',');
! 515: }
! 516:
! 517: static char *canonicalise_opt(char *s)
! 518: {
! 519: char *ret;
! 520: int nomem;
! 521:
! 522: if (!s)
! 523: return 0;
! 524:
! 525: unhide_metas(s);
! 526: if (!(ret = canonicalise(s, &nomem)) && nomem)
! 527: {
! 528: if (mem_recover)
! 529: longjmp(mem_jmp, 1);
! 530: else
! 531: die(_("could not get memory"), NULL, EC_NOMEM);
! 532: }
! 533:
! 534: return ret;
! 535: }
! 536:
! 537: static int atoi_check(char *a, int *res)
! 538: {
! 539: char *p;
! 540:
! 541: if (!a)
! 542: return 0;
! 543:
! 544: unhide_metas(a);
! 545:
! 546: for (p = a; *p; p++)
! 547: if (*p < '0' || *p > '9')
! 548: return 0;
! 549:
! 550: *res = atoi(a);
! 551: return 1;
! 552: }
! 553:
! 554: static int atoi_check16(char *a, int *res)
! 555: {
! 556: if (!(atoi_check(a, res)) ||
! 557: *res < 0 ||
! 558: *res > 0xffff)
! 559: return 0;
! 560:
! 561: return 1;
! 562: }
! 563:
! 564: static void add_txt(char *name, char *txt)
! 565: {
! 566: size_t len = strlen(txt);
! 567: struct txt_record *r = opt_malloc(sizeof(struct txt_record));
! 568:
! 569: r->name = opt_string_alloc(name);
! 570: r->next = daemon->txt;
! 571: daemon->txt = r;
! 572: r->class = C_CHAOS;
! 573: r->txt = opt_malloc(len+1);
! 574: r->len = len+1;
! 575: *(r->txt) = len;
! 576: memcpy((r->txt)+1, txt, len);
! 577: }
! 578:
! 579: static void do_usage(void)
! 580: {
! 581: char buff[100];
! 582: int i, j;
! 583:
! 584: struct {
! 585: char handle;
! 586: int val;
! 587: } tab[] = {
! 588: { '$', CACHESIZ },
! 589: { '*', EDNS_PKTSZ },
! 590: { '&', MAXLEASES },
! 591: { '!', FTABSIZ },
! 592: { '#', TFTP_MAX_CONNECTIONS },
! 593: { '\0', 0 }
! 594: };
! 595:
! 596: printf(_("Usage: dnsmasq [options]\n\n"));
! 597: #ifndef HAVE_GETOPT_LONG
! 598: printf(_("Use short options only on the command line.\n"));
! 599: #endif
! 600: printf(_("Valid options are:\n"));
! 601:
! 602: for (i = 0; usage[i].opt != 0; i++)
! 603: {
! 604: char *desc = usage[i].flagdesc;
! 605: char *eq = "=";
! 606:
! 607: if (!desc || *desc == '[')
! 608: eq = "";
! 609:
! 610: if (!desc)
! 611: desc = "";
! 612:
! 613: for ( j = 0; opts[j].name; j++)
! 614: if (opts[j].val == usage[i].opt)
! 615: break;
! 616: if (usage[i].opt < 256)
! 617: sprintf(buff, "-%c, ", usage[i].opt);
! 618: else
! 619: sprintf(buff, " ");
! 620:
! 621: sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
! 622: printf("%-40.40s", buff);
! 623:
! 624: if (usage[i].arg)
! 625: {
! 626: strcpy(buff, usage[i].arg);
! 627: for (j = 0; tab[j].handle; j++)
! 628: if (tab[j].handle == *(usage[i].arg))
! 629: sprintf(buff, "%d", tab[j].val);
! 630: }
! 631: printf(_(usage[i].desc), buff);
! 632: printf("\n");
! 633: }
! 634: }
! 635:
! 636: #define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
! 637:
! 638: char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
! 639: {
! 640: int source_port = 0, serv_port = NAMESERVER_PORT;
! 641: char *portno, *source;
! 642: #ifdef HAVE_IPV6
! 643: int scope_index = 0;
! 644: char *scope_id;
! 645: #endif
! 646:
! 647: if ((source = split_chr(arg, '@')) && /* is there a source. */
! 648: (portno = split_chr(source, '#')) &&
! 649: !atoi_check16(portno, &source_port))
! 650: return _("bad port");
! 651:
! 652: if ((portno = split_chr(arg, '#')) && /* is there a port no. */
! 653: !atoi_check16(portno, &serv_port))
! 654: return _("bad port");
! 655:
! 656: #ifdef HAVE_IPV6
! 657: scope_id = split_chr(arg, '%');
! 658: #endif
! 659:
! 660: if ((addr->in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t) -1)
! 661: {
! 662: addr->in.sin_port = htons(serv_port);
! 663: addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
! 664: #ifdef HAVE_SOCKADDR_SA_LEN
! 665: source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
! 666: #endif
! 667: source_addr->in.sin_addr.s_addr = INADDR_ANY;
! 668: source_addr->in.sin_port = htons(daemon->query_port);
! 669:
! 670: if (source)
! 671: {
! 672: if (flags)
! 673: *flags |= SERV_HAS_SOURCE;
! 674: source_addr->in.sin_port = htons(source_port);
! 675: if ((source_addr->in.sin_addr.s_addr = inet_addr(source)) == (in_addr_t) -1)
! 676: {
! 677: #if defined(SO_BINDTODEVICE)
! 678: source_addr->in.sin_addr.s_addr = INADDR_ANY;
! 679: strncpy(interface, source, IF_NAMESIZE - 1);
! 680: #else
! 681: return _("interface binding not supported");
! 682: #endif
! 683: }
! 684: }
! 685: }
! 686: #ifdef HAVE_IPV6
! 687: else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
! 688: {
! 689: if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
! 690: return _("bad interface name");
! 691:
! 692: addr->in6.sin6_port = htons(serv_port);
! 693: addr->in6.sin6_scope_id = scope_index;
! 694: source_addr->in6.sin6_addr = in6addr_any;
! 695: source_addr->in6.sin6_port = htons(daemon->query_port);
! 696: source_addr->in6.sin6_scope_id = 0;
! 697: addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
! 698: addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
! 699: #ifdef HAVE_SOCKADDR_SA_LEN
! 700: addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
! 701: #endif
! 702: if (source)
! 703: {
! 704: if (flags)
! 705: *flags |= SERV_HAS_SOURCE;
! 706: source_addr->in6.sin6_port = htons(source_port);
! 707: if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
! 708: {
! 709: #if defined(SO_BINDTODEVICE)
! 710: source_addr->in6.sin6_addr = in6addr_any;
! 711: strncpy(interface, source, IF_NAMESIZE - 1);
! 712: #else
! 713: return _("interface binding not supported");
! 714: #endif
! 715: }
! 716: }
! 717: }
! 718: #endif
! 719: else
! 720: return _("bad address");
! 721:
! 722: return NULL;
! 723: }
! 724:
! 725: #ifdef HAVE_DHCP
! 726:
! 727: static int is_tag_prefix(char *arg)
! 728: {
! 729: if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
! 730: return 1;
! 731:
! 732: return 0;
! 733: }
! 734:
! 735: static char *set_prefix(char *arg)
! 736: {
! 737: if (strstr(arg, "set:") == arg)
! 738: return arg+4;
! 739:
! 740: return arg;
! 741: }
! 742:
! 743: /* This is too insanely large to keep in-line in the switch */
! 744: static int parse_dhcp_opt(char *errstr, char *arg, int flags)
! 745: {
! 746: struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
! 747: char lenchar = 0, *cp;
! 748: int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
! 749: char *comma = NULL;
! 750: struct dhcp_netid *np = NULL;
! 751: u16 opt_len = 0;
! 752: int is6 = 0;
! 753:
! 754: new->len = 0;
! 755: new->flags = flags;
! 756: new->netid = NULL;
! 757: new->val = NULL;
! 758: new->opt = 0;
! 759:
! 760: while (arg)
! 761: {
! 762: comma = split(arg);
! 763:
! 764: for (cp = arg; *cp; cp++)
! 765: if (*cp < '0' || *cp > '9')
! 766: break;
! 767:
! 768: if (!*cp)
! 769: {
! 770: new->opt = atoi(arg);
! 771: opt_len = 0;
! 772: break;
! 773: }
! 774:
! 775: if (strstr(arg, "option:") == arg)
! 776: {
! 777: new->opt = lookup_dhcp_opt(AF_INET, arg+7);
! 778: opt_len = lookup_dhcp_len(AF_INET, new->opt);
! 779: /* option:<optname> must follow tag and vendor string. */
! 780: if ((opt_len & OT_INTERNAL) && flags != DHOPT_MATCH)
! 781: new->opt = 0;
! 782: break;
! 783: }
! 784: #ifdef HAVE_DHCP6
! 785: else if (strstr(arg, "option6:") == arg)
! 786: {
! 787: for (cp = arg+8; *cp; cp++)
! 788: if (*cp < '0' || *cp > '9')
! 789: break;
! 790:
! 791: if (!*cp)
! 792: {
! 793: new->opt = atoi(arg+8);
! 794: opt_len = 0;
! 795: }
! 796: else
! 797: {
! 798: new->opt = lookup_dhcp_opt(AF_INET6, arg+8);
! 799: opt_len = lookup_dhcp_len(AF_INET6, new->opt);
! 800: if ((opt_len & OT_INTERNAL) && flags != DHOPT_MATCH)
! 801: new->opt = 0;
! 802: }
! 803: /* option6:<opt>|<optname> must follow tag and vendor string. */
! 804: is6 = 1;
! 805: break;
! 806: }
! 807: #endif
! 808: else if (strstr(arg, "vendor:") == arg)
! 809: {
! 810: new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
! 811: new->flags |= DHOPT_VENDOR;
! 812: }
! 813: else if (strstr(arg, "encap:") == arg)
! 814: {
! 815: new->u.encap = atoi(arg+6);
! 816: new->flags |= DHOPT_ENCAPSULATE;
! 817: }
! 818: else if (strstr(arg, "vi-encap:") == arg)
! 819: {
! 820: new->u.encap = atoi(arg+9);
! 821: new->flags |= DHOPT_RFC3925;
! 822: if (flags == DHOPT_MATCH)
! 823: {
! 824: new->opt = 1; /* avoid error below */
! 825: break;
! 826: }
! 827: }
! 828: else
! 829: {
! 830: new->netid = opt_malloc(sizeof (struct dhcp_netid));
! 831: /* allow optional "net:" or "tag:" for consistency */
! 832: if (is_tag_prefix(arg))
! 833: new->netid->net = opt_string_alloc(arg+4);
! 834: else
! 835: new->netid->net = opt_string_alloc(set_prefix(arg));
! 836: new->netid->next = np;
! 837: np = new->netid;
! 838: }
! 839:
! 840: arg = comma;
! 841: }
! 842:
! 843: #ifdef HAVE_DHCP6
! 844: if (is6)
! 845: {
! 846: if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
! 847: ret_err(_("unsupported encapsulation for IPv6 option"));
! 848:
! 849: if (opt_len == 0 &&
! 850: !(new->flags & DHOPT_RFC3925))
! 851: opt_len = lookup_dhcp_len(AF_INET6 ,new->opt);
! 852: }
! 853: else
! 854: #endif
! 855: if (opt_len == 0 &&
! 856: !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
! 857: opt_len = lookup_dhcp_len(AF_INET ,new->opt);
! 858:
! 859: /* option may be missing with rfc3925 match */
! 860: if (new->opt == 0)
! 861: ret_err(_("bad dhcp-option"));
! 862:
! 863: if (comma)
! 864: {
! 865: /* characterise the value */
! 866: char c;
! 867: int found_dig = 0;
! 868: is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
! 869: addrs = digs = 1;
! 870: dots = 0;
! 871: for (cp = comma; (c = *cp); cp++)
! 872: if (c == ',')
! 873: {
! 874: addrs++;
! 875: is_dec = is_hex = 0;
! 876: }
! 877: else if (c == ':')
! 878: {
! 879: digs++;
! 880: is_dec = is_addr = 0;
! 881: }
! 882: else if (c == '/')
! 883: {
! 884: is_addr6 = is_dec = is_hex = 0;
! 885: if (cp == comma) /* leading / means a pathname */
! 886: is_addr = 0;
! 887: }
! 888: else if (c == '.')
! 889: {
! 890: is_addr6 = is_dec = is_hex = 0;
! 891: dots++;
! 892: }
! 893: else if (c == '-')
! 894: is_hex = is_addr = is_addr6 = 0;
! 895: else if (c == ' ')
! 896: is_dec = is_hex = 0;
! 897: else if (!(c >='0' && c <= '9'))
! 898: {
! 899: is_addr = 0;
! 900: if (cp[1] == 0 && is_dec &&
! 901: (c == 'b' || c == 's' || c == 'i'))
! 902: {
! 903: lenchar = c;
! 904: *cp = 0;
! 905: }
! 906: else
! 907: is_dec = 0;
! 908: if (!((c >='A' && c <= 'F') ||
! 909: (c >='a' && c <= 'f') ||
! 910: (c == '*' && (flags & DHOPT_MATCH))))
! 911: {
! 912: is_hex = 0;
! 913: if (c != '[' && c != ']')
! 914: is_addr6 = 0;
! 915: }
! 916: }
! 917: else
! 918: found_dig = 1;
! 919:
! 920: if (!found_dig)
! 921: is_dec = is_addr = 0;
! 922:
! 923: /* We know that some options take addresses */
! 924: if (opt_len & OT_ADDR_LIST)
! 925: {
! 926: is_string = is_dec = is_hex = 0;
! 927:
! 928: if (!is6 && (!is_addr || dots == 0))
! 929: ret_err(_("bad IP address"));
! 930:
! 931: if (is6 && !is_addr6)
! 932: ret_err(_("bad IPv6 address"));
! 933: }
! 934: /* or names */
! 935: else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
! 936: is_addr6 = is_addr = is_dec = is_hex = 0;
! 937:
! 938: if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
! 939: {
! 940: int val, fac = 1;
! 941:
! 942: switch (comma[strlen(comma) - 1])
! 943: {
! 944: case 'w':
! 945: case 'W':
! 946: fac *= 7;
! 947: /* fall through */
! 948: case 'd':
! 949: case 'D':
! 950: fac *= 24;
! 951: /* fall though */
! 952: case 'h':
! 953: case 'H':
! 954: fac *= 60;
! 955: /* fall through */
! 956: case 'm':
! 957: case 'M':
! 958: fac *= 60;
! 959: /* fall through */
! 960: case 's':
! 961: case 'S':
! 962: comma[strlen(comma) - 1] = 0;
! 963: }
! 964:
! 965: new->len = 4;
! 966: new->val = opt_malloc(4);
! 967: val = atoi(comma);
! 968: *((int *)new->val) = htonl(val * fac);
! 969: }
! 970: else if (is_hex && digs > 1)
! 971: {
! 972: new->len = digs;
! 973: new->val = opt_malloc(new->len);
! 974: parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
! 975: new->flags |= DHOPT_HEX;
! 976: }
! 977: else if (is_dec)
! 978: {
! 979: int i, val = atoi(comma);
! 980: /* assume numeric arg is 1 byte except for
! 981: options where it is known otherwise.
! 982: For vendor class option, we have to hack. */
! 983: if (opt_len != 0)
! 984: new->len = opt_len;
! 985: else if (val & 0xffff0000)
! 986: new->len = 4;
! 987: else if (val & 0xff00)
! 988: new->len = 2;
! 989: else
! 990: new->len = 1;
! 991:
! 992: if (lenchar == 'b')
! 993: new->len = 1;
! 994: else if (lenchar == 's')
! 995: new->len = 2;
! 996: else if (lenchar == 'i')
! 997: new->len = 4;
! 998:
! 999: new->val = opt_malloc(new->len);
! 1000: for (i=0; i<new->len; i++)
! 1001: new->val[i] = val>>((new->len - i - 1)*8);
! 1002: }
! 1003: else if (is_addr && !is6)
! 1004: {
! 1005: struct in_addr in;
! 1006: unsigned char *op;
! 1007: char *slash;
! 1008: /* max length of address/subnet descriptor is five bytes,
! 1009: add one for the option 120 enc byte too */
! 1010: new->val = op = opt_malloc((5 * addrs) + 1);
! 1011: new->flags |= DHOPT_ADDR;
! 1012:
! 1013: if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
! 1014: new->opt == OPTION_SIP_SERVER)
! 1015: {
! 1016: *(op++) = 1; /* RFC 3361 "enc byte" */
! 1017: new->flags &= ~DHOPT_ADDR;
! 1018: }
! 1019: while (addrs--)
! 1020: {
! 1021: cp = comma;
! 1022: comma = split(cp);
! 1023: slash = split_chr(cp, '/');
! 1024: in.s_addr = inet_addr(cp);
! 1025: if (!slash)
! 1026: {
! 1027: memcpy(op, &in, INADDRSZ);
! 1028: op += INADDRSZ;
! 1029: }
! 1030: else
! 1031: {
! 1032: unsigned char *p = (unsigned char *)∈
! 1033: int netsize = atoi(slash);
! 1034: *op++ = netsize;
! 1035: if (netsize > 0)
! 1036: *op++ = *p++;
! 1037: if (netsize > 8)
! 1038: *op++ = *p++;
! 1039: if (netsize > 16)
! 1040: *op++ = *p++;
! 1041: if (netsize > 24)
! 1042: *op++ = *p++;
! 1043: new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
! 1044: }
! 1045: }
! 1046: new->len = op - new->val;
! 1047: }
! 1048: else if (is_addr6 && is6)
! 1049: {
! 1050: unsigned char *op;
! 1051: new->val = op = opt_malloc(16 * addrs);
! 1052: new->flags |= DHOPT_ADDR6;
! 1053: while (addrs--)
! 1054: {
! 1055: cp = comma;
! 1056: comma = split(cp);
! 1057:
! 1058: /* check for [1234::7] */
! 1059: if (*cp == '[')
! 1060: cp++;
! 1061: if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
! 1062: cp[strlen(cp)-1] = 0;
! 1063:
! 1064: if (inet_pton(AF_INET6, cp, op))
! 1065: {
! 1066: op += IN6ADDRSZ;
! 1067: continue;
! 1068: }
! 1069:
! 1070: ret_err(_("bad IPv6 address"));
! 1071: }
! 1072: new->len = op - new->val;
! 1073: }
! 1074: else if (is_string)
! 1075: {
! 1076: /* text arg */
! 1077: if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
! 1078: !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
! 1079: {
! 1080: /* dns search, RFC 3397, or SIP, RFC 3361 */
! 1081: unsigned char *q, *r, *tail;
! 1082: unsigned char *p, *m = NULL, *newp;
! 1083: size_t newlen, len = 0;
! 1084: int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
! 1085:
! 1086: arg = comma;
! 1087: comma = split(arg);
! 1088:
! 1089: while (arg && *arg)
! 1090: {
! 1091: char *in, *dom = NULL;
! 1092: size_t domlen = 1;
! 1093: /* Allow "." as an empty domain */
! 1094: if (strcmp (arg, ".") != 0)
! 1095: {
! 1096: if (!(dom = canonicalise_opt(arg)))
! 1097: ret_err(_("bad domain in dhcp-option"));
! 1098:
! 1099: domlen = strlen(dom) + 2;
! 1100: }
! 1101:
! 1102: newp = opt_malloc(len + domlen + header_size);
! 1103: if (m)
! 1104: {
! 1105: memcpy(newp, m, header_size + len);
! 1106: free(m);
! 1107: }
! 1108: m = newp;
! 1109: p = m + header_size;
! 1110: q = p + len;
! 1111:
! 1112: /* add string on the end in RFC1035 format */
! 1113: for (in = dom; in && *in;)
! 1114: {
! 1115: unsigned char *cp = q++;
! 1116: int j;
! 1117: for (j = 0; *in && (*in != '.'); in++, j++)
! 1118: *q++ = *in;
! 1119: *cp = j;
! 1120: if (*in)
! 1121: in++;
! 1122: }
! 1123: *q++ = 0;
! 1124: free(dom);
! 1125:
! 1126: /* Now tail-compress using earlier names. */
! 1127: newlen = q - p;
! 1128: for (tail = p + len; *tail; tail += (*tail) + 1)
! 1129: for (r = p; r - p < (int)len; r += (*r) + 1)
! 1130: if (strcmp((char *)r, (char *)tail) == 0)
! 1131: {
! 1132: PUTSHORT((r - p) | 0xc000, tail);
! 1133: newlen = tail - p;
! 1134: goto end;
! 1135: }
! 1136: end:
! 1137: len = newlen;
! 1138:
! 1139: arg = comma;
! 1140: comma = split(arg);
! 1141: }
! 1142:
! 1143: /* RFC 3361, enc byte is zero for names */
! 1144: if (new->opt == OPTION_SIP_SERVER)
! 1145: m[0] = 0;
! 1146: new->len = (int) len + header_size;
! 1147: new->val = m;
! 1148: }
! 1149: #ifdef HAVE_DHCP6
! 1150: else if (comma && (opt_len & OT_CSTRING))
! 1151: {
! 1152: /* length fields are two bytes so need 16 bits for each string */
! 1153: int i, commas = 1;
! 1154: unsigned char *p, *newp;
! 1155:
! 1156: for (i = 0; comma[i]; i++)
! 1157: if (comma[i] == ',')
! 1158: commas++;
! 1159:
! 1160: newp = opt_malloc(strlen(comma)+(2*commas));
! 1161: p = newp;
! 1162: arg = comma;
! 1163: comma = split(arg);
! 1164:
! 1165: while (arg && *arg)
! 1166: {
! 1167: u16 len = strlen(arg);
! 1168: unhide_metas(arg);
! 1169: PUTSHORT(len, p);
! 1170: memcpy(p, arg, len);
! 1171: p += len;
! 1172:
! 1173: arg = comma;
! 1174: comma = split(arg);
! 1175: }
! 1176:
! 1177: new->val = newp;
! 1178: new->len = p - newp;
! 1179: }
! 1180: else if (comma && (opt_len & OT_RFC1035_NAME))
! 1181: {
! 1182: unsigned char *p = NULL, *newp, *end;
! 1183: int len = 0;
! 1184: arg = comma;
! 1185: comma = split(arg);
! 1186:
! 1187: while (arg && *arg)
! 1188: {
! 1189: char *dom = canonicalise_opt(arg);
! 1190: if (!dom)
! 1191: ret_err(_("bad domain in dhcp-option"));
! 1192:
! 1193: newp = opt_malloc(len + strlen(dom) + 2);
! 1194:
! 1195: if (p)
! 1196: {
! 1197: memcpy(newp, p, len);
! 1198: free(p);
! 1199: }
! 1200:
! 1201: p = newp;
! 1202: end = do_rfc1035_name(p + len, dom);
! 1203: *end++ = 0;
! 1204: len = end - p;
! 1205: free(dom);
! 1206:
! 1207: arg = comma;
! 1208: comma = split(arg);
! 1209: }
! 1210:
! 1211: new->val = p;
! 1212: new->len = len;
! 1213: }
! 1214: #endif
! 1215: else
! 1216: {
! 1217: new->len = strlen(comma);
! 1218: /* keep terminating zero on string */
! 1219: new->val = (unsigned char *)opt_string_alloc(comma);
! 1220: new->flags |= DHOPT_STRING;
! 1221: }
! 1222: }
! 1223: }
! 1224:
! 1225: if (!is6 &&
! 1226: ((new->len > 255) ||
! 1227: (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
! 1228: (new->len > 250 && (new->flags & DHOPT_RFC3925))))
! 1229: ret_err(_("dhcp-option too long"));
! 1230:
! 1231: if (flags == DHOPT_MATCH)
! 1232: {
! 1233: if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
! 1234: !new->netid ||
! 1235: new->netid->next)
! 1236: ret_err(_("illegal dhcp-match"));
! 1237:
! 1238: if (is6)
! 1239: {
! 1240: new->next = daemon->dhcp_match6;
! 1241: daemon->dhcp_match6 = new;
! 1242: }
! 1243: else
! 1244: {
! 1245: new->next = daemon->dhcp_match;
! 1246: daemon->dhcp_match = new;
! 1247: }
! 1248: }
! 1249: else if (is6)
! 1250: {
! 1251: new->next = daemon->dhcp_opts6;
! 1252: daemon->dhcp_opts6 = new;
! 1253: }
! 1254: else
! 1255: {
! 1256: new->next = daemon->dhcp_opts;
! 1257: daemon->dhcp_opts = new;
! 1258: }
! 1259:
! 1260: return 1;
! 1261: }
! 1262:
! 1263: #endif
! 1264:
! 1265: void set_option_bool(unsigned int opt)
! 1266: {
! 1267: if (opt < 32)
! 1268: daemon->options |= 1u << opt;
! 1269: else
! 1270: daemon->options2 |= 1u << (opt - 32);
! 1271: }
! 1272:
! 1273: void reset_option_bool(unsigned int opt)
! 1274: {
! 1275: if (opt < 32)
! 1276: daemon->options &= ~(1u << opt);
! 1277: else
! 1278: daemon->options2 &= ~(1u << (opt - 32));
! 1279: }
! 1280:
! 1281: static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line)
! 1282: {
! 1283: int i;
! 1284: char *comma;
! 1285:
! 1286: if (option == '?')
! 1287: ret_err(gen_err);
! 1288:
! 1289: for (i=0; usage[i].opt != 0; i++)
! 1290: if (usage[i].opt == option)
! 1291: {
! 1292: int rept = usage[i].rept;
! 1293:
! 1294: if (command_line)
! 1295: {
! 1296: /* command line */
! 1297: if (rept == ARG_USED_CL)
! 1298: ret_err(_("illegal repeated flag"));
! 1299: if (rept == ARG_ONE)
! 1300: usage[i].rept = ARG_USED_CL;
! 1301: }
! 1302: else
! 1303: {
! 1304: /* allow file to override command line */
! 1305: if (rept == ARG_USED_FILE)
! 1306: ret_err(_("illegal repeated keyword"));
! 1307: if (rept == ARG_USED_CL || rept == ARG_ONE)
! 1308: usage[i].rept = ARG_USED_FILE;
! 1309: }
! 1310:
! 1311: if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
! 1312: {
! 1313: set_option_bool(rept);
! 1314: return 1;
! 1315: }
! 1316:
! 1317: break;
! 1318: }
! 1319:
! 1320: switch (option)
! 1321: {
! 1322: case 'C': /* --conf-file */
! 1323: {
! 1324: char *file = opt_string_alloc(arg);
! 1325: if (file)
! 1326: {
! 1327: one_file(file, 0);
! 1328: free(file);
! 1329: }
! 1330: break;
! 1331: }
! 1332:
! 1333: case '7': /* --conf-dir */
! 1334: {
! 1335: DIR *dir_stream;
! 1336: struct dirent *ent;
! 1337: char *directory, *path;
! 1338: struct list {
! 1339: char *suffix;
! 1340: struct list *next;
! 1341: } *ignore_suffix = NULL, *li;
! 1342:
! 1343: comma = split(arg);
! 1344: if (!(directory = opt_string_alloc(arg)))
! 1345: break;
! 1346:
! 1347: for (arg = comma; arg; arg = comma)
! 1348: {
! 1349: comma = split(arg);
! 1350: li = opt_malloc(sizeof(struct list));
! 1351: li->next = ignore_suffix;
! 1352: ignore_suffix = li;
! 1353: /* Have to copy: buffer is overwritten */
! 1354: li->suffix = opt_string_alloc(arg);
! 1355: };
! 1356:
! 1357: if (!(dir_stream = opendir(directory)))
! 1358: die(_("cannot access directory %s: %s"), directory, EC_FILE);
! 1359:
! 1360: while ((ent = readdir(dir_stream)))
! 1361: {
! 1362: size_t len = strlen(ent->d_name);
! 1363: struct stat buf;
! 1364:
! 1365: /* ignore emacs backups and dotfiles */
! 1366: if (len == 0 ||
! 1367: ent->d_name[len - 1] == '~' ||
! 1368: (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
! 1369: ent->d_name[0] == '.')
! 1370: continue;
! 1371:
! 1372: for (li = ignore_suffix; li; li = li->next)
! 1373: {
! 1374: /* check for proscribed suffices */
! 1375: size_t ls = strlen(li->suffix);
! 1376: if (len > ls &&
! 1377: strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
! 1378: break;
! 1379: }
! 1380: if (li)
! 1381: continue;
! 1382:
! 1383: path = opt_malloc(strlen(directory) + len + 2);
! 1384: strcpy(path, directory);
! 1385: strcat(path, "/");
! 1386: strcat(path, ent->d_name);
! 1387:
! 1388: /* files must be readable */
! 1389: if (stat(path, &buf) == -1)
! 1390: die(_("cannot access %s: %s"), path, EC_FILE);
! 1391:
! 1392: /* only reg files allowed. */
! 1393: if (S_ISREG(buf.st_mode))
! 1394: one_file(path, 0);
! 1395:
! 1396: free(path);
! 1397: }
! 1398:
! 1399: closedir(dir_stream);
! 1400: free(directory);
! 1401: for(; ignore_suffix; ignore_suffix = li)
! 1402: {
! 1403: li = ignore_suffix->next;
! 1404: free(ignore_suffix->suffix);
! 1405: free(ignore_suffix);
! 1406: }
! 1407:
! 1408: break;
! 1409: }
! 1410:
! 1411: case '1': /* --enable-dbus */
! 1412: set_option_bool(OPT_DBUS);
! 1413: if (arg)
! 1414: daemon->dbus_name = opt_string_alloc(arg);
! 1415: else
! 1416: daemon->dbus_name = DNSMASQ_SERVICE;
! 1417: break;
! 1418:
! 1419: case '8': /* --log-facility */
! 1420: /* may be a filename */
! 1421: if (strchr(arg, '/') || strcmp (arg, "-") == 0)
! 1422: daemon->log_file = opt_string_alloc(arg);
! 1423: else
! 1424: {
! 1425: #ifdef __ANDROID__
! 1426: ret_err(_("setting log facility is not possible under Android"));
! 1427: #else
! 1428: for (i = 0; facilitynames[i].c_name; i++)
! 1429: if (hostname_isequal((char *)facilitynames[i].c_name, arg))
! 1430: break;
! 1431:
! 1432: if (facilitynames[i].c_name)
! 1433: daemon->log_fac = facilitynames[i].c_val;
! 1434: else
! 1435: ret_err(_("bad log facility"));
! 1436: #endif
! 1437: }
! 1438: break;
! 1439:
! 1440: case 'x': /* --pid-file */
! 1441: daemon->runfile = opt_string_alloc(arg);
! 1442: break;
! 1443:
! 1444: case 'r': /* --resolv-file */
! 1445: {
! 1446: char *name = opt_string_alloc(arg);
! 1447: struct resolvc *new, *list = daemon->resolv_files;
! 1448:
! 1449: if (list && list->is_default)
! 1450: {
! 1451: /* replace default resolv file - possibly with nothing */
! 1452: if (name)
! 1453: {
! 1454: list->is_default = 0;
! 1455: list->name = name;
! 1456: }
! 1457: else
! 1458: list = NULL;
! 1459: }
! 1460: else if (name)
! 1461: {
! 1462: new = opt_malloc(sizeof(struct resolvc));
! 1463: new->next = list;
! 1464: new->name = name;
! 1465: new->is_default = 0;
! 1466: new->mtime = 0;
! 1467: new->logged = 0;
! 1468: list = new;
! 1469: }
! 1470: daemon->resolv_files = list;
! 1471: break;
! 1472: }
! 1473:
! 1474: case 'm': /* --mx-host */
! 1475: {
! 1476: int pref = 1;
! 1477: struct mx_srv_record *new;
! 1478: char *name, *target = NULL;
! 1479:
! 1480: if ((comma = split(arg)))
! 1481: {
! 1482: char *prefstr;
! 1483: if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
! 1484: ret_err(_("bad MX preference"));
! 1485: }
! 1486:
! 1487: if (!(name = canonicalise_opt(arg)) ||
! 1488: (comma && !(target = canonicalise_opt(comma))))
! 1489: ret_err(_("bad MX name"));
! 1490:
! 1491: new = opt_malloc(sizeof(struct mx_srv_record));
! 1492: new->next = daemon->mxnames;
! 1493: daemon->mxnames = new;
! 1494: new->issrv = 0;
! 1495: new->name = name;
! 1496: new->target = target; /* may be NULL */
! 1497: new->weight = pref;
! 1498: break;
! 1499: }
! 1500:
! 1501: case 't': /* --mx-target */
! 1502: if (!(daemon->mxtarget = canonicalise_opt(arg)))
! 1503: ret_err(_("bad MX target"));
! 1504: break;
! 1505:
! 1506: #ifdef HAVE_DHCP
! 1507: case 'l': /* --dhcp-leasefile */
! 1508: daemon->lease_file = opt_string_alloc(arg);
! 1509: break;
! 1510:
! 1511: /* Sorry about the gross pre-processor abuse */
! 1512: case '6': /* --dhcp-script */
! 1513: case LOPT_LUASCRIPT: /* --dhcp-luascript */
! 1514: # if defined(NO_FORK)
! 1515: ret_err(_("cannot run scripts under uClinux"));
! 1516: # elif !defined(HAVE_SCRIPT)
! 1517: ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
! 1518: # else
! 1519: if (option == LOPT_LUASCRIPT)
! 1520: # if !defined(HAVE_LUASCRIPT)
! 1521: ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
! 1522: # else
! 1523: daemon->luascript = opt_string_alloc(arg);
! 1524: # endif
! 1525: else
! 1526: daemon->lease_change_command = opt_string_alloc(arg);
! 1527: # endif
! 1528: break;
! 1529: #endif /* HAVE_DHCP */
! 1530:
! 1531: case LOPT_DHCP_HOST: /* --dhcp-hostfile */
! 1532: case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
! 1533: case 'H': /* --addn-hosts */
! 1534: {
! 1535: struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
! 1536: static int hosts_index = 1;
! 1537: new->fname = opt_string_alloc(arg);
! 1538: new->index = hosts_index++;
! 1539: new->flags = 0;
! 1540: if (option == 'H')
! 1541: {
! 1542: new->next = daemon->addn_hosts;
! 1543: daemon->addn_hosts = new;
! 1544: }
! 1545: else if (option == LOPT_DHCP_HOST)
! 1546: {
! 1547: new->next = daemon->dhcp_hosts_file;
! 1548: daemon->dhcp_hosts_file = new;
! 1549: }
! 1550: else if (option == LOPT_DHCP_OPTS)
! 1551: {
! 1552: new->next = daemon->dhcp_opts_file;
! 1553: daemon->dhcp_opts_file = new;
! 1554: }
! 1555: break;
! 1556: }
! 1557:
! 1558: case LOPT_AUTHSERV: /* --auth-server */
! 1559: if (!(comma = split(arg)))
! 1560: ret_err(gen_err);
! 1561:
! 1562: daemon->authserver = opt_string_alloc(arg);
! 1563: arg = comma;
! 1564: do {
! 1565: struct iname *new = opt_malloc(sizeof(struct iname));
! 1566: comma = split(arg);
! 1567: new->name = NULL;
! 1568: unhide_metas(arg);
! 1569: if ((new->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
! 1570: new->addr.sa.sa_family = AF_INET;
! 1571: #ifdef HAVE_IPV6
! 1572: else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
! 1573: new->addr.sa.sa_family = AF_INET6;
! 1574: #endif
! 1575: else
! 1576: new->name = opt_string_alloc(arg);
! 1577:
! 1578: new->next = daemon->authinterface;
! 1579: daemon->authinterface = new;
! 1580:
! 1581: arg = comma;
! 1582: } while (arg);
! 1583:
! 1584: break;
! 1585:
! 1586: case LOPT_AUTHSFS: /* --auth-sec-servers */
! 1587: {
! 1588: struct name_list *new;
! 1589:
! 1590: do {
! 1591: comma = split(arg);
! 1592: new = opt_malloc(sizeof(struct name_list));
! 1593: new->name = opt_string_alloc(arg);
! 1594: new->next = daemon->secondary_forward_server;
! 1595: daemon->secondary_forward_server = new;
! 1596: arg = comma;
! 1597: } while (arg);
! 1598: break;
! 1599: }
! 1600:
! 1601: case LOPT_AUTHZONE: /* --auth-zone */
! 1602: {
! 1603: struct auth_zone *new;
! 1604:
! 1605: comma = split(arg);
! 1606:
! 1607: new = opt_malloc(sizeof(struct auth_zone));
! 1608: new->domain = opt_string_alloc(arg);
! 1609: new->subnet = NULL;
! 1610: new->next = daemon->auth_zones;
! 1611: daemon->auth_zones = new;
! 1612:
! 1613: while ((arg = comma))
! 1614: {
! 1615: int prefixlen = 0;
! 1616: char *prefix;
! 1617: struct subnet *subnet = opt_malloc(sizeof(struct subnet));
! 1618:
! 1619: subnet->next = new->subnet;
! 1620: new->subnet = subnet;
! 1621:
! 1622: comma = split(arg);
! 1623: prefix = split_chr(arg, '/');
! 1624:
! 1625: if (prefix && !atoi_check(prefix, &prefixlen))
! 1626: ret_err(gen_err);
! 1627:
! 1628: if (inet_pton(AF_INET, arg, &subnet->addr4))
! 1629: {
! 1630: if ((prefixlen & 0x07) != 0 || prefixlen > 24)
! 1631: ret_err(_("bad prefix"));
! 1632: subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
! 1633: subnet->is6 = 0;
! 1634: }
! 1635: #ifdef HAVE_IPV6
! 1636: else if (inet_pton(AF_INET6, arg, &subnet->addr6))
! 1637: {
! 1638: subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
! 1639: subnet->is6 = 1;
! 1640: }
! 1641: #endif
! 1642: else
! 1643: ret_err(gen_err);
! 1644: }
! 1645: break;
! 1646: }
! 1647:
! 1648: case LOPT_AUTHSOA: /* --auth-soa */
! 1649: comma = split(arg);
! 1650: atoi_check(arg, (int *)&daemon->soa_sn);
! 1651: if (comma)
! 1652: {
! 1653: char *cp;
! 1654: arg = comma;
! 1655: comma = split(arg);
! 1656: daemon->hostmaster = opt_string_alloc(arg);
! 1657: for (cp = daemon->hostmaster; *cp; cp++)
! 1658: if (*cp == '@')
! 1659: *cp = '.';
! 1660:
! 1661: if (comma)
! 1662: {
! 1663: arg = comma;
! 1664: comma = split(arg);
! 1665: atoi_check(arg, (int *)&daemon->soa_refresh);
! 1666: if (comma)
! 1667: {
! 1668: arg = comma;
! 1669: comma = split(arg);
! 1670: atoi_check(arg, (int *)&daemon->soa_retry);
! 1671: if (comma)
! 1672: {
! 1673: arg = comma;
! 1674: comma = split(arg);
! 1675: atoi_check(arg, (int *)&daemon->soa_expiry);
! 1676: }
! 1677: }
! 1678: }
! 1679: }
! 1680:
! 1681: break;
! 1682:
! 1683: case 's': /* --domain */
! 1684: if (strcmp (arg, "#") == 0)
! 1685: set_option_bool(OPT_RESOLV_DOMAIN);
! 1686: else
! 1687: {
! 1688: char *d;
! 1689: comma = split(arg);
! 1690: if (!(d = canonicalise_opt(arg)))
! 1691: ret_err(gen_err);
! 1692: else
! 1693: {
! 1694: if (comma)
! 1695: {
! 1696: struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
! 1697: char *netpart;
! 1698:
! 1699: unhide_metas(comma);
! 1700: if ((netpart = split_chr(comma, '/')))
! 1701: {
! 1702: int msize;
! 1703:
! 1704: arg = split(netpart);
! 1705: if (!atoi_check(netpart, &msize))
! 1706: ret_err(gen_err);
! 1707: else if (inet_pton(AF_INET, comma, &new->start))
! 1708: {
! 1709: int mask = (1 << (32 - msize)) - 1;
! 1710: new->is6 = 0;
! 1711: new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
! 1712: new->end.s_addr = new->start.s_addr | htonl(mask);
! 1713: if (arg)
! 1714: {
! 1715: /* generate the equivalent of
! 1716: local=/<domain>/
! 1717: local=/xxx.yyy.zzz.in-addr.arpa/ */
! 1718:
! 1719: if (strcmp(arg, "local") != 0 ||
! 1720: (msize != 8 && msize != 16 && msize != 24))
! 1721: ret_err(gen_err);
! 1722: else
! 1723: {
! 1724: struct server *serv = opt_malloc(sizeof(struct server));
! 1725: in_addr_t a = ntohl(new->start.s_addr) >> 8;
! 1726: char *p;
! 1727:
! 1728: memset(serv, 0, sizeof(struct server));
! 1729: serv->domain = d;
! 1730: serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
! 1731: serv->next = daemon->servers;
! 1732: daemon->servers = serv;
! 1733:
! 1734: serv = opt_malloc(sizeof(struct server));
! 1735: memset(serv, 0, sizeof(struct server));
! 1736: p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */
! 1737:
! 1738: if (msize == 24)
! 1739: p += sprintf(p, "%d.", a & 0xff);
! 1740: a = a >> 8;
! 1741: if (msize != 8)
! 1742: p += sprintf(p, "%d.", a & 0xff);
! 1743: a = a >> 8;
! 1744: p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
! 1745:
! 1746: serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
! 1747: serv->next = daemon->servers;
! 1748: daemon->servers = serv;
! 1749: }
! 1750: }
! 1751: }
! 1752: #ifdef HAVE_IPV6
! 1753: else if (inet_pton(AF_INET6, comma, &new->start6))
! 1754: {
! 1755: u64 mask = (1LLU << (128 - msize)) - 1LLU;
! 1756: u64 addrpart = addr6part(&new->start6);
! 1757: new->is6 = 1;
! 1758:
! 1759: /* prefix==64 overflows the mask calculation above */
! 1760: if (msize == 64)
! 1761: mask = (u64)-1LL;
! 1762:
! 1763: new->end6 = new->start6;
! 1764: setaddr6part(&new->start6, addrpart & ~mask);
! 1765: setaddr6part(&new->end6, addrpart | mask);
! 1766:
! 1767: if (msize < 64)
! 1768: ret_err(gen_err);
! 1769: else if (arg)
! 1770: {
! 1771: /* generate the equivalent of
! 1772: local=/<domain>/
! 1773: local=/xxx.yyy.zzz.ip6.arpa/ */
! 1774:
! 1775: if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
! 1776: ret_err(gen_err);
! 1777: else
! 1778: {
! 1779: struct server *serv = opt_malloc(sizeof(struct server));
! 1780: char *p;
! 1781:
! 1782: memset(serv, 0, sizeof(struct server));
! 1783: serv->domain = d;
! 1784: serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
! 1785: serv->next = daemon->servers;
! 1786: daemon->servers = serv;
! 1787:
! 1788: serv = opt_malloc(sizeof(struct server));
! 1789: memset(serv, 0, sizeof(struct server));
! 1790: p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
! 1791:
! 1792: for (i = msize-1; i >= 0; i -= 4)
! 1793: {
! 1794: int dig = ((unsigned char *)&new->start6)[i>>3];
! 1795: p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
! 1796: }
! 1797: p += sprintf(p, "ip6.arpa");
! 1798:
! 1799: serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
! 1800: serv->next = daemon->servers;
! 1801: daemon->servers = serv;
! 1802: }
! 1803: }
! 1804: }
! 1805: #endif
! 1806: else
! 1807: ret_err(gen_err);
! 1808: }
! 1809: else
! 1810: {
! 1811: arg = split(comma);
! 1812: if (inet_pton(AF_INET, comma, &new->start))
! 1813: {
! 1814: new->is6 = 0;
! 1815: if (!arg)
! 1816: new->end.s_addr = new->start.s_addr;
! 1817: else if (!inet_pton(AF_INET, arg, &new->end))
! 1818: ret_err(gen_err);
! 1819: }
! 1820: #ifdef HAVE_IPV6
! 1821: else if (inet_pton(AF_INET6, comma, &new->start6))
! 1822: {
! 1823: new->is6 = 1;
! 1824: if (!arg)
! 1825: memcpy(&new->end6, &new->start6, IN6ADDRSZ);
! 1826: else if (!inet_pton(AF_INET6, arg, &new->end6))
! 1827: ret_err(gen_err);
! 1828: }
! 1829: #endif
! 1830: else
! 1831: ret_err(gen_err);
! 1832: }
! 1833:
! 1834: new->domain = d;
! 1835: new->next = daemon->cond_domain;
! 1836: daemon->cond_domain = new;
! 1837: }
! 1838: else
! 1839: daemon->domain_suffix = d;
! 1840: }
! 1841: }
! 1842: break;
! 1843:
! 1844: case 'u': /* --user */
! 1845: daemon->username = opt_string_alloc(arg);
! 1846: break;
! 1847:
! 1848: case 'g': /* --group */
! 1849: daemon->groupname = opt_string_alloc(arg);
! 1850: daemon->group_set = 1;
! 1851: break;
! 1852:
! 1853: #ifdef HAVE_DHCP
! 1854: case LOPT_SCRIPTUSR: /* --scriptuser */
! 1855: daemon->scriptuser = opt_string_alloc(arg);
! 1856: break;
! 1857: #endif
! 1858:
! 1859: case 'i': /* --interface */
! 1860: do {
! 1861: struct iname *new = opt_malloc(sizeof(struct iname));
! 1862: comma = split(arg);
! 1863: new->next = daemon->if_names;
! 1864: daemon->if_names = new;
! 1865: /* new->name may be NULL if someone does
! 1866: "interface=" to disable all interfaces except loop. */
! 1867: new->name = opt_string_alloc(arg);
! 1868: new->used = 0;
! 1869: arg = comma;
! 1870: } while (arg);
! 1871: break;
! 1872:
! 1873: case 'I': /* --except-interface */
! 1874: case '2': /* --no-dhcp-interface */
! 1875: do {
! 1876: struct iname *new = opt_malloc(sizeof(struct iname));
! 1877: comma = split(arg);
! 1878: new->name = opt_string_alloc(arg);
! 1879: if (option == 'I')
! 1880: {
! 1881: new->next = daemon->if_except;
! 1882: daemon->if_except = new;
! 1883: }
! 1884: else
! 1885: {
! 1886: new->next = daemon->dhcp_except;
! 1887: daemon->dhcp_except = new;
! 1888: }
! 1889: arg = comma;
! 1890: } while (arg);
! 1891: break;
! 1892:
! 1893: case 'B': /* --bogus-nxdomain */
! 1894: {
! 1895: struct in_addr addr;
! 1896: unhide_metas(arg);
! 1897: if (arg && (addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
! 1898: {
! 1899: struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
! 1900: baddr->next = daemon->bogus_addr;
! 1901: daemon->bogus_addr = baddr;
! 1902: baddr->addr = addr;
! 1903: }
! 1904: else
! 1905: ret_err(gen_err); /* error */
! 1906: break;
! 1907: }
! 1908:
! 1909: case 'a': /* --listen-address */
! 1910: case LOPT_AUTHPEER: /* --auth-peer */
! 1911: do {
! 1912: struct iname *new = opt_malloc(sizeof(struct iname));
! 1913: comma = split(arg);
! 1914: unhide_metas(arg);
! 1915: if (arg && (new->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
! 1916: {
! 1917: new->addr.sa.sa_family = AF_INET;
! 1918: new->addr.in.sin_port = 0;
! 1919: #ifdef HAVE_SOCKADDR_SA_LEN
! 1920: new->addr.in.sin_len = sizeof(new->addr.in);
! 1921: #endif
! 1922: }
! 1923: #ifdef HAVE_IPV6
! 1924: else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
! 1925: {
! 1926: new->addr.sa.sa_family = AF_INET6;
! 1927: new->addr.in6.sin6_flowinfo = 0;
! 1928: new->addr.in6.sin6_scope_id = 0;
! 1929: new->addr.in6.sin6_port = 0;
! 1930: #ifdef HAVE_SOCKADDR_SA_LEN
! 1931: new->addr.in6.sin6_len = sizeof(new->addr.in6);
! 1932: #endif
! 1933: }
! 1934: #endif
! 1935: else
! 1936: ret_err(gen_err);
! 1937:
! 1938: new->used = 0;
! 1939: if (option == 'a')
! 1940: {
! 1941: new->next = daemon->if_addrs;
! 1942: daemon->if_addrs = new;
! 1943: }
! 1944: else
! 1945: {
! 1946: new->next = daemon->auth_peers;
! 1947: daemon->auth_peers = new;
! 1948: }
! 1949: arg = comma;
! 1950: } while (arg);
! 1951: break;
! 1952:
! 1953: case 'S': /* --server */
! 1954: case LOPT_LOCAL: /* --local */
! 1955: case 'A': /* --address */
! 1956: case LOPT_NO_REBIND: /* --rebind-domain-ok */
! 1957: {
! 1958: struct server *serv, *newlist = NULL;
! 1959:
! 1960: unhide_metas(arg);
! 1961:
! 1962: if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
! 1963: {
! 1964: int rebind = !(*arg == '/');
! 1965: char *end = NULL;
! 1966: if (!rebind)
! 1967: arg++;
! 1968: while (rebind || (end = split_chr(arg, '/')))
! 1969: {
! 1970: char *domain = NULL;
! 1971: /* elide leading dots - they are implied in the search algorithm */
! 1972: while (*arg == '.') arg++;
! 1973: /* # matches everything and becomes a zero length domain string */
! 1974: if (strcmp(arg, "#") == 0)
! 1975: domain = "";
! 1976: else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
! 1977: option = '?';
! 1978: serv = opt_malloc(sizeof(struct server));
! 1979: memset(serv, 0, sizeof(struct server));
! 1980: serv->next = newlist;
! 1981: newlist = serv;
! 1982: serv->domain = domain;
! 1983: serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
! 1984: arg = end;
! 1985: if (rebind)
! 1986: break;
! 1987: }
! 1988: if (!newlist)
! 1989: ret_err(gen_err);
! 1990: }
! 1991: else
! 1992: {
! 1993: newlist = opt_malloc(sizeof(struct server));
! 1994: memset(newlist, 0, sizeof(struct server));
! 1995: }
! 1996:
! 1997: if (option == 'A')
! 1998: {
! 1999: newlist->flags |= SERV_LITERAL_ADDRESS;
! 2000: if (!(newlist->flags & SERV_TYPE))
! 2001: ret_err(gen_err);
! 2002: }
! 2003: else if (option == LOPT_NO_REBIND)
! 2004: newlist->flags |= SERV_NO_REBIND;
! 2005:
! 2006: if (!arg || !*arg)
! 2007: {
! 2008: if (!(newlist->flags & SERV_NO_REBIND))
! 2009: newlist->flags |= SERV_NO_ADDR; /* no server */
! 2010: if (newlist->flags & SERV_LITERAL_ADDRESS)
! 2011: ret_err(gen_err);
! 2012: }
! 2013:
! 2014: else if (strcmp(arg, "#") == 0)
! 2015: {
! 2016: newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
! 2017: if (newlist->flags & SERV_LITERAL_ADDRESS)
! 2018: ret_err(gen_err);
! 2019: }
! 2020: else
! 2021: {
! 2022: char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
! 2023: if (err)
! 2024: ret_err(err);
! 2025: }
! 2026:
! 2027: serv = newlist;
! 2028: while (serv->next)
! 2029: {
! 2030: serv->next->flags = serv->flags;
! 2031: serv->next->addr = serv->addr;
! 2032: serv->next->source_addr = serv->source_addr;
! 2033: strcpy(serv->next->interface, serv->interface);
! 2034: serv = serv->next;
! 2035: }
! 2036: serv->next = daemon->servers;
! 2037: daemon->servers = newlist;
! 2038: break;
! 2039: }
! 2040:
! 2041: case LOPT_IPSET: /* --ipset */
! 2042: #ifndef HAVE_IPSET
! 2043: ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
! 2044: break;
! 2045: #else
! 2046: {
! 2047: struct ipsets ipsets_head;
! 2048: struct ipsets *ipsets = &ipsets_head;
! 2049: int size;
! 2050: char *end;
! 2051: char **sets, **sets_pos;
! 2052: memset(ipsets, 0, sizeof(struct ipsets));
! 2053: unhide_metas(arg);
! 2054: if (arg && *arg == '/')
! 2055: {
! 2056: arg++;
! 2057: while ((end = split_chr(arg, '/')))
! 2058: {
! 2059: char *domain = NULL;
! 2060: /* elide leading dots - they are implied in the search algorithm */
! 2061: while (*arg == '.')
! 2062: arg++;
! 2063: /* # matches everything and becomes a zero length domain string */
! 2064: if (strcmp(arg, "#") == 0 || !*arg)
! 2065: domain = "";
! 2066: else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
! 2067: option = '?';
! 2068: ipsets->next = opt_malloc(sizeof(struct ipsets));
! 2069: ipsets = ipsets->next;
! 2070: memset(ipsets, 0, sizeof(struct ipsets));
! 2071: ipsets->domain = domain;
! 2072: arg = end;
! 2073: }
! 2074: }
! 2075: else
! 2076: {
! 2077: ipsets->next = opt_malloc(sizeof(struct ipsets));
! 2078: ipsets = ipsets->next;
! 2079: memset(ipsets, 0, sizeof(struct ipsets));
! 2080: ipsets->domain = "";
! 2081: }
! 2082: if (!arg || !*arg)
! 2083: {
! 2084: option = '?';
! 2085: break;
! 2086: }
! 2087: size = 2;
! 2088: for (end = arg; *end; ++end)
! 2089: if (*end == ',')
! 2090: ++size;
! 2091:
! 2092: sets = sets_pos = opt_malloc(sizeof(char *) * size);
! 2093:
! 2094: do {
! 2095: end = split(arg);
! 2096: *sets_pos++ = opt_string_alloc(arg);
! 2097: arg = end;
! 2098: } while (end);
! 2099: *sets_pos = 0;
! 2100: for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
! 2101: ipsets->next->sets = sets;
! 2102: ipsets->next = daemon->ipsets;
! 2103: daemon->ipsets = ipsets_head.next;
! 2104:
! 2105: break;
! 2106: }
! 2107: #endif
! 2108:
! 2109: case 'c': /* --cache-size */
! 2110: {
! 2111: int size;
! 2112:
! 2113: if (!atoi_check(arg, &size))
! 2114: ret_err(gen_err);
! 2115: else
! 2116: {
! 2117: /* zero is OK, and means no caching. */
! 2118:
! 2119: if (size < 0)
! 2120: size = 0;
! 2121: else if (size > 10000)
! 2122: size = 10000;
! 2123:
! 2124: daemon->cachesize = size;
! 2125: }
! 2126: break;
! 2127: }
! 2128:
! 2129: case 'p': /* --port */
! 2130: if (!atoi_check16(arg, &daemon->port))
! 2131: ret_err(gen_err);
! 2132: break;
! 2133:
! 2134: case LOPT_MINPORT: /* --min-port */
! 2135: if (!atoi_check16(arg, &daemon->min_port))
! 2136: ret_err(gen_err);
! 2137: break;
! 2138:
! 2139: case '0': /* --dns-forward-max */
! 2140: if (!atoi_check(arg, &daemon->ftabsize))
! 2141: ret_err(gen_err);
! 2142: break;
! 2143:
! 2144: case LOPT_MAX_LOGS: /* --log-async */
! 2145: daemon->max_logs = LOG_MAX; /* default */
! 2146: if (arg && !atoi_check(arg, &daemon->max_logs))
! 2147: ret_err(gen_err);
! 2148: else if (daemon->max_logs > 100)
! 2149: daemon->max_logs = 100;
! 2150: break;
! 2151:
! 2152: case 'P': /* --edns-packet-max */
! 2153: {
! 2154: int i;
! 2155: if (!atoi_check(arg, &i))
! 2156: ret_err(gen_err);
! 2157: daemon->edns_pktsz = (unsigned short)i;
! 2158: break;
! 2159: }
! 2160:
! 2161: case 'Q': /* --query-port */
! 2162: if (!atoi_check16(arg, &daemon->query_port))
! 2163: ret_err(gen_err);
! 2164: /* if explicitly set to zero, use single OS ephemeral port
! 2165: and disable random ports */
! 2166: if (daemon->query_port == 0)
! 2167: daemon->osport = 1;
! 2168: break;
! 2169:
! 2170: case 'T': /* --local-ttl */
! 2171: case LOPT_NEGTTL: /* --neg-ttl */
! 2172: case LOPT_MAXTTL: /* --max-ttl */
! 2173: case LOPT_MAXCTTL: /* --max-cache-ttl */
! 2174: case LOPT_AUTHTTL: /* --auth-ttl */
! 2175: {
! 2176: int ttl;
! 2177: if (!atoi_check(arg, &ttl))
! 2178: ret_err(gen_err);
! 2179: else if (option == LOPT_NEGTTL)
! 2180: daemon->neg_ttl = (unsigned long)ttl;
! 2181: else if (option == LOPT_MAXTTL)
! 2182: daemon->max_ttl = (unsigned long)ttl;
! 2183: else if (option == LOPT_MAXCTTL)
! 2184: daemon->max_cache_ttl = (unsigned long)ttl;
! 2185: else if (option == LOPT_AUTHTTL)
! 2186: daemon->auth_ttl = (unsigned long)ttl;
! 2187: else
! 2188: daemon->local_ttl = (unsigned long)ttl;
! 2189: break;
! 2190: }
! 2191:
! 2192: #ifdef HAVE_DHCP
! 2193: case 'X': /* --dhcp-lease-max */
! 2194: if (!atoi_check(arg, &daemon->dhcp_max))
! 2195: ret_err(gen_err);
! 2196: break;
! 2197: #endif
! 2198:
! 2199: #ifdef HAVE_TFTP
! 2200: case LOPT_TFTP_MAX: /* --tftp-max */
! 2201: if (!atoi_check(arg, &daemon->tftp_max))
! 2202: ret_err(gen_err);
! 2203: break;
! 2204:
! 2205: case LOPT_PREFIX: /* --tftp-prefix */
! 2206: comma = split(arg);
! 2207: if (comma)
! 2208: {
! 2209: struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
! 2210: new->interface = opt_string_alloc(comma);
! 2211: new->prefix = opt_string_alloc(arg);
! 2212: new->next = daemon->if_prefix;
! 2213: daemon->if_prefix = new;
! 2214: }
! 2215: else
! 2216: daemon->tftp_prefix = opt_string_alloc(arg);
! 2217: break;
! 2218:
! 2219: case LOPT_TFTPPORTS: /* --tftp-port-range */
! 2220: if (!(comma = split(arg)) ||
! 2221: !atoi_check16(arg, &daemon->start_tftp_port) ||
! 2222: !atoi_check16(comma, &daemon->end_tftp_port))
! 2223: ret_err(_("bad port range"));
! 2224:
! 2225: if (daemon->start_tftp_port > daemon->end_tftp_port)
! 2226: {
! 2227: int tmp = daemon->start_tftp_port;
! 2228: daemon->start_tftp_port = daemon->end_tftp_port;
! 2229: daemon->end_tftp_port = tmp;
! 2230: }
! 2231:
! 2232: break;
! 2233: #endif
! 2234:
! 2235: case LOPT_BRIDGE: /* --bridge-interface */
! 2236: {
! 2237: struct dhcp_bridge *new = opt_malloc(sizeof(struct dhcp_bridge));
! 2238: if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
! 2239: ret_err(_("bad bridge-interface"));
! 2240:
! 2241: strcpy(new->iface, arg);
! 2242: new->alias = NULL;
! 2243: new->next = daemon->bridges;
! 2244: daemon->bridges = new;
! 2245:
! 2246: do {
! 2247: arg = comma;
! 2248: comma = split(arg);
! 2249: if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
! 2250: {
! 2251: struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
! 2252: b->next = new->alias;
! 2253: new->alias = b;
! 2254: strcpy(b->iface, arg);
! 2255: }
! 2256: } while (comma);
! 2257:
! 2258: break;
! 2259: }
! 2260:
! 2261: #ifdef HAVE_DHCP
! 2262: case 'F': /* --dhcp-range */
! 2263: {
! 2264: int k, leasepos = 2;
! 2265: char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
! 2266: struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
! 2267:
! 2268: memset (new, 0, sizeof(*new));
! 2269: new->lease_time = DEFLEASE;
! 2270:
! 2271: if (!arg)
! 2272: {
! 2273: option = '?';
! 2274: break;
! 2275: }
! 2276:
! 2277: while(1)
! 2278: {
! 2279: for (cp = arg; *cp; cp++)
! 2280: if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
! 2281: (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
! 2282: (*cp >='0' && *cp <= '9')))
! 2283: break;
! 2284:
! 2285: if (*cp != ',' && (comma = split(arg)))
! 2286: {
! 2287: if (is_tag_prefix(arg))
! 2288: {
! 2289: struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
! 2290: tt->net = opt_string_alloc(arg+4);
! 2291: tt->next = new->filter;
! 2292: new->filter = tt;
! 2293: }
! 2294: else
! 2295: {
! 2296: if (new->netid.net)
! 2297: ret_err(_("only one tag allowed"));
! 2298: else if (strstr(arg, "set:") == arg)
! 2299: new->netid.net = opt_string_alloc(arg+4);
! 2300: else
! 2301: new->netid.net = opt_string_alloc(arg);
! 2302: }
! 2303: arg = comma;
! 2304: }
! 2305: else
! 2306: {
! 2307: a[0] = arg;
! 2308: break;
! 2309: }
! 2310: }
! 2311:
! 2312: for (k = 1; k < 8; k++)
! 2313: if (!(a[k] = split(a[k-1])))
! 2314: break;
! 2315:
! 2316: if (k < 2)
! 2317: ret_err(_("bad dhcp-range"));
! 2318:
! 2319: if (inet_pton(AF_INET, a[0], &new->start))
! 2320: {
! 2321: new->next = daemon->dhcp;
! 2322: daemon->dhcp = new;
! 2323: new->end = new->start;
! 2324: if (strcmp(a[1], "static") == 0)
! 2325: new->flags |= CONTEXT_STATIC;
! 2326: else if (strcmp(a[1], "proxy") == 0)
! 2327: new->flags |= CONTEXT_PROXY;
! 2328: else if (!inet_pton(AF_INET, a[1], &new->end))
! 2329: ret_err(_("bad dhcp-range"));
! 2330:
! 2331: if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
! 2332: {
! 2333: struct in_addr tmp = new->start;
! 2334: new->start = new->end;
! 2335: new->end = tmp;
! 2336: }
! 2337:
! 2338: if (k >= 3 && strchr(a[2], '.') &&
! 2339: ((new->netmask.s_addr = inet_addr(a[2])) != (in_addr_t)-1))
! 2340: {
! 2341: new->flags |= CONTEXT_NETMASK;
! 2342: leasepos = 3;
! 2343: if (!is_same_net(new->start, new->end, new->netmask))
! 2344: ret_err(_("inconsistent DHCP range"));
! 2345: }
! 2346:
! 2347: if (k >= 4 && strchr(a[3], '.') &&
! 2348: ((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t)-1))
! 2349: {
! 2350: new->flags |= CONTEXT_BRDCAST;
! 2351: leasepos = 4;
! 2352: }
! 2353: }
! 2354: #ifdef HAVE_DHCP6
! 2355: else if (inet_pton(AF_INET6, a[0], &new->start6))
! 2356: {
! 2357: new->prefix = 64; /* default */
! 2358: new->end6 = new->start6;
! 2359:
! 2360: /* dhcp-range=:: enables DHCP stateless on any interface */
! 2361: if (IN6_IS_ADDR_UNSPECIFIED(&new->start6))
! 2362: new->prefix = 0;
! 2363:
! 2364: for (leasepos = 1; leasepos < k; leasepos++)
! 2365: {
! 2366: if (strcmp(a[leasepos], "static") == 0)
! 2367: new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
! 2368: else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
! 2369: new->flags |= CONTEXT_RA_ONLY | CONTEXT_RA;
! 2370: else if (strcmp(a[leasepos], "ra-names") == 0)
! 2371: new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
! 2372: else if (strcmp(a[leasepos], "ra-stateless") == 0)
! 2373: new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
! 2374: else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
! 2375: new->flags |= CONTEXT_DHCP;
! 2376: else if (strstr(a[leasepos], "constructor:") == a[leasepos])
! 2377: {
! 2378: new->template_interface = opt_string_alloc(a[leasepos] + 12);
! 2379: new->flags |= CONTEXT_TEMPLATE;
! 2380: }
! 2381: else
! 2382: break;
! 2383: }
! 2384:
! 2385: new->next = daemon->dhcp6;
! 2386: daemon->dhcp6 = new;
! 2387:
! 2388: /* bare integer < 128 is prefix value */
! 2389: if (leasepos < k)
! 2390: {
! 2391: int pref;
! 2392: for (cp = a[leasepos]; *cp; cp++)
! 2393: if (!(*cp >= '0' && *cp <= '9'))
! 2394: break;
! 2395: if (!*cp && (pref = atoi(a[leasepos])) <= 128)
! 2396: {
! 2397: new->prefix = pref;
! 2398: leasepos++;
! 2399: if (new->prefix != 64)
! 2400: {
! 2401: if ((new->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
! 2402: ret_err(_("prefix must be exactly 64 for RA subnets"));
! 2403: else if (new->template_interface)
! 2404: ret_err(_("prefix must be exactly 64 for subnet constructors"));
! 2405: }
! 2406: if (new->prefix < 64)
! 2407: ret_err(_("prefix must be at least 64"));
! 2408: }
! 2409: }
! 2410:
! 2411: if (!is_same_net6(&new->start6, &new->end6, new->prefix))
! 2412: ret_err(_("inconsistent DHCPv6 range"));
! 2413: if (addr6part(&new->start6) > addr6part(&new->end6))
! 2414: {
! 2415: struct in6_addr tmp = new->start6;
! 2416: new->start6 = new->end6;
! 2417: new->end6 = tmp;
! 2418: }
! 2419: }
! 2420: #endif
! 2421: else
! 2422: ret_err(_("bad dhcp-range"));
! 2423:
! 2424: if (leasepos < k)
! 2425: {
! 2426: if (strcmp(a[leasepos], "infinite") == 0)
! 2427: new->lease_time = 0xffffffff;
! 2428: else if (strcmp(a[leasepos], "deprecated") == 0)
! 2429: new->flags |= CONTEXT_DEPRECATE;
! 2430: else
! 2431: {
! 2432: int fac = 1;
! 2433: if (strlen(a[leasepos]) > 0)
! 2434: {
! 2435: switch (a[leasepos][strlen(a[leasepos]) - 1])
! 2436: {
! 2437: case 'w':
! 2438: case 'W':
! 2439: fac *= 7;
! 2440: /* fall through */
! 2441: case 'd':
! 2442: case 'D':
! 2443: fac *= 24;
! 2444: /* fall though */
! 2445: case 'h':
! 2446: case 'H':
! 2447: fac *= 60;
! 2448: /* fall through */
! 2449: case 'm':
! 2450: case 'M':
! 2451: fac *= 60;
! 2452: /* fall through */
! 2453: case 's':
! 2454: case 'S':
! 2455: a[leasepos][strlen(a[leasepos]) - 1] = 0;
! 2456: }
! 2457:
! 2458: for (cp = a[leasepos]; *cp; cp++)
! 2459: if (!(*cp >= '0' && *cp <= '9'))
! 2460: break;
! 2461:
! 2462: if (*cp || (leasepos+1 < k))
! 2463: ret_err(_("bad dhcp-range"));
! 2464:
! 2465: new->lease_time = atoi(a[leasepos]) * fac;
! 2466: /* Leases of a minute or less confuse
! 2467: some clients, notably Apple's */
! 2468: if (new->lease_time < 120)
! 2469: new->lease_time = 120;
! 2470: }
! 2471: }
! 2472: }
! 2473: break;
! 2474: }
! 2475:
! 2476: case LOPT_BANK:
! 2477: case 'G': /* --dhcp-host */
! 2478: {
! 2479: int j, k = 0;
! 2480: char *a[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
! 2481: struct dhcp_config *new;
! 2482: struct in_addr in;
! 2483:
! 2484: new = opt_malloc(sizeof(struct dhcp_config));
! 2485:
! 2486: new->next = daemon->dhcp_conf;
! 2487: new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
! 2488: new->hwaddr = NULL;
! 2489: new->netid = NULL;
! 2490:
! 2491: if ((a[0] = arg))
! 2492: for (k = 1; k < 6; k++)
! 2493: if (!(a[k] = split(a[k-1])))
! 2494: break;
! 2495:
! 2496: for (j = 0; j < k; j++)
! 2497: if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
! 2498: {
! 2499: char *arg = a[j];
! 2500:
! 2501: if ((arg[0] == 'i' || arg[0] == 'I') &&
! 2502: (arg[1] == 'd' || arg[1] == 'D') &&
! 2503: arg[2] == ':')
! 2504: {
! 2505: if (arg[3] == '*')
! 2506: new->flags |= CONFIG_NOCLID;
! 2507: else
! 2508: {
! 2509: int len;
! 2510: arg += 3; /* dump id: */
! 2511: if (strchr(arg, ':'))
! 2512: len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
! 2513: else
! 2514: {
! 2515: unhide_metas(arg);
! 2516: len = (int) strlen(arg);
! 2517: }
! 2518:
! 2519: if (len == -1)
! 2520:
! 2521: ret_err(_("bad hex constant"));
! 2522: else if ((new->clid = opt_malloc(len)))
! 2523: {
! 2524: new->flags |= CONFIG_CLID;
! 2525: new->clid_len = len;
! 2526: memcpy(new->clid, arg, len);
! 2527: }
! 2528: }
! 2529: }
! 2530: /* dhcp-host has strange backwards-compat needs. */
! 2531: else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
! 2532: {
! 2533: struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
! 2534: struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
! 2535: newtag->net = opt_malloc(strlen(arg + 4) + 1);
! 2536: newlist->next = new->netid;
! 2537: new->netid = newlist;
! 2538: newlist->list = newtag;
! 2539: strcpy(newtag->net, arg+4);
! 2540: unhide_metas(newtag->net);
! 2541: }
! 2542: else if (strstr(arg, "tag:") == arg)
! 2543: ret_err(_("cannot match tags in --dhcp-host"));
! 2544: #ifdef HAVE_DHCP6
! 2545: else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
! 2546: {
! 2547: arg[strlen(arg)-1] = 0;
! 2548: arg++;
! 2549:
! 2550: if (!inet_pton(AF_INET6, arg, &new->addr6))
! 2551: ret_err(_("bad IPv6 address"));
! 2552:
! 2553: for (i= 0; i < 8; i++)
! 2554: if (new->addr6.s6_addr[i] != 0)
! 2555: break;
! 2556:
! 2557: /* set WILDCARD if network part all zeros */
! 2558: if (i == 8)
! 2559: new->flags |= CONFIG_WILDCARD;
! 2560:
! 2561: new->flags |= CONFIG_ADDR6;
! 2562: }
! 2563: #endif
! 2564: else
! 2565: {
! 2566: struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
! 2567: if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX,
! 2568: &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
! 2569: ret_err(_("bad hex constant"));
! 2570: else
! 2571: {
! 2572:
! 2573: newhw->next = new->hwaddr;
! 2574: new->hwaddr = newhw;
! 2575: }
! 2576: }
! 2577: }
! 2578: else if (strchr(a[j], '.') && (in.s_addr = inet_addr(a[j])) != (in_addr_t)-1)
! 2579: {
! 2580: struct dhcp_config *configs;
! 2581:
! 2582: new->addr = in;
! 2583: new->flags |= CONFIG_ADDR;
! 2584:
! 2585: /* If the same IP appears in more than one host config, then DISCOVER
! 2586: for one of the hosts will get the address, but REQUEST will be NAKed,
! 2587: since the address is reserved by the other one -> protocol loop. */
! 2588: for (configs = daemon->dhcp_conf; configs; configs = configs->next)
! 2589: if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
! 2590: {
! 2591: sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
! 2592: return 0;
! 2593: }
! 2594: }
! 2595: else
! 2596: {
! 2597: char *cp, *lastp = NULL, last = 0;
! 2598: int fac = 1;
! 2599:
! 2600: if (strlen(a[j]) > 1)
! 2601: {
! 2602: lastp = a[j] + strlen(a[j]) - 1;
! 2603: last = *lastp;
! 2604: switch (last)
! 2605: {
! 2606: case 'w':
! 2607: case 'W':
! 2608: fac *= 7;
! 2609: /* fall through */
! 2610: case 'd':
! 2611: case 'D':
! 2612: fac *= 24;
! 2613: /* fall through */
! 2614: case 'h':
! 2615: case 'H':
! 2616: fac *= 60;
! 2617: /* fall through */
! 2618: case 'm':
! 2619: case 'M':
! 2620: fac *= 60;
! 2621: /* fall through */
! 2622: case 's':
! 2623: case 'S':
! 2624: *lastp = 0;
! 2625: }
! 2626: }
! 2627:
! 2628: for (cp = a[j]; *cp; cp++)
! 2629: if (!isdigit((unsigned char)*cp) && *cp != ' ')
! 2630: break;
! 2631:
! 2632: if (*cp)
! 2633: {
! 2634: if (lastp)
! 2635: *lastp = last;
! 2636: if (strcmp(a[j], "infinite") == 0)
! 2637: {
! 2638: new->lease_time = 0xffffffff;
! 2639: new->flags |= CONFIG_TIME;
! 2640: }
! 2641: else if (strcmp(a[j], "ignore") == 0)
! 2642: new->flags |= CONFIG_DISABLE;
! 2643: else
! 2644: {
! 2645: if (!(new->hostname = canonicalise_opt(a[j])) ||
! 2646: !legal_hostname(new->hostname))
! 2647: ret_err(_("bad DHCP host name"));
! 2648:
! 2649: new->flags |= CONFIG_NAME;
! 2650: new->domain = strip_hostname(new->hostname);
! 2651: }
! 2652: }
! 2653: else
! 2654: {
! 2655: new->lease_time = atoi(a[j]) * fac;
! 2656: /* Leases of a minute or less confuse
! 2657: some clients, notably Apple's */
! 2658: if (new->lease_time < 120)
! 2659: new->lease_time = 120;
! 2660: new->flags |= CONFIG_TIME;
! 2661: }
! 2662: }
! 2663:
! 2664: daemon->dhcp_conf = new;
! 2665: break;
! 2666: }
! 2667:
! 2668: case LOPT_TAG_IF: /* --tag-if */
! 2669: {
! 2670: struct tag_if *new = opt_malloc(sizeof(struct tag_if));
! 2671:
! 2672: new->tag = NULL;
! 2673: new->set = NULL;
! 2674: new->next = NULL;
! 2675:
! 2676: /* preserve order */
! 2677: if (!daemon->tag_if)
! 2678: daemon->tag_if = new;
! 2679: else
! 2680: {
! 2681: struct tag_if *tmp;
! 2682: for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
! 2683: tmp->next = new;
! 2684: }
! 2685:
! 2686: while (arg)
! 2687: {
! 2688: size_t len;
! 2689:
! 2690: comma = split(arg);
! 2691: len = strlen(arg);
! 2692:
! 2693: if (len < 5)
! 2694: {
! 2695: new->set = NULL;
! 2696: break;
! 2697: }
! 2698: else
! 2699: {
! 2700: struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
! 2701: newtag->net = opt_malloc(len - 3);
! 2702: strcpy(newtag->net, arg+4);
! 2703: unhide_metas(newtag->net);
! 2704:
! 2705: if (strstr(arg, "set:") == arg)
! 2706: {
! 2707: struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
! 2708: newlist->next = new->set;
! 2709: new->set = newlist;
! 2710: newlist->list = newtag;
! 2711: }
! 2712: else if (strstr(arg, "tag:") == arg)
! 2713: {
! 2714: newtag->next = new->tag;
! 2715: new->tag = newtag;
! 2716: }
! 2717: else
! 2718: {
! 2719: new->set = NULL;
! 2720: free(newtag);
! 2721: break;
! 2722: }
! 2723: }
! 2724:
! 2725: arg = comma;
! 2726: }
! 2727:
! 2728: if (!new->set)
! 2729: ret_err(_("bad tag-if"));
! 2730:
! 2731: break;
! 2732: }
! 2733:
! 2734:
! 2735: case 'O': /* --dhcp-option */
! 2736: case LOPT_FORCE: /* --dhcp-option-force */
! 2737: case LOPT_OPTS:
! 2738: case LOPT_MATCH: /* --dhcp-match */
! 2739: return parse_dhcp_opt(errstr, arg,
! 2740: option == LOPT_FORCE ? DHOPT_FORCE :
! 2741: (option == LOPT_MATCH ? DHOPT_MATCH :
! 2742: (option == LOPT_OPTS ? DHOPT_BANK : 0)));
! 2743:
! 2744: case 'M': /* --dhcp-boot */
! 2745: {
! 2746: struct dhcp_netid *id = NULL;
! 2747: while (is_tag_prefix(arg))
! 2748: {
! 2749: struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
! 2750: newid->next = id;
! 2751: id = newid;
! 2752: comma = split(arg);
! 2753: newid->net = opt_string_alloc(arg+4);
! 2754: arg = comma;
! 2755: };
! 2756:
! 2757: if (!arg)
! 2758: ret_err(gen_err);
! 2759: else
! 2760: {
! 2761: char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
! 2762: struct in_addr dhcp_next_server;
! 2763: struct dhcp_boot *new;
! 2764: comma = split(arg);
! 2765: dhcp_file = opt_string_alloc(arg);
! 2766: dhcp_next_server.s_addr = 0;
! 2767: if (comma)
! 2768: {
! 2769: arg = comma;
! 2770: comma = split(arg);
! 2771: dhcp_sname = opt_string_alloc(arg);
! 2772: if (comma)
! 2773: {
! 2774: unhide_metas(comma);
! 2775: if ((dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1) {
! 2776:
! 2777: /*
! 2778: * The user may have specified the tftp hostname here.
! 2779: * save it so that it can be resolved/looked up during
! 2780: * actual dhcp_reply().
! 2781: */
! 2782:
! 2783: tftp_sname = opt_string_alloc(comma);
! 2784: dhcp_next_server.s_addr = 0;
! 2785: }
! 2786: }
! 2787: }
! 2788:
! 2789: new = opt_malloc(sizeof(struct dhcp_boot));
! 2790: new->file = dhcp_file;
! 2791: new->sname = dhcp_sname;
! 2792: new->tftp_sname = tftp_sname;
! 2793: new->next_server = dhcp_next_server;
! 2794: new->netid = id;
! 2795: new->next = daemon->boot_config;
! 2796: daemon->boot_config = new;
! 2797: }
! 2798:
! 2799: break;
! 2800: }
! 2801:
! 2802: case LOPT_PXE_PROMT: /* --pxe-prompt */
! 2803: {
! 2804: struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
! 2805: int timeout;
! 2806:
! 2807: new->netid = NULL;
! 2808: new->opt = 10; /* PXE_MENU_PROMPT */
! 2809:
! 2810: while (is_tag_prefix(arg))
! 2811: {
! 2812: struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
! 2813: comma = split(arg);
! 2814: nn->next = new->netid;
! 2815: new->netid = nn;
! 2816: nn->net = opt_string_alloc(arg+4);
! 2817: arg = comma;
! 2818: }
! 2819:
! 2820: if (!arg)
! 2821: ret_err(gen_err);
! 2822: else
! 2823: {
! 2824: comma = split(arg);
! 2825: unhide_metas(arg);
! 2826: new->len = strlen(arg) + 1;
! 2827: new->val = opt_malloc(new->len);
! 2828: memcpy(new->val + 1, arg, new->len - 1);
! 2829:
! 2830: new->u.vendor_class = (unsigned char *)"PXEClient";
! 2831: new->flags = DHOPT_VENDOR;
! 2832:
! 2833: if (comma && atoi_check(comma, &timeout))
! 2834: *(new->val) = timeout;
! 2835: else
! 2836: *(new->val) = 255;
! 2837:
! 2838: new->next = daemon->dhcp_opts;
! 2839: daemon->dhcp_opts = new;
! 2840: daemon->enable_pxe = 1;
! 2841: }
! 2842:
! 2843: break;
! 2844: }
! 2845:
! 2846: case LOPT_PXE_SERV: /* --pxe-service */
! 2847: {
! 2848: struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
! 2849: char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
! 2850: "IA32_EFI", "BC_EFI", "Xscale_EFI", "x86-64_EFI", NULL };
! 2851: static int boottype = 32768;
! 2852:
! 2853: new->netid = NULL;
! 2854: new->sname = NULL;
! 2855: new->server.s_addr = 0;
! 2856:
! 2857: while (is_tag_prefix(arg))
! 2858: {
! 2859: struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
! 2860: comma = split(arg);
! 2861: nn->next = new->netid;
! 2862: new->netid = nn;
! 2863: nn->net = opt_string_alloc(arg+4);
! 2864: arg = comma;
! 2865: }
! 2866:
! 2867: if (arg && (comma = split(arg)))
! 2868: {
! 2869: for (i = 0; CSA[i]; i++)
! 2870: if (strcasecmp(CSA[i], arg) == 0)
! 2871: break;
! 2872:
! 2873: if (CSA[i] || atoi_check(arg, &i))
! 2874: {
! 2875: arg = comma;
! 2876: comma = split(arg);
! 2877:
! 2878: new->CSA = i;
! 2879: new->menu = opt_string_alloc(arg);
! 2880:
! 2881: if (!comma)
! 2882: {
! 2883: new->type = 0; /* local boot */
! 2884: new->basename = NULL;
! 2885: }
! 2886: else
! 2887: {
! 2888: arg = comma;
! 2889: comma = split(arg);
! 2890: if (atoi_check(arg, &i))
! 2891: {
! 2892: new->type = i;
! 2893: new->basename = NULL;
! 2894: }
! 2895: else
! 2896: {
! 2897: new->type = boottype++;
! 2898: new->basename = opt_string_alloc(arg);
! 2899: }
! 2900:
! 2901: if (comma)
! 2902: {
! 2903: if (!inet_pton(AF_INET, comma, &new->server))
! 2904: {
! 2905: new->server.s_addr = 0;
! 2906: new->sname = opt_string_alloc(comma);
! 2907: }
! 2908:
! 2909: }
! 2910: }
! 2911:
! 2912: /* Order matters */
! 2913: new->next = NULL;
! 2914: if (!daemon->pxe_services)
! 2915: daemon->pxe_services = new;
! 2916: else
! 2917: {
! 2918: struct pxe_service *s;
! 2919: for (s = daemon->pxe_services; s->next; s = s->next);
! 2920: s->next = new;
! 2921: }
! 2922:
! 2923: daemon->enable_pxe = 1;
! 2924: break;
! 2925:
! 2926: }
! 2927: }
! 2928:
! 2929: ret_err(gen_err);
! 2930: }
! 2931:
! 2932: case '4': /* --dhcp-mac */
! 2933: {
! 2934: if (!(comma = split(arg)))
! 2935: ret_err(gen_err);
! 2936: else
! 2937: {
! 2938: struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
! 2939: new->netid.net = opt_string_alloc(set_prefix(arg));
! 2940: unhide_metas(comma);
! 2941: new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
! 2942: if (new->hwaddr_len == -1)
! 2943: ret_err(gen_err);
! 2944: else
! 2945: {
! 2946: new->next = daemon->dhcp_macs;
! 2947: daemon->dhcp_macs = new;
! 2948: }
! 2949: }
! 2950: }
! 2951: break;
! 2952:
! 2953: #ifdef OPTION6_PREFIX_CLASS
! 2954: case LOPT_PREF_CLSS: /* --dhcp-prefix-class */
! 2955: {
! 2956: struct prefix_class *new = opt_malloc(sizeof(struct prefix_class));
! 2957:
! 2958: if (!(comma = split(arg)) ||
! 2959: !atoi_check16(comma, &new->class))
! 2960: ret_err(gen_err);
! 2961:
! 2962: new->tag.net = opt_string_alloc(set_prefix(arg));
! 2963: new->next = daemon->prefix_classes;
! 2964: daemon->prefix_classes = new;
! 2965:
! 2966: break;
! 2967: }
! 2968: #endif
! 2969:
! 2970:
! 2971: case 'U': /* --dhcp-vendorclass */
! 2972: case 'j': /* --dhcp-userclass */
! 2973: case LOPT_CIRCUIT: /* --dhcp-circuitid */
! 2974: case LOPT_REMOTE: /* --dhcp-remoteid */
! 2975: case LOPT_SUBSCR: /* --dhcp-subscrid */
! 2976: {
! 2977: unsigned char *p;
! 2978: int dig = 0;
! 2979: struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
! 2980:
! 2981: if (!(comma = split(arg)))
! 2982: ret_err(gen_err);
! 2983:
! 2984: new->netid.net = opt_string_alloc(set_prefix(arg));
! 2985: /* check for hex string - must digits may include : must not have nothing else,
! 2986: only allowed for agent-options. */
! 2987:
! 2988: arg = comma;
! 2989: if ((comma = split(arg)))
! 2990: {
! 2991: if (option != 'U' || strstr(arg, "enterprise:") != arg)
! 2992: ret_err(gen_err);
! 2993: else
! 2994: new->enterprise = atoi(arg+11);
! 2995: }
! 2996: else
! 2997: comma = arg;
! 2998:
! 2999: for (p = (unsigned char *)comma; *p; p++)
! 3000: if (isxdigit(*p))
! 3001: dig = 1;
! 3002: else if (*p != ':')
! 3003: break;
! 3004: unhide_metas(comma);
! 3005: if (option == 'U' || option == 'j' || *p || !dig)
! 3006: {
! 3007: new->len = strlen(comma);
! 3008: new->data = opt_malloc(new->len);
! 3009: memcpy(new->data, comma, new->len);
! 3010: }
! 3011: else
! 3012: {
! 3013: new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
! 3014: new->data = opt_malloc(new->len);
! 3015: memcpy(new->data, comma, new->len);
! 3016: }
! 3017:
! 3018: switch (option)
! 3019: {
! 3020: case 'j':
! 3021: new->match_type = MATCH_USER;
! 3022: break;
! 3023: case 'U':
! 3024: new->match_type = MATCH_VENDOR;
! 3025: break;
! 3026: case LOPT_CIRCUIT:
! 3027: new->match_type = MATCH_CIRCUIT;
! 3028: break;
! 3029: case LOPT_REMOTE:
! 3030: new->match_type = MATCH_REMOTE;
! 3031: break;
! 3032: case LOPT_SUBSCR:
! 3033: new->match_type = MATCH_SUBSCRIBER;
! 3034: break;
! 3035: }
! 3036: new->next = daemon->dhcp_vendors;
! 3037: daemon->dhcp_vendors = new;
! 3038:
! 3039: break;
! 3040: }
! 3041:
! 3042: case LOPT_ALTPORT: /* --dhcp-alternate-port */
! 3043: if (!arg)
! 3044: {
! 3045: daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
! 3046: daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
! 3047: }
! 3048: else
! 3049: {
! 3050: comma = split(arg);
! 3051: if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
! 3052: (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
! 3053: ret_err(_("invalid port number"));
! 3054: if (!comma)
! 3055: daemon->dhcp_client_port = daemon->dhcp_server_port+1;
! 3056: }
! 3057: break;
! 3058:
! 3059: case 'J': /* --dhcp-ignore */
! 3060: case LOPT_NO_NAMES: /* --dhcp-ignore-names */
! 3061: case LOPT_BROADCAST: /* --dhcp-broadcast */
! 3062: case '3': /* --bootp-dynamic */
! 3063: case LOPT_GEN_NAMES: /* --dhcp-generate-names */
! 3064: {
! 3065: struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
! 3066: struct dhcp_netid *list = NULL;
! 3067: if (option == 'J')
! 3068: {
! 3069: new->next = daemon->dhcp_ignore;
! 3070: daemon->dhcp_ignore = new;
! 3071: }
! 3072: else if (option == LOPT_BROADCAST)
! 3073: {
! 3074: new->next = daemon->force_broadcast;
! 3075: daemon->force_broadcast = new;
! 3076: }
! 3077: else if (option == '3')
! 3078: {
! 3079: new->next = daemon->bootp_dynamic;
! 3080: daemon->bootp_dynamic = new;
! 3081: }
! 3082: else if (option == LOPT_GEN_NAMES)
! 3083: {
! 3084: new->next = daemon->dhcp_gen_names;
! 3085: daemon->dhcp_gen_names = new;
! 3086: }
! 3087: else
! 3088: {
! 3089: new->next = daemon->dhcp_ignore_names;
! 3090: daemon->dhcp_ignore_names = new;
! 3091: }
! 3092:
! 3093: while (arg) {
! 3094: struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));
! 3095: comma = split(arg);
! 3096: member->next = list;
! 3097: list = member;
! 3098: if (is_tag_prefix(arg))
! 3099: member->net = opt_string_alloc(arg+4);
! 3100: else
! 3101: member->net = opt_string_alloc(arg);
! 3102: arg = comma;
! 3103: }
! 3104:
! 3105: new->list = list;
! 3106: break;
! 3107: }
! 3108:
! 3109: case LOPT_PROXY: /* --dhcp-proxy */
! 3110: daemon->override = 1;
! 3111: while (arg) {
! 3112: struct addr_list *new = opt_malloc(sizeof(struct addr_list));
! 3113: comma = split(arg);
! 3114: if ((new->addr.s_addr = inet_addr(arg)) == (in_addr_t)-1)
! 3115: ret_err(_("bad dhcp-proxy address"));
! 3116: new->next = daemon->override_relays;
! 3117: daemon->override_relays = new;
! 3118: arg = comma;
! 3119: }
! 3120: break;
! 3121: #endif
! 3122:
! 3123: #ifdef HAVE_DHCP6
! 3124: case LOPT_DUID: /* --dhcp-duid */
! 3125: if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
! 3126: ret_err(_("bad DUID"));
! 3127: else
! 3128: {
! 3129: daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
! 3130: daemon->duid_config = opt_malloc(daemon->duid_config_len);
! 3131: memcpy(daemon->duid_config, comma, daemon->duid_config_len);
! 3132: }
! 3133: break;
! 3134: #endif
! 3135:
! 3136: case 'V': /* --alias */
! 3137: {
! 3138: char *dash, *a[3] = { NULL, NULL, NULL };
! 3139: int k = 0;
! 3140: struct doctor *new = opt_malloc(sizeof(struct doctor));
! 3141: new->next = daemon->doctors;
! 3142: daemon->doctors = new;
! 3143: new->mask.s_addr = 0xffffffff;
! 3144: new->end.s_addr = 0;
! 3145:
! 3146: if ((a[0] = arg))
! 3147: for (k = 1; k < 3; k++)
! 3148: {
! 3149: if (!(a[k] = split(a[k-1])))
! 3150: break;
! 3151: unhide_metas(a[k]);
! 3152: }
! 3153:
! 3154: dash = split_chr(a[0], '-');
! 3155:
! 3156: if ((k < 2) ||
! 3157: ((new->in.s_addr = inet_addr(a[0])) == (in_addr_t)-1) ||
! 3158: ((new->out.s_addr = inet_addr(a[1])) == (in_addr_t)-1))
! 3159: option = '?';
! 3160:
! 3161: if (k == 3)
! 3162: new->mask.s_addr = inet_addr(a[2]);
! 3163:
! 3164: if (dash &&
! 3165: ((new->end.s_addr = inet_addr(dash)) == (in_addr_t)-1 ||
! 3166: !is_same_net(new->in, new->end, new->mask) ||
! 3167: ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
! 3168: ret_err(_("invalid alias range"));
! 3169:
! 3170: break;
! 3171: }
! 3172:
! 3173: case LOPT_INTNAME: /* --interface-name */
! 3174: {
! 3175: struct interface_name *new, **up;
! 3176: char *domain = NULL;
! 3177:
! 3178: comma = split(arg);
! 3179:
! 3180: if (!comma || !(domain = canonicalise_opt(arg)))
! 3181: ret_err(_("bad interface name"));
! 3182:
! 3183: new = opt_malloc(sizeof(struct interface_name));
! 3184: new->next = NULL;
! 3185: /* Add to the end of the list, so that first name
! 3186: of an interface is used for PTR lookups. */
! 3187: for (up = &daemon->int_names; *up; up = &((*up)->next));
! 3188: *up = new;
! 3189: new->name = domain;
! 3190: new->intr = opt_string_alloc(comma);
! 3191: break;
! 3192: }
! 3193:
! 3194: case LOPT_CNAME: /* --cname */
! 3195: {
! 3196: struct cname *new;
! 3197: char *alias;
! 3198: char *target;
! 3199:
! 3200: if (!(comma = split(arg)))
! 3201: ret_err(gen_err);
! 3202:
! 3203: alias = canonicalise_opt(arg);
! 3204: target = canonicalise_opt(comma);
! 3205:
! 3206: if (!alias || !target)
! 3207: ret_err(_("bad CNAME"));
! 3208: else
! 3209: {
! 3210: for (new = daemon->cnames; new; new = new->next)
! 3211: if (hostname_isequal(new->alias, arg))
! 3212: ret_err(_("duplicate CNAME"));
! 3213: new = opt_malloc(sizeof(struct cname));
! 3214: new->next = daemon->cnames;
! 3215: daemon->cnames = new;
! 3216: new->alias = alias;
! 3217: new->target = target;
! 3218: }
! 3219:
! 3220: break;
! 3221: }
! 3222:
! 3223: case LOPT_PTR: /* --ptr-record */
! 3224: {
! 3225: struct ptr_record *new;
! 3226: char *dom, *target = NULL;
! 3227:
! 3228: comma = split(arg);
! 3229:
! 3230: if (!(dom = canonicalise_opt(arg)) ||
! 3231: (comma && !(target = canonicalise_opt(comma))))
! 3232: ret_err(_("bad PTR record"));
! 3233: else
! 3234: {
! 3235: new = opt_malloc(sizeof(struct ptr_record));
! 3236: new->next = daemon->ptr;
! 3237: daemon->ptr = new;
! 3238: new->name = dom;
! 3239: new->ptr = target;
! 3240: }
! 3241: break;
! 3242: }
! 3243:
! 3244: case LOPT_NAPTR: /* --naptr-record */
! 3245: {
! 3246: char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
! 3247: int k = 0;
! 3248: struct naptr *new;
! 3249: int order, pref;
! 3250: char *name, *replace = NULL;
! 3251:
! 3252: if ((a[0] = arg))
! 3253: for (k = 1; k < 7; k++)
! 3254: if (!(a[k] = split(a[k-1])))
! 3255: break;
! 3256:
! 3257:
! 3258: if (k < 6 ||
! 3259: !(name = canonicalise_opt(a[0])) ||
! 3260: !atoi_check16(a[1], &order) ||
! 3261: !atoi_check16(a[2], &pref) ||
! 3262: (k == 7 && !(replace = canonicalise_opt(a[6]))))
! 3263: ret_err(_("bad NAPTR record"));
! 3264: else
! 3265: {
! 3266: new = opt_malloc(sizeof(struct naptr));
! 3267: new->next = daemon->naptr;
! 3268: daemon->naptr = new;
! 3269: new->name = name;
! 3270: new->flags = opt_string_alloc(a[3]);
! 3271: new->services = opt_string_alloc(a[4]);
! 3272: new->regexp = opt_string_alloc(a[5]);
! 3273: new->replace = replace;
! 3274: new->order = order;
! 3275: new->pref = pref;
! 3276: }
! 3277: break;
! 3278: }
! 3279:
! 3280: case LOPT_RR: /* dns-rr */
! 3281: {
! 3282: struct txt_record *new;
! 3283: size_t len;
! 3284: char *data;
! 3285: int val;
! 3286:
! 3287: comma = split(arg);
! 3288: data = split(comma);
! 3289:
! 3290: new = opt_malloc(sizeof(struct txt_record));
! 3291: new->next = daemon->rr;
! 3292: daemon->rr = new;
! 3293:
! 3294: if (!atoi_check(comma, &val) ||
! 3295: !(new->name = canonicalise_opt(arg)) ||
! 3296: (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
! 3297: ret_err(_("bad RR record"));
! 3298:
! 3299: new->class = val;
! 3300: new->len = 0;
! 3301:
! 3302: if (data)
! 3303: {
! 3304: new->txt=opt_malloc(len);
! 3305: new->len = len;
! 3306: memcpy(new->txt, data, len);
! 3307: }
! 3308:
! 3309: break;
! 3310: }
! 3311:
! 3312: case 'Y': /* --txt-record */
! 3313: {
! 3314: struct txt_record *new;
! 3315: unsigned char *p, *cnt;
! 3316: size_t len;
! 3317:
! 3318: comma = split(arg);
! 3319:
! 3320: new = opt_malloc(sizeof(struct txt_record));
! 3321: new->next = daemon->txt;
! 3322: daemon->txt = new;
! 3323: new->class = C_IN;
! 3324:
! 3325: if (!(new->name = canonicalise_opt(arg)))
! 3326: ret_err(_("bad TXT record"));
! 3327:
! 3328: len = comma ? strlen(comma) : 0;
! 3329: len += (len/255) + 1; /* room for extra counts */
! 3330: new->txt = p = opt_malloc(len);
! 3331:
! 3332: cnt = p++;
! 3333: *cnt = 0;
! 3334:
! 3335: while (comma && *comma)
! 3336: {
! 3337: unsigned char c = (unsigned char)*comma++;
! 3338:
! 3339: if (c == ',' || *cnt == 255)
! 3340: {
! 3341: if (c != ',')
! 3342: comma--;
! 3343: cnt = p++;
! 3344: *cnt = 0;
! 3345: }
! 3346: else
! 3347: {
! 3348: *p++ = unhide_meta(c);
! 3349: (*cnt)++;
! 3350: }
! 3351: }
! 3352:
! 3353: new->len = p - new->txt;
! 3354:
! 3355: break;
! 3356: }
! 3357:
! 3358: case 'W': /* --srv-host */
! 3359: {
! 3360: int port = 1, priority = 0, weight = 0;
! 3361: char *name, *target = NULL;
! 3362: struct mx_srv_record *new;
! 3363:
! 3364: comma = split(arg);
! 3365:
! 3366: if (!(name = canonicalise_opt(arg)))
! 3367: ret_err(_("bad SRV record"));
! 3368:
! 3369: if (comma)
! 3370: {
! 3371: arg = comma;
! 3372: comma = split(arg);
! 3373: if (!(target = canonicalise_opt(arg)))
! 3374: ret_err(_("bad SRV target"));
! 3375:
! 3376: if (comma)
! 3377: {
! 3378: arg = comma;
! 3379: comma = split(arg);
! 3380: if (!atoi_check16(arg, &port))
! 3381: ret_err(_("invalid port number"));
! 3382:
! 3383: if (comma)
! 3384: {
! 3385: arg = comma;
! 3386: comma = split(arg);
! 3387: if (!atoi_check16(arg, &priority))
! 3388: ret_err(_("invalid priority"));
! 3389:
! 3390: if (comma)
! 3391: {
! 3392: arg = comma;
! 3393: comma = split(arg);
! 3394: if (!atoi_check16(arg, &weight))
! 3395: ret_err(_("invalid weight"));
! 3396: }
! 3397: }
! 3398: }
! 3399: }
! 3400:
! 3401: new = opt_malloc(sizeof(struct mx_srv_record));
! 3402: new->next = daemon->mxnames;
! 3403: daemon->mxnames = new;
! 3404: new->issrv = 1;
! 3405: new->name = name;
! 3406: new->target = target;
! 3407: new->srvport = port;
! 3408: new->priority = priority;
! 3409: new->weight = weight;
! 3410: break;
! 3411: }
! 3412:
! 3413: case LOPT_HOST_REC: /* --host-record */
! 3414: {
! 3415: struct host_record *new = opt_malloc(sizeof(struct host_record));
! 3416: memset(new, 0, sizeof(struct host_record));
! 3417:
! 3418: if (!arg || !(comma = split(arg)))
! 3419: ret_err(_("Bad host-record"));
! 3420:
! 3421: while (arg)
! 3422: {
! 3423: struct all_addr addr;
! 3424: if (inet_pton(AF_INET, arg, &addr))
! 3425: new->addr = addr.addr.addr4;
! 3426: #ifdef HAVE_IPV6
! 3427: else if (inet_pton(AF_INET6, arg, &addr))
! 3428: new->addr6 = addr.addr.addr6;
! 3429: #endif
! 3430: else
! 3431: {
! 3432: int nomem;
! 3433: char *canon = canonicalise(arg, &nomem);
! 3434: struct name_list *nl = opt_malloc(sizeof(struct name_list));
! 3435: if (!canon)
! 3436: ret_err(_("Bad name in host-record"));
! 3437:
! 3438: nl->name = canon;
! 3439: /* keep order, so that PTR record goes to first name */
! 3440: nl->next = NULL;
! 3441: if (!new->names)
! 3442: new->names = nl;
! 3443: else
! 3444: {
! 3445: struct name_list *tmp;
! 3446: for (tmp = new->names; tmp->next; tmp = tmp->next);
! 3447: tmp->next = nl;
! 3448: }
! 3449: }
! 3450:
! 3451: arg = comma;
! 3452: comma = split(arg);
! 3453: }
! 3454:
! 3455: /* Keep list order */
! 3456: if (!daemon->host_records_tail)
! 3457: daemon->host_records = new;
! 3458: else
! 3459: daemon->host_records_tail->next = new;
! 3460: new->next = NULL;
! 3461: daemon->host_records_tail = new;
! 3462: break;
! 3463: }
! 3464:
! 3465: default:
! 3466: ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"));
! 3467:
! 3468: }
! 3469:
! 3470: return 1;
! 3471: }
! 3472:
! 3473: static void read_file(char *file, FILE *f, int hard_opt)
! 3474: {
! 3475: volatile int lineno = 0;
! 3476: char *buff = daemon->namebuff;
! 3477:
! 3478: while (fgets(buff, MAXDNAME, f))
! 3479: {
! 3480: int white, i, option = hard_opt;
! 3481: char *errmess, *p, *arg = NULL, *start;
! 3482: size_t len;
! 3483:
! 3484: /* Memory allocation failure longjmps here if mem_recover == 1 */
! 3485: if (option != 0)
! 3486: {
! 3487: if (setjmp(mem_jmp))
! 3488: continue;
! 3489: mem_recover = 1;
! 3490: }
! 3491:
! 3492: lineno++;
! 3493: errmess = NULL;
! 3494:
! 3495: /* Implement quotes, inside quotes we allow \\ \" \n and \t
! 3496: metacharacters get hidden also strip comments */
! 3497: for (white = 1, p = buff; *p; p++)
! 3498: {
! 3499: if (*p == '"')
! 3500: {
! 3501: memmove(p, p+1, strlen(p+1)+1);
! 3502:
! 3503: for(; *p && *p != '"'; p++)
! 3504: {
! 3505: if (*p == '\\' && strchr("\"tnebr\\", p[1]))
! 3506: {
! 3507: if (p[1] == 't')
! 3508: p[1] = '\t';
! 3509: else if (p[1] == 'n')
! 3510: p[1] = '\n';
! 3511: else if (p[1] == 'b')
! 3512: p[1] = '\b';
! 3513: else if (p[1] == 'r')
! 3514: p[1] = '\r';
! 3515: else if (p[1] == 'e') /* escape */
! 3516: p[1] = '\033';
! 3517: memmove(p, p+1, strlen(p+1)+1);
! 3518: }
! 3519: *p = hide_meta(*p);
! 3520: }
! 3521:
! 3522: if (*p == 0)
! 3523: {
! 3524: errmess = _("missing \"");
! 3525: goto oops;
! 3526: }
! 3527:
! 3528: memmove(p, p+1, strlen(p+1)+1);
! 3529: }
! 3530:
! 3531: if (isspace(*p))
! 3532: {
! 3533: *p = ' ';
! 3534: white = 1;
! 3535: }
! 3536: else
! 3537: {
! 3538: if (white && *p == '#')
! 3539: {
! 3540: *p = 0;
! 3541: break;
! 3542: }
! 3543: white = 0;
! 3544: }
! 3545: }
! 3546:
! 3547:
! 3548: /* strip leading spaces */
! 3549: for (start = buff; *start && *start == ' '; start++);
! 3550:
! 3551: /* strip trailing spaces */
! 3552: for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
! 3553:
! 3554: if (len == 0)
! 3555: continue;
! 3556: else
! 3557: start[len] = 0;
! 3558:
! 3559: if (option != 0)
! 3560: arg = start;
! 3561: else if ((p=strchr(start, '=')))
! 3562: {
! 3563: /* allow spaces around "=" */
! 3564: for (arg = p+1; *arg == ' '; arg++);
! 3565: for (; p >= start && (*p == ' ' || *p == '='); p--)
! 3566: *p = 0;
! 3567: }
! 3568: else
! 3569: arg = NULL;
! 3570:
! 3571: if (option == 0)
! 3572: {
! 3573: for (option = 0, i = 0; opts[i].name; i++)
! 3574: if (strcmp(opts[i].name, start) == 0)
! 3575: {
! 3576: option = opts[i].val;
! 3577: break;
! 3578: }
! 3579:
! 3580: if (!option)
! 3581: errmess = _("bad option");
! 3582: else if (opts[i].has_arg == 0 && arg)
! 3583: errmess = _("extraneous parameter");
! 3584: else if (opts[i].has_arg == 1 && !arg)
! 3585: errmess = _("missing parameter");
! 3586: }
! 3587:
! 3588: oops:
! 3589: if (errmess)
! 3590: strcpy(daemon->namebuff, errmess);
! 3591:
! 3592: if (errmess || !one_opt(option, arg, buff, _("error"), 0))
! 3593: {
! 3594: sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
! 3595: if (hard_opt != 0)
! 3596: my_syslog(LOG_ERR, "%s", daemon->namebuff);
! 3597: else
! 3598: die("%s", daemon->namebuff, EC_BADCONF);
! 3599: }
! 3600: }
! 3601:
! 3602: mem_recover = 0;
! 3603: fclose(f);
! 3604: }
! 3605:
! 3606: static int one_file(char *file, int hard_opt)
! 3607: {
! 3608: FILE *f;
! 3609: int nofile_ok = 0;
! 3610: static int read_stdin = 0;
! 3611: static struct fileread {
! 3612: dev_t dev;
! 3613: ino_t ino;
! 3614: struct fileread *next;
! 3615: } *filesread = NULL;
! 3616:
! 3617: if (hard_opt == '7')
! 3618: {
! 3619: /* default conf-file reading */
! 3620: hard_opt = 0;
! 3621: nofile_ok = 1;
! 3622: }
! 3623:
! 3624: if (hard_opt == 0 && strcmp(file, "-") == 0)
! 3625: {
! 3626: if (read_stdin == 1)
! 3627: return 1;
! 3628: read_stdin = 1;
! 3629: file = "stdin";
! 3630: f = stdin;
! 3631: }
! 3632: else
! 3633: {
! 3634: /* ignore repeated files. */
! 3635: struct stat statbuf;
! 3636:
! 3637: if (hard_opt == 0 && stat(file, &statbuf) == 0)
! 3638: {
! 3639: struct fileread *r;
! 3640:
! 3641: for (r = filesread; r; r = r->next)
! 3642: if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
! 3643: return 1;
! 3644:
! 3645: r = safe_malloc(sizeof(struct fileread));
! 3646: r->next = filesread;
! 3647: filesread = r;
! 3648: r->dev = statbuf.st_dev;
! 3649: r->ino = statbuf.st_ino;
! 3650: }
! 3651:
! 3652: if (!(f = fopen(file, "r")))
! 3653: {
! 3654: if (errno == ENOENT && nofile_ok)
! 3655: return 1; /* No conffile, all done. */
! 3656: else
! 3657: {
! 3658: char *str = _("cannot read %s: %s");
! 3659: if (hard_opt != 0)
! 3660: {
! 3661: my_syslog(LOG_ERR, str, file, strerror(errno));
! 3662: return 0;
! 3663: }
! 3664: else
! 3665: die(str, file, EC_FILE);
! 3666: }
! 3667: }
! 3668: }
! 3669:
! 3670: read_file(file, f, hard_opt);
! 3671: return 1;
! 3672: }
! 3673:
! 3674: /* expand any name which is a directory */
! 3675: struct hostsfile *expand_filelist(struct hostsfile *list)
! 3676: {
! 3677: int i;
! 3678: struct hostsfile *ah;
! 3679:
! 3680: for (i = 0, ah = list; ah; ah = ah->next)
! 3681: {
! 3682: if (i <= ah->index)
! 3683: i = ah->index + 1;
! 3684:
! 3685: if (ah->flags & AH_DIR)
! 3686: ah->flags |= AH_INACTIVE;
! 3687: else
! 3688: ah->flags &= ~AH_INACTIVE;
! 3689: }
! 3690:
! 3691: for (ah = list; ah; ah = ah->next)
! 3692: if (!(ah->flags & AH_INACTIVE))
! 3693: {
! 3694: struct stat buf;
! 3695: if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
! 3696: {
! 3697: DIR *dir_stream;
! 3698: struct dirent *ent;
! 3699:
! 3700: /* don't read this as a file */
! 3701: ah->flags |= AH_INACTIVE;
! 3702:
! 3703: if (!(dir_stream = opendir(ah->fname)))
! 3704: my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
! 3705: ah->fname, strerror(errno));
! 3706: else
! 3707: {
! 3708: while ((ent = readdir(dir_stream)))
! 3709: {
! 3710: size_t lendir = strlen(ah->fname);
! 3711: size_t lenfile = strlen(ent->d_name);
! 3712: struct hostsfile *ah1;
! 3713: char *path;
! 3714:
! 3715: /* ignore emacs backups and dotfiles */
! 3716: if (lenfile == 0 ||
! 3717: ent->d_name[lenfile - 1] == '~' ||
! 3718: (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
! 3719: ent->d_name[0] == '.')
! 3720: continue;
! 3721:
! 3722: /* see if we have an existing record.
! 3723: dir is ah->fname
! 3724: file is ent->d_name
! 3725: path to match is ah1->fname */
! 3726:
! 3727: for (ah1 = list; ah1; ah1 = ah1->next)
! 3728: {
! 3729: if (lendir < strlen(ah1->fname) &&
! 3730: strstr(ah1->fname, ah->fname) == ah1->fname &&
! 3731: ah1->fname[lendir] == '/' &&
! 3732: strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
! 3733: {
! 3734: ah1->flags &= ~AH_INACTIVE;
! 3735: break;
! 3736: }
! 3737: }
! 3738:
! 3739: /* make new record */
! 3740: if (!ah1)
! 3741: {
! 3742: if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
! 3743: continue;
! 3744:
! 3745: if (!(path = whine_malloc(lendir + lenfile + 2)))
! 3746: {
! 3747: free(ah1);
! 3748: continue;
! 3749: }
! 3750:
! 3751: strcpy(path, ah->fname);
! 3752: strcat(path, "/");
! 3753: strcat(path, ent->d_name);
! 3754: ah1->fname = path;
! 3755: ah1->index = i++;
! 3756: ah1->flags = AH_DIR;
! 3757: ah1->next = list;
! 3758: list = ah1;
! 3759: }
! 3760:
! 3761: /* inactivate record if not regular file */
! 3762: if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
! 3763: ah1->flags |= AH_INACTIVE;
! 3764:
! 3765: }
! 3766: closedir(dir_stream);
! 3767: }
! 3768: }
! 3769: }
! 3770:
! 3771: return list;
! 3772: }
! 3773:
! 3774:
! 3775: #ifdef HAVE_DHCP
! 3776: void reread_dhcp(void)
! 3777: {
! 3778: struct hostsfile *hf;
! 3779:
! 3780: if (daemon->dhcp_hosts_file)
! 3781: {
! 3782: struct dhcp_config *configs, *cp, **up;
! 3783:
! 3784: /* remove existing... */
! 3785: for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
! 3786: {
! 3787: cp = configs->next;
! 3788:
! 3789: if (configs->flags & CONFIG_BANK)
! 3790: {
! 3791: struct hwaddr_config *mac, *tmp;
! 3792: struct dhcp_netid_list *list, *tmplist;
! 3793:
! 3794: for (mac = configs->hwaddr; mac; mac = tmp)
! 3795: {
! 3796: tmp = mac->next;
! 3797: free(mac);
! 3798: }
! 3799:
! 3800: if (configs->flags & CONFIG_CLID)
! 3801: free(configs->clid);
! 3802:
! 3803: for (list = configs->netid; list; list = tmplist)
! 3804: {
! 3805: free(list->list);
! 3806: tmplist = list->next;
! 3807: free(list);
! 3808: }
! 3809:
! 3810: if (configs->flags & CONFIG_NAME)
! 3811: free(configs->hostname);
! 3812:
! 3813: *up = configs->next;
! 3814: free(configs);
! 3815: }
! 3816: else
! 3817: up = &configs->next;
! 3818: }
! 3819:
! 3820: daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
! 3821: for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
! 3822: if (!(hf->flags & AH_INACTIVE))
! 3823: {
! 3824: if (one_file(hf->fname, LOPT_BANK))
! 3825: my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
! 3826: }
! 3827: }
! 3828:
! 3829: if (daemon->dhcp_opts_file)
! 3830: {
! 3831: struct dhcp_opt *opts, *cp, **up;
! 3832: struct dhcp_netid *id, *next;
! 3833:
! 3834: for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
! 3835: {
! 3836: cp = opts->next;
! 3837:
! 3838: if (opts->flags & DHOPT_BANK)
! 3839: {
! 3840: if ((opts->flags & DHOPT_VENDOR))
! 3841: free(opts->u.vendor_class);
! 3842: free(opts->val);
! 3843: for (id = opts->netid; id; id = next)
! 3844: {
! 3845: next = id->next;
! 3846: free(id->net);
! 3847: free(id);
! 3848: }
! 3849: *up = opts->next;
! 3850: free(opts);
! 3851: }
! 3852: else
! 3853: up = &opts->next;
! 3854: }
! 3855:
! 3856: daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
! 3857: for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
! 3858: if (!(hf->flags & AH_INACTIVE))
! 3859: {
! 3860: if (one_file(hf->fname, LOPT_OPTS))
! 3861: my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
! 3862: }
! 3863: }
! 3864: }
! 3865: #endif
! 3866:
! 3867: void read_opts(int argc, char **argv, char *compile_opts)
! 3868: {
! 3869: char *buff = opt_malloc(MAXDNAME);
! 3870: int option, conffile_opt = '7', testmode = 0;
! 3871: char *arg, *conffile = CONFFILE;
! 3872:
! 3873: opterr = 0;
! 3874:
! 3875: daemon = opt_malloc(sizeof(struct daemon));
! 3876: memset(daemon, 0, sizeof(struct daemon));
! 3877: daemon->namebuff = buff;
! 3878:
! 3879: /* Set defaults - everything else is zero or NULL */
! 3880: daemon->cachesize = CACHESIZ;
! 3881: daemon->ftabsize = FTABSIZ;
! 3882: daemon->port = NAMESERVER_PORT;
! 3883: daemon->dhcp_client_port = DHCP_CLIENT_PORT;
! 3884: daemon->dhcp_server_port = DHCP_SERVER_PORT;
! 3885: daemon->default_resolv.is_default = 1;
! 3886: daemon->default_resolv.name = RESOLVFILE;
! 3887: daemon->resolv_files = &daemon->default_resolv;
! 3888: daemon->username = CHUSER;
! 3889: daemon->runfile = RUNFILE;
! 3890: daemon->dhcp_max = MAXLEASES;
! 3891: daemon->tftp_max = TFTP_MAX_CONNECTIONS;
! 3892: daemon->edns_pktsz = EDNS_PKTSZ;
! 3893: daemon->log_fac = -1;
! 3894: daemon->auth_ttl = AUTH_TTL;
! 3895: daemon->soa_refresh = SOA_REFRESH;
! 3896: daemon->soa_retry = SOA_RETRY;
! 3897: daemon->soa_expiry = SOA_EXPIRY;
! 3898: add_txt("version.bind", "dnsmasq-" VERSION );
! 3899: add_txt("authors.bind", "Simon Kelley");
! 3900: add_txt("copyright.bind", COPYRIGHT);
! 3901:
! 3902: while (1)
! 3903: {
! 3904: #ifdef HAVE_GETOPT_LONG
! 3905: option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
! 3906: #else
! 3907: option = getopt(argc, argv, OPTSTRING);
! 3908: #endif
! 3909:
! 3910: if (option == -1)
! 3911: {
! 3912: for (; optind < argc; optind++)
! 3913: {
! 3914: unsigned char *c = (unsigned char *)argv[optind];
! 3915: for (; *c != 0; c++)
! 3916: if (!isspace(*c))
! 3917: die(_("junk found in command line"), NULL, EC_BADCONF);
! 3918: }
! 3919: break;
! 3920: }
! 3921:
! 3922: /* Copy optarg so that argv doesn't get changed */
! 3923: if (optarg)
! 3924: {
! 3925: strncpy(buff, optarg, MAXDNAME);
! 3926: buff[MAXDNAME-1] = 0;
! 3927: arg = buff;
! 3928: }
! 3929: else
! 3930: arg = NULL;
! 3931:
! 3932: /* command-line only stuff */
! 3933: if (option == LOPT_TEST)
! 3934: testmode = 1;
! 3935: else if (option == 'w')
! 3936: {
! 3937: #ifdef HAVE_DHCP
! 3938: if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
! 3939: display_opts();
! 3940: #ifdef HAVE_DHCP6
! 3941: else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
! 3942: display_opts6();
! 3943: #endif
! 3944: else
! 3945: #endif
! 3946: do_usage();
! 3947:
! 3948: exit(0);
! 3949: }
! 3950: else if (option == 'v')
! 3951: {
! 3952: printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
! 3953: printf(_("Compile time options: %s\n\n"), compile_opts);
! 3954: printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
! 3955: printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
! 3956: printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
! 3957: exit(0);
! 3958: }
! 3959: else if (option == 'C')
! 3960: {
! 3961: conffile_opt = 0; /* file must exist */
! 3962: conffile = opt_string_alloc(arg);
! 3963: }
! 3964: else
! 3965: {
! 3966: #ifdef HAVE_GETOPT_LONG
! 3967: if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1))
! 3968: #else
! 3969: if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1))
! 3970: #endif
! 3971: die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
! 3972: }
! 3973: }
! 3974:
! 3975: if (conffile)
! 3976: one_file(conffile, conffile_opt);
! 3977:
! 3978: /* port might not be known when the address is parsed - fill in here */
! 3979: if (daemon->servers)
! 3980: {
! 3981: struct server *tmp;
! 3982: for (tmp = daemon->servers; tmp; tmp = tmp->next)
! 3983: if (!(tmp->flags & SERV_HAS_SOURCE))
! 3984: {
! 3985: if (tmp->source_addr.sa.sa_family == AF_INET)
! 3986: tmp->source_addr.in.sin_port = htons(daemon->query_port);
! 3987: #ifdef HAVE_IPV6
! 3988: else if (tmp->source_addr.sa.sa_family == AF_INET6)
! 3989: tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
! 3990: #endif
! 3991: }
! 3992: }
! 3993:
! 3994: if (daemon->if_addrs)
! 3995: {
! 3996: struct iname *tmp;
! 3997: for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
! 3998: if (tmp->addr.sa.sa_family == AF_INET)
! 3999: tmp->addr.in.sin_port = htons(daemon->port);
! 4000: #ifdef HAVE_IPV6
! 4001: else if (tmp->addr.sa.sa_family == AF_INET6)
! 4002: tmp->addr.in6.sin6_port = htons(daemon->port);
! 4003: #endif /* IPv6 */
! 4004: }
! 4005:
! 4006: /* create default, if not specified */
! 4007: if (daemon->authserver && !daemon->hostmaster)
! 4008: {
! 4009: strcpy(buff, "hostmaster.");
! 4010: strcat(buff, daemon->authserver);
! 4011: daemon->hostmaster = opt_string_alloc(buff);
! 4012: }
! 4013:
! 4014: /* only one of these need be specified: the other defaults to the host-name */
! 4015: if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
! 4016: {
! 4017: struct mx_srv_record *mx;
! 4018:
! 4019: if (gethostname(buff, MAXDNAME) == -1)
! 4020: die(_("cannot get host-name: %s"), NULL, EC_MISC);
! 4021:
! 4022: for (mx = daemon->mxnames; mx; mx = mx->next)
! 4023: if (!mx->issrv && hostname_isequal(mx->name, buff))
! 4024: break;
! 4025:
! 4026: if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
! 4027: {
! 4028: mx = opt_malloc(sizeof(struct mx_srv_record));
! 4029: mx->next = daemon->mxnames;
! 4030: mx->issrv = 0;
! 4031: mx->target = NULL;
! 4032: mx->name = opt_string_alloc(buff);
! 4033: daemon->mxnames = mx;
! 4034: }
! 4035:
! 4036: if (!daemon->mxtarget)
! 4037: daemon->mxtarget = opt_string_alloc(buff);
! 4038:
! 4039: for (mx = daemon->mxnames; mx; mx = mx->next)
! 4040: if (!mx->issrv && !mx->target)
! 4041: mx->target = daemon->mxtarget;
! 4042: }
! 4043:
! 4044: if (!option_bool(OPT_NO_RESOLV) &&
! 4045: daemon->resolv_files &&
! 4046: daemon->resolv_files->next &&
! 4047: option_bool(OPT_NO_POLL))
! 4048: die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
! 4049:
! 4050: if (option_bool(OPT_RESOLV_DOMAIN))
! 4051: {
! 4052: char *line;
! 4053: FILE *f;
! 4054:
! 4055: if (option_bool(OPT_NO_RESOLV) ||
! 4056: !daemon->resolv_files ||
! 4057: (daemon->resolv_files)->next)
! 4058: die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
! 4059:
! 4060: if (!(f = fopen((daemon->resolv_files)->name, "r")))
! 4061: die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
! 4062:
! 4063: while ((line = fgets(buff, MAXDNAME, f)))
! 4064: {
! 4065: char *token = strtok(line, " \t\n\r");
! 4066:
! 4067: if (!token || strcmp(token, "search") != 0)
! 4068: continue;
! 4069:
! 4070: if ((token = strtok(NULL, " \t\n\r")) &&
! 4071: (daemon->domain_suffix = canonicalise_opt(token)))
! 4072: break;
! 4073: }
! 4074:
! 4075: fclose(f);
! 4076:
! 4077: if (!daemon->domain_suffix)
! 4078: die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
! 4079: }
! 4080:
! 4081: if (daemon->domain_suffix)
! 4082: {
! 4083: /* add domain for any srv record without one. */
! 4084: struct mx_srv_record *srv;
! 4085:
! 4086: for (srv = daemon->mxnames; srv; srv = srv->next)
! 4087: if (srv->issrv &&
! 4088: strchr(srv->name, '.') &&
! 4089: strchr(srv->name, '.') == strrchr(srv->name, '.'))
! 4090: {
! 4091: strcpy(buff, srv->name);
! 4092: strcat(buff, ".");
! 4093: strcat(buff, daemon->domain_suffix);
! 4094: free(srv->name);
! 4095: srv->name = opt_string_alloc(buff);
! 4096: }
! 4097: }
! 4098: else if (option_bool(OPT_DHCP_FQDN))
! 4099: die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
! 4100:
! 4101: if (testmode)
! 4102: {
! 4103: fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
! 4104: exit(0);
! 4105: }
! 4106: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>