sg_socket_udp.cxx
HTTPClient.cxx
HTTPRequest.cxx
+ HostLookup.cxx
+ HostLookup.hxx
)
simgear_component(io io "${SOURCES}" "${HEADERS}")
add_executable(test_sock socktest.cxx)
-target_link_libraries(test_sock sgio sgstructure sgdebug)
+target_link_libraries(test_sock sgio sgstructure sgdebug sgthreads)
add_executable(test_http test_HTTP.cxx)
target_link_libraries(test_http
- sgio sgstructure sgtiming sgmisc sgdebug
+ sgio sgstructure sgtiming sgmisc sgdebug sgthreads
${RT_LIBRARY})
add_test(http ${EXECUTABLE_OUTPUT_PATH}/test_http)
add_executable(httpget httpget.cxx)
target_link_libraries(httpget
- sgio sgstructure sgtiming sgmisc sgdebug
+ sgio sgstructure sgtiming sgmisc sgdebug sgthreads
${RT_LIBRARY})
\ No newline at end of file
#include "version.h"
#else
# if !defined(SIMGEAR_VERSION)
-# define SIMGEAR_VERSION "simgear-development"
+# define SIMGEAR_VERSION development
# endif
#endif
--- /dev/null
+#include "HostLookup.hxx"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <map>
+
+#include <simgear/threads/SGThread.hxx>
+
+using std::string;
+
+namespace simgear
+{
+
+class Resolver : public SGThread
+{
+public:
+ Resolver()
+ {
+ // take lock when resolver starts,
+ // won't release until it's actually running, and waiting on the
+ // condition. Otherwise we get a race when starting.
+ _lock.lock();
+ }
+
+ virtual void run()
+ {
+ while (true) {
+ _cond.wait(_lock); // wait until there's something to lookup
+
+ // find the first un-resolved entry in the cache
+ HostCache::iterator it;
+ for (it = _cache.begin(); it != _cache.end(); ++it) {
+ if (!it->second->resolved()) {
+ break;
+ }
+ }
+
+ if (it == _cache.end()) {
+ continue;
+ }
+
+ string host = it->first;
+ // don't hold any locks while looking up
+ _lock.unlock();
+
+ // start the actual lookup - may take a long period of time!
+ // (eg, one, five or sixty seconds)
+ struct addrinfo hints;
+ bzero(&hints, sizeof(struct addrinfo));
+ hints.ai_family = PF_UNSPEC;
+
+ struct addrinfo *results;
+
+ int result = getaddrinfo(host.c_str(), NULL, &hints, &results);
+ _lock.lock(); // take the lock back before going any further
+
+ if (result == 0) {
+ _cache[host]->resolve(IPAddress(results->ai_addr, results->ai_addrlen));
+ freeaddrinfo(results);
+ } else if (result == EAGAIN) {
+
+ } else {
+ const char* errMsg = gai_strerror(result);
+ // bad lookup, remove from cache? or set as failed?
+ _cache[host]->fail();
+ }
+ } // of thread loop
+
+ _lock.unlock();
+ }
+
+ void addLookup(HostLookup* l)
+ {
+ _lock.lock();
+ _cache[l->host()] = l;
+ _cond.signal();
+ _lock.unlock();
+ }
+
+ HostLookup* lookup(const string& host)
+ {
+ _lock.lock();
+ HostCache::iterator it = _cache.find(host);
+ HostLookup* result = it->second;
+ _lock.unlock();
+ return result;
+ }
+private:
+ SGMutex _lock;
+ SGPthreadCond _cond;
+
+ typedef std::map<string, HostLookup*> HostCache;
+ HostCache _cache;
+};
+
+Resolver* static_resolve = NULL;
+
+HostLookup* HostLookup::lookup(const string& host)
+{
+ if (!static_resolve) {
+ static_resolve = new Resolver;
+ static_resolve->start();
+ }
+
+ HostLookup* l = static_resolve->lookup(host);
+ if (l) {
+ return l;
+ }
+
+ l = new HostLookup(host);
+ SGReferenced::get(l);
+ static_resolve->addLookup(l);
+ return l;
+}
+
+HostLookup::HostLookup(const std::string& h) :
+ _host(h),
+ _resolved(false)
+{
+}
+
+void HostLookup::resolve(const IPAddress& addr)
+{
+ _failed = false;
+ _address = addr;
+ _age.stamp();
+ _resolved = true;
+}
+
+void HostLookup::fail()
+{
+ _failed = true;
+ _resolved = false;
+ _age.stamp();
+}
+
+} // of namespace
--- /dev/null
+#ifndef SG_HOST_LOOKUP_HXX
+#define SG_HOST_LOOKUP_HXX
+
+#include <simgear/io/raw_socket.hxx>
+#include <simgear/timing/timestamp.hxx>
+
+#include <simgear/structure/SGReferenced.hxx>
+
+namespace simgear
+{
+
+class HostLookup : public SGReferenced
+{
+public:
+ static HostLookup* lookup(const std::string& h);
+
+ bool resolved() const
+ { return _resolved; }
+
+ bool failed() const
+ { return _failed; }
+
+ const IPAddress& address() const
+ { return _address; }
+
+ const std::string& host() const
+ { return _host; }
+private:
+ HostLookup(const std::string& h);
+
+ friend class Resolver;
+
+ void resolve(const IPAddress& addr);
+ void fail();
+
+ std::string _host;
+ bool _resolved;
+ bool _failed;
+ IPAddress _address;
+ SGTimeStamp _age;
+};
+
+} // of namespace simgear
+
+#endif // of SG_HOST_LOOKUP_HXX
#include <cstring>
#include <cassert>
#include <cstdio> // for snprintf
+#include <iostream>
#if defined(WINSOCK)
# include <winsock.h>
set ( host, port ) ;
}
+IPAddress::IPAddress ( struct sockaddr* addr, size_t len )
+{
+ memset(&_raw, 0, sizeof(struct sockaddr_in));
+ if ((addr->sa_family != AF_INET) ||
+ (len != sizeof(struct sockaddr_in)))
+ {
+ std::cout << "address size mismatch" << std::endl;
+ return;
+ }
+
+ memcpy(&_raw, addr, len);
+}
+
+void IPAddress::setPort(unsigned int port)
+{
+ _raw.sin_port = htons(port);
+}
void IPAddress::set ( const char* host, int port )
{
- memset(this, 0, sizeof(IPAddress));
+ memset(&_raw, 0, sizeof(struct sockaddr_in));
- sin_family = AF_INET ;
- sin_port = htons (port);
+ _raw.sin_family = AF_INET ;
+ _raw.sin_port = htons (port);
/* Convert a string specifying a host name or one of a few symbolic
** names to a numeric IP address. This usually calls gethostbyname()
*/
if (!host || host[0] == '\0') {
- sin_addr = INADDR_ANY;
+ _raw.sin_addr.s_addr = INADDR_ANY;
return;
}
if (strcmp(host, "<broadcast>") == 0) {
- sin_addr = INADDR_BROADCAST;
+ _raw.sin_addr.s_addr = INADDR_BROADCAST;
return;
}
- sin_addr = inet_addr ( host ) ;
- if (sin_addr != INADDR_NONE) {
+ _raw.sin_addr.s_addr = inet_addr ( host ) ;
+ if (_raw.sin_addr.s_addr != INADDR_NONE) {
return;
}
// finally, try gethostbyname
struct hostent *hp = gethostbyname ( host ) ;
if (!hp) {
SG_LOG(SG_IO, SG_WARN, "gethostbyname failed for " << host);
- sin_addr = INADDR_ANY ;
+ _raw.sin_addr.s_addr = INADDR_ANY ;
return;
}
- memcpy ( (char *) &sin_addr, hp->h_addr, hp->h_length ) ;
+ memcpy ( (char *) &_raw.sin_addr, hp->h_addr, hp->h_length ) ;
}
const char* buf = inet_ntoa ( sin_addr ) ;
#else
static char buf [32];
- long x = ntohl(sin_addr);
+ long x = ntohl(_raw.sin_addr.s_addr);
sprintf(buf, "%d.%d.%d.%d",
(int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
(int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff );
unsigned int IPAddress::getIP () const
{
- return sin_addr;
+ return _raw.sin_addr.s_addr;
}
unsigned int IPAddress::getPort() const
{
- return ntohs(sin_port);
+ return ntohs(_raw.sin_port);
}
unsigned int IPAddress::getFamily () const
{
- return sin_family;
+ return _raw.sin_family;
}
const char* IPAddress::getLocalHost ()
bool IPAddress::getBroadcast () const
{
- return sin_addr == INADDR_BROADCAST;
+ return (_raw.sin_addr.s_addr == INADDR_BROADCAST);
}
}
else
{
- socklen_t addr_len = (socklen_t) sizeof(IPAddress) ;
- return ::accept(handle,(sockaddr*)addr,&addr_len);
+ socklen_t addr_len = addr->getAddressSize();
+ return ::accept(handle,(sockaddr*)addr->getAddress(),&addr_len);
}
}
if ( addr.getBroadcast() ) {
setBroadcast( true );
}
- return ::connect(handle,(const sockaddr*)&addr,sizeof(IPAddress));
+ return ::connect(handle,(const sockaddr*) addr.getAddress(), addr.getAddressSize());
}
+int Socket::connect ( const IPAddress& addr )
+{
+ assert ( handle != -1 ) ;
+ if ( addr.getBroadcast() ) {
+ setBroadcast( true );
+ }
+
+ return ::connect(handle,(const sockaddr*) addr.getAddress(), addr.getAddressSize());
+}
int Socket::send (const void * buffer, int size, int flags)
{
*/
class IPAddress
{
- /* DANGER!!! This MUST match 'struct sockaddr_in' exactly! */
-#if defined(__APPLE__) || defined(__FreeBSD__)
- __uint8_t sin_len;
- __uint8_t sin_family;
- in_port_t sin_port;
- in_addr_t sin_addr;
- char sin_zero[8];
-#else
- short sin_family ;
- unsigned short sin_port ;
- unsigned int sin_addr ;
- char sin_zero [ 8 ] ;
-#endif
-
public:
IPAddress () {}
IPAddress ( const char* host, int port ) ;
-
+ IPAddress ( struct sockaddr* addr, size_t len );
+
void set ( const char* host, int port ) ;
const char* getHost () const ;
unsigned int getPort() const ;
static const char* getLocalHost () ;
bool getBroadcast () const ;
+
+ void setPort(unsigned int port);
+ const struct sockaddr_in* getAddress() const
+ { return &_raw; }
+
+ const size_t getAddressSize() const
+ { return sizeof(struct sockaddr_in); }
+private:
+ struct sockaddr_in _raw;
};
int listen ( int backlog ) ;
int accept ( IPAddress* addr ) ;
int connect ( const char* host, int port ) ;
+ int connect ( const IPAddress& addr ) ;
int send ( const void * buffer, int size, int flags = 0 ) ;
int sendto ( const void * buffer, int size, int flags, const IPAddress* to ) ;
int recv ( void * buffer, int size, int flags = 0 ) ;
#include <cstring>
#include <simgear/debug/logstream.hxx>
+#include <simgear/io/HostLookup.hxx>
namespace simgear {
}
int
-NetChannel::connect ( const char* host, int port )
+NetChannel::connect ( const char* host, int p )
{
- int result = Socket::connect ( host, port ) ;
- if (result == 0) {
- connected = true ;
- //this->handleConnect();
- return 0;
- } else if (isNonBlockingError ()) {
- return 0;
- } else {
- // some other error condition
- this->handleError (result);
- close();
- return -1;
- }
+ host_lookup = HostLookup::lookup(host);
+ port = p;
+ return 0;
}
int
this->handleWrite();
}
+void
+NetChannel::doConnect()
+{
+ IPAddress addr( host_lookup->address() );
+ addr.setPort(port);
+ int result = Socket::connect ( addr ) ;
+ host_lookup = NULL;
+
+ if (result == 0) {
+ connected = true ;
+ } else if (isNonBlockingError ()) {
+
+ } else {
+ // some other error condition
+ handleError (result);
+ close();
+ }
+}
+
bool
NetChannel::poll (unsigned int timeout)
{
else if ( ! ch -> closed )
{
nopen++ ;
+ if (ch->host_lookup) {
+ if (ch->host_lookup->resolved()) {
+ ch->doConnect();
+ } else if (ch->host_lookup->failed()) {
+ ch->handleError (-1);
+ ch->close();
+ }
+ continue;
+ }
+
if (ch -> readable()) {
assert(nreads<MAX_SOCKETS);
reads[nreads++] = ch ;
#define SG_NET_CHANNEL_H
#include <simgear/io/raw_socket.hxx>
+#include <simgear/structure/SGSharedPtr.hxx>
+
namespace simgear
{
+class HostLookup;
+
class NetChannel : public Socket
{
bool closed, connected, accepting, write_blocked, should_delete ;
NetChannel* next_channel ;
+ SGSharedPtr<HostLookup> host_lookup ;
+ int port;
friend bool netPoll (unsigned int timeout);
+ void doConnect();
public:
NetChannel () ;