/************************************************************************/
/* BattlEye Server code                                                 */
/************************************************************************/

#include "../server/server.h"

#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>

extern qboolean	Sys_StringToSockaddr(char *s, struct sockaddr *sadr);

#ifdef BATTLEYE

static int mastersocket = -1;


qboolean SV_BE_ConnectToMaster(void)
{
	qboolean _true = qtrue;
	struct sockaddr masteraddress;

	SV_BE_DisconnectFromMaster();

	// create a TCP socket
	if ((mastersocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
	{
		Com_Printf("ERROR: SV_BE_ConnectToMaster: socket: %s\n", NET_ErrorString());
		return qfalse;
	}

	// make it non-blocking
	if (ioctl(mastersocket, FIONBIO, &_true) == -1)
	{
		Com_Printf("ERROR: SV_BE_ConnectToMaster: ioctl FIONBIO: %s\n", NET_ErrorString());
		return qfalse;
	}

	// resolve BE Master DNS host name
	if (!Sys_StringToSockaddr(BATTLEYE_MASTERSERVER, &masteraddress))
	{
		Com_Printf("ERROR: SV_BE_ConnectToMaster: Sys_StringToSockaddr: %s\n", hstrerror(h_errno));
		return qfalse;
	}

	if (connect(mastersocket, &masteraddress, sizeof(masteraddress)) == -1
		&& errno != EINPROGRESS)
	{
		Com_Printf("ERROR: SV_BE_ConnectToMaster: connect: %s\n", NET_ErrorString());
		return qfalse;
	}

	return qtrue;
}

int SV_BE_CheckConnectAttempt(void)
{
	int result;
	struct timeval timeout = { 0, 0 };
	fd_set set;
	FD_ZERO(&set);
	FD_SET(mastersocket, &set);

	if ((result = select(mastersocket+1, NULL, &set, NULL, &timeout)) == -1)
	{
		Com_Printf("ERROR: SV_BE_CheckConnectAttempt: select: %s\n", NET_ErrorString());
		return -1;
	}
	else if (result)
	{
		struct sockaddr addr;
		socklen_t len;

		if( !FD_ISSET(mastersocket, &set) ) {
			Com_Printf("ERROR: SV_BE_CheckConnectAttempt: write fd not set\n");
			return -1;
		}
		// trick to check if we actually got connection succesfully
		// idea from http://cr.yp.to/docs/connect.html
		if( getpeername(mastersocket, &addr, &len) != 0 ) {
			char ch;
			read(mastersocket, &ch, 1);
			return -1;
		}
		return 1;
	}
	else
		return 0;
}

void SV_BE_SendToMaster(void* buf, int len)
{
#if ( defined(__FreeBSD__) && (__FreeBSD_version < 600020) )
	int opt_val = 1;
	// Currently ignore the return code from setsockopt
	// Disable SIGPIPE
	setsockopt( mastersocket, SOL_SOCKET, SO_NOSIGPIPE, &opt_val, sizeof( opt_val ) );
	send(mastersocket, buf, len, 0);
	opt_val = 0;
	// Enable SIGPIPE
	setsockopt( mastersocket, SOL_SOCKET, SO_NOSIGPIPE, &opt_val, sizeof( opt_val ) );
#else
	send(mastersocket, buf, len, MSG_NOSIGNAL);
#endif
}

int SV_BE_ReceiveFromMaster(void* buf, int len)
{
	int result = recv(mastersocket, buf, len, 0);

	if (result == -1 && errno == EWOULDBLOCK)
		return 0;
	else if (result == -1 || result == 0 || result != len)
		return -1;
	else
		return 1;
}

void SV_BE_DisconnectFromMaster(void)
{
	if (mastersocket != -1)
	{
		close(mastersocket);
		mastersocket = -1;
	}
}

#endif // BATTLEYE
