Merge pull request #5 from EQEmu/master

eqmeu merge
This commit is contained in:
regneq 2017-04-16 18:50:03 -07:00 committed by GitHub
commit 999677d314
938 changed files with 236877 additions and 13501 deletions

View File

@ -3,15 +3,23 @@ compiler: gcc
sudo: false
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- gcc-4.8
- g++-4.8
- libmysqlclient-dev
- libperl-dev
- libboost-dev
- liblua5.1-0-dev
- zlib1g-dev
- uuid-dev
- libssl-dev
install:
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
script:
- cmake -G "Unix Makefiles" -DEQEMU_BUILD_TESTS=ON -DEQEMU_ENABLE_BOTS=ON
- make -j8
- cmake -G "Unix Makefiles" -DEQEMU_BUILD_TESTS=ON -DEQEMU_ENABLE_BOTS=ON -DEQEMU_BUILD_LOGIN=ON
- make -j2
- ./bin/tests
branches:
only:

View File

@ -64,10 +64,26 @@ IF(MSVC)
SET(ZLIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/zlib_x64")
SET(MYSQL_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/mysql_x64")
SET(LUA_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/luaj_x64")
SET(SODIUM_INCLUDE_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/include")
IF(MSVC_VERSION GREATER 1800)
SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/x64/Release/v140/dynamic")
ELSEIF(MSVC_VERSION EQUAL 1800)
SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/x64/Release/v120/dynamic")
ELSE()
SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/x64/Release/v110/dynamic")
ENDIF()
ELSE(CMAKE_CL_64)
SET(ZLIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/zlib_x86")
SET(MYSQL_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/mysql_x86")
SET(LUA_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/luaj_x86")
SET(SODIUM_INCLUDE_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/include")
IF(MSVC_VERSION GREATER 1800)
SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/Win32/Release/v140/dynamic")
ELSEIF(MSVC_VERSION EQUAL 1800)
SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/Win32/Release/v120/dynamic")
ELSE()
SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/Win32/Release/v110/dynamic")
ENDIF()
ENDIF(CMAKE_CL_64)
#disable CRT warnings on windows cause they're annoying as shit and we use C functions everywhere
@ -254,6 +270,7 @@ ENDIF(EQEMU_ENABLE_BOTS)
#What to build
OPTION(EQEMU_BUILD_SERVER "Build the game server." ON)
OPTION(EQEMU_BUILD_LOGIN "Build the login server." OFF)
OPTION(EQEMU_BUILD_HC "Build the headless client." OFF)
OPTION(EQEMU_BUILD_TESTS "Build utility tests." OFF)
OPTION(EQEMU_BUILD_PERL "Build Perl parser." ON)
OPTION(EQEMU_BUILD_LUA "Build Lua parser." ON)
@ -315,6 +332,30 @@ IF(EQEMU_BUILD_PERL)
INCLUDE_DIRECTORIES(SYSTEM "${PERL_INCLUDE_PATH}")
ENDIF(EQEMU_BUILD_PERL)
SET(SERVER_LIBS common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} ${ZLIB_LIBRARY} libuv fmt)
FIND_PACKAGE(Sodium REQUIRED)
IF(SODIUM_FOUND)
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)
SET(SERVER_LIBS ${SERVER_LIBS} "ws2_32" "psapi" "iphlpapi" "userenv")
ENDIF()
IF(UNIX)
SET(SERVER_LIBS ${SERVER_LIBS} ${CMAKE_DL_LIBS} "z" "m" "pthread")
IF(NOT DARWIN)
SET(SERVER_LIBS ${SERVER_LIBS} "rt")
ENDIF()
SET(SERVER_LIBS ${SERVER_LIBS} "uuid")
ENDIF()
IF(EQEMU_BUILD_LUA)
FIND_PACKAGE(EQLua51 REQUIRED)
SET(Boost_USE_STATIC_LIBS OFF)
@ -323,7 +364,8 @@ IF(EQEMU_BUILD_LUA)
SET(BOOST_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/boost")
FIND_PACKAGE(Boost REQUIRED)
INCLUDE_DIRECTORIES(SYSTEM "${LUA_INCLUDE_DIR}" "${Boost_INCLUDE_DIRS}" "luabind")
INCLUDE_DIRECTORIES(SYSTEM "${LUA_INCLUDE_DIR}" "${Boost_INCLUDE_DIRS}")
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/luabind")
OPTION(EQEMU_SANITIZE_LUA_LIBS "Sanitize Lua Libraries (Remove OS and IO standard libraries from being able to run)." ON)
IF(EQEMU_SANITIZE_LUA_LIBS)
@ -331,15 +373,18 @@ IF(EQEMU_BUILD_LUA)
ENDIF(EQEMU_SANITIZE_LUA_LIBS)
ENDIF(EQEMU_BUILD_LUA)
INCLUDE_DIRECTORIES(SYSTEM "${ZLIB_INCLUDE_DIRS}" "${MySQL_INCLUDE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/common/glm")
INCLUDE_DIRECTORIES(SYSTEM "${ZLIB_INCLUDE_DIRS}")
INCLUDE_DIRECTORIES(SYSTEM "${MySQL_INCLUDE_DIR}")
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/common/glm")
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/cereal")
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/libuv/include" )
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/libuv/src")
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/libs/format")
IF(EQEMU_BUILD_LUA)
ADD_SUBDIRECTORY(luabind)
ENDIF(EQEMU_BUILD_LUA)
IF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS)
IF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS OR EQEMU_BUILD_HC)
ADD_SUBDIRECTORY(common)
ENDIF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS)
ADD_SUBDIRECTORY(libs)
ENDIF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS OR EQEMU_BUILD_HC)
IF(EQEMU_BUILD_SERVER)
ADD_SUBDIRECTORY(shared_memory)
ADD_SUBDIRECTORY(world)
@ -352,6 +397,10 @@ IF(EQEMU_BUILD_LOGIN)
ADD_SUBDIRECTORY(loginserver)
ENDIF(EQEMU_BUILD_LOGIN)
IF(EQEMU_BUILD_HC)
ADD_SUBDIRECTORY(hc)
ENDIF(EQEMU_BUILD_HC)
IF(EQEMU_BUILD_TESTS)
ADD_SUBDIRECTORY(tests)
ENDIF(EQEMU_BUILD_TESTS)

View File

@ -1,5 +1,27 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 4/16/2017 ==
KLS: Merge eqstream branch
-UDP client stack completely rewritten should both have better throughput and recover better (peq has had far fewer reports of desyncs).
-TCP Server to Server connection stack completely rewritten.
-Server connections reconnect much more reliably and quickly now.
-Now supports optional packet encryption via libsodium (https://download.libsodium.org/doc/).
-Protocol behind the tcp connections has changed (see breaking changes section).
-API significantly changed and should be easier to write new servers or handlers for.
-Telnet console connection has been separated out from the current port (see breaking changes section).
-Because of changes to the TCP stack, lsreconnect and echo have been disabled.
-The server tic rate has been changed to be approx 30 fps from 500+ fps.
-Changed how missiles and movement were calculated slightly to account for this (Missiles in particular are not perfect but close enough).
-Breaking changes:
-Users who use the cmake install feature should be aware that the install directory is now %cmake_install_dir%/bin instead of just %cmake_install_dir%/
-To support new features such as encryption the underlying protocol had to change... however some servers such as the public login server will be slow to change so we've included a compatibility layer for legacy login connections:
-You should add <legacy>1</legacy> to the login section of your configuration file when connecting to a server that is using the old protocol.
-The central eqemu login server uses the old protocol and probably will for the forseeable future so if your server is connecting to it be sure to add that tag to your configuration file in that section.
-Telnet no longer uses the same port as the Server to Server connection and because of this the tcp tag no longer has any effect on telnet connections.
-To enable telnet you need to add a telnet tag in the world section of configuration such as:
<telnet ip="0.0.0.0" port="9001" enabled="true"/>
== 4/1/2017 ==
Akkadius: Cleaned up some of the NPC to NPC aggro code, only do aggro checks to other NPC's when the NPC is flagged for it
Akkadius: [Performance] Reworked how all log calls are made in the source

View File

@ -9,28 +9,8 @@ SET(export_headers
ADD_EXECUTABLE(export_client_files ${export_sources} ${export_headers})
INSTALL(TARGETS export_client_files RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX})
INSTALL(TARGETS export_client_files RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
TARGET_LINK_LIBRARIES(export_client_files common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} ${ZLIB_LIBRARY})
IF(MSVC)
SET_TARGET_PROPERTIES(export_client_files PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF")
TARGET_LINK_LIBRARIES(export_client_files "Ws2_32.lib")
ENDIF(MSVC)
IF(MINGW)
TARGET_LINK_LIBRARIES(export_client_files "WS2_32")
ENDIF(MINGW)
IF(UNIX)
TARGET_LINK_LIBRARIES(export_client_files "${CMAKE_DL_LIBS}")
TARGET_LINK_LIBRARIES(export_client_files "z")
TARGET_LINK_LIBRARIES(export_client_files "m")
IF(NOT DARWIN)
TARGET_LINK_LIBRARIES(export_client_files "rt")
ENDIF(NOT DARWIN)
TARGET_LINK_LIBRARIES(export_client_files "pthread")
ADD_DEFINITIONS(-fPIC)
ENDIF(UNIX)
TARGET_LINK_LIBRARIES(export_client_files ${SERVER_LIBS})
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

View File

@ -9,28 +9,8 @@ SET(import_headers
ADD_EXECUTABLE(import_client_files ${import_sources} ${import_headers})
INSTALL(TARGETS import_client_files RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX})
INSTALL(TARGETS import_client_files RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
TARGET_LINK_LIBRARIES(import_client_files common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} ${ZLIB_LIBRARY})
IF(MSVC)
SET_TARGET_PROPERTIES(import_client_files PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF")
TARGET_LINK_LIBRARIES(import_client_files "Ws2_32.lib")
ENDIF(MSVC)
IF(MINGW)
TARGET_LINK_LIBRARIES(import_client_files "WS2_32")
ENDIF(MINGW)
IF(UNIX)
TARGET_LINK_LIBRARIES(import_client_files "${CMAKE_DL_LIBS}")
TARGET_LINK_LIBRARIES(import_client_files "z")
TARGET_LINK_LIBRARIES(import_client_files "m")
IF(NOT DARWIN)
TARGET_LINK_LIBRARIES(import_client_files "rt")
ENDIF(NOT DARWIN)
TARGET_LINK_LIBRARIES(import_client_files "pthread")
ADD_DEFINITIONS(-fPIC)
ENDIF(UNIX)
TARGET_LINK_LIBRARIES(import_client_files ${SERVER_LIBS})
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

30
cmake/FindSodium.cmake Normal file
View File

@ -0,0 +1,30 @@
if (NOT MSVC)
include(FindPkgConfig)
pkg_check_modules(PC_SODIUM "libsodium")
if (NOT PC_SODIUM_FOUND)
pkg_check_modules(PC_SODIUM "sodium")
endif (NOT PC_SODIUM_FOUND)
if (PC_SODIUM_FOUND)
set(SODIUM_INCLUDE_HINTS ${PC_SODIUM_INCLUDE_DIRS} ${PC_SODIUM_INCLUDE_DIRS}/*)
set(SODIUM_LIBRARY_HINTS ${PC_SODIUM_LIBRARY_DIRS} ${PC_SODIUM_LIBRARY_DIRS}/*)
endif()
endif (NOT MSVC)
# some libraries install the headers is a subdirectory of the include dir
# returned by pkg-config, so use a wildcard match to improve chances of finding
# headers and libraries.
find_path(
SODIUM_INCLUDE_DIRS
NAMES sodium.h
HINTS ${SODIUM_INCLUDE_HINTS}
)
find_library(
SODIUM_LIBRARIES
NAMES libsodium sodium
HINTS ${SODIUM_LIBRARY_HINTS}
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SODIUM DEFAULT_MSG SODIUM_LIBRARIES SODIUM_INCLUDE_DIRS)
mark_as_advanced(SODIUM_FOUND SODIUM_LIBRARIES SODIUM_INCLUDE_DIRS)

View File

@ -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
@ -26,11 +24,10 @@ SET(common_sources
eqemu_logsys.cpp
eq_limits.cpp
eq_packet.cpp
eq_stream.cpp
eq_stream_factory.cpp
eq_stream_ident.cpp
eq_stream_proxy.cpp
eqtime.cpp
event_sub.cpp
extprofile.cpp
faction.cpp
guild_base.cpp
@ -40,6 +37,7 @@ SET(common_sources
ipc_mutex.cpp
item_data.cpp
item_instance.cpp
json_config.cpp
light_source.cpp
md5.cpp
memory_buffer.cpp
@ -68,15 +66,25 @@ SET(common_sources
spdat.cpp
string_util.cpp
struct_strategy.cpp
tcp_connection.cpp
tcp_server.cpp
textures.cpp
timeoutmgr.cpp
timer.cpp
unix.cpp
worldconn.cpp
xml_parser.cpp
platform.cpp
event/event_loop.cpp
json/jsoncpp.cpp
net/console_server.cpp
net/console_server_connection.cpp
net/crc32.cpp
net/daybreak_connection.cpp
net/eqstream.cpp
net/packet.cpp
net/servertalk_client_connection.cpp
net/servertalk_legacy_client_connection.cpp
net/servertalk_server.cpp
net/servertalk_server_connection.cpp
net/tcp_connection.cpp
net/tcp_server.cpp
patches/patches.cpp
patches/sod.cpp
patches/sod_limits.cpp
@ -90,22 +98,12 @@ 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
tinyxml/tinyxmlerror.cpp
tinyxml/tinyxmlparser.cpp
util/uuid.cpp
)
SET(common_headers
@ -127,8 +125,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
@ -140,15 +136,13 @@ SET(common_headers
eqemu_logsys.h
eq_limits.h
eq_packet.h
eq_stream.h
eq_stream_factory.h
eq_stream_ident.h
eq_stream_intf.h
eq_stream_locator.h
eq_stream_proxy.h
eq_stream_type.h
eqtime.h
errmsg.h
event_sub.h
extprofile.h
faction.h
features.h
@ -163,6 +157,7 @@ SET(common_headers
item_data.h
item_fieldlist.h
item_instance.h
json_config.h
languages.h
light_source.h
linked_list.h
@ -201,19 +196,35 @@ SET(common_headers
spdat.h
string_util.h
struct_strategy.h
tcp_basic_server.h
tcp_connection.h
tcp_server.h
textures.h
timeoutmgr.h
timer.h
types.h
unix.h
useperl.h
version.h
worldconn.h
xml_parser.h
zone_numbers.h
event/background_task.h
event/event_loop.h
event/timer.h
json/json.h
json/json-forwards.h
net/console_server.h
net/console_server_connection.h
net/crc32.h
net/daybreak_connection.h
net/daybreak_structs.h
net/dns.h
net/endian.h
net/eqstream.h
net/packet.h
net/servertalk_client_connection.h
net/servertalk_legacy_client_connection.h
net/servertalk_common.h
net/servertalk_server.h
net/servertalk_server_connection.h
net/tcp_connection.h
net/tcp_server.h
patches/patches.h
patches/sod.h
patches/sod_limits.h
@ -242,21 +253,57 @@ 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
util/memory_stream.h
util/uuid.h
)
SOURCE_GROUP(Event FILES
event/background_task.h
event/event_loop.cpp
event/event_loop.h
event/timer.h
)
SOURCE_GROUP(Json FILES
json/json.h
json/jsoncpp.cpp
json/json-forwards.h
)
SOURCE_GROUP(Net FILES
net/console_server.cpp
net/console_server.h
net/console_server_connection.cpp
net/console_server_connection.h
net/crc32.cpp
net/crc32.h
net/daybreak_connection.cpp
net/daybreak_connection.h
net/daybreak_structs.h
net/dns.h
net/endian.h
net/eqmq.cpp
net/eqmq.h
net/eqstream.cpp
net/eqstream.h
net/packet.cpp
net/packet.h
net/servertalk_client_connection.cpp
net/servertalk_client_connection.h
net/servertalk_legacy_client_connection.cpp
net/servertalk_legacy_client_connection.h
net/servertalk_common.h
net/servertalk_server.cpp
net/servertalk_server.h
net/servertalk_server_connection.cpp
net/servertalk_server_connection.h
net/tcp_connection.cpp
net/tcp_connection.h
net/tcp_server.cpp
net/tcp_server.h
)
SOURCE_GROUP(Patches FILES
@ -303,32 +350,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
@ -343,12 +364,17 @@ SOURCE_GROUP(TinyXML FILES
tinyxml/tinyxmlparser.cpp
)
SOURCE_GROUP(Util FILES
util/memory_stream.h
util/uuid.cpp
util/uuid.h
)
INCLUDE_DIRECTORIES(Patches SocketLib StackWalker TinyXML)
ADD_LIBRARY(common ${common_sources} ${common_headers})
IF(UNIX)
ADD_DEFINITIONS(-fPIC)
SET_SOURCE_FILES_PROPERTIES("SocketLib/Mime.cpp" PROPERTY COMPILE_FLAGS -Wno-unused-result)
SET_SOURCE_FILES_PROPERTIES("patches/sod.cpp" "patches/sof.cpp" "patches/rof.cpp" "patches/rof2.cpp" "patches/uf.cpp" PROPERTIES COMPILE_FLAGS -O0)
ENDIF(UNIX)

View File

@ -35,6 +35,7 @@ typedef enum {
BT_Greater_Akheva = 14,
BT_Khati_Sha = 15,
BT_Seru = 16, //not confirmed....
BT_Draz_Nurakk = 18,
BT_Zek = 19,
BT_Luggald = 20,
BT_Animal = 21,

View File

@ -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);
}

View File

@ -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_*/

View File

@ -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);
}

View File

@ -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_*/

View File

@ -132,7 +132,7 @@ void EQApplicationPacket::build_header_dump(char *buffer) const
#ifdef STATIC_OPCODE
sprintf(buffer, "[OpCode 0x%04x Size=%u]\n", emu_opcode,size);
#else
sprintf(buffer, "[OpCode %s Size=%u]",OpcodeManager::EmuToName(emu_opcode),size);
sprintf(buffer, "[OpCode %s(0x%04x) Size=%u]",OpcodeManager::EmuToName(emu_opcode), GetProtocolOpcode(), size);
#endif
}

View File

@ -115,11 +115,14 @@ public:
virtual void DumpRawHeader(uint16 seq=0xffff, FILE *to = stdout) const;
virtual void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const;
uint16 GetOpcodeBypass() { return opcode_bypass; }
uint16 GetOpcodeBypass() const { return opcode_bypass; }
void SetOpcodeBypass(uint16 v) { opcode_bypass = v; }
uint16 GetProtocolOpcode() const { return protocol_opcode; }
void SetProtocolOpcode(uint16 v) { protocol_opcode = v; }
protected:
uint16 protocol_opcode;
uint8 app_opcode_size;
uint16 opcode_bypass;
private:

View File

@ -529,7 +529,7 @@ void EQStream::FastQueuePacket(EQApplicationPacket **p, bool ack_req)
if(pack->GetOpcodeBypass() != 0) {
opcode = pack->GetOpcodeBypass();
} else {
opcode = (*OpMgr)->EmuToEQ(pack->emu_opcode);
opcode = (*OpMgr)->EmuToEQ(pack->GetOpcode());
}
if (!ack_req) {

View File

@ -241,7 +241,7 @@ class EQStream : public EQStreamInterface {
virtual bool CheckState(EQStreamState state) { return GetState() == state; }
virtual std::string Describe() const { return("Direct EQStream"); }
void SetOpcodeManager(OpcodeManager **opm) { OpMgr = opm; }
virtual void SetOpcodeManager(OpcodeManager **opm) { OpMgr = opm; }
void CheckTimeout(uint32 now, uint32 timeout=30);
bool HasOutgoingData();
@ -250,13 +250,13 @@ class EQStream : public EQStreamInterface {
void Write(int eq_fd);
// whether or not the stream has been assigned (we passed our stream match)
void SetActive(bool val) { streamactive = val; }
virtual void SetActive(bool val) { streamactive = val; }
//
inline bool IsInUse() { bool flag; MInUse.lock(); flag=(active_users>0); MInUse.unlock(); return flag; }
inline void PutInUse() { MInUse.lock(); active_users++; MInUse.unlock(); }
inline EQStreamState GetState() { EQStreamState s; MState.lock(); s=State; MState.unlock(); return s; }
virtual EQStreamState GetState() { EQStreamState s; MState.lock(); s=State; MState.unlock(); return s; }
static SeqOrder CompareSequence(uint16 expected_seq , uint16 seq);
@ -306,19 +306,7 @@ class EQStream : public EQStreamInterface {
const uint64 GetPacketsReceived() { return received_packet_count; }
//used for dynamic stream identification
class Signature {
public:
//this object could get more complicated if needed...
uint16 ignore_eq_opcode; //0=dont ignore
uint16 first_eq_opcode;
uint32 first_length; //0=dont check length
};
typedef enum {
MatchNotReady,
MatchSuccessful,
MatchFailed
} MatchState;
MatchState CheckSignature(const Signature *sig);
virtual MatchState CheckSignature(const Signature *sig);
};

View File

@ -4,7 +4,7 @@
#include "eqemu_logsys.h"
#include "eq_stream_ident.h"
#include "eq_stream_proxy.h"
#include "misc.h"
EQStreamIdentifier::~EQStreamIdentifier() {
while(!m_identified.empty()) {
@ -26,7 +26,7 @@ EQStreamIdentifier::~EQStreamIdentifier() {
}
}
void EQStreamIdentifier::RegisterPatch(const EQStream::Signature &sig, const char *name, OpcodeManager ** opcodes, const StructStrategy *structs) {
void EQStreamIdentifier::RegisterPatch(const EQStreamInterface::Signature &sig, const char *name, OpcodeManager ** opcodes, const StructStrategy *structs) {
auto p = new Patch;
p->signature = sig;
p->name = name;
@ -46,9 +46,9 @@ void EQStreamIdentifier::Process() {
//first see if this stream has expired
if(r.expire.Check(false)) {
//this stream has failed to match any pattern in our timeframe.
Log(Logs::General, Logs::Netcode, "[IDENTIFY] Unable to identify stream from %s:%d before timeout.", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort()));
r.stream->ReleaseFromUse();
Log(Logs::General, Logs::Netcode, "[IDENTIFY] Unable to identify stream from %s:%d before timeout.", r.stream->GetRemoteAddr().c_str(), ntohs(r.stream->GetRemotePort()));
r.stream->Close();
cur = m_streams.erase(cur);
continue;
}
@ -98,14 +98,14 @@ void EQStreamIdentifier::Process() {
Patch *p = *curp;
//ask the stream to see if it matches the supplied signature
EQStream::MatchState res = r.stream->CheckSignature(&p->signature);
EQStreamInterface::MatchState res = r.stream->CheckSignature(&p->signature);
switch(res) {
case EQStream::MatchNotReady:
case EQStreamInterface::MatchNotReady:
//the stream has not received enough packets to compare with this signature
// Log.LogDebugType(Logs::General, Logs::Netcode, "[IDENT_TRACE] %s:%d: Tried patch %s, but stream is not ready for it.", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort()), p->name.c_str());
all_ready = false;
break;
case EQStream::MatchSuccessful: {
case EQStreamInterface::MatchSuccessful: {
//yay, a match.
Log(Logs::General, Logs::Netcode, "[IDENTIFY] Identified stream %s:%d with signature %s", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort()), p->name.c_str());
@ -120,7 +120,7 @@ void EQStreamIdentifier::Process() {
found_one = true;
break;
}
case EQStream::MatchFailed:
case EQStreamInterface::MatchFailed:
//do nothing...
Log(Logs::General, Logs::Netcode, "[IDENT_TRACE] %s:%d: Tried patch %s, and it did not match.", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort()), p->name.c_str());
break;
@ -144,7 +144,7 @@ void EQStreamIdentifier::Process() {
} //end foreach stream
}
void EQStreamIdentifier::AddStream(std::shared_ptr<EQStream> &eqs) {
void EQStreamIdentifier::AddStream(std::shared_ptr<EQStreamInterface> eqs) {
m_streams.push_back(Record(eqs));
eqs = nullptr;
}
@ -157,7 +157,7 @@ EQStreamInterface *EQStreamIdentifier::PopIdentified() {
return(res);
}
EQStreamIdentifier::Record::Record(std::shared_ptr<EQStream> s)
EQStreamIdentifier::Record::Record(std::shared_ptr<EQStreamInterface> s)
: stream(std::move(s)),
expire(STREAM_IDENT_WAIT_MS)
{

View File

@ -1,13 +1,13 @@
#ifndef EQSTREAMIDENT_H_
#define EQSTREAMIDENT_H_
#include "eq_stream.h"
#include "eq_stream_intf.h"
#include "timer.h"
#include <vector>
#include <queue>
#include <memory>
#define STREAM_IDENT_WAIT_MS 10000
#define STREAM_IDENT_WAIT_MS 30000
class OpcodeManager;
class StructStrategy;
@ -18,11 +18,11 @@ public:
~EQStreamIdentifier();
//registration interface.
void RegisterPatch(const EQStream::Signature &sig, const char *name, OpcodeManager ** opcodes, const StructStrategy *structs);
void RegisterPatch(const EQStreamInterface::Signature &sig, const char *name, OpcodeManager ** opcodes, const StructStrategy *structs);
//main processing interface
void Process();
void AddStream(std::shared_ptr<EQStream> &eqs);
void AddStream(std::shared_ptr<EQStreamInterface> eqs);
EQStreamInterface *PopIdentified();
protected:
@ -31,7 +31,7 @@ protected:
class Patch {
public:
std::string name;
EQStream::Signature signature;
EQStreamInterface::Signature signature;
OpcodeManager ** opcodes;
const StructStrategy *structs;
};
@ -40,8 +40,8 @@ protected:
//pending streams..
class Record {
public:
Record(std::shared_ptr<EQStream> s);
std::shared_ptr<EQStream> stream; //we own this
Record(std::shared_ptr<EQStreamInterface> s);
std::shared_ptr<EQStreamInterface> stream; //we own this
Timer expire;
};
std::vector<Record> m_streams; //we own these objects, and the streams contained in them.

View File

@ -5,6 +5,7 @@
#include <string>
#include "emu_versions.h"
#include "eq_packet.h"
typedef enum {
ESTABLISHED,
@ -15,21 +16,40 @@ typedef enum {
} EQStreamState;
class EQApplicationPacket;
class OpcodeManager;
class EQStreamInterface {
public:
virtual ~EQStreamInterface() {}
class Signature {
public:
//this object could get more complicated if needed...
uint16 ignore_eq_opcode; //0=dont ignore
uint16 first_eq_opcode;
uint32 first_length; //0=dont check length
};
typedef enum {
MatchNotReady,
MatchSuccessful,
MatchFailed
} MatchState;
virtual void QueuePacket(const EQApplicationPacket *p, bool ack_req=true) = 0;
virtual void FastQueuePacket(EQApplicationPacket **p, bool ack_req=true) = 0;
virtual EQApplicationPacket *PopPacket() = 0;
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;
virtual std::string Describe() const = 0;
virtual void SetActive(bool val) { }
virtual MatchState CheckSignature(const Signature *sig) { return MatchFailed; }
virtual EQStreamState GetState() = 0;
virtual void SetOpcodeManager(OpcodeManager **opm) = 0;
virtual const uint32 GetBytesSent() const { return 0; }
virtual const uint32 GetBytesRecieved() const { return 0; }

View File

@ -1,11 +1,10 @@
#include "global_define.h"
#include "eq_stream_proxy.h"
#include "eq_stream.h"
#include "struct_strategy.h"
EQStreamProxy::EQStreamProxy(std::shared_ptr<EQStream> &stream, const StructStrategy *structs, OpcodeManager **opcodes)
EQStreamProxy::EQStreamProxy(std::shared_ptr<EQStreamInterface> &stream, const StructStrategy *structs, OpcodeManager **opcodes)
: m_stream(stream),
m_structs(structs),
m_opcodes(opcodes)
@ -26,6 +25,16 @@ const EQEmu::versions::ClientVersion EQStreamProxy::ClientVersion() const
return m_structs->ClientVersion();
}
EQStreamState EQStreamProxy::GetState()
{
return m_stream->GetState();
}
void EQStreamProxy::SetOpcodeManager(OpcodeManager **opm)
{
return m_stream->SetOpcodeManager(opm);
}
void EQStreamProxy::QueuePacket(const EQApplicationPacket *p, bool ack_req) {
if(p == nullptr)
return;
@ -54,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());
}

View File

@ -4,7 +4,6 @@
#include "types.h"
#include "eq_stream_intf.h"
#include "eq_stream.h"
#include <memory>
class StructStrategy;
@ -14,7 +13,7 @@ class EQApplicationPacket;
class EQStreamProxy : public EQStreamInterface {
public:
//takes ownership of the stream.
EQStreamProxy(std::shared_ptr<EQStream> &stream, const StructStrategy *structs, OpcodeManager **opcodes);
EQStreamProxy(std::shared_ptr<EQStreamInterface> &stream, const StructStrategy *structs, OpcodeManager **opcodes);
virtual ~EQStreamProxy();
//EQStreamInterface:
@ -22,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();
@ -29,6 +29,8 @@ public:
virtual bool CheckState(EQStreamState state);
virtual std::string Describe() const;
virtual const EQEmu::versions::ClientVersion ClientVersion() const;
virtual EQStreamState GetState();
virtual void SetOpcodeManager(OpcodeManager **opm);
virtual const uint32 GetBytesSent() const;
virtual const uint32 GetBytesRecieved() const;
@ -36,8 +38,8 @@ public:
virtual const uint32 GetBytesRecvPerSecond() const;
protected:
std::shared_ptr<EQStream> const m_stream; //we own this stream object.
const StructStrategy *const m_structs; //we do not own this object.
std::shared_ptr<EQStreamInterface> const m_stream; //we own this stream object.
const StructStrategy *const m_structs; //we do not own this object.
//this is a pointer to a pointer to make it less likely that a packet will
//reference an invalid opcode manager when they are being reloaded.
OpcodeManager **const m_opcodes; //we do not own this object.

View File

@ -66,6 +66,10 @@ void EQEmuConfig::do_world(TiXmlElement *ele)
if (text) {
LoginPort = atoi(text);
}
text = ParseTextBlock(sub_ele, "legacy", true);
if (text) {
LoginLegacy = atoi(text) > 0 ? true : false;
}
text = ParseTextBlock(sub_ele, "account", true);
if (text) {
LoginAccount = text;
@ -89,6 +93,10 @@ void EQEmuConfig::do_world(TiXmlElement *ele)
if (text) {
loginconfig->LoginPort = atoi(text);
}
text = ParseTextBlock(sub_ele, "legacy", true);
if (text) {
loginconfig->LoginLegacy = atoi(text) > 0 ? true : false;
}
text = ParseTextBlock(sub_ele, "account", true);
if (text) {
loginconfig->LoginAccount = text;
@ -117,11 +125,24 @@ void EQEmuConfig::do_world(TiXmlElement *ele)
if (text) {
WorldTCPPort = atoi(text);
}
text = sub_ele->Attribute("telnet");
if (text && !strcasecmp(text, "enabled")) {
}
sub_ele = ele->FirstChildElement("telnet");
if (sub_ele != nullptr) {
text = sub_ele->Attribute("ip");
if (text) {
TelnetIP = text;
}
text = sub_ele->Attribute("port");
if (text) {
TelnetTCPPort = atoi(text);
}
text = sub_ele->Attribute("enabled");
if (text && !strcasecmp(text, "true")) {
TelnetEnabled = true;
}
}
// Get the <http> element
sub_ele = ele->FirstChildElement("http");
if (sub_ele != nullptr) {
@ -370,6 +391,9 @@ std::string EQEmuConfig::GetByName(const std::string &var_name) const
if (var_name == "LoginPort") {
return (itoa(LoginPort));
}
if (var_name == "LoginLegacy") {
return (itoa(LoginLegacy ? 1 : 0));
}
if (var_name == "Locked") {
return (Locked ? "true" : "false");
}
@ -379,6 +403,12 @@ std::string EQEmuConfig::GetByName(const std::string &var_name) const
if (var_name == "WorldIP") {
return (WorldIP);
}
if (var_name == "TelnetTCPPort") {
return (itoa(TelnetTCPPort));
}
if (var_name == "TelnetIP") {
return (TelnetIP);
}
if (var_name == "TelnetEnabled") {
return (TelnetEnabled ? "true" : "false");
}
@ -495,9 +525,12 @@ void EQEmuConfig::Dump() const
std::cout << "LoginAccount = " << LoginAccount << std::endl;
std::cout << "LoginPassword = " << LoginPassword << std::endl;
std::cout << "LoginPort = " << LoginPort << std::endl;
std::cout << "LoginLegacy = " << LoginLegacy << std::endl;
std::cout << "Locked = " << Locked << std::endl;
std::cout << "WorldTCPPort = " << WorldTCPPort << std::endl;
std::cout << "WorldIP = " << WorldIP << std::endl;
std::cout << "TelnetTCPPort = " << TelnetTCPPort << std::endl;
std::cout << "TelnetIP = " << TelnetIP << std::endl;
std::cout << "TelnetEnabled = " << TelnetEnabled << std::endl;
std::cout << "WorldHTTPPort = " << WorldHTTPPort << std::endl;
std::cout << "WorldHTTPMimeFile = " << WorldHTTPMimeFile << std::endl;

View File

@ -26,6 +26,7 @@ struct LoginConfig {
std::string LoginAccount;
std::string LoginPassword;
uint16 LoginPort;
bool LoginLegacy;
};
class EQEmuConfig : public XMLParser
@ -42,11 +43,14 @@ class EQEmuConfig : public XMLParser
std::string LoginAccount;
std::string LoginPassword;
uint16 LoginPort;
bool LoginLegacy;
uint32 LoginCount;
LinkedList<LoginConfig*> loginlist;
bool Locked;
uint16 WorldTCPPort;
std::string WorldIP;
uint16 TelnetTCPPort;
std::string TelnetIP;
bool TelnetEnabled;
int32 MaxClients;
bool WorldHTTPEnabled;
@ -127,11 +131,13 @@ class EQEmuConfig : public XMLParser
#include "eqemu_config_elements.h"
// Set sane defaults
// Login server
LoginHost = "eqemulator.net";
LoginHost = "login.eqemulator.net";
LoginPort = 5998;
LoginLegacy = false;
// World
Locked = false;
WorldTCPPort = 9000;
TelnetTCPPort = 9001;
TelnetEnabled = false;
WorldHTTPEnabled = false;
WorldHTTPPort = 9080;
@ -186,6 +192,7 @@ class EQEmuConfig : public XMLParser
DefaultStatus = 0;
// For where zones need to connect to.
WorldIP = "127.0.0.1";
TelnetIP = "127.0.0.1";
// Dynamics to start
//DynamicCount=5;
MaxClients = -1;

View File

@ -103,6 +103,7 @@ void EQEmuLogSys::LoadLogSettingsDefaults()
log_settings[Logs::Crash].log_to_console = Logs::General;
log_settings[Logs::MySQLError].log_to_console = Logs::General;
log_settings[Logs::Login_Server].log_to_console = Logs::General;
log_settings[Logs::Headless_Client].log_to_console = Logs::General;
/* Set Category enabled status on defaults */
log_settings[Logs::World_Server].is_category_enabled = 1;
@ -128,6 +129,8 @@ void EQEmuLogSys::LoadLogSettingsDefaults()
platform_file_name = "login";
else if (EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformLaunch)
platform_file_name = "launcher";
else if (EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformHC)
platform_file_name = "hc";
}
std::string EQEmuLogSys::FormatOutMessageString(uint16 log_category, const std::string &in_message)

View File

@ -20,11 +20,11 @@
#ifndef EQEMU_LOGSYS_H
#define EQEMU_LOGSYS_H
#include <fmt/format.h>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <functional>
#include "types.h"
namespace Logs {
@ -85,6 +85,7 @@ enum LogCategory {
Client_Server_Packet_With_Dump,
Login_Server,
Client_Login,
Headless_Client,
MaxCategoryID /* Don't Remove this*/
};
@ -143,6 +144,11 @@ static const char* LogCategoryName[LogCategory::MaxCategoryID] = {
LogSys.Out(debug_level, log_category, message, ##__VA_ARGS__);\
} while (0)
#define LogF(debug_level, log_category, message, ...) do {\
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
LogSys.OutF(debug_level, log_category, message, ##__VA_ARGS__);\
} while (0)
class EQEmuLogSys {
public:
EQEmuLogSys();
@ -164,6 +170,13 @@ public:
void SetCurrentTimeStamp(char* time_stamp); /* Used in file logs to prepend a timestamp entry for logs */
void StartFileLogs(const std::string &log_name = ""); /* Used to declare the processes file log and to keep it open for later use */
template <typename... Args>
void OutF(Logs::DebugLevel debug_level, uint16 log_category, const char *fmt, const Args&... args)
{
std::string log_str = fmt::format(fmt, args...);
Out(debug_level, log_category, log_str);
}
/*
LogSettings Struct
@ -204,9 +217,9 @@ private:
uint16 GetWindowsConsoleColorFromCategory(uint16 log_category); /* Windows console color messages mapped by category */
void ProcessConsoleMessage(uint16 debug_level, uint16 log_category, const std::string &message); /* ProcessConsoleMessage called via Log.Out */
void ProcessGMSay(uint16 debug_level, uint16 log_category, const std::string &message); /* ProcessGMSay called via Log.Out */
void ProcessLogWrite(uint16 debug_level, uint16 log_category, const std::string &message); /* ProcessLogWrite called via Log.Out */
void ProcessConsoleMessage(uint16 debug_level, uint16 log_category, const std::string &message); /* ProcessConsoleMessage called via Log */
void ProcessGMSay(uint16 debug_level, uint16 log_category, const std::string &message); /* ProcessGMSay called via Log */
void ProcessLogWrite(uint16 debug_level, uint16 log_category, const std::string &message); /* ProcessLogWrite called via Log */
};
extern EQEmuLogSys LogSys;

View File

@ -0,0 +1,39 @@
#pragma once
#include <functional>
#include "event_loop.h"
namespace EQ {
class BackgroundTask
{
public:
typedef std::function<void(void)> BackgroundTaskFunction;
struct BackgroundTaskBaton
{
BackgroundTaskFunction fn;
BackgroundTaskFunction on_finish;
};
BackgroundTask(BackgroundTaskFunction fn, BackgroundTaskFunction on_finish) {
uv_work_t *m_work = new uv_work_t;
memset(m_work, 0, sizeof(uv_work_t));
BackgroundTaskBaton *baton = new BackgroundTaskBaton();
baton->fn = fn;
baton->on_finish = on_finish;
m_work->data = baton;
uv_queue_work(EventLoop::Get().Handle(), m_work, [](uv_work_t* req) {
BackgroundTaskBaton *baton = (BackgroundTaskBaton*)req->data;
baton->fn();
}, [](uv_work_t* req, int status) {
BackgroundTaskBaton *baton = (BackgroundTaskBaton*)req->data;
baton->on_finish();
delete baton;
delete req;
});
}
~BackgroundTask() {
}
};
}

View File

37
common/event/event_loop.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include <functional>
#include <uv.h>
#include <cstring>
namespace EQ
{
class EventLoop
{
public:
static EventLoop &Get() {
static EventLoop inst;
return inst;
}
~EventLoop() {
uv_loop_close(&m_loop);
}
void Process() {
uv_run(&m_loop, UV_RUN_NOWAIT);
}
uv_loop_t* Handle() { return &m_loop; }
private:
EventLoop() {
memset(&m_loop, 0, sizeof(uv_loop_t));
uv_loop_init(&m_loop);
}
EventLoop(const EventLoop&);
EventLoop& operator=(const EventLoop&);
uv_loop_t m_loop;
};
}

67
common/event/timer.h Normal file
View File

@ -0,0 +1,67 @@
#pragma once
#include <functional>
#include "event_loop.h"
namespace EQ {
class Timer
{
public:
Timer(std::function<void(Timer *)> cb)
{
m_timer = nullptr;
m_cb = cb;
}
Timer(uint64_t duration_ms, bool repeats, std::function<void(Timer *)> cb)
{
m_timer = nullptr;
m_cb = cb;
Start(duration_ms, repeats);
}
~Timer()
{
Stop();
}
void Start(uint64_t duration_ms, bool repeats) {
auto loop = EventLoop::Get().Handle();
if (!m_timer) {
m_timer = new uv_timer_t;
memset(m_timer, 0, sizeof(uv_timer_t));
uv_timer_init(loop, m_timer);
m_timer->data = this;
if (repeats) {
uv_timer_start(m_timer, [](uv_timer_t *handle) {
Timer *t = (Timer*)handle->data;
t->Execute();
}, duration_ms, duration_ms);
}
else {
uv_timer_start(m_timer, [](uv_timer_t *handle) {
Timer *t = (Timer*)handle->data;
t->Stop();
t->Execute();
}, duration_ms, 0);
}
}
}
void Stop() {
if (m_timer) {
uv_close((uv_handle_t*)m_timer, [](uv_handle_t* handle) {
delete handle;
});
m_timer = nullptr;
}
}
private:
void Execute() {
m_cb(this);
}
uv_timer_t *m_timer;
std::function<void(Timer*)> m_cb;
};
}

22
common/event_sub.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "event_sub.h"
#include <string.h>
void EventSubscriptionWatcher::Subscribe(const std::string &event_name)
{
m_subs[event_name] = 1;
}
void EventSubscriptionWatcher::Unsubscribe(const std::string &event_name)
{
m_subs[event_name] = 0;
}
bool EventSubscriptionWatcher::IsSubscribed(const std::string &event_name) const
{
auto iter = m_subs.find(event_name);
if (iter != m_subs.end()) {
return iter->second;
}
return false;
}

28
common/event_sub.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <unordered_map>
class EventSubscriptionWatcher
{
public:
~EventSubscriptionWatcher();
void Subscribe(const std::string &event_name);
void Unsubscribe(const std::string &event_name);
bool IsSubscribed(const std::string &event_name) const;
static EventSubscriptionWatcher *Get() {
static EventSubscriptionWatcher* inst = nullptr;
if(!inst) {
inst = new EventSubscriptionWatcher();
}
return inst;
}
private:
EventSubscriptionWatcher() { }
EventSubscriptionWatcher(const EventSubscriptionWatcher&);
EventSubscriptionWatcher& operator=(const EventSubscriptionWatcher&);
std::unordered_map<std::string, bool> m_subs;
};

View File

@ -160,7 +160,6 @@ enum { //timer settings, all in milliseconds
ClientProximity_interval = 150,
CombatEventTimer_expire = 12000,
Tribute_duration = 600000,
ZoneTimerResolution = 3, //sleep time between zone main loop runs (milliseconds)
FeignMemoryDuration = 120000, // Duration player must feign death to clear zonewide agro.
EnragedTimer = 360000,
EnragedDurationTimer = 10000

330
common/json/json-forwards.h Normal file
View File

@ -0,0 +1,330 @@
/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/).
/// It is intended to be used with #include "json/json-forwards.h"
/// This header provides forward declaration for all JsonCpp types.
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
/*
The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following
conditions...
The author (Baptiste Lepilleur) explicitly disclaims copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
this software is released into the Public Domain.
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
released under the terms of the MIT License (see below).
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
Public Domain/MIT License conditions described here, as they choose.
The MIT License is about as close to Public Domain as a license can get, and is
described in clear, concise terms at:
http://en.wikipedia.org/wiki/MIT_License
The full text of the MIT License follows:
========================================================================
Copyright (c) 2007-2010 Baptiste Lepilleur
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
(END LICENSE TEXT)
The MIT license is compatible with both the GPL and commercial
software, affording one all of the rights of Public Domain with the
minor nuisance of being required to keep the above copyright notice
and license text in the source code. Note also that by accepting the
Public Domain "license" you can re-license your copy using whatever
license you like.
*/
// //////////////////////////////////////////////////////////////////////
// End of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED
# define JSON_FORWARD_AMALGATED_H_INCLUDED
/// If defined, indicates that the source file is amalgated
/// to prevent private header inclusion.
#define JSON_IS_AMALGAMATION
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_CONFIG_H_INCLUDED
#define JSON_CONFIG_H_INCLUDED
#include <stddef.h>
#include <string> //typedef String
#include <stdint.h> //typedef int64_t, uint64_t
/// If defined, indicates that json library is embedded in CppTL library.
//# define JSON_IN_CPPTL 1
/// If defined, indicates that json may leverage CppTL library
//# define JSON_USE_CPPTL 1
/// If defined, indicates that cpptl vector based map should be used instead of
/// std::map
/// as Value container.
//# define JSON_USE_CPPTL_SMALLMAP 1
// If non-zero, the library uses exceptions to report bad input instead of C
// assertion macros. The default is to use exceptions.
#ifndef JSON_USE_EXCEPTION
#define JSON_USE_EXCEPTION 1
#endif
/// If defined, indicates that the source file is amalgated
/// to prevent private header inclusion.
/// Remarks: it is automatically defined in the generated amalgated header.
// #define JSON_IS_AMALGAMATION
#ifdef JSON_IN_CPPTL
#include <cpptl/config.h>
#ifndef JSON_USE_CPPTL
#define JSON_USE_CPPTL 1
#endif
#endif
#ifdef JSON_IN_CPPTL
#define JSON_API CPPTL_API
#elif defined(JSON_DLL_BUILD)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllexport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#elif defined(JSON_DLL)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllimport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#endif // ifdef JSON_IN_CPPTL
#if !defined(JSON_API)
#define JSON_API
#endif
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
// integer
// Storages, and 64 bits integer support is disabled.
// #define JSON_NO_INT64 1
#if defined(_MSC_VER) // MSVC
# if _MSC_VER <= 1200 // MSVC 6
// Microsoft Visual Studio 6 only support conversion from __int64 to double
// (no conversion from unsigned __int64).
# define JSON_USE_INT64_DOUBLE_CONVERSION 1
// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
// characters in the debug information)
// All projects I've ever seen with VS6 were using this globally (not bothering
// with pragma push/pop).
# pragma warning(disable : 4786)
# endif // MSVC 6
# if _MSC_VER >= 1500 // MSVC 2008
/// Indicates that the following function is deprecated.
# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
# endif
#endif // defined(_MSC_VER)
// In c++11 the override keyword allows you to explicity define that a function
// is intended to override the base-class version. This makes the code more
// managable and fixes a set of common hard-to-find bugs.
#if __cplusplus >= 201103L
# define JSONCPP_OVERRIDE override
# define JSONCPP_NOEXCEPT noexcept
#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900
# define JSONCPP_OVERRIDE override
# define JSONCPP_NOEXCEPT throw()
#elif defined(_MSC_VER) && _MSC_VER >= 1900
# define JSONCPP_OVERRIDE override
# define JSONCPP_NOEXCEPT noexcept
#else
# define JSONCPP_OVERRIDE
# define JSONCPP_NOEXCEPT throw()
#endif
#ifndef JSON_HAS_RVALUE_REFERENCES
#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // MSVC >= 2010
#ifdef __clang__
#if __has_feature(cxx_rvalue_references)
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // has_feature
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // GXX_EXPERIMENTAL
#endif // __clang__ || __GNUC__
#endif // not defined JSON_HAS_RVALUE_REFERENCES
#ifndef JSON_HAS_RVALUE_REFERENCES
#define JSON_HAS_RVALUE_REFERENCES 0
#endif
#ifdef __clang__
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
# endif // GNUC version
#endif // __clang__ || __GNUC__
#if !defined(JSONCPP_DEPRECATED)
#define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED)
#if __GNUC__ >= 6
# define JSON_USE_INT64_DOUBLE_CONVERSION 1
#endif
#if !defined(JSON_IS_AMALGAMATION)
# include "version.h"
# if JSONCPP_USING_SECURE_MEMORY
# include "allocator.h" //typedef Allocator
# endif
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
typedef int Int;
typedef unsigned int UInt;
#if defined(JSON_NO_INT64)
typedef int LargestInt;
typedef unsigned int LargestUInt;
#undef JSON_HAS_INT64
#else // if defined(JSON_NO_INT64)
// For Microsoft Visual use specific types as long long is not supported
#if defined(_MSC_VER) // Microsoft Visual Studio
typedef __int64 Int64;
typedef unsigned __int64 UInt64;
#else // if defined(_MSC_VER) // Other platforms, use long long
typedef int64_t Int64;
typedef uint64_t UInt64;
#endif // if defined(_MSC_VER)
typedef Int64 LargestInt;
typedef UInt64 LargestUInt;
#define JSON_HAS_INT64
#endif // if defined(JSON_NO_INT64)
#if JSONCPP_USING_SECURE_MEMORY
#define JSONCPP_STRING std::basic_string<char, std::char_traits<char>, Json::SecureAllocator<char> >
#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
#define JSONCPP_OSTREAM std::basic_ostream<char, std::char_traits<char>>
#define JSONCPP_ISTRINGSTREAM std::basic_istringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
#define JSONCPP_ISTREAM std::istream
#else
#define JSONCPP_STRING std::string
#define JSONCPP_OSTRINGSTREAM std::ostringstream
#define JSONCPP_OSTREAM std::ostream
#define JSONCPP_ISTRINGSTREAM std::istringstream
#define JSONCPP_ISTREAM std::istream
#endif // if JSONCPP_USING_SECURE_MEMORY
} // end namespace Json
#endif // JSON_CONFIG_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FORWARDS_H_INCLUDED
#define JSON_FORWARDS_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
// writer.h
class FastWriter;
class StyledWriter;
// reader.h
class Reader;
// features.h
class Features;
// value.h
typedef unsigned int ArrayIndex;
class StaticString;
class Path;
class PathArgument;
class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
} // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
#endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED

2161
common/json/json.h Normal file

File diff suppressed because it is too large Load Diff

5311
common/json/jsoncpp.cpp Normal file

File diff suppressed because it is too large Load Diff

89
common/json_config.cpp Normal file
View File

@ -0,0 +1,89 @@
#include "json_config.h"
#include <fstream>
EQ::JsonConfigFile::JsonConfigFile()
{
}
EQ::JsonConfigFile::JsonConfigFile(const Json::Value &value)
{
m_root = value;
}
EQ::JsonConfigFile::~JsonConfigFile()
{
}
EQ::JsonConfigFile EQ::JsonConfigFile::Load(const std::string &filename)
{
JsonConfigFile ret;
ret.m_root = Json::Value();
std::ifstream ifs;
ifs.open(filename, std::ifstream::in);
if (!ifs.good()) {
return ret;
}
try {
ifs >> ret.m_root;
}
catch (std::exception) {
return ret;
}
return ret;
}
std::string EQ::JsonConfigFile::GetVariableString(const std::string &title, const std::string &parameter, const std::string &default_value) {
try {
if (m_root.isMember(title) && m_root[title].isMember(parameter)) {
return m_root[title][parameter].asString();
}
}
catch (std::exception) {
return default_value;
}
return default_value;
}
int EQ::JsonConfigFile::GetVariableInt(const std::string &title, const std::string &parameter, const int default_value) {
try {
if (m_root.isMember(title) && m_root[title].isMember(parameter)) {
return m_root[title][parameter].asInt();
}
}
catch (std::exception) {
return default_value;
}
return default_value;
}
bool EQ::JsonConfigFile::GetVariableBool(const std::string &title, const std::string &parameter, const bool default_value) {
try {
if (m_root.isMember(title) && m_root[title].isMember(parameter)) {
return m_root[title][parameter].asBool();
}
}
catch (std::exception) {
return default_value;
}
return default_value;
}
double EQ::JsonConfigFile::GetVariableDouble(const std::string &title, const std::string &parameter, const double default_value) {
try {
if (m_root.isMember(title) && m_root[title].isMember(parameter)) {
return m_root[title][parameter].asDouble();
}
}
catch (std::exception) {
return default_value;
}
return default_value;
}

26
common/json_config.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include "json/json.h"
namespace EQ
{
class JsonConfigFile
{
public:
JsonConfigFile(const Json::Value &value);
~JsonConfigFile();
static JsonConfigFile Load(const std::string &filename);
std::string GetVariableString(const std::string &title, const std::string &parameter, const std::string &default_value);
int GetVariableInt(const std::string &title, const std::string &parameter, const int default_value);
bool GetVariableBool(const std::string &title, const std::string &parameter, const bool default_value);
double GetVariableDouble(const std::string &title, const std::string &parameter, const double default_value);
Json::Value& RawHandle() { return m_root; }
private:
JsonConfigFile();
Json::Value m_root;
};
}

View File

@ -0,0 +1,78 @@
#include "console_server.h"
#include "../string_util.h"
#include <fmt/format.h>
EQ::Net::ConsoleServer::ConsoleServer(const std::string &addr, int port)
{
m_server.reset(new EQ::Net::TCPServer());
m_server->Listen(addr, port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
ConsoleServerConnection *c = new ConsoleServerConnection(this, connection);
m_connections.insert(std::make_pair(c->GetUUID(), std::unique_ptr<ConsoleServerConnection>(c)));
});
}
EQ::Net::ConsoleServer::~ConsoleServer()
{
}
void EQ::Net::ConsoleServer::RegisterCall(const std::string &command, int status_required, const std::string& help_definition, ConsoleServerCallback fn)
{
m_commands[command] = { fn, status_required, help_definition };
}
void EQ::Net::ConsoleServer::RegisterLogin(ConsoleServerLoginCallback fn)
{
m_login = fn;
}
void EQ::Net::ConsoleServer::ConnectionDisconnected(ConsoleServerConnection *c)
{
auto iter = m_connections.find(c->GetUUID());
if (iter != m_connections.end()) {
m_connections.erase(iter);
}
}
void EQ::Net::ConsoleServer::ProcessCommand(ConsoleServerConnection *c, const std::string &cmd)
{
auto split = SplitString(cmd, ' ');
if (split.size() > 0) {
auto command = split[0];
ToLowerString(command);
if (command == "help" || command == "?") {
c->SendLine("Commands:");
for (auto &cmd_def : m_commands) {
if (cmd_def.second.status_required <= c->Admin()) {
auto display = fmt::format(" {0}", cmd_def.second.help_definition);
c->SendLine(display);
}
}
c->SendPrompt();
return;
}
split.erase(split.begin(), split.begin() + 1);
auto cmd_def = m_commands.find(command);
if (cmd_def != m_commands.end()) {
if (c->Admin() >= cmd_def->second.status_required) {
cmd_def->second.fn(c, command, split);
c->SendPrompt();
}
else {
c->SendLine(fmt::format("Access denied for command: {0}", command));
c->SendPrompt();
}
}
else {
c->SendLine(fmt::format("Command not found: {0}", command));
c->SendPrompt();
}
}
else {
c->SendPrompt();
}
}

View File

@ -0,0 +1,49 @@
#pragma once
#include "console_server_connection.h"
#include <functional>
#include <vector>
namespace EQ
{
namespace Net
{
struct ConsoleLoginStatus
{
int status;
int account_id;
std::string account_name;
};
class ConsoleServer
{
public:
typedef std::function<void(ConsoleServerConnection*, const std::string&, const std::vector<std::string>&)> ConsoleServerCallback;
typedef std::function<struct ConsoleLoginStatus(const std::string&, const std::string&)> ConsoleServerLoginCallback;
ConsoleServer(const std::string &addr, int port);
~ConsoleServer();
void RegisterCall(const std::string& command, int status_required, const std::string& help_definition, ConsoleServerCallback fn);
void RegisterLogin(ConsoleServerLoginCallback fn);
private:
void ConnectionDisconnected(ConsoleServerConnection *c);
void ProcessCommand(ConsoleServerConnection *c, const std::string& cmd);
std::unique_ptr<TCPServer> m_server;
std::map<std::string, std::unique_ptr<ConsoleServerConnection>> m_connections;
struct ConsoleServerCommand
{
ConsoleServerCallback fn;
int status_required;
std::string help_definition;
};
std::map<std::string, ConsoleServerCommand> m_commands;
ConsoleServerLoginCallback m_login;
friend class ConsoleServerConnection;
};
}
}

View File

@ -0,0 +1,221 @@
#include "console_server.h"
#include "../common/util/uuid.h"
#include "../common/net/packet.h"
#include "../common/eqemu_logsys.h"
EQ::Net::ConsoleServerConnection::ConsoleServerConnection(ConsoleServer *parent, std::shared_ptr<TCPConnection> connection)
{
m_parent = parent;
m_connection = connection;
m_uuid = EQ::Util::UUID::Generate().ToString();
m_cursor = 0;
memset(m_line, 0, MaxConsoleLineLength);
m_accept_messages = false;
m_user_id = 0;
m_admin = 0;
m_connection->OnRead(std::bind(&ConsoleServerConnection::OnRead, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_connection->OnDisconnect(std::bind(&ConsoleServerConnection::OnDisconnect, this, std::placeholders::_1));
m_connection->Start();
ClearBuffer();
auto addr = m_connection->RemoteIP();
SendLine(fmt::format("Establishing connection from: {0}:{1}", addr, m_connection->RemotePort()));
if (addr.find("127.0.0.1") != std::string::npos || addr.find("::0") != std::string::npos) {
SendLine("Connection established from localhost, assuming admin");
m_status = ConsoleStatusLoggedIn;
m_admin = 255;
SendPrompt();
}
else {
m_status = ConsoleStatusWaitingForLogin;
Send("Username: ");
}
}
EQ::Net::ConsoleServerConnection::~ConsoleServerConnection()
{
}
void EQ::Net::ConsoleServerConnection::SendClear()
{
EQ::Net::DynamicPacket clear;
clear.PutUInt8(0, 0);
m_connection->Write((const char*)clear.Data(), clear.Length());
}
void EQ::Net::ConsoleServerConnection::Send(const std::string &msg)
{
m_connection->Write(msg.c_str(), msg.length());
}
void EQ::Net::ConsoleServerConnection::SendLine(const std::string &line)
{
Send(line);
SendNewLine();
}
void EQ::Net::ConsoleServerConnection::SendNewLine()
{
EQ::Net::DynamicPacket newline;
newline.PutUInt8(0, 10);
newline.PutUInt8(1, 13);
m_connection->Write((const char*)newline.Data(), newline.Length());
}
void EQ::Net::ConsoleServerConnection::QueueMessage(const std::string &msg)
{
if (!m_accept_messages) {
return;
}
if (m_cursor == 0) {
size_t len = m_user.length() + 2;
for (size_t i = 0; i < len; ++i) {
Send("\x08");
}
SendLine(msg);
SendPrompt();
}
else {
std::string cmd(m_line, m_line + m_cursor);
size_t len = m_user.length() + 2 + cmd.length();
for (size_t i = 0; i < len; ++i) {
Send("\x08");
}
if (msg.length() < cmd.length()) {
Send(msg);
size_t blank_spaces = 2 + cmd.length() - msg.length();
for (size_t i = 0; i < blank_spaces; ++i) {
Send(" ");
}
SendNewLine();
}
else {
SendLine(msg);
}
SendPrompt();
Send(cmd);
}
}
void EQ::Net::ConsoleServerConnection::OnRead(TCPConnection *c, const unsigned char *data, size_t sz)
{
for (size_t i = 0; i < sz; ++i) {
unsigned char c = data[i];
switch (c) {
case 0:
m_cursor = 0;
break;
case 10: // \n
{
if (m_cursor > 0) {
std::string cmd(m_line, m_line + m_cursor);
ProcessCommand(cmd);
m_cursor = 0;
}
else {
ProcessCommand("");
}
}
break;
case 13: // \r
break;
case 8:
if (m_cursor > 0) {
m_cursor--;
}
break;
case 255:
//255 is always followed by a character
i++;
if (i < sz) {
unsigned char c = data[i];
if (c == 255) {
//Escaped 255
if (m_cursor < MaxConsoleLineLength && isprint(c)) {
m_line[m_cursor] = c;
m_cursor++;
}
break;
}
if (c == 251 || c == 252 || c == 253 || c == 254) {
//Option code is always followed by an extra character
//We don't really care about negotiation tho.
i++;
}
}
break;
default:
if (m_cursor < MaxConsoleLineLength && isprint(c)) {
m_line[m_cursor] = c;
m_cursor++;
}
break;
}
}
}
void EQ::Net::ConsoleServerConnection::OnDisconnect(TCPConnection *c)
{
m_parent->ConnectionDisconnected(this);
}
void EQ::Net::ConsoleServerConnection::ProcessCommand(const std::string &cmd)
{
if (m_status == ConsoleStatusWaitingForLogin) {
m_user = cmd;
m_status = ConsoleStatusWaitingForPassword;
Send("Password: ");
return;
}
if (m_status == ConsoleStatusWaitingForPassword) {
auto status = m_parent->m_login(m_user, cmd);
if (status.account_id == 0) {
m_status = ConsoleStatusFailedLogin;
SendLine("Access denied");
m_connection->Disconnect();
return;
}
if (status.status < ConsoleLoginStatus) {
m_status = ConsoleStatusFailedLogin;
SendLine("Access denied");
m_connection->Disconnect();
return;
}
m_user = status.account_name;
m_user_id = status.account_id;
m_admin = status.status;
m_status = ConsoleStatusLoggedIn;
SendPrompt();
return;
}
if (m_status == ConsoleStatusLoggedIn) {
m_parent->ProcessCommand(this, cmd);
}
}
void EQ::Net::ConsoleServerConnection::SendPrompt()
{
Send(fmt::format("{0}> ", m_user));
}

View File

@ -0,0 +1,63 @@
#pragma once
#include "tcp_server.h"
#include <memory>
#include <map>
namespace EQ
{
namespace Net
{
enum ConsoleConnectionStatus
{
ConsoleStatusWaitingForLogin,
ConsoleStatusWaitingForPassword,
ConsoleStatusLoggedIn,
ConsoleStatusFailedLogin
};
const int MaxConsoleLineLength = 512;
const int ConsoleLoginStatus = 50;
class ConsoleServer;
class ConsoleServerConnection
{
public:
ConsoleServerConnection(ConsoleServer *parent, std::shared_ptr<TCPConnection> connection);
~ConsoleServerConnection();
std::string GetUUID() const { return m_uuid; }
void ClearBuffer() { m_cursor = 0; }
void Close() { m_connection->Disconnect(); }
void SendClear();
void Send(const std::string &msg);
void SendLine(const std::string &line);
void SendNewLine();
void SendPrompt();
ConsoleConnectionStatus Status() const { return m_status; }
std::string UserName() const { return m_user; }
int UserId() const { return m_user_id; }
int Admin() const { return m_admin; }
bool AcceptMessages() const { return m_accept_messages; }
void SetAcceptMessages(bool v) { m_accept_messages = v; }
void QueueMessage(const std::string &msg);
private:
void OnRead(TCPConnection* c, const unsigned char* data, size_t sz);
void OnDisconnect(TCPConnection* c);
void ProcessCommand(const std::string &cmd);
ConsoleServer *m_parent;
std::shared_ptr<TCPConnection> m_connection;
std::string m_uuid;
ConsoleConnectionStatus m_status;
std::string m_user;
int m_user_id;
int m_admin;
bool m_accept_messages;
size_t m_cursor;
char m_line[MaxConsoleLineLength];
};
}
}

96
common/net/crc32.cpp Normal file
View File

@ -0,0 +1,96 @@
#include "crc32.h"
#include <memory.h>
unsigned int CRC32EncodeTable[256] =
{
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
int EQ::Crc32(const void * data, int size)
{
int crc = 0xffffffff;
auto buffer = (const uint8_t *)data;
for (int i = 0; i < size; ++i) {
crc = ((crc >> 8) & 0x00FFFFFFL) ^ CRC32EncodeTable[(crc ^ *&buffer[i]) & 0x000000FFL];
}
return ~crc;
}
int EQ::Crc32(const void * data, int size, int key)
{
int crc = 0xffffffff;
for (int i = 0; i < 4; ++i) {
crc = ((crc >> 8) & 0x00FFFFFFL) ^ CRC32EncodeTable[(crc ^ ((key >> (i * 8)) & 0xff)) & 0x000000FFL];
}
auto buffer = (const uint8_t *)data;
for (int i = 0; i < size; ++i) {
crc = ((crc >> 8) & 0x00FFFFFFL) ^ CRC32EncodeTable[(crc ^ *&buffer[i]) & 0x000000FFL];
}
return ~crc;
}

9
common/net/crc32.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <stdint.h>
namespace EQ
{
int Crc32(const void *data, int size);
int Crc32(const void *data, int size, int key);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,285 @@
#pragma once
#include "../random.h"
#include "packet.h"
#include "daybreak_structs.h"
#include <uv.h>
#include <chrono>
#include <functional>
#include <memory>
#include <map>
#include <queue>
#include <list>
namespace EQ
{
namespace Net
{
enum DaybreakProtocolOpcode
{
OP_Padding = 0x00,
OP_SessionRequest = 0x01,
OP_SessionResponse = 0x02,
OP_Combined = 0x03,
OP_SessionDisconnect = 0x05,
OP_KeepAlive = 0x06,
OP_SessionStatRequest = 0x07,
OP_SessionStatResponse = 0x08,
OP_Packet = 0x09,
OP_Packet2 = 0x0a,
OP_Packet3 = 0x0b,
OP_Packet4 = 0x0c,
OP_Fragment = 0x0d,
OP_Fragment2 = 0x0e,
OP_Fragment3 = 0x0f,
OP_Fragment4 = 0x10,
OP_OutOfOrderAck = 0x11,
OP_OutOfOrderAck2 = 0x12,
OP_OutOfOrderAck3 = 0x13,
OP_OutOfOrderAck4 = 0x14,
OP_Ack = 0x15,
OP_Ack2 = 0x16,
OP_Ack3 = 0x17,
OP_Ack4 = 0x18,
OP_AppCombined = 0x19,
OP_OutboundPing = 0x1c,
OP_OutOfSession = 0x1d
};
enum DbProtocolStatus
{
StatusConnecting,
StatusConnected,
StatusDisconnecting,
StatusDisconnected
};
enum DaybreakEncodeType
{
EncodeNone = 0,
EncodeCompression = 1,
EncodeXOR = 4,
};
enum SequenceOrder
{
SequenceCurrent,
SequenceFuture,
SequencePast
};
typedef std::chrono::high_resolution_clock::time_point Timestamp;
typedef std::chrono::high_resolution_clock Clock;
struct DaybreakConnectionStats
{
DaybreakConnectionStats() {
recv_bytes = 0;
sent_bytes = 0;
recv_packets = 0;
sent_packets = 0;
min_ping = 0xFFFFFFFFFFFFFFFFUL;
max_ping = 0;
created = Clock::now();
}
uint64_t recv_bytes;
uint64_t sent_bytes;
uint64_t recv_packets;
uint64_t sent_packets;
uint64_t min_ping;
uint64_t max_ping;
uint64_t last_ping;
Timestamp created;
};
class DaybreakConnectionManager;
class DaybreakConnection;
class DaybreakConnection
{
public:
DaybreakConnection(DaybreakConnectionManager *owner, const DaybreakConnect &connect, const std::string &endpoint, int port);
DaybreakConnection(DaybreakConnectionManager *owner, const std::string &endpoint, int port);
~DaybreakConnection();
const std::string& RemoteEndpoint() const { return m_endpoint; }
int RemotePort() const { return m_port; }
void Close();
void QueuePacket(Packet &p);
void QueuePacket(Packet &p, int stream);
void QueuePacket(Packet &p, int stream, bool reliable);
const DaybreakConnectionStats& GetStats() const { return m_stats; }
void ResetStats();
size_t GetRollingPing() const { return m_rolling_ping; }
DbProtocolStatus GetStatus() { return m_status; }
private:
DaybreakConnectionManager *m_owner;
std::string m_endpoint;
int m_port;
uint32_t m_connect_code;
uint32_t m_encode_key;
uint32_t m_max_packet_size;
uint32_t m_crc_bytes;
DaybreakEncodeType m_encode_passes[2];
Timestamp m_last_send;
Timestamp m_last_recv;
DbProtocolStatus m_status;
Timestamp m_hold_time;
std::list<DynamicPacket> m_buffered_packets;
size_t m_buffered_packets_length;
std::unique_ptr<char[]> m_combined;
DaybreakConnectionStats m_stats;
Timestamp m_last_session_stats;
size_t m_resend_delay;
size_t m_rolling_ping;
Timestamp m_close_time;
struct DaybreakSentPacket
{
DynamicPacket packet;
Timestamp last_sent;
Timestamp first_sent;
size_t times_resent;
};
struct DaybreakStream
{
DaybreakStream() {
sequence_in = 0;
sequence_out = 0;
fragment_current_bytes = 0;
fragment_total_bytes = 0;
}
uint16_t sequence_in;
uint16_t sequence_out;
std::map<uint16_t, Packet*> packet_queue;
DynamicPacket fragment_packet;
uint32_t fragment_current_bytes;
uint32_t fragment_total_bytes;
std::map<uint16_t, DaybreakSentPacket> sent_packets;
};
DaybreakStream m_streams[4];
std::weak_ptr<DaybreakConnection> m_self;
void Process();
void ProcessPacket(Packet &p);
void ProcessQueue();
void RemoveFromQueue(int stream, uint16_t seq);
void AddToQueue(int stream, uint16_t seq, const Packet &p);
void ProcessDecodedPacket(const Packet &p);
void ChangeStatus(DbProtocolStatus new_status);
bool ValidateCRC(Packet &p);
void AppendCRC(Packet &p);
bool PacketCanBeEncoded(Packet &p) const;
void Decode(Packet &p, size_t offset, size_t length);
void Encode(Packet &p, size_t offset, size_t length);
void Decompress(Packet &p, size_t offset, size_t length);
void Compress(Packet &p, size_t offset, size_t length);
void ProcessResend();
void ProcessResend(int stream);
void Ack(int stream, uint16_t seq);
void OutOfOrderAck(int stream, uint16_t seq);
void SendConnect();
void SendKeepAlive();
void SendAck(int stream, uint16_t seq);
void SendOutOfOrderAck(int stream, uint16_t seq);
void SendDisconnect();
void InternalBufferedSend(Packet &p);
void InternalSend(Packet &p);
void InternalQueuePacket(Packet &p, int stream_id, bool reliable);
void FlushBuffer();
SequenceOrder CompareSequence(uint16_t expected, uint16_t actual) const;
friend class DaybreakConnectionManager;
};
struct DaybreakConnectionManagerOptions
{
DaybreakConnectionManagerOptions() {
max_connection_count = 0;
keepalive_delay_ms = 9000;
resend_delay_ms = 150;
resend_delay_factor = 1.5;
resend_delay_min = 150;
resend_delay_max = 1000;
connect_delay_ms = 500;
stale_connection_ms = 90000;
connect_stale_ms = 5000;
crc_length = 2;
max_packet_size = 512;
encode_passes[0] = DaybreakEncodeType::EncodeNone;
encode_passes[1] = DaybreakEncodeType::EncodeNone;
port = 0;
hold_size = 448;
hold_length_ms = 10;
simulated_in_packet_loss = 0;
simulated_out_packet_loss = 0;
tic_rate_hertz = 60.0;
resend_timeout = 90000;
connection_close_time = 2000;
}
size_t max_packet_size;
size_t max_connection_count;
size_t keepalive_delay_ms;
double resend_delay_factor;
size_t resend_delay_ms;
size_t resend_delay_min;
size_t resend_delay_max;
size_t connect_delay_ms;
size_t connect_stale_ms;
size_t stale_connection_ms;
size_t crc_length;
size_t hold_size;
size_t hold_length_ms;
size_t simulated_in_packet_loss;
size_t simulated_out_packet_loss;
double tic_rate_hertz;
size_t resend_timeout;
size_t connection_close_time;
DaybreakEncodeType encode_passes[2];
int port;
};
class DaybreakConnectionManager
{
public:
DaybreakConnectionManager();
DaybreakConnectionManager(const DaybreakConnectionManagerOptions &opts);
~DaybreakConnectionManager();
void Connect(const std::string &addr, int port);
void Process();
void ProcessResend();
void OnNewConnection(std::function<void(std::shared_ptr<DaybreakConnection>)> func) { m_on_new_connection = func; }
void OnConnectionStateChange(std::function<void(std::shared_ptr<DaybreakConnection>, DbProtocolStatus, DbProtocolStatus)> func) { m_on_connection_state_change = func; }
void OnPacketRecv(std::function<void(std::shared_ptr<DaybreakConnection>, const Packet &)> func) { m_on_packet_recv = func; }
private:
void Attach(uv_loop_t *loop);
void Detach();
EQEmu::Random m_rand;
uv_timer_t m_timer;
uv_udp_t m_socket;
uv_loop_t *m_attached;
DaybreakConnectionManagerOptions m_options;
std::function<void(std::shared_ptr<DaybreakConnection>)> m_on_new_connection;
std::function<void(std::shared_ptr<DaybreakConnection>, DbProtocolStatus, DbProtocolStatus)> m_on_connection_state_change;
std::function<void(std::shared_ptr<DaybreakConnection>, const Packet&)> m_on_packet_recv;
std::map<std::pair<std::string, int>, std::shared_ptr<DaybreakConnection>> m_connections;
void ProcessPacket(const std::string &endpoint, int port, const char *data, size_t size);
std::shared_ptr<DaybreakConnection> FindConnectionByEndpoint(std::string addr, int port);
void SendDisconnect(const std::string &addr, int port);
friend class DaybreakConnection;
};
}
}

View File

@ -0,0 +1,173 @@
#pragma once
#include <cereal/cereal.hpp>
#include <cstdint>
#include "endian.h"
namespace EQ
{
namespace Net
{
struct DaybreakHeader
{
static size_t size() { return 2; }
uint8_t zero;
uint8_t opcode;
template <class Archive>
void serialize(Archive & archive)
{
archive(zero,
opcode);
}
};
struct DaybreakConnect
{
static size_t size() { return 14; }
uint8_t zero;
uint8_t opcode;
uint32_t protocol_version;
uint32_t connect_code;
uint32_t max_packet_size;
template <class Archive>
void serialize(Archive & archive)
{
archive(zero,
opcode,
protocol_version,
connect_code,
max_packet_size);
}
};
struct DaybreakConnectReply
{
static size_t size() { return 17; }
uint8_t zero;
uint8_t opcode;
uint32_t connect_code;
uint32_t encode_key;
uint8_t crc_bytes;
uint8_t encode_pass1;
uint8_t encode_pass2;
uint32_t max_packet_size;
template <class Archive>
void serialize(Archive & archive)
{
archive(zero,
opcode,
connect_code,
encode_key,
crc_bytes,
encode_pass1,
encode_pass2,
max_packet_size);
}
};
struct DaybreakDisconnect
{
static size_t size() { return 8; }
uint8_t zero;
uint8_t opcode;
uint32_t connect_code;
template <class Archive>
void serialize(Archive & archive)
{
archive(zero,
opcode,
connect_code);
}
};
struct DaybreakReliableHeader
{
static size_t size() { return 4; }
uint8_t zero;
uint8_t opcode;
uint16_t sequence;
template <class Archive>
void serialize(Archive & archive)
{
archive(zero,
opcode,
sequence);
}
};
struct DaybreakReliableFragmentHeader
{
static size_t size() { return 4 + DaybreakReliableHeader::size(); }
DaybreakReliableHeader reliable;
uint32_t total_size;
template <class Archive>
void serialize(Archive & archive)
{
archive(reliable,
total_size);
}
};
struct DaybreakSessionStatRequest
{
static size_t size() { return 40; }
uint8_t zero;
uint8_t opcode;
uint16_t timestamp;
uint32_t stat_ping;
uint32_t avg_ping;
uint32_t min_ping;
uint32_t max_ping;
uint32_t last_ping;
uint64_t packets_sent;
uint64_t packets_recv;
template <class Archive>
void serialize(Archive & archive)
{
archive(zero,
opcode,
timestamp,
stat_ping,
avg_ping,
min_ping,
max_ping,
last_ping,
packets_sent,
packets_recv);
}
};
struct DaybreakSessionStatResponse
{
static size_t size() { return 40; }
uint8_t zero;
uint8_t opcode;
uint16_t timestamp;
uint32_t our_timestamp;
uint64_t client_sent;
uint64_t client_recv;
uint64_t server_sent;
uint64_t server_recv;
template <class Archive>
void serialize(Archive & archive)
{
archive(zero,
opcode,
timestamp,
our_timestamp,
client_sent,
client_recv,
server_sent,
server_recv);
}
};
}
}

61
common/net/dns.h Normal file
View File

@ -0,0 +1,61 @@
#pragma once
#include "../event/event_loop.h"
#include <string>
#include <functional>
namespace EQ
{
namespace Net
{
static void DNSLookup(const std::string &addr, int port, bool ipv6, std::function<void(const std::string&)> cb) {
struct DNSBaton
{
std::function<void(const std::string&)> cb;
bool ipv6;
};
addrinfo hints;
memset(&hints, 0, sizeof(addrinfo));
hints.ai_family = PF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
auto loop = EQ::EventLoop::Get().Handle();
uv_getaddrinfo_t *resolver = new uv_getaddrinfo_t();
memset(resolver, 0, sizeof(uv_getaddrinfo_t));
auto port_str = std::to_string(port);
DNSBaton *baton = new DNSBaton();
baton->cb = cb;
baton->ipv6 = ipv6;
resolver->data = baton;
uv_getaddrinfo(loop, resolver, [](uv_getaddrinfo_t* req, int status, addrinfo* res) {
DNSBaton *baton = (DNSBaton*)req->data;
if (status < 0) {
auto cb = baton->cb;
delete baton;
delete req;
cb("");
return;
}
char addr[40] = { 0 };
if (baton->ipv6) {
uv_ip6_name((struct sockaddr_in6*)res->ai_addr, addr, 40);
}
else {
uv_ip4_name((struct sockaddr_in*)res->ai_addr, addr, 40);
}
auto cb = baton->cb;
delete baton;
delete req;
uv_freeaddrinfo(res);
cb(addr);
}, addr.c_str(), port_str.c_str(), &hints);
}
}
}

51
common/net/endian.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include <cstdint>
#include <algorithm>
#include <type_traits>
namespace EQ
{
namespace Net
{
inline bool IsLittleEndian() {
const int32_t v = 1;
return 1 == *(int8_t*)&v;
}
template<typename T>
T ByteSwap(T in) {
static_assert(std::is_integral<T>::value, "Byte swap only works on integer types.");
T ret;
char *first = (char*)&in;
char *last = (char*)&in + sizeof(in);
char *d_first = (char*)&ret;
while (first != last) {
*(d_first++) = *(--last);
}
return ret;
}
template<typename T>
T HostToNetwork(T in) {
if (IsLittleEndian()) {
return ByteSwap(in);
}
else {
return in;
}
}
template<typename T>
T NetworkToHost(T in) {
if (IsLittleEndian()) {
return ByteSwap(in);
}
else {
return in;
}
}
}
}

211
common/net/eqstream.cpp Normal file
View File

@ -0,0 +1,211 @@
#include "eqstream.h"
#include "../eqemu_logsys.h"
EQ::Net::EQStreamManager::EQStreamManager(EQStreamManagerOptions &options) : m_daybreak(options.daybreak_options)
{
m_options = options;
m_daybreak.OnNewConnection(std::bind(&EQStreamManager::DaybreakNewConnection, this, std::placeholders::_1));
m_daybreak.OnConnectionStateChange(std::bind(&EQStreamManager::DaybreakConnectionStateChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_daybreak.OnPacketRecv(std::bind(&EQStreamManager::DaybreakPacketRecv, this, std::placeholders::_1, std::placeholders::_2));
}
EQ::Net::EQStreamManager::~EQStreamManager()
{
}
void EQ::Net::EQStreamManager::DaybreakNewConnection(std::shared_ptr<DaybreakConnection> connection)
{
std::shared_ptr<EQStream> stream(new EQStream(this, connection));
m_streams.insert(std::make_pair(connection, stream));
if (m_on_new_connection) {
m_on_new_connection(stream);
}
}
void EQ::Net::EQStreamManager::DaybreakConnectionStateChange(std::shared_ptr<DaybreakConnection> connection, DbProtocolStatus from, DbProtocolStatus to)
{
auto iter = m_streams.find(connection);
if (iter != m_streams.end()) {
if (m_on_connection_state_change) {
m_on_connection_state_change(iter->second, from, to);
}
if (to == EQ::Net::StatusDisconnected) {
m_streams.erase(iter);
}
}
}
void EQ::Net::EQStreamManager::DaybreakPacketRecv(std::shared_ptr<DaybreakConnection> connection, const Packet &p)
{
auto iter = m_streams.find(connection);
if (iter != m_streams.end()) {
auto &stream = iter->second;
std::unique_ptr<EQ::Net::Packet> t(new EQ::Net::DynamicPacket());
t->PutPacket(0, p);
stream->m_packet_queue.push_back(std::move(t));
}
}
EQ::Net::EQStream::EQStream(EQStreamManager *owner, std::shared_ptr<DaybreakConnection> connection)
{
m_owner = owner;
m_connection = connection;
m_opcode_manager = nullptr;
}
EQ::Net::EQStream::~EQStream()
{
}
void EQ::Net::EQStream::QueuePacket(const EQApplicationPacket *p, bool ack_req) {
if (m_opcode_manager && *m_opcode_manager) {
uint16 opcode = 0;
if (p->GetOpcodeBypass() != 0) {
opcode = p->GetOpcodeBypass();
}
else {
opcode = (*m_opcode_manager)->EmuToEQ(p->GetOpcode());
}
EQ::Net::DynamicPacket out;
switch (m_owner->m_options.opcode_size) {
case 1:
out.PutUInt8(0, opcode);
out.PutData(1, p->pBuffer, p->size);
break;
case 2:
out.PutUInt16(0, opcode);
out.PutData(2, p->pBuffer, p->size);
break;
}
m_connection->QueuePacket(out);
}
}
void EQ::Net::EQStream::FastQueuePacket(EQApplicationPacket **p, bool ack_req) {
QueuePacket(*p, ack_req);
delete *p;
*p = nullptr;
}
EQApplicationPacket *EQ::Net::EQStream::PopPacket() {
if (m_packet_queue.empty()) {
return nullptr;
}
if (m_opcode_manager != nullptr && *m_opcode_manager != nullptr) {
auto &p = m_packet_queue.front();
uint16 opcode = 0;
switch (m_owner->m_options.opcode_size) {
case 1:
opcode = p->GetUInt8(0);
break;
case 2:
opcode = p->GetUInt16(0);
break;
}
EmuOpcode emu_op = (*m_opcode_manager)->EQToEmu(opcode);
EQApplicationPacket *ret = new EQApplicationPacket(emu_op, (unsigned char*)p->Data() + m_owner->m_options.opcode_size, p->Length() - m_owner->m_options.opcode_size);
ret->SetProtocolOpcode(opcode);
m_packet_queue.pop_front();
return ret;
}
return nullptr;
}
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());
}
bool EQ::Net::EQStream::CheckState(EQStreamState state) {
return GetState() == state;
}
EQStreamInterface::MatchState EQ::Net::EQStream::CheckSignature(const Signature *sig) {
if (!m_packet_queue.empty()) {
auto p = m_packet_queue.front().get();
uint16 opcode = 0;
size_t length = p->Length() - m_owner->m_options.opcode_size;
switch (m_owner->m_options.opcode_size) {
case 1:
opcode = p->GetUInt8(0);
break;
case 2:
opcode = p->GetUInt16(0);
break;
}
if (sig->ignore_eq_opcode != 0 && opcode == sig->ignore_eq_opcode) {
if (m_packet_queue.size() > 1) {
p = m_packet_queue[1].get();
opcode = 0;
length = p->Length() - m_owner->m_options.opcode_size;
switch (m_owner->m_options.opcode_size) {
case 1:
opcode = p->GetUInt8(0);
break;
case 2:
opcode = p->GetUInt16(0);
break;
}
}
else {
return MatchNotReady;
}
}
if (opcode == sig->first_eq_opcode) {
if (length == sig->first_length) {
LogF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode matched {2:#x} and length matched {3}",
RemoteEndpoint(), m_connection->RemotePort(), sig->first_eq_opcode, length);
return MatchSuccessful;
}
else if (length == 0) {
LogF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode matched {2:#x} and length is ignored.",
RemoteEndpoint(), m_connection->RemotePort(), sig->first_eq_opcode);
return MatchSuccessful;
}
else {
LogF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode matched {2:#x} but length {3} did not match expected {4}",
RemoteEndpoint(), m_connection->RemotePort(), sig->first_eq_opcode, length, sig->first_length);
return MatchFailed;
}
}
else {
LogF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode {1:#x} did not match expected {2:#x}",
RemoteEndpoint(), m_connection->RemotePort(), opcode, sig->first_eq_opcode);
return MatchFailed;
}
}
return MatchNotReady;
}
EQStreamState EQ::Net::EQStream::GetState() {
auto status = m_connection->GetStatus();
switch (status) {
case StatusConnecting:
return UNESTABLISHED;
case StatusConnected:
return ESTABLISHED;
case StatusDisconnecting:
return DISCONNECTING;
default:
return CLOSED;
}
}

99
common/net/eqstream.h Normal file
View File

@ -0,0 +1,99 @@
#pragma once
#include "../eq_packet.h"
#include "../eq_stream_intf.h"
#include "../opcodemgr.h"
#include "daybreak_connection.h"
#include <vector>
#include <deque>
namespace EQ
{
namespace Net
{
struct EQStreamManagerOptions
{
EQStreamManagerOptions() {
opcode_size = 2;
}
EQStreamManagerOptions(int port, bool encoded, bool compressed) {
opcode_size = 2;
//World seems to support both compression and xor zone supports one or the others.
//Enforce one or the other in the convienence construct
//Login I had trouble getting to recognize compression at all
//but that might be because it was still a bit buggy when i was testing that.
if (compressed) {
daybreak_options.encode_passes[0] = EncodeCompression;
}
else if (encoded) {
daybreak_options.encode_passes[0] = EncodeXOR;
}
daybreak_options.port = port;
}
int opcode_size;
DaybreakConnectionManagerOptions daybreak_options;
};
class EQStream;
class EQStreamManager
{
public:
EQStreamManager(EQStreamManagerOptions &options);
~EQStreamManager();
void OnNewConnection(std::function<void(std::shared_ptr<EQStream>)> func) { m_on_new_connection = func; }
void OnConnectionStateChange(std::function<void(std::shared_ptr<EQStream>, DbProtocolStatus, DbProtocolStatus)> func) { m_on_connection_state_change = func; }
private:
EQStreamManagerOptions m_options;
DaybreakConnectionManager m_daybreak;
std::function<void(std::shared_ptr<EQStream>)> m_on_new_connection;
std::function<void(std::shared_ptr<EQStream>, DbProtocolStatus, DbProtocolStatus)> m_on_connection_state_change;
std::map<std::shared_ptr<DaybreakConnection>, std::shared_ptr<EQStream>> m_streams;
void DaybreakNewConnection(std::shared_ptr<DaybreakConnection> connection);
void DaybreakConnectionStateChange(std::shared_ptr<DaybreakConnection> connection, DbProtocolStatus from, DbProtocolStatus to);
void DaybreakPacketRecv(std::shared_ptr<DaybreakConnection> connection, const Packet &p);
friend class EQStream;
};
class EQStream : public EQStreamInterface
{
public:
EQStream(EQStreamManager *parent, std::shared_ptr<DaybreakConnection> connection);
~EQStream();
virtual void QueuePacket(const EQApplicationPacket *p, bool ack_req = true);
virtual void FastQueuePacket(EQApplicationPacket **p, bool ack_req = true);
virtual EQApplicationPacket *PopPacket();
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);
virtual std::string Describe() const { return "Direct EQStream"; }
virtual void SetActive(bool val) { }
virtual MatchState CheckSignature(const Signature *sig);
virtual EQStreamState GetState();
virtual void SetOpcodeManager(OpcodeManager **opm) {
m_opcode_manager = opm;
}
const std::string& RemoteEndpoint() const { return m_connection->RemoteEndpoint(); }
const DaybreakConnectionStats& GetStats() const { return m_connection->GetStats(); }
void ResetStats() { m_connection->ResetStats(); }
size_t GetRollingPing() const { return m_connection->GetRollingPing(); }
private:
EQStreamManager *m_owner;
std::shared_ptr<DaybreakConnection> m_connection;
OpcodeManager **m_opcode_manager;
std::deque<std::unique_ptr<EQ::Net::Packet>> m_packet_queue;
friend class EQStreamManager;
};
}
}

345
common/net/packet.cpp Normal file
View File

@ -0,0 +1,345 @@
#include "packet.h"
#include "endian.h"
#include <fmt/format.h>
#include <cctype>
void EQ::Net::Packet::PutInt8(size_t offset, int8_t value)
{
if (Length() < offset + 1) {
if (!Resize(offset + 1)) {
throw std::out_of_range("Packet::PutInt8(), could not resize packet and would of written past the end.");
}
}
*(int8_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutInt16(size_t offset, int16_t value)
{
if (Length() < offset + 2) {
if (!Resize(offset + 2)) {
throw std::out_of_range("Packet::PutInt16(), could not resize packet and would of written past the end.");
}
}
*(int16_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutInt32(size_t offset, int32_t value)
{
if (Length() < offset + 4) {
if (!Resize(offset + 4)) {
throw std::out_of_range("Packet::PutInt32(), could not resize packet and would of written past the end.");
}
}
*(int32_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutInt64(size_t offset, int64_t value)
{
if (Length() < offset + 8) {
if (!Resize(offset + 8)) {
throw std::out_of_range("Packet::PutInt64(), could not resize packet and would of written past the end.");
}
}
*(int64_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutUInt8(size_t offset, uint8_t value)
{
if (Length() < offset + 1) {
if (!Resize(offset + 1)) {
throw std::out_of_range("Packet::PutUInt8(), could not resize packet and would of written past the end.");
}
}
*(uint8_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutUInt16(size_t offset, uint16_t value)
{
if (Length() < offset + 2) {
if (!Resize(offset + 2)) {
throw std::out_of_range("Packet::PutUInt16(), could not resize packet and would of written past the end.");
}
}
*(uint16_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutUInt32(size_t offset, uint32_t value)
{
if (Length() < offset + 4) {
if (!Resize(offset + 4)) {
throw std::out_of_range("Packet::PutUInt32(), could not resize packet and would of written past the end.");
}
}
*(uint32_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutUInt64(size_t offset, uint64_t value)
{
if (Length() < offset + 8) {
if (!Resize(offset + 8)) {
throw std::out_of_range("Packet::PutUInt64(), could not resize packet and would of written past the end.");
}
}
*(uint64_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutFloat(size_t offset, float value)
{
if (Length() < offset + 4) {
if (!Resize(offset + 4)) {
throw std::out_of_range("Packet::PutFloat(), could not resize packet and would of written past the end.");
}
}
*(float*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutDouble(size_t offset, double value)
{
if (Length() < offset + 8) {
if (!Resize(offset + 8)) {
throw std::out_of_range("Packet::PutDouble(), could not resize packet and would of written past the end.");
}
}
*(double*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutString(size_t offset, const std::string &str)
{
if (Length() < offset + str.length()) {
if (!Resize(offset + str.length())) {
throw std::out_of_range("Packet::PutString(), could not resize packet and would of written past the end.");
}
}
memcpy(((char*)Data() + offset), str.c_str(), str.length());
}
void EQ::Net::Packet::PutCString(size_t offset, const char *str)
{
size_t sz = strlen(str);
if (Length() < offset + sz + 1) {
if (!Resize(offset + sz + 1)) {
throw std::out_of_range("Packet::PutCString(), could not resize packet and would of written past the end.");
}
}
memcpy(((char*)Data() + offset), str, sz);
*((char*)Data() + offset + sz) = 0;
}
void EQ::Net::Packet::PutPacket(size_t offset, const Packet &p)
{
if (Length() < offset + p.Length()) {
if (!Resize(offset + p.Length())) {
throw std::out_of_range("Packet::PutPacket(), could not resize packet and would of written past the end.");
}
}
memcpy(((char*)Data() + offset), p.Data(), p.Length());
}
void EQ::Net::Packet::PutData(size_t offset, void *data, size_t length)
{
if (Length() < offset + length) {
if (!Resize(offset + length)) {
throw std::out_of_range("Packet::PutData(), could not resize packet and would of written past the end.");
}
}
memcpy(((char*)Data() + offset), data, length);
}
int8_t EQ::Net::Packet::GetInt8(size_t offset) const
{
if (Length() < offset + 1) {
throw std::out_of_range("Packet read out of range.");
}
return *(int8_t*)((char*)Data() + offset);
}
int16_t EQ::Net::Packet::GetInt16(size_t offset) const
{
if (Length() < offset + 2) {
throw std::out_of_range("Packet read out of range.");
}
return *(int16_t*)((char*)Data() + offset);
}
int32_t EQ::Net::Packet::GetInt32(size_t offset) const
{
if (Length() < offset + 4) {
throw std::out_of_range("Packet read out of range.");
}
return *(int32_t*)((char*)Data() + offset);
}
int64_t EQ::Net::Packet::GetInt64(size_t offset) const
{
if (Length() < offset + 8) {
throw std::out_of_range("Packet read out of range.");
}
return *(int64_t*)((char*)Data() + offset);
}
uint8_t EQ::Net::Packet::GetUInt8(size_t offset) const
{
if (Length() < offset + 1) {
throw std::out_of_range("Packet read out of range.");
}
return *(uint8_t*)((char*)Data() + offset);
}
uint16_t EQ::Net::Packet::GetUInt16(size_t offset) const
{
if (Length() < offset + 2) {
throw std::out_of_range("Packet read out of range.");
}
return *(uint16_t*)((char*)Data() + offset);
}
uint32_t EQ::Net::Packet::GetUInt32(size_t offset) const
{
if (Length() < offset + 4) {
throw std::out_of_range("Packet read out of range.");
}
return *(uint32_t*)((char*)Data() + offset);
}
uint64_t EQ::Net::Packet::GetUInt64(size_t offset) const
{
if (Length() < offset + 8) {
throw std::out_of_range("Packet read out of range.");
}
return *(uint64_t*)((char*)Data() + offset);
}
float EQ::Net::Packet::GetFloat(size_t offset) const
{
if (Length() < offset + 4) {
throw std::out_of_range("Packet read out of range.");
}
return *(float*)((char*)Data() + offset);
}
double EQ::Net::Packet::GetDouble(size_t offset) const
{
if (Length() < offset + 8) {
throw std::out_of_range("Packet read out of range.");
}
return *(double*)((char*)Data() + offset);
}
std::string EQ::Net::Packet::GetString(size_t offset, size_t length) const
{
if (Length() < offset + length) {
throw std::out_of_range("Packet read out of range.");
}
return std::string((char*)Data() + offset, (char*)Data() + offset + length);
}
std::string EQ::Net::Packet::GetCString(size_t offset) const
{
if (Length() < offset + 1) {
throw std::out_of_range("Packet read out of range.");
}
char *str = ((char*)Data() + offset);
return std::string(str);
}
char ToSafePrint(unsigned char in) {
if (std::isprint(in)) {
return in;
}
return '.';
}
std::string EQ::Net::Packet::ToString() const
{
return ToString(16);
}
std::string EQ::Net::Packet::ToString(size_t line_length) const
{
if (Length() == 0) {
return fmt::format("{:0>5x} |", 0);
}
std::string ret;
size_t lines = Length() / line_length;
size_t i;
char *data = (char*)Data();
for (i = 0; i < lines; ++i) {
ret += fmt::format("{:0>5x} |", i * line_length);
std::string hex;
std::string ascii;
for (size_t j = 0; j < line_length; ++j) {
hex += fmt::format(" {:0>2x}", (uint8_t)data[(i * line_length) + j]);
ascii += fmt::format("{}", ToSafePrint(data[(i * line_length) + j]));
}
ret += hex;
ret += " | ";
ret += ascii;
ret += "\n";
}
if (Length() % line_length > 0) {
ret += fmt::format("{:0>5x} |", i * line_length);
size_t non_blank_count = Length() % line_length;
size_t blank_count = line_length - non_blank_count;
std::string hex;
std::string ascii;
for (size_t j = 0; j < non_blank_count; ++j) {
hex += fmt::format(" {:0>2x}", (uint8_t)data[(i * line_length) + j]);
ascii += fmt::format("{}", ToSafePrint(data[(i * line_length) + j]));
}
for (size_t j = 0; j < blank_count; ++j) {
hex += " ";
ascii += " ";
}
ret += hex;
ret += " | ";
ret += ascii;
ret += "\n";
}
return ret;
}
bool EQ::Net::StaticPacket::Resize(size_t new_size)
{
if (new_size > m_max_data_length) {
return false;
}
m_data_length = new_size;
}

130
common/net/packet.h Normal file
View File

@ -0,0 +1,130 @@
#pragma once
#include <cstdint>
#include <string>
#include <stdexcept>
#include <cstring>
#include "../util/memory_stream.h"
#include <cereal/cereal.hpp>
#include <cereal/archives/binary.hpp>
namespace EQ {
namespace Net {
class Packet
{
public:
Packet() : m_stream(std::ios::out | std::ios::binary) { }
virtual ~Packet() { }
virtual const void *Data() const = 0;
virtual void *Data() = 0;
virtual size_t Length() const = 0;
virtual size_t Length() = 0;
virtual bool Clear() = 0;
virtual bool Resize(size_t new_size) = 0;
virtual void Reserve(size_t new_size) = 0;
template<typename T>
T GetSerialize(size_t offset) const
{
T ret;
Util::MemoryStreamReader reader(((char*)Data() + offset), Length());
cereal::BinaryInputArchive input(reader);
input(ret);
return ret;
}
template<typename T>
void PutSerialize(size_t offset, const T &value) {
m_stream.clear();
cereal::BinaryOutputArchive output(m_stream);
output(value);
auto str = m_stream.str();
if (Length() < offset + str.length()) {
if (!Resize(offset + str.length())) {
throw std::out_of_range("Packet::PutSerialize(), could not resize packet and would of written past the end.");
}
}
memcpy((char*)Data() + offset, &str[0], str.length());
}
void PutInt8(size_t offset, int8_t value);
void PutInt16(size_t offset, int16_t value);
void PutInt32(size_t offset, int32_t value);
void PutInt64(size_t offset, int64_t value);
void PutUInt8(size_t offset, uint8_t value);
void PutUInt16(size_t offset, uint16_t value);
void PutUInt32(size_t offset, uint32_t value);
void PutUInt64(size_t offset, uint64_t value);
void PutFloat(size_t offset, float value);
void PutDouble(size_t offset, double value);
void PutString(size_t offset, const std::string &str);
void PutCString(size_t offset, const char *str);
void PutPacket(size_t offset, const Packet &p);
void PutData(size_t offset, void *data, size_t length);
int8_t GetInt8(size_t offset) const;
int16_t GetInt16(size_t offset) const;
int32_t GetInt32(size_t offset) const;
int64_t GetInt64(size_t offset) const;
uint8_t GetUInt8(size_t offset) const;
uint16_t GetUInt16(size_t offset) const;
uint32_t GetUInt32(size_t offset) const;
uint64_t GetUInt64(size_t offset) const;
float GetFloat(size_t offset) const;
double GetDouble(size_t offset) const;
std::string GetString(size_t offset, size_t length) const;
std::string GetCString(size_t offset) const;
std::string ToString() const;
std::string ToString(size_t line_length) const;
protected:
std::stringstream m_stream;
};
class StaticPacket : public Packet
{
public:
StaticPacket(void *data, size_t size) { m_data = data; m_data_length = size; m_max_data_length = size; }
virtual ~StaticPacket() { }
StaticPacket(const StaticPacket &o) { m_data = o.m_data; m_data_length = o.m_data_length; }
StaticPacket& operator=(const StaticPacket &o) { m_data = o.m_data; m_data_length = o.m_data_length; return *this; }
StaticPacket(StaticPacket &&o) { m_data = o.m_data; m_data_length = o.m_data_length; }
virtual const void *Data() const { return m_data; }
virtual void *Data() { return m_data; }
virtual size_t Length() const { return m_data_length; }
virtual size_t Length() { return m_data_length; }
virtual bool Clear() { return false; }
virtual bool Resize(size_t new_size);
virtual void Reserve(size_t new_size) { }
protected:
void *m_data;
size_t m_data_length;
size_t m_max_data_length;
};
class DynamicPacket : public Packet
{
public:
DynamicPacket() { }
virtual ~DynamicPacket() { }
DynamicPacket(DynamicPacket &&o) { m_data = std::move(o.m_data); }
DynamicPacket(const DynamicPacket &o) { m_data = o.m_data; }
DynamicPacket& operator=(const DynamicPacket &o) { m_data = o.m_data; return *this; }
virtual const void *Data() const { return &m_data[0]; }
virtual void *Data() { return &m_data[0]; }
virtual size_t Length() const { return m_data.size(); }
virtual size_t Length() { return m_data.size(); }
virtual bool Clear() { m_data.clear(); return true; }
virtual bool Resize(size_t new_size) { m_data.resize(new_size, 0); return true; }
virtual void Reserve(size_t new_size) { m_data.reserve(new_size); }
protected:
std::vector<char> m_data;
};
}
}

View File

@ -0,0 +1,380 @@
#include "servertalk_client_connection.h"
#include "dns.h"
#include "../eqemu_logsys.h"
EQ::Net::ServertalkClient::ServertalkClient(const std::string &addr, int port, bool ipv6, const std::string &identifier, const std::string &credentials)
: m_timer(std::unique_ptr<EQ::Timer>(new EQ::Timer(100, true, std::bind(&EQ::Net::ServertalkClient::Connect, this))))
{
m_port = port;
m_ipv6 = ipv6;
m_identifier = identifier.empty() ? "Unknown" : identifier;
m_credentials = credentials;
m_connecting = false;
DNSLookup(addr, port, false, [this](const std::string &address) {
m_addr = address;
});
}
EQ::Net::ServertalkClient::~ServertalkClient()
{
}
void EQ::Net::ServertalkClient::Send(uint16_t opcode, EQ::Net::Packet &p)
{
EQ::Net::DynamicPacket out;
#ifdef ENABLE_SECURITY
if (m_encrypted) {
if (p.Length() == 0) {
p.PutUInt8(0, 0);
}
out.PutUInt32(0, p.Length() + crypto_secretbox_MACBYTES);
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);
}
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::DynamicPacket pout;
if (p->pBuffer) {
pout.PutData(0, p->pBuffer, p->size);
}
Send(p->opcode, pout);
}
void EQ::Net::ServertalkClient::OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb)
{
m_message_callbacks.insert(std::make_pair(opcode, cb));
}
void EQ::Net::ServertalkClient::OnMessage(std::function<void(uint16_t, EQ::Net::Packet&)> cb)
{
m_message_callback = cb;
}
void EQ::Net::ServertalkClient::Connect()
{
if (m_addr.length() == 0 || m_port == 0 || m_connection || m_connecting) {
return;
}
m_connecting = true;
EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
if (connection == nullptr) {
LogF(Logs::General, Logs::TCP_Connection, "Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port);
m_connecting = false;
return;
}
LogF(Logs::General, Logs::TCP_Connection, "Connected to {0}:{1}", m_addr, m_port);
m_connection = connection;
m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) {
LogF(Logs::General, Logs::TCP_Connection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port);
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));
m_connection->Start();
SendHello();
m_connecting = false;
});
}
void EQ::Net::ServertalkClient::ProcessData(EQ::Net::TCPConnection *c, const unsigned char *data, size_t length)
{
m_buffer.insert(m_buffer.end(), (const char*)data, (const char*)data + length);
ProcessReadBuffer();
}
void EQ::Net::ServertalkClient::SendHello()
{
EQ::Net::DynamicPacket p;
InternalSend(ServertalkClientHello, p);
}
void EQ::Net::ServertalkClient::InternalSend(ServertalkPacketType type, EQ::Net::Packet &p)
{
if (!m_connection)
return;
EQ::Net::DynamicPacket out;
out.PutUInt32(0, (uint32_t)p.Length());
out.PutUInt8(4, (uint8_t)type);
if (p.Length() > 0) {
out.PutPacket(5, p);
}
m_connection->Write((const char*)out.Data(), out.Length());
}
void EQ::Net::ServertalkClient::ProcessReadBuffer()
{
size_t current = 0;
size_t total = m_buffer.size();
while (current < total) {
auto left = total - current;
/*
//header:
//uint32 length;
//uint8 type;
*/
size_t length = 0;
uint8_t type = 0;
if (left < 5) {
break;
}
length = *(uint32_t*)&m_buffer[current];
type = *(uint8_t*)&m_buffer[current + 4];
if (current + 5 + length > total) {
break;
}
if (length == 0) {
EQ::Net::DynamicPacket p;
switch (type) {
case ServertalkServerHello:
ProcessHello(p);
break;
case ServertalkMessage:
ProcessMessage(p);
break;
}
}
else {
EQ::Net::StaticPacket p(&m_buffer[current + 5], length);
switch (type) {
case ServertalkServerHello:
ProcessHello(p);
break;
case ServertalkMessage:
ProcessMessage(p);
break;
}
}
current += length + 5;
}
if (current == total) {
m_buffer.clear();
}
else {
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + current);
}
}
void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p)
{
#ifdef ENABLE_SECURITY
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);
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 {
bool enc = p.GetInt8(0) == 1 ? true : false;
if (enc) {
if (p.Length() == (1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES)) {
memcpy(m_public_key_theirs, (char*)p.Data() + 1, crypto_box_PUBLICKEYBYTES);
memcpy(m_nonce_theirs, (char*)p.Data() + 1 + crypto_box_PUBLICKEYBYTES, crypto_box_NONCEBYTES);
m_encrypted = true;
SendHandshake();
if (m_on_connect_cb) {
m_on_connect_cb(this);
}
}
else {
LogF(Logs::General, Logs::Error, "Could not process hello, size != {0}", 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES);
}
}
else {
SendHandshake();
if (m_on_connect_cb) {
m_on_connect_cb(this);
}
}
}
catch (std::exception &ex) {
LogF(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) {
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) {
LogF(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<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))
{
LogF(Logs::General, Logs::Error, "Error decrypting message from server");
(*(uint64_t*)&m_nonce_theirs[0])++;
return;
}
EQ::Net::StaticPacket 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);
}
if (m_message_callback) {
m_message_callback(opcode, decrypted_packet);
}
}
else {
size_t message_len = length;
EQ::Net::StaticPacket packet(&data[0], message_len);
auto cb = m_message_callbacks.find(opcode);
if (cb != m_message_callbacks.end()) {
cb->second(opcode, packet);
}
if (m_message_callback) {
m_message_callback(opcode, packet);
}
}
#else
size_t message_len = length;
EQ::Net::StaticPacket packet(&data[0], message_len);
auto cb = m_message_callbacks.find(opcode);
if (cb != m_message_callbacks.end()) {
cb->second(opcode, packet);
}
if (m_message_callback) {
m_message_callback(opcode, packet);
}
#endif
}
}
catch (std::exception &ex) {
LogF(Logs::General, Logs::Error, "Error parsing message from server: {0}", ex.what());
}
}
void EQ::Net::ServertalkClient::SendHandshake(bool downgrade)
{
EQ::Net::DynamicPacket handshake;
#ifdef ENABLE_SECURITY
if (m_encrypted) {
crypto_box_keypair(m_public_key_ours, m_private_key_ours);
randombytes_buf(m_nonce_ours, crypto_box_NONCEBYTES);
crypto_box_beforenm(m_shared_key, m_public_key_theirs, m_private_key_ours);
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;
std::unique_ptr<unsigned char[]> signed_buffer(new unsigned char[cipher_length]);
std::unique_ptr<unsigned char[]> 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[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[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
if (downgrade) {
InternalSend(ServertalkClientDowngradeSecurityHandshake, handshake);
}
else {
InternalSend(ServertalkClientHandshake, handshake);
}
}

View File

@ -0,0 +1,67 @@
#pragma once
#include "tcp_connection.h"
#include "../event/timer.h"
#include "servertalk_common.h"
#include "packet.h"
#ifdef ENABLE_SECURITY
#include <sodium.h>
#endif
namespace EQ
{
namespace Net
{
class ServertalkClient
{
public:
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<void(ServertalkClient*)> cb) { m_on_connect_cb = cb; }
void OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb);
void OnMessage(std::function<void(uint16_t, EQ::Net::Packet&)> cb);
bool Connected() const { return m_connecting != true; }
std::shared_ptr<EQ::Net::TCPConnection> Handle() { return m_connection; }
private:
void Connect();
void ProcessData(EQ::Net::TCPConnection *c, const unsigned char *data, size_t length);
void SendHello();
void InternalSend(ServertalkPacketType type, EQ::Net::Packet &p);
void ProcessReadBuffer();
void ProcessHello(EQ::Net::Packet &p);
void ProcessMessage(EQ::Net::Packet &p);
void SendHandshake() { SendHandshake(false); }
void SendHandshake(bool downgrade);
std::unique_ptr<EQ::Timer> m_timer;
std::string m_addr;
std::string m_identifier;
std::string m_credentials;
bool m_connecting;
int m_port;
bool m_ipv6;
bool m_encrypted;
std::shared_ptr<EQ::Net::TCPConnection> m_connection;
std::vector<char> m_buffer;
std::unordered_map<uint16_t, std::function<void(uint16_t, EQ::Net::Packet&)>> m_message_callbacks;
std::function<void(uint16_t, EQ::Net::Packet&)> m_message_callback;
std::function<void(ServertalkClient*)> m_on_connect_cb;
#ifdef ENABLE_SECURITY
unsigned char m_public_key_ours[crypto_box_PUBLICKEYBYTES];
unsigned char m_private_key_ours[crypto_box_SECRETKEYBYTES];
unsigned char m_nonce_ours[crypto_box_NONCEBYTES];
unsigned char m_public_key_theirs[crypto_box_PUBLICKEYBYTES];
unsigned char m_nonce_theirs[crypto_box_NONCEBYTES];
unsigned char m_shared_key[crypto_box_BEFORENMBYTES];
#endif
};
}
}

View File

@ -0,0 +1,18 @@
#pragma once
#include "../servertalk.h"
namespace EQ
{
namespace Net
{
enum ServertalkPacketType
{
ServertalkClientHello = 1,
ServertalkServerHello,
ServertalkClientHandshake,
ServertalkClientDowngradeSecurityHandshake,
ServertalkMessage,
};
}
}

View File

@ -0,0 +1,154 @@
#include "servertalk_legacy_client_connection.h"
#include "dns.h"
#include "../eqemu_logsys.h"
EQ::Net::ServertalkLegacyClient::ServertalkLegacyClient(const std::string &addr, int port, bool ipv6)
: m_timer(std::unique_ptr<EQ::Timer>(new EQ::Timer(100, true, std::bind(&EQ::Net::ServertalkLegacyClient::Connect, this))))
{
m_port = port;
m_ipv6 = ipv6;
m_connecting = false;
DNSLookup(addr, port, false, [this](const std::string &address) {
m_addr = address;
});
}
EQ::Net::ServertalkLegacyClient::~ServertalkLegacyClient()
{
}
void EQ::Net::ServertalkLegacyClient::Send(uint16_t opcode, EQ::Net::Packet &p)
{
if (!m_connection)
return;
EQ::Net::DynamicPacket out;
out.PutUInt16(0, opcode);
out.PutUInt16(2, p.Length() + 4);
out.PutPacket(4, p);
m_connection->Write((const char*)out.Data(), out.Length());
}
void EQ::Net::ServertalkLegacyClient::SendPacket(ServerPacket *p)
{
EQ::Net::DynamicPacket pout;
if (p->pBuffer) {
pout.PutData(0, p->pBuffer, p->size);
}
Send(p->opcode, pout);
}
void EQ::Net::ServertalkLegacyClient::OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb)
{
m_message_callbacks.insert(std::make_pair(opcode, cb));
}
void EQ::Net::ServertalkLegacyClient::OnMessage(std::function<void(uint16_t, EQ::Net::Packet&)> cb)
{
m_message_callback = cb;
}
void EQ::Net::ServertalkLegacyClient::Connect()
{
if (m_addr.length() == 0 || m_port == 0 || m_connection || m_connecting) {
return;
}
m_connecting = true;
EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
if (connection == nullptr) {
LogF(Logs::General, Logs::TCP_Connection, "Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port);
m_connecting = false;
return;
}
LogF(Logs::General, Logs::TCP_Connection, "Connected to {0}:{1}", m_addr, m_port);
m_connection = connection;
m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) {
LogF(Logs::General, Logs::TCP_Connection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port);
m_connection.reset();
});
m_connection->OnRead(std::bind(&EQ::Net::ServertalkLegacyClient::ProcessData, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_connection->Start();
m_connecting = false;
if (m_on_connect_cb) {
m_on_connect_cb(this);
}
});
}
void EQ::Net::ServertalkLegacyClient::ProcessData(EQ::Net::TCPConnection *c, const unsigned char *data, size_t length)
{
m_buffer.insert(m_buffer.end(), (const char*)data, (const char*)data + length);
ProcessReadBuffer();
}
void EQ::Net::ServertalkLegacyClient::ProcessReadBuffer()
{
size_t current = 0;
size_t total = m_buffer.size();
while (current < total) {
auto left = total - current;
/*
//header:
//uint16 opcode;
//uint16 length;
*/
uint16_t length = 0;
uint16_t opcode = 0;
if (left < 4) {
break;
}
opcode = *(uint16_t*)&m_buffer[current];
length = *(uint16_t*)&m_buffer[current + 2];
if (length < 4) {
break;
}
length -= 4;
if (current + 4 + length > total) {
break;
}
if (length == 0) {
EQ::Net::DynamicPacket p;
auto cb = m_message_callbacks.find(opcode);
if (cb != m_message_callbacks.end()) {
cb->second(opcode, p);
}
if (m_message_callback) {
m_message_callback(opcode, p);
}
}
else {
EQ::Net::StaticPacket p(&m_buffer[current + 4], length);
auto cb = m_message_callbacks.find(opcode);
if (cb != m_message_callbacks.end()) {
cb->second(opcode, p);
}
if (m_message_callback) {
m_message_callback(opcode, p);
}
}
current += length + 4;
}
if (current == total) {
m_buffer.clear();
}
else {
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + current);
}
}

View File

@ -0,0 +1,44 @@
#pragma once
#include "tcp_connection.h"
#include "../event/timer.h"
#include "servertalk_common.h"
#include "packet.h"
namespace EQ
{
namespace Net
{
class ServertalkLegacyClient
{
public:
ServertalkLegacyClient(const std::string &addr, int port, bool ipv6);
~ServertalkLegacyClient();
void Send(uint16_t opcode, EQ::Net::Packet &p);
void SendPacket(ServerPacket *p);
void OnConnect(std::function<void(ServertalkLegacyClient*)> cb) { m_on_connect_cb = cb; }
void OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb);
void OnMessage(std::function<void(uint16_t, EQ::Net::Packet&)> cb);
bool Connected() const { return m_connecting != true; }
std::shared_ptr<EQ::Net::TCPConnection> Handle() { return m_connection; }
private:
void Connect();
void ProcessData(EQ::Net::TCPConnection *c, const unsigned char *data, size_t length);
void ProcessReadBuffer();
std::unique_ptr<EQ::Timer> m_timer;
std::string m_addr;
bool m_connecting;
int m_port;
bool m_ipv6;
std::shared_ptr<EQ::Net::TCPConnection> m_connection;
std::vector<char> m_buffer;
std::unordered_map<uint16_t, std::function<void(uint16_t, EQ::Net::Packet&)>> m_message_callbacks;
std::function<void(uint16_t, EQ::Net::Packet&)> m_message_callback;
std::function<void(ServertalkLegacyClient*)> m_on_connect_cb;
};
}
}

View File

@ -0,0 +1,97 @@
#include "servertalk_server.h"
EQ::Net::ServertalkServer::ServertalkServer()
{
}
EQ::Net::ServertalkServer::~ServertalkServer()
{
}
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<EQ::Net::TCPConnection> connection) {
m_unident_connections.push_back(std::make_shared<ServertalkServerConnection>(connection, this, m_encrypted, m_allow_downgrade));
});
}
void EQ::Net::ServertalkServer::OnConnectionIdentified(const std::string &type, std::function<void(std::shared_ptr<ServertalkServerConnection>)> cb)
{
m_on_ident.insert(std::make_pair(type, cb));
}
void EQ::Net::ServertalkServer::OnConnectionRemoved(const std::string &type, std::function<void(std::shared_ptr<ServertalkServerConnection>)> cb)
{
m_on_disc.insert(std::make_pair(type, cb));
}
void EQ::Net::ServertalkServer::ConnectionDisconnected(ServertalkServerConnection *conn)
{
if (conn->GetIdentifier().empty()) {
auto iter = m_unident_connections.begin();
while (iter != m_unident_connections.end()) {
if (conn == iter->get()) {
m_unident_connections.erase(iter);
return;
}
++iter;
}
}
else {
auto type = m_ident_connections.find(conn->GetIdentifier());
if (type != m_ident_connections.end()) {
auto iter = type->second.begin();
while (iter != type->second.end()) {
if (conn == iter->get()) {
auto on_disc = m_on_disc.find(conn->GetIdentifier());
if (on_disc != m_on_disc.end()) {
on_disc->second(*iter);
}
type->second.erase(iter);
return;
}
++iter;
}
}
}
}
void EQ::Net::ServertalkServer::ConnectionIdentified(ServertalkServerConnection *conn)
{
auto iter = m_unident_connections.begin();
while (iter != m_unident_connections.end()) {
if (conn == iter->get()) {
auto on_ident = m_on_ident.find(conn->GetIdentifier());
if (on_ident != m_on_ident.end()) {
on_ident->second(*iter);
}
if (m_ident_connections.count(conn->GetIdentifier()) > 0) {
auto &vec = m_ident_connections[conn->GetIdentifier()];
vec.push_back(*iter);
}
else {
std::vector<std::shared_ptr<EQ::Net::ServertalkServerConnection>> vec;
vec.push_back(*iter);
m_ident_connections.insert(std::make_pair(conn->GetIdentifier(), vec));
}
m_unident_connections.erase(iter);
return;
}
++iter;
}
}
bool EQ::Net::ServertalkServer::CheckCredentials(const std::string &credentials)
{
if (credentials.compare(m_credentials) == 0) {
return true;
}
return false;
}

View File

@ -0,0 +1,64 @@
#pragma once
#include "tcp_server.h"
#include "servertalk_server_connection.h"
#include <vector>
#include <map>
#ifdef ENABLE_SECURITY
#include <sodium.h>
#endif
namespace EQ
{
namespace Net
{
struct ServertalkServerOptions
{
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;
}
};
class ServertalkServer
{
public:
ServertalkServer();
~ServertalkServer();
void Listen(const ServertalkServerOptions& opts);
void OnConnectionIdentified(const std::string &type, std::function<void(std::shared_ptr<ServertalkServerConnection>)> cb);
void OnConnectionRemoved(const std::string &type, std::function<void(std::shared_ptr<ServertalkServerConnection>)> cb);
private:
void ConnectionDisconnected(ServertalkServerConnection *conn);
void ConnectionIdentified(ServertalkServerConnection *conn);
bool CheckCredentials(const std::string &credentials);
std::unique_ptr<EQ::Net::TCPServer> m_server;
std::vector<std::shared_ptr<ServertalkServerConnection>> m_unident_connections;
std::map<std::string, std::vector<std::shared_ptr<ServertalkServerConnection>>> m_ident_connections;
std::map<std::string, std::function<void(std::shared_ptr<ServertalkServerConnection>)>> m_on_ident;
std::map<std::string, std::function<void(std::shared_ptr<ServertalkServerConnection>)>> m_on_disc;
bool m_encrypted;
bool m_allow_downgrade;
std::string m_credentials;
friend class ServertalkServerConnection;
};
}
}

View File

@ -0,0 +1,348 @@
#include "servertalk_server_connection.h"
#include "servertalk_server.h"
#include "../eqemu_logsys.h"
#include "../util/uuid.h"
EQ::Net::ServertalkServerConnection::ServertalkServerConnection(std::shared_ptr<EQ::Net::TCPConnection> 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_uuid = EQ::Util::UUID::Generate().ToString();
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();
}
EQ::Net::ServertalkServerConnection::~ServertalkServerConnection()
{
}
void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet & p)
{
EQ::Net::DynamicPacket out;
#ifdef ENABLE_SECURITY
if (m_encrypted) {
if (p.Length() == 0) {
p.PutUInt8(0, 0);
}
out.PutUInt32(0, p.Length() + crypto_secretbox_MACBYTES);
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);
}
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::DynamicPacket pout;
if (p->pBuffer) {
pout.PutData(0, p->pBuffer, p->size);
}
Send(p->opcode, pout);
}
void EQ::Net::ServertalkServerConnection::OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb)
{
m_message_callbacks.insert(std::make_pair(opcode, cb));
}
void EQ::Net::ServertalkServerConnection::OnMessage(std::function<void(uint16_t, EQ::Net::Packet&)> cb)
{
m_message_callback = 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);
ProcessReadBuffer();
}
void EQ::Net::ServertalkServerConnection::ProcessReadBuffer()
{
size_t current = 0;
size_t total = m_buffer.size();
while (current < total) {
auto left = total - current;
/*
//header:
//uint32 length;
//uint8 type;
*/
size_t length = 0;
uint8_t type = 0;
if (left < 5) {
break;
}
length = *(uint32_t*)&m_buffer[current];
type = *(uint8_t*)&m_buffer[current + 4];
if (current + 5 + length > total) {
break;
}
if (length == 0) {
EQ::Net::DynamicPacket p;
switch (type) {
case ServertalkClientHello:
{
SendHello();
}
break;
case ServertalkClientHandshake:
ProcessHandshake(p);
break;
case ServertalkMessage:
ProcessMessage(p);
break;
}
}
else {
EQ::Net::StaticPacket p(&m_buffer[current + 5], length);
switch (type) {
case ServertalkClientHello:
{
SendHello();
}
break;
case ServertalkClientHandshake:
ProcessHandshake(p);
break;
case ServertalkClientDowngradeSecurityHandshake:
ProcessHandshake(p, true);
break;
case ServertalkMessage:
ProcessMessage(p);
break;
}
}
current += length + 5;
}
if (current == total) {
m_buffer.clear();
}
else {
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + current);
}
}
void EQ::Net::ServertalkServerConnection::OnDisconnect(TCPConnection *c)
{
m_parent->ConnectionDisconnected(this);
}
void EQ::Net::ServertalkServerConnection::SendHello()
{
EQ::Net::DynamicPacket hello;
#ifdef ENABLE_SECURITY
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);
memset(m_nonce_ours, 0, crypto_box_NONCEBYTES);
memset(m_nonce_theirs, 0, crypto_box_NONCEBYTES);
if (m_encrypted) {
hello.PutInt8(0, 1);
crypto_box_keypair(m_public_key_ours, m_private_key_ours);
randombytes_buf(m_nonce_ours, crypto_box_NONCEBYTES);
hello.PutData(1, m_public_key_ours, crypto_box_PUBLICKEYBYTES);
hello.PutData(1 + crypto_box_PUBLICKEYBYTES, m_nonce_ours, crypto_box_NONCEBYTES);
}
else {
hello.PutInt8(0, 0);
}
#else
hello.PutInt8(0, 0);
#endif
InternalSend(ServertalkServerHello, hello);
}
void EQ::Net::ServertalkServerConnection::InternalSend(ServertalkPacketType type, EQ::Net::Packet &p)
{
if (!m_connection)
return;
EQ::Net::DynamicPacket out;
out.PutUInt32(0, (uint32_t)p.Length());
out.PutUInt8(4, (uint8_t)type);
if (p.Length() > 0) {
out.PutPacket(5, p);
}
m_connection->Write((const char*)out.Data(), out.Length());
}
void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, bool downgrade_security)
{
#ifdef ENABLE_SECURITY
if (downgrade_security && m_allow_downgrade && m_encrypted) {
LogF(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)) {
memcpy(m_public_key_theirs, (char*)p.Data(), crypto_box_PUBLICKEYBYTES);
memcpy(m_nonce_theirs, (char*)p.Data() + crypto_box_PUBLICKEYBYTES, crypto_box_NONCEBYTES);
crypto_box_beforenm(m_shared_key, m_public_key_theirs, m_private_key_ours);
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))
{
LogF(Logs::General, Logs::Error, "Error decrypting handshake from client, dropping connection.");
m_connection->Disconnect();
return;
}
m_identifier = (const char*)&decrypted_text[0];
std::string credentials = (const char*)&decrypted_text[0] + (m_identifier.length() + 1);
if (!m_parent->CheckCredentials(credentials)) {
LogF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection.");
m_connection->Disconnect();
return;
}
m_parent->ConnectionIdentified(this);
(*(uint64_t*)&m_nonce_theirs[0])++;
}
}
catch (std::exception &ex) {
LogF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what());
m_connection->Disconnect();
}
}
else {
try {
m_identifier = p.GetCString(0);
auto credentials = p.GetCString(m_identifier.length() + 1);
if (!m_parent->CheckCredentials(credentials)) {
LogF(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) {
LogF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what());
m_connection->Disconnect();
}
}
#else
try {
m_identifier = p.GetCString(0);
auto credentials = p.GetCString(m_identifier.length() + 1);
if (!m_parent->CheckCredentials(credentials)) {
LogF(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) {
LogF(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<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))
{
LogF(Logs::General, Logs::Error, "Error decrypting message from client");
(*(uint64_t*)&m_nonce_theirs[0])++;
return;
}
EQ::Net::StaticPacket 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);
}
if (m_message_callback) {
m_message_callback(opcode, decrypted_packet);
}
}
else {
size_t message_len = length;
EQ::Net::StaticPacket packet(&data[0], message_len);
auto cb = m_message_callbacks.find(opcode);
if (cb != m_message_callbacks.end()) {
cb->second(opcode, packet);
}
if (m_message_callback) {
m_message_callback(opcode, packet);
}
}
#else
size_t message_len = length;
EQ::Net::StaticPacket packet(&data[0], message_len);
auto cb = m_message_callbacks.find(opcode);
if (cb != m_message_callbacks.end()) {
cb->second(opcode, packet);
}
if (m_message_callback) {
m_message_callback(opcode, packet);
}
#endif
}
}
catch (std::exception &ex) {
LogF(Logs::General, Logs::Error, "Error parsing message from client: {0}", ex.what());
}
}

View File

@ -0,0 +1,63 @@
#pragma once
#include "tcp_connection.h"
#include "servertalk_common.h"
#include "packet.h"
#include <vector>
#ifdef ENABLE_SECURITY
#include <sodium.h>
#endif
namespace EQ
{
namespace Net
{
class ServertalkServer;
class ServertalkServerConnection
{
public:
ServertalkServerConnection(std::shared_ptr<EQ::Net::TCPConnection> c, ServertalkServer *parent, bool encrypted, bool allow_downgrade);
~ServertalkServerConnection();
void Send(uint16_t opcode, EQ::Net::Packet &p);
void SendPacket(ServerPacket *p);
void OnMessage(uint16_t opcode, std::function<void(uint16_t, EQ::Net::Packet&)> cb);
void OnMessage(std::function<void(uint16_t, EQ::Net::Packet&)> cb);
std::string GetIdentifier() const { return m_identifier; }
std::shared_ptr<EQ::Net::TCPConnection> Handle() { return m_connection; }
std::string GetUUID() const { return m_uuid; }
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) { ProcessHandshake(p, false); }
void ProcessHandshake(EQ::Net::Packet &p, bool security_downgrade);
void ProcessMessage(EQ::Net::Packet &p);
std::shared_ptr<EQ::Net::TCPConnection> m_connection;
ServertalkServer *m_parent;
std::vector<char> m_buffer;
std::unordered_map<uint16_t, std::function<void(uint16_t, EQ::Net::Packet&)>> m_message_callbacks;
std::function<void(uint16_t, EQ::Net::Packet&)> m_message_callback;
std::string m_identifier;
std::string m_uuid;
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];
unsigned char m_nonce_ours[crypto_box_NONCEBYTES];
unsigned char m_public_key_theirs[crypto_box_PUBLICKEYBYTES];
unsigned char m_nonce_theirs[crypto_box_NONCEBYTES];
unsigned char m_shared_key[crypto_box_BEFORENMBYTES];
#endif
};
}
}

View File

@ -0,0 +1,238 @@
#include "tcp_connection.h"
#include "../event/event_loop.h"
void on_close_handle(uv_handle_t* handle) {
delete handle;
}
EQ::Net::TCPConnection::TCPConnection(uv_tcp_t *socket)
{
m_socket = socket;
m_socket->data = this;
}
EQ::Net::TCPConnection::~TCPConnection() {
Disconnect();
}
void EQ::Net::TCPConnection::Connect(const std::string &addr, int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb)
{
struct EQTCPConnectBaton
{
uv_tcp_t *socket;
std::function<void(std::shared_ptr<EQ::Net::TCPConnection>)> cb;
};
auto loop = EQ::EventLoop::Get().Handle();
uv_tcp_t *socket = new uv_tcp_t;
memset(socket, 0, sizeof(uv_tcp_t));
uv_tcp_init(loop, socket);
sockaddr_storage iaddr;
if (ipv6) {
uv_ip6_addr(addr.c_str(), port, (sockaddr_in6*)&iaddr);
}
else {
uv_ip4_addr(addr.c_str(), port, (sockaddr_in*)&iaddr);
}
uv_connect_t *connect = new uv_connect_t;
memset(connect, 0, sizeof(uv_connect_t));
EQTCPConnectBaton *baton = new EQTCPConnectBaton;
baton->cb = cb;
baton->socket = socket;
connect->data = baton;
uv_tcp_connect(connect, socket, (sockaddr*)&iaddr,
[](uv_connect_t* req, int status) {
EQTCPConnectBaton *baton = (EQTCPConnectBaton*)req->data;
auto socket = baton->socket;
auto cb = baton->cb;
delete baton;
if (status < 0) {
uv_close((uv_handle_t*)socket, on_close_handle);
delete req;
cb(nullptr);
}
else {
delete req;
std::shared_ptr<EQ::Net::TCPConnection> connection(new EQ::Net::TCPConnection(socket));
cb(connection);
}
});
}
void EQ::Net::TCPConnection::Start() {
uv_read_start((uv_stream_t*)m_socket, [](uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
buf->base = new char[suggested_size];
buf->len = suggested_size;
}, [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
TCPConnection *connection = (TCPConnection*)stream->data;
if (nread > 0) {
connection->Read(buf->base, nread);
if (buf->base) {
delete[] buf->base;
}
}
else if (nread == UV_EOF) {
if (buf->base) {
delete[] buf->base;
}
}
else if (nread < 0) {
connection->Disconnect();
if (buf->base) {
delete[] buf->base;
}
}
});
}
void EQ::Net::TCPConnection::OnRead(std::function<void(TCPConnection*, const unsigned char*, size_t)> cb)
{
m_on_read_cb = cb;
}
void EQ::Net::TCPConnection::OnDisconnect(std::function<void(TCPConnection*)> cb)
{
m_on_disconnect_cb = cb;
}
void EQ::Net::TCPConnection::Disconnect()
{
if (m_socket) {
m_socket->data = this;
uv_close((uv_handle_t*)m_socket, [](uv_handle_t* handle) {
TCPConnection *connection = (TCPConnection*)handle->data;
if (connection->m_on_disconnect_cb) {
connection->m_on_disconnect_cb(connection);
}
delete handle;
});
m_socket = nullptr;
}
}
void EQ::Net::TCPConnection::Read(const char *data, size_t count)
{
if (m_on_read_cb) {
m_on_read_cb(this, (unsigned char*)data, count);
}
}
void EQ::Net::TCPConnection::Write(const char *data, size_t count)
{
if (!m_socket) {
return;
}
struct WriteBaton
{
TCPConnection *connection;
char *buffer;
};
WriteBaton *baton = new WriteBaton;
baton->connection = this;
baton->buffer = new char[count];;
uv_write_t *write_req = new uv_write_t;
memset(write_req, 0, sizeof(uv_write_t));
write_req->data = baton;
uv_buf_t send_buffers[1];
memcpy(baton->buffer, data, count);
send_buffers[0] = uv_buf_init(baton->buffer, count);
uv_write(write_req, (uv_stream_t*)m_socket, send_buffers, 1, [](uv_write_t* req, int status) {
WriteBaton *baton = (WriteBaton*)req->data;
delete[] baton->buffer;
delete req;
if (status < 0) {
baton->connection->Disconnect();
}
delete baton;
});
}
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;
int addr_len = sizeof(addr);
uv_tcp_getpeername(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::RemotePort() const
{
sockaddr_storage addr;
int addr_len = sizeof(addr);
uv_tcp_getpeername(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;
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <functional>
#include <string>
#include <memory>
#include <uv.h>
namespace EQ
{
namespace Net
{
class TCPConnection
{
public:
TCPConnection(uv_tcp_t *socket);
~TCPConnection();
static void Connect(const std::string &addr, int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb);
void Start();
void OnRead(std::function<void(TCPConnection*, const unsigned char *, size_t)> cb);
void OnDisconnect(std::function<void(TCPConnection*)> cb);
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:
TCPConnection();
uv_tcp_t *m_socket;
std::function<void(TCPConnection*, const unsigned char *, size_t)> m_on_read_cb;
std::function<void(TCPConnection*)> m_on_disconnect_cb;
};
}
}

85
common/net/tcp_server.cpp Normal file
View File

@ -0,0 +1,85 @@
#include "tcp_server.h"
#include "../event/event_loop.h"
void on_close_tcp_server_handle(uv_handle_t* handle) {
delete handle;
}
EQ::Net::TCPServer::TCPServer()
{
m_socket = nullptr;
}
EQ::Net::TCPServer::~TCPServer() {
Close();
}
void EQ::Net::TCPServer::Listen(int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb)
{
if (ipv6) {
Listen("::", port, ipv6, cb);
}
else {
Listen("0.0.0.0", port, ipv6, cb);
}
}
void EQ::Net::TCPServer::Listen(const std::string &addr, int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb)
{
if (m_socket) {
return;
}
m_on_new_connection = cb;
auto loop = EQ::EventLoop::Get().Handle();
m_socket = new uv_tcp_t;
memset(m_socket, 0, sizeof(uv_tcp_t));
uv_tcp_init(loop, m_socket);
sockaddr_storage iaddr;
if (ipv6) {
uv_ip6_addr(addr.c_str(), port, (sockaddr_in6*)&iaddr);
}
else {
uv_ip4_addr(addr.c_str(), port, (sockaddr_in*)&iaddr);
}
uv_tcp_bind(m_socket, (sockaddr*)&iaddr, 0);
m_socket->data = this;
uv_listen((uv_stream_t*)m_socket, 128, [](uv_stream_t* server, int status) {
if (status < 0) {
return;
}
auto loop = EQ::EventLoop::Get().Handle();
uv_tcp_t *client = new uv_tcp_t;
memset(client, 0, sizeof(uv_tcp_t));
uv_tcp_init(loop, client);
if (uv_accept(server, (uv_stream_t*)client) < 0) {
delete client;
return;
}
EQ::Net::TCPServer *s = (EQ::Net::TCPServer*)server->data;
s->AddClient(client);
});
}
void EQ::Net::TCPServer::Close()
{
if (m_socket) {
uv_close((uv_handle_t*)m_socket, on_close_tcp_server_handle);
m_socket = nullptr;
}
}
void EQ::Net::TCPServer::AddClient(uv_tcp_t *c)
{
std::shared_ptr<TCPConnection> client(new TCPConnection(c));
if (m_on_new_connection) {
m_on_new_connection(client);
}
}

25
common/net/tcp_server.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include "tcp_connection.h"
namespace EQ
{
namespace Net
{
class TCPServer
{
public:
TCPServer();
~TCPServer();
void Listen(int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb);
void Listen(const std::string &addr, int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb);
void Close();
void AddClient(uv_tcp_t *c);
private:
std::function<void(std::shared_ptr<TCPConnection>)> m_on_new_connection;
uv_tcp_t *m_socket;
};
}
}

View File

@ -152,6 +152,15 @@ void DumpPacket(const ServerPacket* pack, bool iShowInfo) {
DumpPacketHex(pack->pBuffer, pack->size);
}
void DumpPacket(uint16 opcode, const EQ::Net::Packet &p, bool iShowInfo) {
if (iShowInfo) {
std::cout << "Dumping ServerPacket: 0x" << std::hex << std::setfill('0') << std::setw(4) << opcode << std::dec;
std::cout << " size:" << p.Length() << std::endl;
}
std::cout << p.ToString() << std::endl;
}
void DumpPacketBin(const ServerPacket* pack) {
DumpPacketBin(pack->pBuffer, pack->size);
}

View File

@ -18,7 +18,8 @@
#ifndef PACKET_DUMP_H
#define PACKET_DUMP_H
#include "../common/types.h"
#include "types.h"
#include "net/packet.h"
class ServerPacket;
@ -28,6 +29,7 @@ std::string DumpPacketHexToString(const uchar* buf, uint32 size, uint32 cols = 1
void DumpPacketBin(const void* data, uint32 len);
void DumpPacket(const uchar* buf, uint32 size);
void DumpPacket(const ServerPacket* pack, bool iShowInfo = false);
void DumpPacket(uint16 opcode, const EQ::Net::Packet &p, bool iShowInfo = false);
void DumpPacketBin(const ServerPacket* pack);
void DumpPacketBin(uint32 data);
void DumpPacketBin(uint16 data);

View File

@ -37,7 +37,7 @@
#include <stdarg.h>
#endif
#include "eq_stream.h"
#include "eq_stream_intf.h"
#include "packet_dump_file.h"
void FileDumpPacketAscii(const char* filename, const uchar* buf, uint32 size, uint32 cols, uint32 skip) {

View File

@ -90,7 +90,7 @@ namespace RoF
//ok, now we have what we need to register.
EQStream::Signature signature;
EQStreamInterface::Signature signature;
std::string pname;
//register our world signature.

View File

@ -90,7 +90,7 @@ namespace RoF2
//ok, now we have what we need to register.
EQStream::Signature signature;
EQStreamInterface::Signature signature;
std::string pname;
//register our world signature.

View File

@ -86,7 +86,7 @@ namespace SoD
//ok, now we have what we need to register.
EQStream::Signature signature;
EQStreamInterface::Signature signature;
std::string pname;
//register our world signature.

View File

@ -86,7 +86,7 @@ namespace SoF
//ok, now we have what we need to register.
EQStream::Signature signature;
EQStreamInterface::Signature signature;
std::string pname;
//register our world signature.

View File

@ -17,5 +17,5 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define E(x) static void Encode_##x(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req);
#define E(x) static void Encode_##x(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req);
#define D(x) static void Decode_##x(EQApplicationPacket *p);

View File

@ -17,7 +17,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define ENCODE(x) void Strategy::Encode_##x(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req)
#define ENCODE(x) void Strategy::Encode_##x(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req)
#define DECODE(x) void Strategy::Decode_##x(EQApplicationPacket *__packet)
#define StructDist(in, f1, f2) (uint32(&in->f2)-uint32(&in->f1))

View File

@ -85,7 +85,7 @@ namespace Titanium
//ok, now we have what we need to register.
EQStream::Signature signature;
EQStreamInterface::Signature signature;
std::string pname;
//register our world signature.

View File

@ -86,7 +86,7 @@ namespace UF
//ok, now we have what we need to register.
EQStream::Signature signature;
EQStreamInterface::Signature signature;
std::string pname;
//register our world signature.

View File

@ -13,7 +13,8 @@ enum EQEmuExePlatform
ExePlatformLaunch,
ExePlatformSharedMemory,
ExePlatformClientImport,
ExePlatformClientExport
ExePlatformClientExport,
ExePlatformHC
};
void RegisterExecutablePlatform(EQEmuExePlatform p);

View File

@ -22,7 +22,8 @@
#include <map>
#include <vector>
enum { //values for pTimerType
enum : int { //values for pTimerType
pTimerNegativeItemReuse = -1, // these grow down basically, we will have item ID * -1 for the timer ID
pTimerStartAdventureTimer = 1,
pTimerSurnameChange = 2,
pTimerFeignDeath = 3,

View File

@ -56,6 +56,7 @@ RULE_REAL(Character, GroupExpMultiplier, 0.5)
RULE_REAL(Character, RaidExpMultiplier, 0.2)
RULE_BOOL(Character, UseXPConScaling, true)
RULE_INT(Character, ShowExpValues, 0) //0 - normal, 1 - Show raw experience values, 2 - Show raw experience values AND percent.
RULE_INT(Character, GreenModifier, 20)
RULE_INT(Character, LightBlueModifier, 40)
RULE_INT(Character, BlueModifier, 90)
RULE_INT(Character, WhiteModifier, 100)
@ -149,6 +150,7 @@ RULE_INT(Character, AvoidanceCap, 750) // 750 Is a pretty good value, seen peopl
RULE_BOOL(Character, AllowMQTarget, false) // Disables putting players in the 'hackers' list for targeting beyond the clip plane or attempting to target something untargetable
RULE_BOOL(Character, UseOldBindWound, false) // Uses the original bind wound behavior
RULE_BOOL(Character, GrantHoTTOnCreate, false) // Grant Health of Target's Target leadership AA on character creation
RULE_BOOL(Character, UseOldConSystem, false) // Grant Health of Target's Target leadership AA on character creation
RULE_CATEGORY_END()
RULE_CATEGORY(Mercs)

View File

@ -4,6 +4,9 @@
#include "../common/types.h"
#include "../common/packet_functions.h"
#include "../common/eq_packet_structs.h"
#include "../net/packet.h"
#include <cereal/cereal.hpp>
#include <cereal/types/string.hpp>
#define SERVER_TIMEOUT 45000 // how often keepalive gets sent
#define INTERSERVER_TIMER 10000
@ -59,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
@ -85,6 +88,9 @@
#define ServerOP_DepopPlayerCorpse 0x0065
#define ServerOP_RequestTellQueue 0x0066 // client asks for it's tell queues
#define ServerOP_ChangeSharedMem 0x0067
#define ServerOP_WebInterfaceEvent 0x0068
#define ServerOP_WebInterfaceSubscribe 0x0069
#define ServerOP_WebInterfaceUnsubscribe 0x0070
#define ServerOP_RaidAdd 0x0100 //in use
#define ServerOP_RaidRemove 0x0101 //in use
@ -221,6 +227,22 @@ public:
_wpos = 0;
_rpos = 0;
}
ServerPacket(uint16 in_opcode, const EQ::Net::Packet &p) {
this->compressed = false;
size = (uint32)p.Length();
opcode = in_opcode;
if (size == 0) {
pBuffer = 0;
}
else {
pBuffer = new uchar[size];
memcpy(pBuffer, p.Data(), size);
}
_wpos = 0;
_rpos = 0;
}
ServerPacket* Copy() {
if (this == 0) {
return 0;
@ -232,43 +254,6 @@ public:
ret->InflatedSize = this->InflatedSize;
return ret;
}
bool Deflate() {
if (compressed)
return false;
if ((!this->pBuffer) || (!this->size))
return false;
uchar* tmp = new uchar[this->size + 128];
uint32 tmpsize = DeflatePacket(this->pBuffer, this->size, tmp, this->size + 128);
if (!tmpsize) {
safe_delete_array(tmp);
return false;
}
this->compressed = true;
this->InflatedSize = this->size;
this->size = tmpsize;
uchar* tmpdel = this->pBuffer;
this->pBuffer = tmp;
safe_delete_array(tmpdel);
return true;
}
bool Inflate() {
if (!compressed)
return false;
if ((!this->pBuffer) || (!this->size))
return false;
uchar* tmp = new uchar[InflatedSize];
uint32 tmpsize = InflatePacket(this->pBuffer, this->size, tmp, InflatedSize);
if (!tmpsize) {
safe_delete_array(tmp);
return false;
}
compressed = false;
this->size = tmpsize;
uchar* tmpdel = this->pBuffer;
this->pBuffer = tmp;
safe_delete_array(tmpdel);
return true;
}
void WriteUInt8(uint8 value) { *(uint8 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint8); }
void WriteUInt32(uint32 value) { *(uint32 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint32); }
@ -524,14 +509,21 @@ struct ServerLSPlayerZoneChange_Struct {
uint32 from; // 0 = world
uint32 to; // 0 = world
};
struct ClientAuth_Struct {
uint32 lsaccount_id; // ID# in login server's db
char name[30]; // username in login server's db
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;
uint8 local; // 1 if the client is from the local network
uint32 lsaccount_id; // ID# in login server's db
char name[30]; // username in login server's db
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;
uint8 local; // 1 if the client is from the local network
template <class Archive>
void serialize(Archive &ar)
{
ar(lsaccount_id, name, key, lsadmin, worldadmin, ip, local);
}
};
struct ServerSystemwideMessage {

View File

@ -15,6 +15,7 @@
*/
#include "string_util.h"
#include <algorithm>
#ifdef _WINDOWS
#include <windows.h>
@ -71,246 +72,6 @@ const std::string StringFormat(const char* format, ...)
return output;
}
// normal strncpy doesnt put a null term on copied strings, this one does
// ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp
char* strn0cpy(char* dest, const char* source, uint32 size) {
if (!dest)
return 0;
if (size == 0 || source == 0) {
dest[0] = 0;
return dest;
}
strncpy(dest, source, size);
dest[size - 1] = 0;
return dest;
}
// String N w/null Copy Truncated?
// return value =true if entire string(source) fit, false if it was truncated
bool strn0cpyt(char* dest, const char* source, uint32 size) {
if (!dest)
return 0;
if (size == 0 || source == 0) {
dest[0] = 0;
return false;
}
strncpy(dest, source, size);
dest[size - 1] = 0;
return (bool) (source[strlen(dest)] == 0);
}
const char *MakeLowerString(const char *source) {
static char str[128];
if (!source)
return nullptr;
MakeLowerString(source, str);
return str;
}
void MakeLowerString(const char *source, char *target) {
if (!source || !target) {
*target=0;
return;
}
while (*source)
{
*target = tolower(*source);
target++;source++;
}
*target = 0;
}
int MakeAnyLenString(char** ret, const char* format, ...) {
int buf_len = 128;
int chars = -1;
va_list argptr, tmpargptr;
va_start(argptr, format);
while (chars == -1 || chars >= buf_len) {
safe_delete_array(*ret);
if (chars == -1)
buf_len *= 2;
else
buf_len = chars + 1;
*ret = new char[buf_len];
va_copy(tmpargptr, argptr);
chars = vsnprintf(*ret, buf_len, format, tmpargptr);
}
va_end(argptr);
return chars;
}
uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...) {
if (*bufsize == 0)
*bufsize = 256;
if (*ret == 0)
*strlen = 0;
int chars = -1;
char* oldret = 0;
va_list argptr, tmpargptr;
va_start(argptr, format);
while (chars == -1 || chars >= (int32)(*bufsize-*strlen)) {
if (chars == -1)
*bufsize += 256;
else
*bufsize += chars + 25;
oldret = *ret;
*ret = new char[*bufsize];
if (oldret) {
if (*strlen)
memcpy(*ret, oldret, *strlen);
safe_delete_array(oldret);
}
va_copy(tmpargptr, argptr);
chars = vsnprintf(&(*ret)[*strlen], (*bufsize-*strlen), format, tmpargptr);
}
va_end(argptr);
*strlen += chars;
return *strlen;
}
uint32 hextoi(const char* num) {
if (num == nullptr)
return 0;
int len = strlen(num);
if (len < 3)
return 0;
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X'))
return 0;
uint32 ret = 0;
int mul = 1;
for (int i=len-1; i>=2; i--) {
if (num[i] >= 'A' && num[i] <= 'F')
ret += ((num[i] - 'A') + 10) * mul;
else if (num[i] >= 'a' && num[i] <= 'f')
ret += ((num[i] - 'a') + 10) * mul;
else if (num[i] >= '0' && num[i] <= '9')
ret += (num[i] - '0') * mul;
else
return 0;
mul *= 16;
}
return ret;
}
uint64 hextoi64(const char* num) {
if (num == nullptr)
return 0;
int len = strlen(num);
if (len < 3)
return 0;
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X'))
return 0;
uint64 ret = 0;
int mul = 1;
for (int i=len-1; i>=2; i--) {
if (num[i] >= 'A' && num[i] <= 'F')
ret += ((num[i] - 'A') + 10) * mul;
else if (num[i] >= 'a' && num[i] <= 'f')
ret += ((num[i] - 'a') + 10) * mul;
else if (num[i] >= '0' && num[i] <= '9')
ret += (num[i] - '0') * mul;
else
return 0;
mul *= 16;
}
return ret;
}
bool atobool(const char* iBool) {
if (iBool == nullptr)
return false;
if (!strcasecmp(iBool, "true"))
return true;
if (!strcasecmp(iBool, "false"))
return false;
if (!strcasecmp(iBool, "yes"))
return true;
if (!strcasecmp(iBool, "no"))
return false;
if (!strcasecmp(iBool, "on"))
return true;
if (!strcasecmp(iBool, "off"))
return false;
if (!strcasecmp(iBool, "enable"))
return true;
if (!strcasecmp(iBool, "disable"))
return false;
if (!strcasecmp(iBool, "enabled"))
return true;
if (!strcasecmp(iBool, "disabled"))
return false;
if (!strcasecmp(iBool, "y"))
return true;
if (!strcasecmp(iBool, "n"))
return false;
if (atoi(iBool))
return true;
return false;
}
// removes the crap and turns the underscores into spaces.
char *CleanMobName(const char *in, char *out)
{
unsigned i, j;
for(i = j = 0; i < strlen(in); i++)
{
// convert _ to space.. any other conversions like this? I *think* this
// is the only non alpha char that's not stripped but converted.
if(in[i] == '_')
{
out[j++] = ' ';
}
else
{
if(isalpha(in[i]) || (in[i] == '`')) // numbers, #, or any other crap just gets skipped
out[j++] = in[i];
}
}
out[j] = 0; // terimnate the string before returning it
return out;
}
void RemoveApostrophes(std::string &s)
{
for(unsigned int i = 0; i < s.length(); ++i)
if(s[i] == '\'')
s[i] = '_';
}
char *RemoveApostrophes(const char *s)
{
auto NewString = new char[strlen(s) + 1];
strcpy(NewString, s);
for(unsigned int i = 0 ; i < strlen(NewString); ++i)
if(NewString[i] == '\'')
NewString[i] = '_';
return NewString;
}
const char *ConvertArray(int input, char *returnchar)
{
sprintf(returnchar, "%i" ,input);
return returnchar;
}
const char *ConvertArrayF(float input, char *returnchar)
{
sprintf(returnchar, "%0.2f", input);
return returnchar;
}
std::vector<std::string> SplitString(const std::string &str, char delim) {
std::vector<std::string> ret;
std::stringstream ss(str);
@ -396,16 +157,35 @@ std::string EscapeString(const char *src, size_t sz) {
return ret;
}
bool isAlphaNumeric(const char *text)
{
for (unsigned int charIndex=0; charIndex<strlen(text); charIndex++) {
if ((text[charIndex] < 'a' || text[charIndex] > 'z') &&
(text[charIndex] < 'A' || text[charIndex] > 'Z') &&
(text[charIndex] < '0' || text[charIndex] > '9'))
return false;
bool StringIsNumber(const std::string &s) {
try {
auto r = stod(s);
return true;
}
catch (std::exception) {
return false;
}
}
void ToLowerString(std::string &s) {
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
}
void ToUpperString(std::string &s) {
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
}
std::string JoinString(const std::vector<std::string>& ar, const std::string &delim) {
std::string ret;
for (size_t i = 0; i < ar.size(); ++i) {
if (i != 0) {
ret += delim;
}
ret += ar[i];
}
return true;
return ret;
}
void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string) {
@ -415,3 +195,257 @@ void find_replace(std::string& string_subject, const std::string& search_string,
index = string_subject.find_first_of(search_string);
}
}
//Const char based
// normal strncpy doesnt put a null term on copied strings, this one does
// ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp
char* strn0cpy(char* dest, const char* source, uint32 size) {
if (!dest)
return 0;
if (size == 0 || source == 0) {
dest[0] = 0;
return dest;
}
strncpy(dest, source, size);
dest[size - 1] = 0;
return dest;
}
// String N w/null Copy Truncated?
// return value =true if entire string(source) fit, false if it was truncated
bool strn0cpyt(char* dest, const char* source, uint32 size) {
if (!dest)
return 0;
if (size == 0 || source == 0) {
dest[0] = 0;
return false;
}
strncpy(dest, source, size);
dest[size - 1] = 0;
return (bool)(source[strlen(dest)] == 0);
}
const char *MakeLowerString(const char *source) {
static char str[128];
if (!source)
return nullptr;
MakeLowerString(source, str);
return str;
}
void MakeLowerString(const char *source, char *target) {
if (!source || !target) {
*target = 0;
return;
}
while (*source)
{
*target = tolower(*source);
target++; source++;
}
*target = 0;
}
int MakeAnyLenString(char** ret, const char* format, ...) {
int buf_len = 128;
int chars = -1;
va_list argptr, tmpargptr;
va_start(argptr, format);
while (chars == -1 || chars >= buf_len) {
safe_delete_array(*ret);
if (chars == -1)
buf_len *= 2;
else
buf_len = chars + 1;
*ret = new char[buf_len];
va_copy(tmpargptr, argptr);
chars = vsnprintf(*ret, buf_len, format, tmpargptr);
}
va_end(argptr);
return chars;
}
uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...) {
if (*bufsize == 0)
*bufsize = 256;
if (*ret == 0)
*strlen = 0;
int chars = -1;
char* oldret = 0;
va_list argptr, tmpargptr;
va_start(argptr, format);
while (chars == -1 || chars >= (int32)(*bufsize - *strlen)) {
if (chars == -1)
*bufsize += 256;
else
*bufsize += chars + 25;
oldret = *ret;
*ret = new char[*bufsize];
if (oldret) {
if (*strlen)
memcpy(*ret, oldret, *strlen);
safe_delete_array(oldret);
}
va_copy(tmpargptr, argptr);
chars = vsnprintf(&(*ret)[*strlen], (*bufsize - *strlen), format, tmpargptr);
}
va_end(argptr);
*strlen += chars;
return *strlen;
}
uint32 hextoi(const char* num) {
if (num == nullptr)
return 0;
int len = strlen(num);
if (len < 3)
return 0;
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X'))
return 0;
uint32 ret = 0;
int mul = 1;
for (int i = len - 1; i >= 2; i--) {
if (num[i] >= 'A' && num[i] <= 'F')
ret += ((num[i] - 'A') + 10) * mul;
else if (num[i] >= 'a' && num[i] <= 'f')
ret += ((num[i] - 'a') + 10) * mul;
else if (num[i] >= '0' && num[i] <= '9')
ret += (num[i] - '0') * mul;
else
return 0;
mul *= 16;
}
return ret;
}
uint64 hextoi64(const char* num) {
if (num == nullptr)
return 0;
int len = strlen(num);
if (len < 3)
return 0;
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X'))
return 0;
uint64 ret = 0;
int mul = 1;
for (int i = len - 1; i >= 2; i--) {
if (num[i] >= 'A' && num[i] <= 'F')
ret += ((num[i] - 'A') + 10) * mul;
else if (num[i] >= 'a' && num[i] <= 'f')
ret += ((num[i] - 'a') + 10) * mul;
else if (num[i] >= '0' && num[i] <= '9')
ret += (num[i] - '0') * mul;
else
return 0;
mul *= 16;
}
return ret;
}
bool atobool(const char* iBool) {
if (iBool == nullptr)
return false;
if (!strcasecmp(iBool, "true"))
return true;
if (!strcasecmp(iBool, "false"))
return false;
if (!strcasecmp(iBool, "yes"))
return true;
if (!strcasecmp(iBool, "no"))
return false;
if (!strcasecmp(iBool, "on"))
return true;
if (!strcasecmp(iBool, "off"))
return false;
if (!strcasecmp(iBool, "enable"))
return true;
if (!strcasecmp(iBool, "disable"))
return false;
if (!strcasecmp(iBool, "enabled"))
return true;
if (!strcasecmp(iBool, "disabled"))
return false;
if (!strcasecmp(iBool, "y"))
return true;
if (!strcasecmp(iBool, "n"))
return false;
if (atoi(iBool))
return true;
return false;
}
// removes the crap and turns the underscores into spaces.
char *CleanMobName(const char *in, char *out)
{
unsigned i, j;
for (i = j = 0; i < strlen(in); i++)
{
// convert _ to space.. any other conversions like this? I *think* this
// is the only non alpha char that's not stripped but converted.
if (in[i] == '_')
{
out[j++] = ' ';
}
else
{
if (isalpha(in[i]) || (in[i] == '`')) // numbers, #, or any other crap just gets skipped
out[j++] = in[i];
}
}
out[j] = 0; // terimnate the string before returning it
return out;
}
void RemoveApostrophes(std::string &s)
{
for (unsigned int i = 0; i < s.length(); ++i)
if (s[i] == '\'')
s[i] = '_';
}
char *RemoveApostrophes(const char *s)
{
auto NewString = new char[strlen(s) + 1];
strcpy(NewString, s);
for (unsigned int i = 0; i < strlen(NewString); ++i)
if (NewString[i] == '\'')
NewString[i] = '_';
return NewString;
}
const char *ConvertArray(int input, char *returnchar)
{
sprintf(returnchar, "%i", input);
return returnchar;
}
const char *ConvertArrayF(float input, char *returnchar)
{
sprintf(returnchar, "%0.2f", input);
return returnchar;
}
bool isAlphaNumeric(const char *text)
{
for (unsigned int charIndex = 0; charIndex<strlen(text); charIndex++) {
if ((text[charIndex] < 'a' || text[charIndex] > 'z') &&
(text[charIndex] < 'A' || text[charIndex] > 'Z') &&
(text[charIndex] < '0' || text[charIndex] > '9'))
return false;
}
return true;
}

View File

@ -23,34 +23,34 @@
#include "types.h"
//std::string based
const std::string StringFormat(const char* format, ...);
const std::string vStringFormat(const char* format, va_list args);
std::vector<std::string> SplitString(const std::string &s, char delim);
std::string EscapeString(const char *src, size_t sz);
std::string EscapeString(const std::string &s);
bool StringIsNumber(const std::string &s);
void ToLowerString(std::string &s);
void ToUpperString(std::string &s);
std::string JoinString(const std::vector<std::string>& ar, const std::string &delim);
void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string);
//const char based
bool atobool(const char* iBool);
bool isAlphaNumeric(const char *text);
bool strn0cpyt(char* dest, const char* source, uint32 size);
char *CleanMobName(const char *in, char *out);
char *RemoveApostrophes(const char *s);
char* strn0cpy(char* dest, const char* source, uint32 size);
const char *ConvertArray(int input, char *returnchar);
const char *ConvertArrayF(float input, char *returnchar);
const char *MakeLowerString(const char *source);
const std::string StringFormat(const char* format, ...);
const std::string vStringFormat(const char* format, va_list args);
int MakeAnyLenString(char** ret, const char* format, ...);
std::string EscapeString(const char *src, size_t sz);
std::string EscapeString(const std::string &s);
std::vector<std::string> SplitString(const std::string &s, char delim);
uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...);
uint32 hextoi(const char* num);
uint64 hextoi64(const char* num);
void MakeLowerString(const char *source, char *target);
void RemoveApostrophes(std::string &s);
void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string);
#endif

View File

@ -3,7 +3,8 @@
#include "eqemu_logsys.h"
#include "struct_strategy.h"
#include "eq_stream.h"
#include "eq_stream_intf.h"
#include "opcodemgr.h"
#include <map>
#include <memory>
@ -18,7 +19,7 @@ StructStrategy::StructStrategy() {
}
}
void StructStrategy::Encode(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req) const {
void StructStrategy::Encode(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req) const {
if((*p)->GetOpcodeBypass() != 0) {
PassEncoder(p, dest, ack_req);
return;
@ -36,7 +37,7 @@ void StructStrategy::Decode(EQApplicationPacket *p) const {
}
void StructStrategy::ErrorEncoder(EQApplicationPacket **in_p, std::shared_ptr<EQStream> dest, bool ack_req) {
void StructStrategy::ErrorEncoder(EQApplicationPacket **in_p, std::shared_ptr<EQStreamInterface> dest, bool ack_req) {
EQApplicationPacket *p = *in_p;
*in_p = nullptr;
@ -50,7 +51,7 @@ void StructStrategy::ErrorDecoder(EQApplicationPacket *p) {
p->SetOpcode(OP_Unknown);
}
void StructStrategy::PassEncoder(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req) {
void StructStrategy::PassEncoder(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req) {
dest->FastQueuePacket(p, ack_req);
}

View File

@ -2,7 +2,7 @@
#define STRUCTSTRATEGY_H_
class EQApplicationPacket;
class EQStream;
class EQStreamInterface;
#include "emu_opcodes.h"
#include "emu_versions.h"
@ -12,7 +12,7 @@ class EQStream;
class StructStrategy {
public:
//the encoder takes ownership of the supplied packet, and may enqueue multiple resulting packets into the stream
typedef void(*Encoder)(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req);
typedef void(*Encoder)(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req);
//the decoder may only edit the supplied packet, producing a single packet for eqemu to consume.
typedef void (*Decoder)(EQApplicationPacket *p);
@ -20,7 +20,7 @@ public:
virtual ~StructStrategy() {}
//this method takes an eqemu struct, and enqueues the produced structs into the stream.
void Encode(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req) const;
void Encode(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req) const;
//this method takes an EQ wire struct, and converts it into an eqemu struct
void Decode(EQApplicationPacket *p) const;
@ -30,10 +30,10 @@ public:
protected:
//some common coders:
//Print an error saying unknown struct/opcode and drop it
static void ErrorEncoder(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req);
static void ErrorEncoder(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req);
static void ErrorDecoder(EQApplicationPacket *p);
//pass the packet through without modification (emu == EQ) (default)
static void PassEncoder(EQApplicationPacket **p, std::shared_ptr<EQStream> dest, bool ack_req);
static void PassEncoder(EQApplicationPacket **p, std::shared_ptr<EQStreamInterface> dest, bool ack_req);
static void PassDecoder(EQApplicationPacket *p);
Encoder encoders[_maxEmuOpcode];

View File

@ -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_*/

View File

@ -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

View File

@ -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_*/

View File

@ -0,0 +1,31 @@
#pragma once
#include <streambuf>
#include <istream>
#include <ostream>
namespace EQ
{
namespace Util {
class MemoryBuffer : public std::streambuf
{
public:
MemoryBuffer(char* base, size_t size) {
setg(base, base, base + size);
setp(base, base + size);
}
};
class MemoryStreamWriter : virtual MemoryBuffer, public std::ostream
{
public:
MemoryStreamWriter(char* base, size_t size) : MemoryBuffer(base, size), std::ostream(static_cast<std::streambuf*>(this)) { }
};
class MemoryStreamReader : virtual MemoryBuffer, public std::istream
{
public:
MemoryStreamReader(char* base, size_t size) : MemoryBuffer(base, size), std::istream(static_cast<std::streambuf*>(this)) { }
};
}
}

189
common/util/uuid.cpp Normal file
View File

@ -0,0 +1,189 @@
#include "uuid.h"
#include <fmt/format.h>
#ifdef _WIN32
#include <objbase.h>
#endif
#ifdef __linux__
#include <uuid/uuid.h>
#endif
#ifdef __APPLE__
#include <CoreFoundation/CFUUID.h>
#endif
unsigned char hexDigitToChar(char ch)
{
if (ch > 47 && ch < 58)
return ch - 48;
if (ch > 96 && ch < 103)
return ch - 87;
if (ch > 64 && ch < 71)
return ch - 55;
return 0;
}
unsigned char hexPairToChar(char a, char b)
{
return hexDigitToChar(a) * 16 + hexDigitToChar(b);
}
EQ::Util::UUID::UUID()
{
m_bytes = std::vector<char>(16, 0);
}
EQ::Util::UUID::UUID(const unsigned char *bytes)
{
m_bytes.assign(bytes, bytes + 16);
}
EQ::Util::UUID::UUID(const UUID &o)
{
m_bytes = o.m_bytes;
}
EQ::Util::UUID::UUID(UUID &&o)
{
std::swap(m_bytes, o.m_bytes);
}
EQ::Util::UUID& EQ::Util::UUID::operator=(const UUID &o)
{
m_bytes = o.m_bytes;
return *this;
}
EQ::Util::UUID::~UUID()
{
}
EQ::Util::UUID EQ::Util::UUID::Generate()
{
#ifdef _WIN32
GUID id;
CoCreateGuid(&id);
const unsigned char buffer[16] =
{
static_cast<unsigned char>((id.Data1 >> 24) & 0xFF),
static_cast<unsigned char>((id.Data1 >> 16) & 0xFF),
static_cast<unsigned char>((id.Data1 >> 8) & 0xFF),
static_cast<unsigned char>((id.Data1) & 0xff),
static_cast<unsigned char>((id.Data2 >> 8) & 0xFF),
static_cast<unsigned char>((id.Data2) & 0xff),
static_cast<unsigned char>((id.Data3 >> 8) & 0xFF),
static_cast<unsigned char>((id.Data3) & 0xFF),
id.Data4[0],
id.Data4[1],
id.Data4[2],
id.Data4[3],
id.Data4[4],
id.Data4[5],
id.Data4[6],
id.Data4[7]
};
return buffer;
#endif
#ifdef __linux__
uuid_t id;
uuid_generate(id);
return id;
#endif
#ifdef __APPLE__
auto id = CFUUIDCreate(nullptr);
auto bytes = CFUUIDGetUUIDBytes(id);
const unsigned char buffer[16] =
{
bytes.byte0,
bytes.byte1,
bytes.byte2,
bytes.byte3,
bytes.byte4,
bytes.byte5,
bytes.byte6,
bytes.byte7,
bytes.byte8,
bytes.byte9,
bytes.byte10,
bytes.byte11,
bytes.byte12,
bytes.byte13,
bytes.byte14,
bytes.byte15
};
CFRelease(id);
return buffer;
#endif
}
EQ::Util::UUID EQ::Util::UUID::FromString(const std::string &str)
{
UUID ret;
ret.m_bytes.clear();
size_t i = 0;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
ret.m_bytes.push_back(hexPairToChar(str[i], str[i + 1])); ++i;
return ret;
}
EQ::Util::UUID EQ::Util::UUID::FromByteArray(const char *buffer)
{
return UUID((unsigned char*)buffer);
}
std::string EQ::Util::UUID::ToString() const
{
return fmt::format("{:0<2x}{:0<2x}{:0<2x}{:0<2x}-{:0<2x}{:0<2x}-{:0<2x}{:0<2x}-{:0<2x}{:0<2x}-{:0<2x}{:0<2x}{:0<2x}{:0<2x}{:0<2x}{:0<2x}",
(unsigned char)m_bytes[0],
(unsigned char)m_bytes[1],
(unsigned char)m_bytes[2],
(unsigned char)m_bytes[3],
(unsigned char)m_bytes[4],
(unsigned char)m_bytes[5],
(unsigned char)m_bytes[6],
(unsigned char)m_bytes[7],
(unsigned char)m_bytes[8],
(unsigned char)m_bytes[9],
(unsigned char)m_bytes[10],
(unsigned char)m_bytes[11],
(unsigned char)m_bytes[12],
(unsigned char)m_bytes[13],
(unsigned char)m_bytes[14],
(unsigned char)m_bytes[15]);
}
const std::vector<char>& EQ::Util::UUID::ToByteArray() const
{
return m_bytes;
}

35
common/util/uuid.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include <string>
#include <vector>
#include <ios>
namespace EQ
{
namespace Util
{
class UUID
{
public:
UUID(const UUID &o);
UUID(UUID &&o);
UUID& operator=(const UUID &o);
~UUID();
static UUID Generate();
static UUID FromString(const std::string &str);
static UUID FromByteArray(const char *buffer);
std::string ToString() const;
const std::vector<char>& ToByteArray() const;
friend std::ostream &operator<<(std::ostream &os, const UUID &id) {
return os << id.ToString();
}
private:
UUID();
UUID(const unsigned char *bytes);
std::vector<char> m_bytes;
};
}
}

View File

@ -30,7 +30,7 @@
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9108
#define CURRENT_BINARY_DATABASE_VERSION 9110
#ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9017
#else

View File

@ -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

View File

@ -13,28 +13,8 @@ SET(eqlaunch_headers
ADD_EXECUTABLE(eqlaunch ${eqlaunch_sources} ${eqlaunch_headers})
INSTALL(TARGETS eqlaunch RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX})
INSTALL(TARGETS eqlaunch RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
TARGET_LINK_LIBRARIES(eqlaunch common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} ${ZLIB_LIBRARY})
IF(MSVC)
SET_TARGET_PROPERTIES(eqlaunch PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF")
TARGET_LINK_LIBRARIES(eqlaunch "Ws2_32.lib")
ENDIF(MSVC)
IF(MINGW)
TARGET_LINK_LIBRARIES(eqlaunch "WS2_32")
ENDIF(MINGW)
IF(UNIX)
TARGET_LINK_LIBRARIES(eqlaunch "${CMAKE_DL_LIBS}")
TARGET_LINK_LIBRARIES(eqlaunch "z")
TARGET_LINK_LIBRARIES(eqlaunch "m")
IF(NOT DARWIN)
TARGET_LINK_LIBRARIES(eqlaunch "rt")
ENDIF(NOT DARWIN)
TARGET_LINK_LIBRARIES(eqlaunch "pthread")
ADD_DEFINITIONS(-fPIC)
ENDIF(UNIX)
TARGET_LINK_LIBRARIES(eqlaunch ${SERVER_LIBS})
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

View File

@ -23,6 +23,7 @@
#include "../common/servertalk.h"
#include "../common/platform.h"
#include "../common/crash.h"
#include "../common/unix.h"
#include "worldserver.h"
#include "zone_launch.h"
#include <vector>
@ -91,9 +92,6 @@ int main(int argc, char *argv[]) {
std::map<std::string, ZoneLaunch *> zones;
WorldServer world(zones, launcher_name.c_str(), Config);
if (!world.Connect()) {
Log(Logs::Detail, Logs::Launcher, "worldserver.Connect() FAILED! Will retry.");
}
std::map<std::string, ZoneLaunch *>::iterator zone, zend;
std::set<std::string> to_remove;
@ -108,11 +106,6 @@ int main(int argc, char *argv[]) {
//Advance the timer to our current point in time
Timer::SetCurrentTime();
/*
* Process the world connection
*/
world.Process();
/*
* Let the process manager look for dead children
*/
@ -143,19 +136,8 @@ int main(int argc, char *argv[]) {
zones.erase(rem);
}
if (InterserverTimer.Check()) {
if (world.TryReconnect() && (!world.Connected()))
world.AsyncConnect();
}
/*
* Take a nice nap until next cycle
*/
if(zones.empty())
Sleep(5000);
else
Sleep(2000);
EQ::EventLoop::Get().Process();
Sleep(5);
}
//try to be semi-nice about this... without waiting too long

View File

@ -1,19 +1,19 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net)
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 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.
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
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"
@ -25,112 +25,104 @@
#include "zone_launch.h"
WorldServer::WorldServer(std::map<std::string, ZoneLaunch *> &zones, const char *name, const EQEmuConfig *config)
: WorldConnection(EmuTCPConnection::packetModeLauncher, config->SharedKey.c_str()),
m_name(name),
: m_name(name),
m_config(config),
m_zones(zones)
{
m_connection.reset(new EQ::Net::ServertalkClient(config->WorldIP, config->WorldTCPPort, false, "Launcher", config->SharedKey));
m_connection->OnConnect([this](EQ::Net::ServertalkClient *client) {
OnConnected();
});
m_connection->OnMessage(std::bind(&WorldServer::HandleMessage, this, std::placeholders::_1, std::placeholders::_2));
}
WorldServer::~WorldServer() {
}
void WorldServer::OnConnected() {
WorldConnection::OnConnected();
auto pack = new ServerPacket(ServerOP_LauncherConnectInfo, sizeof(LauncherConnectInfo));
LauncherConnectInfo* sci = (LauncherConnectInfo*) pack->pBuffer;
LauncherConnectInfo* sci = (LauncherConnectInfo*)pack->pBuffer;
strn0cpy(sci->name, m_name, sizeof(sci->name));
// sci->port = net.GetZonePort();
// strcpy(sci->address, net.GetZoneAddress());
SendPacket(pack);
m_connection->SendPacket(pack);
safe_delete(pack);
//send status for all zones...
std::map<std::string, ZoneLaunch *>::iterator cur, end;
cur = m_zones.begin();
end = m_zones.end();
for(; cur != end; ++cur) {
for (; cur != end; ++cur) {
cur->second->SendStatus();
}
}
void WorldServer::Process() {
void WorldServer::HandleMessage(uint16 opcode, EQ::Net::Packet &p) {
ServerPacket tpack(opcode, p);
ServerPacket *pack = &tpack;
WorldConnection::Process();
if (!Connected())
return;
ServerPacket *pack = 0;
while((pack = tcpc.PopPacket())) {
switch(pack->opcode) {
case 0: {
switch (opcode) {
case 0: {
break;
}
case ServerOP_EmoteMessage:
case ServerOP_KeepAlive: {
// ignore this
break;
}
case ServerOP_LauncherZoneRequest: {
if (pack->size != sizeof(LauncherZoneRequest)) {
Log(Logs::Detail, Logs::Launcher, "Invalid size of LauncherZoneRequest: %d", pack->size);
break;
}
case ServerOP_EmoteMessage:
case ServerOP_KeepAlive: {
// ignore this
break;
}
case ServerOP_ZAAuthFailed: {
Log(Logs::Detail, Logs::Launcher, "World server responded 'Not Authorized', disabling reconnect");
pTryReconnect = false;
Disconnect();
break;
}
case ServerOP_LauncherZoneRequest: {
if(pack->size != sizeof(LauncherZoneRequest)) {
Log(Logs::Detail, Logs::Launcher, "Invalid size of LauncherZoneRequest: %d", pack->size);
break;
}
const LauncherZoneRequest *lzr = (const LauncherZoneRequest *) pack->pBuffer;
const LauncherZoneRequest *lzr = (const LauncherZoneRequest *)pack->pBuffer;
switch(ZoneRequestCommands(lzr->command)) {
case ZR_Start: {
if(m_zones.find(lzr->short_name) != m_zones.end()) {
Log(Logs::Detail, Logs::Launcher, "World told us to start zone %s, but it is already running.", lzr->short_name);
} else {
Log(Logs::Detail, Logs::Launcher, "World told us to start zone %s.", lzr->short_name);
auto l = new ZoneLaunch(this, m_name, lzr->short_name, lzr->port, m_config);
m_zones[lzr->short_name] = l;
}
break;
}
case ZR_Restart: {
auto res = m_zones.find(lzr->short_name);
if(res == m_zones.end()) {
Log(Logs::Detail, Logs::Launcher, "World told us to restart zone %s, but it is not running.", lzr->short_name);
} else {
Log(Logs::Detail, Logs::Launcher, "World told us to restart zone %s.", lzr->short_name);
res->second->Restart();
}
break;
}
case ZR_Stop: {
auto res = m_zones.find(lzr->short_name);
if(res == m_zones.end()) {
Log(Logs::Detail, Logs::Launcher, "World told us to stop zone %s, but it is not running.", lzr->short_name);
} else {
Log(Logs::Detail, Logs::Launcher, "World told us to stop zone %s.", lzr->short_name);
res->second->Stop();
}
break;
switch (ZoneRequestCommands(lzr->command)) {
case ZR_Start: {
if (m_zones.find(lzr->short_name) != m_zones.end()) {
Log(Logs::Detail, Logs::Launcher, "World told us to start zone %s, but it is already running.", lzr->short_name);
}
else {
Log(Logs::Detail, Logs::Launcher, "World told us to start zone %s.", lzr->short_name);
auto l = new ZoneLaunch(this, m_name, lzr->short_name, lzr->port, m_config);
m_zones[lzr->short_name] = l;
}
break;
}
case ServerOP_GroupIDReply: {
//ignore this, world is still being dumb
case ZR_Restart: {
auto res = m_zones.find(lzr->short_name);
if (res == m_zones.end()) {
Log(Logs::Detail, Logs::Launcher, "World told us to restart zone %s, but it is not running.", lzr->short_name);
}
else {
Log(Logs::Detail, Logs::Launcher, "World told us to restart zone %s.", lzr->short_name);
res->second->Restart();
}
break;
}
case ZR_Stop: {
auto res = m_zones.find(lzr->short_name);
if (res == m_zones.end()) {
Log(Logs::Detail, Logs::Launcher, "World told us to stop zone %s, but it is not running.", lzr->short_name);
}
else {
Log(Logs::Detail, Logs::Launcher, "World told us to stop zone %s.", lzr->short_name);
res->second->Stop();
}
break;
}
}
break;
}
case ServerOP_GroupIDReply: {
//ignore this, world is still being dumb
break;
}
default: {
Log(Logs::Detail, Logs::Launcher, "Unknown opcode 0x%x from World of len %d", pack->opcode, pack->size);
break;
}
}
safe_delete(pack);
default: {
Log(Logs::Detail, Logs::Launcher, "Unknown opcode 0x%x from World of len %d", pack->opcode, pack->size);
break;
}
}
}
@ -138,18 +130,12 @@ void WorldServer::Process() {
void WorldServer::SendStatus(const char *short_name, uint32 start_count, bool running) {
auto pack = new ServerPacket(ServerOP_LauncherZoneStatus, sizeof(LauncherZoneStatus));
LauncherZoneStatus* it =(LauncherZoneStatus*) pack->pBuffer;
LauncherZoneStatus* it = (LauncherZoneStatus*)pack->pBuffer;
strn0cpy(it->short_name, short_name, 32);
it->start_count = start_count;
it->running = running?1:0;
it->running = running ? 1 : 0;
SendPacket(pack);
m_connection->SendPacket(pack);
safe_delete(pack);
}
}

View File

@ -18,7 +18,8 @@
#ifndef WORLDSERVER_H
#define WORLDSERVER_H
#include "../common/worldconn.h"
#include "../common/net/servertalk_client_connection.h"
#include <memory>
#include <string>
#include <queue>
#include <map>
@ -26,18 +27,19 @@
class ZoneLaunch;
class EQEmuConfig;
class WorldServer : public WorldConnection {
class WorldServer {
public:
WorldServer(std::map<std::string, ZoneLaunch *> &zones, const char *name, const EQEmuConfig *config);
virtual ~WorldServer();
~WorldServer();
virtual void Process();
void HandleMessage(uint16 opcode, EQ::Net::Packet &p);
void SendStatus(const char *short_name, uint32 start_count, bool running);
private:
virtual void OnConnected();
std::unique_ptr<EQ::Net::ServertalkClient> m_connection;
const char *const m_name;
const EQEmuConfig *const m_config;
std::map<std::string, ZoneLaunch *> &m_zones;

26
hc/CMakeLists.txt Normal file
View File

@ -0,0 +1,26 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(hc_sources
eq.cpp
main.cpp
login.cpp
world.cpp
)
SET(hc_headers
eq.h
login.h
world.h
)
FIND_PACKAGE(OpenSSL REQUIRED)
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
ADD_EXECUTABLE(hc ${hc_sources} ${hc_headers})
INSTALL(TARGETS hc RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
TARGET_LINK_LIBRARIES(hc ${SERVER_LIBS} ${OPENSSL_LIBRARIES})
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

350
hc/eq.cpp Normal file
View File

@ -0,0 +1,350 @@
#include "eq.h"
#include "../common/net/dns.h"
const char* eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char* buffer_out, bool enc) {
DES_key_schedule k;
DES_cblock v;
memset(&k, 0, sizeof(DES_key_schedule));
memset(&v, 0, sizeof(DES_cblock));
if (!enc && buffer_in_sz && buffer_in_sz % 8 != 0) {
return nullptr;
}
DES_ncbc_encrypt((const unsigned char*)buffer_in, (unsigned char*)buffer_out, (long)buffer_in_sz, &k, &v, enc);
return buffer_out;
}
EverQuest::EverQuest(const std::string &host, int port, const std::string &user, const std::string &pass, const std::string &server, const std::string &character)
{
m_host = host;
m_port = port;
m_user = user;
m_pass = pass;
m_server = server;
m_character = character;
m_dbid = 0;
EQ::Net::DNSLookup(m_host, port, false, [&](const std::string &addr) {
if (addr.empty()) {
Log.OutF(Logs::General, Logs::Headless_Client, "Could not resolve address: {0}", m_host);
return;
}
else {
m_host = addr;
m_login_connection_manager.reset(new EQ::Net::DaybreakConnectionManager());
m_login_connection_manager->OnNewConnection(std::bind(&EverQuest::LoginOnNewConnection, this, std::placeholders::_1));
m_login_connection_manager->OnConnectionStateChange(std::bind(&EverQuest::LoginOnStatusChangeReconnectEnabled, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_login_connection_manager->OnPacketRecv(std::bind(&EverQuest::LoginOnPacketRecv, this, std::placeholders::_1, std::placeholders::_2));
m_login_connection_manager->Connect(m_host, m_port);
}
});
}
EverQuest::~EverQuest()
{
}
void EverQuest::LoginOnNewConnection(std::shared_ptr<EQ::Net::DaybreakConnection> connection)
{
m_login_connection = connection;
Log.OutF(Logs::General, Logs::Headless_Client, "Connecting...");
}
void EverQuest::LoginOnStatusChangeReconnectEnabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to)
{
if (to == EQ::Net::StatusConnected) {
Log.OutF(Logs::General, Logs::Headless_Client, "Login connected.");
LoginSendSessionReady();
}
if (to == EQ::Net::StatusDisconnected) {
Log.OutF(Logs::General, Logs::Headless_Client, "Login connection lost before we got to world, reconnecting.");
m_key.clear();
m_dbid = 0;
m_login_connection.reset();
m_login_connection_manager->Connect(m_host, m_port);
}
}
void EverQuest::LoginOnStatusChangeReconnectDisabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to)
{
if (to == EQ::Net::StatusDisconnected) {
m_login_connection.reset();
}
}
void EverQuest::LoginOnPacketRecv(std::shared_ptr<EQ::Net::DaybreakConnection> conn, const EQ::Net::Packet & p)
{
auto opcode = p.GetUInt16(0);
switch (opcode) {
case 0x0017: //OP_ChatMessage
LoginSendLogin();
break;
case 0x0018:
LoginProcessLoginResponse(p);
break;
case 0x0019:
LoginProcessServerPacketList(p);
break;
case 0x0022:
LoginProcessServerPlayResponse(p);
break;
}
}
void EverQuest::LoginSendSessionReady()
{
EQ::Net::DynamicPacket p;
p.PutUInt16(0, 1); //OP_SessionReady
p.PutUInt32(2, 2);
m_login_connection->QueuePacket(p);
}
void EverQuest::LoginSendLogin()
{
size_t buffer_len = m_user.length() + m_pass.length() + 2;
std::unique_ptr<char[]> buffer(new char[buffer_len]);
strcpy(&buffer[0], m_user.c_str());
strcpy(&buffer[m_user.length() + 1], m_pass.c_str());
size_t encrypted_len = buffer_len;
if (encrypted_len % 8 > 0) {
encrypted_len = ((encrypted_len / 8) + 1) * 8;
}
EQ::Net::DynamicPacket p;
p.Resize(12 + encrypted_len);
p.PutUInt16(0, 2); //OP_Login
p.PutUInt32(2, 3);
eqcrypt_block(&buffer[0], buffer_len, (char*)p.Data() + 12, true);
m_login_connection->QueuePacket(p);
}
void EverQuest::LoginSendServerRequest()
{
EQ::Net::DynamicPacket p;
p.PutUInt16(0, 4); //OP_ServerListRequest
p.PutUInt32(2, 4);
m_login_connection->QueuePacket(p);
}
void EverQuest::LoginSendPlayRequest(uint32_t id)
{
EQ::Net::DynamicPacket p;
p.PutUInt16(0, 0x000d);
p.PutUInt16(2, 5);
p.PutUInt32(4, 0);
p.PutUInt32(8, 0);
p.PutUInt32(12, id);
m_login_connection->QueuePacket(p);
}
void EverQuest::LoginProcessLoginResponse(const EQ::Net::Packet & p)
{
auto encrypt_size = p.Length() - 12;
if (encrypt_size % 8 > 0) {
encrypt_size = (encrypt_size / 8) * 8;
}
std::unique_ptr<char[]> decrypted(new char[encrypt_size]);
eqcrypt_block((char*)p.Data() + 12, encrypt_size, &decrypted[0], false);
EQ::Net::StaticPacket sp(&decrypted[0], encrypt_size);
auto response_error = sp.GetUInt16(1);
if (response_error > 101) {
Log.OutF(Logs::General, Logs::Headless_Client, "Error logging in response code: {0}", response_error);
LoginDisableReconnect();
}
else {
m_key = sp.GetCString(12);
m_dbid = sp.GetUInt32(8);
Log.OutF(Logs::General, Logs::Headless_Client, "Logged in successfully with dbid {0} and key {1}", m_dbid, m_key);
LoginSendServerRequest();
}
}
void EverQuest::LoginProcessServerPacketList(const EQ::Net::Packet & p)
{
m_world_servers.clear();
auto number_of_servers = p.GetUInt32(18);
size_t idx = 22;
for (auto i = 0U; i < number_of_servers; ++i) {
WorldServer ws;
ws.address = p.GetCString(idx);
idx += (ws.address.length() + 1);
ws.type = p.GetInt32(idx);
idx += 4;
auto id = p.GetUInt32(idx);
idx += 4;
ws.long_name = p.GetCString(idx);
idx += (ws.long_name.length() + 1);
ws.lang = p.GetCString(idx);
idx += (ws.lang.length() + 1);
ws.region = p.GetCString(idx);
idx += (ws.region.length() + 1);
ws.status = p.GetInt32(idx);
idx += 4;
ws.players = p.GetInt32(idx);
idx += 4;
m_world_servers[id] = ws;
}
for (auto server : m_world_servers) {
if (server.second.long_name.compare(m_server) == 0) {
Log.OutF(Logs::General, Logs::Headless_Client, "Found world server {0}, attempting to login.", m_server);
LoginSendPlayRequest(server.first);
return;
}
}
Log.OutF(Logs::General, Logs::Headless_Client, "Got response from login server but could not find world server {0} disconnecting.", m_server);
LoginDisableReconnect();
}
void EverQuest::LoginProcessServerPlayResponse(const EQ::Net::Packet &p)
{
auto allowed = p.GetUInt8(12);
if (allowed) {
auto server = p.GetUInt32(18);
auto ws = m_world_servers.find(server);
if (ws != m_world_servers.end()) {
ConnectToWorld();
LoginDisableReconnect();
}
}
else {
auto message = p.GetUInt16(13);
Log.OutF(Logs::General, Logs::Headless_Client, "Failed to login to server with message {0}");
LoginDisableReconnect();
}
}
void EverQuest::LoginDisableReconnect()
{
m_login_connection_manager->OnConnectionStateChange(std::bind(&EverQuest::LoginOnStatusChangeReconnectDisabled, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_login_connection->Close();
}
void EverQuest::ConnectToWorld()
{
m_world_connection_manager.reset(new EQ::Net::DaybreakConnectionManager());
m_world_connection_manager->OnNewConnection(std::bind(&EverQuest::WorldOnNewConnection, this, std::placeholders::_1));
m_world_connection_manager->OnConnectionStateChange(std::bind(&EverQuest::WorldOnStatusChangeReconnectEnabled, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_world_connection_manager->OnPacketRecv(std::bind(&EverQuest::WorldOnPacketRecv, this, std::placeholders::_1, std::placeholders::_2));
m_world_connection_manager->Connect(m_host, 9000);
}
void EverQuest::WorldOnNewConnection(std::shared_ptr<EQ::Net::DaybreakConnection> connection)
{
m_world_connection = connection;
Log.OutF(Logs::General, Logs::Headless_Client, "Connecting to world...");
}
void EverQuest::WorldOnStatusChangeReconnectEnabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to)
{
if (to == EQ::Net::StatusConnected) {
Log.OutF(Logs::General, Logs::Headless_Client, "World connected.");
WorldSendClientAuth();
}
if (to == EQ::Net::StatusDisconnected) {
Log.OutF(Logs::General, Logs::Headless_Client, "World connection lost, reconnecting.");
m_world_connection.reset();
m_world_connection_manager->Connect(m_host, 9000);
}
}
void EverQuest::WorldOnStatusChangeReconnectDisabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to)
{
if (to == EQ::Net::StatusDisconnected) {
m_world_connection.reset();
}
}
void EverQuest::WorldOnPacketRecv(std::shared_ptr<EQ::Net::DaybreakConnection> conn, const EQ::Net::Packet & p)
{
auto opcode = p.GetUInt16(0);
switch (opcode) {
case 0x00d2:
WorldProcessCharacterSelect(p);
break;
default:
Log.OutF(Logs::General, Logs::Headless_Client, "Unhandled opcode: {0:#x}", opcode);
break;
}
}
void EverQuest::WorldSendClientAuth()
{
EQ::Net::DynamicPacket p;
p.Resize(2 + 464);
p.PutUInt16(0, 0x7a09U);
std::string dbid_str = std::to_string(m_dbid);
p.PutCString(2, dbid_str.c_str());
p.PutCString(2 + dbid_str.length() + 1, m_key.c_str());
m_world_connection->QueuePacket(p);
}
void EverQuest::WorldSendEnterWorld(const std::string &character)
{
EQ::Net::DynamicPacket p;
p.PutUInt16(0, 0x578f);
p.PutString(2, character);
p.PutUInt32(66, 0);
p.PutUInt32(70, 0);
m_world_connection->QueuePacket(p);
}
void EverQuest::WorldProcessCharacterSelect(const EQ::Net::Packet &p)
{
auto char_count = p.GetUInt32(2);
size_t idx = 6;
//Log.OutF(Logs::General, Logs::Headless_Client, "{0} characters", char_count);
for (uint32_t i = 0; i < char_count; ++i) {
auto name = p.GetCString(idx);
idx += name.length() + 1;
auto pclass = p.GetUInt8(idx);
auto prace = p.GetUInt32(idx + 1);
auto plevel = p.GetUInt8(idx + 5);
idx += 274;
if (m_character.compare(name) == 0) {
Log.OutF(Logs::General, Logs::Headless_Client, "Found {0}, would attempt to login here.", m_character);
WorldSendEnterWorld(m_character);
return;
}
}
Log.OutF(Logs::General, Logs::Headless_Client, "Could not find {0}, cannot continue to login.", m_character);
}

74
hc/eq.h Normal file
View File

@ -0,0 +1,74 @@
#pragma once
#include "../common/eqemu_logsys.h"
#include "../common/net/daybreak_connection.h"
#include "../common/event/timer.h"
#include <openssl/des.h>
#include <string>
#include <map>
struct WorldServer
{
std::string long_name;
std::string address;
int type;
std::string lang;
std::string region;
int status;
int players;
};
class EverQuest
{
public:
EverQuest(const std::string &host, int port, const std::string &user, const std::string &pass, const std::string &server, const std::string &character);
~EverQuest();
private:
//Login
void LoginOnNewConnection(std::shared_ptr<EQ::Net::DaybreakConnection> connection);
void LoginOnStatusChangeReconnectEnabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to);
void LoginOnStatusChangeReconnectDisabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to);
void LoginOnPacketRecv(std::shared_ptr<EQ::Net::DaybreakConnection> conn, const EQ::Net::Packet &p);
void LoginSendSessionReady();
void LoginSendLogin();
void LoginSendServerRequest();
void LoginSendPlayRequest(uint32_t id);
void LoginProcessLoginResponse(const EQ::Net::Packet &p);
void LoginProcessServerPacketList(const EQ::Net::Packet &p);
void LoginProcessServerPlayResponse(const EQ::Net::Packet &p);
void LoginDisableReconnect();
std::unique_ptr<EQ::Net::DaybreakConnectionManager> m_login_connection_manager;
std::shared_ptr<EQ::Net::DaybreakConnection> m_login_connection;
std::map<uint32_t, WorldServer> m_world_servers;
//World
void ConnectToWorld();
void WorldOnNewConnection(std::shared_ptr<EQ::Net::DaybreakConnection> connection);
void WorldOnStatusChangeReconnectEnabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to);
void WorldOnStatusChangeReconnectDisabled(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to);
void WorldOnPacketRecv(std::shared_ptr<EQ::Net::DaybreakConnection> conn, const EQ::Net::Packet &p);
void WorldSendClientAuth();
void WorldSendEnterWorld(const std::string &character);
void WorldProcessCharacterSelect(const EQ::Net::Packet &p);
std::unique_ptr<EQ::Net::DaybreakConnectionManager> m_world_connection_manager;
std::shared_ptr<EQ::Net::DaybreakConnection> m_world_connection;
//Variables
std::string m_host;
int m_port;
std::string m_user;
std::string m_pass;
std::string m_server;
std::string m_character;
std::string m_key;
uint32_t m_dbid;
};

255
hc/login.cpp Normal file
View File

@ -0,0 +1,255 @@
/*#include "login.h"
#include "../common/eqemu_logsys.h"
#include <openssl/des.h>
const char* eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char* buffer_out, bool enc) {
DES_key_schedule k;
DES_cblock v;
memset(&k, 0, sizeof(DES_key_schedule));
memset(&v, 0, sizeof(DES_cblock));
if (!enc && buffer_in_sz && buffer_in_sz % 8 != 0) {
return nullptr;
}
DES_ncbc_encrypt((const unsigned char*)buffer_in, (unsigned char*)buffer_out, (long)buffer_in_sz, &k, &v, enc);
return buffer_out;
}
LoginConnection::LoginConnection(const std::string &username, const std::string &password, const std::string &host, int host_port, const std::string &server)
{
m_connecting = false;
m_username = username;
m_password = password;
m_host = host;
m_host_port = host_port;
m_server = server;
m_connection_manager.reset(new EQ::Net::DaybreakConnectionManager());
m_connection_manager->OnNewConnection(std::bind(&LoginConnection::OnNewConnection, this, std::placeholders::_1));
m_connection_manager->OnConnectionStateChange(std::bind(&LoginConnection::OnStatusChangeActive, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_connection_manager->OnPacketRecv(std::bind(&LoginConnection::OnPacketRecv, this, std::placeholders::_1, std::placeholders::_2));
m_connection_manager->Connect(host, host_port);
}
LoginConnection::~LoginConnection()
{
}
void LoginConnection::OnNewConnection(std::shared_ptr<EQ::Net::DaybreakConnection> connection)
{
m_connection = connection;
Log.OutF(Logs::General, Logs::Headless_Client, "Connecting...");
}
void LoginConnection::OnStatusChangeActive(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to)
{
if (to == EQ::Net::StatusConnected) {
Log.OutF(Logs::General, Logs::Headless_Client, "Login connected.");
SendSessionReady();
}
if (to == EQ::Net::StatusDisconnected) {
Log.OutF(Logs::General, Logs::Headless_Client, "Login connection lost, reconnecting.");
m_key.clear();
m_dbid = 0;
m_connection.reset();
m_connection_manager->Connect(m_host, m_host_port);
}
}
void LoginConnection::OnStatusChangeInactive(std::shared_ptr<EQ::Net::DaybreakConnection> conn, EQ::Net::DbProtocolStatus from, EQ::Net::DbProtocolStatus to)
{
if (to == EQ::Net::StatusDisconnected) {
m_key.clear();
m_dbid = 0;
m_connection.reset();
}
}
void LoginConnection::OnPacketRecv(std::shared_ptr<EQ::Net::DaybreakConnection> conn, const EQ::Net::Packet &p)
{
auto opcode = p.GetUInt16(0);
switch (opcode) {
case 0x0017: //OP_ChatMessage
SendLogin();
break;
case 0x0018:
ProcessLoginResponse(p);
break;
case 0x0019:
ProcessServerPacketList(p);
break;
case 0x0022:
ProcessServerPlayResponse(p);
break;
}
}
void LoginConnection::Kill()
{
m_connection_manager->OnConnectionStateChange(std::bind(&LoginConnection::OnStatusChangeInactive, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_key.clear();
m_dbid = 0;
m_connection->Close();
}
void LoginConnection::Start()
{
m_connection_manager->OnConnectionStateChange(std::bind(&LoginConnection::OnStatusChangeActive, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_connection_manager->Connect(m_host, m_host_port);
}
void LoginConnection::SendSessionReady()
{
EQ::Net::DynamicPacket p;
p.PutUInt16(0, 1); //OP_SessionReady
p.PutUInt32(2, 2);
m_connection->QueuePacket(p);
}
void LoginConnection::SendLogin()
{
size_t buffer_len = m_username.length() + m_password.length() + 2;
std::unique_ptr<char[]> buffer(new char[buffer_len]);
strcpy(&buffer[0], m_username.c_str());
strcpy(&buffer[m_username.length() + 1], m_password.c_str());
size_t encrypted_len = buffer_len;
if (encrypted_len % 8 > 0) {
encrypted_len = ((encrypted_len / 8) + 1) * 8;
}
EQ::Net::DynamicPacket p;
p.Resize(12 + encrypted_len);
p.PutUInt16(0, 2); //OP_Login
p.PutUInt32(2, 3);
eqcrypt_block(&buffer[0], buffer_len, (char*)p.Data() + 12, true);
m_connection->QueuePacket(p);
}
void LoginConnection::SendServerRequest()
{
EQ::Net::DynamicPacket p;
p.PutUInt16(0, 4); //OP_ServerListRequest
p.PutUInt32(2, 4);
m_connection->QueuePacket(p);
}
void LoginConnection::SendPlayRequest(uint32_t id)
{
EQ::Net::DynamicPacket p;
p.PutUInt16(0, 0x000d);
p.PutUInt16(2, 5);
p.PutUInt32(4, 0);
p.PutUInt32(8, 0);
p.PutUInt32(12, id);
m_connection->QueuePacket(p);
}
void LoginConnection::ProcessLoginResponse(const EQ::Net::Packet &p)
{
auto encrypt_size = p.Length() - 12;
if (encrypt_size % 8 > 0) {
encrypt_size = (encrypt_size / 8) * 8;
}
std::unique_ptr<char[]> decrypted(new char[encrypt_size]);
eqcrypt_block((char*)p.Data() + 12, encrypt_size, &decrypted[0], false);
EQ::Net::StaticPacket sp(&decrypted[0], encrypt_size);
auto response_error = sp.GetUInt16(1);
if (response_error > 101) {
Log.OutF(Logs::General, Logs::Headless_Client, "Error logging in response code: {0}", response_error);
Kill();
}
else {
m_key = sp.GetCString(12);
m_dbid = sp.GetUInt32(8);
Log.OutF(Logs::General, Logs::Headless_Client, "Logged in successfully with dbid {0} and key {1}", m_dbid, m_key);
SendServerRequest();
}
}
void LoginConnection::ProcessServerPacketList(const EQ::Net::Packet &p)
{
m_world_servers.clear();
auto number_of_servers = p.GetUInt32(18);
size_t idx = 22;
for (auto i = 0U; i < number_of_servers; ++i) {
WorldServer ws;
ws.address = p.GetCString(idx);
idx += (ws.address.length() + 1);
ws.type = p.GetInt32(idx);
idx += 4;
auto id = p.GetUInt32(idx);
idx += 4;
ws.long_name = p.GetCString(idx);
idx += (ws.long_name.length() + 1);
ws.lang = p.GetCString(idx);
idx += (ws.lang.length() + 1);
ws.region = p.GetCString(idx);
idx += (ws.region.length() + 1);
ws.status = p.GetInt32(idx);
idx += 4;
ws.players = p.GetInt32(idx);
idx += 4;
m_world_servers[id] = ws;
}
for (auto server : m_world_servers) {
if (server.second.long_name.compare(m_server) == 0) {
Log.OutF(Logs::General, Logs::Headless_Client, "Found world server {0}, attempting to login.", m_server);
SendPlayRequest(server.first);
return;
}
}
Log.OutF(Logs::General, Logs::Headless_Client, "Got response from login server but could not find world server {0} disconnecting.", m_server);
Kill();
}
void LoginConnection::ProcessServerPlayResponse(const EQ::Net::Packet &p)
{
auto allowed = p.GetUInt8(12);
if (allowed) {
auto server = p.GetUInt32(18);
auto ws = m_world_servers.find(server);
if (ws != m_world_servers.end()) {
if (m_on_can_login_world) {
m_on_can_login_world(ws->second, m_key, m_dbid);
}
Kill();
}
}
else {
auto message = p.GetUInt16(13);
Log.OutF(Logs::General, Logs::Headless_Client, "Failed to login to server with message {0}");
Kill();
}
}
*/

Some files were not shown because too many files have changed in this diff Show More