summaryrefslogtreecommitdiffstats
path: root/src/VBox/NetworkServices/NetLib/VBoxNetPortForwardString.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/NetworkServices/NetLib/VBoxNetPortForwardString.cpp
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/NetworkServices/NetLib/VBoxNetPortForwardString.cpp')
-rw-r--r--src/VBox/NetworkServices/NetLib/VBoxNetPortForwardString.cpp382
1 files changed, 382 insertions, 0 deletions
diff --git a/src/VBox/NetworkServices/NetLib/VBoxNetPortForwardString.cpp b/src/VBox/NetworkServices/NetLib/VBoxNetPortForwardString.cpp
new file mode 100644
index 00000000..c60d6860
--- /dev/null
+++ b/src/VBox/NetworkServices/NetLib/VBoxNetPortForwardString.cpp
@@ -0,0 +1,382 @@
+/* $Id: VBoxNetPortForwardString.cpp $ */
+/** @file
+ * VBoxNetPortForwardString - Routines for managing port-forward strings.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifndef RT_OS_WINDOWS
+# include <netinet/in.h>
+#else
+# include <iprt/win/winsock2.h>
+# include <Ws2ipdef.h>
+#endif
+
+#include <iprt/cdefs.h>
+#include <iprt/cidr.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/getopt.h>
+#include <iprt/net.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+
+#include <VBox/log.h>
+
+#include "VBoxPortForwardString.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define PF_FIELD_SEPARATOR ':'
+#define PF_ADDRESS_FIELD_STARTS '['
+#define PF_ADDRESS_FIELD_ENDS ']'
+
+#define PF_STR_FIELD_SEPARATOR ":"
+#define PF_STR_ADDRESS_FIELD_STARTS "["
+#define PF_STR_ADDRESS_FIELD_ENDS "]"
+
+
+static int netPfStrAddressParse(char *pszRaw, size_t cchRaw,
+ char *pszAddress, int cbAddress,
+ bool fEmptyAcceptable)
+{
+ size_t cchField = 0;
+
+ AssertPtrReturn(pszRaw, -1);
+ AssertPtrReturn(pszAddress, -1);
+ AssertReturn(pszRaw[0] == PF_ADDRESS_FIELD_STARTS, -1);
+
+ if (pszRaw[0] == PF_ADDRESS_FIELD_STARTS)
+ {
+ /* shift pszRaw to next symbol */
+ pszRaw++;
+ cchRaw--;
+
+
+ /* we shouldn't face with ending here */
+ AssertReturn(cchRaw > 0, VERR_INVALID_PARAMETER);
+
+ char *pszEndOfAddress = RTStrStr(pszRaw, PF_STR_ADDRESS_FIELD_ENDS);
+
+ /* no pair closing sign */
+ AssertPtrReturn(pszEndOfAddress, VERR_INVALID_PARAMETER);
+
+ cchField = pszEndOfAddress - pszRaw;
+
+ /* field should be less then the rest of the string */
+ AssertReturn(cchField < cchRaw, VERR_INVALID_PARAMETER);
+
+ if (cchField != 0)
+ RTStrCopy(pszAddress, RT_MIN(cchField + 1, (size_t)cbAddress), pszRaw);
+ else if (!fEmptyAcceptable)
+ return -1;
+ }
+
+ AssertReturn(pszRaw[cchField] == PF_ADDRESS_FIELD_ENDS, -1);
+
+ return (int)cchField + 2; /* length of the field and closing braces */
+}
+
+
+/**
+ * Parses a port something.
+ *
+ * @returns Offset relative to @a pszRaw of the end of the port field.
+ * -1 on failure.
+ * @param pszRaw The zero terminated string to parse. Points a field
+ * separator.
+ * @param pu16Port Where to store the port number on success.
+ */
+static int netPfStrPortParse(char *pszRaw, uint16_t *pu16Port)
+{
+#if 1
+ AssertPtrReturn(pszRaw, -1);
+ AssertPtrReturn(pu16Port, -1);
+ AssertReturn(pszRaw[0] == PF_FIELD_SEPARATOR, -1);
+
+ char *pszNext = NULL;
+ int rc = RTStrToUInt16Ex(&pszRaw[1], &pszNext, 0, pu16Port);
+ if (rc == VWRN_TRAILING_CHARS)
+ AssertReturn(*pszNext == PF_FIELD_SEPARATOR, -1);
+ else if (rc == VINF_SUCCESS)
+ Assert(*pszNext == '\0');
+ else
+ AssertMsgFailedReturn(("rc=%Rrc\n", rc), -1);
+ if (*pu16Port == 0)
+ return -1;
+ return (int)(pszNext - pszRaw);
+
+#else /* The same code, just a little more verbose: */
+ char *pszEndOfPort = NULL;
+ uint16_t u16Port = 0;
+ int idxRaw = 1; /* we increment pszRaw after checks. */
+ int cbRest = 0;
+ size_t cbPort = 0;
+
+ AssertPtrReturn(pszRaw, -1);
+ AssertPtrReturn(pu16Port, -1);
+ AssertReturn(pszRaw[0] == PF_FIELD_SEPARATOR, -1);
+
+ pszRaw++; /* skip field separator */
+ cchRaw --;
+
+ char *pszEndOfPort = RTStrStr(pszRaw, ":");
+ if (!pszEndOfPort)
+ {
+ cbRest = strlen(pszRaw);
+
+ Assert(cchRaw == cbRest);
+
+ /* XXX: Assumption that if string is too big, it will be reported by
+ * RTStrToUint16.
+ */
+ if (cbRest > 0)
+ {
+ pszEndOfPort = pszRaw + cbRest;
+ cbPort = cbRest;
+ }
+ else
+ return -1;
+ }
+ else
+ cbPort = pszEndOfPort - pszRaw;
+
+
+ idxRaw += cbPort;
+
+ Assert(cbRest || pszRaw[idxRaw - 1] == PF_FIELD_SEPARATOR); /* we are 1 char ahead */
+
+ char szPort[10];
+ RT_ZERO(szPort);
+
+ Assert(idxRaw > 0);
+ RTStrCopy(szPort, RT_MIN(sizeof(szPort), (size_t)(cbPort) + 1), pszRaw);
+
+ if (!(u16Port = RTStrToUInt16(szPort)))
+ return -1;
+
+ *pu16Port = u16Port;
+
+ return idxRaw;
+#endif
+}
+
+
+static int netPfStrAddressPortPairParse(char *pszRaw, size_t cchRaw,
+ char *pszAddress, int cbAddress,
+ bool fEmptyAddressAcceptable,
+ uint16_t *pu16Port)
+{
+ int idxRaw = 0;
+ int idxRawTotal = 0;
+
+ AssertPtrReturn(pszRaw, -1);
+ AssertPtrReturn(pszAddress, -1);
+ AssertPtrReturn(pu16Port, -2);
+
+ /* XXX: Here we should check 0 - ':' and 1 - '[' */
+ Assert( pszRaw[0] == PF_FIELD_SEPARATOR
+ && pszRaw[1] == PF_ADDRESS_FIELD_STARTS);
+
+ pszRaw++; /* field separator skip */
+ cchRaw--;
+ AssertReturn(cchRaw > 0, VERR_INVALID_PARAMETER);
+
+ idxRaw = 0;
+
+ if (pszRaw[0] == PF_ADDRESS_FIELD_STARTS)
+ {
+ idxRaw += netPfStrAddressParse(pszRaw,
+ cchRaw - idxRaw,
+ pszAddress,
+ cbAddress,
+ fEmptyAddressAcceptable);
+ if (idxRaw == -1)
+ return -1;
+
+ Assert(pszRaw[idxRaw] == PF_FIELD_SEPARATOR);
+ }
+ else return -1;
+
+ pszRaw += idxRaw;
+ idxRawTotal += idxRaw;
+ cchRaw -= idxRaw;
+
+ AssertReturn(cchRaw > 0, VERR_INVALID_PARAMETER);
+
+ idxRaw = 0;
+
+ Assert(pszRaw[0] == PF_FIELD_SEPARATOR);
+
+ if (pszRaw[0] == PF_FIELD_SEPARATOR)
+ {
+ idxRaw = netPfStrPortParse(pszRaw, pu16Port);
+
+ Assert(strlen(&pszRaw[idxRaw]) == 0 || pszRaw[idxRaw] == PF_FIELD_SEPARATOR);
+
+ if (idxRaw == -1)
+ return -2;
+
+ idxRawTotal += idxRaw;
+
+ return idxRawTotal + 1;
+ }
+ else return -1; /* trailing garbage in the address */
+}
+
+/* XXX: Having fIPv6 we might emprove adress verification comparing address length
+ * with INET[6]_ADDRLEN
+ *
+ */
+int netPfStrToPf(const char *pcszStrPortForward, bool fIPv6, PPORTFORWARDRULE pPfr)
+{
+/** r=bird: Redo from scratch? This is very hard to read. And it's going about
+ * things in a very complicated, potentially leaky (pszRaw) fashion. */
+
+ int proto;
+ uint16_t u16HostPort;
+ uint16_t u16GuestPort;
+ bool fTcpProto = false;
+
+ int idxRaw = 0;
+ int cbToken = 0;
+
+ AssertPtrReturn(pcszStrPortForward, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pPfr, VERR_INVALID_PARAMETER);
+
+ RT_ZERO(*pPfr);
+
+ char *pszHostAddr = &pPfr->szPfrHostAddr[0];
+ char *pszGuestAddr = &pPfr->szPfrGuestAddr[0];
+ char *pszName = &pPfr->szPfrName[0];
+
+ size_t cchRaw = strlen(pcszStrPortForward);
+
+ /* Minimal rule ":tcp:[]:0:[]:0" has got lenght 14 */
+ AssertReturn(cchRaw > 14, VERR_INVALID_PARAMETER);
+
+ char *pszRaw = RTStrDup(pcszStrPortForward);
+ AssertReturn(pszRaw, VERR_NO_MEMORY);
+
+ char *pszRawBegin = pszRaw;
+
+ /* name */
+ if (pszRaw[idxRaw] == PF_FIELD_SEPARATOR)
+ idxRaw = 1; /* begin of the next segment */
+ else
+ {
+ char *pszEndOfName = RTStrStr(pszRaw + 1, PF_STR_FIELD_SEPARATOR);
+ if (!pszEndOfName)
+ goto invalid_parameter;
+
+ cbToken = pszEndOfName - pszRaw; /* don't take : into account */
+ /* XXX it's unacceptable to have only name entry in PF */
+ AssertReturn(cbToken < (ssize_t)cchRaw, VERR_INVALID_PARAMETER);
+
+ if ( cbToken < 0
+ || (size_t)cbToken >= PF_NAMELEN)
+ goto invalid_parameter;
+
+ RTStrCopy(pszName,
+ RT_MIN((size_t)cbToken + 1, PF_NAMELEN),
+ pszRaw);
+ pszRaw += cbToken; /* move to separator */
+ cchRaw -= cbToken;
+ }
+
+ AssertReturn(pszRaw[0] == PF_FIELD_SEPARATOR, VERR_INVALID_PARAMETER);
+ /* protocol */
+
+ pszRaw++; /* skip separator */
+ cchRaw--;
+ idxRaw = 0;
+
+ if ( ( (fTcpProto = (RTStrNICmp(pszRaw, "tcp", 3) == 0))
+ || RTStrNICmp(pszRaw, "udp", 3) == 0)
+ && pszRaw[3] == PF_FIELD_SEPARATOR)
+ {
+ proto = (fTcpProto ? IPPROTO_TCP : IPPROTO_UDP);
+ idxRaw = 3;
+ }
+ else
+ goto invalid_parameter;
+
+ pszRaw += idxRaw;
+ cchRaw -= idxRaw;
+
+ idxRaw = netPfStrAddressPortPairParse(pszRaw, cchRaw,
+ pszHostAddr, INET6_ADDRSTRLEN,
+ true, &u16HostPort);
+ if (idxRaw < 0)
+ return VERR_INVALID_PARAMETER;
+
+ pszRaw += idxRaw;
+ cchRaw -= idxRaw;
+
+ Assert(pszRaw[0] == PF_FIELD_SEPARATOR);
+
+ idxRaw = netPfStrAddressPortPairParse(pszRaw, cchRaw,
+ pszGuestAddr, INET6_ADDRSTRLEN,
+ false, &u16GuestPort);
+
+ if (idxRaw < 0)
+ goto invalid_parameter;
+
+ /* XXX: fill the rule */
+ pPfr->fPfrIPv6 = fIPv6;
+ pPfr->iPfrProto = proto;
+
+ pPfr->u16PfrHostPort = u16HostPort;
+
+ if (*pszGuestAddr == '\0')
+ goto invalid_parameter; /* guest address should be defined */
+
+ pPfr->u16PfrGuestPort = u16GuestPort;
+
+ Log(("name: %s\n"
+ "proto: %d\n"
+ "host address: %s\n"
+ "host port: %d\n"
+ "guest address: %s\n"
+ "guest port:%d\n",
+ pszName, proto,
+ pszHostAddr, u16HostPort,
+ pszGuestAddr, u16GuestPort));
+
+ RTStrFree(pszRawBegin);
+ return VINF_SUCCESS;
+
+invalid_parameter:
+ RTStrFree(pszRawBegin);
+ if (pPfr)
+ RT_ZERO(*pPfr);
+ return VERR_INVALID_PARAMETER;
+}