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