/* Nessus Attack Scripting Language 
 *
 * Copyright (C) 2002 - 2003 Michel Arboi and Renaud Deraison
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation
 *
 * 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.
 *
 * In addition, as a special exception, Renaud Deraison and Michel Arboi
 * give permission to link the code of this program with any
 * version of the OpenSSL library which is distributed under a
 * license identical to that listed in the included COPYING.OpenSSL
 * file, and distribute linked combinations including the two.
 * You must obey the GNU General Public License in all respects
 * for all of the code used other than OpenSSL.  If you modify
 * this file, you may extend this exception to your version of the
 * file, but you are not obligated to do so.  If you do not wish to
 * do so, delete this exception statement from your version.
 *
 */
 /*
  * This file contains all the function that make the "glue" between
  * as NASL script and nessusd.
  * (script_*(), *kb*(), scanner_*())
  */
  
#include <includes.h>
#include "strutils.h"

#include "nasl_tree.h"
#include "nasl_global_ctxt.h"
#include "nasl_func.h"
#include "nasl_var.h"
#include "nasl_lex_ctxt.h"
#include "exec.h"  

#include "nasl_debug.h"
#include "nasl_nessusd_glue.h"


/*------------------- Private utilities ---------------------------------*/

static int isalldigit(char * str, int len)
{
 int i ;
 char buf[1024];
 for (i = 0; i < len; i ++)
 {
  if(!isdigit(str[i]))return 0;
 }

 snprintf(buf, sizeof(buf), "%d", atoi(str));
 if ( strcmp(buf, str) != 0 ) return 0;
 else return 1;
}



/*-------------------[ script_*() functions ]----------------------------*/
 
 /*
  * These functions are used when the script registers itself to nessusd
  */

tree_cell *script_timeout(lex_ctxt * lexic)
{
 struct arglist * script_infos = lexic->script_infos;
 int to = get_int_var_by_num(lexic, 0, -65535);

 if(to == -65535)
	 return FAKE_CELL;

  plug_set_timeout(script_infos, to ? to : -1 );
  return FAKE_CELL;
}


tree_cell* script_id(lex_ctxt * lexic)
{
 struct arglist * script_infos = lexic->script_infos;
 int id;

 id = get_int_var_by_num(lexic, 0, -1);
 if(id > 0)
  	plug_set_id(script_infos, id);

  return FAKE_CELL;
}

/*
 * TODO: support multiple CVE entries
 */
tree_cell* script_cve_id(lex_ctxt* lexic)
{
 struct arglist * script_infos = lexic->script_infos;
 char * cve = get_str_var_by_num(lexic, 0);
 int i;
 
 
 for (i = 0; cve != NULL ; i ++)
 { 
  plug_set_cve_id(script_infos, cve);
  cve = get_str_var_by_num(lexic, i + 1);
 }
	 

 return FAKE_CELL;
}

/*
 * TODO: support multiple bugtraq entries
 */
tree_cell* script_bugtraq_id(lex_ctxt* lexic)
{
 struct arglist * script_infos  = lexic->script_infos;
 char * bid = get_str_var_by_num(lexic, 0);
 int i;
 
 for (i = 0; bid != NULL ; i ++)
 {
  plug_set_bugtraq_id(script_infos, bid);
  bid = get_str_var_by_num(lexic, i + 1);
 }
 
 return FAKE_CELL;
}


tree_cell* script_xref(lex_ctxt* lexic)
{
 struct arglist * script_infos = lexic->script_infos;
 char * name = get_str_var_by_name(lexic, "name");
 char * value = get_str_var_by_name(lexic, "value");
 
 
 if( value == NULL || name == NULL )
 {
  fprintf(stderr, "script_xref() syntax error - should be script_xref(name:<name>, value:<value>)\n");
  return FAKE_CELL;
 }
 
 plug_set_xref(script_infos, name, value);
 
 return FAKE_CELL;
}


