VirtualBox

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

Last change on this file since 49409 was 49330, checked in by vboxsync, 11 years ago

NetworkServices/proxy.c: removes annoying asserts on darwin.

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