VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use