diff options
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | BUGS | 4 | ||||
-rw-r--r-- | COPYING | 14 | ||||
-rw-r--r-- | ChangeLog | 2 | ||||
-rw-r--r-- | INSTALL | 11 | ||||
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | README | 15 | ||||
-rw-r--r-- | configure.ac | 26 | ||||
-rw-r--r-- | src/Makefile.am | 7 | ||||
-rw-r--r-- | src/ipnets.c | 484 | ||||
-rw-r--r-- | src/ipsort.c | 153 | ||||
-rw-r--r-- | src/resolve.c | 516 | ||||
-rw-r--r-- | testfile | 11 |
14 files changed, 1249 insertions, 0 deletions
@@ -0,0 +1 @@ +nielsen@memberwebs.com
\ No newline at end of file @@ -0,0 +1,4 @@ +ADDRTOOLS TODOS and BUGS + +- resolve doesn't resverse resolve MAC addresses (not sure if this is possible) +- @@ -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: <nielsen@memberwebs.com> + 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 @@ -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 + + @@ -0,0 +1 @@ +See ChangeLog
\ No newline at end of file @@ -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: <nielsen@memberwebs.com> +*/ + +#include <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> + +#include <netinet/in.h> + +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: <nielsen@memberwebs.com> +*/ + +#include <sys/types.h> +#include <sys/socket.h> +#include <db.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> + +#include <netinet/in.h> + +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: <nielsen@memberwebs.com> +*/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <stdarg.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <resolv.h> + +#if HAVE_CONFIG_H +#include <config.h> +#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 |