diff options
Diffstat (limited to 'src/option.c')
-rw-r--r-- | src/option.c | 1380 |
1 files changed, 861 insertions, 519 deletions
diff --git a/src/option.c b/src/option.c index d358d99..dbe5f90 100644 --- a/src/option.c +++ b/src/option.c @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -129,9 +129,6 @@ struct myoption { #define LOPT_AUTHPEER 318 #define LOPT_IPSET 319 #define LOPT_SYNTH 320 -#ifdef OPTION6_PREFIX_CLASS -#define LOPT_PREF_CLSS 321 -#endif #define LOPT_RELAY 323 #define LOPT_RA_PARAM 324 #define LOPT_ADD_SBNET 325 @@ -160,6 +157,16 @@ struct myoption { #define LOPT_DHCPTTL 348 #define LOPT_TFTP_MTU 349 #define LOPT_REPLY_DELAY 350 +#define LOPT_RAPID_COMMIT 351 +#define LOPT_DUMPFILE 352 +#define LOPT_DUMPMASK 353 +#define LOPT_UBUS 354 +#define LOPT_NAME_MATCH 355 +#define LOPT_CAA 356 +#define LOPT_SHARED_NET 357 +#define LOPT_IGNORE_CLID 358 +#define LOPT_SINGLE_PORT 359 +#define LOPT_SCRIPT_TIME 360 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -228,8 +235,10 @@ static const struct myoption opts[] = { "srv-host", 1, 0, 'W' }, { "localise-queries", 0, 0, 'y' }, { "txt-record", 1, 0, 'Y' }, + { "caa-record", 1, 0 , LOPT_CAA }, { "dns-rr", 1, 0, LOPT_RR }, { "enable-dbus", 2, 0, '1' }, + { "enable-ubus", 2, 0, LOPT_UBUS }, { "bootp-dynamic", 2, 0, '3' }, { "dhcp-mac", 1, 0, '4' }, { "no-ping", 0, 0, '5' }, @@ -237,6 +246,7 @@ static const struct myoption opts[] = { "conf-dir", 1, 0, '7' }, { "log-facility", 1, 0 ,'8' }, { "leasefile-ro", 0, 0, '9' }, + { "script-on-renewal", 0, 0, LOPT_SCRIPT_TIME}, { "dns-forward-max", 1, 0, '0' }, { "clear-on-reload", 0, 0, LOPT_RELOAD }, { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES }, @@ -248,9 +258,11 @@ static const struct myoption opts[] = { "tftp-max", 1, 0, LOPT_TFTP_MAX }, { "tftp-mtu", 1, 0, LOPT_TFTP_MTU }, { "tftp-lowercase", 0, 0, LOPT_TFTP_LC }, + { "tftp-single-port", 0, 0, LOPT_SINGLE_PORT }, { "ptr-record", 1, 0, LOPT_PTR }, { "naptr-record", 1, 0, LOPT_NAPTR }, { "bridge-interface", 1, 0 , LOPT_BRIDGE }, + { "shared-network", 1, 0, LOPT_SHARED_NET }, { "dhcp-option-force", 1, 0, LOPT_FORCE }, { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK }, { "log-dhcp", 0, 0, LOPT_LOG_OPTS }, @@ -268,7 +280,8 @@ static const struct myoption opts[] = { "stop-dns-rebind", 0, 0, LOPT_REBIND }, { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND }, { "all-servers", 0, 0, LOPT_NOLAST }, - { "dhcp-match", 1, 0, LOPT_MATCH }, + { "dhcp-match", 1, 0, LOPT_MATCH }, + { "dhcp-name-match", 1, 0, LOPT_NAME_MATCH }, { "dhcp-broadcast", 2, 0, LOPT_BROADCAST }, { "neg-ttl", 1, 0, LOPT_NEGTTL }, { "max-ttl", 1, 0, LOPT_MAXTTL }, @@ -310,12 +323,9 @@ static const struct myoption opts[] = { "dnssec", 0, 0, LOPT_SEC_VALID }, { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR }, { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG }, - { "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK }, + { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK }, { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME }, { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP }, -#ifdef OPTION6_PREFIX_CLASS - { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS }, -#endif { "dhcp-relay", 1, 0, LOPT_RELAY }, { "ra-param", 1, 0, LOPT_RA_PARAM }, { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP }, @@ -325,6 +335,10 @@ static const struct myoption opts[] = { "script-arp", 0, 0, LOPT_SCRIPT_ARP }, { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL }, { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY }, + { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT }, + { "dumpfile", 1, 0, LOPT_DUMPFILE }, + { "dumpmask", 1, 0, LOPT_DUMPMASK }, + { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID }, { NULL, 0, 0, 0 } }; @@ -414,10 +428,12 @@ static struct { { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL }, { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE }, { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL }, + { LOPT_UBUS, ARG_ONE, "[=<busname>]", gettext_noop("Enable the UBus interface."), NULL }, { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL }, { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL }, { '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL }, { LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL }, + { LOPT_SHARED_NET, ARG_DUP, "<iface>|<addr>,<addr>", gettext_noop("Specify extra networks sharing a broadcast domain for DHCP"), NULL}, { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL }, { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL }, { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL }, @@ -440,6 +456,7 @@ static struct { { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL }, { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL }, { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL }, + { LOPT_SINGLE_PORT, OPT_SINGLE_PORT, NULL, gettext_noop("Use only one port for TFTP server."), NULL }, { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL }, { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL }, { LOPT_REBIND, OPT_NO_REBIND, NULL, gettext_noop("Stop DNS rebinding. Filter private IP ranges when resolving."), NULL }, @@ -447,6 +464,7 @@ static struct { { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL }, { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL }, { LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL }, + { LOPT_NAME_MATCH, ARG_DUP, "set:<tag>,<string>[*]", gettext_noop("Set tag if client provides given name."), NULL }, { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL }, { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL }, { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL }, @@ -464,11 +482,13 @@ static struct { { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL }, { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL }, { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL }, + { LOPT_IGNORE_CLID, OPT_IGNORE_CLID, NULL, gettext_noop("Ignore client identifier option sent by DHCP clients."), NULL }, { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL }, { LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL }, { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL }, { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL }, { LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL }, + { LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL }, { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL }, { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL }, { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL }, @@ -482,12 +502,9 @@ static struct { { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL }, { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL }, { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL }, - { LOPT_DNSSEC_CHECK, OPT_DNSSEC_NO_SIGN, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL }, + { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL }, { LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL }, { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL }, -#ifdef OPTION6_PREFIX_CLASS - { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL }, -#endif { LOPT_RA_PARAM, ARG_DUP, "<iface>,[mtu:<value>|<interface>|off,][<prio>,]<intval>[,<lifetime>]", gettext_noop("Set MTU, priority, resend-interval and router-lifetime"), NULL }, { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL }, { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL }, @@ -497,6 +514,10 @@ static struct { { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL }, { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL }, { LOPT_REPLY_DELAY, ARG_ONE, "<integer>", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL }, + { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL }, + { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL }, + { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL }, + { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL }, { 0, 0, NULL, NULL, NULL } }; @@ -559,14 +580,15 @@ static void *opt_malloc(size_t size) return ret; } -static char *opt_string_alloc(char *cp) +static char *opt_string_alloc(const char *cp) { char *ret = NULL; + size_t len; - if (cp && strlen(cp) != 0) + if (cp && (len = strlen(cp)) != 0) { - ret = opt_malloc(strlen(cp)+1); - strcpy(ret, cp); + ret = opt_malloc(len+1); + memcpy(ret, cp, len+1); /* restore hidden metachars */ unhide_metas(ret); @@ -741,15 +763,15 @@ static void do_usage(void) } #define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0) +#define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0) +#define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0) static char *parse_mysockaddr(char *arg, union mysockaddr *addr) { if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0) addr->sa.sa_family = AF_INET; -#ifdef HAVE_IPV6 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0) addr->sa.sa_family = AF_INET6; -#endif else return _("bad address"); @@ -761,10 +783,8 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a int source_port = 0, serv_port = NAMESERVER_PORT; char *portno, *source; char *interface_opt = NULL; -#ifdef HAVE_IPV6 int scope_index = 0; char *scope_id; -#endif if (!arg || strlen(arg) == 0) { @@ -782,9 +802,7 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a !atoi_check16(portno, &serv_port)) return _("bad port"); -#ifdef HAVE_IPV6 scope_id = split_chr(arg, '%'); -#endif if (source) { interface_opt = split_chr(source, '@'); @@ -792,7 +810,7 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a if (interface_opt) { #if defined(SO_BINDTODEVICE) - strncpy(interface, interface_opt, IF_NAMESIZE - 1); + safe_strncpy(interface, interface_opt, IF_NAMESIZE); #else return _("interface binding not supported"); #endif @@ -821,14 +839,13 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a return _("interface can only be specified once"); source_addr->in.sin_addr.s_addr = INADDR_ANY; - strncpy(interface, source, IF_NAMESIZE - 1); + safe_strncpy(interface, source, IF_NAMESIZE); #else return _("interface binding not supported"); #endif } } } -#ifdef HAVE_IPV6 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0) { if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0) @@ -856,14 +873,13 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a return _("interface can only be specified once"); source_addr->in6.sin6_addr = in6addr_any; - strncpy(interface, source, IF_NAMESIZE - 1); + safe_strncpy(interface, source, IF_NAMESIZE); #else return _("interface binding not supported"); #endif } } } -#endif else return _("bad address"); @@ -894,6 +910,8 @@ static struct server *add_rev4(struct in_addr addr, int msize) p += sprintf(p, "%d.", (a >> 24) & 0xff); break; default: + free(serv->domain); + free(serv); return NULL; } @@ -948,6 +966,116 @@ static char *set_prefix(char *arg) return arg; } +static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next) +{ + struct dhcp_netid *tt; + tt = opt_malloc(sizeof (struct dhcp_netid)); + tt->net = opt_string_alloc(net); + tt->next = next; + return tt; +} + +static void dhcp_netid_free(struct dhcp_netid *nid) +{ + while (nid) + { + struct dhcp_netid *tmp = nid; + nid = nid->next; + free(tmp->net); + free(tmp); + } +} + +/* Parse one or more tag:s before parameters. + * Moves arg to the end of tags. */ +static struct dhcp_netid * dhcp_tags(char **arg) +{ + struct dhcp_netid *id = NULL; + + while (is_tag_prefix(*arg)) + { + char *comma = split(*arg); + id = dhcp_netid_create((*arg)+4, id); + *arg = comma; + }; + if (!*arg) + { + dhcp_netid_free(id); + id = NULL; + } + return id; +} + +static void dhcp_netid_list_free(struct dhcp_netid_list *netid) +{ + while (netid) + { + struct dhcp_netid_list *tmplist = netid; + netid = netid->next; + dhcp_netid_free(tmplist->list); + free(tmplist); + } +} + +static void dhcp_config_free(struct dhcp_config *config) +{ + if (config) + { + struct hwaddr_config *hwaddr = config->hwaddr; + + while (hwaddr) + { + struct hwaddr_config *tmp = hwaddr; + hwaddr = hwaddr->next; + free(tmp); + } + + dhcp_netid_list_free(config->netid); + dhcp_netid_free(config->filter); + + if (config->flags & CONFIG_CLID) + free(config->clid); + +#ifdef HAVE_DHCP6 + if (config->flags & CONFIG_ADDR6) + { + struct addrlist *addr, *tmp; + + for (addr = config->addr6; addr; addr = tmp) + { + tmp = addr->next; + free(addr); + } + } +#endif + + free(config); + } +} + +static void dhcp_context_free(struct dhcp_context *ctx) +{ + if (ctx) + { + dhcp_netid_free(ctx->filter); + free(ctx->netid.net); +#ifdef HAVE_DHCP6 + free(ctx->template_interface); +#endif + free(ctx); + } +} + +static void dhcp_opt_free(struct dhcp_opt *opt) +{ + if (opt->flags & DHOPT_VENDOR) + free(opt->u.vendor_class); + dhcp_netid_free(opt->netid); + free(opt->val); + free(opt); +} + + /* This is too insanely large to keep in-line in the switch */ static int parse_dhcp_opt(char *errstr, char *arg, int flags) { @@ -955,7 +1083,6 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) char lenchar = 0, *cp; int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots; char *comma = NULL; - struct dhcp_netid *np = NULL; u16 opt_len = 0; int is6 = 0; int option_ok = 0; @@ -1042,14 +1169,9 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) } else { - new->netid = opt_malloc(sizeof (struct dhcp_netid)); /* allow optional "net:" or "tag:" for consistency */ - if (is_tag_prefix(arg)) - new->netid->net = opt_string_alloc(arg+4); - else - new->netid->net = opt_string_alloc(set_prefix(arg)); - new->netid->next = np; - np = new->netid; + const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg); + new->netid = dhcp_netid_create(name, new->netid); } arg = comma; @@ -1059,7 +1181,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) if (is6) { if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE)) - ret_err(_("unsupported encapsulation for IPv6 option")); + goto_err(_("unsupported encapsulation for IPv6 option")); if (opt_len == 0 && !(new->flags & DHOPT_RFC3925)) @@ -1073,13 +1195,13 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) /* option may be missing with rfc3925 match */ if (!option_ok) - ret_err(_("bad dhcp-option")); + goto_err(_("bad dhcp-option")); if (comma) { /* characterise the value */ char c; - int found_dig = 0; + int found_dig = 0, found_colon = 0; is_addr = is_addr6 = is_hex = is_dec = is_string = 1; addrs = digs = 1; dots = 0; @@ -1093,6 +1215,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) { digs++; is_dec = is_addr = 0; + found_colon = 1; } else if (c == '/') { @@ -1102,7 +1225,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) } else if (c == '.') { - is_addr6 = is_dec = is_hex = 0; + is_dec = is_hex = 0; dots++; } else if (c == '-') @@ -1134,6 +1257,15 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) if (!found_dig) is_dec = is_addr = 0; + + if (!found_colon) + is_addr6 = 0; + +#ifdef HAVE_DHCP6 + /* NTP server option takes hex, addresses or FQDN */ + if (is6 && new->opt == OPTION6_NTP_SERVER && !is_hex) + opt_len |= is_addr6 ? OT_ADDR_LIST : OT_RFC1035_NAME; +#endif /* We know that some options take addresses */ if (opt_len & OT_ADDR_LIST) @@ -1141,10 +1273,10 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) is_string = is_dec = is_hex = 0; if (!is6 && (!is_addr || dots == 0)) - ret_err(_("bad IP address")); + goto_err(_("bad IP address")); if (is6 && !is_addr6) - ret_err(_("bad IPv6 address")); + goto_err(_("bad IPv6 address")); } /* or names */ else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING)) @@ -1237,7 +1369,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) comma = split(cp); slash = split_chr(cp, '/'); if (!inet_pton(AF_INET, cp, &in)) - ret_err(_("bad IPv4 address")); + goto_err(_("bad IPv4 address")); if (!slash) { memcpy(op, &in, INADDRSZ); @@ -1282,8 +1414,8 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) op += IN6ADDRSZ; continue; } - - ret_err(_("bad IPv6 address")); + + goto_err(_("bad IPv6 address")); } new->len = op - new->val; } @@ -1310,7 +1442,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) if (strcmp (arg, ".") != 0) { if (!(dom = canonicalise_opt(arg))) - ret_err(_("bad domain in dhcp-option")); + goto_err(_("bad domain in dhcp-option")); domlen = strlen(dom) + 2; } @@ -1357,7 +1489,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) } /* RFC 3361, enc byte is zero for names */ - if (new->opt == OPTION_SIP_SERVER) + if (new->opt == OPTION_SIP_SERVER && m) m[0] = 0; new->len = (int) len + header_size; new->val = m; @@ -1395,8 +1527,9 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) } else if (comma && (opt_len & OT_RFC1035_NAME)) { - unsigned char *p = NULL, *newp, *end; + unsigned char *p = NULL, *q, *newp, *end; int len = 0; + int header_size = (is6 && new->opt == OPTION6_NTP_SERVER) ? 4 : 0; arg = comma; comma = split(arg); @@ -1404,9 +1537,9 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) { char *dom = canonicalise_opt(arg); if (!dom) - ret_err(_("bad domain in dhcp-option")); + goto_err(_("bad domain in dhcp-option")); - newp = opt_malloc(len + strlen(dom) + 2); + newp = opt_malloc(len + header_size + strlen(dom) + 2); if (p) { @@ -1415,8 +1548,14 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) } p = newp; - end = do_rfc1035_name(p + len, dom, NULL); + q = p + len; + end = do_rfc1035_name(q + header_size, dom, NULL); *end++ = 0; + if (is6 && new->opt == OPTION6_NTP_SERVER) + { + PUTSHORT(NTP_SUBOPTION_SRV_FQDN, q); + PUTSHORT(end - q - 2, q); + } len = end - p; free(dom); @@ -1442,14 +1581,14 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) ((new->len > 255) || (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) || (new->len > 250 && (new->flags & DHOPT_RFC3925)))) - ret_err(_("dhcp-option too long")); + goto_err(_("dhcp-option too long")); if (flags == DHOPT_MATCH) { if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) || !new->netid || new->netid->next) - ret_err(_("illegal dhcp-match")); + goto_err(_("illegal dhcp-match")); if (is6) { @@ -1474,24 +1613,31 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) } return 1; +on_error: + dhcp_opt_free(new); + return 0; } #endif void set_option_bool(unsigned int opt) { - if (opt < 32) - daemon->options |= 1u << opt; - else - daemon->options2 |= 1u << (opt - 32); + option_var(opt) |= option_val(opt); } void reset_option_bool(unsigned int opt) { - if (opt < 32) - daemon->options &= ~(1u << opt); - else - daemon->options2 &= ~(1u << (opt - 32)); + option_var(opt) &= ~(option_val(opt)); +} + +static void server_list_free(struct server *list) +{ + while (list) + { + struct server *tmp = list; + list = list->next; + free(tmp); + } } static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only) @@ -1552,9 +1698,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma struct dirent *ent; char *directory, *path; struct list { - char *suffix; + char *name; struct list *next; - } *ignore_suffix = NULL, *match_suffix = NULL, *li; + } *ignore_suffix = NULL, *match_suffix = NULL, *files = NULL, *li; comma = split(arg); if (!(directory = opt_string_alloc(arg))) @@ -1576,7 +1722,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma li->next = match_suffix; match_suffix = li; /* Have to copy: buffer is overwritten */ - li->suffix = opt_string_alloc(arg+1); + li->name = opt_string_alloc(arg+1); } } else @@ -1584,7 +1730,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma li->next = ignore_suffix; ignore_suffix = li; /* Have to copy: buffer is overwritten */ - li->suffix = opt_string_alloc(arg); + li->name = opt_string_alloc(arg); } } } @@ -1609,9 +1755,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma for (li = match_suffix; li; li = li->next) { /* check for required suffices */ - size_t ls = strlen(li->suffix); + size_t ls = strlen(li->name); if (len > ls && - strcmp(li->suffix, &ent->d_name[len - ls]) == 0) + strcmp(li->name, &ent->d_name[len - ls]) == 0) break; } if (!li) @@ -1621,9 +1767,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma for (li = ignore_suffix; li; li = li->next) { /* check for proscribed suffices */ - size_t ls = strlen(li->suffix); + size_t ls = strlen(li->name); if (len > ls && - strcmp(li->suffix, &ent->d_name[len - ls]) == 0) + strcmp(li->name, &ent->d_name[len - ls]) == 0) break; } if (li) @@ -1640,25 +1786,44 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma /* only reg files allowed. */ if (S_ISREG(buf.st_mode)) - one_file(path, 0); - - free(path); + { + /* sort files into order. */ + struct list **up, *new = opt_malloc(sizeof(struct list)); + new->name = path; + + for (up = &files, li = files; li; up = &li->next, li = li->next) + if (strcmp(li->name, path) >=0) + break; + + new->next = li; + *up = new; + } + } - + + for (li = files; li; li = li->next) + one_file(li->name, 0); + closedir(dir_stream); free(directory); for(; ignore_suffix; ignore_suffix = li) { li = ignore_suffix->next; - free(ignore_suffix->suffix); + free(ignore_suffix->name); free(ignore_suffix); } for(; match_suffix; match_suffix = li) { li = match_suffix->next; - free(match_suffix->suffix); + free(match_suffix->name); free(match_suffix); } + for(; files; files = li) + { + li = files->next; + free(files->name); + free(files); + } break; } @@ -1675,13 +1840,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma /* has subnet+len */ err = parse_mysockaddr(arg, &new->addr); if (err) - ret_err(err); + ret_err_free(err, new); if (!atoi_check(end, &new->mask)) - ret_err(gen_err); + ret_err_free(gen_err, new); new->addr_used = 1; } else if (!atoi_check(arg, &new->mask)) - ret_err(gen_err); + ret_err_free(gen_err, new); daemon->add_subnet4 = new; @@ -1693,15 +1858,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma /* has subnet+len */ err = parse_mysockaddr(comma, &new->addr); if (err) - ret_err(err); + ret_err_free(err, new); if (!atoi_check(end, &new->mask)) - ret_err(gen_err); + ret_err_free(gen_err, new); new->addr_used = 1; } else { if (!atoi_check(comma, &new->mask)) - ret_err(gen_err); + ret_err_free(gen_err, new); } daemon->add_subnet6 = new; @@ -1716,7 +1881,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma else daemon->dbus_name = DNSMASQ_SERVICE; break; - + + case LOPT_UBUS: /* --enable-ubus */ + set_option_bool(OPT_UBUS); + if (arg) + daemon->ubus_name = opt_string_alloc(arg); + else + daemon->ubus_name = DNSMASQ_UBUS_NAME; + break; + case '8': /* --log-facility */ /* may be a filename */ if (strchr(arg, '/') || strcmp (arg, "-") == 0) @@ -1737,7 +1910,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma #endif } break; - + case 'x': /* --pid-file */ daemon->runfile = opt_string_alloc(arg); break; @@ -1808,6 +1981,14 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma ret_err(_("bad MX target")); break; + case LOPT_DUMPFILE: /* --dumpfile */ + daemon->dump_file = opt_string_alloc(arg); + break; + + case LOPT_DUMPMASK: /* --dumpmask */ + daemon->dump_mask = strtol(arg, NULL, 0); + break; + #ifdef HAVE_DHCP case 'l': /* --dhcp-leasefile */ daemon->lease_file = opt_string_alloc(arg); @@ -1816,9 +1997,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma /* Sorry about the gross pre-processor abuse */ case '6': /* --dhcp-script */ case LOPT_LUASCRIPT: /* --dhcp-luascript */ -# if defined(NO_FORK) - ret_err(_("cannot run scripts under uClinux")); -# elif !defined(HAVE_SCRIPT) +# if !defined(HAVE_SCRIPT) ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts")); # else if (option == LOPT_LUASCRIPT) @@ -1875,47 +2054,42 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma break; } - -#ifdef HAVE_AUTH case LOPT_AUTHSERV: /* --auth-server */ - if (!(comma = split(arg))) - ret_err(gen_err); + comma = split(arg); daemon->authserver = opt_string_alloc(arg); - arg = comma; - do { - struct iname *new = opt_malloc(sizeof(struct iname)); - comma = split(arg); - new->name = NULL; - unhide_metas(arg); - if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0) - new->addr.sa.sa_family = AF_INET; -#ifdef HAVE_IPV6 - else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0) - new->addr.sa.sa_family = AF_INET6; -#endif - else - { - char *fam = split_chr(arg, '/'); - new->name = opt_string_alloc(arg); - new->addr.sa.sa_family = 0; - if (fam) - { - if (strcmp(fam, "4") == 0) - new->addr.sa.sa_family = AF_INET; -#ifdef HAVE_IPV6 - else if (strcmp(fam, "6") == 0) - new->addr.sa.sa_family = AF_INET6; -#endif - else - ret_err(gen_err); - } - } - new->next = daemon->authinterface; - daemon->authinterface = new; - - arg = comma; - } while (arg); + + while ((arg = comma)) + { + struct iname *new = opt_malloc(sizeof(struct iname)); + comma = split(arg); + new->name = NULL; + unhide_metas(arg); + if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0) + new->addr.sa.sa_family = AF_INET; + else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0) + new->addr.sa.sa_family = AF_INET6; + else + { + char *fam = split_chr(arg, '/'); + new->name = opt_string_alloc(arg); + new->addr.sa.sa_family = 0; + if (fam) + { + if (strcmp(fam, "4") == 0) + new->addr.sa.sa_family = AF_INET; + else if (strcmp(fam, "6") == 0) + new->addr.sa.sa_family = AF_INET6; + else + { + free(new->name); + ret_err_free(gen_err, new); + } + } + } + new->next = daemon->authinterface; + daemon->authinterface = new; + }; break; @@ -1954,7 +2128,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma int is_exclude = 0; char *prefix; struct addrlist *subnet = NULL; - struct all_addr addr; + union all_addr addr; comma = split(arg); prefix = split_chr(arg, '/'); @@ -1968,20 +2142,18 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma arg = arg+8; } - if (inet_pton(AF_INET, arg, &addr.addr.addr4)) + if (inet_pton(AF_INET, arg, &addr.addr4)) { subnet = opt_malloc(sizeof(struct addrlist)); subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen; subnet->flags = ADDRLIST_LITERAL; } -#ifdef HAVE_IPV6 - else if (inet_pton(AF_INET6, arg, &addr.addr.addr6)) + else if (inet_pton(AF_INET6, arg, &addr.addr6)) { subnet = opt_malloc(sizeof(struct addrlist)); subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen; subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6; } -#endif else { struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list)); @@ -1993,10 +2165,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma { if (prefixlen == 4) name->flags &= ~AUTH6; -#ifdef HAVE_IPV6 else if (prefixlen == 6) name->flags &= ~AUTH4; -#endif else ret_err(gen_err); } @@ -2051,7 +2221,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } break; -#endif case 's': /* --domain */ case LOPT_SYNTH: /* --synth-domain */ @@ -2080,7 +2249,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma arg = split(netpart); if (!atoi_check(netpart, &msize)) - ret_err(gen_err); + ret_err_free(gen_err, new); else if (inet_pton(AF_INET, comma, &new->start)) { int mask = (1 << (32 - msize)) - 1; @@ -2093,18 +2262,18 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma { if (!(new->prefix = canonicalise_opt(arg)) || strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN) - ret_err(_("bad prefix")); + ret_err_free(_("bad prefix"), new); } else if (strcmp(arg, "local") != 0 || (msize != 8 && msize != 16 && msize != 24)) - ret_err(gen_err); + ret_err_free(gen_err, new); else { /* generate the equivalent of local=/xxx.yyy.zzz.in-addr.arpa/ */ struct server *serv = add_rev4(new->start, msize); if (!serv) - ret_err(_("bad prefix")); + ret_err_free(_("bad prefix"), new); serv->flags |= SERV_NO_ADDR; @@ -2118,7 +2287,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } } } -#ifdef HAVE_IPV6 else if (inet_pton(AF_INET6, comma, &new->start6)) { u64 mask = (1LLU << (128 - msize)) - 1LLU; @@ -2134,17 +2302,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma setaddr6part(&new->end6, addrpart | mask); if (msize < 64) - ret_err(gen_err); + ret_err_free(gen_err, new); else if (arg) { if (option != 's') { if (!(new->prefix = canonicalise_opt(arg)) || strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN) - ret_err(_("bad prefix")); + ret_err_free(_("bad prefix"), new); } else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0)) - ret_err(gen_err); + ret_err_free(gen_err, new); else { /* generate the equivalent of @@ -2162,9 +2330,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } } } -#endif else - ret_err(gen_err); + ret_err_free(gen_err, new); } else { @@ -2178,26 +2345,24 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma if (!arg) new->end.s_addr = new->start.s_addr; else if (!inet_pton(AF_INET, arg, &new->end)) - ret_err(gen_err); + ret_err_free(gen_err, new); } -#ifdef HAVE_IPV6 else if (inet_pton(AF_INET6, comma, &new->start6)) { new->is6 = 1; if (!arg) memcpy(&new->end6, &new->start6, IN6ADDRSZ); else if (!inet_pton(AF_INET6, arg, &new->end6)) - ret_err(gen_err); + ret_err_free(gen_err, new); } -#endif else - ret_err(gen_err); + ret_err_free(gen_err, new); if (option != 's' && prefstr) { if (!(new->prefix = canonicalise_opt(prefstr)) || strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN) - ret_err(_("bad prefix")); + ret_err_free(_("bad prefix"), new); } } @@ -2212,7 +2377,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma char *star; new->next = daemon->synth_domains; daemon->synth_domains = new; - if ((star = strrchr(new->prefix, '*')) && *(star+1) == 0) + if (new->prefix && + (star = strrchr(new->prefix, '*')) + && *(star+1) == 0) { *star = 0; new->indexed = 1; @@ -2346,7 +2513,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma new->addr.in.sin_len = sizeof(new->addr.in); #endif } -#ifdef HAVE_IPV6 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0) { new->addr.sa.sa_family = AF_INET6; @@ -2357,9 +2523,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma new->addr.in6.sin6_len = sizeof(new->addr.in6); #endif } -#endif else - ret_err(gen_err); + ret_err_free(gen_err, new); new->used = 0; if (option == 'a') @@ -2400,7 +2565,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma if (strcmp(arg, "#") == 0) domain = ""; else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg))) - option = '?'; + ret_err(gen_err); serv = opt_malloc(sizeof(struct server)); memset(serv, 0, sizeof(struct server)); serv->next = newlist; @@ -2430,7 +2595,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma { newlist->flags |= SERV_LITERAL_ADDRESS; if (!(newlist->flags & SERV_TYPE)) - ret_err(gen_err); + { + server_list_free(newlist); + ret_err(gen_err); + } } else if (option == LOPT_NO_REBIND) newlist->flags |= SERV_NO_REBIND; @@ -2442,22 +2610,21 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } else if (strcmp(arg, "#") == 0) - { - newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */ - if (newlist->flags & SERV_LITERAL_ADDRESS) - ret_err(gen_err); - } + newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */ else { char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags); if (err) - ret_err(err); + { + server_list_free(newlist); + ret_err(err); + } } serv = newlist; while (serv->next) { - serv->next->flags = serv->flags; + serv->next->flags |= serv->flags & ~(SERV_HAS_DOMAIN | SERV_FOR_NODOTS); serv->next->addr = serv->addr; serv->next->source_addr = serv->source_addr; strcpy(serv->next->interface, serv->interface); @@ -2474,24 +2641,25 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma int size; struct server *serv; struct in_addr addr4; -#ifdef HAVE_IPV6 struct in6_addr addr6; -#endif unhide_metas(arg); - if (!arg || !(comma=split(arg)) || !(string = split_chr(arg, '/')) || !atoi_check(string, &size)) + if (!arg) ret_err(gen_err); + + comma=split(arg); + if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size)) + ret_err(gen_err); + if (inet_pton(AF_INET, arg, &addr4)) { serv = add_rev4(addr4, size); if (!serv) ret_err(_("bad prefix")); } -#ifdef HAVE_IPV6 else if (inet_pton(AF_INET6, arg, &addr6)) serv = add_rev6(&addr6, size); -#endif else ret_err(gen_err); @@ -2532,7 +2700,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma if (strcmp(arg, "#") == 0 || !*arg) domain = ""; else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg))) - option = '?'; + ret_err(gen_err); ipsets->next = opt_malloc(sizeof(struct ipsets)); ipsets = ipsets->next; memset(ipsets, 0, sizeof(struct ipsets)); @@ -2547,13 +2715,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma memset(ipsets, 0, sizeof(struct ipsets)); ipsets->domain = ""; } + if (!arg || !*arg) - { - option = '?'; - break; - } - size = 2; - for (end = arg; *end; ++end) + ret_err(gen_err); + + for (size = 2, end = arg; *end; ++end) if (*end == ',') ++size; @@ -2586,8 +2752,14 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma if (size < 0) size = 0; - else if (size > 10000) - size = 10000; + + /* Note that for very large cache sizes, the malloc() + will overflow. For the size of the cache record + at the time this was noted, the value of "very large" + was 46684428. Limit to an order of magnitude less than + that to be safe from changes to the cache record. */ + if (size > 5000000) + size = 5000000; daemon->cachesize = size; } @@ -2774,6 +2946,44 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } #ifdef HAVE_DHCP + case LOPT_SHARED_NET: /* --shared-network */ + { + struct shared_network *new = opt_malloc(sizeof(struct shared_network)); + +#ifdef HAVE_DHCP6 + new->shared_addr.s_addr = 0; +#endif + new->if_index = 0; + + if (!(comma = split(arg))) + { + snerr: + free(new); + ret_err(_("bad shared-network")); + } + + if (inet_pton(AF_INET, comma, &new->shared_addr)) + { + if (!inet_pton(AF_INET, arg, &new->match_addr) && + !(new->if_index = if_nametoindex(arg))) + goto snerr; + } +#ifdef HAVE_DHCP6 + else if (inet_pton(AF_INET6, comma, &new->shared_addr6)) + { + if (!inet_pton(AF_INET6, arg, &new->match_addr6) && + !(new->if_index = if_nametoindex(arg))) + goto snerr; + } +#endif + else + goto snerr; + + new->next = daemon->shared_networks; + daemon->shared_networks = new; + break; + } + case 'F': /* --dhcp-range */ { int k, leasepos = 2; @@ -2781,13 +2991,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context)); memset (new, 0, sizeof(*new)); - new->lease_time = DEFLEASE; - - if (!arg) - { - option = '?'; - break; - } while(1) { @@ -2801,21 +3004,19 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma { if (is_tag_prefix(arg)) { - struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid)); - tt->net = opt_string_alloc(arg+4); - tt->next = new->filter; /* ignore empty tag */ - if (tt->net) - new->filter = tt; + if (arg[4]) + new->filter = dhcp_netid_create(arg+4, new->filter); } else { if (new->netid.net) - ret_err(_("only one tag allowed")); - else if (strstr(arg, "set:") == arg) - new->netid.net = opt_string_alloc(arg+4); + { + dhcp_context_free(new); + ret_err(_("only one tag allowed")); + } else - new->netid.net = opt_string_alloc(arg); + new->netid.net = opt_string_alloc(set_prefix(arg)); } arg = comma; } @@ -2831,11 +3032,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma break; if (k < 2) - ret_err(_("bad dhcp-range")); + { + dhcp_context_free(new); + ret_err(_("bad dhcp-range")); + } if (inet_pton(AF_INET, a[0], &new->start)) { new->next = daemon->dhcp; + new->lease_time = DEFLEASE; daemon->dhcp = new; new->end = new->start; if (strcmp(a[1], "static") == 0) @@ -2843,7 +3048,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma else if (strcmp(a[1], "proxy") == 0) new->flags |= CONTEXT_PROXY; else if (!inet_pton(AF_INET, a[1], &new->end)) - ret_err(_("bad dhcp-range")); + { + dhcp_context_free(new); + ret_err(_("bad dhcp-range")); + } if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr)) { @@ -2858,7 +3066,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma new->flags |= CONTEXT_NETMASK; leasepos = 3; if (!is_same_net(new->start, new->end, new->netmask)) - ret_err(_("inconsistent DHCP range")); + { + dhcp_context_free(new); + ret_err(_("inconsistent DHCP range")); + } if (k >= 4 && strchr(a[3], '.') && @@ -2872,9 +3083,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma #ifdef HAVE_DHCP6 else if (inet_pton(AF_INET6, a[0], &new->start6)) { + const char *err = NULL; + new->flags |= CONTEXT_V6; new->prefix = 64; /* default */ new->end6 = new->start6; + new->lease_time = DEFLEASE6; new->next = daemon->dhcp6; daemon->dhcp6 = new; @@ -2917,19 +3131,24 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } } - if (new->prefix != 64) + if (new->prefix > 64) { if (new->flags & CONTEXT_RA) - ret_err(_("prefix length must be exactly 64 for RA subnets")); + err=(_("prefix length must be exactly 64 for RA subnets")); else if (new->flags & CONTEXT_TEMPLATE) - ret_err(_("prefix length must be exactly 64 for subnet constructors")); + err=(_("prefix length must be exactly 64 for subnet constructors")); } - - if (new->prefix < 64) - ret_err(_("prefix length must be at least 64")); + else if (new->prefix < 64) + err=(_("prefix length must be at least 64")); - if (!is_same_net6(&new->start6, &new->end6, new->prefix)) - ret_err(_("inconsistent DHCPv6 range")); + if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix)) + err=(_("inconsistent DHCPv6 range")); + + if (err) + { + dhcp_context_free(new); + ret_err(err); + } /* dhcp-range=:: enables DHCP stateless on any interface */ if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE)) @@ -2940,7 +3159,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma struct in6_addr zero; memset(&zero, 0, sizeof(zero)); if (!is_same_net6(&zero, &new->start6, new->prefix)) - ret_err(_("prefix must be zero with \"constructor:\" argument")); + { + dhcp_context_free(new); + ret_err(_("prefix must be zero with \"constructor:\" argument")); + } } if (addr6part(&new->start6) > addr6part(&new->end6)) @@ -2952,15 +3174,24 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } #endif else - ret_err(_("bad dhcp-range")); + { + dhcp_context_free(new); + ret_err(_("bad dhcp-range")); + } if (leasepos < k) { if (leasepos != k-1) - ret_err(_("bad dhcp-range")); + { + dhcp_context_free(new); + ret_err(_("bad dhcp-range")); + } if (strcmp(a[leasepos], "infinite") == 0) - new->lease_time = 0xffffffff; + { + new->lease_time = 0xffffffff; + new->flags |= CONTEXT_SETLEASE; + } else if (strcmp(a[leasepos], "deprecated") == 0) new->flags |= CONTEXT_DEPRECATE; else @@ -2996,9 +3227,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma break; if (*cp || (leasepos+1 < k)) - ret_err(_("bad dhcp-range")); + ret_err_free(_("bad dhcp-range"), new); new->lease_time = atoi(a[leasepos]) * fac; + new->flags |= CONTEXT_SETLEASE; /* Leases of a minute or less confuse some clients, notably Apple's */ if (new->lease_time < 120) @@ -3006,14 +3238,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } } } + break; } case LOPT_BANK: case 'G': /* --dhcp-host */ { - int j, k = 0; - char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; struct dhcp_config *new; struct in_addr in; @@ -3023,185 +3254,223 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0; new->hwaddr = NULL; new->netid = NULL; - - if ((a[0] = arg)) - for (k = 1; k < 7; k++) - if (!(a[k] = split(a[k-1]))) - break; - - for (j = 0; j < k; j++) - if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */ - { - char *arg = a[j]; - - if ((arg[0] == 'i' || arg[0] == 'I') && - (arg[1] == 'd' || arg[1] == 'D') && - arg[2] == ':') - { - if (arg[3] == '*') - new->flags |= CONFIG_NOCLID; - else - { - int len; - arg += 3; /* dump id: */ - if (strchr(arg, ':')) - len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL); - else - { - unhide_metas(arg); - len = (int) strlen(arg); - } - - if (len == -1) - ret_err(_("bad hex constant")); - else if ((new->clid = opt_malloc(len))) - { - new->flags |= CONFIG_CLID; - new->clid_len = len; - memcpy(new->clid, arg, len); - } - } - } - /* dhcp-host has strange backwards-compat needs. */ - else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg) - { - struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid)); - struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list)); - newtag->net = opt_malloc(strlen(arg + 4) + 1); - newlist->next = new->netid; - new->netid = newlist; - newlist->list = newtag; - strcpy(newtag->net, arg+4); - unhide_metas(newtag->net); - } - else if (strstr(arg, "tag:") == arg) - ret_err(_("cannot match tags in --dhcp-host")); + new->filter = NULL; + new->clid = NULL; #ifdef HAVE_DHCP6 - else if (arg[0] == '[' && arg[strlen(arg)-1] == ']') - { - arg[strlen(arg)-1] = 0; - arg++; - - if (!inet_pton(AF_INET6, arg, &new->addr6)) - ret_err(_("bad IPv6 address")); + new->addr6 = NULL; +#endif - for (i= 0; i < 8; i++) - if (new->addr6.s6_addr[i] != 0) - break; + while (arg) + { + comma = split(arg); + if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */ + { + if ((arg[0] == 'i' || arg[0] == 'I') && + (arg[1] == 'd' || arg[1] == 'D') && + arg[2] == ':') + { + if (arg[3] == '*') + new->flags |= CONFIG_NOCLID; + else + { + int len; + arg += 3; /* dump id: */ + if (strchr(arg, ':')) + len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL); + else + { + unhide_metas(arg); + len = (int) strlen(arg); + } + + if (len == -1) + { + dhcp_config_free(new); + ret_err(_("bad hex constant")); + } + else if ((new->clid = opt_malloc(len))) + { + new->flags |= CONFIG_CLID; + new->clid_len = len; + memcpy(new->clid, arg, len); + } + } + } + /* dhcp-host has strange backwards-compat needs. */ + else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg) + { + struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list)); + newlist->next = new->netid; + new->netid = newlist; + newlist->list = dhcp_netid_create(arg+4, NULL); + } + else if (strstr(arg, "tag:") == arg) + new->filter = dhcp_netid_create(arg+4, new->filter); + +#ifdef HAVE_DHCP6 + else if (arg[0] == '[' && arg[strlen(arg)-1] == ']') + { + char *pref; + struct in6_addr in6; + struct addrlist *new_addr; + + arg[strlen(arg)-1] = 0; + arg++; + pref = split_chr(arg, '/'); + + if (!inet_pton(AF_INET6, arg, &in6)) + { + dhcp_config_free(new); + ret_err(_("bad IPv6 address")); + } - /* set WILDCARD if network part all zeros */ - if (i == 8) - new->flags |= CONFIG_WILDCARD; + new_addr = opt_malloc(sizeof(struct addrlist)); + new_addr->next = new->addr6; + new_addr->flags = 0; + new_addr->addr.addr6 = in6; + new->addr6 = new_addr; + + if (pref) + { + u64 addrpart = addr6part(&in6); + + if (!atoi_check(pref, &new_addr->prefixlen) || + new_addr->prefixlen > 128 || + ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0) + { + dhcp_config_free(new); + ret_err(_("bad IPv6 prefix")); + } + + new_addr->flags |= ADDRLIST_PREFIX; + } - new->flags |= CONFIG_ADDR6; - } + for (i= 0; i < 8; i++) + if (in6.s6_addr[i] != 0) + break; + + /* set WILDCARD if network part all zeros */ + if (i == 8) + new_addr->flags |= ADDRLIST_WILDCARD; + + new->flags |= CONFIG_ADDR6; + } #endif - else - { - struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config)); - if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX, - &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1) - ret_err(_("bad hex constant")); - else - { - - newhw->next = new->hwaddr; - new->hwaddr = newhw; - } - } - } - else if (strchr(a[j], '.') && (inet_pton(AF_INET, a[j], &in) > 0)) - { - struct dhcp_config *configs; - - new->addr = in; - new->flags |= CONFIG_ADDR; - - /* If the same IP appears in more than one host config, then DISCOVER - for one of the hosts will get the address, but REQUEST will be NAKed, - since the address is reserved by the other one -> protocol loop. */ - for (configs = daemon->dhcp_conf; configs; configs = configs->next) - if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr) + else { - sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in)); - return 0; - } - } - else - { - char *cp, *lastp = NULL, last = 0; - int fac = 1, isdig = 0; - - if (strlen(a[j]) > 1) - { - lastp = a[j] + strlen(a[j]) - 1; - last = *lastp; - switch (last) + struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config)); + if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX, + &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1) + { + free(newhw); + dhcp_config_free(new); + ret_err(_("bad hex constant")); + } + else + { + newhw->next = new->hwaddr; + new->hwaddr = newhw; + } + } + } + else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0)) + { + struct dhcp_config *configs; + + new->addr = in; + new->flags |= CONFIG_ADDR; + + /* If the same IP appears in more than one host config, then DISCOVER + for one of the hosts will get the address, but REQUEST will be NAKed, + since the address is reserved by the other one -> protocol loop. */ + for (configs = daemon->dhcp_conf; configs; configs = configs->next) + if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr) { - case 'w': - case 'W': - fac *= 7; - /* fall through */ - case 'd': - case 'D': - fac *= 24; - /* fall through */ - case 'h': - case 'H': - fac *= 60; - /* fall through */ - case 'm': - case 'M': - fac *= 60; - /* fall through */ - case 's': - case 'S': - *lastp = 0; - } - } - - for (cp = a[j]; *cp; cp++) - if (isdigit((unsigned char)*cp)) - isdig = 1; - else if (*cp != ' ') - break; + sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in)); + return 0; + } + } + else + { + char *cp, *lastp = NULL, last = 0; + int fac = 1, isdig = 0; + + if (strlen(arg) > 1) + { + lastp = arg + strlen(arg) - 1; + last = *lastp; + switch (last) + { + case 'w': + case 'W': + fac *= 7; + /* fall through */ + case 'd': + case 'D': + fac *= 24; + /* fall through */ + case 'h': + case 'H': + fac *= 60; + /* fall through */ + case 'm': + case 'M': + fac *= 60; + /* fall through */ + case 's': + case 'S': + *lastp = 0; + } + } + + for (cp = arg; *cp; cp++) + if (isdigit((unsigned char)*cp)) + isdig = 1; + else if (*cp != ' ') + break; + + if (*cp) + { + if (lastp) + *lastp = last; + if (strcmp(arg, "infinite") == 0) + { + new->lease_time = 0xffffffff; + new->flags |= CONFIG_TIME; + } + else if (strcmp(arg, "ignore") == 0) + new->flags |= CONFIG_DISABLE; + else + { + if (!(new->hostname = canonicalise_opt(arg)) || + !legal_hostname(new->hostname)) + { + dhcp_config_free(new); + ret_err(_("bad DHCP host name")); + } + + new->flags |= CONFIG_NAME; + new->domain = strip_hostname(new->hostname); + } + } + else if (isdig) + { + new->lease_time = atoi(arg) * fac; + /* Leases of a minute or less confuse + some clients, notably Apple's */ + if (new->lease_time < 120) + new->lease_time = 120; + new->flags |= CONFIG_TIME; + } + } + + arg = comma; + } - if (*cp) - { - if (lastp) - *lastp = last; - if (strcmp(a[j], "infinite") == 0) - { - new->lease_time = 0xffffffff; - new->flags |= CONFIG_TIME; - } - else if (strcmp(a[j], "ignore") == 0) - new->flags |= CONFIG_DISABLE; - else - { - if (!(new->hostname = canonicalise_opt(a[j])) || - !legal_hostname(new->hostname)) - ret_err(_("bad DHCP host name")); - - new->flags |= CONFIG_NAME; - new->domain = strip_hostname(new->hostname); - } - } - else if (isdig) - { - new->lease_time = atoi(a[j]) * fac; - /* Leases of a minute or less confuse - some clients, notably Apple's */ - if (new->lease_time < 120) - new->lease_time = 120; - new->flags |= CONFIG_TIME; - } - } - daemon->dhcp_conf = new; break; } - + case LOPT_TAG_IF: /* --tag-if */ { struct tag_if *new = opt_malloc(sizeof(struct tag_if)); @@ -3234,10 +3503,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } else { - struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid)); - newtag->net = opt_malloc(len - 3); - strcpy(newtag->net, arg+4); - unhide_metas(newtag->net); + struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL); if (strstr(arg, "set:") == arg) { @@ -3254,7 +3520,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma else { new->set = NULL; - free(newtag); + dhcp_netid_free(newtag); break; } } @@ -3263,7 +3529,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } if (!new->set) - ret_err(_("bad tag-if")); + { + dhcp_netid_free(new->tag); + dhcp_netid_list_free(new->set); + ret_err_free(_("bad tag-if"), new); + } break; } @@ -3277,22 +3547,41 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma option == LOPT_FORCE ? DHOPT_FORCE : (option == LOPT_MATCH ? DHOPT_MATCH : (option == LOPT_OPTS ? DHOPT_BANK : 0))); - - case 'M': /* --dhcp-boot */ + + case LOPT_NAME_MATCH: /* --dhcp-name-match */ { - struct dhcp_netid *id = NULL; - while (is_tag_prefix(arg)) + struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name)); + struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid)); + ssize_t len; + + if (!(comma = split(arg)) || (len = strlen(comma)) == 0) + ret_err(gen_err); + + new->wildcard = 0; + new->netid = id; + id->net = opt_string_alloc(set_prefix(arg)); + + if (comma[len-1] == '*') { - struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid)); - newid->next = id; - id = newid; - comma = split(arg); - newid->net = opt_string_alloc(arg+4); - arg = comma; - }; + comma[len-1] = 0; + new->wildcard = 1; + } + new->name = opt_string_alloc(comma); + + new->next = daemon->dhcp_name_match; + daemon->dhcp_name_match = new; + + break; + } + + case 'M': /* --dhcp-boot */ + { + struct dhcp_netid *id = dhcp_tags(&arg); if (!arg) - ret_err(gen_err); + { + ret_err(gen_err); + } else { char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL; @@ -3338,19 +3627,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */ { - struct dhcp_netid *id = NULL; - while (is_tag_prefix(arg)) - { - struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid)); - newid->next = id; - id = newid; - comma = split(arg); - newid->net = opt_string_alloc(arg+4); - arg = comma; - }; + struct dhcp_netid *id = dhcp_tags(&arg); if (!arg) - ret_err(gen_err); + { + ret_err(gen_err); + } else { struct delay_config *new; @@ -3375,19 +3657,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma new->netid = NULL; new->opt = 10; /* PXE_MENU_PROMPT */ - - while (is_tag_prefix(arg)) - { - struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid)); - comma = split(arg); - nn->next = new->netid; - new->netid = nn; - nn->net = opt_string_alloc(arg+4); - arg = comma; - } + new->netid = dhcp_tags(&arg); if (!arg) - ret_err(gen_err); + { + dhcp_opt_free(new); + ret_err(gen_err); + } else { comma = split(arg); @@ -3423,17 +3699,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma new->netid = NULL; new->sname = NULL; new->server.s_addr = 0; + new->netid = dhcp_tags(&arg); - while (is_tag_prefix(arg)) - { - struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid)); - comma = split(arg); - nn->next = new->netid; - new->netid = nn; - nn->net = opt_string_alloc(arg+4); - arg = comma; - } - if (arg && (comma = split(arg))) { for (i = 0; CSA[i]; i++) @@ -3510,7 +3777,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma unhide_metas(comma); new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type); if (new->hwaddr_len == -1) - ret_err(gen_err); + { + free(new->netid.net); + ret_err_free(gen_err, new); + } else { new->next = daemon->dhcp_macs; @@ -3520,24 +3790,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } break; -#ifdef OPTION6_PREFIX_CLASS - case LOPT_PREF_CLSS: /* --dhcp-prefix-class */ - { - struct prefix_class *new = opt_malloc(sizeof(struct prefix_class)); - - if (!(comma = split(arg)) || - !atoi_check16(comma, &new->class)) - ret_err(gen_err); - - new->tag.net = opt_string_alloc(set_prefix(arg)); - new->next = daemon->prefix_classes; - daemon->prefix_classes = new; - - break; - } -#endif - - case 'U': /* --dhcp-vendorclass */ case 'j': /* --dhcp-userclass */ case LOPT_CIRCUIT: /* --dhcp-circuitid */ @@ -3549,7 +3801,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor)); if (!(comma = split(arg))) - ret_err(gen_err); + ret_err_free(gen_err, new); new->netid.net = opt_string_alloc(set_prefix(arg)); /* check for hex string - must digits may include : must not have nothing else, @@ -3559,7 +3811,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma if ((comma = split(arg))) { if (option != 'U' || strstr(arg, "enterprise:") != arg) - ret_err(gen_err); + { + free(new->netid.net); + ret_err_free(gen_err, new); + } else new->enterprise = atoi(arg+11); } @@ -3661,14 +3916,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } while (arg) { - struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid)); comma = split(arg); - member->next = list; - list = member; - if (is_tag_prefix(arg)) - member->net = opt_string_alloc(arg+4); - else - member->net = opt_string_alloc(arg); + list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list); arg = comma; } @@ -3682,7 +3931,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma struct addr_list *new = opt_malloc(sizeof(struct addr_list)); comma = split(arg); if (!(inet_pton(AF_INET, arg, &new->addr) > 0)) - ret_err(_("bad dhcp-proxy address")); + ret_err_free(_("bad dhcp-proxy address"), new); new->next = daemon->override_relays; daemon->override_relays = new; arg = comma; @@ -3708,7 +3957,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } #endif else - ret_err(_("Bad dhcp-relay")); + { + free(new->interface); + ret_err_free(_("Bad dhcp-relay"), new); + } break; } @@ -3748,8 +4000,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma arg = split(comma); if (!atoi_check(comma, &new->interval) || (arg && !atoi_check(arg, &new->lifetime))) + { err: - ret_err(_("bad RA-params")); + free(new->name); + ret_err_free(_("bad RA-params"), new); + } new->next = daemon->ra_interfaces; daemon->ra_interfaces = new; @@ -3790,17 +4045,15 @@ err: if ((k < 2) || (!(inet_pton(AF_INET, a[0], &new->in) > 0)) || - (!(inet_pton(AF_INET, a[1], &new->out) > 0))) - option = '?'; - - if (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)) - option = '?'; + (!(inet_pton(AF_INET, a[1], &new->out) > 0)) || + (k == 3 && !inet_pton(AF_INET, a[2], &new->mask))) + ret_err(_("missing address in alias")); if (dash && (!(inet_pton(AF_INET, dash, &new->end) > 0) || !is_same_net(new->in, new->end, new->mask) || ntohl(new->in.s_addr) > ntohl(new->end.s_addr))) - ret_err(_("invalid alias range")); + ret_err_free(_("invalid alias range"), new); break; } @@ -3830,12 +4083,10 @@ err: { if (strcmp(arg, "4") == 0) new->family = AF_INET; -#ifdef HAVE_IPV6 else if (strcmp(arg, "6") == 0) new->family = AF_INET6; -#endif else - ret_err(gen_err); + ret_err_free(gen_err, new); } new->intr = opt_string_alloc(comma); break; @@ -3867,11 +4118,19 @@ err: alias = canonicalise_opt(arg); if (!alias || !target) - ret_err(_("bad CNAME")); + { + free(target); + free(alias); + ret_err(_("bad CNAME")); + } for (new = daemon->cnames; new; new = new->next) if (hostname_isequal(new->alias, alias)) - ret_err(_("duplicate CNAME")); + { + free(target); + free(alias); + ret_err(_("duplicate CNAME")); + } new = opt_malloc(sizeof(struct cname)); new->next = daemon->cnames; daemon->cnames = new; @@ -3894,7 +4153,11 @@ err: if (!(dom = canonicalise_opt(arg)) || (comma && !(target = canonicalise_opt(comma)))) - ret_err(_("bad PTR record")); + { + free(dom); + free(target); + ret_err(_("bad PTR record")); + } else { new = opt_malloc(sizeof(struct ptr_record)); @@ -3912,7 +4175,7 @@ err: int k = 0; struct naptr *new; int order, pref; - char *name, *replace = NULL; + char *name=NULL, *replace = NULL; if ((a[0] = arg)) for (k = 1; k < 7; k++) @@ -3925,7 +4188,11 @@ err: !atoi_check16(a[1], &order) || !atoi_check16(a[2], &pref) || (k == 7 && !(replace = canonicalise_opt(a[6])))) - ret_err(_("bad NAPTR record")); + { + free(name); + free(replace); + ret_err(_("bad NAPTR record")); + } else { new = opt_malloc(sizeof(struct naptr)); @@ -3947,26 +4214,30 @@ err: struct txt_record *new; size_t len = 0; char *data; - int val; + int class; comma = split(arg); data = split(comma); new = opt_malloc(sizeof(struct txt_record)); - new->next = daemon->rr; - daemon->rr = new; + new->name = NULL; - if (!atoi_check(comma, &val) || + if (!atoi_check(comma, &class) || !(new->name = canonicalise_opt(arg)) || (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U)) - ret_err(_("bad RR record")); - - new->class = val; + { + free(new->name); + ret_err_free(_("bad RR record"), new); + } + new->len = 0; + new->class = class; + new->next = daemon->rr; + daemon->rr = new; if (data) { - new->txt=opt_malloc(len); + new->txt = opt_malloc(len); new->len = len; memcpy(new->txt, data, len); } @@ -3974,6 +4245,37 @@ err: break; } + case LOPT_CAA: /* --caa-record */ + { + struct txt_record *new; + char *tag, *value; + int flags; + + comma = split(arg); + tag = split(comma); + value = split(tag); + + new = opt_malloc(sizeof(struct txt_record)); + new->next = daemon->rr; + daemon->rr = new; + + if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg))) + ret_err(_("bad CAA record")); + + unhide_metas(tag); + unhide_metas(value); + + new->len = strlen(tag) + strlen(value) + 2; + new->txt = opt_malloc(new->len); + new->txt[0] = flags; + new->txt[1] = strlen(tag); + memcpy(&new->txt[2], tag, strlen(tag)); + memcpy(&new->txt[2 + strlen(tag)], value, strlen(value)); + new->class = T_CAA; + + break; + } + case 'Y': /* --txt-record */ { struct txt_record *new; @@ -3983,14 +4285,14 @@ err: comma = split(arg); new = opt_malloc(sizeof(struct txt_record)); - new->next = daemon->txt; - daemon->txt = new; new->class = C_IN; new->stat = 0; if (!(new->name = canonicalise_opt(arg))) - ret_err(_("bad TXT record")); + ret_err_free(_("bad TXT record"), new); + new->next = daemon->txt; + daemon->txt = new; len = comma ? strlen(comma) : 0; len += (len/255) + 1; /* room for extra counts */ new->txt = p = opt_malloc(len); @@ -4037,24 +4339,32 @@ err: arg = comma; comma = split(arg); if (!(target = canonicalise_opt(arg))) - ret_err(_("bad SRV target")); + ret_err_free(_("bad SRV target"), name); if (comma) { arg = comma; comma = split(arg); if (!atoi_check16(arg, &port)) - ret_err(_("invalid port number")); + { + free(name); + ret_err_free(_("invalid port number"), target); + } if (comma) { arg = comma; comma = split(arg); if (!atoi_check16(arg, &priority)) - ret_err(_("invalid priority")); - + { + free(name); + ret_err_free(_("invalid priority"), target); + } if (comma && !atoi_check16(comma, &weight)) - ret_err(_("invalid weight")); + { + free(name); + ret_err_free(_("invalid weight"), target); + } } } } @@ -4073,16 +4383,19 @@ err: case LOPT_HOST_REC: /* --host-record */ { - struct host_record *new = opt_malloc(sizeof(struct host_record)); - memset(new, 0, sizeof(struct host_record)); - new->ttl = -1; + struct host_record *new; if (!arg || !(comma = split(arg))) ret_err(_("Bad host-record")); + new = opt_malloc(sizeof(struct host_record)); + memset(new, 0, sizeof(struct host_record)); + new->ttl = -1; + new->flags = 0; + while (arg) { - struct all_addr addr; + union all_addr addr; char *dig; for (dig = arg; *dig != 0; dig++) @@ -4090,20 +4403,33 @@ err: break; if (*dig == 0) new->ttl = atoi(arg); - else if (inet_pton(AF_INET, arg, &addr)) - new->addr = addr.addr.addr4; -#ifdef HAVE_IPV6 - else if (inet_pton(AF_INET6, arg, &addr)) - new->addr6 = addr.addr.addr6; -#endif + else if (inet_pton(AF_INET, arg, &addr.addr4)) + { + new->addr = addr.addr4; + new->flags |= HR_4; + } + else if (inet_pton(AF_INET6, arg, &addr.addr6)) + { + new->addr6 = addr.addr6; + new->flags |= HR_6; + } else { int nomem; char *canon = canonicalise(arg, &nomem); - struct name_list *nl = opt_malloc(sizeof(struct name_list)); + struct name_list *nl; if (!canon) - ret_err(_("Bad name in host-record")); + { + struct name_list *tmp = new->names, *next; + for (tmp = new->names; tmp; tmp = next) + { + next = tmp->next; + free(tmp); + } + ret_err_free(_("Bad name in host-record"), new); + } + nl = opt_malloc(sizeof(struct name_list)); nl->name = canon; /* keep order, so that PTR record goes to first name */ nl->next = NULL; @@ -4132,17 +4458,28 @@ err: } #ifdef HAVE_DNSSEC - case LOPT_DNSSEC_STAMP: + case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */ daemon->timestamp_file = opt_string_alloc(arg); break; - case LOPT_TRUST_ANCHOR: + case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */ + if (arg) + { + if (strcmp(arg, "no") == 0) + set_option_bool(OPT_DNSSEC_IGN_NS); + else + ret_err(_("bad value for dnssec-check-unsigned")); + } + break; + + case LOPT_TRUST_ANCHOR: /* --trust-anchor */ { struct ds_config *new = opt_malloc(sizeof(struct ds_config)); char *cp, *cp1, *keyhex, *digest, *algo = NULL; int len; new->class = C_IN; + new->name = NULL; if ((comma = split(arg)) && (algo = split(comma))) { @@ -4167,7 +4504,7 @@ err: !atoi_check8(algo, &new->algo) || !atoi_check8(digest, &new->digest_type) || !(new->name = canonicalise_opt(arg))) - ret_err(_("bad trust anchor")); + ret_err_free(_("bad trust anchor"), new); /* Upper bound on length */ len = (2*strlen(keyhex))+1; @@ -4181,7 +4518,10 @@ err: else cp++; if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1) - ret_err(_("bad HEX in trust anchor")); + { + free(new->name); + ret_err_free(_("bad HEX in trust anchor"), new); + } new->next = daemon->ds; daemon->ds = new; @@ -4321,7 +4661,7 @@ static void read_file(char *file, FILE *f, int hard_opt) if (errmess) strcpy(daemon->namebuff, errmess); - if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV)) + if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV)) { sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file); if (hard_opt != 0) @@ -4650,8 +4990,8 @@ void read_opts(int argc, char **argv, char *compile_opts) size_t argbuf_size = MAXDNAME; char *argbuf = opt_malloc(argbuf_size); char *buff = opt_malloc(MAXDNAME); - int option, conffile_opt = '7', testmode = 0; - char *arg, *conffile = CONFFILE; + int option, testmode = 0; + char *arg, *conffile = NULL; opterr = 0; @@ -4725,8 +5065,7 @@ void read_opts(int argc, char **argv, char *compile_opts) argbuf_size = strlen(optarg) + 1; argbuf = opt_malloc(argbuf_size); } - strncpy(argbuf, optarg, argbuf_size); - argbuf[argbuf_size-1] = 0; + safe_strncpy(argbuf, optarg, argbuf_size); arg = argbuf; } else @@ -4761,8 +5100,14 @@ void read_opts(int argc, char **argv, char *compile_opts) } else if (option == 'C') { - conffile_opt = 0; /* file must exist */ - conffile = opt_string_alloc(arg); + if (!conffile) + conffile = opt_string_alloc(arg); + else + { + char *extra = opt_string_alloc(arg); + one_file(extra, 0); + free(extra); + } } else { @@ -4779,10 +5124,11 @@ void read_opts(int argc, char **argv, char *compile_opts) if (conffile) { - one_file(conffile, conffile_opt); - if (conffile_opt == 0) - free(conffile); + one_file(conffile, 0); + free(conffile); } + else + one_file(CONFFILE, '7'); /* port might not be known when the address is parsed - fill in here */ if (daemon->servers) @@ -4793,10 +5139,8 @@ void read_opts(int argc, char **argv, char *compile_opts) { if (tmp->source_addr.sa.sa_family == AF_INET) tmp->source_addr.in.sin_port = htons(daemon->query_port); -#ifdef HAVE_IPV6 else if (tmp->source_addr.sa.sa_family == AF_INET6) tmp->source_addr.in6.sin6_port = htons(daemon->query_port); -#endif } } @@ -4816,7 +5160,7 @@ void read_opts(int argc, char **argv, char *compile_opts) #define NOLOOP 1 #define TESTLOOP 2 - /* Fill in TTL for CNAMES noe we have local_ttl. + /* Fill in TTL for CNAMES now we have local_ttl. Also prepare to do loop detection. */ for (cn = daemon->cnames; cn; cn = cn->next) { @@ -4857,10 +5201,8 @@ void read_opts(int argc, char **argv, char *compile_opts) for(tmp = daemon->if_addrs; tmp; tmp = tmp->next) if (tmp->addr.sa.sa_family == AF_INET) tmp->addr.in.sin_port = htons(daemon->port); -#ifdef HAVE_IPV6 else if (tmp->addr.sa.sa_family == AF_INET6) tmp->addr.in6.sin6_port = htons(daemon->port); -#endif /* IPv6 */ } /* create default, if not specified */ |