2 * Copyright (c) 2021, 2024 Omar Polo <op@omarpolo.com>
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.
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.
20 #include <sys/types.h>
21 #include <sys/socket.h>
24 #include <netinet/in.h>
44 #include "telescope.h"
47 static struct imsgev *iev_ui;
58 /* a pending request */
61 enum conn_state state;
79 struct addrinfo *servinfo, *p;
85 TAILQ_ENTRY(req) reqs;
88 static struct req *req_by_id(uint32_t);
90 static void die(void) __attribute__((__noreturn__));
92 static void close_with_err(struct req*, const char*);
93 static void close_with_errf(struct req*, const char*, ...)
94 __attribute__((format(printf, 2, 3)));
96 static int try_to_connect(struct req *);
97 static int gemini_parse_reply(struct req *, const char *, size_t);
98 static void net_ev(int, int, void *);
99 static void handle_dispatch_imsg(int, int, void*);
101 static int net_send_ui(int, uint32_t, const void *, uint16_t);
103 /* TODO: making this customizable */
104 struct timeval timeout_for_handshake = { 5, 0 };
106 TAILQ_HEAD(, req) reqhead;
109 req_by_id(uint32_t id)
113 TAILQ_FOREACH(r, &reqhead, reqs) {
121 static void __attribute__((__noreturn__))
128 req_bio_ev(struct req *req)
133 ev = bufio_ev(&req->bio);
134 if (ev & BUFIO_WANT_READ)
136 if (ev & BUFIO_WANT_WRITE)
142 close_conn(int fd, int ev, void *d)
146 if (req->state != CONN_ERROR)
147 req->state = CONN_CLOSE;
156 if (req->timer != 0) {
157 ev_timer_cancel(req->timer);
161 if (req->state == CONN_CLOSE &&
163 bufio_close(&req->bio) == -1 &&
165 ev_add(req->fd, req_bio_ev(req), close_conn, req);
170 freeaddrinfo(req->servinfo);
172 bufio_free(&req->bio);
174 if (req->ccert != NULL) {
175 munmap(req->ccert, req->ccert_len);
176 close(req->ccert_fd);
183 TAILQ_REMOVE(&reqhead, req, reqs);
192 close_with_err(struct req *req, const char *err)
194 req->state = CONN_ERROR;
195 net_send_ui(IMSG_ERR, req->id, err, strlen(err)+1);
196 close_conn(0, 0, req);
200 close_with_errf(struct req *req, const char *fmt, ...)
206 if (vasprintf(&s, fmt, ap) == -1)
210 close_with_err(req, s);
216 req_resolve(int fd, int ev, void *d)
219 struct addrinfo hints;
220 struct asr_result ar;
223 if (req->q == NULL) {
224 memset(&hints, 0, sizeof(hints));
225 hints.ai_family = AF_UNSPEC;
226 hints.ai_socktype = SOCK_STREAM;
228 req->q = getaddrinfo_async(req->host, req->port, &hints, NULL);
229 if (req->q == NULL) {
230 close_with_errf(req, "getaddrinfo_async: %s",
239 ev_timer_cancel(req->timer);
243 if (asr_run(req->q, &ar) == 0) {
245 if (ar.ar_cond & ASR_WANT_READ)
247 if (ar.ar_cond & ASR_WANT_WRITE)
250 req->ar_fd = ar.ar_fd;
251 if (ev_add(req->ar_fd, ev, req_resolve, req) == -1) {
252 close_with_errf(req, "ev_add failure: %s",
257 tv.tv_sec = ar.ar_timeout / 1000;
258 tv.tv_usec = (ar.ar_timeout % 1000) * 1000;
259 req->timer = ev_timer(&tv, req_resolve, req);
261 close_with_errf(req, "ev_timer failure: %s",
269 if (ar.ar_gai_errno) {
270 close_with_errf(req, "failed to resolve %s: %s",
271 req->host, gai_strerror(ar.ar_gai_errno));
275 req->servinfo = ar.ar_addrinfo;
277 req->p = req->servinfo;
278 net_ev(-1, EV_READ, req);
282 req_resolve(int fd, int ev, struct req *req)
284 struct addrinfo hints;
287 memset(&hints, 0, sizeof(hints));
288 hints.ai_family = AF_UNSPEC;
289 hints.ai_socktype = SOCK_STREAM;
291 s = getaddrinfo(req->host, req->port, &hints, &req->servinfo);
293 close_with_errf(req, "failed to resolve %s: %s",
294 req->host, gai_strerror(s));
299 req->p = req->servinfo;
300 net_ev(-1, EV_READ, req);
305 try_to_connect(struct req *req)
308 socklen_t len = sizeof(error);
315 if (getsockopt(req->fd, SOL_SOCKET, SO_ERROR, &error,
317 req->conn_error = errno;
318 req->cause = "getsockopt";
322 if (error == 0) /* connected */
325 req->conn_error = error;
326 req->cause = "connect";
329 req->p = req->p->ai_next;
333 req->fd = socket(req->p->ai_family, req->p->ai_socktype,
334 req->p->ai_protocol);
336 req->conn_error = errno;
337 req->cause = "socket";
338 req->p = req->p->ai_next;
342 if (!mark_nonblock_cloexec(req->fd)) {
343 req->conn_error = errno;
344 req->cause = "setsockopt";
348 if (connect(req->fd, req->p->ai_addr, req->p->ai_addrlen) == 0)
355 gemini_parse_reply(struct req *req, const char *header, size_t len)
364 if (!isdigit(header[0]) || !isdigit(header[1]))
367 code = (header[0] - '0')*10 + (header[1] - '0');
368 if (header[2] != ' ')
374 if ((ibuf = imsg_create(&iev_ui->ibuf, IMSG_REPLY, req->id, 0,
375 sizeof(code) + len)) == NULL)
377 if (imsg_add(ibuf, &code, sizeof(code)) == -1 ||
378 imsg_add(ibuf, t, len) == -1)
380 imsg_close(&iev_ui->ibuf, ibuf);
381 imsg_event_add(iev_ui);
383 /* pause until we've told go go ahead */
390 net_send_req(struct req *req)
392 return (bufio_compose(&req->bio, req->req, req->len));
396 net_ev(int fd, int ev, void *d)
398 static char buf[4096];
406 if (ev == EV_TIMEOUT) {
407 close_with_err(req, "Timeout loading page");
411 if (req->state == CONN_CONNECTING) {
413 if (try_to_connect(req) == -1) {
414 if (req->fd != -1 && errno == EAGAIN) {
415 ev_add(req->fd, EV_WRITE, net_ev, req);
418 close_with_errf(req, "failed to connect to %s"
419 " (%s: %s)", req->host, req->cause,
420 strerror(req->conn_error));
424 bufio_set_fd(&req->bio, req->fd);
426 switch (req->proto) {
429 /* finger and gopher don't have a header nor TLS */
430 req->state = CONN_BODY;
431 if (net_send_req(req) == -1) {
432 close_with_err(req, "failed to send request");
437 req->state = CONN_HANDSHAKE;
438 if (bufio_starttls(&req->bio, req->host, 1,
439 req->ccert, req->ccert_len,
440 req->ccert, req->ccert_len) == -1) {
441 close_with_err(req, "failed to setup TLS");
444 req->timer = ev_timer(&timeout_for_handshake,
446 if (req->timer == 0) {
447 close_with_err(req, "failed to setup"
455 if (req->state == CONN_HANDSHAKE) {
456 if (bufio_handshake(&req->bio) == -1 && errno == EAGAIN) {
457 ev_add(req->fd, req_bio_ev(req), net_ev, req);
461 ev_timer_cancel(req->timer);
464 req->state = CONN_HEADER;
466 /* pause until we've told the certificate is OK */
469 hash = tls_peer_cert_hash(req->bio.ctx);
471 close_with_errf(req, "handshake failed: %s",
472 tls_error(req->bio.ctx));
476 net_send_ui(IMSG_CHECK_CERT, req->id, hash, strlen(hash)+1);
481 read = bufio_read(&req->bio);
482 if (read == -1 && errno != EAGAIN) {
483 close_with_errf(req, "Read error");
490 if ((ev & EV_WRITE) && bufio_write(&req->bio) == -1 &&
492 close_with_errf(req, "bufio_write: %s", strerror(errno));
496 if (req->state == CONN_HEADER) {
497 header = req->bio.rbuf.buf;
498 endl = memmem(header, req->bio.rbuf.len, "\r\n", 2);
499 if (endl == NULL && req->bio.rbuf.len >= 1024) {
500 close_with_err(req, "Invalid gemini reply (too long)");
503 if (endl == NULL && req->eof) {
504 close_with_err(req, "Invalid gemini reply.");
508 ev_add(req->fd, req_bio_ev(req), net_ev, req);
512 req->state = CONN_BODY;
513 r = gemini_parse_reply(req, header, strlen(header));
514 buf_drain(&req->bio.rbuf, endl - header + 2);
516 close_with_err(req, "Malformed gemini reply");
519 if (r < 20 || r >= 30)
520 close_conn(0, 0, req);
525 * Split data into chunks before sending. imsg can't handle
526 * message that are "too big".
529 if ((len = bufio_drain(&req->bio, buf, sizeof(buf))) == 0)
531 net_send_ui(IMSG_BUF, req->id, buf, len);
535 net_send_ui(IMSG_EOF, req->id, NULL, 0);
536 close_conn(0, 0, req);
540 ev_add(req->fd, req_bio_ev(req), net_ev, req);
544 load_cert(struct imsg *imsg, struct req *req)
549 if ((fd = imsg_get_fd(imsg)) == -1)
552 if (fstat(fd, &sb) == -1)
556 if (sb.st_size >= (off_t)SIZE_MAX) {
562 req->ccert = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
563 if (req->ccert == MAP_FAILED) {
569 req->ccert_len = sb.st_size;
576 handle_dispatch_imsg(int fd, int event, void *d)
578 struct imsgev *iev = d;
579 struct imsgbuf *ibuf = &iev->ibuf;
586 if (event & EV_READ) {
587 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
590 err(1, "connection closed");
592 if (event & EV_WRITE) {
593 if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
594 err(1, "msgbuf_write");
596 err(1, "connection closed");
600 if ((n = imsg_get(ibuf, &imsg)) == -1)
604 switch (imsg_get_type(&imsg)) {
606 if (imsg_get_data(&imsg, &r, sizeof(r)) == -1 ||
607 r.host[sizeof(r.host) - 1] != '\0' ||
608 r.port[sizeof(r.port) - 1] != '\0' ||
609 r.req[sizeof(r.req) - 1] != '\0')
611 if (r.proto != PROTO_FINGER &&
612 r.proto != PROTO_GEMINI &&
613 r.proto != PROTO_GOPHER)
616 if ((req = calloc(1, sizeof(*req))) == NULL)
624 req->id = imsg_get_id(&imsg);
625 TAILQ_INSERT_HEAD(&reqhead, req, reqs);
627 if ((req->host = strdup(r.host)) == NULL)
629 if ((req->port = strdup(r.port)) == NULL)
631 if ((req->req = strdup(r.req)) == NULL)
633 if (load_cert(&imsg, req) == -1)
635 if (bufio_init(&req->bio) == -1)
638 req->len = strlen(req->req);
639 req->proto = r.proto;
640 req_resolve(-1, 0, req);
643 case IMSG_CERT_STATUS:
644 if ((req = req_by_id(imsg_get_id(&imsg))) == NULL)
647 if (imsg_get_data(&imsg, &certok, sizeof(certok)) ==
651 close_conn(0, 0, req);
655 if (net_send_req(req) == -1) {
656 close_with_err(req, "failed to send request");
660 if (ev_add(req->fd, EV_WRITE, net_ev, req) == -1) {
662 "failed to register event.");
668 if ((req = req_by_id(imsg_get_id(&imsg))) == NULL)
670 ev_add(req->fd, EV_READ, net_ev, req);
671 net_ev(req->fd, 0, req);
675 if ((req = req_by_id(imsg_get_id(&imsg))) == NULL)
677 close_conn(0, 0, req);
686 errx(1, "got unknown imsg %d", imsg_get_type(&imsg));
696 net_send_ui(int type, uint32_t peerid, const void *data,
699 return imsg_compose_event(iev_ui, type, peerid, 0, -1,
708 TAILQ_INIT(&reqhead);
713 /* Setup pipe and event handler to the main process */
714 if ((iev_ui = malloc(sizeof(*iev_ui))) == NULL)
716 imsg_init(&iev_ui->ibuf, 3);
717 iev_ui->handler = handle_dispatch_imsg;
718 iev_ui->events = EV_READ;
719 ev_add(iev_ui->ibuf.fd, iev_ui->events, iev_ui->handler, iev_ui);
721 sandbox_net_process();
725 msgbuf_clear(&iev_ui->ibuf.w);
726 close(iev_ui->ibuf.fd);