aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOmar Polo <op@omarpolo.com>2022-01-02 16:33:28 +0000
committerOmar Polo <op@omarpolo.com>2022-01-02 16:33:28 +0000
commitb7967bc1f695126e1bf2705bfd486bbc32aaf8b0 (patch)
treed24f103eb78223a61049bfcebedb1686bd628c3c
parente2f167afb3444d3ba55fdffe234ef7812cac72f0 (diff)
proxy: allow multiple proxy blocks, matching options and validations
as a side effect the order of the content of a server block is relaxed: options, location or proxy blocks can be put in any order.
-rw-r--r--gmid.c16
-rw-r--r--gmid.h10
-rw-r--r--parse.y140
-rw-r--r--proxy.c8
-rw-r--r--server.c32
5 files changed, 144 insertions, 62 deletions
diff --git a/gmid.c b/gmid.c
index a6e0d1b..2abdce0 100644
--- a/gmid.c
+++ b/gmid.c
@@ -279,6 +279,7 @@ free_config(void)
{
struct vhost *h, *th;
struct location *l, *tl;
+ struct proxy *p, *tp;
struct envlist *e, *te;
struct alist *a, *ta;
int v, i;
@@ -331,6 +332,17 @@ free_config(void)
free(a);
}
+ TAILQ_FOREACH_SAFE(p, &h->proxies, proxies, tp) {
+ TAILQ_REMOVE(&h->proxies, p, proxies);
+
+ free(p->match_proto);
+ free(p->match_host);
+ free(p->host);
+ tls_unload_file(p->cert, p->certlen);
+ tls_unload_file(p->key, p->keylen);
+ free(p);
+ }
+
free((char*)h->domain);
free((char*)h->cert);
free((char*)h->key);
@@ -338,10 +350,6 @@ free_config(void)
free((char*)h->cgi);
free((char*)h->entrypoint);
- free(h->proxy.host);
- tls_unload_file(h->proxy.cert, h->proxy.certlen);
- tls_unload_file(h->proxy.key, h->proxy.keylen);
-
TAILQ_REMOVE(&hosts, h, vhosts);
free(h);
}
diff --git a/gmid.h b/gmid.h
index 96c67fb..92f6985 100644
--- a/gmid.h
+++ b/gmid.h
@@ -97,7 +97,12 @@ struct fcgi {
};
extern struct fcgi fcgi[FCGI_MAX];
+TAILQ_HEAD(proxyhead, proxy);
struct proxy {
+ char *match_proto;
+ char *match_host;
+ const char *match_port;
+
char *host;
const char *port;
int notls;
@@ -107,6 +112,8 @@ struct proxy {
size_t certlen;
uint8_t *key;
size_t keylen;
+
+ TAILQ_ENTRY(proxy) proxies;
};
TAILQ_HEAD(lochead, location);
@@ -163,7 +170,7 @@ struct vhost {
struct envhead env;
struct envhead params;
struct aliashead aliases;
- struct proxy proxy;
+ struct proxyhead proxies;
};
struct etm { /* extension to mime */
@@ -229,6 +236,7 @@ struct client {
struct bufferevent *cgibev;
+ struct proxy *proxy;
struct bufferevent *proxybev;
struct tls *proxyctx;
struct event proxyev;
diff --git a/parse.y b/parse.y
index c5ef112..43a7b15 100644
--- a/parse.y
+++ b/parse.y
@@ -81,6 +81,7 @@ char *symget(const char *);
struct vhost *new_vhost(void);
struct location *new_location(void);
+struct proxy *new_proxy(void);
char *ensure_absolute_path(char*);
int check_block_code(int);
char *check_block_fmt(char*);
@@ -88,6 +89,8 @@ int check_strip_no(int);
int check_port_num(int);
int check_prefork_num(int);
void advance_loc(void);
+void advance_proxy(void);
+void parsehp(char *, char **, const char **, const char *);
void only_once(const void*, const char*);
void only_oncei(int, const char*);
int fastcgi_conf(char *, char *, char *);
@@ -95,6 +98,7 @@ void add_param(char *, char *, int);
static struct vhost *host;
static struct location *loc;
+static struct proxy *proxy;
static int errors;
typedef struct {
@@ -115,13 +119,13 @@ typedef struct {
%token CA CERT CGI CHROOT CLIENT
%token DEFAULT
%token ENTRYPOINT ENV
-%token FASTCGI
+%token FASTCGI FOR_HOST
%token INCLUDE INDEX IPV6
%token KEY
%token LANG LOCATION LOG
%token MAP MIME
%token OCSP OFF ON
-%token PARAM PORT PREFORK PROTOCOLS PROXY
+%token PARAM PORT PREFORK PROTO PROTOCOLS PROXY
%token RELAY_TO REQUIRE RETURN ROOT
%token SERVER SPAWN STRIP
%token TCP TOEXT TYPE
@@ -222,6 +226,8 @@ vhost : SERVER string {
loc = new_location();
TAILQ_INSERT_HEAD(&host->locations, loc, locations);
+ TAILQ_INIT(&host->proxies);
+
loc->match = xstrdup("*");
host->domain = $2;
@@ -229,15 +235,17 @@ vhost : SERVER string {
yywarn("\"%s\" looks like punycode: you "
"should use the decoded hostname", $2);
}
- } '{' optnl servopts locations '}' {
+ } '{' optnl servbody '}' {
if (host->cert == NULL || host->key == NULL)
yyerror("invalid vhost definition: %s", $2);
}
| error '}' { yyerror("bad server directive"); }
;
-servopts : /* empty */
- | servopts servopt optnl
+servbody : /* empty */
+ | servbody servopt optnl
+ | servbody location optnl
+ | servbody proxy optnl
;
servopt : ALIAS string {
@@ -281,12 +289,34 @@ servopt : ALIAS string {
| PARAM string '=' string {
add_param($2, $4, 0);
}
- | proxy
| locopt
;
-proxy : PROXY proxy_opt
- | PROXY '{' optnl proxy_opts '}'
+proxy : PROXY { advance_proxy(); }
+ proxy_matches '{' optnl proxy_opts '}' {
+ if (proxy->host == NULL)
+ yyerror("invalid proxy block: missing `relay-to' option");
+
+ if ((proxy->cert == NULL && proxy->key != NULL) ||
+ (proxy->cert != NULL && proxy->key == NULL))
+ yyerror("invalid proxy block: missing cert or key");
+ }
+ ;
+
+proxy_matches : /* empty */
+ | proxy_matches proxy_match
+ ;
+
+proxy_match : PROTO string {
+ only_once(proxy->match_proto, "proxy proto");
+ free(proxy->match_proto);
+ proxy->match_proto = $2;
+ }
+ | FOR_HOST string {
+ only_once(proxy->match_host, "proxy for-host");
+ free(proxy->match_host);
+ parsehp($2, &proxy->match_host, &proxy->match_port, "10965");
+ }
;
proxy_opts : /* empty */
@@ -294,63 +324,41 @@ proxy_opts : /* empty */
;
proxy_opt : CERT string {
- struct proxy *p = &host->proxy;
-
- only_once(p->cert, "proxy cert");
+ only_once(proxy->cert, "proxy cert");
+ tls_unload_file(proxy->cert, proxy->certlen);
ensure_absolute_path($2);
- p->cert = tls_load_file($2, &p->certlen, NULL);
- if (p->cert == NULL)
+ proxy->cert = tls_load_file($2, &proxy->certlen, NULL);
+ if (proxy->cert == NULL)
yyerror("can't load cert %s", $2);
free($2);
}
| KEY string {
- struct proxy *p = &host->proxy;
-
- only_once(p->key, "proxy key");
+ only_once(proxy->key, "proxy key");
+ tls_unload_file(proxy->key, proxy->keylen);
ensure_absolute_path($2);
- p->key = tls_load_file($2, &p->keylen, NULL);
- if (p->key == NULL)
+ proxy->key = tls_load_file($2, &proxy->keylen, NULL);
+ if (proxy->key == NULL)
yyerror("can't load key %s", $2);
free($2);
}
| PROTOCOLS string {
- struct proxy *p = &host->proxy;
-
- if (tls_config_parse_protocols(&p->protocols, $2) == -1)
+ if (tls_config_parse_protocols(&proxy->protocols, $2) == -1)
yyerror("invalid protocols string \"%s\"", $2);
free($2);
}
| RELAY_TO string {
- char *at;
- const char *errstr;
- struct proxy *p = &host->proxy;
-
- only_once(p->host, "proxy relay-to");
- p->host = $2;
-
- if ((at = strchr($2, ':')) != NULL) {
- *at++ = '\0';
- p->port = at;
- } else
- p->port = "1965";
-
- strtonum(p->port, 1, UINT16_MAX, &errstr);
- if (errstr != NULL)
- yyerror("proxy port is %s: %s", errstr,
- p->port);
+ only_once(proxy->host, "proxy relay-to");
+ free(proxy->host);
+ parsehp($2, &proxy->host, &proxy->port, "1965");
}
| USE_TLS bool {
- host->proxy.notls = !$2;
+ proxy->notls = !$2;
}
| VERIFYNAME bool {
- host->proxy.noverifyname = !$2;
+ proxy->noverifyname = !$2;
}
;
-locations : /* empty */
- | locations location optnl
- ;
-
location : LOCATION { advance_loc(); } string '{' optnl locopts '}' {
/* drop the starting '/' if any */
if (*$3 == '/')
@@ -459,6 +467,7 @@ static struct keyword {
{"entrypoint", ENTRYPOINT},
{"env", ENV},
{"fastcgi", FASTCGI},
+ {"for-host", FOR_HOST},
{"index", INDEX},
{"ipv6", IPV6},
{"key", KEY},
@@ -473,6 +482,7 @@ static struct keyword {
{"param", PARAM},
{"port", PORT},
{"prefork", PREFORK},
+ {"proto", PROTO},
{"protocols", PROTOCOLS},
{"proxy", PROXY},
{"relay-to", RELAY_TO},
@@ -976,11 +986,7 @@ symget(const char *nam)
struct vhost *
new_vhost(void)
{
- struct vhost *v;
-
- v = xcalloc(1, sizeof(*v));
- v->proxy.protocols = TLS_PROTOCOLS_DEFAULT;
- return v;
+ return xcalloc(1, sizeof(struct vhost));
}
struct location *
@@ -994,6 +1000,16 @@ new_location(void)
return l;
}
+struct proxy *
+new_proxy(void)
+{
+ struct proxy *p;
+
+ p = xcalloc(1, sizeof(*p));
+ p->protocols = TLS_PROTOCOLS_DEFAULT;
+ return p;
+}
+
char *
ensure_absolute_path(char *path)
{
@@ -1067,6 +1083,32 @@ advance_loc(void)
}
void
+advance_proxy(void)
+{
+ proxy = new_proxy();
+ TAILQ_INSERT_TAIL(&host->proxies, proxy, proxies);
+}
+
+void
+parsehp(char *str, char **host, const char **port, const char *def)
+{
+ char *at;
+ const char *errstr;
+
+ *host = str;
+
+ if ((at = strchr(str, ':')) != NULL) {
+ *at++ = '\0';
+ *port = at;
+ } else
+ *port = def;
+
+ strtonum(*port, 1, UINT16_MAX, &errstr);
+ if (errstr != NULL)
+ yyerror("port is %s: %s", errstr, *port);
+}
+
+void
only_once(const void *ptr, const char *name)
{
if (ptr != NULL)
diff --git a/proxy.c b/proxy.c
index 3c55ca2..6f892c0 100644
--- a/proxy.c
+++ b/proxy.c
@@ -233,7 +233,7 @@ proxy_error(struct bufferevent *bev, short error, void *d)
static void
proxy_enqueue_req(struct client *c)
{
- struct proxy *p = &c->host->proxy;
+ struct proxy *p = c->proxy;
struct evbuffer *evb;
char iribuf[GEMINI_URL_LEN];
@@ -294,7 +294,7 @@ proxy_handshake(int fd, short event, void *d)
static int
proxy_setup_tls(struct client *c)
{
- struct proxy *p = &c->host->proxy;
+ struct proxy *p = c->proxy;
struct tls_config *conf = NULL;
if ((conf = tls_config_new()) == NULL)
@@ -324,7 +324,7 @@ proxy_setup_tls(struct client *c)
if (tls_configure(c->proxyctx, conf) == -1)
goto err;
- if (tls_connect_socket(c->proxyctx, c->pfd, c->domain) == -1)
+ if (tls_connect_socket(c->proxyctx, c->pfd, p->host) == -1)
goto err;
event_set(&c->proxyev, c->pfd, EV_READ|EV_WRITE, proxy_handshake, c);
@@ -345,7 +345,7 @@ err:
int
proxy_init(struct client *c)
{
- struct proxy *p = &c->host->proxy;
+ struct proxy *p = c->proxy;
c->type = REQUEST_PROXY;
diff --git a/server.c b/server.c
index 2a4ed66..991e126 100644
--- a/server.c
+++ b/server.c
@@ -605,6 +605,31 @@ apply_block_return(struct client *c)
return 1;
}
+static struct proxy *
+matched_proxy(struct client *c)
+{
+ struct proxy *p;
+ const char *proto;
+ const char *host;
+ const char *port;
+
+ TAILQ_FOREACH(p, &c->host->proxies, proxies) {
+ if ((proto = p->match_proto) == NULL)
+ proto = "gemini";
+ if ((host = p->match_host) == NULL)
+ host = "*";
+ if ((port = p->match_port) == NULL)
+ port = "*";
+
+ if (matches(proto, c->iri.schema) &&
+ matches(host, c->domain) &&
+ matches(port, c->iri.port))
+ return p;
+ }
+
+ return NULL;
+}
+
/* 1 if matching a proxy relay-to (and apply it), 0 otherwise */
static int
apply_reverse_proxy(struct client *c)
@@ -612,18 +637,17 @@ apply_reverse_proxy(struct client *c)
struct proxy *p;
struct connreq r;
- p = &c->host->proxy;
- if (p->host == NULL)
+ if ((p = matched_proxy(c)) == NULL)
return 0;
+ c->proxy = p;
+
log_debug(c, "opening proxy connection for %s:%s",
p->host, p->port);
strlcpy(r.host, p->host, sizeof(r.host));
strlcpy(r.port, p->port, sizeof(r.port));
- strlcpy(c->domain, p->host, sizeof(c->domain));
-
imsg_compose(&exibuf, IMSG_CONN_REQ, c->id, 0, -1, &r, sizeof(r));
imsg_flush(&exibuf);