net7/branches/linux-port/UDPConnection.cpp
2014-05-11 16:54:16 -04:00

364 lines
8.6 KiB
C++

#include "Net7.h"
#include "ServerManager.h"
#include "UDPConnection.h"
#include "PacketStructures.h"
#include "PlayerClass.h"
#include "Opcodes.h"
// This helper function is referenced by _beginthread to launch the TCP thread.
#ifdef WIN32
/*void __cdecl LaunchUDPSendThread(void *arg)
{
((UDP_Connection *) arg)->RunSendThread();
_endthread();
}*/
void __cdecl LaunchUDPRecvThread(void *arg)
{
((UDP_Connection *) arg)->RunRecvThread();
_endthread();
}
#else // Linux
/*void * LaunchUDPSendThread(void *arg)
{
((UDP_Connection *) arg)->RunSendThread();
return NULL;
}*/
void * LaunchUDPRecvThread(void *arg)
{
((UDP_Connection *) arg)->RunRecvThread();
return NULL;
}
#endif
//make a send and receive socket, and start a send and receive thread
UDP_Connection::UDP_Connection(unsigned short port, ServerManager *server_mgr, int server_type)
: m_ServerType(server_type)
{
//m_Send_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
m_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
m_Port = port;
m_ServerMgr = server_mgr;
m_Sector_Operational = true; //false
m_SectorID = 0;
m_LoginAttempts = 0;
m_Socket_Reset_Required = false;
if(m_Socket == INVALID_SOCKET)
{
LogMessage("Invalid Socket %d for UDP connection.\n", GetLastError());
return;
}
UDP_SetBroadcast(m_Socket);
if (!UDP_BindPort(m_Port, m_Socket))
{
m_Socket = INVALID_SOCKET;
return;
}
m_ThreadRunning = false;
// Launch the Receiver thread
#ifdef WIN32
_beginthread(&LaunchUDPRecvThread, 0, this);
#else
pthread_create(&m_Thread, NULL, &LaunchUDPRecvThread, (void *) this);
#endif
}
UDP_Connection::~UDP_Connection()
{
LogMessage("UDP connection terminates\n");
m_ThreadRunning = false;
}
ULONG GetLocalAddr()
{
char localname[MAX_PATH];
ULONG addr = 0;
#ifdef WIN32
if (SOCKET_ERROR != gethostname(localname, MAX_PATH))
#else
if (0 != gethostname(localname, MAX_PATH))
#endif
{
struct hostent* hp;
hp = gethostbyname(localname);
if (hp != NULL)
{
strcpy(localname, hp->h_name);
addr = *((ULONG *) hp->h_addr_list[0]);
}
}
return addr;
}
bool UDP_Connection::UDP_BindPort(short port, SOCKET socket)
{
sockaddr_in localAddr;
ULONG addr;
memset(&localAddr, 0, sizeof(localAddr));
localAddr.sin_family = AF_INET;
addr = GetLocalAddr();
localAddr.sin_addr.s_addr = addr;//INADDR_ANY; //addr
localAddr.sin_port = htons(port);
if (bind(socket, (sockaddr *) &localAddr, sizeof(sockaddr_in)) < 0)
{
LogMessage("Set of local port failed.\n");
return false;
}
return true;
}
unsigned long UDP_Connection::checksum(char *buffer, int size)
{
unsigned long cksum=0;
while(size > 1) { cksum+=*buffer++; size -= 2; }
if(size) cksum += *(UCHAR*)buffer;
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (~cksum);
}
void UDP_Connection::UDP_SetBroadcast(SOCKET socket)
{
int broadcastPermission = 1;
setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&broadcastPermission, sizeof(broadcastPermission));
}
void UDP_Connection::RunRecvThread()
{
long source_addr;
unsigned short source_port;
int received;
EnbUdpHeader *header;
bool fail = false;
if (m_Port <= 0)
LogMessage("UDP_Connection: RunRecvThread - Port not set\n");
if (m_Socket == INVALID_SOCKET)
LogMessage("UDP_Connection: RunRecvThread - Invalid Socket\n");
if (m_ThreadRunning)
LogMessage("UDP_Connection: RunRecvThread - Thread already running\n");
m_ThreadRunning = true;
//LogMessage("Receive thread running.\n");
while (!g_ServerShutdown && m_ThreadRunning)
{
received = UDP_RecvS((char*)m_RecvBuffer, MAX_BUFFER, source_addr, source_port);
if (received != -1)
{
header = (EnbUdpHeader*)m_RecvBuffer;
unsigned short bytes = header->size - sizeof(EnbUdpHeader);
short opcode = header->opcode;
long player_id = header->player_id;
char *msg = (char*)(m_RecvBuffer + sizeof(EnbUdpHeader));
m_RecvBuffer[header->size] = 0;
// Make sure CRC & Bytes match
if (received == (int)(bytes + sizeof(EnbUdpHeader)) )
{
switch (m_ServerType)
{
case CONNECTION_TYPE_MVAS_TO_PROXY:
HandleMVASOpcode(msg, header, source_addr, source_port);
break;
case CONNECTION_TYPE_GLOBAL_SERVER_TO_PROXY:
HandleGlobalOpcode(msg, header, source_addr, source_port);
break;
case CONNECTION_TYPE_SECTOR_SERVER_TO_PROXY:
HandleClientOpcode(msg, header, source_addr, source_port);
break;
case CONNECTION_TYPE_MASTER_SERVER_TO_PROXY:
HandleMasterOpcode(msg, header, source_addr, source_port);
break;
default:
LogMessage("Unknown reception opcode, port %d\n", m_Port);
break;
}
}
}
if (m_Socket_Reset_Required)
{
m_Socket_Reset_Required = false;
LogMessage("WARNING: Socket loss, Attempting to reset socket\n");
Reset_Socket();
}
}
closesocket(m_Socket);
m_Socket = INVALID_SOCKET;
}
void UDP_Connection::Reset_Socket()
{
closesocket(m_Socket);
m_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(m_Socket == INVALID_SOCKET)
{
LogMessage("Socket Reset: Invalid Socket %d for UDP connection.\n", GetLastError());
return;
}
UDP_SetBroadcast(m_Socket);
if (!UDP_BindPort(m_Port, m_Socket))
{
m_Socket = INVALID_SOCKET;
return;
}
}
void UDP_Connection::UDP_Send(const char *buffer, int bufferLen, long IPaddr, short port)
{
sockaddr_in lSockAddr;
memset(&lSockAddr, 0, sizeof(lSockAddr));
lSockAddr.sin_family = AF_INET;
lSockAddr.sin_addr.s_addr = IPaddr;
lSockAddr.sin_port = htons(port);
unsigned char *ip = (unsigned char *) &IPaddr;
LogDebug("Sending signal to %d (%d.%d.%d.%d)\n",IPaddr, ip[0], ip[1], ip[2], ip[3]);
// Write out the whole buffer as a single message.
if (sendto(m_Socket, buffer, bufferLen, 0,
(sockaddr *) &lSockAddr, sizeof(lSockAddr)) != bufferLen)
{
LogMessage("Send failed.\n");
}
}
void UDP_Connection::SetServerManager(ServerManager * server_mgr)
{
m_ServerMgr = server_mgr;
}
int UDP_Connection::UDP_RecvS(char *buffer, int size, long &source_addr, unsigned short &source_port)
{
sockaddr_in clntAddr;
int rtn;
#ifdef WIN32
int addrLen = sizeof(clntAddr);
if ((rtn = recvfrom(m_Socket, buffer, size, 0, (sockaddr *) &clntAddr, (int *) &addrLen)) < 0)
{
Sleep(200);
DWORD dwError = WSAGetLastError();
if (dwError == WSAENOTSOCK)
{
//return to thread and perform a socket reset, see if this helps
m_Socket_Reset_Required = true;
}
//LogMessage("Receive failed.\n");
}
#else
socklen_t addrLen = sizeof(clntAddr);
if ((rtn = recvfrom(m_Socket, buffer, size, 0, (sockaddr *) &clntAddr, &addrLen)) < 0)
{
Sleep(200);
DWORD dwError = WSAGetLastError();
if (dwError == ENOTSOCK)
{
//return to thread and perform a socket reset, see if this helps
m_Socket_Reset_Required = true;
}
//LogMessage("Receive failed.\n");
}
#endif
source_addr = clntAddr.sin_addr.s_addr;
source_port = ntohs(clntAddr.sin_port);
return rtn;
}
void UDP_Connection::SendOpcode(short opcode, unsigned char *data, size_t length, long player_ip, short port)
{
unsigned char buffer[2060];
EnbUdpHeader * header = (EnbUdpHeader *) &buffer[0];
header->size = (short) length + sizeof(EnbUdpHeader);
header->opcode = opcode;
header->player_id = 0;
header->packet_sequence = 0;
if ((length + sizeof(EnbUdpHeader)) > 2060)
{
LogMessage("UDP send exceeds stack allocation! DEBUG ME: size = %d\n", (length + sizeof(EnbUdpHeader)));
}
else
{
if (length)
{
memcpy(buffer + sizeof(EnbUdpHeader), data, length);
}
}
int bytes = length + sizeof(EnbUdpHeader);
UDP_Send((const char *) buffer, bytes, player_ip, port);
}
void UDP_Connection::SendOpcode(short opcode, Player *p, unsigned char *data, size_t length, long player_ip, short port, long sequence_num)
{
unsigned char *buffer = p->GetUDPBuffer();
EnbUdpHeader * header = (EnbUdpHeader *) &buffer[0];
header->size = (short) length + sizeof(EnbUdpHeader);
header->opcode = opcode;
header->player_id = p->GameID();
header->packet_sequence = sequence_num;
if ((length + sizeof(EnbUdpHeader)) > UDP_BUFFER_SEND_SIZE)
{
LogMessage("UDP send exceeds player buffer allocation! DEBUG ME: size = %d", (length + sizeof(EnbUdpHeader)));
}
else
{
if (length)
{
memcpy(buffer + sizeof(EnbUdpHeader), data, length);
}
}
int bytes = length + sizeof(EnbUdpHeader);
//LogMessage("Opcode: %04x, Length: %x\n", opcode, length);
//DumpBuffer(buffer, bytes);
UDP_Send((const char *) buffer, bytes, player_ip, port);
}
void UDP_Connection::Shutdown()
{
closesocket(m_Socket);
m_Socket = INVALID_SOCKET;
}