/*
 * Program: ipmatch
 * Version: 1.2
 * About:   Checks if given IP address matches given range
 * Author:  (C) 2004 - 2013 C. McCohy <mccohy@kyberdigi.cz>
 * Usage:   ipmatch ip network/prefix
 * Example: ipmatch 192.168.0.54 192.168.0.1/24
 *
 */

#include "ipinfo.h"

int usage(char *progname) {
  printf(
    "%s %s\n"
    "Usage: %s [-r|-n|-h|-b] ip network/netmask\n"
    "Options:\n"
    "  -r, --range          Check if IP address is in given range (default)\n"
    "  -n, --network        Check if IP address is network address\n"  
    "  -h, --host           Check if IP address is a host address\n"   
    "  -b, --broadcast      Check if IP address is broadcast address\n"
    "\n"
    "  -t, --text           Print text output (IN/OUT) - default: no output\n"
    "      --help, --usage  Print this help\n"
    "\n"
    "  network 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"
    "Return values:\n"
    "  0 - IP was checked as being in range\n"
    "  1 - syntax or usage error\n"
    "  2 - IP was checked as being out of range\n"
    "\n"
    "(C) %s %s <%s>\n"
    "See %s for more information\n",
    PROGRAM_NAME_IPMATCH, 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;
  int result = 0, check = CHECK_RANGE, text_output = 0;
  char *argv_ip = NULL, *argv_network = NULL, *argv_netmask = NULL;

  int ip_address[4], network_address[4], network_netmask[4], network_prefix = 0;
  unsigned int ip = 0;
  unsigned long long nm = 0, nw = 0, bc = 0;

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

  for(i = 1; i < argc; i++)
    if(!strcmp(argv[i], "--help") || !strcmp(argv[i], "--usage"))
      usage(argv[0]);
    else if(!strcmp(argv[i], "-r") || !strcmp(argv[i], "--range"))
      check = CHECK_RANGE;
    else if(!strcmp(argv[i], "-n") || !strcmp(argv[i], "--network"))
      check = CHECK_NETWORK;
    else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--host"))
      check = CHECK_HOST;
    else if(!strcmp(argv[i], "-b") || !strcmp(argv[i], "--broadcast"))
      check = CHECK_BROADCAST;
    else if(!strcmp(argv[i], "-t") || !strcmp(argv[i], "--text"))
      text_output++;
    else if(!argv_ip)
      argv_ip = argv[i];
    else if(!argv_network)
      argv_network = 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(!argv_network) out(argv[0], "Network address was not set");

  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");

  if(sscanf(argv_network, "%d.%d.%d.%d/%d.%d.%d.%d", &network_address[0], &network_address[1], &network_address[2], &network_address[3], &network_netmask[0], &network_netmask[1], &network_netmask[2], &network_netmask[3]) == 8)
    netmask_type = NETMASK_LONG;
  else if(sscanf(argv_network, "%d.%d.%d.%d/%d", &network_address[0], &network_address[1], &network_address[2], &network_address[3], &network_prefix) == 5)
    netmask_type = NETMASK_PREFIX;
  else if(sscanf(argv_network, "%d.%d.%d.%d", &network_address[0], &network_address[1], &network_address[2], &network_address[3]) == 4)
    if(argv_netmask)
      if(sscanf(argv_netmask, "%d.%d.%d.%d", &network_netmask[0], &network_netmask[1], &network_netmask[2], &network_netmask[3]) == 4)
        netmask_type = NETMASK_LONG;
      else if(sscanf(argv_netmask, "%d", &network_prefix) == 1)
        netmask_type = NETMASK_PREFIX;
      else
        out(argv[0], "invalid netmask/prefix syntax");
    else
      netmask_type = NETMASK_AUTO;
  else
    out(argv[0], "invalid network syntax");

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

  for(i = 0; i < 3; i++) {
    if((network_address[i] < 0) || (network_address[i] > 255)) out(argv[0], "invalid network address value (only 0..255 are allowed)");
    nw += (network_address[i] << ((3 - i) * 8));
  }

  switch(netmask_type) {
    case NETMASK_PREFIX:
      if((network_prefix < 0) || (network_prefix > 32)) out(argv[0], "invalid prefix value");
      break;
    case NETMASK_AUTO:
      network_prefix = class_prefix[class];
      if(!network_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(network_netmask[i] == netmask_values[j]) {
            found = j;
            break;
          }
        if((found < 0) || (found > netmask_max)) out(argv[0], "invalid netmask values");

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

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

  switch(check) {
    case CHECK_RANGE:     if((ip & nm) != nw) result = 2; break;
    case CHECK_NETWORK:   if(ip != nw) result = 2; break;
    case CHECK_HOST:      if((ip <= nw) || (ip >= bc)) result = 2; break;
    case CHECK_BROADCAST: if(ip != bc) result = 2; break;
  }

  if(text_output) {
    if(result) printf("OUT\n"); else printf("IN\n");
  }

  exit(result);
}
