From 91bbf96b09d2399ef990274f2fbccc9221995df5 Mon Sep 17 00:00:00 2001 From: Kevin Darbyshire-Bryant Date: Fri, 21 Feb 2014 23:16:51 +0700 Subject: [PATCH] dnsmasq latest includes upstream changes up to 21/2/2014 --- release/src/router/dnsmasq/CHANGELOG | 5 + release/src/router/dnsmasq/VERSION | 2 +- release/src/router/dnsmasq/debian/rules | 4 + release/src/router/dnsmasq/man/dnsmasq.8 | 15 +++ release/src/router/dnsmasq/src/dbus.c | 132 +++---------------- release/src/router/dnsmasq/src/dnsmasq.c | 22 +++- release/src/router/dnsmasq/src/dnsmasq.h | 10 ++ release/src/router/dnsmasq/src/dnssec.c | 21 ++- release/src/router/dnsmasq/src/network.c | 218 +++++++++++++++++++------------ release/src/router/dnsmasq/src/option.c | 193 +++++++++++++++++++-------- 10 files changed, 356 insertions(+), 266 deletions(-) diff --git a/release/src/router/dnsmasq/CHANGELOG b/release/src/router/dnsmasq/CHANGELOG index 08175c0614..806fc8e6e8 100644 --- a/release/src/router/dnsmasq/CHANGELOG +++ b/release/src/router/dnsmasq/CHANGELOG @@ -62,7 +62,12 @@ version 2.69 an invaluable period of full-time work to get it to a workable state. + Add --rev-server. Thanks to Dave Taht for suggesting this. + Add --servers-file. Allows dynamic update of upstream servers + full access to configuration. + + version 2.68 Use random addresses for DHCPv6 temporary address allocations, instead of algorithmically determined stable diff --git a/release/src/router/dnsmasq/VERSION b/release/src/router/dnsmasq/VERSION index 829d7e0ed7..fc0534b34c 100644 --- a/release/src/router/dnsmasq/VERSION +++ b/release/src/router/dnsmasq/VERSION @@ -1 +1 @@ -v2.69pretest920140213 +v2.69test8-78-g6e0290a diff --git a/release/src/router/dnsmasq/debian/rules b/release/src/router/dnsmasq/debian/rules index d485652cad..fac8e556d6 100755 --- a/release/src/router/dnsmasq/debian/rules +++ b/release/src/router/dnsmasq/debian/rules @@ -19,6 +19,10 @@ LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS) DEB_COPTS = $(COPTS) +# The nettle library in Debian is too old to include +# ECC support. +DEB_COPTS += -DNO_NETTLE_ECC + TARGET = install-i18n DEB_BUILD_ARCH_OS := $(shell dpkg-architecture -qDEB_BUILD_ARCH_OS) diff --git a/release/src/router/dnsmasq/man/dnsmasq.8 b/release/src/router/dnsmasq/man/dnsmasq.8 index f3c5de1f34..ed71ebe305 100644 --- a/release/src/router/dnsmasq/man/dnsmasq.8 +++ b/release/src/router/dnsmasq/man/dnsmasq.8 @@ -415,6 +415,14 @@ source address specified but the port may be specified directly as part of the source address. Forcing queries to an interface is not implemented on all platforms supported by dnsmasq. .TP +.B --rev-server=/,[#][@|[#]] +This is functionally the same as +.B --server, +but provides some syntactic sugar to make specifying address-to-name queries easier. For example +.B --rev-server=1.2.3.0/24,192.168.0.1 +is exactly equivalent to +.B --server=/3.2.1.in-addr.arpa/192.168.0.1 +.TP .B \-A, --address=//[domain/] Specify an IP address to return for any host in the given domains. Queries in the domains are never forwarded and always replied to @@ -1670,6 +1678,13 @@ files. If extension(s) are given, any files which end in those extensions are skipped. Any files whose names end in ~ or start with . or start and end with # are always skipped. This flag may be given on the command line or in a configuration file. +.TP +.B --servers-file= +A special case of +.B --conf-file +which differs in two respects. Firstly, only --server and --rev-server are allowed +in the configuration file included. Secondly, the file is re-read and the configuration +therein is updated when dnsmasq recieves SIGHUP. .SH CONFIG FILE At startup, dnsmasq reads .I /etc/dnsmasq.conf, diff --git a/release/src/router/dnsmasq/src/dbus.c b/release/src/router/dnsmasq/src/dbus.c index 5e9fdc99aa..ed7ac8a634 100644 --- a/release/src/router/dnsmasq/src/dbus.c +++ b/release/src/router/dnsmasq/src/dbus.c @@ -108,108 +108,6 @@ static void remove_watch(DBusWatch *watch, void *data) w = data; /* no warning */ } -static void add_update_server(union mysockaddr *addr, - union mysockaddr *source_addr, - const char *interface, - const char *domain) -{ - struct server *serv; - - /* See if there is a suitable candidate, and unmark */ - for (serv = daemon->servers; serv; serv = serv->next) - if ((serv->flags & SERV_FROM_DBUS) && - (serv->flags & SERV_MARK)) - { - if (domain) - { - if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain)) - continue; - } - else - { - if (serv->flags & SERV_HAS_DOMAIN) - continue; - } - - serv->flags &= ~SERV_MARK; - - break; - } - - if (!serv && (serv = whine_malloc(sizeof (struct server)))) - { - /* Not found, create a new one. */ - memset(serv, 0, sizeof(struct server)); - - if (domain && !(serv->domain = whine_malloc(strlen(domain)+1))) - { - free(serv); - serv = NULL; - } - else - { - serv->next = daemon->servers; - daemon->servers = serv; - serv->flags = SERV_FROM_DBUS; - if (domain) - { - strcpy(serv->domain, domain); - serv->flags |= SERV_HAS_DOMAIN; - } - } - } - - if (serv) - { - if (interface) - strcpy(serv->interface, interface); - else - serv->interface[0] = 0; - - if (source_addr->in.sin_family == AF_INET && - addr->in.sin_addr.s_addr == 0 && - serv->domain) - serv->flags |= SERV_NO_ADDR; - else - { - serv->flags &= ~SERV_NO_ADDR; - serv->addr = *addr; - serv->source_addr = *source_addr; - } - } -} - -static void mark_dbus(void) -{ - struct server *serv; - - /* mark everything from DBUS */ - for (serv = daemon->servers; serv; serv = serv->next) - if (serv->flags & SERV_FROM_DBUS) - serv->flags |= SERV_MARK; -} - -static void cleanup_dbus() -{ - struct server *serv, *tmp, **up; - - /* unlink and free anything still marked. */ - for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) - { - tmp = serv->next; - if (serv->flags & SERV_MARK) - { - server_gone(serv); - *up = serv->next; - if (serv->domain) - free(serv->domain); - free(serv); - } - else - up = &serv->next; - } -} - static void dbus_read_servers(DBusMessage *message) { DBusMessageIter iter; @@ -218,8 +116,8 @@ static void dbus_read_servers(DBusMessage *message) dbus_message_iter_init(message, &iter); - mark_dbus(); - + mark_servers(SERV_FROM_DBUS); + while (1) { int skip = 0; @@ -289,13 +187,13 @@ static void dbus_read_servers(DBusMessage *message) domain = NULL; if (!skip) - add_update_server(&addr, &source_addr, NULL, domain); + add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain); } while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING); } /* unlink and free anything still marked. */ - cleanup_dbus(); + cleanup_servers(); } static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings) @@ -319,7 +217,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings) strings ? "Expected array of string" : "Expected array of string arrays"); } - mark_dbus(); + mark_servers(SERV_FROM_DBUS); /* array_iter points to each "as" element in the outer array */ dbus_message_iter_recurse(&iter, &array_iter); @@ -327,6 +225,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings) { const char *str = NULL; union mysockaddr addr, source_addr; + int flags = 0; char interface[IF_NAMESIZE]; char *str_addr, *str_domain = NULL; @@ -416,16 +315,19 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings) memset(&interface, 0, sizeof(interface)); /* parse the IP address */ - addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, NULL); - - if (addr_err) - { + if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags))) + { error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "Invalid IP address '%s': %s", str, addr_err); break; } - + + /* 0.0.0.0 for server address == NULL, for Dbus */ + if (addr.in.sin_family == AF_INET && + addr.in.sin_addr.s_addr == 0) + flags |= SERV_NO_ADDR; + if (strings) { char *p; @@ -439,7 +341,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings) else p = NULL; - add_update_server(&addr, &source_addr, interface, str_domain); + add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain); } while ((str_domain = p)); } else @@ -454,7 +356,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings) dbus_message_iter_get_basic(&string_iter, &str); dbus_message_iter_next (&string_iter); - add_update_server(&addr, &source_addr, interface, str); + add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str); } while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING); } @@ -462,7 +364,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings) dbus_message_iter_next(&array_iter); } - cleanup_dbus(); + cleanup_servers(); if (dup) free(dup); diff --git a/release/src/router/dnsmasq/src/dnsmasq.c b/release/src/router/dnsmasq/src/dnsmasq.c index f242eba9be..02ee26fe17 100644 --- a/release/src/router/dnsmasq/src/dnsmasq.c +++ b/release/src/router/dnsmasq/src/dnsmasq.c @@ -1153,7 +1153,7 @@ static void async_event(int pipe, time_t now) { pid_t p; struct event_desc ev; - int i; + int i, check = 0; char *msg; /* NOTE: the memory used to return msg is leaked: use msgs in events only @@ -1164,11 +1164,25 @@ static void async_event(int pipe, time_t now) { case EVENT_RELOAD: clear_cache_and_reload(now); - if (daemon->port != 0 && daemon->resolv_files && option_bool(OPT_NO_POLL)) + + if (daemon->port != 0) { - reload_servers(daemon->resolv_files->name); - check_servers(); + if (daemon->resolv_files && option_bool(OPT_NO_POLL)) + { + reload_servers(daemon->resolv_files->name); + check = 1; + } + + if (daemon->servers_file) + { + read_servers_file(); + check = 1; + } + + if (check) + check_servers(); } + #ifdef HAVE_DHCP rerun_scripts(); #endif diff --git a/release/src/router/dnsmasq/src/dnsmasq.h b/release/src/router/dnsmasq/src/dnsmasq.h index 072af94a63..446987c828 100644 --- a/release/src/router/dnsmasq/src/dnsmasq.h +++ b/release/src/router/dnsmasq/src/dnsmasq.h @@ -456,6 +456,7 @@ union mysockaddr { #define SERV_COUNTED 512 /* workspace for log code */ #define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */ #define SERV_NO_REBIND 2048 /* inhibit dns-rebind protection */ +#define SERV_FROM_FILE 4096 /* read from --servers-file */ struct serverfd { int fd; @@ -864,6 +865,7 @@ extern struct daemon { unsigned int options, options2; struct resolvc default_resolv, *resolv_files; time_t last_resolv; + char *servers_file; struct mx_srv_record *mxnames; struct naptr *naptr; struct txt_record *txt, *rr; @@ -1130,6 +1132,7 @@ void read_opts (int argc, char **argv, char *compile_opts); char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len); void reread_dhcp(void); +void read_servers_file(void); void set_option_bool(unsigned int opt); void reset_option_bool(unsigned int opt); struct hostsfile *expand_filelist(struct hostsfile *list); @@ -1153,6 +1156,13 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp); int random_sock(int family); void pre_allocate_sfds(void); int reload_servers(char *fname); +void mark_servers(int flag); +void cleanup_servers(void); +void add_update_server(int flags, + union mysockaddr *addr, + union mysockaddr *source_addr, + const char *interface, + const char *domain); void check_servers(void); int enumerate_interfaces(int reset); void create_wildcard_listeners(void); diff --git a/release/src/router/dnsmasq/src/dnssec.c b/release/src/router/dnsmasq/src/dnssec.c index 13e6787256..8bd7c294ef 100644 --- a/release/src/router/dnsmasq/src/dnssec.c +++ b/release/src/router/dnsmasq/src/dnssec.c @@ -21,8 +21,10 @@ #include #include -#include -#include +#ifndef NO_NETTLE_ECC +# include +# include +#endif #include #include @@ -210,7 +212,9 @@ static int dsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned return nettle_dsa_sha1_verify_digest(key, digest, sig_struct); } -static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len, +#ifndef NO_NETTLE_ECC +static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len, + unsigned char *sig, size_t sig_len, unsigned char *digest, size_t digest_len, int algo) { unsigned char *p; @@ -278,10 +282,13 @@ static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len return nettle_ecdsa_verify(key, digest_len, digest, sig_struct); } - +#endif + static int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len, unsigned char *digest, size_t digest_len, int algo) { + (void)digest_len; + switch (algo) { case 1: case 5: case 7: case 8: case 10: @@ -289,10 +296,12 @@ static int verify(struct blockdata *key_data, unsigned int key_len, unsigned cha case 3: case 6: return dsa_verify(key_data, key_len, sig, sig_len, digest, algo); - + +#ifndef NO_NETTLE_ECC case 13: case 14: return dnsmasq_ecdsa_verify(key_data, key_len, sig, sig_len, digest, digest_len, algo); -} +#endif + } return 0; } diff --git a/release/src/router/dnsmasq/src/network.c b/release/src/router/dnsmasq/src/network.c index f068454dd9..a4380aee84 100644 --- a/release/src/router/dnsmasq/src/network.c +++ b/release/src/router/dnsmasq/src/network.c @@ -1254,87 +1254,187 @@ void pre_allocate_sfds(void) } } +void mark_servers(int flag) +{ + struct server *serv; + + /* mark everything with argument flag */ + for (serv = daemon->servers; serv; serv = serv->next) + if (serv->flags & flag) + serv->flags |= SERV_MARK; +} + +void cleanup_servers(void) +{ + struct server *serv, *tmp, **up; + + /* unlink and free anything still marked. */ + for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) + { + tmp = serv->next; + if (serv->flags & SERV_MARK) + { + server_gone(serv); + *up = serv->next; + if (serv->domain) + free(serv->domain); + free(serv); + } + else + up = &serv->next; + } +} + +void add_update_server(int flags, + union mysockaddr *addr, + union mysockaddr *source_addr, + const char *interface, + const char *domain) +{ + struct server *serv, *next = NULL; + char *domain_str = NULL; + + /* See if there is a suitable candidate, and unmark */ + for (serv = daemon->servers; serv; serv = serv->next) + if (serv->flags & SERV_MARK) + { + if (domain) + { + if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain)) + continue; + } + else + { + if (serv->flags & SERV_HAS_DOMAIN) + continue; + } + + break; + } + + if (serv) + { + domain_str = serv->domain; + next = serv->next; + } + else if ((serv = whine_malloc(sizeof (struct server)))) + { + /* Not found, create a new one. */ + if (domain && !(domain_str = whine_malloc(strlen(domain)+1))) + { + free(serv); + serv = NULL; + } + else + { + struct server *s; + /* Add to the end of the chain, for order */ + if (!daemon->servers) + daemon->servers = serv; + else + { + for (s = daemon->servers; s->next; s = s->next); + s->next = serv; + } + if (domain) + strcpy(domain_str, domain); + } + } + + if (serv) + { + memset(serv, 0, sizeof(struct server)); + serv->flags = flags; + serv->domain = domain_str; + serv->next = next; + serv->queries = serv->failed_queries = 0; + + if (domain) + serv->flags |= SERV_HAS_DOMAIN; + + if (interface) + strcpy(serv->interface, interface); + if (addr) + serv->addr = *addr; + if (source_addr) + serv->source_addr = *source_addr; + } +} void check_servers(void) { struct irec *iface; - struct server *new, *tmp, *ret = NULL; + struct server *serv; int port = 0; /* interface may be new since startup */ if (!option_bool(OPT_NOWILD)) enumerate_interfaces(0); - for (new = daemon->servers; new; new = tmp) + for (serv = daemon->servers; serv; serv = serv->next) { - tmp = new->next; - - if (!(new->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND))) + if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND))) { - port = prettyprint_addr(&new->addr, daemon->namebuff); + port = prettyprint_addr(&serv->addr, daemon->namebuff); /* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */ - if (new->addr.sa.sa_family == AF_INET && - new->addr.in.sin_addr.s_addr == 0) + if (serv->addr.sa.sa_family == AF_INET && + serv->addr.in.sin_addr.s_addr == 0) { - free(new); + serv->flags |= SERV_MARK; continue; } for (iface = daemon->interfaces; iface; iface = iface->next) - if (sockaddr_isequal(&new->addr, &iface->addr)) + if (sockaddr_isequal(&serv->addr, &iface->addr)) break; if (iface) { my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff); - free(new); + serv->flags |= SERV_MARK; continue; } /* Do we need a socket set? */ - if (!new->sfd && - !(new->sfd = allocate_sfd(&new->source_addr, new->interface)) && + if (!serv->sfd && + !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) && errno != 0) { my_syslog(LOG_WARNING, _("ignoring nameserver %s - cannot make/bind socket: %s"), daemon->namebuff, strerror(errno)); - free(new); + serv->flags |= SERV_MARK; continue; } } - /* reverse order - gets it right. */ - new->next = ret; - ret = new; - - if (!(new->flags & SERV_NO_REBIND)) + if (!(serv->flags & SERV_NO_REBIND)) { - if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV)) + if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV)) { char *s1, *s2; - if (!(new->flags & SERV_HAS_DOMAIN)) + if (!(serv->flags & SERV_HAS_DOMAIN)) s1 = _("unqualified"), s2 = _("names"); - else if (strlen(new->domain) == 0) + else if (strlen(serv->domain) == 0) s1 = _("default"), s2 = ""; else - s1 = _("domain"), s2 = new->domain; + s1 = _("domain"), s2 = serv->domain; - if (new->flags & SERV_NO_ADDR) + if (serv->flags & SERV_NO_ADDR) my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2); - else if (new->flags & SERV_USE_RESOLV) + else if (serv->flags & SERV_USE_RESOLV) my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2); - else if (!(new->flags & SERV_LITERAL_ADDRESS)) + else if (!(serv->flags & SERV_LITERAL_ADDRESS)) my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2); } - else if (new->interface[0] != 0) - my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, new->interface); + else if (serv->interface[0] != 0) + my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface); else my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port); } } - - daemon->servers = ret; + + cleanup_servers(); } /* Return zero if no servers found, in that case we keep polling. @@ -1343,9 +1443,6 @@ int reload_servers(char *fname) { FILE *f; char *line; - struct server *old_servers = NULL; - struct server *new_servers = NULL; - struct server *serv; int gotone = 0; /* buff happens to be MAXDNAME long... */ @@ -1354,28 +1451,9 @@ int reload_servers(char *fname) my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno)); return 0; } - - /* move old servers to free list - we can reuse the memory - and not risk malloc if there are the same or fewer new servers. - Servers which were specced on the command line go to the new list. */ - for (serv = daemon->servers; serv;) - { - struct server *tmp = serv->next; - if (serv->flags & SERV_FROM_RESOLV) - { - serv->next = old_servers; - old_servers = serv; - /* forward table rules reference servers, so have to blow them away */ - server_gone(serv); - } - else - { - serv->next = new_servers; - new_servers = serv; - } - serv = tmp; - } - + + mark_servers(SERV_FROM_RESOLV); + while ((line = fgets(daemon->namebuff, MAXDNAME, f))) { union mysockaddr addr, source_addr; @@ -1434,38 +1512,12 @@ int reload_servers(char *fname) continue; #endif - if (old_servers) - { - serv = old_servers; - old_servers = old_servers->next; - } - else if (!(serv = whine_malloc(sizeof (struct server)))) - continue; - - /* this list is reverse ordered: - it gets reversed again in check_servers */ - serv->next = new_servers; - new_servers = serv; - serv->addr = addr; - serv->source_addr = source_addr; - serv->domain = NULL; - serv->interface[0] = 0; - serv->sfd = NULL; - serv->flags = SERV_FROM_RESOLV; - serv->queries = serv->failed_queries = 0; + add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL); gotone = 1; } - /* Free any memory not used. */ - while (old_servers) - { - struct server *tmp = old_servers->next; - free(old_servers); - old_servers = tmp; - } - - daemon->servers = new_servers; fclose(f); + cleanup_servers(); return gotone; } diff --git a/release/src/router/dnsmasq/src/option.c b/release/src/router/dnsmasq/src/option.c index f7f98d5cdc..2d860d5563 100644 --- a/release/src/router/dnsmasq/src/option.c +++ b/release/src/router/dnsmasq/src/option.c @@ -141,6 +141,8 @@ struct myoption { #define LOPT_SEC_VALID 329 #define LOPT_TRUST_ANCHOR 330 #define LOPT_DNSSEC_DEBUG 331 +#define LOPT_REV_SERV 332 +#define LOPT_SERVERS_FILE 333 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -157,6 +159,7 @@ static const struct myoption opts[] = { "user", 2, 0, 'u' }, { "group", 2, 0, 'g' }, { "resolv-file", 2, 0, 'r' }, + { "servers-file", 1, 0, LOPT_SERVERS_FILE }, { "mx-host", 1, 0, 'm' }, { "mx-target", 1, 0, 't' }, { "cache-size", 2, 0, 'c' }, @@ -178,6 +181,7 @@ static const struct myoption opts[] = { "pid-file", 2, 0, 'x' }, { "strict-order", 0, 0, 'o' }, { "server", 1, 0, 'S' }, + { "rev-server", 1, 0, LOPT_REV_SERV }, { "local", 1, 0, LOPT_LOCAL }, { "address", 1, 0, 'A' }, { "conf-file", 2, 0, 'C' }, @@ -347,7 +351,9 @@ static struct { { 'Q', ARG_ONE, "", gettext_noop("Force the originating port for upstream DNS queries."), NULL }, { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL }, { 'r', ARG_DUP, "", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE }, + { LOPT_SERVERS_FILE, ARG_ONE, "", gettext_noop("Specify path to file with server= options"), NULL }, { 'S', ARG_DUP, "//", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL }, + { LOPT_REV_SERV, ARG_DUP, "/,", gettext_noop("Specify address of upstream servers for reverse address queries"), NULL }, { LOPT_LOCAL, ARG_DUP, "//", gettext_noop("Never forward queries to specified domains."), NULL }, { 's', ARG_DUP, "[,]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL }, { 't', ARG_ONE, "", gettext_noop("Specify default target in an MX record."), NULL }, @@ -591,6 +597,7 @@ static int atoi_check16(char *a, int *res) return 1; } +#ifdef HAVE_DNSSEC static int atoi_check8(char *a, int *res) { if (!(atoi_check(a, res)) || @@ -600,6 +607,7 @@ static int atoi_check8(char *a, int *res) return 1; } +#endif static void add_txt(char *name, char *txt) { @@ -684,6 +692,13 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a char *scope_id; #endif + if (!arg || strlen(arg) == 0) + { + *flags |= SERV_NO_ADDR; + *interface = 0; + return NULL; + } + if ((source = split_chr(arg, '@')) && /* is there a source. */ (portno = split_chr(source, '#')) && !atoi_check16(portno, &source_port)) @@ -762,6 +777,54 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a return NULL; } +static struct server *add_rev4(struct in_addr addr, int msize) +{ + struct server *serv = opt_malloc(sizeof(struct server)); + in_addr_t a = ntohl(addr.s_addr) >> 8; + char *p; + + memset(serv, 0, sizeof(struct server)); + p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */ + + if (msize == 24) + p += sprintf(p, "%d.", a & 0xff); + a = a >> 8; + if (msize != 8) + p += sprintf(p, "%d.", a & 0xff); + a = a >> 8; + p += sprintf(p, "%d.in-addr.arpa", a & 0xff); + + serv->flags = SERV_HAS_DOMAIN; + serv->next = daemon->servers; + daemon->servers = serv; + + return serv; + +} + +static struct server *add_rev6(struct in6_addr *addr, int msize) +{ + struct server *serv = opt_malloc(sizeof(struct server)); + char *p; + int i; + + memset(serv, 0, sizeof(struct server)); + p = serv->domain = opt_malloc(73); /* strlen("32*ip6.arpa")+1 */ + + for (i = msize-1; i >= 0; i -= 4) + { + int dig = ((unsigned char *)addr)[i>>3]; + p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4); + } + p += sprintf(p, "ip6.arpa"); + + serv->flags = SERV_HAS_DOMAIN; + serv->next = daemon->servers; + daemon->servers = serv; + + return serv; +} + #ifdef HAVE_DHCP static int is_tag_prefix(char *arg) @@ -1325,7 +1388,7 @@ void reset_option_bool(unsigned int opt) daemon->options2 &= ~(1u << (opt - 32)); } -static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line) +static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only) { int i; char *comma; @@ -1528,6 +1591,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma daemon->resolv_files = list; break; } + + case LOPT_SERVERS_FILE: + daemon->servers_file = opt_string_alloc(arg); + break; case 'm': /* --mx-host */ { @@ -1824,34 +1891,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma ret_err(gen_err); else { - /* generate the equivalent of - local=// - local=/xxx.yyy.zzz.in-addr.arpa/ */ - struct server *serv = opt_malloc(sizeof(struct server)); - in_addr_t a = ntohl(new->start.s_addr) >> 8; - char *p; - - memset(serv, 0, sizeof(struct server)); - serv->domain = d; - serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; - serv->next = daemon->servers; - daemon->servers = serv; - - serv = opt_malloc(sizeof(struct server)); - memset(serv, 0, sizeof(struct server)); - p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */ - - if (msize == 24) - p += sprintf(p, "%d.", a & 0xff); - a = a >> 8; - if (msize != 8) - p += sprintf(p, "%d.", a & 0xff); - a = a >> 8; - p += sprintf(p, "%d.in-addr.arpa", a & 0xff); - - serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; - serv->next = daemon->servers; - daemon->servers = serv; + /* generate the equivalent of + local=// + local=/xxx.yyy.zzz.in-addr.arpa/ */ + struct server *serv = add_rev4(new->start, msize); + serv->flags |= SERV_NO_ADDR; } } } @@ -1887,29 +1931,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma /* generate the equivalent of local=// local=/xxx.yyy.zzz.ip6.arpa/ */ - struct server *serv = opt_malloc(sizeof(struct server)); - char *p; - - memset(serv, 0, sizeof(struct server)); - serv->domain = d; - serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; - serv->next = daemon->servers; - daemon->servers = serv; - - serv = opt_malloc(sizeof(struct server)); - memset(serv, 0, sizeof(struct server)); - p = serv->domain = opt_malloc(73); /* strlen("32*ip6.arpa")+1 */ - - for (i = msize-1; i >= 0; i -= 4) - { - int dig = ((unsigned char *)&new->start6)[i>>3]; - p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4); - } - p += sprintf(p, "ip6.arpa"); - - serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; - serv->next = daemon->servers; - daemon->servers = serv; + struct server *serv = add_rev6(&new->start6, msize); + serv->flags |= SERV_NO_ADDR; } } } @@ -2136,6 +2159,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma memset(newlist, 0, sizeof(struct server)); } + if (servers_only && option == 'S') + newlist->flags |= SERV_FROM_FILE; + if (option == 'A') { newlist->flags |= SERV_LITERAL_ADDRESS; @@ -2180,6 +2206,40 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma break; } + case LOPT_REV_SERV: /* --rev-server */ + { + char *string; + 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)) + ret_err(gen_err); + + if (inet_pton(AF_INET, arg, &addr4)) + serv = add_rev4(addr4, size); +#ifdef HAVE_IPV6 + else if (inet_pton(AF_INET6, arg, &addr6)) + serv = add_rev6(&addr6, size); +#endif + else + ret_err(gen_err); + + string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags); + + if (string) + ret_err(string); + + if (servers_only) + serv->flags |= SERV_FROM_FILE; + + break; + } + case LOPT_IPSET: /* --ipset */ #ifndef HAVE_IPSET ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives")); @@ -3754,12 +3814,13 @@ static void read_file(char *file, FILE *f, int hard_opt) while (fgets(buff, MAXDNAME, f)) { - int white, i, option = hard_opt; + int white, i; + volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt; char *errmess, *p, *arg = NULL, *start; size_t len; /* Memory allocation failure longjmps here if mem_recover == 1 */ - if (option != 0) + if (option != 0 || hard_opt == LOPT_REV_SERV) { if (setjmp(mem_jmp)) continue; @@ -3860,13 +3921,15 @@ static void read_file(char *file, FILE *f, int hard_opt) errmess = _("extraneous parameter"); else if (opts[i].has_arg == 1 && !arg) errmess = _("missing parameter"); + else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV) + errmess = _("illegal option"); } oops: if (errmess) strcpy(daemon->namebuff, errmess); - if (errmess || !one_opt(option, arg, buff, _("error"), 0)) + if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV)) { sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file); if (hard_opt != 0) @@ -4048,6 +4111,22 @@ struct hostsfile *expand_filelist(struct hostsfile *list) return list; } +void read_servers_file(void) +{ + FILE *f; + + if (!(f = fopen(daemon->servers_file, "r"))) + { + my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno)); + return; + } + + mark_servers(SERV_FROM_FILE); + cleanup_servers(); + + read_file(daemon->servers_file, f, LOPT_REV_SERV); +} + #ifdef HAVE_DHCP void reread_dhcp(void) @@ -4241,9 +4320,9 @@ void read_opts(int argc, char **argv, char *compile_opts) else { #ifdef HAVE_GETOPT_LONG - if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1)) + if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0)) #else - if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1)) + if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0)) #endif die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF); } -- 2.11.4.GIT