/*
 * This code is part of Screws project.
 * Copylefted by pancake@phreaker.net at 2004
 */

#include "main.h" 

bool screws_socket_isSSL(int s)
{
#if USESSL
	if (s>9999) // XXX invalid ssl-detection method :)
		return true;
#endif
	return false;
}

int screws_socket_new(family)
	int family;
{
	int s;
	int err;

	s=socket(family,SOCK_STREAM,0);
	if (s==-1) return -1;

	// Default Socket options
   setsockopt(s,SOL_SOCKET, SO_REUSEADDR,&err,sizeof(err));
   #ifdef SO_REUSEPORT
	err=1;
   setsockopt(s,SOL_SOCKET, SO_REUSEPORT,&err,sizeof(err));
   #endif
   #ifdef SO_PRIORITY
	err=1;
   setsockopt(s,SOL_SOCKET, SO_PRIORITY,&err,sizeof(err));
   #endif
	err=0;
//	setsockopt(s,SOL_SOCKET, SO_KEEPALIVE,&err,sizeof(err));

	return s;
}

bool screws_socket_listen(family,ip,port) // TODO catch errors (...)
	int family,port;
	const char *ip;
{
	int s;
	#if IPV6
   struct sockaddr_in6 servaddr6;
	#endif
   struct sockaddr_in servaddr;
   struct hostent *he;

	s=screws_socket_new(family);

	#if IPV6
   switch(family)
   {
   case AF_INET:
	#endif
      memset(&servaddr,0,sizeof(servaddr));
      servaddr.sin_family  = family;
      he=(struct hostent *)gethostbyname(ip);
      if (he==(struct hostent*)0)
			return -1;
   //      exit( Error(Svr.Lis[n].name,ERROR_GETHOST) );
      memcpy((char*)&servaddr.sin_addr,he->h_addr,he->h_length);
      servaddr.sin_port = htons(port);
      if ( (bind(s, (struct sockaddr*)&servaddr,
                  sizeof(servaddr)))<0 )
			return -1;
         //exit( Error(Svr.Lis[n].name,ERROR_BIND) );
	#if IPV6
      break;
   case AF_INET6:
      bzero((char *)&servaddr6,sizeof(struct sockaddr_in6));
      servaddr6.sin6_family=AF_INET6;
      servaddr6.sin6_port=htons(port);
      servaddr6.sin6_addr=(struct in6_addr) in6addr_any;
      if ( (bind(s, (struct sockaddr*)&servaddr6,
                  sizeof(servaddr6)))<0 )
			return -1;
         //exit( Error(Svr.Lis[n].name,ERROR_BIND) );
      break;
   }
	#endif

	if ( listen(s, Svr.backlog) < 0 ) 
		return -1;
		//exit( Error(Svr.Lis[n].name,ERROR_LISTEN) );

	return s;
}

bool screws_send(s,b,i)
	int s,i;
	char *b;
{
	int ret;

	do
	{
	#if USESSL
		if (screws_socket_isSSL(s))
			ret=SSL_write((SSL*)s,b,i);
		else 
	#endif
			ret=send(s,b,i,0);

		if (ret==-1)
		{
			if (errno!=EAGAIN)
				return false;
			ret=0;
			usleep(100); // BSD tip
		}

		b=b+ret;
		i-=ret;
	} while(i>0);

	return true;
}

void screws_socket_close(int s)
{
	shutdown(s,SHUT_RDWR);
	close(s);
}

int screws_socket_recv(int s,char *buf,long len) // XXX len!=long
{
	int ret;

	#if USESSL
	if ( screws_socket_isSSL(s) )
		ret=SSL_read((SSL*)s,buf,len);
	else
	#endif
		ret=recv(s,buf,len,0);

	return ret;
}
