fix GC NTE proxy behavior

This commit is contained in:
Martin Michelsen
2023-12-03 16:23:56 -08:00
parent b8d4ab589e
commit fbda7a2a48
8 changed files with 50 additions and 45 deletions
+1
View File
@@ -1072,6 +1072,7 @@ static HandlerResult C_GXB_61(shared_ptr<ProxyServer::LinkedSession> ses, uint16
pd = reinterpret_cast<C_CharacterData_V3_61_98*>(&ep3_pd);
} else {
if (is_ep3(ses->version())) {
ses->log.info("Version changed to GC_EP3_TRIAL_EDITION");
ses->set_version(Version::GC_EP3_TRIAL_EDITION);
}
pd = &check_size_t<C_CharacterData_V3_61_98>(data, 0xFFFF);
+35 -38
View File
@@ -239,8 +239,7 @@ ProxyServer::UnlinkedSession::UnlinkedSession(
string_printf("UnlinkedSession:%p", bev),
TerminalFormat::FG_YELLOW,
TerminalFormat::FG_GREEN),
local_port(local_port),
version(version) {
local_port(local_port) {
memset(&this->next_destination, 0, sizeof(this->next_destination));
}
@@ -264,40 +263,46 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
bool should_close_unlinked_session = false;
try {
switch (ses->version) {
case Version::DC_NTE: {
// We should only get an 8B while the session is unlinked
if (command != 0x8B) {
throw runtime_error("command is not 8B");
}
const auto& cmd = check_size_t<C_Login_DCNTE_8B>(data, sizeof(C_LoginExtended_DCNTE_8B));
ses->license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
ses->sub_version = cmd.sub_version;
ses->channel.language = cmd.language;
ses->character_name = cmd.name.decode(ses->channel.language);
// TODO: Parse cmd.hardware_id
ses->version = Version::DC_NTE;
break;
}
switch (ses->version()) {
case Version::DC_NTE:
case Version::DC_V1_11_2000_PROTOTYPE:
case Version::DC_V1:
case Version::DC_V2:
// We should only get a 93 or 9D while the session is unlinked
if (command == 0x93) {
case Version::GC_NTE:
// We should only get an 8B, 93 or 9D while the session is unlinked
if (command == 0x8B) {
ses->channel.version = Version::DC_NTE;
ses->log.info("Version changed to DC_NTE");
const auto& cmd = check_size_t<C_Login_DCNTE_8B>(data, sizeof(C_LoginExtended_DCNTE_8B));
ses->license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
ses->sub_version = cmd.sub_version;
ses->channel.language = cmd.language;
ses->character_name = cmd.name.decode(ses->channel.language);
// TODO: Parse cmd.hardware_id
} else if (command == 0x93) { // 11/2000 proto through DC V1
ses->channel.version = Version::DC_V1;
ses->log.info("Version changed to DC_V1");
const auto& cmd = check_size_t<C_LoginV1_DC_93>(data);
ses->license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
ses->sub_version = cmd.sub_version;
ses->channel.language = cmd.language;
ses->character_name = cmd.name.decode(ses->channel.language);
ses->hardware_id = cmd.hardware_id.decode();
ses->version = Version::DC_V1;
} else if (command == 0x9D) {
const auto& cmd = check_size_t<C_Login_DC_PC_GC_9D>(data, sizeof(C_LoginExtended_DC_GC_9D));
ses->license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
if (cmd.sub_version >= 0x30) {
ses->log.info("Version changed to GC_NTE");
ses->channel.version = Version::GC_NTE;
ses->license = s->license_index->verify_gc(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
} else { // DC V2
ses->log.info("Version changed to DC_V2");
ses->channel.version = Version::DC_V2;
ses->license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
}
ses->sub_version = cmd.sub_version;
ses->channel.language = cmd.language;
ses->character_name = cmd.name.decode(ses->channel.language);
ses->config.set_flags_for_version(ses->version(), cmd.sub_version);
} else {
throw runtime_error("command is not 93 or 9D");
}
@@ -316,20 +321,11 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
break;
}
case Version::GC_NTE:
case Version::GC_V3:
case Version::GC_EP3_TRIAL_EDITION:
case Version::GC_EP3:
// We should only get a 9D or 9E while the session is unlinked
if (command == 0x9D) {
const auto& cmd = check_size_t<C_Login_DC_PC_GC_9D>(data, sizeof(C_LoginExtended_DC_GC_9D));
ses->license = s->license_index->verify_gc(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
ses->sub_version = cmd.sub_version;
ses->channel.language = cmd.language;
ses->character_name = cmd.name.decode(ses->channel.language);
ses->version = Version::GC_NTE;
ses->config.set_flags_for_version(ses->version, cmd.sub_version);
} else if (command == 0x9E) {
// We should only get a 9E while the session is unlinked
if (command == 0x9E) {
const auto& cmd = check_size_t<C_Login_GC_9E>(data, sizeof(C_LoginExtended_GC_9E));
ses->license = s->license_index->verify_gc(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
ses->sub_version = cmd.sub_version;
@@ -337,7 +333,8 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
ses->character_name = cmd.name.decode(ses->channel.language);
ses->config.parse_from(cmd.client_config);
if (cmd.sub_version >= 0x40) {
ses->version = Version::GC_EP3;
ses->log.info("Version changed to GC_EP3");
ses->channel.version = Version::GC_EP3;
}
} else {
throw runtime_error("command is not 9D or 9E");
@@ -425,10 +422,10 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
// destination somewhere - either in the client config or in the unlinked
// session
if (ses->config.proxy_destination_address != 0) {
linked_ses = make_shared<LinkedSession>(server, ses->local_port, ses->version, ses->license, ses->config);
linked_ses = make_shared<LinkedSession>(server, ses->local_port, ses->version(), ses->license, ses->config);
linked_ses->log.info("Opened licensed session for unlinked session based on client config");
} else if (ses->next_destination.ss_family == AF_INET) {
linked_ses = make_shared<LinkedSession>(server, ses->local_port, ses->version, ses->license, ses->next_destination);
linked_ses = make_shared<LinkedSession>(server, ses->local_port, ses->version(), ses->license, ses->next_destination);
linked_ses->log.info("Opened licensed session for unlinked session based on unlinked default destination");
} else {
ses->log.error("Cannot open linked session: no valid destination in client config or unlinked session");
@@ -437,12 +434,12 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
if (linked_ses.get()) {
server->id_to_session.emplace(ses->license->serial_number, linked_ses);
if (linked_ses->version() != ses->version) {
if (linked_ses->version() != ses->version()) {
linked_ses->log.error("Linked session has different game version");
} else {
// Resume the linked session using the unlinked session
try {
if (ses->version == Version::BB_V4) {
if (ses->version() == Version::BB_V4) {
linked_ses->resume(
std::move(ses->channel),
ses->detector_crypt,
+4 -1
View File
@@ -219,7 +219,6 @@ private:
PrefixedLogger log;
Channel channel;
uint16_t local_port;
Version version;
struct sockaddr_storage next_destination;
std::shared_ptr<PSOBBMultiKeyDetectorEncryption> detector_crypt;
@@ -242,6 +241,10 @@ private:
std::shared_ptr<ProxyServer> require_server() const;
std::shared_ptr<ServerState> require_server_state() const;
inline Version version() const {
return this->channel.version;
}
void receive_and_process_commands();
static void on_input(Channel& ch, uint16_t command, uint32_t flag, std::string& msg);
+1 -1
View File
@@ -370,10 +370,10 @@ const vector<pair<string, uint16_t>>& ServerState::proxy_destinations_for_versio
case Version::DC_V1_11_2000_PROTOTYPE:
case Version::DC_V1:
case Version::DC_V2:
case Version::GC_NTE:
return this->proxy_destinations_dc;
case Version::PC_V2:
return this->proxy_destinations_pc;
case Version::GC_NTE:
case Version::GC_V3:
case Version::GC_EP3_TRIAL_EDITION:
case Version::GC_EP3:
+1 -1
View File
@@ -70,8 +70,8 @@ const char* proxy_port_name_for_version(Version v) {
case Version::DC_V1_11_2000_PROTOTYPE:
case Version::DC_V1:
case Version::DC_V2:
return "dc-proxy";
case Version::GC_NTE:
return "dc-proxy";
case Version::GC_V3:
case Version::GC_EP3_TRIAL_EDITION:
case Version::GC_EP3:
+3 -2
View File
@@ -84,8 +84,9 @@ inline bool uses_v2_encryption(Version version) {
}
inline bool uses_v3_encryption(Version version) {
return (version == Version::GC_V3) ||
(version == Version::XB_V3) ||
(version == Version::GC_EP3);
(version == Version::GC_EP3_TRIAL_EDITION) ||
(version == Version::GC_EP3) ||
(version == Version::XB_V3);
}
inline bool uses_v4_encryption(Version version) {
return (version == Version::BB_V4);