/* Nessus
 * Copyright (C) 1998 Renaud Deraison
 *
 * 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.
 *
 * 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
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
#include <includes.h>
#ifndef NESSUSNT
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else  
extern int optind;
#endif /* not HAVE_GETOPT_H */   

#else/* not def NESSUSNT */
#include "wstuff.h"
#endif

#include "pluginload.h"
#include "preferences.h"
#include "auth.h"
#include "rules.h"
#include "comm.h"
#include "attack.h"
#include "sighand.h"
#include "log.h"
#include "threads.h"
#include "users.h"
#include "ntp.h"
#include "utils.h"

/*
 * Functions prototypes
 */
static void main_loop(int, struct arglist *, struct arglist *, struct nessus_rules*, 
	     struct users *);
static int init_nessusd(int, char *, int *, struct arglist **, struct arglist **,
		struct nessus_rules**, struct users **);
static int init_network(int, int *);
static void server_thread(struct arglist *);


static void 
server_thread(globals)
 struct arglist * globals;
{
 struct sockaddr_in * address = arg_get_value(globals, "client_address");
 struct arglist * plugins = arg_get_value(globals, "plugins");
 int ntp_version;
 int soc = (int)arg_get_value(globals, "global_socket");
 char * perms;
 struct nessus_rules * rules = arg_get_value(globals, "rules");
 

 ntp_version = comm_init(soc);
 arg_add_value(globals, "ntp_version", ARG_INT, -1, (void *)ntp_version);   
 plugins_set_ntp_version(plugins, ntp_version);     
 plugins_set_socket(plugins, soc);
 if(!(perms = auth_check_user(globals, (char *)inet_ntoa(address->sin_addr))))
 {
   auth_printf(globals, "Bad login attempt !\n"); 
   log_write("bad login attempt from %s\n", 
			(char *)inet_ntoa(address->sin_addr));
 }
  else {
   rules_add(rules, perms, 1);
   comm_send_pluginlist(globals);
   if(ntp_version > NTP_10){
       comm_send_preferences(globals);
       comm_send_rules(globals);
       }
   comm_wait_order(globals);
   attack_host(globals);
   comm_terminate(globals);
   }
 shutdown(soc, 2);
 close(soc);
#ifndef NESSUSNT
 arg_free_all(globals);
#endif
 EXIT(0);
}
     
static void 
main_loop(serv_socket, plugins, preferences, rules, users)
 int serv_socket;
 struct arglist * plugins;
 struct arglist * preferences;
 struct nessus_rules * rules;
 struct users * users;
{
  while(1)
    {
      int soc;
      unsigned int lg_address = sizeof(struct sockaddr_in);
      struct sockaddr_in address;
  
      struct arglist * globals;
      struct arglist * my_plugins, * my_preferences, * my_scanners;
      struct nessus_rules * my_rules;
      struct sockaddr_in * p_addr;
      struct arglist * plugs;
      struct arglist * scanners;
      
      soc = accept(serv_socket, (struct sockaddr *)(&address), &lg_address);
      if(soc == -1){
#ifndef NESSUSNT
    	if(errno == EINTR)continue;
  	else 
#endif
	{
	  perror("accept ");
	  DO_EXIT(2);
	}
      }
      log_write("connection from %s\n", (char *)inet_ntoa(address.sin_addr));
      
      /* 
       * duplicate everything so that the threads don't share the
       * same variables
       */
      globals = emalloc(sizeof(struct arglist));
      arg_add_value(globals, "global_socket", ARG_INT, -1, (void *)soc);
          

      my_plugins = emalloc(sizeof(struct arglist));
      plugs = arg_get_value(plugins, "plugins");
      arg_dup(my_plugins, plugs);
      arg_add_value(globals, "plugins", ARG_ARGLIST, -1, my_plugins);
      
      my_scanners = emalloc(sizeof(struct arglist));
      scanners = arg_get_value(plugins, "scanners");
      arg_dup(my_scanners, scanners);
      arg_add_value(globals, "scanners", ARG_ARGLIST, -1, my_scanners);
          
      my_preferences = emalloc(sizeof(struct arglist));
      arg_dup(my_preferences, preferences);
      arg_add_value(globals, "preferences", ARG_ARGLIST, -1, my_preferences);
          
      my_rules = emalloc(sizeof(struct nessus_rules));
      rules_dup(my_rules, rules);
      
      arg_add_value(globals, "users", ARG_PTR, -1, users);
          
      p_addr = emalloc(sizeof(struct sockaddr_in));
      *p_addr = address;
      arg_add_value(globals, "client_address", ARG_PTR, -1, p_addr);
      
      arg_add_value(globals, "rules", ARG_PTR, -1, my_rules);
      create_thread(server_thread, globals);
    }
}


/*
 * Initialization of the network : 
 * we setup the socket that will listen for
 * incoming connections on port <port>
 */ 
