From 0b8b41d91f8868cf9a932b4e9cbd6f8383f61874 Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 28 Oct 2016 19:02:03 -0700 Subject: [PATCH] World <-> Login connection reworked --- CMakeLists.txt | 13 +- common/eq_stream_intf.h | 1 + common/eq_stream_proxy.cpp | 4 + common/eq_stream_proxy.h | 1 + common/net/eqstream.cpp | 5 + common/net/eqstream.h | 1 + common/net/servertalk_client_connection.cpp | 156 ++++++++-- common/net/servertalk_client_connection.h | 14 +- common/net/servertalk_common.h | 3 + common/net/servertalk_server.cpp | 3 +- common/net/servertalk_server.h | 6 + common/net/servertalk_server_connection.cpp | 145 ++++++++- common/net/servertalk_server_connection.h | 18 +- common/net/tcp_connection.cpp | 36 +++ common/net/tcp_connection.h | 3 + common/servertalk.h | 2 +- loginserver/CMakeLists.txt | 20 +- loginserver/main.cpp | 1 - loginserver/server_manager.cpp | 148 ++++----- loginserver/server_manager.h | 17 +- loginserver/world_server.cpp | 315 +++++++++----------- loginserver/world_server.h | 19 +- ucs/ucs.cpp | 22 +- world/console.cpp | 12 - world/eqw.cpp | 78 ----- world/eqw.h | 1 - world/login_server.cpp | 265 +++++++--------- world/login_server.h | 18 +- world/login_server_list.cpp | 32 -- world/login_server_list.h | 9 - world/net.cpp | 38 ++- world/perl_eqw.cpp | 24 -- 32 files changed, 729 insertions(+), 701 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a4cf4e529..3d0cfed83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -335,9 +335,12 @@ SET(SERVER_LIBS common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RE FIND_PACKAGE(Sodium REQUIRED) IF(SODIUM_FOUND) - INCLUDE_DIRECTORIES(SYSTEM "${SODIUM_INCLUDE_DIRS}") - ADD_DEFINITIONS(-DENABLE_SECURITY) - SET(SERVER_LIBS ${SERVER_LIBS} ${SODIUM_LIBRARIES}) + OPTION(EQEMU_ENABLE_SECURITY "Use Encryption For TCP Connections" ON) + IF(EQEMU_ENABLE_SECURITY) + INCLUDE_DIRECTORIES(SYSTEM "${SODIUM_INCLUDE_DIRS}") + ADD_DEFINITIONS(-DENABLE_SECURITY) + SET(SERVER_LIBS ${SERVER_LIBS} ${SODIUM_LIBRARIES}) + ENDIF() ENDIF() IF(WIN32) @@ -384,9 +387,9 @@ IF(EQEMU_BUILD_SERVER) ADD_SUBDIRECTORY(shared_memory) ADD_SUBDIRECTORY(world) ADD_SUBDIRECTORY(zone) - ADD_SUBDIRECTORY(ucs) + #ADD_SUBDIRECTORY(ucs) #ADD_SUBDIRECTORY(queryserv) - ADD_SUBDIRECTORY(eqlaunch) + #ADD_SUBDIRECTORY(eqlaunch) ENDIF(EQEMU_BUILD_SERVER) IF(EQEMU_BUILD_LOGIN) ADD_SUBDIRECTORY(loginserver) diff --git a/common/eq_stream_intf.h b/common/eq_stream_intf.h index 40358998f..c65ae9348 100644 --- a/common/eq_stream_intf.h +++ b/common/eq_stream_intf.h @@ -41,6 +41,7 @@ public: virtual void Close() = 0; virtual void ReleaseFromUse() = 0; virtual void RemoveData() = 0; + virtual std::string GetRemoteAddr() const = 0; virtual uint32 GetRemoteIP() const = 0; virtual uint16 GetRemotePort() const = 0; virtual bool CheckState(EQStreamState state) = 0; diff --git a/common/eq_stream_proxy.cpp b/common/eq_stream_proxy.cpp index 1d6a3bd08..7fb4c9432 100644 --- a/common/eq_stream_proxy.cpp +++ b/common/eq_stream_proxy.cpp @@ -63,6 +63,10 @@ void EQStreamProxy::Close() { m_stream->Close(); } +std::string EQStreamProxy::GetRemoteAddr() const { + return(m_stream->GetRemoteAddr()); +} + uint32 EQStreamProxy::GetRemoteIP() const { return(m_stream->GetRemoteIP()); } diff --git a/common/eq_stream_proxy.h b/common/eq_stream_proxy.h index 602c59a83..5e01c8cf0 100644 --- a/common/eq_stream_proxy.h +++ b/common/eq_stream_proxy.h @@ -21,6 +21,7 @@ public: virtual void FastQueuePacket(EQApplicationPacket **p, bool ack_req=true); virtual EQApplicationPacket *PopPacket(); virtual void Close(); + virtual std::string GetRemoteAddr() const; virtual uint32 GetRemoteIP() const; virtual uint16 GetRemotePort() const; virtual void ReleaseFromUse(); diff --git a/common/net/eqstream.cpp b/common/net/eqstream.cpp index dee97df01..0825d1ce7 100644 --- a/common/net/eqstream.cpp +++ b/common/net/eqstream.cpp @@ -132,6 +132,11 @@ void EQ::Net::EQStream::Close() { m_connection->Close(); } +std::string EQ::Net::EQStream::GetRemoteAddr() const +{ + return RemoteEndpoint(); +} + uint32 EQ::Net::EQStream::GetRemoteIP() const { return inet_addr(RemoteEndpoint().c_str()); } diff --git a/common/net/eqstream.h b/common/net/eqstream.h index a24aeacec..ff27bb0b9 100644 --- a/common/net/eqstream.h +++ b/common/net/eqstream.h @@ -75,6 +75,7 @@ namespace EQ virtual void Close(); virtual void ReleaseFromUse() { }; virtual void RemoveData() { }; + virtual std::string GetRemoteAddr() const; virtual uint32 GetRemoteIP() const; virtual uint16 GetRemotePort() const { return m_connection->RemotePort(); } virtual bool CheckState(EQStreamState state); diff --git a/common/net/servertalk_client_connection.cpp b/common/net/servertalk_client_connection.cpp index 1c2117d55..e3fe67817 100644 --- a/common/net/servertalk_client_connection.cpp +++ b/common/net/servertalk_client_connection.cpp @@ -19,6 +19,45 @@ EQ::Net::ServertalkClient::~ServertalkClient() { } +void EQ::Net::ServertalkClient::Send(uint16_t opcode, EQ::Net::Packet & p) +{ + EQ::Net::WritablePacket out; +#ifdef ENABLE_SECURITY + if (m_encrypted) { + out.PutUInt32(0, p.Length() + crypto_secretbox_MACBYTES); + out.PutUInt16(4, opcode); + unsigned char *cipher = new unsigned char[p.Length() + crypto_secretbox_MACBYTES]; + + crypto_box_easy_afternm(cipher, (unsigned char*)p.Data(), p.Length(), m_nonce_ours, m_shared_key); + (*(uint64_t*)&m_nonce_ours[0])++; + out.PutData(6, cipher, p.Length() + crypto_secretbox_MACBYTES); + + delete[] cipher; + } + else { + out.PutUInt32(0, p.Length()); + out.PutUInt16(4, opcode); + out.PutPacket(6, p); + } +#else + out.PutUInt32(0, p.Length()); + out.PutUInt16(4, opcode); + out.PutPacket(6, p); +#endif + InternalSend(ServertalkMessage, out); +} + +void EQ::Net::ServertalkClient::SendPacket(ServerPacket *p) +{ + EQ::Net::ReadOnlyPacket pout(p->pBuffer, p->size); + Send(p->opcode, pout); +} + +void EQ::Net::ServertalkClient::OnMessage(uint16_t opcode, std::function cb) +{ + m_message_callbacks.insert(std::make_pair(opcode, cb)); +} + void EQ::Net::ServertalkClient::Connect() { if (m_addr.length() == 0 || m_port == 0 || m_connection || m_connecting) { @@ -37,8 +76,8 @@ void EQ::Net::ServertalkClient::Connect() m_connection = connection; m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) { Log.OutF(Logs::General, Logs::TCP_Connection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port); - m_connection.reset(); m_encrypted = false; + m_connection.reset(); }); m_connection->OnRead(std::bind(&EQ::Net::ServertalkClient::ProcessData, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); @@ -98,7 +137,7 @@ void EQ::Net::ServertalkClient::ProcessReadBuffer() length = *(uint32_t*)&m_buffer[current]; type = *(uint8_t*)&m_buffer[current + 4]; - if (current + 5 + length < total) { + if (current + 5 + length > total) { break; } @@ -144,6 +183,7 @@ void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p) memset(m_private_key_ours, 0, crypto_box_SECRETKEYBYTES); memset(m_nonce_ours, 0, crypto_box_NONCEBYTES); memset(m_nonce_theirs, 0, crypto_box_NONCEBYTES); + memset(m_shared_key, 0, crypto_box_BEFORENMBYTES); m_encrypted = false; try { @@ -156,6 +196,10 @@ void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p) m_encrypted = true; SendHandshake(); + + if (m_on_connect_cb) { + m_on_connect_cb(this); + } } else { Log.OutF(Logs::General, Logs::Error, "Could not process hello, size != {0}", 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES); @@ -163,37 +207,104 @@ void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p) } else { SendHandshake(); + + if (m_on_connect_cb) { + m_on_connect_cb(this); + } } } catch (std::exception &ex) { Log.OutF(Logs::General, Logs::Error, "Error parsing hello from server: {0}", ex.what()); m_connection->Disconnect(); + + if (m_on_connect_cb) { + m_on_connect_cb(nullptr); + } } #else try { bool enc = p.GetInt8(0) == 1 ? true : false; if (enc) { - Log.OutF(Logs::General, Logs::Error, "Server requested encryption but we do not support encryption."); - m_connection->Disconnect(); - return; + SendHandshake(true); + + if (m_on_connect_cb) { + m_on_connect_cb(this); + } } else { SendHandshake(); + + if (m_on_connect_cb) { + m_on_connect_cb(this); + } } } catch (std::exception &ex) { Log.OutF(Logs::General, Logs::Error, "Error parsing hello from server: {0}", ex.what()); m_connection->Disconnect(); + + if (m_on_connect_cb) { + m_on_connect_cb(nullptr); + } } #endif } void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p) { + try { + auto length = p.GetUInt32(0); + auto opcode = p.GetUInt16(4); + if (length > 0) { + auto data = p.GetString(6, length); +#ifdef ENABLE_SECURITY + if (m_encrypted) { + size_t message_len = length - crypto_secretbox_MACBYTES; + std::unique_ptr 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 server"); + (*(uint64_t*)&m_nonce_theirs[0])++; + return; + } + + EQ::Net::ReadOnlyPacket decrypted_packet(&decrypted_text[0], message_len); + + (*(uint64_t*)&m_nonce_theirs[0])++; + + auto cb = m_message_callbacks.find(opcode); + if (cb != m_message_callbacks.end()) { + cb->second(opcode, decrypted_packet); + } + } + else { + size_t message_len = length; + EQ::Net::ReadOnlyPacket packet(&data[0], message_len); + + auto cb = m_message_callbacks.find(opcode); + if (cb != m_message_callbacks.end()) { + cb->second(opcode, packet); + } + } + +#else + size_t message_len = length; + EQ::Net::ReadOnlyPacket packet(&data[0], message_len); + + auto cb = m_message_callbacks.find(opcode); + if (cb != m_message_callbacks.end()) { + cb->second(opcode, packet); + } +#endif + } + } + catch (std::exception &ex) { + Log.OutF(Logs::General, Logs::Error, "Error parsing message from server: {0}", ex.what()); + } } -void EQ::Net::ServertalkClient::SendHandshake() +void EQ::Net::ServertalkClient::SendHandshake(bool downgrade) { EQ::Net::WritablePacket handshake; #ifdef ENABLE_SECURITY @@ -206,30 +317,41 @@ void EQ::Net::ServertalkClient::SendHandshake() handshake.PutData(0, m_public_key_ours, crypto_box_PUBLICKEYBYTES); handshake.PutData(crypto_box_PUBLICKEYBYTES, m_nonce_ours, crypto_box_NONCEBYTES); + memset(m_public_key_ours, 0, crypto_box_PUBLICKEYBYTES); + memset(m_public_key_theirs, 0, crypto_box_PUBLICKEYBYTES); + memset(m_private_key_ours, 0, crypto_box_SECRETKEYBYTES); + size_t cipher_length = m_identifier.length() + 1 + m_credentials.length() + 1 + crypto_secretbox_MACBYTES; size_t data_length = m_identifier.length() + 1 + m_credentials.length() + 1; - unsigned char *signed_buffer = new unsigned char[cipher_length]; - unsigned char *data_buffer = new unsigned char[data_length]; - memset(data_buffer, 0, data_length); + + std::unique_ptr signed_buffer(new unsigned char[cipher_length]); + std::unique_ptr data_buffer(new unsigned char[data_length]); + + memset(&data_buffer[0], 0, data_length); memcpy(&data_buffer[0], m_identifier.c_str(), m_identifier.length()); memcpy(&data_buffer[1 + m_identifier.length()], m_credentials.c_str(), m_credentials.length()); - crypto_box_easy_afternm(signed_buffer, data_buffer, data_length, m_nonce_ours, m_shared_key); + crypto_box_easy_afternm(&signed_buffer[0], &data_buffer[0], data_length, m_nonce_ours, m_shared_key); (*(uint64_t*)&m_nonce_ours[0])++; - handshake.PutData(crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, signed_buffer, cipher_length); - - Log.OutF(Logs::General, Logs::Debug, "Sending {1} bytes handshake:\n{0}", handshake.ToString(), handshake.Length()); - - delete[] signed_buffer; - delete[] data_buffer; + handshake.PutData(crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, &signed_buffer[0], cipher_length); } else { handshake.PutString(0, m_identifier); + handshake.PutString(m_identifier.length() + 1, m_credentials); + handshake.PutUInt8(m_identifier.length() + 1 + m_credentials.length(), 0); } #else handshake.PutString(0, m_identifier); + handshake.PutString(m_identifier.length() + 1, m_credentials); + handshake.PutUInt8(m_identifier.length() + 1 + m_credentials.length(), 0); #endif - InternalSend(ServertalkClientHandshake, handshake); + + if (downgrade) { + InternalSend(ServertalkClientDowngradeSecurityHandshake, handshake); + } + else { + InternalSend(ServertalkClientHandshake, handshake); + } } diff --git a/common/net/servertalk_client_connection.h b/common/net/servertalk_client_connection.h index 61513dada..929c4dc98 100644 --- a/common/net/servertalk_client_connection.h +++ b/common/net/servertalk_client_connection.h @@ -4,7 +4,9 @@ #include "../event/timer.h" #include "servertalk_common.h" #include "packet.h" +#ifdef ENABLE_SECURITY #include +#endif namespace EQ { @@ -16,6 +18,13 @@ namespace EQ ServertalkClient(const std::string &addr, int port, bool ipv6, const std::string &identifier, const std::string &credentials); ~ServertalkClient(); + void Send(uint16_t opcode, EQ::Net::Packet &p); + void SendPacket(ServerPacket *p); + void OnConnect(std::function cb) { m_on_connect_cb = cb; } + void OnMessage(uint16_t opcode, std::function cb); + bool Connected() const { return m_connecting != true; } + + std::shared_ptr Handle() { return m_connection; } private: void Connect(); void ProcessData(EQ::Net::TCPConnection *c, const unsigned char *data, size_t length); @@ -24,7 +33,8 @@ namespace EQ void ProcessReadBuffer(); void ProcessHello(EQ::Net::Packet &p); void ProcessMessage(EQ::Net::Packet &p); - void SendHandshake(); + void SendHandshake() { SendHandshake(false); } + void SendHandshake(bool downgrade); std::unique_ptr m_timer; @@ -37,6 +47,8 @@ namespace EQ bool m_encrypted; std::shared_ptr m_connection; std::vector m_buffer; + std::unordered_map> m_message_callbacks; + std::function m_on_connect_cb; #ifdef ENABLE_SECURITY unsigned char m_public_key_ours[crypto_box_PUBLICKEYBYTES]; diff --git a/common/net/servertalk_common.h b/common/net/servertalk_common.h index 2accfa0af..f30d21b00 100644 --- a/common/net/servertalk_common.h +++ b/common/net/servertalk_common.h @@ -1,5 +1,7 @@ #pragma once +#include "../servertalk.h" + namespace EQ { namespace Net @@ -9,6 +11,7 @@ namespace EQ ServertalkClientHello = 1, ServertalkServerHello, ServertalkClientHandshake, + ServertalkClientDowngradeSecurityHandshake, ServertalkMessage, }; } diff --git a/common/net/servertalk_server.cpp b/common/net/servertalk_server.cpp index 1fe2f6d26..fcbea917c 100644 --- a/common/net/servertalk_server.cpp +++ b/common/net/servertalk_server.cpp @@ -12,9 +12,10 @@ void EQ::Net::ServertalkServer::Listen(const ServertalkServerOptions& opts) { m_encrypted = opts.encrypted; m_credentials = opts.credentials; + m_allow_downgrade = opts.allow_downgrade; m_server.reset(new EQ::Net::TCPServer()); m_server->Listen(opts.port, opts.ipv6, [this](std::shared_ptr connection) { - m_unident_connections.push_back(std::make_shared(connection, this, m_encrypted)); + m_unident_connections.push_back(std::make_shared(connection, this, m_encrypted, m_allow_downgrade)); }); } diff --git a/common/net/servertalk_server.h b/common/net/servertalk_server.h index 262732322..e6062d847 100644 --- a/common/net/servertalk_server.h +++ b/common/net/servertalk_server.h @@ -18,11 +18,16 @@ namespace EQ int port; bool ipv6; bool encrypted; + bool allow_downgrade; std::string credentials; ServertalkServerOptions() { #ifdef ENABLE_SECURITY encrypted = true; + allow_downgrade = true; +#else + encrypted = false; + allow_downgrade = true; #endif ipv6 = false; } @@ -50,6 +55,7 @@ namespace EQ std::map)>> m_on_ident; std::map)>> m_on_disc; bool m_encrypted; + bool m_allow_downgrade; std::string m_credentials; friend class ServertalkServerConnection; diff --git a/common/net/servertalk_server_connection.cpp b/common/net/servertalk_server_connection.cpp index a8f999e07..98fbc07b3 100644 --- a/common/net/servertalk_server_connection.cpp +++ b/common/net/servertalk_server_connection.cpp @@ -2,11 +2,12 @@ #include "servertalk_server.h" #include "../eqemu_logsys.h" -EQ::Net::ServertalkServerConnection::ServertalkServerConnection(std::shared_ptr c, EQ::Net::ServertalkServer *parent, bool encrypted) +EQ::Net::ServertalkServerConnection::ServertalkServerConnection(std::shared_ptr c, EQ::Net::ServertalkServer *parent, bool encrypted, bool allow_downgrade) { m_connection = c; m_parent = parent; m_encrypted = encrypted; + m_allow_downgrade = allow_downgrade; m_connection->OnRead(std::bind(&ServertalkServerConnection::OnRead, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); m_connection->OnDisconnect(std::bind(&ServertalkServerConnection::OnDisconnect, this, std::placeholders::_1)); m_connection->Start(); @@ -16,6 +17,43 @@ EQ::Net::ServertalkServerConnection::~ServertalkServerConnection() { } +void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet & p) +{ + EQ::Net::WritablePacket out; +#ifdef ENABLE_SECURITY + if (m_encrypted) { + out.PutUInt32(0, p.Length() + crypto_secretbox_MACBYTES); + out.PutUInt16(4, opcode); + std::unique_ptr 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); + } + else { + out.PutUInt32(0, p.Length()); + out.PutUInt16(4, opcode); + out.PutPacket(6, p); + } +#else + out.PutUInt32(0, p.Length()); + out.PutUInt16(4, opcode); + out.PutPacket(6, p); +#endif + InternalSend(ServertalkMessage, out); +} + +void EQ::Net::ServertalkServerConnection::SendPacket(ServerPacket * p) +{ + EQ::Net::ReadOnlyPacket pout(p->pBuffer, p->size); + Send(p->opcode, pout); +} + +void EQ::Net::ServertalkServerConnection::OnMessage(uint16_t opcode, std::function cb) +{ + m_message_callbacks.insert(std::make_pair(opcode, cb)); +} + void EQ::Net::ServertalkServerConnection::OnRead(TCPConnection *c, const unsigned char *data, size_t sz) { m_buffer.insert(m_buffer.end(), (const char*)data, (const char*)data + sz); @@ -44,7 +82,7 @@ void EQ::Net::ServertalkServerConnection::ProcessReadBuffer() length = *(uint32_t*)&m_buffer[current]; type = *(uint8_t*)&m_buffer[current + 4]; - if (current + 5 + length < total) { + if (current + 5 + length > total) { break; } @@ -75,6 +113,9 @@ void EQ::Net::ServertalkServerConnection::ProcessReadBuffer() case ServertalkClientHandshake: ProcessHandshake(p); break; + case ServertalkClientDowngradeSecurityHandshake: + ProcessHandshake(p, true); + break; case ServertalkMessage: ProcessMessage(p); break; @@ -142,9 +183,15 @@ void EQ::Net::ServertalkServerConnection::InternalSend(ServertalkPacketType type m_connection->Write((const char*)out.Data(), out.Length()); } -void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p) +void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, bool downgrade_security) { #ifdef ENABLE_SECURITY + if (downgrade_security && m_allow_downgrade && m_encrypted) { + Log.OutF(Logs::General, Logs::TCP_Connection, "Downgraded encrypted connection to plaintext because otherside didn't support encryption {0}:{1}", + m_connection->RemoteIP(), m_connection->RemotePort()); + m_encrypted = false; + } + if (m_encrypted) { try { if (p.Length() > (crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES)) { @@ -155,16 +202,16 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p) size_t cipher_len = p.Length() - crypto_box_PUBLICKEYBYTES - crypto_box_NONCEBYTES; size_t message_len = cipher_len - crypto_secretbox_MACBYTES; - unsigned char *decrypted_text = new unsigned char[message_len]; - if (crypto_box_open_easy_afternm(decrypted_text, (unsigned char*)p.Data() + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, cipher_len, m_nonce_theirs, m_shared_key)) + std::unique_ptr 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."); m_connection->Disconnect(); return; } - m_identifier = (const char*)decrypted_text; - std::string credentials = (const char*)decrypted_text + (m_identifier.length() + 1); + m_identifier = (const char*)&decrypted_text[0]; + std::string credentials = (const char*)&decrypted_text[0] + (m_identifier.length() + 1); if (!m_parent->CheckCredentials(credentials)) { Log.OutF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection."); @@ -175,7 +222,6 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p) m_parent->ConnectionIdentified(this); (*(uint64_t*)&m_nonce_theirs[0])++; - delete[] decrypted_text; } } catch (std::exception &ex) { @@ -184,13 +230,92 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p) } } else { - m_identifier.assign((char*)p.Data(), p.Length()); + try { + m_identifier = p.GetCString(0); + auto credentials = p.GetCString(m_identifier.length() + 1); + + if (!m_parent->CheckCredentials(credentials)) { + Log.OutF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection."); + m_connection->Disconnect(); + return; + } + + m_parent->ConnectionIdentified(this); + } + catch (std::exception &ex) { + Log.OutF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what()); + m_connection->Disconnect(); + } } #else - m_identifier.assign((char*)p.Data(), p.Length()); + try { + m_identifier = p.GetCString(0); + auto credentials = p.GetCString(m_identifier.length() + 1); + + if (!m_parent->CheckCredentials(credentials)) { + Log.OutF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection."); + m_connection->Disconnect(); + return; + } + + m_parent->ConnectionIdentified(this); + } + catch (std::exception &ex) { + Log.OutF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what()); + m_connection->Disconnect(); + } #endif } void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p) { + try { + auto length = p.GetUInt32(0); + auto opcode = p.GetUInt16(4); + if (length > 0) { + auto data = p.GetString(6, length); +#ifdef ENABLE_SECURITY + if (m_encrypted) { + size_t message_len = length - crypto_secretbox_MACBYTES; + std::unique_ptr 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"); + (*(uint64_t*)&m_nonce_theirs[0])++; + return; + } + + EQ::Net::ReadOnlyPacket decrypted_packet(&decrypted_text[0], message_len); + + (*(uint64_t*)&m_nonce_theirs[0])++; + + auto cb = m_message_callbacks.find(opcode); + if (cb != m_message_callbacks.end()) { + cb->second(opcode, decrypted_packet); + } + } + else { + size_t message_len = length; + EQ::Net::ReadOnlyPacket packet(&data[0], message_len); + + auto cb = m_message_callbacks.find(opcode); + if (cb != m_message_callbacks.end()) { + cb->second(opcode, packet); + } + } + +#else + size_t message_len = length; + EQ::Net::ReadOnlyPacket packet(&data[0], message_len); + + auto cb = m_message_callbacks.find(opcode); + if (cb != m_message_callbacks.end()) { + cb->second(opcode, packet); + } +#endif + } + } + catch (std::exception &ex) { + Log.OutF(Logs::General, Logs::Error, "Error parsing message from client: {0}", ex.what()); + } } diff --git a/common/net/servertalk_server_connection.h b/common/net/servertalk_server_connection.h index c1859b890..0e496e920 100644 --- a/common/net/servertalk_server_connection.h +++ b/common/net/servertalk_server_connection.h @@ -4,7 +4,9 @@ #include "servertalk_common.h" #include "packet.h" #include +#ifdef ENABLE_SECURITY #include +#endif namespace EQ { @@ -14,28 +16,34 @@ namespace EQ class ServertalkServerConnection { public: - ServertalkServerConnection(std::shared_ptr c, ServertalkServer *parent, bool encrypted); + ServertalkServerConnection(std::shared_ptr c, ServertalkServer *parent, bool encrypted, bool allow_downgrade); ~ServertalkServerConnection(); - std::string GetIdentifier() const { - return m_identifier; - } + void Send(uint16_t opcode, EQ::Net::Packet &p); + void SendPacket(ServerPacket *p); + void OnMessage(uint16_t opcode, std::function cb); + + std::string GetIdentifier() const { return m_identifier; } + std::shared_ptr Handle() { return m_connection; } private: void OnRead(TCPConnection* c, const unsigned char* data, size_t sz); void ProcessReadBuffer(); void OnDisconnect(TCPConnection* c); void SendHello(); void InternalSend(ServertalkPacketType type, EQ::Net::Packet &p); - void ProcessHandshake(EQ::Net::Packet &p); + void ProcessHandshake(EQ::Net::Packet &p) { ProcessHandshake(p, false); } + void ProcessHandshake(EQ::Net::Packet &p, bool security_downgrade); void ProcessMessage(EQ::Net::Packet &p); std::shared_ptr m_connection; ServertalkServer *m_parent; std::vector m_buffer; + std::unordered_map> m_message_callbacks; std::string m_identifier; bool m_encrypted; + bool m_allow_downgrade; #ifdef ENABLE_SECURITY unsigned char m_public_key_ours[crypto_box_PUBLICKEYBYTES]; unsigned char m_private_key_ours[crypto_box_SECRETKEYBYTES]; diff --git a/common/net/tcp_connection.cpp b/common/net/tcp_connection.cpp index be5266104..62c049d0e 100644 --- a/common/net/tcp_connection.cpp +++ b/common/net/tcp_connection.cpp @@ -151,6 +151,42 @@ void EQ::Net::TCPConnection::Write(const char *data, size_t count) }); } +std::string EQ::Net::TCPConnection::LocalIP() const +{ + sockaddr_storage addr; + int addr_len = sizeof(addr); + uv_tcp_getsockname(m_socket, (sockaddr*)&addr, &addr_len); + + char endpoint[64] = { 0 }; + if (addr.ss_family == AF_INET) { + uv_ip4_name((const sockaddr_in*)&addr, endpoint, 64); + } + else if (addr.ss_family == AF_INET6) { + uv_ip6_name((const sockaddr_in6*)&addr, endpoint, 64); + } + + return endpoint; +} + +int EQ::Net::TCPConnection::LocalPort() const +{ + sockaddr_storage addr; + int addr_len = sizeof(addr); + uv_tcp_getsockname(m_socket, (sockaddr*)&addr, &addr_len); + + char endpoint[64] = { 0 }; + if (addr.ss_family == AF_INET) { + sockaddr_in *s = (sockaddr_in*)&addr; + return ntohs(s->sin_port); + } + else if (addr.ss_family == AF_INET6) { + sockaddr_in6 *s = (sockaddr_in6*)&addr; + return ntohs(s->sin6_port); + } + + return 0; +} + std::string EQ::Net::TCPConnection::RemoteIP() const { sockaddr_storage addr; diff --git a/common/net/tcp_connection.h b/common/net/tcp_connection.h index 595b79018..c69671da8 100644 --- a/common/net/tcp_connection.h +++ b/common/net/tcp_connection.h @@ -23,6 +23,9 @@ namespace EQ void Disconnect(); void Read(const char *data, size_t count); void Write(const char *data, size_t count); + + std::string LocalIP() const; + int LocalPort() const; std::string RemoteIP() const; int RemotePort() const; private: diff --git a/common/servertalk.h b/common/servertalk.h index b0197985f..7de9491b7 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -530,7 +530,7 @@ struct ClientAuth_Struct { char key[30]; // the Key the client will present uint8 lsadmin; // login server admin level int16 worldadmin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it - uint32 ip; + char ip[64]; uint8 local; // 1 if the client is from the local network }; diff --git a/loginserver/CMakeLists.txt b/loginserver/CMakeLists.txt index 5a3c1d0d2..ac1f4320b 100644 --- a/loginserver/CMakeLists.txt +++ b/loginserver/CMakeLists.txt @@ -41,24 +41,6 @@ ADD_EXECUTABLE(loginserver ${eqlogin_sources} ${eqlogin_headers}) INSTALL(TARGETS loginserver RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) -TARGET_LINK_LIBRARIES(loginserver common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} ${ZLIB_LIBRARY} libuv fmt) - -IF(WIN32) - TARGET_LINK_LIBRARIES(loginserver "ws2_32" "psapi" "iphlpapi" "userenv") -ENDIF(WIN32) - - -IF(UNIX) - TARGET_LINK_LIBRARIES(loginserver "${CMAKE_DL_LIBS}") - TARGET_LINK_LIBRARIES(loginserver "z") - TARGET_LINK_LIBRARIES(loginserver "m") - IF(NOT DARWIN) - TARGET_LINK_LIBRARIES(loginserver "rt") - ENDIF(NOT DARWIN) - TARGET_LINK_LIBRARIES(loginserver "pthread") - TARGET_LINK_LIBRARIES(loginserver "EQEmuAuthCrypto") - TARGET_LINK_LIBRARIES(loginserver "cryptopp") - ADD_DEFINITIONS(-fPIC) -ENDIF(UNIX) +TARGET_LINK_LIBRARIES(loginserver ${SERVER_LIBS}) SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) diff --git a/loginserver/main.cpp b/loginserver/main.cpp index 4ddb7fbd4..7bd842a40 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -197,7 +197,6 @@ int main() while (run_server) { Timer::SetCurrentTime(); server.client_manager->Process(); - server.server_manager->Process(); EQ::EventLoop::Get().Process(); Sleep(1); } diff --git a/loginserver/server_manager.cpp b/loginserver/server_manager.cpp index 87e269854..54c86f937 100644 --- a/loginserver/server_manager.cpp +++ b/loginserver/server_manager.cpp @@ -28,91 +28,58 @@ extern bool run_server; ServerManager::ServerManager() { - char error_buffer[TCPConnection_ErrorBufferSize]; - int listen_port = atoi(server.config->GetVariable("options", "listen_port").c_str()); - tcps = new EmuTCPServer(listen_port, true); - if(tcps->Open(listen_port, error_buffer)) { - Log.Out(Logs::General, Logs::Login_Server, "ServerManager listening on port %u", listen_port); - } - else { - Log.Out(Logs::General, Logs::Error, "ServerManager fatal error opening port on %u: %s", listen_port, error_buffer); - run_server = false; - } + + server_connection.reset(new EQ::Net::ServertalkServer()); + EQ::Net::ServertalkServerOptions opts; + opts.port = listen_port; + opts.ipv6 = false; + server_connection->Listen(opts); + + server_connection->OnConnectionIdentified("World", [this](std::shared_ptr c) { + Log.OutF(Logs::General, Logs::Login_Server, "New world server connection from {0}:{1}", c->Handle()->RemoteIP(), c->Handle()->RemotePort()); + + WorldServer *server_entity = GetServerByAddress(c->Handle()->RemoteIP(), c->Handle()->RemotePort()); + if (server_entity) { + Log.OutF(Logs::General, Logs::Login_Server, "World server already existed for {0}:{1}, removing existing connection and updating current.", + c->Handle()->RemoteIP(), c->Handle()->RemotePort()); + + server_entity->SetConnection(c); + server_entity->Reset(); + } + else { + world_servers.push_back(std::make_unique(c)); + } + + }); + + server_connection->OnConnectionRemoved("World", [this](std::shared_ptr c) { + auto iter = world_servers.begin(); + while (iter != world_servers.end()) { + if ((*iter)->GetConnection()->Handle() == c->Handle()) { + Log.OutF(Logs::General, Logs::World_Server, "World server {0} has been disconnected, removing.", (*iter)->GetLongName().c_str()); + world_servers.erase(iter); + return; + } + } + }); } ServerManager::~ServerManager() { - if (tcps) { - tcps->Close(); - delete tcps; - } + } -void ServerManager::Process() +WorldServer* ServerManager::GetServerByAddress(const std::string &addr, int port) { - ProcessDisconnect(); - EmuTCPConnection *tcp_c = nullptr; - while (tcp_c = tcps->NewQueuePop()) { - in_addr tmp; - tmp.s_addr = tcp_c->GetrIP(); - Log.Out(Logs::General, Logs::Login_Server, "New world server connection from %s:%d", inet_ntoa(tmp), tcp_c->GetrPort()); - - WorldServer *server_entity = GetServerByAddress(tcp_c->GetrIP()); - if (server_entity) { - Log.Out(Logs::General, Logs::Login_Server, "World server already existed for %s, removing existing connection and updating current.", inet_ntoa(tmp)); - server_entity->GetConnection()->Free(); - server_entity->SetConnection(tcp_c); - server_entity->Reset(); - } - else { - WorldServer *w = new WorldServer(tcp_c); - world_servers.push_back(w); - } - } - - list::iterator iter = world_servers.begin(); + auto iter = world_servers.begin(); while (iter != world_servers.end()) { - if ((*iter)->Process() == false) { - Log.Out(Logs::General, Logs::World_Server, "World server %s had a fatal error and had to be removed from the login.", (*iter)->GetLongName().c_str()); - delete (*iter); - iter = world_servers.erase(iter); - } - else { - ++iter; - } - } -} - -void ServerManager::ProcessDisconnect() -{ - list::iterator iter = world_servers.begin(); - while (iter != world_servers.end()) { - EmuTCPConnection *connection = (*iter)->GetConnection(); - if (!connection->Connected()) { - in_addr tmp; - tmp.s_addr = connection->GetrIP(); - Log.Out(Logs::General, Logs::Login_Server, "World server disconnected from the server, removing server and freeing connection."); - connection->Free(); - delete (*iter); - iter = world_servers.erase(iter); - } - else { - ++iter; - } - } -} - -WorldServer* ServerManager::GetServerByAddress(unsigned int address) -{ - list::iterator iter = world_servers.begin(); - while (iter != world_servers.end()) { - if ((*iter)->GetConnection()->GetrIP() == address) { - return (*iter); + if ((*iter)->GetConnection()->Handle()->RemoteIP() == addr && (*iter)->GetConnection()->Handle()->RemotePort()) { + return (*iter).get(); } ++iter; } - + return nullptr; } @@ -124,15 +91,14 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *c) in.s_addr = c->GetConnection()->GetRemoteIP(); string client_ip = inet_ntoa(in); - list::iterator iter = world_servers.begin(); + auto iter = world_servers.begin(); while (iter != world_servers.end()) { if ((*iter)->IsAuthorized() == false) { ++iter; continue; } - in.s_addr = (*iter)->GetConnection()->GetrIP(); - string world_ip = inet_ntoa(in); + std::string world_ip = (*iter)->GetConnection()->Handle()->RemoteIP(); if (world_ip.compare(client_ip) == 0) { packet_size += (*iter)->GetLongName().size() + (*iter)->GetLocalIP().size() + 24; @@ -171,8 +137,7 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *c) continue; } - in.s_addr = (*iter)->GetConnection()->GetrIP(); - string world_ip = inet_ntoa(in); + std::string world_ip = (*iter)->GetConnection()->Handle()->RemoteIP(); if (world_ip.compare(client_ip) == 0) { memcpy(data_pointer, (*iter)->GetLocalIP().c_str(), (*iter)->GetLocalIP().size()); data_pointer += ((*iter)->GetLocalIP().size() + 1); @@ -239,21 +204,21 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *c) void ServerManager::SendUserToWorldRequest(unsigned int server_id, unsigned int client_account_id) { - list::iterator iter = world_servers.begin(); + auto iter = world_servers.begin(); bool found = false; while (iter != world_servers.end()) { if ((*iter)->GetRuntimeID() == server_id) { - ServerPacket *outapp = new ServerPacket(ServerOP_UsertoWorldReq, sizeof(UsertoWorldRequest_Struct)); - UsertoWorldRequest_Struct *utwr = (UsertoWorldRequest_Struct*)outapp->pBuffer; + EQ::Net::WritablePacket outapp; + outapp.Resize(sizeof(UsertoWorldRequest_Struct)); + UsertoWorldRequest_Struct *utwr = (UsertoWorldRequest_Struct*)outapp.Data(); utwr->worldid = server_id; utwr->lsaccountid = client_account_id; - (*iter)->GetConnection()->SendPacket(outapp); + (*iter)->GetConnection()->Send(ServerOP_UsertoWorldReq, outapp); found = true; if (server.options.IsDumpInPacketsOn()) { - DumpPacket(outapp); + Log.OutF(Logs::General, Logs::Login_Server, "{0}", outapp.ToString()); } - delete outapp; } ++iter; } @@ -265,9 +230,9 @@ void ServerManager::SendUserToWorldRequest(unsigned int server_id, unsigned int bool ServerManager::ServerExists(string l_name, string s_name, WorldServer *ignore) { - list::iterator iter = world_servers.begin(); + auto iter = world_servers.begin(); while (iter != world_servers.end()) { - if ((*iter) == ignore) { + if ((*iter).get() == ignore) { ++iter; continue; } @@ -283,19 +248,14 @@ bool ServerManager::ServerExists(string l_name, string s_name, WorldServer *igno void ServerManager::DestroyServerByName(string l_name, string s_name, WorldServer *ignore) { - list::iterator iter = world_servers.begin(); + auto iter = world_servers.begin(); while (iter != world_servers.end()) { - if ((*iter) == ignore) { + if ((*iter).get() == ignore) { ++iter; } if ((*iter)->GetLongName().compare(l_name) == 0 && (*iter)->GetShortName().compare(s_name) == 0) { - EmuTCPConnection *c = (*iter)->GetConnection(); - if (c->Connected()) { - c->Disconnect(); - } - c->Free(); - delete (*iter); + (*iter)->GetConnection()->Handle()->Disconnect(); iter = world_servers.erase(iter); } diff --git a/loginserver/server_manager.h b/loginserver/server_manager.h index 6fb6c9758..76d50ab2f 100644 --- a/loginserver/server_manager.h +++ b/loginserver/server_manager.h @@ -23,6 +23,7 @@ #include "../common/emu_tcp_server.h" #include "../common/servertalk.h" #include "../common/packet_dump.h" +#include "../common/net/servertalk_server.h" #include "world_server.h" #include "client.h" #include @@ -43,11 +44,6 @@ public: */ ~ServerManager(); - /** - * Does basic processing for all the servers. - */ - void Process(); - /** * Sends a request to world to see if the client is banned or suspended. */ @@ -69,19 +65,14 @@ public: void DestroyServerByName(std::string l_name, std::string s_name, WorldServer *ignore = nullptr); private: - /** - * Processes all the disconnected connections in Process(), not used outside. - */ - void ProcessDisconnect(); - /** * Retrieves a server(if exists) by ip address * Useful utility for the reconnect process. */ - WorldServer* GetServerByAddress(unsigned int address); + WorldServer* GetServerByAddress(const std::string &address, int port); - EmuTCPServer* tcps; - std::list world_servers; + std::unique_ptr server_connection; + std::list> world_servers; }; #endif diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index 8e912e285..bcf036815 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -25,7 +25,7 @@ extern EQEmuLogSys Log; extern LoginServer server; -WorldServer::WorldServer(EmuTCPConnection *c) +WorldServer::WorldServer(std::shared_ptr c) { connection = c; zones_booted = 0; @@ -37,13 +37,16 @@ WorldServer::WorldServer(EmuTCPConnection *c) is_server_authorized = false; is_server_trusted = false; is_server_logged_in = false; + + c->OnMessage(ServerOP_NewLSInfo, std::bind(&WorldServer::ProcessPacket, this, std::placeholders::_1, std::placeholders::_2)); + c->OnMessage(ServerOP_LSStatus, std::bind(&WorldServer::ProcessPacket, this, std::placeholders::_1, std::placeholders::_2)); + c->OnMessage(ServerOP_UsertoWorldResp, std::bind(&WorldServer::ProcessPacket, this, std::placeholders::_1, std::placeholders::_2)); + c->OnMessage(ServerOP_LSAccountUpdate, std::bind(&WorldServer::ProcessPacket, this, std::placeholders::_1, std::placeholders::_2)); } WorldServer::~WorldServer() { - if(connection) { - connection->Free(); - } + } void WorldServer::Reset() @@ -58,183 +61,162 @@ void WorldServer::Reset() is_server_logged_in = false; } -bool WorldServer::Process() +void WorldServer::ProcessPacket(uint16_t opcode, const EQ::Net::Packet &p) { - ServerPacket *app = nullptr; - while(app = connection->PopPacket()) + if(server.options.IsWorldTraceOn()) { - if(server.options.IsWorldTraceOn()) + Log.Out(Logs::General, Logs::Netcode, "Application packet received from server: 0x%.4X, (size %u)", opcode, p.Length()); + } + + if(server.options.IsDumpInPacketsOn()) + { + Log.OutF(Logs::General, Logs::Login_Server, "{0}", p.ToString()); + } + + switch(opcode) + { + case ServerOP_NewLSInfo: { - Log.Out(Logs::General, Logs::Netcode, "Application packet received from server: 0x%.4X, (size %u)", app->opcode, app->size); + if(p.Length() < sizeof(ServerNewLSInfo_Struct)) + { + Log.Out(Logs::General, Logs::Error, "Received application packet from server that had opcode ServerOP_NewLSInfo, " + "but was too small. Discarded to avoid buffer overrun."); + break; + } + + if(server.options.IsWorldTraceOn()) + { + Log.Out(Logs::General, Logs::Netcode, "New Login Info Recieved."); + } + + ServerNewLSInfo_Struct *info = (ServerNewLSInfo_Struct*)p.Data(); + Handle_NewLSInfo(info); + break; } - - if(server.options.IsDumpInPacketsOn()) + case ServerOP_LSStatus: { - DumpPacket(app); + if(p.Length() < sizeof(ServerLSStatus_Struct)) + { + Log.Out(Logs::General, Logs::Error, "Recieved application packet from server that had opcode ServerOP_LSStatus, " + "but was too small. Discarded to avoid buffer overrun."); + break; + } + + if(server.options.IsWorldTraceOn()) + { + Log.Out(Logs::General, Logs::Netcode, "World Server Status Recieved."); + } + + ServerLSStatus_Struct *ls_status = (ServerLSStatus_Struct*)p.Data(); + Handle_LSStatus(ls_status); + break; } - - switch(app->opcode) + case ServerOP_UsertoWorldResp: { - case ServerOP_NewLSInfo: + if(p.Length() < sizeof(UsertoWorldResponse_Struct)) { - if(app->size < sizeof(ServerNewLSInfo_Struct)) - { - Log.Out(Logs::General, Logs::Error, "Received application packet from server that had opcode ServerOP_NewLSInfo, " - "but was too small. Discarded to avoid buffer overrun."); - break; - } - - if(server.options.IsWorldTraceOn()) - { - Log.Out(Logs::General, Logs::Netcode, "New Login Info Recieved."); - } - - ServerNewLSInfo_Struct *info = (ServerNewLSInfo_Struct*)app->pBuffer; - Handle_NewLSInfo(info); + Log.Out(Logs::General, Logs::Error, "Recieved application packet from server that had opcode ServerOP_UsertoWorldResp, " + "but was too small. Discarded to avoid buffer overrun."); break; } - case ServerOP_LSStatus: + + //I don't use world trace for this and here is why: + //Because this is a part of the client login procedure it makes tracking client errors + //While keeping world server spam with multiple servers connected almost impossible. + if(server.options.IsTraceOn()) { - if(app->size < sizeof(ServerLSStatus_Struct)) + Log.Out(Logs::General, Logs::Netcode, "User-To-World Response received."); + } + + UsertoWorldResponse_Struct *utwr = (UsertoWorldResponse_Struct*)p.Data(); + Log.Out(Logs::General, Logs::Debug, "Trying to find client with user id of %u.", utwr->lsaccountid); + Client *c = server.client_manager->GetClient(utwr->lsaccountid); + if(c) + { + Log.Out(Logs::General, Logs::Debug, "Found client with user id of %u and account name of %s.", utwr->lsaccountid, c->GetAccountName().c_str()); + EQApplicationPacket *outapp = new EQApplicationPacket(OP_PlayEverquestResponse, sizeof(PlayEverquestResponse_Struct)); + PlayEverquestResponse_Struct *per = (PlayEverquestResponse_Struct*)outapp->pBuffer; + per->Sequence = c->GetPlaySequence(); + per->ServerNumber = c->GetPlayServerID(); + Log.Out(Logs::General, Logs::Debug, "Found sequence and play of %u %u", c->GetPlaySequence(), c->GetPlayServerID()); + + Log.Out(Logs::General, Logs::Netcode, "[Size: %u] %s", outapp->size, DumpPacketToString(outapp).c_str()); + + if(utwr->response > 0) { - Log.Out(Logs::General, Logs::Error, "Recieved application packet from server that had opcode ServerOP_LSStatus, " - "but was too small. Discarded to avoid buffer overrun."); + per->Allowed = 1; + SendClientAuth(c->GetConnection()->GetRemoteAddr(), c->GetAccountName(), c->GetKey(), c->GetAccountID()); + } + + switch(utwr->response) + { + case 1: + per->Message = 101; + break; + case 0: + per->Message = 326; + break; + case -1: + per->Message = 337; + break; + case -2: + per->Message = 338; + break; + case -3: + per->Message = 303; break; } - - if(server.options.IsWorldTraceOn()) - { - Log.Out(Logs::General, Logs::Netcode, "World Server Status Recieved."); - } - - ServerLSStatus_Struct *ls_status = (ServerLSStatus_Struct*)app->pBuffer; - Handle_LSStatus(ls_status); - break; - } - case ServerOP_LSZoneInfo: - case ServerOP_LSZoneShutdown: - case ServerOP_LSZoneStart: - case ServerOP_LSZoneBoot: - case ServerOP_LSZoneSleep: - case ServerOP_LSPlayerLeftWorld: - case ServerOP_LSPlayerJoinWorld: - case ServerOP_LSPlayerZoneChange: - { - //Not logging these to cut down on spam until we implement them - break; - } - - case ServerOP_UsertoWorldResp: - { - if(app->size < sizeof(UsertoWorldResponse_Struct)) - { - Log.Out(Logs::General, Logs::Error, "Recieved application packet from server that had opcode ServerOP_UsertoWorldResp, " - "but was too small. Discarded to avoid buffer overrun."); - break; - } - - //I don't use world trace for this and here is why: - //Because this is a part of the client login procedure it makes tracking client errors - //While keeping world server spam with multiple servers connected almost impossible. + if(server.options.IsTraceOn()) { - Log.Out(Logs::General, Logs::Netcode, "User-To-World Response received."); - } - - UsertoWorldResponse_Struct *utwr = (UsertoWorldResponse_Struct*)app->pBuffer; - Log.Out(Logs::General, Logs::Debug, "Trying to find client with user id of %u.", utwr->lsaccountid); - Client *c = server.client_manager->GetClient(utwr->lsaccountid); - if(c) - { - Log.Out(Logs::General, Logs::Debug, "Found client with user id of %u and account name of %s.", utwr->lsaccountid, c->GetAccountName().c_str()); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_PlayEverquestResponse, sizeof(PlayEverquestResponse_Struct)); - PlayEverquestResponse_Struct *per = (PlayEverquestResponse_Struct*)outapp->pBuffer; - per->Sequence = c->GetPlaySequence(); - per->ServerNumber = c->GetPlayServerID(); - Log.Out(Logs::General, Logs::Debug, "Found sequence and play of %u %u", c->GetPlaySequence(), c->GetPlayServerID()); - + Log.Out(Logs::General, Logs::Netcode, "Sending play response with following data, allowed %u, sequence %u, server number %u, message %u", + per->Allowed, per->Sequence, per->ServerNumber, per->Message); Log.Out(Logs::General, Logs::Netcode, "[Size: %u] %s", outapp->size, DumpPacketToString(outapp).c_str()); - - if(utwr->response > 0) - { - per->Allowed = 1; - SendClientAuth(c->GetConnection()->GetRemoteIP(), c->GetAccountName(), c->GetKey(), c->GetAccountID()); - } - - switch(utwr->response) - { - case 1: - per->Message = 101; - break; - case 0: - per->Message = 326; - break; - case -1: - per->Message = 337; - break; - case -2: - per->Message = 338; - break; - case -3: - per->Message = 303; - break; - } - - if(server.options.IsTraceOn()) - { - Log.Out(Logs::General, Logs::Netcode, "Sending play response with following data, allowed %u, sequence %u, server number %u, message %u", - per->Allowed, per->Sequence, per->ServerNumber, per->Message); - Log.Out(Logs::General, Logs::Netcode, "[Size: %u] %s", outapp->size, DumpPacketToString(outapp).c_str()); - } - - if(server.options.IsDumpOutPacketsOn()) - { - DumpPacket(outapp); - } - - c->SendPlayResponse(outapp); - delete outapp; } - else + + if(server.options.IsDumpOutPacketsOn()) { - Log.Out(Logs::General, Logs::Error, "Recieved User-To-World Response for %u but could not find the client referenced!.", utwr->lsaccountid); + DumpPacket(outapp); } - break; + + c->SendPlayResponse(outapp); + delete outapp; } - case ServerOP_LSAccountUpdate: + else { - if(app->size < sizeof(ServerLSAccountUpdate_Struct)) - { - Log.Out(Logs::General, Logs::Error, "Recieved application packet from server that had opcode ServerLSAccountUpdate_Struct, " - "but was too small. Discarded to avoid buffer overrun."); - break; - } - - Log.Out(Logs::General, Logs::Netcode, "ServerOP_LSAccountUpdate packet received from: %s", short_name.c_str()); - ServerLSAccountUpdate_Struct *lsau = (ServerLSAccountUpdate_Struct*)app->pBuffer; - if(is_server_trusted) - { - Log.Out(Logs::General, Logs::Netcode, "ServerOP_LSAccountUpdate update processed for: %s", lsau->useraccount); - string name; - string password; - string email; - name.assign(lsau->useraccount); - password.assign(lsau->userpassword); - email.assign(lsau->useremail); - server.db->UpdateLSAccountInfo(lsau->useraccountid, name, password, email); - } - break; - } - default: - { - Log.Out(Logs::General, Logs::Error, "Recieved application packet from server that had an unknown operation code 0x%.4X.", app->opcode); + Log.Out(Logs::General, Logs::Error, "Recieved User-To-World Response for %u but could not find the client referenced!.", utwr->lsaccountid); } + break; + } + case ServerOP_LSAccountUpdate: + { + if(p.Length() < sizeof(ServerLSAccountUpdate_Struct)) + { + Log.Out(Logs::General, Logs::Error, "Recieved application packet from server that had opcode ServerLSAccountUpdate_Struct, " + "but was too small. Discarded to avoid buffer overrun."); + break; + } + + Log.Out(Logs::General, Logs::Netcode, "ServerOP_LSAccountUpdate packet received from: %s", short_name.c_str()); + ServerLSAccountUpdate_Struct *lsau = (ServerLSAccountUpdate_Struct*)p.Data(); + if(is_server_trusted) + { + Log.Out(Logs::General, Logs::Netcode, "ServerOP_LSAccountUpdate update processed for: %s", lsau->useraccount); + string name; + string password; + string email; + name.assign(lsau->useraccount); + password.assign(lsau->userpassword); + email.assign(lsau->useremail); + server.db->UpdateLSAccountInfo(lsau->useraccountid, name, password, email); + } + break; + } + default: + { + Log.Out(Logs::General, Logs::Error, "Recieved application packet from server that had an unknown operation code 0x%.4X.", opcode); } - - delete app; - app = nullptr; } - return true; } void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct* i) @@ -308,8 +290,7 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct* i) if(strlen(i->remote_address) == 0) { in_addr in; - in.s_addr = GetConnection()->GetrIP(); - remote_ip = inet_ntoa(in); + remote_ip = GetConnection()->Handle()->RemoteIP(); Log.Out(Logs::General, Logs::Error, "Handle_NewLSInfo error, remote address was null, defaulting to stream address %s.", remote_ip.c_str()); } else @@ -320,8 +301,7 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct* i) else { in_addr in; - in.s_addr = GetConnection()->GetrIP(); - remote_ip = inet_ntoa(in); + remote_ip = GetConnection()->Handle()->RemoteIP(); Log.Out(Logs::General, Logs::Error, "Handle_NewLSInfo error, remote address was too long, defaulting to stream address %s.", remote_ip.c_str()); } @@ -491,9 +471,7 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct* i) } } - in_addr in; - in.s_addr = connection->GetrIP(); - server.db->UpdateWorldRegistration(GetRuntimeID(), long_name, string(inet_ntoa(in))); + server.db->UpdateWorldRegistration(GetRuntimeID(), long_name, GetConnection()->Handle()->RemoteIP()); if(is_server_authorized) { @@ -508,7 +486,7 @@ void WorldServer::Handle_LSStatus(ServerLSStatus_Struct *s) server_status = s->status; } -void WorldServer::SendClientAuth(unsigned int ip, string account, string key, unsigned int account_id) +void WorldServer::SendClientAuth(std::string ip, string account, string key, unsigned int account_id) { ServerPacket *outapp = new ServerPacket(ServerOP_LSClientAuth, sizeof(ClientAuth_Struct)); ClientAuth_Struct* client_auth = (ClientAuth_Struct*)outapp->pBuffer; @@ -518,13 +496,10 @@ void WorldServer::SendClientAuth(unsigned int ip, string account, string key, un strncpy(client_auth->key, key.c_str(), 10); client_auth->lsadmin = 0; client_auth->worldadmin = 0; - client_auth->ip = ip; + strcpy(client_auth->ip, ip.c_str()); - in_addr in; - in.s_addr = ip; connection->GetrIP(); - string client_address(inet_ntoa(in)); - in.s_addr = connection->GetrIP(); - string world_address(inet_ntoa(in)); + string client_address(ip); + string world_address(connection->Handle()->RemoteIP()); if (client_address.compare(world_address) == 0) { client_auth->local = 1; diff --git a/loginserver/world_server.h b/loginserver/world_server.h index bceba4baa..8ee00fbba 100644 --- a/loginserver/world_server.h +++ b/loginserver/world_server.h @@ -19,11 +19,11 @@ #define EQEMU_WORLDSERVER_H #include "../common/global_define.h" -#include "../common/emu_tcp_connection.h" -#include "../common/emu_tcp_server.h" +#include "../common/net/servertalk_server_connection.h" #include "../common/servertalk.h" #include "../common/packet_dump.h" #include +#include /** * World server class, controls the connected server processing. @@ -34,7 +34,7 @@ public: /** * Constructor, sets our connection to c. */ - WorldServer(EmuTCPConnection *c); + WorldServer(std::shared_ptr c); /** * Destructor, frees our connection if it exists. @@ -47,20 +47,19 @@ public: void Reset(); /** - * Does processing of all the connections for this world. - * Returns true except for a fatal error that requires disconnection. + * Does processing of all the packets in for this world. */ - bool Process(); + void ProcessPacket(uint16_t opcode, const EQ::Net::Packet &p); /** * Accesses connection, it is intentional that this is not const (trust me). */ - EmuTCPConnection *GetConnection() { return connection; } + std::shared_ptr GetConnection() { return connection; } /** * Sets the connection to c. */ - void SetConnection(EmuTCPConnection *c) { connection = c; } + void SetConnection(std::shared_ptr c) { connection = c; } /** * Gets the runtime id of this server. @@ -130,11 +129,11 @@ public: /** * Informs world that there is a client incoming with the following data. */ - void SendClientAuth(unsigned int ip, std::string account, std::string key, unsigned int account_id); + void SendClientAuth(std::string ip, std::string account, std::string key, unsigned int account_id); private: - EmuTCPConnection *connection; + std::shared_ptr connection; unsigned int zones_booted; unsigned int players_online; int server_status; diff --git a/ucs/ucs.cpp b/ucs/ucs.cpp index afb345f29..022d58f0c 100644 --- a/ucs/ucs.cpp +++ b/ucs/ucs.cpp @@ -34,7 +34,6 @@ #include #include "../common/net/tcp_server.h" -#include "../common/net/servertalk_server.h" #include "../common/net/servertalk_client_connection.h" ChatChannelList *ChannelList; @@ -146,21 +145,14 @@ int main() { worldserver->Connect(); - EQ::Net::ServertalkServer server; - EQ::Net::ServertalkServerOptions opts; - opts.port = 5999; - opts.credentials = "User:Root;Password:1234567890"; - server.Listen(opts); - - server.OnConnectionIdentified("QueryServ", [](std::shared_ptr conn) { - Log.Out(Logs::General, Logs::Debug, "New QueryServ Connection...."); - }); - - server.OnConnectionRemoved("QueryServ", [](std::shared_ptr conn) { - Log.Out(Logs::General, Logs::Debug, "Lost QueryServ connection."); - }); - EQ::Net::ServertalkClient client("127.0.0.1", 5999, false, "QueryServ", "User:Root;Password:1234567890"); + client.OnMessage(1, [&](uint16_t opcode, EQ::Net::Packet &p) { + Log.OutF(Logs::General, Logs::Debug, "Client got message of type {0}\n{1}", opcode, p.ToString()); + + EQ::Net::WritablePacket out; + out.PutCString(0, "Why Hello"); + client.Send(2, out); + }); while(RunLoops) { diff --git a/world/console.cpp b/world/console.cpp index a933ad56f..f67f714d2 100644 --- a/world/console.cpp +++ b/world/console.cpp @@ -467,7 +467,6 @@ void Console::ProcessCommand(const char* command) { SendMessage(1, " IPLookup [name]"); } if (admin >= 100) { - SendMessage(1, " LSReconnect"); SendMessage(1, " signalcharbyname charname ID"); SendMessage(1, " reloadworld"); } @@ -794,17 +793,6 @@ void Console::ProcessCommand(const char* command) { else if (strcasecmp(sep.arg[0], "IPLookup") == 0 && admin >= 201) { client_list.SendCLEList(admin, 0, this, sep.argplus[1]); } - else if (strcasecmp(sep.arg[0], "LSReconnect") == 0 && admin >= 100) { - #ifdef _WINDOWS - _beginthread(AutoInitLoginServer, 0, nullptr); - #else - pthread_t thread; - pthread_create(&thread, nullptr, &AutoInitLoginServer, nullptr); - #endif - RunLoops = true; - SendMessage(1, " Login Server Reconnect manually restarted by Console"); - Log.Out(Logs::Detail, Logs::World_Server,"Login Server Reconnect manually restarted by Console"); - } else if (strcasecmp(sep.arg[0], "zonelock") == 0 && admin >= consoleZoneStatus) { if (strcasecmp(sep.arg[1], "list") == 0) { zoneserver_list.ListLockedZones(0, this); diff --git a/world/eqw.cpp b/world/eqw.cpp index 2bed01919..a245d3cbd 100644 --- a/world/eqw.cpp +++ b/world/eqw.cpp @@ -261,84 +261,6 @@ void EQW::CreateLauncher(Const_char *launcher_name, int dynamic_count) { launcher_list.CreateLauncher(launcher_name, dynamic_count); } -void EQW::LSReconnect() { - #ifdef _WINDOWS - _beginthread(AutoInitLoginServer, 0, nullptr); - #else - pthread_t thread; - pthread_create(&thread, nullptr, &AutoInitLoginServer, nullptr); - #endif - RunLoops = true; - Log.Out(Logs::Detail, Logs::World_Server,"Login Server Reconnect manually restarted by Web Tool"); -} - -/*EQLConfig * EQW::FindLauncher(Const_char *zone_ref) { - return(nullptr); -}*/ - -/* -map EQW::GetLaunchersDetails(Const_char *launcher_name) { - map res; - - LauncherLink *ll = launcher_list.Get(launcher_name); - if(ll == nullptr) { - res["name"] = launcher_name; - res["ip"] = "Not Connected"; - res["id"] = "0"; - res["zone_count"] = "0"; - res["connected"] = "no"; - return(res); - } else { - res["name"] = ll->GetName(); - res["ip"] = long2ip(ll->GetIP()); - res["id"] = itoa(ll->GetID()); - res["zone_count"] = itoa(ll->CountZones()); - res["connected"] = "yes"; - } - - return(res); -} - -vector EQW::ListLauncherZones(Const_char *launcher_name) { - vector list; - LauncherLink *ll = launcher_list.Get(launcher_name); - if(ll != nullptr) { - ll->GetZoneList(list); - } - return(list); -} - -map EQW::GetLauncherZoneDetails(Const_char *launcher_name, Const_char *zone_ref) { - map res; - LauncherLink *ll = launcher_list.Get(launcher_name); - if(ll != nullptr) { - ll->GetZoneDetails(zone_ref, res); - } else { - res["error"] = "Launcher Not Found"; - } - return(res); -} - -void EQW::CreateLauncher(Const_char *launcher_name, int dynamic_count) { -} - -bool EQW::BootStaticZone(Const_char *launcher_name, Const_char *short_name) { - return(false); -} - -bool EQW::DeleteStaticZone(Const_char *launcher_name, Const_char *short_name) { - return(false); -} - -bool EQW::SetDynamicCount(Const_char *launcher_name, int count) { - return(false); -} - -int EQW::GetDynamicCount(Const_char *launcher_name) { - return(0); -} -*/ - uint32 EQW::CreateGuild(const char* name, uint32 leader_char_id) { uint32 id = guild_mgr.CreateGuild(name, leader_char_id); if(id != GUILD_NONE) diff --git a/world/eqw.h b/world/eqw.h index 0f3cc14ab..99b917183 100644 --- a/world/eqw.h +++ b/world/eqw.h @@ -42,7 +42,6 @@ public: void UnlockWorld(); bool LSConnected(); - void LSReconnect(); int CountZones(); std::vector ListBootedZones(); //returns an array of zone_refs (opaque) diff --git a/world/login_server.cpp b/world/login_server.cpp index b8c23d573..f2cd9bd4e 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -22,35 +22,6 @@ #include #include #include "../common/version.h" - -#ifdef _WINDOWS - #include - #include - #include - - #define snprintf _snprintf - #define strncasecmp _strnicmp - #define strcasecmp _stricmp -#else // Pyro: fix for linux - #include -#ifdef FREEBSD //Timothy Whitman - January 7, 2003 - #include -#endif - #include - #include - #include - #include - #include - - #include "../common/unix.h" - - #define SOCKET_ERROR -1 - #define INVALID_SOCKET -1 - extern int errno; -#endif - -#define IGNORE_LS_FATAL_ERROR - #include "../common/servertalk.h" #include "login_server.h" #include "login_server_list.h" @@ -70,145 +41,107 @@ extern uint32 numplayers; extern volatile bool RunLoops; LoginServer::LoginServer(const char* iAddress, uint16 iPort, const char* Account, const char* Password) -: statusupdate_timer(LoginServer_StatusUpdateInterval) { strn0cpy(LoginServerAddress,iAddress,256); LoginServerPort = iPort; strn0cpy(LoginAccount,Account,31); strn0cpy(LoginPassword,Password,31); CanAccountUpdate = false; - tcpc = new EmuTCPConnection(true); - tcpc->SetPacketMode(EmuTCPConnection::packetModeLogin); + Connect(); } LoginServer::~LoginServer() { - delete tcpc; } -bool LoginServer::Process() { +void LoginServer::ProcessPacket(uint16_t opcode, EQ::Net::Packet &p) { const WorldConfig *Config=WorldConfig::get(); - if (statusupdate_timer.Check()) { - this->SendStatus(); - } - /************ Get all packets from packet manager out queue and process them ************/ - ServerPacket *pack = 0; - while((pack = tcpc->PopPacket())) - { - Log.Out(Logs::Detail, Logs::World_Server,"Recevied ServerPacket from LS OpCode 0x04x",pack->opcode); + Log.Out(Logs::Detail, Logs::World_Server,"Recevied ServerPacket from LS OpCode 0x04x", opcode); - switch(pack->opcode) { - case 0: - break; - case ServerOP_KeepAlive: { - // ignore this - break; - } - case ServerOP_UsertoWorldReq: { - UsertoWorldRequest_Struct* utwr = (UsertoWorldRequest_Struct*) pack->pBuffer; - uint32 id = database.GetAccountIDFromLSID(utwr->lsaccountid); - int16 status = database.CheckStatus(id); + switch(opcode) { + case ServerOP_UsertoWorldReq: { + UsertoWorldRequest_Struct* utwr = (UsertoWorldRequest_Struct*)p.Data(); + uint32 id = database.GetAccountIDFromLSID(utwr->lsaccountid); + int16 status = database.CheckStatus(id); - auto outpack = new ServerPacket; - outpack->opcode = ServerOP_UsertoWorldResp; - outpack->size = sizeof(UsertoWorldResponse_Struct); - outpack->pBuffer = new uchar[outpack->size]; - memset(outpack->pBuffer, 0, outpack->size); - UsertoWorldResponse_Struct* utwrs = (UsertoWorldResponse_Struct*) outpack->pBuffer; - utwrs->lsaccountid = utwr->lsaccountid; - utwrs->ToID = utwr->FromID; + auto outpack = new ServerPacket; + outpack->opcode = ServerOP_UsertoWorldResp; + outpack->size = sizeof(UsertoWorldResponse_Struct); + outpack->pBuffer = new uchar[outpack->size]; + memset(outpack->pBuffer, 0, outpack->size); + UsertoWorldResponse_Struct* utwrs = (UsertoWorldResponse_Struct*) outpack->pBuffer; + utwrs->lsaccountid = utwr->lsaccountid; + utwrs->ToID = utwr->FromID; - if(Config->Locked == true) - { - if((status == 0 || status < 100) && (status != -2 || status != -1)) - utwrs->response = 0; - if(status >= 100) - utwrs->response = 1; - } - else { - utwrs->response = 1; - } - - int32 x = Config->MaxClients; - if( (int32)numplayers >= x && x != -1 && x != 255 && status < 80) - utwrs->response = -3; - - if(status == -1) - utwrs->response = -1; - if(status == -2) - utwrs->response = -2; - - utwrs->worldid = utwr->worldid; - SendPacket(outpack); - delete outpack; - break; - } - case ServerOP_LSClientAuth: { - ClientAuth_Struct* slsca = (ClientAuth_Struct*) pack->pBuffer; - - if (RuleI(World, AccountSessionLimit) >= 0) { - // Enforce the limit on the number of characters on the same account that can be - // online at the same time. - client_list.EnforceSessionLimit(slsca->lsaccount_id); - } - - client_list.CLEAdd(slsca->lsaccount_id, slsca->name, slsca->key, slsca->worldadmin, slsca->ip, slsca->local); - break; - } - case ServerOP_LSFatalError: { - #ifndef IGNORE_LS_FATAL_ERROR - WorldConfig::DisableLoginserver(); - Log.Out(Logs::Detail, Logs::World_Server, "Login server responded with FatalError. Disabling reconnect."); - #else - Log.Out(Logs::Detail, Logs::World_Server, "Login server responded with FatalError."); - #endif - if (pack->size > 1) { - Log.Out(Logs::Detail, Logs::World_Server, " %s",pack->pBuffer); - } - break; - } - case ServerOP_SystemwideMessage: { - ServerSystemwideMessage* swm = (ServerSystemwideMessage*) pack->pBuffer; - zoneserver_list.SendEmoteMessageRaw(0, 0, 0, swm->type, swm->message); - break; - } - case ServerOP_LSRemoteAddr: { - if (!Config->WorldAddress.length()) { - WorldConfig::SetWorldAddress((char *)pack->pBuffer); - Log.Out(Logs::Detail, Logs::World_Server, "Loginserver provided %s as world address",pack->pBuffer); - } - break; - } - case ServerOP_LSAccountUpdate: { - Log.Out(Logs::Detail, Logs::World_Server, "Received ServerOP_LSAccountUpdate packet from loginserver"); - CanAccountUpdate = true; - break; - } - default: + if(Config->Locked == true) { - Log.Out(Logs::Detail, Logs::World_Server, "Unknown LSOpCode: 0x%04x size=%d",(int)pack->opcode,pack->size); - DumpPacket(pack->pBuffer, pack->size); - break; + if((status == 0 || status < 100) && (status != -2 || status != -1)) + utwrs->response = 0; + if(status >= 100) + utwrs->response = 1; } + else { + utwrs->response = 1; + } + + int32 x = Config->MaxClients; + if( (int32)numplayers >= x && x != -1 && x != 255 && status < 80) + utwrs->response = -3; + + if(status == -1) + utwrs->response = -1; + if(status == -2) + utwrs->response = -2; + + utwrs->worldid = utwr->worldid; + SendPacket(outpack); + delete outpack; + break; } - delete pack; - } + case ServerOP_LSClientAuth: { + ClientAuth_Struct* slsca = (ClientAuth_Struct*)p.Data(); - return true; -} + if (RuleI(World, AccountSessionLimit) >= 0) { + // Enforce the limit on the number of characters on the same account that can be + // online at the same time. + client_list.EnforceSessionLimit(slsca->lsaccount_id); + } -bool LoginServer::InitLoginServer() { - if(Connected() == false) { - if(ConnectReady()) { - Log.Out(Logs::Detail, Logs::World_Server, "Connecting to login server: %s:%d",LoginServerAddress,LoginServerPort); - Connect(); - } else { - Log.Out(Logs::Detail, Logs::World_Server, "Not connected but not ready to connect, this is bad: %s:%d", - LoginServerAddress,LoginServerPort); + client_list.CLEAdd(slsca->lsaccount_id, slsca->name, slsca->key, slsca->worldadmin, inet_addr(slsca->ip), slsca->local); + break; + } + case ServerOP_LSFatalError: { + Log.Out(Logs::Detail, Logs::World_Server, "Login server responded with FatalError."); + if (p.Length() > 1) { + Log.Out(Logs::Detail, Logs::World_Server, " %s", (const char*)p.Data()); + } + break; + } + case ServerOP_SystemwideMessage: { + ServerSystemwideMessage* swm = (ServerSystemwideMessage*)p.Data(); + zoneserver_list.SendEmoteMessageRaw(0, 0, 0, swm->type, swm->message); + break; + } + case ServerOP_LSRemoteAddr: { + if (!Config->WorldAddress.length()) { + WorldConfig::SetWorldAddress((char *)p.Data()); + Log.Out(Logs::Detail, Logs::World_Server, "Loginserver provided %s as world address", (const char*)p.Data()); + } + break; + } + case ServerOP_LSAccountUpdate: { + Log.Out(Logs::Detail, Logs::World_Server, "Received ServerOP_LSAccountUpdate packet from loginserver"); + CanAccountUpdate = true; + break; + } + default: + { + Log.Out(Logs::Detail, Logs::World_Server, "Unknown LSOpCode: 0x%04x size=%d",(int)opcode, p.Length()); + Log.OutF(Logs::General, Logs::Login_Server, "{0}", p.ToString()); + break; } } - return true; } bool LoginServer::Connect() { @@ -236,20 +169,32 @@ bool LoginServer::Connect() { return false; } - if (tcpc->ConnectIP(LoginServerIP, LoginServerPort, errbuf)) { - Log.Out(Logs::Detail, Logs::World_Server, "Connected to Loginserver: %s:%d",LoginServerAddress,LoginServerPort); - if (minilogin) - SendInfo(); - else - SendNewInfo(); - SendStatus(); - zoneserver_list.SendLSZones(); - return true; - } - else { - Log.Out(Logs::Detail, Logs::World_Server, "Could not connect to login server: %s:%d %s",LoginServerAddress,LoginServerPort,errbuf); - return false; - } + client.reset(new EQ::Net::ServertalkClient(LoginServerAddress, LoginServerPort, false, "World", "")); + client->OnConnect([this](EQ::Net::ServertalkClient *client) { + if (client) { + Log.Out(Logs::Detail, Logs::World_Server, "Connected to Loginserver: %s:%d", LoginServerAddress, LoginServerPort); + if (minilogin) + SendInfo(); + else + SendNewInfo(); + SendStatus(); + zoneserver_list.SendLSZones(); + + statusupdate_timer.reset(new EQ::Timer(LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) { + SendStatus(); + })); + } + else { + Log.Out(Logs::Detail, Logs::World_Server, "Could not connect to Loginserver: %s:%d", LoginServerAddress, LoginServerPort); + } + }); + + client->OnMessage(ServerOP_UsertoWorldReq, std::bind(&LoginServer::ProcessPacket, this, std::placeholders::_1, std::placeholders::_2)); + client->OnMessage(ServerOP_LSClientAuth, std::bind(&LoginServer::ProcessPacket, this, std::placeholders::_1, std::placeholders::_2)); + client->OnMessage(ServerOP_LSFatalError, std::bind(&LoginServer::ProcessPacket, this, std::placeholders::_1, std::placeholders::_2)); + client->OnMessage(ServerOP_SystemwideMessage, std::bind(&LoginServer::ProcessPacket, this, std::placeholders::_1, std::placeholders::_2)); + client->OnMessage(ServerOP_LSRemoteAddr, std::bind(&LoginServer::ProcessPacket, this, std::placeholders::_1, std::placeholders::_2)); + client->OnMessage(ServerOP_LSAccountUpdate, std::bind(&LoginServer::ProcessPacket, this, std::placeholders::_1, std::placeholders::_2)); } void LoginServer::SendInfo() { const WorldConfig *Config=WorldConfig::get(); @@ -291,15 +236,13 @@ void LoginServer::SendNewInfo() { if (Config->LocalAddress.length()) strcpy(lsi->local_address, Config->LocalAddress.c_str()); else { - tcpc->GetSockName(lsi->local_address,&port); - WorldConfig::SetLocalAddress(lsi->local_address); + WorldConfig::SetLocalAddress(client->Handle()->LocalIP()); } SendPacket(pack); delete pack; } void LoginServer::SendStatus() { - statusupdate_timer.Start(); auto pack = new ServerPacket; pack->opcode = ServerOP_LSStatus; pack->size = sizeof(ServerLSStatus_Struct); diff --git a/world/login_server.h b/world/login_server.h index 766bdbf38..31438b4d8 100644 --- a/world/login_server.h +++ b/world/login_server.h @@ -24,39 +24,37 @@ #include "../common/queue.h" #include "../common/eq_packet_structs.h" #include "../common/mutex.h" -#include "../common/emu_tcp_connection.h" +#include "../common/net/servertalk_client_connection.h" +#include "../common/event/timer.h" +#include class LoginServer{ public: LoginServer(const char*, uint16, const char*, const char*); ~LoginServer(); - bool InitLoginServer(); - - bool Process(); + void ProcessPacket(uint16_t opcode, EQ::Net::Packet &p); bool Connect(); void SendInfo(); void SendNewInfo(); void SendStatus(); - void SendPacket(ServerPacket* pack) { tcpc->SendPacket(pack); } + void SendPacket(ServerPacket* pack) { client->SendPacket(pack); } void SendAccountUpdate(ServerPacket* pack); - bool ConnectReady() { return tcpc->ConnectReady(); } - bool Connected() { return tcpc->Connected(); } + bool Connected() { return client->Connected(); } bool MiniLogin() { return minilogin; } bool CanUpdate() { return CanAccountUpdate; } private: bool minilogin; - EmuTCPConnection* tcpc; + std::unique_ptr client; + std::unique_ptr statusupdate_timer; char LoginServerAddress[256]; uint32 LoginServerIP; uint16 LoginServerPort; char LoginAccount[32]; char LoginPassword[32]; bool CanAccountUpdate; - - Timer statusupdate_timer; }; #endif diff --git a/world/login_server_list.cpp b/world/login_server_list.cpp index 6104f4f0a..8d7ccef7b 100644 --- a/world/login_server_list.cpp +++ b/world/login_server_list.cpp @@ -55,38 +55,6 @@ void LoginServerList::Add(const char* iAddress, uint16 iPort, const char* Accoun list.Insert(loginserver); } -bool LoginServerList::Process() { - LinkedListIterator iterator(list); - - iterator.Reset(); - while(iterator.MoreElements()){ - iterator.GetData()->Process(); - iterator.Advance(); - } - return true; -} - -#ifdef _WINDOWS -void AutoInitLoginServer(void *tmp) { -#else -void *AutoInitLoginServer(void *tmp) { -#endif - loginserverlist.InitLoginServer(); -#ifndef WIN32 - return 0; -#endif -} - -void LoginServerList::InitLoginServer() { - LinkedListIterator iterator(list); - - iterator.Reset(); - while(iterator.MoreElements()){ - iterator.GetData()->InitLoginServer(); - iterator.Advance(); - } -} - bool LoginServerList::SendInfo() { LinkedListIterator iterator(list); diff --git a/world/login_server_list.h b/world/login_server_list.h index 200e53b93..0d05d3394 100644 --- a/world/login_server_list.h +++ b/world/login_server_list.h @@ -9,12 +9,6 @@ #include "../common/mutex.h" #include "../common/emu_tcp_connection.h" -#ifdef _WINDOWS - void AutoInitLoginServer(void *tmp); -#else - void *AutoInitLoginServer(void *tmp); -#endif - class LoginServer; class LoginServerList{ @@ -23,9 +17,6 @@ public: ~LoginServerList(); void Add(const char*, uint16, const char*, const char*); - void InitLoginServer(); - - bool Process(); bool SendInfo(); bool SendNewInfo(); diff --git a/world/net.cpp b/world/net.cpp index b864c1363..714ddd1be 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -86,6 +86,9 @@ #include "ucs.h" #include "queryserv.h" +#include "../common/net/tcp_server.h" +#include "../common/net/servertalk_server.h" + EmuTCPServer tcps; ClientList client_list; GroupLFPList LFPGroupList; @@ -412,6 +415,28 @@ int main(int argc, char** argv) { Log.OutF(Logs::Detail, Logs::World_Server, "New connection from IP {0}:{1}", stream->RemoteEndpoint(), ntohs(stream->GetRemotePort())); }); + EQ::Net::ServertalkServer server; + EQ::Net::ServertalkServerOptions stopts; + stopts.port = 5999; + stopts.credentials = "User:Root;Password:1234567890"; + stopts.encrypted = true; + server.Listen(stopts); + + server.OnConnectionIdentified("QueryServ", [](std::shared_ptr conn) { + Log.Out(Logs::General, Logs::Debug, "New QueryServ Connection...."); + EQ::Net::WritablePacket out; + out.PutCString(0, "Hello"); + conn->Send(1, out); + + conn->OnMessage(2, [&](uint16_t opcode, EQ::Net::Packet &p) { + Log.OutF(Logs::General, Logs::Debug, "Server got message of type {0}\n{1}", opcode, p.ToString()); + }); + }); + + server.OnConnectionRemoved("QueryServ", [](std::shared_ptr conn) { + Log.Out(Logs::General, Logs::Debug, "Lost QueryServ connection."); + }); + while(RunLoops) { Timer::SetCurrentTime(); eqs = nullptr; @@ -487,7 +512,6 @@ int main(int argc, char** argv) { Log.Out(Logs::Detail, Logs::World_Server, "EQTime successfully saved."); } - loginserverlist.Process(); console_list.Process(); zoneserver_list.Process(); launcher_list.Process(); @@ -498,17 +522,7 @@ int main(int argc, char** argv) { if (InterserverTimer.Check()) { InterserverTimer.Start(); - database.ping(); - - if (loginserverlist.AllConnected() == false) { -#ifdef _WINDOWS - _beginthread(AutoInitLoginServer, 0, nullptr); -#else - pthread_t thread; - pthread_create(&thread, nullptr, &AutoInitLoginServer, nullptr); -#endif - } - + database.ping(); } EQ::EventLoop::Get().Process(); diff --git a/world/perl_eqw.cpp b/world/perl_eqw.cpp index 675eac26c..27b937721 100644 --- a/world/perl_eqw.cpp +++ b/world/perl_eqw.cpp @@ -140,29 +140,6 @@ XS(XS_EQW_LSConnected) XSRETURN(1); } -XS(XS_EQW_LSReconnect); /* prototype to pass -Wmissing-prototypes */ -XS(XS_EQW_LSReconnect) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: EQW::LSReconnect(THIS)"); - { - EQW * THIS; - - if (sv_derived_from(ST(0), "EQW")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(EQW *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type EQW"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->LSReconnect(); - } - XSRETURN_EMPTY; -} - XS(XS_EQW_CountZones); /* prototype to pass -Wmissing-prototypes */ XS(XS_EQW_CountZones) { @@ -1010,7 +987,6 @@ XS(boot_EQW) newXSproto(strcpy(buf, "LockWorld"), XS_EQW_LockWorld, file, "$"); newXSproto(strcpy(buf, "UnlockWorld"), XS_EQW_UnlockWorld, file, "$"); newXSproto(strcpy(buf, "LSConnected"), XS_EQW_LSConnected, file, "$"); - newXSproto(strcpy(buf, "LSReconnect"), XS_EQW_LSReconnect, file, "$"); newXSproto(strcpy(buf, "CountZones"), XS_EQW_CountZones, file, "$"); newXSproto(strcpy(buf, "ListBootedZones"), XS_EQW_ListBootedZones, file, "$"); newXSproto(strcpy(buf, "GetZoneDetails"), XS_EQW_GetZoneDetails, file, "$$");