/*   
     SCOL sockets - windows  . Magma 1.0 . 1996 . Sylvain HUET

         socket.c : routines sockets bas-niveau
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>

#define SCOL_SOCKET_C
#include "../../kernel/include/socket.h"
#include "../unixscol.h"
#include "../../kernel/mmemory.h"
#include "mbytec.h"

int socklife;
int sockudp;
int STDPORT=1200;

#define SC_TYPCHN 0
#define SC_TYPSRV 1
#define SC_TYPUDP 2

int localscolIP;
int proxyIp;
int proxyPort;
int proxyMask;

int hostIPtab[32];
int nbIPtab;

int SCKaddsock(int s,int typ);
int SCKdelsock(int s);
int SCKsetwrite(int s);
int SCKclrwrite(int s);
int SCKtodestroy(int s);

int SCKbindadress()
{
  if (forcedIP[0]) return inet_addr(forcedIP);
  return INADDR_ANY;
}

int SCKlocalconnection()
{
  if (forcedIP[0]) return inet_addr(forcedIP);
  return inet_addr("127.0.0.1");
}

int SCKtestLife()
{
	char bf[8];
	int res;

	if (socklife==-1) return 0;
    res=recv(socklife,bf,4,MSG_PEEK);
    if (res<0)
      {
	if (errno!=EAGAIN) res=-1;
	else res=0;
      }
    else if (res==0) res=-1;
	if (res<0)
	{
		MMechostr(1,"Broken LifeSocket\n");
		EndScolMachine(0);
	}
	return 0;
}

/* initialisation des sockets */
int SCKinit(void *x)
{
  int un=1;
  SCKloopbackIP();
  nbIPtab=0;
  proxyIp=0;
  sockudp=socket(PF_INET,SOCK_DGRAM,0);
  //  MMechostr(1,"udp socket\n");
  if (sockudp==-1) return -1;
  setsockopt(sockudp,SOL_SOCKET,SO_BROADCAST,&un,sizeof(int));
  
  return 0;
}

/* fin d'utilisation des sockets */
int SCKend()
{
  return 0;
}

/* regle le proxy (ip="0.0.0.0" -> pas de proxy) */
int SCKsetproxy(char *ip, char *port,char *mask)
{
  struct hostent *hp;
  if (ip)
    {
      if ((proxyIp=inet_addr(ip))==INADDR_NONE)
	{
	  if ((hp=gethostbyname(ip))) proxyIp=*((int *) (hp->h_addr));
	  else proxyIp=0;
	}
    }
  else proxyIp=0;
  if (port) proxyPort=atoi(port);
  if (mask) proxyMask=inet_addr(mask);
  return 0;
}

/* definit l'adresse IP locale comme loopback */
int SCKloopbackIP()
{
  localscolIP=inet_addr("127.0.0.1");
  hostIPtab[0]=localscolIP;
  nbIPtab=1;
  return localscolIP;
}

/* definit l'adresse IP locale */
int SCKgethostIP()
{
  int k;
  char name[256];
  struct hostent *hp;
  int i;
  int **p;

  k=gethostname(name,256);
  SCKloopbackIP();
  if (k!=-1)
    {
      MMechostr(0,"gethostname=%s\n",name);
      hp = gethostbyname(name);
      if (hp)
	{
	  localscolIP = *((int *) (hp->h_addr));
	  if (localscolIP==0) return SCKloopbackIP();
	  p=(int**)hp->h_addr_list;
	  i=0;
	  while((p[i])&&(i<32))
	    {
	      hostIPtab[i]=*(p[i]);
	      i++;
	    }
	  nbIPtab=i;
	}
    }
  else MMechostr(0,"gethostname=ERROR\n");

  return localscolIP;
}
/*retourne le nombre d'adresses IP locales */
int SCKgetnbIP()
{
  return nbIPtab;
}

/*retourne la i-eme adresse IP locale */
int SCKgetnthIP(char *buf,int i)
{
  struct in_addr Addr;
  if ((i<0)||(i>=nbIPtab))
    buf[0]=0;
  else
    {
      Addr.s_addr=hostIPtab[i];
      strcpy(buf,inet_ntoa(Addr));
    }
  return 0;
}

char *SCKhostname(char *buf)
{
  if (gethostname(buf,256)) strcpy(buf,"localhost");
  return buf;
}

char *SCKhostIP(char *buf)
{
  struct in_addr Addr;

  Addr.s_addr=localscolIP;
  strcpy(buf,(char*)inet_ntoa(Addr));
  return buf;
}

char *SCKgetstringbyip(char *buf,int ip,int port)
{
  struct in_addr Addr;

  Addr.s_addr=ip;
  sprintf(buf,"%s:%d",inet_ntoa(Addr),port);
  return buf;
}

char *SCKgetstringbyip2(char *buf,int ip)
{
  struct in_addr Addr;

  Addr.s_addr=ip;
  sprintf(buf,"%s",inet_ntoa(Addr));
  return buf;
}

char *SCKgethostbyname(char *buf,char *name)
{
  struct hostent *hp;
  struct in_addr Addr;

  hp = gethostbyname(name);
  if (hp==NULL) return NULL;
  Addr.s_addr=*((int *) (hp->h_addr));
  sprintf(buf,"%s",inet_ntoa(Addr));
  return buf;
}

char *SCKgetnamebyIP(char *buf,char *IP)
{
  struct hostent *h;
  int k;
  char *p;

  k=inet_addr(IP);
  p=(char*)&k;
  h=gethostbyaddr(p,4,AF_INET);
  if (h==NULL) return NULL;
  strcpy(buf,h->h_name);
  return buf;
}

/* definit le serveur associe a la machine scol
   retourne le descripteur de socket si succes
   retourne un code negatif si erreur */
int SCKstartserver(int port)
{
  struct sockaddr_in ina;
  int s;
  long argp=1;
  int opt;
  
  s=socket(PF_INET,SOCK_STREAM,0);
  if (s==-1) return -1;
  
  opt=1;
  setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt));
  /* passage en non bloquant */
  ioctl (s, FIONBIO, &argp);
  
  ina.sin_family = PF_INET;
  ina.sin_port   = htons(port);
  ina.sin_addr.s_addr = SCKbindadress();
  
  if (bind(s,(struct sockaddr*)&ina,sizeof(ina))!=0)
    {
      MMechostr(MSKDEBUG,"Tcp port %d busy\n",port);
      close(s);
      return -2;
    }
  MMechostr(MSKDEBUG,"create Tcp server %d on %d\n",s,port);
  if (listen(s,5)!=0) return -3;
  if (SCKaddsock(s,SC_TYPSRV)) return -1;
  return s;
}

/* acceptation d'une liaison de type serveur
   retourne le descripteur de socket si succes
   retourne un code negatif si erreur */
int SCKaccept(int s,int *ip,int *port)
{
  struct sockaddr_in cor;
  int ns;
  int sizecor;
  long argp=1;
  
  sizecor=sizeof(cor);
  ns=accept(s,(struct sockaddr*)&cor,&sizecor);
  if (ns==-1) return -1;
  /* passage en non bloquant */
  ioctl (ns, FIONBIO, &argp);
  if (SCKaddsock(ns,SC_TYPCHN)) return -1;
  *ip=cor.sin_addr.s_addr;
  *port=ntohs(cor.sin_port);
  MMechostr(MSKTRACE,"new socket %d\n",ns);
  return ns;
}


int cutaddr(char *addr, char *buf)
{
  while(((*addr)!=':')&&(*addr)) *(buf++)=*(addr++);
  *buf=0;
  if ((*addr)!=':') return -1;
  return atoi(&addr[1]);
}

/* creation d'une liaison de type client
   retourne le descripteur de socket si succes
   retourne un code negatif si erreur */
int SCKconnect(char *addr,int *ip,int *prt,int *proxy)
{
  struct sockaddr_in ina;
  int s;
  int port,x;
  char buf[256];
  long argp=1;
  
  *proxy=0;
  if (strlen(addr)>256) return -1;
  port=cutaddr(addr,buf);
  if (port==-1) return -1;
  if (buf[0]==0) x=SCKlocalconnection();
  else x=inet_addr(buf);
  
  sprintf(MMbuf,"CONNECT %s (%x):%x\n",buf,x,port);
  MMechostr(MSKDEBUG,MMbuf);
  
  s=socket(PF_INET,SOCK_STREAM,0);
  if (s==-1) return -1;
  /* passage en non bloquant */
  ioctl (s, FIONBIO, &argp);
  
  ina.sin_family = PF_INET;
  ina.sin_port   = htons(port);
  ina.sin_addr.s_addr = x;
  if ( (connect(s,(struct sockaddr *)&ina,sizeof(ina))!=0)
      &&(errno!=EINPROGRESS) )
    {
      close(s);
      return -1;
    }
  if (SCKaddsock(s,SC_TYPCHN)) return -1;
  *ip=x;
  *prt=port;
  return s;
}

