/*
---------------------------------------------------------------------------
 $Id: tsp_local.c,v 1.13 2007/04/25 19:31:27 cnepveu Exp $
---------------------------------------------------------------------------
Copyright (c) 2001-2007 Hexago Inc. All rights reserved.

  LICENSE NOTICE: You may use and modify this source code only if you
  have executed a valid license agreement with Hexago Inc. granting
  you the right to do so, the said license agreement governing such
  use and modifications.   Copyright or other intellectual property
  notices are not to be removed from the source code.
---------------------------------------------------------------------------
*/


#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>

#include <sys/types.h>
#include <sys/wait.h>

#define _USES_SYS_SOCKET_H_
#define _USES_SYS_TIME_H_
#define _USES_NETINET_IN_H_
#define _USES_ARPA_INET_H_

#include "platform.h"

/* get data types needed here */

#include "config.h"	/* tConf */
#include "xml_tun.h" /* tTunnel */
#include "net.h"	/* net_tools_t */
#include "tsp_net.h"/* tspClose */
#include "tsp_client.h"/*tspmain*/
#include "net_ka.h"

/* some globals and the logging */

#include "lib.h"
#include "log.h"
#include "hex_strings.h"
#include "errors.h"

//#include "tsp_tun.h"   /* linux's tun */
#include "tsp_setup.h"

/* these globals are defined by US used by alot of things in  */

char *FileName  = "gw6c.conf";
char *ScriptInterpretor = "/bin/sh";
char *ScriptExtension = "sh";
char *ScriptDir = NULL;
char *TspHomeDir = "/usr/local/etc/gw6";
char DirSeparator = '/';

int RootUid = 0;


#include <gw6cmessaging/gw6cuistrings.h>
// Dummy implementation for non-win32 targets
// (Library gw6cmessaging is not linked in non-win32 targets).
error_t send_status_info( void ) { return GW6CM_UIS__NOERROR; }
error_t send_tunnel_info( void ) { return GW6CM_UIS__NOERROR; }
error_t send_broker_list( void ) { return GW6CM_UIS__NOERROR; }
error_t send_hap6_status_info( void ) { return GW6CM_UIS__NOERROR; }


/* linux specific to setup an env variable */

void
tspSetEnv(char *Variable, char *Value, int Flag)
{
	Display(LOG_LEVEL_3, ELInfo, "tspSetEnv", HEX_STR_ENV_PRINT_VALUE, Variable, Value);
	setenv(Variable, Value, Flag);
}

// --------------------------------------------------------------------------
// Called from tsp_setup.c -> tspSetupInterface
//   Do extra platform-specific stuff before tunnel script is launched.
//
int tspSetupInterfaceLocal( tConf* pConf, tTunnel* pTun )
{
  return 0;
}

/* tspSetupTunnel() will callback here */

char *
tspGetLocalAddress(int socket, char *buffer, int size)
{
	struct sockaddr_in6 addr; /* enough place for v4 and v6 */
	struct sockaddr_in  *addr_v4 = (struct sockaddr_in *)&addr;
	struct sockaddr_in6 *addr_v6 = (struct sockaddr_in6 *)&addr;
	int len;

	len = sizeof addr;
	if (getsockname(socket, (struct sockaddr *)&addr, &len) < 0) {
		Display(LOG_LEVEL_3, ELError, "TryServer", HEX_STR_ERR_FIND_SRC_IP);
		return NULL;
	}

	if (addr.sin6_family == AF_INET6)
		return (char *)inet_ntop(AF_INET6, (const void*) &addr_v6->sin6_addr, buffer, size);
	else
		return (char *)inet_ntop(AF_INET, (const void*) &addr_v4->sin_addr, buffer, size);
}

/* tspSetupTunnel() will callback here */

/* start locally, ie, setup interface and any daemons or anything needed */

