summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--BUGS4
-rw-r--r--COPYING14
-rw-r--r--ChangeLog2
-rw-r--r--INSTALL11
-rw-r--r--Makefile.am4
-rw-r--r--NEWS1
-rw-r--r--README15
-rw-r--r--configure.ac26
-rw-r--r--src/Makefile.am7
-rw-r--r--src/ipnets.c484
-rw-r--r--src/ipsort.c153
-rw-r--r--src/resolve.c516
-rw-r--r--testfile11
14 files changed, 1249 insertions, 0 deletions
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: <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
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: <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