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);


Reply via email to