113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci/*
2513498266Sopenharmony_ci * The Alt-Svc: header is defined in RFC 7838:
2613498266Sopenharmony_ci * https://datatracker.ietf.org/doc/html/rfc7838
2713498266Sopenharmony_ci */
2813498266Sopenharmony_ci#include "curl_setup.h"
2913498266Sopenharmony_ci
3013498266Sopenharmony_ci#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC)
3113498266Sopenharmony_ci#include <curl/curl.h>
3213498266Sopenharmony_ci#include "urldata.h"
3313498266Sopenharmony_ci#include "altsvc.h"
3413498266Sopenharmony_ci#include "curl_get_line.h"
3513498266Sopenharmony_ci#include "strcase.h"
3613498266Sopenharmony_ci#include "parsedate.h"
3713498266Sopenharmony_ci#include "sendf.h"
3813498266Sopenharmony_ci#include "warnless.h"
3913498266Sopenharmony_ci#include "fopen.h"
4013498266Sopenharmony_ci#include "rename.h"
4113498266Sopenharmony_ci#include "strdup.h"
4213498266Sopenharmony_ci#include "inet_pton.h"
4313498266Sopenharmony_ci
4413498266Sopenharmony_ci/* The last 3 #include files should be in this order */
4513498266Sopenharmony_ci#include "curl_printf.h"
4613498266Sopenharmony_ci#include "curl_memory.h"
4713498266Sopenharmony_ci#include "memdebug.h"
4813498266Sopenharmony_ci
4913498266Sopenharmony_ci#define MAX_ALTSVC_LINE 4095
5013498266Sopenharmony_ci#define MAX_ALTSVC_DATELENSTR "64"
5113498266Sopenharmony_ci#define MAX_ALTSVC_DATELEN 64
5213498266Sopenharmony_ci#define MAX_ALTSVC_HOSTLENSTR "512"
5313498266Sopenharmony_ci#define MAX_ALTSVC_HOSTLEN 512
5413498266Sopenharmony_ci#define MAX_ALTSVC_ALPNLENSTR "10"
5513498266Sopenharmony_ci#define MAX_ALTSVC_ALPNLEN 10
5613498266Sopenharmony_ci
5713498266Sopenharmony_ci#define H3VERSION "h3"
5813498266Sopenharmony_ci
5913498266Sopenharmony_cistatic enum alpnid alpn2alpnid(char *name)
6013498266Sopenharmony_ci{
6113498266Sopenharmony_ci  if(strcasecompare(name, "h1"))
6213498266Sopenharmony_ci    return ALPN_h1;
6313498266Sopenharmony_ci  if(strcasecompare(name, "h2"))
6413498266Sopenharmony_ci    return ALPN_h2;
6513498266Sopenharmony_ci  if(strcasecompare(name, H3VERSION))
6613498266Sopenharmony_ci    return ALPN_h3;
6713498266Sopenharmony_ci  return ALPN_none; /* unknown, probably rubbish input */
6813498266Sopenharmony_ci}
6913498266Sopenharmony_ci
7013498266Sopenharmony_ci/* Given the ALPN ID, return the name */
7113498266Sopenharmony_ciconst char *Curl_alpnid2str(enum alpnid id)
7213498266Sopenharmony_ci{
7313498266Sopenharmony_ci  switch(id) {
7413498266Sopenharmony_ci  case ALPN_h1:
7513498266Sopenharmony_ci    return "h1";
7613498266Sopenharmony_ci  case ALPN_h2:
7713498266Sopenharmony_ci    return "h2";
7813498266Sopenharmony_ci  case ALPN_h3:
7913498266Sopenharmony_ci    return H3VERSION;
8013498266Sopenharmony_ci  default:
8113498266Sopenharmony_ci    return ""; /* bad */
8213498266Sopenharmony_ci  }
8313498266Sopenharmony_ci}
8413498266Sopenharmony_ci
8513498266Sopenharmony_ci
8613498266Sopenharmony_cistatic void altsvc_free(struct altsvc *as)
8713498266Sopenharmony_ci{
8813498266Sopenharmony_ci  free(as->src.host);
8913498266Sopenharmony_ci  free(as->dst.host);
9013498266Sopenharmony_ci  free(as);
9113498266Sopenharmony_ci}
9213498266Sopenharmony_ci
9313498266Sopenharmony_cistatic struct altsvc *altsvc_createid(const char *srchost,
9413498266Sopenharmony_ci                                      const char *dsthost,
9513498266Sopenharmony_ci                                      enum alpnid srcalpnid,
9613498266Sopenharmony_ci                                      enum alpnid dstalpnid,
9713498266Sopenharmony_ci                                      unsigned int srcport,
9813498266Sopenharmony_ci                                      unsigned int dstport)
9913498266Sopenharmony_ci{
10013498266Sopenharmony_ci  struct altsvc *as = calloc(1, sizeof(struct altsvc));
10113498266Sopenharmony_ci  size_t hlen;
10213498266Sopenharmony_ci  size_t dlen;
10313498266Sopenharmony_ci  if(!as)
10413498266Sopenharmony_ci    return NULL;
10513498266Sopenharmony_ci  hlen = strlen(srchost);
10613498266Sopenharmony_ci  dlen = strlen(dsthost);
10713498266Sopenharmony_ci  DEBUGASSERT(hlen);
10813498266Sopenharmony_ci  DEBUGASSERT(dlen);
10913498266Sopenharmony_ci  if(!hlen || !dlen) {
11013498266Sopenharmony_ci    /* bad input */
11113498266Sopenharmony_ci    free(as);
11213498266Sopenharmony_ci    return NULL;
11313498266Sopenharmony_ci  }
11413498266Sopenharmony_ci  if((hlen > 2) && srchost[0] == '[') {
11513498266Sopenharmony_ci    /* IPv6 address, strip off brackets */
11613498266Sopenharmony_ci    srchost++;
11713498266Sopenharmony_ci    hlen -= 2;
11813498266Sopenharmony_ci  }
11913498266Sopenharmony_ci  else if(srchost[hlen - 1] == '.')
12013498266Sopenharmony_ci    /* strip off trailing dot */
12113498266Sopenharmony_ci    hlen--;
12213498266Sopenharmony_ci  if((dlen > 2) && dsthost[0] == '[') {
12313498266Sopenharmony_ci    /* IPv6 address, strip off brackets */
12413498266Sopenharmony_ci    dsthost++;
12513498266Sopenharmony_ci    dlen -= 2;
12613498266Sopenharmony_ci  }
12713498266Sopenharmony_ci
12813498266Sopenharmony_ci  as->src.host = Curl_memdup0(srchost, hlen);
12913498266Sopenharmony_ci  if(!as->src.host)
13013498266Sopenharmony_ci    goto error;
13113498266Sopenharmony_ci
13213498266Sopenharmony_ci  as->dst.host = Curl_memdup0(dsthost, dlen);
13313498266Sopenharmony_ci  if(!as->dst.host)
13413498266Sopenharmony_ci    goto error;
13513498266Sopenharmony_ci
13613498266Sopenharmony_ci  as->src.alpnid = srcalpnid;
13713498266Sopenharmony_ci  as->dst.alpnid = dstalpnid;
13813498266Sopenharmony_ci  as->src.port = curlx_ultous(srcport);
13913498266Sopenharmony_ci  as->dst.port = curlx_ultous(dstport);
14013498266Sopenharmony_ci
14113498266Sopenharmony_ci  return as;
14213498266Sopenharmony_cierror:
14313498266Sopenharmony_ci  altsvc_free(as);
14413498266Sopenharmony_ci  return NULL;
14513498266Sopenharmony_ci}
14613498266Sopenharmony_ci
14713498266Sopenharmony_cistatic struct altsvc *altsvc_create(char *srchost,
14813498266Sopenharmony_ci                                    char *dsthost,
14913498266Sopenharmony_ci                                    char *srcalpn,
15013498266Sopenharmony_ci                                    char *dstalpn,
15113498266Sopenharmony_ci                                    unsigned int srcport,
15213498266Sopenharmony_ci                                    unsigned int dstport)
15313498266Sopenharmony_ci{
15413498266Sopenharmony_ci  enum alpnid dstalpnid = alpn2alpnid(dstalpn);
15513498266Sopenharmony_ci  enum alpnid srcalpnid = alpn2alpnid(srcalpn);
15613498266Sopenharmony_ci  if(!srcalpnid || !dstalpnid)
15713498266Sopenharmony_ci    return NULL;
15813498266Sopenharmony_ci  return altsvc_createid(srchost, dsthost, srcalpnid, dstalpnid,
15913498266Sopenharmony_ci                         srcport, dstport);
16013498266Sopenharmony_ci}
16113498266Sopenharmony_ci
16213498266Sopenharmony_ci/* only returns SERIOUS errors */
16313498266Sopenharmony_cistatic CURLcode altsvc_add(struct altsvcinfo *asi, char *line)
16413498266Sopenharmony_ci{
16513498266Sopenharmony_ci  /* Example line:
16613498266Sopenharmony_ci     h2 example.com 443 h3 shiny.example.com 8443 "20191231 10:00:00" 1
16713498266Sopenharmony_ci   */
16813498266Sopenharmony_ci  char srchost[MAX_ALTSVC_HOSTLEN + 1];
16913498266Sopenharmony_ci  char dsthost[MAX_ALTSVC_HOSTLEN + 1];
17013498266Sopenharmony_ci  char srcalpn[MAX_ALTSVC_ALPNLEN + 1];
17113498266Sopenharmony_ci  char dstalpn[MAX_ALTSVC_ALPNLEN + 1];
17213498266Sopenharmony_ci  char date[MAX_ALTSVC_DATELEN + 1];
17313498266Sopenharmony_ci  unsigned int srcport;
17413498266Sopenharmony_ci  unsigned int dstport;
17513498266Sopenharmony_ci  unsigned int prio;
17613498266Sopenharmony_ci  unsigned int persist;
17713498266Sopenharmony_ci  int rc;
17813498266Sopenharmony_ci
17913498266Sopenharmony_ci  rc = sscanf(line,
18013498266Sopenharmony_ci              "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u "
18113498266Sopenharmony_ci              "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u "
18213498266Sopenharmony_ci              "\"%" MAX_ALTSVC_DATELENSTR "[^\"]\" %u %u",
18313498266Sopenharmony_ci              srcalpn, srchost, &srcport,
18413498266Sopenharmony_ci              dstalpn, dsthost, &dstport,
18513498266Sopenharmony_ci              date, &persist, &prio);
18613498266Sopenharmony_ci  if(9 == rc) {
18713498266Sopenharmony_ci    struct altsvc *as;
18813498266Sopenharmony_ci    time_t expires = Curl_getdate_capped(date);
18913498266Sopenharmony_ci    as = altsvc_create(srchost, dsthost, srcalpn, dstalpn, srcport, dstport);
19013498266Sopenharmony_ci    if(as) {
19113498266Sopenharmony_ci      as->expires = expires;
19213498266Sopenharmony_ci      as->prio = prio;
19313498266Sopenharmony_ci      as->persist = persist ? 1 : 0;
19413498266Sopenharmony_ci      Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node);
19513498266Sopenharmony_ci    }
19613498266Sopenharmony_ci  }
19713498266Sopenharmony_ci
19813498266Sopenharmony_ci  return CURLE_OK;
19913498266Sopenharmony_ci}
20013498266Sopenharmony_ci
20113498266Sopenharmony_ci/*
20213498266Sopenharmony_ci * Load alt-svc entries from the given file. The text based line-oriented file
20313498266Sopenharmony_ci * format is documented here: https://curl.se/docs/alt-svc.html
20413498266Sopenharmony_ci *
20513498266Sopenharmony_ci * This function only returns error on major problems that prevent alt-svc
20613498266Sopenharmony_ci * handling to work completely. It will ignore individual syntactical errors
20713498266Sopenharmony_ci * etc.
20813498266Sopenharmony_ci */
20913498266Sopenharmony_cistatic CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
21013498266Sopenharmony_ci{
21113498266Sopenharmony_ci  CURLcode result = CURLE_OK;
21213498266Sopenharmony_ci  char *line = NULL;
21313498266Sopenharmony_ci  FILE *fp;
21413498266Sopenharmony_ci
21513498266Sopenharmony_ci  /* we need a private copy of the file name so that the altsvc cache file
21613498266Sopenharmony_ci     name survives an easy handle reset */
21713498266Sopenharmony_ci  free(asi->filename);
21813498266Sopenharmony_ci  asi->filename = strdup(file);
21913498266Sopenharmony_ci  if(!asi->filename)
22013498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
22113498266Sopenharmony_ci
22213498266Sopenharmony_ci  fp = fopen(file, FOPEN_READTEXT);
22313498266Sopenharmony_ci  if(fp) {
22413498266Sopenharmony_ci    line = malloc(MAX_ALTSVC_LINE);
22513498266Sopenharmony_ci    if(!line)
22613498266Sopenharmony_ci      goto fail;
22713498266Sopenharmony_ci    while(Curl_get_line(line, MAX_ALTSVC_LINE, fp)) {
22813498266Sopenharmony_ci      char *lineptr = line;
22913498266Sopenharmony_ci      while(*lineptr && ISBLANK(*lineptr))
23013498266Sopenharmony_ci        lineptr++;
23113498266Sopenharmony_ci      if(*lineptr == '#')
23213498266Sopenharmony_ci        /* skip commented lines */
23313498266Sopenharmony_ci        continue;
23413498266Sopenharmony_ci
23513498266Sopenharmony_ci      altsvc_add(asi, lineptr);
23613498266Sopenharmony_ci    }
23713498266Sopenharmony_ci    free(line); /* free the line buffer */
23813498266Sopenharmony_ci    fclose(fp);
23913498266Sopenharmony_ci  }
24013498266Sopenharmony_ci  return result;
24113498266Sopenharmony_ci
24213498266Sopenharmony_cifail:
24313498266Sopenharmony_ci  Curl_safefree(asi->filename);
24413498266Sopenharmony_ci  free(line);
24513498266Sopenharmony_ci  fclose(fp);
24613498266Sopenharmony_ci  return CURLE_OUT_OF_MEMORY;
24713498266Sopenharmony_ci}
24813498266Sopenharmony_ci
24913498266Sopenharmony_ci/*
25013498266Sopenharmony_ci * Write this single altsvc entry to a single output line
25113498266Sopenharmony_ci */
25213498266Sopenharmony_ci
25313498266Sopenharmony_cistatic CURLcode altsvc_out(struct altsvc *as, FILE *fp)
25413498266Sopenharmony_ci{
25513498266Sopenharmony_ci  struct tm stamp;
25613498266Sopenharmony_ci  const char *dst6_pre = "";
25713498266Sopenharmony_ci  const char *dst6_post = "";
25813498266Sopenharmony_ci  const char *src6_pre = "";
25913498266Sopenharmony_ci  const char *src6_post = "";
26013498266Sopenharmony_ci  CURLcode result = Curl_gmtime(as->expires, &stamp);
26113498266Sopenharmony_ci  if(result)
26213498266Sopenharmony_ci    return result;
26313498266Sopenharmony_ci#ifdef ENABLE_IPV6
26413498266Sopenharmony_ci  else {
26513498266Sopenharmony_ci    char ipv6_unused[16];
26613498266Sopenharmony_ci    if(1 == Curl_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) {
26713498266Sopenharmony_ci      dst6_pre = "[";
26813498266Sopenharmony_ci      dst6_post = "]";
26913498266Sopenharmony_ci    }
27013498266Sopenharmony_ci    if(1 == Curl_inet_pton(AF_INET6, as->src.host, ipv6_unused)) {
27113498266Sopenharmony_ci      src6_pre = "[";
27213498266Sopenharmony_ci      src6_post = "]";
27313498266Sopenharmony_ci    }
27413498266Sopenharmony_ci  }
27513498266Sopenharmony_ci#endif
27613498266Sopenharmony_ci  fprintf(fp,
27713498266Sopenharmony_ci          "%s %s%s%s %u "
27813498266Sopenharmony_ci          "%s %s%s%s %u "
27913498266Sopenharmony_ci          "\"%d%02d%02d "
28013498266Sopenharmony_ci          "%02d:%02d:%02d\" "
28113498266Sopenharmony_ci          "%u %d\n",
28213498266Sopenharmony_ci          Curl_alpnid2str(as->src.alpnid),
28313498266Sopenharmony_ci          src6_pre, as->src.host, src6_post,
28413498266Sopenharmony_ci          as->src.port,
28513498266Sopenharmony_ci
28613498266Sopenharmony_ci          Curl_alpnid2str(as->dst.alpnid),
28713498266Sopenharmony_ci          dst6_pre, as->dst.host, dst6_post,
28813498266Sopenharmony_ci          as->dst.port,
28913498266Sopenharmony_ci
29013498266Sopenharmony_ci          stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
29113498266Sopenharmony_ci          stamp.tm_hour, stamp.tm_min, stamp.tm_sec,
29213498266Sopenharmony_ci          as->persist, as->prio);
29313498266Sopenharmony_ci  return CURLE_OK;
29413498266Sopenharmony_ci}
29513498266Sopenharmony_ci
29613498266Sopenharmony_ci/* ---- library-wide functions below ---- */
29713498266Sopenharmony_ci
29813498266Sopenharmony_ci/*
29913498266Sopenharmony_ci * Curl_altsvc_init() creates a new altsvc cache.
30013498266Sopenharmony_ci * It returns the new instance or NULL if something goes wrong.
30113498266Sopenharmony_ci */
30213498266Sopenharmony_cistruct altsvcinfo *Curl_altsvc_init(void)
30313498266Sopenharmony_ci{
30413498266Sopenharmony_ci  struct altsvcinfo *asi = calloc(1, sizeof(struct altsvcinfo));
30513498266Sopenharmony_ci  if(!asi)
30613498266Sopenharmony_ci    return NULL;
30713498266Sopenharmony_ci  Curl_llist_init(&asi->list, NULL);
30813498266Sopenharmony_ci
30913498266Sopenharmony_ci  /* set default behavior */
31013498266Sopenharmony_ci  asi->flags = CURLALTSVC_H1
31113498266Sopenharmony_ci#ifdef USE_HTTP2
31213498266Sopenharmony_ci    | CURLALTSVC_H2
31313498266Sopenharmony_ci#endif
31413498266Sopenharmony_ci#ifdef ENABLE_QUIC
31513498266Sopenharmony_ci    | CURLALTSVC_H3
31613498266Sopenharmony_ci#endif
31713498266Sopenharmony_ci    ;
31813498266Sopenharmony_ci  return asi;
31913498266Sopenharmony_ci}
32013498266Sopenharmony_ci
32113498266Sopenharmony_ci/*
32213498266Sopenharmony_ci * Curl_altsvc_load() loads alt-svc from file.
32313498266Sopenharmony_ci */
32413498266Sopenharmony_ciCURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file)
32513498266Sopenharmony_ci{
32613498266Sopenharmony_ci  CURLcode result;
32713498266Sopenharmony_ci  DEBUGASSERT(asi);
32813498266Sopenharmony_ci  result = altsvc_load(asi, file);
32913498266Sopenharmony_ci  return result;
33013498266Sopenharmony_ci}
33113498266Sopenharmony_ci
33213498266Sopenharmony_ci/*
33313498266Sopenharmony_ci * Curl_altsvc_ctrl() passes on the external bitmask.
33413498266Sopenharmony_ci */
33513498266Sopenharmony_ciCURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl)
33613498266Sopenharmony_ci{
33713498266Sopenharmony_ci  DEBUGASSERT(asi);
33813498266Sopenharmony_ci  asi->flags = ctrl;
33913498266Sopenharmony_ci  return CURLE_OK;
34013498266Sopenharmony_ci}
34113498266Sopenharmony_ci
34213498266Sopenharmony_ci/*
34313498266Sopenharmony_ci * Curl_altsvc_cleanup() frees an altsvc cache instance and all associated
34413498266Sopenharmony_ci * resources.
34513498266Sopenharmony_ci */
34613498266Sopenharmony_civoid Curl_altsvc_cleanup(struct altsvcinfo **altsvcp)
34713498266Sopenharmony_ci{
34813498266Sopenharmony_ci  struct Curl_llist_element *e;
34913498266Sopenharmony_ci  struct Curl_llist_element *n;
35013498266Sopenharmony_ci  if(*altsvcp) {
35113498266Sopenharmony_ci    struct altsvcinfo *altsvc = *altsvcp;
35213498266Sopenharmony_ci    for(e = altsvc->list.head; e; e = n) {
35313498266Sopenharmony_ci      struct altsvc *as = e->ptr;
35413498266Sopenharmony_ci      n = e->next;
35513498266Sopenharmony_ci      altsvc_free(as);
35613498266Sopenharmony_ci    }
35713498266Sopenharmony_ci    free(altsvc->filename);
35813498266Sopenharmony_ci    free(altsvc);
35913498266Sopenharmony_ci    *altsvcp = NULL; /* clear the pointer */
36013498266Sopenharmony_ci  }
36113498266Sopenharmony_ci}
36213498266Sopenharmony_ci
36313498266Sopenharmony_ci/*
36413498266Sopenharmony_ci * Curl_altsvc_save() writes the altsvc cache to a file.
36513498266Sopenharmony_ci */
36613498266Sopenharmony_ciCURLcode Curl_altsvc_save(struct Curl_easy *data,
36713498266Sopenharmony_ci                          struct altsvcinfo *altsvc, const char *file)
36813498266Sopenharmony_ci{
36913498266Sopenharmony_ci  struct Curl_llist_element *e;
37013498266Sopenharmony_ci  struct Curl_llist_element *n;
37113498266Sopenharmony_ci  CURLcode result = CURLE_OK;
37213498266Sopenharmony_ci  FILE *out;
37313498266Sopenharmony_ci  char *tempstore = NULL;
37413498266Sopenharmony_ci
37513498266Sopenharmony_ci  if(!altsvc)
37613498266Sopenharmony_ci    /* no cache activated */
37713498266Sopenharmony_ci    return CURLE_OK;
37813498266Sopenharmony_ci
37913498266Sopenharmony_ci  /* if not new name is given, use the one we stored from the load */
38013498266Sopenharmony_ci  if(!file && altsvc->filename)
38113498266Sopenharmony_ci    file = altsvc->filename;
38213498266Sopenharmony_ci
38313498266Sopenharmony_ci  if((altsvc->flags & CURLALTSVC_READONLYFILE) || !file || !file[0])
38413498266Sopenharmony_ci    /* marked as read-only, no file or zero length file name */
38513498266Sopenharmony_ci    return CURLE_OK;
38613498266Sopenharmony_ci
38713498266Sopenharmony_ci  result = Curl_fopen(data, file, &out, &tempstore);
38813498266Sopenharmony_ci  if(!result) {
38913498266Sopenharmony_ci    fputs("# Your alt-svc cache. https://curl.se/docs/alt-svc.html\n"
39013498266Sopenharmony_ci          "# This file was generated by libcurl! Edit at your own risk.\n",
39113498266Sopenharmony_ci          out);
39213498266Sopenharmony_ci    for(e = altsvc->list.head; e; e = n) {
39313498266Sopenharmony_ci      struct altsvc *as = e->ptr;
39413498266Sopenharmony_ci      n = e->next;
39513498266Sopenharmony_ci      result = altsvc_out(as, out);
39613498266Sopenharmony_ci      if(result)
39713498266Sopenharmony_ci        break;
39813498266Sopenharmony_ci    }
39913498266Sopenharmony_ci    fclose(out);
40013498266Sopenharmony_ci    if(!result && tempstore && Curl_rename(tempstore, file))
40113498266Sopenharmony_ci      result = CURLE_WRITE_ERROR;
40213498266Sopenharmony_ci
40313498266Sopenharmony_ci    if(result && tempstore)
40413498266Sopenharmony_ci      unlink(tempstore);
40513498266Sopenharmony_ci  }
40613498266Sopenharmony_ci  free(tempstore);
40713498266Sopenharmony_ci  return result;
40813498266Sopenharmony_ci}
40913498266Sopenharmony_ci
41013498266Sopenharmony_cistatic CURLcode getalnum(const char **ptr, char *alpnbuf, size_t buflen)
41113498266Sopenharmony_ci{
41213498266Sopenharmony_ci  size_t len;
41313498266Sopenharmony_ci  const char *protop;
41413498266Sopenharmony_ci  const char *p = *ptr;
41513498266Sopenharmony_ci  while(*p && ISBLANK(*p))
41613498266Sopenharmony_ci    p++;
41713498266Sopenharmony_ci  protop = p;
41813498266Sopenharmony_ci  while(*p && !ISBLANK(*p) && (*p != ';') && (*p != '='))
41913498266Sopenharmony_ci    p++;
42013498266Sopenharmony_ci  len = p - protop;
42113498266Sopenharmony_ci  *ptr = p;
42213498266Sopenharmony_ci
42313498266Sopenharmony_ci  if(!len || (len >= buflen))
42413498266Sopenharmony_ci    return CURLE_BAD_FUNCTION_ARGUMENT;
42513498266Sopenharmony_ci  memcpy(alpnbuf, protop, len);
42613498266Sopenharmony_ci  alpnbuf[len] = 0;
42713498266Sopenharmony_ci  return CURLE_OK;
42813498266Sopenharmony_ci}
42913498266Sopenharmony_ci
43013498266Sopenharmony_ci/* hostcompare() returns true if 'host' matches 'check'. The first host
43113498266Sopenharmony_ci * argument may have a trailing dot present that will be ignored.
43213498266Sopenharmony_ci */
43313498266Sopenharmony_cistatic bool hostcompare(const char *host, const char *check)
43413498266Sopenharmony_ci{
43513498266Sopenharmony_ci  size_t hlen = strlen(host);
43613498266Sopenharmony_ci  size_t clen = strlen(check);
43713498266Sopenharmony_ci
43813498266Sopenharmony_ci  if(hlen && (host[hlen - 1] == '.'))
43913498266Sopenharmony_ci    hlen--;
44013498266Sopenharmony_ci  if(hlen != clen)
44113498266Sopenharmony_ci    /* they can't match if they have different lengths */
44213498266Sopenharmony_ci    return FALSE;
44313498266Sopenharmony_ci  return strncasecompare(host, check, hlen);
44413498266Sopenharmony_ci}
44513498266Sopenharmony_ci
44613498266Sopenharmony_ci/* altsvc_flush() removes all alternatives for this source origin from the
44713498266Sopenharmony_ci   list */
44813498266Sopenharmony_cistatic void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid,
44913498266Sopenharmony_ci                         const char *srchost, unsigned short srcport)
45013498266Sopenharmony_ci{
45113498266Sopenharmony_ci  struct Curl_llist_element *e;
45213498266Sopenharmony_ci  struct Curl_llist_element *n;
45313498266Sopenharmony_ci  for(e = asi->list.head; e; e = n) {
45413498266Sopenharmony_ci    struct altsvc *as = e->ptr;
45513498266Sopenharmony_ci    n = e->next;
45613498266Sopenharmony_ci    if((srcalpnid == as->src.alpnid) &&
45713498266Sopenharmony_ci       (srcport == as->src.port) &&
45813498266Sopenharmony_ci       hostcompare(srchost, as->src.host)) {
45913498266Sopenharmony_ci      Curl_llist_remove(&asi->list, e, NULL);
46013498266Sopenharmony_ci      altsvc_free(as);
46113498266Sopenharmony_ci    }
46213498266Sopenharmony_ci  }
46313498266Sopenharmony_ci}
46413498266Sopenharmony_ci
46513498266Sopenharmony_ci#ifdef DEBUGBUILD
46613498266Sopenharmony_ci/* to play well with debug builds, we can *set* a fixed time this will
46713498266Sopenharmony_ci   return */
46813498266Sopenharmony_cistatic time_t altsvc_debugtime(void *unused)
46913498266Sopenharmony_ci{
47013498266Sopenharmony_ci  char *timestr = getenv("CURL_TIME");
47113498266Sopenharmony_ci  (void)unused;
47213498266Sopenharmony_ci  if(timestr) {
47313498266Sopenharmony_ci    unsigned long val = strtol(timestr, NULL, 10);
47413498266Sopenharmony_ci    return (time_t)val;
47513498266Sopenharmony_ci  }
47613498266Sopenharmony_ci  return time(NULL);
47713498266Sopenharmony_ci}
47813498266Sopenharmony_ci#undef time
47913498266Sopenharmony_ci#define time(x) altsvc_debugtime(x)
48013498266Sopenharmony_ci#endif
48113498266Sopenharmony_ci
48213498266Sopenharmony_ci#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r')
48313498266Sopenharmony_ci
48413498266Sopenharmony_ci/*
48513498266Sopenharmony_ci * Curl_altsvc_parse() takes an incoming alt-svc response header and stores
48613498266Sopenharmony_ci * the data correctly in the cache.
48713498266Sopenharmony_ci *
48813498266Sopenharmony_ci * 'value' points to the header *value*. That's contents to the right of the
48913498266Sopenharmony_ci * header name.
49013498266Sopenharmony_ci *
49113498266Sopenharmony_ci * Currently this function rejects invalid data without returning an error.
49213498266Sopenharmony_ci * Invalid host name, port number will result in the specific alternative
49313498266Sopenharmony_ci * being rejected. Unknown protocols are skipped.
49413498266Sopenharmony_ci */
49513498266Sopenharmony_ciCURLcode Curl_altsvc_parse(struct Curl_easy *data,
49613498266Sopenharmony_ci                           struct altsvcinfo *asi, const char *value,
49713498266Sopenharmony_ci                           enum alpnid srcalpnid, const char *srchost,
49813498266Sopenharmony_ci                           unsigned short srcport)
49913498266Sopenharmony_ci{
50013498266Sopenharmony_ci  const char *p = value;
50113498266Sopenharmony_ci  size_t len;
50213498266Sopenharmony_ci  char namebuf[MAX_ALTSVC_HOSTLEN] = "";
50313498266Sopenharmony_ci  char alpnbuf[MAX_ALTSVC_ALPNLEN] = "";
50413498266Sopenharmony_ci  struct altsvc *as;
50513498266Sopenharmony_ci  unsigned short dstport = srcport; /* the same by default */
50613498266Sopenharmony_ci  CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
50713498266Sopenharmony_ci  size_t entries = 0;
50813498266Sopenharmony_ci#ifdef CURL_DISABLE_VERBOSE_STRINGS
50913498266Sopenharmony_ci  (void)data;
51013498266Sopenharmony_ci#endif
51113498266Sopenharmony_ci  if(result) {
51213498266Sopenharmony_ci    infof(data, "Excessive alt-svc header, ignoring.");
51313498266Sopenharmony_ci    return CURLE_OK;
51413498266Sopenharmony_ci  }
51513498266Sopenharmony_ci
51613498266Sopenharmony_ci  DEBUGASSERT(asi);
51713498266Sopenharmony_ci
51813498266Sopenharmony_ci  /* "clear" is a magic keyword */
51913498266Sopenharmony_ci  if(strcasecompare(alpnbuf, "clear")) {
52013498266Sopenharmony_ci    /* Flush cached alternatives for this source origin */
52113498266Sopenharmony_ci    altsvc_flush(asi, srcalpnid, srchost, srcport);
52213498266Sopenharmony_ci    return CURLE_OK;
52313498266Sopenharmony_ci  }
52413498266Sopenharmony_ci
52513498266Sopenharmony_ci  do {
52613498266Sopenharmony_ci    if(*p == '=') {
52713498266Sopenharmony_ci      /* [protocol]="[host][:port]" */
52813498266Sopenharmony_ci      enum alpnid dstalpnid = alpn2alpnid(alpnbuf); /* the same by default */
52913498266Sopenharmony_ci      p++;
53013498266Sopenharmony_ci      if(*p == '\"') {
53113498266Sopenharmony_ci        const char *dsthost = "";
53213498266Sopenharmony_ci        const char *value_ptr;
53313498266Sopenharmony_ci        char option[32];
53413498266Sopenharmony_ci        unsigned long num;
53513498266Sopenharmony_ci        char *end_ptr;
53613498266Sopenharmony_ci        bool quoted = FALSE;
53713498266Sopenharmony_ci        time_t maxage = 24 * 3600; /* default is 24 hours */
53813498266Sopenharmony_ci        bool persist = FALSE;
53913498266Sopenharmony_ci        bool valid = TRUE;
54013498266Sopenharmony_ci        p++;
54113498266Sopenharmony_ci        if(*p != ':') {
54213498266Sopenharmony_ci          /* host name starts here */
54313498266Sopenharmony_ci          const char *hostp = p;
54413498266Sopenharmony_ci          if(*p == '[') {
54513498266Sopenharmony_ci            /* pass all valid IPv6 letters - does not handle zone id */
54613498266Sopenharmony_ci            len = strspn(++p, "0123456789abcdefABCDEF:.");
54713498266Sopenharmony_ci            if(p[len] != ']')
54813498266Sopenharmony_ci              /* invalid host syntax, bail out */
54913498266Sopenharmony_ci              break;
55013498266Sopenharmony_ci            /* we store the IPv6 numerical address *with* brackets */
55113498266Sopenharmony_ci            len += 2;
55213498266Sopenharmony_ci            p = &p[len-1];
55313498266Sopenharmony_ci          }
55413498266Sopenharmony_ci          else {
55513498266Sopenharmony_ci            while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-')))
55613498266Sopenharmony_ci              p++;
55713498266Sopenharmony_ci            len = p - hostp;
55813498266Sopenharmony_ci          }
55913498266Sopenharmony_ci          if(!len || (len >= MAX_ALTSVC_HOSTLEN)) {
56013498266Sopenharmony_ci            infof(data, "Excessive alt-svc host name, ignoring.");
56113498266Sopenharmony_ci            valid = FALSE;
56213498266Sopenharmony_ci          }
56313498266Sopenharmony_ci          else {
56413498266Sopenharmony_ci            memcpy(namebuf, hostp, len);
56513498266Sopenharmony_ci            namebuf[len] = 0;
56613498266Sopenharmony_ci            dsthost = namebuf;
56713498266Sopenharmony_ci          }
56813498266Sopenharmony_ci        }
56913498266Sopenharmony_ci        else {
57013498266Sopenharmony_ci          /* no destination name, use source host */
57113498266Sopenharmony_ci          dsthost = srchost;
57213498266Sopenharmony_ci        }
57313498266Sopenharmony_ci        if(*p == ':') {
57413498266Sopenharmony_ci          unsigned long port = 0;
57513498266Sopenharmony_ci          p++;
57613498266Sopenharmony_ci          if(ISDIGIT(*p))
57713498266Sopenharmony_ci            /* a port number */
57813498266Sopenharmony_ci            port = strtoul(p, &end_ptr, 10);
57913498266Sopenharmony_ci          else
58013498266Sopenharmony_ci            end_ptr = (char *)p; /* not left uninitialized */
58113498266Sopenharmony_ci          if(!port || port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') {
58213498266Sopenharmony_ci            infof(data, "Unknown alt-svc port number, ignoring.");
58313498266Sopenharmony_ci            valid = FALSE;
58413498266Sopenharmony_ci          }
58513498266Sopenharmony_ci          else {
58613498266Sopenharmony_ci            dstport = curlx_ultous(port);
58713498266Sopenharmony_ci            p = end_ptr;
58813498266Sopenharmony_ci          }
58913498266Sopenharmony_ci        }
59013498266Sopenharmony_ci        if(*p++ != '\"')
59113498266Sopenharmony_ci          break;
59213498266Sopenharmony_ci        /* Handle the optional 'ma' and 'persist' flags. Unknown flags
59313498266Sopenharmony_ci           are skipped. */
59413498266Sopenharmony_ci        for(;;) {
59513498266Sopenharmony_ci          while(ISBLANK(*p))
59613498266Sopenharmony_ci            p++;
59713498266Sopenharmony_ci          if(*p != ';')
59813498266Sopenharmony_ci            break;
59913498266Sopenharmony_ci          p++; /* pass the semicolon */
60013498266Sopenharmony_ci          if(!*p || ISNEWLINE(*p))
60113498266Sopenharmony_ci            break;
60213498266Sopenharmony_ci          result = getalnum(&p, option, sizeof(option));
60313498266Sopenharmony_ci          if(result) {
60413498266Sopenharmony_ci            /* skip option if name is too long */
60513498266Sopenharmony_ci            option[0] = '\0';
60613498266Sopenharmony_ci          }
60713498266Sopenharmony_ci          while(*p && ISBLANK(*p))
60813498266Sopenharmony_ci            p++;
60913498266Sopenharmony_ci          if(*p != '=')
61013498266Sopenharmony_ci            return CURLE_OK;
61113498266Sopenharmony_ci          p++;
61213498266Sopenharmony_ci          while(*p && ISBLANK(*p))
61313498266Sopenharmony_ci            p++;
61413498266Sopenharmony_ci          if(!*p)
61513498266Sopenharmony_ci            return CURLE_OK;
61613498266Sopenharmony_ci          if(*p == '\"') {
61713498266Sopenharmony_ci            /* quoted value */
61813498266Sopenharmony_ci            p++;
61913498266Sopenharmony_ci            quoted = TRUE;
62013498266Sopenharmony_ci          }
62113498266Sopenharmony_ci          value_ptr = p;
62213498266Sopenharmony_ci          if(quoted) {
62313498266Sopenharmony_ci            while(*p && *p != '\"')
62413498266Sopenharmony_ci              p++;
62513498266Sopenharmony_ci            if(!*p++)
62613498266Sopenharmony_ci              return CURLE_OK;
62713498266Sopenharmony_ci          }
62813498266Sopenharmony_ci          else {
62913498266Sopenharmony_ci            while(*p && !ISBLANK(*p) && *p!= ';' && *p != ',')
63013498266Sopenharmony_ci              p++;
63113498266Sopenharmony_ci          }
63213498266Sopenharmony_ci          num = strtoul(value_ptr, &end_ptr, 10);
63313498266Sopenharmony_ci          if((end_ptr != value_ptr) && (num < ULONG_MAX)) {
63413498266Sopenharmony_ci            if(strcasecompare("ma", option))
63513498266Sopenharmony_ci              maxage = num;
63613498266Sopenharmony_ci            else if(strcasecompare("persist", option) && (num == 1))
63713498266Sopenharmony_ci              persist = TRUE;
63813498266Sopenharmony_ci          }
63913498266Sopenharmony_ci        }
64013498266Sopenharmony_ci        if(dstalpnid && valid) {
64113498266Sopenharmony_ci          if(!entries++)
64213498266Sopenharmony_ci            /* Flush cached alternatives for this source origin, if any - when
64313498266Sopenharmony_ci               this is the first entry of the line. */
64413498266Sopenharmony_ci            altsvc_flush(asi, srcalpnid, srchost, srcport);
64513498266Sopenharmony_ci
64613498266Sopenharmony_ci          as = altsvc_createid(srchost, dsthost,
64713498266Sopenharmony_ci                               srcalpnid, dstalpnid,
64813498266Sopenharmony_ci                               srcport, dstport);
64913498266Sopenharmony_ci          if(as) {
65013498266Sopenharmony_ci            /* The expires time also needs to take the Age: value (if any) into
65113498266Sopenharmony_ci               account. [See RFC 7838 section 3.1] */
65213498266Sopenharmony_ci            as->expires = maxage + time(NULL);
65313498266Sopenharmony_ci            as->persist = persist;
65413498266Sopenharmony_ci            Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node);
65513498266Sopenharmony_ci            infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport,
65613498266Sopenharmony_ci                  Curl_alpnid2str(dstalpnid));
65713498266Sopenharmony_ci          }
65813498266Sopenharmony_ci        }
65913498266Sopenharmony_ci      }
66013498266Sopenharmony_ci      else
66113498266Sopenharmony_ci        break;
66213498266Sopenharmony_ci      /* after the double quote there can be a comma if there's another
66313498266Sopenharmony_ci         string or a semicolon if no more */
66413498266Sopenharmony_ci      if(*p == ',') {
66513498266Sopenharmony_ci        /* comma means another alternative is presented */
66613498266Sopenharmony_ci        p++;
66713498266Sopenharmony_ci        result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
66813498266Sopenharmony_ci        if(result)
66913498266Sopenharmony_ci          break;
67013498266Sopenharmony_ci      }
67113498266Sopenharmony_ci    }
67213498266Sopenharmony_ci    else
67313498266Sopenharmony_ci      break;
67413498266Sopenharmony_ci  } while(*p && (*p != ';') && (*p != '\n') && (*p != '\r'));
67513498266Sopenharmony_ci
67613498266Sopenharmony_ci  return CURLE_OK;
67713498266Sopenharmony_ci}
67813498266Sopenharmony_ci
67913498266Sopenharmony_ci/*
68013498266Sopenharmony_ci * Return TRUE on a match
68113498266Sopenharmony_ci */
68213498266Sopenharmony_cibool Curl_altsvc_lookup(struct altsvcinfo *asi,
68313498266Sopenharmony_ci                        enum alpnid srcalpnid, const char *srchost,
68413498266Sopenharmony_ci                        int srcport,
68513498266Sopenharmony_ci                        struct altsvc **dstentry,
68613498266Sopenharmony_ci                        const int versions) /* one or more bits */
68713498266Sopenharmony_ci{
68813498266Sopenharmony_ci  struct Curl_llist_element *e;
68913498266Sopenharmony_ci  struct Curl_llist_element *n;
69013498266Sopenharmony_ci  time_t now = time(NULL);
69113498266Sopenharmony_ci  DEBUGASSERT(asi);
69213498266Sopenharmony_ci  DEBUGASSERT(srchost);
69313498266Sopenharmony_ci  DEBUGASSERT(dstentry);
69413498266Sopenharmony_ci
69513498266Sopenharmony_ci  for(e = asi->list.head; e; e = n) {
69613498266Sopenharmony_ci    struct altsvc *as = e->ptr;
69713498266Sopenharmony_ci    n = e->next;
69813498266Sopenharmony_ci    if(as->expires < now) {
69913498266Sopenharmony_ci      /* an expired entry, remove */
70013498266Sopenharmony_ci      Curl_llist_remove(&asi->list, e, NULL);
70113498266Sopenharmony_ci      altsvc_free(as);
70213498266Sopenharmony_ci      continue;
70313498266Sopenharmony_ci    }
70413498266Sopenharmony_ci    if((as->src.alpnid == srcalpnid) &&
70513498266Sopenharmony_ci       hostcompare(srchost, as->src.host) &&
70613498266Sopenharmony_ci       (as->src.port == srcport) &&
70713498266Sopenharmony_ci       (versions & as->dst.alpnid)) {
70813498266Sopenharmony_ci      /* match */
70913498266Sopenharmony_ci      *dstentry = as;
71013498266Sopenharmony_ci      return TRUE;
71113498266Sopenharmony_ci    }
71213498266Sopenharmony_ci  }
71313498266Sopenharmony_ci  return FALSE;
71413498266Sopenharmony_ci}
71513498266Sopenharmony_ci
71613498266Sopenharmony_ci#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_ALTSVC */
717