fix rejection of dns responses with pointers past 512 byte offset
[musl] / src / network / if_nameindex.c
index 580acce..2deaef7 100644 (file)
 #define _GNU_SOURCE
 #include <net/if.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
 #include <errno.h>
-#include "syscall.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include "netlink.h"
 
-#include <stdio.h>
+#define IFADDRS_HASH_SIZE 64
 
-static void *do_nameindex(int s, size_t n)
-{
-       size_t i, len;
-       struct ifconf conf;
-       struct if_nameindex *idx;
+struct ifnamemap {
+       unsigned int hash_next;
+       unsigned int index;
+       unsigned char namelen;
+       char name[IFNAMSIZ];
+};
 
-       idx = malloc(n * (sizeof(struct if_nameindex)+sizeof(struct ifreq)));
-       if (!idx) return 0;
+struct ifnameindexctx {
+       unsigned int num, allocated, str_bytes;
+       struct ifnamemap *list;
+       unsigned int hash[IFADDRS_HASH_SIZE];
+};
 
-       conf.ifc_buf = (void *)&idx[n];
-       conf.ifc_len = len = n * sizeof(struct ifreq);
-       if (ioctl(s, SIOCGIFCONF, &conf) < 0) {
-               free(idx);
-               return 0;
-       }
-       if (conf.ifc_len == len) {
-               free(idx);
-               return (void *)-1;
+static int netlink_msg_to_nameindex(void *pctx, struct nlmsghdr *h)
+{
+       struct ifnameindexctx *ctx = pctx;
+       struct ifnamemap *map;
+       struct rtattr *rta;
+       unsigned int i;
+       int index, type, namelen, bucket;
+
+       if (h->nlmsg_type == RTM_NEWLINK) {
+               struct ifinfomsg *ifi = NLMSG_DATA(h);
+               index = ifi->ifi_index;
+               type = IFLA_IFNAME;
+               rta = NLMSG_RTA(h, sizeof(*ifi));
+       } else {
+               struct ifaddrmsg *ifa = NLMSG_DATA(h);
+               index = ifa->ifa_index;
+               type = IFA_LABEL;
+               rta = NLMSG_RTA(h, sizeof(*ifa));
        }
+       for (; NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
+               if (rta->rta_type != type) continue;
+
+               namelen = RTA_DATALEN(rta) - 1;
+               if (namelen > IFNAMSIZ) return 0;
 
-       n = conf.ifc_len / sizeof(struct ifreq);
-       for (i=0; i<n; i++) {
-               if (ioctl(s, SIOCGIFINDEX, &conf.ifc_req[i]) < 0) {
-                       i--;
-                       n--;
-                       continue;
+               /* suppress duplicates */
+               bucket = index % IFADDRS_HASH_SIZE;
+               i = ctx->hash[bucket];
+               while (i) {
+                       map = &ctx->list[i-1];
+                       if (map->index == index &&
+                           map->namelen == namelen &&
+                           memcmp(map->name, RTA_DATA(rta), namelen) == 0)
+                               return 0;
+                       i = map->hash_next;
                }
-               idx[i].if_index = conf.ifc_req[i].ifr_ifindex;
-               idx[i].if_name = conf.ifc_req[i].ifr_name;
-       }
-       idx[i].if_name = 0;
-       idx[i].if_index = 0;
 
-       return idx;
+               if (ctx->num >= ctx->allocated) {
+                       size_t a = ctx->allocated ? ctx->allocated * 2 + 1 : 8;
+                       if (a > SIZE_MAX/sizeof *map) return -1;
+                       map = realloc(ctx->list, a * sizeof *map);
+                       if (!map) return -1;
+                       ctx->list = map;
+                       ctx->allocated = a;
+               }
+               map = &ctx->list[ctx->num];
+               map->index = index;
+               map->namelen = namelen;
+               memcpy(map->name, RTA_DATA(rta), namelen);
+               ctx->str_bytes += namelen + 1;
+               ctx->num++;
+               map->hash_next = ctx->hash[bucket];
+               ctx->hash[bucket] = ctx->num;
+               return 0;
+       }
+       return 0;
 }
 
 struct if_nameindex *if_nameindex()
 {
-       size_t n;
-       void *p = 0;
-       int s = socket(AF_UNIX, SOCK_DGRAM, 0);
-       if (s>=0) {
-               for (n=0; (p=do_nameindex(s, n)) == (void *)-1; n++);
-               __syscall(SYS_close, s);
+       struct ifnameindexctx _ctx, *ctx = &_ctx;
+       struct if_nameindex *ifs = 0, *d;
+       struct ifnamemap *s;
+       char *p;
+       int i;
+       int cs;
+
+       pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+       memset(ctx, 0, sizeof(*ctx));
+       if (__rtnetlink_enumerate(AF_UNSPEC, AF_INET, netlink_msg_to_nameindex, ctx) < 0) goto err;
+
+       ifs = malloc(sizeof(struct if_nameindex[ctx->num+1]) + ctx->str_bytes);
+       if (!ifs) goto err;
+
+       p = (char*)(ifs + ctx->num + 1);
+       for (i = ctx->num, d = ifs, s = ctx->list; i; i--, s++, d++) {
+               d->if_index = s->index;
+               d->if_name = p;
+               memcpy(p, s->name, s->namelen);
+               p += s->namelen;
+               *p++ = 0;
        }
+       d->if_index = 0;
+       d->if_name = 0;
+err:
+       pthread_setcancelstate(cs, 0);
+       free(ctx->list);
        errno = ENOBUFS;
-       return p;
+       return ifs;
 }