1 #include "api/internal/io/HostAddress_p.h"
2 using namespace BamTools;
3 using namespace BamTools::Internal;
11 // ------------------------
12 // static utility methods
13 // ------------------------
18 // convenience 'isalpha' wrapper
20 bool isAlpha(char c) {
21 return ( isalpha(c) != 0 );
24 // split a string into fields, on delimiter character
26 vector<string> split(const string& source, char delim) {
27 stringstream ss(source);
29 vector<string> fields;
30 while ( getline(ss, field, delim) )
31 fields.push_back(field);
35 // return number of occurrences of @pattern in @source
37 uint8_t countHits(const string& source, const string& pattern) {
40 size_t found = source.find(pattern);
41 while ( found != string::npos ) {
43 found = source.find(pattern, found+1);
49 bool parseIp4(const string& address, uint32_t& maybeIp4 ) {
51 // split IP address into string fields
52 vector<string> addressFields = split(address, '.');
53 if ( addressFields.size() != 4 )
56 // convert each field to integer value
58 for ( uint8_t i = 0; i < 4; ++i ) {
60 int value = atoi( addressFields.at(i).c_str() );
61 if ( value < 0 || value > 255 )
69 // store 32-bit IP address & return success
75 bool parseIp6(const string& address, uint8_t* maybeIp6 ) {
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);
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 )
91 // get number of '::' separators
92 const uint8_t numColonColons = countHits(tmp, "::");
93 if ( numFields == 8 && numColonColons > 1 )
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)) )
103 // iterate over provided fields
105 size_t fillCount = 9 - numFields;
106 for ( int8_t i = numFields - 1; i >= 0; --i ) {
109 const string& field = fields.at(i);
112 if ( field.empty() ) {
114 // if last field empty
115 if ( i == numFields - 1 ) {
116 const string& previousField = fields.at(i-1);
117 if ( previousField.empty() )
119 maybeIp6[--index] = 0;
120 maybeIp6[--index] = 0;
123 // if first field empty
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;
132 // fill in 'compressed' 0s
134 for ( uint8_t j = 0; j < fillCount; ++j ) {
135 if ( index == 0 ) return false;
136 maybeIp6[--index] = 0;
137 maybeIp6[--index] = 0;
144 uint32_t value = static_cast<uint32_t>( strtoul(field.c_str(), 0, 16) );
146 if ( value <= 0xffff ) {
147 maybeIp6[--index] = value & 0xff;
148 maybeIp6[--index] = (value >> 8) & 0xff;
151 // possible mixed IPv4/6 notation
154 // mixed field must be last
155 if ( i != numFields - 1 )
158 // parse the IPv4 section
160 if ( !parseIp4(field, maybeIp4) )
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;
173 // should have parsed OK, return success
177 } // namespace Internal
178 } // namespace BamTools
180 // ----------------------------
181 // HostAddress implementation
182 // ----------------------------
184 HostAddress::HostAddress(void)
185 : m_protocol(HostAddress::UnknownNetworkProtocol)
187 , m_hasIpAddress(true)
190 HostAddress::HostAddress(const uint32_t ip4Address)
191 : m_protocol(HostAddress::UnknownNetworkProtocol)
193 , m_hasIpAddress(true)
195 SetAddress(ip4Address);
198 HostAddress::HostAddress(const uint8_t* ip6Address)
199 : m_protocol(HostAddress::UnknownNetworkProtocol)
201 , m_hasIpAddress(true)
203 SetAddress(ip6Address);
206 HostAddress::HostAddress(const IPv6Address& ip6Address)
207 : m_protocol(HostAddress::UnknownNetworkProtocol)
209 , m_hasIpAddress(true)
211 SetAddress(ip6Address);
214 HostAddress::HostAddress(const std::string& address)
215 : m_protocol(HostAddress::UnknownNetworkProtocol)
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)
229 HostAddress::~HostAddress(void) { }
231 bool HostAddress::operator==(const HostAddress& other) const {
234 if ( m_protocol == HostAddress::IPv4Protocol ) {
235 return ( other.m_protocol == HostAddress::IPv4Protocol &&
236 m_ip4Address == other.m_ip4Address
241 else if ( m_protocol == HostAddress::IPv6Protocol ) {
242 return ( other.m_protocol == HostAddress::IPv6Protocol &&
243 memcmp(&m_ip6Address, &other.m_ip6Address, sizeof(IPv6Address)) == 0
247 // otherwise compare protocols
248 else return m_protocol == other.m_protocol;
251 bool HostAddress::operator<(const HostAddress& other) const {
254 if ( m_protocol == HostAddress::IPv4Protocol ) {
255 if ( other.m_protocol == HostAddress::IPv4Protocol )
256 return m_ip4Address < m_ip4Address;
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);
265 // otherwise compare protocol types
266 return m_protocol < other.m_protocol;
269 void HostAddress::Clear(void) {
271 m_protocol = HostAddress::UnknownNetworkProtocol;
273 memset(&m_ip6Address, 0, sizeof(IPv6Address));
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
279 // this flag is only false iff the string passed in is a 'plain-text' hostname (www.foo.bar)
280 m_hasIpAddress = true;
283 bool HostAddress::HasIPAddress(void) const {
284 return m_hasIpAddress;
287 bool HostAddress::IsNull(void) const {
288 return m_protocol == HostAddress::UnknownNetworkProtocol;
291 uint32_t HostAddress::GetIPv4Address(void) const {
295 IPv6Address HostAddress::GetIPv6Address(void) const {
299 std::string HostAddress::GetIPString(void) const {
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 );
313 else if ( m_protocol == HostAddress::IPv6Protocol ) {
314 for ( uint8_t i = 0; i < 8; ++i ) {
317 ss << hex << ( (uint16_t(m_ip6Address[2*i]) << 8) |
318 (uint16_t(m_ip6Address[2*i+1]))
323 // return result (empty string if unknown protocol)
327 HostAddress::NetworkProtocol HostAddress::GetProtocol(void) const {
331 bool HostAddress::ParseAddress(void) {
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;
346 // all IPv4 addresses should have a '.'
348 if ( found != string::npos ) {
349 uint32_t maybeIp4(0);
350 if ( parseIp4(s, maybeIp4) ) {
351 SetAddress(maybeIp4);
352 m_protocol = HostAddress::IPv4Protocol;
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;
363 void HostAddress::SetAddress(const uint32_t ip4Address) {
364 m_ip4Address = ip4Address;
365 m_protocol = HostAddress::IPv4Protocol;
366 m_hasIpAddress = true;
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;
376 void HostAddress::SetAddress(const IPv6Address& ip6Address) {
377 m_ip6Address = ip6Address;
379 m_protocol = HostAddress::IPv6Protocol;
380 m_hasIpAddress = true;
383 void HostAddress::SetAddress(const std::string& address) {
384 m_ipString = address;
385 m_hasIpAddress = ParseAddress();