From 07adecf58e8c133d9228aedb0eeb351c0ea0afc4 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Sat, 20 Sep 2003 07:09:42 +0000 Subject: Initial Import --- AUTHORS | 1 + BUGS | 4 + COPYING | 14 ++ ChangeLog | 2 + INSTALL | 11 ++ Makefile.am | 4 + NEWS | 1 + README | 15 ++ configure.ac | 26 +++ src/Makefile.am | 7 + src/ipnets.c | 484 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ipsort.c | 153 +++++++++++++++++ src/resolve.c | 516 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ testfile | 11 ++ 14 files changed, 1249 insertions(+) create mode 100644 AUTHORS create mode 100644 BUGS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 configure.ac create mode 100644 src/Makefile.am create mode 100644 src/ipnets.c create mode 100644 src/ipsort.c create mode 100644 src/resolve.c create mode 100644 testfile diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..0cc01a2 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +nielsen@memberwebs.com \ No newline at end of file diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..19991b7 --- /dev/null +++ b/BUGS @@ -0,0 +1,4 @@ +ADDRTOOLS TODOS and BUGS + +- resolve doesn't resverse resolve MAC addresses (not sure if this is possible) +- diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..cf2037b --- /dev/null +++ b/COPYING @@ -0,0 +1,14 @@ +LICENSE +This software is in the public domain. + +The software is provided "as is", without warranty of any kind, +express or implied, including but not limited to the warranties +of merchantability, fitness for a particular purpose, and +noninfringement. In no event shall the author(s) be liable for any +claim, damages, or other liability, whether in an action of +contract, tort, or otherwise, arising from, out of, or in connection +with the software or the use or other dealings in the software. + +SUPPORT +Send bug reports to: + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..1cb4346 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,2 @@ +Version 0.2 + - Initial version diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..fad2dec --- /dev/null +++ b/INSTALL @@ -0,0 +1,11 @@ +====================================================================== + IPTOOLS 0.2 INSTALL + +These tools are mainly to be used in scripting. + +QUICK INSTALLATION: + # tar -zxvf addr.tar.gz + # ./configure + # make && make install + + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..479e90a --- /dev/null +++ b/Makefile.am @@ -0,0 +1,4 @@ +EXTRA_DIST = BUGS +SUBDIRS = src + + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..c7ab92a --- /dev/null +++ b/NEWS @@ -0,0 +1 @@ +See ChangeLog \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..d2094ad --- /dev/null +++ b/README @@ -0,0 +1,15 @@ +================================================================= + IPTOOLS 0.2 README + +Set of tools to be used with IP addresses in scripts. + +resolve + Resolves or reverse resolves between IP addresses + host names, or mac addresses. + +ipsort + Sorts IP addresses in numerical order. + +ipcoll + Collates IP addresses into "subnet" blocks where + possible \ No newline at end of file diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..d1da445 --- /dev/null +++ b/configure.ac @@ -0,0 +1,26 @@ +# Process this file with autoconf to produce a configure script. +AC_INIT(iptools, 0.2, nielsen@memberwebs.com) +AM_INIT_AUTOMAKE(iptools, 0.2) + +AC_CONFIG_SRCDIR([src/ipsort.c]) +AM_CONFIG_HEADER([config.h]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_INSTALL + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([arpa/inet.h fcntl.h netinet/in.h stdlib.h string.h sys/ioctl.h sys/socket.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_MALLOC +AC_FUNC_MEMCMP +AC_CHECK_FUNCS([atexit inet_ntoa inet_pton memset strerror strspn strtol]) + +AC_CONFIG_FILES([Makefile src/Makefile]) +AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..a04af8c --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,7 @@ +bin_PROGRAMS = ipsort ipnets resolve +ipsort_SOURCES = ipsort.c +ipnets_SOURCES = ipnets.c +resolve_SOURCES = resolve.c +# man_MANS = ipsort.1 ipnets.1 resolve.1 +# EXTRA_DIST = $(man_MANS) +CFLAGS = -g -O0 diff --git a/src/ipnets.c b/src/ipnets.c new file mode 100644 index 0000000..97196fa --- /dev/null +++ b/src/ipnets.c @@ -0,0 +1,484 @@ +/* +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.2 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +*/ + +#include +#include +#include +#include +#include +#include + +#include + +typedef unsigned int uint32; +void processIps(FILE* fIn, FILE* fOut); + +/* Returns the number of zero bits at the right of a number */ +uint32 zb(uint32 num) +{ + uint32 bits = 0; + if(num > 0) + while(num == (num & (0xFFFFFFFF << (bits + 1)))) + bits++; + return bits; +} + +/* Returns the number of non-zero bits at the right of a number */ +uint32 nzb(uint32 num) +{ + uint32 bits = 0; + while(!(num & (0x80000000 >> bits))) + bits++; + return 32 - bits; +} + +#define INET_BLOCK_LENGTH 35 + +#define INET_BLOCK_SIMPLE 0x00000001 +#define INET_BLOCK_BITS 0x00000002 +#define INET_BLOCK_NETMASK 0x00000004 +#define INET_BLOCK_TRUNC 0x00000008 +#define INET_BLOCK_HEX 0x00000010 + + +int block_ntoa(const char* s, struct in_addr* addr, + struct in_addr* netmask, int format) +{ + int truncated = 0; + const char* org = s; + + /* First we print the IP */ + + /* If we've been asked to truncate and the IP's + netmask is on a unit, then we can truncate */ + if(format & INET_BLOCK_TRUNC && + zb(ntohl(netmask.s_addr)) % 8 == 0) + { + in_addr_t ip = ntohl(in_addr.s_addr); + + /* Loop through and print relevant octets */ + int octets = (32 - zb(ip)) / 8; + while(octets--) + { + s += sprintf(s, octets == 0 ? "%d" : "%d.", + (ip & 0x000000FF << (octets * 8)) >> (octets * 8)); + } + + truncated = 1; + } + else + { + int ret = inet_ntop(AF_INET, addr, s, INET_BLOCK_LENGTH - (s - org)); + if(ret == -1) return ret; + s += ret; + } + + /* If we've been asked for simple and the IP was either + truncated successfully, or it was or is a complete IP */ + if(format & INET_BLOCK_SIMPLE && + (truncated || netmask.s_addr == 0xFFFFFFFF) + { + /* Then we're done */ + } + else + { + /* Put the netmask separator on */ + strcat(s, "/"); + + if(format & INET_BLOCK_HEX) + { + if(!(format & INET_BLOCK_NETMASK)) + return -1; + + s += sprintf(s, "0x%08X", ntohl(netmask.s_addr)); + } + else if(format & INET_BLOCK_NETMASK) + { + int ret = inet_ntop(AF_INET, netmask, s, INET_BLOCK_LENGTH - (s - org)); + if(ret == -1) return ret; + s += ret; + } + else /* if(format & INET_BLOCK_BITS) */ + { + s += sprintf(s, "%d", 32 - zb(ntohl(netmask.s_addr))); + } + } + + return s - org; +} + +int block_aton(const char* s, struct in_addr* addr, struct in_addr* netmask) +{ + byte ipoctets[] = { 0, 0, 0, 0 }; + byte nmoctets[] = { 0, 0, 0, 0 }; + int format = 0; + int i; + + for(i = 0; i < 4; i++) + { + char* end; + + /* Must start with a number */ + if(!isdigit(*s)) return -1; + unsigned int num = strtoul(s, &end, 10); + + /* Must contain some valid number ... */ + if(end == s) return -1; + + /* That is less that 255 */ + if(num > 255) return -1; + + ipoctets[i] = num; + + s = end; + if(*s != '.') + break; + } + + if(i != 3) + format |= INET_BLOCK_TRUNC; + + /* If it's the end of the address */ + if(*s == '\0' || isspace(*s)) + { + while(i) + nmoctets[i--] = 255; + + format |= INET_BLOCK_SIMPLE; + } + + + /* Otherwise read a netmask */ + else if(*s == '/') + { + /* It's a netmask like so /0xFFFFFF00 */ + if(s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) + { + char* end; + unsigned int num = strtoul(s, &end, 15); + + if(s == end) + return -1; + + nmoctets[0] = num & 0xFF000000; + nmoctets[1] = num & 0x00FF0000; + nmoctets[2] = num & 0x0000FF00; + nmoctets[3] = num & 0x000000FF; + i = 4; + s = end; + + format |= INET_BLOCK_NETMASK | INET_BLOCK_HEX; + } + + /* Otherwise we assume is's either a bit mask /30 + or a full netmask 255.255.255.192 */ + else + { + for(i = 0; i < 4; i++) + { + char* end; + + /* Must start with a number */ + if(!isdigit(*s)) return -1; + unsigned int num = strtoul(s, &end, 10); + + /* Must contain some valid number ... */ + if(end == s) return -1; + + /* That is less that 255 */ + if(num > 255) return -1; + + nmoctets[i] = num; + + s = end; + if(*s != '.') + break; + } + } + + + addr->s_addr = (htonl(ipoctets[0] | + ipoctets[1] << 8 | + ipoctets[2] << 16 | + ipoctets[3] << 24)); + + if(netmask) + { + /* A netmask has to be either just a + number of bits, or a full netmask*/ + switch(i) + { + case 0: + if(nmoctets[0] > 32) + return -1; + netmask->s_addr = 0xFFFFFFFF << (32 - nmoctets[0]); + format |= INET_BLOCK_BITS; + break; + + case 3: + netmask->s_addr = (htonl(nmoctets[0] | + nmoctets[1] << 8 | + nmoctets[2] << 16 | + nmoctets[3] << 24)); + format |= INET_BLOCK_NETMASK; + break; + + default: + return -1; + } + } + } + + /* This has got to be the end */ + if(*s != '\0' || !isspace(*s)) + return -1; + + + return format; +} + +int block_check(struct in_addr* addr, struct in_addr* netmask) +{ + int zb = 0; + in_addr_t nm; + /* TODO: what should we check on the address? */ + + /* Make sure we have a valid netmask */ + in_addr_t nm = ntohl(netmask.s_addr); + zb = zb(nm); + if(0xFFFFFFFFF >> zb != nm >> zb) + return -1; + + /* Make sure that it's the right abount of bits + for our IP */ + if(BLOCK_START(addr) > COUNT_BLOCK(netmask)) + return -1; + + return 0; +} + + +void usage() +{ + fprintf(stderr, "usage: ipcoll"); + exit(2); +} + +int g_format = INET_BLOCK_SIMPLE | INET_BLOCK_BITS; + +int main(int argc, char* argv[]) +{ + int mode = 0; + int ch; + + while((ch = getopt(argc, argv, "fnhtx")) != -1) + { + switch(ch) + { + case 'f': + g_format &= ~INET_BLOCK_SIMPLE; + break; + + case 'n': + g_format &= ~INET_BLOCK_BITS; + g_format |= INET_BLOCK_NETMASK; + break; + + case 'h': + g_format &= ~INET_BLOCK_BITS; + g_format |= INET_BLOCK_NETMASK | INET_BLOCK_HEX; + break; + + case 't': + g_format |= INET_BLOCK_TRUNC; + break; + + case 'x': + mode = 1; + break; + } + } + + if(mode) + return processExpand(stdin, stdout); + else + return processCollate(stdin, stdout); +} + + +/* Parse helper */ +static void eatSpace(FILE* file) +{ + char ch; + + do + { + ch = getc(file); + + if(ch == '#') + { + while(ch != '\n') + ch = getc(file); + } + } + while(isspace(ch) && !feof(file) && !ferror(file)); + + ungetc(ch, file); +} + + + +#define IS_SAME_ADDR(a, b) ((a).s_addr == (b).s_addr) +#define IS_NEXT_ADDR(a, b) (ntohl((a).s_addr) == ntohl((b).s_addr) + 1) +#define INC_ADDR(a, c) ((a).s_addr = htonl((c) + ntohl((a).s_addr))) +#define BLOCK_ADDR(a) (1 << zb(ntohl((a).s_addr))) +#define BLOCK_SIZE(s) (1 << (nzb(s) - 1)) + + +struct in_addr kEndAddr = { 0x00000000 }; + +/* + * Read Input + */ +void processCollate(FILE* fIn, FILE* fOut) +{ + struct in_addr addr; /* The current address */ + struct in_addr netmask; /* The current address */ + struct in_addr prev; /* The previous address */ + + struct in_addr top; /* The address at the top of the current stack */ + int size = 0; + char address[INET_BLOCK_LENGTH]; + + eatSpace(fIn); + + while(!ferror(fIn)) + { + char ch; + int i = 0; + + /* Put in a dummy address so we flush and exit down below */ + if(feof(fIn)) + { + addr = kEndAddr; + netmask = kEndAddr; + } + + /* Read in an address */ + else + { + /* Read a word and store the first x chars */ + while(!ferror(fIn)) + { + ch = fgetc(fIn); + + if(feof(fIn) || isspace(ch)) + break; + + if(i < INET_BLOCK_LENGTH) + address[i] = ch; + + i++; + } + + address[i] = 0; + + /* Try to convert to an address */ + if(block_aton(address, addr, netmask) == -1 || + block_check(addr, netmask) == -1) + warnx("invalid ip or net block: %s", address); + + eatSpace(fIn); + } + + + /* If we already have something in this block */ + if(size > 0) + { + /* Just skip, and do nothing */ + if(IS_SAME_ADDR(addr, prev) || + IS_LESS_ADDR(addr, prev)) + { + + } + + /* If address is adjacent then add to block*/ + else if(IS_NEXT_ADDR(addr, prev)) + { + size += COUNT_BLOCK(netmask); + memcpy(&prev, &addr, sizeof(prev)); + INC_ADDR(prev, size - 1); + } + + /* Otherwise write out current stack */ + else + { + while(size > 0) + { + uint32 block, subnet, max; + + /* This is the amount of addresses we can group based + on where the block starts */ + block = BLOCK_ADDR(top); + + /* This is the amount of addresses we can group based + on the size of the block */ + max = BLOCK_SIZE(size); + + /* Okay go figure */ + block = max < block ? max : block; + subnet = 32 - zb(block); + + /* And print */ + if(subnet == 32) + fprintf(fOut, "%s\n", inet_ntoa(top)); + else + fprintf(fOut, "%s/%d\n", inet_ntoa(top), subnet); + + /* Go around for next section in block if we have */ + size -= block; + INC_ADDR(top, block); + } + + } + } + + /* If this is the last address then exit */ + if(IS_SAME_ADDR(addr, kEndAddr)) + break; + + + /* If this is a new stack */ + if(size == 0) + { + /* Copy the current address and go for it */ + memcpy(&top, &addr, sizeof(top)); + memcpy(&prev, &addr, sizeof(prev)); + + size++; + } + } + + if(ferror(fIn)) + err(1, "error reading addresses"); +} + diff --git a/src/ipsort.c b/src/ipsort.c new file mode 100644 index 0000000..f7d37b9 --- /dev/null +++ b/src/ipsort.c @@ -0,0 +1,153 @@ +/* +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.2 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +void readIps(FILE* fIn); +void writeIps(FILE* fOut); + +/* DB of traffic accessed recently */ +DB* g_db = NULL; + +/* TODO: Use an actual compare function */ + +int main(int argc, char* argv[]) +{ + BTREEINFO bi; + + memset(&bi, 0, sizeof(bi)); + /* bi.compare = compareIp; */ + + g_db = dbopen(NULL, O_CREAT | O_RDWR, 0, DB_BTREE, (const void*)&bi); + + if(g_db == NULL) + errx(1, "db initialization failed"); + + readIps(stdin); + writeIps(stdout); +} + +/* + * Parse helpers + */ +static void eatSpace(FILE* file) +{ + char ch; + + do + { + ch = getc(file); + + if(ch == '#') + { + while(ch != '\n') + ch = getc(file); + } + } + while(isspace(ch) && !feof(file) && !ferror(file)); + + ungetc(ch, file); +} + +/* + * Read Input + */ +void readIps(FILE* fIn) +{ + DBT key, value; + struct in_addr addr; + char address[16]; + + key.data = &addr; + key.size = sizeof(addr); + value.data = &addr; + value.size = sizeof(addr); + + eatSpace(fIn); + + while(!feof(fIn) && !ferror(fIn)) + { + char ch; + int i = 0; + + /* Read a word and store the first 16 chars */ + while(!feof(fIn) && !ferror(fIn)) + { + ch = fgetc(fIn); + + if(isspace(ch)) + break; + + if(i < 16) + address[i] = ch; + + i++; + } + + address[i] = 0; + + if(!inet_pton(AF_INET, address, (void*)&addr)) + warnx("invalid ip in address: %s", address); + + if(g_db->put(g_db, &key, &value, 0) < 0) + err(1, "database error: %s\n"); + + eatSpace(fIn); + } + + if(ferror(fIn)) + err(1, "error reading addresses"); +} + +/* + * Write collated output back out + */ +void writeIps(FILE* fOut) +{ + DBT key, value; + int ret; + + struct in_addr prev; /* The previous address */ + + struct in_addr top; /* The address at the top of the current stack */ + int size; /* The stack size */ + + memset(&key, 0, sizeof(key)); + memset(&value, 0, sizeof(value)); + + /* Get first key from DB */ + ret = g_db->seq(g_db, &key, &value, R_FIRST); + + while(ret == 0) + { + printf("%s\n", inet_ntoa(*((struct in_addr*)key.data))); + ret = g_db->seq(g_db, &key, &value, R_NEXT); + } +} diff --git a/src/resolve.c b/src/resolve.c new file mode 100644 index 0000000..880b678 --- /dev/null +++ b/src/resolve.c @@ -0,0 +1,516 @@ +/* +// AUTHOR +// N. Nielsen +// +// VERSION +// 0.2 +// +// LICENSE +// This software is in the public domain. +// +// The software is provided "as is", without warranty of any kind, +// express or implied, including but not limited to the warranties +// of merchantability, fitness for a particular purpose, and +// noninfringement. In no event shall the author(s) be liable for any +// claim, damages, or other liability, whether in an action of +// contract, tort, or otherwise, arising from, out of, or in connection +// with the software or the use or other dealings in the software. +// +// SUPPORT +// Send bug reports to: +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#if HAVE_CONFIG_H +#include +#endif + +/* The main processing functions */ +static void processFile(FILE* fIn, FILE* fOut); +static void processReverse(const char* address, FILE* fOut); +static void processNone(const char* address, FILE* fOut); +static void processAddress(const char* address, FILE* fOut); +static void processHost(const char* address, FILE* fOut); +static void processMAC(const char* address, FILE* fOut); + +static void atExit(); +static void usage(); + +/* Are we in reverse resolve mode? */ +int g_doReverse = 0; +int g_doVerbose = 0; + +/* These are globals for efficiency */ +u_char* g_packetBuf = NULL; +char* g_domainBuf = NULL; +u_char* g_routeBuf = NULL; +size_t g_routeLen = 0; + + +int main(int argc, char** argv) +{ + int useFiles = 0; + int ch; + + /* Register clean up function */ + atexit(atExit); + + /* Parse command line options here */ + while((ch = getopt(argc, argv, "rfv")) != -1) + { + switch(ch) + { + /* Treat arguments as files */ + case 'f': + useFiles = 1; + break; + + case 'v': + g_doVerbose = 1; + break; + + /* Do reverse resolve */ + case 'r': + g_doReverse = 1; + break; + + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if(argc) + { + while(argc > 0) + { + /* Process the argument as a file full of addresses */ + if(useFiles) + { + FILE* file = fopen(argv[0], "r"); + if(file == NULL) + err(1, "can't open file: %s", argv[0]); + + processFile(file, stdout); + + fclose(file); + } + + /* Process each argument as an address */ + else + { + processAddress(argv[0], stdout); + } + + argc--; + argv++; + } + } + else + { + /* No args, just process stdin etc... */ + processFile(stdin, stdout); + } + + exit(0); +} + + +/** + * Eats comments and spaces + */ +static void eatSpace(FILE* file) +{ + char ch; + + do + { + ch = getc(file); + + if(ch == '#') + { + while(ch != '\n') + ch = getc(file); + } + } + while(isspace(ch)); + + ungetc(ch, file); +} + + +/** + * Read addresses from a file and process + */ +static void processFile(FILE* fIn, FILE* fOut) +{ + /* Allocate memory, cache for efficiency */ + if(g_domainBuf == NULL) + { + g_domainBuf = (char*)malloc(sizeof(char) * MAXDNAME); + if(g_domainBuf == NULL) + errx(1, "out of memory."); + } + + + /* And process rest of file */ + eatSpace(fIn); + + while(!feof(fIn) && !ferror(fIn)) + { + char ch; + int i = 0; + + while(1) + { + ch = fgetc(fIn); + + if(isspace(ch)) + break; + + if(i < MAXDNAME) + g_domainBuf[i] = ch; + + i++; + } + + g_domainBuf[i] = 0; + + processAddress(g_domainBuf, fOut); + + eatSpace(fIn); + } + + /* TODO: Should this be a warning or quit? */ + if(ferror(fIn)) + err(1, "error reading file"); +} + + +/** + * Main address processing function + */ +static void processAddress(const char* address, FILE* fOut) +{ + if(g_doVerbose) + fprintf(stderr, "resolve: resolving %s\n", address); + + /* If it's an IP then .... */ + if(strlen(address) == strspn(address, "0123456789.")) + { + if(g_doReverse) + processReverse(address, fOut); + + else + processNone(address, fOut); + } + + /* If it's a MAC address then .... */ + else if(strlen(address) == strspn(address, "0123456789abcdefABCDEF-:")) + { + if(g_doReverse) + processNone(address, fOut); + else + processMAC(address, fOut); + } + + /* If it's a net block then .... */ + if(strlen(address) == strspn(address, "0123456789./")) + { + if(g_doReverse) + warnx("can't reverse resolve net block: %s", address); + else + processNone(address, fOut); + } + + /* Otherwise it should be a domain name. try to resolve it. */ + else + { + if(g_doReverse) + processNone(address, fOut); + else + processHost(address, fOut); + } +} + + +/** + * Used if address is aleady (hopefully) in correct form + */ +static void processNone(const char* address, FILE* fOut) +{ + fprintf(fOut, address); + fputc('\n', fOut); +} + + +/** + * Forward resolve a host name into an IP + */ +static void processHost(const char* address, FILE* fOut) +{ + int ret; + ns_msg handle; + int rrnum; + ns_rr rr; + + /* Allocation cached for efficiency */ + if(g_packetBuf == NULL) + { + g_packetBuf = (u_char*)malloc(sizeof(u_char) * PACKETSZ); + if(g_packetBuf == NULL) + errx(1, "out of memory."); + } + + /* Do a DNS lookup */ + ret = res_search(address, C_IN, ns_t_a, g_packetBuf, PACKETSZ); + if(ret == -1) + warnx("couldn't resolve: %s\n", address); + else + { + /* Initialize a handle to this response. */ + if(ns_initparse(g_packetBuf, ret, &handle) < 0) + warn("couldn't parse dns response: %s\n", strerror(errno)); + else + { + /* Parse the packet */ + for(rrnum = 0; rrnum < ns_msg_count(handle, ns_s_an); rrnum++) + { + if(!ns_parserr(&handle, ns_s_an, rrnum, &rr)) + { + if(ns_rr_type(rr) == ns_t_a && /* It's a host adress */ + ns_rr_rdlen(rr) == 0x04) /* And has an IP */ + { + struct in_addr addr; + memcpy(&(addr.s_addr), ns_rr_rdata(rr), 0x04); + fprintf(fOut, inet_ntoa(addr)); + fputc('\n', fOut); + } + } + } + } + } +} + + +/** + * Reverse resolve an IP into a host name + */ +static void processReverse(const char* address, FILE* fOut) +{ + int ret; + ns_msg handle; + int rrnum; + ns_rr rr; + u_int32_t ha; + char name[NS_MAXDNAME]; + struct in_addr addr; + + /* Is it a valid IP address first of all */ + if(!inet_aton(address, &addr)) + { + warnx("invalid ip address: %s\n", address); + return; + } + + /* Format query */ + ha = ntohl(addr.s_addr); + + sprintf(name, "%u.%u.%u.%u.IN-ADDR.ARPA.", + (ha) & 0xff, + (ha >> 8) & 0xff, + (ha >> 16) & 0xff, + (ha >> 24) & 0xff); + + /* Allocation cached for efficiency */ + if(g_packetBuf == NULL) + { + g_packetBuf = (u_char*)malloc(sizeof(u_char) * PACKETSZ); + if(g_packetBuf == NULL) + errx("out of memory."); + } + + /* Do that lookup */ + ret = res_search(name, C_IN, ns_t_ptr, g_packetBuf, PACKETSZ); + if(ret == -1) + warnx("couldn't resolve: %s\n", name); + else + { + /* Initialize a handle to this response. */ + if(ns_initparse(g_packetBuf, ret, &handle) < 0) + warn("couldn't parse dns response"); + else + { + /* Parse the packet */ + for(rrnum = 0; rrnum < ns_msg_count(handle, ns_s_an); rrnum++) + { + if(!ns_parserr(&handle, ns_s_an, rrnum, &rr)) + { + if(ns_rr_type(rr) == ns_t_ptr) /* It's a domain pointer */ + { + /* Expand the host's name */ + if (ns_name_uncompress( + ns_msg_base(handle),/* Start of the packet */ + ns_msg_end(handle), /* End of the packet */ + ns_rr_rdata(rr), /* Position in the packet*/ + name, /* Result */ + MAXDNAME) /* Size of nsList buffer */ + < 0) /* Negative: error */ + { + warn("couldn't parse dns response"); + } + else + { + fprintf(fOut, "%s\n", name); + } + } + } + } + } + } +} + + +/** + * Parse components of a MAC address + */ +static int parseMAC(const char* address, u_char* mac) +{ + int i; + + for(i = 0; i < 6; i++) + { + char* end; + int part; + + part = strtol(address, &end, 16); + + if(i != 5 && *end != ':' && *end != '-') + return 0; + + if(part < 0 || part > 255) + return 0; + + mac[i] = (u_char)part; + + address = end + 1; + } + + return 1; +} + + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + +/** + * Reverse resolve MAC address if in the ARP table + */ +static void processMAC(const char* address, FILE* fOut) +{ + u_char* next; + struct rt_msghdr* rtm; + struct sockaddr_inarp* sin; + struct sockaddr_dl* sdl; + u_char mac[6]; + + /* Load the kernel routing table */ + if(g_routeBuf == NULL) + { + int mib[6]; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_FLAGS; + mib[5] = RTF_LLINFO; + + if(sysctl(mib, 6, NULL, &g_routeLen, NULL, 0) < 0) + errx(1, "can't load routing table"); + + g_routeBuf = malloc(g_routeLen); + + if(g_routeBuf == NULL) + errx(1, "out of memory"); + + if(sysctl(mib, 6, g_routeBuf, &g_routeLen, NULL, 0) < 0) + errx(1, "can't load routing table"); + } + + /* Get MAC bytes */ + if(!parseMAC(address, mac)) + { + warn("invalid MAC address: %s", address); + return; + } + + /* Look for it in the routing table */ + for(next = g_routeBuf; next < (g_routeBuf + g_routeLen); next += rtm->rtm_msglen) + { + rtm = (struct rt_msghdr*)next; + sin = (struct sockaddr_inarp*)(rtm + 1); + (char*)sdl = (char*)sin + ROUNDUP(sin->sin_len); + + if(!memcmp(LLADDR(sdl), mac, sizeof(mac))) + { + fprintf(fOut, inet_ntoa(sin->sin_addr)); + fputc('\n', fOut); + return; + } + } + + /* oops */ + warnx("unlisted MAC address: %s", address); +} + + + +static void usage() +{ + fprintf(stderr, "usage: resolve [-rv] -f file ...\n"); + fprintf(stderr, " resolve [-rv] address ...\n"); + exit(2); +} + + +/* + * Clean up function. + */ +static void atExit() +{ + if(g_packetBuf != NULL) + free(g_packetBuf); + if(g_domainBuf != NULL) + free(g_domainBuf); +} + + diff --git a/testfile b/testfile new file mode 100644 index 0000000..f3003cc --- /dev/null +++ b/testfile @@ -0,0 +1,11 @@ +1.0.0.0 +1.0.0.1 +1.0.0.2 +1.0.0.3 +1.0.0.4 +1.0.0.5 +1.0.0.6 +1.0.0.7 +32.32.32.33 +32.32.32.34 +32.32.32.35 \ No newline at end of file -- cgit v1.2.3