VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NAT/proxy.c@ 52288

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

NAT/Net: Ouch, FIONBIO argument has been wrong since initial check-in
so on Windows we have been doing blocking connect(). Fortunately,
WSAEventSelect() automatically switches sockets to non-blocking mode
when we start polling them.

Fix it and adjust the test of connect()'s errno, since Windows returns
EWOULDBLOCK instead of EINPROGRESS on non-blocking connect.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 16.5 KB
Line 
1/* -*- indent-tabs-mode: nil; -*- */
2#define LOG_GROUP LOG_GROUP_NAT_SERVICE
3
4#include "winutils.h"
5
6#include "proxy.h"
7#include "proxy_pollmgr.h"
8#include "portfwd.h"
9
10#include "lwip/opt.h"
11
12#include "lwip/sys.h"
13#include "lwip/tcpip.h"
14
15#ifndef RT_OS_WINDOWS
16#include <sys/poll.h>
17#include <sys/socket.h>
18#include <netinet/in.h>
19#include <arpa/inet.h>
20#include <fcntl.h>
21#include <stdio.h>
22#include <iprt/string.h>
23#include <unistd.h>
24#include <err.h>
25#else
26# include <iprt/string.h>
27#endif
28
29#if defined(SOCK_NONBLOCK) && defined(RT_OS_NETBSD) /* XXX: PR kern/47569 */
30# undef SOCK_NONBLOCK
31#endif
32
33#ifndef __arraycount
34# define __arraycount(a) (sizeof(a)/sizeof(a[0]))
35#endif
36
37static FNRTSTRFORMATTYPE proxy_sockerr_rtstrfmt;
38
39static SOCKET proxy_create_socket(int, int);
40
41volatile struct proxy_options *g_proxy_options;
42static sys_thread_t pollmgr_tid;
43
44/* XXX: for mapping loopbacks to addresses in our network (ip4) */
45struct netif *g_proxy_netif;
46
47
48/*
49 * Called on the lwip thread (aka tcpip thread) from tcpip_init() via
50 * its "tcpip_init_done" callback. Raw API is ok to use here
51 * (e.g. rtadvd), but netconn API is not.
52 */
53void
54proxy_init(struct netif *proxy_netif, struct proxy_options *opts)
55{
56 int status;
57
58 LWIP_ASSERT1(opts != NULL);
59 LWIP_UNUSED_ARG(proxy_netif);
60
61 status = RTStrFormatTypeRegister("sockerr", proxy_sockerr_rtstrfmt, NULL);
62 AssertRC(status);
63
64 g_proxy_options = opts;
65 g_proxy_netif = proxy_netif;
66
67#if 1
68 proxy_rtadvd_start(proxy_netif);
69#endif
70
71 /*
72 * XXX: We use stateless DHCPv6 only to report IPv6 address(es) of
73 * nameserver(s). Since we don't yet support IPv6 addresses in
74 * HostDnsService, there's no point in running DHCPv6.
75 */
76#if 0
77 dhcp6ds_init(proxy_netif);
78#endif
79
80 if (opts->tftp_root != NULL) {
81 tftpd_init(proxy_netif, opts->tftp_root);
82 }
83
84 status = pollmgr_init();
85 if (status < 0) {
86 errx(EXIT_FAILURE, "failed to initialize poll manager");
87 /* NOTREACHED */
88 }
89
90 pxtcp_init();
91 pxudp_init();
92
93 portfwd_init();
94
95 pxdns_init(proxy_netif);
96
97 pxping_init(proxy_netif, opts->icmpsock4, opts->icmpsock6);
98
99 pollmgr_tid = sys_thread_new("pollmgr_thread",
100 pollmgr_thread, NULL,
101 DEFAULT_THREAD_STACKSIZE,
102 DEFAULT_THREAD_PRIO);
103 if (!pollmgr_tid) {
104 errx(EXIT_FAILURE, "failed to create poll manager thread");
105 /* NOTREACHED */
106 }
107}
108
109
110#if !defined(RT_OS_WINDOWS)
111/**
112 * Formatter for %R[sockerr] - unix strerror_r() version.
113 */
114static DECLCALLBACK(size_t)
115proxy_sockerr_rtstrfmt(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
116 const char *pszType, const void *pvValue,
117 int cchWidth, int cchPrecision, unsigned int fFlags,
118 void *pvUser)
119{
120 const int error = (int)(intptr_t)pvValue;
121 size_t cb = 0;
122
123 const char *msg = NULL;
124 char buf[128];
125
126 NOREF(cchWidth);
127 NOREF(cchPrecision);
128 NOREF(fFlags);
129 NOREF(pvUser);
130
131 AssertReturn(strcmp(pszType, "sockerr") == 0, 0);
132
133 /* make sure return type mismatch is caught */
134#if defined(RT_OS_LINUX) && defined(_GNU_SOURCE)
135 msg = strerror_r(error, buf, sizeof(buf));
136#else
137 {
138 int status = strerror_r(error, buf, sizeof(buf));
139 msg = buf;
140 }
141#endif
142 return RTStrFormat(pfnOutput, pvArgOutput, NULL, NULL, "%s", msg);
143}
144
145#else /* RT_OS_WINDOWS */
146
147/**
148 * Formatter for %R[sockerr] - windows FormatMessage() version.
149 */
150static DECLCALLBACK(size_t)
151proxy_sockerr_rtstrfmt(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
152 const char *pszType, const void *pvValue,
153 int cchWidth, int cchPrecision, unsigned int fFlags,
154 void *pvUser)
155{
156 const int error = (int)(intptr_t)pvValue;
157 size_t cb = 0;
158
159 NOREF(cchWidth);
160 NOREF(cchPrecision);
161 NOREF(fFlags);
162 NOREF(pvUser);
163
164 AssertReturn(strcmp(pszType, "sockerr") == 0, 0);
165
166 /*
167 * XXX: Windows strerror() doesn't handle posix error codes, but
168 * since winsock uses its own, it shouldn't be much of a problem.
169 * If you see a strange error message, it's probably from
170 * FormatMessage() for an error from <WinError.h> that has the
171 * same numeric value.
172 */
173 if (error < _sys_nerr) {
174 char buf[128] = "";
175 int status;
176
177 status = strerror_s(buf, sizeof(buf), error);
178 if (status == 0) {
179 if (strcmp(buf, "Unknown error") == 0) {
180 /* windows strerror() doesn't add the numeric value */
181 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, NULL,
182 "Unknown error: %d", error);
183 }
184 else {
185 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, NULL,
186 "%s", buf);
187 }
188 }
189 else {
190 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, NULL,
191 "Unknown error: %d", error);
192 }
193 }
194 else {
195 DWORD nchars;
196 char *msg = NULL;
197
198 nchars = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM
199 | FORMAT_MESSAGE_ALLOCATE_BUFFER,
200 NULL, error, LANG_NEUTRAL,
201 (LPSTR)&msg, 0,
202 NULL);
203 if (nchars == 0 || msg == NULL) {
204 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, NULL,
205 "Unknown error: %d", error);
206 }
207 else {
208 /* FormatMessage() "helpfully" adds newline; get rid of it */
209 char *crpos = strchr(msg, '\r');
210 if (crpos != NULL) {
211 *crpos = '\0';
212 }
213
214 cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, NULL,
215 "%s", msg);
216 }
217
218 if (msg != NULL) {
219 LocalFree(msg);
220 }
221 }
222
223 return cb;
224}
225#endif /* RT_OS_WINDOWS */
226
227
228/**
229 * Send static callback message from poll manager thread to lwip
230 * thread, scheduling a function call in lwip thread context.
231 *
232 * XXX: Existing lwip api only provides non-blocking version for this.
233 * It may fail when lwip thread is not running (mbox invalid) or if
234 * post failed (mbox full). How to handle these?
235 */
236void
237proxy_lwip_post(struct tcpip_msg *msg)
238{
239 struct tcpip_callback_msg *m;
240 err_t error;
241
242 LWIP_ASSERT1(msg != NULL);
243
244 /*
245 * lwip plays games with fake incomplete struct tag to enforce API
246 */
247 m = (struct tcpip_callback_msg *)msg;
248 error = tcpip_callbackmsg(m);
249
250 if (error == ERR_VAL) {
251 /* XXX: lwip thread is not running (mbox invalid) */
252 LWIP_ASSERT1(error != ERR_VAL);
253 }
254
255 LWIP_ASSERT1(error == ERR_OK);
256}
257
258
259/**
260 * Create a non-blocking socket. Disable SIGPIPE for TCP sockets if
261 * possible. On Linux it's not possible and should be disabled for
262 * each send(2) individually.
263 */
264static SOCKET
265proxy_create_socket(int sdom, int stype)
266{
267 SOCKET s;
268 int stype_and_flags;
269 int status;
270
271 LWIP_UNUSED_ARG(status); /* depends on ifdefs */
272
273
274 stype_and_flags = stype;
275
276#if defined(SOCK_NONBLOCK)
277 stype_and_flags |= SOCK_NONBLOCK;
278#endif
279
280 /*
281 * Disable SIGPIPE on disconnected socket. It might be easier to
282 * forgo it and just use MSG_NOSIGNAL on each send*(2), since we
283 * have to do it for Linux anyway, but Darwin does NOT have that
284 * flag (but has SO_NOSIGPIPE socket option).
285 */
286#if !defined(SOCK_NOSIGPIPE) && !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
287#if 0 /* XXX: Solaris has neither, the program should ignore SIGPIPE globally */
288#error Need a way to disable SIGPIPE on connection oriented sockets!
289#endif
290#endif
291
292#if defined(SOCK_NOSIGPIPE)
293 if (stype == SOCK_STREAM) {
294 stype_and_flags |= SOCK_NOSIGPIPE;
295 }
296#endif
297
298 s = socket(sdom, stype_and_flags, 0);
299 if (s == INVALID_SOCKET) {
300 DPRINTF(("socket: %R[sockerr]\n", SOCKERRNO()));
301 return INVALID_SOCKET;
302 }
303
304#if !defined(SOCK_NONBLOCK) && !defined(RT_OS_WINDOWS)
305 {
306 int sflags;
307
308 sflags = fcntl(s, F_GETFL, 0);
309 if (sflags < 0) {
310 DPRINTF(("F_GETFL: %R[sockerr]\n", SOCKERRNO()));
311 closesocket(s);
312 return INVALID_SOCKET;
313 }
314
315 status = fcntl(s, F_SETFL, sflags | O_NONBLOCK);
316 if (status < 0) {
317 DPRINTF(("O_NONBLOCK: %R[sockerr]\n", SOCKERRNO()));
318 closesocket(s);
319 return INVALID_SOCKET;
320 }
321 }
322#endif
323
324#if !defined(SOCK_NOSIGPIPE) && defined(SO_NOSIGPIPE)
325 if (stype == SOCK_STREAM) {
326 int on = 1;
327 const socklen_t onlen = sizeof(on);
328
329 status = setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &on, onlen);
330 if (status < 0) {
331 DPRINTF(("SO_NOSIGPIPE: %R[sockerr]\n", SOCKERRNO()));
332 closesocket(s);
333 return INVALID_SOCKET;
334 }
335 }
336#endif
337
338#if defined(RT_OS_WINDOWS)
339 {
340 u_long mode = 1;
341 status = ioctlsocket(s, FIONBIO, &mode);
342 if (status == SOCKET_ERROR) {
343 DPRINTF(("FIONBIO: %R[sockerr]\n", SOCKERRNO()));
344 closesocket(s);
345 return INVALID_SOCKET;
346 }
347 }
348#endif
349
350 return s;
351}
352
353
354/**
355 * Create a socket for outbound connection to dst_addr:dst_port.
356 *
357 * The socket is non-blocking and TCP sockets has SIGPIPE disabled if
358 * possible. On Linux it's not possible and should be disabled for
359 * each send(2) individually.
360 */
361SOCKET
362proxy_connected_socket(int sdom, int stype,
363 ipX_addr_t *dst_addr, u16_t dst_port)
364{
365 struct sockaddr_in6 dst_sin6;
366 struct sockaddr_in dst_sin;
367 struct sockaddr *pdst_sa;
368 socklen_t dst_sa_len;
369 void *pdst_addr;
370 const struct sockaddr *psrc_sa;
371 socklen_t src_sa_len;
372 int status;
373 int sockerr;
374 SOCKET s;
375
376 LWIP_ASSERT1(sdom == PF_INET || sdom == PF_INET6);
377 LWIP_ASSERT1(stype == SOCK_STREAM || stype == SOCK_DGRAM);
378
379 DPRINTF(("---> %s ", stype == SOCK_STREAM ? "TCP" : "UDP"));
380 if (sdom == PF_INET6) {
381 pdst_sa = (struct sockaddr *)&dst_sin6;
382 pdst_addr = (void *)&dst_sin6.sin6_addr;
383
384 memset(&dst_sin6, 0, sizeof(dst_sin6));
385#if HAVE_SA_LEN
386 dst_sin6.sin6_len =
387#endif
388 dst_sa_len = sizeof(dst_sin6);
389 dst_sin6.sin6_family = AF_INET6;
390 memcpy(&dst_sin6.sin6_addr, &dst_addr->ip6, sizeof(ip6_addr_t));
391 dst_sin6.sin6_port = htons(dst_port);
392
393 DPRINTF(("[%RTnaipv6]:%d ", &dst_sin6.sin6_addr, dst_port));
394 }
395 else { /* sdom = PF_INET */
396 pdst_sa = (struct sockaddr *)&dst_sin;
397 pdst_addr = (void *)&dst_sin.sin_addr;
398
399 memset(&dst_sin, 0, sizeof(dst_sin));
400#if HAVE_SA_LEN
401 dst_sin.sin_len =
402#endif
403 dst_sa_len = sizeof(dst_sin);
404 dst_sin.sin_family = AF_INET;
405 dst_sin.sin_addr.s_addr = dst_addr->ip4.addr; /* byte-order? */
406 dst_sin.sin_port = htons(dst_port);
407
408 DPRINTF(("%RTnaipv4:%d ", dst_sin.sin_addr.s_addr, dst_port));
409 }
410
411 s = proxy_create_socket(sdom, stype);
412 if (s == INVALID_SOCKET) {
413 return INVALID_SOCKET;
414 }
415 DPRINTF(("socket %d\n", s));
416
417 /* TODO: needs locking if dynamic modifyvm is allowed */
418 if (sdom == PF_INET6) {
419 psrc_sa = (const struct sockaddr *)g_proxy_options->src6;
420 src_sa_len = sizeof(struct sockaddr_in6);
421 }
422 else {
423 psrc_sa = (const struct sockaddr *)g_proxy_options->src4;
424 src_sa_len = sizeof(struct sockaddr_in);
425 }
426 if (psrc_sa != NULL) {
427 status = bind(s, psrc_sa, src_sa_len);
428 if (status == SOCKET_ERROR) {
429 sockerr = SOCKERRNO();
430 DPRINTF(("socket %d: bind: %R[sockerr]\n", s, sockerr));
431 closesocket(s);
432 SET_SOCKERRNO(sockerr);
433 return INVALID_SOCKET;
434 }
435 }
436
437 status = connect(s, pdst_sa, dst_sa_len);
438 if (status == SOCKET_ERROR
439#if !defined(RT_OS_WINDOWS)
440 && SOCKERRNO() != EINPROGRESS
441#else
442 && SOCKERRNO() != EWOULDBLOCK
443#endif
444 )
445 {
446 sockerr = SOCKERRNO();
447 DPRINTF(("socket %d: connect: %R[sockerr]\n", s, sockerr));
448 closesocket(s);
449 SET_SOCKERRNO(sockerr);
450 return INVALID_SOCKET;
451 }
452
453 return s;
454}
455
456
457/**
458 * Create a socket for inbound (port-forwarded) connections to
459 * src_addr (port is part of sockaddr, so not a separate argument).
460 *
461 * The socket is non-blocking and TCP sockets has SIGPIPE disabled if
462 * possible. On Linux it's not possible and should be disabled for
463 * each send(2) individually.
464 *
465 * TODO?: Support v6-mapped v4 so that user can specify she wants
466 * "udp" and get both versions?
467 */
468SOCKET
469proxy_bound_socket(int sdom, int stype, struct sockaddr *src_addr)
470{
471 SOCKET s;
472 int on;
473 const socklen_t onlen = sizeof(on);
474 int status;
475 int sockerr;
476
477 s = proxy_create_socket(sdom, stype);
478 if (s == INVALID_SOCKET) {
479 return INVALID_SOCKET;
480 }
481 DPRINTF(("socket %d\n", s));
482
483 on = 1;
484 status = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, onlen);
485 if (status < 0) { /* not good, but not fatal */
486 DPRINTF(("SO_REUSEADDR: %R[sockerr]\n", SOCKERRNO()));
487 }
488
489 status = bind(s, src_addr,
490 sdom == PF_INET ?
491 sizeof(struct sockaddr_in)
492 : sizeof(struct sockaddr_in6));
493 if (status == SOCKET_ERROR) {
494 sockerr = SOCKERRNO();
495 DPRINTF(("bind: %R[sockerr]\n", sockerr));
496 closesocket(s);
497 SET_SOCKERRNO(sockerr);
498 return INVALID_SOCKET;
499 }
500
501 if (stype == SOCK_STREAM) {
502 status = listen(s, 5);
503 if (status == SOCKET_ERROR) {
504 sockerr = SOCKERRNO();
505 DPRINTF(("listen: %R[sockerr]\n", sockerr));
506 closesocket(s);
507 SET_SOCKERRNO(sockerr);
508 return INVALID_SOCKET;
509 }
510 }
511
512 return s;
513}
514
515
516void
517proxy_reset_socket(SOCKET s)
518{
519 struct linger linger;
520
521 linger.l_onoff = 1;
522 linger.l_linger = 0;
523
524 /* On Windows we can run into issue here, perhaps SO_LINGER isn't enough, and
525 * we should use WSA{Send,Recv}Disconnect instead.
526 *
527 * Links for the reference:
528 * http://msdn.microsoft.com/en-us/library/windows/desktop/ms738547%28v=vs.85%29.aspx
529 * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4468997
530 */
531 setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&linger, sizeof(linger));
532
533 closesocket(s);
534}
535
536
537int
538proxy_sendto(SOCKET sock, struct pbuf *p, void *name, size_t namelen)
539{
540 struct pbuf *q;
541 size_t i, clen;
542#ifndef RT_OS_WINDOWS
543 struct msghdr mh;
544 ssize_t nsent;
545#else
546 DWORD nsent;
547#endif
548 int rc;
549 IOVEC fixiov[8]; /* fixed size (typical case) */
550 const size_t fixiovsize = sizeof(fixiov)/sizeof(fixiov[0]);
551 IOVEC *dyniov; /* dynamically sized */
552 IOVEC *iov;
553 int error = 0;
554
555 /*
556 * Static iov[] is usually enough since UDP protocols use small
557 * datagrams to avoid fragmentation, but be prepared.
558 */
559 clen = pbuf_clen(p);
560 if (clen > fixiovsize) {
561 /*
562 * XXX: TODO: check that clen is shorter than IOV_MAX
563 */
564 dyniov = (IOVEC *)malloc(clen * sizeof(*dyniov));
565 if (dyniov == NULL) {
566 error = -errno; /* sic: not a socket error */
567 goto out;
568 }
569 iov = dyniov;
570 }
571 else {
572 dyniov = NULL;
573 iov = fixiov;
574 }
575
576
577 for (q = p, i = 0; i < clen; q = q->next, ++i) {
578 LWIP_ASSERT1(q != NULL);
579
580 IOVEC_SET_BASE(iov[i], q->payload);
581 IOVEC_SET_LEN(iov[i], q->len);
582 }
583
584#ifndef RT_OS_WINDOWS
585 memset(&mh, 0, sizeof(mh));
586 mh.msg_name = name;
587 mh.msg_namelen = namelen;
588 mh.msg_iov = iov;
589 mh.msg_iovlen = clen;
590
591 nsent = sendmsg(sock, &mh, 0);
592 rc = (nsent >= 0) ? 0 : SOCKET_ERROR;
593#else
594 rc = WSASendTo(sock, iov, (DWORD)clen, &nsent, 0,
595 name, (int)namelen, NULL, NULL);
596#endif
597 if (rc == SOCKET_ERROR) {
598 error = SOCKERRNO();
599 DPRINTF(("%s: socket %d: sendmsg: %R[sockerr]\n",
600 __func__, sock, error));
601 error = -error;
602 }
603
604 out:
605 if (dyniov != NULL) {
606 free(dyniov);
607 }
608 return error;
609}
610
611
612static const char *lwiperr[] = {
613 "ERR_OK",
614 "ERR_MEM",
615 "ERR_BUF",
616 "ERR_TIMEOUT",
617 "ERR_RTE",
618 "ERR_INPROGRESS",
619 "ERR_VAL",
620 "ERR_WOULDBLOCK",
621 "ERR_USE",
622 "ERR_ISCONN",
623 "ERR_ABRT",
624 "ERR_RST",
625 "ERR_CLSD",
626 "ERR_CONN",
627 "ERR_ARG",
628 "ERR_IF"
629};
630
631
632const char *
633proxy_lwip_strerr(err_t error)
634{
635 static char buf[32];
636 int e = -error;
637
638 if (0 < e || e < (int)__arraycount(lwiperr)) {
639 return lwiperr[e];
640 }
641 else {
642 RTStrPrintf(buf, sizeof(buf), "unknown error %d", error);
643 return buf;
644 }
645}
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