VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NAT/pxdns.c@ 78333

Last change on this file since 78333 was 77022, checked in by vboxsync, 6 years ago

NAT/Net: pxdns_timeout_add - call sys_untimeout() before scheduling a
new one, we might still have one pending even if there's nothing for
it to do. Scale back the number of timers temporarily bumped as a
work around.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.7 KB
Line 
1/* $Id: pxdns.c 77022 2019-01-28 15:02:16Z vboxsync $ */
2/** @file
3 * NAT Network - DNS proxy.
4 */
5
6/*
7 * Copyright (C) 2009-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*
19 * Copyright (c) 2003,2004,2005 Armin Wolfermann
20 *
21 * Permission is hereby granted, free of charge, to any person obtaining a
22 * copy of this software and associated documentation files (the "Software"),
23 * to deal in the Software without restriction, including without limitation
24 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
25 * and/or sell copies of the Software, and to permit persons to whom the
26 * Software is furnished to do so, subject to the following conditions:
27 *
28 * The above copyright notice and this permission notice shall be included in
29 * all copies or substantial portions of the Software.
30 *
31 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
34 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
36 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
37 * DEALINGS IN THE SOFTWARE.
38 */
39#define LOG_GROUP LOG_GROUP_NAT_SERVICE
40
41#include "winutils.h"
42
43#include "proxy.h"
44#include "proxy_pollmgr.h"
45#include "pxtcp.h"
46
47#include "lwip/sys.h"
48#include "lwip/tcpip.h"
49#include "lwip/ip_addr.h"
50#include "lwip/udp.h"
51#include "lwip/tcp.h"
52
53#ifndef RT_OS_WINDOWS
54#include <sys/poll.h>
55#include <sys/socket.h>
56#include <netinet/in.h>
57#include <netdb.h>
58#else
59#include "winpoll.h"
60#endif
61
62#include <stdio.h>
63#include <string.h>
64
65
66union sockaddr_inet {
67 struct sockaddr sa;
68 struct sockaddr_in sin;
69 struct sockaddr_in6 sin6;
70};
71
72
73struct request;
74
75
76/**
77 * DNS Proxy
78 */
79struct pxdns {
80 SOCKET sock4;
81 SOCKET sock6;
82
83 struct pollmgr_handler pmhdl4;
84 struct pollmgr_handler pmhdl6;
85
86 struct udp_pcb *pcb4;
87 struct udp_pcb *pcb6;
88
89 struct tcp_pcb *ltcp;
90
91 size_t generation;
92 size_t nresolvers;
93 union sockaddr_inet *resolvers;
94
95 u16_t id;
96
97 sys_mutex_t lock;
98
99 size_t active_queries;
100 size_t expired_queries;
101 size_t late_answers;
102 size_t hash_collisions;
103
104#define TIMEOUT 5
105 size_t timeout_slot;
106 u32_t timeout_mask;
107 struct request *timeout_list[TIMEOUT];
108
109#define HASHSIZE 10
110#define HASH(id) ((id) & ((1 << HASHSIZE) - 1))
111 struct request *request_hash[1 << HASHSIZE];
112} g_pxdns;
113
114
115struct request {
116 /**
117 * Request ID that we use in relayed request.
118 */
119 u16_t id;
120
121 /**
122 * pxdns::generation used for this request
123 */
124 size_t generation;
125
126 /**
127 * Current index into pxdns::resolvers
128 */
129 size_t residx;
130
131 /**
132 * PCB from which we have received this request. lwIP doesn't
133 * support listening for both IPv4 and IPv6 on the same pcb, so we
134 * use two and need to keep track.
135 */
136 struct udp_pcb *pcb;
137
138 /**
139 * Client this request is from and its original request ID.
140 */
141 ipX_addr_t client_addr;
142 u16_t client_port;
143 u16_t client_id;
144
145 /**
146 * Chaining for pxdns::request_hash
147 */
148 struct request **pprev_hash;
149 struct request *next_hash;
150
151 /**
152 * Chaining for pxdns::timeout_list
153 */
154 struct request **pprev_timeout;
155 struct request *next_timeout;
156
157 /**
158 * Slot in pxdns::timeout_list
159 */
160 size_t timeout_slot;
161
162 /**
163 * Pbuf with reply received on pollmgr thread.
164 */
165 struct pbuf *reply;
166
167 /**
168 * Preallocated lwIP message to send reply from the lwIP thread.
169 */
170 struct tcpip_msg msg_reply;
171
172 /**
173 * Client request. ID is replaced with ours, original saved in
174 * client_id. Use a copy since we might need to resend and we
175 * don't want to hold onto pbuf of the request.
176 */
177 size_t size;
178 u8_t data[1];
179};
180
181
182static void pxdns_create_resolver_sockaddrs(struct pxdns *pxdns,
183 const char **nameservers);
184
185static err_t pxdns_accept_syn(void *arg, struct tcp_pcb *newpcb, struct pbuf *syn);
186
187static void pxdns_recv4(void *arg, struct udp_pcb *pcb, struct pbuf *p,
188 ip_addr_t *addr, u16_t port);
189static void pxdns_recv6(void *arg, struct udp_pcb *pcb, struct pbuf *p,
190 ip6_addr_t *addr, u16_t port);
191static void pxdns_query(struct pxdns *pxdns, struct udp_pcb *pcb, struct pbuf *p,
192 ipX_addr_t *addr, u16_t port);
193static void pxdns_timer(void *arg);
194static int pxdns_rexmit(struct pxdns *pxdns, struct request *req);
195static int pxdns_forward_outbound(struct pxdns *pxdns, struct request *req);
196
197static int pxdns_pmgr_pump(struct pollmgr_handler *handler, SOCKET fd, int revents);
198static void pxdns_pcb_reply(void *ctx);
199
200static void pxdns_request_register(struct pxdns *pxdns, struct request *req);
201static void pxdns_request_deregister(struct pxdns *pxdns, struct request *req);
202static struct request *pxdns_request_find(struct pxdns *pxdns, u16_t id);
203
204static void pxdns_hash_add(struct pxdns *pxdns, struct request *req);
205static void pxdns_hash_del(struct pxdns *pxdns, struct request *req);
206static void pxdns_timeout_add(struct pxdns *pxdns, struct request *req);
207static void pxdns_timeout_del(struct pxdns *pxdns, struct request *req);
208
209static void pxdns_request_free(struct request *req);
210
211
212err_t
213pxdns_init(struct netif *proxy_netif)
214{
215 struct pxdns *pxdns = &g_pxdns;
216 err_t error;
217
218 LWIP_UNUSED_ARG(proxy_netif);
219
220 pxdns->ltcp = tcp_new();
221 if (pxdns->ltcp != NULL) {
222 tcp_bind_ip6(pxdns->ltcp, IP6_ADDR_ANY, 53);
223 pxdns->ltcp = tcp_listen_dual(pxdns->ltcp);
224 if (pxdns->ltcp != NULL) {
225 tcp_arg(pxdns->ltcp, pxdns);
226 tcp_accept_syn(pxdns->ltcp, pxdns_accept_syn);
227 }
228 }
229
230 pxdns->pmhdl4.callback = pxdns_pmgr_pump;
231 pxdns->pmhdl4.data = (void *)pxdns;
232 pxdns->pmhdl4.slot = -1;
233
234 pxdns->pmhdl6.callback = pxdns_pmgr_pump;
235 pxdns->pmhdl6.data = (void *)pxdns;
236 pxdns->pmhdl6.slot = -1;
237
238 pxdns->pcb4 = udp_new();
239 if (pxdns->pcb4 == NULL) {
240 error = ERR_MEM;
241 goto err_cleanup_pcb;
242 }
243
244 pxdns->pcb6 = udp_new_ip6();
245 if (pxdns->pcb6 == NULL) {
246 error = ERR_MEM;
247 goto err_cleanup_pcb;
248 }
249
250 error = udp_bind(pxdns->pcb4, IP_ADDR_ANY, 53);
251 if (error != ERR_OK) {
252 goto err_cleanup_pcb;
253 }
254
255 error = udp_bind_ip6(pxdns->pcb6, IP6_ADDR_ANY, 53);
256 if (error != ERR_OK) {
257 goto err_cleanup_pcb;
258 }
259
260 udp_recv(pxdns->pcb4, pxdns_recv4, pxdns);
261 udp_recv_ip6(pxdns->pcb6, pxdns_recv6, pxdns);
262
263 pxdns->sock4 = socket(AF_INET, SOCK_DGRAM, 0);
264 if (pxdns->sock4 == INVALID_SOCKET) {
265 goto err_cleanup_pcb;
266 }
267
268 pxdns->sock6 = socket(AF_INET6, SOCK_DGRAM, 0);
269 if (pxdns->sock6 == INVALID_SOCKET) {
270 /* it's ok if the host doesn't support IPv6 */
271 /* XXX: TODO: log */
272 }
273
274 pxdns->generation = 0;
275 pxdns->nresolvers = 0;
276 pxdns->resolvers = NULL;
277 pxdns_create_resolver_sockaddrs(pxdns, g_proxy_options->nameservers);
278
279 sys_mutex_new(&pxdns->lock);
280
281 pxdns->timeout_slot = 0;
282 pxdns->timeout_mask = 0;
283
284 /* NB: assumes pollmgr thread is not running yet */
285 pollmgr_add(&pxdns->pmhdl4, pxdns->sock4, POLLIN);
286 if (pxdns->sock6 != INVALID_SOCKET) {
287 pollmgr_add(&pxdns->pmhdl6, pxdns->sock6, POLLIN);
288 }
289
290 return ERR_OK;
291
292 err_cleanup_pcb:
293 if (pxdns->pcb4 != NULL) {
294 udp_remove(pxdns->pcb4);
295 pxdns->pcb4 = NULL;
296 }
297 if (pxdns->pcb6 != NULL) {
298 udp_remove(pxdns->pcb6);
299 pxdns->pcb4 = NULL;
300 }
301
302 return error;
303}
304
305
306/**
307 * lwIP thread callback to set the new list of nameservers.
308 */
309void
310pxdns_set_nameservers(void *arg)
311{
312 const char **nameservers = (const char **)arg;
313
314 if (g_proxy_options->nameservers != NULL) {
315 RTMemFree((void *)g_proxy_options->nameservers);
316 }
317 g_proxy_options->nameservers = nameservers;
318
319 pxdns_create_resolver_sockaddrs(&g_pxdns, nameservers);
320}
321
322
323/**
324 * Use this list of nameservers to resolve guest requests.
325 *
326 * Runs on lwIP thread, so no new queries or retramsmits compete with
327 * it for the use of the existing list of resolvers (to be replaced).
328 */
329static void
330pxdns_create_resolver_sockaddrs(struct pxdns *pxdns, const char **nameservers)
331{
332 struct addrinfo hints;
333 union sockaddr_inet *resolvers;
334 size_t nnames, nresolvers;
335 const char **p;
336 int status;
337
338 resolvers = NULL;
339 nresolvers = 0;
340
341 if (nameservers == NULL) {
342 goto update_resolvers;
343 }
344
345 nnames = 0;
346 for (p = nameservers; *p != NULL; ++p) {
347 ++nnames;
348 }
349
350 if (nnames == 0) {
351 goto update_resolvers;
352 }
353
354 resolvers = (union sockaddr_inet *)calloc(sizeof(resolvers[0]), nnames);
355 if (resolvers == NULL) {
356 nresolvers = 0;
357 goto update_resolvers;
358 }
359
360 memset(&hints, 0, sizeof(hints));
361 hints.ai_family = AF_UNSPEC;
362 hints.ai_socktype = SOCK_DGRAM;
363 hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
364
365 for (p = nameservers; *p != NULL; ++p) {
366 const char *name = *p;
367 struct addrinfo *ai;
368 status = getaddrinfo(name, /* "domain" */ "53", &hints, &ai);
369 if (status != 0) {
370 /* XXX: log failed resolution */
371 continue;
372 }
373
374 if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) {
375 /* XXX: log unsupported address family */
376 freeaddrinfo(ai);
377 continue;
378 }
379
380 if (ai->ai_addrlen > sizeof(resolvers[nresolvers])) {
381 /* XXX: log */
382 freeaddrinfo(ai);
383 continue;
384 }
385
386 if (ai->ai_family == AF_INET6 && pxdns->sock6 == INVALID_SOCKET) {
387 /* no IPv6 support on the host, can't use this resolver */
388 freeaddrinfo(ai);
389 continue;
390 }
391
392 memcpy(&resolvers[nresolvers], ai->ai_addr, ai->ai_addrlen);
393 freeaddrinfo(ai);
394 ++nresolvers;
395 }
396
397 if (nresolvers == 0) {
398 if (resolvers != NULL) {
399 free(resolvers);
400 }
401 resolvers = NULL;
402 }
403
404 update_resolvers:
405 ++pxdns->generation;
406 if (pxdns->resolvers != NULL) {
407 free(pxdns->resolvers);
408 }
409 pxdns->resolvers = resolvers;
410 pxdns->nresolvers = nresolvers;
411}
412
413
414static void
415pxdns_request_free(struct request *req)
416{
417 LWIP_ASSERT1(req->pprev_hash == NULL);
418 LWIP_ASSERT1(req->pprev_timeout == NULL);
419
420 if (req->reply != NULL) {
421 pbuf_free(req->reply);
422 }
423 free(req);
424}
425
426
427static void
428pxdns_hash_add(struct pxdns *pxdns, struct request *req)
429{
430 struct request **chain;
431
432 LWIP_ASSERT1(req->pprev_hash == NULL);
433 ++pxdns->active_queries;
434
435 chain = &pxdns->request_hash[HASH(req->id)];
436 if ((req->next_hash = *chain) != NULL) {
437 (*chain)->pprev_hash = &req->next_hash;
438 ++pxdns->hash_collisions;
439 }
440 *chain = req;
441 req->pprev_hash = chain;
442}
443
444
445static void
446pxdns_timeout_add(struct pxdns *pxdns, struct request *req)
447{
448 struct request **chain;
449 u32_t omask;
450
451 LWIP_ASSERT1(req->pprev_timeout == NULL);
452
453 req->timeout_slot = pxdns->timeout_slot;
454 chain = &pxdns->timeout_list[req->timeout_slot];
455 if ((req->next_timeout = *chain) != NULL) {
456 (*chain)->pprev_timeout = &req->next_timeout;
457 }
458 *chain = req;
459 req->pprev_timeout = chain;
460
461 omask = pxdns->timeout_mask;
462 pxdns->timeout_mask |= 1U << req->timeout_slot;
463 if (omask == 0) {
464 sys_untimeout(pxdns_timer, pxdns);
465 sys_timeout(1 * 1000, pxdns_timer, pxdns);
466 }
467}
468
469
470static void
471pxdns_hash_del(struct pxdns *pxdns, struct request *req)
472{
473 LWIP_ASSERT1(req->pprev_hash != NULL);
474 --pxdns->active_queries;
475
476 if (req->next_hash != NULL) {
477 req->next_hash->pprev_hash = req->pprev_hash;
478 }
479 *req->pprev_hash = req->next_hash;
480 req->pprev_hash = NULL;
481 req->next_hash = NULL;
482}
483
484
485static void
486pxdns_timeout_del(struct pxdns *pxdns, struct request *req)
487{
488 LWIP_ASSERT1(req->pprev_timeout != NULL);
489 LWIP_ASSERT1(req->timeout_slot < TIMEOUT);
490
491 if (req->next_timeout != NULL) {
492 req->next_timeout->pprev_timeout = req->pprev_timeout;
493 }
494 *req->pprev_timeout = req->next_timeout;
495 req->pprev_timeout = NULL;
496 req->next_timeout = NULL;
497
498 if (pxdns->timeout_list[req->timeout_slot] == NULL) {
499 pxdns->timeout_mask &= ~(1U << req->timeout_slot);
500 /* may be on pollmgr thread so no sys_untimeout */
501 }
502}
503
504
505
506/**
507 * Do bookkeeping on new request. Called from pxdns_query().
508 */
509static void
510pxdns_request_register(struct pxdns *pxdns, struct request *req)
511{
512 sys_mutex_lock(&pxdns->lock);
513
514 pxdns_hash_add(pxdns, req);
515 pxdns_timeout_add(pxdns, req);
516
517 sys_mutex_unlock(&pxdns->lock);
518}
519
520
521static void
522pxdns_request_deregister(struct pxdns *pxdns, struct request *req)
523{
524 sys_mutex_lock(&pxdns->lock);
525
526 pxdns_hash_del(pxdns, req);
527 pxdns_timeout_del(pxdns, req);
528
529 sys_mutex_unlock(&pxdns->lock);
530}
531
532
533/**
534 * Find request by the id we used when relaying it and remove it from
535 * id hash and timeout list. Called from pxdns_pmgr_pump() when reply
536 * comes.
537 */
538static struct request *
539pxdns_request_find(struct pxdns *pxdns, u16_t id)
540{
541 struct request *req = NULL;
542
543 sys_mutex_lock(&pxdns->lock);
544
545 /* find request in the id->req hash */
546 for (req = pxdns->request_hash[HASH(id)]; req != NULL; req = req->next_hash) {
547 if (req->id == id) {
548 break;
549 }
550 }
551
552 if (req != NULL) {
553 pxdns_hash_del(pxdns, req);
554 pxdns_timeout_del(pxdns, req);
555 }
556
557 sys_mutex_unlock(&pxdns->lock);
558 return req;
559}
560
561
562/**
563 * Retransmit of g/c expired requests and move timeout slot forward.
564 */
565static void
566pxdns_timer(void *arg)
567{
568 struct pxdns *pxdns = (struct pxdns *)arg;
569 struct request **chain, *req;
570 u32_t mask;
571
572 sys_mutex_lock(&pxdns->lock);
573
574 /*
575 * Move timeout slot first. New slot points to the list of
576 * expired requests. If any expired request is retransmitted, we
577 * keep it on the list (that is now current), effectively
578 * resetting the timeout.
579 */
580 LWIP_ASSERT1(pxdns->timeout_slot < TIMEOUT);
581 if (++pxdns->timeout_slot == TIMEOUT) {
582 pxdns->timeout_slot = 0;
583 }
584
585 chain = &pxdns->timeout_list[pxdns->timeout_slot];
586 req = *chain;
587 while (req != NULL) {
588 struct request *expired = req;
589 req = req->next_timeout;
590
591 if (pxdns_rexmit(pxdns, expired)) {
592 continue;
593 }
594
595 pxdns_hash_del(pxdns, expired);
596 pxdns_timeout_del(pxdns, expired);
597 ++pxdns->expired_queries;
598
599 pxdns_request_free(expired);
600 }
601
602 if (pxdns->timeout_list[pxdns->timeout_slot] == NULL) {
603 pxdns->timeout_mask &= ~(1U << pxdns->timeout_slot);
604 }
605 else {
606 pxdns->timeout_mask |= 1U << pxdns->timeout_slot;
607 }
608 mask = pxdns->timeout_mask;
609
610 sys_mutex_unlock(&pxdns->lock);
611
612 if (mask != 0) {
613 sys_timeout(1 * 1000, pxdns_timer, pxdns);
614 }
615}
616
617
618static void
619pxdns_recv4(void *arg, struct udp_pcb *pcb, struct pbuf *p,
620 ip_addr_t *addr, u16_t port)
621{
622 struct pxdns *pxdns = (struct pxdns *)arg;
623 pxdns_query(pxdns, pcb, p, ip_2_ipX(addr), port);
624}
625
626static void
627pxdns_recv6(void *arg, struct udp_pcb *pcb, struct pbuf *p,
628 ip6_addr_t *addr, u16_t port)
629{
630 struct pxdns *pxdns = (struct pxdns *)arg;
631 pxdns_query(pxdns, pcb, p, ip6_2_ipX(addr), port);
632}
633
634
635static void
636pxdns_query(struct pxdns *pxdns, struct udp_pcb *pcb, struct pbuf *p,
637 ipX_addr_t *addr, u16_t port)
638{
639 struct request *req;
640 int sent;
641
642 if (pxdns->nresolvers == 0) {
643 /* nothing we can do */
644 pbuf_free(p);
645 return;
646 }
647
648 req = calloc(1, sizeof(struct request) - 1 + p->tot_len);
649 if (req == NULL) {
650 pbuf_free(p);
651 return;
652 }
653
654 /* copy request data */
655 req->size = p->tot_len;
656 pbuf_copy_partial(p, req->data, p->tot_len, 0);
657
658 /* save client identity and client's request id */
659 req->pcb = pcb;
660 ipX_addr_copy(PCB_ISIPV6(pcb), req->client_addr, *addr);
661 req->client_port = port;
662 memcpy(&req->client_id, req->data, sizeof(req->client_id));
663
664 /* slap our request id onto it */
665 req->id = pxdns->id++;
666 memcpy(req->data, &req->id, sizeof(u16_t));
667
668 /* resolver to forward to */
669 req->generation = pxdns->generation;
670 req->residx = 0;
671
672 /* prepare for relaying the reply back to guest */
673 req->msg_reply.type = TCPIP_MSG_CALLBACK_STATIC;
674 req->msg_reply.sem = NULL;
675 req->msg_reply.msg.cb.function = pxdns_pcb_reply;
676 req->msg_reply.msg.cb.ctx = (void *)req;
677
678 DPRINTF2(("%s: req=%p: client id %d -> id %d\n",
679 __func__, (void *)req, req->client_id, req->id));
680
681 pxdns_request_register(pxdns, req);
682
683 sent = pxdns_forward_outbound(pxdns, req);
684 if (!sent) {
685 sent = pxdns_rexmit(pxdns, req);
686 }
687 if (!sent) {
688 pxdns_request_deregister(pxdns, req);
689 pxdns_request_free(req);
690 }
691}
692
693
694/**
695 * Forward request to the req::residx resolver in the pxdns::resolvers
696 * array of upstream resolvers.
697 *
698 * Returns 1 on success, 0 on failure.
699 */
700static int
701pxdns_forward_outbound(struct pxdns *pxdns, struct request *req)
702{
703 union sockaddr_inet *resolver;
704 ssize_t nsent;
705#ifdef RT_OS_WINDOWS
706 const char *pSendData = (const char *)&req->data[0];
707 int cbSendData = (int)req->size;
708 Assert((size_t)cbSendData == req->size);
709#else
710 const void *pSendData = &req->data[0];
711 size_t cbSendData = req->size;
712#endif
713
714 DPRINTF2(("%s: req %p: sending to resolver #%lu\n",
715 __func__, (void *)req, (unsigned long)req->residx));
716
717 LWIP_ASSERT1(req->generation == pxdns->generation);
718 LWIP_ASSERT1(req->residx < pxdns->nresolvers);
719 resolver = &pxdns->resolvers[req->residx];
720
721 if (resolver->sa.sa_family == AF_INET) {
722 nsent = sendto(pxdns->sock4, pSendData, cbSendData, 0,
723 &resolver->sa, sizeof(resolver->sin));
724
725 }
726 else if (resolver->sa.sa_family == AF_INET6) {
727 if (pxdns->sock6 != INVALID_SOCKET) {
728 nsent = sendto(pxdns->sock6, pSendData, cbSendData, 0,
729 &resolver->sa, sizeof(resolver->sin6));
730 }
731 else {
732 /* shouldn't happen, we should have weeded out IPv6 resolvers */
733 return 0;
734 }
735 }
736 else {
737 /* shouldn't happen, we should have weeded out unsupported families */
738 return 0;
739 }
740
741 if ((size_t)nsent == req->size) {
742 return 1; /* sent */
743 }
744
745 if (nsent < 0) {
746 DPRINTF2(("%s: send: %R[sockerr]\n", __func__, SOCKERRNO()));
747 }
748 else {
749 DPRINTF2(("%s: sent only %lu of %lu\n",
750 __func__, (unsigned long)nsent, (unsigned long)req->size));
751 }
752 return 0; /* not sent, caller will retry as necessary */
753}
754
755
756/**
757 * Forward request to the next resolver in the pxdns::resolvers array
758 * of upstream resolvers if there are any left.
759 */
760static int
761pxdns_rexmit(struct pxdns *pxdns, struct request *req)
762{
763 int sent;
764
765 if (/* __predict_false */ req->generation != pxdns->generation) {
766 DPRINTF2(("%s: req %p: generation %lu != pxdns generation %lu\n",
767 __func__, (void *)req,
768 (unsigned long)req->generation,
769 (unsigned long)pxdns->generation));
770 return 0;
771 }
772
773 LWIP_ASSERT1(req->residx < pxdns->nresolvers);
774 do {
775 if (++req->residx == pxdns->nresolvers) {
776 return 0;
777 }
778
779 sent = pxdns_forward_outbound(pxdns, req);
780 } while (!sent);
781
782 return 1;
783}
784
785
786static int
787pxdns_pmgr_pump(struct pollmgr_handler *handler, SOCKET fd, int revents)
788{
789 struct pxdns *pxdns;
790 struct request *req;
791 ssize_t nread;
792 err_t error;
793 u16_t id;
794
795 pxdns = (struct pxdns *)handler->data;
796 LWIP_ASSERT1(handler == &pxdns->pmhdl4 || handler == &pxdns->pmhdl6);
797 LWIP_ASSERT1(fd == (handler == &pxdns->pmhdl4 ? pxdns->sock4 : pxdns->sock6));
798
799 if (revents & ~(POLLIN|POLLERR)) {
800 DPRINTF0(("%s: unexpected revents 0x%x\n", __func__, revents));
801 return POLLIN;
802 }
803
804 if (revents & POLLERR) {
805 int sockerr = -1;
806 socklen_t optlen = (socklen_t)sizeof(sockerr);
807 int status;
808
809 status = getsockopt(fd, SOL_SOCKET,
810 SO_ERROR, (char *)&sockerr, &optlen);
811 if (status < 0) {
812 DPRINTF(("%s: sock %d: SO_ERROR failed: %R[sockerr]\n",
813 __func__, fd, SOCKERRNO()));
814 }
815 else {
816 DPRINTF(("%s: sock %d: %R[sockerr]\n",
817 __func__, fd, sockerr));
818 }
819 }
820
821 if ((revents & POLLIN) == 0) {
822 return POLLIN;
823 }
824
825
826#ifdef RT_OS_WINDOWS
827 nread = recv(fd, (char *)pollmgr_udpbuf, sizeof(pollmgr_udpbuf), 0);
828#else
829 nread = recv(fd, pollmgr_udpbuf, sizeof(pollmgr_udpbuf), 0);
830#endif
831 if (nread < 0) {
832 DPRINTF(("%s: %R[sockerr]\n", __func__, SOCKERRNO()));
833 return POLLIN;
834 }
835
836 /* check for minimum dns packet length */
837 if (nread < 12) {
838 DPRINTF2(("%s: short reply %lu bytes\n",
839 __func__, (unsigned long)nread));
840 return POLLIN;
841 }
842
843 /* XXX: shall we proxy back RCODE=Refused responses? */
844
845 memcpy(&id, pollmgr_udpbuf, sizeof(id));
846 req = pxdns_request_find(pxdns, id);
847 if (req == NULL) {
848 DPRINTF2(("%s: orphaned reply for %d\n", __func__, id));
849 ++pxdns->late_answers;
850 return POLLIN;
851 }
852
853 DPRINTF2(("%s: reply for req=%p: id %d -> client id %d\n",
854 __func__, (void *)req, req->id, req->client_id));
855
856 req->reply = pbuf_alloc(PBUF_RAW, nread, PBUF_RAM);
857 if (req->reply == NULL) {
858 DPRINTF(("%s: pbuf_alloc(%d) failed\n", __func__, (int)nread));
859 pxdns_request_free(req);
860 return POLLIN;
861 }
862
863 memcpy(pollmgr_udpbuf, &req->client_id, sizeof(req->client_id));
864 error = pbuf_take(req->reply, pollmgr_udpbuf, nread);
865 if (error != ERR_OK) {
866 DPRINTF(("%s: pbuf_take(%d) failed\n", __func__, (int)nread));
867 pxdns_request_free(req);
868 return POLLIN;
869 }
870
871 proxy_lwip_post(&req->msg_reply);
872 return POLLIN;
873}
874
875
876/**
877 * Called on lwIP thread via request::msg_reply callback.
878 */
879static void
880pxdns_pcb_reply(void *ctx)
881{
882 struct request *req = (struct request *)ctx;
883 err_t error;
884
885 error = udp_sendto(req->pcb, req->reply,
886 ipX_2_ip(&req->client_addr), req->client_port);
887 if (error != ERR_OK) {
888 DPRINTF(("%s: udp_sendto err %s\n",
889 __func__, proxy_lwip_strerr(error)));
890 }
891
892 pxdns_request_free(req);
893}
894
895
896/**
897 * TCP DNS proxy. This kicks in for large replies that don't fit into
898 * 512 bytes of UDP payload. Client will retry with TCP to get
899 * complete reply.
900 */
901static err_t
902pxdns_accept_syn(void *arg, struct tcp_pcb *newpcb, struct pbuf *syn)
903{
904 struct pxdns *pxdns = (struct pxdns *)arg;
905 union sockaddr_inet *si;
906 ipX_addr_t *dst;
907 u16_t dst_port;
908
909 tcp_accepted(pxdns->ltcp);
910
911 if (pxdns->nresolvers == 0) {
912 return ERR_CONN;
913 }
914
915 si = &pxdns->resolvers[0];
916
917 if (si->sa.sa_family == AF_INET6) {
918 dst = (ipX_addr_t *)&si->sin6.sin6_addr;
919 dst_port = ntohs(si->sin6.sin6_port);
920 }
921 else {
922 dst = (ipX_addr_t *)&si->sin.sin_addr;
923 dst_port = ntohs(si->sin.sin_port);
924 }
925
926 /*
927 * XXX: TODO: need to implement protocol hooks. E.g. here if
928 * connect fails, we should try connecting to a different server.
929 */
930 return pxtcp_pcb_accept_outbound(newpcb, syn,
931 si->sa.sa_family == AF_INET6, dst, dst_port);
932}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette