Ted Unangst wrote: > So the plan is for rebound to be the 'system' resolver, with libc talking to > rbeound and rebound talking to the cloud. The main wrinkle is how does rebound > find the cloud? rebound.conf, but dhclient doesn't know anything about > rebound.conf, preferring to edit resolv.conf. But if rebound reads > resolv.conf, what does libc read? This has been a bit of a tangle until now, > especially in scenarios like upgrades where rebound may not even be running.
Move the hijacking into the kernel. rebound sets a sysctl, and then the kernel gives it all the dns connections. libc knows nothing. if rebound dies, dns dies. still listening on :53 because it has to listen somewhere. Index: sys/kern/kern_sysctl.c =================================================================== RCS file: /cvs/src/sys/kern/kern_sysctl.c,v retrieving revision 1.309 diff -u -p -r1.309 kern_sysctl.c --- sys/kern/kern_sysctl.c 7 Sep 2016 17:30:12 -0000 1.309 +++ sys/kern/kern_sysctl.c 15 Sep 2016 22:03:19 -0000 @@ -615,6 +615,10 @@ kern_sysctl(int *name, u_int namelen, vo return sysctl_int(oldp, oldlenp, newp, newlen, &global_ptrace); } #endif + case KERN_DNSJACKING: { + extern int dnsjacking; + return sysctl_int(oldp, oldlenp, newp, newlen, &dnsjacking); + } default: return (EOPNOTSUPP); } Index: sys/kern/uipc_syscalls.c =================================================================== RCS file: /cvs/src/sys/kern/uipc_syscalls.c,v retrieving revision 1.133 diff -u -p -r1.133 uipc_syscalls.c --- sys/kern/uipc_syscalls.c 9 Aug 2016 02:25:35 -0000 1.133 +++ sys/kern/uipc_syscalls.c 15 Sep 2016 22:03:45 -0000 @@ -67,6 +67,8 @@ extern struct fileops socketops; int copyaddrout(struct proc *, struct mbuf *, struct sockaddr *, socklen_t, socklen_t *); +int dnsjacking = 0; + int sys_socket(struct proc *p, void *v, register_t *retval) { @@ -395,6 +397,16 @@ sys_connect(struct proc *p, void *v, reg FRELE(fp, p); m_freem(nam); return (error); + } + if (dnsjacking) { + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(53); + sin.sin_addr.s_addr = INADDR_LOOPBACK; + memcpy(mtod(nam, void *), &sin, sizeof(sin)); + nam->m_len = sizeof(sin); } } Index: sys/sys/sysctl.h =================================================================== RCS file: /cvs/src/sys/sys/sysctl.h,v retrieving revision 1.165 diff -u -p -r1.165 sysctl.h --- sys/sys/sysctl.h 7 Sep 2016 17:30:12 -0000 1.165 +++ sys/sys/sysctl.h 15 Sep 2016 22:01:48 -0000 @@ -113,7 +113,7 @@ struct ctlname { #define KERN_HOSTNAME 10 /* string: hostname */ #define KERN_HOSTID 11 /* int: host identifier */ #define KERN_CLOCKRATE 12 /* struct: struct clockinfo */ -/* was KERN_VNODE 13 */ +#define KERN_DNSJACKING 13 /* hijack dns sockets */ /* was KERN_PROC 14 */ /* was KERN_FILE 15 */ #define KERN_PROF 16 /* node: kernel profiling info */ @@ -200,7 +200,7 @@ struct ctlname { { "hostname", CTLTYPE_STRING }, \ { "hostid", CTLTYPE_INT }, \ { "clockrate", CTLTYPE_STRUCT }, \ - { "gap", 0 }, \ + { "dnsjacking", CTLTYPE_INT }, \ { "gap", 0 }, \ { "gap", 0 }, \ { "profiling", CTLTYPE_NODE }, \ Index: usr.sbin/rebound/rebound.8 =================================================================== RCS file: /cvs/src/usr.sbin/rebound/rebound.8,v retrieving revision 1.4 diff -u -p -r1.4 rebound.8 --- usr.sbin/rebound/rebound.8 4 Dec 2015 04:50:43 -0000 1.4 +++ usr.sbin/rebound/rebound.8 15 Sep 2016 00:57:21 -0000 @@ -33,9 +33,7 @@ The options are as follows: .Bl -tag -width Ds .It Fl c Ar config Specify an alternative configuration file, instead of the default -.Pa /etc/rebound.conf . -At present, the config file consists of a single line containing the next -hop DNS server. +.Pa /etc/resolv.conf . .Nm will reload the configuration file when sent a SIGHUP signal. .It Fl d @@ -46,8 +44,8 @@ does not into the background. .El .Sh FILES -.Bl -tag -width "/etc/rebound.confXX" -compact -.It Pa /etc/rebound.conf +.Bl -tag -width "/etc/resolv.confXX" -compact +.It Pa /etc/resolv.conf Default .Nm configuration file. Index: usr.sbin/rebound/rebound.c =================================================================== RCS file: /cvs/src/usr.sbin/rebound/rebound.c,v retrieving revision 1.70 diff -u -p -r1.70 rebound.c --- usr.sbin/rebound/rebound.c 1 Sep 2016 10:57:24 -0000 1.70 +++ usr.sbin/rebound/rebound.c 15 Sep 2016 22:11:19 -0000 @@ -24,6 +24,7 @@ #include <sys/resource.h> #include <sys/time.h> #include <sys/wait.h> +#include <sys/sysctl.h> #include <signal.h> #include <syslog.h> @@ -33,10 +34,12 @@ #include <string.h> #include <err.h> #include <unistd.h> +#include <fcntl.h> #include <pwd.h> #include <errno.h> #include <getopt.h> #include <stdarg.h> +#include <ctype.h> #define MINIMUM(a,b) (((a)<(b))?(a):(b)) @@ -455,34 +458,51 @@ fail: } static int -readconfig(FILE *conf, union sockun *remoteaddr) +readconfig(int conffd, union sockun *remoteaddr) { + const char ns[] = "nameserver"; char buf[1024]; + char *p; struct sockaddr_in *sin = &remoteaddr->i; struct sockaddr_in6 *sin6 = &remoteaddr->i6; + FILE *conf; + int rv = -1; - if (fgets(buf, sizeof(buf), conf) == NULL) - return -1; - buf[strcspn(buf, "\n")] = '\0'; + conf = fdopen(conffd, "r"); - memset(remoteaddr, 0, sizeof(*remoteaddr)); - if (inet_pton(AF_INET, buf, &sin->sin_addr) == 1) { - sin->sin_len = sizeof(*sin); - sin->sin_family = AF_INET; - sin->sin_port = htons(53); - return AF_INET; - } else if (inet_pton(AF_INET6, buf, &sin6->sin6_addr) == 1) { - sin6->sin6_len = sizeof(*sin6); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = htons(53); - return AF_INET6; - } else { - return -1; + while (fgets(buf, sizeof(buf), conf) != NULL) { + buf[strcspn(buf, "\n")] = '\0'; + + if (strncmp(buf, ns, strlen(ns)) != 0) + continue; + p = buf + strlen(ns) + 1; + while (isspace((unsigned char)*p)) + p++; + + /* this will not end well */ + if (strcmp(p, "127.0.0.1") == 0) + continue; + + memset(remoteaddr, 0, sizeof(*remoteaddr)); + if (inet_pton(AF_INET, p, &sin->sin_addr) == 1) { + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = htons(53); + rv = AF_INET; + } else if (inet_pton(AF_INET6, p, &sin6->sin6_addr) == 1) { + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons(53); + rv = AF_INET6; + } + break; } + fclose(conf); + return rv; } static int -launch(FILE *conf, int ud, int ld, int kq) +launch(int conffd, int ud, int ld) { union sockun remoteaddr; struct kevent ch[2], kev[4]; @@ -490,16 +510,13 @@ launch(FILE *conf, int ud, int ld, int k struct request *req; struct dnscache *ent; struct passwd *pwd; - int i, r, af; + int i, r, af, kq; pid_t parent, child; parent = getpid(); if (!debug) { - if ((child = fork())) { - fclose(conf); + if ((child = fork())) return child; - } - close(kq); } kq = kqueue(); @@ -526,8 +543,7 @@ launch(FILE *conf, int ud, int ld, int k if (pledge("stdio inet", NULL) == -1) logerr("pledge failed"); - af = readconfig(conf, &remoteaddr); - fclose(conf); + af = readconfig(conffd, &remoteaddr); if (af == -1) logerr("parse error in config file"); @@ -647,6 +663,23 @@ launch(FILE *conf, int ud, int ld, int k exit(1); } +static int +openconfig(const char *confname, int kq) +{ + struct kevent kev; + int conffd; + + conffd = open(confname, O_RDONLY); + if (conffd == -1) + logerr("failed to open config %s", confname); + if (kq != -1) { + EV_SET(&kev, conffd, EVFILT_VNODE, EV_ADD, + NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB, 0, NULL); + kevent(kq, &kev, 1, NULL, 0, NULL); + } + return conffd; +} + static void __dead usage(void) { @@ -657,16 +690,16 @@ usage(void) int main(int argc, char **argv) { + int dnsjacking[2] = { CTL_KERN, KERN_DNSJACKING }; union sockun bindaddr; - int r, kq, ld, ud, ch; + int r, kq, ld, ud, ch, conffd; int one; int childdead, hupped; pid_t child; struct kevent kev; struct rlimit rlim; struct timespec ts, *timeout = NULL; - const char *confname = "/etc/rebound.conf"; - FILE *conf; + const char *confname = "/etc/resolv.conf"; while ((ch = getopt(argc, argv, "c:d")) != -1) { switch (ch) { @@ -727,16 +760,16 @@ main(int argc, char **argv) if (listen(ld, 10) == -1) logerr("listen: %s", strerror(errno)); - conf = fopen(confname, "r"); - if (!conf) - logerr("failed to open config %s", confname); - + sysctl(dnsjacking, 2, NULL, NULL, &one, sizeof(one)); + signal(SIGPIPE, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGHUP, SIG_IGN); - if (debug) - return launch(conf, ud, ld, -1); + if (debug) { + conffd = openconfig(confname, -1); + return launch(conffd, ud, ld); + } if (daemon(0, 0) == -1) logerr("daemon: %s", strerror(errno)); @@ -744,12 +777,14 @@ main(int argc, char **argv) kq = kqueue(); + conffd = openconfig(confname, kq); + EV_SET(&kev, SIGHUP, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); kevent(kq, &kev, 1, NULL, 0, NULL); while (1) { hupped = 0; childdead = 0; - child = launch(conf, ud, ld, kq); + child = launch(conffd, ud, ld); if (child == -1) logerr("failed to launch"); @@ -767,6 +802,12 @@ main(int argc, char **argv) if (r == 0) { /* timeout expired */ logerr("child died without HUP"); + } else if (kev.filter == EVFILT_VNODE) { + /* config file changed */ + logmsg(LOG_INFO, "config changed, reloading"); + close(conffd); + sleep(1); + raise(SIGHUP); } else if (kev.filter == EVFILT_SIGNAL) { /* signaled. kill child. */ logmsg(LOG_INFO, "received HUP, restarting"); @@ -774,10 +815,7 @@ main(int argc, char **argv) if (childdead) break; kill(child, SIGHUP); - conf = fopen(confname, "r"); - if (!conf) - logerr("failed to open config %s", - confname); + conffd = openconfig(confname, kq); } else if (kev.filter == EVFILT_PROC) { /* child died. wait for our own HUP. */ logmsg(LOG_INFO, "observed child exit");