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/ext/filters/client_channel/http_proxy.h" 22 23 #include <stdbool.h> 24 #include <string.h> 25 26 #include <grpc/support/alloc.h> 27 #include <grpc/support/log.h> 28 #include <grpc/support/string_util.h> 29 30 #include "src/core/ext/filters/client_channel/http_connect_handshaker.h" 31 #include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" 32 #include "src/core/ext/filters/client_channel/uri_parser.h" 33 #include "src/core/lib/channel/channel_args.h" 34 #include "src/core/lib/gpr/env.h" 35 #include "src/core/lib/gpr/host_port.h" 36 #include "src/core/lib/gpr/string.h" 37 #include "src/core/lib/slice/b64.h" 38 39 /** 40 * Parses the 'https_proxy' env var (fallback on 'http_proxy') and returns the 41 * proxy hostname to resolve or nullptr on error. Also sets 'user_cred' to user 42 * credentials if present in the 'http_proxy' env var, otherwise leaves it 43 * unchanged. It is caller's responsibility to gpr_free user_cred. 44 */ 45 static char* get_http_proxy_server(char** user_cred) { 46 GPR_ASSERT(user_cred != nullptr); 47 char* proxy_name = nullptr; 48 char** authority_strs = nullptr; 49 size_t authority_nstrs; 50 /* Prefer using 'https_proxy'. Fallback on 'http_proxy' if it is not set. The 51 * fallback behavior can be removed if there's a demand for it. 52 */ 53 char* uri_str = gpr_getenv("https_proxy"); 54 if (uri_str == nullptr) uri_str = gpr_getenv("http_proxy"); 55 if (uri_str == nullptr) return nullptr; 56 grpc_uri* uri = grpc_uri_parse(uri_str, false /* suppress_errors */); 57 if (uri == nullptr || uri->authority == nullptr) { 58 gpr_log(GPR_ERROR, "cannot parse value of 'http_proxy' env var"); 59 goto done; 60 } 61 if (strcmp(uri->scheme, "http") != 0) { 62 gpr_log(GPR_ERROR, "'%s' scheme not supported in proxy URI", uri->scheme); 63 goto done; 64 } 65 /* Split on '@' to separate user credentials from host */ 66 gpr_string_split(uri->authority, "@", &authority_strs, &authority_nstrs); 67 GPR_ASSERT(authority_nstrs != 0); /* should have at least 1 string */ 68 if (authority_nstrs == 1) { 69 /* User cred not present in authority */ 70 proxy_name = authority_strs[0]; 71 } else if (authority_nstrs == 2) { 72 /* User cred found */ 73 *user_cred = authority_strs[0]; 74 proxy_name = authority_strs[1]; 75 gpr_log(GPR_DEBUG, "userinfo found in proxy URI"); 76 } else { 77 /* Bad authority */ 78 for (size_t i = 0; i < authority_nstrs; i++) { 79 gpr_free(authority_strs[i]); 80 } 81 proxy_name = nullptr; 82 } 83 gpr_free(authority_strs); 84 done: 85 gpr_free(uri_str); 86 grpc_uri_destroy(uri); 87 return proxy_name; 88 } 89 90 /** 91 * Checks the value of GRPC_ARG_ENABLE_HTTP_PROXY to determine if http_proxy 92 * should be used. 93 */ 94 bool http_proxy_enabled(const grpc_channel_args* args) { 95 const grpc_arg* arg = 96 grpc_channel_args_find(args, GRPC_ARG_ENABLE_HTTP_PROXY); 97 return grpc_channel_arg_get_bool(arg, true); 98 } 99 100 static bool proxy_mapper_map_name(grpc_proxy_mapper* mapper, 101 const char* server_uri, 102 const grpc_channel_args* args, 103 char** name_to_resolve, 104 grpc_channel_args** new_args) { 105 if (!http_proxy_enabled(args)) { 106 return false; 107 } 108 char* user_cred = nullptr; 109 *name_to_resolve = get_http_proxy_server(&user_cred); 110 if (*name_to_resolve == nullptr) return false; 111 char* no_proxy_str = nullptr; 112 grpc_uri* uri = grpc_uri_parse(server_uri, false /* suppress_errors */); 113 if (uri == nullptr || uri->path[0] == '\0') { 114 gpr_log(GPR_ERROR, 115 "'http_proxy' environment variable set, but cannot " 116 "parse server URI '%s' -- not using proxy", 117 server_uri); 118 goto no_use_proxy; 119 } 120 if (strcmp(uri->scheme, "unix") == 0) { 121 gpr_log(GPR_INFO, "not using proxy for Unix domain socket '%s'", 122 server_uri); 123 goto no_use_proxy; 124 } 125 no_proxy_str = gpr_getenv("no_proxy"); 126 if (no_proxy_str != nullptr) { 127 static const char* NO_PROXY_SEPARATOR = ","; 128 bool use_proxy = true; 129 char* server_host; 130 char* server_port; 131 if (!gpr_split_host_port(uri->path[0] == '/' ? uri->path + 1 : uri->path, 132 &server_host, &server_port)) { 133 gpr_log(GPR_INFO, 134 "unable to split host and port, not checking no_proxy list for " 135 "host '%s'", 136 server_uri); 137 gpr_free(no_proxy_str); 138 } else { 139 size_t uri_len = strlen(server_host); 140 char** no_proxy_hosts; 141 size_t num_no_proxy_hosts; 142 gpr_string_split(no_proxy_str, NO_PROXY_SEPARATOR, &no_proxy_hosts, 143 &num_no_proxy_hosts); 144 for (size_t i = 0; i < num_no_proxy_hosts; i++) { 145 char* no_proxy_entry = no_proxy_hosts[i]; 146 size_t no_proxy_len = strlen(no_proxy_entry); 147 if (no_proxy_len <= uri_len && 148 gpr_stricmp(no_proxy_entry, &server_host[uri_len - no_proxy_len]) == 149 0) { 150 gpr_log(GPR_INFO, "not using proxy for host in no_proxy list '%s'", 151 server_uri); 152 use_proxy = false; 153 break; 154 } 155 } 156 for (size_t i = 0; i < num_no_proxy_hosts; i++) { 157 gpr_free(no_proxy_hosts[i]); 158 } 159 gpr_free(no_proxy_hosts); 160 gpr_free(server_host); 161 gpr_free(server_port); 162 gpr_free(no_proxy_str); 163 if (!use_proxy) goto no_use_proxy; 164 } 165 } 166 grpc_arg args_to_add[2]; 167 args_to_add[0] = grpc_channel_arg_string_create( 168 (char*)GRPC_ARG_HTTP_CONNECT_SERVER, 169 uri->path[0] == '/' ? uri->path + 1 : uri->path); 170 if (user_cred != nullptr) { 171 /* Use base64 encoding for user credentials as stated in RFC 7617 */ 172 char* encoded_user_cred = 173 grpc_base64_encode(user_cred, strlen(user_cred), 0, 0); 174 char* header; 175 gpr_asprintf(&header, "Proxy-Authorization:Basic %s", encoded_user_cred); 176 gpr_free(encoded_user_cred); 177 args_to_add[1] = grpc_channel_arg_string_create( 178 (char*)GRPC_ARG_HTTP_CONNECT_HEADERS, header); 179 *new_args = grpc_channel_args_copy_and_add(args, args_to_add, 2); 180 gpr_free(header); 181 } else { 182 *new_args = grpc_channel_args_copy_and_add(args, args_to_add, 1); 183 } 184 grpc_uri_destroy(uri); 185 gpr_free(user_cred); 186 return true; 187 no_use_proxy: 188 if (uri != nullptr) grpc_uri_destroy(uri); 189 gpr_free(*name_to_resolve); 190 *name_to_resolve = nullptr; 191 gpr_free(user_cred); 192 return false; 193 } 194 195 static bool proxy_mapper_map_address(grpc_proxy_mapper* mapper, 196 const grpc_resolved_address* address, 197 const grpc_channel_args* args, 198 grpc_resolved_address** new_address, 199 grpc_channel_args** new_args) { 200 return false; 201 } 202 203 static void proxy_mapper_destroy(grpc_proxy_mapper* mapper) {} 204 205 static const grpc_proxy_mapper_vtable proxy_mapper_vtable = { 206 proxy_mapper_map_name, proxy_mapper_map_address, proxy_mapper_destroy}; 207 208 static grpc_proxy_mapper proxy_mapper = {&proxy_mapper_vtable}; 209 210 void grpc_register_http_proxy_mapper() { 211 grpc_proxy_mapper_register(true /* at_start */, &proxy_mapper); 212 } 213