/* UNUSED */
tree_cell* script_see_also(lex_ctxt* lexic)
{
 nasl_perror(lexic, "Error - script_see_also() called\n");
 return FAKE_CELL;
}

typedef void(*script_register_func_t)(struct arglist*, const char *, const char*);

static tree_cell * script_elem(lex_ctxt* lexic, script_register_func_t script_register_func)
{ 
 struct arglist * script_infos = lexic->script_infos;
 char * lang = NULL;
 
 char * str;
 char * dfl = "english";
 
 if( lang == NULL )
 	lang = dfl;
 
 str = get_str_local_var_by_name(lexic, lang);
 

 if(str == NULL){
 	str = get_str_local_var_by_name(lexic, "english");
 	if( str == NULL ){
		str = get_str_var_by_num(lexic, 0);
 		if( str == NULL )
			return FAKE_CELL;
			
		}
	    }
	    
 script_register_func(script_infos, str, NULL);	    
 return FAKE_CELL;
}


tree_cell * script_name(lex_ctxt * lexic)
{
 
 return(script_elem(lexic, plug_set_name));
}



tree_cell * script_version(lex_ctxt * lexic)
{
 struct arglist * script_infos = lexic->script_infos;
 
 char * version = get_str_var_by_num(lexic, 0);
 if(version == NULL){
 	nasl_perror(lexic, "Argument error in function script_version()\n");
	nasl_perror(lexic, "Function usage is : script_version(<name>)\n");
	nasl_perror(lexic, "Where <name> is the name of another script\n");
	}
  
 else
 	plug_set_version(script_infos, version);
 
 return FAKE_CELL;
 
}

tree_cell * script_description(lex_ctxt * lexic)
{
 return(script_elem(lexic, plug_set_description));
}

tree_cell * script_copyright(lex_ctxt * lexic)
{
 return(script_elem(lexic, plug_set_copyright));
}

tree_cell * script_summary(lex_ctxt * lexic)
{
 return(script_elem(lexic, plug_set_summary));
}

tree_cell * script_category(lex_ctxt * lexic)
{
 struct arglist * script_infos  = lexic->script_infos;

 int category = get_int_var_by_num(lexic, 0, -1);
 
 
 if(category < 0){
 	nasl_perror(lexic, "Argument error in function script_category()\n");
	nasl_perror(lexic, "Function usage is : script_category(<category>)\n");
 	return FAKE_CELL;
 	}
 plug_set_category(script_infos, category);
 return FAKE_CELL;
}

tree_cell * script_family(lex_ctxt * lexic)
{
 return(script_elem(lexic, plug_set_family));
}

tree_cell * script_dependencie(lex_ctxt * lexic)
{
 struct arglist * script_infos  = lexic->script_infos;
 char * dep = get_str_var_by_num(lexic, 0);
 int i;
 
 if(dep == NULL){
 	nasl_perror(lexic, "Argument error in function script_dependencie()\n");
	nasl_perror(lexic, "Function usage is : script_dependencie(<name>)\n");
	nasl_perror(lexic, "Where <name> is the name of another script\n");
	
	return FAKE_CELL;
	}
  
 for(i=0;dep != NULL;i++)
 {
  dep = get_str_var_by_num(lexic, i);
  if(dep != NULL)
	plug_set_dep(script_infos, dep);
 }
 
 return FAKE_CELL;
}


tree_cell * script_require_keys(lex_ctxt * lexic)
{
 struct arglist * script_infos  = lexic->script_infos;
 
 char * keys = get_str_var_by_num(lexic, 0);
 int i;
 
 if(keys == NULL){
 	nasl_perror(lexic, "Argument error in function script_require_keys()\n");
	nasl_perror(lexic, "Function usage is : script_require_keys(<name>)\n");
	nasl_perror(lexic, "Where <name> is the name of a key\n");
	return FAKE_CELL;
	}
  
 for(i=0; keys != NULL;i++)
 {
  keys = get_str_var_by_num(lexic, i);
  if(keys != NULL)
  	plug_require_key(script_infos, keys);
 }
 
 return FAKE_CELL;
}

