xref: /freebsd/contrib/unbound/services/outside_network.c (revision b7579f77d18196a58ff700756c84dc9a302a7f67)
1*b7579f77SDag-Erling Smørgrav /*
2*b7579f77SDag-Erling Smørgrav  * services/outside_network.c - implement sending of queries and wait answer.
3*b7579f77SDag-Erling Smørgrav  *
4*b7579f77SDag-Erling Smørgrav  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5*b7579f77SDag-Erling Smørgrav  *
6*b7579f77SDag-Erling Smørgrav  * This software is open source.
7*b7579f77SDag-Erling Smørgrav  *
8*b7579f77SDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
9*b7579f77SDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
10*b7579f77SDag-Erling Smørgrav  * are met:
11*b7579f77SDag-Erling Smørgrav  *
12*b7579f77SDag-Erling Smørgrav  * Redistributions of source code must retain the above copyright notice,
13*b7579f77SDag-Erling Smørgrav  * this list of conditions and the following disclaimer.
14*b7579f77SDag-Erling Smørgrav  *
15*b7579f77SDag-Erling Smørgrav  * Redistributions in binary form must reproduce the above copyright notice,
16*b7579f77SDag-Erling Smørgrav  * this list of conditions and the following disclaimer in the documentation
17*b7579f77SDag-Erling Smørgrav  * and/or other materials provided with the distribution.
18*b7579f77SDag-Erling Smørgrav  *
19*b7579f77SDag-Erling Smørgrav  * Neither the name of the NLNET LABS nor the names of its contributors may
20*b7579f77SDag-Erling Smørgrav  * be used to endorse or promote products derived from this software without
21*b7579f77SDag-Erling Smørgrav  * specific prior written permission.
22*b7579f77SDag-Erling Smørgrav  *
23*b7579f77SDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24*b7579f77SDag-Erling Smørgrav  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25*b7579f77SDag-Erling Smørgrav  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26*b7579f77SDag-Erling Smørgrav  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27*b7579f77SDag-Erling Smørgrav  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28*b7579f77SDag-Erling Smørgrav  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29*b7579f77SDag-Erling Smørgrav  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30*b7579f77SDag-Erling Smørgrav  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31*b7579f77SDag-Erling Smørgrav  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32*b7579f77SDag-Erling Smørgrav  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33*b7579f77SDag-Erling Smørgrav  * POSSIBILITY OF SUCH DAMAGE.
34*b7579f77SDag-Erling Smørgrav  */
35*b7579f77SDag-Erling Smørgrav 
36*b7579f77SDag-Erling Smørgrav /**
37*b7579f77SDag-Erling Smørgrav  * \file
38*b7579f77SDag-Erling Smørgrav  *
39*b7579f77SDag-Erling Smørgrav  * This file has functions to send queries to authoritative servers and
40*b7579f77SDag-Erling Smørgrav  * wait for the pending answer events.
41*b7579f77SDag-Erling Smørgrav  */
42*b7579f77SDag-Erling Smørgrav #include "config.h"
43*b7579f77SDag-Erling Smørgrav #include <ctype.h>
44*b7579f77SDag-Erling Smørgrav #ifdef HAVE_SYS_TYPES_H
45*b7579f77SDag-Erling Smørgrav #  include <sys/types.h>
46*b7579f77SDag-Erling Smørgrav #endif
47*b7579f77SDag-Erling Smørgrav #include <sys/time.h>
48*b7579f77SDag-Erling Smørgrav #include <ldns/wire2host.h>
49*b7579f77SDag-Erling Smørgrav #include "services/outside_network.h"
50*b7579f77SDag-Erling Smørgrav #include "services/listen_dnsport.h"
51*b7579f77SDag-Erling Smørgrav #include "services/cache/infra.h"
52*b7579f77SDag-Erling Smørgrav #include "util/data/msgparse.h"
53*b7579f77SDag-Erling Smørgrav #include "util/data/msgreply.h"
54*b7579f77SDag-Erling Smørgrav #include "util/data/msgencode.h"
55*b7579f77SDag-Erling Smørgrav #include "util/data/dname.h"
56*b7579f77SDag-Erling Smørgrav #include "util/netevent.h"
57*b7579f77SDag-Erling Smørgrav #include "util/log.h"
58*b7579f77SDag-Erling Smørgrav #include "util/net_help.h"
59*b7579f77SDag-Erling Smørgrav #include "util/random.h"
60*b7579f77SDag-Erling Smørgrav #include "util/fptr_wlist.h"
61*b7579f77SDag-Erling Smørgrav #include <openssl/ssl.h>
62*b7579f77SDag-Erling Smørgrav 
63*b7579f77SDag-Erling Smørgrav #ifdef HAVE_NETDB_H
64*b7579f77SDag-Erling Smørgrav #include <netdb.h>
65*b7579f77SDag-Erling Smørgrav #endif
66*b7579f77SDag-Erling Smørgrav #include <fcntl.h>
67*b7579f77SDag-Erling Smørgrav 
68*b7579f77SDag-Erling Smørgrav /** number of times to retry making a random ID that is unique. */
69*b7579f77SDag-Erling Smørgrav #define MAX_ID_RETRY 1000
70*b7579f77SDag-Erling Smørgrav /** number of times to retry finding interface, port that can be opened. */
71*b7579f77SDag-Erling Smørgrav #define MAX_PORT_RETRY 10000
72*b7579f77SDag-Erling Smørgrav /** number of retries on outgoing UDP queries */
73*b7579f77SDag-Erling Smørgrav #define OUTBOUND_UDP_RETRY 1
74*b7579f77SDag-Erling Smørgrav 
75*b7579f77SDag-Erling Smørgrav /** initiate TCP transaction for serviced query */
76*b7579f77SDag-Erling Smørgrav static void serviced_tcp_initiate(struct outside_network* outnet,
77*b7579f77SDag-Erling Smørgrav 	struct serviced_query* sq, ldns_buffer* buff);
78*b7579f77SDag-Erling Smørgrav /** with a fd available, randomize and send UDP */
79*b7579f77SDag-Erling Smørgrav static int randomize_and_send_udp(struct outside_network* outnet,
80*b7579f77SDag-Erling Smørgrav 	struct pending* pend, ldns_buffer* packet, int timeout);
81*b7579f77SDag-Erling Smørgrav 
82*b7579f77SDag-Erling Smørgrav int
83*b7579f77SDag-Erling Smørgrav pending_cmp(const void* key1, const void* key2)
84*b7579f77SDag-Erling Smørgrav {
85*b7579f77SDag-Erling Smørgrav 	struct pending *p1 = (struct pending*)key1;
86*b7579f77SDag-Erling Smørgrav 	struct pending *p2 = (struct pending*)key2;
87*b7579f77SDag-Erling Smørgrav 	if(p1->id < p2->id)
88*b7579f77SDag-Erling Smørgrav 		return -1;
89*b7579f77SDag-Erling Smørgrav 	if(p1->id > p2->id)
90*b7579f77SDag-Erling Smørgrav 		return 1;
91*b7579f77SDag-Erling Smørgrav 	log_assert(p1->id == p2->id);
92*b7579f77SDag-Erling Smørgrav 	return sockaddr_cmp(&p1->addr, p1->addrlen, &p2->addr, p2->addrlen);
93*b7579f77SDag-Erling Smørgrav }
94*b7579f77SDag-Erling Smørgrav 
95*b7579f77SDag-Erling Smørgrav int
96*b7579f77SDag-Erling Smørgrav serviced_cmp(const void* key1, const void* key2)
97*b7579f77SDag-Erling Smørgrav {
98*b7579f77SDag-Erling Smørgrav 	struct serviced_query* q1 = (struct serviced_query*)key1;
99*b7579f77SDag-Erling Smørgrav 	struct serviced_query* q2 = (struct serviced_query*)key2;
100*b7579f77SDag-Erling Smørgrav 	int r;
101*b7579f77SDag-Erling Smørgrav 	if(q1->qbuflen < q2->qbuflen)
102*b7579f77SDag-Erling Smørgrav 		return -1;
103*b7579f77SDag-Erling Smørgrav 	if(q1->qbuflen > q2->qbuflen)
104*b7579f77SDag-Erling Smørgrav 		return 1;
105*b7579f77SDag-Erling Smørgrav 	log_assert(q1->qbuflen == q2->qbuflen);
106*b7579f77SDag-Erling Smørgrav 	log_assert(q1->qbuflen >= 15 /* 10 header, root, type, class */);
107*b7579f77SDag-Erling Smørgrav 	/* alternate casing of qname is still the same query */
108*b7579f77SDag-Erling Smørgrav 	if((r = memcmp(q1->qbuf, q2->qbuf, 10)) != 0)
109*b7579f77SDag-Erling Smørgrav 		return r;
110*b7579f77SDag-Erling Smørgrav 	if((r = memcmp(q1->qbuf+q1->qbuflen-4, q2->qbuf+q2->qbuflen-4, 4)) != 0)
111*b7579f77SDag-Erling Smørgrav 		return r;
112*b7579f77SDag-Erling Smørgrav 	if(q1->dnssec != q2->dnssec) {
113*b7579f77SDag-Erling Smørgrav 		if(q1->dnssec < q2->dnssec)
114*b7579f77SDag-Erling Smørgrav 			return -1;
115*b7579f77SDag-Erling Smørgrav 		return 1;
116*b7579f77SDag-Erling Smørgrav 	}
117*b7579f77SDag-Erling Smørgrav 	if((r = query_dname_compare(q1->qbuf+10, q2->qbuf+10)) != 0)
118*b7579f77SDag-Erling Smørgrav 		return r;
119*b7579f77SDag-Erling Smørgrav 	return sockaddr_cmp(&q1->addr, q1->addrlen, &q2->addr, q2->addrlen);
120*b7579f77SDag-Erling Smørgrav }
121*b7579f77SDag-Erling Smørgrav 
122*b7579f77SDag-Erling Smørgrav /** delete waiting_tcp entry. Does not unlink from waiting list.
123*b7579f77SDag-Erling Smørgrav  * @param w: to delete.
124*b7579f77SDag-Erling Smørgrav  */
125*b7579f77SDag-Erling Smørgrav static void
126*b7579f77SDag-Erling Smørgrav waiting_tcp_delete(struct waiting_tcp* w)
127*b7579f77SDag-Erling Smørgrav {
128*b7579f77SDag-Erling Smørgrav 	if(!w) return;
129*b7579f77SDag-Erling Smørgrav 	if(w->timer)
130*b7579f77SDag-Erling Smørgrav 		comm_timer_delete(w->timer);
131*b7579f77SDag-Erling Smørgrav 	free(w);
132*b7579f77SDag-Erling Smørgrav }
133*b7579f77SDag-Erling Smørgrav 
134*b7579f77SDag-Erling Smørgrav /**
135*b7579f77SDag-Erling Smørgrav  * Pick random outgoing-interface of that family, and bind it.
136*b7579f77SDag-Erling Smørgrav  * port set to 0 so OS picks a port number for us.
137*b7579f77SDag-Erling Smørgrav  * if it is the ANY address, do not bind.
138*b7579f77SDag-Erling Smørgrav  * @param w: tcp structure with destination address.
139*b7579f77SDag-Erling Smørgrav  * @param s: socket fd.
140*b7579f77SDag-Erling Smørgrav  * @return false on error, socket closed.
141*b7579f77SDag-Erling Smørgrav  */
142*b7579f77SDag-Erling Smørgrav static int
143*b7579f77SDag-Erling Smørgrav pick_outgoing_tcp(struct waiting_tcp* w, int s)
144*b7579f77SDag-Erling Smørgrav {
145*b7579f77SDag-Erling Smørgrav 	struct port_if* pi = NULL;
146*b7579f77SDag-Erling Smørgrav 	int num;
147*b7579f77SDag-Erling Smørgrav #ifdef INET6
148*b7579f77SDag-Erling Smørgrav 	if(addr_is_ip6(&w->addr, w->addrlen))
149*b7579f77SDag-Erling Smørgrav 		num = w->outnet->num_ip6;
150*b7579f77SDag-Erling Smørgrav 	else
151*b7579f77SDag-Erling Smørgrav #endif
152*b7579f77SDag-Erling Smørgrav 		num = w->outnet->num_ip4;
153*b7579f77SDag-Erling Smørgrav 	if(num == 0) {
154*b7579f77SDag-Erling Smørgrav 		log_err("no TCP outgoing interfaces of family");
155*b7579f77SDag-Erling Smørgrav 		log_addr(VERB_OPS, "for addr", &w->addr, w->addrlen);
156*b7579f77SDag-Erling Smørgrav #ifndef USE_WINSOCK
157*b7579f77SDag-Erling Smørgrav 		close(s);
158*b7579f77SDag-Erling Smørgrav #else
159*b7579f77SDag-Erling Smørgrav 		closesocket(s);
160*b7579f77SDag-Erling Smørgrav #endif
161*b7579f77SDag-Erling Smørgrav 		return 0;
162*b7579f77SDag-Erling Smørgrav 	}
163*b7579f77SDag-Erling Smørgrav #ifdef INET6
164*b7579f77SDag-Erling Smørgrav 	if(addr_is_ip6(&w->addr, w->addrlen))
165*b7579f77SDag-Erling Smørgrav 		pi = &w->outnet->ip6_ifs[ub_random_max(w->outnet->rnd, num)];
166*b7579f77SDag-Erling Smørgrav 	else
167*b7579f77SDag-Erling Smørgrav #endif
168*b7579f77SDag-Erling Smørgrav 		pi = &w->outnet->ip4_ifs[ub_random_max(w->outnet->rnd, num)];
169*b7579f77SDag-Erling Smørgrav 	log_assert(pi);
170*b7579f77SDag-Erling Smørgrav 	if(addr_is_any(&pi->addr, pi->addrlen)) {
171*b7579f77SDag-Erling Smørgrav 		/* binding to the ANY interface is for listening sockets */
172*b7579f77SDag-Erling Smørgrav 		return 1;
173*b7579f77SDag-Erling Smørgrav 	}
174*b7579f77SDag-Erling Smørgrav 	/* set port to 0 */
175*b7579f77SDag-Erling Smørgrav 	if(addr_is_ip6(&pi->addr, pi->addrlen))
176*b7579f77SDag-Erling Smørgrav 		((struct sockaddr_in6*)&pi->addr)->sin6_port = 0;
177*b7579f77SDag-Erling Smørgrav 	else	((struct sockaddr_in*)&pi->addr)->sin_port = 0;
178*b7579f77SDag-Erling Smørgrav 	if(bind(s, (struct sockaddr*)&pi->addr, pi->addrlen) != 0) {
179*b7579f77SDag-Erling Smørgrav #ifndef USE_WINSOCK
180*b7579f77SDag-Erling Smørgrav 		log_err("outgoing tcp: bind: %s", strerror(errno));
181*b7579f77SDag-Erling Smørgrav 		close(s);
182*b7579f77SDag-Erling Smørgrav #else
183*b7579f77SDag-Erling Smørgrav 		log_err("outgoing tcp: bind: %s",
184*b7579f77SDag-Erling Smørgrav 			wsa_strerror(WSAGetLastError()));
185*b7579f77SDag-Erling Smørgrav 		closesocket(s);
186*b7579f77SDag-Erling Smørgrav #endif
187*b7579f77SDag-Erling Smørgrav 		return 0;
188*b7579f77SDag-Erling Smørgrav 	}
189*b7579f77SDag-Erling Smørgrav 	log_addr(VERB_ALGO, "tcp bound to src", &pi->addr, pi->addrlen);
190*b7579f77SDag-Erling Smørgrav 	return 1;
191*b7579f77SDag-Erling Smørgrav }
192*b7579f77SDag-Erling Smørgrav 
193*b7579f77SDag-Erling Smørgrav /** use next free buffer to service a tcp query */
194*b7579f77SDag-Erling Smørgrav static int
195*b7579f77SDag-Erling Smørgrav outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len)
196*b7579f77SDag-Erling Smørgrav {
197*b7579f77SDag-Erling Smørgrav 	struct pending_tcp* pend = w->outnet->tcp_free;
198*b7579f77SDag-Erling Smørgrav 	int s;
199*b7579f77SDag-Erling Smørgrav 	log_assert(pend);
200*b7579f77SDag-Erling Smørgrav 	log_assert(pkt);
201*b7579f77SDag-Erling Smørgrav 	log_assert(w->addrlen > 0);
202*b7579f77SDag-Erling Smørgrav 	/* open socket */
203*b7579f77SDag-Erling Smørgrav #ifdef INET6
204*b7579f77SDag-Erling Smørgrav 	if(addr_is_ip6(&w->addr, w->addrlen))
205*b7579f77SDag-Erling Smørgrav 		s = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
206*b7579f77SDag-Erling Smørgrav 	else
207*b7579f77SDag-Erling Smørgrav #endif
208*b7579f77SDag-Erling Smørgrav 		s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
209*b7579f77SDag-Erling Smørgrav 	if(s == -1) {
210*b7579f77SDag-Erling Smørgrav #ifndef USE_WINSOCK
211*b7579f77SDag-Erling Smørgrav 		log_err("outgoing tcp: socket: %s", strerror(errno));
212*b7579f77SDag-Erling Smørgrav #else
213*b7579f77SDag-Erling Smørgrav 		log_err("outgoing tcp: socket: %s",
214*b7579f77SDag-Erling Smørgrav 			wsa_strerror(WSAGetLastError()));
215*b7579f77SDag-Erling Smørgrav #endif
216*b7579f77SDag-Erling Smørgrav 		log_addr(0, "failed address", &w->addr, w->addrlen);
217*b7579f77SDag-Erling Smørgrav 		return 0;
218*b7579f77SDag-Erling Smørgrav 	}
219*b7579f77SDag-Erling Smørgrav 	if(!pick_outgoing_tcp(w, s))
220*b7579f77SDag-Erling Smørgrav 		return 0;
221*b7579f77SDag-Erling Smørgrav 
222*b7579f77SDag-Erling Smørgrav 	fd_set_nonblock(s);
223*b7579f77SDag-Erling Smørgrav 	if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) {
224*b7579f77SDag-Erling Smørgrav #ifndef USE_WINSOCK
225*b7579f77SDag-Erling Smørgrav #ifdef EINPROGRESS
226*b7579f77SDag-Erling Smørgrav 		if(errno != EINPROGRESS) {
227*b7579f77SDag-Erling Smørgrav #else
228*b7579f77SDag-Erling Smørgrav 		if(1) {
229*b7579f77SDag-Erling Smørgrav #endif
230*b7579f77SDag-Erling Smørgrav 			if(tcp_connect_errno_needs_log(
231*b7579f77SDag-Erling Smørgrav 				(struct sockaddr*)&w->addr, w->addrlen))
232*b7579f77SDag-Erling Smørgrav 				log_err("outgoing tcp: connect: %s",
233*b7579f77SDag-Erling Smørgrav 					strerror(errno));
234*b7579f77SDag-Erling Smørgrav 			close(s);
235*b7579f77SDag-Erling Smørgrav #else /* USE_WINSOCK */
236*b7579f77SDag-Erling Smørgrav 		if(WSAGetLastError() != WSAEINPROGRESS &&
237*b7579f77SDag-Erling Smørgrav 			WSAGetLastError() != WSAEWOULDBLOCK) {
238*b7579f77SDag-Erling Smørgrav 			closesocket(s);
239*b7579f77SDag-Erling Smørgrav #endif
240*b7579f77SDag-Erling Smørgrav 			log_addr(0, "failed address", &w->addr, w->addrlen);
241*b7579f77SDag-Erling Smørgrav 			return 0;
242*b7579f77SDag-Erling Smørgrav 		}
243*b7579f77SDag-Erling Smørgrav 	}
244*b7579f77SDag-Erling Smørgrav 	if(w->outnet->sslctx && w->ssl_upstream) {
245*b7579f77SDag-Erling Smørgrav 		pend->c->ssl = outgoing_ssl_fd(w->outnet->sslctx, s);
246*b7579f77SDag-Erling Smørgrav 		if(!pend->c->ssl) {
247*b7579f77SDag-Erling Smørgrav 			pend->c->fd = s;
248*b7579f77SDag-Erling Smørgrav 			comm_point_close(pend->c);
249*b7579f77SDag-Erling Smørgrav 			return 0;
250*b7579f77SDag-Erling Smørgrav 		}
251*b7579f77SDag-Erling Smørgrav #ifdef USE_WINSOCK
252*b7579f77SDag-Erling Smørgrav 		comm_point_tcp_win_bio_cb(pend->c, pend->c->ssl);
253*b7579f77SDag-Erling Smørgrav #endif
254*b7579f77SDag-Erling Smørgrav 		pend->c->ssl_shake_state = comm_ssl_shake_write;
255*b7579f77SDag-Erling Smørgrav 	}
256*b7579f77SDag-Erling Smørgrav 	w->pkt = NULL;
257*b7579f77SDag-Erling Smørgrav 	w->next_waiting = (void*)pend;
258*b7579f77SDag-Erling Smørgrav 	pend->id = LDNS_ID_WIRE(pkt);
259*b7579f77SDag-Erling Smørgrav 	w->outnet->tcp_free = pend->next_free;
260*b7579f77SDag-Erling Smørgrav 	pend->next_free = NULL;
261*b7579f77SDag-Erling Smørgrav 	pend->query = w;
262*b7579f77SDag-Erling Smørgrav 	pend->c->repinfo.addrlen = w->addrlen;
263*b7579f77SDag-Erling Smørgrav 	memcpy(&pend->c->repinfo.addr, &w->addr, w->addrlen);
264*b7579f77SDag-Erling Smørgrav 	ldns_buffer_clear(pend->c->buffer);
265*b7579f77SDag-Erling Smørgrav 	ldns_buffer_write(pend->c->buffer, pkt, pkt_len);
266*b7579f77SDag-Erling Smørgrav 	ldns_buffer_flip(pend->c->buffer);
267*b7579f77SDag-Erling Smørgrav 	pend->c->tcp_is_reading = 0;
268*b7579f77SDag-Erling Smørgrav 	pend->c->tcp_byte_count = 0;
269*b7579f77SDag-Erling Smørgrav 	comm_point_start_listening(pend->c, s, -1);
270*b7579f77SDag-Erling Smørgrav 	return 1;
271*b7579f77SDag-Erling Smørgrav }
272*b7579f77SDag-Erling Smørgrav 
273*b7579f77SDag-Erling Smørgrav /** see if buffers can be used to service TCP queries */
274*b7579f77SDag-Erling Smørgrav static void
275*b7579f77SDag-Erling Smørgrav use_free_buffer(struct outside_network* outnet)
276*b7579f77SDag-Erling Smørgrav {
277*b7579f77SDag-Erling Smørgrav 	struct waiting_tcp* w;
278*b7579f77SDag-Erling Smørgrav 	while(outnet->tcp_free && outnet->tcp_wait_first
279*b7579f77SDag-Erling Smørgrav 		&& !outnet->want_to_quit) {
280*b7579f77SDag-Erling Smørgrav 		w = outnet->tcp_wait_first;
281*b7579f77SDag-Erling Smørgrav 		outnet->tcp_wait_first = w->next_waiting;
282*b7579f77SDag-Erling Smørgrav 		if(outnet->tcp_wait_last == w)
283*b7579f77SDag-Erling Smørgrav 			outnet->tcp_wait_last = NULL;
284*b7579f77SDag-Erling Smørgrav 		if(!outnet_tcp_take_into_use(w, w->pkt, w->pkt_len)) {
285*b7579f77SDag-Erling Smørgrav 			comm_point_callback_t* cb = w->cb;
286*b7579f77SDag-Erling Smørgrav 			void* cb_arg = w->cb_arg;
287*b7579f77SDag-Erling Smørgrav 			waiting_tcp_delete(w);
288*b7579f77SDag-Erling Smørgrav 			fptr_ok(fptr_whitelist_pending_tcp(cb));
289*b7579f77SDag-Erling Smørgrav 			(void)(*cb)(NULL, cb_arg, NETEVENT_CLOSED, NULL);
290*b7579f77SDag-Erling Smørgrav 		}
291*b7579f77SDag-Erling Smørgrav 	}
292*b7579f77SDag-Erling Smørgrav }
293*b7579f77SDag-Erling Smørgrav 
294*b7579f77SDag-Erling Smørgrav /** decomission a tcp buffer, closes commpoint and frees waiting_tcp entry */
295*b7579f77SDag-Erling Smørgrav static void
296*b7579f77SDag-Erling Smørgrav decomission_pending_tcp(struct outside_network* outnet,
297*b7579f77SDag-Erling Smørgrav 	struct pending_tcp* pend)
298*b7579f77SDag-Erling Smørgrav {
299*b7579f77SDag-Erling Smørgrav 	if(pend->c->ssl) {
300*b7579f77SDag-Erling Smørgrav 		SSL_shutdown(pend->c->ssl);
301*b7579f77SDag-Erling Smørgrav 		SSL_free(pend->c->ssl);
302*b7579f77SDag-Erling Smørgrav 		pend->c->ssl = NULL;
303*b7579f77SDag-Erling Smørgrav 	}
304*b7579f77SDag-Erling Smørgrav 	comm_point_close(pend->c);
305*b7579f77SDag-Erling Smørgrav 	pend->next_free = outnet->tcp_free;
306*b7579f77SDag-Erling Smørgrav 	outnet->tcp_free = pend;
307*b7579f77SDag-Erling Smørgrav 	waiting_tcp_delete(pend->query);
308*b7579f77SDag-Erling Smørgrav 	pend->query = NULL;
309*b7579f77SDag-Erling Smørgrav 	use_free_buffer(outnet);
310*b7579f77SDag-Erling Smørgrav }
311*b7579f77SDag-Erling Smørgrav 
312*b7579f77SDag-Erling Smørgrav int
313*b7579f77SDag-Erling Smørgrav outnet_tcp_cb(struct comm_point* c, void* arg, int error,
314*b7579f77SDag-Erling Smørgrav 	struct comm_reply *reply_info)
315*b7579f77SDag-Erling Smørgrav {
316*b7579f77SDag-Erling Smørgrav 	struct pending_tcp* pend = (struct pending_tcp*)arg;
317*b7579f77SDag-Erling Smørgrav 	struct outside_network* outnet = pend->query->outnet;
318*b7579f77SDag-Erling Smørgrav 	verbose(VERB_ALGO, "outnettcp cb");
319*b7579f77SDag-Erling Smørgrav 	if(error != NETEVENT_NOERROR) {
320*b7579f77SDag-Erling Smørgrav 		verbose(VERB_QUERY, "outnettcp got tcp error %d", error);
321*b7579f77SDag-Erling Smørgrav 		/* pass error below and exit */
322*b7579f77SDag-Erling Smørgrav 	} else {
323*b7579f77SDag-Erling Smørgrav 		/* check ID */
324*b7579f77SDag-Erling Smørgrav 		if(ldns_buffer_limit(c->buffer) < sizeof(uint16_t) ||
325*b7579f77SDag-Erling Smørgrav 			LDNS_ID_WIRE(ldns_buffer_begin(c->buffer))!=pend->id) {
326*b7579f77SDag-Erling Smørgrav 			log_addr(VERB_QUERY,
327*b7579f77SDag-Erling Smørgrav 				"outnettcp: bad ID in reply, from:",
328*b7579f77SDag-Erling Smørgrav 				&pend->query->addr, pend->query->addrlen);
329*b7579f77SDag-Erling Smørgrav 			error = NETEVENT_CLOSED;
330*b7579f77SDag-Erling Smørgrav 		}
331*b7579f77SDag-Erling Smørgrav 	}
332*b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_pending_tcp(pend->query->cb));
333*b7579f77SDag-Erling Smørgrav 	(void)(*pend->query->cb)(c, pend->query->cb_arg, error, reply_info);
334*b7579f77SDag-Erling Smørgrav 	decomission_pending_tcp(outnet, pend);
335*b7579f77SDag-Erling Smørgrav 	return 0;
336*b7579f77SDag-Erling Smørgrav }
337*b7579f77SDag-Erling Smørgrav 
338*b7579f77SDag-Erling Smørgrav /** lower use count on pc, see if it can be closed */
339*b7579f77SDag-Erling Smørgrav static void
340*b7579f77SDag-Erling Smørgrav portcomm_loweruse(struct outside_network* outnet, struct port_comm* pc)
341*b7579f77SDag-Erling Smørgrav {
342*b7579f77SDag-Erling Smørgrav 	struct port_if* pif;
343*b7579f77SDag-Erling Smørgrav 	pc->num_outstanding--;
344*b7579f77SDag-Erling Smørgrav 	if(pc->num_outstanding > 0) {
345*b7579f77SDag-Erling Smørgrav 		return;
346*b7579f77SDag-Erling Smørgrav 	}
347*b7579f77SDag-Erling Smørgrav 	/* close it and replace in unused list */
348*b7579f77SDag-Erling Smørgrav 	verbose(VERB_ALGO, "close of port %d", pc->number);
349*b7579f77SDag-Erling Smørgrav 	comm_point_close(pc->cp);
350*b7579f77SDag-Erling Smørgrav 	pif = pc->pif;
351*b7579f77SDag-Erling Smørgrav 	log_assert(pif->inuse > 0);
352*b7579f77SDag-Erling Smørgrav 	pif->avail_ports[pif->avail_total - pif->inuse] = pc->number;
353*b7579f77SDag-Erling Smørgrav 	pif->inuse--;
354*b7579f77SDag-Erling Smørgrav 	pif->out[pc->index] = pif->out[pif->inuse];
355*b7579f77SDag-Erling Smørgrav 	pif->out[pc->index]->index = pc->index;
356*b7579f77SDag-Erling Smørgrav 	pc->next = outnet->unused_fds;
357*b7579f77SDag-Erling Smørgrav 	outnet->unused_fds = pc;
358*b7579f77SDag-Erling Smørgrav }
359*b7579f77SDag-Erling Smørgrav 
360*b7579f77SDag-Erling Smørgrav /** try to send waiting UDP queries */
361*b7579f77SDag-Erling Smørgrav static void
362*b7579f77SDag-Erling Smørgrav outnet_send_wait_udp(struct outside_network* outnet)
363*b7579f77SDag-Erling Smørgrav {
364*b7579f77SDag-Erling Smørgrav 	struct pending* pend;
365*b7579f77SDag-Erling Smørgrav 	/* process waiting queries */
366*b7579f77SDag-Erling Smørgrav 	while(outnet->udp_wait_first && outnet->unused_fds
367*b7579f77SDag-Erling Smørgrav 		&& !outnet->want_to_quit) {
368*b7579f77SDag-Erling Smørgrav 		pend = outnet->udp_wait_first;
369*b7579f77SDag-Erling Smørgrav 		outnet->udp_wait_first = pend->next_waiting;
370*b7579f77SDag-Erling Smørgrav 		if(!pend->next_waiting) outnet->udp_wait_last = NULL;
371*b7579f77SDag-Erling Smørgrav 		ldns_buffer_clear(outnet->udp_buff);
372*b7579f77SDag-Erling Smørgrav 		ldns_buffer_write(outnet->udp_buff, pend->pkt, pend->pkt_len);
373*b7579f77SDag-Erling Smørgrav 		ldns_buffer_flip(outnet->udp_buff);
374*b7579f77SDag-Erling Smørgrav 		free(pend->pkt); /* freeing now makes get_mem correct */
375*b7579f77SDag-Erling Smørgrav 		pend->pkt = NULL;
376*b7579f77SDag-Erling Smørgrav 		pend->pkt_len = 0;
377*b7579f77SDag-Erling Smørgrav 		if(!randomize_and_send_udp(outnet, pend, outnet->udp_buff,
378*b7579f77SDag-Erling Smørgrav 			pend->timeout)) {
379*b7579f77SDag-Erling Smørgrav 			/* callback error on pending */
380*b7579f77SDag-Erling Smørgrav 			fptr_ok(fptr_whitelist_pending_udp(pend->cb));
381*b7579f77SDag-Erling Smørgrav 			(void)(*pend->cb)(outnet->unused_fds->cp, pend->cb_arg,
382*b7579f77SDag-Erling Smørgrav 				NETEVENT_CLOSED, NULL);
383*b7579f77SDag-Erling Smørgrav 			pending_delete(outnet, pend);
384*b7579f77SDag-Erling Smørgrav 		}
385*b7579f77SDag-Erling Smørgrav 	}
386*b7579f77SDag-Erling Smørgrav }
387*b7579f77SDag-Erling Smørgrav 
388*b7579f77SDag-Erling Smørgrav int
389*b7579f77SDag-Erling Smørgrav outnet_udp_cb(struct comm_point* c, void* arg, int error,
390*b7579f77SDag-Erling Smørgrav 	struct comm_reply *reply_info)
391*b7579f77SDag-Erling Smørgrav {
392*b7579f77SDag-Erling Smørgrav 	struct outside_network* outnet = (struct outside_network*)arg;
393*b7579f77SDag-Erling Smørgrav 	struct pending key;
394*b7579f77SDag-Erling Smørgrav 	struct pending* p;
395*b7579f77SDag-Erling Smørgrav 	verbose(VERB_ALGO, "answer cb");
396*b7579f77SDag-Erling Smørgrav 
397*b7579f77SDag-Erling Smørgrav 	if(error != NETEVENT_NOERROR) {
398*b7579f77SDag-Erling Smørgrav 		verbose(VERB_QUERY, "outnetudp got udp error %d", error);
399*b7579f77SDag-Erling Smørgrav 		return 0;
400*b7579f77SDag-Erling Smørgrav 	}
401*b7579f77SDag-Erling Smørgrav 	if(ldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
402*b7579f77SDag-Erling Smørgrav 		verbose(VERB_QUERY, "outnetudp udp too short");
403*b7579f77SDag-Erling Smørgrav 		return 0;
404*b7579f77SDag-Erling Smørgrav 	}
405*b7579f77SDag-Erling Smørgrav 	log_assert(reply_info);
406*b7579f77SDag-Erling Smørgrav 
407*b7579f77SDag-Erling Smørgrav 	/* setup lookup key */
408*b7579f77SDag-Erling Smørgrav 	key.id = (unsigned)LDNS_ID_WIRE(ldns_buffer_begin(c->buffer));
409*b7579f77SDag-Erling Smørgrav 	memcpy(&key.addr, &reply_info->addr, reply_info->addrlen);
410*b7579f77SDag-Erling Smørgrav 	key.addrlen = reply_info->addrlen;
411*b7579f77SDag-Erling Smørgrav 	verbose(VERB_ALGO, "Incoming reply id = %4.4x", key.id);
412*b7579f77SDag-Erling Smørgrav 	log_addr(VERB_ALGO, "Incoming reply addr =",
413*b7579f77SDag-Erling Smørgrav 		&reply_info->addr, reply_info->addrlen);
414*b7579f77SDag-Erling Smørgrav 
415*b7579f77SDag-Erling Smørgrav 	/* find it, see if this thing is a valid query response */
416*b7579f77SDag-Erling Smørgrav 	verbose(VERB_ALGO, "lookup size is %d entries", (int)outnet->pending->count);
417*b7579f77SDag-Erling Smørgrav 	p = (struct pending*)rbtree_search(outnet->pending, &key);
418*b7579f77SDag-Erling Smørgrav 	if(!p) {
419*b7579f77SDag-Erling Smørgrav 		verbose(VERB_QUERY, "received unwanted or unsolicited udp reply dropped.");
420*b7579f77SDag-Erling Smørgrav 		log_buf(VERB_ALGO, "dropped message", c->buffer);
421*b7579f77SDag-Erling Smørgrav 		outnet->unwanted_replies++;
422*b7579f77SDag-Erling Smørgrav 		if(outnet->unwanted_threshold && ++outnet->unwanted_total
423*b7579f77SDag-Erling Smørgrav 			>= outnet->unwanted_threshold) {
424*b7579f77SDag-Erling Smørgrav 			log_warn("unwanted reply total reached threshold (%u)"
425*b7579f77SDag-Erling Smørgrav 				" you may be under attack."
426*b7579f77SDag-Erling Smørgrav 				" defensive action: clearing the cache",
427*b7579f77SDag-Erling Smørgrav 				(unsigned)outnet->unwanted_threshold);
428*b7579f77SDag-Erling Smørgrav 			fptr_ok(fptr_whitelist_alloc_cleanup(
429*b7579f77SDag-Erling Smørgrav 				outnet->unwanted_action));
430*b7579f77SDag-Erling Smørgrav 			(*outnet->unwanted_action)(outnet->unwanted_param);
431*b7579f77SDag-Erling Smørgrav 			outnet->unwanted_total = 0;
432*b7579f77SDag-Erling Smørgrav 		}
433*b7579f77SDag-Erling Smørgrav 		return 0;
434*b7579f77SDag-Erling Smørgrav 	}
435*b7579f77SDag-Erling Smørgrav 
436*b7579f77SDag-Erling Smørgrav 	verbose(VERB_ALGO, "received udp reply.");
437*b7579f77SDag-Erling Smørgrav 	log_buf(VERB_ALGO, "udp message", c->buffer);
438*b7579f77SDag-Erling Smørgrav 	if(p->pc->cp != c) {
439*b7579f77SDag-Erling Smørgrav 		verbose(VERB_QUERY, "received reply id,addr on wrong port. "
440*b7579f77SDag-Erling Smørgrav 			"dropped.");
441*b7579f77SDag-Erling Smørgrav 		outnet->unwanted_replies++;
442*b7579f77SDag-Erling Smørgrav 		if(outnet->unwanted_threshold && ++outnet->unwanted_total
443*b7579f77SDag-Erling Smørgrav 			>= outnet->unwanted_threshold) {
444*b7579f77SDag-Erling Smørgrav 			log_warn("unwanted reply total reached threshold (%u)"
445*b7579f77SDag-Erling Smørgrav 				" you may be under attack."
446*b7579f77SDag-Erling Smørgrav 				" defensive action: clearing the cache",
447*b7579f77SDag-Erling Smørgrav 				(unsigned)outnet->unwanted_threshold);
448*b7579f77SDag-Erling Smørgrav 			fptr_ok(fptr_whitelist_alloc_cleanup(
449*b7579f77SDag-Erling Smørgrav 				outnet->unwanted_action));
450*b7579f77SDag-Erling Smørgrav 			(*outnet->unwanted_action)(outnet->unwanted_param);
451*b7579f77SDag-Erling Smørgrav 			outnet->unwanted_total = 0;
452*b7579f77SDag-Erling Smørgrav 		}
453*b7579f77SDag-Erling Smørgrav 		return 0;
454*b7579f77SDag-Erling Smørgrav 	}
455*b7579f77SDag-Erling Smørgrav 	comm_timer_disable(p->timer);
456*b7579f77SDag-Erling Smørgrav 	verbose(VERB_ALGO, "outnet handle udp reply");
457*b7579f77SDag-Erling Smørgrav 	/* delete from tree first in case callback creates a retry */
458*b7579f77SDag-Erling Smørgrav 	(void)rbtree_delete(outnet->pending, p->node.key);
459*b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_pending_udp(p->cb));
460*b7579f77SDag-Erling Smørgrav 	(void)(*p->cb)(p->pc->cp, p->cb_arg, NETEVENT_NOERROR, reply_info);
461*b7579f77SDag-Erling Smørgrav 	portcomm_loweruse(outnet, p->pc);
462*b7579f77SDag-Erling Smørgrav 	pending_delete(NULL, p);
463*b7579f77SDag-Erling Smørgrav 	outnet_send_wait_udp(outnet);
464*b7579f77SDag-Erling Smørgrav 	return 0;
465*b7579f77SDag-Erling Smørgrav }
466*b7579f77SDag-Erling Smørgrav 
467*b7579f77SDag-Erling Smørgrav /** calculate number of ip4 and ip6 interfaces*/
468*b7579f77SDag-Erling Smørgrav static void
469*b7579f77SDag-Erling Smørgrav calc_num46(char** ifs, int num_ifs, int do_ip4, int do_ip6,
470*b7579f77SDag-Erling Smørgrav 	int* num_ip4, int* num_ip6)
471*b7579f77SDag-Erling Smørgrav {
472*b7579f77SDag-Erling Smørgrav 	int i;
473*b7579f77SDag-Erling Smørgrav 	*num_ip4 = 0;
474*b7579f77SDag-Erling Smørgrav 	*num_ip6 = 0;
475*b7579f77SDag-Erling Smørgrav 	if(num_ifs <= 0) {
476*b7579f77SDag-Erling Smørgrav 		if(do_ip4)
477*b7579f77SDag-Erling Smørgrav 			*num_ip4 = 1;
478*b7579f77SDag-Erling Smørgrav 		if(do_ip6)
479*b7579f77SDag-Erling Smørgrav 			*num_ip6 = 1;
480*b7579f77SDag-Erling Smørgrav 		return;
481*b7579f77SDag-Erling Smørgrav 	}
482*b7579f77SDag-Erling Smørgrav 	for(i=0; i<num_ifs; i++)
483*b7579f77SDag-Erling Smørgrav 	{
484*b7579f77SDag-Erling Smørgrav 		if(str_is_ip6(ifs[i])) {
485*b7579f77SDag-Erling Smørgrav 			if(do_ip6)
486*b7579f77SDag-Erling Smørgrav 				(*num_ip6)++;
487*b7579f77SDag-Erling Smørgrav 		} else {
488*b7579f77SDag-Erling Smørgrav 			if(do_ip4)
489*b7579f77SDag-Erling Smørgrav 				(*num_ip4)++;
490*b7579f77SDag-Erling Smørgrav 		}
491*b7579f77SDag-Erling Smørgrav 	}
492*b7579f77SDag-Erling Smørgrav 
493*b7579f77SDag-Erling Smørgrav }
494*b7579f77SDag-Erling Smørgrav 
495*b7579f77SDag-Erling Smørgrav void
496*b7579f77SDag-Erling Smørgrav pending_udp_timer_cb(void *arg)
497*b7579f77SDag-Erling Smørgrav {
498*b7579f77SDag-Erling Smørgrav 	struct pending* p = (struct pending*)arg;
499*b7579f77SDag-Erling Smørgrav 	struct outside_network* outnet = p->outnet;
500*b7579f77SDag-Erling Smørgrav 	/* it timed out */
501*b7579f77SDag-Erling Smørgrav 	verbose(VERB_ALGO, "timeout udp");
502*b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_pending_udp(p->cb));
503*b7579f77SDag-Erling Smørgrav 	(void)(*p->cb)(p->pc->cp, p->cb_arg, NETEVENT_TIMEOUT, NULL);
504*b7579f77SDag-Erling Smørgrav 	portcomm_loweruse(outnet, p->pc);
505*b7579f77SDag-Erling Smørgrav 	pending_delete(outnet, p);
506*b7579f77SDag-Erling Smørgrav 	outnet_send_wait_udp(outnet);
507*b7579f77SDag-Erling Smørgrav }
508*b7579f77SDag-Erling Smørgrav 
509*b7579f77SDag-Erling Smørgrav /** create pending_tcp buffers */
510*b7579f77SDag-Erling Smørgrav static int
511*b7579f77SDag-Erling Smørgrav create_pending_tcp(struct outside_network* outnet, size_t bufsize)
512*b7579f77SDag-Erling Smørgrav {
513*b7579f77SDag-Erling Smørgrav 	size_t i;
514*b7579f77SDag-Erling Smørgrav 	if(outnet->num_tcp == 0)
515*b7579f77SDag-Erling Smørgrav 		return 1; /* no tcp needed, nothing to do */
516*b7579f77SDag-Erling Smørgrav 	if(!(outnet->tcp_conns = (struct pending_tcp **)calloc(
517*b7579f77SDag-Erling Smørgrav 			outnet->num_tcp, sizeof(struct pending_tcp*))))
518*b7579f77SDag-Erling Smørgrav 		return 0;
519*b7579f77SDag-Erling Smørgrav 	for(i=0; i<outnet->num_tcp; i++) {
520*b7579f77SDag-Erling Smørgrav 		if(!(outnet->tcp_conns[i] = (struct pending_tcp*)calloc(1,
521*b7579f77SDag-Erling Smørgrav 			sizeof(struct pending_tcp))))
522*b7579f77SDag-Erling Smørgrav 			return 0;
523*b7579f77SDag-Erling Smørgrav 		outnet->tcp_conns[i]->next_free = outnet->tcp_free;
524*b7579f77SDag-Erling Smørgrav 		outnet->tcp_free = outnet->tcp_conns[i];
525*b7579f77SDag-Erling Smørgrav 		outnet->tcp_conns[i]->c = comm_point_create_tcp_out(
526*b7579f77SDag-Erling Smørgrav 			outnet->base, bufsize, outnet_tcp_cb,
527*b7579f77SDag-Erling Smørgrav 			outnet->tcp_conns[i]);
528*b7579f77SDag-Erling Smørgrav 		if(!outnet->tcp_conns[i]->c)
529*b7579f77SDag-Erling Smørgrav 			return 0;
530*b7579f77SDag-Erling Smørgrav 	}
531*b7579f77SDag-Erling Smørgrav 	return 1;
532*b7579f77SDag-Erling Smørgrav }
533*b7579f77SDag-Erling Smørgrav 
534*b7579f77SDag-Erling Smørgrav /** setup an outgoing interface, ready address */
535*b7579f77SDag-Erling Smørgrav static int setup_if(struct port_if* pif, const char* addrstr,
536*b7579f77SDag-Erling Smørgrav 	int* avail, int numavail, size_t numfd)
537*b7579f77SDag-Erling Smørgrav {
538*b7579f77SDag-Erling Smørgrav 	pif->avail_total = numavail;
539*b7579f77SDag-Erling Smørgrav 	pif->avail_ports = (int*)memdup(avail, (size_t)numavail*sizeof(int));
540*b7579f77SDag-Erling Smørgrav 	if(!pif->avail_ports)
541*b7579f77SDag-Erling Smørgrav 		return 0;
542*b7579f77SDag-Erling Smørgrav 	if(!ipstrtoaddr(addrstr, UNBOUND_DNS_PORT, &pif->addr, &pif->addrlen))
543*b7579f77SDag-Erling Smørgrav 		return 0;
544*b7579f77SDag-Erling Smørgrav 	pif->maxout = (int)numfd;
545*b7579f77SDag-Erling Smørgrav 	pif->inuse = 0;
546*b7579f77SDag-Erling Smørgrav 	pif->out = (struct port_comm**)calloc(numfd,
547*b7579f77SDag-Erling Smørgrav 		sizeof(struct port_comm*));
548*b7579f77SDag-Erling Smørgrav 	if(!pif->out)
549*b7579f77SDag-Erling Smørgrav 		return 0;
550*b7579f77SDag-Erling Smørgrav 	return 1;
551*b7579f77SDag-Erling Smørgrav }
552*b7579f77SDag-Erling Smørgrav 
553*b7579f77SDag-Erling Smørgrav struct outside_network*
554*b7579f77SDag-Erling Smørgrav outside_network_create(struct comm_base *base, size_t bufsize,
555*b7579f77SDag-Erling Smørgrav 	size_t num_ports, char** ifs, int num_ifs, int do_ip4,
556*b7579f77SDag-Erling Smørgrav 	int do_ip6, size_t num_tcp, struct infra_cache* infra,
557*b7579f77SDag-Erling Smørgrav 	struct ub_randstate* rnd, int use_caps_for_id, int* availports,
558*b7579f77SDag-Erling Smørgrav 	int numavailports, size_t unwanted_threshold,
559*b7579f77SDag-Erling Smørgrav 	void (*unwanted_action)(void*), void* unwanted_param, int do_udp,
560*b7579f77SDag-Erling Smørgrav 	void* sslctx)
561*b7579f77SDag-Erling Smørgrav {
562*b7579f77SDag-Erling Smørgrav 	struct outside_network* outnet = (struct outside_network*)
563*b7579f77SDag-Erling Smørgrav 		calloc(1, sizeof(struct outside_network));
564*b7579f77SDag-Erling Smørgrav 	size_t k;
565*b7579f77SDag-Erling Smørgrav 	if(!outnet) {
566*b7579f77SDag-Erling Smørgrav 		log_err("malloc failed");
567*b7579f77SDag-Erling Smørgrav 		return NULL;
568*b7579f77SDag-Erling Smørgrav 	}
569*b7579f77SDag-Erling Smørgrav 	comm_base_timept(base, &outnet->now_secs, &outnet->now_tv);
570*b7579f77SDag-Erling Smørgrav 	outnet->base = base;
571*b7579f77SDag-Erling Smørgrav 	outnet->num_tcp = num_tcp;
572*b7579f77SDag-Erling Smørgrav 	outnet->infra = infra;
573*b7579f77SDag-Erling Smørgrav 	outnet->rnd = rnd;
574*b7579f77SDag-Erling Smørgrav 	outnet->sslctx = sslctx;
575*b7579f77SDag-Erling Smørgrav 	outnet->svcd_overhead = 0;
576*b7579f77SDag-Erling Smørgrav 	outnet->want_to_quit = 0;
577*b7579f77SDag-Erling Smørgrav 	outnet->unwanted_threshold = unwanted_threshold;
578*b7579f77SDag-Erling Smørgrav 	outnet->unwanted_action = unwanted_action;
579*b7579f77SDag-Erling Smørgrav 	outnet->unwanted_param = unwanted_param;
580*b7579f77SDag-Erling Smørgrav 	outnet->use_caps_for_id = use_caps_for_id;
581*b7579f77SDag-Erling Smørgrav 	outnet->do_udp = do_udp;
582*b7579f77SDag-Erling Smørgrav 	if(numavailports == 0) {
583*b7579f77SDag-Erling Smørgrav 		log_err("no outgoing ports available");
584*b7579f77SDag-Erling Smørgrav 		outside_network_delete(outnet);
585*b7579f77SDag-Erling Smørgrav 		return NULL;
586*b7579f77SDag-Erling Smørgrav 	}
587*b7579f77SDag-Erling Smørgrav #ifndef INET6
588*b7579f77SDag-Erling Smørgrav 	do_ip6 = 0;
589*b7579f77SDag-Erling Smørgrav #endif
590*b7579f77SDag-Erling Smørgrav 	calc_num46(ifs, num_ifs, do_ip4, do_ip6,
591*b7579f77SDag-Erling Smørgrav 		&outnet->num_ip4, &outnet->num_ip6);
592*b7579f77SDag-Erling Smørgrav 	if(outnet->num_ip4 != 0) {
593*b7579f77SDag-Erling Smørgrav 		if(!(outnet->ip4_ifs = (struct port_if*)calloc(
594*b7579f77SDag-Erling Smørgrav 			(size_t)outnet->num_ip4, sizeof(struct port_if)))) {
595*b7579f77SDag-Erling Smørgrav 			log_err("malloc failed");
596*b7579f77SDag-Erling Smørgrav 			outside_network_delete(outnet);
597*b7579f77SDag-Erling Smørgrav 			return NULL;
598*b7579f77SDag-Erling Smørgrav 		}
599*b7579f77SDag-Erling Smørgrav 	}
600*b7579f77SDag-Erling Smørgrav 	if(outnet->num_ip6 != 0) {
601*b7579f77SDag-Erling Smørgrav 		if(!(outnet->ip6_ifs = (struct port_if*)calloc(
602*b7579f77SDag-Erling Smørgrav 			(size_t)outnet->num_ip6, sizeof(struct port_if)))) {
603*b7579f77SDag-Erling Smørgrav 			log_err("malloc failed");
604*b7579f77SDag-Erling Smørgrav 			outside_network_delete(outnet);
605*b7579f77SDag-Erling Smørgrav 			return NULL;
606*b7579f77SDag-Erling Smørgrav 		}
607*b7579f77SDag-Erling Smørgrav 	}
608*b7579f77SDag-Erling Smørgrav 	if(	!(outnet->udp_buff = ldns_buffer_new(bufsize)) ||
609*b7579f77SDag-Erling Smørgrav 		!(outnet->pending = rbtree_create(pending_cmp)) ||
610*b7579f77SDag-Erling Smørgrav 		!(outnet->serviced = rbtree_create(serviced_cmp)) ||
611*b7579f77SDag-Erling Smørgrav 		!create_pending_tcp(outnet, bufsize)) {
612*b7579f77SDag-Erling Smørgrav 		log_err("malloc failed");
613*b7579f77SDag-Erling Smørgrav 		outside_network_delete(outnet);
614*b7579f77SDag-Erling Smørgrav 		return NULL;
615*b7579f77SDag-Erling Smørgrav 	}
616*b7579f77SDag-Erling Smørgrav 
617*b7579f77SDag-Erling Smørgrav 	/* allocate commpoints */
618*b7579f77SDag-Erling Smørgrav 	for(k=0; k<num_ports; k++) {
619*b7579f77SDag-Erling Smørgrav 		struct port_comm* pc;
620*b7579f77SDag-Erling Smørgrav 		pc = (struct port_comm*)calloc(1, sizeof(*pc));
621*b7579f77SDag-Erling Smørgrav 		if(!pc) {
622*b7579f77SDag-Erling Smørgrav 			log_err("malloc failed");
623*b7579f77SDag-Erling Smørgrav 			outside_network_delete(outnet);
624*b7579f77SDag-Erling Smørgrav 			return NULL;
625*b7579f77SDag-Erling Smørgrav 		}
626*b7579f77SDag-Erling Smørgrav 		pc->cp = comm_point_create_udp(outnet->base, -1,
627*b7579f77SDag-Erling Smørgrav 			outnet->udp_buff, outnet_udp_cb, outnet);
628*b7579f77SDag-Erling Smørgrav 		if(!pc->cp) {
629*b7579f77SDag-Erling Smørgrav 			log_err("malloc failed");
630*b7579f77SDag-Erling Smørgrav 			free(pc);
631*b7579f77SDag-Erling Smørgrav 			outside_network_delete(outnet);
632*b7579f77SDag-Erling Smørgrav 			return NULL;
633*b7579f77SDag-Erling Smørgrav 		}
634*b7579f77SDag-Erling Smørgrav 		pc->next = outnet->unused_fds;
635*b7579f77SDag-Erling Smørgrav 		outnet->unused_fds = pc;
636*b7579f77SDag-Erling Smørgrav 	}
637*b7579f77SDag-Erling Smørgrav 
638*b7579f77SDag-Erling Smørgrav 	/* allocate interfaces */
639*b7579f77SDag-Erling Smørgrav 	if(num_ifs == 0) {
640*b7579f77SDag-Erling Smørgrav 		if(do_ip4 && !setup_if(&outnet->ip4_ifs[0], "0.0.0.0",
641*b7579f77SDag-Erling Smørgrav 			availports, numavailports, num_ports)) {
642*b7579f77SDag-Erling Smørgrav 			log_err("malloc failed");
643*b7579f77SDag-Erling Smørgrav 			outside_network_delete(outnet);
644*b7579f77SDag-Erling Smørgrav 			return NULL;
645*b7579f77SDag-Erling Smørgrav 		}
646*b7579f77SDag-Erling Smørgrav 		if(do_ip6 && !setup_if(&outnet->ip6_ifs[0], "::",
647*b7579f77SDag-Erling Smørgrav 			availports, numavailports, num_ports)) {
648*b7579f77SDag-Erling Smørgrav 			log_err("malloc failed");
649*b7579f77SDag-Erling Smørgrav 			outside_network_delete(outnet);
650*b7579f77SDag-Erling Smørgrav 			return NULL;
651*b7579f77SDag-Erling Smørgrav 		}
652*b7579f77SDag-Erling Smørgrav 	} else {
653*b7579f77SDag-Erling Smørgrav 		size_t done_4 = 0, done_6 = 0;
654*b7579f77SDag-Erling Smørgrav 		int i;
655*b7579f77SDag-Erling Smørgrav 		for(i=0; i<num_ifs; i++) {
656*b7579f77SDag-Erling Smørgrav 			if(str_is_ip6(ifs[i]) && do_ip6) {
657*b7579f77SDag-Erling Smørgrav 				if(!setup_if(&outnet->ip6_ifs[done_6], ifs[i],
658*b7579f77SDag-Erling Smørgrav 					availports, numavailports, num_ports)){
659*b7579f77SDag-Erling Smørgrav 					log_err("malloc failed");
660*b7579f77SDag-Erling Smørgrav 					outside_network_delete(outnet);
661*b7579f77SDag-Erling Smørgrav 					return NULL;
662*b7579f77SDag-Erling Smørgrav 				}
663*b7579f77SDag-Erling Smørgrav 				done_6++;
664*b7579f77SDag-Erling Smørgrav 			}
665*b7579f77SDag-Erling Smørgrav 			if(!str_is_ip6(ifs[i]) && do_ip4) {
666*b7579f77SDag-Erling Smørgrav 				if(!setup_if(&outnet->ip4_ifs[done_4], ifs[i],
667*b7579f77SDag-Erling Smørgrav 					availports, numavailports, num_ports)){
668*b7579f77SDag-Erling Smørgrav 					log_err("malloc failed");
669*b7579f77SDag-Erling Smørgrav 					outside_network_delete(outnet);
670*b7579f77SDag-Erling Smørgrav 					return NULL;
671*b7579f77SDag-Erling Smørgrav 				}
672*b7579f77SDag-Erling Smørgrav 				done_4++;
673*b7579f77SDag-Erling Smørgrav 			}
674*b7579f77SDag-Erling Smørgrav 		}
675*b7579f77SDag-Erling Smørgrav 	}
676*b7579f77SDag-Erling Smørgrav 	return outnet;
677*b7579f77SDag-Erling Smørgrav }
678*b7579f77SDag-Erling Smørgrav 
679*b7579f77SDag-Erling Smørgrav /** helper pending delete */
680*b7579f77SDag-Erling Smørgrav static void
681*b7579f77SDag-Erling Smørgrav pending_node_del(rbnode_t* node, void* arg)
682*b7579f77SDag-Erling Smørgrav {
683*b7579f77SDag-Erling Smørgrav 	struct pending* pend = (struct pending*)node;
684*b7579f77SDag-Erling Smørgrav 	struct outside_network* outnet = (struct outside_network*)arg;
685*b7579f77SDag-Erling Smørgrav 	pending_delete(outnet, pend);
686*b7579f77SDag-Erling Smørgrav }
687*b7579f77SDag-Erling Smørgrav 
688*b7579f77SDag-Erling Smørgrav /** helper serviced delete */
689*b7579f77SDag-Erling Smørgrav static void
690*b7579f77SDag-Erling Smørgrav serviced_node_del(rbnode_t* node, void* ATTR_UNUSED(arg))
691*b7579f77SDag-Erling Smørgrav {
692*b7579f77SDag-Erling Smørgrav 	struct serviced_query* sq = (struct serviced_query*)node;
693*b7579f77SDag-Erling Smørgrav 	struct service_callback* p = sq->cblist, *np;
694*b7579f77SDag-Erling Smørgrav 	free(sq->qbuf);
695*b7579f77SDag-Erling Smørgrav 	free(sq->zone);
696*b7579f77SDag-Erling Smørgrav 	while(p) {
697*b7579f77SDag-Erling Smørgrav 		np = p->next;
698*b7579f77SDag-Erling Smørgrav 		free(p);
699*b7579f77SDag-Erling Smørgrav 		p = np;
700*b7579f77SDag-Erling Smørgrav 	}
701*b7579f77SDag-Erling Smørgrav 	free(sq);
702*b7579f77SDag-Erling Smørgrav }
703*b7579f77SDag-Erling Smørgrav 
704*b7579f77SDag-Erling Smørgrav void
705*b7579f77SDag-Erling Smørgrav outside_network_quit_prepare(struct outside_network* outnet)
706*b7579f77SDag-Erling Smørgrav {
707*b7579f77SDag-Erling Smørgrav 	if(!outnet)
708*b7579f77SDag-Erling Smørgrav 		return;
709*b7579f77SDag-Erling Smørgrav 	/* prevent queued items from being sent */
710*b7579f77SDag-Erling Smørgrav 	outnet->want_to_quit = 1;
711*b7579f77SDag-Erling Smørgrav }
712*b7579f77SDag-Erling Smørgrav 
713*b7579f77SDag-Erling Smørgrav void
714*b7579f77SDag-Erling Smørgrav outside_network_delete(struct outside_network* outnet)
715*b7579f77SDag-Erling Smørgrav {
716*b7579f77SDag-Erling Smørgrav 	if(!outnet)
717*b7579f77SDag-Erling Smørgrav 		return;
718*b7579f77SDag-Erling Smørgrav 	outnet->want_to_quit = 1;
719*b7579f77SDag-Erling Smørgrav 	/* check every element, since we can be called on malloc error */
720*b7579f77SDag-Erling Smørgrav 	if(outnet->pending) {
721*b7579f77SDag-Erling Smørgrav 		/* free pending elements, but do no unlink from tree. */
722*b7579f77SDag-Erling Smørgrav 		traverse_postorder(outnet->pending, pending_node_del, NULL);
723*b7579f77SDag-Erling Smørgrav 		free(outnet->pending);
724*b7579f77SDag-Erling Smørgrav 	}
725*b7579f77SDag-Erling Smørgrav 	if(outnet->serviced) {
726*b7579f77SDag-Erling Smørgrav 		traverse_postorder(outnet->serviced, serviced_node_del, NULL);
727*b7579f77SDag-Erling Smørgrav 		free(outnet->serviced);
728*b7579f77SDag-Erling Smørgrav 	}
729*b7579f77SDag-Erling Smørgrav 	if(outnet->udp_buff)
730*b7579f77SDag-Erling Smørgrav 		ldns_buffer_free(outnet->udp_buff);
731*b7579f77SDag-Erling Smørgrav 	if(outnet->unused_fds) {
732*b7579f77SDag-Erling Smørgrav 		struct port_comm* p = outnet->unused_fds, *np;
733*b7579f77SDag-Erling Smørgrav 		while(p) {
734*b7579f77SDag-Erling Smørgrav 			np = p->next;
735*b7579f77SDag-Erling Smørgrav 			comm_point_delete(p->cp);
736*b7579f77SDag-Erling Smørgrav 			free(p);
737*b7579f77SDag-Erling Smørgrav 			p = np;
738*b7579f77SDag-Erling Smørgrav 		}
739*b7579f77SDag-Erling Smørgrav 		outnet->unused_fds = NULL;
740*b7579f77SDag-Erling Smørgrav 	}
741*b7579f77SDag-Erling Smørgrav 	if(outnet->ip4_ifs) {
742*b7579f77SDag-Erling Smørgrav 		int i, k;
743*b7579f77SDag-Erling Smørgrav 		for(i=0; i<outnet->num_ip4; i++) {
744*b7579f77SDag-Erling Smørgrav 			for(k=0; k<outnet->ip4_ifs[i].inuse; k++) {
745*b7579f77SDag-Erling Smørgrav 				struct port_comm* pc = outnet->ip4_ifs[i].
746*b7579f77SDag-Erling Smørgrav 					out[k];
747*b7579f77SDag-Erling Smørgrav 				comm_point_delete(pc->cp);
748*b7579f77SDag-Erling Smørgrav 				free(pc);
749*b7579f77SDag-Erling Smørgrav 			}
750*b7579f77SDag-Erling Smørgrav 			free(outnet->ip4_ifs[i].avail_ports);
751*b7579f77SDag-Erling Smørgrav 			free(outnet->ip4_ifs[i].out);
752*b7579f77SDag-Erling Smørgrav 		}
753*b7579f77SDag-Erling Smørgrav 		free(outnet->ip4_ifs);
754*b7579f77SDag-Erling Smørgrav 	}
755*b7579f77SDag-Erling Smørgrav 	if(outnet->ip6_ifs) {
756*b7579f77SDag-Erling Smørgrav 		int i, k;
757*b7579f77SDag-Erling Smørgrav 		for(i=0; i<outnet->num_ip6; i++) {
758*b7579f77SDag-Erling Smørgrav 			for(k=0; k<outnet->ip6_ifs[i].inuse; k++) {
759*b7579f77SDag-Erling Smørgrav 				struct port_comm* pc = outnet->ip6_ifs[i].
760*b7579f77SDag-Erling Smørgrav 					out[k];
761*b7579f77SDag-Erling Smørgrav 				comm_point_delete(pc->cp);
762*b7579f77SDag-Erling Smørgrav 				free(pc);
763*b7579f77SDag-Erling Smørgrav 			}
764*b7579f77SDag-Erling Smørgrav 			free(outnet->ip6_ifs[i].avail_ports);
765*b7579f77SDag-Erling Smørgrav 			free(outnet->ip6_ifs[i].out);
766*b7579f77SDag-Erling Smørgrav 		}
767*b7579f77SDag-Erling Smørgrav 		free(outnet->ip6_ifs);
768*b7579f77SDag-Erling Smørgrav 	}
769*b7579f77SDag-Erling Smørgrav 	if(outnet->tcp_conns) {
770*b7579f77SDag-Erling Smørgrav 		size_t i;
771*b7579f77SDag-Erling Smørgrav 		for(i=0; i<outnet->num_tcp; i++)
772*b7579f77SDag-Erling Smørgrav 			if(outnet->tcp_conns[i]) {
773*b7579f77SDag-Erling Smørgrav 				comm_point_delete(outnet->tcp_conns[i]->c);
774*b7579f77SDag-Erling Smørgrav 				waiting_tcp_delete(outnet->tcp_conns[i]->query);
775*b7579f77SDag-Erling Smørgrav 				free(outnet->tcp_conns[i]);
776*b7579f77SDag-Erling Smørgrav 			}
777*b7579f77SDag-Erling Smørgrav 		free(outnet->tcp_conns);
778*b7579f77SDag-Erling Smørgrav 	}
779*b7579f77SDag-Erling Smørgrav 	if(outnet->tcp_wait_first) {
780*b7579f77SDag-Erling Smørgrav 		struct waiting_tcp* p = outnet->tcp_wait_first, *np;
781*b7579f77SDag-Erling Smørgrav 		while(p) {
782*b7579f77SDag-Erling Smørgrav 			np = p->next_waiting;
783*b7579f77SDag-Erling Smørgrav 			waiting_tcp_delete(p);
784*b7579f77SDag-Erling Smørgrav 			p = np;
785*b7579f77SDag-Erling Smørgrav 		}
786*b7579f77SDag-Erling Smørgrav 	}
787*b7579f77SDag-Erling Smørgrav 	if(outnet->udp_wait_first) {
788*b7579f77SDag-Erling Smørgrav 		struct pending* p = outnet->udp_wait_first, *np;
789*b7579f77SDag-Erling Smørgrav 		while(p) {
790*b7579f77SDag-Erling Smørgrav 			np = p->next_waiting;
791*b7579f77SDag-Erling Smørgrav 			pending_delete(NULL, p);
792*b7579f77SDag-Erling Smørgrav 			p = np;
793*b7579f77SDag-Erling Smørgrav 		}
794*b7579f77SDag-Erling Smørgrav 	}
795*b7579f77SDag-Erling Smørgrav 	free(outnet);
796*b7579f77SDag-Erling Smørgrav }
797*b7579f77SDag-Erling Smørgrav 
798*b7579f77SDag-Erling Smørgrav void
799*b7579f77SDag-Erling Smørgrav pending_delete(struct outside_network* outnet, struct pending* p)
800*b7579f77SDag-Erling Smørgrav {
801*b7579f77SDag-Erling Smørgrav 	if(!p)
802*b7579f77SDag-Erling Smørgrav 		return;
803*b7579f77SDag-Erling Smørgrav 	if(outnet && outnet->udp_wait_first &&
804*b7579f77SDag-Erling Smørgrav 		(p->next_waiting || p == outnet->udp_wait_last) ) {
805*b7579f77SDag-Erling Smørgrav 		/* delete from waiting list, if it is in the waiting list */
806*b7579f77SDag-Erling Smørgrav 		struct pending* prev = NULL, *x = outnet->udp_wait_first;
807*b7579f77SDag-Erling Smørgrav 		while(x && x != p) {
808*b7579f77SDag-Erling Smørgrav 			prev = x;
809*b7579f77SDag-Erling Smørgrav 			x = x->next_waiting;
810*b7579f77SDag-Erling Smørgrav 		}
811*b7579f77SDag-Erling Smørgrav 		if(x) {
812*b7579f77SDag-Erling Smørgrav 			log_assert(x == p);
813*b7579f77SDag-Erling Smørgrav 			if(prev)
814*b7579f77SDag-Erling Smørgrav 				prev->next_waiting = p->next_waiting;
815*b7579f77SDag-Erling Smørgrav 			else	outnet->udp_wait_first = p->next_waiting;
816*b7579f77SDag-Erling Smørgrav 			if(outnet->udp_wait_last == p)
817*b7579f77SDag-Erling Smørgrav 				outnet->udp_wait_last = prev;
818*b7579f77SDag-Erling Smørgrav 		}
819*b7579f77SDag-Erling Smørgrav 	}
820*b7579f77SDag-Erling Smørgrav 	if(outnet) {
821*b7579f77SDag-Erling Smørgrav 		(void)rbtree_delete(outnet->pending, p->node.key);
822*b7579f77SDag-Erling Smørgrav 	}
823*b7579f77SDag-Erling Smørgrav 	if(p->timer)
824*b7579f77SDag-Erling Smørgrav 		comm_timer_delete(p->timer);
825*b7579f77SDag-Erling Smørgrav 	free(p->pkt);
826*b7579f77SDag-Erling Smørgrav 	free(p);
827*b7579f77SDag-Erling Smørgrav }
828*b7579f77SDag-Erling Smørgrav 
829*b7579f77SDag-Erling Smørgrav /**
830*b7579f77SDag-Erling Smørgrav  * Try to open a UDP socket for outgoing communication.
831*b7579f77SDag-Erling Smørgrav  * Sets sockets options as needed.
832*b7579f77SDag-Erling Smørgrav  * @param addr: socket address.
833*b7579f77SDag-Erling Smørgrav  * @param addrlen: length of address.
834*b7579f77SDag-Erling Smørgrav  * @param port: port override for addr.
835*b7579f77SDag-Erling Smørgrav  * @param inuse: if -1 is returned, this bool means the port was in use.
836*b7579f77SDag-Erling Smørgrav  * @return fd or -1
837*b7579f77SDag-Erling Smørgrav  */
838*b7579f77SDag-Erling Smørgrav static int
839*b7579f77SDag-Erling Smørgrav udp_sockport(struct sockaddr_storage* addr, socklen_t addrlen, int port,
840*b7579f77SDag-Erling Smørgrav 	int* inuse)
841*b7579f77SDag-Erling Smørgrav {
842*b7579f77SDag-Erling Smørgrav 	int fd, noproto;
843*b7579f77SDag-Erling Smørgrav 	if(addr_is_ip6(addr, addrlen)) {
844*b7579f77SDag-Erling Smørgrav 		struct sockaddr_in6* sa = (struct sockaddr_in6*)addr;
845*b7579f77SDag-Erling Smørgrav 		sa->sin6_port = (in_port_t)htons((uint16_t)port);
846*b7579f77SDag-Erling Smørgrav 		fd = create_udp_sock(AF_INET6, SOCK_DGRAM,
847*b7579f77SDag-Erling Smørgrav 			(struct sockaddr*)addr, addrlen, 1, inuse, &noproto,
848*b7579f77SDag-Erling Smørgrav 			0, 0);
849*b7579f77SDag-Erling Smørgrav 	} else {
850*b7579f77SDag-Erling Smørgrav 		struct sockaddr_in* sa = (struct sockaddr_in*)addr;
851*b7579f77SDag-Erling Smørgrav 		sa->sin_port = (in_port_t)htons((uint16_t)port);
852*b7579f77SDag-Erling Smørgrav 		fd = create_udp_sock(AF_INET, SOCK_DGRAM,
853*b7579f77SDag-Erling Smørgrav 			(struct sockaddr*)addr, addrlen, 1, inuse, &noproto,
854*b7579f77SDag-Erling Smørgrav 			0, 0);
855*b7579f77SDag-Erling Smørgrav 	}
856*b7579f77SDag-Erling Smørgrav 	return fd;
857*b7579f77SDag-Erling Smørgrav }
858*b7579f77SDag-Erling Smørgrav 
859*b7579f77SDag-Erling Smørgrav /** Select random ID */
860*b7579f77SDag-Erling Smørgrav static int
861*b7579f77SDag-Erling Smørgrav select_id(struct outside_network* outnet, struct pending* pend,
862*b7579f77SDag-Erling Smørgrav 	ldns_buffer* packet)
863*b7579f77SDag-Erling Smørgrav {
864*b7579f77SDag-Erling Smørgrav 	int id_tries = 0;
865*b7579f77SDag-Erling Smørgrav 	pend->id = ((unsigned)ub_random(outnet->rnd)>>8) & 0xffff;
866*b7579f77SDag-Erling Smørgrav 	LDNS_ID_SET(ldns_buffer_begin(packet), pend->id);
867*b7579f77SDag-Erling Smørgrav 
868*b7579f77SDag-Erling Smørgrav 	/* insert in tree */
869*b7579f77SDag-Erling Smørgrav 	pend->node.key = pend;
870*b7579f77SDag-Erling Smørgrav 	while(!rbtree_insert(outnet->pending, &pend->node)) {
871*b7579f77SDag-Erling Smørgrav 		/* change ID to avoid collision */
872*b7579f77SDag-Erling Smørgrav 		pend->id = ((unsigned)ub_random(outnet->rnd)>>8) & 0xffff;
873*b7579f77SDag-Erling Smørgrav 		LDNS_ID_SET(ldns_buffer_begin(packet), pend->id);
874*b7579f77SDag-Erling Smørgrav 		id_tries++;
875*b7579f77SDag-Erling Smørgrav 		if(id_tries == MAX_ID_RETRY) {
876*b7579f77SDag-Erling Smørgrav 			pend->id=99999; /* non existant ID */
877*b7579f77SDag-Erling Smørgrav 			log_err("failed to generate unique ID, drop msg");
878*b7579f77SDag-Erling Smørgrav 			return 0;
879*b7579f77SDag-Erling Smørgrav 		}
880*b7579f77SDag-Erling Smørgrav 	}
881*b7579f77SDag-Erling Smørgrav 	verbose(VERB_ALGO, "inserted new pending reply id=%4.4x", pend->id);
882*b7579f77SDag-Erling Smørgrav 	return 1;
883*b7579f77SDag-Erling Smørgrav }
884*b7579f77SDag-Erling Smørgrav 
885*b7579f77SDag-Erling Smørgrav /** Select random interface and port */
886*b7579f77SDag-Erling Smørgrav static int
887*b7579f77SDag-Erling Smørgrav select_ifport(struct outside_network* outnet, struct pending* pend,
888*b7579f77SDag-Erling Smørgrav 	int num_if, struct port_if* ifs)
889*b7579f77SDag-Erling Smørgrav {
890*b7579f77SDag-Erling Smørgrav 	int my_if, my_port, fd, portno, inuse, tries=0;
891*b7579f77SDag-Erling Smørgrav 	struct port_if* pif;
892*b7579f77SDag-Erling Smørgrav 	/* randomly select interface and port */
893*b7579f77SDag-Erling Smørgrav 	if(num_if == 0) {
894*b7579f77SDag-Erling Smørgrav 		verbose(VERB_QUERY, "Need to send query but have no "
895*b7579f77SDag-Erling Smørgrav 			"outgoing interfaces of that family");
896*b7579f77SDag-Erling Smørgrav 		return 0;
897*b7579f77SDag-Erling Smørgrav 	}
898*b7579f77SDag-Erling Smørgrav 	log_assert(outnet->unused_fds);
899*b7579f77SDag-Erling Smørgrav 	tries = 0;
900*b7579f77SDag-Erling Smørgrav 	while(1) {
901*b7579f77SDag-Erling Smørgrav 		my_if = ub_random_max(outnet->rnd, num_if);
902*b7579f77SDag-Erling Smørgrav 		pif = &ifs[my_if];
903*b7579f77SDag-Erling Smørgrav 		my_port = ub_random_max(outnet->rnd, pif->avail_total);
904*b7579f77SDag-Erling Smørgrav 		if(my_port < pif->inuse) {
905*b7579f77SDag-Erling Smørgrav 			/* port already open */
906*b7579f77SDag-Erling Smørgrav 			pend->pc = pif->out[my_port];
907*b7579f77SDag-Erling Smørgrav 			verbose(VERB_ALGO, "using UDP if=%d port=%d",
908*b7579f77SDag-Erling Smørgrav 				my_if, pend->pc->number);
909*b7579f77SDag-Erling Smørgrav 			break;
910*b7579f77SDag-Erling Smørgrav 		}
911*b7579f77SDag-Erling Smørgrav 		/* try to open new port, if fails, loop to try again */
912*b7579f77SDag-Erling Smørgrav 		log_assert(pif->inuse < pif->maxout);
913*b7579f77SDag-Erling Smørgrav 		portno = pif->avail_ports[my_port - pif->inuse];
914*b7579f77SDag-Erling Smørgrav 		fd = udp_sockport(&pif->addr, pif->addrlen, portno, &inuse);
915*b7579f77SDag-Erling Smørgrav 		if(fd == -1 && !inuse) {
916*b7579f77SDag-Erling Smørgrav 			/* nonrecoverable error making socket */
917*b7579f77SDag-Erling Smørgrav 			return 0;
918*b7579f77SDag-Erling Smørgrav 		}
919*b7579f77SDag-Erling Smørgrav 		if(fd != -1) {
920*b7579f77SDag-Erling Smørgrav 			verbose(VERB_ALGO, "opened UDP if=%d port=%d",
921*b7579f77SDag-Erling Smørgrav 				my_if, portno);
922*b7579f77SDag-Erling Smørgrav 			/* grab fd */
923*b7579f77SDag-Erling Smørgrav 			pend->pc = outnet->unused_fds;
924*b7579f77SDag-Erling Smørgrav 			outnet->unused_fds = pend->pc->next;
925*b7579f77SDag-Erling Smørgrav 
926*b7579f77SDag-Erling Smørgrav 			/* setup portcomm */
927*b7579f77SDag-Erling Smørgrav 			pend->pc->next = NULL;
928*b7579f77SDag-Erling Smørgrav 			pend->pc->number = portno;
929*b7579f77SDag-Erling Smørgrav 			pend->pc->pif = pif;
930*b7579f77SDag-Erling Smørgrav 			pend->pc->index = pif->inuse;
931*b7579f77SDag-Erling Smørgrav 			pend->pc->num_outstanding = 0;
932*b7579f77SDag-Erling Smørgrav 			comm_point_start_listening(pend->pc->cp, fd, -1);
933*b7579f77SDag-Erling Smørgrav 
934*b7579f77SDag-Erling Smørgrav 			/* grab port in interface */
935*b7579f77SDag-Erling Smørgrav 			pif->out[pif->inuse] = pend->pc;
936*b7579f77SDag-Erling Smørgrav 			pif->avail_ports[my_port - pif->inuse] =
937*b7579f77SDag-Erling Smørgrav 				pif->avail_ports[pif->avail_total-pif->inuse-1];
938*b7579f77SDag-Erling Smørgrav 			pif->inuse++;
939*b7579f77SDag-Erling Smørgrav 			break;
940*b7579f77SDag-Erling Smørgrav 		}
941*b7579f77SDag-Erling Smørgrav 		/* failed, already in use */
942*b7579f77SDag-Erling Smørgrav 		verbose(VERB_QUERY, "port %d in use, trying another", portno);
943*b7579f77SDag-Erling Smørgrav 		tries++;
944*b7579f77SDag-Erling Smørgrav 		if(tries == MAX_PORT_RETRY) {
945*b7579f77SDag-Erling Smørgrav 			log_err("failed to find an open port, drop msg");
946*b7579f77SDag-Erling Smørgrav 			return 0;
947*b7579f77SDag-Erling Smørgrav 		}
948*b7579f77SDag-Erling Smørgrav 	}
949*b7579f77SDag-Erling Smørgrav 	log_assert(pend->pc);
950*b7579f77SDag-Erling Smørgrav 	pend->pc->num_outstanding++;
951*b7579f77SDag-Erling Smørgrav 
952*b7579f77SDag-Erling Smørgrav 	return 1;
953*b7579f77SDag-Erling Smørgrav }
954*b7579f77SDag-Erling Smørgrav 
955*b7579f77SDag-Erling Smørgrav static int
956*b7579f77SDag-Erling Smørgrav randomize_and_send_udp(struct outside_network* outnet, struct pending* pend,
957*b7579f77SDag-Erling Smørgrav 	ldns_buffer* packet, int timeout)
958*b7579f77SDag-Erling Smørgrav {
959*b7579f77SDag-Erling Smørgrav 	struct timeval tv;
960*b7579f77SDag-Erling Smørgrav 
961*b7579f77SDag-Erling Smørgrav 	/* select id */
962*b7579f77SDag-Erling Smørgrav 	if(!select_id(outnet, pend, packet)) {
963*b7579f77SDag-Erling Smørgrav 		return 0;
964*b7579f77SDag-Erling Smørgrav 	}
965*b7579f77SDag-Erling Smørgrav 
966*b7579f77SDag-Erling Smørgrav 	/* select src_if, port */
967*b7579f77SDag-Erling Smørgrav 	if(addr_is_ip6(&pend->addr, pend->addrlen)) {
968*b7579f77SDag-Erling Smørgrav 		if(!select_ifport(outnet, pend,
969*b7579f77SDag-Erling Smørgrav 			outnet->num_ip6, outnet->ip6_ifs))
970*b7579f77SDag-Erling Smørgrav 			return 0;
971*b7579f77SDag-Erling Smørgrav 	} else {
972*b7579f77SDag-Erling Smørgrav 		if(!select_ifport(outnet, pend,
973*b7579f77SDag-Erling Smørgrav 			outnet->num_ip4, outnet->ip4_ifs))
974*b7579f77SDag-Erling Smørgrav 			return 0;
975*b7579f77SDag-Erling Smørgrav 	}
976*b7579f77SDag-Erling Smørgrav 	log_assert(pend->pc && pend->pc->cp);
977*b7579f77SDag-Erling Smørgrav 
978*b7579f77SDag-Erling Smørgrav 	/* send it over the commlink */
979*b7579f77SDag-Erling Smørgrav 	if(!comm_point_send_udp_msg(pend->pc->cp, packet,
980*b7579f77SDag-Erling Smørgrav 		(struct sockaddr*)&pend->addr, pend->addrlen)) {
981*b7579f77SDag-Erling Smørgrav 		portcomm_loweruse(outnet, pend->pc);
982*b7579f77SDag-Erling Smørgrav 		return 0;
983*b7579f77SDag-Erling Smørgrav 	}
984*b7579f77SDag-Erling Smørgrav 
985*b7579f77SDag-Erling Smørgrav 	/* system calls to set timeout after sending UDP to make roundtrip
986*b7579f77SDag-Erling Smørgrav 	   smaller. */
987*b7579f77SDag-Erling Smørgrav #ifndef S_SPLINT_S
988*b7579f77SDag-Erling Smørgrav 	tv.tv_sec = timeout/1000;
989*b7579f77SDag-Erling Smørgrav 	tv.tv_usec = (timeout%1000)*1000;
990*b7579f77SDag-Erling Smørgrav #endif
991*b7579f77SDag-Erling Smørgrav 	comm_timer_set(pend->timer, &tv);
992*b7579f77SDag-Erling Smørgrav 	return 1;
993*b7579f77SDag-Erling Smørgrav }
994*b7579f77SDag-Erling Smørgrav 
995*b7579f77SDag-Erling Smørgrav struct pending*
996*b7579f77SDag-Erling Smørgrav pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
997*b7579f77SDag-Erling Smørgrav 	struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
998*b7579f77SDag-Erling Smørgrav 	comm_point_callback_t* cb, void* cb_arg)
999*b7579f77SDag-Erling Smørgrav {
1000*b7579f77SDag-Erling Smørgrav 	struct pending* pend = (struct pending*)calloc(1, sizeof(*pend));
1001*b7579f77SDag-Erling Smørgrav 	if(!pend) return NULL;
1002*b7579f77SDag-Erling Smørgrav 	pend->outnet = outnet;
1003*b7579f77SDag-Erling Smørgrav 	pend->addrlen = addrlen;
1004*b7579f77SDag-Erling Smørgrav 	memmove(&pend->addr, addr, addrlen);
1005*b7579f77SDag-Erling Smørgrav 	pend->cb = cb;
1006*b7579f77SDag-Erling Smørgrav 	pend->cb_arg = cb_arg;
1007*b7579f77SDag-Erling Smørgrav 	pend->node.key = pend;
1008*b7579f77SDag-Erling Smørgrav 	pend->timer = comm_timer_create(outnet->base, pending_udp_timer_cb,
1009*b7579f77SDag-Erling Smørgrav 		pend);
1010*b7579f77SDag-Erling Smørgrav 	if(!pend->timer) {
1011*b7579f77SDag-Erling Smørgrav 		free(pend);
1012*b7579f77SDag-Erling Smørgrav 		return NULL;
1013*b7579f77SDag-Erling Smørgrav 	}
1014*b7579f77SDag-Erling Smørgrav 
1015*b7579f77SDag-Erling Smørgrav 	if(outnet->unused_fds == NULL) {
1016*b7579f77SDag-Erling Smørgrav 		/* no unused fd, cannot create a new port (randomly) */
1017*b7579f77SDag-Erling Smørgrav 		verbose(VERB_ALGO, "no fds available, udp query waiting");
1018*b7579f77SDag-Erling Smørgrav 		pend->timeout = timeout;
1019*b7579f77SDag-Erling Smørgrav 		pend->pkt_len = ldns_buffer_limit(packet);
1020*b7579f77SDag-Erling Smørgrav 		pend->pkt = (uint8_t*)memdup(ldns_buffer_begin(packet),
1021*b7579f77SDag-Erling Smørgrav 			pend->pkt_len);
1022*b7579f77SDag-Erling Smørgrav 		if(!pend->pkt) {
1023*b7579f77SDag-Erling Smørgrav 			comm_timer_delete(pend->timer);
1024*b7579f77SDag-Erling Smørgrav 			free(pend);
1025*b7579f77SDag-Erling Smørgrav 			return NULL;
1026*b7579f77SDag-Erling Smørgrav 		}
1027*b7579f77SDag-Erling Smørgrav 		/* put at end of waiting list */
1028*b7579f77SDag-Erling Smørgrav 		if(outnet->udp_wait_last)
1029*b7579f77SDag-Erling Smørgrav 			outnet->udp_wait_last->next_waiting = pend;
1030*b7579f77SDag-Erling Smørgrav 		else
1031*b7579f77SDag-Erling Smørgrav 			outnet->udp_wait_first = pend;
1032*b7579f77SDag-Erling Smørgrav 		outnet->udp_wait_last = pend;
1033*b7579f77SDag-Erling Smørgrav 		return pend;
1034*b7579f77SDag-Erling Smørgrav 	}
1035*b7579f77SDag-Erling Smørgrav 	if(!randomize_and_send_udp(outnet, pend, packet, timeout)) {
1036*b7579f77SDag-Erling Smørgrav 		pending_delete(outnet, pend);
1037*b7579f77SDag-Erling Smørgrav 		return NULL;
1038*b7579f77SDag-Erling Smørgrav 	}
1039*b7579f77SDag-Erling Smørgrav 	return pend;
1040*b7579f77SDag-Erling Smørgrav }
1041*b7579f77SDag-Erling Smørgrav 
1042*b7579f77SDag-Erling Smørgrav void
1043*b7579f77SDag-Erling Smørgrav outnet_tcptimer(void* arg)
1044*b7579f77SDag-Erling Smørgrav {
1045*b7579f77SDag-Erling Smørgrav 	struct waiting_tcp* w = (struct waiting_tcp*)arg;
1046*b7579f77SDag-Erling Smørgrav 	struct outside_network* outnet = w->outnet;
1047*b7579f77SDag-Erling Smørgrav 	comm_point_callback_t* cb;
1048*b7579f77SDag-Erling Smørgrav 	void* cb_arg;
1049*b7579f77SDag-Erling Smørgrav 	if(w->pkt) {
1050*b7579f77SDag-Erling Smørgrav 		/* it is on the waiting list */
1051*b7579f77SDag-Erling Smørgrav 		struct waiting_tcp* p=outnet->tcp_wait_first, *prev=NULL;
1052*b7579f77SDag-Erling Smørgrav 		while(p) {
1053*b7579f77SDag-Erling Smørgrav 			if(p == w) {
1054*b7579f77SDag-Erling Smørgrav 				if(prev) prev->next_waiting = w->next_waiting;
1055*b7579f77SDag-Erling Smørgrav 				else	outnet->tcp_wait_first=w->next_waiting;
1056*b7579f77SDag-Erling Smørgrav 				outnet->tcp_wait_last = prev;
1057*b7579f77SDag-Erling Smørgrav 				break;
1058*b7579f77SDag-Erling Smørgrav 			}
1059*b7579f77SDag-Erling Smørgrav 			prev = p;
1060*b7579f77SDag-Erling Smørgrav 			p=p->next_waiting;
1061*b7579f77SDag-Erling Smørgrav 		}
1062*b7579f77SDag-Erling Smørgrav 	} else {
1063*b7579f77SDag-Erling Smørgrav 		/* it was in use */
1064*b7579f77SDag-Erling Smørgrav 		struct pending_tcp* pend=(struct pending_tcp*)w->next_waiting;
1065*b7579f77SDag-Erling Smørgrav 		comm_point_close(pend->c);
1066*b7579f77SDag-Erling Smørgrav 		pend->query = NULL;
1067*b7579f77SDag-Erling Smørgrav 		pend->next_free = outnet->tcp_free;
1068*b7579f77SDag-Erling Smørgrav 		outnet->tcp_free = pend;
1069*b7579f77SDag-Erling Smørgrav 	}
1070*b7579f77SDag-Erling Smørgrav 	cb = w->cb;
1071*b7579f77SDag-Erling Smørgrav 	cb_arg = w->cb_arg;
1072*b7579f77SDag-Erling Smørgrav 	waiting_tcp_delete(w);
1073*b7579f77SDag-Erling Smørgrav 	fptr_ok(fptr_whitelist_pending_tcp(cb));
1074*b7579f77SDag-Erling Smørgrav 	(void)(*cb)(NULL, cb_arg, NETEVENT_TIMEOUT, NULL);
1075*b7579f77SDag-Erling Smørgrav 	use_free_buffer(outnet);
1076*b7579f77SDag-Erling Smørgrav }
1077*b7579f77SDag-Erling Smørgrav 
1078*b7579f77SDag-Erling Smørgrav struct waiting_tcp*
1079*b7579f77SDag-Erling Smørgrav pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
1080*b7579f77SDag-Erling Smørgrav 	struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
1081*b7579f77SDag-Erling Smørgrav 	comm_point_callback_t* callback, void* callback_arg, int ssl_upstream)
1082*b7579f77SDag-Erling Smørgrav {
1083*b7579f77SDag-Erling Smørgrav 	struct pending_tcp* pend = outnet->tcp_free;
1084*b7579f77SDag-Erling Smørgrav 	struct waiting_tcp* w;
1085*b7579f77SDag-Erling Smørgrav 	struct timeval tv;
1086*b7579f77SDag-Erling Smørgrav 	uint16_t id;
1087*b7579f77SDag-Erling Smørgrav 	/* if no buffer is free allocate space to store query */
1088*b7579f77SDag-Erling Smørgrav 	w = (struct waiting_tcp*)malloc(sizeof(struct waiting_tcp)
1089*b7579f77SDag-Erling Smørgrav 		+ (pend?0:ldns_buffer_limit(packet)));
1090*b7579f77SDag-Erling Smørgrav 	if(!w) {
1091*b7579f77SDag-Erling Smørgrav 		return NULL;
1092*b7579f77SDag-Erling Smørgrav 	}
1093*b7579f77SDag-Erling Smørgrav 	if(!(w->timer = comm_timer_create(outnet->base, outnet_tcptimer, w))) {
1094*b7579f77SDag-Erling Smørgrav 		free(w);
1095*b7579f77SDag-Erling Smørgrav 		return NULL;
1096*b7579f77SDag-Erling Smørgrav 	}
1097*b7579f77SDag-Erling Smørgrav 	w->pkt = NULL;
1098*b7579f77SDag-Erling Smørgrav 	w->pkt_len = 0;
1099*b7579f77SDag-Erling Smørgrav 	id = ((unsigned)ub_random(outnet->rnd)>>8) & 0xffff;
1100*b7579f77SDag-Erling Smørgrav 	LDNS_ID_SET(ldns_buffer_begin(packet), id);
1101*b7579f77SDag-Erling Smørgrav 	memcpy(&w->addr, addr, addrlen);
1102*b7579f77SDag-Erling Smørgrav 	w->addrlen = addrlen;
1103*b7579f77SDag-Erling Smørgrav 	w->outnet = outnet;
1104*b7579f77SDag-Erling Smørgrav 	w->cb = callback;
1105*b7579f77SDag-Erling Smørgrav 	w->cb_arg = callback_arg;
1106*b7579f77SDag-Erling Smørgrav 	w->ssl_upstream = ssl_upstream;
1107*b7579f77SDag-Erling Smørgrav #ifndef S_SPLINT_S
1108*b7579f77SDag-Erling Smørgrav 	tv.tv_sec = timeout;
1109*b7579f77SDag-Erling Smørgrav 	tv.tv_usec = 0;
1110*b7579f77SDag-Erling Smørgrav #endif
1111*b7579f77SDag-Erling Smørgrav 	comm_timer_set(w->timer, &tv);
1112*b7579f77SDag-Erling Smørgrav 	if(pend) {
1113*b7579f77SDag-Erling Smørgrav 		/* we have a buffer available right now */
1114*b7579f77SDag-Erling Smørgrav 		if(!outnet_tcp_take_into_use(w, ldns_buffer_begin(packet),
1115*b7579f77SDag-Erling Smørgrav 			ldns_buffer_limit(packet))) {
1116*b7579f77SDag-Erling Smørgrav 			waiting_tcp_delete(w);
1117*b7579f77SDag-Erling Smørgrav 			return NULL;
1118*b7579f77SDag-Erling Smørgrav 		}
1119*b7579f77SDag-Erling Smørgrav 	} else {
1120*b7579f77SDag-Erling Smørgrav 		/* queue up */
1121*b7579f77SDag-Erling Smørgrav 		w->pkt = (uint8_t*)w + sizeof(struct waiting_tcp);
1122*b7579f77SDag-Erling Smørgrav 		w->pkt_len = ldns_buffer_limit(packet);
1123*b7579f77SDag-Erling Smørgrav 		memmove(w->pkt, ldns_buffer_begin(packet), w->pkt_len);
1124*b7579f77SDag-Erling Smørgrav 		w->next_waiting = NULL;
1125*b7579f77SDag-Erling Smørgrav 		if(outnet->tcp_wait_last)
1126*b7579f77SDag-Erling Smørgrav 			outnet->tcp_wait_last->next_waiting = w;
1127*b7579f77SDag-Erling Smørgrav 		else	outnet->tcp_wait_first = w;
1128*b7579f77SDag-Erling Smørgrav 		outnet->tcp_wait_last = w;
1129*b7579f77SDag-Erling Smørgrav 	}
1130*b7579f77SDag-Erling Smørgrav 	return w;
1131*b7579f77SDag-Erling Smørgrav }
1132*b7579f77SDag-Erling Smørgrav 
1133*b7579f77SDag-Erling Smørgrav /** create query for serviced queries */
1134*b7579f77SDag-Erling Smørgrav static void
1135*b7579f77SDag-Erling Smørgrav serviced_gen_query(ldns_buffer* buff, uint8_t* qname, size_t qnamelen,
1136*b7579f77SDag-Erling Smørgrav 	uint16_t qtype, uint16_t qclass, uint16_t flags)
1137*b7579f77SDag-Erling Smørgrav {
1138*b7579f77SDag-Erling Smørgrav 	ldns_buffer_clear(buff);
1139*b7579f77SDag-Erling Smørgrav 	/* skip id */
1140*b7579f77SDag-Erling Smørgrav 	ldns_buffer_write_u16(buff, flags);
1141*b7579f77SDag-Erling Smørgrav 	ldns_buffer_write_u16(buff, 1); /* qdcount */
1142*b7579f77SDag-Erling Smørgrav 	ldns_buffer_write_u16(buff, 0); /* ancount */
1143*b7579f77SDag-Erling Smørgrav 	ldns_buffer_write_u16(buff, 0); /* nscount */
1144*b7579f77SDag-Erling Smørgrav 	ldns_buffer_write_u16(buff, 0); /* arcount */
1145*b7579f77SDag-Erling Smørgrav 	ldns_buffer_write(buff, qname, qnamelen);
1146*b7579f77SDag-Erling Smørgrav 	ldns_buffer_write_u16(buff, qtype);
1147*b7579f77SDag-Erling Smørgrav 	ldns_buffer_write_u16(buff, qclass);
1148*b7579f77SDag-Erling Smørgrav 	ldns_buffer_flip(buff);
1149*b7579f77SDag-Erling Smørgrav }
1150*b7579f77SDag-Erling Smørgrav 
1151*b7579f77SDag-Erling Smørgrav /** lookup serviced query in serviced query rbtree */
1152*b7579f77SDag-Erling Smørgrav static struct serviced_query*
1153*b7579f77SDag-Erling Smørgrav lookup_serviced(struct outside_network* outnet, ldns_buffer* buff, int dnssec,
1154*b7579f77SDag-Erling Smørgrav 	struct sockaddr_storage* addr, socklen_t addrlen)
1155*b7579f77SDag-Erling Smørgrav {
1156*b7579f77SDag-Erling Smørgrav 	struct serviced_query key;
1157*b7579f77SDag-Erling Smørgrav 	key.node.key = &key;
1158*b7579f77SDag-Erling Smørgrav 	key.qbuf = ldns_buffer_begin(buff);
1159*b7579f77SDag-Erling Smørgrav 	key.qbuflen = ldns_buffer_limit(buff);
1160*b7579f77SDag-Erling Smørgrav 	key.dnssec = dnssec;
1161*b7579f77SDag-Erling Smørgrav 	memcpy(&key.addr, addr, addrlen);
1162*b7579f77SDag-Erling Smørgrav 	key.addrlen = addrlen;
1163*b7579f77SDag-Erling Smørgrav 	key.outnet = outnet;
1164*b7579f77SDag-Erling Smørgrav 	return (struct serviced_query*)rbtree_search(outnet->serviced, &key);
1165*b7579f77SDag-Erling Smørgrav }
1166*b7579f77SDag-Erling Smørgrav 
1167*b7579f77SDag-Erling Smørgrav /** Create new serviced entry */
1168*b7579f77SDag-Erling Smørgrav static struct serviced_query*
1169*b7579f77SDag-Erling Smørgrav serviced_create(struct outside_network* outnet, ldns_buffer* buff, int dnssec,
1170*b7579f77SDag-Erling Smørgrav 	int want_dnssec, int tcp_upstream, int ssl_upstream,
1171*b7579f77SDag-Erling Smørgrav 	struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
1172*b7579f77SDag-Erling Smørgrav 	size_t zonelen, int qtype)
1173*b7579f77SDag-Erling Smørgrav {
1174*b7579f77SDag-Erling Smørgrav 	struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq));
1175*b7579f77SDag-Erling Smørgrav #ifdef UNBOUND_DEBUG
1176*b7579f77SDag-Erling Smørgrav 	rbnode_t* ins;
1177*b7579f77SDag-Erling Smørgrav #endif
1178*b7579f77SDag-Erling Smørgrav 	if(!sq)
1179*b7579f77SDag-Erling Smørgrav 		return NULL;
1180*b7579f77SDag-Erling Smørgrav 	sq->node.key = sq;
1181*b7579f77SDag-Erling Smørgrav 	sq->qbuf = memdup(ldns_buffer_begin(buff), ldns_buffer_limit(buff));
1182*b7579f77SDag-Erling Smørgrav 	if(!sq->qbuf) {
1183*b7579f77SDag-Erling Smørgrav 		free(sq);
1184*b7579f77SDag-Erling Smørgrav 		return NULL;
1185*b7579f77SDag-Erling Smørgrav 	}
1186*b7579f77SDag-Erling Smørgrav 	sq->qbuflen = ldns_buffer_limit(buff);
1187*b7579f77SDag-Erling Smørgrav 	sq->zone = memdup(zone, zonelen);
1188*b7579f77SDag-Erling Smørgrav 	if(!sq->zone) {
1189*b7579f77SDag-Erling Smørgrav 		free(sq->qbuf);
1190*b7579f77SDag-Erling Smørgrav 		free(sq);
1191*b7579f77SDag-Erling Smørgrav 		return NULL;
1192*b7579f77SDag-Erling Smørgrav 	}
1193*b7579f77SDag-Erling Smørgrav 	sq->zonelen = zonelen;
1194*b7579f77SDag-Erling Smørgrav 	sq->qtype = qtype;
1195*b7579f77SDag-Erling Smørgrav 	sq->dnssec = dnssec;
1196*b7579f77SDag-Erling Smørgrav 	sq->want_dnssec = want_dnssec;
1197*b7579f77SDag-Erling Smørgrav 	sq->tcp_upstream = tcp_upstream;
1198*b7579f77SDag-Erling Smørgrav 	sq->ssl_upstream = ssl_upstream;
1199*b7579f77SDag-Erling Smørgrav 	memcpy(&sq->addr, addr, addrlen);
1200*b7579f77SDag-Erling Smørgrav 	sq->addrlen = addrlen;
1201*b7579f77SDag-Erling Smørgrav 	sq->outnet = outnet;
1202*b7579f77SDag-Erling Smørgrav 	sq->cblist = NULL;
1203*b7579f77SDag-Erling Smørgrav 	sq->pending = NULL;
1204*b7579f77SDag-Erling Smørgrav 	sq->status = serviced_initial;
1205*b7579f77SDag-Erling Smørgrav 	sq->retry = 0;
1206*b7579f77SDag-Erling Smørgrav 	sq->to_be_deleted = 0;
1207*b7579f77SDag-Erling Smørgrav #ifdef UNBOUND_DEBUG
1208*b7579f77SDag-Erling Smørgrav 	ins =
1209*b7579f77SDag-Erling Smørgrav #endif
1210*b7579f77SDag-Erling Smørgrav 	rbtree_insert(outnet->serviced, &sq->node);
1211*b7579f77SDag-Erling Smørgrav 	log_assert(ins != NULL); /* must not be already present */
1212*b7579f77SDag-Erling Smørgrav 	return sq;
1213*b7579f77SDag-Erling Smørgrav }
1214*b7579f77SDag-Erling Smørgrav 
1215*b7579f77SDag-Erling Smørgrav /** remove waiting tcp from the outnet waiting list */
1216*b7579f77SDag-Erling Smørgrav static void
1217*b7579f77SDag-Erling Smørgrav waiting_list_remove(struct outside_network* outnet, struct waiting_tcp* w)
1218*b7579f77SDag-Erling Smørgrav {
1219*b7579f77SDag-Erling Smørgrav 	struct waiting_tcp* p = outnet->tcp_wait_first, *prev = NULL;
1220*b7579f77SDag-Erling Smørgrav 	while(p) {
1221*b7579f77SDag-Erling Smørgrav 		if(p == w) {
1222*b7579f77SDag-Erling Smørgrav 			/* remove w */
1223*b7579f77SDag-Erling Smørgrav 			if(prev)
1224*b7579f77SDag-Erling Smørgrav 				prev->next_waiting = w->next_waiting;
1225*b7579f77SDag-Erling Smørgrav 			else	outnet->tcp_wait_first = w->next_waiting;
1226*b7579f77SDag-Erling Smørgrav 			if(outnet->tcp_wait_last == w)
1227*b7579f77SDag-Erling Smørgrav 				outnet->tcp_wait_last = prev;
1228*b7579f77SDag-Erling Smørgrav 			return;
1229*b7579f77SDag-Erling Smørgrav 		}
1230*b7579f77SDag-Erling Smørgrav 		prev = p;
1231*b7579f77SDag-Erling Smørgrav 		p = p->next_waiting;
1232*b7579f77SDag-Erling Smørgrav 	}
1233*b7579f77SDag-Erling Smørgrav }
1234*b7579f77SDag-Erling Smørgrav 
1235*b7579f77SDag-Erling Smørgrav /** cleanup serviced query entry */
1236*b7579f77SDag-Erling Smørgrav static void
1237*b7579f77SDag-Erling Smørgrav serviced_delete(struct serviced_query* sq)
1238*b7579f77SDag-Erling Smørgrav {
1239*b7579f77SDag-Erling Smørgrav 	if(sq->pending) {
1240*b7579f77SDag-Erling Smørgrav 		/* clear up the pending query */
1241*b7579f77SDag-Erling Smørgrav 		if(sq->status == serviced_query_UDP_EDNS ||
1242*b7579f77SDag-Erling Smørgrav 			sq->status == serviced_query_UDP ||
1243*b7579f77SDag-Erling Smørgrav 			sq->status == serviced_query_PROBE_EDNS ||
1244*b7579f77SDag-Erling Smørgrav 			sq->status == serviced_query_UDP_EDNS_FRAG ||
1245*b7579f77SDag-Erling Smørgrav 			sq->status == serviced_query_UDP_EDNS_fallback) {
1246*b7579f77SDag-Erling Smørgrav 			struct pending* p = (struct pending*)sq->pending;
1247*b7579f77SDag-Erling Smørgrav 			if(p->pc)
1248*b7579f77SDag-Erling Smørgrav 				portcomm_loweruse(sq->outnet, p->pc);
1249*b7579f77SDag-Erling Smørgrav 			pending_delete(sq->outnet, p);
1250*b7579f77SDag-Erling Smørgrav 			/* this call can cause reentrant calls back into the
1251*b7579f77SDag-Erling Smørgrav 			 * mesh */
1252*b7579f77SDag-Erling Smørgrav 			outnet_send_wait_udp(sq->outnet);
1253*b7579f77SDag-Erling Smørgrav 		} else {
1254*b7579f77SDag-Erling Smørgrav 			struct waiting_tcp* p = (struct waiting_tcp*)
1255*b7579f77SDag-Erling Smørgrav 				sq->pending;
1256*b7579f77SDag-Erling Smørgrav 			if(p->pkt == NULL) {
1257*b7579f77SDag-Erling Smørgrav 				decomission_pending_tcp(sq->outnet,
1258*b7579f77SDag-Erling Smørgrav 					(struct pending_tcp*)p->next_waiting);
1259*b7579f77SDag-Erling Smørgrav 			} else {
1260*b7579f77SDag-Erling Smørgrav 				waiting_list_remove(sq->outnet, p);
1261*b7579f77SDag-Erling Smørgrav 				waiting_tcp_delete(p);
1262*b7579f77SDag-Erling Smørgrav 			}
1263*b7579f77SDag-Erling Smørgrav 		}
1264*b7579f77SDag-Erling Smørgrav 	}
1265*b7579f77SDag-Erling Smørgrav 	/* does not delete from tree, caller has to do that */
1266*b7579f77SDag-Erling Smørgrav 	serviced_node_del(&sq->node, NULL);
1267*b7579f77SDag-Erling Smørgrav }
1268*b7579f77SDag-Erling Smørgrav 
1269*b7579f77SDag-Erling Smørgrav /** perturb a dname capitalization randomly */
1270*b7579f77SDag-Erling Smørgrav static void
1271*b7579f77SDag-Erling Smørgrav serviced_perturb_qname(struct ub_randstate* rnd, uint8_t* qbuf, size_t len)
1272*b7579f77SDag-Erling Smørgrav {
1273*b7579f77SDag-Erling Smørgrav 	uint8_t lablen;
1274*b7579f77SDag-Erling Smørgrav 	uint8_t* d = qbuf + 10;
1275*b7579f77SDag-Erling Smørgrav 	long int random = 0;
1276*b7579f77SDag-Erling Smørgrav 	int bits = 0;
1277*b7579f77SDag-Erling Smørgrav 	log_assert(len >= 10 + 5 /* offset qname, root, qtype, qclass */);
1278*b7579f77SDag-Erling Smørgrav 	lablen = *d++;
1279*b7579f77SDag-Erling Smørgrav 	while(lablen) {
1280*b7579f77SDag-Erling Smørgrav 		while(lablen--) {
1281*b7579f77SDag-Erling Smørgrav 			/* only perturb A-Z, a-z */
1282*b7579f77SDag-Erling Smørgrav 			if(isalpha((int)*d)) {
1283*b7579f77SDag-Erling Smørgrav 				/* get a random bit */
1284*b7579f77SDag-Erling Smørgrav 				if(bits == 0) {
1285*b7579f77SDag-Erling Smørgrav 					random = ub_random(rnd);
1286*b7579f77SDag-Erling Smørgrav 					bits = 30;
1287*b7579f77SDag-Erling Smørgrav 				}
1288*b7579f77SDag-Erling Smørgrav 				if(random & 0x1) {
1289*b7579f77SDag-Erling Smørgrav 					*d = (uint8_t)toupper((int)*d);
1290*b7579f77SDag-Erling Smørgrav 				} else {
1291*b7579f77SDag-Erling Smørgrav 					*d = (uint8_t)tolower((int)*d);
1292*b7579f77SDag-Erling Smørgrav 				}
1293*b7579f77SDag-Erling Smørgrav 				random >>= 1;
1294*b7579f77SDag-Erling Smørgrav 				bits--;
1295*b7579f77SDag-Erling Smørgrav 			}
1296*b7579f77SDag-Erling Smørgrav 			d++;
1297*b7579f77SDag-Erling Smørgrav 		}
1298*b7579f77SDag-Erling Smørgrav 		lablen = *d++;
1299*b7579f77SDag-Erling Smørgrav 	}
1300*b7579f77SDag-Erling Smørgrav 	if(verbosity >= VERB_ALGO) {
1301*b7579f77SDag-Erling Smørgrav 		char buf[LDNS_MAX_DOMAINLEN+1];
1302*b7579f77SDag-Erling Smørgrav 		dname_str(qbuf+10, buf);
1303*b7579f77SDag-Erling Smørgrav 		verbose(VERB_ALGO, "qname perturbed to %s", buf);
1304*b7579f77SDag-Erling Smørgrav 	}
1305*b7579f77SDag-Erling Smørgrav }
1306*b7579f77SDag-Erling Smørgrav 
1307*b7579f77SDag-Erling Smørgrav /** put serviced query into a buffer */
1308*b7579f77SDag-Erling Smørgrav static void
1309*b7579f77SDag-Erling Smørgrav serviced_encode(struct serviced_query* sq, ldns_buffer* buff, int with_edns)
1310*b7579f77SDag-Erling Smørgrav {
1311*b7579f77SDag-Erling Smørgrav 	/* if we are using 0x20 bits for ID randomness, perturb them */
1312*b7579f77SDag-Erling Smørgrav 	if(sq->outnet->use_caps_for_id) {
1313*b7579f77SDag-Erling Smørgrav 		serviced_perturb_qname(sq->outnet->rnd, sq->qbuf, sq->qbuflen);
1314*b7579f77SDag-Erling Smørgrav 	}
1315*b7579f77SDag-Erling Smørgrav 	/* generate query */
1316*b7579f77SDag-Erling Smørgrav 	ldns_buffer_clear(buff);
1317*b7579f77SDag-Erling Smørgrav 	ldns_buffer_write_u16(buff, 0); /* id placeholder */
1318*b7579f77SDag-Erling Smørgrav 	ldns_buffer_write(buff, sq->qbuf, sq->qbuflen);
1319*b7579f77SDag-Erling Smørgrav 	ldns_buffer_flip(buff);
1320*b7579f77SDag-Erling Smørgrav 	if(with_edns) {
1321*b7579f77SDag-Erling Smørgrav 		/* add edns section */
1322*b7579f77SDag-Erling Smørgrav 		struct edns_data edns;
1323*b7579f77SDag-Erling Smørgrav 		edns.edns_present = 1;
1324*b7579f77SDag-Erling Smørgrav 		edns.ext_rcode = 0;
1325*b7579f77SDag-Erling Smørgrav 		edns.edns_version = EDNS_ADVERTISED_VERSION;
1326*b7579f77SDag-Erling Smørgrav 		if(sq->status == serviced_query_UDP_EDNS_FRAG) {
1327*b7579f77SDag-Erling Smørgrav 			if(addr_is_ip6(&sq->addr, sq->addrlen)) {
1328*b7579f77SDag-Erling Smørgrav 				if(EDNS_FRAG_SIZE_IP6 < EDNS_ADVERTISED_SIZE)
1329*b7579f77SDag-Erling Smørgrav 					edns.udp_size = EDNS_FRAG_SIZE_IP6;
1330*b7579f77SDag-Erling Smørgrav 				else	edns.udp_size = EDNS_ADVERTISED_SIZE;
1331*b7579f77SDag-Erling Smørgrav 			} else {
1332*b7579f77SDag-Erling Smørgrav 				if(EDNS_FRAG_SIZE_IP4 < EDNS_ADVERTISED_SIZE)
1333*b7579f77SDag-Erling Smørgrav 					edns.udp_size = EDNS_FRAG_SIZE_IP4;
1334*b7579f77SDag-Erling Smørgrav 				else	edns.udp_size = EDNS_ADVERTISED_SIZE;
1335*b7579f77SDag-Erling Smørgrav 			}
1336*b7579f77SDag-Erling Smørgrav 		} else {
1337*b7579f77SDag-Erling Smørgrav 			edns.udp_size = EDNS_ADVERTISED_SIZE;
1338*b7579f77SDag-Erling Smørgrav 		}
1339*b7579f77SDag-Erling Smørgrav 		edns.bits = 0;
1340*b7579f77SDag-Erling Smørgrav 		if(sq->dnssec & EDNS_DO)
1341*b7579f77SDag-Erling Smørgrav 			edns.bits = EDNS_DO;
1342*b7579f77SDag-Erling Smørgrav 		if(sq->dnssec & BIT_CD)
1343*b7579f77SDag-Erling Smørgrav 			LDNS_CD_SET(ldns_buffer_begin(buff));
1344*b7579f77SDag-Erling Smørgrav 		attach_edns_record(buff, &edns);
1345*b7579f77SDag-Erling Smørgrav 	}
1346*b7579f77SDag-Erling Smørgrav }
1347*b7579f77SDag-Erling Smørgrav 
1348*b7579f77SDag-Erling Smørgrav /**
1349*b7579f77SDag-Erling Smørgrav  * Perform serviced query UDP sending operation.
1350*b7579f77SDag-Erling Smørgrav  * Sends UDP with EDNS, unless infra host marked non EDNS.
1351*b7579f77SDag-Erling Smørgrav  * @param sq: query to send.
1352*b7579f77SDag-Erling Smørgrav  * @param buff: buffer scratch space.
1353*b7579f77SDag-Erling Smørgrav  * @return 0 on error.
1354*b7579f77SDag-Erling Smørgrav  */
1355*b7579f77SDag-Erling Smørgrav static int
1356*b7579f77SDag-Erling Smørgrav serviced_udp_send(struct serviced_query* sq, ldns_buffer* buff)
1357*b7579f77SDag-Erling Smørgrav {
1358*b7579f77SDag-Erling Smørgrav 	int rtt, vs;
1359*b7579f77SDag-Erling Smørgrav 	uint8_t edns_lame_known;
1360*b7579f77SDag-Erling Smørgrav 	uint32_t now = *sq->outnet->now_secs;
1361*b7579f77SDag-Erling Smørgrav 
1362*b7579f77SDag-Erling Smørgrav 	if(!infra_host(sq->outnet->infra, &sq->addr, sq->addrlen, sq->zone,
1363*b7579f77SDag-Erling Smørgrav 		sq->zonelen, now, &vs, &edns_lame_known, &rtt))
1364*b7579f77SDag-Erling Smørgrav 		return 0;
1365*b7579f77SDag-Erling Smørgrav 	sq->last_rtt = rtt;
1366*b7579f77SDag-Erling Smørgrav 	verbose(VERB_ALGO, "EDNS lookup known=%d vs=%d", edns_lame_known, vs);
1367*b7579f77SDag-Erling Smørgrav 	if(sq->status == serviced_initial) {
1368*b7579f77SDag-Erling Smørgrav 		if(edns_lame_known == 0 && rtt > 5000 && rtt < 10001) {
1369*b7579f77SDag-Erling Smørgrav 			/* perform EDNS lame probe - check if server is
1370*b7579f77SDag-Erling Smørgrav 			 * EDNS lame (EDNS queries to it are dropped) */
1371*b7579f77SDag-Erling Smørgrav 			verbose(VERB_ALGO, "serviced query: send probe to see "
1372*b7579f77SDag-Erling Smørgrav 				" if use of EDNS causes timeouts");
1373*b7579f77SDag-Erling Smørgrav 			/* even 700 msec may be too small */
1374*b7579f77SDag-Erling Smørgrav 			rtt = 1000;
1375*b7579f77SDag-Erling Smørgrav 			sq->status = serviced_query_PROBE_EDNS;
1376*b7579f77SDag-Erling Smørgrav 		} else if(vs != -1) {
1377*b7579f77SDag-Erling Smørgrav 			sq->status = serviced_query_UDP_EDNS;
1378*b7579f77SDag-Erling Smørgrav 		} else {
1379*b7579f77SDag-Erling Smørgrav 			sq->status = serviced_query_UDP;
1380*b7579f77SDag-Erling Smørgrav 		}
1381*b7579f77SDag-Erling Smørgrav 	}
1382*b7579f77SDag-Erling Smørgrav 	serviced_encode(sq, buff, (sq->status == serviced_query_UDP_EDNS) ||
1383*b7579f77SDag-Erling Smørgrav 		(sq->status == serviced_query_UDP_EDNS_FRAG));
1384*b7579f77SDag-Erling Smørgrav 	sq->last_sent_time = *sq->outnet->now_tv;
1385*b7579f77SDag-Erling Smørgrav 	sq->edns_lame_known = (int)edns_lame_known;
1386*b7579f77SDag-Erling Smørgrav 	verbose(VERB_ALGO, "serviced query UDP timeout=%d msec", rtt);
1387*b7579f77SDag-Erling Smørgrav 	sq->pending = pending_udp_query(sq->outnet, buff, &sq->addr,
1388*b7579f77SDag-Erling Smørgrav 		sq->addrlen, rtt, serviced_udp_callback, sq);
1389*b7579f77SDag-Erling Smørgrav 	if(!sq->pending)
1390*b7579f77SDag-Erling Smørgrav 		return 0;
1391*b7579f77SDag-Erling Smørgrav 	return 1;
1392*b7579f77SDag-Erling Smørgrav }
1393*b7579f77SDag-Erling Smørgrav 
1394*b7579f77SDag-Erling Smørgrav /** check that perturbed qname is identical */
1395*b7579f77SDag-Erling Smørgrav static int
1396*b7579f77SDag-Erling Smørgrav serviced_check_qname(ldns_buffer* pkt, uint8_t* qbuf, size_t qbuflen)
1397*b7579f77SDag-Erling Smørgrav {
1398*b7579f77SDag-Erling Smørgrav 	uint8_t* d1 = ldns_buffer_at(pkt, 12);
1399*b7579f77SDag-Erling Smørgrav 	uint8_t* d2 = qbuf+10;
1400*b7579f77SDag-Erling Smørgrav 	uint8_t len1, len2;
1401*b7579f77SDag-Erling Smørgrav 	int count = 0;
1402*b7579f77SDag-Erling Smørgrav 	log_assert(qbuflen >= 15 /* 10 header, root, type, class */);
1403*b7579f77SDag-Erling Smørgrav 	len1 = *d1++;
1404*b7579f77SDag-Erling Smørgrav 	len2 = *d2++;
1405*b7579f77SDag-Erling Smørgrav 	if(ldns_buffer_limit(pkt) < 12+1+4) /* packet too small for qname */
1406*b7579f77SDag-Erling Smørgrav 		return 0;
1407*b7579f77SDag-Erling Smørgrav 	while(len1 != 0 || len2 != 0) {
1408*b7579f77SDag-Erling Smørgrav 		if(LABEL_IS_PTR(len1)) {
1409*b7579f77SDag-Erling Smørgrav 			d1 = ldns_buffer_at(pkt, PTR_OFFSET(len1, *d1));
1410*b7579f77SDag-Erling Smørgrav 			if(d1 >= ldns_buffer_at(pkt, ldns_buffer_limit(pkt)))
1411*b7579f77SDag-Erling Smørgrav 				return 0;
1412*b7579f77SDag-Erling Smørgrav 			len1 = *d1++;
1413*b7579f77SDag-Erling Smørgrav 			if(count++ > MAX_COMPRESS_PTRS)
1414*b7579f77SDag-Erling Smørgrav 				return 0;
1415*b7579f77SDag-Erling Smørgrav 			continue;
1416*b7579f77SDag-Erling Smørgrav 		}
1417*b7579f77SDag-Erling Smørgrav 		if(d2 > qbuf+qbuflen)
1418*b7579f77SDag-Erling Smørgrav 			return 0;
1419*b7579f77SDag-Erling Smørgrav 		if(len1 != len2)
1420*b7579f77SDag-Erling Smørgrav 			return 0;
1421*b7579f77SDag-Erling Smørgrav 		if(len1 > LDNS_MAX_LABELLEN)
1422*b7579f77SDag-Erling Smørgrav 			return 0;
1423*b7579f77SDag-Erling Smørgrav 		log_assert(len1 <= LDNS_MAX_LABELLEN);
1424*b7579f77SDag-Erling Smørgrav 		log_assert(len2 <= LDNS_MAX_LABELLEN);
1425*b7579f77SDag-Erling Smørgrav 		log_assert(len1 == len2 && len1 != 0);
1426*b7579f77SDag-Erling Smørgrav 		/* compare the labels - bitwise identical */
1427*b7579f77SDag-Erling Smørgrav 		if(memcmp(d1, d2, len1) != 0)
1428*b7579f77SDag-Erling Smørgrav 			return 0;
1429*b7579f77SDag-Erling Smørgrav 		d1 += len1;
1430*b7579f77SDag-Erling Smørgrav 		d2 += len2;
1431*b7579f77SDag-Erling Smørgrav 		len1 = *d1++;
1432*b7579f77SDag-Erling Smørgrav 		len2 = *d2++;
1433*b7579f77SDag-Erling Smørgrav 	}
1434*b7579f77SDag-Erling Smørgrav 	return 1;
1435*b7579f77SDag-Erling Smørgrav }
1436*b7579f77SDag-Erling Smørgrav 
1437*b7579f77SDag-Erling Smørgrav /** call the callbacks for a serviced query */
1438*b7579f77SDag-Erling Smørgrav static void
1439*b7579f77SDag-Erling Smørgrav serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c,
1440*b7579f77SDag-Erling Smørgrav 	struct comm_reply* rep)
1441*b7579f77SDag-Erling Smørgrav {
1442*b7579f77SDag-Erling Smørgrav 	struct service_callback* p = sq->cblist, *n;
1443*b7579f77SDag-Erling Smørgrav 	int dobackup = (sq->cblist && sq->cblist->next); /* >1 cb*/
1444*b7579f77SDag-Erling Smørgrav 	uint8_t *backup_p = NULL;
1445*b7579f77SDag-Erling Smørgrav 	size_t backlen = 0;
1446*b7579f77SDag-Erling Smørgrav #ifdef UNBOUND_DEBUG
1447*b7579f77SDag-Erling Smørgrav 	rbnode_t* rem =
1448*b7579f77SDag-Erling Smørgrav #endif
1449*b7579f77SDag-Erling Smørgrav 	/* remove from tree, and schedule for deletion, so that callbacks
1450*b7579f77SDag-Erling Smørgrav 	 * can safely deregister themselves and even create new serviced
1451*b7579f77SDag-Erling Smørgrav 	 * queries that are identical to this one. */
1452*b7579f77SDag-Erling Smørgrav 	rbtree_delete(sq->outnet->serviced, sq);
1453*b7579f77SDag-Erling Smørgrav 	log_assert(rem); /* should have been present */
1454*b7579f77SDag-Erling Smørgrav 	sq->to_be_deleted = 1;
1455*b7579f77SDag-Erling Smørgrav 	verbose(VERB_ALGO, "svcd callbacks start");
1456*b7579f77SDag-Erling Smørgrav 	if(sq->outnet->use_caps_for_id && error == NETEVENT_NOERROR && c) {
1457*b7579f77SDag-Erling Smørgrav 		/* noerror and nxdomain must have a qname in reply */
1458*b7579f77SDag-Erling Smørgrav 		if(ldns_buffer_read_u16_at(c->buffer, 4) == 0 &&
1459*b7579f77SDag-Erling Smørgrav 			(LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer))
1460*b7579f77SDag-Erling Smørgrav 				== LDNS_RCODE_NOERROR ||
1461*b7579f77SDag-Erling Smørgrav 			 LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer))
1462*b7579f77SDag-Erling Smørgrav 				== LDNS_RCODE_NXDOMAIN)) {
1463*b7579f77SDag-Erling Smørgrav 			verbose(VERB_DETAIL, "no qname in reply to check 0x20ID");
1464*b7579f77SDag-Erling Smørgrav 			log_addr(VERB_DETAIL, "from server",
1465*b7579f77SDag-Erling Smørgrav 				&sq->addr, sq->addrlen);
1466*b7579f77SDag-Erling Smørgrav 			log_buf(VERB_DETAIL, "for packet", c->buffer);
1467*b7579f77SDag-Erling Smørgrav 			error = NETEVENT_CLOSED;
1468*b7579f77SDag-Erling Smørgrav 			c = NULL;
1469*b7579f77SDag-Erling Smørgrav 		} else if(ldns_buffer_read_u16_at(c->buffer, 4) > 0 &&
1470*b7579f77SDag-Erling Smørgrav 			!serviced_check_qname(c->buffer, sq->qbuf,
1471*b7579f77SDag-Erling Smørgrav 			sq->qbuflen)) {
1472*b7579f77SDag-Erling Smørgrav 			verbose(VERB_DETAIL, "wrong 0x20-ID in reply qname");
1473*b7579f77SDag-Erling Smørgrav 			log_addr(VERB_DETAIL, "from server",
1474*b7579f77SDag-Erling Smørgrav 				&sq->addr, sq->addrlen);
1475*b7579f77SDag-Erling Smørgrav 			log_buf(VERB_DETAIL, "for packet", c->buffer);
1476*b7579f77SDag-Erling Smørgrav 			error = NETEVENT_CAPSFAIL;
1477*b7579f77SDag-Erling Smørgrav 			/* and cleanup too */
1478*b7579f77SDag-Erling Smørgrav 			pkt_dname_tolower(c->buffer,
1479*b7579f77SDag-Erling Smørgrav 				ldns_buffer_at(c->buffer, 12));
1480*b7579f77SDag-Erling Smørgrav 		} else {
1481*b7579f77SDag-Erling Smørgrav 			verbose(VERB_ALGO, "good 0x20-ID in reply qname");
1482*b7579f77SDag-Erling Smørgrav 			/* cleanup caps, prettier cache contents. */
1483*b7579f77SDag-Erling Smørgrav 			pkt_dname_tolower(c->buffer,
1484*b7579f77SDag-Erling Smørgrav 				ldns_buffer_at(c->buffer, 12));
1485*b7579f77SDag-Erling Smørgrav 		}
1486*b7579f77SDag-Erling Smørgrav 	}
1487*b7579f77SDag-Erling Smørgrav 	if(dobackup && c) {
1488*b7579f77SDag-Erling Smørgrav 		/* make a backup of the query, since the querystate processing
1489*b7579f77SDag-Erling Smørgrav 		 * may send outgoing queries that overwrite the buffer.
1490*b7579f77SDag-Erling Smørgrav 		 * use secondary buffer to store the query.
1491*b7579f77SDag-Erling Smørgrav 		 * This is a data copy, but faster than packet to server */
1492*b7579f77SDag-Erling Smørgrav 		backlen = ldns_buffer_limit(c->buffer);
1493*b7579f77SDag-Erling Smørgrav 		backup_p = memdup(ldns_buffer_begin(c->buffer), backlen);
1494*b7579f77SDag-Erling Smørgrav 		if(!backup_p) {
1495*b7579f77SDag-Erling Smørgrav 			log_err("malloc failure in serviced query callbacks");
1496*b7579f77SDag-Erling Smørgrav 			error = NETEVENT_CLOSED;
1497*b7579f77SDag-Erling Smørgrav 			c = NULL;
1498*b7579f77SDag-Erling Smørgrav 		}
1499*b7579f77SDag-Erling Smørgrav 		sq->outnet->svcd_overhead = backlen;
1500*b7579f77SDag-Erling Smørgrav 	}
1501*b7579f77SDag-Erling Smørgrav 	while(p) {
1502*b7579f77SDag-Erling Smørgrav 		n = p->next;
1503*b7579f77SDag-Erling Smørgrav 		if(dobackup && c) {
1504*b7579f77SDag-Erling Smørgrav 			ldns_buffer_clear(c->buffer);
1505*b7579f77SDag-Erling Smørgrav 			ldns_buffer_write(c->buffer, backup_p, backlen);
1506*b7579f77SDag-Erling Smørgrav 			ldns_buffer_flip(c->buffer);
1507*b7579f77SDag-Erling Smørgrav 		}
1508*b7579f77SDag-Erling Smørgrav 		fptr_ok(fptr_whitelist_serviced_query(p->cb));
1509*b7579f77SDag-Erling Smørgrav 		(void)(*p->cb)(c, p->cb_arg, error, rep);
1510*b7579f77SDag-Erling Smørgrav 		p = n;
1511*b7579f77SDag-Erling Smørgrav 	}
1512*b7579f77SDag-Erling Smørgrav 	if(backup_p) {
1513*b7579f77SDag-Erling Smørgrav 		free(backup_p);
1514*b7579f77SDag-Erling Smørgrav 		sq->outnet->svcd_overhead = 0;
1515*b7579f77SDag-Erling Smørgrav 	}
1516*b7579f77SDag-Erling Smørgrav 	verbose(VERB_ALGO, "svcd callbacks end");
1517*b7579f77SDag-Erling Smørgrav 	log_assert(sq->cblist == NULL);
1518*b7579f77SDag-Erling Smørgrav 	serviced_delete(sq);
1519*b7579f77SDag-Erling Smørgrav }
1520*b7579f77SDag-Erling Smørgrav 
1521*b7579f77SDag-Erling Smørgrav int
1522*b7579f77SDag-Erling Smørgrav serviced_tcp_callback(struct comm_point* c, void* arg, int error,
1523*b7579f77SDag-Erling Smørgrav         struct comm_reply* rep)
1524*b7579f77SDag-Erling Smørgrav {
1525*b7579f77SDag-Erling Smørgrav 	struct serviced_query* sq = (struct serviced_query*)arg;
1526*b7579f77SDag-Erling Smørgrav 	struct comm_reply r2;
1527*b7579f77SDag-Erling Smørgrav 	sq->pending = NULL; /* removed after this callback */
1528*b7579f77SDag-Erling Smørgrav 	if(error != NETEVENT_NOERROR)
1529*b7579f77SDag-Erling Smørgrav 		log_addr(VERB_QUERY, "tcp error for address",
1530*b7579f77SDag-Erling Smørgrav 			&sq->addr, sq->addrlen);
1531*b7579f77SDag-Erling Smørgrav 	if(error==NETEVENT_NOERROR)
1532*b7579f77SDag-Erling Smørgrav 		infra_update_tcp_works(sq->outnet->infra, &sq->addr,
1533*b7579f77SDag-Erling Smørgrav 			sq->addrlen, sq->zone, sq->zonelen);
1534*b7579f77SDag-Erling Smørgrav 	if(error==NETEVENT_NOERROR && sq->status == serviced_query_TCP_EDNS &&
1535*b7579f77SDag-Erling Smørgrav 		(LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) ==
1536*b7579f77SDag-Erling Smørgrav 		LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE(ldns_buffer_begin(
1537*b7579f77SDag-Erling Smørgrav 		c->buffer)) == LDNS_RCODE_NOTIMPL) ) {
1538*b7579f77SDag-Erling Smørgrav 		/* attempt to fallback to nonEDNS */
1539*b7579f77SDag-Erling Smørgrav 		sq->status = serviced_query_TCP_EDNS_fallback;
1540*b7579f77SDag-Erling Smørgrav 		serviced_tcp_initiate(sq->outnet, sq, c->buffer);
1541*b7579f77SDag-Erling Smørgrav 		return 0;
1542*b7579f77SDag-Erling Smørgrav 	} else if(error==NETEVENT_NOERROR &&
1543*b7579f77SDag-Erling Smørgrav 		sq->status == serviced_query_TCP_EDNS_fallback &&
1544*b7579f77SDag-Erling Smørgrav 			(LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) ==
1545*b7579f77SDag-Erling Smørgrav 			LDNS_RCODE_NOERROR || LDNS_RCODE_WIRE(
1546*b7579f77SDag-Erling Smørgrav 			ldns_buffer_begin(c->buffer)) == LDNS_RCODE_NXDOMAIN
1547*b7579f77SDag-Erling Smørgrav 			|| LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer))
1548*b7579f77SDag-Erling Smørgrav 			== LDNS_RCODE_YXDOMAIN)) {
1549*b7579f77SDag-Erling Smørgrav 		/* the fallback produced a result that looks promising, note
1550*b7579f77SDag-Erling Smørgrav 		 * that this server should be approached without EDNS */
1551*b7579f77SDag-Erling Smørgrav 		/* only store noEDNS in cache if domain is noDNSSEC */
1552*b7579f77SDag-Erling Smørgrav 		if(!sq->want_dnssec)
1553*b7579f77SDag-Erling Smørgrav 		  if(!infra_edns_update(sq->outnet->infra, &sq->addr,
1554*b7579f77SDag-Erling Smørgrav 			sq->addrlen, sq->zone, sq->zonelen, -1,
1555*b7579f77SDag-Erling Smørgrav 			*sq->outnet->now_secs))
1556*b7579f77SDag-Erling Smørgrav 			log_err("Out of memory caching no edns for host");
1557*b7579f77SDag-Erling Smørgrav 		sq->status = serviced_query_TCP;
1558*b7579f77SDag-Erling Smørgrav 	}
1559*b7579f77SDag-Erling Smørgrav 	if(sq->tcp_upstream || sq->ssl_upstream) {
1560*b7579f77SDag-Erling Smørgrav 	    struct timeval now = *sq->outnet->now_tv;
1561*b7579f77SDag-Erling Smørgrav 	    if(now.tv_sec > sq->last_sent_time.tv_sec ||
1562*b7579f77SDag-Erling Smørgrav 		(now.tv_sec == sq->last_sent_time.tv_sec &&
1563*b7579f77SDag-Erling Smørgrav 		now.tv_usec > sq->last_sent_time.tv_usec)) {
1564*b7579f77SDag-Erling Smørgrav 		/* convert from microseconds to milliseconds */
1565*b7579f77SDag-Erling Smørgrav 		int roundtime = ((int)now.tv_sec - (int)sq->last_sent_time.tv_sec)*1000
1566*b7579f77SDag-Erling Smørgrav 		  + ((int)now.tv_usec - (int)sq->last_sent_time.tv_usec)/1000;
1567*b7579f77SDag-Erling Smørgrav 		verbose(VERB_ALGO, "measured TCP-time at %d msec", roundtime);
1568*b7579f77SDag-Erling Smørgrav 		log_assert(roundtime >= 0);
1569*b7579f77SDag-Erling Smørgrav 		/* only store if less then AUTH_TIMEOUT seconds, it could be
1570*b7579f77SDag-Erling Smørgrav 		 * huge due to system-hibernated and we woke up */
1571*b7579f77SDag-Erling Smørgrav 		if(roundtime < TCP_AUTH_QUERY_TIMEOUT*1000) {
1572*b7579f77SDag-Erling Smørgrav 		    if(!infra_rtt_update(sq->outnet->infra, &sq->addr,
1573*b7579f77SDag-Erling Smørgrav 			sq->addrlen, sq->zone, sq->zonelen, sq->qtype,
1574*b7579f77SDag-Erling Smørgrav 			roundtime, sq->last_rtt, (uint32_t)now.tv_sec))
1575*b7579f77SDag-Erling Smørgrav 			log_err("out of memory noting rtt.");
1576*b7579f77SDag-Erling Smørgrav 		}
1577*b7579f77SDag-Erling Smørgrav 	    }
1578*b7579f77SDag-Erling Smørgrav 	}
1579*b7579f77SDag-Erling Smørgrav 	/* insert address into reply info */
1580*b7579f77SDag-Erling Smørgrav 	if(!rep) {
1581*b7579f77SDag-Erling Smørgrav 		/* create one if there isn't (on errors) */
1582*b7579f77SDag-Erling Smørgrav 		rep = &r2;
1583*b7579f77SDag-Erling Smørgrav 		r2.c = c;
1584*b7579f77SDag-Erling Smørgrav 	}
1585*b7579f77SDag-Erling Smørgrav 	memcpy(&rep->addr, &sq->addr, sq->addrlen);
1586*b7579f77SDag-Erling Smørgrav 	rep->addrlen = sq->addrlen;
1587*b7579f77SDag-Erling Smørgrav 	serviced_callbacks(sq, error, c, rep);
1588*b7579f77SDag-Erling Smørgrav 	return 0;
1589*b7579f77SDag-Erling Smørgrav }
1590*b7579f77SDag-Erling Smørgrav 
1591*b7579f77SDag-Erling Smørgrav static void
1592*b7579f77SDag-Erling Smørgrav serviced_tcp_initiate(struct outside_network* outnet,
1593*b7579f77SDag-Erling Smørgrav 	struct serviced_query* sq, ldns_buffer* buff)
1594*b7579f77SDag-Erling Smørgrav {
1595*b7579f77SDag-Erling Smørgrav 	verbose(VERB_ALGO, "initiate TCP query %s",
1596*b7579f77SDag-Erling Smørgrav 		sq->status==serviced_query_TCP_EDNS?"EDNS":"");
1597*b7579f77SDag-Erling Smørgrav 	serviced_encode(sq, buff, sq->status == serviced_query_TCP_EDNS);
1598*b7579f77SDag-Erling Smørgrav 	sq->last_sent_time = *sq->outnet->now_tv;
1599*b7579f77SDag-Erling Smørgrav 	sq->pending = pending_tcp_query(outnet, buff, &sq->addr,
1600*b7579f77SDag-Erling Smørgrav 		sq->addrlen, TCP_AUTH_QUERY_TIMEOUT, serviced_tcp_callback,
1601*b7579f77SDag-Erling Smørgrav 		sq, sq->ssl_upstream);
1602*b7579f77SDag-Erling Smørgrav 	if(!sq->pending) {
1603*b7579f77SDag-Erling Smørgrav 		/* delete from tree so that a retry by above layer does not
1604*b7579f77SDag-Erling Smørgrav 		 * clash with this entry */
1605*b7579f77SDag-Erling Smørgrav 		log_err("serviced_tcp_initiate: failed to send tcp query");
1606*b7579f77SDag-Erling Smørgrav 		serviced_callbacks(sq, NETEVENT_CLOSED, NULL, NULL);
1607*b7579f77SDag-Erling Smørgrav 	}
1608*b7579f77SDag-Erling Smørgrav }
1609*b7579f77SDag-Erling Smørgrav 
1610*b7579f77SDag-Erling Smørgrav /** Send serviced query over TCP return false on initial failure */
1611*b7579f77SDag-Erling Smørgrav static int
1612*b7579f77SDag-Erling Smørgrav serviced_tcp_send(struct serviced_query* sq, ldns_buffer* buff)
1613*b7579f77SDag-Erling Smørgrav {
1614*b7579f77SDag-Erling Smørgrav 	int vs, rtt;
1615*b7579f77SDag-Erling Smørgrav 	uint8_t edns_lame_known;
1616*b7579f77SDag-Erling Smørgrav 	if(!infra_host(sq->outnet->infra, &sq->addr, sq->addrlen, sq->zone,
1617*b7579f77SDag-Erling Smørgrav 		sq->zonelen, *sq->outnet->now_secs, &vs, &edns_lame_known,
1618*b7579f77SDag-Erling Smørgrav 		&rtt))
1619*b7579f77SDag-Erling Smørgrav 		return 0;
1620*b7579f77SDag-Erling Smørgrav 	if(vs != -1)
1621*b7579f77SDag-Erling Smørgrav 		sq->status = serviced_query_TCP_EDNS;
1622*b7579f77SDag-Erling Smørgrav 	else 	sq->status = serviced_query_TCP;
1623*b7579f77SDag-Erling Smørgrav 	serviced_encode(sq, buff, sq->status == serviced_query_TCP_EDNS);
1624*b7579f77SDag-Erling Smørgrav 	sq->last_sent_time = *sq->outnet->now_tv;
1625*b7579f77SDag-Erling Smørgrav 	sq->pending = pending_tcp_query(sq->outnet, buff, &sq->addr,
1626*b7579f77SDag-Erling Smørgrav 		sq->addrlen, TCP_AUTH_QUERY_TIMEOUT, serviced_tcp_callback,
1627*b7579f77SDag-Erling Smørgrav 		sq, sq->ssl_upstream);
1628*b7579f77SDag-Erling Smørgrav 	return sq->pending != NULL;
1629*b7579f77SDag-Erling Smørgrav }
1630*b7579f77SDag-Erling Smørgrav 
1631*b7579f77SDag-Erling Smørgrav int
1632*b7579f77SDag-Erling Smørgrav serviced_udp_callback(struct comm_point* c, void* arg, int error,
1633*b7579f77SDag-Erling Smørgrav         struct comm_reply* rep)
1634*b7579f77SDag-Erling Smørgrav {
1635*b7579f77SDag-Erling Smørgrav 	struct serviced_query* sq = (struct serviced_query*)arg;
1636*b7579f77SDag-Erling Smørgrav 	struct outside_network* outnet = sq->outnet;
1637*b7579f77SDag-Erling Smørgrav 	struct timeval now = *sq->outnet->now_tv;
1638*b7579f77SDag-Erling Smørgrav 	int fallback_tcp = 0;
1639*b7579f77SDag-Erling Smørgrav 
1640*b7579f77SDag-Erling Smørgrav 	sq->pending = NULL; /* removed after callback */
1641*b7579f77SDag-Erling Smørgrav 	if(error == NETEVENT_TIMEOUT) {
1642*b7579f77SDag-Erling Smørgrav 		int rto = 0;
1643*b7579f77SDag-Erling Smørgrav 		if(sq->status == serviced_query_PROBE_EDNS) {
1644*b7579f77SDag-Erling Smørgrav 			/* non-EDNS probe failed; we do not know its status,
1645*b7579f77SDag-Erling Smørgrav 			 * keep trying with EDNS, timeout may not be caused
1646*b7579f77SDag-Erling Smørgrav 			 * by EDNS. */
1647*b7579f77SDag-Erling Smørgrav 			sq->status = serviced_query_UDP_EDNS;
1648*b7579f77SDag-Erling Smørgrav 		}
1649*b7579f77SDag-Erling Smørgrav 		if(sq->status == serviced_query_UDP_EDNS && sq->last_rtt < 5000) {
1650*b7579f77SDag-Erling Smørgrav 			/* fallback to 1480/1280 */
1651*b7579f77SDag-Erling Smørgrav 			sq->status = serviced_query_UDP_EDNS_FRAG;
1652*b7579f77SDag-Erling Smørgrav 			log_name_addr(VERB_ALGO, "try edns1xx0", sq->qbuf+10,
1653*b7579f77SDag-Erling Smørgrav 				&sq->addr, sq->addrlen);
1654*b7579f77SDag-Erling Smørgrav 			if(!serviced_udp_send(sq, c->buffer)) {
1655*b7579f77SDag-Erling Smørgrav 				serviced_callbacks(sq, NETEVENT_CLOSED, c, rep);
1656*b7579f77SDag-Erling Smørgrav 			}
1657*b7579f77SDag-Erling Smørgrav 			return 0;
1658*b7579f77SDag-Erling Smørgrav 		}
1659*b7579f77SDag-Erling Smørgrav 		if(sq->status == serviced_query_UDP_EDNS_FRAG) {
1660*b7579f77SDag-Erling Smørgrav 			/* fragmentation size did not fix it */
1661*b7579f77SDag-Erling Smørgrav 			sq->status = serviced_query_UDP_EDNS;
1662*b7579f77SDag-Erling Smørgrav 		}
1663*b7579f77SDag-Erling Smørgrav 		sq->retry++;
1664*b7579f77SDag-Erling Smørgrav 		if(!(rto=infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen,
1665*b7579f77SDag-Erling Smørgrav 			sq->zone, sq->zonelen, sq->qtype, -1, sq->last_rtt,
1666*b7579f77SDag-Erling Smørgrav 			(uint32_t)now.tv_sec)))
1667*b7579f77SDag-Erling Smørgrav 			log_err("out of memory in UDP exponential backoff");
1668*b7579f77SDag-Erling Smørgrav 		if(sq->retry < OUTBOUND_UDP_RETRY) {
1669*b7579f77SDag-Erling Smørgrav 			log_name_addr(VERB_ALGO, "retry query", sq->qbuf+10,
1670*b7579f77SDag-Erling Smørgrav 				&sq->addr, sq->addrlen);
1671*b7579f77SDag-Erling Smørgrav 			if(!serviced_udp_send(sq, c->buffer)) {
1672*b7579f77SDag-Erling Smørgrav 				serviced_callbacks(sq, NETEVENT_CLOSED, c, rep);
1673*b7579f77SDag-Erling Smørgrav 			}
1674*b7579f77SDag-Erling Smørgrav 			return 0;
1675*b7579f77SDag-Erling Smørgrav 		}
1676*b7579f77SDag-Erling Smørgrav 		if(rto >= RTT_MAX_TIMEOUT) {
1677*b7579f77SDag-Erling Smørgrav 			fallback_tcp = 1;
1678*b7579f77SDag-Erling Smørgrav 			/* UDP does not work, fallback to TCP below */
1679*b7579f77SDag-Erling Smørgrav 		} else {
1680*b7579f77SDag-Erling Smørgrav 			serviced_callbacks(sq, NETEVENT_TIMEOUT, c, rep);
1681*b7579f77SDag-Erling Smørgrav 			return 0;
1682*b7579f77SDag-Erling Smørgrav 		}
1683*b7579f77SDag-Erling Smørgrav 	} else if(error != NETEVENT_NOERROR) {
1684*b7579f77SDag-Erling Smørgrav 		/* udp returns error (due to no ID or interface available) */
1685*b7579f77SDag-Erling Smørgrav 		serviced_callbacks(sq, error, c, rep);
1686*b7579f77SDag-Erling Smørgrav 		return 0;
1687*b7579f77SDag-Erling Smørgrav 	}
1688*b7579f77SDag-Erling Smørgrav 	if(!fallback_tcp) {
1689*b7579f77SDag-Erling Smørgrav 	    if( (sq->status == serviced_query_UDP_EDNS
1690*b7579f77SDag-Erling Smørgrav 	        ||sq->status == serviced_query_UDP_EDNS_FRAG)
1691*b7579f77SDag-Erling Smørgrav 		&& (LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer))
1692*b7579f77SDag-Erling Smørgrav 			== LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE(
1693*b7579f77SDag-Erling Smørgrav 			ldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOTIMPL)) {
1694*b7579f77SDag-Erling Smørgrav 		/* try to get an answer by falling back without EDNS */
1695*b7579f77SDag-Erling Smørgrav 		verbose(VERB_ALGO, "serviced query: attempt without EDNS");
1696*b7579f77SDag-Erling Smørgrav 		sq->status = serviced_query_UDP_EDNS_fallback;
1697*b7579f77SDag-Erling Smørgrav 		sq->retry = 0;
1698*b7579f77SDag-Erling Smørgrav 		if(!serviced_udp_send(sq, c->buffer)) {
1699*b7579f77SDag-Erling Smørgrav 			serviced_callbacks(sq, NETEVENT_CLOSED, c, rep);
1700*b7579f77SDag-Erling Smørgrav 		}
1701*b7579f77SDag-Erling Smørgrav 		return 0;
1702*b7579f77SDag-Erling Smørgrav 	    } else if(sq->status == serviced_query_PROBE_EDNS) {
1703*b7579f77SDag-Erling Smørgrav 		/* probe without EDNS succeeds, so we conclude that this
1704*b7579f77SDag-Erling Smørgrav 		 * host likely has EDNS packets dropped */
1705*b7579f77SDag-Erling Smørgrav 		log_addr(VERB_DETAIL, "timeouts, concluded that connection to "
1706*b7579f77SDag-Erling Smørgrav 			"host drops EDNS packets", &sq->addr, sq->addrlen);
1707*b7579f77SDag-Erling Smørgrav 		/* only store noEDNS in cache if domain is noDNSSEC */
1708*b7579f77SDag-Erling Smørgrav 		if(!sq->want_dnssec)
1709*b7579f77SDag-Erling Smørgrav 		  if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen,
1710*b7579f77SDag-Erling Smørgrav 			sq->zone, sq->zonelen, -1, (uint32_t)now.tv_sec)) {
1711*b7579f77SDag-Erling Smørgrav 			log_err("Out of memory caching no edns for host");
1712*b7579f77SDag-Erling Smørgrav 		  }
1713*b7579f77SDag-Erling Smørgrav 		sq->status = serviced_query_UDP;
1714*b7579f77SDag-Erling Smørgrav 	    } else if(sq->status == serviced_query_UDP_EDNS &&
1715*b7579f77SDag-Erling Smørgrav 		!sq->edns_lame_known) {
1716*b7579f77SDag-Erling Smørgrav 		/* now we know that edns queries received answers store that */
1717*b7579f77SDag-Erling Smørgrav 		log_addr(VERB_ALGO, "serviced query: EDNS works for",
1718*b7579f77SDag-Erling Smørgrav 			&sq->addr, sq->addrlen);
1719*b7579f77SDag-Erling Smørgrav 		if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen,
1720*b7579f77SDag-Erling Smørgrav 			sq->zone, sq->zonelen, 0, (uint32_t)now.tv_sec)) {
1721*b7579f77SDag-Erling Smørgrav 			log_err("Out of memory caching edns works");
1722*b7579f77SDag-Erling Smørgrav 		}
1723*b7579f77SDag-Erling Smørgrav 		sq->edns_lame_known = 1;
1724*b7579f77SDag-Erling Smørgrav 	    } else if(sq->status == serviced_query_UDP_EDNS_fallback &&
1725*b7579f77SDag-Erling Smørgrav 		!sq->edns_lame_known && (LDNS_RCODE_WIRE(
1726*b7579f77SDag-Erling Smørgrav 		ldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOERROR ||
1727*b7579f77SDag-Erling Smørgrav 		LDNS_RCODE_WIRE(ldns_buffer_begin(c->buffer)) ==
1728*b7579f77SDag-Erling Smørgrav 		LDNS_RCODE_NXDOMAIN || LDNS_RCODE_WIRE(ldns_buffer_begin(
1729*b7579f77SDag-Erling Smørgrav 		c->buffer)) == LDNS_RCODE_YXDOMAIN)) {
1730*b7579f77SDag-Erling Smørgrav 		/* the fallback produced a result that looks promising, note
1731*b7579f77SDag-Erling Smørgrav 		 * that this server should be approached without EDNS */
1732*b7579f77SDag-Erling Smørgrav 		/* only store noEDNS in cache if domain is noDNSSEC */
1733*b7579f77SDag-Erling Smørgrav 		if(!sq->want_dnssec) {
1734*b7579f77SDag-Erling Smørgrav 		  log_addr(VERB_ALGO, "serviced query: EDNS fails for",
1735*b7579f77SDag-Erling Smørgrav 			&sq->addr, sq->addrlen);
1736*b7579f77SDag-Erling Smørgrav 		  if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen,
1737*b7579f77SDag-Erling Smørgrav 			sq->zone, sq->zonelen, -1, (uint32_t)now.tv_sec)) {
1738*b7579f77SDag-Erling Smørgrav 			log_err("Out of memory caching no edns for host");
1739*b7579f77SDag-Erling Smørgrav 		  }
1740*b7579f77SDag-Erling Smørgrav 		} else {
1741*b7579f77SDag-Erling Smørgrav 		  log_addr(VERB_ALGO, "serviced query: EDNS fails, but "
1742*b7579f77SDag-Erling Smørgrav 		  	"not stored because need DNSSEC for", &sq->addr,
1743*b7579f77SDag-Erling Smørgrav 			sq->addrlen);
1744*b7579f77SDag-Erling Smørgrav 		}
1745*b7579f77SDag-Erling Smørgrav 		sq->status = serviced_query_UDP;
1746*b7579f77SDag-Erling Smørgrav 	    }
1747*b7579f77SDag-Erling Smørgrav 	    if(now.tv_sec > sq->last_sent_time.tv_sec ||
1748*b7579f77SDag-Erling Smørgrav 		(now.tv_sec == sq->last_sent_time.tv_sec &&
1749*b7579f77SDag-Erling Smørgrav 		now.tv_usec > sq->last_sent_time.tv_usec)) {
1750*b7579f77SDag-Erling Smørgrav 		/* convert from microseconds to milliseconds */
1751*b7579f77SDag-Erling Smørgrav 		int roundtime = ((int)now.tv_sec - (int)sq->last_sent_time.tv_sec)*1000
1752*b7579f77SDag-Erling Smørgrav 		  + ((int)now.tv_usec - (int)sq->last_sent_time.tv_usec)/1000;
1753*b7579f77SDag-Erling Smørgrav 		verbose(VERB_ALGO, "measured roundtrip at %d msec", roundtime);
1754*b7579f77SDag-Erling Smørgrav 		log_assert(roundtime >= 0);
1755*b7579f77SDag-Erling Smørgrav 		/* in case the system hibernated, do not enter a huge value,
1756*b7579f77SDag-Erling Smørgrav 		 * above this value gives trouble with server selection */
1757*b7579f77SDag-Erling Smørgrav 		if(roundtime < 60000) {
1758*b7579f77SDag-Erling Smørgrav 		    if(!infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen,
1759*b7579f77SDag-Erling Smørgrav 			sq->zone, sq->zonelen, sq->qtype, roundtime,
1760*b7579f77SDag-Erling Smørgrav 			sq->last_rtt, (uint32_t)now.tv_sec))
1761*b7579f77SDag-Erling Smørgrav 			log_err("out of memory noting rtt.");
1762*b7579f77SDag-Erling Smørgrav 		}
1763*b7579f77SDag-Erling Smørgrav 	    }
1764*b7579f77SDag-Erling Smørgrav 	} /* end of if_!fallback_tcp */
1765*b7579f77SDag-Erling Smørgrav 	/* perform TC flag check and TCP fallback after updating our
1766*b7579f77SDag-Erling Smørgrav 	 * cache entries for EDNS status and RTT times */
1767*b7579f77SDag-Erling Smørgrav 	if(LDNS_TC_WIRE(ldns_buffer_begin(c->buffer)) || fallback_tcp) {
1768*b7579f77SDag-Erling Smørgrav 		/* fallback to TCP */
1769*b7579f77SDag-Erling Smørgrav 		/* this discards partial UDP contents */
1770*b7579f77SDag-Erling Smørgrav 		if(sq->status == serviced_query_UDP_EDNS ||
1771*b7579f77SDag-Erling Smørgrav 			sq->status == serviced_query_UDP_EDNS_FRAG ||
1772*b7579f77SDag-Erling Smørgrav 			sq->status == serviced_query_UDP_EDNS_fallback)
1773*b7579f77SDag-Erling Smørgrav 			/* if we have unfinished EDNS_fallback, start again */
1774*b7579f77SDag-Erling Smørgrav 			sq->status = serviced_query_TCP_EDNS;
1775*b7579f77SDag-Erling Smørgrav 		else	sq->status = serviced_query_TCP;
1776*b7579f77SDag-Erling Smørgrav 		serviced_tcp_initiate(outnet, sq, c->buffer);
1777*b7579f77SDag-Erling Smørgrav 		return 0;
1778*b7579f77SDag-Erling Smørgrav 	}
1779*b7579f77SDag-Erling Smørgrav 	/* yay! an answer */
1780*b7579f77SDag-Erling Smørgrav 	serviced_callbacks(sq, error, c, rep);
1781*b7579f77SDag-Erling Smørgrav 	return 0;
1782*b7579f77SDag-Erling Smørgrav }
1783*b7579f77SDag-Erling Smørgrav 
1784*b7579f77SDag-Erling Smørgrav /** find callback in list */
1785*b7579f77SDag-Erling Smørgrav static struct service_callback*
1786*b7579f77SDag-Erling Smørgrav callback_list_find(struct serviced_query* sq, void* cb_arg,
1787*b7579f77SDag-Erling Smørgrav 	int (*arg_compare)(void*,void*))
1788*b7579f77SDag-Erling Smørgrav {
1789*b7579f77SDag-Erling Smørgrav 	struct service_callback* p;
1790*b7579f77SDag-Erling Smørgrav 	for(p = sq->cblist; p; p = p->next) {
1791*b7579f77SDag-Erling Smørgrav 		if(arg_compare(p->cb_arg, cb_arg))
1792*b7579f77SDag-Erling Smørgrav 			return p;
1793*b7579f77SDag-Erling Smørgrav 	}
1794*b7579f77SDag-Erling Smørgrav 	return NULL;
1795*b7579f77SDag-Erling Smørgrav }
1796*b7579f77SDag-Erling Smørgrav 
1797*b7579f77SDag-Erling Smørgrav struct serviced_query*
1798*b7579f77SDag-Erling Smørgrav outnet_serviced_query(struct outside_network* outnet,
1799*b7579f77SDag-Erling Smørgrav 	uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
1800*b7579f77SDag-Erling Smørgrav 	uint16_t flags, int dnssec, int want_dnssec, int tcp_upstream,
1801*b7579f77SDag-Erling Smørgrav 	int ssl_upstream, struct sockaddr_storage* addr, socklen_t addrlen,
1802*b7579f77SDag-Erling Smørgrav 	uint8_t* zone, size_t zonelen, comm_point_callback_t* callback,
1803*b7579f77SDag-Erling Smørgrav 	void* callback_arg, ldns_buffer* buff, int (*arg_compare)(void*,void*))
1804*b7579f77SDag-Erling Smørgrav {
1805*b7579f77SDag-Erling Smørgrav 	struct serviced_query* sq;
1806*b7579f77SDag-Erling Smørgrav 	struct service_callback* cb;
1807*b7579f77SDag-Erling Smørgrav 	serviced_gen_query(buff, qname, qnamelen, qtype, qclass, flags);
1808*b7579f77SDag-Erling Smørgrav 	sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen);
1809*b7579f77SDag-Erling Smørgrav 	if(sq) {
1810*b7579f77SDag-Erling Smørgrav 		/* see if it is a duplicate notification request for cb_arg */
1811*b7579f77SDag-Erling Smørgrav 		if(callback_list_find(sq, callback_arg, arg_compare)) {
1812*b7579f77SDag-Erling Smørgrav 			return sq;
1813*b7579f77SDag-Erling Smørgrav 		}
1814*b7579f77SDag-Erling Smørgrav 	}
1815*b7579f77SDag-Erling Smørgrav 	if(!(cb = (struct service_callback*)malloc(sizeof(*cb))))
1816*b7579f77SDag-Erling Smørgrav 		return NULL;
1817*b7579f77SDag-Erling Smørgrav 	if(!sq) {
1818*b7579f77SDag-Erling Smørgrav 		/* make new serviced query entry */
1819*b7579f77SDag-Erling Smørgrav 		sq = serviced_create(outnet, buff, dnssec, want_dnssec,
1820*b7579f77SDag-Erling Smørgrav 			tcp_upstream, ssl_upstream, addr, addrlen, zone,
1821*b7579f77SDag-Erling Smørgrav 			zonelen, (int)qtype);
1822*b7579f77SDag-Erling Smørgrav 		if(!sq) {
1823*b7579f77SDag-Erling Smørgrav 			free(cb);
1824*b7579f77SDag-Erling Smørgrav 			return NULL;
1825*b7579f77SDag-Erling Smørgrav 		}
1826*b7579f77SDag-Erling Smørgrav 		/* perform first network action */
1827*b7579f77SDag-Erling Smørgrav 		if(outnet->do_udp && !(tcp_upstream || ssl_upstream)) {
1828*b7579f77SDag-Erling Smørgrav 			if(!serviced_udp_send(sq, buff)) {
1829*b7579f77SDag-Erling Smørgrav 				(void)rbtree_delete(outnet->serviced, sq);
1830*b7579f77SDag-Erling Smørgrav 				free(sq->qbuf);
1831*b7579f77SDag-Erling Smørgrav 				free(sq->zone);
1832*b7579f77SDag-Erling Smørgrav 				free(sq);
1833*b7579f77SDag-Erling Smørgrav 				free(cb);
1834*b7579f77SDag-Erling Smørgrav 				return NULL;
1835*b7579f77SDag-Erling Smørgrav 			}
1836*b7579f77SDag-Erling Smørgrav 		} else {
1837*b7579f77SDag-Erling Smørgrav 			if(!serviced_tcp_send(sq, buff)) {
1838*b7579f77SDag-Erling Smørgrav 				(void)rbtree_delete(outnet->serviced, sq);
1839*b7579f77SDag-Erling Smørgrav 				free(sq->qbuf);
1840*b7579f77SDag-Erling Smørgrav 				free(sq->zone);
1841*b7579f77SDag-Erling Smørgrav 				free(sq);
1842*b7579f77SDag-Erling Smørgrav 				free(cb);
1843*b7579f77SDag-Erling Smørgrav 				return NULL;
1844*b7579f77SDag-Erling Smørgrav 			}
1845*b7579f77SDag-Erling Smørgrav 		}
1846*b7579f77SDag-Erling Smørgrav 	}
1847*b7579f77SDag-Erling Smørgrav 	/* add callback to list of callbacks */
1848*b7579f77SDag-Erling Smørgrav 	cb->cb = callback;
1849*b7579f77SDag-Erling Smørgrav 	cb->cb_arg = callback_arg;
1850*b7579f77SDag-Erling Smørgrav 	cb->next = sq->cblist;
1851*b7579f77SDag-Erling Smørgrav 	sq->cblist = cb;
1852*b7579f77SDag-Erling Smørgrav 	return sq;
1853*b7579f77SDag-Erling Smørgrav }
1854*b7579f77SDag-Erling Smørgrav 
1855*b7579f77SDag-Erling Smørgrav /** remove callback from list */
1856*b7579f77SDag-Erling Smørgrav static void
1857*b7579f77SDag-Erling Smørgrav callback_list_remove(struct serviced_query* sq, void* cb_arg)
1858*b7579f77SDag-Erling Smørgrav {
1859*b7579f77SDag-Erling Smørgrav 	struct service_callback** pp = &sq->cblist;
1860*b7579f77SDag-Erling Smørgrav 	while(*pp) {
1861*b7579f77SDag-Erling Smørgrav 		if((*pp)->cb_arg == cb_arg) {
1862*b7579f77SDag-Erling Smørgrav 			struct service_callback* del = *pp;
1863*b7579f77SDag-Erling Smørgrav 			*pp = del->next;
1864*b7579f77SDag-Erling Smørgrav 			free(del);
1865*b7579f77SDag-Erling Smørgrav 			return;
1866*b7579f77SDag-Erling Smørgrav 		}
1867*b7579f77SDag-Erling Smørgrav 		pp = &(*pp)->next;
1868*b7579f77SDag-Erling Smørgrav 	}
1869*b7579f77SDag-Erling Smørgrav }
1870*b7579f77SDag-Erling Smørgrav 
1871*b7579f77SDag-Erling Smørgrav void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg)
1872*b7579f77SDag-Erling Smørgrav {
1873*b7579f77SDag-Erling Smørgrav 	if(!sq)
1874*b7579f77SDag-Erling Smørgrav 		return;
1875*b7579f77SDag-Erling Smørgrav 	callback_list_remove(sq, cb_arg);
1876*b7579f77SDag-Erling Smørgrav 	/* if callbacks() routine scheduled deletion, let it do that */
1877*b7579f77SDag-Erling Smørgrav 	if(!sq->cblist && !sq->to_be_deleted) {
1878*b7579f77SDag-Erling Smørgrav #ifdef UNBOUND_DEBUG
1879*b7579f77SDag-Erling Smørgrav 		rbnode_t* rem =
1880*b7579f77SDag-Erling Smørgrav #endif
1881*b7579f77SDag-Erling Smørgrav 		rbtree_delete(sq->outnet->serviced, sq);
1882*b7579f77SDag-Erling Smørgrav 		log_assert(rem); /* should be present */
1883*b7579f77SDag-Erling Smørgrav 		serviced_delete(sq);
1884*b7579f77SDag-Erling Smørgrav 	}
1885*b7579f77SDag-Erling Smørgrav }
1886*b7579f77SDag-Erling Smørgrav 
1887*b7579f77SDag-Erling Smørgrav /** get memory used by waiting tcp entry (in use or not) */
1888*b7579f77SDag-Erling Smørgrav static size_t
1889*b7579f77SDag-Erling Smørgrav waiting_tcp_get_mem(struct waiting_tcp* w)
1890*b7579f77SDag-Erling Smørgrav {
1891*b7579f77SDag-Erling Smørgrav 	size_t s;
1892*b7579f77SDag-Erling Smørgrav 	if(!w) return 0;
1893*b7579f77SDag-Erling Smørgrav 	s = sizeof(*w) + w->pkt_len;
1894*b7579f77SDag-Erling Smørgrav 	if(w->timer)
1895*b7579f77SDag-Erling Smørgrav 		s += comm_timer_get_mem(w->timer);
1896*b7579f77SDag-Erling Smørgrav 	return s;
1897*b7579f77SDag-Erling Smørgrav }
1898*b7579f77SDag-Erling Smørgrav 
1899*b7579f77SDag-Erling Smørgrav /** get memory used by port if */
1900*b7579f77SDag-Erling Smørgrav static size_t
1901*b7579f77SDag-Erling Smørgrav if_get_mem(struct port_if* pif)
1902*b7579f77SDag-Erling Smørgrav {
1903*b7579f77SDag-Erling Smørgrav 	size_t s;
1904*b7579f77SDag-Erling Smørgrav 	int i;
1905*b7579f77SDag-Erling Smørgrav 	s = sizeof(*pif) + sizeof(int)*pif->avail_total +
1906*b7579f77SDag-Erling Smørgrav 		sizeof(struct port_comm*)*pif->maxout;
1907*b7579f77SDag-Erling Smørgrav 	for(i=0; i<pif->inuse; i++)
1908*b7579f77SDag-Erling Smørgrav 		s += sizeof(*pif->out[i]) +
1909*b7579f77SDag-Erling Smørgrav 			comm_point_get_mem(pif->out[i]->cp);
1910*b7579f77SDag-Erling Smørgrav 	return s;
1911*b7579f77SDag-Erling Smørgrav }
1912*b7579f77SDag-Erling Smørgrav 
1913*b7579f77SDag-Erling Smørgrav /** get memory used by waiting udp */
1914*b7579f77SDag-Erling Smørgrav static size_t
1915*b7579f77SDag-Erling Smørgrav waiting_udp_get_mem(struct pending* w)
1916*b7579f77SDag-Erling Smørgrav {
1917*b7579f77SDag-Erling Smørgrav 	size_t s;
1918*b7579f77SDag-Erling Smørgrav 	s = sizeof(*w) + comm_timer_get_mem(w->timer) + w->pkt_len;
1919*b7579f77SDag-Erling Smørgrav 	return s;
1920*b7579f77SDag-Erling Smørgrav }
1921*b7579f77SDag-Erling Smørgrav 
1922*b7579f77SDag-Erling Smørgrav size_t outnet_get_mem(struct outside_network* outnet)
1923*b7579f77SDag-Erling Smørgrav {
1924*b7579f77SDag-Erling Smørgrav 	size_t i;
1925*b7579f77SDag-Erling Smørgrav 	int k;
1926*b7579f77SDag-Erling Smørgrav 	struct waiting_tcp* w;
1927*b7579f77SDag-Erling Smørgrav 	struct pending* u;
1928*b7579f77SDag-Erling Smørgrav 	struct serviced_query* sq;
1929*b7579f77SDag-Erling Smørgrav 	struct service_callback* sb;
1930*b7579f77SDag-Erling Smørgrav 	struct port_comm* pc;
1931*b7579f77SDag-Erling Smørgrav 	size_t s = sizeof(*outnet) + sizeof(*outnet->base) +
1932*b7579f77SDag-Erling Smørgrav 		sizeof(*outnet->udp_buff) +
1933*b7579f77SDag-Erling Smørgrav 		ldns_buffer_capacity(outnet->udp_buff);
1934*b7579f77SDag-Erling Smørgrav 	/* second buffer is not ours */
1935*b7579f77SDag-Erling Smørgrav 	for(pc = outnet->unused_fds; pc; pc = pc->next) {
1936*b7579f77SDag-Erling Smørgrav 		s += sizeof(*pc) + comm_point_get_mem(pc->cp);
1937*b7579f77SDag-Erling Smørgrav 	}
1938*b7579f77SDag-Erling Smørgrav 	for(k=0; k<outnet->num_ip4; k++)
1939*b7579f77SDag-Erling Smørgrav 		s += if_get_mem(&outnet->ip4_ifs[k]);
1940*b7579f77SDag-Erling Smørgrav 	for(k=0; k<outnet->num_ip6; k++)
1941*b7579f77SDag-Erling Smørgrav 		s += if_get_mem(&outnet->ip6_ifs[k]);
1942*b7579f77SDag-Erling Smørgrav 	for(u=outnet->udp_wait_first; u; u=u->next_waiting)
1943*b7579f77SDag-Erling Smørgrav 		s += waiting_udp_get_mem(u);
1944*b7579f77SDag-Erling Smørgrav 
1945*b7579f77SDag-Erling Smørgrav 	s += sizeof(struct pending_tcp*)*outnet->num_tcp;
1946*b7579f77SDag-Erling Smørgrav 	for(i=0; i<outnet->num_tcp; i++) {
1947*b7579f77SDag-Erling Smørgrav 		s += sizeof(struct pending_tcp);
1948*b7579f77SDag-Erling Smørgrav 		s += comm_point_get_mem(outnet->tcp_conns[i]->c);
1949*b7579f77SDag-Erling Smørgrav 		if(outnet->tcp_conns[i]->query)
1950*b7579f77SDag-Erling Smørgrav 			s += waiting_tcp_get_mem(outnet->tcp_conns[i]->query);
1951*b7579f77SDag-Erling Smørgrav 	}
1952*b7579f77SDag-Erling Smørgrav 	for(w=outnet->tcp_wait_first; w; w = w->next_waiting)
1953*b7579f77SDag-Erling Smørgrav 		s += waiting_tcp_get_mem(w);
1954*b7579f77SDag-Erling Smørgrav 	s += sizeof(*outnet->pending);
1955*b7579f77SDag-Erling Smørgrav 	s += (sizeof(struct pending) + comm_timer_get_mem(NULL)) *
1956*b7579f77SDag-Erling Smørgrav 		outnet->pending->count;
1957*b7579f77SDag-Erling Smørgrav 	s += sizeof(*outnet->serviced);
1958*b7579f77SDag-Erling Smørgrav 	s += outnet->svcd_overhead;
1959*b7579f77SDag-Erling Smørgrav 	RBTREE_FOR(sq, struct serviced_query*, outnet->serviced) {
1960*b7579f77SDag-Erling Smørgrav 		s += sizeof(*sq) + sq->qbuflen;
1961*b7579f77SDag-Erling Smørgrav 		for(sb = sq->cblist; sb; sb = sb->next)
1962*b7579f77SDag-Erling Smørgrav 			s += sizeof(*sb);
1963*b7579f77SDag-Erling Smørgrav 	}
1964*b7579f77SDag-Erling Smørgrav 	return s;
1965*b7579f77SDag-Erling Smørgrav }
1966*b7579f77SDag-Erling Smørgrav 
1967*b7579f77SDag-Erling Smørgrav size_t
1968*b7579f77SDag-Erling Smørgrav serviced_get_mem(struct serviced_query* sq)
1969*b7579f77SDag-Erling Smørgrav {
1970*b7579f77SDag-Erling Smørgrav 	struct service_callback* sb;
1971*b7579f77SDag-Erling Smørgrav 	size_t s;
1972*b7579f77SDag-Erling Smørgrav 	s = sizeof(*sq) + sq->qbuflen;
1973*b7579f77SDag-Erling Smørgrav 	for(sb = sq->cblist; sb; sb = sb->next)
1974*b7579f77SDag-Erling Smørgrav 		s += sizeof(*sb);
1975*b7579f77SDag-Erling Smørgrav 	if(sq->status == serviced_query_UDP_EDNS ||
1976*b7579f77SDag-Erling Smørgrav 		sq->status == serviced_query_UDP ||
1977*b7579f77SDag-Erling Smørgrav 		sq->status == serviced_query_PROBE_EDNS ||
1978*b7579f77SDag-Erling Smørgrav 		sq->status == serviced_query_UDP_EDNS_FRAG ||
1979*b7579f77SDag-Erling Smørgrav 		sq->status == serviced_query_UDP_EDNS_fallback) {
1980*b7579f77SDag-Erling Smørgrav 		s += sizeof(struct pending);
1981*b7579f77SDag-Erling Smørgrav 		s += comm_timer_get_mem(NULL);
1982*b7579f77SDag-Erling Smørgrav 	} else {
1983*b7579f77SDag-Erling Smørgrav 		/* does not have size of the pkt pointer */
1984*b7579f77SDag-Erling Smørgrav 		/* always has a timer except on malloc failures */
1985*b7579f77SDag-Erling Smørgrav 
1986*b7579f77SDag-Erling Smørgrav 		/* these sizes are part of the main outside network mem */
1987*b7579f77SDag-Erling Smørgrav 		/*
1988*b7579f77SDag-Erling Smørgrav 		s += sizeof(struct waiting_tcp);
1989*b7579f77SDag-Erling Smørgrav 		s += comm_timer_get_mem(NULL);
1990*b7579f77SDag-Erling Smørgrav 		*/
1991*b7579f77SDag-Erling Smørgrav 	}
1992*b7579f77SDag-Erling Smørgrav 	return s;
1993*b7579f77SDag-Erling Smørgrav }
1994*b7579f77SDag-Erling Smørgrav 
1995