Commit Diff


commit - e2f167afb3444d3ba55fdffe234ef7812cac72f0
commit + b7967bc1f695126e1bf2705bfd486bbc32aaf8b0
blob - a6e0d1bd0b46a24ebaa2337add20711460cee46a
blob + 2abdce04ab1750b7fbdebaafa01606ff9063ac32
--- gmid.c
+++ 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;
@@ -329,6 +330,17 @@ free_config(void)
 
 			free(a->alias);
 			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);
@@ -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);
 	}
blob - 96c67fbbaaa6377d3255fba78c0049b1036a045c
blob + 92f6985ae691b1d31f5aeb3421516ef2e02af9d9
--- gmid.h
+++ 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;
blob - c5ef112bfc8a8559a6ac21b49ac0d9619bcefe9c
blob + 43a7b15c80d3c94252642ca2f81c727b4f53f720
--- parse.y
+++ 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)
blob - 3c55ca2a65cc25c68f599d6ff74d554fa3813fef
blob + 6f892c07b0ce77157f4e83b4bebf13921ad90164
--- proxy.c
+++ proxy.c
@@ -233,7 +233,7 @@ proxy_error(struct bufferevent *bev, short error, void
 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;
 
blob - 2a4ed662878234d8ee4f88064cfb74988a5a4da8
blob + 991e126f99721dd111e9225158cbba52b7557d6a
--- server.c
+++ server.c
@@ -603,6 +603,31 @@ apply_block_return(struct client *c)
 
 	start_reply(c, code, c->sbuf);
 	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 */
@@ -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);