Coverage Report

Created: 2024-02-25 06:14

/src/PROJ/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 "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. */
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
0
{
47
0
  struct curl_header *h = hout;
48
0
  h->name = hs->name;
49
0
  h->value = hs->value;
50
0
  h->amount = amount;
51
0
  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
0
  h->origin = hs->type | (1<<27);
57
0
  h->anchor = e;
58
0
}
59
60
/* public API */
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
0
{
68
0
  struct Curl_llist_element *e;
69
0
  struct Curl_llist_element *e_pick = NULL;
70
0
  struct Curl_easy *data = easy;
71
0
  size_t match = 0;
72
0
  size_t amount = 0;
73
0
  struct Curl_header_store *hs = NULL;
74
0
  struct Curl_header_store *pick = NULL;
75
0
  if(!name || !hout || !data ||
76
0
     (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX|
77
0
              CURLH_PSEUDO)) || !type || (request < -1))
78
0
    return CURLHE_BAD_ARGUMENT;
79
0
  if(!Curl_llist_count(&data->state.httphdrs))
80
0
    return CURLHE_NOHEADERS; /* no headers available */
81
0
  if(request > data->state.requests)
82
0
    return CURLHE_NOREQUEST;
83
0
  if(request == -1)
84
0
    request = data->state.requests;
85
86
  /* we need a first round to count amount of this header */
87
0
  for(e = data->state.httphdrs.head; e; e = e->next) {
88
0
    hs = e->ptr;
89
0
    if(strcasecompare(hs->name, name) &&
90
0
       (hs->type & type) &&
91
0
       (hs->request == request)) {
92
0
      amount++;
93
0
      pick = hs;
94
0
      e_pick = e;
95
0
    }
96
0
  }
97
0
  if(!amount)
98
0
    return CURLHE_MISSING;
99
0
  else if(nameindex >= amount)
100
0
    return CURLHE_BADINDEX;
101
102
0
  if(nameindex == amount - 1)
103
    /* if the last or only occurrence is what's asked for, then we know it */
104
0
    hs = pick;
105
0
  else {
106
0
    for(e = data->state.httphdrs.head; e; e = e->next) {
107
0
      hs = e->ptr;
108
0
      if(strcasecompare(hs->name, name) &&
109
0
         (hs->type & type) &&
110
0
         (hs->request == request) &&
111
0
         (match++ == nameindex)) {
112
0
        e_pick = e;
113
0
        break;
114
0
      }
115
0
    }
116
0
    if(!e) /* this shouldn't happen */
117
0
      return CURLHE_MISSING;
118
0
  }
119
  /* this is the name we want */
120
0
  copy_header_external(hs, nameindex, amount, e_pick,
121
0
                       &data->state.headerout[0]);
122
0
  *hout = &data->state.headerout[0];
123
0
  return CURLHE_OK;
124
0
}
125
126
/* public API */
127
struct curl_header *curl_easy_nextheader(CURL *easy,
128
                                         unsigned int type,
129
                                         int request,
130
                                         struct curl_header *prev)
131
0
{
132
0
  struct Curl_easy *data = easy;
133
0
  struct Curl_llist_element *pick;
134
0
  struct Curl_llist_element *e;
135
0
  struct Curl_header_store *hs;
136
0
  size_t amount = 0;
137
0
  size_t index = 0;
138
139
0
  if(request > data->state.requests)
140
0
    return NULL;
141
0
  if(request == -1)
142
0
    request = data->state.requests;
143
144
0
  if(prev) {
145
0
    pick = prev->anchor;
146
0
    if(!pick)
147
      /* something is wrong */
148
0
      return NULL;
149
0
    pick = pick->next;
150
0
  }
151
0
  else
152
0
    pick = data->state.httphdrs.head;
153
154
0
  if(pick) {
155
    /* make sure it is the next header of the desired type */
156
0
    do {
157
0
      hs = pick->ptr;
158
0
      if((hs->type & type) && (hs->request == request))
159
0
        break;
160
0
      pick = pick->next;
161
0
    } while(pick);
162
0
  }
163
164
0
  if(!pick)
165
    /* no more headers available */
166
0
    return NULL;
167
168
0
  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
0
  for(e = data->state.httphdrs.head; e; e = e->next) {
173
0
    struct Curl_header_store *check = e->ptr;
174
0
    if(strcasecompare(hs->name, check->name) &&
175
0
       (check->request == request) &&
176
0
       (check->type & type))
177
0
      amount++;
178
0
    if(e == pick)
179
0
      index = amount - 1;
180
0
  }
181
182
0
  copy_header_external(hs, index, amount, pick,
183
0
                       &data->state.headerout[1]);
184
0
  return &data->state.headerout[1];
185
0
}
186
187
static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
188
                          char **name, char **value)
189
0
{
190
0
  char *end = header + hlen - 1; /* point to the last byte */
191
0
  DEBUGASSERT(hlen);
192
0
  *name = header;
193
194
0
  if(type == CURLH_PSEUDO) {
195
0
    if(*header != ':')
196
0
      return CURLE_BAD_FUNCTION_ARGUMENT;
197
0
    header++;
198
0
  }
199
200
  /* Find the end of the header name */
201
0
  while(*header && (*header != ':'))
202
0
    ++header;
203
204
0
  if(*header)
205
    /* Skip over colon, null it */
206
0
    *header++ = 0;
207
0
  else
208
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
209
210
  /* skip all leading space letters */
211
0
  while(*header && ISBLANK(*header))
212
0
    header++;
213
214
0
  *value = header;
215
216
  /* skip all trailing space letters */
217
0
  while((end > header) && ISSPACE(*end))
218
0
    *end-- = 0; /* nul terminate */
219
0
  return CURLE_OK;
220
0
}
221
222
static CURLcode unfold_value(struct Curl_easy *data, const char *value,
223
                             size_t vlen)  /* length of the incoming header */
