Coverage Report

Created: 2026-09-01 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/hsts.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
 * The Strict-Transport-Security header is defined in RFC 6797:
26
 * https://datatracker.ietf.org/doc/html/rfc6797
27
 */
28
#include "curl_setup.h"
29
30
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS)
31
#include "urldata.h"
32
#include "llist.h"
33
#include "hsts.h"
34
#include "curl_fopen.h"
35
#include "curl_get_line.h"
36
#include "parsedate.h"
37
#include "curl_share.h"
38
#include "curlx/strparse.h"
39
#include "curlx/strcopy.h"
40
41
12.8k
#define MAX_HSTS_LINE    4095
42
832
#define MAX_HSTS_HOSTLEN 2048
43
0
#define MAX_HSTS_DATELEN 17
44
0
#define UNLIMITED        "unlimited"
45
46
#if defined(DEBUGBUILD) || defined(UNITTESTS)
47
/* to play well with debug builds, we can *set* a fixed time this will
48
   return */
49
time_t deltatime; /* allow for "adjustments" for unit test purposes */
50
static time_t hsts_debugtime(void *unused)
51
832
{
52
832
  const char *timestr = getenv("CURL_TIME");
53
832
  (void)unused;
54
832
  if(timestr) {
55
0
    curl_off_t val;
56
0
    if(!curlx_str_number(&timestr, &val, TIME_T_MAX))
57
0
      val += (curl_off_t)deltatime;
58
0
    return (time_t)val;
59
0
  }
60
832
  return time(NULL);
61
832
}
62
#undef time
63
832
#define time(x) hsts_debugtime(x)
64
#endif
65
66
struct hsts *Curl_hsts_init(void)
67
17.1k
{
68
17.1k
  struct hsts *h = curlx_calloc(1, sizeof(struct hsts));
69
17.1k
  if(h) {
70
17.1k
    Curl_llist_init(&h->list, NULL);
71
17.1k
  }
72
17.1k
  return h;
73
17.1k
}
74
75
0
#define hsts_free(x) curlx_free(x)
76
77
void Curl_hsts_cleanup(struct hsts **hp)
78
36.3k
{
79
36.3k
  struct hsts *h = *hp;
80
36.3k
  if(h) {
81
17.1k
    struct Curl_llist_node *e;
82
17.1k
    struct Curl_llist_node *n;
83
17.1k
    for(e = Curl_llist_head(&h->list); e; e = n) {
84
0
      struct stsentry *sts = Curl_node_elem(e);
85
0
      n = Curl_node_next(e);
86
0
      hsts_free(sts);
87
0
    }
88
17.1k
    curlx_free(h->filename);
89
17.1k
    curlx_free(h);
90
17.1k
    *hp = NULL;
91
17.1k
  }
92
36.3k
}
93
94
/* append the new entry to the list after possibly removing an old entry
95
   first */
96
static void hsts_append(struct hsts *h, struct stsentry *sts)
97
0
{
98
0
  if(Curl_llist_count(&h->list) == MAX_HSTS_ENTRIES) {
99
    /* It is full. Remove the first entry in the list */
100
0
    struct Curl_llist_node *e = Curl_llist_head(&h->list);
101
0
    struct stsentry *oldsts = Curl_node_elem(e);
102
0
    Curl_node_remove(e);
103
0
    hsts_free(oldsts);
104
0
  }
105
0
  Curl_llist_append(&h->list, sts, &sts->node);
106
0
}
107
108
static CURLcode hsts_create(struct hsts *h,
109
                            const char *hostname,
110
                            size_t hlen,
111
                            bool subdomains,
112
                            curl_off_t expires)
113
0
{
114
0
  DEBUGASSERT(h);
115
0
  DEBUGASSERT(hostname);
116
117
0
  if(hlen && (hostname[hlen - 1] == '.'))
118
    /* strip off any trailing dot */
119
0
    --hlen;
120
0
  if(hlen) {
121
0
    struct stsentry *sts = curlx_calloc(1, sizeof(struct stsentry) + hlen);
122
0
    if(!sts)
123
0
      return CURLE_OUT_OF_MEMORY;
124
    /* the null-terminator is already there */
125
0
    memcpy(sts->host, hostname, hlen);
126
0
    sts->expires = expires;
127
0
    sts->includeSubDomains = subdomains;
128
0
    hsts_append(h, sts);
129
0
  }
130
0
  return CURLE_OK;
131
0
}
132
133
/* Copy all live entries from src into dst. Used by curl_easy_duphandle so the
134
 * clone inherits entries learned at runtime. E.g. Strict-Transport-Security.
135
 */
