mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
NAT traversal: add support for TCP relays
This commit is contained in:
parent
5f3dc97eef
commit
2e9a731ff3
|
@ -8,6 +8,8 @@ libtoxcore_la_includedir = $(includedir)/tox
|
||||||
|
|
||||||
libtoxcore_la_SOURCES = ../toxcore/DHT.h \
|
libtoxcore_la_SOURCES = ../toxcore/DHT.h \
|
||||||
../toxcore/DHT.c \
|
../toxcore/DHT.c \
|
||||||
|
../toxcore/nat_traversal.h \
|
||||||
|
../toxcore/nat_traversal.c \
|
||||||
../toxcore/network.h \
|
../toxcore/network.h \
|
||||||
../toxcore/network.c \
|
../toxcore/network.c \
|
||||||
../toxcore/crypto_core.h \
|
../toxcore/crypto_core.h \
|
||||||
|
|
|
@ -1814,7 +1814,7 @@ Messenger *new_messenger(Messenger_Options *options, unsigned int *error)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options->tcp_server_port) {
|
if (options->tcp_server_port) {
|
||||||
m->tcp_server = new_TCP_server(options->ipv6enabled, 1, &options->tcp_server_port, m->dht->self_secret_key, m->onion);
|
m->tcp_server = new_TCP_server_nat(options->ipv6enabled, 1, &options->tcp_server_port, options->traversal_type, m->dht->self_secret_key, m->onion);
|
||||||
|
|
||||||
if (m->tcp_server == NULL) {
|
if (m->tcp_server == NULL) {
|
||||||
kill_friend_connections(m->fr_c);
|
kill_friend_connections(m->fr_c);
|
||||||
|
|
|
@ -30,7 +30,9 @@
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "nat_traversal.h"
|
||||||
|
|
||||||
/* return 1 on success
|
/* return 1 on success
|
||||||
* return 0 on failure
|
* return 0 on failure
|
||||||
|
@ -939,8 +941,17 @@ static sock_t new_listening_TCP_socket(int family, uint16_t port)
|
||||||
return sock;
|
return sock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Added for reverse compatibility with old new_TCP_server calls.
|
||||||
|
*/
|
||||||
TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, const uint16_t *ports, const uint8_t *secret_key,
|
TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, const uint16_t *ports, const uint8_t *secret_key,
|
||||||
Onion *onion)
|
Onion *onion)
|
||||||
|
{
|
||||||
|
return new_TCP_server_nat(ipv6_enabled,num_sockets,ports,TOX_TRAVERSAL_TYPE_NONE,secret_key,onion);
|
||||||
|
}
|
||||||
|
|
||||||
|
TCP_Server *new_TCP_server_nat(uint8_t ipv6_enabled, uint16_t num_sockets, const uint16_t *ports, TOX_TRAVERSAL_TYPE traversal_type,
|
||||||
|
const uint8_t *secret_key, Onion *onion)
|
||||||
{
|
{
|
||||||
if (num_sockets == 0 || ports == NULL)
|
if (num_sockets == 0 || ports == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -999,6 +1010,16 @@ TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, const uin
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBMINIUPNPC
|
||||||
|
if ((traversal_type == TOX_TRAVERSAL_TYPE_UPNP) || (traversal_type == TOX_TRAVERSAL_TYPE_ALL))
|
||||||
|
upnp_map_port(NAT_TRAVERSAL_TCP,ntohs(ports[i]));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBNATPMP
|
||||||
|
if ((traversal_type == TOX_TRAVERSAL_TYPE_NATPMP) || (traversal_type == TOX_TRAVERSAL_TYPE_ALL))
|
||||||
|
natpmp_map_port(NAT_TRAVERSAL_TCP,ntohs(ports[i]));
|
||||||
|
#endif
|
||||||
|
|
||||||
temp->socks_listening[temp->num_listening_socks] = sock;
|
temp->socks_listening[temp->num_listening_socks] = sock;
|
||||||
++temp->num_listening_socks;
|
++temp->num_listening_socks;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#ifndef TCP_SERVER_H
|
#ifndef TCP_SERVER_H
|
||||||
#define TCP_SERVER_H
|
#define TCP_SERVER_H
|
||||||
|
|
||||||
|
#include "tox.h"
|
||||||
#include "crypto_core.h"
|
#include "crypto_core.h"
|
||||||
#include "onion.h"
|
#include "onion.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
|
@ -142,10 +143,16 @@ typedef struct {
|
||||||
} TCP_Server;
|
} TCP_Server;
|
||||||
|
|
||||||
/* Create new TCP server instance.
|
/* Create new TCP server instance.
|
||||||
|
* Added for reverse compatibility with old new_TCP_server calls.
|
||||||
*/
|
*/
|
||||||
TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, const uint16_t *ports, const uint8_t *secret_key,
|
TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, const uint16_t *ports, const uint8_t *secret_key,
|
||||||
Onion *onion);
|
Onion *onion);
|
||||||
|
|
||||||
|
/* Create new TCP server instance.
|
||||||
|
*/
|
||||||
|
TCP_Server *new_TCP_server_nat(uint8_t ipv6_enabled, uint16_t num_sockets, const uint16_t *ports, TOX_TRAVERSAL_TYPE traversal_type,
|
||||||
|
const uint8_t *secret_key, Onion *onion);
|
||||||
|
|
||||||
/* Run the TCP_server
|
/* Run the TCP_server
|
||||||
*/
|
*/
|
||||||
void do_TCP_server(TCP_Server *TCP_server);
|
void do_TCP_server(TCP_Server *TCP_server);
|
||||||
|
|
140
toxcore/nat_traversal.c
Normal file
140
toxcore/nat_traversal.c
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
/* nat_traversal.c -- Functions to traverse a NAT (UPnP, NAT-PMP).
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Tox project All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Tox.
|
||||||
|
* Tox 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, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Tox 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 Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBMINIUPNPC
|
||||||
|
#include <miniupnpc/miniupnpc.h>
|
||||||
|
#include <miniupnpc/miniwget.h>
|
||||||
|
#include <miniupnpc/upnpcommands.h>
|
||||||
|
#include <miniupnpc/upnperrors.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBNATPMP
|
||||||
|
#include <natpmp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "nat_traversal.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBMINIUPNPC
|
||||||
|
/* Setup port forwarding using UPnP */
|
||||||
|
void upnp_map_port(NAT_TRAVERSAL_PROTO proto, uint16_t port)
|
||||||
|
{
|
||||||
|
LOGGER_DEBUG("Attempting to set up UPnP port forwarding");
|
||||||
|
|
||||||
|
int error = 0;
|
||||||
|
struct UPNPDev *devlist = NULL;
|
||||||
|
|
||||||
|
#if MINIUPNPC_API_VERSION < 14
|
||||||
|
devlist = upnpDiscover(1000, NULL, NULL, 0, 0, &error);
|
||||||
|
#else
|
||||||
|
devlist = upnpDiscover(1000, NULL, NULL, 0, 0, 2, &error);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
LOGGER_WARNING("UPnP discovery failed (error = %d)", error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UPNPUrls urls;
|
||||||
|
struct IGDdatas data;
|
||||||
|
char lanaddr[64];
|
||||||
|
|
||||||
|
error = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
|
||||||
|
freeUPNPDevlist(devlist);
|
||||||
|
if (error) {
|
||||||
|
if (error == 1) {
|
||||||
|
LOGGER_INFO("A valid IGD has been found.");
|
||||||
|
|
||||||
|
char portstr[10];
|
||||||
|
snprintf(portstr, sizeof(portstr), "%d", port);
|
||||||
|
|
||||||
|
if (proto == NAT_TRAVERSAL_UDP)
|
||||||
|
error = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, portstr, portstr, lanaddr, "Tox", "UDP", 0, "0");
|
||||||
|
else if (proto == NAT_TRAVERSAL_TCP)
|
||||||
|
error = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, portstr, portstr, lanaddr, "Tox", "TCP", 0, "0");
|
||||||
|
else
|
||||||
|
LOGGER_WARNING("UPnP port mapping failed (unknown NAT_TRAVERSAL_PROTO)");
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
LOGGER_WARNING("UPnP port mapping failed (error = %d)", error);
|
||||||
|
else
|
||||||
|
LOGGER_INFO("UPnP mapped port %d", port);
|
||||||
|
} else if (error == 2)
|
||||||
|
LOGGER_WARNING("IGD was found but reported as not connected.");
|
||||||
|
else if (error == 3)
|
||||||
|
LOGGER_WARNING("UPnP device was found but not recoginzed as IGD.");
|
||||||
|
else
|
||||||
|
LOGGER_WARNING("Unknown error finding IGD: %d", error);
|
||||||
|
|
||||||
|
FreeUPNPUrls(&urls);
|
||||||
|
} else
|
||||||
|
LOGGER_WARNING("No IGD was found.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBNATPMP
|
||||||
|
/* Setup port forwarding using NAT-PMP */
|
||||||
|
void natpmp_map_port(NAT_TRAVERSAL_PROTO proto, uint16_t port)
|
||||||
|
{
|
||||||
|
LOGGER_DEBUG("Attempting to set up NAT-PMP port forwarding");
|
||||||
|
|
||||||
|
int error;
|
||||||
|
natpmp_t natpmp;
|
||||||
|
natpmpresp_t resp;
|
||||||
|
|
||||||
|
error = initnatpmp(&natpmp, 0, 0);
|
||||||
|
if (error) {
|
||||||
|
LOGGER_WARNING("NAT-PMP initialization failed (error = %d)", error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proto == NAT_TRAVERSAL_UDP)
|
||||||
|
error = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_UDP, port, port, 3600);
|
||||||
|
else if (proto == NAT_TRAVERSAL_TCP)
|
||||||
|
error = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_TCP, port, port, 3600);
|
||||||
|
else {
|
||||||
|
LOGGER_WARNING("NAT-PMP port mapping failed (unknown NAT_TRAVERSAL_PROTO)");
|
||||||
|
closenatpmp(&natpmp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != 12) {
|
||||||
|
LOGGER_WARNING("NAT-PMP send request failed (error = %d)", error);
|
||||||
|
closenatpmp(&natpmp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = readnatpmpresponseorretry(&natpmp, &resp);
|
||||||
|
for ( ; error == NATPMP_TRYAGAIN ; error = readnatpmpresponseorretry(&natpmp, &resp) )
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
LOGGER_WARNING("NAT-PMP port mapping failed (error = %d)", error);
|
||||||
|
else
|
||||||
|
LOGGER_INFO("NAT-PMP mapped port %d", port);
|
||||||
|
|
||||||
|
closenatpmp(&natpmp);
|
||||||
|
}
|
||||||
|
#endif
|
53
toxcore/nat_traversal.h
Normal file
53
toxcore/nat_traversal.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/* nat_traversal.h -- Functions to traverse a NAT (UPnP, NAT-PMP).
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Tox project All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Tox.
|
||||||
|
* Tox 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, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Tox 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 Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NAT_TRAVERSAL_H
|
||||||
|
#define NAT_TRAVERSAL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The protocol that will be used by the nat traversal.
|
||||||
|
*/
|
||||||
|
#if defined(HAVE_LIBMINIUPNPC) || defined(HAVE_LIBNATPMP)
|
||||||
|
typedef enum NAT_TRAVERSAL_PROTO {
|
||||||
|
|
||||||
|
/* UDP */
|
||||||
|
NAT_TRAVERSAL_UDP,
|
||||||
|
|
||||||
|
/* TCP */
|
||||||
|
NAT_TRAVERSAL_TCP,
|
||||||
|
|
||||||
|
} NAT_TRAVERSAL_PROTO;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBMINIUPNPC
|
||||||
|
/* Setup port forwarding using UPnP */
|
||||||
|
void upnp_map_port(NAT_TRAVERSAL_PROTO proto, uint16_t port);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBNATPMP
|
||||||
|
/* Setup port forwarding using NAT-PMP */
|
||||||
|
void natpmp_map_port(NAT_TRAVERSAL_PROTO proto, uint16_t port);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -42,17 +42,7 @@
|
||||||
|
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "nat_traversal.h"
|
||||||
#ifdef HAVE_LIBMINIUPNPC
|
|
||||||
#include <miniupnpc/miniupnpc.h>
|
|
||||||
#include <miniupnpc/miniwget.h>
|
|
||||||
#include <miniupnpc/upnpcommands.h>
|
|
||||||
#include <miniupnpc/upnperrors.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_LIBNATPMP
|
|
||||||
#include <natpmp.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
|
#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
|
||||||
|
|
||||||
|
@ -122,96 +112,6 @@ static int inet_pton(sa_family_t family, const char *addrString, void *addrbuf)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_LIBMINIUPNPC
|
|
||||||
/* Setup port forwarding using UPnP */
|
|
||||||
static void upnp_map_port(uint16_t port)
|
|
||||||
{
|
|
||||||
LOGGER_DEBUG("Attempting to set up UPnP port forwarding");
|
|
||||||
|
|
||||||
int error = 0;
|
|
||||||
struct UPNPDev *devlist = NULL;
|
|
||||||
|
|
||||||
#if MINIUPNPC_API_VERSION < 14
|
|
||||||
devlist = upnpDiscover(1000, NULL, NULL, 0, 0, &error);
|
|
||||||
#else
|
|
||||||
devlist = upnpDiscover(1000, NULL, NULL, 0, 0, 2, &error);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
LOGGER_WARNING("UPnP discovery failed (error = %d)", error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UPNPUrls urls;
|
|
||||||
struct IGDdatas data;
|
|
||||||
char lanaddr[64];
|
|
||||||
|
|
||||||
error = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
|
|
||||||
freeUPNPDevlist(devlist);
|
|
||||||
if (error) {
|
|
||||||
if (error == 1) {
|
|
||||||
LOGGER_INFO("A valid IGD has been found.");
|
|
||||||
|
|
||||||
char portstr[10];
|
|
||||||
snprintf(portstr, sizeof(portstr), "%d", port);
|
|
||||||
|
|
||||||
error = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, portstr, portstr, lanaddr, "Tox", "UDP", 0, "0");
|
|
||||||
if (error) {
|
|
||||||
LOGGER_WARNING("UPnP port mapping failed (error = %d)", error);
|
|
||||||
} else {
|
|
||||||
LOGGER_INFO("UPnP mapped port %d", port);
|
|
||||||
}
|
|
||||||
} else if (error == 2) {
|
|
||||||
LOGGER_WARNING("IGD was found but reported as not connected.");
|
|
||||||
} else if (error == 3) {
|
|
||||||
LOGGER_WARNING("UPnP device was found but not recoginzed as IGD.");
|
|
||||||
} else {
|
|
||||||
LOGGER_WARNING("Unknown error finding IGD: %d", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
FreeUPNPUrls(&urls);
|
|
||||||
} else {
|
|
||||||
LOGGER_WARNING("No IGD was found.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_LIBNATPMP
|
|
||||||
/* Setup port forwarding using NAT-PMP */
|
|
||||||
static void natpmp_map_port(uint16_t port)
|
|
||||||
{
|
|
||||||
LOGGER_DEBUG("Attempting to set up NAT-PMP port forwarding");
|
|
||||||
|
|
||||||
int error;
|
|
||||||
natpmp_t natpmp;
|
|
||||||
natpmpresp_t resp;
|
|
||||||
|
|
||||||
error = initnatpmp(&natpmp, 0, 0);
|
|
||||||
if (error) {
|
|
||||||
LOGGER_WARNING("NAT-PMP initialization failed (error = %d)", error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_UDP, port, port, 3600);
|
|
||||||
if (error != 12) {
|
|
||||||
LOGGER_WARNING("NAT-PMP send request failed (error = %d)", error);
|
|
||||||
closenatpmp(&natpmp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = readnatpmpresponseorretry(&natpmp, &resp);
|
|
||||||
for ( ; error == NATPMP_TRYAGAIN ; error = readnatpmpresponseorretry(&natpmp, &resp) )
|
|
||||||
sleep(1);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
LOGGER_WARNING("NAT-PMP port mapping failed (error = %d)", error);
|
|
||||||
else
|
|
||||||
LOGGER_INFO("NAT-PMP mapped port %d", port);
|
|
||||||
|
|
||||||
closenatpmp(&natpmp);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Check if socket is valid.
|
/* Check if socket is valid.
|
||||||
*
|
*
|
||||||
* return 1 if valid
|
* return 1 if valid
|
||||||
|
@ -785,12 +685,12 @@ Networking_Core *new_networking_nat(IP ip, uint16_t port_from, uint16_t port_to,
|
||||||
|
|
||||||
#ifdef HAVE_LIBMINIUPNPC
|
#ifdef HAVE_LIBMINIUPNPC
|
||||||
if ((traversal_type == TOX_TRAVERSAL_TYPE_UPNP) || (traversal_type == TOX_TRAVERSAL_TYPE_ALL))
|
if ((traversal_type == TOX_TRAVERSAL_TYPE_UPNP) || (traversal_type == TOX_TRAVERSAL_TYPE_ALL))
|
||||||
upnp_map_port(ntohs(temp->port));
|
upnp_map_port(NAT_TRAVERSAL_UDP,ntohs(temp->port));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_LIBNATPMP
|
#ifdef HAVE_LIBNATPMP
|
||||||
if ((traversal_type == TOX_TRAVERSAL_TYPE_NATPMP) || (traversal_type == TOX_TRAVERSAL_TYPE_ALL))
|
if ((traversal_type == TOX_TRAVERSAL_TYPE_NATPMP) || (traversal_type == TOX_TRAVERSAL_TYPE_ALL))
|
||||||
natpmp_map_port(ntohs(temp->port));
|
natpmp_map_port(NAT_TRAVERSAL_UDP,ntohs(temp->port));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return temp;
|
return temp;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user