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