diff options
author | Omar Polo <op@omarpolo.com> | 2022-01-02 16:33:28 +0000 |
---|---|---|
committer | Omar Polo <op@omarpolo.com> | 2022-01-02 16:33:28 +0000 |
commit | b7967bc1f695126e1bf2705bfd486bbc32aaf8b0 (patch) | |
tree | d24f103eb78223a61049bfcebedb1686bd628c3c | |
parent | e2f167afb3444d3ba55fdffe234ef7812cac72f0 (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.c | 16 | ||||
-rw-r--r-- | gmid.h | 10 | ||||
-rw-r--r-- | parse.y | 140 | ||||
-rw-r--r-- | proxy.c | 8 | ||||
-rw-r--r-- | server.c | 32 |
5 files changed, 144 insertions, 62 deletions
@@ -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); } @@ -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; @@ -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) @@ -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; @@ -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); |