]> git.donarmstrong.com Git - bamtools.git/blob - src/api/internal/io/HostInfo_p.cpp
2bb0187a799c4891af39c75382d1d65e509ebc4b
[bamtools.git] / src / api / internal / io / HostInfo_p.cpp
1 #include "api/internal/io/HostInfo_p.h"
2 using namespace BamTools;
3 using namespace BamTools::Internal;
4
5 // platorm-specifics
6 #ifdef _WIN32
7 #  include "api/internal/io/NetWin_p.h"
8 #else
9 #  include "api/internal/io/NetUnix_p.h"
10 #endif
11
12 // standard C++ includes
13 #include <cstdlib>
14 #include <cstring>
15 #include <set>
16 using namespace std;
17
18 // -------------------------
19 // HostInfo basics
20 // -------------------------
21
22 HostInfo::HostInfo(void)
23     : m_error(HostInfo::NoError)
24 { }
25
26 HostInfo::HostInfo(const HostInfo& other)
27     : m_hostName(other.m_hostName)
28     , m_addresses(other.m_addresses)
29     , m_error(other.m_error)
30     , m_errorString(other.m_errorString)
31 { }
32
33 HostInfo::~HostInfo(void) { }
34
35 vector<HostAddress> HostInfo::Addresses(void) const {
36     return m_addresses;
37 }
38
39 HostInfo::ErrorType HostInfo::GetError(void) const {
40     return m_error;
41 }
42
43 string HostInfo::GetErrorString(void) const {
44     return m_errorString;
45 }
46
47 string HostInfo::HostName(void) const {
48     return m_hostName;
49 }
50
51 void HostInfo::SetAddresses(const std::vector<HostAddress>& addresses) {
52     m_addresses = addresses;
53 }
54
55 void HostInfo::SetError(const HostInfo::ErrorType error) {
56     m_error = error;
57 }
58
59 void HostInfo::SetErrorString(const std::string& errorString) {
60     m_errorString = errorString;
61 }
62
63 void HostInfo::SetHostName(const string& name) {
64     m_hostName = name;
65 }
66
67 // ------------------------------
68 // HostInfo::Lookup(host, port)
69 // ------------------------------
70
71 HostInfo HostInfo::Lookup(const string& hostname, const string& port) {
72
73     HostInfo result;
74     set<HostAddress> uniqueAddresses;
75
76 #ifdef _WIN32
77     WindowsSockInit init;
78 #endif
79
80     HostAddress address;
81     address.SetAddress(hostname);
82
83     // if hostname is an IP string ('0.0.0.0' or IPv6 format)
84     // do reverse lookup for host domain name
85     //
86     // TODO: might just remove this... not sure if proper 'hostname' from IP string is needed
87     //
88     //       so far, haven't been able to successfully fetch a domain name with reverse DNS
89     //       getnameinfo() on test sites just returns original IP string. BUT this is likely a rare
90     //       case that client code tries to use an IP string and the connection should work fine
91     //       anyway. GetHostName() just won't quite show what I was hoping for. :(
92     if ( address.HasIPAddress() ) {
93
94         const uint16_t portNum = static_cast<uint16_t>( atoi(port.c_str()) );
95
96         sockaddr_in  sa4;
97         sockaddr_in6 sa6;
98         sockaddr* sa = 0;
99         BT_SOCKLEN_T saSize = 0;
100
101         // IPv4
102         if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
103             sa = (sockaddr*)&sa4;
104             saSize = sizeof(sa4);
105             memset(&sa4, 0, sizeof(sa4));
106             sa4.sin_family = AF_INET;
107             sa4.sin_addr.s_addr = htonl(address.GetIPv4Address());
108             sa4.sin_port = htons(portNum);
109         }
110
111         // IPv6
112         else if ( address.GetProtocol() == HostAddress::IPv4Protocol ){
113             sa = (sockaddr*)&sa6;
114             saSize = sizeof(sa6);
115             memset(&sa6, 0, sizeof(sa6));
116             sa6.sin6_family = AF_INET6;
117             memcpy(sa6.sin6_addr.s6_addr, address.GetIPv6Address().data, sizeof(sa6.sin6_addr.s6_addr));
118             sa6.sin6_port = htons(portNum);
119         }
120
121         // unknown (should be unreachable)
122         else BT_ASSERT_X(false, "HostInfo::Lookup: unknown network protocol");
123
124         // lookup name for IP
125         char hbuf[NI_MAXHOST];
126         char serv[NI_MAXSERV];
127         if ( sa && (getnameinfo(sa, saSize, hbuf, sizeof(hbuf), serv, sizeof(serv), 0) == 0) )
128             result.SetHostName(string(hbuf));
129
130         // if no domain name found, just use the original address's IP string
131         if ( result.HostName().empty() )
132             result.SetHostName(address.GetIPString());
133
134         // store address in HostInfo
135         uniqueAddresses.insert(address);
136     }
137
138     // otherwise, hostname is a domain name ('www.foo.bar')
139     // do 'normal' lookup
140     else {
141
142         // setup address lookup 'hints'
143         addrinfo hints;
144         memset(&hints, 0, sizeof(hints));
145         hints.ai_family   = AF_UNSPEC;   // allow either IPv4 or IPv6
146         hints.ai_socktype = SOCK_STREAM; // for TCP
147         hints.ai_protocol = IPPROTO_TCP;
148
149         // fetch addresses for requested hostname/port
150         addrinfo* res;
151         int status = getaddrinfo(hostname.c_str(), port.c_str(), &hints, &res );
152
153         // if everything OK
154         if ( status == 0 ) {
155
156             // iterate over all IP addresses found
157             addrinfo* p = res;
158             for ( ; p != NULL; p = p->ai_next ) {
159
160                 // IPv4
161                 if ( p->ai_family == AF_INET ) {
162                     sockaddr_in* ipv4 = (sockaddr_in*)p->ai_addr;
163                     HostAddress a( ntohl(ipv4->sin_addr.s_addr) );
164                     uniqueAddresses.insert(a);
165                 }
166
167                 // IPv6
168                 else if ( p->ai_family == AF_INET6 ) {
169                     sockaddr_in6* ipv6 = (sockaddr_in6*)p->ai_addr;
170                     HostAddress a(ipv6->sin6_addr.s6_addr);
171                     uniqueAddresses.insert(a);
172                 }
173             }
174
175             // if we iterated, but no addresses were stored
176             if ( uniqueAddresses.empty() && (p == NULL) ) {
177                 result.SetError(HostInfo::UnknownError);
178                 result.SetErrorString("HostInfo: unknown address types found");
179             }
180         }
181
182         // handle error cases
183         else if (
184 #ifndef _WIN32
185                      status == EAI_NONAME
186                   || status == EAI_FAIL
187 #  ifdef EAI_NODATA
188                   || status == EAI_NODATA  // officially deprecated, but just in case we run into it
189 #  endif // EAI_NODATA
190
191 #else  // _WIN32
192                      WSAGetLastError() == WSAHOST_NOT_FOUND
193                   || WSAGetLastError() == WSANO_DATA
194                   || WSAGetLastError() == WSANO_RECOVERY
195 #endif // _WIN32
196                 )
197         {
198             result.SetError(HostInfo::HostNotFound);
199             result.SetErrorString("HostInfo: host not found");
200         }
201         else {
202             result.SetError(HostInfo::UnknownError);
203             result.SetErrorString("HostInfo: unknown error encountered");
204         }
205
206         // cleanup
207         freeaddrinfo(res);
208     }
209
210     // store fetched addresses (converting set -> vector) in result & return
211     result.SetAddresses( vector<HostAddress>(uniqueAddresses.begin(), uniqueAddresses.end()) );
212     return result;
213 }