/*

                                   _JNJ`                        
                               .JNMH`                           
                              JMMF`       `;.                   
                            .NMM)          `MN.                 
                            MMM)            (MML                
                           (MMM`             MMML               
                     M  I  D  N  I  G  H  T    C  o  D  E       
                           (NMMF             MHNH               
                            NMML            .MMM                
                             NMML          .NMH                 
                              4MMNL       .#F                   
                               `4HNNL_   `                      
                                   `"""`                        

                          Copyright (C) 2004-2006
       "Ian (Larry) Latter" <ian dot latter at midnightcode dot org>

    Midnight Code is a registered trademark of Ian Latter.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, and mirrored at the Midnight Code web
    site; as at version 2 of the License only.
  
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    General Public License for more details.
  
    You should have received a copy of the GNU General Public License
    (version 2) along with this program; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA, or see http://midnightcode.org/gplv2.txt

*/


/* midnightcode-ip.c :: Midnight Code Internet Protocol library	*/

/* Includes software to support RFCs :: 1166, 1517-1520, 1700, 1878,
                                        1918, 2544, 3068, 3171, 3330 */


#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <ctype.h>
#include <arpa/inet.h>

#include "preferences.h"
#include "midnightcode-ip.h"



int
midnightcode_ip_cidrtomask(u_int32_t * netmask, int cidr) {


  *netmask = htonl(~((1 << (32 - cidr)) - 1));

  return 1;
}



int
midnightcode_ip_masktocidr(int * cidr, u_int32_t netmask) {
  int bit_index;


  *cidr = 32;
  for (bit_index = 0; bit_index < 32; bit_index++) {
    if (!(ntohl(netmask) & ((2 << bit_index) - 1)))
      (*cidr)--;
  }

  return 1;
}



int
midnightcode_ip_classmask(u_int32_t * netmask, u_int32_t address) {
  uint class_a_upper;
  uint class_b_upper;
  uint class_c_upper;


  class_a_upper = 127;
  class_b_upper = 191;
  class_c_upper = 223;

  if (((ntohl(address) & 0xff000000) >> 24) <= class_a_upper) {
    *netmask = 0xff000000;

  } else if (((ntohl(address) & 0xff000000) >> 24) <= class_b_upper) {
    *netmask = 0xffff0000;

  } else if (((ntohl(address) & 0xff000000) >> 24) <= class_c_upper) {
    *netmask = 0xffffff00;

  } else {
    *netmask = 0xffffffff;

  }

  return 1;
}



int
midnightcode_ip_broadcast(u_int32_t * broadcast, u_int32_t address,
                          u_int32_t netmask) {
  u_int32_t address_n;
  u_int32_t netmask_n;


  address_n = htonl(address);
  netmask_n = htonl(netmask);
  *broadcast = (address_n & netmask_n) | ~netmask_n;

  return 1;
}



int
midnightcode_ip_network(u_int32_t * network, u_int32_t address,
                        u_int32_t netmask) {
  u_int32_t address_n;
  u_int32_t netmask_n;


  address_n = htonl(address);
  netmask_n = htonl(netmask);
  *network = address_n & netmask_n;

  return 1;
}



int
midnightcode_ip_isinnet(u_int32_t target, u_int32_t address,
                        u_int32_t netmask) {
  int ret_i;
  u_int32_t target_n;
  u_int32_t address_n;
  u_int32_t netmask_n;
  u_int32_t network;
  u_int32_t broadcast;


  ret_i = 0;
  target_n = htonl(target);
  address_n = htonl(address);
  netmask_n = htonl(netmask);

  network = address_n & netmask_n;
  broadcast = network | ~netmask_n;

  if(network <= target_n && target_n <= broadcast)
    ret_i = 1;

  return ret_i;
}



int
midnightcode_ip_isinnet_s(u_int32_t target, const char * address_s,
                          const char * netmask_s) {
  int ret_i;
  u_int32_t address;
  u_int32_t netmask;


  ret_i = 0;
  if(inet_pton(AF_INET, address_s, &address) < 0)
    return ret_i;
  if(inet_pton(AF_INET, netmask_s, &netmask) < 0)
    return ret_i;

  ret_i = midnightcode_ip_isinnet(target, address, netmask);

  return ret_i;
}



