mirror of
https://github.com/curl/curl.git
synced 2026-08-25 22:03:47 +03:00
hsts: add read/write callbacks
- read/write callback options - man pages for the 4 new setopts - test 1915 verifies the callbacks Closes #5896
This commit is contained in:
parent
7385610d0c
commit
2cfc4ed983
27 changed files with 652 additions and 22 deletions
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC)) || \
|
||||
#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \
|
||||
defined(USE_HSTS)
|
||||
|
||||
#include "curl_get_line.h"
|
||||
|
|
|
|||
|
|
@ -888,7 +888,9 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
|
|||
if(!outcurl->hsts)
|
||||
goto fail;
|
||||
if(outcurl->set.str[STRING_HSTS])
|
||||
(void)Curl_hsts_load(outcurl->hsts, outcurl->set.str[STRING_HSTS]);
|
||||
(void)Curl_hsts_loadfile(outcurl,
|
||||
outcurl->hsts, outcurl->set.str[STRING_HSTS]);
|
||||
(void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
|
||||
}
|
||||
#endif
|
||||
/* Clone the resolver handle, if present, for the new handle */
|
||||
|
|
|
|||
|
|
@ -116,6 +116,10 @@ struct curl_easyoption Curl_easyopts[] = {
|
|||
{"HEADERFUNCTION", CURLOPT_HEADERFUNCTION, CURLOT_FUNCTION, 0},
|
||||
{"HEADEROPT", CURLOPT_HEADEROPT, CURLOT_VALUES, 0},
|
||||
{"HSTS", CURLOPT_HSTS, CURLOT_STRING, 0},
|
||||
{"HSTSREADDATA", CURLOPT_HSTSREADDATA, CURLOT_CBPTR, 0},
|
||||
{"HSTSREADFUNCTION", CURLOPT_HSTSREADFUNCTION, CURLOT_FUNCTION, 0},
|
||||
{"HSTSWRITEDATA", CURLOPT_HSTSWRITEDATA, CURLOT_CBPTR, 0},
|
||||
{"HSTSWRITEFUNCTION", CURLOPT_HSTSWRITEFUNCTION, CURLOT_FUNCTION, 0},
|
||||
{"HSTS_CTRL", CURLOPT_HSTS_CTRL, CURLOT_LONG, 0},
|
||||
{"HTTP09_ALLOWED", CURLOPT_HTTP09_ALLOWED, CURLOT_LONG, 0},
|
||||
{"HTTP200ALIASES", CURLOPT_HTTP200ALIASES, CURLOT_SLIST, 0},
|
||||
|
|
@ -344,6 +348,6 @@ struct curl_easyoption Curl_easyopts[] = {
|
|||
*/
|
||||
int Curl_easyopts_check(void)
|
||||
{
|
||||
return (CURLOPT_LASTENTRY != (300 + 1));
|
||||
return (CURLOPT_LASTENTRY != (304 + 1));
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
109
lib/hsts.c
109
lib/hsts.c
|
|
@ -261,6 +261,37 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
|
|||
return NULL; /* no match */
|
||||
}
|
||||
|
||||
/*
|
||||
* Send this HSTS entry to the write callback.
|
||||
*/
|
||||
static CURLcode hsts_push(struct Curl_easy *data,
|
||||
struct curl_index *i,
|
||||
struct stsentry *sts,
|
||||
bool *stop)
|
||||
{
|
||||
struct curl_hstsentry e;
|
||||
CURLSTScode sc;
|
||||
struct tm stamp;
|
||||
CURLcode result;
|
||||
|
||||
e.name = (char *)sts->host;
|
||||
e.namelen = strlen(sts->host);
|
||||
e.includeSubDomains = sts->includeSubDomains;
|
||||
|
||||
result = Curl_gmtime(sts->expires, &stamp);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d",
|
||||
stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
|
||||
stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
|
||||
|
||||
sc = data->set.hsts_write(data, &e, i,
|
||||
data->set.hsts_write_userp);
|
||||
*stop = (sc != CURLSTS_OK);
|
||||
return sc == CURLSTS_FAIL ? CURLE_BAD_FUNCTION_ARGUMENT : CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write this single hsts entry to a single output line
|
||||
*/
|
||||
|
|
@ -280,7 +311,7 @@ static CURLcode hsts_out(struct stsentry *sts, FILE *fp)
|
|||
|
||||
|
||||
/*
|
||||
* Curl_https_save() writes the HSTS cache to a file.
|
||||
* Curl_https_save() writes the HSTS cache to file and callback.
|
||||
*/
|
||||
CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
|
||||
const char *file)
|
||||
|
|
@ -302,7 +333,7 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
|
|||
|
||||
if((h->flags & CURLHSTS_READONLYFILE) || !file || !file[0])
|
||||
/* marked as read-only, no file or zero length file name */
|
||||
return CURLE_OK;
|
||||
goto skipsave;
|
||||
|
||||
if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix)))
|
||||
return CURLE_FAILED_INIT;
|
||||
|
|
@ -333,6 +364,22 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
|
|||
unlink(tempstore);
|
||||
}
|
||||
free(tempstore);
|
||||
skipsave:
|
||||
if(data->set.hsts_write) {
|
||||
/* if there's a write callback */
|
||||
struct curl_index i; /* count */
|
||||
i.total = h->list.size;
|
||||
i.index = 0;
|
||||
for(e = h->list.head; e; e = n) {
|
||||
struct stsentry *sts = e->ptr;
|
||||
bool stop;
|
||||
n = e->next;
|
||||
result = hsts_push(data, &i, sts, &stop);
|
||||
if(result || stop)
|
||||
break;
|
||||
i.index++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -367,6 +414,46 @@ static CURLcode hsts_add(struct hsts *h, char *line)
|
|||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load HSTS data from callback.
|
||||
*
|
||||
*/
|
||||
static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
|
||||
{
|
||||
/* if the HSTS read callback is set, use it */
|
||||
if(data->set.hsts_read) {
|
||||
CURLSTScode sc;
|
||||
DEBUGASSERT(h);
|
||||
do {
|
||||
char buffer[257];
|
||||
struct curl_hstsentry e;
|
||||
e.name = buffer;
|
||||
e.namelen = sizeof(buffer)-1;
|
||||
e.includeSubDomains = FALSE; /* default */
|
||||
e.expire[0] = 0;
|
||||
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;
|
||||
CURLcode result;
|
||||
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);
|
||||
else
|
||||
expires = TIME_T_MAX; /* the end of time */
|
||||
result = hsts_create(h, e.name, e.includeSubDomains, expires);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
else if(sc == CURLSTS_FAIL)
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
} while(sc == CURLSTS_OK);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load the HSTS cache from the given file. The text based line-oriented file
|
||||
* format is documented here:
|
||||
|
|
@ -417,14 +504,22 @@ static CURLcode hsts_load(struct hsts *h, const char *file)
|
|||
}
|
||||
|
||||
/*
|
||||
* Curl_hsts_load() loads HSTS from file.
|
||||
* Curl_hsts_loadfile() loads HSTS from file
|
||||
*/
|
||||
CURLcode Curl_hsts_load(struct hsts *h, const char *file)
|
||||
CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
|
||||
struct hsts *h, const char *file)
|
||||
{
|
||||
CURLcode result;
|
||||
DEBUGASSERT(h);
|
||||
result = hsts_load(h, file);
|
||||
return result;
|
||||
(void)data;
|
||||
return hsts_load(h, file);
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_hsts_loadcb() loads HSTS from callback
|
||||
*/
|
||||
CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h)
|
||||
{
|
||||
return hsts_pull(data, h);
|
||||
}
|
||||
|
||||
#endif /* CURL_DISABLE_HTTP || USE_HSTS */
|
||||
|
|
|
|||
|
|
@ -53,8 +53,13 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
|
|||
bool subdomain);
|
||||
CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
|
||||
const char *file);
|
||||
CURLcode Curl_hsts_load(struct hsts *h, const char *file);
|
||||
CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
|
||||
struct hsts *h, const char *file);
|
||||
CURLcode Curl_hsts_loadcb(struct Curl_easy *data,
|
||||
struct hsts *h);
|
||||
#else
|
||||
#define Curl_hsts_cleanup(x)
|
||||
#define Curl_hsts_loadcb(x,y)
|
||||
#define Curl_hsts_save(x,y,z)
|
||||
#endif /* CURL_DISABLE_HTTP || USE_HSTS */
|
||||
#endif /* HEADER_CURL_HSTS_H */
|
||||
|
|
|
|||
14
lib/setopt.c
14
lib/setopt.c
|
|
@ -2841,6 +2841,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
|
|||
#endif
|
||||
break;
|
||||
#ifdef USE_HSTS
|
||||
case CURLOPT_HSTSREADFUNCTION:
|
||||
data->set.hsts_read = va_arg(param, curl_hstsread_callback);
|
||||
break;
|
||||
case CURLOPT_HSTSREADDATA:
|
||||
data->set.hsts_read_userp = va_arg(param, void *);
|
||||
break;
|
||||
case CURLOPT_HSTSWRITEFUNCTION:
|
||||
data->set.hsts_write = va_arg(param, curl_hstswrite_callback);
|
||||
break;
|
||||
case CURLOPT_HSTSWRITEDATA:
|
||||
data->set.hsts_write_userp = va_arg(param, void *);
|
||||
break;
|
||||
case CURLOPT_HSTS:
|
||||
if(!data->hsts) {
|
||||
data->hsts = Curl_hsts_init();
|
||||
|
|
@ -2852,7 +2864,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
|
|||
if(result)
|
||||
return result;
|
||||
if(argptr)
|
||||
(void)Curl_hsts_load(data->hsts, argptr);
|
||||
(void)Curl_hsts_loadfile(data, data->hsts, argptr);
|
||||
break;
|
||||
case CURLOPT_HSTS_CTRL:
|
||||
arg = va_arg(param, long);
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@
|
|||
#include "mime.h"
|
||||
#include "strcase.h"
|
||||
#include "urlapi-int.h"
|
||||
#include "hsts.h"
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
|
|
@ -1528,6 +1529,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
|
|||
}
|
||||
#endif
|
||||
Curl_http2_init_state(&data->state);
|
||||
Curl_hsts_loadcb(data, data->hsts);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -412,6 +412,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
|
|||
Curl_flush_cookies(data, TRUE);
|
||||
Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
|
||||
Curl_altsvc_cleanup(&data->asi);
|
||||
Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]);
|
||||
Curl_hsts_cleanup(&data->hsts);
|
||||
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
|
||||
Curl_http_auth_cleanup_digest(data);
|
||||
|
|
|
|||
|
|
@ -1639,7 +1639,12 @@ struct UserDefined {
|
|||
curl_conv_callback convtonetwork;
|
||||
/* function to convert from UTF-8 encoding: */
|
||||
curl_conv_callback convfromutf8;
|
||||
|
||||
#ifdef USE_HSTS
|
||||
curl_hstsread_callback hsts_read;
|
||||
void *hsts_read_userp;
|
||||
curl_hstswrite_callback hsts_write;
|
||||
void *hsts_write_userp;
|
||||
#endif
|
||||
void *progress_client; /* pointer to pass to the progress callback */
|
||||
void *ioctl_client; /* pointer to pass to the ioctl callback */
|
||||
long timeout; /* in milliseconds, 0 means no timeout */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue