/* 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.
 *
 * Nessus Communication Manager -- it manages the NTP Protocol, version 1.1
 *
 */ 
 
#include <includes.h>

#include "auth.h"
#include "comm.h" 
#include "preferences.h"
#include "parser.h"
#include "globals.h"

/*
 * comm_init
 *
 * This function initializes the communication between 
 * the server and the client.
 * Its role is to check that the remote server is using NTP/1.1
 * 
 * Arguments :
 *  soc : a socket connected to the remote server
 * Returns :
 *  0 if the remote server is using NTP/1.1
 * -1 if it's not
 */
int 
comm_init(soc)
	int soc;
{  
  char * buf;
  send(soc, "< NTP/1.1 >\n", 12, 0);

  buf = emalloc(15);
  recv_line(soc, buf, 14);
  if(strncmp(buf, "< NTP/1.1 >", 11))
  	{
	efree(&buf);
	return(-1);
	}
  efree(&buf);
  return(0);
}


/*
 * Retrieves the server preferences
 * we must make a difference between the prefs of the 
 * server itself, and the prefs of the plugins
 */
int
comm_get_preferences(prefs)
 struct arglist * prefs;
{
 char * buf;
 int finished = 0;
 struct arglist * serv_prefs;
 struct arglist * plugin = NULL;
 
 serv_prefs = emalloc(sizeof(struct arglist));
 buf = emalloc(4096);
 network_gets(buf, 255);
 if(!strncmp(buf, "SERVER <|> PREFERENCES <|>", 26))
 {
 
  while(!finished)
  {
   bzero(buf, 4096);
   network_gets(buf, 4095);
   if(buf[strlen(buf)-1]=='\n')buf[strlen(buf)-1]=0;
   if(!strncmp(buf, "<|> SERVER", 10))finished = 1;
  else
   {
    char * pref;
    char * value;
    char * v;
    char * a = NULL , *b = NULL, *c = NULL;
    
    pref = buf;
    v = strchr(buf, '<');
    v-=1;
    v[0] = 0;
    
    value = v + 5;
    v = emalloc(strlen(value)+1);
    strncpy(v, value, strlen(value));
    a = strchr(pref, '[');
    if(a)b=strchr(a, ']');
    if(b)c=strchr(b,':');
    if((!a)||(!b)||(!c))arg_add_value(serv_prefs, pref, ARG_STRING, strlen(v), v);
    else
    {
     /* the format of the pref name is xxxx[xxxx] : this is a plugin pref */
     char * plugname;
     char * type;
     char * name;
     struct arglist * prefs, *prf;
     a[0]=0;
     plugname = emalloc(strlen(pref)+1);
     strncpy(plugname, pref, strlen(pref));
     
     a[0]='[';
     a++;
     b[0]=0;
     type = emalloc(strlen(a)+1);
     strncpy(type, a, strlen(a));
     b[0]=']';
     c++;
     name = emalloc(strlen(c)+1);
     strncpy(name, c, strlen(c));
     
     plugin = arg_get_value(Plugins, plugname);
     if(!plugin){
      printf("Error : we received a preference for the plugin %s\n", plugname);
      printf("but apparently the server has not loaded it\n");
      }
     prefs = arg_get_value(plugin, "plugin_prefs");
     if(!prefs)
     {
      prefs = emalloc(sizeof(struct arglist));
      arg_add_value(plugin, "plugin_prefs", ARG_ARGLIST, -1, prefs);
     }
     prf = emalloc(sizeof(struct arglist));
     arg_add_value(prf, "value", ARG_STRING,strlen(v), v);
     arg_add_value(prf, "type", ARG_STRING, strlen(type), type);
     arg_add_value(prefs, name, ARG_ARGLIST, -1, prf);
   }
   }
  }
  if(!arg_get_value(prefs, "SERVER_PREFS"))
   arg_add_value(prefs, "SERVER_PREFS", ARG_ARGLIST, sizeof(serv_prefs), serv_prefs);
 }
 efree(&buf);
 return(0); 
}

