VirtualBox

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

Last change on this file since 51582 was 51581, checked in by vboxsync, 11 years ago

NAT/Net: Start untangling errno vs. winsock mess. Don't refer errno
directly, while here convert some perror() calls to DPRINTFs.

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