blob: 405d8c0e559679a83f0ef5dca1072fb27a5a29b4 [file] [log] [blame]
Mark D. Rothd58a9852017-01-18 08:28:57 -08001/*
2 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02003 * Copyright 2016 gRPC authors.
Mark D. Rothd58a9852017-01-18 08:28:57 -08004 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02005 * 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
Mark D. Rothd58a9852017-01-18 08:28:57 -08008 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02009 * http://www.apache.org/licenses/LICENSE-2.0
Mark D. Rothd58a9852017-01-18 08:28:57 -080010 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +020011 * 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.
Mark D. Rothd58a9852017-01-18 08:28:57 -080016 *
17 */
18
Craig Tiller9eb0fde2017-03-31 16:59:30 -070019#include "src/core/ext/filters/client_channel/http_proxy.h"
Mark D. Rothd58a9852017-01-18 08:28:57 -080020
21#include <stdbool.h>
22#include <string.h>
23
24#include <grpc/support/alloc.h>
Ben Sykes02d426e2017-07-11 13:58:42 -070025#include <grpc/support/host_port.h>
Mark D. Rothd58a9852017-01-18 08:28:57 -080026#include <grpc/support/log.h>
27#include <grpc/support/string_util.h>
28
Craig Tiller9eb0fde2017-03-31 16:59:30 -070029#include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
30#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
31#include "src/core/ext/filters/client_channel/uri_parser.h"
Mark D. Rothdc9bee72017-02-07 12:29:14 -080032#include "src/core/lib/channel/channel_args.h"
Yash Tibrewalc62ce802017-07-20 16:48:01 -070033#include "src/core/lib/slice/b64.h"
Mark D. Rothd58a9852017-01-18 08:28:57 -080034#include "src/core/lib/support/env.h"
Yash Tibrewalf7350ea2017-07-19 10:26:41 -070035#include "src/core/lib/support/string.h"
Mark D. Rothd58a9852017-01-18 08:28:57 -080036
Yash Tibrewal78d71252017-07-19 16:33:16 -070037/**
38 * Parses the 'http_proxy' env var and returns the proxy hostname to resolve or
Yash Tibrewalc62ce802017-07-20 16:48:01 -070039 * NULL on error. Also sets 'user_cred' to user credentials if present in the
40 * 'http_proxy' env var, otherwise leaves it unchanged. It is caller's
41 * responsibility to gpr_free user_cred.
Yash Tibrewal78d71252017-07-19 16:33:16 -070042 */
Yash Tibrewalc62ce802017-07-20 16:48:01 -070043static char* get_http_proxy_server(grpc_exec_ctx* exec_ctx, char** user_cred) {
Craig Tiller4782d922017-11-10 09:53:21 -080044 GPR_ASSERT(user_cred != nullptr);
45 char* proxy_name = nullptr;
Mark D. Rothd58a9852017-01-18 08:28:57 -080046 char* uri_str = gpr_getenv("http_proxy");
Craig Tiller4782d922017-11-10 09:53:21 -080047 char** authority_strs = nullptr;
Yash Tibrewal533d1182017-09-18 10:48:22 -070048 size_t authority_nstrs;
Craig Tiller4782d922017-11-10 09:53:21 -080049 if (uri_str == nullptr) return nullptr;
David Garcia Quintasdcb71e02017-03-06 10:16:26 -080050 grpc_uri* uri =
51 grpc_uri_parse(exec_ctx, uri_str, false /* suppress_errors */);
Craig Tiller4782d922017-11-10 09:53:21 -080052 if (uri == nullptr || uri->authority == nullptr) {
Mark D. Rothd58a9852017-01-18 08:28:57 -080053 gpr_log(GPR_ERROR, "cannot parse value of 'http_proxy' env var");
54 goto done;
55 }
56 if (strcmp(uri->scheme, "http") != 0) {
57 gpr_log(GPR_ERROR, "'%s' scheme not supported in proxy URI", uri->scheme);
58 goto done;
59 }
Yash Tibrewal78d71252017-07-19 16:33:16 -070060 /* Split on '@' to separate user credentials from host */
Yash Tibrewal78d71252017-07-19 16:33:16 -070061 gpr_string_split(uri->authority, "@", &authority_strs, &authority_nstrs);
Yash Tibrewala74ea862017-07-19 17:43:30 -070062 GPR_ASSERT(authority_nstrs != 0); /* should have at least 1 string */
Yash Tibrewalc62ce802017-07-20 16:48:01 -070063 if (authority_nstrs == 1) {
64 /* User cred not present in authority */
65 proxy_name = authority_strs[0];
66 } else if (authority_nstrs == 2) {
67 /* User cred found */
68 *user_cred = authority_strs[0];
69 proxy_name = authority_strs[1];
Yash Tibrewald0c1e502017-07-21 12:34:38 -070070 gpr_log(GPR_DEBUG, "userinfo found in proxy URI");
Yash Tibrewalf7350ea2017-07-19 10:26:41 -070071 } else {
Yash Tibrewalc62ce802017-07-20 16:48:01 -070072 /* Bad authority */
73 for (size_t i = 0; i < authority_nstrs; i++) {
74 gpr_free(authority_strs[i]);
75 }
Craig Tiller4782d922017-11-10 09:53:21 -080076 proxy_name = nullptr;
Mark D. Rothd58a9852017-01-18 08:28:57 -080077 }
Yash Tibrewal78d71252017-07-19 16:33:16 -070078 gpr_free(authority_strs);
Mark D. Rothd58a9852017-01-18 08:28:57 -080079done:
80 gpr_free(uri_str);
81 grpc_uri_destroy(uri);
Yash Tibrewal78d71252017-07-19 16:33:16 -070082 return proxy_name;
Mark D. Rothd58a9852017-01-18 08:28:57 -080083}
Mark D. Rothdc9bee72017-02-07 12:29:14 -080084
85static bool proxy_mapper_map_name(grpc_exec_ctx* exec_ctx,
86 grpc_proxy_mapper* mapper,
87 const char* server_uri,
88 const grpc_channel_args* args,
89 char** name_to_resolve,
90 grpc_channel_args** new_args) {
Craig Tiller4782d922017-11-10 09:53:21 -080091 char* user_cred = nullptr;
Yash Tibrewalc62ce802017-07-20 16:48:01 -070092 *name_to_resolve = get_http_proxy_server(exec_ctx, &user_cred);
Craig Tiller4782d922017-11-10 09:53:21 -080093 if (*name_to_resolve == nullptr) return false;
94 char* no_proxy_str = nullptr;
David Garcia Quintasdcb71e02017-03-06 10:16:26 -080095 grpc_uri* uri =
96 grpc_uri_parse(exec_ctx, server_uri, false /* suppress_errors */);
Craig Tiller4782d922017-11-10 09:53:21 -080097 if (uri == nullptr || uri->path[0] == '\0') {
Mark D. Rothdc9bee72017-02-07 12:29:14 -080098 gpr_log(GPR_ERROR,
99 "'http_proxy' environment variable set, but cannot "
100 "parse server URI '%s' -- not using proxy",
101 server_uri);
Mark D. Rothcb799c52017-09-27 17:35:14 +0000102 goto no_use_proxy;
Mark D. Rothdc9bee72017-02-07 12:29:14 -0800103 }
Mark D. Roth38788592017-02-09 09:28:53 -0800104 if (strcmp(uri->scheme, "unix") == 0) {
105 gpr_log(GPR_INFO, "not using proxy for Unix domain socket '%s'",
106 server_uri);
Mark D. Rothcb799c52017-09-27 17:35:14 +0000107 goto no_use_proxy;
Mark D. Roth38788592017-02-09 09:28:53 -0800108 }
Mark D. Roth1c34d1d2017-09-27 12:25:19 -0700109 no_proxy_str = gpr_getenv("no_proxy");
Craig Tiller4782d922017-11-10 09:53:21 -0800110 if (no_proxy_str != nullptr) {
Ben Sykes02d426e2017-07-11 13:58:42 -0700111 static const char* NO_PROXY_SEPARATOR = ",";
112 bool use_proxy = true;
113 char* server_host;
114 char* server_port;
115 if (!gpr_split_host_port(uri->path[0] == '/' ? uri->path + 1 : uri->path,
116 &server_host, &server_port)) {
117 gpr_log(GPR_INFO,
118 "unable to split host and port, not checking no_proxy list for "
119 "host '%s'",
120 server_uri);
121 } else {
122 size_t uri_len = strlen(server_host);
123 char** no_proxy_hosts;
124 size_t num_no_proxy_hosts;
125 gpr_string_split(no_proxy_str, NO_PROXY_SEPARATOR, &no_proxy_hosts,
126 &num_no_proxy_hosts);
127 for (size_t i = 0; i < num_no_proxy_hosts; i++) {
128 char* no_proxy_entry = no_proxy_hosts[i];
129 size_t no_proxy_len = strlen(no_proxy_entry);
130 if (no_proxy_len <= uri_len &&
131 gpr_stricmp(no_proxy_entry, &server_host[uri_len - no_proxy_len]) ==
132 0) {
133 gpr_log(GPR_INFO, "not using proxy for host in no_proxy list '%s'",
134 server_uri);
135 use_proxy = false;
136 break;
137 }
138 }
139 for (size_t i = 0; i < num_no_proxy_hosts; i++) {
140 gpr_free(no_proxy_hosts[i]);
141 }
142 gpr_free(no_proxy_hosts);
143 gpr_free(server_host);
144 gpr_free(server_port);
Mark D. Rothcb799c52017-09-27 17:35:14 +0000145 if (!use_proxy) goto no_use_proxy;
Ben Sykes02d426e2017-07-11 13:58:42 -0700146 }
147 }
Yash Tibrewalf7350ea2017-07-19 10:26:41 -0700148 grpc_arg args_to_add[2];
149 args_to_add[0] = grpc_channel_arg_string_create(
Yash Tibrewal9eb86722017-09-17 23:43:30 -0700150 (char*)GRPC_ARG_HTTP_CONNECT_SERVER,
Mark D. Roth8d5e60b2017-06-09 09:08:23 -0700151 uri->path[0] == '/' ? uri->path + 1 : uri->path);
Craig Tiller4782d922017-11-10 09:53:21 -0800152 if (user_cred != nullptr) {
Yash Tibrewalc62ce802017-07-20 16:48:01 -0700153 /* Use base64 encoding for user credentials as stated in RFC 7617 */
154 char* encoded_user_cred =
Yash Tibrewalc59ef5e2017-07-19 10:40:17 -0700155 grpc_base64_encode(user_cred, strlen(user_cred), 0, 0);
Yash Tibrewalc62ce802017-07-20 16:48:01 -0700156 char* header;
Yash Tibrewalf7350ea2017-07-19 10:26:41 -0700157 gpr_asprintf(&header, "Proxy-Authorization:Basic %s", encoded_user_cred);
158 gpr_free(encoded_user_cred);
Yash Tibrewal9eb86722017-09-17 23:43:30 -0700159 args_to_add[1] = grpc_channel_arg_string_create(
160 (char*)GRPC_ARG_HTTP_CONNECT_HEADERS, header);
Yash Tibrewalf7350ea2017-07-19 10:26:41 -0700161 *new_args = grpc_channel_args_copy_and_add(args, args_to_add, 2);
162 gpr_free(header);
163 } else {
164 *new_args = grpc_channel_args_copy_and_add(args, args_to_add, 1);
165 }
Mark D. Rothdc9bee72017-02-07 12:29:14 -0800166 grpc_uri_destroy(uri);
Mark D. Rothcb799c52017-09-27 17:35:14 +0000167 gpr_free(user_cred);
Mark D. Rothdc9bee72017-02-07 12:29:14 -0800168 return true;
Mark D. Rothcb799c52017-09-27 17:35:14 +0000169no_use_proxy:
Craig Tiller4782d922017-11-10 09:53:21 -0800170 if (uri != nullptr) grpc_uri_destroy(uri);
Mark D. Rothcb799c52017-09-27 17:35:14 +0000171 gpr_free(*name_to_resolve);
Craig Tiller4782d922017-11-10 09:53:21 -0800172 *name_to_resolve = nullptr;
Mark D. Rothcb799c52017-09-27 17:35:14 +0000173 gpr_free(user_cred);
174 return false;
Mark D. Rothdc9bee72017-02-07 12:29:14 -0800175}
176
177static bool proxy_mapper_map_address(grpc_exec_ctx* exec_ctx,
178 grpc_proxy_mapper* mapper,
179 const grpc_resolved_address* address,
180 const grpc_channel_args* args,
181 grpc_resolved_address** new_address,
182 grpc_channel_args** new_args) {
183 return false;
184}
185
186static void proxy_mapper_destroy(grpc_proxy_mapper* mapper) {}
187
188static const grpc_proxy_mapper_vtable proxy_mapper_vtable = {
189 proxy_mapper_map_name, proxy_mapper_map_address, proxy_mapper_destroy};
190
191static grpc_proxy_mapper proxy_mapper = {&proxy_mapper_vtable};
192
193void grpc_register_http_proxy_mapper() {
194 grpc_proxy_mapper_register(true /* at_start */, &proxy_mapper);
195}