/*
* $Id: src/arpdig/arpdig.c,v 1.16 2006/05/06 10:08:42 marck Exp $
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <err.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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 $ */
syntax highlighted by Code2HTML, v. 0.9.1