xref: /openbsd-src/usr.sbin/relayd/relay.c (revision 92388deed9318960f925fb877fdd678869b3dcd1)
1*92388deeStb /*	$OpenBSD: relay.c,v 1.260 2024/10/28 19:56:18 tb Exp $	*/
22edd718bSreyk 
32edd718bSreyk /*
43d77879fSreyk  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
52edd718bSreyk  *
62edd718bSreyk  * Permission to use, copy, modify, and distribute this software for any
72edd718bSreyk  * purpose with or without fee is hereby granted, provided that the above
82edd718bSreyk  * copyright notice and this permission notice appear in all copies.
92edd718bSreyk  *
102edd718bSreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
112edd718bSreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
122edd718bSreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
132edd718bSreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
142edd718bSreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
152edd718bSreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
162edd718bSreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
172edd718bSreyk  */
182edd718bSreyk 
192edd718bSreyk #include <sys/types.h>
200ca734d7Sreyk #include <sys/queue.h>
212edd718bSreyk #include <sys/time.h>
222edd718bSreyk #include <sys/socket.h>
232edd718bSreyk #include <sys/tree.h>
242edd718bSreyk 
252edd718bSreyk #include <netinet/in.h>
262edd718bSreyk #include <netinet/tcp.h>
27f04ff968Sreyk #include <arpa/inet.h>
282edd718bSreyk 
29f04ff968Sreyk #include <limits.h>
30860302f3Sreyk #include <netdb.h>
319c908525Sclaudio #include <poll.h>
32f04ff968Sreyk #include <stdio.h>
33f04ff968Sreyk #include <stdlib.h>
342edd718bSreyk #include <errno.h>
352edd718bSreyk #include <fcntl.h>
362edd718bSreyk #include <string.h>
372edd718bSreyk #include <unistd.h>
382edd718bSreyk #include <event.h>
39f04ff968Sreyk #include <siphash.h>
40f04ff968Sreyk #include <imsg.h>
412edd718bSreyk 
4285e5f500Sclaudio #include <tls.h>
432edd718bSreyk 
44748ceb64Sreyk #include "relayd.h"
452edd718bSreyk 
46e2318a52Sderaadt #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
47e2318a52Sderaadt 
482edd718bSreyk void		 relay_statistics(int, short, void *);
490325c666Sreyk int		 relay_dispatch_parent(int, struct privsep_proc *,
500325c666Sreyk 		    struct imsg *);
510325c666Sreyk int		 relay_dispatch_pfe(int, struct privsep_proc *,
520325c666Sreyk 		    struct imsg *);
533d77879fSreyk int		 relay_dispatch_ca(int, struct privsep_proc *,
543d77879fSreyk 		    struct imsg *);
556e07057bSblambert int		 relay_dispatch_hce(int, struct privsep_proc *,
566e07057bSblambert 		    struct imsg *);
572edd718bSreyk void		 relay_shutdown(void);
582edd718bSreyk 
592edd718bSreyk void		 relay_protodebug(struct relay *);
60cb8b0e56Sreyk void		 relay_ruledebug(struct relay_rule *);
610325c666Sreyk void		 relay_init(struct privsep *, struct privsep_proc *p, void *);
622edd718bSreyk void		 relay_launch(void);
632edd718bSreyk int		 relay_socket(struct sockaddr_storage *, in_port_t,
645db041b0Sdhill 		    struct protocol *, int, int);
652edd718bSreyk int		 relay_socket_listen(struct sockaddr_storage *, in_port_t,
662edd718bSreyk 		    struct protocol *);
672edd718bSreyk int		 relay_socket_connect(struct sockaddr_storage *, in_port_t,
683c03a838Sreyk 		    struct protocol *, int);
692edd718bSreyk 
702edd718bSreyk void		 relay_accept(int, short, void *);
71f4a6e73bSreyk void		 relay_input(struct rsession *);
722edd718bSreyk 
73acb89df4Sreyk void		 relay_hash_addr(SIPHASH_CTX *, struct sockaddr_storage *, int);
742edd718bSreyk 
7585e5f500Sclaudio int		 relay_tls_ctx_create(struct relay *);
767bb52228Sreyk void		 relay_tls_transaction(struct rsession *,
7733e8bb87Sreyk 		    struct ctl_relay_event *);
7885e5f500Sclaudio void		 relay_tls_handshake(int, short, void *);
797bb52228Sreyk void		 relay_tls_connected(struct ctl_relay_event *);
807bb52228Sreyk void		 relay_tls_readcb(int, short, void *);
817bb52228Sreyk void		 relay_tls_writecb(int, short, void *);
822edd718bSreyk 
8365f47834Sreyk void		 relay_connect_retry(int, short, void *);
8465f47834Sreyk void		 relay_connect_state(struct rsession *,
8565f47834Sreyk 		    struct ctl_relay_event *, enum relay_state);
8665f47834Sreyk 
872edd718bSreyk extern void	 bufferevent_read_pressure_cb(struct evbuffer *, size_t,
882edd718bSreyk 		    size_t, void *);
892edd718bSreyk 
902f89c21dSbenno volatile int relay_sessions;
912f89c21dSbenno volatile int relay_inflight = 0;
922edd718bSreyk objid_t relay_conid;
932edd718bSreyk 
94748ceb64Sreyk static struct relayd		*env = NULL;
952edd718bSreyk 
960325c666Sreyk static struct privsep_proc procs[] = {
970325c666Sreyk 	{ "parent",	PROC_PARENT,	relay_dispatch_parent },
980325c666Sreyk 	{ "pfe",	PROC_PFE,	relay_dispatch_pfe },
996e07057bSblambert 	{ "ca",		PROC_CA,	relay_dispatch_ca },
1006e07057bSblambert 	{ "hce",	PROC_HCE,	relay_dispatch_hce },
1010325c666Sreyk };
1022edd718bSreyk 
103f910ac11Sreyk void
1040325c666Sreyk relay(struct privsep *ps, struct privsep_proc *p)
1052edd718bSreyk {
1060325c666Sreyk 	env = ps->ps_env;
107f910ac11Sreyk 	proc_run(ps, p, procs, nitems(procs), relay_init, NULL);
108cb8b0e56Sreyk 	relay_http(env);
1092edd718bSreyk }
1102edd718bSreyk 
1112edd718bSreyk void
1122edd718bSreyk relay_shutdown(void)
1132edd718bSreyk {
114a2195becSreyk 	config_purge(env, CONFIG_ALL);
1152edd718bSreyk 	usleep(200);	/* XXX relay needs to shutdown last */
1162edd718bSreyk }
1172edd718bSreyk 
1182edd718bSreyk void
119cb8b0e56Sreyk relay_ruledebug(struct relay_rule *rule)
1202edd718bSreyk {
121cb8b0e56Sreyk 	struct kv	*kv = NULL;
122cb8b0e56Sreyk 	u_int		 i;
123860302f3Sreyk 	char		 buf[NI_MAXHOST];
124d2491b41Sreyk 
1252edd718bSreyk 	fprintf(stderr, "\t\t");
12632678a96Sreyk 
127cb8b0e56Sreyk 	switch (rule->rule_action) {
128cb8b0e56Sreyk 	case RULE_ACTION_MATCH:
129cb8b0e56Sreyk 		fprintf(stderr, "match ");
130dae1a80bSreyk 		break;
131cb8b0e56Sreyk 	case RULE_ACTION_BLOCK:
132cb8b0e56Sreyk 		fprintf(stderr, "block ");
133dae1a80bSreyk 		break;
134cb8b0e56Sreyk 	case RULE_ACTION_PASS:
135cb8b0e56Sreyk 		fprintf(stderr, "pass ");
136cb8b0e56Sreyk 		break;
137cb8b0e56Sreyk 	}
138cb8b0e56Sreyk 
139cb8b0e56Sreyk 	switch (rule->rule_dir) {
140cb8b0e56Sreyk 	case RELAY_DIR_ANY:
141cb8b0e56Sreyk 		break;
142cb8b0e56Sreyk 	case RELAY_DIR_REQUEST:
143cb8b0e56Sreyk 		fprintf(stderr, "request ");
144cb8b0e56Sreyk 		break;
145cb8b0e56Sreyk 	case RELAY_DIR_RESPONSE:
146cb8b0e56Sreyk 		fprintf(stderr, "response ");
147cb8b0e56Sreyk 		break;
148cb8b0e56Sreyk 	default:
149cb8b0e56Sreyk 		return;
150cb8b0e56Sreyk 		/* NOTREACHED */
151cb8b0e56Sreyk 		break;
152cb8b0e56Sreyk 	}
153cb8b0e56Sreyk 
154cb8b0e56Sreyk 	if (rule->rule_flags & RULE_FLAG_QUICK)
155cb8b0e56Sreyk 		fprintf(stderr, "quick ");
156cb8b0e56Sreyk 
157860302f3Sreyk 	switch (rule->rule_af) {
158860302f3Sreyk 	case AF_INET:
159860302f3Sreyk 		fprintf(stderr, "inet ");
160860302f3Sreyk 		break;
161860302f3Sreyk 	case AF_INET6:
162860302f3Sreyk 		fprintf(stderr, "inet6 ");
163860302f3Sreyk 		break;
164860302f3Sreyk 	}
165860302f3Sreyk 
166860302f3Sreyk 	if (rule->rule_src.addr.ss_family != AF_UNSPEC)
167860302f3Sreyk 		fprintf(stderr, "from %s/%d ",
168860302f3Sreyk 		    print_host(&rule->rule_src.addr, buf, sizeof(buf)),
169860302f3Sreyk 		    rule->rule_src.addr_mask);
170860302f3Sreyk 
171860302f3Sreyk 	if (rule->rule_dst.addr.ss_family != AF_UNSPEC)
172860302f3Sreyk 		fprintf(stderr, "to %s/%d ",
173860302f3Sreyk 		    print_host(&rule->rule_dst.addr, buf, sizeof(buf)),
174860302f3Sreyk 		    rule->rule_dst.addr_mask);
175860302f3Sreyk 
176cb8b0e56Sreyk 	for (i = 1; i < KEY_TYPE_MAX; i++) {
177cb8b0e56Sreyk 		kv = &rule->rule_kv[i];
178cb8b0e56Sreyk 		if (kv->kv_type != i)
179cb8b0e56Sreyk 			continue;
180cb8b0e56Sreyk 
181cb8b0e56Sreyk 		switch (kv->kv_type) {
182cb8b0e56Sreyk 		case KEY_TYPE_COOKIE:
183dae1a80bSreyk 			fprintf(stderr, "cookie ");
184dae1a80bSreyk 			break;
185cb8b0e56Sreyk 		case KEY_TYPE_HEADER:
186cb8b0e56Sreyk 			fprintf(stderr, "header ");
187cb8b0e56Sreyk 			break;
188cb8b0e56Sreyk 		case KEY_TYPE_PATH:
18925963c4bSreyk 			fprintf(stderr, "path ");
19025963c4bSreyk 			break;
191cb8b0e56Sreyk 		case KEY_TYPE_QUERY:
192cb8b0e56Sreyk 			fprintf(stderr, "query ");
193cb8b0e56Sreyk 			break;
194cb8b0e56Sreyk 		case KEY_TYPE_URL:
195a088a0c2Sreyk 			fprintf(stderr, "url ");
196a088a0c2Sreyk 			break;
197cb8b0e56Sreyk 		default:
198cb8b0e56Sreyk 			continue;
199cb8b0e56Sreyk 		}
200cb8b0e56Sreyk 
201cb8b0e56Sreyk 		switch (kv->kv_option) {
202cb8b0e56Sreyk 		case KEY_OPTION_APPEND:
203cb8b0e56Sreyk 			fprintf(stderr, "append ");
204cb8b0e56Sreyk 			break;
205cb8b0e56Sreyk 		case KEY_OPTION_SET:
206cb8b0e56Sreyk 			fprintf(stderr, "set ");
207cb8b0e56Sreyk 			break;
208cb8b0e56Sreyk 		case KEY_OPTION_REMOVE:
209cb8b0e56Sreyk 			fprintf(stderr, "remove ");
210cb8b0e56Sreyk 			break;
211cb8b0e56Sreyk 		case KEY_OPTION_HASH:
212cb8b0e56Sreyk 			fprintf(stderr, "hash ");
213cb8b0e56Sreyk 			break;
214cb8b0e56Sreyk 		case KEY_OPTION_LOG:
215cb8b0e56Sreyk 			fprintf(stderr, "log ");
216cb8b0e56Sreyk 			break;
217eeb1fea4Sdenis 		case KEY_OPTION_STRIP:
218eeb1fea4Sdenis 			fprintf(stderr, "strip ");
219eeb1fea4Sdenis 			break;
220cb8b0e56Sreyk 		case KEY_OPTION_NONE:
221cb8b0e56Sreyk 			break;
222dae1a80bSreyk 		}
223dae1a80bSreyk 
224cb8b0e56Sreyk 		switch (kv->kv_digest) {
225cb8b0e56Sreyk 		case DIGEST_SHA1:
226cb8b0e56Sreyk 		case DIGEST_MD5:
227cb8b0e56Sreyk 			fprintf(stderr, "digest ");
2282edd718bSreyk 			break;
229cb8b0e56Sreyk 		default:
2302edd718bSreyk 			break;
2312edd718bSreyk 		}
232cb8b0e56Sreyk 
233eeb1fea4Sdenis 		int kvv = (kv->kv_option == KEY_OPTION_STRIP ||
234eeb1fea4Sdenis 		     kv->kv_value == NULL);
235cb8b0e56Sreyk 		fprintf(stderr, "%s%s%s%s%s%s ",
236cb8b0e56Sreyk 		    kv->kv_key == NULL ? "" : "\"",
237cb8b0e56Sreyk 		    kv->kv_key == NULL ? "" : kv->kv_key,
238cb8b0e56Sreyk 		    kv->kv_key == NULL ? "" : "\"",
239eeb1fea4Sdenis 		    kvv ? "" : " value \"",
240cb8b0e56Sreyk 		    kv->kv_value == NULL ? "" : kv->kv_value,
241eeb1fea4Sdenis 		    kvv ? "" : "\"");
242cb8b0e56Sreyk 	}
243cb8b0e56Sreyk 
244cb8b0e56Sreyk 	if (rule->rule_tablename[0])
245cb8b0e56Sreyk 		fprintf(stderr, "forward to <%s> ", rule->rule_tablename);
246cb8b0e56Sreyk 
247cb8b0e56Sreyk 	if (rule->rule_tag == -1)
248cb8b0e56Sreyk 		fprintf(stderr, "no tag ");
249cb8b0e56Sreyk 	else if (rule->rule_tag && rule->rule_tagname[0])
250cb8b0e56Sreyk 		fprintf(stderr, "tag \"%s\" ",
251cb8b0e56Sreyk 		    rule->rule_tagname);
252cb8b0e56Sreyk 
253cb8b0e56Sreyk 	if (rule->rule_tagged && rule->rule_taggedname[0])
254cb8b0e56Sreyk 		fprintf(stderr, "tagged \"%s\" ",
255cb8b0e56Sreyk 		    rule->rule_taggedname);
256cb8b0e56Sreyk 
257cb8b0e56Sreyk 	if (rule->rule_label == -1)
258cb8b0e56Sreyk 		fprintf(stderr, "no label ");
259cb8b0e56Sreyk 	else if (rule->rule_label && rule->rule_labelname[0])
260cb8b0e56Sreyk 		fprintf(stderr, "label \"%s\" ",
261cb8b0e56Sreyk 		    rule->rule_labelname);
262cb8b0e56Sreyk 
2632edd718bSreyk 	fprintf(stderr, "\n");
2642edd718bSreyk }
2650d428c40Sreyk 
2660d428c40Sreyk void
2670d428c40Sreyk relay_protodebug(struct relay *rlay)
2680d428c40Sreyk {
2694a5b9b3eSreyk 	struct protocol		*proto = rlay->rl_proto;
270cb8b0e56Sreyk 	struct relay_rule	*rule = NULL;
2710d428c40Sreyk 
272a2195becSreyk 	fprintf(stderr, "protocol %d: name %s\n",
273a2195becSreyk 	    proto->id, proto->name);
274471dd29dSreyk 	fprintf(stderr, "\tflags: %s, relay flags: %s\n",
275471dd29dSreyk 	    printb_flags(proto->flags, F_BITS),
276471dd29dSreyk 	    printb_flags(rlay->rl_conf.flags, F_BITS));
277471dd29dSreyk 	if (proto->tcpflags)
278471dd29dSreyk 		fprintf(stderr, "\ttcp flags: %s\n",
279471dd29dSreyk 		    printb_flags(proto->tcpflags, TCPFLAG_BITS));
2807bb52228Sreyk 	if ((rlay->rl_conf.flags & (F_TLS|F_TLSCLIENT)) && proto->tlsflags)
2817bb52228Sreyk 		fprintf(stderr, "\ttls flags: %s\n",
2827bb52228Sreyk 		    printb_flags(proto->tlsflags, TLSFLAG_BITS));
2839c908525Sclaudio 	fprintf(stderr, "\ttls session tickets: %s\n",
28485e5f500Sclaudio 	    (proto->tickets == 1) ? "enabled" : "disabled");
2850d428c40Sreyk 	fprintf(stderr, "\ttype: ");
2860d428c40Sreyk 	switch (proto->type) {
2870d428c40Sreyk 	case RELAY_PROTO_TCP:
2880d428c40Sreyk 		fprintf(stderr, "tcp\n");
2890d428c40Sreyk 		break;
2900d428c40Sreyk 	case RELAY_PROTO_HTTP:
2910d428c40Sreyk 		fprintf(stderr, "http\n");
2920d428c40Sreyk 		break;
2930d428c40Sreyk 	case RELAY_PROTO_DNS:
2940d428c40Sreyk 		fprintf(stderr, "dns\n");
2950d428c40Sreyk 		break;
2960d428c40Sreyk 	}
2970d428c40Sreyk 
298cb8b0e56Sreyk 	rule = TAILQ_FIRST(&proto->rules);
299cb8b0e56Sreyk 	while (rule != NULL) {
300cb8b0e56Sreyk 		relay_ruledebug(rule);
301cb8b0e56Sreyk 		rule = TAILQ_NEXT(rule, rule_entry);
30232678a96Sreyk 	}
3032edd718bSreyk }
3042edd718bSreyk 
305a2195becSreyk int
306a2195becSreyk relay_privinit(struct relay *rlay)
3072edd718bSreyk {
30885a8c65fSreyk 	log_debug("%s: adding relay %s", __func__, rlay->rl_conf.name);
3092edd718bSreyk 
310871fc12cSreyk 	if (log_getverbose() > 1)
3112edd718bSreyk 		relay_protodebug(rlay);
3122edd718bSreyk 
3134a5b9b3eSreyk 	switch (rlay->rl_proto->type) {
3142380f4f2Sreyk 	case RELAY_PROTO_DNS:
315f164d32fSbenno 		relay_udp_privinit(rlay);
3162380f4f2Sreyk 		break;
3172380f4f2Sreyk 	case RELAY_PROTO_TCP:
318cb8b0e56Sreyk 		break;
3192380f4f2Sreyk 	case RELAY_PROTO_HTTP:
3202380f4f2Sreyk 		break;
3212380f4f2Sreyk 	}
3222380f4f2Sreyk 
3234a5b9b3eSreyk 	if (rlay->rl_conf.flags & F_UDP)
3244a5b9b3eSreyk 		rlay->rl_s = relay_udp_bind(&rlay->rl_conf.ss,
3254a5b9b3eSreyk 		    rlay->rl_conf.port, rlay->rl_proto);
3262380f4f2Sreyk 	else
3274a5b9b3eSreyk 		rlay->rl_s = relay_socket_listen(&rlay->rl_conf.ss,
3284a5b9b3eSreyk 		    rlay->rl_conf.port, rlay->rl_proto);
3294a5b9b3eSreyk 	if (rlay->rl_s == -1)
330a2195becSreyk 		return (-1);
331a2195becSreyk 
332a2195becSreyk 	return (0);
3332edd718bSreyk }
3342edd718bSreyk 
3352edd718bSreyk void
3360325c666Sreyk relay_init(struct privsep *ps, struct privsep_proc *p, void *arg)
3372edd718bSreyk {
3382edd718bSreyk 	struct timeval	 tv;
339931050deSreyk 
340a2195becSreyk 	if (config_init(ps->ps_env) == -1)
341a2195becSreyk 		fatal("failed to initialize configuration");
342a2195becSreyk 
3430325c666Sreyk 	/* We use a custom shutdown callback */
3440325c666Sreyk 	p->p_shutdown = relay_shutdown;
3450325c666Sreyk 
346bb8fa3feSreyk 	/* Unlimited file descriptors (use system limits) */
347bb8fa3feSreyk 	socket_rlimit(-1);
3482edd718bSreyk 
34943d3c4caSbenno 	if (pledge("stdio recvfd inet", NULL) == -1)
35043d3c4caSbenno 		fatal("pledge");
35143d3c4caSbenno 
3522edd718bSreyk 	/* Schedule statistics timer */
353325f6e14Sreyk 	evtimer_set(&env->sc_statev, relay_statistics, ps);
354586b5f8aSreyk 	bcopy(&env->sc_conf.statinterval, &tv, sizeof(tv));
35535d10c30Sreyk 	evtimer_add(&env->sc_statev, &tv);
3562edd718bSreyk }
3572edd718bSreyk 
3582edd718bSreyk void
3596e07057bSblambert relay_session_publish(struct rsession *s)
3606e07057bSblambert {
361c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_PFE, IMSG_SESS_PUBLISH, s, sizeof(*s));
3626e07057bSblambert }
3636e07057bSblambert 
3646e07057bSblambert void
3656e07057bSblambert relay_session_unpublish(struct rsession *s)
3666e07057bSblambert {
367c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_PFE, IMSG_SESS_UNPUBLISH,
3686e07057bSblambert 	    &s->se_id, sizeof(s->se_id));
3696e07057bSblambert }
3706e07057bSblambert 
3716e07057bSblambert void
3722edd718bSreyk relay_statistics(int fd, short events, void *arg)
3732edd718bSreyk {
374325f6e14Sreyk 	struct privsep		*ps = arg;
3752edd718bSreyk 	struct relay		*rlay;
3762edd718bSreyk 	struct ctl_stats	 crs, *cur;
3772edd718bSreyk 	struct timeval		 tv, tv_now;
3782edd718bSreyk 	int			 resethour = 0, resetday = 0;
379f4a6e73bSreyk 	struct rsession		*con, *next_con;
3802edd718bSreyk 
3812edd718bSreyk 	/*
3822edd718bSreyk 	 * This is a hack to calculate some average statistics.
3832edd718bSreyk 	 * It doesn't try to be very accurate, but could be improved...
3842edd718bSreyk 	 */
3852edd718bSreyk 
3862edd718bSreyk 	timerclear(&tv);
387fd1841a3Sreyk 	getmonotime(&tv_now);
3882edd718bSreyk 
3894a5b9b3eSreyk 	TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
3902edd718bSreyk 		bzero(&crs, sizeof(crs));
3912edd718bSreyk 		resethour = resetday = 0;
3922edd718bSreyk 
393325f6e14Sreyk 		cur = &rlay->rl_stats[ps->ps_instance];
3942edd718bSreyk 		cur->cnt += cur->last;
3952edd718bSreyk 		cur->tick++;
3962edd718bSreyk 		cur->avg = (cur->last + cur->avg) / 2;
3972edd718bSreyk 		cur->last_hour += cur->last;
398586b5f8aSreyk 		if ((cur->tick %
399586b5f8aSreyk 		    (3600 / env->sc_conf.statinterval.tv_sec)) == 0) {
4002edd718bSreyk 			cur->avg_hour = (cur->last_hour + cur->avg_hour) / 2;
4012edd718bSreyk 			resethour++;
4022edd718bSreyk 		}
4032edd718bSreyk 		cur->last_day += cur->last;
404586b5f8aSreyk 		if ((cur->tick %
405586b5f8aSreyk 		    (86400 / env->sc_conf.statinterval.tv_sec)) == 0) {
4062edd718bSreyk 			cur->avg_day = (cur->last_day + cur->avg_day) / 2;
4072edd718bSreyk 			resethour++;
4082edd718bSreyk 		}
4092edd718bSreyk 		bcopy(cur, &crs, sizeof(crs));
4102edd718bSreyk 
4112edd718bSreyk 		cur->last = 0;
4122edd718bSreyk 		if (resethour)
4132edd718bSreyk 			cur->last_hour = 0;
4142edd718bSreyk 		if (resetday)
4152edd718bSreyk 			cur->last_day = 0;
4162edd718bSreyk 
4174a5b9b3eSreyk 		crs.id = rlay->rl_conf.id;
418325f6e14Sreyk 		crs.proc = ps->ps_instance;
419c2c37c5dSreyk 		proc_compose(env->sc_ps, PROC_PFE, IMSG_STATISTICS,
4202edd718bSreyk 		    &crs, sizeof(crs));
4212edd718bSreyk 
4224a5b9b3eSreyk 		for (con = SPLAY_ROOT(&rlay->rl_sessions);
4232edd718bSreyk 		    con != NULL; con = next_con) {
4246b52fad8Sreyk 			next_con = SPLAY_NEXT(session_tree,
4254a5b9b3eSreyk 			    &rlay->rl_sessions, con);
426f8eb77d7Sthib 			timersub(&tv_now, &con->se_tv_last, &tv);
4274a5b9b3eSreyk 			if (timercmp(&tv, &rlay->rl_conf.timeout, >=))
4280be9d00aSbenno 				relay_close(con, "hard timeout", 1);
4292edd718bSreyk 		}
4302edd718bSreyk 	}
4312edd718bSreyk 
4322edd718bSreyk 	/* Schedule statistics timer */
433325f6e14Sreyk 	evtimer_set(&env->sc_statev, relay_statistics, ps);
434586b5f8aSreyk 	bcopy(&env->sc_conf.statinterval, &tv, sizeof(tv));
43535d10c30Sreyk 	evtimer_add(&env->sc_statev, &tv);
4362edd718bSreyk }
4372edd718bSreyk 
4382edd718bSreyk void
4392edd718bSreyk relay_launch(void)
4402edd718bSreyk {
4412380f4f2Sreyk 	void			(*callback)(int, short, void *);
442a2195becSreyk 	struct relay		*rlay;
443a2195becSreyk 	struct host		*host;
444416fa9c0Sreyk 	struct relay_table	*rlt;
4452edd718bSreyk 
4464a5b9b3eSreyk 	TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
4477bb52228Sreyk 		if ((rlay->rl_conf.flags & (F_TLS|F_TLSCLIENT)) &&
44885e5f500Sclaudio 		    relay_tls_ctx_create(rlay) == -1)
449efc39811Sbenno 			fatalx("%s: failed to create TLS context", __func__);
450a2195becSreyk 
451416fa9c0Sreyk 		TAILQ_FOREACH(rlt, &rlay->rl_tables, rlt_entry) {
452cb8b0e56Sreyk 			/*
453cb8b0e56Sreyk 			 * set rule->rule_table in advance and save time
454cb8b0e56Sreyk 			 * looking up for this later on rule/connection
455cb8b0e56Sreyk 			 * evalution
456cb8b0e56Sreyk 			 */
457cb8b0e56Sreyk 			rule_settable(&rlay->rl_proto->rules, rlt);
458cb8b0e56Sreyk 
459acb89df4Sreyk 			rlt->rlt_index = 0;
460416fa9c0Sreyk 			rlt->rlt_nhosts = 0;
461416fa9c0Sreyk 			TAILQ_FOREACH(host, &rlt->rlt_table->hosts, entry) {
462416fa9c0Sreyk 				if (rlt->rlt_nhosts >= RELAY_MAXHOSTS)
463efc39811Sbenno 					fatal("%s: too many hosts in table",
464efc39811Sbenno 					    __func__);
465416fa9c0Sreyk 				host->idx = rlt->rlt_nhosts;
466416fa9c0Sreyk 				rlt->rlt_host[rlt->rlt_nhosts++] = host;
467a2195becSreyk 			}
468a2195becSreyk 			log_info("adding %d hosts from table %s%s",
469416fa9c0Sreyk 			    rlt->rlt_nhosts, rlt->rlt_table->conf.name,
470416fa9c0Sreyk 			    rlt->rlt_table->conf.check ? "" : " (no check)");
471a2195becSreyk 		}
472a2195becSreyk 
473a2195becSreyk 		switch (rlay->rl_proto->type) {
474a2195becSreyk 		case RELAY_PROTO_DNS:
475f164d32fSbenno 			relay_udp_init(env, rlay);
476a2195becSreyk 			break;
477a2195becSreyk 		case RELAY_PROTO_TCP:
478a2195becSreyk 		case RELAY_PROTO_HTTP:
479cb8b0e56Sreyk 			relay_http_init(rlay);
480a2195becSreyk 			/* Use defaults */
481a2195becSreyk 			break;
482a2195becSreyk 		}
483a2195becSreyk 
48485a8c65fSreyk 		log_debug("%s: running relay %s", __func__,
48585a8c65fSreyk 		    rlay->rl_conf.name);
4862edd718bSreyk 
4874a5b9b3eSreyk 		rlay->rl_up = HOST_UP;
4882edd718bSreyk 
4894a5b9b3eSreyk 		if (rlay->rl_conf.flags & F_UDP)
4902380f4f2Sreyk 			callback = relay_udp_server;
4912380f4f2Sreyk 		else
4922380f4f2Sreyk 			callback = relay_accept;
4932380f4f2Sreyk 
4946b61fee8Sderaadt 		event_set(&rlay->rl_ev, rlay->rl_s, EV_READ,
4952380f4f2Sreyk 		    callback, rlay);
4964a5b9b3eSreyk 		event_add(&rlay->rl_ev, NULL);
4976b61fee8Sderaadt 		evtimer_set(&rlay->rl_evt, callback, rlay);
4982edd718bSreyk 	}
4992edd718bSreyk }
5002edd718bSreyk 
5012edd718bSreyk int
5022380f4f2Sreyk relay_socket_af(struct sockaddr_storage *ss, in_port_t port)
5032edd718bSreyk {
5042edd718bSreyk 	switch (ss->ss_family) {
5052edd718bSreyk 	case AF_INET:
5062edd718bSreyk 		((struct sockaddr_in *)ss)->sin_port = port;
5072edd718bSreyk 		((struct sockaddr_in *)ss)->sin_len =
5082edd718bSreyk 		    sizeof(struct sockaddr_in);
5092edd718bSreyk 		break;
5102edd718bSreyk 	case AF_INET6:
5112edd718bSreyk 		((struct sockaddr_in6 *)ss)->sin6_port = port;
5122edd718bSreyk 		((struct sockaddr_in6 *)ss)->sin6_len =
5132edd718bSreyk 		    sizeof(struct sockaddr_in6);
5142edd718bSreyk 		break;
5152380f4f2Sreyk 	default:
5162380f4f2Sreyk 		return (-1);
5172edd718bSreyk 	}
5182edd718bSreyk 
5192380f4f2Sreyk 	return (0);
5202380f4f2Sreyk }
5212380f4f2Sreyk 
522523113bfSreyk in_port_t
523523113bfSreyk relay_socket_getport(struct sockaddr_storage *ss)
524523113bfSreyk {
525523113bfSreyk 	switch (ss->ss_family) {
526523113bfSreyk 	case AF_INET:
527523113bfSreyk 		return (((struct sockaddr_in *)ss)->sin_port);
528523113bfSreyk 	case AF_INET6:
529523113bfSreyk 		return (((struct sockaddr_in6 *)ss)->sin6_port);
530523113bfSreyk 	default:
531523113bfSreyk 		return (0);
532523113bfSreyk 	}
533523113bfSreyk 
534523113bfSreyk 	/* NOTREACHED */
535523113bfSreyk 	return (0);
536523113bfSreyk }
537523113bfSreyk 
5382380f4f2Sreyk int
5392380f4f2Sreyk relay_socket(struct sockaddr_storage *ss, in_port_t port,
5405db041b0Sdhill     struct protocol *proto, int fd, int reuseport)
5412380f4f2Sreyk {
5422380f4f2Sreyk 	struct linger	lng;
5434bfdeee0Sandre 	int		s = -1, val;
5442380f4f2Sreyk 
5452380f4f2Sreyk 	if (relay_socket_af(ss, port) == -1)
5462380f4f2Sreyk 		goto bad;
5472380f4f2Sreyk 
548b045ffeeSreyk 	s = fd == -1 ? socket(ss->ss_family,
549b045ffeeSreyk 	    SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP) : fd;
5503c03a838Sreyk 	if (s == -1)
5512edd718bSreyk 		goto bad;
55277273a16Sreyk 
55377273a16Sreyk 	/*
55477273a16Sreyk 	 * Socket options
55577273a16Sreyk 	 */
5562edd718bSreyk 	bzero(&lng, sizeof(lng));
5572edd718bSreyk 	if (setsockopt(s, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1)
5582edd718bSreyk 		goto bad;
5595db041b0Sdhill 	if (reuseport) {
5602edd718bSreyk 		val = 1;
5615db041b0Sdhill 		if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &val,
5625db041b0Sdhill 			sizeof(int)) == -1)
5632edd718bSreyk 			goto bad;
5645db041b0Sdhill 	}
56577273a16Sreyk 	if (proto->tcpflags & TCPFLAG_BUFSIZ) {
56677273a16Sreyk 		val = proto->tcpbufsiz;
56777273a16Sreyk 		if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
56877273a16Sreyk 		    &val, sizeof(val)) == -1)
56977273a16Sreyk 			goto bad;
57077273a16Sreyk 		val = proto->tcpbufsiz;
57177273a16Sreyk 		if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
57277273a16Sreyk 		    &val, sizeof(val)) == -1)
57377273a16Sreyk 			goto bad;
57477273a16Sreyk 	}
5752edd718bSreyk 
57677273a16Sreyk 	/*
57777273a16Sreyk 	 * IP options
57877273a16Sreyk 	 */
57977273a16Sreyk 	if (proto->tcpflags & TCPFLAG_IPTTL) {
58077273a16Sreyk 		val = (int)proto->tcpipttl;
581829b7945Sjca 		switch (ss->ss_family) {
582829b7945Sjca 		case AF_INET:
58377273a16Sreyk 			if (setsockopt(s, IPPROTO_IP, IP_TTL,
58477273a16Sreyk 			    &val, sizeof(val)) == -1)
58577273a16Sreyk 				goto bad;
586829b7945Sjca 			break;
587829b7945Sjca 		case AF_INET6:
588829b7945Sjca 			if (setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
589829b7945Sjca 			    &val, sizeof(val)) == -1)
590829b7945Sjca 				goto bad;
591829b7945Sjca 			break;
592829b7945Sjca 		}
59377273a16Sreyk 	}
59477273a16Sreyk 	if (proto->tcpflags & TCPFLAG_IPMINTTL) {
59577273a16Sreyk 		val = (int)proto->tcpipminttl;
596829b7945Sjca 		switch (ss->ss_family) {
597829b7945Sjca 		case AF_INET:
59877273a16Sreyk 			if (setsockopt(s, IPPROTO_IP, IP_MINTTL,
59977273a16Sreyk 			    &val, sizeof(val)) == -1)
60077273a16Sreyk 				goto bad;
601829b7945Sjca 			break;
602829b7945Sjca 		case AF_INET6:
603829b7945Sjca 			if (setsockopt(s, IPPROTO_IPV6, IPV6_MINHOPCOUNT,
604829b7945Sjca 			    &val, sizeof(val)) == -1)
605829b7945Sjca 				goto bad;
606829b7945Sjca 			break;
607829b7945Sjca 		}
60877273a16Sreyk 	}
60977273a16Sreyk 
61077273a16Sreyk 	/*
61177273a16Sreyk 	 * TCP options
61277273a16Sreyk 	 */
6132edd718bSreyk 	if (proto->tcpflags & (TCPFLAG_NODELAY|TCPFLAG_NNODELAY)) {
6142edd718bSreyk 		if (proto->tcpflags & TCPFLAG_NNODELAY)
6152edd718bSreyk 			val = 0;
6162edd718bSreyk 		else
6172edd718bSreyk 			val = 1;
6182edd718bSreyk 		if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
6192edd718bSreyk 		    &val, sizeof(val)) == -1)
6202edd718bSreyk 			goto bad;
6212edd718bSreyk 	}
6222edd718bSreyk 	if (proto->tcpflags & (TCPFLAG_SACK|TCPFLAG_NSACK)) {
6232edd718bSreyk 		if (proto->tcpflags & TCPFLAG_NSACK)
6242edd718bSreyk 			val = 0;
6252edd718bSreyk 		else
6262edd718bSreyk 			val = 1;
6272edd718bSreyk 		if (setsockopt(s, IPPROTO_TCP, TCP_SACK_ENABLE,
6282edd718bSreyk 		    &val, sizeof(val)) == -1)
6292edd718bSreyk 			goto bad;
6302edd718bSreyk 	}
6312edd718bSreyk 
6322edd718bSreyk 	return (s);
6332edd718bSreyk 
6342edd718bSreyk  bad:
6352edd718bSreyk 	if (s != -1)
6362edd718bSreyk 		close(s);
6372edd718bSreyk 	return (-1);
6382edd718bSreyk }
6392edd718bSreyk 
6402edd718bSreyk int
6412edd718bSreyk relay_socket_connect(struct sockaddr_storage *ss, in_port_t port,
6423c03a838Sreyk     struct protocol *proto, int fd)
6432edd718bSreyk {
6442edd718bSreyk 	int	s;
6452edd718bSreyk 
6465db041b0Sdhill 	if ((s = relay_socket(ss, port, proto, fd, 0)) == -1)
6472edd718bSreyk 		return (-1);
6482edd718bSreyk 
6492edd718bSreyk 	if (connect(s, (struct sockaddr *)ss, ss->ss_len) == -1) {
6502edd718bSreyk 		if (errno != EINPROGRESS)
6512edd718bSreyk 			goto bad;
6522edd718bSreyk 	}
6532edd718bSreyk 
6542edd718bSreyk 	return (s);
6552edd718bSreyk 
6562edd718bSreyk  bad:
6572edd718bSreyk 	close(s);
6582edd718bSreyk 	return (-1);
6592edd718bSreyk }
6602edd718bSreyk 
6612edd718bSreyk int
6622edd718bSreyk relay_socket_listen(struct sockaddr_storage *ss, in_port_t port,
6632edd718bSreyk     struct protocol *proto)
6642edd718bSreyk {
6652edd718bSreyk 	int s;
6662edd718bSreyk 
6675db041b0Sdhill 	if ((s = relay_socket(ss, port, proto, -1, 1)) == -1)
6682edd718bSreyk 		return (-1);
6692edd718bSreyk 
6702edd718bSreyk 	if (bind(s, (struct sockaddr *)ss, ss->ss_len) == -1)
6712edd718bSreyk 		goto bad;
672dae1a80bSreyk 	if (listen(s, proto->tcpbacklog) == -1)
6732edd718bSreyk 		goto bad;
6742edd718bSreyk 
6752edd718bSreyk 	return (s);
6762edd718bSreyk 
6772edd718bSreyk  bad:
6782edd718bSreyk 	close(s);
6792edd718bSreyk 	return (-1);
6802edd718bSreyk }
6812edd718bSreyk 
6822edd718bSreyk void
6832edd718bSreyk relay_connected(int fd, short sig, void *arg)
6842edd718bSreyk {
68565f47834Sreyk 	char			 obuf[128];
68648240b8fSbluhm 	struct rsession		*con = arg;
68748240b8fSbluhm 	struct relay		*rlay = con->se_relay;
688807a0a46Sbenno 	struct protocol		*proto = rlay->rl_proto;
6892edd718bSreyk 	evbuffercb		 outrd = relay_read;
6902edd718bSreyk 	evbuffercb		 outwr = relay_write;
6912edd718bSreyk 	struct bufferevent	*bev;
69233e8bb87Sreyk 	struct ctl_relay_event	*out = &con->se_out;
693e432967fSbenno 	char			*msg;
694ad7f4743Sbenno 	socklen_t		 len;
695ad7f4743Sbenno 	int			 error;
6962edd718bSreyk 
6972edd718bSreyk 	if (sig == EV_TIMEOUT) {
698a15b848eSreyk 		relay_abort_http(con, 504, "connect timeout", 0);
6992edd718bSreyk 		return;
7002edd718bSreyk 	}
7012edd718bSreyk 
702ad7f4743Sbenno 	len = sizeof(error);
703e432967fSbenno 	if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) {
704e432967fSbenno 		relay_abort_http(con, 500, "getsockopt failed", 0);
705ad7f4743Sbenno 		return;
706ad7f4743Sbenno 	}
707e432967fSbenno 	if (error) {
708e432967fSbenno 		errno = error;
709e432967fSbenno 		if (asprintf(&msg, "socket error: %s",
710e432967fSbenno 		    strerror(error)) >= 0) {
711e432967fSbenno 			relay_abort_http(con, 500, msg, 0);
712e432967fSbenno 			free(msg);
713e432967fSbenno 			return;
714e432967fSbenno 		} else {
715e432967fSbenno 			relay_abort_http(con, 500,
716e432967fSbenno 			    "socket error and asprintf failed", 0);
717e432967fSbenno 			return;
718e432967fSbenno 		}
719e432967fSbenno 	}
720ad7f4743Sbenno 
72185e5f500Sclaudio 	if ((rlay->rl_conf.flags & F_TLSCLIENT) && (out->tls == NULL)) {
7227bb52228Sreyk 		relay_tls_transaction(con, out);
72333e8bb87Sreyk 		return;
72433e8bb87Sreyk 	}
72533e8bb87Sreyk 
726cb8b0e56Sreyk 	DPRINTF("%s: session %d: successful", __func__, con->se_id);
7272edd718bSreyk 
72865f47834Sreyk 	/* Log destination if it was changed in a keep-alive connection */
72965f47834Sreyk 	if ((con->se_table != con->se_table0) &&
73065f47834Sreyk 	    (env->sc_conf.opts & (RELAYD_OPT_LOGCON|RELAYD_OPT_LOGCONERR))) {
73165f47834Sreyk 		con->se_table0 = con->se_table;
73265f47834Sreyk 		memset(&obuf, 0, sizeof(obuf));
73365f47834Sreyk 		(void)print_host(&con->se_out.ss, obuf, sizeof(obuf));
73465f47834Sreyk 		if (asprintf(&msg, " -> %s:%d",
73565f47834Sreyk 		    obuf, ntohs(con->se_out.port)) == -1) {
73665f47834Sreyk 			relay_abort_http(con, 500,
73765f47834Sreyk 			    "connection changed and asprintf failed", 0);
73865f47834Sreyk 			return;
73965f47834Sreyk 		}
74065f47834Sreyk 		relay_log(con, msg);
74165f47834Sreyk 		free(msg);
74265f47834Sreyk 	}
74365f47834Sreyk 
7444a5b9b3eSreyk 	switch (rlay->rl_proto->type) {
74532678a96Sreyk 	case RELAY_PROTO_HTTP:
746cb8b0e56Sreyk 		if (relay_httpdesc_init(out) == -1) {
747cb8b0e56Sreyk 			relay_close(con,
7480be9d00aSbenno 			    "failed to allocate http descriptor", 1);
74932678a96Sreyk 			return;
75032678a96Sreyk 		}
751cb8b0e56Sreyk 		con->se_out.toread = TOREAD_HTTP_HEADER;
752cb8b0e56Sreyk 		outrd = relay_read_http;
75332678a96Sreyk 		break;
75432678a96Sreyk 	case RELAY_PROTO_TCP:
7557cb87172Sbluhm 		/* Use defaults */
75632678a96Sreyk 		break;
75732678a96Sreyk 	default:
758efc39811Sbenno 		fatalx("%s: unknown protocol", __func__);
75932678a96Sreyk 	}
76032678a96Sreyk 
7612edd718bSreyk 	/*
7622edd718bSreyk 	 * Relay <-> Server
7632edd718bSreyk 	 */
764f8eb77d7Sthib 	bev = bufferevent_new(fd, outrd, outwr, relay_error, &con->se_out);
7652edd718bSreyk 	if (bev == NULL) {
766a15b848eSreyk 		relay_abort_http(con, 500,
767485dd52fSreyk 		    "failed to allocate output buffer event", 0);
7682edd718bSreyk 		return;
7692edd718bSreyk 	}
7709fe0157eSclaudio 	/* write pending output buffer now */
7719fe0157eSclaudio 	if (bufferevent_write_buffer(bev, con->se_out.output)) {
7729fe0157eSclaudio 		relay_abort_http(con, 500, strerror(errno), 0);
7739fe0157eSclaudio 		return;
7749fe0157eSclaudio 	}
775f8eb77d7Sthib 	con->se_out.bev = bev;
77633e8bb87Sreyk 
7777bb52228Sreyk 	/* Initialize the TLS wrapper */
77885e5f500Sclaudio 	if ((rlay->rl_conf.flags & F_TLSCLIENT) && (out->tls != NULL))
7797bb52228Sreyk 		relay_tls_connected(out);
78033e8bb87Sreyk 
7812edd718bSreyk 	bufferevent_settimeout(bev,
7824a5b9b3eSreyk 	    rlay->rl_conf.timeout.tv_sec, rlay->rl_conf.timeout.tv_sec);
783807a0a46Sbenno 	bufferevent_setwatermark(bev, EV_WRITE,
784807a0a46Sbenno 		RELAY_MIN_PREFETCHED * proto->tcpbufsiz, 0);
7852edd718bSreyk 	bufferevent_enable(bev, EV_READ|EV_WRITE);
786807a0a46Sbenno 	if (con->se_in.bev)
787807a0a46Sbenno 		bufferevent_enable(con->se_in.bev, EV_READ);
7887cb87172Sbluhm 
7897cb87172Sbluhm 	if (relay_splice(&con->se_out) == -1)
7900be9d00aSbenno 		relay_close(con, strerror(errno), 1);
7912edd718bSreyk }
7922edd718bSreyk 
7932edd718bSreyk void
794f4a6e73bSreyk relay_input(struct rsession *con)
7952edd718bSreyk {
79648240b8fSbluhm 	struct relay	*rlay = con->se_relay;
797807a0a46Sbenno 	struct protocol	*proto = rlay->rl_proto;
7982edd718bSreyk 	evbuffercb	 inrd = relay_read;
7992edd718bSreyk 	evbuffercb	 inwr = relay_write;
8002edd718bSreyk 
8014a5b9b3eSreyk 	switch (rlay->rl_proto->type) {
8022edd718bSreyk 	case RELAY_PROTO_HTTP:
80353e8df0dSbenno 		if (relay_http_priv_init(con) == -1) {
804cb8b0e56Sreyk 			relay_close(con,
8050be9d00aSbenno 			    "failed to allocate http descriptor", 1);
8062edd718bSreyk 			return;
8072edd718bSreyk 		}
808cb8b0e56Sreyk 		con->se_in.toread = TOREAD_HTTP_HEADER;
809cb8b0e56Sreyk 		inrd = relay_read_http;
8102edd718bSreyk 		break;
8112edd718bSreyk 	case RELAY_PROTO_TCP:
8122edd718bSreyk 		/* Use defaults */
8132edd718bSreyk 		break;
8142edd718bSreyk 	default:
815efc39811Sbenno 		fatalx("%s: unknown protocol", __func__);
8162edd718bSreyk 	}
8172edd718bSreyk 
8182edd718bSreyk 	/*
8192edd718bSreyk 	 * Client <-> Relay
8202edd718bSreyk 	 */
821f8eb77d7Sthib 	con->se_in.bev = bufferevent_new(con->se_in.s, inrd, inwr,
822f8eb77d7Sthib 	    relay_error, &con->se_in);
823f8eb77d7Sthib 	if (con->se_in.bev == NULL) {
8240be9d00aSbenno 		relay_close(con, "failed to allocate input buffer event", 1);
8252edd718bSreyk 		return;
8262edd718bSreyk 	}
8272edd718bSreyk 
8287bb52228Sreyk 	/* Initialize the TLS wrapper */
82985e5f500Sclaudio 	if ((rlay->rl_conf.flags & F_TLS) && con->se_in.tls != NULL)
8307bb52228Sreyk 		relay_tls_connected(&con->se_in);
8312edd718bSreyk 
832f8eb77d7Sthib 	bufferevent_settimeout(con->se_in.bev,
8334a5b9b3eSreyk 	    rlay->rl_conf.timeout.tv_sec, rlay->rl_conf.timeout.tv_sec);
834807a0a46Sbenno 	bufferevent_setwatermark(con->se_in.bev, EV_WRITE,
835807a0a46Sbenno 		RELAY_MIN_PREFETCHED * proto->tcpbufsiz, 0);
836f8eb77d7Sthib 	bufferevent_enable(con->se_in.bev, EV_READ|EV_WRITE);
8377cb87172Sbluhm 
8387cb87172Sbluhm 	if (relay_splice(&con->se_in) == -1)
8390be9d00aSbenno 		relay_close(con, strerror(errno), 1);
8402edd718bSreyk }
8412edd718bSreyk 
8422edd718bSreyk void
8432edd718bSreyk relay_write(struct bufferevent *bev, void *arg)
8442edd718bSreyk {
84548240b8fSbluhm 	struct ctl_relay_event	*cre = arg;
846ff13fb34Sbluhm 	struct rsession		*con = cre->con;
847c58e32e8Sbluhm 
848fd1841a3Sreyk 	getmonotime(&con->se_tv_last);
849fd1841a3Sreyk 
850adb5490dSbluhm 	if (con->se_done && EVBUFFER_LENGTH(EVBUFFER_OUTPUT(bev)) == 0)
851c58e32e8Sbluhm 		goto done;
852807a0a46Sbenno 	if (cre->dst->bev)
853807a0a46Sbenno 		bufferevent_enable(cre->dst->bev, EV_READ);
854adb5490dSbluhm 	if (relay_splice(cre->dst) == -1)
855adb5490dSbluhm 		goto fail;
856807a0a46Sbenno 
857c58e32e8Sbluhm 	return;
858c58e32e8Sbluhm  done:
8590be9d00aSbenno 	relay_close(con, "last write (done)", 0);
860c58e32e8Sbluhm 	return;
861c58e32e8Sbluhm  fail:
8620be9d00aSbenno 	relay_close(con, strerror(errno), 1);
8632edd718bSreyk }
8642edd718bSreyk 
8652edd718bSreyk void
86607c84b7eSreyk relay_dump(struct ctl_relay_event *cre, const void *buf, size_t len)
86707c84b7eSreyk {
868bd8d7023Sreyk 	if (!len)
869bd8d7023Sreyk 		return;
870bd8d7023Sreyk 
87107c84b7eSreyk 	/*
87207c84b7eSreyk 	 * This function will dump the specified message directly
87307c84b7eSreyk 	 * to the underlying session, without waiting for success
87407c84b7eSreyk 	 * of non-blocking events etc. This is useful to print an
87507c84b7eSreyk 	 * error message before gracefully closing the session.
87607c84b7eSreyk 	 */
87785e5f500Sclaudio 	if (cre->tls != NULL)
87885e5f500Sclaudio 		(void)tls_write(cre->tls, buf, len);
87907c84b7eSreyk 	else
88007c84b7eSreyk 		(void)write(cre->s, buf, len);
88107c84b7eSreyk }
88207c84b7eSreyk 
88307c84b7eSreyk void
8842edd718bSreyk relay_read(struct bufferevent *bev, void *arg)
8852edd718bSreyk {
88648240b8fSbluhm 	struct ctl_relay_event	*cre = arg;
887ff13fb34Sbluhm 	struct rsession		*con = cre->con;
888807a0a46Sbenno 	struct protocol		*proto = con->se_relay->rl_proto;
8892edd718bSreyk 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
8902edd718bSreyk 
891fd1841a3Sreyk 	getmonotime(&con->se_tv_last);
89230791b79Sbluhm 	cre->timedout = 0;
893fd1841a3Sreyk 
8942edd718bSreyk 	if (!EVBUFFER_LENGTH(src))
8952edd718bSreyk 		return;
89611aa8c65Sreyk 	if (relay_bufferevent_write_buffer(cre->dst, src) == -1)
89711aa8c65Sreyk 		goto fail;
898f8eb77d7Sthib 	if (con->se_done)
8992edd718bSreyk 		goto done;
900f657cf8dSbluhm 	if (cre->dst->bev)
901fe48c67dSbluhm 		bufferevent_enable(cre->dst->bev, EV_READ);
902807a0a46Sbenno 	if (cre->dst->bev && EVBUFFER_LENGTH(EVBUFFER_OUTPUT(cre->dst->bev)) >
903807a0a46Sbenno 	    (size_t)RELAY_MAX_PREFETCH * proto->tcpbufsiz)
904807a0a46Sbenno 		bufferevent_disable(bev, EV_READ);
905807a0a46Sbenno 
9062edd718bSreyk 	return;
9072edd718bSreyk  done:
9080be9d00aSbenno 	relay_close(con, "last read (done)", 0);
90911aa8c65Sreyk 	return;
91011aa8c65Sreyk  fail:
9110be9d00aSbenno 	relay_close(con, strerror(errno), 1);
9122edd718bSreyk }
9132edd718bSreyk 
91470d03dadSbluhm /*
91570d03dadSbluhm  * Splice sockets from cre to cre->dst if applicable.  Returns:
91670d03dadSbluhm  * -1 socket splicing has failed
91770d03dadSbluhm  * 0 socket splicing is currently not possible
91870d03dadSbluhm  * 1 socket splicing was successful
91970d03dadSbluhm  */
9200d428c40Sreyk int
9217cb87172Sbluhm relay_splice(struct ctl_relay_event *cre)
9227cb87172Sbluhm {
9237cb87172Sbluhm 	struct rsession		*con = cre->con;
92448240b8fSbluhm 	struct relay		*rlay = con->se_relay;
9257cb87172Sbluhm 	struct protocol		*proto = rlay->rl_proto;
9267cb87172Sbluhm 	struct splice		 sp;
9277cb87172Sbluhm 
9287bb52228Sreyk 	if ((rlay->rl_conf.flags & (F_TLS|F_TLSCLIENT)) ||
9297cb87172Sbluhm 	    (proto->tcpflags & TCPFLAG_NSPLICE))
9307cb87172Sbluhm 		return (0);
9317cb87172Sbluhm 
932c58e32e8Sbluhm 	if (cre->splicelen >= 0)
9337cb87172Sbluhm 		return (0);
9347cb87172Sbluhm 
935c58e32e8Sbluhm 	/* still not connected */
936c58e32e8Sbluhm 	if (cre->bev == NULL || cre->dst->bev == NULL)
937c58e32e8Sbluhm 		return (0);
938c58e32e8Sbluhm 
939c58e32e8Sbluhm 	if (!(cre->toread == TOREAD_UNLIMITED || cre->toread > 0)) {
940c58e32e8Sbluhm 		DPRINTF("%s: session %d: splice dir %d, nothing to read %lld",
941c58e32e8Sbluhm 		    __func__, con->se_id, cre->dir, cre->toread);
942c58e32e8Sbluhm 		return (0);
943c58e32e8Sbluhm 	}
944c58e32e8Sbluhm 
945c58e32e8Sbluhm 	/* do not splice before buffers have not been completely flushed */
946c58e32e8Sbluhm 	if (EVBUFFER_LENGTH(cre->bev->input) ||
947c58e32e8Sbluhm 	    EVBUFFER_LENGTH(cre->dst->bev->output)) {
948c58e32e8Sbluhm 		DPRINTF("%s: session %d: splice dir %d, dirty buffer",
949c58e32e8Sbluhm 		    __func__, con->se_id, cre->dir);
950c58e32e8Sbluhm 		bufferevent_disable(cre->bev, EV_READ);
951c58e32e8Sbluhm 		return (0);
952c58e32e8Sbluhm 	}
953c58e32e8Sbluhm 
9547cb87172Sbluhm 	bzero(&sp, sizeof(sp));
9557cb87172Sbluhm 	sp.sp_fd = cre->dst->s;
956c58e32e8Sbluhm 	sp.sp_max = cre->toread > 0 ? cre->toread : 0;
957cb8b0e56Sreyk 	bcopy(&rlay->rl_conf.timeout, &sp.sp_idle, sizeof(sp.sp_idle));
9587cb87172Sbluhm 	if (setsockopt(cre->s, SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)) == -1) {
9597cb87172Sbluhm 		log_debug("%s: session %d: splice dir %d failed: %s",
9607cb87172Sbluhm 		    __func__, con->se_id, cre->dir, strerror(errno));
9617cb87172Sbluhm 		return (-1);
9627cb87172Sbluhm 	}
9637cb87172Sbluhm 	cre->splicelen = 0;
964c58e32e8Sbluhm 	bufferevent_enable(cre->bev, EV_READ);
965c58e32e8Sbluhm 
966c58e32e8Sbluhm 	DPRINTF("%s: session %d: splice dir %d, maximum %lld, successful",
967c58e32e8Sbluhm 	    __func__, con->se_id, cre->dir, cre->toread);
968c58e32e8Sbluhm 
96970d03dadSbluhm 	return (1);
9707cb87172Sbluhm }
9717cb87172Sbluhm 
9727cb87172Sbluhm int
973570b76e9Sbluhm relay_splicelen(struct ctl_relay_event *cre)
974570b76e9Sbluhm {
975ff13fb34Sbluhm 	struct rsession		*con = cre->con;
976570b76e9Sbluhm 	off_t			 len;
977570b76e9Sbluhm 	socklen_t		 optlen;
978570b76e9Sbluhm 
979c58e32e8Sbluhm 	if (cre->splicelen < 0)
980c58e32e8Sbluhm 		return (0);
981c58e32e8Sbluhm 
982570b76e9Sbluhm 	optlen = sizeof(len);
983570b76e9Sbluhm 	if (getsockopt(cre->s, SOL_SOCKET, SO_SPLICE, &len, &optlen) == -1) {
9847cb87172Sbluhm 		log_debug("%s: session %d: splice dir %d get length failed: %s",
9857cb87172Sbluhm 		    __func__, con->se_id, cre->dir, strerror(errno));
9867cb87172Sbluhm 		return (-1);
987570b76e9Sbluhm 	}
988c58e32e8Sbluhm 
989c58e32e8Sbluhm 	DPRINTF("%s: session %d: splice dir %d, length %lld",
990c58e32e8Sbluhm 	    __func__, con->se_id, cre->dir, len);
991c58e32e8Sbluhm 
992570b76e9Sbluhm 	if (len > cre->splicelen) {
993fd1841a3Sreyk 		getmonotime(&con->se_tv_last);
994fd1841a3Sreyk 
995570b76e9Sbluhm 		cre->splicelen = len;
996570b76e9Sbluhm 		return (1);
997570b76e9Sbluhm 	}
998c58e32e8Sbluhm 
999c58e32e8Sbluhm 	return (0);
1000c58e32e8Sbluhm }
1001c58e32e8Sbluhm 
1002c58e32e8Sbluhm int
1003c58e32e8Sbluhm relay_spliceadjust(struct ctl_relay_event *cre)
1004c58e32e8Sbluhm {
1005c58e32e8Sbluhm 	if (cre->splicelen < 0)
1006c58e32e8Sbluhm 		return (0);
1007c58e32e8Sbluhm 	if (relay_splicelen(cre) == -1)
1008c58e32e8Sbluhm 		return (-1);
1009c58e32e8Sbluhm 	if (cre->splicelen > 0 && cre->toread > 0)
1010c58e32e8Sbluhm 		cre->toread -= cre->splicelen;
1011c58e32e8Sbluhm 	cre->splicelen = -1;
1012c58e32e8Sbluhm 
1013570b76e9Sbluhm 	return (0);
1014570b76e9Sbluhm }
1015570b76e9Sbluhm 
10162edd718bSreyk void
10172edd718bSreyk relay_error(struct bufferevent *bev, short error, void *arg)
10182edd718bSreyk {
101948240b8fSbluhm 	struct ctl_relay_event	*cre = arg;
1020ff13fb34Sbluhm 	struct rsession		*con = cre->con;
10212edd718bSreyk 	struct evbuffer		*dst;
10222edd718bSreyk 
1023ce7520d0Sclaudio 	DPRINTF("%s: session %d: dir %d state %d to read %lld event error %x",
1024ce7520d0Sclaudio 		__func__, con->se_id, cre->dir, cre->state, cre->toread, error);
10252edd718bSreyk 	if (error & EVBUFFER_TIMEOUT) {
10267cb87172Sbluhm 		if (cre->splicelen >= 0) {
10277cb87172Sbluhm 			bufferevent_enable(bev, EV_READ);
10287cb87172Sbluhm 		} else if (cre->dst->splicelen >= 0) {
10297cb87172Sbluhm 			switch (relay_splicelen(cre->dst)) {
10307cb87172Sbluhm 			case -1:
10317cb87172Sbluhm 				goto fail;
10327cb87172Sbluhm 			case 0:
10330be9d00aSbenno 				relay_close(con, "buffer event timeout", 1);
10347cb87172Sbluhm 				break;
10357cb87172Sbluhm 			case 1:
103630791b79Sbluhm 				cre->timedout = 1;
10377cb87172Sbluhm 				bufferevent_enable(bev, EV_READ);
10387cb87172Sbluhm 				break;
10397cb87172Sbluhm 			}
10407cb87172Sbluhm 		} else {
10410be9d00aSbenno 			relay_close(con, "buffer event timeout", 1);
10427cb87172Sbluhm 		}
1043570b76e9Sbluhm 		return;
1044570b76e9Sbluhm 	}
10457cb87172Sbluhm 	if (error & EVBUFFER_ERROR && errno == ETIMEDOUT) {
10467cb87172Sbluhm 		if (cre->dst->splicelen >= 0) {
10477cb87172Sbluhm 			switch (relay_splicelen(cre->dst)) {
10487cb87172Sbluhm 			case -1:
10497cb87172Sbluhm 				goto fail;
10507cb87172Sbluhm 			case 0:
10510be9d00aSbenno 				relay_close(con, "splice timeout", 1);
10527cb87172Sbluhm 				return;
10537cb87172Sbluhm 			case 1:
10547cb87172Sbluhm 				bufferevent_enable(bev, EV_READ);
10557cb87172Sbluhm 				break;
10567cb87172Sbluhm 			}
105730791b79Sbluhm 		} else if (cre->dst->timedout) {
10580be9d00aSbenno 			relay_close(con, "splice timeout", 1);
105930791b79Sbluhm 			return;
10607cb87172Sbluhm 		}
1061c58e32e8Sbluhm 		if (relay_spliceadjust(cre) == -1)
1062c58e32e8Sbluhm 			goto fail;
10637cb87172Sbluhm 		if (relay_splice(cre) == -1)
10647cb87172Sbluhm 			goto fail;
10652edd718bSreyk 		return;
10662edd718bSreyk 	}
1067c58e32e8Sbluhm 	if (error & EVBUFFER_ERROR && errno == EFBIG) {
1068c58e32e8Sbluhm 		if (relay_spliceadjust(cre) == -1)
1069c58e32e8Sbluhm 			goto fail;
1070c58e32e8Sbluhm 		bufferevent_enable(cre->bev, EV_READ);
1071c58e32e8Sbluhm 		return;
1072c58e32e8Sbluhm 	}
10732edd718bSreyk 	if (error & (EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF)) {
10742edd718bSreyk 		bufferevent_disable(bev, EV_READ|EV_WRITE);
10752edd718bSreyk 
1076f8eb77d7Sthib 		con->se_done = 1;
10772edd718bSreyk 		if (cre->dst->bev != NULL) {
10782edd718bSreyk 			dst = EVBUFFER_OUTPUT(cre->dst->bev);
1079cd9e7874Sreyk 			if (EVBUFFER_LENGTH(dst))
10802edd718bSreyk 				return;
108124d4a724Sbluhm 		} else if (cre->toread == TOREAD_UNLIMITED || cre->toread == 0)
1082c7143fa4Sbluhm 			return;
10832edd718bSreyk 
10840be9d00aSbenno 		relay_close(con, "done", 0);
10852edd718bSreyk 		return;
10862edd718bSreyk 	}
10870be9d00aSbenno 	relay_close(con, "buffer event error", 1);
10887cb87172Sbluhm 	return;
10897cb87172Sbluhm  fail:
10900be9d00aSbenno 	relay_close(con, strerror(errno), 1);
10912edd718bSreyk }
10922edd718bSreyk 
10932edd718bSreyk void
10946b61fee8Sderaadt relay_accept(int fd, short event, void *arg)
10952edd718bSreyk {
1096325f6e14Sreyk 	struct privsep		*ps = env->sc_ps;
109748240b8fSbluhm 	struct relay		*rlay = arg;
1098f4a6e73bSreyk 	struct rsession		*con = NULL;
10992edd718bSreyk 	struct ctl_natlook	*cnl = NULL;
11002edd718bSreyk 	socklen_t		 slen;
11012edd718bSreyk 	struct timeval		 tv;
11022edd718bSreyk 	struct sockaddr_storage	 ss;
11032edd718bSreyk 	int			 s = -1;
11042edd718bSreyk 
11056b61fee8Sderaadt 	event_add(&rlay->rl_ev, NULL);
11066b61fee8Sderaadt 	if ((event & EV_TIMEOUT))
11072edd718bSreyk 		return;
11082edd718bSreyk 
11096b61fee8Sderaadt 	slen = sizeof(ss);
11102f89c21dSbenno 	if ((s = accept_reserve(fd, (struct sockaddr *)&ss,
11110ccd53b9Sguenther 	    &slen, FD_RESERVE, &relay_inflight)) == -1) {
11126b61fee8Sderaadt 		/*
11136b61fee8Sderaadt 		 * Pause accept if we are out of file descriptors, or
11146b61fee8Sderaadt 		 * libevent will haunt us here too.
11156b61fee8Sderaadt 		 */
11166b61fee8Sderaadt 		if (errno == ENFILE || errno == EMFILE) {
11176b61fee8Sderaadt 			struct timeval evtpause = { 1, 0 };
11186b61fee8Sderaadt 
11196b61fee8Sderaadt 			event_del(&rlay->rl_ev);
11206b61fee8Sderaadt 			evtimer_add(&rlay->rl_evt, &evtpause);
11210ccd53b9Sguenther 			log_debug("%s: deferring connections", __func__);
11226b61fee8Sderaadt 		}
11236b61fee8Sderaadt 		return;
11246b61fee8Sderaadt 	}
1125ea42f25aSclaudio 	if (rlay->rl_conf.flags & F_DISABLE)
11262edd718bSreyk 		goto err;
11272edd718bSreyk 
1128ff13fb34Sbluhm 	if ((con = calloc(1, sizeof(*con))) == NULL)
11292edd718bSreyk 		goto err;
11302edd718bSreyk 
1131f89c4966Sflorian 	/* Pre-allocate log buffer */
1132f89c4966Sflorian 	con->se_haslog = 0;
1133f89c4966Sflorian 	con->se_log = evbuffer_new();
1134f89c4966Sflorian 	if (con->se_log == NULL)
1135f89c4966Sflorian 		goto err;
1136f89c4966Sflorian 
1137f8eb77d7Sthib 	con->se_in.s = s;
113885e5f500Sclaudio 	con->se_in.tls = NULL;
1139f8eb77d7Sthib 	con->se_out.s = -1;
114085e5f500Sclaudio 	con->se_out.tls = NULL;
1141f8eb77d7Sthib 	con->se_in.dst = &con->se_out;
1142f8eb77d7Sthib 	con->se_out.dst = &con->se_in;
1143f8eb77d7Sthib 	con->se_in.con = con;
1144f8eb77d7Sthib 	con->se_out.con = con;
1145570b76e9Sbluhm 	con->se_in.splicelen = -1;
1146570b76e9Sbluhm 	con->se_out.splicelen = -1;
1147a9feb0c6Sbluhm 	con->se_in.toread = TOREAD_UNLIMITED;
1148a9feb0c6Sbluhm 	con->se_out.toread = TOREAD_UNLIMITED;
1149f8eb77d7Sthib 	con->se_relay = rlay;
1150f8eb77d7Sthib 	con->se_id = ++relay_conid;
1151f8eb77d7Sthib 	con->se_relayid = rlay->rl_conf.id;
115286b74329Sreyk 	con->se_pid = getpid();
1153f8eb77d7Sthib 	con->se_in.dir = RELAY_DIR_REQUEST;
1154f8eb77d7Sthib 	con->se_out.dir = RELAY_DIR_RESPONSE;
1155f8eb77d7Sthib 	con->se_retry = rlay->rl_conf.dstretry;
11563c03a838Sreyk 	con->se_bnds = -1;
1157f8eb77d7Sthib 	con->se_out.port = rlay->rl_conf.dstport;
11589d421a7aSreyk 	switch (ss.ss_family) {
11599d421a7aSreyk 	case AF_INET:
1160f8eb77d7Sthib 		con->se_in.port = ((struct sockaddr_in *)&ss)->sin_port;
11619d421a7aSreyk 		break;
11629d421a7aSreyk 	case AF_INET6:
1163f8eb77d7Sthib 		con->se_in.port = ((struct sockaddr_in6 *)&ss)->sin6_port;
11649d421a7aSreyk 		break;
11659d421a7aSreyk 	}
1166860302f3Sreyk 	memcpy(&con->se_in.ss, &ss, sizeof(con->se_in.ss));
1167860302f3Sreyk 
1168860302f3Sreyk 	slen = sizeof(con->se_sockname);
1169860302f3Sreyk 	if (getsockname(s, (struct sockaddr *)&con->se_sockname, &slen) == -1) {
1170860302f3Sreyk 		relay_close(con, "sockname lookup failed", 1);
1171860302f3Sreyk 		return;
1172860302f3Sreyk 	}
1173fd1841a3Sreyk 
1174fd1841a3Sreyk 	getmonotime(&con->se_tv_start);
1175fd1841a3Sreyk 	bcopy(&con->se_tv_start, &con->se_tv_last, sizeof(con->se_tv_last));
11762edd718bSreyk 
1177acb89df4Sreyk 	if (rlay->rl_conf.flags & F_HASHKEY) {
1178acb89df4Sreyk 		SipHash24_Init(&con->se_siphashctx,
1179acb89df4Sreyk 		    &rlay->rl_conf.hashkey.siphashkey);
1180acb89df4Sreyk 	}
1181acb89df4Sreyk 
11821c772384Sreyk 	relay_sessions++;
11834a5b9b3eSreyk 	SPLAY_INSERT(session_tree, &rlay->rl_sessions, con);
11846e07057bSblambert 	relay_session_publish(con);
11851c772384Sreyk 
11861c772384Sreyk 	/* Increment the per-relay session counter */
1187325f6e14Sreyk 	rlay->rl_stats[ps->ps_instance].last++;
11881c772384Sreyk 
11892edd718bSreyk 	/* Pre-allocate output buffer */
1190f8eb77d7Sthib 	con->se_out.output = evbuffer_new();
1191f8eb77d7Sthib 	if (con->se_out.output == NULL) {
11920be9d00aSbenno 		relay_close(con, "failed to allocate output buffer", 1);
11932edd718bSreyk 		return;
11942edd718bSreyk 	}
11952edd718bSreyk 
1196523113bfSreyk 	if (rlay->rl_conf.flags & F_DIVERT) {
1197860302f3Sreyk 		memcpy(&con->se_out.ss, &con->se_sockname,
1198860302f3Sreyk 		    sizeof(con->se_out.ss));
1199523113bfSreyk 		con->se_out.port = relay_socket_getport(&con->se_out.ss);
1200523113bfSreyk 
1201523113bfSreyk 		/* Detect loop and fall back to the alternate forward target */
1202523113bfSreyk 		if (bcmp(&rlay->rl_conf.ss, &con->se_out.ss,
1203523113bfSreyk 		    sizeof(con->se_out.ss)) == 0 &&
1204523113bfSreyk 		    con->se_out.port == rlay->rl_conf.port)
1205523113bfSreyk 			con->se_out.ss.ss_family = AF_UNSPEC;
1206523113bfSreyk 	} else if (rlay->rl_conf.flags & F_NATLOOK) {
120748240b8fSbluhm 		if ((cnl = calloc(1, sizeof(*cnl))) == NULL) {
12080be9d00aSbenno 			relay_close(con, "failed to allocate nat lookup", 1);
12091c772384Sreyk 			return;
12102edd718bSreyk 		}
12112edd718bSreyk 
1212f8eb77d7Sthib 		con->se_cnl = cnl;
12132edd718bSreyk 		bzero(cnl, sizeof(*cnl));
12142edd718bSreyk 		cnl->in = -1;
1215f8eb77d7Sthib 		cnl->id = con->se_id;
1216325f6e14Sreyk 		cnl->proc = ps->ps_instance;
1217aeda6e0eSreyk 		cnl->proto = IPPROTO_TCP;
1218c086f8ecSreyk 
1219860302f3Sreyk 		memcpy(&cnl->src, &con->se_in.ss, sizeof(cnl->src));
1220860302f3Sreyk 		memcpy(&cnl->dst, &con->se_sockname, sizeof(cnl->dst));
1221c086f8ecSreyk 
1222c2c37c5dSreyk 		proc_compose(env->sc_ps, PROC_PFE, IMSG_NATLOOK,
1223b1feadc4Sbluhm 		    cnl, sizeof(*cnl));
12242edd718bSreyk 
12252edd718bSreyk 		/* Schedule timeout */
1226f8eb77d7Sthib 		evtimer_set(&con->se_ev, relay_natlook, con);
12274a5b9b3eSreyk 		bcopy(&rlay->rl_conf.timeout, &tv, sizeof(tv));
1228f8eb77d7Sthib 		evtimer_add(&con->se_ev, &tv);
12292edd718bSreyk 		return;
12302edd718bSreyk 	}
12312edd718bSreyk 
12327bb52228Sreyk 	if (rlay->rl_conf.flags & F_TLSINSPECT) {
1233cf39ad79Sreyk 		relay_preconnect(con);
1234cf39ad79Sreyk 		return;
1235cf39ad79Sreyk 	}
1236cf39ad79Sreyk 
12372edd718bSreyk 	relay_session(con);
12382edd718bSreyk 	return;
12392edd718bSreyk  err:
12402edd718bSreyk 	if (s != -1) {
12412edd718bSreyk 		close(s);
12422edd718bSreyk 		free(con);
12432f89c21dSbenno 		/*
1244e17f2f8fSmmcc 		 * the session struct was not completely set up, but still
12452f89c21dSbenno 		 * counted as an inflight session. account for this.
12462f89c21dSbenno 		 */
12472f89c21dSbenno 		relay_inflight--;
12482f89c21dSbenno 		log_debug("%s: inflight decremented, now %d",
12492f89c21dSbenno 		    __func__, relay_inflight);
12502edd718bSreyk 	}
12512edd718bSreyk }
12522edd718bSreyk 
1253acb89df4Sreyk void
1254acb89df4Sreyk relay_hash_addr(SIPHASH_CTX *ctx, struct sockaddr_storage *ss, int portset)
12552edd718bSreyk {
12562edd718bSreyk 	struct sockaddr_in	*sin4;
12572edd718bSreyk 	struct sockaddr_in6	*sin6;
1258acb89df4Sreyk 	in_port_t		 port;
12592edd718bSreyk 
12602edd718bSreyk 	if (ss->ss_family == AF_INET) {
12612edd718bSreyk 		sin4 = (struct sockaddr_in *)ss;
1262acb89df4Sreyk 		SipHash24_Update(ctx, &sin4->sin_addr,
1263acb89df4Sreyk 		    sizeof(struct in_addr));
12642edd718bSreyk 	} else {
12652edd718bSreyk 		sin6 = (struct sockaddr_in6 *)ss;
1266acb89df4Sreyk 		SipHash24_Update(ctx, &sin6->sin6_addr,
1267acb89df4Sreyk 		    sizeof(struct in6_addr));
12682edd718bSreyk 	}
12692edd718bSreyk 
1270acb89df4Sreyk 	if (portset != -1) {
1271acb89df4Sreyk 		port = (in_port_t)portset;
1272acb89df4Sreyk 		SipHash24_Update(ctx, &port, sizeof(port));
1273acb89df4Sreyk 	}
12742edd718bSreyk }
12752edd718bSreyk 
12762edd718bSreyk int
1277f4a6e73bSreyk relay_from_table(struct rsession *con)
12782edd718bSreyk {
127948240b8fSbluhm 	struct relay		*rlay = con->se_relay;
1280dfe4ee48Sbenno 	struct host		*host = NULL;
1281416fa9c0Sreyk 	struct relay_table	*rlt = NULL;
1282416fa9c0Sreyk 	struct table		*table = NULL;
128359e187d0Sreyk 	int			 idx = -1;
1284bfcf47adSbenno 	int			 cnt = 0;
1285bfcf47adSbenno 	int			 maxtries;
1286acb89df4Sreyk 	u_int64_t		 p = 0;
12872edd718bSreyk 
1288416fa9c0Sreyk 	/* the table is already selected */
1289416fa9c0Sreyk 	if (con->se_table != NULL) {
1290416fa9c0Sreyk 		rlt = con->se_table;
1291416fa9c0Sreyk 		table = rlt->rlt_table;
1292416fa9c0Sreyk 		if (table->conf.check && !table->up)
1293416fa9c0Sreyk 			table = NULL;
1294416fa9c0Sreyk 		goto gottable;
12952edd718bSreyk 	}
12962edd718bSreyk 
1297416fa9c0Sreyk 	/* otherwise grep the first active table */
1298416fa9c0Sreyk 	TAILQ_FOREACH(rlt, &rlay->rl_tables, rlt_entry) {
1299416fa9c0Sreyk 		table = rlt->rlt_table;
1300118081d7Sreyk 		if ((rlt->rlt_flags & F_USED) == 0 ||
1301416fa9c0Sreyk 		    (table->conf.check && !table->up))
1302416fa9c0Sreyk 			table = NULL;
1303416fa9c0Sreyk 		else
1304416fa9c0Sreyk 			break;
1305416fa9c0Sreyk 	}
1306416fa9c0Sreyk 
1307416fa9c0Sreyk  gottable:
1308416fa9c0Sreyk 	if (table == NULL) {
1309416fa9c0Sreyk 		log_debug("%s: session %d: no active hosts",
1310416fa9c0Sreyk 		    __func__, con->se_id);
1311416fa9c0Sreyk 		return (-1);
1312416fa9c0Sreyk 	}
1313416fa9c0Sreyk 
1314416fa9c0Sreyk 	switch (rlt->rlt_mode) {
13152edd718bSreyk 	case RELAY_DSTMODE_ROUNDROBIN:
1316acb89df4Sreyk 		if ((int)rlt->rlt_index >= rlt->rlt_nhosts)
1317acb89df4Sreyk 			rlt->rlt_index = 0;
1318acb89df4Sreyk 		idx = (int)rlt->rlt_index;
13192edd718bSreyk 		break;
132059e187d0Sreyk 	case RELAY_DSTMODE_RANDOM:
132159e187d0Sreyk 		idx = (int)arc4random_uniform(rlt->rlt_nhosts);
132259e187d0Sreyk 		break;
132359e187d0Sreyk 	case RELAY_DSTMODE_SRCHASH:
1324acb89df4Sreyk 		/* Source IP address without port */
1325acb89df4Sreyk 		relay_hash_addr(&con->se_siphashctx, &con->se_in.ss, -1);
1326acb89df4Sreyk 		break;
1327d4264a38Sblambert 	case RELAY_DSTMODE_LOADBALANCE:
1328d4264a38Sblambert 		/* Source IP address without port */
1329acb89df4Sreyk 		relay_hash_addr(&con->se_siphashctx, &con->se_in.ss, -1);
1330ae15eb0aSblambert 		/* FALLTHROUGH */
1331ae15eb0aSblambert 	case RELAY_DSTMODE_HASH:
133259e187d0Sreyk 		/* Local "destination" IP address and port */
1333acb89df4Sreyk 		relay_hash_addr(&con->se_siphashctx, &rlay->rl_conf.ss,
1334acb89df4Sreyk 		    rlay->rl_conf.port);
133559e187d0Sreyk 		break;
133659e187d0Sreyk 	default:
1337efc39811Sbenno 		fatalx("%s: unsupported mode", __func__);
133859e187d0Sreyk 		/* NOTREACHED */
13392edd718bSreyk 	}
1340acb89df4Sreyk 	if (idx == -1) {
1341bfcf47adSbenno 		/* handle all hashing algorithms */
1342acb89df4Sreyk 		p = SipHash24_End(&con->se_siphashctx);
1343acb89df4Sreyk 
1344acb89df4Sreyk 		/* Reset hash context */
1345acb89df4Sreyk 		SipHash24_Init(&con->se_siphashctx,
1346acb89df4Sreyk 		    &rlay->rl_conf.hashkey.siphashkey);
1347acb89df4Sreyk 
1348bfcf47adSbenno 		maxtries = (rlt->rlt_nhosts < RELAY_MAX_HASH_RETRIES ?
1349bfcf47adSbenno 		    rlt->rlt_nhosts : RELAY_MAX_HASH_RETRIES);
1350bfcf47adSbenno 		for (cnt = 0; cnt < maxtries; cnt++) {
1351acb89df4Sreyk 			if ((idx = p % rlt->rlt_nhosts) >= RELAY_MAXHOSTS)
135259e187d0Sreyk 				return (-1);
1353bfcf47adSbenno 
1354bfcf47adSbenno 			host = rlt->rlt_host[idx];
1355bfcf47adSbenno 
1356bfcf47adSbenno 			DPRINTF("%s: session %d: table %s host %s, "
1357bfcf47adSbenno 			    "p 0x%016llx, idx %d, cnt %d, max %d",
1358bfcf47adSbenno 			    __func__, con->se_id, table->conf.name,
1359bfcf47adSbenno 			    host->conf.name, p, idx, cnt, maxtries);
1360bfcf47adSbenno 
1361bfcf47adSbenno 			if (!table->conf.check || host->up == HOST_UP)
1362bfcf47adSbenno 				goto found;
1363bfcf47adSbenno 			p = p >> 1;
1364acb89df4Sreyk 		}
1365bfcf47adSbenno 	} else {
1366bfcf47adSbenno 		/* handle all non-hashing algorithms */
1367416fa9c0Sreyk 		host = rlt->rlt_host[idx];
136824d4a724Sbluhm 		DPRINTF("%s: session %d: table %s host %s, p 0x%016llx, idx %d",
13692166201eSreyk 		    __func__, con->se_id, table->conf.name, host->conf.name,
13702166201eSreyk 		    p, idx);
1371bfcf47adSbenno 	}
1372bfcf47adSbenno 
13732edd718bSreyk 	while (host != NULL) {
1374416fa9c0Sreyk 		DPRINTF("%s: session %d: host %s", __func__,
1375416fa9c0Sreyk 		    con->se_id, host->conf.name);
1376af50a7a9Sreyk 		if (!table->conf.check || host->up == HOST_UP)
13772edd718bSreyk 			goto found;
13782edd718bSreyk 		host = TAILQ_NEXT(host, entry);
13792edd718bSreyk 	}
1380af50a7a9Sreyk 	TAILQ_FOREACH(host, &table->hosts, entry) {
1381cb8b0e56Sreyk 		DPRINTF("%s: session %d: next host %s",
1382cb8b0e56Sreyk 		    __func__, con->se_id, host->conf.name);
1383af50a7a9Sreyk 		if (!table->conf.check || host->up == HOST_UP)
13842edd718bSreyk 			goto found;
13852edd718bSreyk 	}
13862edd718bSreyk 
13872edd718bSreyk 	/* Should not happen */
1388efc39811Sbenno 	fatalx("%s: no active hosts, desynchronized", __func__);
13892edd718bSreyk 
13902edd718bSreyk  found:
1391416fa9c0Sreyk 	if (rlt->rlt_mode == RELAY_DSTMODE_ROUNDROBIN)
1392acb89df4Sreyk 		rlt->rlt_index = host->idx + 1;
1393f8eb77d7Sthib 	con->se_retry = host->conf.retry;
1394f8eb77d7Sthib 	con->se_out.port = table->conf.port;
1395f8eb77d7Sthib 	bcopy(&host->conf.ss, &con->se_out.ss, sizeof(con->se_out.ss));
13962edd718bSreyk 
13972edd718bSreyk 	return (0);
13982edd718bSreyk }
13992edd718bSreyk 
14002edd718bSreyk void
14012edd718bSreyk relay_natlook(int fd, short event, void *arg)
14022edd718bSreyk {
140348240b8fSbluhm 	struct rsession		*con = arg;
140448240b8fSbluhm 	struct relay		*rlay = con->se_relay;
1405f8eb77d7Sthib 	struct ctl_natlook	*cnl = con->se_cnl;
14062edd718bSreyk 
14072edd718bSreyk 	if (cnl == NULL)
14082edd718bSreyk 		fatalx("invalid NAT lookup");
14092edd718bSreyk 
1410f8eb77d7Sthib 	if (con->se_out.ss.ss_family == AF_UNSPEC && cnl->in == -1 &&
141138426f8cSreyk 	    rlay->rl_conf.dstss.ss_family == AF_UNSPEC &&
1412416fa9c0Sreyk 	    TAILQ_EMPTY(&rlay->rl_tables)) {
14130be9d00aSbenno 		relay_close(con, "session NAT lookup failed", 1);
14142edd718bSreyk 		return;
14152edd718bSreyk 	}
14162edd718bSreyk 	if (cnl->in != -1) {
1417f8eb77d7Sthib 		bcopy(&cnl->rdst, &con->se_out.ss, sizeof(con->se_out.ss));
1418f8eb77d7Sthib 		con->se_out.port = cnl->rdport;
14192edd718bSreyk 	}
1420f8eb77d7Sthib 	free(con->se_cnl);
1421f8eb77d7Sthib 	con->se_cnl = NULL;
14222edd718bSreyk 
14232edd718bSreyk 	relay_session(con);
14242edd718bSreyk }
14252edd718bSreyk 
14262edd718bSreyk void
1427f4a6e73bSreyk relay_session(struct rsession *con)
14282edd718bSreyk {
142948240b8fSbluhm 	struct relay		*rlay = con->se_relay;
1430f8eb77d7Sthib 	struct ctl_relay_event	*in = &con->se_in, *out = &con->se_out;
14312edd718bSreyk 
14324a5b9b3eSreyk 	if (bcmp(&rlay->rl_conf.ss, &out->ss, sizeof(out->ss)) == 0 &&
14334a5b9b3eSreyk 	    out->port == rlay->rl_conf.port) {
143485a8c65fSreyk 		log_debug("%s: session %d: looping", __func__, con->se_id);
14350be9d00aSbenno 		relay_close(con, "session aborted", 1);
14362edd718bSreyk 		return;
14372edd718bSreyk 	}
14382edd718bSreyk 
14394a5b9b3eSreyk 	if (rlay->rl_conf.flags & F_UDP) {
14402380f4f2Sreyk 		/*
14412380f4f2Sreyk 		 * Call the UDP protocol-specific handler
14422380f4f2Sreyk 		 */
14434a5b9b3eSreyk 		if (rlay->rl_proto->request == NULL)
14443a50f0a9Sjmc 			fatalx("invalid UDP session");
14454a5b9b3eSreyk 		if ((*rlay->rl_proto->request)(con) == -1)
14460be9d00aSbenno 			relay_close(con, "session failed", 1);
14472380f4f2Sreyk 		return;
14482380f4f2Sreyk 	}
14492380f4f2Sreyk 
145085e5f500Sclaudio 	if ((rlay->rl_conf.flags & F_TLS) && (in->tls == NULL)) {
14517bb52228Sreyk 		relay_tls_transaction(con, in);
14522edd718bSreyk 		return;
14532edd718bSreyk 	}
14542edd718bSreyk 
1455a168ef1aSreyk 	if (rlay->rl_proto->type != RELAY_PROTO_HTTP) {
14563c03a838Sreyk 		if (rlay->rl_conf.fwdmode == FWD_TRANS)
14573c03a838Sreyk 			relay_bindanyreq(con, 0, IPPROTO_TCP);
14583c03a838Sreyk 		else if (relay_connect(con) == -1) {
14590be9d00aSbenno 			relay_close(con, "session failed", 1);
14602edd718bSreyk 			return;
14612edd718bSreyk 		}
14623c03a838Sreyk 	}
14632edd718bSreyk 
14642edd718bSreyk 	relay_input(con);
14652edd718bSreyk }
14662edd718bSreyk 
14673c03a838Sreyk void
1468f4a6e73bSreyk relay_bindanyreq(struct rsession *con, in_port_t port, int proto)
14693c03a838Sreyk {
1470325f6e14Sreyk 	struct privsep		*ps = env->sc_ps;
147148240b8fSbluhm 	struct relay		*rlay = con->se_relay;
14723c03a838Sreyk 	struct ctl_bindany	 bnd;
14733c03a838Sreyk 	struct timeval		 tv;
14743c03a838Sreyk 
14753c03a838Sreyk 	bzero(&bnd, sizeof(bnd));
14763c03a838Sreyk 	bnd.bnd_id = con->se_id;
1477325f6e14Sreyk 	bnd.bnd_proc = ps->ps_instance;
14783c03a838Sreyk 	bnd.bnd_port = port;
14793c03a838Sreyk 	bnd.bnd_proto = proto;
14803c03a838Sreyk 	bcopy(&con->se_in.ss, &bnd.bnd_ss, sizeof(bnd.bnd_ss));
1481c2c37c5dSreyk 	proc_compose(env->sc_ps, PROC_PARENT, IMSG_BINDANY,
1482c2c37c5dSreyk 	    &bnd, sizeof(bnd));
14833c03a838Sreyk 
14843c03a838Sreyk 	/* Schedule timeout */
14853c03a838Sreyk 	evtimer_set(&con->se_ev, relay_bindany, con);
14863c03a838Sreyk 	bcopy(&rlay->rl_conf.timeout, &tv, sizeof(tv));
14873c03a838Sreyk 	evtimer_add(&con->se_ev, &tv);
14883c03a838Sreyk }
14893c03a838Sreyk 
14903c03a838Sreyk void
14913c03a838Sreyk relay_bindany(int fd, short event, void *arg)
14923c03a838Sreyk {
149348240b8fSbluhm 	struct rsession	*con = arg;
14943c03a838Sreyk 
14953c03a838Sreyk 	if (con->se_bnds == -1) {
14960be9d00aSbenno 		relay_close(con, "bindany failed, invalid socket", 1);
14973c03a838Sreyk 		return;
14983c03a838Sreyk 	}
1499ff13fb34Sbluhm 	if (relay_connect(con) == -1)
15000be9d00aSbenno 		relay_close(con, "session failed", 1);
15013c03a838Sreyk }
15023c03a838Sreyk 
15032f89c21dSbenno void
150465f47834Sreyk relay_connect_state(struct rsession *con, struct ctl_relay_event *cre,
150565f47834Sreyk     enum relay_state new)
150665f47834Sreyk {
150765f47834Sreyk 	DPRINTF("%s: session %d: %s state %s -> %s",
150865f47834Sreyk 	    __func__, con->se_id,
150965f47834Sreyk 	    cre->dir == RELAY_DIR_REQUEST ? "accept" : "connect",
151065f47834Sreyk 	    relay_state(cre->state), relay_state(new));
151165f47834Sreyk 	cre->state = new;
151265f47834Sreyk }
151365f47834Sreyk 
151465f47834Sreyk void
15152f89c21dSbenno relay_connect_retry(int fd, short sig, void *arg)
15162f89c21dSbenno {
15172f89c21dSbenno 	struct timeval	 evtpause = { 1, 0 };
151848240b8fSbluhm 	struct rsession	*con = arg;
151948240b8fSbluhm 	struct relay	*rlay = con->se_relay;
15202f89c21dSbenno 	int		 bnds = -1;
15212f89c21dSbenno 
1522f20bb366Sclaudio 	if (relay_inflight < 1) {
1523efc39811Sbenno 		log_warnx("%s: no connection in flight", __func__);
1524f20bb366Sclaudio 		relay_inflight = 1;
1525f20bb366Sclaudio 	}
15262f89c21dSbenno 
15272f89c21dSbenno 	DPRINTF("%s: retry %d of %d, inflight: %d",__func__,
15282f89c21dSbenno 	    con->se_retrycount, con->se_retry, relay_inflight);
15292f89c21dSbenno 
15302f89c21dSbenno 	if (sig != EV_TIMEOUT)
1531efc39811Sbenno 		fatalx("%s: called without timeout", __func__);
15322f89c21dSbenno 
15332f89c21dSbenno 	evtimer_del(&con->se_inflightevt);
15342f89c21dSbenno 
15352f89c21dSbenno 	/*
15362f89c21dSbenno 	 * XXX we might want to check if the inbound socket is still
15372f89c21dSbenno 	 * available: client could have closed it while we were waiting?
15382f89c21dSbenno 	 */
15392f89c21dSbenno 
15402f89c21dSbenno 	DPRINTF("%s: got EV_TIMEOUT", __func__);
15412f89c21dSbenno 
15422f89c21dSbenno 	if (getdtablecount() + FD_RESERVE +
15432f89c21dSbenno 	    relay_inflight > getdtablesize()) {
15442f89c21dSbenno 		if (con->se_retrycount < RELAY_OUTOF_FD_RETRIES) {
15452f89c21dSbenno 			evtimer_add(&con->se_inflightevt, &evtpause);
15462f89c21dSbenno 			return;
15472f89c21dSbenno 		}
15482f89c21dSbenno 		/* we waited for RELAY_OUTOF_FD_RETRIES seconds, give up */
15492f89c21dSbenno 		event_add(&rlay->rl_ev, NULL);
15502f89c21dSbenno 		relay_abort_http(con, 504, "connection timed out", 0);
15512f89c21dSbenno 		return;
15522f89c21dSbenno 	}
15532f89c21dSbenno 
15542f89c21dSbenno 	if (rlay->rl_conf.fwdmode == FWD_TRANS) {
15552f89c21dSbenno 		/* con->se_bnds cannot be unset */
15562f89c21dSbenno 		bnds = con->se_bnds;
15572f89c21dSbenno 	}
15582f89c21dSbenno 
15592f89c21dSbenno  retry:
15602f89c21dSbenno 	if ((con->se_out.s = relay_socket_connect(&con->se_out.ss,
15612f89c21dSbenno 	    con->se_out.port, rlay->rl_proto, bnds)) == -1) {
15622f89c21dSbenno 		log_debug("%s: session %d: "
15632f89c21dSbenno 		    "forward failed: %s, %s", __func__,
15642f89c21dSbenno 		    con->se_id, strerror(errno),
15652f89c21dSbenno 		    con->se_retry ? "next retry" : "last retry");
15662f89c21dSbenno 
15672f89c21dSbenno 		con->se_retrycount++;
15682f89c21dSbenno 
15692f89c21dSbenno 		if ((errno == ENFILE || errno == EMFILE) &&
15702f89c21dSbenno 		    (con->se_retrycount < con->se_retry)) {
15712f89c21dSbenno 			event_del(&rlay->rl_ev);
15722f89c21dSbenno 			evtimer_add(&con->se_inflightevt, &evtpause);
15732f89c21dSbenno 			evtimer_add(&rlay->rl_evt, &evtpause);
15742f89c21dSbenno 			return;
15752f89c21dSbenno 		} else if (con->se_retrycount < con->se_retry)
15762f89c21dSbenno 			goto retry;
15772f89c21dSbenno 		event_add(&rlay->rl_ev, NULL);
15782f89c21dSbenno 		relay_abort_http(con, 504, "connect failed", 0);
15792f89c21dSbenno 		return;
15802f89c21dSbenno 	}
15812f89c21dSbenno 
1582f20bb366Sclaudio 	if (rlay->rl_conf.flags & F_TLSINSPECT)
158365f47834Sreyk 		relay_connect_state(con, &con->se_out, STATE_PRECONNECT);
1584f20bb366Sclaudio 	else
158565f47834Sreyk 		relay_connect_state(con, &con->se_out, STATE_CONNECTED);
15862f89c21dSbenno 	relay_inflight--;
15872f89c21dSbenno 	DPRINTF("%s: inflight decremented, now %d",__func__, relay_inflight);
15882f89c21dSbenno 
15892f89c21dSbenno 	event_add(&rlay->rl_ev, NULL);
15902f89c21dSbenno 
15912f89c21dSbenno 	if (errno == EINPROGRESS)
15922f89c21dSbenno 		event_again(&con->se_ev, con->se_out.s, EV_WRITE|EV_TIMEOUT,
15932f89c21dSbenno 		    relay_connected, &con->se_tv_start, &rlay->rl_conf.timeout,
15942f89c21dSbenno 		    con);
15952f89c21dSbenno 	else
15962f89c21dSbenno 		relay_connected(con->se_out.s, EV_WRITE, con);
15972f89c21dSbenno 
15982f89c21dSbenno 	return;
15992f89c21dSbenno }
16002f89c21dSbenno 
16012edd718bSreyk int
1602cf39ad79Sreyk relay_preconnect(struct rsession *con)
1603cf39ad79Sreyk {
1604f20bb366Sclaudio 	int rv;
1605f20bb366Sclaudio 
1606cf39ad79Sreyk 	log_debug("%s: session %d: process %d", __func__,
1607cf39ad79Sreyk 	    con->se_id, privsep_process);
1608f20bb366Sclaudio 	rv = relay_connect(con);
1609b40fcd2fSreyk 	if (con->se_out.state == STATE_CONNECTED)
161065f47834Sreyk 		relay_connect_state(con, &con->se_out, STATE_PRECONNECT);
1611f20bb366Sclaudio 	return (rv);
1612cf39ad79Sreyk }
1613cf39ad79Sreyk 
1614cf39ad79Sreyk int
1615f4a6e73bSreyk relay_connect(struct rsession *con)
16162edd718bSreyk {
161748240b8fSbluhm 	struct relay	*rlay = con->se_relay;
16182f89c21dSbenno 	struct timeval	 evtpause = { 1, 0 };
1619c564d004Sreyk 	int		 bnds = -1, ret;
16202edd718bSreyk 
1621f20bb366Sclaudio 	/* relay_connect should only be called once per relay */
1622b40fcd2fSreyk 	if (con->se_out.state == STATE_CONNECTED) {
1623f20bb366Sclaudio 		log_debug("%s: connect already called once", __func__);
1624f20bb366Sclaudio 		return (0);
1625f20bb366Sclaudio 	}
1626f20bb366Sclaudio 
1627cf39ad79Sreyk 	/* Connection is already established but session not active */
1628f20bb366Sclaudio 	if ((rlay->rl_conf.flags & F_TLSINSPECT) &&
1629b40fcd2fSreyk 	    con->se_out.state == STATE_PRECONNECT) {
163085e5f500Sclaudio 		if (con->se_out.tls == NULL) {
16317bb52228Sreyk 			log_debug("%s: tls connect failed", __func__);
1632cf39ad79Sreyk 			return (-1);
1633cf39ad79Sreyk 		}
1634cf39ad79Sreyk 		relay_connected(con->se_out.s, EV_WRITE, con);
163565f47834Sreyk 		relay_connect_state(con, &con->se_out, STATE_CONNECTED);
1636cf39ad79Sreyk 		return (0);
1637cf39ad79Sreyk 	}
1638cf39ad79Sreyk 
1639f20bb366Sclaudio 	if (relay_inflight < 1) {
1640f20bb366Sclaudio 		log_warnx("relay_connect: no connection in flight");
1641f20bb366Sclaudio 		relay_inflight = 1;
1642f20bb366Sclaudio 	}
16432f89c21dSbenno 
1644fd1841a3Sreyk 	getmonotime(&con->se_tv_start);
16452edd718bSreyk 
1646053cc50eSchrisz 	if (con->se_out.ss.ss_family == AF_UNSPEC &&
1647053cc50eSchrisz 	    !TAILQ_EMPTY(&rlay->rl_tables)) {
16482edd718bSreyk 		if (relay_from_table(con) != 0)
16492edd718bSreyk 			return (-1);
1650f8eb77d7Sthib 	} else if (con->se_out.ss.ss_family == AF_UNSPEC) {
16513c03a838Sreyk 		bcopy(&rlay->rl_conf.dstss, &con->se_out.ss,
16523c03a838Sreyk 		    sizeof(con->se_out.ss));
1653f8eb77d7Sthib 		con->se_out.port = rlay->rl_conf.dstport;
16542edd718bSreyk 	}
16552edd718bSreyk 
16563c03a838Sreyk 	if (rlay->rl_conf.fwdmode == FWD_TRANS) {
16573c03a838Sreyk 		if (con->se_bnds == -1) {
165885a8c65fSreyk 			log_debug("%s: could not bind any sock", __func__);
16593c03a838Sreyk 			return (-1);
16603c03a838Sreyk 		}
16613c03a838Sreyk 		bnds = con->se_bnds;
16623c03a838Sreyk 	}
16633c03a838Sreyk 
1664c564d004Sreyk 	/* Do the IPv4-to-IPv6 or IPv6-to-IPv4 translation if requested */
1665c564d004Sreyk 	if (rlay->rl_conf.dstaf.ss_family != AF_UNSPEC) {
1666c564d004Sreyk 		if (con->se_out.ss.ss_family == AF_INET &&
1667c564d004Sreyk 		    rlay->rl_conf.dstaf.ss_family == AF_INET6)
1668c564d004Sreyk 			ret = map4to6(&con->se_out.ss, &rlay->rl_conf.dstaf);
1669c564d004Sreyk 		else if (con->se_out.ss.ss_family == AF_INET6 &&
1670c564d004Sreyk 		    rlay->rl_conf.dstaf.ss_family == AF_INET)
1671c564d004Sreyk 			ret = map6to4(&con->se_out.ss);
1672c564d004Sreyk 		else
1673c564d004Sreyk 			ret = 0;
1674c564d004Sreyk 		if (ret != 0) {
167585a8c65fSreyk 			log_debug("%s: mapped to invalid address", __func__);
1676c564d004Sreyk 			return (-1);
1677c564d004Sreyk 		}
1678c564d004Sreyk 	}
1679c564d004Sreyk 
16802e44c71eSreyk  retry:
16813c03a838Sreyk 	if ((con->se_out.s = relay_socket_connect(&con->se_out.ss,
16823c03a838Sreyk 	    con->se_out.port, rlay->rl_proto, bnds)) == -1) {
16832f89c21dSbenno 		if (errno == ENFILE || errno == EMFILE) {
16842f89c21dSbenno 			log_debug("%s: session %d: forward failed: %s",
16852f89c21dSbenno 			    __func__, con->se_id, strerror(errno));
16862f89c21dSbenno 			evtimer_set(&con->se_inflightevt, relay_connect_retry,
16872f89c21dSbenno 			    con);
16882f89c21dSbenno 			event_del(&rlay->rl_ev);
16892f89c21dSbenno 			evtimer_add(&con->se_inflightevt, &evtpause);
16902f89c21dSbenno 			evtimer_add(&rlay->rl_evt, &evtpause);
1691b40fcd2fSreyk 
1692f20bb366Sclaudio 			/* this connect is pending */
169365f47834Sreyk 			relay_connect_state(con, &con->se_out, STATE_PENDING);
16942f89c21dSbenno 			return (0);
16952f89c21dSbenno 		} else {
1696f8eb77d7Sthib 			if (con->se_retry) {
1697f8eb77d7Sthib 				con->se_retry--;
169885a8c65fSreyk 				log_debug("%s: session %d: "
169985a8c65fSreyk 				    "forward failed: %s, %s", __func__,
1700f8eb77d7Sthib 				    con->se_id, strerror(errno),
1701820dd719Sreyk 				    con->se_retry ?
1702820dd719Sreyk 				    "next retry" : "last retry");
17032e44c71eSreyk 				goto retry;
17042e44c71eSreyk 			}
1705820dd719Sreyk 			log_debug("%s: session %d: forward failed: %s",
1706820dd719Sreyk 			    __func__, con->se_id, strerror(errno));
17072edd718bSreyk 			return (-1);
17082edd718bSreyk 		}
17092f89c21dSbenno 	}
17102f89c21dSbenno 
171165f47834Sreyk 	relay_connect_state(con, &con->se_out, STATE_CONNECTED);
17122f89c21dSbenno 	relay_inflight--;
17132f89c21dSbenno 	DPRINTF("%s: inflight decremented, now %d",__func__,
17142f89c21dSbenno 	    relay_inflight);
1715dae1a80bSreyk 
17162edd718bSreyk 	if (errno == EINPROGRESS)
1717f8eb77d7Sthib 		event_again(&con->se_ev, con->se_out.s, EV_WRITE|EV_TIMEOUT,
1718a9dd04c1Sbluhm 		    relay_connected, &con->se_tv_start, &rlay->rl_conf.timeout,
1719a9dd04c1Sbluhm 		    con);
17202edd718bSreyk 	else
1721f8eb77d7Sthib 		relay_connected(con->se_out.s, EV_WRITE, con);
17222edd718bSreyk 
17232edd718bSreyk 	return (0);
17242edd718bSreyk }
17252edd718bSreyk 
17262edd718bSreyk void
17270be9d00aSbenno relay_close(struct rsession *con, const char *msg, int err)
17282edd718bSreyk {
1729dae1a80bSreyk 	char		 ibuf[128], obuf[128], *ptr = NULL;
173048240b8fSbluhm 	struct relay	*rlay = con->se_relay;
1731cb8b0e56Sreyk 	struct protocol	*proto = rlay->rl_proto;
17322edd718bSreyk 
17334a5b9b3eSreyk 	SPLAY_REMOVE(session_tree, &rlay->rl_sessions, con);
17346e07057bSblambert 	relay_session_unpublish(con);
17352edd718bSreyk 
1736f8eb77d7Sthib 	event_del(&con->se_ev);
17372edd718bSreyk 
17380be9d00aSbenno 	if ((env->sc_conf.opts & (RELAYD_OPT_LOGCON|RELAYD_OPT_LOGCONERR)) &&
17390be9d00aSbenno 	    msg != NULL) {
174011aa8c65Sreyk 		bzero(&ibuf, sizeof(ibuf));
174111aa8c65Sreyk 		bzero(&obuf, sizeof(obuf));
1742f8eb77d7Sthib 		(void)print_host(&con->se_in.ss, ibuf, sizeof(ibuf));
1743f8eb77d7Sthib 		(void)print_host(&con->se_out.ss, obuf, sizeof(obuf));
1744f8eb77d7Sthib 		if (EVBUFFER_LENGTH(con->se_log) &&
1745536becf6Sbluhm 		    evbuffer_add_printf(con->se_log, "\r\n") != -1) {
1746536becf6Sbluhm 			ptr = evbuffer_readln(con->se_log, NULL,
1747536becf6Sbluhm 			    EVBUFFER_EOL_CRLF);
1748536becf6Sbluhm 		}
17490be9d00aSbenno 		if (err == 0 && (env->sc_conf.opts & RELAYD_OPT_LOGCON))
17500325c666Sreyk 			log_info("relay %s, "
1751cb8b0e56Sreyk 			    "session %d (%d active), %s, %s -> %s:%d, "
17520be9d00aSbenno 			    "%s%s%s", rlay->rl_conf.name, con->se_id,
17530be9d00aSbenno 			    relay_sessions, con->se_tag != 0 ?
17540be9d00aSbenno 			    tag_id2name(con->se_tag) : "0", ibuf, obuf,
17550be9d00aSbenno 			    ntohs(con->se_out.port), msg, ptr == NULL ?
17560be9d00aSbenno 			    "" : ",", ptr == NULL ? "" : ptr);
17570be9d00aSbenno 		if (err == 1 && (env->sc_conf.opts & RELAYD_OPT_LOGCONERR))
17580be9d00aSbenno 			log_warn("relay %s, "
17590be9d00aSbenno 			    "session %d (%d active), %s, %s -> %s:%d, "
17600be9d00aSbenno 			    "%s%s%s", rlay->rl_conf.name, con->se_id,
17610be9d00aSbenno 			    relay_sessions, con->se_tag != 0 ?
17620be9d00aSbenno 			    tag_id2name(con->se_tag) : "0", ibuf, obuf,
17630be9d00aSbenno 			    ntohs(con->se_out.port), msg, ptr == NULL ?
17640be9d00aSbenno 			    "" : ",", ptr == NULL ? "" : ptr);
17659492b359Sreyk 		free(ptr);
1766dae1a80bSreyk 	}
1767dae1a80bSreyk 
1768cb8b0e56Sreyk 	if (proto->close != NULL)
1769cb8b0e56Sreyk 		(*proto->close)(con);
1770cb8b0e56Sreyk 
17710dbc5f9eSreyk 	free(con->se_priv);
177213561b2dSclaudio 
177365f47834Sreyk 	relay_connect_state(con, &con->se_in, STATE_DONE);
177465f47834Sreyk 	if (relay_reset_event(con, &con->se_in)) {
17752f89c21dSbenno 		if (con->se_out.s == -1) {
17762f89c21dSbenno 			/*
17772f89c21dSbenno 			 * the output was never connected,
17782f89c21dSbenno 			 * thus this was an inflight session.
17792f89c21dSbenno 			 */
17802f89c21dSbenno 			relay_inflight--;
17812f89c21dSbenno 			log_debug("%s: sessions inflight decremented, now %d",
17822f89c21dSbenno 			    __func__, relay_inflight);
17832f89c21dSbenno 		}
17842f89c21dSbenno 	}
178513561b2dSclaudio 	if (con->se_in.output != NULL)
178613561b2dSclaudio 		evbuffer_free(con->se_in.output);
17872edd718bSreyk 
178865f47834Sreyk 	relay_connect_state(con, &con->se_out, STATE_DONE);
178965f47834Sreyk 	if (relay_reset_event(con, &con->se_out)) {
17906b61fee8Sderaadt 		/* Some file descriptors are available again. */
17916b61fee8Sderaadt 		if (evtimer_pending(&rlay->rl_evt, NULL)) {
17926b61fee8Sderaadt 			evtimer_del(&rlay->rl_evt);
17936b61fee8Sderaadt 			event_add(&rlay->rl_ev, NULL);
17946b61fee8Sderaadt 		}
17956b61fee8Sderaadt 	}
179613561b2dSclaudio 	if (con->se_out.output != NULL)
179713561b2dSclaudio 		evbuffer_free(con->se_out.output);
17986b61fee8Sderaadt 
1799f8eb77d7Sthib 	if (con->se_log != NULL)
1800f8eb77d7Sthib 		evbuffer_free(con->se_log);
1801dae1a80bSreyk 
1802f8eb77d7Sthib 	if (con->se_cnl != NULL) {
18032edd718bSreyk #if 0
18040325c666Sreyk 		proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_KILLSTATES, -1,
18052edd718bSreyk 		    cnl, sizeof(*cnl));
18062edd718bSreyk #endif
1807f8eb77d7Sthib 		free(con->se_cnl);
18082edd718bSreyk 	}
18092edd718bSreyk 
18102edd718bSreyk 	free(con);
18112edd718bSreyk 	relay_sessions--;
18122edd718bSreyk }
18132edd718bSreyk 
18140325c666Sreyk int
181565f47834Sreyk relay_reset_event(struct rsession *con, struct ctl_relay_event *cre)
181613561b2dSclaudio {
181713561b2dSclaudio 	int		 rv = 0;
181813561b2dSclaudio 
181965f47834Sreyk 	if (cre->state != STATE_DONE)
182065f47834Sreyk 		relay_connect_state(con, cre, STATE_CLOSED);
182165f47834Sreyk 	if (cre->bev != NULL) {
182265f47834Sreyk 		bufferevent_disable(cre->bev, EV_READ|EV_WRITE);
182313561b2dSclaudio 		bufferevent_free(cre->bev);
182465f47834Sreyk 	}
182513561b2dSclaudio 	if (cre->tls != NULL)
182613561b2dSclaudio 		tls_close(cre->tls);
182713561b2dSclaudio 	tls_free(cre->tls);
182813561b2dSclaudio 	tls_free(cre->tls_ctx);
182913561b2dSclaudio 	tls_config_free(cre->tls_cfg);
183013561b2dSclaudio 	free(cre->tlscert);
183113561b2dSclaudio 	if (cre->s != -1) {
183213561b2dSclaudio 		close(cre->s);
183313561b2dSclaudio 		rv = 1;
183413561b2dSclaudio 	}
183513561b2dSclaudio 	cre->bev = NULL;
183613561b2dSclaudio 	cre->tls = NULL;
183713561b2dSclaudio 	cre->tls_cfg = NULL;
183813561b2dSclaudio 	cre->tlscert = NULL;
183913561b2dSclaudio 	cre->s = -1;
184013561b2dSclaudio 
184113561b2dSclaudio 	return (rv);
184213561b2dSclaudio }
184313561b2dSclaudio 
184413561b2dSclaudio int
18450325c666Sreyk relay_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
18462edd718bSreyk {
18479d421a7aSreyk 	struct relay		*rlay;
184886b74329Sreyk 	struct rsession		*con, se;
18492edd718bSreyk 	struct ctl_natlook	 cnl;
18502edd718bSreyk 	struct timeval		 tv;
18512edd718bSreyk 	struct host		*host;
18522edd718bSreyk 	struct table		*table;
18532edd718bSreyk 	struct ctl_status	 st;
1854253ca2ceSreyk 	objid_t			 id;
185586b74329Sreyk 	int			 cid;
18562edd718bSreyk 
18570325c666Sreyk 	switch (imsg->hdr.type) {
1858253ca2ceSreyk 	case IMSG_HOST_DISABLE:
18590325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
1860253ca2ceSreyk 		if ((host = host_find(env, id)) == NULL)
1861efc39811Sbenno 			fatalx("%s: desynchronized", __func__);
186268b79041Spyr 		if ((table = table_find(env, host->conf.tableid)) ==
186368b79041Spyr 		    NULL)
1864efc39811Sbenno 			fatalx("%s: invalid table id", __func__);
1865253ca2ceSreyk 		if (host->up == HOST_UP)
1866253ca2ceSreyk 			table->up--;
1867253ca2ceSreyk 		host->flags |= F_DISABLE;
1868253ca2ceSreyk 		host->up = HOST_UNKNOWN;
1869253ca2ceSreyk 		break;
1870253ca2ceSreyk 	case IMSG_HOST_ENABLE:
18710325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
1872253ca2ceSreyk 		if ((host = host_find(env, id)) == NULL)
1873efc39811Sbenno 			fatalx("%s: desynchronized", __func__);
1874253ca2ceSreyk 		host->flags &= ~(F_DISABLE);
1875253ca2ceSreyk 		host->up = HOST_UNKNOWN;
1876253ca2ceSreyk 		break;
1877b850e197Sjsg 	case IMSG_TABLE_DISABLE:
18780325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
1879b850e197Sjsg 		if ((table = table_find(env, id)) == NULL)
1880efc39811Sbenno 			fatalx("%s: desynchronized", __func__);
1881b850e197Sjsg 		table->conf.flags |= F_DISABLE;
1882a1fa31afSjsg 		table->up = 0;
1883b850e197Sjsg 		TAILQ_FOREACH(host, &table->hosts, entry)
1884b850e197Sjsg 			host->up = HOST_UNKNOWN;
1885b850e197Sjsg 		break;
1886b850e197Sjsg 	case IMSG_TABLE_ENABLE:
18870325c666Sreyk 		memcpy(&id, imsg->data, sizeof(id));
1888b850e197Sjsg 		if ((table = table_find(env, id)) == NULL)
1889efc39811Sbenno 			fatalx("%s: desynchronized", __func__);
1890b850e197Sjsg 		table->conf.flags &= ~(F_DISABLE);
1891a1fa31afSjsg 		table->up = 0;
1892b850e197Sjsg 		TAILQ_FOREACH(host, &table->hosts, entry)
1893b850e197Sjsg 			host->up = HOST_UNKNOWN;
1894b850e197Sjsg 		break;
18952edd718bSreyk 	case IMSG_HOST_STATUS:
18960325c666Sreyk 		IMSG_SIZE_CHECK(imsg, &st);
18970325c666Sreyk 		memcpy(&st, imsg->data, sizeof(st));
18982edd718bSreyk 		if ((host = host_find(env, st.id)) == NULL)
1899efc39811Sbenno 			fatalx("%s: invalid host id", __func__);
1900253ca2ceSreyk 		if (host->flags & F_DISABLE)
1901253ca2ceSreyk 			break;
19022edd718bSreyk 		if (host->up == st.up) {
190385a8c65fSreyk 			log_debug("%s: host %d => %d", __func__,
190468b79041Spyr 			    host->conf.id, host->up);
1905efc39811Sbenno 			fatalx("%s: desynchronized", __func__);
19062edd718bSreyk 		}
19072edd718bSreyk 
190868b79041Spyr 		if ((table = table_find(env, host->conf.tableid))
190968b79041Spyr 		    == NULL)
1910efc39811Sbenno 			fatalx("%s: invalid table id", __func__);
19112edd718bSreyk 
191285a8c65fSreyk 		DPRINTF("%s: [%d] state %d for "
191327b47d92Sbenno 		    "host %u %s", __func__, p->p_ps->ps_instance, st.up,
191468b79041Spyr 		    host->conf.id, host->conf.name);
19152edd718bSreyk 
19162edd718bSreyk 		if ((st.up == HOST_UNKNOWN && host->up == HOST_DOWN) ||
19172edd718bSreyk 		    (st.up == HOST_DOWN && host->up == HOST_UNKNOWN)) {
19182edd718bSreyk 			host->up = st.up;
19192edd718bSreyk 			break;
19202edd718bSreyk 		}
19212edd718bSreyk 		if (st.up == HOST_UP)
19222edd718bSreyk 			table->up++;
19232edd718bSreyk 		else
19242edd718bSreyk 			table->up--;
19252edd718bSreyk 		host->up = st.up;
19262edd718bSreyk 		break;
19272edd718bSreyk 	case IMSG_NATLOOK:
19280325c666Sreyk 		bcopy(imsg->data, &cnl, sizeof(cnl));
19292edd718bSreyk 		if ((con = session_find(env, cnl.id)) == NULL ||
1930f8eb77d7Sthib 		    con->se_cnl == NULL) {
193185a8c65fSreyk 			log_debug("%s: session %d: expired",
193285a8c65fSreyk 			    __func__, cnl.id);
19332edd718bSreyk 			break;
19342edd718bSreyk 		}
1935f8eb77d7Sthib 		bcopy(&cnl, con->se_cnl, sizeof(*con->se_cnl));
1936f8eb77d7Sthib 		evtimer_del(&con->se_ev);
1937f8eb77d7Sthib 		evtimer_set(&con->se_ev, relay_natlook, con);
19382edd718bSreyk 		bzero(&tv, sizeof(tv));
1939f8eb77d7Sthib 		evtimer_add(&con->se_ev, &tv);
19402edd718bSreyk 		break;
19419d421a7aSreyk 	case IMSG_CTL_SESSION:
194286b74329Sreyk 		IMSG_SIZE_CHECK(imsg, &cid);
194386b74329Sreyk 		memcpy(&cid, imsg->data, sizeof(cid));
19440325c666Sreyk 		TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
19459d421a7aSreyk 			SPLAY_FOREACH(con, session_tree,
19460325c666Sreyk 			    &rlay->rl_sessions) {
194786b74329Sreyk 				memcpy(&se, con, sizeof(se));
194886b74329Sreyk 				se.se_cid = cid;
1949c2c37c5dSreyk 				proc_compose(env->sc_ps, p->p_id,
1950c2c37c5dSreyk 				    IMSG_CTL_SESSION, &se, sizeof(se));
19510325c666Sreyk 			}
19520325c666Sreyk 		}
1953c2c37c5dSreyk 		proc_compose(env->sc_ps, p->p_id, IMSG_CTL_END,
1954c2c37c5dSreyk 		    &cid, sizeof(cid));
1955f579a0f7Sjsg 		break;
19562edd718bSreyk 	default:
19570325c666Sreyk 		return (-1);
19582edd718bSreyk 	}
19592edd718bSreyk 
19600325c666Sreyk 	return (0);
19610325c666Sreyk }
19620325c666Sreyk 
19630325c666Sreyk int
19643d77879fSreyk relay_dispatch_ca(int fd, struct privsep_proc *p, struct imsg *imsg)
19653d77879fSreyk {
1966917ba595Sreyk 	switch (imsg->hdr.type) {
1967917ba595Sreyk 	case IMSG_CA_PRIVENC:
1968917ba595Sreyk 	case IMSG_CA_PRIVDEC:
1969917ba595Sreyk 		log_warnx("%s: priv%s result after timeout", __func__,
1970917ba595Sreyk 		    imsg->hdr.type == IMSG_CA_PRIVENC ? "enc" : "dec");
1971917ba595Sreyk 		return (0);
1972917ba595Sreyk 	}
1973917ba595Sreyk 
19743d77879fSreyk 	return (-1);
19753d77879fSreyk }
19763d77879fSreyk 
19773d77879fSreyk int
19780325c666Sreyk relay_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
19792edd718bSreyk {
198085e5f500Sclaudio 	struct relay_ticket_key	 ticket;
198185e5f500Sclaudio 	struct relay		*rlay;
1982f4a6e73bSreyk 	struct rsession		*con;
19833c03a838Sreyk 	struct timeval		 tv;
19843c03a838Sreyk 	objid_t			 id;
19852edd718bSreyk 
19860325c666Sreyk 	switch (imsg->hdr.type) {
19873c03a838Sreyk 	case IMSG_BINDANY:
19880325c666Sreyk 		bcopy(imsg->data, &id, sizeof(id));
19893c03a838Sreyk 		if ((con = session_find(env, id)) == NULL) {
199085a8c65fSreyk 			log_debug("%s: session %d: expired",
199185a8c65fSreyk 			    __func__, id);
19923c03a838Sreyk 			break;
19933c03a838Sreyk 		}
19943c03a838Sreyk 
19953c03a838Sreyk 		/* Will validate the result later */
1996a1416996Sclaudio 		con->se_bnds = imsg_get_fd(imsg);
19973c03a838Sreyk 
19983c03a838Sreyk 		evtimer_del(&con->se_ev);
19993c03a838Sreyk 		evtimer_set(&con->se_ev, relay_bindany, con);
20003c03a838Sreyk 		bzero(&tv, sizeof(tv));
20013c03a838Sreyk 		evtimer_add(&con->se_ev, &tv);
20023c03a838Sreyk 		break;
2003a2195becSreyk 	case IMSG_CFG_TABLE:
2004a2195becSreyk 		config_gettable(env, imsg);
2005a2195becSreyk 		break;
2006a2195becSreyk 	case IMSG_CFG_HOST:
2007a2195becSreyk 		config_gethost(env, imsg);
2008a2195becSreyk 		break;
2009a2195becSreyk 	case IMSG_CFG_PROTO:
2010a2195becSreyk 		config_getproto(env, imsg);
2011a2195becSreyk 		break;
2012cb8b0e56Sreyk 	case IMSG_CFG_RULE:
2013cb8b0e56Sreyk 		config_getrule(env, imsg);
2014cb8b0e56Sreyk 		break;
2015a2195becSreyk 	case IMSG_CFG_RELAY:
2016a2195becSreyk 		config_getrelay(env, imsg);
2017a2195becSreyk 		break;
2018416fa9c0Sreyk 	case IMSG_CFG_RELAY_TABLE:
2019416fa9c0Sreyk 		config_getrelaytable(env, imsg);
2020416fa9c0Sreyk 		break;
2021114ce177Sclaudio 	case IMSG_CFG_RELAY_FD:
2022114ce177Sclaudio 		config_getrelayfd(env, imsg);
2023114ce177Sclaudio 		break;
2024a2195becSreyk 	case IMSG_CFG_DONE:
2025a2195becSreyk 		config_getcfg(env, imsg);
20260dcba380Scamield 		break;
20270dcba380Scamield 	case IMSG_CTL_START:
2028a2195becSreyk 		relay_launch();
2029a2195becSreyk 		break;
2030a2195becSreyk 	case IMSG_CTL_RESET:
2031a2195becSreyk 		config_getreset(env, imsg);
2032a2195becSreyk 		break;
20339c908525Sclaudio 	case IMSG_TLSTICKET_REKEY:
203485e5f500Sclaudio 		IMSG_SIZE_CHECK(imsg, (&ticket));
203585e5f500Sclaudio 		memcpy(&env->sc_ticket, imsg->data, sizeof(env->sc_ticket));
203685e5f500Sclaudio 		TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
203785e5f500Sclaudio 			if (rlay->rl_conf.flags & F_TLS)
203885e5f500Sclaudio 				tls_config_add_ticket_key(rlay->rl_tls_cfg,
203985e5f500Sclaudio 				    env->sc_ticket.tt_keyrev,
204085e5f500Sclaudio 				    env->sc_ticket.tt_key,
204185e5f500Sclaudio 				    sizeof(env->sc_ticket.tt_key));
204285e5f500Sclaudio 		}
20439c908525Sclaudio 		break;
20442edd718bSreyk 	default:
20450325c666Sreyk 		return (-1);
20462edd718bSreyk 	}
20470325c666Sreyk 
20480325c666Sreyk 	return (0);
20492edd718bSreyk }
20502edd718bSreyk 
20516e07057bSblambert int
20526e07057bSblambert relay_dispatch_hce(int fd, struct privsep_proc *p, struct imsg *imsg)
20536e07057bSblambert {
20546e07057bSblambert 	switch (imsg->hdr.type) {
20556e07057bSblambert 	default:
20566e07057bSblambert 		break;
20576e07057bSblambert 	}
20586e07057bSblambert 
20596e07057bSblambert 	return (-1);
20606e07057bSblambert }
20616e07057bSblambert 
206285e5f500Sclaudio static int
206385e5f500Sclaudio relay_tls_ctx_create_proto(struct protocol *proto, struct tls_config *tls_cfg)
20642edd718bSreyk {
206585e5f500Sclaudio 	uint32_t		 protocols = 0;
2066dae1a80bSreyk 
2067f71e4cb7Skn 	/* Set the allowed TLS protocols */
206885e5f500Sclaudio 	if (proto->tlsflags & TLSFLAG_TLSV1_2)
206985e5f500Sclaudio 		protocols |= TLS_PROTOCOL_TLSv1_2;
2070b06d93a4Spvk 	if (proto->tlsflags & TLSFLAG_TLSV1_3)
2071b06d93a4Spvk 		protocols |= TLS_PROTOCOL_TLSv1_3;
207285e5f500Sclaudio 	if (tls_config_set_protocols(tls_cfg, protocols) == -1) {
207385e5f500Sclaudio 		log_warnx("could not set the TLS protocol: %s",
207485e5f500Sclaudio 		    tls_config_error(tls_cfg));
207585e5f500Sclaudio 		return (-1);
2076cc7b12f3Sreyk 	}
2077cc7b12f3Sreyk 
207885e5f500Sclaudio 	if (tls_config_set_ciphers(tls_cfg, proto->tlsciphers)) {
207985e5f500Sclaudio 		log_warnx("could not set the TLS cypers: %s",
208085e5f500Sclaudio 		    tls_config_error(tls_cfg));
208185e5f500Sclaudio 		return (-1);
20823675f6daSreyk 	}
20833675f6daSreyk 
208485e5f500Sclaudio 	if ((proto->tlsflags & TLSFLAG_CIPHER_SERVER_PREF) == 0)
208585e5f500Sclaudio 		tls_config_prefer_ciphers_client(tls_cfg);
208600ae3104Sreyk 
208731413d7eSreyk 	/*
20889c908525Sclaudio 	 * Set session ID context to a random value. It needs to be the
20893a50f0a9Sjmc 	 * same across all relay processes or session caching will fail.
209031413d7eSreyk 	 */
209185e5f500Sclaudio 	if (tls_config_set_session_id(tls_cfg, env->sc_conf.tls_sid,
209285e5f500Sclaudio 	    sizeof(env->sc_conf.tls_sid)) == -1) {
209385e5f500Sclaudio 		log_warnx("could not set the TLS session ID: %s",
209485e5f500Sclaudio 		    tls_config_error(tls_cfg));
209585e5f500Sclaudio 		return (-1);
209685e5f500Sclaudio 	}
209785e5f500Sclaudio 
209885e5f500Sclaudio 	/* Set callback for TLS session tickets if enabled */
209985e5f500Sclaudio 	if (proto->tickets == 1) {
210085e5f500Sclaudio 		/* set timeout to the ticket rekey time */
210185e5f500Sclaudio 		tls_config_set_session_lifetime(tls_cfg, TLS_SESSION_LIFETIME);
210285e5f500Sclaudio 
210385e5f500Sclaudio 		tls_config_add_ticket_key(tls_cfg,
210485e5f500Sclaudio 		    env->sc_ticket.tt_keyrev, env->sc_ticket.tt_key,
210585e5f500Sclaudio 		    sizeof(env->sc_ticket.tt_key));
210685e5f500Sclaudio 	}
210785e5f500Sclaudio 
2108353c00bcSclaudio 	if (tls_config_set_ecdhecurves(tls_cfg, proto->tlsecdhecurves) != 0) {
2109353c00bcSclaudio 		log_warnx("failed to set ecdhe curves %s: %s",
2110353c00bcSclaudio 		    proto->tlsecdhecurves, tls_config_error(tls_cfg));
211185e5f500Sclaudio 		return (-1);
211285e5f500Sclaudio 	}
211385e5f500Sclaudio 
211485e5f500Sclaudio 	if (tls_config_set_dheparams(tls_cfg, proto->tlsdhparams) != 0) {
211585e5f500Sclaudio 		log_warnx("failed to set dh params %s: %s",
211685e5f500Sclaudio 		    proto->tlsdhparams, tls_config_error(tls_cfg));
211785e5f500Sclaudio 		return (-1);
211885e5f500Sclaudio 	}
211985e5f500Sclaudio 
212085e5f500Sclaudio 	return (0);
212185e5f500Sclaudio }
212285e5f500Sclaudio 
212385e5f500Sclaudio /*
212485e5f500Sclaudio  * This function is not publicy exported because it is a hack until libtls
212585e5f500Sclaudio  * has a proper privsep setup
212685e5f500Sclaudio  */
2127a825c52dSeric void tls_config_use_fake_private_key(struct tls_config *config);
212885e5f500Sclaudio 
212985e5f500Sclaudio int
213085e5f500Sclaudio relay_tls_ctx_create(struct relay *rlay)
213185e5f500Sclaudio {
213285e5f500Sclaudio 	struct tls_config	*tls_cfg, *tls_client_cfg;
213385e5f500Sclaudio 	struct tls		*tls = NULL;
2134f2f4e153Sreyk 	struct relay_cert	*cert;
2135a825c52dSeric 	int			 keyfound = 0;
213631604b98Sreyk 	char			*buf = NULL, *cabuf = NULL, *ocspbuf = NULL;
213731604b98Sreyk 	off_t			 len = 0, calen = 0, ocsplen = 0;
213885e5f500Sclaudio 
213985e5f500Sclaudio 	if ((tls_cfg = tls_config_new()) == NULL) {
214085e5f500Sclaudio 		log_warnx("unable to allocate TLS config");
214185e5f500Sclaudio 		return (-1);
214285e5f500Sclaudio 	}
214385e5f500Sclaudio 	if ((tls_client_cfg = tls_config_new()) == NULL) {
214485e5f500Sclaudio 		log_warnx("unable to allocate TLS config");
214585e5f500Sclaudio 		return (-1);
214685e5f500Sclaudio 	}
214785e5f500Sclaudio 
214885e5f500Sclaudio 	if (relay_tls_ctx_create_proto(rlay->rl_proto, tls_cfg) == -1)
21492edd718bSreyk 		goto err;
215085e5f500Sclaudio 	if (relay_tls_ctx_create_proto(rlay->rl_proto, tls_client_cfg) == -1)
215185e5f500Sclaudio 		goto err;
215285e5f500Sclaudio 
215385e5f500Sclaudio 	/* Verify the server certificate if we have a CA chain */
215485e5f500Sclaudio 	if (rlay->rl_conf.flags & F_TLSCLIENT) {
215585e5f500Sclaudio 		/*
215685e5f500Sclaudio 		 * Currently relayd can't verify the name of certs and changing
215785e5f500Sclaudio 		 * this is non trivial. For now just disable name verification.
215885e5f500Sclaudio 		 */
215985e5f500Sclaudio 		tls_config_insecure_noverifyname(tls_client_cfg);
216085e5f500Sclaudio 
2161114ce177Sclaudio 		if (rlay->rl_tls_ca_fd != -1) {
2162*92388deeStb 			if ((buf = relay_load_fd(rlay->rl_tls_ca_fd, &len)) == NULL) {
2163114ce177Sclaudio 				log_warn("failed to read root certificates");
2164114ce177Sclaudio 				goto err;
2165114ce177Sclaudio 			}
2166f2f4e153Sreyk 			rlay->rl_tls_ca_fd = -1;
2167114ce177Sclaudio 
2168114ce177Sclaudio 			if (tls_config_set_ca_mem(tls_client_cfg, buf, len) !=
2169114ce177Sclaudio 			    0) {
217085e5f500Sclaudio 				log_warnx("failed to set root certificates: %s",
217185e5f500Sclaudio 				    tls_config_error(tls_client_cfg));
217285e5f500Sclaudio 				goto err;
217385e5f500Sclaudio 			}
2174114ce177Sclaudio 			purge_key(&buf, len);
217585e5f500Sclaudio 		} else {
217685e5f500Sclaudio 			/* No root cert available so disable the checking */
217785e5f500Sclaudio 			tls_config_insecure_noverifycert(tls_client_cfg);
217885e5f500Sclaudio 		}
217985e5f500Sclaudio 
218085e5f500Sclaudio 		rlay->rl_tls_client_cfg = tls_client_cfg;
218185e5f500Sclaudio 	}
218285e5f500Sclaudio 
218385e5f500Sclaudio 	if (rlay->rl_conf.flags & F_TLS) {
218485e5f500Sclaudio 		log_debug("%s: loading certificate", __func__);
218585e5f500Sclaudio 		/*
218685e5f500Sclaudio 		 * Use the public key as the "private" key - the secret key
218785e5f500Sclaudio 		 * parameters are hidden in an extra process that will be
2188f71e4cb7Skn 		 * contacted by the RSA engine.  The TLS library needs at
218985e5f500Sclaudio 		 * least the public key parameters in the current process.
219085e5f500Sclaudio 		 */
2191a825c52dSeric 		tls_config_use_fake_private_key(tls_cfg);
219285e5f500Sclaudio 
2193f2f4e153Sreyk 		TAILQ_FOREACH(cert, env->sc_certs, cert_entry) {
2194f2f4e153Sreyk 			if (cert->cert_relayid != rlay->rl_conf.id ||
2195f2f4e153Sreyk 			    cert->cert_fd == -1)
2196f2f4e153Sreyk 				continue;
2197f2f4e153Sreyk 			keyfound++;
2198f2f4e153Sreyk 
2199f2f4e153Sreyk 			if ((buf = relay_load_fd(cert->cert_fd,
2200f2f4e153Sreyk 			    &len)) == NULL) {
2201114ce177Sclaudio 				log_warn("failed to load tls certificate");
2202114ce177Sclaudio 				goto err;
2203114ce177Sclaudio 			}
2204f2f4e153Sreyk 			cert->cert_fd = -1;
2205114ce177Sclaudio 
220631604b98Sreyk 			if (cert->cert_ocsp_fd != -1 &&
220731604b98Sreyk 			    (ocspbuf = relay_load_fd(cert->cert_ocsp_fd,
220831604b98Sreyk 			    &ocsplen)) == NULL) {
220931604b98Sreyk 				log_warn("failed to load OCSP staplefile");
221031604b98Sreyk 				goto err;
221131604b98Sreyk 			}
221231604b98Sreyk 			if (ocsplen == 0)
221331604b98Sreyk 				purge_key(&ocspbuf, ocsplen);
221431604b98Sreyk 			cert->cert_ocsp_fd = -1;
221531604b98Sreyk 
2216f2f4e153Sreyk 			if (keyfound == 1 &&
2217f2f4e153Sreyk 			    tls_config_set_keypair_ocsp_mem(tls_cfg, buf, len,
2218a825c52dSeric 			    NULL, 0, ocspbuf, ocsplen) != 0) {
221985e5f500Sclaudio 				log_warnx("failed to set tls certificate: %s",
222085e5f500Sclaudio 				    tls_config_error(tls_cfg));
222185e5f500Sclaudio 				goto err;
222285e5f500Sclaudio 			}
222385e5f500Sclaudio 
2224f2f4e153Sreyk 			/* loading certificate public key */
2225f2f4e153Sreyk 			if (keyfound == 1 &&
2226f2f4e153Sreyk 			    !ssl_load_pkey(buf, len, NULL, &rlay->rl_tls_pkey))
2227f2f4e153Sreyk 				goto err;
2228f2f4e153Sreyk 
2229f2f4e153Sreyk 			if (tls_config_add_keypair_ocsp_mem(tls_cfg, buf, len,
2230a825c52dSeric 			    NULL, 0, ocspbuf, ocsplen) != 0) {
2231f2f4e153Sreyk 				log_warnx("failed to add tls certificate: %s",
2232f2f4e153Sreyk 				    tls_config_error(tls_cfg));
2233f2f4e153Sreyk 				goto err;
2234f2f4e153Sreyk 			}
2235f2f4e153Sreyk 
2236f2f4e153Sreyk 			purge_key(&buf, len);
223731604b98Sreyk 			purge_key(&ocspbuf, ocsplen);
2238f2f4e153Sreyk 		}
2239114ce177Sclaudio 
2240114ce177Sclaudio 		if (rlay->rl_tls_cacert_fd != -1) {
2241114ce177Sclaudio 			if ((cabuf = relay_load_fd(rlay->rl_tls_cacert_fd,
2242114ce177Sclaudio 			    &calen)) == NULL) {
2243114ce177Sclaudio 				log_warn("failed to load tls CA certificate");
2244114ce177Sclaudio 				goto err;
2245114ce177Sclaudio 			}
224685e5f500Sclaudio 			log_debug("%s: loading CA certificate", __func__);
2247114ce177Sclaudio 			if (!ssl_load_pkey(cabuf, calen,
224885e5f500Sclaudio 			    &rlay->rl_tls_cacertx509, &rlay->rl_tls_capkey))
224985e5f500Sclaudio 				goto err;
225085e5f500Sclaudio 		}
2251f2f4e153Sreyk 		rlay->rl_tls_cacert_fd = -1;
225285e5f500Sclaudio 
2253*92388deeStb 		if (rlay->rl_tls_client_ca_fd != -1) {
2254*92388deeStb 			if ((buf = relay_load_fd(rlay->rl_tls_client_ca_fd,
2255*92388deeStb 			    &len)) == NULL) {
2256*92388deeStb 				log_warn(
2257*92388deeStb 				    "failed to read tls client CA certificate");
2258*92388deeStb 				goto err;
2259*92388deeStb 			}
2260*92388deeStb 
2261*92388deeStb 			if (tls_config_set_ca_mem(tls_cfg, buf, len) != 0) {
2262*92388deeStb 				log_warnx(
2263*92388deeStb 				    "failed to set tls client CA cert: %s",
2264*92388deeStb 				    tls_config_error(tls_cfg));
2265*92388deeStb 				goto err;
2266*92388deeStb 			}
2267*92388deeStb 			purge_key(&buf, len);
2268*92388deeStb 
2269*92388deeStb 			tls_config_verify_client(tls_cfg);
2270*92388deeStb 		}
2271*92388deeStb 		rlay->rl_tls_client_ca_fd = -1;
2272*92388deeStb 
227385e5f500Sclaudio 		tls = tls_server();
227485e5f500Sclaudio 		if (tls == NULL) {
227585e5f500Sclaudio 			log_warnx("unable to allocate TLS context");
227685e5f500Sclaudio 			goto err;
227785e5f500Sclaudio 		}
227885e5f500Sclaudio 		if (tls_configure(tls, tls_cfg) == -1) {
227985e5f500Sclaudio 			log_warnx("could not configure the TLS context: %s",
228085e5f500Sclaudio 			    tls_error(tls));
228185e5f500Sclaudio 			tls_free(tls);
228285e5f500Sclaudio 			goto err;
228385e5f500Sclaudio 		}
228485e5f500Sclaudio 		rlay->rl_tls_cfg = tls_cfg;
228585e5f500Sclaudio 		rlay->rl_tls_ctx = tls;
2286114ce177Sclaudio 
2287114ce177Sclaudio 		purge_key(&cabuf, calen);
228885e5f500Sclaudio 	}
22892edd718bSreyk 
229085e5f500Sclaudio 	if (rlay->rl_tls_client_cfg == NULL)
229185e5f500Sclaudio 		tls_config_free(tls_client_cfg);
229285e5f500Sclaudio 	if (rlay->rl_tls_cfg == NULL)
229385e5f500Sclaudio 		tls_config_free(tls_cfg);
22942edd718bSreyk 
229585e5f500Sclaudio 	return (0);
22962edd718bSreyk  err:
229731604b98Sreyk 	purge_key(&ocspbuf, ocsplen);
2298114ce177Sclaudio 	purge_key(&cabuf, calen);
2299114ce177Sclaudio 	purge_key(&buf, len);
2300114ce177Sclaudio 
230185e5f500Sclaudio 	tls_config_free(tls_client_cfg);
230285e5f500Sclaudio 	tls_config_free(tls_cfg);
230385e5f500Sclaudio 	return (-1);
230485e5f500Sclaudio }
230585e5f500Sclaudio 
230685e5f500Sclaudio static struct tls *
230785e5f500Sclaudio relay_tls_inspect_create(struct relay *rlay, struct ctl_relay_event *cre)
230885e5f500Sclaudio {
230985e5f500Sclaudio 	struct tls_config	*tls_cfg;
2310b0d8ea5fSclaudio 	struct tls		*tls = NULL;
231185e5f500Sclaudio 
231285e5f500Sclaudio 	/* TLS inspection: use session-specific certificate */
231385e5f500Sclaudio 	if ((tls_cfg = tls_config_new()) == NULL) {
231485e5f500Sclaudio 		log_warnx("unable to allocate TLS config");
231585e5f500Sclaudio 		goto err;
231685e5f500Sclaudio 	}
2317950709d2Sbluhm 	if (relay_tls_ctx_create_proto(rlay->rl_proto, tls_cfg) == -1) {
2318950709d2Sbluhm 		/* error already printed */
231985e5f500Sclaudio 		goto err;
232085e5f500Sclaudio 	}
232185e5f500Sclaudio 
2322a825c52dSeric 	tls_config_use_fake_private_key(tls_cfg);
23239cb375a5Sclaudio 
232485e5f500Sclaudio 	if (tls_config_set_keypair_ocsp_mem(tls_cfg,
2325a825c52dSeric 	    cre->tlscert, cre->tlscert_len, NULL, 0, NULL, 0) != 0) {
232685e5f500Sclaudio 		log_warnx("failed to set tls certificate: %s",
232785e5f500Sclaudio 		    tls_config_error(tls_cfg));
232885e5f500Sclaudio 		goto err;
232985e5f500Sclaudio 	}
233085e5f500Sclaudio 
233185e5f500Sclaudio 	tls = tls_server();
233285e5f500Sclaudio 	if (tls == NULL) {
233385e5f500Sclaudio 		log_warnx("unable to allocate TLS context");
233485e5f500Sclaudio 		goto err;
233585e5f500Sclaudio 	}
233685e5f500Sclaudio 	if (tls_configure(tls, tls_cfg) == -1) {
233785e5f500Sclaudio 		log_warnx("could not configure the TLS context: %s",
233885e5f500Sclaudio 		    tls_error(tls));
233985e5f500Sclaudio 		tls_free(tls);
234085e5f500Sclaudio 		goto err;
234185e5f500Sclaudio 	}
234285e5f500Sclaudio 
234385e5f500Sclaudio 	cre->tls_cfg = tls_cfg;
2344b0d8ea5fSclaudio 	cre->tls_ctx = tls;
234585e5f500Sclaudio 	return (tls);
234685e5f500Sclaudio  err:
234785e5f500Sclaudio 	tls_config_free(tls_cfg);
23482edd718bSreyk 	return (NULL);
23492edd718bSreyk }
23502edd718bSreyk 
23512edd718bSreyk void
23527bb52228Sreyk relay_tls_transaction(struct rsession *con, struct ctl_relay_event *cre)
23532edd718bSreyk {
235448240b8fSbluhm 	struct relay		*rlay = con->se_relay;
235585e5f500Sclaudio 	struct tls		*tls_server;
235685e5f500Sclaudio 	const char		*errstr;
2357a9dd04c1Sbluhm 	u_int			 flag;
23582edd718bSreyk 
235933e8bb87Sreyk 	if (cre->dir == RELAY_DIR_REQUEST) {
23607bb52228Sreyk 		if (cre->tlscert != NULL)
236185e5f500Sclaudio 			tls_server = relay_tls_inspect_create(rlay, cre);
236285e5f500Sclaudio 		else
236385e5f500Sclaudio 			tls_server = rlay->rl_tls_ctx;
2364f8a1e24fSbluhm 		if (tls_server == NULL) {
2365f8a1e24fSbluhm 			errstr = "no TLS server context available";
2366f8a1e24fSbluhm 			goto err;
2367f8a1e24fSbluhm 		}
236885e5f500Sclaudio 
236985e5f500Sclaudio 		if (tls_accept_socket(tls_server, &cre->tls, cre->s) == -1) {
237085e5f500Sclaudio 			errstr = "could not accept the TLS connection";
237185e5f500Sclaudio 			goto err;
237285e5f500Sclaudio 		}
237385e5f500Sclaudio 		flag = EV_READ;
237433e8bb87Sreyk 	} else {
237585e5f500Sclaudio 		cre->tls = tls_client();
237685e5f500Sclaudio 		if (cre->tls == NULL ||
237785e5f500Sclaudio 		    tls_configure(cre->tls, rlay->rl_tls_client_cfg) == -1) {
237885e5f500Sclaudio 			errstr = "could not configure the TLS client context";
237985e5f500Sclaudio 			goto err;
238085e5f500Sclaudio 		}
238185e5f500Sclaudio 		if (tls_connect_socket(cre->tls, cre->s, NULL) == -1) {
238285e5f500Sclaudio 			errstr = "could not connect the TLS connection";
238385e5f500Sclaudio 			goto err;
238485e5f500Sclaudio 		}
2385a9dd04c1Sbluhm 		flag = EV_WRITE;
238633e8bb87Sreyk 	}
238733e8bb87Sreyk 
238885e5f500Sclaudio 	log_debug("%s: session %d: scheduling on %s", __func__, con->se_id,
2389a9dd04c1Sbluhm 	    (flag == EV_READ) ? "EV_READ" : "EV_WRITE");
239085e5f500Sclaudio 	event_again(&con->se_ev, cre->s, EV_TIMEOUT|flag, relay_tls_handshake,
239185e5f500Sclaudio 	    &con->se_tv_start, &rlay->rl_conf.timeout, cre);
23922edd718bSreyk 	return;
23932edd718bSreyk 
23942edd718bSreyk  err:
23950be9d00aSbenno 	relay_close(con, errstr, 1);
23962edd718bSreyk }
23972edd718bSreyk 
23982edd718bSreyk void
239985e5f500Sclaudio relay_tls_handshake(int fd, short event, void *arg)
24002edd718bSreyk {
240185e5f500Sclaudio 	struct ctl_relay_event	*cre = arg;
240285e5f500Sclaudio 	struct rsession		*con = cre->con;
240348240b8fSbluhm 	struct relay		*rlay = con->se_relay;
2404900cf880Sreyk 	int			 retry_flag = 0;
24052edd718bSreyk 	int			 ret;
24064b9f9c90Sbenno 	char			*msg;
24072edd718bSreyk 
24082edd718bSreyk 	if (event == EV_TIMEOUT) {
24090be9d00aSbenno 		relay_close(con, "TLS handshake timeout", 1);
24102edd718bSreyk 		return;
24112edd718bSreyk 	}
24122edd718bSreyk 
241385e5f500Sclaudio 	ret = tls_handshake(cre->tls);
2414dae1a80bSreyk 	if (ret == 0) {
2415dae1a80bSreyk #ifdef DEBUG
241685a8c65fSreyk 		log_info(
2417dae1a80bSreyk #else
241885a8c65fSreyk 		log_debug(
2419dae1a80bSreyk #endif
242085e5f500Sclaudio 		    "relay %s, tls session %d %s (%d active)",
242185e5f500Sclaudio 		    rlay->rl_conf.name, con->se_id,
242285e5f500Sclaudio 		    cre->dir == RELAY_DIR_REQUEST ? "established" : "connected",
242385e5f500Sclaudio 		    relay_sessions);
242485a8c65fSreyk 
242585e5f500Sclaudio 		if (cre->dir == RELAY_DIR_REQUEST) {
24262edd718bSreyk 			relay_session(con);
24272edd718bSreyk 			return;
24282edd718bSreyk 		}
24292edd718bSreyk 
24307bb52228Sreyk 		if (rlay->rl_conf.flags & F_TLSINSPECT) {
243185e5f500Sclaudio 			const uint8_t	*servercert;
243285e5f500Sclaudio 			size_t		 len;
243385e5f500Sclaudio 
243485e5f500Sclaudio 			servercert = tls_peer_cert_chain_pem(con->se_out.tls,
243585e5f500Sclaudio 			    &len);
243685e5f500Sclaudio 			if (servercert != NULL) {
243785e5f500Sclaudio 				con->se_in.tlscert = ssl_update_certificate(
243885e5f500Sclaudio 				    servercert, len,
24397bb52228Sreyk 				    rlay->rl_tls_pkey, rlay->rl_tls_capkey,
244085e5f500Sclaudio 				    rlay->rl_tls_cacertx509,
244185e5f500Sclaudio 				    &con->se_in.tlscert_len);
2442cf39ad79Sreyk 			} else
24437bb52228Sreyk 				con->se_in.tlscert = NULL;
24447bb52228Sreyk 			if (con->se_in.tlscert == NULL)
244585e5f500Sclaudio 				relay_close(con,
24460be9d00aSbenno 				    "could not create certificate", 1);
2447cf39ad79Sreyk 			else
2448cf39ad79Sreyk 				relay_session(con);
2449cf39ad79Sreyk 			return;
2450cf39ad79Sreyk 		}
245133e8bb87Sreyk 		relay_connected(fd, EV_WRITE, con);
245233e8bb87Sreyk 		return;
245385e5f500Sclaudio 	} else if (ret == TLS_WANT_POLLIN) {
245485e5f500Sclaudio 		retry_flag = EV_READ;
245585e5f500Sclaudio 	} else if (ret == TLS_WANT_POLLOUT) {
245685e5f500Sclaudio 		retry_flag = EV_WRITE;
245785e5f500Sclaudio 	} else {
24584b9f9c90Sbenno 		if (asprintf(&msg, "TLS handshake error: %s",
24594b9f9c90Sbenno 		    tls_error(cre->tls)) >= 0) {
24600be9d00aSbenno 			relay_close(con, msg, 1);
24614b9f9c90Sbenno 			free(msg);
24624b9f9c90Sbenno 		} else {
24630be9d00aSbenno 			relay_close(con, "TLS handshake error", 1);
24644b9f9c90Sbenno 		}
246585e5f500Sclaudio 		return;
246685e5f500Sclaudio 	}
246733e8bb87Sreyk 
246885a8c65fSreyk 	DPRINTF("%s: session %d: scheduling on %s", __func__, con->se_id,
246933e8bb87Sreyk 	    (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE");
247085e5f500Sclaudio 	event_again(&con->se_ev, fd, EV_TIMEOUT|retry_flag, relay_tls_handshake,
247185e5f500Sclaudio 	    &con->se_tv_start, &rlay->rl_conf.timeout, cre);
247233e8bb87Sreyk }
247333e8bb87Sreyk 
247433e8bb87Sreyk void
24757bb52228Sreyk relay_tls_connected(struct ctl_relay_event *cre)
24762edd718bSreyk {
24772edd718bSreyk 	/*
24782edd718bSreyk 	 * Hack libevent - we overwrite the internal bufferevent I/O
24797bb52228Sreyk 	 * functions to handle the TLS abstraction.
24802edd718bSreyk 	 */
248182569029Sclaudio 	event_del(&cre->bev->ev_read);
248282569029Sclaudio 	event_del(&cre->bev->ev_write);
248382569029Sclaudio 
24842edd718bSreyk 	event_set(&cre->bev->ev_read, cre->s, EV_READ,
24857bb52228Sreyk 	    relay_tls_readcb, cre->bev);
24862edd718bSreyk 	event_set(&cre->bev->ev_write, cre->s, EV_WRITE,
24877bb52228Sreyk 	    relay_tls_writecb, cre->bev);
24882edd718bSreyk }
24892edd718bSreyk 
24902edd718bSreyk void
24917bb52228Sreyk relay_tls_readcb(int fd, short event, void *arg)
24922edd718bSreyk {
2493900cf880Sreyk 	char			 rbuf[IBUF_READ_SIZE];
24942edd718bSreyk 	struct bufferevent	*bufev = arg;
249548240b8fSbluhm 	struct ctl_relay_event	*cre = bufev->cbarg;
24962edd718bSreyk 	short			 what = EVBUFFER_READ;
2497e39620e5Snicm 	int			 howmuch = IBUF_READ_SIZE;
249885e5f500Sclaudio 	ssize_t			 ret;
2499900cf880Sreyk 	size_t			 len;
25002edd718bSreyk 
25012edd718bSreyk 	if (event == EV_TIMEOUT) {
25022edd718bSreyk 		what |= EVBUFFER_TIMEOUT;
25032edd718bSreyk 		goto err;
25042edd718bSreyk 	}
25052edd718bSreyk 
25062edd718bSreyk 	if (bufev->wm_read.high != 0)
2507e2318a52Sderaadt 		howmuch = MINIMUM(sizeof(rbuf), bufev->wm_read.high);
25082edd718bSreyk 
250985e5f500Sclaudio 	ret = tls_read(cre->tls, rbuf, howmuch);
251085e5f500Sclaudio 	if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) {
25112edd718bSreyk 		goto retry;
2512df69c215Sderaadt 	} else if (ret == -1) {
25132edd718bSreyk 		what |= EVBUFFER_ERROR;
25142edd718bSreyk 		goto err;
25152edd718bSreyk 	}
251685e5f500Sclaudio 	len = ret;
251785e5f500Sclaudio 
251885e5f500Sclaudio 	if (len == 0) {
251985e5f500Sclaudio 		what |= EVBUFFER_EOF;
252085e5f500Sclaudio 		goto err;
25212edd718bSreyk 	}
25222edd718bSreyk 
25232edd718bSreyk 	if (evbuffer_add(bufev->input, rbuf, ret) == -1) {
25242edd718bSreyk 		what |= EVBUFFER_ERROR;
25252edd718bSreyk 		goto err;
25262edd718bSreyk 	}
25272edd718bSreyk 
25282edd718bSreyk 	relay_bufferevent_add(&bufev->ev_read, bufev->timeout_read);
25292edd718bSreyk 
25302edd718bSreyk 	len = EVBUFFER_LENGTH(bufev->input);
25312edd718bSreyk 	if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
25322edd718bSreyk 		return;
25332edd718bSreyk 	if (bufev->wm_read.high != 0 && len > bufev->wm_read.high) {
25342edd718bSreyk 		struct evbuffer *buf = bufev->input;
25352edd718bSreyk 		event_del(&bufev->ev_read);
25362edd718bSreyk 		evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev);
25372edd718bSreyk 		return;
25382edd718bSreyk 	}
25392edd718bSreyk 
25402edd718bSreyk 	if (bufev->readcb != NULL)
25412edd718bSreyk 		(*bufev->readcb)(bufev, bufev->cbarg);
25422edd718bSreyk 	return;
25432edd718bSreyk 
25442edd718bSreyk  retry:
25452edd718bSreyk 	relay_bufferevent_add(&bufev->ev_read, bufev->timeout_read);
25462edd718bSreyk 	return;
25472edd718bSreyk 
25482edd718bSreyk  err:
25492edd718bSreyk 	(*bufev->errorcb)(bufev, what, bufev->cbarg);
25502edd718bSreyk }
25512edd718bSreyk 
25522edd718bSreyk void
25537bb52228Sreyk relay_tls_writecb(int fd, short event, void *arg)
25542edd718bSreyk {
25552edd718bSreyk 	struct bufferevent	*bufev = arg;
255648240b8fSbluhm 	struct ctl_relay_event	*cre = bufev->cbarg;
255785e5f500Sclaudio 	ssize_t			 ret;
255885e5f500Sclaudio 	size_t			 len;
25592edd718bSreyk 	short			 what = EVBUFFER_WRITE;
25602edd718bSreyk 
25612edd718bSreyk 	if (event == EV_TIMEOUT) {
25622edd718bSreyk 		what |= EVBUFFER_TIMEOUT;
25632edd718bSreyk 		goto err;
25642edd718bSreyk 	}
25652edd718bSreyk 
25662edd718bSreyk 	if (EVBUFFER_LENGTH(bufev->output)) {
256785e5f500Sclaudio 		ret = tls_write(cre->tls, EVBUFFER_DATA(bufev->output),
256885e5f500Sclaudio 		    EVBUFFER_LENGTH(bufev->output));
256985e5f500Sclaudio 		if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) {
257085e5f500Sclaudio 			goto retry;
2571df69c215Sderaadt 		} else if (ret == -1) {
25722edd718bSreyk 			what |= EVBUFFER_ERROR;
25732edd718bSreyk 			goto err;
25742edd718bSreyk 		}
257585e5f500Sclaudio 		len = ret;
257685e5f500Sclaudio 		evbuffer_drain(bufev->output, len);
25772edd718bSreyk 	}
25782edd718bSreyk 
25792edd718bSreyk 	if (EVBUFFER_LENGTH(bufev->output) != 0)
25802edd718bSreyk 		relay_bufferevent_add(&bufev->ev_write, bufev->timeout_write);
25812edd718bSreyk 
25822edd718bSreyk 	if (bufev->writecb != NULL &&
25832edd718bSreyk 	    EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
25842edd718bSreyk 		(*bufev->writecb)(bufev, bufev->cbarg);
25852edd718bSreyk 	return;
25862edd718bSreyk 
25872edd718bSreyk  retry:
25882edd718bSreyk 	relay_bufferevent_add(&bufev->ev_write, bufev->timeout_write);
25892edd718bSreyk 	return;
25902edd718bSreyk 
25912edd718bSreyk  err:
25922edd718bSreyk 	(*bufev->errorcb)(bufev, what, bufev->cbarg);
25932edd718bSreyk }
25942edd718bSreyk 
25952edd718bSreyk int
25962edd718bSreyk relay_bufferevent_add(struct event *ev, int timeout)
25972edd718bSreyk {
25982edd718bSreyk 	struct timeval tv, *ptv = NULL;
25992edd718bSreyk 
26002edd718bSreyk 	if (timeout) {
26012edd718bSreyk 		timerclear(&tv);
26022edd718bSreyk 		tv.tv_sec = timeout;
26032edd718bSreyk 		ptv = &tv;
26042edd718bSreyk 	}
26052edd718bSreyk 
26062edd718bSreyk 	return (event_add(ev, ptv));
26072edd718bSreyk }
26082edd718bSreyk 
26092edd718bSreyk #ifdef notyet
26102edd718bSreyk int
26112edd718bSreyk relay_bufferevent_printf(struct ctl_relay_event *cre, const char *fmt, ...)
26122edd718bSreyk {
26132edd718bSreyk 	int	ret;
26142edd718bSreyk 	va_list	ap;
26152edd718bSreyk 
26162edd718bSreyk 	va_start(ap, fmt);
26172edd718bSreyk 	ret = evbuffer_add_vprintf(cre->output, fmt, ap);
26182edd718bSreyk 	va_end(ap);
26192edd718bSreyk 
26202edd718bSreyk 	if (cre->bev != NULL &&
26212edd718bSreyk 	    ret != -1 && EVBUFFER_LENGTH(cre->output) > 0 &&
26222edd718bSreyk 	    (cre->bev->enabled & EV_WRITE))
26232edd718bSreyk 		bufferevent_enable(cre->bev, EV_WRITE);
26242edd718bSreyk 
26252edd718bSreyk 	return (ret);
26262edd718bSreyk }
26272edd718bSreyk #endif
26282edd718bSreyk 
26292edd718bSreyk int
2630cb8b0e56Sreyk relay_bufferevent_print(struct ctl_relay_event *cre, const char *str)
26312edd718bSreyk {
26322edd718bSreyk 	if (cre->bev == NULL)
26332edd718bSreyk 		return (evbuffer_add(cre->output, str, strlen(str)));
26342edd718bSreyk 	return (bufferevent_write(cre->bev, str, strlen(str)));
26352edd718bSreyk }
26362edd718bSreyk 
26372edd718bSreyk int
26384a26497dSreyk relay_bufferevent_write_buffer(struct ctl_relay_event *cre,
26394a26497dSreyk     struct evbuffer *buf)
26402edd718bSreyk {
26412edd718bSreyk 	if (cre->bev == NULL)
26422edd718bSreyk 		return (evbuffer_add_buffer(cre->output, buf));
26432edd718bSreyk 	return (bufferevent_write_buffer(cre->bev, buf));
26442edd718bSreyk }
26452edd718bSreyk 
26462edd718bSreyk int
264769245c99Sreyk relay_bufferevent_write_chunk(struct ctl_relay_event *cre,
264869245c99Sreyk     struct evbuffer *buf, size_t size)
264969245c99Sreyk {
265069245c99Sreyk 	int ret;
26515b9b6c6bSnicm 	ret = relay_bufferevent_write(cre, EVBUFFER_DATA(buf), size);
265269245c99Sreyk 	if (ret != -1)
265369245c99Sreyk 		evbuffer_drain(buf, size);
265469245c99Sreyk 	return (ret);
265569245c99Sreyk }
265669245c99Sreyk 
265769245c99Sreyk int
26582edd718bSreyk relay_bufferevent_write(struct ctl_relay_event *cre, void *data, size_t size)
26592edd718bSreyk {
26602edd718bSreyk 	if (cre->bev == NULL)
26612edd718bSreyk 		return (evbuffer_add(cre->output, data, size));
26622edd718bSreyk 	return (bufferevent_write(cre->bev, data, size));
26632edd718bSreyk }
26642edd718bSreyk 
26652380f4f2Sreyk int
26662380f4f2Sreyk relay_cmp_af(struct sockaddr_storage *a, struct sockaddr_storage *b)
26672380f4f2Sreyk {
26687674c2baSreyk 	int			ret = -1;
26692380f4f2Sreyk 	struct sockaddr_in	ia, ib;
26702380f4f2Sreyk 	struct sockaddr_in6	ia6, ib6;
26712380f4f2Sreyk 
26722380f4f2Sreyk 	switch (a->ss_family) {
26732380f4f2Sreyk 	case AF_INET:
26742380f4f2Sreyk 		bcopy(a, &ia, sizeof(struct sockaddr_in));
26752380f4f2Sreyk 		bcopy(b, &ib, sizeof(struct sockaddr_in));
26762380f4f2Sreyk 
26777674c2baSreyk 		ret = memcmp(&ia.sin_addr, &ib.sin_addr,
26787674c2baSreyk 		    sizeof(ia.sin_addr));
26797674c2baSreyk 		if (ret == 0)
26807674c2baSreyk 			ret = memcmp(&ia.sin_port, &ib.sin_port,
26817674c2baSreyk 			    sizeof(ia.sin_port));
26822380f4f2Sreyk 		break;
26832380f4f2Sreyk 	case AF_INET6:
26842380f4f2Sreyk 		bcopy(a, &ia6, sizeof(struct sockaddr_in6));
26852380f4f2Sreyk 		bcopy(b, &ib6, sizeof(struct sockaddr_in6));
26862380f4f2Sreyk 
26877674c2baSreyk 		ret = memcmp(&ia6.sin6_addr, &ib6.sin6_addr,
26887674c2baSreyk 		    sizeof(ia6.sin6_addr));
26897674c2baSreyk 		if (ret == 0)
26907674c2baSreyk 			ret = memcmp(&ia6.sin6_port, &ib6.sin6_port,
26917674c2baSreyk 			    sizeof(ia6.sin6_port));
26922380f4f2Sreyk 		break;
26932380f4f2Sreyk 	default:
26947674c2baSreyk 		break;
26952380f4f2Sreyk 	}
26967674c2baSreyk 
26977674c2baSreyk 	return (ret);
26982380f4f2Sreyk }
26992380f4f2Sreyk 
27006b52fad8Sreyk int
2701f4a6e73bSreyk relay_session_cmp(struct rsession *a, struct rsession *b)
27026b52fad8Sreyk {
270348240b8fSbluhm 	struct relay	*rlay = b->se_relay;
27044a5b9b3eSreyk 	struct protocol	*proto = rlay->rl_proto;
27056b52fad8Sreyk 
2706ff46f440Sreyk 	if (proto != NULL && proto->cmp != NULL)
27076b52fad8Sreyk 		return ((*proto->cmp)(a, b));
27086b52fad8Sreyk 
2709f8eb77d7Sthib 	return ((int)a->se_id - b->se_id);
27106b52fad8Sreyk }
27116b52fad8Sreyk 
2712c84d6099Sbenno void
2713c84d6099Sbenno relay_log(struct rsession *con, char *msg)
2714c84d6099Sbenno {
2715c84d6099Sbenno 	if (con->se_haslog && con->se_log != NULL) {
2716c84d6099Sbenno 		evbuffer_add(con->se_log, msg, strlen(msg));
2717c84d6099Sbenno 	}
2718c84d6099Sbenno }
2719c84d6099Sbenno 
2720f4a6e73bSreyk SPLAY_GENERATE(session_tree, rsession, se_nodes, relay_session_cmp);
2721