X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fio%2Fraw_socket.cxx;h=2ef5d4c4dca82b4955a3b7e364a43cedf06af1ae;hb=1cb9a79fd48ce6151f6a2cf8d07ef23e002a81b1;hp=7fe2c6884cb61ca49dd07915a74a7bcce7e93bc2;hpb=b7654c181dc8d52875365b170bc3092e8e51d68f;p=simgear.git diff --git a/simgear/io/raw_socket.cxx b/simgear/io/raw_socket.cxx index 7fe2c688..2ef5d4c4 100644 --- a/simgear/io/raw_socket.cxx +++ b/simgear/io/raw_socket.cxx @@ -39,7 +39,8 @@ #include #if defined(WINSOCK) -# include +# include +# include # include #else # include @@ -56,8 +57,161 @@ #define socklen_t int #endif +#include + #include #include +#include + +namespace { + + + +class Resolver : public SGThread +{ +public: + static Resolver* instance() + { + if (!static_instance) { + static_instance = new Resolver; + atexit(&Resolver::cleanup); + static_instance->start(); + } + + return static_instance; + } + + static void cleanup() + { + static_instance->cancel(); + } + + Resolver() + { + // take the lock initially, thread will wait upon it once running + _lock.lock(); + } + + simgear::IPAddress* lookup(const string& host) + { + simgear::IPAddress* result = NULL; + _lock.lock(); + AddressCache::iterator it = _cache.find(host); + if (it == _cache.end()) { + _cache[host] = NULL; // mark as needing looked up + _wait.signal(); // if the thread was sleeping, poke it + } else { + result = it->second; + } + _lock.unlock(); + return result; + } + + simgear::IPAddress* lookupSync(const string& host) + { + simgear::IPAddress* result = NULL; + _lock.lock(); + AddressCache::iterator it = _cache.find(host); + if (it == _cache.end()) { + _lock.unlock(); + result = new simgear::IPAddress; + bool ok = lookupHost(host.c_str(), *result); + _lock.lock(); + if (ok) { + _cache[host] = result; // mark as needing looked up + } else { + delete result; + result = NULL; + } + } else { // found in cache, easy + result = it->second; + } + _lock.unlock(); + return result; + } +protected: + /** + * run method waits on a condition (_wait), and when awoken, + * finds any unresolved entries in _cache, resolves them, and goes + * back to sleep. + */ + virtual void run() + { + while (true) { + _wait.wait(_lock); + AddressCache::iterator it; + + for (it = _cache.begin(); it != _cache.end(); ++it) { + if (it->second == NULL) { + string h = it->first; + + _lock.unlock(); + simgear::IPAddress* addr = new simgear::IPAddress; + // may take seconds or even minutes! + lookupHost(h.c_str(), *addr); + _lock.lock(); + + // cahce may have changed while we had the lock released - + // so iterators may be invalid: restart the traversal + it = _cache.begin(); + _cache[h] = addr; + } // of found un-resolved entry + } // of un-resolved address iteration + } // of thread run loop + } +private: + static Resolver* static_instance; + + /** + * The actual synchronous, blocking host lookup function + * do *not* call this with any locks (mutexs) held, since depending + * on local system configuration / network availability, it + * may block for seconds or minutes. + */ + bool lookupHost(const char* host, simgear::IPAddress& addr) + { + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + bool ok = false; + + struct addrinfo* result0 = NULL; + int err = getaddrinfo(host, NULL, &hints, &result0); + if (err) { + SG_LOG(SG_IO, SG_WARN, "getaddrinfo failed for '" << host << "' : " << gai_strerror(err)); + return false; + } else { + struct addrinfo* result; + for (result = result0; result != NULL; result = result->ai_next) { + if (result->ai_family != AF_INET) { // only accept IP4 for the moment + continue; + } + + if (result->ai_addrlen != addr.getAddrLen()) { + SG_LOG(SG_IO, SG_ALERT, "mismatch in socket address sizes: got " << + result->ai_addrlen << ", expected " << addr.getAddrLen()); + continue; + } + + memcpy(addr.getAddr(), result->ai_addr, result->ai_addrlen); + ok = true; + break; + } // of getaddrinfo results iteration + } // of getaddrinfo succeeded + + freeaddrinfo(result0); + return ok; + } + + SGMutex _lock; + SGPthreadCond _wait; + typedef std::map AddressCache; + AddressCache _cache; +}; + +Resolver* Resolver::static_instance = NULL; + +} // of anonymous namespace namespace simgear { @@ -114,17 +268,12 @@ void IPAddress::set ( const char* host, int port ) return; } - struct addrinfo* result = NULL; - int err = getaddrinfo(host, NULL, NULL /* no hints */, &result); - if (err) { - SG_LOG(SG_IO, SG_WARN, "getaddrinfo failed for '" << host << "' : " << gai_strerror(err)); - } else if (result->ai_addrlen != getAddrLen()) { - SG_LOG(SG_IO, SG_ALERT, "mismatch in socket address sizes"); - } else { - memcpy(addr, result->ai_addr, result->ai_addrlen); +// check the cache + IPAddress* cached = Resolver::instance()->lookupSync(host); + if (cached) { + memcpy(addr, cached->getAddr(), cached->getAddrLen()); } - freeaddrinfo(result); addr->sin_port = htons (port); // fix up port after getaddrinfo } @@ -135,6 +284,17 @@ IPAddress::~IPAddress() } } +bool IPAddress::lookupNonblocking(const char* host, IPAddress& addr) +{ + IPAddress* cached = Resolver::instance()->lookup(host); + if (!cached) { + return false; + } + + addr = *cached; + return true; +} + /* Create a string object representing an IP address. This is always a string of the form 'dd.dd.dd.dd' (with variable size numbers). */ @@ -159,6 +319,11 @@ unsigned int IPAddress::getPort() const return ntohs(addr->sin_port); } +void IPAddress::setPort(int port) +{ + addr->sin_port = htons(port); +} + unsigned int IPAddress::getFamily () const { return addr->sin_family; @@ -198,6 +363,11 @@ unsigned int IPAddress::getAddrLen() const struct sockaddr* IPAddress::getAddr() const { + if (addr == NULL) { + addr = (struct sockaddr_in*) malloc(sizeof(struct sockaddr_in)); + memset(addr, 0, sizeof(struct sockaddr_in)); + } + return (struct sockaddr*) addr; } @@ -328,7 +498,7 @@ int Socket::bind ( const char* host, int port ) } } #if defined(WINSOCK) - else if( (result = ::bind(handle,addr->getAddr(), addr->getAddrLen())) < 0 ) { + else if( (result = ::bind(handle,addr.getAddr(), addr.getAddrLen())) < 0 ) { SG_LOG(SG_IO, SG_ALERT, "bind(" << host << ":" << port << ") failed. Errno " << errno << " (" << strerror(errno) << ")"); return result; }