/*

                                   _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-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 <errno.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 "preferences.h"
#include "midnightcode-list.h"
#include "midnightcode-parse.h"
#include "midnightcode-license.h"
#include "midnightcode-config.h"
#include "midnightcode-interface.h"
#include "midnightcode-interface-vendor.h"



int
midnightcode_interface_writehosts(char * hosts_file,
                     struct midnightcode_interface_head_t * elements) {
  struct midnightcode_interface_element_t * element;
  FILE *fp;
  char nodeid[MIDNIGHTCODE_LICENSE_NODEID_SIZE];
  char * nodeid_p;
  char * buffer;
  int buffer_size;
  int comment_offset;


  if(!hosts_file)
    return -1;
  if(!elements)
    return -1;

  if(access(hosts_file, F_OK) == 0) {
    if(access(hosts_file, W_OK) != 0) {
      return -1;
    }
  }

  buffer_size = 4096;
  buffer = malloc(buffer_size);
  if(!buffer)
    return -1;

  if((fp = fopen(hosts_file, "w")) == NULL) {
    free(buffer);
    return -1;
  }

  fprintf(fp, "#\n");
  fprintf(fp, "# Hosts configuration file: %s\n", hosts_file);
  fprintf(fp, "#\n");
  fprintf(fp, "%-17.17s %s %s       # %s\n",
    "127.0.0.1", "localhost", "localhost.localdomain", "lo");

  comment_offset = 55;
  nodeid_p = nodeid;
  midnightcode_list_for_each(element, elements) {
    if(strncmp(element->name, "lo", 2) == 0)
      continue;
    if(strlen(element->ipv4_address_s) == 0)
      continue;

    memset(buffer, 0, buffer_size);
    memset(&nodeid, 0, MIDNIGHTCODE_LICENSE_NODEID_SIZE);

    midnightcode_license_get_nodeid(nodeid_p,
      MIDNIGHTCODE_LICENSE_NODEID_FULL, &(element->ipv4_address));
    snprintf(buffer, buffer_size - 1, "%-17.17s n%s",
      element->ipv4_address_s, nodeid);
    for(; strlen(buffer) < (unsigned int)comment_offset; )
      strncat(buffer, " ", buffer_size - strlen(buffer) - 1);
    strncat(buffer, " # ", buffer_size - strlen(buffer) - 1);
    strncat(buffer, element->name, buffer_size - strlen(buffer) - 1);
    strncat(buffer, "\n", buffer_size - strlen(buffer) - 1);

    if(fputs(buffer, fp) < 0 ) {
      unlink(hosts_file);
      free(buffer);
      return -1;
    }
  }

  free(buffer);
  fclose(fp);


  return 0;
}



int
midnightcode_interface_up(
                      struct midnightcode_interface_element_t * interface) {
  struct ifreq ifr;
  int sockfd;
  int one;


  if(!interface)
    return -1;

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

  one = 1;
  if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) {
    close(sockfd);
    return -1;
  }

  memset(&ifr, 0, sizeof(struct ifreq));
  strncpy(ifr.ifr_name, interface->name, IFNAMSIZ);

  if(ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
    close(sockfd);
    return -1;
  }

  /* TODO: Improve "up" check for vifs; check for IN_ADDR_ANY values */
  if(!(ifr.ifr_flags & IFF_UP)) {
    if(midnightcode_interface_configure(interface)) {
      close(sockfd);
      return -1;
    }
  } else {
    if(ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
      close(sockfd);
      return -1;
    }
    interface->flags = ifr.ifr_flags;
  }
  interface->is_enabled = 1;
  close(sockfd);


  return 0;
}



int
midnightcode_interface_down(
                      struct midnightcode_interface_element_t * interface) {
  struct ifreq ifr;
  int sockfd;
  int one;


  if(!interface)
    return -1;

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

  one = 1;
  if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) {
    close(sockfd);
    return -1;
  }

  memset(&ifr, 0, sizeof(struct ifreq));
  strncpy(ifr.ifr_name, interface->name, IFNAMSIZ);

  if(ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
    close(sockfd);
    return -1;
  }

  if(ifr.ifr_flags & IFF_UP) {
    ifr.ifr_flags &= ~IFF_UP;
    if(ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
      close(sockfd);
      return -1;
    }
  }

  if(ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
    close(sockfd);
    return -1;
  }
  interface->flags = ifr.ifr_flags;
  interface->is_enabled = 0;
  close(sockfd);


  return 0;
}



