Add first version
[ric-plt/sdl.git] / src / hostandport.cpp
diff --git a/src/hostandport.cpp b/src/hostandport.cpp
new file mode 100644 (file)
index 0000000..de045ed
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+   Copyright (c) 2018-2019 Nokia.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+#include "private/hostandport.hpp"
+#include <sstream>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <boost/lexical_cast.hpp>
+
+using namespace shareddatalayer;
+
+namespace
+{
+    uint16_t stringToPort(const std::string& port)
+    {
+        if (port.empty())
+            throw HostAndPort::EmptyPort();
+        try
+        {
+            return htons(boost::lexical_cast<uint16_t>(port));
+        }
+        catch (const boost::bad_lexical_cast&)
+        {
+            char buf[1024];
+            servent resultbuf = { };
+            servent *result(nullptr);
+            getservbyname_r(port.c_str(), nullptr, &resultbuf, buf, sizeof(buf), &result);
+            if (result == nullptr)
+                throw HostAndPort::InvalidPort(port);
+            return static_cast<uint16_t>(result->s_port);
+        }
+    }
+
+    bool isInBrackets(const std::string& str)
+    {
+        return ((!str.empty()) && (str.front() == '[') && (str.back() == ']'));
+    }
+
+    std::string removeBrackets(const std::string& str)
+    {
+        return str.substr(1U, str.size() - 2U);
+    }
+
+    bool isLiteralIPv6(const std::string& str)
+    {
+        in6_addr tmp;
+        return inet_pton(AF_INET6, str.c_str(), &tmp) == 1;
+    }
+
+    std::string buildInvalidPortError(const std::string& port)
+    {
+        std::ostringstream os;
+        os << "invalid port: " << port;
+        return os.str();
+    }
+}
+
+HostAndPort::InvalidPort::InvalidPort(const std::string& port):
+    Exception(buildInvalidPortError(port))
+{
+}
+
+HostAndPort::EmptyPort::EmptyPort():
+    Exception("empty port")
+{
+}
+
+HostAndPort::EmptyHost::EmptyHost():
+    Exception("empty host")
+{
+}
+
+HostAndPort::HostAndPort(const std::string& addressAndOptionalPort, uint16_t defaultPort)
+{
+    if (isInBrackets(addressAndOptionalPort))
+        parse(removeBrackets(addressAndOptionalPort), defaultPort);
+    else
+        parse(addressAndOptionalPort, defaultPort);
+}
+
+void HostAndPort::parse(const std::string& addressAndOptionalPort, uint16_t defaultPort)
+{
+    const auto pos(addressAndOptionalPort.rfind(':'));
+    if (pos == std::string::npos)
+    {
+        host = addressAndOptionalPort;
+        port = defaultPort;
+        literalIPv6 = false;
+    }
+    else if (isLiteralIPv6(addressAndOptionalPort))
+    {
+        host = addressAndOptionalPort;
+        port = defaultPort;
+        literalIPv6 = true;
+    }
+    else
+    {
+        host = addressAndOptionalPort.substr(0, pos);
+        if (isInBrackets(host))
+            host = removeBrackets(host);
+        literalIPv6 = isLiteralIPv6(host);
+        port = stringToPort(addressAndOptionalPort.substr(pos + 1U, addressAndOptionalPort.size() - pos - 1U));
+    }
+    if (host.empty())
+        throw EmptyHost();
+}
+
+const std::string& HostAndPort::getHost() const
+{
+    return host;
+}
+
+uint16_t HostAndPort::getPort() const
+{
+    return port;
+}
+
+std::string HostAndPort::getString() const
+{
+    std::ostringstream os;
+    if (literalIPv6)
+        os << '[' << host << "]:" << ntohs(port);
+    else
+        os << host << ':' << ntohs(port);
+    return os.str();
+}
+
+bool HostAndPort::operator==(const HostAndPort& hp) const
+{
+    return this->getPort() == hp.getPort() && this->getHost() == hp.getHost();
+}
+
+bool HostAndPort::operator!=(const HostAndPort& hp) const
+{
+    return !(hp == *this);
+}
+
+bool HostAndPort::operator<(const HostAndPort& hp) const
+{
+    if (this->getHost() == hp.getHost())
+        return this->getPort() < hp.getPort();
+    else
+        return this->getHost() < hp.getHost();
+}