commit - f53b250bab68756757a8b1f8e2417e96334eaf87
commit + c8dba1e6a00cc0a1100ac09a6a9ac16084b44b04
blob - b2bc36548d7f01fbe23949106ef66dfa4f354b5e
blob + f4dece444925d5a3c688f7fb4eb0bf38af9e03a7
--- Makefile.am
+++ Makefile.am
defaults.h \
emoji-matcher.c \
fs.c \
- gemini.c \
gemtext.c \
gen-emoji-matcher.sh \
gencmd.awk \
mime.c \
minibuffer.c \
minibuffer.h \
+ net.c \
pages.c \
pages.h \
parse.y \
blob - 33b4183d849142c658414b8605b182c9be6c2e6e (mode 644)
blob + /dev/null
--- gemini.c
+++ /dev/null
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "compat.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <netinet/in.h>
-
-#include <assert.h>
-#include <ctype.h>
-#include <errno.h>
-#include <netdb.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <tls.h>
-#include <unistd.h>
-
-#if HAVE_ASR_RUN
-# include <asr.h>
-#endif
-
-#include "telescope.h"
-
-static struct imsgev *iev_ui;
-static struct tls_config *tlsconf;
-
-struct req;
-
-static void die(void) __attribute__((__noreturn__));
-
-static void try_to_connect(int, short, void*);
-
-#if HAVE_ASR_RUN
-static void query_done(struct asr_result*, void*);
-static void async_conn_towards(struct req*);
-#else
-static void blocking_conn_towards(struct req*);
-#endif
-
-static void close_with_err(struct req*, const char*);
-static void close_with_errf(struct req*, const char*, ...) __attribute__((format(printf, 2, 3)));
-static struct req *req_by_id(uint32_t);
-static struct req *req_by_id_try(uint32_t);
-
-static void setup_tls(struct req*);
-static void do_handshake(int, short, void*);
-static void write_request(int, short, void*);
-static void read_reply(int, short, void*);
-static void parse_reply(struct req*);
-static void copy_body(int, short, void*);
-
-static void handle_get_raw(struct imsg *, size_t);
-static void handle_cert_status(struct imsg*, size_t);
-static void handle_proceed(struct imsg*, size_t);
-static void handle_stop(struct imsg*, size_t);
-static void handle_quit(struct imsg*, size_t);
-static void handle_dispatch_imsg(int, short, void*);
-
-static int net_send_ui(int, uint32_t, const void *, uint16_t);
-
-/* TODO: making this customizable */
-struct timeval timeout_for_handshake = { 5, 0 };
-
-static imsg_handlerfn *handlers[] = {
- [IMSG_GET_RAW] = handle_get_raw,
- [IMSG_CERT_STATUS] = handle_cert_status,
- [IMSG_PROCEED] = handle_proceed,
- [IMSG_STOP] = handle_stop,
- [IMSG_QUIT] = handle_quit,
-};
-
-typedef void (*statefn)(int, short, void*);
-
-TAILQ_HEAD(, req) reqhead;
-/* a pending request */
-struct req {
- struct event ev;
- struct phos_uri url;
- uint32_t id;
- int fd;
- struct tls *ctx;
- char buf[1024];
- size_t off;
-
- struct addrinfo *servinfo, *p;
-#if HAVE_ASR_RUN
- struct addrinfo hints;
- struct event_asr *asrev;
-#endif
-
- TAILQ_ENTRY(req) reqs;
-};
-
-static inline void
-yield_r(struct req *req, statefn fn, struct timeval *tv)
-{
- event_once(req->fd, EV_READ, fn, req, tv);
-}
-
-static inline void
-yield_w(struct req *req, statefn fn, struct timeval *tv)
-{
- event_once(req->fd, EV_WRITE, fn, req, tv);
-}
-
-static inline void
-advance_buf(struct req *req, size_t len)
-{
- assert(len <= req->off);
-
- req->off -= len;
- memmove(req->buf, req->buf + len, req->off);
-}
-
-static void __attribute__((__noreturn__))
-die(void)
-{
- abort(); /* TODO */
-}
-
-static void
-try_to_connect(int fd, short ev, void *d)
-{
- struct req *req = d;
- int error = 0;
- socklen_t len = sizeof(error);
-
-again:
- if (req->p == NULL)
- goto err;
-
- if (req->fd != -1) {
- if (getsockopt(req->fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1)
- goto err;
- if (error != 0) {
- errno = error;
- goto err;
- }
- goto done;
- }
-
- req->fd = socket(req->p->ai_family, req->p->ai_socktype, req->p->ai_protocol);
- if (req->fd == -1) {
- req->p = req->p->ai_next;
- goto again;
- } else {
- mark_nonblock(req->fd);
- if (connect(req->fd, req->p->ai_addr, req->p->ai_addrlen) == 0)
- goto done;
- yield_w(req, try_to_connect, NULL);
- }
- return;
-
-err:
- freeaddrinfo(req->servinfo);
- close_with_errf(req, "failed to connect to %s",
- req->url.host);
- return;
-
-done:
- freeaddrinfo(req->servinfo);
- setup_tls(req);
-}
-
-#if HAVE_ASR_RUN
-static void
-query_done(struct asr_result *res, void *d)
-{
- struct req *req = d;
-
- req->asrev = NULL;
- if (res->ar_gai_errno != 0) {
- close_with_errf(req, "failed to resolve %s: %s",
- req->url.host, gai_strerror(res->ar_gai_errno));
- return;
- }
-
- req->fd = -1;
- req->servinfo = res->ar_addrinfo;
- req->p = res->ar_addrinfo;
- try_to_connect(0, 0, req);
-}
-
-static void
-async_conn_towards(struct req *req)
-{
- struct asr_query *q;
- const char *proto = "1965";
-
- if (*req->url.port != '\0')
- proto = req->url.port;
-
- req->hints.ai_family = AF_UNSPEC;
- req->hints.ai_socktype = SOCK_STREAM;
- q = getaddrinfo_async(req->url.host, proto, &req->hints, NULL);
- req->asrev = event_asr_run(q, query_done, req);
-}
-#else
-static void
-blocking_conn_towards(struct req *req)
-{
- struct addrinfo hints;
- struct phos_uri *url = &req->url;
- int status;
- const char *proto = "1965";
-
- if (*url->port != '\0')
- proto = url->port;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
-
- if ((status = getaddrinfo(url->host, proto, &hints, &req->servinfo))) {
- close_with_errf(req, "failed to resolve %s: %s",
- url->host, gai_strerror(status));
- return;
- }
-
- req->fd = -1;
- req->p = req->servinfo;
- try_to_connect(0, 0, req);
-}
-#endif
-
-static struct req *
-req_by_id(uint32_t id)
-{
- struct req *r;
-
- if ((r = req_by_id_try(id)) == NULL)
- die();
- return r;
-}
-
-static struct req *
-req_by_id_try(uint32_t id)
-{
- struct req *r;
-
- TAILQ_FOREACH(r, &reqhead, reqs) {
- if (r->id == id)
- return r;
- }
-
- return NULL;
-}
-
-static void
-close_conn(int fd, short ev, void *d)
-{
- struct req *req = d;
-
-#if HAVE_ASR_RUN
- if (req->asrev != NULL)
- event_asr_abort(req->asrev);
-#endif
-
- if (req->ctx != NULL) {
- switch (tls_close(req->ctx)) {
- case TLS_WANT_POLLIN:
- yield_r(req, close_conn, NULL);
- return;
- case TLS_WANT_POLLOUT:
- yield_w(req, close_conn, NULL);
- return;
- }
-
- tls_free(req->ctx);
- }
-
- TAILQ_REMOVE(&reqhead, req, reqs);
- if (req->fd != -1)
- close(req->fd);
- free(req);
-}
-
-static void
-close_with_err(struct req *req, const char *err)
-{
- net_send_ui(IMSG_ERR, req->id, err, strlen(err)+1);
- close_conn(0, 0, req);
-}
-
-static void
-close_with_errf(struct req *req, const char *fmt, ...)
-{
- va_list ap;
- char *s;
-
- va_start(ap, fmt);
- if (vasprintf(&s, fmt, ap) == -1)
- abort();
- va_end(ap);
-
- close_with_err(req, s);
- free(s);
-}
-
-static void
-setup_tls(struct req *req)
-{
- if ((req->ctx = tls_client()) == NULL) {
- close_with_errf(req, "tls_client: %s", strerror(errno));
- return;
- }
- if (tls_configure(req->ctx, tlsconf) == -1) {
- close_with_errf(req, "tls_configure: %s", tls_error(req->ctx));
- return;
- }
- if (tls_connect_socket(req->ctx, req->fd, req->url.host) == -1) {
- close_with_errf(req, "tls_connect_socket: %s", tls_error(req->ctx));
- return;
- }
- yield_w(req, do_handshake, &timeout_for_handshake);
-}
-
-static void
-do_handshake(int fd, short ev, void *d)
-{
- struct req *req = d;
- const char *hash;
-
- if (ev == EV_TIMEOUT) {
- close_with_err(req, "Timeout loading page");
- return;
- }
-
- switch (tls_handshake(req->ctx)) {
- case TLS_WANT_POLLIN:
- yield_r(req, do_handshake, NULL);
- return;
- case TLS_WANT_POLLOUT:
- yield_w(req, do_handshake, NULL);
- return;
- }
-
- hash = tls_peer_cert_hash(req->ctx);
- if (hash == NULL) {
- close_with_errf(req, "handshake failed: %s", tls_error(req->ctx));
- return;
- }
- net_send_ui(IMSG_CHECK_CERT, req->id, hash, strlen(hash)+1);
-}
-
-static void
-write_request(int fd, short ev, void *d)
-{
- struct req *req = d;
- ssize_t r;
- size_t len;
-
- len = strlen(req->buf);
-
- switch (r = tls_write(req->ctx, req->buf, len)) {
- case -1:
- close_with_errf(req, "tls_write: %s", tls_error(req->ctx));
- break;
- case TLS_WANT_POLLIN:
- yield_r(req, write_request, NULL);
- break;
- case TLS_WANT_POLLOUT:
- yield_w(req, write_request, NULL);
- break;
- default:
- /* assume r == len */
- (void)r;
- yield_r(req, read_reply, NULL);
- break;
- }
-}
-
-static void
-read_reply(int fd, short ev, void *d)
-{
- struct req *req = d;
- size_t len;
- ssize_t r;
- char *buf;
-
- buf = req->buf + req->off;
- len = sizeof(req->buf) - req->off;
-
- switch (r = tls_read(req->ctx, buf, len)) {
- case -1:
- close_with_errf(req, "tls_read: %s", tls_error(req->ctx));
- break;
- case TLS_WANT_POLLIN:
- yield_r(req, read_reply, NULL);
- break;
- case TLS_WANT_POLLOUT:
- yield_w(req, read_reply, NULL);
- break;
- default:
- req->off += r;
-
- if (memmem(req->buf, req->off, "\r\n", 2) != NULL)
- parse_reply(req);
- else if (req->off == sizeof(req->buf))
- close_with_err(req, "invalid response");
- else
- yield_r(req, read_reply, NULL);
- break;
- }
-}
-
-static void
-parse_reply(struct req *req)
-{
- int code;
- char *e;
- size_t len;
-
- if (req->off < 4)
- goto err;
-
- if (!isdigit(req->buf[0]) || !isdigit(req->buf[1]))
- goto err;
-
- code = (req->buf[0] - '0')*10 + (req->buf[1] - '0');
-
- if (!isspace(req->buf[2]))
- goto err;
-
- advance_buf(req, 3);
- if ((e = memmem(req->buf, req->off, "\r\n", 2)) == NULL)
- goto err;
-
- *e = '\0';
- e++;
- len = e - req->buf;
- net_send_ui(IMSG_GOT_CODE, req->id, &code, sizeof(code));
- net_send_ui(IMSG_GOT_META, req->id, req->buf, len);
-
- if (20 <= code && code < 30)
- advance_buf(req, len+1); /* skip \n too */
- else
- close_conn(0, 0, req);
-
- return;
-
-err:
- close_with_err(req, "malformed request");
-}
-
-static void
-copy_body(int fd, short ev, void *d)
-{
- struct req *req = d;
- ssize_t r;
-
- for (;;) {
- if (req->off != 0) {
- net_send_ui(IMSG_BUF, req->id,
- req->buf, req->off);
- req->off = 0;
- }
-
- switch (r = tls_read(req->ctx, req->buf, sizeof(req->buf))) {
- case TLS_WANT_POLLIN:
- yield_r(req, copy_body, NULL);
- return;
- case TLS_WANT_POLLOUT:
- yield_w(req, copy_body, NULL);
- return;
- case -1:
- /*
- * XXX: should notify the user that the
- * download was aborted.
- */
- /* fallthrough */
- case 0:
- net_send_ui(IMSG_EOF, req->id, NULL, 0);
- close_conn(0, 0, req);
- return;
- default:
- req->off = r;
- }
- }
-}
-
-static void
-handle_get_raw(struct imsg *imsg, size_t datalen)
-{
- struct req *req;
- struct get_req *r;
-
- r = imsg->data;
-
- if (datalen != sizeof(*r))
- die();
-
- if ((req = calloc(1, sizeof(*req))) == NULL)
- die();
-
- req->id = imsg->hdr.peerid;
- TAILQ_INSERT_HEAD(&reqhead, req, reqs);
-
- strlcpy(req->url.host, r->host, sizeof(req->url.host));
- strlcpy(req->url.port, r->port, sizeof(req->url.port));
- strlcpy(req->buf, r->req, sizeof(req->buf));
-
-#if HAVE_ASR_RUN
- async_conn_towards(req);
-#else
- blocking_conn_towards(req);
-#endif
-}
-
-static void
-handle_cert_status(struct imsg *imsg, size_t datalen)
-{
- struct req *req;
- int is_ok;
-
- req = req_by_id(imsg->hdr.peerid);
-
- if (datalen < sizeof(is_ok))
- die();
- memcpy(&is_ok, imsg->data, sizeof(is_ok));
-
- if (is_ok)
- yield_w(req, write_request, NULL);
- else
- close_conn(0, 0, req);
-}
-
-static void
-handle_proceed(struct imsg *imsg, size_t datalen)
-{
- yield_r(req_by_id(imsg->hdr.peerid),
- copy_body, NULL);
-}
-
-static void
-handle_stop(struct imsg *imsg, size_t datalen)
-{
- struct req *req;
-
- if ((req = req_by_id_try(imsg->hdr.peerid)) == NULL)
- return;
- close_conn(0, 0, req);
-}
-
-static void
-handle_quit(struct imsg *imsg, size_t datalen)
-{
- event_loopbreak();
-}
-
-static void
-handle_dispatch_imsg(int fd, short ev, void *d)
-{
- struct imsgev *iev = d;
-
- if (dispatch_imsg(iev, ev, handlers, sizeof(handlers)) == -1)
- err(1, "connection closed");
-}
-
-static int
-net_send_ui(int type, uint32_t peerid, const void *data,
- uint16_t datalen)
-{
- return imsg_compose_event(iev_ui, type, peerid, 0, -1,
- data, datalen);
-}
-
-int
-client_main(void)
-{
- setproctitle("net");
-
- TAILQ_INIT(&reqhead);
-
- if ((tlsconf = tls_config_new()) == NULL)
- die();
- tls_config_insecure_noverifycert(tlsconf);
- tls_config_insecure_noverifyname(tlsconf);
-
- event_init();
-
- /* Setup pipe and event handler to the main process */
- if ((iev_ui = malloc(sizeof(*iev_ui))) == NULL)
- die();
- imsg_init(&iev_ui->ibuf, 3);
- iev_ui->handler = handle_dispatch_imsg;
- iev_ui->events = EV_READ;
- event_set(&iev_ui->ev, iev_ui->ibuf.fd, iev_ui->events,
- iev_ui->handler, iev_ui);
- event_add(&iev_ui->ev, NULL);
-
- sandbox_net_process();
-
- event_dispatch();
- return 0;
-}
blob - /dev/null
blob + 33b4183d849142c658414b8605b182c9be6c2e6e (mode 644)
--- /dev/null
+++ net.c
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "compat.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tls.h>
+#include <unistd.h>
+
+#if HAVE_ASR_RUN
+# include <asr.h>
+#endif
+
+#include "telescope.h"
+
+static struct imsgev *iev_ui;
+static struct tls_config *tlsconf;
+
+struct req;
+
+static void die(void) __attribute__((__noreturn__));
+
+static void try_to_connect(int, short, void*);
+
+#if HAVE_ASR_RUN
+static void query_done(struct asr_result*, void*);
+static void async_conn_towards(struct req*);
+#else
+static void blocking_conn_towards(struct req*);
+#endif
+
+static void close_with_err(struct req*, const char*);
+static void close_with_errf(struct req*, const char*, ...) __attribute__((format(printf, 2, 3)));
+static struct req *req_by_id(uint32_t);
+static struct req *req_by_id_try(uint32_t);
+
+static void setup_tls(struct req*);
+static void do_handshake(int, short, void*);
+static void write_request(int, short, void*);
+static void read_reply(int, short, void*);
+static void parse_reply(struct req*);
+static void copy_body(int, short, void*);
+
+static void handle_get_raw(struct imsg *, size_t);
+static void handle_cert_status(struct imsg*, size_t);
+static void handle_proceed(struct imsg*, size_t);
+static void handle_stop(struct imsg*, size_t);
+static void handle_quit(struct imsg*, size_t);
+static void handle_dispatch_imsg(int, short, void*);
+
+static int net_send_ui(int, uint32_t, const void *, uint16_t);
+
+/* TODO: making this customizable */
+struct timeval timeout_for_handshake = { 5, 0 };
+
+static imsg_handlerfn *handlers[] = {
+ [IMSG_GET_RAW] = handle_get_raw,
+ [IMSG_CERT_STATUS] = handle_cert_status,
+ [IMSG_PROCEED] = handle_proceed,
+ [IMSG_STOP] = handle_stop,
+ [IMSG_QUIT] = handle_quit,
+};
+
+typedef void (*statefn)(int, short, void*);
+
+TAILQ_HEAD(, req) reqhead;
+/* a pending request */
+struct req {
+ struct event ev;
+ struct phos_uri url;
+ uint32_t id;
+ int fd;
+ struct tls *ctx;
+ char buf[1024];
+ size_t off;
+
+ struct addrinfo *servinfo, *p;
+#if HAVE_ASR_RUN
+ struct addrinfo hints;
+ struct event_asr *asrev;
+#endif
+
+ TAILQ_ENTRY(req) reqs;
+};
+
+static inline void
+yield_r(struct req *req, statefn fn, struct timeval *tv)
+{
+ event_once(req->fd, EV_READ, fn, req, tv);
+}
+
+static inline void
+yield_w(struct req *req, statefn fn, struct timeval *tv)
+{
+ event_once(req->fd, EV_WRITE, fn, req, tv);
+}
+
+static inline void
+advance_buf(struct req *req, size_t len)
+{
+ assert(len <= req->off);
+
+ req->off -= len;
+ memmove(req->buf, req->buf + len, req->off);
+}
+
+static void __attribute__((__noreturn__))
+die(void)
+{
+ abort(); /* TODO */
+}
+
+static void
+try_to_connect(int fd, short ev, void *d)
+{
+ struct req *req = d;
+ int error = 0;
+ socklen_t len = sizeof(error);
+
+again:
+ if (req->p == NULL)
+ goto err;
+
+ if (req->fd != -1) {
+ if (getsockopt(req->fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1)
+ goto err;
+ if (error != 0) {
+ errno = error;
+ goto err;
+ }
+ goto done;
+ }
+
+ req->fd = socket(req->p->ai_family, req->p->ai_socktype, req->p->ai_protocol);
+ if (req->fd == -1) {
+ req->p = req->p->ai_next;
+ goto again;
+ } else {
+ mark_nonblock(req->fd);
+ if (connect(req->fd, req->p->ai_addr, req->p->ai_addrlen) == 0)
+ goto done;
+ yield_w(req, try_to_connect, NULL);
+ }
+ return;
+
+err:
+ freeaddrinfo(req->servinfo);
+ close_with_errf(req, "failed to connect to %s",
+ req->url.host);
+ return;
+
+done:
+ freeaddrinfo(req->servinfo);
+ setup_tls(req);
+}
+
+#if HAVE_ASR_RUN
+static void
+query_done(struct asr_result *res, void *d)
+{
+ struct req *req = d;
+
+ req->asrev = NULL;
+ if (res->ar_gai_errno != 0) {
+ close_with_errf(req, "failed to resolve %s: %s",
+ req->url.host, gai_strerror(res->ar_gai_errno));
+ return;
+ }
+
+ req->fd = -1;
+ req->servinfo = res->ar_addrinfo;
+ req->p = res->ar_addrinfo;
+ try_to_connect(0, 0, req);
+}
+
+static void
+async_conn_towards(struct req *req)
+{
+ struct asr_query *q;
+ const char *proto = "1965";
+
+ if (*req->url.port != '\0')
+ proto = req->url.port;
+
+ req->hints.ai_family = AF_UNSPEC;
+ req->hints.ai_socktype = SOCK_STREAM;
+ q = getaddrinfo_async(req->url.host, proto, &req->hints, NULL);
+ req->asrev = event_asr_run(q, query_done, req);
+}
+#else
+static void
+blocking_conn_towards(struct req *req)
+{
+ struct addrinfo hints;
+ struct phos_uri *url = &req->url;
+ int status;
+ const char *proto = "1965";
+
+ if (*url->port != '\0')
+ proto = url->port;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ if ((status = getaddrinfo(url->host, proto, &hints, &req->servinfo))) {
+ close_with_errf(req, "failed to resolve %s: %s",
+ url->host, gai_strerror(status));
+ return;
+ }
+
+ req->fd = -1;
+ req->p = req->servinfo;
+ try_to_connect(0, 0, req);
+}
+#endif
+
+static struct req *
+req_by_id(uint32_t id)
+{
+ struct req *r;
+
+ if ((r = req_by_id_try(id)) == NULL)
+ die();
+ return r;
+}
+
+static struct req *
+req_by_id_try(uint32_t id)
+{
+ struct req *r;
+
+ TAILQ_FOREACH(r, &reqhead, reqs) {
+ if (r->id == id)
+ return r;
+ }
+
+ return NULL;
+}
+
+static void
+close_conn(int fd, short ev, void *d)
+{
+ struct req *req = d;
+
+#if HAVE_ASR_RUN
+ if (req->asrev != NULL)
+ event_asr_abort(req->asrev);
+#endif
+
+ if (req->ctx != NULL) {
+ switch (tls_close(req->ctx)) {
+ case TLS_WANT_POLLIN:
+ yield_r(req, close_conn, NULL);
+ return;
+ case TLS_WANT_POLLOUT:
+ yield_w(req, close_conn, NULL);
+ return;
+ }
+
+ tls_free(req->ctx);
+ }
+
+ TAILQ_REMOVE(&reqhead, req, reqs);
+ if (req->fd != -1)
+ close(req->fd);
+ free(req);
+}
+
+static void
+close_with_err(struct req *req, const char *err)
+{
+ net_send_ui(IMSG_ERR, req->id, err, strlen(err)+1);
+ close_conn(0, 0, req);
+}
+
+static void
+close_with_errf(struct req *req, const char *fmt, ...)
+{
+ va_list ap;
+ char *s;
+
+ va_start(ap, fmt);
+ if (vasprintf(&s, fmt, ap) == -1)
+ abort();
+ va_end(ap);
+
+ close_with_err(req, s);
+ free(s);
+}
+
+static void
+setup_tls(struct req *req)
+{
+ if ((req->ctx = tls_client()) == NULL) {
+ close_with_errf(req, "tls_client: %s", strerror(errno));
+ return;
+ }
+ if (tls_configure(req->ctx, tlsconf) == -1) {
+ close_with_errf(req, "tls_configure: %s", tls_error(req->ctx));
+ return;
+ }
+ if (tls_connect_socket(req->ctx, req->fd, req->url.host) == -1) {
+ close_with_errf(req, "tls_connect_socket: %s", tls_error(req->ctx));
+ return;
+ }
+ yield_w(req, do_handshake, &timeout_for_handshake);
+}
+
+static void
+do_handshake(int fd, short ev, void *d)
+{
+ struct req *req = d;
+ const char *hash;
+
+ if (ev == EV_TIMEOUT) {
+ close_with_err(req, "Timeout loading page");
+ return;
+ }
+
+ switch (tls_handshake(req->ctx)) {
+ case TLS_WANT_POLLIN:
+ yield_r(req, do_handshake, NULL);
+ return;
+ case TLS_WANT_POLLOUT:
+ yield_w(req, do_handshake, NULL);
+ return;
+ }
+
+ hash = tls_peer_cert_hash(req->ctx);
+ if (hash == NULL) {
+ close_with_errf(req, "handshake failed: %s", tls_error(req->ctx));
+ return;
+ }
+ net_send_ui(IMSG_CHECK_CERT, req->id, hash, strlen(hash)+1);
+}
+
+static void
+write_request(int fd, short ev, void *d)
+{
+ struct req *req = d;
+ ssize_t r;
+ size_t len;
+
+ len = strlen(req->buf);
+
+ switch (r = tls_write(req->ctx, req->buf, len)) {
+ case -1:
+ close_with_errf(req, "tls_write: %s", tls_error(req->ctx));
+ break;
+ case TLS_WANT_POLLIN:
+ yield_r(req, write_request, NULL);
+ break;
+ case TLS_WANT_POLLOUT:
+ yield_w(req, write_request, NULL);
+ break;
+ default:
+ /* assume r == len */
+ (void)r;
+ yield_r(req, read_reply, NULL);
+ break;
+ }
+}
+
+static void
+read_reply(int fd, short ev, void *d)
+{
+ struct req *req = d;
+ size_t len;
+ ssize_t r;
+ char *buf;
+
+ buf = req->buf + req->off;
+ len = sizeof(req->buf) - req->off;
+
+ switch (r = tls_read(req->ctx, buf, len)) {
+ case -1:
+ close_with_errf(req, "tls_read: %s", tls_error(req->ctx));
+ break;
+ case TLS_WANT_POLLIN:
+ yield_r(req, read_reply, NULL);
+ break;
+ case TLS_WANT_POLLOUT:
+ yield_w(req, read_reply, NULL);
+ break;
+ default:
+ req->off += r;
+
+ if (memmem(req->buf, req->off, "\r\n", 2) != NULL)
+ parse_reply(req);
+ else if (req->off == sizeof(req->buf))
+ close_with_err(req, "invalid response");
+ else
+ yield_r(req, read_reply, NULL);
+ break;
+ }
+}
+
+static void
+parse_reply(struct req *req)
+{
+ int code;
+ char *e;
+ size_t len;
+
+ if (req->off < 4)
+ goto err;
+
+ if (!isdigit(req->buf[0]) || !isdigit(req->buf[1]))
+ goto err;
+
+ code = (req->buf[0] - '0')*10 + (req->buf[1] - '0');
+
+ if (!isspace(req->buf[2]))
+ goto err;
+
+ advance_buf(req, 3);
+ if ((e = memmem(req->buf, req->off, "\r\n", 2)) == NULL)
+ goto err;
+
+ *e = '\0';
+ e++;
+ len = e - req->buf;
+ net_send_ui(IMSG_GOT_CODE, req->id, &code, sizeof(code));
+ net_send_ui(IMSG_GOT_META, req->id, req->buf, len);
+
+ if (20 <= code && code < 30)
+ advance_buf(req, len+1); /* skip \n too */
+ else
+ close_conn(0, 0, req);
+
+ return;
+
+err:
+ close_with_err(req, "malformed request");
+}
+
+static void
+copy_body(int fd, short ev, void *d)
+{
+ struct req *req = d;
+ ssize_t r;
+
+ for (;;) {
+ if (req->off != 0) {
+ net_send_ui(IMSG_BUF, req->id,
+ req->buf, req->off);
+ req->off = 0;
+ }
+
+ switch (r = tls_read(req->ctx, req->buf, sizeof(req->buf))) {
+ case TLS_WANT_POLLIN:
+ yield_r(req, copy_body, NULL);
+ return;
+ case TLS_WANT_POLLOUT:
+ yield_w(req, copy_body, NULL);
+ return;
+ case -1:
+ /*
+ * XXX: should notify the user that the
+ * download was aborted.
+ */
+ /* fallthrough */
+ case 0:
+ net_send_ui(IMSG_EOF, req->id, NULL, 0);
+ close_conn(0, 0, req);
+ return;
+ default:
+ req->off = r;
+ }
+ }
+}
+
+static void
+handle_get_raw(struct imsg *imsg, size_t datalen)
+{
+ struct req *req;
+ struct get_req *r;
+
+ r = imsg->data;
+
+ if (datalen != sizeof(*r))
+ die();
+
+ if ((req = calloc(1, sizeof(*req))) == NULL)
+ die();
+
+ req->id = imsg->hdr.peerid;
+ TAILQ_INSERT_HEAD(&reqhead, req, reqs);
+
+ strlcpy(req->url.host, r->host, sizeof(req->url.host));
+ strlcpy(req->url.port, r->port, sizeof(req->url.port));
+ strlcpy(req->buf, r->req, sizeof(req->buf));
+
+#if HAVE_ASR_RUN
+ async_conn_towards(req);
+#else
+ blocking_conn_towards(req);
+#endif
+}
+
+static void
+handle_cert_status(struct imsg *imsg, size_t datalen)
+{
+ struct req *req;
+ int is_ok;
+
+ req = req_by_id(imsg->hdr.peerid);
+
+ if (datalen < sizeof(is_ok))
+ die();
+ memcpy(&is_ok, imsg->data, sizeof(is_ok));
+
+ if (is_ok)
+ yield_w(req, write_request, NULL);
+ else
+ close_conn(0, 0, req);
+}
+
+static void
+handle_proceed(struct imsg *imsg, size_t datalen)
+{
+ yield_r(req_by_id(imsg->hdr.peerid),
+ copy_body, NULL);
+}
+
+static void
+handle_stop(struct imsg *imsg, size_t datalen)
+{
+ struct req *req;
+
+ if ((req = req_by_id_try(imsg->hdr.peerid)) == NULL)
+ return;
+ close_conn(0, 0, req);
+}
+
+static void
+handle_quit(struct imsg *imsg, size_t datalen)
+{
+ event_loopbreak();
+}
+
+static void
+handle_dispatch_imsg(int fd, short ev, void *d)
+{
+ struct imsgev *iev = d;
+
+ if (dispatch_imsg(iev, ev, handlers, sizeof(handlers)) == -1)
+ err(1, "connection closed");
+}
+
+static int
+net_send_ui(int type, uint32_t peerid, const void *data,
+ uint16_t datalen)
+{
+ return imsg_compose_event(iev_ui, type, peerid, 0, -1,
+ data, datalen);
+}
+
+int
+client_main(void)
+{
+ setproctitle("net");
+
+ TAILQ_INIT(&reqhead);
+
+ if ((tlsconf = tls_config_new()) == NULL)
+ die();
+ tls_config_insecure_noverifycert(tlsconf);
+ tls_config_insecure_noverifyname(tlsconf);
+
+ event_init();
+
+ /* Setup pipe and event handler to the main process */
+ if ((iev_ui = malloc(sizeof(*iev_ui))) == NULL)
+ die();
+ imsg_init(&iev_ui->ibuf, 3);
+ iev_ui->handler = handle_dispatch_imsg;
+ iev_ui->events = EV_READ;
+ event_set(&iev_ui->ev, iev_ui->ibuf.fd, iev_ui->events,
+ iev_ui->handler, iev_ui);
+ event_add(&iev_ui->ev, NULL);
+
+ sandbox_net_process();
+
+ event_dispatch();
+ return 0;
+}
blob - 7df00153aef20574474487911253c30fa648bdf9
blob + 523e1125f6ed73f39f71ef7b356e4345e98fa671
--- telescope.h
+++ telescope.h
int last_time_crashed(void);
int lock_session(void);
int load_certs(struct ohash*);
-
-/* gemini.c */
-int client_main(void);
/* help.c */
void recompute_help(void);
/* mime.c */
int setup_parser_for(struct tab*);
+/* net.c */
+int client_main(void);
+
/* parse.y */
void parseconfig(const char *, int);