commit b7967bc1f695126e1bf2705bfd486bbc32aaf8b0 from: Omar Polo date: Sun Jan 02 16:33:28 2022 UTC 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. 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);