VirtualBox

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

Last change on this file since 54124 was 54124, checked in by vboxsync, 10 years ago

NAT/Network: Add missing Id/@file/copyright headers.

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