/*

                                   _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-2007
       "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-resolve.c :: Midnight Code network resolver 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 <resolv.h>

#include "preferences.h"
#include "midnightcode-list.h"
#include "midnightcode-parse.h"
#include "midnightcode-config.h"
#include "midnightcode-resolve.h"



int
midnightcode_resolve_check(int options) {
  int ret_i;


  if(options == MIDNIGHTCODE_RESOLVE_TEST_DEFAULT)
    options = MIDNIGHTCODE_RESOLVE_TEST_PUBLIC;

  ret_i = 0;
  if(options & MIDNIGHTCODE_RESOLVE_TEST_PRIVATE) {
    if(gethostbyname("www") != NULL) {
      ret_i |= MIDNIGHTCODE_RESOLVE_TEST_PRIVATE;
    } else {
      if(gethostbyname("mail") != NULL) {
        ret_i |= MIDNIGHTCODE_RESOLVE_TEST_PRIVATE;
      } else {
        if(gethostbyname("proxy") != NULL) {
          ret_i |= MIDNIGHTCODE_RESOLVE_TEST_PRIVATE;
        }
      }
    }
  }

  if(options & MIDNIGHTCODE_RESOLVE_TEST_PUBLIC) {
    if(gethostbyname("www.midnightcode.org") != NULL) {
      ret_i |= MIDNIGHTCODE_RESOLVE_TEST_PUBLIC;
    } else {
      if(gethostbyname("www.kernel.org") != NULL) {
        ret_i |= MIDNIGHTCODE_RESOLVE_TEST_PUBLIC;
      } else {
        if(gethostbyname("www.linux.org") != NULL) {
          ret_i |= MIDNIGHTCODE_RESOLVE_TEST_PUBLIC;
        }
      }
    }
  }

  /* TODO: Consider "REVERSE" versus "SELF", implement winner */


  return ret_i;
}



int
midnightcode_resolve_writeconf(char * resolver_file,
                     struct midnightcode_resolve_domain_head_t * domains,
                     struct midnightcode_resolve_server_head_t * servers) {
  struct midnightcode_resolve_domain_element_t * domain;
  struct midnightcode_resolve_server_element_t * server;
  FILE *fp;
  char * buffer;
  int buffer_size;
  int entry_count;


  if(!resolver_file)
    return -1;
  if(!domains)
    return -1;
  if(!servers)
    return -1;

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

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

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

  fprintf(fp, "#\n");
  fprintf(fp, "# Resolver configuration file: %s\n", resolver_file);
  fprintf(fp, "#\n");

  midnightcode_list_for_each(domain, domains) {
    memset(buffer, 0, buffer_size);
    if(domain->type != MIDNIGHTCODE_RESOLVE_DOMAIN_WITHIN)
      continue;
    snprintf(buffer, buffer_size - 1, "domain     %s\n", domain->name);
    if(fputs(buffer, fp) < 0 ) {
      unlink(resolver_file);
      free(buffer);
      return -1;
    }
    break;
  }

  memset(buffer, 0, buffer_size);
  snprintf(buffer, buffer_size - 1, "search    ");
  midnightcode_list_for_each(domain, domains) {
    if(domain->type != MIDNIGHTCODE_RESOLVE_DOMAIN_WITHIN)
      continue;
    strncat(buffer, " ", buffer_size - strlen(buffer) - 1);
    strncat(buffer, domain->name, buffer_size - strlen(buffer) - 1);
  }
  midnightcode_list_for_each(domain, domains) {
    if(domain->type != MIDNIGHTCODE_RESOLVE_DOMAIN_SEARCH)
      continue;
    strncat(buffer, " ", buffer_size - strlen(buffer) - 1);
    strncat(buffer, domain->name, buffer_size - strlen(buffer) - 1);
  }
  strncat(buffer, "\n", buffer_size - strlen(buffer) - 1);
  if(fputs(buffer, fp) < 0 ) {
    unlink(resolver_file);
    free(buffer);
    return -1;
  }

  entry_count = 0;
  midnightcode_list_for_each(server, servers) {
    memset(buffer, 0, buffer_size);
    snprintf(buffer, buffer_size - 1, "nameserver %s\n",
      server->ipv4_address_s);
    if(fputs(buffer, fp) < 0 ) {
      unlink(resolver_file);
      free(buffer);
      return -1;
    }
    if(entry_count == MAXNS)
      break;
    entry_count++;
  }

  memset(buffer, 0, buffer_size);
  snprintf(buffer, buffer_size - 1,
    "options    timeout:%d attempts:%d rotate\n",
    MIDNIGHTCODE_RESOLVE_CONF_TIMEOUT, MIDNIGHTCODE_RESOLVE_CONF_ATTEMPTS);
  if(fputs(buffer, fp) < 0 ) {
    unlink(resolver_file);
    free(buffer);
    return -1;
  }

  free(buffer);
  fclose(fp);


  return 0;
}



