Annotation of embedaddon/curl/lib/altsvc.c, revision 1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>