mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-16 22:58:34 +00:00
TCP cleanup, added basis of web interface
This commit is contained in:
@@ -16,8 +16,6 @@ SET(common_sources
|
||||
emu_legacy.cpp
|
||||
emu_limits.cpp
|
||||
emu_opcodes.cpp
|
||||
emu_tcp_connection.cpp
|
||||
emu_tcp_server.cpp
|
||||
emu_versions.cpp
|
||||
eqdb.cpp
|
||||
eqdb_res.cpp
|
||||
@@ -67,12 +65,9 @@ SET(common_sources
|
||||
spdat.cpp
|
||||
string_util.cpp
|
||||
struct_strategy.cpp
|
||||
tcp_connection.cpp
|
||||
tcp_server.cpp
|
||||
textures.cpp
|
||||
timer.cpp
|
||||
unix.cpp
|
||||
worldconn.cpp
|
||||
xml_parser.cpp
|
||||
platform.cpp
|
||||
event/event_loop.cpp
|
||||
@@ -100,17 +95,6 @@ SET(common_sources
|
||||
patches/titanium_limits.cpp
|
||||
patches/uf.cpp
|
||||
patches/uf_limits.cpp
|
||||
SocketLib/Base64.cpp
|
||||
SocketLib/File.cpp
|
||||
SocketLib/HttpdCookies.cpp
|
||||
SocketLib/HttpdForm.cpp
|
||||
SocketLib/HttpdSocket.cpp
|
||||
SocketLib/HTTPSocket.cpp
|
||||
SocketLib/MemFile.cpp
|
||||
SocketLib/Mime.cpp
|
||||
SocketLib/Parse.cpp
|
||||
SocketLib/socket_include.cpp
|
||||
SocketLib/Utility.cpp
|
||||
StackWalker/StackWalker.cpp
|
||||
tinyxml/tinystr.cpp
|
||||
tinyxml/tinyxml.cpp
|
||||
@@ -138,8 +122,6 @@ SET(common_headers
|
||||
emu_limits.h
|
||||
emu_opcodes.h
|
||||
emu_oplist.h
|
||||
emu_tcp_connection.h
|
||||
emu_tcp_server.h
|
||||
emu_versions.h
|
||||
eq_constants.h
|
||||
eq_packet_structs.h
|
||||
@@ -210,16 +192,12 @@ SET(common_headers
|
||||
spdat.h
|
||||
string_util.h
|
||||
struct_strategy.h
|
||||
tcp_basic_server.h
|
||||
tcp_connection.h
|
||||
tcp_server.h
|
||||
textures.h
|
||||
timer.h
|
||||
types.h
|
||||
unix.h
|
||||
useperl.h
|
||||
version.h
|
||||
worldconn.h
|
||||
xml_parser.h
|
||||
zone_numbers.h
|
||||
event/background_task.h
|
||||
@@ -269,18 +247,6 @@ SET(common_headers
|
||||
patches/uf_limits.h
|
||||
patches/uf_ops.h
|
||||
patches/uf_structs.h
|
||||
SocketLib/Base64.h
|
||||
SocketLib/File.h
|
||||
SocketLib/HttpdCookies.h
|
||||
SocketLib/HttpdForm.h
|
||||
SocketLib/HttpdSocket.h
|
||||
SocketLib/HTTPSocket.h
|
||||
SocketLib/IFile.h
|
||||
SocketLib/MemFile.h
|
||||
SocketLib/Mime.h
|
||||
SocketLib/Parse.h
|
||||
SocketLib/socket_include.h
|
||||
SocketLib/Utility.h
|
||||
StackWalker/StackWalker.h
|
||||
tinyxml/tinystr.h
|
||||
tinyxml/tinyxml.h
|
||||
@@ -374,32 +340,6 @@ SOURCE_GROUP(Patches FILES
|
||||
patches/uf_limits.cpp
|
||||
)
|
||||
|
||||
SOURCE_GROUP(SocketLib FILES
|
||||
SocketLib/Base64.h
|
||||
SocketLib/File.h
|
||||
SocketLib/HttpdCookies.h
|
||||
SocketLib/HttpdForm.h
|
||||
SocketLib/HttpdSocket.h
|
||||
SocketLib/HTTPSocket.h
|
||||
SocketLib/IFile.h
|
||||
SocketLib/MemFile.h
|
||||
SocketLib/Mime.h
|
||||
SocketLib/Parse.h
|
||||
SocketLib/socket_include.h
|
||||
SocketLib/Utility.h
|
||||
SocketLib/Base64.cpp
|
||||
SocketLib/File.cpp
|
||||
SocketLib/HttpdCookies.cpp
|
||||
SocketLib/HttpdForm.cpp
|
||||
SocketLib/HttpdSocket.cpp
|
||||
SocketLib/HTTPSocket.cpp
|
||||
SocketLib/MemFile.cpp
|
||||
SocketLib/Mime.cpp
|
||||
SocketLib/Parse.cpp
|
||||
SocketLib/socket_include.cpp
|
||||
SocketLib/Utility.cpp
|
||||
)
|
||||
|
||||
SOURCE_GROUP(StackWalker FILES
|
||||
StackWalker/StackWalker.h
|
||||
StackWalker/StackWalker.cpp
|
||||
|
||||
@@ -1,818 +0,0 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
There are really two or three different objects shoe-hored into this
|
||||
connection object. Sombody really needs to factor out the relay link
|
||||
crap into its own subclass of this object, it will clean things up
|
||||
tremendously.
|
||||
*/
|
||||
|
||||
#include "../common/global_define.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
|
||||
#include "emu_tcp_connection.h"
|
||||
#include "emu_tcp_server.h"
|
||||
#include "../common/servertalk.h"
|
||||
|
||||
#ifdef FREEBSD //Timothy Whitman - January 7, 2003
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
#define TCPN_DEBUG 0
|
||||
#define TCPN_DEBUG_Console 0
|
||||
#define TCPN_DEBUG_Memory 0
|
||||
#define TCPN_LOG_PACKETS 0
|
||||
#define TCPN_LOG_RAW_DATA_OUT 0
|
||||
#define TCPN_LOG_RAW_DATA_IN 0
|
||||
|
||||
|
||||
//server side case
|
||||
EmuTCPConnection::EmuTCPConnection(uint32 ID, EmuTCPServer* iServer, SOCKET in_socket, uint32 irIP, uint16 irPort, bool iOldFormat)
|
||||
: TCPConnection(ID, in_socket, irIP, irPort),
|
||||
keepalive_timer(SERVER_TIMEOUT),
|
||||
timeout_timer(SERVER_TIMEOUT * 2)
|
||||
{
|
||||
id = 0;
|
||||
Server = nullptr;
|
||||
pOldFormat = iOldFormat;
|
||||
#ifdef MINILOGIN
|
||||
TCPMode = modePacket;
|
||||
PacketMode = packetModeLogin;
|
||||
#else
|
||||
if (pOldFormat)
|
||||
TCPMode = modePacket;
|
||||
else
|
||||
TCPMode = modeConsole;
|
||||
PacketMode = packetModeZone;
|
||||
#endif
|
||||
RelayLink = 0;
|
||||
RelayServer = false;
|
||||
RelayCount = 0;
|
||||
RemoteID = 0;
|
||||
|
||||
}
|
||||
|
||||
//client outgoing connection case (and client side relay)
|
||||
EmuTCPConnection::EmuTCPConnection(bool iOldFormat, EmuTCPServer* iRelayServer, eTCPMode iMode)
|
||||
: TCPConnection(),
|
||||
keepalive_timer(SERVER_TIMEOUT),
|
||||
timeout_timer(SERVER_TIMEOUT * 2)
|
||||
{
|
||||
Server = iRelayServer;
|
||||
if (Server)
|
||||
RelayServer = true;
|
||||
else
|
||||
RelayServer = false;
|
||||
RelayLink = 0;
|
||||
RelayCount = 0;
|
||||
RemoteID = 0;
|
||||
pOldFormat = iOldFormat;
|
||||
TCPMode = iMode;
|
||||
PacketMode = packetModeZone;
|
||||
#if TCPN_DEBUG_Memory >= 7
|
||||
std::cout << "Constructor #1 on outgoing TCP# " << GetID() << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
//server side relay case
|
||||
EmuTCPConnection::EmuTCPConnection(uint32 ID, EmuTCPServer* iServer, EmuTCPConnection* iRelayLink, uint32 iRemoteID, uint32 irIP, uint16 irPort)
|
||||
: TCPConnection(ID, 0, irIP, irPort),
|
||||
keepalive_timer(SERVER_TIMEOUT),
|
||||
timeout_timer(SERVER_TIMEOUT * 2)
|
||||
{
|
||||
Server = iServer;
|
||||
RelayLink = iRelayLink;
|
||||
RelayServer = true;
|
||||
RelayCount = 0;
|
||||
RemoteID = iRemoteID;
|
||||
pOldFormat = false;
|
||||
ConnectionType = Incoming;
|
||||
TCPMode = modePacket;
|
||||
PacketMode = packetModeZone;
|
||||
#if TCPN_DEBUG_Memory >= 7
|
||||
std::cout << "Constructor #3 on outgoing TCP# " << GetID() << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
EmuTCPConnection::~EmuTCPConnection() {
|
||||
//the queues free their content right now I believe.
|
||||
}
|
||||
|
||||
EmuTCPNetPacket_Struct* EmuTCPConnection::MakePacket(ServerPacket* pack, uint32 iDestination) {
|
||||
int32 size = sizeof(EmuTCPNetPacket_Struct) + pack->size;
|
||||
if (pack->compressed) {
|
||||
size += 4;
|
||||
}
|
||||
if (iDestination) {
|
||||
size += 4;
|
||||
}
|
||||
EmuTCPNetPacket_Struct* tnps = (EmuTCPNetPacket_Struct*) new uchar[size];
|
||||
tnps->size = size;
|
||||
tnps->opcode = pack->opcode;
|
||||
*((uint8*) &tnps->flags) = 0;
|
||||
uchar* buffer = tnps->buffer;
|
||||
if (pack->compressed) {
|
||||
tnps->flags.compressed = 1;
|
||||
*((int32*) buffer) = pack->InflatedSize;
|
||||
buffer += 4;
|
||||
}
|
||||
if (iDestination) {
|
||||
tnps->flags.destination = 1;
|
||||
*((int32*) buffer) = iDestination;
|
||||
buffer += 4;
|
||||
}
|
||||
memcpy(buffer, pack->pBuffer, pack->size);
|
||||
return tnps;
|
||||
}
|
||||
|
||||
SPackSendQueue* EmuTCPConnection::MakeOldPacket(ServerPacket* pack) {
|
||||
SPackSendQueue* spsq = (SPackSendQueue*) new uchar[sizeof(SPackSendQueue) + pack->size + 4];
|
||||
if (pack->pBuffer != 0 && pack->size != 0)
|
||||
memcpy((char *) &spsq->buffer[4], (char *) pack->pBuffer, pack->size);
|
||||
memcpy((char *) &spsq->buffer[0], (char *) &pack->opcode, 2);
|
||||
spsq->size = pack->size+4;
|
||||
memcpy((char *) &spsq->buffer[2], (char *) &spsq->size, 2);
|
||||
return spsq;
|
||||
}
|
||||
|
||||
bool EmuTCPConnection::SendPacket(ServerPacket* pack, uint32 iDestination) {
|
||||
if (!Connected())
|
||||
return false;
|
||||
eTCPMode tmp = GetMode();
|
||||
if (tmp != modePacket && tmp != modeTransition)
|
||||
return false;
|
||||
LockMutex lock(&MState);
|
||||
if (RemoteID)
|
||||
return RelayLink->SendPacket(pack, RemoteID);
|
||||
else if (pOldFormat) {
|
||||
#if TCPN_LOG_PACKETS >= 1
|
||||
if (pack && pack->opcode != 0) {
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
CoutTimestamp(true);
|
||||
std::cout << ": Logging outgoing TCP OldPacket. OPCode: 0x" << std::hex << std::setw(4) << std::setfill('0') << pack->opcode << std::dec << ", size: " << std::setw(5) << std::setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << std::endl;
|
||||
#if TCPN_LOG_PACKETS == 2
|
||||
if (pack->size >= 32)
|
||||
DumpPacket(pack->pBuffer, 32);
|
||||
else
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
#if TCPN_LOG_PACKETS >= 3
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
SPackSendQueue* spsq = MakeOldPacket(pack);
|
||||
ServerSendQueuePushEnd(spsq->buffer, spsq->size);
|
||||
safe_delete_array(spsq);
|
||||
}
|
||||
else {
|
||||
EmuTCPNetPacket_Struct* tnps = MakePacket(pack, iDestination);
|
||||
if (tmp == modeTransition) {
|
||||
InModeQueuePush(tnps);
|
||||
}
|
||||
else {
|
||||
#if TCPN_LOG_PACKETS >= 1
|
||||
if (pack && pack->opcode != 0) {
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
CoutTimestamp(true);
|
||||
std::cout << ": Logging outgoing TCP packet. OPCode: 0x" << std::hex << std::setw(4) << std::setfill('0') << pack->opcode << std::dec << ", size: " << std::setw(5) << std::setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << std::endl;
|
||||
#if TCPN_LOG_PACKETS == 2
|
||||
if (pack->size >= 32)
|
||||
DumpPacket(pack->pBuffer, 32);
|
||||
else
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
#if TCPN_LOG_PACKETS >= 3
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
ServerSendQueuePushEnd((uchar**) &tnps, tnps->size);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmuTCPConnection::SendPacket(EmuTCPNetPacket_Struct* tnps) {
|
||||
if (RemoteID)
|
||||
return false;
|
||||
if (!Connected())
|
||||
return false;
|
||||
if (GetMode() != modePacket)
|
||||
return false;
|
||||
|
||||
LockMutex lock(&MState);
|
||||
eTCPMode tmp = GetMode();
|
||||
if (tmp == modeTransition) {
|
||||
EmuTCPNetPacket_Struct* tnps2 = (EmuTCPNetPacket_Struct*) new uchar[tnps->size];
|
||||
memcpy(tnps2, tnps, tnps->size);
|
||||
InModeQueuePush(tnps2);
|
||||
return true;
|
||||
}
|
||||
#if TCPN_LOG_PACKETS >= 1
|
||||
if (tnps && tnps->opcode != 0) {
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
CoutTimestamp(true);
|
||||
std::cout << ": Logging outgoing TCP NetPacket. OPCode: 0x" << std::hex << std::setw(4) << std::setfill('0') << tnps->opcode << std::dec << ", size: " << std::setw(5) << std::setfill(' ') << tnps->size << " " << inet_ntoa(in) << ":" << GetrPort();
|
||||
if (pOldFormat)
|
||||
std::cout << " (OldFormat)";
|
||||
std::cout << std::endl;
|
||||
#if TCPN_LOG_PACKETS == 2
|
||||
if (tnps->size >= 32)
|
||||
DumpPacket((uchar*) tnps, 32);
|
||||
else
|
||||
DumpPacket((uchar*) tnps, tnps->size);
|
||||
#endif
|
||||
#if TCPN_LOG_PACKETS >= 3
|
||||
DumpPacket((uchar*) tnps, tnps->size);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
ServerSendQueuePushEnd((const uchar*) tnps, tnps->size);
|
||||
return true;
|
||||
}
|
||||
|
||||
ServerPacket* EmuTCPConnection::PopPacket() {
|
||||
ServerPacket* ret;
|
||||
if (!MOutQueueLock.trylock())
|
||||
return nullptr;
|
||||
ret = OutQueue.pop();
|
||||
MOutQueueLock.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EmuTCPConnection::InModeQueuePush(EmuTCPNetPacket_Struct* tnps) {
|
||||
MSendQueue.lock();
|
||||
InModeQueue.push(tnps);
|
||||
MSendQueue.unlock();
|
||||
}
|
||||
|
||||
void EmuTCPConnection::OutQueuePush(ServerPacket* pack) {
|
||||
MOutQueueLock.lock();
|
||||
OutQueue.push(pack);
|
||||
MOutQueueLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
bool EmuTCPConnection::LineOutQueuePush(char* line) {
|
||||
#if defined(GOTFRAGS) && 0
|
||||
if (strcmp(line, "**CRASHME**") == 0) {
|
||||
int i = 0;
|
||||
std::cout << (5 / i) << std::endl;
|
||||
}
|
||||
#endif
|
||||
if(line[0] == '*') {
|
||||
if (strcmp(line, "**PACKETMODE**") == 0) {
|
||||
MSendQueue.lock();
|
||||
safe_delete_array(sendbuf);
|
||||
if (TCPMode == modeConsole)
|
||||
Send((const uchar*) "\0**PACKETMODE**\r", 16);
|
||||
TCPMode = modePacket;
|
||||
PacketMode = packetModeLogin;
|
||||
EmuTCPNetPacket_Struct* tnps = 0;
|
||||
while ((tnps = InModeQueue.pop())) {
|
||||
SendPacket(tnps);
|
||||
safe_delete_array(tnps);
|
||||
}
|
||||
MSendQueue.unlock();
|
||||
safe_delete_array(line);
|
||||
return(true);
|
||||
}
|
||||
if (strcmp(line, "**PACKETMODEZONE**") == 0) {
|
||||
MSendQueue.lock();
|
||||
safe_delete_array(sendbuf);
|
||||
if (TCPMode == modeConsole)
|
||||
Send((const uchar*) "\0**PACKETMODEZONE**\r", 20);
|
||||
TCPMode = modePacket;
|
||||
PacketMode = packetModeZone;
|
||||
EmuTCPNetPacket_Struct* tnps = 0;
|
||||
while ((tnps = InModeQueue.pop())) {
|
||||
SendPacket(tnps);
|
||||
safe_delete_array(tnps);
|
||||
}
|
||||
MSendQueue.unlock();
|
||||
safe_delete_array(line);
|
||||
return(true);
|
||||
}
|
||||
if (strcmp(line, "**PACKETMODELAUNCHER**") == 0) {
|
||||
MSendQueue.lock();
|
||||
safe_delete_array(sendbuf);
|
||||
if (TCPMode == modeConsole)
|
||||
Send((const uchar*) "\0**PACKETMODELAUNCHER**\r", 24);
|
||||
TCPMode = modePacket;
|
||||
PacketMode = packetModeLauncher;
|
||||
EmuTCPNetPacket_Struct* tnps = 0;
|
||||
while ((tnps = InModeQueue.pop())) {
|
||||
SendPacket(tnps);
|
||||
safe_delete_array(tnps);
|
||||
}
|
||||
MSendQueue.unlock();
|
||||
safe_delete_array(line);
|
||||
return(true);
|
||||
}
|
||||
if (strcmp(line, "**PACKETMODEUCS**") == 0) {
|
||||
MSendQueue.lock();
|
||||
safe_delete_array(sendbuf);
|
||||
if (TCPMode == modeConsole)
|
||||
Send((const uchar*) "\0**PACKETMODEUCS**\r", 19);
|
||||
TCPMode = modePacket;
|
||||
PacketMode = packetModeUCS;
|
||||
EmuTCPNetPacket_Struct* tnps = 0;
|
||||
while ((tnps = InModeQueue.pop())) {
|
||||
SendPacket(tnps);
|
||||
safe_delete_array(tnps);
|
||||
}
|
||||
MSendQueue.unlock();
|
||||
safe_delete_array(line);
|
||||
return(true);
|
||||
}
|
||||
if (strcmp(line, "**PACKETMODEQS**") == 0) {
|
||||
MSendQueue.lock();
|
||||
safe_delete_array(sendbuf);
|
||||
if (TCPMode == modeConsole)
|
||||
Send((const uchar*) "\0**PACKETMODEQS**\r", 18);
|
||||
TCPMode = modePacket;
|
||||
PacketMode = packetModeQueryServ;
|
||||
EmuTCPNetPacket_Struct* tnps = 0;
|
||||
while ((tnps = InModeQueue.pop())) {
|
||||
SendPacket(tnps);
|
||||
safe_delete_array(tnps);
|
||||
}
|
||||
MSendQueue.unlock();
|
||||
safe_delete_array(line);
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
|
||||
return(TCPConnection::LineOutQueuePush(line));
|
||||
}
|
||||
|
||||
void EmuTCPConnection::Disconnect(bool iSendRelayDisconnect) {
|
||||
TCPConnection::Disconnect();
|
||||
|
||||
if (RelayLink) {
|
||||
RelayLink->RemoveRelay(this, iSendRelayDisconnect);
|
||||
RelayLink = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool EmuTCPConnection::ConnectIP(uint32 irIP, uint16 irPort, char* errbuf) {
|
||||
if(!TCPConnection::ConnectIP(irIP, irPort, errbuf))
|
||||
return(false);
|
||||
|
||||
MSendQueue.lock();
|
||||
#ifdef MINILOGIN
|
||||
TCPMode = modePacket;
|
||||
#else
|
||||
if (pOldFormat) {
|
||||
TCPMode = modePacket;
|
||||
}
|
||||
else if (TCPMode == modePacket || TCPMode == modeTransition) {
|
||||
TCPMode = modeTransition;
|
||||
if(PacketMode == packetModeLauncher) {
|
||||
safe_delete_array(sendbuf);
|
||||
sendbuf_size = 24;
|
||||
sendbuf_used = sendbuf_size;
|
||||
sendbuf = new uchar[sendbuf_size];
|
||||
memcpy(sendbuf, "\0**PACKETMODELAUNCHER**\r", sendbuf_size);
|
||||
} else if(PacketMode == packetModeLogin) {
|
||||
safe_delete_array(sendbuf);
|
||||
sendbuf_size = 16;
|
||||
sendbuf_used = sendbuf_size;
|
||||
sendbuf = new uchar[sendbuf_size];
|
||||
memcpy(sendbuf, "\0**PACKETMODE**\r", sendbuf_size);
|
||||
} else if(PacketMode == packetModeUCS) {
|
||||
safe_delete_array(sendbuf);
|
||||
sendbuf_size = 19;
|
||||
sendbuf_used = sendbuf_size;
|
||||
sendbuf = new uchar[sendbuf_size];
|
||||
memcpy(sendbuf, "\0**PACKETMODEUCS**\r", sendbuf_size);
|
||||
}
|
||||
else if(PacketMode == packetModeQueryServ) {
|
||||
safe_delete_array(sendbuf);
|
||||
sendbuf_size = 18;
|
||||
sendbuf_used = sendbuf_size;
|
||||
sendbuf = new uchar[sendbuf_size];
|
||||
memcpy(sendbuf, "\0**PACKETMODEQS**\r", sendbuf_size);
|
||||
}
|
||||
else {
|
||||
//default: packetModeZone
|
||||
safe_delete_array(sendbuf);
|
||||
sendbuf_size = 20;
|
||||
sendbuf_used = sendbuf_size;
|
||||
sendbuf = new uchar[sendbuf_size];
|
||||
memcpy(sendbuf, "\0**PACKETMODEZONE**\r", sendbuf_size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
MSendQueue.unlock();
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
void EmuTCPConnection::ClearBuffers() {
|
||||
TCPConnection::ClearBuffers();
|
||||
|
||||
LockMutex lock2(&MOutQueueLock);
|
||||
ServerPacket* pack = 0;
|
||||
while ((pack = OutQueue.pop()))
|
||||
safe_delete(pack);
|
||||
|
||||
EmuTCPNetPacket_Struct* tnps = 0;
|
||||
while ((tnps = InModeQueue.pop()))
|
||||
safe_delete(tnps);
|
||||
|
||||
keepalive_timer.Start();
|
||||
timeout_timer.Start();
|
||||
}
|
||||
|
||||
void EmuTCPConnection::SendNetErrorPacket(const char* reason) {
|
||||
#if TCPC_DEBUG >= 1
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
std::cout "NetError: '";
|
||||
if (reason)
|
||||
std::cout << reason;
|
||||
std::cout << "': " << inet_ntoa(in) << ":" << GetPort() << std::endl;
|
||||
#endif
|
||||
auto pack = new ServerPacket(0);
|
||||
pack->size = 1;
|
||||
if (reason)
|
||||
pack->size += strlen(reason) + 1;
|
||||
pack->pBuffer = new uchar[pack->size];
|
||||
memset(pack->pBuffer, 0, pack->size);
|
||||
pack->pBuffer[0] = 255;
|
||||
strcpy((char *)&pack->pBuffer[1], reason);
|
||||
SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
void EmuTCPConnection::RemoveRelay(EmuTCPConnection* relay, bool iSendRelayDisconnect) {
|
||||
if (iSendRelayDisconnect) {
|
||||
auto pack = new ServerPacket(0, 5);
|
||||
pack->pBuffer[0] = 3;
|
||||
*((uint32*) &pack->pBuffer[1]) = relay->GetRemoteID();
|
||||
SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
RelayCount--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool EmuTCPConnection::ProcessReceivedData(char* errbuf) {
|
||||
if (errbuf)
|
||||
errbuf[0] = 0;
|
||||
timeout_timer.Start();
|
||||
if (!recvbuf)
|
||||
return true;
|
||||
if (TCPMode == modePacket) {
|
||||
if (pOldFormat)
|
||||
return ProcessReceivedDataAsOldPackets(errbuf);
|
||||
else
|
||||
return ProcessReceivedDataAsPackets(errbuf);
|
||||
}
|
||||
//else, use the base class's text processing.
|
||||
bool ret = TCPConnection::ProcessReceivedData(errbuf);
|
||||
//see if we made the transition to packet mode...
|
||||
if(ret && TCPMode == modePacket) {
|
||||
return ProcessReceivedDataAsPackets(errbuf);
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool EmuTCPConnection::ProcessReceivedDataAsPackets(char* errbuf) {
|
||||
if (errbuf)
|
||||
errbuf[0] = 0;
|
||||
int32 base = 0;
|
||||
int32 size = 7;
|
||||
uchar* buffer;
|
||||
ServerPacket* pack = 0;
|
||||
while ((recvbuf_used - base) >= size) {
|
||||
EmuTCPNetPacket_Struct* tnps = (EmuTCPNetPacket_Struct*) &recvbuf[base];
|
||||
buffer = tnps->buffer;
|
||||
size = tnps->size;
|
||||
if (size >= MaxTCPReceiveBuffferSize) {
|
||||
#if TCPN_DEBUG_Memory >= 1
|
||||
std::cout << "TCPConnection[" << GetID() << "]::ProcessReceivedDataAsPackets(): size[" << size << "] >= MaxTCPReceiveBuffferSize" << std::endl;
|
||||
DumpPacket(&recvbuf[base], 16);
|
||||
#endif
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "EmuTCPConnection::ProcessReceivedDataAsPackets(): size >= MaxTCPReceiveBuffferSize");
|
||||
return false;
|
||||
}
|
||||
if ((recvbuf_used - base) >= size) {
|
||||
// ok, we got enough data to make this packet!
|
||||
pack = new ServerPacket;
|
||||
pack->size = size - sizeof(EmuTCPNetPacket_Struct);
|
||||
// read headers
|
||||
pack->opcode = tnps->opcode;
|
||||
if (tnps->flags.compressed) {
|
||||
pack->compressed = true;
|
||||
pack->InflatedSize = *((int32*)buffer);
|
||||
pack->size -= 4;
|
||||
buffer += 4;
|
||||
}
|
||||
if (tnps->flags.destination) {
|
||||
pack->destination = *((int32*)buffer);
|
||||
pack->size -= 4;
|
||||
buffer += 4;
|
||||
}
|
||||
// end read headers
|
||||
if (pack->size > 0) {
|
||||
if (tnps->flags.compressed) {
|
||||
// Lets decompress the packet here
|
||||
pack->compressed = false;
|
||||
pack->pBuffer = new uchar[pack->InflatedSize];
|
||||
pack->size = InflatePacket(buffer, pack->size, pack->pBuffer, pack->InflatedSize);
|
||||
}
|
||||
else {
|
||||
pack->pBuffer = new uchar[pack->size];
|
||||
memcpy(pack->pBuffer, buffer, pack->size);
|
||||
}
|
||||
}
|
||||
if (pack->opcode == 0) {
|
||||
if (pack->size) {
|
||||
#if TCPN_DEBUG >= 2
|
||||
std::cout << "Received TCP Network layer packet" << std::endl;
|
||||
#endif
|
||||
ProcessNetworkLayerPacket(pack);
|
||||
}
|
||||
#if TCPN_DEBUG >= 5
|
||||
else {
|
||||
std::cout << "Received TCP keepalive packet. (opcode=0)" << std::endl;
|
||||
}
|
||||
#endif
|
||||
// keepalive, no need to process
|
||||
safe_delete(pack);
|
||||
}
|
||||
else {
|
||||
#if TCPN_LOG_PACKETS >= 1
|
||||
if (pack && pack->opcode != 0) {
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
CoutTimestamp(true);
|
||||
std::cout << ": Logging incoming TCP packet. OPCode: 0x" << std::hex << std::setw(4) << std::setfill('0') << pack->opcode << std::dec << ", size: " << std::setw(5) << std::setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << std::endl;
|
||||
#if TCPN_LOG_PACKETS == 2
|
||||
if (pack->size >= 32)
|
||||
DumpPacket(pack->pBuffer, 32);
|
||||
else
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
#if TCPN_LOG_PACKETS >= 3
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (RelayServer && Server && pack->destination) {
|
||||
EmuTCPConnection* con = Server->FindConnection(pack->destination);
|
||||
if (!con) {
|
||||
#if TCPN_DEBUG >= 1
|
||||
std::cout << "Error relaying packet: con = 0" << std::endl;
|
||||
#endif
|
||||
safe_delete(pack);
|
||||
}
|
||||
else
|
||||
con->OutQueuePush(pack);
|
||||
}
|
||||
else
|
||||
OutQueuePush(pack);
|
||||
}
|
||||
base += size;
|
||||
size = 7;
|
||||
}
|
||||
}
|
||||
if (base != 0) {
|
||||
if (base >= recvbuf_used) {
|
||||
safe_delete_array(recvbuf);
|
||||
} else {
|
||||
auto tmpbuf = new uchar[recvbuf_size - base];
|
||||
memcpy(tmpbuf, &recvbuf[base], recvbuf_used - base);
|
||||
safe_delete_array(recvbuf);
|
||||
recvbuf = tmpbuf;
|
||||
recvbuf_used -= base;
|
||||
recvbuf_size -= base;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmuTCPConnection::ProcessReceivedDataAsOldPackets(char* errbuf) {
|
||||
int32 base = 0;
|
||||
int32 size = 4;
|
||||
uchar* buffer;
|
||||
ServerPacket* pack = 0;
|
||||
while ((recvbuf_used - base) >= size) {
|
||||
buffer = &recvbuf[base];
|
||||
memcpy(&size, &buffer[2], 2);
|
||||
if (size >= MaxTCPReceiveBuffferSize) {
|
||||
#if TCPN_DEBUG_Memory >= 1
|
||||
std::cout << "TCPConnection[" << GetID() << "]::ProcessReceivedDataAsPackets(): size[" << size << "] >= MaxTCPReceiveBuffferSize" << std::endl;
|
||||
#endif
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "EmuTCPConnection::ProcessReceivedDataAsPackets(): size >= MaxTCPReceiveBuffferSize");
|
||||
return false;
|
||||
}
|
||||
if ((recvbuf_used - base) >= size) {
|
||||
// ok, we got enough data to make this packet!
|
||||
pack = new ServerPacket;
|
||||
memcpy(&pack->opcode, &buffer[0], 2);
|
||||
pack->size = size - 4;
|
||||
/* if () { // TODO: Checksum or size check or something similar
|
||||
// Datastream corruption, get the hell outta here!
|
||||
delete pack;
|
||||
return false;
|
||||
}*/
|
||||
if (pack->size > 0) {
|
||||
pack->pBuffer = new uchar[pack->size];
|
||||
memcpy(pack->pBuffer, &buffer[4], pack->size);
|
||||
}
|
||||
if (pack->opcode == 0) {
|
||||
// keepalive, no need to process
|
||||
safe_delete(pack);
|
||||
}
|
||||
else {
|
||||
#if TCPN_LOG_PACKETS >= 1
|
||||
if (pack && pack->opcode != 0) {
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
CoutTimestamp(true);
|
||||
std::cout << ": Logging incoming TCP OldPacket. OPCode: 0x" << std::hex << std::setw(4) << std::setfill('0') << pack->opcode << std::dec << ", size: " << std::setw(5) << std::setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << std::endl;
|
||||
#if TCPN_LOG_PACKETS == 2
|
||||
if (pack->size >= 32)
|
||||
DumpPacket(pack->pBuffer, 32);
|
||||
else
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
#if TCPN_LOG_PACKETS >= 3
|
||||
DumpPacket(pack);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
OutQueuePush(pack);
|
||||
}
|
||||
base += size;
|
||||
size = 4;
|
||||
}
|
||||
}
|
||||
if (base != 0) {
|
||||
if (base >= recvbuf_used) {
|
||||
safe_delete_array(recvbuf);
|
||||
}
|
||||
else {
|
||||
auto tmpbuf = new uchar[recvbuf_size - base];
|
||||
memcpy(tmpbuf, &recvbuf[base], recvbuf_used - base);
|
||||
safe_delete_array(recvbuf);
|
||||
recvbuf = tmpbuf;
|
||||
recvbuf_used -= base;
|
||||
recvbuf_size -= base;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void EmuTCPConnection::ProcessNetworkLayerPacket(ServerPacket* pack) {
|
||||
uint8 opcode = pack->pBuffer[0];
|
||||
uint8* data = &pack->pBuffer[1];
|
||||
switch (opcode) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
case 1: { // Switch to RelayServer mode
|
||||
if (pack->size != 1) {
|
||||
SendNetErrorPacket("New RelayClient: wrong size, expected 1");
|
||||
break;
|
||||
}
|
||||
if (RelayServer) {
|
||||
SendNetErrorPacket("Switch to RelayServer mode when already in RelayServer mode");
|
||||
break;
|
||||
}
|
||||
if (RemoteID) {
|
||||
SendNetErrorPacket("Switch to RelayServer mode by a Relay Client");
|
||||
break;
|
||||
}
|
||||
if (ConnectionType != Incoming) {
|
||||
SendNetErrorPacket("Switch to RelayServer mode on outgoing connection");
|
||||
break;
|
||||
}
|
||||
#if TCPC_DEBUG >= 3
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
std::cout << "Switching to RelayServer mode: " << inet_ntoa(in) << ":" << GetPort() << std::endl;
|
||||
#endif
|
||||
RelayServer = true;
|
||||
break;
|
||||
}
|
||||
case 2: { // New Relay Client
|
||||
if (!RelayServer) {
|
||||
SendNetErrorPacket("New RelayClient when not in RelayServer mode");
|
||||
break;
|
||||
}
|
||||
if (pack->size != 11) {
|
||||
SendNetErrorPacket("New RelayClient: wrong size, expected 11");
|
||||
break;
|
||||
}
|
||||
if (ConnectionType != Incoming) {
|
||||
SendNetErrorPacket("New RelayClient: illegal on outgoing connection");
|
||||
break;
|
||||
}
|
||||
auto con = new EmuTCPConnection(Server->GetNextID(), Server, this, *((uint32 *)data),
|
||||
*((uint32 *)&data[4]), *((uint16 *)&data[8]));
|
||||
Server->AddConnection(con);
|
||||
RelayCount++;
|
||||
break;
|
||||
}
|
||||
case 3: { // Delete Relay Client
|
||||
if (!RelayServer) {
|
||||
SendNetErrorPacket("Delete RelayClient when not in RelayServer mode");
|
||||
break;
|
||||
}
|
||||
if (pack->size != 5) {
|
||||
SendNetErrorPacket("Delete RelayClient: wrong size, expected 5");
|
||||
break;
|
||||
}
|
||||
EmuTCPConnection* con = Server->FindConnection(*((uint32*)data));
|
||||
if (con) {
|
||||
if (ConnectionType == Incoming) {
|
||||
if (con->GetRelayLink() != this) {
|
||||
SendNetErrorPacket("Delete RelayClient: RelayLink != this");
|
||||
break;
|
||||
}
|
||||
}
|
||||
con->Disconnect(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 255: {
|
||||
#if TCPC_DEBUG >= 1
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
std::cout "Received NetError: '";
|
||||
if (pack->size > 1)
|
||||
std::cout << (char*) data;
|
||||
std::cout << "': " << inet_ntoa(in) << ":" << GetPort() << std::endl;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EmuTCPConnection::SendData(bool &sent_something, char* errbuf) {
|
||||
sent_something = false;
|
||||
if(!TCPConnection::SendData(sent_something, errbuf))
|
||||
return(false);
|
||||
|
||||
if(sent_something)
|
||||
keepalive_timer.Start();
|
||||
else if (TCPMode == modePacket && keepalive_timer.Check()) {
|
||||
auto pack = new ServerPacket(0, 0);
|
||||
SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
#if TCPN_DEBUG >= 5
|
||||
std::cout << "Sending TCP keepalive packet. (timeout=" << timeout_timer.GetRemainingTime() << " remaining)" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool EmuTCPConnection::RecvData(char* errbuf) {
|
||||
if(!TCPConnection::RecvData(errbuf)) {
|
||||
if (OutQueue.count())
|
||||
return(true);
|
||||
else
|
||||
return(false);
|
||||
}
|
||||
|
||||
if ((TCPMode == modePacket || TCPMode == modeTransition) && timeout_timer.Check()) {
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Connection timeout");
|
||||
return false;
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
#ifndef EmuTCPCONNECTION_H_
|
||||
#define EmuTCPCONNECTION_H_
|
||||
|
||||
#include "tcp_connection.h"
|
||||
#include "timer.h"
|
||||
|
||||
//moved out of TCPConnection:: to be more exportable
|
||||
#pragma pack(1)
|
||||
struct EmuTCPNetPacket_Struct {
|
||||
uint32 size;
|
||||
struct {
|
||||
uint8
|
||||
compressed : 1,
|
||||
destination : 1,
|
||||
flag3 : 1,
|
||||
flag4 : 1,
|
||||
flag5 : 1,
|
||||
flag6 : 1,
|
||||
flag7 : 1,
|
||||
flag8 : 1;
|
||||
} flags;
|
||||
uint16 opcode;
|
||||
uchar buffer[0];
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
struct SPackSendQueue;
|
||||
class EmuTCPServer;
|
||||
class ServerPacket;
|
||||
|
||||
class EmuTCPConnection : public TCPConnection {
|
||||
public:
|
||||
enum eTCPMode { modeConsole, modeTransition, modePacket };
|
||||
enum ePacketMode { packetModeZone, packetModeLauncher, packetModeLogin, packetModeUCS, packetModeQueryServ };
|
||||
|
||||
EmuTCPConnection(uint32 ID, EmuTCPServer* iServer, SOCKET iSock, uint32 irIP, uint16 irPort, bool iOldFormat = false);
|
||||
EmuTCPConnection(bool iOldFormat = false, EmuTCPServer* iRelayServer = 0, eTCPMode iMode = modePacket); // for outgoing connections
|
||||
EmuTCPConnection(uint32 ID, EmuTCPServer* iServer, EmuTCPConnection* iRelayLink, uint32 iRemoteID, uint32 irIP, uint16 irPort); // for relay connections
|
||||
virtual ~EmuTCPConnection();
|
||||
|
||||
virtual bool ConnectIP(uint32 irIP, uint16 irPort, char* errbuf = 0);
|
||||
virtual void Disconnect(bool iSendRelayDisconnect = true);
|
||||
|
||||
static EmuTCPNetPacket_Struct* MakePacket(ServerPacket* pack, uint32 iDestination = 0);
|
||||
static SPackSendQueue* MakeOldPacket(ServerPacket* pack);
|
||||
|
||||
virtual bool SendPacket(ServerPacket* pack, uint32 iDestination = 0);
|
||||
virtual bool SendPacket(EmuTCPNetPacket_Struct* tnps);
|
||||
ServerPacket* PopPacket(); // OutQueuePop()
|
||||
void SetPacketMode(ePacketMode mode) { PacketMode = mode; }
|
||||
|
||||
eTCPMode GetMode() const { return TCPMode; }
|
||||
ePacketMode GetPacketMode() const { return(PacketMode); }
|
||||
|
||||
//relay crap:
|
||||
inline bool IsRelayServer() const { return RelayServer; }
|
||||
inline TCPConnection* GetRelayLink() const { return RelayLink; }
|
||||
inline uint32 GetRemoteID() const { return RemoteID; }
|
||||
|
||||
protected:
|
||||
void OutQueuePush(ServerPacket* pack);
|
||||
void RemoveRelay(EmuTCPConnection* relay, bool iSendRelayDisconnect);
|
||||
|
||||
void SendNetErrorPacket(const char* reason = 0);
|
||||
|
||||
virtual bool SendData(bool &sent_something, char* errbuf = 0);
|
||||
virtual bool RecvData(char* errbuf = 0);
|
||||
|
||||
virtual bool ProcessReceivedData(char* errbuf = 0);
|
||||
bool ProcessReceivedDataAsPackets(char* errbuf = 0);
|
||||
bool ProcessReceivedDataAsOldPackets(char* errbuf = 0);
|
||||
void ProcessNetworkLayerPacket(ServerPacket* pack);
|
||||
|
||||
virtual bool LineOutQueuePush(char* line);
|
||||
virtual void ClearBuffers();
|
||||
|
||||
EmuTCPServer* Server;
|
||||
|
||||
eTCPMode TCPMode;
|
||||
ePacketMode PacketMode;
|
||||
bool pOldFormat;
|
||||
|
||||
Timer keepalive_timer;
|
||||
Timer timeout_timer;
|
||||
|
||||
//relay crap:
|
||||
EmuTCPConnection* RelayLink;
|
||||
int32 RelayCount;
|
||||
bool RelayServer;
|
||||
uint32 RemoteID;
|
||||
|
||||
//input queue...
|
||||
void InModeQueuePush(EmuTCPNetPacket_Struct* tnps);
|
||||
MyQueue<EmuTCPNetPacket_Struct> InModeQueue;
|
||||
|
||||
//output queue...
|
||||
MyQueue<ServerPacket> OutQueue;
|
||||
Mutex MOutQueueLock;
|
||||
};
|
||||
|
||||
#endif /*EmuTCPCONNECTION_H_*/
|
||||
|
||||
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
#include "global_define.h"
|
||||
#include "emu_tcp_server.h"
|
||||
#include "emu_tcp_connection.h"
|
||||
|
||||
EmuTCPServer::EmuTCPServer(uint16 iPort, bool iOldFormat)
|
||||
: TCPServer<EmuTCPConnection>(iPort),
|
||||
pOldFormat(iOldFormat)
|
||||
{
|
||||
}
|
||||
|
||||
EmuTCPServer::~EmuTCPServer() {
|
||||
MInQueue.lock();
|
||||
while(!m_InQueue.empty()) {
|
||||
delete m_InQueue.front();
|
||||
m_InQueue.pop();
|
||||
}
|
||||
MInQueue.unlock();
|
||||
}
|
||||
|
||||
void EmuTCPServer::Process() {
|
||||
CheckInQueue();
|
||||
TCPServer<EmuTCPConnection>::Process();
|
||||
}
|
||||
|
||||
void EmuTCPServer::CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort)
|
||||
{
|
||||
auto conn = new EmuTCPConnection(ID, this, in_socket, irIP, irPort, pOldFormat);
|
||||
AddConnection(conn);
|
||||
}
|
||||
|
||||
|
||||
void EmuTCPServer::SendPacket(ServerPacket* pack) {
|
||||
EmuTCPNetPacket_Struct* tnps = EmuTCPConnection::MakePacket(pack);
|
||||
SendPacket(&tnps);
|
||||
}
|
||||
|
||||
void EmuTCPServer::SendPacket(EmuTCPNetPacket_Struct** tnps) {
|
||||
MInQueue.lock();
|
||||
m_InQueue.push(*tnps);
|
||||
MInQueue.unlock();
|
||||
tnps = nullptr;
|
||||
}
|
||||
|
||||
void EmuTCPServer::CheckInQueue() {
|
||||
EmuTCPNetPacket_Struct* tnps = 0;
|
||||
|
||||
while (( tnps = InQueuePop() )) {
|
||||
vitr cur, end;
|
||||
cur = m_list.begin();
|
||||
end = m_list.end();
|
||||
for(; cur != end; cur++) {
|
||||
if ((*cur)->GetMode() != EmuTCPConnection::modeConsole && (*cur)->GetRemoteID() == 0)
|
||||
(*cur)->SendPacket(tnps);
|
||||
}
|
||||
safe_delete(tnps);
|
||||
}
|
||||
}
|
||||
|
||||
EmuTCPNetPacket_Struct* EmuTCPServer::InQueuePop() {
|
||||
EmuTCPNetPacket_Struct* ret = nullptr;
|
||||
MInQueue.lock();
|
||||
if(!m_InQueue.empty()) {
|
||||
ret = m_InQueue.front();
|
||||
m_InQueue.pop();
|
||||
}
|
||||
MInQueue.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
EmuTCPConnection *EmuTCPServer::FindConnection(uint32 iID) {
|
||||
vitr cur, end;
|
||||
cur = m_list.begin();
|
||||
end = m_list.end();
|
||||
for(; cur != end; cur++) {
|
||||
if ((*cur)->GetID() == iID)
|
||||
return *cur;
|
||||
}
|
||||
return(nullptr);
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#ifndef EmuTCPSERVER_H_
|
||||
#define EmuTCPSERVER_H_
|
||||
|
||||
#include "tcp_server.h"
|
||||
|
||||
class EmuTCPConnection;
|
||||
struct EmuTCPNetPacket_Struct;
|
||||
class ServerPacket;
|
||||
|
||||
class EmuTCPServer : public TCPServer<EmuTCPConnection> {
|
||||
public:
|
||||
EmuTCPServer(uint16 iPort = 0, bool iOldFormat = false);
|
||||
virtual ~EmuTCPServer();
|
||||
|
||||
//packet broadcast routines.
|
||||
void SendPacket(ServerPacket* pack);
|
||||
void SendPacket(EmuTCPNetPacket_Struct** tnps);
|
||||
|
||||
//special crap for relay management
|
||||
EmuTCPConnection *FindConnection(uint32 iID);
|
||||
|
||||
//exposed for some crap we pull. Do not call from outside this object.
|
||||
using TCPServer<EmuTCPConnection>::AddConnection;
|
||||
|
||||
protected:
|
||||
virtual void Process();
|
||||
|
||||
virtual void CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort);
|
||||
|
||||
bool pOldFormat;
|
||||
|
||||
//broadcast packet queue..
|
||||
void CheckInQueue();
|
||||
Mutex MInQueue;
|
||||
EmuTCPNetPacket_Struct* InQueuePop(); //returns ownership
|
||||
std::queue<EmuTCPNetPacket_Struct *> m_InQueue;
|
||||
};
|
||||
#endif /*EmuTCPSERVER_H_*/
|
||||
@@ -32,7 +32,6 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
|
||||
out.PutUInt16(4, opcode);
|
||||
|
||||
std::unique_ptr<unsigned char[]> cipher(new unsigned char[p.Length() + crypto_secretbox_MACBYTES]);
|
||||
|
||||
crypto_box_easy_afternm(&cipher[0], (unsigned char*)p.Data(), p.Length(), m_nonce_ours, m_shared_key);
|
||||
(*(uint64_t*)&m_nonce_ours[0])++;
|
||||
out.PutData(6, &cipher[0], p.Length() + crypto_secretbox_MACBYTES);
|
||||
@@ -218,6 +217,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b
|
||||
size_t cipher_len = p.Length() - crypto_box_PUBLICKEYBYTES - crypto_box_NONCEBYTES;
|
||||
size_t message_len = cipher_len - crypto_secretbox_MACBYTES;
|
||||
std::unique_ptr<unsigned char[]> decrypted_text(new unsigned char[message_len]);
|
||||
|
||||
if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)p.Data() + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, cipher_len, m_nonce_theirs, m_shared_key))
|
||||
{
|
||||
Log.OutF(Logs::General, Logs::Error, "Error decrypting handshake from client, dropping connection.");
|
||||
@@ -235,7 +235,6 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b
|
||||
}
|
||||
|
||||
m_parent->ConnectionIdentified(this);
|
||||
|
||||
(*(uint64_t*)&m_nonce_theirs[0])++;
|
||||
}
|
||||
}
|
||||
@@ -293,6 +292,7 @@ void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p)
|
||||
if (m_encrypted) {
|
||||
size_t message_len = length - crypto_secretbox_MACBYTES;
|
||||
std::unique_ptr<unsigned char[]> decrypted_text(new unsigned char[message_len]);
|
||||
|
||||
if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)&data[0], length, m_nonce_theirs, m_shared_key))
|
||||
{
|
||||
Log.OutF(Logs::General, Logs::Error, "Error decrypting message from client");
|
||||
|
||||
+2
-2
@@ -62,7 +62,7 @@
|
||||
#define ServerOP_ItemStatus 0x002C
|
||||
#define ServerOP_OOCMute 0x002D
|
||||
#define ServerOP_Revoke 0x002E
|
||||
//#define 0x002F
|
||||
#define ServerOP_WebInterfaceCall 0x002F
|
||||
#define ServerOP_GroupIDReq 0x0030
|
||||
#define ServerOP_GroupIDReply 0x0031
|
||||
#define ServerOP_GroupLeave 0x0032 // for disbanding out of zone folks
|
||||
@@ -227,7 +227,7 @@ public:
|
||||
|
||||
ServerPacket(uint16 in_opcode, const EQ::Net::Packet &p) {
|
||||
this->compressed = false;
|
||||
size = p.Length();
|
||||
size = (uint32)p.Length();
|
||||
opcode = in_opcode;
|
||||
if (size == 0) {
|
||||
pBuffer = 0;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#ifndef TCPBASICSERVER_H_
|
||||
#define TCPBASICSERVER_H_
|
||||
|
||||
#include "tcp_server.h"
|
||||
#include "tcp_connection.h"
|
||||
|
||||
class TCPBasicServer : public TCPServer<TCPConnection> {
|
||||
public:
|
||||
inline TCPBasicServer(uint16 iPort = 0) : TCPServer<TCPConnection>(iPort) { }
|
||||
inline virtual void CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort) {
|
||||
TCPConnection *conn = new TCPConnection(ID, in_socket, irIP, irPort);
|
||||
AddConnection(conn);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /*TCPBASICSERVER_H_*/
|
||||
|
||||
@@ -1,942 +0,0 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../common/global_define.h"
|
||||
#include "../common/eqemu_logsys.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#include <iomanip>
|
||||
|
||||
#include "tcp_connection.h"
|
||||
|
||||
#ifdef FREEBSD //Timothy Whitman - January 7, 2003
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
#ifdef DARWIN
|
||||
#define MSG_NOSIGNAL SO_NOSIGPIPE // Corysia Taware - Sept. 27, 2013
|
||||
// See http://lists.apple.com/archives/macnetworkprog/2002/Dec/msg00091.html
|
||||
#endif // DARWIN
|
||||
|
||||
#ifdef _WINDOWS
|
||||
InitWinsock winsock;
|
||||
#endif
|
||||
|
||||
#define LOOP_GRANULARITY 3 //# of ms between checking our socket/queues
|
||||
|
||||
#define TCPN_DEBUG 0
|
||||
#define TCPN_DEBUG_Console 0
|
||||
#define TCPN_DEBUG_Memory 0
|
||||
#define TCPN_LOG_RAW_DATA_OUT 0 //1 = info, 2 = length limited dump, 3 = full dump
|
||||
#define TCPN_LOG_RAW_DATA_IN 0 //1 = info, 2 = length limited dump, 3 = full dump
|
||||
|
||||
//client version
|
||||
TCPConnection::TCPConnection()
|
||||
: ConnectionType(Outgoing),
|
||||
connection_socket(0),
|
||||
id(0),
|
||||
rIP(0),
|
||||
rPort(0)
|
||||
{
|
||||
pState = TCPS_Ready;
|
||||
pFree = false;
|
||||
pEcho = false;
|
||||
recvbuf = nullptr;
|
||||
sendbuf = nullptr;
|
||||
pRunLoop = false;
|
||||
charAsyncConnect = 0;
|
||||
pAsyncConnect = false;
|
||||
m_previousLineEnd = false;
|
||||
#if TCPN_DEBUG_Memory >= 7
|
||||
std::cout << "Constructor #2 on outgoing TCP# " << GetID() << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
//server version
|
||||
TCPConnection::TCPConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort)
|
||||
: ConnectionType(Incoming),
|
||||
connection_socket(in_socket),
|
||||
id(ID),
|
||||
rIP(irIP),
|
||||
rPort(irPort)
|
||||
{
|
||||
pState = TCPS_Connected;
|
||||
pFree = false;
|
||||
pEcho = false;
|
||||
recvbuf = nullptr;
|
||||
sendbuf = nullptr;
|
||||
pRunLoop = false;
|
||||
charAsyncConnect = 0;
|
||||
pAsyncConnect = false;
|
||||
m_previousLineEnd = false;
|
||||
#if TCPN_DEBUG_Memory >= 7
|
||||
std::cout << "Constructor #2 on incoming TCP# " << GetID() << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
TCPConnection::~TCPConnection() {
|
||||
FinishDisconnect();
|
||||
ClearBuffers();
|
||||
if (ConnectionType == Outgoing) {
|
||||
MRunLoop.lock();
|
||||
pRunLoop = false;
|
||||
MRunLoop.unlock();
|
||||
MLoopRunning.lock();
|
||||
MLoopRunning.unlock();
|
||||
#if TCPN_DEBUG_Memory >= 6
|
||||
std::cout << "Deconstructor on outgoing TCP# " << GetID() << std::endl;
|
||||
#endif
|
||||
}
|
||||
#if TCPN_DEBUG_Memory >= 5
|
||||
else {
|
||||
std::cout << "Deconstructor on incoming TCP# " << GetID() << std::endl;
|
||||
}
|
||||
#endif
|
||||
safe_delete_array(recvbuf);
|
||||
safe_delete_array(sendbuf);
|
||||
safe_delete_array(charAsyncConnect);
|
||||
}
|
||||
|
||||
void TCPConnection::SetState(State_t in_state) {
|
||||
MState.lock();
|
||||
pState = in_state;
|
||||
MState.unlock();
|
||||
}
|
||||
|
||||
TCPConnection::State_t TCPConnection::GetState() const {
|
||||
State_t ret;
|
||||
MState.lock();
|
||||
ret = pState;
|
||||
MState.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool TCPConnection::GetSockName(char *host, uint16 *port)
|
||||
{
|
||||
bool result=false;
|
||||
LockMutex lock(&MState);
|
||||
if (!Connected())
|
||||
return false;
|
||||
|
||||
struct sockaddr_in local;
|
||||
|
||||
#ifdef _WINDOWS
|
||||
int addrlen;
|
||||
#else
|
||||
#ifdef FREEBSD
|
||||
socklen_t addrlen;
|
||||
#else
|
||||
size_t addrlen;
|
||||
#endif
|
||||
#endif
|
||||
addrlen=sizeof(struct sockaddr_in);
|
||||
#ifdef _WINDOWS
|
||||
if (!getsockname(connection_socket,(struct sockaddr *)&local,&addrlen)) {
|
||||
#else
|
||||
if (!getsockname(connection_socket,(struct sockaddr *)&local,(socklen_t *)&addrlen)) {
|
||||
#endif
|
||||
unsigned long ip=local.sin_addr.s_addr;
|
||||
sprintf(host,"%d.%d.%d.%d",
|
||||
*(unsigned char *)&ip,
|
||||
*((unsigned char *)&ip+1),
|
||||
*((unsigned char *)&ip+2),
|
||||
*((unsigned char *)&ip+3));
|
||||
*port=ntohs(local.sin_port);
|
||||
|
||||
result=true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TCPConnection::Free() {
|
||||
Disconnect();
|
||||
pFree = true;
|
||||
}
|
||||
|
||||
bool TCPConnection::Send(const uchar* data, int32 size) {
|
||||
if (!Connected())
|
||||
return false;
|
||||
if (!size)
|
||||
return true;
|
||||
ServerSendQueuePushEnd(data, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TCPConnection::ServerSendQueuePushEnd(const uchar* data, int32 size) {
|
||||
MSendQueue.lock();
|
||||
if (sendbuf == nullptr) {
|
||||
sendbuf = new uchar[size];
|
||||
sendbuf_size = size;
|
||||
sendbuf_used = 0;
|
||||
}
|
||||
else if (size > (sendbuf_size - sendbuf_used)) {
|
||||
sendbuf_size += size + 1024;
|
||||
auto tmp = new uchar[sendbuf_size];
|
||||
memcpy(tmp, sendbuf, sendbuf_used);
|
||||
safe_delete_array(sendbuf);
|
||||
sendbuf = tmp;
|
||||
}
|
||||
memcpy(&sendbuf[sendbuf_used], data, size);
|
||||
sendbuf_used += size;
|
||||
MSendQueue.unlock();
|
||||
}
|
||||
|
||||
void TCPConnection::ServerSendQueuePushEnd(uchar** data, int32 size) {
|
||||
MSendQueue.lock();
|
||||
if (sendbuf == 0) {
|
||||
sendbuf = *data;
|
||||
sendbuf_size = size;
|
||||
sendbuf_used = size;
|
||||
MSendQueue.unlock();
|
||||
*data = 0;
|
||||
return;
|
||||
}
|
||||
if (size > (sendbuf_size - sendbuf_used)) {
|
||||
sendbuf_size += size;
|
||||
auto tmp = new uchar[sendbuf_size];
|
||||
memcpy(tmp, sendbuf, sendbuf_used);
|
||||
safe_delete_array(sendbuf);
|
||||
sendbuf = tmp;
|
||||
}
|
||||
memcpy(&sendbuf[sendbuf_used], *data, size);
|
||||
sendbuf_used += size;
|
||||
MSendQueue.unlock();
|
||||
safe_delete_array(*data);
|
||||
}
|
||||
|
||||
void TCPConnection::ServerSendQueuePushFront(uchar* data, int32 size) {
|
||||
MSendQueue.lock();
|
||||
if (sendbuf == 0) {
|
||||
sendbuf = new uchar[size];
|
||||
sendbuf_size = size;
|
||||
sendbuf_used = 0;
|
||||
}
|
||||
else if (size > (sendbuf_size - sendbuf_used)) {
|
||||
sendbuf_size += size;
|
||||
auto tmp = new uchar[sendbuf_size];
|
||||
memcpy(&tmp[size], sendbuf, sendbuf_used);
|
||||
safe_delete_array(sendbuf);
|
||||
sendbuf = tmp;
|
||||
}
|
||||
memcpy(sendbuf, data, size);
|
||||
sendbuf_used += size;
|
||||
MSendQueue.unlock();
|
||||
}
|
||||
|
||||
bool TCPConnection::ServerSendQueuePop(uchar** data, int32* size) {
|
||||
bool ret;
|
||||
if (!MSendQueue.trylock())
|
||||
return false;
|
||||
if (sendbuf) {
|
||||
*data = sendbuf;
|
||||
*size = sendbuf_used;
|
||||
sendbuf = 0;
|
||||
ret = true;
|
||||
}
|
||||
else {
|
||||
ret = false;
|
||||
}
|
||||
MSendQueue.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool TCPConnection::ServerSendQueuePopForce(uchar** data, int32* size) {
|
||||
bool ret;
|
||||
MSendQueue.lock();
|
||||
if (sendbuf) {
|
||||
*data = sendbuf;
|
||||
*size = sendbuf_used;
|
||||
sendbuf = 0;
|
||||
ret = true;
|
||||
}
|
||||
else {
|
||||
ret = false;
|
||||
}
|
||||
MSendQueue.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
char* TCPConnection::PopLine() {
|
||||
char* ret;
|
||||
if (!MLineOutQueue.trylock())
|
||||
return 0;
|
||||
ret = (char*) LineOutQueue.pop();
|
||||
MLineOutQueue.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool TCPConnection::LineOutQueuePush(char* line) {
|
||||
MLineOutQueue.lock();
|
||||
LineOutQueue.push(line);
|
||||
MLineOutQueue.unlock();
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
void TCPConnection::FinishDisconnect() {
|
||||
MState.lock();
|
||||
if (connection_socket != INVALID_SOCKET && connection_socket != 0) {
|
||||
if (pState == TCPS_Connected || pState == TCPS_Disconnecting || pState == TCPS_Disconnected) {
|
||||
bool sent_something = false;
|
||||
SendData(sent_something);
|
||||
}
|
||||
pState = TCPS_Closing;
|
||||
shutdown(connection_socket, 0x01);
|
||||
shutdown(connection_socket, 0x00);
|
||||
#ifdef _WINDOWS
|
||||
closesocket(connection_socket);
|
||||
#else
|
||||
close(connection_socket);
|
||||
#endif
|
||||
connection_socket = 0;
|
||||
rIP = 0;
|
||||
rPort = 0;
|
||||
ClearBuffers();
|
||||
}
|
||||
pState = TCPS_Disconnected;
|
||||
MState.unlock();
|
||||
}
|
||||
|
||||
void TCPConnection::Disconnect() {
|
||||
MState.lock();
|
||||
if(pState == TCPS_Connected || pState == TCPS_Connecting) {
|
||||
pState = TCPS_Disconnecting;
|
||||
}
|
||||
MState.unlock();
|
||||
}
|
||||
|
||||
bool TCPConnection::GetAsyncConnect() {
|
||||
bool ret;
|
||||
MAsyncConnect.lock();
|
||||
ret = pAsyncConnect;
|
||||
MAsyncConnect.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool TCPConnection::SetAsyncConnect(bool iValue) {
|
||||
bool ret;
|
||||
MAsyncConnect.lock();
|
||||
ret = pAsyncConnect;
|
||||
pAsyncConnect = iValue;
|
||||
MAsyncConnect.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool TCPConnection::ConnectReady() const {
|
||||
State_t s = GetState();
|
||||
if (s != TCPS_Ready && s != TCPS_Disconnected)
|
||||
return(false);
|
||||
return(ConnectionType == Outgoing);
|
||||
}
|
||||
|
||||
void TCPConnection::AsyncConnect(const char* irAddress, uint16 irPort) {
|
||||
safe_delete_array(charAsyncConnect);
|
||||
charAsyncConnect = new char[strlen(irAddress) + 1];
|
||||
strcpy(charAsyncConnect, irAddress);
|
||||
AsyncConnect((uint32) 0, irPort);
|
||||
}
|
||||
|
||||
void TCPConnection::AsyncConnect(uint32 irIP, uint16 irPort) {
|
||||
if (ConnectionType != Outgoing) {
|
||||
// If this code runs, we got serious problems
|
||||
// Crash and burn.
|
||||
return;
|
||||
}
|
||||
if(!ConnectReady()) {
|
||||
#if TCPN_DEBUG > 0
|
||||
printf("Trying to do async connect in invalid state %s\n", GetState());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
MAsyncConnect.lock();
|
||||
if (pAsyncConnect) {
|
||||
MAsyncConnect.unlock();
|
||||
#if TCPN_DEBUG > 0
|
||||
printf("Trying to do async connect when already doing one.\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#if TCPN_DEBUG > 0
|
||||
printf("Start async connect.\n");
|
||||
#endif
|
||||
pAsyncConnect = true;
|
||||
if(irIP != 0)
|
||||
safe_delete_array(charAsyncConnect);
|
||||
rIP = irIP;
|
||||
rPort = irPort;
|
||||
MAsyncConnect.unlock();
|
||||
if (!pRunLoop) {
|
||||
pRunLoop = true;
|
||||
#ifdef _WINDOWS
|
||||
_beginthread(TCPConnectionLoop, 0, this);
|
||||
#else
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, nullptr, TCPConnectionLoop, this);
|
||||
#endif
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool TCPConnection::Connect(const char* irAddress, uint16 irPort, char* errbuf) {
|
||||
if (errbuf)
|
||||
errbuf[0] = 0;
|
||||
uint32 tmpIP = ResolveIP(irAddress);
|
||||
if (!tmpIP) {
|
||||
if (errbuf) {
|
||||
#ifdef _WINDOWS
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Couldnt resolve hostname. Error: %i", WSAGetLastError());
|
||||
#else
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Couldnt resolve hostname. Error #%i: %s", errno, strerror(errno));
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return ConnectIP(tmpIP, irPort, errbuf);
|
||||
}
|
||||
|
||||
bool TCPConnection::ConnectIP(uint32 in_ip, uint16 in_port, char* errbuf) {
|
||||
if (errbuf)
|
||||
errbuf[0] = 0;
|
||||
if (ConnectionType != Outgoing) {
|
||||
// If this code runs, we got serious problems
|
||||
// Crash and burn.
|
||||
return false;
|
||||
}
|
||||
MState.lock();
|
||||
if (ConnectReady()) {
|
||||
pState = TCPS_Connecting;
|
||||
} else {
|
||||
MState.unlock();
|
||||
SetAsyncConnect(false);
|
||||
return false;
|
||||
}
|
||||
MState.unlock();
|
||||
if (!pRunLoop) {
|
||||
pRunLoop = true;
|
||||
#ifdef _WINDOWS
|
||||
_beginthread(TCPConnectionLoop, 0, this);
|
||||
#else
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, nullptr, TCPConnectionLoop, this);
|
||||
#endif
|
||||
}
|
||||
|
||||
connection_socket = INVALID_SOCKET;
|
||||
struct sockaddr_in server_sin;
|
||||
//struct in_addr in;
|
||||
|
||||
if ((connection_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET || connection_socket == 0) {
|
||||
#ifdef _WINDOWS
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Allocating socket failed. Error: %i", WSAGetLastError());
|
||||
#else
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Allocating socket failed. Error: %s", strerror(errno));
|
||||
#endif
|
||||
SetState(TCPS_Ready);
|
||||
SetAsyncConnect(false);
|
||||
return false;
|
||||
}
|
||||
server_sin.sin_family = AF_INET;
|
||||
server_sin.sin_addr.s_addr = in_ip;
|
||||
server_sin.sin_port = htons(in_port);
|
||||
|
||||
// Establish a connection to the server socket.
|
||||
#ifdef _WINDOWS
|
||||
if (connect(connection_socket, (PSOCKADDR) &server_sin, sizeof (server_sin)) == SOCKET_ERROR) {
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): connect() failed. Error: %i", WSAGetLastError());
|
||||
closesocket(connection_socket);
|
||||
connection_socket = 0;
|
||||
SetState(TCPS_Ready);
|
||||
SetAsyncConnect(false);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (connect(connection_socket, (struct sockaddr *) &server_sin, sizeof (server_sin)) == SOCKET_ERROR) {
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): connect() failed. Error: %s", strerror(errno));
|
||||
close(connection_socket);
|
||||
connection_socket = 0;
|
||||
SetState(TCPS_Ready);
|
||||
SetAsyncConnect(false);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k
|
||||
setsockopt(connection_socket, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize));
|
||||
#ifdef _WINDOWS
|
||||
unsigned long nonblocking = 1;
|
||||
ioctlsocket(connection_socket, FIONBIO, &nonblocking);
|
||||
#else
|
||||
fcntl(connection_socket, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
|
||||
SetEcho(false);
|
||||
ClearBuffers();
|
||||
|
||||
rIP = in_ip;
|
||||
rPort = in_port;
|
||||
SetState(TCPS_Connected);
|
||||
SetAsyncConnect(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TCPConnection::ClearBuffers() {
|
||||
LockMutex lock1(&MSendQueue);
|
||||
LockMutex lock3(&MRunLoop);
|
||||
LockMutex lock4(&MState);
|
||||
safe_delete_array(recvbuf);
|
||||
safe_delete_array(sendbuf);
|
||||
|
||||
char* line = 0;
|
||||
while ((line = LineOutQueue.pop()))
|
||||
safe_delete_array(line);
|
||||
}
|
||||
|
||||
bool TCPConnection::CheckNetActive() {
|
||||
MState.lock();
|
||||
if (pState == TCPS_Connected || pState == TCPS_Disconnecting) {
|
||||
MState.unlock();
|
||||
return true;
|
||||
}
|
||||
MState.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This is always called from an IO thread. Either the server socket's thread, or a
|
||||
* special thread we create when we make an outbound connection. */
|
||||
bool TCPConnection::Process() {
|
||||
char errbuf[TCPConnection_ErrorBufferSize];
|
||||
switch(GetState()) {
|
||||
case TCPS_Ready:
|
||||
case TCPS_Connecting:
|
||||
if (ConnectionType == Outgoing) {
|
||||
if (GetAsyncConnect()) {
|
||||
if (charAsyncConnect)
|
||||
rIP = ResolveIP(charAsyncConnect);
|
||||
ConnectIP(rIP, rPort);
|
||||
}
|
||||
}
|
||||
return(true);
|
||||
|
||||
case TCPS_Connected:
|
||||
// only receive data in the connected state, no others...
|
||||
if (!RecvData(errbuf)) {
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
return false;
|
||||
}
|
||||
/* we break to do the send */
|
||||
break;
|
||||
|
||||
case TCPS_Disconnecting: {
|
||||
//waiting for any sending data to go out...
|
||||
MSendQueue.lock();
|
||||
if(sendbuf) {
|
||||
if(sendbuf_used > 0) {
|
||||
//something left to send, keep processing...
|
||||
MSendQueue.unlock();
|
||||
break;
|
||||
}
|
||||
//else, send buffer is empty.
|
||||
safe_delete_array(sendbuf);
|
||||
} //else, no send buffer, we are done.
|
||||
MSendQueue.unlock();
|
||||
}
|
||||
/* Fallthrough */
|
||||
|
||||
case TCPS_Disconnected:
|
||||
FinishDisconnect();
|
||||
MRunLoop.lock();
|
||||
pRunLoop = false;
|
||||
MRunLoop.unlock();
|
||||
// SetState(TCPS_Ready); //reset the state in case they want to use it again...
|
||||
return(false);
|
||||
|
||||
case TCPS_Closing:
|
||||
//I dont understand this state...
|
||||
|
||||
case TCPS_Error:
|
||||
MRunLoop.lock();
|
||||
pRunLoop = false;
|
||||
MRunLoop.unlock();
|
||||
return(false);
|
||||
}
|
||||
|
||||
/* we get here in connected or disconnecting with more data to send */
|
||||
|
||||
bool sent_something = false;
|
||||
if (!SendData(sent_something, errbuf)) {
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
std::cout << inet_ntoa(in) << ":" << GetrPort() << ": " << errbuf << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TCPConnection::RecvData(char* errbuf) {
|
||||
if (errbuf)
|
||||
errbuf[0] = 0;
|
||||
if (!Connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
if (recvbuf == 0) {
|
||||
recvbuf = new uchar[5120];
|
||||
recvbuf_size = 5120;
|
||||
recvbuf_used = 0;
|
||||
recvbuf_echo = 0;
|
||||
}
|
||||
else if ((recvbuf_size - recvbuf_used) < 2048) {
|
||||
auto tmpbuf = new uchar[recvbuf_size + 5120];
|
||||
memcpy(tmpbuf, recvbuf, recvbuf_used);
|
||||
recvbuf_size += 5120;
|
||||
safe_delete_array(recvbuf);
|
||||
recvbuf = tmpbuf;
|
||||
if (recvbuf_size >= MaxTCPReceiveBuffferSize) {
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): recvbuf_size >= MaxTCPReceiveBuffferSize");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
status = recv(connection_socket, (char *) &recvbuf[recvbuf_used], (recvbuf_size - recvbuf_used), 0);
|
||||
|
||||
if (status >= 1) {
|
||||
#if TCPN_LOG_RAW_DATA_IN >= 1
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
CoutTimestamp(true);
|
||||
std::cout << ": Read " << status << " bytes from network. (recvbuf_used = " << recvbuf_used << ") " << inet_ntoa(in) << ":" << GetrPort();
|
||||
std::cout << std::endl;
|
||||
#if TCPN_LOG_RAW_DATA_IN == 2
|
||||
int32 tmp = status;
|
||||
if (tmp > 32)
|
||||
tmp = 32;
|
||||
DumpPacket(&recvbuf[recvbuf_used], status);
|
||||
#elif TCPN_LOG_RAW_DATA_IN >= 3
|
||||
DumpPacket(&recvbuf[recvbuf_used], status);
|
||||
#endif
|
||||
#endif
|
||||
recvbuf_used += status;
|
||||
if (!ProcessReceivedData(errbuf))
|
||||
return false;
|
||||
}
|
||||
else if (status == SOCKET_ERROR) {
|
||||
#ifdef _WINDOWS
|
||||
if (!(WSAGetLastError() == WSAEWOULDBLOCK)) {
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Error: %i", WSAGetLastError());
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (!(errno == EWOULDBLOCK)) {
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Error: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
} else if (status == 0) {
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Connection closed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool TCPConnection::GetEcho() {
|
||||
bool ret;
|
||||
ret = pEcho;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TCPConnection::SetEcho(bool iValue) {
|
||||
pEcho = iValue;
|
||||
}
|
||||
|
||||
bool TCPConnection::ProcessReceivedData(char* errbuf) {
|
||||
if (errbuf)
|
||||
errbuf[0] = 0;
|
||||
if (!recvbuf)
|
||||
return true;
|
||||
#if TCPN_DEBUG_Console >= 4
|
||||
if (recvbuf_used) {
|
||||
std::cout << "Starting Processing: recvbuf=" << recvbuf_used << std::endl;
|
||||
DumpPacket(recvbuf, recvbuf_used);
|
||||
}
|
||||
#endif
|
||||
for (int i=0; i < recvbuf_used; i++) {
|
||||
if (GetEcho() && i >= recvbuf_echo) {
|
||||
Send(&recvbuf[i], 1);
|
||||
recvbuf_echo = i + 1;
|
||||
}
|
||||
switch(recvbuf[i]) {
|
||||
case 0: { // 0 is the code for clear buffer
|
||||
if (i==0) {
|
||||
recvbuf_used--;
|
||||
recvbuf_echo--;
|
||||
memmove(recvbuf, &recvbuf[1], recvbuf_used);
|
||||
i = -1;
|
||||
} else {
|
||||
if (i == recvbuf_used) {
|
||||
safe_delete_array(recvbuf);
|
||||
i = -1;
|
||||
}
|
||||
else {
|
||||
uchar* tmpdel = recvbuf;
|
||||
recvbuf = new uchar[recvbuf_size];
|
||||
memcpy(recvbuf, &tmpdel[i+1], recvbuf_used-i);
|
||||
recvbuf_used -= i + 1;
|
||||
recvbuf_echo -= i + 1;
|
||||
safe_delete_array(tmpdel);
|
||||
i = -1;
|
||||
}
|
||||
}
|
||||
#if TCPN_DEBUG_Console >= 5
|
||||
std::cout << "Removed 0x00" << std::endl;
|
||||
if (recvbuf_used) {
|
||||
std::cout << "recvbuf left: " << recvbuf_used << std::endl;
|
||||
DumpPacket(recvbuf, recvbuf_used);
|
||||
}
|
||||
else
|
||||
std::cout << "recbuf left: None" << std::endl;
|
||||
#endif
|
||||
m_previousLineEnd = false;
|
||||
break;
|
||||
}
|
||||
case 10:
|
||||
case 13: // newline marker
|
||||
{
|
||||
char *line = nullptr;
|
||||
if (i==0) { // empty line
|
||||
if(!m_previousLineEnd) {
|
||||
//char right before this was NOT a CR, report the empty line.
|
||||
line = new char[1];
|
||||
line[0] = '\0';
|
||||
m_previousLineEnd = true;
|
||||
} else {
|
||||
m_previousLineEnd = false;
|
||||
}
|
||||
recvbuf_used--;
|
||||
recvbuf_echo--;
|
||||
memcpy(recvbuf, &recvbuf[1], recvbuf_used);
|
||||
i = -1;
|
||||
} else {
|
||||
line = new char[i+1];
|
||||
memset(line, 0, i+1);
|
||||
memcpy(line, recvbuf, i);
|
||||
#if TCPN_DEBUG_Console >= 3
|
||||
std::cout << "Line Out: " << std::endl;
|
||||
DumpPacket((uchar*) line, i);
|
||||
#endif
|
||||
//line[i] = 0;
|
||||
uchar* tmpdel = recvbuf;
|
||||
recvbuf = new uchar[recvbuf_size];
|
||||
recvbuf_used -= i+1;
|
||||
recvbuf_echo -= i+1;
|
||||
memcpy(recvbuf, &tmpdel[i+1], recvbuf_used);
|
||||
#if TCPN_DEBUG_Console >= 5
|
||||
std::cout << "i+1=" << i+1 << std::endl;
|
||||
if (recvbuf_used) {
|
||||
std::cout << "recvbuf left: " << recvbuf_used << std::endl;
|
||||
DumpPacket(recvbuf, recvbuf_used);
|
||||
}
|
||||
else
|
||||
std::cout << "recbuf left: None" << std::endl;
|
||||
#endif
|
||||
safe_delete_array(tmpdel);
|
||||
i = -1;
|
||||
m_previousLineEnd = true;
|
||||
}
|
||||
|
||||
|
||||
if(line != nullptr) {
|
||||
bool finish_proc = false;
|
||||
finish_proc = LineOutQueuePush(line);
|
||||
if(finish_proc)
|
||||
return(true); //break early as requested by LineOutQueuePush
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 8: // backspace
|
||||
{
|
||||
if (i==0) { // nothin to backspace
|
||||
recvbuf_used--;
|
||||
recvbuf_echo--;
|
||||
memmove(recvbuf, &recvbuf[1], recvbuf_used);
|
||||
i = -1;
|
||||
} else {
|
||||
uchar* tmpdel = recvbuf;
|
||||
recvbuf = new uchar[recvbuf_size];
|
||||
memcpy(recvbuf, tmpdel, i-1);
|
||||
memcpy(&recvbuf[i-1], &tmpdel[i+1], recvbuf_used-i);
|
||||
recvbuf_used -= 2;
|
||||
recvbuf_echo -= 2;
|
||||
safe_delete_array(tmpdel);
|
||||
i -= 2;
|
||||
}
|
||||
break;
|
||||
m_previousLineEnd = false;
|
||||
}
|
||||
default:
|
||||
m_previousLineEnd = false;
|
||||
}
|
||||
}
|
||||
if (recvbuf_used < 0)
|
||||
safe_delete_array(recvbuf);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TCPConnection::SendData(bool &sent_something, char* errbuf) {
|
||||
if (errbuf)
|
||||
errbuf[0] = 0;
|
||||
/************ Get first send packet on queue and send it! ************/
|
||||
uchar* data = 0;
|
||||
int32 size = 0;
|
||||
int status = 0;
|
||||
if (ServerSendQueuePop(&data, &size)) {
|
||||
#ifdef _WINDOWS
|
||||
status = send(connection_socket, (const char *) data, size, 0);
|
||||
#else
|
||||
status = send(connection_socket, data, size, MSG_NOSIGNAL);
|
||||
if(errno==EPIPE) status = SOCKET_ERROR;
|
||||
#endif
|
||||
if (status >= 1) {
|
||||
#if TCPN_LOG_RAW_DATA_OUT >= 1
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
CoutTimestamp(true);
|
||||
std::cout << ": Wrote " << status << " bytes to network. " << inet_ntoa(in) << ":" << GetrPort();
|
||||
std::cout << std::endl;
|
||||
#if TCPN_LOG_RAW_DATA_OUT == 2
|
||||
int32 tmp = status;
|
||||
if (tmp > 32)
|
||||
tmp = 32;
|
||||
DumpPacket(data, status);
|
||||
#elif TCPN_LOG_RAW_DATA_OUT >= 3
|
||||
DumpPacket(data, status);
|
||||
#endif
|
||||
#endif
|
||||
sent_something = true;
|
||||
if (status < (signed)size) {
|
||||
#if TCPN_LOG_RAW_DATA_OUT >= 1
|
||||
struct in_addr in;
|
||||
in.s_addr = GetrIP();
|
||||
CoutTimestamp(true);
|
||||
std::cout << ": Pushed " << (size - status) << " bytes back onto the send queue. " << inet_ntoa(in) << ":" << GetrPort();
|
||||
std::cout << std::endl;
|
||||
#endif
|
||||
// If there's network congestion, the number of bytes sent can be less than
|
||||
// what we tried to give it... Push the extra back on the queue for later
|
||||
ServerSendQueuePushFront(&data[status], size - status);
|
||||
}
|
||||
else if (status > (signed)size) {
|
||||
return false;
|
||||
}
|
||||
// else if (status == size) {}
|
||||
}
|
||||
else {
|
||||
ServerSendQueuePushFront(data, size);
|
||||
}
|
||||
|
||||
safe_delete_array(data);
|
||||
if (status == SOCKET_ERROR) {
|
||||
#ifdef _WINDOWS
|
||||
if (WSAGetLastError() != WSAEWOULDBLOCK)
|
||||
#else
|
||||
if (errno != EWOULDBLOCK)
|
||||
#endif
|
||||
{
|
||||
if (errbuf) {
|
||||
#ifdef _WINDOWS
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::SendData(): send(): Errorcode: %i", WSAGetLastError());
|
||||
#else
|
||||
snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::SendData(): send(): Errorcode: %s", strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
//if we get an error while disconnecting, just jump to disconnected
|
||||
MState.lock();
|
||||
if(pState == TCPS_Disconnecting)
|
||||
pState = TCPS_Disconnected;
|
||||
MState.unlock();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ThreadReturnType TCPConnection::TCPConnectionLoop(void* tmp) {
|
||||
#ifdef _WINDOWS
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
|
||||
#endif
|
||||
if (tmp == 0) {
|
||||
THREAD_RETURN(nullptr);
|
||||
}
|
||||
TCPConnection* tcpc = (TCPConnection*) tmp;
|
||||
#ifndef WIN32
|
||||
Log.Out(Logs::Detail, Logs::TCP_Connection, "%s Starting TCPConnectionLoop with thread ID %d", __FUNCTION__, pthread_self());
|
||||
#endif
|
||||
tcpc->MLoopRunning.lock();
|
||||
while (tcpc->RunLoop()) {
|
||||
Sleep(LOOP_GRANULARITY);
|
||||
if (!tcpc->ConnectReady()) {
|
||||
if (!tcpc->Process()) {
|
||||
//the processing loop has detecting an error..
|
||||
//we want to drop the link immediately, so we clear buffers too.
|
||||
tcpc->ClearBuffers();
|
||||
tcpc->Disconnect();
|
||||
}
|
||||
Sleep(1);
|
||||
}
|
||||
else if (tcpc->GetAsyncConnect()) {
|
||||
if (tcpc->charAsyncConnect)
|
||||
tcpc->Connect(tcpc->charAsyncConnect, tcpc->GetrPort());
|
||||
else
|
||||
tcpc->ConnectIP(tcpc->GetrIP(), tcpc->GetrPort());
|
||||
tcpc->SetAsyncConnect(false);
|
||||
}
|
||||
else
|
||||
Sleep(10); //nothing to do.
|
||||
}
|
||||
tcpc->MLoopRunning.unlock();
|
||||
|
||||
#ifndef WIN32
|
||||
Log.Out(Logs::Detail, Logs::TCP_Connection, "%s Ending TCPConnectionLoop with thread ID %d", __FUNCTION__, pthread_self());
|
||||
#endif
|
||||
|
||||
THREAD_RETURN(nullptr);
|
||||
}
|
||||
|
||||
bool TCPConnection::RunLoop() {
|
||||
bool ret;
|
||||
MRunLoop.lock();
|
||||
ret = pRunLoop;
|
||||
MRunLoop.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef TCP_CONNECTION_H
|
||||
#define TCP_CONNECTION_H
|
||||
/*
|
||||
Parent classes for interserver TCP Communication.
|
||||
-Quagmire
|
||||
*/
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#if (!defined(_MSC_VER) || (defined(_MSC_VER) && _MSC_VER < 1900))
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#define strncasecmp _strnicmp
|
||||
#define strcasecmp _stricmp
|
||||
|
||||
#include <process.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#define INVALID_SOCKET -1
|
||||
#define SOCKET_ERROR -1
|
||||
#include "unix.h"
|
||||
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "mutex.h"
|
||||
#include "queue.h"
|
||||
#include "misc_functions.h"
|
||||
|
||||
|
||||
#define TCPConnection_ErrorBufferSize 1024
|
||||
#define MaxTCPReceiveBuffferSize 524288
|
||||
|
||||
|
||||
#ifndef DEF_eConnectionType
|
||||
#define DEF_eConnectionType
|
||||
enum eConnectionType {Incoming, Outgoing};
|
||||
#endif
|
||||
|
||||
|
||||
class TCPConnection {
|
||||
protected:
|
||||
typedef enum {
|
||||
TCPS_Ready = 0,
|
||||
TCPS_Connecting = 1,
|
||||
TCPS_Connected = 100,
|
||||
TCPS_Disconnecting = 200, //I do not know the difference between Disconnecting and Closing
|
||||
TCPS_Disconnected = 201,
|
||||
TCPS_Closing = 250,
|
||||
TCPS_Error = 255
|
||||
} State_t;
|
||||
|
||||
public:
|
||||
//socket created by a server (incoming)
|
||||
TCPConnection(uint32 ID, SOCKET iSock, uint32 irIP, uint16 irPort);
|
||||
//socket created to connect to a server (outgoing)
|
||||
TCPConnection(); // for outgoing connections
|
||||
|
||||
virtual ~TCPConnection();
|
||||
|
||||
// Functions for outgoing connections
|
||||
bool Connect(const char* irAddress, uint16 irPort, char* errbuf = 0);
|
||||
virtual bool ConnectIP(uint32 irIP, uint16 irPort, char* errbuf = 0);
|
||||
void AsyncConnect(const char* irAddress, uint16 irPort);
|
||||
void AsyncConnect(uint32 irIP, uint16 irPort);
|
||||
virtual void Disconnect();
|
||||
|
||||
bool Send(const uchar* data, int32 size);
|
||||
|
||||
char* PopLine(); //returns ownership of allocated byte array
|
||||
inline uint32 GetrIP() const { return rIP; }
|
||||
inline uint16 GetrPort() const { return rPort; }
|
||||
virtual State_t GetState() const;
|
||||
inline bool Connected() const { return (GetState() == TCPS_Connected); }
|
||||
bool ConnectReady() const;
|
||||
void Free(); // Inform TCPServer that this connection object is no longer referanced
|
||||
|
||||
inline uint32 GetID() const { return id; }
|
||||
|
||||
bool GetEcho();
|
||||
void SetEcho(bool iValue);
|
||||
bool GetSockName(char *host, uint16 *port);
|
||||
|
||||
//should only be used by TCPServer<T>:
|
||||
bool CheckNetActive();
|
||||
inline bool IsFree() const { return pFree; }
|
||||
virtual bool Process();
|
||||
|
||||
protected:
|
||||
friend class BaseTCPServer;
|
||||
void SetState(State_t iState);
|
||||
|
||||
static ThreadReturnType TCPConnectionLoop(void* tmp);
|
||||
// SOCKET sock;
|
||||
bool RunLoop();
|
||||
Mutex MLoopRunning;
|
||||
Mutex MAsyncConnect;
|
||||
bool GetAsyncConnect();
|
||||
bool SetAsyncConnect(bool iValue);
|
||||
char* charAsyncConnect;
|
||||
bool pAsyncConnect; //this flag should really be turned into a state instead.
|
||||
|
||||
virtual bool ProcessReceivedData(char* errbuf = 0);
|
||||
virtual bool SendData(bool &sent_something, char* errbuf = 0);
|
||||
virtual bool RecvData(char* errbuf = 0);
|
||||
|
||||
virtual void ClearBuffers();
|
||||
|
||||
|
||||
bool m_previousLineEnd;
|
||||
|
||||
eConnectionType ConnectionType;
|
||||
Mutex MRunLoop;
|
||||
bool pRunLoop;
|
||||
|
||||
SOCKET connection_socket;
|
||||
uint32 id;
|
||||
uint32 rIP;
|
||||
uint16 rPort; // host byte order
|
||||
bool pFree;
|
||||
|
||||
mutable Mutex MState;
|
||||
State_t pState;
|
||||
|
||||
//text based line out queue.
|
||||
Mutex MLineOutQueue;
|
||||
virtual bool LineOutQueuePush(char* line); //this is really kinda a hack for the transition to packet mode. Returns true to stop processing the output.
|
||||
MyQueue<char> LineOutQueue;
|
||||
|
||||
uchar* recvbuf;
|
||||
int32 recvbuf_size;
|
||||
int32 recvbuf_used;
|
||||
|
||||
int32 recvbuf_echo;
|
||||
volatile bool pEcho;
|
||||
|
||||
Mutex MSendQueue;
|
||||
uchar* sendbuf;
|
||||
int32 sendbuf_size;
|
||||
int32 sendbuf_used;
|
||||
bool ServerSendQueuePop(uchar** data, int32* size);
|
||||
bool ServerSendQueuePopForce(uchar** data, int32* size); //does a lock() instead of a trylock()
|
||||
void ServerSendQueuePushEnd(const uchar* data, int32 size);
|
||||
void ServerSendQueuePushEnd(uchar** data, int32 size);
|
||||
void ServerSendQueuePushFront(uchar* data, int32 size);
|
||||
|
||||
private:
|
||||
void FinishDisconnect();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
#include "global_define.h"
|
||||
#include "tcp_server.h"
|
||||
#include "../common/eqemu_logsys.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#include <process.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define INVALID_SOCKET -1
|
||||
#define SOCKET_ERROR -1
|
||||
#endif
|
||||
|
||||
#define SERVER_LOOP_GRANULARITY 3 //# of ms between checking our socket/queues
|
||||
|
||||
BaseTCPServer::BaseTCPServer(uint16 in_port) {
|
||||
NextID = 1;
|
||||
pPort = in_port;
|
||||
sock = 0;
|
||||
pRunLoop = true;
|
||||
#ifdef _WINDOWS
|
||||
_beginthread(BaseTCPServer::TCPServerLoop, 0, this);
|
||||
#else
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, nullptr, &BaseTCPServer::TCPServerLoop, this);
|
||||
#endif
|
||||
}
|
||||
|
||||
BaseTCPServer::~BaseTCPServer() {
|
||||
StopLoopAndWait();
|
||||
}
|
||||
|
||||
void BaseTCPServer::StopLoopAndWait() {
|
||||
MRunLoop.lock();
|
||||
if(pRunLoop) {
|
||||
pRunLoop = false;
|
||||
MRunLoop.unlock();
|
||||
//wait for loop to stop.
|
||||
MLoopRunning.lock();
|
||||
MLoopRunning.unlock();
|
||||
} else {
|
||||
MRunLoop.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
bool BaseTCPServer::RunLoop() {
|
||||
bool ret;
|
||||
MRunLoop.lock();
|
||||
ret = pRunLoop;
|
||||
MRunLoop.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ThreadReturnType BaseTCPServer::TCPServerLoop(void* tmp) {
|
||||
#ifdef _WINDOWS
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
|
||||
#endif
|
||||
if (tmp == 0) {
|
||||
THREAD_RETURN(nullptr);
|
||||
}
|
||||
BaseTCPServer* tcps = (BaseTCPServer*) tmp;
|
||||
|
||||
#ifndef WIN32
|
||||
Log.Out(Logs::Detail, Logs::None, "Starting TCPServerLoop with thread ID %d", pthread_self());
|
||||
#endif
|
||||
|
||||
tcps->MLoopRunning.lock();
|
||||
while (tcps->RunLoop()) {
|
||||
Sleep(SERVER_LOOP_GRANULARITY);
|
||||
tcps->Process();
|
||||
}
|
||||
tcps->MLoopRunning.unlock();
|
||||
|
||||
#ifndef WIN32
|
||||
Log.Out(Logs::Detail, Logs::None, "Ending TCPServerLoop with thread ID %d", pthread_self());
|
||||
#endif
|
||||
|
||||
THREAD_RETURN(nullptr);
|
||||
}
|
||||
|
||||
void BaseTCPServer::Process() {
|
||||
ListenNewConnections();
|
||||
}
|
||||
|
||||
void BaseTCPServer::ListenNewConnections() {
|
||||
SOCKET tmpsock;
|
||||
struct sockaddr_in from;
|
||||
struct in_addr in;
|
||||
unsigned int fromlen;
|
||||
unsigned short port;
|
||||
|
||||
from.sin_family = AF_INET;
|
||||
fromlen = sizeof(from);
|
||||
LockMutex lock(&MSock);
|
||||
#ifndef DARWIN // Corysia - On OSX, 0 is a valid fd.
|
||||
if (!sock)
|
||||
return;
|
||||
#else
|
||||
if (sock == -1) return;
|
||||
#endif
|
||||
|
||||
// Check for pending connects
|
||||
#ifdef _WINDOWS
|
||||
unsigned long nonblocking = 1;
|
||||
while ((tmpsock = accept(sock, (struct sockaddr*) &from, (int *) &fromlen)) != INVALID_SOCKET) {
|
||||
ioctlsocket (tmpsock, FIONBIO, &nonblocking);
|
||||
#else
|
||||
#ifdef __CYGWIN__
|
||||
while ((tmpsock = accept(sock, (struct sockaddr *) &from, (int *) &fromlen)) != INVALID_SOCKET) {
|
||||
#else
|
||||
while ((tmpsock = accept(sock, (struct sockaddr*) &from, &fromlen)) != INVALID_SOCKET) {
|
||||
#endif
|
||||
fcntl(tmpsock, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k
|
||||
setsockopt(tmpsock, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize));
|
||||
port = from.sin_port;
|
||||
in.s_addr = from.sin_addr.s_addr;
|
||||
|
||||
// New TCP connection, this must consume the socket.
|
||||
CreateNewConnection(GetNextID(), tmpsock, in.s_addr, ntohs(from.sin_port));
|
||||
}
|
||||
}
|
||||
|
||||
bool BaseTCPServer::Open(uint16 in_port, char* errbuf) {
|
||||
if (errbuf)
|
||||
errbuf[0] = 0;
|
||||
LockMutex lock(&MSock);
|
||||
if (sock != 0) {
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPServer_ErrorBufferSize, "Listening socket already open");
|
||||
return false;
|
||||
}
|
||||
if (in_port != 0) {
|
||||
pPort = in_port;
|
||||
}
|
||||
|
||||
#ifdef _WINDOWS
|
||||
SOCKADDR_IN address;
|
||||
unsigned long nonblocking = 1;
|
||||
#else
|
||||
struct sockaddr_in address;
|
||||
#endif
|
||||
int reuse_addr = 1;
|
||||
|
||||
// Setup internet address information.
|
||||
// This is used with the bind() call
|
||||
memset((char *) &address, 0, sizeof(address));
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = htons(pPort);
|
||||
address.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
// Setting up TCP port for new TCP connections
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPServer_ErrorBufferSize, "socket(): INVALID_SOCKET");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Quag: dont think following is good stuff for TCP, good for UDP
|
||||
// Mis: SO_REUSEADDR shouldn't be a problem for tcp--allows you to restart
|
||||
// without waiting for conns in TIME_WAIT to die
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse_addr, sizeof(reuse_addr));
|
||||
|
||||
|
||||
if (bind(sock, (struct sockaddr *) &address, sizeof(address)) < 0) {
|
||||
#ifdef _WINDOWS
|
||||
closesocket(sock);
|
||||
#else
|
||||
close(sock);
|
||||
#endif
|
||||
sock = 0;
|
||||
if (errbuf)
|
||||
sprintf(errbuf, "bind(): <0");
|
||||
return false;
|
||||
}
|
||||
|
||||
int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize));
|
||||
#ifdef _WINDOWS
|
||||
ioctlsocket (sock, FIONBIO, &nonblocking);
|
||||
#else
|
||||
fcntl(sock, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
|
||||
if (listen(sock, SOMAXCONN) == SOCKET_ERROR) {
|
||||
#ifdef _WINDOWS
|
||||
closesocket(sock);
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPServer_ErrorBufferSize, "listen() failed, Error: %d", WSAGetLastError());
|
||||
#else
|
||||
close(sock);
|
||||
if (errbuf)
|
||||
snprintf(errbuf, TCPServer_ErrorBufferSize, "listen() failed, Error: %s", strerror(errno));
|
||||
#endif
|
||||
sock = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BaseTCPServer::Close() {
|
||||
StopLoopAndWait();
|
||||
|
||||
LockMutex lock(&MSock);
|
||||
if (sock) {
|
||||
#ifdef _WINDOWS
|
||||
closesocket(sock);
|
||||
#else
|
||||
close(sock);
|
||||
#endif
|
||||
}
|
||||
sock = 0;
|
||||
}
|
||||
|
||||
bool BaseTCPServer::IsOpen() {
|
||||
MSock.lock();
|
||||
bool ret = (bool) (sock != 0);
|
||||
MSock.unlock();
|
||||
return ret;
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
#ifndef TCPSERVER_H_
|
||||
#define TCPSERVER_H_
|
||||
|
||||
#include "types.h"
|
||||
#include "mutex.h"
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#define TCPServer_ErrorBufferSize 1024
|
||||
|
||||
//this is the non-connection type specific server.
|
||||
class BaseTCPServer {
|
||||
public:
|
||||
BaseTCPServer(uint16 iPort = 0);
|
||||
virtual ~BaseTCPServer();
|
||||
|
||||
bool Open(uint16 iPort = 0, char* errbuf = 0); // opens the port
|
||||
void Close(); // closes the port
|
||||
bool IsOpen();
|
||||
inline uint16 GetPort() { return pPort; }
|
||||
inline uint32 GetNextID() { return NextID++; }
|
||||
|
||||
protected:
|
||||
static ThreadReturnType TCPServerLoop(void* tmp);
|
||||
|
||||
//factory method:
|
||||
virtual void CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort) = 0;
|
||||
|
||||
|
||||
virtual void Process();
|
||||
bool RunLoop();
|
||||
Mutex MLoopRunning;
|
||||
|
||||
void StopLoopAndWait();
|
||||
|
||||
void ListenNewConnections();
|
||||
|
||||
uint32 NextID;
|
||||
|
||||
Mutex MRunLoop;
|
||||
bool pRunLoop;
|
||||
|
||||
Mutex MSock;
|
||||
SOCKET sock;
|
||||
uint16 pPort;
|
||||
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class TCPServer : public BaseTCPServer {
|
||||
protected:
|
||||
typedef typename std::vector<T *> vstore;
|
||||
typedef typename std::vector<T *>::iterator vitr;
|
||||
public:
|
||||
TCPServer(uint16 iPort = 0)
|
||||
: BaseTCPServer(iPort) {
|
||||
}
|
||||
|
||||
virtual ~TCPServer() {
|
||||
StopLoopAndWait();
|
||||
|
||||
//im not sure what the right thing to do here is...
|
||||
//we are freeing a connection which somebody likely has a pointer to..
|
||||
//but, we really shouldent ever get called anyhow..
|
||||
vitr cur, end;
|
||||
cur = m_list.begin();
|
||||
end = m_list.end();
|
||||
for(; cur != end; ++cur) {
|
||||
delete *cur;
|
||||
}
|
||||
}
|
||||
|
||||
T * NewQueuePop() {
|
||||
T * ret = nullptr;
|
||||
MNewQueue.lock();
|
||||
if(!m_NewQueue.empty()) {
|
||||
ret = m_NewQueue.front();
|
||||
m_NewQueue.pop();
|
||||
}
|
||||
MNewQueue.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void Process() {
|
||||
BaseTCPServer::Process();
|
||||
|
||||
vitr cur;
|
||||
cur = m_list.begin();
|
||||
while(cur != m_list.end()) {
|
||||
T *data = *cur;
|
||||
if (data->IsFree() && (!data->CheckNetActive())) {
|
||||
#if EQN_DEBUG >= 4
|
||||
std::cout << "TCPConnection Connection deleted." << std::endl;
|
||||
#endif
|
||||
delete data;
|
||||
cur = m_list.erase(cur);
|
||||
} else {
|
||||
if (!data->Process())
|
||||
data->Disconnect();
|
||||
++cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddConnection(T *con) {
|
||||
m_list.push_back(con);
|
||||
MNewQueue.lock();
|
||||
m_NewQueue.push(con);
|
||||
MNewQueue.unlock();
|
||||
}
|
||||
|
||||
//queue of new connections, for the app to pull from
|
||||
Mutex MNewQueue;
|
||||
std::queue<T *> m_NewQueue;
|
||||
|
||||
vstore m_list;
|
||||
};
|
||||
|
||||
|
||||
#endif /*TCPSERVER_H_*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../common/global_define.h"
|
||||
#include "../common/eqemu_logsys.h"
|
||||
#include <iomanip>
|
||||
|
||||
#include "worldconn.h"
|
||||
#include "eqemu_config.h"
|
||||
#include "md5.h"
|
||||
#include "servertalk.h"
|
||||
|
||||
WorldConnection::WorldConnection(EmuTCPConnection::ePacketMode mode, const char *password)
|
||||
: m_password(password)
|
||||
{
|
||||
tcpc.SetPacketMode(mode);
|
||||
pTryReconnect = true;
|
||||
pConnected = false;
|
||||
}
|
||||
|
||||
WorldConnection::~WorldConnection() {
|
||||
}
|
||||
|
||||
bool WorldConnection::SendPacket(ServerPacket* pack) {
|
||||
if (!Connected())
|
||||
return false;
|
||||
return tcpc.SendPacket(pack);
|
||||
}
|
||||
|
||||
void WorldConnection::OnConnected() {
|
||||
const EQEmuConfig *Config=EQEmuConfig::get();
|
||||
Log.Out(Logs::General, Logs::Netcode, "[WORLD] Connected to World: %s:%d", Config->WorldIP.c_str(), Config->WorldTCPPort);
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_ZAAuth, 16);
|
||||
MD5::Generate((const uchar*) m_password.c_str(), m_password.length(), pack->pBuffer);
|
||||
SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
void WorldConnection::Process() {
|
||||
//persistent connection....
|
||||
if (!Connected()) {
|
||||
pConnected = tcpc.Connected();
|
||||
if (pConnected) {
|
||||
OnConnected();
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void WorldConnection::AsyncConnect() {
|
||||
const EQEmuConfig *Config=EQEmuConfig::get();
|
||||
tcpc.AsyncConnect(Config->WorldIP.c_str(), Config->WorldTCPPort);
|
||||
}
|
||||
|
||||
bool WorldConnection::Connect() {
|
||||
const EQEmuConfig *Config=EQEmuConfig::get();
|
||||
char errbuf[TCPConnection_ErrorBufferSize];
|
||||
if (tcpc.Connect(Config->WorldIP.c_str(), Config->WorldTCPPort, errbuf)) {
|
||||
return true;
|
||||
} else {
|
||||
Log.Out(Logs::General, Logs::Netcode, "[WORLD] WorldConnection connect: Connecting to the server %s:%d failed: %s", Config->WorldIP.c_str(), Config->WorldTCPPort, errbuf);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WorldConnection::Disconnect() {
|
||||
tcpc.Disconnect();
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef WORLDCONNECTION_H
|
||||
#define WORLDCONNECTION_H
|
||||
|
||||
#include "../common/emu_tcp_connection.h"
|
||||
#include <string>
|
||||
|
||||
class ServerPacket;
|
||||
|
||||
/*
|
||||
* This object is an arbitrary connection to world.
|
||||
*/
|
||||
class WorldConnection {
|
||||
public:
|
||||
WorldConnection(EmuTCPConnection::ePacketMode mode, const char *password = "");
|
||||
virtual ~WorldConnection();
|
||||
|
||||
virtual void Process();
|
||||
bool SendPacket(ServerPacket* pack);
|
||||
|
||||
uint32 GetIP() const { return tcpc.GetrIP(); }
|
||||
uint16 GetPort() const { return tcpc.GetrPort(); }
|
||||
bool Connected() const { return (pConnected && tcpc.Connected()); }
|
||||
|
||||
void SetPassword(const char *password) { m_password = password; }
|
||||
bool Connect();
|
||||
void AsyncConnect();
|
||||
void Disconnect();
|
||||
inline bool TryReconnect() const { return pTryReconnect; }
|
||||
|
||||
protected:
|
||||
virtual void OnConnected();
|
||||
|
||||
std::string m_password;
|
||||
EmuTCPConnection tcpc;
|
||||
bool pTryReconnect;
|
||||
bool pConnected;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user