From: Daniel Stenberg Date: Mon, 1 Sep 2025 14:36:53 +0000 (+0200) Subject: parsedate: make Curl_getdate_capped able to return epoch X-Git-Tag: curl-8_16_0~48 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f8e6e11725e5b7aca4b1f7d1e0eabbbe31f93336;p=thirdparty%2Fcurl.git parsedate: make Curl_getdate_capped able to return epoch By returning error separately on parse errors and avoiding magic numbers, this function can now return 0 or -1 as proper dates when such a date string is provided. Closes #18445 --- diff --git a/lib/altsvc.c b/lib/altsvc.c index a06f40bc2a..c7448692fb 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -187,13 +187,13 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line) else { struct altsvc *as; char dbuf[MAX_ALTSVC_DATELEN + 1]; - time_t expires; + time_t expires = 0; /* The date parser works on a null-terminated string. The maximum length is upheld by curlx_str_quotedword(). */ memcpy(dbuf, curlx_str(&date), curlx_strlen(&date)); dbuf[curlx_strlen(&date)] = 0; - expires = Curl_getdate_capped(dbuf); + Curl_getdate_capped(dbuf, &expires); as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn, (size_t)srcport, (size_t)dstport); if(as) { diff --git a/lib/cookie.c b/lib/cookie.c index 29252da41a..35d33268f9 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -107,7 +107,7 @@ static void strstore(char **str, const char *newstr, size_t len); */ static void cap_expires(time_t now, struct Cookie *co) { - if((TIME_T_MAX - COOKIES_MAXAGE - 30) > now) { + if(co->expires && (TIME_T_MAX - COOKIES_MAXAGE - 30) > now) { timediff_t cap = now + COOKIES_MAXAGE; if(co->expires > cap) { cap += 30; @@ -388,17 +388,17 @@ static void remove_expired(struct CookieInfo *ci) for(n = Curl_llist_head(&ci->cookielist[i]); n; n = e) { co = Curl_node_elem(n); e = Curl_node_next(n); - if(co->expires && co->expires < now) { - Curl_node_remove(n); - freecookie(co); - ci->numcookies--; - } - else { - /* - * If this cookie has an expiration timestamp earlier than what we - * have seen so far then record it for the next round of expirations. - */ - if(co->expires && co->expires < ci->next_expiration) + if(co->expires) { + if(co->expires < now) { + Curl_node_remove(n); + freecookie(co); + ci->numcookies--; + } + else if(co->expires < ci->next_expiration) + /* + * If this cookie has an expiration timestamp earlier than what we + * have seen so far then record it for the next round of expirations. + */ ci->next_expiration = co->expires; } } @@ -666,7 +666,6 @@ parse_cookie_header(struct Curl_easy *data, if(*maxage == '\"') maxage++; rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX); - switch(rc) { case STRE_OVERFLOW: /* overflow, used max value */ @@ -678,8 +677,7 @@ parse_cookie_header(struct Curl_easy *data, break; case STRE_OK: if(!co->expires) - /* already expired */ - co->expires = 1; + co->expires = 1; /* expire now */ else if(CURL_OFF_T_MAX - now < co->expires) /* would overflow */ co->expires = CURL_OFF_T_MAX; @@ -698,18 +696,15 @@ parse_cookie_header(struct Curl_easy *data, * will be treated as a session cookie */ char dbuf[MAX_DATE_LENGTH + 1]; + time_t date = 0; memcpy(dbuf, curlx_str(&val), curlx_strlen(&val)); dbuf[curlx_strlen(&val)] = 0; - co->expires = Curl_getdate_capped(dbuf); - - /* - * Session cookies have expires set to 0 so if we get that back - * from the date parser let's add a second to make it a - * non-session cookie - */ - if(co->expires == 0) - co->expires = 1; - else if(co->expires < 0) + if(!Curl_getdate_capped(dbuf, &date)) { + if(!date) + date++; + co->expires = (curl_off_t)date; + } + else co->expires = 0; cap_expires(now, co); } @@ -1103,7 +1098,7 @@ Curl_cookie_add(struct Curl_easy *data, if(!ci->running && /* read from a file */ ci->newsession && /* clean session cookies */ - !co->expires) /* this is a session cookie since it does not expire */ + !co->expires) /* this is a session cookie */ goto fail; co->livecookie = ci->running; diff --git a/lib/ftp.c b/lib/ftp.c index bfd772b637..6d7c210ce5 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -2102,6 +2102,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, int year, month, day, hour, minute, second; struct pingpong *pp = &ftpc->pp; char *resp = curlx_dyn_ptr(&pp->recvbuf) + 4; + bool showtime = FALSE; if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) { /* we have a time, reformat it */ char timebuf[24]; @@ -2109,7 +2110,8 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, "%04d%02d%02d %02d:%02d:%02d GMT", year, month, day, hour, minute, second); /* now, convert this into a time() value: */ - data->info.filetime = Curl_getdate_capped(timebuf); + if(!Curl_getdate_capped(timebuf, &data->info.filetime)) + showtime = TRUE; } #ifdef CURL_FTP_HTTPSTYLE_HEAD @@ -2122,10 +2124,8 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, warning: comparison of unsigned expression in '>= 0' is always true */ #pragma GCC diagnostic ignored "-Wtype-limits" #endif - if(data->req.no_body && - ftpc->file && - data->set.get_filetime && - (data->info.filetime >= 0) ) { + if(data->req.no_body && ftpc->file && + data->set.get_filetime && showtime) { #if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__)) #pragma GCC diagnostic pop #endif diff --git a/lib/hsts.c b/lib/hsts.c index 91f32276f9..9525158bcc 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -426,7 +426,7 @@ static CURLcode hsts_add(struct hsts *h, const char *line) bool subdomain = FALSE; struct stsentry *e; char dbuf[MAX_HSTS_DATELEN + 1]; - time_t expires; + time_t expires = 0; const char *hp = curlx_str(&host); /* The date parser works on a null-terminated string. The maximum length @@ -434,8 +434,10 @@ static CURLcode hsts_add(struct hsts *h, const char *line) memcpy(dbuf, curlx_str(&date), curlx_strlen(&date)); dbuf[curlx_strlen(&date)] = 0; - expires = strcmp(dbuf, UNLIMITED) ? Curl_getdate_capped(dbuf) : - TIME_T_MAX; + if(!strcmp(dbuf, UNLIMITED)) + expires = TIME_T_MAX; + else + Curl_getdate_capped(dbuf, &expires); if(hp[0] == '.') { curlx_str_nudge(&host, 1); @@ -478,14 +480,14 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h) e.name[0] = 0; /* just to make it clean */ sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp); if(sc == CURLSTS_OK) { - time_t expires; + time_t expires = 0; CURLcode result; DEBUGASSERT(e.name[0]); if(!e.name[0]) /* bail out if no name was stored */ return CURLE_BAD_FUNCTION_ARGUMENT; if(e.expire[0]) - expires = Curl_getdate_capped(e.expire); + Curl_getdate_capped(e.expire, &expires); else expires = TIME_T_MAX; /* the end of time */ result = hsts_create(h, e.name, strlen(e.name), diff --git a/lib/http.c b/lib/http.c index 0ce3b25bc3..bcbe6e9e75 100644 --- a/lib/http.c +++ b/lib/http.c @@ -3196,7 +3196,8 @@ static CURLcode http_header_l(struct Curl_easy *data, (data->set.timecondition || data->set.get_filetime)) ? HD_VAL(hd, hdlen, "Last-Modified:") : NULL; if(v) { - k->timeofdoc = Curl_getdate_capped(v); + if(Curl_getdate_capped(v, &k->timeofdoc)) + k->timeofdoc = 0; if(data->set.get_filetime) data->info.filetime = k->timeofdoc; return CURLE_OK; @@ -3311,14 +3312,12 @@ static CURLcode http_header_r(struct Curl_easy *data, if(v) { /* Retry-After = HTTP-date / delay-seconds */ curl_off_t retry_after = 0; /* zero for unknown or "now" */ - time_t date; + time_t date = 0; curlx_str_passblanks(&v); /* try it as a date first, because a date can otherwise start with and get treated as a number */ - date = Curl_getdate_capped(v); - - if((time_t)-1 != date) { + if(!Curl_getdate_capped(v, &date)) { time_t current = time(NULL); if(date >= current) /* convert date to number of seconds into the future */ diff --git a/lib/parsedate.c b/lib/parsedate.c index eedab7c411..b9429db7f6 100644 --- a/lib/parsedate.c +++ b/lib/parsedate.c @@ -589,26 +589,12 @@ time_t curl_getdate(const char *p, const time_t *now) /* Curl_getdate_capped() differs from curl_getdate() in that this will return TIME_T_MAX in case the parsed time value was too big, instead of an - error. */ + error. Returns non-zero on error. */ -time_t Curl_getdate_capped(const char *p) +int Curl_getdate_capped(const char *p, time_t *tp) { - time_t parsed = -1; - int rc = parsedate(p, &parsed); - - switch(rc) { - case PARSEDATE_OK: - if(parsed == (time_t)-1) - /* avoid returning -1 for a working scenario */ - parsed++; - return parsed; - case PARSEDATE_LATER: - /* this returns the maximum time value */ - return parsed; - default: - return -1; /* everything else is fail */ - } - /* UNREACHABLE */ + int rc = parsedate(p, tp); + return (rc == PARSEDATE_FAIL); } /* diff --git a/lib/parsedate.h b/lib/parsedate.h index 84c37f1675..e5efb53f61 100644 --- a/lib/parsedate.h +++ b/lib/parsedate.h @@ -29,10 +29,10 @@ extern const char * const Curl_month[12]; CURLcode Curl_gmtime(time_t intime, struct tm *store); -/* Curl_getdate_capped() differs from curl_getdate() in that this will return +/* Curl_getdate_capped() differs from curl_getdate() in that this returns TIME_T_MAX in case the parsed time value was too big, instead of an error. */ -time_t Curl_getdate_capped(const char *p); +int Curl_getdate_capped(const char *p, time_t *store); #endif /* HEADER_CURL_PARSEDATE_H */ diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 8c366aafae..2898a70f0a 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -1855,9 +1855,9 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, } else if(!strncmp(cmd, "atime", 5) || !strncmp(cmd, "mtime", 5)) { - time_t date = Curl_getdate_capped(sshc->quote_path1); + time_t date; bool fail = FALSE; - if(date == -1) { + if(Curl_getdate_capped(sshc->quote_path1, &date)) { failf(data, "incorrect date format for %.*s", 5, cmd); fail = TRUE; } diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index ea76482394..13f374564f 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -1390,16 +1390,16 @@ sftp_quote_stat(struct Curl_easy *data, } else if(!strncmp(cmd, "atime", 5) || !strncmp(cmd, "mtime", 5)) { - time_t date = Curl_getdate_capped(sshc->quote_path1); + time_t date; bool fail = FALSE; - if(date == -1) { + if(Curl_getdate_capped(sshc->quote_path1, &date)) { failf(data, "incorrect date format for %.*s", 5, cmd); fail = TRUE; } #if SIZEOF_TIME_T > SIZEOF_LONG if(date > 0xffffffff) { - /* if 'long' cannot old >32-bit, this date cannot be sent */ + /* if 'long' cannot hold >32-bit, this date cannot be sent */ failf(data, "date overflow"); fail = TRUE; }