1
2 /**
3 * Copyright (C) 2018-present MongoDB, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the Server Side Public License, version 1,
7 * as published by MongoDB, Inc.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * Server Side Public License for more details.
13 *
14 * You should have received a copy of the Server Side Public License
15 * along with this program. If not, see
16 * <http://www.mongodb.com/licensing/server-side-public-license>.
17 *
18 * As a special exception, the copyright holders give permission to link the
19 * code of portions of this program with the OpenSSL library under certain
20 * conditions as described in each individual source file and distribute
21 * linked combinations including the program with the OpenSSL library. You
22 * must comply with the Server Side Public License in all respects for
23 * all of the code used other than as permitted herein. If you modify file(s)
24 * with this exception, you may extend this exception to your version of the
25 * file(s), but you are not obligated to do so. If you do not wish to do so,
26 * delete this exception statement from your version. If you delete this
27 * exception statement from all source files in the program, then also delete
28 * it in the license file.
29 */
30
31 #include "mongo/platform/basic.h"
32
33 #include "mongo/util/net/hostandport.h"
34
35 #include <boost/functional/hash.hpp>
36
37 #include "mongo/base/status.h"
38 #include "mongo/base/status_with.h"
39 #include "mongo/base/string_data.h"
40 #include "mongo/bson/util/builder.h"
41 #include "mongo/db/server_options.h"
42 #include "mongo/util/assert_util.h"
43 #include "mongo/util/mongoutils/str.h"
44 #include "mongo/util/stringutils.h"
45
46 namespace mongo {
47
parse(StringData text)48 StatusWith<HostAndPort> HostAndPort::parse(StringData text) {
49 HostAndPort result;
50 Status status = result.initialize(text);
51 if (!status.isOK()) {
52 return StatusWith<HostAndPort>(status);
53 }
54 return StatusWith<HostAndPort>(result);
55 }
56
HostAndPort()57 HostAndPort::HostAndPort() : _port(-1) {}
58
HostAndPort(StringData text)59 HostAndPort::HostAndPort(StringData text) {
60 uassertStatusOK(initialize(text));
61 }
62
HostAndPort(const std::string & h,int p)63 HostAndPort::HostAndPort(const std::string& h, int p) : _host(h), _port(p) {}
64
HostAndPort(SockAddr addr)65 HostAndPort::HostAndPort(SockAddr addr) : _addr(std::move(addr)) {
66 uassertStatusOK(initialize(_addr->toString(true)));
67 }
68
operator <(const HostAndPort & r) const69 bool HostAndPort::operator<(const HostAndPort& r) const {
70 const int cmp = host().compare(r.host());
71 if (cmp)
72 return cmp < 0;
73 return port() < r.port();
74 }
75
operator ==(const HostAndPort & r) const76 bool HostAndPort::operator==(const HostAndPort& r) const {
77 return host() == r.host() && port() == r.port();
78 }
79
port() const80 int HostAndPort::port() const {
81 if (hasPort())
82 return _port;
83 return ServerGlobalParams::DefaultDBPort;
84 }
85
isLocalHost() const86 bool HostAndPort::isLocalHost() const {
87 return (_host == "localhost" || str::startsWith(_host.c_str(), "127.") || _host == "::1" ||
88 _host == "anonymous unix socket" || _host.c_str()[0] == '/' // unix socket
89 );
90 }
91
isDefaultRoute() const92 bool HostAndPort::isDefaultRoute() const {
93 if (_host == "0.0.0.0") {
94 return true;
95 }
96
97 // There are multiple ways to write IPv6 addresses.
98 // We're looking for any representation of the address "0:0:0:0:0:0:0:0".
99 // A single sequence of "0" bytes in an IPv6 address may be represented as "::",
100 // so we must also match addresses like "::" or "0::0:0".
101 // Return false if a character other than ':' or '0' is contained in the address.
102 auto firstNonDefaultIPv6Char =
103 std::find_if(std::begin(_host), std::end(_host), [](const char& c) {
104 return c != ':' && c != '0' && c != '[' && c != ']';
105 });
106 return firstNonDefaultIPv6Char == std::end(_host);
107 }
108
toString() const109 std::string HostAndPort::toString() const {
110 StringBuilder ss;
111 append(ss);
112 return ss.str();
113 }
114
append(StringBuilder & ss) const115 void HostAndPort::append(StringBuilder& ss) const {
116 // wrap ipv6 addresses in []s for roundtrip-ability
117 if (host().find(':') != std::string::npos) {
118 ss << '[' << host() << ']';
119 } else {
120 ss << host();
121 }
122 if (host().find('/') == std::string::npos) {
123 ss << ':' << port();
124 }
125 }
126
empty() const127 bool HostAndPort::empty() const {
128 return _host.empty() && _port < 0;
129 }
130
initialize(StringData s)131 Status HostAndPort::initialize(StringData s) {
132 size_t colonPos = s.rfind(':');
133 StringData hostPart = s.substr(0, colonPos);
134
135 // handle ipv6 hostPart (which we require to be wrapped in []s)
136 const size_t openBracketPos = s.find('[');
137 const size_t closeBracketPos = s.find(']');
138 if (openBracketPos != std::string::npos) {
139 if (openBracketPos != 0) {
140 return Status(ErrorCodes::FailedToParse,
141 str::stream() << "'[' present, but not first character in "
142 << s.toString());
143 }
144 if (closeBracketPos == std::string::npos) {
145 return Status(ErrorCodes::FailedToParse,
146 str::stream() << "ipv6 address is missing closing ']' in hostname in "
147 << s.toString());
148 }
149
150 hostPart = s.substr(openBracketPos + 1, closeBracketPos - openBracketPos - 1);
151 // prevent accidental assignment of port to the value of the final portion of hostPart
152 if (colonPos < closeBracketPos) {
153 // If the last colon is inside the brackets, then there must not be a port.
154 if (s.size() != closeBracketPos + 1) {
155 return Status(ErrorCodes::FailedToParse,
156 str::stream() << "missing colon after ']' before the port in "
157 << s.toString());
158 }
159 colonPos = std::string::npos;
160 } else if (colonPos != closeBracketPos + 1) {
161 return Status(ErrorCodes::FailedToParse,
162 str::stream() << "Extraneous characters between ']' and pre-port ':'"
163 << " in "
164 << s.toString());
165 }
166 } else if (closeBracketPos != std::string::npos) {
167 return Status(ErrorCodes::FailedToParse,
168 str::stream() << "']' present without '[' in " << s.toString());
169 } else if (s.find(':') != colonPos) {
170 return Status(ErrorCodes::FailedToParse,
171 str::stream() << "More than one ':' detected. If this is an ipv6 address,"
172 << " it needs to be surrounded by '[' and ']'; "
173 << s.toString());
174 }
175
176 if (hostPart.empty()) {
177 return Status(ErrorCodes::FailedToParse,
178 str::stream() << "Empty host component parsing HostAndPort from \""
179 << escape(s.toString())
180 << "\"");
181 }
182
183 int port;
184 if (colonPos != std::string::npos) {
185 const StringData portPart = s.substr(colonPos + 1);
186 Status status = parseNumberFromStringWithBase(portPart, 10, &port);
187 if (!status.isOK()) {
188 return status;
189 }
190 if (port <= 0 || port > 65535) {
191 return Status(ErrorCodes::FailedToParse,
192 str::stream() << "Port number " << port
193 << " out of range parsing HostAndPort from \""
194 << escape(s.toString())
195 << "\"");
196 }
197 } else {
198 port = -1;
199 }
200 _host = hostPart.toString();
201 _port = port;
202 return Status::OK();
203 }
204
operator <<(std::ostream & os,const HostAndPort & hp)205 std::ostream& operator<<(std::ostream& os, const HostAndPort& hp) {
206 return os << hp.toString();
207 }
208
209 template <typename Allocator>
operator <<(StringBuilderImpl<Allocator> & os,const HostAndPort & hp)210 StringBuilderImpl<Allocator>& operator<<(StringBuilderImpl<Allocator>& os, const HostAndPort& hp) {
211 return os << hp.toString();
212 }
213
214 template StringBuilderImpl<StackAllocator>& operator<<(StringBuilderImpl<StackAllocator>&,
215 const HostAndPort&);
216 template StringBuilderImpl<SharedBufferAllocator>& operator<<(
217 StringBuilderImpl<SharedBufferAllocator>&, const HostAndPort&);
218
219 } // namespace mongo
220
221 MONGO_HASH_NAMESPACE_START
operator ()(const mongo::HostAndPort & host) const222 size_t hash<mongo::HostAndPort>::operator()(const mongo::HostAndPort& host) const {
223 hash<int> intHasher;
224 size_t hash = intHasher(host.port());
225 boost::hash_combine(hash, host.host());
226 return hash;
227 }
228 MONGO_HASH_NAMESPACE_END
229