This callback function gets called by libcurl repeatedly when it populates the
in-memory HSTS cache.
-Set the *clientp* argument with the CURLOPT_HSTSREADDATA(3) option
-or it is NULL.
-
-When this callback is invoked, the *sts* pointer points to a populated
-struct: Copy the hostname to *name* (no longer than *namelen*
-bytes). Make it null-terminated. Set *includeSubDomains* to TRUE or
-FALSE. Set *expire* to a date stamp or a zero length string for *forever*
-(wrong date stamp format might cause the name to not get accepted)
-
-The callback should return *CURLSTS_OK* if it returns a name and is
-prepared to be called again (for another host) or *CURLSTS_DONE* if it has
-no entry to return. It can also return *CURLSTS_FAIL* to signal
-error. Returning *CURLSTS_FAIL* stops the transfer from being performed
-and make *CURLE_ABORTED_BY_CALLBACK* get returned.
-
-This option does not enable HSTS, you need to use CURLOPT_HSTS_CTRL(3) to
-do that.
+Set the *clientp* argument with the CURLOPT_HSTSREADDATA(3) option or it is
+NULL.
+
+When this callback is invoked, the *sts* pointer points to a populated struct:
+Copy the hostname to *name* (no longer than *namelen* bytes). Make it
+null-terminated. Set *includeSubDomains* to TRUE or FALSE. Set *expire* to a
+date stamp or a zero length string for *forever* (wrong date stamp format
+might cause the name to not get accepted)
+
+The callback should return *CURLSTS_OK* if it returns a name and is prepared
+to be called again (for another host) or *CURLSTS_DONE* if it has no entry to
+return. It can also return *CURLSTS_FAIL* to signal error. Returning
+*CURLSTS_FAIL* stops the transfer from being performed and make
+*CURLE_ABORTED_BY_CALLBACK* get returned.
+
+This option does not enable HSTS, you need to use CURLOPT_HSTS_CTRL(3) to do
+that.
+
+The hostname provided to libcurl *should not* have a trailing dot nor leading
+dot.
# DEFAULT
#define MAX_HSTS_LINE 4095
#define MAX_HSTS_HOSTLEN 2048
-#define MAX_HSTS_DATELEN 256
+#define MAX_HSTS_DATELEN 17
#define UNLIMITED "unlimited"
#if defined(DEBUGBUILD) || defined(UNITTESTS)
return result;
}
+/* only returns SERIOUS errors */
+static CURLcode hsts_add_host_expire(struct hsts *h,
+ const char *host, size_t hostlen,
+ const char *expire, size_t explen,
+ bool subdomain) /* default */
+{
+ CURLcode result = CURLE_OK;
+ struct stsentry *e;
+ char dbuf[MAX_HSTS_DATELEN + 1];
+ time_t expires = 0;
+ time_t now = time(NULL);
+
+ /* The date parser works on a null-terminated string. */
+ if(explen > MAX_HSTS_DATELEN)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ memcpy(dbuf, expire, explen);
+ dbuf[explen] = 0;
+
+ if(!strcmp(dbuf, UNLIMITED))
+ expires = TIME_T_MAX;
+ else
+ Curl_getdate_capped(dbuf, &expires);
+
+ if(expires <= now)
+ /* this entry already expired */
+ return CURLE_OK;
+
+ if(host[0] == '.') {
+ host++;
+ hostlen--;
+ subdomain = TRUE;
+ }
+ if(hostlen && (host[hostlen - 1] == '.'))
+ /* strip off any trailing dot */
+ hostlen--;
+
+ if(hostlen) {
+ /* only add it if not already present */
+ e = Curl_hsts(h, host, hostlen, subdomain);
+ if(!e)
+ result = hsts_create(h, host, hostlen, subdomain, expires);
+ /* 'host' is not necessarily null terminated */
+ else if((hostlen == strlen(e->host) &&
+ curl_strnequal(host, e->host, hostlen))) {
+ /* the same hostname, use the largest expire time and keep the strictest
+ subdomain policy */
+ if(expires > e->expires)
+ e->expires = expires;
+ if(subdomain)
+ e->includeSubDomains = TRUE;
+ }
+ }
+ return result;
+}
+
/* only returns SERIOUS errors */
static CURLcode hsts_add(struct hsts *h, const char *line)
{
curlx_str_newline(&line))
;
else {
- CURLcode result = CURLE_OK;
- bool subdomain = FALSE;
- struct stsentry *e;
- char dbuf[MAX_HSTS_DATELEN + 1];
- time_t expires = 0;
- const char *hp = curlx_str(&host);
- size_t hlen;
- time_t now = time(NULL);
-
- /* 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;
-
- if(!strcmp(dbuf, UNLIMITED))
- expires = TIME_T_MAX;
- else
- Curl_getdate_capped(dbuf, &expires);
-
- if(expires <= now)
- /* this entry already expired */
- return CURLE_OK;
-
- if(hp[0] == '.') {
- curlx_str_nudge(&host, 1);
- hp = curlx_str(&host);
- subdomain = TRUE;
- }
- hlen = curlx_strlen(&host);
- if(hlen && (hp[hlen - 1] == '.'))
- /* strip off any trailing dot */
- curlx_str_trim(&host, 1);
-
- /* only add it if not already present */
- e = Curl_hsts(h, curlx_str(&host), curlx_strlen(&host), subdomain);
- if(!e)
- result = hsts_create(h, curlx_str(&host), curlx_strlen(&host),
- subdomain, expires);
- else if(curlx_str_casecompare(&host, e->host)) {
- /* the same hostname, use the largest expire time and keep the
- strictest subdomain policy */
- if(expires > e->expires)
- e->expires = expires;
- if(subdomain)
- e->includeSubDomains = TRUE;
- }
- if(result)
- return result;
+ return hsts_add_host_expire(h, curlx_str(&host), curlx_strlen(&host),
+ curlx_str(&date), curlx_strlen(&date),
+ FALSE);
}
return CURLE_OK;
e.namelen = sizeof(buffer) - 1;
e.includeSubDomains = FALSE; /* default */
e.expire[0] = 0;
+ e.expire[MAX_HSTS_DATELEN] = 0;
e.name[0] = 0; /* to make it clean */
+ e.name[MAX_HSTS_HOSTLEN] = 0;
sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp);
if(sc == CURLSTS_OK) {
- time_t expires = 0;
CURLcode result;
- DEBUGASSERT(e.name[0]);
- if(!e.name[0])
- /* bail out if no name was stored */
+ const char *date = e.expire;
+ if(!e.name[0] || e.expire[MAX_HSTS_DATELEN] ||
+ e.name[MAX_HSTS_HOSTLEN])
+ /* bail out if no name was stored or if a null terminator is gone */
return CURLE_BAD_FUNCTION_ARGUMENT;
- if(e.expire[0])
- Curl_getdate_capped(e.expire, &expires);
- else
- expires = TIME_T_MAX; /* the end of time */
- result = hsts_create(h, e.name, strlen(e.name),
- /* bitfield to bool conversion: */
- e.includeSubDomains ? TRUE : FALSE,
- expires);
+ if(!date[0])
+ date = UNLIMITED;
+ result = hsts_add_host_expire(h, e.name, strlen(e.name),
+ date, strlen(date),
+ /* bitfield to bool conversion: */
+ e.includeSubDomains ? TRUE : FALSE);
if(result)
return result;
}
static const struct entry preload_hosts[] = {
#if (SIZEOF_TIME_T < 5)
{ "1.example.com", "20370320 01:02:03" },
- { "2.example.com", "20370320 03:02:01" },
+ { "2.example.com.", "20370320 03:02:01" },
{ "3.example.com", "20370319 01:02:03" },
+ { ".3.example.com", "20270319 01:02:03" },
#else
{ "1.example.com", "25250320 01:02:03" },
- { "2.example.com", "25250320 03:02:01" },
+ { "2.example.com.", "25250320 03:02:01" },
{ "3.example.com", "25250319 01:02:03" },
+ { ".3.example.com", "22250319 01:02:03" },
#endif
- { "4.example.com", "" },
+ { "4.example.com", "" }, /* forever */
+ { ".4.example.com", "20370319 01:02:03" },
{ NULL, NULL } /* end of list marker */
};
{
(void)curl;
(void)userp;
- curl_mprintf("[%zu/%zu] %s %s\n", i->index, i->total, e->name, e->expire);
+ curl_mprintf("[%zu/%zu] %s%s %s\n", i->index, i->total,
+ e->includeSubDomains ? "." : "", e->name, e->expire);
return CURLSTS_OK;
}