int 
comm_send_preferences(preferences)
 struct arglist * preferences;
{
 struct arglist * pref = arg_get_value(preferences, "SERVER_PREFS");
 struct arglist * plugs = Plugins;
 
 network_printf("CLIENT <|> PREFERENCES <|>\n");
 while(pref && pref->next)
  {
   if(pref->type == ARG_STRING)
    network_printf("%s <|> %s\n", pref->name, pref->value);
   pref = pref->next;
   }
 
 /* send the plugins prefs back to the server */
 while(plugs && plugs->next)
 {
  struct arglist * plugin_prefs = arg_get_value(plugs->value, "plugin_prefs");
  while(plugin_prefs && plugin_prefs->next)
  {
   char * name = plugin_prefs->name;
   char * type = arg_get_value(plugin_prefs->value, "type");
   char * value = arg_get_value(plugin_prefs->value, "value");
   network_printf("%s[%s]:%s <|> %s\n", plugs->name, type, name, value);
   plugin_prefs =   plugin_prefs->next;
  }
  plugs = plugs->next;
 }
 network_printf("<|> CLIENT\n");
 return(0);
}

int
comm_send_rules(preferences)
 struct arglist * preferences;
{
 struct arglist * serv_prefs = arg_get_value(preferences, "SERVER_PREFS");
 struct arglist * rules = arg_get_value(serv_prefs, "RULES");
 network_printf("CLIENT <|> RULES <|>\n");
 while(rules && rules->next)
 {
  network_printf("%s\n", rules->value);
  rules = rules->next;
 }
 network_printf("<|> CLIENT\n");
 return(0);
}


void
comm_get_preferences_errors(preferences)
 struct arglist * preferences;
{
 char * buf = emalloc(512);
 network_gets(buf, 512);
 network_gets(buf, 512);
 efree(&buf);
}


/*
 * Retrieves the server rules and store them in
 * a subcategory in the preferences
 */
int
comm_get_rules(prefs)
  struct arglist * prefs;
{
 struct arglist * serv_prefs = arg_get_value(prefs, "SERVER_PREFS");
 struct arglist * rules = NULL;
 char * buf = emalloc(4096);
 int finished = 0;
 
 rules = arg_get_value(prefs, "RULES");
 if(!rules)rules = arg_get_value(serv_prefs, "RULES");
 
 if(!rules){
 	rules = emalloc(sizeof(struct arglist));
        arg_add_value(prefs, "RULES", ARG_ARGLIST, -1, rules);
        }
 
 network_gets(buf, 4095);
 if(!strncmp(buf, "SERVER <|> RULES <|>", 20))
 {
  int i = 0;
  while(!finished)
  {
   char * rule, * name;
   network_gets(buf, 4095);
   if(strstr(buf, "<|> SERVER"))finished = 1;
   else
   {
    struct arglist * t = rules;
    int ok = 1;
    
    rule = emalloc(strlen(buf));
    strncpy(rule, buf, strlen(buf)-1);
    while(t && t->next && ok)
    	{
        if(!strcmp(t->value, rule))ok = 0;     
        t = t->next;
        }
    if(ok)
    {
     name = emalloc(10);
     sprintf(name, "%d", ++i);
     arg_add_value(rules, name, ARG_STRING, strlen(rule),rule); 
     efree(&name);
    }
    else efree(&rule);
   }
  }
 if(!arg_get_value(serv_prefs, "RULES"))
   arg_add_value(serv_prefs, "RULES", ARG_ARGLIST, -1, rules);
 else
   arg_set_value(serv_prefs, "RULES", -1, rules);
 }
 efree(&buf);
 return(0);
} 

/*
 * comm_get_pluginlist
 * 
 * This function reads the list of plugins sent by the server after 
 * a successful connection, in the format described by the NTP 
 * whitepaper, version 1.1
 *
 * returns : 
 *   0  if no problem occured
 *   -1 else
 */
int 
comm_get_pluginlist()
{
  char * buf;
  int flag = 0;
  int num = 0;
  int num_2 = 0;
  struct arglist * plugin_set;
  struct arglist * scanner_set;
  
  plugin_set = arg_get_value(Prefs, "PLUGIN_SET");
  scanner_set = arg_get_value(Prefs, "SCANNER_SET");
  
  buf = emalloc(4096);
  network_gets(buf, 27);
  if(strncmp(buf, "SERVER <|> PLUGIN_LIST <|>", 27))return(-1);
  bzero(buf, 4096);
  while(!flag)
    {
      network_gets(buf, 4095);
      if(!strncmp(buf, "<|> SERVER", 10))flag = 1;
      else
	{
	  char * str;
	  char * t;
	  struct arglist * plugin;
	  int id;
	  size_t l;
	  
	  sscanf(buf, "%d", &id);
#ifdef DEBUGMORE
	  fprintf(stderr, "buf : %s\nid : %d\n", buf, id);
#endif
	  plugin = emalloc(sizeof(struct arglist));
	  arg_add_value(plugin, "ID", ARG_INT, sizeof(int), (void *)id);
	  
	  str = parse_separator(buf);if(!str)continue;
	  plug_set_name(plugin, str);
	  
	  l = strlen(str) + 5;
	  str =  parse_separator(buf+l);if(!str)continue;
	  arg_add_value(plugin, "CATEGORY", ARG_STRING, strlen(str),str);
	  
	  l += strlen(str) + 5;
	  str =  parse_separator(buf+l);if(!str)continue;
	  plug_set_copyright(plugin, str);
	  
	  l+= strlen(str) + 5;
	  
	  str = parse_separator(buf+l);if(!str)continue;
	  t = str;
	  while((t=strchr(t, ';')))t[0]='\n';
	  plug_set_description(plugin, str);
	  
	  l+= strlen(str) + 5;
	  
	  str = parse_separator(buf+l);if(!str)continue;
	  plug_set_summary(plugin, str);
	  
	  l+= strlen(str) + 5;
	  
	  str = parse_separator(buf+l);if(!str)continue;
	  plug_set_family(plugin, str);
	  
	  arg_add_value(plugin, "ENABLED", ARG_INT, sizeof(int), (void *)1);
          
          
          if(!strcmp(arg_get_value(plugin, "CATEGORY"), "scanner"))
          {
           num_2++;
           if(!arg_get_value(Scanners, arg_get_value(plugin, "NAME")))
            arg_add_value(Scanners, arg_get_value(plugin, "NAME"), ARG_ARGLIST,
            		-1, plugin);
           else
             arg_set_value(Scanners, arg_get_value(plugin, "NAME"), -1, plugin); 
            if(scanner_set && 
              ((int)arg_get_type(scanner_set, arg_get_value(plugin, "NAME"))<0))
              arg_add_value(scanner_set, arg_get_value(plugin, "NAME"), ARG_INT,
                 sizeof(int),(void*)1);
           }
          else
          {
           num++;
           if(!arg_get_value(Plugins, arg_get_value(plugin, "NAME")))
	    arg_add_value(Plugins, arg_get_value(plugin, "NAME"), ARG_ARGLIST,
			-1, plugin);
           else
             arg_set_value(Plugins, arg_get_value(plugin, "NAME"), -1, plugin);
              
           if(plugin_set &&
             ((int)arg_get_type(plugin_set, arg_get_value(plugin, "NAME"))<0))
              arg_add_value(plugin_set, arg_get_value(plugin, "NAME"), ARG_INT,
              sizeof(int),(void*)1);
            
           }
        
            		
#ifdef DEBUG
	  fprintf(stderr, "%d : %s [%s] %s\n", 
		  (int)arg_get_value(plugin, "ID"),
		  (char *)arg_get_value(plugin, "NAME"),
		  (char *)arg_get_value(plugin, "COPYRIGHT"),
		  (char *)arg_get_value(plugin, "FAMILY"));
#endif
	}
    }
  pluginset_apply(Plugins, "PLUGIN_SET");
  pluginset_apply(Scanners, "SCANNER_SET");
  PluginsNum = num;
  ScannersNum = num_2;
  return(0);
}
