X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fio%2Fraw_socket.cxx;h=bb08aae76a26b1e7f240a89cce4a903473e58827;hb=09b0dd2b2d7d934c1d4059cb2cbd3b4fcbb7872f;hp=d972c14531e4f5b292600cf0519bd901818ee0d8;hpb=c7152af1917c1388fe3a944e7481a271cce797ba;p=simgear.git diff --git a/simgear/io/raw_socket.cxx b/simgear/io/raw_socket.cxx index d972c145..bb08aae7 100644 --- a/simgear/io/raw_socket.cxx +++ b/simgear/io/raw_socket.cxx @@ -1,20 +1,20 @@ /* simgear::Socket, adapted from PLIB Socket by James Turner Copyright (C) 2010 James Turner - + PLIB - A Suite of Portable Game Libraries Copyright (C) 1998,2002 Steve Baker - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This library 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 Library General Public License for more details. - + You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA @@ -47,7 +47,7 @@ # include # include # include -# include +# include # include # include # include @@ -57,12 +57,175 @@ #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->shutdown(); + static_instance->join(); + } + + Resolver() : + _done(false) + { + } + + void shutdown() + { + _lock.lock(); + _done = true; + _wait.signal(); + _lock.unlock(); + } + + 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() + { + _lock.lock(); + while (!_done) { + 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 + _wait.wait(_lock); + } // of thread run loop + _lock.unlock(); + } +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; + SGWaitCondition _wait; + + typedef std::map AddressCache; + AddressCache _cache; + bool _done; +}; + +Resolver* Resolver::static_instance = NULL; + +} // of anonymous namespace namespace simgear { - + IPAddress::IPAddress ( const char* host, int port ) { set ( host, port ) ; @@ -109,39 +272,18 @@ void IPAddress::set ( const char* host, int port ) addr->sin_addr.s_addr = INADDR_ANY; return; } - + if (strcmp(host, "") == 0) { addr->sin_addr.s_addr = INADDR_BROADCAST; return; } - - struct addrinfo hints; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_INET; - - 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)); - } 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 != getAddrLen()) { - SG_LOG(SG_IO, SG_ALERT, "mismatch in socket address sizes: got " << - result->ai_addrlen << ", expected " << getAddrLen()); - continue; - } - - memcpy(addr, result->ai_addr, result->ai_addrlen); - break; - } // of getaddrinfo results iteration - } // of getaddrinfo succeeded - - freeaddrinfo(result0); + +// check the cache + IPAddress* cached = Resolver::instance()->lookupSync(host); + if (cached) { + memcpy(addr, cached->getAddr(), cached->getAddrLen()); + } + addr->sin_port = htons (port); // fix up port after getaddrinfo } @@ -152,12 +294,27 @@ 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). */ const char* IPAddress::getHost () const { + if (!addr) { + return NULL; + } + static char buf [32]; long x = ntohl(addr->sin_addr.s_addr); sprintf(buf, "%d.%d.%d.%d", @@ -166,19 +323,40 @@ const char* IPAddress::getHost () const return buf; } -unsigned int IPAddress::getIP () const -{ - return addr->sin_addr.s_addr; +unsigned int IPAddress::getIP () const +{ + if (!addr) { + return 0; + } + + return addr->sin_addr.s_addr; } unsigned int IPAddress::getPort() const { + if (!addr) { + return 0; + } + return ntohs(addr->sin_port); } -unsigned int IPAddress::getFamily () const -{ - return addr->sin_family; +void IPAddress::setPort(int port) +{ + if (!addr) { + return; + } + + addr->sin_port = htons(port); +} + +unsigned int IPAddress::getFamily () const +{ + if (!addr) { + return 0; + } + + return addr->sin_family; } const char* IPAddress::getLocalHost () @@ -205,7 +383,11 @@ const char* IPAddress::getLocalHost () bool IPAddress::getBroadcast () const { - return (addr->sin_addr.s_addr == INADDR_BROADCAST); + if (!addr) { + return false; + } + + return (addr->sin_addr.s_addr == INADDR_BROADCAST); } unsigned int IPAddress::getAddrLen() const @@ -215,9 +397,19 @@ 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; } +bool IPAddress::isValid() const +{ + return (addr != NULL); +} + Socket::Socket () { handle = -1 ; @@ -299,7 +491,7 @@ void Socket::setBroadcast ( bool broadcast ) } else { result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, NULL, 0 ); } - + if ( result < 0 ) { throw sg_exception("Socket::setBroadcast failed"); } @@ -319,7 +511,7 @@ int Socket::bind ( const char* host, int port ) } #endif - // 224.0.0.0 - 239.255.255.255 are multicast + // 224.0.0.0 - 239.255.255.255 are multicast // Usage of 239.x.x.x is recommended for local scope // Reference: http://tools.ietf.org/html/rfc5771 if( ntohl(addr.getIP()) >= 0xe0000000 && ntohl(addr.getIP()) <= 0xefffffff ) { @@ -329,7 +521,7 @@ int Socket::bind ( const char* host, int port ) a.sin_addr.S_un.S_addr = INADDR_ANY; a.sin_family = AF_INET; a.sin_port = htons(port); - + if( (result = ::bind(handle,(const sockaddr*)&a,sizeof(a))) < 0 ) { SG_LOG(SG_IO, SG_ALERT, "bind(any:" << port << ") failed. Errno " << errno << " (" << strerror(errno) << ")"); return result; @@ -479,7 +671,7 @@ int Socket::select ( Socket** reads, Socket** writes, int timeout ) { fd_set r,w; int retval; - + FD_ZERO (&r); FD_ZERO (&w); @@ -517,7 +709,7 @@ int Socket::select ( Socket** reads, Socket** writes, int timeout ) // It bothers me that select()'s first argument does not appear to // work as advertised... [it hangs like this if called with // anything less than FD_SETSIZE, which seems wasteful?] - + // Note: we ignore the 'exception' fd_set - I have never had a // need to use it. The name is somewhat misleading - the only // thing I have ever seen it used for is to detect urgent data -