Skip to content

Commit

Permalink
Merge pull request #184 from Hoikas/sdl_exchange2
Browse files Browse the repository at this point in the history
Send DS's SDL to clients from the AuthSrv
  • Loading branch information
zrax committed Feb 20, 2024
2 parents 7cb600a + 4e17321 commit b9f06e7
Show file tree
Hide file tree
Showing 11 changed files with 644 additions and 78 deletions.
8 changes: 8 additions & 0 deletions AuthServ/AuthManifest.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ namespace DS
struct AuthFileInfo
{
AuthFileInfo() : m_fileSize() { }
AuthFileInfo(ST::string filename, uint32_t fileSize)
: m_filename(std::move(filename)), m_fileSize(fileSize)
{ }

AuthFileInfo(const AuthFileInfo&) = delete;
AuthFileInfo& operator=(const AuthFileInfo&) = delete;
Expand All @@ -48,6 +51,11 @@ namespace DS

size_t fileCount() const { return m_files.size(); }

void addFile(ST::string filename, uint32_t fileSize)
{
m_files.emplace_back(std::move(filename), fileSize);
}

private:
std::vector<AuthFileInfo> m_files;
};
Expand Down
74 changes: 65 additions & 9 deletions AuthServ/AuthServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@

#include "AuthServer_Private.h"
#include "AuthManifest.h"
#include "SDL/DescriptorDb.h"
#include "Types/BitVector.h"
#include "Types/Uuid.h"
#include "settings.h"
#include "errors.h"

#include <string_theory/format>
#include <openssl/rand.h>
#include <poll.h>
#include <sys/stat.h>
#include <sys/types.h>

#define NODE_SIZE_MAX (4 * 1024 * 1024)

Expand Down Expand Up @@ -566,7 +570,33 @@ void cb_fileList(AuthServer_Private& client)
ST::string mfsname = ST::format("{}{}_{}.list", DS::Settings::AuthRoot(),
directory, fileext);
DS::AuthManifest mfs;
DS::NetResultCode result = mfs.loadManifest(mfsname.c_str());
DS::NetResultCode result = DS::e_NetPending;

// Special case: SDL files
// For production shards, we expect for them to be listed in the secure preloader manifest.
// If that hasn't been done, don't worry about the SDL lists - just use the SDL files that
// DS would load on start up.
if (directory.compare_i("SDL") == 0 && fileext.compare_i("sdl") == 0) {
auto populateSdl = [&mfs](const ST::string& path) {
struct stat sbuf;
if (stat(path.c_str(), &sbuf) < 0)
throw DS::SystemError("[Auth] Unable to stat SDL file", strerror(errno));
ST::string filename = path.after_last('/');
mfs.addFile(ST::format("SDL\\{}", filename), sbuf.st_size);
return true;
};
try {
SDL::DescriptorDb::ForDescriptorFiles(DS::Settings::SdlPath(), std::move(populateSdl));
result = DS::e_NetSuccess;
} catch (const DS::SystemError& err) {
fputs(err.what(), stderr);
result = DS::e_NetInternalError;
}
} else {
result = mfs.loadManifest(mfsname.c_str());
}

DS_ASSERT(result != DS::e_NetPending);
client.m_buffer.write<uint32_t>(result);

