1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include "src/core/util/host_port.h"
20
21 #include <grpc/support/port_platform.h>
22 #include <stddef.h>
23
24 #include "absl/log/check.h"
25 #include "absl/strings/str_format.h"
26 #include "absl/strings/string_view.h"
27
28 namespace grpc_core {
29
JoinHostPort(absl::string_view host,int port)30 std::string JoinHostPort(absl::string_view host, int port) {
31 if (!host.empty() && host[0] != '[' && host.rfind(':') != host.npos) {
32 // IPv6 literals must be enclosed in brackets.
33 return absl::StrFormat("[%s]:%d", host, port);
34 }
35 // Ordinary non-bracketed host:port.
36 return absl::StrFormat("%s:%d", host, port);
37 }
38
39 namespace {
DoSplitHostPort(absl::string_view name,absl::string_view * host,absl::string_view * port,bool * has_port)40 bool DoSplitHostPort(absl::string_view name, absl::string_view* host,
41 absl::string_view* port, bool* has_port) {
42 *has_port = false;
43 if (!name.empty() && name[0] == '[') {
44 // Parse a bracketed host, typically an IPv6 literal.
45 const size_t rbracket = name.find(']', 1);
46 if (rbracket == absl::string_view::npos) {
47 // Unmatched [
48 return false;
49 }
50 if (rbracket == name.size() - 1) {
51 // ]<end>
52 *port = absl::string_view();
53 } else if (name[rbracket + 1] == ':') {
54 // ]:<port?>
55 *port = name.substr(rbracket + 2, name.size() - rbracket - 2);
56 *has_port = true;
57 } else {
58 // ]<invalid>
59 return false;
60 }
61 *host = name.substr(1, rbracket - 1);
62 if (host->find(':') == absl::string_view::npos) {
63 // Require all bracketed hosts to contain a colon, because a hostname or
64 // IPv4 address should never use brackets.
65 *host = absl::string_view();
66 return false;
67 }
68 } else {
69 size_t colon = name.find(':');
70 if (colon != absl::string_view::npos &&
71 name.find(':', colon + 1) == absl::string_view::npos) {
72 // Exactly 1 colon. Split into host:port.
73 *host = name.substr(0, colon);
74 *port = name.substr(colon + 1, name.size() - colon - 1);
75 *has_port = true;
76 } else {
77 // 0 or 2+ colons. Bare hostname or IPv6 litearal.
78 *host = name;
79 *port = absl::string_view();
80 }
81 }
82 return true;
83 }
84 } // namespace
85
SplitHostPort(absl::string_view name,absl::string_view * host,absl::string_view * port)86 bool SplitHostPort(absl::string_view name, absl::string_view* host,
87 absl::string_view* port) {
88 bool unused;
89 return DoSplitHostPort(name, host, port, &unused);
90 }
91
SplitHostPort(absl::string_view name,std::string * host,std::string * port)92 bool SplitHostPort(absl::string_view name, std::string* host,
93 std::string* port) {
94 DCHECK(host != nullptr);
95 DCHECK(host->empty());
96 DCHECK(port != nullptr);
97 DCHECK(port->empty());
98 absl::string_view host_view;
99 absl::string_view port_view;
100 bool has_port;
101 const bool ret = DoSplitHostPort(name, &host_view, &port_view, &has_port);
102 if (ret) {
103 // We always set the host, but port is set only when DoSplitHostPort find a
104 // port in the string, to remain backward compatible with the old
105 // gpr_split_host_port API.
106 *host = std::string(host_view);
107 if (has_port) {
108 *port = std::string(port_view);
109 }
110 }
111 return ret;
112 }
113
114 } // namespace grpc_core
115