int
midnightcode_interface_configure(
                      struct midnightcode_interface_element_t * interface) {
  struct ifreq ifr;
  struct sockaddr_in *sa;
  int sockfd;
  int one;


  if(!interface)
    return -1;

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

  one = 1;
  if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) {
    close(sockfd);
    return -1;
  }

  memset(&ifr, 0, sizeof(struct ifreq));
  strncpy(ifr.ifr_name, interface->name, IFNAMSIZ);

  if(interface->is_static) {

    sa = (struct sockaddr_in *)&ifr.ifr_addr;
    sa->sin_family = AF_INET;
    sa->sin_port = 0;
    sa->sin_addr = interface->ipv4_address;

    if(ioctl(sockfd, SIOCSIFADDR, &ifr)) {
      close(sockfd);
      return -1;
    }

    if(interface->ipv4_mask.s_addr) {
      sa->sin_addr = interface->ipv4_mask;
      if(ioctl(sockfd, SIOCSIFNETMASK, &ifr)) {
        close(sockfd);
        return -1;
      }

      sa->sin_addr = interface->ipv4_address;
      if(ioctl(sockfd, SIOCSIFADDR, &ifr)) {
        close(sockfd);
        return -1;
      }
    }

    if(interface->ipv4_broadcast.s_addr) {
      sa->sin_addr = interface->ipv4_broadcast;
      if(ioctl(sockfd, SIOCSIFBRDADDR, &ifr)) {
        close(sockfd);
        return -1;
      }
    }

  }

  if(!interface->is_alias) {
    if(interface->mtu) {
      ifr.ifr_mtu = interface->mtu;
      if(ioctl(sockfd, SIOCGIFMTU, &ifr)) {
        close(sockfd);
        return -1;
      }
    }
  }

  ifr.ifr_flags = interface->flags | IFF_UP | IFF_RUNNING | IFF_BROADCAST;
  if(ioctl(sockfd, SIOCSIFFLAGS, &ifr)) {
    close(sockfd);
    return -1;
  }
  interface->flags = ifr.ifr_flags;
  interface->is_enabled = 1;
  close(sockfd);


  return 0;
}



int
midnightcode_interface_add(
                          struct midnightcode_interface_head_t * elements,
                          char * pattern,
                          struct midnightcode_interface_element_t ** element) {


  if(!elements)
    return -1;
  if(!pattern)
    return -1;
  if(strlen(pattern) > 63)
    return -1;

  *element = NULL;
  if(midnightcode_interface_byname(elements, pattern, element) == 1)
    return -1;

  *element = malloc(sizeof(struct midnightcode_interface_element_t));
  memset(*element, 0, sizeof(struct midnightcode_interface_element_t));

  snprintf((*element)->name, 63, "%s", pattern);
  if(midnightcode_interface_isalias(*element) < 0) {
    free(*element);
    *element = NULL;
    return -1;
  }

  midnightcode_list_append(elements, (*element));


  return 1;
}



int
midnightcode_interface_probe(
                          struct midnightcode_interface_element_t * element,
                          int sockfd, void * ifr_v) {
  struct sockaddr_in sa;
  struct ifreq * ifr;
  u_char * uchar_p;


  if(!element)
    return -1;
  if(sockfd < 0)
    return -1;
  if(!ifr_v)
    return -1;

  ifr = (struct ifreq *)ifr_v;
  if(ioctl(sockfd, SIOCGIFINDEX, ifr)) {
    return -1;
  }
  element->ifindex = ifr->ifr_ifindex;
  strncpy(element->name, ifr->ifr_name, sizeof(element->name));

  if(midnightcode_interface_isalias(element) < 0) {
    return -1;
  }

  if(ioctl(sockfd, SIOCGIFFLAGS, ifr)) {
    return -1;
  }
  element->flags = (int)ifr->ifr_flags;
  if(ifr->ifr_flags & IFF_UP)
    element->is_enabled = 1;

  if(!ioctl(sockfd, SIOCGIFHWADDR, ifr)) {
    uchar_p = (u_char *)&ifr->ifr_hwaddr.sa_data[0];
    snprintf(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));
    memcpy(&element->mac_address, &ifr->ifr_hwaddr.sa_data[0], IFHWADDRLEN);
    midnightcode_interface_vendor(element->vendor_name, 128,
      element->mac_address);
  }

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

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

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

  if(!ioctl(sockfd, SIOCGIFMTU, ifr)) {
    element->mtu = ifr->ifr_mtu;
  }


  return 0;
}