tree_cell * script_exclude_keys(lex_ctxt * lexic)
{
 struct arglist * script_infos = lexic->script_infos;
 
 int i;
 char * keys = get_str_var_by_num(lexic, 0);
 
 for(i=0;keys != NULL;i++)
 {
  keys = get_str_var_by_num(lexic, i);
  if(keys != NULL)
  {
   plug_exclude_key(script_infos, keys);
  }
 }
 
 return FAKE_CELL;
}


tree_cell * script_require_ports(lex_ctxt * lexic)
{
 struct arglist * script_infos = lexic->script_infos;
 char * port;
 int i;

 for (i=0;;i++)
 {
  port = get_str_var_by_num(lexic, i);
  if(port != NULL)
	  plug_require_port(script_infos, port);
  else
	  break;
 }
 
 return FAKE_CELL;
}


tree_cell * script_require_udp_ports(lex_ctxt * lexic)
{
 struct arglist * script_infos = lexic->script_infos;
 int i;
 char * port;

 for ( i = 0; ; i ++)
 {
	 port = get_str_var_by_num(lexic, i);
	 if(port != NULL)
		 plug_require_udp_port(script_infos, port);
	 else
		 break;
 }

 return FAKE_CELL;
}

tree_cell * script_add_preference(lex_ctxt * lexic)
{
 char* name = get_str_local_var_by_name(lexic, "name");
 char* type = get_str_local_var_by_name(lexic, "type");
 char* value = get_str_local_var_by_name(lexic, "value");
 struct arglist *  script_infos = lexic->script_infos;
 
 if(name == NULL || type == NULL || value == NULL)
  nasl_perror(lexic, "Argument error in the call to script_add_preference()\n");
 else
   add_plugin_preference(script_infos, name, type, value);

 return FAKE_CELL;
}

tree_cell * script_get_preference(lex_ctxt * lexic)
{
 struct arglist *  script_infos = lexic->script_infos;
 tree_cell * retc;
 char * pref = get_str_var_by_num(lexic, 0);
 char * value;
 
 if(pref == NULL){
 	nasl_perror(lexic, "Argument error in the function script_get_preference()\n");
	nasl_perror(lexic, "Function usage is : pref = script_get_preference(<name>)\n");
 	return FAKE_CELL;
	}

 value = get_plugin_preference(script_infos, pref);
 if(value != NULL)
 {
 	 retc = alloc_tree_cell(0, NULL);
 	 if(isalldigit(value, strlen(value)))
	 {
	  retc->type = CONST_INT;
	  retc->x.i_val = atoi(value);
	 }
	 else
	 {
	  retc->type = CONST_DATA;
	  retc->size = strlen(value);
	  retc->x.str_val = estrdup(value);
	 }
	 return retc;
 } 
 else
	 return FAKE_CELL;
}


/* Are safe checks enabled ? */
tree_cell * safe_checks(lex_ctxt * lexic)
{
 struct arglist * script_infos = lexic->script_infos;
 struct arglist * prefs = arg_get_value(script_infos, "preferences");
 char * value;
 tree_cell * retc = alloc_tree_cell(0, NULL);
 
 retc->type = CONST_INT;
 value = arg_get_value(prefs, "safe_checks");
 if((value && !strcmp(value, "yes")))
 {
	 retc->x.i_val = 1;
 }
 else
	 retc->x.i_val = 0;

 return retc;
}  

/*--------------------[ KB ]---------------------------------------*/


