/*   
SCOL sockets - windows  . Magma 1.0 . 1996 . Sylvain HUET

  socket.c : routines sockets bas-niveau
*/
// Modifications History
//
//$ LB(20/06/2001): Modify function SCKconnect : replace old socks4 protocol by SOCKSconnect function call 
//                  in order to manage socks4 and socks5
//$ FA(06/08/2001): Modify include of mbytec.h to vm/mbytec.h
//$ FA(30/08/2001): Set no delay option (TCP_NODELAY) (commented out)
//

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <winsock.h>
#include <windows.h>
#include "../../kernel/include/common.h"
#include "../../kernel/include/socket.h"
#include "../../kernel/mmemory.h"
#include "../../kernel/vm/mbytec.h"
#include "../winscol.h"
#include "../../kernel/include/socks.h"

#include <wininet.h>

int socklife;
int sockudp;
int STDPORT=1200;

int localscolIP;
char szHost[256];
char achHostInfo[MAXGETHOSTSTRUCT];

int proxyIp;
int proxyPort;
int proxyMask;

int hostIPtab[32];
int nbIPtab;

int testconnect (int ip)
{
	if ((localscolIP==(int)inet_addr("127.0.0.1"))&&(ip!=localscolIP)) return 0;
	return 1;
}

int SCKbindadress()
{
#ifdef SERVER
  if (forcedIP[0]) return inet_addr(forcedIP);
#endif
  return INADDR_ANY;
}

int SCKlocalconnection()
{
#ifdef SERVER
  if (forcedIP[0]) return inet_addr(forcedIP);
#endif
  return inet_addr("127.0.0.1");
}

/* 2 fonctions mises pour compatibilite unix */
int SCKclrwrite(int s)
{
    return 0;
}

int SCKsetwrite(int s)
{
    return 0;
}

int SCKtestLife()
{
	char bf[8];
	int res;
	
	if (socklife==-1) return 0;
    res=recv((SOCKET)socklife,bf,4,MSG_PEEK);
    if (res<0)
	{
		if (WSAGetLastError()!=WSAEWOULDBLOCK) res=-1;
		else res=0;
	}
    else if (res==0) res=-1;
	if (res<0)
	{
		MMechostr(MSKRUNTIME,"Broken LifeSocket\n");
		EndScolMachine(0);
		return -1;
	}
	return 0;
}

int flaggethost;

/*
int hostnameWin(char *name,int size)
{
int n;
HKEY key;

  return -1;
  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,"System\\CurrentControlSet\\Services\\VxD\\MSTCP",0,KEY_ALL_ACCESS,&key)
		!=ERROR_SUCCESS) return -1;
		n=size;
		if (RegQueryValueEx(key,"HostName",0,NULL,name,&n)!=ERROR_SUCCESS)
		{
		name[0]=0;
		return -1;
		}
		if (name[0]==0) return -1;
		return 0;
		}
*/
int myhostname(char *name,int size)
{
	if (name[0]) return 0;
	MMechostr(MSKFOO,"BSD request\n");
	if (gethostname(name,size)==SOCKET_ERROR)
	{
		name[0]=0;
		return -1;
	}
	return 0;
}

int SCKstarthostname(HWND h)
{
	time_t timep;
	struct tm *res;

	time(&timep);
	res=localtime(&timep);
	if (res) MMechostr(MSKFOO,">%d-%02d-%02d_%02d-%02d-%02d\n",
		res->tm_year+1900,res->tm_mon+1,res->tm_mday,
		res->tm_hour,res->tm_min,res->tm_sec);
	if ((!flaggethost)&&(!myhostname(szHost,256)))
	{
		MMechostr(MSKFOO,">%s\n",szHost);
		flaggethost=1;
		WSAAsyncGetHostByName(h,WSA_GETHOST,szHost,achHostInfo,MAXGETHOSTSTRUCT);
	}
	return 0;
}

int SCKendhostname(LONG lParam)
{
	int i;
    MMechostr(MSKFOO,">>resultat gethostbyname\n");
    SCKgethostIP(lParam);
//    MMechostr(MSKFOO,"Local IP = %s\n",inet_ntoa(*(struct in_addr*)&localscolIP));
	for(i=0;i<nbIPtab;i++) MMechostr(MSKFOO," %d:%s\n",i,inet_ntoa(*(struct in_addr*)&hostIPtab[i]));
	flaggethost=0;
	return 0;
}

