Coverage Report

Created: 2026-09-14 07:06

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