Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send DS's SDL to clients from the AuthSrv #184

Merged
merged 6 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Hoikas marked this conversation as resolved.
Show resolved Hide resolved
}
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