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.
This commit is contained in:
Omar Polo 2022-01-02 16:33:28 +00:00
parent e2f167afb3
commit b7967bc1f6
5 changed files with 144 additions and 62 deletions

16
gmid.c
View File

@ -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);
}

10
gmid.h
View File

@ -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;

140
parse.y
View File

@ -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)
{
@ -1066,6 +1082,32 @@ advance_loc(void)
TAILQ_INSERT_TAIL(&host->locations, loc, locations);
}
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)
{

View File

@ -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;

View File

@ -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);