if (result != DS::e_NetSuccess) {
Expand Down Expand Up @@ -594,6 +624,7 @@ void cb_downloadStart(AuthServer_Private& client)

// Download filename
ST::string filename = DS::CryptRecvString(client.m_sock, client.m_crypt);
filename = filename.replace("\\", "/");

// Ensure filename is jailed to our data path
if (filename.find("..") != -1) {
Expand All @@ -604,12 +635,18 @@ void cb_downloadStart(AuthServer_Private& client)
SEND_REPLY();
return;
}
filename = filename.replace("\\", "/");

filename = DS::Settings::AuthRoot() + filename;
DS::FileStream* stream = new DS::FileStream();
// Special case: SDL files from the server' SDL directory.
ST_ssize_t slashPos = filename.find_last('/');
if (slashPos != -1 && filename.left(slashPos).compare_i("SDL") == 0 && filename.after_last('.').compare_i("sdl") == 0) {
filename = DS::Settings::SdlPath() + filename.substr(slashPos);
} else {
filename = DS::Settings::AuthRoot() + filename;
}

auto fileStream = std::make_unique<DS::FileStream>();
try {
stream->open(filename.c_str(), "rb");
fileStream->open(filename.c_str(), "rb");
} catch (const DS::FileIOException& ex) {
ST::printf(stderr, "[Auth] Could not open file {}: {}\n[Auth] Requested by {}\n",
filename, ex.what(), DS::SockIpAddress(client.m_sock));
Expand All @@ -618,10 +655,31 @@ void cb_downloadStart(AuthServer_Private& client)
client.m_buffer.write<uint32_t>(0); // Chunk offset
client.m_buffer.write<uint32_t>(0); // Data packet size
SEND_REPLY();
delete stream;
return;
}

// All auth downloads must be encrypted.
std::unique_ptr<DS::Stream> stream;
if (!DS::EncryptedStream::CheckEncryption(fileStream.get()).has_value()) {
auto bufStream = std::make_unique<DS::BufferStream>();
{
DS::EncryptedStream encStream(bufStream.get(), DS::EncryptedStream::Mode::e_write,
DS::EncryptedStream::Type::e_xxtea,
DS::Settings::DroidKey());
uint8_t buf[CHUNK_SIZE];
while (fileStream->tell() < fileStream->size()) {
ssize_t nread = fileStream->readBytes(buf, sizeof(buf));
DS_ASSERT(nread >= 0);
encStream.writeBytes(buf, nread);
}
}
bufStream->seek(0, SEEK_SET);
stream = std::move(bufStream);
} else {
stream = std::move(fileStream);
}

DS_ASSERT(stream);
client.m_buffer.write<uint32_t>(DS::e_NetSuccess);
client.m_buffer.write<uint32_t>(stream->size());
client.m_buffer.write<uint32_t>(stream->tell());
Expand All @@ -631,12 +689,11 @@ void cb_downloadStart(AuthServer_Private& client)
client.m_buffer.write<uint32_t>(CHUNK_SIZE);
stream->readBytes(data, CHUNK_SIZE);
client.m_buffer.writeBytes(data, CHUNK_SIZE);
client.m_downloads[transId] = stream;
client.m_downloads[transId] = std::move(stream);
} else {
client.m_buffer.write<uint32_t>(stream->size());
stream->readBytes(data, stream->size());
client.m_buffer.writeBytes(data, stream->size());
delete stream;
}

SEND_REPLY();
Expand Down Expand Up @@ -670,7 +727,6 @@ void cb_downloadNext(AuthServer_Private& client)
client.m_buffer.write<uint32_t>(bytesLeft);
fi->second->readBytes(data, bytesLeft);
client.m_buffer.writeBytes(data, bytesLeft);
delete fi->second;
client.m_downloads.erase(fi);
}

Expand Down
12 changes: 2 additions & 10 deletions AuthServ/AuthServer_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <unordered_map>
#include <thread>
#include <mutex>
#include <memory>

enum AuthServer_MsgIds
{
Expand Down Expand Up @@ -105,18 +106,9 @@ struct AuthServer_Private : public AuthClient_Private
uint32_t m_acctFlags;
AuthServer_PlayerInfo m_player;
uint32_t m_ageNodeId;
std::map<uint32_t, DS::Stream*> m_downloads;
std::map<uint32_t, std::unique_ptr<DS::Stream>> m_downloads;

AuthServer_Private() : m_serverChallenge(0), m_acctFlags(0), m_ageNodeId(0) { }

~AuthServer_Private()
{
while (!m_downloads.empty()) {
auto item = m_downloads.begin();
delete item->second;
m_downloads.erase(item);
}
}
};

