From e3373366bb81c57cdc3ec96c6936b6c9df535bb2 Mon Sep 17 00:00:00 2001 From: Aleksi Lindeman Date: Sat, 1 Dec 2018 12:43:06 +0100 Subject: Starting with upnp support --- include/sibs/DirectConnection.hpp | 17 +++- project.conf | 1 + src/DirectConnection.cpp | 191 ++++++++++++++++++++++++++++++++------ tests/main.cpp | 11 --- 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 +#include 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> connectionResults; - std::mutex connectionResultsMutex; + std::mutex connectionResultsMutex; + + struct UPNPUrlData + { + UPNPUrls *upnpUrls; + IGDdatas *igdDatas; + bool portOpened; + }; + + UPNPDev *upnpDevList; + std::vector 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 #include #include +#include +#include #ifndef WIN32 #include @@ -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 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 peer = std::make_shared(); std::unique_ptr 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 connection1 = sibs::BootstrapConnection::connect(sibs::Ipv4("127.0.0.1", PORT)).get(); bool gotData1 = false; bool gotAsdf1 = false; -- cgit v1.2.3