/*
 * The olsr.org Optimized Link-State Routing daemon(olsrd)
 * Copyright (c) 2004, Andreas Tønnesen(andreto@olsr.org)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met:
 *
 * * Redistributions of source code must retain the above copyright 
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright 
 *   notice, this list of conditions and the following disclaimer in 
 *   the documentation and/or other materials provided with the 
 *   distribution.
 * * Neither the name of olsr.org, olsrd nor the names of its 
 *   contributors may be used to endorse or promote products derived 
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Visit http://www.olsr.org for more information.
 *
 * If you find this software useful feel free to make a donation
 * to the project. For more information see the website or contact
 * the copyright holders.
 *
 * $Id: olsrd_conf.c,v 1.55 2007/09/13 16:08:13 bernd67 Exp $
 */


#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "olsrd_conf.h"


extern FILE *yyin;
extern int yyparse(void);

static char copyright_string[] = "The olsr.org Optimized Link-State Routing daemon(olsrd) Copyright (c) 2004, Andreas Tønnesen(andreto@olsr.org) All rights reserved.";

#ifdef MAKEBIN

/* Build as standalone binary */
int 
main(int argc, char *argv[])
{
  struct olsrd_config *cnf;

  if(argc == 1)
    {
      fprintf(stderr, "Usage: olsrd_cfgparser [filename] -print\n\n");
      exit(EXIT_FAILURE);
    }

  if((cnf = olsrd_parse_cnf(argv[1])) != NULL)
    {
      if((argc > 2) && (!strcmp(argv[2], "-print")))
	{
	  olsrd_print_cnf(cnf);  
	  olsrd_write_cnf(cnf, "./out.conf");
	}
      else
        printf("Use -print to view parsed values\n");
      printf("Configfile parsed OK\n");
    }
  else
    {
      printf("Failed parsing \"%s\"\n", argv[1]);
    }

  return 0;
}

#else

/* Build as part of olsrd */


#endif

struct olsrd_config *
olsrd_parse_cnf(const char *filename)
{
  struct olsr_if *in, *new_ifqueue, *in_tmp;

  /* Stop the compiler from complaining */
  (void)strlen(copyright_string);

  cnf = malloc(sizeof(struct olsrd_config));
  if (cnf == NULL)
    {
      fprintf(stderr, "Out of memory %s\n", __func__);
      return NULL;
    }

  set_default_cnf(cnf);

  printf("Parsing file: \"%s\"\n", filename);

  yyin = fopen(filename, "r");
  
  if (yyin == NULL)
    {
      fprintf(stderr, "Cannot open configuration file '%s': %s.\n",
	      filename, strerror(errno));
      free(cnf);
      return NULL;
    }

  current_line = 1;

  if (yyparse() != 0)
    {
      fclose(yyin);
      olsrd_free_cnf(cnf);
      return NULL;
    }
  
  fclose(yyin);

  /* Reverse the queue (added by user request) */
  in = cnf->interfaces;
  new_ifqueue = NULL;

  while(in)
    {
      in_tmp = in; 
      in = in->next;

      in_tmp->next = new_ifqueue;
      new_ifqueue = in_tmp;
    }

  cnf->interfaces = new_ifqueue;

  in = cnf->interfaces;

  while(in)
    {
      /* set various stuff */
      in->configured = OLSR_FALSE;
      in->interf = NULL;
      in->host_emul = OLSR_FALSE;
      in = in->next;
    }


  return cnf;
}