static int 
init_network(port, sock)
     int port;
     int * sock;
{

#ifndef NESSUSNT
  int option = 1;
#endif

#ifdef USE_AF_INET
  struct sockaddr_in address;
#else
   struct sockaddr_un address;
  char * name = AF_UNIX_PATH;
#endif


#ifdef NESSUSNT
    WORD wVersionRequested;
    WSADATA wsaData;
	int err;
    wVersionRequested = MAKEWORD( 2, 0 );
    err = WSAStartup( wVersionRequested, &wsaData );
    if(err){print_error("Could not initialize WinSock !");DO_EXIT(0);}
#endif

#ifdef USE_AF_INET
  if((*sock = socket(AF_INET, SOCK_STREAM, 0))==-1)
    {
      perror("socket ");
      DO_EXIT(1);
    }
  address.sin_family = AF_INET;
  address.sin_addr.s_addr = htonl(INADDR_ANY);
  address.sin_port = htons((unsigned short)port);
#else
  if((*sock = socket(AF_UNIX, SOCK_STREAM,0))==-1)
    {
     perror("socket ");
     DO_EXIT(1);
   }
  bzero(&address, sizeof(struct sockaddr_un));
  address.sun_family = AF_UNIX;
  bcopy(name, address.sun_path, strlen(name));
#endif

#ifndef NESSUSNT
  setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(int));
#endif
  if(bind(*sock, (struct sockaddr *)(&address), sizeof(address))==-1)
    {
      perror("bind ");
      DO_EXIT(1);
    }
  if(listen(*sock, 10)==-1)
    {
      perror("listen ");
      shutdown(*sock, 2);
      close(*sock);
      DO_EXIT(1);
    }
  return(0);
}

/*
 * Initialize everything
 */
static int 
init_nessusd(serv_port, config_file, sock, plugins, preferences,rules, users)
     int serv_port;
     char * config_file;
     int * sock;
     struct arglist ** plugins;
     struct arglist ** preferences;
     struct nessus_rules ** rules;
     struct users ** users;
{
  preferences_init(config_file, preferences);
  arg_add_value(*preferences, "config_file", ARG_STRING, strlen(config_file), config_file);
  rules_init(rules, *preferences);
  users_init(users, *preferences);
  log_init(arg_get_value(*preferences, "logfile"));
  *plugins = plugins_init(*preferences);
  init_network(serv_port, sock);
  signal(SIGCHLD, sighand_chld);
  signal(SIGTERM, sighand_term);
  signal(SIGINT, sighand_int);
  signal(SIGKILL, sighand_kill);
#ifndef DEBUG
  signal(SIGSEGV, sighand_segv);
#endif
  return(0);
}


static void 
display_help()
{
  printf("nessusd, version %s\n", NESSUS_VERSION);
#ifdef USE_AF_INET
  printf("\nusage : nessusd [-vcphd]\n\n");
#else
  printf("\nusage : nessusd [-vchd]\n\n");
#endif /*def USE_AF_INET */
  printf("\tv : shows version number\n\th : shows this help\n");
#ifdef USE_AF_INET
  printf("\tp < number > : use port number <number>\n");
#endif /* USE_AF_INET */
  printf("\tc <filename> : alternate configuration file to use\n\t\t\t(default : %s)\n",
	 NESSUSD_CONF);
  printf("\td : dumps the nessusd compilation options\n");
}

#ifdef NESSUSNT
int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst,
                                 LPSTR lpszArgs, int nWinMode)
#else
int 
main(int argc, char * argv[])
#endif
{
  int serv_port = -1;
  int sock;
  char * config_file = NULL;
  struct users * users;
  struct nessus_rules * rules;
   
  struct arglist * plugins;
  struct arglist * preferences;
#ifndef NESSUSNT
  int i;
  while((i=getopt(argc, argv, "phvcd"))!=EOF)
    {
      switch(i)
	{
	case 'p' :
	  if(!argv[optind]){display_help();DO_EXIT(0);}
	  serv_port = atoi(argv[optind]);
	  if((serv_port<=0)||(serv_port>=65536))
	    {
	      printf("Invalid port specification\n");
	      display_help();
	      DO_EXIT(1);
	    }
	  break;
	  
	case 'v' :
	  print_error("nessusd (%s) %s for %s -- (c) 1998 Renaud Deraison <deraison@worldnet.fr>\n", 
		 PROGNAME,NESSUS_VERSION, NESS_OS_NAME);
	  DO_EXIT(0);
	  break;
	case 'h' :
	  display_help();
	  DO_EXIT(0);
	  break;
	case 'c' : 
	  if(!argv[optind]){display_help();DO_EXIT(1);}
	  config_file = emalloc(strlen(argv[optind])+1);
	  strncpy(config_file, argv[optind], strlen(argv[optind]));
	  break;
       case 'd' :
           printf("This is Nessus %s for %s %s\n", NESSUS_VERSION, NESS_OS_NAME, NESS_OS_VERSION);
           printf("compiled with %s\n", NESS_COMPILER);
           printf("Current setup :\n");
           printf("\tThread manager : ");
#ifdef USE_PTHREADS
	   printf("pthreads\n");
#else
	   printf("fork\n");
#endif
	   printf("\tRunning as euid : %d\n", geteuid());
           printf("\n\nInclude these infos in your bug reports\n");
	   exit(0);
	   break;
	}
    } 
#endif
  if(serv_port == -1)serv_port = DEFAULT_PORT;
  if(!config_file)
    {
      config_file = emalloc(strlen(NESSUSD_CONF)+1);
      strncpy(config_file, NESSUSD_CONF, strlen(NESSUSD_CONF));
    }

  init_nessusd(serv_port, config_file, &sock, &plugins, &preferences,&rules, &users);
  
#ifdef NESSUSNT
  /*
   * Just tell to the user that nessusd is running in background
   */
  create_thread(print_error, "nessusd is now running in background");
#endif /* defined(NESSUSNT) */
  main_loop(sock, plugins, preferences, rules, users);
  DO_EXIT(0);
  return(0);
}
 
 