136
CURLcode Curl_hsts_copy(struct hsts *dst, struct hsts *src)
137
0
{
138
0
  struct Curl_llist_node *e;
139
0
  time_t now = time(NULL);
140
0
  for(e = Curl_llist_head(&src->list); e; e = Curl_node_next(e)) {
141
0
    struct stsentry *sts = Curl_node_elem(e);
142
0
    if(sts->expires > now) {
143
0
      CURLcode result = hsts_create(dst, sts->host, strlen(sts->host),
144
0
                                    sts->includeSubDomains != 0, sts->expires);
145
0
      if(result)
146
0
        return result;
147
0
    }
148
0
  }
149
0
  return CURLE_OK;
150
0
}
151
152
/*
153
 * Return the matching HSTS entry, or NULL if the given hostname is not
154
 * currently an HSTS one.
155
 *
156
 * The 'subdomain' argument tells the function if subdomain matching should be
157
 * attempted.
158
 *
159
 * @unittest 1660
160
 */
161
UNITTEST struct stsentry *hsts_check(struct hsts *h, const char *hostname,
162
                                     size_t hlen, bool subdomain);
163
UNITTEST struct stsentry *hsts_check(struct hsts *h, const char *hostname,
164
                                     size_t hlen, bool subdomain)
165
832
{
166
832
  struct stsentry *bestsub = NULL;
167
832
  if(h) {
168
832
    time_t now = time(NULL);
169
832
    struct Curl_llist_node *e;
170
832
    struct Curl_llist_node *n;
171
832
    size_t blen = 0;
172
173
832
    if((hlen > MAX_HSTS_HOSTLEN) || !hlen)
174
1
      return NULL;
175
831
    if(hostname[hlen - 1] == '.')
176
      /* remove the trailing dot */
177
13
      --hlen;
178
179
831
    for(e = Curl_llist_head(&h->list); e; e = n) {
180
0
      struct stsentry *sts = Curl_node_elem(e);
181
0
      size_t ntail;
182
0
      n = Curl_node_next(e);
183
0
      if(sts->expires <= now) {
184
        /* remove expired entries */
185
0
        Curl_node_remove(&sts->node);
186
0
        hsts_free(sts);
187
0
        continue;
188
0
      }
189
0
      ntail = strlen(sts->host);
190
0
      if((subdomain && sts->includeSubDomains) && (ntail < hlen)) {
191
0
        size_t offs = hlen - ntail;
192
0
        if((hostname[offs - 1] == '.') &&
193
0
           curl_strnequal(&hostname[offs], sts->host, ntail) &&
194
0
           (ntail > blen)) {
195
          /* save the tail match with the longest tail */
196
0
          bestsub = sts;
197
0
          blen = ntail;
198
0
        }
199
0
      }
200
      /* avoid curl_strequal because the hostname is not null-terminated */
201
0
      if((hlen == ntail) && curl_strnequal(hostname, sts->host, hlen))
202
0
        return sts;
203
0
    }
204
831
  }
205
831
  return bestsub;
206
832
}
207
208
CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
209
                         const char *header)
