Coverage Report

Created: 2025-12-05 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/dynhds.c
Line
Count
Source
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
#include "dynhds.h"
27
#include "strcase.h"
28
29
#ifdef USE_NGHTTP2
30
#include <stdint.h>
31
#include <nghttp2/nghttp2.h>
32
#endif /* USE_NGHTTP2 */
33
34
35
static struct dynhds_entry *
36
entry_new(const char *name, size_t namelen,
37
          const char *value, size_t valuelen, int opts)
38
1.84M
{
39
1.84M
  struct dynhds_entry *e;
40
1.84M
  char *p;
41
42
1.84M
  DEBUGASSERT(name);
43
1.84M
  DEBUGASSERT(value);
44
1.84M
  e = curlx_calloc(1, sizeof(*e) + namelen + valuelen + 2);
45
1.84M
  if(!e)
46
0
    return NULL;
47
1.84M
  e->name = p = ((char *)e) + sizeof(*e);
48
1.84M
  memcpy(p, name, namelen);
49
1.84M
  e->namelen = namelen;
50
1.84M
  e->value = p += namelen + 1; /* leave a \0 at the end of name */
51
1.84M
  memcpy(p, value, valuelen);
52
1.84M
  e->valuelen = valuelen;
53
1.84M
  if(opts & DYNHDS_OPT_LOWERCASE)
54
886k
    Curl_strntolower(e->name, e->name, e->namelen);
55
1.84M
  return e;
56
1.84M
}
57
58
static struct dynhds_entry *entry_append(struct dynhds_entry *e,
59
                                         const char *value, size_t valuelen)
60
24.9k
{
61
24.9k
  struct dynhds_entry *e2;
62
24.9k
  size_t valuelen2 = e->valuelen + 1 + valuelen;
63
24.9k
  char *p;
64
65
24.9k
  DEBUGASSERT(value);
66
24.9k
  e2 = curlx_calloc(1, sizeof(*e) + e->namelen + valuelen2 + 2);
67
24.9k
  if(!e2)
68
0
    return NULL;
69
24.9k
  e2->name = p = ((char *)e2) + sizeof(*e2);
70
24.9k
  memcpy(p, e->name, e->namelen);
71
24.9k
  e2->namelen = e->namelen;
72
24.9k
  e2->value = p += e->namelen + 1; /* leave a \0 at the end of name */
73
24.9k
  memcpy(p, e->value, e->valuelen);
74
24.9k
  p += e->valuelen;
75
24.9k
  p[0] = ' ';
76
24.9k
  memcpy(p + 1, value, valuelen);
77
24.9k
  e2->valuelen = valuelen2;
78
24.9k
  return e2;
79
24.9k
}
80
81
static void entry_free(struct dynhds_entry *e)
82
1.86M
{
83
1.86M
  curlx_free(e);
84
1.86M
}
85
86
void Curl_dynhds_init(struct dynhds *dynhds, size_t max_entries,
87
                      size_t max_strs_size)
