CURLcode Curl_altsvc_parse()

in libs/curl/lib/altsvc.c [487:669]


CURLcode Curl_altsvc_parse(struct Curl_easy *data,
                           struct altsvcinfo *asi, const char *value,
                           enum alpnid srcalpnid, const char *srchost,
                           unsigned short srcport)
{
  const char *p = value;
  size_t len;
  char namebuf[MAX_ALTSVC_HOSTLEN] = "";
  char alpnbuf[MAX_ALTSVC_ALPNLEN] = "";
  struct altsvc *as;
  unsigned short dstport = srcport; /* the same by default */
  CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
  size_t entries = 0;
#ifdef CURL_DISABLE_VERBOSE_STRINGS
  (void)data;
#endif
  if(result) {
    infof(data, "Excessive alt-svc header, ignoring.");
    return CURLE_OK;
  }

  DEBUGASSERT(asi);

  /* "clear" is a magic keyword */
  if(strcasecompare(alpnbuf, "clear")) {
    /* Flush cached alternatives for this source origin */
    altsvc_flush(asi, srcalpnid, srchost, srcport);
    return CURLE_OK;
  }

  do {
    if(*p == '=') {
      /* [protocol]="[host][:port]" */
      enum alpnid dstalpnid = alpn2alpnid(alpnbuf); /* the same by default */
      p++;
      if(*p == '\"') {
        const char *dsthost = "";
        const char *value_ptr;
        char option[32];
        unsigned long num;
        char *end_ptr;
        bool quoted = FALSE;
        time_t maxage = 24 * 3600; /* default is 24 hours */
        bool persist = FALSE;
        bool valid = TRUE;
        p++;
        if(*p != ':') {
          /* hostname starts here */
          const char *hostp = p;
          if(*p == '[') {
            /* pass all valid IPv6 letters - does not handle zone id */
            len = strspn(++p, "0123456789abcdefABCDEF:.");
            if(p[len] != ']')
              /* invalid host syntax, bail out */
              break;
            /* we store the IPv6 numerical address *with* brackets */
            len += 2;
            p = &p[len-1];
          }
          else {
            while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-')))
              p++;
            len = p - hostp;
          }
          if(!len || (len >= MAX_ALTSVC_HOSTLEN)) {
            infof(data, "Excessive alt-svc hostname, ignoring.");
            valid = FALSE;
          }
          else {
            memcpy(namebuf, hostp, len);
            namebuf[len] = 0;
            dsthost = namebuf;
          }
        }
        else {
          /* no destination name, use source host */
          dsthost = srchost;
        }
        if(*p == ':') {
          unsigned long port = 0;
          p++;
          if(ISDIGIT(*p))
            /* a port number */
            port = strtoul(p, &end_ptr, 10);
          else
            end_ptr = (char *)p; /* not left uninitialized */
          if(!port || port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') {
            infof(data, "Unknown alt-svc port number, ignoring.");
            valid = FALSE;
          }
          else {
            dstport = curlx_ultous(port);
            p = end_ptr;
          }
        }
        if(*p++ != '\"')
          break;
        /* Handle the optional 'ma' and 'persist' flags. Unknown flags
           are skipped. */
        for(;;) {
          while(ISBLANK(*p))
            p++;
          if(*p != ';')
            break;
          p++; /* pass the semicolon */
          if(!*p || ISNEWLINE(*p))
            break;
          result = getalnum(&p, option, sizeof(option));
          if(result) {
            /* skip option if name is too long */
            option[0] = '\0';
          }
          while(*p && ISBLANK(*p))
            p++;
          if(*p != '=')
            return CURLE_OK;
          p++;
          while(*p && ISBLANK(*p))
            p++;
          if(!*p)
            return CURLE_OK;
          if(*p == '\"') {
            /* quoted value */
            p++;
            quoted = TRUE;
          }
          value_ptr = p;
          if(quoted) {
            while(*p && *p != '\"')
              p++;
            if(!*p++)
              return CURLE_OK;
          }
          else {
            while(*p && !ISBLANK(*p) && *p!= ';' && *p != ',')
              p++;
          }
          num = strtoul(value_ptr, &end_ptr, 10);
          if((end_ptr != value_ptr) && (num < ULONG_MAX)) {
            if(strcasecompare("ma", option))
              maxage = (time_t)num;
            else if(strcasecompare("persist", option) && (num == 1))
              persist = TRUE;
          }
        }
        if(dstalpnid && valid) {
          if(!entries++)
            /* Flush cached alternatives for this source origin, if any - when
               this is the first entry of the line. */
            altsvc_flush(asi, srcalpnid, srchost, srcport);

          as = altsvc_createid(srchost, dsthost,
                               srcalpnid, dstalpnid,
                               srcport, dstport);
          if(as) {
            /* The expires time also needs to take the Age: value (if any) into
               account. [See RFC 7838 section 3.1] */
            as->expires = maxage + time(NULL);
            as->persist = persist;
            Curl_llist_append(&asi->list, as, &as->node);
            infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport,
                  Curl_alpnid2str(dstalpnid));
          }
        }
      }
      else
        break;
      /* after the double quote there can be a comma if there is another
         string or a semicolon if no more */
      if(*p == ',') {
        /* comma means another alternative is presented */
        p++;
        result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
        if(result)
          break;
      }
    }
    else
      break;
  } while(*p && (*p != ';') && (*p != '\n') && (*p != '\r'));

  return CURLE_OK;
}