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