Add first version
[ric-plt/sdl.git] / src / hostandport.cpp
1 /*
2    Copyright (c) 2018-2019 Nokia.
3
4    Licensed under the Apache License, Version 2.0 (the "License");
5    you may not use this file except in compliance with the License.
6    You may obtain a copy of the License at
7
8        http://www.apache.org/licenses/LICENSE-2.0
9
10    Unless required by applicable law or agreed to in writing, software
11    distributed under the License is distributed on an "AS IS" BASIS,
12    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13    See the License for the specific language governing permissions and
14    limitations under the License.
15 */
16
17 #include "private/hostandport.hpp"
18 #include <sstream>
19 #include <netdb.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <boost/lexical_cast.hpp>
23
24 using namespace shareddatalayer;
25
26 namespace
27 {
28     uint16_t stringToPort(const std::string& port)
29     {
30         if (port.empty())
31             throw HostAndPort::EmptyPort();
32         try
33         {
34             return htons(boost::lexical_cast<uint16_t>(port));
35         }
36         catch (const boost::bad_lexical_cast&)
37         {
38             char buf[1024];
39             servent resultbuf = { };
40             servent *result(nullptr);
41             getservbyname_r(port.c_str(), nullptr, &resultbuf, buf, sizeof(buf), &result);
42             if (result == nullptr)
43                 throw HostAndPort::InvalidPort(port);
44             return static_cast<uint16_t>(result->s_port);
45         }
46     }
47
48     bool isInBrackets(const std::string& str)
49     {
50         return ((!str.empty()) && (str.front() == '[') && (str.back() == ']'));
51     }
52
53     std::string removeBrackets(const std::string& str)
54     {
55         return str.substr(1U, str.size() - 2U);
56     }
57
58     bool isLiteralIPv6(const std::string& str)
59     {
60         in6_addr tmp;
61         return inet_pton(AF_INET6, str.c_str(), &tmp) == 1;
62     }
63
64     std::string buildInvalidPortError(const std::string& port)
65     {
66         std::ostringstream os;
67         os << "invalid port: " << port;
68         return os.str();
69     }
70 }
71
72 HostAndPort::InvalidPort::InvalidPort(const std::string& port):
73     Exception(buildInvalidPortError(port))
74 {
75 }
76
77 HostAndPort::EmptyPort::EmptyPort():
78     Exception("empty port")
79 {
80 }
81
82 HostAndPort::EmptyHost::EmptyHost():
83     Exception("empty host")
84 {
85 }
86
87 HostAndPort::HostAndPort(const std::string& addressAndOptionalPort, uint16_t defaultPort)
88 {
89     if (isInBrackets(addressAndOptionalPort))
90         parse(removeBrackets(addressAndOptionalPort), defaultPort);
91     else
92         parse(addressAndOptionalPort, defaultPort);
93 }
94
95 void HostAndPort::parse(const std::string& addressAndOptionalPort, uint16_t defaultPort)
96 {
97     const auto pos(addressAndOptionalPort.rfind(':'));
98     if (pos == std::string::npos)
99     {
100         host = addressAndOptionalPort;
101         port = defaultPort;
102         literalIPv6 = false;
103     }
104     else if (isLiteralIPv6(addressAndOptionalPort))
105     {
106         host = addressAndOptionalPort;
107         port = defaultPort;
108         literalIPv6 = true;
109     }
110     else
111     {
112         host = addressAndOptionalPort.substr(0, pos);
113         if (isInBrackets(host))
114             host = removeBrackets(host);
115         literalIPv6 = isLiteralIPv6(host);
116         port = stringToPort(addressAndOptionalPort.substr(pos + 1U, addressAndOptionalPort.size() - pos - 1U));
117     }
118     if (host.empty())
119         throw EmptyHost();
120 }
121
122 const std::string& HostAndPort::getHost() const
123 {
124     return host;
125 }
126
127 uint16_t HostAndPort::getPort() const
128 {
129     return port;
130 }
131
132 std::string HostAndPort::getString() const
133 {
134     std::ostringstream os;
135     if (literalIPv6)
136         os << '[' << host << "]:" << ntohs(port);
137     else
138         os << host << ':' << ntohs(port);
139     return os.str();
140 }
141
142 bool HostAndPort::operator==(const HostAndPort& hp) const
143 {
144     return this->getPort() == hp.getPort() && this->getHost() == hp.getHost();
145 }
146
147 bool HostAndPort::operator!=(const HostAndPort& hp) const
148 {
149     return !(hp == *this);
150 }
151
152 bool HostAndPort::operator<(const HostAndPort& hp) const
153 {
154     if (this->getHost() == hp.getHost())
155         return this->getPort() < hp.getPort();
156     else
157         return this->getHost() < hp.getHost();
158 }