Coverage Report

Created: 2026-07-16 06:10

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
#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
0
{
52
0
  const char *timestr = getenv("CURL_TIME");
53
0
  (void)unused;
54
0
  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
0
  return time(NULL);
61
0
}
62
#undef time
63
0
#define time(x) hsts_debugtime(x)
64
#endif
65
66
struct hsts *Curl_hsts_init(void)
67
4.73k
{
68
4.73k
  struct hsts *h = curlx_calloc(1, sizeof(struct hsts));
69
4.73k
  if(h) {
70
4.73k
    Curl_llist_init(&h->list, NULL);
71
4.73k
  }
72
4.73k
  return h;
73
4.73k
}
74
75
0
#define hsts_free(x) curlx_free(x)
76
77
void Curl_hsts_cleanup(struct hsts **hp)
78
6.82k
{
79
6.82k
  struct hsts *h = *hp;
80
6.82k
  if(h) {
81
4.73k
    struct Curl_llist_node *e;
82
4.73k
    struct Curl_llist_node *n;
83
4.73k
    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
4.73k
    curlx_free(h->filename);
89
4.73k
    curlx_free(h);
90
4.73k
    *hp = NULL;
91
4.73k
  }
92
6.82k
}
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
0
{
166
0
  struct stsentry *bestsub = NULL;
167
0
  if(h) {
168
0
    time_t now = time(NULL);
169
0
    struct Curl_llist_node *e;
170
0
    struct Curl_llist_node *n;
171
0
    size_t blen = 0;
172
173
0
    if((hlen > MAX_HSTS_HOSTLEN) || !hlen)
174
0
      return NULL;
175
0
    if(hostname[hlen - 1] == '.')
176
      /* remove the trailing dot */
177
0
      --hlen;
178
179
0
    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
0
  }
205
0
  return bestsub;
206
0
}
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
    curlx_str_passblanks(&p);
227
0
    if(curl_strnequal("max-age", p, 7)) {
228
0
      bool quoted = FALSE;
229
0
      int rc;
230
231
0
      if(gotma)
232
0
        return CURLE_BAD_FUNCTION_ARGUMENT;
233
234
0
      p += 7;
235
0
      curlx_str_passblanks(&p);
236
0
      if(curlx_str_single(&p, '='))
237
0
        return CURLE_BAD_FUNCTION_ARGUMENT;
238
0
      curlx_str_passblanks(&p);
239
240
0
      if(!curlx_str_single(&p, '\"'))
241
0
        quoted = TRUE;
242
243
0
      rc = curlx_str_number(&p, &expires, TIME_T_MAX);
244
0
      if(rc == STRE_OVERFLOW)
245
0
        expires = CURL_OFF_T_MAX;
246
0
      else if(rc)
247
        /* invalid max-age */
248
0
        return CURLE_BAD_FUNCTION_ARGUMENT;
249
250
0
      if(quoted) {
251
0
        if(*p != '\"')
252
0
          return CURLE_BAD_FUNCTION_ARGUMENT;
253
0
        p++;
254
0
      }
255
0
      gotma = TRUE;
256
0
    }
257
0
    else if(curl_strnequal("includesubdomains", p, 17)) {
258
0
      if(gotinc)
259
0
        return CURLE_BAD_FUNCTION_ARGUMENT;
260
0
      subdomains = TRUE;
261
0
      p += 17;
262
0
      gotinc = TRUE;
263
0
    }
264
0
    else {
265
      /* unknown directive, do a lame attempt to skip */
266
0
      while(*p && (*p != ';'))
267
0
        p++;
268
0
    }
269
270
0
    curlx_str_passblanks(&p);
271
0
    if(*p == ';')
272
0
      p++;
273
0
  } while(*p);
274
275
0
  if(!gotma)
276
    /* max-age is mandatory */
277
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
278
279
0
  if(!expires) {
280
    /* remove the entry if present verbatim (without subdomain match) */
281
0
    sts = hsts_check(h, hostname, hlen, FALSE);
282
0
    if(sts) {
283
0
      Curl_node_remove(&sts->node);
284
0
      hsts_free(sts);
285
0
    }
286
0
    return CURLE_OK;
287
0
  }
288
289
0
  if(CURL_OFF_T_MAX - now < expires)
290
    /* would overflow, use maximum value */
291
0
    expires = CURL_OFF_T_MAX;
292
0
  else
293
0
    expires += now;
294
295
  /* check if it already exists */
296
0
  sts = hsts_check(h, hostname, hlen, FALSE);
