1 #include "api/internal/io/HostAddress_p.h"
2 using namespace BamTools;
3 using namespace BamTools::Internal;
11 // ------------------------
12 // static utility methods
13 // ------------------------
18 // split a string into fields, on delimiter character
20 vector<string> split(const string& source, char delim) {
21 stringstream ss(source);
23 vector<string> fields;
24 while ( getline(ss, field, delim) )
25 fields.push_back(field);
29 // return number of occurrences of @pattern in @source
31 uint8_t countHits(const string& source, const string& pattern) {
34 size_t found = source.find(pattern);
35 while ( found != string::npos ) {
37 found = source.find(pattern, found+1);
43 bool parseIp4(const string& address, uint32_t& maybeIp4 ) {
45 // split IP address into string fields
46 vector<string> addressFields = split(address, '.');
47 if ( addressFields.size() != 4 )
50 // convert each field to integer value
52 for ( uint8_t i = 0; i < 4; ++i ) {
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]) )
61 int value = atoi( addressFields.at(i).c_str() );
62 if ( value < 0 || value > 255 )
70 // store 32-bit IP address & return success
76 bool parseIp6(const string& address, uint8_t* maybeIp6 ) {
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);
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 )
92 // get number of '::' separators
93 const uint8_t numColonColons = countHits(tmp, "::");
94 if ( numFields == 8 && numColonColons > 1 )
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)) )
104 // iterate over provided fields
106 size_t fillCount = 9 - numFields;
107 for ( int8_t i = numFields - 1; i >= 0; --i ) {
110 const string& field = fields.at(i);
113 if ( field.empty() ) {
115 // if last field empty
116 if ( i == numFields - 1 ) {
117 const string& previousField = fields.at(i-1);
118 if ( previousField.empty() )
120 maybeIp6[--index] = 0;
121 maybeIp6[--index] = 0;
124 // if first field empty
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;
133 // fill in 'compressed' 0s
135 for ( uint8_t j = 0; j < fillCount; ++j ) {
136 if ( index == 0 ) return false;
137 maybeIp6[--index] = 0;
138 maybeIp6[--index] = 0;
145 uint32_t value = static_cast<uint32_t>( strtoul(field.c_str(), 0, 16) );
147 if ( value <= 0xffff ) {
148 maybeIp6[--index] = value & 0xff;
149 maybeIp6[--index] = (value >> 8) & 0xff;
152 // possible mixed IPv4/6 notation
155 // mixed field must be last
156 if ( i != numFields - 1 )
159 // parse the IPv4 section
161 if ( !parseIp4(field, maybeIp4) )
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;
174 // should have parsed OK, return success
178 } // namespace Internal
179 } // namespace BamTools
181 // ----------------------------
182 // HostAddress implementation
183 // ----------------------------
185 HostAddress::HostAddress(void)
186 : m_protocol(HostAddress::UnknownNetworkProtocol)
188 , m_hasIpAddress(true)
191 HostAddress::HostAddress(const uint32_t ip4Address)
192 : m_protocol(HostAddress::UnknownNetworkProtocol)
194 , m_hasIpAddress(true)
196 SetAddress(ip4Address);
199 HostAddress::HostAddress(const uint8_t* ip6Address)
200 : m_protocol(HostAddress::UnknownNetworkProtocol)
202 , m_hasIpAddress(true)
204 SetAddress(ip6Address);
207 HostAddress::HostAddress(const IPv6Address& ip6Address)
208 : m_protocol(HostAddress::UnknownNetworkProtocol)
210 , m_hasIpAddress(true)
212 SetAddress(ip6Address);
215 HostAddress::HostAddress(const std::string& address)
216 : m_protocol(HostAddress::UnknownNetworkProtocol)
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)
230 HostAddress::~HostAddress(void) { }
232 bool HostAddress::operator==(const HostAddress& other) const {
235 if ( m_protocol == HostAddress::IPv4Protocol ) {
236 return ( other.m_protocol == HostAddress::IPv4Protocol &&
237 m_ip4Address == other.m_ip4Address
242 else if ( m_protocol == HostAddress::IPv6Protocol ) {
243 return ( other.m_protocol == HostAddress::IPv6Protocol &&
244 memcmp(&m_ip6Address, &other.m_ip6Address, sizeof(IPv6Address)) == 0
248 // otherwise compare protocols
249 else return m_protocol == other.m_protocol;
252 bool HostAddress::operator<(const HostAddress& other) const {
255 if ( m_protocol == HostAddress::IPv4Protocol ) {
256 if ( other.m_protocol == HostAddress::IPv4Protocol )
257 return m_ip4Address < m_ip4Address;
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);
266 // otherwise compare protocol types
267 return m_protocol < other.m_protocol;
270 void HostAddress::Clear(void) {
272 m_protocol = HostAddress::UnknownNetworkProtocol;
274 memset(&m_ip6Address, 0, sizeof(IPv6Address));
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
280 // this flag is only false iff the string passed in is a 'plain-text' hostname (www.foo.bar)
281 m_hasIpAddress = true;
284 bool HostAddress::HasIPAddress(void) const {
285 return m_hasIpAddress;
288 bool HostAddress::IsNull(void) const {
289 return m_protocol == HostAddress::UnknownNetworkProtocol;
292 uint32_t HostAddress::GetIPv4Address(void) const {
296 IPv6Address HostAddress::GetIPv6Address(void) const {
300 std::string HostAddress::GetIPString(void) const {
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 );
314 else if ( m_protocol == HostAddress::IPv6Protocol ) {
315 for ( uint8_t i = 0; i < 8; ++i ) {
318 ss << hex << ( (uint16_t(m_ip6Address[2*i]) << 8) |
319 (uint16_t(m_ip6Address[2*i+1]))
324 // return result (empty string if unknown protocol)
328 HostAddress::NetworkProtocol HostAddress::GetProtocol(void) const {
332 bool HostAddress::ParseAddress(void) {
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;
347 // all IPv4 addresses should have a '.'
349 if ( found != string::npos ) {
350 uint32_t maybeIp4(0);
351 if ( parseIp4(s, maybeIp4) ) {
352 SetAddress(maybeIp4);
353 m_protocol = HostAddress::IPv4Protocol;
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;
364 void HostAddress::SetAddress(const uint32_t ip4Address) {
365 m_ip4Address = ip4Address;
366 m_protocol = HostAddress::IPv4Protocol;
367 m_hasIpAddress = true;
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;
377 void HostAddress::SetAddress(const IPv6Address& ip6Address) {
378 m_ip6Address = ip6Address;
380 m_protocol = HostAddress::IPv6Protocol;
381 m_hasIpAddress = true;
384 void HostAddress::SetAddress(const std::string& address) {
385 m_ipString = address;
386 m_hasIpAddress = ParseAddress();