aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAleksi Lindeman <dec05eba@protonmail.com>2018-12-01 12:43:06 +0100
committerAleksi Lindeman <dec05eba@protonmail.com>2018-12-01 12:43:06 +0100
commite3373366bb81c57cdc3ec96c6936b6c9df535bb2 (patch)
tree27b3f0299a3b955768625b8e8a85dd09b52ce6ac /src
parent9b5d7e4f7a402d52280015a12d2268eed060f1cd (diff)
Starting with upnp support
Diffstat (limited to 'src')
-rw-r--r--src/DirectConnection.cpp191
1 files changed, 165 insertions, 26 deletions
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;