Annotation of embedaddon/curl/lib/altsvc.c, revision 1.1.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>