aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOmar Polo <op@omarpolo.com>2021-12-29 20:36:54 +0000
committerOmar Polo <op@omarpolo.com>2021-12-29 20:36:54 +0000
commit72b033ef18ae3f82922f6f11ce0f5194e95f667d (patch)
tree5f06b0c70851aa17f17251579adb65a66a8081ca
parent054387bb26e75cef12e8dc0f531e7ee42614edd7 (diff)
add ability to proxy requests
Add to gmid the ability to forwad a request to another gemini server and thus acting like a reverse proxy. The current syntax for the config file is server "example.com" { ... proxy relay-to host:port } Further options (like the use of custom certificates) are planned. cf. github issue #7
-rw-r--r--Makefile2
-rw-r--r--ex.c58
-rw-r--r--gmid.c2
-rw-r--r--gmid.h26
-rw-r--r--parse.y24
-rw-r--r--proxy.c318
-rw-r--r--server.c112
7 files changed, 538 insertions, 4 deletions
diff --git a/Makefile b/Makefile
index fd02c84..bf7836c 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,7 @@ y.tab.c: parse.y
${YACC} -b y parse.y
SRCS = gmid.c iri.c utf8.c ex.c server.c sandbox.c mime.c puny.c \
- utils.c log.c dirs.c fcgi.c
+ utils.c log.c dirs.c fcgi.c proxy.c
OBJS = ${SRCS:.c=.o} y.tab.o ${COMPAT}
gmid: ${OBJS}
diff --git a/ex.c b/ex.c
index ad21cd5..425c7fe 100644
--- a/ex.c
+++ b/ex.c
@@ -31,12 +31,14 @@
static void handle_imsg_cgi_req(struct imsgbuf*, struct imsg*, size_t);
static void handle_imsg_fcgi_req(struct imsgbuf*, struct imsg*, size_t);
+static void handle_imsg_conn_req(struct imsgbuf *, struct imsg *, size_t);
static void handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t);
static void handle_dispatch_imsg(int, short, void*);
static imsg_handlerfn *handlers[] = {
[IMSG_FCGI_REQ] = handle_imsg_fcgi_req,
[IMSG_CGI_REQ] = handle_imsg_cgi_req,
+ [IMSG_CONN_REQ] = handle_imsg_conn_req,
[IMSG_QUIT] = handle_imsg_quit,
};
@@ -425,6 +427,62 @@ handle_imsg_fcgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
}
static void
+handle_imsg_conn_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
+{
+ struct addrinfo hints, *res, *res0;
+ struct connreq req;
+ int r, sock;
+
+ if (datalen != sizeof(req))
+ abort();
+ memcpy(&req, imsg->data, sizeof(req));
+ req.flag = 0;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ /* XXX: do this asynchronously if possible */
+ r = getaddrinfo(req.host, req.port, &hints, &res0);
+ if (r != 0) {
+ log_warn(NULL, "getaddrinfo(\"%s\", \"%s\"): %s",
+ req.host, req.port, gai_strerror(r));
+ goto err;
+ }
+
+ for (res = res0; res; res = res->ai_next) {
+ sock = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ if (sock == -1)
+ continue;
+
+ if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
+ close(sock);
+ sock = -1;
+ continue;
+ }
+
+ break;
+ }
+
+ freeaddrinfo(res0);
+
+ if (sock == -1) {
+ log_warn(NULL, "can't connect to %s:%s", req.host,
+ req.port);
+ goto err;
+ }
+
+ imsg_compose(ibuf, IMSG_CONN_FD, imsg->hdr.peerid, 0, sock, NULL, 0);
+ imsg_flush(ibuf);
+ return;
+
+err:
+ imsg_compose(ibuf, IMSG_CONN_FD, imsg->hdr.peerid, 0, -1, NULL, 0);
+ imsg_flush(ibuf);
+}
+
+static void
handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
{
int i;
diff --git a/gmid.c b/gmid.c
index e0c777a..7c2c7bd 100644
--- a/gmid.c
+++ b/gmid.c
@@ -302,6 +302,8 @@ free_config(void)
free((char*)l->block_fmt);
free((char*)l->dir);
+ free(l->proxy_host);
+
if (l->dirfd != -1)
close(l->dirfd);
diff --git a/gmid.h b/gmid.h
index a92665c..6798800 100644
--- a/gmid.h
+++ b/gmid.h
@@ -59,6 +59,7 @@
#define TEMP_REDIRECT 30
#define TEMP_FAILURE 40
#define CGI_ERROR 42
+#define PROXY_ERROR 43
#define NOT_FOUND 51
#define PROXY_REFUSED 53
#define BAD_REQUEST 59
@@ -110,6 +111,9 @@ struct location {
int disable_log;
int fcgi;
+ char *proxy_host;
+ const char *proxy_port;
+
const char *dir;
int dirfd;
@@ -193,10 +197,14 @@ enum {
REQUEST_DIR,
REQUEST_CGI,
REQUEST_FCGI,
+ REQUEST_PROXY,
REQUEST_DONE,
};
-#define IS_INTERNAL_REQUEST(x) ((x) != REQUEST_CGI && (x) != REQUEST_FCGI)
+#define IS_INTERNAL_REQUEST(x) \
+ ((x) != REQUEST_CGI && \
+ (x) != REQUEST_FCGI && \
+ (x) != REQUEST_PROXY)
struct client {
uint32_t id;
@@ -211,6 +219,10 @@ struct client {
struct bufferevent *cgibev;
+ struct bufferevent *proxybev;
+ struct tls *proxyctx;
+ struct event proxyev;
+
char *header;
int code;
@@ -262,6 +274,12 @@ struct cgireq {
size_t loc_off;
};
+struct connreq {
+ char host[NI_MAXHOST];
+ char port[NI_MAXSERV];
+ int flag;
+};
+
enum {
FILE_EXISTS,
FILE_EXECUTABLE,
@@ -274,6 +292,8 @@ enum imsg_type {
IMSG_CGI_RES,
IMSG_FCGI_REQ,
IMSG_FCGI_FD,
+ IMSG_CONN_REQ,
+ IMSG_CONN_FD,
IMSG_LOG,
IMSG_LOG_REQUEST,
IMSG_LOG_TYPE,
@@ -322,6 +342,7 @@ const char *vhost_default_mime(struct vhost*, const char*);
const char *vhost_index(struct vhost*, const char*);
int vhost_auto_index(struct vhost*, const char*);
int vhost_block_return(struct vhost*, const char*, int*, const char**);
+struct location *vhost_reverse_proxy(struct vhost *, const char *);
int vhost_fastcgi(struct vhost*, const char*);
int vhost_dirfd(struct vhost*, const char*, size_t*);
int vhost_strip(struct vhost*, const char*);
@@ -378,6 +399,9 @@ int parse_iri(char*, struct iri*, const char**);
int serialize_iri(struct iri*, char*, size_t);
char *pct_decode_str(char *);
+/* proxy.c */
+int proxy_init(struct client *);
+
/* puny.c */
int puny_decode(const char*, char*, size_t, const char**);
diff --git a/parse.y b/parse.y
index 8a9bae0..bab5209 100644
--- a/parse.y
+++ b/parse.y
@@ -121,8 +121,8 @@ typedef struct {
%token LANG LOCATION LOG
%token MAP MIME
%token OCSP OFF ON
-%token PARAM PORT PREFORK PROTOCOLS
-%token REQUIRE RETURN ROOT
+%token PARAM PORT PREFORK PROTOCOLS PROXY
+%token RELAY_TO REQUIRE RETURN ROOT
%token SERVER SPAWN STRIP
%token TCP TOEXT TYPE USER
@@ -330,6 +330,24 @@ locopt : AUTO INDEX bool { loc->auto_index = $3 ? 1 : -1; }
loc->lang = $2;
}
| LOG bool { loc->disable_log = !$2; }
+ | PROXY RELAY_TO string {
+ char *at;
+ const char *errstr;
+
+ only_once(loc->proxy_host, "proxy relay-to");
+ loc->proxy_host = $3;
+
+ if ((at = strchr($3, ':')) != NULL) {
+ *at++ = '\0';
+ loc->proxy_port = at;
+ } else
+ loc->proxy_port = "1965";
+
+ strtonum(loc->proxy_port, 1, UINT16_MAX, &errstr);
+ if (errstr != NULL)
+ yyerror("proxy port is %s: %s", errstr,
+ loc->proxy_port);
+ }
| REQUIRE CLIENT CA string {
only_once(loc->reqca, "require client ca");
ensure_absolute_path($4);
@@ -408,6 +426,8 @@ static struct keyword {
{"port", PORT},
{"prefork", PREFORK},
{"protocols", PROTOCOLS},
+ {"proxy", PROXY},
+ {"relay-to", RELAY_TO},
{"require", REQUIRE},
{"return", RETURN},
{"root", ROOT},
diff --git a/proxy.c b/proxy.c
new file mode 100644
index 0000000..72ce9f7
--- /dev/null
+++ b/proxy.c
@@ -0,0 +1,318 @@
+/*
+ * 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 "gmid.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+static struct timeval handshake_timeout = { 5, 0 };
+
+static void proxy_tls_readcb(int, short, void *);
+static void proxy_tls_writecb(int, short, void *);
+static void proxy_read(struct bufferevent *, void *);
+static void proxy_write(struct bufferevent *, void *);
+static void proxy_error(struct bufferevent *, short, void *);
+
+static void
+proxy_tls_readcb(int fd, short event, void *d)
+{
+ struct bufferevent *bufev = d;
+ struct client *c = bufev->cbarg;
+ char buf[IBUF_READ_SIZE];
+ int what = EVBUFFER_READ;
+ int howmuch = IBUF_READ_SIZE;
+ int res;
+ ssize_t ret;
+ size_t len;
+
+ 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(c->proxyctx, buf, howmuch)) {
+ case TLS_WANT_POLLIN:
+ case TLS_WANT_POLLOUT:
+ goto retry;
+ case -1:
+ what |= EVBUFFER_ERROR;
+ goto err;
+ }
+ len = ret;
+
+ if (len == 0) {
+ what |= EVBUFFER_EOF;
+ goto err;
+ }
+
+ res = evbuffer_add(bufev->input, buf, len);
+ if (res == -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
+proxy_tls_writecb(int fd, short event, void *d)
+{
+ struct bufferevent *bufev = d;
+ struct client *c = bufev->cbarg;
+ ssize_t ret;
+ size_t len;
+ short what = EVBUFFER_WRITE;
+
+ if (event & EV_TIMEOUT) {
+ what |= EVBUFFER_TIMEOUT;
+ goto err;
+ }
+
+ if (EVBUFFER_LENGTH(bufev->output) != 0) {
+ ret = tls_write(c->proxyctx, 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);
+ }
+
+ if (EVBUFFER_LENGTH(bufev->output) != 0)
+ event_add(&bufev->ev_write, NULL);
+
+ if (bufev->writecb != NULL &&
+ EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
+ (*bufev->writecb)(bufev, bufev->cbarg);
+ return;
+
+retry:
+ event_add(&bufev->ev_write, NULL);
+ return;
+
+err:
+ (*bufev->errorcb)(bufev, what, bufev->cbarg);
+}
+
+static void
+proxy_read(struct bufferevent *bev, void *d)
+{
+ struct client *c = d;
+ struct evbuffer *src = EVBUFFER_INPUT(bev);
+ char *hdr;
+ size_t len;
+ int code;
+
+ /* intercept the header */
+ if (c->code == 0) {
+ hdr = evbuffer_readln(src, &len, EVBUFFER_EOL_CRLF_STRICT);
+ if (hdr == NULL) {
+ /* max reply + \r\n */
+ if (EVBUFFER_LENGTH(src) > 1029) {
+ log_warn(c, "upstream server is trying to "
+ "send a header that's too long.");
+ proxy_error(bev, EVBUFFER_READ, c);
+ }
+
+ /* wait a bit */
+ return;
+ }
+
+ if (len < 3 || len > 1029 ||
+ !isdigit(hdr[0]) ||
+ !isdigit(hdr[1]) ||
+ !isspace(hdr[2])) {
+ free(hdr);
+ log_warn(c, "upstream server is trying to send a "
+ "header that's too long.");
+ proxy_error(bev, EVBUFFER_READ, c);
+ return;
+ }
+
+ c->header = hdr;
+ code = (hdr[0] - '0') * 10 + (hdr[1] - '0');
+
+ if (code < 10 || code >= 70) {
+ log_warn(c, "upstream server is trying to send an "
+ "invalid reply code: %d", code);
+ proxy_error(bev, EVBUFFER_READ, c);
+ return;
+ }
+
+ start_reply(c, code, hdr + 3);
+
+ if (c->code < 20 || c->code > 29) {
+ proxy_error(bev, EVBUFFER_EOF, c);
+ return;
+ }
+ }
+
+ bufferevent_write_buffer(c->bev, src);
+}
+
+static void
+proxy_write(struct bufferevent *bev, void *d)
+{
+ struct evbuffer *dst = EVBUFFER_OUTPUT(bev);
+
+ /* request successfully sent */
+ if (EVBUFFER_LENGTH(dst) == 0)
+ bufferevent_disable(bev, EV_WRITE);
+}
+
+static void
+proxy_error(struct bufferevent *bev, short error, void *d)
+{
+ struct client *c = d;
+
+ /*
+ * If we're here it means that some kind of non-recoverable
+ * error appened.
+ */
+
+ bufferevent_free(bev);
+ c->proxybev = NULL;
+
+ tls_free(c->proxyctx);
+ c->proxyctx = NULL;
+
+ close(c->pfd);
+ c->pfd = -1;
+
+ /* EOF and no header */
+ if (c->code == 0) {
+ start_reply(c, PROXY_ERROR, "protocol error");
+ return;
+ }
+
+ c->type = REQUEST_DONE;
+ client_write(c->bev, c);
+}
+
+static void
+proxy_handshake(int fd, short event, void *d)
+{
+ struct client *c = d;
+ struct evbuffer *evb;
+ char iribuf[GEMINI_URL_LEN];
+
+ if (event == EV_TIMEOUT) {
+ start_reply(c, PROXY_ERROR, "timeout");
+ return;
+ }
+
+ switch (tls_handshake(c->proxyctx)) {
+ case TLS_WANT_POLLIN:
+ event_set(&c->proxyev, fd, EV_READ, proxy_handshake, c);
+ event_add(&c->proxyev, &handshake_timeout);
+ return;
+ case TLS_WANT_POLLOUT:
+ event_set(&c->proxyev, fd, EV_WRITE, proxy_handshake, c);
+ event_add(&c->proxyev, &handshake_timeout);
+ return;
+ case -1:
+ log_warn(c, "handshake with proxy failed: %s",
+ tls_error(c->proxyctx));
+ start_reply(c, PROXY_ERROR, "handshake failed");
+ return;
+ }
+
+ c->proxybev = bufferevent_new(c->pfd, proxy_read, proxy_write,
+ proxy_error, c);
+ if (c->proxybev == NULL)
+ fatal("can't allocate bufferevent: %s", strerror(errno));
+
+ event_set(&c->proxybev->ev_read, c->pfd, EV_READ,
+ proxy_tls_readcb, c->proxybev);
+ event_set(&c->proxybev->ev_write, c->pfd, EV_WRITE,
+ proxy_tls_writecb, c->proxybev);
+
+#if HAVE_LIBEVENT2
+ evbuffer_unfreeze(c->proxybev->input, 0);
+ evbuffer_unfreeze(c->proxybev->output, 1);
+#endif
+
+ serialize_iri(&c->iri, iribuf, sizeof(iribuf));
+
+ evb = EVBUFFER_OUTPUT(c->proxybev);
+ evbuffer_add_printf(evb, "%s\r\n", iribuf);
+
+ bufferevent_enable(c->proxybev, EV_READ|EV_WRITE);
+}
+
+int
+proxy_init(struct client *c)
+{
+ struct tls_config *conf = NULL;
+
+ c->type = REQUEST_PROXY;
+
+ if ((conf = tls_config_new()) == NULL)
+ return -1;
+
+ /* TODO: tls_config_set_protocols here */
+ /* TODO: optionally load a client keypair here */
+ tls_config_insecure_noverifycert(conf);
+
+ if ((c->proxyctx = tls_client()) == NULL)
+ goto err;
+
+ if (tls_configure(c->proxyctx, conf) == -1)
+ goto err;
+
+ if (tls_connect_socket(c->proxyctx, c->pfd, c->domain) == -1)
+ goto err;
+
+ event_set(&c->proxyev, c->pfd, EV_READ|EV_WRITE, proxy_handshake, c);
+ event_add(&c->proxyev, &handshake_timeout);
+
+ tls_config_free(conf);
+ return 0;
+
+err:
+ tls_config_free(conf);
+ if (c->proxyctx != NULL)
+ tls_free(c->proxyctx);
+ return -1;
+}
diff --git a/server.c b/server.c
index 151ede7..402eda4 100644
--- a/server.c
+++ b/server.c
@@ -47,6 +47,7 @@ static void handle_handshake(int, short, void*);
static const char *strip_path(const char*, int);
static void fmt_sbuf(const char*, struct client*, const char*);
static int apply_block_return(struct client*);
+static int apply_reverse_proxy(struct client *);
static int apply_fastcgi(struct client*);
static int apply_require_ca(struct client*);
static size_t host_nth(struct vhost*);
@@ -72,6 +73,7 @@ static struct client *client_by_id(int);
static void handle_imsg_cgi_res(struct imsgbuf*, struct imsg*, size_t);
static void handle_imsg_fcgi_fd(struct imsgbuf*, struct imsg*, size_t);
+static void handle_imsg_conn_fd(struct imsgbuf*, struct imsg*, size_t);
static void handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t);
static void handle_dispatch_imsg(int, short, void *);
static void handle_siginfo(int, short, void*);
@@ -80,6 +82,7 @@ static imsg_handlerfn *handlers[] = {
[IMSG_QUIT] = handle_imsg_quit,
[IMSG_CGI_RES] = handle_imsg_cgi_res,
[IMSG_FCGI_FD] = handle_imsg_fcgi_fd,
+ [IMSG_CONN_FD] = handle_imsg_conn_fd,
};
static uint32_t server_client_id;
@@ -204,6 +207,27 @@ vhost_block_return(struct vhost *v, const char *path, int *code, const char **fm
return loc->block_code != 0;
}
+struct location *
+vhost_reverse_proxy(struct vhost *v, const char *path)
+{
+ struct location *loc;
+
+ if (v == NULL || path == NULL)
+ return NULL;
+
+ loc = TAILQ_FIRST(&v->locations);
+ while ((loc = TAILQ_NEXT(loc, locations)) != NULL) {
+ if (loc->proxy_host != NULL)
+ if (matches(loc->match, path))
+ return loc;
+ }
+
+ loc = TAILQ_FIRST(&v->locations);
+ if (loc->proxy_host != NULL)
+ return loc;
+ return NULL;
+}
+
int
vhost_fastcgi(struct vhost *v, const char *path)
{
@@ -602,6 +626,30 @@ apply_block_return(struct client *c)
return 1;
}
+/* 1 if matching a proxy relay-to (and apply it), 0 otherwise */
+static int
+apply_reverse_proxy(struct client *c)
+{
+ struct location *loc;
+ struct connreq r;
+
+ if ((loc = vhost_reverse_proxy(c->host, c->iri.path)) == NULL)
+ return 0;
+
+ log_debug(c, "opening proxy connection for %s:%s",
+ loc->proxy_host, loc->proxy_port);
+
+ strlcpy(r.host, loc->proxy_host, sizeof(r.host));
+ strlcpy(r.port, loc->proxy_port, sizeof(r.port));
+
+ strlcpy(c->domain, loc->proxy_host, sizeof(c->domain));
+
+ imsg_compose(&exibuf, IMSG_CONN_REQ, c->id, 0, -1, &r, sizeof(r));
+ imsg_flush(&exibuf);
+
+ return 1;
+}
+
/* 1 if matching `fcgi' (and apply it), 0 otherwise */
static int
apply_fastcgi(struct client *c)
@@ -963,6 +1011,9 @@ client_read(struct bufferevent *bev, void *d)
return;
}
+ if (apply_reverse_proxy(c))
+ return;
+
/* ignore the port number */
if (strcmp(c->iri.schema, "gemini") ||
strcmp(decoded, c->domain)) {
@@ -1030,6 +1081,7 @@ client_write(struct bufferevent *bev, void *d)
case REQUEST_CGI:
case REQUEST_FCGI:
+ case REQUEST_PROXY:
/*
* Here we depend on on the cgi script or fastcgi
* connection to provide data.
@@ -1091,6 +1143,7 @@ start_reply(struct client *c, int code, const char *meta)
if (c->type != REQUEST_CGI &&
c->type != REQUEST_FCGI &&
+ c->type != REQUEST_PROXY &&
!strcmp(meta, "text/gemini") &&
(lang = vhost_lang(c->host, c->iri.path)) != NULL) {
rr = evbuffer_add_printf(evb, ";lang=%s", lang);
@@ -1155,6 +1208,29 @@ client_close_ev(int fd, short event, void *d)
c->fd = -1;
}
+static void
+client_proxy_close(int fd, short event, void *d)
+{
+ struct tls *ctx = d;
+
+ if (ctx == NULL) {
+ close(fd);
+ return;
+ }
+
+ switch (tls_close(ctx)) {
+ case TLS_WANT_POLLIN:
+ event_once(fd, EV_READ, client_proxy_close, d, NULL);
+ break;
+ case TLS_WANT_POLLOUT:
+ event_once(fd, EV_WRITE, client_proxy_close, d, NULL);
+ break;
+ }
+
+ tls_free(ctx);
+ close(fd);
+}
+
void
client_close(struct client *c)
{
@@ -1179,6 +1255,18 @@ client_close(struct client *c)
bufferevent_free(c->bev);
c->bev = NULL;
+ if (c->proxybev != NULL) {
+ if (event_pending(&c->proxyev, EV_READ|EV_WRITE, NULL))
+ event_del(&c->proxyev);
+
+ if (c->pfd != -1) {
+ client_proxy_close(c->pfd, 0, c->proxyctx);
+ c->pfd = -1;
+ }
+
+ bufferevent_free(c->proxybev);
+ }
+
client_close_ev(c->fd, 0, c);
}
@@ -1380,6 +1468,30 @@ handle_imsg_fcgi_fd(struct imsgbuf *ibuf, struct imsg *imsg, size_t len)
}
static void
+handle_imsg_conn_fd(struct imsgbuf *ibuf, struct imsg *imsg, size_t len)
+{
+ struct client *c;
+ int id;
+
+ id = imsg->hdr.peerid;
+ if ((c = try_client_by_id(id)) == NULL) {
+ if (imsg->fd != -1)
+ close(imsg->fd);
+ return;
+ }
+
+ if ((c->pfd = imsg->fd) == -1) {
+ start_reply(c, PROXY_ERROR, "proxy error");
+ return;
+ }
+
+ mark_nonblock(c->pfd);
+
+ if (proxy_init(c) == -1)
+ start_reply(c, PROXY_ERROR, "proxy error");
+}
+
+static void
handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t len)
{
/*