/******************************************************************************* * libretroshare/src/util: extaddrfinder.cc * * * * libretroshare: retroshare core library * * * * Copyright (C) 2017 Retroshare Team * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation, either version 3 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public License * * along with this program. If not, see . * * * *******************************************************************************/ //#define EXTADDRSEARCH_DEBUG #include "extaddrfinder.h" #include "pqi/pqinetwork.h" #include "rsdebug.h" #include "util/rsstring.h" #include "util/rsmemory.h" #ifndef WIN32 #include #endif #include #include #include #include #include #include #include #include "util/rstime.h" #include const uint32_t MAX_IP_STORE = 300; /* seconds ip address timeout */ class ZeroInt { public: ZeroInt() : n(0) {} uint32_t n ; }; void ExtAddrFinder::run() { std::vector res ; for(const auto& it : _ip_servers) { std::string ip = ""; rsGetHostByNameSpecDNS(it,"myip.opendns.com",ip,2); if(ip != "") res.push_back(ip) ; #ifdef EXTADDRSEARCH_DEBUG RS_DBG("ip found through DNS ", it, ": \"", ip, "\""); #endif } if(res.empty()) { reset(); return ; } std::map addrV4_votes; std::map addrV6_votes; std::string all_addrV4_Found; std::string all_addrV6_Found; for(auto curRes : res) { sockaddr_storage addr; sockaddr_storage_clear(addr); bool validIP = sockaddr_storage_inet_pton(addr, curRes) && sockaddr_storage_isValidNet(addr); bool isIPv4 = sockaddr_storage_ipv6_to_ipv4(addr); if( validIP && isIPv4 ) { addr.ss_family = AF_INET; addrV4_votes[addr].n++ ; all_addrV4_Found += sockaddr_storage_tostring(addr) + "\n"; } else if( validIP && !isIPv4) { addr.ss_family = AF_INET6; addrV6_votes[addr].n++ ; all_addrV6_Found += sockaddr_storage_tostring(addr) + "\n"; } else RS_ERR("Invalid addresse reported: ", curRes) ; } if( (0 == addrV4_votes.size()) && (0 == addrV6_votes.size()) ) { RS_ERR("Could not find any external address."); reset(); return ; } if( 1 < addrV4_votes.size() ) RS_ERR("Multiple external IPv4 addresses reported: " , all_addrV4_Found ) ; if( 1 < addrV6_votes.size() ) RS_ERR("Multiple external IPv6 addresses reported: " , all_addrV6_Found ) ; { RS_STACK_MUTEX(mAddrMtx); mSearching = false ; mFoundTS = time(NULL) ; // Only save more reported address if not only once. uint32_t admax = 0 ; sockaddr_storage_clear(mAddrV4); for (auto it : addrV4_votes) if (admax < it.second.n) { mAddrV4 = it.first ; mFoundV4 = true ; admax = it.second.n ; } admax = 0 ; sockaddr_storage_clear(mAddrV6); for (auto it : addrV6_votes) if (admax < it.second.n) { mAddrV6 = it.first ; mFoundV6 = true ; admax = it.second.n ; } } return ; } void ExtAddrFinder::start_request() { if (!isRunning()) start("ExtAddrFinder"); } bool ExtAddrFinder::hasValidIPV4(struct sockaddr_storage &addr) { #ifdef EXTADDRSEARCH_DEBUG RS_DBG("Getting ip."); #endif { RS_STACK_MUTEX(mAddrMtx) ; if(mFoundV4) { #ifdef EXTADDRSEARCH_DEBUG RS_DBG("Has stored ip responding with this ip:", sockaddr_storage_iptostring(mAddrV4)) ; #endif sockaddr_storage_copyip(addr,mAddrV4); // just copy the IP so we dont erase the port. } } testTimeOut(); RS_STACK_MUTEX(mAddrMtx) ; return mFoundV4; } bool ExtAddrFinder::hasValidIPV6(struct sockaddr_storage &addr) { #ifdef EXTADDRSEARCH_DEBUG RS_DBG("Getting ip."); #endif { RS_STACK_MUTEX(mAddrMtx) ; if(mFoundV6) { #ifdef EXTADDRSEARCH_DEBUG RS_DBG("Has stored ip responding with this ip:", sockaddr_storage_iptostring(mAddrV6)) ; #endif sockaddr_storage_copyip(addr,mAddrV6); // just copy the IP so we dont erase the port. } } testTimeOut(); RS_STACK_MUTEX(mAddrMtx) ; return mFoundV6; } void ExtAddrFinder::testTimeOut() { bool timeOut; { RS_STACK_MUTEX(mAddrMtx) ; //timeout the current ip timeOut = (mFoundTS + MAX_IP_STORE < time(NULL)); } if(timeOut || mFirstTime) {//launch a research if( mAddrMtx.trylock()) { if(!mSearching) { #ifdef EXTADDRSEARCH_DEBUG RS_DBG("No stored ip: Initiating new search."); #endif mSearching = true ; start_request() ; } #ifdef EXTADDRSEARCH_DEBUG else RS_DBG("Already searching."); #endif mFirstTime = false; mAddrMtx.unlock(); } #ifdef EXTADDRSEARCH_DEBUG else RS_DBG("(Note) Could not acquire lock. Busy."); #endif } } void ExtAddrFinder::reset(bool firstTime /*=false*/) { #ifdef EXTADDRSEARCH_DEBUG RS_DBG("firstTime=", firstTime); #endif RS_STACK_MUTEX(mAddrMtx) ; mSearching = false ; mFoundV4 = false ; mFoundV6 = false ; mFirstTime = firstTime; mFoundTS = time(nullptr); sockaddr_storage_clear(mAddrV4); sockaddr_storage_clear(mAddrV6); } ExtAddrFinder::~ExtAddrFinder() { #ifdef EXTADDRSEARCH_DEBUG RS_DBG("Deleting ExtAddrFinder."); #endif } ExtAddrFinder::ExtAddrFinder() : mAddrMtx("ExtAddrFinder") { #ifdef EXTADDRSEARCH_DEBUG RS_DBG("Creating new ExtAddrFinder."); #endif reset( true ); //https://unix.stackexchange.com/questions/22615/how-can-i-get-my-external-ip-address-in-a-shell-script //Enter direct ip so local DNS cannot change it. //DNS servers must recognize "myip.opendns.com" _ip_servers.push_back(std::string( "208.67.222.222" )) ;//resolver1.opendns.com _ip_servers.push_back(std::string( "208.67.220.220" )) ;//resolver2.opendns.com _ip_servers.push_back(std::string( "208.67.222.220" )) ;//resolver3.opendns.com _ip_servers.push_back(std::string( "208.67.220.222" )) ;//resolver4.opendns.com _ip_servers.push_back(std::string( "2620:119:35::35" )) ;//resolver1.opendns.com _ip_servers.push_back(std::string( "2620:119:53::53" )) ;//resolver2.opendns.com }