1*92388deeStb /* $OpenBSD: parse.y,v 1.258 2024/10/28 19:56:18 tb Exp $ */ 2feb9ff76Sreyk 3feb9ff76Sreyk /* 4d535e21cSreyk * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org> 502ced5b1Sreyk * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> 636f5dc5eSpyr * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> 7feb9ff76Sreyk * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 8feb9ff76Sreyk * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> 9feb9ff76Sreyk * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 10feb9ff76Sreyk * Copyright (c) 2001 Markus Friedl. All rights reserved. 11feb9ff76Sreyk * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 12feb9ff76Sreyk * Copyright (c) 2001 Theo de Raadt. All rights reserved. 13feb9ff76Sreyk * 14feb9ff76Sreyk * Permission to use, copy, modify, and distribute this software for any 15feb9ff76Sreyk * purpose with or without fee is hereby granted, provided that the above 16feb9ff76Sreyk * copyright notice and this permission notice appear in all copies. 17feb9ff76Sreyk * 18feb9ff76Sreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 19feb9ff76Sreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 20feb9ff76Sreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 21feb9ff76Sreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 22feb9ff76Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 23feb9ff76Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 24feb9ff76Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 25feb9ff76Sreyk */ 26feb9ff76Sreyk 27feb9ff76Sreyk %{ 28feb9ff76Sreyk #include <sys/types.h> 29feb9ff76Sreyk #include <sys/socket.h> 3020741916Sderaadt #include <sys/stat.h> 31feb9ff76Sreyk #include <sys/queue.h> 3202ced5b1Sreyk #include <sys/ioctl.h> 33f04ff968Sreyk #include <sys/time.h> 34f04ff968Sreyk #include <sys/tree.h> 350ca734d7Sreyk 360ca734d7Sreyk #include <netinet/in.h> 37feb9ff76Sreyk #include <arpa/inet.h> 3868928c43Sderaadt #include <net/if.h> 3968928c43Sderaadt #include <net/pfvar.h> 40f04ff968Sreyk #include <net/route.h> 41feb9ff76Sreyk 421ae60b2aSmartijn #include <agentx.h> 432edd718bSreyk #include <stdint.h> 44feb9ff76Sreyk #include <stdarg.h> 45feb9ff76Sreyk #include <stdio.h> 46f04ff968Sreyk #include <unistd.h> 47f04ff968Sreyk #include <ctype.h> 48f04ff968Sreyk #include <err.h> 49f04ff968Sreyk #include <endian.h> 50f04ff968Sreyk #include <errno.h> 51f04ff968Sreyk #include <limits.h> 52feb9ff76Sreyk #include <netdb.h> 53feb9ff76Sreyk #include <string.h> 54f576f50fSreyk #include <ifaddrs.h> 555ff8dd2eSsthen #include <syslog.h> 56acb89df4Sreyk #include <md5.h> 57feb9ff76Sreyk 58748ceb64Sreyk #include "relayd.h" 59cb8b0e56Sreyk #include "http.h" 60feb9ff76Sreyk 6120741916Sderaadt TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 6220741916Sderaadt static struct file { 6320741916Sderaadt TAILQ_ENTRY(file) entry; 6420741916Sderaadt FILE *stream; 6520741916Sderaadt char *name; 6620331712Sdenis size_t ungetpos; 6720331712Sdenis size_t ungetsize; 6820331712Sdenis u_char *ungetbuf; 6920331712Sdenis int eof_reached; 7020741916Sderaadt int lineno; 7120741916Sderaadt int errors; 72c6004ab9Smpf } *file, *topfile; 7320741916Sderaadt struct file *pushfile(const char *, int); 7420741916Sderaadt int popfile(void); 7520741916Sderaadt int check_file_secrecy(int, const char *); 7620741916Sderaadt int yyparse(void); 7720741916Sderaadt int yylex(void); 783512060dSpatrick int yyerror(const char *, ...) 793512060dSpatrick __attribute__((__format__ (printf, 1, 2))) 803512060dSpatrick __attribute__((__nonnull__ (1))); 8120741916Sderaadt int kw_cmp(const void *, const void *); 8220741916Sderaadt int lookup(char *); 8320331712Sdenis int igetc(void); 8420741916Sderaadt int lgetc(int); 8520331712Sdenis void lungetc(int); 8620741916Sderaadt int findeol(void); 8720741916Sderaadt 8820741916Sderaadt TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 8920741916Sderaadt struct sym { 9020741916Sderaadt TAILQ_ENTRY(sym) entry; 9120741916Sderaadt int used; 9220741916Sderaadt int persist; 9320741916Sderaadt char *nam; 9420741916Sderaadt char *val; 9520741916Sderaadt }; 9620741916Sderaadt int symset(const char *, const char *, int); 9720741916Sderaadt char *symget(const char *); 9820741916Sderaadt 99748ceb64Sreyk struct relayd *conf = NULL; 100feb9ff76Sreyk static int errors = 0; 101a2195becSreyk static int loadcfg = 0; 1029591a9f7Spyr objid_t last_rdr_id = 0; 103feb9ff76Sreyk objid_t last_table_id = 0; 104feb9ff76Sreyk objid_t last_host_id = 0; 1052edd718bSreyk objid_t last_relay_id = 0; 1062edd718bSreyk objid_t last_proto_id = 0; 10734438db4Sreyk objid_t last_rt_id = 0; 10834438db4Sreyk objid_t last_nr_id = 0; 109feb9ff76Sreyk 1109591a9f7Spyr static struct rdr *rdr = NULL; 111feb9ff76Sreyk static struct table *table = NULL; 1122edd718bSreyk static struct relay *rlay = NULL; 1133847fa47Sreyk static struct host *hst = NULL; 1149f29cb9cSreyk struct relaylist relays; 1152edd718bSreyk static struct protocol *proto = NULL; 116cb8b0e56Sreyk static struct relay_rule *rule = NULL; 11734438db4Sreyk static struct router *router = NULL; 118cb8b0e56Sreyk static int label = 0; 119cb8b0e56Sreyk static int tagged = 0; 120cb8b0e56Sreyk static int tag = 0; 121af50a7a9Sreyk static in_port_t tableport = 0; 122416fa9c0Sreyk static int dstmode; 123cb8b0e56Sreyk static enum key_type keytype = KEY_TYPE_NONE; 124cb8b0e56Sreyk static enum direction dir = RELAY_DIR_ANY; 125cb8b0e56Sreyk static char *rulefile = NULL; 126acb89df4Sreyk static union hashkey *hashkey = NULL; 127feb9ff76Sreyk 1284687b7c6Sdenis struct address *host_ip(const char *); 129feb9ff76Sreyk int host_dns(const char *, struct addresslist *, 1304ac0ad23Sreyk int, struct portrange *, const char *, int); 131f576f50fSreyk int host_if(const char *, struct addresslist *, 132f576f50fSreyk int, struct portrange *, const char *, int); 133feb9ff76Sreyk int host(const char *, struct addresslist *, 1344ac0ad23Sreyk int, struct portrange *, const char *, int); 135a2195becSreyk void host_free(struct addresslist *); 136feb9ff76Sreyk 137af50a7a9Sreyk struct table *table_inherit(struct table *); 13800ae3104Sreyk int relay_id(struct relay *); 1399f29cb9cSreyk struct relay *relay_inherit(struct relay *, struct relay *); 140232f7df1Sreyk int getservice(char *); 14102ced5b1Sreyk int is_if_in_group(const char *, const char *); 14299ca6689Sreyk 143feb9ff76Sreyk typedef struct { 144feb9ff76Sreyk union { 145d5d66eaeSderaadt int64_t number; 146feb9ff76Sreyk char *string; 147feb9ff76Sreyk struct host *host; 148fc29228bSreyk struct timeval tv; 149af50a7a9Sreyk struct table *table; 150232f7df1Sreyk struct portrange port; 151acb89df4Sreyk struct { 152acb89df4Sreyk union hashkey key; 153acb89df4Sreyk int keyset; 154acb89df4Sreyk } key; 155cb8b0e56Sreyk enum direction dir; 1567ae0cc5eSreyk struct { 157d1800b62Sreyk struct sockaddr_storage ss; 158860302f3Sreyk int prefixlen; 159e2318a52Sderaadt char name[HOST_NAME_MAX+1]; 160d1800b62Sreyk } addr; 161d1800b62Sreyk struct { 1627ae0cc5eSreyk enum digest_type type; 1637ae0cc5eSreyk char *digest; 1647ae0cc5eSreyk } digest; 165feb9ff76Sreyk } v; 166feb9ff76Sreyk int lineno; 167feb9ff76Sreyk } YYSTYPE; 168feb9ff76Sreyk 169feb9ff76Sreyk %} 170feb9ff76Sreyk 171c26b8e61Smartijn %token AGENTX APPEND BACKLOG BACKUP BINARY BUFFER CA CACHE SET CHECK CIPHERS 172c26b8e61Smartijn %token CODE COOKIE DEMOTE DIGEST DISABLE ERROR EXPECT PASS BLOCK EXTERNAL 173c26b8e61Smartijn %token FILENAME FORWARD FROM HASH HEADER HEADERLEN HOST HTTP ICMP INCLUDE INET 174c26b8e61Smartijn %token INET6 INTERFACE INTERVAL IP KEYPAIR LABEL LISTEN VALUE LOADBALANCE LOG 175c26b8e61Smartijn %token LOOKUP METHOD MODE NAT NO DESTINATION NODELAY NOTHING ON PARENT PATH 176c26b8e61Smartijn %token PFTAG PORT PREFORK PRIORITY PROTO QUERYSTR REAL REDIRECT RELAY REMOVE 177c26b8e61Smartijn %token REQUEST RESPONSE RETRY QUICK RETURN ROUNDROBIN ROUTE SACK SCRIPT SEND 178f71e4cb7Skn %token SESSION SOCKET SPLICE STICKYADDR STRIP STYLE TABLE TAG TAGGED TCP 179235e6337Smartijn %token TIMEOUT TLS TO ROUTER RTLABEL TRANSPARENT URL WITH TTL RTABLE 180353c00bcSclaudio %token MATCH PARAMS RANDOM LEASTSTATES SRCHASH KEY CERTIFICATE PASSWORD ECDHE 181c26b8e61Smartijn %token EDH TICKETS CONNECTION CONNECTIONS CONTEXT ERRORS STATE CHANGES CHECKS 182*92388deeStb %token WEBSOCKETS PFLOG CLIENT 183feb9ff76Sreyk %token <v.string> STRING 1846debaf0dSpyr %token <v.number> NUMBER 185235e6337Smartijn %type <v.string> context hostname interface table value path 186235e6337Smartijn %type <v.number> http_type loglevel quick 187cb8b0e56Sreyk %type <v.number> dstmode flag forwardmode retry 1889c908525Sclaudio %type <v.number> opttls opttlsclient 1897c726e76Ssashan %type <v.number> redirect_proto relay_proto match pflog 190cb8b0e56Sreyk %type <v.number> action ruleaf key_option 191232f7df1Sreyk %type <v.port> port 192feb9ff76Sreyk %type <v.host> host 193860302f3Sreyk %type <v.addr> address rulesrc ruledst addrprefix 194fc29228bSreyk %type <v.tv> timeout 195cb8b0e56Sreyk %type <v.digest> digest optdigest 196af50a7a9Sreyk %type <v.table> tablespec 197cb8b0e56Sreyk %type <v.dir> dir 198acb89df4Sreyk %type <v.key> hashkey 199feb9ff76Sreyk 200feb9ff76Sreyk %% 201feb9ff76Sreyk 202feb9ff76Sreyk grammar : /* empty */ 203386626e8Sreyk | grammar include '\n' 204feb9ff76Sreyk | grammar '\n' 205feb9ff76Sreyk | grammar varset '\n' 206feb9ff76Sreyk | grammar main '\n' 2079591a9f7Spyr | grammar rdr '\n' 208af50a7a9Sreyk | grammar tabledef '\n' 2092edd718bSreyk | grammar relay '\n' 2102edd718bSreyk | grammar proto '\n' 21134438db4Sreyk | grammar router '\n' 21220741916Sderaadt | grammar error '\n' { file->errors++; } 213feb9ff76Sreyk ; 214feb9ff76Sreyk 215386626e8Sreyk include : INCLUDE STRING { 216386626e8Sreyk struct file *nfile; 217386626e8Sreyk 218386626e8Sreyk if ((nfile = pushfile($2, 0)) == NULL) { 219386626e8Sreyk yyerror("failed to include file %s", $2); 220386626e8Sreyk free($2); 221386626e8Sreyk YYERROR; 222386626e8Sreyk } 223386626e8Sreyk free($2); 224386626e8Sreyk 225386626e8Sreyk file = nfile; 226386626e8Sreyk lungetc('\n'); 227386626e8Sreyk } 228976a7bb1Ssthen ; 229386626e8Sreyk 2307bb52228Sreyk opttls : /*empty*/ { $$ = 0; } 231f71e4cb7Skn | TLS { $$ = 1; } 2327bb52228Sreyk ; 2337bb52228Sreyk 2347bb52228Sreyk opttlsclient : /*empty*/ { $$ = 0; } 235f71e4cb7Skn | WITH TLS { $$ = 1; } 23633e8bb87Sreyk ; 23733e8bb87Sreyk 2380ad20b85Sbenno http_type : HTTP { $$ = 0; } 2390ad20b85Sbenno | STRING { 2403e812c37Spyr if (strcmp("https", $1) == 0) { 2413e812c37Spyr $$ = 1; 2423e812c37Spyr } else { 24373de9915Sthib yyerror("invalid check type: %s", $1); 2443e812c37Spyr free($1); 2453e812c37Spyr YYERROR; 2463e812c37Spyr } 2473e812c37Spyr free($1); 2483e812c37Spyr } 2493e812c37Spyr ; 2503e812c37Spyr 25198aa59adSpyr hostname : /* empty */ { 25298aa59adSpyr $$ = strdup(""); 25398aa59adSpyr if ($$ == NULL) 25498aa59adSpyr fatal("calloc"); 25598aa59adSpyr } 25698aa59adSpyr | HOST STRING { 257bec4ed1fSreyk if (asprintf(&$$, "Host: %s\r\nConnection: close\r\n", 258bec4ed1fSreyk $2) == -1) 25998aa59adSpyr fatal("asprintf"); 26098aa59adSpyr } 26198aa59adSpyr ; 26298aa59adSpyr 2634ac0ad23Sreyk relay_proto : /* empty */ { $$ = RELAY_PROTO_TCP; } 2646694a36dSreyk | TCP { $$ = RELAY_PROTO_TCP; } 2650ad20b85Sbenno | HTTP { $$ = RELAY_PROTO_HTTP; } 2663e812c37Spyr | STRING { 2670ad20b85Sbenno if (strcmp("dns", $1) == 0) { 2682380f4f2Sreyk $$ = RELAY_PROTO_DNS; 2693e812c37Spyr } else { 27073de9915Sthib yyerror("invalid protocol type: %s", $1); 2713e812c37Spyr free($1); 2723e812c37Spyr YYERROR; 2733e812c37Spyr } 2743e812c37Spyr free($1); 2753e812c37Spyr } 276e8fb3979Spyr ; 277e8fb3979Spyr 2784ac0ad23Sreyk redirect_proto : /* empty */ { $$ = IPPROTO_TCP; } 2794ac0ad23Sreyk | TCP { $$ = IPPROTO_TCP; } 2804ac0ad23Sreyk | STRING { 2814ac0ad23Sreyk struct protoent *p; 2824ac0ad23Sreyk 2834ac0ad23Sreyk if ((p = getprotobyname($1)) == NULL) { 2844ac0ad23Sreyk yyerror("invalid protocol: %s", $1); 2854ac0ad23Sreyk free($1); 2864ac0ad23Sreyk YYERROR; 2874ac0ad23Sreyk } 2884ac0ad23Sreyk free($1); 2894ac0ad23Sreyk 2904ac0ad23Sreyk $$ = p->p_proto; 2914ac0ad23Sreyk } 2924ac0ad23Sreyk ; 2934ac0ad23Sreyk 29407c84b7eSreyk eflags_l : eflags comma eflags_l 29507c84b7eSreyk | eflags 29607c84b7eSreyk ; 29707c84b7eSreyk 29807c84b7eSreyk opteflags : /* nothing */ 29907c84b7eSreyk | eflags 30007c84b7eSreyk ; 30107c84b7eSreyk 30207c84b7eSreyk eflags : STYLE STRING 30307c84b7eSreyk { 30407c84b7eSreyk if ((proto->style = strdup($2)) == NULL) 30507c84b7eSreyk fatal("out of memory"); 30607c84b7eSreyk free($2); 30707c84b7eSreyk } 30807c84b7eSreyk ; 30907c84b7eSreyk 3100ad20b85Sbenno port : PORT HTTP { 3110ad20b85Sbenno int p = 0; 3120ad20b85Sbenno $$.op = PF_OP_EQ; 3130ad20b85Sbenno if ((p = getservice("http")) == -1) 3140ad20b85Sbenno YYERROR; 3150ad20b85Sbenno $$.val[0] = p; 3160ad20b85Sbenno $$.val[1] = 0; 3170ad20b85Sbenno } 3180ad20b85Sbenno | PORT STRING { 319232f7df1Sreyk char *a, *b; 320232f7df1Sreyk int p[2]; 32119c8d0a5Sreyk 322232f7df1Sreyk p[0] = p[1] = 0; 323232f7df1Sreyk 324232f7df1Sreyk a = $2; 325232f7df1Sreyk b = strchr($2, ':'); 326232f7df1Sreyk if (b == NULL) 327232f7df1Sreyk $$.op = PF_OP_EQ; 328232f7df1Sreyk else { 329232f7df1Sreyk *b++ = '\0'; 330232f7df1Sreyk if ((p[1] = getservice(b)) == -1) { 33119c8d0a5Sreyk free($2); 33219c8d0a5Sreyk YYERROR; 33319c8d0a5Sreyk } 334232f7df1Sreyk $$.op = PF_OP_RRG; 335232f7df1Sreyk } 336232f7df1Sreyk if ((p[0] = getservice(a)) == -1) { 337232f7df1Sreyk free($2); 338232f7df1Sreyk YYERROR; 339232f7df1Sreyk } 340232f7df1Sreyk $$.val[0] = p[0]; 341232f7df1Sreyk $$.val[1] = p[1]; 34219c8d0a5Sreyk free($2); 34319c8d0a5Sreyk } 3446debaf0dSpyr | PORT NUMBER { 3450ead3a23Sflorian if ($2 <= 0 || $2 > (int)USHRT_MAX) { 3463512060dSpatrick yyerror("invalid port: %lld", $2); 3476debaf0dSpyr YYERROR; 3486debaf0dSpyr } 349232f7df1Sreyk $$.val[0] = htons($2); 350232f7df1Sreyk $$.op = PF_OP_EQ; 3516debaf0dSpyr } 35219c8d0a5Sreyk ; 35319c8d0a5Sreyk 354feb9ff76Sreyk varset : STRING '=' STRING { 3550c7b4ca6Sbenno char *s = $1; 3560c7b4ca6Sbenno while (*s++) { 3570c7b4ca6Sbenno if (isspace((unsigned char)*s)) { 3580c7b4ca6Sbenno yyerror("macro name cannot contain " 3590c7b4ca6Sbenno "whitespace"); 36016a0a906Skrw free($1); 36116a0a906Skrw free($3); 3620c7b4ca6Sbenno YYERROR; 3630c7b4ca6Sbenno } 3640c7b4ca6Sbenno } 365feb9ff76Sreyk if (symset($1, $3, 0) == -1) 366feb9ff76Sreyk fatal("cannot store variable"); 367feb9ff76Sreyk free($1); 368feb9ff76Sreyk free($3); 369feb9ff76Sreyk } 370feb9ff76Sreyk ; 371feb9ff76Sreyk 372bf9a553dSreyk sendbuf : NOTHING { 37392b666f1Spyr table->sendbuf = NULL; 374bf9a553dSreyk } 375bf9a553dSreyk | STRING { 37692b666f1Spyr table->sendbuf = strdup($1); 37792b666f1Spyr if (table->sendbuf == NULL) 37892b666f1Spyr fatal("out of memory"); 379bf9a553dSreyk free($1); 380bf9a553dSreyk } 381bf9a553dSreyk ; 382bf9a553dSreyk 3833f229715Srob sendbinbuf : NOTHING { 3843f229715Srob table->sendbinbuf = NULL; 3853f229715Srob } 3863f229715Srob | STRING { 3873f229715Srob if (strlen($1) == 0) { 3883f229715Srob yyerror("empty binary send data"); 3893f229715Srob free($1); 3903f229715Srob YYERROR; 3913f229715Srob } 3923f229715Srob table->sendbuf = strdup($1); 3933f229715Srob if (table->sendbuf == NULL) 3943f229715Srob fatal("out of memory"); 3953f229715Srob table->sendbinbuf = string2binary($1); 3963f229715Srob if (table->sendbinbuf == NULL) 3973f229715Srob fatal("failed in binary send data"); 3983f229715Srob free($1); 3993f229715Srob } 4003f229715Srob ; 4013f229715Srob 4026debaf0dSpyr main : INTERVAL NUMBER { 403586b5f8aSreyk if ((conf->sc_conf.interval.tv_sec = $2) < 0) { 4043512060dSpatrick yyerror("invalid interval: %lld", $2); 4056debaf0dSpyr YYERROR; 4066debaf0dSpyr } 4076debaf0dSpyr } 408a2195becSreyk | LOG loglevel { 409586b5f8aSreyk conf->sc_conf.opts |= $2; 410a2195becSreyk } 411fc29228bSreyk | TIMEOUT timeout { 4122166201eSreyk bcopy(&$2, &conf->sc_conf.timeout, 4132166201eSreyk sizeof(struct timeval)); 414fc29228bSreyk } 4156debaf0dSpyr | PREFORK NUMBER { 4168e01c6e3Sreyk if ($2 <= 0 || $2 > PROC_MAX_INSTANCES) { 4172edd718bSreyk yyerror("invalid number of preforked " 4183512060dSpatrick "relays: %lld", $2); 4192edd718bSreyk YYERROR; 4202edd718bSreyk } 421586b5f8aSreyk conf->sc_conf.prefork_relay = $2; 4222edd718bSreyk } 423c26b8e61Smartijn | AGENTX context path { 424c26b8e61Smartijn conf->sc_conf.flags |= F_AGENTX; 425c26b8e61Smartijn if ($2 != NULL) { 426c26b8e61Smartijn if (strlcpy(conf->sc_conf.agentx_context, $2, 427c26b8e61Smartijn sizeof(conf->sc_conf.agentx_context)) >= 428c26b8e61Smartijn sizeof(conf->sc_conf.agentx_context)) { 429c26b8e61Smartijn yyerror("agentx context too long"); 430c26b8e61Smartijn free($2); 431c26b8e61Smartijn free($3); 432c26b8e61Smartijn YYERROR; 433c26b8e61Smartijn } 434c26b8e61Smartijn free($2); 435c26b8e61Smartijn } else 436c26b8e61Smartijn conf->sc_conf.agentx_context[0] = '\0'; 437c26b8e61Smartijn if ($3 != NULL) { 438c26b8e61Smartijn if (strlcpy(conf->sc_conf.agentx_path, $3, 439c26b8e61Smartijn sizeof(conf->sc_conf.agentx_path)) >= 440c26b8e61Smartijn sizeof(conf->sc_conf.agentx_path)) { 441c26b8e61Smartijn yyerror("agentx path too long"); 442284419ceSreyk free($3); 443284419ceSreyk YYERROR; 444284419ceSreyk } 445284419ceSreyk free($3); 446284419ceSreyk } else 447c26b8e61Smartijn (void)strlcpy(conf->sc_conf.agentx_path, 4481ae60b2aSmartijn AGENTX_MASTER_PATH, 449c26b8e61Smartijn sizeof(conf->sc_conf.agentx_path)); 450c26b8e61Smartijn } 451ec4c1254Sbenno | SOCKET STRING { 452ec4c1254Sbenno conf->sc_ps->ps_csock.cs_name = $2; 453ec4c1254Sbenno } 454feb9ff76Sreyk ; 455feb9ff76Sreyk 456c26b8e61Smartijn path : /* nothing */ { $$ = NULL; } 457c26b8e61Smartijn | PATH STRING { $$ = $2; } 458c26b8e61Smartijn 459c26b8e61Smartijn context : /* nothing */ { $$ = NULL; } 460c26b8e61Smartijn | CONTEXT STRING { $$ = $2; } 461c26b8e61Smartijn 462213c5bf7Sbenno loglevel : STATE CHANGES { $$ = RELAYD_OPT_LOGUPDATE; } 4630be9d00aSbenno | HOST CHECKS { $$ = RELAYD_OPT_LOGHOSTCHECK; } 4640be9d00aSbenno | CONNECTION { $$ = (RELAYD_OPT_LOGCON | 4650be9d00aSbenno RELAYD_OPT_LOGCONERR); } 4660be9d00aSbenno | CONNECTION ERRORS { $$ = RELAYD_OPT_LOGCONERR; } 467609cf3a7Sreyk ; 468609cf3a7Sreyk 4699591a9f7Spyr rdr : REDIRECT STRING { 4709591a9f7Spyr struct rdr *srv; 471feb9ff76Sreyk 472586b5f8aSreyk conf->sc_conf.flags |= F_NEEDPF; 473a2195becSreyk 474a2195becSreyk if (!loadcfg) { 475a2195becSreyk free($2); 476a2195becSreyk YYACCEPT; 477a2195becSreyk } 478a2195becSreyk 47935d10c30Sreyk TAILQ_FOREACH(srv, conf->sc_rdrs, entry) 48068b79041Spyr if (!strcmp(srv->conf.name, $2)) 481feb9ff76Sreyk break; 482feb9ff76Sreyk if (srv != NULL) { 483af50a7a9Sreyk yyerror("redirection %s defined twice", $2); 484feb9ff76Sreyk free($2); 485feb9ff76Sreyk YYERROR; 486feb9ff76Sreyk } 487feb9ff76Sreyk if ((srv = calloc(1, sizeof (*srv))) == NULL) 488feb9ff76Sreyk fatal("out of memory"); 489feb9ff76Sreyk 49068b79041Spyr if (strlcpy(srv->conf.name, $2, 49168b79041Spyr sizeof(srv->conf.name)) >= 49268b79041Spyr sizeof(srv->conf.name)) { 4932b4fa706Stb yyerror("redirection name truncated: %s", $2); 494e28634eaSreyk free($2); 495d65de6b8Spyr free(srv); 496feb9ff76Sreyk YYERROR; 497feb9ff76Sreyk } 498feb9ff76Sreyk free($2); 499a1a58453Sreyk srv->conf.id = ++last_rdr_id; 5000eb8c740Sreyk srv->conf.timeout.tv_sec = RELAY_TIMEOUT; 5019591a9f7Spyr if (last_rdr_id == INT_MAX) { 502af50a7a9Sreyk yyerror("too many redirections defined"); 503d65de6b8Spyr free(srv); 504feb9ff76Sreyk YYERROR; 505feb9ff76Sreyk } 5069591a9f7Spyr rdr = srv; 5079591a9f7Spyr } '{' optnl rdropts_l '}' { 5089591a9f7Spyr if (rdr->table == NULL) { 509af50a7a9Sreyk yyerror("redirection %s has no table", 5109591a9f7Spyr rdr->conf.name); 511feb9ff76Sreyk YYERROR; 512feb9ff76Sreyk } 5139591a9f7Spyr if (TAILQ_EMPTY(&rdr->virts)) { 514af50a7a9Sreyk yyerror("redirection %s has no virtual ip", 5159591a9f7Spyr rdr->conf.name); 516feb9ff76Sreyk YYERROR; 517feb9ff76Sreyk } 51835d10c30Sreyk conf->sc_rdrcount++; 5199591a9f7Spyr if (rdr->backup == NULL) { 5209591a9f7Spyr rdr->conf.backup_id = 52135d10c30Sreyk conf->sc_empty_table.conf.id; 52235d10c30Sreyk rdr->backup = &conf->sc_empty_table; 5239591a9f7Spyr } else if (rdr->backup->conf.port != 5249591a9f7Spyr rdr->table->conf.port) { 525af50a7a9Sreyk yyerror("redirection %s uses two different " 526af50a7a9Sreyk "ports for its table and backup table", 5279591a9f7Spyr rdr->conf.name); 528feb9ff76Sreyk YYERROR; 529feb9ff76Sreyk } 5309591a9f7Spyr if (!(rdr->conf.flags & F_DISABLE)) 5319591a9f7Spyr rdr->conf.flags |= F_ADD; 532041405e3Sreyk TAILQ_INSERT_TAIL(conf->sc_rdrs, rdr, entry); 533af50a7a9Sreyk tableport = 0; 5349591a9f7Spyr rdr = NULL; 535feb9ff76Sreyk } 536feb9ff76Sreyk ; 537feb9ff76Sreyk 5389591a9f7Spyr rdropts_l : rdropts_l rdroptsl nl 5399591a9f7Spyr | rdroptsl optnl 540feb9ff76Sreyk ; 541feb9ff76Sreyk 5420eb8c740Sreyk rdroptsl : forwardmode TO tablespec interface { 543acb89df4Sreyk if (hashkey != NULL) { 5446933853bSreyk memcpy(&rdr->conf.key, 5456933853bSreyk hashkey, sizeof(rdr->conf.key)); 5466933853bSreyk rdr->conf.flags |= F_HASHKEY; 547acb89df4Sreyk free(hashkey); 548acb89df4Sreyk hashkey = NULL; 549acb89df4Sreyk } 550acb89df4Sreyk 5513c03a838Sreyk switch ($1) { 5523c03a838Sreyk case FWD_NORMAL: 5533c03a838Sreyk if ($4 == NULL) 5543c03a838Sreyk break; 5553c03a838Sreyk yyerror("superfluous interface"); 556e28634eaSreyk free($4); 5573c03a838Sreyk YYERROR; 5583c03a838Sreyk case FWD_ROUTE: 5593c03a838Sreyk if ($4 != NULL) 5603c03a838Sreyk break; 5613c03a838Sreyk yyerror("missing interface to route to"); 562e28634eaSreyk free($4); 5633c03a838Sreyk YYERROR; 5643c03a838Sreyk case FWD_TRANS: 5653c03a838Sreyk yyerror("no transparent forward here"); 566c07ec172Sreyk if ($4 != NULL) 567c07ec172Sreyk free($4); 5683c03a838Sreyk YYERROR; 5693c03a838Sreyk } 5700eb8c740Sreyk if ($4 != NULL) { 571c07ec172Sreyk if (strlcpy($3->conf.ifname, $4, 572c07ec172Sreyk sizeof($3->conf.ifname)) >= 573c07ec172Sreyk sizeof($3->conf.ifname)) { 574c07ec172Sreyk yyerror("interface name truncated"); 575c07ec172Sreyk free($4); 576c07ec172Sreyk YYERROR; 577c07ec172Sreyk } 5780eb8c740Sreyk free($4); 5790eb8c740Sreyk } 5800eb8c740Sreyk 581af50a7a9Sreyk if ($3->conf.check == CHECK_NOCHECK) { 582af50a7a9Sreyk yyerror("table %s has no check", $3->conf.name); 5832c8c2287Sclaudio purge_table(conf, conf->sc_tables, $3); 584feb9ff76Sreyk YYERROR; 58599ca6689Sreyk } 5869591a9f7Spyr if (rdr->backup) { 587af50a7a9Sreyk yyerror("only one backup table is allowed"); 5882c8c2287Sclaudio purge_table(conf, conf->sc_tables, $3); 589feb9ff76Sreyk YYERROR; 590feb9ff76Sreyk } 5919591a9f7Spyr if (rdr->table) { 5929591a9f7Spyr rdr->backup = $3; 5939591a9f7Spyr rdr->conf.backup_id = $3->conf.id; 59459e187d0Sreyk if (dstmode != rdr->conf.mode) { 59559e187d0Sreyk yyerror("backup table for %s with " 59659e187d0Sreyk "different mode", rdr->conf.name); 59759e187d0Sreyk YYERROR; 59859e187d0Sreyk } 599af50a7a9Sreyk } else { 6009591a9f7Spyr rdr->table = $3; 6019591a9f7Spyr rdr->conf.table_id = $3->conf.id; 60259e187d0Sreyk rdr->conf.mode = dstmode; 60399ca6689Sreyk } 6043c03a838Sreyk $3->conf.fwdmode = $1; 6059591a9f7Spyr $3->conf.rdrid = rdr->conf.id; 606ea03d477Sreyk $3->conf.flags |= F_USED; 607feb9ff76Sreyk } 6087c726e76Ssashan | LISTEN ON STRING redirect_proto port interface pflog { 6099591a9f7Spyr if (host($3, &rdr->virts, 6104ac0ad23Sreyk SRV_MAX_VIRTS, &$5, $6, $4) <= 0) { 611feb9ff76Sreyk yyerror("invalid virtual ip: %s", $3); 612feb9ff76Sreyk free($3); 6134ac0ad23Sreyk free($6); 614feb9ff76Sreyk YYERROR; 615feb9ff76Sreyk } 616feb9ff76Sreyk free($3); 6174ac0ad23Sreyk free($6); 6189591a9f7Spyr if (rdr->conf.port == 0) 6194ac0ad23Sreyk rdr->conf.port = $5.val[0]; 6209591a9f7Spyr tableport = rdr->conf.port; 6217c726e76Ssashan if ($7) 6227c726e76Ssashan rdr->conf.flags |= F_PFLOG; 623feb9ff76Sreyk } 6249591a9f7Spyr | DISABLE { rdr->conf.flags |= F_DISABLE; } 6259591a9f7Spyr | STICKYADDR { rdr->conf.flags |= F_STICKY; } 626cb8b0e56Sreyk | match PFTAG STRING { 627586b5f8aSreyk conf->sc_conf.flags |= F_NEEDPF; 628e48c97edSreyk if (strlcpy(rdr->conf.tag, $3, 6299591a9f7Spyr sizeof(rdr->conf.tag)) >= 6309591a9f7Spyr sizeof(rdr->conf.tag)) { 6312b4fa706Stb yyerror("redirection tag name truncated: %s", 6322b4fa706Stb $3); 633e48c97edSreyk free($3); 634feb9ff76Sreyk YYERROR; 635feb9ff76Sreyk } 636e48c97edSreyk if ($1) 637e48c97edSreyk rdr->conf.flags |= F_MATCH; 638e48c97edSreyk free($3); 639feb9ff76Sreyk } 6400eb8c740Sreyk | SESSION TIMEOUT NUMBER { 6410eb8c740Sreyk if ((rdr->conf.timeout.tv_sec = $3) < 0) { 642b02f4fdbSbenno yyerror("invalid timeout: %lld", $3); 643b02f4fdbSbenno YYERROR; 644b02f4fdbSbenno } 645b02f4fdbSbenno if (rdr->conf.timeout.tv_sec > INT_MAX) { 646b02f4fdbSbenno yyerror("timeout too large: %lld", $3); 6470eb8c740Sreyk YYERROR; 6480eb8c740Sreyk } 6490eb8c740Sreyk } 65059a200f9Sreyk | include 651feb9ff76Sreyk ; 652feb9ff76Sreyk 653e48c97edSreyk match : /* empty */ { $$ = 0; } 654e48c97edSreyk | MATCH { $$ = 1; } 655e48c97edSreyk ; 656e48c97edSreyk 6577c726e76Ssashan pflog : /* empty */ { $$ = 0; } 6587c726e76Ssashan | PFLOG { $$ = 1; } 6597c726e76Ssashan ; 6607c726e76Ssashan 6613c03a838Sreyk forwardmode : FORWARD { $$ = FWD_NORMAL; } 6623c03a838Sreyk | ROUTE { $$ = FWD_ROUTE; } 6633c03a838Sreyk | TRANSPARENT FORWARD { $$ = FWD_TRANS; } 6640eb8c740Sreyk ; 6650eb8c740Sreyk 666af50a7a9Sreyk table : '<' STRING '>' { 667af50a7a9Sreyk if (strlen($2) >= TABLE_NAME_SIZE) { 668af50a7a9Sreyk yyerror("invalid table name"); 669af50a7a9Sreyk free($2); 670af50a7a9Sreyk YYERROR; 671af50a7a9Sreyk } 672af50a7a9Sreyk $$ = $2; 673af50a7a9Sreyk } 674af50a7a9Sreyk ; 675af50a7a9Sreyk 676af50a7a9Sreyk tabledef : TABLE table { 677feb9ff76Sreyk struct table *tb; 678feb9ff76Sreyk 679a2195becSreyk if (!loadcfg) { 680a2195becSreyk free($2); 681a2195becSreyk YYACCEPT; 682a2195becSreyk } 683a2195becSreyk 68435d10c30Sreyk TAILQ_FOREACH(tb, conf->sc_tables, entry) 68568b79041Spyr if (!strcmp(tb->conf.name, $2)) 686feb9ff76Sreyk break; 687feb9ff76Sreyk if (tb != NULL) { 68873de9915Sthib yyerror("table %s defined twice", $2); 689feb9ff76Sreyk free($2); 690feb9ff76Sreyk YYERROR; 691feb9ff76Sreyk } 692feb9ff76Sreyk 693feb9ff76Sreyk if ((tb = calloc(1, sizeof (*tb))) == NULL) 694feb9ff76Sreyk fatal("out of memory"); 695feb9ff76Sreyk 696c07ec172Sreyk if (strlcpy(tb->conf.name, $2, 697c07ec172Sreyk sizeof(tb->conf.name)) >= sizeof(tb->conf.name)) { 698c07ec172Sreyk yyerror("table name truncated"); 699c07ec172Sreyk free($2); 700c07ec172Sreyk YYERROR; 701c07ec172Sreyk } 702af50a7a9Sreyk free($2); 703af50a7a9Sreyk 704a1a58453Sreyk tb->conf.id = 0; /* will be set later */ 705586b5f8aSreyk bcopy(&conf->sc_conf.timeout, &tb->conf.timeout, 706490ab404Sreyk sizeof(struct timeval)); 7076e16db00Sreyk TAILQ_INIT(&tb->hosts); 708feb9ff76Sreyk table = tb; 709416fa9c0Sreyk dstmode = RELAY_DSTMODE_DEFAULT; 710af50a7a9Sreyk } tabledefopts_l { 711feb9ff76Sreyk if (TAILQ_EMPTY(&table->hosts)) { 71268b79041Spyr yyerror("table %s has no hosts", 71368b79041Spyr table->conf.name); 714feb9ff76Sreyk YYERROR; 715feb9ff76Sreyk } 71635d10c30Sreyk conf->sc_tablecount++; 7176e16db00Sreyk TAILQ_INSERT_TAIL(conf->sc_tables, table, entry); 718feb9ff76Sreyk } 719feb9ff76Sreyk ; 720feb9ff76Sreyk 721af50a7a9Sreyk tabledefopts_l : tabledefopts_l tabledefopts 722af50a7a9Sreyk | tabledefopts 723feb9ff76Sreyk ; 724feb9ff76Sreyk 725af50a7a9Sreyk tabledefopts : DISABLE { table->conf.flags |= F_DISABLE; } 726af50a7a9Sreyk | '{' optnl tablelist_l '}' 727af50a7a9Sreyk ; 728af50a7a9Sreyk 729af50a7a9Sreyk tablelist_l : tablelist comma tablelist_l 730af50a7a9Sreyk | tablelist optnl 731af50a7a9Sreyk ; 732af50a7a9Sreyk 733af50a7a9Sreyk tablelist : host { 73468b79041Spyr $1->conf.tableid = table->conf.id; 73568b79041Spyr $1->tablename = table->conf.name; 7366e16db00Sreyk TAILQ_INSERT_TAIL(&table->hosts, $1, entry); 737feb9ff76Sreyk } 738af50a7a9Sreyk | include 739af50a7a9Sreyk ; 740af50a7a9Sreyk 741af50a7a9Sreyk tablespec : table { 742af50a7a9Sreyk struct table *tb; 743af50a7a9Sreyk if ((tb = calloc(1, sizeof (*tb))) == NULL) 744af50a7a9Sreyk fatal("out of memory"); 745c07ec172Sreyk if (strlcpy(tb->conf.name, $1, 746c07ec172Sreyk sizeof(tb->conf.name)) >= sizeof(tb->conf.name)) { 747c07ec172Sreyk yyerror("table name truncated"); 748c07ec172Sreyk free($1); 749c07ec172Sreyk YYERROR; 750c07ec172Sreyk } 751af50a7a9Sreyk free($1); 752af50a7a9Sreyk table = tb; 753cb8b0e56Sreyk dstmode = RELAY_DSTMODE_DEFAULT; 754acb89df4Sreyk hashkey = NULL; 755af50a7a9Sreyk } tableopts_l { 756af50a7a9Sreyk struct table *tb; 757af50a7a9Sreyk if (table->conf.port == 0) 758af50a7a9Sreyk table->conf.port = tableport; 759232f7df1Sreyk else 760232f7df1Sreyk table->conf.flags |= F_PORT; 761af50a7a9Sreyk if ((tb = table_inherit(table)) == NULL) 762af50a7a9Sreyk YYERROR; 763af50a7a9Sreyk $$ = tb; 764af50a7a9Sreyk } 765af50a7a9Sreyk ; 766af50a7a9Sreyk 767af50a7a9Sreyk tableopts_l : tableopts tableopts_l 768af50a7a9Sreyk | tableopts 769af50a7a9Sreyk ; 770af50a7a9Sreyk 771af50a7a9Sreyk tableopts : CHECK tablecheck 772232f7df1Sreyk | port { 773232f7df1Sreyk if ($1.op != PF_OP_EQ) { 774232f7df1Sreyk yyerror("invalid port"); 775232f7df1Sreyk YYERROR; 776232f7df1Sreyk } 777232f7df1Sreyk table->conf.port = $1.val[0]; 778232f7df1Sreyk } 779fc29228bSreyk | TIMEOUT timeout { 78068b79041Spyr bcopy(&$2, &table->conf.timeout, 78168b79041Spyr sizeof(struct timeval)); 782feb9ff76Sreyk } 7832edd718bSreyk | DEMOTE STRING { 78468b79041Spyr table->conf.flags |= F_DEMOTE; 78568b79041Spyr if (strlcpy(table->conf.demote_group, $2, 78668b79041Spyr sizeof(table->conf.demote_group)) 78768b79041Spyr >= sizeof(table->conf.demote_group)) { 7882edd718bSreyk yyerror("yyparse: demote group name too long"); 7892edd718bSreyk free($2); 7902edd718bSreyk YYERROR; 7912edd718bSreyk } 7922edd718bSreyk free($2); 79368b79041Spyr if (carp_demote_init(table->conf.demote_group, 1) 79468b79041Spyr == -1) { 7952a271b1fSpyr yyerror("yyparse: error initializing group " 79668b79041Spyr "'%s'", table->conf.demote_group); 7972edd718bSreyk YYERROR; 7982edd718bSreyk } 7992edd718bSreyk } 80018de159aSpyr | INTERVAL NUMBER { 801586b5f8aSreyk if ($2 < conf->sc_conf.interval.tv_sec || 802586b5f8aSreyk $2 % conf->sc_conf.interval.tv_sec) { 80318de159aSpyr yyerror("table interval must be " 80418de159aSpyr "divisible by global interval"); 80518de159aSpyr YYERROR; 80618de159aSpyr } 807820dd719Sreyk table->conf.skip_cnt = 808586b5f8aSreyk ($2 / conf->sc_conf.interval.tv_sec) - 1; 80918de159aSpyr } 810acb89df4Sreyk | MODE dstmode hashkey { 811af50a7a9Sreyk switch ($2) { 812af50a7a9Sreyk case RELAY_DSTMODE_LOADBALANCE: 813af50a7a9Sreyk case RELAY_DSTMODE_HASH: 81459e187d0Sreyk case RELAY_DSTMODE_SRCHASH: 815acb89df4Sreyk if (hashkey != NULL) { 816acb89df4Sreyk yyerror("key already specified"); 817acb89df4Sreyk free(hashkey); 818acb89df4Sreyk YYERROR; 819acb89df4Sreyk } 820acb89df4Sreyk if ((hashkey = calloc(1, 821acb89df4Sreyk sizeof(*hashkey))) == NULL) 822acb89df4Sreyk fatal("out of memory"); 823acb89df4Sreyk memcpy(hashkey, &$3.key, sizeof(*hashkey)); 824acb89df4Sreyk break; 825acb89df4Sreyk default: 826acb89df4Sreyk if ($3.keyset) { 827acb89df4Sreyk yyerror("key not supported by mode"); 828acb89df4Sreyk YYERROR; 829acb89df4Sreyk } 830acb89df4Sreyk hashkey = NULL; 831acb89df4Sreyk break; 832acb89df4Sreyk } 833acb89df4Sreyk 834acb89df4Sreyk switch ($2) { 835acb89df4Sreyk case RELAY_DSTMODE_LOADBALANCE: 836acb89df4Sreyk case RELAY_DSTMODE_HASH: 8379591a9f7Spyr if (rdr != NULL) { 838af50a7a9Sreyk yyerror("mode not supported " 839af50a7a9Sreyk "for redirections"); 840af50a7a9Sreyk YYERROR; 841af50a7a9Sreyk } 842af50a7a9Sreyk /* FALLTHROUGH */ 8436933853bSreyk case RELAY_DSTMODE_RANDOM: 844af50a7a9Sreyk case RELAY_DSTMODE_ROUNDROBIN: 8456933853bSreyk case RELAY_DSTMODE_SRCHASH: 846416fa9c0Sreyk dstmode = $2; 847af50a7a9Sreyk break; 84859e187d0Sreyk case RELAY_DSTMODE_LEASTSTATES: 84959e187d0Sreyk if (rdr == NULL) { 85059e187d0Sreyk yyerror("mode not supported " 85159e187d0Sreyk "for relays"); 85259e187d0Sreyk YYERROR; 85359e187d0Sreyk } 85459e187d0Sreyk dstmode = $2; 85559e187d0Sreyk break; 856af50a7a9Sreyk } 857af50a7a9Sreyk } 858af50a7a9Sreyk ; 859af50a7a9Sreyk 860acb89df4Sreyk /* should be in sync with sbin/pfctl/parse.y's hashkey */ 861acb89df4Sreyk hashkey : /* empty */ { 862acb89df4Sreyk $$.keyset = 0; 863acb89df4Sreyk $$.key.data[0] = arc4random(); 864acb89df4Sreyk $$.key.data[1] = arc4random(); 865acb89df4Sreyk $$.key.data[2] = arc4random(); 866acb89df4Sreyk $$.key.data[3] = arc4random(); 867acb89df4Sreyk } 868acb89df4Sreyk | STRING { 869acb89df4Sreyk /* manual key configuration */ 870acb89df4Sreyk $$.keyset = 1; 871acb89df4Sreyk 872acb89df4Sreyk if (!strncmp($1, "0x", 2)) { 873acb89df4Sreyk if (strlen($1) != 34) { 874acb89df4Sreyk free($1); 875acb89df4Sreyk yyerror("hex key must be 128 bits " 876acb89df4Sreyk "(32 hex digits) long"); 877acb89df4Sreyk YYERROR; 878acb89df4Sreyk } 879acb89df4Sreyk 880acb89df4Sreyk if (sscanf($1, "0x%8x%8x%8x%8x", 881acb89df4Sreyk &$$.key.data[0], &$$.key.data[1], 882acb89df4Sreyk &$$.key.data[2], &$$.key.data[3]) != 4) { 883acb89df4Sreyk free($1); 884acb89df4Sreyk yyerror("invalid hex key"); 885acb89df4Sreyk YYERROR; 886acb89df4Sreyk } 887acb89df4Sreyk } else { 888acb89df4Sreyk MD5_CTX context; 889acb89df4Sreyk 890acb89df4Sreyk MD5Init(&context); 891acb89df4Sreyk MD5Update(&context, (unsigned char *)$1, 892acb89df4Sreyk strlen($1)); 893acb89df4Sreyk MD5Final((unsigned char *)$$.key.data, 894acb89df4Sreyk &context); 895acb89df4Sreyk HTONL($$.key.data[0]); 896acb89df4Sreyk HTONL($$.key.data[1]); 897acb89df4Sreyk HTONL($$.key.data[2]); 898acb89df4Sreyk HTONL($$.key.data[3]); 899acb89df4Sreyk } 900acb89df4Sreyk free($1); 901acb89df4Sreyk } 902acb89df4Sreyk ; 903acb89df4Sreyk 904af50a7a9Sreyk tablecheck : ICMP { table->conf.check = CHECK_ICMP; } 905af50a7a9Sreyk | TCP { table->conf.check = CHECK_TCP; } 906f71e4cb7Skn | TLS { 907af50a7a9Sreyk table->conf.check = CHECK_TCP; 908586b5f8aSreyk conf->sc_conf.flags |= F_TLS; 9097bb52228Sreyk table->conf.flags |= F_TLS; 910af50a7a9Sreyk } 911af50a7a9Sreyk | http_type STRING hostname CODE NUMBER { 912af50a7a9Sreyk if ($1) { 913586b5f8aSreyk conf->sc_conf.flags |= F_TLS; 9147bb52228Sreyk table->conf.flags |= F_TLS; 915af50a7a9Sreyk } 916af50a7a9Sreyk table->conf.check = CHECK_HTTP_CODE; 917af50a7a9Sreyk if ((table->conf.retcode = $5) <= 0) { 9183512060dSpatrick yyerror("invalid HTTP code: %lld", $5); 919af50a7a9Sreyk free($2); 920af50a7a9Sreyk free($3); 921af50a7a9Sreyk YYERROR; 922af50a7a9Sreyk } 923af50a7a9Sreyk if (asprintf(&table->sendbuf, 924f04e7030Sreyk "HEAD %s HTTP/1.%c\r\n%s\r\n", 925f04e7030Sreyk $2, strlen($3) ? '1' : '0', $3) == -1) 926af50a7a9Sreyk fatal("asprintf"); 927af50a7a9Sreyk free($2); 928af50a7a9Sreyk free($3); 929af50a7a9Sreyk if (table->sendbuf == NULL) 930af50a7a9Sreyk fatal("out of memory"); 931af50a7a9Sreyk } 932af50a7a9Sreyk | http_type STRING hostname digest { 933af50a7a9Sreyk if ($1) { 934586b5f8aSreyk conf->sc_conf.flags |= F_TLS; 9357bb52228Sreyk table->conf.flags |= F_TLS; 936af50a7a9Sreyk } 937af50a7a9Sreyk table->conf.check = CHECK_HTTP_DIGEST; 938af50a7a9Sreyk if (asprintf(&table->sendbuf, 939f04e7030Sreyk "GET %s HTTP/1.%c\r\n%s\r\n", 940f04e7030Sreyk $2, strlen($3) ? '1' : '0', $3) == -1) 941af50a7a9Sreyk fatal("asprintf"); 942af50a7a9Sreyk free($2); 943af50a7a9Sreyk free($3); 944af50a7a9Sreyk if (table->sendbuf == NULL) 945af50a7a9Sreyk fatal("out of memory"); 946c07ec172Sreyk if (strlcpy(table->conf.digest, $4.digest, 947c07ec172Sreyk sizeof(table->conf.digest)) >= 948c07ec172Sreyk sizeof(table->conf.digest)) { 949c07ec172Sreyk yyerror("digest truncated"); 950c07ec172Sreyk free($4.digest); 951c07ec172Sreyk YYERROR; 952c07ec172Sreyk } 953af50a7a9Sreyk table->conf.digest_type = $4.type; 954af50a7a9Sreyk free($4.digest); 955af50a7a9Sreyk } 9567bb52228Sreyk | SEND sendbuf EXPECT STRING opttls { 957af50a7a9Sreyk table->conf.check = CHECK_SEND_EXPECT; 958af50a7a9Sreyk if ($5) { 959586b5f8aSreyk conf->sc_conf.flags |= F_TLS; 9607bb52228Sreyk table->conf.flags |= F_TLS; 961af50a7a9Sreyk } 962af50a7a9Sreyk if (strlcpy(table->conf.exbuf, $4, 963af50a7a9Sreyk sizeof(table->conf.exbuf)) 964af50a7a9Sreyk >= sizeof(table->conf.exbuf)) { 965af50a7a9Sreyk yyerror("yyparse: expect buffer truncated"); 966af50a7a9Sreyk free($4); 967af50a7a9Sreyk YYERROR; 968af50a7a9Sreyk } 969af50a7a9Sreyk translate_string(table->conf.exbuf); 970af50a7a9Sreyk free($4); 971af50a7a9Sreyk } 9723f229715Srob | BINARY SEND sendbinbuf EXPECT STRING opttls { 9733f229715Srob table->conf.check = CHECK_BINSEND_EXPECT; 9743f229715Srob if ($6) { 9753f229715Srob conf->sc_conf.flags |= F_TLS; 9763f229715Srob table->conf.flags |= F_TLS; 9773f229715Srob } 9783f229715Srob if (strlen($5) == 0) { 9793f229715Srob yyerror("empty binary expect data"); 9803f229715Srob free($5); 9813f229715Srob YYERROR; 9823f229715Srob } 9833f229715Srob if (strlcpy(table->conf.exbuf, $5, 9843f229715Srob sizeof(table->conf.exbuf)) 9853f229715Srob >= sizeof(table->conf.exbuf)) { 9863f229715Srob yyerror("expect buffer truncated"); 9873f229715Srob free($5); 9883f229715Srob YYERROR; 9893f229715Srob } 9903f229715Srob struct ibuf *ibuf = string2binary($5); 9913f229715Srob if (ibuf == NULL) { 9923f229715Srob yyerror("failed in binary expect data buffer"); 9933f229715Srob ibuf_free(ibuf); 9943f229715Srob free($5); 9953f229715Srob YYERROR; 9963f229715Srob } 997bce5c5ddSclaudio memcpy(table->conf.exbinbuf, ibuf_data(ibuf), 9983f229715Srob ibuf_size(ibuf)); 9993f229715Srob ibuf_free(ibuf); 10003f229715Srob free($5); 10013f229715Srob } 1002af50a7a9Sreyk | SCRIPT STRING { 1003af50a7a9Sreyk table->conf.check = CHECK_SCRIPT; 1004af50a7a9Sreyk if (strlcpy(table->conf.path, $2, 1005af50a7a9Sreyk sizeof(table->conf.path)) >= 1006af50a7a9Sreyk sizeof(table->conf.path)) { 1007af50a7a9Sreyk yyerror("script path truncated"); 1008af50a7a9Sreyk free($2); 1009af50a7a9Sreyk YYERROR; 1010af50a7a9Sreyk } 1011586b5f8aSreyk conf->sc_conf.flags |= F_SCRIPT; 1012af50a7a9Sreyk free($2); 1013af50a7a9Sreyk } 1014feb9ff76Sreyk ; 1015feb9ff76Sreyk 10167ae0cc5eSreyk digest : DIGEST STRING 10177ae0cc5eSreyk { 10187ae0cc5eSreyk switch (strlen($2)) { 10197ae0cc5eSreyk case 40: 10207ae0cc5eSreyk $$.type = DIGEST_SHA1; 10217ae0cc5eSreyk break; 10227ae0cc5eSreyk case 32: 10237ae0cc5eSreyk $$.type = DIGEST_MD5; 10247ae0cc5eSreyk break; 10257ae0cc5eSreyk default: 10267ae0cc5eSreyk yyerror("invalid http digest"); 10277ae0cc5eSreyk free($2); 10287ae0cc5eSreyk YYERROR; 10297ae0cc5eSreyk } 10307ae0cc5eSreyk $$.digest = $2; 10317ae0cc5eSreyk } 10327ae0cc5eSreyk ; 10337ae0cc5eSreyk 1034cb8b0e56Sreyk optdigest : digest { 1035cb8b0e56Sreyk $$.digest = $1.digest; 1036cb8b0e56Sreyk $$.type = $1.type; 1037cb8b0e56Sreyk } 1038cb8b0e56Sreyk | STRING { 1039cb8b0e56Sreyk $$.digest = $1; 1040cb8b0e56Sreyk $$.type = DIGEST_NONE; 1041cb8b0e56Sreyk } 1042cb8b0e56Sreyk ; 1043cb8b0e56Sreyk 10444ac0ad23Sreyk proto : relay_proto PROTO STRING { 10452edd718bSreyk struct protocol *p; 10462edd718bSreyk 1047a2195becSreyk if (!loadcfg) { 1048a2195becSreyk free($3); 1049a2195becSreyk YYACCEPT; 1050a2195becSreyk } 1051a2195becSreyk 1052af50a7a9Sreyk if (strcmp($3, "default") == 0) { 105335d10c30Sreyk p = &conf->sc_proto_default; 105483ffe365Spyr } else { 105535d10c30Sreyk TAILQ_FOREACH(p, conf->sc_protos, entry) 1056af50a7a9Sreyk if (!strcmp(p->name, $3)) 10572edd718bSreyk break; 105883ffe365Spyr } 10592edd718bSreyk if (p != NULL) { 1060af50a7a9Sreyk yyerror("protocol %s defined twice", $3); 1061af50a7a9Sreyk free($3); 10622edd718bSreyk YYERROR; 10632edd718bSreyk } 10642edd718bSreyk if ((p = calloc(1, sizeof (*p))) == NULL) 10652edd718bSreyk fatal("out of memory"); 10662edd718bSreyk 1067af50a7a9Sreyk if (strlcpy(p->name, $3, sizeof(p->name)) >= 10682edd718bSreyk sizeof(p->name)) { 10692edd718bSreyk yyerror("protocol name truncated"); 1070e28634eaSreyk free($3); 1071d65de6b8Spyr free(p); 10722edd718bSreyk YYERROR; 10732edd718bSreyk } 1074af50a7a9Sreyk free($3); 1075a1a58453Sreyk p->id = ++last_proto_id; 1076af50a7a9Sreyk p->type = $1; 1077dae1a80bSreyk p->tcpflags = TCPFLAG_DEFAULT; 10787bb52228Sreyk p->tlsflags = TLSFLAG_DEFAULT; 1079dae1a80bSreyk p->tcpbacklog = RELAY_BACKLOG; 10800ad20b85Sbenno p->httpheaderlen = RELAY_DEFHEADERLENGTH; 1081cb8b0e56Sreyk TAILQ_INIT(&p->rules); 10823ca2f577Sreyk TAILQ_INIT(&p->tlscerts); 10837bb52228Sreyk (void)strlcpy(p->tlsciphers, TLSCIPHERS_DEFAULT, 10847bb52228Sreyk sizeof(p->tlsciphers)); 1085353c00bcSclaudio (void)strlcpy(p->tlsecdhecurves, TLSECDHECURVES_DEFAULT, 1086353c00bcSclaudio sizeof(p->tlsecdhecurves)); 108785e5f500Sclaudio (void)strlcpy(p->tlsdhparams, TLSDHPARAM_DEFAULT, 108885e5f500Sclaudio sizeof(p->tlsdhparams)); 10892edd718bSreyk if (last_proto_id == INT_MAX) { 10902edd718bSreyk yyerror("too many protocols defined"); 1091d65de6b8Spyr free(p); 10922edd718bSreyk YYERROR; 10932edd718bSreyk } 10942edd718bSreyk proto = p; 1095af50a7a9Sreyk } protopts_n { 109635d10c30Sreyk conf->sc_protocount++; 1097dae1a80bSreyk 10987bb52228Sreyk if ((proto->tlsflags & TLSFLAG_VERSION) == 0) { 10997bb52228Sreyk yyerror("invalid TLS protocol"); 1100dae1a80bSreyk YYERROR; 1101dae1a80bSreyk } 1102041405e3Sreyk TAILQ_INSERT_TAIL(conf->sc_protos, proto, entry); 11032edd718bSreyk } 11042edd718bSreyk ; 11052edd718bSreyk 1106af50a7a9Sreyk protopts_n : /* empty */ 1107af50a7a9Sreyk | '{' '}' 1108af50a7a9Sreyk | '{' optnl protopts_l '}' 1109af50a7a9Sreyk ; 1110af50a7a9Sreyk 11112edd718bSreyk protopts_l : protopts_l protoptsl nl 11122edd718bSreyk | protoptsl optnl 11132edd718bSreyk ; 11142edd718bSreyk 1115f71e4cb7Skn protoptsl : TLS { 11164f72d6edSbenno if (!(proto->type == RELAY_PROTO_TCP || 11174f72d6edSbenno proto->type == RELAY_PROTO_HTTP)) { 11184f72d6edSbenno yyerror("can set tls options only for " 11194f72d6edSbenno "tcp or http protocols"); 11204f72d6edSbenno YYERROR; 11214f72d6edSbenno } 11224f72d6edSbenno } tlsflags 1123f71e4cb7Skn | TLS { 11244f72d6edSbenno if (!(proto->type == RELAY_PROTO_TCP || 11254f72d6edSbenno proto->type == RELAY_PROTO_HTTP)) { 11264f72d6edSbenno yyerror("can set tls options only for " 11274f72d6edSbenno "tcp or http protocols"); 11284f72d6edSbenno YYERROR; 11294f72d6edSbenno } 11304f72d6edSbenno } '{' tlsflags_l '}' 11314f72d6edSbenno | TCP { 11324f72d6edSbenno if (!(proto->type == RELAY_PROTO_TCP || 11334f72d6edSbenno proto->type == RELAY_PROTO_HTTP)) { 11344f72d6edSbenno yyerror("can set tcp options only for " 11354f72d6edSbenno "tcp or http protocols"); 11364f72d6edSbenno YYERROR; 11374f72d6edSbenno } 11384f72d6edSbenno } tcpflags 11394f72d6edSbenno | TCP { 11404f72d6edSbenno if (!(proto->type == RELAY_PROTO_TCP || 11414f72d6edSbenno proto->type == RELAY_PROTO_HTTP)) { 11424f72d6edSbenno yyerror("can set tcp options only for " 11434f72d6edSbenno "tcp or http protocols"); 11444f72d6edSbenno YYERROR; 11454f72d6edSbenno } 11464f72d6edSbenno } '{' tcpflags_l '}' 1147e7742cb1Sbenno | HTTP { 1148e7742cb1Sbenno if (proto->type != RELAY_PROTO_HTTP) { 1149e7742cb1Sbenno yyerror("can set http options only for " 1150e7742cb1Sbenno "http protocol"); 1151e7742cb1Sbenno YYERROR; 1152e7742cb1Sbenno } 1153e7742cb1Sbenno } httpflags 1154e7742cb1Sbenno | HTTP { 1155e7742cb1Sbenno if (proto->type != RELAY_PROTO_HTTP) { 1156e7742cb1Sbenno yyerror("can set http options only for " 1157e7742cb1Sbenno "http protocol"); 1158e7742cb1Sbenno YYERROR; 1159e7742cb1Sbenno } 1160e7742cb1Sbenno } '{' httpflags_l '}' 116107c84b7eSreyk | RETURN ERROR opteflags { proto->flags |= F_RETURN; } 116207c84b7eSreyk | RETURN ERROR '{' eflags_l '}' { proto->flags |= F_RETURN; } 1163cb8b0e56Sreyk | filterrule 116459a200f9Sreyk | include 11652edd718bSreyk ; 11662edd718bSreyk 11670ad20b85Sbenno 11680ad20b85Sbenno httpflags_l : httpflags comma httpflags_l 11690ad20b85Sbenno | httpflags 11700ad20b85Sbenno ; 11710ad20b85Sbenno 11720ad20b85Sbenno httpflags : HEADERLEN NUMBER { 11730ad20b85Sbenno if ($2 < 0 || $2 > RELAY_MAXHEADERLENGTH) { 11743512060dSpatrick yyerror("invalid headerlen: %lld", $2); 11750ad20b85Sbenno YYERROR; 11760ad20b85Sbenno } 11770ad20b85Sbenno proto->httpheaderlen = $2; 11780ad20b85Sbenno } 1179e7742cb1Sbenno | WEBSOCKETS { proto->httpflags |= HTTPFLAG_WEBSOCKETS; } 1180e7742cb1Sbenno | NO WEBSOCKETS { proto->httpflags &= ~HTTPFLAG_WEBSOCKETS; } 11810ad20b85Sbenno ; 11820ad20b85Sbenno 11832edd718bSreyk tcpflags_l : tcpflags comma tcpflags_l 11842edd718bSreyk | tcpflags 11852edd718bSreyk ; 11862edd718bSreyk 11872edd718bSreyk tcpflags : SACK { proto->tcpflags |= TCPFLAG_SACK; } 11882edd718bSreyk | NO SACK { proto->tcpflags |= TCPFLAG_NSACK; } 11892edd718bSreyk | NODELAY { proto->tcpflags |= TCPFLAG_NODELAY; } 11902edd718bSreyk | NO NODELAY { proto->tcpflags |= TCPFLAG_NNODELAY; } 1191471dd29dSreyk | SPLICE { /* default */ } 1192471dd29dSreyk | NO SPLICE { proto->tcpflags |= TCPFLAG_NSPLICE; } 11936debaf0dSpyr | BACKLOG NUMBER { 1194ea42f25aSclaudio if ($2 < 0 || $2 > RELAY_MAX_BACKLOG) { 11953512060dSpatrick yyerror("invalid backlog: %lld", $2); 1196dae1a80bSreyk YYERROR; 1197dae1a80bSreyk } 1198dae1a80bSreyk proto->tcpbacklog = $2; 1199dae1a80bSreyk } 12006debaf0dSpyr | SOCKET BUFFER NUMBER { 12012edd718bSreyk proto->tcpflags |= TCPFLAG_BUFSIZ; 12026debaf0dSpyr if ((proto->tcpbufsiz = $3) < 0) { 12033512060dSpatrick yyerror("invalid socket buffer size: %lld", $3); 12046debaf0dSpyr YYERROR; 12052edd718bSreyk } 12066debaf0dSpyr } 12076debaf0dSpyr | IP STRING NUMBER { 12086debaf0dSpyr if ($3 < 0) { 12093512060dSpatrick yyerror("invalid ttl: %lld", $3); 12106debaf0dSpyr free($2); 12116debaf0dSpyr YYERROR; 12126debaf0dSpyr } 121377273a16Sreyk if (strcasecmp("ttl", $2) == 0) { 121477273a16Sreyk proto->tcpflags |= TCPFLAG_IPTTL; 121577273a16Sreyk proto->tcpipttl = $3; 121677273a16Sreyk } else if (strcasecmp("minttl", $2) == 0) { 121777273a16Sreyk proto->tcpflags |= TCPFLAG_IPMINTTL; 121877273a16Sreyk proto->tcpipminttl = $3; 121977273a16Sreyk } else { 122077273a16Sreyk yyerror("invalid TCP/IP flag: %s", $2); 122177273a16Sreyk free($2); 122277273a16Sreyk YYERROR; 122377273a16Sreyk } 122477273a16Sreyk free($2); 122577273a16Sreyk } 12262edd718bSreyk ; 12272edd718bSreyk 12287bb52228Sreyk tlsflags_l : tlsflags comma tlsflags_l 12297bb52228Sreyk | tlsflags 1230dae1a80bSreyk ; 1231dae1a80bSreyk 123285e5f500Sclaudio tlsflags : SESSION TICKETS { proto->tickets = 1; } 123385e5f500Sclaudio | NO SESSION TICKETS { proto->tickets = 0; } 1234dae1a80bSreyk | CIPHERS STRING { 12357bb52228Sreyk if (strlcpy(proto->tlsciphers, $2, 12367bb52228Sreyk sizeof(proto->tlsciphers)) >= 12377bb52228Sreyk sizeof(proto->tlsciphers)) { 12387bb52228Sreyk yyerror("tlsciphers truncated"); 1239177cf12dSpyr free($2); 1240177cf12dSpyr YYERROR; 1241177cf12dSpyr } 1242dae1a80bSreyk free($2); 1243dae1a80bSreyk } 12443675f6daSreyk | NO EDH { 124585e5f500Sclaudio (void)strlcpy(proto->tlsdhparams, "none", 124685e5f500Sclaudio sizeof(proto->tlsdhparams)); 1247cc7b12f3Sreyk } 124885e5f500Sclaudio | EDH { 124985e5f500Sclaudio (void)strlcpy(proto->tlsdhparams, "auto", 125085e5f500Sclaudio sizeof(proto->tlsdhparams)); 125185e5f500Sclaudio } 125285e5f500Sclaudio | EDH PARAMS STRING { 125385e5f500Sclaudio struct tls_config *tls_cfg; 125485e5f500Sclaudio if ((tls_cfg = tls_config_new()) == NULL) { 125585e5f500Sclaudio yyerror("tls_config_new failed"); 125685e5f500Sclaudio free($3); 125785e5f500Sclaudio YYERROR; 125885e5f500Sclaudio } 125985e5f500Sclaudio if (tls_config_set_dheparams(tls_cfg, $3) != 0) { 126085e5f500Sclaudio yyerror("tls edh params %s: %s", $3, 126185e5f500Sclaudio tls_config_error(tls_cfg)); 126285e5f500Sclaudio tls_config_free(tls_cfg); 126385e5f500Sclaudio free($3); 126485e5f500Sclaudio YYERROR; 126585e5f500Sclaudio } 126685e5f500Sclaudio tls_config_free(tls_cfg); 126785e5f500Sclaudio if (strlcpy(proto->tlsdhparams, $3, 126885e5f500Sclaudio sizeof(proto->tlsdhparams)) >= 126985e5f500Sclaudio sizeof(proto->tlsdhparams)) { 127085e5f500Sclaudio yyerror("tls edh truncated"); 127185e5f500Sclaudio free($3); 127285e5f500Sclaudio YYERROR; 127385e5f500Sclaudio } 127485e5f500Sclaudio free($3); 12753675f6daSreyk } 1276353c00bcSclaudio | ECDHE STRING { 127785e5f500Sclaudio struct tls_config *tls_cfg; 127885e5f500Sclaudio if ((tls_cfg = tls_config_new()) == NULL) { 127985e5f500Sclaudio yyerror("tls_config_new failed"); 1280353c00bcSclaudio free($2); 128185e5f500Sclaudio YYERROR; 128285e5f500Sclaudio } 1283353c00bcSclaudio if (tls_config_set_ecdhecurves(tls_cfg, $2) != 0) { 1284353c00bcSclaudio yyerror("tls ecdhe %s: %s", $2, 128585e5f500Sclaudio tls_config_error(tls_cfg)); 128685e5f500Sclaudio tls_config_free(tls_cfg); 1287353c00bcSclaudio free($2); 128885e5f500Sclaudio YYERROR; 128985e5f500Sclaudio } 129085e5f500Sclaudio tls_config_free(tls_cfg); 1291353c00bcSclaudio if (strlcpy(proto->tlsecdhecurves, $2, 1292353c00bcSclaudio sizeof(proto->tlsecdhecurves)) >= 1293353c00bcSclaudio sizeof(proto->tlsecdhecurves)) { 1294353c00bcSclaudio yyerror("tls ecdhe curves truncated"); 1295353c00bcSclaudio free($2); 129685e5f500Sclaudio YYERROR; 129785e5f500Sclaudio } 1298353c00bcSclaudio free($2); 1299cc7b12f3Sreyk } 1300062e776aSreyk | CA FILENAME STRING { 13017bb52228Sreyk if (strlcpy(proto->tlsca, $3, 13027bb52228Sreyk sizeof(proto->tlsca)) >= 13037bb52228Sreyk sizeof(proto->tlsca)) { 13047bb52228Sreyk yyerror("tlsca truncated"); 1305062e776aSreyk free($3); 1306062e776aSreyk YYERROR; 1307062e776aSreyk } 1308a2195becSreyk free($3); 1309062e776aSreyk } 1310cf39ad79Sreyk | CA KEY STRING PASSWORD STRING { 13117bb52228Sreyk if (strlcpy(proto->tlscakey, $3, 13127bb52228Sreyk sizeof(proto->tlscakey)) >= 13137bb52228Sreyk sizeof(proto->tlscakey)) { 13147bb52228Sreyk yyerror("tlscakey truncated"); 1315cf39ad79Sreyk free($3); 1316cf39ad79Sreyk free($5); 1317cf39ad79Sreyk YYERROR; 1318cf39ad79Sreyk } 13197bb52228Sreyk if ((proto->tlscapass = strdup($5)) == NULL) { 13207bb52228Sreyk yyerror("tlscapass"); 1321cf39ad79Sreyk free($3); 1322cf39ad79Sreyk free($5); 1323cf39ad79Sreyk YYERROR; 1324cf39ad79Sreyk } 1325cf39ad79Sreyk free($3); 1326cf39ad79Sreyk free($5); 1327cf39ad79Sreyk } 1328cf39ad79Sreyk | CA CERTIFICATE STRING { 13297bb52228Sreyk if (strlcpy(proto->tlscacert, $3, 13307bb52228Sreyk sizeof(proto->tlscacert)) >= 13317bb52228Sreyk sizeof(proto->tlscacert)) { 13327bb52228Sreyk yyerror("tlscacert truncated"); 1333cf39ad79Sreyk free($3); 1334cf39ad79Sreyk YYERROR; 1335cf39ad79Sreyk } 1336cf39ad79Sreyk free($3); 1337cf39ad79Sreyk } 13383ca2f577Sreyk | KEYPAIR STRING { 13393ca2f577Sreyk struct keyname *name; 13403ca2f577Sreyk 13413ca2f577Sreyk if (strlen($2) >= PATH_MAX) { 13423ca2f577Sreyk yyerror("keypair name too long"); 13433ca2f577Sreyk free($2); 13443ca2f577Sreyk YYERROR; 13453ca2f577Sreyk } 13463ca2f577Sreyk if ((name = calloc(1, sizeof(*name))) == NULL) { 13473ca2f577Sreyk yyerror("calloc"); 13483ca2f577Sreyk free($2); 13493ca2f577Sreyk YYERROR; 13503ca2f577Sreyk } 13513ca2f577Sreyk name->name = $2; 13523ca2f577Sreyk TAILQ_INSERT_TAIL(&proto->tlscerts, name, entry); 13533ca2f577Sreyk } 1354*92388deeStb | CLIENT CA STRING { 1355*92388deeStb if (strlcpy(proto->tlsclientca, $3, 1356*92388deeStb sizeof(proto->tlsclientca)) >= 1357*92388deeStb sizeof(proto->tlsclientca)) { 1358*92388deeStb yyerror("tlsclientca truncated"); 1359*92388deeStb free($3); 1360*92388deeStb YYERROR; 1361*92388deeStb } 1362*92388deeStb free($3); 1363*92388deeStb } 13647bb52228Sreyk | NO flag { proto->tlsflags &= ~($2); } 13657bb52228Sreyk | flag { proto->tlsflags |= $1; } 1366a7bda987Spyr ; 1367a7bda987Spyr 1368a7bda987Spyr flag : STRING { 1369b820fe85Sjsing if (strcmp("sslv3", $1) == 0) 13707bb52228Sreyk $$ = TLSFLAG_SSLV3; 1371a7bda987Spyr else if (strcmp("tlsv1", $1) == 0) 13727bb52228Sreyk $$ = TLSFLAG_TLSV1; 13737462166bSreyk else if (strcmp("tlsv1.0", $1) == 0) 13747bb52228Sreyk $$ = TLSFLAG_TLSV1_0; 13757462166bSreyk else if (strcmp("tlsv1.1", $1) == 0) 13767bb52228Sreyk $$ = TLSFLAG_TLSV1_1; 13777462166bSreyk else if (strcmp("tlsv1.2", $1) == 0) 13787bb52228Sreyk $$ = TLSFLAG_TLSV1_2; 1379b06d93a4Spvk else if (strcmp("tlsv1.3", $1) == 0) 1380b06d93a4Spvk $$ = TLSFLAG_TLSV1_3; 13813675f6daSreyk else if (strcmp("cipher-server-preference", $1) == 0) 13827bb52228Sreyk $$ = TLSFLAG_CIPHER_SERVER_PREF; 13833675f6daSreyk else if (strcmp("client-renegotiation", $1) == 0) 13847bb52228Sreyk $$ = TLSFLAG_CLIENT_RENEG; 1385a7bda987Spyr else { 13867bb52228Sreyk yyerror("invalid TLS flag: %s", $1); 1387a7bda987Spyr free($1); 1388a7bda987Spyr YYERROR; 1389a7bda987Spyr } 1390a7bda987Spyr free($1); 1391dae1a80bSreyk } 1392dae1a80bSreyk ; 13932edd718bSreyk 1394cb8b0e56Sreyk filterrule : action dir quick ruleaf rulesrc ruledst { 1395cb8b0e56Sreyk if ((rule = calloc(1, sizeof(*rule))) == NULL) 1396cb8b0e56Sreyk fatal("out of memory"); 1397cb8b0e56Sreyk 1398cb8b0e56Sreyk rule->rule_action = $1; 1399cb8b0e56Sreyk rule->rule_proto = proto->type; 1400cb8b0e56Sreyk rule->rule_dir = $2; 1401cb8b0e56Sreyk rule->rule_flags |= $3; 1402cb8b0e56Sreyk rule->rule_af = $4; 1403860302f3Sreyk rule->rule_src.addr = $5.ss; 1404860302f3Sreyk rule->rule_src.addr_mask = $5.prefixlen; 1405860302f3Sreyk rule->rule_dst.addr = $6.ss; 1406860302f3Sreyk rule->rule_dst.addr_mask = $6.prefixlen; 1407860302f3Sreyk 1408860302f3Sreyk if (RELAY_AF_NEQ(rule->rule_af, 1409860302f3Sreyk rule->rule_src.addr.ss_family) || 1410860302f3Sreyk RELAY_AF_NEQ(rule->rule_af, 1411860302f3Sreyk rule->rule_dst.addr.ss_family) || 1412860302f3Sreyk RELAY_AF_NEQ(rule->rule_src.addr.ss_family, 1413860302f3Sreyk rule->rule_dst.addr.ss_family)) { 1414860302f3Sreyk yyerror("address family mismatch"); 1415860302f3Sreyk YYERROR; 1416860302f3Sreyk } 1417cb8b0e56Sreyk 1418cb8b0e56Sreyk rulefile = NULL; 1419cb8b0e56Sreyk } ruleopts_l { 1420cb8b0e56Sreyk if (rule_add(proto, rule, rulefile) == -1) { 1421cb8b0e56Sreyk if (rulefile == NULL) { 1422cb8b0e56Sreyk yyerror("failed to load rule"); 1423cb8b0e56Sreyk } else { 1424cb8b0e56Sreyk yyerror("failed to load rules from %s", 1425cb8b0e56Sreyk rulefile); 1426cb8b0e56Sreyk free(rulefile); 1427cb8b0e56Sreyk } 1428cb8b0e56Sreyk rule_free(rule); 1429cb8b0e56Sreyk free(rule); 1430cb8b0e56Sreyk YYERROR; 1431cb8b0e56Sreyk } 1432cb8b0e56Sreyk if (rulefile) 1433cb8b0e56Sreyk free(rulefile); 1434cb8b0e56Sreyk rulefile = NULL; 1435cb8b0e56Sreyk rule = NULL; 1436cb8b0e56Sreyk keytype = KEY_TYPE_NONE; 1437cb8b0e56Sreyk } 1438cb8b0e56Sreyk ; 1439cb8b0e56Sreyk 1440cb8b0e56Sreyk action : PASS { $$ = RULE_ACTION_PASS; } 1441cb8b0e56Sreyk | BLOCK { $$ = RULE_ACTION_BLOCK; } 1442cb8b0e56Sreyk | MATCH { $$ = RULE_ACTION_MATCH; } 1443cb8b0e56Sreyk ; 1444cb8b0e56Sreyk 1445cb8b0e56Sreyk dir : /* empty */ { 1446cb8b0e56Sreyk $$ = dir = RELAY_DIR_REQUEST; 1447cb8b0e56Sreyk } 1448cb8b0e56Sreyk | REQUEST { 1449cb8b0e56Sreyk $$ = dir = RELAY_DIR_REQUEST; 1450cb8b0e56Sreyk } 1451cb8b0e56Sreyk | RESPONSE { 1452cb8b0e56Sreyk $$ = dir = RELAY_DIR_RESPONSE; 1453cb8b0e56Sreyk } 1454cb8b0e56Sreyk ; 1455cb8b0e56Sreyk 1456cb8b0e56Sreyk quick : /* empty */ { $$ = 0; } 1457cb8b0e56Sreyk | QUICK { $$ = RULE_FLAG_QUICK; } 1458cb8b0e56Sreyk ; 1459cb8b0e56Sreyk 1460cb8b0e56Sreyk ruleaf : /* empty */ { $$ = AF_UNSPEC; } 1461cb8b0e56Sreyk | INET6 { $$ = AF_INET6; } 1462cb8b0e56Sreyk | INET { $$ = AF_INET; } 1463cb8b0e56Sreyk ; 1464cb8b0e56Sreyk 1465860302f3Sreyk rulesrc : /* empty */ { 1466860302f3Sreyk memset(&$$, 0, sizeof($$)); 1467860302f3Sreyk } 1468860302f3Sreyk | FROM addrprefix { 1469860302f3Sreyk $$ = $2; 1470860302f3Sreyk } 1471cb8b0e56Sreyk ; 1472cb8b0e56Sreyk 1473860302f3Sreyk ruledst : /* empty */ { 1474860302f3Sreyk memset(&$$, 0, sizeof($$)); 1475860302f3Sreyk } 1476860302f3Sreyk | TO addrprefix { 1477860302f3Sreyk $$ = $2; 1478860302f3Sreyk } 1479cb8b0e56Sreyk ; 1480cb8b0e56Sreyk 1481cb8b0e56Sreyk ruleopts_l : /* empty */ 1482cb8b0e56Sreyk | ruleopts_t 1483cb8b0e56Sreyk ; 1484cb8b0e56Sreyk 1485cb8b0e56Sreyk ruleopts_t : ruleopts ruleopts_t 1486cb8b0e56Sreyk | ruleopts 1487cb8b0e56Sreyk ; 1488cb8b0e56Sreyk 1489cb8b0e56Sreyk ruleopts : METHOD STRING { 1490cb8b0e56Sreyk u_int id; 1491cb8b0e56Sreyk if ((id = relay_httpmethod_byname($2)) == 1492c307a266Sreyk HTTP_METHOD_NONE) { 1493cb8b0e56Sreyk yyerror("unknown HTTP method currently not " 1494cb8b0e56Sreyk "supported"); 1495cb8b0e56Sreyk free($2); 1496cb8b0e56Sreyk YYERROR; 1497cb8b0e56Sreyk } 1498cb8b0e56Sreyk rule->rule_method = id; 1499cb8b0e56Sreyk free($2); 1500cb8b0e56Sreyk } 1501cb8b0e56Sreyk | COOKIE key_option STRING value { 1502cb8b0e56Sreyk keytype = KEY_TYPE_COOKIE; 1503cb8b0e56Sreyk rule->rule_kv[keytype].kv_key = strdup($3); 1504cb8b0e56Sreyk rule->rule_kv[keytype].kv_option = $2; 1505cb8b0e56Sreyk rule->rule_kv[keytype].kv_value = (($4 != NULL) ? 1506cb8b0e56Sreyk strdup($4) : strdup("*")); 1507cb8b0e56Sreyk if (rule->rule_kv[keytype].kv_key == NULL || 1508cb8b0e56Sreyk rule->rule_kv[keytype].kv_value == NULL) 1509cb8b0e56Sreyk fatal("out of memory"); 1510cb8b0e56Sreyk free($3); 1511cb8b0e56Sreyk if ($4) 1512cb8b0e56Sreyk free($4); 1513cb8b0e56Sreyk rule->rule_kv[keytype].kv_type = keytype; 1514cb8b0e56Sreyk } 1515cb8b0e56Sreyk | COOKIE key_option { 1516cb8b0e56Sreyk keytype = KEY_TYPE_COOKIE; 1517cb8b0e56Sreyk rule->rule_kv[keytype].kv_option = $2; 1518cb8b0e56Sreyk rule->rule_kv[keytype].kv_type = keytype; 1519cb8b0e56Sreyk } 1520cb8b0e56Sreyk | HEADER key_option STRING value { 1521cb8b0e56Sreyk keytype = KEY_TYPE_HEADER; 1522cb8b0e56Sreyk memset(&rule->rule_kv[keytype], 0, 1523cb8b0e56Sreyk sizeof(rule->rule_kv[keytype])); 1524cb8b0e56Sreyk rule->rule_kv[keytype].kv_option = $2; 1525cb8b0e56Sreyk rule->rule_kv[keytype].kv_key = strdup($3); 1526cb8b0e56Sreyk rule->rule_kv[keytype].kv_value = (($4 != NULL) ? 1527cb8b0e56Sreyk strdup($4) : strdup("*")); 1528cb8b0e56Sreyk if (rule->rule_kv[keytype].kv_key == NULL || 1529cb8b0e56Sreyk rule->rule_kv[keytype].kv_value == NULL) 1530cb8b0e56Sreyk fatal("out of memory"); 1531cb8b0e56Sreyk free($3); 1532cb8b0e56Sreyk if ($4) 1533cb8b0e56Sreyk free($4); 1534cb8b0e56Sreyk rule->rule_kv[keytype].kv_type = keytype; 1535cb8b0e56Sreyk } 1536cb8b0e56Sreyk | HEADER key_option { 1537cb8b0e56Sreyk keytype = KEY_TYPE_HEADER; 1538cb8b0e56Sreyk rule->rule_kv[keytype].kv_option = $2; 1539cb8b0e56Sreyk rule->rule_kv[keytype].kv_type = keytype; 1540cb8b0e56Sreyk } 1541cb8b0e56Sreyk | PATH key_option STRING value { 1542cb8b0e56Sreyk keytype = KEY_TYPE_PATH; 1543cb8b0e56Sreyk rule->rule_kv[keytype].kv_option = $2; 1544cb8b0e56Sreyk rule->rule_kv[keytype].kv_key = strdup($3); 1545cb8b0e56Sreyk rule->rule_kv[keytype].kv_value = (($4 != NULL) ? 1546cb8b0e56Sreyk strdup($4) : strdup("*")); 1547cb8b0e56Sreyk if (rule->rule_kv[keytype].kv_key == NULL || 1548cb8b0e56Sreyk rule->rule_kv[keytype].kv_value == NULL) 1549cb8b0e56Sreyk fatal("out of memory"); 1550cb8b0e56Sreyk free($3); 1551cb8b0e56Sreyk if ($4) 1552cb8b0e56Sreyk free($4); 1553cb8b0e56Sreyk rule->rule_kv[keytype].kv_type = keytype; 1554cb8b0e56Sreyk } 1555cb8b0e56Sreyk | PATH key_option { 1556cb8b0e56Sreyk keytype = KEY_TYPE_PATH; 1557cb8b0e56Sreyk rule->rule_kv[keytype].kv_option = $2; 1558cb8b0e56Sreyk rule->rule_kv[keytype].kv_type = keytype; 1559cb8b0e56Sreyk } 1560eeb1fea4Sdenis | PATH STRIP NUMBER { 1561eeb1fea4Sdenis char *strip = NULL; 1562eeb1fea4Sdenis 1563eeb1fea4Sdenis if ($3 < 0 || $3 > INT_MAX) { 1564eeb1fea4Sdenis yyerror("invalid strip number"); 1565eeb1fea4Sdenis YYERROR; 1566eeb1fea4Sdenis } 1567eeb1fea4Sdenis if (asprintf(&strip, "%lld", $3) <= 0) 1568eeb1fea4Sdenis fatal("can't parse strip"); 1569eeb1fea4Sdenis keytype = KEY_TYPE_PATH; 1570eeb1fea4Sdenis rule->rule_kv[keytype].kv_option = KEY_OPTION_STRIP; 1571eeb1fea4Sdenis rule->rule_kv[keytype].kv_value = strip; 1572eeb1fea4Sdenis rule->rule_kv[keytype].kv_type = keytype; 1573eeb1fea4Sdenis } 1574cb8b0e56Sreyk | QUERYSTR key_option STRING value { 1575cb8b0e56Sreyk switch ($2) { 1576cb8b0e56Sreyk case KEY_OPTION_APPEND: 1577cb8b0e56Sreyk case KEY_OPTION_SET: 1578cb8b0e56Sreyk case KEY_OPTION_REMOVE: 1579cb8b0e56Sreyk yyerror("combining query type and the given " 1580cb8b0e56Sreyk "option is not supported"); 1581cb8b0e56Sreyk free($3); 1582cb8b0e56Sreyk if ($4) 1583cb8b0e56Sreyk free($4); 1584cb8b0e56Sreyk YYERROR; 1585cb8b0e56Sreyk break; 1586cb8b0e56Sreyk } 1587cb8b0e56Sreyk keytype = KEY_TYPE_QUERY; 1588cb8b0e56Sreyk rule->rule_kv[keytype].kv_option = $2; 1589cb8b0e56Sreyk rule->rule_kv[keytype].kv_key = strdup($3); 1590cb8b0e56Sreyk rule->rule_kv[keytype].kv_value = (($4 != NULL) ? 1591cb8b0e56Sreyk strdup($4) : strdup("*")); 1592cb8b0e56Sreyk if (rule->rule_kv[keytype].kv_key == NULL || 1593cb8b0e56Sreyk rule->rule_kv[keytype].kv_value == NULL) 1594cb8b0e56Sreyk fatal("out of memory"); 1595cb8b0e56Sreyk free($3); 1596cb8b0e56Sreyk if ($4) 1597cb8b0e56Sreyk free($4); 1598cb8b0e56Sreyk rule->rule_kv[keytype].kv_type = keytype; 1599cb8b0e56Sreyk } 1600cb8b0e56Sreyk | QUERYSTR key_option { 1601cb8b0e56Sreyk switch ($2) { 1602cb8b0e56Sreyk case KEY_OPTION_APPEND: 1603cb8b0e56Sreyk case KEY_OPTION_SET: 1604cb8b0e56Sreyk case KEY_OPTION_REMOVE: 1605cb8b0e56Sreyk yyerror("combining query type and the given " 1606cb8b0e56Sreyk "option is not supported"); 1607cb8b0e56Sreyk YYERROR; 1608cb8b0e56Sreyk break; 1609cb8b0e56Sreyk } 1610cb8b0e56Sreyk keytype = KEY_TYPE_QUERY; 1611cb8b0e56Sreyk rule->rule_kv[keytype].kv_option = $2; 1612cb8b0e56Sreyk rule->rule_kv[keytype].kv_type = keytype; 1613cb8b0e56Sreyk } 1614cb8b0e56Sreyk | URL key_option optdigest value { 1615cb8b0e56Sreyk switch ($2) { 1616cb8b0e56Sreyk case KEY_OPTION_APPEND: 1617cb8b0e56Sreyk case KEY_OPTION_SET: 1618cb8b0e56Sreyk case KEY_OPTION_REMOVE: 1619cb8b0e56Sreyk yyerror("combining url type and the given " 1620cb8b0e56Sreyk "option is not supported"); 1621cb8b0e56Sreyk free($3.digest); 1622cb8b0e56Sreyk free($4); 1623cb8b0e56Sreyk YYERROR; 1624cb8b0e56Sreyk break; 1625cb8b0e56Sreyk } 1626cb8b0e56Sreyk keytype = KEY_TYPE_URL; 1627cb8b0e56Sreyk rule->rule_kv[keytype].kv_option = $2; 1628cb8b0e56Sreyk rule->rule_kv[keytype].kv_key = strdup($3.digest); 1629cb8b0e56Sreyk rule->rule_kv[keytype].kv_digest = $3.type; 1630cb8b0e56Sreyk rule->rule_kv[keytype].kv_value = (($4 != NULL) ? 1631cb8b0e56Sreyk strdup($4) : strdup("*")); 1632cb8b0e56Sreyk if (rule->rule_kv[keytype].kv_key == NULL || 1633cb8b0e56Sreyk rule->rule_kv[keytype].kv_value == NULL) 1634cb8b0e56Sreyk fatal("out of memory"); 1635cb8b0e56Sreyk free($3.digest); 1636cb8b0e56Sreyk if ($4) 1637cb8b0e56Sreyk free($4); 1638cb8b0e56Sreyk rule->rule_kv[keytype].kv_type = keytype; 1639cb8b0e56Sreyk } 1640cb8b0e56Sreyk | URL key_option { 1641cb8b0e56Sreyk switch ($2) { 1642cb8b0e56Sreyk case KEY_OPTION_APPEND: 1643cb8b0e56Sreyk case KEY_OPTION_SET: 1644cb8b0e56Sreyk case KEY_OPTION_REMOVE: 1645cb8b0e56Sreyk yyerror("combining url type and the given " 1646cb8b0e56Sreyk "option is not supported"); 1647cb8b0e56Sreyk YYERROR; 1648cb8b0e56Sreyk break; 1649cb8b0e56Sreyk } 1650cb8b0e56Sreyk keytype = KEY_TYPE_URL; 1651cb8b0e56Sreyk rule->rule_kv[keytype].kv_option = $2; 1652cb8b0e56Sreyk rule->rule_kv[keytype].kv_type = keytype; 1653cb8b0e56Sreyk } 1654cb8b0e56Sreyk | FORWARD TO table { 1655cb8b0e56Sreyk if (table_findbyname(conf, $3) == NULL) { 1656cb8b0e56Sreyk yyerror("undefined forward table"); 1657cb8b0e56Sreyk free($3); 1658cb8b0e56Sreyk YYERROR; 1659cb8b0e56Sreyk } 1660cb8b0e56Sreyk if (strlcpy(rule->rule_tablename, $3, 1661cb8b0e56Sreyk sizeof(rule->rule_tablename)) >= 1662cb8b0e56Sreyk sizeof(rule->rule_tablename)) { 1663cb8b0e56Sreyk yyerror("invalid forward table name"); 1664cb8b0e56Sreyk free($3); 1665cb8b0e56Sreyk YYERROR; 1666cb8b0e56Sreyk } 1667cb8b0e56Sreyk free($3); 1668cb8b0e56Sreyk } 1669cb8b0e56Sreyk | TAG STRING { 1670cb8b0e56Sreyk tag = tag_name2id($2); 1671cb8b0e56Sreyk if (rule->rule_tag) { 1672cb8b0e56Sreyk yyerror("tag already defined"); 1673cb8b0e56Sreyk free($2); 1674cb8b0e56Sreyk rule_free(rule); 1675cb8b0e56Sreyk free(rule); 1676cb8b0e56Sreyk YYERROR; 1677cb8b0e56Sreyk } 1678cb8b0e56Sreyk if (tag == 0) { 1679cb8b0e56Sreyk yyerror("invalid tag"); 1680cb8b0e56Sreyk free($2); 1681cb8b0e56Sreyk rule_free(rule); 1682cb8b0e56Sreyk free(rule); 1683cb8b0e56Sreyk YYERROR; 1684cb8b0e56Sreyk } 1685cb8b0e56Sreyk rule->rule_tag = tag; 1686cb8b0e56Sreyk if (strlcpy(rule->rule_tagname, $2, 1687cb8b0e56Sreyk sizeof(rule->rule_tagname)) >= 1688cb8b0e56Sreyk sizeof(rule->rule_tagname)) { 1689cb8b0e56Sreyk yyerror("tag truncated"); 1690cb8b0e56Sreyk free($2); 1691cb8b0e56Sreyk rule_free(rule); 1692cb8b0e56Sreyk free(rule); 1693cb8b0e56Sreyk YYERROR; 1694cb8b0e56Sreyk } 1695cb8b0e56Sreyk free($2); 1696cb8b0e56Sreyk } 1697cb8b0e56Sreyk | NO TAG { 1698cb8b0e56Sreyk if (tag == 0) { 1699cb8b0e56Sreyk yyerror("no tag defined"); 1700cb8b0e56Sreyk YYERROR; 1701cb8b0e56Sreyk } 1702cb8b0e56Sreyk rule->rule_tag = -1; 1703cb8b0e56Sreyk memset(rule->rule_tagname, 0, 1704cb8b0e56Sreyk sizeof(rule->rule_tagname)); 1705cb8b0e56Sreyk } 1706cb8b0e56Sreyk | TAGGED STRING { 1707cb8b0e56Sreyk tagged = tag_name2id($2); 1708cb8b0e56Sreyk if (rule->rule_tagged) { 1709cb8b0e56Sreyk yyerror("tagged already defined"); 1710cb8b0e56Sreyk free($2); 1711cb8b0e56Sreyk rule_free(rule); 1712cb8b0e56Sreyk free(rule); 1713cb8b0e56Sreyk YYERROR; 1714cb8b0e56Sreyk } 1715cb8b0e56Sreyk if (tagged == 0) { 1716cb8b0e56Sreyk yyerror("invalid tag"); 1717cb8b0e56Sreyk free($2); 1718cb8b0e56Sreyk rule_free(rule); 1719cb8b0e56Sreyk free(rule); 1720cb8b0e56Sreyk YYERROR; 1721cb8b0e56Sreyk } 1722cb8b0e56Sreyk rule->rule_tagged = tagged; 1723cb8b0e56Sreyk if (strlcpy(rule->rule_taggedname, $2, 1724cb8b0e56Sreyk sizeof(rule->rule_taggedname)) >= 1725cb8b0e56Sreyk sizeof(rule->rule_taggedname)) { 1726cb8b0e56Sreyk yyerror("tagged truncated"); 1727cb8b0e56Sreyk free($2); 1728cb8b0e56Sreyk rule_free(rule); 1729cb8b0e56Sreyk free(rule); 1730cb8b0e56Sreyk YYERROR; 1731cb8b0e56Sreyk } 1732cb8b0e56Sreyk free($2); 1733cb8b0e56Sreyk } 1734cb8b0e56Sreyk | LABEL STRING { 1735cb8b0e56Sreyk label = label_name2id($2); 1736cb8b0e56Sreyk if (rule->rule_label) { 1737cb8b0e56Sreyk yyerror("label already defined"); 1738cb8b0e56Sreyk free($2); 1739cb8b0e56Sreyk rule_free(rule); 1740cb8b0e56Sreyk free(rule); 1741cb8b0e56Sreyk YYERROR; 1742cb8b0e56Sreyk } 1743cb8b0e56Sreyk if (label == 0) { 1744cb8b0e56Sreyk yyerror("invalid label"); 1745cb8b0e56Sreyk free($2); 1746cb8b0e56Sreyk rule_free(rule); 1747cb8b0e56Sreyk free(rule); 1748cb8b0e56Sreyk YYERROR; 1749cb8b0e56Sreyk } 1750cb8b0e56Sreyk rule->rule_label = label; 1751cb8b0e56Sreyk if (strlcpy(rule->rule_labelname, $2, 1752cb8b0e56Sreyk sizeof(rule->rule_labelname)) >= 1753cb8b0e56Sreyk sizeof(rule->rule_labelname)) { 1754cb8b0e56Sreyk yyerror("label truncated"); 1755cb8b0e56Sreyk free($2); 1756cb8b0e56Sreyk rule_free(rule); 1757cb8b0e56Sreyk free(rule); 1758cb8b0e56Sreyk YYERROR; 1759cb8b0e56Sreyk } 1760cb8b0e56Sreyk free($2); 1761cb8b0e56Sreyk } 1762cb8b0e56Sreyk | NO LABEL { 1763cb8b0e56Sreyk if (label == 0) { 1764cb8b0e56Sreyk yyerror("no label defined"); 1765cb8b0e56Sreyk YYERROR; 1766cb8b0e56Sreyk } 1767cb8b0e56Sreyk rule->rule_label = -1; 1768cb8b0e56Sreyk memset(rule->rule_labelname, 0, 1769cb8b0e56Sreyk sizeof(rule->rule_labelname)); 1770cb8b0e56Sreyk } 1771cb8b0e56Sreyk | FILENAME STRING value { 1772cb8b0e56Sreyk if (rulefile != NULL) { 1773cb8b0e56Sreyk yyerror("only one file per rule supported"); 1774cb8b0e56Sreyk free($2); 1775cb8b0e56Sreyk free($3); 1776cb8b0e56Sreyk rule_free(rule); 1777cb8b0e56Sreyk free(rule); 1778cb8b0e56Sreyk YYERROR; 1779cb8b0e56Sreyk } 1780cb8b0e56Sreyk if ($3) { 1781cb8b0e56Sreyk if ((rule->rule_kv[keytype].kv_value = 1782cb8b0e56Sreyk strdup($3)) == NULL) 1783cb8b0e56Sreyk fatal("out of memory"); 1784cb8b0e56Sreyk free($3); 1785cb8b0e56Sreyk } else 1786cb8b0e56Sreyk rule->rule_kv[keytype].kv_value = NULL; 1787cb8b0e56Sreyk rulefile = $2; 1788cb8b0e56Sreyk } 1789cb8b0e56Sreyk ; 1790cb8b0e56Sreyk 1791cb8b0e56Sreyk value : /* empty */ { $$ = NULL; } 1792cb8b0e56Sreyk | VALUE STRING { $$ = $2; } 1793cb8b0e56Sreyk ; 1794cb8b0e56Sreyk 1795cb8b0e56Sreyk key_option : /* empty */ { $$ = KEY_OPTION_NONE; } 1796cb8b0e56Sreyk | APPEND { $$ = KEY_OPTION_APPEND; } 1797cb8b0e56Sreyk | SET { $$ = KEY_OPTION_SET; } 1798cb8b0e56Sreyk | REMOVE { $$ = KEY_OPTION_REMOVE; } 1799cb8b0e56Sreyk | HASH { $$ = KEY_OPTION_HASH; } 1800cb8b0e56Sreyk | LOG { $$ = KEY_OPTION_LOG; } 1801cb8b0e56Sreyk ; 1802cb8b0e56Sreyk 18032edd718bSreyk relay : RELAY STRING { 18042edd718bSreyk struct relay *r; 18052edd718bSreyk 1806a2195becSreyk if (!loadcfg) { 1807a2195becSreyk free($2); 1808a2195becSreyk YYACCEPT; 1809a2195becSreyk } 1810a2195becSreyk 18112edd718bSreyk if ((r = calloc(1, sizeof (*r))) == NULL) 18122edd718bSreyk fatal("out of memory"); 1813ee816639Sreyk TAILQ_INIT(&relays); 18142edd718bSreyk 1815820dd719Sreyk if (strlcpy(r->rl_conf.name, $2, 1816820dd719Sreyk sizeof(r->rl_conf.name)) >= 18174a5b9b3eSreyk sizeof(r->rl_conf.name)) { 18182edd718bSreyk yyerror("relay name truncated"); 1819e28634eaSreyk free($2); 1820d65de6b8Spyr free(r); 18212edd718bSreyk YYERROR; 18222edd718bSreyk } 18232edd718bSreyk free($2); 182400ae3104Sreyk if (relay_id(r) == -1) { 182500ae3104Sreyk yyerror("too many relays defined"); 182600ae3104Sreyk free(r); 182700ae3104Sreyk YYERROR; 182800ae3104Sreyk } 18294a5b9b3eSreyk r->rl_conf.timeout.tv_sec = RELAY_TIMEOUT; 18304a5b9b3eSreyk r->rl_proto = NULL; 18314a5b9b3eSreyk r->rl_conf.proto = EMPTY_ID; 18324a5b9b3eSreyk r->rl_conf.dstretry = 0; 1833114ce177Sclaudio r->rl_tls_ca_fd = -1; 1834114ce177Sclaudio r->rl_tls_cacert_fd = -1; 1835*92388deeStb r->rl_tls_client_ca_fd = -1; 1836416fa9c0Sreyk TAILQ_INIT(&r->rl_tables); 18372edd718bSreyk if (last_relay_id == INT_MAX) { 18382edd718bSreyk yyerror("too many relays defined"); 1839d65de6b8Spyr free(r); 18402edd718bSreyk YYERROR; 18412edd718bSreyk } 1842416fa9c0Sreyk dstmode = RELAY_DSTMODE_DEFAULT; 18432edd718bSreyk rlay = r; 18442edd718bSreyk } '{' optnl relayopts_l '}' { 18459f29cb9cSreyk struct relay *r; 1846ee816639Sreyk struct relay_config *rlconf = &rlay->rl_conf; 18473ca2f577Sreyk struct keyname *name; 1848ee816639Sreyk 1849ee816639Sreyk if (relay_findbyname(conf, rlconf->name) != NULL || 1850ee816639Sreyk relay_findbyaddr(conf, rlconf) != NULL) { 1851ee816639Sreyk yyerror("relay %s or listener defined twice", 1852ee816639Sreyk rlconf->name); 1853ee816639Sreyk YYERROR; 1854ee816639Sreyk } 18559f29cb9cSreyk 18564a5b9b3eSreyk if (rlay->rl_conf.ss.ss_family == AF_UNSPEC) { 18572edd718bSreyk yyerror("relay %s has no listener", 18584a5b9b3eSreyk rlay->rl_conf.name); 18592edd718bSreyk YYERROR; 18602edd718bSreyk } 1861523113bfSreyk if ((rlay->rl_conf.flags & (F_NATLOOK|F_DIVERT)) == 1862523113bfSreyk (F_NATLOOK|F_DIVERT)) { 1863523113bfSreyk yyerror("relay %s with conflicting nat lookup " 1864523113bfSreyk "and peer options", rlay->rl_conf.name); 1865523113bfSreyk YYERROR; 1866523113bfSreyk } 1867523113bfSreyk if ((rlay->rl_conf.flags & (F_NATLOOK|F_DIVERT)) == 0 && 18684a5b9b3eSreyk rlay->rl_conf.dstss.ss_family == AF_UNSPEC && 1869416fa9c0Sreyk TAILQ_EMPTY(&rlay->rl_tables)) { 18709591a9f7Spyr yyerror("relay %s has no target, rdr, " 18714a5b9b3eSreyk "or table", rlay->rl_conf.name); 18722edd718bSreyk YYERROR; 18732edd718bSreyk } 18744a5b9b3eSreyk if (rlay->rl_conf.proto == EMPTY_ID) { 18754a5b9b3eSreyk rlay->rl_proto = &conf->sc_proto_default; 18764a5b9b3eSreyk rlay->rl_conf.proto = conf->sc_proto_default.id; 187776c67854Spyr } 1878ee816639Sreyk 18793ca2f577Sreyk if (TAILQ_EMPTY(&rlay->rl_proto->tlscerts) && 18803ca2f577Sreyk relay_load_certfiles(conf, rlay, NULL) == -1) { 1881b0633fd6Spyr yyerror("cannot load certificates for relay %s", 18824a5b9b3eSreyk rlay->rl_conf.name); 1883b0633fd6Spyr YYERROR; 1884b0633fd6Spyr } 18853ca2f577Sreyk TAILQ_FOREACH(name, &rlay->rl_proto->tlscerts, entry) { 18863ca2f577Sreyk if (relay_load_certfiles(conf, 18873ca2f577Sreyk rlay, name->name) == -1) { 18883ca2f577Sreyk yyerror("cannot load keypair %s" 18893ca2f577Sreyk " for relay %s", name->name, 18903ca2f577Sreyk rlay->rl_conf.name); 18913ca2f577Sreyk YYERROR; 18923ca2f577Sreyk } 18933ca2f577Sreyk } 1894ee816639Sreyk 189535d10c30Sreyk conf->sc_relaycount++; 18964a5b9b3eSreyk SPLAY_INIT(&rlay->rl_sessions); 1897041405e3Sreyk TAILQ_INSERT_TAIL(conf->sc_relays, rlay, rl_entry); 18980325c666Sreyk 1899af50a7a9Sreyk tableport = 0; 19009f29cb9cSreyk 19019f29cb9cSreyk while ((r = TAILQ_FIRST(&relays)) != NULL) { 19029f29cb9cSreyk TAILQ_REMOVE(&relays, r, rl_entry); 19039f29cb9cSreyk if (relay_inherit(rlay, r) == NULL) { 19049f29cb9cSreyk YYERROR; 19059f29cb9cSreyk } 19069f29cb9cSreyk } 1907af50a7a9Sreyk rlay = NULL; 19082edd718bSreyk } 19092edd718bSreyk ; 19102edd718bSreyk 19112edd718bSreyk relayopts_l : relayopts_l relayoptsl nl 19122edd718bSreyk | relayoptsl optnl 19132edd718bSreyk ; 19142edd718bSreyk 19157bb52228Sreyk relayoptsl : LISTEN ON STRING port opttls { 19162edd718bSreyk struct addresslist al; 19172edd718bSreyk struct address *h; 19189f29cb9cSreyk struct relay *r; 19192edd718bSreyk 19204a5b9b3eSreyk if (rlay->rl_conf.ss.ss_family != AF_UNSPEC) { 19219f29cb9cSreyk if ((r = calloc(1, sizeof (*r))) == NULL) 19229f29cb9cSreyk fatal("out of memory"); 19239f29cb9cSreyk TAILQ_INSERT_TAIL(&relays, r, rl_entry); 19249f29cb9cSreyk } else 19259f29cb9cSreyk r = rlay; 1926232f7df1Sreyk if ($4.op != PF_OP_EQ) { 1927232f7df1Sreyk yyerror("invalid port"); 1928232f7df1Sreyk free($3); 19292edd718bSreyk YYERROR; 19302edd718bSreyk } 19312edd718bSreyk 19322edd718bSreyk TAILQ_INIT(&al); 19334ac0ad23Sreyk if (host($3, &al, 1, &$4, NULL, -1) <= 0) { 19342edd718bSreyk yyerror("invalid listen ip: %s", $3); 19352edd718bSreyk free($3); 19362edd718bSreyk YYERROR; 19372edd718bSreyk } 19382edd718bSreyk free($3); 19392edd718bSreyk h = TAILQ_FIRST(&al); 19409f29cb9cSreyk bcopy(&h->ss, &r->rl_conf.ss, sizeof(r->rl_conf.ss)); 19419f29cb9cSreyk r->rl_conf.port = h->port.val[0]; 19423c201a99Spyr if ($5) { 19437bb52228Sreyk r->rl_conf.flags |= F_TLS; 1944586b5f8aSreyk conf->sc_conf.flags |= F_TLS; 19453c201a99Spyr } 1946232f7df1Sreyk tableport = h->port.val[0]; 1947a2195becSreyk host_free(&al); 19482edd718bSreyk } 19497bb52228Sreyk | forwardmode opttlsclient TO forwardspec dstaf { 19503c03a838Sreyk rlay->rl_conf.fwdmode = $1; 1951435fc164Sreyk if ($1 == FWD_ROUTE) { 1952435fc164Sreyk yyerror("no route for relays"); 19533c03a838Sreyk YYERROR; 195433e8bb87Sreyk } 195533e8bb87Sreyk if ($2) { 19567bb52228Sreyk rlay->rl_conf.flags |= F_TLSCLIENT; 1957586b5f8aSreyk conf->sc_conf.flags |= F_TLSCLIENT; 19583c03a838Sreyk } 19593c03a838Sreyk } 1960cc06b109Sreyk | SESSION TIMEOUT NUMBER { 19614a5b9b3eSreyk if ((rlay->rl_conf.timeout.tv_sec = $3) < 0) { 1962b02f4fdbSbenno yyerror("invalid timeout: %lld", $3); 1963b02f4fdbSbenno YYERROR; 1964b02f4fdbSbenno } 1965b02f4fdbSbenno if (rlay->rl_conf.timeout.tv_sec > INT_MAX) { 1966b02f4fdbSbenno yyerror("timeout too large: %lld", $3); 19672edd718bSreyk YYERROR; 19682edd718bSreyk } 19692edd718bSreyk } 19702edd718bSreyk | PROTO STRING { 19712edd718bSreyk struct protocol *p; 19722edd718bSreyk 19734f72d6edSbenno if (rlay->rl_conf.proto != EMPTY_ID) { 19744f72d6edSbenno yyerror("more than one protocol specified"); 19754f72d6edSbenno YYERROR; 19764f72d6edSbenno } 19774f72d6edSbenno 197835d10c30Sreyk TAILQ_FOREACH(p, conf->sc_protos, entry) 19792edd718bSreyk if (!strcmp(p->name, $2)) 19802edd718bSreyk break; 19812edd718bSreyk if (p == NULL) { 19822edd718bSreyk yyerror("no such protocol: %s", $2); 19832edd718bSreyk free($2); 19842edd718bSreyk YYERROR; 19852edd718bSreyk } 19862edd718bSreyk p->flags |= F_USED; 19874a5b9b3eSreyk rlay->rl_conf.proto = p->id; 19884a5b9b3eSreyk rlay->rl_proto = p; 19892edd718bSreyk free($2); 19902edd718bSreyk } 19914a5b9b3eSreyk | DISABLE { rlay->rl_conf.flags |= F_DISABLE; } 1992af50a7a9Sreyk | include 1993af50a7a9Sreyk ; 1994af50a7a9Sreyk 199533e8bb87Sreyk forwardspec : STRING port retry { 1996af50a7a9Sreyk struct addresslist al; 1997af50a7a9Sreyk struct address *h; 1998af50a7a9Sreyk 19994a5b9b3eSreyk if (rlay->rl_conf.dstss.ss_family != AF_UNSPEC) { 2000820dd719Sreyk yyerror("relay %s target or redirection " 2001820dd719Sreyk "already specified", rlay->rl_conf.name); 2002af50a7a9Sreyk free($1); 2003af50a7a9Sreyk YYERROR; 2004af50a7a9Sreyk } 2005232f7df1Sreyk if ($2.op != PF_OP_EQ) { 2006232f7df1Sreyk yyerror("invalid port"); 2007232f7df1Sreyk free($1); 2008232f7df1Sreyk YYERROR; 2009232f7df1Sreyk } 2010af50a7a9Sreyk 2011af50a7a9Sreyk TAILQ_INIT(&al); 20124ac0ad23Sreyk if (host($1, &al, 1, &$2, NULL, -1) <= 0) { 2013b92dabb4Schrisz yyerror("invalid forward ip: %s", $1); 2014af50a7a9Sreyk free($1); 2015af50a7a9Sreyk YYERROR; 2016af50a7a9Sreyk } 2017af50a7a9Sreyk free($1); 2018af50a7a9Sreyk h = TAILQ_FIRST(&al); 20194a5b9b3eSreyk bcopy(&h->ss, &rlay->rl_conf.dstss, 20204a5b9b3eSreyk sizeof(rlay->rl_conf.dstss)); 2021232f7df1Sreyk rlay->rl_conf.dstport = h->port.val[0]; 20224a5b9b3eSreyk rlay->rl_conf.dstretry = $3; 2023a2195becSreyk host_free(&al); 2024af50a7a9Sreyk } 20254517372eSreyk | NAT LOOKUP retry { 2026586b5f8aSreyk conf->sc_conf.flags |= F_NEEDPF; 20274a5b9b3eSreyk rlay->rl_conf.flags |= F_NATLOOK; 20284a5b9b3eSreyk rlay->rl_conf.dstretry = $3; 20294517372eSreyk } 2030523113bfSreyk | DESTINATION retry { 2031586b5f8aSreyk conf->sc_conf.flags |= F_NEEDPF; 2032523113bfSreyk rlay->rl_conf.flags |= F_DIVERT; 2033523113bfSreyk rlay->rl_conf.dstretry = $2; 2034523113bfSreyk } 203533e8bb87Sreyk | tablespec { 2036416fa9c0Sreyk struct relay_table *rlt; 2037416fa9c0Sreyk 2038416fa9c0Sreyk if ((rlt = calloc(1, sizeof(*rlt))) == NULL) { 2039416fa9c0Sreyk yyerror("failed to allocate table reference"); 204033e8bb87Sreyk YYERROR; 204133e8bb87Sreyk } 2042416fa9c0Sreyk 2043416fa9c0Sreyk rlt->rlt_table = $1; 2044416fa9c0Sreyk rlt->rlt_table->conf.flags |= F_USED; 2045416fa9c0Sreyk rlt->rlt_mode = dstmode; 2046416fa9c0Sreyk rlt->rlt_flags = F_USED; 2047416fa9c0Sreyk if (!TAILQ_EMPTY(&rlay->rl_tables)) 2048416fa9c0Sreyk rlt->rlt_flags |= F_BACKUP; 2049416fa9c0Sreyk 2050acb89df4Sreyk if (hashkey != NULL && 2051acb89df4Sreyk (rlay->rl_conf.flags & F_HASHKEY) == 0) { 2052acb89df4Sreyk memcpy(&rlay->rl_conf.hashkey, 2053acb89df4Sreyk hashkey, sizeof(rlay->rl_conf.hashkey)); 2054acb89df4Sreyk rlay->rl_conf.flags |= F_HASHKEY; 2055acb89df4Sreyk } 2056acb89df4Sreyk free(hashkey); 2057acb89df4Sreyk hashkey = NULL; 2058acb89df4Sreyk 2059416fa9c0Sreyk TAILQ_INSERT_TAIL(&rlay->rl_tables, rlt, rlt_entry); 20607a4012efSsthen } 20612edd718bSreyk ; 20622edd718bSreyk 20632edd718bSreyk dstmode : /* empty */ { $$ = RELAY_DSTMODE_DEFAULT; } 20642edd718bSreyk | LOADBALANCE { $$ = RELAY_DSTMODE_LOADBALANCE; } 20652edd718bSreyk | ROUNDROBIN { $$ = RELAY_DSTMODE_ROUNDROBIN; } 20662edd718bSreyk | HASH { $$ = RELAY_DSTMODE_HASH; } 206759e187d0Sreyk | LEASTSTATES { $$ = RELAY_DSTMODE_LEASTSTATES; } 206859e187d0Sreyk | SRCHASH { $$ = RELAY_DSTMODE_SRCHASH; } 206959e187d0Sreyk | RANDOM { $$ = RELAY_DSTMODE_RANDOM; } 20702edd718bSreyk ; 20712edd718bSreyk 207234438db4Sreyk router : ROUTER STRING { 207334438db4Sreyk struct router *rt = NULL; 207434438db4Sreyk 2075a2195becSreyk if (!loadcfg) { 2076a2195becSreyk free($2); 2077a2195becSreyk YYACCEPT; 2078a2195becSreyk } 2079a2195becSreyk 2080586b5f8aSreyk conf->sc_conf.flags |= F_NEEDRT; 208134438db4Sreyk TAILQ_FOREACH(rt, conf->sc_rts, rt_entry) 208234438db4Sreyk if (!strcmp(rt->rt_conf.name, $2)) 208334438db4Sreyk break; 208434438db4Sreyk if (rt != NULL) { 208534438db4Sreyk yyerror("router %s defined twice", $2); 208634438db4Sreyk free($2); 208734438db4Sreyk YYERROR; 208834438db4Sreyk } 208934438db4Sreyk 209034438db4Sreyk if ((rt = calloc(1, sizeof (*rt))) == NULL) 209134438db4Sreyk fatal("out of memory"); 209234438db4Sreyk 209334438db4Sreyk if (strlcpy(rt->rt_conf.name, $2, 209434438db4Sreyk sizeof(rt->rt_conf.name)) >= 209534438db4Sreyk sizeof(rt->rt_conf.name)) { 209634438db4Sreyk yyerror("router name truncated"); 209734438db4Sreyk free(rt); 209834438db4Sreyk YYERROR; 209934438db4Sreyk } 210034438db4Sreyk free($2); 210134438db4Sreyk rt->rt_conf.id = ++last_rt_id; 210234438db4Sreyk if (last_rt_id == INT_MAX) { 210334438db4Sreyk yyerror("too many routers defined"); 210434438db4Sreyk free(rt); 210534438db4Sreyk YYERROR; 210634438db4Sreyk } 210734438db4Sreyk TAILQ_INIT(&rt->rt_netroutes); 210834438db4Sreyk router = rt; 210934438db4Sreyk 211034438db4Sreyk tableport = -1; 211134438db4Sreyk } '{' optnl routeopts_l '}' { 211234438db4Sreyk if (!router->rt_conf.nroutes) { 211334438db4Sreyk yyerror("router %s without routes", 211434438db4Sreyk router->rt_conf.name); 211534438db4Sreyk free(router); 211634438db4Sreyk router = NULL; 211734438db4Sreyk YYERROR; 211834438db4Sreyk } 211934438db4Sreyk 212034438db4Sreyk conf->sc_routercount++; 212134438db4Sreyk TAILQ_INSERT_TAIL(conf->sc_rts, router, rt_entry); 212234438db4Sreyk router = NULL; 212334438db4Sreyk 212434438db4Sreyk tableport = 0; 212534438db4Sreyk } 212634438db4Sreyk ; 212734438db4Sreyk 212834438db4Sreyk routeopts_l : routeopts_l routeoptsl nl 212934438db4Sreyk | routeoptsl optnl 213034438db4Sreyk ; 213134438db4Sreyk 2132860302f3Sreyk routeoptsl : ROUTE addrprefix { 213334438db4Sreyk struct netroute *nr; 213434438db4Sreyk 2135a2195becSreyk if (router->rt_conf.af == AF_UNSPEC) 2136a2195becSreyk router->rt_conf.af = $2.ss.ss_family; 2137a2195becSreyk else if (router->rt_conf.af != $2.ss.ss_family) { 213834438db4Sreyk yyerror("router %s address family mismatch", 213934438db4Sreyk router->rt_conf.name); 214034438db4Sreyk YYERROR; 214134438db4Sreyk } 214234438db4Sreyk 214334438db4Sreyk if ((nr = calloc(1, sizeof(*nr))) == NULL) 214434438db4Sreyk fatal("out of memory"); 214534438db4Sreyk 214634438db4Sreyk nr->nr_conf.id = ++last_nr_id; 214734438db4Sreyk if (last_nr_id == INT_MAX) { 214834438db4Sreyk yyerror("too many routes defined"); 214934438db4Sreyk free(nr); 215034438db4Sreyk YYERROR; 215134438db4Sreyk } 2152860302f3Sreyk nr->nr_conf.prefixlen = $2.prefixlen; 215334438db4Sreyk nr->nr_conf.routerid = router->rt_conf.id; 215434438db4Sreyk nr->nr_router = router; 215534438db4Sreyk bcopy(&$2.ss, &nr->nr_conf.ss, sizeof($2.ss)); 215634438db4Sreyk 215734438db4Sreyk router->rt_conf.nroutes++; 215834438db4Sreyk conf->sc_routecount++; 215934438db4Sreyk TAILQ_INSERT_TAIL(&router->rt_netroutes, nr, nr_entry); 216034438db4Sreyk TAILQ_INSERT_TAIL(conf->sc_routes, nr, nr_route); 216134438db4Sreyk } 216234438db4Sreyk | FORWARD TO tablespec { 2163acb89df4Sreyk free(hashkey); 2164acb89df4Sreyk hashkey = NULL; 2165acb89df4Sreyk 216634438db4Sreyk if (router->rt_gwtable) { 216734438db4Sreyk yyerror("router %s table already specified", 216834438db4Sreyk router->rt_conf.name); 21692c8c2287Sclaudio purge_table(conf, conf->sc_tables, $3); 217034438db4Sreyk YYERROR; 217134438db4Sreyk } 217234438db4Sreyk router->rt_gwtable = $3; 217334438db4Sreyk router->rt_gwtable->conf.flags |= F_USED; 217434438db4Sreyk router->rt_conf.gwtable = $3->conf.id; 217534438db4Sreyk router->rt_conf.gwport = $3->conf.port; 217634438db4Sreyk } 217734438db4Sreyk | RTABLE NUMBER { 217834438db4Sreyk if (router->rt_conf.rtable) { 217934438db4Sreyk yyerror("router %s rtable already specified", 218034438db4Sreyk router->rt_conf.name); 218134438db4Sreyk YYERROR; 218234438db4Sreyk } 218334438db4Sreyk if ($2 < 0 || $2 > RT_TABLEID_MAX) { 21843512060dSpatrick yyerror("invalid rtable id %lld", $2); 218534438db4Sreyk YYERROR; 218634438db4Sreyk } 218734438db4Sreyk router->rt_conf.rtable = $2; 218834438db4Sreyk } 218934438db4Sreyk | RTLABEL STRING { 219034438db4Sreyk if (strlcpy(router->rt_conf.label, $2, 219134438db4Sreyk sizeof(router->rt_conf.label)) >= 219234438db4Sreyk sizeof(router->rt_conf.label)) { 219334438db4Sreyk yyerror("route label truncated"); 219434438db4Sreyk free($2); 219534438db4Sreyk YYERROR; 219634438db4Sreyk } 219734438db4Sreyk free($2); 219834438db4Sreyk } 219934438db4Sreyk | DISABLE { rlay->rl_conf.flags |= F_DISABLE; } 220034438db4Sreyk | include 220134438db4Sreyk ; 220234438db4Sreyk 2203c564d004Sreyk dstaf : /* empty */ { 2204c564d004Sreyk rlay->rl_conf.dstaf.ss_family = AF_UNSPEC; 2205c564d004Sreyk } 2206c564d004Sreyk | INET { 2207c564d004Sreyk rlay->rl_conf.dstaf.ss_family = AF_INET; 2208c564d004Sreyk } 2209c564d004Sreyk | INET6 STRING { 2210c564d004Sreyk struct sockaddr_in6 *sin6; 2211c564d004Sreyk 2212c564d004Sreyk sin6 = (struct sockaddr_in6 *)&rlay->rl_conf.dstaf; 2213c564d004Sreyk if (inet_pton(AF_INET6, $2, &sin6->sin6_addr) == -1) { 2214c564d004Sreyk yyerror("invalid ipv6 address %s", $2); 2215c564d004Sreyk free($2); 2216c564d004Sreyk YYERROR; 2217c564d004Sreyk } 2218c564d004Sreyk free($2); 2219c564d004Sreyk 2220c564d004Sreyk sin6->sin6_family = AF_INET6; 2221c564d004Sreyk sin6->sin6_len = sizeof(*sin6); 2222c564d004Sreyk } 2223c564d004Sreyk ; 2224c564d004Sreyk 2225feb9ff76Sreyk interface : /* empty */ { $$ = NULL; } 2226feb9ff76Sreyk | INTERFACE STRING { $$ = $2; } 2227feb9ff76Sreyk ; 2228feb9ff76Sreyk 22293847fa47Sreyk host : address { 22303847fa47Sreyk if ((hst = calloc(1, sizeof(*(hst)))) == NULL) 2231feb9ff76Sreyk fatal("out of memory"); 2232feb9ff76Sreyk 22333847fa47Sreyk if (strlcpy(hst->conf.name, $1.name, 22343847fa47Sreyk sizeof(hst->conf.name)) >= sizeof(hst->conf.name)) { 2235feb9ff76Sreyk yyerror("host name truncated"); 22363847fa47Sreyk free(hst); 2237feb9ff76Sreyk YYERROR; 2238feb9ff76Sreyk } 22393200d585Sreyk bcopy(&$1.ss, &hst->conf.ss, sizeof($1.ss)); 22403847fa47Sreyk hst->conf.id = 0; /* will be set later */ 22413847fa47Sreyk SLIST_INIT(&hst->children); 22423847fa47Sreyk } opthostflags { 22433847fa47Sreyk $$ = hst; 22443847fa47Sreyk hst = NULL; 22453847fa47Sreyk } 22463847fa47Sreyk ; 22473847fa47Sreyk 22483847fa47Sreyk opthostflags : /* empty */ 22493847fa47Sreyk | hostflags_l 22503847fa47Sreyk ; 22513847fa47Sreyk 22523847fa47Sreyk hostflags_l : hostflags hostflags_l 22533847fa47Sreyk | hostflags 22543847fa47Sreyk ; 22553847fa47Sreyk 22563847fa47Sreyk hostflags : RETRY NUMBER { 22573847fa47Sreyk if (hst->conf.retry) { 22583847fa47Sreyk yyerror("retry value already set"); 22593847fa47Sreyk YYERROR; 22603847fa47Sreyk } 22613847fa47Sreyk if ($2 < 0) { 22623512060dSpatrick yyerror("invalid retry value: %lld\n", $2); 22633847fa47Sreyk YYERROR; 22643847fa47Sreyk } 22653847fa47Sreyk hst->conf.retry = $2; 22663847fa47Sreyk } 22673847fa47Sreyk | PARENT NUMBER { 22683847fa47Sreyk if (hst->conf.parentid) { 22693847fa47Sreyk yyerror("parent value already set"); 22703847fa47Sreyk YYERROR; 22713847fa47Sreyk } 22723847fa47Sreyk if ($2 < 0) { 22733512060dSpatrick yyerror("invalid parent value: %lld\n", $2); 22743847fa47Sreyk YYERROR; 22753847fa47Sreyk } 22763847fa47Sreyk hst->conf.parentid = $2; 2277feb9ff76Sreyk } 227841835f22Sphessler | PRIORITY NUMBER { 227941835f22Sphessler if (hst->conf.priority) { 228041835f22Sphessler yyerror("priority already set"); 228141835f22Sphessler YYERROR; 228241835f22Sphessler } 228341835f22Sphessler if ($2 < 0 || $2 > RTP_MAX) { 22843512060dSpatrick yyerror("invalid priority value: %lld\n", $2); 228541835f22Sphessler YYERROR; 228641835f22Sphessler } 228741835f22Sphessler hst->conf.priority = $2; 228841835f22Sphessler } 228959354cb3Sreyk | IP TTL NUMBER { 229059354cb3Sreyk if (hst->conf.ttl) { 229159354cb3Sreyk yyerror("ttl value already set"); 229259354cb3Sreyk YYERROR; 229359354cb3Sreyk } 229459354cb3Sreyk if ($3 < 0) { 22953512060dSpatrick yyerror("invalid ttl value: %lld\n", $3); 229659354cb3Sreyk YYERROR; 229759354cb3Sreyk } 229859354cb3Sreyk hst->conf.ttl = $3; 229959354cb3Sreyk } 2300feb9ff76Sreyk ; 2301feb9ff76Sreyk 2302d1800b62Sreyk address : STRING { 2303a2195becSreyk struct address *h; 2304d1800b62Sreyk struct addresslist al; 2305d1800b62Sreyk 2306d1800b62Sreyk if (strlcpy($$.name, $1, 2307d1800b62Sreyk sizeof($$.name)) >= sizeof($$.name)) { 2308d1800b62Sreyk yyerror("host name truncated"); 2309d1800b62Sreyk free($1); 2310d1800b62Sreyk YYERROR; 2311d1800b62Sreyk } 2312d1800b62Sreyk 2313d1800b62Sreyk TAILQ_INIT(&al); 2314d1800b62Sreyk if (host($1, &al, 1, NULL, NULL, -1) <= 0) { 2315d1800b62Sreyk yyerror("invalid host %s", $1); 2316d1800b62Sreyk free($1); 2317d1800b62Sreyk YYERROR; 2318d1800b62Sreyk } 2319d1800b62Sreyk free($1); 2320a2195becSreyk h = TAILQ_FIRST(&al); 2321a2195becSreyk memcpy(&$$.ss, &h->ss, sizeof($$.ss)); 2322a2195becSreyk host_free(&al); 2323d1800b62Sreyk } 2324d1800b62Sreyk ; 2325d1800b62Sreyk 2326860302f3Sreyk addrprefix : address '/' NUMBER { 2327860302f3Sreyk $$ = $1; 2328860302f3Sreyk if (($$.ss.ss_family == AF_INET && 2329860302f3Sreyk ($3 > 32 || $3 < 0)) || 2330860302f3Sreyk ($$.ss.ss_family == AF_INET6 && 2331860302f3Sreyk ($3 > 128 || $3 < 0))) { 23323512060dSpatrick yyerror("invalid prefixlen %lld", $3); 2333860302f3Sreyk YYERROR; 2334860302f3Sreyk } 2335860302f3Sreyk $$.prefixlen = $3; 2336860302f3Sreyk } 2337860302f3Sreyk | address { 2338860302f3Sreyk $$ = $1; 2339860302f3Sreyk if ($$.ss.ss_family == AF_INET) 2340860302f3Sreyk $$.prefixlen = 32; 2341860302f3Sreyk else if ($$.ss.ss_family == AF_INET6) 2342860302f3Sreyk $$.prefixlen = 128; 2343860302f3Sreyk } 2344860302f3Sreyk ; 2345860302f3Sreyk 23463847fa47Sreyk retry : /* empty */ { $$ = 0; } 23476debaf0dSpyr | RETRY NUMBER { 23486debaf0dSpyr if (($$ = $2) < 0) { 23493512060dSpatrick yyerror("invalid retry value: %lld\n", $2); 23506debaf0dSpyr YYERROR; 23516debaf0dSpyr } 23526debaf0dSpyr } 23532edd718bSreyk ; 23542edd718bSreyk 23556debaf0dSpyr timeout : NUMBER 2356fc29228bSreyk { 23576debaf0dSpyr if ($1 < 0) { 23583512060dSpatrick yyerror("invalid timeout: %lld\n", $1); 23596debaf0dSpyr YYERROR; 23606debaf0dSpyr } 2361fc29228bSreyk $$.tv_sec = $1 / 1000; 2362fc29228bSreyk $$.tv_usec = ($1 % 1000) * 1000; 2363fc29228bSreyk } 2364fc29228bSreyk ; 2365fc29228bSreyk 23662edd718bSreyk comma : ',' 2367af50a7a9Sreyk | nl 23682edd718bSreyk | /* empty */ 23692edd718bSreyk ; 23702edd718bSreyk 2371feb9ff76Sreyk optnl : '\n' optnl 2372feb9ff76Sreyk | 2373feb9ff76Sreyk ; 2374feb9ff76Sreyk 2375feb9ff76Sreyk nl : '\n' optnl 2376feb9ff76Sreyk ; 2377feb9ff76Sreyk %% 2378feb9ff76Sreyk 2379feb9ff76Sreyk struct keywords { 2380feb9ff76Sreyk const char *k_name; 2381feb9ff76Sreyk int k_val; 2382feb9ff76Sreyk }; 2383feb9ff76Sreyk 2384feb9ff76Sreyk int 2385feb9ff76Sreyk yyerror(const char *fmt, ...) 2386feb9ff76Sreyk { 2387feb9ff76Sreyk va_list ap; 2388f7d69561Sbluhm char *msg; 2389feb9ff76Sreyk 239020741916Sderaadt file->errors++; 2391feb9ff76Sreyk va_start(ap, fmt); 2392f7d69561Sbluhm if (vasprintf(&msg, fmt, ap) == -1) 2393f7d69561Sbluhm fatalx("yyerror vasprintf"); 2394feb9ff76Sreyk va_end(ap); 2395f7d69561Sbluhm logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg); 2396f7d69561Sbluhm free(msg); 2397feb9ff76Sreyk return (0); 2398feb9ff76Sreyk } 2399feb9ff76Sreyk 2400feb9ff76Sreyk int 2401feb9ff76Sreyk kw_cmp(const void *k, const void *e) 2402feb9ff76Sreyk { 2403feb9ff76Sreyk return (strcmp(k, ((const struct keywords *)e)->k_name)); 2404feb9ff76Sreyk } 2405feb9ff76Sreyk 2406feb9ff76Sreyk int 2407feb9ff76Sreyk lookup(char *s) 2408feb9ff76Sreyk { 2409feb9ff76Sreyk /* this has to be sorted always */ 2410feb9ff76Sreyk static const struct keywords keywords[] = { 2411c26b8e61Smartijn { "agentx", AGENTX }, 24122edd718bSreyk { "append", APPEND }, 2413dae1a80bSreyk { "backlog", BACKLOG }, 2414feb9ff76Sreyk { "backup", BACKUP }, 24153f229715Srob { "binary", BINARY }, 2416cb8b0e56Sreyk { "block", BLOCK }, 24172edd718bSreyk { "buffer", BUFFER }, 2418062e776aSreyk { "ca", CA }, 24192edd718bSreyk { "cache", CACHE }, 2420cf39ad79Sreyk { "cert", CERTIFICATE }, 24210be9d00aSbenno { "changes", CHANGES }, 2422feb9ff76Sreyk { "check", CHECK }, 24230be9d00aSbenno { "checks", CHECKS }, 2424dae1a80bSreyk { "ciphers", CIPHERS }, 2425*92388deeStb { "client", CLIENT }, 2426feb9ff76Sreyk { "code", CODE }, 24270be9d00aSbenno { "connection", CONNECTION }, 2428c26b8e61Smartijn { "context", CONTEXT }, 2429fc850f73Sreyk { "cookie", COOKIE }, 24302edd718bSreyk { "demote", DEMOTE }, 2431523113bfSreyk { "destination", DESTINATION }, 2432feb9ff76Sreyk { "digest", DIGEST }, 2433feb9ff76Sreyk { "disable", DISABLE }, 2434353c00bcSclaudio { "ecdhe", ECDHE }, 24353675f6daSreyk { "edh", EDH }, 243607c84b7eSreyk { "error", ERROR }, 24370be9d00aSbenno { "errors", ERRORS }, 2438bf9a553dSreyk { "expect", EXPECT }, 2439feb9ff76Sreyk { "external", EXTERNAL }, 24402faabbadSreyk { "file", FILENAME }, 24412edd718bSreyk { "forward", FORWARD }, 24422edd718bSreyk { "from", FROM }, 24432edd718bSreyk { "hash", HASH }, 2444a7bda987Spyr { "header", HEADER }, 24450ad20b85Sbenno { "headerlen", HEADERLEN }, 2446feb9ff76Sreyk { "host", HOST }, 24470ad20b85Sbenno { "http", HTTP }, 2448feb9ff76Sreyk { "icmp", ICMP }, 2449386626e8Sreyk { "include", INCLUDE }, 2450c564d004Sreyk { "inet", INET }, 2451c564d004Sreyk { "inet6", INET6 }, 2452feb9ff76Sreyk { "interface", INTERFACE }, 2453feb9ff76Sreyk { "interval", INTERVAL }, 245477273a16Sreyk { "ip", IP }, 2455cf39ad79Sreyk { "key", KEY }, 24563ca2f577Sreyk { "keypair", KEYPAIR }, 2457485dd52fSreyk { "label", LABEL }, 245859e187d0Sreyk { "least-states", LEASTSTATES }, 24592edd718bSreyk { "listen", LISTEN }, 24602edd718bSreyk { "loadbalance", LOADBALANCE }, 2461609cf3a7Sreyk { "log", LOG }, 24622edd718bSreyk { "lookup", LOOKUP }, 2463e48c97edSreyk { "match", MATCH }, 2464cb8b0e56Sreyk { "method", METHOD }, 2465af50a7a9Sreyk { "mode", MODE }, 24662edd718bSreyk { "nat", NAT }, 24672edd718bSreyk { "no", NO }, 24682edd718bSreyk { "nodelay", NODELAY }, 2469bf9a553dSreyk { "nothing", NOTHING }, 24702edd718bSreyk { "on", ON }, 24713675f6daSreyk { "params", PARAMS }, 2472c723f8edSreyk { "parent", PARENT }, 2473cb8b0e56Sreyk { "pass", PASS }, 2474cf39ad79Sreyk { "password", PASSWORD }, 247525963c4bSreyk { "path", PATH }, 24767c726e76Ssashan { "pflog", PFLOG }, 2477cb8b0e56Sreyk { "pftag", PFTAG }, 2478feb9ff76Sreyk { "port", PORT }, 24792edd718bSreyk { "prefork", PREFORK }, 248041835f22Sphessler { "priority", PRIORITY }, 24812edd718bSreyk { "protocol", PROTO }, 2482d2491b41Sreyk { "query", QUERYSTR }, 2483cb8b0e56Sreyk { "quick", QUICK }, 248459e187d0Sreyk { "random", RANDOM }, 2485feb9ff76Sreyk { "real", REAL }, 2486af50a7a9Sreyk { "redirect", REDIRECT }, 24872edd718bSreyk { "relay", RELAY }, 24882edd718bSreyk { "remove", REMOVE }, 248932678a96Sreyk { "request", REQUEST }, 249032678a96Sreyk { "response", RESPONSE }, 24912edd718bSreyk { "retry", RETRY }, 249207c84b7eSreyk { "return", RETURN }, 24932edd718bSreyk { "roundrobin", ROUNDROBIN }, 24940eb8c740Sreyk { "route", ROUTE }, 249534438db4Sreyk { "router", ROUTER }, 249634438db4Sreyk { "rtable", RTABLE }, 249734438db4Sreyk { "rtlabel", RTLABEL }, 24982edd718bSreyk { "sack", SACK }, 24994156152fSreyk { "script", SCRIPT }, 2500bf9a553dSreyk { "send", SEND }, 25012edd718bSreyk { "session", SESSION }, 2502cb8b0e56Sreyk { "set", SET }, 25032edd718bSreyk { "socket", SOCKET }, 250459e187d0Sreyk { "source-hash", SRCHASH }, 2505471dd29dSreyk { "splice", SPLICE }, 25060be9d00aSbenno { "state", STATE }, 2507682bf2adSreyk { "sticky-address", STICKYADDR }, 2508eeb1fea4Sdenis { "strip", STRIP }, 250907c84b7eSreyk { "style", STYLE }, 2510feb9ff76Sreyk { "table", TABLE }, 2511feb9ff76Sreyk { "tag", TAG }, 2512cb8b0e56Sreyk { "tagged", TAGGED }, 2513feb9ff76Sreyk { "tcp", TCP }, 25149c908525Sclaudio { "tickets", TICKETS }, 2515feb9ff76Sreyk { "timeout", TIMEOUT }, 25167bb52228Sreyk { "tls", TLS }, 25172edd718bSreyk { "to", TO }, 25183c03a838Sreyk { "transparent", TRANSPARENT }, 251959354cb3Sreyk { "ttl", TTL }, 2520a088a0c2Sreyk { "url", URL }, 2521cb8b0e56Sreyk { "value", VALUE }, 2522e7742cb1Sbenno { "websockets", WEBSOCKETS }, 2523aadc8ea4Sreyk { "with", WITH } 2524feb9ff76Sreyk }; 2525feb9ff76Sreyk const struct keywords *p; 2526feb9ff76Sreyk 2527feb9ff76Sreyk p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 2528feb9ff76Sreyk sizeof(keywords[0]), kw_cmp); 2529feb9ff76Sreyk 2530feb9ff76Sreyk if (p) 2531feb9ff76Sreyk return (p->k_val); 2532feb9ff76Sreyk else 2533feb9ff76Sreyk return (STRING); 2534feb9ff76Sreyk } 2535feb9ff76Sreyk 2536feb9ff76Sreyk 253720331712Sdenis #define START_EXPAND 1 253820331712Sdenis #define DONE_EXPAND 2 253920331712Sdenis 254020331712Sdenis static int expanding; 254120331712Sdenis 254220331712Sdenis int 254320331712Sdenis igetc(void) 254420331712Sdenis { 254520331712Sdenis int c; 254620331712Sdenis 254720331712Sdenis while (1) { 254820331712Sdenis if (file->ungetpos > 0) 254920331712Sdenis c = file->ungetbuf[--file->ungetpos]; 255020331712Sdenis else c = getc(file->stream); 255120331712Sdenis 255220331712Sdenis if (c == START_EXPAND) 255320331712Sdenis expanding = 1; 255420331712Sdenis else if (c == DONE_EXPAND) 255520331712Sdenis expanding = 0; 255620331712Sdenis else 255720331712Sdenis break; 255820331712Sdenis } 255920331712Sdenis return (c); 256020331712Sdenis } 2561feb9ff76Sreyk 2562feb9ff76Sreyk int 256320741916Sderaadt lgetc(int quotec) 2564feb9ff76Sreyk { 2565feb9ff76Sreyk int c, next; 2566feb9ff76Sreyk 256720741916Sderaadt if (quotec) { 256820331712Sdenis if ((c = igetc()) == EOF) { 2569ce7146fbSreyk yyerror("reached end of file while parsing " 2570ce7146fbSreyk "quoted string"); 2571c6004ab9Smpf if (file == topfile || popfile() == EOF) 257220741916Sderaadt return (EOF); 257320741916Sderaadt return (quotec); 257420741916Sderaadt } 2575bdd8283dSpyr return (c); 2576bdd8283dSpyr } 2577bdd8283dSpyr 257820331712Sdenis while ((c = igetc()) == '\\') { 257920331712Sdenis next = igetc(); 2580bdd8283dSpyr if (next != '\n') { 2581feb9ff76Sreyk c = next; 2582feb9ff76Sreyk break; 2583feb9ff76Sreyk } 258420741916Sderaadt yylval.lineno = file->lineno; 258520741916Sderaadt file->lineno++; 2586feb9ff76Sreyk } 2587feb9ff76Sreyk 258820331712Sdenis if (c == EOF) { 258920331712Sdenis /* 259020331712Sdenis * Fake EOL when hit EOF for the first time. This gets line 259120331712Sdenis * count right if last line in included file is syntactically 259220331712Sdenis * invalid and has no newline. 259320331712Sdenis */ 259420331712Sdenis if (file->eof_reached == 0) { 259520331712Sdenis file->eof_reached = 1; 259620331712Sdenis return ('\n'); 259720331712Sdenis } 259820741916Sderaadt while (c == EOF) { 2599c6004ab9Smpf if (file == topfile || popfile() == EOF) 260020741916Sderaadt return (EOF); 260120331712Sdenis c = igetc(); 260220331712Sdenis } 260320741916Sderaadt } 2604feb9ff76Sreyk return (c); 2605feb9ff76Sreyk } 2606feb9ff76Sreyk 260720331712Sdenis void 2608feb9ff76Sreyk lungetc(int c) 2609feb9ff76Sreyk { 2610feb9ff76Sreyk if (c == EOF) 261120331712Sdenis return; 261220331712Sdenis 261320331712Sdenis if (file->ungetpos >= file->ungetsize) { 261420331712Sdenis void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); 261520331712Sdenis if (p == NULL) 2616a062aa9dSkrw err(1, "%s", __func__); 261720331712Sdenis file->ungetbuf = p; 261820331712Sdenis file->ungetsize *= 2; 2619feb9ff76Sreyk } 262020331712Sdenis file->ungetbuf[file->ungetpos++] = c; 2621feb9ff76Sreyk } 2622feb9ff76Sreyk 2623feb9ff76Sreyk int 2624feb9ff76Sreyk findeol(void) 2625feb9ff76Sreyk { 2626feb9ff76Sreyk int c; 2627feb9ff76Sreyk 2628feb9ff76Sreyk /* skip to either EOF or the first real EOL */ 2629feb9ff76Sreyk while (1) { 2630d5d66eaeSderaadt c = lgetc(0); 2631bdd8283dSpyr if (c == '\n') { 263220741916Sderaadt file->lineno++; 2633feb9ff76Sreyk break; 2634feb9ff76Sreyk } 2635feb9ff76Sreyk if (c == EOF) 2636feb9ff76Sreyk break; 2637feb9ff76Sreyk } 2638feb9ff76Sreyk return (ERROR); 2639feb9ff76Sreyk } 2640feb9ff76Sreyk 2641feb9ff76Sreyk int 2642feb9ff76Sreyk yylex(void) 2643feb9ff76Sreyk { 264408f6ba19Snaddy char buf[8096]; 264508f6ba19Snaddy char *p, *val; 264620741916Sderaadt int quotec, next, c; 2647feb9ff76Sreyk int token; 2648feb9ff76Sreyk 2649feb9ff76Sreyk top: 2650feb9ff76Sreyk p = buf; 26512053f12aSmpf while ((c = lgetc(0)) == ' ' || c == '\t') 2652feb9ff76Sreyk ; /* nothing */ 2653feb9ff76Sreyk 265420741916Sderaadt yylval.lineno = file->lineno; 2655feb9ff76Sreyk if (c == '#') 2656d5d66eaeSderaadt while ((c = lgetc(0)) != '\n' && c != EOF) 2657feb9ff76Sreyk ; /* nothing */ 265820331712Sdenis if (c == '$' && !expanding) { 2659feb9ff76Sreyk while (1) { 2660d5d66eaeSderaadt if ((c = lgetc(0)) == EOF) 2661feb9ff76Sreyk return (0); 2662feb9ff76Sreyk 2663feb9ff76Sreyk if (p + 1 >= buf + sizeof(buf) - 1) { 2664feb9ff76Sreyk yyerror("string too long"); 2665feb9ff76Sreyk return (findeol()); 2666feb9ff76Sreyk } 2667feb9ff76Sreyk if (isalnum(c) || c == '_') { 2668015d7b4dSbenno *p++ = c; 2669feb9ff76Sreyk continue; 2670feb9ff76Sreyk } 2671feb9ff76Sreyk *p = '\0'; 2672feb9ff76Sreyk lungetc(c); 2673feb9ff76Sreyk break; 2674feb9ff76Sreyk } 2675feb9ff76Sreyk val = symget(buf); 2676feb9ff76Sreyk if (val == NULL) { 2677feb9ff76Sreyk yyerror("macro '%s' not defined", buf); 2678feb9ff76Sreyk return (findeol()); 2679feb9ff76Sreyk } 268020331712Sdenis p = val + strlen(val) - 1; 268120331712Sdenis lungetc(DONE_EXPAND); 268220331712Sdenis while (p >= val) { 268308f6ba19Snaddy lungetc((unsigned char)*p); 268420331712Sdenis p--; 268520331712Sdenis } 268620331712Sdenis lungetc(START_EXPAND); 2687feb9ff76Sreyk goto top; 2688feb9ff76Sreyk } 2689feb9ff76Sreyk 2690feb9ff76Sreyk switch (c) { 2691feb9ff76Sreyk case '\'': 2692feb9ff76Sreyk case '"': 269320741916Sderaadt quotec = c; 2694feb9ff76Sreyk while (1) { 269520741916Sderaadt if ((c = lgetc(quotec)) == EOF) 2696feb9ff76Sreyk return (0); 2697d5d66eaeSderaadt if (c == '\n') { 269820741916Sderaadt file->lineno++; 2699d5d66eaeSderaadt continue; 2700d5d66eaeSderaadt } else if (c == '\\') { 270120741916Sderaadt if ((next = lgetc(quotec)) == EOF) 2702d5d66eaeSderaadt return (0); 2703a1533359Ssashan if (next == quotec || next == ' ' || 2704a1533359Ssashan next == '\t') 2705bdd8283dSpyr c = next; 2706daf24110Shenning else if (next == '\n') { 2707daf24110Shenning file->lineno++; 2708ea014f46Sderaadt continue; 2709daf24110Shenning } else 2710bdd8283dSpyr lungetc(next); 271120741916Sderaadt } else if (c == quotec) { 2712feb9ff76Sreyk *p = '\0'; 2713feb9ff76Sreyk break; 271441eef22fSjsg } else if (c == '\0') { 271541eef22fSjsg yyerror("syntax error"); 271641eef22fSjsg return (findeol()); 2717feb9ff76Sreyk } 2718feb9ff76Sreyk if (p + 1 >= buf + sizeof(buf) - 1) { 2719feb9ff76Sreyk yyerror("string too long"); 2720feb9ff76Sreyk return (findeol()); 2721feb9ff76Sreyk } 2722015d7b4dSbenno *p++ = c; 2723feb9ff76Sreyk } 2724feb9ff76Sreyk yylval.v.string = strdup(buf); 2725feb9ff76Sreyk if (yylval.v.string == NULL) 2726a062aa9dSkrw err(1, "%s", __func__); 2727feb9ff76Sreyk return (STRING); 2728feb9ff76Sreyk } 2729feb9ff76Sreyk 27306debaf0dSpyr #define allowed_to_end_number(x) \ 27310cf2c9c3Smpf (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 27326debaf0dSpyr 27336debaf0dSpyr if (c == '-' || isdigit(c)) { 27346debaf0dSpyr do { 27356debaf0dSpyr *p++ = c; 2736915c3f33Sderaadt if ((size_t)(p-buf) >= sizeof(buf)) { 27376debaf0dSpyr yyerror("string too long"); 27386debaf0dSpyr return (findeol()); 27396debaf0dSpyr } 2740d5d66eaeSderaadt } while ((c = lgetc(0)) != EOF && isdigit(c)); 27416debaf0dSpyr lungetc(c); 27426debaf0dSpyr if (p == buf + 1 && buf[0] == '-') 27436debaf0dSpyr goto nodigits; 27446debaf0dSpyr if (c == EOF || allowed_to_end_number(c)) { 27456debaf0dSpyr const char *errstr = NULL; 27466debaf0dSpyr 27476debaf0dSpyr *p = '\0'; 2748d5d66eaeSderaadt yylval.v.number = strtonum(buf, LLONG_MIN, 2749d5d66eaeSderaadt LLONG_MAX, &errstr); 27506debaf0dSpyr if (errstr) { 275103625741Spyr yyerror("\"%s\" invalid number: %s", 275203625741Spyr buf, errstr); 27536debaf0dSpyr return (findeol()); 27546debaf0dSpyr } 27556debaf0dSpyr return (NUMBER); 27566debaf0dSpyr } else { 27576debaf0dSpyr nodigits: 27586debaf0dSpyr while (p > buf + 1) 275908f6ba19Snaddy lungetc((unsigned char)*--p); 276008f6ba19Snaddy c = (unsigned char)*--p; 27616debaf0dSpyr if (c == '-') 27626debaf0dSpyr return (c); 27636debaf0dSpyr } 27646debaf0dSpyr } 27656debaf0dSpyr 2766feb9ff76Sreyk #define allowed_in_string(x) \ 2767feb9ff76Sreyk (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 2768af50a7a9Sreyk x != '{' && x != '}' && x != '<' && x != '>' && \ 2769feb9ff76Sreyk x != '!' && x != '=' && x != '#' && \ 277034438db4Sreyk x != ',' && x != '/')) 2771feb9ff76Sreyk 2772feb9ff76Sreyk if (isalnum(c) || c == ':' || c == '_') { 2773feb9ff76Sreyk do { 2774feb9ff76Sreyk *p++ = c; 2775915c3f33Sderaadt if ((size_t)(p-buf) >= sizeof(buf)) { 2776feb9ff76Sreyk yyerror("string too long"); 2777feb9ff76Sreyk return (findeol()); 2778feb9ff76Sreyk } 2779d5d66eaeSderaadt } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 2780feb9ff76Sreyk lungetc(c); 2781feb9ff76Sreyk *p = '\0'; 2782feb9ff76Sreyk if ((token = lookup(buf)) == STRING) 2783feb9ff76Sreyk if ((yylval.v.string = strdup(buf)) == NULL) 2784a062aa9dSkrw err(1, "%s", __func__); 2785feb9ff76Sreyk return (token); 2786feb9ff76Sreyk } 2787feb9ff76Sreyk if (c == '\n') { 278820741916Sderaadt yylval.lineno = file->lineno; 278920741916Sderaadt file->lineno++; 2790feb9ff76Sreyk } 2791feb9ff76Sreyk if (c == EOF) 2792feb9ff76Sreyk return (0); 2793feb9ff76Sreyk return (c); 2794feb9ff76Sreyk } 2795feb9ff76Sreyk 279620741916Sderaadt int 279720741916Sderaadt check_file_secrecy(int fd, const char *fname) 279820741916Sderaadt { 279920741916Sderaadt struct stat st; 280020741916Sderaadt 280120741916Sderaadt if (fstat(fd, &st)) { 2802e6037baaSpyr log_warn("cannot stat %s", fname); 280320741916Sderaadt return (-1); 280420741916Sderaadt } 280520741916Sderaadt if (st.st_uid != 0 && st.st_uid != getuid()) { 2806e6037baaSpyr log_warnx("%s: owner not root or current user", fname); 280720741916Sderaadt return (-1); 280820741916Sderaadt } 28097140c133Shenning if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { 28107140c133Shenning log_warnx("%s: group writable or world read/writable", fname); 281120741916Sderaadt return (-1); 281220741916Sderaadt } 281320741916Sderaadt return (0); 281420741916Sderaadt } 281520741916Sderaadt 281620741916Sderaadt struct file * 281720741916Sderaadt pushfile(const char *name, int secret) 281820741916Sderaadt { 281920741916Sderaadt struct file *nfile; 282020741916Sderaadt 28217fc93de0Stobias if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 28226a3d55f9Skrw log_warn("%s", __func__); 282320741916Sderaadt return (NULL); 2824953e8caaSderaadt } 28257fc93de0Stobias if ((nfile->name = strdup(name)) == NULL) { 28266a3d55f9Skrw log_warn("%s", __func__); 28277fc93de0Stobias free(nfile); 28287fc93de0Stobias return (NULL); 28297fc93de0Stobias } 283020741916Sderaadt if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 283185a8c65fSreyk log_warn("%s: %s", __func__, nfile->name); 283220741916Sderaadt free(nfile->name); 283320741916Sderaadt free(nfile); 283420741916Sderaadt return (NULL); 283520741916Sderaadt } else if (secret && 283620741916Sderaadt check_file_secrecy(fileno(nfile->stream), nfile->name)) { 283720741916Sderaadt fclose(nfile->stream); 283820741916Sderaadt free(nfile->name); 283920741916Sderaadt free(nfile); 284020741916Sderaadt return (NULL); 284120741916Sderaadt } 284220331712Sdenis nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; 284320331712Sdenis nfile->ungetsize = 16; 284420331712Sdenis nfile->ungetbuf = malloc(nfile->ungetsize); 284520331712Sdenis if (nfile->ungetbuf == NULL) { 28466a3d55f9Skrw log_warn("%s", __func__); 284720331712Sdenis fclose(nfile->stream); 284820331712Sdenis free(nfile->name); 284920331712Sdenis free(nfile); 285020331712Sdenis return (NULL); 285120331712Sdenis } 285220741916Sderaadt TAILQ_INSERT_TAIL(&files, nfile, entry); 285320741916Sderaadt return (nfile); 285420741916Sderaadt } 285520741916Sderaadt 285620741916Sderaadt int 285720741916Sderaadt popfile(void) 285820741916Sderaadt { 285920741916Sderaadt struct file *prev; 286020741916Sderaadt 2861c6004ab9Smpf if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 286220741916Sderaadt prev->errors += file->errors; 2863c6004ab9Smpf 286420741916Sderaadt TAILQ_REMOVE(&files, file, entry); 286520741916Sderaadt fclose(file->stream); 286620741916Sderaadt free(file->name); 286720331712Sdenis free(file->ungetbuf); 286820741916Sderaadt free(file); 286920741916Sderaadt file = prev; 2870c6004ab9Smpf return (file ? 0 : EOF); 287120741916Sderaadt } 287220741916Sderaadt 2873a2195becSreyk int 2874a2195becSreyk parse_config(const char *filename, struct relayd *x_conf) 2875a2195becSreyk { 2876a2195becSreyk struct sym *sym, *next; 2877a2195becSreyk 2878a2195becSreyk conf = x_conf; 2879a2195becSreyk if (config_init(conf) == -1) { 2880a2195becSreyk log_warn("%s: cannot initialize configuration", __func__); 2881a2195becSreyk return (-1); 2882a2195becSreyk } 2883a2195becSreyk 2884a2195becSreyk errors = 0; 2885a2195becSreyk 2886a2195becSreyk if ((file = pushfile(filename, 0)) == NULL) 2887a2195becSreyk return (-1); 2888a2195becSreyk 2889a2195becSreyk topfile = file; 2890a2195becSreyk setservent(1); 2891a2195becSreyk 2892a2195becSreyk yyparse(); 2893a2195becSreyk errors = file->errors; 289420331712Sdenis while (popfile() != EOF) 289520331712Sdenis ; 2896a2195becSreyk 2897a2195becSreyk endservent(); 2898a2195becSreyk endprotoent(); 2899a2195becSreyk 2900a2195becSreyk /* Free macros */ 290146bca67bSkrw TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { 2902a2195becSreyk if (!sym->persist) { 2903a2195becSreyk free(sym->nam); 2904a2195becSreyk free(sym->val); 2905a2195becSreyk TAILQ_REMOVE(&symhead, sym, entry); 2906a2195becSreyk free(sym); 2907a2195becSreyk } 2908a2195becSreyk } 2909a2195becSreyk 2910a2195becSreyk return (errors ? -1 : 0); 2911a2195becSreyk } 2912a2195becSreyk 2913a2195becSreyk int 2914a2195becSreyk load_config(const char *filename, struct relayd *x_conf) 2915feb9ff76Sreyk { 2916feb9ff76Sreyk struct sym *sym, *next; 291799ca6689Sreyk struct table *nexttb; 2918c723f8edSreyk struct host *h, *ph; 2919416fa9c0Sreyk struct relay_table *rlt; 2920feb9ff76Sreyk 2921a2195becSreyk conf = x_conf; 2922586b5f8aSreyk conf->sc_conf.flags = 0; 29236e8056acSreyk 2924a2195becSreyk loadcfg = 1; 2925ce00797eSpyr errors = 0; 29269591a9f7Spyr last_host_id = last_table_id = last_rdr_id = last_proto_id = 292734438db4Sreyk last_relay_id = last_rt_id = last_nr_id = 0; 2928a518a4b9Spyr 29299591a9f7Spyr rdr = NULL; 2930ce00797eSpyr table = NULL; 2931ce00797eSpyr rlay = NULL; 2932ce00797eSpyr proto = NULL; 293334438db4Sreyk router = NULL; 2934ce00797eSpyr 2935a2195becSreyk if ((file = pushfile(filename, 0)) == NULL) 2936a2195becSreyk return (-1); 29372edd718bSreyk 2938d6fc1e53Smpf topfile = file; 293919c8d0a5Sreyk setservent(1); 294020741916Sderaadt 2941feb9ff76Sreyk yyparse(); 294220741916Sderaadt errors = file->errors; 294320331712Sdenis while (popfile() != EOF) 294420331712Sdenis ; 294520741916Sderaadt 294619c8d0a5Sreyk endservent(); 29474ac0ad23Sreyk endprotoent(); 2948feb9ff76Sreyk 2949feb9ff76Sreyk /* Free macros and check which have not been used. */ 2950feb9ff76Sreyk for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { 295120741916Sderaadt next = TAILQ_NEXT(sym, entry); 2952586b5f8aSreyk if ((conf->sc_conf.opts & RELAYD_OPT_VERBOSE) && !sym->used) 2953feb9ff76Sreyk fprintf(stderr, "warning: macro '%s' not " 2954feb9ff76Sreyk "used\n", sym->nam); 2955feb9ff76Sreyk if (!sym->persist) { 2956feb9ff76Sreyk free(sym->nam); 2957feb9ff76Sreyk free(sym->val); 295820741916Sderaadt TAILQ_REMOVE(&symhead, sym, entry); 2959feb9ff76Sreyk free(sym); 2960feb9ff76Sreyk } 2961feb9ff76Sreyk } 2962feb9ff76Sreyk 296334438db4Sreyk if (TAILQ_EMPTY(conf->sc_rdrs) && 296434438db4Sreyk TAILQ_EMPTY(conf->sc_relays) && 296534438db4Sreyk TAILQ_EMPTY(conf->sc_rts)) { 296634438db4Sreyk log_warnx("no actions, nothing to do"); 2967feb9ff76Sreyk errors++; 2968feb9ff76Sreyk } 2969feb9ff76Sreyk 29709f29cb9cSreyk /* Cleanup relay list to inherit */ 29719f29cb9cSreyk while ((rlay = TAILQ_FIRST(&relays)) != NULL) { 29729f29cb9cSreyk TAILQ_REMOVE(&relays, rlay, rl_entry); 2973416fa9c0Sreyk while ((rlt = TAILQ_FIRST(&rlay->rl_tables))) { 2974416fa9c0Sreyk TAILQ_REMOVE(&rlay->rl_tables, rlt, rlt_entry); 2975416fa9c0Sreyk free(rlt); 2976416fa9c0Sreyk } 29779f29cb9cSreyk free(rlay); 29789f29cb9cSreyk } 29799f29cb9cSreyk 2980586b5f8aSreyk if (timercmp(&conf->sc_conf.timeout, &conf->sc_conf.interval, >=)) { 2981f1464c62Sreyk log_warnx("global timeout exceeds interval"); 2982f1464c62Sreyk errors++; 2983f1464c62Sreyk } 2984f1464c62Sreyk 2985feb9ff76Sreyk /* Verify that every table is used */ 298635d10c30Sreyk for (table = TAILQ_FIRST(conf->sc_tables); table != NULL; 298799ca6689Sreyk table = nexttb) { 298899ca6689Sreyk nexttb = TAILQ_NEXT(table, entry); 298968b79041Spyr if (table->conf.port == 0) { 299035d10c30Sreyk TAILQ_REMOVE(conf->sc_tables, table, entry); 299199ca6689Sreyk while ((h = TAILQ_FIRST(&table->hosts)) != NULL) { 299299ca6689Sreyk TAILQ_REMOVE(&table->hosts, h, entry); 299399ca6689Sreyk free(h); 299499ca6689Sreyk } 299599ca6689Sreyk if (table->sendbuf != NULL) 299699ca6689Sreyk free(table->sendbuf); 29973f229715Srob if (table->sendbinbuf != NULL) 29983f229715Srob ibuf_free(table->sendbinbuf); 299999ca6689Sreyk free(table); 300099ca6689Sreyk continue; 300199ca6689Sreyk } 3002c723f8edSreyk 3003c723f8edSreyk TAILQ_FOREACH(h, &table->hosts, entry) { 3004c723f8edSreyk if (h->conf.parentid) { 3005c723f8edSreyk ph = host_find(conf, h->conf.parentid); 3006c723f8edSreyk 3007c723f8edSreyk /* Validate the parent id */ 3008c723f8edSreyk if (h->conf.id == h->conf.parentid || 3009c723f8edSreyk ph == NULL || ph->conf.parentid) 3010c723f8edSreyk ph = NULL; 3011c723f8edSreyk 3012c723f8edSreyk if (ph == NULL) { 3013c723f8edSreyk log_warnx("host parent id %d invalid", 3014c723f8edSreyk h->conf.parentid); 3015c723f8edSreyk errors++; 3016c723f8edSreyk } else 30171584e35dSreyk SLIST_INSERT_HEAD(&ph->children, 3018c723f8edSreyk h, child); 3019c723f8edSreyk } 3020c723f8edSreyk } 3021c723f8edSreyk 302268b79041Spyr if (!(table->conf.flags & F_USED)) { 302368b79041Spyr log_warnx("unused table: %s", table->conf.name); 3024feb9ff76Sreyk errors++; 3025feb9ff76Sreyk } 3026586b5f8aSreyk if (timercmp(&table->conf.timeout, 3027586b5f8aSreyk &conf->sc_conf.interval, >=)) { 3028f1464c62Sreyk log_warnx("table timeout exceeds interval: %s", 302968b79041Spyr table->conf.name); 3030f1464c62Sreyk errors++; 3031f1464c62Sreyk } 3032f1464c62Sreyk } 3033feb9ff76Sreyk 30342edd718bSreyk /* Verify that every non-default protocol is used */ 303535d10c30Sreyk TAILQ_FOREACH(proto, conf->sc_protos, entry) { 30362edd718bSreyk if (!(proto->flags & F_USED)) { 30372edd718bSreyk log_warnx("unused protocol: %s", proto->name); 30382edd718bSreyk } 30392edd718bSreyk } 30402edd718bSreyk 3041a2195becSreyk return (errors ? -1 : 0); 3042feb9ff76Sreyk } 3043feb9ff76Sreyk 3044feb9ff76Sreyk int 3045feb9ff76Sreyk symset(const char *nam, const char *val, int persist) 3046feb9ff76Sreyk { 3047feb9ff76Sreyk struct sym *sym; 3048feb9ff76Sreyk 304954c95b7aSkrw TAILQ_FOREACH(sym, &symhead, entry) { 305054c95b7aSkrw if (strcmp(nam, sym->nam) == 0) 305154c95b7aSkrw break; 305254c95b7aSkrw } 3053feb9ff76Sreyk 3054feb9ff76Sreyk if (sym != NULL) { 3055feb9ff76Sreyk if (sym->persist == 1) 3056feb9ff76Sreyk return (0); 3057feb9ff76Sreyk else { 3058feb9ff76Sreyk free(sym->nam); 3059feb9ff76Sreyk free(sym->val); 306020741916Sderaadt TAILQ_REMOVE(&symhead, sym, entry); 3061feb9ff76Sreyk free(sym); 3062feb9ff76Sreyk } 3063feb9ff76Sreyk } 3064feb9ff76Sreyk if ((sym = calloc(1, sizeof(*sym))) == NULL) 3065feb9ff76Sreyk return (-1); 3066feb9ff76Sreyk 3067feb9ff76Sreyk sym->nam = strdup(nam); 3068feb9ff76Sreyk if (sym->nam == NULL) { 3069feb9ff76Sreyk free(sym); 3070feb9ff76Sreyk return (-1); 3071feb9ff76Sreyk } 3072feb9ff76Sreyk sym->val = strdup(val); 3073feb9ff76Sreyk if (sym->val == NULL) { 3074feb9ff76Sreyk free(sym->nam); 3075feb9ff76Sreyk free(sym); 3076feb9ff76Sreyk return (-1); 3077feb9ff76Sreyk } 3078feb9ff76Sreyk sym->used = 0; 3079feb9ff76Sreyk sym->persist = persist; 308020741916Sderaadt TAILQ_INSERT_TAIL(&symhead, sym, entry); 3081feb9ff76Sreyk return (0); 3082feb9ff76Sreyk } 3083feb9ff76Sreyk 3084feb9ff76Sreyk int 3085feb9ff76Sreyk cmdline_symset(char *s) 3086feb9ff76Sreyk { 3087feb9ff76Sreyk char *sym, *val; 3088feb9ff76Sreyk int ret; 3089feb9ff76Sreyk 3090feb9ff76Sreyk if ((val = strrchr(s, '=')) == NULL) 3091feb9ff76Sreyk return (-1); 3092ed1b9eb8Smiko sym = strndup(s, val - s); 3093ed1b9eb8Smiko if (sym == NULL) 3094ed1b9eb8Smiko errx(1, "%s: strndup", __func__); 3095feb9ff76Sreyk ret = symset(sym, val + 1, 1); 3096feb9ff76Sreyk free(sym); 3097feb9ff76Sreyk 3098feb9ff76Sreyk return (ret); 3099feb9ff76Sreyk } 3100feb9ff76Sreyk 3101feb9ff76Sreyk char * 3102feb9ff76Sreyk symget(const char *nam) 3103feb9ff76Sreyk { 3104feb9ff76Sreyk struct sym *sym; 3105feb9ff76Sreyk 310654c95b7aSkrw TAILQ_FOREACH(sym, &symhead, entry) { 3107feb9ff76Sreyk if (strcmp(nam, sym->nam) == 0) { 3108feb9ff76Sreyk sym->used = 1; 3109feb9ff76Sreyk return (sym->val); 3110feb9ff76Sreyk } 311154c95b7aSkrw } 3112feb9ff76Sreyk return (NULL); 3113feb9ff76Sreyk } 3114feb9ff76Sreyk 3115feb9ff76Sreyk struct address * 31164687b7c6Sdenis host_ip(const char *s) 3117feb9ff76Sreyk { 3118676eb00eSreyk struct addrinfo hints, *res; 3119676eb00eSreyk struct address *h = NULL; 3120feb9ff76Sreyk 31214687b7c6Sdenis memset(&hints, 0, sizeof(hints)); 31224687b7c6Sdenis hints.ai_family = AF_UNSPEC; 3123676eb00eSreyk hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 3124676eb00eSreyk hints.ai_flags = AI_NUMERICHOST; 3125676eb00eSreyk if (getaddrinfo(s, "0", &hints, &res) == 0) { 31264687b7c6Sdenis if (res->ai_family == AF_INET || 31274687b7c6Sdenis res->ai_family == AF_INET6) { 3128feb9ff76Sreyk if ((h = calloc(1, sizeof(*h))) == NULL) 31294687b7c6Sdenis fatal(NULL); 31304687b7c6Sdenis memcpy(&h->ss, res->ai_addr, res->ai_addrlen); 31314687b7c6Sdenis } 3132676eb00eSreyk freeaddrinfo(res); 3133676eb00eSreyk } 3134feb9ff76Sreyk 3135feb9ff76Sreyk return (h); 3136feb9ff76Sreyk } 3137feb9ff76Sreyk 3138feb9ff76Sreyk int 3139feb9ff76Sreyk host_dns(const char *s, struct addresslist *al, int max, 31404ac0ad23Sreyk struct portrange *port, const char *ifname, int ipproto) 3141feb9ff76Sreyk { 3142feb9ff76Sreyk struct addrinfo hints, *res0, *res; 3143feb9ff76Sreyk int error, cnt = 0; 3144feb9ff76Sreyk struct address *h; 3145feb9ff76Sreyk 3146f576f50fSreyk if ((cnt = host_if(s, al, max, port, ifname, ipproto)) != 0) 3147f576f50fSreyk return (cnt); 3148f576f50fSreyk 3149feb9ff76Sreyk bzero(&hints, sizeof(hints)); 31504687b7c6Sdenis hints.ai_family = AF_UNSPEC; 3151feb9ff76Sreyk hints.ai_socktype = SOCK_DGRAM; /* DUMMY */ 3152ea230c89Sreyk hints.ai_flags = AI_ADDRCONFIG; 3153feb9ff76Sreyk error = getaddrinfo(s, NULL, &hints, &res0); 3154feb9ff76Sreyk if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME) 3155feb9ff76Sreyk return (0); 3156feb9ff76Sreyk if (error) { 315785a8c65fSreyk log_warnx("%s: could not parse \"%s\": %s", __func__, s, 3158feb9ff76Sreyk gai_strerror(error)); 3159feb9ff76Sreyk return (-1); 3160feb9ff76Sreyk } 3161feb9ff76Sreyk 3162feb9ff76Sreyk for (res = res0; res && cnt < max; res = res->ai_next) { 3163feb9ff76Sreyk if (res->ai_family != AF_INET && 3164feb9ff76Sreyk res->ai_family != AF_INET6) 3165feb9ff76Sreyk continue; 3166feb9ff76Sreyk if ((h = calloc(1, sizeof(*h))) == NULL) 31670f12961aSreyk fatal(__func__); 3168feb9ff76Sreyk 3169232f7df1Sreyk if (port != NULL) 3170232f7df1Sreyk bcopy(port, &h->port, sizeof(h->port)); 3171feb9ff76Sreyk if (ifname != NULL) { 3172feb9ff76Sreyk if (strlcpy(h->ifname, ifname, sizeof(h->ifname)) >= 3173feb9ff76Sreyk sizeof(h->ifname)) 317485a8c65fSreyk log_warnx("%s: interface name truncated", 317585a8c65fSreyk __func__); 3176d65de6b8Spyr freeaddrinfo(res0); 3177a2195becSreyk free(h); 3178feb9ff76Sreyk return (-1); 3179feb9ff76Sreyk } 31804ac0ad23Sreyk if (ipproto != -1) 31814ac0ad23Sreyk h->ipproto = ipproto; 31824ac0ad23Sreyk 31834687b7c6Sdenis memcpy(&h->ss, res->ai_addr, res->ai_addrlen); 3184feb9ff76Sreyk 3185feb9ff76Sreyk TAILQ_INSERT_HEAD(al, h, entry); 3186feb9ff76Sreyk cnt++; 3187feb9ff76Sreyk } 3188feb9ff76Sreyk if (cnt == max && res) { 318985a8c65fSreyk log_warnx("%s: %s resolves to more than %d hosts", __func__, 3190feb9ff76Sreyk s, max); 3191feb9ff76Sreyk } 3192feb9ff76Sreyk freeaddrinfo(res0); 3193feb9ff76Sreyk return (cnt); 3194feb9ff76Sreyk } 3195feb9ff76Sreyk 3196feb9ff76Sreyk int 3197f576f50fSreyk host_if(const char *s, struct addresslist *al, int max, 3198f576f50fSreyk struct portrange *port, const char *ifname, int ipproto) 3199f576f50fSreyk { 3200f576f50fSreyk struct ifaddrs *ifap, *p; 3201f576f50fSreyk struct sockaddr_in *sain; 3202f576f50fSreyk struct sockaddr_in6 *sin6; 3203f576f50fSreyk struct address *h; 3204f576f50fSreyk int cnt = 0, af; 3205f576f50fSreyk 3206f576f50fSreyk if (getifaddrs(&ifap) == -1) 3207f576f50fSreyk fatal("getifaddrs"); 3208f576f50fSreyk 3209f576f50fSreyk /* First search for IPv4 addresses */ 3210f576f50fSreyk af = AF_INET; 3211f576f50fSreyk 3212f576f50fSreyk nextaf: 3213f576f50fSreyk for (p = ifap; p != NULL && cnt < max; p = p->ifa_next) { 3214bed9f3d2Sbenno if (p->ifa_addr == NULL || 3215bed9f3d2Sbenno p->ifa_addr->sa_family != af || 321602ced5b1Sreyk (strcmp(s, p->ifa_name) != 0 && 321702ced5b1Sreyk !is_if_in_group(p->ifa_name, s))) 3218f576f50fSreyk continue; 3219f576f50fSreyk if ((h = calloc(1, sizeof(*h))) == NULL) 3220f576f50fSreyk fatal("calloc"); 3221f576f50fSreyk 3222f576f50fSreyk if (port != NULL) 3223f576f50fSreyk bcopy(port, &h->port, sizeof(h->port)); 3224f576f50fSreyk if (ifname != NULL) { 3225f576f50fSreyk if (strlcpy(h->ifname, ifname, sizeof(h->ifname)) >= 3226f576f50fSreyk sizeof(h->ifname)) 322785a8c65fSreyk log_warnx("%s: interface name truncated", 322885a8c65fSreyk __func__); 3229f576f50fSreyk freeifaddrs(ifap); 3230e300b0a4Srob free(h); 3231f576f50fSreyk return (-1); 3232f576f50fSreyk } 3233f576f50fSreyk if (ipproto != -1) 3234f576f50fSreyk h->ipproto = ipproto; 3235f576f50fSreyk h->ss.ss_family = af; 3236f576f50fSreyk 3237f576f50fSreyk if (af == AF_INET) { 3238f576f50fSreyk sain = (struct sockaddr_in *)&h->ss; 3239f576f50fSreyk sain->sin_len = sizeof(struct sockaddr_in); 3240f576f50fSreyk sain->sin_addr.s_addr = ((struct sockaddr_in *) 3241f576f50fSreyk p->ifa_addr)->sin_addr.s_addr; 3242f576f50fSreyk } else { 3243f576f50fSreyk sin6 = (struct sockaddr_in6 *)&h->ss; 3244f576f50fSreyk sin6->sin6_len = sizeof(struct sockaddr_in6); 3245f576f50fSreyk memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *) 3246f576f50fSreyk p->ifa_addr)->sin6_addr, sizeof(struct in6_addr)); 3247f576f50fSreyk sin6->sin6_scope_id = ((struct sockaddr_in6 *) 3248f576f50fSreyk p->ifa_addr)->sin6_scope_id; 3249f576f50fSreyk } 3250f576f50fSreyk 3251f576f50fSreyk TAILQ_INSERT_HEAD(al, h, entry); 3252f576f50fSreyk cnt++; 3253f576f50fSreyk } 3254f576f50fSreyk if (af == AF_INET) { 3255f576f50fSreyk /* Next search for IPv6 addresses */ 3256f576f50fSreyk af = AF_INET6; 3257f576f50fSreyk goto nextaf; 3258f576f50fSreyk } 3259f576f50fSreyk 3260f576f50fSreyk if (cnt > max) { 326185a8c65fSreyk log_warnx("%s: %s resolves to more than %d hosts", __func__, 3262f576f50fSreyk s, max); 3263f576f50fSreyk } 3264f576f50fSreyk freeifaddrs(ifap); 3265f576f50fSreyk return (cnt); 3266f576f50fSreyk } 3267f576f50fSreyk 3268f576f50fSreyk int 3269feb9ff76Sreyk host(const char *s, struct addresslist *al, int max, 32704ac0ad23Sreyk struct portrange *port, const char *ifname, int ipproto) 3271feb9ff76Sreyk { 3272feb9ff76Sreyk struct address *h; 3273feb9ff76Sreyk 32744687b7c6Sdenis if ((h = host_ip(s)) == NULL) 32754687b7c6Sdenis return (host_dns(s, al, max, port, ifname, ipproto)); 3276feb9ff76Sreyk 3277232f7df1Sreyk if (port != NULL) 3278232f7df1Sreyk bcopy(port, &h->port, sizeof(h->port)); 3279feb9ff76Sreyk if (ifname != NULL) { 3280feb9ff76Sreyk if (strlcpy(h->ifname, ifname, sizeof(h->ifname)) >= 3281feb9ff76Sreyk sizeof(h->ifname)) { 328285a8c65fSreyk log_warnx("%s: interface name truncated", 328385a8c65fSreyk __func__); 3284a2195becSreyk free(h); 3285feb9ff76Sreyk return (-1); 3286feb9ff76Sreyk } 3287feb9ff76Sreyk } 32884ac0ad23Sreyk if (ipproto != -1) 32894ac0ad23Sreyk h->ipproto = ipproto; 3290feb9ff76Sreyk 3291feb9ff76Sreyk TAILQ_INSERT_HEAD(al, h, entry); 3292feb9ff76Sreyk return (1); 3293feb9ff76Sreyk } 3294feb9ff76Sreyk 3295a2195becSreyk void 3296a2195becSreyk host_free(struct addresslist *al) 3297a2195becSreyk { 3298a2195becSreyk struct address *h; 3299a2195becSreyk 3300a2195becSreyk while ((h = TAILQ_FIRST(al)) != NULL) { 3301a2195becSreyk TAILQ_REMOVE(al, h, entry); 3302a2195becSreyk free(h); 3303a2195becSreyk } 3304a2195becSreyk } 3305a2195becSreyk 330699ca6689Sreyk struct table * 3307af50a7a9Sreyk table_inherit(struct table *tb) 330899ca6689Sreyk { 330999ca6689Sreyk char pname[TABLE_NAME_SIZE + 6]; 331099ca6689Sreyk struct host *h, *dsth; 3311af50a7a9Sreyk struct table *dsttb, *oldtb; 331299ca6689Sreyk 331399ca6689Sreyk /* Get the table or table template */ 3314af50a7a9Sreyk if ((dsttb = table_findbyname(conf, tb->conf.name)) == NULL) { 3315af50a7a9Sreyk yyerror("unknown table %s", tb->conf.name); 3316a2195becSreyk goto fail; 331799ca6689Sreyk } 331868b79041Spyr if (dsttb->conf.port != 0) 3319af50a7a9Sreyk fatal("invalid table"); /* should not happen */ 332099ca6689Sreyk 3321af50a7a9Sreyk if (tb->conf.port == 0) { 332299ca6689Sreyk yyerror("invalid port"); 3323a2195becSreyk goto fail; 332499ca6689Sreyk } 332599ca6689Sreyk 332699ca6689Sreyk /* Check if a matching table already exists */ 3327af50a7a9Sreyk if (snprintf(pname, sizeof(pname), "%s:%u", 3328af50a7a9Sreyk tb->conf.name, ntohs(tb->conf.port)) >= (int)sizeof(pname)) { 3329af50a7a9Sreyk yyerror("invalid table name"); 3330a2195becSreyk goto fail; 333199ca6689Sreyk } 3332c07ec172Sreyk if (strlcpy(tb->conf.name, pname, sizeof(tb->conf.name)) >= 3333c07ec172Sreyk sizeof(tb->conf.name)) { 3334c07ec172Sreyk yyerror("invalid table mame"); 3335c07ec172Sreyk goto fail; 3336c07ec172Sreyk } 33370db66ba9Sclaudio if ((oldtb = table_findbyconf(conf, tb)) != NULL) { 33382c8c2287Sclaudio purge_table(conf, NULL, tb); 3339af50a7a9Sreyk return (oldtb); 33400db66ba9Sclaudio } 334199ca6689Sreyk 334299ca6689Sreyk /* Create a new table */ 3343a1a58453Sreyk tb->conf.id = ++last_table_id; 334499ca6689Sreyk if (last_table_id == INT_MAX) { 334599ca6689Sreyk yyerror("too many tables defined"); 3346a2195becSreyk goto fail; 334799ca6689Sreyk } 3348af50a7a9Sreyk tb->conf.flags |= dsttb->conf.flags; 334999ca6689Sreyk 3350850c1bdeSreyk /* Inherit global table options */ 33514007026dSbenno if (tb->conf.timeout.tv_sec == 0 && tb->conf.timeout.tv_usec == 0) 33524007026dSbenno bcopy(&dsttb->conf.timeout, &tb->conf.timeout, 33534007026dSbenno sizeof(struct timeval)); 3354850c1bdeSreyk 335599ca6689Sreyk /* Copy the associated hosts */ 33566e16db00Sreyk TAILQ_INIT(&tb->hosts); 335799ca6689Sreyk TAILQ_FOREACH(dsth, &dsttb->hosts, entry) { 335899ca6689Sreyk if ((h = (struct host *) 335999ca6689Sreyk calloc(1, sizeof (*h))) == NULL) 336099ca6689Sreyk fatal("out of memory"); 336199ca6689Sreyk bcopy(dsth, h, sizeof(*h)); 3362a1a58453Sreyk h->conf.id = ++last_host_id; 336399ca6689Sreyk if (last_host_id == INT_MAX) { 336499ca6689Sreyk yyerror("too many hosts defined"); 33652e5853e7Sjsg free(h); 3366a2195becSreyk goto fail; 336799ca6689Sreyk } 336868b79041Spyr h->conf.tableid = tb->conf.id; 336968b79041Spyr h->tablename = tb->conf.name; 33701584e35dSreyk SLIST_INIT(&h->children); 33716e16db00Sreyk TAILQ_INSERT_TAIL(&tb->hosts, h, entry); 33726e07057bSblambert TAILQ_INSERT_TAIL(&conf->sc_hosts, h, globalentry); 337399ca6689Sreyk } 337499ca6689Sreyk 337535d10c30Sreyk conf->sc_tablecount++; 3376041405e3Sreyk TAILQ_INSERT_TAIL(conf->sc_tables, tb, entry); 337799ca6689Sreyk 337899ca6689Sreyk return (tb); 3379a2195becSreyk 3380a2195becSreyk fail: 33812c8c2287Sclaudio purge_table(conf, NULL, tb); 3382a2195becSreyk return (NULL); 338399ca6689Sreyk } 3384232f7df1Sreyk 338500ae3104Sreyk int 338600ae3104Sreyk relay_id(struct relay *rl) 338700ae3104Sreyk { 338800ae3104Sreyk rl->rl_conf.id = ++last_relay_id; 338900ae3104Sreyk 3390114ce177Sclaudio if (last_relay_id == INT_MAX) 339100ae3104Sreyk return (-1); 339200ae3104Sreyk 339300ae3104Sreyk return (0); 339400ae3104Sreyk } 339500ae3104Sreyk 33969f29cb9cSreyk struct relay * 33979f29cb9cSreyk relay_inherit(struct relay *ra, struct relay *rb) 33989f29cb9cSreyk { 33999f29cb9cSreyk struct relay_config rc; 3400416fa9c0Sreyk struct relay_table *rta, *rtb; 34019f29cb9cSreyk 34029f29cb9cSreyk bcopy(&rb->rl_conf, &rc, sizeof(rc)); 34039f29cb9cSreyk bcopy(ra, rb, sizeof(*rb)); 34049f29cb9cSreyk 34059f29cb9cSreyk bcopy(&rc.ss, &rb->rl_conf.ss, sizeof(rb->rl_conf.ss)); 34069f29cb9cSreyk rb->rl_conf.port = rc.port; 34079f29cb9cSreyk rb->rl_conf.flags = 34087bb52228Sreyk (ra->rl_conf.flags & ~F_TLS) | (rc.flags & F_TLS); 34097bb52228Sreyk if (!(rb->rl_conf.flags & F_TLS)) { 3410114ce177Sclaudio rb->rl_tls_cacert_fd = -1; 3411114ce177Sclaudio rb->rl_tls_ca_fd = -1; 3412*92388deeStb rb->rl_tls_client_ca_fd = -1; 3413ae74277dSbenno } 3414416fa9c0Sreyk TAILQ_INIT(&rb->rl_tables); 34159f29cb9cSreyk 341600ae3104Sreyk if (relay_id(rb) == -1) { 34179f29cb9cSreyk yyerror("too many relays defined"); 34189f29cb9cSreyk goto err; 34199f29cb9cSreyk } 34209f29cb9cSreyk 34219f29cb9cSreyk if (snprintf(rb->rl_conf.name, sizeof(rb->rl_conf.name), "%s%u:%u", 34229f29cb9cSreyk ra->rl_conf.name, rb->rl_conf.id, ntohs(rc.port)) >= 34239f29cb9cSreyk (int)sizeof(rb->rl_conf.name)) { 34249f29cb9cSreyk yyerror("invalid relay name"); 34259f29cb9cSreyk goto err; 34269f29cb9cSreyk } 34279f29cb9cSreyk 34288a93e612Sreyk if (relay_findbyname(conf, rb->rl_conf.name) != NULL || 34298a93e612Sreyk relay_findbyaddr(conf, &rb->rl_conf) != NULL) { 3430ee816639Sreyk yyerror("relay %s or listener defined twice", 3431ee816639Sreyk rb->rl_conf.name); 34329f29cb9cSreyk goto err; 34339f29cb9cSreyk } 3434ee816639Sreyk 34353ca2f577Sreyk if (relay_load_certfiles(conf, rb, NULL) == -1) { 34369f29cb9cSreyk yyerror("cannot load certificates for relay %s", 34379f29cb9cSreyk rb->rl_conf.name); 34389f29cb9cSreyk goto err; 34399f29cb9cSreyk } 34409f29cb9cSreyk 3441416fa9c0Sreyk TAILQ_FOREACH(rta, &ra->rl_tables, rlt_entry) { 3442416fa9c0Sreyk if ((rtb = calloc(1, sizeof(*rtb))) == NULL) { 3443416fa9c0Sreyk yyerror("cannot allocate relay table"); 3444416fa9c0Sreyk goto err; 3445416fa9c0Sreyk } 3446416fa9c0Sreyk rtb->rlt_table = rta->rlt_table; 3447416fa9c0Sreyk rtb->rlt_mode = rta->rlt_mode; 3448118081d7Sreyk rtb->rlt_flags = rta->rlt_flags; 3449416fa9c0Sreyk 3450416fa9c0Sreyk TAILQ_INSERT_TAIL(&rb->rl_tables, rtb, rlt_entry); 3451416fa9c0Sreyk } 3452416fa9c0Sreyk 34539f29cb9cSreyk conf->sc_relaycount++; 34549f29cb9cSreyk SPLAY_INIT(&rlay->rl_sessions); 34559f29cb9cSreyk TAILQ_INSERT_TAIL(conf->sc_relays, rb, rl_entry); 34569f29cb9cSreyk 34579f29cb9cSreyk return (rb); 34589f29cb9cSreyk 34599f29cb9cSreyk err: 3460416fa9c0Sreyk while ((rtb = TAILQ_FIRST(&rb->rl_tables))) { 3461416fa9c0Sreyk TAILQ_REMOVE(&rb->rl_tables, rtb, rlt_entry); 3462416fa9c0Sreyk free(rtb); 3463416fa9c0Sreyk } 34649f29cb9cSreyk free(rb); 34659f29cb9cSreyk return (NULL); 34669f29cb9cSreyk } 34679f29cb9cSreyk 3468232f7df1Sreyk int 3469232f7df1Sreyk getservice(char *n) 3470232f7df1Sreyk { 3471232f7df1Sreyk struct servent *s; 3472232f7df1Sreyk const char *errstr; 3473232f7df1Sreyk long long llval; 3474232f7df1Sreyk 3475232f7df1Sreyk llval = strtonum(n, 0, UINT16_MAX, &errstr); 3476232f7df1Sreyk if (errstr) { 3477232f7df1Sreyk s = getservbyname(n, "tcp"); 3478232f7df1Sreyk if (s == NULL) 3479232f7df1Sreyk s = getservbyname(n, "udp"); 3480232f7df1Sreyk if (s == NULL) { 3481232f7df1Sreyk yyerror("unknown port %s", n); 3482232f7df1Sreyk return (-1); 3483232f7df1Sreyk } 3484232f7df1Sreyk return (s->s_port); 3485232f7df1Sreyk } 3486232f7df1Sreyk 3487232f7df1Sreyk return (htons((u_short)llval)); 3488232f7df1Sreyk } 348902ced5b1Sreyk 349002ced5b1Sreyk int 349102ced5b1Sreyk is_if_in_group(const char *ifname, const char *groupname) 349202ced5b1Sreyk { 349302ced5b1Sreyk unsigned int len; 349402ced5b1Sreyk struct ifgroupreq ifgr; 349502ced5b1Sreyk struct ifg_req *ifg; 349602ced5b1Sreyk int s; 349702ced5b1Sreyk int ret = 0; 349802ced5b1Sreyk 3499df69c215Sderaadt if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 350002ced5b1Sreyk err(1, "socket"); 350102ced5b1Sreyk 350202ced5b1Sreyk memset(&ifgr, 0, sizeof(ifgr)); 3503c07ec172Sreyk if (strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ) >= IFNAMSIZ) 3504c07ec172Sreyk err(1, "IFNAMSIZ"); 350502ced5b1Sreyk if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) { 350602ced5b1Sreyk if (errno == EINVAL || errno == ENOTTY) 350702ced5b1Sreyk goto end; 350802ced5b1Sreyk err(1, "SIOCGIFGROUP"); 350902ced5b1Sreyk } 351002ced5b1Sreyk 351102ced5b1Sreyk len = ifgr.ifgr_len; 351235de856eSderaadt ifgr.ifgr_groups = calloc(len / sizeof(struct ifg_req), 351302ced5b1Sreyk sizeof(struct ifg_req)); 351402ced5b1Sreyk if (ifgr.ifgr_groups == NULL) 351502ced5b1Sreyk err(1, "getifgroups"); 351602ced5b1Sreyk if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) 351702ced5b1Sreyk err(1, "SIOCGIFGROUP"); 351802ced5b1Sreyk 351902ced5b1Sreyk ifg = ifgr.ifgr_groups; 352002ced5b1Sreyk for (; ifg && len >= sizeof(struct ifg_req); ifg++) { 352102ced5b1Sreyk len -= sizeof(struct ifg_req); 352202ced5b1Sreyk if (strcmp(ifg->ifgrq_group, groupname) == 0) { 352302ced5b1Sreyk ret = 1; 352402ced5b1Sreyk break; 352502ced5b1Sreyk } 352602ced5b1Sreyk } 352702ced5b1Sreyk free(ifgr.ifgr_groups); 352802ced5b1Sreyk 352902ced5b1Sreyk end: 353002ced5b1Sreyk close(s); 353102ced5b1Sreyk return (ret); 353202ced5b1Sreyk } 3533