int
olsrd_sanity_check_cnf(struct olsrd_config *cnf)
{
  struct olsr_if           *in = cnf->interfaces;
  struct if_config_options *io;

  /* Debug level */
  if(cnf->debug_level < MIN_DEBUGLVL ||
     cnf->debug_level > MAX_DEBUGLVL)
    {
      fprintf(stderr, "Debuglevel %d is not allowed\n", cnf->debug_level);
      return -1;
    }

  /* IP version */
  if(cnf->ip_version != AF_INET &&
     cnf->ip_version != AF_INET6)
    {
      fprintf(stderr, "Ipversion %d not allowed!\n", cnf->ip_version);
      return -1;
    }

  /* TOS */
  if(//cnf->tos < MIN_TOS ||
     cnf->tos > MAX_TOS)
    {
      fprintf(stderr, "TOS %d is not allowed\n", cnf->tos);
      return -1;
    }

  if(cnf->willingness_auto == OLSR_FALSE &&
     (cnf->willingness > MAX_WILLINGNESS))
    {
      fprintf(stderr, "Willingness %d is not allowed\n", cnf->willingness);
      return -1;
    }

  /* Hysteresis */
  if(cnf->use_hysteresis == OLSR_TRUE)
    {
      if(cnf->hysteresis_param.scaling < MIN_HYST_PARAM ||
	 cnf->hysteresis_param.scaling > MAX_HYST_PARAM)
	{
	  fprintf(stderr, "Hyst scaling %0.2f is not allowed\n", cnf->hysteresis_param.scaling);
	  return -1;
	}

      if(cnf->hysteresis_param.thr_high <= cnf->hysteresis_param.thr_low)
	{
	  fprintf(stderr, "Hyst upper(%0.2f) thr must be bigger than lower(%0.2f) threshold!\n", cnf->hysteresis_param.thr_high, cnf->hysteresis_param.thr_low);
	  return -1;
	}

      if(cnf->hysteresis_param.thr_high < MIN_HYST_PARAM ||
	 cnf->hysteresis_param.thr_high > MAX_HYST_PARAM)
	{
	  fprintf(stderr, "Hyst upper thr %0.2f is not allowed\n", cnf->hysteresis_param.thr_high);
	  return -1;
	}

      if(cnf->hysteresis_param.thr_low < MIN_HYST_PARAM ||
	 cnf->hysteresis_param.thr_low > MAX_HYST_PARAM)
	{
	  fprintf(stderr, "Hyst lower thr %0.2f is not allowed\n", cnf->hysteresis_param.thr_low);
	  return -1;
	}
    }

  /* Pollrate */

  if(cnf->pollrate < MIN_POLLRATE ||
     cnf->pollrate > MAX_POLLRATE)
    {
      fprintf(stderr, "Pollrate %0.2f is not allowed\n", cnf->pollrate);
      return -1;
    }

  /* NIC Changes Pollrate */

  if(cnf->nic_chgs_pollrate < MIN_NICCHGPOLLRT ||
     cnf->nic_chgs_pollrate > MAX_NICCHGPOLLRT)
    {
      fprintf(stderr, "NIC Changes Pollrate %0.2f is not allowed\n", cnf->nic_chgs_pollrate);
      return -1;
    }

  /* TC redundancy */

  if(//cnf->tc_redundancy < MIN_TC_REDUNDANCY ||
     cnf->tc_redundancy > MAX_TC_REDUNDANCY)
    {
      fprintf(stderr, "TC redundancy %d is not allowed\n", cnf->tc_redundancy);
      return -1;
    }

  /* MPR coverage */
  if(cnf->mpr_coverage < MIN_MPR_COVERAGE ||
     cnf->mpr_coverage > MAX_MPR_COVERAGE)
    {
      fprintf(stderr, "MPR coverage %d is not allowed\n", cnf->mpr_coverage);
      return -1;
    }

  /* Link Q and hysteresis cannot be activated at the same time */
  if(cnf->use_hysteresis == OLSR_TRUE && cnf->lq_level)
    {
      fprintf(stderr, "Hysteresis and LinkQuality cannot both be active! Deactivate one of them.\n");
      return -1;
    }

  /* Link quality level */

  if(cnf->lq_level > MAX_LQ_LEVEL)
    {
      fprintf(stderr, "LQ level %d is not allowed\n", cnf->lq_level);
      return -1;
    }

  /* Link quality window size */
  if(cnf->lq_level && (cnf->lq_wsize < MIN_LQ_WSIZE || cnf->lq_wsize > MAX_LQ_WSIZE))
    {
      fprintf(stderr, "LQ window size %d is not allowed\n", cnf->lq_wsize);
      return -1;
    }

  if(in == NULL)
    {
      fprintf(stderr, "No interfaces configured!\n");
      return -1;
    }

  /* Interfaces */
  while(in)
    {
      io = in->cnf;

      if(in->name == NULL || !strlen(in->name))
	{
	  fprintf(stderr, "Interface has no name!\n");
	  return -1;
	}

      if(io == NULL)
	{
	  fprintf(stderr, "Interface %s has no configuration!\n", in->name);
	  return -1;
	}
	
      /* HELLO interval */

      if (io->hello_params.validity_time < 0.0)
      {
        if (cnf->lq_level == 0)
          io->hello_params.validity_time = NEIGHB_HOLD_TIME;

        else
          io->hello_params.validity_time = cnf->lq_wsize * io->hello_params.emission_interval;
      }

      if(io->hello_params.emission_interval < cnf->pollrate ||
	 io->hello_params.emission_interval > io->hello_params.validity_time)
	{
	  fprintf(stderr, "Bad HELLO parameters! (em: %0.2f, vt: %0.2f)\n", io->hello_params.emission_interval, io->hello_params.validity_time);
	  return -1;
	}

      /* TC interval */
      if(io->tc_params.emission_interval < cnf->pollrate ||
	 io->tc_params.emission_interval > io->tc_params.validity_time)
	{
	  fprintf(stderr, "Bad TC parameters! (em: %0.2f, vt: %0.2f)\n", io->tc_params.emission_interval, io->tc_params.validity_time);
	  return -1;
	}

      /* MID interval */
      if(io->mid_params.emission_interval < cnf->pollrate ||
	 io->mid_params.emission_interval > io->mid_params.validity_time)
	{
	  fprintf(stderr, "Bad MID parameters! (em: %0.2f, vt: %0.2f)\n", io->mid_params.emission_interval, io->mid_params.validity_time);
	  return -1;
	}

      /* HNA interval */
      if(io->hna_params.emission_interval < cnf->pollrate ||
	 io->hna_params.emission_interval > io->hna_params.validity_time)
	{
	  fprintf(stderr, "Bad HNA parameters! (em: %0.2f, vt: %0.2f)\n", io->hna_params.emission_interval, io->hna_params.validity_time);
	  return -1;
	}

      in = in->next;
    }

  return 0;
}


