Better patch, with validation in the domain name decoder function.
Still unsure how to proceed with regards to the objection to "less SMALL".
Chuck
Index: dump.c
===================================================================
RCS file: /cvs/src/usr.sbin/rtsold/dump.c,v
retrieving revision 1.15
diff -u -p -r1.15 dump.c
--- dump.c 21 Apr 2013 19:46:31 -0000 1.15
+++ dump.c 21 Sep 2014 02:37:15 -0000
@@ -33,6 +33,7 @@
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
+#include <sys/queue.h>
#include <net/if.h>
#include <netinet/in.h>
@@ -51,7 +52,6 @@ static FILE *fp;
extern struct ifinfo *iflist;
static void dump_interface_status(void);
-static char *sec2str(time_t);
char *ifstatstr[] = {"IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE"};
static void
@@ -108,7 +108,7 @@ rtsold_dump_file(char *dumpfile)
fclose(fp);
}
-static char *
+const char *
sec2str(time_t total)
{
static char result[256];
Index: rtsol.c
===================================================================
RCS file: /cvs/src/usr.sbin/rtsold/rtsol.c,v
retrieving revision 1.19
diff -u -p -r1.19 rtsol.c
--- rtsol.c 21 Oct 2013 09:58:14 -0000 1.19
+++ rtsol.c 21 Sep 2014 02:37:15 -0000
@@ -50,6 +50,7 @@
#include <arpa/inet.h>
+#include <netdb.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
@@ -59,6 +60,7 @@
#include <stdlib.h>
#include <syslog.h>
#include <fcntl.h>
+#include <ctype.h>
#include "rtsold.h"
@@ -71,13 +73,39 @@ static struct iovec sndiov[2];
static struct sockaddr_in6 from;
int rssock;
+static char rsid[IFNAMSIZ + 1 + sizeof(DNSINFO_ORIGIN_LABEL) + 1 + NI_MAXHOST];
static struct sockaddr_in6 sin6_allrouters = {sizeof(sin6_allrouters),
AF_INET6};
-#ifndef SMALL
-void call_script(char *, char *);
+static void call_script(const int, const char *const *,
+ struct script_msg_head_t *);
int safefile(const char *);
-#endif
+static size_t dname_labeldec(char *, size_t, const char *);
+static struct ra_opt *find_raopt(struct rainfo *, int, void *, size_t);
+static int ra_opt_rdnss_dispatch(struct ifinfo *, struct rainfo *,
+ struct script_msg_head_t *, struct script_msg_head_t *);
+static char *make_rsid(const char *, const char *, struct rainfo *);
+
+#define _ARGS_OTHER otherconf_script, ifi->ifname
+#define _ARGS_RESADD resolvconf_script, "-a", rsid
+#define _ARGS_RESDEL resolvconf_script, "-d", rsid
+
+#define CALL_SCRIPT(name, sm_head)
\
+ do { \
+ const char *const sarg[] = { _ARGS_##name, NULL }; \
+ call_script(sizeof(sarg), sarg, sm_head); \
+ } while(0)
+
+#define ELM_MALLOC(p,error_action)
\
+ do { \
+ p = malloc(sizeof(*p)); \
+ if (p == NULL) { \
+ warnmsg(LOG_ERR, __func__, "malloc failed: %s", \
+ strerror(errno)); \
+ error_action; \
+ } \
+ memset(p, 0, sizeof(*p)); \
+ } while(0)
int
sockopen(u_int rdomain)
@@ -226,18 +254,31 @@ void
rtsol_input(int s)
{
u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
- int ifindex = 0, *hlimp = NULL;
+ int l, ifindex = 0, *hlimp = NULL;
+ ssize_t msglen;
struct in6_pktinfo *pi = NULL;
struct ifinfo *ifi = NULL;
+ struct ra_opt *rao = NULL;
struct icmp6_hdr *icp;
struct cmsghdr *cm;
- ssize_t i;
-#ifndef SMALL
struct nd_router_advert *nd_ra;
-#endif
+ struct rainfo *rai;
+ 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;
+ char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1];
+ char dname[NI_MAXHOST];
+ struct timeval now;
+ struct timeval lifetime;
+ int newent_rai;
+ int newent_rao;
/* get message */
- if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) {
+ if ((msglen = recvmsg(s, &rcvmhdr, 0)) < 0) {
warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno));
return;
}
@@ -268,9 +309,9 @@ rtsol_input(int s)
return;
}
- if (i < sizeof(struct nd_router_advert)) {
+ if (msglen < sizeof(struct nd_router_advert)) {
warnmsg(LOG_ERR, __func__,
- "packet size(%zd) is too short", i);
+ "packet size(%zd) is too short", msglen);
return;
}
@@ -329,7 +370,6 @@ rtsol_input(int s)
inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, INET6_ADDRSTRLEN),
ifi->ifname, ifi->state);
-#ifndef SMALL
nd_ra = (struct nd_router_advert *)icp;
/*
@@ -344,9 +384,183 @@ 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);
+ }
+ gettimeofday(&now, NULL);
+ newent_rai = 0;
+ rai = find_rainfo(ifi, &from);
+ if (rai == NULL) {
+ ELM_MALLOC(rai, exit(1));
+ rai->rai_ifinfo = ifi;
+ TAILQ_INIT(&rai->rai_ra_opt);
+ rai->rai_saddr.sin6_family = AF_INET6;
+ rai->rai_saddr.sin6_len = sizeof(rai->rai_saddr);
+ memcpy(&rai->rai_saddr.sin6_addr, &from.sin6_addr,
+ sizeof(rai->rai_saddr.sin6_addr));
+ newent_rai = 1;
+ }
+
+#define RA_OPT_NEXT_HDR(x) (struct nd_opt_hdr *)((char *)x + \
+ (((struct nd_opt_hdr *)x)->nd_opt_len * 8))
+ /* Process RA options. */
+ warnmsg(LOG_DEBUG, __func__, "Processing RA");
+ raoptp = (char *)icp + sizeof(struct nd_router_advert);
+ 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:
+ warnmsg(LOG_DEBUG, __func__, "RDNSS option");
+ rdnss = (struct nd_opt_rdnss *)raoptp;
+
+ /* Optlen sanity check (Section 5.3.1 in RFC 6106) */
+ if (rdnss->nd_opt_rdnss_len < 3) {
+ warnmsg(LOG_INFO, __func__,
+ "too short RDNSS option"
+ "in RA from %s was ignored.",
+ inet_ntop(AF_INET6, &from.sin6_addr,
+ ntopbuf, sizeof(ntopbuf)));
+ break;
+ }
+
+ addr = (struct in6_addr *)(void *)(raoptp +
sizeof(*rdnss));
+ while ((char *)addr < (char *)RA_OPT_NEXT_HDR(raoptp)) {
+ if (inet_ntop(AF_INET6, addr, ntopbuf,
+ sizeof(ntopbuf)) == 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, sizeof(ntopbuf)));
+ addr++;
+ continue;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(addr))
+ /* XXX: % has to be escaped here */
+ l = snprintf(nsbuf, sizeof(nsbuf),
+ "%s%c%s", ntopbuf,
+ SCOPE_DELIMITER,
+ ifi->ifname);
+ else
+ l = snprintf(nsbuf, sizeof(nsbuf),
+ "%s", ntopbuf);
+ if (l < 0 || (size_t)l >= sizeof(nsbuf)) {
+ warnmsg(LOG_ERR, __func__,
+ "address copying error in "
+ "RDNSS option: %d.", l);
+ addr++;
+ continue;
+ }
+ warnmsg(LOG_DEBUG, __func__, "nsbuf = %s",
+ nsbuf);
+
+ newent_rao = 0;
+ rao = find_raopt(rai, ndo->nd_opt_type, nsbuf,
+ strlen(nsbuf));
+ if (rao == NULL) {
+ ELM_MALLOC(rao, break);
+ rao->rao_type = ndo->nd_opt_type;
+ rao->rao_len = strlen(nsbuf);
+ rao->rao_msg = strdup(nsbuf);
+ if (rao->rao_msg == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "strdup failed: %s",
+ strerror(errno));
+ free(rao);
+ addr++;
+ continue;
+ }
+ newent_rao = 1;
+ }
+ /* Set expiration timer */
+ memset(&rao->rao_expire, 0,
+ sizeof(rao->rao_expire));
+ memset(&lifetime, 0, sizeof(lifetime));
+ lifetime.tv_sec =
+ ntohl(rdnss->nd_opt_rdnss_lifetime);
+ timeradd(&now, &lifetime, &rao->rao_expire);
+
+ if (newent_rao)
+ TAILQ_INSERT_TAIL(&rai->rai_ra_opt,
+ rao, rao_next);
+ addr++;
+ }
+ break;
+ case ND_OPT_DNSSL:
+ warnmsg(LOG_DEBUG, __func__, "DNSSL option");
+ dnssl = (struct nd_opt_dnssl *)raoptp;
+
+ /* Optlen sanity check (Section 5.3.1 in RFC 6106) */
+ if (dnssl->nd_opt_dnssl_len < 2) {
+ warnmsg(LOG_INFO, __func__,
+ "too short DNSSL option"
+ "in RA from %s was ignored.",
+ inet_ntop(AF_INET6, &from.sin6_addr,
+ ntopbuf, sizeof(ntopbuf)));
+ break;
+ }
+
+ /*
+ * Ensure NUL-termination in DNSSL in case of
+ * malformed field.
+ */
+ p = (char *)RA_OPT_NEXT_HDR(raoptp);
+ *(p - 1) = '\0';
+
+ p = raoptp + sizeof(*dnssl);
+ while (1 < (len = dname_labeldec(dname, sizeof(dname),
+ p))) {
+ /* length == 1 means empty string */
+ warnmsg(LOG_DEBUG, __func__, "dname = %s",
+ dname);
+
+ newent_rao = 0;
+ rao = find_raopt(rai, ndo->nd_opt_type, dname,
+ strlen(dname));
+ if (rao == NULL) {
+ ELM_MALLOC(rao, break);
+ rao->rao_type = ndo->nd_opt_type;
+ rao->rao_len = strlen(dname);
+ rao->rao_msg = strdup(dname);
+ if (rao->rao_msg == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "strdup failed: %s",
+ strerror(errno));
+ free(rao);
+ addr++;
+ continue;
+ }
+ newent_rao = 1;
+ }
+ /* Set expiration timer */
+ memset(&rao->rao_expire, 0,
+ sizeof(rao->rao_expire));
+ memset(&lifetime, 0, sizeof(lifetime));
+ lifetime.tv_sec =
+ ntohl(dnssl->nd_opt_dnssl_lifetime);
+ timeradd(&now, &lifetime, &rao->rao_expire);
+
+ if (newent_rao)
+ TAILQ_INSERT_TAIL(&rai->rai_ra_opt,
+ rao, rao_next);
+ p += len;
+ }
+ break;
+ default:
+ /* nothing to do for other options */
+ break;
+ }
+ raoptp = (char *)RA_OPT_NEXT_HDR(raoptp);
}
-#endif
+ if (newent_rai)
+ TAILQ_INSERT_TAIL(&ifi->ifi_rainfo, rai, rai_next);
+
+ ra_opt_handler(ifi);
ifi->racnt++;
@@ -362,15 +576,242 @@ rtsol_input(int s)
}
}
-#ifndef SMALL
-void
-call_script(char *scriptpath, char *ifname)
+static char resstr_ns_prefix[] = "nameserver ";
+static char resstr_sh_prefix[] = "search ";
+static char resstr_nl[] = "\n";
+static char resstr_sp[] = " ";
+
+int
+ra_opt_handler(struct ifinfo *ifi)
{
+ struct ra_opt *rao;
+ struct rainfo *rai;
+ struct script_msg *smp1, *smp2, *smp3;
+ struct timeval now;
+ struct script_msg_head_t sm_rdnss_head =
+ TAILQ_HEAD_INITIALIZER(sm_rdnss_head);
+ struct script_msg_head_t sm_dnssl_head =
+ TAILQ_HEAD_INITIALIZER(sm_dnssl_head);
+
+ int dcount, dlen;
+
+ dcount = 0;
+ dlen = strlen(resstr_sh_prefix) + strlen(resstr_nl);
+ gettimeofday(&now, NULL);
+
+ /*
+ * All options from multiple RAs with the same or different
+ * source addresses on a single interface will be gathered and
+ * handled, not overridden. [RFC 4861 6.3.4]
+ */
+ TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) {
+ TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) {
+ switch (rao->rao_type) {
+ case ND_OPT_RDNSS:
+ if (timercmp(&now, &rao->rao_expire, >)) {
+ warnmsg(LOG_INFO, __func__,
+ "expired rdnss entry: %s",
+ (char *)rao->rao_msg);
+ break;
+ }
+ ELM_MALLOC(smp1, continue);
+ ELM_MALLOC(smp2, goto free1);
+ ELM_MALLOC(smp3, goto free2);
+ smp1->sm_msg = resstr_ns_prefix;
+ TAILQ_INSERT_TAIL(&sm_rdnss_head, smp1,
+ sm_next);
+ smp2->sm_msg = rao->rao_msg;
+ TAILQ_INSERT_TAIL(&sm_rdnss_head, smp2,
+ sm_next);
+ smp3->sm_msg = resstr_nl;
+ TAILQ_INSERT_TAIL(&sm_rdnss_head, smp3,
+ sm_next);
+ ifi->ifi_rdnss = IFI_DNSOPT_STATE_RECEIVED;
+
+ break;
+ case ND_OPT_DNSSL:
+ if (timercmp(&now, &rao->rao_expire, >)) {
+ warnmsg(LOG_INFO, __func__,
+ "expired dnssl entry: %s",
+ (char *)rao->rao_msg);
+ break;
+ }
+ dcount++;
+ /* Check resolv.conf(5) restrictions. */
+ if (dcount > 6) {
+ warnmsg(LOG_INFO, __func__,
+ "dnssl entry exceeding maximum
count (%d>6)"
+ ": %s", dcount, (char
*)rao->rao_msg);
+ break;
+ }
+ if (256 < dlen + strlen(rao->rao_msg) +
+ strlen(resstr_sp)) {
+ warnmsg(LOG_INFO, __func__,
+ "dnssl entry exceeding maximum
length "
+ "(>256): %s", (char *)rao->rao_msg);
+ break;
+ }
+ ELM_MALLOC(smp1, continue);
+ ELM_MALLOC(smp2, goto free1);
+ if (TAILQ_EMPTY(&sm_dnssl_head)) {
+ ELM_MALLOC(smp3, goto free2);
+ smp3->sm_msg = resstr_sh_prefix;
+ TAILQ_INSERT_TAIL(&sm_dnssl_head, smp3,
+ sm_next);
+ }
+ smp1->sm_msg = rao->rao_msg;
+ TAILQ_INSERT_TAIL(&sm_dnssl_head, smp1,
+ sm_next);
+ smp2->sm_msg = resstr_sp;
+ TAILQ_INSERT_TAIL(&sm_dnssl_head, smp2,
+ sm_next);
+ dlen += strlen(rao->rao_msg) +
+ strlen(resstr_sp);
+ break;
+
+ ifi->ifi_dnssl = IFI_DNSOPT_STATE_RECEIVED;
+ default:
+ break;
+ }
+ continue;
+free2:
+ free(smp2);
+free1:
+ free(smp1);
+ }
+ /* Call the script for each information source. */
+ if (uflag)
+ ra_opt_rdnss_dispatch(ifi, rai, &sm_rdnss_head,
+ &sm_dnssl_head);
+ }
+ /* Call the script for each interface. */
+ if (!uflag)
+ ra_opt_rdnss_dispatch(ifi, NULL, &sm_rdnss_head,
+ &sm_dnssl_head);
+ return (0);
+}
+
+char *
+make_rsid(const char *ifname, const char *origin, struct rainfo *rai)
+{
+ char hbuf[NI_MAXHOST];
+
+ if (rai == NULL)
+ snprintf(rsid, sizeof(rsid), "%s:%s", ifname, origin);
+ else {
+ if (!IN6_IS_ADDR_LINKLOCAL(&rai->rai_saddr.sin6_addr))
+ return (NULL);
+ if (getnameinfo((struct sockaddr *)&rai->rai_saddr,
+ rai->rai_saddr.sin6_len, hbuf, sizeof(hbuf), NULL, 0,
+ NI_NUMERICHOST) != 0)
+ return (NULL);
+ snprintf(rsid, sizeof(rsid), "%s:%s:[%s]", ifname, origin,
hbuf);
+ }
+ warnmsg(LOG_DEBUG, __func__, "rsid = [%s]", rsid);
+ return (rsid);
+}
+
+int
+ra_opt_rdnss_dispatch(struct ifinfo *ifi,
+ struct rainfo *rai,
+ struct script_msg_head_t *sm_rdnss_head,
+ struct script_msg_head_t *sm_dnssl_head)
+{
+ const char *r;
+ struct script_msg *smp1, *dnssl, *dnssl_tmp;
+ int error;
+
+ error = 0;
+
+ /* Add \n for DNSSL list. */
+ if (!TAILQ_EMPTY(sm_dnssl_head)) {
+ ELM_MALLOC(smp1, goto ra_opt_rdnss_freeit);
+ smp1->sm_msg = resstr_nl;
+ TAILQ_INSERT_TAIL(sm_dnssl_head, smp1, sm_next);
+ }
+ /* Concatenate the search list elements onto the server list
+ elements.
+ */
+
+ TAILQ_FOREACH_SAFE(dnssl, sm_dnssl_head, sm_next, dnssl_tmp) {
+ TAILQ_REMOVE(sm_dnssl_head, dnssl, sm_next);
+ TAILQ_INSERT_TAIL(sm_rdnss_head, dnssl, sm_next);
+ }
+
+ if (rai != NULL && uflag)
+ r = make_rsid(ifi->ifname, DNSINFO_ORIGIN_LABEL, rai);
+ else
+ r = make_rsid(ifi->ifname, DNSINFO_ORIGIN_LABEL, NULL);
+ if (r == NULL) {
+ warnmsg(LOG_ERR, __func__, "make_rsid() failed. "
+ "Script was not invoked.");
+ error = 1;
+ goto ra_opt_rdnss_freeit;
+ }
+ if (!TAILQ_EMPTY(sm_rdnss_head))
+ CALL_SCRIPT(RESADD, sm_rdnss_head);
+ else if (ifi->ifi_rdnss == IFI_DNSOPT_STATE_RECEIVED ||
+ ifi->ifi_dnssl == IFI_DNSOPT_STATE_RECEIVED) {
+ CALL_SCRIPT(RESDEL, NULL);
+ ifi->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO;
+ ifi->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO;
+ }
+
+ra_opt_rdnss_freeit:
+ /* Clear script message queue. */
+ if (!TAILQ_EMPTY(sm_rdnss_head)) {
+ while ((smp1 = TAILQ_FIRST(sm_rdnss_head)) != NULL) {
+ TAILQ_REMOVE(sm_rdnss_head, smp1, sm_next);
+ free(smp1);
+ }
+ }
+ if (!TAILQ_EMPTY(sm_dnssl_head)) {
+ while ((smp1 = TAILQ_FIRST(sm_dnssl_head)) != NULL) {
+ TAILQ_REMOVE(sm_dnssl_head, smp1, sm_next);
+ free(smp1);
+ }
+ }
+
+ return (error);
+}
+
+static struct ra_opt *
+find_raopt(struct rainfo *rai, int type, void *msg, size_t len)
+{
+ struct ra_opt *rao;
+
+ TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) {
+ if (rao->rao_type == type &&
+ rao->rao_len == strlen(msg) &&
+ memcmp(rao->rao_msg, msg, len) == 0)
+ break;
+ }
+
+ return (rao);
+}
+
+static void
+call_script(const int argc, const char *const argv[],
+ struct script_msg_head_t *sm_head)
+{
+ const char *scriptpath;
+ int fd[2];
+ int error;
pid_t pid, wpid;
- if (scriptpath == NULL)
+ if ((scriptpath = argv[0]) == NULL)
return;
+ fd[0] = fd[1] = -1;
+ 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) {
@@ -380,6 +821,25 @@ call_script(char *scriptpath, char *ifna
} else if (pid) {
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(%zd)",
+ 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);
@@ -392,12 +852,8 @@ call_script(char *scriptpath, char *ifna
"script \"%s\" terminated", scriptpath);
}
} else {
- char *argv[3];
- int fd;
-
- argv[0] = scriptpath;
- argv[1] = ifname;
- argv[2] = NULL;
+ int nullfd;
+ char **_argv;
if (safefile(scriptpath) != 0) {
warnmsg(LOG_ERR, __func__,
@@ -405,17 +861,39 @@ call_script(char *scriptpath, char *ifna
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);
-
+ _argv = malloc(sizeof(*_argv) * argc);
+ if (_argv == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "malloc: %s", strerror(errno));
+ exit(1);
+ }
+ memcpy(_argv, argv, (size_t)argc);
+ execv(scriptpath, (char *const *)_argv);
warnmsg(LOG_ERR, __func__, "child: exec failed: %s",
strerror(errno));
exit(1);
@@ -463,4 +941,42 @@ safefile(const char *path)
return (0);
}
-#endif
+
+/* Decode domain name label encoding in RFC 1035 Section 3.1 */
+static size_t
+dname_labeldec(char *dst, size_t dlen, const char *src)
+{
+ size_t len;
+ const char *src_origin;
+ const char *src_last;
+ const char *dst_origin;
+ const char *dec;
+
+ src_origin = src;
+ src_last = strchr(src, '\0');
+ dst_origin = dst;
+ memset(dst, '\0', dlen);
+ while (src && (len = (uint8_t)(*src++) & 0x3f) &&
+ (src + len) <= src_last) {
+ if (dst != dst_origin)
+ *dst++ = '.';
+ warnmsg(LOG_DEBUG, __func__, "labellen = %zd", len);
+ memcpy(dst, src, len);
+ src += len;
+ dst += len;
+ }
+ *dst = '\0';
+
+ /* Check for valid domain name characters. On failure, return value
+ that caller interprets as an empty string.
+ */
+ for (dec = dst_origin; *dec != '\0'; dec++) {
+ if ( !(isalnum(*dec) || *dec == '.' || *dec == '-' )) {
+ warnmsg(LOG_WARNING, __func__,
+ "encountered domain with invalid char");
+ return(1);
+ }
+ }
+
+ return (src - src_origin);
+}
Index: rtsold.8
===================================================================
RCS file: /cvs/src/usr.sbin/rtsold/rtsold.8,v
retrieving revision 1.33
diff -u -p -r1.33 rtsold.8
--- rtsold.8 27 Aug 2014 14:04:16 -0000 1.33
+++ rtsold.8 21 Sep 2014 02:37:15 -0000
@@ -38,19 +38,25 @@
.\"
.Sh SYNOPSIS
.Nm rtsold
-.Op Fl 1DdFfm
+.Op Fl 1DdFfmu
.Op Fl O Ar script-name
+.Op Fl R Ar script-name
.Ar interface ...
.Nm rtsold
.Op Fl 1DdFfm
+.Op Fl O Ar script-name
+.Op Fl R Ar script-name
.Fl a
.Pp
.Nm rtsol
-.Op Fl DdF
+.Op Fl DdFu
.Op Fl O Ar script-name
+.Op Fl R Ar script-name
.Ar interface ...
.Nm rtsol
.Op Fl DdF
+.Op Fl O Ar script-name
+.Op Fl R Ar script-name
.Fl a
.\"
.Sh DESCRIPTION
@@ -137,6 +143,15 @@ When sending a Router Solicitation on an
includes a Source Link-layer address option if the interface
has a link-layer address.
.Pp
+If
+.Nm
+receives a Router Advertisement with RDNSS (Recursive DNS Server) or
+DNSSL (DNS Search List) options, it invokes a script, passing the options
+on standard input. By default, the resolvconf(8) script is invoked. This can
+be overridden with the
+.Fl R
+option.
+.Pp
.Nm
is able to do some additional configuration for interfaces
where more than setting the host's address is needed.
@@ -216,6 +231,30 @@ should be specified as the absolute path
and the file itself should be a regular file
and owned by the same user running
.Nm .
+.It Fl R Ar script-name
+Specifies a script to run when router advertisement options RDNSS
+(Recursive DNS Server) or DNSSL (DNS Search List) are encountered. The
+information of DNS servers and DNS search domains will be sent to standard
+input of this script.
+.Ar script-name
+should be specified as the absolute path from root to the script file,
+and the file itself should be a regular file and owned by the same user
+running
+.Nm .
+The
+resolvconf(8)
+script is used by default.
+.It Fl u
+Adds the source address of Router Advertisement messages to the interface
+name in an argument of the RDNSS and DNSSL script.
+.Pp
+If
+.Fl u
+is specified, the interface name in the script argument will be
+.Ql ifname:slaac:[RA-source-address] .
+.Pp
+If not, it will be
+.Ql ifname:slaac .
.El
.\"
.Sh FILES
Index: rtsold.c
===================================================================
RCS file: /cvs/src/usr.sbin/rtsold/rtsold.c,v
retrieving revision 1.53
diff -u -p -r1.53 rtsold.c
--- rtsold.c 27 Aug 2014 14:04:16 -0000 1.53
+++ rtsold.c 21 Sep 2014 02:37:15 -0000
@@ -33,6 +33,7 @@
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
+#include <sys/queue.h>
#include <sys/param.h>
#include <net/if.h>
@@ -60,11 +61,13 @@ struct ifinfo *iflist;
static int log_upto = 999;
static int fflag = 0;
static int Fflag = 0; /* force setting sysctl parameters */
+int uflag = 0;
int aflag = 0;
int dflag = 0;
char *otherconf_script;
+const char *resolvconf_script = "/sbin/resolvconf";
/* protocol constants */
#define MAX_RTR_SOLICITATION_DELAY 1 /* second */
@@ -116,9 +119,9 @@ main(int argc, char *argv[])
if (argv0 && argv0[0] != '\0' && argv0[strlen(argv0) - 1] != 'd') {
fflag = 1;
once = 1;
- opts = "adDFO:";
+ opts = "adDFuO:R:";
} else
- opts = "adDfFm1O:";
+ opts = "adDfFmu1O:R:";
while ((ch = getopt(argc, argv, opts)) != -1) {
switch (ch) {
@@ -143,11 +146,15 @@ main(int argc, char *argv[])
case '1':
once = 1;
break;
-#ifndef SMALL
case 'O':
otherconf_script = optarg;
break;
-#endif
+ case 'R':
+ resolvconf_script = optarg;
+ break;
+ case 'u':
+ uflag = 1;
+ break;
default:
usage(argv0);
/*NOTREACHED*/
@@ -177,12 +184,10 @@ main(int argc, char *argv[])
setlogmask(LOG_UPTO(log_upto));
}
-#ifndef SMALL
if (otherconf_script && *otherconf_script != '/') {
errx(1, "configuration script (%s) must be an absolute path",
otherconf_script);
}
-#endif
if (Fflag)
setinet6sysctl(IPPROTO_IPV6, IPV6CTL_FORWARDING, 0);
@@ -322,8 +327,10 @@ ifconfig(char *ifname)
return(-1);
}
ifinfo->sdl = sdl;
-
- strncpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname));
+ ifinfo->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO;
+ ifinfo->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO;
+ TAILQ_INIT(&ifinfo->ifi_rainfo);
+ strlcpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname));
/* construct a router solicitation message */
if (make_packet(ifinfo))
@@ -402,6 +409,19 @@ ifreconfig(char *ifname)
}
#endif
+struct rainfo *
+find_rainfo(struct ifinfo *ifi, struct sockaddr_in6 *sin6)
+{
+ struct rainfo *rai;
+
+ TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next)
+ if (memcmp(&rai->rai_saddr.sin6_addr, &sin6->sin6_addr,
+ sizeof(rai->rai_saddr.sin6_addr)) == 0)
+ return (rai);
+
+ return (NULL);
+}
+
struct ifinfo *
find_ifinfo(int ifindex)
{
@@ -457,6 +477,8 @@ rtsol_check_timer(void)
static struct timeval returnval;
struct timeval now, rtsol_timer;
struct ifinfo *ifinfo;
+ struct rainfo *rai;
+ struct ra_opt *rao;
int flags, timers;
gettimeofday(&now, NULL);
@@ -471,6 +493,20 @@ rtsol_check_timer(void)
"state = %d", ifinfo->ifname,
ifinfo->state);
+ while((rai = TAILQ_FIRST(&ifinfo->ifi_rainfo)) != NULL)
{
+ /* Remove all RA options. */
+ TAILQ_REMOVE(&ifinfo->ifi_rainfo, rai,
rai_next);
+ while ((rao = TAILQ_FIRST(&rai->rai_ra_opt)) !=
+ NULL) {
+ TAILQ_REMOVE(&rai->rai_ra_opt, rao,
+ rao_next);
+ if (rao->rao_msg != NULL)
+ free(rao->rao_msg);
+ free(rao);
+ }
+ free(rai);
+ }
+
switch (ifinfo->state) {
case IFS_DOWN:
case IFS_TENTATIVE:
@@ -508,14 +544,12 @@ rtsol_check_timer(void)
ifinfo->state = IFS_PROBE;
}
-#ifndef SMALL
/*
* If we need a probe, clear the previous
* status wrt the "other" configuration.
*/
if (probe)
ifinfo->otherconfig = 0;
-#endif
if (probe && mobile_node)
defrouter_probe(ifinfo);
@@ -538,6 +572,33 @@ rtsol_check_timer(void)
break;
}
rtsol_timer_update(ifinfo);
+ } else {
+ /* Expiration check for RA options. */
+ int expire = 0;
+
+ TAILQ_FOREACH(rai, &ifinfo->ifi_rainfo, rai_next) {
+ TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) {
+ warnmsg(LOG_DEBUG, __func__,
+ "RA expiration timer: "
+ "type=%d, msg=%s, expire=%s",
+ rao->rao_type, (char *)rao->rao_msg,
+
sec2str(rao->rao_expire.tv_sec));
+ if (timercmp(&now, &rao->rao_expire,
+ >=)) {
+ warnmsg(LOG_DEBUG, __func__,
+ "RA expiration timer: "
+ "expired.");
+ TAILQ_REMOVE(&rai->rai_ra_opt,
+ rao, rao_next);
+ if (rao->rao_msg != NULL)
+ free(rao->rao_msg);
+ free(rao);
+ expire = 1;
+ }
+ }
+ }
+ if (expire)
+ ra_opt_handler(ifinfo);
}
if (!ifinfo->stoptimer) {
if (timers == 0)
@@ -652,12 +713,14 @@ usage(char *progname)
{
if (progname && progname[0] != '\0' && progname[strlen(progname) - 1]
!= 'd') {
fprintf(stderr,
- "usage: rtsol [-DdF] [-O script-name] interface ...\n"
- " rtsol [-DdF] -a\n");
+ "usage: rtsol [-DdFu] [-O script-name] [-R script-name] "
+ "interface ...\n"
+ " rtsol [-DdFu] [-O script-name] [-R script-name]
-a\n");
} else {
fprintf(stderr,
- "usage: rtsold [-1DdFfm] [-O script-name] interface ...\n"
- " rtsold [-1DdFfm] -a\n");
+ "usage: rtsold [-1DdFfmu] [-O script-name] [-R script-name]
"
+ "interface ...\n"
+ " rtsold [-1DdFfmu] [-O script-name] [-R script-name]
-a\n");
}
exit(1);
}
Index: rtsold.h
===================================================================
RCS file: /cvs/src/usr.sbin/rtsold/rtsold.h,v
retrieving revision 1.17
diff -u -p -r1.17 rtsold.h
--- rtsold.h 12 Nov 2013 22:27:13 -0000 1.17
+++ rtsold.h 21 Sep 2014 02:37:15 -0000
@@ -30,6 +30,33 @@
* SUCH DAMAGE.
*/
+struct script_msg {
+ TAILQ_ENTRY(script_msg) sm_next;
+
+ char *sm_msg;
+};
+
+TAILQ_HEAD(script_msg_head_t, script_msg);
+
+struct ra_opt {
+ TAILQ_ENTRY(ra_opt) rao_next;
+
+ u_int8_t rao_type;
+ struct timeval rao_expire;
+ size_t rao_len;
+ void *rao_msg;
+};
+
+TAILQ_HEAD(rainfo_head, ra_opt);
+
+struct rainfo {
+ TAILQ_ENTRY(rainfo) rai_next;
+
+ struct ifinfo *rai_ifinfo;
+ struct sockaddr_in6 rai_saddr;
+ TAILQ_HEAD(, ra_opt) rai_ra_opt;
+};
+
struct ifinfo {
struct ifinfo *next; /* pointer to the next interface */
@@ -49,8 +76,13 @@ struct ifinfo {
struct timeval expire;
int stoptimer;
int errors; /* # of errors we've got - detect wedge */
+#define IFI_DNSOPT_STATE_NOINFO 0
+#define IFI_DNSOPT_STATE_RECEIVED 1
+ int ifi_rdnss; /* RDNSS option state */
+ int ifi_dnssl; /* DNSSL option state */
int racnt; /* total # of valid RAs it have got */
+ TAILQ_HEAD(, rainfo) ifi_rainfo;
size_t rs_datalen;
u_char *rs_data;
@@ -63,14 +95,20 @@ struct ifinfo {
#define IFS_DOWN 3
#define IFS_TENTATIVE 4
+#define DNSINFO_ORIGIN_LABEL "slaac"
+
/* rtsold.c */
extern int dflag;
+extern int uflag;
extern char *otherconf_script;
+extern const char *resolvconf_script;
struct ifinfo *find_ifinfo(int ifindex);
+struct rainfo *find_rainfo(struct ifinfo *, struct sockaddr_in6 *);
void rtsol_timer_update(struct ifinfo *);
extern void warnmsg(int, const char *, const char *, ...)
__attribute__((__format__(__printf__, 3, 4)));
extern char **autoifprobe(u_int);
+extern int ra_opt_handler(struct ifinfo *);
/* if.c */
extern int ifinit(void);
@@ -94,3 +132,4 @@ extern void defrouter_probe(struct ifinf
/* dump.c */
extern void rtsold_dump_file(char *);
+extern const char *sec2str(time_t);