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
25 #include "curl_setup.h"
26
27 #include "urldata.h"
28 #include "strdup.h"
29 #include "strcase.h"
30 #include "sendf.h"
31 #include "headers.h"
32
33 /* The last 3 #include files should be in this order */
34 #include "curl_printf.h"
35 #include "curl_memory.h"
36 #include "memdebug.h"
37
38 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
39
40 /* Generate the curl_header struct for the user. This function MUST assign all
41 struct fields in the output struct. */
copy_header_external(struct Curl_header_store * hs,size_t index,size_t amount,struct Curl_llist_element * e,struct curl_header * hout)42 static void copy_header_external(struct Curl_header_store *hs,
43 size_t index,
44 size_t amount,
45 struct Curl_llist_element *e,
46 struct curl_header *hout)
47 {
48 struct curl_header *h = hout;
49 h->name = hs->name;
50 h->value = hs->value;
51 h->amount = amount;
52 h->index = index;
53 /* this will randomly OR a reserved bit for the sole purpose of making it
54 impossible for applications to do == comparisons, as that would otherwise
55 be very tempting and then lead to the reserved bits not being reserved
56 anymore. */
57 h->origin = hs->type | (1<<27);
58 h->anchor = e;
59 }
60
61 /* public API */
curl_easy_header(CURL * easy,const char * name,size_t nameindex,unsigned int type,int request,struct curl_header ** hout)62 CURLHcode curl_easy_header(CURL *easy,
63 const char *name,
64 size_t nameindex,
65 unsigned int type,
66 int request,
67 struct curl_header **hout)
68 {
69 struct Curl_llist_element *e;
70 struct Curl_llist_element *e_pick = NULL;
71 struct Curl_easy *data = easy;
72 size_t match = 0;
73 size_t amount = 0;
74 struct Curl_header_store *hs = NULL;
75 struct Curl_header_store *pick = NULL;
76 if(!name || !hout || !data ||
77 (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX|
78 CURLH_PSEUDO)) || !type || (request < -1))
79 return CURLHE_BAD_ARGUMENT;
80 if(!Curl_llist_count(&data->state.httphdrs))
81 return CURLHE_NOHEADERS; /* no headers available */
82 if(request > data->state.requests)
83 return CURLHE_NOREQUEST;
84 if(request == -1)
85 request = data->state.requests;
86
87 /* we need a first round to count amount of this header */
88 for(e = data->state.httphdrs.head; e; e = e->next) {
89 hs = e->ptr;
90 if(strcasecompare(hs->name, name) &&
91 (hs->type & type) &&
92 (hs->request == request)) {
93 amount++;
94 pick = hs;
95 e_pick = e;
96 }
97 }
98 if(!amount)
99 return CURLHE_MISSING;
100 else if(nameindex >= amount)
101 return CURLHE_BADINDEX;
102
103 if(nameindex == amount - 1)
104 /* if the last or only occurrence is what's asked for, then we know it */
105 hs = pick;
106 else {
107 for(e = data->state.httphdrs.head; e; e = e->next) {
108 hs = e->ptr;
109 if(strcasecompare(hs->name, name) &&
110 (hs->type & type) &&
111 (hs->request == request) &&
112 (match++ == nameindex)) {
113 e_pick = e;
114 break;
115 }
116 }
117 if(!e) /* this shouldn't happen */
118 return CURLHE_MISSING;
119 }
120 /* this is the name we want */
121 copy_header_external(hs, nameindex, amount, e_pick,
122 &data->state.headerout[0]);
123 *hout = &data->state.headerout[0];
124 return CURLHE_OK;
125 }
126
127 /* public API */
curl_easy_nextheader(CURL * easy,unsigned int type,int request,struct curl_header * prev)128 struct curl_header *curl_easy_nextheader(CURL *easy,
129 unsigned int type,
130 int request,
131 struct curl_header *prev)
132 {
133 struct Curl_easy *data = easy;
134 struct Curl_llist_element *pick;
135 struct Curl_llist_element *e;
136 struct Curl_header_store *hs;
137 size_t amount = 0;
138 size_t index = 0;
139
140 if(request > data->state.requests)
141 return NULL;
142 if(request == -1)
143 request = data->state.requests;
144
145 if(prev) {
146 pick = prev->anchor;
147 if(!pick)
148 /* something is wrong */
149 return NULL;
150 pick = pick->next;
151 }
152 else
153 pick = data->state.httphdrs.head;
154
155 if(pick) {
156 /* make sure it is the next header of the desired type */
157 do {
158 hs = pick->ptr;
159 if((hs->type & type) && (hs->request == request))
160 break;
161 pick = pick->next;
162 } while(pick);
163 }
164
165 if(!pick)
166 /* no more headers available */
167 return NULL;
168
169 hs = pick->ptr;
170
171 /* count number of occurrences of this name within the mask and figure out
172 the index for the currently selected entry */
173 for(e = data->state.httphdrs.head; e; e = e->next) {
174 struct Curl_header_store *check = e->ptr;
175 if(strcasecompare(hs->name, check->name) &&
176 (check->request == request) &&
177 (check->type & type))
178 amount++;
179 if(e == pick)
180 index = amount - 1;
181 }
182
183 copy_header_external(hs, index, amount, pick,
184 &data->state.headerout[1]);
185 return &data->state.headerout[1];
186 }
187
namevalue(char * header,size_t hlen,unsigned int type,char ** name,char ** value)188 static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
189 char **name, char **value)
190 {
191 char *end = header + hlen - 1; /* point to the last byte */
192 DEBUGASSERT(hlen);
193 *name = header;
194
195 if(type == CURLH_PSEUDO) {
196 if(*header != ':')
197 return CURLE_BAD_FUNCTION_ARGUMENT;
198 header++;
199 }
200
201 /* Find the end of the header name */
202 while(*header && (*header != ':'))
203 ++header;
204
205 if(*header)
206 /* Skip over colon, null it */
207 *header++ = 0;
208 else
209 return CURLE_BAD_FUNCTION_ARGUMENT;
210
211 /* skip all leading space letters */
212 while(*header && ISBLANK(*header))
213 header++;
214
215 *value = header;
216
217 /* skip all trailing space letters */
218 while((end > header) && ISSPACE(*end))
219 *end-- = 0; /* nul terminate */
220 return CURLE_OK;
221 }
222
unfold_value(struct Curl_easy * data,const char * value,size_t vlen)223 static CURLcode unfold_value(struct Curl_easy *data, const char *value,
224 size_t vlen) /* length of the incoming header */
225 {
226 struct Curl_header_store *hs;
227 struct Curl_header_store *newhs;
228 size_t olen; /* length of the old value */
229 size_t oalloc; /* length of the old name + value + separator */
230 size_t offset;
231 DEBUGASSERT(data->state.prevhead);
232 hs = data->state.prevhead;
233 olen = strlen(hs->value);
234 offset = hs->value - hs->buffer;
235 oalloc = olen + offset + 1;
236
237 /* skip all trailing space letters */
238 while(vlen && ISSPACE(value[vlen - 1]))
239 vlen--;
240
241 /* save only one leading space */
242 while((vlen > 1) && ISBLANK(value[0]) && ISBLANK(value[1])) {
243 vlen--;
244 value++;
245 }
246
247 /* since this header block might move in the realloc below, it needs to
248 first be unlinked from the list and then re-added again after the
249 realloc */
250 Curl_llist_remove(&data->state.httphdrs, &hs->node, NULL);
251
252 /* new size = struct + new value length + old name+value length */
253 newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1);
254 if(!newhs)
255 return CURLE_OUT_OF_MEMORY;
256 /* ->name and ->value point into ->buffer (to keep the header allocation
257 in a single memory block), which now potentially have moved. Adjust
258 them. */
259 newhs->name = newhs->buffer;
260 newhs->value = &newhs->buffer[offset];
261
262 /* put the data at the end of the previous data, not the newline */
263 memcpy(&newhs->value[olen], value, vlen);
264 newhs->value[olen + vlen] = 0; /* null-terminate at newline */
265
266 /* insert this node into the list of headers */
267 Curl_llist_append(&data->state.httphdrs, newhs, &newhs->node);
268 data->state.prevhead = newhs;
269 return CURLE_OK;
270 }
271
272
273 /*
274 * Curl_headers_push() gets passed a full HTTP header to store. It gets called
275 * immediately before the header callback. The header is CRLF terminated.
276 */
Curl_headers_push(struct Curl_easy * data,const char * header,unsigned char type)277 CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
278 unsigned char type)
279 {
280 char *value = NULL;
281 char *name = NULL;
282 char *end;
283 size_t hlen; /* length of the incoming header */
284 struct Curl_header_store *hs;
285 CURLcode result = CURLE_OUT_OF_MEMORY;
286
287 if((header[0] == '\r') || (header[0] == '\n'))
288 /* ignore the body separator */
289 return CURLE_OK;
290
291 end = strchr(header, '\r');
292 if(!end) {
293 end = strchr(header, '\n');
294 if(!end)
295 /* neither CR nor LF as terminator is not a valid header */
296 return CURLE_WEIRD_SERVER_REPLY;
297 }
298 hlen = end - header;
299
300 if((header[0] == ' ') || (header[0] == '\t')) {
301 if(data->state.prevhead)
302 /* line folding, append value to the previous header's value */
303 return unfold_value(data, header, hlen);
304 else {
305 /* Can't unfold without a previous header. Instead of erroring, just
306 pass the leading blanks. */
307 while(hlen && ISBLANK(*header)) {
308 header++;
309 hlen--;
310 }
311 if(!hlen)
312 return CURLE_WEIRD_SERVER_REPLY;
313 }
314 }
315 if(Curl_llist_count(&data->state.httphdrs) >= MAX_HTTP_RESP_HEADER_COUNT) {
316 failf(data, "Too many response headers, %d is max",
317 MAX_HTTP_RESP_HEADER_COUNT);
318 return CURLE_TOO_LARGE;
319 }
320 hs = calloc(1, sizeof(*hs) + hlen);
321 if(!hs)
322 return CURLE_OUT_OF_MEMORY;
323 memcpy(hs->buffer, header, hlen);
324 hs->buffer[hlen] = 0; /* nul terminate */
325
326 result = namevalue(hs->buffer, hlen, type, &name, &value);
327 if(!result) {
328 hs->name = name;
329 hs->value = value;
330 hs->type = type;
331 hs->request = data->state.requests;
332
333 /* insert this node into the list of headers */
334 Curl_llist_append(&data->state.httphdrs, hs, &hs->node);
335 data->state.prevhead = hs;
336 }
337 else
338 free(hs);
339 return result;
340 }
341
342 /*
343 * Curl_headers_reset(). Reset the headers subsystem.
344 */
headers_reset(struct Curl_easy * data)345 static void headers_reset(struct Curl_easy *data)
346 {
347 Curl_llist_init(&data->state.httphdrs, NULL);
348 data->state.prevhead = NULL;
349 }
350
351 struct hds_cw_collect_ctx {
352 struct Curl_cwriter super;
353 };
354
hds_cw_collect_write(struct Curl_easy * data,struct Curl_cwriter * writer,int type,const char * buf,size_t blen)355 static CURLcode hds_cw_collect_write(struct Curl_easy *data,
356 struct Curl_cwriter *writer, int type,
357 const char *buf, size_t blen)
358 {
359 if((type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS)) {
360 unsigned char htype = (unsigned char)
361 (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
362 (type & CLIENTWRITE_1XX ? CURLH_1XX :
363 (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
364 CURLH_HEADER)));
365 CURLcode result = Curl_headers_push(data, buf, htype);
366 CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d",
367 htype, blen, result);
368 if(result)
369 return result;
370 }
371 return Curl_cwriter_write(data, writer->next, type, buf, blen);
372 }
373
374 static const struct Curl_cwtype hds_cw_collect = {
375 "hds-collect",
376 NULL,
377 Curl_cwriter_def_init,
378 hds_cw_collect_write,
379 Curl_cwriter_def_close,
380 sizeof(struct hds_cw_collect_ctx)
381 };
382
Curl_headers_init(struct Curl_easy * data)383 CURLcode Curl_headers_init(struct Curl_easy *data)
384 {
385 struct Curl_cwriter *writer;
386 CURLcode result;
387
388 if(data->conn && (data->conn->handler->protocol & PROTO_FAMILY_HTTP)) {
389 /* avoid installing it twice */
390 if(Curl_cwriter_get_by_name(data, hds_cw_collect.name))
391 return CURLE_OK;
392
393 result = Curl_cwriter_create(&writer, data, &hds_cw_collect,
394 CURL_CW_PROTOCOL);
395 if(result)
396 return result;
397
398 result = Curl_cwriter_add(data, writer);
399 if(result) {
400 Curl_cwriter_free(data, writer);
401 return result;
402 }
403 }
404 return CURLE_OK;
405 }
406
407 /*
408 * Curl_headers_cleanup(). Free all stored headers and associated memory.
409 */
Curl_headers_cleanup(struct Curl_easy * data)410 CURLcode Curl_headers_cleanup(struct Curl_easy *data)
411 {
412 struct Curl_llist_element *e;
413 struct Curl_llist_element *n;
414
415 for(e = data->state.httphdrs.head; e; e = n) {
416 struct Curl_header_store *hs = e->ptr;
417 n = e->next;
418 free(hs);
419 }
420 headers_reset(data);
421 return CURLE_OK;
422 }
423
424 #else /* HTTP-disabled builds below */
425
curl_easy_header(CURL * easy,const char * name,size_t index,unsigned int origin,int request,struct curl_header ** hout)426 CURLHcode curl_easy_header(CURL *easy,
427 const char *name,
428 size_t index,
429 unsigned int origin,
430 int request,
431 struct curl_header **hout)
432 {
433 (void)easy;
434 (void)name;
435 (void)index;
436 (void)origin;
437 (void)request;
438 (void)hout;
439 return CURLHE_NOT_BUILT_IN;
440 }
441
curl_easy_nextheader(CURL * easy,unsigned int type,int request,struct curl_header * prev)442 struct curl_header *curl_easy_nextheader(CURL *easy,
443 unsigned int type,
444 int request,
445 struct curl_header *prev)
446 {
447 (void)easy;
448 (void)type;
449 (void)request;
450 (void)prev;
451 return NULL;
452 }
453 #endif
454