diff options
Diffstat (limited to 'usr.sbin/relayd/parse.y')
-rw-r--r-- | usr.sbin/relayd/parse.y | 489 |
1 files changed, 480 insertions, 9 deletions
diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y index bd8cfd83173..efbb11882b8 100644 --- a/usr.sbin/relayd/parse.y +++ b/usr.sbin/relayd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.25 2007/02/09 17:55:49 reyk Exp $ */ +/* $OpenBSD: parse.y,v 1.26 2007/02/22 03:32:39 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -36,6 +36,7 @@ #include <errno.h> #include <event.h> #include <limits.h> +#include <stdint.h> #include <stdarg.h> #include <stdio.h> #include <netdb.h> @@ -53,9 +54,14 @@ const char *infile; objid_t last_service_id = 0; objid_t last_table_id = 0; objid_t last_host_id = 0; +objid_t last_relay_id = 0; +objid_t last_proto_id = 0; static struct service *service = NULL; static struct table *table = NULL; +static struct relay *rlay = NULL; +static struct protocol *proto = NULL; +static struct protonode node; int yyerror(const char *, ...); int yyparse(void); @@ -101,12 +107,15 @@ typedef struct { %token CHECK HTTP HTTPS TCP ICMP EXTERNAL %token TIMEOUT CODE DIGEST PORT TAG INTERFACE %token VIRTUAL IP INTERVAL DISABLE STICKYADDR -%token SEND EXPECT NOTHING USE SSL -%token LOG UPDATES ALL +%token SEND EXPECT NOTHING USE SSL LOADBALANCE ROUNDROBIN +%token RELAY LISTEN ON FORWARD TO NAT LOOKUP PREFORK NO MARK MARKED +%token PROTO SESSION CACHE APPEND CHANGE REMOVE FROM FILTER HASH +%token LOG UPDATES ALL DEMOTE NODELAY SACK SOCKET BUFFER URL RETRY %token ERROR %token <v.string> STRING %type <v.string> interface -%type <v.number> number port http_type loglevel +%type <v.number> number port http_type loglevel sslcache +%type <v.number> prototype dstmode docheck retry %type <v.host> host %type <v.tv> timeout @@ -118,6 +127,8 @@ grammar : /* empty */ | grammar main '\n' | grammar service '\n' | grammar table '\n' + | grammar relay '\n' + | grammar proto '\n' | grammar error '\n' { errors++; } ; @@ -206,6 +217,30 @@ main : INTERVAL number { conf->interval.tv_sec = $2; } | TIMEOUT timeout { bcopy(&$2, &conf->timeout, sizeof(struct timeval)); } + | PREFORK number { + if ($2 <= 0 || $2 > RELAY_MAXPROC) { + yyerror("invalid number of preforked " + "relays: %d", $2); + YYERROR; + } + conf->prefork_relay = $2; + } + | DEMOTE STRING { + conf->flags |= F_DEMOTE; + if (strlcpy(conf->demote_group, $2, + sizeof(conf->demote_group)) + >= sizeof(conf->demote_group)) { + yyerror("yyparse: demote group name too long"); + free($2); + YYERROR; + } + free($2); + if (carp_demote_init(conf->demote_group, 1) == -1) { + yyerror("yyparse: error initializing group '%s'", + conf->demote_group); + YYERROR; + } + } ; loglevel : UPDATES { $$ = HOSTSTATED_OPT_LOGUPDATE; } @@ -233,7 +268,7 @@ service : SERVICE STRING { } free($2); srv->id = last_service_id++; - if (last_service_id == UINT_MAX) { + if (last_service_id == INT_MAX) { yyerror("too many services defined"); YYERROR; } @@ -358,7 +393,7 @@ table : TABLE STRING { tb->id = last_table_id++; bcopy(&conf->timeout, &tb->timeout, sizeof(struct timeval)); - if (last_table_id == UINT_MAX) { + if (last_table_id == INT_MAX) { yyerror("too many tables defined"); YYERROR; } @@ -452,6 +487,22 @@ tableoptsl : host { | REAL port { table->port = $2; } + | DEMOTE STRING { + table->flags |= F_DEMOTE; + if (strlcpy(table->demote_group, $2, + sizeof(table->demote_group)) + >= sizeof(table->demote_group)) { + yyerror("yyparse: demote group name too long"); + free($2); + YYERROR; + } + free($2); + if (carp_demote_init(table->demote_group, 1) == -1) { + yyerror("yyparse: error initializing group '%s'", + table->demote_group); + YYERROR; + } + } | DISABLE { table->flags |= F_DISABLE; } | USE SSL { table->flags |= F_SSL; @@ -459,11 +510,371 @@ tableoptsl : host { } ; +proto : PROTO STRING { + struct protocol *p; + + TAILQ_FOREACH(p, &conf->protos, entry) + if (!strcmp(p->name, $2)) + break; + if (p != NULL) { + yyerror("protocol %s defined twice", $2); + free($2); + YYERROR; + } + if ((p = calloc(1, sizeof (*p))) == NULL) + fatal("out of memory"); + + if (strlcpy(p->name, $2, sizeof(p->name)) >= + sizeof(p->name)) { + yyerror("protocol name truncated"); + YYERROR; + } + free($2); + p->id = last_proto_id++; + p->cache = RELAY_CACHESIZE; + p->type = RELAY_PROTO_TCP; + if (last_proto_id == INT_MAX) { + yyerror("too many protocols defined"); + YYERROR; + } + RB_INIT(&p->tree); + proto = p; + } '{' optnl protopts_l '}' { + conf->protocount++; + TAILQ_INSERT_HEAD(&conf->protos, proto, entry); + } + ; + +protopts_l : protopts_l protoptsl nl + | protoptsl optnl + ; + +protoptsl : SSL SESSION CACHE sslcache { proto->cache = $4; } + | PROTO prototype { proto->type = $2; } + | TCP tcpflags + | TCP '{' tcpflags_l '}' + | protonode { + struct protonode *pn, pk; + + pn = RB_FIND(proto_tree, &proto->tree, &node); + if (pn != NULL) { + yyerror("protocol node %s defined twice", + node.key); + YYERROR; + } + if ((pn = calloc(1, sizeof (*pn))) == NULL) + fatal("out of memory"); + + bcopy(&node, pn, sizeof(*pn)); + pn->key = node.key; + pn->value = node.value; + pn->header = node.getvars ? 0 : 1; + pn->id = proto->nodecount++; + if (pn->id == INT_MAX) { + yyerror("too many protocol nodes defined"); + YYERROR; + } + RB_INSERT(proto_tree, &proto->tree, pn); + + if (node.getvars) { + pk.key = "GET"; + pn = RB_FIND(proto_tree, &proto->tree, &pk); + if (pn != NULL) { + pn->getvars++; + } else if (pn == NULL) { + if ((pn = (struct protonode *) + calloc(1, sizeof(*pn))) == NULL) + fatal("out of memory"); + pn->key = strdup("GET"); + if (pn->key == NULL) + fatal("out of memory"); + pn->value = NULL; + pn->action = NODE_ACTION_NONE; + pn->getvars = 1; + pn->id = proto->nodecount++; + if (pn->id == INT_MAX) { + yyerror("too many protocol " + "nodes defined"); + YYERROR; + } + RB_INSERT(proto_tree, &proto->tree, pn); + } + } + + bzero(&node, sizeof(node)); + } + ; + +tcpflags_l : tcpflags comma tcpflags_l + | tcpflags + ; + +tcpflags : SACK { proto->tcpflags |= TCPFLAG_SACK; } + | NO SACK { proto->tcpflags |= TCPFLAG_NSACK; } + | NODELAY { proto->tcpflags |= TCPFLAG_NODELAY; } + | NO NODELAY { proto->tcpflags |= TCPFLAG_NNODELAY; } + | SOCKET BUFFER number { + proto->tcpflags |= TCPFLAG_BUFSIZ; + proto->tcpbufsiz = $3; + } + ; + + +protonode : APPEND STRING TO STRING marked { + node.action = NODE_ACTION_APPEND; + node.key = strdup($4); + node.value = strdup($2); + if (node.key == NULL || node.value == NULL) + fatal("out of memory"); + if (strchr(node.value, '$') != NULL) + node.macro = 1; + free($4); + free($2); + } + | CHANGE STRING TO STRING marked { + node.action = NODE_ACTION_CHANGE; + node.key = strdup($2); + node.value = strdup($4); + if (node.key == NULL || node.value == NULL) + fatal("out of memory"); + if (strchr(node.value, '$') != NULL) + node.macro = 1; + free($4); + free($2); + } + | REMOVE STRING marked { + node.action = NODE_ACTION_REMOVE; + node.key = strdup($2); + node.value = NULL; + if (node.key == NULL) + fatal("out of memory"); + free($2); + } + | getvars EXPECT STRING FROM STRING mark { + node.action = NODE_ACTION_EXPECT; + node.key = strdup($5); + node.value = strdup($3);; + if (node.key == NULL || node.value == NULL) + fatal("out of memory"); + free($5); + free($3); + } + | getvars FILTER STRING FROM STRING mark { + node.action = NODE_ACTION_FILTER; + node.key = strdup($5); + node.value = strdup($3);; + if (node.key == NULL || node.value == NULL) + fatal("out of memory"); + free($5); + free($3); + } + | getvars HASH STRING marked { + node.action = NODE_ACTION_HASH; + node.key = strdup($3); + node.value = NULL; + if (node.key == NULL) + fatal("out of memory"); + free($3); + proto->lateconnect++; + } + ; + +mark : /* nothing */ + | MARK { node.mark++; } + ; + +marked : /* nothing */ + | MARKED { node.mark++; } + ; + +getvars : /* nothing */ + | URL { node.getvars++; } + ; + +sslcache : /* empty */ { $$ = RELAY_CACHESIZE; } + | number { $$ = $1; } + | DISABLE { $$ = -2; } + ; + +prototype : TCP { $$ = RELAY_PROTO_TCP; } + | HTTP { $$ = RELAY_PROTO_HTTP; } + ; + +relay : RELAY STRING { + struct relay *r; + + TAILQ_FOREACH(r, &conf->relays, entry) + if (!strcmp(r->name, $2)) + break; + if (r != NULL) { + yyerror("relay %s defined twice", $2); + free($2); + YYERROR; + } + if ((r = calloc(1, sizeof (*r))) == NULL) + fatal("out of memory"); + + if (strlcpy(r->name, $2, sizeof(r->name)) >= + sizeof(r->name)) { + yyerror("relay name truncated"); + YYERROR; + } + free($2); + r->id = last_relay_id++; + r->timeout.tv_sec = RELAY_TIMEOUT; + r->proto = NULL; + r->dsttable = NULL; + if (last_relay_id == INT_MAX) { + yyerror("too many relays defined"); + YYERROR; + } + rlay = r; + } '{' optnl relayopts_l '}' { + if (rlay->ss.ss_family == AF_UNSPEC) { + yyerror("relay %s has no listener", + rlay->name); + YYERROR; + } + if ((rlay->flags & F_NATLOOK) == 0 && + rlay->dstss.ss_family == AF_UNSPEC && + rlay->dsttable == NULL) { + yyerror("relay %s has no target, service, " + "or table", rlay->name); + YYERROR; + } + if (rlay->proto == NULL) + rlay->proto = &conf->proto_default; + conf->relaycount++; + TAILQ_INIT(&rlay->sessions); + TAILQ_INSERT_HEAD(&conf->relays, rlay, entry); + } + ; + +relayopts_l : relayopts_l relayoptsl nl + | relayoptsl optnl + ; + +relayoptsl : LISTEN ON STRING port sslserv { + struct addresslist al; + struct address *h; + + if (rlay->ss.ss_family != AF_UNSPEC) { + yyerror("relay %s listener already specified", + rlay->name); + YYERROR; + } + + TAILQ_INIT(&al); + if (host($3, &al, 1, $4, NULL) <= 0) { + yyerror("invalid listen ip: %s", $3); + free($3); + YYERROR; + } + free($3); + h = TAILQ_FIRST(&al); + bcopy(&h->ss, &rlay->ss, sizeof(rlay->ss)); + rlay->port = h->port; + } + | FORWARD TO STRING port { + struct addresslist al; + struct address *h; + + if (rlay->dstss.ss_family != AF_UNSPEC) { + yyerror("relay %s target or service already specified", + rlay->name); + free($3); + YYERROR; + } + + TAILQ_INIT(&al); + if (host($3, &al, 1, $4, NULL) <= 0) { + yyerror("invalid listen ip: %s", $3); + free($3); + YYERROR; + } + free($3); + h = TAILQ_FIRST(&al); + bcopy(&h->ss, &rlay->dstss, sizeof(rlay->dstss)); + rlay->dstport = h->port; + } + | SERVICE STRING { + struct service *svc; + struct address *h; + + if (rlay->dstss.ss_family != AF_UNSPEC) { + yyerror("relay %s target or service already specified", + rlay->name); + free($2); + YYERROR; + } + + if ((svc = service_findbyname(conf, $2)) == NULL) { + yyerror("relay %s for unknown service %s", + rlay->name, $2); + free($2); + YYERROR; + } + free($2); + h = TAILQ_FIRST(&svc->virts); + bcopy(&h->ss, &rlay->dstss, sizeof(rlay->dstss)); + rlay->dstport = h->port; + } + | TABLE STRING dstmode docheck { + struct table *dsttable; + + if ((dsttable = table_findbyname(conf, $2)) == NULL) { + yyerror("relay %d for unknown table %s", + rlay->name, $2); + free($2); + YYERROR; + } + free($2); + rlay->dsttable = dsttable; + rlay->dstmode = $3; + rlay->dstcheck = $4; + } + | PROTO STRING { + struct protocol *p; + + TAILQ_FOREACH(p, &conf->protos, entry) + if (!strcmp(p->name, $2)) + break; + if (p == NULL) { + yyerror("no such protocol: %s", $2); + free($2); + YYERROR; + } + p->flags |= F_USED; + rlay->proto = p; + free($2); + } + | NAT LOOKUP { rlay->flags |= F_NATLOOK; } + | TIMEOUT number { rlay->timeout.tv_sec = $2; } + | DISABLE { rlay->flags |= F_DISABLE; } + ; + +dstmode : /* empty */ { $$ = RELAY_DSTMODE_DEFAULT; } + | LOADBALANCE { $$ = RELAY_DSTMODE_LOADBALANCE; } + | ROUNDROBIN { $$ = RELAY_DSTMODE_ROUNDROBIN; } + | HASH { $$ = RELAY_DSTMODE_HASH; } + ; + +docheck : /* empty */ { $$ = 1; } + | NO CHECK { $$ = 0; } + ; + +sslserv : /* empty */ + | SSL { + rlay->flags |= F_SSL; + conf->flags |= F_SSL; + } + ; + interface : /*empty*/ { $$ = NULL; } | INTERFACE STRING { $$ = $2; } ; -host : HOST STRING { +host : HOST STRING retry { struct address *a; struct addresslist al; @@ -490,7 +901,8 @@ host : HOST STRING { } free($2); $$->id = last_host_id++; - if (last_host_id == UINT_MAX) { + $$->retry = $3; + if (last_host_id == INT_MAX) { yyerror("too many hosts defined"); free($$); YYERROR; @@ -498,6 +910,10 @@ host : HOST STRING { } ; +retry : /* nothing */ { $$ = 0; } + | RETRY number { $$ = $2; } + ; + timeout : number { $$.tv_sec = $1 / 1000; @@ -505,6 +921,10 @@ timeout : number } ; +comma : ',' + | /* empty */ + ; + optnl : '\n' optnl | ; @@ -546,13 +966,22 @@ lookup(char *s) /* this has to be sorted always */ static const struct keywords keywords[] = { { "all", ALL }, + { "append", APPEND }, { "backup", BACKUP }, + { "buffer", BUFFER }, + { "cache", CACHE }, + { "change", CHANGE }, { "check", CHECK }, { "code", CODE }, + { "demote", DEMOTE }, { "digest", DIGEST }, { "disable", DISABLE }, { "expect", EXPECT }, { "external", EXTERNAL }, + { "filter", FILTER }, + { "forward", FORWARD }, + { "from", FROM }, + { "hash", HASH }, { "host", HOST }, { "http", HTTP }, { "https", HTTPS }, @@ -560,19 +989,39 @@ lookup(char *s) { "interface", INTERFACE }, { "interval", INTERVAL }, { "ip", IP }, + { "listen", LISTEN }, + { "loadbalance", LOADBALANCE }, { "log", LOG }, + { "lookup", LOOKUP }, + { "mark", MARK }, + { "marked", MARKED }, + { "nat", NAT }, + { "no", NO }, + { "nodelay", NODELAY }, { "nothing", NOTHING }, + { "on", ON }, { "port", PORT }, + { "prefork", PREFORK }, + { "protocol", PROTO }, { "real", REAL }, + { "relay", RELAY }, + { "remove", REMOVE }, + { "retry", RETRY }, + { "roundrobin", ROUNDROBIN }, + { "sack", SACK }, { "send", SEND }, { "service", SERVICE }, + { "session", SESSION }, + { "socket", SOCKET }, { "ssl", SSL }, { "sticky-address", STICKYADDR }, { "table", TABLE }, { "tag", TAG }, { "tcp", TCP }, { "timeout", TIMEOUT }, + { "to", TO }, { "updates", UPDATES }, + { "url", URL }, { "use", USE }, { "virtual", VIRTUAL } }; @@ -795,16 +1244,30 @@ parse_config(struct hoststated *x_conf, const char *filename, int opts) TAILQ_INIT(&conf->services); TAILQ_INIT(&conf->tables); + TAILQ_INIT(&conf->protos); + TAILQ_INIT(&conf->relays); + memset(&conf->empty_table, 0, sizeof(conf->empty_table)); conf->empty_table.id = EMPTY_TABLE; conf->empty_table.flags |= F_DISABLE; (void)strlcpy(conf->empty_table.name, "empty", sizeof(conf->empty_table.name)); + bzero(&conf->proto_default, sizeof(conf->proto_default)); + conf->proto_default.flags = F_USED; + conf->proto_default.cache = RELAY_CACHESIZE; + conf->proto_default.type = RELAY_PROTO_TCP; + (void)strlcpy(conf->proto_default.name, "default", + sizeof(conf->proto_default.name)); + RB_INIT(&conf->proto_default.tree); + TAILQ_INSERT_TAIL(&conf->protos, &conf->proto_default, entry); + conf->timeout.tv_sec = CHECK_TIMEOUT / 1000; conf->timeout.tv_usec = (CHECK_TIMEOUT % 1000) * 1000; conf->interval.tv_sec = CHECK_INTERVAL; conf->interval.tv_usec = 0; + conf->prefork_relay = RELAY_NUMPROC; + conf->statinterval.tv_sec = RELAY_STATINTERVAL; conf->opts = opts; if ((fin = fopen(filename, "r")) == NULL) { @@ -831,7 +1294,7 @@ parse_config(struct hoststated *x_conf, const char *filename, int opts) } } - if (TAILQ_EMPTY(&conf->services)) { + if (TAILQ_EMPTY(&conf->services) && TAILQ_EMPTY(&conf->relays)) { log_warnx("no services, nothing to do"); errors++; } @@ -854,6 +1317,14 @@ parse_config(struct hoststated *x_conf, const char *filename, int opts) } } + /* Verify that every non-default protocol is used */ + TAILQ_FOREACH(proto, &conf->protos, entry) { + if (!(proto->flags & F_USED)) { + log_warnx("unused protocol: %s", proto->name); + errors++; + } + } + if (errors) { bzero(&conf, sizeof (*conf)); return (-1); |