diff --git a/daemon/src/upnp/upnp.cpp b/daemon/src/upnp/upnp.cpp index 0ba1b2d2c6c14e9ab4095929482df773a445b58b..ac2ff91c48abbb946f2f4ca875330fb38f639b57 100644 --- a/daemon/src/upnp/upnp.cpp +++ b/daemon/src/upnp/upnp.cpp @@ -56,38 +56,40 @@ namespace ring { namespace upnp { #if HAVE_UPNP -IGD::IGD(IGDdatas d, UPNPUrls u) - : datas_(std::make_shared<const IGDdatas>(d)) +static void +resetURLs(UPNPUrls& urls) { - UPNPUrls newURLs = UPNPUrls(u); - copyURLs(u, &newURLs); - urls_ = std::make_shared<const UPNPUrls>(newURLs); -}; + urls.controlURL = nullptr; + urls.ipcondescURL = nullptr; + urls.controlURL_CIF = nullptr; + urls.controlURL_6FC = nullptr; +#ifdef MINIUPNPC_VERSION /* if not defined, its version 1.6 */ + urls.rootdescURL = nullptr; +#endif +} -IGD::~IGD() +/* move constructor */ +IGD::IGD(IGD&& other) + : datas_(other.datas_) + , urls_(other.urls_) { - /* if last user, then free the URLs */ - if (urls_.unique() and urls_) { - UPNPUrls urls = UPNPUrls(*urls_.get()); - FreeUPNPUrls(&urls); - } + resetURLs(other.urls_); } -void -IGD::copyURLs(const UPNPUrls &urlsSource, UPNPUrls *urlsDest) +/* move operator */ +IGD& IGD::operator=(IGD&& other) { - if (urlsSource.controlURL != nullptr) - urlsDest->controlURL = strdup(urlsSource.controlURL); - if (urlsSource.ipcondescURL != nullptr) - urlsDest->ipcondescURL = strdup(urlsSource.ipcondescURL); - if (urlsSource.controlURL_CIF != nullptr) - urlsDest->controlURL_CIF = strdup(urlsSource.controlURL_CIF); - if (urlsSource.controlURL_6FC != nullptr) - urlsDest->controlURL_6FC = strdup(urlsSource.controlURL_6FC); -#ifdef MINIUPNPC_VERSION /* if not defined, its version 1.6 */ - if (urlsSource.rootdescURL != nullptr) - urlsDest->rootdescURL = strdup(urlsSource.rootdescURL); -#endif + datas_ = other.datas_; + urls_ = other.urls_; + resetURLs(other.urls_); + return *this; +} + +IGD::~IGD() +{ + /* free the URLs */ + RING_WARN("UPnP : deleting IGD"); + FreeUPNPUrls(&urls_); } #endif /* HAVE_UPNP */ @@ -100,8 +102,8 @@ bool IGD::isEmpty() const { #if HAVE_UPNP - if (urls_ and urls_->controlURL != nullptr) { - if (urls_->controlURL[0] == '\0') { + if (urls_.controlURL != nullptr) { + if (urls_.controlURL[0] == '\0') { return true; } else { return false; @@ -228,8 +230,8 @@ Controller::addMapping(uint16_t port_external, uint16_t port_internal, PortType /* mapping doesn't exist, so try to add it */ RING_DBG("UPnP : adding port mapping : %s", mapping.toString().c_str()); - upnp_status = UPNP_AddPortMapping(mapping.igd->getURLs()->controlURL, - mapping.igd->getDatas()->first.servicetype, + upnp_status = UPNP_AddPortMapping(mapping.igd->getURLs().controlURL, + mapping.igd->getDatas().first.servicetype, mapping.getExternalPort().c_str(), mapping.getInternalPort().c_str(), mapping.local_ip.toString().c_str(), @@ -269,6 +271,11 @@ bool Controller::addAnyMapping(uint16_t port_desired, uint16_t port_local, PortType type, bool use_same_port, bool unique, uint16_t *port_used) { #if HAVE_UPNP + if (defaultIGD_->isEmpty()) { + RING_WARN("UPnP : cannot perform command as the IGD has either not been chosen or is not available."); + return false; + } + auto globalMappings = type == PortType::UDP ? (PortMapGlobal*)udpGlobalMappings_.get() : (PortMapGlobal*)tcpGlobalMappings_.get(); if (unique) { /* check that port is not already used by the client */ @@ -374,8 +381,8 @@ Controller::removeMappings(PortType type) { /* delete the mapping from the router if this instance was the only user */ if (users < 2) { RING_DBG("UPnP : deleting mapping: %s", mapping.toString().c_str()); - int upnp_status = UPNP_DeletePortMapping(mapping.igd->getURLs()->controlURL, - mapping.igd->getDatas()->first.servicetype, + int upnp_status = UPNP_DeletePortMapping(mapping.igd->getURLs().controlURL, + mapping.igd->getDatas().first.servicetype, mapping.getExternalPort().c_str(), mapping.getType().c_str(), NULL); @@ -448,8 +455,8 @@ Controller::removeMappingsByLocalIPAndDescription(const std::string& description extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0'; - upnp_status = UPNP_GetGenericPortMappingEntry(defaultIGD_->getURLs()->controlURL, - defaultIGD_->getDatas()->first.servicetype, + upnp_status = UPNP_GetGenericPortMappingEntry(defaultIGD_->getURLs().controlURL, + defaultIGD_->getDatas().first.servicetype, index, extPort, intClient, intPort, protocol, desc, enabled, @@ -463,7 +470,7 @@ Controller::removeMappingsByLocalIPAndDescription(const std::string& description RING_DBG("UPnP : found mapping with matching description and ip:\n\t%s %5s->%s:%-5s '%s'", protocol, extPort, intClient, intPort, desc); int delete_err = 0; - delete_err = UPNP_DeletePortMapping(defaultIGD_->getURLs()->controlURL, defaultIGD_->getDatas()->first.servicetype, extPort, protocol, NULL); + delete_err = UPNP_DeletePortMapping(defaultIGD_->getURLs().controlURL, defaultIGD_->getDatas().first.servicetype, extPort, protocol, NULL); if(delete_err != UPNPCOMMAND_SUCCESS) { RING_DBG("UPnP : UPNP_DeletePortMapping() failed with error code %d : %s", delete_err, strupnperror(delete_err)); } else { @@ -504,7 +511,7 @@ Controller::getExternalIP() #if HAVE_UPNP if (defaultIGD_->isEmpty()) { RING_WARN("UPnP : cannot perform command as the IGD has either not been chosen or is not available."); - return IpAddr(); + return {}; } int upnp_status; @@ -512,19 +519,19 @@ Controller::getExternalIP() RING_DBG("UPnP : getting external IP"); - upnp_status = UPNP_GetExternalIPAddress(defaultIGD_->getURLs()->controlURL, - defaultIGD_->getDatas()->first.servicetype, + upnp_status = UPNP_GetExternalIPAddress(defaultIGD_->getURLs().controlURL, + defaultIGD_->getDatas().first.servicetype, externalIPAddress); if(upnp_status != UPNPCOMMAND_SUCCESS) { RING_DBG("UPnP : GetExternalIPAddress failed with error code %d : %s", upnp_status, strupnperror(upnp_status)); - return IpAddr(); + return {}; } else { RING_DBG("UPnP : got external IP = %s", externalIPAddress); - return IpAddr(std::string(externalIPAddress)); + return {std::string(externalIPAddress)}; } #else /* return empty address */ - return IpAddr(); + return {}; #endif } @@ -535,73 +542,63 @@ static IGD chooseIGD(void) { #if HAVE_UPNP - struct UPNPDev * devlist = nullptr; + std::unique_ptr<UPNPDev, decltype(freeUPNPDevlist)&> devlist(nullptr, freeUPNPDevlist); int upnp_status = 0; RING_DBG("UPnP : finding default IGD"); /* look for UPnP devices on the network */ - devlist = upnpDiscover(2000, NULL, NULL, 0, 0, &upnp_status); + devlist.reset(upnpDiscover(2000, NULL, NULL, 0, 0, &upnp_status)); if (devlist) { - struct UPNPDev * device = nullptr; - struct UPNPUrls newIGDURLs = UPNPUrls(); - struct IGDdatas newIGDDatas = IGDdatas(); + UPNPDev * device = nullptr; + std::unique_ptr<UPNPUrls, decltype(FreeUPNPUrls)&> newIGDURLs(new UPNPUrls, FreeUPNPUrls); + std::unique_ptr<IGDdatas> newIGDDatas(new IGDdatas); char lanaddr[64]; - newIGDURLs = UPNPUrls(); - newIGDDatas = IGDdatas(); - RING_DBG("UPnP devices found on the network"); - for(device = devlist; device; device = device->pNext) + for(device = devlist.get(); device; device = device->pNext) { RING_DBG(" desc: %s\n st: %s", device->descURL, device->st); } - upnp_status = UPNP_GetValidIGD(devlist, &newIGDURLs, &newIGDDatas, lanaddr, sizeof(lanaddr)); + upnp_status = UPNP_GetValidIGD(devlist.get(), newIGDURLs.get(), newIGDDatas.get(), lanaddr, sizeof(lanaddr)); switch(upnp_status) { case -1: RING_ERR("UPnP : internal error getting valid IGD"); - newIGDURLs = UPNPUrls(); - newIGDDatas = IGDdatas(); break; case 0: RING_WARN("UPnP : no valid IGD found"); - FreeUPNPUrls(&newIGDURLs); - newIGDURLs = UPNPUrls(); - newIGDDatas = IGDdatas(); break; case 1: - RING_DBG("UPnP : found valid IGD : %s", newIGDURLs.controlURL); + RING_DBG("UPnP : found valid IGD : %s", newIGDURLs->controlURL); break; case 2: - RING_DBG("UPnP : found a (not connected?) IGD, will try to use it anyway: %s", newIGDURLs.controlURL); + RING_WARN("UPnP : found an IGD, but it does not seem to be connected : %s", newIGDURLs->controlURL); break; case 3: - RING_DBG("UPnP : UPnP device found, cannot determine if it is an IGD, will try to use it anyway : %s", newIGDURLs.controlURL); + RING_WARN("UPnP : UPnP device found, but it does not seem to be an IGD : %s", newIGDURLs->controlURL); break; default: - RING_DBG("UPnP : device found, cannot determine if it is a UPnP device, will try to use it anyway : %s", newIGDURLs.controlURL); + RING_WARN("UPnP : device found, but cannot determine if it is a UPnP device : %s", newIGDURLs->controlURL); } - if (upnp_status > 0) + /* only accept IGD if it was determined to be valid */ + if (upnp_status == 1) { RING_DBG("UPnP : local IP address reported as: %s", lanaddr); - - IGD igd{newIGDDatas, newIGDURLs}; - - freeUPNPDevlist(devlist); - devlist = nullptr; - - return igd; + return {*newIGDDatas.release(), *newIGDURLs.release()}; + } else { + return {}; + } } else { RING_WARN("UPnP : looking for IGD UPnP devices on the network failed with error: %d : %s", upnp_status, strupnperror(upnp_status)); - return IGD(); + return {}; } #else - return IGD(); + return {}; #endif } diff --git a/daemon/src/upnp/upnp.h b/daemon/src/upnp/upnp.h index 22a16d8e596ad7ee88f2d3fd81dad33a68c499d1..86902edbf3c934ad27f457db9cce032d3022d9ee 100644 --- a/daemon/src/upnp/upnp.h +++ b/daemon/src/upnp/upnp.h @@ -55,16 +55,18 @@ public: #if HAVE_UPNP - IGD(IGDdatas d = IGDdatas(), UPNPUrls u = UPNPUrls()); + /* constructors */ + IGD() : datas_(), urls_() {}; + IGD(IGDdatas& d , UPNPUrls& u) : datas_(d), urls_(u) {}; /* move constructor and operator */ - IGD(IGD&& other) = default; - IGD& operator=(IGD&&) = default; + IGD(IGD&& other); + IGD& operator=(IGD&& other); ~IGD(); - std::shared_ptr<const IGDdatas> getDatas() const {return datas_;}; - std::shared_ptr<const UPNPUrls> getURLs() const {return urls_;}; + const IGDdatas& getDatas() const {return datas_;}; + const UPNPUrls& getURLs() const {return urls_;}; #else /* use default constructor and destructor */ #endif @@ -74,10 +76,8 @@ private: NON_COPYABLE(IGD); #if HAVE_UPNP - void copyURLs(const UPNPUrls &urlsSource, UPNPUrls *urlsDest); - - std::shared_ptr<const IGDdatas> datas_; - std::shared_ptr<const UPNPUrls> urls_; + IGDdatas datas_; + UPNPUrls urls_; #endif };