/* definit un serveur udp
   retourne le descripteur de socket si succes
   retourne un code negatif si erreur */
int SCKstartudpserver(int port)
{
  struct sockaddr_in ina;
  int socksrv;
  int un=1;
  long argp=1;
  
  socksrv=socket(PF_INET,SOCK_DGRAM,0);
  if (socksrv==-1) return -1;
  
  setsockopt(socksrv,SOL_SOCKET,SO_BROADCAST,&un,sizeof(int));
  
  ina.sin_family = PF_INET;
  ina.sin_port   = htons(port);
  ina.sin_addr.s_addr = SCKbindadress();
  if (bind(socksrv,(struct sockaddr*)&ina,sizeof(ina))!=0)
    {
      MMechostr(MSKDEBUG,"Tcp port %d busy\n",port);
      close(socksrv);
      return -2;
    }
  ioctl (socksrv, FIONBIO, &argp);
  if (SCKaddsock(socksrv,SC_TYPUDP)) return -1;
  MMechostr(MSKDEBUG,"create Udp server %d on %d\n",socksrv,port);
  return (int)socksrv;
}

/* creation d'une liaison de type client
   retourne le descripteur de socket si succes
   retourne un code negatif si erreur */
int SCKsendudp(char *addr,char *txt, int len)
{
  struct sockaddr_in ina;
  int port,x;
  char buf[256];
  
  if (strlen(addr)>256) return -1;
  port=cutaddr(addr,buf);
  if (port==-1) return -1;
  if (buf[0]==0) x=SCKlocalconnection();
  else x=inet_addr(buf);
    
  ina.sin_family = PF_INET;
  ina.sin_port   = htons(port);
  ina.sin_addr.s_addr = x;
  sendto(sockudp,txt,len,0,(struct sockaddr *)&ina,sizeof(ina));
  
  return 0;
}

/* creation d'une liaison de type client
   retourne le descripteur de socket si succes
   retourne un code negatif si erreur */
int SCKsendudpchn(int sock,char *addr,char *txt, int len)
{
  struct sockaddr_in ina;
  int port,x;
  char buf[256];
  
  if (strlen(addr)>256) return -1;
  port=cutaddr(addr,buf);
  if (port==-1) return -1;
  if (buf[0]==0) x=SCKlocalconnection();
  else x=inet_addr(buf);
  
  ina.sin_family = PF_INET;
  ina.sin_port   = htons(port);
  ina.sin_addr.s_addr = x;
  sendto(sock,txt,len,0,(struct sockaddr *)&ina,sizeof(ina));
  
  return 0;
}

/* ferme une socket
   retourne toujours 0 */
int SCKclose(int s)
{
  SCKdelsock(s);
  close(s);
  return 0;
}

/* ferme le serveur
   retourne toujours 0 */
int SCKcloseserver(int s)
{
  SCKdelsock(s);
  close(s);
  return 0;
}

/* envoie un paquet de caracteres sur une socket
   retourne le nombre d'octets effectivement envoyes (eventuellement 0)
   retourne un code negatif si erreur autre que WouldBlock */
int SCKsend(int s,char *buf, int len)
{
  int res;
  res=send(s,buf,len,0);
  if (res<0)
    {
      if (errno!=EAGAIN) res=-1;
      else res=0;
    }
  return res;
}

/* lit un paquet de caracteres sur une socket
   retourne le nombre d'octets effectivement lus (eventuellement 0)
   retourne un code negatif si erreur autre que WouldBlock */
int SCKrecv(int s,char *buf, int len,int *ip,int *port)
{
  int res;
  struct sockaddr_in ina;
  int l,i;
  char bufhack[17];

  l=sizeof(ina);
  res=recvfrom(s,buf,len,0,(struct sockaddr *)&ina,&l);
  /*
  res=recv(s,buf,len,0);
  */
  if (res<0)
    {
      if (errno!=EAGAIN) res=-1;
      else res=0;
    }
  else if (res==0) res=-1;
  *ip=ina.sin_addr.s_addr;
  *port=ntohs(ina.sin_port);
  /*
    for(i=0;((i<16)&&(i<res));i++)
    if (buf[i]<32) bufhack[i]='.'; else bufhack[i]=buf[i];
    bufhack[i]=0; 
    MMechostr(MSKFOO,"#rec %d from %x (%s)\n",res,*ip,bufhack);
  */
  return res;
}

int SCKpostclose(int s)
{
  /*
  close(s);
  SCKtodestroy(s);
  */
  return 0;
}









