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