From 5747d7b3dffdcd45d4410bb380e466818734cb27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= Date: Mon, 19 Apr 2021 13:56:23 +0200 Subject: [PATCH] Use load-balancing also for --server=/domains/ Do not (yet) move servers to server_domain structure. Instead use separate server_domains to store just last_server and requests count and time. Introduces domain information duplicity, but minimizes required changes to daemon->servers usage. Optimize server domain record Set pointer to domain record when struct server is created. When searching for domain pointer, use this pointer to make it quick. --- src/dnsmasq.h | 18 +++++++-- src/forward.c | 52 ++++++++++++++++---------- src/network.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++---- src/option.c | 5 +++ 4 files changed, 146 insertions(+), 30 deletions(-) diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 1e21005..b6dcc50 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -559,6 +559,17 @@ struct randfd_list { struct randfd_list *next; }; +/* contains domain specific set of servers. + * If domain is NULL, just normal servers. */ +struct server_domain { + char *domain; + struct server *last_server; + time_t forwardtime; + int forwardcount; + unsigned int flags; /* server.flags alternative */ + struct server_domain *next; +}; + struct server { union mysockaddr addr, source_addr; char interface[IF_NAMESIZE+1]; @@ -571,6 +582,7 @@ struct server { #ifdef HAVE_LOOP u32 uid; #endif + struct server_domain *serv_domain; struct server *next; }; @@ -1053,6 +1065,7 @@ extern struct daemon { struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces; struct bogus_addr *bogus_addr, *ignore_addr; struct server *servers; + struct server_domain *server_domains; struct ipsets *ipsets; int log_fac; /* log facility */ char *log_file; /* optional log file */ @@ -1121,9 +1134,6 @@ extern struct daemon { struct serverfd *sfds; struct irec *interfaces; struct listener *listeners; - struct server *last_server; - time_t forwardtime; - int forwardcount; struct server *srv_save; /* Used for resend on DoD */ size_t packet_len; /* " " */ int fd_save; /* " " */ @@ -1394,6 +1404,8 @@ int label_exception(int index, int family, union all_addr *addr); int fix_fd(int fd); int tcp_interface(int fd, int af); int set_ipv6pktinfo(int fd); +struct server_domain *server_domain_find_domain(const char *domain); +struct server_domain *server_domain_new(struct server *serv); #ifdef HAVE_DHCP6 void join_multicast(int dienow); #endif diff --git a/src/forward.c b/src/forward.c index 9322b6a..b09dc96 100644 --- a/src/forward.c +++ b/src/forward.c @@ -107,7 +107,8 @@ int send_from(int fd, int nowild, char *packet, size_t len, } static unsigned int search_servers(time_t now, union all_addr **addrpp, unsigned int qtype, - char *qdomain, int *type, char **domain, int *norebind) + char *qdomain, int *type, char **domain, int *norebind, + struct server_domain **serv_domain) { /* If the query ends in the domain in one of our servers, set @@ -120,6 +121,9 @@ static unsigned int search_servers(time_t now, union all_addr **addrpp, unsigned unsigned int flags = 0; static union all_addr zero; + if (serv_domain) + *serv_domain = NULL; + for (serv = daemon->servers; serv; serv=serv->next) if (qtype == F_DNSSECOK && !(serv->flags & SERV_DO_DNSSEC)) continue; @@ -187,6 +191,8 @@ static unsigned int search_servers(time_t now, union all_addr **addrpp, unsigned { *type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_DO_DNSSEC); *domain = serv->domain; + if (serv_domain) + *serv_domain = serv->serv_domain; matchlen = domainlen; if (serv->flags & SERV_NO_ADDR) flags = F_NXDOMAIN; @@ -243,6 +249,8 @@ static unsigned int search_servers(time_t now, union all_addr **addrpp, unsigned *type = 0; /* use normal servers for this domain */ *domain = NULL; } + if (serv_domain && !*serv_domain) + *serv_domain = server_domain_find_domain(*domain); return flags; } @@ -304,6 +312,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, unsigned int flags = 0; unsigned int fwd_flags = 0; struct server *start = NULL; + struct server_domain *sd = NULL; void *hash = hash_questions(header, plen, daemon->namebuff); #ifdef HAVE_DNSSEC int do_dnssec = 0; @@ -422,8 +431,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, forward->sentto->failed_queries++; if (!option_bool(OPT_ORDER) && old_src) { + sd = forward->sentto->serv_domain; forward->forwardall = 1; - daemon->last_server = NULL; + if (sd) + sd->last_server = NULL; } type = forward->sentto->flags & SERV_TYPE; #ifdef HAVE_DNSSEC @@ -439,8 +450,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, /* new query */ if (gotname) - flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); - + flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind, &sd); + #ifdef HAVE_DNSSEC do_dnssec = type & SERV_DO_DNSSEC; #endif @@ -482,18 +493,18 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, always try all the available servers, otherwise, use the one last known to work. */ - if (type == 0) + if (sd) { if (option_bool(OPT_ORDER)) start = daemon->servers; - else if (!(start = daemon->last_server) || - daemon->forwardcount++ > FORWARD_TEST || - difftime(now, daemon->forwardtime) > FORWARD_TIME) + else if (!(start = sd->last_server) || + sd->forwardcount++ > FORWARD_TEST || + difftime(now, sd->forwardtime) > FORWARD_TIME) { start = daemon->servers; forward->forwardall = 1; - daemon->forwardcount = 0; - daemon->forwardtime = now; + sd->forwardcount = 0; + sd->forwardtime = now; } } else @@ -844,6 +855,7 @@ void reply_query(int fd, time_t now) size_t nn; struct server *server; void *hash; + struct server_domain *sd; /* packet buffer overwritten */ daemon->srv_save = NULL; @@ -968,7 +980,8 @@ void reply_query(int fd, time_t now) } server = forward->sentto; - if ((forward->sentto->flags & SERV_TYPE) == 0) + sd = server->serv_domain; + if (sd) { if (RCODE(header) == REFUSED) server = NULL; @@ -986,7 +999,7 @@ void reply_query(int fd, time_t now) } } if (!option_bool(OPT_ALL_SERVERS)) - daemon->last_server = server; + sd->last_server = server; } /* We tried resending to this server with a smaller maximum size and got an answer. @@ -1093,7 +1106,7 @@ void reply_query(int fd, time_t now) /* Find server to forward to. This will normally be the same as for the original query, but may be another if servers for domains are involved. */ - if (search_servers(now, NULL, F_DNSSECOK, daemon->keyname, &type, &domain, NULL) == 0) + if (search_servers(now, NULL, F_DNSSECOK, daemon->keyname, &type, &domain, NULL, &sd) == 0) { struct server *start, *new_server = NULL; start = server = forward->sentto; @@ -1664,7 +1677,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si /* Find server to forward to. This will normally be the same as for the original query, but may be another if servers for domains are involved. */ - if (search_servers(now, NULL, F_DNSSECOK, keyname, &type, &domain, NULL) != 0) + if (search_servers(now, NULL, F_DNSSECOK, keyname, &type, &domain, NULL, NULL) != 0) { new_status = STAT_ABANDONED; break; @@ -1944,12 +1957,13 @@ unsigned char *tcp_request(int confd, time_t now, union all_addr *addrp = NULL; int type = SERV_DO_DNSSEC; char *domain = NULL; + struct server_domain *sd = NULL; unsigned char *oph = find_pseudoheader(header, size, NULL, NULL, NULL, NULL); size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet, &cacheable); if (gotname) - flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); + flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind, &sd); #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID) && (type & SERV_DO_DNSSEC)) @@ -1970,10 +1984,10 @@ unsigned char *tcp_request(int confd, time_t now, type &= ~SERV_DO_DNSSEC; - if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server) + if (!sd || option_bool(OPT_ORDER) || !sd->last_server) last_server = daemon->servers; else - last_server = daemon->last_server; + last_server = sd->last_server; if (!flags && last_server) { @@ -2567,9 +2581,7 @@ void server_gone(struct server *server) if (daemon->randomsocks[i].refcount != 0 && daemon->randomsocks[i].serv == server) daemon->randomsocks[i].serv = NULL; - if (daemon->last_server == server) - daemon->last_server = NULL; - + /* last_server cleared by server_domains_cleanup */ if (daemon->srv_save == server) daemon->srv_save = NULL; } diff --git a/src/network.c b/src/network.c index 3600250..1fa81ff 100644 --- a/src/network.c +++ b/src/network.c @@ -1537,6 +1537,29 @@ void cleanup_servers(void) #endif } +static void server_domains_cleanup(void) +{ + struct server_domain *sd, *tmp, **up; + + /* unlink and free anything still marked. */ + for (up = &daemon->server_domains, sd=*up; sd; sd = tmp) + { + tmp = sd->next; + if (sd->flags & SERV_MARK) + { + *up = sd->next; + if (sd->domain) + free(sd->domain); + free(sd); + } + else { + up = &sd->next; + if (sd->last_server && (sd->last_server->flags & SERV_MARK)) + sd->last_server = NULL; + } + } +} + void add_update_server(int flags, union mysockaddr *addr, union mysockaddr *source_addr, @@ -1616,10 +1639,72 @@ void add_update_server(int flags, } } +static const char *server_get_domain(const struct server *serv) +{ + const char *domain = serv->domain; + + if (serv->flags & SERV_HAS_DOMAIN) + /* .example.com is valid */ + while (*domain == '.') + domain++; + + return domain; +} + +struct server_domain *server_domain_find_domain(const char *domain) +{ + struct server_domain *sd; + for (sd = daemon->server_domains; sd; sd = sd->next) + if ((!domain && sd->domain == domain) || (domain && sd->domain && hostname_isequal(domain, sd->domain))) + return sd; + return NULL; +} + +/**< Test structure has already set domain pointer. + * + * If not, create a new record. */ +struct server_domain *server_domain_new(struct server *serv) +{ + struct server_domain *sd; + + if ((sd = whine_malloc(sizeof(struct server_domain)))) + { + const char *domain = server_get_domain(serv); + + /* Ensure all serv->domain values have own record in server_domain. + * Add a new record. */ + if (domain) + { + size_t len = strlen(domain)+1; + sd->domain = whine_malloc(len); + if (sd->domain) + memcpy(sd->domain, domain, len); + } + sd->next = daemon->server_domains; + serv->serv_domain = sd; + daemon->server_domains = sd; + } + return sd; +} + +/**< Test structure has already set domain pointer. + * + * If not, create a new record. */ +static void server_domain_check(struct server *serv) +{ + struct server_domain *sd = serv->serv_domain; + + if (sd) + sd->flags &= (~SERV_MARK); /* found domain, mark active */ + else + server_domain_new(serv); +} + void check_servers(void) { struct irec *iface; struct server *serv; + struct server_domain *sd; struct serverfd *sfd, *tmp, **up; int port = 0, count; int locals = 0; @@ -1632,10 +1717,14 @@ void check_servers(void) for (sfd = daemon->sfds; sfd; sfd = sfd->next) sfd->used = sfd->preallocated; + for (sd = daemon->server_domains; sd; sd = sd->next) + sd->flags |= SERV_MARK; + for (count = 0, serv = daemon->servers; serv; serv = serv->next) { if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND))) { + /* Init edns_pktsz for newly created server records. */ if (serv->edns_pktsz == 0) serv->edns_pktsz = daemon->edns_pktsz; @@ -1651,12 +1740,8 @@ void check_servers(void) if (serv->flags & SERV_HAS_DOMAIN) { struct ds_config *ds; - char *domain = serv->domain; - - /* .example.com is valid */ - while (*domain == '.') - domain++; - + const char *domain = server_get_domain(serv); + for (ds = daemon->ds; ds; ds = ds->next) if (ds->name[0] != 0 && hostname_isequal(domain, ds->name)) break; @@ -1666,7 +1751,6 @@ void check_servers(void) } } #endif - port = prettyprint_addr(&serv->addr, daemon->namebuff); /* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */ @@ -1701,6 +1785,8 @@ void check_servers(void) if (serv->sfd) serv->sfd->used = 1; + + server_domain_check(serv); } if (!(serv->flags & SERV_NO_REBIND) && !(serv->flags & SERV_LITERAL_ADDRESS)) @@ -1763,6 +1849,7 @@ void check_servers(void) up = &sfd->next; } + server_domains_cleanup(); cleanup_servers(); } diff --git a/src/option.c b/src/option.c index 6de5914..e4e3182 100644 --- a/src/option.c +++ b/src/option.c @@ -928,6 +928,7 @@ static struct server *add_rev4(struct in_addr addr, int msize) p += sprintf(p, "in-addr.arpa"); serv->flags = SERV_HAS_DOMAIN; + server_domain_new(serv); serv->next = daemon->servers; daemon->servers = serv; @@ -952,6 +953,7 @@ static struct server *add_rev6(struct in6_addr *addr, int msize) p += sprintf(p, "ip6.arpa"); serv->flags = SERV_HAS_DOMAIN; + server_domain_new(serv); serv->next = daemon->servers; daemon->servers = serv; @@ -2292,6 +2294,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma memset(serv, 0, sizeof(struct server)); serv->domain = d; serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; + server_domain_new(serv); serv->next = daemon->servers; daemon->servers = serv; } @@ -2335,6 +2338,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma memset(serv, 0, sizeof(struct server)); serv->domain = d; serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; + server_domain_new(serv); serv->next = daemon->servers; daemon->servers = serv; } @@ -2587,6 +2591,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma newlist = serv; serv->domain = domain; serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS; + server_domain_new(serv); arg = end; if (rebind) break; -- 2.34.1