]> git.saurik.com Git - apple/network_cmds.git/blobdiff - ndp.tproj/ndp.c
network_cmds-396.6.tar.gz
[apple/network_cmds.git] / ndp.tproj / ndp.c
index d1a55b758d71d39aa3317cb581ffd645d60f7238..76770171c26164fe2934f3b6122a005d970f44a9 100644 (file)
@@ -1,3 +1,31 @@
+/*
+ * Copyright (c) 2009-2012 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
 /*     $FreeBSD: src/usr.sbin/ndp/ndp.c,v 1.2.2.5 2001/08/13 02:58:26 sumikawa Exp $   */
 /*     $KAME: ndp.c,v 1.65 2001/05/08 04:36:34 itojun Exp $    */
 
  * ndp - display, set, delete and flush neighbor cache
  */
 
-
+#include <stdint.h>
 #include <sys/param.h>
 #include <sys/file.h>
 #include <sys/ioctl.h>
 
 /* packing rule for routing socket */
 #define ROUNDUP(a) \
-       ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+       ((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))
 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
 
 static int pid;
@@ -141,6 +169,7 @@ int set __P((int, char **));
 void get __P((char *));
 int delete __P((char *));
 void dump __P((struct in6_addr *));
+void dump_ext __P((struct in6_addr *, int));
 static struct in6_nbrinfo *getnbrinfo __P((struct in6_addr *addr,
                                           int ifindex, int));
 static char *ether_str __P((struct sockaddr_dl *));
@@ -176,11 +205,12 @@ main(argc, argv)
 {
        int ch;
        int aflag = 0, dflag = 0, sflag = 0, Hflag = 0,
-               pflag = 0, rflag = 0, Pflag = 0, Rflag = 0;
+               pflag = 0, rflag = 0, Pflag = 0, Rflag = 0, lflag = 0,
+               xflag = 0;
 
        pid = getpid();
 //     thiszone = gmt2local(0);
-       while ((ch = getopt(argc, argv, "acndfIilprstA:HPR")) != -1)
+       while ((ch = getopt(argc, argv, "acndfIilprstA:HPRx")) != -1)
                switch ((char)ch) {
                case 'a':
                        aflag = 1;
@@ -220,7 +250,7 @@ main(argc, argv)
                        file(argv[2]);
                        exit(0);
                case 'l' :
-                       /* obsolete, ignored */
+                       lflag = 1;
                        break;
                case 'r' :
                        rflag = 1;
@@ -246,6 +276,10 @@ main(argc, argv)
                case 'R':
                        Rflag = 1;
                        break;
+               case 'x':
+                       xflag = 1;
+                       lflag = 1;
+                       break;
                default:
                        usage();
                }
@@ -254,7 +288,10 @@ main(argc, argv)
        argv += optind;
 
        if (aflag || cflag) {
-               dump(0);
+               if (lflag)
+                       dump_ext(0, xflag);
+               else
+                       dump(0);
                exit(0);
        }
        if (dflag) {
@@ -561,7 +598,6 @@ dump(addr)
        struct rt_msghdr *rtm;
        struct sockaddr_in6 *sin;
        struct sockaddr_dl *sdl;
-       extern int h_errno;
        struct in6_nbrinfo *nbi;
        struct timeval time;
        int addrwidth;
@@ -624,6 +660,7 @@ again:;
                } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))
                        continue;
                if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) ||
+                   IN6_IS_ADDR_MC_NODELOCAL(&sin->sin6_addr) ||
                    IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) {
                        /* XXX: should scope id be filled in the kernel? */
                        if (sin->sin6_scope_id == 0)
@@ -748,6 +785,270 @@ again:;
        }
 }
 
+/*
+ * Dump the entire neighbor cache (extended)
+ */
+void
+dump_ext(addr, xflag)
+       struct in6_addr *addr;
+       int xflag;
+{
+       int mib[6];
+       size_t needed;
+       char *lim, *buf, *next;
+       struct rt_msghdr_ext *ertm;
+       struct sockaddr_in6 *sin;
+       struct sockaddr_dl *sdl;
+       struct in6_nbrinfo *nbi;
+       struct timeval time;
+       int addrwidth;
+       int llwidth;
+       int ifwidth;
+       char flgbuf[8];
+       char *ifname;
+
+       /* Print header */
+       if (!tflag && !cflag) {
+               printf("%-*.*s %-*.*s %*.*s %-9.9s %-9.9s %2s %4s %4s",
+                   W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address",
+                   W_IF, W_IF, "Netif", "Expire(O)", "Expire(I)", "St",
+                   "Flgs", "Prbs");
+               if (xflag)
+                       printf(" %-7.7s %-7.7s %-7.7s", "RSSI", "LQM", "NPM");
+               printf("\n");
+       }
+
+again:;
+       mib[0] = CTL_NET;
+       mib[1] = PF_ROUTE;
+       mib[2] = 0;
+       mib[3] = AF_INET6;
+       mib[4] = NET_RT_DUMPX_FLAGS;
+       mib[5] = RTF_LLINFO;
+       if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+               err(1, "sysctl(PF_ROUTE estimate)");
+       if (needed > 0) {
+               if ((buf = malloc(needed)) == NULL)
+                       errx(1, "malloc");
+               if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+                       err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)");
+               lim = buf + needed;
+       } else
+               buf = lim = NULL;
+
+       for (next = buf; next && next < lim; next += ertm->rtm_msglen) {
+               int isrouter = 0, prbs = 0;
+
+               ertm = (struct rt_msghdr_ext *)next;
+               sin = (struct sockaddr_in6 *)(ertm + 1);
+               sdl = (struct sockaddr_dl *)((char *)sin + ROUNDUP(sin->sin6_len));
+
+               /*
+                * Some OSes can produce a route that has the LINK flag but
+                * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD
+                * and BSD/OS, where xx is not the interface identifier on
+                * lo0).  Such routes entry would annoy getnbrinfo() below,
+                * so we skip them.
+                * XXX: such routes should have the GATEWAY flag, not the
+                * LINK flag.  However, there are rotten routing software
+                * that advertises all routes that have the GATEWAY flag.
+                * Thus, KAME kernel intentionally does not set the LINK flag.
+                * What is to be fixed is not ndp, but such routing software
+                * (and the kernel workaround)...
+                */
+               if (sdl->sdl_family != AF_LINK)
+                       continue;
+
+               if (addr) {
+                       if (!IN6_ARE_ADDR_EQUAL(addr, &sin->sin6_addr))
+                               continue;
+                       found_entry = 1;
+               } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))
+                       continue;
+               if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) ||
+                   IN6_IS_ADDR_MC_NODELOCAL(&sin->sin6_addr) ||
+                   IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) {
+                       /* XXX: should scope id be filled in the kernel? */
+                       if (sin->sin6_scope_id == 0)
+                               sin->sin6_scope_id = sdl->sdl_index;
+#ifdef __KAME__
+                       /* KAME specific hack; removed the embedded id */
+                       *(u_int16_t *)&sin->sin6_addr.s6_addr[2] = 0;
+#endif
+               }
+               getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
+                           sizeof(host_buf), NULL, 0,
+                           NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
+               if (cflag == 1) {
+#ifdef RTF_WASCLONED
+                       if (ertm->rtm_flags & RTF_WASCLONED)
+                               delete(host_buf);
+#else
+                       delete(host_buf);
+#endif
+                       continue;
+               }
+               gettimeofday(&time, 0);
+               if (tflag)
+                       ts_print(&time);
+
+               addrwidth = strlen(host_buf);
+               if (addrwidth < W_ADDR)
+                       addrwidth = W_ADDR;
+               llwidth = strlen(ether_str(sdl));
+               if (W_ADDR + W_LL - addrwidth > llwidth)
+                       llwidth = W_ADDR + W_LL - addrwidth;
+               ifname = if_indextoname(sdl->sdl_index, ifix_buf);
+               if (!ifname)
+                       ifname = "?";
+               ifwidth = strlen(ifname);
+               if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
+                       ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
+
+               printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host_buf,
+                   llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname);
+
+               if (ertm->rtm_ri.ri_refcnt == 0 ||
+                   ertm->rtm_ri.ri_snd_expire == 0)
+                       printf(" %-9.9s", "(none)");
+               else if (ertm->rtm_ri.ri_snd_expire > time.tv_sec)
+                       printf(" %-9.9s",
+                           sec2str(ertm->rtm_ri.ri_snd_expire - time.tv_sec));
+               else
+                       printf(" %-9.9s", "expired");
+
+               if (ertm->rtm_ri.ri_refcnt == 0 ||
+                   ertm->rtm_ri.ri_rcv_expire == 0)
+                       printf(" %-9.9s", "(none)");
+               else if (ertm->rtm_ri.ri_rcv_expire > time.tv_sec)
+                       printf(" %-9.9s",
+                           sec2str(ertm->rtm_ri.ri_rcv_expire - time.tv_sec));
+               else
+                       printf(" %-9.9s", "expired");
+
+               /* Print neighbor discovery specific informations */
+               nbi = getnbrinfo(&sin->sin6_addr, sdl->sdl_index, 1);
+               if (nbi) {
+                       switch(nbi->state) {
+                        case ND6_LLINFO_NOSTATE:
+                                printf(" N");
+                                break;
+#ifdef ND6_LLINFO_WAITDELETE
+                        case ND6_LLINFO_WAITDELETE:
+                                printf(" W");
+                                break;
+#endif
+                        case ND6_LLINFO_INCOMPLETE:
+                                printf(" I");
+                                break;
+                        case ND6_LLINFO_REACHABLE:
+                                printf(" R");
+                                break;
+                        case ND6_LLINFO_STALE:
+                                printf(" S");
+                                break;
+                        case ND6_LLINFO_DELAY:
+                                printf(" D");
+                                break;
+                        case ND6_LLINFO_PROBE:
+                                printf(" P");
+                                break;
+                        default:
+                                printf(" ?");
+                                break;
+                       }
+
+                       isrouter = nbi->isrouter;
+                       prbs = nbi->asked;
+               } else {
+                       warnx("failed to get neighbor information");
+                       printf("  ");
+               }
+               putchar(' ');
+
+               /*
+                * other flags. R: router, P: proxy, W: ??
+                */
+               if ((ertm->rtm_addrs & RTA_NETMASK) == 0) {
+                       snprintf(flgbuf, sizeof(flgbuf), "%s%s",
+                               isrouter ? "R" : "",
+                               (ertm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
+               } else {
+                       sin = (struct sockaddr_in6 *)
+                               (sdl->sdl_len + (char *)sdl);
+                       snprintf(flgbuf, sizeof(flgbuf), "%s%s%s%s",
+                               isrouter ? "R" : "",
+                               !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr)
+                                       ? "P" : "",
+                               (sin->sin6_len != sizeof(struct sockaddr_in6))
+                                       ? "W" : "",
+                               (ertm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
+               }
+               printf(" %-4.4s", flgbuf);
+
+               if (prbs)
+                       printf(" %4d", prbs);
+
+               if (xflag) {
+                       if (!prbs)
+                               printf(" %-4.4s", "none");
+
+                       if (ertm->rtm_ri.ri_rssi != IFNET_RSSI_UNKNOWN)
+                               printf(" %7d", ertm->rtm_ri.ri_rssi);
+                       else
+                               printf(" %-7.7s", "unknown");
+
+                       switch (ertm->rtm_ri.ri_lqm)
+                       {
+                       case IFNET_LQM_THRESH_OFF:
+                               printf(" %-7.7s", "off");
+                               break;
+                       case IFNET_LQM_THRESH_UNKNOWN:
+                               printf(" %-7.7s", "unknown");
+                               break;
+                       case IFNET_LQM_THRESH_POOR:
+                               printf(" %-7.7s", "poor");
+                               break;
+                       case IFNET_LQM_THRESH_GOOD:
+                               printf(" %-7.7s", "good");
+                               break;
+                       default:
+                               printf(" %7d", ertm->rtm_ri.ri_lqm);
+                               break;
+                       }
+
+                       switch (ertm->rtm_ri.ri_npm)
+                       {
+                       case IFNET_NPM_THRESH_UNKNOWN:
+                               printf(" %-7.7s", "unknown");
+                               break;
+                       case IFNET_NPM_THRESH_NEAR:
+                               printf(" %-7.7s", "near");
+                               break;
+                       case IFNET_NPM_THRESH_GENERAL:
+                               printf(" %-7.7s", "general");
+                               break;
+                       case IFNET_NPM_THRESH_FAR:
+                               printf(" %-7.7s", "far");
+                               break;
+                       default:
+                               printf(" %7d", ertm->rtm_ri.ri_npm);
+                               break;
+                       }
+               }
+
+               printf("\n");
+       }
+       if (buf != NULL)
+               free(buf);
+
+       if (repeat) {
+               printf("\n");
+               sleep(repeat);
+               goto again;
+       }
+}
+
 static struct in6_nbrinfo *
 getnbrinfo(addr, ifindex, warning)
        struct in6_addr *addr;
@@ -783,10 +1084,10 @@ ether_str(sdl)
 
        if (sdl->sdl_alen) {
                cp = (u_char *)LLADDR(sdl);
-               sprintf(ebuf, "%x:%x:%x:%x:%x:%x",
+               snprintf(ebuf, sizeof(ebuf), "%x:%x:%x:%x:%x:%x",
                        cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
        } else {
-               sprintf(ebuf, "(incomplete)");
+               snprintf(ebuf, sizeof(ebuf), "(incomplete)");
        }
 
        return(ebuf);
@@ -814,7 +1115,7 @@ void
 usage()
 {
        printf("usage: ndp hostname\n");
-       printf("       ndp -a[nt]\n");
+       printf("       ndp -a[lnt]\n");
        printf("       ndp [-nt] -A wait\n");
        printf("       ndp -c[nt]\n");
        printf("       ndp -d[nt] hostname\n");
@@ -913,7 +1214,7 @@ ifinfo(argc, argv)
                exit(1);
        }
        bzero(&nd, sizeof(nd));
-       strcpy(nd.ifname, ifname);
+       strlcpy(nd.ifname, ifname, sizeof(nd.ifname));
        if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
                perror("ioctl (SIOCGIFINFO_IN6)");
                exit(1);
@@ -939,6 +1240,8 @@ ifinfo(argc, argv)
                }\
        } while (0)
                SETFLAG("nud", ND6_IFF_PERFORMNUD);
+               SETFLAG("proxy_prefixes", ND6_IFF_PROXY_PREFIXES);
+               SETFLAG("ignore_na", ND6_IFF_IGNORE_NA);
 
                ND.flags = newflags;
                if (ioctl(s, SIOCSIFINFO_FLAGS, (caddr_t)&nd) < 0) {
@@ -958,7 +1261,7 @@ ifinfo(argc, argv)
        memset(nullbuf, 0, sizeof(nullbuf));
        if (memcmp(nullbuf, ND.randomid, sizeof(nullbuf)) != 0) {
                int j;
-               u_int8_t *rbuf;
+               u_int8_t *rbuf = NULL;
 
                for (i = 0; i < 3; i++) {
                        switch(i) {
@@ -984,6 +1287,12 @@ ifinfo(argc, argv)
                printf("\nFlags: ");
                if ((ND.flags & ND6_IFF_PERFORMNUD) != 0)
                        printf("PERFORMNUD ");
+               if ((ND.flags & ND6_IFF_PROXY_PREFIXES) != 0)
+                       printf("PROXY_PREFIXES ");
+               if ((ND.flags & ND6_IFF_IFDISABLED) != 0)
+                       printf("IFDISABLED ");
+               if ((ND.flags & ND6_IFF_IGNORE_NA) != 0)
+                       printf("IGNORE_NA ");
        }
        putc('\n', stdout);
 #undef ND
@@ -1030,9 +1339,12 @@ rtrlist()
                
                printf("%s if=%s", host_buf,
                       if_indextoname(p->if_index, ifix_buf));
-               printf(", flags=%s%s",
+               printf(", flags=%s%s%s%s%s",
                       p->flags & ND_RA_FLAG_MANAGED ? "M" : "",
-                      p->flags & ND_RA_FLAG_OTHER   ? "O" : "");
+                      p->flags & ND_RA_FLAG_OTHER   ? "O" : "",
+                      p->stateflags & NDDRF_INSTALLED ? "T" : "",
+                      p->stateflags & NDDRF_IFSCOPE ? "I" : "",
+                      p->stateflags & NDDRF_STATIC ? "S" : "");
                rtpref = ((p->flags & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff;
                printf(", pref=%s", rtpref_str[rtpref]);
                
@@ -1054,7 +1366,7 @@ rtrlist()
                exit(1);
        }
        bzero(&dr, sizeof(dr));
-       strcpy(dr.ifname, "lo0"); /* dummy */
+       strlcpy(dr.ifname, "lo0", sizeof(dr.ifname)); /* dummy */
        if (ioctl(s, SIOCGDRLST_IN6, (caddr_t)&dr) < 0) {
                perror("ioctl (SIOCGDRLST_IN6)");
                exit(1);
@@ -1138,13 +1450,20 @@ plist()
                 * meaning of fields, especially flags, is very different
                 * by origin.  notify the difference to the users.
                 */
-               printf("flags=%s%s%s%s%s",
+               printf("flags=%s%s%s%s%s%s%s%s",
                       p->raflags.onlink ? "L" : "",
                       p->raflags.autonomous ? "A" : "",
                       (p->flags & NDPRF_ONLINK) != 0 ? "O" : "",
                       (p->flags & NDPRF_DETACHED) != 0 ? "D" : "",
+                      (p->flags & NDPRF_IFSCOPE) != 0 ? "I" : "",
+                      (p->flags & NDPRF_PRPROXY) != 0 ? "Y" : "",
 #ifdef NDPRF_HOME
-                      (p->flags & NDPRF_HOME) != 0 ? "H" : ""
+                      (p->flags & NDPRF_HOME) != 0 ? "H" : "",
+#else
+                      "",
+#endif
+#ifdef NDPRF_STATIC
+                      (p->flags & NDPRF_STATIC) != 0 ? "S" : ""
 #else
                       ""
 #endif
@@ -1218,7 +1537,7 @@ plist()
                exit(1);
        }
        bzero(&pr, sizeof(pr));
-       strcpy(pr.ifname, "lo0"); /* dummy */
+       strlcpy(pr.ifname, "lo0", sizeof(pr.ifname)); /* dummy */
        if (ioctl(s, SIOCGPRLST_IN6, (caddr_t)&pr) < 0) {
                perror("ioctl (SIOCGPRLST_IN6)");
                exit(1);
@@ -1390,7 +1709,7 @@ pfx_flush()
 
        if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
                err(1, "socket");
-       strcpy(dummyif, "lo0"); /* dummy */
+       strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
        if (ioctl(s, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0)
                err(1, "ioctl(SIOCSPFXFLUSH_IN6)");
 }
@@ -1403,7 +1722,7 @@ rtr_flush()
 
        if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
                err(1, "socket");
-       strcpy(dummyif, "lo0"); /* dummy */
+       strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
        if (ioctl(s, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0)
                err(1, "ioctl(SIOCSRTRFLUSH_IN6)");
 
@@ -1418,7 +1737,7 @@ harmonize_rtr()
 
        if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
                err(1, "socket");
-       strcpy(dummyif, "lo0"); /* dummy */
+       strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
        if (ioctl(s, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0)
                err(1, "ioctl (SIOCSNDFLUSH_IN6)");
 
@@ -1443,7 +1762,7 @@ setdefif(ifname)
        if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
                err(1, "socket");
 
-       strcpy(ndifreq.ifname, "lo0"); /* dummy */
+       strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */
        ndifreq.ifindex = ifindex;
 
        if (ioctl(s, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
@@ -1462,7 +1781,7 @@ getdefif()
                err(1, "socket");
 
        memset(&ndifreq, 0, sizeof(ndifreq));
-       strcpy(ndifreq.ifname, "lo0"); /* dummy */
+       strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */
 
        if (ioctl(s, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
                err(1, "ioctl (SIOCGDEFIFACE_IN6)");
@@ -1496,17 +1815,17 @@ sec2str(total)
 
        if (days) {
                first = 0;
-               p += sprintf(p, "%dd", days);
+               p += snprintf(p, sizeof(result) - (p - result), "%dd", days);
        }
        if (!first || hours) {
                first = 0;
-               p += sprintf(p, "%dh", hours);
+               p += snprintf(p, sizeof(result) - (p - result), "%dh", hours);
        }
        if (!first || mins) {
                first = 0;
-               p += sprintf(p, "%dm", mins);
+               p += snprintf(p, sizeof(result) - (p - result), "%dm", mins);
        }
-       sprintf(p, "%ds", secs);
+       snprintf(p, sizeof(result) - (p - result), "%ds", secs);
 
        return(result);
 }