http_proxy.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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. #ifdef USE_HYPER
  29. #error #include <hyper.h>
  30. #endif
  31. #include "sendf.h"
  32. #include "http.h"
  33. #include "url.h"
  34. #include "select.h"
  35. #include "progress.h"
  36. #include "cfilters.h"
  37. #include "cf-h1-proxy.h"
  38. #include "cf-h2-proxy.h"
  39. #include "connect.h"
  40. #include "curlx.h"
  41. #include "vtls/vtls.h"
  42. #include "transfer.h"
  43. #include "multiif.h"
  44. /* The last 3 #include files should be in this order */
  45. #include "curl_printf.h"
  46. #include "curl_memory.h"
  47. #include "memdebug.h"
  48. CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf,
  49. const char **phostname,
  50. int *pport, bool *pipv6_ip)
  51. {
  52. DEBUGASSERT(cf);
  53. DEBUGASSERT(cf->conn);
  54. if(cf->conn->bits.conn_to_host)
  55. *phostname = cf->conn->conn_to_host.name;
  56. else if(cf->sockindex == SECONDARYSOCKET)
  57. *phostname = cf->conn->secondaryhostname;
  58. else
  59. *phostname = cf->conn->host.name;
  60. if(cf->sockindex == SECONDARYSOCKET)
  61. *pport = cf->conn->secondary_port;
  62. else if(cf->conn->bits.conn_to_port)
  63. *pport = cf->conn->conn_to_port;
  64. else
  65. *pport = cf->conn->remote_port;
  66. if(*phostname != cf->conn->host.name)
  67. *pipv6_ip = (strchr(*phostname, ':') != NULL);
  68. else
  69. *pipv6_ip = cf->conn->bits.ipv6_ip;
  70. return CURLE_OK;
  71. }
  72. CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
  73. struct Curl_cfilter *cf,
  74. struct Curl_easy *data,
  75. int http_version_major)
  76. {
  77. const char *hostname = NULL;
  78. char *authority = NULL;
  79. int port;
  80. bool ipv6_ip;
  81. CURLcode result;
  82. struct httpreq *req = NULL;
  83. result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
  84. if(result)
  85. goto out;
  86. authority = aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname,
  87. ipv6_ip?"]":"", port);
  88. if(!authority) {
  89. result = CURLE_OUT_OF_MEMORY;
  90. goto out;
  91. }
  92. result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1,
  93. NULL, 0, authority, strlen(authority),
  94. NULL, 0);
  95. if(result)
  96. goto out;
  97. /* Setup the proxy-authorization header, if any */
  98. result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET,
  99. req->authority, TRUE);
  100. if(result)
  101. goto out;
  102. /* If user is not overriding Host: header, we add for HTTP/1.x */
  103. if(http_version_major == 1 &&
  104. !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) {
  105. result = Curl_dynhds_cadd(&req->headers, "Host", authority);
  106. if(result)
  107. goto out;
  108. }
  109. if(data->state.aptr.proxyuserpwd) {
  110. result = Curl_dynhds_h1_cadd_line(&req->headers,
  111. data->state.aptr.proxyuserpwd);
  112. if(result)
  113. goto out;
  114. }
  115. if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent"))
  116. && data->set.str[STRING_USERAGENT]) {
  117. result = Curl_dynhds_cadd(&req->headers, "User-Agent",
  118. data->set.str[STRING_USERAGENT]);
  119. if(result)
  120. goto out;
  121. }
  122. if(http_version_major == 1 &&
  123. !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) {
  124. result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive");
  125. if(result)
  126. goto out;
  127. }
  128. result = Curl_dynhds_add_custom(data, TRUE, &req->headers);
  129. out:
  130. if(result && req) {
  131. Curl_http_req_free(req);
  132. req = NULL;
  133. }
  134. free(authority);
  135. *preq = req;
  136. return result;
  137. }
  138. struct cf_proxy_ctx {
  139. /* the protocol specific sub-filter we install during connect */
  140. struct Curl_cfilter *cf_protocol;
  141. };
  142. static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
  143. struct Curl_easy *data,
  144. bool blocking, bool *done)
  145. {
  146. struct cf_proxy_ctx *ctx = cf->ctx;
  147. CURLcode result;
  148. if(cf->connected) {
  149. *done = TRUE;
  150. return CURLE_OK;
  151. }
  152. CURL_TRC_CF(data, cf, "connect");
  153. connect_sub:
  154. result = cf->next->cft->do_connect(cf->next, data, blocking, done);
  155. if(result || !*done)
  156. return result;
  157. *done = FALSE;
  158. if(!ctx->cf_protocol) {
  159. struct Curl_cfilter *cf_protocol = NULL;
  160. int alpn = Curl_conn_cf_is_ssl(cf->next)?
  161. cf->conn->proxy_alpn : CURL_HTTP_VERSION_1_1;
  162. /* First time call after the subchain connected */
  163. switch(alpn) {
  164. case CURL_HTTP_VERSION_NONE:
  165. case CURL_HTTP_VERSION_1_0:
  166. case CURL_HTTP_VERSION_1_1:
  167. CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1");
  168. infof(data, "CONNECT tunnel: HTTP/1.%d negotiated",
  169. (alpn == CURL_HTTP_VERSION_1_0)? 0 : 1);
  170. result = Curl_cf_h1_proxy_insert_after(cf, data);
  171. if(result)
  172. goto out;
  173. cf_protocol = cf->next;
  174. break;
  175. #ifdef USE_NGHTTP2
  176. case CURL_HTTP_VERSION_2:
  177. CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2");
  178. infof(data, "CONNECT tunnel: HTTP/2 negotiated");
  179. result = Curl_cf_h2_proxy_insert_after(cf, data);
  180. if(result)
  181. goto out;
  182. cf_protocol = cf->next;
  183. break;
  184. #endif
  185. default:
  186. infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn);
  187. result = CURLE_COULDNT_CONNECT;
  188. goto out;
  189. }
  190. ctx->cf_protocol = cf_protocol;
  191. /* after we installed the filter "below" us, we call connect
  192. * on out sub-chain again.
  193. */
  194. goto connect_sub;
  195. }
  196. else {
  197. /* subchain connected and we had already installed the protocol filter.
  198. * This means the protocol tunnel is established, we are done.
  199. */
  200. DEBUGASSERT(ctx->cf_protocol);
  201. result = CURLE_OK;
  202. }
  203. out:
  204. if(!result) {
  205. cf->connected = TRUE;
  206. *done = TRUE;
  207. }
  208. return result;
  209. }
  210. void Curl_cf_http_proxy_get_host(struct Curl_cfilter *cf,
  211. struct Curl_easy *data,
  212. const char **phost,
  213. const char **pdisplay_host,
  214. int *pport)
  215. {
  216. (void)data;
  217. if(!cf->connected) {
  218. *phost = cf->conn->http_proxy.host.name;
  219. *pdisplay_host = cf->conn->http_proxy.host.dispname;
  220. *pport = (int)cf->conn->http_proxy.port;
  221. }
  222. else {
  223. cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
  224. }
  225. }
  226. static void http_proxy_cf_destroy(struct Curl_cfilter *cf,
  227. struct Curl_easy *data)
  228. {
  229. struct cf_proxy_ctx *ctx = cf->ctx;
  230. (void)data;
  231. CURL_TRC_CF(data, cf, "destroy");
  232. free(ctx);
  233. }
  234. static void http_proxy_cf_close(struct Curl_cfilter *cf,
  235. struct Curl_easy *data)
  236. {
  237. struct cf_proxy_ctx *ctx = cf->ctx;
  238. CURL_TRC_CF(data, cf, "close");
  239. cf->connected = FALSE;
  240. if(ctx->cf_protocol) {
  241. struct Curl_cfilter *f;
  242. /* if someone already removed it, we assume he also
  243. * took care of destroying it. */
  244. for(f = cf->next; f; f = f->next) {
  245. if(f == ctx->cf_protocol) {
  246. /* still in our sub-chain */
  247. Curl_conn_cf_discard_sub(cf, ctx->cf_protocol, data, FALSE);
  248. break;
  249. }
  250. }
  251. ctx->cf_protocol = NULL;
  252. }
  253. if(cf->next)
  254. cf->next->cft->do_close(cf->next, data);
  255. }
  256. struct Curl_cftype Curl_cft_http_proxy = {
  257. "HTTP-PROXY",
  258. CF_TYPE_IP_CONNECT,
  259. 0,
  260. http_proxy_cf_destroy,
  261. http_proxy_cf_connect,
  262. http_proxy_cf_close,
  263. Curl_cf_http_proxy_get_host,
  264. Curl_cf_def_adjust_pollset,
  265. Curl_cf_def_data_pending,
  266. Curl_cf_def_send,
  267. Curl_cf_def_recv,
  268. Curl_cf_def_cntrl,
  269. Curl_cf_def_conn_is_alive,
  270. Curl_cf_def_conn_keep_alive,
  271. Curl_cf_def_query,
  272. };
  273. CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
  274. struct Curl_easy *data)
  275. {
  276. struct Curl_cfilter *cf;
  277. struct cf_proxy_ctx *ctx = NULL;
  278. CURLcode result;
  279. (void)data;
  280. ctx = calloc(1, sizeof(*ctx));
  281. if(!ctx) {
  282. result = CURLE_OUT_OF_MEMORY;
  283. goto out;
  284. }
  285. result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx);
  286. if(result)
  287. goto out;
  288. ctx = NULL;
  289. Curl_conn_cf_insert_after(cf_at, cf);
  290. out:
  291. free(ctx);
  292. return result;
  293. }
  294. #endif /* ! CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */