1 /*
2  * Copyright (c) 2019-2021 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
kore_curl_sysinit(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
kore_curl_init(struct kore_curl * client,const char * url,int flags)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
kore_curl_cleanup(struct kore_curl * client)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
kore_curl_do_timeout(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
kore_curl_run_scheduled(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
kore_curl_tobuf(char * ptr,size_t size,size_t nmemb,void * udata)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
kore_curl_frombuf(char * ptr,size_t size,size_t nmemb,void * udata)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
kore_curl_bind_request(struct kore_curl * client,struct http_request * req)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
kore_curl_bind_callback(struct kore_curl * client,void (* cb)(struct kore_curl *,void *),void * arg)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
kore_curl_run(struct kore_curl * client)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
kore_curl_success(struct kore_curl * client)340 kore_curl_success(struct kore_curl *client)
341 {
342 	return (client->result == CURLE_OK);
343 }
344 
345 const char *
kore_curl_strerror(struct kore_curl * client)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
kore_curl_logerror(struct kore_curl * client)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
kore_curl_response_as_bytes(struct kore_curl * client,const u_int8_t ** body,size_t * len)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 *
kore_curl_response_as_string(struct kore_curl * client)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
kore_curl_http_setup(struct kore_curl * client,int method,const void * data,size_t len)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 		break;
411 	case HTTP_METHOD_HEAD:
412 		curl_easy_setopt(client->handle, CURLOPT_NOBODY, 1);
413 		break;
414 	case HTTP_METHOD_DELETE:
415 	case HTTP_METHOD_OPTIONS:
416 		break;
417 	case HTTP_METHOD_PUT:
418 		has_body = 1;
419 		curl_easy_setopt(client->handle, CURLOPT_UPLOAD, 1);
420 		break;
421 	case HTTP_METHOD_PATCH:
422 		mname = http_method_text(method);
423 		/* fallthrough */
424 	case HTTP_METHOD_POST:
425 		has_body = 1;
426 		curl_easy_setopt(client->handle, CURLOPT_POST, 1);
427 		break;
428 	default:
429 		fatal("%s: unknown method %d", __func__, method);
430 	}
431 
432 	if (has_body && data != NULL && len > 0) {
433 		client->http.tosend = kore_buf_alloc(len);
434 		kore_buf_append(client->http.tosend, data, len);
435 		kore_buf_reset(client->http.tosend);
436 
437 		curl_easy_setopt(client->handle, CURLOPT_READDATA,
438 		    client->http.tosend);
439 		curl_easy_setopt(client->handle, CURLOPT_READFUNCTION,
440 		    kore_curl_frombuf);
441 	}
442 
443 	if (has_body) {
444 		if (method == HTTP_METHOD_PUT) {
445 			curl_easy_setopt(client->handle,
446 			    CURLOPT_INFILESIZE_LARGE, (curl_off_t)len);
447 		} else {
448 			curl_easy_setopt(client->handle,
449 			    CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)len);
450 		}
451 	} else {
452 		if (data != NULL || len != 0) {
453 			fatal("%s: %d should not have a body",
454 			    __func__, method);
455 		}
456 	}
457 
458 	if (mname != NULL)
459 		curl_easy_setopt(client->handle, CURLOPT_CUSTOMREQUEST, mname);
460 }
461 
462 void
kore_curl_http_set_header(struct kore_curl * client,const char * header,const char * value)463 kore_curl_http_set_header(struct kore_curl *client, const char *header,
464     const char *value)
465 {
466 	struct kore_buf		buf;
467 	const char		*hdr;
468 
469 	kore_buf_init(&buf, 512);
470 
471 	if (value != NULL || *value != '\0') {
472 		kore_buf_appendf(&buf, "%s: %s", header, value);
473 	} else {
474 		kore_buf_appendf(&buf, "%s:", header);
475 	}
476 
477 	hdr = kore_buf_stringify(&buf, NULL);
478 
479 	client->http.hdrlist = curl_slist_append(client->http.hdrlist, hdr);
480 	kore_buf_cleanup(&buf);
481 
482 	curl_easy_setopt(client->handle,
483 	    CURLOPT_HTTPHEADER, client->http.hdrlist);
484 }
485 
486 int
kore_curl_http_get_header(struct kore_curl * client,const char * header,const char ** out)487 kore_curl_http_get_header(struct kore_curl *client, const char *header,
488     const char **out)
489 {
490 	struct http_header	*hdr;
491 
492 	if (!(client->flags & KORE_CURL_FLAG_HTTP_PARSED_HEADERS))
493 		kore_curl_http_parse_headers(client);
494 
495 	TAILQ_FOREACH(hdr, &(client->http.resp_hdrs), list) {
496 		if (!strcasecmp(hdr->header, header)) {
497 			*out = hdr->value;
498 			return (KORE_RESULT_OK);
499 		}
500 	}
501 
502 	return (KORE_RESULT_ERROR);
503 }
504 
505 void
kore_curl_http_parse_headers(struct kore_curl * client)506 kore_curl_http_parse_headers(struct kore_curl *client)
507 {
508 	struct http_header	*hdr;
509 	int			i, cnt;
510 	char			*value, *hbuf, *headers[HTTP_REQ_HEADER_MAX];
511 
512 	if (client->flags & KORE_CURL_FLAG_HTTP_PARSED_HEADERS)
513 		fatal("%s: headers already parsed", __func__);
514 
515 	client->flags |= KORE_CURL_FLAG_HTTP_PARSED_HEADERS;
516 
517 	if (client->http.headers == NULL)
518 		return;
519 
520 	hbuf = kore_buf_stringify(client->http.headers, NULL);
521 	cnt = kore_split_string(hbuf, "\r\n", headers, HTTP_REQ_HEADER_MAX);
522 
523 	for (i = 0; i < cnt; i++) {
524 		if ((value = http_validate_header(headers[i])) == NULL)
525 			continue;
526 
527 		if (*value == '\0')
528 			continue;
529 
530 		hdr = kore_pool_get(&http_header_pool);
531 		hdr->header = headers[i];
532 		hdr->value = value;
533 		TAILQ_INSERT_TAIL(&(client->http.resp_hdrs), hdr, list);
534 	}
535 }
536 
537 static int
curl_socket(CURL * easy,curl_socket_t fd,int action,void * arg,void * sock)538 curl_socket(CURL *easy, curl_socket_t fd, int action, void *arg, void *sock)
539 {
540 	CURLcode		res;
541 	struct fd_cache		*fdc;
542 	struct kore_curl	*client;
543 
544 	client = NULL;
545 
546 	res = curl_easy_getinfo(easy, CURLINFO_PRIVATE, (char **)&client);
547 	if (res != CURLE_OK)
548 		fatal("curl_easy_getinfo: %s", curl_easy_strerror(res));
549 
550 	if (client == NULL)
551 		fatal("%s: failed to get client context", __func__);
552 
553 	fdc = fd_cache_get(fd);
554 
555 	switch (action) {
556 	case CURL_POLL_NONE:
557 		break;
558 	case CURL_POLL_IN:
559 		if (fdc->scheduled) {
560 			kore_platform_disable_read(fd);
561 #if !defined(__linux__)
562 			kore_platform_disable_write(fd);
563 #endif
564 		}
565 		fdc->scheduled = 1;
566 		kore_platform_event_level_read(fd, fdc);
567 		break;
568 	case CURL_POLL_OUT:
569 	case CURL_POLL_INOUT:
570 		if (fdc->scheduled) {
571 			kore_platform_disable_read(fd);
572 #if !defined(__linux__)
573 			kore_platform_disable_write(fd);
574 #endif
575 		}
576 		fdc->scheduled = 1;
577 		kore_platform_event_level_all(fd, fdc);
578 		break;
579 	case CURL_POLL_REMOVE:
580 		if (fdc->scheduled) {
581 			fdc->evt.flags = 0;
582 			fdc->scheduled = 0;
583 			kore_platform_disable_read(fd);
584 #if !defined(__linux__)
585 			kore_platform_disable_write(fd);
586 #endif
587 		}
588 		break;
589 	default:
590 		fatal("unknown action value: %d", action);
591 	}
592 
593 	if (action != CURL_POLL_NONE && action != CURL_POLL_REMOVE)
594 		curl_run_schedule(fdc, 0);
595 
596 	return (CURLM_OK);
597 }
598 
599 static void
curl_process(void)600 curl_process(void)
601 {
602 	CURLcode		res;
603 	CURLMsg			*msg;
604 	CURL			*handle;
605 	struct kore_curl	*client;
606 	int			pending;
607 
608 	pending = 0;
609 
610 	while ((msg = curl_multi_info_read(multi, &pending)) != NULL) {
611 		if (msg->msg != CURLMSG_DONE)
612 			continue;
613 
614 		handle = msg->easy_handle;
615 
616 		res = curl_easy_getinfo(handle, CURLINFO_PRIVATE,
617 		    (char **)&client);
618 		if (res != CURLE_OK)
619 			fatal("curl_easy_getinfo: %s", curl_easy_strerror(res));
620 
621 		if (client == NULL)
622 			fatal("%s: failed to get client context", __func__);
623 
624 		client->result = msg->data.result;
625 
626 		if (client->type == KORE_CURL_TYPE_HTTP_CLIENT) {
627 			curl_easy_getinfo(client->handle,
628 			    CURLINFO_RESPONSE_CODE, &client->http.status);
629 		}
630 
631 		curl_multi_remove_handle(multi, client->handle);
632 		curl_easy_cleanup(client->handle);
633 
634 		client->handle = NULL;
635 
636 		if (client->req != NULL)
637 			http_request_wakeup(client->req);
638 		else if (client->cb != NULL)
639 			client->cb(client, client->arg);
640 	}
641 }
642 
643 static void
curl_timeout(void * uarg,u_int64_t now)644 curl_timeout(void *uarg, u_int64_t now)
645 {
646 	CURLMcode	res;
647 
648 	timer = NULL;
649 
650 	res = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &running);
651 	if (res != CURLM_OK)
652 		fatal("curl_multi_socket_action: %s", curl_multi_strerror(res));
653 
654 	curl_process();
655 }
656 
657 static int
curl_timer(CURLM * mctx,long timeout,void * arg)658 curl_timer(CURLM *mctx, long timeout, void *arg)
659 {
660 	timeout_immediate = 0;
661 
662 	if (timeout < 0) {
663 		if (timer != NULL) {
664 			kore_timer_remove(timer);
665 			timer = NULL;
666 		}
667 		return (CURLM_OK);
668 	}
669 
670 	if (timer != NULL) {
671 		kore_timer_remove(timer);
672 		timer = NULL;
673 	}
674 
675 	if (timeout == 0) {
676 		timeout_immediate = 1;
677 		return (CURLM_OK);
678 	}
679 
680 	timer = kore_timer_add(curl_timeout, timeout, mctx, KORE_TIMER_ONESHOT);
681 
682 	return (CURLM_OK);
683 }
684 
685 static void
curl_run_schedule(struct fd_cache * fdc,int eof)686 curl_run_schedule(struct fd_cache *fdc, int eof)
687 {
688 	struct curl_run		*run;
689 
690 	run = kore_pool_get(&run_pool);
691 	run->fdc = fdc;
692 	run->eof = eof;
693 
694 	TAILQ_INSERT_TAIL(&runlist, run, list);
695 }
696 
697 static void
curl_event_handle(void * arg,int eof)698 curl_event_handle(void *arg, int eof)
699 {
700 	curl_run_schedule(arg, eof);
701 }
702 
703 static void
curl_run_handle(struct curl_run * run)704 curl_run_handle(struct curl_run *run)
705 {
706 	CURLMcode		res;
707 	int			flags;
708 	struct fd_cache		*fdc = run->fdc;
709 
710 	flags = 0;
711 
712 	if (fdc->evt.flags & KORE_EVENT_READ)
713 		flags |= CURL_CSELECT_IN;
714 
715 	if (fdc->evt.flags & KORE_EVENT_WRITE)
716 		flags |= CURL_CSELECT_OUT;
717 
718 	if (run->eof)
719 		flags |= CURL_CSELECT_ERR;
720 
721 	res = curl_multi_socket_action(multi, fdc->fd, flags, &running);
722 	if (res != CURLM_OK)
723 		fatal("curl_multi_socket_action: %s", curl_multi_strerror(res));
724 }
725 
726 static struct fd_cache *
fd_cache_get(int fd)727 fd_cache_get(int fd)
728 {
729 	struct fd_cache		*fdc;
730 	int			bucket;
731 
732 	bucket = fd % FD_CACHE_BUCKETS;
733 
734 	LIST_FOREACH(fdc, &cache[bucket], list) {
735 		if (fdc->fd == fd)
736 			return (fdc);
737 	}
738 
739 	fdc = kore_pool_get(&fd_cache_pool);
740 
741 	fdc->fd = fd;
742 	fdc->scheduled = 0;
743 
744 	fdc->evt.flags = 0;
745 	fdc->evt.handle = curl_event_handle;
746 	fdc->evt.type = KORE_TYPE_CURL_HANDLE;
747 
748 	LIST_INSERT_HEAD(&cache[bucket], fdc, list);
749 
750 	return (fdc);
751 }
752