/*

                                   _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-2005
       "Ian (Larry) Latter" <ian dot latter at midnightcode dot org>

    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-interface.c :: Midnight Code network interface library	*/


#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <arpa/inet.h>


#include "midnightcode-list.h"
#include "midnightcode-parse.h"
#include "midnightcode-interface.h"


int
midnightcode_interface_resolvcheck(void) {

  if(gethostbyname("www.midnightcode.org") == NULL) {
    return 1;
  }

  return 0;
}


int
midnightcode_interface_list(struct midnightcode_interface_head_t *elements) {
  struct ifreq *ifr;
  struct ifconf ifc;
  struct sockaddr_in sa;
  u_char * uchar_p;
  int sockfd;
  int size;
  int ifrsize;
  int count;
  struct midnightcode_interface_element_t * now_element;


  size = 1;
  ifrsize = ((int)(size * sizeof (struct ifreq)));

  if((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
    return -1;
  }

  ifc.ifc_len = ifrsize;
  ifc.ifc_req = NULL;
  count = 0;

  do {
    ++size;
    ifrsize = ((int)(size * sizeof (struct ifreq)));
    if ((ifc.ifc_req = realloc(ifc.ifc_req, ifrsize)) == NULL) {
      return -1;
    }
    ifc.ifc_len = ifrsize;
    if(ioctl(sockfd, SIOCGIFCONF, &ifc)) {
      return -1;
    }
  } while(ifrsize <= ifc.ifc_len);

  ifr = ifc.ifc_req;
  for(; (char *)ifr < (char *)ifc.ifc_req + ifc.ifc_len; ifr++) {
    if(ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data)
      continue;

    now_element = malloc(sizeof(struct midnightcode_interface_element_t));
    memset(now_element, 0, sizeof(struct midnightcode_interface_element_t));

    if(ioctl(sockfd, SIOCGIFINDEX, ifr)) {
      free(now_element);
      continue;
    }
    now_element->ifindex = ifr->ifr_ifindex;

    if(!ioctl(sockfd, SIOCGIFHWADDR, ifr)) {
      uchar_p = (u_char *)&ifr->ifr_hwaddr.sa_data[0];
      snprintf(now_element->mac_address_s, 31, "%02x:%02x:%02x:%02x:%02x:%02x",
            *uchar_p, *(uchar_p + 1), *(uchar_p + 2),
            *(uchar_p + 3), *(uchar_p + 4), *(uchar_p + 5));
    }
    now_element->mac_address = 
      (*(long double *)&ifr->ifr_hwaddr.sa_data[0]);

    if(ioctl(sockfd, SIOCGIFADDR, ifr)) {
      free(now_element);
      continue;
    }
    strncpy(now_element->name, ifr->ifr_name, sizeof(now_element->name));
    strncpy(now_element->ipv4_address_s,
      inet_ntoa(
        (*(struct in_addr *)&ifr->ifr_addr.sa_data[sizeof sa.sin_port])
      ), 31);
    now_element->ipv4_address = 
      (*(struct in_addr *)&ifr->ifr_addr.sa_data[sizeof sa.sin_port]);

    if(ioctl(sockfd, SIOCGIFNETMASK, ifr)) {
      free(now_element);
      continue;
    }
    strncpy(now_element->ipv4_mask_s,
      inet_ntoa(
        (*(struct in_addr *)&ifr->ifr_netmask.sa_data[sizeof sa.sin_port])
      ), 31);
    now_element->ipv4_mask = 
      (*(struct in_addr *)&ifr->ifr_netmask.sa_data[sizeof sa.sin_port]);

    if(ioctl(sockfd, SIOCGIFBRDADDR, ifr)) {
      free(now_element);
      continue;
    }
    strncpy(now_element->ipv4_broadcast_s,
      inet_ntoa(
        (*(struct in_addr *)&ifr->ifr_broadaddr.sa_data[sizeof sa.sin_port])
      ), 31);
    now_element->ipv4_broadcast = 
      (*(struct in_addr *)&ifr->ifr_broadaddr.sa_data[sizeof sa.sin_port]);

    if(ioctl(sockfd, SIOCGIFFLAGS, ifr)) {
      free(now_element);
      continue;
    }
    now_element->flags = (int)ifr->ifr_flags;

    if(ioctl(sockfd, SIOCGIFMTU, ifr)) {
      free(now_element);
      continue;
    }
    now_element->mtu = ifr->ifr_mtu;

    /* TODO: true alias detection - :: is not alias */
    if(midnightcode_parse_any(now_element->name, ":"))
      now_element->is_alias = 1;

    midnightcode_list_append(elements, now_element);

    count++;
  }
  close(sockfd);

  return count;
}


int
midnightcode_interface_byindex(struct midnightcode_interface_head_t *elements,
                               int ifindex) {
  struct midnightcode_interface_element_t * now_element;
  struct midnightcode_interface_element_t * old_element;


  if(midnightcode_interface_list(elements) < 0)
    return -1;

  old_element = NULL;
  midnightcode_list_for_each(now_element, elements) {
    if(old_element != NULL) {
      free(old_element);
      old_element = NULL;
    }
    if(now_element->ifindex != ifindex || now_element->is_alias == 1) {
      midnightcode_list_remove(elements, now_element);
      old_element = now_element;
    }
  }
  if(old_element != NULL)
    free(old_element);

  return 0;
}

