diff options
Diffstat (limited to 'src/ipnets.c')
-rw-r--r-- | src/ipnets.c | 484 |
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"); +} + |