int
midnightcode_interface_list(struct midnightcode_interface_head_t * elements) {
  struct midnightcode_interface_element_t * element;
  struct ifreq *ifr;
  struct ifconf ifc;
  char ifname[IFNAMSIZ + 1];
  char * ifname_p;
  int sockfd;
  int size;
  int ifrsize;
  int count;
  int ifindex;
  int have_interface;


  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)) {
      free(ifc.ifc_req);
      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;

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

    if(midnightcode_interface_probe(element, sockfd, ifr) < 0) {
      free(element);
      continue;
    }

    midnightcode_list_append(elements, element);

    count++;
  }
  free(ifc.ifc_req);


  /* NOTE: SIOCIFCONF won't return un-addressed interfaces */

  for(ifindex = 1;; ifindex++) {
    ifname_p = ifname;
    memset(ifname_p, 0, sizeof(ifname));
    if(!if_indextoname(ifindex, ifname_p)) {
      break;
    }
    have_interface = 0;
    element = NULL;
    midnightcode_list_for_each(element, elements) {
      if(element->ifindex == ifindex &&
        strncmp(element->name, ifname, strlen(ifname)) == 0) {
        have_interface = 1;
        break;
      }
    }
    if(!have_interface)  {
      ifr = malloc(sizeof(struct ifreq));
      if(!ifr)
        continue;
      memset(ifr, 0, sizeof(struct ifreq));
      ifr->ifr_ifindex = ifindex;
      strncpy(ifr->ifr_name, ifname, IFNAMSIZ);

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

      if(midnightcode_interface_probe(element, sockfd, ifr) < 0) {
        free(element);
        free(ifr);
        continue;
      }
      free(ifr);

      midnightcode_list_append(elements, element);

      count++;
    }
  }
  close(sockfd);


  return count;
}



int
midnightcode_interface_support(void) {


  if(access("/proc/net/dev", R_OK) != 0)
    return 0;


  return 1;
}



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;
  int ret_i;


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

  ret_i = 0;
  old_element = NULL;
  midnightcode_list_for_each(now_element, elements) {
    if(now_element->ifindex == ifindex && now_element->is_alias != 1) {
      ret_i++;
    } else {
      old_element = now_element;
      midnightcode_list_remove(elements, now_element);
      if(old_element != NULL) {
        free(old_element);
        old_element = NULL;
      }
    }
  }


  return ret_i;
}



int
midnightcode_interface_byname(
                          struct midnightcode_interface_head_t * elements,
                          char * pattern,
                          struct midnightcode_interface_element_t ** element) {
  struct midnightcode_interface_element_t * found_element;


  if(!elements || !pattern)
    return -1;

  if(strlen(pattern) < 1)
    return -1;

  midnightcode_list_for_each(found_element, elements) {
    if(strcmp(found_element->name, pattern) == 0) {
      *element = found_element;
      return 1;
    }
  }

  return 0;
}



int
midnightcode_interface_only(struct midnightcode_interface_head_t * elements) {
  struct midnightcode_interface_element_t * now_element;
  struct midnightcode_interface_element_t * old_element;
  int ret_i;


  ret_i = 0;
  old_element = NULL;
  midnightcode_list_for_each(now_element, elements) {
    if(strncmp(now_element->name, "eth", 3) == 0) {
      ret_i++;
    } else {
      old_element = now_element;
      midnightcode_list_remove(elements, now_element);
      if(old_element != NULL) {
        free(old_element);
        old_element = NULL;
      }
    }
  }


  return ret_i;
}



