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. */
41static 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 */
61CURLHcode 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, index: nameindex, amount, e: e_pick,
121 hout: &data->state.headerout[0]);
122 *hout = &data->state.headerout[0];
123 return CURLHE_OK;
124}
125
126/* public API */
127struct 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, e: pick,
183 hout: &data->state.headerout[1]);
184 return &data->state.headerout[1];
185}
186
187static 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
222static 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(s: 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(ptr: hs, size: 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(dest: &newhs->value[olen], src: value, n: 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, node: &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 */
277CURLcode 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(s: header, c: '\r');
292 if(!end) {
293 end = strchr(s: header, c: '\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, value: header, vlen: 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(dest: hs->buffer, src: header, n: hlen);
319 hs->buffer[hlen] = 0; /* nul terminate */
320
321 result = namevalue(header: hs->buffer, hlen, type, name: &name, value: &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, node: &hs->node);
333 data->state.prevhead = hs;
334 return CURLE_OK;
335fail:
336 free(hs);
337 return result;
338}
339
340/*
341 * Curl_headers_init(). Init the headers subsystem.
342 */
343static 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 */
352CURLcode 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
368CURLHcode 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
384struct 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