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