xref: /aosp_15_r20/external/curl/lib/altsvc.c (revision 6236dae45794135f37c4eb022389c904c8b0090d)
1*6236dae4SAndroid Build Coastguard Worker /***************************************************************************
2*6236dae4SAndroid Build Coastguard Worker  *                                  _   _ ____  _
3*6236dae4SAndroid Build Coastguard Worker  *  Project                     ___| | | |  _ \| |
4*6236dae4SAndroid Build Coastguard Worker  *                             / __| | | | |_) | |
5*6236dae4SAndroid Build Coastguard Worker  *                            | (__| |_| |  _ <| |___
6*6236dae4SAndroid Build Coastguard Worker  *                             \___|\___/|_| \_\_____|
7*6236dae4SAndroid Build Coastguard Worker  *
8*6236dae4SAndroid Build Coastguard Worker  * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
9*6236dae4SAndroid Build Coastguard Worker  *
10*6236dae4SAndroid Build Coastguard Worker  * This software is licensed as described in the file COPYING, which
11*6236dae4SAndroid Build Coastguard Worker  * you should have received as part of this distribution. The terms
12*6236dae4SAndroid Build Coastguard Worker  * are also available at https://curl.se/docs/copyright.html.
13*6236dae4SAndroid Build Coastguard Worker  *
14*6236dae4SAndroid Build Coastguard Worker  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15*6236dae4SAndroid Build Coastguard Worker  * copies of the Software, and permit persons to whom the Software is
16*6236dae4SAndroid Build Coastguard Worker  * furnished to do so, under the terms of the COPYING file.
17*6236dae4SAndroid Build Coastguard Worker  *
18*6236dae4SAndroid Build Coastguard Worker  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19*6236dae4SAndroid Build Coastguard Worker  * KIND, either express or implied.
20*6236dae4SAndroid Build Coastguard Worker  *
21*6236dae4SAndroid Build Coastguard Worker  * SPDX-License-Identifier: curl
22*6236dae4SAndroid Build Coastguard Worker  *
23*6236dae4SAndroid Build Coastguard Worker  ***************************************************************************/
24*6236dae4SAndroid Build Coastguard Worker /*
25*6236dae4SAndroid Build Coastguard Worker  * The Alt-Svc: header is defined in RFC 7838:
26*6236dae4SAndroid Build Coastguard Worker  * https://datatracker.ietf.org/doc/html/rfc7838
27*6236dae4SAndroid Build Coastguard Worker  */
28*6236dae4SAndroid Build Coastguard Worker #include "curl_setup.h"
29*6236dae4SAndroid Build Coastguard Worker 
30*6236dae4SAndroid Build Coastguard Worker #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC)
31*6236dae4SAndroid Build Coastguard Worker #include <curl/curl.h>
32*6236dae4SAndroid Build Coastguard Worker #include "urldata.h"
33*6236dae4SAndroid Build Coastguard Worker #include "altsvc.h"
34*6236dae4SAndroid Build Coastguard Worker #include "curl_get_line.h"
35*6236dae4SAndroid Build Coastguard Worker #include "strcase.h"
36*6236dae4SAndroid Build Coastguard Worker #include "parsedate.h"
37*6236dae4SAndroid Build Coastguard Worker #include "sendf.h"
38*6236dae4SAndroid Build Coastguard Worker #include "warnless.h"
39*6236dae4SAndroid Build Coastguard Worker #include "fopen.h"
40*6236dae4SAndroid Build Coastguard Worker #include "rename.h"
41*6236dae4SAndroid Build Coastguard Worker #include "strdup.h"
42*6236dae4SAndroid Build Coastguard Worker #include "inet_pton.h"
43*6236dae4SAndroid Build Coastguard Worker 
44*6236dae4SAndroid Build Coastguard Worker /* The last 3 #include files should be in this order */
45*6236dae4SAndroid Build Coastguard Worker #include "curl_printf.h"
46*6236dae4SAndroid Build Coastguard Worker #include "curl_memory.h"
47*6236dae4SAndroid Build Coastguard Worker #include "memdebug.h"
48*6236dae4SAndroid Build Coastguard Worker 
49*6236dae4SAndroid Build Coastguard Worker #define MAX_ALTSVC_LINE 4095
50*6236dae4SAndroid Build Coastguard Worker #define MAX_ALTSVC_DATELENSTR "64"
51*6236dae4SAndroid Build Coastguard Worker #define MAX_ALTSVC_DATELEN 64
52*6236dae4SAndroid Build Coastguard Worker #define MAX_ALTSVC_HOSTLENSTR "512"
53*6236dae4SAndroid Build Coastguard Worker #define MAX_ALTSVC_HOSTLEN 512
54*6236dae4SAndroid Build Coastguard Worker #define MAX_ALTSVC_ALPNLENSTR "10"
55*6236dae4SAndroid Build Coastguard Worker #define MAX_ALTSVC_ALPNLEN 10
56*6236dae4SAndroid Build Coastguard Worker 
57*6236dae4SAndroid Build Coastguard Worker #define H3VERSION "h3"
58*6236dae4SAndroid Build Coastguard Worker 
alpn2alpnid(char * name)59*6236dae4SAndroid Build Coastguard Worker static enum alpnid alpn2alpnid(char *name)
60*6236dae4SAndroid Build Coastguard Worker {
61*6236dae4SAndroid Build Coastguard Worker   if(strcasecompare(name, "h1"))
62*6236dae4SAndroid Build Coastguard Worker     return ALPN_h1;
63*6236dae4SAndroid Build Coastguard Worker   if(strcasecompare(name, "h2"))
64*6236dae4SAndroid Build Coastguard Worker     return ALPN_h2;
65*6236dae4SAndroid Build Coastguard Worker   if(strcasecompare(name, H3VERSION))
66*6236dae4SAndroid Build Coastguard Worker     return ALPN_h3;
67*6236dae4SAndroid Build Coastguard Worker   if(strcasecompare(name, "http/1.1"))
68*6236dae4SAndroid Build Coastguard Worker     return ALPN_h1;
69*6236dae4SAndroid Build Coastguard Worker   return ALPN_none; /* unknown, probably rubbish input */
70*6236dae4SAndroid Build Coastguard Worker }
71*6236dae4SAndroid Build Coastguard Worker 
72*6236dae4SAndroid Build Coastguard Worker /* Given the ALPN ID, return the name */
Curl_alpnid2str(enum alpnid id)73*6236dae4SAndroid Build Coastguard Worker const char *Curl_alpnid2str(enum alpnid id)
74*6236dae4SAndroid Build Coastguard Worker {
75*6236dae4SAndroid Build Coastguard Worker   switch(id) {
76*6236dae4SAndroid Build Coastguard Worker   case ALPN_h1:
77*6236dae4SAndroid Build Coastguard Worker     return "h1";
78*6236dae4SAndroid Build Coastguard Worker   case ALPN_h2:
79*6236dae4SAndroid Build Coastguard Worker     return "h2";
80*6236dae4SAndroid Build Coastguard Worker   case ALPN_h3:
81*6236dae4SAndroid Build Coastguard Worker     return H3VERSION;
82*6236dae4SAndroid Build Coastguard Worker   default:
83*6236dae4SAndroid Build Coastguard Worker     return ""; /* bad */
84*6236dae4SAndroid Build Coastguard Worker   }
85*6236dae4SAndroid Build Coastguard Worker }
86*6236dae4SAndroid Build Coastguard Worker 
87*6236dae4SAndroid Build Coastguard Worker 
altsvc_free(struct altsvc * as)88*6236dae4SAndroid Build Coastguard Worker static void altsvc_free(struct altsvc *as)
89*6236dae4SAndroid Build Coastguard Worker {
90*6236dae4SAndroid Build Coastguard Worker   free(as->src.host);
91*6236dae4SAndroid Build Coastguard Worker   free(as->dst.host);
92*6236dae4SAndroid Build Coastguard Worker   free(as);
93*6236dae4SAndroid Build Coastguard Worker }
94*6236dae4SAndroid Build Coastguard Worker 
altsvc_createid(const char * srchost,const char * dsthost,size_t dlen,enum alpnid srcalpnid,enum alpnid dstalpnid,unsigned int srcport,unsigned int dstport)95*6236dae4SAndroid Build Coastguard Worker static struct altsvc *altsvc_createid(const char *srchost,
96*6236dae4SAndroid Build Coastguard Worker                                       const char *dsthost,
97*6236dae4SAndroid Build Coastguard Worker                                       size_t dlen, /* dsthost length */
98*6236dae4SAndroid Build Coastguard Worker                                       enum alpnid srcalpnid,
99*6236dae4SAndroid Build Coastguard Worker                                       enum alpnid dstalpnid,
100*6236dae4SAndroid Build Coastguard Worker                                       unsigned int srcport,
101*6236dae4SAndroid Build Coastguard Worker                                       unsigned int dstport)
102*6236dae4SAndroid Build Coastguard Worker {
103*6236dae4SAndroid Build Coastguard Worker   struct altsvc *as = calloc(1, sizeof(struct altsvc));
104*6236dae4SAndroid Build Coastguard Worker   size_t hlen;
105*6236dae4SAndroid Build Coastguard Worker   if(!as)
106*6236dae4SAndroid Build Coastguard Worker     return NULL;
107*6236dae4SAndroid Build Coastguard Worker   hlen = strlen(srchost);
108*6236dae4SAndroid Build Coastguard Worker   DEBUGASSERT(hlen);
109*6236dae4SAndroid Build Coastguard Worker   DEBUGASSERT(dlen);
110*6236dae4SAndroid Build Coastguard Worker   if(!hlen || !dlen) {
111*6236dae4SAndroid Build Coastguard Worker     /* bad input */
112*6236dae4SAndroid Build Coastguard Worker     free(as);
113*6236dae4SAndroid Build Coastguard Worker     return NULL;
114*6236dae4SAndroid Build Coastguard Worker   }
115*6236dae4SAndroid Build Coastguard Worker   if((hlen > 2) && srchost[0] == '[') {
116*6236dae4SAndroid Build Coastguard Worker     /* IPv6 address, strip off brackets */
117*6236dae4SAndroid Build Coastguard Worker     srchost++;
118*6236dae4SAndroid Build Coastguard Worker     hlen -= 2;
119*6236dae4SAndroid Build Coastguard Worker   }
120*6236dae4SAndroid Build Coastguard Worker   else if(srchost[hlen - 1] == '.')
121*6236dae4SAndroid Build Coastguard Worker     /* strip off trailing dot */
122*6236dae4SAndroid Build Coastguard Worker     hlen--;
123*6236dae4SAndroid Build Coastguard Worker   if((dlen > 2) && dsthost[0] == '[') {
124*6236dae4SAndroid Build Coastguard Worker     /* IPv6 address, strip off brackets */
125*6236dae4SAndroid Build Coastguard Worker     dsthost++;
126*6236dae4SAndroid Build Coastguard Worker     dlen -= 2;
127*6236dae4SAndroid Build Coastguard Worker   }
128*6236dae4SAndroid Build Coastguard Worker 
129*6236dae4SAndroid Build Coastguard Worker   as->src.host = Curl_memdup0(srchost, hlen);
130*6236dae4SAndroid Build Coastguard Worker   if(!as->src.host)
131*6236dae4SAndroid Build Coastguard Worker     goto error;
132*6236dae4SAndroid Build Coastguard Worker 
133*6236dae4SAndroid Build Coastguard Worker   as->dst.host = Curl_memdup0(dsthost, dlen);
134*6236dae4SAndroid Build Coastguard Worker   if(!as->dst.host)
135*6236dae4SAndroid Build Coastguard Worker     goto error;
136*6236dae4SAndroid Build Coastguard Worker 
137*6236dae4SAndroid Build Coastguard Worker   as->src.alpnid = srcalpnid;
138*6236dae4SAndroid Build Coastguard Worker   as->dst.alpnid = dstalpnid;
139*6236dae4SAndroid Build Coastguard Worker   as->src.port = curlx_ultous(srcport);
140*6236dae4SAndroid Build Coastguard Worker   as->dst.port = curlx_ultous(dstport);
141*6236dae4SAndroid Build Coastguard Worker 
142*6236dae4SAndroid Build Coastguard Worker   return as;
143*6236dae4SAndroid Build Coastguard Worker error:
144*6236dae4SAndroid Build Coastguard Worker   altsvc_free(as);
145*6236dae4SAndroid Build Coastguard Worker   return NULL;
146*6236dae4SAndroid Build Coastguard Worker }
147*6236dae4SAndroid Build Coastguard Worker 
altsvc_create(char * srchost,char * dsthost,char * srcalpn,char * dstalpn,unsigned int srcport,unsigned int dstport)148*6236dae4SAndroid Build Coastguard Worker static struct altsvc *altsvc_create(char *srchost,
149*6236dae4SAndroid Build Coastguard Worker                                     char *dsthost,
150*6236dae4SAndroid Build Coastguard Worker                                     char *srcalpn,
151*6236dae4SAndroid Build Coastguard Worker                                     char *dstalpn,
152*6236dae4SAndroid Build Coastguard Worker                                     unsigned int srcport,
153*6236dae4SAndroid Build Coastguard Worker                                     unsigned int dstport)
154*6236dae4SAndroid Build Coastguard Worker {
155*6236dae4SAndroid Build Coastguard Worker   enum alpnid dstalpnid = alpn2alpnid(dstalpn);
156*6236dae4SAndroid Build Coastguard Worker   enum alpnid srcalpnid = alpn2alpnid(srcalpn);
157*6236dae4SAndroid Build Coastguard Worker   if(!srcalpnid || !dstalpnid)
158*6236dae4SAndroid Build Coastguard Worker     return NULL;
159*6236dae4SAndroid Build Coastguard Worker   return altsvc_createid(srchost, dsthost, strlen(dsthost),
160*6236dae4SAndroid Build Coastguard Worker                          srcalpnid, dstalpnid,
161*6236dae4SAndroid Build Coastguard Worker                          srcport, dstport);
162*6236dae4SAndroid Build Coastguard Worker }
163*6236dae4SAndroid Build Coastguard Worker 
164*6236dae4SAndroid Build Coastguard Worker /* only returns SERIOUS errors */
altsvc_add(struct altsvcinfo * asi,char * line)165*6236dae4SAndroid Build Coastguard Worker static CURLcode altsvc_add(struct altsvcinfo *asi, char *line)
166*6236dae4SAndroid Build Coastguard Worker {
167*6236dae4SAndroid Build Coastguard Worker   /* Example line:
168*6236dae4SAndroid Build Coastguard Worker      h2 example.com 443 h3 shiny.example.com 8443 "20191231 10:00:00" 1
169*6236dae4SAndroid Build Coastguard Worker    */
170*6236dae4SAndroid Build Coastguard Worker   char srchost[MAX_ALTSVC_HOSTLEN + 1];
171*6236dae4SAndroid Build Coastguard Worker   char dsthost[MAX_ALTSVC_HOSTLEN + 1];
172*6236dae4SAndroid Build Coastguard Worker   char srcalpn[MAX_ALTSVC_ALPNLEN + 1];
173*6236dae4SAndroid Build Coastguard Worker   char dstalpn[MAX_ALTSVC_ALPNLEN + 1];
174*6236dae4SAndroid Build Coastguard Worker   char date[MAX_ALTSVC_DATELEN + 1];
175*6236dae4SAndroid Build Coastguard Worker   unsigned int srcport;
176*6236dae4SAndroid Build Coastguard Worker   unsigned int dstport;
177*6236dae4SAndroid Build Coastguard Worker   unsigned int prio;
178*6236dae4SAndroid Build Coastguard Worker   unsigned int persist;
179*6236dae4SAndroid Build Coastguard Worker   int rc;
180*6236dae4SAndroid Build Coastguard Worker 
181*6236dae4SAndroid Build Coastguard Worker   rc = sscanf(line,
182*6236dae4SAndroid Build Coastguard Worker               "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u "
183*6236dae4SAndroid Build Coastguard Worker               "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u "
184*6236dae4SAndroid Build Coastguard Worker               "\"%" MAX_ALTSVC_DATELENSTR "[^\"]\" %u %u",
185*6236dae4SAndroid Build Coastguard Worker               srcalpn, srchost, &srcport,
186*6236dae4SAndroid Build Coastguard Worker               dstalpn, dsthost, &dstport,
187*6236dae4SAndroid Build Coastguard Worker               date, &persist, &prio);
188*6236dae4SAndroid Build Coastguard Worker   if(9 == rc) {
189*6236dae4SAndroid Build Coastguard Worker     struct altsvc *as;
190*6236dae4SAndroid Build Coastguard Worker     time_t expires = Curl_getdate_capped(date);
191*6236dae4SAndroid Build Coastguard Worker     as = altsvc_create(srchost, dsthost, srcalpn, dstalpn, srcport, dstport);
192*6236dae4SAndroid Build Coastguard Worker     if(as) {
193*6236dae4SAndroid Build Coastguard Worker       as->expires = expires;
194*6236dae4SAndroid Build Coastguard Worker       as->prio = prio;
195*6236dae4SAndroid Build Coastguard Worker       as->persist = persist ? 1 : 0;
196*6236dae4SAndroid Build Coastguard Worker       Curl_llist_append(&asi->list, as, &as->node);
197*6236dae4SAndroid Build Coastguard Worker     }
198*6236dae4SAndroid Build Coastguard Worker   }
199*6236dae4SAndroid Build Coastguard Worker 
200*6236dae4SAndroid Build Coastguard Worker   return CURLE_OK;
201*6236dae4SAndroid Build Coastguard Worker }
202*6236dae4SAndroid Build Coastguard Worker 
203*6236dae4SAndroid Build Coastguard Worker /*
204*6236dae4SAndroid Build Coastguard Worker  * Load alt-svc entries from the given file. The text based line-oriented file
205*6236dae4SAndroid Build Coastguard Worker  * format is documented here: https://curl.se/docs/alt-svc.html
206*6236dae4SAndroid Build Coastguard Worker  *
207*6236dae4SAndroid Build Coastguard Worker  * This function only returns error on major problems that prevent alt-svc
208*6236dae4SAndroid Build Coastguard Worker  * handling to work completely. It will ignore individual syntactical errors
209*6236dae4SAndroid Build Coastguard Worker  * etc.
210*6236dae4SAndroid Build Coastguard Worker  */
altsvc_load(struct altsvcinfo * asi,const char * file)211*6236dae4SAndroid Build Coastguard Worker static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
212*6236dae4SAndroid Build Coastguard Worker {
213*6236dae4SAndroid Build Coastguard Worker   CURLcode result = CURLE_OK;
214*6236dae4SAndroid Build Coastguard Worker   FILE *fp;
215*6236dae4SAndroid Build Coastguard Worker 
216*6236dae4SAndroid Build Coastguard Worker   /* we need a private copy of the filename so that the altsvc cache file
217*6236dae4SAndroid Build Coastguard Worker      name survives an easy handle reset */
218*6236dae4SAndroid Build Coastguard Worker   free(asi->filename);
219*6236dae4SAndroid Build Coastguard Worker   asi->filename = strdup(file);
220*6236dae4SAndroid Build Coastguard Worker   if(!asi->filename)
221*6236dae4SAndroid Build Coastguard Worker     return CURLE_OUT_OF_MEMORY;
222*6236dae4SAndroid Build Coastguard Worker 
223*6236dae4SAndroid Build Coastguard Worker   fp = fopen(file, FOPEN_READTEXT);
224*6236dae4SAndroid Build Coastguard Worker   if(fp) {
225*6236dae4SAndroid Build Coastguard Worker     struct dynbuf buf;
226*6236dae4SAndroid Build Coastguard Worker     Curl_dyn_init(&buf, MAX_ALTSVC_LINE);
227*6236dae4SAndroid Build Coastguard Worker     while(Curl_get_line(&buf, fp)) {
228*6236dae4SAndroid Build Coastguard Worker       char *lineptr = Curl_dyn_ptr(&buf);
229*6236dae4SAndroid Build Coastguard Worker       while(*lineptr && ISBLANK(*lineptr))
230*6236dae4SAndroid Build Coastguard Worker         lineptr++;
231*6236dae4SAndroid Build Coastguard Worker       if(*lineptr == '#')
232*6236dae4SAndroid Build Coastguard Worker         /* skip commented lines */
233*6236dae4SAndroid Build Coastguard Worker         continue;
234*6236dae4SAndroid Build Coastguard Worker 
235*6236dae4SAndroid Build Coastguard Worker       altsvc_add(asi, lineptr);
236*6236dae4SAndroid Build Coastguard Worker     }
237*6236dae4SAndroid Build Coastguard Worker     Curl_dyn_free(&buf); /* free the line buffer */
238*6236dae4SAndroid Build Coastguard Worker     fclose(fp);
239*6236dae4SAndroid Build Coastguard Worker   }
240*6236dae4SAndroid Build Coastguard Worker   return result;
241*6236dae4SAndroid Build Coastguard Worker }
242*6236dae4SAndroid Build Coastguard Worker 
243*6236dae4SAndroid Build Coastguard Worker /*
244*6236dae4SAndroid Build Coastguard Worker  * Write this single altsvc entry to a single output line
245*6236dae4SAndroid Build Coastguard Worker  */
246*6236dae4SAndroid Build Coastguard Worker 
altsvc_out(struct altsvc * as,FILE * fp)247*6236dae4SAndroid Build Coastguard Worker static CURLcode altsvc_out(struct altsvc *as, FILE *fp)
248*6236dae4SAndroid Build Coastguard Worker {
249*6236dae4SAndroid Build Coastguard Worker   struct tm stamp;
250*6236dae4SAndroid Build Coastguard Worker   const char *dst6_pre = "";
251*6236dae4SAndroid Build Coastguard Worker   const char *dst6_post = "";
252*6236dae4SAndroid Build Coastguard Worker   const char *src6_pre = "";
253*6236dae4SAndroid Build Coastguard Worker   const char *src6_post = "";
254*6236dae4SAndroid Build Coastguard Worker   CURLcode result = Curl_gmtime(as->expires, &stamp);
255*6236dae4SAndroid Build Coastguard Worker   if(result)
256*6236dae4SAndroid Build Coastguard Worker     return result;
257*6236dae4SAndroid Build Coastguard Worker #ifdef USE_IPV6
258*6236dae4SAndroid Build Coastguard Worker   else {
259*6236dae4SAndroid Build Coastguard Worker     char ipv6_unused[16];
260*6236dae4SAndroid Build Coastguard Worker     if(1 == Curl_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) {
261*6236dae4SAndroid Build Coastguard Worker       dst6_pre = "[";
262*6236dae4SAndroid Build Coastguard Worker       dst6_post = "]";
263*6236dae4SAndroid Build Coastguard Worker     }
264*6236dae4SAndroid Build Coastguard Worker     if(1 == Curl_inet_pton(AF_INET6, as->src.host, ipv6_unused)) {
265*6236dae4SAndroid Build Coastguard Worker       src6_pre = "[";
266*6236dae4SAndroid Build Coastguard Worker       src6_post = "]";
267*6236dae4SAndroid Build Coastguard Worker     }
268*6236dae4SAndroid Build Coastguard Worker   }
269*6236dae4SAndroid Build Coastguard Worker #endif
270*6236dae4SAndroid Build Coastguard Worker   fprintf(fp,
271*6236dae4SAndroid Build Coastguard Worker           "%s %s%s%s %u "
272*6236dae4SAndroid Build Coastguard Worker           "%s %s%s%s %u "
273*6236dae4SAndroid Build Coastguard Worker           "\"%d%02d%02d "
274*6236dae4SAndroid Build Coastguard Worker           "%02d:%02d:%02d\" "
275*6236dae4SAndroid Build Coastguard Worker           "%u %u\n",
276*6236dae4SAndroid Build Coastguard Worker           Curl_alpnid2str(as->src.alpnid),
277*6236dae4SAndroid Build Coastguard Worker           src6_pre, as->src.host, src6_post,
278*6236dae4SAndroid Build Coastguard Worker           as->src.port,
279*6236dae4SAndroid Build Coastguard Worker 
280*6236dae4SAndroid Build Coastguard Worker           Curl_alpnid2str(as->dst.alpnid),
281*6236dae4SAndroid Build Coastguard Worker           dst6_pre, as->dst.host, dst6_post,
282*6236dae4SAndroid Build Coastguard Worker           as->dst.port,
283*6236dae4SAndroid Build Coastguard Worker 
284*6236dae4SAndroid Build Coastguard Worker           stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
285*6236dae4SAndroid Build Coastguard Worker           stamp.tm_hour, stamp.tm_min, stamp.tm_sec,
286*6236dae4SAndroid Build Coastguard Worker           as->persist, as->prio);
287*6236dae4SAndroid Build Coastguard Worker   return CURLE_OK;
288*6236dae4SAndroid Build Coastguard Worker }
289*6236dae4SAndroid Build Coastguard Worker 
290*6236dae4SAndroid Build Coastguard Worker /* ---- library-wide functions below ---- */
291*6236dae4SAndroid Build Coastguard Worker 
292*6236dae4SAndroid Build Coastguard Worker /*
293*6236dae4SAndroid Build Coastguard Worker  * Curl_altsvc_init() creates a new altsvc cache.
294*6236dae4SAndroid Build Coastguard Worker  * It returns the new instance or NULL if something goes wrong.
295*6236dae4SAndroid Build Coastguard Worker  */
Curl_altsvc_init(void)296*6236dae4SAndroid Build Coastguard Worker struct altsvcinfo *Curl_altsvc_init(void)
297*6236dae4SAndroid Build Coastguard Worker {
298*6236dae4SAndroid Build Coastguard Worker   struct altsvcinfo *asi = calloc(1, sizeof(struct altsvcinfo));
299*6236dae4SAndroid Build Coastguard Worker   if(!asi)
300*6236dae4SAndroid Build Coastguard Worker     return NULL;
301*6236dae4SAndroid Build Coastguard Worker   Curl_llist_init(&asi->list, NULL);
302*6236dae4SAndroid Build Coastguard Worker 
303*6236dae4SAndroid Build Coastguard Worker   /* set default behavior */
304*6236dae4SAndroid Build Coastguard Worker   asi->flags = CURLALTSVC_H1
305*6236dae4SAndroid Build Coastguard Worker #ifdef USE_HTTP2
306*6236dae4SAndroid Build Coastguard Worker     | CURLALTSVC_H2
307*6236dae4SAndroid Build Coastguard Worker #endif
308*6236dae4SAndroid Build Coastguard Worker #ifdef USE_HTTP3
309*6236dae4SAndroid Build Coastguard Worker     | CURLALTSVC_H3
310*6236dae4SAndroid Build Coastguard Worker #endif
311*6236dae4SAndroid Build Coastguard Worker     ;
312*6236dae4SAndroid Build Coastguard Worker   return asi;
313*6236dae4SAndroid Build Coastguard Worker }
314*6236dae4SAndroid Build Coastguard Worker 
315*6236dae4SAndroid Build Coastguard Worker /*
316*6236dae4SAndroid Build Coastguard Worker  * Curl_altsvc_load() loads alt-svc from file.
317*6236dae4SAndroid Build Coastguard Worker  */
Curl_altsvc_load(struct altsvcinfo * asi,const char * file)318*6236dae4SAndroid Build Coastguard Worker CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file)
319*6236dae4SAndroid Build Coastguard Worker {
320*6236dae4SAndroid Build Coastguard Worker   DEBUGASSERT(asi);
321*6236dae4SAndroid Build Coastguard Worker   return altsvc_load(asi, file);
322*6236dae4SAndroid Build Coastguard Worker }
323*6236dae4SAndroid Build Coastguard Worker 
324*6236dae4SAndroid Build Coastguard Worker /*
325*6236dae4SAndroid Build Coastguard Worker  * Curl_altsvc_ctrl() passes on the external bitmask.
326*6236dae4SAndroid Build Coastguard Worker  */
Curl_altsvc_ctrl(struct altsvcinfo * asi,const long ctrl)327*6236dae4SAndroid Build Coastguard Worker CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl)
328*6236dae4SAndroid Build Coastguard Worker {
329*6236dae4SAndroid Build Coastguard Worker   DEBUGASSERT(asi);
330*6236dae4SAndroid Build Coastguard Worker   asi->flags = ctrl;
331*6236dae4SAndroid Build Coastguard Worker   return CURLE_OK;
332*6236dae4SAndroid Build Coastguard Worker }
333*6236dae4SAndroid Build Coastguard Worker 
334*6236dae4SAndroid Build Coastguard Worker /*
335*6236dae4SAndroid Build Coastguard Worker  * Curl_altsvc_cleanup() frees an altsvc cache instance and all associated
336*6236dae4SAndroid Build Coastguard Worker  * resources.
337*6236dae4SAndroid Build Coastguard Worker  */
Curl_altsvc_cleanup(struct altsvcinfo ** altsvcp)338*6236dae4SAndroid Build Coastguard Worker void Curl_altsvc_cleanup(struct altsvcinfo **altsvcp)
339*6236dae4SAndroid Build Coastguard Worker {
340*6236dae4SAndroid Build Coastguard Worker   if(*altsvcp) {
341*6236dae4SAndroid Build Coastguard Worker     struct Curl_llist_node *e;
342*6236dae4SAndroid Build Coastguard Worker     struct Curl_llist_node *n;
343*6236dae4SAndroid Build Coastguard Worker     struct altsvcinfo *altsvc = *altsvcp;
344*6236dae4SAndroid Build Coastguard Worker     for(e = Curl_llist_head(&altsvc->list); e; e = n) {
345*6236dae4SAndroid Build Coastguard Worker       struct altsvc *as = Curl_node_elem(e);
346*6236dae4SAndroid Build Coastguard Worker       n = Curl_node_next(e);
347*6236dae4SAndroid Build Coastguard Worker       altsvc_free(as);
348*6236dae4SAndroid Build Coastguard Worker     }
349*6236dae4SAndroid Build Coastguard Worker     free(altsvc->filename);
350*6236dae4SAndroid Build Coastguard Worker     free(altsvc);
351*6236dae4SAndroid Build Coastguard Worker     *altsvcp = NULL; /* clear the pointer */
352*6236dae4SAndroid Build Coastguard Worker   }
353*6236dae4SAndroid Build Coastguard Worker }
354*6236dae4SAndroid Build Coastguard Worker 
355*6236dae4SAndroid Build Coastguard Worker /*
356*6236dae4SAndroid Build Coastguard Worker  * Curl_altsvc_save() writes the altsvc cache to a file.
357*6236dae4SAndroid Build Coastguard Worker  */
Curl_altsvc_save(struct Curl_easy * data,struct altsvcinfo * altsvc,const char * file)358*6236dae4SAndroid Build Coastguard Worker CURLcode Curl_altsvc_save(struct Curl_easy *data,
359*6236dae4SAndroid Build Coastguard Worker                           struct altsvcinfo *altsvc, const char *file)
360*6236dae4SAndroid Build Coastguard Worker {
361*6236dae4SAndroid Build Coastguard Worker   CURLcode result = CURLE_OK;
362*6236dae4SAndroid Build Coastguard Worker   FILE *out;
363*6236dae4SAndroid Build Coastguard Worker   char *tempstore = NULL;
364*6236dae4SAndroid Build Coastguard Worker 
365*6236dae4SAndroid Build Coastguard Worker   if(!altsvc)
366*6236dae4SAndroid Build Coastguard Worker     /* no cache activated */
367*6236dae4SAndroid Build Coastguard Worker     return CURLE_OK;
368*6236dae4SAndroid Build Coastguard Worker 
369*6236dae4SAndroid Build Coastguard Worker   /* if not new name is given, use the one we stored from the load */
370*6236dae4SAndroid Build Coastguard Worker   if(!file && altsvc->filename)
371*6236dae4SAndroid Build Coastguard Worker     file = altsvc->filename;
372*6236dae4SAndroid Build Coastguard Worker 
373*6236dae4SAndroid Build Coastguard Worker   if((altsvc->flags & CURLALTSVC_READONLYFILE) || !file || !file[0])
374*6236dae4SAndroid Build Coastguard Worker     /* marked as read-only, no file or zero length filename */
375*6236dae4SAndroid Build Coastguard Worker     return CURLE_OK;
376*6236dae4SAndroid Build Coastguard Worker 
377*6236dae4SAndroid Build Coastguard Worker   result = Curl_fopen(data, file, &out, &tempstore);
378*6236dae4SAndroid Build Coastguard Worker   if(!result) {
379*6236dae4SAndroid Build Coastguard Worker     struct Curl_llist_node *e;
380*6236dae4SAndroid Build Coastguard Worker     struct Curl_llist_node *n;
381*6236dae4SAndroid Build Coastguard Worker     fputs("# Your alt-svc cache. https://curl.se/docs/alt-svc.html\n"
382*6236dae4SAndroid Build Coastguard Worker           "# This file was generated by libcurl! Edit at your own risk.\n",
383*6236dae4SAndroid Build Coastguard Worker           out);
384*6236dae4SAndroid Build Coastguard Worker     for(e = Curl_llist_head(&altsvc->list); e; e = n) {
385*6236dae4SAndroid Build Coastguard Worker       struct altsvc *as = Curl_node_elem(e);
386*6236dae4SAndroid Build Coastguard Worker       n = Curl_node_next(e);
387*6236dae4SAndroid Build Coastguard Worker       result = altsvc_out(as, out);
388*6236dae4SAndroid Build Coastguard Worker       if(result)
389*6236dae4SAndroid Build Coastguard Worker         break;
390*6236dae4SAndroid Build Coastguard Worker     }
391*6236dae4SAndroid Build Coastguard Worker     fclose(out);
392*6236dae4SAndroid Build Coastguard Worker     if(!result && tempstore && Curl_rename(tempstore, file))
393*6236dae4SAndroid Build Coastguard Worker       result = CURLE_WRITE_ERROR;
394*6236dae4SAndroid Build Coastguard Worker 
395*6236dae4SAndroid Build Coastguard Worker     if(result && tempstore)
396*6236dae4SAndroid Build Coastguard Worker       unlink(tempstore);
397*6236dae4SAndroid Build Coastguard Worker   }
398*6236dae4SAndroid Build Coastguard Worker   free(tempstore);
399*6236dae4SAndroid Build Coastguard Worker   return result;
400*6236dae4SAndroid Build Coastguard Worker }
401*6236dae4SAndroid Build Coastguard Worker 
getalnum(const char ** ptr,char * alpnbuf,size_t buflen)402*6236dae4SAndroid Build Coastguard Worker static CURLcode getalnum(const char **ptr, char *alpnbuf, size_t buflen)
403*6236dae4SAndroid Build Coastguard Worker {
404*6236dae4SAndroid Build Coastguard Worker   size_t len;
405*6236dae4SAndroid Build Coastguard Worker   const char *protop;
406*6236dae4SAndroid Build Coastguard Worker   const char *p = *ptr;
407*6236dae4SAndroid Build Coastguard Worker   while(*p && ISBLANK(*p))
408*6236dae4SAndroid Build Coastguard Worker     p++;
409*6236dae4SAndroid Build Coastguard Worker   protop = p;
410*6236dae4SAndroid Build Coastguard Worker   while(*p && !ISBLANK(*p) && (*p != ';') && (*p != '='))
411*6236dae4SAndroid Build Coastguard Worker     p++;
412*6236dae4SAndroid Build Coastguard Worker   len = p - protop;
413*6236dae4SAndroid Build Coastguard Worker   *ptr = p;
414*6236dae4SAndroid Build Coastguard Worker 
415*6236dae4SAndroid Build Coastguard Worker   if(!len || (len >= buflen))
416*6236dae4SAndroid Build Coastguard Worker     return CURLE_BAD_FUNCTION_ARGUMENT;
417*6236dae4SAndroid Build Coastguard Worker   memcpy(alpnbuf, protop, len);
418*6236dae4SAndroid Build Coastguard Worker   alpnbuf[len] = 0;
419*6236dae4SAndroid Build Coastguard Worker   return CURLE_OK;
420*6236dae4SAndroid Build Coastguard Worker }
421*6236dae4SAndroid Build Coastguard Worker 
422*6236dae4SAndroid Build Coastguard Worker /* hostcompare() returns true if 'host' matches 'check'. The first host
423*6236dae4SAndroid Build Coastguard Worker  * argument may have a trailing dot present that will be ignored.
424*6236dae4SAndroid Build Coastguard Worker  */
hostcompare(const char * host,const char * check)425*6236dae4SAndroid Build Coastguard Worker static bool hostcompare(const char *host, const char *check)
426*6236dae4SAndroid Build Coastguard Worker {
427*6236dae4SAndroid Build Coastguard Worker   size_t hlen = strlen(host);
428*6236dae4SAndroid Build Coastguard Worker   size_t clen = strlen(check);
429*6236dae4SAndroid Build Coastguard Worker 
430*6236dae4SAndroid Build Coastguard Worker   if(hlen && (host[hlen - 1] == '.'))
431*6236dae4SAndroid Build Coastguard Worker     hlen--;
432*6236dae4SAndroid Build Coastguard Worker   if(hlen != clen)
433*6236dae4SAndroid Build Coastguard Worker     /* they cannot match if they have different lengths */
434*6236dae4SAndroid Build Coastguard Worker     return FALSE;
435*6236dae4SAndroid Build Coastguard Worker   return strncasecompare(host, check, hlen);
436*6236dae4SAndroid Build Coastguard Worker }
437*6236dae4SAndroid Build Coastguard Worker 
438*6236dae4SAndroid Build Coastguard Worker /* altsvc_flush() removes all alternatives for this source origin from the
439*6236dae4SAndroid Build Coastguard Worker    list */
altsvc_flush(struct altsvcinfo * asi,enum alpnid srcalpnid,const char * srchost,unsigned short srcport)440*6236dae4SAndroid Build Coastguard Worker static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid,
441*6236dae4SAndroid Build Coastguard Worker                          const char *srchost, unsigned short srcport)
442*6236dae4SAndroid Build Coastguard Worker {
443*6236dae4SAndroid Build Coastguard Worker   struct Curl_llist_node *e;
444*6236dae4SAndroid Build Coastguard Worker   struct Curl_llist_node *n;
445*6236dae4SAndroid Build Coastguard Worker   for(e = Curl_llist_head(&asi->list); e; e = n) {
446*6236dae4SAndroid Build Coastguard Worker     struct altsvc *as = Curl_node_elem(e);
447*6236dae4SAndroid Build Coastguard Worker     n = Curl_node_next(e);
448*6236dae4SAndroid Build Coastguard Worker     if((srcalpnid == as->src.alpnid) &&
449*6236dae4SAndroid Build Coastguard Worker        (srcport == as->src.port) &&
450*6236dae4SAndroid Build Coastguard Worker        hostcompare(srchost, as->src.host)) {
451*6236dae4SAndroid Build Coastguard Worker       Curl_node_remove(e);
452*6236dae4SAndroid Build Coastguard Worker       altsvc_free(as);
453*6236dae4SAndroid Build Coastguard Worker     }
454*6236dae4SAndroid Build Coastguard Worker   }
455*6236dae4SAndroid Build Coastguard Worker }
456*6236dae4SAndroid Build Coastguard Worker 
457*6236dae4SAndroid Build Coastguard Worker #ifdef DEBUGBUILD
458*6236dae4SAndroid Build Coastguard Worker /* to play well with debug builds, we can *set* a fixed time this will
459*6236dae4SAndroid Build Coastguard Worker    return */
altsvc_debugtime(void * unused)460*6236dae4SAndroid Build Coastguard Worker static time_t altsvc_debugtime(void *unused)
461*6236dae4SAndroid Build Coastguard Worker {
462*6236dae4SAndroid Build Coastguard Worker   char *timestr = getenv("CURL_TIME");
463*6236dae4SAndroid Build Coastguard Worker   (void)unused;
464*6236dae4SAndroid Build Coastguard Worker   if(timestr) {
465*6236dae4SAndroid Build Coastguard Worker     long val = strtol(timestr, NULL, 10);
466*6236dae4SAndroid Build Coastguard Worker     return (time_t)val;
467*6236dae4SAndroid Build Coastguard Worker   }
468*6236dae4SAndroid Build Coastguard Worker   return time(NULL);
469*6236dae4SAndroid Build Coastguard Worker }
470*6236dae4SAndroid Build Coastguard Worker #undef time
471*6236dae4SAndroid Build Coastguard Worker #define time(x) altsvc_debugtime(x)
472*6236dae4SAndroid Build Coastguard Worker #endif
473*6236dae4SAndroid Build Coastguard Worker 
474*6236dae4SAndroid Build Coastguard Worker #define ISNEWLINE(x) (((x) == '\n') || (x) == '\r')
475*6236dae4SAndroid Build Coastguard Worker 
476*6236dae4SAndroid Build Coastguard Worker /*
477*6236dae4SAndroid Build Coastguard Worker  * Curl_altsvc_parse() takes an incoming alt-svc response header and stores
478*6236dae4SAndroid Build Coastguard Worker  * the data correctly in the cache.
479*6236dae4SAndroid Build Coastguard Worker  *
480*6236dae4SAndroid Build Coastguard Worker  * 'value' points to the header *value*. That is contents to the right of the
481*6236dae4SAndroid Build Coastguard Worker  * header name.
482*6236dae4SAndroid Build Coastguard Worker  *
483*6236dae4SAndroid Build Coastguard Worker  * Currently this function rejects invalid data without returning an error.
484*6236dae4SAndroid Build Coastguard Worker  * Invalid hostname, port number will result in the specific alternative
485*6236dae4SAndroid Build Coastguard Worker  * being rejected. Unknown protocols are skipped.
486*6236dae4SAndroid Build Coastguard Worker  */
Curl_altsvc_parse(struct Curl_easy * data,struct altsvcinfo * asi,const char * value,enum alpnid srcalpnid,const char * srchost,unsigned short srcport)487*6236dae4SAndroid Build Coastguard Worker CURLcode Curl_altsvc_parse(struct Curl_easy *data,
488*6236dae4SAndroid Build Coastguard Worker                            struct altsvcinfo *asi, const char *value,
489*6236dae4SAndroid Build Coastguard Worker                            enum alpnid srcalpnid, const char *srchost,
490*6236dae4SAndroid Build Coastguard Worker                            unsigned short srcport)
491*6236dae4SAndroid Build Coastguard Worker {
492*6236dae4SAndroid Build Coastguard Worker   const char *p = value;
493*6236dae4SAndroid Build Coastguard Worker   char alpnbuf[MAX_ALTSVC_ALPNLEN] = "";
494*6236dae4SAndroid Build Coastguard Worker   struct altsvc *as;
495*6236dae4SAndroid Build Coastguard Worker   unsigned short dstport = srcport; /* the same by default */
496*6236dae4SAndroid Build Coastguard Worker   CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
497*6236dae4SAndroid Build Coastguard Worker   size_t entries = 0;
498*6236dae4SAndroid Build Coastguard Worker #ifdef CURL_DISABLE_VERBOSE_STRINGS
499*6236dae4SAndroid Build Coastguard Worker   (void)data;
500*6236dae4SAndroid Build Coastguard Worker #endif
501*6236dae4SAndroid Build Coastguard Worker   if(result) {
502*6236dae4SAndroid Build Coastguard Worker     infof(data, "Excessive alt-svc header, ignoring.");
503*6236dae4SAndroid Build Coastguard Worker     return CURLE_OK;
504*6236dae4SAndroid Build Coastguard Worker   }
505*6236dae4SAndroid Build Coastguard Worker 
506*6236dae4SAndroid Build Coastguard Worker   DEBUGASSERT(asi);
507*6236dae4SAndroid Build Coastguard Worker 
508*6236dae4SAndroid Build Coastguard Worker   /* "clear" is a magic keyword */
509*6236dae4SAndroid Build Coastguard Worker   if(strcasecompare(alpnbuf, "clear")) {
510*6236dae4SAndroid Build Coastguard Worker     /* Flush cached alternatives for this source origin */
511*6236dae4SAndroid Build Coastguard Worker     altsvc_flush(asi, srcalpnid, srchost, srcport);
512*6236dae4SAndroid Build Coastguard Worker     return CURLE_OK;
513*6236dae4SAndroid Build Coastguard Worker   }
514*6236dae4SAndroid Build Coastguard Worker 
515*6236dae4SAndroid Build Coastguard Worker   do {
516*6236dae4SAndroid Build Coastguard Worker     if(*p == '=') {
517*6236dae4SAndroid Build Coastguard Worker       /* [protocol]="[host][:port]" */
518*6236dae4SAndroid Build Coastguard Worker       enum alpnid dstalpnid = alpn2alpnid(alpnbuf); /* the same by default */
519*6236dae4SAndroid Build Coastguard Worker       p++;
520*6236dae4SAndroid Build Coastguard Worker       if(*p == '\"') {
521*6236dae4SAndroid Build Coastguard Worker         const char *dsthost = "";
522*6236dae4SAndroid Build Coastguard Worker         size_t dstlen = 0; /* destination hostname length */
523*6236dae4SAndroid Build Coastguard Worker         const char *value_ptr;
524*6236dae4SAndroid Build Coastguard Worker         char option[32];
525*6236dae4SAndroid Build Coastguard Worker         unsigned long num;
526*6236dae4SAndroid Build Coastguard Worker         char *end_ptr;
527*6236dae4SAndroid Build Coastguard Worker         bool quoted = FALSE;
528*6236dae4SAndroid Build Coastguard Worker         time_t maxage = 24 * 3600; /* default is 24 hours */
529*6236dae4SAndroid Build Coastguard Worker         bool persist = FALSE;
530*6236dae4SAndroid Build Coastguard Worker         bool valid = TRUE;
531*6236dae4SAndroid Build Coastguard Worker         p++;
532*6236dae4SAndroid Build Coastguard Worker         if(*p != ':') {
533*6236dae4SAndroid Build Coastguard Worker           /* hostname starts here */
534*6236dae4SAndroid Build Coastguard Worker           const char *hostp = p;
535*6236dae4SAndroid Build Coastguard Worker           if(*p == '[') {
536*6236dae4SAndroid Build Coastguard Worker             /* pass all valid IPv6 letters - does not handle zone id */
537*6236dae4SAndroid Build Coastguard Worker             dstlen = strspn(++p, "0123456789abcdefABCDEF:.");
538*6236dae4SAndroid Build Coastguard Worker             if(p[dstlen] != ']')
539*6236dae4SAndroid Build Coastguard Worker               /* invalid host syntax, bail out */
540*6236dae4SAndroid Build Coastguard Worker               break;
541*6236dae4SAndroid Build Coastguard Worker             /* we store the IPv6 numerical address *with* brackets */
542*6236dae4SAndroid Build Coastguard Worker             dstlen += 2;
543*6236dae4SAndroid Build Coastguard Worker             p = &p[dstlen-1];
544*6236dae4SAndroid Build Coastguard Worker           }
545*6236dae4SAndroid Build Coastguard Worker           else {
546*6236dae4SAndroid Build Coastguard Worker             while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-')))
547*6236dae4SAndroid Build Coastguard Worker               p++;
548*6236dae4SAndroid Build Coastguard Worker             dstlen = p - hostp;
549*6236dae4SAndroid Build Coastguard Worker           }
550*6236dae4SAndroid Build Coastguard Worker           if(!dstlen || (dstlen >= MAX_ALTSVC_HOSTLEN)) {
551*6236dae4SAndroid Build Coastguard Worker             infof(data, "Excessive alt-svc hostname, ignoring.");
552*6236dae4SAndroid Build Coastguard Worker             valid = FALSE;
553*6236dae4SAndroid Build Coastguard Worker           }
554*6236dae4SAndroid Build Coastguard Worker           else {
555*6236dae4SAndroid Build Coastguard Worker             dsthost = hostp;
556*6236dae4SAndroid Build Coastguard Worker           }
557*6236dae4SAndroid Build Coastguard Worker         }
558*6236dae4SAndroid Build Coastguard Worker         else {
559*6236dae4SAndroid Build Coastguard Worker           /* no destination name, use source host */
560*6236dae4SAndroid Build Coastguard Worker           dsthost = srchost;
561*6236dae4SAndroid Build Coastguard Worker           dstlen = strlen(srchost);
562*6236dae4SAndroid Build Coastguard Worker         }
563*6236dae4SAndroid Build Coastguard Worker         if(*p == ':') {
564*6236dae4SAndroid Build Coastguard Worker           unsigned long port = 0;
565*6236dae4SAndroid Build Coastguard Worker           p++;
566*6236dae4SAndroid Build Coastguard Worker           if(ISDIGIT(*p))
567*6236dae4SAndroid Build Coastguard Worker             /* a port number */
568*6236dae4SAndroid Build Coastguard Worker             port = strtoul(p, &end_ptr, 10);
569*6236dae4SAndroid Build Coastguard Worker           else
570*6236dae4SAndroid Build Coastguard Worker             end_ptr = (char *)p; /* not left uninitialized */
571*6236dae4SAndroid Build Coastguard Worker           if(!port || port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') {
572*6236dae4SAndroid Build Coastguard Worker             infof(data, "Unknown alt-svc port number, ignoring.");
573*6236dae4SAndroid Build Coastguard Worker             valid = FALSE;
574*6236dae4SAndroid Build Coastguard Worker           }
575*6236dae4SAndroid Build Coastguard Worker           else {
576*6236dae4SAndroid Build Coastguard Worker             dstport = curlx_ultous(port);
577*6236dae4SAndroid Build Coastguard Worker             p = end_ptr;
578*6236dae4SAndroid Build Coastguard Worker           }
579*6236dae4SAndroid Build Coastguard Worker         }
580*6236dae4SAndroid Build Coastguard Worker         if(*p++ != '\"')
581*6236dae4SAndroid Build Coastguard Worker           break;
582*6236dae4SAndroid Build Coastguard Worker         /* Handle the optional 'ma' and 'persist' flags. Unknown flags
583*6236dae4SAndroid Build Coastguard Worker            are skipped. */
584*6236dae4SAndroid Build Coastguard Worker         for(;;) {
585*6236dae4SAndroid Build Coastguard Worker           while(ISBLANK(*p))
586*6236dae4SAndroid Build Coastguard Worker             p++;
587*6236dae4SAndroid Build Coastguard Worker           if(*p != ';')
588*6236dae4SAndroid Build Coastguard Worker             break;
589*6236dae4SAndroid Build Coastguard Worker           p++; /* pass the semicolon */
590*6236dae4SAndroid Build Coastguard Worker           if(!*p || ISNEWLINE(*p))
591*6236dae4SAndroid Build Coastguard Worker             break;
592*6236dae4SAndroid Build Coastguard Worker           result = getalnum(&p, option, sizeof(option));
593*6236dae4SAndroid Build Coastguard Worker           if(result) {
594*6236dae4SAndroid Build Coastguard Worker             /* skip option if name is too long */
595*6236dae4SAndroid Build Coastguard Worker             option[0] = '\0';
596*6236dae4SAndroid Build Coastguard Worker           }
597*6236dae4SAndroid Build Coastguard Worker           while(*p && ISBLANK(*p))
598*6236dae4SAndroid Build Coastguard Worker             p++;
599*6236dae4SAndroid Build Coastguard Worker           if(*p != '=')
600*6236dae4SAndroid Build Coastguard Worker             return CURLE_OK;
601*6236dae4SAndroid Build Coastguard Worker           p++;
602*6236dae4SAndroid Build Coastguard Worker           while(*p && ISBLANK(*p))
603*6236dae4SAndroid Build Coastguard Worker             p++;
604*6236dae4SAndroid Build Coastguard Worker           if(!*p)
605*6236dae4SAndroid Build Coastguard Worker             return CURLE_OK;
606*6236dae4SAndroid Build Coastguard Worker           if(*p == '\"') {
607*6236dae4SAndroid Build Coastguard Worker             /* quoted value */
608*6236dae4SAndroid Build Coastguard Worker             p++;
609*6236dae4SAndroid Build Coastguard Worker             quoted = TRUE;
610*6236dae4SAndroid Build Coastguard Worker           }
611*6236dae4SAndroid Build Coastguard Worker           value_ptr = p;
612*6236dae4SAndroid Build Coastguard Worker           if(quoted) {
613*6236dae4SAndroid Build Coastguard Worker             while(*p && *p != '\"')
614*6236dae4SAndroid Build Coastguard Worker               p++;
615*6236dae4SAndroid Build Coastguard Worker             if(!*p++)
616*6236dae4SAndroid Build Coastguard Worker               return CURLE_OK;
617*6236dae4SAndroid Build Coastguard Worker           }
618*6236dae4SAndroid Build Coastguard Worker           else {
619*6236dae4SAndroid Build Coastguard Worker             while(*p && !ISBLANK(*p) && *p!= ';' && *p != ',')
620*6236dae4SAndroid Build Coastguard Worker               p++;
621*6236dae4SAndroid Build Coastguard Worker           }
622*6236dae4SAndroid Build Coastguard Worker           num = strtoul(value_ptr, &end_ptr, 10);
623*6236dae4SAndroid Build Coastguard Worker           if((end_ptr != value_ptr) && (num < ULONG_MAX)) {
624*6236dae4SAndroid Build Coastguard Worker             if(strcasecompare("ma", option))
625*6236dae4SAndroid Build Coastguard Worker               maxage = (time_t)num;
626*6236dae4SAndroid Build Coastguard Worker             else if(strcasecompare("persist", option) && (num == 1))
627*6236dae4SAndroid Build Coastguard Worker               persist = TRUE;
628*6236dae4SAndroid Build Coastguard Worker           }
629*6236dae4SAndroid Build Coastguard Worker         }
630*6236dae4SAndroid Build Coastguard Worker         if(dstalpnid && valid) {
631*6236dae4SAndroid Build Coastguard Worker           if(!entries++)
632*6236dae4SAndroid Build Coastguard Worker             /* Flush cached alternatives for this source origin, if any - when
633*6236dae4SAndroid Build Coastguard Worker                this is the first entry of the line. */
634*6236dae4SAndroid Build Coastguard Worker             altsvc_flush(asi, srcalpnid, srchost, srcport);
635*6236dae4SAndroid Build Coastguard Worker 
636*6236dae4SAndroid Build Coastguard Worker           as = altsvc_createid(srchost, dsthost, dstlen,
637*6236dae4SAndroid Build Coastguard Worker                                srcalpnid, dstalpnid,
638*6236dae4SAndroid Build Coastguard Worker                                srcport, dstport);
639*6236dae4SAndroid Build Coastguard Worker           if(as) {
640*6236dae4SAndroid Build Coastguard Worker             /* The expires time also needs to take the Age: value (if any) into
641*6236dae4SAndroid Build Coastguard Worker                account. [See RFC 7838 section 3.1] */
642*6236dae4SAndroid Build Coastguard Worker             as->expires = maxage + time(NULL);
643*6236dae4SAndroid Build Coastguard Worker             as->persist = persist;
644*6236dae4SAndroid Build Coastguard Worker             Curl_llist_append(&asi->list, as, &as->node);
645*6236dae4SAndroid Build Coastguard Worker             infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport,
646*6236dae4SAndroid Build Coastguard Worker                   Curl_alpnid2str(dstalpnid));
647*6236dae4SAndroid Build Coastguard Worker           }
648*6236dae4SAndroid Build Coastguard Worker         }
649*6236dae4SAndroid Build Coastguard Worker       }
650*6236dae4SAndroid Build Coastguard Worker       else
651*6236dae4SAndroid Build Coastguard Worker         break;
652*6236dae4SAndroid Build Coastguard Worker       /* after the double quote there can be a comma if there is another
653*6236dae4SAndroid Build Coastguard Worker          string or a semicolon if no more */
654*6236dae4SAndroid Build Coastguard Worker       if(*p == ',') {
655*6236dae4SAndroid Build Coastguard Worker         /* comma means another alternative is presented */
656*6236dae4SAndroid Build Coastguard Worker         p++;
657*6236dae4SAndroid Build Coastguard Worker         result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
658*6236dae4SAndroid Build Coastguard Worker         if(result)
659*6236dae4SAndroid Build Coastguard Worker           break;
660*6236dae4SAndroid Build Coastguard Worker       }
661*6236dae4SAndroid Build Coastguard Worker     }
662*6236dae4SAndroid Build Coastguard Worker     else
663*6236dae4SAndroid Build Coastguard Worker       break;
664*6236dae4SAndroid Build Coastguard Worker   } while(*p && (*p != ';') && (*p != '\n') && (*p != '\r'));
665*6236dae4SAndroid Build Coastguard Worker 
666*6236dae4SAndroid Build Coastguard Worker   return CURLE_OK;
667*6236dae4SAndroid Build Coastguard Worker }
668*6236dae4SAndroid Build Coastguard Worker 
669*6236dae4SAndroid Build Coastguard Worker /*
670*6236dae4SAndroid Build Coastguard Worker  * Return TRUE on a match
671*6236dae4SAndroid Build Coastguard Worker  */
Curl_altsvc_lookup(struct altsvcinfo * asi,enum alpnid srcalpnid,const char * srchost,int srcport,struct altsvc ** dstentry,const int versions)672*6236dae4SAndroid Build Coastguard Worker bool Curl_altsvc_lookup(struct altsvcinfo *asi,
673*6236dae4SAndroid Build Coastguard Worker                         enum alpnid srcalpnid, const char *srchost,
674*6236dae4SAndroid Build Coastguard Worker                         int srcport,
675*6236dae4SAndroid Build Coastguard Worker                         struct altsvc **dstentry,
676*6236dae4SAndroid Build Coastguard Worker                         const int versions) /* one or more bits */
677*6236dae4SAndroid Build Coastguard Worker {
678*6236dae4SAndroid Build Coastguard Worker   struct Curl_llist_node *e;
679*6236dae4SAndroid Build Coastguard Worker   struct Curl_llist_node *n;
680*6236dae4SAndroid Build Coastguard Worker   time_t now = time(NULL);
681*6236dae4SAndroid Build Coastguard Worker   DEBUGASSERT(asi);
682*6236dae4SAndroid Build Coastguard Worker   DEBUGASSERT(srchost);
683*6236dae4SAndroid Build Coastguard Worker   DEBUGASSERT(dstentry);
684*6236dae4SAndroid Build Coastguard Worker 
685*6236dae4SAndroid Build Coastguard Worker   for(e = Curl_llist_head(&asi->list); e; e = n) {
686*6236dae4SAndroid Build Coastguard Worker     struct altsvc *as = Curl_node_elem(e);
687*6236dae4SAndroid Build Coastguard Worker     n = Curl_node_next(e);
688*6236dae4SAndroid Build Coastguard Worker     if(as->expires < now) {
689*6236dae4SAndroid Build Coastguard Worker       /* an expired entry, remove */
690*6236dae4SAndroid Build Coastguard Worker       Curl_node_remove(e);
691*6236dae4SAndroid Build Coastguard Worker       altsvc_free(as);
692*6236dae4SAndroid Build Coastguard Worker       continue;
693*6236dae4SAndroid Build Coastguard Worker     }
694*6236dae4SAndroid Build Coastguard Worker     if((as->src.alpnid == srcalpnid) &&
695*6236dae4SAndroid Build Coastguard Worker        hostcompare(srchost, as->src.host) &&
696*6236dae4SAndroid Build Coastguard Worker        (as->src.port == srcport) &&
697*6236dae4SAndroid Build Coastguard Worker        (versions & (int)as->dst.alpnid)) {
698*6236dae4SAndroid Build Coastguard Worker       /* match */
699*6236dae4SAndroid Build Coastguard Worker       *dstentry = as;
700*6236dae4SAndroid Build Coastguard Worker       return TRUE;
701*6236dae4SAndroid Build Coastguard Worker     }
702*6236dae4SAndroid Build Coastguard Worker   }
703*6236dae4SAndroid Build Coastguard Worker   return FALSE;
704*6236dae4SAndroid Build Coastguard Worker }
705*6236dae4SAndroid Build Coastguard Worker 
706*6236dae4SAndroid Build Coastguard Worker #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_ALTSVC */
707