
/* OpenWebSpider
*
 *  Authors:     Stefano Alimonti AND Stefano Fantin
 *  Version:     0.8
 *  E-Mails:     shen139 [at] openwebspider (dot) org AND stefanofantinguz@yahoo.it
*
*
* This file is part of OpenWebSpider
*
* 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*/

#ifndef __SOCKET
#define __SOCKET

#define SEND(sock,buf)       send(sock,buf,strlen(buf),0)

#if defined(_WIN32) && !defined(WIN32)
#define WIN32
#endif

#ifdef WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib,"Ws2_32.lib")
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#endif

#include <errno.h>
#include <string.h>

static char gOwsSocketSetupError[256] = "";

static int OwsSocketLastError(void)
{
#ifdef WIN32
    return WSAGetLastError();
#else
    return errno;
#endif
}

static const char *OwsSocketErrorString(int err)
{
#ifdef WIN32
    static char msg[64];
    snprintf(msg, sizeof(msg), "WSA error %d", err);
    return msg;
#else
    return strerror(err);
#endif
}

static const char *OwsSocketSetupErrorString(void)
{
    return gOwsSocketSetupError[0] ? gOwsSocketSetupError : "unknown socket setup error";
}

static int OwsSocketErrorIsTransient(int err)
{
#ifdef WIN32
    return err == WSAEINTR || err == WSAEWOULDBLOCK || err == WSAEALREADY ||
           err == WSAEINPROGRESS || err == WSAETIMEDOUT ||
           err == WSAENETUNREACH || err == WSAEHOSTUNREACH;
#else
    return err == EINTR || err == EAGAIN || err == EALREADY ||
           err == EINPROGRESS || err == ETIMEDOUT ||
           err == ENETUNREACH || err == EHOSTUNREACH;
#endif
}


/*
 * Lettura sicura da socket in 'packet', fino a maxlen-1 byte
 * e terminazione con '\0'.
 *
 * sock   = puntatore a SOCKET (come da functions.h)
 * packet = buffer destinazione
 * maxlen = dimensione totale del buffer (in byte)
 */
int RecvPackets(SOCKET *sock, char *packet, int maxlen)
{
    int x;
    int recvdbytes = 0;

    if (!sock || !packet || maxlen <= 1)
        return 0;

    /* Azzeriamo tutto il buffer (così se leggiamo meno abbiamo già '\0') */
    memset(packet, 0, maxlen);

    while (1) {
        /* Lasciamo SEMPRE 1 byte libero per il terminatore '\0' */
        int space = maxlen - 1 - recvdbytes;

        if (space <= 0) {
            /* buffer pieno, non leggiamo oltre */
            break;
        }

        x = recv(*sock, packet + recvdbytes, space, 0);

        if (x < 0) {
            int sockErr = OwsSocketLastError();
            if (
#ifdef WIN32
                sockErr == WSAEINTR
#else
                sockErr == EINTR
#endif
            )
                continue;    /* se è stato un segnale, riproviamo */
            break;           /* errore reale */
        }

        if (x == 0) {
            /* connessione chiusa dal peer */
            break;
        }

        recvdbytes += x;
    }

    /* packet è già tutto azzerato, ma per sicurezza: */
    packet[recvdbytes] = '\0';

    return recvdbytes;
}


SOCKET OWS_Server_fd_conn;
SOCKET OWS_Server_fd;


int setnonblock(SOCKET sock,int to)
{
#ifdef WIN32
	DWORD timeout = (to < 0) ? 0 : (DWORD)to;
	if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*) &timeout, sizeof(timeout)) != 0)
		return 0;
	if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*) &timeout, sizeof(timeout)) != 0)
		return 0;
#else
	struct timeval timeout;
	
	timeout.tv_sec = to/1000;
	timeout.tv_usec=0;
	
	if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,(char*) &timeout, sizeof(timeout)))
		return 0;
	if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,(char*) &timeout, sizeof(timeout)))
		return 0;
#endif
	return 1;
}

int StartUpWinsock()
{
#ifdef WIN32
	WSADATA wsadata;
	
	if((WSAStartup(MAKEWORD(2,2),&wsadata))!=0 ||
	   LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2)
		return 0;
#endif
	return 1;
}

