VirtualBox

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

Last change on this file since 58613 was 58613, checked in by vboxsync, 9 years ago

NAT/Net: Export pxtcp_pcb_accept_outbound() and use it to provide DNS
TCP proxy. For now we only try to connect to the first resolver,
since we simply hand off connection to pxtcp and there are currently
no hooks for us to retry connection to a different server.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.4 KB
Line 
1/* $Id: pxdns.c 58613 2015-11-09 02:45:26Z vboxsync $ */
2/** @file
3 * NAT Network - DNS proxy.
4 */
5
6/*
7 * Copyright (C) 2009-2015 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(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 chain = &pxdns->request_hash[HASH(req->id)];
434 if ((req->next_hash = *chain) != NULL) {
435 (*chain)->pprev_hash = &req->next_hash;
436 ++pxdns->hash_collisions;
437 }
438 *chain = req;
439 req->pprev_hash = chain;
440}
441
442
443static void
444pxdns_timeout_add(struct pxdns *pxdns, struct request *req)
445{
446 struct request **chain;
447 u32_t omask;
448
449 LWIP_ASSERT1(req->pprev_timeout == NULL);
450
451 req->timeout_slot = pxdns->timeout_slot;
452 chain = &pxdns->timeout_list[req->timeout_slot];
453 if ((req->next_timeout = *chain) != NULL) {
454 (*chain)->pprev_timeout = &req->next_timeout;
455 }
456 *chain = req;
457 req->pprev_timeout = chain;
458
459 omask = pxdns->timeout_mask;
460 pxdns->timeout_mask |= 1U << req->timeout_slot;
461 if (omask == 0) {
462 sys_timeout(1 * 1000, pxdns_timer, pxdns);
463 }
464}
465
466
467static void
468pxdns_hash_del(struct pxdns *pxdns, struct request *req)
469{
470 LWIP_ASSERT1(req->pprev_hash != NULL);
471 --pxdns->active_queries;
472
473 if (req->next_hash != NULL) {
474 req->next_hash->pprev_hash = req->pprev_hash;
475 }
476 *req->pprev_hash = req->next_hash;
477 req->pprev_hash = NULL;
478 req->next_hash = NULL;
479}
480
481
482static void
483pxdns_timeout_del(struct pxdns *pxdns, struct request *req)
484{
485 LWIP_ASSERT1(req->pprev_timeout != NULL);
486 LWIP_ASSERT1(req->timeout_slot < TIMEOUT);
487
488 if (req->next_timeout != NULL) {
489 req->next_timeout->pprev_timeout = req->pprev_timeout;
490 }
491 *req->pprev_timeout = req->next_timeout;
492 req->pprev_timeout = NULL;
493 req->next_timeout = NULL;
494
495 if (pxdns->timeout_list[req->timeout_slot] == NULL) {
496 pxdns->timeout_mask &= ~(1U << req->timeout_slot);
497 /* may be on pollmgr thread so no sys_untimeout */
498 }
499}
500
501
502
503/**
504 * Do bookkeeping on new request. Called from pxdns_query().
505 */
506static void
507pxdns_request_register(struct pxdns *pxdns, struct request *req)
508{
509 sys_mutex_lock(&pxdns->lock);
510
511 pxdns_hash_add(pxdns, req);
512 pxdns_timeout_add(pxdns, req);
513 ++pxdns->active_queries;
514
515 sys_mutex_unlock(&pxdns->lock);
516}
517
518
519static void
520pxdns_request_deregister(struct pxdns *pxdns, struct request *req)
521{
522 sys_mutex_lock(&pxdns->lock);
523
524 pxdns_hash_del(pxdns, req);
525 pxdns_timeout_del(pxdns, req);
526 --pxdns->active_queries;
527
528 sys_mutex_unlock(&pxdns->lock);
529}
530
531
532/**
533 * Find request by the id we used when relaying it and remove it from
534 * id hash and timeout list. Called from pxdns_pmgr_pump() when reply
535 * comes.
536 */
537static struct request *
538pxdns_request_find(struct pxdns *pxdns, u16_t id)
539{
540 struct request *req = NULL;
541
542 sys_mutex_lock(&pxdns->lock);
543
544 /* find request in the id->req hash */
545 for (req = pxdns->request_hash[HASH(id)]; req != NULL; req = req->next_hash) {
546 if (req->id == id) {
547 break;
548 }
549 }
550
551 if (req != NULL) {
552 pxdns_hash_del(pxdns, req);
553 pxdns_timeout_del(pxdns, req);
554 --pxdns->active_queries;
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
706 DPRINTF2(("%s: req %p: sending to resolver #%lu\n",
707 __func__, (void *)req, (unsigned long)req->residx));
708
709 LWIP_ASSERT1(req->generation == pxdns->generation);
710 LWIP_ASSERT1(req->residx < pxdns->nresolvers);
711 resolver = &pxdns->resolvers[req->residx];
712
713 if (resolver->sa.sa_family == AF_INET) {
714 nsent = sendto(pxdns->sock4, req->data, req->size, 0,
715 &resolver->sa, sizeof(resolver->sin));
716
717 }
718 else if (resolver->sa.sa_family == AF_INET6) {
719 if (pxdns->sock6 != INVALID_SOCKET) {
720 nsent = sendto(pxdns->sock6, req->data, req->size, 0,
721 &resolver->sa, sizeof(resolver->sin6));
722 }
723 else {
724 /* shouldn't happen, we should have weeded out IPv6 resolvers */
725 return 0;
726 }
727 }
728 else {
729 /* shouldn't happen, we should have weeded out unsupported families */
730 return 0;
731 }
732
733 if ((size_t)nsent == req->size) {
734 return 1; /* sent */
735 }
736
737 if (nsent < 0) {
738 DPRINTF2(("%s: send: %R[sockerr]\n", __func__, SOCKERRNO()));
739 }
740 else {
741 DPRINTF2(("%s: sent only %lu of %lu\n",
742 __func__, (unsigned long)nsent, (unsigned long)req->size));
743 }
744 return 0; /* not sent, caller will retry as necessary */
745}
746
747
748/**
749 * Forward request to the next resolver in the pxdns::resolvers array
750 * of upstream resolvers if there are any left.
751 */
752static int
753pxdns_rexmit(struct pxdns *pxdns, struct request *req)
754{
755 int sent;
756
757 if (/* __predict_false */ req->generation != pxdns->generation) {
758 DPRINTF2(("%s: req %p: generation %lu != pxdns generation %lu\n",
759 __func__, (void *)req,
760 (unsigned long)req->generation,
761 (unsigned long)pxdns->generation));
762 return 0;
763 }
764
765 LWIP_ASSERT1(req->residx < pxdns->nresolvers);
766 do {
767 if (++req->residx == pxdns->nresolvers) {
768 return 0;
769 }
770
771 sent = pxdns_forward_outbound(pxdns, req);
772 } while (!sent);
773
774 return 1;
775}
776
777
778static int
779pxdns_pmgr_pump(struct pollmgr_handler *handler, SOCKET fd, int revents)
780{
781 struct pxdns *pxdns;
782 struct request *req;
783 ssize_t nread;
784 err_t error;
785 u16_t id;
786
787 pxdns = (struct pxdns *)handler->data;
788 LWIP_ASSERT1(handler == &pxdns->pmhdl4 || handler == &pxdns->pmhdl6);
789 LWIP_ASSERT1(fd == (handler == &pxdns->pmhdl4 ? pxdns->sock4 : pxdns->sock6));
790
791 if (revents & ~(POLLIN|POLLERR)) {
792 DPRINTF0(("%s: unexpected revents 0x%x\n", __func__, revents));
793 return POLLIN;
794 }
795
796 if (revents & POLLERR) {
797 int sockerr = -1;
798 socklen_t optlen = (socklen_t)sizeof(sockerr);
799 int status;
800
801 status = getsockopt(fd, SOL_SOCKET,
802 SO_ERROR, (char *)&sockerr, &optlen);
803 if (status < 0) {
804 DPRINTF(("%s: sock %d: SO_ERROR failed: %R[sockerr]\n",
805 __func__, fd, SOCKERRNO()));
806 }
807 else {
808 DPRINTF(("%s: sock %d: %R[sockerr]\n",
809 __func__, fd, sockerr));
810 }
811 }
812
813 if ((revents & POLLIN) == 0) {
814 return POLLIN;
815 }
816
817
818 nread = recv(fd, pollmgr_udpbuf, sizeof(pollmgr_udpbuf), 0);
819 if (nread < 0) {
820 DPRINTF(("%s: %R[sockerr]\n", __func__, SOCKERRNO()));
821 return POLLIN;
822 }
823
824 /* check for minimum dns packet length */
825 if (nread < 12) {
826 DPRINTF2(("%s: short reply %lu bytes\n",
827 __func__, (unsigned long)nread));
828 return POLLIN;
829 }
830
831 /* XXX: shall we proxy back RCODE=Refused responses? */
832
833 memcpy(&id, pollmgr_udpbuf, sizeof(id));
834 req = pxdns_request_find(pxdns, id);
835 if (req == NULL) {
836 DPRINTF2(("%s: orphaned reply for %d\n", __func__, id));
837 ++pxdns->late_answers;
838 return POLLIN;
839 }
840
841 DPRINTF2(("%s: reply for req=%p: id %d -> client id %d\n",
842 __func__, (void *)req, req->id, req->client_id));
843
844 req->reply = pbuf_alloc(PBUF_RAW, nread, PBUF_RAM);
845 if (req->reply == NULL) {
846 DPRINTF(("%s: pbuf_alloc(%d) failed\n", __func__, (int)nread));
847 pxdns_request_free(req);
848 return POLLIN;
849 }
850
851 memcpy(pollmgr_udpbuf, &req->client_id, sizeof(req->client_id));
852 error = pbuf_take(req->reply, pollmgr_udpbuf, nread);
853 if (error != ERR_OK) {
854 DPRINTF(("%s: pbuf_take(%d) failed\n", __func__, (int)nread));
855 pxdns_request_free(req);
856 return POLLIN;
857 }
858
859 proxy_lwip_post(&req->msg_reply);
860 return POLLIN;
861}
862
863
864/**
865 * Called on lwIP thread via request::msg_reply callback.
866 */
867static void
868pxdns_pcb_reply(void *ctx)
869{
870 struct request *req = (struct request *)ctx;
871 err_t error;
872
873 error = udp_sendto(req->pcb, req->reply,
874 ipX_2_ip(&req->client_addr), req->client_port);
875 if (error != ERR_OK) {
876 DPRINTF(("%s: udp_sendto err %s\n",
877 __func__, proxy_lwip_strerr(error)));
878 }
879
880 pxdns_request_free(req);
881}
882
883
884/**
885 * TCP DNS proxy. This kicks in for large replies that don't fit into
886 * 512 bytes of UDP payload. Client will retry with TCP to get
887 * complete reply.
888 */
889static err_t
890pxdns_accept_syn(void *arg, struct tcp_pcb *newpcb, struct pbuf *syn)
891{
892 struct pxdns *pxdns = (struct pxdns *)arg;
893 union sockaddr_inet *si;
894 ipX_addr_t *dst;
895 u16_t dst_port;
896
897 tcp_accepted(pxdns->ltcp);
898
899 if (pxdns->nresolvers == 0) {
900 return ERR_CONN;
901 }
902
903 si = &pxdns->resolvers[0];
904
905 if (si->sa.sa_family == AF_INET6) {
906 dst = (ipX_addr_t *)&si->sin6.sin6_addr;
907 dst_port = ntohs(si->sin6.sin6_port);
908 }
909 else {
910 dst = (ipX_addr_t *)&si->sin.sin_addr;
911 dst_port = ntohs(si->sin.sin_port);
912 }
913
914 /*
915 * XXX: TODO: need to implement protocol hooks. E.g. here if
916 * connect fails, we should try connecting to a different server.
917 */
918 return pxtcp_pcb_accept_outbound(newpcb, syn,
919 si->sa.sa_family == AF_INET6, dst, dst_port);
920}
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