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