88
116k
{
89
116k
  DEBUGASSERT(dynhds);
90
116k
  DEBUGASSERT(max_strs_size);
91
116k
  dynhds->hds = NULL;
92
116k
  dynhds->hds_len = dynhds->hds_allc = dynhds->strs_len = 0;
93
116k
  dynhds->max_entries = max_entries;
94
116k
  dynhds->max_strs_size = max_strs_size;
95
116k
  dynhds->opts = 0;
96
116k
}
97
98
void Curl_dynhds_free(struct dynhds *dynhds)
99
116k
{
100
116k
  DEBUGASSERT(dynhds);
101
116k
  if(dynhds->hds && dynhds->hds_len) {
102
57.7k
    size_t i;
103
57.7k
    DEBUGASSERT(dynhds->hds);
104
1.90M
    for(i = 0; i < dynhds->hds_len; ++i) {
105
1.84M
      entry_free(dynhds->hds[i]);
106
1.84M
    }
107
57.7k
  }
108
116k
  Curl_safefree(dynhds->hds);
109
116k
  dynhds->hds_len = dynhds->hds_allc = dynhds->strs_len = 0;
110
116k
}
111
112
void Curl_dynhds_reset(struct dynhds *dynhds)
113
18.0k
{
114
18.0k
  DEBUGASSERT(dynhds);
115
18.0k
  if(dynhds->hds_len) {
116
0
    size_t i;
117
0
    DEBUGASSERT(dynhds->hds);
118
0
    for(i = 0; i < dynhds->hds_len; ++i) {
119
0
      entry_free(dynhds->hds[i]);
120
0
      dynhds->hds[i] = NULL;
121
0
    }
122
0
  }
123
18.0k
  dynhds->hds_len = dynhds->strs_len = 0;
124
18.0k
}
125
126
size_t Curl_dynhds_count(struct dynhds *dynhds)
127
871k
{
128
871k
  return dynhds->hds_len;
129
871k
}
130
131
void Curl_dynhds_set_opts(struct dynhds *dynhds, int opts)
132
18.0k
{
133
18.0k
  dynhds->opts = opts;
134
18.0k
}
135
136
struct dynhds_entry *Curl_dynhds_getn(struct dynhds *dynhds, size_t n)
137
853k
{
138
853k
  DEBUGASSERT(dynhds);
139
853k
  return (n < dynhds->hds_len) ? dynhds->hds[n] : NULL;
140
853k
}
141
142
struct dynhds_entry *Curl_dynhds_get(struct dynhds *dynhds, const char *name,
143
                                     size_t namelen)
144
17.9k
{
145
17.9k
  size_t i;
146
43.8k
  for(i = 0; i < dynhds->hds_len; ++i) {
147
43.7k
    if(dynhds->hds[i]->namelen == namelen &&
148
21.3k
       curl_strnequal(dynhds->hds[i]->name, name, namelen)) {
149
17.8k
      return dynhds->hds[i];
150
17.8k
    }
151
43.7k
  }
152
81
  return NULL;
153
17.9k
}
154
155
struct dynhds_entry *Curl_dynhds_cget(struct dynhds *dynhds, const char *name)
156
0
{
157
0
  return Curl_dynhds_get(dynhds, name, strlen(name));
158
0
}
159
160
CURLcode Curl_dynhds_add(struct dynhds *dynhds,
161
                         const char *name, size_t namelen,
162
                         const char *value, size_t valuelen)
163
1.84M
{
164
1.84M
  struct dynhds_entry *entry = NULL;
165
1.84M
  CURLcode result = CURLE_OUT_OF_MEMORY;
166
167
1.84M
  DEBUGASSERT(dynhds);
168
1.84M
  if(dynhds->max_entries && dynhds->hds_len >= dynhds->max_entries)
169
0
    return CURLE_OUT_OF_MEMORY;
170
1.84M
  if(dynhds->strs_len + namelen + valuelen > dynhds->max_strs_size)
171
0
    return CURLE_OUT_OF_MEMORY;
172
173
1.84M
  entry = entry_new(name, namelen, value, valuelen, dynhds->opts);
174
1.84M
  if(!entry)
175
0
    goto out;
176
177
1.84M
  if(dynhds->hds_len + 1 >= dynhds->hds_allc) {
178
164k
    size_t nallc = dynhds->hds_len + 16;
179
164k
    struct dynhds_entry **nhds;
180
181
164k
    if(dynhds->max_entries && nallc > dynhds->max_entries)
182
0
      nallc = dynhds->max_entries;
183
184
164k
    nhds = curlx_calloc(nallc, sizeof(struct dynhds_entry *));
185
164k
    if(!nhds)
186
0
      goto out;
187
164k
    if(dynhds->hds) {
188
106k
      memcpy(nhds, dynhds->hds,
189
106k
             dynhds->hds_len * sizeof(struct dynhds_entry *));
190
106k
      Curl_safefree(dynhds->hds);
191
106k
    }
192
164k
    dynhds->hds = nhds;
193
164k
    dynhds->hds_allc = nallc;
194
164k
  }
195
1.84M
  dynhds->hds[dynhds->hds_len++] = entry;
196
1.84M
  entry = NULL;
197
1.84M
  dynhds->strs_len += namelen + valuelen;
198
1.84M
  result = CURLE_OK;
199
200
1.84M
out:
201
1.84M
  if(entry)
202
0
    entry_free(entry);
203
1.84M
  return result;
204
1.84M
}
205
206
CURLcode Curl_dynhds_cadd(struct dynhds *dynhds,
207
                          const char *name, const char *value)
