VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NAT/proxytest.c@ 48306

Last change on this file since 48306 was 48241, checked in by vboxsync, 11 years ago

Provide ability to bind proxy socket to a local address - to be used
to implement equivalent of --natbindip<N>.

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