int
midnightcode_resolve_to_config(
                     struct midnightcode_resolve_domain_head_t * domains,
                     struct midnightcode_resolve_server_head_t * servers,
                     struct midnightcode_config_head_t * config_elements) {
  struct midnightcode_resolve_domain_element_t * domain;
  struct midnightcode_resolve_server_element_t * server;
  char section_name[64];
  int entry_count;


  if(!domains || !servers || !config_elements)
    return -1;

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

  midnightcode_list_for_each(domain, domains) {
    if(domain->type != MIDNIGHTCODE_RESOLVE_DOMAIN_WITHIN)
      continue;

    if(midnightcode_config_add_pair(config_elements,
                                    MIDNIGHTCODE_RESOLVE_STRUCTURE,
                                    "domain",
                                    "name",
                                    domain->name,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      return -1;

    break;
  }

  entry_count = 0;
  midnightcode_list_for_each(domain, domains) {
    if(domain->type != MIDNIGHTCODE_RESOLVE_DOMAIN_SEARCH)
      continue;

    memset(&section_name, 0, sizeof(section_name));
    snprintf(section_name, sizeof(section_name) - 1, "search%d", entry_count);

    if(midnightcode_config_add_pair(config_elements,
                                    MIDNIGHTCODE_RESOLVE_STRUCTURE,
                                    section_name,
                                    "name",
                                    domain->name,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      return -1;

    entry_count++;
  }

  entry_count = 0;
  midnightcode_list_for_each(server, servers) {

    memset(&section_name, 0, sizeof(section_name));
    snprintf(section_name, sizeof(section_name) - 1,
      "nameserver%d", entry_count);

    if(midnightcode_config_add_pair(config_elements,
                                    MIDNIGHTCODE_RESOLVE_STRUCTURE,
                                    section_name,
                                    "ipv4_address",
                                    server->ipv4_address_s,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      return -1;

    if(entry_count == MAXNS)
      break;
    entry_count++;
  }


  return 0;
}



int
midnightcode_resolve_from_config(
                     struct midnightcode_resolve_domain_head_t * domains,
                     struct midnightcode_resolve_server_head_t * servers,
                     struct midnightcode_config_head_t * config_elements) {
  struct midnightcode_resolve_domain_element_t * domain;
  struct midnightcode_resolve_server_element_t * server;
  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;
  int entry_count;
  int ret_i;
  midnightcode_list_init(config_section_struct_head,
                         midnightcode_config_section_element_t);


  if(!domains || !servers || !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_RESOLVE_STRUCTURE))
    return -1;

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

    if(strncmp(section_element->section, "domain",
      strlen(section_element->section)) != 0)
      continue;

    domain = malloc(sizeof(struct midnightcode_resolve_domain_element_t));
    if(!domain)
      break;
    memset(domain, 0, sizeof(struct midnightcode_resolve_domain_element_t));

    domain->type = MIDNIGHTCODE_RESOLVE_DOMAIN_WITHIN;

    if(midnightcode_config_get_pair(config_elements,
                                    MIDNIGHTCODE_RESOLVE_STRUCTURE,
                                    "domain",
                                    "name",
                                    &domain->name, 256,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    ret_i = 0;
    midnightcode_list_append(domains, domain);

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


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

    if(strncmp(section_element->section, "search",
      strlen("search")) != 0)
      continue;

    domain = malloc(sizeof(struct midnightcode_resolve_domain_element_t));
    if(!domain)
      break;
    memset(domain, 0, sizeof(struct midnightcode_resolve_domain_element_t));

    domain->type = MIDNIGHTCODE_RESOLVE_DOMAIN_SEARCH;

    if(midnightcode_config_get_pair(config_elements,
                                    MIDNIGHTCODE_RESOLVE_STRUCTURE,
                                    section_element->section,
                                    "name",
                                    &domain->name, 256,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    ret_i = 0;
    midnightcode_list_append(domains, domain);

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


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

    if(strncmp(section_element->section, "nameserver",
      strlen("nameserver")) != 0)
      continue;

    server = malloc(sizeof(struct midnightcode_resolve_server_element_t));
    if(!server)
      break;
    memset(server, 0, sizeof(struct midnightcode_resolve_server_element_t));

    if(midnightcode_config_get_pair(config_elements,
                                    MIDNIGHTCODE_RESOLVE_STRUCTURE,
                                    section_element->section,
                                    "ipv4_address",
                                    &server->ipv4_address, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_IP))
      break;

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

    ret_i = 0;
    midnightcode_list_append(servers, server);

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

    if(entry_count == MAXNS)
      break;
    entry_count++;
  }
  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;
}


