xref: /curl/lib/headers.c (revision fbf5d507)
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_node * 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_node *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 = (unsigned int)(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_node *e;
70   struct Curl_llist_node *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 = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
89     hs = Curl_node_elem(e);
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 = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
108       hs = Curl_node_elem(e);
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 should not 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_node *pick;
135   struct Curl_llist_node *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 = Curl_node_next(pick);
151   }
152   else
153     pick = Curl_llist_head(&data->state.httphdrs);
154 
155   if(pick) {
156     /* make sure it is the next header of the desired type */
157     do {
158       hs = Curl_node_elem(pick);
159       if((hs->type & type) && (hs->request == request))
160         break;
161       pick = Curl_node_next(pick);
162     } while(pick);
163   }
164 
165   if(!pick)
166     /* no more headers available */
167     return NULL;
168 
169   hs = Curl_node_elem(pick);
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 = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
174     struct Curl_header_store *check = Curl_node_elem(e);
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_node_remove(&hs->node);
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       /* cannot 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 
316   hs = calloc(1, sizeof(*hs) + hlen);
317   if(!hs)
318     return CURLE_OUT_OF_MEMORY;
319   memcpy(hs->buffer, header, hlen);
320   hs->buffer[hlen] = 0; /* nul terminate */
321 
322   result = namevalue(hs->buffer, hlen, type, &name, &value);
323   if(!result) {
324     hs->name = name;
325     hs->value = value;
326     hs->type = type;
327     hs->request = data->state.requests;
328 
329     /* insert this node into the list of headers */
330     Curl_llist_append(&data->state.httphdrs, hs, &hs->node);
331     data->state.prevhead = hs;
332   }
333   else
334     free(hs);
335   return result;
336 }
337 
338 /*
339  * Curl_headers_reset(). Reset the headers subsystem.
340  */
headers_reset(struct Curl_easy * data)341 static void headers_reset(struct Curl_easy *data)
342 {
343   Curl_llist_init(&data->state.httphdrs, NULL);
344   data->state.prevhead = NULL;
345 }
346 
347 struct hds_cw_collect_ctx {
348   struct Curl_cwriter super;
349 };
350 
hds_cw_collect_write(struct Curl_easy * data,struct Curl_cwriter * writer,int type,const char * buf,size_t blen)351 static CURLcode hds_cw_collect_write(struct Curl_easy *data,
352                                      struct Curl_cwriter *writer, int type,
353                                      const char *buf, size_t blen)
354 {
355   if((type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS)) {
356     unsigned char htype = (unsigned char)
357       (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
358        (type & CLIENTWRITE_1XX ? CURLH_1XX :
359         (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
360          CURLH_HEADER)));
361     CURLcode result = Curl_headers_push(data, buf, htype);
362     CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d",
363                    htype, blen, result);
364     if(result)
365       return result;
366   }
367   return Curl_cwriter_write(data, writer->next, type, buf, blen);
368 }
369 
370 static const struct Curl_cwtype hds_cw_collect = {
371   "hds-collect",
372   NULL,
373   Curl_cwriter_def_init,
374   hds_cw_collect_write,
375   Curl_cwriter_def_close,
376   sizeof(struct hds_cw_collect_ctx)
377 };
378 
Curl_headers_init(struct Curl_easy * data)379 CURLcode Curl_headers_init(struct Curl_easy *data)
380 {
381   struct Curl_cwriter *writer;
382   CURLcode result;
383 
384   if(data->conn && (data->conn->handler->protocol & PROTO_FAMILY_HTTP)) {
385     /* avoid installing it twice */
386     if(Curl_cwriter_get_by_name(data, hds_cw_collect.name))
387       return CURLE_OK;
388 
389     result = Curl_cwriter_create(&writer, data, &hds_cw_collect,
390                                  CURL_CW_PROTOCOL);
391     if(result)
392       return result;
393 
394     result = Curl_cwriter_add(data, writer);
395     if(result) {
396       Curl_cwriter_free(data, writer);
397       return result;
398     }
399   }
400   return CURLE_OK;
401 }
402 
403 /*
404  * Curl_headers_cleanup(). Free all stored headers and associated memory.
405  */
Curl_headers_cleanup(struct Curl_easy * data)406 CURLcode Curl_headers_cleanup(struct Curl_easy *data)
407 {
408   struct Curl_llist_node *e;
409   struct Curl_llist_node *n;
410 
411   for(e = Curl_llist_head(&data->state.httphdrs); e; e = n) {
412     struct Curl_header_store *hs = Curl_node_elem(e);
413     n = Curl_node_next(e);
414     free(hs);
415   }
416   headers_reset(data);
417   return CURLE_OK;
418 }
419 
420 #else /* HTTP-disabled builds below */
421 
curl_easy_header(CURL * easy,const char * name,size_t index,unsigned int origin,int request,struct curl_header ** hout)422 CURLHcode curl_easy_header(CURL *easy,
423                            const char *name,
424                            size_t index,
425                            unsigned int origin,
426                            int request,
427                            struct curl_header **hout)
428 {
429   (void)easy;
430   (void)name;
431   (void)index;
432   (void)origin;
433   (void)request;
434   (void)hout;
435   return CURLHE_NOT_BUILT_IN;
436 }
437 
curl_easy_nextheader(CURL * easy,unsigned int type,int request,struct curl_header * prev)438 struct curl_header *curl_easy_nextheader(CURL *easy,
439                                          unsigned int type,
440                                          int request,
441                                          struct curl_header *prev)
442 {
443   (void)easy;
444   (void)type;
445   (void)request;
446   (void)prev;
447   return NULL;
448 }
449 #endif
450