http_proxy.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
  9. *
  10. * This software is licensed as described in the file COPYING, which
  11. * you should have received as part of this distribution. The terms
  12. * are also available at https://curl.se/docs/copyright.html.
  13. *
  14. * You may opt to use, copy, modify, merge, publish, distribute and/or sell
  15. * copies of the Software, and permit persons to whom the Software is
  16. * furnished to do so, under the terms of the COPYING file.
  17. *
  18. * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  19. * KIND, either express or implied.
  20. *
  21. * SPDX-License-Identifier: curl
  22. *
  23. ***************************************************************************/
  24. #include "curl_setup.h"
  25. #include "http_proxy.h"
  26. #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
  27. #include <curl/curl.h>
  28. #include "sendf.h"
  29. #include "http.h"
  30. #include "url.h"
  31. #include "select.h"
  32. #include "progress.h"
  33. #include "cfilters.h"
  34. #include "cf-h1-proxy.h"
  35. #include "cf-h2-proxy.h"
  36. #include "connect.h"
  37. #include "vtls/vtls.h"
  38. #include "transfer.h"
  39. #include "multiif.h"
  40. #include "vauth/vauth.h"
  41. #include "curlx/strparse.h"
  42. /* The last 2 #include files should be in this order */
  43. #include "curl_memory.h"
  44. #include "memdebug.h"
  45. static CURLcode dynhds_add_custom(struct Curl_easy *data,
  46. bool is_connect, int httpversion,
  47. struct dynhds *hds)
  48. {
  49. struct connectdata *conn = data->conn;
  50. struct curl_slist *h[2];
  51. struct curl_slist *headers;
  52. int numlists = 1; /* by default */
  53. int i;
  54. enum Curl_proxy_use proxy;
  55. if(is_connect)
  56. proxy = HEADER_CONNECT;
  57. else
  58. proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy ?
  59. HEADER_PROXY : HEADER_SERVER;
  60. switch(proxy) {
  61. case HEADER_SERVER:
  62. h[0] = data->set.headers;
  63. break;
  64. case HEADER_PROXY:
  65. h[0] = data->set.headers;
  66. if(data->set.sep_headers) {
  67. h[1] = data->set.proxyheaders;
  68. numlists++;
  69. }
  70. break;
  71. case HEADER_CONNECT:
  72. if(data->set.sep_headers)
  73. h[0] = data->set.proxyheaders;
  74. else
  75. h[0] = data->set.headers;
  76. break;
  77. }
  78. /* loop through one or two lists */
  79. for(i = 0; i < numlists; i++) {
  80. for(headers = h[i]; headers; headers = headers->next) {
  81. struct Curl_str name;
  82. const char *value = NULL;
  83. size_t valuelen = 0;
  84. const char *ptr = headers->data;
  85. /* There are 2 quirks in place for custom headers:
  86. * 1. setting only 'name:' to suppress a header from being sent
  87. * 2. setting only 'name;' to send an empty (illegal) header
  88. */
  89. if(!curlx_str_cspn(&ptr, &name, ";:")) {
  90. if(!curlx_str_single(&ptr, ':')) {
  91. curlx_str_passblanks(&ptr);
  92. if(*ptr) {
  93. value = ptr;
  94. valuelen = strlen(value);
  95. }
  96. else {
  97. /* quirk #1, suppress this header */
  98. continue;
  99. }
  100. }
  101. else if(!curlx_str_single(&ptr, ';')) {
  102. curlx_str_passblanks(&ptr);
  103. if(!*ptr) {
  104. /* quirk #2, send an empty header */
  105. value = "";
  106. valuelen = 0;
  107. }
  108. else {
  109. /* this may be used for something else in the future,
  110. * ignore this for now */
  111. continue;
  112. }
  113. }
  114. else
  115. /* neither : nor ; in provided header value. We ignore this
  116. * silently */
  117. continue;
  118. }
  119. else
  120. /* no name, move on */
  121. continue;
  122. DEBUGASSERT(curlx_strlen(&name) && value);
  123. if(data->state.aptr.host &&
  124. /* a Host: header was sent already, do not pass on any custom Host:
  125. header as that will produce *two* in the same request! */
  126. curlx_str_casecompare(&name, "Host"));
  127. else if(data->state.httpreq == HTTPREQ_POST_FORM &&
  128. /* this header (extended by formdata.c) is sent later */
  129. curlx_str_casecompare(&name, "Content-Type"));
  130. else if(data->state.httpreq == HTTPREQ_POST_MIME &&
  131. /* this header is sent later */
  132. curlx_str_casecompare(&name, "Content-Type"));
  133. else if(data->req.authneg &&
  134. /* while doing auth neg, do not allow the custom length since
  135. we will force length zero then */
  136. curlx_str_casecompare(&name, "Content-Length"));
  137. else if((httpversion >= 20) &&
  138. curlx_str_casecompare(&name, "Transfer-Encoding"));
  139. /* HTTP/2 and HTTP/3 do not support chunked requests */
  140. else if((curlx_str_casecompare(&name, "Authorization") ||
  141. curlx_str_casecompare(&name, "Cookie")) &&
  142. /* be careful of sending this potentially sensitive header to
  143. other hosts */
  144. !Curl_auth_allowed_to_host(data));
  145. else {
  146. CURLcode result =
  147. Curl_dynhds_add(hds, curlx_str(&name), curlx_strlen(&name),
  148. value, valuelen);
  149. if(result)
  150. return result;
  151. }
  152. }
  153. }
  154. return CURLE_OK;
  155. }
  156. CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf,
  157. const char **phostname,
  158. int *pport, bool *pipv6_ip)
  159. {
  160. DEBUGASSERT(cf);
  161. DEBUGASSERT(cf->conn);
  162. if(cf->conn->bits.conn_to_host)
  163. *phostname = cf->conn->conn_to_host.name;
  164. else if(cf->sockindex == SECONDARYSOCKET)
  165. *phostname = cf->conn->secondaryhostname;
  166. else
  167. *phostname = cf->conn->host.name;
  168. if(cf->sockindex == SECONDARYSOCKET)
  169. *pport = cf->conn->secondary_port;
  170. else if(cf->conn->bits.conn_to_port)
  171. *pport = cf->conn->conn_to_port;
  172. else
  173. *pport = cf->conn->remote_port;
  174. if(*phostname != cf->conn->host.name)
  175. *pipv6_ip = (strchr(*phostname, ':') != NULL);
  176. else
  177. *pipv6_ip = cf->conn->bits.ipv6_ip;
  178. return CURLE_OK;
  179. }
  180. struct cf_proxy_ctx {
  181. int httpversion; /* HTTP version used to CONNECT */
  182. BIT(sub_filter_installed);
  183. };
  184. CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
  185. struct Curl_cfilter *cf,
  186. struct Curl_easy *data,
  187. int http_version_major)
  188. {
  189. struct cf_proxy_ctx *ctx = cf->ctx;
  190. const char *hostname = NULL;
  191. char *authority = NULL;
  192. int port;
  193. bool ipv6_ip;
  194. CURLcode result;
  195. struct httpreq *req = NULL;
  196. result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
  197. if(result)
  198. goto out;
  199. authority = curl_maprintf("%s%s%s:%d", ipv6_ip ? "[" : "", hostname,
  200. ipv6_ip ?"]" : "", port);
  201. if(!authority) {
  202. result = CURLE_OUT_OF_MEMORY;
  203. goto out;
  204. }
  205. result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1,
  206. NULL, 0, authority, strlen(authority),
  207. NULL, 0);
  208. if(result)
  209. goto out;
  210. /* Setup the proxy-authorization header, if any */
  211. result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET,
  212. req->authority, TRUE);
  213. if(result)
  214. goto out;
  215. /* If user is not overriding Host: header, we add for HTTP/1.x */
  216. if(http_version_major == 1 &&
  217. !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) {
  218. result = Curl_dynhds_cadd(&req->headers, "Host", authority);
  219. if(result)
  220. goto out;
  221. }
  222. if(data->state.aptr.proxyuserpwd) {
  223. result = Curl_dynhds_h1_cadd_line(&req->headers,
  224. data->state.aptr.proxyuserpwd);
  225. if(result)
  226. goto out;
  227. }
  228. if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) &&
  229. data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) {
  230. result = Curl_dynhds_cadd(&req->headers, "User-Agent",
  231. data->set.str[STRING_USERAGENT]);
  232. if(result)
  233. goto out;
  234. }
  235. if(http_version_major == 1 &&
  236. !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) {
  237. result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive");
  238. if(result)
  239. goto out;
  240. }
  241. result = dynhds_add_custom(data, TRUE, ctx->httpversion, &req->headers);
  242. out:
  243. if(result && req) {
  244. Curl_http_req_free(req);
  245. req = NULL;
  246. }
  247. free(authority);
  248. *preq = req;
  249. return result;
  250. }
  251. static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
  252. struct Curl_easy *data,
  253. bool *done)
  254. {
  255. struct cf_proxy_ctx *ctx = cf->ctx;
  256. CURLcode result;
  257. if(cf->connected) {
  258. *done = TRUE;
  259. return CURLE_OK;
  260. }
  261. CURL_TRC_CF(data, cf, "connect");
  262. connect_sub:
  263. result = cf->next->cft->do_connect(cf->next, data, done);
  264. if(result || !*done)
  265. return result;
  266. *done = FALSE;
  267. if(!ctx->sub_filter_installed) {
  268. int httpversion = 0;
  269. const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data);
  270. if(alpn)
  271. infof(data, "CONNECT: '%s' negotiated", alpn);
  272. else
  273. infof(data, "CONNECT: no ALPN negotiated");
  274. if(alpn && !strcmp(alpn, "http/1.0")) {
  275. CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.0");
  276. result = Curl_cf_h1_proxy_insert_after(cf, data);
  277. if(result)
  278. goto out;
  279. httpversion = 10;
  280. }
  281. else if(!alpn || !strcmp(alpn, "http/1.1")) {
  282. CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1");
  283. result = Curl_cf_h1_proxy_insert_after(cf, data);
  284. if(result)
  285. goto out;
  286. /* Assume that without an ALPN, we are talking to an ancient one */
  287. httpversion = 11;
  288. }
  289. #ifdef USE_NGHTTP2
  290. else if(!strcmp(alpn, "h2")) {
  291. CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2");
  292. result = Curl_cf_h2_proxy_insert_after(cf, data);
  293. if(result)
  294. goto out;
  295. httpversion = 20;
  296. }
  297. #endif
  298. else {
  299. failf(data, "CONNECT: negotiated ALPN '%s' not supported", alpn);
  300. result = CURLE_COULDNT_CONNECT;
  301. goto out;
  302. }
  303. ctx->sub_filter_installed = TRUE;
  304. ctx->httpversion = httpversion;
  305. /* after we installed the filter "below" us, we call connect
  306. * on out sub-chain again.
  307. */
  308. goto connect_sub;
  309. }
  310. else {
  311. /* subchain connected and we had already installed the protocol filter.
  312. * This means the protocol tunnel is established, we are done.
  313. */
  314. DEBUGASSERT(ctx->sub_filter_installed);
  315. result = CURLE_OK;
  316. }
  317. out:
  318. if(!result) {
  319. cf->connected = TRUE;
  320. *done = TRUE;
  321. }
  322. return result;
  323. }
  324. CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf,
  325. struct Curl_easy *data,
  326. int query, int *pres1, void *pres2)
  327. {
  328. switch(query) {
  329. case CF_QUERY_HOST_PORT:
  330. *pres1 = (int)cf->conn->http_proxy.port;
  331. *((const char **)pres2) = cf->conn->http_proxy.host.name;
  332. return CURLE_OK;
  333. case CF_QUERY_ALPN_NEGOTIATED: {
  334. const char **palpn = pres2;
  335. DEBUGASSERT(palpn);
  336. *palpn = NULL;
  337. return CURLE_OK;
  338. }
  339. default:
  340. break;
  341. }
  342. return cf->next ?
  343. cf->next->cft->query(cf->next, data, query, pres1, pres2) :
  344. CURLE_UNKNOWN_OPTION;
  345. }
  346. static void http_proxy_cf_destroy(struct Curl_cfilter *cf,
  347. struct Curl_easy *data)
  348. {
  349. struct cf_proxy_ctx *ctx = cf->ctx;
  350. (void)data;
  351. CURL_TRC_CF(data, cf, "destroy");
  352. free(ctx);
  353. }
  354. static void http_proxy_cf_close(struct Curl_cfilter *cf,
  355. struct Curl_easy *data)
  356. {
  357. CURL_TRC_CF(data, cf, "close");
  358. cf->connected = FALSE;
  359. if(cf->next)
  360. cf->next->cft->do_close(cf->next, data);
  361. }
  362. struct Curl_cftype Curl_cft_http_proxy = {
  363. "HTTP-PROXY",
  364. CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
  365. 0,
  366. http_proxy_cf_destroy,
  367. http_proxy_cf_connect,
  368. http_proxy_cf_close,
  369. Curl_cf_def_shutdown,
  370. Curl_cf_def_adjust_pollset,
  371. Curl_cf_def_data_pending,
  372. Curl_cf_def_send,
  373. Curl_cf_def_recv,
  374. Curl_cf_def_cntrl,
  375. Curl_cf_def_conn_is_alive,
  376. Curl_cf_def_conn_keep_alive,
  377. Curl_cf_http_proxy_query,
  378. };
  379. CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
  380. struct Curl_easy *data)
  381. {
  382. struct Curl_cfilter *cf;
  383. struct cf_proxy_ctx *ctx = NULL;
  384. CURLcode result;
  385. (void)data;
  386. ctx = calloc(1, sizeof(*ctx));
  387. if(!ctx) {
  388. result = CURLE_OUT_OF_MEMORY;
  389. goto out;
  390. }
  391. result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx);
  392. if(result)
  393. goto out;
  394. ctx = NULL;
  395. Curl_conn_cf_insert_after(cf_at, cf);
  396. out:
  397. free(ctx);
  398. return result;
  399. }
  400. #endif /* ! CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */