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