Some cleanups in src/url.c and added error message for the very unusual

case where curl may fail to create a handle.
This commit is contained in:
Bram Matthys 2021-08-18 15:19:51 +02:00
parent d8c3da7698
commit 0b5fab1617
No known key found for this signature in database
GPG key ID: BF8116B163EAAE98

123
src/url.c
View file

@ -133,7 +133,7 @@ char *url_getfilename(const char *url)
static void set_curl_tls_options(CURL *curl)
{
char buf[512];
#if 0
/* This would only be necessary if you use client certificates over HTTPS and such.
* But this information is not known yet since the configuration file has not been
@ -245,7 +245,7 @@ static int url_socket_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *so
{
FDEntry *fde = &fd_table[s];
int flags = 0;
if (!fde->is_open)
{
/* NOTE: We use FDCLOSE_NONE here because cURL will take
@ -314,62 +314,75 @@ void url_init(void)
void download_file_async(const char *url, time_t cachetime, vFP callback, void *callback_data)
{
static char errorbuf[CURL_ERROR_SIZE];
CURL *curl = curl_easy_init();
if (curl)
{
char *file = url_getfilename(url);
char *filename = unreal_getfilename(file);
char *tmp = unreal_mktemp(TMPDIR, filename ? filename : "download.conf");
FileHandle *handle = safe_alloc(sizeof(FileHandle));
handle->fd = fopen(tmp, "wb");
if (!handle->fd)
{
snprintf(errorbuf, sizeof(errorbuf), "Cannot create '%s': %s", tmp, strerror(ERRNO));
callback(url, NULL, errorbuf, 0, callback_data);
safe_free(file);
safe_free(handle);
return;
}
handle->callback = callback;
handle->callback_data = callback_data;
handle->cachetime = cachetime;
safe_strdup(handle->url, url);
strlcpy(handle->filename, tmp, sizeof(handle->filename));
safe_free(file);
CURL *curl;
char *file;
char *filename;
char *tmp;
FileHandle *handle;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, do_download);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)handle->fd);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
set_curl_tls_options(curl);
memset(handle->errorbuf, 0, CURL_ERROR_SIZE);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, handle->errorbuf);
curl_easy_setopt(curl, CURLOPT_PRIVATE, (char *)handle);
curl_easy_setopt(curl, CURLOPT_FILETIME, 1);
/* We need to set CURLOPT_FORBID_REUSE because otherwise libcurl does not
* notify us (or not in time) about FD close/opens, thus we end up closing and
* screwing up another innocent FD, like a listener (BAD!). In my view a bug, but
* mailing list archives seem to indicate curl devs have a different opinion
* on these matters...
* Actually I don't know for sure if this option alone fixes 100% of the cases
* but at least I can't crash my server anymore.
* As a side-effect we also fix useless CLOSE_WAIT connections.
*/
curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
if (cachetime)
{
curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
curl_easy_setopt(curl, CURLOPT_TIMEVALUE, cachetime);
}
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 45);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15);
curl = curl_easy_init();
if (!curl)
{
unreal_log(ULOG_ERROR, "main", "CURL_INTERNAL_FAILURE", NULL,
"Could not initialize curl handle. Maybe out of memory/resources?");
snprintf(errorbuf, sizeof(errorbuf), "Could not initialize curl handle");
return;
}
file = url_getfilename(url);
filename = unreal_getfilename(file);
tmp = unreal_mktemp(TMPDIR, filename ? filename : "download.conf");
handle = safe_alloc(sizeof(FileHandle));
handle->fd = fopen(tmp, "wb");
if (!handle->fd)
{
snprintf(errorbuf, sizeof(errorbuf), "Cannot create '%s': %s", tmp, strerror(ERRNO));
callback(url, NULL, errorbuf, 0, callback_data);
safe_free(file);
safe_free(handle);
return;
}
handle->callback = callback;
handle->callback_data = callback_data;
handle->cachetime = cachetime;
safe_strdup(handle->url, url);
strlcpy(handle->filename, tmp, sizeof(handle->filename));
safe_free(file);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, do_download);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)handle->fd);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
set_curl_tls_options(curl);
memset(handle->errorbuf, 0, CURL_ERROR_SIZE);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, handle->errorbuf);
curl_easy_setopt(curl, CURLOPT_PRIVATE, (char *)handle);
curl_easy_setopt(curl, CURLOPT_FILETIME, 1);
/* We need to set CURLOPT_FORBID_REUSE because otherwise libcurl does not
* notify us (or not in time) about FD close/opens, thus we end up closing and
* screwing up another innocent FD, like a listener (BAD!). In my view a bug, but
* mailing list archives seem to indicate curl devs have a different opinion
* on these matters...
* Actually I don't know for sure if this option alone fixes 100% of the cases
* but at least I can't crash my server anymore.
* As a side-effect we also fix useless CLOSE_WAIT connections.
*/
curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
if (cachetime)
{
curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
curl_easy_setopt(curl, CURLOPT_TIMEVALUE, cachetime);
}
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 45);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15);
#if LIBCURL_VERSION_NUM >= 0x070f01
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1);
#endif
curl_multi_add_handle(multihandle, curl);
}
curl_multi_add_handle(multihandle, curl);
}
#endif