/src/unbound/services/outside_network.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * services/outside_network.c - implement sending of queries and wait answer. |
3 | | * |
4 | | * Copyright (c) 2007, NLnet Labs. All rights reserved. |
5 | | * |
6 | | * This software is open source. |
7 | | * |
8 | | * Redistribution and use in source and binary forms, with or without |
9 | | * modification, are permitted provided that the following conditions |
10 | | * are met: |
11 | | * |
12 | | * Redistributions of source code must retain the above copyright notice, |
13 | | * this list of conditions and the following disclaimer. |
14 | | * |
15 | | * Redistributions in binary form must reproduce the above copyright notice, |
16 | | * this list of conditions and the following disclaimer in the documentation |
17 | | * and/or other materials provided with the distribution. |
18 | | * |
19 | | * Neither the name of the NLNET LABS nor the names of its contributors may |
20 | | * be used to endorse or promote products derived from this software without |
21 | | * specific prior written permission. |
22 | | * |
23 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
24 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
25 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
26 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
27 | | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
28 | | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
29 | | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
30 | | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
31 | | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
32 | | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
33 | | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
34 | | */ |
35 | | |
36 | | /** |
37 | | * \file |
38 | | * |
39 | | * This file has functions to send queries to authoritative servers and |
40 | | * wait for the pending answer events. |
41 | | */ |
42 | | #include "config.h" |
43 | | #include <ctype.h> |
44 | | #ifdef HAVE_SYS_TYPES_H |
45 | | # include <sys/types.h> |
46 | | #endif |
47 | | #include <sys/time.h> |
48 | | #include "services/outside_network.h" |
49 | | #include "services/listen_dnsport.h" |
50 | | #include "services/cache/infra.h" |
51 | | #include "iterator/iterator.h" |
52 | | #include "util/data/msgparse.h" |
53 | | #include "util/data/msgreply.h" |
54 | | #include "util/data/msgencode.h" |
55 | | #include "util/data/dname.h" |
56 | | #include "util/netevent.h" |
57 | | #include "util/log.h" |
58 | | #include "util/net_help.h" |
59 | | #include "util/random.h" |
60 | | #include "util/fptr_wlist.h" |
61 | | #include "util/edns.h" |
62 | | #include "sldns/sbuffer.h" |
63 | | #include "dnstap/dnstap.h" |
64 | | #ifdef HAVE_OPENSSL_SSL_H |
65 | | #include <openssl/ssl.h> |
66 | | #endif |
67 | | #ifdef HAVE_X509_VERIFY_PARAM_SET1_HOST |
68 | | #include <openssl/x509v3.h> |
69 | | #endif |
70 | | |
71 | | #ifdef HAVE_NETDB_H |
72 | | #include <netdb.h> |
73 | | #endif |
74 | | #include <fcntl.h> |
75 | | |
76 | | /** number of times to retry making a random ID that is unique. */ |
77 | 0 | #define MAX_ID_RETRY 1000 |
78 | | /** number of times to retry finding interface, port that can be opened. */ |
79 | 0 | #define MAX_PORT_RETRY 10000 |
80 | | /** number of retries on outgoing UDP queries */ |
81 | 0 | #define OUTBOUND_UDP_RETRY 1 |
82 | | |
83 | | /** initiate TCP transaction for serviced query */ |
84 | | static void serviced_tcp_initiate(struct serviced_query* sq, sldns_buffer* buff); |
85 | | /** with a fd available, randomize and send UDP */ |
86 | | static int randomize_and_send_udp(struct pending* pend, sldns_buffer* packet, |
87 | | int timeout); |
88 | | |
89 | | /** select a DNS ID for a TCP stream */ |
90 | | static uint16_t tcp_select_id(struct outside_network* outnet, |
91 | | struct reuse_tcp* reuse); |
92 | | |
93 | | /** Perform serviced query UDP sending operation */ |
94 | | static int serviced_udp_send(struct serviced_query* sq, sldns_buffer* buff); |
95 | | |
96 | | /** Send serviced query over TCP return false on initial failure */ |
97 | | static int serviced_tcp_send(struct serviced_query* sq, sldns_buffer* buff); |
98 | | |
99 | | /** call the callbacks for a serviced query */ |
100 | | static void serviced_callbacks(struct serviced_query* sq, int error, |
101 | | struct comm_point* c, struct comm_reply* rep); |
102 | | |
103 | | int |
104 | | pending_cmp(const void* key1, const void* key2) |
105 | 0 | { |
106 | 0 | struct pending *p1 = (struct pending*)key1; |
107 | 0 | struct pending *p2 = (struct pending*)key2; |
108 | 0 | if(p1->id < p2->id) |
109 | 0 | return -1; |
110 | 0 | if(p1->id > p2->id) |
111 | 0 | return 1; |
112 | 0 | log_assert(p1->id == p2->id); |
113 | 0 | return sockaddr_cmp(&p1->addr, p1->addrlen, &p2->addr, p2->addrlen); |
114 | 0 | } |
115 | | |
116 | | int |
117 | | serviced_cmp(const void* key1, const void* key2) |
118 | 0 | { |
119 | 0 | struct serviced_query* q1 = (struct serviced_query*)key1; |
120 | 0 | struct serviced_query* q2 = (struct serviced_query*)key2; |
121 | 0 | int r; |
122 | 0 | if(q1->qbuflen < q2->qbuflen) |
123 | 0 | return -1; |
124 | 0 | if(q1->qbuflen > q2->qbuflen) |
125 | 0 | return 1; |
126 | 0 | log_assert(q1->qbuflen == q2->qbuflen); |
127 | 0 | log_assert(q1->qbuflen >= 15 /* 10 header, root, type, class */); |
128 | | /* alternate casing of qname is still the same query */ |
129 | 0 | if((r = memcmp(q1->qbuf, q2->qbuf, 10)) != 0) |
130 | 0 | return r; |
131 | 0 | if((r = memcmp(q1->qbuf+q1->qbuflen-4, q2->qbuf+q2->qbuflen-4, 4)) != 0) |
132 | 0 | return r; |
133 | 0 | if(q1->dnssec != q2->dnssec) { |
134 | 0 | if(q1->dnssec < q2->dnssec) |
135 | 0 | return -1; |
136 | 0 | return 1; |
137 | 0 | } |
138 | 0 | if((r = query_dname_compare(q1->qbuf+10, q2->qbuf+10)) != 0) |
139 | 0 | return r; |
140 | 0 | if((r = edns_opt_list_compare(q1->opt_list, q2->opt_list)) != 0) |
141 | 0 | return r; |
142 | 0 | return sockaddr_cmp(&q1->addr, q1->addrlen, &q2->addr, q2->addrlen); |
143 | 0 | } |
144 | | |
145 | | /** compare if the reuse element has the same address, port and same ssl-is |
146 | | * used-for-it characteristic */ |
147 | | static int |
148 | | reuse_cmp_addrportssl(const void* key1, const void* key2) |
149 | 0 | { |
150 | 0 | struct reuse_tcp* r1 = (struct reuse_tcp*)key1; |
151 | 0 | struct reuse_tcp* r2 = (struct reuse_tcp*)key2; |
152 | 0 | int r; |
153 | | /* compare address and port */ |
154 | 0 | r = sockaddr_cmp(&r1->addr, r1->addrlen, &r2->addr, r2->addrlen); |
155 | 0 | if(r != 0) |
156 | 0 | return r; |
157 | | |
158 | | /* compare if SSL-enabled */ |
159 | 0 | if(r1->is_ssl && !r2->is_ssl) |
160 | 0 | return 1; |
161 | 0 | if(!r1->is_ssl && r2->is_ssl) |
162 | 0 | return -1; |
163 | 0 | return 0; |
164 | 0 | } |
165 | | |
166 | | int |
167 | | reuse_cmp(const void* key1, const void* key2) |
168 | 0 | { |
169 | 0 | int r; |
170 | 0 | r = reuse_cmp_addrportssl(key1, key2); |
171 | 0 | if(r != 0) |
172 | 0 | return r; |
173 | | |
174 | | /* compare ptr value */ |
175 | 0 | if(key1 < key2) return -1; |
176 | 0 | if(key1 > key2) return 1; |
177 | 0 | return 0; |
178 | 0 | } |
179 | | |
180 | | int reuse_id_cmp(const void* key1, const void* key2) |
181 | 0 | { |
182 | 0 | struct waiting_tcp* w1 = (struct waiting_tcp*)key1; |
183 | 0 | struct waiting_tcp* w2 = (struct waiting_tcp*)key2; |
184 | 0 | if(w1->id < w2->id) |
185 | 0 | return -1; |
186 | 0 | if(w1->id > w2->id) |
187 | 0 | return 1; |
188 | 0 | return 0; |
189 | 0 | } |
190 | | |
191 | | /** delete waiting_tcp entry. Does not unlink from waiting list. |
192 | | * @param w: to delete. |
193 | | */ |
194 | | static void |
195 | | waiting_tcp_delete(struct waiting_tcp* w) |
196 | 0 | { |
197 | 0 | if(!w) return; |
198 | 0 | if(w->timer) |
199 | 0 | comm_timer_delete(w->timer); |
200 | 0 | free(w); |
201 | 0 | } |
202 | | |
203 | | /** |
204 | | * Pick random outgoing-interface of that family, and bind it. |
205 | | * port set to 0 so OS picks a port number for us. |
206 | | * if it is the ANY address, do not bind. |
207 | | * @param pend: pending tcp structure, for storing the local address choice. |
208 | | * @param w: tcp structure with destination address. |
209 | | * @param s: socket fd. |
210 | | * @return false on error, socket closed. |
211 | | */ |
212 | | static int |
213 | | pick_outgoing_tcp(struct pending_tcp* pend, struct waiting_tcp* w, int s) |
214 | 0 | { |
215 | 0 | struct port_if* pi = NULL; |
216 | 0 | int num; |
217 | 0 | pend->pi = NULL; |
218 | 0 | #ifdef INET6 |
219 | 0 | if(addr_is_ip6(&w->addr, w->addrlen)) |
220 | 0 | num = w->outnet->num_ip6; |
221 | 0 | else |
222 | 0 | #endif |
223 | 0 | num = w->outnet->num_ip4; |
224 | 0 | if(num == 0) { |
225 | 0 | log_err("no TCP outgoing interfaces of family"); |
226 | 0 | log_addr(VERB_OPS, "for addr", &w->addr, w->addrlen); |
227 | 0 | sock_close(s); |
228 | 0 | return 0; |
229 | 0 | } |
230 | 0 | #ifdef INET6 |
231 | 0 | if(addr_is_ip6(&w->addr, w->addrlen)) |
232 | 0 | pi = &w->outnet->ip6_ifs[ub_random_max(w->outnet->rnd, num)]; |
233 | 0 | else |
234 | 0 | #endif |
235 | 0 | pi = &w->outnet->ip4_ifs[ub_random_max(w->outnet->rnd, num)]; |
236 | 0 | log_assert(pi); |
237 | 0 | pend->pi = pi; |
238 | 0 | if(addr_is_any(&pi->addr, pi->addrlen)) { |
239 | | /* binding to the ANY interface is for listening sockets */ |
240 | 0 | return 1; |
241 | 0 | } |
242 | | /* set port to 0 */ |
243 | 0 | if(addr_is_ip6(&pi->addr, pi->addrlen)) |
244 | 0 | ((struct sockaddr_in6*)&pi->addr)->sin6_port = 0; |
245 | 0 | else ((struct sockaddr_in*)&pi->addr)->sin_port = 0; |
246 | 0 | if(bind(s, (struct sockaddr*)&pi->addr, pi->addrlen) != 0) { |
247 | 0 | #ifndef USE_WINSOCK |
248 | 0 | #ifdef EADDRNOTAVAIL |
249 | 0 | if(!(verbosity < 4 && errno == EADDRNOTAVAIL)) |
250 | 0 | #endif |
251 | | #else /* USE_WINSOCK */ |
252 | | if(!(verbosity < 4 && WSAGetLastError() == WSAEADDRNOTAVAIL)) |
253 | | #endif |
254 | 0 | log_err("outgoing tcp: bind: %s", sock_strerror(errno)); |
255 | 0 | sock_close(s); |
256 | 0 | return 0; |
257 | 0 | } |
258 | 0 | log_addr(VERB_ALGO, "tcp bound to src", &pi->addr, pi->addrlen); |
259 | 0 | return 1; |
260 | 0 | } |
261 | | |
262 | | /** get TCP file descriptor for address, returns -1 on failure, |
263 | | * tcp_mss is 0 or maxseg size to set for TCP packets. */ |
264 | | int |
265 | | outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, |
266 | | int tcp_mss, int dscp, int nodelay) |
267 | 0 | { |
268 | 0 | int s; |
269 | 0 | int af; |
270 | 0 | char* err; |
271 | 0 | #if defined(SO_REUSEADDR) || defined(IP_BIND_ADDRESS_NO_PORT) \ |
272 | 0 | || defined(TCP_NODELAY) |
273 | 0 | int on = 1; |
274 | 0 | #endif |
275 | 0 | #ifdef INET6 |
276 | 0 | if(addr_is_ip6(addr, addrlen)){ |
277 | 0 | s = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); |
278 | 0 | af = AF_INET6; |
279 | 0 | } else { |
280 | | #else |
281 | | { |
282 | | #endif |
283 | 0 | af = AF_INET; |
284 | 0 | s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); |
285 | 0 | } |
286 | 0 | if(s == -1) { |
287 | 0 | log_err_addr("outgoing tcp: socket", sock_strerror(errno), |
288 | 0 | addr, addrlen); |
289 | 0 | return -1; |
290 | 0 | } |
291 | | |
292 | 0 | #ifdef SO_REUSEADDR |
293 | 0 | if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on, |
294 | 0 | (socklen_t)sizeof(on)) < 0) { |
295 | 0 | verbose(VERB_ALGO, "outgoing tcp:" |
296 | 0 | " setsockopt(.. SO_REUSEADDR ..) failed"); |
297 | 0 | } |
298 | 0 | #endif |
299 | |
|
300 | 0 | err = set_ip_dscp(s, af, dscp); |
301 | 0 | if(err != NULL) { |
302 | 0 | verbose(VERB_ALGO, "outgoing tcp:" |
303 | 0 | "error setting IP DiffServ codepoint on socket"); |
304 | 0 | } |
305 | |
|
306 | 0 | if(tcp_mss > 0) { |
307 | 0 | #if defined(IPPROTO_TCP) && defined(TCP_MAXSEG) |
308 | 0 | if(setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, |
309 | 0 | (void*)&tcp_mss, (socklen_t)sizeof(tcp_mss)) < 0) { |
310 | 0 | verbose(VERB_ALGO, "outgoing tcp:" |
311 | 0 | " setsockopt(.. TCP_MAXSEG ..) failed"); |
312 | 0 | } |
313 | | #else |
314 | | verbose(VERB_ALGO, "outgoing tcp:" |
315 | | " setsockopt(TCP_MAXSEG) unsupported"); |
316 | | #endif /* defined(IPPROTO_TCP) && defined(TCP_MAXSEG) */ |
317 | 0 | } |
318 | 0 | #ifdef IP_BIND_ADDRESS_NO_PORT |
319 | 0 | if(setsockopt(s, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, (void*)&on, |
320 | 0 | (socklen_t)sizeof(on)) < 0) { |
321 | 0 | verbose(VERB_ALGO, "outgoing tcp:" |
322 | 0 | " setsockopt(.. IP_BIND_ADDRESS_NO_PORT ..) failed"); |
323 | 0 | } |
324 | 0 | #endif /* IP_BIND_ADDRESS_NO_PORT */ |
325 | 0 | if(nodelay) { |
326 | 0 | #if defined(IPPROTO_TCP) && defined(TCP_NODELAY) |
327 | 0 | if(setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void*)&on, |
328 | 0 | (socklen_t)sizeof(on)) < 0) { |
329 | 0 | verbose(VERB_ALGO, "outgoing tcp:" |
330 | 0 | " setsockopt(.. TCP_NODELAY ..) failed"); |
331 | 0 | } |
332 | | #else |
333 | | verbose(VERB_ALGO, "outgoing tcp:" |
334 | | " setsockopt(.. TCP_NODELAY ..) unsupported"); |
335 | | #endif /* defined(IPPROTO_TCP) && defined(TCP_NODELAY) */ |
336 | 0 | } |
337 | 0 | return s; |
338 | 0 | } |
339 | | |
340 | | /** connect tcp connection to addr, 0 on failure */ |
341 | | int |
342 | | outnet_tcp_connect(int s, struct sockaddr_storage* addr, socklen_t addrlen) |
343 | 0 | { |
344 | 0 | if(connect(s, (struct sockaddr*)addr, addrlen) == -1) { |
345 | 0 | #ifndef USE_WINSOCK |
346 | 0 | #ifdef EINPROGRESS |
347 | 0 | if(errno != EINPROGRESS) { |
348 | 0 | #endif |
349 | 0 | if(tcp_connect_errno_needs_log( |
350 | 0 | (struct sockaddr*)addr, addrlen)) |
351 | 0 | log_err_addr("outgoing tcp: connect", |
352 | 0 | strerror(errno), addr, addrlen); |
353 | 0 | close(s); |
354 | 0 | return 0; |
355 | 0 | #ifdef EINPROGRESS |
356 | 0 | } |
357 | 0 | #endif |
358 | | #else /* USE_WINSOCK */ |
359 | | if(WSAGetLastError() != WSAEINPROGRESS && |
360 | | WSAGetLastError() != WSAEWOULDBLOCK) { |
361 | | closesocket(s); |
362 | | return 0; |
363 | | } |
364 | | #endif |
365 | 0 | } |
366 | 0 | return 1; |
367 | 0 | } |
368 | | |
369 | | /** log reuse item addr and ptr with message */ |
370 | | static void |
371 | | log_reuse_tcp(enum verbosity_value v, const char* msg, struct reuse_tcp* reuse) |
372 | 0 | { |
373 | 0 | uint16_t port; |
374 | 0 | char addrbuf[128]; |
375 | 0 | if(verbosity < v) return; |
376 | 0 | if(!reuse || !reuse->pending || !reuse->pending->c) |
377 | 0 | return; |
378 | 0 | addr_to_str(&reuse->addr, reuse->addrlen, addrbuf, sizeof(addrbuf)); |
379 | 0 | port = ntohs(((struct sockaddr_in*)&reuse->addr)->sin_port); |
380 | 0 | verbose(v, "%s %s#%u fd %d", msg, addrbuf, (unsigned)port, |
381 | 0 | reuse->pending->c->fd); |
382 | 0 | } |
383 | | |
384 | | /** pop the first element from the writewait list */ |
385 | | struct waiting_tcp* |
386 | | reuse_write_wait_pop(struct reuse_tcp* reuse) |
387 | 0 | { |
388 | 0 | struct waiting_tcp* w = reuse->write_wait_first; |
389 | 0 | if(!w) |
390 | 0 | return NULL; |
391 | 0 | log_assert(w->write_wait_queued); |
392 | 0 | log_assert(!w->write_wait_prev); |
393 | 0 | reuse->write_wait_first = w->write_wait_next; |
394 | 0 | if(w->write_wait_next) |
395 | 0 | w->write_wait_next->write_wait_prev = NULL; |
396 | 0 | else reuse->write_wait_last = NULL; |
397 | 0 | w->write_wait_queued = 0; |
398 | 0 | w->write_wait_next = NULL; |
399 | 0 | w->write_wait_prev = NULL; |
400 | 0 | return w; |
401 | 0 | } |
402 | | |
403 | | /** remove the element from the writewait list */ |
404 | | void |
405 | | reuse_write_wait_remove(struct reuse_tcp* reuse, struct waiting_tcp* w) |
406 | 0 | { |
407 | 0 | log_assert(w); |
408 | 0 | log_assert(w->write_wait_queued); |
409 | 0 | if(!w) |
410 | 0 | return; |
411 | 0 | if(!w->write_wait_queued) |
412 | 0 | return; |
413 | 0 | if(w->write_wait_prev) |
414 | 0 | w->write_wait_prev->write_wait_next = w->write_wait_next; |
415 | 0 | else reuse->write_wait_first = w->write_wait_next; |
416 | 0 | log_assert(!w->write_wait_prev || |
417 | 0 | w->write_wait_prev->write_wait_next != w->write_wait_prev); |
418 | 0 | if(w->write_wait_next) |
419 | 0 | w->write_wait_next->write_wait_prev = w->write_wait_prev; |
420 | 0 | else reuse->write_wait_last = w->write_wait_prev; |
421 | 0 | log_assert(!w->write_wait_next |
422 | 0 | || w->write_wait_next->write_wait_prev != w->write_wait_next); |
423 | 0 | w->write_wait_queued = 0; |
424 | 0 | w->write_wait_next = NULL; |
425 | 0 | w->write_wait_prev = NULL; |
426 | 0 | } |
427 | | |
428 | | /** push the element after the last on the writewait list */ |
429 | | void |
430 | | reuse_write_wait_push_back(struct reuse_tcp* reuse, struct waiting_tcp* w) |
431 | 0 | { |
432 | 0 | if(!w) return; |
433 | 0 | log_assert(!w->write_wait_queued); |
434 | 0 | if(reuse->write_wait_last) { |
435 | 0 | reuse->write_wait_last->write_wait_next = w; |
436 | 0 | log_assert(reuse->write_wait_last->write_wait_next != |
437 | 0 | reuse->write_wait_last); |
438 | 0 | w->write_wait_prev = reuse->write_wait_last; |
439 | 0 | } else { |
440 | 0 | reuse->write_wait_first = w; |
441 | 0 | w->write_wait_prev = NULL; |
442 | 0 | } |
443 | 0 | w->write_wait_next = NULL; |
444 | 0 | reuse->write_wait_last = w; |
445 | 0 | w->write_wait_queued = 1; |
446 | 0 | } |
447 | | |
448 | | /** insert element in tree by id */ |
449 | | void |
450 | | reuse_tree_by_id_insert(struct reuse_tcp* reuse, struct waiting_tcp* w) |
451 | 0 | { |
452 | | #ifdef UNBOUND_DEBUG |
453 | | rbnode_type* added; |
454 | | #endif |
455 | 0 | log_assert(w->id_node.key == NULL); |
456 | 0 | w->id_node.key = w; |
457 | | #ifdef UNBOUND_DEBUG |
458 | | added = |
459 | | #else |
460 | 0 | (void) |
461 | 0 | #endif |
462 | 0 | rbtree_insert(&reuse->tree_by_id, &w->id_node); |
463 | 0 | log_assert(added); /* should have been added */ |
464 | 0 | } |
465 | | |
466 | | /** find element in tree by id */ |
467 | | struct waiting_tcp* |
468 | | reuse_tcp_by_id_find(struct reuse_tcp* reuse, uint16_t id) |
469 | 0 | { |
470 | 0 | struct waiting_tcp key_w; |
471 | 0 | rbnode_type* n; |
472 | 0 | memset(&key_w, 0, sizeof(key_w)); |
473 | 0 | key_w.id_node.key = &key_w; |
474 | 0 | key_w.id = id; |
475 | 0 | n = rbtree_search(&reuse->tree_by_id, &key_w); |
476 | 0 | if(!n) return NULL; |
477 | 0 | return (struct waiting_tcp*)n->key; |
478 | 0 | } |
479 | | |
480 | | /** return ID value of rbnode in tree_by_id */ |
481 | | static uint16_t |
482 | | tree_by_id_get_id(rbnode_type* node) |
483 | 0 | { |
484 | 0 | struct waiting_tcp* w = (struct waiting_tcp*)node->key; |
485 | 0 | return w->id; |
486 | 0 | } |
487 | | |
488 | | /** insert into reuse tcp tree and LRU, false on failure (duplicate) */ |
489 | | int |
490 | | reuse_tcp_insert(struct outside_network* outnet, struct pending_tcp* pend_tcp) |
491 | 0 | { |
492 | 0 | log_reuse_tcp(VERB_CLIENT, "reuse_tcp_insert", &pend_tcp->reuse); |
493 | 0 | if(pend_tcp->reuse.item_on_lru_list) { |
494 | 0 | if(!pend_tcp->reuse.node.key) |
495 | 0 | log_err("internal error: reuse_tcp_insert: " |
496 | 0 | "in lru list without key"); |
497 | 0 | return 1; |
498 | 0 | } |
499 | 0 | pend_tcp->reuse.node.key = &pend_tcp->reuse; |
500 | 0 | pend_tcp->reuse.pending = pend_tcp; |
501 | 0 | if(!rbtree_insert(&outnet->tcp_reuse, &pend_tcp->reuse.node)) { |
502 | | /* We are not in the LRU list but we are already in the |
503 | | * tcp_reuse tree, strange. |
504 | | * Continue to add ourselves to the LRU list. */ |
505 | 0 | log_err("internal error: reuse_tcp_insert: in lru list but " |
506 | 0 | "not in the tree"); |
507 | 0 | } |
508 | | /* insert into LRU, first is newest */ |
509 | 0 | pend_tcp->reuse.lru_prev = NULL; |
510 | 0 | if(outnet->tcp_reuse_first) { |
511 | 0 | pend_tcp->reuse.lru_next = outnet->tcp_reuse_first; |
512 | 0 | log_assert(pend_tcp->reuse.lru_next != &pend_tcp->reuse); |
513 | 0 | outnet->tcp_reuse_first->lru_prev = &pend_tcp->reuse; |
514 | 0 | log_assert(outnet->tcp_reuse_first->lru_prev != |
515 | 0 | outnet->tcp_reuse_first); |
516 | 0 | } else { |
517 | 0 | pend_tcp->reuse.lru_next = NULL; |
518 | 0 | outnet->tcp_reuse_last = &pend_tcp->reuse; |
519 | 0 | } |
520 | 0 | outnet->tcp_reuse_first = &pend_tcp->reuse; |
521 | 0 | pend_tcp->reuse.item_on_lru_list = 1; |
522 | 0 | log_assert((!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) || |
523 | 0 | (outnet->tcp_reuse_first && outnet->tcp_reuse_last)); |
524 | 0 | log_assert(outnet->tcp_reuse_first != outnet->tcp_reuse_first->lru_next && |
525 | 0 | outnet->tcp_reuse_first != outnet->tcp_reuse_first->lru_prev); |
526 | 0 | log_assert(outnet->tcp_reuse_last != outnet->tcp_reuse_last->lru_next && |
527 | 0 | outnet->tcp_reuse_last != outnet->tcp_reuse_last->lru_prev); |
528 | 0 | return 1; |
529 | 0 | } |
530 | | |
531 | | /** find reuse tcp stream to destination for query, or NULL if none */ |
532 | | static struct reuse_tcp* |
533 | | reuse_tcp_find(struct outside_network* outnet, struct sockaddr_storage* addr, |
534 | | socklen_t addrlen, int use_ssl) |
535 | 0 | { |
536 | 0 | struct waiting_tcp key_w; |
537 | 0 | struct pending_tcp key_p; |
538 | 0 | struct comm_point c; |
539 | 0 | rbnode_type* result = NULL, *prev; |
540 | 0 | verbose(VERB_CLIENT, "reuse_tcp_find"); |
541 | 0 | memset(&key_w, 0, sizeof(key_w)); |
542 | 0 | memset(&key_p, 0, sizeof(key_p)); |
543 | 0 | memset(&c, 0, sizeof(c)); |
544 | 0 | key_p.query = &key_w; |
545 | 0 | key_p.c = &c; |
546 | 0 | key_p.reuse.pending = &key_p; |
547 | 0 | key_p.reuse.node.key = &key_p.reuse; |
548 | 0 | if(use_ssl) |
549 | 0 | key_p.reuse.is_ssl = 1; |
550 | 0 | if(addrlen > (socklen_t)sizeof(key_p.reuse.addr)) |
551 | 0 | return NULL; |
552 | 0 | memmove(&key_p.reuse.addr, addr, addrlen); |
553 | 0 | key_p.reuse.addrlen = addrlen; |
554 | |
|
555 | 0 | verbose(VERB_CLIENT, "reuse_tcp_find: num reuse streams %u", |
556 | 0 | (unsigned)outnet->tcp_reuse.count); |
557 | 0 | if(outnet->tcp_reuse.root == NULL || |
558 | 0 | outnet->tcp_reuse.root == RBTREE_NULL) |
559 | 0 | return NULL; |
560 | 0 | if(rbtree_find_less_equal(&outnet->tcp_reuse, &key_p.reuse, |
561 | 0 | &result)) { |
562 | | /* exact match */ |
563 | | /* but the key is on stack, and ptr is compared, impossible */ |
564 | 0 | log_assert(&key_p.reuse != (struct reuse_tcp*)result); |
565 | 0 | log_assert(&key_p != ((struct reuse_tcp*)result)->pending); |
566 | 0 | } |
567 | | |
568 | | /* It is possible that we search for something before the first element |
569 | | * in the tree. Replace a null pointer with the first element. |
570 | | */ |
571 | 0 | if (!result) { |
572 | 0 | verbose(VERB_CLIENT, "reuse_tcp_find: taking first"); |
573 | 0 | result = rbtree_first(&outnet->tcp_reuse); |
574 | 0 | } |
575 | | |
576 | | /* not found, return null */ |
577 | 0 | if(!result || result == RBTREE_NULL) |
578 | 0 | return NULL; |
579 | | |
580 | | /* It is possible that we got the previous address, but that the |
581 | | * address we are looking for is in the tree. If the address we got |
582 | | * is less than the address we are looking, then take the next entry. |
583 | | */ |
584 | 0 | if (reuse_cmp_addrportssl(result->key, &key_p.reuse) < 0) { |
585 | 0 | verbose(VERB_CLIENT, "reuse_tcp_find: key too low"); |
586 | 0 | result = rbtree_next(result); |
587 | 0 | } |
588 | |
|
589 | 0 | verbose(VERB_CLIENT, "reuse_tcp_find check inexact match"); |
590 | | /* inexact match, find one of possibly several connections to the |
591 | | * same destination address, with the correct port, ssl, and |
592 | | * also less than max number of open queries, or else, fail to open |
593 | | * a new one */ |
594 | | /* rewind to start of sequence of same address,port,ssl */ |
595 | 0 | prev = rbtree_previous(result); |
596 | 0 | while(prev && prev != RBTREE_NULL && |
597 | 0 | reuse_cmp_addrportssl(prev->key, &key_p.reuse) == 0) { |
598 | 0 | result = prev; |
599 | 0 | prev = rbtree_previous(result); |
600 | 0 | } |
601 | | |
602 | | /* loop to find first one that has correct characteristics */ |
603 | 0 | while(result && result != RBTREE_NULL && |
604 | 0 | reuse_cmp_addrportssl(result->key, &key_p.reuse) == 0) { |
605 | 0 | if(((struct reuse_tcp*)result)->tree_by_id.count < |
606 | 0 | outnet->max_reuse_tcp_queries) { |
607 | | /* same address, port, ssl-yes-or-no, and has |
608 | | * space for another query */ |
609 | 0 | return (struct reuse_tcp*)result; |
610 | 0 | } |
611 | 0 | result = rbtree_next(result); |
612 | 0 | } |
613 | 0 | return NULL; |
614 | 0 | } |
615 | | |
616 | | /** use the buffer to setup writing the query */ |
617 | | static void |
618 | | outnet_tcp_take_query_setup(int s, struct pending_tcp* pend, |
619 | | struct waiting_tcp* w) |
620 | 0 | { |
621 | 0 | struct timeval tv; |
622 | 0 | verbose(VERB_CLIENT, "outnet_tcp_take_query_setup: setup packet to write " |
623 | 0 | "len %d timeout %d msec", |
624 | 0 | (int)w->pkt_len, w->timeout); |
625 | 0 | pend->c->tcp_write_pkt = w->pkt; |
626 | 0 | pend->c->tcp_write_pkt_len = w->pkt_len; |
627 | 0 | pend->c->tcp_write_and_read = 1; |
628 | 0 | pend->c->tcp_write_byte_count = 0; |
629 | 0 | pend->c->tcp_is_reading = 0; |
630 | 0 | comm_point_start_listening(pend->c, s, -1); |
631 | | /* set timer on the waiting_tcp entry, this is the write timeout |
632 | | * for the written packet. The timer on pend->c is the timer |
633 | | * for when there is no written packet and we have readtimeouts */ |
634 | 0 | #ifndef S_SPLINT_S |
635 | 0 | tv.tv_sec = w->timeout/1000; |
636 | 0 | tv.tv_usec = (w->timeout%1000)*1000; |
637 | 0 | #endif |
638 | | /* if the waiting_tcp was previously waiting for a buffer in the |
639 | | * outside_network.tcpwaitlist, then the timer is reset now that |
640 | | * we start writing it */ |
641 | 0 | comm_timer_set(w->timer, &tv); |
642 | 0 | } |
643 | | |
644 | | /** use next free buffer to service a tcp query */ |
645 | | static int |
646 | | outnet_tcp_take_into_use(struct waiting_tcp* w) |
647 | 0 | { |
648 | 0 | struct pending_tcp* pend = w->outnet->tcp_free; |
649 | 0 | int s; |
650 | 0 | log_assert(pend); |
651 | 0 | log_assert(w->pkt); |
652 | 0 | log_assert(w->pkt_len > 0); |
653 | 0 | log_assert(w->addrlen > 0); |
654 | 0 | pend->c->tcp_do_toggle_rw = 0; |
655 | 0 | pend->c->tcp_do_close = 0; |
656 | | |
657 | | /* Consistency check, if we have ssl_upstream but no sslctx, then |
658 | | * log an error and return failure. |
659 | | */ |
660 | 0 | if (w->ssl_upstream && !w->outnet->sslctx) { |
661 | 0 | log_err("SSL upstream requested but no SSL context"); |
662 | 0 | return 0; |
663 | 0 | } |
664 | | |
665 | | /* open socket */ |
666 | 0 | s = outnet_get_tcp_fd(&w->addr, w->addrlen, w->outnet->tcp_mss, |
667 | 0 | w->outnet->ip_dscp, w->ssl_upstream); |
668 | |
|
669 | 0 | if(s == -1) |
670 | 0 | return 0; |
671 | | |
672 | 0 | if(!pick_outgoing_tcp(pend, w, s)) |
673 | 0 | return 0; |
674 | | |
675 | 0 | fd_set_nonblock(s); |
676 | | #ifdef USE_OSX_MSG_FASTOPEN |
677 | | /* API for fast open is different here. We use a connectx() function and |
678 | | then writes can happen as normal even using SSL.*/ |
679 | | /* connectx requires that the len be set in the sockaddr struct*/ |
680 | | struct sockaddr_in *addr_in = (struct sockaddr_in *)&w->addr; |
681 | | addr_in->sin_len = w->addrlen; |
682 | | sa_endpoints_t endpoints; |
683 | | endpoints.sae_srcif = 0; |
684 | | endpoints.sae_srcaddr = NULL; |
685 | | endpoints.sae_srcaddrlen = 0; |
686 | | endpoints.sae_dstaddr = (struct sockaddr *)&w->addr; |
687 | | endpoints.sae_dstaddrlen = w->addrlen; |
688 | | if (connectx(s, &endpoints, SAE_ASSOCID_ANY, |
689 | | CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE, |
690 | | NULL, 0, NULL, NULL) == -1) { |
691 | | /* if fails, failover to connect for OSX 10.10 */ |
692 | | #ifdef EINPROGRESS |
693 | | if(errno != EINPROGRESS) { |
694 | | #else |
695 | | if(1) { |
696 | | #endif |
697 | | if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) { |
698 | | #else /* USE_OSX_MSG_FASTOPEN*/ |
699 | | #ifdef USE_MSG_FASTOPEN |
700 | | pend->c->tcp_do_fastopen = 1; |
701 | | /* Only do TFO for TCP in which case no connect() is required here. |
702 | | Don't combine client TFO with SSL, since OpenSSL can't |
703 | | currently support doing a handshake on fd that already isn't connected*/ |
704 | | if (w->outnet->sslctx && w->ssl_upstream) { |
705 | | if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) { |
706 | | #else /* USE_MSG_FASTOPEN*/ |
707 | 0 | if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) { |
708 | 0 | #endif /* USE_MSG_FASTOPEN*/ |
709 | 0 | #endif /* USE_OSX_MSG_FASTOPEN*/ |
710 | 0 | #ifndef USE_WINSOCK |
711 | 0 | #ifdef EINPROGRESS |
712 | 0 | if(errno != EINPROGRESS) { |
713 | | #else |
714 | | if(1) { |
715 | | #endif |
716 | 0 | if(tcp_connect_errno_needs_log( |
717 | 0 | (struct sockaddr*)&w->addr, w->addrlen)) |
718 | 0 | log_err_addr("outgoing tcp: connect", |
719 | 0 | strerror(errno), &w->addr, w->addrlen); |
720 | 0 | close(s); |
721 | | #else /* USE_WINSOCK */ |
722 | | if(WSAGetLastError() != WSAEINPROGRESS && |
723 | | WSAGetLastError() != WSAEWOULDBLOCK) { |
724 | | closesocket(s); |
725 | | #endif |
726 | 0 | return 0; |
727 | 0 | } |
728 | 0 | } |
729 | | #ifdef USE_MSG_FASTOPEN |
730 | | } |
731 | | #endif /* USE_MSG_FASTOPEN */ |
732 | | #ifdef USE_OSX_MSG_FASTOPEN |
733 | | } |
734 | | } |
735 | | #endif /* USE_OSX_MSG_FASTOPEN */ |
736 | 0 | if(w->outnet->sslctx && w->ssl_upstream) { |
737 | 0 | pend->c->ssl = outgoing_ssl_fd(w->outnet->sslctx, s); |
738 | 0 | if(!pend->c->ssl) { |
739 | 0 | pend->c->fd = s; |
740 | 0 | comm_point_close(pend->c); |
741 | 0 | return 0; |
742 | 0 | } |
743 | 0 | verbose(VERB_ALGO, "the query is using TLS encryption, for %s", |
744 | 0 | (w->tls_auth_name?w->tls_auth_name:"an unauthenticated connection")); |
745 | | #ifdef USE_WINSOCK |
746 | | comm_point_tcp_win_bio_cb(pend->c, pend->c->ssl); |
747 | | #endif |
748 | 0 | pend->c->ssl_shake_state = comm_ssl_shake_write; |
749 | 0 | if(!set_auth_name_on_ssl(pend->c->ssl, w->tls_auth_name, |
750 | 0 | w->outnet->tls_use_sni)) { |
751 | 0 | pend->c->fd = s; |
752 | 0 | #ifdef HAVE_SSL |
753 | 0 | SSL_free(pend->c->ssl); |
754 | 0 | #endif |
755 | 0 | pend->c->ssl = NULL; |
756 | 0 | comm_point_close(pend->c); |
757 | 0 | return 0; |
758 | 0 | } |
759 | 0 | } |
760 | 0 | w->next_waiting = (void*)pend; |
761 | 0 | w->outnet->num_tcp_outgoing++; |
762 | 0 | w->outnet->tcp_free = pend->next_free; |
763 | 0 | pend->next_free = NULL; |
764 | 0 | pend->query = w; |
765 | 0 | pend->reuse.outnet = w->outnet; |
766 | 0 | pend->c->repinfo.remote_addrlen = w->addrlen; |
767 | 0 | pend->c->tcp_more_read_again = &pend->reuse.cp_more_read_again; |
768 | 0 | pend->c->tcp_more_write_again = &pend->reuse.cp_more_write_again; |
769 | 0 | pend->reuse.cp_more_read_again = 0; |
770 | 0 | pend->reuse.cp_more_write_again = 0; |
771 | 0 | memcpy(&pend->c->repinfo.remote_addr, &w->addr, w->addrlen); |
772 | 0 | pend->reuse.pending = pend; |
773 | | |
774 | | /* Remove from tree in case the is_ssl will be different and causes the |
775 | | * identity of the reuse_tcp to change; could result in nodes not being |
776 | | * deleted from the tree (because the new identity does not match the |
777 | | * previous node) but their ->key would be changed to NULL. */ |
778 | 0 | if(pend->reuse.node.key) |
779 | 0 | reuse_tcp_remove_tree_list(w->outnet, &pend->reuse); |
780 | |
|
781 | 0 | if(pend->c->ssl) |
782 | 0 | pend->reuse.is_ssl = 1; |
783 | 0 | else pend->reuse.is_ssl = 0; |
784 | | /* insert in reuse by address tree if not already inserted there */ |
785 | 0 | (void)reuse_tcp_insert(w->outnet, pend); |
786 | 0 | reuse_tree_by_id_insert(&pend->reuse, w); |
787 | 0 | outnet_tcp_take_query_setup(s, pend, w); |
788 | 0 | return 1; |
789 | 0 | } |
790 | | |
791 | | /** Touch the lru of a reuse_tcp element, it is in use. |
792 | | * This moves it to the front of the list, where it is not likely to |
793 | | * be closed. Items at the back of the list are closed to make space. */ |
794 | | void |
795 | | reuse_tcp_lru_touch(struct outside_network* outnet, struct reuse_tcp* reuse) |
796 | 0 | { |
797 | 0 | if(!reuse->item_on_lru_list) { |
798 | 0 | log_err("internal error: we need to touch the lru_list but item not in list"); |
799 | 0 | return; /* not on the list, no lru to modify */ |
800 | 0 | } |
801 | 0 | log_assert(reuse->lru_prev || |
802 | 0 | (!reuse->lru_prev && outnet->tcp_reuse_first == reuse)); |
803 | 0 | if(!reuse->lru_prev) |
804 | 0 | return; /* already first in the list */ |
805 | | /* remove at current position */ |
806 | | /* since it is not first, there is a previous element */ |
807 | 0 | reuse->lru_prev->lru_next = reuse->lru_next; |
808 | 0 | log_assert(reuse->lru_prev->lru_next != reuse->lru_prev); |
809 | 0 | if(reuse->lru_next) |
810 | 0 | reuse->lru_next->lru_prev = reuse->lru_prev; |
811 | 0 | else outnet->tcp_reuse_last = reuse->lru_prev; |
812 | 0 | log_assert(!reuse->lru_next || reuse->lru_next->lru_prev != reuse->lru_next); |
813 | 0 | log_assert(outnet->tcp_reuse_last != outnet->tcp_reuse_last->lru_next && |
814 | 0 | outnet->tcp_reuse_last != outnet->tcp_reuse_last->lru_prev); |
815 | | /* insert at the front */ |
816 | 0 | reuse->lru_prev = NULL; |
817 | 0 | reuse->lru_next = outnet->tcp_reuse_first; |
818 | 0 | if(outnet->tcp_reuse_first) { |
819 | 0 | outnet->tcp_reuse_first->lru_prev = reuse; |
820 | 0 | } |
821 | 0 | log_assert(reuse->lru_next != reuse); |
822 | | /* since it is not first, it is not the only element and |
823 | | * lru_next is thus not NULL and thus reuse is now not the last in |
824 | | * the list, so outnet->tcp_reuse_last does not need to be modified */ |
825 | 0 | outnet->tcp_reuse_first = reuse; |
826 | 0 | log_assert(outnet->tcp_reuse_first != outnet->tcp_reuse_first->lru_next && |
827 | 0 | outnet->tcp_reuse_first != outnet->tcp_reuse_first->lru_prev); |
828 | 0 | log_assert((!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) || |
829 | 0 | (outnet->tcp_reuse_first && outnet->tcp_reuse_last)); |
830 | 0 | } |
831 | | |
832 | | /** Snip the last reuse_tcp element off of the LRU list */ |
833 | | struct reuse_tcp* |
834 | | reuse_tcp_lru_snip(struct outside_network* outnet) |
835 | 0 | { |
836 | 0 | struct reuse_tcp* reuse = outnet->tcp_reuse_last; |
837 | 0 | if(!reuse) return NULL; |
838 | | /* snip off of LRU */ |
839 | 0 | log_assert(reuse->lru_next == NULL); |
840 | 0 | if(reuse->lru_prev) { |
841 | 0 | outnet->tcp_reuse_last = reuse->lru_prev; |
842 | 0 | reuse->lru_prev->lru_next = NULL; |
843 | 0 | } else { |
844 | 0 | outnet->tcp_reuse_last = NULL; |
845 | 0 | outnet->tcp_reuse_first = NULL; |
846 | 0 | } |
847 | 0 | log_assert((!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) || |
848 | 0 | (outnet->tcp_reuse_first && outnet->tcp_reuse_last)); |
849 | 0 | reuse->item_on_lru_list = 0; |
850 | 0 | reuse->lru_next = NULL; |
851 | 0 | reuse->lru_prev = NULL; |
852 | 0 | return reuse; |
853 | 0 | } |
854 | | |
855 | | /** remove waiting tcp from the outnet waiting list */ |
856 | | void |
857 | | outnet_waiting_tcp_list_remove(struct outside_network* outnet, struct waiting_tcp* w) |
858 | 0 | { |
859 | 0 | struct waiting_tcp* p = outnet->tcp_wait_first, *prev = NULL; |
860 | 0 | w->on_tcp_waiting_list = 0; |
861 | 0 | while(p) { |
862 | 0 | if(p == w) { |
863 | | /* remove w */ |
864 | 0 | if(prev) |
865 | 0 | prev->next_waiting = w->next_waiting; |
866 | 0 | else outnet->tcp_wait_first = w->next_waiting; |
867 | 0 | if(outnet->tcp_wait_last == w) |
868 | 0 | outnet->tcp_wait_last = prev; |
869 | 0 | w->next_waiting = NULL; |
870 | 0 | return; |
871 | 0 | } |
872 | 0 | prev = p; |
873 | 0 | p = p->next_waiting; |
874 | 0 | } |
875 | | /* outnet_waiting_tcp_list_remove is currently called only with items |
876 | | * that are already in the waiting list. */ |
877 | 0 | log_assert(0); |
878 | 0 | } |
879 | | |
880 | | /** pop the first waiting tcp from the outnet waiting list */ |
881 | | struct waiting_tcp* |
882 | | outnet_waiting_tcp_list_pop(struct outside_network* outnet) |
883 | 0 | { |
884 | 0 | struct waiting_tcp* w = outnet->tcp_wait_first; |
885 | 0 | if(!outnet->tcp_wait_first) return NULL; |
886 | 0 | log_assert(w->on_tcp_waiting_list); |
887 | 0 | outnet->tcp_wait_first = w->next_waiting; |
888 | 0 | if(outnet->tcp_wait_last == w) |
889 | 0 | outnet->tcp_wait_last = NULL; |
890 | 0 | w->on_tcp_waiting_list = 0; |
891 | 0 | w->next_waiting = NULL; |
892 | 0 | return w; |
893 | 0 | } |
894 | | |
895 | | /** add waiting_tcp element to the outnet tcp waiting list */ |
896 | | void |
897 | | outnet_waiting_tcp_list_add(struct outside_network* outnet, |
898 | | struct waiting_tcp* w, int set_timer) |
899 | 0 | { |
900 | 0 | struct timeval tv; |
901 | 0 | log_assert(!w->on_tcp_waiting_list); |
902 | 0 | if(w->on_tcp_waiting_list) |
903 | 0 | return; |
904 | 0 | w->next_waiting = NULL; |
905 | 0 | if(outnet->tcp_wait_last) |
906 | 0 | outnet->tcp_wait_last->next_waiting = w; |
907 | 0 | else outnet->tcp_wait_first = w; |
908 | 0 | outnet->tcp_wait_last = w; |
909 | 0 | w->on_tcp_waiting_list = 1; |
910 | 0 | if(set_timer) { |
911 | 0 | #ifndef S_SPLINT_S |
912 | 0 | tv.tv_sec = w->timeout/1000; |
913 | 0 | tv.tv_usec = (w->timeout%1000)*1000; |
914 | 0 | #endif |
915 | 0 | comm_timer_set(w->timer, &tv); |
916 | 0 | } |
917 | 0 | } |
918 | | |
919 | | /** add waiting_tcp element as first to the outnet tcp waiting list */ |
920 | | void |
921 | | outnet_waiting_tcp_list_add_first(struct outside_network* outnet, |
922 | | struct waiting_tcp* w, int reset_timer) |
923 | 0 | { |
924 | 0 | struct timeval tv; |
925 | 0 | log_assert(!w->on_tcp_waiting_list); |
926 | 0 | if(w->on_tcp_waiting_list) |
927 | 0 | return; |
928 | 0 | w->next_waiting = outnet->tcp_wait_first; |
929 | 0 | log_assert(w->next_waiting != w); |
930 | 0 | if(!outnet->tcp_wait_last) |
931 | 0 | outnet->tcp_wait_last = w; |
932 | 0 | outnet->tcp_wait_first = w; |
933 | 0 | w->on_tcp_waiting_list = 1; |
934 | 0 | if(reset_timer) { |
935 | 0 | #ifndef S_SPLINT_S |
936 | 0 | tv.tv_sec = w->timeout/1000; |
937 | 0 | tv.tv_usec = (w->timeout%1000)*1000; |
938 | 0 | #endif |
939 | 0 | comm_timer_set(w->timer, &tv); |
940 | 0 | } |
941 | 0 | log_assert( |
942 | 0 | (!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) || |
943 | 0 | (outnet->tcp_reuse_first && outnet->tcp_reuse_last)); |
944 | 0 | } |
945 | | |
946 | | /** call callback on waiting_tcp, if not NULL */ |
947 | | static void |
948 | | waiting_tcp_callback(struct waiting_tcp* w, struct comm_point* c, int error, |
949 | | struct comm_reply* reply_info) |
950 | 0 | { |
951 | 0 | if(w && w->cb) { |
952 | 0 | fptr_ok(fptr_whitelist_pending_tcp(w->cb)); |
953 | 0 | (void)(*w->cb)(c, w->cb_arg, error, reply_info); |
954 | 0 | } |
955 | 0 | } |
956 | | |
957 | | /** see if buffers can be used to service TCP queries */ |
958 | | static void |
959 | | use_free_buffer(struct outside_network* outnet) |
960 | 0 | { |
961 | 0 | struct waiting_tcp* w; |
962 | 0 | while(outnet->tcp_wait_first && !outnet->want_to_quit) { |
963 | | #ifdef USE_DNSTAP |
964 | | struct pending_tcp* pend_tcp = NULL; |
965 | | #endif |
966 | 0 | struct reuse_tcp* reuse = NULL; |
967 | 0 | w = outnet_waiting_tcp_list_pop(outnet); |
968 | 0 | log_assert( |
969 | 0 | (!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) || |
970 | 0 | (outnet->tcp_reuse_first && outnet->tcp_reuse_last)); |
971 | 0 | reuse = reuse_tcp_find(outnet, &w->addr, w->addrlen, |
972 | 0 | w->ssl_upstream); |
973 | | /* re-select an ID when moving to a new TCP buffer */ |
974 | 0 | w->id = tcp_select_id(outnet, reuse); |
975 | 0 | LDNS_ID_SET(w->pkt, w->id); |
976 | 0 | if(reuse) { |
977 | 0 | log_reuse_tcp(VERB_CLIENT, "use free buffer for waiting tcp: " |
978 | 0 | "found reuse", reuse); |
979 | | #ifdef USE_DNSTAP |
980 | | pend_tcp = reuse->pending; |
981 | | #endif |
982 | 0 | reuse_tcp_lru_touch(outnet, reuse); |
983 | 0 | comm_timer_disable(w->timer); |
984 | 0 | w->next_waiting = (void*)reuse->pending; |
985 | 0 | reuse_tree_by_id_insert(reuse, w); |
986 | 0 | if(reuse->pending->query) { |
987 | | /* on the write wait list */ |
988 | 0 | reuse_write_wait_push_back(reuse, w); |
989 | 0 | } else { |
990 | | /* write straight away */ |
991 | | /* stop the timer on read of the fd */ |
992 | 0 | comm_point_stop_listening(reuse->pending->c); |
993 | 0 | reuse->pending->query = w; |
994 | 0 | outnet_tcp_take_query_setup( |
995 | 0 | reuse->pending->c->fd, reuse->pending, |
996 | 0 | w); |
997 | 0 | } |
998 | 0 | } else if(outnet->tcp_free) { |
999 | 0 | struct pending_tcp* pend = w->outnet->tcp_free; |
1000 | 0 | rbtree_init(&pend->reuse.tree_by_id, reuse_id_cmp); |
1001 | 0 | pend->reuse.pending = pend; |
1002 | 0 | memcpy(&pend->reuse.addr, &w->addr, w->addrlen); |
1003 | 0 | pend->reuse.addrlen = w->addrlen; |
1004 | 0 | if(!outnet_tcp_take_into_use(w)) { |
1005 | 0 | waiting_tcp_callback(w, NULL, NETEVENT_CLOSED, |
1006 | 0 | NULL); |
1007 | 0 | waiting_tcp_delete(w); |
1008 | | #ifdef USE_DNSTAP |
1009 | | w = NULL; |
1010 | | #endif |
1011 | 0 | } |
1012 | | #ifdef USE_DNSTAP |
1013 | | pend_tcp = pend; |
1014 | | #endif |
1015 | 0 | } else { |
1016 | | /* no reuse and no free buffer, put back at the start */ |
1017 | 0 | outnet_waiting_tcp_list_add_first(outnet, w, 0); |
1018 | 0 | break; |
1019 | 0 | } |
1020 | | #ifdef USE_DNSTAP |
1021 | | if(outnet->dtenv && pend_tcp && w && w->sq && |
1022 | | (outnet->dtenv->log_resolver_query_messages || |
1023 | | outnet->dtenv->log_forwarder_query_messages)) { |
1024 | | sldns_buffer tmp; |
1025 | | sldns_buffer_init_frm_data(&tmp, w->pkt, w->pkt_len); |
1026 | | dt_msg_send_outside_query(outnet->dtenv, &w->sq->addr, |
1027 | | &pend_tcp->pi->addr, comm_tcp, NULL, w->sq->zone, |
1028 | | w->sq->zonelen, &tmp); |
1029 | | } |
1030 | | #endif |
1031 | 0 | } |
1032 | 0 | } |
1033 | | |
1034 | | /** delete element from tree by id */ |
1035 | | static void |
1036 | | reuse_tree_by_id_delete(struct reuse_tcp* reuse, struct waiting_tcp* w) |
1037 | 0 | { |
1038 | | #ifdef UNBOUND_DEBUG |
1039 | | rbnode_type* rem; |
1040 | | #endif |
1041 | 0 | log_assert(w->id_node.key != NULL); |
1042 | | #ifdef UNBOUND_DEBUG |
1043 | | rem = |
1044 | | #else |
1045 | 0 | (void) |
1046 | 0 | #endif |
1047 | 0 | rbtree_delete(&reuse->tree_by_id, w); |
1048 | 0 | log_assert(rem); /* should have been there */ |
1049 | 0 | w->id_node.key = NULL; |
1050 | 0 | } |
1051 | | |
1052 | | /** move writewait list to go for another connection. */ |
1053 | | static void |
1054 | | reuse_move_writewait_away(struct outside_network* outnet, |
1055 | | struct pending_tcp* pend) |
1056 | 0 | { |
1057 | | /* the writewait list has not been written yet, so if the |
1058 | | * stream was closed, they have not actually been failed, only |
1059 | | * the queries written. Other queries can get written to another |
1060 | | * stream. For upstreams that do not support multiple queries |
1061 | | * and answers, the stream can get closed, and then the queries |
1062 | | * can get written on a new socket */ |
1063 | 0 | struct waiting_tcp* w; |
1064 | 0 | if(pend->query && pend->query->error_count == 0 && |
1065 | 0 | pend->c->tcp_write_pkt == pend->query->pkt && |
1066 | 0 | pend->c->tcp_write_pkt_len == pend->query->pkt_len) { |
1067 | | /* since the current query is not written, it can also |
1068 | | * move to a free buffer */ |
1069 | 0 | if(verbosity >= VERB_CLIENT && pend->query->pkt_len > 12+2+2 && |
1070 | 0 | LDNS_QDCOUNT(pend->query->pkt) > 0 && |
1071 | 0 | dname_valid(pend->query->pkt+12, pend->query->pkt_len-12)) { |
1072 | 0 | char buf[LDNS_MAX_DOMAINLEN]; |
1073 | 0 | dname_str(pend->query->pkt+12, buf); |
1074 | 0 | verbose(VERB_CLIENT, "reuse_move_writewait_away current %s %d bytes were written", |
1075 | 0 | buf, (int)pend->c->tcp_write_byte_count); |
1076 | 0 | } |
1077 | 0 | pend->c->tcp_write_pkt = NULL; |
1078 | 0 | pend->c->tcp_write_pkt_len = 0; |
1079 | 0 | pend->c->tcp_write_and_read = 0; |
1080 | 0 | pend->reuse.cp_more_read_again = 0; |
1081 | 0 | pend->reuse.cp_more_write_again = 0; |
1082 | 0 | pend->c->tcp_is_reading = 1; |
1083 | 0 | w = pend->query; |
1084 | 0 | pend->query = NULL; |
1085 | | /* increase error count, so that if the next socket fails too |
1086 | | * the server selection is run again with this query failed |
1087 | | * and it can select a different server (if possible), or |
1088 | | * fail the query */ |
1089 | 0 | w->error_count ++; |
1090 | 0 | reuse_tree_by_id_delete(&pend->reuse, w); |
1091 | 0 | outnet_waiting_tcp_list_add(outnet, w, 1); |
1092 | 0 | } |
1093 | 0 | while((w = reuse_write_wait_pop(&pend->reuse)) != NULL) { |
1094 | 0 | if(verbosity >= VERB_CLIENT && w->pkt_len > 12+2+2 && |
1095 | 0 | LDNS_QDCOUNT(w->pkt) > 0 && |
1096 | 0 | dname_valid(w->pkt+12, w->pkt_len-12)) { |
1097 | 0 | char buf[LDNS_MAX_DOMAINLEN]; |
1098 | 0 | dname_str(w->pkt+12, buf); |
1099 | 0 | verbose(VERB_CLIENT, "reuse_move_writewait_away item %s", buf); |
1100 | 0 | } |
1101 | 0 | reuse_tree_by_id_delete(&pend->reuse, w); |
1102 | 0 | outnet_waiting_tcp_list_add(outnet, w, 1); |
1103 | 0 | } |
1104 | 0 | } |
1105 | | |
1106 | | /** remove reused element from tree and lru list */ |
1107 | | void |
1108 | | reuse_tcp_remove_tree_list(struct outside_network* outnet, |
1109 | | struct reuse_tcp* reuse) |
1110 | 0 | { |
1111 | 0 | verbose(VERB_CLIENT, "reuse_tcp_remove_tree_list"); |
1112 | 0 | if(reuse->node.key) { |
1113 | | /* delete it from reuse tree */ |
1114 | 0 | if(!rbtree_delete(&outnet->tcp_reuse, reuse)) { |
1115 | | /* should not be possible, it should be there */ |
1116 | 0 | char buf[256]; |
1117 | 0 | addr_to_str(&reuse->addr, reuse->addrlen, buf, |
1118 | 0 | sizeof(buf)); |
1119 | 0 | log_err("reuse tcp delete: node not present, internal error, %s ssl %d lru %d", buf, reuse->is_ssl, reuse->item_on_lru_list); |
1120 | 0 | } |
1121 | 0 | reuse->node.key = NULL; |
1122 | | /* defend against loops on broken tree by zeroing the |
1123 | | * rbnode structure */ |
1124 | 0 | memset(&reuse->node, 0, sizeof(reuse->node)); |
1125 | 0 | } |
1126 | | /* delete from reuse list */ |
1127 | 0 | if(reuse->item_on_lru_list) { |
1128 | 0 | if(reuse->lru_prev) { |
1129 | | /* assert that members of the lru list are waiting |
1130 | | * and thus have a pending pointer to the struct */ |
1131 | 0 | log_assert(reuse->lru_prev->pending); |
1132 | 0 | reuse->lru_prev->lru_next = reuse->lru_next; |
1133 | 0 | log_assert(reuse->lru_prev->lru_next != reuse->lru_prev); |
1134 | 0 | } else { |
1135 | 0 | log_assert(!reuse->lru_next || reuse->lru_next->pending); |
1136 | 0 | outnet->tcp_reuse_first = reuse->lru_next; |
1137 | 0 | log_assert(!outnet->tcp_reuse_first || |
1138 | 0 | (outnet->tcp_reuse_first != |
1139 | 0 | outnet->tcp_reuse_first->lru_next && |
1140 | 0 | outnet->tcp_reuse_first != |
1141 | 0 | outnet->tcp_reuse_first->lru_prev)); |
1142 | 0 | } |
1143 | 0 | if(reuse->lru_next) { |
1144 | | /* assert that members of the lru list are waiting |
1145 | | * and thus have a pending pointer to the struct */ |
1146 | 0 | log_assert(reuse->lru_next->pending); |
1147 | 0 | reuse->lru_next->lru_prev = reuse->lru_prev; |
1148 | 0 | log_assert(reuse->lru_next->lru_prev != reuse->lru_next); |
1149 | 0 | } else { |
1150 | 0 | log_assert(!reuse->lru_prev || reuse->lru_prev->pending); |
1151 | 0 | outnet->tcp_reuse_last = reuse->lru_prev; |
1152 | 0 | log_assert(!outnet->tcp_reuse_last || |
1153 | 0 | (outnet->tcp_reuse_last != |
1154 | 0 | outnet->tcp_reuse_last->lru_next && |
1155 | 0 | outnet->tcp_reuse_last != |
1156 | 0 | outnet->tcp_reuse_last->lru_prev)); |
1157 | 0 | } |
1158 | 0 | log_assert((!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) || |
1159 | 0 | (outnet->tcp_reuse_first && outnet->tcp_reuse_last)); |
1160 | 0 | reuse->item_on_lru_list = 0; |
1161 | 0 | reuse->lru_next = NULL; |
1162 | 0 | reuse->lru_prev = NULL; |
1163 | 0 | } |
1164 | 0 | reuse->pending = NULL; |
1165 | 0 | } |
1166 | | |
1167 | | /** helper function that deletes an element from the tree of readwait |
1168 | | * elements in tcp reuse structure */ |
1169 | | static void reuse_del_readwait_elem(rbnode_type* node, void* ATTR_UNUSED(arg)) |
1170 | 0 | { |
1171 | 0 | struct waiting_tcp* w = (struct waiting_tcp*)node->key; |
1172 | 0 | waiting_tcp_delete(w); |
1173 | 0 | } |
1174 | | |
1175 | | /** delete readwait waiting_tcp elements, deletes the elements in the list */ |
1176 | | void reuse_del_readwait(rbtree_type* tree_by_id) |
1177 | 0 | { |
1178 | 0 | if(tree_by_id->root == NULL || |
1179 | 0 | tree_by_id->root == RBTREE_NULL) |
1180 | 0 | return; |
1181 | 0 | traverse_postorder(tree_by_id, &reuse_del_readwait_elem, NULL); |
1182 | 0 | rbtree_init(tree_by_id, reuse_id_cmp); |
1183 | 0 | } |
1184 | | |
1185 | | /** decommission a tcp buffer, closes commpoint and frees waiting_tcp entry */ |
1186 | | static void |
1187 | | decommission_pending_tcp(struct outside_network* outnet, |
1188 | | struct pending_tcp* pend) |
1189 | 0 | { |
1190 | 0 | verbose(VERB_CLIENT, "decommission_pending_tcp"); |
1191 | | /* A certain code path can lead here twice for the same pending_tcp |
1192 | | * creating a loop in the free pending_tcp list. */ |
1193 | 0 | if(outnet->tcp_free != pend) { |
1194 | 0 | pend->next_free = outnet->tcp_free; |
1195 | 0 | outnet->tcp_free = pend; |
1196 | 0 | } |
1197 | 0 | if(pend->reuse.node.key) { |
1198 | | /* needs unlink from the reuse tree to get deleted */ |
1199 | 0 | reuse_tcp_remove_tree_list(outnet, &pend->reuse); |
1200 | 0 | } |
1201 | | /* free SSL structure after remove from outnet tcp reuse tree, |
1202 | | * because the c->ssl null or not is used for sorting in the tree */ |
1203 | 0 | if(pend->c->ssl) { |
1204 | 0 | #ifdef HAVE_SSL |
1205 | 0 | SSL_shutdown(pend->c->ssl); |
1206 | 0 | SSL_free(pend->c->ssl); |
1207 | 0 | pend->c->ssl = NULL; |
1208 | 0 | #endif |
1209 | 0 | } |
1210 | 0 | comm_point_close(pend->c); |
1211 | 0 | pend->reuse.cp_more_read_again = 0; |
1212 | 0 | pend->reuse.cp_more_write_again = 0; |
1213 | | /* unlink the query and writewait list, it is part of the tree |
1214 | | * nodes and is deleted */ |
1215 | 0 | pend->query = NULL; |
1216 | 0 | pend->reuse.write_wait_first = NULL; |
1217 | 0 | pend->reuse.write_wait_last = NULL; |
1218 | 0 | reuse_del_readwait(&pend->reuse.tree_by_id); |
1219 | 0 | } |
1220 | | |
1221 | | /** perform failure callbacks for waiting queries in reuse read rbtree */ |
1222 | | static void reuse_cb_readwait_for_failure(rbtree_type* tree_by_id, int err) |
1223 | 0 | { |
1224 | 0 | rbnode_type* node; |
1225 | 0 | if(tree_by_id->root == NULL || |
1226 | 0 | tree_by_id->root == RBTREE_NULL) |
1227 | 0 | return; |
1228 | 0 | node = rbtree_first(tree_by_id); |
1229 | 0 | while(node && node != RBTREE_NULL) { |
1230 | 0 | struct waiting_tcp* w = (struct waiting_tcp*)node->key; |
1231 | 0 | waiting_tcp_callback(w, NULL, err, NULL); |
1232 | 0 | node = rbtree_next(node); |
1233 | 0 | } |
1234 | 0 | } |
1235 | | |
1236 | | /** mark the entry for being in the cb_and_decommission stage */ |
1237 | | static void mark_for_cb_and_decommission(rbnode_type* node, |
1238 | | void* ATTR_UNUSED(arg)) |
1239 | 0 | { |
1240 | 0 | struct waiting_tcp* w = (struct waiting_tcp*)node->key; |
1241 | | /* Mark the waiting_tcp to signal later code (serviced_delete) that |
1242 | | * this item is part of the backed up tree_by_id and will be deleted |
1243 | | * later. */ |
1244 | 0 | w->in_cb_and_decommission = 1; |
1245 | | /* Mark the serviced_query for deletion so that later code through |
1246 | | * callbacks (iter_clear .. outnet_serviced_query_stop) won't |
1247 | | * prematurely delete it. */ |
1248 | 0 | if(w->cb) |
1249 | 0 | ((struct serviced_query*)w->cb_arg)->to_be_deleted = 1; |
1250 | 0 | } |
1251 | | |
1252 | | /** perform callbacks for failure and also decommission pending tcp. |
1253 | | * the callbacks remove references in sq->pending to the waiting_tcp |
1254 | | * members of the tree_by_id in the pending tcp. The pending_tcp is |
1255 | | * removed before the callbacks, so that the callbacks do not modify |
1256 | | * the pending_tcp due to its reference in the outside_network reuse tree */ |
1257 | | static void reuse_cb_and_decommission(struct outside_network* outnet, |
1258 | | struct pending_tcp* pend, int error) |
1259 | 0 | { |
1260 | 0 | rbtree_type store; |
1261 | 0 | store = pend->reuse.tree_by_id; |
1262 | 0 | pend->query = NULL; |
1263 | 0 | rbtree_init(&pend->reuse.tree_by_id, reuse_id_cmp); |
1264 | 0 | pend->reuse.write_wait_first = NULL; |
1265 | 0 | pend->reuse.write_wait_last = NULL; |
1266 | 0 | decommission_pending_tcp(outnet, pend); |
1267 | 0 | if(store.root != NULL && store.root != RBTREE_NULL) { |
1268 | 0 | traverse_postorder(&store, &mark_for_cb_and_decommission, NULL); |
1269 | 0 | } |
1270 | 0 | reuse_cb_readwait_for_failure(&store, error); |
1271 | 0 | reuse_del_readwait(&store); |
1272 | 0 | } |
1273 | | |
1274 | | /** set timeout on tcp fd and setup read event to catch incoming dns msgs */ |
1275 | | static void |
1276 | | reuse_tcp_setup_timeout(struct pending_tcp* pend_tcp, int tcp_reuse_timeout) |
1277 | 0 | { |
1278 | 0 | log_reuse_tcp(VERB_CLIENT, "reuse_tcp_setup_timeout", &pend_tcp->reuse); |
1279 | 0 | comm_point_start_listening(pend_tcp->c, -1, tcp_reuse_timeout); |
1280 | 0 | } |
1281 | | |
1282 | | /** set timeout on tcp fd and setup read event to catch incoming dns msgs */ |
1283 | | static void |
1284 | | reuse_tcp_setup_read_and_timeout(struct pending_tcp* pend_tcp, int tcp_reuse_timeout) |
1285 | 0 | { |
1286 | 0 | log_reuse_tcp(VERB_CLIENT, "reuse_tcp_setup_readtimeout", &pend_tcp->reuse); |
1287 | 0 | sldns_buffer_clear(pend_tcp->c->buffer); |
1288 | 0 | pend_tcp->c->tcp_is_reading = 1; |
1289 | 0 | pend_tcp->c->tcp_byte_count = 0; |
1290 | 0 | comm_point_stop_listening(pend_tcp->c); |
1291 | 0 | comm_point_start_listening(pend_tcp->c, -1, tcp_reuse_timeout); |
1292 | 0 | } |
1293 | | |
1294 | | int |
1295 | | outnet_tcp_cb(struct comm_point* c, void* arg, int error, |
1296 | | struct comm_reply *reply_info) |
1297 | 0 | { |
1298 | 0 | struct pending_tcp* pend = (struct pending_tcp*)arg; |
1299 | 0 | struct outside_network* outnet = pend->reuse.outnet; |
1300 | 0 | struct waiting_tcp* w = NULL; |
1301 | 0 | log_assert(pend->reuse.item_on_lru_list && pend->reuse.node.key); |
1302 | 0 | verbose(VERB_ALGO, "outnettcp cb"); |
1303 | 0 | if(error == NETEVENT_TIMEOUT) { |
1304 | 0 | if(pend->c->tcp_write_and_read) { |
1305 | 0 | verbose(VERB_QUERY, "outnettcp got tcp timeout " |
1306 | 0 | "for read, ignored because write underway"); |
1307 | | /* if we are writing, ignore readtimer, wait for write timer |
1308 | | * or write is done */ |
1309 | 0 | return 0; |
1310 | 0 | } else { |
1311 | 0 | verbose(VERB_QUERY, "outnettcp got tcp timeout %s", |
1312 | 0 | (pend->reuse.tree_by_id.count?"for reading pkt": |
1313 | 0 | "for keepalive for reuse")); |
1314 | 0 | } |
1315 | | /* must be timeout for reading or keepalive reuse, |
1316 | | * close it. */ |
1317 | 0 | reuse_tcp_remove_tree_list(outnet, &pend->reuse); |
1318 | 0 | } else if(error == NETEVENT_PKT_WRITTEN) { |
1319 | | /* the packet we want to write has been written. */ |
1320 | 0 | verbose(VERB_ALGO, "outnet tcp pkt was written event"); |
1321 | 0 | log_assert(c == pend->c); |
1322 | 0 | log_assert(pend->query->pkt == pend->c->tcp_write_pkt); |
1323 | 0 | log_assert(pend->query->pkt_len == pend->c->tcp_write_pkt_len); |
1324 | 0 | pend->c->tcp_write_pkt = NULL; |
1325 | 0 | pend->c->tcp_write_pkt_len = 0; |
1326 | | /* the pend.query is already in tree_by_id */ |
1327 | 0 | log_assert(pend->query->id_node.key); |
1328 | 0 | pend->query = NULL; |
1329 | | /* setup to write next packet or setup read timeout */ |
1330 | 0 | if(pend->reuse.write_wait_first) { |
1331 | 0 | verbose(VERB_ALGO, "outnet tcp setup next pkt"); |
1332 | | /* we can write it straight away perhaps, set flag |
1333 | | * because this callback called after a tcp write |
1334 | | * succeeded and likely more buffer space is available |
1335 | | * and we can write some more. */ |
1336 | 0 | pend->reuse.cp_more_write_again = 1; |
1337 | 0 | pend->query = reuse_write_wait_pop(&pend->reuse); |
1338 | 0 | comm_point_stop_listening(pend->c); |
1339 | 0 | outnet_tcp_take_query_setup(pend->c->fd, pend, |
1340 | 0 | pend->query); |
1341 | 0 | } else { |
1342 | 0 | verbose(VERB_ALGO, "outnet tcp writes done, wait"); |
1343 | 0 | pend->c->tcp_write_and_read = 0; |
1344 | 0 | pend->reuse.cp_more_read_again = 0; |
1345 | 0 | pend->reuse.cp_more_write_again = 0; |
1346 | 0 | pend->c->tcp_is_reading = 1; |
1347 | 0 | comm_point_stop_listening(pend->c); |
1348 | 0 | reuse_tcp_setup_timeout(pend, outnet->tcp_reuse_timeout); |
1349 | 0 | } |
1350 | 0 | return 0; |
1351 | 0 | } else if(error != NETEVENT_NOERROR) { |
1352 | 0 | verbose(VERB_QUERY, "outnettcp got tcp error %d", error); |
1353 | 0 | reuse_move_writewait_away(outnet, pend); |
1354 | | /* pass error below and exit */ |
1355 | 0 | } else { |
1356 | | /* check ID */ |
1357 | 0 | if(sldns_buffer_limit(c->buffer) < sizeof(uint16_t)) { |
1358 | 0 | log_addr(VERB_QUERY, |
1359 | 0 | "outnettcp: bad ID in reply, too short, from:", |
1360 | 0 | &pend->reuse.addr, pend->reuse.addrlen); |
1361 | 0 | error = NETEVENT_CLOSED; |
1362 | 0 | } else { |
1363 | 0 | uint16_t id = LDNS_ID_WIRE(sldns_buffer_begin( |
1364 | 0 | c->buffer)); |
1365 | | /* find the query the reply is for */ |
1366 | 0 | w = reuse_tcp_by_id_find(&pend->reuse, id); |
1367 | | /* Make sure that the reply we got is at least for a |
1368 | | * sent query with the same ID; the waiting_tcp that |
1369 | | * gets a reply is assumed to not be waiting to be |
1370 | | * sent. */ |
1371 | 0 | if(w && (w->on_tcp_waiting_list || w->write_wait_queued)) |
1372 | 0 | w = NULL; |
1373 | 0 | } |
1374 | 0 | } |
1375 | 0 | if(error == NETEVENT_NOERROR && !w) { |
1376 | | /* no struct waiting found in tree, no reply to call */ |
1377 | 0 | log_addr(VERB_QUERY, "outnettcp: bad ID in reply, from:", |
1378 | 0 | &pend->reuse.addr, pend->reuse.addrlen); |
1379 | 0 | error = NETEVENT_CLOSED; |
1380 | 0 | } |
1381 | 0 | if(error == NETEVENT_NOERROR) { |
1382 | | /* add to reuse tree so it can be reused, if not a failure. |
1383 | | * This is possible if the state machine wants to make a tcp |
1384 | | * query again to the same destination. */ |
1385 | 0 | if(outnet->tcp_reuse.count < outnet->tcp_reuse_max) { |
1386 | 0 | (void)reuse_tcp_insert(outnet, pend); |
1387 | 0 | } |
1388 | 0 | } |
1389 | 0 | if(w) { |
1390 | 0 | log_assert(!w->on_tcp_waiting_list); |
1391 | 0 | log_assert(!w->write_wait_queued); |
1392 | 0 | reuse_tree_by_id_delete(&pend->reuse, w); |
1393 | 0 | verbose(VERB_CLIENT, "outnet tcp callback query err %d buflen %d", |
1394 | 0 | error, (int)sldns_buffer_limit(c->buffer)); |
1395 | 0 | waiting_tcp_callback(w, c, error, reply_info); |
1396 | 0 | waiting_tcp_delete(w); |
1397 | 0 | } |
1398 | 0 | verbose(VERB_CLIENT, "outnet_tcp_cb reuse after cb"); |
1399 | 0 | if(error == NETEVENT_NOERROR && pend->reuse.node.key) { |
1400 | 0 | verbose(VERB_CLIENT, "outnet_tcp_cb reuse after cb: keep it"); |
1401 | | /* it is in the reuse_tcp tree, with other queries, or |
1402 | | * on the empty list. do not decommission it */ |
1403 | | /* if there are more outstanding queries, we could try to |
1404 | | * read again, to see if it is on the input, |
1405 | | * because this callback called after a successful read |
1406 | | * and there could be more bytes to read on the input */ |
1407 | 0 | if(pend->reuse.tree_by_id.count != 0) |
1408 | 0 | pend->reuse.cp_more_read_again = 1; |
1409 | 0 | reuse_tcp_setup_read_and_timeout(pend, outnet->tcp_reuse_timeout); |
1410 | 0 | return 0; |
1411 | 0 | } |
1412 | 0 | verbose(VERB_CLIENT, "outnet_tcp_cb reuse after cb: decommission it"); |
1413 | | /* no queries on it, no space to keep it. or timeout or closed due |
1414 | | * to error. Close it */ |
1415 | 0 | reuse_cb_and_decommission(outnet, pend, (error==NETEVENT_TIMEOUT? |
1416 | 0 | NETEVENT_TIMEOUT:NETEVENT_CLOSED)); |
1417 | 0 | use_free_buffer(outnet); |
1418 | 0 | return 0; |
1419 | 0 | } |
1420 | | |
1421 | | /** lower use count on pc, see if it can be closed */ |
1422 | | static void |
1423 | | portcomm_loweruse(struct outside_network* outnet, struct port_comm* pc) |
1424 | 0 | { |
1425 | 0 | struct port_if* pif; |
1426 | 0 | pc->num_outstanding--; |
1427 | 0 | if(pc->num_outstanding > 0) { |
1428 | 0 | return; |
1429 | 0 | } |
1430 | | /* close it and replace in unused list */ |
1431 | 0 | verbose(VERB_ALGO, "close of port %d", pc->number); |
1432 | 0 | comm_point_close(pc->cp); |
1433 | 0 | pif = pc->pif; |
1434 | 0 | log_assert(pif->inuse > 0); |
1435 | 0 | #ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION |
1436 | 0 | pif->avail_ports[pif->avail_total - pif->inuse] = pc->number; |
1437 | 0 | #endif |
1438 | 0 | pif->inuse--; |
1439 | 0 | pif->out[pc->index] = pif->out[pif->inuse]; |
1440 | 0 | pif->out[pc->index]->index = pc->index; |
1441 | 0 | pc->next = outnet->unused_fds; |
1442 | 0 | outnet->unused_fds = pc; |
1443 | 0 | } |
1444 | | |
1445 | | /** try to send waiting UDP queries */ |
1446 | | static void |
1447 | | outnet_send_wait_udp(struct outside_network* outnet) |
1448 | 0 | { |
1449 | 0 | struct pending* pend; |
1450 | | /* process waiting queries */ |
1451 | 0 | while(outnet->udp_wait_first && outnet->unused_fds |
1452 | 0 | && !outnet->want_to_quit) { |
1453 | 0 | pend = outnet->udp_wait_first; |
1454 | 0 | outnet->udp_wait_first = pend->next_waiting; |
1455 | 0 | if(!pend->next_waiting) outnet->udp_wait_last = NULL; |
1456 | 0 | sldns_buffer_clear(outnet->udp_buff); |
1457 | 0 | sldns_buffer_write(outnet->udp_buff, pend->pkt, pend->pkt_len); |
1458 | 0 | sldns_buffer_flip(outnet->udp_buff); |
1459 | 0 | free(pend->pkt); /* freeing now makes get_mem correct */ |
1460 | 0 | pend->pkt = NULL; |
1461 | 0 | pend->pkt_len = 0; |
1462 | 0 | log_assert(!pend->sq->busy); |
1463 | 0 | pend->sq->busy = 1; |
1464 | 0 | if(!randomize_and_send_udp(pend, outnet->udp_buff, |
1465 | 0 | pend->timeout)) { |
1466 | | /* callback error on pending */ |
1467 | 0 | if(pend->cb) { |
1468 | 0 | fptr_ok(fptr_whitelist_pending_udp(pend->cb)); |
1469 | 0 | (void)(*pend->cb)(outnet->unused_fds->cp, pend->cb_arg, |
1470 | 0 | NETEVENT_CLOSED, NULL); |
1471 | 0 | } |
1472 | 0 | pending_delete(outnet, pend); |
1473 | 0 | } else { |
1474 | 0 | pend->sq->busy = 0; |
1475 | 0 | } |
1476 | 0 | } |
1477 | 0 | } |
1478 | | |
1479 | | int |
1480 | | outnet_udp_cb(struct comm_point* c, void* arg, int error, |
1481 | | struct comm_reply *reply_info) |
1482 | 0 | { |
1483 | 0 | struct outside_network* outnet = (struct outside_network*)arg; |
1484 | 0 | struct pending key; |
1485 | 0 | struct pending* p; |
1486 | 0 | verbose(VERB_ALGO, "answer cb"); |
1487 | |
|
1488 | 0 | if(error != NETEVENT_NOERROR) { |
1489 | 0 | verbose(VERB_QUERY, "outnetudp got udp error %d", error); |
1490 | 0 | return 0; |
1491 | 0 | } |
1492 | 0 | if(sldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) { |
1493 | 0 | verbose(VERB_QUERY, "outnetudp udp too short"); |
1494 | 0 | return 0; |
1495 | 0 | } |
1496 | 0 | log_assert(reply_info); |
1497 | | |
1498 | | /* setup lookup key */ |
1499 | 0 | key.id = (unsigned)LDNS_ID_WIRE(sldns_buffer_begin(c->buffer)); |
1500 | 0 | memcpy(&key.addr, &reply_info->remote_addr, reply_info->remote_addrlen); |
1501 | 0 | key.addrlen = reply_info->remote_addrlen; |
1502 | 0 | verbose(VERB_ALGO, "Incoming reply id = %4.4x", key.id); |
1503 | 0 | log_addr(VERB_ALGO, "Incoming reply addr =", |
1504 | 0 | &reply_info->remote_addr, reply_info->remote_addrlen); |
1505 | | |
1506 | | /* find it, see if this thing is a valid query response */ |
1507 | 0 | verbose(VERB_ALGO, "lookup size is %d entries", (int)outnet->pending->count); |
1508 | 0 | p = (struct pending*)rbtree_search(outnet->pending, &key); |
1509 | 0 | if(!p) { |
1510 | 0 | verbose(VERB_QUERY, "received unwanted or unsolicited udp reply dropped."); |
1511 | 0 | log_buf(VERB_ALGO, "dropped message", c->buffer); |
1512 | 0 | outnet->unwanted_replies++; |
1513 | 0 | if(outnet->unwanted_threshold && ++outnet->unwanted_total |
1514 | 0 | >= outnet->unwanted_threshold) { |
1515 | 0 | log_warn("unwanted reply total reached threshold (%u)" |
1516 | 0 | " you may be under attack." |
1517 | 0 | " defensive action: clearing the cache", |
1518 | 0 | (unsigned)outnet->unwanted_threshold); |
1519 | 0 | fptr_ok(fptr_whitelist_alloc_cleanup( |
1520 | 0 | outnet->unwanted_action)); |
1521 | 0 | (*outnet->unwanted_action)(outnet->unwanted_param); |
1522 | 0 | outnet->unwanted_total = 0; |
1523 | 0 | } |
1524 | 0 | return 0; |
1525 | 0 | } |
1526 | | |
1527 | 0 | verbose(VERB_ALGO, "received udp reply."); |
1528 | 0 | log_buf(VERB_ALGO, "udp message", c->buffer); |
1529 | 0 | if(p->pc->cp != c) { |
1530 | 0 | verbose(VERB_QUERY, "received reply id,addr on wrong port. " |
1531 | 0 | "dropped."); |
1532 | 0 | outnet->unwanted_replies++; |
1533 | 0 | if(outnet->unwanted_threshold && ++outnet->unwanted_total |
1534 | 0 | >= outnet->unwanted_threshold) { |
1535 | 0 | log_warn("unwanted reply total reached threshold (%u)" |
1536 | 0 | " you may be under attack." |
1537 | 0 | " defensive action: clearing the cache", |
1538 | 0 | (unsigned)outnet->unwanted_threshold); |
1539 | 0 | fptr_ok(fptr_whitelist_alloc_cleanup( |
1540 | 0 | outnet->unwanted_action)); |
1541 | 0 | (*outnet->unwanted_action)(outnet->unwanted_param); |
1542 | 0 | outnet->unwanted_total = 0; |
1543 | 0 | } |
1544 | 0 | return 0; |
1545 | 0 | } |
1546 | 0 | comm_timer_disable(p->timer); |
1547 | 0 | verbose(VERB_ALGO, "outnet handle udp reply"); |
1548 | | /* delete from tree first in case callback creates a retry */ |
1549 | 0 | (void)rbtree_delete(outnet->pending, p->node.key); |
1550 | 0 | if(p->cb) { |
1551 | 0 | fptr_ok(fptr_whitelist_pending_udp(p->cb)); |
1552 | 0 | (void)(*p->cb)(p->pc->cp, p->cb_arg, NETEVENT_NOERROR, reply_info); |
1553 | 0 | } |
1554 | 0 | portcomm_loweruse(outnet, p->pc); |
1555 | 0 | pending_delete(NULL, p); |
1556 | 0 | outnet_send_wait_udp(outnet); |
1557 | 0 | return 0; |
1558 | 0 | } |
1559 | | |
1560 | | /** calculate number of ip4 and ip6 interfaces*/ |
1561 | | static void |
1562 | | calc_num46(char** ifs, int num_ifs, int do_ip4, int do_ip6, |
1563 | | int* num_ip4, int* num_ip6) |
1564 | 0 | { |
1565 | 0 | int i; |
1566 | 0 | *num_ip4 = 0; |
1567 | 0 | *num_ip6 = 0; |
1568 | 0 | if(num_ifs <= 0) { |
1569 | 0 | if(do_ip4) |
1570 | 0 | *num_ip4 = 1; |
1571 | 0 | if(do_ip6) |
1572 | 0 | *num_ip6 = 1; |
1573 | 0 | return; |
1574 | 0 | } |
1575 | 0 | for(i=0; i<num_ifs; i++) |
1576 | 0 | { |
1577 | 0 | if(str_is_ip6(ifs[i])) { |
1578 | 0 | if(do_ip6) |
1579 | 0 | (*num_ip6)++; |
1580 | 0 | } else { |
1581 | 0 | if(do_ip4) |
1582 | 0 | (*num_ip4)++; |
1583 | 0 | } |
1584 | 0 | } |
1585 | 0 | } |
1586 | | |
1587 | | void |
1588 | | pending_udp_timer_delay_cb(void* arg) |
1589 | 0 | { |
1590 | 0 | struct pending* p = (struct pending*)arg; |
1591 | 0 | struct outside_network* outnet = p->outnet; |
1592 | 0 | verbose(VERB_ALGO, "timeout udp with delay"); |
1593 | 0 | portcomm_loweruse(outnet, p->pc); |
1594 | 0 | pending_delete(outnet, p); |
1595 | 0 | outnet_send_wait_udp(outnet); |
1596 | 0 | } |
1597 | | |
1598 | | void |
1599 | | pending_udp_timer_cb(void *arg) |
1600 | 0 | { |
1601 | 0 | struct pending* p = (struct pending*)arg; |
1602 | 0 | struct outside_network* outnet = p->outnet; |
1603 | | /* it timed out */ |
1604 | 0 | verbose(VERB_ALGO, "timeout udp"); |
1605 | 0 | if(p->cb) { |
1606 | 0 | fptr_ok(fptr_whitelist_pending_udp(p->cb)); |
1607 | 0 | (void)(*p->cb)(p->pc->cp, p->cb_arg, NETEVENT_TIMEOUT, NULL); |
1608 | 0 | } |
1609 | | /* if delayclose, keep port open for a longer time. |
1610 | | * But if the udpwaitlist exists, then we are struggling to |
1611 | | * keep up with demand for sockets, so do not wait, but service |
1612 | | * the customer (customer service more important than portICMPs) */ |
1613 | 0 | if(outnet->delayclose && !outnet->udp_wait_first) { |
1614 | 0 | p->cb = NULL; |
1615 | 0 | p->timer->callback = &pending_udp_timer_delay_cb; |
1616 | 0 | comm_timer_set(p->timer, &outnet->delay_tv); |
1617 | 0 | return; |
1618 | 0 | } |
1619 | 0 | portcomm_loweruse(outnet, p->pc); |
1620 | 0 | pending_delete(outnet, p); |
1621 | 0 | outnet_send_wait_udp(outnet); |
1622 | 0 | } |
1623 | | |
1624 | | /** create pending_tcp buffers */ |
1625 | | static int |
1626 | | create_pending_tcp(struct outside_network* outnet, size_t bufsize) |
1627 | 0 | { |
1628 | 0 | size_t i; |
1629 | 0 | if(outnet->num_tcp == 0) |
1630 | 0 | return 1; /* no tcp needed, nothing to do */ |
1631 | 0 | if(!(outnet->tcp_conns = (struct pending_tcp **)calloc( |
1632 | 0 | outnet->num_tcp, sizeof(struct pending_tcp*)))) |
1633 | 0 | return 0; |
1634 | 0 | for(i=0; i<outnet->num_tcp; i++) { |
1635 | 0 | if(!(outnet->tcp_conns[i] = (struct pending_tcp*)calloc(1, |
1636 | 0 | sizeof(struct pending_tcp)))) |
1637 | 0 | return 0; |
1638 | 0 | outnet->tcp_conns[i]->next_free = outnet->tcp_free; |
1639 | 0 | outnet->tcp_free = outnet->tcp_conns[i]; |
1640 | 0 | outnet->tcp_conns[i]->c = comm_point_create_tcp_out( |
1641 | 0 | outnet->base, bufsize, outnet_tcp_cb, |
1642 | 0 | outnet->tcp_conns[i]); |
1643 | 0 | if(!outnet->tcp_conns[i]->c) |
1644 | 0 | return 0; |
1645 | 0 | } |
1646 | 0 | return 1; |
1647 | 0 | } |
1648 | | |
1649 | | /** setup an outgoing interface, ready address */ |
1650 | | static int setup_if(struct port_if* pif, const char* addrstr, |
1651 | | int* avail, int numavail, size_t numfd) |
1652 | 0 | { |
1653 | 0 | #ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION |
1654 | 0 | pif->avail_total = numavail; |
1655 | 0 | pif->avail_ports = (int*)memdup(avail, (size_t)numavail*sizeof(int)); |
1656 | 0 | if(!pif->avail_ports) |
1657 | 0 | return 0; |
1658 | 0 | #endif |
1659 | 0 | if(!ipstrtoaddr(addrstr, UNBOUND_DNS_PORT, &pif->addr, &pif->addrlen) && |
1660 | 0 | !netblockstrtoaddr(addrstr, UNBOUND_DNS_PORT, |
1661 | 0 | &pif->addr, &pif->addrlen, &pif->pfxlen)) |
1662 | 0 | return 0; |
1663 | 0 | pif->maxout = (int)numfd; |
1664 | 0 | pif->inuse = 0; |
1665 | 0 | pif->out = (struct port_comm**)calloc(numfd, |
1666 | 0 | sizeof(struct port_comm*)); |
1667 | 0 | if(!pif->out) |
1668 | 0 | return 0; |
1669 | 0 | return 1; |
1670 | 0 | } |
1671 | | |
1672 | | struct outside_network* |
1673 | | outside_network_create(struct comm_base *base, size_t bufsize, |
1674 | | size_t num_ports, char** ifs, int num_ifs, int do_ip4, |
1675 | | int do_ip6, size_t num_tcp, int dscp, struct infra_cache* infra, |
1676 | | struct ub_randstate* rnd, int use_caps_for_id, int* availports, |
1677 | | int numavailports, size_t unwanted_threshold, int tcp_mss, |
1678 | | void (*unwanted_action)(void*), void* unwanted_param, int do_udp, |
1679 | | void* sslctx, int delayclose, int tls_use_sni, struct dt_env* dtenv, |
1680 | | int udp_connect, int max_reuse_tcp_queries, int tcp_reuse_timeout, |
1681 | | int tcp_auth_query_timeout) |
1682 | 0 | { |
1683 | 0 | struct outside_network* outnet = (struct outside_network*) |
1684 | 0 | calloc(1, sizeof(struct outside_network)); |
1685 | 0 | size_t k; |
1686 | 0 | if(!outnet) { |
1687 | 0 | log_err("malloc failed"); |
1688 | 0 | return NULL; |
1689 | 0 | } |
1690 | 0 | comm_base_timept(base, &outnet->now_secs, &outnet->now_tv); |
1691 | 0 | outnet->base = base; |
1692 | 0 | outnet->num_tcp = num_tcp; |
1693 | 0 | outnet->max_reuse_tcp_queries = max_reuse_tcp_queries; |
1694 | 0 | outnet->tcp_reuse_timeout= tcp_reuse_timeout; |
1695 | 0 | outnet->tcp_auth_query_timeout = tcp_auth_query_timeout; |
1696 | 0 | outnet->num_tcp_outgoing = 0; |
1697 | 0 | outnet->num_udp_outgoing = 0; |
1698 | 0 | outnet->infra = infra; |
1699 | 0 | outnet->rnd = rnd; |
1700 | 0 | outnet->sslctx = sslctx; |
1701 | 0 | outnet->tls_use_sni = tls_use_sni; |
1702 | | #ifdef USE_DNSTAP |
1703 | | outnet->dtenv = dtenv; |
1704 | | #else |
1705 | 0 | (void)dtenv; |
1706 | 0 | #endif |
1707 | 0 | outnet->svcd_overhead = 0; |
1708 | 0 | outnet->want_to_quit = 0; |
1709 | 0 | outnet->unwanted_threshold = unwanted_threshold; |
1710 | 0 | outnet->unwanted_action = unwanted_action; |
1711 | 0 | outnet->unwanted_param = unwanted_param; |
1712 | 0 | outnet->use_caps_for_id = use_caps_for_id; |
1713 | 0 | outnet->do_udp = do_udp; |
1714 | 0 | outnet->tcp_mss = tcp_mss; |
1715 | 0 | outnet->ip_dscp = dscp; |
1716 | 0 | #ifndef S_SPLINT_S |
1717 | 0 | if(delayclose) { |
1718 | 0 | outnet->delayclose = 1; |
1719 | 0 | outnet->delay_tv.tv_sec = delayclose/1000; |
1720 | 0 | outnet->delay_tv.tv_usec = (delayclose%1000)*1000; |
1721 | 0 | } |
1722 | 0 | #endif |
1723 | 0 | if(udp_connect) { |
1724 | 0 | outnet->udp_connect = 1; |
1725 | 0 | } |
1726 | 0 | if(numavailports == 0 || num_ports == 0) { |
1727 | 0 | log_err("no outgoing ports available"); |
1728 | 0 | outside_network_delete(outnet); |
1729 | 0 | return NULL; |
1730 | 0 | } |
1731 | | #ifndef INET6 |
1732 | | do_ip6 = 0; |
1733 | | #endif |
1734 | 0 | calc_num46(ifs, num_ifs, do_ip4, do_ip6, |
1735 | 0 | &outnet->num_ip4, &outnet->num_ip6); |
1736 | 0 | if(outnet->num_ip4 != 0) { |
1737 | 0 | if(!(outnet->ip4_ifs = (struct port_if*)calloc( |
1738 | 0 | (size_t)outnet->num_ip4, sizeof(struct port_if)))) { |
1739 | 0 | log_err("malloc failed"); |
1740 | 0 | outside_network_delete(outnet); |
1741 | 0 | return NULL; |
1742 | 0 | } |
1743 | 0 | } |
1744 | 0 | if(outnet->num_ip6 != 0) { |
1745 | 0 | if(!(outnet->ip6_ifs = (struct port_if*)calloc( |
1746 | 0 | (size_t)outnet->num_ip6, sizeof(struct port_if)))) { |
1747 | 0 | log_err("malloc failed"); |
1748 | 0 | outside_network_delete(outnet); |
1749 | 0 | return NULL; |
1750 | 0 | } |
1751 | 0 | } |
1752 | 0 | if( !(outnet->udp_buff = sldns_buffer_new(bufsize)) || |
1753 | 0 | !(outnet->pending = rbtree_create(pending_cmp)) || |
1754 | 0 | !(outnet->serviced = rbtree_create(serviced_cmp)) || |
1755 | 0 | !create_pending_tcp(outnet, bufsize)) { |
1756 | 0 | log_err("malloc failed"); |
1757 | 0 | outside_network_delete(outnet); |
1758 | 0 | return NULL; |
1759 | 0 | } |
1760 | 0 | rbtree_init(&outnet->tcp_reuse, reuse_cmp); |
1761 | 0 | outnet->tcp_reuse_max = num_tcp; |
1762 | | |
1763 | | /* allocate commpoints */ |
1764 | 0 | for(k=0; k<num_ports; k++) { |
1765 | 0 | struct port_comm* pc; |
1766 | 0 | pc = (struct port_comm*)calloc(1, sizeof(*pc)); |
1767 | 0 | if(!pc) { |
1768 | 0 | log_err("malloc failed"); |
1769 | 0 | outside_network_delete(outnet); |
1770 | 0 | return NULL; |
1771 | 0 | } |
1772 | 0 | pc->cp = comm_point_create_udp(outnet->base, -1, |
1773 | 0 | outnet->udp_buff, 0, outnet_udp_cb, outnet, NULL); |
1774 | 0 | if(!pc->cp) { |
1775 | 0 | log_err("malloc failed"); |
1776 | 0 | free(pc); |
1777 | 0 | outside_network_delete(outnet); |
1778 | 0 | return NULL; |
1779 | 0 | } |
1780 | 0 | pc->next = outnet->unused_fds; |
1781 | 0 | outnet->unused_fds = pc; |
1782 | 0 | } |
1783 | | |
1784 | | /* allocate interfaces */ |
1785 | 0 | if(num_ifs == 0) { |
1786 | 0 | if(do_ip4 && !setup_if(&outnet->ip4_ifs[0], "0.0.0.0", |
1787 | 0 | availports, numavailports, num_ports)) { |
1788 | 0 | log_err("malloc failed"); |
1789 | 0 | outside_network_delete(outnet); |
1790 | 0 | return NULL; |
1791 | 0 | } |
1792 | 0 | if(do_ip6 && !setup_if(&outnet->ip6_ifs[0], "::", |
1793 | 0 | availports, numavailports, num_ports)) { |
1794 | 0 | log_err("malloc failed"); |
1795 | 0 | outside_network_delete(outnet); |
1796 | 0 | return NULL; |
1797 | 0 | } |
1798 | 0 | } else { |
1799 | 0 | size_t done_4 = 0, done_6 = 0; |
1800 | 0 | int i; |
1801 | 0 | for(i=0; i<num_ifs; i++) { |
1802 | 0 | if(str_is_ip6(ifs[i]) && do_ip6) { |
1803 | 0 | if(!setup_if(&outnet->ip6_ifs[done_6], ifs[i], |
1804 | 0 | availports, numavailports, num_ports)){ |
1805 | 0 | log_err("malloc failed"); |
1806 | 0 | outside_network_delete(outnet); |
1807 | 0 | return NULL; |
1808 | 0 | } |
1809 | 0 | done_6++; |
1810 | 0 | } |
1811 | 0 | if(!str_is_ip6(ifs[i]) && do_ip4) { |
1812 | 0 | if(!setup_if(&outnet->ip4_ifs[done_4], ifs[i], |
1813 | 0 | availports, numavailports, num_ports)){ |
1814 | 0 | log_err("malloc failed"); |
1815 | 0 | outside_network_delete(outnet); |
1816 | 0 | return NULL; |
1817 | 0 | } |
1818 | 0 | done_4++; |
1819 | 0 | } |
1820 | 0 | } |
1821 | 0 | } |
1822 | 0 | return outnet; |
1823 | 0 | } |
1824 | | |
1825 | | /** helper pending delete */ |
1826 | | static void |
1827 | | pending_node_del(rbnode_type* node, void* arg) |
1828 | 0 | { |
1829 | 0 | struct pending* pend = (struct pending*)node; |
1830 | 0 | struct outside_network* outnet = (struct outside_network*)arg; |
1831 | 0 | pending_delete(outnet, pend); |
1832 | 0 | } |
1833 | | |
1834 | | /** helper serviced delete */ |
1835 | | static void |
1836 | | serviced_node_del(rbnode_type* node, void* ATTR_UNUSED(arg)) |
1837 | 0 | { |
1838 | 0 | struct serviced_query* sq = (struct serviced_query*)node; |
1839 | 0 | alloc_reg_release(sq->alloc, sq->region); |
1840 | 0 | if(sq->timer) |
1841 | 0 | comm_timer_delete(sq->timer); |
1842 | 0 | free(sq); |
1843 | 0 | } |
1844 | | |
1845 | | void |
1846 | | outside_network_quit_prepare(struct outside_network* outnet) |
1847 | 0 | { |
1848 | 0 | if(!outnet) |
1849 | 0 | return; |
1850 | | /* prevent queued items from being sent */ |
1851 | 0 | outnet->want_to_quit = 1; |
1852 | 0 | } |
1853 | | |
1854 | | void |
1855 | | outside_network_delete(struct outside_network* outnet) |
1856 | 0 | { |
1857 | 0 | if(!outnet) |
1858 | 0 | return; |
1859 | 0 | outnet->want_to_quit = 1; |
1860 | | /* check every element, since we can be called on malloc error */ |
1861 | 0 | if(outnet->pending) { |
1862 | | /* free pending elements, but do no unlink from tree. */ |
1863 | 0 | traverse_postorder(outnet->pending, pending_node_del, NULL); |
1864 | 0 | free(outnet->pending); |
1865 | 0 | } |
1866 | 0 | if(outnet->serviced) { |
1867 | 0 | traverse_postorder(outnet->serviced, serviced_node_del, NULL); |
1868 | 0 | free(outnet->serviced); |
1869 | 0 | } |
1870 | 0 | if(outnet->udp_buff) |
1871 | 0 | sldns_buffer_free(outnet->udp_buff); |
1872 | 0 | if(outnet->unused_fds) { |
1873 | 0 | struct port_comm* p = outnet->unused_fds, *np; |
1874 | 0 | while(p) { |
1875 | 0 | np = p->next; |
1876 | 0 | comm_point_delete(p->cp); |
1877 | 0 | free(p); |
1878 | 0 | p = np; |
1879 | 0 | } |
1880 | 0 | outnet->unused_fds = NULL; |
1881 | 0 | } |
1882 | 0 | if(outnet->ip4_ifs) { |
1883 | 0 | int i, k; |
1884 | 0 | for(i=0; i<outnet->num_ip4; i++) { |
1885 | 0 | for(k=0; k<outnet->ip4_ifs[i].inuse; k++) { |
1886 | 0 | struct port_comm* pc = outnet->ip4_ifs[i]. |
1887 | 0 | out[k]; |
1888 | 0 | comm_point_delete(pc->cp); |
1889 | 0 | free(pc); |
1890 | 0 | } |
1891 | 0 | #ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION |
1892 | 0 | free(outnet->ip4_ifs[i].avail_ports); |
1893 | 0 | #endif |
1894 | 0 | free(outnet->ip4_ifs[i].out); |
1895 | 0 | } |
1896 | 0 | free(outnet->ip4_ifs); |
1897 | 0 | } |
1898 | 0 | if(outnet->ip6_ifs) { |
1899 | 0 | int i, k; |
1900 | 0 | for(i=0; i<outnet->num_ip6; i++) { |
1901 | 0 | for(k=0; k<outnet->ip6_ifs[i].inuse; k++) { |
1902 | 0 | struct port_comm* pc = outnet->ip6_ifs[i]. |
1903 | 0 | out[k]; |
1904 | 0 | comm_point_delete(pc->cp); |
1905 | 0 | free(pc); |
1906 | 0 | } |
1907 | 0 | #ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION |
1908 | 0 | free(outnet->ip6_ifs[i].avail_ports); |
1909 | 0 | #endif |
1910 | 0 | free(outnet->ip6_ifs[i].out); |
1911 | 0 | } |
1912 | 0 | free(outnet->ip6_ifs); |
1913 | 0 | } |
1914 | 0 | if(outnet->tcp_conns) { |
1915 | 0 | size_t i; |
1916 | 0 | for(i=0; i<outnet->num_tcp; i++) |
1917 | 0 | if(outnet->tcp_conns[i]) { |
1918 | 0 | struct pending_tcp* pend; |
1919 | 0 | pend = outnet->tcp_conns[i]; |
1920 | 0 | if(pend->reuse.item_on_lru_list) { |
1921 | | /* delete waiting_tcp elements that |
1922 | | * the tcp conn is working on */ |
1923 | 0 | decommission_pending_tcp(outnet, pend); |
1924 | 0 | } |
1925 | 0 | comm_point_delete(outnet->tcp_conns[i]->c); |
1926 | 0 | free(outnet->tcp_conns[i]); |
1927 | 0 | outnet->tcp_conns[i] = NULL; |
1928 | 0 | } |
1929 | 0 | free(outnet->tcp_conns); |
1930 | 0 | outnet->tcp_conns = NULL; |
1931 | 0 | } |
1932 | 0 | if(outnet->tcp_wait_first) { |
1933 | 0 | struct waiting_tcp* p = outnet->tcp_wait_first, *np; |
1934 | 0 | while(p) { |
1935 | 0 | np = p->next_waiting; |
1936 | 0 | waiting_tcp_delete(p); |
1937 | 0 | p = np; |
1938 | 0 | } |
1939 | 0 | } |
1940 | | /* was allocated in struct pending that was deleted above */ |
1941 | 0 | rbtree_init(&outnet->tcp_reuse, reuse_cmp); |
1942 | 0 | outnet->tcp_reuse_first = NULL; |
1943 | 0 | outnet->tcp_reuse_last = NULL; |
1944 | 0 | if(outnet->udp_wait_first) { |
1945 | 0 | struct pending* p = outnet->udp_wait_first, *np; |
1946 | 0 | while(p) { |
1947 | 0 | np = p->next_waiting; |
1948 | 0 | pending_delete(NULL, p); |
1949 | 0 | p = np; |
1950 | 0 | } |
1951 | 0 | } |
1952 | 0 | free(outnet); |
1953 | 0 | } |
1954 | | |
1955 | | void |
1956 | | pending_delete(struct outside_network* outnet, struct pending* p) |
1957 | 0 | { |
1958 | 0 | if(!p) |
1959 | 0 | return; |
1960 | 0 | if(outnet && outnet->udp_wait_first && |
1961 | 0 | (p->next_waiting || p == outnet->udp_wait_last) ) { |
1962 | | /* delete from waiting list, if it is in the waiting list */ |
1963 | 0 | struct pending* prev = NULL, *x = outnet->udp_wait_first; |
1964 | 0 | while(x && x != p) { |
1965 | 0 | prev = x; |
1966 | 0 | x = x->next_waiting; |
1967 | 0 | } |
1968 | 0 | if(x) { |
1969 | 0 | log_assert(x == p); |
1970 | 0 | if(prev) |
1971 | 0 | prev->next_waiting = p->next_waiting; |
1972 | 0 | else outnet->udp_wait_first = p->next_waiting; |
1973 | 0 | if(outnet->udp_wait_last == p) |
1974 | 0 | outnet->udp_wait_last = prev; |
1975 | 0 | } |
1976 | 0 | } |
1977 | 0 | if(outnet) { |
1978 | 0 | (void)rbtree_delete(outnet->pending, p->node.key); |
1979 | 0 | } |
1980 | 0 | if(p->timer) |
1981 | 0 | comm_timer_delete(p->timer); |
1982 | 0 | free(p->pkt); |
1983 | 0 | free(p); |
1984 | 0 | } |
1985 | | |
1986 | | static void |
1987 | | sai6_putrandom(struct sockaddr_in6 *sa, int pfxlen, struct ub_randstate *rnd) |
1988 | 0 | { |
1989 | 0 | int i, last; |
1990 | 0 | if(!(pfxlen > 0 && pfxlen < 128)) |
1991 | 0 | return; |
1992 | 0 | for(i = 0; i < (128 - pfxlen) / 8; i++) { |
1993 | 0 | sa->sin6_addr.s6_addr[15-i] = (uint8_t)ub_random_max(rnd, 256); |
1994 | 0 | } |
1995 | 0 | last = pfxlen & 7; |
1996 | 0 | if(last != 0) { |
1997 | 0 | sa->sin6_addr.s6_addr[15-i] |= |
1998 | 0 | ((0xFF >> last) & ub_random_max(rnd, 256)); |
1999 | 0 | } |
2000 | 0 | } |
2001 | | |
2002 | | /** |
2003 | | * Try to open a UDP socket for outgoing communication. |
2004 | | * Sets sockets options as needed. |
2005 | | * @param addr: socket address. |
2006 | | * @param addrlen: length of address. |
2007 | | * @param pfxlen: length of network prefix (for address randomisation). |
2008 | | * @param port: port override for addr. |
2009 | | * @param inuse: if -1 is returned, this bool means the port was in use. |
2010 | | * @param rnd: random state (for address randomisation). |
2011 | | * @param dscp: DSCP to use. |
2012 | | * @return fd or -1 |
2013 | | */ |
2014 | | static int |
2015 | | udp_sockport(struct sockaddr_storage* addr, socklen_t addrlen, int pfxlen, |
2016 | | int port, int* inuse, struct ub_randstate* rnd, int dscp) |
2017 | 0 | { |
2018 | 0 | int fd, noproto; |
2019 | 0 | if(addr_is_ip6(addr, addrlen)) { |
2020 | 0 | int freebind = 0; |
2021 | 0 | struct sockaddr_in6 sa = *(struct sockaddr_in6*)addr; |
2022 | 0 | sa.sin6_port = (in_port_t)htons((uint16_t)port); |
2023 | 0 | sa.sin6_flowinfo = 0; |
2024 | 0 | sa.sin6_scope_id = 0; |
2025 | 0 | if(pfxlen != 0) { |
2026 | 0 | freebind = 1; |
2027 | 0 | sai6_putrandom(&sa, pfxlen, rnd); |
2028 | 0 | } |
2029 | 0 | fd = create_udp_sock(AF_INET6, SOCK_DGRAM, |
2030 | 0 | (struct sockaddr*)&sa, addrlen, 1, inuse, &noproto, |
2031 | 0 | 0, 0, 0, NULL, 0, freebind, 0, dscp); |
2032 | 0 | } else { |
2033 | 0 | struct sockaddr_in* sa = (struct sockaddr_in*)addr; |
2034 | 0 | sa->sin_port = (in_port_t)htons((uint16_t)port); |
2035 | 0 | fd = create_udp_sock(AF_INET, SOCK_DGRAM, |
2036 | 0 | (struct sockaddr*)addr, addrlen, 1, inuse, &noproto, |
2037 | 0 | 0, 0, 0, NULL, 0, 0, 0, dscp); |
2038 | 0 | } |
2039 | 0 | return fd; |
2040 | 0 | } |
2041 | | |
2042 | | /** Select random ID */ |
2043 | | static int |
2044 | | select_id(struct outside_network* outnet, struct pending* pend, |
2045 | | sldns_buffer* packet) |
2046 | 0 | { |
2047 | 0 | int id_tries = 0; |
2048 | 0 | pend->id = GET_RANDOM_ID(outnet->rnd); |
2049 | 0 | LDNS_ID_SET(sldns_buffer_begin(packet), pend->id); |
2050 | | |
2051 | | /* insert in tree */ |
2052 | 0 | pend->node.key = pend; |
2053 | 0 | while(!rbtree_insert(outnet->pending, &pend->node)) { |
2054 | | /* change ID to avoid collision */ |
2055 | 0 | pend->id = GET_RANDOM_ID(outnet->rnd); |
2056 | 0 | LDNS_ID_SET(sldns_buffer_begin(packet), pend->id); |
2057 | 0 | id_tries++; |
2058 | 0 | if(id_tries == MAX_ID_RETRY) { |
2059 | 0 | pend->id=99999; /* non existent ID */ |
2060 | 0 | log_err("failed to generate unique ID, drop msg"); |
2061 | 0 | return 0; |
2062 | 0 | } |
2063 | 0 | } |
2064 | 0 | verbose(VERB_ALGO, "inserted new pending reply id=%4.4x", pend->id); |
2065 | 0 | return 1; |
2066 | 0 | } |
2067 | | |
2068 | | /** return true is UDP connect error needs to be logged */ |
2069 | | static int udp_connect_needs_log(int err, struct sockaddr_storage* addr, |
2070 | | socklen_t addrlen) |
2071 | 0 | { |
2072 | 0 | switch(err) { |
2073 | 0 | case ECONNREFUSED: |
2074 | 0 | # ifdef ENETUNREACH |
2075 | 0 | case ENETUNREACH: |
2076 | 0 | # endif |
2077 | 0 | # ifdef EHOSTDOWN |
2078 | 0 | case EHOSTDOWN: |
2079 | 0 | # endif |
2080 | 0 | # ifdef EHOSTUNREACH |
2081 | 0 | case EHOSTUNREACH: |
2082 | 0 | # endif |
2083 | 0 | # ifdef ENETDOWN |
2084 | 0 | case ENETDOWN: |
2085 | 0 | # endif |
2086 | 0 | # ifdef EADDRNOTAVAIL |
2087 | 0 | case EADDRNOTAVAIL: |
2088 | 0 | # endif |
2089 | 0 | case EPERM: |
2090 | 0 | case EACCES: |
2091 | 0 | if(verbosity >= VERB_ALGO) |
2092 | 0 | return 1; |
2093 | 0 | return 0; |
2094 | 0 | case EINVAL: |
2095 | | /* Stop 'Invalid argument for fe80::/10' addresses appearing |
2096 | | * in the logs, at low verbosity. They cannot be sent to. */ |
2097 | 0 | if(addr_is_ip6linklocal(addr, addrlen)) { |
2098 | 0 | if(verbosity >= VERB_ALGO) |
2099 | 0 | return 1; |
2100 | 0 | return 0; |
2101 | 0 | } |
2102 | 0 | break; |
2103 | 0 | default: |
2104 | 0 | break; |
2105 | 0 | } |
2106 | 0 | return 1; |
2107 | 0 | } |
2108 | | |
2109 | | |
2110 | | /** Select random interface and port */ |
2111 | | static int |
2112 | | select_ifport(struct outside_network* outnet, struct pending* pend, |
2113 | | int num_if, struct port_if* ifs) |
2114 | 0 | { |
2115 | 0 | int my_if, my_port, fd, portno, inuse, tries=0; |
2116 | 0 | struct port_if* pif; |
2117 | | /* randomly select interface and port */ |
2118 | 0 | if(num_if == 0) { |
2119 | 0 | verbose(VERB_QUERY, "Need to send query but have no " |
2120 | 0 | "outgoing interfaces of that family"); |
2121 | 0 | return 0; |
2122 | 0 | } |
2123 | 0 | log_assert(outnet->unused_fds); |
2124 | 0 | tries = 0; |
2125 | 0 | while(1) { |
2126 | 0 | my_if = ub_random_max(outnet->rnd, num_if); |
2127 | 0 | pif = &ifs[my_if]; |
2128 | 0 | #ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION |
2129 | 0 | if(outnet->udp_connect) { |
2130 | | /* if we connect() we cannot reuse fds for a port */ |
2131 | 0 | if(pif->inuse >= pif->avail_total) { |
2132 | 0 | tries++; |
2133 | 0 | if(tries < MAX_PORT_RETRY) |
2134 | 0 | continue; |
2135 | 0 | log_err("failed to find an open port, drop msg"); |
2136 | 0 | return 0; |
2137 | 0 | } |
2138 | 0 | my_port = pif->inuse + ub_random_max(outnet->rnd, |
2139 | 0 | pif->avail_total - pif->inuse); |
2140 | 0 | } else { |
2141 | 0 | my_port = ub_random_max(outnet->rnd, pif->avail_total); |
2142 | 0 | if(my_port < pif->inuse) { |
2143 | | /* port already open */ |
2144 | 0 | pend->pc = pif->out[my_port]; |
2145 | 0 | verbose(VERB_ALGO, "using UDP if=%d port=%d", |
2146 | 0 | my_if, pend->pc->number); |
2147 | 0 | break; |
2148 | 0 | } |
2149 | 0 | } |
2150 | | /* try to open new port, if fails, loop to try again */ |
2151 | 0 | log_assert(pif->inuse < pif->maxout); |
2152 | 0 | portno = pif->avail_ports[my_port - pif->inuse]; |
2153 | | #else |
2154 | | my_port = portno = 0; |
2155 | | #endif |
2156 | 0 | fd = udp_sockport(&pif->addr, pif->addrlen, pif->pfxlen, |
2157 | 0 | portno, &inuse, outnet->rnd, outnet->ip_dscp); |
2158 | 0 | if(fd == -1 && !inuse) { |
2159 | | /* nonrecoverable error making socket */ |
2160 | 0 | return 0; |
2161 | 0 | } |
2162 | 0 | if(fd != -1) { |
2163 | 0 | verbose(VERB_ALGO, "opened UDP if=%d port=%d", |
2164 | 0 | my_if, portno); |
2165 | 0 | if(outnet->udp_connect) { |
2166 | | /* connect() to the destination */ |
2167 | 0 | if(connect(fd, (struct sockaddr*)&pend->addr, |
2168 | 0 | pend->addrlen) < 0) { |
2169 | 0 | if(udp_connect_needs_log(errno, |
2170 | 0 | &pend->addr, pend->addrlen)) { |
2171 | 0 | log_err_addr("udp connect failed", |
2172 | 0 | strerror(errno), &pend->addr, |
2173 | 0 | pend->addrlen); |
2174 | 0 | } |
2175 | 0 | sock_close(fd); |
2176 | 0 | return 0; |
2177 | 0 | } |
2178 | 0 | } |
2179 | | /* grab fd */ |
2180 | 0 | pend->pc = outnet->unused_fds; |
2181 | 0 | outnet->unused_fds = pend->pc->next; |
2182 | | |
2183 | | /* setup portcomm */ |
2184 | 0 | pend->pc->next = NULL; |
2185 | 0 | pend->pc->number = portno; |
2186 | 0 | pend->pc->pif = pif; |
2187 | 0 | pend->pc->index = pif->inuse; |
2188 | 0 | pend->pc->num_outstanding = 0; |
2189 | 0 | comm_point_start_listening(pend->pc->cp, fd, -1); |
2190 | | |
2191 | | /* grab port in interface */ |
2192 | 0 | pif->out[pif->inuse] = pend->pc; |
2193 | 0 | #ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION |
2194 | 0 | pif->avail_ports[my_port - pif->inuse] = |
2195 | 0 | pif->avail_ports[pif->avail_total-pif->inuse-1]; |
2196 | 0 | #endif |
2197 | 0 | pif->inuse++; |
2198 | 0 | break; |
2199 | 0 | } |
2200 | | /* failed, already in use */ |
2201 | 0 | verbose(VERB_QUERY, "port %d in use, trying another", portno); |
2202 | 0 | tries++; |
2203 | 0 | if(tries == MAX_PORT_RETRY) { |
2204 | 0 | log_err("failed to find an open port, drop msg"); |
2205 | 0 | return 0; |
2206 | 0 | } |
2207 | 0 | } |
2208 | 0 | log_assert(pend->pc); |
2209 | 0 | pend->pc->num_outstanding++; |
2210 | |
|
2211 | 0 | return 1; |
2212 | 0 | } |
2213 | | |
2214 | | static int |
2215 | | randomize_and_send_udp(struct pending* pend, sldns_buffer* packet, int timeout) |
2216 | 0 | { |
2217 | 0 | struct timeval tv; |
2218 | 0 | struct outside_network* outnet = pend->sq->outnet; |
2219 | | |
2220 | | /* select id */ |
2221 | 0 | if(!select_id(outnet, pend, packet)) { |
2222 | 0 | return 0; |
2223 | 0 | } |
2224 | | |
2225 | | /* select src_if, port */ |
2226 | 0 | if(addr_is_ip6(&pend->addr, pend->addrlen)) { |
2227 | 0 | if(!select_ifport(outnet, pend, |
2228 | 0 | outnet->num_ip6, outnet->ip6_ifs)) |
2229 | 0 | return 0; |
2230 | 0 | } else { |
2231 | 0 | if(!select_ifport(outnet, pend, |
2232 | 0 | outnet->num_ip4, outnet->ip4_ifs)) |
2233 | 0 | return 0; |
2234 | 0 | } |
2235 | 0 | log_assert(pend->pc && pend->pc->cp); |
2236 | | |
2237 | | /* send it over the commlink */ |
2238 | 0 | if(!comm_point_send_udp_msg(pend->pc->cp, packet, |
2239 | 0 | (struct sockaddr*)&pend->addr, pend->addrlen, outnet->udp_connect)) { |
2240 | 0 | portcomm_loweruse(outnet, pend->pc); |
2241 | 0 | return 0; |
2242 | 0 | } |
2243 | 0 | outnet->num_udp_outgoing++; |
2244 | | |
2245 | | /* system calls to set timeout after sending UDP to make roundtrip |
2246 | | smaller. */ |
2247 | 0 | #ifndef S_SPLINT_S |
2248 | 0 | tv.tv_sec = timeout/1000; |
2249 | 0 | tv.tv_usec = (timeout%1000)*1000; |
2250 | 0 | #endif |
2251 | 0 | comm_timer_set(pend->timer, &tv); |
2252 | |
|
2253 | | #ifdef USE_DNSTAP |
2254 | | /* |
2255 | | * sending src (local service)/dst (upstream) addresses over DNSTAP |
2256 | | * There are no chances to get the src (local service) addr if unbound |
2257 | | * is not configured with specific outgoing IP-addresses. So we will |
2258 | | * pass 0.0.0.0 (::) to argument for |
2259 | | * dt_msg_send_outside_query()/dt_msg_send_outside_response() calls. |
2260 | | */ |
2261 | | if(outnet->dtenv && |
2262 | | (outnet->dtenv->log_resolver_query_messages || |
2263 | | outnet->dtenv->log_forwarder_query_messages)) { |
2264 | | log_addr(VERB_ALGO, "from local addr", &pend->pc->pif->addr, pend->pc->pif->addrlen); |
2265 | | log_addr(VERB_ALGO, "request to upstream", &pend->addr, pend->addrlen); |
2266 | | dt_msg_send_outside_query(outnet->dtenv, &pend->addr, &pend->pc->pif->addr, comm_udp, NULL, |
2267 | | pend->sq->zone, pend->sq->zonelen, packet); |
2268 | | } |
2269 | | #endif |
2270 | 0 | return 1; |
2271 | 0 | } |
2272 | | |
2273 | | struct pending* |
2274 | | pending_udp_query(struct serviced_query* sq, struct sldns_buffer* packet, |
2275 | | int timeout, comm_point_callback_type* cb, void* cb_arg) |
2276 | 0 | { |
2277 | 0 | struct pending* pend = (struct pending*)calloc(1, sizeof(*pend)); |
2278 | 0 | if(!pend) return NULL; |
2279 | 0 | pend->outnet = sq->outnet; |
2280 | 0 | pend->sq = sq; |
2281 | 0 | pend->addrlen = sq->addrlen; |
2282 | 0 | memmove(&pend->addr, &sq->addr, sq->addrlen); |
2283 | 0 | pend->cb = cb; |
2284 | 0 | pend->cb_arg = cb_arg; |
2285 | 0 | pend->node.key = pend; |
2286 | 0 | pend->timer = comm_timer_create(sq->outnet->base, pending_udp_timer_cb, |
2287 | 0 | pend); |
2288 | 0 | if(!pend->timer) { |
2289 | 0 | free(pend); |
2290 | 0 | return NULL; |
2291 | 0 | } |
2292 | | |
2293 | 0 | if(sq->outnet->unused_fds == NULL) { |
2294 | | /* no unused fd, cannot create a new port (randomly) */ |
2295 | 0 | verbose(VERB_ALGO, "no fds available, udp query waiting"); |
2296 | 0 | pend->timeout = timeout; |
2297 | 0 | pend->pkt_len = sldns_buffer_limit(packet); |
2298 | 0 | pend->pkt = (uint8_t*)memdup(sldns_buffer_begin(packet), |
2299 | 0 | pend->pkt_len); |
2300 | 0 | if(!pend->pkt) { |
2301 | 0 | comm_timer_delete(pend->timer); |
2302 | 0 | free(pend); |
2303 | 0 | return NULL; |
2304 | 0 | } |
2305 | | /* put at end of waiting list */ |
2306 | 0 | if(sq->outnet->udp_wait_last) |
2307 | 0 | sq->outnet->udp_wait_last->next_waiting = pend; |
2308 | 0 | else |
2309 | 0 | sq->outnet->udp_wait_first = pend; |
2310 | 0 | sq->outnet->udp_wait_last = pend; |
2311 | 0 | return pend; |
2312 | 0 | } |
2313 | 0 | log_assert(!sq->busy); |
2314 | 0 | sq->busy = 1; |
2315 | 0 | if(!randomize_and_send_udp(pend, packet, timeout)) { |
2316 | 0 | pending_delete(sq->outnet, pend); |
2317 | 0 | return NULL; |
2318 | 0 | } |
2319 | 0 | sq->busy = 0; |
2320 | 0 | return pend; |
2321 | 0 | } |
2322 | | |
2323 | | void |
2324 | | outnet_tcptimer(void* arg) |
2325 | 0 | { |
2326 | 0 | struct waiting_tcp* w = (struct waiting_tcp*)arg; |
2327 | 0 | struct outside_network* outnet = w->outnet; |
2328 | 0 | verbose(VERB_CLIENT, "outnet_tcptimer"); |
2329 | 0 | if(w->on_tcp_waiting_list) { |
2330 | | /* it is on the waiting list */ |
2331 | 0 | outnet_waiting_tcp_list_remove(outnet, w); |
2332 | 0 | waiting_tcp_callback(w, NULL, NETEVENT_TIMEOUT, NULL); |
2333 | 0 | waiting_tcp_delete(w); |
2334 | 0 | } else { |
2335 | | /* it was in use */ |
2336 | 0 | struct pending_tcp* pend=(struct pending_tcp*)w->next_waiting; |
2337 | 0 | reuse_cb_and_decommission(outnet, pend, NETEVENT_TIMEOUT); |
2338 | 0 | } |
2339 | 0 | use_free_buffer(outnet); |
2340 | 0 | } |
2341 | | |
2342 | | /** close the oldest reuse_tcp connection to make a fd and struct pend |
2343 | | * available for a new stream connection */ |
2344 | | static void |
2345 | | reuse_tcp_close_oldest(struct outside_network* outnet) |
2346 | 0 | { |
2347 | 0 | struct reuse_tcp* reuse; |
2348 | 0 | verbose(VERB_CLIENT, "reuse_tcp_close_oldest"); |
2349 | 0 | reuse = reuse_tcp_lru_snip(outnet); |
2350 | 0 | if(!reuse) return; |
2351 | | /* free up */ |
2352 | 0 | reuse_cb_and_decommission(outnet, reuse->pending, NETEVENT_CLOSED); |
2353 | 0 | } |
2354 | | |
2355 | | static uint16_t |
2356 | | tcp_select_id(struct outside_network* outnet, struct reuse_tcp* reuse) |
2357 | 0 | { |
2358 | 0 | if(reuse) |
2359 | 0 | return reuse_tcp_select_id(reuse, outnet); |
2360 | 0 | return GET_RANDOM_ID(outnet->rnd); |
2361 | 0 | } |
2362 | | |
2363 | | /** find spare ID value for reuse tcp stream. That is random and also does |
2364 | | * not collide with an existing query ID that is in use or waiting */ |
2365 | | uint16_t |
2366 | | reuse_tcp_select_id(struct reuse_tcp* reuse, struct outside_network* outnet) |
2367 | 0 | { |
2368 | 0 | uint16_t id = 0, curid, nextid; |
2369 | 0 | const int try_random = 2000; |
2370 | 0 | int i; |
2371 | 0 | unsigned select, count, space; |
2372 | 0 | rbnode_type* node; |
2373 | | |
2374 | | /* make really sure the tree is not empty */ |
2375 | 0 | if(reuse->tree_by_id.count == 0) { |
2376 | 0 | id = GET_RANDOM_ID(outnet->rnd); |
2377 | 0 | return id; |
2378 | 0 | } |
2379 | | |
2380 | | /* try to find random empty spots by picking them */ |
2381 | 0 | for(i = 0; i<try_random; i++) { |
2382 | 0 | id = GET_RANDOM_ID(outnet->rnd); |
2383 | 0 | if(!reuse_tcp_by_id_find(reuse, id)) { |
2384 | 0 | return id; |
2385 | 0 | } |
2386 | 0 | } |
2387 | | |
2388 | | /* equally pick a random unused element from the tree that is |
2389 | | * not in use. Pick a the n-th index of an unused number, |
2390 | | * then loop over the empty spaces in the tree and find it */ |
2391 | 0 | log_assert(reuse->tree_by_id.count < 0xffff); |
2392 | 0 | select = ub_random_max(outnet->rnd, 0xffff - reuse->tree_by_id.count); |
2393 | | /* select value now in 0 .. num free - 1 */ |
2394 | |
|
2395 | 0 | count = 0; /* number of free spaces passed by */ |
2396 | 0 | node = rbtree_first(&reuse->tree_by_id); |
2397 | 0 | log_assert(node && node != RBTREE_NULL); /* tree not empty */ |
2398 | | /* see if select is before first node */ |
2399 | 0 | if(select < (unsigned)tree_by_id_get_id(node)) |
2400 | 0 | return select; |
2401 | 0 | count += tree_by_id_get_id(node); |
2402 | | /* perhaps select is between nodes */ |
2403 | 0 | while(node && node != RBTREE_NULL) { |
2404 | 0 | rbnode_type* next = rbtree_next(node); |
2405 | 0 | if(next && next != RBTREE_NULL) { |
2406 | 0 | curid = tree_by_id_get_id(node); |
2407 | 0 | nextid = tree_by_id_get_id(next); |
2408 | 0 | log_assert(curid < nextid); |
2409 | 0 | if(curid != 0xffff && curid + 1 < nextid) { |
2410 | | /* space between nodes */ |
2411 | 0 | space = nextid - curid - 1; |
2412 | 0 | log_assert(select >= count); |
2413 | 0 | if(select < count + space) { |
2414 | | /* here it is */ |
2415 | 0 | return curid + 1 + (select - count); |
2416 | 0 | } |
2417 | 0 | count += space; |
2418 | 0 | } |
2419 | 0 | } |
2420 | 0 | node = next; |
2421 | 0 | } |
2422 | | |
2423 | | /* select is after the last node */ |
2424 | | /* count is the number of free positions before the nodes in the |
2425 | | * tree */ |
2426 | 0 | node = rbtree_last(&reuse->tree_by_id); |
2427 | 0 | log_assert(node && node != RBTREE_NULL); /* tree not empty */ |
2428 | 0 | curid = tree_by_id_get_id(node); |
2429 | 0 | log_assert(count + (0xffff-curid) + reuse->tree_by_id.count == 0xffff); |
2430 | 0 | return curid + 1 + (select - count); |
2431 | 0 | } |
2432 | | |
2433 | | struct waiting_tcp* |
2434 | | pending_tcp_query(struct serviced_query* sq, sldns_buffer* packet, |
2435 | | int timeout, comm_point_callback_type* callback, void* callback_arg) |
2436 | 0 | { |
2437 | 0 | struct pending_tcp* pend = sq->outnet->tcp_free; |
2438 | 0 | struct reuse_tcp* reuse = NULL; |
2439 | 0 | struct waiting_tcp* w; |
2440 | |
|
2441 | 0 | verbose(VERB_CLIENT, "pending_tcp_query"); |
2442 | 0 | if(sldns_buffer_limit(packet) < sizeof(uint16_t)) { |
2443 | 0 | verbose(VERB_ALGO, "pending tcp query with too short buffer < 2"); |
2444 | 0 | return NULL; |
2445 | 0 | } |
2446 | | |
2447 | | /* find out if a reused stream to the target exists */ |
2448 | | /* if so, take it into use */ |
2449 | 0 | reuse = reuse_tcp_find(sq->outnet, &sq->addr, sq->addrlen, |
2450 | 0 | sq->ssl_upstream); |
2451 | 0 | if(reuse) { |
2452 | 0 | log_reuse_tcp(VERB_CLIENT, "pending_tcp_query: found reuse", reuse); |
2453 | 0 | log_assert(reuse->pending); |
2454 | 0 | pend = reuse->pending; |
2455 | 0 | reuse_tcp_lru_touch(sq->outnet, reuse); |
2456 | 0 | } |
2457 | |
|
2458 | 0 | log_assert(!reuse || (reuse && pend)); |
2459 | | /* if !pend but we have reuse streams, close a reuse stream |
2460 | | * to be able to open a new one to this target, no use waiting |
2461 | | * to reuse a file descriptor while another query needs to use |
2462 | | * that buffer and file descriptor now. */ |
2463 | 0 | if(!pend) { |
2464 | 0 | reuse_tcp_close_oldest(sq->outnet); |
2465 | 0 | pend = sq->outnet->tcp_free; |
2466 | 0 | log_assert(!reuse || (pend == reuse->pending)); |
2467 | 0 | } |
2468 | | |
2469 | | /* allocate space to store query */ |
2470 | 0 | w = (struct waiting_tcp*)malloc(sizeof(struct waiting_tcp) |
2471 | 0 | + sldns_buffer_limit(packet)); |
2472 | 0 | if(!w) { |
2473 | 0 | return NULL; |
2474 | 0 | } |
2475 | 0 | if(!(w->timer = comm_timer_create(sq->outnet->base, outnet_tcptimer, w))) { |
2476 | 0 | free(w); |
2477 | 0 | return NULL; |
2478 | 0 | } |
2479 | 0 | w->pkt = (uint8_t*)w + sizeof(struct waiting_tcp); |
2480 | 0 | w->pkt_len = sldns_buffer_limit(packet); |
2481 | 0 | memmove(w->pkt, sldns_buffer_begin(packet), w->pkt_len); |
2482 | 0 | w->id = tcp_select_id(sq->outnet, reuse); |
2483 | 0 | LDNS_ID_SET(w->pkt, w->id); |
2484 | 0 | memcpy(&w->addr, &sq->addr, sq->addrlen); |
2485 | 0 | w->addrlen = sq->addrlen; |
2486 | 0 | w->outnet = sq->outnet; |
2487 | 0 | w->on_tcp_waiting_list = 0; |
2488 | 0 | w->next_waiting = NULL; |
2489 | 0 | w->cb = callback; |
2490 | 0 | w->cb_arg = callback_arg; |
2491 | 0 | w->ssl_upstream = sq->ssl_upstream; |
2492 | 0 | w->tls_auth_name = sq->tls_auth_name; |
2493 | 0 | w->timeout = timeout; |
2494 | 0 | w->id_node.key = NULL; |
2495 | 0 | w->write_wait_prev = NULL; |
2496 | 0 | w->write_wait_next = NULL; |
2497 | 0 | w->write_wait_queued = 0; |
2498 | 0 | w->error_count = 0; |
2499 | | #ifdef USE_DNSTAP |
2500 | | w->sq = NULL; |
2501 | | #endif |
2502 | 0 | w->in_cb_and_decommission = 0; |
2503 | 0 | if(pend) { |
2504 | | /* we have a buffer available right now */ |
2505 | 0 | if(reuse) { |
2506 | 0 | log_assert(reuse == &pend->reuse); |
2507 | | /* reuse existing fd, write query and continue */ |
2508 | | /* store query in tree by id */ |
2509 | 0 | verbose(VERB_CLIENT, "pending_tcp_query: reuse, store"); |
2510 | 0 | w->next_waiting = (void*)pend; |
2511 | 0 | reuse_tree_by_id_insert(&pend->reuse, w); |
2512 | | /* can we write right now? */ |
2513 | 0 | if(pend->query == NULL) { |
2514 | | /* write straight away */ |
2515 | | /* stop the timer on read of the fd */ |
2516 | 0 | comm_point_stop_listening(pend->c); |
2517 | 0 | pend->query = w; |
2518 | 0 | outnet_tcp_take_query_setup(pend->c->fd, pend, |
2519 | 0 | w); |
2520 | 0 | } else { |
2521 | | /* put it in the waiting list for |
2522 | | * this stream */ |
2523 | 0 | reuse_write_wait_push_back(&pend->reuse, w); |
2524 | 0 | } |
2525 | 0 | } else { |
2526 | | /* create new fd and connect to addr, setup to |
2527 | | * write query */ |
2528 | 0 | verbose(VERB_CLIENT, "pending_tcp_query: new fd, connect"); |
2529 | 0 | rbtree_init(&pend->reuse.tree_by_id, reuse_id_cmp); |
2530 | 0 | pend->reuse.pending = pend; |
2531 | 0 | memcpy(&pend->reuse.addr, &sq->addr, sq->addrlen); |
2532 | 0 | pend->reuse.addrlen = sq->addrlen; |
2533 | 0 | if(!outnet_tcp_take_into_use(w)) { |
2534 | 0 | waiting_tcp_delete(w); |
2535 | 0 | return NULL; |
2536 | 0 | } |
2537 | 0 | } |
2538 | | #ifdef USE_DNSTAP |
2539 | | if(sq->outnet->dtenv && |
2540 | | (sq->outnet->dtenv->log_resolver_query_messages || |
2541 | | sq->outnet->dtenv->log_forwarder_query_messages)) { |
2542 | | /* use w->pkt, because it has the ID value */ |
2543 | | sldns_buffer tmp; |
2544 | | sldns_buffer_init_frm_data(&tmp, w->pkt, w->pkt_len); |
2545 | | dt_msg_send_outside_query(sq->outnet->dtenv, &sq->addr, |
2546 | | &pend->pi->addr, comm_tcp, NULL, sq->zone, |
2547 | | sq->zonelen, &tmp); |
2548 | | } |
2549 | | #endif |
2550 | 0 | } else { |
2551 | | /* queue up */ |
2552 | | /* waiting for a buffer on the outside network buffer wait |
2553 | | * list */ |
2554 | 0 | verbose(VERB_CLIENT, "pending_tcp_query: queue to wait"); |
2555 | | #ifdef USE_DNSTAP |
2556 | | w->sq = sq; |
2557 | | #endif |
2558 | 0 | outnet_waiting_tcp_list_add(sq->outnet, w, 1); |
2559 | 0 | } |
2560 | 0 | return w; |
2561 | 0 | } |
2562 | | |
2563 | | /** create query for serviced queries */ |
2564 | | static void |
2565 | | serviced_gen_query(sldns_buffer* buff, uint8_t* qname, size_t qnamelen, |
2566 | | uint16_t qtype, uint16_t qclass, uint16_t flags) |
2567 | 0 | { |
2568 | 0 | sldns_buffer_clear(buff); |
2569 | | /* skip id */ |
2570 | 0 | sldns_buffer_write_u16(buff, flags); |
2571 | 0 | sldns_buffer_write_u16(buff, 1); /* qdcount */ |
2572 | 0 | sldns_buffer_write_u16(buff, 0); /* ancount */ |
2573 | 0 | sldns_buffer_write_u16(buff, 0); /* nscount */ |
2574 | 0 | sldns_buffer_write_u16(buff, 0); /* arcount */ |
2575 | 0 | sldns_buffer_write(buff, qname, qnamelen); |
2576 | 0 | sldns_buffer_write_u16(buff, qtype); |
2577 | 0 | sldns_buffer_write_u16(buff, qclass); |
2578 | 0 | sldns_buffer_flip(buff); |
2579 | 0 | } |
2580 | | |
2581 | | /** lookup serviced query in serviced query rbtree */ |
2582 | | static struct serviced_query* |
2583 | | lookup_serviced(struct outside_network* outnet, sldns_buffer* buff, int dnssec, |
2584 | | struct sockaddr_storage* addr, socklen_t addrlen, |
2585 | | struct edns_option* opt_list) |
2586 | 0 | { |
2587 | 0 | struct serviced_query key; |
2588 | 0 | key.node.key = &key; |
2589 | 0 | key.qbuf = sldns_buffer_begin(buff); |
2590 | 0 | key.qbuflen = sldns_buffer_limit(buff); |
2591 | 0 | key.dnssec = dnssec; |
2592 | 0 | memcpy(&key.addr, addr, addrlen); |
2593 | 0 | key.addrlen = addrlen; |
2594 | 0 | key.outnet = outnet; |
2595 | 0 | key.opt_list = opt_list; |
2596 | 0 | return (struct serviced_query*)rbtree_search(outnet->serviced, &key); |
2597 | 0 | } |
2598 | | |
2599 | | void |
2600 | | serviced_timer_cb(void* arg) |
2601 | 0 | { |
2602 | 0 | struct serviced_query* sq = (struct serviced_query*)arg; |
2603 | 0 | struct outside_network* outnet = sq->outnet; |
2604 | 0 | verbose(VERB_ALGO, "serviced send timer"); |
2605 | | /* By the time this cb is called, if we don't have any registered |
2606 | | * callbacks for this serviced_query anymore; do not send. */ |
2607 | 0 | if(!sq->cblist) |
2608 | 0 | goto delete; |
2609 | | /* perform first network action */ |
2610 | 0 | if(outnet->do_udp && !(sq->tcp_upstream || sq->ssl_upstream)) { |
2611 | 0 | if(!serviced_udp_send(sq, outnet->udp_buff)) |
2612 | 0 | goto delete; |
2613 | 0 | } else { |
2614 | 0 | if(!serviced_tcp_send(sq, outnet->udp_buff)) |
2615 | 0 | goto delete; |
2616 | 0 | } |
2617 | | /* Maybe by this time we don't have callbacks attached anymore. Don't |
2618 | | * proactively try to delete; let it run and maybe another callback |
2619 | | * will get attached by the time we get an answer. */ |
2620 | 0 | return; |
2621 | 0 | delete: |
2622 | 0 | serviced_callbacks(sq, NETEVENT_CLOSED, NULL, NULL); |
2623 | 0 | } |
2624 | | |
2625 | | /** Create new serviced entry */ |
2626 | | static struct serviced_query* |
2627 | | serviced_create(struct outside_network* outnet, sldns_buffer* buff, int dnssec, |
2628 | | int want_dnssec, int nocaps, int tcp_upstream, int ssl_upstream, |
2629 | | char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen, |
2630 | | uint8_t* zone, size_t zonelen, int qtype, struct edns_option* opt_list, |
2631 | | size_t pad_queries_block_size, struct alloc_cache* alloc, |
2632 | | struct regional* region) |
2633 | 0 | { |
2634 | 0 | struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq)); |
2635 | 0 | struct timeval t; |
2636 | | #ifdef UNBOUND_DEBUG |
2637 | | rbnode_type* ins; |
2638 | | #endif |
2639 | 0 | if(!sq) { |
2640 | 0 | alloc_reg_release(alloc, region); |
2641 | 0 | return NULL; |
2642 | 0 | } |
2643 | 0 | sq->node.key = sq; |
2644 | 0 | sq->alloc = alloc; |
2645 | 0 | sq->region = region; |
2646 | 0 | sq->qbuf = regional_alloc_init(region, sldns_buffer_begin(buff), |
2647 | 0 | sldns_buffer_limit(buff)); |
2648 | 0 | if(!sq->qbuf) { |
2649 | 0 | alloc_reg_release(alloc, region); |
2650 | 0 | free(sq); |
2651 | 0 | return NULL; |
2652 | 0 | } |
2653 | 0 | sq->qbuflen = sldns_buffer_limit(buff); |
2654 | 0 | sq->zone = regional_alloc_init(region, zone, zonelen); |
2655 | 0 | if(!sq->zone) { |
2656 | 0 | alloc_reg_release(alloc, region); |
2657 | 0 | free(sq); |
2658 | 0 | return NULL; |
2659 | 0 | } |
2660 | 0 | sq->zonelen = zonelen; |
2661 | 0 | sq->qtype = qtype; |
2662 | 0 | sq->dnssec = dnssec; |
2663 | 0 | sq->want_dnssec = want_dnssec; |
2664 | 0 | sq->nocaps = nocaps; |
2665 | 0 | sq->tcp_upstream = tcp_upstream; |
2666 | 0 | sq->ssl_upstream = ssl_upstream; |
2667 | 0 | if(tls_auth_name) { |
2668 | 0 | sq->tls_auth_name = regional_strdup(region, tls_auth_name); |
2669 | 0 | if(!sq->tls_auth_name) { |
2670 | 0 | alloc_reg_release(alloc, region); |
2671 | 0 | free(sq); |
2672 | 0 | return NULL; |
2673 | 0 | } |
2674 | 0 | } else { |
2675 | 0 | sq->tls_auth_name = NULL; |
2676 | 0 | } |
2677 | 0 | memcpy(&sq->addr, addr, addrlen); |
2678 | 0 | sq->addrlen = addrlen; |
2679 | 0 | sq->opt_list = opt_list; |
2680 | 0 | sq->busy = 0; |
2681 | 0 | sq->timer = comm_timer_create(outnet->base, serviced_timer_cb, sq); |
2682 | 0 | if(!sq->timer) { |
2683 | 0 | alloc_reg_release(alloc, region); |
2684 | 0 | free(sq); |
2685 | 0 | return NULL; |
2686 | 0 | } |
2687 | 0 | memset(&t, 0, sizeof(t)); |
2688 | 0 | comm_timer_set(sq->timer, &t); |
2689 | 0 | sq->outnet = outnet; |
2690 | 0 | sq->cblist = NULL; |
2691 | 0 | sq->pending = NULL; |
2692 | 0 | sq->status = serviced_initial; |
2693 | 0 | sq->retry = 0; |
2694 | 0 | sq->to_be_deleted = 0; |
2695 | 0 | sq->padding_block_size = pad_queries_block_size; |
2696 | | #ifdef UNBOUND_DEBUG |
2697 | | ins = |
2698 | | #else |
2699 | 0 | (void) |
2700 | 0 | #endif |
2701 | 0 | rbtree_insert(outnet->serviced, &sq->node); |
2702 | 0 | log_assert(ins != NULL); /* must not be already present */ |
2703 | 0 | return sq; |
2704 | 0 | } |
2705 | | |
2706 | | /** reuse tcp stream, remove serviced query from stream, |
2707 | | * return true if the stream is kept, false if it is to be closed */ |
2708 | | static int |
2709 | | reuse_tcp_remove_serviced_keep(struct waiting_tcp* w, |
2710 | | struct serviced_query* sq) |
2711 | 0 | { |
2712 | 0 | struct pending_tcp* pend_tcp = (struct pending_tcp*)w->next_waiting; |
2713 | 0 | verbose(VERB_CLIENT, "reuse_tcp_remove_serviced_keep"); |
2714 | | /* remove the callback. let query continue to write to not cancel |
2715 | | * the stream itself. also keep it as an entry in the tree_by_id, |
2716 | | * in case the answer returns (that we no longer want), but we cannot |
2717 | | * pick the same ID number meanwhile */ |
2718 | 0 | w->cb = NULL; |
2719 | | /* see if can be entered in reuse tree |
2720 | | * for that the FD has to be non-1 */ |
2721 | 0 | if(pend_tcp->c->fd == -1) { |
2722 | 0 | verbose(VERB_CLIENT, "reuse_tcp_remove_serviced_keep: -1 fd"); |
2723 | 0 | return 0; |
2724 | 0 | } |
2725 | | /* if in tree and used by other queries */ |
2726 | 0 | if(pend_tcp->reuse.node.key) { |
2727 | 0 | verbose(VERB_CLIENT, "reuse_tcp_remove_serviced_keep: in use by other queries"); |
2728 | | /* do not reset the keepalive timer, for that |
2729 | | * we'd need traffic, and this is where the serviced is |
2730 | | * removed due to state machine internal reasons, |
2731 | | * eg. iterator no longer interested in this query */ |
2732 | 0 | return 1; |
2733 | 0 | } |
2734 | | /* if still open and want to keep it open */ |
2735 | 0 | if(pend_tcp->c->fd != -1 && sq->outnet->tcp_reuse.count < |
2736 | 0 | sq->outnet->tcp_reuse_max) { |
2737 | 0 | verbose(VERB_CLIENT, "reuse_tcp_remove_serviced_keep: keep open"); |
2738 | | /* set a keepalive timer on it */ |
2739 | 0 | if(!reuse_tcp_insert(sq->outnet, pend_tcp)) { |
2740 | 0 | return 0; |
2741 | 0 | } |
2742 | 0 | reuse_tcp_setup_timeout(pend_tcp, sq->outnet->tcp_reuse_timeout); |
2743 | 0 | return 1; |
2744 | 0 | } |
2745 | 0 | return 0; |
2746 | 0 | } |
2747 | | |
2748 | | /** cleanup serviced query entry */ |
2749 | | static void |
2750 | | serviced_delete(struct serviced_query* sq) |
2751 | 0 | { |
2752 | 0 | verbose(VERB_CLIENT, "serviced_delete"); |
2753 | 0 | if(sq->pending) { |
2754 | | /* clear up the pending query */ |
2755 | 0 | if(sq->status == serviced_query_UDP_EDNS || |
2756 | 0 | sq->status == serviced_query_UDP || |
2757 | 0 | sq->status == serviced_query_UDP_EDNS_FRAG || |
2758 | 0 | sq->status == serviced_query_UDP_EDNS_fallback) { |
2759 | 0 | struct pending* p = (struct pending*)sq->pending; |
2760 | 0 | verbose(VERB_CLIENT, "serviced_delete: UDP"); |
2761 | 0 | if(p->pc) |
2762 | 0 | portcomm_loweruse(sq->outnet, p->pc); |
2763 | 0 | pending_delete(sq->outnet, p); |
2764 | | /* this call can cause reentrant calls back into the |
2765 | | * mesh */ |
2766 | 0 | outnet_send_wait_udp(sq->outnet); |
2767 | 0 | } else { |
2768 | 0 | struct waiting_tcp* w = (struct waiting_tcp*) |
2769 | 0 | sq->pending; |
2770 | 0 | verbose(VERB_CLIENT, "serviced_delete: TCP"); |
2771 | 0 | log_assert(!(w->write_wait_queued && w->on_tcp_waiting_list)); |
2772 | | /* if on stream-write-waiting list then |
2773 | | * remove from waiting list and waiting_tcp_delete */ |
2774 | 0 | if(w->write_wait_queued) { |
2775 | 0 | struct pending_tcp* pend = |
2776 | 0 | (struct pending_tcp*)w->next_waiting; |
2777 | 0 | verbose(VERB_CLIENT, "serviced_delete: writewait"); |
2778 | 0 | if(!w->in_cb_and_decommission) |
2779 | 0 | reuse_tree_by_id_delete(&pend->reuse, w); |
2780 | 0 | reuse_write_wait_remove(&pend->reuse, w); |
2781 | 0 | if(!w->in_cb_and_decommission) |
2782 | 0 | waiting_tcp_delete(w); |
2783 | 0 | } else if(!w->on_tcp_waiting_list) { |
2784 | 0 | struct pending_tcp* pend = |
2785 | 0 | (struct pending_tcp*)w->next_waiting; |
2786 | 0 | verbose(VERB_CLIENT, "serviced_delete: tcpreusekeep"); |
2787 | | /* w needs to stay on tree_by_id to not assign |
2788 | | * the same ID; remove the callback since its |
2789 | | * serviced_query will be gone. */ |
2790 | 0 | w->cb = NULL; |
2791 | 0 | if(!reuse_tcp_remove_serviced_keep(w, sq)) { |
2792 | 0 | if(!w->in_cb_and_decommission) |
2793 | 0 | reuse_cb_and_decommission(sq->outnet, |
2794 | 0 | pend, NETEVENT_CLOSED); |
2795 | 0 | use_free_buffer(sq->outnet); |
2796 | 0 | } |
2797 | 0 | sq->pending = NULL; |
2798 | 0 | } else { |
2799 | 0 | verbose(VERB_CLIENT, "serviced_delete: tcpwait"); |
2800 | 0 | outnet_waiting_tcp_list_remove(sq->outnet, w); |
2801 | 0 | if(!w->in_cb_and_decommission) |
2802 | 0 | waiting_tcp_delete(w); |
2803 | 0 | } |
2804 | 0 | } |
2805 | 0 | } |
2806 | | /* does not delete from tree, caller has to do that */ |
2807 | 0 | serviced_node_del(&sq->node, NULL); |
2808 | 0 | } |
2809 | | |
2810 | | /** perturb a dname capitalization randomly */ |
2811 | | static void |
2812 | | serviced_perturb_qname(struct ub_randstate* rnd, uint8_t* qbuf, size_t len) |
2813 | 0 | { |
2814 | 0 | uint8_t lablen; |
2815 | 0 | uint8_t* d = qbuf + 10; |
2816 | 0 | long int random = 0; |
2817 | 0 | int bits = 0; |
2818 | 0 | log_assert(len >= 10 + 5 /* offset qname, root, qtype, qclass */); |
2819 | 0 | (void)len; |
2820 | 0 | lablen = *d++; |
2821 | 0 | while(lablen) { |
2822 | 0 | while(lablen--) { |
2823 | | /* only perturb A-Z, a-z */ |
2824 | 0 | if(isalpha((unsigned char)*d)) { |
2825 | | /* get a random bit */ |
2826 | 0 | if(bits == 0) { |
2827 | 0 | random = ub_random(rnd); |
2828 | 0 | bits = 30; |
2829 | 0 | } |
2830 | 0 | if((random & 0x1)) { |
2831 | 0 | *d = (uint8_t)toupper((unsigned char)*d); |
2832 | 0 | } else { |
2833 | 0 | *d = (uint8_t)tolower((unsigned char)*d); |
2834 | 0 | } |
2835 | 0 | random >>= 1; |
2836 | 0 | bits--; |
2837 | 0 | } |
2838 | 0 | d++; |
2839 | 0 | } |
2840 | 0 | lablen = *d++; |
2841 | 0 | } |
2842 | 0 | if(verbosity >= VERB_ALGO) { |
2843 | 0 | char buf[LDNS_MAX_DOMAINLEN]; |
2844 | 0 | dname_str(qbuf+10, buf); |
2845 | 0 | verbose(VERB_ALGO, "qname perturbed to %s", buf); |
2846 | 0 | } |
2847 | 0 | } |
2848 | | |
2849 | | static uint16_t |
2850 | 0 | serviced_query_udp_size(struct serviced_query* sq, enum serviced_query_status status) { |
2851 | 0 | uint16_t udp_size; |
2852 | 0 | if(status == serviced_query_UDP_EDNS_FRAG) { |
2853 | 0 | if(addr_is_ip6(&sq->addr, sq->addrlen)) { |
2854 | 0 | if(EDNS_FRAG_SIZE_IP6 < EDNS_ADVERTISED_SIZE) |
2855 | 0 | udp_size = EDNS_FRAG_SIZE_IP6; |
2856 | 0 | else udp_size = EDNS_ADVERTISED_SIZE; |
2857 | 0 | } else { |
2858 | 0 | if(EDNS_FRAG_SIZE_IP4 < EDNS_ADVERTISED_SIZE) |
2859 | 0 | udp_size = EDNS_FRAG_SIZE_IP4; |
2860 | 0 | else udp_size = EDNS_ADVERTISED_SIZE; |
2861 | 0 | } |
2862 | 0 | } else { |
2863 | 0 | udp_size = EDNS_ADVERTISED_SIZE; |
2864 | 0 | } |
2865 | 0 | return udp_size; |
2866 | 0 | } |
2867 | | |
2868 | | /** put serviced query into a buffer */ |
2869 | | static void |
2870 | | serviced_encode(struct serviced_query* sq, sldns_buffer* buff, int with_edns) |
2871 | 0 | { |
2872 | | /* if we are using 0x20 bits for ID randomness, perturb them */ |
2873 | 0 | if(sq->outnet->use_caps_for_id && !sq->nocaps) { |
2874 | 0 | serviced_perturb_qname(sq->outnet->rnd, sq->qbuf, sq->qbuflen); |
2875 | 0 | } |
2876 | | /* generate query */ |
2877 | 0 | sldns_buffer_clear(buff); |
2878 | 0 | sldns_buffer_write_u16(buff, 0); /* id placeholder */ |
2879 | 0 | sldns_buffer_write(buff, sq->qbuf, sq->qbuflen); |
2880 | 0 | sldns_buffer_flip(buff); |
2881 | 0 | if(with_edns) { |
2882 | | /* add edns section */ |
2883 | 0 | struct edns_data edns; |
2884 | 0 | struct edns_option padding_option; |
2885 | 0 | edns.edns_present = 1; |
2886 | 0 | edns.ext_rcode = 0; |
2887 | 0 | edns.edns_version = EDNS_ADVERTISED_VERSION; |
2888 | 0 | edns.opt_list_in = NULL; |
2889 | 0 | edns.opt_list_out = sq->opt_list; |
2890 | 0 | edns.opt_list_inplace_cb_out = NULL; |
2891 | 0 | edns.udp_size = serviced_query_udp_size(sq, sq->status); |
2892 | 0 | edns.bits = 0; |
2893 | 0 | if((sq->dnssec & EDNS_DO)) |
2894 | 0 | edns.bits = EDNS_DO; |
2895 | 0 | if((sq->dnssec & BIT_CD)) |
2896 | 0 | LDNS_CD_SET(sldns_buffer_begin(buff)); |
2897 | 0 | if (sq->ssl_upstream && sq->padding_block_size) { |
2898 | 0 | padding_option.opt_code = LDNS_EDNS_PADDING; |
2899 | 0 | padding_option.opt_len = 0; |
2900 | 0 | padding_option.opt_data = NULL; |
2901 | 0 | padding_option.next = edns.opt_list_out; |
2902 | 0 | edns.opt_list_out = &padding_option; |
2903 | 0 | edns.padding_block_size = sq->padding_block_size; |
2904 | 0 | } |
2905 | 0 | attach_edns_record(buff, &edns); |
2906 | 0 | } |
2907 | 0 | } |
2908 | | |
2909 | | /** |
2910 | | * Perform serviced query UDP sending operation. |
2911 | | * Sends UDP with EDNS, unless infra host marked non EDNS. |
2912 | | * @param sq: query to send. |
2913 | | * @param buff: buffer scratch space. |
2914 | | * @return 0 on error. |
2915 | | */ |
2916 | | static int |
2917 | | serviced_udp_send(struct serviced_query* sq, sldns_buffer* buff) |
2918 | 0 | { |
2919 | 0 | int rtt, vs; |
2920 | 0 | uint8_t edns_lame_known; |
2921 | 0 | time_t now = *sq->outnet->now_secs; |
2922 | |
|
2923 | 0 | if(!infra_host(sq->outnet->infra, &sq->addr, sq->addrlen, sq->zone, |
2924 | 0 | sq->zonelen, now, &vs, &edns_lame_known, &rtt)) |
2925 | 0 | return 0; |
2926 | 0 | sq->last_rtt = rtt; |
2927 | 0 | verbose(VERB_ALGO, "EDNS lookup known=%d vs=%d", edns_lame_known, vs); |
2928 | 0 | if(sq->status == serviced_initial) { |
2929 | 0 | if(vs != -1) { |
2930 | 0 | sq->status = serviced_query_UDP_EDNS; |
2931 | 0 | } else { |
2932 | 0 | sq->status = serviced_query_UDP; |
2933 | 0 | } |
2934 | 0 | } |
2935 | 0 | serviced_encode(sq, buff, (sq->status == serviced_query_UDP_EDNS) || |
2936 | 0 | (sq->status == serviced_query_UDP_EDNS_FRAG)); |
2937 | 0 | sq->last_sent_time = *sq->outnet->now_tv; |
2938 | 0 | sq->edns_lame_known = (int)edns_lame_known; |
2939 | 0 | verbose(VERB_ALGO, "serviced query UDP timeout=%d msec", rtt); |
2940 | 0 | sq->pending = pending_udp_query(sq, buff, rtt, |
2941 | 0 | serviced_udp_callback, sq); |
2942 | 0 | if(!sq->pending) |
2943 | 0 | return 0; |
2944 | 0 | return 1; |
2945 | 0 | } |
2946 | | |
2947 | | /** check that perturbed qname is identical */ |
2948 | | static int |
2949 | | serviced_check_qname(sldns_buffer* pkt, uint8_t* qbuf, size_t qbuflen) |
2950 | 0 | { |
2951 | 0 | uint8_t* d1 = sldns_buffer_begin(pkt)+12; |
2952 | 0 | uint8_t* d2 = qbuf+10; |
2953 | 0 | uint8_t len1, len2; |
2954 | 0 | int count = 0; |
2955 | 0 | if(sldns_buffer_limit(pkt) < 12+1+4) /* packet too small for qname */ |
2956 | 0 | return 0; |
2957 | 0 | log_assert(qbuflen >= 15 /* 10 header, root, type, class */); |
2958 | 0 | len1 = *d1++; |
2959 | 0 | len2 = *d2++; |
2960 | 0 | while(len1 != 0 || len2 != 0) { |
2961 | 0 | if(LABEL_IS_PTR(len1)) { |
2962 | | /* check if we can read *d1 with compression ptr rest */ |
2963 | 0 | if(d1 >= sldns_buffer_at(pkt, sldns_buffer_limit(pkt))) |
2964 | 0 | return 0; |
2965 | 0 | d1 = sldns_buffer_begin(pkt)+PTR_OFFSET(len1, *d1); |
2966 | | /* check if we can read the destination *d1 */ |
2967 | 0 | if(d1 >= sldns_buffer_at(pkt, sldns_buffer_limit(pkt))) |
2968 | 0 | return 0; |
2969 | 0 | len1 = *d1++; |
2970 | 0 | if(count++ > MAX_COMPRESS_PTRS) |
2971 | 0 | return 0; |
2972 | 0 | continue; |
2973 | 0 | } |
2974 | 0 | if(d2 > qbuf+qbuflen) |
2975 | 0 | return 0; |
2976 | 0 | if(len1 != len2) |
2977 | 0 | return 0; |
2978 | 0 | if(len1 > LDNS_MAX_LABELLEN) |
2979 | 0 | return 0; |
2980 | | /* check len1 + 1(next length) are okay to read */ |
2981 | 0 | if(d1+len1 >= sldns_buffer_at(pkt, sldns_buffer_limit(pkt))) |
2982 | 0 | return 0; |
2983 | 0 | log_assert(len1 <= LDNS_MAX_LABELLEN); |
2984 | 0 | log_assert(len2 <= LDNS_MAX_LABELLEN); |
2985 | 0 | log_assert(len1 == len2 && len1 != 0); |
2986 | | /* compare the labels - bitwise identical */ |
2987 | 0 | if(memcmp(d1, d2, len1) != 0) |
2988 | 0 | return 0; |
2989 | 0 | d1 += len1; |
2990 | 0 | d2 += len2; |
2991 | 0 | len1 = *d1++; |
2992 | 0 | len2 = *d2++; |
2993 | 0 | } |
2994 | 0 | return 1; |
2995 | 0 | } |
2996 | | |
2997 | | /** call the callbacks for a serviced query */ |
2998 | | static void |
2999 | | serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c, |
3000 | | struct comm_reply* rep) |
3001 | 0 | { |
3002 | 0 | struct service_callback* p; |
3003 | 0 | int dobackup = (sq->cblist && sq->cblist->next); /* >1 cb*/ |
3004 | 0 | uint8_t *backup_p = NULL; |
3005 | 0 | size_t backlen = 0; |
3006 | | #ifdef UNBOUND_DEBUG |
3007 | | rbnode_type* rem = |
3008 | | #else |
3009 | 0 | (void) |
3010 | 0 | #endif |
3011 | | /* remove from tree, and schedule for deletion, so that callbacks |
3012 | | * can safely deregister themselves and even create new serviced |
3013 | | * queries that are identical to this one. */ |
3014 | 0 | rbtree_delete(sq->outnet->serviced, sq); |
3015 | 0 | log_assert(rem); /* should have been present */ |
3016 | 0 | sq->to_be_deleted = 1; |
3017 | 0 | verbose(VERB_ALGO, "svcd callbacks start"); |
3018 | 0 | if(sq->outnet->use_caps_for_id && error == NETEVENT_NOERROR && c && |
3019 | 0 | !sq->nocaps && sq->qtype != LDNS_RR_TYPE_PTR) { |
3020 | | /* for type PTR do not check perturbed name in answer, |
3021 | | * compatibility with cisco dns guard boxes that mess up |
3022 | | * reverse queries 0x20 contents */ |
3023 | | /* noerror and nxdomain must have a qname in reply */ |
3024 | 0 | if(sldns_buffer_read_u16_at(c->buffer, 4) == 0 && |
3025 | 0 | (LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) |
3026 | 0 | == LDNS_RCODE_NOERROR || |
3027 | 0 | LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) |
3028 | 0 | == LDNS_RCODE_NXDOMAIN)) { |
3029 | 0 | verbose(VERB_DETAIL, "no qname in reply to check 0x20ID"); |
3030 | 0 | log_addr(VERB_DETAIL, "from server", |
3031 | 0 | &sq->addr, sq->addrlen); |
3032 | 0 | log_buf(VERB_DETAIL, "for packet", c->buffer); |
3033 | 0 | error = NETEVENT_CLOSED; |
3034 | 0 | c = NULL; |
3035 | 0 | } else if(sldns_buffer_read_u16_at(c->buffer, 4) > 0 && |
3036 | 0 | !serviced_check_qname(c->buffer, sq->qbuf, |
3037 | 0 | sq->qbuflen)) { |
3038 | 0 | verbose(VERB_DETAIL, "wrong 0x20-ID in reply qname"); |
3039 | 0 | log_addr(VERB_DETAIL, "from server", |
3040 | 0 | &sq->addr, sq->addrlen); |
3041 | 0 | log_buf(VERB_DETAIL, "for packet", c->buffer); |
3042 | 0 | error = NETEVENT_CAPSFAIL; |
3043 | | /* and cleanup too */ |
3044 | 0 | pkt_dname_tolower(c->buffer, |
3045 | 0 | sldns_buffer_at(c->buffer, 12)); |
3046 | 0 | } else { |
3047 | 0 | verbose(VERB_ALGO, "good 0x20-ID in reply qname"); |
3048 | | /* cleanup caps, prettier cache contents. */ |
3049 | 0 | pkt_dname_tolower(c->buffer, |
3050 | 0 | sldns_buffer_at(c->buffer, 12)); |
3051 | 0 | } |
3052 | 0 | } |
3053 | 0 | if(dobackup && c) { |
3054 | | /* make a backup of the query, since the querystate processing |
3055 | | * may send outgoing queries that overwrite the buffer. |
3056 | | * use secondary buffer to store the query. |
3057 | | * This is a data copy, but faster than packet to server */ |
3058 | 0 | backlen = sldns_buffer_limit(c->buffer); |
3059 | 0 | backup_p = regional_alloc_init(sq->region, |
3060 | 0 | sldns_buffer_begin(c->buffer), backlen); |
3061 | 0 | if(!backup_p) { |
3062 | 0 | log_err("malloc failure in serviced query callbacks"); |
3063 | 0 | error = NETEVENT_CLOSED; |
3064 | 0 | c = NULL; |
3065 | 0 | } |
3066 | 0 | sq->outnet->svcd_overhead = backlen; |
3067 | 0 | } |
3068 | | /* test the actual sq->cblist, because the next elem could be deleted*/ |
3069 | 0 | while((p=sq->cblist) != NULL) { |
3070 | 0 | sq->cblist = p->next; /* remove this element */ |
3071 | 0 | if(dobackup && c) { |
3072 | 0 | sldns_buffer_clear(c->buffer); |
3073 | 0 | sldns_buffer_write(c->buffer, backup_p, backlen); |
3074 | 0 | sldns_buffer_flip(c->buffer); |
3075 | 0 | } |
3076 | 0 | fptr_ok(fptr_whitelist_serviced_query(p->cb)); |
3077 | 0 | (void)(*p->cb)(c, p->cb_arg, error, rep); |
3078 | 0 | } |
3079 | 0 | if(backup_p) { |
3080 | 0 | sq->outnet->svcd_overhead = 0; |
3081 | 0 | } |
3082 | 0 | verbose(VERB_ALGO, "svcd callbacks end"); |
3083 | 0 | log_assert(sq->cblist == NULL); |
3084 | 0 | serviced_delete(sq); |
3085 | 0 | } |
3086 | | |
3087 | | int |
3088 | | serviced_tcp_callback(struct comm_point* c, void* arg, int error, |
3089 | | struct comm_reply* rep) |
3090 | 0 | { |
3091 | 0 | struct serviced_query* sq = (struct serviced_query*)arg; |
3092 | 0 | struct comm_reply r2; |
3093 | | #ifdef USE_DNSTAP |
3094 | | struct waiting_tcp* w = (struct waiting_tcp*)sq->pending; |
3095 | | struct pending_tcp* pend_tcp = NULL; |
3096 | | struct port_if* pi = NULL; |
3097 | | if(w && !w->on_tcp_waiting_list && w->next_waiting) { |
3098 | | pend_tcp = (struct pending_tcp*)w->next_waiting; |
3099 | | pi = pend_tcp->pi; |
3100 | | } |
3101 | | #endif |
3102 | 0 | sq->pending = NULL; /* removed after this callback */ |
3103 | 0 | if(error != NETEVENT_NOERROR) |
3104 | 0 | log_addr(VERB_QUERY, "tcp error for address", |
3105 | 0 | &sq->addr, sq->addrlen); |
3106 | 0 | if(error==NETEVENT_NOERROR) |
3107 | 0 | infra_update_tcp_works(sq->outnet->infra, &sq->addr, |
3108 | 0 | sq->addrlen, sq->zone, sq->zonelen); |
3109 | | #ifdef USE_DNSTAP |
3110 | | /* |
3111 | | * sending src (local service)/dst (upstream) addresses over DNSTAP |
3112 | | */ |
3113 | | if(error==NETEVENT_NOERROR && pi && sq->outnet->dtenv && |
3114 | | (sq->outnet->dtenv->log_resolver_response_messages || |
3115 | | sq->outnet->dtenv->log_forwarder_response_messages)) { |
3116 | | log_addr(VERB_ALGO, "response from upstream", &sq->addr, sq->addrlen); |
3117 | | log_addr(VERB_ALGO, "to local addr", &pi->addr, pi->addrlen); |
3118 | | dt_msg_send_outside_response(sq->outnet->dtenv, &sq->addr, |
3119 | | &pi->addr, c->type, c->ssl, sq->zone, sq->zonelen, sq->qbuf, |
3120 | | sq->qbuflen, &sq->last_sent_time, sq->outnet->now_tv, |
3121 | | c->buffer); |
3122 | | } |
3123 | | #endif |
3124 | 0 | if(error==NETEVENT_NOERROR && sq->status == serviced_query_TCP_EDNS && |
3125 | 0 | (LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) == |
3126 | 0 | LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE(sldns_buffer_begin( |
3127 | 0 | c->buffer)) == LDNS_RCODE_NOTIMPL) ) { |
3128 | | /* attempt to fallback to nonEDNS */ |
3129 | 0 | sq->status = serviced_query_TCP_EDNS_fallback; |
3130 | 0 | serviced_tcp_initiate(sq, c->buffer); |
3131 | 0 | return 0; |
3132 | 0 | } else if(error==NETEVENT_NOERROR && |
3133 | 0 | sq->status == serviced_query_TCP_EDNS_fallback && |
3134 | 0 | (LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) == |
3135 | 0 | LDNS_RCODE_NOERROR || LDNS_RCODE_WIRE( |
3136 | 0 | sldns_buffer_begin(c->buffer)) == LDNS_RCODE_NXDOMAIN |
3137 | 0 | || LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) |
3138 | 0 | == LDNS_RCODE_YXDOMAIN)) { |
3139 | | /* the fallback produced a result that looks promising, note |
3140 | | * that this server should be approached without EDNS */ |
3141 | | /* only store noEDNS in cache if domain is noDNSSEC */ |
3142 | 0 | if(!sq->want_dnssec) |
3143 | 0 | if(!infra_edns_update(sq->outnet->infra, &sq->addr, |
3144 | 0 | sq->addrlen, sq->zone, sq->zonelen, -1, |
3145 | 0 | *sq->outnet->now_secs)) |
3146 | 0 | log_err("Out of memory caching no edns for host"); |
3147 | 0 | sq->status = serviced_query_TCP; |
3148 | 0 | } |
3149 | 0 | if(sq->tcp_upstream || sq->ssl_upstream) { |
3150 | 0 | struct timeval now = *sq->outnet->now_tv; |
3151 | 0 | if(error!=NETEVENT_NOERROR) { |
3152 | 0 | if(!infra_rtt_update(sq->outnet->infra, &sq->addr, |
3153 | 0 | sq->addrlen, sq->zone, sq->zonelen, sq->qtype, |
3154 | 0 | -1, sq->last_rtt, (time_t)now.tv_sec)) |
3155 | 0 | log_err("out of memory in TCP exponential backoff."); |
3156 | 0 | } else if(now.tv_sec > sq->last_sent_time.tv_sec || |
3157 | 0 | (now.tv_sec == sq->last_sent_time.tv_sec && |
3158 | 0 | now.tv_usec > sq->last_sent_time.tv_usec)) { |
3159 | | /* convert from microseconds to milliseconds */ |
3160 | 0 | int roundtime = ((int)(now.tv_sec - sq->last_sent_time.tv_sec))*1000 |
3161 | 0 | + ((int)now.tv_usec - (int)sq->last_sent_time.tv_usec)/1000; |
3162 | 0 | verbose(VERB_ALGO, "measured TCP-time at %d msec", roundtime); |
3163 | 0 | log_assert(roundtime >= 0); |
3164 | | /* only store if less then AUTH_TIMEOUT seconds, it could be |
3165 | | * huge due to system-hibernated and we woke up */ |
3166 | 0 | if(roundtime < 60000) { |
3167 | 0 | if(!infra_rtt_update(sq->outnet->infra, &sq->addr, |
3168 | 0 | sq->addrlen, sq->zone, sq->zonelen, sq->qtype, |
3169 | 0 | roundtime, sq->last_rtt, (time_t)now.tv_sec)) |
3170 | 0 | log_err("out of memory noting rtt."); |
3171 | 0 | } |
3172 | 0 | } |
3173 | 0 | } |
3174 | | /* insert address into reply info */ |
3175 | 0 | if(!rep) { |
3176 | | /* create one if there isn't (on errors) */ |
3177 | 0 | rep = &r2; |
3178 | 0 | r2.c = c; |
3179 | 0 | } |
3180 | 0 | memcpy(&rep->remote_addr, &sq->addr, sq->addrlen); |
3181 | 0 | rep->remote_addrlen = sq->addrlen; |
3182 | 0 | serviced_callbacks(sq, error, c, rep); |
3183 | 0 | return 0; |
3184 | 0 | } |
3185 | | |
3186 | | static void |
3187 | | serviced_tcp_initiate(struct serviced_query* sq, sldns_buffer* buff) |
3188 | 0 | { |
3189 | 0 | verbose(VERB_ALGO, "initiate TCP query %s", |
3190 | 0 | sq->status==serviced_query_TCP_EDNS?"EDNS":""); |
3191 | 0 | serviced_encode(sq, buff, sq->status == serviced_query_TCP_EDNS); |
3192 | 0 | sq->last_sent_time = *sq->outnet->now_tv; |
3193 | 0 | log_assert(!sq->busy); |
3194 | 0 | sq->busy = 1; |
3195 | 0 | sq->pending = pending_tcp_query(sq, buff, sq->outnet->tcp_auth_query_timeout, |
3196 | 0 | serviced_tcp_callback, sq); |
3197 | 0 | sq->busy = 0; |
3198 | 0 | if(!sq->pending) { |
3199 | | /* delete from tree so that a retry by above layer does not |
3200 | | * clash with this entry */ |
3201 | 0 | verbose(VERB_ALGO, "serviced_tcp_initiate: failed to send tcp query"); |
3202 | 0 | serviced_callbacks(sq, NETEVENT_CLOSED, NULL, NULL); |
3203 | 0 | } |
3204 | 0 | } |
3205 | | |
3206 | | /** Send serviced query over TCP return false on initial failure */ |
3207 | | static int |
3208 | | serviced_tcp_send(struct serviced_query* sq, sldns_buffer* buff) |
3209 | 0 | { |
3210 | 0 | int vs, rtt, timeout; |
3211 | 0 | uint8_t edns_lame_known; |
3212 | 0 | if(!infra_host(sq->outnet->infra, &sq->addr, sq->addrlen, sq->zone, |
3213 | 0 | sq->zonelen, *sq->outnet->now_secs, &vs, &edns_lame_known, |
3214 | 0 | &rtt)) |
3215 | 0 | return 0; |
3216 | 0 | sq->last_rtt = rtt; |
3217 | 0 | if(vs != -1) |
3218 | 0 | sq->status = serviced_query_TCP_EDNS; |
3219 | 0 | else sq->status = serviced_query_TCP; |
3220 | 0 | serviced_encode(sq, buff, sq->status == serviced_query_TCP_EDNS); |
3221 | 0 | sq->last_sent_time = *sq->outnet->now_tv; |
3222 | 0 | if(sq->tcp_upstream || sq->ssl_upstream) { |
3223 | 0 | timeout = rtt; |
3224 | 0 | if(rtt >= UNKNOWN_SERVER_NICENESS && rtt < sq->outnet->tcp_auth_query_timeout) |
3225 | 0 | timeout = sq->outnet->tcp_auth_query_timeout; |
3226 | 0 | } else { |
3227 | 0 | timeout = sq->outnet->tcp_auth_query_timeout; |
3228 | 0 | } |
3229 | 0 | log_assert(!sq->busy); |
3230 | 0 | sq->busy = 1; |
3231 | 0 | sq->pending = pending_tcp_query(sq, buff, timeout, |
3232 | 0 | serviced_tcp_callback, sq); |
3233 | 0 | sq->busy = 0; |
3234 | 0 | return sq->pending != NULL; |
3235 | 0 | } |
3236 | | |
3237 | | /* see if packet is edns malformed; got zeroes at start. |
3238 | | * This is from servers that return malformed packets to EDNS0 queries, |
3239 | | * but they return good packets for nonEDNS0 queries. |
3240 | | * We try to detect their output; without resorting to a full parse or |
3241 | | * check for too many bytes after the end of the packet. */ |
3242 | | static int |
3243 | | packet_edns_malformed(struct sldns_buffer* buf, int qtype) |
3244 | 0 | { |
3245 | 0 | size_t len; |
3246 | 0 | if(sldns_buffer_limit(buf) < LDNS_HEADER_SIZE) |
3247 | 0 | return 1; /* malformed */ |
3248 | | /* they have NOERROR rcode, 1 answer. */ |
3249 | 0 | if(LDNS_RCODE_WIRE(sldns_buffer_begin(buf)) != LDNS_RCODE_NOERROR) |
3250 | 0 | return 0; |
3251 | | /* one query (to skip) and answer records */ |
3252 | 0 | if(LDNS_QDCOUNT(sldns_buffer_begin(buf)) != 1 || |
3253 | 0 | LDNS_ANCOUNT(sldns_buffer_begin(buf)) == 0) |
3254 | 0 | return 0; |
3255 | | /* skip qname */ |
3256 | 0 | len = dname_valid(sldns_buffer_at(buf, LDNS_HEADER_SIZE), |
3257 | 0 | sldns_buffer_limit(buf)-LDNS_HEADER_SIZE); |
3258 | 0 | if(len == 0) |
3259 | 0 | return 0; |
3260 | 0 | if(len == 1 && qtype == 0) |
3261 | 0 | return 0; /* we asked for '.' and type 0 */ |
3262 | | /* and then 4 bytes (type and class of query) */ |
3263 | 0 | if(sldns_buffer_limit(buf) < LDNS_HEADER_SIZE + len + 4 + 3) |
3264 | 0 | return 0; |
3265 | | |
3266 | | /* and start with 11 zeroes as the answer RR */ |
3267 | | /* so check the qtype of the answer record, qname=0, type=0 */ |
3268 | 0 | if(sldns_buffer_at(buf, LDNS_HEADER_SIZE+len+4)[0] == 0 && |
3269 | 0 | sldns_buffer_at(buf, LDNS_HEADER_SIZE+len+4)[1] == 0 && |
3270 | 0 | sldns_buffer_at(buf, LDNS_HEADER_SIZE+len+4)[2] == 0) |
3271 | 0 | return 1; |
3272 | 0 | return 0; |
3273 | 0 | } |
3274 | | |
3275 | | int |
3276 | | serviced_udp_callback(struct comm_point* c, void* arg, int error, |
3277 | | struct comm_reply* rep) |
3278 | 0 | { |
3279 | 0 | struct serviced_query* sq = (struct serviced_query*)arg; |
3280 | 0 | struct outside_network* outnet = sq->outnet; |
3281 | 0 | struct timeval now = *sq->outnet->now_tv; |
3282 | | #ifdef USE_DNSTAP |
3283 | | struct pending* p = (struct pending*)sq->pending; |
3284 | | #endif |
3285 | |
|
3286 | 0 | sq->pending = NULL; /* removed after callback */ |
3287 | 0 | if(error == NETEVENT_TIMEOUT) { |
3288 | 0 | if(sq->status == serviced_query_UDP_EDNS && sq->last_rtt < 5000 && |
3289 | 0 | (serviced_query_udp_size(sq, serviced_query_UDP_EDNS_FRAG) < serviced_query_udp_size(sq, serviced_query_UDP_EDNS))) { |
3290 | | /* fallback to 1480/1280 */ |
3291 | 0 | sq->status = serviced_query_UDP_EDNS_FRAG; |
3292 | 0 | log_name_addr(VERB_ALGO, "try edns1xx0", sq->qbuf+10, |
3293 | 0 | &sq->addr, sq->addrlen); |
3294 | 0 | if(!serviced_udp_send(sq, c->buffer)) { |
3295 | 0 | serviced_callbacks(sq, NETEVENT_CLOSED, c, rep); |
3296 | 0 | } |
3297 | 0 | return 0; |
3298 | 0 | } |
3299 | 0 | if(sq->status == serviced_query_UDP_EDNS_FRAG) { |
3300 | | /* fragmentation size did not fix it */ |
3301 | 0 | sq->status = serviced_query_UDP_EDNS; |
3302 | 0 | } |
3303 | 0 | sq->retry++; |
3304 | 0 | if(!infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen, |
3305 | 0 | sq->zone, sq->zonelen, sq->qtype, -1, sq->last_rtt, |
3306 | 0 | (time_t)now.tv_sec)) |
3307 | 0 | log_err("out of memory in UDP exponential backoff"); |
3308 | 0 | if(sq->retry < OUTBOUND_UDP_RETRY) { |
3309 | 0 | log_name_addr(VERB_ALGO, "retry query", sq->qbuf+10, |
3310 | 0 | &sq->addr, sq->addrlen); |
3311 | 0 | if(!serviced_udp_send(sq, c->buffer)) { |
3312 | 0 | serviced_callbacks(sq, NETEVENT_CLOSED, c, rep); |
3313 | 0 | } |
3314 | 0 | return 0; |
3315 | 0 | } |
3316 | 0 | } |
3317 | 0 | if(error != NETEVENT_NOERROR) { |
3318 | | /* udp returns error (due to no ID or interface available) */ |
3319 | 0 | serviced_callbacks(sq, error, c, rep); |
3320 | 0 | return 0; |
3321 | 0 | } |
3322 | | #ifdef USE_DNSTAP |
3323 | | /* |
3324 | | * sending src (local service)/dst (upstream) addresses over DNSTAP |
3325 | | */ |
3326 | | if(error == NETEVENT_NOERROR && outnet->dtenv && p->pc && |
3327 | | (outnet->dtenv->log_resolver_response_messages || |
3328 | | outnet->dtenv->log_forwarder_response_messages)) { |
3329 | | log_addr(VERB_ALGO, "response from upstream", &sq->addr, sq->addrlen); |
3330 | | log_addr(VERB_ALGO, "to local addr", &p->pc->pif->addr, |
3331 | | p->pc->pif->addrlen); |
3332 | | dt_msg_send_outside_response(outnet->dtenv, &sq->addr, |
3333 | | &p->pc->pif->addr, c->type, c->ssl, sq->zone, sq->zonelen, |
3334 | | sq->qbuf, sq->qbuflen, &sq->last_sent_time, |
3335 | | sq->outnet->now_tv, c->buffer); |
3336 | | } |
3337 | | #endif |
3338 | 0 | if( (sq->status == serviced_query_UDP_EDNS |
3339 | 0 | ||sq->status == serviced_query_UDP_EDNS_FRAG) |
3340 | 0 | && (LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) |
3341 | 0 | == LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE( |
3342 | 0 | sldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOTIMPL |
3343 | 0 | || packet_edns_malformed(c->buffer, sq->qtype) |
3344 | 0 | )) { |
3345 | | /* try to get an answer by falling back without EDNS */ |
3346 | 0 | verbose(VERB_ALGO, "serviced query: attempt without EDNS"); |
3347 | 0 | sq->status = serviced_query_UDP_EDNS_fallback; |
3348 | 0 | sq->retry = 0; |
3349 | 0 | if(!serviced_udp_send(sq, c->buffer)) { |
3350 | 0 | serviced_callbacks(sq, NETEVENT_CLOSED, c, rep); |
3351 | 0 | } |
3352 | 0 | return 0; |
3353 | 0 | } else if(sq->status == serviced_query_UDP_EDNS && |
3354 | 0 | !sq->edns_lame_known) { |
3355 | | /* now we know that edns queries received answers store that */ |
3356 | 0 | log_addr(VERB_ALGO, "serviced query: EDNS works for", |
3357 | 0 | &sq->addr, sq->addrlen); |
3358 | 0 | if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen, |
3359 | 0 | sq->zone, sq->zonelen, 0, (time_t)now.tv_sec)) { |
3360 | 0 | log_err("Out of memory caching edns works"); |
3361 | 0 | } |
3362 | 0 | sq->edns_lame_known = 1; |
3363 | 0 | } else if(sq->status == serviced_query_UDP_EDNS_fallback && |
3364 | 0 | !sq->edns_lame_known && (LDNS_RCODE_WIRE( |
3365 | 0 | sldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOERROR || |
3366 | 0 | LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) == |
3367 | 0 | LDNS_RCODE_NXDOMAIN || LDNS_RCODE_WIRE(sldns_buffer_begin( |
3368 | 0 | c->buffer)) == LDNS_RCODE_YXDOMAIN)) { |
3369 | | /* the fallback produced a result that looks promising, note |
3370 | | * that this server should be approached without EDNS */ |
3371 | | /* only store noEDNS in cache if domain is noDNSSEC */ |
3372 | 0 | if(!sq->want_dnssec) { |
3373 | 0 | log_addr(VERB_ALGO, "serviced query: EDNS fails for", |
3374 | 0 | &sq->addr, sq->addrlen); |
3375 | 0 | if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen, |
3376 | 0 | sq->zone, sq->zonelen, -1, (time_t)now.tv_sec)) { |
3377 | 0 | log_err("Out of memory caching no edns for host"); |
3378 | 0 | } |
3379 | 0 | } else { |
3380 | 0 | log_addr(VERB_ALGO, "serviced query: EDNS fails, but " |
3381 | 0 | "not stored because need DNSSEC for", &sq->addr, |
3382 | 0 | sq->addrlen); |
3383 | 0 | } |
3384 | 0 | sq->status = serviced_query_UDP; |
3385 | 0 | } |
3386 | 0 | if(now.tv_sec > sq->last_sent_time.tv_sec || |
3387 | 0 | (now.tv_sec == sq->last_sent_time.tv_sec && |
3388 | 0 | now.tv_usec > sq->last_sent_time.tv_usec)) { |
3389 | | /* convert from microseconds to milliseconds */ |
3390 | 0 | int roundtime = ((int)(now.tv_sec - sq->last_sent_time.tv_sec))*1000 |
3391 | 0 | + ((int)now.tv_usec - (int)sq->last_sent_time.tv_usec)/1000; |
3392 | 0 | verbose(VERB_ALGO, "measured roundtrip at %d msec", roundtime); |
3393 | 0 | log_assert(roundtime >= 0); |
3394 | | /* in case the system hibernated, do not enter a huge value, |
3395 | | * above this value gives trouble with server selection */ |
3396 | 0 | if(roundtime < 60000) { |
3397 | 0 | if(!infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen, |
3398 | 0 | sq->zone, sq->zonelen, sq->qtype, roundtime, |
3399 | 0 | sq->last_rtt, (time_t)now.tv_sec)) |
3400 | 0 | log_err("out of memory noting rtt."); |
3401 | 0 | } |
3402 | 0 | } |
3403 | | /* perform TC flag check and TCP fallback after updating our |
3404 | | * cache entries for EDNS status and RTT times */ |
3405 | 0 | if(LDNS_TC_WIRE(sldns_buffer_begin(c->buffer))) { |
3406 | | /* fallback to TCP */ |
3407 | | /* this discards partial UDP contents */ |
3408 | 0 | if(sq->status == serviced_query_UDP_EDNS || |
3409 | 0 | sq->status == serviced_query_UDP_EDNS_FRAG || |
3410 | 0 | sq->status == serviced_query_UDP_EDNS_fallback) |
3411 | | /* if we have unfinished EDNS_fallback, start again */ |
3412 | 0 | sq->status = serviced_query_TCP_EDNS; |
3413 | 0 | else sq->status = serviced_query_TCP; |
3414 | 0 | serviced_tcp_initiate(sq, c->buffer); |
3415 | 0 | return 0; |
3416 | 0 | } |
3417 | | /* yay! an answer */ |
3418 | 0 | serviced_callbacks(sq, error, c, rep); |
3419 | 0 | return 0; |
3420 | 0 | } |
3421 | | |
3422 | | struct serviced_query* |
3423 | | outnet_serviced_query(struct outside_network* outnet, |
3424 | | struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec, |
3425 | | int nocaps, int check_ratelimit, int tcp_upstream, int ssl_upstream, |
3426 | | char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen, |
3427 | | uint8_t* zone, size_t zonelen, struct module_qstate* qstate, |
3428 | | comm_point_callback_type* callback, void* callback_arg, |
3429 | | sldns_buffer* buff, struct module_env* env, int* was_ratelimited) |
3430 | 0 | { |
3431 | 0 | struct serviced_query* sq; |
3432 | 0 | struct service_callback* cb; |
3433 | 0 | struct edns_string_addr* client_string_addr; |
3434 | 0 | struct regional* region; |
3435 | 0 | struct edns_option* backed_up_opt_list = qstate->edns_opts_back_out; |
3436 | 0 | struct edns_option* per_upstream_opt_list = NULL; |
3437 | 0 | time_t timenow = 0; |
3438 | | |
3439 | | /* If we have an already populated EDNS option list make a copy since |
3440 | | * we may now add upstream specific EDNS options. */ |
3441 | | /* Use a region that could be attached to a serviced_query, if it needs |
3442 | | * to be created. If an existing one is found then this region will be |
3443 | | * destroyed here. */ |
3444 | 0 | region = alloc_reg_obtain(env->alloc); |
3445 | 0 | if(!region) return NULL; |
3446 | 0 | if(qstate->edns_opts_back_out) { |
3447 | 0 | per_upstream_opt_list = edns_opt_copy_region( |
3448 | 0 | qstate->edns_opts_back_out, region); |
3449 | 0 | if(!per_upstream_opt_list) { |
3450 | 0 | alloc_reg_release(env->alloc, region); |
3451 | 0 | return NULL; |
3452 | 0 | } |
3453 | 0 | qstate->edns_opts_back_out = per_upstream_opt_list; |
3454 | 0 | } |
3455 | | |
3456 | 0 | if(!inplace_cb_query_call(env, qinfo, flags, addr, addrlen, zone, |
3457 | 0 | zonelen, qstate, region)) { |
3458 | 0 | alloc_reg_release(env->alloc, region); |
3459 | 0 | return NULL; |
3460 | 0 | } |
3461 | | /* Restore the option list; we can explicitly use the copied one from |
3462 | | * now on. */ |
3463 | 0 | per_upstream_opt_list = qstate->edns_opts_back_out; |
3464 | 0 | qstate->edns_opts_back_out = backed_up_opt_list; |
3465 | |
|
3466 | 0 | if((client_string_addr = edns_string_addr_lookup( |
3467 | 0 | &env->edns_strings->client_strings, addr, addrlen))) { |
3468 | 0 | edns_opt_list_append(&per_upstream_opt_list, |
3469 | 0 | env->edns_strings->client_string_opcode, |
3470 | 0 | client_string_addr->string_len, |
3471 | 0 | client_string_addr->string, region); |
3472 | 0 | } |
3473 | |
|
3474 | 0 | serviced_gen_query(buff, qinfo->qname, qinfo->qname_len, qinfo->qtype, |
3475 | 0 | qinfo->qclass, flags); |
3476 | 0 | sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen, |
3477 | 0 | per_upstream_opt_list); |
3478 | 0 | if(!sq) { |
3479 | | /* Check ratelimit only for new serviced_query */ |
3480 | 0 | if(check_ratelimit) { |
3481 | 0 | timenow = *env->now; |
3482 | 0 | if(!infra_ratelimit_inc(env->infra_cache, zone, |
3483 | 0 | zonelen, timenow, env->cfg->ratelimit_backoff, |
3484 | 0 | &qstate->qinfo, |
3485 | 0 | qstate->mesh_info->reply_list |
3486 | 0 | ?&qstate->mesh_info->reply_list->query_reply |
3487 | 0 | :NULL)) { |
3488 | | /* Can we pass through with slip factor? */ |
3489 | 0 | if(env->cfg->ratelimit_factor == 0 || |
3490 | 0 | ub_random_max(env->rnd, |
3491 | 0 | env->cfg->ratelimit_factor) != 1) { |
3492 | 0 | *was_ratelimited = 1; |
3493 | 0 | alloc_reg_release(env->alloc, region); |
3494 | 0 | return NULL; |
3495 | 0 | } |
3496 | 0 | log_nametypeclass(VERB_ALGO, |
3497 | 0 | "ratelimit allowed through for " |
3498 | 0 | "delegation point", zone, |
3499 | 0 | LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN); |
3500 | 0 | } |
3501 | 0 | } |
3502 | | /* make new serviced query entry */ |
3503 | 0 | sq = serviced_create(outnet, buff, dnssec, want_dnssec, nocaps, |
3504 | 0 | tcp_upstream, ssl_upstream, tls_auth_name, addr, |
3505 | 0 | addrlen, zone, zonelen, (int)qinfo->qtype, |
3506 | 0 | per_upstream_opt_list, |
3507 | 0 | ( ssl_upstream && env->cfg->pad_queries |
3508 | 0 | ? env->cfg->pad_queries_block_size : 0 ), |
3509 | 0 | env->alloc, region); |
3510 | 0 | if(!sq) { |
3511 | 0 | if(check_ratelimit) { |
3512 | 0 | infra_ratelimit_dec(env->infra_cache, |
3513 | 0 | zone, zonelen, timenow); |
3514 | 0 | } |
3515 | 0 | return NULL; |
3516 | 0 | } |
3517 | 0 | if(!(cb = (struct service_callback*)regional_alloc( |
3518 | 0 | sq->region, sizeof(*cb)))) { |
3519 | 0 | if(check_ratelimit) { |
3520 | 0 | infra_ratelimit_dec(env->infra_cache, |
3521 | 0 | zone, zonelen, timenow); |
3522 | 0 | } |
3523 | 0 | (void)rbtree_delete(outnet->serviced, sq); |
3524 | 0 | serviced_node_del(&sq->node, NULL); |
3525 | 0 | return NULL; |
3526 | 0 | } |
3527 | | /* No network action at this point; it will be invoked with the |
3528 | | * serviced_query timer instead to run outside of the mesh. */ |
3529 | 0 | } else { |
3530 | | /* We don't need this region anymore. */ |
3531 | 0 | alloc_reg_release(env->alloc, region); |
3532 | | /* duplicate entries are included in the callback list, because |
3533 | | * there is a counterpart registration by our caller that needs |
3534 | | * to be doubly-removed (with callbacks perhaps). */ |
3535 | 0 | if(!(cb = (struct service_callback*)regional_alloc( |
3536 | 0 | sq->region, sizeof(*cb)))) { |
3537 | 0 | return NULL; |
3538 | 0 | } |
3539 | 0 | } |
3540 | | /* add callback to list of callbacks */ |
3541 | 0 | cb->cb = callback; |
3542 | 0 | cb->cb_arg = callback_arg; |
3543 | 0 | cb->next = sq->cblist; |
3544 | 0 | sq->cblist = cb; |
3545 | 0 | return sq; |
3546 | 0 | } |
3547 | | |
3548 | | /** remove callback from list */ |
3549 | | static void |
3550 | | callback_list_remove(struct serviced_query* sq, void* cb_arg) |
3551 | 0 | { |
3552 | 0 | struct service_callback** pp = &sq->cblist; |
3553 | 0 | while(*pp) { |
3554 | 0 | if((*pp)->cb_arg == cb_arg) { |
3555 | 0 | struct service_callback* del = *pp; |
3556 | 0 | *pp = del->next; |
3557 | 0 | return; |
3558 | 0 | } |
3559 | 0 | pp = &(*pp)->next; |
3560 | 0 | } |
3561 | 0 | } |
3562 | | |
3563 | | void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg) |
3564 | 0 | { |
3565 | 0 | if(!sq) |
3566 | 0 | return; |
3567 | 0 | callback_list_remove(sq, cb_arg); |
3568 | | /* if callbacks() routine scheduled deletion, let it do that */ |
3569 | 0 | if(!sq->cblist && !sq->busy && !sq->to_be_deleted) { |
3570 | 0 | (void)rbtree_delete(sq->outnet->serviced, sq); |
3571 | 0 | serviced_delete(sq); |
3572 | 0 | } |
3573 | 0 | } |
3574 | | |
3575 | | /** create fd to send to this destination */ |
3576 | | static int |
3577 | | fd_for_dest(struct outside_network* outnet, struct sockaddr_storage* to_addr, |
3578 | | socklen_t to_addrlen) |
3579 | 0 | { |
3580 | 0 | struct sockaddr_storage* addr; |
3581 | 0 | socklen_t addrlen; |
3582 | 0 | int i, try, pnum, dscp; |
3583 | 0 | struct port_if* pif; |
3584 | | |
3585 | | /* create fd */ |
3586 | 0 | dscp = outnet->ip_dscp; |
3587 | 0 | for(try = 0; try<1000; try++) { |
3588 | 0 | int port = 0; |
3589 | 0 | int freebind = 0; |
3590 | 0 | int noproto = 0; |
3591 | 0 | int inuse = 0; |
3592 | 0 | int fd = -1; |
3593 | | |
3594 | | /* select interface */ |
3595 | 0 | if(addr_is_ip6(to_addr, to_addrlen)) { |
3596 | 0 | if(outnet->num_ip6 == 0) { |
3597 | 0 | char to[64]; |
3598 | 0 | addr_to_str(to_addr, to_addrlen, to, sizeof(to)); |
3599 | 0 | verbose(VERB_QUERY, "need ipv6 to send, but no ipv6 outgoing interfaces, for %s", to); |
3600 | 0 | return -1; |
3601 | 0 | } |
3602 | 0 | i = ub_random_max(outnet->rnd, outnet->num_ip6); |
3603 | 0 | pif = &outnet->ip6_ifs[i]; |
3604 | 0 | } else { |
3605 | 0 | if(outnet->num_ip4 == 0) { |
3606 | 0 | char to[64]; |
3607 | 0 | addr_to_str(to_addr, to_addrlen, to, sizeof(to)); |
3608 | 0 | verbose(VERB_QUERY, "need ipv4 to send, but no ipv4 outgoing interfaces, for %s", to); |
3609 | 0 | return -1; |
3610 | 0 | } |
3611 | 0 | i = ub_random_max(outnet->rnd, outnet->num_ip4); |
3612 | 0 | pif = &outnet->ip4_ifs[i]; |
3613 | 0 | } |
3614 | 0 | addr = &pif->addr; |
3615 | 0 | addrlen = pif->addrlen; |
3616 | 0 | #ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION |
3617 | 0 | pnum = ub_random_max(outnet->rnd, pif->avail_total); |
3618 | 0 | if(pnum < pif->inuse) { |
3619 | | /* port already open */ |
3620 | 0 | port = pif->out[pnum]->number; |
3621 | 0 | } else { |
3622 | | /* unused ports in start part of array */ |
3623 | 0 | port = pif->avail_ports[pnum - pif->inuse]; |
3624 | 0 | } |
3625 | | #else |
3626 | | pnum = port = 0; |
3627 | | #endif |
3628 | 0 | if(addr_is_ip6(to_addr, to_addrlen)) { |
3629 | 0 | struct sockaddr_in6 sa = *(struct sockaddr_in6*)addr; |
3630 | 0 | sa.sin6_port = (in_port_t)htons((uint16_t)port); |
3631 | 0 | fd = create_udp_sock(AF_INET6, SOCK_DGRAM, |
3632 | 0 | (struct sockaddr*)&sa, addrlen, 1, &inuse, &noproto, |
3633 | 0 | 0, 0, 0, NULL, 0, freebind, 0, dscp); |
3634 | 0 | } else { |
3635 | 0 | struct sockaddr_in* sa = (struct sockaddr_in*)addr; |
3636 | 0 | sa->sin_port = (in_port_t)htons((uint16_t)port); |
3637 | 0 | fd = create_udp_sock(AF_INET, SOCK_DGRAM, |
3638 | 0 | (struct sockaddr*)addr, addrlen, 1, &inuse, &noproto, |
3639 | 0 | 0, 0, 0, NULL, 0, freebind, 0, dscp); |
3640 | 0 | } |
3641 | 0 | if(fd != -1) { |
3642 | 0 | return fd; |
3643 | 0 | } |
3644 | 0 | if(!inuse) { |
3645 | 0 | return -1; |
3646 | 0 | } |
3647 | 0 | } |
3648 | | /* too many tries */ |
3649 | 0 | log_err("cannot send probe, ports are in use"); |
3650 | 0 | return -1; |
3651 | 0 | } |
3652 | | |
3653 | | struct comm_point* |
3654 | | outnet_comm_point_for_udp(struct outside_network* outnet, |
3655 | | comm_point_callback_type* cb, void* cb_arg, |
3656 | | struct sockaddr_storage* to_addr, socklen_t to_addrlen) |
3657 | 0 | { |
3658 | 0 | struct comm_point* cp; |
3659 | 0 | int fd = fd_for_dest(outnet, to_addr, to_addrlen); |
3660 | 0 | if(fd == -1) { |
3661 | 0 | return NULL; |
3662 | 0 | } |
3663 | 0 | cp = comm_point_create_udp(outnet->base, fd, outnet->udp_buff, 0, |
3664 | 0 | cb, cb_arg, NULL); |
3665 | 0 | if(!cp) { |
3666 | 0 | log_err("malloc failure"); |
3667 | 0 | close(fd); |
3668 | 0 | return NULL; |
3669 | 0 | } |
3670 | 0 | return cp; |
3671 | 0 | } |
3672 | | |
3673 | | /** setup SSL for comm point */ |
3674 | | static int |
3675 | | setup_comm_ssl(struct comm_point* cp, struct outside_network* outnet, |
3676 | | int fd, char* host) |
3677 | 0 | { |
3678 | 0 | cp->ssl = outgoing_ssl_fd(outnet->sslctx, fd); |
3679 | 0 | if(!cp->ssl) { |
3680 | 0 | log_err("cannot create SSL object"); |
3681 | 0 | return 0; |
3682 | 0 | } |
3683 | | #ifdef USE_WINSOCK |
3684 | | comm_point_tcp_win_bio_cb(cp, cp->ssl); |
3685 | | #endif |
3686 | 0 | cp->ssl_shake_state = comm_ssl_shake_write; |
3687 | | /* https verification */ |
3688 | 0 | #ifdef HAVE_SSL |
3689 | 0 | if(outnet->tls_use_sni) { |
3690 | 0 | (void)SSL_set_tlsext_host_name(cp->ssl, host); |
3691 | 0 | } |
3692 | 0 | #endif |
3693 | 0 | #ifdef HAVE_SSL_SET1_HOST |
3694 | 0 | if((SSL_CTX_get_verify_mode(outnet->sslctx)&SSL_VERIFY_PEER)) { |
3695 | | /* because we set SSL_VERIFY_PEER, in netevent in |
3696 | | * ssl_handshake, it'll check if the certificate |
3697 | | * verification has succeeded */ |
3698 | | /* SSL_VERIFY_PEER is set on the sslctx */ |
3699 | | /* and the certificates to verify with are loaded into |
3700 | | * it with SSL_load_verify_locations or |
3701 | | * SSL_CTX_set_default_verify_paths */ |
3702 | | /* setting the hostname makes openssl verify the |
3703 | | * host name in the x509 certificate in the |
3704 | | * SSL connection*/ |
3705 | 0 | if(!SSL_set1_host(cp->ssl, host)) { |
3706 | 0 | log_err("SSL_set1_host failed"); |
3707 | 0 | return 0; |
3708 | 0 | } |
3709 | 0 | } |
3710 | | #elif defined(HAVE_X509_VERIFY_PARAM_SET1_HOST) |
3711 | | /* openssl 1.0.2 has this function that can be used for |
3712 | | * set1_host like verification */ |
3713 | | if((SSL_CTX_get_verify_mode(outnet->sslctx)&SSL_VERIFY_PEER)) { |
3714 | | X509_VERIFY_PARAM* param = SSL_get0_param(cp->ssl); |
3715 | | # ifdef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS |
3716 | | X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); |
3717 | | # endif |
3718 | | if(!X509_VERIFY_PARAM_set1_host(param, host, strlen(host))) { |
3719 | | log_err("X509_VERIFY_PARAM_set1_host failed"); |
3720 | | return 0; |
3721 | | } |
3722 | | } |
3723 | | #else |
3724 | | (void)host; |
3725 | | #endif /* HAVE_SSL_SET1_HOST */ |
3726 | 0 | return 1; |
3727 | 0 | } |
3728 | | |
3729 | | struct comm_point* |
3730 | | outnet_comm_point_for_tcp(struct outside_network* outnet, |
3731 | | comm_point_callback_type* cb, void* cb_arg, |
3732 | | struct sockaddr_storage* to_addr, socklen_t to_addrlen, |
3733 | | sldns_buffer* query, int timeout, int ssl, char* host) |
3734 | 0 | { |
3735 | 0 | struct comm_point* cp; |
3736 | 0 | int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss, |
3737 | 0 | outnet->ip_dscp, ssl); |
3738 | 0 | if(fd == -1) { |
3739 | 0 | return 0; |
3740 | 0 | } |
3741 | 0 | fd_set_nonblock(fd); |
3742 | 0 | if(!outnet_tcp_connect(fd, to_addr, to_addrlen)) { |
3743 | | /* outnet_tcp_connect has closed fd on error for us */ |
3744 | 0 | return 0; |
3745 | 0 | } |
3746 | 0 | cp = comm_point_create_tcp_out(outnet->base, 65552, cb, cb_arg); |
3747 | 0 | if(!cp) { |
3748 | 0 | log_err("malloc failure"); |
3749 | 0 | close(fd); |
3750 | 0 | return 0; |
3751 | 0 | } |
3752 | 0 | cp->repinfo.remote_addrlen = to_addrlen; |
3753 | 0 | memcpy(&cp->repinfo.remote_addr, to_addr, to_addrlen); |
3754 | | |
3755 | | /* setup for SSL (if needed) */ |
3756 | 0 | if(ssl) { |
3757 | 0 | if(!setup_comm_ssl(cp, outnet, fd, host)) { |
3758 | 0 | log_err("cannot setup XoT"); |
3759 | 0 | comm_point_delete(cp); |
3760 | 0 | return NULL; |
3761 | 0 | } |
3762 | 0 | } |
3763 | | |
3764 | | /* set timeout on TCP connection */ |
3765 | 0 | comm_point_start_listening(cp, fd, timeout); |
3766 | | /* copy scratch buffer to cp->buffer */ |
3767 | 0 | sldns_buffer_copy(cp->buffer, query); |
3768 | 0 | return cp; |
3769 | 0 | } |
3770 | | |
3771 | | /** setup the User-Agent HTTP header based on http-user-agent configuration */ |
3772 | | static void |
3773 | | setup_http_user_agent(sldns_buffer* buf, struct config_file* cfg) |
3774 | 0 | { |
3775 | 0 | if(cfg->hide_http_user_agent) return; |
3776 | 0 | if(cfg->http_user_agent==NULL || cfg->http_user_agent[0] == 0) { |
3777 | 0 | sldns_buffer_printf(buf, "User-Agent: %s/%s\r\n", PACKAGE_NAME, |
3778 | 0 | PACKAGE_VERSION); |
3779 | 0 | } else { |
3780 | 0 | sldns_buffer_printf(buf, "User-Agent: %s\r\n", cfg->http_user_agent); |
3781 | 0 | } |
3782 | 0 | } |
3783 | | |
3784 | | /** setup http request headers in buffer for sending query to destination */ |
3785 | | static int |
3786 | | setup_http_request(sldns_buffer* buf, char* host, char* path, |
3787 | | struct config_file* cfg) |
3788 | 0 | { |
3789 | 0 | sldns_buffer_clear(buf); |
3790 | 0 | sldns_buffer_printf(buf, "GET /%s HTTP/1.1\r\n", path); |
3791 | 0 | sldns_buffer_printf(buf, "Host: %s\r\n", host); |
3792 | 0 | setup_http_user_agent(buf, cfg); |
3793 | | /* We do not really do multiple queries per connection, |
3794 | | * but this header setting is also not needed. |
3795 | | * sldns_buffer_printf(buf, "Connection: close\r\n") */ |
3796 | 0 | sldns_buffer_printf(buf, "\r\n"); |
3797 | 0 | if(sldns_buffer_position(buf)+10 > sldns_buffer_capacity(buf)) |
3798 | 0 | return 0; /* somehow buffer too short, but it is about 60K |
3799 | | and the request is only a couple bytes long. */ |
3800 | 0 | sldns_buffer_flip(buf); |
3801 | 0 | return 1; |
3802 | 0 | } |
3803 | | |
3804 | | struct comm_point* |
3805 | | outnet_comm_point_for_http(struct outside_network* outnet, |
3806 | | comm_point_callback_type* cb, void* cb_arg, |
3807 | | struct sockaddr_storage* to_addr, socklen_t to_addrlen, int timeout, |
3808 | | int ssl, char* host, char* path, struct config_file* cfg) |
3809 | 0 | { |
3810 | | /* cp calls cb with err=NETEVENT_DONE when transfer is done */ |
3811 | 0 | struct comm_point* cp; |
3812 | 0 | int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss, |
3813 | 0 | outnet->ip_dscp, ssl); |
3814 | 0 | if(fd == -1) { |
3815 | 0 | return 0; |
3816 | 0 | } |
3817 | 0 | fd_set_nonblock(fd); |
3818 | 0 | if(!outnet_tcp_connect(fd, to_addr, to_addrlen)) { |
3819 | | /* outnet_tcp_connect has closed fd on error for us */ |
3820 | 0 | return 0; |
3821 | 0 | } |
3822 | 0 | cp = comm_point_create_http_out(outnet->base, 65552, cb, cb_arg, |
3823 | 0 | outnet->udp_buff); |
3824 | 0 | if(!cp) { |
3825 | 0 | log_err("malloc failure"); |
3826 | 0 | close(fd); |
3827 | 0 | return 0; |
3828 | 0 | } |
3829 | 0 | cp->repinfo.remote_addrlen = to_addrlen; |
3830 | 0 | memcpy(&cp->repinfo.remote_addr, to_addr, to_addrlen); |
3831 | | |
3832 | | /* setup for SSL (if needed) */ |
3833 | 0 | if(ssl) { |
3834 | 0 | if(!setup_comm_ssl(cp, outnet, fd, host)) { |
3835 | 0 | log_err("cannot setup https"); |
3836 | 0 | comm_point_delete(cp); |
3837 | 0 | return NULL; |
3838 | 0 | } |
3839 | 0 | } |
3840 | | |
3841 | | /* set timeout on TCP connection */ |
3842 | 0 | comm_point_start_listening(cp, fd, timeout); |
3843 | | |
3844 | | /* setup http request in cp->buffer */ |
3845 | 0 | if(!setup_http_request(cp->buffer, host, path, cfg)) { |
3846 | 0 | log_err("error setting up http request"); |
3847 | 0 | comm_point_delete(cp); |
3848 | 0 | return NULL; |
3849 | 0 | } |
3850 | 0 | return cp; |
3851 | 0 | } |
3852 | | |
3853 | | /** get memory used by waiting tcp entry (in use or not) */ |
3854 | | static size_t |
3855 | | waiting_tcp_get_mem(struct waiting_tcp* w) |
3856 | 0 | { |
3857 | 0 | size_t s; |
3858 | 0 | if(!w) return 0; |
3859 | 0 | s = sizeof(*w) + w->pkt_len; |
3860 | 0 | if(w->timer) |
3861 | 0 | s += comm_timer_get_mem(w->timer); |
3862 | 0 | return s; |
3863 | 0 | } |
3864 | | |
3865 | | /** get memory used by port if */ |
3866 | | static size_t |
3867 | | if_get_mem(struct port_if* pif) |
3868 | 0 | { |
3869 | 0 | size_t s; |
3870 | 0 | int i; |
3871 | 0 | s = sizeof(*pif) + |
3872 | 0 | #ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION |
3873 | 0 | sizeof(int)*pif->avail_total + |
3874 | 0 | #endif |
3875 | 0 | sizeof(struct port_comm*)*pif->maxout; |
3876 | 0 | for(i=0; i<pif->inuse; i++) |
3877 | 0 | s += sizeof(*pif->out[i]) + |
3878 | 0 | comm_point_get_mem(pif->out[i]->cp); |
3879 | 0 | return s; |
3880 | 0 | } |
3881 | | |
3882 | | /** get memory used by waiting udp */ |
3883 | | static size_t |
3884 | | waiting_udp_get_mem(struct pending* w) |
3885 | 0 | { |
3886 | 0 | size_t s; |
3887 | 0 | s = sizeof(*w) + comm_timer_get_mem(w->timer) + w->pkt_len; |
3888 | 0 | return s; |
3889 | 0 | } |
3890 | | |
3891 | | size_t outnet_get_mem(struct outside_network* outnet) |
3892 | 0 | { |
3893 | 0 | size_t i; |
3894 | 0 | int k; |
3895 | 0 | struct waiting_tcp* w; |
3896 | 0 | struct pending* u; |
3897 | 0 | struct serviced_query* sq; |
3898 | 0 | struct service_callback* sb; |
3899 | 0 | struct port_comm* pc; |
3900 | 0 | size_t s = sizeof(*outnet) + sizeof(*outnet->base) + |
3901 | 0 | sizeof(*outnet->udp_buff) + |
3902 | 0 | sldns_buffer_capacity(outnet->udp_buff); |
3903 | | /* second buffer is not ours */ |
3904 | 0 | for(pc = outnet->unused_fds; pc; pc = pc->next) { |
3905 | 0 | s += sizeof(*pc) + comm_point_get_mem(pc->cp); |
3906 | 0 | } |
3907 | 0 | for(k=0; k<outnet->num_ip4; k++) |
3908 | 0 | s += if_get_mem(&outnet->ip4_ifs[k]); |
3909 | 0 | for(k=0; k<outnet->num_ip6; k++) |
3910 | 0 | s += if_get_mem(&outnet->ip6_ifs[k]); |
3911 | 0 | for(u=outnet->udp_wait_first; u; u=u->next_waiting) |
3912 | 0 | s += waiting_udp_get_mem(u); |
3913 | | |
3914 | 0 | s += sizeof(struct pending_tcp*)*outnet->num_tcp; |
3915 | 0 | for(i=0; i<outnet->num_tcp; i++) { |
3916 | 0 | s += sizeof(struct pending_tcp); |
3917 | 0 | s += comm_point_get_mem(outnet->tcp_conns[i]->c); |
3918 | 0 | if(outnet->tcp_conns[i]->query) |
3919 | 0 | s += waiting_tcp_get_mem(outnet->tcp_conns[i]->query); |
3920 | 0 | } |
3921 | 0 | for(w=outnet->tcp_wait_first; w; w = w->next_waiting) |
3922 | 0 | s += waiting_tcp_get_mem(w); |
3923 | 0 | s += sizeof(*outnet->pending); |
3924 | 0 | s += (sizeof(struct pending) + comm_timer_get_mem(NULL)) * |
3925 | 0 | outnet->pending->count; |
3926 | 0 | s += sizeof(*outnet->serviced); |
3927 | 0 | s += outnet->svcd_overhead; |
3928 | 0 | RBTREE_FOR(sq, struct serviced_query*, outnet->serviced) { |
3929 | 0 | s += sizeof(*sq) + sq->qbuflen; |
3930 | 0 | for(sb = sq->cblist; sb; sb = sb->next) |
3931 | 0 | s += sizeof(*sb); |
3932 | 0 | } |
3933 | 0 | return s; |
3934 | 0 | } |
3935 | | |
3936 | | size_t |
3937 | | serviced_get_mem(struct serviced_query* sq) |
3938 | 0 | { |
3939 | 0 | struct service_callback* sb; |
3940 | 0 | size_t s; |
3941 | 0 | s = sizeof(*sq) + sq->qbuflen; |
3942 | 0 | for(sb = sq->cblist; sb; sb = sb->next) |
3943 | 0 | s += sizeof(*sb); |
3944 | 0 | if(sq->status == serviced_query_UDP_EDNS || |
3945 | 0 | sq->status == serviced_query_UDP || |
3946 | 0 | sq->status == serviced_query_UDP_EDNS_FRAG || |
3947 | 0 | sq->status == serviced_query_UDP_EDNS_fallback) { |
3948 | 0 | s += sizeof(struct pending); |
3949 | 0 | s += comm_timer_get_mem(NULL); |
3950 | 0 | } else { |
3951 | | /* does not have size of the pkt pointer */ |
3952 | | /* always has a timer except on malloc failures */ |
3953 | | |
3954 | | /* these sizes are part of the main outside network mem */ |
3955 | | /* |
3956 | | s += sizeof(struct waiting_tcp); |
3957 | | s += comm_timer_get_mem(NULL); |
3958 | | */ |
3959 | 0 | } |
3960 | 0 | return s; |
3961 | 0 | } |
3962 | | |