]> git.donarmstrong.com Git - bamtools.git/blob - src/api/internal/io/HostInfo_p.cpp
Updated file headers (filename, license, description, etc)
[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: 10 November 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     set<HostAddress> uniqueAddresses;
85
86 #ifdef _WIN32
87     WindowsSockInit init;
88 #endif
89
90     HostAddress address;
91     address.SetAddress(hostname);
92
93     // if hostname is an IP string ('0.0.0.0' or IPv6 format)
94     // do reverse lookup for host domain name
95     //
96     // TODO: might just remove this... not sure if proper 'hostname' from IP string is needed
97     //
98     //       so far, haven't been able to successfully fetch a domain name with reverse DNS
99     //       getnameinfo() on test sites just returns original IP string. BUT this is likely a rare
100     //       case that client code tries to use an IP string and the connection should work fine
101     //       anyway. GetHostName() just won't quite show what I was hoping for. :(
102     if ( address.HasIPAddress() ) {
103
104         const uint16_t portNum = static_cast<uint16_t>( atoi(port.c_str()) );
105
106         sockaddr_in  sa4;
107         sockaddr_in6 sa6;
108         sockaddr* sa = 0;
109         BT_SOCKLEN_T saSize = 0;
110
111         // IPv4
112         if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
113             sa = (sockaddr*)&sa4;
114             saSize = sizeof(sa4);
115             memset(&sa4, 0, sizeof(sa4));
116             sa4.sin_family = AF_INET;
117             sa4.sin_addr.s_addr = htonl(address.GetIPv4Address());
118             sa4.sin_port = htons(portNum);
119         }
120
121         // IPv6
122         else if ( address.GetProtocol() == HostAddress::IPv4Protocol ){
123             sa = (sockaddr*)&sa6;
124             saSize = sizeof(sa6);
125             memset(&sa6, 0, sizeof(sa6));
126             sa6.sin6_family = AF_INET6;
127             memcpy(sa6.sin6_addr.s6_addr, address.GetIPv6Address().data, sizeof(sa6.sin6_addr.s6_addr));
128             sa6.sin6_port = htons(portNum);
129         }
130
131         // unknown (should be unreachable)
132         else BT_ASSERT_X(false, "HostInfo::Lookup: unknown network protocol");
133
134         // lookup name for IP
135         char hbuf[NI_MAXHOST];
136         char serv[NI_MAXSERV];
137         if ( sa && (getnameinfo(sa, saSize, hbuf, sizeof(hbuf), serv, sizeof(serv), 0) == 0) )
138             result.SetHostName(string(hbuf));
139
140         // if no domain name found, just use the original address's IP string
141         if ( result.HostName().empty() )
142             result.SetHostName(address.GetIPString());
143
144         // store address in HostInfo
145         uniqueAddresses.insert(address);
146     }
147
148     // otherwise, hostname is a domain name ('www.foo.bar')
149     // do 'normal' lookup
150     else {
151
152         // setup address lookup 'hints'
153         addrinfo hints;
154         memset(&hints, 0, sizeof(hints));
155         hints.ai_family   = AF_UNSPEC;   // allow either IPv4 or IPv6
156         hints.ai_socktype = SOCK_STREAM; // for TCP
157         hints.ai_protocol = IPPROTO_TCP;
158
159         // fetch addresses for requested hostname/port
160         addrinfo* res;
161         int status = getaddrinfo(hostname.c_str(), port.c_str(), &hints, &res );
162
163         // if everything OK
164         if ( status == 0 ) {
165
166             // iterate over all IP addresses found
167             addrinfo* p = res;
168             for ( ; p != NULL; p = p->ai_next ) {
169
170                 // IPv4
171                 if ( p->ai_family == AF_INET ) {
172                     sockaddr_in* ipv4 = (sockaddr_in*)p->ai_addr;
173                     HostAddress a( ntohl(ipv4->sin_addr.s_addr) );
174                     uniqueAddresses.insert(a);
175                 }
176
177                 // IPv6
178                 else if ( p->ai_family == AF_INET6 ) {
179                     sockaddr_in6* ipv6 = (sockaddr_in6*)p->ai_addr;
180                     HostAddress a(ipv6->sin6_addr.s6_addr);
181                     uniqueAddresses.insert(a);
182                 }
183             }
184
185             // if we iterated, but no addresses were stored
186             if ( uniqueAddresses.empty() && (p == NULL) ) {
187                 result.SetError(HostInfo::UnknownError);
188                 result.SetErrorString("HostInfo: unknown address types found");
189             }
190         }
191
192         // handle error cases
193         else if (
194 #ifndef _WIN32
195                      status == EAI_NONAME
196                   || status == EAI_FAIL
197 #  ifdef EAI_NODATA
198                   || status == EAI_NODATA  // officially deprecated, but just in case we happen to hit it
199 #  endif // EAI_NODATA
200
201 #else  // _WIN32
202                      WSAGetLastError() == WSAHOST_NOT_FOUND
203                   || WSAGetLastError() == WSANO_DATA
204                   || WSAGetLastError() == WSANO_RECOVERY
205 #endif // _WIN32
206                 )
207         {
208             result.SetError(HostInfo::HostNotFound);
209             result.SetErrorString("HostInfo: host not found");
210         }
211         else {
212             result.SetError(HostInfo::UnknownError);
213             result.SetErrorString("HostInfo: unknown error encountered");
214         }
215
216         // cleanup
217         freeaddrinfo(res);
218     }
219
220     // store fetched addresses (converting set -> vector) in result & return
221     result.SetAddresses( vector<HostAddress>(uniqueAddresses.begin(), uniqueAddresses.end()) );
222     return result;
223 }