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>
43 #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 *);
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)
361 if (!isdigit(header[0]) || !isdigit(header[1]))
364 code = (header[0] - '0')*10 + (header[1] - '0');
365 if (header[2] != ' ')
369 len = strlen(header) + 1;
371 if ((ibuf = imsg_create(&iev_ui->ibuf, IMSG_REPLY, req->id, 0,
372 sizeof(code) + len)) == NULL)
374 if (imsg_add(ibuf, &code, sizeof(code)) == -1 ||
375 imsg_add(ibuf, header, len) == -1)
377 imsg_close(&iev_ui->ibuf, ibuf);
378 imsg_event_add(iev_ui);
383 net_send_req(struct req *req)
385 return (bufio_compose(&req->bio, req->req, req->len));
389 net_ev(int fd, int ev, void *d)
391 static char buf[4096];
399 if (ev == EV_TIMEOUT) {
400 close_with_err(req, "Timeout loading page");
404 if (req->state == CONN_CONNECTING) {
406 if (try_to_connect(req) == -1) {
407 if (req->fd != -1 && errno == EAGAIN) {
408 ev_add(req->fd, EV_WRITE, net_ev, req);
411 close_with_errf(req, "failed to connect to %s"
412 " (%s: %s)", req->host, req->cause,
413 strerror(req->conn_error));
417 bufio_set_fd(&req->bio, req->fd);
419 switch (req->proto) {
422 /* finger and gopher don't have a header nor TLS */
423 req->state = CONN_BODY;
424 if (net_send_req(req) == -1) {
425 close_with_err(req, "failed to send request");
430 req->state = CONN_HANDSHAKE;
431 if (bufio_starttls(&req->bio, req->host, 1,
432 req->ccert, req->ccert_len,
433 req->ccert, req->ccert_len) == -1) {
434 close_with_err(req, "failed to setup TLS");
437 req->timer = ev_timer(&timeout_for_handshake,
439 if (req->timer == 0) {
440 close_with_err(req, "failed to setup"
448 if (req->state == CONN_HANDSHAKE) {
449 if (bufio_handshake(&req->bio) == -1 && errno == EAGAIN) {
450 ev_add(req->fd, req_bio_ev(req), net_ev, req);
454 ev_timer_cancel(req->timer);
457 req->state = CONN_HEADER;
459 /* pause until we've told the certificate is OK */
462 hash = tls_peer_cert_hash(req->bio.ctx);
464 close_with_errf(req, "handshake failed: %s",
465 tls_error(req->bio.ctx));
469 net_send_ui(IMSG_CHECK_CERT, req->id, hash, strlen(hash)+1);
474 read = bufio_read(&req->bio);
475 if (read == -1 && errno != EAGAIN) {
477 net_send_ui(IMSG_FAULTY_GEMSERVER, req->id, NULL, 0);
483 if ((ev & EV_WRITE) && bufio_write(&req->bio) == -1 &&
485 close_with_errf(req, "bufio_write: %s", strerror(errno));
489 if (req->state == CONN_HEADER) {
490 header = buf_getdelim(&req->bio.rbuf, "\r\n", &len);
491 if (header == NULL && req->bio.rbuf.len >= 1024) {
492 close_with_err(req, "Invalid gemini reply (too long)");
495 if (header == NULL && req->eof) {
496 close_with_err(req, "Invalid gemini reply.");
499 if (header == NULL) {
500 ev_add(req->fd, req_bio_ev(req), net_ev, req);
503 req->state = CONN_BODY;
504 if ((code = gemini_parse_reply(req, header)) == -1) {
505 close_with_err(req, "Malformed gemini reply");
508 if (code < 20 || code >= 30) {
509 close_conn(0, 0, req);
513 buf_drain(&req->bio.rbuf, len);
515 /* pause until we've been told to go ahead */
521 * Split data into chunks before sending. imsg can't handle
522 * message that are "too big".
525 if ((len = bufio_drain(&req->bio, buf, sizeof(buf))) == 0)
527 net_send_ui(IMSG_BUF, req->id, buf, len);
531 net_send_ui(IMSG_EOF, req->id, NULL, 0);
532 close_conn(0, 0, req);
536 ev_add(req->fd, req_bio_ev(req), net_ev, req);
540 load_cert(struct imsg *imsg, struct req *req)
545 if ((fd = imsg_get_fd(imsg)) == -1)
548 if (fstat(fd, &sb) == -1)
552 if (sb.st_size >= (off_t)SIZE_MAX) {
558 req->ccert = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
559 if (req->ccert == MAP_FAILED) {
565 req->ccert_len = sb.st_size;
572 handle_dispatch_imsg(int fd, int event, void *d)
574 struct imsgev *iev = d;
575 struct imsgbuf *ibuf = &iev->ibuf;
582 if (event & EV_READ) {
583 if (imsgbuf_read(ibuf) == -1) {
585 errx(1, "connection closed");
587 err(1, "imsg_read failure");
590 if (event & EV_WRITE) {
591 if (imsgbuf_write(ibuf) == -1 && errno != EAGAIN)
592 err(1, "msgbuf_write");
596 if ((n = imsg_get(ibuf, &imsg)) == -1)
600 switch (imsg_get_type(&imsg)) {
602 if (imsg_get_data(&imsg, &r, sizeof(r)) == -1 ||
603 r.host[sizeof(r.host) - 1] != '\0' ||
604 r.port[sizeof(r.port) - 1] != '\0' ||
605 r.req[sizeof(r.req) - 1] != '\0')
607 if (r.proto != PROTO_FINGER &&
608 r.proto != PROTO_GEMINI &&
609 r.proto != PROTO_GOPHER)
612 req = xcalloc(1, sizeof(*req));
619 req->id = imsg_get_id(&imsg);
620 TAILQ_INSERT_HEAD(&reqhead, req, reqs);
622 req->host = xstrdup(r.host);
623 req->port = xstrdup(r.port);
624 req->req = xstrdup(r.req);
625 if (load_cert(&imsg, req) == -1)
627 if (bufio_init(&req->bio) == -1)
630 req->len = strlen(req->req);
631 req->proto = r.proto;
632 req_resolve(-1, 0, req);
635 case IMSG_CERT_STATUS:
636 if ((req = req_by_id(imsg_get_id(&imsg))) == NULL)
639 if (imsg_get_data(&imsg, &certok, sizeof(certok)) ==
643 close_conn(0, 0, req);
647 if (net_send_req(req) == -1) {
648 close_with_err(req, "failed to send request");
652 if (ev_add(req->fd, EV_WRITE, net_ev, req) == -1) {
654 "failed to register event.");
660 if ((req = req_by_id(imsg_get_id(&imsg))) == NULL)
662 ev_add(req->fd, EV_READ, net_ev, req);
663 net_ev(req->fd, 0, req);
667 if ((req = req_by_id(imsg_get_id(&imsg))) == NULL)
669 close_conn(0, 0, req);
678 errx(1, "got unknown imsg %d", imsg_get_type(&imsg));
688 net_send_ui(int type, uint32_t peerid, const void *data,
691 return imsg_compose_event(iev_ui, type, peerid, 0, -1,
700 TAILQ_INIT(&reqhead);
705 /* Setup pipe and event handler to the main process */
706 iev_ui = xmalloc(sizeof(*iev_ui));
707 if (imsgbuf_init(&iev_ui->ibuf, 3) == -1)
708 err(1, "imsgbuf_init failed");
709 imsgbuf_allow_fdpass(&iev_ui->ibuf);
710 iev_ui->handler = handle_dispatch_imsg;
711 iev_ui->events = EV_READ;
712 ev_add(iev_ui->ibuf.fd, iev_ui->events, iev_ui->handler, iev_ui);
714 sandbox_net_process();
718 imsgbuf_clear(&iev_ui->ibuf);
719 close(iev_ui->ibuf.fd);