xref: /openbsd-src/usr.sbin/relayd/parse.y (revision 92388deed9318960f925fb877fdd678869b3dcd1)
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