210
0
{
211
0
  const char *p = header;
212
0
  curl_off_t expires = 0;
213
0
  bool gotma = FALSE;
214
0
  bool gotinc = FALSE;
215
0
  bool subdomains = FALSE;
216
0
  struct stsentry *sts;
217
0
  time_t now = time(NULL);
218
0
  size_t hlen = strlen(hostname);
219
220
0
  if(Curl_host_is_ipnum(hostname))
221
    /* "explicit IP address identification of all forms is excluded."
222
       / RFC 6797 */
223
0
    return CURLE_OK;
224
225
0
  do {
226
0
    struct Curl_str word;
227
0
    struct Curl_str val = { 0 };
228
0
    int rc;
229
0
    bool assign = FALSE;
230
231
0
    do {
232
0
      curlx_str_passblanks(&p);
233
0
      if(*p == ';')
234
0
        p++;
235
0
      else
236
0
        break;
237
0
    } while(1);
238
0
    if(curlx_str_cspn(&p, &word, ";=\r\n \t"))
239
0
      break;
240
241
0
    curlx_str_passblanks(&p);
242
0
    if(!curlx_str_single(&p, '=')) {
243
0
      assign = TRUE;
244
0
      curlx_str_passblanks(&p);
245
246
0
      if(*p == '\"') {
247
0
        if(curlx_str_quotedword(&p, &val, MAX_HSTS_LINE))
248
0
          break;
249
0
      }
250
0
      else {
251
0
        if(curlx_str_cspn(&p, &val, ", ;\r\n"))
252
0
          break;
253
0
      }
254
0
    }
255
256
0
    if(assign && curlx_str_casecompare(&word, "max-age")) {
257
0
      const char *vp = curlx_str(&val);
258
0
      if(gotma)
259
0
        return CURLE_BAD_FUNCTION_ARGUMENT;
260
0
      rc = curlx_str_number(&vp, &expires, TIME_T_MAX);
261
0
      if(rc == STRE_OVERFLOW)
262
0
        expires = CURL_OFF_T_MAX;
263
0
      else if(rc)
264
        /* invalid max-age */
265
0
        return CURLE_BAD_FUNCTION_ARGUMENT;
266
267
0
      gotma = TRUE;
268
0
    }
269
0
    else if(curlx_str_casecompare(&word, "includesubdomains")) {
270
0
      if(gotinc)
271
0
        return CURLE_BAD_FUNCTION_ARGUMENT;
272
0
      subdomains = TRUE;
273
0
      gotinc = TRUE;
274
0
    }
275
0
  } while(*p);
276
277
0
  if(!gotma)
278
    /* max-age is mandatory */
279
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
280
281
0
  if(!expires) {
282
    /* remove the entry if present verbatim (without subdomain match) */
283
0
    sts = hsts_check(h, hostname, hlen, FALSE);
284
0
    if(sts) {
285
0
      Curl_node_remove(&sts->node);
286
0
      hsts_free(sts);
287
0
    }
288
0
    return CURLE_OK;
289
0
  }
290
291
0
  if(CURL_OFF_T_MAX - now < expires)
292
    /* would overflow, use maximum value */
293
0
    expires = CURL_OFF_T_MAX;
294
0
  else
295
0
    expires += now;
296
297
  /* check if it already exists */
298
0
  sts = hsts_check(h, hostname, hlen, FALSE);
299
0
  if(sts) {
300
    /* update these fields */
301
0
    sts->expires = expires;
302
0
    sts->includeSubDomains = subdomains;
303
0
  }
304
0
  else
305
0
    return hsts_create(h, hostname, hlen, subdomains, expires);
306
307
0
  return CURLE_OK;
308
0
}
309
310
/*
311
 * Send this HSTS entry to the write callback.
312
 */
313
static CURLcode hsts_push(struct Curl_easy *data,
314
                          struct curl_index *i,
315
                          struct stsentry *sts,
316
                          bool *stop)
317
0
{
318
0
  struct curl_hstsentry e;
319
0
  CURLSTScode sc;
320
0
  struct tm stamp;
321
0
  CURLcode result;
322
323
0
  e.name = (char *)sts->host;
324
0
  e.namelen = strlen(sts->host);
325
0
  e.includeSubDomains = sts->includeSubDomains;
326
327
0
  if(sts->expires != TIME_T_MAX) {
328
0
    result = curlx_gmtime((time_t)sts->expires, &stamp);
329
0
    if(result)
330
0
      return result;
331
332
0
    curl_msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d",
333
0
                   stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
334
0
                   stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
335
0
  }
336
0
  else
337
0
    curlx_strcopy(e.expire, sizeof(e.expire), STRCONST(UNLIMITED));
338
339
0
  sc = data->set.hsts_write(data, &e, i, data->set.hsts_write_userp);
340
0
  *stop = (sc != CURLSTS_OK);
341
0
  return sc == CURLSTS_FAIL ? CURLE_BAD_FUNCTION_ARGUMENT : CURLE_OK;
342
0
}
343
344
/*
345
 * Write this single hsts entry to a single output line
346
 */
