summaryrefslogtreecommitdiffstats
path: root/usr.sbin/rtsold/rtsol.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/rtsold/rtsol.c')
-rw-r--r--usr.sbin/rtsold/rtsol.c304
1 files changed, 279 insertions, 25 deletions
diff --git a/usr.sbin/rtsold/rtsol.c b/usr.sbin/rtsold/rtsol.c
index be2a9b8..2200fe8 100644
--- a/usr.sbin/rtsold/rtsol.c
+++ b/usr.sbin/rtsold/rtsol.c
@@ -50,6 +50,7 @@
#include <arpa/inet.h>
+#include <netdb.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
@@ -77,9 +78,24 @@ static struct sockaddr_in6 sin6_allrouters = {
.sin6_family = AF_INET6,
};
-static void call_script(char *, char *);
+struct script_msg {
+ TAILQ_ENTRY(script_msg) sm_next;
+
+ char *sm_msg;
+};
+
+static void call_script(char **, void *);
+static size_t dname_labeldec(char *, const char *);
static int safefile(const char *);
+#define _ARGS_OTHER otherconf_script, ifi->ifname
+#define _ARGS_RESCONF resolvconf_script, "-a", ifi->ifname
+#define CALL_SCRIPT(name, sm_head) \
+ do { \
+ char *sarg[] = { _ARGS_##name, NULL }; \
+ call_script(sarg, sm_head); \
+ } while(0);
+
int
sockopen(void)
{
@@ -234,17 +250,34 @@ rtsol_input(int s)
{
u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
int ifindex = 0, *hlimp = NULL;
- ssize_t i;
+ ssize_t msglen;
struct in6_pktinfo *pi = NULL;
struct ifinfo *ifi = NULL;
struct icmp6_hdr *icp;
struct nd_router_advert *nd_ra;
struct cmsghdr *cm;
+ char *raoptp;
+ char *p;
+ struct in6_addr *addr;
+ struct nd_opt_hdr *ndo;
+ struct nd_opt_rdnss *rdnss;
+ struct nd_opt_dnssl *dnssl;
+ size_t len;
+ struct script_msg *smp;
+ TAILQ_HEAD(, script_msg) sm_ns_head =
+ TAILQ_HEAD_INITIALIZER(sm_ns_head);
+ char nsbuf[11 + INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1 + 1];
+ /* 11 = sizeof("nameserver "), 1+1 = \n\0 termination */
+ TAILQ_HEAD(, script_msg) sm_sl_head =
+ TAILQ_HEAD_INITIALIZER(sm_sl_head);
+ char slbuf[7 + NI_MAXHOST + 1 + 1];
+ /* 7 = sizeof("search "), 1+1 = \n\0 termination */
+ char dname[NI_MAXHOST + 1];
/* get message. namelen and controllen must always be initialized. */
rcvmhdr.msg_namelen = sizeof(from);
rcvmhdr.msg_controllen = rcvcmsglen;
- if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) {
+ if ((msglen = recvmsg(s, &rcvmhdr, 0)) < 0) {
warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno));
return;
}
@@ -275,9 +308,9 @@ rtsol_input(int s)
return;
}
- if ((size_t)i < sizeof(struct nd_router_advert)) {
+ if ((size_t)msglen < sizeof(struct nd_router_advert)) {
warnmsg(LOG_INFO, __func__,
- "packet size(%zd) is too short", i);
+ "packet size(%zd) is too short", msglen);
return;
}
@@ -354,9 +387,166 @@ rtsol_input(int s)
warnmsg(LOG_DEBUG, __func__,
"OtherConfigFlag on %s is turned on", ifi->ifname);
ifi->otherconfig = 1;
- call_script(otherconf_script, ifi->ifname);
+ CALL_SCRIPT(OTHER, NULL);
}
+#define RA_OPT_NEXT_HDR(x) (struct nd_opt_hdr *)((char *)x + \
+ (((struct nd_opt_hdr *)x)->nd_opt_len * 8))
+ raoptp = (char *)icp + sizeof(struct nd_router_advert);
+
+ warnmsg(LOG_DEBUG, __func__, "Processing RA");
+ /* Process RA options. */
+ while (raoptp < (char *)icp + msglen) {
+ ndo = (struct nd_opt_hdr *)raoptp;
+ warnmsg(LOG_DEBUG, __func__, "ndo = %p", raoptp);
+ warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_type = %d",
+ ndo->nd_opt_type);
+ warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_len = %d",
+ ndo->nd_opt_len);
+
+ switch (ndo->nd_opt_type) {
+ case ND_OPT_RDNSS:
+ if (resolvconf_script == NULL)
+ break;
+ rdnss = (struct nd_opt_rdnss *)raoptp;
+ /* XXX: no lifetime handling now */
+
+ addr = (struct in6_addr *)(raoptp + sizeof(*rdnss));
+ while ((char *)addr < (char *)RA_OPT_NEXT_HDR(raoptp)) {
+ if (inet_ntop(AF_INET6, addr, ntopbuf,
+ INET6_ADDRSTRLEN) == NULL) {
+ warnmsg(LOG_INFO, __func__,
+ "an invalid address in RDNSS option "
+ "in RA from %s was ignored.",
+ inet_ntop(AF_INET6, &from.sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN));
+
+ continue;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(addr))
+ /* XXX: % has to be escaped here */
+ sprintf(nsbuf, "nameserver "
+ "%s%c%c%c%c%s\n",
+ ntopbuf,
+ SCOPE_DELIMITER,
+ SCOPE_DELIMITER,
+ SCOPE_DELIMITER,
+ SCOPE_DELIMITER,
+ ifi->ifname);
+ else
+ sprintf(nsbuf, "nameserver %s\n",
+ ntopbuf);
+ warnmsg(LOG_DEBUG, __func__, "nsbuf = %s",
+ nsbuf);
+
+ smp = malloc(sizeof(*smp));
+ if (smp == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "malloc failed: %s",
+ strerror(errno));
+ continue;
+ }
+ memset(smp, 0, sizeof(*smp));
+ smp->sm_msg = strdup(nsbuf);
+ if (smp->sm_msg == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "strdup failed: %s",
+ strerror(errno));
+ free(smp);
+ continue;
+ }
+ TAILQ_INSERT_TAIL(&sm_ns_head, smp, sm_next);
+ addr++;
+ }
+ break;
+ case ND_OPT_DNSSL:
+ if (resolvconf_script == NULL)
+ break;
+ dnssl = (struct nd_opt_dnssl *)raoptp;
+ /* XXX: no lifetime handling now */
+
+ if (TAILQ_EMPTY(&sm_sl_head)) {
+ smp = malloc(sizeof(*smp));
+ if (smp == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "malloc failed: %s",
+ strerror(errno));
+ break;
+ }
+ smp = malloc(sizeof(*smp));
+ smp->sm_msg = strdup("search ");
+ if (smp->sm_msg == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "strdup failed: %s",
+ strerror(errno));
+ free(smp);
+ break;
+ }
+ TAILQ_INSERT_TAIL(&sm_sl_head, smp, sm_next);
+ }
+
+ p = raoptp + sizeof(*dnssl);
+ while (0 < (len = dname_labeldec(dname, p))) {
+ sprintf(slbuf, "%s ", dname);
+ warnmsg(LOG_DEBUG, __func__, "slbuf = %s",
+ slbuf);
+
+ smp = malloc(sizeof(*smp));
+ if (smp == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "malloc failed: %s",
+ strerror(errno));
+ break;
+ }
+ memset(smp, 0, sizeof(*smp));
+ smp->sm_msg = strdup(slbuf);
+ if (smp->sm_msg == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "strdup failed: %s",
+ strerror(errno));
+ free(smp);
+ break;
+ }
+ TAILQ_INSERT_TAIL(&sm_sl_head, smp, sm_next);
+ p += len;
+ }
+ break;
+ default:
+ /* nothing to do for other options */
+ break;
+ }
+ raoptp = (char *)RA_OPT_NEXT_HDR(raoptp);
+ }
+ if (!TAILQ_EMPTY(&sm_sl_head)) {
+ smp = malloc(sizeof(*smp));
+ if (smp == NULL) {
+ warnmsg(LOG_ERR, __func__, "malloc failed: %s",
+ strerror(errno));
+ return;
+ }
+ smp = malloc(sizeof(*smp));
+ smp->sm_msg = strdup("\n");
+ if (smp->sm_msg == NULL) {
+ warnmsg(LOG_ERR, __func__, "strdup failed: %s",
+ strerror(errno));
+ free(smp);
+ return;
+ }
+ }
+ TAILQ_CONCAT(&sm_ns_head, &sm_sl_head, sm_next);
+ if (!TAILQ_EMPTY(&sm_ns_head) || !TAILQ_EMPTY(&sm_sl_head)) {
+ struct script_msg *sm_tmp;
+
+ CALL_SCRIPT(RESCONF, &sm_ns_head);
+
+ /* Clear script message queue. */
+ smp = TAILQ_FIRST(&sm_ns_head);
+ while(smp != NULL) {
+ sm_tmp = TAILQ_NEXT(smp, sm_next);
+ free(smp);
+ smp = sm_tmp;
+ }
+ }
ifi->racnt++;
switch (ifi->state) {
@@ -372,22 +562,56 @@ rtsol_input(int s)
}
static void
-call_script(char *scriptpath, char *ifname)
+call_script(char *argv[], void *head)
{
+ char *scriptpath;
+ int fd[2];
+ int error;
pid_t pid, wpid;
+ TAILQ_HEAD(, script_msg) *sm_head = NULL;
- if (scriptpath == NULL)
+ sm_head = head;
+ fd[0] = fd[1] = -1;
+ if ((scriptpath = argv[0]) == NULL)
return;
+ if (sm_head != NULL && !TAILQ_EMPTY(sm_head)) {
+ error = pipe(fd);
+ if (error) {
+ warnmsg(LOG_ERR, __func__,
+ "failed to create a pipe: %s", strerror(errno));
+ return;
+ }
+ }
+
/* launch the script */
pid = fork();
if (pid < 0) {
warnmsg(LOG_ERR, __func__,
"failed to fork: %s", strerror(errno));
return;
- } else if (pid) {
+ } else if (pid) { /* parent */
int wstatus;
+ if (fd[0] != -1) { /* Send message to the child if any. */
+ ssize_t len;
+ struct script_msg *smp;
+
+ close(fd[0]);
+ TAILQ_FOREACH(smp, sm_head, sm_next) {
+ len = strlen(smp->sm_msg);
+ warnmsg(LOG_DEBUG, __func__,
+ "write to child = %s(%d)",
+ smp->sm_msg, len);
+ if (write(fd[1], smp->sm_msg, len) != len) {
+ warnmsg(LOG_ERR, __func__,
+ "write to child failed: %s",
+ strerror(errno));
+ break;
+ }
+ }
+ close(fd[1]);
+ }
do {
wpid = wait(&wstatus);
} while (wpid != pid && wpid > 0);
@@ -399,13 +623,8 @@ call_script(char *scriptpath, char *ifname)
warnmsg(LOG_DEBUG, __func__,
"script \"%s\" terminated", scriptpath);
}
- } else {
- char *argv[3];
- int fd;
-
- argv[0] = scriptpath;
- argv[1] = ifname;
- argv[2] = NULL;
+ } else { /* child */
+ int nullfd;
if (safefile(scriptpath)) {
warnmsg(LOG_ERR, __func__,
@@ -413,20 +632,35 @@ call_script(char *scriptpath, char *ifname)
scriptpath);
exit(1);
}
-
- if ((fd = open("/dev/null", O_RDWR)) != -1) {
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- if (fd > STDERR_FILENO)
- close(fd);
+ nullfd = open("/dev/null", O_RDWR);
+ if (nullfd < 0) {
+ warnmsg(LOG_ERR, __func__,
+ "open /dev/null: %s", strerror(errno));
+ exit(1);
}
+ if (fd[0] != -1) { /* Receive message from STDIN if any. */
+ close(fd[1]);
+ if (fd[0] != STDIN_FILENO) {
+ /* Connect a pipe read-end to child's STDIN. */
+ if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) {
+ warnmsg(LOG_ERR, __func__,
+ "dup2 STDIN: %s", strerror(errno));
+ exit(1);
+ }
+ close(fd[0]);
+ }
+ } else
+ dup2(nullfd, STDIN_FILENO);
+
+ dup2(nullfd, STDOUT_FILENO);
+ dup2(nullfd, STDERR_FILENO);
+ if (nullfd > STDERR_FILENO)
+ close(nullfd);
execv(scriptpath, argv);
-
warnmsg(LOG_ERR, __func__, "child: exec failed: %s",
strerror(errno));
- exit(0);
+ exit(1);
}
return;
@@ -471,3 +705,23 @@ safefile(const char *path)
return (0);
}
+
+/* Decode domain name label encoding in RFC 1035 Section 3.1 */
+static size_t
+dname_labeldec(char *dst, const char *src)
+{
+ size_t len;
+ const char *src_origin;
+
+ src_origin = src;
+ while (*src && (len = (uint8_t)(*src++) & 0x3f) != 0) {
+ warnmsg(LOG_DEBUG, __func__, "labellen = %d", len);
+ memcpy(dst, src, len);
+ src += len;
+ dst += len;
+ if (*(dst - 1) == '\0')
+ break;
+ }
+
+ return (src - src_origin);
+}
OpenPOWER on IntegriCloud