h2h3.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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 "urldata.h"
  26. #include "h2h3.h"
  27. #include "transfer.h"
  28. #include "sendf.h"
  29. #include "strcase.h"
  30. /* The last 3 #include files should be in this order */
  31. #include "curl_printf.h"
  32. #include "curl_memory.h"
  33. #include "memdebug.h"
  34. /*
  35. * Curl_pseudo_headers() creates the array with pseudo headers to be
  36. * used in an HTTP/2 or HTTP/3 request.
  37. */
  38. #if defined(USE_NGHTTP2) || defined(ENABLE_QUIC)
  39. /* Index where :authority header field will appear in request header
  40. field list. */
  41. #define AUTHORITY_DST_IDX 3
  42. /* USHRT_MAX is 65535 == 0xffff */
  43. #define HEADER_OVERFLOW(x) \
  44. (x.namelen > 0xffff || x.valuelen > 0xffff - x.namelen)
  45. /*
  46. * Check header memory for the token "trailers".
  47. * Parse the tokens as separated by comma and surrounded by whitespace.
  48. * Returns TRUE if found or FALSE if not.
  49. */
  50. static bool contains_trailers(const char *p, size_t len)
  51. {
  52. const char *end = p + len;
  53. for(;;) {
  54. for(; p != end && (*p == ' ' || *p == '\t'); ++p)
  55. ;
  56. if(p == end || (size_t)(end - p) < sizeof("trailers") - 1)
  57. return FALSE;
  58. if(strncasecompare("trailers", p, sizeof("trailers") - 1)) {
  59. p += sizeof("trailers") - 1;
  60. for(; p != end && (*p == ' ' || *p == '\t'); ++p)
  61. ;
  62. if(p == end || *p == ',')
  63. return TRUE;
  64. }
  65. /* skip to next token */
  66. for(; p != end && *p != ','; ++p)
  67. ;
  68. if(p == end)
  69. return FALSE;
  70. ++p;
  71. }
  72. }
  73. typedef enum {
  74. /* Send header to server */
  75. HEADERINST_FORWARD,
  76. /* Don't send header to server */
  77. HEADERINST_IGNORE,
  78. /* Discard header, and replace it with "te: trailers" */
  79. HEADERINST_TE_TRAILERS
  80. } header_instruction;
  81. /* Decides how to treat given header field. */
  82. static header_instruction inspect_header(const char *name, size_t namelen,
  83. const char *value, size_t valuelen) {
  84. switch(namelen) {
  85. case 2:
  86. if(!strncasecompare("te", name, namelen))
  87. return HEADERINST_FORWARD;
  88. return contains_trailers(value, valuelen) ?
  89. HEADERINST_TE_TRAILERS : HEADERINST_IGNORE;
  90. case 7:
  91. return strncasecompare("upgrade", name, namelen) ?
  92. HEADERINST_IGNORE : HEADERINST_FORWARD;
  93. case 10:
  94. return (strncasecompare("connection", name, namelen) ||
  95. strncasecompare("keep-alive", name, namelen)) ?
  96. HEADERINST_IGNORE : HEADERINST_FORWARD;
  97. case 16:
  98. return strncasecompare("proxy-connection", name, namelen) ?
  99. HEADERINST_IGNORE : HEADERINST_FORWARD;
  100. case 17:
  101. return strncasecompare("transfer-encoding", name, namelen) ?
  102. HEADERINST_IGNORE : HEADERINST_FORWARD;
  103. default:
  104. return HEADERINST_FORWARD;
  105. }
  106. }
  107. CURLcode Curl_pseudo_headers(struct Curl_easy *data,
  108. const char *mem, /* the request */
  109. const size_t len /* size of request */,
  110. size_t* hdrlen /* opt size of headers read */,
  111. struct h2h3req **hp)
  112. {
  113. struct connectdata *conn = data->conn;
  114. size_t nheader = 0;
  115. size_t i;
  116. size_t authority_idx;
  117. char *hdbuf = (char *)mem;
  118. char *end, *line_end;
  119. struct h2h3pseudo *nva = NULL;
  120. struct h2h3req *hreq = NULL;
  121. char *vptr;
  122. /* Calculate number of headers contained in [mem, mem + len). Assumes a
  123. correctly generated HTTP header field block. */
  124. for(i = 1; i < len; ++i) {
  125. if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
  126. ++nheader;
  127. ++i;
  128. }
  129. }
  130. if(nheader < 2) {
  131. goto fail;
  132. }
  133. /* We counted additional 2 \r\n in the first and last line. We need 3
  134. new headers: :method, :path and :scheme. Therefore we need one
  135. more space. */
  136. nheader += 1;
  137. hreq = malloc(sizeof(struct h2h3req) +
  138. sizeof(struct h2h3pseudo) * (nheader - 1));
  139. if(!hreq) {
  140. goto fail;
  141. }
  142. nva = &hreq->header[0];
  143. /* Extract :method, :path from request line
  144. We do line endings with CRLF so checking for CR is enough */
  145. line_end = memchr(hdbuf, '\r', len);
  146. if(!line_end) {
  147. goto fail;
  148. }
  149. /* Method does not contain spaces */
  150. end = memchr(hdbuf, ' ', line_end - hdbuf);
  151. if(!end || end == hdbuf)
  152. goto fail;
  153. nva[0].name = H2H3_PSEUDO_METHOD;
  154. nva[0].namelen = sizeof(H2H3_PSEUDO_METHOD) - 1;
  155. nva[0].value = hdbuf;
  156. nva[0].valuelen = (size_t)(end - hdbuf);
  157. hdbuf = end + 1;
  158. /* Path may contain spaces so scan backwards */
  159. end = NULL;
  160. for(i = (size_t)(line_end - hdbuf); i; --i) {
  161. if(hdbuf[i - 1] == ' ') {
  162. end = &hdbuf[i - 1];
  163. break;
  164. }
  165. }
  166. if(!end || end == hdbuf)
  167. goto fail;
  168. nva[1].name = H2H3_PSEUDO_PATH;
  169. nva[1].namelen = sizeof(H2H3_PSEUDO_PATH) - 1;
  170. nva[1].value = hdbuf;
  171. nva[1].valuelen = (end - hdbuf);
  172. nva[2].name = H2H3_PSEUDO_SCHEME;
  173. nva[2].namelen = sizeof(H2H3_PSEUDO_SCHEME) - 1;
  174. vptr = Curl_checkheaders(data, STRCONST(H2H3_PSEUDO_SCHEME));
  175. if(vptr) {
  176. vptr += sizeof(H2H3_PSEUDO_SCHEME);
  177. while(*vptr && ISBLANK(*vptr))
  178. vptr++;
  179. nva[2].value = vptr;
  180. infof(data, "set pseudo header %s to %s", H2H3_PSEUDO_SCHEME, vptr);
  181. }
  182. else {
  183. if(conn->handler->flags & PROTOPT_SSL)
  184. nva[2].value = "https";
  185. else
  186. nva[2].value = "http";
  187. }
  188. nva[2].valuelen = strlen((char *)nva[2].value);
  189. authority_idx = 0;
  190. i = 3;
  191. while(i < nheader) {
  192. size_t hlen;
  193. hdbuf = line_end + 2;
  194. /* check for next CR, but only within the piece of data left in the given
  195. buffer */
  196. line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem));
  197. if(!line_end || (line_end == hdbuf))
  198. goto fail;
  199. /* header continuation lines are not supported */
  200. if(*hdbuf == ' ' || *hdbuf == '\t')
  201. goto fail;
  202. for(end = hdbuf; end < line_end && *end != ':'; ++end)
  203. ;
  204. if(end == hdbuf || end == line_end)
  205. goto fail;
  206. hlen = end - hdbuf;
  207. if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
  208. authority_idx = i;
  209. nva[i].name = H2H3_PSEUDO_AUTHORITY;
  210. nva[i].namelen = sizeof(H2H3_PSEUDO_AUTHORITY) - 1;
  211. }
  212. else {
  213. nva[i].namelen = (size_t)(end - hdbuf);
  214. /* Lower case the header name for HTTP/3 */
  215. Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen);
  216. nva[i].name = hdbuf;
  217. }
  218. hdbuf = end + 1;
  219. while(*hdbuf == ' ' || *hdbuf == '\t')
  220. ++hdbuf;
  221. end = line_end;
  222. switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
  223. end - hdbuf)) {
  224. case HEADERINST_IGNORE:
  225. /* skip header fields prohibited by HTTP/2 specification. */
  226. --nheader;
  227. continue;
  228. case HEADERINST_TE_TRAILERS:
  229. nva[i].value = "trailers";
  230. nva[i].valuelen = sizeof("trailers") - 1;
  231. break;
  232. default:
  233. nva[i].value = hdbuf;
  234. nva[i].valuelen = (end - hdbuf);
  235. }
  236. ++i;
  237. }
  238. /* :authority must come before non-pseudo header fields */
  239. if(authority_idx && authority_idx != AUTHORITY_DST_IDX) {
  240. struct h2h3pseudo authority = nva[authority_idx];
  241. for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
  242. nva[i] = nva[i - 1];
  243. }
  244. nva[i] = authority;
  245. }
  246. /* Warn stream may be rejected if cumulative length of headers is too
  247. large. */
  248. #define MAX_ACC 60000 /* <64KB to account for some overhead */
  249. {
  250. size_t acc = 0;
  251. for(i = 0; i < nheader; ++i) {
  252. acc += nva[i].namelen + nva[i].valuelen;
  253. infof(data, "h2h3 [%.*s: %.*s]",
  254. (int)nva[i].namelen, nva[i].name,
  255. (int)nva[i].valuelen, nva[i].value);
  256. }
  257. if(acc > MAX_ACC) {
  258. infof(data, "http_request: Warning: The cumulative length of all "
  259. "headers exceeds %d bytes and that could cause the "
  260. "stream to be rejected.", MAX_ACC);
  261. }
  262. }
  263. if(hdrlen) {
  264. /* Skip trailing CRLF */
  265. end += 4;
  266. *hdrlen = end - mem;
  267. }
  268. hreq->entries = nheader;
  269. *hp = hreq;
  270. return CURLE_OK;
  271. fail:
  272. free(hreq);
  273. return CURLE_OUT_OF_MEMORY;
  274. }
  275. void Curl_pseudo_free(struct h2h3req *hp)
  276. {
  277. free(hp);
  278. }
  279. #endif /* USE_NGHTTP2 or HTTP/3 enabled */