From 729c16a8ace49d472bc29cc37f87ca39ade920b6 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 26 Jan 2026 15:25:22 +0000 Subject: [PATCH] Rationalise DNS TCP buffer use. This fixes the plethora of 64k buffers that got allocated when doing DNSSEC over TCP. By using the UDP buffer is pass the query into tcp_talk() and allowing tcp_talk to allocate its output buffer one the size of the reply is known, we only need to allocate as much memory as is required. The final reply to the TCP query still needs the 64k buffer because answer_request() and answer_auth() are not capable of extending their output buffers. --- src/dnsmasq.c | 8 +- src/dnsmasq.h | 4 +- src/forward.c | 265 +++++++++++++++++++++++++------------------------- 3 files changed, 137 insertions(+), 140 deletions(-) diff --git a/src/dnsmasq.c b/src/dnsmasq.c index f6daae6..7a6da56 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -1980,11 +1980,11 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot) pid_t p; union mysockaddr tcp_addr; socklen_t tcp_len = sizeof(union mysockaddr); - unsigned char *buff; struct server *s; int flags, auth_dns = 0; struct in_addr netmask; int pipefd[2]; + struct iovec tcpbuff; #ifdef HAVE_LINUX_NETWORK unsigned char a = 0; #endif @@ -2159,10 +2159,8 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot) if ((flags = fcntl(confd, F_GETFL, 0)) != -1) while(retry_send(fcntl(confd, F_SETFL, flags & ~O_NONBLOCK))); - buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns); - - if (buff) - free(buff); + tcp_request(confd, now, &tcpbuff, &tcp_addr, netmask, auth_dns); + free(tcpbuff.iov_base); for (s = daemon->servers; s; s = s->next) if (s->tcpfd != -1) diff --git a/src/dnsmasq.h b/src/dnsmasq.h index b9062e3..0a5064c 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1569,8 +1569,8 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *n, int class, char *name, struct server *server, int *keycount, int *validatecount); #endif -unsigned char *tcp_request(int confd, time_t now, - union mysockaddr *local_addr, struct in_addr netmask, int auth_dns); +void tcp_request(int confd, time_t now, struct iovec *bigbuff, + union mysockaddr *local_addr, struct in_addr netmask, int auth_dns); void server_gone(struct server *server); int send_from(int fd, int nowild, char *packet, size_t len, union mysockaddr *to, union all_addr *source, diff --git a/src/forward.c b/src/forward.c index 11d61b7..65bd080 100644 --- a/src/forward.c +++ b/src/forward.c @@ -135,9 +135,9 @@ static void log_query_mysockaddr(unsigned int flags, char *name, union mysockadd } static void server_send(struct server *server, int fd, - const void *header, size_t plen, int flags) + const void *header, size_t plen) { - while (retry_send(sendto(fd, header, plen, flags, + while (retry_send(sendto(fd, header, plen, 0, &server->addr.sa, sa_len(&server->addr)))); } @@ -1074,7 +1074,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header, set_outgoing_mark(orig, fd); #endif - server_send(server, fd, header, nn, 0); + server_send(server, fd, header, nn); server->queries++; #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr, fd); @@ -2018,20 +2018,21 @@ void receive_query(struct listener *listen, time_t now) } -/* Send query in packet, qsize to a server determined by first,last,start and - get the reply. return reply size. */ -static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, size_t qsize, - int have_mark, unsigned int mark, struct server **servp) +/* Send query in header, qsize to a server determined by first,last,start and + get the reply into the buffer passed by outbuff. Return reply size. */ +static ssize_t tcp_talk(int first, int last, int start, struct dns_header *header, size_t qsize, + struct iovec *recvbuff, int have_mark, unsigned int mark, struct server **servp) { int firstsendto = -1; - u16 *length = (u16 *)packet; - unsigned char *payload = &packet[2]; - struct dns_header *header = (struct dns_header *)payload; - unsigned int rsize; + u16 length; + unsigned int rsize = 0; int class, rclass, type, rtype; unsigned char *p; - struct blockdata *saved_question; struct timeval tv; +#ifdef MSG_FASTOPEN + struct msghdr msg; + struct iovec sendio[2]; +#endif (void)mark; (void)have_mark; @@ -2043,10 +2044,6 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, GETSHORT(type, p); GETSHORT(class, p); - /* Save question for retry. */ - if (!(saved_question = blockdata_alloc((char *)header, (size_t)qsize))) - return 0; - while (1) { int data_sent = 0, fatal = 0; @@ -2068,9 +2065,7 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, *servp = serv = daemon->serverarray[start]; retry: - blockdata_retrieve(saved_question, qsize, header); - - *length = htons(qsize); + length = htons(qsize); if (serv->tcpfd == -1) { @@ -2104,8 +2099,20 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, #endif #ifdef MSG_FASTOPEN - server_send(serv, serv->tcpfd, packet, qsize + sizeof(u16), MSG_FASTOPEN); - + sendio[0].iov_base = (unsigned char *)&length; + sendio[0].iov_len = sizeof(length); + sendio[1].iov_base = (unsigned char *)header; + sendio[1].iov_len = qsize; + msg.msg_name = &serv->addr.sa; + msg.msg_namelen = sa_len(&serv->addr); + msg.msg_iov = sendio; + msg.msg_iovlen = 2; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + while (retry_send(sendmsg(serv->tcpfd, &msg, MSG_FASTOPEN))); + if (errno == 0) data_sent = 1; else if (errno == ETIMEDOUT || errno == EHOSTUNREACH || errno == EINPROGRESS || errno == ECONNREFUSED) @@ -2131,12 +2138,15 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, serv->flags &= ~SERV_GOT_TCP; } - /* We us the _ONCE veriant of read_write() here because we've set a timeout on the tcp socket + /* We us the _ONCE variant of read_write() here because we've set a timeout on the tcp socket and wish to abort if the whole data is not read/written within the timeout. */ - if ((!data_sent && !read_write(serv->tcpfd, (unsigned char *)packet, qsize + sizeof(u16), RW_WRITE_ONCE)) || - !read_write(serv->tcpfd, (unsigned char *)length, sizeof (*length), RW_READ_ONCE) || - !read_write(serv->tcpfd, payload, (rsize = ntohs(*length)), RW_READ_ONCE)) - { + if ((!data_sent && + (!read_write(serv->tcpfd, (unsigned char *)&length, sizeof(length), RW_WRITE_ONCE) || + !read_write(serv->tcpfd, (unsigned char *)header, qsize, RW_WRITE_ONCE))) || + !read_write(serv->tcpfd, (unsigned char *)&length, sizeof(length), RW_READ_ONCE) || + !expand_buf(recvbuff, (rsize = ntohs(length))) || + !read_write(serv->tcpfd, recvbuff->iov_base, rsize, RW_READ_ONCE)) + { /* We get data then EOF, reopen connection to same server, else try next. This avoids DoS from a server which accepts connections and then closes them. */ @@ -2149,28 +2159,28 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, else goto failed; } + else + { + /* If the question section of the reply doesn't match the question we sent, then + someone might be attempting to insert bogus values into the cache by + sending replies containing questions and bogus answers. + Try another server, or give up */ + p = (unsigned char *)(((struct dns_header *)recvbuff->iov_base)+1); + if (extract_name(((struct dns_header *)recvbuff->iov_base), rsize, &p, daemon->namebuff, EXTR_NAME_COMPARE, 4) != 1) + continue; + GETSHORT(rtype, p); + GETSHORT(rclass, p); - /* If the question section of the reply doesn't match the question we sent, then - someone might be attempting to insert bogus values into the cache by - sending replies containing questions and bogus answers. - Try another server, or give up */ - p = (unsigned char *)(header+1); - if (extract_name(header, rsize, &p, daemon->namebuff, EXTR_NAME_COMPARE, 4) != 1) - continue; - GETSHORT(rtype, p); - GETSHORT(rclass, p); - - if (type != rtype || class != rclass) - continue; + if (type != rtype || class != rclass) + continue; + } serv->flags |= SERV_GOT_TCP; *servp = serv; - blockdata_free(saved_question); return rsize; } - blockdata_free(saved_question); return 0; } @@ -2182,19 +2192,16 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple int class, char *name, struct server *server, int *keycount, int *validatecount) { - unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16)); - struct dns_header *new_header = (struct dns_header *)&packet[2]; int start, first, last, new_status; ssize_t n = *plenp; int log_save = daemon->log_display_id; + struct iovec recvbuff; + + recvbuff.iov_len = 0; + recvbuff.iov_base = NULL; *plenp = 0; - if (!packet) - return STAT_ABANDONED; - - memcpy(new_header, header, n); - /* Set TCP flag in logs. */ daemon->log_display_id = -daemon->log_display_id; @@ -2212,10 +2219,11 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, name, &server->addr, STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0); - if ((n = tcp_talk(first, last, start, packet, n, 0, 0, &server)) == 0) + if ((n = tcp_talk(first, last, start, header, n, &recvbuff, 0, 0, &server)) == 0) new_status = STAT_ABANDONED; else { + struct dns_header *new_header = (struct dns_header *)recvbuff.iov_base; new_status = tcp_key_recurse(now, status, new_header, n, class, daemon->namebuff, daemon->keyname, server, 0, 0, keycount, validatecount); if (STAT_ISEQUAL(status, STAT_OK)) @@ -2252,7 +2260,7 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple } daemon->log_display_id = log_save; - free(packet); + free(recvbuff.iov_base); return new_status; } @@ -2262,9 +2270,12 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si int have_mark, unsigned int mark, int *keycount, int *validatecount) { int first, last, start, new_status; - unsigned char *packet = NULL; - struct dns_header *new_header = NULL; + struct iovec new_packet; + struct dns_header *query_header = NULL, *new_header; + new_packet.iov_base = NULL; + new_packet.iov_len = 0; + while (1) { size_t m; @@ -2298,34 +2309,21 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si } /* Can't validate because we need a key/DS whose name now in keyname. - Make query for same, and recurse to validate */ - if (!packet) - { - packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16)); - new_header = (struct dns_header *)&packet[2]; - } - - if (!packet) - { - new_status = STAT_ABANDONED; - break; - } - - m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, 0, + Make query for same in UDP packet buffer, recurse to validate*/ + query_header = (struct dns_header *)daemon->packet; + daemon->srv_save = NULL; + + m = dnssec_generate_query(query_header, ((unsigned char *)query_header) + daemon->edns_pktsz, keyname, class, 0, STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS); - if ((start = dnssec_server(server, keyname, STAT_ISEQUAL(new_status, STAT_NEED_DS), &first, &last)) == -1) + if ((start = dnssec_server(server, keyname, STAT_ISEQUAL(new_status, STAT_NEED_DS), &first, &last)) == -1 || + (m = tcp_talk(first, last, start, query_header, m, &new_packet, have_mark, mark, &server)) == 0) { new_status = STAT_ABANDONED; break; } - if ((m = tcp_talk(first, last, start, packet, m, have_mark, mark, &server)) == 0) - { - new_status = STAT_ABANDONED; - break; - } - + new_header = new_packet.iov_base; log_save = daemon->log_display_id; daemon->log_display_id = -(++daemon->log_id); @@ -2342,8 +2340,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si break; } - if (packet) - free(packet); + free(new_packet.iov_base); return new_status; } @@ -2353,11 +2350,11 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si /* The daemon forks before calling this: it should deal with one connection, blocking as necessary, and then return. Note, need to be a bit careful about resources for debug mode, when the fork is suppressed: that's - done by the caller. */ -unsigned char *tcp_request(int confd, time_t now, - union mysockaddr *local_addr, struct in_addr netmask, int auth_dns) + done by the caller, which also frees bigbuff. */ +void tcp_request(int confd, time_t now, struct iovec *bigbuff, + union mysockaddr *local_addr, struct in_addr netmask, int auth_dns) { - size_t size = 0, saved_size = 0; + size_t size = 0; int norebind = 0; #ifdef HAVE_CONNTRACK int allowed = 1; @@ -2366,16 +2363,10 @@ unsigned char *tcp_request(int confd, time_t now, int local_auth = 0; #endif int checking_disabled, do_bit = 0, ad_reqd = 0, have_pseudoheader = 0; - struct blockdata *saved_question = NULL; unsigned short qtype; unsigned int gotname = 0; - /* Max TCP packet + slop + size */ - unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16)); - unsigned char *payload = &packet[2]; - u16 tcp_len; - /* 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; + u16 tcp_len, out_len; + struct dns_header *header, *out_header; struct server *serv; struct in_addr dst_addr_4; union mysockaddr peer_addr; @@ -2385,9 +2376,12 @@ unsigned char *tcp_request(int confd, time_t now, unsigned int mark = 0; int have_mark = 0; int first, last, filtered, do_stale = 0; - - if (!packet || getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1) - return packet; + + bigbuff->iov_base = NULL; + bigbuff->iov_len = 0; + + if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1) + return; #ifdef HAVE_CONNTRACK /* Get connection mark of incoming query to set on outgoing connections. */ @@ -2408,7 +2402,7 @@ unsigned char *tcp_request(int confd, time_t now, if (option_bool(OPT_LOCAL_SERVICE)) { struct addrlist *addr; - + if (peer_addr.sa.sa_family == AF_INET6) { for (addr = daemon->interface_addrs; addr; addr = addr->next) @@ -2431,7 +2425,7 @@ unsigned char *tcp_request(int confd, time_t now, { prettyprint_addr(&peer_addr, daemon->addrbuff); my_syslog(LOG_WARNING, _("ignoring query from non-local network %s"), daemon->addrbuff); - return packet; + return; } } @@ -2449,18 +2443,26 @@ unsigned char *tcp_request(int confd, time_t now, if (query_count >= TCP_MAX_QUERIES) break; + /* Now get the query into the normal UDP packet buffer. + Ignore queries long than this. If we're answering locally, + copy the query into the output buffer, but for forwarding, tcp_talk() + wants the query in a a different buffer from the reply. + Note that we overwrote any saved UDP query - this onlt matters in debug mode. */ + daemon->srv_save = NULL; if (!read_write(confd, (unsigned char *)&tcp_len, sizeof(tcp_len), RW_READ) || - !(size = ntohs(tcp_len)) || - !read_write(confd, payload, size, RW_READ)) + !(size = ntohs(tcp_len)) || size > (size_t)daemon->packet_buff_sz || + !read_write(confd, (unsigned char *)daemon->packet, size, RW_READ)) break; - + if (size < (int)sizeof(struct dns_header)) continue; - /* Clear buffer beyond request to avoid risk of - information disclosure. */ - memset(payload + size, 0, 65536 - size); + /* Make sure we have a buffer big enough for the largest answer. */ + expand_buf(bigbuff, 65536 + MAXDNAME + RRFIXEDSZ); + out_header = bigbuff->iov_base; + /* header == query */ + header = (struct dns_header *)daemon->packet; query_count++; /* log_query gets called indirectly all over the place, so @@ -2479,9 +2481,6 @@ unsigned char *tcp_request(int confd, time_t now, ede = EDE_INVALID_DATA; else { - if (saved_question) - blockdata_free(saved_question); - do_bit = 0; if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL)) @@ -2496,9 +2495,12 @@ unsigned char *tcp_request(int confd, time_t now, do_bit = 1; /* do bit */ } - size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &cacheable); - saved_question = blockdata_alloc((char *)header, (size_t)size); - saved_size = size; + size = add_edns0_config(header, size, ((unsigned char *) header) + daemon->edns_pktsz, &peer_addr, now, &cacheable); + + /* Clear buffer to avoid risk of information disclosure. */ + memset(bigbuff->iov_base, 0, bigbuff->iov_len); + /* Copy query into output buffer for local answering */ + memcpy(out_header, header, size); log_query_mysockaddr((auth_dns ? F_NOERR | F_AUTH : 0) | F_QUERY | F_FORWARD, daemon->namebuff, &peer_addr, NULL, qtype); @@ -2557,10 +2559,10 @@ unsigned char *tcp_request(int confd, time_t now, #endif #ifdef HAVE_AUTH else if (auth_dns) - m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr, local_auth); + m = answer_auth(out_header, ((char *) out_header) + 65536, (size_t)size, now, &peer_addr, local_auth); #endif else - m = answer_request(header, ((char *) header) + 65536, (size_t)size, + m = answer_request(out_header, ((char *) out_header) + 65536, (size_t)size, dst_addr_4, netmask, now, ad_reqd, do_bit, !cacheable, &stale, &filtered); } } @@ -2568,15 +2570,12 @@ unsigned char *tcp_request(int confd, time_t now, /* Do this by steam now we're not in the select() loop */ check_log_writer(1); - if (m == 0 && ede == EDE_UNSET && saved_question) + if (m == 0 && ede == EDE_UNSET) { struct server *master; int start; int no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; - blockdata_retrieve(saved_question, (size_t)saved_size, header); - size = saved_size; - /* save state of "cd" flag in query */ checking_disabled = header->hb4 & HB4_CD; @@ -2605,7 +2604,7 @@ unsigned char *tcp_request(int confd, time_t now, #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID)) { - size = add_do_bit(header, size, ((unsigned char *) header) + 65536); + size = add_do_bit(header, size, ((unsigned char *) header) + daemon->edns_pktsz); /* For debugging, set Checking Disabled, otherwise, have the upstream check too, this allows it to select auth servers when one is returning bad data. */ @@ -2615,12 +2614,14 @@ unsigned char *tcp_request(int confd, time_t now, #endif /* Loop round available servers until we succeed in connecting to one. */ - if ((m = tcp_talk(first, last, start, packet, size, have_mark, mark, &serv)) == 0) + if ((m = tcp_talk(first, last, start, header, size, bigbuff, have_mark, mark, &serv)) == 0) ede = EDE_NETERR; else { + /* just in case tcp_talk() expanded buffer - should never happen */ + out_header = bigbuff->iov_base; /* get query name again for logging - may have been overwritten */ - if (!extract_name(header, (unsigned int)size, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0)) + if (!extract_name(out_header, (unsigned int)size, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0)) strcpy(daemon->namebuff, "query"); log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff, &serv->addr, NULL, 0); @@ -2636,7 +2637,8 @@ unsigned char *tcp_request(int confd, time_t now, { int keycount = daemon->limit[LIMIT_WORK]; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */ int validatecount = daemon->limit[LIMIT_CRYPTO]; - int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname, + /* tcp_key_recurse() may overwrite packetbuf, and thuse *header is now invalid */ + int status = tcp_key_recurse(now, STAT_OK, out_header, m, 0, daemon->namebuff, daemon->keyname, serv, have_mark, mark, &keycount, &validatecount); char *result, *domain = "result"; @@ -2662,7 +2664,7 @@ unsigned char *tcp_request(int confd, time_t now, no_cache_dnssec = 1; bogusanswer = 1; - if (extract_name(header, m, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0)) + if (extract_name(out_header, m, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0)) domain = daemon->namebuff; } @@ -2686,18 +2688,18 @@ unsigned char *tcp_request(int confd, time_t now, /* restore CD bit to the value in the query */ if (checking_disabled) - header->hb4 |= HB4_CD; + out_header->hb4 |= HB4_CD; else - header->hb4 &= ~HB4_CD; + out_header->hb4 &= ~HB4_CD; /* Never cache answers which are contingent on the source or MAC address EDSN0 option, since the cache is ignorant of such things. */ if (!cacheable) no_cache_dnssec = 1; - m = process_reply(header, now, serv, (unsigned int)m, + m = process_reply(out_header, now, serv, (unsigned int)m, option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer, - ad_reqd, do_bit, !have_pseudoheader, &peer_addr, ((unsigned char *)header) + 65536, ede); + ad_reqd, do_bit, !have_pseudoheader, &peer_addr, ((unsigned char *)out_header) + 65536, ede); /* process_reply() adds pheader itself */ have_pseudoheader = 0; @@ -2712,8 +2714,8 @@ unsigned char *tcp_request(int confd, time_t now, /* In case of local answer or no connections made. */ if (m == 0) { - if (!(m = make_local_answer(flags, gotname, size, header, daemon->namebuff, - ((char *) header) + 65536, first, last, ede))) + if (!(m = make_local_answer(flags, gotname, size, out_header, daemon->namebuff, + ((char *) out_header) + 65536, first, last, ede))) break; } else if (ede == EDE_UNSET) @@ -2729,14 +2731,12 @@ unsigned char *tcp_request(int confd, time_t now, u16 swap = htons((u16)ede); if (ede != EDE_UNSET) - m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); + m = add_pseudoheader(out_header, m, ((unsigned char *) out_header) + 65536, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); else - m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, 0, NULL, 0, do_bit, 0); + m = add_pseudoheader(out_header, m, ((unsigned char *) out_header) + 65536, 0, NULL, 0, do_bit, 0); } - - check_log_writer(1); - *length = htons(m); + check_log_writer(1); #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) #ifdef HAVE_AUTH @@ -2746,7 +2746,9 @@ unsigned char *tcp_request(int confd, time_t now, report_addresses(header, m, mark); #endif - if (!read_write(confd, packet, m + sizeof(u16), RW_WRITE)) + out_len = htons(m); + if (!read_write(confd, (unsigned char *)&out_len, sizeof(out_len), RW_WRITE) | + !read_write(confd, bigbuff->iov_base, m, RW_WRITE)) break; /* If we answered with stale data, this process will now try and get fresh data into @@ -2762,18 +2764,15 @@ unsigned char *tcp_request(int confd, time_t now, daemon->log_source_addr = NULL; } } - - /* If we ran once to get fresh data, confd is already closed. */ + +/* If we ran once to get fresh data, confd is already closed. */ if (!do_stale) { shutdown(confd, SHUT_RDWR); close(confd); } - - blockdata_free(saved_question); + check_log_writer(1); - - return packet; } /* return a UDP socket bound to a random port, have to cope with straying into @@ -3274,7 +3273,7 @@ void resend_query(void) { if (daemon->srv_save) server_send(daemon->srv_save, daemon->fd_save, - daemon->packet, daemon->packet_len, 0); + daemon->packet, daemon->packet_len); } /* A server record is going away, remove references to it */