From 85bc7534dae7711f6c82742feaa7dacb41af3f36 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Fri, 25 Jun 2021 22:09:08 +0100 Subject: [PATCH] Rationalise --server parsing and datastructure building. Use add_update_server for everything. --- src/dbus.c | 14 ++-- src/dnsmasq.c | 6 +- src/dnsmasq.h | 18 ++--- src/domain-match.c | 174 ++++++++++++++++++++++++++++++++++++++++++--- src/loop.c | 6 +- src/network.c | 157 +++------------------------------------- src/option.c | 136 +++++++++-------------------------- src/rfc1035.c | 4 +- 8 files changed, 231 insertions(+), 284 deletions(-) diff --git a/src/dbus.c b/src/dbus.c index 0a67e3c..c6172b2 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -215,13 +215,14 @@ static void dbus_read_servers(DBusMessage *message) domain = NULL; if (!skip) - add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain); + add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain, NULL); } while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING); } /* unlink and free anything still marked. */ cleanup_servers(); + check_servers(0); } #ifdef HAVE_LOOP @@ -361,10 +362,6 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings) strcpy(str_addr, str); } - memset(&addr, 0, sizeof(addr)); - memset(&source_addr, 0, sizeof(source_addr)); - memset(&interface, 0, sizeof(interface)); - /* parse the IP address */ if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags))) { @@ -392,7 +389,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings) else p = NULL; - add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain); + add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL); } while ((str_domain = p)); } else @@ -407,7 +404,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(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str); + add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL); } while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING); } @@ -416,7 +413,8 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings) } cleanup_servers(); - + check_servers(0); + if (dup) free(dup); diff --git a/src/dnsmasq.c b/src/dnsmasq.c index c74d48d..04582da 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -1034,7 +1034,7 @@ int main (int argc, char **argv) close(err_pipe[1]); if (daemon->port != 0) - check_servers(); + check_servers(0); pid = getpid(); @@ -1446,7 +1446,7 @@ static void async_event(int pipe, time_t now) } if (check) - check_servers(); + check_servers(0); } #ifdef HAVE_DHCP @@ -1645,7 +1645,7 @@ static void poll_resolv(int force, int do_reload, time_t now) { my_syslog(LOG_INFO, _("reading %s"), latest->name); warned = 0; - check_servers(); + check_servers(0); if (option_bool(OPT_RELOAD) && do_reload) clear_cache_and_reload(now); } diff --git a/src/dnsmasq.h b/src/dnsmasq.h index b21fc17..61d16f2 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1414,14 +1414,7 @@ int indextoname(int fd, int index, char *name); int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp); 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); +void check_servers(int no_loop_call); int enumerate_interfaces(int reset); void create_wildcard_listeners(void); void create_bound_listeners(int dienow); @@ -1754,4 +1747,11 @@ int server_samegroup(struct server *a, struct server *b); #ifdef HAVE_DNSSEC int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp); #endif - +void mark_servers(int flag); +void cleanup_servers(void); +int add_update_server(int flags, + union mysockaddr *addr, + union mysockaddr *source_addr, + const char *interface, + const char *domain, + union all_addr *local_addr); diff --git a/src/domain-match.c b/src/domain-match.c index acaafbf..77c3363 100644 --- a/src/domain-match.c +++ b/src/domain-match.c @@ -20,14 +20,20 @@ static int order(char *qdomain, size_t qlen, struct server *serv); static int order_qsort(const void *a, const void *b); static int order_servers(struct server *s, struct server *s2); +/* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain. */ +#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS) + void build_server_array(void) { struct server *serv; int count = 0; for (serv = daemon->servers; serv; serv = serv->next) - count++; - +#ifdef HAVE_LOOP + if (!(serv->flags & SERV_LOOP)) +#endif + count++; + for (serv = daemon->local_domains; serv; serv = serv->next) count++; @@ -48,21 +54,24 @@ void build_server_array(void) count = 0; for (serv = daemon->servers; serv; serv = serv->next, count++) - { - daemon->serverarray[count] = serv; - serv->serial = count; - serv->last_server = -1; - } - +#ifdef HAVE_LOOP + if (!(serv->flags & SERV_LOOP)) +#endif + { + daemon->serverarray[count] = serv; + serv->serial = count; + serv->last_server = -1; + } + for (serv = daemon->local_domains; serv; serv = serv->next, count++) daemon->serverarray[count] = serv; qsort(daemon->serverarray, daemon->serverarraysz, sizeof(struct server *), order_qsort); - + /* servers need the location in the array to find all the whole set of equivalent servers from a pointer to a single one. */ for (count = 0; count < daemon->serverarraysz; count++) - if (!(daemon->serverarray[count]->flags & (SERV_LITERAL_ADDRESS | SERV_USE_RESOLV))) + if (!(daemon->serverarray[count]->flags & SERV_IS_LOCAL)) daemon->serverarray[count]->arrayposn = count; } @@ -467,3 +476,148 @@ static int order_qsort(const void *a, const void *b) return rc; } + +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; + + for (serv = daemon->local_domains; 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; + free(serv->domain); + free(serv); + } + else + up = &serv->next; + } + + for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = tmp) + { + tmp = serv->next; + if (serv->flags & SERV_MARK) + { + *up = serv->next; + free(serv->domain); + free(serv); + } + else + up = &serv->next; + } +} + +int add_update_server(int flags, + union mysockaddr *addr, + union mysockaddr *source_addr, + const char *interface, + const char *domain, + union all_addr *local_addr) +{ + struct server *serv; + char *alloc_domain; + + if (!domain || strlen(domain) == 0) + alloc_domain = whine_malloc(1); + else if (!(alloc_domain = canonicalise((char *)domain, NULL))) + return 0; + + /* See if there is a suitable candidate, and unmark */ + for (serv = (flags & SERV_IS_LOCAL) ? daemon->local_domains : daemon->servers; serv; serv = serv->next) + if ((serv->flags & SERV_MARK) && + hostname_isequal(alloc_domain, serv->domain) && + ((serv->flags & (SERV_6ADDR | SERV_4ADDR)) == (flags & (SERV_6ADDR | SERV_4ADDR)))) + break; + + if (serv) + { + free(alloc_domain); + alloc_domain = serv->domain; + } + else + { + size_t size; + + if (flags & SERV_LITERAL_ADDRESS) + { + if (flags & SERV_6ADDR) + size = sizeof(struct serv_addr6); + else if (flags & SERV_4ADDR) + size = sizeof(struct serv_addr4); + else + size = sizeof(struct serv_local); + } + else + size = sizeof(struct server); + + if (!(serv = whine_malloc(size))) + return 0; + + if (flags & SERV_IS_LOCAL) + { + serv->next = daemon->local_domains; + daemon->local_domains = serv; + } + 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; + } + + serv->next = NULL; + } + } + + if (!(flags & SERV_IS_LOCAL)) + memset(serv, 0, sizeof(struct server)); + + serv->flags = flags; + serv->domain = alloc_domain; + serv->domain_len = strlen(alloc_domain); + + if (flags & SERV_4ADDR) + ((struct serv_addr4*)serv)->addr = local_addr->addr4; + + if (flags & SERV_6ADDR) + ((struct serv_addr6*)serv)->addr = local_addr->addr6; + + if (!(flags & SERV_IS_LOCAL)) + { +#ifdef HAVE_LOOP + serv->uid = rand32(); +#endif + + if (interface) + safe_strncpy(serv->interface, interface, sizeof(serv->interface)); + if (addr) + serv->addr = *addr; + if (source_addr) + serv->source_addr = *source_addr; + } + + return 1; +} + diff --git a/src/loop.c b/src/loop.c index 56b5287..01f0c28 100644 --- a/src/loop.c +++ b/src/loop.c @@ -31,11 +31,13 @@ void loop_send_probes() identifiable, via the uid. If we see that query back again, then the server is looping, and we should not use it. */ for (serv = daemon->servers; serv; serv = serv->next) if (strlen(serv->domain) == 0 && - !(serv->flags & (SERV_FOR_NODOTS | SERV_LOOP))) + !(serv->flags & (SERV_FOR_NODOTS))) { ssize_t len = loop_make_probe(serv->uid); int fd; + serv->flags &= ~SERV_LOOP; + if ((fd = allocate_rfd(&rfds, serv)) == -1) continue; @@ -101,7 +103,7 @@ int detect_loop(char *query, int type) uid == serv->uid) { serv->flags |= SERV_LOOP; - check_servers(); /* log new state */ + check_servers(1); /* log new state - don't send more probes. */ return 1; } diff --git a/src/network.c b/src/network.c index 52291b4..e189edd 100644 --- a/src/network.c +++ b/src/network.c @@ -1494,149 +1494,7 @@ 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; -#ifdef HAVE_LOOP - /* Give looped servers another chance */ - serv->flags &= ~SERV_LOOP; -#endif - } - - for (serv = daemon->local_domains; 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; - free(serv->domain); - free(serv); - } - else - up = &serv->next; - } - - for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = tmp) - { - tmp = serv->next; - if (serv->flags & SERV_MARK) - { - *up = serv->next; - free(serv->domain); - free(serv); - } - else - up = &serv->next; - } - - -#ifdef HAVE_LOOP - /* Now we have a new set of servers, test for loops. */ - loop_send_probes(); -#endif -} - -void add_update_server(int flags, - union mysockaddr *addr, - union mysockaddr *source_addr, - const char *interface, - const char *domain) -{ - struct server *serv; - char *domain_str; - - if (!domain) - domain = ""; - - /* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain. - NOTE that we can get local=/domain/ here, but NOT address=/domain/1.2.3.4 */ -#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS) - - /* See if there is a suitable candidate, and unmark */ - for (serv = (flags & SERV_IS_LOCAL) ? daemon->local_domains : daemon->servers; serv; serv = serv->next) - if ((serv->flags & SERV_MARK) && hostname_isequal(domain, serv->domain)) - break; - - if (serv) - domain_str = serv->domain; - else if ((serv = whine_malloc(sizeof (struct server)))) - { - /* Not found, create a new one. */ - if (!(domain_str = whine_malloc(strlen(domain)+1))) - { - free(serv); - serv = NULL; - } - else - { - strcpy(domain_str, domain); - - if (flags & SERV_IS_LOCAL) - { - serv->next = daemon->local_domains; - daemon->local_domains = serv; - } - 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; - } - - serv->next = NULL; - } - } - } - - if (serv) - { - if (!(flags & SERV_IS_LOCAL)) - memset(serv, 0, sizeof(struct server)); - - serv->flags = flags; - serv->domain = domain_str; - serv->domain_len = strlen(domain_str); - - if (!(flags & SERV_IS_LOCAL)) - { - serv->queries = serv->failed_queries = 0; -#ifdef HAVE_LOOP - serv->uid = rand32(); -#endif - - if (interface) - safe_strncpy(serv->interface, interface, sizeof(serv->interface)); - if (addr) - serv->addr = *addr; - if (source_addr) - serv->source_addr = *source_addr; - } - } -} - -void check_servers(void) +void check_servers(int no_loop_check) { struct irec *iface; struct server *serv; @@ -1644,7 +1502,12 @@ void check_servers(void) int port = 0, count; int locals = 0; - /* interface may be new since startup */ +#ifdef HAVE_LOOP + if (!no_loop_check) + loop_send_probes(); +#endif + + /* interface may be new since startup */ if (!option_bool(OPT_NOWILD)) enumerate_interfaces(0); @@ -1786,8 +1649,8 @@ void check_servers(void) up = &sfd->next; } - cleanup_servers(); - build_server_array(); + cleanup_servers(); /* remove servers we just deleted. */ + build_server_array(); } /* Return zero if no servers found, in that case we keep polling. @@ -1860,7 +1723,7 @@ int reload_servers(char *fname) continue; } - add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL); + add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL, NULL); gotone = 1; } diff --git a/src/option.c b/src/option.c index 843ec24..f5f6ce7 100644 --- a/src/option.c +++ b/src/option.c @@ -829,6 +829,8 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a int scope_index = 0; char *scope_id; + *interface = 0; + if (strcmp(arg, "#") == 0) { if (flags) @@ -2639,14 +2641,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma case LOPT_LOCAL: /* --local */ case 'A': /* --address */ { - struct server *new; - size_t size; char *lastdomain = NULL, *domain = ""; - char *alloc_domain; u16 flags = 0; char *err; - struct in_addr addr4; - struct in6_addr addr6; + union all_addr addr; + union mysockaddr serv_addr, source_addr; + char interface[IF_NAMESIZE+1]; unhide_metas(arg); @@ -2668,116 +2668,45 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } } - if (servers_only && option == 'S') - flags |= SERV_FROM_FILE; - if (!arg || !*arg) flags = SERV_LITERAL_ADDRESS; else if (option == 'A') { /* # as literal address means return zero address for 4 and 6 */ if (strcmp(arg, "#") == 0) - flags |= SERV_ALL_ZEROS | SERV_LITERAL_ADDRESS; - else if (inet_pton(AF_INET, arg, &addr4) > 0) - flags |= SERV_4ADDR | SERV_LITERAL_ADDRESS; - else if (inet_pton(AF_INET6, arg, &addr6) > 0) - flags |= SERV_6ADDR | SERV_LITERAL_ADDRESS; + flags = SERV_ALL_ZEROS | SERV_LITERAL_ADDRESS; + else if (inet_pton(AF_INET, arg, &addr.addr4) > 0) + flags = SERV_4ADDR | SERV_LITERAL_ADDRESS; + else if (inet_pton(AF_INET6, arg, &addr.addr6) > 0) + flags = SERV_6ADDR | SERV_LITERAL_ADDRESS; else ret_err(_("Bad address in --address")); } - - if (!(alloc_domain = canonicalise_opt(domain))) - ret_err(gen_err); - - - if (flags & SERV_LITERAL_ADDRESS) - { - if (flags & SERV_6ADDR) - { - size = sizeof(struct serv_addr6); - new = opt_malloc(sizeof(struct serv_addr6)); - ((struct serv_addr6*)new)->addr = addr6; - } - else if (flags & SERV_4ADDR) - { - size = sizeof(struct serv_addr4); - new = opt_malloc(sizeof(struct serv_addr4)); - ((struct serv_addr4*)new)->addr = addr4; - } - else - { - size = sizeof(struct serv_local); - new = opt_malloc(sizeof(struct serv_local)); - } - - new->next = daemon->local_domains; - daemon->local_domains = new; - } else { - size = sizeof(struct server); - new = opt_malloc(sizeof(struct server)); + if ((err = parse_server(arg, &serv_addr, &source_addr, interface, &flags))) + ret_err(err); -#ifdef HAVE_LOOP - new->uid = rand32(); -#endif - if ((err = parse_server(arg, &new->addr, &new->source_addr, new->interface, &flags))) - { - free(new); - ret_err(err); - } + /* server=//1.2.3.4 is special. */ + if (strlen(domain) == 0 && lastdomain) + flags |= SERV_FOR_NODOTS; + + } + + if (servers_only && option == 'S') + flags |= SERV_FROM_FILE; + + while (1) + { + if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, &addr)) + ret_err(gen_err); - /* Since domains that use standard servers don't have the - network stuff, it's easier to treat them as local. */ - if (flags & SERV_USE_RESOLV) - { - new->next = daemon->local_domains; - daemon->local_domains = new; - } - else - { - new->next = daemon->servers; - daemon->servers = new; - } + if (!lastdomain || domain == lastdomain) + break; + + domain += strlen(domain) + 1; } - new->domain = alloc_domain; - new->domain_len = strlen(alloc_domain); - - /* server=//1.2.3.4 is special. */ - if (strlen(domain) == 0 && lastdomain) - flags |= SERV_FOR_NODOTS; - - new->flags = flags; - - /* If we have more than one domain, copy and iterate */ - if (lastdomain) - while (domain != lastdomain) - { - struct server *last = new; - - domain += strlen(domain) + 1; - - if (!(alloc_domain = canonicalise_opt(domain))) - ret_err(gen_err); - - new = opt_malloc(size); - memcpy(new, last, size); - new->domain = alloc_domain; - new->domain_len = strlen(alloc_domain); - - if (flags & (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)) - { - new->next = daemon->local_domains; - daemon->local_domains = new; - } - else - { - new->next = daemon->servers; - daemon->servers = new; - } - } - break; } @@ -4739,8 +4668,7 @@ err: } else { - int nomem; - char *canon = canonicalise(arg, &nomem); + char *canon = canonicalise_opt(arg); struct name_list *nl; if (!canon) { @@ -5193,9 +5121,9 @@ void read_servers_file(void) } mark_servers(SERV_FROM_FILE); - cleanup_servers(); - read_file(daemon->servers_file, f, LOPT_REV_SERV); + cleanup_servers(); + check_servers(0); } diff --git a/src/rfc1035.c b/src/rfc1035.c index 3bd728e..19d2172 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -1005,12 +1005,14 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, return F_IPV4 | F_IPV6; } +#ifdef HAVE_DNSSEC /* F_DNSSECOK as agument to search_servers() inhibits forwarding to servers for domains without a trust anchor. This make the behaviour for DS and DNSKEY queries we forward the same as for DS and DNSKEY queries we originate. */ - if (qtype == T_DS || qtype == T_DNSKEY) + if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DS || qtype == T_DNSKEY)) return F_DNSSECOK; +#endif return F_QUERY; }