mirror of https://github.com/omar-polo/gmid.git
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
This commit is contained in:
parent
054387bb26
commit
72b033ef18
2
Makefile
2
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}
|
||||
|
|
58
ex.c
58
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,
|
||||
};
|
||||
|
||||
|
@ -424,6 +426,62 @@ handle_imsg_fcgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
|
|||
imsg_flush(ibuf);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
2
gmid.c
2
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);
|
||||
|
||||
|
|
26
gmid.h
26
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**);
|
||||
|
||||
|
|
24
parse.y
24
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},
|
||||
|
|
|
@ -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;
|
||||
}
|
112
server.c
112
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);
|
||||
}
|
||||
|
||||
|
@ -1379,6 +1467,30 @@ handle_imsg_fcgi_fd(struct imsgbuf *ibuf, struct imsg *imsg, size_t len)
|
|||
fcgi_req(c);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue