bool TcpSocketEngine::nativeConnect(const HostAddress& address, const uint16_t port) { // setup connection parameters from address/port sockaddr_in sockAddrIPv4; sockaddr_in6 sockAddrIPv6; sockaddr* sockAddrPtr = 0; BT_SOCKLEN_T sockAddrSize = 0; // IPv6 if ( address.GetProtocol() == HostAddress::IPv6Protocol ) { memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6)); sockAddrIPv6.sin6_family = AF_INET6; sockAddrIPv6.sin6_port = htons(port); IPv6Address ip6 = address.GetIPv6Address(); memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6)); sockAddrSize = sizeof(sockAddrIPv6); sockAddrPtr = (sockaddr*)&sockAddrIPv6; } // IPv4 else if ( address.GetProtocol() == HostAddress::IPv4Protocol ) { memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4)); sockAddrIPv4.sin_family = AF_INET; sockAddrIPv4.sin_port = htons(port); sockAddrIPv4.sin_addr.s_addr = htonl(address.GetIPv4Address()); sockAddrSize = sizeof(sockAddrIPv4); sockAddrPtr = (sockaddr*)&sockAddrIPv4; } // unknown (should be unreachable) else BT_ASSERT_X(false, "TcpSocketEngine::nativeConnect() : unknown network protocol"); // attempt connection int connectResult = connect(m_socketDescriptor, sockAddrPtr, sockAddrSize); // if failed, handle error if ( connectResult == -1 ) { // ensure state is set before checking errno m_socketState = TcpSocket::UnconnectedState; // set error type/message depending on errno switch ( errno ) { // <-- potential thread issues later? but can't get error type from connectResult case EISCONN: m_socketState = TcpSocket::ConnectedState; // socket was already connected break; case ECONNREFUSED: case EINVAL: m_socketError = TcpSocket::ConnectionRefusedError; m_errorString = "connection refused"; break; case ETIMEDOUT: m_socketError = TcpSocket::NetworkError; m_errorString = "connection timed out"; break; case EHOSTUNREACH: m_socketError = TcpSocket::NetworkError; m_errorString = "host unreachable"; break; case ENETUNREACH: m_socketError = TcpSocket::NetworkError; m_errorString = "network unreachable"; break; case EADDRINUSE: m_socketError = TcpSocket::SocketResourceError; m_errorString = "address already in use"; break; case EACCES: case EPERM: m_socketError = TcpSocket::SocketAccessError; m_errorString = "permission denied"; break; default: break; } // double check that we're not in 'connected' state; if so, return failure if ( m_socketState != TcpSocket::ConnectedState ) return false; } // otherwise, we should be good // update state & return success m_socketState = TcpSocket::ConnectedState; return true; }
HostInfo HostInfo::Lookup(const string& hostname, const string& port) { HostInfo result; result.SetHostName(hostname); set<HostAddress> uniqueAddresses; #ifdef _WIN32 WindowsSockInit init; #endif HostAddress address; address.SetAddress(hostname); // if hostname is an IP string ('0.0.0.0' or IPv6 format) // do reverse lookup for host domain name // // TODO: might just remove this... not sure if proper 'hostname' from IP string is needed // // so far, haven't been able to successfully fetch a domain name with reverse DNS // getnameinfo() on test sites just returns original IP string. BUT this is likely a rare // case that client code tries to use an IP string and the connection should work fine // anyway. GetHostName() just won't quite show what I was hoping for. :( if ( address.HasIPAddress() ) { const uint16_t portNum = static_cast<uint16_t>( atoi(port.c_str()) ); sockaddr_in sa4; sockaddr_in6 sa6; sockaddr* sa = 0; BT_SOCKLEN_T saSize = 0; // IPv4 if ( address.GetProtocol() == HostAddress::IPv4Protocol ) { sa = (sockaddr*)&sa4; saSize = sizeof(sa4); memset(&sa4, 0, sizeof(sa4)); sa4.sin_family = AF_INET; sa4.sin_addr.s_addr = htonl(address.GetIPv4Address()); sa4.sin_port = htons(portNum); } // IPv6 else if ( address.GetProtocol() == HostAddress::IPv4Protocol ){ sa = (sockaddr*)&sa6; saSize = sizeof(sa6); memset(&sa6, 0, sizeof(sa6)); sa6.sin6_family = AF_INET6; memcpy(sa6.sin6_addr.s6_addr, address.GetIPv6Address().data, sizeof(sa6.sin6_addr.s6_addr)); sa6.sin6_port = htons(portNum); } // unknown (should be unreachable) else BT_ASSERT_X(false, "HostInfo::Lookup: unknown network protocol"); // lookup name for IP char hbuf[NI_MAXHOST]; char serv[NI_MAXSERV]; if ( sa && (getnameinfo(sa, saSize, hbuf, sizeof(hbuf), serv, sizeof(serv), 0) == 0) ) result.SetHostName(string(hbuf)); // if no domain name found, just use the original address's IP string if ( result.HostName().empty() ) result.SetHostName(address.GetIPString()); // store address in HostInfo uniqueAddresses.insert(address); } // otherwise, hostname is a domain name ('www.foo.bar') // do 'normal' lookup else { // setup address lookup 'hints' addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; // allow either IPv4 or IPv6 hints.ai_socktype = SOCK_STREAM; // for TCP hints.ai_protocol = IPPROTO_TCP; // fetch addresses for requested hostname/port addrinfo* res; int status = getaddrinfo(hostname.c_str(), port.c_str(), &hints, &res ); // if everything OK if ( status == 0 ) { // iterate over all IP addresses found addrinfo* p = res; for ( ; p != NULL; p = p->ai_next ) { // IPv4 if ( p->ai_family == AF_INET ) { sockaddr_in* ipv4 = (sockaddr_in*)p->ai_addr; HostAddress a( ntohl(ipv4->sin_addr.s_addr) ); uniqueAddresses.insert(a); } // IPv6 else if ( p->ai_family == AF_INET6 ) { sockaddr_in6* ipv6 = (sockaddr_in6*)p->ai_addr; HostAddress a(ipv6->sin6_addr.s6_addr); uniqueAddresses.insert(a); } } // if we iterated, but no addresses were stored if ( uniqueAddresses.empty() && (p == NULL) ) { result.SetError(HostInfo::UnknownError); result.SetErrorString("HostInfo: unknown address types found"); } } // handle error cases else if ( #ifndef _WIN32 status == EAI_NONAME || status == EAI_FAIL # ifdef EAI_NODATA || status == EAI_NODATA // officially deprecated, but just in case we happen to hit it # endif // EAI_NODATA #else // _WIN32 WSAGetLastError() == WSAHOST_NOT_FOUND || WSAGetLastError() == WSANO_DATA || WSAGetLastError() == WSANO_RECOVERY #endif // _WIN32 ) { result.SetError(HostInfo::HostNotFound); result.SetErrorString("HostInfo: host not found"); } else { result.SetError(HostInfo::UnknownError); result.SetErrorString("HostInfo: unknown error encountered"); } // cleanup freeaddrinfo(res); } // store fetched addresses (converting set -> vector) in result & return result.SetAddresses( vector<HostAddress>(uniqueAddresses.begin(), uniqueAddresses.end()) ); return result; }