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 --- src/DirectConnection.cpp | 191 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 165 insertions(+), 26 deletions(-) (limited to 'src') 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; -- cgit v1.2.3