extern std::list<AuthServer_Private*> s_authClients;
Expand Down
81 changes: 49 additions & 32 deletions SDL/DescriptorDb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,46 +30,39 @@ static int sel_sdl(const dirent* de)

SDL::DescriptorDb::descmap_t SDL::DescriptorDb::s_descriptors;

bool SDL::DescriptorDb::LoadDescriptors(const char* sdlpath)
bool SDL::DescriptorDb::LoadDescriptorsFromFile(const ST::string& filename)
{
dirent** dirls;
int count = scandir(sdlpath, &dirls, &sel_sdl, &alphasort);
if (count < 0) {
ST::printf(stderr, "[SDL] Error reading SDL descriptors: {}\n", strerror(errno));
return false;
}
if (count == 0) {
fputs("[SDL] Warning: No SDL descriptors found!\n", stderr);
free(dirls);
return true;
}

SDL::Parser parser;
for (int i=0; i<count; ++i) {
ST::string filename = ST::format("{}/{}", sdlpath, dirls[i]->d_name);
if (parser.open(filename.c_str())) {
std::list<StateDescriptor> descriptors = parser.parse();
for (auto it = descriptors.begin(); it != descriptors.end(); ++it) {
if (parser.open(filename.c_str())) {
std::list<StateDescriptor> descriptors = parser.parse();
for (auto it = descriptors.begin(); it != descriptors.end(); ++it) {
#ifdef DEBUG
descmap_t::iterator namei = s_descriptors.find(it->m_name);
if (namei != s_descriptors.end()) {
if (namei->second.find(it->m_version) != namei->second.end()) {
ST::printf(stderr, "[SDL] Warning: Duplicate descriptor version for {}\n",
it->m_name);
}
descmap_t::iterator namei = s_descriptors.find(it->m_name);
if (namei != s_descriptors.end()) {
if (namei->second.find(it->m_version) != namei->second.end()) {
ST::printf(stderr, "[SDL] Warning: Duplicate descriptor version for {}\n",
it->m_name);
}
}
#endif
s_descriptors[it->m_name][it->m_version] = *it;
s_descriptors[it->m_name][it->m_version] = *it;

// Keep the highest version in -1
if (s_descriptors[it->m_name][-1].m_version < it->m_version)
s_descriptors[it->m_name][-1] = *it;
}
// Keep the highest version in -1
if (s_descriptors[it->m_name][-1].m_version < it->m_version)
s_descriptors[it->m_name][-1] = *it;
}
parser.close();
free(dirls[i]);
}
free(dirls);
return true;
}

bool SDL::DescriptorDb::LoadDescriptors(const char* sdlpath)
{
try {
ForDescriptorFiles(sdlpath, LoadDescriptorsFromFile);
} catch (const DS::SystemError& err) {
fputs(err.what(), stderr);
return false;
}
return true;
}

Expand Down Expand Up @@ -124,3 +117,27 @@ bool SDL::DescriptorDb::ForLatestDescriptors(descfunc_t functor)
return true;
}

bool SDL::DescriptorDb::ForDescriptorFiles(const char* sdlpath, filefunc_t functor)
{
dirent** dirls;
int count = scandir(sdlpath, &dirls, &sel_sdl, &alphasort);

DS_ASSERT(count > 0);
if (count == 0)
fputs("[SDL] Warning: No SDL descriptors found!\n", stderr);
if (count < 0)
throw DS::SystemError("[SDL] Error scanning for SDL files", strerror(errno));

bool retval = true;
for (int i = 0; i < count; i++) {
if (!functor(ST::format("{}/{}", sdlpath, dirls[i]->d_name))) {
retval = false;
break;
}
}

for (int i = 0; i < count; i++)
free(dirls[i]);
free(dirls);
return retval;
}
4 changes: 4 additions & 0 deletions SDL/DescriptorDb.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,21 @@ namespace SDL
{
public:
typedef std::function<bool(const ST::string&, StateDescriptor*)> descfunc_t;
typedef std::function<bool(ST::string path)> filefunc_t;

static bool LoadDescriptors(const char* sdlpath);
static StateDescriptor* FindDescriptor(const ST::string& name, int version);
static StateDescriptor* FindLatestDescriptor(const ST::string& name);
static bool ForLatestDescriptors(descfunc_t functor);
static bool ForDescriptorFiles(const char* sdlpath, filefunc_t functor);

private:
DescriptorDb() = delete;
DescriptorDb(const DescriptorDb&) = delete;
~DescriptorDb() = delete;

static bool LoadDescriptorsFromFile(const ST::string& path);

typedef std::unordered_map<int, StateDescriptor> versionmap_t;
typedef std::unordered_map<ST::string, versionmap_t, ST::hash_i, ST::equal_i> descmap_t;
static descmap_t s_descriptors;
Expand Down
54 changes: 38 additions & 16 deletions SDL/SdlParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
******************************************************************************/

#include "SdlParser.h"
#include "settings.h"
#include "streams.h"

#include <string_theory/stdio>

Expand All @@ -27,25 +29,35 @@ static const char* s_toknames[] = {
"<Identifier>", "<Number>", "<String>", "<Typename>",
};

bool SDL::Parser::open(const char* filename)
DS::Stream* SDL::Parser::stream() const
{
char sanitycheck[12];
if (m_encStream)
return m_encStream;
else
return m_fileStream;
}

m_file = fopen(filename, "r");
if (!m_file) {
ST::printf(stderr, "[SDL] Error opening file {} for reading\n", filename);
bool SDL::Parser::open(const char* filename)
{
m_fileStream = new DS::FileStream();
try {
m_fileStream->open(filename, "r");
} catch (DS::FileIOException& ex) {
ST::printf(stderr, "[SDL] Error opening file {} for reading: {}\n", filename, ex.what());
close();
return false;
}
memset(sanitycheck, 0, sizeof(sanitycheck));
fread(sanitycheck, 1, 12, m_file);
fseek(m_file, 0, SEEK_SET);
if (memcmp(sanitycheck, "whatdoyousee", 12) == 0
|| memcmp(sanitycheck, "notthedroids", 12) == 0
|| memcmp(sanitycheck, "BriceIsSmart", 12) == 0) {
fputs("[SDL] Error: DirtSand does not support encrypted SDL sources\n", stderr);
fputs("[SDL] Please decrypt your SDL files and re-start DirtSand\n", stderr);
ST::printf(stderr, "[SDL] Error in file: {}\n", filename);
return false;

if (DS::EncryptedStream::CheckEncryption(m_fileStream).has_value()) {
try {
m_encStream = new DS::EncryptedStream(m_fileStream, DS::EncryptedStream::Mode::e_read);
} catch (DS::FileIOException& ex) {
ST::printf(stderr, "[SDL] Error opening file {} for reading: {}\n", filename, ex.what());
close();
return false;
}
if (m_encStream->getEncType() == DS::EncryptedStream::Type::e_xxtea)
m_encStream->setKeys(DS::Settings::DroidKey());
}

m_filename = filename;
Expand All @@ -54,6 +66,16 @@ bool SDL::Parser::open(const char* filename)
return true;
}

void SDL::Parser::close()
{
delete m_encStream;
m_encStream = nullptr;
delete m_fileStream;
m_fileStream = nullptr;
m_filename.clear();
m_lineno = -1;
}

static SDL::TokenType str_to_toktype(const ST::string& str)
{
if (str == "STATEDESC")
Expand Down Expand Up @@ -112,7 +134,7 @@ SDL::Token SDL::Parser::next()
while (m_buffer.empty()) {
Token tokbuf;
char lnbuf[4096];
if (!fgets(reinterpret_cast<char*>(lnbuf), 4096, m_file)) {
if (!m_fileStream->readLine(lnbuf, sizeof(lnbuf))) {
tokbuf.m_type = e_TokEof;
m_buffer.push_back(tokbuf);
break;
Expand Down
Loading

0 comments on commit b9f06e7

Please sign in to comment.