mirror of
https://github.com/therealKyp/Earth-and-Beyond-server
synced 2026-08-13 22:23:03 -04:00
364 lines
8.6 KiB
C++
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;
|
|
}
|