File: [local] / src / sbin / unwind / unwind.c (download)
Revision 1.68, Wed Feb 15 13:47:00 2023 UTC (15 months, 3 weeks ago) by florian
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, HEAD Changes since 1.67: +2 -2 lines
Use correct order of arguments for shutdown(2).
Luckily routesock would never be <= 2 so shutdown would always failing
with EINVAL and not shutting down random other sockets.
The kernel was just uselessly piling up route messages that we were
never reading.
This mistake first showed up in slaacd(8) and then was copied to the
other daemons.
Reported & fixed by Josiah Frentsos (jfrent at tilde.team), thanks!
OK kn
|
/* $OpenBSD: unwind.c,v 1.68 2023/02/15 13:47:00 florian Exp $ */
/*
* Copyright (c) 2018 Florian Obser <florian@openbsd.org>
* Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2004 Esben Norby <norby@openbsd.org>
* Copyright (c) 2003, 2004 Henning Brauer <henning@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/syslog.h>
#include <sys/wait.h>
#include <net/if.h>
#include <net/route.h>
#include <err.h>
#include <errno.h>
#include <event.h>
#include <fcntl.h>
#include <imsg.h>
#include <netdb.h>
#include <asr.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include "log.h"
#include "unwind.h"
#include "frontend.h"
#include "resolver.h"
#include "control.h"
#define TRUST_ANCHOR_FILE "/var/db/unwind.key"
enum uw_process {
PROC_MAIN,
PROC_RESOLVER,
PROC_FRONTEND,
};
__dead void usage(void);
__dead void main_shutdown(void);
void main_sig_handler(int, short, void *);
static pid_t start_child(enum uw_process, char *, int, int, int);
void main_dispatch_frontend(int, short, void *);
void main_dispatch_resolver(int, short, void *);
static int main_imsg_send_ipc_sockets(struct imsgbuf *, struct imsgbuf *);
static int main_imsg_send_config(struct uw_conf *);
int main_reload(void);
int main_sendall(enum imsg_type, void *, uint16_t);
void open_ports(void);
void solicit_dns_proposals(void);
void send_blocklist_fd(void);
struct uw_conf *main_conf;
static struct imsgev *iev_frontend;
static struct imsgev *iev_resolver;
char *conffile;
pid_t frontend_pid;
pid_t resolver_pid;
uint32_t cmd_opts;
int routesock;
void
main_sig_handler(int sig, short event, void *arg)
{
/*
* Normal signal handler rules don't apply because libevent
* decouples for us.
*/
switch (sig) {
case SIGTERM:
case SIGINT:
main_shutdown();
break;
case SIGHUP:
if (main_reload() == -1)
log_warnx("configuration reload failed");
else
log_debug("configuration reloaded");
break;
default:
fatalx("unexpected signal");
}
}
__dead void
usage(void)
{
extern char *__progname;
fprintf(stderr, "usage: %s [-dnv] [-f file] [-s socket]\n",
__progname);
exit(1);
}
int
main(int argc, char *argv[])
{
struct event ev_sigint, ev_sigterm, ev_sighup;
int ch, debug = 0, resolver_flag = 0, frontend_flag = 0;
int frontend_routesock, rtfilter;
int pipe_main2frontend[2], pipe_main2resolver[2];
int control_fd, ta_fd;
char *csock, *saved_argv0;
csock = _PATH_UNWIND_SOCKET;
log_init(1, LOG_DAEMON); /* Log to stderr until daemonized. */
log_setverbose(1);
saved_argv0 = argv[0];
if (saved_argv0 == NULL)
saved_argv0 = "unwind";
while ((ch = getopt(argc, argv, "dEFf:ns:v")) != -1) {
switch (ch) {
case 'd':
debug = 1;
break;
case 'E':
resolver_flag = 1;
break;
case 'F':
frontend_flag = 1;
break;
case 'f':
conffile = optarg;
break;
case 'n':
cmd_opts |= OPT_NOACTION;
break;
case 's':
csock = optarg;
break;
case 'v':
if (cmd_opts & OPT_VERBOSE2)
cmd_opts |= OPT_VERBOSE3;
if (cmd_opts & OPT_VERBOSE)
cmd_opts |= OPT_VERBOSE2;
cmd_opts |= OPT_VERBOSE;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc > 0 || (resolver_flag && frontend_flag))
usage();
if (resolver_flag)
resolver(debug, cmd_opts & (OPT_VERBOSE | OPT_VERBOSE2 |
OPT_VERBOSE3));
else if (frontend_flag)
frontend(debug, cmd_opts & (OPT_VERBOSE | OPT_VERBOSE2 |
OPT_VERBOSE3));
if ((main_conf = parse_config(conffile)) == NULL)
exit(1);
if (cmd_opts & OPT_NOACTION) {
if (cmd_opts & OPT_VERBOSE)
print_config(main_conf);
else
fprintf(stderr, "configuration OK\n");
exit(0);
}
/* Check for root privileges. */
if (geteuid())
errx(1, "need root privileges");
/* Check for assigned daemon user */
if (getpwnam(UNWIND_USER) == NULL)
errx(1, "unknown user %s", UNWIND_USER);
log_init(debug, LOG_DAEMON);
log_setverbose(cmd_opts & (OPT_VERBOSE | OPT_VERBOSE2 | OPT_VERBOSE3));
if (!debug)
daemon(1, 0);
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
PF_UNSPEC, pipe_main2frontend) == -1)
fatal("main2frontend socketpair");
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
PF_UNSPEC, pipe_main2resolver) == -1)
fatal("main2resolver socketpair");
/* Start children. */
resolver_pid = start_child(PROC_RESOLVER, saved_argv0,
pipe_main2resolver[1], debug, cmd_opts & (OPT_VERBOSE |
OPT_VERBOSE2 | OPT_VERBOSE3));
frontend_pid = start_child(PROC_FRONTEND, saved_argv0,
pipe_main2frontend[1], debug, cmd_opts & (OPT_VERBOSE |
OPT_VERBOSE2 | OPT_VERBOSE3));
log_procinit("main");
event_init();
/* Setup signal handler. */
signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL);
signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL);
signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL);
signal_add(&ev_sigint, NULL);
signal_add(&ev_sigterm, NULL);
signal_add(&ev_sighup, NULL);
signal(SIGPIPE, SIG_IGN);
/* Setup pipes to children. */
if ((iev_frontend = malloc(sizeof(struct imsgev))) == NULL ||
(iev_resolver = malloc(sizeof(struct imsgev))) == NULL)
fatal(NULL);
imsg_init(&iev_frontend->ibuf, pipe_main2frontend[0]);
iev_frontend->handler = main_dispatch_frontend;
imsg_init(&iev_resolver->ibuf, pipe_main2resolver[0]);
iev_resolver->handler = main_dispatch_resolver;
/* Setup event handlers for pipes. */
iev_frontend->events = EV_READ;
event_set(&iev_frontend->ev, iev_frontend->ibuf.fd,
iev_frontend->events, iev_frontend->handler, iev_frontend);
event_add(&iev_frontend->ev, NULL);
iev_resolver->events = EV_READ;
event_set(&iev_resolver->ev, iev_resolver->ibuf.fd,
iev_resolver->events, iev_resolver->handler, iev_resolver);
event_add(&iev_resolver->ev, NULL);
if (main_imsg_send_ipc_sockets(&iev_frontend->ibuf,
&iev_resolver->ibuf))
fatal("could not establish imsg links");
open_ports();
if ((control_fd = control_init(csock)) == -1)
fatalx("control socket setup failed");
if ((frontend_routesock = socket(AF_ROUTE, SOCK_RAW | SOCK_CLOEXEC |
SOCK_NONBLOCK, 0)) == -1)
fatal("route socket");
rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_PROPOSAL)
| ROUTE_FILTER(RTM_IFANNOUNCE) | ROUTE_FILTER(RTM_NEWADDR)
| ROUTE_FILTER(RTM_DELADDR);
if (setsockopt(frontend_routesock, AF_ROUTE, ROUTE_MSGFILTER,
&rtfilter, sizeof(rtfilter)) == -1)
fatal("setsockopt(ROUTE_MSGFILTER)");
if ((routesock = socket(AF_ROUTE, SOCK_RAW | SOCK_CLOEXEC |
SOCK_NONBLOCK, 0)) == -1)
fatal("route socket");
shutdown(routesock, SHUT_RD);
if ((ta_fd = open(TRUST_ANCHOR_FILE, O_RDWR | O_CREAT, 0644)) == -1)
log_warn("%s", TRUST_ANCHOR_FILE);
/* receiver handles failed open correctly */
main_imsg_compose_frontend_fd(IMSG_TAFD, 0, ta_fd);
main_imsg_compose_frontend_fd(IMSG_CONTROLFD, 0, control_fd);
main_imsg_compose_frontend_fd(IMSG_ROUTESOCK, 0, frontend_routesock);
main_imsg_send_config(main_conf);
if (main_conf->blocklist_file != NULL)
send_blocklist_fd();
if (pledge("stdio rpath sendfd", NULL) == -1)
fatal("pledge");
main_imsg_compose_frontend(IMSG_STARTUP, 0, NULL, 0);
main_imsg_compose_resolver(IMSG_STARTUP, 0, NULL, 0);
event_dispatch();
main_shutdown();
return (0);
}
__dead void
main_shutdown(void)
{
pid_t pid;
int status;
/* Close pipes. */
msgbuf_clear(&iev_frontend->ibuf.w);
close(iev_frontend->ibuf.fd);
msgbuf_clear(&iev_resolver->ibuf.w);
close(iev_resolver->ibuf.fd);
config_clear(main_conf);
log_debug("waiting for children to terminate");
do {
pid = wait(&status);
if (pid == -1) {
if (errno != EINTR && errno != ECHILD)
fatal("wait");
} else if (WIFSIGNALED(status))
log_warnx("%s terminated; signal %d",
(pid == resolver_pid) ? "resolver" :
"frontend", WTERMSIG(status));
} while (pid != -1 || (pid == -1 && errno == EINTR));
free(iev_frontend);
free(iev_resolver);
log_info("terminating");
exit(0);
}
static pid_t
start_child(enum uw_process p, char *argv0, int fd, int debug, int verbose)
{
char *argv[7];
int argc = 0;
pid_t pid;
switch (pid = fork()) {
case -1:
fatal("cannot fork");
case 0:
break;
default:
close(fd);
return (pid);
}
if (fd != 3) {
if (dup2(fd, 3) == -1)
fatal("cannot setup imsg fd");
} else if (fcntl(fd, F_SETFD, 0) == -1)
fatal("cannot setup imsg fd");
argv[argc++] = argv0;
switch (p) {
case PROC_MAIN:
fatalx("Can not start main process");
case PROC_RESOLVER:
argv[argc++] = "-E";
break;
case PROC_FRONTEND:
argv[argc++] = "-F";
break;
}
if (debug)
argv[argc++] = "-d";
if (verbose & OPT_VERBOSE)
argv[argc++] = "-v";
if (verbose & OPT_VERBOSE2)
argv[argc++] = "-v";
if (verbose & OPT_VERBOSE3)
argv[argc++] = "-v";
argv[argc++] = NULL;
execvp(argv0, argv);
fatal("execvp");
}
void
main_dispatch_frontend(int fd, short event, void *bula)
{
struct imsgev *iev = bula;
struct imsgbuf *ibuf;
struct imsg imsg;
ssize_t n;
int shut = 0, verbose;
ibuf = &iev->ibuf;
if (event & EV_READ) {
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
fatal("imsg_read error");
if (n == 0) /* Connection closed. */
shut = 1;
}
if (event & EV_WRITE) {
if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
fatal("msgbuf_write");
if (n == 0) /* Connection closed. */
shut = 1;
}
for (;;) {
if ((n = imsg_get(ibuf, &imsg)) == -1)
fatal("imsg_get");
if (n == 0) /* No more messages. */
break;
switch (imsg.hdr.type) {
case IMSG_STARTUP_DONE:
solicit_dns_proposals();
break;
case IMSG_CTL_RELOAD:
if (main_reload() == -1)
log_warnx("configuration reload failed");
else
log_warnx("configuration reloaded");
break;
case IMSG_CTL_LOG_VERBOSE:
if (IMSG_DATA_SIZE(imsg) != sizeof(verbose))
fatalx("%s: IMSG_CTL_LOG_VERBOSE wrong length: "
"%lu", __func__, IMSG_DATA_SIZE(imsg));
memcpy(&verbose, imsg.data, sizeof(verbose));
log_setverbose(verbose);
break;
default:
log_debug("%s: error handling imsg %d", __func__,
imsg.hdr.type);
break;
}
imsg_free(&imsg);
}
if (!shut)
imsg_event_add(iev);
else {
/* This pipe is dead. Remove its event handler */
event_del(&iev->ev);
event_loopexit(NULL);
}
}
void
main_dispatch_resolver(int fd, short event, void *bula)
{
struct imsgev *iev = bula;
struct imsgbuf *ibuf;
struct imsg imsg;
ssize_t n;
int shut = 0;
ibuf = &iev->ibuf;
if (event & EV_READ) {
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
fatal("imsg_read error");
if (n == 0) /* Connection closed. */
shut = 1;
}
if (event & EV_WRITE) {
if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
fatal("msgbuf_write");
if (n == 0) /* Connection closed. */
shut = 1;
}
for (;;) {
if ((n = imsg_get(ibuf, &imsg)) == -1)
fatal("imsg_get");
if (n == 0) /* No more messages. */
break;
switch (imsg.hdr.type) {
default:
log_debug("%s: error handling imsg %d", __func__,
imsg.hdr.type);
break;
}
imsg_free(&imsg);
}
if (!shut)
imsg_event_add(iev);
else {
/* This pipe is dead. Remove its event handler. */
event_del(&iev->ev);
event_loopexit(NULL);
}
}
void
main_imsg_compose_frontend(int type, pid_t pid, void *data, uint16_t datalen)
{
if (iev_frontend)
imsg_compose_event(iev_frontend, type, 0, pid, -1, data,
datalen);
}
void
main_imsg_compose_frontend_fd(int type, pid_t pid, int fd)
{
if (iev_frontend)
imsg_compose_event(iev_frontend, type, 0, pid, fd, NULL, 0);
}
void
main_imsg_compose_resolver(int type, pid_t pid, void *data, uint16_t datalen)
{
if (iev_resolver)
imsg_compose_event(iev_resolver, type, 0, pid, -1, data,
datalen);
}
void
imsg_event_add(struct imsgev *iev)
{
iev->events = EV_READ;
if (iev->ibuf.w.queued)
iev->events |= EV_WRITE;
event_del(&iev->ev);
event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
event_add(&iev->ev, NULL);
}
int
imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
pid_t pid, int fd, void *data, uint16_t datalen)
{
int ret;
if ((ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data,
datalen)) != -1)
imsg_event_add(iev);
return (ret);
}
static int
main_imsg_send_ipc_sockets(struct imsgbuf *frontend_buf,
struct imsgbuf *resolver_buf)
{
int pipe_frontend2resolver[2];
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
PF_UNSPEC, pipe_frontend2resolver) == -1)
return (-1);
if (imsg_compose(frontend_buf, IMSG_SOCKET_IPC_RESOLVER, 0, 0,
pipe_frontend2resolver[0], NULL, 0) == -1)
return (-1);
if (imsg_compose(resolver_buf, IMSG_SOCKET_IPC_FRONTEND, 0, 0,
pipe_frontend2resolver[1], NULL, 0) == -1)
return (-1);
return (0);
}
int
main_reload(void)
{
struct uw_conf *xconf;
if ((xconf = parse_config(conffile)) == NULL)
return (-1);
if (main_imsg_send_config(xconf) == -1)
return (-1);
merge_config(main_conf, xconf);
if (main_conf->blocklist_file != NULL)
send_blocklist_fd();
return (0);
}
int
main_imsg_send_config(struct uw_conf *xconf)
{
struct uw_forwarder *uw_forwarder;
struct force_tree_entry *force_entry;
/* Send fixed part of config to children. */
if (main_sendall(IMSG_RECONF_CONF, xconf, sizeof(*xconf)) == -1)
return (-1);
if (xconf->blocklist_file != NULL) {
if (main_sendall(IMSG_RECONF_BLOCKLIST_FILE,
xconf->blocklist_file, strlen(xconf->blocklist_file) + 1)
== -1)
return (-1);
}
/* send static forwarders to children */
TAILQ_FOREACH(uw_forwarder, &xconf->uw_forwarder_list, entry) {
if (main_sendall(IMSG_RECONF_FORWARDER, uw_forwarder,
sizeof(*uw_forwarder)) == -1)
return (-1);
}
/* send static DoT forwarders to children */
TAILQ_FOREACH(uw_forwarder, &xconf->uw_dot_forwarder_list,
entry) {
if (main_sendall(IMSG_RECONF_DOT_FORWARDER, uw_forwarder,
sizeof(*uw_forwarder)) == -1)
return (-1);
}
RB_FOREACH(force_entry, force_tree, &xconf->force) {
if (main_sendall(IMSG_RECONF_FORCE, force_entry,
sizeof(*force_entry)) == -1)
return (-1);
}
/* Tell children the revised config is now complete. */
if (main_sendall(IMSG_RECONF_END, NULL, 0) == -1)
return (-1);
return (0);
}
int
main_sendall(enum imsg_type type, void *buf, uint16_t len)
{
if (imsg_compose_event(iev_frontend, type, 0, 0, -1, buf, len) == -1)
return (-1);
if (imsg_compose_event(iev_resolver, type, 0, 0, -1, buf, len) == -1)
return (-1);
return (0);
}
void
merge_config(struct uw_conf *conf, struct uw_conf *xconf)
{
struct uw_forwarder *uw_forwarder;
struct force_tree_entry *n, *nxt;
/* Remove & discard existing forwarders. */
while ((uw_forwarder = TAILQ_FIRST(&conf->uw_forwarder_list)) !=
NULL) {
TAILQ_REMOVE(&conf->uw_forwarder_list, uw_forwarder, entry);
free(uw_forwarder);
}
while ((uw_forwarder = TAILQ_FIRST(&conf->uw_dot_forwarder_list)) !=
NULL) {
TAILQ_REMOVE(&conf->uw_dot_forwarder_list, uw_forwarder, entry);
free(uw_forwarder);
}
/* Remove & discard existing force tree. */
RB_FOREACH_SAFE(n, force_tree, &conf->force, nxt) {
RB_REMOVE(force_tree, &conf->force, n);
free(n);
}
memcpy(&conf->enabled_resolvers, &xconf->enabled_resolvers,
sizeof(conf->enabled_resolvers));
memcpy(&conf->res_pref, &xconf->res_pref,
sizeof(conf->res_pref));
free(conf->blocklist_file);
conf->blocklist_file = xconf->blocklist_file;
conf->blocklist_log = xconf->blocklist_log;
/* Add new forwarders. */
TAILQ_CONCAT(&conf->uw_forwarder_list, &xconf->uw_forwarder_list,
entry);
TAILQ_CONCAT(&conf->uw_dot_forwarder_list,
&xconf->uw_dot_forwarder_list, entry);
RB_FOREACH_SAFE(n, force_tree, &xconf->force, nxt) {
RB_REMOVE(force_tree, &xconf->force, n);
RB_INSERT(force_tree, &conf->force, n);
}
free(xconf);
}
struct uw_conf *
config_new_empty(void)
{
struct uw_conf *xconf;
xconf = calloc(1, sizeof(*xconf));
if (xconf == NULL)
fatal(NULL);
TAILQ_INIT(&xconf->uw_forwarder_list);
TAILQ_INIT(&xconf->uw_dot_forwarder_list);
RB_INIT(&xconf->force);
return (xconf);
}
void
config_clear(struct uw_conf *conf)
{
struct uw_conf *xconf;
/* Merge current config with an empty config. */
xconf = config_new_empty();
merge_config(conf, xconf);
free(conf);
}
void
open_ports(void)
{
struct addrinfo hints, *res0;
int udp4sock = -1, udp6sock = -1, error, bsize = 65535;
int tcp4sock = -1, tcp6sock = -1;
int opt = 1;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
error = getaddrinfo("127.0.0.1", "domain", &hints, &res0);
if (!error && res0) {
if ((udp4sock = socket(res0->ai_family, res0->ai_socktype,
res0->ai_protocol)) != -1) {
if (setsockopt(udp4sock, SOL_SOCKET, SO_REUSEADDR,
&opt, sizeof(opt)) == -1)
log_warn("setting SO_REUSEADDR on socket");
if (setsockopt(udp4sock, SOL_SOCKET, SO_SNDBUF, &bsize,
sizeof(bsize)) == -1)
log_warn("setting SO_SNDBUF on socket");
if (bind(udp4sock, res0->ai_addr, res0->ai_addrlen)
== -1) {
close(udp4sock);
udp4sock = -1;
}
}
}
if (res0)
freeaddrinfo(res0);
hints.ai_family = AF_INET6;
error = getaddrinfo("::1", "domain", &hints, &res0);
if (!error && res0) {
if ((udp6sock = socket(res0->ai_family, res0->ai_socktype,
res0->ai_protocol)) != -1) {
if (setsockopt(udp6sock, SOL_SOCKET, SO_REUSEADDR,
&opt, sizeof(opt)) == -1)
log_warn("setting SO_REUSEADDR on socket");
if (setsockopt(udp6sock, SOL_SOCKET, SO_SNDBUF, &bsize,
sizeof(bsize)) == -1)
log_warn("setting SO_SNDBUF on socket");
if (bind(udp6sock, res0->ai_addr, res0->ai_addrlen)
== -1) {
close(udp6sock);
udp6sock = -1;
}
}
}
if (res0)
freeaddrinfo(res0);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo("127.0.0.1", "domain", &hints, &res0);
if (!error && res0) {
if ((tcp4sock = socket(res0->ai_family,
res0->ai_socktype | SOCK_NONBLOCK,
res0->ai_protocol)) != -1) {
if (setsockopt(tcp4sock, SOL_SOCKET, SO_REUSEADDR,
&opt, sizeof(opt)) == -1)
log_warn("setting SO_REUSEADDR on socket");
if (setsockopt(tcp4sock, SOL_SOCKET, SO_SNDBUF, &bsize,
sizeof(bsize)) == -1)
log_warn("setting SO_SNDBUF on socket");
if (bind(tcp4sock, res0->ai_addr, res0->ai_addrlen)
== -1) {
close(tcp4sock);
tcp4sock = -1;
}
if (listen(tcp4sock, 5) == -1) {
close(tcp4sock);
tcp4sock = -1;
}
}
}
if (res0)
freeaddrinfo(res0);
hints.ai_family = AF_INET6;
error = getaddrinfo("::1", "domain", &hints, &res0);
if (!error && res0) {
if ((tcp6sock = socket(res0->ai_family,
res0->ai_socktype | SOCK_NONBLOCK,
res0->ai_protocol)) != -1) {
if (setsockopt(tcp6sock, SOL_SOCKET, SO_REUSEADDR,
&opt, sizeof(opt)) == -1)
log_warn("setting SO_REUSEADDR on socket");
if (setsockopt(tcp6sock, SOL_SOCKET, SO_SNDBUF, &bsize,
sizeof(bsize)) == -1)
log_warn("setting SO_SNDBUF on socket");
if (bind(tcp6sock, res0->ai_addr, res0->ai_addrlen)
== -1) {
close(tcp6sock);
tcp6sock = -1;
}
if (listen(tcp6sock, 5) == -1) {
close(tcp6sock);
tcp6sock = -1;
}
}
}
if (res0)
freeaddrinfo(res0);
if ((udp4sock == -1 || tcp4sock == -1) && (udp6sock == -1 ||
tcp6sock == -1))
fatalx("could not bind to 127.0.0.1 or ::1 on port 53");
if (udp4sock != -1)
main_imsg_compose_frontend_fd(IMSG_UDP4SOCK, 0, udp4sock);
if (udp6sock != -1)
main_imsg_compose_frontend_fd(IMSG_UDP6SOCK, 0, udp6sock);
if (tcp4sock != -1)
main_imsg_compose_frontend_fd(IMSG_TCP4SOCK, 0, tcp4sock);
if (tcp6sock != -1)
main_imsg_compose_frontend_fd(IMSG_TCP6SOCK, 0, tcp6sock);
}
void
solicit_dns_proposals(void)
{
struct rt_msghdr rtm;
struct iovec iov[1];
int iovcnt = 0;
memset(&rtm, 0, sizeof(rtm));
rtm.rtm_version = RTM_VERSION;
rtm.rtm_type = RTM_PROPOSAL;
rtm.rtm_msglen = sizeof(rtm);
rtm.rtm_tableid = 0;
rtm.rtm_index = 0;
rtm.rtm_seq = arc4random();
rtm.rtm_priority = RTP_PROPOSAL_SOLICIT;
iov[iovcnt].iov_base = &rtm;
iov[iovcnt++].iov_len = sizeof(rtm);
if (writev(routesock, iov, iovcnt) == -1)
log_warn("failed to send solicitation");
}
void
send_blocklist_fd(void)
{
int bl_fd;
if ((bl_fd = open(main_conf->blocklist_file, O_RDONLY)) != -1)
main_imsg_compose_frontend_fd(IMSG_BLFD, 0, bl_fd);
else
log_warn("%s", main_conf->blocklist_file);
}
void
imsg_receive_config(struct imsg *imsg, struct uw_conf **xconf)
{
struct uw_conf *nconf;
struct uw_forwarder *uw_forwarder;
struct force_tree_entry *force_entry;
nconf = *xconf;
switch (imsg->hdr.type) {
case IMSG_RECONF_CONF:
if (nconf != NULL)
fatalx("%s: IMSG_RECONF_CONF already in "
"progress", __func__);
if (IMSG_DATA_SIZE(*imsg) != sizeof(struct uw_conf))
fatalx("%s: IMSG_RECONF_CONF wrong length: %lu",
__func__, IMSG_DATA_SIZE(*imsg));
if ((*xconf = malloc(sizeof(struct uw_conf))) == NULL)
fatal(NULL);
nconf = *xconf;
memcpy(nconf, imsg->data, sizeof(struct uw_conf));
TAILQ_INIT(&nconf->uw_forwarder_list);
TAILQ_INIT(&nconf->uw_dot_forwarder_list);
RB_INIT(&nconf->force);
break;
case IMSG_RECONF_BLOCKLIST_FILE:
if (((char *)imsg->data)[IMSG_DATA_SIZE(*imsg) - 1] != '\0')
fatalx("Invalid blocklist file");
if ((nconf->blocklist_file = strdup(imsg->data)) ==
NULL)
fatal("%s: strdup", __func__);
break;
case IMSG_RECONF_FORWARDER:
if (IMSG_DATA_SIZE(*imsg) != sizeof(struct uw_forwarder))
fatalx("%s: IMSG_RECONF_FORWARDER wrong length:"
" %lu", __func__, IMSG_DATA_SIZE(*imsg));
if ((uw_forwarder = malloc(sizeof(struct
uw_forwarder))) == NULL)
fatal(NULL);
memcpy(uw_forwarder, imsg->data, sizeof(struct
uw_forwarder));
TAILQ_INSERT_TAIL(&nconf->uw_forwarder_list,
uw_forwarder, entry);
break;
case IMSG_RECONF_DOT_FORWARDER:
if (IMSG_DATA_SIZE(*imsg) != sizeof(struct uw_forwarder))
fatalx("%s: IMSG_RECONF_DOT_FORWARDER wrong "
"length: %lu", __func__,
IMSG_DATA_SIZE(*imsg));
if ((uw_forwarder = malloc(sizeof(struct
uw_forwarder))) == NULL)
fatal(NULL);
memcpy(uw_forwarder, imsg->data, sizeof(struct
uw_forwarder));
TAILQ_INSERT_TAIL(&nconf->uw_dot_forwarder_list,
uw_forwarder, entry);
break;
case IMSG_RECONF_FORCE:
if (IMSG_DATA_SIZE(*imsg) != sizeof(struct force_tree_entry))
fatalx("%s: IMSG_RECONF_FORCE wrong "
"length: %lu", __func__,
IMSG_DATA_SIZE(*imsg));
if ((force_entry = malloc(sizeof(struct
force_tree_entry))) == NULL)
fatal(NULL);
memcpy(force_entry, imsg->data, sizeof(struct
force_tree_entry));
if (RB_INSERT(force_tree, &nconf->force, force_entry) != NULL) {
free(force_entry);
fatalx("%s: IMSG_RECONF_FORCE duplicate entry",
__func__);
}
break;
default:
log_debug("%s: error handling imsg %d", __func__,
imsg->hdr.type);
break;
}
}