summaryrefslogtreecommitdiff
path: root/src/ipnets.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ipnets.c')
-rw-r--r--src/ipnets.c484
1 files changed, 484 insertions, 0 deletions
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");
+}
+