void
olsrd_free_cnf(struct olsrd_config *cnf)
{
  struct hna4_entry        *h4d, *h4 = cnf->hna4_entries;
  struct hna6_entry        *h6d, *h6 = cnf->hna6_entries;
  struct olsr_if           *ind, *in = cnf->interfaces;
  struct plugin_entry      *ped, *pe = cnf->plugins;
  struct olsr_lq_mult      *mult, *next_mult;
  
  while(h4)
    {
      h4d = h4;
      h4 = h4->next;
      free(h4d);
    }

  while(h6)
    {
      h6d = h6;
      h6 = h6->next;
      free(h6d);
    }

  while(in)
    {
      for (mult = in->cnf->lq_mult; mult != NULL; mult = next_mult)
      {
        next_mult = mult->next;
        free(mult);
      }

      free(in->cnf);
      ind = in;
      in = in->next;
      free(ind->name);
      free(ind->config);
      free(ind);
    }

  while(pe)
    {
      ped = pe;
      pe = pe->next;
      free(ped->name);
      free(ped);
    }

  return;
}



struct olsrd_config *
olsrd_get_default_cnf(void)
{
  cnf = malloc(sizeof(struct olsrd_config));
  if (cnf == NULL)
    {
      fprintf(stderr, "Out of memory %s\n", __func__);
      return NULL;
    }

  set_default_cnf(cnf);

  return cnf;
}




void
set_default_cnf(struct olsrd_config *cnf)
{
    memset(cnf, 0, sizeof(struct olsrd_config));
    
    cnf->debug_level = DEF_DEBUGLVL;
    cnf->no_fork = OLSR_FALSE;
    cnf->host_emul = OLSR_FALSE;
    cnf->ip_version  = AF_INET;
    cnf->allow_no_interfaces = DEF_ALLOW_NO_INTS;
    cnf->tos = DEF_TOS;
    cnf->rttable = 254;
    cnf->willingness_auto = DEF_WILL_AUTO;
    cnf->ipc_connections = DEF_IPC_CONNECTIONS;
    cnf->open_ipc = cnf->ipc_connections ? OLSR_TRUE : OLSR_FALSE;

    cnf->use_hysteresis = DEF_USE_HYST;
    cnf->hysteresis_param.scaling = HYST_SCALING;
    cnf->hysteresis_param.thr_high = HYST_THRESHOLD_HIGH;
    cnf->hysteresis_param.thr_low = HYST_THRESHOLD_LOW;

    cnf->pollrate = DEF_POLLRATE;
    cnf->nic_chgs_pollrate = DEF_NICCHGPOLLRT;

    cnf->tc_redundancy = TC_REDUNDANCY;
    cnf->mpr_coverage = MPR_COVERAGE;
    cnf->lq_level = DEF_LQ_LEVEL;
    cnf->lq_fish = DEF_LQ_FISH;
    cnf->lq_dlimit = DEF_LQ_DIJK_LIMIT;
    cnf->lq_dinter = DEF_LQ_DIJK_INTER;
    cnf->lq_wsize = DEF_LQ_WSIZE;
    cnf->clear_screen = DEF_CLEAR_SCREEN;

    cnf->del_gws = OLSR_FALSE;
    cnf->will_int = 10 * HELLO_INTERVAL;
    cnf->max_jitter = 0.0;
    cnf->exit_value = EXIT_SUCCESS;
    cnf->max_tc_vtime = 0.0;
    cnf->ioctl_s = 0;
#if LINUX_POLICY_ROUTING
    cnf->rtnl_s = 0;
#else
    cnf->rts = 0;
#endif
}