208
44.0k
{
209
44.0k
  return Curl_dynhds_add(dynhds, name, strlen(name), value, strlen(value));
210
44.0k
}
211
212
CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds,
213
                                 const char *line, size_t line_len)
214
894k
{
215
894k
  const char *p;
216
894k
  const char *name;
217
894k
  size_t namelen;
218
894k
  const char *value;
219
894k
  size_t valuelen, i;
220
221
894k
  if(!line || !line_len)
222
0
    return CURLE_OK;
223
224
894k
  if((line[0] == ' ') || (line[0] == '\t')) {
225
24.9k
    struct dynhds_entry *e, *e2;
226
    /* header continuation, yikes! */
227
24.9k
    if(!dynhds->hds_len)
228
6
      return CURLE_BAD_FUNCTION_ARGUMENT;
229
230
67.2k
    while(line_len && ISBLANK(line[0])) {
231
42.3k
      ++line;
232
42.3k
      --line_len;
233
42.3k
    }
234
24.9k
    if(!line_len)
235
7
      return CURLE_BAD_FUNCTION_ARGUMENT;
236
24.9k
    e = dynhds->hds[dynhds->hds_len - 1];
237
24.9k
    e2 = entry_append(e, line, line_len);
238
24.9k
    if(!e2)
239
0
      return CURLE_OUT_OF_MEMORY;
240
24.9k
    dynhds->hds[dynhds->hds_len - 1] = e2;
241
24.9k
    entry_free(e);
242
24.9k
    return CURLE_OK;
243
24.9k
  }
244
869k
  else {
245
869k
    p = memchr(line, ':', line_len);
246
869k
    if(!p)
247
52
      return CURLE_BAD_FUNCTION_ARGUMENT;
248
869k
    name = line;
249
869k
    namelen = p - line;
250
869k
    p++; /* move past the colon */
251
1.03M
    for(i = namelen + 1; i < line_len; ++i, ++p) {
252
818k
      if(!ISBLANK(*p))
253
653k
        break;
254
818k
    }
255
869k
    value = p;
256
869k
    valuelen = line_len - i;
257
258
869k
    p = memchr(value, '\r', valuelen);
259
869k
    if(!p)
260
821k
      p = memchr(value, '\n', valuelen);
261
869k
    if(p)
262
48.2k
      valuelen = (size_t)(p - value);
263
264
869k
    return Curl_dynhds_add(dynhds, name, namelen, value, valuelen);
265
869k
  }
266
894k
}
267
268
CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line)
269
11.7k
{
270
11.7k
  return Curl_dynhds_h1_add_line(dynhds, line, line ? strlen(line) : 0);
271
11.7k
}
272
273
#ifdef UNITTESTS
274
/* used by unit2602.c */
275
276
bool Curl_dynhds_contains(struct dynhds *dynhds,
277
                          const char *name, size_t namelen)
278
{
279
  return !!Curl_dynhds_get(dynhds, name, namelen);
280
}
281
282
bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name)
283
{
284
  return Curl_dynhds_contains(dynhds, name, strlen(name));
285
}
286
287
size_t Curl_dynhds_count_name(struct dynhds *dynhds,
288
                              const char *name, size_t namelen)
