]> git.donarmstrong.com Git - bamtools.git/blob - src/api/internal/io/HostAddress_p.cpp
Fixed: premature EOF issues & updated Windows implementation
[bamtools.git] / src / api / internal / io / HostAddress_p.cpp
1 // ***************************************************************************
2 // HostAddress_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 a generic IP address container
8 // ***************************************************************************
9
10 #include "api/internal/io/HostAddress_p.h"
11 using namespace BamTools;
12 using namespace BamTools::Internal;
13
14 #include <cctype>
15 #include <cstdlib>
16 #include <sstream>
17 #include <vector>
18 using namespace std;
19
20 // ------------------------
21 // static utility methods
22 // ------------------------
23
24 namespace BamTools {
25 namespace Internal {
26
27 // split a string into fields, on delimiter character
28 static inline
29 vector<string> Split(const string& source, char delim) {
30     stringstream ss(source);
31     string field;
32     vector<string> fields;
33     while ( getline(ss, field, delim) )
34         fields.push_back(field);
35     return fields;
36 }
37
38 // return number of occurrences of @pattern in @source
39 static inline
40 uint8_t CountHits(const string& source, const string& pattern) {
41
42     uint8_t count(0);
43     size_t found = source.find(pattern);
44     while ( found != string::npos ) {
45         ++count;
46         found = source.find(pattern, found+1);
47     }
48     return count;
49 }
50
51 static
52 bool ParseIp4(const string& address, uint32_t& maybeIp4 ) {
53
54     // split IP address into string fields
55     vector<string> addressFields = Split(address, '.');
56     if ( addressFields.size() != 4 )
57         return false;
58
59     // convert each field to integer value
60     uint32_t ipv4(0);
61     for ( uint8_t i = 0; i < 4; ++i ) {
62
63         const string& field = addressFields.at(i);
64         const size_t fieldSize = field.size();
65         for ( size_t j = 0; j < fieldSize; ++j ) {
66             if ( !isdigit(field[j]) )
67                 return false;
68         }
69
70         int value = atoi( addressFields.at(i).c_str() );
71         if ( value < 0 || value > 255 )
72             return false;
73
74         // append byte value
75         ipv4 <<= 8;
76         ipv4 += value;
77     }
78
79     // store 32-bit IP address & return success
80     maybeIp4 = ipv4;
81     return true;
82 }
83
84 static
85 bool ParseIp6(const string& address, uint8_t* maybeIp6 ) {
86
87     string tmp = address;
88
89     // look for '%' char (if found, lop off that part of address)
90     // we're going to ignore any link-local zone index, for now at least
91     const size_t percentFound = tmp.rfind('%');
92     if ( percentFound != string::npos )
93         tmp = tmp.substr(0, percentFound);
94
95     // split IP address into string fields
96     vector<string> fields = Split(tmp, ':');
97     const uint8_t numFields = fields.size();
98     if ( numFields < 3 || numFields > 8 )
99         return false;
100
101     // get number of '::' separators
102     const uint8_t numColonColons = CountHits(tmp, "::");
103     if ( numFields == 8 && numColonColons > 1 )
104         return false;
105
106     // check valid IPv6 'compression'
107     // must be valid 'pure' IPv6 or mixed IPv4/6 notation
108     const size_t dotFound = tmp.find('.');
109     const bool isMixed = ( dotFound != string::npos );
110     if ( numColonColons != 1 && (numFields < (isMixed ? 7 : 8)) )
111         return false;
112
113     // iterate over provided fields
114     size_t index = 16;
115     size_t fillCount = 9 - numFields;
116     for ( int8_t i = numFields - 1; i >= 0; --i ) {
117         if ( index == 0 )
118             return false;
119         const string& field = fields.at(i);
120
121         // if field empty
122         if ( field.empty() ) {
123
124             // if last field empty
125             if ( i == numFields - 1 ) {
126                 const string& previousField = fields.at(i-1);
127                 if ( previousField.empty() )
128                     return false;
129                 maybeIp6[--index] = 0;
130                 maybeIp6[--index] = 0;
131             }
132
133             // if first field empty
134             else if ( i == 0 ) {
135                 // make sure ':' isn't first character
136                 const string& nextField = fields.at(i+1);
137                 if ( nextField.empty() ) return false;
138                 maybeIp6[--index] = 0;
139                 maybeIp6[--index] = 0;
140             }
141
142             // fill in 'compressed' 0s
143             else {
144                 for ( uint8_t j = 0; j < fillCount; ++j ) {
145                     if ( index == 0 ) return false;
146                     maybeIp6[--index] = 0;
147                     maybeIp6[--index] = 0;
148                 }
149             }
150         }
151
152         // field has data
153         else {
154             uint32_t value = static_cast<uint32_t>( strtoul(field.c_str(), 0, 16) );
155
156             if ( value <= 0xffff ) {
157                 maybeIp6[--index] =  value       & 0xff;
158                 maybeIp6[--index] = (value >> 8) & 0xff;
159             }
160
161             // possible mixed IPv4/6 notation
162             else {
163
164                 // mixed field must be last
165                 if ( i != numFields - 1 )
166                     return false;
167
168                 // parse the IPv4 section
169                 uint32_t maybeIp4;
170                 if ( !ParseIp4(field, maybeIp4) )
171                     return false;
172
173                 // store IPv4 fields in IPv6 container
174                 maybeIp6[--index] =  maybeIp4        & 0xff;
175                 maybeIp6[--index] = (maybeIp4 >> 8)  & 0xff;
176                 maybeIp6[--index] = (maybeIp4 >> 16) & 0xff;
177                 maybeIp6[--index] = (maybeIp4 >> 24) & 0xff;
178                 --fillCount;
179             }
180         }
181     }
182
183     // should have parsed OK, return success
184     return true;
185 }
186
187 } // namespace Internal
188 } // namespace BamTools
189
190 // ----------------------------
191 // HostAddress implementation
192 // ----------------------------
193
194 HostAddress::HostAddress(void)
195     : m_protocol(HostAddress::UnknownNetworkProtocol)
196     , m_ip4Address(0)
197     , m_hasIpAddress(true)
198 { }
199
200 HostAddress::HostAddress(const uint32_t ip4Address)
201     : m_protocol(HostAddress::UnknownNetworkProtocol)
202     , m_ip4Address(0)
203     , m_hasIpAddress(true)
204 {
205     SetAddress(ip4Address);
206 }
207
208 HostAddress::HostAddress(const uint8_t* ip6Address)
209     : m_protocol(HostAddress::UnknownNetworkProtocol)
210     , m_ip4Address(0)
211     , m_hasIpAddress(true)
212 {
213     SetAddress(ip6Address);
214 }
215
216 HostAddress::HostAddress(const IPv6Address& ip6Address)
217     : m_protocol(HostAddress::UnknownNetworkProtocol)
218     , m_ip4Address(0)
219     , m_hasIpAddress(true)
220 {
221     SetAddress(ip6Address);
222 }
223
224 HostAddress::HostAddress(const std::string& address)
225     : m_protocol(HostAddress::UnknownNetworkProtocol)
226     , m_ip4Address(0)
227 {
228     SetAddress(address);
229 }
230
231 HostAddress::HostAddress(const HostAddress& other)
232     : m_protocol(other.m_protocol)
233     , m_ip4Address(other.m_ip4Address)
234     , m_ip6Address(other.m_ip6Address)
235     , m_ipString(other.m_ipString)
236     , m_hasIpAddress(other.m_hasIpAddress)
237 { }
238
239 HostAddress::~HostAddress(void) { }
240
241 bool HostAddress::operator==(const HostAddress& other) const {
242
243     // if self is IPv4
244     if ( m_protocol == HostAddress::IPv4Protocol ) {
245         return ( other.m_protocol == HostAddress::IPv4Protocol &&
246                  m_ip4Address == other.m_ip4Address
247                );
248     }
249
250     // if self is IPv6
251     else if ( m_protocol == HostAddress::IPv6Protocol ) {
252         return ( other.m_protocol == HostAddress::IPv6Protocol &&
253                  memcmp(&m_ip6Address, &other.m_ip6Address, sizeof(IPv6Address)) == 0
254                );
255     }
256
257     // otherwise compare protocols
258     else return m_protocol == other.m_protocol;
259 }
260
261 bool HostAddress::operator<(const HostAddress& other) const {
262
263     // if self is IPv4
264     if ( m_protocol == HostAddress::IPv4Protocol ) {
265         if ( other.m_protocol == HostAddress::IPv4Protocol )
266             return m_ip4Address < m_ip4Address;
267     }
268
269     // if self is IPv6
270     else if ( m_protocol == HostAddress::IPv6Protocol ) {
271         if ( other.m_protocol == HostAddress::IPv6Protocol )
272             return (memcmp(&m_ip6Address, &other.m_ip6Address, sizeof(IPv6Address)) < 0);
273     }
274
275     // otherwise compare protocol types
276     return m_protocol < other.m_protocol;
277 }
278
279 void HostAddress::Clear(void) {
280
281     m_protocol = HostAddress::UnknownNetworkProtocol;
282     m_ip4Address = 0;
283     memset(&m_ip6Address, 0, sizeof(IPv6Address));
284     m_ipString.clear();
285
286     // this may feel funny, but cleared IP (equivalent to '0.0.0.0') is technically valid
287     // and that's not really what this flag is checking anyway
288     //
289     // this flag is false *iff* the string passed in is a 'plain-text' hostname (www.foo.bar)
290     m_hasIpAddress = true;
291 }
292
293 bool HostAddress::HasIPAddress(void) const {
294     return m_hasIpAddress;
295 }
296
297 bool HostAddress::IsNull(void) const {
298     return m_protocol == HostAddress::UnknownNetworkProtocol;
299 }
300
301 uint32_t HostAddress::GetIPv4Address(void) const {
302     return m_ip4Address;
303 }
304
305 IPv6Address HostAddress::GetIPv6Address(void) const {
306     return m_ip6Address;
307 }
308
309 std::string HostAddress::GetIPString(void) const {
310
311     stringstream ss("");
312
313     // IPv4 format
314     if ( m_protocol == HostAddress::IPv4Protocol ) {
315         ss << ( (m_ip4Address>>24) & 0xff ) << '.'
316            << ( (m_ip4Address>>16) & 0xff ) << '.'
317            << ( (m_ip4Address>> 8) & 0xff ) << '.'
318            << (  m_ip4Address      & 0xff );
319
320     }
321
322     // IPv6 format
323     else if ( m_protocol == HostAddress::IPv6Protocol ) {
324         for ( uint8_t i = 0; i < 8; ++i ) {
325             if ( i != 0 )
326                 ss << ':';
327                 ss << hex << ( (uint16_t(m_ip6Address[2*i]) << 8) |
328                                (uint16_t(m_ip6Address[2*i+1]))
329                              );
330         }
331     }
332
333     // return result (empty string if unknown protocol)
334     return ss.str();
335 }
336
337 HostAddress::NetworkProtocol HostAddress::GetProtocol(void) const {
338     return m_protocol;
339 }
340
341 bool HostAddress::ParseAddress(void) {
342
343     // all IPv6 addresses should have a ':'
344     string s = m_ipString;
345     size_t found = s.find(':');
346     if ( found != string::npos ) {
347         // try parse IP6 address
348         uint8_t maybeIp6[16];
349         if ( ParseIp6(s, maybeIp6) ) {
350             SetAddress(maybeIp6);
351             m_protocol = HostAddress::IPv6Protocol;
352             return true;
353         }
354     }
355
356     // all IPv4 addresses should have a '.'
357     found = s.find('.');
358     if ( found != string::npos ) {
359         uint32_t maybeIp4(0);
360         if ( ParseIp4(s, maybeIp4) ) {
361             SetAddress(maybeIp4);
362             m_protocol = HostAddress::IPv4Protocol;
363             return true;
364         }
365     }
366
367     // else likely just a plain-text host name "www.foo.bar"
368     // will need to look up IP address info later
369     m_protocol = HostAddress::UnknownNetworkProtocol;
370     return false;
371 }
372
373 void HostAddress::SetAddress(const uint32_t ip4Address) {
374     m_ip4Address = ip4Address;
375     m_protocol = HostAddress::IPv4Protocol;
376     m_hasIpAddress = true;
377 }
378
379 void HostAddress::SetAddress(const uint8_t* ip6Address) {
380     for ( uint8_t i = 0; i < 16; ++i )
381         m_ip6Address[i] = ip6Address[i];
382     m_protocol = HostAddress::IPv6Protocol;
383     m_hasIpAddress = true;
384 }
385
386 void HostAddress::SetAddress(const IPv6Address& ip6Address) {
387     m_ip6Address = ip6Address;
388     m_ip4Address = 0;
389     m_protocol = HostAddress::IPv6Protocol;
390     m_hasIpAddress = true;
391 }
392
393 void HostAddress::SetAddress(const std::string& address) {
394     m_ipString = address;
395     m_hasIpAddress = ParseAddress();
396 }