VirtualBox

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

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

Change vestigial names proxytest.* to proxy.*

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