]> git.donarmstrong.com Git - bamtools.git/blobdiff - src/api/internal/io/HostAddress_p.cpp
Implemented basic TCP support layer
[bamtools.git] / src / api / internal / io / HostAddress_p.cpp
diff --git a/src/api/internal/io/HostAddress_p.cpp b/src/api/internal/io/HostAddress_p.cpp
new file mode 100644 (file)
index 0000000..aa3c9a3
--- /dev/null
@@ -0,0 +1,386 @@
+#include "api/internal/io/HostAddress_p.h"
+using namespace BamTools;
+using namespace BamTools::Internal;
+
+#include <cctype>
+#include <cstdlib>
+#include <sstream>
+#include <vector>
+using namespace std;
+
+// ------------------------
+// static utility methods
+// ------------------------
+
+namespace BamTools {
+namespace Internal {
+
+// convenience 'isalpha' wrapper
+static inline
+bool isAlpha(char c) {
+    return ( isalpha(c) != 0 );
+}
+
+// split a string into fields, on delimiter character
+static inline
+vector<string> split(const string& source, char delim) {
+    stringstream ss(source);
+    string field;
+    vector<string> fields;
+    while ( getline(ss, field, delim) )
+        fields.push_back(field);
+    return fields;
+}
+
+// return number of occurrences of @pattern in @source
+static inline
+uint8_t countHits(const string& source, const string& pattern) {
+
+    uint8_t count(0);
+    size_t found = source.find(pattern);
+    while ( found != string::npos ) {
+        ++count;
+        found = source.find(pattern, found+1);
+    }
+    return count;
+}
+
+static
+bool parseIp4(const string& address, uint32_t& maybeIp4 ) {
+
+    // split IP address into string fields
+    vector<string> addressFields = split(address, '.');
+    if ( addressFields.size() != 4 )
+        return false;
+
+    // convert each field to integer value
+    uint32_t ipv4(0);
+    for ( uint8_t i = 0; i < 4; ++i ) {
+
+        int value = atoi( addressFields.at(i).c_str() );
+        if ( value < 0 || value > 255 )
+            return false;
+
+        // append byte value
+        ipv4 <<= 8;
+        ipv4 += value;
+    }
+
+    // store 32-bit IP address & return success
+    maybeIp4 = ipv4;
+    return true;
+}
+
+static
+bool parseIp6(const string& address, uint8_t* maybeIp6 ) {
+
+    string tmp = address;
+
+    // look for '%' char (if found, lop off that part of address)
+    // we're going to ignore any link-local zone index, for now at least
+    const size_t percentFound = tmp.rfind('%');
+    if ( percentFound != string::npos )
+        tmp = tmp.substr(0, percentFound);
+
+    // split IP address into string fields
+    vector<string> fields = split(tmp, ':');
+    const uint8_t numFields = fields.size();
+    if ( numFields < 3 || numFields > 8 )
+        return false;
+
+    // get number of '::' separators
+    const uint8_t numColonColons = countHits(tmp, "::");
+    if ( numFields == 8 && numColonColons > 1 )
+        return false;
+
+    // check valid IPv6 'compression'
+    // must be valid 'pure' IPv6 or mixed IPv4/6 notation
+    const size_t dotFound = tmp.find('.');
+    const bool isMixed = ( dotFound != string::npos );
+    if ( numColonColons != 1 && (numFields < (isMixed ? 7 : 8)) )
+        return false;
+
+    // iterate over provided fields
+    size_t index = 16;
+    size_t fillCount = 9 - numFields;
+    for ( int8_t i = numFields - 1; i >= 0; --i ) {
+        if ( index == 0 )
+            return false;
+        const string& field = fields.at(i);
+
+        // if field empty
+        if ( field.empty() ) {
+
+            // if last field empty
+            if ( i == numFields - 1 ) {
+                const string& previousField = fields.at(i-1);
+                if ( previousField.empty() )
+                    return false;
+                maybeIp6[--index] = 0;
+                maybeIp6[--index] = 0;
+            }
+
+            // if first field empty
+            else if ( i == 0 ) {
+                // make sure ':' isn't first character
+                const string& nextField = fields.at(i+1);
+                if ( nextField.empty() ) return false;
+                maybeIp6[--index] = 0;
+                maybeIp6[--index] = 0;
+            }
+
+            // fill in 'compressed' 0s
+            else {
+                for ( uint8_t j = 0; j < fillCount; ++j ) {
+                    if ( index == 0 ) return false;
+                    maybeIp6[--index] = 0;
+                    maybeIp6[--index] = 0;
+                }
+            }
+        }
+
+        // field has data
+        else {
+            uint32_t value = static_cast<uint32_t>( strtoul(field.c_str(), 0, 16) );
+
+            if ( value <= 0xffff ) {
+                maybeIp6[--index] =  value       & 0xff;
+                maybeIp6[--index] = (value >> 8) & 0xff;
+            }
+
+            // possible mixed IPv4/6 notation
+            else {
+
+                // mixed field must be last
+                if ( i != numFields - 1 )
+                    return false;
+
+                // parse the IPv4 section
+                uint32_t maybeIp4;
+                if ( !parseIp4(field, maybeIp4) )
+                    return false;
+
+                // store IPv4 fields in IPv6 container
+                maybeIp6[--index] =  maybeIp4        & 0xff;
+                maybeIp6[--index] = (maybeIp4 >> 8)  & 0xff;
+                maybeIp6[--index] = (maybeIp4 >> 16) & 0xff;
+                maybeIp6[--index] = (maybeIp4 >> 24) & 0xff;
+                --fillCount;
+            }
+        }
+    }
+
+    // should have parsed OK, return success
+    return true;
+}
+
+} // namespace Internal
+} // namespace BamTools
+
+// ----------------------------
+// HostAddress implementation
+// ----------------------------
+
+HostAddress::HostAddress(void)
+    : m_protocol(HostAddress::UnknownNetworkProtocol)
+    , m_ip4Address(0)
+    , m_hasIpAddress(true)
+{ }
+
+HostAddress::HostAddress(const uint32_t ip4Address)
+    : m_protocol(HostAddress::UnknownNetworkProtocol)
+    , m_ip4Address(0)
+    , m_hasIpAddress(true)
+{
+    SetAddress(ip4Address);
+}
+
+HostAddress::HostAddress(const uint8_t* ip6Address)
+    : m_protocol(HostAddress::UnknownNetworkProtocol)
+    , m_ip4Address(0)
+    , m_hasIpAddress(true)
+{
+    SetAddress(ip6Address);
+}
+
+HostAddress::HostAddress(const IPv6Address& ip6Address)
+    : m_protocol(HostAddress::UnknownNetworkProtocol)
+    , m_ip4Address(0)
+    , m_hasIpAddress(true)
+{
+    SetAddress(ip6Address);
+}
+
+HostAddress::HostAddress(const std::string& address)
+    : m_protocol(HostAddress::UnknownNetworkProtocol)
+    , m_ip4Address(0)
+{
+    SetAddress(address);
+}
+
+HostAddress::HostAddress(const HostAddress& other)
+    : m_protocol(other.m_protocol)
+    , m_ip4Address(other.m_ip4Address)
+    , m_ip6Address(other.m_ip6Address)
+    , m_ipString(other.m_ipString)
+    , m_hasIpAddress(other.m_hasIpAddress)
+{ }
+
+HostAddress::~HostAddress(void) { }
+
+bool HostAddress::operator==(const HostAddress& other) const {
+
+    // if self is IPv4
+    if ( m_protocol == HostAddress::IPv4Protocol ) {
+        return ( other.m_protocol == HostAddress::IPv4Protocol &&
+                 m_ip4Address == other.m_ip4Address
+               );
+    }
+
+    // if self is IPv6
+    else if ( m_protocol == HostAddress::IPv6Protocol ) {
+        return ( other.m_protocol == HostAddress::IPv6Protocol &&
+                 memcmp(&m_ip6Address, &other.m_ip6Address, sizeof(IPv6Address)) == 0
+               );
+    }
+
+    // otherwise compare protocols
+    else return m_protocol == other.m_protocol;
+}
+
+bool HostAddress::operator<(const HostAddress& other) const {
+
+    // if self is IPv4
+    if ( m_protocol == HostAddress::IPv4Protocol ) {
+        if ( other.m_protocol == HostAddress::IPv4Protocol )
+            return m_ip4Address < m_ip4Address;
+    }
+
+    // if self is IPv6
+    else if ( m_protocol == HostAddress::IPv6Protocol ) {
+        if ( other.m_protocol == HostAddress::IPv6Protocol )
+            return (memcmp(&m_ip6Address, &other.m_ip6Address, sizeof(IPv6Address)) < 0);
+    }
+
+    // otherwise compare protocol types
+    return m_protocol < other.m_protocol;
+}
+
+void HostAddress::Clear(void) {
+
+    m_protocol = HostAddress::UnknownNetworkProtocol;
+    m_ip4Address = 0;
+    memset(&m_ip6Address, 0, sizeof(IPv6Address));
+    m_ipString.clear();
+
+    // this may feel funny, but cleared IP value (equivalent to '0.0.0.0') is technically valid IP
+    // and that's not really what this flag is checking
+    //
+    // this flag is only false iff the string passed in is a 'plain-text' hostname (www.foo.bar)
+    m_hasIpAddress = true;
+}
+
+bool HostAddress::HasIPAddress(void) const {
+    return m_hasIpAddress;
+}
+
+bool HostAddress::IsNull(void) const {
+    return m_protocol == HostAddress::UnknownNetworkProtocol;
+}
+
+uint32_t HostAddress::GetIPv4Address(void) const {
+    return m_ip4Address;
+}
+
+IPv6Address HostAddress::GetIPv6Address(void) const {
+    return m_ip6Address;
+}
+
+std::string HostAddress::GetIPString(void) const {
+
+    stringstream ss("");
+
+    // IPv4 format
+    if ( m_protocol == HostAddress::IPv4Protocol ) {
+        ss << ( (m_ip4Address>>24) & 0xff ) << '.'
+           << ( (m_ip4Address>>16) & 0xff ) << '.'
+           << ( (m_ip4Address>> 8) & 0xff ) << '.'
+           << (  m_ip4Address      & 0xff );
+
+    }
+
+    // IPv6 format
+    else if ( m_protocol == HostAddress::IPv6Protocol ) {
+        for ( uint8_t i = 0; i < 8; ++i ) {
+            if ( i != 0 )
+                ss << ':';
+                ss << hex << ( (uint16_t(m_ip6Address[2*i]) << 8) |
+                               (uint16_t(m_ip6Address[2*i+1]))
+                             );
+        }
+    }
+
+    // return result (empty string if unknown protocol)
+    return ss.str();
+}
+
+HostAddress::NetworkProtocol HostAddress::GetProtocol(void) const {
+    return m_protocol;
+}
+
+bool HostAddress::ParseAddress(void) {
+
+    // all IPv6 addresses should have a ':'
+    string s = m_ipString;
+    size_t found = s.find(':');
+    if ( found != string::npos ) {
+        // try parse IP6 address
+        uint8_t maybeIp6[16];
+        if ( parseIp6(s, maybeIp6) ) {
+            SetAddress(maybeIp6);
+            m_protocol = HostAddress::IPv6Protocol;
+            return true;
+        }
+    }
+
+    // all IPv4 addresses should have a '.'
+    found = s.find('.');
+    if ( found != string::npos ) {
+        uint32_t maybeIp4(0);
+        if ( parseIp4(s, maybeIp4) ) {
+            SetAddress(maybeIp4);
+            m_protocol = HostAddress::IPv4Protocol;
+            return true;
+        }
+    }
+
+    // else likely just a plain-text host name "www.foo.bar"
+    // will need to look up IP address info later
+    m_protocol = HostAddress::UnknownNetworkProtocol;
+    return false;
+}
+
+void HostAddress::SetAddress(const uint32_t ip4Address) {
+    m_ip4Address = ip4Address;
+    m_protocol = HostAddress::IPv4Protocol;
+    m_hasIpAddress = true;
+}
+
+void HostAddress::SetAddress(const uint8_t* ip6Address) {
+    for ( uint8_t i = 0; i < 16; ++i )
+        m_ip6Address[i] = ip6Address[i];
+    m_protocol = HostAddress::IPv6Protocol;
+    m_hasIpAddress = true;
+}
+
+void HostAddress::SetAddress(const IPv6Address& ip6Address) {
+    m_ip6Address = ip6Address;
+    m_ip4Address = 0;
+    m_protocol = HostAddress::IPv6Protocol;
+    m_hasIpAddress = true;
+}
+
+void HostAddress::SetAddress(const std::string& address) {
+    m_ipString = address;
+    m_hasIpAddress = ParseAddress();
+}