• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "headers.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 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
38 
39 /* Generate the curl_header struct for the user. This function MUST assign all
40    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)41 static void copy_header_external(struct Curl_header_store *hs,
42                                  size_t index,
43                                  size_t amount,
44                                  struct Curl_llist_element *e,
45                                  struct curl_header *hout)
46 {
47   struct curl_header *h = hout;
48   h->name = hs->name;
49   h->value = hs->value;
50   h->amount = amount;
51   h->index = index;
52   /* this will randomly OR a reserved bit for the sole purpose of making it
53      impossible for applications to do == comparisons, as that would otherwise
54      be very tempting and then lead to the reserved bits not being reserved
55      anymore. */
56   h->origin = hs->type | (1<<27);
57   h->anchor = e;
58 }
59 
60 /* public API */
curl_easy_header(CURL * easy,const char * name,size_t nameindex,unsigned int type,int request,struct curl_header ** hout)61 CURLHcode curl_easy_header(CURL *easy,
62                            const char *name,
63                            size_t nameindex,
64                            unsigned int type,
65                            int request,
66                            struct curl_header **hout)
67 {
68   struct Curl_llist_element *e;
69   struct Curl_llist_element *e_pick = NULL;
70   struct Curl_easy *data = easy;
71   size_t match = 0;
72   size_t amount = 0;
73   struct Curl_header_store *hs = NULL;
74   struct Curl_header_store *pick = NULL;
75   if(!name || !hout || !data ||
76      (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX|
77               CURLH_PSEUDO)) || !type || (request < -1))
78     return CURLHE_BAD_ARGUMENT;
79   if(!Curl_llist_count(&data->state.httphdrs))
80     return CURLHE_NOHEADERS; /* no headers available */
81   if(request > data->state.requests)
82     return CURLHE_NOREQUEST;
83   if(request == -1)
84     request = data->state.requests;
85 
86   /* we need a first round to count amount of this header */
87   for(e = data->state.httphdrs.head; e; e = e->next) {
88     hs = e->ptr;
89     if(strcasecompare(hs->name, name) &&
90        (hs->type & type) &&
91        (hs->request == request)) {
92       amount++;
93       pick = hs;
94       e_pick = e;
95     }
96   }
97   if(!amount)
98     return CURLHE_MISSING;
99   else if(nameindex >= amount)
100     return CURLHE_BADINDEX;
101 
102   if(nameindex == amount - 1)
103     /* if the last or only occurrence is what's asked for, then we know it */
104     hs = pick;
105   else {
106     for(e = data->state.httphdrs.head; e; e = e->next) {
107       hs = e->ptr;
108       if(strcasecompare(hs->name, name) &&
109          (hs->type & type) &&
110          (hs->request == request) &&
111          (match++ == nameindex)) {
112         e_pick = e;
113         break;
114       }
115     }
116     if(!e) /* this shouldn't happen */
117       return CURLHE_MISSING;
118   }
119   /* this is the name we want */
120   copy_header_external(hs, nameindex, amount, e_pick,
121                        &data->state.headerout[0]);
122   *hout = &data->state.headerout[0];
123   return CURLHE_OK;
124 }
125 
126 /* public API */
curl_easy_nextheader(CURL * easy,unsigned int type,int request,struct curl_header * prev)127 struct curl_header *curl_easy_nextheader(CURL *easy,
128                                          unsigned int type,
129                                          int request,
130                                          struct curl_header *prev)
131 {
132   struct Curl_easy *data = easy;
133   struct Curl_llist_element *pick;
134   struct Curl_llist_element *e;
135   struct Curl_header_store *hs;
136   size_t amount = 0;
137   size_t index = 0;
138 
139   if(request > data->state.requests)
140     return NULL;
141   if(request == -1)
142     request = data->state.requests;
143 
144   if(prev) {
145     pick = prev->anchor;
146     if(!pick)
147       /* something is wrong */
148       return NULL;
149     pick = pick->next;
150   }
151   else
152     pick = data->state.httphdrs.head;
153 
154   if(pick) {
155     /* make sure it is the next header of the desired type */
156     do {
157       hs = pick->ptr;
158       if((hs->type & type) && (hs->request == request))
159         break;
160       pick = pick->next;
161     } while(pick);
162   }
163 
164   if(!pick)
165     /* no more headers available */
166     return NULL;
167 
168   hs = pick->ptr;
169 
170   /* count number of occurrences of this name within the mask and figure out
171      the index for the currently selected entry */
172   for(e = data->state.httphdrs.head; e; e = e->next) {
173     struct Curl_header_store *check = e->ptr;
174     if(strcasecompare(hs->name, check->name) &&
175        (check->request == request) &&
176        (check->type & type))
177       amount++;
178     if(e == pick)
179       index = amount - 1;
180   }
181 
182   copy_header_external(hs, index, amount, pick,
183                        &data->state.headerout[1]);
184   return &data->state.headerout[1];
185 }
186 
namevalue(char * header,size_t hlen,unsigned int type,char ** name,char ** value)187 static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
188                            char **name, char **value)
189 {
190   char *end = header + hlen - 1; /* point to the last byte */
191   DEBUGASSERT(hlen);
192   *name = header;
193 
194   if(type == CURLH_PSEUDO) {
195     if(*header != ':')
196       return CURLE_BAD_FUNCTION_ARGUMENT;
197     header++;
198   }
199 
200   /* Find the end of the header name */
201   while(*header && (*header != ':'))
202     ++header;
203 
204   if(*header)
205     /* Skip over colon, null it */
206     *header++ = 0;
207   else
208     return CURLE_BAD_FUNCTION_ARGUMENT;
209 
210   /* skip all leading space letters */
211   while(*header && ISBLANK(*header))
212     header++;
213 
214   *value = header;
215 
216   /* skip all trailing space letters */
217   while((end > header) && ISSPACE(*end))
218     *end-- = 0; /* nul terminate */
219   return CURLE_OK;
220 }
221 
unfold_value(struct Curl_easy * data,const char * value,size_t vlen)222 static CURLcode unfold_value(struct Curl_easy *data, const char *value,
223                              size_t vlen)  /* length of the incoming header */
224 {
225   struct Curl_header_store *hs;
226   struct Curl_header_store *newhs;
227   size_t olen; /* length of the old value */
228   size_t oalloc; /* length of the old name + value + separator */
229   size_t offset;
230   DEBUGASSERT(data->state.prevhead);
231   hs = data->state.prevhead;
232   olen = strlen(hs->value);
233   offset = hs->value - hs->buffer;
234   oalloc = olen + offset + 1;
235 
236   /* skip all trailing space letters */
237   while(vlen && ISSPACE(value[vlen - 1]))
238     vlen--;
239 
240   /* save only one leading space */
241   while((vlen > 1) && ISBLANK(value[0]) && ISBLANK(value[1])) {
242     vlen--;
243     value++;
244   }
245 
246   /* since this header block might move in the realloc below, it needs to
247      first be unlinked from the list and then re-added again after the
248      realloc */
249   Curl_llist_remove(&data->state.httphdrs, &hs->node, NULL);
250 
251   /* new size = struct + new value length + old name+value length */
252   newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1);
253   if(!newhs)
254     return CURLE_OUT_OF_MEMORY;
255   /* ->name' and ->value point into ->buffer (to keep the header allocation
256      in a single memory block), which now potentially have moved. Adjust
257      them. */
258   newhs->name = newhs->buffer;
259   newhs->value = &newhs->buffer[offset];
260 
261   /* put the data at the end of the previous data, not the newline */
262   memcpy(&newhs->value[olen], value, vlen);
263   newhs->value[olen + vlen] = 0; /* null-terminate at newline */
264 
265   /* insert this node into the list of headers */
266   Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
267                          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       return CURLE_BAD_FUNCTION_ARGUMENT;
296   }
297   hlen = end - header + 1;
298 
299   if((header[0] == ' ') || (header[0] == '\t')) {
300     if(data->state.prevhead)
301       /* line folding, append value to the previous header's value */
302       return unfold_value(data, header, hlen);
303     else {
304       /* Can't unfold without a previous header. Instead of erroring, just
305          pass the leading blanks. */
306       while(hlen && ISBLANK(*header)) {
307         header++;
308         hlen--;
309       }
310       if(!hlen)
311         return CURLE_WEIRD_SERVER_REPLY;
312     }
313   }
314 
315   hs = calloc(1, sizeof(*hs) + hlen);
316   if(!hs)
317     return CURLE_OUT_OF_MEMORY;
318   memcpy(hs->buffer, header, hlen);
319   hs->buffer[hlen] = 0; /* nul terminate */
320 
321   result = namevalue(hs->buffer, hlen, type, &name, &value);
322   if(result)
323     goto fail;
324 
325   hs->name = name;
326   hs->value = value;
327   hs->type = type;
328   hs->request = data->state.requests;
329 
330   /* insert this node into the list of headers */
331   Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
332                          hs, &hs->node);
333   data->state.prevhead = hs;
334   return CURLE_OK;
335 fail:
336   free(hs);
337   return result;
338 }
339 
340 /*
341  * Curl_headers_init(). Init the headers subsystem.
342  */
headers_init(struct Curl_easy * data)343 static void headers_init(struct Curl_easy *data)
344 {
345   Curl_llist_init(&data->state.httphdrs, NULL);
346   data->state.prevhead = NULL;
347 }
348 
349 /*
350  * Curl_headers_cleanup(). Free all stored headers and associated memory.
351  */
Curl_headers_cleanup(struct Curl_easy * data)352 CURLcode Curl_headers_cleanup(struct Curl_easy *data)
353 {
354   struct Curl_llist_element *e;
355   struct Curl_llist_element *n;
356 
357   for(e = data->state.httphdrs.head; e; e = n) {
358     struct Curl_header_store *hs = e->ptr;
359     n = e->next;
360     free(hs);
361   }
362   headers_init(data);
363   return CURLE_OK;
364 }
365 
366 #else /* HTTP-disabled builds below */
367 
curl_easy_header(CURL * easy,const char * name,size_t index,unsigned int origin,int request,struct curl_header ** hout)368 CURLHcode curl_easy_header(CURL *easy,
369                            const char *name,
370                            size_t index,
371                            unsigned int origin,
372                            int request,
373                            struct curl_header **hout)
374 {
375   (void)easy;
376   (void)name;
377   (void)index;
378   (void)origin;
379   (void)request;
380   (void)hout;
381   return CURLHE_NOT_BUILT_IN;
382 }
383 
curl_easy_nextheader(CURL * easy,unsigned int type,int request,struct curl_header * prev)384 struct curl_header *curl_easy_nextheader(CURL *easy,
385                                          unsigned int type,
386                                          int request,
387                                          struct curl_header *prev)
388 {
389   (void)easy;
390   (void)type;
391   (void)request;
392   (void)prev;
393   return NULL;
394 }
395 #endif
396