297
0
  if(sts) {
298
    /* update these fields */
299
0
    sts->expires = expires;
300
0
    sts->includeSubDomains = subdomains;
301
0
  }
302
0
  else
303
0
    return hsts_create(h, hostname, hlen, subdomains, expires);
304
305
0
  return CURLE_OK;
306
0
}
307
308
/*
309
 * Send this HSTS entry to the write callback.
310
 */
311
static CURLcode hsts_push(struct Curl_easy *data,
312
                          struct curl_index *i,
313
                          struct stsentry *sts,
314
                          bool *stop)
315
0
{
316
0
  struct curl_hstsentry e;
317
0
  CURLSTScode sc;
318
0
  struct tm stamp;
319
0
  CURLcode result;
320
321
0
  e.name = (char *)sts->host;
322
0
  e.namelen = strlen(sts->host);
323
0
  e.includeSubDomains = sts->includeSubDomains;
324
325
0
  if(sts->expires != TIME_T_MAX) {
326
0
    result = curlx_gmtime((time_t)sts->expires, &stamp);
327
0
    if(result)
328
0
      return result;
329
330
0
    curl_msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d",
331
0
                   stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
332
0
                   stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
333
0
  }
334
0
  else
335
0
    curlx_strcopy(e.expire, sizeof(e.expire), STRCONST(UNLIMITED));
336
337
0
  sc = data->set.hsts_write(data, &e, i, data->set.hsts_write_userp);
338
0
  *stop = (sc != CURLSTS_OK);
339
0
  return sc == CURLSTS_FAIL ? CURLE_BAD_FUNCTION_ARGUMENT : CURLE_OK;
340
0
}
341
342
/*
343
 * Write this single hsts entry to a single output line
344
 */
345
static CURLcode hsts_out(struct stsentry *sts, FILE *fp)
346
0
{
347
0
  struct tm stamp;
348
0
  if(sts->expires != TIME_T_MAX) {
349
0
    CURLcode result = curlx_gmtime((time_t)sts->expires, &stamp);
350
0
    if(result)
351
0
      return result;
352
0
    curl_mfprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n",
353
0
                  sts->includeSubDomains ? "." : "", sts->host,
354
0
                  stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
355
0
                  stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
356
0
  }
357
0
  else
358
0
    curl_mfprintf(fp, "%s%s \"%s\"\n",
359
0
                  sts->includeSubDomains ? "." : "", sts->host, UNLIMITED);
360
0
  return CURLE_OK;
361
0
}
362
363
/*
364
 * Curl_https_save() writes the HSTS cache to file and callback.
365
 */
366
CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
367
                        const char *file)
368
6.82k
{
369
6.82k
  struct Curl_llist_node *e;
370
6.82k
  struct Curl_llist_node *n;
371
6.82k
  CURLcode result = CURLE_OK;
372
6.82k
  FILE *out;
373
6.82k
  char *tempstore = NULL;
374
375
6.82k
  if(!h)
376
    /* no cache activated */
377
2.08k
    return CURLE_OK;
378
379
  /* if no new name is given, use the one we stored from the load */
380
4.73k
  if(!file && h->filename)
381
0
    file = h->filename;
382
383
4.73k
  if((h->flags & CURLHSTS_READONLYFILE) || !file || !file[0])
384
    /* marked as read-only, no file or zero length filename */
385
3
    goto skipsave;
386
387
4.73k
  result = Curl_fopen(data, file, &out, &tempstore);
388
4.73k
  if(!result) {
389
4.73k
    fputs("# Your HSTS cache. https://curl.se/docs/hsts.html\n"
390
4.73k
          "# This file was generated by libcurl! Edit at your own risk.\n",
391
4.73k
          out);
392
4.73k
    for(e = Curl_llist_head(&h->list); e; e = n) {
393
0
      struct stsentry *sts = Curl_node_elem(e);
394
0
      n = Curl_node_next(e);
395
0
      result = hsts_out(sts, out);
396
0
      if(result)
397
0
        break;
398
0
    }
399
4.73k
    curlx_fclose(out);
400
4.73k
    if(!result && tempstore && curlx_rename(tempstore, file))
401
0
      result = CURLE_WRITE_ERROR;
402
403
4.73k
    if(result && tempstore)
404
0
      unlink(tempstore);
405
4.73k
  }
406
4.73k
  curlx_free(tempstore);
407
4.73k
skipsave:
408
4.73k
  if(data->set.hsts_write) {
409
    /* if there is a write callback */
410
0
    struct curl_index i; /* count */
411
0
    i.total = Curl_llist_count(&h->list);
412
0
    i.index = 0;
413
0
    for(e = Curl_llist_head(&h->list); e; e = n) {
414
0
      struct stsentry *sts = Curl_node_elem(e);
415
0
      bool stop;
416
0
      n = Curl_node_next(e);
417
0
      result = hsts_push(data, &i, sts, &stop);
418
0
      if(result || stop)
419
0
        break;
420
0
      i.index++;
421
0
    }
422
0
  }
423
4.73k
  return result;
424
4.73k
}
425
426
/* only returns SERIOUS errors */
427
static CURLcode hsts_add_host_expire(struct hsts *h,
428
                                     const char *host, size_t hostlen,
429
                                     const char *expire, size_t explen,
430
                                     bool subdomain) /* default */
