1 //
2 //
3 // Copyright 2016 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/handshaker/http_connect/http_proxy_mapper.h"
20
21 #include <grpc/impl/channel_arg_names.h>
22 #include <grpc/support/alloc.h>
23 #include <grpc/support/port_platform.h>
24 #include <stdint.h>
25 #include <string.h>
26
27 #include <memory>
28 #include <string>
29 #include <utility>
30
31 #include "absl/log/check.h"
32 #include "absl/log/log.h"
33 #include "absl/status/status.h"
34 #include "absl/status/statusor.h"
35 #include "absl/strings/ascii.h"
36 #include "absl/strings/escaping.h"
37 #include "absl/strings/match.h"
38 #include "absl/strings/numbers.h"
39 #include "absl/strings/str_cat.h"
40 #include "absl/strings/str_split.h"
41 #include "absl/strings/string_view.h"
42 #include "absl/strings/strip.h"
43 #include "absl/types/optional.h"
44 #include "src/core/handshaker/http_connect/http_connect_handshaker.h"
45 #include "src/core/lib/address_utils/parse_address.h"
46 #include "src/core/lib/address_utils/sockaddr_utils.h"
47 #include "src/core/lib/channel/channel_args.h"
48 #include "src/core/lib/iomgr/resolve_address.h"
49 #include "src/core/util/env.h"
50 #include "src/core/util/host_port.h"
51 #include "src/core/util/memory.h"
52 #include "src/core/util/string.h"
53 #include "src/core/util/uri.h"
54
55 namespace grpc_core {
56 namespace {
57
ServerInCIDRRange(const grpc_resolved_address & server_address,absl::string_view cidr_range)58 bool ServerInCIDRRange(const grpc_resolved_address& server_address,
59 absl::string_view cidr_range) {
60 std::pair<absl::string_view, absl::string_view> possible_cidr =
61 absl::StrSplit(cidr_range, absl::MaxSplits('/', 1), absl::SkipEmpty());
62 if (possible_cidr.first.empty() || possible_cidr.second.empty()) {
63 return false;
64 }
65 auto proxy_address = StringToSockaddr(possible_cidr.first, 0);
66 if (!proxy_address.ok()) {
67 return false;
68 }
69 uint32_t mask_bits = 0;
70 if (absl::SimpleAtoi(possible_cidr.second, &mask_bits)) {
71 grpc_sockaddr_mask_bits(&*proxy_address, mask_bits);
72 return grpc_sockaddr_match_subnet(&server_address, &*proxy_address,
73 mask_bits);
74 }
75 return false;
76 }
77
ExactMatchOrSubdomain(absl::string_view host_name,absl::string_view host_name_or_domain)78 bool ExactMatchOrSubdomain(absl::string_view host_name,
79 absl::string_view host_name_or_domain) {
80 return absl::EndsWithIgnoreCase(host_name, host_name_or_domain);
81 }
82
83 // Parses the list of host names, addresses or subnet masks and returns true if
84 // the target address or host matches any value.
AddressIncluded(const absl::optional<grpc_resolved_address> & target_address,absl::string_view host_name,absl::string_view addresses_and_subnets)85 bool AddressIncluded(
86 const absl::optional<grpc_resolved_address>& target_address,
87 absl::string_view host_name, absl::string_view addresses_and_subnets) {
88 for (absl::string_view entry :
89 absl::StrSplit(addresses_and_subnets, ',', absl::SkipEmpty())) {
90 absl::string_view sanitized_entry = absl::StripAsciiWhitespace(entry);
91 if (ExactMatchOrSubdomain(host_name, sanitized_entry) ||
92 (target_address.has_value() &&
93 ServerInCIDRRange(*target_address, sanitized_entry))) {
94 return true;
95 }
96 }
97 return false;
98 }
99
100 ///
101 /// Parses the 'https_proxy' env var (fallback on 'http_proxy') and returns the
102 /// proxy hostname to resolve or nullopt on error. Also sets 'user_cred' to user
103 /// credentials if present in the 'http_proxy' env var, otherwise leaves it
104 /// unchanged.
105 ///
GetHttpProxyServer(const ChannelArgs & args,absl::optional<std::string> * user_cred)106 absl::optional<std::string> GetHttpProxyServer(
107 const ChannelArgs& args, absl::optional<std::string>* user_cred) {
108 CHECK_NE(user_cred, nullptr);
109 absl::StatusOr<URI> uri;
110 // We check the following places to determine the HTTP proxy to use, stopping
111 // at the first one that is set:
112 // 1. GRPC_ARG_HTTP_PROXY channel arg
113 // 2. grpc_proxy environment variable
114 // 3. https_proxy environment variable
115 // 4. http_proxy environment variable
116 // If none of the above are set, then no HTTP proxy will be used.
117 //
118 absl::optional<std::string> uri_str =
119 args.GetOwnedString(GRPC_ARG_HTTP_PROXY);
120 if (!uri_str.has_value()) uri_str = GetEnv("grpc_proxy");
121 if (!uri_str.has_value()) uri_str = GetEnv("https_proxy");
122 if (!uri_str.has_value()) uri_str = GetEnv("http_proxy");
123 if (!uri_str.has_value()) return absl::nullopt;
124 // an empty value means "don't use proxy"
125 if (uri_str->empty()) return absl::nullopt;
126 uri = URI::Parse(*uri_str);
127 if (!uri.ok() || uri->authority().empty()) {
128 LOG(ERROR) << "cannot parse value of 'http_proxy' env var. Error: "
129 << uri.status();
130 return absl::nullopt;
131 }
132 if (uri->scheme() != "http") {
133 LOG(ERROR) << "'" << uri->scheme() << "' scheme not supported in proxy URI";
134 return absl::nullopt;
135 }
136 // Split on '@' to separate user credentials from host
137 char** authority_strs = nullptr;
138 size_t authority_nstrs;
139 gpr_string_split(uri->authority().c_str(), "@", &authority_strs,
140 &authority_nstrs);
141 CHECK_NE(authority_nstrs, 0u); // should have at least 1 string
142 absl::optional<std::string> proxy_name;
143 if (authority_nstrs == 1) {
144 // User cred not present in authority
145 proxy_name = authority_strs[0];
146 } else if (authority_nstrs == 2) {
147 // User cred found
148 *user_cred = authority_strs[0];
149 proxy_name = authority_strs[1];
150 VLOG(2) << "userinfo found in proxy URI";
151 } else {
152 // Bad authority
153 proxy_name = absl::nullopt;
154 }
155 for (size_t i = 0; i < authority_nstrs; i++) {
156 gpr_free(authority_strs[i]);
157 }
158 gpr_free(authority_strs);
159 return proxy_name;
160 }
161
162 // Adds the default port if target does not contain a port.
MaybeAddDefaultPort(absl::string_view target)163 std::string MaybeAddDefaultPort(absl::string_view target) {
164 absl::string_view host;
165 absl::string_view port;
166 SplitHostPort(target, &host, &port);
167 if (port.empty()) {
168 return JoinHostPort(host, kDefaultSecurePortInt);
169 }
170 return std::string(target);
171 }
172
GetChannelArgOrEnvVarValue(const ChannelArgs & args,absl::string_view channel_arg,const char * env_var)173 absl::optional<std::string> GetChannelArgOrEnvVarValue(
174 const ChannelArgs& args, absl::string_view channel_arg,
175 const char* env_var) {
176 auto arg_value = args.GetOwnedString(channel_arg);
177 if (arg_value.has_value()) {
178 return arg_value;
179 }
180 return GetEnv(env_var);
181 }
182
GetAddressProxyServer(const ChannelArgs & args)183 absl::optional<grpc_resolved_address> GetAddressProxyServer(
184 const ChannelArgs& args) {
185 auto address_value = GetChannelArgOrEnvVarValue(
186 args, GRPC_ARG_ADDRESS_HTTP_PROXY, HttpProxyMapper::kAddressProxyEnvVar);
187 if (!address_value.has_value()) {
188 return absl::nullopt;
189 }
190 auto address = StringToSockaddr(*address_value);
191 if (!address.ok()) {
192 LOG(ERROR) << "cannot parse value of '"
193 << std::string(HttpProxyMapper::kAddressProxyEnvVar)
194 << "' env var. Error: " << address.status().ToString();
195 return absl::nullopt;
196 }
197 return *address;
198 }
199
200 } // namespace
201
MapName(absl::string_view server_uri,ChannelArgs * args)202 absl::optional<std::string> HttpProxyMapper::MapName(
203 absl::string_view server_uri, ChannelArgs* args) {
204 if (!args->GetBool(GRPC_ARG_ENABLE_HTTP_PROXY).value_or(true)) {
205 return absl::nullopt;
206 }
207 absl::optional<std::string> user_cred;
208 auto name_to_resolve = GetHttpProxyServer(*args, &user_cred);
209 if (!name_to_resolve.has_value()) return name_to_resolve;
210 absl::StatusOr<URI> uri = URI::Parse(server_uri);
211 if (!uri.ok() || uri->path().empty()) {
212 LOG(ERROR) << "'http_proxy' environment variable set, but cannot "
213 "parse server URI '"
214 << server_uri << "' -- not using proxy. Error: " << uri.status();
215 return absl::nullopt;
216 }
217 if (uri->scheme() == "unix") {
218 VLOG(2) << "not using proxy for Unix domain socket '" << server_uri << "'";
219 return absl::nullopt;
220 }
221 if (uri->scheme() == "vsock") {
222 VLOG(2) << "not using proxy for VSock '" << server_uri << "'";
223 return absl::nullopt;
224 }
225 // Prefer using 'no_grpc_proxy'. Fallback on 'no_proxy' if it is not set.
226 auto no_proxy_str = GetEnv("no_grpc_proxy");
227 if (!no_proxy_str.has_value()) {
228 no_proxy_str = GetEnv("no_proxy");
229 }
230 if (no_proxy_str.has_value()) {
231 std::string server_host;
232 std::string server_port;
233 if (!SplitHostPort(absl::StripPrefix(uri->path(), "/"), &server_host,
234 &server_port)) {
235 VLOG(2) << "unable to split host and port, not checking no_proxy list "
236 "for host '"
237 << server_uri << "'";
238 } else {
239 auto address = StringToSockaddr(server_host, 0);
240 if (AddressIncluded(address.ok()
241 ? absl::optional<grpc_resolved_address>(*address)
242 : absl::nullopt,
243 server_host, *no_proxy_str)) {
244 VLOG(2) << "not using proxy for host in no_proxy list '" << server_uri
245 << "'";
246 return absl::nullopt;
247 }
248 }
249 }
250 *args = args->Set(GRPC_ARG_HTTP_CONNECT_SERVER,
251 MaybeAddDefaultPort(absl::StripPrefix(uri->path(), "/")));
252 if (user_cred.has_value()) {
253 // Use base64 encoding for user credentials as stated in RFC 7617
254 std::string encoded_user_cred = absl::Base64Escape(*user_cred);
255 *args = args->Set(
256 GRPC_ARG_HTTP_CONNECT_HEADERS,
257 absl::StrCat("Proxy-Authorization:Basic ", encoded_user_cred));
258 }
259 return name_to_resolve;
260 }
261
MapAddress(const grpc_resolved_address & address,ChannelArgs * args)262 absl::optional<grpc_resolved_address> HttpProxyMapper::MapAddress(
263 const grpc_resolved_address& address, ChannelArgs* args) {
264 auto proxy_address = GetAddressProxyServer(*args);
265 if (!proxy_address.has_value()) {
266 return absl::nullopt;
267 }
268 auto address_string = grpc_sockaddr_to_string(&address, true);
269 if (!address_string.ok()) {
270 LOG(ERROR) << "Unable to convert address to string: "
271 << address_string.status();
272 return absl::nullopt;
273 }
274 std::string host_name, port;
275 if (!SplitHostPort(*address_string, &host_name, &port)) {
276 LOG(ERROR) << "Address " << *address_string
277 << " cannot be split in host and port";
278 return absl::nullopt;
279 }
280 auto enabled_addresses = GetChannelArgOrEnvVarValue(
281 *args, GRPC_ARG_ADDRESS_HTTP_PROXY_ENABLED_ADDRESSES,
282 kAddressProxyEnabledAddressesEnvVar);
283 if (!enabled_addresses.has_value() ||
284 !AddressIncluded(address, host_name, *enabled_addresses)) {
285 return absl::nullopt;
286 }
287 *args = args->Set(GRPC_ARG_HTTP_CONNECT_SERVER, *address_string);
288 return proxy_address;
289 }
290
RegisterHttpProxyMapper(CoreConfiguration::Builder * builder)291 void RegisterHttpProxyMapper(CoreConfiguration::Builder* builder) {
292 builder->proxy_mapper_registry()->Register(
293 true /* at_start */,
294 std::unique_ptr<ProxyMapperInterface>(new HttpProxyMapper()));
295 }
296
297 } // namespace grpc_core
298