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