431
0
{
432
0
  CURLcode result = CURLE_OK;
433
0
  struct stsentry *e;
434
0
  char dbuf[MAX_HSTS_DATELEN + 1];
435
0
  time_t expires = 0;
436
0
  time_t now = time(NULL);
437
438
  /* The date parser works on a null-terminated string. */
439
0
  if(explen > MAX_HSTS_DATELEN)
440
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
441
0
  memcpy(dbuf, expire, explen);
442
0
  dbuf[explen] = 0;
443
444
0
  if(!strcmp(dbuf, UNLIMITED))
445
0
    expires = TIME_T_MAX;
446
0
  else
447
0
    Curl_getdate_capped(dbuf, &expires);
448
449
0
  if(expires <= now)
450
    /* this entry already expired */
451
0
    return CURLE_OK;
452
453
0
  if(host[0] == '.') {
454
0
    host++;
455
0
    hostlen--;
456
0
    subdomain = TRUE;
457
0
  }
458
0
  if(hostlen && (host[hostlen - 1] == '.'))
459
    /* strip off any trailing dot */
460
0
    hostlen--;
461
462
0
  if(hostlen) {
463
    /* only add it if not already present */
464
0
    e = hsts_check(h, host, hostlen, subdomain);
465
0
    if(!e)
466
0
      result = hsts_create(h, host, hostlen, subdomain, expires);
467
    /* 'host' is not necessarily null-terminated */
468
0
    else if((hostlen == strlen(e->host) &&
469
0
             curl_strnequal(host, e->host, hostlen))) {
470
      /* the same hostname, use the largest expire time and keep the strictest
471
         subdomain policy */
472
0
      if(expires > e->expires)
473
0
        e->expires = expires;
474
0
      if(subdomain)
475
0
        e->includeSubDomains = TRUE;
476
0
    }
477
0
  }
478
0
  return result;
479
0
}
480
481
/* only returns SERIOUS errors */
482
static CURLcode hsts_add(struct hsts *h, const char *line)
483
0
{
484
  /* Example lines:
485
     example.com "20191231 10:00:00"
486
     .example.net "20191231 10:00:00"
487
   */
488
0
  struct Curl_str host;
489
0
  struct Curl_str date;
490
491
0
  if(curlx_str_word(&line, &host, MAX_HSTS_HOSTLEN) ||
492
0
     curlx_str_singlespace(&line) ||
493
0
     curlx_str_quotedword(&line, &date, MAX_HSTS_DATELEN) ||
494
0
     curlx_str_newline(&line))
495
0
    ;
496
0
  else {
497
0
    return hsts_add_host_expire(h, curlx_str(&host), curlx_strlen(&host),
498
0
                                curlx_str(&date), curlx_strlen(&date),
499
0
                                FALSE);
500
0
  }
501
502
0
  return CURLE_OK;
503
0
}
504
505
/*
506
 * Load HSTS data from callback.
507
 *
508
 */