int tspStartLocal(int socket, tConf *c, tTunnel *t, net_tools_t *nt)
{
	  int status;
	  int keepalive_interval = 0;

	  /* Test for root privileges */
	  if(geteuid() != 0) {
		  Display(LOG_LEVEL_1, ELError, "tspStartLocal", HEX_STR_FATAL_NOT_ROOT_FOR_TUN);
		  return INTERFACE_SETUP_FAILED;
	  }
	  
	/* start the tunneler service */

	 if (t->keepalive_interval != NULL) {
		keepalive_interval = atoi(t->keepalive_interval);
		Display(LOG_LEVEL_3, ELInfo, "tspStartLocal", HEX_STR_KEEPALIVE_INTERVAL, t->keepalive_interval);
	 }
	 
	  {
		int tunfd;

		Display(LOG_LEVEL_3, ELInfo, "tspStartLocal", HEX_STR_GOING_DAEMON);

		if (daemon(1, 0) == -1) {
			Display(LOG_LEVEL_3, ELError, "tspStartLocal", HEX_STR_CANT_FORK);
			return INTERFACE_SETUP_FAILED;
		}

		if (strcasecmp(t->type, STR_CONFIG_TUNNELMODE_V6UDPV4) == 0 ) {
			if ((tunfd = TunInit(c->if_tunnel_v6udpv4)) == -1) {
				Display(LOG_LEVEL_1, ELError, "tspStartLocal", HEX_STR_UNABLE_INIT_TUN_DEV);
				return(INTERFACE_SETUP_FAILED);
			}
		}
		
		if (strcasecmp(t->type, STR_CONFIG_TUNNELMODE_V4V6) == 0 ) {
			Display(LOG_LEVEL_1, ELError, "tspStartLocal", HEX_STR_NO_V4V6_ON_PLATFORM);
				return(INTERFACE_SETUP_FAILED);
		}
		
		/* now, run the config script without 
		   giving it our tunnel file descriptor.

		   This is important because otherwise
		   the tunnnel will stay open even if we get killed
		   */

		{
			int pid = fork();
			if (pid < 0)
				// fork() error
				return INTERFACE_SETUP_FAILED;

			else if (pid == 0) {	// child
				close(tunfd);
				if (tspSetupInterface(c, t) != 0)
					exit(INTERFACE_SETUP_FAILED);
				exit(0);
			}

			else {	//parent
				int s = 0;
				Display(LOG_LEVEL_3, ELInfo, "tspStartLocal", HEX_STR_WAITING_FOR_SETUP_SCRIPT);
				if (wait(&s) == pid) { // ok our child returned 
					if ( !WIFEXITED(s) ) {
						Display(LOG_LEVEL_3, ELError, "tspStartLocal", HEX_STR_SCRIPT_FAILED);
						return INTERFACE_SETUP_FAILED;
					}
					if ( WEXITSTATUS(s) != 0 ) {
						Display(LOG_LEVEL_3, ELError, "tspStartLocal", HEX_STR_SCRIPT_FAILED);
						return INTERFACE_SETUP_FAILED;
					}
					// else everything is fine
				}
				else { // error occured we have no other child
					Display(LOG_LEVEL_1, ELError, "tspStartLocal", HEX_STR_ERR_WAITING_SCRIPT);
				return INTERFACE_SETUP_FAILED;
				}
			}
		}

		if (strcasecmp(t->type, STR_CONFIG_TUNNELMODE_V6UDPV4) == 0 ) {
			status = TunMainLoop(tunfd, socket, c->keepalive,
			keepalive_interval, t->client_address_ipv6,
			    t->keepalive_address);
			/* We got out of main loop = keepalive timeout || tunnel error */
			close(tunfd);
			tspClose(socket, nt);
			return status;
		} else if (strcasecmp(t->type, STR_CONFIG_TUNNELMODE_V6V4) == 0 ) {
			if (keepalive_interval == 0)
				return NO_ERROR; /* if there is no keepalive, we can exit safe at this point */

			status = NetKeepaliveV6V4Tunnel(t->client_address_ipv6,
				t->keepalive_address,
				keepalive_interval);
			return status;
		}
	}	
	return INTERFACE_SETUP_FAILED; /* should never reach here */
}


syntax highlighted by Code2HTML, v. 0.9.1