/*
 * Program: ipinfo
 * Version: 1.2
 * About:   Returns all possible information about given IP address range
 * Author:  (C) 2004 - 2014 C. McCohy <mccohy@kyberdigi.cz>
 * Usage:   ipinfo a.b.c.d/prefix
 * Example: ipinfo 192.168.0.1/24
 *
 */

#include "ipinfo.h"

int usage(char *progname) {
  printf(
    "%s %s\n"
    "Usage: %s [options] ip [netmask]\n"
    "Options:\n"
    "  -a, --all       Display all (default)\n"
    "  -i, --ip        Display given IP address\n"
    "  -d, --decimal   Display decimal value of given IP address\n"
    "  -c, --class     Display class information\n"
    "  -m, --netmask   Display netmask\n"
    "  -p, --prefix    Display prefix\n"
    "  -n, --network   Display network address\n"
    "  -w, --wildcard  Display wildcard\n"
    "  -b, --broadcast Display broadcast address\n"
    "  -t, --ipcount   Display count af all IP addresses\n"
    "  -H, --hostcount Display count of host IP addresses\n"
    "  -I, --hostmin   Display first usable IP address of a host\n"
    "  -A, --hostmax   Display last usable IP address of a host\n"
    "\n"
    "  -l, --legend    Force display legend (usable only if output has 1 line)\n"
    "  -h, --help, --usage  Print this help\n"
    "\n"
    "  ip and netmask can be specified in one of these formats:\n"
    "    a.b.c.d/prefix   (mask is not specified)\n"
    "    a.b.c.d prefix\n"
    "    a.b.c.d/e.f.g.h  (a.b.c.d is IP address, e.f.g.h is netmask)\n"
    "    a.b.c.d e.f.g.h\n"
    "    a.b.c.d          (netmask will be determined automatically)\n"
    "\n"
    "(C) %s %s <%s>\n"
    "See %s for more information\n",
    PROGRAM_NAME_IPINFO, PROGRAM_RELEASE,
    progname,
    PROGRAM_RELEASE_DATE, PROGRAM_AUTHOR_NAME, PROGRAM_AUTHOR_EMAIL,
    PROGRAM_AUTHOR_HOMEPAGE
  );
  exit(1);
}