509
static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
510
0
{
511
  /* if the HSTS read callback is set, use it */
512
0
  if(data->set.hsts_read) {
513
0
    CURLSTScode sc;
514
0
    DEBUGASSERT(h);
515
0
    do {
516
0
      char buffer[MAX_HSTS_HOSTLEN + 1];
517
0
      struct curl_hstsentry e;
518
0
      e.name = buffer;
519
0
      e.namelen = sizeof(buffer) - 1;
520
0
      e.includeSubDomains = FALSE; /* default */
521
0
      e.expire[0] = 0;
522
0
      e.expire[MAX_HSTS_DATELEN] = 0;
523
0
      e.name[0] = 0; /* to make it clean */
524
0
      e.name[MAX_HSTS_HOSTLEN] = 0;
525
0
      sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp);
526
0
      if(sc == CURLSTS_OK) {
527
0
        CURLcode result;
528
0
        const char *date = e.expire;
529
0
        if(!e.name[0] || e.expire[MAX_HSTS_DATELEN] ||
530
0
           e.name[MAX_HSTS_HOSTLEN])
531
          /* bail out if no name was stored or if a null-terminator is gone */
532
0
          return CURLE_BAD_FUNCTION_ARGUMENT;
533
0
        if(!date[0])
534
0
          date = UNLIMITED;
535
0
        result = hsts_add_host_expire(h, e.name, strlen(e.name),
536
0
                                      date, strlen(date),
537
                                      /* bitfield to bool conversion: */
538
0
                                      e.includeSubDomains ? TRUE : FALSE);
539
0
        if(result)
540
0
          return result;
541
0
      }
542
0
      else if(sc == CURLSTS_FAIL)
543
0
        return CURLE_ABORTED_BY_CALLBACK;
544
0
    } while(sc == CURLSTS_OK);
545
0
  }
546
0
  return CURLE_OK;
547
0
}
548
549
/*
550
 * Load the HSTS cache from the given file. The text based line-oriented file
551
 * format is documented here: https://curl.se/docs/hsts.html
552
 *
553
 * This function only returns error on major problems that prevent hsts
554
 * handling to work completely. It will ignore individual syntactical errors
555
 * etc.
556
 */
557
static CURLcode hsts_load(struct hsts *h, const char *file)
558
0
{
559
0
  CURLcode result = CURLE_OK;
560
0
  FILE *fp;
561
562
  /* we need a private copy of the filename so that the hsts cache file
563
     name survives an easy handle reset */
564
0
  curlx_free(h->filename);
565
0
  h->filename = curlx_strdup(file);
566
0
  if(!h->filename)
567
0
    return CURLE_OUT_OF_MEMORY;
568
569
0
  fp = curlx_fopen(file, FOPEN_READTEXT);
570
0
  if(fp) {
571
0
    curlx_struct_stat stat;
572
0
    if((curlx_fstat(fileno(fp), &stat) == -1) || !S_ISDIR(stat.st_mode)) {
573
0
      struct dynbuf buf;
574
0
      bool eof = FALSE;
575
0
      curlx_dyn_init(&buf, MAX_HSTS_LINE);
576
0
      do {
577
0
        result = Curl_get_line(&buf, fp, &eof);
578
0
        if(!result) {
579
0
          const char *lineptr = curlx_dyn_ptr(&buf);
580
0
          curlx_str_passblanks(&lineptr);
581
582
          /* Skip empty or commented lines, since we know the line will have
583
             a trailing newline from Curl_get_line we can treat length 1 as
584
             empty. */
585
0
          if((*lineptr == '#') || strlen(lineptr) <= 1)
586
0
            continue;
587
588
0
          hsts_add(h, lineptr);
589
0
        }
590
0
      } while(!result && !eof);
591
0
      curlx_dyn_free(&buf); /* free the line buffer */
592
0
    }
593
0
    curlx_fclose(fp);
594
0
  }
595
0
  return result;
596
0
}
597
598
/*
599
 * Curl_hsts_loadfile() loads HSTS from file
600
 */
601
CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
602
                            struct hsts *h, const char *file)
603
0
{
604
0
  DEBUGASSERT(h);
605
0
  (void)data;
606
0
  return hsts_load(h, file);
607
0
}
608
609
/*
610
 * Curl_hsts_loadcb() loads HSTS from callback
611
 */
612
CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h)
613
0
{
614
0
  if(h)
615
0
    return hsts_pull(data, h);
616
0
  return CURLE_OK;
617
0
}
618
619
CURLcode Curl_hsts_loadfiles(struct Curl_easy *data)
620
0
{
621
0
  CURLcode result = CURLE_OK;
622
0
  struct curl_slist *l = data->state.hstslist;
623
0
  if(l) {
624
0
    Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE);
625
626
0
    while(l) {
627
0
      result = Curl_hsts_loadfile(data, data->hsts, l->data);
628
0
      if(result)
629
0
        break;
630
0
      l = l->next;
631
0
    }
632
0
    Curl_share_unlock(data, CURL_LOCK_DATA_HSTS);
633
0
  }
634
0
  return result;
635
0
}
636
637
bool Curl_hsts_applies(struct hsts *h, const struct Curl_peer *dest)
638
0
{
639
  return !!hsts_check(h, dest->hostname, strlen(dest->hostname), TRUE);
640
0
}
641
642
#if defined(DEBUGBUILD) || defined(UNITTESTS)
643
#undef time
644
#endif
645
646
#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */