Coverage Report

Created: 2025-06-09 07:42

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