tree_cell * get_kb_list(lex_ctxt * lexic)
{
 struct arglist * script_infos = lexic->script_infos;
 struct arglist * kb = arg_get_value(script_infos, "key");	/* gruik gruik */
 char * kb_mask = get_str_var_by_num(lexic, 0);
 tree_cell * retc;
 int num_elems = 0;
 nasl_array * a;
 
 
 if(kb_mask == NULL)
 {
  nasl_perror(lexic, "get_kb_list() usage : get_kb_list(<NameOfItem>)\n");
  return NULL;
 }
 
 if(kb == NULL)
 {
   return NULL;
 }
 
 retc = alloc_tree_cell(0,NULL);
 retc->type = DYN_ARRAY;
 retc->x.ref_val = a = emalloc(sizeof(nasl_array));
 while(kb->next != NULL)
 {
  anon_nasl_var v;
  bzero(&v, sizeof(v));
  if(str_match(kb->name, kb_mask, 1) != 0)
  {
  if(kb->type == ARG_INT)
  {
  v.var_type = VAR2_INT;
  v.v.v_int = (int)kb->value;
  add_var_to_array(a, kb->name, &v);
  num_elems ++;
  }
  else if(kb->type == ARG_STRING)
  {
   v.var_type = VAR2_DATA;
   v.v.v_str.s_val = kb->value;
   v.v.v_str.s_siz = strlen(kb->value);
   add_var_to_array(a, kb->name, &v);
   num_elems ++;
  }
  else if(kb->type == ARG_ARGLIST)
  {
   struct arglist * tmp = kb->value;
   if( tmp != NULL )
     while (tmp->next != NULL )
     {
       v.var_type = VAR2_DATA;
       v.v.v_str.s_val = tmp->value;
       v.v.v_str.s_siz = strlen(tmp->value);
       add_var_to_array(a, kb->name, &v);
       num_elems ++;
       tmp = tmp->next;
     }
   }
  }
  kb = kb->next;
 }
 
 if(num_elems == 0)
 {
  deref_cell(retc);
  return NULL;
 }
 return retc;
}


tree_cell * get_kb_item(lex_ctxt * lexic)
{
 struct arglist * script_infos = lexic->script_infos;

 char * kb_entry = get_str_var_by_num(lexic, 0);
 char * val;
 tree_cell * retc;

 if(kb_entry == NULL)
	 return NULL;

 val = plug_get_key(script_infos,kb_entry);


 retc = alloc_tree_cell(0, NULL);
 if ( val == NULL )
 {
  /* Shall we return a const int of 0 ? */
  retc->type = CONST_DATA;	
  retc->size = 0;
  retc->x.str_val = estrdup("");
  return retc;
 }
 
 if(isalldigit(val, strlen(val)))
 {
  retc->type = CONST_INT;
  retc->x.i_val = atoi(val);
  return retc;
 }
 else
 {
 retc->type = CONST_DATA;
 retc->size = strlen(val);
 retc->x.str_val = estrdup(val);
 }

 return retc;
}

tree_cell * set_kb_item(lex_ctxt * lexic)
{
 struct arglist * script_infos = lexic->script_infos;
 char * name = get_str_local_var_by_name(lexic, "name");
 char * value= get_str_local_var_by_name(lexic, "value");

 
 if(name != NULL && value != NULL)
  plug_set_key(script_infos, name, ARG_STRING, value);
 else
   {
     nasl_perror(lexic, "Syntax error with set_kb_item(name:0x%.2x, value: 0x%.2x)\n", name, value);
     return NULL;
   }

 return FAKE_CELL;
}

/*------------------------[ Reporting a problem ]---------------------------*/


/*
 * These functions are used when the script wants to report a problem
 * back to nessusd
 */
typedef void(*proto_post_something_t)(struct arglist*, int, const char*, const char *);
typedef void(*post_something_t)(struct arglist*, int, const char*);


