kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

curl.c (17109B)



      1 /*
      2  * Copyright (c) 2019-2022 Joris Vink <joris@coders.se>
      3  *
      4  * Permission to use, copy, modify, and distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #include <sys/types.h>
     18 
     19 #include <inttypes.h>
     20 
     21 #include "kore.h"
     22 #include "http.h"
     23 #include "curl.h"
     24 
     25 #if defined(__linux__)
     26 #include "seccomp.h"
     27 
     28 static struct sock_filter filter_curl[] = {
     29 	/* Allow sockets and libcurl to call connect. */
     30 	KORE_SYSCALL_ALLOW(bind),
     31 	KORE_SYSCALL_ALLOW(ioctl),
     32 	KORE_SYSCALL_ALLOW(connect),
     33 	KORE_SYSCALL_ALLOW(getsockopt),
     34 	KORE_SYSCALL_ALLOW(getsockname),
     35 	KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_INET),
     36 	KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_INET6),
     37 	KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_UNIX),
     38 	KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_NETLINK),
     39 
     40 	/* Threading related. */
     41 	KORE_SYSCALL_ALLOW(clone),
     42 	KORE_SYSCALL_ALLOW(set_robust_list),
     43 
     44 	/* Other */
     45 	KORE_SYSCALL_ALLOW(uname),
     46 	KORE_SYSCALL_ALLOW(ioctl),
     47 	KORE_SYSCALL_ALLOW(madvise),
     48 	KORE_SYSCALL_ALLOW(recvmsg),
     49 	KORE_SYSCALL_ALLOW(sendmmsg),
     50 	KORE_SYSCALL_ALLOW(faccessat),
     51 	KORE_SYSCALL_ALLOW(newfstatat),
     52 	KORE_SYSCALL_ALLOW(getpeername),
     53 };
     54 #endif
     55 
     56 #define FD_CACHE_BUCKETS	2048
     57 
     58 struct fd_cache {
     59 	struct kore_event	evt;
     60 	int			fd;
     61 	int			scheduled;
     62 	LIST_ENTRY(fd_cache)	list;
     63 };
     64 
     65 struct curl_run {
     66 	int			eof;
     67 	struct fd_cache		*fdc;
     68 	TAILQ_ENTRY(curl_run)	list;
     69 };
     70 
     71 static void	curl_process(void);
     72 static void	curl_event_handle(void *, int);
     73 static void	curl_timeout(void *, u_int64_t);
     74 static int	curl_timer(CURLM *, long, void *);
     75 static void	curl_run_handle(struct curl_run *);
     76 static void	curl_run_schedule(struct fd_cache *, int);
     77 static int	curl_socket(CURL *, curl_socket_t, int, void *, void *);
     78 
     79 static struct fd_cache	*fd_cache_get(int);
     80 
     81 static TAILQ_HEAD(, curl_run)	runlist;
     82 static struct kore_pool		run_pool;
     83 static int			running = 0;
     84 static CURLM			*multi = NULL;
     85 static struct kore_timer	*timer = NULL;
     86 static struct kore_pool		fd_cache_pool;
     87 static char			user_agent[64];
     88 static int			timeout_immediate = 0;
     89 static LIST_HEAD(, fd_cache)	cache[FD_CACHE_BUCKETS];
     90 
     91 u_int16_t	kore_curl_timeout = KORE_CURL_TIMEOUT;
     92 u_int64_t	kore_curl_recv_max = KORE_CURL_RECV_MAX;
     93 
     94 void
     95 kore_curl_sysinit(void)
     96 {
     97 	CURLMcode	res;
     98 	int		i, len;
     99 
    100 	if (curl_global_init(CURL_GLOBAL_ALL))
    101 		fatal("failed to initialize libcurl");
    102 
    103 	if ((multi = curl_multi_init()) == NULL)
    104 		fatal("curl_multi_init(): failed");
    105 
    106 	/* XXX - make configurable? */
    107 	curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, 500);
    108 
    109 	if ((res = curl_multi_setopt(multi,
    110 	    CURLMOPT_SOCKETFUNCTION, curl_socket)) != CURLM_OK)
    111 		fatal("curl_multi_setopt: %s", curl_multi_strerror(res));
    112 
    113 	if ((res = curl_multi_setopt(multi,
    114 	    CURLMOPT_TIMERFUNCTION, curl_timer)) != CURLM_OK)
    115 		fatal("curl_multi_setopt: %s", curl_multi_strerror(res));
    116 
    117 	for (i = 0; i < FD_CACHE_BUCKETS; i++)
    118 		LIST_INIT(&cache[i]);
    119 
    120 	TAILQ_INIT(&runlist);
    121 
    122 	kore_pool_init(&fd_cache_pool, "fd_cache_pool", 100,
    123 	    sizeof(struct fd_cache));
    124 	kore_pool_init(&run_pool, "run_pool", 100, sizeof(struct curl_run));
    125 
    126 	len = snprintf(user_agent, sizeof(user_agent), "kore/%s", kore_version);
    127 	if (len == -1 || (size_t)len >= sizeof(user_agent))
    128 		fatal("user-agent string too long");
    129 
    130 #if defined(__linux__)
    131 	kore_seccomp_filter("curl", filter_curl, KORE_FILTER_LEN(filter_curl));
    132 #endif
    133 #if defined(KORE_USE_PLATFORM_PLEDGE)
    134 	kore_platform_add_pledge("dns");
    135 #endif
    136 }
    137 
    138 int
    139 kore_curl_init(struct kore_curl *client, const char *url, int flags)
    140 {
    141 	CURL		*handle;
    142 
    143 	if ((flags & KORE_CURL_ASYNC) && (flags & KORE_CURL_SYNC)) {
    144 		(void)kore_strlcpy(client->errbuf, "invalid flags",
    145 		    sizeof(client->errbuf));
    146 		return (KORE_RESULT_ERROR);
    147 	}
    148 
    149 	memset(client, 0, sizeof(*client));
    150 
    151 	TAILQ_INIT(&client->http.resp_hdrs);
    152 
    153 	if ((handle = curl_easy_init()) == NULL) {
    154 		(void)kore_strlcpy(client->errbuf, "failed to setup curl",
    155 		    sizeof(client->errbuf));
    156 		return (KORE_RESULT_ERROR);
    157 	}
    158 
    159 	curl_easy_setopt(handle, CURLOPT_WRITEDATA, &client->response);
    160 	curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, kore_curl_tobuf);
    161 
    162 	curl_easy_setopt(handle, CURLOPT_URL, url);
    163 	curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1);
    164 	curl_easy_setopt(handle, CURLOPT_PRIVATE, client);
    165 	curl_easy_setopt(handle, CURLOPT_USERAGENT, user_agent);
    166 	curl_easy_setopt(handle, CURLOPT_TIMEOUT, kore_curl_timeout);
    167 	curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, client->errbuf);
    168 
    169 	client->flags = flags;
    170 	client->handle = handle;
    171 	client->url = kore_strdup(url);
    172 	client->type = KORE_CURL_TYPE_CUSTOM;
    173 
    174 	return (KORE_RESULT_OK);
    175 }
    176 
    177 void
    178 kore_curl_cleanup(struct kore_curl *client)
    179 {
    180 	struct http_header	*hdr, *next;
    181 
    182 	kore_free(client->url);
    183 
    184 	if (client->flags & KORE_CURL_FLAG_BOUND)
    185 		LIST_REMOVE(client, list);
    186 
    187 	if (client->handle != NULL) {
    188 		curl_multi_remove_handle(multi, client->handle);
    189 		curl_easy_cleanup(client->handle);
    190 	}
    191 
    192 	if (client->http.hdrlist != NULL)
    193 		curl_slist_free_all(client->http.hdrlist);
    194 
    195 	if (client->response != NULL)
    196 		kore_buf_free(client->response);
    197 
    198 	if (client->http.headers != NULL)
    199 		kore_buf_free(client->http.headers);
    200 
    201 	if (client->http.tosend != NULL)
    202 		kore_buf_free(client->http.tosend);
    203 
    204 	for (hdr = TAILQ_FIRST(&client->http.resp_hdrs);
    205 	    hdr != NULL; hdr = next) {
    206 		next = TAILQ_NEXT(hdr, list);
    207 		TAILQ_REMOVE(&client->http.resp_hdrs, hdr, list);
    208 		kore_pool_put(&http_header_pool, hdr);
    209 	}
    210 }
    211 
    212 void
    213 kore_curl_do_timeout(void)
    214 {
    215 	while (timeout_immediate) {
    216 		curl_timeout(NULL, kore_time_ms());
    217 		if (running == 0)
    218 			curl_timer(multi, -1, NULL);
    219 	}
    220 }
    221 
    222 void
    223 kore_curl_run_scheduled(void)
    224 {
    225 	struct curl_run		*run;
    226 
    227 	while ((run = TAILQ_FIRST(&runlist))) {
    228 		TAILQ_REMOVE(&runlist, run, list);
    229 		curl_run_handle(run);
    230 		kore_pool_put(&run_pool, run);
    231 	}
    232 
    233 	curl_process();
    234 }
    235 
    236 size_t
    237 kore_curl_tobuf(char *ptr, size_t size, size_t nmemb, void *udata)
    238 {
    239 	size_t			len;
    240 	struct kore_buf		**buf, *b;
    241 
    242 	if (SIZE_MAX / nmemb < size)
    243 		fatal("%s: %zu * %zu overflow", __func__, nmemb, size);
    244 
    245 	buf = udata;
    246 	len = size * nmemb;
    247 
    248 	if (*buf == NULL)
    249 		*buf = kore_buf_alloc(len);
    250 
    251 	b = *buf;
    252 
    253 	if (b->offset + len < b->offset)
    254 		fatal("%s: %zu+%zu overflows", __func__, b->offset, len);
    255 
    256 	if ((b->offset + len) > kore_curl_recv_max) {
    257 		kore_log(LOG_ERR,
    258 		    "received too large transfer (%zu > %" PRIu64 ")",
    259 		    b->offset + len, kore_curl_recv_max);
    260 		return (0);
    261 	}
    262 
    263 	kore_buf_append(b, ptr, len);
    264 
    265 	return (len);
    266 }
    267 
    268 size_t
    269 kore_curl_frombuf(char *ptr, size_t size, size_t nmemb, void *udata)
    270 {
    271 	size_t			len;
    272 	struct kore_buf		*buf;
    273 
    274 	if (SIZE_MAX / nmemb < size)
    275 		fatal("%s: %zu * %zu overflow", __func__, nmemb, size);
    276 
    277 	buf = udata;
    278 	len = size * nmemb;
    279 
    280 	if (buf->offset == buf->length)
    281 		return (0);
    282 
    283 	if (buf->offset + len < buf->offset)
    284 		fatal("%s: %zu+%zu overflows", __func__, buf->offset, len);
    285 
    286 	if ((buf->offset + len) < buf->length) {
    287 		memcpy(ptr, buf->data + buf->offset, len);
    288 	} else {
    289 		len = buf->length - buf->offset;
    290 		memcpy(ptr, buf->data + buf->offset, len);
    291 	}
    292 
    293 	buf->offset += len;
    294 
    295 	return (len);
    296 }
    297 
    298 void
    299 kore_curl_bind_request(struct kore_curl *client, struct http_request *req)
    300 {
    301 	if (client->cb != NULL)
    302 		fatal("%s: already bound to callback", __func__);
    303 
    304 	client->req = req;
    305 	http_request_sleep(req);
    306 
    307 	client->flags |= KORE_CURL_FLAG_BOUND;
    308 	LIST_INSERT_HEAD(&req->chandles, client, list);
    309 }
    310 
    311 void
    312 kore_curl_bind_callback(struct kore_curl *client,
    313     void (*cb)(struct kore_curl *, void *), void *arg)
    314 {
    315 	if (client->req != NULL)
    316 		fatal("%s: already bound to request", __func__);
    317 
    318 	client->cb = cb;
    319 	client->arg = arg;
    320 }
    321 
    322 void
    323 kore_curl_run(struct kore_curl *client)
    324 {
    325 	if (client->flags & KORE_CURL_ASYNC) {
    326 		curl_multi_add_handle(multi, client->handle);
    327 		return;
    328 	}
    329 
    330 	client->result = curl_easy_perform(client->handle);
    331 
    332 	curl_easy_getinfo(client->handle,
    333 	    CURLINFO_RESPONSE_CODE, &client->http.status);
    334 
    335 	curl_easy_cleanup(client->handle);
    336 	client->handle = NULL;
    337 }
    338 
    339 int
    340 kore_curl_success(struct kore_curl *client)
    341 {
    342 	return (client->result == CURLE_OK);
    343 }
    344 
    345 const char *
    346 kore_curl_strerror(struct kore_curl *client)
    347 {
    348 	const char	*err;
    349 
    350 	if (client->errbuf[0] != '\0')
    351 		err = &client->errbuf[0];
    352 	else
    353 		err = curl_easy_strerror(client->result);
    354 
    355 	return (err);
    356 }
    357 
    358 void
    359 kore_curl_logerror(struct kore_curl *client)
    360 {
    361 	kore_log(LOG_NOTICE, "curl error: %s -> %s", client->url,
    362 	    kore_curl_strerror(client));
    363 }
    364 
    365 void
    366 kore_curl_response_as_bytes(struct kore_curl *client, const u_int8_t **body,
    367     size_t *len)
    368 {
    369 	if (client->response == NULL) {
    370 		*len = 0;
    371 		*body = NULL;
    372 	} else {
    373 		*len = client->response->offset;
    374 		*body = client->response->data;
    375 	}
    376 }
    377 
    378 char *
    379 kore_curl_response_as_string(struct kore_curl *client)
    380 {
    381 	kore_buf_stringify(client->response, NULL);
    382 
    383 	return ((char *)client->response->data);
    384 }
    385 
    386 void
    387 kore_curl_http_setup(struct kore_curl *client, int method, const void *data,
    388     size_t len)
    389 {
    390 	const char	*mname;
    391 	int		has_body;
    392 
    393 	if (client->handle == NULL)
    394 		fatal("%s: called without setup", __func__);
    395 
    396 	mname = NULL;
    397 	has_body = 1;
    398 
    399 	client->type = KORE_CURL_TYPE_HTTP_CLIENT;
    400 
    401 	curl_easy_setopt(client->handle, CURLOPT_HEADERDATA,
    402 	    &client->http.headers);
    403 	curl_easy_setopt(client->handle, CURLOPT_HEADERFUNCTION,
    404 	    kore_curl_tobuf);
    405 
    406 	kore_curl_http_set_header(client, "expect", "");
    407 
    408 	switch (method) {
    409 	case HTTP_METHOD_GET:
    410 	case HTTP_METHOD_OPTIONS:
    411 		break;
    412 	case HTTP_METHOD_HEAD:
    413 		curl_easy_setopt(client->handle, CURLOPT_NOBODY, 1);
    414 		break;
    415 	case HTTP_METHOD_PUT:
    416 		has_body = 1;
    417 		curl_easy_setopt(client->handle, CURLOPT_UPLOAD, 1);
    418 		break;
    419 	case HTTP_METHOD_PATCH:
    420 	case HTTP_METHOD_DELETE:
    421 		mname = http_method_text(method);
    422 		/* fallthrough */
    423 	case HTTP_METHOD_POST:
    424 		has_body = 1;
    425 		curl_easy_setopt(client->handle, CURLOPT_POST, 1);
    426 		break;
    427 	default:
    428 		fatal("%s: unknown method %d", __func__, method);
    429 	}
    430 
    431 	if (has_body && data != NULL && len > 0) {
    432 		client->http.tosend = kore_buf_alloc(len);
    433 		kore_buf_append(client->http.tosend, data, len);
    434 		kore_buf_reset(client->http.tosend);
    435 
    436 		curl_easy_setopt(client->handle, CURLOPT_READDATA,
    437 		    client->http.tosend);
    438 		curl_easy_setopt(client->handle, CURLOPT_READFUNCTION,
    439 		    kore_curl_frombuf);
    440 	}
    441 
    442 	if (has_body) {
    443 		if (method == HTTP_METHOD_PUT) {
    444 			curl_easy_setopt(client->handle,
    445 			    CURLOPT_INFILESIZE_LARGE, (curl_off_t)len);
    446 		} else {
    447 			curl_easy_setopt(client->handle,
    448 			    CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)len);
    449 		}
    450 	} else {
    451 		if (data != NULL || len != 0) {
    452 			fatal("%s: %d should not have a body",
    453 			    __func__, method);
    454 		}
    455 	}
    456 
    457 	if (mname != NULL)
    458 		curl_easy_setopt(client->handle, CURLOPT_CUSTOMREQUEST, mname);
    459 }
    460 
    461 void
    462 kore_curl_http_set_header(struct kore_curl *client, const char *header,
    463     const char *value)
    464 {
    465 	struct kore_buf		buf;
    466 	const char		*hdr;
    467 
    468 	kore_buf_init(&buf, 512);
    469 
    470 	if (value != NULL || *value != '\0') {
    471 		kore_buf_appendf(&buf, "%s: %s", header, value);
    472 	} else {
    473 		kore_buf_appendf(&buf, "%s:", header);
    474 	}
    475 
    476 	hdr = kore_buf_stringify(&buf, NULL);
    477 
    478 	client->http.hdrlist = curl_slist_append(client->http.hdrlist, hdr);
    479 	kore_buf_cleanup(&buf);
    480 
    481 	curl_easy_setopt(client->handle,
    482 	    CURLOPT_HTTPHEADER, client->http.hdrlist);
    483 }
    484 
    485 int
    486 kore_curl_http_get_header(struct kore_curl *client, const char *header,
    487     const char **out)
    488 {
    489 	struct http_header	*hdr;
    490 
    491 	if (!(client->flags & KORE_CURL_FLAG_HTTP_PARSED_HEADERS))
    492 		kore_curl_http_parse_headers(client);
    493 
    494 	TAILQ_FOREACH(hdr, &(client->http.resp_hdrs), list) {
    495 		if (!strcasecmp(hdr->header, header)) {
    496 			*out = hdr->value;
    497 			return (KORE_RESULT_OK);
    498 		}
    499 	}
    500 
    501 	return (KORE_RESULT_ERROR);
    502 }
    503 
    504 void
    505 kore_curl_http_parse_headers(struct kore_curl *client)
    506 {
    507 	struct http_header	*hdr;
    508 	int			i, cnt;
    509 	char			*value, *hbuf, *headers[HTTP_REQ_HEADER_MAX];
    510 
    511 	if (client->flags & KORE_CURL_FLAG_HTTP_PARSED_HEADERS)
    512 		fatal("%s: headers already parsed", __func__);
    513 
    514 	client->flags |= KORE_CURL_FLAG_HTTP_PARSED_HEADERS;
    515 
    516 	if (client->http.headers == NULL)
    517 		return;
    518 
    519 	hbuf = kore_buf_stringify(client->http.headers, NULL);
    520 	cnt = kore_split_string(hbuf, "\r\n", headers, HTTP_REQ_HEADER_MAX);
    521 
    522 	for (i = 0; i < cnt; i++) {
    523 		if ((value = http_validate_header(headers[i])) == NULL)
    524 			continue;
    525 
    526 		if (*value == '\0')
    527 			continue;
    528 
    529 		hdr = kore_pool_get(&http_header_pool);
    530 		hdr->header = headers[i];
    531 		hdr->value = value;
    532 		TAILQ_INSERT_TAIL(&(client->http.resp_hdrs), hdr, list);
    533 	}
    534 }
    535 
    536 static int
    537 curl_socket(CURL *easy, curl_socket_t fd, int action, void *arg, void *sock)
    538 {
    539 	CURLcode		res;
    540 	struct fd_cache		*fdc;
    541 	struct kore_curl	*client;
    542 
    543 	client = NULL;
    544 
    545 	res = curl_easy_getinfo(easy, CURLINFO_PRIVATE, (char **)&client);
    546 	if (res != CURLE_OK)
    547 		fatal("curl_easy_getinfo: %s", curl_easy_strerror(res));
    548 
    549 	if (client == NULL)
    550 		fatal("%s: failed to get client context", __func__);
    551 
    552 	fdc = fd_cache_get(fd);
    553 
    554 	switch (action) {
    555 	case CURL_POLL_NONE:
    556 		break;
    557 	case CURL_POLL_IN:
    558 		if (fdc->scheduled) {
    559 			kore_platform_disable_read(fd);
    560 #if !defined(__linux__)
    561 			kore_platform_disable_write(fd);
    562 #endif
    563 		}
    564 		fdc->scheduled = 1;
    565 		kore_platform_event_level_read(fd, fdc);
    566 		break;
    567 	case CURL_POLL_OUT:
    568 	case CURL_POLL_INOUT:
    569 		if (fdc->scheduled) {
    570 			kore_platform_disable_read(fd);
    571 #if !defined(__linux__)
    572 			kore_platform_disable_write(fd);
    573 #endif
    574 		}
    575 		fdc->scheduled = 1;
    576 		kore_platform_event_level_all(fd, fdc);
    577 		break;
    578 	case CURL_POLL_REMOVE:
    579 		if (fdc->scheduled) {
    580 			fdc->evt.flags = 0;
    581 			fdc->scheduled = 0;
    582 			kore_platform_disable_read(fd);
    583 #if !defined(__linux__)
    584 			kore_platform_disable_write(fd);
    585 #endif
    586 		}
    587 		break;
    588 	default:
    589 		fatal("unknown action value: %d", action);
    590 	}
    591 
    592 	if (action != CURL_POLL_NONE && action != CURL_POLL_REMOVE)
    593 		curl_run_schedule(fdc, 0);
    594 
    595 	return (CURLM_OK);
    596 }
    597 
    598 static void
    599 curl_process(void)
    600 {
    601 	CURLcode		res;
    602 	CURLMsg			*msg;
    603 	CURL			*handle;
    604 	struct kore_curl	*client;
    605 	int			pending;
    606 
    607 	pending = 0;
    608 
    609 	while ((msg = curl_multi_info_read(multi, &pending)) != NULL) {
    610 		if (msg->msg != CURLMSG_DONE)
    611 			continue;
    612 
    613 		handle = msg->easy_handle;
    614 
    615 		res = curl_easy_getinfo(handle, CURLINFO_PRIVATE,
    616 		    (char **)&client);
    617 		if (res != CURLE_OK)
    618 			fatal("curl_easy_getinfo: %s", curl_easy_strerror(res));
    619 
    620 		if (client == NULL)
    621 			fatal("%s: failed to get client context", __func__);
    622 
    623 		client->result = msg->data.result;
    624 
    625 		if (client->type == KORE_CURL_TYPE_HTTP_CLIENT) {
    626 			curl_easy_getinfo(client->handle,
    627 			    CURLINFO_RESPONSE_CODE, &client->http.status);
    628 		}
    629 
    630 		curl_multi_remove_handle(multi, client->handle);
    631 		curl_easy_cleanup(client->handle);
    632 
    633 		client->handle = NULL;
    634 
    635 		if (client->req != NULL)
    636 			http_request_wakeup(client->req);
    637 		else if (client->cb != NULL)
    638 			client->cb(client, client->arg);
    639 	}
    640 }
    641 
    642 static void
    643 curl_timeout(void *uarg, u_int64_t now)
    644 {
    645 	CURLMcode	res;
    646 
    647 	timer = NULL;
    648 
    649 	res = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &running);
    650 	if (res != CURLM_OK)
    651 		fatal("curl_multi_socket_action: %s", curl_multi_strerror(res));
    652 
    653 	curl_process();
    654 }
    655 
    656 static int
    657 curl_timer(CURLM *mctx, long timeout, void *arg)
    658 {
    659 	timeout_immediate = 0;
    660 
    661 	if (timeout < 0) {
    662 		if (timer != NULL) {
    663 			kore_timer_remove(timer);
    664 			timer = NULL;
    665 		}
    666 		return (CURLM_OK);
    667 	}
    668 
    669 	if (timer != NULL) {
    670 		kore_timer_remove(timer);
    671 		timer = NULL;
    672 	}
    673 
    674 	if (timeout == 0) {
    675 		timeout_immediate = 1;
    676 		return (CURLM_OK);
    677 	}
    678 
    679 	timer = kore_timer_add(curl_timeout, timeout, mctx, KORE_TIMER_ONESHOT);
    680 
    681 	return (CURLM_OK);
    682 }
    683 
    684 static void
    685 curl_run_schedule(struct fd_cache *fdc, int eof)
    686 {
    687 	struct curl_run		*run;
    688 
    689 	run = kore_pool_get(&run_pool);
    690 	run->fdc = fdc;
    691 	run->eof = eof;
    692 
    693 	TAILQ_INSERT_TAIL(&runlist, run, list);
    694 }
    695 
    696 static void
    697 curl_event_handle(void *arg, int eof)
    698 {
    699 	curl_run_schedule(arg, eof);
    700 }
    701 
    702 static void
    703 curl_run_handle(struct curl_run *run)
    704 {
    705 	CURLMcode		res;
    706 	int			flags;
    707 	struct fd_cache		*fdc = run->fdc;
    708 
    709 	flags = 0;
    710 
    711 	if (fdc->evt.flags & KORE_EVENT_READ)
    712 		flags |= CURL_CSELECT_IN;
    713 
    714 	if (fdc->evt.flags & KORE_EVENT_WRITE)
    715 		flags |= CURL_CSELECT_OUT;
    716 
    717 	if (run->eof)
    718 		flags |= CURL_CSELECT_ERR;
    719 
    720 	res = curl_multi_socket_action(multi, fdc->fd, flags, &running);
    721 	if (res != CURLM_OK)
    722 		fatal("curl_multi_socket_action: %s", curl_multi_strerror(res));
    723 }
    724 
    725 static struct fd_cache *
    726 fd_cache_get(int fd)
    727 {
    728 	struct fd_cache		*fdc;
    729 	int			bucket;
    730 
    731 	bucket = fd % FD_CACHE_BUCKETS;
    732 
    733 	LIST_FOREACH(fdc, &cache[bucket], list) {
    734 		if (fdc->fd == fd)
    735 			return (fdc);
    736 	}
    737 
    738 	fdc = kore_pool_get(&fd_cache_pool);
    739 
    740 	fdc->fd = fd;
    741 	fdc->scheduled = 0;
    742 
    743 	fdc->evt.flags = 0;
    744 	fdc->evt.handle = curl_event_handle;
    745 	fdc->evt.type = KORE_TYPE_CURL_HANDLE;
    746 
    747 	LIST_INSERT_HEAD(&cache[bucket], fdc, list);
    748 
    749 	return (fdc);
    750 }