int LoadSocket(SOCKET *sock,struct sHost* shost,SOCKADDR_IN *saddr)
{
	struct hostent *gAddr;
	struct addrinfo hints;
	struct addrinfo *res = NULL;
	char portStr[16];
	int gaiRc;

	if(!sock || !shost || !saddr || strlen(shost->Host)<3)
	{
		snprintf(gOwsSocketSetupError, sizeof(gOwsSocketSetupError), "invalid host/socket data");
		return 0;
	}

	gOwsSocketSetupError[0] = '\0';
	*sock = INVALID_SOCKET;
	memset(saddr, 0, sizeof(*saddr));
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_INET;      /* keep existing SOCKADDR_IN API */
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	snprintf(portStr, sizeof(portStr), "%u", (unsigned int)shost->port);

	gaiRc = getaddrinfo(shost->Host, portStr, &hints, &res);
	if (gaiRc == 0 && res && res->ai_addr && res->ai_addrlen >= sizeof(struct sockaddr_in))
	{
		struct sockaddr_in *ai4 = (struct sockaddr_in*)res->ai_addr;
		saddr->sin_family = AF_INET;
		saddr->sin_addr = ai4->sin_addr;
		saddr->sin_port = ai4->sin_port;
		freeaddrinfo(res);
	}
	else
	{
		if (res)
			freeaddrinfo(res);
		if((gAddr=gethostbyname(shost->Host))==0)
		{
			snprintf(gOwsSocketSetupError, sizeof(gOwsSocketSetupError),
			         "DNS lookup failed for %s:%u (%s)",
			         shost->Host,
			         (unsigned int)shost->port,
			         gaiRc ? gai_strerror(gaiRc) : "host not found");
			return 0;
		}
		saddr->sin_addr.s_addr=((struct in_addr *)(gAddr->h_addr_list[0]))->s_addr;
		saddr->sin_family = AF_INET;
		saddr->sin_port = htons((unsigned short int)shost->port);
	}

	*sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

#ifdef WIN32
	if (*sock==INVALID_SOCKET)
#else
	if (*sock<0)
#endif
	{
		int sockErr = OwsSocketLastError();
		snprintf(gOwsSocketSetupError, sizeof(gOwsSocketSetupError),
		         "socket() failed for %s:%u (%d: %s)",
		         shost->Host,
		         (unsigned int)shost->port,
		         sockErr,
		         OwsSocketErrorString(sockErr));
		return 0;
	}

	return 1;
}

int RecvPacketsSSL(SSL *ssl, char *packet, int maxlen)
{
    int x = 0;
    int recvdbytes = 0;

    if (!ssl || !packet || maxlen <= 0)
        return 0;

    memset(packet, 0, maxlen);

    /* Lettura blocchi da SSL_read, fino a esaurimento o buffer pieno */
    for (;;)
    {
        int space = maxlen - x;   /* spazio ancora disponibile nel buffer */

        if (space <= 0)
            break;               /* buffer pieno, stop */

        if (space > 50000)
            space = 50000;       /* leggi al massimo 50k per volta */

        recvdbytes = SSL_read(ssl, packet + x, space);

        if (recvdbytes > 0)
        {
            x += recvdbytes;
            /* continuiamo finché SSL_read restituisce dati
               oppure finché riempiamo il buffer */
            continue;
        }
        else
        {
            /* 0 = connessione chiusa, <0 = errore */
            break;
        }
    }

    return x;
}

unsigned int RecvPacketsDyn(SOCKET *sock, char **packet, unsigned int *cap, unsigned int maxlen)
{
    int x;
    unsigned int recvdbytes = 0;
    unsigned int space;
    unsigned int newCap;
    char *newBuf;

    if (!sock || !packet || !cap || maxlen <= 1)
        return 0;

    if (*packet == NULL || *cap < 2)
    {
        *cap = 262144;
        if (*cap > maxlen + 1)
            *cap = maxlen + 1;
        *packet = (char*)malloc(*cap);
        if (!*packet)
            return 0;
    }

    while (1)
    {
        if (recvdbytes >= maxlen)
            break;

        if (recvdbytes + 1 >= *cap)
        {
            newCap = (*cap) * 2;
            if (newCap > maxlen + 1)
                newCap = maxlen + 1;
            if (newCap <= *cap)
                break;

            newBuf = (char*)realloc(*packet, newCap);
            if (!newBuf)
                break;
            *packet = newBuf;
            *cap = newCap;
        }

        space = *cap - 1 - recvdbytes;
        if (space <= 0)
            break;

        x = recv(*sock, *packet + recvdbytes, (int)space, 0);
        if (x < 0)
        {
            int sockErr = OwsSocketLastError();
            if (
#ifdef WIN32
                sockErr == WSAEINTR
#else
                sockErr == EINTR
#endif
            )
                continue;
            if (
#ifdef WIN32
                sockErr == WSAEWOULDBLOCK
#else
                sockErr == EAGAIN || sockErr == EWOULDBLOCK
#endif
            )
                break;
            break;
        }
        if (x == 0)
            break;

        recvdbytes += x;
    }

    (*packet)[recvdbytes] = '\0';
    return recvdbytes;
}