static tree_cell * security_something(lex_ctxt * lexic, proto_post_something_t proto_post_func, post_something_t post_func)
{
 struct arglist * script_infos = lexic->script_infos;

 char* proto = get_str_local_var_by_name(lexic, "protocol");
 char* data = get_str_local_var_by_name(lexic, "data");
 int port = get_int_local_var_by_name(lexic, "port", -1); 
 char * dup = NULL;
 
 if(data != NULL)
 {
  int len = get_local_var_size_by_name(lexic, "data");
  int i;
  
  dup = nasl_strndup(data, len);
  for(i=0;i<len;i++)
   if(dup[i] == 0)dup[i]=' ';
 }
 
 if((arg_get_value(script_infos, "standalone")) != NULL)
 {
  if( data != NULL )
   fprintf(stdout, "%s\n", dup);
  else
   fprintf(stdout, "Success\n");
 }
   
 if(proto == NULL)
	 proto = get_str_local_var_by_name(lexic, "proto");

 if(port < 0)
	 port = get_int_var_by_num(lexic, 0, -1);

 
 if(dup != NULL)
 {
  if(proto == NULL)
   post_func(script_infos, port, dup);
  else
   proto_post_func(script_infos, port, proto, dup);

  efree(&dup);
  return FAKE_CELL;
 }
 
 if(proto == NULL)
  post_func(script_infos, port, NULL);
 else
  proto_post_func(script_infos, port, proto, NULL);
  

 return FAKE_CELL;
}
 
tree_cell * security_hole(lex_ctxt * lexic)
{
 return security_something(lexic, proto_post_hole, post_hole);
}

tree_cell * security_warning(lex_ctxt * lexic)
{
 return security_something(lexic, proto_post_info, post_info);
}

tree_cell * security_note(lex_ctxt * lexic)
{
 return security_something(lexic, proto_post_note, post_note);
}


/*-------------------------[ Reporting an open port ]---------------------*/

/*
 * If the plugin is a port scanner, it needs to report the list of open
 * ports back to nessusd, and it also needs to know which ports are
 * to scan
 */
 
 
tree_cell * nasl_scanner_get_port(lex_ctxt * lexic)
{
 tree_cell * retc;
 int idx = get_int_var_by_num(lexic, 0, -1);
 struct arglist * script_infos = lexic->script_infos;
 struct arglist * prefs = arg_get_value(script_infos, "preferences");
 char  *prange = arg_get_value(prefs, "port_range");
 static int num = 0;
 static u_short * ports = NULL;

 if (prange == NULL)
   return NULL; 

 if(idx < 0)
 {
 	nasl_perror(lexic, "Argument error in scanner_get_port()\n");
	nasl_perror(lexic, "Correct usage is : num = scanner_get_port(<num>)\n");
	nasl_perror(lexic, "Where <num> should be 0 the first time you call it\n");
	return NULL;
 }

 if (ports == NULL)
   {
     ports = (u_short*)getpts(prange, &num);
     if (ports == NULL)
       {
	 return NULL;
       }
   }
  
 if(idx >= num)
   {
    return NULL;
   }
 
 retc = alloc_tree_cell(0, NULL);
 retc->type = CONST_INT;
 retc->x.i_val = ports[idx];
 return retc;
}

 
tree_cell * nasl_scanner_add_port(lex_ctxt * lexic)
{ 
 struct arglist * script_infos = lexic->script_infos;

 int port = get_int_local_var_by_name(lexic, "port", -1);
 char* proto = get_str_local_var_by_name(lexic, "proto");

 if(port >= 0)
 {
  scanner_add_port(script_infos, port, proto?proto:"tcp");
 }

 return FAKE_CELL;
}
 
tree_cell * nasl_scanner_status(lex_ctxt * lexic)
{
 int current = get_int_local_var_by_name(lexic, "current", -1);
 int total   = get_int_local_var_by_name(lexic, "total", -1); 
 struct arglist * script_infos = lexic->script_infos;
 struct arglist * hostdata = arg_get_value(script_infos, "HOSTNAME");

 if(current != -1 && total != -1)
 {
  struct arglist * globs = arg_get_value(script_infos, "globals");			
  comm_send_status(globs, arg_get_value(hostdata, "FQDN"), "portscan",
  		     current, total);
  }
 return FAKE_CELL;
} 