struct if_config_options *
get_default_if_config(void)
{
  struct if_config_options *io = malloc(sizeof(struct if_config_options));
  struct in6_addr in6;

  if(io == NULL)
    {
      fprintf(stderr, "Out of memory %s\n", __func__);
      return NULL;
    }

  memset(io, 0, sizeof(struct if_config_options));

  io->ipv6_addrtype = 1; /* XXX - FixMe */

  inet_pton(AF_INET6, OLSR_IPV6_MCAST_SITE_LOCAL, &in6);
  memcpy(&io->ipv6_multi_site.v6, &in6, sizeof(struct in6_addr));

  inet_pton(AF_INET6, OLSR_IPV6_MCAST_GLOBAL, &in6);
  memcpy(&io->ipv6_multi_glbl.v6, &in6, sizeof(struct in6_addr));

  io->lq_mult = NULL;

  io->weight.fixed = OLSR_FALSE;
  io->weight.value = 0;

  io->ipv6_addrtype = 0; /* global */

  io->hello_params.emission_interval = HELLO_INTERVAL;
  io->hello_params.validity_time = -1.0;
  io->tc_params.emission_interval = TC_INTERVAL;
  io->tc_params.validity_time = TOP_HOLD_TIME;
  io->mid_params.emission_interval = MID_INTERVAL;
  io->mid_params.validity_time = MID_HOLD_TIME;
  io->hna_params.emission_interval = HNA_INTERVAL;
  io->hna_params.validity_time = HNA_HOLD_TIME;
  io->autodetect_chg = OLSR_TRUE;

  return io;

}



