support full DC NTE credentials

This commit is contained in:
Martin Michelsen
2024-02-24 22:49:37 -08:00
parent 70325793d9
commit 28cb1c52b5
8 changed files with 123 additions and 45 deletions
+23
View File
@@ -18,3 +18,26 @@ void forward_to_event_thread(std::shared_ptr<struct event_base> base, std::funct
std::function<void()>* new_fn = new std::function<void()>(std::move(fn));
event_base_once(base.get(), -1, EV_TIMEOUT, dispatch_forward_to_event_thread, new_fn, &tv);
}
template <>
void call_on_event_thread<void>(std::shared_ptr<struct event_base> base, std::function<void()>&& compute) {
bool succeeded = false;
std::string exc_what;
std::mutex ret_lock;
std::condition_variable ret_cv;
std::unique_lock<std::mutex> g(ret_lock);
forward_to_event_thread(base, [&]() -> void {
std::lock_guard<std::mutex> g(ret_lock);
try {
compute();
succeeded = true;
} catch (const std::exception& e) {
exc_what = e.what();
}
ret_cv.notify_one();
});
ret_cv.wait(g);
if (!succeeded) {
throw std::runtime_error(exc_what);
}
}
+3
View File
@@ -33,3 +33,6 @@ T call_on_event_thread(std::shared_ptr<struct event_base> base, std::function<T(
}
return ret.value();
}
template <>
void call_on_event_thread<void>(std::shared_ptr<struct event_base> base, std::function<void()>&& compute);
+38 -4
View File
@@ -19,6 +19,8 @@ License::License(const JSON& json)
bb_team_id(0) {
this->serial_number = json.get_int("SerialNumber");
this->access_key = json.get_string("AccessKey", "");
this->dc_nte_serial_number = json.get_string("DCNTESerialNumber", "");
this->dc_nte_access_key = json.get_string("DCNTEAccessKey", "");
this->gc_password = json.get_string("GCPassword", "");
this->xb_gamertag = json.get_string("XBGamerTag", "");
this->xb_user_id = json.get_int("XBUserID", 0);
@@ -38,6 +40,8 @@ JSON License::json() const {
return JSON::dict({
{"SerialNumber", this->serial_number},
{"AccessKey", this->access_key},
{"DCNTESerialNumber", this->dc_nte_serial_number},
{"DCNTEAccessKey", this->dc_nte_access_key},
{"GCPassword", this->gc_password},
{"XBGamerTag", this->xb_gamertag},
{"XBUserID", this->xb_user_id},
@@ -63,6 +67,12 @@ string License::str() const {
if (!this->access_key.empty()) {
tokens.emplace_back("access_key=" + this->access_key);
}
if (!this->dc_nte_serial_number.empty()) {
tokens.emplace_back("dc_nte_serial_number=" + this->dc_nte_serial_number);
}
if (!this->dc_nte_access_key.empty()) {
tokens.emplace_back("dc_nte_access_key=" + this->dc_nte_access_key);
}
if (!this->gc_password.empty()) {
tokens.emplace_back("gc_password=" + this->gc_password);
}
@@ -147,23 +157,47 @@ vector<shared_ptr<License>> LicenseIndex::all() const {
void LicenseIndex::add(shared_ptr<License> l) {
this->serial_number_to_license[l->serial_number] = l;
if (!l->bb_username.empty()) {
this->bb_username_to_license[l->bb_username] = l;
if (!l->dc_nte_serial_number.empty()) {
this->dc_nte_serial_number_to_license[l->dc_nte_serial_number] = l;
}
if (!l->xb_gamertag.empty()) {
this->xb_gamertag_to_license[l->xb_gamertag] = l;
}
if (!l->bb_username.empty()) {
this->bb_username_to_license[l->bb_username] = l;
}
}
void LicenseIndex::remove(uint32_t serial_number) {
auto l = this->serial_number_to_license.at(serial_number);
this->serial_number_to_license.erase(l->serial_number);
if (!l->bb_username.empty()) {
this->bb_username_to_license.erase(l->bb_username);
if (!l->dc_nte_serial_number.empty()) {
this->dc_nte_serial_number_to_license.erase(l->dc_nte_serial_number);
}
if (!l->xb_gamertag.empty()) {
this->xb_gamertag_to_license.erase(l->xb_gamertag);
}
if (!l->bb_username.empty()) {
this->bb_username_to_license.erase(l->bb_username);
}
}
shared_ptr<License> LicenseIndex::verify_dc_nte(const string& serial_number, const string& access_key) const {
if (serial_number.empty()) {
throw no_username();
}
try {
auto& license = this->dc_nte_serial_number_to_license.at(serial_number);
if (license->ban_end_time && (license->ban_end_time >= now())) {
throw invalid_argument("user is banned");
}
if (license->dc_nte_access_key != access_key) {
throw incorrect_access_key();
}
return license;
} catch (const out_of_range&) {
throw missing_license();
}
}
shared_ptr<License> LicenseIndex::verify_v1_v2(
+5 -1
View File
@@ -35,6 +35,8 @@ public:
};
uint32_t serial_number = 0;
std::string dc_nte_serial_number;
std::string dc_nte_access_key;
std::string access_key;
std::string gc_password;
std::string xb_gamertag;
@@ -123,6 +125,7 @@ public:
void add(std::shared_ptr<License> l);
void remove(uint32_t serial_number);
std::shared_ptr<License> verify_dc_nte(const std::string& serial_number, const std::string& access_key) const;
std::shared_ptr<License> verify_v1_v2(
uint32_t serial_number,
const std::string& access_key,
@@ -140,8 +143,9 @@ public:
std::shared_ptr<License> verify_bb(const std::string& username, const std::string& password) const;
protected:
std::unordered_map<std::string, std::shared_ptr<License>> bb_username_to_license;
std::unordered_map<std::string, std::shared_ptr<License>> dc_nte_serial_number_to_license;
std::unordered_map<std::string, std::shared_ptr<License>> xb_gamertag_to_license;
std::unordered_map<std::string, std::shared_ptr<License>> bb_username_to_license;
std::unordered_map<uint32_t, std::shared_ptr<License>> serial_number_to_license;
std::shared_ptr<License> create_temporary_license_for_shared_license(
+8 -8
View File
@@ -465,9 +465,8 @@ static void on_88_DCNTE(shared_ptr<Client> c, uint16_t, uint32_t, string& data)
c->config.set_flags_for_version(c->version(), -1);
c->log.info("Game version changed to DC_NTE");
uint32_t serial_number = stoul(cmd.serial_number.decode(), nullptr, 16);
try {
shared_ptr<License> l = s->license_index->verify_v1_v2(serial_number, cmd.access_key.decode(), "");
shared_ptr<License> l = s->license_index->verify_dc_nte(cmd.serial_number.decode(), cmd.access_key.decode());
c->set_license(l);
send_command(c, 0x88, 0x00);
@@ -487,8 +486,9 @@ static void on_88_DCNTE(shared_ptr<Client> c, uint16_t, uint32_t, string& data)
} else {
c->config.set_flag(Client::Flag::LICENSE_WAS_CREATED);
auto l = s->license_index->create_license();
l->serial_number = serial_number;
l->access_key = cmd.access_key.decode();
l->dc_nte_serial_number = cmd.serial_number.decode();
l->dc_nte_access_key = cmd.access_key.decode();
l->serial_number = fnv1a32(l->dc_nte_serial_number) & 0x7FFFFFFF;
s->license_index->add(l);
if (!s->is_replay) {
l->save();
@@ -510,9 +510,8 @@ static void on_8B_DCNTE(shared_ptr<Client> c, uint16_t, uint32_t, string& data)
c->config.set_flags_for_version(c->version(), -1);
c->log.info("Game version changed to DC_NTE");
uint32_t serial_number = stoul(cmd.serial_number.decode(), nullptr, 16);
try {
shared_ptr<License> l = s->license_index->verify_v1_v2(serial_number, cmd.access_key.decode(), cmd.name.decode());
shared_ptr<License> l = s->license_index->verify_dc_nte(cmd.serial_number.decode(), cmd.access_key.decode());
c->set_license(l);
} catch (const LicenseIndex::no_username& e) {
@@ -531,8 +530,9 @@ static void on_8B_DCNTE(shared_ptr<Client> c, uint16_t, uint32_t, string& data)
} else {
c->config.set_flag(Client::Flag::LICENSE_WAS_CREATED);
auto l = s->license_index->create_license();
l->serial_number = serial_number;
l->access_key = cmd.access_key.decode();
l->dc_nte_serial_number = cmd.serial_number.decode();
l->dc_nte_access_key = cmd.access_key.decode();
l->serial_number = fnv1a32(l->dc_nte_serial_number) & 0x7FFFFFFF;
s->license_index->add(l);
if (!s->is_replay) {
l->save();
+11 -1
View File
@@ -52,7 +52,7 @@ struct CommandDefinition {
if (!def) {
fprintf(stderr, "FAILED: no such command; try 'help'\n");
} else if (def->run_on_event_thread) {
args.s->forward_to_event_thread([def, args]() {
args.s->call_on_event_thread<void>([def, args]() {
CommandArgs local_args = args;
try {
def->run(local_args);
@@ -282,6 +282,8 @@ CommandDefinition c_add_license(
xb-user-id=<user-id> (Xbox user ID)\n\
xb-account-id=<account-id> (Xbox account ID)\n\
gc-password=<password> (GC password)\n\
dc-nte-serial-number=<serial-number> (DC NTE serial number)\n\
dc-nte-access-key=<access-key> (DC NTE access key)\n\
access-key=<access-key> (DC/GC/PC access key)\n\
serial=<serial-number> (decimal serial number; required for all licenses)\n\
flags=<privilege-mask> (see below)\n\
@@ -318,6 +320,10 @@ CommandDefinition c_add_license(
l->xb_account_id = stoull(token.substr(14), nullptr, 16);
} else if (starts_with(token, "gc-password=")) {
l->gc_password = token.substr(12);
} else if (starts_with(token, "dc-nte-serial-number=")) {
l->dc_nte_serial_number = token.substr(21);
} else if (starts_with(token, "dc-nte-access-key=")) {
l->dc_nte_access_key = token.substr(18);
} else if (starts_with(token, "access-key=")) {
l->access_key = token.substr(11);
} else if (starts_with(token, "serial=")) {
@@ -383,6 +389,10 @@ CommandDefinition c_update_license(
l->xb_account_id = stoull(token.substr(14), nullptr, 16);
} else if (starts_with(token, "gc-password=")) {
l->gc_password = token.substr(12);
} else if (starts_with(token, "dc-nte-serial-number=")) {
l->dc_nte_serial_number = token.substr(21);
} else if (starts_with(token, "dc-nte-access-key=")) {
l->dc_nte_access_key = token.substr(18);
} else if (starts_with(token, "access-key=")) {
l->access_key = token.substr(11);
} else if (starts_with(token, "serial=")) {
+4
View File
@@ -309,6 +309,10 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::pair<std::string, uint16_t> parse_port_spec(const JSON& json) const;
std::vector<PortConfiguration> parse_port_configuration(const JSON& json) const;
template <typename T>
inline void call_on_event_thread(std::function<T()>&& fn) {
return ::call_on_event_thread<T>(this->base, std::move(fn));
}
inline void forward_to_event_thread(std::function<void()>&& fn) {
::forward_to_event_thread(this->base, std::move(fn));
}