[Dnsmasq-discuss] [PATCH 4/4] process client request based on domain name
weichen302 at icloud.com
weichen302 at icloud.com
Sun Feb 8 10:11:22 GMT 2015
Process client's UDP & TCP request based on domain name. If the domain name
matches a ipset rule, the result ip will be added to ipset.
---
src/forward.c | 779 +++++++++++++++++++++++++++++----------------------------
1 file changed, 401 insertions(+), 378 deletions(-)
diff --git a/src/forward.c b/src/forward.c
index 438e9fa..a846a7b 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -30,7 +30,9 @@ static int do_check_sign(struct frec *forward, int status, time_t now, char *nam
static int send_check_sign(struct frec *forward, time_t now, struct dns_header *header, size_t plen,
char *name, char *keyname);
#endif
-
+static int tcp_conn_serv(struct server *serv, time_t now,
+ unsigned char *packet, size_t payload_size,
+ union mysockaddr *peer_addr);
/* Send a UDP packet with its source address set as "source"
unless nowild is true, when we just send it with the kernel default */
@@ -118,126 +120,115 @@ int send_from(int fd, int nowild, char *packet, size_t len,
return 1;
}
-
-static unsigned int search_servers(time_t now, struct all_addr **addrpp,
- unsigned int qtype, char *qdomain, int *type, char **domain, int *norebind)
-
+
+/* search domain pattern for --address, --local, --server, --rebind-domain-ok
+
+ if matches --address, the address is stored in addrpp
+
+ if matches --server, a pointer to one of servers in daemon->servers is
+ stored in fwdserv, unless --server=/example.org/#, in which case fwdserv
+ will be NULL, means use normal server
+
+ if matches --rebind-domain-ok, the pass in norebind will be set to 1
+
+ we find largest match, e.g. given pattern debian.org and cn.debian.org,
+ ftp.cn.debian.org will match cn.debian.org
+ */
+static unsigned int
+search_servers (time_t now, struct all_addr **addrpp,
+ unsigned int qtype, char *qdomain, int *type,
+ char **domain, int *norebind, struct server **fwdserv)
{
- /* If the query ends in the domain in one of our servers, set
- domain to point to that name. We find the largest match to allow both
- domain.org and sub.domain.org to exist. */
-
- unsigned int namelen = strlen(qdomain);
- unsigned int matchlen = 0;
- struct server *serv;
+ unsigned int namelen = strlen (qdomain);
unsigned int flags = 0;
-
- for (serv = daemon->servers; serv; serv=serv->next)
- /* domain matches take priority over NODOTS matches */
- if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.') && namelen != 0)
- {
- unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
- *type = SERV_FOR_NODOTS;
- if (serv->flags & SERV_NO_ADDR)
- flags = F_NXDOMAIN;
- else if (serv->flags & SERV_LITERAL_ADDRESS)
- {
- if (sflag & qtype)
- {
- flags = sflag;
- if (serv->addr.sa.sa_family == AF_INET)
- *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
-#ifdef HAVE_IPV6
- else
- *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
-#endif
- }
- else if (!flags || (flags & F_NXDOMAIN))
- flags = F_NOERR;
- }
- }
- else if (serv->flags & SERV_HAS_DOMAIN)
- {
- unsigned int domainlen = strlen(serv->domain);
- char *matchstart = qdomain + namelen - domainlen;
- if (namelen >= domainlen &&
- hostname_isequal(matchstart, serv->domain) &&
- (domainlen == 0 || namelen == domainlen || *(matchstart-1) == '.' ))
- {
- if (serv->flags & SERV_NO_REBIND)
- *norebind = 1;
- else
- {
- unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
- /* implement priority rules for --address and --server for same domain.
- --address wins if the address is for the correct AF
- --server wins otherwise. */
- if (domainlen != 0 && domainlen == matchlen)
- {
- if ((serv->flags & SERV_LITERAL_ADDRESS))
- {
- if (!(sflag & qtype) && flags == 0)
- continue;
- }
- else
- {
- if (flags & (F_IPV4 | F_IPV6))
- continue;
- }
- }
-
- if (domainlen >= matchlen)
- {
- *type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV | SERV_NO_REBIND);
- *domain = serv->domain;
- matchlen = domainlen;
- if (serv->flags & SERV_NO_ADDR)
- flags = F_NXDOMAIN;
- else if (serv->flags & SERV_LITERAL_ADDRESS)
- {
- if (sflag & qtype)
- {
- flags = sflag;
- if (serv->addr.sa.sa_family == AF_INET)
- *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
+ unsigned int sflag;
+ struct dict_node *np;
+ struct special_domain *obj;
+
+ *type = 0;
+ /* label of root node is "#", means --address=/#/1.2.3.4 */
+ if (daemon->dh_special_domains && daemon->dh_special_domains->label &&
+ *daemon->dh_special_domains->label == '#')
+ np = daemon->dh_special_domains;
+ else
+ np = match_domain (daemon->dh_special_domains, qdomain);
+
+ if (np != NULL)
+ {
+ obj = (struct special_domain *) np->obj;
+
+ *type |= SERV_HAS_DOMAIN;
+
+ if (obj->domain_flags & SERV_NO_REBIND)
+ *norebind = 1;
+
+ // no server, domain is local only
+ if (obj->domain_flags & SERV_NO_ADDR)
+ {
+ flags = F_NXDOMAIN;
+
+ }
+ else if (obj->domain_flags & SERV_LITERAL_ADDRESS)
+ {
+ // --address and AF matches
+ sflag = obj->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
+ if (sflag & qtype)
+ {
+ flags = sflag;
+ if (obj->addr.sa.sa_family == AF_INET)
+ *addrpp = (struct all_addr *) &obj->addr.in.sin_addr;
#ifdef HAVE_IPV6
- else
- *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
+ else
+ *addrpp = (struct all_addr *) &obj->addr.in6.sin6_addr;
#endif
- }
- else if (!flags || (flags & F_NXDOMAIN))
- flags = F_NOERR;
- }
- else
- flags = 0;
- }
- }
- }
- }
-
- if (flags == 0 && !(qtype & F_QUERY) &&
- option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
+ }
+
+ }
+ else if (obj->domain_flags & SERV_USE_RESOLV)
+ {
+ // --server=8.8.8.8
+ *type = 0; // use normal server
+ *fwdserv = NULL;
+
+ }
+ else
+ {
+ *fwdserv = obj->server;
+ flags = 0;
+ }
+ }
+ else
+ {
+ *type = 0; /* use normal servers for this domain */
+ *fwdserv = NULL;
+ }
+
+ if (flags == 0 && !(qtype & F_QUERY) &&
+ option_bool (OPT_NODOTS_LOCAL) && !strchr (qdomain, '.')
+ && namelen != 0)
/* don't forward A or AAAA queries for simple names, except the empty name */
flags = F_NOERR;
-
- if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now))
+
+ if (flags == F_NXDOMAIN && check_for_local_domain (qdomain, now))
flags = F_NOERR;
if (flags)
{
int logflags = 0;
-
+
if (flags == F_NXDOMAIN || flags == F_NOERR)
- logflags = F_NEG | qtype;
-
- log_query(logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp, NULL);
+ logflags = F_NEG | qtype;
+
+ log_query (logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp,
+ NULL);
}
else if ((*type) & SERV_USE_RESOLV)
{
- *type = 0; /* use normal servers for this domain */
+ *type = 0; /* use normal servers for this domain */
*domain = NULL;
}
- return flags;
+
+ return flags;
}
static int forward_query(int udpfd, union mysockaddr *udpaddr,
@@ -250,6 +241,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
struct all_addr *addrp = NULL;
unsigned int flags = 0;
struct server *start = NULL;
+ struct server *fwdserv = NULL;
#ifdef HAVE_DNSSEC
void *hash = hash_questions(header, plen, daemon->namebuff);
#else
@@ -321,7 +313,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
else
{
if (gotname)
- flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
+ flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind, &fwdserv);
if (!flags && !(forward = get_new_frec(now, NULL, 0)))
/* table full - server failure. */
@@ -386,7 +378,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (!flags && forward)
{
- struct server *firstsentto = start;
int forwarded = 0;
/* If a query is retried, use the log_id for the retry when logging the answer. */
@@ -422,95 +413,89 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
#endif
- while (1)
- {
- /* only send to servers dealing with our domain.
- domain may be NULL, in which case server->domain
- must be NULL also. */
-
- if (type == (start->flags & SERV_TYPE) &&
- (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
- !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
- {
- int fd;
+ // we have the server for our domain by lookup daemon->dh_special_domains
+ int fd;
- /* find server socket to use, may need to get random one. */
- if (start->sfd)
- fd = start->sfd->fd;
- else
- {
+ /* didn't find a server matches query domain */
+ if (fwdserv == NULL)
+ {
+ for (fwdserv = daemon->servers;
+ fwdserv != NULL; fwdserv = fwdserv->next)
+ {
+ //TODO figure out how to skip unresponsive server
+ if (!(fwdserv->flags & (SERV_HAS_DOMAIN | SERV_LOOP)))
+ {
+ break;
+ }
+ }
+ }
+
+ if (fwdserv->sfd)
+ fd = fwdserv->sfd->fd;
+ else
+ {
#ifdef HAVE_IPV6
- if (start->addr.sa.sa_family == AF_INET6)
- {
- if (!forward->rfd6 &&
- !(forward->rfd6 = allocate_rfd(AF_INET6)))
- break;
- daemon->rfd_save = forward->rfd6;
- fd = forward->rfd6->fd;
- }
- else
+ if (fwdserv->addr.sa.sa_family == AF_INET6)
+ {
+ if (forward->rfd6 == NULL)
+ forward->rfd6 = allocate_rfd (AF_INET6);
+ daemon->rfd_save = forward->rfd6;
+ fd = forward->rfd6->fd;
+ }
+ else
#endif
- {
- if (!forward->rfd4 &&
- !(forward->rfd4 = allocate_rfd(AF_INET)))
- break;
- daemon->rfd_save = forward->rfd4;
- fd = forward->rfd4->fd;
- }
+ {
+ if (forward->rfd4 == NULL)
+ forward->rfd4 = allocate_rfd (AF_INET);
+ daemon->rfd_save = forward->rfd4;
+ fd = forward->rfd4->fd;
+ }
#ifdef HAVE_CONNTRACK
- /* Copy connection mark of incoming query to outgoing connection. */
- if (option_bool(OPT_CONNTRACK))
- {
- unsigned int mark;
- if (get_incoming_mark(&forward->source, &forward->dest, 0, &mark))
- setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
- }
+ /* Copy connection mark of incoming query to outgoing connection. */
+ if (option_bool (OPT_CONNTRACK))
+ {
+ unsigned int mark;
+ if (get_incoming_mark (&forward->source, &forward->dest, 0, &mark))
+ setsockopt (fd, SOL_SOCKET, SO_MARK, &mark,
+ sizeof (unsigned int));
+ }
#endif
- }
-
- if (sendto(fd, (char *)header, plen, 0,
- &start->addr.sa,
- sa_len(&start->addr)) == -1)
- {
- if (retry_send())
- continue;
- }
- else
- {
- /* Keep info in case we want to re-send this packet */
- daemon->srv_save = start;
- daemon->packet_len = plen;
-
- if (!gotname)
- strcpy(daemon->namebuff, "query");
- if (start->addr.sa.sa_family == AF_INET)
- log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&start->addr.in.sin_addr, NULL);
+ }
+
+ //TODO how to retry here?
+ if (sendto (fd, (char *) header, plen, 0,
+ &fwdserv->addr.sa, sa_len (&fwdserv->addr)) == -1)
+ {
+ retry_send ();
+ }
+
+ else
+ {
+ /* Keep info in case we want to re-send this packet */
+ daemon->srv_save = fwdserv;
+ daemon->packet_len = plen;
+
+ if (!gotname)
+ strcpy (daemon->namebuff, "query");
+ if (fwdserv->addr.sa.sa_family == AF_INET)
+ log_query (F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
+ (struct all_addr *) &fwdserv->addr.in.sin_addr, NULL);
#ifdef HAVE_IPV6
- else
- log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&start->addr.in6.sin6_addr, NULL);
-#endif
- start->queries++;
- forwarded = 1;
- forward->sentto = start;
- if (!forward->forwardall)
- break;
- forward->forwardall++;
- }
- }
-
- if (!(start = start->next))
- start = daemon->servers;
-
- if (start == firstsentto)
- break;
- }
-
+ else
+ log_query (F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
+ (struct all_addr *) &fwdserv->addr.in6.sin6_addr, NULL);
+#endif
+ fwdserv->queries++;
+ forwarded = 1;
+ forward->sentto = fwdserv;
+ if (forward->forwardall)
+ forward->forwardall++;
+ }
+
if (forwarded)
- return 1;
-
+ return 1;
+
/* could not send on, prepare to return */
header->id = htons(forward->orig_id);
free_frec(forward); /* cancel */
@@ -533,29 +518,21 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
char **sets = 0;
int munged = 0, is_sign;
size_t plen;
+ struct dict_node *np;
+ struct ipsets_names *obj;
(void)ad_reqd;
(void) do_bit;
#ifdef HAVE_IPSET
- if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL))
+ if (daemon->dh_ipsets && extract_request(header, n, daemon->namebuff, NULL))
{
- /* Similar algorithm to search_servers. */
- struct ipsets *ipset_pos;
- unsigned int namelen = strlen(daemon->namebuff);
- unsigned int matchlen = 0;
- for (ipset_pos = daemon->ipsets; ipset_pos; ipset_pos = ipset_pos->next)
- {
- unsigned int domainlen = strlen(ipset_pos->domain);
- char *matchstart = daemon->namebuff + namelen - domainlen;
- if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) &&
- (domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) &&
- domainlen >= matchlen)
- {
- matchlen = domainlen;
- sets = ipset_pos->sets;
- }
- }
+ np = match_domain(daemon->dh_ipsets, daemon->namebuff);
+ if (np != NULL)
+ {
+ obj = (struct ipsets_names *) np->obj;
+ sets = obj->sets;
+ }
}
#endif
@@ -1659,6 +1636,167 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
}
#endif
+/* try establish tcp connection to upstream server and forward query
+ *
+ * return -1 on tcp connect/read/write error
+ * 0 on upstream response has 0 length DNS message, or DNSSEC error
+ * > 0 DNS message length received from upstream server */
+static int tcp_conn_serv(struct server *serv, time_t now,
+ unsigned char *packet, size_t payload_size,
+ union mysockaddr *peer_addr)
+{
+ unsigned char *payload = packet + 2; /* skip msg length field */
+ struct dns_header *header = (struct dns_header *) payload;
+ unsigned int gotname;
+ unsigned short qtype;
+ int checking_disabled = header->hb4 & HB4_CD;
+ unsigned int m = 0;
+ u16 msg_len;
+
+#ifdef HAVE_DNSSEC
+ unsigned char *newhash, hash[HASH_SIZE];
+ if ((newhash = hash_questions (header, (unsigned int) payload_size,
+ daemon->namebuff)))
+ memcpy (hash, newhash, HASH_SIZE);
+ else
+ memset (hash, 0, HASH_SIZE);
+#else
+ unsigned int crc =
+ questions_crc (header, (unsigned int) payload_size, daemon->namebuff);
+#endif
+
+ if (serv->tcpfd == -1)
+ {
+ if ((serv->tcpfd =
+ socket (serv->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
+ return -1;
+
+#ifdef HAVE_CONNTRACK
+ /* Copy connection mark of incoming query to outgoing connection. */
+ if (option_bool (OPT_CONNTRACK))
+ {
+ unsigned int mark;
+ struct all_addr local;
+#ifdef HAVE_IPV6
+ if (local_addr->sa.sa_family == AF_INET6)
+ local.addr.addr6 = local_addr->in6.sin6_addr;
+ else
+#endif
+ local.addr.addr4 = local_addr->in.sin_addr;
+
+ if (get_incoming_mark (&peer_addr, &local, 1, &mark))
+ setsockopt (serv->tcpfd, SOL_SOCKET, SO_MARK, &mark,
+ sizeof (unsigned int));
+ }
+#endif
+
+ if (!local_bind (serv->tcpfd, &serv->source_addr, serv->interface, 1) ||
+ connect (serv->tcpfd, &serv->addr.sa, sa_len (&serv->addr)) == -1)
+ {
+ close (serv->tcpfd);
+ serv->tcpfd = -1;
+ return -1;
+ }
+
+#ifdef HAVE_DNSSEC
+ if (option_bool (OPT_DNSSEC_VALID))
+ {
+ size_t new_size =
+ add_do_bit (header, payload_size, ((char *) header) + 65536);
+
+ /* For debugging, set Checking Disabled, otherwise, have the upstream
+ check too, this allows it to select auth servers when one is
+ returning bad data. */
+ if (option_bool (OPT_DNSSEC_DEBUG))
+ header->hb4 |= HB4_CD;
+
+ if (payload_size != new_size)
+ added_pheader = 1;
+
+ payload_size = new_size;
+ }
+#endif
+ }
+
+ /* get query name again for logging - may have been overwritten */
+ if (!(gotname = extract_request (header, (unsigned int) payload_size,
+ daemon->namebuff, &qtype)))
+ strcpy (daemon->namebuff, "query");
+
+ u16 n_size = htons (payload_size);
+ memcpy (packet, &n_size, sizeof (u16));
+
+ if (!read_write (serv->tcpfd, packet, payload_size + sizeof (u16), 0) ||
+ !read_write (serv->tcpfd, (unsigned char *) &msg_len, 2, 1) ||
+ !(m = (unsigned int) ntohs (msg_len)) ||
+ !read_write (serv->tcpfd, payload, (int) m, 1))
+ {
+ close (serv->tcpfd);
+ serv->tcpfd = -1;
+ return -1;
+ }
+
+ if (serv->addr.sa.sa_family == AF_INET)
+ log_query (F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
+ (struct all_addr *) &serv->addr.in.sin_addr, NULL);
+#ifdef HAVE_IPV6
+ else
+ log_query (F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
+ (struct all_addr *) &serv->addr.in6.sin6_addr, NULL);
+#endif
+
+#ifdef HAVE_DNSSEC
+ if (option_bool (OPT_DNSSEC_VALID) && !checking_disabled)
+ {
+ /* Limit to number of DNSSEC questions, to catch loops and avoid filling
+ * cache. */
+ int keycount = DNSSEC_WORK;
+ int status =
+ tcp_key_recurse (now, STAT_TRUNCATED, header, m, 0, daemon->namebuff,
+ daemon->keyname, serv, &keycount);
+ char *result;
+
+ if (keycount == 0)
+ result = "ABANDONED";
+ else
+ result =
+ (status ==
+ STAT_SECURE ? "SECURE" : (status ==
+ STAT_INSECURE ? "INSECURE" : "BOGUS"));
+
+ log_query (F_KEYTAG | F_SECSTAT, "result", NULL, result);
+
+ if (status == STAT_BOGUS)
+ no_cache_dnssec = 1;
+
+ if (status == STAT_SECURE)
+ cache_secure = 1;
+ }
+#endif
+
+ /* restore CD bit to the value in the query */
+ if (checking_disabled)
+ header->hb4 |= HB4_CD;
+ else
+ header->hb4 &= ~HB4_CD;
+
+ /* There's no point in updating the cache, since this process will exit and
+ lose the information after a few queries. We make this call for the alias and
+ bogus-nxdomain side-effects. */
+ /* If the crc of the question section doesn't match the crc we sent, then
+ someone might be attempting to insert bogus values into the cache by
+ sending replies containing questions and bogus answers. */
+#ifdef HAVE_DNSSEC
+ newhash = hash_questions (header, m, daemon->namebuff);
+ if (!newhash || memcmp (hash, newhash, HASH_SIZE) != 0)
+ m = 0;
+#else
+ if (crc != questions_crc (header, m, daemon->namebuff))
+ m = 0;
+#endif
+
+ return (int) m;
+}
/* The daemon forks before calling this: it should deal with one connection,
blocking as neccessary, and then return. Note, need to be a bit careful
@@ -1684,11 +1822,12 @@ unsigned char *tcp_request(int confd, time_t now,
/* largest field in header is 16-bits, so this is still sufficiently aligned */
struct dns_header *header = (struct dns_header *)payload;
u16 *length = (u16 *)packet;
- struct server *last_server;
+ struct server *last_server, *fwdserv, *serv;
struct in_addr dst_addr_4;
union mysockaddr peer_addr;
socklen_t peer_len = sizeof(union mysockaddr);
int query_count = 0;
+ int ret;
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
return packet;
@@ -1817,182 +1956,66 @@ unsigned char *tcp_request(int confd, time_t now,
}
if (gotname)
- flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
-
- if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
- last_server = daemon->servers;
- else
- last_server = daemon->last_server;
+ flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind, &fwdserv);
- if (!flags && last_server)
- {
- struct server *firstsendto = NULL;
-#ifdef HAVE_DNSSEC
- unsigned char *newhash, hash[HASH_SIZE];
- if ((newhash = hash_questions(header, (unsigned int)size, daemon->namebuff)))
- memcpy(hash, newhash, HASH_SIZE);
- else
- memset(hash, 0, HASH_SIZE);
-#else
- unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
-#endif
- /* Loop round available servers until we succeed in connecting to one.
- Note that this code subtley ensures that consecutive queries on this connection
- which can go to the same server, do so. */
- while (1)
- {
- if (!firstsendto)
- firstsendto = last_server;
- else
- {
- if (!(last_server = last_server->next))
- last_server = daemon->servers;
-
- if (last_server == firstsendto)
- break;
- }
-
- /* server for wrong domain */
- if (type != (last_server->flags & SERV_TYPE) ||
- (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)) ||
- (last_server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
- continue;
-
- if (last_server->tcpfd == -1)
- {
- if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
- continue;
-
-#ifdef HAVE_CONNTRACK
- /* Copy connection mark of incoming query to outgoing connection. */
- if (option_bool(OPT_CONNTRACK))
- {
- unsigned int mark;
- struct all_addr local;
-#ifdef HAVE_IPV6
- if (local_addr->sa.sa_family == AF_INET6)
- local.addr.addr6 = local_addr->in6.sin6_addr;
- else
-#endif
- local.addr.addr4 = local_addr->in.sin_addr;
-
- if (get_incoming_mark(&peer_addr, &local, 1, &mark))
- setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
- }
-#endif
-
- if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
- connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
- {
- close(last_server->tcpfd);
- last_server->tcpfd = -1;
- continue;
- }
-
-#ifdef HAVE_DNSSEC
- if (option_bool(OPT_DNSSEC_VALID))
- {
- size_t new_size = add_do_bit(header, size, ((char *) header) + 65536);
-
- /* For debugging, set Checking Disabled, otherwise, have the upstream check too,
- this allows it to select auth servers when one is returning bad data. */
- if (option_bool(OPT_DNSSEC_DEBUG))
- header->hb4 |= HB4_CD;
-
- if (size != new_size)
- added_pheader = 1;
-
- size = new_size;
- }
-#endif
- }
-
- *length = htons(size);
-
- /* get query name again for logging - may have been overwritten */
- if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
- strcpy(daemon->namebuff, "query");
-
- if (!read_write(last_server->tcpfd, packet, size + sizeof(u16), 0) ||
- !read_write(last_server->tcpfd, &c1, 1, 1) ||
- !read_write(last_server->tcpfd, &c2, 1, 1) ||
- !read_write(last_server->tcpfd, payload, (c1 << 8) | c2, 1))
- {
- close(last_server->tcpfd);
- last_server->tcpfd = -1;
- continue;
- }
-
- m = (c1 << 8) | c2;
-
- if (last_server->addr.sa.sa_family == AF_INET)
- log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&last_server->addr.in.sin_addr, NULL);
-#ifdef HAVE_IPV6
- else
- log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
-#endif
-
-#ifdef HAVE_DNSSEC
- if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled)
- {
- int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
- int status = tcp_key_recurse(now, STAT_TRUNCATED, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount);
- char *result;
+ ret = 0;
+ serv = last_server = NULL;
+ /* --address=/xxx/1.2.3.4 */
+ if (addrp != NULL)
+ {
+ m = 0;
+ }
+ else if (fwdserv != NULL)
+ {
+ /* --server=/example.org/1.2.3.4 */
+ serv = fwdserv;
+ ret = tcp_conn_serv (serv, now, packet, size, &peer_addr);
+ }
+ else
+ {
+ /* use normal server */
+ struct server *firstsendto = NULL;
+ if (option_bool (OPT_ORDER) || !daemon->last_server)
+ last_server = daemon->servers;
+ else
+ last_server = daemon->last_server;
+
+ while (1)
+ {
+ if (!firstsendto)
+ firstsendto = last_server;
+ else
+ {
+ if (!(last_server = last_server->next))
+ last_server = daemon->servers;
+
+ if (last_server == firstsendto)
+ break;
+ }
+
+ if (type != (last_server->flags & SERV_TYPE) ||
+ last_server->flags & SERV_HAS_DOMAIN ||
+ last_server->flags & SERV_LOOP)
+ continue;
+
+ serv = last_server;
+ ret = tcp_conn_serv (serv, now, packet, size, &peer_addr);
+ /* something wrong with tcp connect/read/write */
+ if (ret <= 0)
+ continue;
+
+ break;
+ }
+ }
+
+ if (ret > 0)
+ m = process_reply (header, now, serv, ret,
+ option_bool (OPT_NO_REBIND) && !norebind,
+ no_cache_dnssec, cache_secure, ad_question,
+ do_bit, added_pheader, check_subnet, &peer_addr);
- if (keycount == 0)
- result = "ABANDONED";
- else
- result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
-
- log_query(F_KEYTAG | F_SECSTAT, "result", NULL, result);
-
- if (status == STAT_BOGUS)
- no_cache_dnssec = 1;
-
- if (status == STAT_SECURE)
- cache_secure = 1;
- }
-#endif
-
- /* restore CD bit to the value in the query */
- if (checking_disabled)
- header->hb4 |= HB4_CD;
- else
- header->hb4 &= ~HB4_CD;
-
- /* There's no point in updating the cache, since this process will exit and
- lose the information after a few queries. We make this call for the alias and
- bogus-nxdomain side-effects. */
- /* If the crc of the question section doesn't match the crc we sent, then
- someone might be attempting to insert bogus values into the cache by
- sending replies containing questions and bogus answers. */
-#ifdef HAVE_DNSSEC
- newhash = hash_questions(header, (unsigned int)m, daemon->namebuff);
- if (!newhash || memcmp(hash, newhash, HASH_SIZE) != 0)
- {
- m = 0;
- break;
- }
-#else
- if (crc != questions_crc(header, (unsigned int)m, daemon->namebuff))
- {
- m = 0;
- break;
- }
-#endif
-
- m = process_reply(header, now, last_server, (unsigned int)m,
- option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec,
- cache_secure, ad_question, do_bit, added_pheader, check_subnet, &peer_addr);
-
- break;
- }
- }
-
/* In case of local answer or no connections made. */
- if (m == 0)
+ if (m == 0)
m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
}
}
--
1.7.10.4
More information about the Dnsmasq-discuss
mailing list