347
static CURLcode hsts_out(struct stsentry *sts, FILE *fp)
348
0
{
349
0
  struct tm stamp;
350
0
  if(sts->expires != TIME_T_MAX) {
351
0
    CURLcode result = curlx_gmtime((time_t)sts->expires, &stamp);
352
0
    if(result)
353
0
      return result;
354
0
    curl_mfprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n",
355
0
                  sts->includeSubDomains ? "." : "", sts->host,
356
0
                  stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
357
0
                  stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
358
0
  }
359
0
  else
360
0
    curl_mfprintf(fp, "%s%s \"%s\"\n",
361
0
                  sts->includeSubDomains ? "." : "", sts->host, UNLIMITED);
362
0
  return CURLE_OK;
363
0
}
364
365
/*
366
 * Curl_https_save() writes the HSTS cache to file and callback.
367
 */
368
CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
369
                        const char *file)
370
36.3k
{
371
36.3k
  struct Curl_llist_node *e;
372
36.3k
  struct Curl_llist_node *n;
373
36.3k
  CURLcode result = CURLE_OK;
374
36.3k
  FILE *out;
375
36.3k
  char *tempstore = NULL;
376
377
36.3k
  if(!h)
378
    /* no cache activated */
379
19.1k
    return CURLE_OK;
380
381
  /* if no new name is given, use the one we stored from the load */
382
17.1k
  if(!file && h->filename)
383
0
    file = h->filename;
384
385
17.1k
  if((h->flags & CURLHSTS_READONLYFILE) || !file || !file[0])
386
    /* marked as read-only, no file or zero length filename */
387
2
    goto skipsave;
388
389
17.1k
  result = Curl_fopen(data, file, &out, &tempstore);
390
17.1k
  if(!result) {
391
17.1k
    fputs("# Your HSTS cache. https://curl.se/docs/hsts.html\n"
392
17.1k
          "# This file was generated by libcurl! Edit at your own risk.\n",
393
17.1k
          out);
394
17.1k
    for(e = Curl_llist_head(&h->list); e; e = n) {
395
0
      struct stsentry *sts = Curl_node_elem(e);
396
0
      n = Curl_node_next(e);
397
0
      result = hsts_out(sts, out);
398
0
      if(result)
399
0
        break;
400
0
    }
401
17.1k
    curlx_fclose(out);
402
17.1k
    if(!result && tempstore && curlx_rename(tempstore, file))
403
0
      result = CURLE_WRITE_ERROR;
404
405
17.1k
    if(result && tempstore)
406
0
      unlink(tempstore);
407
17.1k
  }
408
17.1k
  curlx_free(tempstore);
409
17.1k
skipsave:
410
17.1k
  if(data->set.hsts_write) {
411
    /* if there is a write callback */
412
0
    struct curl_index i; /* count */
413
0
    i.total = Curl_llist_count(&h->list);
414
0
    i.index = 0;
415
0
    for(e = Curl_llist_head(&h->list); e; e = n) {
416
0
      struct stsentry *sts = Curl_node_elem(e);
417
0
      bool stop;
418
0
      n = Curl_node_next(e);
419
0
      result = hsts_push(data, &i, sts, &stop);
420
0
      if(result || stop)
421
0
        break;
422
0
      i.index++;
423
0
    }
424
0
  }
425
17.1k
  return result;
426
17.1k
}
427
428
/* only returns SERIOUS errors */
429
static CURLcode hsts_add_host_expire(struct hsts *h,
430
                                     const char *host, size_t hostlen,
431
                                     const char *expire, size_t explen,
432
                                     bool subdomain) /* default */
