Add Sentinel configuration reading
[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 #include <ostream>
24
25 using namespace shareddatalayer;
26
27 namespace
28 {
29     uint16_t stringToPort(const std::string& port)
30     {
31         if (port.empty())
32             throw HostAndPort::EmptyPort();
33         try
34         {
35             return htons(boost::lexical_cast<uint16_t>(port));
36         }
37         catch (const boost::bad_lexical_cast&)
38         {
39             char buf[1024];
40             servent resultbuf = { };
41             servent *result(nullptr);
42             getservbyname_r(port.c_str(), nullptr, &resultbuf, buf, sizeof(buf), &result);
43             if (result == nullptr)
44                 throw HostAndPort::InvalidPort(port);
45             return static_cast<uint16_t>(result->s_port);
46         }
47     }
48
49     bool isInBrackets(const std::string& str)
50     {
51         return ((!str.empty()) && (str.front() == '[') && (str.back() == ']'));
52     }
53
54     std::string removeBrackets(const std::string& str)
55     {
56         return str.substr(1U, str.size() - 2U);
57     }
58
59     bool isLiteralIPv6(const std::string& str)
60     {
61         in6_addr tmp;
62         return inet_pton(AF_INET6, str.c_str(), &tmp) == 1;
63     }
64
65     std::string buildInvalidPortError(const std::string& port)
66     {
67         std::ostringstream os;
68         os << "invalid port: " << port;
69         return os.str();
70     }
71 }
72
73 HostAndPort::InvalidPort::InvalidPort(const std::string& port):
74     Exception(buildInvalidPortError(port))
75 {
76 }
77
78 HostAndPort::EmptyPort::EmptyPort():
79     Exception("empty port")
80 {
81 }
82
83 HostAndPort::EmptyHost::EmptyHost():
84     Exception("empty host")
85 {
86 }
87
88 HostAndPort::HostAndPort(const std::string& addressAndOptionalPort, uint16_t defaultPort)
89 {
90     if (isInBrackets(addressAndOptionalPort))
91         parse(removeBrackets(addressAndOptionalPort), defaultPort);
92     else
93         parse(addressAndOptionalPort, defaultPort);
94 }
95
96 void HostAndPort::parse(const std::string& addressAndOptionalPort, uint16_t defaultPort)
97 {
98     const auto pos(addressAndOptionalPort.rfind(':'));
99     if (pos == std::string::npos)
100     {
101         host = addressAndOptionalPort;
102         port = defaultPort;
103         literalIPv6 = false;
104     }
105     else if (isLiteralIPv6(addressAndOptionalPort))
106     {
107         host = addressAndOptionalPort;
108         port = defaultPort;
109         literalIPv6 = true;
110     }
111     else
112     {
113         host = addressAndOptionalPort.substr(0, pos);
114         if (isInBrackets(host))
115             host = removeBrackets(host);
116         literalIPv6 = isLiteralIPv6(host);
117         port = stringToPort(addressAndOptionalPort.substr(pos + 1U, addressAndOptionalPort.size() - pos - 1U));
118     }
119     if (host.empty())
120         throw EmptyHost();
121 }
122
123 const std::string& HostAndPort::getHost() const
124 {
125     return host;
126 }
127
128 uint16_t HostAndPort::getPort() const
129 {
130     return port;
131 }
132
133 std::string HostAndPort::getString() const
134 {
135     std::ostringstream os;
136     if (literalIPv6)
137         os << '[' << host << "]:" << ntohs(port);
138     else
139         os << host << ':' << ntohs(port);
140     return os.str();
141 }
142
143 bool HostAndPort::operator==(const HostAndPort& hp) const
144 {
145     return this->getPort() == hp.getPort() && this->getHost() == hp.getHost();
146 }
147
148 bool HostAndPort::operator!=(const HostAndPort& hp) const
149 {
150     return !(hp == *this);
151 }
152
153 bool HostAndPort::operator<(const HostAndPort& hp) const
154 {
155     if (this->getHost() == hp.getHost())
156         return this->getPort() < hp.getPort();
157     else
158         return this->getHost() < hp.getHost();
159 }
160
161 std::ostream& shareddatalayer::operator << (std::ostream& os, const HostAndPort& hostAndPort)
162 {
163     os << hostAndPort.getHost() << ":" << hostAndPort.getPort();
164     return os;
165 }