• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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