File: [local] / src / usr.sbin / relayd / relayd.c (download)
Revision 1.191, Sun Jun 25 08:07:38 2023 UTC (11 months, 2 weeks ago) by op
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, HEAD Changes since 1.190: +1 -4 lines
remove ssl_init()
it's a noop; nowadays both LibreSSL and OpenSSL libcrypto and libssl
initialize themselves automatically before doing anything.
ok tb
|
/* $OpenBSD: relayd.c,v 1.191 2023/06/25 08:07:38 op Exp $ */
/*
* Copyright (c) 2007 - 2016 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <getopt.h>
#include <fnmatch.h>
#include <syslog.h>
#include <err.h>
#include <errno.h>
#include <event.h>
#include <unistd.h>
#include <ctype.h>
#include <pwd.h>
#include <sha1.h>
#include <md5.h>
#include <tls.h>
#include "relayd.h"
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
__dead void usage(void);
int parent_configure(struct relayd *);
void parent_configure_done(struct relayd *);
void parent_reload(struct relayd *, u_int, const char *);
void parent_sig_handler(int, short, void *);
void parent_shutdown(struct relayd *);
int parent_dispatch_pfe(int, struct privsep_proc *, struct imsg *);
int parent_dispatch_hce(int, struct privsep_proc *, struct imsg *);
int parent_dispatch_relay(int, struct privsep_proc *,
struct imsg *);
int parent_dispatch_ca(int, struct privsep_proc *,
struct imsg *);
int bindany(struct ctl_bindany *);
void parent_tls_ticket_rekey(int, short, void *);
struct relayd *relayd_env;
static struct privsep_proc procs[] = {
{ "pfe", PROC_PFE, parent_dispatch_pfe, pfe },
{ "hce", PROC_HCE, parent_dispatch_hce, hce },
{ "relay", PROC_RELAY, parent_dispatch_relay, relay },
{ "ca", PROC_CA, parent_dispatch_ca, ca }
};
enum privsep_procid privsep_process;
void
parent_sig_handler(int sig, short event, void *arg)
{
struct privsep *ps = arg;
switch (sig) {
case SIGTERM:
case SIGINT:
parent_shutdown(ps->ps_env);
break;
case SIGHUP:
log_info("%s: reload requested with SIGHUP", __func__);
/*
* This is safe because libevent uses async signal handlers
* that run in the event loop and not in signal context.
*/
parent_reload(ps->ps_env, CONFIG_RELOAD, NULL);
break;
case SIGPIPE:
case SIGUSR1:
/* ignore */
break;
default:
fatalx("unexpected signal");
}
}
__dead void
usage(void)
{
extern char *__progname;
fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
__progname);
exit(1);
}
int
main(int argc, char *argv[])
{
int c;
int debug = 0, verbose = 0;
u_int32_t opts = 0;
struct relayd *env;
struct privsep *ps;
const char *conffile = CONF_FILE;
enum privsep_procid proc_id = PROC_PARENT;
int proc_instance = 0;
const char *errp, *title = NULL;
int argc0 = argc;
while ((c = getopt(argc, argv, "dD:nI:P:f:v")) != -1) {
switch (c) {
case 'd':
debug = 2;
break;
case 'D':
if (cmdline_symset(optarg) < 0)
log_warnx("could not parse macro definition %s",
optarg);
break;
case 'n':
debug = 2;
opts |= RELAYD_OPT_NOACTION;
break;
case 'f':
conffile = optarg;
break;
case 'v':
verbose++;
opts |= RELAYD_OPT_VERBOSE;
break;
case 'P':
title = optarg;
proc_id = proc_getid(procs, nitems(procs), title);
if (proc_id == PROC_MAX)
fatalx("invalid process name");
break;
case 'I':
proc_instance = strtonum(optarg, 0,
PROC_MAX_INSTANCES, &errp);
if (errp)
fatalx("invalid process instance");
break;
default:
usage();
}
}
/* log to stderr until daemonized */
log_init(debug ? debug : 1, LOG_DAEMON);
argc -= optind;
if (argc > 0)
usage();
if ((env = calloc(1, sizeof(*env))) == NULL ||
(ps = calloc(1, sizeof(*ps))) == NULL)
exit(1);
relayd_env = env;
env->sc_ps = ps;
ps->ps_env = env;
TAILQ_INIT(&ps->ps_rcsocks);
env->sc_conffile = conffile;
env->sc_conf.opts = opts;
TAILQ_INIT(&env->sc_hosts);
TAILQ_INIT(&env->sc_sessions);
env->sc_rtable = getrtable();
/* initialize the TLS session id to a random key for all relay procs */
arc4random_buf(env->sc_conf.tls_sid, sizeof(env->sc_conf.tls_sid));
if (parse_config(env->sc_conffile, env) == -1)
exit(1);
if (debug)
env->sc_conf.opts |= RELAYD_OPT_LOGUPDATE;
if (geteuid())
errx(1, "need root privileges");
if ((ps->ps_pw = getpwnam(RELAYD_USER)) == NULL)
errx(1, "unknown user %s", RELAYD_USER);
log_init(debug, LOG_DAEMON);
log_setverbose(verbose);
if (env->sc_conf.opts & RELAYD_OPT_NOACTION)
ps->ps_noaction = 1;
ps->ps_instances[PROC_RELAY] = env->sc_conf.prefork_relay;
ps->ps_instances[PROC_CA] = env->sc_conf.prefork_relay;
ps->ps_instance = proc_instance;
if (title != NULL)
ps->ps_title[proc_id] = title;
/* only the parent returns */
proc_init(ps, procs, nitems(procs), debug, argc0, argv, proc_id);
log_procinit("parent");
if (ps->ps_noaction == 0)
log_info("startup");
if (unveil("/", "rx") == -1)
err(1, "unveil /");
if (unveil(NULL, NULL) == -1)
err(1, "unveil");
event_init();
signal_set(&ps->ps_evsigint, SIGINT, parent_sig_handler, ps);
signal_set(&ps->ps_evsigterm, SIGTERM, parent_sig_handler, ps);
signal_set(&ps->ps_evsighup, SIGHUP, parent_sig_handler, ps);
signal_set(&ps->ps_evsigpipe, SIGPIPE, parent_sig_handler, ps);
signal_set(&ps->ps_evsigusr1, SIGUSR1, parent_sig_handler, ps);
signal_add(&ps->ps_evsigint, NULL);
signal_add(&ps->ps_evsigterm, NULL);
signal_add(&ps->ps_evsighup, NULL);
signal_add(&ps->ps_evsigpipe, NULL);
signal_add(&ps->ps_evsigusr1, NULL);
proc_connect(ps);
relay_http(NULL);
if (load_config(env->sc_conffile, env) == -1) {
proc_kill(env->sc_ps);
exit(1);
}
if (env->sc_conf.opts & RELAYD_OPT_NOACTION) {
fprintf(stderr, "configuration OK\n");
proc_kill(env->sc_ps);
exit(0);
}
/* rekey the TLS tickets before pushing the config */
parent_tls_ticket_rekey(0, 0, env);
if (parent_configure(env) == -1)
fatalx("configuration failed");
init_routes(env);
event_dispatch();
parent_shutdown(env);
/* NOTREACHED */
return (0);
}
int
parent_configure(struct relayd *env)
{
struct table *tb;
struct rdr *rdr;
struct router *rt;
struct protocol *proto;
struct relay *rlay;
int id;
int ret = -1;
TAILQ_FOREACH(tb, env->sc_tables, entry)
config_settable(env, tb);
TAILQ_FOREACH(rdr, env->sc_rdrs, entry)
config_setrdr(env, rdr);
TAILQ_FOREACH(rt, env->sc_rts, rt_entry)
config_setrt(env, rt);
TAILQ_FOREACH(proto, env->sc_protos, entry)
config_setproto(env, proto);
TAILQ_FOREACH(proto, env->sc_protos, entry)
config_setrule(env, proto);
TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
/* Check for TLS Inspection */
if ((rlay->rl_conf.flags & (F_TLS|F_TLSCLIENT)) ==
(F_TLS|F_TLSCLIENT) && rlay->rl_tls_cacert_fd != -1)
rlay->rl_conf.flags |= F_TLSINSPECT;
config_setrelay(env, rlay);
}
/* HCE, PFE, CA and the relays need to reload their config. */
env->sc_reload = 2 + (2 * env->sc_conf.prefork_relay);
for (id = 0; id < PROC_MAX; id++) {
if (id == privsep_process)
continue;
proc_compose_imsg(env->sc_ps, id, -1, IMSG_CFG_DONE, -1,
-1, &env->sc_conf, sizeof(env->sc_conf));
}
ret = 0;
config_purge(env, CONFIG_ALL & ~CONFIG_RELAYS);
return (ret);
}
void
parent_reload(struct relayd *env, u_int reset, const char *filename)
{
if (env->sc_reload) {
log_debug("%s: already in progress: %d pending",
__func__, env->sc_reload);
return;
}
/* Switch back to the default config file */
if (filename == NULL || *filename == '\0')
filename = env->sc_conffile;
log_debug("%s: level %d config file %s", __func__, reset, filename);
config_purge(env, CONFIG_ALL);
if (reset == CONFIG_RELOAD) {
if (load_config(filename, env) == -1) {
log_debug("%s: failed to load config file %s",
__func__, filename);
}
config_setreset(env, CONFIG_ALL);
if (parent_configure(env) == -1) {
log_debug("%s: failed to commit config from %s",
__func__, filename);
}
} else
config_setreset(env, reset);
}
void
parent_configure_done(struct relayd *env)
{
int id;
if (env->sc_reload == 0) {
log_warnx("%s: configuration already finished", __func__);
return;
}
env->sc_reload--;
if (env->sc_reload == 0) {
for (id = 0; id < PROC_MAX; id++) {
if (id == privsep_process)
continue;
proc_compose(env->sc_ps, id, IMSG_CTL_START, NULL, 0);
}
}
}
void
parent_shutdown(struct relayd *env)
{
config_purge(env, CONFIG_ALL);
proc_kill(env->sc_ps);
control_cleanup(&env->sc_ps->ps_csock);
carp_demote_shutdown();
free(env->sc_ps);
free(env);
log_info("parent terminating, pid %d", getpid());
exit(0);
}
int
parent_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
{
struct privsep *ps = p->p_ps;
struct relayd *env = ps->ps_env;
struct ctl_demote demote;
struct ctl_netroute crt;
u_int v;
char *str = NULL;
switch (imsg->hdr.type) {
case IMSG_DEMOTE:
IMSG_SIZE_CHECK(imsg, &demote);
memcpy(&demote, imsg->data, sizeof(demote));
carp_demote_set(demote.group, demote.level);
break;
case IMSG_RTMSG:
IMSG_SIZE_CHECK(imsg, &crt);
memcpy(&crt, imsg->data, sizeof(crt));
pfe_route(env, &crt);
break;
case IMSG_CTL_RESET:
IMSG_SIZE_CHECK(imsg, &v);
memcpy(&v, imsg->data, sizeof(v));
parent_reload(env, v, NULL);
break;
case IMSG_CTL_RELOAD:
if (IMSG_DATA_SIZE(imsg) > 0)
str = get_string(imsg->data, IMSG_DATA_SIZE(imsg));
parent_reload(env, CONFIG_RELOAD, str);
free(str);
break;
case IMSG_CTL_SHUTDOWN:
parent_shutdown(env);
break;
case IMSG_CFG_DONE:
parent_configure_done(env);
break;
case IMSG_AGENTXSOCK:
agentx_setsock(env, p->p_id);
break;
default:
return (-1);
}
return (0);
}
int
parent_dispatch_hce(int fd, struct privsep_proc *p, struct imsg *imsg)
{
struct privsep *ps = p->p_ps;
struct relayd *env = ps->ps_env;
struct ctl_script scr;
switch (imsg->hdr.type) {
case IMSG_SCRIPT:
IMSG_SIZE_CHECK(imsg, &scr);
bcopy(imsg->data, &scr, sizeof(scr));
scr.retval = script_exec(env, &scr);
proc_compose(ps, PROC_HCE, IMSG_SCRIPT, &scr, sizeof(scr));
break;
case IMSG_CFG_DONE:
parent_configure_done(env);
break;
default:
return (-1);
}
return (0);
}
int
parent_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
{
struct privsep *ps = p->p_ps;
struct relayd *env = ps->ps_env;
struct ctl_bindany bnd;
int s;
switch (imsg->hdr.type) {
case IMSG_BINDANY:
IMSG_SIZE_CHECK(imsg, &bnd);
bcopy(imsg->data, &bnd, sizeof(bnd));
if (bnd.bnd_proc > env->sc_conf.prefork_relay)
fatalx("%s: invalid relay proc", __func__);
switch (bnd.bnd_proto) {
case IPPROTO_TCP:
case IPPROTO_UDP:
break;
default:
fatalx("%s: requested socket "
"for invalid protocol", __func__);
/* NOTREACHED */
}
s = bindany(&bnd);
proc_compose_imsg(ps, PROC_RELAY, bnd.bnd_proc,
IMSG_BINDANY, -1, s, &bnd.bnd_id, sizeof(bnd.bnd_id));
break;
case IMSG_CFG_DONE:
parent_configure_done(env);
break;
default:
return (-1);
}
return (0);
}
int
parent_dispatch_ca(int fd, struct privsep_proc *p, struct imsg *imsg)
{
struct privsep *ps = p->p_ps;
struct relayd *env = ps->ps_env;
switch (imsg->hdr.type) {
case IMSG_CFG_DONE:
parent_configure_done(env);
break;
default:
return (-1);
}
return (0);
}
void
purge_table(struct relayd *env, struct tablelist *head, struct table *table)
{
struct host *host;
while ((host = TAILQ_FIRST(&table->hosts)) != NULL) {
TAILQ_REMOVE(&table->hosts, host, entry);
TAILQ_REMOVE(&env->sc_hosts, host, globalentry);
if (event_initialized(&host->cte.ev)) {
event_del(&host->cte.ev);
close(host->cte.s);
}
ibuf_free(host->cte.buf);
tls_free(host->cte.tls);
free(host);
}
free(table->sendbuf);
ibuf_free(table->sendbinbuf);
tls_config_free(table->tls_cfg);
if (head != NULL)
TAILQ_REMOVE(head, table, entry);
free(table);
}
void
purge_key(char **key, off_t len)
{
freezero(*key, len);
*key = NULL;
}
void
purge_relay(struct relayd *env, struct relay *rlay)
{
struct rsession *con;
struct relay_table *rlt;
struct relay_cert *cert, *tmpcert;
/* shutdown and remove relay */
if (event_initialized(&rlay->rl_ev))
event_del(&rlay->rl_ev);
close(rlay->rl_s);
TAILQ_REMOVE(env->sc_relays, rlay, rl_entry);
/* cleanup sessions */
while ((con =
SPLAY_ROOT(&rlay->rl_sessions)) != NULL)
relay_close(con, NULL, 0);
/* cleanup relay */
if (rlay->rl_bev != NULL)
bufferevent_free(rlay->rl_bev);
if (rlay->rl_dstbev != NULL)
bufferevent_free(rlay->rl_dstbev);
purge_key(&rlay->rl_tls_cakey, rlay->rl_conf.tls_cakey_len);
if (rlay->rl_tls_pkey != NULL) {
EVP_PKEY_free(rlay->rl_tls_pkey);
rlay->rl_tls_pkey = NULL;
}
if (rlay->rl_tls_cacertx509 != NULL) {
X509_free(rlay->rl_tls_cacertx509);
rlay->rl_tls_cacertx509 = NULL;
}
if (rlay->rl_tls_capkey != NULL) {
EVP_PKEY_free(rlay->rl_tls_capkey);
rlay->rl_tls_capkey = NULL;
}
tls_free(rlay->rl_tls_ctx);
tls_config_free(rlay->rl_tls_cfg);
tls_config_free(rlay->rl_tls_client_cfg);
while ((rlt = TAILQ_FIRST(&rlay->rl_tables))) {
TAILQ_REMOVE(&rlay->rl_tables, rlt, rlt_entry);
free(rlt);
}
TAILQ_FOREACH_SAFE(cert, env->sc_certs, cert_entry, tmpcert) {
if (rlay->rl_conf.id != cert->cert_relayid)
continue;
if (cert->cert_fd != -1)
close(cert->cert_fd);
if (cert->cert_key_fd != -1)
close(cert->cert_key_fd);
if (cert->cert_ocsp_fd != -1)
close(cert->cert_ocsp_fd);
if (cert->cert_pkey != NULL)
EVP_PKEY_free(cert->cert_pkey);
TAILQ_REMOVE(env->sc_certs, cert, cert_entry);
free(cert);
}
free(rlay);
}
struct kv *
kv_add(struct kvtree *keys, char *key, char *value, int unique)
{
struct kv *kv, *oldkv;
if (key == NULL)
return (NULL);
if ((kv = calloc(1, sizeof(*kv))) == NULL)
return (NULL);
if ((kv->kv_key = strdup(key)) == NULL)
goto fail;
if (value != NULL &&
(kv->kv_value = strdup(value)) == NULL)
goto fail;
TAILQ_INIT(&kv->kv_children);
if ((oldkv = RB_INSERT(kvtree, keys, kv)) != NULL) {
/*
* return error if the key should occur only once,
* or add it to a list attached to the key's node.
*/
if (unique)
goto fail;
TAILQ_INSERT_TAIL(&oldkv->kv_children, kv, kv_entry);
kv->kv_parent = oldkv;
}
return (kv);
fail:
free(kv->kv_key);
free(kv->kv_value);
free(kv);
return (NULL);
}
int
kv_set(struct kv *kv, char *fmt, ...)
{
va_list ap;
char *value = NULL;
struct kv *ckv;
int ret;
va_start(ap, fmt);
ret = vasprintf(&value, fmt, ap);
va_end(ap);
if (ret == -1)
return (-1);
/* Remove all children */
while ((ckv = TAILQ_FIRST(&kv->kv_children)) != NULL) {
TAILQ_REMOVE(&kv->kv_children, ckv, kv_entry);
kv_free(ckv);
free(ckv);
}
/* Set the new value */
free(kv->kv_value);
kv->kv_value = value;
return (0);
}
int
kv_setkey(struct kv *kv, char *fmt, ...)
{
va_list ap;
char *key = NULL;
int ret;
va_start(ap, fmt);
ret = vasprintf(&key, fmt, ap);
va_end(ap);
if (ret == -1)
return (-1);
free(kv->kv_key);
kv->kv_key = key;
return (0);
}
void
kv_delete(struct kvtree *keys, struct kv *kv)
{
struct kv *ckv;
RB_REMOVE(kvtree, keys, kv);
/* Remove all children */
while ((ckv = TAILQ_FIRST(&kv->kv_children)) != NULL) {
TAILQ_REMOVE(&kv->kv_children, ckv, kv_entry);
kv_free(ckv);
free(ckv);
}
kv_free(kv);
free(kv);
}
struct kv *
kv_extend(struct kvtree *keys, struct kv *kv, char *value)
{
char *newvalue;
if (kv == NULL) {
return (NULL);
} else if (kv->kv_value != NULL) {
if (asprintf(&newvalue, "%s%s", kv->kv_value, value) == -1)
return (NULL);
free(kv->kv_value);
kv->kv_value = newvalue;
} else if ((kv->kv_value = strdup(value)) == NULL)
return (NULL);
return (kv);
}
void
kv_purge(struct kvtree *keys)
{
struct kv *kv;
while ((kv = RB_MIN(kvtree, keys)) != NULL)
kv_delete(keys, kv);
}
void
kv_free(struct kv *kv)
{
/*
* This function does not clear memory referenced by
* kv_children or stuff on the tailqs. Use kv_delete() instead.
*/
free(kv->kv_key);
free(kv->kv_value);
memset(kv, 0, sizeof(*kv));
}
struct kv *
kv_inherit(struct kv *dst, struct kv *src)
{
memset(dst, 0, sizeof(*dst));
memcpy(dst, src, sizeof(*dst));
TAILQ_INIT(&dst->kv_children);
if (src->kv_key != NULL) {
if ((dst->kv_key = strdup(src->kv_key)) == NULL) {
kv_free(dst);
return (NULL);
}
}
if (src->kv_value != NULL) {
if ((dst->kv_value = strdup(src->kv_value)) == NULL) {
kv_free(dst);
return (NULL);
}
}
if (src->kv_match != NULL)
dst->kv_match = src->kv_match;
if (src->kv_matchtree != NULL)
dst->kv_matchtree = src->kv_matchtree;
return (dst);
}
int
kv_log(struct rsession *con, struct kv *kv, u_int16_t labelid,
enum direction dir)
{
char *msg;
if (con->se_log == NULL)
return (0);
if (asprintf(&msg, " %s%s%s%s%s%s%s",
dir == RELAY_DIR_REQUEST ? "[" : "{",
labelid == 0 ? "" : label_id2name(labelid),
labelid == 0 ? "" : ", ",
kv->kv_key == NULL ? "(unknown)" : kv->kv_key,
kv->kv_value == NULL ? "" : ": ",
kv->kv_value == NULL ? "" : kv->kv_value,
dir == RELAY_DIR_REQUEST ? "]" : "}") == -1)
return (-1);
if (evbuffer_add(con->se_log, msg, strlen(msg)) == -1) {
free(msg);
return (-1);
}
free(msg);
con->se_haslog = 1;
return (0);
}
struct kv *
kv_find(struct kvtree *keys, struct kv *kv)
{
struct kv *match;
const char *key;
if (kv->kv_flags & KV_FLAG_GLOBBING) {
/* Test header key using shell globbing rules */
key = kv->kv_key == NULL ? "" : kv->kv_key;
RB_FOREACH(match, kvtree, keys) {
if (fnmatch(key, match->kv_key, FNM_CASEFOLD) == 0)
break;
}
} else {
/* Fast tree-based lookup only works without globbing */
match = RB_FIND(kvtree, keys, kv);
}
return (match);
}
struct kv *
kv_find_value(struct kvtree *keys, char *key, const char *value,
const char *delim)
{
struct kv *match, kv;
char *val = NULL, *next, *ptr;
size_t len;
kv.kv_key = key;
if ((match = RB_FIND(kvtree, keys, &kv)) == NULL)
return (NULL);
if (match->kv_value == NULL)
return (NULL);
if (delim == NULL) {
if (strcasecmp(match->kv_value, value) == 0)
goto done;
} else {
if ((val = strdup(match->kv_value)) == NULL)
return (NULL);
for (next = ptr = val; ptr != NULL;
ptr = strsep(&next, delim)) {
/* strip whitespace */
ptr += strspn(ptr, " \t");
len = strcspn(ptr, " \t");
if (strncasecmp(ptr, value, len) == 0)
goto done;
}
}
/* not matched */
match = NULL;
done:
#ifdef DEBUG
if (match != NULL)
DPRINTF("%s: matched %s: %s", __func__, key, value);
#endif
free(val);
return (match);
}
int
kv_cmp(struct kv *a, struct kv *b)
{
return (strcasecmp(a->kv_key, b->kv_key));
}
RB_GENERATE(kvtree, kv, kv_node, kv_cmp);
int
rule_add(struct protocol *proto, struct relay_rule *rule, const char *rulefile)
{
struct relay_rule *r = NULL;
struct kv *kv = NULL;
FILE *fp = NULL;
char buf[BUFSIZ];
int ret = -1;
u_int i;
for (i = 0; i < KEY_TYPE_MAX; i++) {
kv = &rule->rule_kv[i];
if (kv->kv_type != i)
continue;
switch (kv->kv_option) {
case KEY_OPTION_LOG:
/* log action needs a key or a file to be specified */
if (kv->kv_key == NULL && rulefile == NULL &&
(kv->kv_key = strdup("*")) == NULL)
goto fail;
break;
default:
break;
}
switch (kv->kv_type) {
case KEY_TYPE_QUERY:
case KEY_TYPE_PATH:
case KEY_TYPE_URL:
if (rule->rule_dir != RELAY_DIR_REQUEST)
goto fail;
break;
default:
break;
}
if (kv->kv_value != NULL && strchr(kv->kv_value, '$') != NULL)
kv->kv_flags |= KV_FLAG_MACRO;
if (kv->kv_key != NULL && strpbrk(kv->kv_key, "*?[") != NULL)
kv->kv_flags |= KV_FLAG_GLOBBING;
}
if (rulefile == NULL) {
TAILQ_INSERT_TAIL(&proto->rules, rule, rule_entry);
return (0);
}
if ((fp = fopen(rulefile, "r")) == NULL)
goto fail;
while (fgets(buf, sizeof(buf), fp) != NULL) {
/* strip whitespace and newline characters */
buf[strcspn(buf, "\r\n\t ")] = '\0';
if (!strlen(buf) || buf[0] == '#')
continue;
if ((r = rule_inherit(rule)) == NULL)
goto fail;
for (i = 0; i < KEY_TYPE_MAX; i++) {
kv = &r->rule_kv[i];
if (kv->kv_type != i)
continue;
free(kv->kv_key);
if ((kv->kv_key = strdup(buf)) == NULL) {
rule_free(r);
free(r);
goto fail;
}
}
TAILQ_INSERT_TAIL(&proto->rules, r, rule_entry);
}
ret = 0;
rule_free(rule);
free(rule);
fail:
if (fp != NULL)
fclose(fp);
return (ret);
}
struct relay_rule *
rule_inherit(struct relay_rule *rule)
{
struct relay_rule *r;
u_int i;
struct kv *kv;
if ((r = calloc(1, sizeof(*r))) == NULL)
return (NULL);
memcpy(r, rule, sizeof(*r));
for (i = 0; i < KEY_TYPE_MAX; i++) {
kv = &rule->rule_kv[i];
if (kv->kv_type != i)
continue;
if (kv_inherit(&r->rule_kv[i], kv) == NULL) {
free(r);
return (NULL);
}
}
if (r->rule_label > 0)
label_ref(r->rule_label);
if (r->rule_tag > 0)
tag_ref(r->rule_tag);
if (r->rule_tagged > 0)
tag_ref(r->rule_tagged);
return (r);
}
void
rule_free(struct relay_rule *rule)
{
u_int i;
for (i = 0; i < KEY_TYPE_MAX; i++)
kv_free(&rule->rule_kv[i]);
if (rule->rule_label > 0)
label_unref(rule->rule_label);
if (rule->rule_tag > 0)
tag_unref(rule->rule_tag);
if (rule->rule_tagged > 0)
tag_unref(rule->rule_tagged);
}
void
rule_delete(struct relay_rules *rules, struct relay_rule *rule)
{
TAILQ_REMOVE(rules, rule, rule_entry);
rule_free(rule);
free(rule);
}
void
rule_settable(struct relay_rules *rules, struct relay_table *rlt)
{
struct relay_rule *r;
char pname[TABLE_NAME_SIZE];
if (rlt->rlt_table == NULL || strlcpy(pname, rlt->rlt_table->conf.name,
sizeof(pname)) >= sizeof(pname))
return;
pname[strcspn(pname, ":")] = '\0';
TAILQ_FOREACH(r, rules, rule_entry) {
if (r->rule_tablename[0] &&
strcmp(pname, r->rule_tablename) == 0)
r->rule_table = rlt;
}
}
/*
* Utility functions
*/
struct host *
host_find(struct relayd *env, objid_t id)
{
struct table *table;
struct host *host;
TAILQ_FOREACH(table, env->sc_tables, entry)
TAILQ_FOREACH(host, &table->hosts, entry)
if (host->conf.id == id)
return (host);
return (NULL);
}
struct table *
table_find(struct relayd *env, objid_t id)
{
struct table *table;
TAILQ_FOREACH(table, env->sc_tables, entry)
if (table->conf.id == id)
return (table);
return (NULL);
}
struct rdr *
rdr_find(struct relayd *env, objid_t id)
{
struct rdr *rdr;
TAILQ_FOREACH(rdr, env->sc_rdrs, entry)
if (rdr->conf.id == id)
return (rdr);
return (NULL);
}
struct relay *
relay_find(struct relayd *env, objid_t id)
{
struct relay *rlay;
TAILQ_FOREACH(rlay, env->sc_relays, rl_entry)
if (rlay->rl_conf.id == id)
return (rlay);
return (NULL);
}
struct protocol *
proto_find(struct relayd *env, objid_t id)
{
struct protocol *p;
TAILQ_FOREACH(p, env->sc_protos, entry)
if (p->id == id)
return (p);
return (NULL);
}
struct rsession *
session_find(struct relayd *env, objid_t id)
{
struct relay *rlay;
struct rsession *con;
TAILQ_FOREACH(rlay, env->sc_relays, rl_entry)
SPLAY_FOREACH(con, session_tree, &rlay->rl_sessions)
if (con->se_id == id)
return (con);
return (NULL);
}
struct netroute *
route_find(struct relayd *env, objid_t id)
{
struct netroute *nr;
TAILQ_FOREACH(nr, env->sc_routes, nr_route)
if (nr->nr_conf.id == id)
return (nr);
return (NULL);
}
struct router *
router_find(struct relayd *env, objid_t id)
{
struct router *rt;
TAILQ_FOREACH(rt, env->sc_rts, rt_entry)
if (rt->rt_conf.id == id)
return (rt);
return (NULL);
}
struct host *
host_findbyname(struct relayd *env, const char *name)
{
struct table *table;
struct host *host;
TAILQ_FOREACH(table, env->sc_tables, entry)
TAILQ_FOREACH(host, &table->hosts, entry)
if (strcmp(host->conf.name, name) == 0)
return (host);
return (NULL);
}
struct table *
table_findbyname(struct relayd *env, const char *name)
{
struct table *table;
TAILQ_FOREACH(table, env->sc_tables, entry)
if (strcmp(table->conf.name, name) == 0)
return (table);
return (NULL);
}
struct table *
table_findbyconf(struct relayd *env, struct table *tb)
{
struct table *table;
struct table_config a, b;
bcopy(&tb->conf, &a, sizeof(a));
a.id = a.rdrid = 0;
a.flags &= ~(F_USED|F_BACKUP);
TAILQ_FOREACH(table, env->sc_tables, entry) {
bcopy(&table->conf, &b, sizeof(b));
b.id = b.rdrid = 0;
b.flags &= ~(F_USED|F_BACKUP);
/*
* Compare two tables and return the existing table if
* the configuration seems to be the same.
*/
if (bcmp(&a, &b, sizeof(b)) == 0 &&
((tb->sendbuf == NULL && table->sendbuf == NULL) ||
(tb->sendbuf != NULL && table->sendbuf != NULL &&
strcmp(tb->sendbuf, table->sendbuf) == 0)))
return (table);
}
return (NULL);
}
struct rdr *
rdr_findbyname(struct relayd *env, const char *name)
{
struct rdr *rdr;
TAILQ_FOREACH(rdr, env->sc_rdrs, entry)
if (strcmp(rdr->conf.name, name) == 0)
return (rdr);
return (NULL);
}
struct relay *
relay_findbyname(struct relayd *env, const char *name)
{
struct relay *rlay;
TAILQ_FOREACH(rlay, env->sc_relays, rl_entry)
if (strcmp(rlay->rl_conf.name, name) == 0)
return (rlay);
return (NULL);
}
struct relay *
relay_findbyaddr(struct relayd *env, struct relay_config *rc)
{
struct relay *rlay;
TAILQ_FOREACH(rlay, env->sc_relays, rl_entry)
if (bcmp(&rlay->rl_conf.ss, &rc->ss, sizeof(rc->ss)) == 0 &&
rlay->rl_conf.port == rc->port)
return (rlay);
return (NULL);
}
EVP_PKEY *
pkey_find(struct relayd *env, char * hash)
{
struct ca_pkey *pkey;
TAILQ_FOREACH(pkey, env->sc_pkeys, pkey_entry)
if (strcmp(hash, pkey->pkey_hash) == 0)
return (pkey->pkey);
return (NULL);
}
struct ca_pkey *
pkey_add(struct relayd *env, EVP_PKEY *pkey, char *hash)
{
struct ca_pkey *ca_pkey;
if (env->sc_pkeys == NULL)
fatalx("pkeys");
if ((ca_pkey = calloc(1, sizeof(*ca_pkey))) == NULL)
return (NULL);
ca_pkey->pkey = pkey;
if (strlcpy(ca_pkey->pkey_hash, hash, sizeof(ca_pkey->pkey_hash)) >=
sizeof(ca_pkey->pkey_hash)) {
free(ca_pkey);
return (NULL);
}
TAILQ_INSERT_TAIL(env->sc_pkeys, ca_pkey, pkey_entry);
return (ca_pkey);
}
struct relay_cert *
cert_add(struct relayd *env, objid_t id)
{
static objid_t last_cert_id = 0;
struct relay_cert *cert;
if ((cert = calloc(1, sizeof(*cert))) == NULL)
return (NULL);
if (id == 0)
id = ++last_cert_id;
if (id == INT_MAX) {
log_warnx("too many tls keypairs defined");
free(cert);
return (NULL);
}
cert->cert_id = id;
cert->cert_fd = -1;
cert->cert_key_fd = -1;
cert->cert_ocsp_fd = -1;
TAILQ_INSERT_TAIL(env->sc_certs, cert, cert_entry);
return (cert);
}
struct relay_cert *
cert_find(struct relayd *env, objid_t id)
{
struct relay_cert *cert;
TAILQ_FOREACH(cert, env->sc_certs, cert_entry)
if (cert->cert_id == id)
return (cert);
return (NULL);
}
char *
relay_load_fd(int fd, off_t *len)
{
char *buf = NULL;
struct stat st;
off_t size;
ssize_t rv;
int err;
if (fstat(fd, &st) != 0)
goto fail;
size = st.st_size;
if ((buf = calloc(1, size + 1)) == NULL)
goto fail;
if ((rv = pread(fd, buf, size, 0)) != size)
goto fail;
close(fd);
*len = size;
return (buf);
fail:
err = errno;
free(buf);
close(fd);
errno = err;
return (NULL);
}
int
relay_load_certfiles(struct relayd *env, struct relay *rlay, const char *name)
{
char certfile[PATH_MAX];
char hbuf[PATH_MAX];
struct protocol *proto = rlay->rl_proto;
struct relay_cert *cert;
int useport = htons(rlay->rl_conf.port);
int cert_fd = -1, key_fd = -1, ocsp_fd = -1;
if (rlay->rl_conf.flags & F_TLSCLIENT) {
if (strlen(proto->tlsca) && rlay->rl_tls_ca_fd == -1) {
if ((rlay->rl_tls_ca_fd =
open(proto->tlsca, O_RDONLY)) == -1)
return (-1);
log_debug("%s: using ca %s", __func__, proto->tlsca);
}
if (strlen(proto->tlscacert) && rlay->rl_tls_cacert_fd == -1) {
if ((rlay->rl_tls_cacert_fd =
open(proto->tlscacert, O_RDONLY)) == -1)
return (-1);
log_debug("%s: using ca certificate %s", __func__,
proto->tlscacert);
}
if (strlen(proto->tlscakey) && !rlay->rl_conf.tls_cakey_len &&
proto->tlscapass != NULL) {
if ((rlay->rl_tls_cakey =
ssl_load_key(env, proto->tlscakey,
&rlay->rl_conf.tls_cakey_len,
proto->tlscapass)) == NULL)
return (-1);
log_debug("%s: using ca key %s", __func__,
proto->tlscakey);
}
}
if ((rlay->rl_conf.flags & F_TLS) == 0)
return (0);
if (name == NULL &&
print_host(&rlay->rl_conf.ss, hbuf, sizeof(hbuf)) == NULL)
goto fail;
else if (name != NULL &&
strlcpy(hbuf, name, sizeof(hbuf)) >= sizeof(hbuf))
goto fail;
if (snprintf(certfile, sizeof(certfile),
"/etc/ssl/%s:%u.crt", hbuf, useport) == -1)
goto fail;
if ((cert_fd = open(certfile, O_RDONLY)) == -1) {
if (snprintf(certfile, sizeof(certfile),
"/etc/ssl/%s.crt", hbuf) == -1)
goto fail;
if ((cert_fd = open(certfile, O_RDONLY)) == -1)
goto fail;
useport = 0;
}
log_debug("%s: using certificate %s", __func__, certfile);
if (useport) {
if (snprintf(certfile, sizeof(certfile),
"/etc/ssl/private/%s:%u.key", hbuf, useport) == -1)
goto fail;
} else {
if (snprintf(certfile, sizeof(certfile),
"/etc/ssl/private/%s.key", hbuf) == -1)
goto fail;
}
if ((key_fd = open(certfile, O_RDONLY)) == -1)
goto fail;
log_debug("%s: using private key %s", __func__, certfile);
if (useport) {
if (snprintf(certfile, sizeof(certfile),
"/etc/ssl/%s:%u.ocsp", hbuf, useport) == -1)
goto fail;
} else {
if (snprintf(certfile, sizeof(certfile),
"/etc/ssl/%s.ocsp", hbuf) == -1)
goto fail;
}
if ((ocsp_fd = open(certfile, O_RDONLY)) != -1)
log_debug("%s: using OCSP staple file %s", __func__, certfile);
if ((cert = cert_add(env, 0)) == NULL)
goto fail;
cert->cert_relayid = rlay->rl_conf.id;
cert->cert_fd = cert_fd;
cert->cert_key_fd = key_fd;
cert->cert_ocsp_fd = ocsp_fd;
return (0);
fail:
if (cert_fd != -1)
close(cert_fd);
if (key_fd != -1)
close(key_fd);
if (ocsp_fd != -1)
close(ocsp_fd);
return (-1);
}
void
event_again(struct event *ev, int fd, short event,
void (*fn)(int, short, void *),
struct timeval *start, struct timeval *end, void *arg)
{
struct timeval tv_next, tv_now, tv;
getmonotime(&tv_now);
bcopy(end, &tv_next, sizeof(tv_next));
timersub(&tv_now, start, &tv_now);
timersub(&tv_next, &tv_now, &tv_next);
bzero(&tv, sizeof(tv));
if (timercmp(&tv_next, &tv, >))
bcopy(&tv_next, &tv, sizeof(tv));
event_del(ev);
event_set(ev, fd, event, fn, arg);
event_add(ev, &tv);
}
int
expand_string(char *label, size_t len, const char *srch, const char *repl)
{
char *tmp;
char *p, *q;
if ((tmp = calloc(1, len)) == NULL) {
log_debug("%s: calloc", __func__);
return (-1);
}
p = q = label;
while ((q = strstr(p, srch)) != NULL) {
*q = '\0';
if ((strlcat(tmp, p, len) >= len) ||
(strlcat(tmp, repl, len) >= len)) {
log_debug("%s: string too long", __func__);
free(tmp);
return (-1);
}
q += strlen(srch);
p = q;
}
if (strlcat(tmp, p, len) >= len) {
log_debug("%s: string too long", __func__);
free(tmp);
return (-1);
}
(void)strlcpy(label, tmp, len); /* always fits */
free(tmp);
return (0);
}
void
translate_string(char *str)
{
char *reader;
char *writer;
reader = writer = str;
while (*reader) {
if (*reader == '\\') {
reader++;
switch (*reader) {
case 'n':
*writer++ = '\n';
break;
case 'r':
*writer++ = '\r';
break;
default:
*writer++ = *reader;
}
} else
*writer++ = *reader;
reader++;
}
*writer = '\0';
}
char *
digeststr(enum digest_type type, const u_int8_t *data, size_t len, char *buf)
{
switch (type) {
case DIGEST_SHA1:
return (SHA1Data(data, len, buf));
break;
case DIGEST_MD5:
return (MD5Data(data, len, buf));
break;
default:
break;
}
return (NULL);
}
const char *
canonicalize_host(const char *host, char *name, size_t len)
{
struct sockaddr_in sin4;
struct sockaddr_in6 sin6;
size_t i, j;
size_t plen;
char c;
if (len < 2)
goto fail;
/*
* Canonicalize an IPv4/6 address
*/
if (inet_pton(AF_INET, host, &sin4) == 1)
return (inet_ntop(AF_INET, &sin4, name, len));
if (inet_pton(AF_INET6, host, &sin6) == 1)
return (inet_ntop(AF_INET6, &sin6, name, len));
/*
* Canonicalize a hostname
*/
/* 1. remove repeated dots and convert upper case to lower case */
plen = strlen(host);
bzero(name, len);
for (i = j = 0; i < plen; i++) {
if (j >= (len - 1))
goto fail;
c = tolower((unsigned char)host[i]);
if ((c == '.') && (j == 0 || name[j - 1] == '.'))
continue;
name[j++] = c;
}
/* 2. remove trailing dots */
for (i = j; i > 0; i--) {
if (name[i - 1] != '.')
break;
name[i - 1] = '\0';
j--;
}
if (j <= 0)
goto fail;
return (name);
fail:
errno = EINVAL;
return (NULL);
}
int
parse_url(const char *url, char **protoptr, char **hostptr, char **pathptr)
{
char *p, *proto = NULL, *host = NULL, *path = NULL;
/* return error if it is not a URL */
if ((p = strstr(url, ":/")) == NULL ||
(strcspn(url, ":/") != (size_t)(p - url)))
return (-1);
/* get protocol */
if ((proto = strdup(url)) == NULL)
goto fail;
p = proto + (p - url);
/* get host */
p += strspn(p, ":/");
if (*p == '\0' || (host = strdup(p)) == NULL)
goto fail;
*p = '\0';
/* find and copy path or default to "/" */
if ((p = strchr(host, '/')) == NULL)
p = "/";
if ((path = strdup(p)) == NULL)
goto fail;
/* strip path after host */
host[strcspn(host, "/")] = '\0';
DPRINTF("%s: %s proto %s, host %s, path %s", __func__,
url, proto, host, path);
*protoptr = proto;
*hostptr = host;
*pathptr = path;
return (0);
fail:
free(proto);
free(host);
free(path);
return (-1);
}
int
bindany(struct ctl_bindany *bnd)
{
int s, v;
s = -1;
v = 1;
if (relay_socket_af(&bnd->bnd_ss, bnd->bnd_port) == -1)
goto fail;
if ((s = socket(bnd->bnd_ss.ss_family,
bnd->bnd_proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM,
bnd->bnd_proto)) == -1)
goto fail;
if (setsockopt(s, SOL_SOCKET, SO_BINDANY,
&v, sizeof(v)) == -1)
goto fail;
if (bind(s, (struct sockaddr *)&bnd->bnd_ss,
bnd->bnd_ss.ss_len) == -1)
goto fail;
return (s);
fail:
if (s != -1)
close(s);
return (-1);
}
int
map6to4(struct sockaddr_storage *in6)
{
struct sockaddr_storage out4;
struct sockaddr_in *sin4 = (struct sockaddr_in *)&out4;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)in6;
bzero(sin4, sizeof(*sin4));
sin4->sin_len = sizeof(*sin4);
sin4->sin_family = AF_INET;
sin4->sin_port = sin6->sin6_port;
bcopy(&sin6->sin6_addr.s6_addr[12], &sin4->sin_addr.s_addr,
sizeof(sin4->sin_addr));
if (sin4->sin_addr.s_addr == INADDR_ANY ||
sin4->sin_addr.s_addr == INADDR_BROADCAST ||
IN_MULTICAST(ntohl(sin4->sin_addr.s_addr)))
return (-1);
bcopy(&out4, in6, sizeof(*in6));
return (0);
}
int
map4to6(struct sockaddr_storage *in4, struct sockaddr_storage *map)
{
struct sockaddr_storage out6;
struct sockaddr_in *sin4 = (struct sockaddr_in *)in4;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&out6;
struct sockaddr_in6 *map6 = (struct sockaddr_in6 *)map;
if (sin4->sin_addr.s_addr == INADDR_ANY ||
sin4->sin_addr.s_addr == INADDR_BROADCAST ||
IN_MULTICAST(ntohl(sin4->sin_addr.s_addr)))
return (-1);
bcopy(map6, sin6, sizeof(*sin6));
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = sin4->sin_port;
bcopy(&sin4->sin_addr.s_addr, &sin6->sin6_addr.s6_addr[12],
sizeof(sin4->sin_addr));
bcopy(&out6, in4, sizeof(*in4));
return (0);
}
void
socket_rlimit(int maxfd)
{
struct rlimit rl;
if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
fatal("%s: failed to get resource limit", __func__);
log_debug("%s: max open files %llu", __func__, rl.rlim_max);
/*
* Allow the maximum number of open file descriptors for this
* login class (which should be the class "daemon" by default).
*/
if (maxfd == -1)
rl.rlim_cur = rl.rlim_max;
else
rl.rlim_cur = MAXIMUM(rl.rlim_max, (rlim_t)maxfd);
if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
fatal("%s: failed to set resource limit", __func__);
}
char *
get_string(u_int8_t *ptr, size_t len)
{
size_t i;
for (i = 0; i < len; i++)
if (!(isprint((unsigned char)ptr[i]) ||
isspace((unsigned char)ptr[i])))
break;
return strndup(ptr, i);
}
void *
get_data(u_int8_t *ptr, size_t len)
{
u_int8_t *data;
if ((data = malloc(len)) == NULL)
return (NULL);
memcpy(data, ptr, len);
return (data);
}
int
sockaddr_cmp(struct sockaddr *a, struct sockaddr *b, int prefixlen)
{
struct sockaddr_in *a4, *b4;
struct sockaddr_in6 *a6, *b6;
u_int32_t av[4], bv[4], mv[4];
if (a->sa_family == AF_UNSPEC || b->sa_family == AF_UNSPEC)
return (0);
else if (a->sa_family > b->sa_family)
return (1);
else if (a->sa_family < b->sa_family)
return (-1);
if (prefixlen == -1)
memset(&mv, 0xff, sizeof(mv));
switch (a->sa_family) {
case AF_INET:
a4 = (struct sockaddr_in *)a;
b4 = (struct sockaddr_in *)b;
av[0] = a4->sin_addr.s_addr;
bv[0] = b4->sin_addr.s_addr;
if (prefixlen != -1)
mv[0] = prefixlen2mask(prefixlen);
if ((av[0] & mv[0]) > (bv[0] & mv[0]))
return (1);
if ((av[0] & mv[0]) < (bv[0] & mv[0]))
return (-1);
break;
case AF_INET6:
a6 = (struct sockaddr_in6 *)a;
b6 = (struct sockaddr_in6 *)b;
memcpy(&av, &a6->sin6_addr.s6_addr, 16);
memcpy(&bv, &b6->sin6_addr.s6_addr, 16);
if (prefixlen != -1)
prefixlen2mask6(prefixlen, mv);
if ((av[3] & mv[3]) > (bv[3] & mv[3]))
return (1);
if ((av[3] & mv[3]) < (bv[3] & mv[3]))
return (-1);
if ((av[2] & mv[2]) > (bv[2] & mv[2]))
return (1);
if ((av[2] & mv[2]) < (bv[2] & mv[2]))
return (-1);
if ((av[1] & mv[1]) > (bv[1] & mv[1]))
return (1);
if ((av[1] & mv[1]) < (bv[1] & mv[1]))
return (-1);
if ((av[0] & mv[0]) > (bv[0] & mv[0]))
return (1);
if ((av[0] & mv[0]) < (bv[0] & mv[0]))
return (-1);
break;
}
return (0);
}
u_int32_t
prefixlen2mask(u_int8_t prefixlen)
{
if (prefixlen == 0)
return (0);
if (prefixlen > 32)
prefixlen = 32;
return (htonl(0xffffffff << (32 - prefixlen)));
}
struct in6_addr *
prefixlen2mask6(u_int8_t prefixlen, u_int32_t *mask)
{
static struct in6_addr s6;
int i;
if (prefixlen > 128)
prefixlen = 128;
bzero(&s6, sizeof(s6));
for (i = 0; i < prefixlen / 8; i++)
s6.s6_addr[i] = 0xff;
i = prefixlen % 8;
if (i)
s6.s6_addr[prefixlen / 8] = 0xff00 >> i;
memcpy(mask, &s6, sizeof(s6));
return (&s6);
}
int
accept_reserve(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
int reserve, volatile int *counter)
{
int ret;
if (getdtablecount() + reserve +
*counter >= getdtablesize()) {
errno = EMFILE;
return (-1);
}
if ((ret = accept4(sockfd, addr, addrlen, SOCK_NONBLOCK)) > -1) {
(*counter)++;
DPRINTF("%s: inflight incremented, now %d",__func__, *counter);
}
return (ret);
}
void
parent_tls_ticket_rekey(int fd, short events, void *arg)
{
static struct event rekeyev;
struct relayd *env = arg;
struct timeval tv;
struct relay_ticket_key key;
log_debug("%s: rekeying tickets", __func__);
key.tt_keyrev = arc4random();
arc4random_buf(key.tt_key, sizeof(key.tt_key));
proc_compose_imsg(env->sc_ps, PROC_RELAY, -1, IMSG_TLSTICKET_REKEY,
-1, -1, &key, sizeof(key));
evtimer_set(&rekeyev, parent_tls_ticket_rekey, env);
timerclear(&tv);
tv.tv_sec = TLS_SESSION_LIFETIME / 4;
evtimer_add(&rekeyev, &tv);
}