int
midnightcode_ip_isspecial(int type, u_int32_t target) {
  int ret_i;
  struct midnightcode_ip_subnets_t * subnet;
  int subidx;
  u_int32_t address;
  u_int32_t netmask;

  struct midnightcode_ip_subnets_t set_rfc1700[] = {
    { "0.0.0.0", "255.0.0.0" },
    { "127.0.0.0", "255.0.0.0" },
    { "240.0.0.0", "240.0.0.0" },
    { 0, 0 }
  };

  struct midnightcode_ip_subnets_t set_rfc1918[] = {
    { "10.0.0.0", "255.0.0.0" },
    { "172.16.0.0", "255.240.0.0" },
    { "192.168.0.0", "255.255.0.0" },
    { 0, 0 }
  };

  struct midnightcode_ip_subnets_t set_rfc2544[] = {
    { "198.18.0.0", "255.254.0.0" },
    { 0, 0 }
  };

  struct midnightcode_ip_subnets_t set_rfc3068[] = {
    { "192.88.99.0", "255.255.255.0" },
    { 0, 0 }
  };

  struct midnightcode_ip_subnets_t set_rfc3171[] = {
    { "224.0.0.0", "240.0.0.0" },
    { 0, 0 }
  };

  struct midnightcode_ip_subnets_t set_rfc3330[] = {
    { "169.254.0.0", "255.255.0.0" },
    { "192.0.2.0", "255.255.255.0" },
    { 0, 0 }
  };


  ret_i = 0;
  address = 0;
  netmask = 0;

  if(type & MIDNIGHTCODE_IP_SPECIAL_RFC1700) {
    subnet = set_rfc1700;
    for(subidx = 0; subnet[subidx].address != NULL; subidx++) {
      ret_i += midnightcode_ip_isinnet_s(target, subnet[subidx].address,
                                         subnet[subidx].netmask);
    }
  }
  if(type & MIDNIGHTCODE_IP_SPECIAL_RFC1918) {
    subnet = set_rfc1918;
    for(subidx = 0; subnet[subidx].address != NULL; subidx++) {
      ret_i += midnightcode_ip_isinnet_s(target, subnet[subidx].address,
                                         subnet[subidx].netmask);
    }
  }
  if(type & MIDNIGHTCODE_IP_SPECIAL_RFC2544) {
    subnet = set_rfc2544;
    for(subidx = 0; subnet[subidx].address != NULL; subidx++) {
      ret_i += midnightcode_ip_isinnet_s(target, subnet[subidx].address,
                                         subnet[subidx].netmask);
    }
  }
  if(type & MIDNIGHTCODE_IP_SPECIAL_RFC3068) {
    subnet = set_rfc3068;
    for(subidx = 0; subnet[subidx].address != NULL; subidx++) {
      ret_i += midnightcode_ip_isinnet_s(target, subnet[subidx].address,
                                         subnet[subidx].netmask);
    }
  }
  if(type & MIDNIGHTCODE_IP_SPECIAL_RFC3171) {
    subnet = set_rfc3171;
    for(subidx = 0; subnet[subidx].address != NULL; subidx++) {
      ret_i += midnightcode_ip_isinnet_s(target, subnet[subidx].address,
                                         subnet[subidx].netmask);
    }
  }
  if(type & MIDNIGHTCODE_IP_SPECIAL_RFC3330) {
    subnet = set_rfc3330;
    for(subidx = 0; subnet[subidx].address != NULL; subidx++) {
      ret_i += midnightcode_ip_isinnet_s(target, subnet[subidx].address,
                                         subnet[subidx].netmask);
    }
  }

  return ret_i;
}



int
midnightcode_ip_isprivate(u_int32_t address) {
  int ret_i;


  ret_i = 0;
  if(midnightcode_ip_isspecial(MIDNIGHTCODE_IP_SPECIAL_RFC1918, address))
    ret_i = 1;

  return ret_i;
}



int
midnightcode_ip_ismulticast(u_int32_t address) {
  int ret_i;


  ret_i = 0;
  if(midnightcode_ip_isspecial(MIDNIGHTCODE_IP_SPECIAL_RFC3171, address))
    ret_i = 1;

  return ret_i;
}



int
midnightcode_ip_cmp(struct in_addr *ipv4_a, struct in_addr *ipv4_b) {


  return memcmp(ipv4_a, ipv4_b, sizeof(struct in_addr));

}

