h2h3.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) 1998 - 2022, 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. struct h2h3req **hp)
  111. {
  112. struct connectdata *conn = data->conn;
  113. size_t nheader = 0;
  114. size_t i;
  115. size_t authority_idx;
  116. char *hdbuf = (char *)mem;
  117. char *end, *line_end;
  118. struct h2h3pseudo *nva = NULL;
  119. struct h2h3req *hreq = NULL;
  120. char *vptr;
  121. /* Calculate number of headers contained in [mem, mem + len). Assumes a
  122. correctly generated HTTP header field block. */
  123. for(i = 1; i < len; ++i) {
  124. if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
  125. ++nheader;
  126. ++i;
  127. }
  128. }
  129. if(nheader < 2) {
  130. goto fail;
  131. }
  132. /* We counted additional 2 \r\n in the first and last line. We need 3
  133. new headers: :method, :path and :scheme. Therefore we need one
  134. more space. */
  135. nheader += 1;
  136. hreq = malloc(sizeof(struct h2h3req) +
  137. sizeof(struct h2h3pseudo) * (nheader - 1));
  138. if(!hreq) {
  139. goto fail;
  140. }
  141. nva = &hreq->header[0];
  142. /* Extract :method, :path from request line
  143. We do line endings with CRLF so checking for CR is enough */
  144. line_end = memchr(hdbuf, '\r', len);
  145. if(!line_end) {
  146. goto fail;
  147. }
  148. /* Method does not contain spaces */
  149. end = memchr(hdbuf, ' ', line_end - hdbuf);
  150. if(!end || end == hdbuf)
  151. goto fail;
  152. nva[0].name = H2H3_PSEUDO_METHOD;
  153. nva[0].namelen = sizeof(H2H3_PSEUDO_METHOD) - 1;
  154. nva[0].value = hdbuf;
  155. nva[0].valuelen = (size_t)(end - hdbuf);
  156. hdbuf = end + 1;
  157. /* Path may contain spaces so scan backwards */
  158. end = NULL;
  159. for(i = (size_t)(line_end - hdbuf); i; --i) {
  160. if(hdbuf[i - 1] == ' ') {
  161. end = &hdbuf[i - 1];
  162. break;
  163. }
  164. }
  165. if(!end || end == hdbuf)
  166. goto fail;
  167. nva[1].name = H2H3_PSEUDO_PATH;
  168. nva[1].namelen = sizeof(H2H3_PSEUDO_PATH) - 1;
  169. nva[1].value = hdbuf;
  170. nva[1].valuelen = (end - hdbuf);
  171. nva[2].name = H2H3_PSEUDO_SCHEME;
  172. nva[2].namelen = sizeof(H2H3_PSEUDO_SCHEME) - 1;
  173. vptr = Curl_checkheaders(data, STRCONST(H2H3_PSEUDO_SCHEME));
  174. if(vptr) {
  175. vptr += sizeof(H2H3_PSEUDO_SCHEME);
  176. while(*vptr && ISBLANK(*vptr))
  177. vptr++;
  178. nva[2].value = vptr;
  179. infof(data, "set pseudo header %s to %s", H2H3_PSEUDO_SCHEME, vptr);
  180. }
  181. else {
  182. if(conn->handler->flags & PROTOPT_SSL)
  183. nva[2].value = "https";
  184. else
  185. nva[2].value = "http";
  186. }
  187. nva[2].valuelen = strlen((char *)nva[2].value);
  188. authority_idx = 0;
  189. i = 3;
  190. while(i < nheader) {
  191. size_t hlen;
  192. hdbuf = line_end + 2;
  193. /* check for next CR, but only within the piece of data left in the given
  194. buffer */
  195. line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem));
  196. if(!line_end || (line_end == hdbuf))
  197. goto fail;
  198. /* header continuation lines are not supported */
  199. if(*hdbuf == ' ' || *hdbuf == '\t')
  200. goto fail;
  201. for(end = hdbuf; end < line_end && *end != ':'; ++end)
  202. ;
  203. if(end == hdbuf || end == line_end)
  204. goto fail;
  205. hlen = end - hdbuf;
  206. if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
  207. authority_idx = i;
  208. nva[i].name = H2H3_PSEUDO_AUTHORITY;
  209. nva[i].namelen = sizeof(H2H3_PSEUDO_AUTHORITY) - 1;
  210. }
  211. else {
  212. nva[i].namelen = (size_t)(end - hdbuf);
  213. /* Lower case the header name for HTTP/3 */
  214. Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen);
  215. nva[i].name = hdbuf;
  216. }
  217. hdbuf = end + 1;
  218. while(*hdbuf == ' ' || *hdbuf == '\t')
  219. ++hdbuf;
  220. end = line_end;
  221. switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
  222. end - hdbuf)) {
  223. case HEADERINST_IGNORE:
  224. /* skip header fields prohibited by HTTP/2 specification. */
  225. --nheader;
  226. continue;
  227. case HEADERINST_TE_TRAILERS:
  228. nva[i].value = "trailers";
  229. nva[i].valuelen = sizeof("trailers") - 1;
  230. break;
  231. default:
  232. nva[i].value = hdbuf;
  233. nva[i].valuelen = (end - hdbuf);
  234. }
  235. ++i;
  236. }
  237. /* :authority must come before non-pseudo header fields */
  238. if(authority_idx && authority_idx != AUTHORITY_DST_IDX) {
  239. struct h2h3pseudo authority = nva[authority_idx];
  240. for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
  241. nva[i] = nva[i - 1];
  242. }
  243. nva[i] = authority;
  244. }
  245. /* Warn stream may be rejected if cumulative length of headers is too
  246. large. */
  247. #define MAX_ACC 60000 /* <64KB to account for some overhead */
  248. {
  249. size_t acc = 0;
  250. for(i = 0; i < nheader; ++i) {
  251. acc += nva[i].namelen + nva[i].valuelen;
  252. infof(data, "h2h3 [%.*s: %.*s]",
  253. (int)nva[i].namelen, nva[i].name,
  254. (int)nva[i].valuelen, nva[i].value);
  255. }
  256. if(acc > MAX_ACC) {
  257. infof(data, "http_request: Warning: The cumulative length of all "
  258. "headers exceeds %d bytes and that could cause the "
  259. "stream to be rejected.", MAX_ACC);
  260. }
  261. }
  262. hreq->entries = nheader;
  263. *hp = hreq;
  264. return CURLE_OK;
  265. fail:
  266. free(hreq);
  267. return CURLE_OUT_OF_MEMORY;
  268. }
  269. void Curl_pseudo_free(struct h2h3req *hp)
  270. {
  271. free(hp);
  272. }
  273. #endif /* USE_NGHTTP2 or HTTP/3 enabled */