aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sibs/DirectConnection.hpp17
-rw-r--r--project.conf1
-rw-r--r--src/DirectConnection.cpp191
-rw-r--r--tests/main.cpp11
4 files changed, 182 insertions, 38 deletions
diff --git a/include/sibs/DirectConnection.hpp b/include/sibs/DirectConnection.hpp
index 70eaf4f..1ba8659 100644
--- a/include/sibs/DirectConnection.hpp
+++ b/include/sibs/DirectConnection.hpp
@@ -14,6 +14,7 @@
#include "Socket.hpp"
#include "Message.hpp"
#include <future>
+#include <miniupnpc.h>
namespace sibs
{
@@ -88,6 +89,10 @@ namespace sibs
void removeDisconnectedPeers();
void receiveData();
int receiveDataFromPeer(const int socket, char *output, usize *receivedTotalSize);
+
+ void openPortUpnpDevice(const Ipv4 &myAddress, UPNPDev *device);
+ void openPort(const Ipv4 &myAddress);
+ int closeOpenedPort(const Ipv4 &myAddress, UPNPUrls *upnpUrls, IGDdatas *igdDatas);
private:
u16 port;
int eid;
@@ -97,7 +102,17 @@ namespace sibs
bool alive;
PubSubOnRemoveDisconnectedPeerCallback removeDisconnectedPeerCallback;
Ipv4Map<std::shared_future<PubSubConnectResult>> connectionResults;
- std::mutex connectionResultsMutex;
+ std::mutex connectionResultsMutex;
+
+ struct UPNPUrlData
+ {
+ UPNPUrls *upnpUrls;
+ IGDdatas *igdDatas;
+ bool portOpened;
+ };
+
+ UPNPDev *upnpDevList;
+ std::vector<UPNPUrlData> upnpData;
};
struct DirectConnectionsUtils
diff --git a/project.conf b/project.conf
index 47851f6..bac01a1 100644
--- a/project.conf
+++ b/project.conf
@@ -10,3 +10,4 @@ expose_include_dirs = ["include"]
[dependencies]
udt = "4.11"
sibs-serializer = ">=2.0.0"
+miniupnpc = "2" \ No newline at end of file
diff --git a/src/DirectConnection.cpp b/src/DirectConnection.cpp
index 5c072d5..ab58a48 100644
--- a/src/DirectConnection.cpp
+++ b/src/DirectConnection.cpp
@@ -5,6 +5,8 @@
#include <random>
#include <sibs/SafeSerializer.hpp>
#include <sibs/SafeDeserializer.hpp>
+#include <upnpcommands.h>
+#include <upnperrors.h>
#ifndef WIN32
#include <arpa/inet.h>
@@ -42,10 +44,12 @@ namespace sibs
DirectConnections::DirectConnections(u16 _port) :
port(_port == 0 ? (u16)generateRandomNumber(2000, 32000) : _port),
alive(true),
- removeDisconnectedPeerCallback(nullptr)
+ removeDisconnectedPeerCallback(nullptr),
+ upnpDevList(nullptr)
{
UDT::startup();
eid = UDT::epoll_create();
+ //openPort(Ipv4(nullptr, port));
receiveDataThread = std::thread(&DirectConnections::receiveData, this);
}
@@ -54,9 +58,138 @@ namespace sibs
alive = false;
receiveDataThread.join();
peers.clear();
+
+ /*
+ for(UPNPUrlData &upnpDataItem : upnpData)
+ {
+ if(upnpDataItem.portOpened)
+ closeOpenedPort(Ipv4(nullptr, port), upnpDataItem.upnpUrls, upnpDataItem.igdDatas);
+ FreeUPNPUrls(upnpDataItem.upnpUrls);
+ delete upnpDataItem.upnpUrls;
+ delete upnpDataItem.igdDatas;
+ }
+
+ if(upnpDevList)
+ freeUPNPDevlist(upnpDevList);
+ */
+
UDT::epoll_release(eid);
UDT::cleanup();
}
+
+ void DirectConnections::openPortUpnpDevice(const Ipv4 &myAddress, UPNPDev *device)
+ {
+ int r = 0;
+ char lanAddr[64] = "unset";
+
+ UPNPUrls *upnpUrl = new UPNPUrls();
+ IGDdatas *igdData = new IGDdatas();
+ int igdStatus = UPNP_GetValidIGD(device, upnpUrl, igdData, lanAddr, sizeof(lanAddr));
+ if(!igdStatus)
+ {
+ delete upnpUrl;
+ delete igdData;
+ Log::warn("No valid IGD found");
+ return;
+ }
+ upnpData.push_back({ upnpUrl, igdData, false });
+
+ switch(igdStatus)
+ {
+ case 1:
+ {
+ Log::debug("Found valid IGD: %s", upnpUrl->controlURL);
+ break;
+ }
+ case 2:
+ {
+ Log::debug("Found a (not connected?) IGD: %s, trying to continue anyway", upnpUrl->controlURL);
+ break;
+ }
+ case 3:
+ {
+ Log::debug("UPnP device found. Is it an IGD?: %s, trying to continue anyway", upnpUrl->controlURL);
+ break;
+ }
+ default:
+ {
+ Log::debug("Found device (igd?): %s, trying to continue anyways", upnpUrl->controlURL);
+ break;
+ }
+ }
+
+ Log::debug("LAN ip address: %s", lanAddr);
+ char externalIPAddress[40];
+ r = UPNP_GetExternalIPAddress(upnpUrl->controlURL, igdData->first.servicetype, externalIPAddress);
+ if(r != UPNPCOMMAND_SUCCESS)
+ {
+ Log::warn("UPNP_GetExternalIPAddress failed with code %d (%s)", r, strupnperror(r));
+ return;
+ }
+ Log::debug("External ip address: %s", externalIPAddress);
+
+ std::string extPort = std::to_string(myAddress.getPort());
+ std::string inPort = extPort;
+ std::string myIp = myAddress.getAddress();
+ const char *leaseDuration = "0"; // forever
+ r = UPNP_AddPortMapping(upnpUrl->controlURL, igdData->first.servicetype, extPort.c_str(), inPort.c_str(), myIp.c_str(), nullptr, "UDP", nullptr, leaseDuration);
+ if(r != UPNPCOMMAND_SUCCESS)
+ {
+ Log::warn("UPNP_AddPortMapping(%s, %s, %s) failed with code %d (%s)", extPort.c_str(), inPort.c_str(), myIp.c_str(), r, strupnperror(r));
+ return;
+ }
+ upnpData.back().portOpened = true;
+
+ char intClient[40];
+ char intPort[6];
+ char duration[16];
+ r = UPNP_GetSpecificPortMappingEntry(upnpUrl->controlURL, igdData->first.servicetype, extPort.c_str(), "UDP", nullptr, intClient, intPort, nullptr, nullptr, duration);
+ if(r != UPNPCOMMAND_SUCCESS)
+ {
+ Log::warn("UPNP_GetSpecificPortMappingEntry failed with code %d (%s)", r, strupnperror(r));
+ return;
+ }
+ Log::debug("InternalIP:Port = %s:%s", intClient, intPort);
+ Log::debug("external %s:%s %s is redirected to internal %s:%s (duration=%s)", externalIPAddress, extPort.c_str(), "UDP", intClient, intPort, duration);
+ }
+
+ void DirectConnections::openPort(const Ipv4 &myAddress)
+ {
+ const int delay = 2000;
+ int error = 0;
+
+ upnpDevList = upnpDiscover(delay, nullptr, nullptr, UPNP_LOCAL_PORT_ANY, 0, 2, &error);
+ if(!upnpDevList)
+ {
+ Log::warn("No upnp device found, error: %d (%s)", error, strupnperror(error));
+ return;
+ }
+
+ int numDevices = 0;
+ for(UPNPDev *device = upnpDevList; device; device = device->pNext)
+ {
+ ++numDevices;
+ }
+ Log::debug("List of UPNP devices found on the network (%d device(s) found):", numDevices);
+ for(UPNPDev *device = upnpDevList; device; device = device->pNext)
+ {
+ Log::debug(" desc: %s, st: %s", device->descURL, device->st);
+ openPortUpnpDevice(myAddress, device);
+ }
+ }
+
+ int DirectConnections::closeOpenedPort(const Ipv4 &myAddress, UPNPUrls *upnpUrls, IGDdatas *igdDatas)
+ {
+ std::string extPort = std::to_string(myAddress.getPort());
+ int r = UPNP_DeletePortMapping(upnpUrls->controlURL, igdDatas->first.servicetype, extPort.c_str(), "UDP", nullptr);
+ if(r != UPNPCOMMAND_SUCCESS)
+ {
+ Log::warn("UPNP_DeletePortMapping failed with code %d (%s)", r, strupnperror(r));
+ return r;
+ }
+ Log::debug("UPNP_DeletePortMapping successful for external port %s (UDP)", extPort.c_str());
+ return 0;
+ }
std::unique_ptr<Socket> DirectConnections::createSocket(const Ipv4 &addressToBind, bool rendezvous, bool reuseAddr, bool bind)
{
@@ -82,6 +215,7 @@ namespace sibs
if(rendezvous || bind)
{
+ #if 0
Ipv4 myAddr = addressToBind;
for(int i = 0; i < 2000; ++i)
{
@@ -95,6 +229,11 @@ namespace sibs
return socket;
}
throw SocketCreateException("UDT: Failed to bind after 2000 tries");
+ #endif
+ if(UDT::bind(udtSocket, (sockaddr*)&addressToBind.address, sizeof(addressToBind.address)) == UDT::ERROR)
+ {
+ throw SocketCreateException("DirectConnections::createSocket: failed to bind socket to port " + std::to_string(addressToBind.getPort()) + ". Fail reason: " + UDT::getlasterror_desc());
+ }
}
return socket;
@@ -146,9 +285,10 @@ namespace sibs
std::shared_ptr<DirectConnectionPeer> peer = std::make_shared<DirectConnectionPeer>();
std::unique_ptr<Socket> socket;
+ Ipv4 myAddress(nullptr, port);
try
{
- socket = createSocket(Ipv4(nullptr, port), rendezvous, reuseAddr, bind);
+ socket = createSocket(myAddress, rendezvous, reuseAddr, bind);
}
catch(SocketCreateException &e)
{
@@ -158,39 +298,38 @@ namespace sibs
}
int socketId = socket->udtSocket;
- if(!server)
- {
- peersMutex.lock();
- peers[socketId] = peer;
- peer->socket = std::move(socket);
- peer->address = address;
- peer->receiveDataCallbackFunc = receiveDataCallbackFunc;
- peer->type = (server ? PeerType::SERVER : PeerType::CLIENT);
- peer->routed = true;
- peer->sharedKeys = 1;
- peersMutex.unlock();
- }
-
+
Log::debug("DirectConnections: Connecting to %s peer (ip: %s, port: %d, rendezvous: %s)", server ? "server" : "client", address.getAddress().c_str(), address.getPort(), rendezvous ? "yes" : "no");
if(UDT::connect(socketId, (sockaddr*)&address.address, sizeof(address.address)) == UDT::ERROR)
{
+ if(!server)
+ {
+ peersMutex.lock();
+ peers[socketId] = peer;
+ peer->socket = std::move(socket);
+ peer->address = address;
+ peer->receiveDataCallbackFunc = receiveDataCallbackFunc;
+ peer->type = PeerType::CLIENT;
+ peer->routed = true;
+ peer->sharedKeys = 1;
+ peersMutex.unlock();
+ }
+
if(connectCallbackFunc)
connectCallbackFunc(peer, PubSubResult::RESULT_ERROR, UDT::getlasterror_desc());
return PubSubConnectResult{ peer, PubSubResult::RESULT_ERROR, UDT::getlasterror_desc() };
}
- if(server)
- {
- peersMutex.lock();
- peers[socketId] = peer;
- peer->socket = std::move(socket);
- peer->address = address;
- peer->receiveDataCallbackFunc = receiveDataCallbackFunc;
- peer->type = PeerType::SERVER;
- peer->sharedKeys = 1;
- peersMutex.unlock();
- }
+ peersMutex.lock();
+ peers[socketId] = peer;
+ peer->socket = std::move(socket);
+ peer->address = address;
+ peer->receiveDataCallbackFunc = receiveDataCallbackFunc;
+ peer->type = (server ? PeerType::SERVER : PeerType::CLIENT);
+ peer->sharedKeys = 1;
+ peersMutex.unlock();
+
UDT::epoll_add_usock(eid, socketId);
peer->socket->eid = eid;
peer->routed = false;
diff --git a/tests/main.cpp b/tests/main.cpp
index 2623884..22bef9d 100644
--- a/tests/main.cpp
+++ b/tests/main.cpp
@@ -14,17 +14,6 @@ int main()
const sibs::PubsubKey key2("zbcdefghcjklmn3pqrs5uvx2z0123F56789", 35);
sibs::BootstrapNode boostrapNode(sibs::Ipv4(nullptr, PORT));
- bool connectionFailure = false;
- try
- {
- sibs::BootstrapConnection::connect(sibs::Ipv4("127.0.0.1", 2222)).get();
- }
- catch(sibs::BootstrapConnectionException &e)
- {
- connectionFailure = true;
- }
- REQUIRE(connectionFailure);
-
std::unique_ptr<sibs::BootstrapConnection> connection1 = sibs::BootstrapConnection::connect(sibs::Ipv4("127.0.0.1", PORT)).get();
bool gotData1 = false;
bool gotAsdf1 = false;