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 // ***************************************************************************
10 #include "api/internal/io/HostAddress_p.h"
11 using namespace BamTools;
12 using namespace BamTools::Internal;
20 // ------------------------
21 // static utility methods
22 // ------------------------
27 // split a string into fields, on delimiter character
29 vector<string> Split(const string& source, char delim) {
30 stringstream ss(source);
32 vector<string> fields;
33 while ( getline(ss, field, delim) )
34 fields.push_back(field);
38 // return number of occurrences of @pattern in @source
40 uint8_t CountHits(const string& source, const string& pattern) {
43 size_t found = source.find(pattern);
44 while ( found != string::npos ) {
46 found = source.find(pattern, found+1);
52 bool ParseIp4(const string& address, uint32_t& maybeIp4 ) {
54 // split IP address into string fields
55 vector<string> addressFields = Split(address, '.');
56 if ( addressFields.size() != 4 )
59 // convert each field to integer value
61 for ( uint8_t i = 0; i < 4; ++i ) {
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]) )
70 int value = atoi( addressFields.at(i).c_str() );
71 if ( value < 0 || value > 255 )
79 // store 32-bit IP address & return success
85 bool ParseIp6(const string& address, uint8_t* maybeIp6 ) {
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);
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 )
101 // get number of '::' separators
102 const uint8_t numColonColons = CountHits(tmp, "::");
103 if ( numFields == 8 && numColonColons > 1 )
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)) )
113 // iterate over provided fields
115 size_t fillCount = 9 - numFields;
116 for ( int8_t i = numFields - 1; i >= 0; --i ) {
119 const string& field = fields.at(i);
122 if ( field.empty() ) {
124 // if last field empty
125 if ( i == numFields - 1 ) {
126 const string& previousField = fields.at(i-1);
127 if ( previousField.empty() )
129 maybeIp6[--index] = 0;
130 maybeIp6[--index] = 0;
133 // if first field empty
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;
142 // fill in 'compressed' 0s
144 for ( uint8_t j = 0; j < fillCount; ++j ) {
145 if ( index == 0 ) return false;
146 maybeIp6[--index] = 0;
147 maybeIp6[--index] = 0;
154 uint32_t value = static_cast<uint32_t>( strtoul(field.c_str(), 0, 16) );
156 if ( value <= 0xffff ) {
157 maybeIp6[--index] = value & 0xff;
158 maybeIp6[--index] = (value >> 8) & 0xff;
161 // possible mixed IPv4/6 notation
164 // mixed field must be last
165 if ( i != numFields - 1 )
168 // parse the IPv4 section
170 if ( !ParseIp4(field, maybeIp4) )
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;
183 // should have parsed OK, return success
187 } // namespace Internal
188 } // namespace BamTools
190 // ----------------------------
191 // HostAddress implementation
192 // ----------------------------
194 HostAddress::HostAddress(void)
195 : m_protocol(HostAddress::UnknownNetworkProtocol)
197 , m_hasIpAddress(true)
200 HostAddress::HostAddress(const uint32_t ip4Address)
201 : m_protocol(HostAddress::UnknownNetworkProtocol)
203 , m_hasIpAddress(true)
205 SetAddress(ip4Address);
208 HostAddress::HostAddress(const uint8_t* ip6Address)
209 : m_protocol(HostAddress::UnknownNetworkProtocol)
211 , m_hasIpAddress(true)
213 SetAddress(ip6Address);
216 HostAddress::HostAddress(const IPv6Address& ip6Address)
217 : m_protocol(HostAddress::UnknownNetworkProtocol)
219 , m_hasIpAddress(true)
221 SetAddress(ip6Address);
224 HostAddress::HostAddress(const std::string& address)
225 : m_protocol(HostAddress::UnknownNetworkProtocol)
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)
239 HostAddress::~HostAddress(void) { }
241 bool HostAddress::operator==(const HostAddress& other) const {
244 if ( m_protocol == HostAddress::IPv4Protocol ) {
245 return ( other.m_protocol == HostAddress::IPv4Protocol &&
246 m_ip4Address == other.m_ip4Address
251 else if ( m_protocol == HostAddress::IPv6Protocol ) {
252 return ( other.m_protocol == HostAddress::IPv6Protocol &&
253 memcmp(&m_ip6Address, &other.m_ip6Address, sizeof(IPv6Address)) == 0
257 // otherwise compare protocols
258 else return m_protocol == other.m_protocol;
261 bool HostAddress::operator<(const HostAddress& other) const {
264 if ( m_protocol == HostAddress::IPv4Protocol ) {
265 if ( other.m_protocol == HostAddress::IPv4Protocol )
266 return m_ip4Address < m_ip4Address;
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);
275 // otherwise compare protocol types
276 return m_protocol < other.m_protocol;
279 void HostAddress::Clear(void) {
281 m_protocol = HostAddress::UnknownNetworkProtocol;
283 memset(&m_ip6Address, 0, sizeof(IPv6Address));
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
289 // this flag is false *iff* the string passed in is a 'plain-text' hostname (www.foo.bar)
290 m_hasIpAddress = true;
293 bool HostAddress::HasIPAddress(void) const {
294 return m_hasIpAddress;
297 bool HostAddress::IsNull(void) const {
298 return m_protocol == HostAddress::UnknownNetworkProtocol;
301 uint32_t HostAddress::GetIPv4Address(void) const {
305 IPv6Address HostAddress::GetIPv6Address(void) const {
309 std::string HostAddress::GetIPString(void) const {
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 );
323 else if ( m_protocol == HostAddress::IPv6Protocol ) {
324 for ( uint8_t i = 0; i < 8; ++i ) {
327 ss << hex << ( (uint16_t(m_ip6Address[2*i]) << 8) |
328 (uint16_t(m_ip6Address[2*i+1]))
333 // return result (empty string if unknown protocol)
337 HostAddress::NetworkProtocol HostAddress::GetProtocol(void) const {
341 bool HostAddress::ParseAddress(void) {
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;
356 // all IPv4 addresses should have a '.'
358 if ( found != string::npos ) {
359 uint32_t maybeIp4(0);
360 if ( ParseIp4(s, maybeIp4) ) {
361 SetAddress(maybeIp4);
362 m_protocol = HostAddress::IPv4Protocol;
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;
373 void HostAddress::SetAddress(const uint32_t ip4Address) {
374 m_ip4Address = ip4Address;
375 m_protocol = HostAddress::IPv4Protocol;
376 m_hasIpAddress = true;
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;
386 void HostAddress::SetAddress(const IPv6Address& ip6Address) {
387 m_ip6Address = ip6Address;
389 m_protocol = HostAddress::IPv6Protocol;
390 m_hasIpAddress = true;
393 void HostAddress::SetAddress(const std::string& address) {
394 m_ipString = address;
395 m_hasIpAddress = ParseAddress();