mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2026-02-16 10:41:54 -07:00
189 lines
6.0 KiB
C++
189 lines
6.0 KiB
C++
#include "server/serverinfoupdater.h"
|
|
#include "server/secrets.h"
|
|
#include "base/base64.h"
|
|
#include "base/md5.h"
|
|
#include "base/log.h"
|
|
#include "base/tick.h"
|
|
#include "mpshared/constants.h"
|
|
#include "mpshared/misc.h"
|
|
#include "yajl/wrapper/jsonbuilder.h"
|
|
#include <map>
|
|
|
|
namespace wi {
|
|
|
|
const int MSG_SERVERINFORESULT = 1;
|
|
|
|
ServerInfoUpdater::ServerInfoUpdater(Server& server,
|
|
base::SocketAddress& post_address, const std::string& post_path,
|
|
int sort_key, const std::string& server_name,
|
|
const std::string& server_location, const std::string& server_type,
|
|
base::SocketAddress& public_address, const std::string& extra_json,
|
|
int expires) : server_(server),
|
|
post_address_(post_address), post_path_(post_path),
|
|
sort_key_(sort_key), server_name_(server_name),
|
|
server_location_(server_location), server_type_(server_type),
|
|
public_address_(public_address), expires_(expires), drain_(false) {
|
|
ParseExtra(extra_json);
|
|
worker_.Start(this, &ServerInfoUpdater::ThreadStart);
|
|
}
|
|
|
|
ServerInfoUpdater::~ServerInfoUpdater() {
|
|
// Thread::Stop gets called in the Thread destructor, which does a join
|
|
// with the actual thread, which synchronizes exiting
|
|
}
|
|
|
|
void ServerInfoUpdater::OnPostComplete(HttpPost *post, int status_code,
|
|
int error, const base::ByteBuffer& result) {
|
|
if (status_code != 200) {
|
|
RLOG() << "ERROR posting ServerInfo, status code: " << status_code;
|
|
return;
|
|
}
|
|
|
|
// Pass on the result, if there is one.
|
|
if (result.Length() != 0) {
|
|
thread_.Post(MSG_SERVERINFORESULT, this,
|
|
new ServerInfoResult(result));
|
|
}
|
|
}
|
|
|
|
void ServerInfoUpdater::OnMessage(base::Message *pmsg) {
|
|
if (pmsg->id == MSG_SERVERINFORESULT) {
|
|
ServerInfoResult *res = (ServerInfoResult *)pmsg->data;
|
|
SignalOnResponse(this, res->result);
|
|
delete res;
|
|
}
|
|
}
|
|
|
|
void ServerInfoUpdater::ThreadStart(void *pv) {
|
|
// Create and send a new ServerInfo post on an expires_ / 2 interval,
|
|
// to keep it fresh. expires_ in seconds, so convert to ticks (100/second).
|
|
while (true) {
|
|
HttpPost *post = new HttpPost(post_address_, post_path_, MakeBody());
|
|
if (post != NULL) {
|
|
post->SignalOnComplete.connect(this,
|
|
&ServerInfoUpdater::OnPostComplete);
|
|
post->Submit();
|
|
}
|
|
worker_.RunLoop(expires_ * 100 / 2);
|
|
if (post != NULL) {
|
|
post->SignalOnComplete.disconnect(this);
|
|
delete post;
|
|
post = NULL;
|
|
}
|
|
if (worker_.IsStopping()) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
base::ByteBuffer *ServerInfoUpdater::MakeBody() {
|
|
std::string json = MakeJson();
|
|
|
|
// Create HMAC like signature of hash(json + secret) so the receiver
|
|
// can ensure it is valid.
|
|
MD5_CTX md5;
|
|
MD5Init(&md5);
|
|
MD5Update(&md5, (const byte *)json.c_str(), json.size());
|
|
MD5Update(&md5, (const byte *)kszServerInfoSecret,
|
|
strlen(kszServerInfoSecret));
|
|
byte hash[16];
|
|
MD5Final(hash, &md5);
|
|
char hash_str[33];
|
|
strncpyz(hash_str, base::Format::ToHex(hash, sizeof(hash)),
|
|
sizeof(hash_str));
|
|
base::ByteBuffer *bb = new base::ByteBuffer(32 + json.size());
|
|
bb->WriteBytes((const byte *)hash_str, 32);
|
|
bb->WriteBytes((const byte *)json.c_str(), json.size());
|
|
return bb;
|
|
}
|
|
|
|
// See stats/serverinfo.py for json description. The poster generates
|
|
// one of the info maps.
|
|
|
|
std::string ServerInfoUpdater::MakeJson() {
|
|
#ifdef DEBUG
|
|
yajl_gen_config yajl_config = { 1, " " };
|
|
#else
|
|
yajl_gen_config yajl_config = { 0, NULL };
|
|
#endif
|
|
yajl_gen g = yajl_gen_alloc(&yajl_config);
|
|
|
|
yajl_gen_map_open(g);
|
|
GenNum(g, "sort_key", sort_key_);
|
|
GenString(g, "name", server_name_.c_str());
|
|
GenString(g, "location", server_location_.c_str());
|
|
GenString(g, "address", public_address_.ToString());
|
|
GenNum(g, "protocol", kdwProtocolCurrent);
|
|
if (drain_) {
|
|
GenString(g, "status", "drain");
|
|
} else {
|
|
GenString(g, "status", "ok");
|
|
}
|
|
GenNum(g, "expires_utc", base::GetSecondsUnixEpocUTC() + expires_);
|
|
GenNum(g, "start_utc", server_.start_time());
|
|
GenNum(g, "player_count", server_.endpoint_count_thread_safe());
|
|
GenString(g, "type", server_type_.c_str());
|
|
|
|
// Add in the extras
|
|
ExtraMap::iterator it = extra_.begin();
|
|
for (; it != extra_.end(); it++) {
|
|
GenString(g, it->first.c_str(), it->second.c_str());
|
|
}
|
|
|
|
yajl_gen_map_close(g);
|
|
|
|
const char *buf;
|
|
unsigned int len;
|
|
yajl_gen_get_buf(g, (const unsigned char **)&buf, &len);
|
|
std::string result(buf);
|
|
yajl_gen_free(g);
|
|
return result;
|
|
}
|
|
|
|
void ServerInfoUpdater::GenNum(yajl_gen g, const char *key, dword value) {
|
|
yajl_gen_string(g, (const unsigned char *)key, strlen(key));
|
|
const char *s = base::Format::ToString("%lu", value);
|
|
yajl_gen_number(g, s, strlen(s));
|
|
}
|
|
|
|
void ServerInfoUpdater::GenString(yajl_gen g, const char *key,
|
|
const char *value) {
|
|
yajl_gen_string(g, (const unsigned char *)key, strlen(key));
|
|
yajl_gen_string(g, (const unsigned char *)value, strlen(value));
|
|
}
|
|
|
|
void ServerInfoUpdater::ParseExtra(const std::string& extra_json) {
|
|
if (extra_json.size() == 0) {
|
|
return;
|
|
}
|
|
|
|
// Parse json
|
|
json::JsonBuilder builder;
|
|
builder.Start();
|
|
builder.Update(extra_json.c_str(), extra_json.size());
|
|
json::JsonObject *obj = builder.End();
|
|
|
|
// Ensure a map results
|
|
if (obj->type() != json::JSONTYPE_MAP) {
|
|
delete obj;
|
|
return;
|
|
}
|
|
json::JsonMap *map = (json::JsonMap *)obj;
|
|
|
|
// Pull out the strings and add them to the extra map.
|
|
Enum enm;
|
|
const char *key;
|
|
while ((key = map->EnumKeys(&enm)) != NULL) {
|
|
const json::JsonObject *value_obj = map->GetObject(key);
|
|
if (value_obj->type() != json::JSONTYPE_STRING) {
|
|
continue;
|
|
}
|
|
const char *value = ((const json::JsonString *)value_obj)->GetString();
|
|
extra_.insert(ExtraMap::value_type(std::string(key),
|
|
std::string(value)));
|
|
}
|
|
delete map;
|
|
}
|
|
|
|
} // namespace wi
|