/* Hostloop -- the Hostloop Library
 * Copyright (C) 1998 Renaud Deraison
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *
 * This library determines the names of the hosts that have special
 * relations with a first host (ie : they can mount its exported fs,
 * or they belong to the same (sub)network).
 * 
 * Used recursively, this library allow the test of a wide range of computers
 *
 *
 * Everything here is (c) 1998 Renaud Deraison <deraison@worldnet.fr>, except :
 *
 *  showmount.c : Copyright (c) 1993 Rick Sladkey <jrs@world.std.com> 
 *  host.c      : Copyright (c) 1986 The Regents of the University of California
 *                Portions Copyright (c) 1993 by Digital Equipment Corporation.
 *    
 *
 */
 
 
 
/* Includes */
#define EXPORTING
#include <includes.h>


#ifdef HAVE_NETINET_IP_H
#include <netinet/ip.h>
#endif

#ifdef HAVE_NETINET_IP_ICMP_H
#include <netinet/ip_icmp.h>
#endif
#include "hosts_loop.h"
#include "ip_loop.h"
#include "icmp.h"

#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif


short is_host_present(char *, struct host_in_loop * , short);
#ifndef NESSUSNT
void add_domain_hosts(struct hls_globals_t *,
		     struct host_in_loop * hosts, 
                     short host_id, short * num_hosts);
void add_showmount_hosts(struct hls_globals_t *,
			struct host_in_loop * hosts,
                         short host_id, short * num_hosts);
#endif
short is_host_tested(char * hostname, struct host_in_loop * hil, short num_hil);
short is_domain_tested(char * domain, struct domainlist_t * dml);
short is_ip_tested(char * hostname, struct host_in_loop * hil, 
             short num_hil);

extern char * name_to_domainname(char * hostname);
extern short get_host_rname(char ** hostname);
extern short get_domain_computers(char * domain, struct hostlist_t *hl);
extern int get_showmount_trusted(char * hostname, struct hostlist_t ** hostlist);  
extern void add_ip_hosts(struct hls_globals_t * hls_globals,
						 struct host_in_loop * hosts, 
						 short host_id, short * num_hosts);
short is_subnet_tested(struct hls_globals_t * hls_globals, 
			char * host, struct iplist_t *list);

void add_single_host(char * hostname, struct host_in_loop * hil, short *num_hil)
{
	if(!is_host_present(hostname, hil, *num_hil))
	{
		struct in_addr addr;
		int i = *num_hil;
		while(hil->next)hil=hil->next;
		hil->next = emalloc(sizeof(struct host_in_loop));
		hil = hil->next;
		hil->hostname = emalloc(strlen(hostname)+1); 
		strncpy(hil->hostname, hostname, strlen(hostname));
	        addr = nn_resolve(hostname);
		hil->hostip = emalloc(strlen(inet_ntoa(addr))+1);
		strncpy(hil->hostip, inet_ntoa(addr), strlen(inet_ntoa(addr)));
		hil->addr = addr;
		hil->id = (i);
		(*num_hil) = i+1;           
    }
}


ExtFunc struct hls_globals_t * hls_new_session(char * hostname, int options, int subnet_class)
{
        char * p;
        struct in_addr addr;
        char * a;
        struct hls_globals_t * hls_globals;
        
        hls_globals = malloc(sizeof(struct hls_globals_t));
        bzero(hls_globals, sizeof(struct hls_globals_t));
        
	hls_globals->hil = emalloc(sizeof(struct host_in_loop));
	hls_globals->c_hil = NULL;
	hls_globals->dml = NULL;
        hls_globals->subnets = NULL;
        p = strchr(hostname, ',');
	if(p)
	{
		p[0]=0;
		p++;
	}
        
	hls_globals->hil->hostname = emalloc(strlen(hostname)+1);
	strncpy(hls_globals->hil->hostname, hostname, strlen(hostname));
	hls_globals->hil->id = 0;
	hls_globals->hil->trusted_by = -1;
	hls_globals->hil->tested = 0;
	hls_globals->hil->next = NULL;
	hls_globals->hil->addr = nn_resolve(hostname);
	hls_globals->num_hil = 1;
	hls_globals->cur_hil = 0;
	hls_globals->dml = NULL;  
        hls_globals->nfs = (options & HL_NFS);
        hls_globals->dns = (options & HL_DNS);
        hls_globals->ip = (options & HL_IP);
        hls_globals->ping = (options & HL_PING)?1:0;
        hls_globals->revlookup = (options & HL_REVLOOKUP)?1:0;
        hls_globals->subnet_class = subnet_class;
	a = (char *)(&addr);
	a[0]=127;
	a[1]=0;
	a[2]=0;
	a[3]=0;
	hls_globals->c_hil = hls_globals->hil;
        if(p)
	{
		char * t;
           
		while((t = strchr(p, ',')))
		{
			*t = 0;
			add_single_host(p, hls_globals->hil, 
					&hls_globals->num_hil);
			p = t+1;
		}
		add_single_host(p, hls_globals->hil, &hls_globals->num_hil);
	}
        hls_globals->subnets = malloc(sizeof(struct iplist_t));
        bzero(hls_globals->subnets, sizeof(struct iplist_t));
        hls_globals->subnets->ip = addr;
        return(hls_globals);
}


ExtFunc char * hls_get_next_host(hls_globals)
 struct hls_globals_t * hls_globals;
{
	char * d;
        char * ret;
	struct domainlist_t *ldml = hls_globals->dml;
	
       
	if(hls_globals->c_hil && hls_globals->c_hil->hostname)
		d=(char *)name_to_domainname(hls_globals->c_hil->hostname);
	else return(NULL);
#ifndef NESSUSNT
	if(hls_globals->dns && d && !is_domain_tested(d, hls_globals->dml))
	{
	     
		add_domain_hosts(hls_globals, hls_globals->hil, hls_globals->cur_hil, 
				 &hls_globals->num_hil);
                                                                         
		if(!ldml)
			{
			hls_globals->dml = emalloc(sizeof(struct domainlist_t));
		        hls_globals->dml->domain = emalloc(strlen(d)+1);
			strncpy(hls_globals->dml->domain, d, strlen(d));
			}
		else
		{
		while(ldml->next)ldml = ldml->next;
		ldml->next = emalloc(sizeof(struct domainlist_t));
	       	ldml = ldml->next;
		ldml->domain = emalloc(strlen(d)+1);
		strncpy(ldml->domain, d, strlen(d));
		}
	}
#endif	
        
        if(hls_globals->ip && 
        	(!is_subnet_tested(hls_globals, hls_globals->c_hil->hostname, hls_globals->subnets)))
        {
        	
        	if(hls_globals->subnets)
                {
                  struct iplist_t * subs;
                  struct in_addr ip;
                 
                  subs = hls_globals->subnets;
                  while(subs->next)subs = subs->next;
                  subs->next = malloc(sizeof(struct iplist_t));
                  bzero(subs->next, sizeof(struct iplist_t));
                  subs = subs->next;
                  ip = hls_globals->c_hil->addr;
                  set_ip_number(&ip, 0, 3);
                  subs->ip = ip;
                 }
              add_ip_hosts(hls_globals, hls_globals->hil, hls_globals->cur_hil, &hls_globals->num_hil);   
                
               
        }
        
#ifndef NESSUSNT       
        if(hls_globals->nfs)
                add_showmount_hosts(hls_globals, hls_globals->hil, hls_globals->cur_hil,
                		&hls_globals->num_hil);
                                
#endif                              
	hls_globals->c_hil = hls_globals->hil;
skip:
	hls_globals->cur_hil++;
	if(hls_globals->cur_hil <= hls_globals->num_hil)
	{
	while(hls_globals->c_hil && hls_globals->c_hil->id != hls_globals->cur_hil)
            hls_globals->c_hil = hls_globals->c_hil->next;
                
	}
	else return(NULL);
        if(!hls_globals->c_hil)return(NULL);
        
        
	if((is_host_tested(hls_globals->c_hil->hostname, 
	   hls_globals->hil, hls_globals->num_hil))||
           (is_ip_tested(hls_globals->c_hil->hostname, 
		hls_globals->hil, hls_globals->num_hil)))goto skip;
           
        hls_globals->c_hil->tested = 1;	
        if(hls_globals->ping)hls_globals->c_hil->alive = is_host_alive(hls_globals->c_hil->addr)>0 ? 1:0;
        else hls_globals->c_hil->alive = 1;
        
        ret = hls_globals->c_hil->hostname;
        if(!hls_globals->c_hil->alive)goto skip;
        
        if(hls_globals->revlookup && inet_addr(ret)!=INADDR_NONE)
        {
         struct in_addr ip;
         char * name;
         
         ip.s_addr = inet_addr(ret);
         name = get_name_from_ip(ip);
         if(inet_addr(name)!=INADDR_NONE)goto skip;
        }
        else return(ret);
      return(NULL);
}
	

#ifndef NESSUSNT
/*
  add_domain_hosts()

  Parameters :
     struct host_in_loop * hosts : a pointer to an already initialized
                                   host_in_loop structure.

     short host id     : the id number of the initial host of a domain
     short * num_hosts : a pointer to the current number of hosts in the
                         structure...

  Returns :
     nothing (void)
     *hosts content is modified
     num_hosts is modified
    
    This function adds all the hosts of the domain of the host <id> in the
    struct *hosts, checks that the same host is not put twice in the structure,
    caches the IP addresses of those hosts and gives them an id number
*/
void add_domain_hosts(struct hls_globals_t * hls_globals, 
		      struct host_in_loop * hosts, 
                      short host_id, short * num_hosts)
{
  short i;
  struct host_in_loop *hil_orig = hosts;
  struct hostlist_t * hl = emalloc(sizeof(struct hostlist_t));
  char * hostname=NULL;
  char * domainname;
  
  bzero(hl, sizeof(struct hostlist_t));
  hil_orig =  hosts;   /* save the starting point */
  
 
  /* Get the name of the host that we shall test 	*/
  while(hosts->id != host_id)
    {
      if(hosts && hosts->next)hosts = hosts->next;
      else  return;
	
    }
  
  hostname = hosts->hostname;
  /* Now, we go to the end of the queue... */
  while(hosts->next)hosts=hosts->next;
  
  
  /* Now, let's get the list of the trusted hosts, and add it in the hil */
  
  if((domainname = (char *)name_to_domainname(hostname))==NULL)return;
  get_domain_computers(domainname, hl);
  if(hl->hostname)
    {
      struct hostlist_t * next;
      i = *num_hosts - 1;
      
      while(hl && hl->hostname)
	{
	 short do_test =1 ; /* pTEST(get_hostname_attributes(hl->hostname)); */
	 
	 
	  /* host_present() avoids to put twice the same host in
	     the hil */
	  
	
	 if(!is_host_present(hl->hostname, hil_orig, i) && do_test)
	    {
	      hosts->next = emalloc(sizeof(struct host_in_loop));
	      hosts = hosts->next;
	      hosts->hostname = emalloc(strlen(hl->hostname)+1);
	      strncpy(hosts->hostname, hl->hostname, strlen(hl->hostname));
	      hosts->hostip = emalloc(strlen(inet_ntoa(hl->hostip)));
	      strncpy(hosts->hostip, inet_ntoa(hl->hostip), 
		      strlen(inet_ntoa(hl->hostip)));
	      hosts->addr = hl->hostip;
	      hosts->id = ++i;
	      hosts->trusted_by = host_id;
	     /* hosts->full_results = malloc(sizeof(struct fullresults_t)); */
	    }
	  next = hl->next;
	  free(hl->hostname);
	  free(hl);
	  hl = next;
	}
    }
  else return;
  *num_hosts = i;
}

/*  add_showmount_hosts

    The same as add_domain_hosts() except that it adds the hosts which are allowed 
    to mount NFS partitions of the host <id> instead of adding the hosts of the
    same domain name.
*/
void add_showmount_hosts(struct hls_globals_t * hls_globals, 
			 struct host_in_loop * hosts, 
                         short host_id, 
			 short * num_hosts)
{
  short i;
  struct host_in_loop *hil_orig;
  struct hostlist_t * hl = malloc(sizeof(struct hostlist_t));
  char * hostname=NULL;

  bzero(hl, sizeof(struct hostlist_t));
  hil_orig =  hosts;   /* save the starting point */
  /* Get the name of the host that we shall test 	*/
  while(hosts->id != host_id)
    {
      if(hosts && hosts->next)hosts = hosts->next;
      else 
	{
	  return;
	}
    }
  hostname = hosts->hostname;
  /* Now, we go to the end of the queue... */
  while(hosts->next)hosts=hosts->next;
  /* Now, let's get the list of the trusted hosts, and add it in the hil */
  if(!get_showmount_trusted(hostname ,&hl ))
    {
      struct hostlist_t * next;
      i = *num_hosts - 1 ;
      while(hl && hl->hostname)
	{
	  char * tmp = "";
	  /* host_present() avoids to put twice the same host in
	     the hil */
	  /* tmp = name_to_ip(hl->hostname); */
	  
	  if(!is_host_present(hl->hostname, hil_orig, i))
	    {
	      hosts->next = emalloc(sizeof(struct host_in_loop));
	      hosts = hosts->next;
	      hosts->hostname = emalloc(strlen(hl->hostname)+1);
       	      strncpy(hosts->hostname, hl->hostname, strlen(hl->hostname));
	      hosts->hostip = emalloc(strlen(tmp)+1);
       	      strncpy(hosts->hostip, tmp, strlen(tmp));
	      hosts->addr = hl->hostip;
	      hosts->id = ++i;
	      hosts->trusted_by = host_id;
	    }
	  next = hl->next;
	  free(hl->hostname);
	  free(hl);
	  hl = next;
	}
    }
  else return;
  *num_hosts = i + 1;
}

#endif /* not defined(NESSUSNT) */

/*   is_host_tested()

     Parameters :
     char * hostname : name of the host
     hil, num_hil : struct and number of items in struct

     Returns :
     1 if this host was tested
     0 if it was not
   
     This function determines wether a given host was already
     tested or not
*/

short is_host_tested(char * hostname, struct host_in_loop * hil, short num_hil)
{
  while(num_hil--)
    {
      if(!strcmp(hostname, hil->hostname) && hil->tested)return(1);
      else hil = hil->next;
    }
  
  return(0);
}
		
		
/* is_host_present()

   Same as is_host_tested but, returns :
   1 if the host is present in the hil structure or
   0 if it's not
*/

short is_host_present(char *hostname, struct host_in_loop * hil, 
             short num_hil)
{	
  struct in_addr addr;
  
  addr = nn_resolve(hostname);
 
  while(num_hil--)
    {
      if(!strcmp(hostname, hil->hostname) ||
      	(addr.s_addr == hil->addr.s_addr))return(1);
      else hil = hil->next;
    }
  return(0);
}



/* is_ip_tested()

   Same as is_host_tested but, returns :
   1 if the host ip is present in the hil structure or
   0 if it's not
*/



short is_ip_tested(char * hostname, struct host_in_loop * hil, 
             short num_hil)
{
	struct in_addr addr = nn_resolve(hostname);
	while(num_hil--)
	{
	 if((addr.s_addr == hil->addr.s_addr)&&hil->tested)
	  return(1);
	 hil=hil->next;
	}
 return(0);
}

/*	is_domain_tested()

	Determines wether a given domain has already been tested.
	Returns :
	   1 if it has been already tested
	   0 if it has not
*/

short is_domain_tested(char * domain, struct domainlist_t * dml)
{
  int i,j;
  if(!domain)return(1);
  i = strlen(domain);
  for(j=0;j<i;j++)domain[j]=tolower(domain[j]);
  while(dml && dml->domain)
    {
      if(!strcmp(domain, dml->domain))return(1);
      else dml = dml->next;
    }
  return(0);
}



short is_subnet_tested(struct hls_globals_t * hls_globals, char * host, 
		      struct iplist_t *list)
{
 struct in_addr ip;
 ip = get_first_ip(hls_globals, host);
 
 while(list)
 {
  if(ip.s_addr == list->ip.s_addr)return(1);
  list = list->next;
 }
 return(0);
}
	
	
	
