bool TcpSocket::ConnectImpl(const HostInfo& hostInfo, const std::string& port, IBamIODevice::OpenMode mode) { // skip if we're already connected if ( m_state == TcpSocket::ConnectedState ) { m_error = TcpSocket::SocketResourceError; m_errorString = "socket already connected"; return false; } // reset socket state m_hostName = hostInfo.HostName(); m_mode = mode; m_state = TcpSocket::UnconnectedState; m_error = TcpSocket::UnknownSocketError; // m_localPort = 0; m_remotePort = 0; // m_localAddress.Clear(); m_remoteAddress.Clear(); m_readBuffer.Clear(); // fetch candidate addresses for requested host vector<HostAddress> addresses = hostInfo.Addresses(); if ( addresses.empty() ) { m_error = TcpSocket::HostNotFoundError; m_errorString = "no IP addresses found for host"; return false; } // convert port string to integer stringstream ss(port); uint16_t portNumber(0); ss >> portNumber; // iterate through adddresses vector<HostAddress>::const_iterator addrIter = addresses.begin(); vector<HostAddress>::const_iterator addrEnd = addresses.end(); for ( ; addrIter != addrEnd; ++addrIter) { const HostAddress& addr = (*addrIter); // try to initialize socket engine with this address if ( !InitializeSocketEngine(addr.GetProtocol()) ) { // failure to initialize is OK here // we'll just try the next available address continue; } // attempt actual connection if ( m_engine->Connect(addr, portNumber) ) { // if connection successful, update our state & return true m_mode = mode; // m_localAddress = m_engine->GetLocalAddress(); // m_localPort = m_engine->GetLocalPort(); m_remoteAddress = m_engine->GetRemoteAddress(); m_remotePort = m_engine->GetRemotePort(); m_cachedSocketDescriptor = m_engine->GetSocketDescriptor(); m_state = TcpSocket::ConnectedState; return true; } } // if we get here, no connection could be made m_error = TcpSocket::HostNotFoundError; m_errorString = "could not connect to any host addresses"; return false; }
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; }