/* initialisation des sockets windows
definition de la fenetre devant recevoir les evenements reseau
lancement de la recherche de l'adresse IP locale 
retourne 0 si OK */
int SCKinit(HWND h)
{
	WORD wVersionRequested;
	WSADATA wsaData;
	
	hscol=h;
	wVersionRequested = MAKEWORD( 1, 1 );
	MMechostr(MSKDEBUG,"wsastartup\n");
	if (WSAStartup( wVersionRequested, &wsaData )) return -1;
	SCKloopbackIP();
	szHost[0]=0;
	nbIPtab=0;
	proxyIp=0;
	flaggethost=0;
	SCKstarthostname(h);
	sockudp=-1;
	return 0;
}

/* fin d'utilisation des sockets windows
retourne toujours 0 */
int SCKend()
{
	WSACleanup();
	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 a partir du resultat de WSAAsyncGetHostByName */
int SCKgethostIP(LONG lParam)
{
	int i;
	u_long **p;
	
    if (WSAGETASYNCERROR(lParam)) return SCKloopbackIP();
    localscolIP=(int)*((u_long FAR*)(((struct hostent FAR*)achHostInfo)->h_addr));
    if (localscolIP==0) return SCKloopbackIP();
	
	p=(u_long**)((struct hostent FAR*)achHostInfo)->h_addr_list;
	i=0;
	while((p[i])&&(i<32))
	{
		hostIPtab[i]=*(p[i]);
		i++;
	}
	nbIPtab=i;
    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,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;
	
	if (testconnect(0)==0)
	{
		MMechostr(MSKDEBUG,"do not perform gethostbyname\n");
		return NULL;
	}
	
	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;
	
	if (testconnect(0)==0)
	{
		MMechostr(MSKDEBUG,"do not perform gethostbyaddr\n");
		return NULL;
	}
	
	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)
{
    SOCKADDR_IN ina;
    SOCKET socksrv;
  //int nodelay = 1;
	
    socksrv=socket(PF_INET,SOCK_STREAM,0);
    if (socksrv==INVALID_SOCKET) return -1;
	
  //$ FA(30/08/2001)
  //setsockopt(socksrv, IPPROTO_TCP, TCP_NODELAY, (const char*)&nodelay, sizeof(int));
	
    if (WSAAsyncSelect(socksrv,hscol,WSA_ASYNC,
        (FD_ACCEPT|FD_READ|FD_WRITE|FD_CLOSE))==SOCKET_ERROR)
	{
		closesocket(socksrv);
		return -1;
	}
	
    ina.sin_family = PF_INET;
    ina.sin_port   = htons((unsigned short)port);
    ina.sin_addr.s_addr = SCKbindadress();
	
    if (bind(socksrv,(struct sockaddr*)&ina,sizeof(ina))!=0)
	{
	    MMechostr(MSKDEBUG,"Tcp port %d busy\n",port);
		closesocket(socksrv);
		return -2;
	}
    if (listen(socksrv,3)!=0)
	{
	    MMechostr(MSKDEBUG,"Tcp port %d listen error\n",port);
		closesocket(socksrv);
		return -3;
	}
    MMechostr(MSKDEBUG,"create Tcp server %d on %d\n",socksrv,port);
    return (int)socksrv;
}

/* 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)
{
    SOCKADDR_IN cor;
    SOCKET ns;
    int sizecor;
	
    sizecor=sizeof(cor);
    ns=accept(s,(struct sockaddr*)&cor,&sizecor);
    if (ns==INVALID_SOCKET) return -1;
    *ip=cor.sin_addr.s_addr;
    *port=ntohs(cor.sin_port);
    return (int)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)
{
    SOCKADDR_IN ina;
    SOCKET s;
    int port,x;
    char buf[256];
  //int nodelay = 1;

    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);
    
    MMechostr(MSKDEBUG,"CONNECT %s (%x):%x\n",buf,x,port);
	
	if (testconnect (x)==0)
	{
		MMechostr(MSKDEBUG,"do not connect\n");
		return -1;
	}
	
	s=socket(PF_INET,SOCK_STREAM,0);
  //$ FA(30/08/2001)
  //setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const char*)&nodelay, sizeof(int));
    if (s==INVALID_SOCKET) return -1;
	if ((proxyIp==0)||(x==(int)SCKlocalconnection())||((proxyIp&proxyMask)==(x&proxyMask)))
	{
        if (WSAAsyncSelect(s,hscol,WSA_ASYNC,
			(FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE))==SOCKET_ERROR)
		{
			closesocket(s);
			return -1;
		}
		ina.sin_family = PF_INET;
		ina.sin_port   = htons((unsigned short)port);
		ina.sin_addr.s_addr = x;
		if ( (connect(s,(struct sockaddr *)&ina,sizeof(ina))!=0)
			&&(WSAGetLastError()!=WSAEWOULDBLOCK) )
		{
			closesocket(s);
			return -1;
		}
		*proxy=0;
	}
	else
	{
		MMechostr(MSKDEBUG,"using proxy %x %x\n",proxyIp,proxyMask);

		//$ LB : replace old socks4 protocol by SOCKSconnect function call from socks.c
		// which manage socks 4 and socks 5
		if (SOCKSconnect (s, x, htons((unsigned short)port)) != SOCKS_SUCCESS)
		{
			MMechostr (MSKDEBUG, "SCKconnect : error connecting socket %d\n", s);
			return -1;
		}
		else
			MMechostr (MSKDEBUG, "SCKconnect : socket %d connected\n", s);
		//

		if (WSAAsyncSelect(s,hscol,WSA_ASYNC,
			(FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE))==SOCKET_ERROR)
		{
			closesocket(s);
			return -1;
		}
		*proxy=0;
	}
    *ip=x;
	*prt=port;
	
    return (int)s;
}

/* definit un serveur udp
retourne le descripteur de socket si succes
retourne un code negatif si erreur */
int SCKstartudpserver(int port)
{
    SOCKADDR_IN ina;
    SOCKET socksrv;
	int un=1;
	
    socksrv=socket(PF_INET,SOCK_DGRAM,0);
    if (socksrv==INVALID_SOCKET) return -1;
	
    setsockopt(socksrv,SOL_SOCKET,SO_BROADCAST,(char*)&un,sizeof(int));
	
	
    ina.sin_family = PF_INET;
    ina.sin_port   = htons((unsigned short)port);
    ina.sin_addr.s_addr = SCKbindadress();
	
    if (bind(socksrv,(struct sockaddr*)&ina,sizeof(ina))!=0)
	{
	    MMechostr(MSKDEBUG,"Tcp port %d busy\n",port);
		closesocket(socksrv);
		return -2;
	}
    if (WSAAsyncSelect(socksrv,hscol,WSA_ASYNC,
        (FD_ACCEPT|FD_READ|FD_WRITE|FD_CLOSE))==SOCKET_ERROR)
	{
		closesocket(socksrv);
		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)
{
    SOCKADDR_IN ina;
    int port,x;
    char buf[256];
	int un=1;
	
    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);
    
	if (testconnect (x)==0)
	{
		MMechostr(MSKDEBUG,"do not send udp\n");
		return -1;
	}
	
	ina.sin_family = PF_INET;
	ina.sin_port   = htons((unsigned short)port);
	ina.sin_addr.s_addr = x;

	if (sockudp==-1)
	{
		sockudp=socket(PF_INET,SOCK_DGRAM,0);
		MMechostr(MSKDEBUG,"create udp socket\n");
		if (sockudp==INVALID_SOCKET)
		{
			MMechostr(MSKTRACE,"create udp socket ERROR\n");
			return 0;
		}
		setsockopt(sockudp,SOL_SOCKET,SO_BROADCAST,(char*)&un,sizeof(int));
	}
	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)
{
    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);
    
	if (testconnect (x)==0)
	{
		MMechostr(MSKDEBUG,"do not sendudp\n");
		return -1;
	}
	
	
	ina.sin_family = PF_INET;
	ina.sin_port   = htons((unsigned short)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)
{
    if (s>=0) closesocket((SOCKET)s);
    return 0;
}

/* ferme le serveur
retourne toujours 0 */
int SCKcloseserver(int s)
{
    if (s>=0) closesocket((SOCKET)s);
    return 0;
}

/* envoie un paquet de caracteres sur une socket
retourne le nombre d'octets effectivement envoyés (eventuellement 0)
retourne un code negatif si erreur autre que WouldBlock */
int SCKsend(int s,char *buf, int len)
{
    int res;
    res=send((SOCKET)s,buf,len,0);
    if (res<0)
	{
		if (WSAGetLastError()!=WSAEWOULDBLOCK) 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;
	SOCKADDR_IN add;
	int l;

//MMechostr(MSKTRACE, "=> SCKrecv(): sock = %d, len = %d\n", s, len);
	l=sizeof(add);
    res=recvfrom((SOCKET)s,buf,len,0,(struct sockaddr *)&add,&l);
	//    res=recv((SOCKET)s,buf,len,0);
    if (res<0)
	{
		if (WSAGetLastError()!=WSAEWOULDBLOCK) res=-1;
		else res=0;
	}
    else if (res==0) res=-1;
    *ip=add.sin_addr.s_addr;
    *port=ntohs(add.sin_port);
//MMechostr(MSKTRACE, "<= SCKrecv(): res = %d\n", res);
    return res;
}

int SCKpostclose(int s)
{
//	closesocket((SOCKET)s);
//  PostMessage(hscol,WSA_ASYNC,s,FD_CLOSE);
    return 0;
}