289
{
290
  size_t n = 0;
291
  if(dynhds->hds_len) {
292
    size_t i;
293
    for(i = 0; i < dynhds->hds_len; ++i) {
294
      if((namelen == dynhds->hds[i]->namelen) &&
295
         curl_strnequal(name, dynhds->hds[i]->name, namelen))
296
        ++n;
297
    }
298
  }
299
  return n;
300
}
301
302
size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name)
303
{
304
  return Curl_dynhds_count_name(dynhds, name, strlen(name));
305
}
306
307
CURLcode Curl_dynhds_set(struct dynhds *dynhds,
308
                         const char *name, size_t namelen,
309
                         const char *value, size_t valuelen)
310
{
311
  Curl_dynhds_remove(dynhds, name, namelen);
312
  return Curl_dynhds_add(dynhds, name, namelen, value, valuelen);
313
}
314
315
size_t Curl_dynhds_remove(struct dynhds *dynhds,
316
                          const char *name, size_t namelen)
317
{
318
  size_t n = 0;
319
  if(dynhds->hds_len) {
320
    size_t i, len;
321
    for(i = 0; i < dynhds->hds_len; ++i) {
322
      if((namelen == dynhds->hds[i]->namelen) &&
323
         curl_strnequal(name, dynhds->hds[i]->name, namelen)) {
324
        ++n;
325
        --dynhds->hds_len;
326
        dynhds->strs_len -= (dynhds->hds[i]->namelen +
327
                             dynhds->hds[i]->valuelen);
328
        entry_free(dynhds->hds[i]);
329
        len = dynhds->hds_len - i; /* remaining entries */
330
        if(len) {
331
          memmove(&dynhds->hds[i], &dynhds->hds[i + 1],
332
                  len * sizeof(dynhds->hds[i]));
333
        }
334
        --i; /* do this index again */
335
      }
336
    }
337
  }
338
  return n;
339
}
340
341
size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name)
342
{
343
  return Curl_dynhds_remove(dynhds, name, strlen(name));
344
}
345
346
#endif
347
348
CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf)
349
21.6k
{
350
21.6k
  CURLcode result = CURLE_OK;
351
21.6k
  size_t i;
352
353
21.6k
  if(!dynhds->hds_len)
354
0
    return result;
355
356
121k
  for(i = 0; i < dynhds->hds_len; ++i) {
357
99.8k
    result = curlx_dyn_addf(dbuf, "%.*s: %.*s\r\n",
358
99.8k
                            (int)dynhds->hds[i]->namelen, dynhds->hds[i]->name,
359
99.8k
                            (int)dynhds->hds[i]->valuelen,
360
99.8k
                            dynhds->hds[i]->value);
361
99.8k
    if(result)
362
0
      break;
363
99.8k
  }
364
365
21.6k
  return result;
366
21.6k
}
367
368
#ifdef USE_NGHTTP2
369
370
nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount)
371
18.0k
{
372
18.0k
  nghttp2_nv *nva = curlx_calloc(1, sizeof(nghttp2_nv) * dynhds->hds_len);
373
18.0k
  size_t i;
374
375
18.0k
  *pcount = 0;
376
18.0k
  if(!nva)
377
0
    return NULL;
378
379
904k
  for(i = 0; i < dynhds->hds_len; ++i) {
380
886k
    struct dynhds_entry *e = dynhds->hds[i];
381
886k
    DEBUGASSERT(e);
382
886k
    nva[i].name = (unsigned char *)e->name;
383
886k
    nva[i].namelen = e->namelen;
384
886k
    nva[i].value = (unsigned char *)e->value;
385
886k
    nva[i].valuelen = e->valuelen;
386
886k
    nva[i].flags = NGHTTP2_NV_FLAG_NONE;
387
886k
  }
388
18.0k
  *pcount = dynhds->hds_len;
389
18.0k
  return nva;
390
18.0k
}
391
392
#endif /* USE_NGHTTP2 */