/*
 * Tentun - covert tunneling project
 *
 * Copyright (C) 2003 David Gordon
 *
 *      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; either version 2 of the License, or
 *      (at your option) any later version.
 */


#include "config.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/socket.h>

#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif

#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif

#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif

#include "tentun.h"
#include "lib.h"
#include "server.h"


void connection(int sock, struct tentun_host *host)
{
     struct sockaddr_in my_addr, cl_addr;
     struct sigaction sa;
     char *ip;
     int opt;

     opt = sizeof(struct sockaddr_in);
     if( getpeername(sock, (struct sockaddr *) &cl_addr, &opt) ){
        TENTUN_PRINT("Can't get peer namei\n");
        exit(1);
     }
     opt = sizeof(struct sockaddr_in);
     if( getsockname(sock, (struct sockaddr *) &my_addr, &opt) < 0 ){
        TENTUN_PRINT("Can't get local socket address\n");
        exit(1); 
     }

     ip = strdup(inet_ntoa(cl_addr.sin_addr));

     io_init();

     sa.sa_handler=SIG_IGN;
     sa.sa_flags=SA_NOCLDWAIT;;
     sigaction(SIGHUP,&sa,NULL);

     host->host = "default name";     
     TENTUN_PRINT("Session %s[%s:%d] opened\n", host->host, ip, 
					ntohs(cl_addr.sin_port) );
     host->rmt_fd = sock; 
     
     host->sopt.laddr = strdup(inet_ntoa(my_addr.sin_addr));
     host->sopt.lport = tentun.svr_port;
     host->sopt.raddr = ip;
     host->sopt.rport = ntohs(cl_addr.sin_port);
     
     /* Start tunnel */
     tunnel_exec(host);

     TENTUN_PRINT("Session %s closed\n", host->host);
     
     close(sock);

     free(host);
     exit(0);
}

void listener(struct tentun_host *host)
{
     struct sockaddr_in my_addr, cl_addr;
     int s, s1, opt;

     memset(&my_addr, 0, sizeof(my_addr));
     my_addr.sin_family = AF_INET;
     my_addr.sin_addr.s_addr = INADDR_ANY;
     my_addr.sin_port = htons(tentun.svr_port);	

     if( (s=socket(AF_INET,SOCK_STREAM,0))== -1 ){
	TENTUN_PRINT("Can't create socket\n");
	exit(1);
     }

     opt=1;
     setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 

     if( bind(s,(struct sockaddr *)&my_addr,sizeof(my_addr)) ){
	TENTUN_PRINT("Can't bind to the socket\n");
	exit(1);
     }

     if( listen(s, 10) ){
	TENTUN_PRINT("Can't listen on the socket\n");
	exit(1);
     }



     opt=sizeof(cl_addr);
     if( (s1=accept(s,(struct sockaddr *)&cl_addr,&opt)) == -1 ) {
       TENTUN_PRINT("Couldn't accept connection\n");
       exit(2); 
     }

     close(s);
     connection(s1, host);

     /*
     while(1){
        opt=sizeof(cl_addr);
	if( (s1=accept(s,(struct sockaddr *)&cl_addr,&opt)) == -1 )
	   continue; 

	switch( fork() ){
	   case 0:
	      close(s);
	      connection(s1);
	      break;
	   case -1:
	      TENTUN_PRINT("Couldn't fork()\n");
	   default:
	      close(s1);
	      break;
	}
     } 
     */ 
}	

void server(int sock, struct tentun_host *host)
{
     struct sigaction sa;

     sa.sa_handler=SIG_IGN;
     sa.sa_flags=SA_NOCLDWAIT;;
     sigaction(SIGINT,&sa,NULL);
     sigaction(SIGQUIT,&sa,NULL);
     sigaction(SIGCHLD,&sa,NULL);
     sigaction(SIGPIPE,&sa,NULL);
     sigaction(SIGUSR1,&sa,NULL);

     TENTUN_PRINT("TENTUN server ver %s (%s)\n", TENTUN_VER,
		 tentun.svr_type == TENTUN_INETD ? "inetd" : "stand" );

     switch( tentun.svr_type ){
	case TENTUN_STAND_ALONE:
	   listener(host);
	   break;
        case TENTUN_INETD:
	   connection(sock, host);
	   break;
     }    
}
