int DNS::connect( const std::string& host, unsigned short port, const LogSink& logInstance ) { int fd = getSocket(); if( fd < 0 ) return fd; struct hostent* h; if( ( h = gethostbyname( host.c_str() ) ) == 0 ) { cleanup(); return -ConnDnsError; } struct sockaddr_in target; target.sin_family = AF_INET; target.sin_port = htons( port ); if( h->h_length != sizeof( struct in_addr ) ) { cleanup(); return -ConnDnsError; } else { memcpy( &target.sin_addr, h->h_addr, sizeof( struct in_addr ) ); } #ifndef _WIN32_WCE std::ostringstream oss; #endif memset( target.sin_zero, '\0', 8 ); if( ::connect( fd, (struct sockaddr *)&target, sizeof( struct sockaddr ) ) == 0 ) { #ifndef _WIN32_WCE oss << "connecting to " << host.c_str() << " (" << inet_ntoa( target.sin_addr ) << ":" << port << ")"; logInstance.dbg( LogAreaClassDns, oss.str() ); #endif return fd; } #ifndef _WIN32_WCE oss << "connection to " << host.c_str() << " (" << inet_ntoa( target.sin_addr ) << ":" << port << ") failed"; logInstance.dbg( LogAreaClassDns, oss.str() ); #endif closeSocket( fd ); return -ConnConnectionRefused; }
int DNS::connect( const std::string& host, int port, const LogSink& logInstance ) { struct addrinfo hints, *servinfo, *p; int rv = 0; int fd = 0; memset( &hints, 0, sizeof( hints ) ); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if( ( rv = getaddrinfo( host.c_str(), util::int2string( port ).c_str(), &hints, &servinfo ) ) != 0 ) { logInstance.dbg( LogAreaClassDns, "getaddrinfo() failed for " + host + "." ); return -ConnDnsError; } for( p = servinfo; p != 0; p = p->ai_next ) { if( ( fd = getSocket( p->ai_family, p->ai_socktype, p->ai_protocol, logInstance ) ) == -1 ) { continue; } if( ::connect( fd, p->ai_addr, p->ai_addrlen ) == -1 ) { closeSocket( fd, logInstance ); continue; } break; } if( p == 0 ) { freeaddrinfo( servinfo ); std::string message = "Connection to " + host + ":" + util::int2string( port ) + " failed. " #if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) "WSAGetLastError: " + util::int2string( ::WSAGetLastError() ); #else "errno: " + util::int2string( errno ) + ": " + strerror( errno ); #endif logInstance.dbg( LogAreaClassDns, message ); return -ConnConnectionRefused; } freeaddrinfo( servinfo ); return fd; }
int DNS::getSocket( int af, int socktype, int proto, const LogSink& logInstance ) { #if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) SOCKET fd; #else int fd; #endif if( ( fd = socket( af, socktype, proto ) ) == INVALID_SOCKET ) { std::string message = "getSocket( " + util::int2string( af ) + ", " + util::int2string( socktype ) + ", " + util::int2string( proto ) + " ) failed. " #if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) "WSAGetLastError: " + util::int2string( ::WSAGetLastError() ); #else "errno: " + util::int2string( errno ) + ": " + strerror( errno ); #endif logInstance.dbg( LogAreaClassDns, message ); cleanup( logInstance ); return -ConnConnectionRefused; } #ifdef HAVE_SETSOCKOPT int timeout = 5000; int reuseaddr = 1; setsockopt( fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof( timeout ) ); setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseaddr, sizeof( reuseaddr ) ); #endif return (int)fd; }
int DNS::connect( struct addrinfo* res, const LogSink& logInstance ) { if( !res ) return -1; int fd = getSocket( res->ai_family, res->ai_socktype, res->ai_protocol, logInstance ); if( fd < 0 ) return fd; if( ::connect( fd, res->ai_addr, res->ai_addrlen ) == 0 ) { char ip[NI_MAXHOST]; char port[NI_MAXSERV]; if( getnameinfo( res->ai_addr, res->ai_addrlen, ip, sizeof( ip ), port, sizeof( port ), NI_NUMERICHOST | NI_NUMERICSERV ) ) { //FIXME do we need to handle this? How? Can it actually happen at all? // printf( "could not get numeric hostname"); } if( res->ai_canonname ) logInstance.dbg( LogAreaClassDns, std::string( "Connecting to " ).append( res->ai_canonname ).append( " (" ).append( ip ).append( "), port " ).append( port ) ); else logInstance.dbg( LogAreaClassDns, std::string( "Connecting to " ).append( ip ).append( ":" ).append( port ) ); return fd; } std::string message = "connect() failed. " #if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) "WSAGetLastError: " + util::int2string( ::WSAGetLastError() ); #else "errno: " + util::int2string( errno ) + ": " + strerror( errno ); #endif logInstance.dbg( LogAreaClassDns, message ); closeSocket( fd, logInstance ); return -ConnConnectionRefused; }
void DNS::cleanup( const LogSink& logInstance ) { #if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) if( WSACleanup() != 0 ) { logInstance.dbg( LogAreaClassDns, "WSACleanup() failed. WSAGetLastError: " + util::int2string( ::WSAGetLastError() ) ); } #else (void)logInstance; #endif }
int DNS::getSocket( const LogSink& logInstance ) { #if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) WSADATA wsaData; if( WSAStartup( MAKEWORD( 1, 1 ), &wsaData ) != 0 ) { logInstance.dbg( LogAreaClassDns, "WSAStartup() failed. WSAGetLastError: " + util::int2string( ::WSAGetLastError() ) ); return -ConnDnsError; } #endif int protocol = IPPROTO_TCP; #if !defined( __APPLE__ ) // Sandboxing on Apple doesn't like you to use getprotobyname struct protoent* prot; if( ( prot = getprotobyname( "tcp" ) ) != 0 ) { protocol = prot->p_proto; } else { std::string message = "getprotobyname( \"tcp\" ) failed. " #if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) "WSAGetLastError: " + util::int2string( ::WSAGetLastError() ) #else "errno: " + util::int2string( errno ) + ": " + strerror( errno ); #endif + ". Falling back to IPPROTO_TCP: " + util::int2string( IPPROTO_TCP ); logInstance.dbg( LogAreaClassDns, message ); // Do not return an error. We'll fall back to IPPROTO_TCP. } #endif // !defined( __APPLE__ ) return getSocket( PF_INET, SOCK_STREAM, protocol, logInstance ); }
void DNS::closeSocket( int fd, const LogSink& logInstance ) { #if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) int result = closesocket( fd ); #else int result = close( fd ); #endif if( result != 0 ) { std::string message = "closeSocket() failed. " #if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) "WSAGetLastError: " + util::int2string( ::WSAGetLastError() ); #else "errno: " + util::int2string( errno ) + ": " + strerror( errno ); #endif logInstance.dbg( LogAreaClassDns, message ); } }
void DNS::resolve( struct addrinfo** res, const std::string& service, const std::string& proto, const std::string& domain, const LogSink& logInstance ) { logInstance.dbg( LogAreaClassDns, "Resolving: _" + service + "._" + proto + "." + domain ); struct addrinfo hints; if( proto == "tcp" ) hints.ai_socktype = SOCK_STREAM; else if( proto == "udp" ) hints.ai_socktype = SOCK_DGRAM; else { logInstance.err( LogAreaClassDns, "Unknown/Invalid protocol: " + proto ); } memset( &hints, '\0', sizeof( hints ) ); hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME; hints.ai_socktype = SOCK_STREAM; int e = getaddrinfo( domain.c_str(), service.c_str(), &hints, res ); if( e ) logInstance.err( LogAreaClassDns, "getaddrinfo() failed" ); }
int DNS::connect( struct addrinfo* res, const LogSink& logInstance ) { if( !res ) return -1; int fd = getSocket( res->ai_family, res->ai_socktype, res->ai_protocol ); if( fd < 0 ) return fd; if( ::connect( fd, res->ai_addr, res->ai_addrlen ) == 0 ) { char ip[NI_MAXHOST]; char port[NI_MAXSERV]; if( getnameinfo( res->ai_addr, sizeof( sockaddr ), ip, sizeof( ip ), port, sizeof( port ), NI_NUMERICHOST | NI_NUMERICSERV ) ) { //FIXME do we need to handle this? How? Can it actually happen at all? // printf( "could not get numeric hostname"); } std::ostringstream oss; oss << "Connecting to "; if( res->ai_canonname ) { oss << res->ai_canonname; oss << " (" << ip << "), port " << port; } else { oss << ip << ":" << port; } logInstance.dbg( LogAreaClassDns, oss.str() ); return fd; } closeSocket( fd ); return -ConnConnectionRefused; }