h2h3.c 8.7 KB

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