unsigned int RecvPacketsSSLDyn(SSL *ssl, char **packet, unsigned int *cap, unsigned int maxlen)
{
    int x;
    int sslErr;
    int idleRetries = 0;
    unsigned int recvdbytes = 0;
    unsigned int space;
    unsigned int newCap;
    char *newBuf;

    if (!ssl || !packet || !cap || maxlen <= 1)
        return 0;

    if (*packet == NULL || *cap < 2)
    {
        *cap = 262144;
        if (*cap > maxlen + 1)
            *cap = maxlen + 1;
        *packet = (char*)malloc(*cap);
        if (!*packet)
            return 0;
    }

    while (1)
    {
        if (recvdbytes >= maxlen)
            break;

        if (recvdbytes + 1 >= *cap)
        {
            newCap = (*cap) * 2;
            if (newCap > maxlen + 1)
                newCap = maxlen + 1;
            if (newCap <= *cap)
                break;

            newBuf = (char*)realloc(*packet, newCap);
            if (!newBuf)
                break;
            *packet = newBuf;
            *cap = newCap;
        }

        space = *cap - 1 - recvdbytes;
        if (space <= 0)
            break;

        if (space > 50000)
            space = 50000;

        x = SSL_read(ssl, *packet + recvdbytes, (int)space);
        if (x <= 0)
        {
            sslErr = SSL_get_error(ssl, x);
            if (sslErr == SSL_ERROR_WANT_READ || sslErr == SSL_ERROR_WANT_WRITE)
            {
                if (++idleRetries >= 100)
                    break;
                Sleep(100);
                continue;
            }

            if (sslErr == SSL_ERROR_SYSCALL)
            {
                int sockErr = OwsSocketLastError();
                if (
#ifdef WIN32
                    sockErr == WSAEINTR
#else
                    sockErr == EINTR
#endif
                )
                    continue;
                if (
#ifdef WIN32
                    sockErr == WSAEWOULDBLOCK
#else
                    sockErr == EAGAIN || sockErr == EWOULDBLOCK
#endif
                )
                {
                    if (++idleRetries >= 100)
                        break;
                    Sleep(100);
                    continue;
                }
            }

            break;
        }

        recvdbytes += x;
        idleRetries = 0;
    }

    (*packet)[recvdbytes] = '\0';
    return recvdbytes;
}



/*
int RecvPacketsSSL(SSL *ssl,char* packet, int maxlen)
{
	int x=0;
	int recvdbytes=0;

	memset(packet,0,maxlen);
	recvdbytes=0;

	do
	{
		recvdbytes=SSL_read(ssl,packet+x,50000);

		if(recvdbytes>0)
			x+=recvdbytes;
		else
			return x;

	} while(recvdbytes!=0 && x+50000<maxlen);

	return x;
}
*/


/*OWS Server*/
int ListenToPort(int port, SOCKET* fd)
{
int opt = 1;

	SOCKADDR_IN saddr;
	
	*fd=socket(AF_INET,SOCK_STREAM,0);
#ifdef WIN32
	if (*fd==INVALID_SOCKET)
#else
		if (*fd<0)
#endif
			return -1;

#ifdef WIN32
		setsockopt(*fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
#else
		setsockopt(*fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#endif
		
		saddr.sin_family = AF_INET;
		saddr.sin_addr.s_addr= htonl(INADDR_ANY);
		saddr.sin_port = htons((unsigned short int)port);
		
		if (bind(*fd, (LPSOCKADDR) &saddr, sizeof(saddr)) == SOCKET_ERROR)
			return -2;
		
		if (listen(*fd,100) == SOCKET_ERROR)
			return -3;
		
		return 1;
}


#endif

/*EOF*/