void
olsrd_print_cnf(struct olsrd_config *cnf)
{
  struct hna4_entry        *h4 = cnf->hna4_entries;
  struct hna6_entry        *h6 = cnf->hna6_entries;
  struct olsr_if           *in = cnf->interfaces;
  struct plugin_entry      *pe = cnf->plugins;
  struct ipc_host          *ih = cnf->ipc_hosts;
  struct ipc_net           *ie = cnf->ipc_nets;
  struct olsr_lq_mult      *mult;
  char ipv6_buf[100];             /* buffer for IPv6 inet_htop */
  struct in_addr in4;

  printf(" *** olsrd configuration ***\n");

  printf("Debug Level      : %d\n", cnf->debug_level);
  if(cnf->ip_version == AF_INET6)
    printf("IpVersion        : 6\n");
  else
    printf("IpVersion        : 4\n");
  if(cnf->allow_no_interfaces)
    printf("No interfaces    : ALLOWED\n");
  else
    printf("No interfaces    : NOT ALLOWED\n");
  printf("TOS              : 0x%02x\n", cnf->tos);
  printf("RtTable          : 0x%02x\n", cnf->rttable);
  if(cnf->willingness_auto)
    printf("Willingness      : AUTO\n");
  else
    printf("Willingness      : %d\n", cnf->willingness);

  printf("IPC connections  : %d\n", cnf->ipc_connections);

  while(ih)
    {
      in4.s_addr = ih->host.v4;
      printf("\tHost %s\n", inet_ntoa(in4));
      ih = ih->next;
    }
  
  while(ie)
    {
      in4.s_addr = ie->net.v4;
      printf("\tNet %s/", inet_ntoa(in4));
      in4.s_addr = ie->mask.v4;
      printf("%s\n", inet_ntoa(in4));
      ie = ie->next;
    }


  printf("Pollrate         : %0.2f\n", cnf->pollrate);

  printf("NIC ChangPollrate: %0.2f\n", cnf->nic_chgs_pollrate);

  printf("TC redundancy    : %d\n", cnf->tc_redundancy);

  printf("MPR coverage     : %d\n", cnf->mpr_coverage);
   
  printf("LQ level         : %d\n", cnf->lq_level);

  printf("LQ fish eye      : %d\n", cnf->lq_fish);

  printf("LQ Dijkstra limit: %d, %0.2f\n", cnf->lq_dlimit, cnf->lq_dinter);

  printf("LQ window size   : %d\n", cnf->lq_wsize);

  printf("Clear screen     : %s\n", cnf->clear_screen ? "yes" : "no");

  /* Interfaces */
  if(in)
    {
      printf("Interfaces:\n");
      while(in)
	{
	  printf(" dev: \"%s\"\n", in->name);
	  
	  if(in->cnf->ipv4_broadcast.v4)
	    {
	      in4.s_addr = in->cnf->ipv4_broadcast.v4;
	      printf("\tIPv4 broadcast           : %s\n", inet_ntoa(in4));
	    }
	  else
	    {
	      printf("\tIPv4 broadcast           : AUTO\n");
	    }
	  
	  printf("\tIPv6 addrtype            : %s\n", in->cnf->ipv6_addrtype ? "site-local" : "global");
	  
	  //union olsr_ip_addr       ipv6_multi_site;
	  //union olsr_ip_addr       ipv6_multi_glbl;
	  printf("\tIPv6 multicast site/glbl : %s", (char *)inet_ntop(AF_INET6, &in->cnf->ipv6_multi_site.v6, ipv6_buf, sizeof(ipv6_buf)));
	  printf("/%s\n", (char *)inet_ntop(AF_INET6, &in->cnf->ipv6_multi_glbl.v6, ipv6_buf, sizeof(ipv6_buf)));
	  
	  printf("\tHELLO emission/validity  : %0.2f/%0.2f\n", in->cnf->hello_params.emission_interval, in->cnf->hello_params.validity_time);
	  printf("\tTC emission/validity     : %0.2f/%0.2f\n", in->cnf->tc_params.emission_interval, in->cnf->tc_params.validity_time);
	  printf("\tMID emission/validity    : %0.2f/%0.2f\n", in->cnf->mid_params.emission_interval, in->cnf->mid_params.validity_time);
	  printf("\tHNA emission/validity    : %0.2f/%0.2f\n", in->cnf->hna_params.emission_interval, in->cnf->hna_params.validity_time);
	  
          for (mult = in->cnf->lq_mult; mult != NULL; mult = mult->next)
          {
            inet_ntop(cnf->ip_version, &mult->addr, ipv6_buf,
                      sizeof (ipv6_buf));

            printf("\tLinkQualityMult          : %s %0.2f\n",
                   ipv6_buf, mult->val);
          }

          printf("\tAutodetetc changes       : %s\n", in->cnf->autodetect_chg ? "yes" : "no");

	  in = in->next;
	}
    }




  /* Plugins */
  if(pe)
    {
      printf("Plugins:\n");

      while(pe)
	{
	  printf("\tName: \"%s\"\n", pe->name);
	  pe = pe->next;
	}
    }

  /* Hysteresis */
  if(cnf->use_hysteresis)
    {
      printf("Using hysteresis:\n");
      printf("\tScaling      : %0.2f\n", cnf->hysteresis_param.scaling);
      printf("\tThr high/low : %0.2f/%0.2f\n", cnf->hysteresis_param.thr_high, cnf->hysteresis_param.thr_low);
    }
  else
    printf("Not using hysteresis\n");

  /* HNA IPv4 */
  if(h4)
    {

      printf("HNA4 entries:\n");
      while(h4)
	{
	  in4.s_addr = h4->net.v4;
	  printf("\t%s/", inet_ntoa(in4));
	  in4.s_addr = h4->netmask.v4;
	  printf("%s\n", inet_ntoa(in4));

	  h4 = h4->next;
	}
    }

  /* HNA IPv6 */
  if(h6)
    {
      printf("HNA6 entries:\n");
      while(h6)
	{
	  printf("\t%s/%d\n", (char *)inet_ntop(AF_INET6, &h6->net.v6, ipv6_buf, sizeof(ipv6_buf)), h6->prefix_len);
	  h6 = h6->next;
	}
    }
}

void *olsrd_cnf_malloc(unsigned int len)
{
  return malloc(len);
}

void olsrd_cnf_free(void *addr)
{
  free(addr);
}

#if defined WIN32_STDIO_HACK
struct ioinfo
{
	unsigned int handle;
	unsigned char attr;
	char buff;
	int flag;
	CRITICAL_SECTION lock;
};

void win32_stdio_hack(unsigned int handle)
{
  HMODULE lib;
  struct ioinfo **info;

  lib = LoadLibrary("msvcrt.dll");

  info = (struct ioinfo **)GetProcAddress(lib, "__pioinfo");

  // (*info)[1].handle = handle;
  // (*info)[1].attr = 0x89; // FOPEN | FTEXT | FPIPE;

  (*info)[2].handle = handle;
  (*info)[2].attr = 0x89;

  // stdout->_file = 1;
  stderr->_file = 2;

  // setbuf(stdout, NULL);
  setbuf(stderr, NULL);
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1