diff options
Diffstat (limited to 'src/forward.c')
-rw-r--r-- | src/forward.c | 540 |
1 files changed, 342 insertions, 198 deletions
diff --git a/src/forward.c b/src/forward.c index cdd11d3..9c2b2c6 100644 --- a/src/forward.c +++ b/src/forward.c @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2020 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 @@ -26,7 +26,7 @@ static void free_frec(struct frec *f); /* Send a UDP packet with its source address set as "source" unless nowild is true, when we just send it with the kernel default */ int send_from(int fd, int nowild, char *packet, size_t len, - union mysockaddr *to, struct all_addr *source, + union mysockaddr *to, union all_addr *source, unsigned int iface) { struct msghdr msg; @@ -38,9 +38,7 @@ int send_from(int fd, int nowild, char *packet, size_t len, #elif defined(IP_SENDSRCADDR) char control[CMSG_SPACE(sizeof(struct in_addr))]; #endif -#ifdef HAVE_IPV6 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; -#endif } control_u; iov[0].iov_base = packet; @@ -66,47 +64,49 @@ int send_from(int fd, int nowild, char *packet, size_t len, #if defined(HAVE_LINUX_NETWORK) struct in_pktinfo p; p.ipi_ifindex = 0; - p.ipi_spec_dst = source->addr.addr4; + p.ipi_spec_dst = source->addr4; + msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); memcpy(CMSG_DATA(cmptr), &p, sizeof(p)); - msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); cmptr->cmsg_level = IPPROTO_IP; cmptr->cmsg_type = IP_PKTINFO; #elif defined(IP_SENDSRCADDR) - memcpy(CMSG_DATA(cmptr), &(source->addr.addr4), sizeof(source->addr.addr4)); - msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + msg.msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); + memcpy(CMSG_DATA(cmptr), &(source->addr4), sizeof(source->addr4)); + cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); cmptr->cmsg_level = IPPROTO_IP; cmptr->cmsg_type = IP_SENDSRCADDR; #endif } else -#ifdef HAVE_IPV6 { struct in6_pktinfo p; p.ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */ - p.ipi6_addr = source->addr.addr6; + p.ipi6_addr = source->addr6; + msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); memcpy(CMSG_DATA(cmptr), &p, sizeof(p)); - msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); cmptr->cmsg_type = daemon->v6pktinfo; cmptr->cmsg_level = IPPROTO_IPV6; } -#else - (void)iface; /* eliminate warning */ -#endif } while (retry_send(sendmsg(fd, &msg, 0))); - /* If interface is still in DAD, EINVAL results - ignore that. */ - if (errno != 0 && errno != EINVAL) + if (errno != 0) { - my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno)); +#ifdef HAVE_LINUX_NETWORK + /* If interface is still in DAD, EINVAL results - ignore that. */ + if (errno != EINVAL) + my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno)); +#endif return 0; } return 1; } -static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigned int qtype, +static unsigned int search_servers(time_t now, union all_addr **addrpp, unsigned int qtype, char *qdomain, int *type, char **domain, int *norebind) { @@ -118,6 +118,7 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne unsigned int matchlen = 0; struct server *serv; unsigned int flags = 0; + static union all_addr zero; for (serv = daemon->servers; serv; serv=serv->next) if (qtype == F_DNSSECOK && !(serv->flags & SERV_DO_DNSSEC)) @@ -127,19 +128,26 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne { unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; *type = SERV_FOR_NODOTS; - if (serv->flags & SERV_NO_ADDR) + if ((serv->flags & SERV_NO_REBIND) && norebind) + *norebind = 1; + else if (serv->flags & SERV_NO_ADDR) flags = F_NXDOMAIN; - else if (serv->flags & SERV_LITERAL_ADDRESS) + else if (serv->flags & SERV_LITERAL_ADDRESS) { - if (sflag & qtype) + /* literal address = '#' -> return all-zero address for IPv4 and IPv6 */ + if ((serv->flags & SERV_USE_RESOLV) && (qtype & (F_IPV6 | F_IPV4))) + { + memset(&zero, 0, sizeof(zero)); + flags = qtype; + *addrpp = &zero; + } + else 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 + *addrpp = (union all_addr *)&serv->addr.in.sin_addr; else - *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr; -#endif + *addrpp = (union all_addr *)&serv->addr.in6.sin6_addr; } else if (!flags || (flags & F_NXDOMAIN)) flags = F_NOERR; @@ -184,15 +192,20 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne flags = F_NXDOMAIN; else if (serv->flags & SERV_LITERAL_ADDRESS) { - if (sflag & qtype) + /* literal address = '#' -> return all-zero address for IPv4 and IPv6 */ + if ((serv->flags & SERV_USE_RESOLV) && (qtype & (F_IPV6 | F_IPV4))) + { + memset(&zero, 0, sizeof(zero)); + flags = qtype; + *addrpp = &zero; + } + else 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 + *addrpp = (union all_addr *)&serv->addr.in.sin_addr; else - *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr; -#endif + *addrpp = (union all_addr *)&serv->addr.in6.sin6_addr; } else if (!flags || (flags & F_NXDOMAIN)) flags = F_NOERR; @@ -214,12 +227,16 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne 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); + if (flags == F_NXDOMAIN || flags == F_NOERR) + log_query(flags | qtype | F_NEG | F_CONFIG | F_FORWARD, qdomain, NULL, NULL); + else + { + /* handle F_IPV4 and F_IPV6 set on ANY query to 0.0.0.0/:: domain. */ + if (flags & F_IPV4) + log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, qdomain, *addrpp, NULL); + if (flags & F_IPV6) + log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, qdomain, *addrpp, NULL); + } } else if ((*type) & SERV_USE_RESOLV) { @@ -230,13 +247,13 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne } static int forward_query(int udpfd, union mysockaddr *udpaddr, - struct all_addr *dst_addr, unsigned int dst_iface, + union all_addr *dst_addr, unsigned int dst_iface, struct dns_header *header, size_t plen, time_t now, struct frec *forward, int ad_reqd, int do_bit) { char *domain = NULL; int type = SERV_DO_DNSSEC, norebind = 0; - struct all_addr *addrp = NULL; + union all_addr *addrp = NULL; unsigned int flags = 0; struct server *start = NULL; #ifdef HAVE_DNSSEC @@ -246,9 +263,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, unsigned int crc = questions_crc(header, plen, daemon->namebuff); void *hash = &crc; #endif - unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); - - (void)do_bit; + unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); + unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL); + (void)do_bit; /* may be no servers available. */ if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash)))) @@ -280,27 +297,24 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, PUTSHORT(SAFE_PKTSZ, pheader); if (forward->sentto->addr.sa.sa_family == AF_INET) - log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec"); -#ifdef HAVE_IPV6 + log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (union all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec"); else - log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec"); -#endif + log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (union all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec"); + if (forward->sentto->sfd) fd = forward->sentto->sfd->fd; else { -#ifdef HAVE_IPV6 if (forward->sentto->addr.sa.sa_family == AF_INET6) fd = forward->rfd6->fd; else -#endif fd = forward->rfd4->fd; } - while (retry_send( sendto(fd, (char *)header, plen, 0, - &forward->sentto->addr.sa, - sa_len(&forward->sentto->addr)))); + while (retry_send(sendto(fd, (char *)header, plen, 0, + &forward->sentto->addr.sa, + sa_len(&forward->sentto->addr)))); return 1; } @@ -334,7 +348,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, type &= ~SERV_DO_DNSSEC; if (daemon->servers && !flags) - forward = get_new_frec(now, NULL, 0); + forward = get_new_frec(now, NULL, NULL); /* table full - flags == 0, return REFUSED */ if (forward) @@ -399,7 +413,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, struct server *firstsentto = start; int subnet, forwarded = 0; size_t edns0_len; - unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL); unsigned char *pheader; /* If a query is retried, use the log_id for the retry when logging the answer. */ @@ -455,7 +468,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, fd = start->sfd->fd; else { -#ifdef HAVE_IPV6 if (start->addr.sa.sa_family == AF_INET6) { if (!forward->rfd6 && @@ -465,7 +477,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, fd = forward->rfd6->fd; } else -#endif { if (!forward->rfd4 && !(forward->rfd4 = allocate_rfd(AF_INET))) @@ -508,6 +519,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, if (errno == 0) { +#ifdef HAVE_DUMPFILE + dump_packet(DUMP_UP_QUERY, (void *)header, plen, NULL, &start->addr); +#endif + /* Keep info in case we want to re-send this packet */ daemon->srv_save = start; daemon->packet_len = plen; @@ -516,12 +531,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, 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); -#ifdef HAVE_IPV6 + (union all_addr *)&start->addr.in.sin_addr, NULL); else log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, - (struct all_addr *)&start->addr.in6.sin6_addr, NULL); -#endif + (union all_addr *)&start->addr.in6.sin6_addr, NULL); start->queries++; forwarded = 1; forward->sentto = start; @@ -550,6 +563,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, if (udpfd != -1) { plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl); + if (oph) + plen = add_pseudoheader(header, plen, ((unsigned char *) header) + PACKETSZ, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface); } @@ -563,6 +578,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server unsigned char *pheader, *sizep; char **sets = 0; int munged = 0, is_sign; + unsigned int rcode = RCODE(header); size_t plen; (void)ad_reqd; @@ -593,6 +609,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL))) { + /* Get extended RCODE. */ + rcode |= sizep[2] << 4; + if (check_subnet && !check_source(header, plen, pheader, query_source)) { my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch")); @@ -641,20 +660,29 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server if (!is_sign && !option_bool(OPT_DNSSEC_PROXY)) header->hb4 &= ~HB4_AD; - if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN)) + if (OPCODE(header) != QUERY) return resize_packet(header, n, pheader, plen); + + if (rcode != NOERROR && rcode != NXDOMAIN) + { + union all_addr a; + a.log.rcode = rcode; + log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL); + + return resize_packet(header, n, pheader, plen); + } /* Complain loudly if the upstream server is non-recursive. */ - if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR && + if (!(header->hb4 & HB4_RA) && rcode == NOERROR && server && !(server->flags & SERV_WARNED_RECURSIVE)) { - prettyprint_addr(&server->addr, daemon->namebuff); + (void)prettyprint_addr(&server->addr, daemon->namebuff); my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff); if (!option_bool(OPT_LOG)) server->flags |= SERV_WARNED_RECURSIVE; } - if (daemon->bogus_addr && RCODE(header) != NXDOMAIN && + if (daemon->bogus_addr && rcode != NXDOMAIN && check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now)) { munged = 1; @@ -666,7 +694,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server { int doctored = 0; - if (RCODE(header) == NXDOMAIN && + if (rcode == NXDOMAIN && extract_request(header, n, daemon->namebuff, NULL) && check_for_local_domain(daemon->namebuff, now)) { @@ -749,14 +777,11 @@ void reply_query(int fd, int family, time_t now) daemon->srv_save = NULL; /* Determine the address of the server replying so that we can mark that as good */ - serveraddr.sa.sa_family = family; -#ifdef HAVE_IPV6 - if (serveraddr.sa.sa_family == AF_INET6) + if ((serveraddr.sa.sa_family = family) == AF_INET6) serveraddr.in6.sin6_flowinfo = 0; -#endif header = (struct dns_header *)daemon->packet; - + if (n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR)) return; @@ -783,6 +808,11 @@ void reply_query(int fd, int family, time_t now) if (!(forward = lookup_frec(ntohs(header->id), hash))) return; +#ifdef HAVE_DUMPFILE + dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_REPLY : DUMP_UP_REPLY, + (void *)header, n, &serveraddr, NULL); +#endif + /* log_query gets called indirectly all over the place, so pass these in global variables - sorry. */ daemon->log_display_id = forward->log_id; @@ -794,7 +824,7 @@ void reply_query(int fd, int family, time_t now) /* Note: if we send extra options in the EDNS0 header, we can't recreate the query from the reply. */ - if (RCODE(header) == REFUSED && + if ((RCODE(header) == REFUSED || RCODE(header) == SERVFAIL) && forward->forwardall == 0 && !(forward->flags & FREC_HAS_EXTRADATA)) /* for broken servers, attempt to send to another one. */ @@ -803,6 +833,70 @@ void reply_query(int fd, int family, time_t now) size_t plen; int is_sign; +#ifdef HAVE_DNSSEC + /* For DNSSEC originated queries, just retry the query to the same server. */ + if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) + { + struct server *start; + + blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); + plen = forward->stash_len; + + forward->forwardall = 2; /* only retry once */ + start = forward->sentto; + + /* for non-domain specific servers, see if we can find another to try. */ + if ((forward->sentto->flags & SERV_TYPE) == 0) + while (1) + { + if (!(start = start->next)) + start = daemon->servers; + if (start == forward->sentto) + break; + + if ((start->flags & SERV_TYPE) == 0 && + (start->flags & SERV_DO_DNSSEC)) + break; + } + + + if (start->sfd) + fd = start->sfd->fd; + else + { + if (start->addr.sa.sa_family == AF_INET6) + { + /* may have changed family */ + if (!forward->rfd6) + forward->rfd6 = allocate_rfd(AF_INET6); + fd = forward->rfd6->fd; + } + else + { + /* may have changed family */ + if (!forward->rfd4) + forward->rfd4 = allocate_rfd(AF_INET); + fd = forward->rfd4->fd; + } + } + +#ifdef HAVE_DUMPFILE + dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)plen, NULL, &start->addr); +#endif + + while (retry_send(sendto(fd, (char *)header, plen, 0, + &start->addr.sa, + sa_len(&start->addr)))); + + if (start->addr.sa.sa_family == AF_INET) + log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (union all_addr *)&start->addr.in.sin_addr, "dnssec"); + else + log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (union all_addr *)&start->addr.in6.sin6_addr, "dnssec"); + + return; + } +#endif + /* In strict order mode, there must be a server later in the chain left to send to, otherwise without the forwardall mechanism, code further on will cycle around the list forwever if they @@ -861,12 +955,12 @@ void reply_query(int fd, int family, time_t now) /* We tried resending to this server with a smaller maximum size and got an answer. Make that permanent. To avoid reduxing the packet size for a single dropped packet, only do this when we get a truncated answer, or one larger than the safe size. */ - if (server && server->edns_pktsz > SAFE_PKTSZ && (forward->flags & FREC_TEST_PKTSZ) && + if (forward->sentto->edns_pktsz > SAFE_PKTSZ && (forward->flags & FREC_TEST_PKTSZ) && ((header->hb3 & HB3_TC) || n >= SAFE_PKTSZ)) { - server->edns_pktsz = SAFE_PKTSZ; - server->pktsz_reduced = now; - prettyprint_addr(&server->addr, daemon->addrbuff); + forward->sentto->edns_pktsz = SAFE_PKTSZ; + forward->sentto->pktsz_reduced = now; + (void)prettyprint_addr(&forward->sentto->addr, daemon->addrbuff); my_syslog(LOG_WARNING, _("reducing DNS packet size for nameserver %s to %d"), daemon->addrbuff, SAFE_PKTSZ); } @@ -875,8 +969,7 @@ void reply_query(int fd, int family, time_t now) we get a good reply from another server. Kill it when we've had replies from all to avoid filling the forwarding table when everything is broken */ - if (forward->forwardall == 0 || --forward->forwardall == 1 || - (RCODE(header) != REFUSED && RCODE(header) != SERVFAIL)) + if (forward->forwardall == 0 || --forward->forwardall == 1 || RCODE(header) != REFUSED) { int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; @@ -889,7 +982,7 @@ void reply_query(int fd, int family, time_t now) no_cache_dnssec = 1; #ifdef HAVE_DNSSEC - if (server && (server->flags & SERV_DO_DNSSEC) && + if ((forward->sentto->flags & SERV_DO_DNSSEC) && option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED)) { int status = 0; @@ -919,8 +1012,13 @@ void reply_query(int fd, int family, time_t now) status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); else status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, - option_bool(OPT_DNSSEC_NO_SIGN) && (server->flags & SERV_DO_DNSSEC), - NULL, NULL); + !option_bool(OPT_DNSSEC_IGN_NS) && (forward->sentto->flags & SERV_DO_DNSSEC), + NULL, NULL, NULL); +#ifdef HAVE_DUMPFILE + if (status == STAT_BOGUS) + dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS, + header, (size_t)n, &serveraddr, NULL); +#endif } /* Can't validate, as we're missing key data. Put this @@ -941,11 +1039,13 @@ void reply_query(int fd, int family, time_t now) /* Find the original query that started it all.... */ for (orig = forward; orig->dependent; orig = orig->dependent); - if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, 1))) + /* Make sure we don't expire and free the orig frec during the + allocation of a new one. */ + if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, orig))) status = STAT_ABANDONED; else { - int fd, type = SERV_DO_DNSSEC; + int querytype, fd, type = SERV_DO_DNSSEC; struct frec *next = new->next; char *domain; @@ -958,12 +1058,13 @@ void reply_query(int fd, int family, time_t now) servers for domains are involved. */ if (search_servers(now, NULL, F_DNSSECOK, daemon->keyname, &type, &domain, NULL) == 0) { - struct server *start = server, *new_server = NULL; + struct server *start, *new_server = NULL; + start = server = forward->sentto; while (1) { if (type == (start->flags & (SERV_TYPE | SERV_DO_DNSSEC)) && - (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) && + ((type & SERV_TYPE) != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) && !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP))) { new_server = start; @@ -986,10 +1087,9 @@ void reply_query(int fd, int family, time_t now) new->sentto = server; new->rfd4 = NULL; -#ifdef HAVE_IPV6 new->rfd6 = NULL; -#endif - new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY); + new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA); + new->forwardall = 0; new->dependent = forward; /* to find query awaiting new one. */ forward->blocking_query = new; /* for garbage cleaning */ @@ -997,15 +1097,24 @@ void reply_query(int fd, int family, time_t now) if (status == STAT_NEED_KEY) { new->flags |= FREC_DNSKEY_QUERY; - nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz, - daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz); + querytype = T_DNSKEY; } else { new->flags |= FREC_DS_QUERY; - nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz, - daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz); + querytype = T_DS; } + + nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz, + daemon->keyname, forward->class, querytype, server->edns_pktsz); + + if (server->addr.sa.sa_family == AF_INET) + log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, daemon->keyname, (union all_addr *)&(server->addr.in.sin_addr), + querystr("dnssec-query", querytype)); + else + log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, daemon->keyname, (union all_addr *)&(server->addr.in6.sin6_addr), + querystr("dnssec-query", querytype)); + if ((hash = hash_questions(header, nn, daemon->namebuff))) memcpy(new->hash, hash, HASH_SIZE); new->new_id = get_id(); @@ -1022,14 +1131,12 @@ void reply_query(int fd, int family, time_t now) else { fd = -1; -#ifdef HAVE_IPV6 if (server->addr.sa.sa_family == AF_INET6) { if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6))) fd = new->rfd6->fd; } else -#endif { if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET))) fd = new->rfd4->fd; @@ -1047,6 +1154,11 @@ void reply_query(int fd, int family, time_t now) setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); } #endif + +#ifdef HAVE_DUMPFILE + dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr); +#endif + while (retry_send(sendto(fd, (char *)header, nn, 0, &server->addr.sa, sa_len(&server->addr)))); @@ -1090,7 +1202,7 @@ void reply_query(int fd, int family, time_t now) if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL)) domain = daemon->namebuff; - log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result); + log_query(F_SECSTAT, domain, NULL, result); } if (status == STAT_SECURE) @@ -1101,8 +1213,9 @@ void reply_query(int fd, int family, time_t now) bogusanswer = 1; } } -#endif - + +#endif + /* restore CD bit to the value in the query */ if (forward->flags & FREC_CHECKING_DISABLED) header->hb4 |= HB4_CD; @@ -1128,6 +1241,11 @@ void reply_query(int fd, int family, time_t now) nn = resize_packet(header, nn, NULL, 0); } #endif + +#ifdef HAVE_DUMPFILE + dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &forward->source); +#endif + send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, &forward->source, &forward->dest, forward->iface); } @@ -1142,7 +1260,7 @@ void receive_query(struct listener *listen, time_t now) union mysockaddr source_addr; unsigned char *pheader; unsigned short type, udp_size = PACKETSZ; /* default if no EDNS0 */ - struct all_addr dst_addr; + union all_addr dst_addr; struct in_addr netmask, dst_addr_4; size_t m; ssize_t n; @@ -1155,9 +1273,7 @@ void receive_query(struct listener *listen, time_t now) struct cmsghdr *cmptr; union { struct cmsghdr align; /* this ensures alignment */ -#ifdef HAVE_IPV6 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; -#endif #if defined(HAVE_LINUX_NETWORK) char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; #elif defined(IP_RECVDSTADDR) && defined(HAVE_SOLARIS_NETWORK) @@ -1168,26 +1284,23 @@ void receive_query(struct listener *listen, time_t now) CMSG_SPACE(sizeof(struct sockaddr_dl))]; #endif } control_u; -#ifdef HAVE_IPV6 + int family = listen->addr.sa.sa_family; /* Can always get recvd interface for IPv6 */ - int check_dst = !option_bool(OPT_NOWILD) || listen->family == AF_INET6; -#else - int check_dst = !option_bool(OPT_NOWILD); -#endif + int check_dst = !option_bool(OPT_NOWILD) || family == AF_INET6; /* packet buffer overwritten */ daemon->srv_save = NULL; - dst_addr_4.s_addr = dst_addr.addr.addr4.s_addr = 0; + dst_addr_4.s_addr = dst_addr.addr4.s_addr = 0; netmask.s_addr = 0; if (option_bool(OPT_NOWILD) && listen->iface) { auth_dns = listen->iface->dns_auth; - if (listen->family == AF_INET) + if (family == AF_INET) { - dst_addr_4 = dst_addr.addr.addr4 = listen->iface->addr.in.sin_addr; + dst_addr_4 = dst_addr.addr4 = listen->iface->addr.in.sin_addr; netmask = listen->iface->netmask; } } @@ -1215,16 +1328,15 @@ void receive_query(struct listener *listen, time_t now) information disclosure. */ memset(daemon->packet + n, 0, daemon->edns_pktsz - n); - source_addr.sa.sa_family = listen->family; + source_addr.sa.sa_family = family; - if (listen->family == AF_INET) + if (family == AF_INET) { /* Source-port == 0 is an error, we can't send back to that. http://www.ietf.org/mail-archive/web/dnsop/current/msg11441.html */ if (source_addr.in.sin_port == 0) return; } -#ifdef HAVE_IPV6 else { /* Source-port == 0 is an error, we can't send back to that. */ @@ -1232,29 +1344,27 @@ void receive_query(struct listener *listen, time_t now) return; source_addr.in6.sin6_flowinfo = 0; } -#endif /* We can be configured to only accept queries from at-most-one-hop-away addresses. */ if (option_bool(OPT_LOCAL_SERVICE)) { struct addrlist *addr; -#ifdef HAVE_IPV6 - if (listen->family == AF_INET6) + + if (family == AF_INET6) { for (addr = daemon->interface_addrs; addr; addr = addr->next) if ((addr->flags & ADDRLIST_IPV6) && - is_same_net6(&addr->addr.addr.addr6, &source_addr.in6.sin6_addr, addr->prefixlen)) + is_same_net6(&addr->addr.addr6, &source_addr.in6.sin6_addr, addr->prefixlen)) break; } else -#endif { struct in_addr netmask; for (addr = daemon->interface_addrs; addr; addr = addr->next) { netmask.s_addr = htonl(~(in_addr_t)0 << (32 - addr->prefixlen)); if (!(addr->flags & ADDRLIST_IPV6) && - is_same_net(addr->addr.addr.addr4, source_addr.in.sin_addr, netmask)) + is_same_net(addr->addr.addr4, source_addr.in.sin_addr, netmask)) break; } } @@ -1278,7 +1388,7 @@ void receive_query(struct listener *listen, time_t now) return; #if defined(HAVE_LINUX_NETWORK) - if (listen->family == AF_INET) + if (family == AF_INET) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO) { @@ -1287,11 +1397,11 @@ void receive_query(struct listener *listen, time_t now) struct in_pktinfo *p; } p; p.c = CMSG_DATA(cmptr); - dst_addr_4 = dst_addr.addr.addr4 = p.p->ipi_spec_dst; + dst_addr_4 = dst_addr.addr4 = p.p->ipi_spec_dst; if_index = p.p->ipi_ifindex; } #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) - if (listen->family == AF_INET) + if (family == AF_INET) { for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) { @@ -1305,7 +1415,7 @@ void receive_query(struct listener *listen, time_t now) } p; p.c = CMSG_DATA(cmptr); if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) - dst_addr_4 = dst_addr.addr.addr4 = *(p.a); + dst_addr_4 = dst_addr.addr4 = *(p.a); else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) #ifdef HAVE_SOLARIS_NETWORK if_index = *(p.i); @@ -1316,8 +1426,7 @@ void receive_query(struct listener *listen, time_t now) } #endif -#ifdef HAVE_IPV6 - if (listen->family == AF_INET6) + if (family == AF_INET6) { for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo) @@ -1328,27 +1437,26 @@ void receive_query(struct listener *listen, time_t now) } p; p.c = CMSG_DATA(cmptr); - dst_addr.addr.addr6 = p.p->ipi6_addr; + dst_addr.addr6 = p.p->ipi6_addr; if_index = p.p->ipi6_ifindex; } } -#endif /* enforce available interface configuration */ if (!indextoname(listen->fd, if_index, ifr.ifr_name)) return; - if (!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns)) + if (!iface_check(family, &dst_addr, ifr.ifr_name, &auth_dns)) { if (!option_bool(OPT_CLEVERBIND)) enumerate_interfaces(0); - if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name) && - !label_exception(if_index, listen->family, &dst_addr)) + if (!loopback_exception(listen->fd, family, &dst_addr, ifr.ifr_name) && + !label_exception(if_index, family, &dst_addr)) return; } - if (listen->family == AF_INET && option_bool(OPT_LOCALISE)) + if (family == AF_INET && option_bool(OPT_LOCALISE)) { struct irec *iface; @@ -1381,7 +1489,11 @@ void receive_query(struct listener *listen, time_t now) pass these in global variables - sorry. */ daemon->log_display_id = ++daemon->log_id; daemon->log_source_addr = &source_addr; - + +#ifdef HAVE_DUMPFILE + dump_packet(DUMP_QUERY, daemon->packet, (size_t)n, &source_addr, NULL); +#endif + if (extract_request(header, (size_t)n, daemon->namebuff, &type)) { #ifdef HAVE_AUTH @@ -1389,14 +1501,12 @@ void receive_query(struct listener *listen, time_t now) #endif char *types = querystr(auth_dns ? "auth" : "query", type); - if (listen->family == AF_INET) + if (family == AF_INET) log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, - (struct all_addr *)&source_addr.in.sin_addr, types); -#ifdef HAVE_IPV6 + (union all_addr *)&source_addr.in.sin_addr, types); else log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, - (struct all_addr *)&source_addr.in6.sin6_addr, types); -#endif + (union all_addr *)&source_addr.in6.sin6_addr, types); #ifdef HAVE_AUTH /* find queries for zones we're authoritative for, and answer them directly */ @@ -1447,7 +1557,7 @@ void receive_query(struct listener *listen, time_t now) { send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, m, &source_addr, &dst_addr, if_index); - daemon->auth_answer++; + daemon->metrics[METRIC_DNS_AUTH_ANSWERED]++; } } else @@ -1465,13 +1575,13 @@ void receive_query(struct listener *listen, time_t now) { send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, m, &source_addr, &dst_addr, if_index); - daemon->local_answer++; + daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++; } else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index, header, (size_t)n, now, NULL, ad_reqd, do_bit)) - daemon->queries_forwarded++; + daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++; else - daemon->local_answer++; + daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++; } } @@ -1504,8 +1614,8 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si new_status = dnssec_validate_ds(now, header, n, name, keyname, class); else new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, - option_bool(OPT_DNSSEC_NO_SIGN) && (server->flags & SERV_DO_DNSSEC), - NULL, NULL); + !option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC), + NULL, NULL, NULL); if (new_status != STAT_NEED_DS && new_status != STAT_NEED_KEY) break; @@ -1525,9 +1635,9 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si new_status = STAT_ABANDONED; break; } - + m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, - new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz); + new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, server->edns_pktsz); *length = htons(m); @@ -1542,6 +1652,8 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si while (1) { + int data_sent = 0; + if (!firstsendto) firstsendto = server; else @@ -1560,32 +1672,46 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, server->domain)) || (server->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP))) continue; - - retry: - /* may need to make new connection. */ - if (server->tcpfd == -1) - { - if ((server->tcpfd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1) - continue; /* No good, next server */ - + + retry: + /* may need to make new connection. */ + if (server->tcpfd == -1) + { + if ((server->tcpfd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1) + continue; /* No good, next server */ + #ifdef HAVE_CONNTRACK - /* Copy connection mark of incoming query to outgoing connection. */ - if (have_mark) - setsockopt(server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); + /* Copy connection mark of incoming query to outgoing connection. */ + if (have_mark) + setsockopt(server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); #endif - - if (!local_bind(server->tcpfd, &server->source_addr, server->interface, 0, 1) || - connect(server->tcpfd, &server->addr.sa, sa_len(&server->addr)) == -1) - { - close(server->tcpfd); - server->tcpfd = -1; - continue; /* No good, next server */ - } - - server->flags &= ~SERV_GOT_TCP; - } + + if (!local_bind(server->tcpfd, &server->source_addr, server->interface, 0, 1)) + { + close(server->tcpfd); + server->tcpfd = -1; + continue; /* No good, next server */ + } + +#ifdef MSG_FASTOPEN + while(retry_send(sendto(server->tcpfd, packet, m + sizeof(u16), + MSG_FASTOPEN, &server->addr.sa, sa_len(&server->addr)))); + + if (errno == 0) + data_sent = 1; +#endif + + if (!data_sent && connect(server->tcpfd, &server->addr.sa, sa_len(&server->addr)) == -1) + { + close(server->tcpfd); + server->tcpfd = -1; + continue; /* No good, next server */ + } + + server->flags &= ~SERV_GOT_TCP; + } - if (!read_write(server->tcpfd, packet, m + sizeof(u16), 0) || + if ((!data_sent && !read_write(server->tcpfd, packet, m + sizeof(u16), 0)) || !read_write(server->tcpfd, &c1, 1, 1) || !read_write(server->tcpfd, &c2, 1, 1) || !read_write(server->tcpfd, payload, (c1 << 8) | c2, 1)) @@ -1600,6 +1726,14 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si else continue; } + + + if (server->addr.sa.sa_family == AF_INET) + log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, keyname, (union all_addr *)&(server->addr.in.sin_addr), + querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS)); + else + log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, keyname, (union all_addr *)&(server->addr.in6.sin6_addr), + querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS)); server->flags |= SERV_GOT_TCP; @@ -1663,13 +1797,12 @@ unsigned char *tcp_request(int confd, time_t now, /* Get connection mark of incoming query to set on outgoing connections. */ if (option_bool(OPT_CONNTRACK)) { - struct all_addr local; -#ifdef HAVE_IPV6 + union all_addr local; + if (local_addr->sa.sa_family == AF_INET6) - local.addr.addr6 = local_addr->in6.sin6_addr; + local.addr6 = local_addr->in6.sin6_addr; else -#endif - local.addr.addr4 = local_addr->in.sin_addr; + local.addr4 = local_addr->in.sin_addr; have_mark = get_incoming_mark(&peer_addr, &local, 1, &mark); } @@ -1679,23 +1812,22 @@ unsigned char *tcp_request(int confd, time_t now, if (option_bool(OPT_LOCAL_SERVICE)) { struct addrlist *addr; -#ifdef HAVE_IPV6 + if (peer_addr.sa.sa_family == AF_INET6) { for (addr = daemon->interface_addrs; addr; addr = addr->next) if ((addr->flags & ADDRLIST_IPV6) && - is_same_net6(&addr->addr.addr.addr6, &peer_addr.in6.sin6_addr, addr->prefixlen)) + is_same_net6(&addr->addr.addr6, &peer_addr.in6.sin6_addr, addr->prefixlen)) break; } else -#endif { struct in_addr netmask; for (addr = daemon->interface_addrs; addr; addr = addr->next) { netmask.s_addr = htonl(~(in_addr_t)0 << (32 - addr->prefixlen)); if (!(addr->flags & ADDRLIST_IPV6) && - is_same_net(addr->addr.addr.addr4, peer_addr.in.sin_addr, netmask)) + is_same_net(addr->addr.addr4, peer_addr.in.sin_addr, netmask)) break; } } @@ -1742,12 +1874,10 @@ unsigned char *tcp_request(int confd, time_t now, if (peer_addr.sa.sa_family == AF_INET) log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, - (struct all_addr *)&peer_addr.in.sin_addr, types); -#ifdef HAVE_IPV6 + (union all_addr *)&peer_addr.in.sin_addr, types); else log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, - (struct all_addr *)&peer_addr.in6.sin6_addr, types); -#endif + (union all_addr *)&peer_addr.in6.sin6_addr, types); #ifdef HAVE_AUTH /* find queries for zones we're authoritative for, and answer them directly */ @@ -1803,7 +1933,7 @@ unsigned char *tcp_request(int confd, time_t now, if (m == 0) { unsigned int flags = 0; - struct all_addr *addrp = NULL; + union all_addr *addrp = NULL; int type = SERV_DO_DNSSEC; char *domain = NULL; unsigned char *oph = find_pseudoheader(header, size, NULL, NULL, NULL, NULL); @@ -1854,6 +1984,8 @@ unsigned char *tcp_request(int confd, time_t now, which can go to the same server, do so. */ while (1) { + int data_sent = 0; + if (!firstsendto) firstsendto = last_server; else @@ -1872,6 +2004,8 @@ unsigned char *tcp_request(int confd, time_t now, continue; retry: + *length = htons(size); + if (last_server->tcpfd == -1) { if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1) @@ -1881,10 +2015,24 @@ unsigned char *tcp_request(int confd, time_t now, /* Copy connection mark of incoming query to outgoing connection. */ if (have_mark) setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); -#endif +#endif - if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 0, 1) || - connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1)) + if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 0, 1))) + { + close(last_server->tcpfd); + last_server->tcpfd = -1; + continue; + } + +#ifdef MSG_FASTOPEN + while(retry_send(sendto(last_server->tcpfd, packet, size + sizeof(u16), + MSG_FASTOPEN, &last_server->addr.sa, sa_len(&last_server->addr)))); + + if (errno == 0) + data_sent = 1; +#endif + + if (!data_sent && connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1) { close(last_server->tcpfd); last_server->tcpfd = -1; @@ -1894,13 +2042,11 @@ unsigned char *tcp_request(int confd, time_t now, last_server->flags &= ~SERV_GOT_TCP; } - *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) || + if ((!data_sent && !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)) @@ -1922,12 +2068,10 @@ unsigned char *tcp_request(int confd, time_t now, 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 + (union all_addr *)&last_server->addr.in.sin_addr, NULL); else log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, - (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL); -#endif + (union all_addr *)&last_server->addr.in6.sin6_addr, NULL); #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (last_server->flags & SERV_DO_DNSSEC)) @@ -1948,7 +2092,7 @@ unsigned char *tcp_request(int confd, time_t now, if (status == STAT_BOGUS && extract_request(header, m, daemon->namebuff, NULL)) domain = daemon->namebuff; - log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result); + log_query(F_SECSTAT, domain, NULL, result); if (status == STAT_BOGUS) { @@ -1998,7 +2142,11 @@ unsigned char *tcp_request(int confd, time_t now, /* In case of local answer or no connections made. */ if (m == 0) - m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl); + { + m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl); + if (have_pseudoheader) + m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); + } } } @@ -2022,9 +2170,7 @@ static struct frec *allocate_frec(time_t now) f->sentto = NULL; f->rfd4 = NULL; f->flags = 0; -#ifdef HAVE_IPV6 f->rfd6 = NULL; -#endif #ifdef HAVE_DNSSEC f->dependent = NULL; f->blocking_query = NULL; @@ -2084,11 +2230,8 @@ static void free_frec(struct frec *f) f->rfd4 = NULL; f->sentto = NULL; f->flags = 0; - -#ifdef HAVE_IPV6 free_rfd(f->rfd6); f->rfd6 = NULL; -#endif #ifdef HAVE_DNSSEC if (f->stash) @@ -2111,9 +2254,10 @@ static void free_frec(struct frec *f) else return *wait zero if one available, or *wait is delay to when the oldest in-use record will expire. Impose an absolute limit of 4*TIMEOUT before we wipe things (for random sockets). - If force is set, always return a result, even if we have - to allocate above the limit. */ -struct frec *get_new_frec(time_t now, int *wait, int force) + If force is non-NULL, always return a result, even if we have + to allocate above the limit, and never free the record pointed + to by the force argument. */ +struct frec *get_new_frec(time_t now, int *wait, struct frec *force) { struct frec *f, *oldest, *target; int count; @@ -2130,7 +2274,7 @@ struct frec *get_new_frec(time_t now, int *wait, int force) /* Don't free DNSSEC sub-queries here, as we may end up with dangling references to them. They'll go when their "real" query is freed. */ - if (!f->dependent) + if (!f->dependent && f != force) #endif { if (difftime(now, f->time) >= 4*TIMEOUT) |