int main(int argc, char *argv[]) {
  int i, j;
  char *argv_ip = NULL, *argv_netmask = NULL;

  int ip_address[4], ip_netmask[4], ip_prefix = 0;
  unsigned long long ip = 0, nm = 0, nw = 0, bc = 0;
  
  int display[DISPLAY_CNT], display_cnt = 0, display_legend = 0, display_default = 1;
  char display_values[DISPLAY_CNT][DISPLAY_VALUES_LEN] = {
    "", "", "", "", "", "", "", "", "", "", "", ""
  };
  char display_legends[DISPLAY_CNT][DISPLAY_LEGEND_LEN] = {
    "IP address: ",
    "Decimal:    ",
    "Class:      ",
    "Netmask:    ",
    "Prefix:     ",
    "Network:    ",
    "Wildcard:   ",
    "Broadcast:  ",
    "IP count:   ",
    "Host count: ",
    "Host min:   ",
    "Host max:   "
  };

  for(i = 0; i < DISPLAY_CNT; i++) display[i] = 0;

  if(argc == 1) usage(argv[0]);

  for(i = 1; i < argc; i++)
    if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help") || !strcmp(argv[i], "--usage"))
      usage(argv[0]);
    else if(!strcmp(argv[i], "-a") || !strcmp(argv[i], "--all")) {
      int j;
      for(j = 0; j < DISPLAY_CNT; j++) display[j]=1;
      display_cnt = DISPLAY_CNT;
      display_default = 0;
    } else if((!strcmp(argv[i], "-d") || !strcmp(argv[i], "--decimal")) && !display[DISPLAY_IP]) {
      display[DISPLAY_DECIMAL] = 1;
      display_cnt++;
      display_default=0;
    } else if((!strcmp(argv[i], "-i") || !strcmp(argv[i], "--ip")) && !display[DISPLAY_IP]) {
      display[DISPLAY_IP] = 1;
      display_cnt++;
      display_default = 0;
    } else if((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--class")) && !display[DISPLAY_CLASS]) {
      display[DISPLAY_CLASS] = 1;
      display_cnt++;
      display_default = 0;
    } else if((!strcmp(argv[i], "-m") || !strcmp(argv[i], "--netmask")) && !display[DISPLAY_NETMASK]) {
      display[DISPLAY_NETMASK] = 1;
      display_cnt++;
      display_default = 0;
    } else if((!strcmp(argv[i], "-n") || !strcmp(argv[i], "--network")) && !display[DISPLAY_NETWORK]) {
      display[DISPLAY_NETWORK] = 1;
      display_cnt++;
      display_default = 0;
    } else if((!strcmp(argv[i], "-p") || !strcmp(argv[i], "--prefix")) && !display[DISPLAY_PREFIX]) {
      display[DISPLAY_PREFIX] = 1;
      display_cnt++;
      display_default = 0;
    } else if((!strcmp(argv[i], "-w") || !strcmp(argv[i], "--wildcard")) && !display[DISPLAY_WILDCARD]) {
      display[DISPLAY_WILDCARD]=1;
      display_cnt++;
      display_default = 0;
    } else if((!strcmp(argv[i], "-b") || !strcmp(argv[i], "--broadcast")) && !display[DISPLAY_BROADCAST]) {
      display[DISPLAY_BROADCAST] = 1;
      display_cnt++;
      display_default = 0;
    } else if((!strcmp(argv[i], "-t") || !strcmp(argv[i], "--ipcount")) && !display[DISPLAY_IPCOUNT]) {
      display[DISPLAY_IPCOUNT] = 1;
      display_cnt++;
      display_default = 0;
    } else if((!strcmp(argv[i], "-H") || !strcmp(argv[i], "--hostcount")) && !display[DISPLAY_HOSTCOUNT]) {
      display[DISPLAY_HOSTCOUNT] = 1;
      display_cnt++;
      display_default = 0;
    } else if((!strcmp(argv[i], "-I") || !strcmp(argv[i], "--hostmin")) && !display[DISPLAY_HOSTMIN]) {
      display[DISPLAY_HOSTMIN] = 1;
      display_cnt++;
      display_default = 0;
    } else if((!strcmp(argv[i], "-A") || !strcmp(argv[i], "--hostmax")) && !display[DISPLAY_HOSTMAX]) {
      display[DISPLAY_HOSTMAX] = 1;
      display_cnt++;
      display_default = 0;
    } else if(!strcmp(argv[i], "-l") || !strcmp(argv[i], "--legend"))
      display_legend++;
    else if(!argv_ip)
      argv_ip = argv[i];
    else if(!argv_netmask)
      argv_netmask = argv[i];
    else
      out(argv[0], "invalid parameter");

  if(!argv_ip) out(argv[0], "IP address was not set");

  if(sscanf(argv_ip, "%d.%d.%d.%d/%d.%d.%d.%d", &ip_address[0], &ip_address[1], &ip_address[2], &ip_address[3], &ip_netmask[0], &ip_netmask[1], &ip_netmask[2], &ip_netmask[3]) != 8)
    if(sscanf(argv_ip, "%d.%d.%d.%d/%d", &ip_address[0], &ip_address[1], &ip_address[2], &ip_address[3], &ip_prefix) != 5)
      if(sscanf(argv_ip, "%d.%d.%d.%d", &ip_address[0], &ip_address[1], &ip_address[2], &ip_address[3]) != 4)
        out(argv[0], "invalid syntax of IP address");
      else
        netmask_type = NETMASK_AUTO;
    else
      netmask_type = NETMASK_PREFIX;
  else
    netmask_type = NETMASK_LONG;

  if(argv_netmask) {
    if(netmask_type != NETMASK_AUTO) out(argv[0], "invalid ip/netmask specification");
    if(sscanf(argv_netmask, "%d.%d.%d.%d", &ip_netmask[0], &ip_netmask[1], &ip_netmask[2], &ip_netmask[3]) != 4)
      if(sscanf(argv_netmask, "%d", &ip_prefix) != 1)
        out(argv[0], "invalid syntax of netmask");
      else
        netmask_type = NETMASK_PREFIX;
    else
      netmask_type = NETMASK_LONG;
  }

  for(i = 0; i < 4; i++) {
    if((ip_address[i] < 0) || (ip_address[i] > 255)) out(argv[0], "invalid IP address values (only 0..255 are allowed)");
    ip += (ip_address[i] << ((3 - i) * 8));
  }
  while((unsigned int)ip < class_limits[class]) class--;

  switch(netmask_type) {
    case NETMASK_PREFIX:
      if((ip_prefix < 0) || (ip_prefix > 32)) out(argv[0], "invalid prefix value");
      break;
    case NETMASK_AUTO:
      ip_prefix = class_prefix[class];
      if(!ip_prefix) out(argv[0], "cannot determine netmask for this kind of address");
      break;
    case NETMASK_LONG:
      for(i = 0; i < 4; i++) {
        int found = -1;
        for(j = 0; j < 9; j++)
          if(ip_netmask[i] == netmask_values[j]) {
            found = j;
            break;
          }
        if((found < 0) || (found > netmask_max)) out(argv[0], "invalid netmask values");

        ip_prefix += found;
        if(found < netmask_max) netmask_max = 0;
      }
      break;
  }

  nm = powl(2, 32) - powl(2, (32 - ip_prefix));
  nw = (ip & nm);
  bc = nw + powl(2, (32 - ip_prefix)) - 1;

  snprintf(display_values[DISPLAY_DECIMAL], DISPLAY_VALUES_LEN, "%u", (unsigned int)ip);
  snprintf(display_values[DISPLAY_IP], DISPLAY_VALUES_LEN, "%s", int2ip(ip));
  snprintf(display_values[DISPLAY_CLASS], DISPLAY_VALUES_LEN, "%s", class_names[class]);
  snprintf(display_values[DISPLAY_NETMASK], DISPLAY_VALUES_LEN, "%s", int2ip(nm));
  snprintf(display_values[DISPLAY_PREFIX], DISPLAY_VALUES_LEN, "%d", ip_prefix);
  snprintf(display_values[DISPLAY_NETWORK], DISPLAY_VALUES_LEN, "%s", int2ip(nw));
  snprintf(display_values[DISPLAY_WILDCARD], DISPLAY_VALUES_LEN, "%s", int2ip(powl(2, 32) - nm - 1));
  snprintf(display_values[DISPLAY_BROADCAST], DISPLAY_VALUES_LEN, "%s", int2ip(bc));
  snprintf(display_values[DISPLAY_IPCOUNT], DISPLAY_VALUES_LEN, "%.0Lf", powl(2, 32 - ip_prefix));
  if(ip_prefix < 31) {
    snprintf(display_values[DISPLAY_HOSTCOUNT], DISPLAY_VALUES_LEN, "%llu", (bc - nw - 1));
    snprintf(display_values[DISPLAY_HOSTMIN], DISPLAY_VALUES_LEN, "%s", int2ip(nw + 1));
    snprintf(display_values[DISPLAY_HOSTMAX], DISPLAY_VALUES_LEN, "%s", int2ip(bc - 1));
  }

  if(display_default) {
    for(i = 0; i < DISPLAY_CNT; i++) display[i]=1;
    display_legend++;
  } else if(!display_legend && (display_cnt > 1))
    display_legend++;

  for(i = 0; i < DISPLAY_CNT; i++)
    if(display[i] && strlen(display_values[i]))
      printf("%s%s\n", (display_legend ? display_legends[i]:""), display_values[i]);

  exit(0);
}