433
0
{
434
0
  CURLcode result = CURLE_OK;
435
0
  struct stsentry *e;
436
0
  char dbuf[MAX_HSTS_DATELEN + 1];
437
0
  time_t expires = 0;
438
0
  time_t now = time(NULL);
439
440
  /* The date parser works on a null-terminated string. */
441
0
  if(explen > MAX_HSTS_DATELEN)
442
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
443
0
  memcpy(dbuf, expire, explen);
444
0
  dbuf[explen] = 0;
445
446
0
  if(!strcmp(dbuf, UNLIMITED))
447
0
    expires = TIME_T_MAX;
448
0
  else
449
0
    Curl_getdate_capped(dbuf, &expires);
450
451
0
  if(expires <= now)
452
    /* this entry already expired */
453
0
    return CURLE_OK;
454
455
0
  if(host[0] == '.') {
456
0
    host++;
457
0
    hostlen--;
458
0
    subdomain = TRUE;
459
0
  }
460
0
  if(hostlen && (host[hostlen - 1] == '.'))
461
    /* strip off any trailing dot */
462
0
    hostlen--;
463
464
0
  if(hostlen) {
465
    /* only add it if not already present */
466
0
    e = hsts_check(h, host, hostlen, subdomain);
467
0
    if(!e)
468
0
      result = hsts_create(h, host, hostlen, subdomain, expires);
469
    /* 'host' is not necessarily null-terminated */
470
0
    else if((hostlen == strlen(e->host) &&
471
0
             curl_strnequal(host, e->host, hostlen))) {
472
      /* the same hostname, use the largest expire time and keep the strictest
473
         subdomain policy */
474
0
      if(expires > e->expires)
475
0
        e->expires = expires;
476
0
      if(subdomain)
477
0
        e->includeSubDomains = TRUE;
478
0
    }
479
0
  }
480
0
  return result;
481
0
}
482
483
/* only returns SERIOUS errors */
484
static CURLcode hsts_add(struct hsts *h, const char *line)
485
0
{
486
  /* Example lines:
487
     example.com "20191231 10:00:00"
488
     .example.net "20191231 10:00:00"
489
   */
490
0
  struct Curl_str host;
491
0
  struct Curl_str date;
492
493
0
  if(curlx_str_word(&line, &host, MAX_HSTS_HOSTLEN) ||
494
0
     curlx_str_singlespace(&line) ||
495
0
     curlx_str_quotedword(&line, &date, MAX_HSTS_DATELEN) ||
496
0
     curlx_str_newline(&line))
497
0
    ;
498
0
  else {
499
0
    return hsts_add_host_expire(h, curlx_str(&host), curlx_strlen(&host),
500
0
                                curlx_str(&date), curlx_strlen(&date),
501
0
                                FALSE);
502
0
  }
503
504
0
  return CURLE_OK;
505
0
}
506
507
/*
508
 * Load HSTS data from callback.
509
 *
510
 */
511
static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
512
12.8k
{
513
  /* if the HSTS read callback is set, use it */
514
12.8k
  if(data->set.hsts_read) {
515
0
    CURLSTScode sc;
516
0
    DEBUGASSERT(h);
517
0
    do {
518
0
      char buffer[MAX_HSTS_HOSTLEN + 1];
519
0
      struct curl_hstsentry e;
520
0
      e.name = buffer;
521
0
      e.namelen = sizeof(buffer) - 1;
522
0
      e.includeSubDomains = FALSE; /* default */
523
0
      e.expire[0] = 0;
524
0
      e.expire[MAX_HSTS_DATELEN] = 0;
525
0
      e.name[0] = 0; /* to make it clean */
526
0
      e.name[MAX_HSTS_HOSTLEN] = 0;
527
0
      sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp);
528
0
      if(sc == CURLSTS_OK) {
529
0
        CURLcode result;
530
0
        const char *date = e.expire;
531
0
        if(!e.name[0] || e.expire[MAX_HSTS_DATELEN] ||
532
0
           e.name[MAX_HSTS_HOSTLEN])
533
          /* bail out if no name was stored or if a null-terminator is gone */
534
0
          return CURLE_BAD_FUNCTION_ARGUMENT;
535
0
        if(!date[0])
536
0
          date = UNLIMITED;
537
0
        result = hsts_add_host_expire(h, e.name, strlen(e.name),
538
0
                                      date, strlen(date),
539
                                      /* bitfield to bool conversion: */
540
0
                                      e.includeSubDomains ? TRUE : FALSE);
541
0
        if(result)
542
0
          return result;
543
0
      }
544
0
      else if(sc == CURLSTS_FAIL)
545
0
        return CURLE_ABORTED_BY_CALLBACK;
546
0
    } while(sc == CURLSTS_OK);
547
0
  }
