2019-04-24 00:10:47 +02:00
|
|
|
/*
|
2021-01-11 23:46:08 +01:00
|
|
|
* Copyright (c) 2019-2021 Joris Vink <joris@coders.se>
|
2019-04-24 00:10:47 +02:00
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
2019-04-24 00:16:51 +02:00
|
|
|
#include <inttypes.h>
|
|
|
|
|
2019-04-24 00:10:47 +02:00
|
|
|
#include "kore.h"
|
|
|
|
#include "http.h"
|
|
|
|
#include "curl.h"
|
|
|
|
|
2019-09-25 14:25:49 +02:00
|
|
|
#if defined(__linux__)
|
|
|
|
#include "seccomp.h"
|
|
|
|
|
|
|
|
static struct sock_filter filter_curl[] = {
|
|
|
|
/* Allow sockets and libcurl to call connect. */
|
|
|
|
KORE_SYSCALL_ALLOW(bind),
|
2019-09-27 12:22:35 +02:00
|
|
|
KORE_SYSCALL_ALLOW(ioctl),
|
2019-09-25 14:25:49 +02:00
|
|
|
KORE_SYSCALL_ALLOW(connect),
|
2019-09-27 12:22:35 +02:00
|
|
|
KORE_SYSCALL_ALLOW(getsockopt),
|
|
|
|
KORE_SYSCALL_ALLOW(getsockname),
|
seccomp improvements.
More BPF helper macros, more helper for granular syscall checking.
Use these throughout kore where it makes sense.
The new helpers:
- KORE_SYSCALL_DENY_ARG(name, arg, value, errno):
Deny the system call with errno if the argument matches value.
- KORE_SYSCALL_DENY_MASK(name, arg, mask, errno):
Deny the system call with errno if the mask argument does not match
the exact mask given.
- KORE_SYSCALL_DENY_WITH_FLAG(name, arg, flag, errno):
Deny the system call with errno if the argument contains the
given flag.
The reverse also exists:
- KORE_SYSCALL_ALLOW_ARG()
- KORE_SYSCALL_ALLOW_MASK()
- KORE_SYSCALL_ALLOW_WITH_FLAG()
2019-09-26 13:51:53 +02:00
|
|
|
KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_INET),
|
|
|
|
KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_INET6),
|
|
|
|
KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_UNIX),
|
2019-09-27 12:22:35 +02:00
|
|
|
KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_NETLINK),
|
2019-09-25 14:25:49 +02:00
|
|
|
|
|
|
|
/* Threading related. */
|
|
|
|
KORE_SYSCALL_ALLOW(clone),
|
|
|
|
KORE_SYSCALL_ALLOW(set_robust_list),
|
|
|
|
|
|
|
|
/* Other */
|
2019-11-06 19:33:53 +01:00
|
|
|
KORE_SYSCALL_ALLOW(uname),
|
2019-09-25 14:25:49 +02:00
|
|
|
KORE_SYSCALL_ALLOW(ioctl),
|
|
|
|
KORE_SYSCALL_ALLOW(madvise),
|
|
|
|
KORE_SYSCALL_ALLOW(recvmsg),
|
|
|
|
KORE_SYSCALL_ALLOW(sendmmsg),
|
2019-11-06 19:33:53 +01:00
|
|
|
KORE_SYSCALL_ALLOW(faccessat),
|
|
|
|
KORE_SYSCALL_ALLOW(newfstatat),
|
2019-09-25 14:25:49 +02:00
|
|
|
KORE_SYSCALL_ALLOW(getpeername),
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2019-04-24 00:10:47 +02:00
|
|
|
#define FD_CACHE_BUCKETS 2048
|
|
|
|
|
|
|
|
struct fd_cache {
|
|
|
|
struct kore_event evt;
|
|
|
|
int fd;
|
|
|
|
int scheduled;
|
|
|
|
LIST_ENTRY(fd_cache) list;
|
|
|
|
};
|
|
|
|
|
2020-08-17 15:15:04 +02:00
|
|
|
struct curl_run {
|
|
|
|
int eof;
|
|
|
|
struct fd_cache *fdc;
|
|
|
|
TAILQ_ENTRY(curl_run) list;
|
|
|
|
};
|
|
|
|
|
2019-04-24 00:10:47 +02:00
|
|
|
static void curl_process(void);
|
|
|
|
static void curl_event_handle(void *, int);
|
2019-06-13 12:59:17 +02:00
|
|
|
static void curl_timeout(void *, u_int64_t);
|
2019-04-24 00:10:47 +02:00
|
|
|
static int curl_timer(CURLM *, long, void *);
|
2020-08-17 15:15:04 +02:00
|
|
|
static void curl_run_handle(struct curl_run *);
|
|
|
|
static void curl_run_schedule(struct fd_cache *, int);
|
2019-04-24 00:10:47 +02:00
|
|
|
static int curl_socket(CURL *, curl_socket_t, int, void *, void *);
|
|
|
|
|
|
|
|
static struct fd_cache *fd_cache_get(int);
|
|
|
|
|
2020-08-17 15:15:04 +02:00
|
|
|
static TAILQ_HEAD(, curl_run) runlist;
|
|
|
|
static struct kore_pool run_pool;
|
2019-04-24 00:10:47 +02:00
|
|
|
static int running = 0;
|
|
|
|
static CURLM *multi = NULL;
|
|
|
|
static struct kore_timer *timer = NULL;
|
|
|
|
static struct kore_pool fd_cache_pool;
|
2019-04-30 20:39:46 +02:00
|
|
|
static char user_agent[64];
|
2019-06-13 12:59:17 +02:00
|
|
|
static int timeout_immediate = 0;
|
2019-04-24 00:10:47 +02:00
|
|
|
static LIST_HEAD(, fd_cache) cache[FD_CACHE_BUCKETS];
|
|
|
|
|
|
|
|
u_int16_t kore_curl_timeout = KORE_CURL_TIMEOUT;
|
|
|
|
u_int64_t kore_curl_recv_max = KORE_CURL_RECV_MAX;
|
|
|
|
|
|
|
|
void
|
|
|
|
kore_curl_sysinit(void)
|
|
|
|
{
|
|
|
|
CURLMcode res;
|
2019-04-30 20:39:46 +02:00
|
|
|
int i, len;
|
2019-04-24 00:10:47 +02:00
|
|
|
|
|
|
|
if (curl_global_init(CURL_GLOBAL_ALL))
|
|
|
|
fatal("failed to initialize libcurl");
|
|
|
|
|
|
|
|
if ((multi = curl_multi_init()) == NULL)
|
|
|
|
fatal("curl_multi_init(): failed");
|
|
|
|
|
|
|
|
/* XXX - make configurable? */
|
|
|
|
curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, 500);
|
|
|
|
|
|
|
|
if ((res = curl_multi_setopt(multi,
|
|
|
|
CURLMOPT_SOCKETFUNCTION, curl_socket)) != CURLM_OK)
|
|
|
|
fatal("curl_multi_setopt: %s", curl_multi_strerror(res));
|
|
|
|
|
|
|
|
if ((res = curl_multi_setopt(multi,
|
|
|
|
CURLMOPT_TIMERFUNCTION, curl_timer)) != CURLM_OK)
|
|
|
|
fatal("curl_multi_setopt: %s", curl_multi_strerror(res));
|
|
|
|
|
|
|
|
for (i = 0; i < FD_CACHE_BUCKETS; i++)
|
|
|
|
LIST_INIT(&cache[i]);
|
|
|
|
|
2020-08-17 15:15:04 +02:00
|
|
|
TAILQ_INIT(&runlist);
|
|
|
|
|
2019-04-24 00:10:47 +02:00
|
|
|
kore_pool_init(&fd_cache_pool, "fd_cache_pool", 100,
|
|
|
|
sizeof(struct fd_cache));
|
2020-08-17 15:15:04 +02:00
|
|
|
kore_pool_init(&run_pool, "run_pool", 100, sizeof(struct curl_run));
|
2019-04-30 20:39:46 +02:00
|
|
|
|
2019-04-30 20:45:56 +02:00
|
|
|
len = snprintf(user_agent, sizeof(user_agent), "kore/%s", kore_version);
|
2019-04-30 20:39:46 +02:00
|
|
|
if (len == -1 || (size_t)len >= sizeof(user_agent))
|
|
|
|
fatal("user-agent string too long");
|
2019-09-25 14:25:49 +02:00
|
|
|
|
|
|
|
#if defined(__linux__)
|
|
|
|
kore_seccomp_filter("curl", filter_curl, KORE_FILTER_LEN(filter_curl));
|
|
|
|
#endif
|
2019-11-03 11:47:49 +01:00
|
|
|
#if defined(KORE_USE_PLATFORM_PLEDGE)
|
|
|
|
kore_platform_add_pledge("dns");
|
|
|
|
#endif
|
2019-04-24 00:10:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2019-10-21 13:26:44 +02:00
|
|
|
kore_curl_init(struct kore_curl *client, const char *url, int flags)
|
2019-04-24 00:10:47 +02:00
|
|
|
{
|
|
|
|
CURL *handle;
|
|
|
|
|
2019-10-21 13:26:44 +02:00
|
|
|
if ((flags & KORE_CURL_ASYNC) && (flags & KORE_CURL_SYNC)) {
|
|
|
|
(void)kore_strlcpy(client->errbuf, "invalid flags",
|
|
|
|
sizeof(client->errbuf));
|
|
|
|
return (KORE_RESULT_ERROR);
|
|
|
|
}
|
|
|
|
|
2019-04-24 00:10:47 +02:00
|
|
|
memset(client, 0, sizeof(*client));
|
|
|
|
|
|
|
|
TAILQ_INIT(&client->http.resp_hdrs);
|
|
|
|
|
2019-10-21 13:26:44 +02:00
|
|
|
if ((handle = curl_easy_init()) == NULL) {
|
|
|
|
(void)kore_strlcpy(client->errbuf, "failed to setup curl",
|
|
|
|
sizeof(client->errbuf));
|
2019-04-24 00:10:47 +02:00
|
|
|
return (KORE_RESULT_ERROR);
|
2019-10-21 13:26:44 +02:00
|
|
|
}
|
2019-04-24 00:10:47 +02:00
|
|
|
|
|
|
|
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &client->response);
|
|
|
|
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, kore_curl_tobuf);
|
|
|
|
|
|
|
|
curl_easy_setopt(handle, CURLOPT_URL, url);
|
|
|
|
curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1);
|
|
|
|
curl_easy_setopt(handle, CURLOPT_PRIVATE, client);
|
2019-04-30 20:39:46 +02:00
|
|
|
curl_easy_setopt(handle, CURLOPT_USERAGENT, user_agent);
|
2019-04-24 00:10:47 +02:00
|
|
|
curl_easy_setopt(handle, CURLOPT_TIMEOUT, kore_curl_timeout);
|
|
|
|
curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, client->errbuf);
|
|
|
|
|
2019-10-21 13:26:44 +02:00
|
|
|
client->flags = flags;
|
2019-04-24 00:10:47 +02:00
|
|
|
client->handle = handle;
|
|
|
|
client->url = kore_strdup(url);
|
|
|
|
client->type = KORE_CURL_TYPE_CUSTOM;
|
|
|
|
|
|
|
|
return (KORE_RESULT_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
kore_curl_cleanup(struct kore_curl *client)
|
|
|
|
{
|
|
|
|
struct http_header *hdr, *next;
|
|
|
|
|
|
|
|
kore_free(client->url);
|
|
|
|
|
|
|
|
if (client->flags & KORE_CURL_FLAG_BOUND)
|
|
|
|
LIST_REMOVE(client, list);
|
|
|
|
|
|
|
|
if (client->handle != NULL) {
|
|
|
|
curl_multi_remove_handle(multi, client->handle);
|
|
|
|
curl_easy_cleanup(client->handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (client->http.hdrlist != NULL)
|
|
|
|
curl_slist_free_all(client->http.hdrlist);
|
|
|
|
|
|
|
|
if (client->response != NULL)
|
|
|
|
kore_buf_free(client->response);
|
|
|
|
|
|
|
|
if (client->http.headers != NULL)
|
|
|
|
kore_buf_free(client->http.headers);
|
|
|
|
|
|
|
|
if (client->http.tosend != NULL)
|
|
|
|
kore_buf_free(client->http.tosend);
|
|
|
|
|
|
|
|
for (hdr = TAILQ_FIRST(&client->http.resp_hdrs);
|
|
|
|
hdr != NULL; hdr = next) {
|
|
|
|
next = TAILQ_NEXT(hdr, list);
|
|
|
|
TAILQ_REMOVE(&client->http.resp_hdrs, hdr, list);
|
|
|
|
kore_pool_put(&http_header_pool, hdr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-13 12:59:17 +02:00
|
|
|
void
|
|
|
|
kore_curl_do_timeout(void)
|
|
|
|
{
|
2019-10-23 12:38:31 +02:00
|
|
|
while (timeout_immediate) {
|
2019-06-13 12:59:17 +02:00
|
|
|
curl_timeout(NULL, kore_time_ms());
|
2019-10-23 12:38:31 +02:00
|
|
|
if (running == 0)
|
|
|
|
curl_timer(multi, -1, NULL);
|
|
|
|
}
|
2019-06-13 12:59:17 +02:00
|
|
|
}
|
|
|
|
|
2020-08-17 15:15:04 +02:00
|
|
|
void
|
|
|
|
kore_curl_run_scheduled(void)
|
|
|
|
{
|
|
|
|
struct curl_run *run;
|
|
|
|
|
|
|
|
while ((run = TAILQ_FIRST(&runlist))) {
|
|
|
|
TAILQ_REMOVE(&runlist, run, list);
|
|
|
|
curl_run_handle(run);
|
|
|
|
kore_pool_put(&run_pool, run);
|
|
|
|
}
|
|
|
|
|
|
|
|
curl_process();
|
|
|
|
}
|
|
|
|
|
2019-04-24 00:10:47 +02:00
|
|
|
size_t
|
|
|
|
kore_curl_tobuf(char *ptr, size_t size, size_t nmemb, void *udata)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
struct kore_buf **buf, *b;
|
|
|
|
|
|
|
|
if (SIZE_MAX / nmemb < size)
|
|
|
|
fatal("%s: %zu * %zu overflow", __func__, nmemb, size);
|
|
|
|
|
|
|
|
buf = udata;
|
|
|
|
len = size * nmemb;
|
|
|
|
|
|
|
|
if (*buf == NULL)
|
|
|
|
*buf = kore_buf_alloc(len);
|
|
|
|
|
|
|
|
b = *buf;
|
|
|
|
|
|
|
|
if (b->offset + len < b->offset)
|
|
|
|
fatal("%s: %zu+%zu overflows", __func__, b->offset, len);
|
|
|
|
|
|
|
|
if ((b->offset + len) > kore_curl_recv_max) {
|
|
|
|
kore_log(LOG_ERR,
|
|
|
|
"received too large transfer (%zu > %" PRIu64 ")",
|
|
|
|
b->offset + len, kore_curl_recv_max);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
kore_buf_append(b, ptr, len);
|
|
|
|
|
|
|
|
return (len);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
kore_curl_frombuf(char *ptr, size_t size, size_t nmemb, void *udata)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
struct kore_buf *buf;
|
|
|
|
|
|
|
|
if (SIZE_MAX / nmemb < size)
|
|
|
|
fatal("%s: %zu * %zu overflow", __func__, nmemb, size);
|
|
|
|
|
|
|
|
buf = udata;
|
|
|
|
len = size * nmemb;
|
|
|
|
|
|
|
|
if (buf->offset == buf->length)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
if (buf->offset + len < buf->offset)
|
|
|
|
fatal("%s: %zu+%zu overflows", __func__, buf->offset, len);
|
|
|
|
|
|
|
|
if ((buf->offset + len) < buf->length) {
|
|
|
|
memcpy(ptr, buf->data + buf->offset, len);
|
|
|
|
} else {
|
|
|
|
len = buf->length - buf->offset;
|
|
|
|
memcpy(ptr, buf->data + buf->offset, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->offset += len;
|
|
|
|
|
|
|
|
return (len);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
kore_curl_bind_request(struct kore_curl *client, struct http_request *req)
|
|
|
|
{
|
|
|
|
if (client->cb != NULL)
|
|
|
|
fatal("%s: already bound to callback", __func__);
|
|
|
|
|
|
|
|
client->req = req;
|
|
|
|
http_request_sleep(req);
|
|
|
|
|
|
|
|
client->flags |= KORE_CURL_FLAG_BOUND;
|
|
|
|
LIST_INSERT_HEAD(&req->chandles, client, list);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
kore_curl_bind_callback(struct kore_curl *client,
|
|
|
|
void (*cb)(struct kore_curl *, void *), void *arg)
|
|
|
|
{
|
|
|
|
if (client->req != NULL)
|
|
|
|
fatal("%s: already bound to request", __func__);
|
|
|
|
|
|
|
|
client->cb = cb;
|
|
|
|
client->arg = arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
kore_curl_run(struct kore_curl *client)
|
|
|
|
{
|
2019-10-21 13:26:44 +02:00
|
|
|
if (client->flags & KORE_CURL_ASYNC) {
|
|
|
|
curl_multi_add_handle(multi, client->handle);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
client->result = curl_easy_perform(client->handle);
|
|
|
|
|
|
|
|
curl_easy_getinfo(client->handle,
|
|
|
|
CURLINFO_RESPONSE_CODE, &client->http.status);
|
|
|
|
|
|
|
|
curl_easy_cleanup(client->handle);
|
|
|
|
client->handle = NULL;
|
2019-04-24 00:10:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
kore_curl_success(struct kore_curl *client)
|
|
|
|
{
|
|
|
|
return (client->result == CURLE_OK);
|
|
|
|
}
|
|
|
|
|
2019-05-30 14:25:04 +02:00
|
|
|
const char *
|
|
|
|
kore_curl_strerror(struct kore_curl *client)
|
|
|
|
{
|
|
|
|
const char *err;
|
|
|
|
|
|
|
|
if (client->errbuf[0] != '\0')
|
|
|
|
err = &client->errbuf[0];
|
|
|
|
else
|
|
|
|
err = curl_easy_strerror(client->result);
|
|
|
|
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
2019-04-24 00:10:47 +02:00
|
|
|
void
|
|
|
|
kore_curl_logerror(struct kore_curl *client)
|
|
|
|
{
|
2019-05-30 14:25:04 +02:00
|
|
|
kore_log(LOG_NOTICE, "curl error: %s -> %s", client->url,
|
|
|
|
kore_curl_strerror(client));
|
2019-04-24 00:10:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
kore_curl_response_as_bytes(struct kore_curl *client, const u_int8_t **body,
|
|
|
|
size_t *len)
|
|
|
|
{
|
|
|
|
if (client->response == NULL) {
|
|
|
|
*len = 0;
|
|
|
|
*body = NULL;
|
|
|
|
} else {
|
|
|
|
*len = client->response->offset;
|
|
|
|
*body = client->response->data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
kore_curl_response_as_string(struct kore_curl *client)
|
|
|
|
{
|
|
|
|
kore_buf_stringify(client->response, NULL);
|
|
|
|
|
|
|
|
return ((char *)client->response->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
kore_curl_http_setup(struct kore_curl *client, int method, const void *data,
|
|
|
|
size_t len)
|
|
|
|
{
|
|
|
|
const char *mname;
|
|
|
|
int has_body;
|
|
|
|
|
|
|
|
if (client->handle == NULL)
|
|
|
|
fatal("%s: called without setup", __func__);
|
|
|
|
|
|
|
|
mname = NULL;
|
|
|
|
has_body = 1;
|
|
|
|
|
|
|
|
client->type = KORE_CURL_TYPE_HTTP_CLIENT;
|
|
|
|
|
|
|
|
curl_easy_setopt(client->handle, CURLOPT_HEADERDATA,
|
|
|
|
&client->http.headers);
|
|
|
|
curl_easy_setopt(client->handle, CURLOPT_HEADERFUNCTION,
|
|
|
|
kore_curl_tobuf);
|
|
|
|
|
|
|
|
kore_curl_http_set_header(client, "expect", "");
|
|
|
|
|
|
|
|
switch (method) {
|
|
|
|
case HTTP_METHOD_GET:
|
|
|
|
break;
|
|
|
|
case HTTP_METHOD_HEAD:
|
|
|
|
curl_easy_setopt(client->handle, CURLOPT_NOBODY, 1);
|
|
|
|
break;
|
|
|
|
case HTTP_METHOD_DELETE:
|
|
|
|
case HTTP_METHOD_OPTIONS:
|
|
|
|
break;
|
|
|
|
case HTTP_METHOD_PUT:
|
|
|
|
has_body = 1;
|
|
|
|
curl_easy_setopt(client->handle, CURLOPT_UPLOAD, 1);
|
|
|
|
break;
|
|
|
|
case HTTP_METHOD_PATCH:
|
|
|
|
mname = http_method_text(method);
|
|
|
|
/* fallthrough */
|
|
|
|
case HTTP_METHOD_POST:
|
|
|
|
has_body = 1;
|
|
|
|
curl_easy_setopt(client->handle, CURLOPT_POST, 1);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fatal("%s: unknown method %d", __func__, method);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_body && data != NULL && len > 0) {
|
|
|
|
client->http.tosend = kore_buf_alloc(len);
|
|
|
|
kore_buf_append(client->http.tosend, data, len);
|
|
|
|
kore_buf_reset(client->http.tosend);
|
|
|
|
|
|
|
|
curl_easy_setopt(client->handle, CURLOPT_READDATA,
|
|
|
|
client->http.tosend);
|
|
|
|
curl_easy_setopt(client->handle, CURLOPT_READFUNCTION,
|
|
|
|
kore_curl_frombuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_body) {
|
|
|
|
if (method == HTTP_METHOD_PUT) {
|
|
|
|
curl_easy_setopt(client->handle,
|
2019-05-05 21:16:42 +02:00
|
|
|
CURLOPT_INFILESIZE_LARGE, (curl_off_t)len);
|
2019-04-24 00:10:47 +02:00
|
|
|
} else {
|
|
|
|
curl_easy_setopt(client->handle,
|
2019-05-05 21:16:42 +02:00
|
|
|
CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)len);
|
2019-04-24 00:10:47 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (data != NULL || len != 0) {
|
|
|
|
fatal("%s: %d should not have a body",
|
|
|
|
__func__, method);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mname != NULL)
|
|
|
|
curl_easy_setopt(client->handle, CURLOPT_CUSTOMREQUEST, mname);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
kore_curl_http_set_header(struct kore_curl *client, const char *header,
|
|
|
|
const char *value)
|
|
|
|
{
|
|
|
|
struct kore_buf buf;
|
|
|
|
const char *hdr;
|
|
|
|
|
|
|
|
kore_buf_init(&buf, 512);
|
|
|
|
|
|
|
|
if (value != NULL || *value != '\0') {
|
|
|
|
kore_buf_appendf(&buf, "%s: %s", header, value);
|
|
|
|
} else {
|
|
|
|
kore_buf_appendf(&buf, "%s:", header);
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = kore_buf_stringify(&buf, NULL);
|
|
|
|
|
|
|
|
client->http.hdrlist = curl_slist_append(client->http.hdrlist, hdr);
|
|
|
|
kore_buf_cleanup(&buf);
|
|
|
|
|
|
|
|
curl_easy_setopt(client->handle,
|
|
|
|
CURLOPT_HTTPHEADER, client->http.hdrlist);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
kore_curl_http_get_header(struct kore_curl *client, const char *header,
|
|
|
|
const char **out)
|
|
|
|
{
|
|
|
|
struct http_header *hdr;
|
|
|
|
|
|
|
|
if (!(client->flags & KORE_CURL_FLAG_HTTP_PARSED_HEADERS))
|
|
|
|
kore_curl_http_parse_headers(client);
|
|
|
|
|
|
|
|
TAILQ_FOREACH(hdr, &(client->http.resp_hdrs), list) {
|
|
|
|
if (!strcasecmp(hdr->header, header)) {
|
|
|
|
*out = hdr->value;
|
|
|
|
return (KORE_RESULT_OK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (KORE_RESULT_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
kore_curl_http_parse_headers(struct kore_curl *client)
|
|
|
|
{
|
|
|
|
struct http_header *hdr;
|
|
|
|
int i, cnt;
|
|
|
|
char *value, *hbuf, *headers[HTTP_REQ_HEADER_MAX];
|
|
|
|
|
|
|
|
if (client->flags & KORE_CURL_FLAG_HTTP_PARSED_HEADERS)
|
|
|
|
fatal("%s: headers already parsed", __func__);
|
|
|
|
|
|
|
|
client->flags |= KORE_CURL_FLAG_HTTP_PARSED_HEADERS;
|
|
|
|
|
|
|
|
if (client->http.headers == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hbuf = kore_buf_stringify(client->http.headers, NULL);
|
|
|
|
cnt = kore_split_string(hbuf, "\r\n", headers, HTTP_REQ_HEADER_MAX);
|
|
|
|
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
|
|
if ((value = http_validate_header(headers[i])) == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (*value == '\0')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
hdr = kore_pool_get(&http_header_pool);
|
|
|
|
hdr->header = headers[i];
|
|
|
|
hdr->value = value;
|
|
|
|
TAILQ_INSERT_TAIL(&(client->http.resp_hdrs), hdr, list);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
curl_socket(CURL *easy, curl_socket_t fd, int action, void *arg, void *sock)
|
|
|
|
{
|
|
|
|
CURLcode res;
|
|
|
|
struct fd_cache *fdc;
|
|
|
|
struct kore_curl *client;
|
|
|
|
|
|
|
|
client = NULL;
|
|
|
|
|
2019-05-05 21:16:42 +02:00
|
|
|
res = curl_easy_getinfo(easy, CURLINFO_PRIVATE, (char **)&client);
|
2019-04-24 00:10:47 +02:00
|
|
|
if (res != CURLE_OK)
|
|
|
|
fatal("curl_easy_getinfo: %s", curl_easy_strerror(res));
|
|
|
|
|
|
|
|
if (client == NULL)
|
|
|
|
fatal("%s: failed to get client context", __func__);
|
|
|
|
|
|
|
|
fdc = fd_cache_get(fd);
|
|
|
|
|
|
|
|
switch (action) {
|
|
|
|
case CURL_POLL_NONE:
|
|
|
|
break;
|
|
|
|
case CURL_POLL_IN:
|
2020-08-17 15:15:04 +02:00
|
|
|
if (fdc->scheduled) {
|
|
|
|
kore_platform_disable_read(fd);
|
|
|
|
#if !defined(__linux__)
|
|
|
|
kore_platform_disable_write(fd);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
fdc->scheduled = 1;
|
|
|
|
kore_platform_event_level_read(fd, fdc);
|
|
|
|
break;
|
2019-04-24 00:10:47 +02:00
|
|
|
case CURL_POLL_OUT:
|
|
|
|
case CURL_POLL_INOUT:
|
2020-08-17 16:45:10 +02:00
|
|
|
if (fdc->scheduled) {
|
|
|
|
kore_platform_disable_read(fd);
|
|
|
|
#if !defined(__linux__)
|
|
|
|
kore_platform_disable_write(fd);
|
|
|
|
#endif
|
2019-04-24 00:10:47 +02:00
|
|
|
}
|
2020-08-17 16:45:10 +02:00
|
|
|
fdc->scheduled = 1;
|
|
|
|
kore_platform_event_level_all(fd, fdc);
|
2019-04-24 00:10:47 +02:00
|
|
|
break;
|
|
|
|
case CURL_POLL_REMOVE:
|
|
|
|
if (fdc->scheduled) {
|
|
|
|
fdc->evt.flags = 0;
|
|
|
|
fdc->scheduled = 0;
|
|
|
|
kore_platform_disable_read(fd);
|
|
|
|
#if !defined(__linux__)
|
|
|
|
kore_platform_disable_write(fd);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fatal("unknown action value: %d", action);
|
|
|
|
}
|
|
|
|
|
2020-08-17 15:15:04 +02:00
|
|
|
if (action != CURL_POLL_NONE && action != CURL_POLL_REMOVE)
|
|
|
|
curl_run_schedule(fdc, 0);
|
2019-04-24 00:10:47 +02:00
|
|
|
|
|
|
|
return (CURLM_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
curl_process(void)
|
|
|
|
{
|
|
|
|
CURLcode res;
|
|
|
|
CURLMsg *msg;
|
|
|
|
CURL *handle;
|
|
|
|
struct kore_curl *client;
|
|
|
|
int pending;
|
|
|
|
|
|
|
|
pending = 0;
|
|
|
|
|
|
|
|
while ((msg = curl_multi_info_read(multi, &pending)) != NULL) {
|
|
|
|
if (msg->msg != CURLMSG_DONE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
handle = msg->easy_handle;
|
|
|
|
|
2019-05-05 21:16:42 +02:00
|
|
|
res = curl_easy_getinfo(handle, CURLINFO_PRIVATE,
|
|
|
|
(char **)&client);
|
2019-04-24 00:10:47 +02:00
|
|
|
if (res != CURLE_OK)
|
|
|
|
fatal("curl_easy_getinfo: %s", curl_easy_strerror(res));
|
|
|
|
|
|
|
|
if (client == NULL)
|
|
|
|
fatal("%s: failed to get client context", __func__);
|
|
|
|
|
|
|
|
client->result = msg->data.result;
|
|
|
|
|
|
|
|
if (client->type == KORE_CURL_TYPE_HTTP_CLIENT) {
|
|
|
|
curl_easy_getinfo(client->handle,
|
|
|
|
CURLINFO_RESPONSE_CODE, &client->http.status);
|
|
|
|
}
|
|
|
|
|
|
|
|
curl_multi_remove_handle(multi, client->handle);
|
|
|
|
curl_easy_cleanup(client->handle);
|
|
|
|
|
|
|
|
client->handle = NULL;
|
|
|
|
|
|
|
|
if (client->req != NULL)
|
|
|
|
http_request_wakeup(client->req);
|
|
|
|
else if (client->cb != NULL)
|
|
|
|
client->cb(client, client->arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
curl_timeout(void *uarg, u_int64_t now)
|
|
|
|
{
|
|
|
|
CURLMcode res;
|
|
|
|
|
|
|
|
timer = NULL;
|
|
|
|
|
|
|
|
res = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &running);
|
|
|
|
if (res != CURLM_OK)
|
|
|
|
fatal("curl_multi_socket_action: %s", curl_multi_strerror(res));
|
|
|
|
|
|
|
|
curl_process();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
curl_timer(CURLM *mctx, long timeout, void *arg)
|
|
|
|
{
|
2019-06-13 12:59:17 +02:00
|
|
|
timeout_immediate = 0;
|
|
|
|
|
2019-04-24 00:10:47 +02:00
|
|
|
if (timeout < 0) {
|
|
|
|
if (timer != NULL) {
|
|
|
|
kore_timer_remove(timer);
|
|
|
|
timer = NULL;
|
|
|
|
}
|
|
|
|
return (CURLM_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (timer != NULL) {
|
|
|
|
kore_timer_remove(timer);
|
|
|
|
timer = NULL;
|
|
|
|
}
|
|
|
|
|
2019-06-13 12:59:17 +02:00
|
|
|
if (timeout == 0) {
|
|
|
|
timeout_immediate = 1;
|
|
|
|
return (CURLM_OK);
|
|
|
|
}
|
2019-04-24 00:10:47 +02:00
|
|
|
|
|
|
|
timer = kore_timer_add(curl_timeout, timeout, mctx, KORE_TIMER_ONESHOT);
|
|
|
|
|
|
|
|
return (CURLM_OK);
|
|
|
|
}
|
|
|
|
|
2020-08-17 15:15:04 +02:00
|
|
|
static void
|
|
|
|
curl_run_schedule(struct fd_cache *fdc, int eof)
|
|
|
|
{
|
|
|
|
struct curl_run *run;
|
|
|
|
|
|
|
|
run = kore_pool_get(&run_pool);
|
|
|
|
run->fdc = fdc;
|
|
|
|
run->eof = eof;
|
|
|
|
|
|
|
|
TAILQ_INSERT_TAIL(&runlist, run, list);
|
|
|
|
}
|
|
|
|
|
2019-04-24 00:10:47 +02:00
|
|
|
static void
|
|
|
|
curl_event_handle(void *arg, int eof)
|
2020-08-17 15:15:04 +02:00
|
|
|
{
|
|
|
|
curl_run_schedule(arg, eof);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
curl_run_handle(struct curl_run *run)
|
2019-04-24 00:10:47 +02:00
|
|
|
{
|
2019-04-24 09:33:56 +02:00
|
|
|
CURLMcode res;
|
2019-04-24 00:10:47 +02:00
|
|
|
int flags;
|
2020-08-17 15:15:04 +02:00
|
|
|
struct fd_cache *fdc = run->fdc;
|
2019-04-24 00:10:47 +02:00
|
|
|
|
|
|
|
flags = 0;
|
|
|
|
|
|
|
|
if (fdc->evt.flags & KORE_EVENT_READ)
|
|
|
|
flags |= CURL_CSELECT_IN;
|
|
|
|
|
|
|
|
if (fdc->evt.flags & KORE_EVENT_WRITE)
|
|
|
|
flags |= CURL_CSELECT_OUT;
|
|
|
|
|
2020-08-17 15:15:04 +02:00
|
|
|
if (run->eof)
|
2019-04-28 21:44:59 +02:00
|
|
|
flags |= CURL_CSELECT_ERR;
|
2019-04-24 00:10:47 +02:00
|
|
|
|
2019-04-24 09:33:56 +02:00
|
|
|
res = curl_multi_socket_action(multi, fdc->fd, flags, &running);
|
|
|
|
if (res != CURLM_OK)
|
|
|
|
fatal("curl_multi_socket_action: %s", curl_multi_strerror(res));
|
2019-04-24 00:10:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct fd_cache *
|
|
|
|
fd_cache_get(int fd)
|
|
|
|
{
|
|
|
|
struct fd_cache *fdc;
|
|
|
|
int bucket;
|
|
|
|
|
|
|
|
bucket = fd % FD_CACHE_BUCKETS;
|
|
|
|
|
|
|
|
LIST_FOREACH(fdc, &cache[bucket], list) {
|
|
|
|
if (fdc->fd == fd)
|
|
|
|
return (fdc);
|
|
|
|
}
|
|
|
|
|
|
|
|
fdc = kore_pool_get(&fd_cache_pool);
|
|
|
|
|
|
|
|
fdc->fd = fd;
|
|
|
|
fdc->scheduled = 0;
|
|
|
|
|
|
|
|
fdc->evt.flags = 0;
|
|
|
|
fdc->evt.handle = curl_event_handle;
|
|
|
|
fdc->evt.type = KORE_TYPE_CURL_HANDLE;
|
|
|
|
|
|
|
|
LIST_INSERT_HEAD(&cache[bucket], fdc, list);
|
|
|
|
|
|
|
|
return (fdc);
|
|
|
|
}
|