xref: /aosp_15_r20/external/grpc-grpc/src/core/client_channel/http_proxy_mapper.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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