548
12.8k
  return CURLE_OK;
549
12.8k
}
550
551
/*
552
 * Load the HSTS cache from the given file. The text based line-oriented file
553
 * format is documented here: https://curl.se/docs/hsts.html
554
 *
555
 * This function only returns error on major problems that prevent hsts
556
 * handling to work completely. It will ignore individual syntactical errors
557
 * etc.
558
 */
559
static CURLcode hsts_load(struct hsts *h, const char *file)
560
12.8k
{
561
12.8k
  CURLcode result = CURLE_OK;
562
12.8k
  FILE *fp;
563
564
  /* we need a private copy of the filename so that the hsts cache file
565
     name survives an easy handle reset */
566
12.8k
  curlx_free(h->filename);
567
12.8k
  h->filename = curlx_strdup(file);
568
12.8k
  if(!h->filename)
569
0
    return CURLE_OUT_OF_MEMORY;
570
571
12.8k
  fp = curlx_fopen(file, FOPEN_READTEXT);
572
12.8k
  if(fp) {
573
12.8k
    curlx_struct_stat stat;
574
12.8k
    if((curlx_fstat(fileno(fp), &stat) == -1) || !S_ISDIR(stat.st_mode)) {
575
12.8k
      struct dynbuf buf;
576
12.8k
      bool eof = FALSE;
577
12.8k
      curlx_dyn_init(&buf, MAX_HSTS_LINE);
578
12.8k
      do {
579
12.8k
        result = Curl_get_line(&buf, fp, &eof);
580
12.8k
        if(!result) {
581
12.8k
          const char *lineptr = curlx_dyn_ptr(&buf);
582
12.8k
          curlx_str_passblanks(&lineptr);
583
584
          /* Skip empty or commented lines, since we know the line will have
585
             a trailing newline from Curl_get_line we can treat length 1 as
586
             empty. */
587
12.8k
          if((*lineptr == '#') || strlen(lineptr) <= 1)
588
12.8k
            continue;
589
590
0
          hsts_add(h, lineptr);
591
0
        }
592
12.8k
      } while(!result && !eof);
593
12.8k
      curlx_dyn_free(&buf); /* free the line buffer */
594
12.8k
    }
595
12.8k
    curlx_fclose(fp);
596
12.8k
  }
597
12.8k
  return result;
598
12.8k
}
599
600
/*
601
 * Curl_hsts_loadfile() loads HSTS from file
602
 */
603
CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
604
                            struct hsts *h, const char *file)
605
12.8k
{
606
12.8k
  DEBUGASSERT(h);
607
12.8k
  (void)data;
608
12.8k
  return hsts_load(h, file);
609
12.8k
}
610
611
/*
612
 * Curl_hsts_loadcb() loads HSTS from callback
613
 */
614
CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h)
615
12.8k
{
616
12.8k
  if(h)
617
12.8k
    return hsts_pull(data, h);
618
0
  return CURLE_OK;
619
12.8k
}
620
621
CURLcode Curl_hsts_loadfiles(struct Curl_easy *data)
622
12.8k
{
623
12.8k
  CURLcode result = CURLE_OK;
624
12.8k
  struct curl_slist *l = data->state.hstslist;
625
12.8k
  if(l) {
626
12.8k
    Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE);
627
628
25.7k
    while(l) {
629
12.8k
      result = Curl_hsts_loadfile(data, data->hsts, l->data);
630
12.8k
      if(result)
631
0
        break;
632
12.8k
      l = l->next;
633
12.8k
    }
634
12.8k
    Curl_share_unlock(data, CURL_LOCK_DATA_HSTS);
635
12.8k
  }
636
12.8k
  return result;
637
12.8k
}
638
639
bool Curl_hsts_applies(struct hsts *h, const struct Curl_peer *dest)
640
832
{
641
  return !!hsts_check(h, dest->hostname, strlen(dest->hostname), TRUE);
642
832
}
643
644
#if defined(DEBUGBUILD) || defined(UNITTESTS)
645
#undef time
646
#endif
647
648
#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */