/* Nessuslib -- the Nessus 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.
 *
 * Network Functions
 */ 

#define EXPORTING
#include <includes.h>
#include <stdarg.h>
#include "network.h"
#include "resolve.h"



#ifdef ENABLE_CRYPTO_LAYER
#include "peks.h"
#endif
#define TIMEOUT 20

#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif


static
void sighand_alarm()
{
 printf("could not open the connection : timeout\n");
}

ExtFunc
int open_sock_opt_hn(hostname, port, type, protocol)
 const char * hostname; 
 unsigned int port; 
 int type;
 int protocol;
{
 struct sockaddr_in addr;
 int soc;
  
  bzero((void*)&addr, sizeof(addr));
  addr.sin_family=AF_INET;
  addr.sin_port=htons((unsigned short)port);
  addr.sin_addr = nn_resolve(hostname);
  if (addr.sin_addr.s_addr == INADDR_NONE || addr.sin_addr.s_addr == 0)
    return(-1);
    
  if ((soc = socket(AF_INET, type, protocol)) < 0)
    return -1;   
  signal(SIGALRM, sighand_alarm);
  alarm(TIMEOUT); 
  if (connect(soc, (struct sockaddr*)&addr, sizeof(addr)) < 0)
    {
      closesocket(soc);
      alarm(0);
      return  -1;
    }
  signal(SIGALRM, SIG_IGN);
  alarm(0); 
  return soc;
}


ExtFunc
int open_sock_tcp_hn(hostname, port)
 const char * hostname;
 unsigned int port;
{
  return(open_sock_opt_hn(hostname, port, SOCK_STREAM, IPPROTO_TCP));
}


ExtFunc
int open_sock_tcp(args, port)
 struct arglist * args; 
 unsigned int port;
{
	return(open_sock_option(args, port, SOCK_STREAM,IPPROTO_TCP));
}


ExtFunc
int open_sock_udp(args, port)
 struct arglist * args;
 unsigned int port;
{
	return(open_sock_option(args, port, SOCK_DGRAM, IPPROTO_UDP));
}


ExtFunc
int open_sock_option(args, port, type, protocol)
 struct arglist * args;
 unsigned int port;
 int type;
 int protocol;
{
  struct sockaddr_in addr;
  struct in_addr * t;
  int soc;
  
  if(host_get_port_state(args, port)<=0)return(-1);
  bzero((void*)&addr, sizeof(addr));
  addr.sin_family=AF_INET;
  addr.sin_port=htons((unsigned short)port);
  t = plug_get_host_ip(args);
  if(!t)
  {
   fprintf(stderr, "ERROR ! NO ADDRESS ASSOCIATED WITH NAME\n");
   arg_dump(args, 0);
   return(-1);
  }
  addr.sin_addr = *t;
  if (addr.sin_addr.s_addr == INADDR_NONE)
    return(-1);
    
  if ((soc = socket(AF_INET, type, protocol)) < 0)
    return -1;  
  alarm(TIMEOUT);
  if (connect(soc, (struct sockaddr*)&addr, sizeof(addr)) < 0)
    {
      closesocket(soc);
      alarm(0);
      return  -1;
    }
  alarm(0); 
  return soc;
}



#ifndef CURRENTLY_DISABLED

/* This function reads a text from the socket stream into the
   argument buffer, always appending a '\0' byte.  The return
   value is the number of bytes read, without the trailing '\0'.

   This function may return 0 upon error, in which case errno is set.  

   Reading stops, if a '\0' or a '\n' is found in the input data 
   stream. (jordan) */

ExtFunc
int recv_line(soc, buf, bufsiz)
 int soc;
 char * buf;
 size_t bufsiz;
{
  char dummy [2] ;
  int n, i = -1;
  /* test the network ?? */
  if (bufsiz == 0)
    buf = dummy ;

  do {
    if ((n = recv (soc, buf + (++ i), 1, 0)) < 0) {
      /* FIXME: We currently do not return an error, yet. 
	 It would have to be globally checked out what the 
	 functions do when -1 is returned. */
     return 0 ;
    }
    if (n == 0) {
      /* This can happen if the cipher layer sends some embedded 
	 commands.  So have another try with a definite timeout */
#     ifdef ENABLE_CRYPTO_LAYER
      int save_tmo = io_recv_tmo (soc, 5 /* secs */) ;
      while ((n = io_recv (soc, buf + i, 1, 0)) == 0 &&
  	     /* check for a end-of-file condition */
  	     io_ctrl (soc, IO_EOF_STATE, 0, 0) == 0)
	;
      io_recv_tmo (soc, save_tmo) ;
      if (n > 0) continue ;
#     endif
      buf [i] = '\0' ;
      break ;
    }
  } while (buf [i] && buf [i] != '\n' && i+1 < bufsiz) ;
  
  /* As we deal with text, append a trailing '\0' and return the
     preceeding number of non-zero charachters */
  if (buf [i] != 0) {
    buf [++ i] = '\0';
    return i;
  }
  
  /* We do return 0 upon error, as well. So this kludge is applied 
     to let the reveiver know that there really was an empty string,
     sent */
#   ifdef USE_PTHREADS
  /* in which case errno is rather a function returning a pointer
     to the errno location of the current thread */
  if (i == 0)
#   endif
    errno = 0 ;
  return bufsiz ? i : 0 ;
} 

#else
ExtFunc
int recv_line(soc, buf, bufsiz)
 int soc;
 char * buf;
 size_t bufsiz;
{
 size_t i = 0;
 char c[2];
 int ok = 0;
 int count = 0;
 bzero(buf, bufsiz);
 bzero(c, 2);
 while(!ok)
 {
  if((recv(soc, c, 1, 0))>0)
  {
  count++;
  buf[i++] = c[0];
  if((!strlen(c)) || (!c[0]) || (c[0]=='\n') || (i>=bufsiz))ok=1;
  }
  else ok = 1;
 }
 return count;
} 
#endif
ExtFunc void
socket_close(soc)
int soc;
{
  closesocket(soc);
}

/*
 * auth_printf()
 *
 * Writes data to the global socket of the thread
 */
ExtFunc void 
auth_printf(struct arglist * globals, char * data, ...)
{
  va_list param;
  int soc = (int)arg_get_value(globals, "global_socket");
  char * buffer;
  int total = 0, current = 0, len = 0;
#ifndef NESSUSNT
  signal(SIGPIPE, exit);
#endif
  buffer = emalloc(4096);
  va_start(param, data);
#ifdef HAVE_VSNPRINTF
  vsnprintf(buffer, 4095, data, param);
#else
  vsprintf(buffer, data,param);
#endif
  len = total = strlen(buffer);
  while(total>0)
  {
   int n;
   n = send(soc, buffer+current, total, 0);
   current += n;
   if(n < 0)
    return;
   total = len - current;
  }
  efree(&buffer);
#ifndef NESSUSNT
  signal(SIGPIPE, SIG_IGN);
#endif
  va_end(param);
}                    


ExtFunc void
auth_send(struct arglist * globals, char * data)
{
 int soc = (int)arg_get_value(globals, "global_socket");
 send(soc, data, strlen(data), 0);
}

/*
 * auth_gets()
 *
 * Reads data from the global socket of the thread
 */
ExtFunc char * 
auth_gets(globals, buf, bufsiz)
     struct arglist * globals;
     char * buf;
     size_t bufsiz;
{
  int soc = (int)arg_get_value(globals, "global_socket");
  /* bzero(buf, bufsiz); */
  recv_line(soc, buf, bufsiz);
  return(buf);
}



#ifdef NOT_IMPLEMENTED
/*
 * Checksum routine for Internet Protocol family headers (C Version)
 * From ping examples in W.Richard Stevens "UNIX NETWORK PROGRAMMING" book.
 */
static
int in_cksum(p,n)
u_short *p; int n;
{
  register u_short answer;
  register long sum = 0;
  u_short odd_byte = 0;

  while( n > 1 )  { sum += *p++; n -= 2; }

  /* mop up an odd byte, if necessary */
  if( n == 1 ) {
      *(u_char *)(&odd_byte) = *(u_char *)p;
      sum += odd_byte;
  }

  sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
  sum += (sum >> 16);			/* add carry */
  answer = (int)~sum;			/* ones-complement, truncate*/
  return (answer);
}

/*
 * ping_host
 *
 * Pings a host -- returns 1 if it answered
 *
 */
ExtFunc
int ping_host(struct in_addr addr)
{
 int soc;
 char * buf;
 struct ip *ip;
 struct icmp *icmp;
 struct protoent * ip_proto;
 struct timeval tv;
#ifndef NESSUSNT
 struct timezone tz;
#endif
 int len;
 struct sockaddr_in saddr;
 int count = 1;
 int max_to = 3;
 int ok = 0;
 ip_proto = getprotobyname("icmp");
 if(!ip_proto)return(1);
  
 soc = socket(AF_INET, SOCK_RAW, ip_proto->p_proto);
 bzero(&saddr, sizeof(saddr));
 if(soc < 0 )return(1);
 while(max_to && !ok)
 {
  fd_set r,w;
  int n;
  buf = (char*)malloc(32);
  bzero(buf, 32);
  icmp = (struct icmp *)buf;
#ifndef NESSUSNT
  gettimeofday(&tv, &tz);
#endif
  icmp->icmp_type = ICMP_ECHO;
  icmp->icmp_code = 0;
  icmp->icmp_cksum = 0;
  icmp->icmp_seq = 1;
/*  SET_ICMP_LIFETIME(*icmp, 1024); */
  icmp->icmp_id = getpid() & 0xFFFF;

#ifndef NESSUSNT
  bcopy(&tv, &buf[8], sizeof(struct timeval));
  len = 8 + sizeof(struct timeval)+sizeof(int);
#else
  len = 8 + sizeof(int);
#endif
  saddr.sin_family = AF_INET;
  saddr.sin_addr = addr;
  saddr.sin_port = 0;
  bcopy(&count, &buf[8+sizeof(struct timeval)], sizeof(int));
  icmp->icmp_cksum = in_cksum((u_short *)icmp,len);
  sendto(soc, buf, len, 0, (struct sockaddr *)(&saddr), sizeof(struct sockaddr_in));
  sendto(soc, buf, len, 0, (struct sockaddr *)(&saddr), sizeof(struct sockaddr_in));
  sendto(soc, buf, len, 0, (struct sockaddr *)(&saddr), sizeof(struct sockaddr_in));
  free(buf);
  
  FD_ZERO(&r);
  FD_ZERO(&w);
#ifndef NESSUSNT  
  tv.tv_sec = 3;
  tv.tv_usec = 0;
#endif
  FD_SET(soc, &r);
  n = select(soc+1, &r, &w, NULL, &tv);
  if(n>0)
  {
   int slen = sizeof(struct sockaddr);
   buf = malloc(4096);
   bzero(buf, 4096);
   ip  = (struct ip *)buf;
   bzero(&saddr, sizeof(struct sockaddr_in));
   recvfrom(soc, buf, 4096, 0, (struct sockaddr *)&saddr, &slen);
   icmp = (struct icmp *)(buf + (ip->ip_hl << 2));
   if((icmp->icmp_type == ICMP_ECHOREPLY)&&
      (icmp->icmp_id == (getpid() & 0xFFFF)))
	  ok = 1;
   free(buf);
   }
   else max_to--;
  }
  shutdown(soc, 2);
  closesocket(soc);
  return(ok);
}
#endif
