/* * $Id: src/arpdig/arpdig.c,v 1.16 2006/05/06 10:08:42 marck Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "arpdig.h" static const char __Version__[] __unused = "arpdig ver 0.5.1"; /* sorted table of ether ifaces */ typedef struct _iface { char ifname[IFNAMSIZ]; int ifindex; /* host order */ uint32_t addr; uint32_t mask; } IFACE; IFACE *iftable; int nifaces = 0; int noresolve = 0; /* mostly stolen from ifconfig(8) */ static int isether(const struct sockaddr_dl *sdl) { return (sdl->sdl_alen > 0 && sdl->sdl_type == IFT_ETHER && sdl->sdl_alen == ETHER_ADDR_LEN); } static int mkaddrtable(void) { int needed; uint32_t addr, mask; char *ifname; struct ifaddrs *ift, *p; struct sockaddr *t; IFACE *ifp; if ((iftable = calloc(MAXIFACES, sizeof(IFACE))) == NULL) errx(1, "Out of memory"); if (getifaddrs(&ift) < 0) err(1, "getifaddrs"); needed = 0; addr = 0; ifname = NULL; for (p = ift; p; p = p->ifa_next) { if ((t = p->ifa_addr) != NULL) { switch (t->sa_family) { case AF_LINK: /* XXX assume AF_LINK is always the first */ addr = 0; needed = isether((struct sockaddr_dl *)t); ifname = p->ifa_name; break; case AF_INET: if (!needed) break; addr = ntohl( ((struct sockaddr_in *)t)->sin_addr.s_addr); break; } } if ((t = p->ifa_netmask) != NULL) { switch (t->sa_family) { case AF_INET: if (!needed) break; mask = ntohl( ((struct sockaddr_in *)t)->sin_addr.s_addr); /* skip /32 aliases */ if (mask == INADDR_BROADCAST) break; /* XXX store address */ ifp = &iftable[nifaces++]; if (nifaces == MAXIFACES) errx(1, "Too many interface addresses: %d", nifaces); strncpy(ifp->ifname, ifname, IFNAMSIZ); ifp->ifname[IFNAMSIZ-1] = '\0'; /* if_nametoindex uses getifaddrs itself. Use trick from there */ ifp->ifindex = ((struct sockaddr_dl*)p->ifa_addr)->sdl_index; ifp->addr = addr; ifp->mask = mask; break; } } } freeifaddrs(ift); return 0; } /* #define DEBUG */ #ifdef DEBUG /* XXX alocates memory; free to avoid leaks */ static char *_ntoa(u_long addr) { #define MAXIPALEN 18 char *buf, *s; int i; buf = malloc(MAXIPALEN); if ((buf = malloc(MAXIPALEN)) == NULL) return NULL; for (i=0, s=buf; i<4; i++) { s += sprintf(s, "%u", (unsigned int)(addr >> 24)); addr <<= 8; *s++ = (i == 3) ? '\0' : '.'; } return buf; } /* * convert netmask (hostorder) to prefixlen. * assumes consistent netmask. */ static int nmlen(u_long addr) { int nm; if (!addr) return 0; nm = 1; while (addr <<= 1) nm++; return nm; } static void printaddrtable(void) { int i; IFACE *ifp; for (i = 0; i < nifaces; i++) { ifp = &iftable[i]; printf("%s\t", ifp->ifname); printf("%s:%s (/%d)\n", _ntoa(ifp->addr), _ntoa(ifp->mask), nmlen(ifp->mask)); } } #endif static char *ip2ifname(struct in_addr ia, struct in_addr *if_ia) { int i; IFACE *ifp; for (i=0, ifp=iftable; i < nifaces; i++, ifp++) if ((ntohl(ia.s_addr) & ifp->mask) == (ifp->addr & ifp->mask)) { if (if_ia) if_ia->s_addr = htonl(ifp->addr); return ifp->ifname; } return NULL; } static void probeaddr(struct in_addr ia, struct in_addr if_ia) { char *ifname; struct in_addr t_ia; t_ia = if_ia; if ((ifname = ip2ifname(ia, &if_ia)) == NULL) warnx("%s seems not directly connected", inet_ntoa(ia)); if (t_ia.s_addr != 0 ) if_ia = t_ia; if (ifname) { #ifdef DEBUG printf("probing %s at %s\n", inet_ntoa(ia), ifname); #endif arpdig(ifname, if_ia, ia, 32); } } static void probeaddrs(int argc, char *argv[], struct in_addr if_ia) { struct in_addr ia; for (; argc; argc--, argv++) { if ((ia.s_addr = inet_addr(*argv)) == INADDR_NONE) warnx("Bad IP address: %s", *argv); else /* * XXX direct probe. * should make table to multiplex probes */ probeaddr(ia, if_ia); } } static void digaddrs(char *ifname, char *block, struct in_addr if_ia) { char *s; int iplen; struct in_addr ipstart, t_ia; iplen = 0; if ((s = strchr(block, '/')) != NULL) { *s++ = '\0'; while (isdigit(*s)) iplen = iplen*10 + (*s++ - '0'); } if ( iplen <= 0 || iplen > 32 || *s != '\0' || (ipstart.s_addr = inet_addr(block)) == INADDR_NONE) errx(1, "Bad netblock presentation: %s", block); /* round ip address by masklen */ ipstart.s_addr = htonl(ntohl(ipstart.s_addr) & ((in_addr_t)-1 << (32 - iplen))); t_ia = if_ia; if (ifname == NULL && (ifname = ip2ifname(ipstart, &if_ia)) == NULL) warnx("%s seems not directly connected", inet_ntoa(ipstart)); if (t_ia.s_addr != 0) if_ia = t_ia; printf("Digging %s:%s", ifname ? ifname : "default", inet_ntoa(if_ia)); printf(" for %s/%d\n", inet_ntoa(ipstart), iplen); if (arpdig(ifname, if_ia, ipstart, iplen)) err(2, "runtime error"); } static char usagestr[] = "usage: arpdig [-n] [-i ifname] [-F fromaddr] { netblock/len | " "-p [-f file] ipaddr ... }"; int main(int argc, char *argv[]) { int argvlen, ch, probemode; char *addrfn, *ifname, *s, **pp; FILE *fp; struct in_addr fromaddr; static char buf[BUFLEN]; mkaddrtable(); #ifdef DEBUG printaddrtable(); #endif /* paranoia */ if (getuid() == 0) seteuid(-1); probemode = 0; ifname = NULL; addrfn = NULL; fromaddr.s_addr = 0; while ((ch = getopt(argc, argv, "f:F:i:np")) != -1) switch(ch) { case 'f': addrfn = optarg; break; case 'F': if ((fromaddr.s_addr = inet_addr(optarg)) == INADDR_NONE) errx(1, "Bad from ip address: %s", optarg); break; case 'i': ifname = optarg; break; case 'n': noresolve = 1; break; case 'p': probemode = 1; break; default: errx(1, usagestr); } argc -= optind; argv += optind; if (probemode) { if(argc == 0 && addrfn == NULL) errx(1, usagestr); if (addrfn) { if ((fp = fopen(addrfn, "r")) == NULL) err(1, "Can't open %s", addrfn); argvlen = (argc < 16) ? 16 : argc; if ((pp = malloc(argvlen * sizeof(char *))) == NULL) err(1, "Out of memory"); if (argc) memcpy(pp, argv, argc * sizeof(char *)); argv = pp; while (fgets(buf, BUFLEN, fp) != NULL) { if (*(s = &buf[strlen(buf) - 1]) == '\n') *s = '\0'; if (s == buf) continue; if ((s = strdup(buf)) == NULL) err(1, "Out of memory"); if (argc >= argvlen && (argv = realloc(argv, (argvlen*=2) * sizeof(char *))) == NULL) err(1, "Out of memory"); argv[argc++] = s; } fclose(fp); } probeaddrs(argc, argv, fromaddr); } else { if (argc != 1) errx(1, usagestr); digaddrs(ifname, argv[0], fromaddr); } return 0; } /* End of $RCSfile: arpdig.c,v $ */