DarkflameServer/thirdparty/raknet/Source/TCPInterface.cpp

756 lines
19 KiB
C++
Raw Permalink Normal View History

/// \file
/// \brief A simple TCP based server allowing sends and receives. Can be connected to by a telnet client.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to 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.
#include "TCPInterface.h"
#ifdef _WIN32
#else
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
#endif
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include "RakAssert.h"
#include "RakSleep.h"
#ifdef _DO_PRINTF
#endif
#ifdef _WIN32
#elif defined(_PS3)
#define closesocket socketclose
#else
#define closesocket close
#include <unistd.h>
#endif
RAK_THREAD_DECLARATION(UpdateTCPInterfaceLoop);
RAK_THREAD_DECLARATION(ConnectionAttemptLoop);
#ifdef _MSC_VER
#pragma warning( push )
#endif
TCPInterface::TCPInterface()
{
isStarted=false;
threadRunning=false;
listenSocket=(SOCKET) -1;
#if defined(OPEN_SSL_CLIENT_SUPPORT)
ctx=0;
meth=0;
#endif
#ifdef _WIN32
WSADATA winsockInfo;
if ( WSAStartup( MAKEWORD( 2, 2 ), &winsockInfo ) != 0 )
{
#if defined(_WIN32) && !defined(_XBOX360) && defined(_DEBUG)
DWORD dwIOError = GetLastError();
LPVOID messageBuffer;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language
( LPTSTR ) & messageBuffer, 0, NULL );
// something has gone wrong here...
printf( "WSAStartup failed:Error code - %d\n%s", dwIOError, messageBuffer );
//Free the buffer.
LocalFree( messageBuffer );
#endif
}
#endif
}
TCPInterface::~TCPInterface()
{
Stop();
}
bool TCPInterface::Start(unsigned short port, unsigned short maxIncomingConnections)
{
if (isStarted)
return false;
isStarted=true;
if (maxIncomingConnections>0)
{
listenSocket = socket(AF_INET, SOCK_STREAM, 0);
if ((int)listenSocket ==-1)
return false;
struct sockaddr_in serverAddress;
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddress.sin_port = htons(port);
if (bind(listenSocket,(struct sockaddr *) &serverAddress,sizeof(serverAddress)) < 0)
return false;
listen(listenSocket, maxIncomingConnections);
}
// Start the update thread
int errorCode = RakNet::RakThread::Create(UpdateTCPInterfaceLoop, this);
if (errorCode!=0)
return false;
return true;
}
void TCPInterface::Stop(void)
{
if (isStarted==false)
return;
unsigned i;
#if defined(OPEN_SSL_CLIENT_SUPPORT)
for (i=0; i < remoteClients.Size(); i++)
remoteClients[i]->DisconnectSSL();
#endif
isStarted=false;
if (listenSocket!=(SOCKET) -1)
{
#ifdef _WIN32
shutdown(listenSocket, SD_BOTH);
#else
shutdown(listenSocket, SHUT_RDWR);
#endif
closesocket(listenSocket);
listenSocket=(SOCKET) -1;
}
// Abort waiting connect calls
blockingSocketListMutex.Lock();
for (i=0; i < blockingSocketList.Size(); i++)
{
closesocket(blockingSocketList[i]);
}
blockingSocketListMutex.Unlock();
// Wait for the thread to stop
while ( threadRunning )
RakSleep(15);
RakSleep(100);
// Stuff from here on to the end of the function is not threadsafe
for (i=0; i < remoteClients.Size(); i++)
{
closesocket(remoteClients[i]->socket);
#if defined(OPEN_SSL_CLIENT_SUPPORT)
remoteClients[i]->FreeSSL();
#endif
delete remoteClients[i];
}
remoteClients.Clear();
for (i=0; i < remoteClientsInsertionQueue.Size(); i++)
{
closesocket(remoteClientsInsertionQueue[i]->socket);
delete remoteClientsInsertionQueue[i];
}
remoteClientsInsertionQueue.Clear();
outgoingMessages.Clear();
incomingMessages.Clear();
newConnections.Clear();
newRemoteClients.Clear();
lostConnections.Clear();
requestedCloseConnections.Clear();
failedConnectionAttempts.Clear();
completedConnectionAttempts.Clear();
failedConnectionAttempts.Clear();
#if defined(OPEN_SSL_CLIENT_SUPPORT)
SSL_CTX_free (ctx);
startSSL.Clear();
activeSSLConnections.Clear();
#endif
}
SystemAddress TCPInterface::Connect(const char* host, unsigned short remotePort, bool block)
{
if (block)
{
SOCKET sockfd = SocketConnect(host, remotePort);
if (sockfd==(SOCKET)-1)
return UNASSIGNED_SYSTEM_ADDRESS;
RemoteClient *remoteClient;
remoteClient = new RemoteClient;
remoteClient->socket=sockfd;
remoteClient->systemAddress.binaryAddress=inet_addr(host);
remoteClient->systemAddress.port=remotePort;
InsertRemoteClient(remoteClient);
return remoteClient->systemAddress;
}
else
{
ThisPtrPlusSysAddr *s = new ThisPtrPlusSysAddr;
s->systemAddress.SetBinaryAddress(host);
s->systemAddress.port=remotePort;
s->tcpInterface=this;
// Start the connection thread
int errorCode = RakNet::RakThread::Create(ConnectionAttemptLoop, s);
if (errorCode!=0)
{
delete s;
failedConnectionAttempts.Push(s->systemAddress);
}
return UNASSIGNED_SYSTEM_ADDRESS;
}
}
#if defined(OPEN_SSL_CLIENT_SUPPORT)
void TCPInterface::StartSSLClient(SystemAddress systemAddress)
{
if (ctx==0)
{
SSLeay_add_ssl_algorithms();
meth = SSLv2_client_method();
SSL_load_error_strings();
ctx = SSL_CTX_new (meth);
RakAssert(ctx!=0);
}
SystemAddress *id = startSSL.WriteLock();
*id=systemAddress;
startSSL.WriteUnlock();
unsigned index = activeSSLConnections.GetIndexOf(systemAddress);
if (index==(unsigned)-1)
activeSSLConnections.Insert(systemAddress);
}
bool TCPInterface::IsSSLActive(SystemAddress systemAddress)
{
return activeSSLConnections.GetIndexOf(systemAddress)!=-1;
}
#endif
void TCPInterface::Send( const char *data, unsigned length, SystemAddress systemAddress )
{
if (isStarted==false)
return;
if (remoteClients.Size()==0)
return;
if (data==0)
return;
Packet *p=outgoingMessages.WriteLock();
p->length=length;
p->data = (unsigned char*) rakMalloc( p->length );
memcpy(p->data, data, p->length);
p->systemAddress=systemAddress;
outgoingMessages.WriteUnlock();
}
Packet* TCPInterface::Receive( void )
{
if (isStarted==false)
return 0;
return incomingMessages.ReadLock();
}
void TCPInterface::CloseConnection( SystemAddress systemAddress )
{
if (isStarted==false)
return;
if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS)
return;
SystemAddress *id = requestedCloseConnections.WriteLock();
*id=systemAddress;
requestedCloseConnections.WriteUnlock();
#if defined(OPEN_SSL_CLIENT_SUPPORT)
unsigned index = activeSSLConnections.GetIndexOf(systemAddress);
if (index!=(unsigned)-1)
activeSSLConnections.RemoveAtIndex(index);
#endif
}
void TCPInterface::DeallocatePacket( Packet *packet )
{
if (packet==0)
return;
assert(incomingMessages.CheckReadUnlockOrder(packet));
rakFree(packet->data);
incomingMessages.ReadUnlock();
}
SystemAddress TCPInterface::HasCompletedConnectionAttempt(void)
{
SystemAddress sysAddr=UNASSIGNED_SYSTEM_ADDRESS;
completedConnectionAttemptMutex.Lock();
if (completedConnectionAttempts.IsEmpty()==false)
sysAddr=completedConnectionAttempts.Pop();
completedConnectionAttemptMutex.Unlock();
return sysAddr;
}
SystemAddress TCPInterface::HasFailedConnectionAttempt(void)
{
SystemAddress sysAddr=UNASSIGNED_SYSTEM_ADDRESS;
failedConnectionAttemptMutex.Lock();
if (failedConnectionAttempts.IsEmpty()==false)
sysAddr=failedConnectionAttempts.Pop();
failedConnectionAttemptMutex.Unlock();
return sysAddr;
}
SystemAddress TCPInterface::HasNewConnection(void)
{
SystemAddress *out;
out = newConnections.ReadLock();
if (out)
{
newConnections.ReadUnlock();
return *out;
}
else
{
return UNASSIGNED_SYSTEM_ADDRESS;
}
}
SystemAddress TCPInterface::HasLostConnection(void)
{
SystemAddress *out;
out = lostConnections.ReadLock();
if (out)
{
lostConnections.ReadUnlock();
return *out;
}
else
{
return UNASSIGNED_SYSTEM_ADDRESS;
}
}
void TCPInterface::DeleteRemoteClient(RemoteClient *remoteClient, fd_set *exceptionFD)
{
(void) exceptionFD;
#if defined(OPEN_SSL_CLIENT_SUPPORT)
remoteClient->DisconnectSSL();
remoteClient->FreeSSL();
#endif
// FD_CLR(remoteClient->socket, exceptionFD);
closesocket(remoteClient->socket);
//shutdown(remoteClient->socket, SD_SEND);
delete remoteClient;
}
void TCPInterface::InsertRemoteClient(RemoteClient* remoteClient)
{
remoteClientsInsertionQueueMutex.Lock();
remoteClientsInsertionQueue.Push(remoteClient);
remoteClientsInsertionQueueMutex.Unlock();
}
SOCKET TCPInterface::SocketConnect(const char* host, unsigned short remotePort)
{
sockaddr_in serverAddress;
#if !defined(_XBOX360)
struct hostent * server;
server = gethostbyname(host);
if (server == NULL)
return (SOCKET) -1;
#endif
SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
return (SOCKET) -1;
memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons( remotePort );
#if !defined(_XBOX360)
memcpy((char *)&serverAddress.sin_addr.s_addr, (char *)server->h_addr, server->h_length);
#else
serverAddress.sin_addr.s_addr = inet_addr( host );
#endif
blockingSocketListMutex.Lock();
blockingSocketList.Insert(sockfd);
blockingSocketListMutex.Unlock();
// This is blocking
int connectResult = connect( sockfd, ( struct sockaddr * ) &serverAddress, sizeof( struct sockaddr ) );
unsigned sockfdIndex;
blockingSocketListMutex.Lock();
sockfdIndex=blockingSocketList.GetIndexOf(sockfd);
if (sockfdIndex!=(unsigned)-1)
blockingSocketList.RemoveAtIndexFast(sockfdIndex);
blockingSocketListMutex.Unlock();
if (connectResult==-1)
{
closesocket(sockfd);
return (SOCKET) -1;
}
return sockfd;
}
RAK_THREAD_DECLARATION(ConnectionAttemptLoop)
{
TCPInterface::ThisPtrPlusSysAddr *s = (TCPInterface::ThisPtrPlusSysAddr *) arguments;
SystemAddress systemAddress = s->systemAddress;
TCPInterface *tcpInterface = s->tcpInterface;
delete s;
SOCKET sockfd = tcpInterface->SocketConnect(systemAddress.ToString(false), systemAddress.port);
if (sockfd==(SOCKET)-1)
{
tcpInterface->failedConnectionAttemptMutex.Lock();
tcpInterface->failedConnectionAttempts.Push(systemAddress);
tcpInterface->failedConnectionAttemptMutex.Unlock();
return 0;
}
RemoteClient *remoteClient;
remoteClient = new RemoteClient;
remoteClient->socket=sockfd;
remoteClient->systemAddress=systemAddress;
tcpInterface->InsertRemoteClient(remoteClient);
// Wait for the other thread to pick up the remote client
bool isEmpty;
do
{
RakSleep(30);
tcpInterface->remoteClientsInsertionQueueMutex.Lock();
isEmpty=tcpInterface->remoteClientsInsertionQueue.IsEmpty();
tcpInterface->remoteClientsInsertionQueueMutex.Unlock();
} while(isEmpty==false && tcpInterface->threadRunning);
// Notify user that the connection attempt has completed.
if (tcpInterface->threadRunning)
{
tcpInterface->completedConnectionAttemptMutex.Lock();
tcpInterface->completedConnectionAttempts.Push(systemAddress);
tcpInterface->completedConnectionAttemptMutex.Unlock();
}
return 0;
}
RAK_THREAD_DECLARATION(UpdateTCPInterfaceLoop)
{
TCPInterface * sts = ( TCPInterface * ) arguments;
RemoteClient *remoteClient;
const int BUFF_SIZE=8096;
char data[ BUFF_SIZE ];
Packet *p;
SystemAddress *systemAddress;
fd_set readFD, exceptionFD;
sts->threadRunning=true;
sockaddr_in sockAddr;
int sockAddrSize = sizeof(sockAddr);
unsigned i;
int len;
SOCKET newSock;
timeval tv;
int selectResult;
tv.tv_sec=0;
tv.tv_usec=25000;
while (sts->isStarted)
{
#if defined(OPEN_SSL_CLIENT_SUPPORT)
systemAddress = sts->startSSL.ReadLock();
if (systemAddress)
{
for (i=0; i < sts->remoteClients.Size(); i++)
{
if (sts->remoteClients[i]->systemAddress==*systemAddress)
{
if (sts->remoteClients[i]->ssl==0)
sts->remoteClients[i]->InitSSL(sts->ctx,sts->meth);
}
}
sts->startSSL.ReadUnlock();
}
#endif
p=sts->outgoingMessages.ReadLock();
while (p)
{
if (p->systemAddress==UNASSIGNED_SYSTEM_ADDRESS)
{
// Send to all
for (i=0; i < sts->remoteClients.Size(); i++)
sts->remoteClients[i]->Send((const char*)p->data, p->length);
}
else
{
// Send to this player
for (i=0; i < sts->remoteClients.Size(); i++)
if (sts->remoteClients[i]->systemAddress==p->systemAddress )
sts->remoteClients[i]->Send((const char*)p->data, p->length);
}
rakFree(p->data);
sts->outgoingMessages.ReadUnlock();
p=sts->outgoingMessages.ReadLock();
}
if (sts->remoteClientsInsertionQueue.IsEmpty()==false)
{
sts->remoteClientsInsertionQueueMutex.Lock();
if (sts->remoteClientsInsertionQueue.IsEmpty()==false)
sts->remoteClients.Insert(sts->remoteClientsInsertionQueue.Pop());
sts->remoteClientsInsertionQueueMutex.Unlock();
}
systemAddress=sts->requestedCloseConnections.ReadLock();
if (systemAddress)
{
for (i=0; i < sts->remoteClients.Size(); i++)
{
if (sts->remoteClients[i]->systemAddress==*systemAddress )
{
systemAddress=sts->lostConnections.WriteLock();
*systemAddress=sts->remoteClients[i]->systemAddress;
sts->lostConnections.WriteUnlock();
sts->DeleteRemoteClient(sts->remoteClients[i], &exceptionFD);
sts->remoteClients.RemoveAtIndex(i);
/*
systemAddress=sts->lostConnections.WriteLock();
*systemAddress=sts->remoteClients[i]->systemAddress;
sts->lostConnections.WriteUnlock();
sts->remoteClients.Del(i);
*/
break;
}
}
sts->requestedCloseConnections.ReadUnlock();
}
SOCKET largestDescriptor=0; // see select()'s first parameter's documentation under linux
// Reset readFD and exceptionFD since select seems to clear it
FD_ZERO(&readFD);
FD_ZERO(&exceptionFD);
#ifdef _MSC_VER
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
#endif
if (sts->listenSocket!=(SOCKET) -1)
{
FD_SET(sts->listenSocket, &readFD);
FD_SET(sts->listenSocket, &exceptionFD);
largestDescriptor = sts->listenSocket; // @see largestDescriptor def
}
for (i=0; i < sts->remoteClients.Size(); i++)
{
FD_SET(sts->remoteClients[i]->socket, &readFD);
FD_SET(sts->remoteClients[i]->socket, &exceptionFD);
if(sts->remoteClients[i]->socket > largestDescriptor) // @see largestDescriptorDef
largestDescriptor = sts->remoteClients[i]->socket;
}
// Linux' select() implementation changes the timeout
tv.tv_sec=0;
tv.tv_usec=25000;
#ifdef _MSC_VER
#pragma warning( disable : 4244 ) // warning C4127: conditional expression is constant
#endif
#if defined(_PS3)
selectResult=socketselect(largestDescriptor+1, &readFD, 0, &exceptionFD, &tv);
#else
selectResult=(int) select(largestDescriptor+1, &readFD, 0, &exceptionFD, &tv);
#endif
//selectResult=select(largestDescriptor+1, &readFD, 0, &exceptionFD, &tv);
//selectResult=select(0, &readFD, 0, &exceptionFD, &tv);
if (selectResult > 0)
{
if (sts->listenSocket!=(SOCKET) -1 && FD_ISSET(sts->listenSocket, &readFD))
{
newSock = accept(sts->listenSocket, (sockaddr*)&sockAddr, (socklen_t*)&sockAddrSize);
if (newSock != (SOCKET) -1)
{
remoteClient = new RemoteClient;
remoteClient->socket=newSock;
remoteClient->systemAddress.binaryAddress=sockAddr.sin_addr.s_addr;
remoteClient->systemAddress.port=ntohs( sockAddr.sin_port);
sts->remoteClients.Insert(remoteClient);
systemAddress=sts->newConnections.WriteLock();
*systemAddress=remoteClient->systemAddress;
sts->newConnections.WriteUnlock();
FD_SET(newSock, &readFD);
FD_SET(newSock, &exceptionFD);
}
else
{
#ifdef _DO_PRINTF
printf("Error: connection failed\n");
#endif
}
}
else if (sts->listenSocket!=(SOCKET) -1 && FD_ISSET(sts->listenSocket, &exceptionFD))
{
#ifdef _DO_PRINTF
int err;
int errlen = sizeof(err);
getsockopt(sts->listenSocket, SOL_SOCKET, SO_ERROR,(char*)&err, &errlen);
printf("Socket error %s on listening socket\n", err);
#endif
}
else
{
i=0;
while (i < sts->remoteClients.Size())
{
if (FD_ISSET(sts->remoteClients[i]->socket, &exceptionFD))
{
#ifdef _DO_PRINTF
if (sts->listenSocket!=-1)
{
int err;
int errlen = sizeof(err);
getsockopt(sts->listenSocket, SOL_SOCKET, SO_ERROR,(char*)&err, &errlen);
in_addr in;
in.s_addr = sts->remoteClients[i]->systemAddress.binaryAddress;
printf("Socket error %i on %s:%i\n", err,inet_ntoa( in ), sts->remoteClients[i]->systemAddress.port );
}
#endif
// Connection lost abruptly
systemAddress=sts->lostConnections.WriteLock();
*systemAddress=sts->remoteClients[i]->systemAddress;
sts->lostConnections.WriteUnlock();
sts->DeleteRemoteClient(sts->remoteClients[i], &exceptionFD);
sts->remoteClients.RemoveAtIndex(i);
}
else
{
if (FD_ISSET(sts->remoteClients[i]->socket, &readFD))
{
// if recv returns 0 this was a graceful close
len = sts->remoteClients[i]->Recv(data,BUFF_SIZE);
if (len>0)
{
p=sts->incomingMessages.WriteLock();
p->data = (unsigned char*) rakMalloc( len+1 );
memcpy(p->data, data, len);
p->data[len]=0; // Null terminate this so we can print it out as regular strings. This is different from RakNet which does not do this.
p->length=len;
p->systemAddress=sts->remoteClients[i]->systemAddress;
sts->incomingMessages.WriteUnlock();
i++; // Nothing deleted so increment the index
}
else
{
// Connection lost gracefully
systemAddress=sts->lostConnections.WriteLock();
*systemAddress=sts->remoteClients[i]->systemAddress;
sts->lostConnections.WriteUnlock();
sts->DeleteRemoteClient(sts->remoteClients[i], &exceptionFD);
sts->remoteClients.RemoveAtIndex(i);
}
}
else
i++; // Nothing deleted so increment the index
}
}
}
}
else if (selectResult==0)
{
// No input - sleep for a while
RakSleep(50);
}
else
{
// FD_CLOSE? closesocket(remoteClient->socket);
#if defined(_DO_PRINTF) && defined(_WIN32)
DWORD dwIOError = WSAGetLastError();
printf("Socket error %i\n", dwIOError);
#endif
}
}
sts->threadRunning=false;
return 0;
}
#if defined(OPEN_SSL_CLIENT_SUPPORT)
void RemoteClient::InitSSL(SSL_CTX* ctx, SSL_METHOD *meth)
{
(void) meth;
ssl = SSL_new (ctx);
RakAssert(ssl);
SSL_set_fd (ssl, socket);
SSL_connect (ssl);
}
void RemoteClient::DisconnectSSL(void)
{
if (ssl)
SSL_shutdown (ssl); /* send SSL/TLS close_notify */
}
void RemoteClient::FreeSSL(void)
{
if (ssl)
SSL_free (ssl);
}
void RemoteClient::Send(const char *data, unsigned int length)
{
int err;
if (ssl)
{
err = SSL_write (ssl, data, length);
RakAssert(err>0);
}
else
send(socket, data, length, 0);
}
int RemoteClient::Recv(char *data, const int dataSize)
{
if (ssl)
{
return SSL_read (ssl, data, dataSize);
}
else
return recv(socket, data, dataSize, 0);
}
#else
void RemoteClient::Send(const char *data, unsigned int length)
{
send(socket, data, length, 0);
}
int RemoteClient::Recv(char *data, const int dataSize)
{
return recv(socket, data, dataSize, 0);
}
#endif
#ifdef _MSC_VER
#pragma warning( pop )
#endif