224
0
{
225
0
  struct Curl_header_store *hs;
226
0
  struct Curl_header_store *newhs;
227
0
  size_t olen; /* length of the old value */
228
0
  size_t oalloc; /* length of the old name + value + separator */
229
0
  size_t offset;
230
0
  DEBUGASSERT(data->state.prevhead);
231
0
  hs = data->state.prevhead;
232
0
  olen = strlen(hs->value);
233
0
  offset = hs->value - hs->buffer;
234
0
  oalloc = olen + offset + 1;
235
236
  /* skip all trailing space letters */
237
0
  while(vlen && ISSPACE(value[vlen - 1]))
238
0
    vlen--;
239
240
  /* save only one leading space */
241
0
  while((vlen > 1) && ISBLANK(value[0]) && ISBLANK(value[1])) {
242
0
    vlen--;
243
0
    value++;
244
0
  }
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
0
  Curl_llist_remove(&data->state.httphdrs, &hs->node, NULL);
250
251
  /* new size = struct + new value length + old name+value length */
252
0
  newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1);
253
0
  if(!newhs)
254
0
    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
0
  newhs->name = newhs->buffer;
259
0
  newhs->value = &newhs->buffer[offset];
260
261
  /* put the data at the end of the previous data, not the newline */
262
0
  memcpy(&newhs->value[olen], value, vlen);
263
0
  newhs->value[olen + vlen] = 0; /* null-terminate at newline */
264
265
  /* insert this node into the list of headers */
266
0
  Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
267
0
                         newhs, &newhs->node);
268
0
  data->state.prevhead = newhs;
269
0
  return CURLE_OK;
270
0
}
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
 */
277
CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
278
                           unsigned char type)
279
0
{
280
0
  char *value = NULL;
281
0
  char *name = NULL;
282
0
  char *end;
283
0
  size_t hlen; /* length of the incoming header */
284
0
  struct Curl_header_store *hs;
285
0
  CURLcode result = CURLE_OUT_OF_MEMORY;
286
287
0
  if((header[0] == '\r') || (header[0] == '\n'))
288
    /* ignore the body separator */
289
0
    return CURLE_OK;
290
291
0
  end = strchr(header, '\r');
292
0
  if(!end) {
293
0
    end = strchr(header, '\n');
294
0
    if(!end)
295
      /* neither CR nor LF as terminator is not a valid header */
296
0
      return CURLE_WEIRD_SERVER_REPLY;
297
0
  }
298
0
  hlen = end - header;
299
300
0
  if((header[0] == ' ') || (header[0] == '\t')) {
301
0
    if(data->state.prevhead)
302
      /* line folding, append value to the previous header's value */
303
0
      return unfold_value(data, header, hlen);
304
0
    else {
305
      /* Can't unfold without a previous header. Instead of erroring, just
306
         pass the leading blanks. */
307
0
      while(hlen && ISBLANK(*header)) {
308
0
        header++;
309
0
        hlen--;
310
0
      }
311
0
      if(!hlen)
312
0
        return CURLE_WEIRD_SERVER_REPLY;
313
0
    }
314
0
  }
315
316
0
  hs = calloc(1, sizeof(*hs) + hlen);
317
0
  if(!hs)
318
0
    return CURLE_OUT_OF_MEMORY;
319
0
  memcpy(hs->buffer, header, hlen);
320
0
  hs->buffer[hlen] = 0; /* nul terminate */
321
322
0
  result = namevalue(hs->buffer, hlen, type, &name, &value);
323
0
  if(!result) {
324
0
    hs->name = name;
325
0
    hs->value = value;
326
0
    hs->type = type;
327
0
    hs->request = data->state.requests;
328
329
    /* insert this node into the list of headers */
330
0
    Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
331
0
                           hs, &hs->node);
332
0
    data->state.prevhead = hs;
333
0
  }
334
0
  else
335
0
    free(hs);
336
0
  return result;
337
0
}
338
339
/*
340
 * Curl_headers_init(). Init the headers subsystem.
341
 */
342
static void headers_init(struct Curl_easy *data)
343
0
{
344
0
  Curl_llist_init(&data->state.httphdrs, NULL);
345
0
  data->state.prevhead = NULL;
346
0
}
347
348
/*
349
 * Curl_headers_cleanup(). Free all stored headers and associated memory.
350
 */
351
CURLcode Curl_headers_cleanup(struct Curl_easy *data)
352
0
{
353
0
  struct Curl_llist_element *e;
354
0
  struct Curl_llist_element *n;
355
356
0
  for(e = data->state.httphdrs.head; e; e = n) {
357
0
    struct Curl_header_store *hs = e->ptr;
358
0
    n = e->next;
359
0
    free(hs);
360
0
  }
361
0
  headers_init(data);
362
0
  return CURLE_OK;
363
0
}
364
365
#else /* HTTP-disabled builds below */
366
367
CURLHcode curl_easy_header(CURL *easy,
368
                           const char *name,
369
                           size_t index,
370
                           unsigned int origin,
371
                           int request,
372
                           struct curl_header **hout)
373
{
374
  (void)easy;
375
  (void)name;
376
  (void)index;
377
  (void)origin;
378
  (void)request;
379
  (void)hout;
380
  return CURLHE_NOT_BUILT_IN;
381
}
382
383
struct curl_header *curl_easy_nextheader(CURL *easy,
384
                                         unsigned int type,
385
                                         int request,
386
                                         struct curl_header *prev)
387
{
388
  (void)easy;
389
  (void)type;
390
  (void)request;
391
  (void)prev;
392
  return NULL;
393
}
394
#endif