[T106][ZXW-22]7520V3SCV2.01.01.02P42U09_VEC_V0.8_AP_VEC origin source commit
Change-Id: Ic6e05d89ecd62fc34f82b23dcf306c93764aec4b
diff --git a/ap/app/dnsmasq/dnsmasq-2.86/src/domain-match.c b/ap/app/dnsmasq/dnsmasq-2.86/src/domain-match.c
new file mode 100755
index 0000000..f8e4796
--- /dev/null
+++ b/ap/app/dnsmasq/dnsmasq-2.86/src/domain-match.c
@@ -0,0 +1,698 @@
+/* dnsmasq is Copyright (c) 2000-2021 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
+ the Free Software Foundation; version 2 dated June, 1991, or
+ (at your option) version 3 dated 29 June, 2007.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "dnsmasq.h"
+
+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)
+#ifdef HAVE_LOOP
+ if (!(serv->flags & SERV_LOOP))
+#endif
+ {
+ count++;
+ if (serv->flags & SERV_WILDCARD)
+ daemon->server_has_wildcard = 1;
+ }
+
+ for (serv = daemon->local_domains; serv; serv = serv->next)
+ {
+ count++;
+ if (serv->flags & SERV_WILDCARD)
+ daemon->server_has_wildcard = 1;
+ }
+
+ daemon->serverarraysz = count;
+
+ if (count > daemon->serverarrayhwm)
+ {
+ struct server **new;
+
+ count += 10; /* A few extra without re-allocating. */
+
+ if ((new = whine_malloc(count * sizeof(struct server *))))
+ {
+ if (daemon->serverarray)
+ free(daemon->serverarray);
+
+ daemon->serverarray = new;
+ daemon->serverarrayhwm = count;
+ }
+ }
+
+ count = 0;
+
+ for (serv = daemon->servers; serv; serv = serv->next)
+#ifdef HAVE_LOOP
+ if (!(serv->flags & SERV_LOOP))
+#endif
+ {
+ daemon->serverarray[count] = serv;
+ serv->serial = count;
+ serv->last_server = -1;
+ count++;
+ }
+
+ 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_IS_LOCAL))
+ daemon->serverarray[count]->arrayposn = count;
+}
+
+/* we're looking for the server whose domain is the longest exact match
+ to the RH end of qdomain, or a local address if the flags match.
+ Add '.' to the LHS of the query string so
+ server=/.example.com/ works.
+
+ A flag of F_SERVER returns an upstream server only.
+ A flag of F_DNSSECOK returns a DNSSEC capable server only and
+ also disables NODOTS servers from consideration.
+ A flag of F_DOMAINSRV returns a domain-specific server only.
+ A flag of F_CONFIG returns anything that generates a local
+ reply of IPv4 or IPV6.
+ return 0 if nothing found, 1 otherwise.
+*/
+int lookup_domain(char *domain, int flags, int *lowout, int *highout)
+{
+ int rc, crop_query, nodots;
+ ssize_t qlen;
+ int try, high, low = 0;
+ int nlow = 0, nhigh = 0;
+ char *cp, *qdomain = domain;
+
+ /* may be no configured servers. */
+ if (daemon->serverarraysz == 0)
+ return 0;
+
+ /* find query length and presence of '.' */
+ for (cp = qdomain, nodots = 1, qlen = 0; *cp; qlen++, cp++)
+ if (*cp == '.')
+ nodots = 0;
+
+ /* Handle empty name, and searches for DNSSEC queries without
+ diverting to NODOTS servers. */
+ if (qlen == 0 || flags & F_DNSSECOK)
+ nodots = 0;
+
+ /* Search shorter and shorter RHS substrings for a match */
+ while (qlen >= 0)
+ {
+ /* Note that when we chop off a label, all the possible matches
+ MUST be at a larger index than the nearest failing match with one more
+ character, since the array is sorted longest to smallest. Hence
+ we don't reset low to zero here, we can go further below and crop the
+ search string to the size of the largest remaining server
+ when this match fails. */
+ high = daemon->serverarraysz;
+ crop_query = 1;
+
+ /* binary search */
+ while (1)
+ {
+ try = (low + high)/2;
+
+ if ((rc = order(qdomain, qlen, daemon->serverarray[try])) == 0)
+ break;
+
+ if (rc < 0)
+ {
+ if (high == try)
+ {
+ /* qdomain is longer or same length as longest domain, and try == 0
+ crop the query to the longest domain. */
+ crop_query = qlen - daemon->serverarray[try]->domain_len;
+ break;
+ }
+ high = try;
+ }
+ else
+ {
+ if (low == try)
+ {
+ /* try now points to the last domain that sorts before the query, so
+ we know that a substring of the query shorter than it is required to match, so
+ find the largest domain that's shorter than try. Note that just going to
+ try+1 is not optimal, consider searching bbb in (aaa,ccc,bb). try will point
+ to aaa, since ccc sorts after bbb, but the first domain that has a chance to
+ match is bb. So find the length of the first domain later than try which is
+ is shorter than it.
+ There's a nasty edge case when qdomain sorts before _any_ of the
+ server domains, where try _doesn't point_ to the last domain that sorts
+ before the query, since no such domain exists. In that case, the loop
+ exits via the rc < 0 && high == try path above and this code is
+ not executed. */
+ ssize_t len, old = daemon->serverarray[try]->domain_len;
+ while (++try != daemon->serverarraysz)
+ {
+ if (old != (len = daemon->serverarray[try]->domain_len))
+ {
+ crop_query = qlen - len;
+ break;
+ }
+ }
+ break;
+ }
+ low = try;
+ }
+ };
+
+ if (rc == 0)
+ {
+ int found = 1;
+
+ if (daemon->server_has_wildcard)
+ {
+ /* if we have example.com and *example.com we need to check against *example.com,
+ but the binary search may have found either. Use the fact that example.com is sorted before *example.com
+ We favour example.com in the case that both match (ie www.example.com) */
+ while (try != 0 && order(qdomain, qlen, daemon->serverarray[try-1]) == 0)
+ try--;
+
+ if (!(qdomain == domain || *qdomain == 0 || *(qdomain-1) == '.'))
+ {
+ while (try < daemon->serverarraysz-1 && order(qdomain, qlen, daemon->serverarray[try+1]) == 0)
+ try++;
+
+ if (!(daemon->serverarray[try]->flags & SERV_WILDCARD))
+ found = 0;
+ }
+ }
+
+ if (found)
+ {
+ /* We've matched a setting which says to use servers without a domain.
+ Continue the search with empty query */
+ if (daemon->serverarray[try]->flags & SERV_USE_RESOLV)
+ crop_query = qlen;
+ else if (filter_servers(try, flags, &nlow, &nhigh))
+ /* We have a match, but it may only be (say) an IPv6 address, and
+ if the query wasn't for an AAAA record, it's no good, and we need
+ to continue generalising */
+ break;
+ }
+ }
+
+ /* crop_query must be at least one always. */
+ if (crop_query == 0)
+ crop_query = 1;
+
+ /* strip chars off the query based on the largest possible remaining match,
+ then continue to the start of the next label unless we have a wildcard
+ domain somewhere, in which case we have to go one at a time. */
+ qlen -= crop_query;
+ qdomain += crop_query;
+ if (!daemon->server_has_wildcard)
+ while (qlen > 0 && (*(qdomain-1) != '.'))
+ qlen--, qdomain++;
+ }
+
+ /* domain has no dots, and we have at least one server configured to handle such,
+ These servers always sort to the very end of the array.
+ A configured server eg server=/lan/ will take precdence. */
+ if (nodots &&
+ (daemon->serverarray[daemon->serverarraysz-1]->flags & SERV_FOR_NODOTS) &&
+ (nlow == nhigh || daemon->serverarray[nlow]->domain_len == 0))
+ filter_servers(daemon->serverarraysz-1, flags, &nlow, &nhigh);
+
+ if (lowout)
+ *lowout = nlow;
+
+ if (highout)
+ *highout = nhigh;
+
+ if (nlow == nhigh)
+ return 0;
+
+ return 1;
+}
+
+/* Return first server in group of equivalent servers; this is the "master" record. */
+int server_samegroup(struct server *a, struct server *b)
+{
+ return order_servers(a, b) == 0;
+}
+
+int filter_servers(int seed, int flags, int *lowout, int *highout)
+{
+ int nlow = seed, nhigh = seed;
+ int i;
+
+ /* expand nlow and nhigh to cover all the records with the same domain
+ nlow is the first, nhigh - 1 is the last. nlow=nhigh means no servers,
+ which can happen below. */
+ while (nlow > 0 && order_servers(daemon->serverarray[nlow-1], daemon->serverarray[nlow]) == 0)
+ nlow--;
+
+ while (nhigh < daemon->serverarraysz-1 && order_servers(daemon->serverarray[nhigh], daemon->serverarray[nhigh+1]) == 0)
+ nhigh++;
+
+ nhigh++;
+
+#define SERV_LOCAL_ADDRESS (SERV_6ADDR | SERV_4ADDR | SERV_ALL_ZEROS)
+
+ if (flags & F_CONFIG)
+ {
+ /* We're just lookin for any matches that return an RR. */
+ for (i = nlow; i < nhigh; i++)
+ if (daemon->serverarray[i]->flags & SERV_LOCAL_ADDRESS)
+ break;
+
+ /* failed, return failure. */
+ if (i == nhigh)
+ nhigh = nlow;
+ }
+ else
+ {
+ /* Now the servers are on order between low and high, in the order
+ IPv6 addr, IPv4 addr, return zero for both, send upstream, no-data return.
+
+ See which of those match our query in that priority order and narrow (low, high) */
+
+ for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
+
+ if (i != nlow && (flags & F_IPV6))
+ nhigh = i;
+ else
+ {
+ nlow = i;
+
+ for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_4ADDR); i++);
+
+ if (i != nlow && (flags & F_IPV4))
+ nhigh = i;
+ else
+ {
+ nlow = i;
+
+ for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_ALL_ZEROS); i++);
+
+ if (i != nlow && (flags & (F_IPV4 | F_IPV6)))
+ nhigh = i;
+ else
+ {
+ nlow = i;
+
+ /* now look for a server */
+ for (i = nlow; i < nhigh && !(daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
+
+ if (i != nlow)
+ {
+ /* If we want a server that can do DNSSEC, and this one can't,
+ return nothing, similarly if were looking only for a server
+ for a particular domain. */
+ if ((flags & F_DNSSECOK) && !(daemon->serverarray[nlow]->flags & SERV_DO_DNSSEC))
+ nlow = nhigh;
+ else if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
+ nlow = nhigh;
+ else
+ nhigh = i;
+ }
+ else
+ {
+ /* --local=/domain/, only return if we don't need a server. */
+ if (flags & (F_DNSSECOK | F_DOMAINSRV | F_SERVER))
+ nhigh = i;
+ }
+ }
+ }
+ }
+ }
+
+ *lowout = nlow;
+ *highout = nhigh;
+
+ return (nlow != nhigh);
+}
+
+int is_local_answer(time_t now, int first, char *name)
+{
+ int flags = 0;
+ int rc = 0;
+
+ if ((flags = daemon->serverarray[first]->flags) & SERV_LITERAL_ADDRESS)
+ {
+ if (flags & SERV_4ADDR)
+ rc = F_IPV4;
+ else if (flags & SERV_6ADDR)
+ rc = F_IPV6;
+ else if (flags & SERV_ALL_ZEROS)
+ rc = F_IPV4 | F_IPV6;
+ else
+ {
+ /* argument first is the first struct server which matches the query type;
+ now roll back to the server which is just the same domain, to check if that
+ provides an answer of a different type. */
+
+ for (;first > 0 && order_servers(daemon->serverarray[first-1], daemon->serverarray[first]) == 0; first--);
+
+ if ((daemon->serverarray[first]->flags & SERV_LOCAL_ADDRESS) ||
+ check_for_local_domain(name, now))
+ rc = F_NOERR;
+ else
+ rc = F_NXDOMAIN;
+ }
+ }
+
+ return rc;
+}
+
+size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede)
+{
+ int trunc = 0;
+ unsigned char *p;
+ int start;
+ union all_addr addr;
+
+ if (flags & (F_NXDOMAIN | F_NOERR))
+ log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL);
+
+ setup_reply(header, flags, ede);
+
+ if (!(p = skip_questions(header, size)))
+ return 0;
+
+ if (flags & gotname & F_IPV4)
+ for (start = first; start != last; start++)
+ {
+ struct serv_addr4 *srv = (struct serv_addr4 *)daemon->serverarray[start];
+
+ if (srv->flags & SERV_ALL_ZEROS)
+ memset(&addr, 0, sizeof(addr));
+ else
+ addr.addr4 = srv->addr;
+
+ header->ancount = htons(ntohs(header->ancount) + 1);
+ add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr);
+ log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL);
+ }
+
+ if (flags & gotname & F_IPV6)
+ for (start = first; start != last; start++)
+ {
+ struct serv_addr6 *srv = (struct serv_addr6 *)daemon->serverarray[start];
+
+ if (srv->flags & SERV_ALL_ZEROS)
+ memset(&addr, 0, sizeof(addr));
+ else
+ addr.addr6 = srv->addr;
+
+ header->ancount = htons(ntohs(header->ancount) + 1);
+ add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr);
+ log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL);
+ }
+
+ if (trunc)
+ header->hb3 |= HB3_TC;
+
+ return p - (unsigned char *)header;
+}
+
+#ifdef HAVE_DNSSEC
+int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp)
+{
+ int first, last, index;
+
+ /* Find server to send DNSSEC query to. This will normally be the
+ same as for the original query, but may be another if
+ servers for domains are involved. */
+ if (!lookup_domain(keyname, F_DNSSECOK, &first, &last))
+ return -1;
+
+ for (index = first; index != last; index++)
+ if (daemon->serverarray[index] == server)
+ break;
+
+ /* No match to server used for original query.
+ Use newly looked up set. */
+ if (index == last)
+ index = daemon->serverarray[first]->last_server == -1 ?
+ first : daemon->serverarray[first]->last_server;
+
+ if (firstp)
+ *firstp = first;
+
+ if (lastp)
+ *lastp = last;
+
+ return index;
+}
+#endif
+
+/* order by size, then by dictionary order */
+static int order(char *qdomain, size_t qlen, struct server *serv)
+{
+ size_t dlen = 0;
+
+ /* servers for dotless names always sort last
+ searched for name is never dotless. */
+ if (serv->flags & SERV_FOR_NODOTS)
+ return -1;
+
+ dlen = serv->domain_len;
+
+ if (qlen < dlen)
+ return 1;
+
+ if (qlen > dlen)
+ return -1;
+
+ return strcmp(qdomain, serv->domain);
+}
+
+static int order_servers(struct server *s1, struct server *s2)
+{
+ int rc;
+
+ /* need full comparison of dotless servers in
+ order_qsort() and filter_servers() */
+
+ if (s1->flags & SERV_FOR_NODOTS)
+ return (s2->flags & SERV_FOR_NODOTS) ? 0 : 1;
+
+ if ((rc = order(s1->domain, s1->domain_len, s2)) != 0)
+ return rc;
+
+ /* For identical domains, sort wildcard ones first */
+ if (s1->flags & SERV_WILDCARD)
+ return (s2->flags & SERV_WILDCARD) ? 0 : 1;
+
+ return (s2->flags & SERV_WILDCARD) ? -1 : 0;
+}
+
+static int order_qsort(const void *a, const void *b)
+{
+ int rc;
+
+ struct server *s1 = *((struct server **)a);
+ struct server *s2 = *((struct server **)b);
+
+ rc = order_servers(s1, s2);
+
+ /* Sort all literal NODATA and local IPV4 or IPV6 responses together,
+ in a very specific order. We flip the SERV_LITERAL_ADDRESS bit
+ so the order is IPv6 literal, IPv4 literal, all-zero literal,
+ upstream server, NXDOMAIN literal. */
+ if (rc == 0)
+ rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) -
+ ((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS);
+
+ /* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
+ if (rc == 0)
+ if (!(s1->flags & SERV_LITERAL_ADDRESS))
+ rc = s1->serial - s2->serial;
+
+ 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;
+ else
+ serv->flags &= ~SERV_MARK;
+
+ for (serv = daemon->local_domains; serv; serv = serv->next)
+ if (serv->flags & flag)
+ serv->flags |= SERV_MARK;
+ else
+ 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 = NULL;
+ char *alloc_domain;
+
+ if (!domain)
+ domain = "";
+
+ /* .domain == domain, for historical reasons. */
+ if (*domain == '.')
+ while (*domain == '.') domain++;
+ else if (*domain == '*')
+ {
+ domain++;
+ if (*domain != 0)
+ flags |= SERV_WILDCARD;
+ }
+
+ if (*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
+ only do this for forwarding servers, not
+ address or local, to avoid delays on large numbers. */
+ if (flags & SERV_IS_LOCAL)
+ for (serv = daemon->servers; serv; serv = serv->next)
+ if ((serv->flags & SERV_MARK) &&
+ hostname_isequal(alloc_domain, serv->domain))
+ 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;
+}
+