VirtualBox

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

Last change on this file since 51287 was 49443, checked in by vboxsync, 11 years ago

Don't schedule pxdns_timer() if we don't have any active requests.

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