int
midnightcode_interface_to_config(
                     struct midnightcode_interface_head_t * interface_elements,
                     struct midnightcode_config_head_t * config_elements) {
  struct midnightcode_interface_element_t * interface_element;


  if(!interface_elements || !config_elements)
    return -1;

  if(midnightcode_config_del_structure(config_elements,
                                       MIDNIGHTCODE_INTERFACE_STRUCTURE))
    return -1;

  midnightcode_list_for_each(interface_element, interface_elements) {
    if(midnightcode_config_add_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "is_enabled",
                                    &interface_element->is_enabled,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      return -1;

    if(midnightcode_config_add_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "is_alias",
                                    &interface_element->is_alias,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      return -1;

    if(midnightcode_config_add_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "flags",
                                    &interface_element->flags,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      return -1;

    if(midnightcode_config_add_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "ifindex",
                                    &interface_element->ifindex,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      return -1;

    if(midnightcode_config_add_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "vendor_name",
                                    interface_element->vendor_name,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      return -1;

    if(midnightcode_config_add_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "mac_address",
                                    &interface_element->mac_address,
                                    MIDNIGHTCODE_PARSE_TYPE_HWADDR))
      return -1;

    if(midnightcode_config_add_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "is_static",
                                    &interface_element->is_static,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      return -1;

    if(midnightcode_config_add_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "ipv4_address",
                                    interface_element->ipv4_address_s,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      return -1;

    if(midnightcode_config_add_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "ipv4_mask",
                                    interface_element->ipv4_mask_s,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      return -1;

    if(midnightcode_config_add_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "ipv4_broadcast",
                                    interface_element->ipv4_broadcast_s,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      return -1;

    if(midnightcode_config_add_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "mtu",
                                    &interface_element->mtu,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      return -1;

  }

  return 0;
}



int
midnightcode_interface_from_config(
                     struct midnightcode_interface_head_t * interface_elements,
                     struct midnightcode_config_head_t * config_elements) {
  struct midnightcode_interface_element_t * interface_element;
  struct midnightcode_config_section_element_t * section_element;
  struct midnightcode_config_section_element_t * old_element;
  struct midnightcode_config_section_head_t * section_elements;
  void * tmp_void_p;
  u_char * uchar_p;
  int ret_i;
  midnightcode_list_init(config_section_struct_head,
                         midnightcode_config_section_element_t);


  if(!interface_elements || !config_elements)
    return -1;

  tmp_void_p = (void *)&config_section_struct_head;
  section_elements = tmp_void_p;

  if(!midnightcode_config_get_sections(config_elements,
                                       section_elements,
                                       MIDNIGHTCODE_INTERFACE_STRUCTURE))
    return -1;

  ret_i = 0;
  old_element = NULL;
  midnightcode_list_for_each(section_element, section_elements) {

    interface_element = malloc(
                          sizeof(struct midnightcode_interface_element_t));
    if(!interface_element)
      break;
    memset(interface_element, 0,
      sizeof(struct midnightcode_interface_element_t));

    snprintf(interface_element->name, 63, "%s", section_element->section);

    ret_i = -1;
    if(midnightcode_config_get_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "is_enabled",
                                    &interface_element->is_enabled, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    /* TODO: Consider dropping is_alias from config file  */
    if(midnightcode_config_get_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "is_alias",
                                    &interface_element->is_alias, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    if(midnightcode_interface_isalias(interface_element) < 0)
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "flags",
                                    &interface_element->flags, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "ifindex",
                                    &interface_element->ifindex, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "mac_address",
                                    &interface_element->mac_address, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_HWADDR))
      break;

    uchar_p = interface_element->mac_address;
    snprintf(interface_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));
    midnightcode_interface_vendor(interface_element->vendor_name, 128,
      interface_element->mac_address);

    if(midnightcode_config_get_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "is_static",
                                    &interface_element->is_static, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "ipv4_address",
                                    &interface_element->ipv4_address, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_IP))
      break;

    inet_ntop(AF_INET, &interface_element->ipv4_address,
      interface_element->ipv4_address_s, 31);

    if(midnightcode_config_get_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "ipv4_mask",
                                    &interface_element->ipv4_mask, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_IP))
      break;

    inet_ntop(AF_INET, &interface_element->ipv4_mask,
      interface_element->ipv4_mask_s, 31);

    if(midnightcode_config_get_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "ipv4_broadcast",
                                    &interface_element->ipv4_broadcast, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_IP))
      break;

    inet_ntop(AF_INET, &interface_element->ipv4_broadcast,
      interface_element->ipv4_broadcast_s, 31);

    if(midnightcode_config_get_pair(config_elements,
                                    MIDNIGHTCODE_INTERFACE_STRUCTURE,
                                    interface_element->name,
                                    "mtu",
                                    &interface_element->mtu, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    ret_i = 0;
    midnightcode_list_append(interface_elements, interface_element);

    old_element = section_element;
    midnightcode_list_remove(section_elements, section_element);
    if(old_element != NULL) {
      free(old_element);
      old_element = NULL;
    }

  }
  old_element = NULL;
  midnightcode_list_for_each(section_element, section_elements) {
    old_element = section_element;
    midnightcode_list_remove(section_elements, section_element);
    if(old_element != NULL) {
      free(old_element);
      old_element = NULL;
    }
  }


  return ret_i;
}



int
midnightcode_interface_transfer(
                          struct midnightcode_interface_element_t * destination,
                          struct midnightcode_interface_element_t * source) {


  if(!destination)
    return -1;
  if(!source)
    return -1;

  destination->flags = source->flags;
  destination->is_static = source->is_static;

  memcpy(&destination->ipv4_address_s, &source->ipv4_address_s, 31);
  destination->ipv4_address = source->ipv4_address;
  memcpy(&destination->ipv4_mask_s, &source->ipv4_mask_s, 31);
  destination->ipv4_mask = source->ipv4_mask;
  memcpy(&destination->ipv4_broadcast_s, &source->ipv4_broadcast_s, 31);
  destination->ipv4_broadcast = source->ipv4_broadcast;
  destination->mtu = source->mtu;

  /* TODO: Seriously consider error checking to pass upwards */
  midnightcode_interface_isalias(destination);

  midnightcode_interface_configure(destination);

  if(source->is_enabled) {
    midnightcode_interface_up(destination);
  } else {
    midnightcode_interface_down(destination);
  }


  return 1;
}



int
midnightcode_interface_marry(
                          struct midnightcode_interface_head_t * lives,
                          struct midnightcode_interface_head_t * stores,
                          int options) {
  struct midnightcode_interface_element_t * old_element;
  struct midnightcode_interface_element_t * element;
  struct midnightcode_interface_element_t * live;
  struct midnightcode_interface_element_t * store;
  int match_mac;
  int match_name;
  int ret_i;


  if(!lives)
    return -1;
  if(!stores)
    return -1;

  if(options < MIDNIGHTCODE_INTERFACE_MARRY_DEFAULT)
    return -1;
  if(options == MIDNIGHTCODE_INTERFACE_MARRY_DEFAULT) {
    options = MIDNIGHTCODE_INTERFACE_MARRY_BASIC;
    options |= MIDNIGHTCODE_INTERFACE_MARRY_ONMAC;
    options |= MIDNIGHTCODE_INTERFACE_MARRY_ONNAME;
  }

  ret_i = 0;

  if(midnightcode_list_count(stores) == 0)
    return ret_i;
  midnightcode_list_for_each(live, lives) {
    midnightcode_list_for_each(store, stores) {
      match_mac = 0;
      match_name = 0;
      if(memcmp(&live->mac_address, &store->mac_address, IFHWADDRLEN) == 0)
        match_mac = MIDNIGHTCODE_INTERFACE_MARRY_ONMAC;
      if(strlen(live->name) == strlen(store->name)) {
        if(strncmp(live->name, store->name, strlen(live->name)) == 0)
          match_name = MIDNIGHTCODE_INTERFACE_MARRY_ONNAME;
      }
      if(((options & MIDNIGHTCODE_INTERFACE_MARRY_ONMAC) == match_mac) &&
        ((options & MIDNIGHTCODE_INTERFACE_MARRY_ONNAME) == match_name)) {
        if(midnightcode_interface_transfer(live, store) == 1) {
          ret_i++;
          old_element = store;
          midnightcode_list_remove(stores, store);
          if(old_element != NULL) {
            free(old_element);
            old_element = NULL;
          }
        }
      }
    }
  }

  /* TODO: Clone alias adding block for complex algo, but after all matching */

  if(options & MIDNIGHTCODE_INTERFACE_MARRY_BASIC) {

    if(midnightcode_list_count(stores) == 0)
      return ret_i;
    midnightcode_list_for_each(store, stores) {
      if(!store->is_alias)
        continue;
      midnightcode_list_for_each(live, lives) {
        match_mac = 0;
        match_name = 0;
        if(memcmp(&live->mac_address, &store->mac_address, IFHWADDRLEN) == 0)
          match_mac = MIDNIGHTCODE_INTERFACE_MARRY_ONMAC;
        if(strlen(live->name_root) == strlen(store->name_root)) {
          if(strncmp(live->name_root, store->name_root,
            strlen(live->name_root)) == 0)
            match_name = MIDNIGHTCODE_INTERFACE_MARRY_ONNAME;
        }
        if(((options & MIDNIGHTCODE_INTERFACE_MARRY_ONMAC) == match_mac) &&
          ((options & MIDNIGHTCODE_INTERFACE_MARRY_ONNAME) == match_name)) {
          if(midnightcode_interface_add(lives, store->name, &element) != 1)
            break;
          memcpy(&element->mac_address, &live->mac_address, IFHWADDRLEN);
          if(midnightcode_interface_transfer(element, store) == 1) {
            ret_i++;
            old_element = store;
            midnightcode_list_remove(stores, store);
            if(old_element != NULL) {
              free(old_element);
              old_element = NULL;
            }
            break;
          }
        }
      }
    }
    return ret_i;
  }

  /* TODO: Build complex marry algo - cross populate names on macs */


  return 0;
}



/* TODO: Do something with this serialization pile */
/*
int
midnightcode_interface_serialize(
                          struct midnightcode_interface_head_t * elements,
                          char * buffer) {
  struct midnightcode_interface_element_t * now_element;
  struct midnightcode_interface_element_t * old_element;


  midnightcode_list_for_each(now_element, elements) {

  }

  return 0;
}
*/


int
midnightcode_interface_isalias(
                          struct midnightcode_interface_element_t * element) {
  int delin_offset;


  if(!element)
    return -1;

  delin_offset = midnightcode_parse_any_offset(element->name, ":");
  if(delin_offset < 0) {
    element->is_alias = 0;
    strncpy(element->name_root, element->name, sizeof(element->name_root));
    return element->is_alias;
  }

  /* NOTE: "::" does not denote an alias */
  if(delin_offset < (int)(strlen(element->name) - 1))  {
    if(midnightcode_parse_any_offset(&(element->name[delin_offset + 1]), ":") >=
      0) {
      element->is_alias = 0;
      strncpy(element->name_root, element->name, sizeof(element->name_root));
      return element->is_alias;
    }
  }

  element->is_alias = 1;
  if(delin_offset < (int)sizeof(element->name_root)) {
    strncpy(element->name_root, element->name, delin_offset);
  } else {
    strncpy(element->name_root, element->name, sizeof(element->name_root));
  }
  if(delin_offset < (int)(strlen(element->name) - 1))  {
    if((strlen(element->name) - delin_offset - 1) <
      sizeof(element->name_alias)) {
      strncpy(element->name_alias, &(element->name[delin_offset + 1]),
        strlen(element->name) - delin_offset - 1);
    } else {
      strncpy(element->name_alias, &(element->name[delin_offset + 1]),
        sizeof(element->name_alias));
    }
  }


  return element->is_alias;
}



int
midnightcode_interface_vendor(char * name, int name_size,
                          unsigned char * mac_address) {
  unsigned int vendor_id;
  int idx;


  if(!name)
    return -1;
  if(name_size <= 0)
    return -1;
  if(!mac_address)
    return -1;

  vendor_id = (*(mac_address) * 0x10000) + (*(mac_address + 1) * 0x100) +
    (*(mac_address + 2));

  if(!vendor_id) {
    snprintf(name, name_size - 1, "Unknown - No Vendor");
    return -1;
  }

  idx = 0;

#ifdef __MIDNIGHTCODE_INTERFACE_VENDOR_H

  for(idx = 0; idx < MIDNIGHTCODE_INTERFACE_VENDORS &&
    midnightcode_interface_vendors[idx].id != vendor_id; idx++);
  if(idx == MIDNIGHTCODE_INTERFACE_VENDORS) {
    snprintf(name, name_size - 1, "Unknown - Not Found");
    return -1;
  }
  if(!midnightcode_interface_vendors[idx].name) {
    snprintf(name, name_size - 1, "Unknown - Not Found");
    return -1;
  }
  if(midnightcode_interface_vendors[idx].id != vendor_id) {
    snprintf(name, name_size - 1, "Unknown - Not Found");
    return -1;
  }

  snprintf(name, name_size - 1, "%s",
    midnightcode_interface_vendors[idx].name);


  return 0;

#else

  snprintf(name, name_size - 1, "Unknown - Not Searched");


  return -2;

#endif

}

