Line | Count | Source |
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 | | |
26 | | #include "urldata.h" |
27 | | #include "sendf.h" |
28 | | #include "curl_trc.h" |
29 | | #include "headers.h" |
30 | | #include "strcase.h" |
31 | | |
32 | | #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API) |
33 | | |
34 | | /* Generate the curl_header struct for the user. This function MUST assign all |
35 | | struct fields in the output struct. */ |
36 | | static void copy_header_external(struct Curl_header_store *hs, |
37 | | size_t index, |
38 | | size_t amount, |
39 | | struct Curl_llist_node *e, |
40 | | struct curl_header *hout) |
41 | 257k | { |
42 | 257k | struct curl_header *h = hout; |
43 | 257k | h->name = hs->name; |
44 | 257k | h->value = hs->value; |
45 | 257k | h->amount = amount; |
46 | 257k | h->index = index; |
47 | | /* this will randomly OR a reserved bit for the sole purpose of making it |
48 | | impossible for applications to do == comparisons, as that would otherwise |
49 | | be tempting and then lead to the reserved bits not being reserved |
50 | | anymore. */ |
51 | 257k | h->origin = (unsigned int)(hs->type | (1 << 27)); |
52 | 257k | h->anchor = e; |
53 | 257k | } |
54 | | |
55 | | /* public API */ |
56 | | CURLHcode curl_easy_header(CURL *curl, |
57 | | const char *name, |
58 | | size_t nameindex, |
59 | | unsigned int origin, |
60 | | int request, |
61 | | struct curl_header **hout) |
62 | 241k | { |
63 | 241k | struct Curl_eapi_guard guard; |
64 | 241k | CURLHcode hresult = CURLHE_OK; |
65 | 241k | CURLcode result; |
66 | | |
67 | 241k | if(CURL_EAPI_ENTER(&guard, curl, easy_header, &result)) { |
68 | 241k | struct Curl_easy *data = curl; |
69 | 241k | struct Curl_llist_node *e; |
70 | 241k | struct Curl_llist_node *e_pick = NULL; |
71 | 241k | size_t match = 0; |
72 | 241k | size_t amount = 0; |
73 | 241k | struct Curl_header_store *hs = NULL; |
74 | 241k | struct Curl_header_store *pick = NULL; |
75 | 241k | if(!name || !hout || !data || |
76 | 241k | (origin > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX | |
77 | 241k | CURLH_PSEUDO)) || !origin || (request < -1)) { |
78 | 0 | hresult = CURLHE_BAD_ARGUMENT; |
79 | 0 | goto out; |
80 | 0 | } |
81 | 241k | if(!Curl_llist_count(&data->state.httphdrs)) { |
82 | 157k | hresult = CURLHE_NOHEADERS; /* no headers available */ |
83 | 157k | goto out; |
84 | 157k | } |
85 | 84.6k | if(request > data->state.requests) { |
86 | 0 | hresult = CURLHE_NOREQUEST; |
87 | 0 | goto out; |
88 | 0 | } |
89 | 84.6k | if(request == -1) |
90 | 84.6k | request = data->state.requests; |
91 | | |
92 | | /* we need a first round to count amount of this header */ |
93 | 1.16M | for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { |
94 | 1.08M | hs = Curl_node_elem(e); |
95 | 1.08M | if(curl_strequal(hs->name, name) && |
96 | 57.6k | (hs->type & origin) && |
97 | 57.6k | (hs->request == request)) { |
98 | 56.3k | amount++; |
99 | 56.3k | pick = hs; |
100 | 56.3k | e_pick = e; |
101 | 56.3k | } |
102 | 1.08M | } |
103 | 84.6k | if(!amount) |
104 | 79.1k | hresult = CURLHE_MISSING; |
105 | 5.54k | else if(nameindex >= amount) |
106 | 0 | hresult = CURLHE_BADINDEX; |
107 | 84.6k | if(hresult) |
108 | 79.1k | goto out; |
109 | | |
110 | 5.54k | if(nameindex == amount - 1) |
111 | | /* if the last or only occurrence is what's asked for, then we know it */ |
112 | 4.33k | hs = pick; |
113 | 1.21k | else { |
114 | 18.1k | for(e = Curl_llist_head(&data->state.httphdrs); e; |
115 | 18.1k | e = Curl_node_next(e)) { |
116 | 18.1k | hs = Curl_node_elem(e); |
117 | 18.1k | if(curl_strequal(hs->name, name) && |
118 | 1.82k | (hs->type & origin) && |
119 | 1.82k | (hs->request == request) && |
120 | 1.21k | (match++ == nameindex)) { |
121 | 1.21k | e_pick = e; |
122 | 1.21k | break; |
123 | 1.21k | } |
124 | 18.1k | } |
125 | 1.21k | if(!e) { /* this should not happen */ |
126 | 0 | hresult = CURLHE_MISSING; |
127 | 0 | goto out; |
128 | 0 | } |
129 | 1.21k | } |
130 | | /* this is the name we want */ |
131 | 5.54k | copy_header_external(hs, nameindex, amount, e_pick, |
132 | 5.54k | &data->state.headerout[0]); |
133 | 5.54k | *hout = &data->state.headerout[0]; |
134 | 5.54k | hresult = CURLHE_OK; |
135 | 5.54k | } |
136 | 241k | out: |
137 | 241k | CURL_EAPI_LEAVE(&guard); |
138 | 241k | if(result) |
139 | 0 | hresult = Curl_eapi_hcode(result); |
140 | 241k | return hresult; |
141 | 241k | } |
142 | | |
143 | | struct nextheader_cache_entry { |
144 | | struct Curl_header_store *header; |
145 | | size_t order; |
146 | | }; |
147 | | |
148 | | static int nextheader_compare(const void *p1, const void *p2) |
149 | 2.58M | { |
150 | 2.58M | const struct nextheader_cache_entry *e1 = p1; |
151 | 2.58M | const struct nextheader_cache_entry *e2 = p2; |
152 | 2.58M | const struct Curl_header_store *h1 = e1->header; |
153 | 2.58M | const struct Curl_header_store *h2 = e2->header; |
154 | 2.58M | const char *n1 = h1->name; |
155 | 2.58M | const char *n2 = h2->name; |
156 | | |
157 | 23.9M | while(*n1 && *n2) { |
158 | 22.1M | unsigned char c1 = (unsigned char)Curl_raw_toupper(*n1++); |
159 | 22.1M | unsigned char c2 = (unsigned char)Curl_raw_toupper(*n2++); |
160 | 22.1M | if(c1 != c2) |
161 | 803k | return c1 > c2 ? 1 : -1; |
162 | 22.1M | } |
163 | 1.78M | if(*n1) |
164 | 67.0k | return 1; |
165 | 1.71M | if(*n2) |
166 | 7.97k | return -1; |
167 | 1.70M | if(e1->order != e2->order) |
168 | 1.70M | return e1->order > e2->order ? 1 : -1; |
169 | 0 | return 0; |
170 | 1.70M | } |
171 | | |
172 | | static bool nextheader_cache_build(struct Curl_easy *data, |
173 | | unsigned int origin, |
174 | | int request, size_t count) |
175 | 60.9k | { |
176 | 60.9k | struct nextheader_cache_entry *headers; |
177 | 60.9k | struct Curl_llist_node *e; |
178 | 60.9k | size_t i = 0; |
179 | 60.9k | size_t first; |
180 | | |
181 | 60.9k | headers = curlx_malloc(sizeof(*headers) * count); |
182 | 60.9k | if(!headers) |
183 | 0 | return FALSE; |
184 | | |
185 | 795k | for(e = Curl_llist_head(&data->state.httphdrs); e; |
186 | 734k | e = Curl_node_next(e)) { |
187 | 734k | struct Curl_header_store *hs = Curl_node_elem(e); |
188 | 734k | if((hs->type & origin) && (hs->request == request)) { |
189 | 691k | headers[i].header = hs; |
190 | 691k | headers[i].order = i; |
191 | 691k | i++; |
192 | 691k | } |
193 | 734k | } |
194 | | |
195 | 60.9k | qsort(headers, i, sizeof(*headers), nextheader_compare); |
196 | 216k | for(first = 0; first < i;) { |
197 | 155k | size_t last = first + 1; |
198 | 155k | size_t index; |
199 | 691k | while((last < i) && |
200 | 631k | curl_strequal(headers[first].header->name, |
201 | 631k | headers[last].header->name)) |
202 | 536k | last++; |
203 | 847k | for(index = first; index < last; index++) { |
204 | 691k | headers[index].header->nh_amount = last - first; |
205 | 691k | headers[index].header->nh_index = index - first; |
206 | 691k | } |
207 | 155k | first = last; |
208 | 155k | } |
209 | 60.9k | curlx_free(headers); |
210 | | |
211 | 60.9k | data->state.nh_origin = origin; |
212 | 60.9k | data->state.nh_request = request; |
213 | 60.9k | data->state.nh_count = count; |
214 | 60.9k | return TRUE; |
215 | 60.9k | } |
216 | | |
217 | | /* public API */ |
218 | | struct curl_header *curl_easy_nextheader(CURL *curl, |
219 | | unsigned int origin, |
220 | | int request, |
221 | | struct curl_header *prev) |
222 | 487k | { |
223 | 487k | struct Curl_eapi_guard guard; |
224 | 487k | struct curl_header *hd = NULL; |
225 | 487k | CURLcode result; |
226 | | |
227 | 487k | if(CURL_EAPI_ENTER(&guard, curl, easy_nextheader, &result)) { |
228 | 487k | struct Curl_easy *data = curl; |
229 | 487k | struct Curl_llist_node *pick; |
230 | 487k | struct Curl_llist_node *e; |
231 | 487k | struct Curl_header_store *hs; |
232 | 487k | size_t amount = 0; |
233 | 487k | size_t index = 0; |
234 | 487k | size_t count; |
235 | | |
236 | 487k | if(request > data->state.requests) |
237 | 0 | goto out; |
238 | 487k | if(request == -1) |
239 | 487k | request = data->state.requests; |
240 | | |
241 | 487k | if(prev) { |
242 | 245k | pick = prev->anchor; |
243 | 245k | if(!pick) |
244 | | /* something is wrong */ |
245 | 0 | goto out; |
246 | 245k | pick = Curl_node_next(pick); |
247 | 245k | } |
248 | 241k | else |
249 | 241k | pick = Curl_llist_head(&data->state.httphdrs); |
250 | | |
251 | 487k | if(pick) { |
252 | | /* make sure it is the next header of the desired type */ |
253 | 644k | do { |
254 | 644k | hs = Curl_node_elem(pick); |
255 | 644k | if((hs->type & origin) && (hs->request == request)) |
256 | 251k | break; |
257 | 392k | pick = Curl_node_next(pick); |
258 | 392k | } while(pick); |
259 | 275k | } |
260 | | |
261 | 487k | if(!pick) |
262 | | /* no more headers available */ |
263 | 235k | goto out; |
264 | | |
265 | 251k | hs = Curl_node_elem(pick); |
266 | 251k | count = Curl_llist_count(&data->state.httphdrs); |
267 | | |
268 | 251k | if(((data->state.nh_count == count) && |
269 | 190k | (data->state.nh_origin == origin) && |
270 | 190k | (data->state.nh_request == request)) || |
271 | 251k | nextheader_cache_build(data, origin, request, count)) { |
272 | 251k | amount = hs->nh_amount; |
273 | 251k | index = hs->nh_index; |
274 | 251k | } |
275 | 0 | else { |
276 | | /* count number of occurrences of this name within the mask and figure |
277 | | out the index for the currently selected entry */ |
278 | 0 | for(e = Curl_llist_head(&data->state.httphdrs); e; |
279 | 0 | e = Curl_node_next(e)) { |
280 | 0 | struct Curl_header_store *check = Curl_node_elem(e); |
281 | 0 | if(curl_strequal(hs->name, check->name) && |
282 | 0 | (check->request == request) && |
283 | 0 | (check->type & origin)) |
284 | 0 | amount++; |
285 | 0 | if(e == pick) |
286 | 0 | index = amount - 1; |
287 | 0 | } |
288 | 0 | } |
289 | 251k | copy_header_external(hs, index, amount, pick, |
290 | 251k | &data->state.headerout[1]); |
291 | 251k | hd = &data->state.headerout[1]; |
292 | 251k | } |
293 | 487k | out: |
294 | 487k | CURL_EAPI_LEAVE(&guard); |
295 | 487k | return hd; |
296 | 487k | } |
297 | | |
298 | | static CURLcode namevalue(char *header, size_t hlen, unsigned int type, |
299 | | char **name, char **value) |
300 | 1.63M | { |
301 | 1.63M | char *end = header + hlen - 1; /* point to the last byte */ |
302 | 1.63M | DEBUGASSERT(hlen); |
303 | 1.63M | *name = header; |
304 | | |
305 | 1.63M | if(type == CURLH_PSEUDO) { |
306 | 28.1k | if(*header != ':') |
307 | 0 | return CURLE_BAD_FUNCTION_ARGUMENT; |
308 | 28.1k | header++; |
309 | 28.1k | } |
310 | | |
311 | | /* Find the end of the header name */ |
312 | 20.8M | while(*header && (*header != ':')) |
313 | 19.2M | ++header; |
314 | | |
315 | 1.63M | if(*header) |
316 | | /* Skip over colon, null it */ |
317 | 1.63M | *header++ = 0; |
318 | 4.50k | else |
319 | 4.50k | return CURLE_BAD_FUNCTION_ARGUMENT; |
320 | | |
321 | | /* skip all leading blank letters */ |
322 | 3.09M | while(ISBLANK(*header)) |
323 | 1.46M | header++; |
324 | | |
325 | 1.63M | *value = header; |
326 | | |
327 | | /* skip all trailing space letters */ |
328 | 1.68M | while((end > header) && ISBLANK(*end)) |
329 | 47.9k | *end-- = 0; /* null-terminate */ |
330 | 1.63M | return CURLE_OK; |
331 | 1.63M | } |
332 | | |
333 | | /* |
334 | | * Curl_headers_push() gets passed a full HTTP header to store. It gets called |
335 | | * immediately before the header callback. The header is CRLF, CR or LF |
336 | | * terminated. |
337 | | */ |
338 | | CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, |
339 | | size_t hlen, /* length of header */ |
340 | | unsigned char type) |
341 | 1.90M | { |
342 | 1.90M | char *value = NULL; |
343 | 1.90M | char *name = NULL; |
344 | 1.90M | struct Curl_header_store *hs; |
345 | 1.90M | CURLcode result = CURLE_OUT_OF_MEMORY; |
346 | 1.90M | const size_t ilen = hlen; |
347 | | |
348 | 1.90M | if((header[0] == '\r') || (header[0] == '\n')) |
349 | | /* ignore the body separator */ |
350 | 241k | return CURLE_OK; |
351 | | |
352 | | /* trim off newline characters */ |
353 | 1.66M | if(hlen && (header[hlen - 1] == '\n')) |
354 | 1.60M | hlen--; |
355 | 1.66M | if(hlen && (header[hlen - 1] == '\r')) |
356 | 1.42M | hlen--; |
357 | 1.66M | if(hlen == ilen) |
358 | | /* neither CR nor LF as terminator is not a valid header */ |
359 | 26.4k | return CURLE_WEIRD_SERVER_REPLY; |
360 | | |
361 | 1.63M | if(ISBLANK(header[0])) { |
362 | | /* pass leading blanks */ |
363 | 11.2k | while(hlen && ISBLANK(*header)) { |
364 | 9.40k | header++; |
365 | 9.40k | hlen--; |
366 | 9.40k | } |
367 | 1.86k | if(!hlen) |
368 | 174 | return CURLE_WEIRD_SERVER_REPLY; |
369 | 1.86k | } |
370 | 1.63M | if(Curl_llist_count(&data->state.httphdrs) >= MAX_HTTP_RESP_HEADER_COUNT) { |
371 | 7 | failf(data, "Too many response headers, %d is max", |
372 | 7 | MAX_HTTP_RESP_HEADER_COUNT); |
373 | 7 | return CURLE_TOO_LARGE; |
374 | 7 | } |
375 | | |
376 | 1.63M | hs = curlx_calloc(1, sizeof(*hs) + hlen); |
377 | 1.63M | if(!hs) |
378 | 0 | return CURLE_OUT_OF_MEMORY; |
379 | 1.63M | memcpy(hs->buffer, header, hlen); |
380 | 1.63M | hs->buffer[hlen] = 0; /* null-terminate */ |
381 | | |
382 | 1.63M | result = namevalue(hs->buffer, hlen, type, &name, &value); |
383 | 1.63M | if(!result) { |
384 | 1.63M | hs->name = name; |
385 | 1.63M | hs->value = value; |
386 | 1.63M | hs->type = type; |
387 | 1.63M | hs->request = data->state.requests; |
388 | | |
389 | | /* insert this node into the list of headers */ |
390 | 1.63M | Curl_llist_append(&data->state.httphdrs, hs, &hs->node); |
391 | 1.63M | } |
392 | 4.50k | else { |
393 | 4.50k | failf(data, "Invalid response header"); |
394 | 4.50k | curlx_free(hs); |
395 | 4.50k | } |
396 | 1.63M | return result; |
397 | 1.63M | } |
398 | | |
399 | | /* |
400 | | * Curl_headers_reset(). Reset the headers subsystem. |
401 | | */ |
402 | | static void headers_reset(struct Curl_easy *data) |
403 | 1.59M | { |
404 | 1.59M | Curl_llist_init(&data->state.httphdrs, NULL); |
405 | 1.59M | data->state.nh_count = 0; |
406 | 1.59M | } |
407 | | |
408 | | struct hds_cw_collect_ctx { |
409 | | struct Curl_cwriter super; |
410 | | }; |
411 | | |
412 | | static CURLcode hds_cw_collect_write(struct Curl_easy *data, |
413 | | struct Curl_cwriter *writer, int type, |
414 | | const char *buf, size_t blen) |
415 | 2.44M | { |
416 | 2.44M | if((type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS)) { |
417 | 1.87M | unsigned char htype = (unsigned char) |
418 | 1.87M | (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT : |
419 | 1.87M | (type & CLIENTWRITE_1XX ? CURLH_1XX : |
420 | 1.85M | (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER : |
421 | 1.70M | CURLH_HEADER))); |
422 | 1.87M | CURLcode result = Curl_headers_push(data, buf, blen, htype); |
423 | 1.87M | CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d", |
424 | 1.87M | htype, blen, (int)result); |
425 | 1.87M | if(result) |
426 | 31.1k | return result; |
427 | 1.87M | } |
428 | 2.41M | return Curl_cwriter_write(data, writer->next, type, buf, blen); |
429 | 2.44M | } |
430 | | |
431 | | static const struct Curl_cwtype hds_cw_collect = { |
432 | | "hds-collect", |
433 | | NULL, |
434 | | 0, |
435 | | Curl_cwriter_def_init, |
436 | | hds_cw_collect_write, |
437 | | Curl_cwriter_def_flush, |
438 | | Curl_cwriter_def_close, |
439 | | sizeof(struct hds_cw_collect_ctx) |
440 | | }; |
441 | | |
442 | | CURLcode Curl_headers_init(struct Curl_easy *data) |
443 | 1.06M | { |
444 | 1.06M | struct Curl_cwriter *writer; |
445 | 1.06M | CURLcode result; |
446 | | |
447 | 1.06M | if(data->conn && (data->conn->scheme->protocol & PROTO_FAMILY_HTTP)) { |
448 | | /* avoid installing it twice */ |
449 | 962k | if(Curl_cwriter_get_by_name(data, hds_cw_collect.name)) |
450 | 351k | return CURLE_OK; |
451 | | |
452 | 611k | result = Curl_cwriter_create(&writer, data, &hds_cw_collect, |
453 | 611k | CURL_CW_PROTOCOL); |
454 | 611k | if(result) |
455 | 0 | return result; |
456 | | |
457 | 611k | result = Curl_cwriter_add(data, writer); |
458 | 611k | if(result) { |
459 | 0 | Curl_cwriter_free(data, writer); |
460 | 0 | return result; |
461 | 0 | } |
462 | 611k | } |
463 | 710k | return CURLE_OK; |
464 | 1.06M | } |
465 | | |
466 | | /* |
467 | | * Curl_headers_cleanup(). Free all stored headers and associated memory. |
468 | | */ |
469 | | CURLcode Curl_headers_cleanup(struct Curl_easy *data) |
470 | 1.59M | { |
471 | 1.59M | struct Curl_llist_node *e; |
472 | 1.59M | struct Curl_llist_node *n; |
473 | | |
474 | 3.23M | for(e = Curl_llist_head(&data->state.httphdrs); e; e = n) { |
475 | 1.63M | struct Curl_header_store *hs = Curl_node_elem(e); |
476 | 1.63M | n = Curl_node_next(e); |
477 | 1.63M | curlx_free(hs); |
478 | 1.63M | } |
479 | 1.59M | headers_reset(data); |
480 | 1.59M | return CURLE_OK; |
481 | 1.59M | } |
482 | | |
483 | | #else /* HTTP-disabled builds below */ |
484 | | |
485 | | CURLHcode curl_easy_header(CURL *easy, |
486 | | const char *name, |
487 | | size_t index, |
488 | | unsigned int origin, |
489 | | int request, |
490 | | struct curl_header **hout) |
491 | | { |
492 | | (void)easy; |
493 | | (void)name; |
494 | | (void)index; |
495 | | (void)origin; |
496 | | (void)request; |
497 | | (void)hout; |
498 | | return CURLHE_NOT_BUILT_IN; |
499 | | } |
500 | | |
501 | | struct curl_header *curl_easy_nextheader(CURL *easy, |
502 | | unsigned int type, |
503 | | int request, |
504 | | struct curl_header *prev) |
505 | | { |
506 | | (void)easy; |
507 | | (void)type; |
508 | | (void)request; |
509 | | (void)prev; |
510 | | return NULL; |
511 | | } |
512 | | #endif |