commit - f45bd2e3b9e02ae3ef0daf3766d36ba35ab3a56c
commit + 5b762f01d4f82c9f2568e1928a42abd87e488bc3
blob - 0ae6b7aa6cc3dc35916fbba6c3807ce8f3aaf58f
blob + 0d19ad3f1f800b015cfdfebcc8d7ad0b7951350d
--- net.c
+++ net.c
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 net_tls_handshake(int, short, void *);
+static void net_tls_readcb(int, short, void *);
+static void net_tls_writecb(int, short, void *);
+
+static int gemini_parse_reply(struct req *, const char *, size_t);
+
+static void net_ready(struct req *req);
+static void net_read(struct bufferevent *, void *);
+static void net_write(struct bufferevent *, void *);
+static void net_error(struct bufferevent *, 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);
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;
+ char req[1024];
+ size_t len;
+ int done_header;
+ struct bufferevent *bev;
struct addrinfo *servinfo, *p;
#if HAVE_ASR_RUN
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__))
event_asr_abort(req->asrev);
#endif
+ if (req->bev != NULL) {
+ bufferevent_free(req->bev);
+ req->bev = NULL;
+ }
+
if (req->ctx != NULL) {
switch (tls_close(req->ctx)) {
case TLS_WANT_POLLIN:
}
tls_free(req->ctx);
+ req->ctx = NULL;
}
TAILQ_REMOVE(&reqhead, req, reqs);
close_with_errf(req, "tls_connect_socket: %s", tls_error(req->ctx));
return;
}
- yield_w(req, do_handshake, &timeout_for_handshake);
+ yield_w(req, net_tls_handshake, &timeout_for_handshake);
}
static void
-do_handshake(int fd, short ev, void *d)
+net_tls_handshake(int fd, short event, void *d)
{
struct req *req = d;
const char *hash;
- if (ev == EV_TIMEOUT) {
+ if (event == 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);
+ yield_r(req, net_tls_handshake, NULL);
return;
case TLS_WANT_POLLOUT:
- yield_w(req, do_handshake, NULL);
+ yield_w(req, net_tls_handshake, NULL);
return;
}
}
static void
-write_request(int fd, short ev, void *d)
+net_tls_readcb(int fd, short event, void *d)
{
- struct req *req = d;
- ssize_t r;
- size_t len;
-
- len = strlen(req->buf);
+ struct bufferevent *bufev = d;
+ struct req *req = bufev->cbarg;
+ char buf[BUFSIZ];
+ int what = EVBUFFER_READ;
+ int howmuch = IBUF_READ_SIZE;
+ ssize_t ret;
+ size_t len;
- switch (r = tls_write(req->ctx, req->buf, len)) {
- case -1:
- close_with_errf(req, "tls_write: %s", tls_error(req->ctx));
- break;
+ if (event == EV_TIMEOUT) {
+ what |= EVBUFFER_TIMEOUT;
+ goto err;
+ }
+
+ if (bufev->wm_read.high != 0)
+ howmuch = MIN(sizeof(buf), bufev->wm_read.high);
+
+ switch (ret = tls_read(req->ctx, buf, howmuch)) {
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;
+ goto retry;
+ case -1:
+ what |= EVBUFFER_ERROR;
+ goto err;
}
-}
+ len = ret;
-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;
+ if (len == 0) {
+ what |= EVBUFFER_EOF;
+ goto err;
+ }
- 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;
+ if (evbuffer_add(bufev->input, buf, len) == -1) {
+ what |= EVBUFFER_ERROR;
+ goto err;
}
+
+ event_add(&bufev->ev_read, NULL);
+
+ len = EVBUFFER_LENGTH(bufev->input);
+ if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
+ return;
+
+ if (bufev->readcb != NULL)
+ (*bufev->readcb)(bufev, bufev->cbarg);
+ return;
+
+retry:
+ event_add(&bufev->ev_read, NULL);
+ return;
+
+err:
+ (*bufev->errorcb)(bufev, what, bufev->cbarg);
}
static void
-parse_reply(struct req *req)
+net_tls_writecb(int fd, short event, void *d)
{
- int code;
- char *e;
- size_t len;
+ struct bufferevent *bufev = d;
+ struct req *req = bufev->cbarg;
+ ssize_t ret;
+ size_t len;
+ short what = EVBUFFER_WRITE;
- if (req->off < 4)
+ if (event & EV_TIMEOUT) {
+ what |= EVBUFFER_TIMEOUT;
goto err;
+ }
- if (!isdigit(req->buf[0]) || !isdigit(req->buf[1]))
- goto err;
+ if (EVBUFFER_LENGTH(bufev->output) != 0) {
+ ret = tls_write(req->ctx, EVBUFFER_DATA(bufev->output),
+ EVBUFFER_LENGTH(bufev->output));
+ switch (ret) {
+ case TLS_WANT_POLLIN:
+ case TLS_WANT_POLLOUT:
+ goto retry;
+ case -1:
+ what |= EVBUFFER_ERROR;
+ goto err;
+ }
+ len = ret;
+ evbuffer_drain(bufev->output, len);
+ }
- code = (req->buf[0] - '0')*10 + (req->buf[1] - '0');
+ if (EVBUFFER_LENGTH(bufev->output) != 0)
+ event_add(&bufev->ev_write, NULL);
- 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 (bufev->writecb != NULL &&
+ EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
+ (*bufev->writecb)(bufev, bufev->cbarg);
+ return;
- if (20 <= code && code < 30)
- advance_buf(req, len+1); /* skip \n too */
- else
+retry:
+ event_add(&bufev->ev_write, NULL);
+ return;
+
+err:
+ (*bufev->errorcb)(bufev, what, bufev->cbarg);
+}
+
+static int
+gemini_parse_reply(struct req *req, const char *header, size_t len)
+{
+ int code;
+ const char *t;
+
+ if (len < 4)
+ return 0;
+
+ if (!isdigit(header[0]) || !isdigit(header[1]))
+ return 0;
+
+ code = (header[0] - '0')*10 + (header[1] - '0');
+ if (header[2] != ' ')
+ return 0;
+
+ t = header + 3;
+
+ net_send_ui(IMSG_GOT_CODE, req->id, &code, sizeof(code));
+ net_send_ui(IMSG_GOT_META, req->id, t, strlen(t)+1);
+
+ bufferevent_disable(req->bev, EV_READ|EV_WRITE);
+
+ if (code < 20 || code >= 30)
close_conn(0, 0, req);
+ return 1;
+}
+/* called when we're ready to read/write */
+static void
+net_ready(struct req *req)
+{
+ req->bev = bufferevent_new(req->fd, net_read, net_write, net_error,
+ req);
+ if (req->bev == NULL)
+ die();
+
+ /* setup tls i/o layer */
+ if (req->ctx != NULL) {
+ event_set(&req->bev->ev_read, req->fd, EV_READ,
+ net_tls_readcb, req->bev);
+ event_set(&req->bev->ev_write, req->fd, EV_WRITE,
+ net_tls_writecb, req->bev);
+ }
+
+ /* TODO: adjust watermarks */
+ bufferevent_setwatermark(req->bev, EV_WRITE, 1, 0);
+ bufferevent_setwatermark(req->bev, EV_READ, 1, 0);
+
+ bufferevent_enable(req->bev, EV_READ|EV_WRITE);
+
+ bufferevent_write(req->bev, req->req, req->len);
+}
+
+/* called after a read has been done */
+static void
+net_read(struct bufferevent *bev, void *d)
+{
+ struct req *req = d;
+ struct evbuffer *src = EVBUFFER_INPUT(bev);
+ void *data;
+ size_t len;
+ int r;
+ char *header;
+
+ if (!req->done_header) {
+ header = evbuffer_readln(src, &len, EVBUFFER_EOL_CRLF_STRICT);
+ if (header == NULL && EVBUFFER_LENGTH(src) >= 1024)
+ goto err;
+ if (header == NULL)
+ return;
+ r = gemini_parse_reply(req, header, len);
+ free(header);
+ if (!r)
+ goto err;
+ req->done_header = 1;
+ return;
+ }
+
+ if ((len = EVBUFFER_LENGTH(src)) == 0)
+ return;
+ data = EVBUFFER_DATA(src);
+ net_send_ui(IMSG_BUF, req->id, data, len);
+ evbuffer_drain(src, len);
return;
err:
- close_with_err(req, "malformed request");
+ (*bev->errorcb)(bev, EVBUFFER_READ, bev->cbarg);
}
+/* called after a write has been done */
static void
-copy_body(int fd, short ev, void *d)
+net_write(struct bufferevent *bev, void *d)
{
+ struct evbuffer *dst = EVBUFFER_OUTPUT(bev);
+
+ if (EVBUFFER_LENGTH(dst) == 0)
+ (*bev->errorcb)(bev, EVBUFFER_WRITE, bev->cbarg);
+}
+
+static void
+net_error(struct bufferevent *bev, short error, 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;
- }
+ if (error & EVBUFFER_TIMEOUT) {
+ close_with_err(req, "Timeout loading page");
+ return;
+ }
- 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;
- }
+ if (error & EVBUFFER_ERROR) {
+ close_with_err(req, "buffer event error");
+ return;
}
+
+ if (error & EVBUFFER_EOF) {
+ net_send_ui(IMSG_EOF, req->id, NULL, 0);
+ close_conn(0, 0, req);
+ return;
+ }
+
+ if (error & EVBUFFER_WRITE) {
+ /* finished sending request */
+ bufferevent_disable(bev, EV_WRITE);
+ return;
+ }
+
+ if (error & EVBUFFER_READ) {
+ close_with_err(req, "protocol error");
+ return;
+ }
+
+ close_with_errf(req, "unknown event error %x", error);
}
static void
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));
+ strlcpy(req->req, r->req, sizeof(req->req));
+ req->len = strlen(r->req);
+
#if HAVE_ASR_RUN
async_conn_towards(req);
#else
memcpy(&is_ok, imsg->data, sizeof(is_ok));
if (is_ok)
- yield_w(req, write_request, NULL);
+ net_ready(req);
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);
+ struct req *req;
+
+ if ((req = req_by_id_try(imsg->hdr.peerid)) == NULL)
+ return;
+
+ bufferevent_enable(req->bev, EV_READ);
}
static void