Overhaul versioncheck system
This commit is contained in:
parent
a78355e88a
commit
b75ac5e39e
14 changed files with 2905 additions and 3369 deletions
|
@ -4,7 +4,7 @@ set(OUTPUT_CONFS ad.json anongame_infos.conf address_translation.conf
|
|||
bnissue.txt bnmaps.conf bnxpcalc.conf
|
||||
bnxplevel.conf channel.conf command_groups.conf realm.conf
|
||||
sql_DB_layout.conf supportfile.conf topics.conf
|
||||
tournament.conf versioncheck.conf icons.conf)
|
||||
tournament.conf versioncheck.json icons.conf)
|
||||
foreach(CONF ${OUTPUT_CONFS})
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${CONF}.in ${CMAKE_CURRENT_BINARY_DIR}/${CONF} @ONLY)
|
||||
endforeach(CONF)
|
||||
|
@ -20,7 +20,7 @@ if(WITH_BNETD)
|
|||
bnetd_default_user.plain bnissue.txt bnmaps.conf
|
||||
bnxpcalc.conf bnxplevel.conf channel.conf command_groups.conf
|
||||
realm.conf sql_DB_layout.conf supportfile.conf topics.conf
|
||||
tournament.conf versioncheck.conf icons.conf)
|
||||
tournament.conf versioncheck.json icons.conf)
|
||||
|
||||
# special treatment for non .in files
|
||||
install(FILES bnetd_default_user.cdb DESTINATION ${SYSCONFDIR})
|
||||
|
|
|
@ -85,7 +85,7 @@ mpqfile = "${SYSCONFDIR}/autoupdate.conf"
|
|||
logfile = "${LOCALSTATEDIR}/bnetd.log"
|
||||
realmfile = "${SYSCONFDIR}/realm.conf"
|
||||
maildir = "${LOCALSTATEDIR}/bnmail"
|
||||
versioncheck_file = "${SYSCONFDIR}/versioncheck.conf"
|
||||
versioncheck_file = "${SYSCONFDIR}/versioncheck.json"
|
||||
mapsfile = "${SYSCONFDIR}/bnmaps.conf"
|
||||
xplevelfile = "${SYSCONFDIR}/bnxplevel.conf"
|
||||
xpcalcfile = "${SYSCONFDIR}/bnxpcalc.conf"
|
||||
|
@ -232,18 +232,10 @@ allow_bad_version = true
|
|||
# is a good idea.
|
||||
allow_unknown_version = true
|
||||
|
||||
# This defines how the exeinfo field in the versioncheck file is being
|
||||
# checked. You can choose between no match at all [none] (default),
|
||||
# exact match [exact], exact case-sensitive match [exactcase], dumb wildcard
|
||||
# match [wildcard], and parsed value comparison [parse].
|
||||
# NOTE: [parse] needs the mktime() function and might therefore not work on
|
||||
# every system.
|
||||
version_exeinfo_match = none
|
||||
|
||||
# If you have choosen [parse] above, this is the tolerance with which
|
||||
# the time can differ. The value must be given in seconds. If it's 0 this
|
||||
# check is disabled.
|
||||
version_exeinfo_maxdiff = 0
|
||||
# Globally enable or disable versioncheck exeinfo matching
|
||||
# If set to false, a client can pass any exeinfo string to pvpgn
|
||||
# and will be allowed to connect.
|
||||
version_exeinfo_match = true
|
||||
|
||||
# #
|
||||
##############################################################################
|
||||
|
@ -448,7 +440,7 @@ max_friends = 20
|
|||
# Set track=0 to disable tracking. Any other number will set number
|
||||
# of seconds between sending tracking packets. This is ON by default.
|
||||
#track = 0
|
||||
track = 60
|
||||
track = 0
|
||||
|
||||
# Tracking server(s)
|
||||
# Use a comma delimited list of hostnames with optional UDP port numbers
|
||||
|
|
|
@ -68,7 +68,7 @@ transfile = conf\address_translation.conf
|
|||
mpqfile = conf\autoupdate.conf
|
||||
logfile = var\bnetd.log
|
||||
realmfile = conf\realm.conf
|
||||
versioncheck_file = conf\versioncheck.conf
|
||||
versioncheck_file = conf\versioncheck.json
|
||||
mapsfile = conf\bnmaps.conf
|
||||
xplevelfile = conf\bnxplevel.conf
|
||||
xpcalcfile = conf\bnxpcalc.conf
|
||||
|
|
File diff suppressed because it is too large
Load diff
2163
conf/versioncheck.json.in
Normal file
2163
conf/versioncheck.json.in
Normal file
File diff suppressed because it is too large
Load diff
|
@ -72,7 +72,6 @@ namespace pvpgn
|
|||
|
||||
/**********************************************************************************/
|
||||
static t_connection *_connlist_find_connection_by_uid(int uid);
|
||||
static char const *_conn_get_versiontag(t_connection * c);
|
||||
|
||||
static int _anongame_gametype_to_queue(int type, int gametype);
|
||||
static int _anongame_level_by_queue(t_connection * c, int queue);
|
||||
|
@ -95,11 +94,6 @@ namespace pvpgn
|
|||
return connlist_find_connection_by_account(accountlist_find_account_by_uid(uid));
|
||||
}
|
||||
|
||||
static char const *_conn_get_versiontag(t_connection * c)
|
||||
{
|
||||
return versioncheck_get_versiontag(conn_get_versioncheck(c));
|
||||
}
|
||||
|
||||
/**********/
|
||||
|
||||
static char const *_anongame_queue_to_string(int queue)
|
||||
|
@ -541,7 +535,7 @@ namespace pvpgn
|
|||
md = (t_matchdata*)xmalloc(sizeof(t_matchdata));
|
||||
md->c = c;
|
||||
md->map_prefs = map_prefs;
|
||||
md->versiontag = _conn_get_versiontag(c);
|
||||
md->versiontag = conn_get_versioncheck(c)->get_version_tag().c_str();
|
||||
|
||||
list_append_data(matchlists[queue][level], md);
|
||||
|
||||
|
@ -834,7 +828,10 @@ namespace pvpgn
|
|||
|
||||
LIST_TRAVERSE(matchlists[queue][level + delta], curr) {
|
||||
md = (t_matchdata*)elem_get_data(curr);
|
||||
if (md->versiontag && _conn_get_versiontag(c) && !std::strcmp(md->versiontag, _conn_get_versiontag(c)) && (cur_prefs & md->map_prefs)) {
|
||||
if (md->versiontag
|
||||
&& !std::strcmp(md->versiontag, conn_get_versioncheck(c)->get_version_tag().c_str())
|
||||
&& (cur_prefs & md->map_prefs))
|
||||
{
|
||||
/* set maxlevel and minlevel to keep all players within 6 levels */
|
||||
maxlevel = (level + delta + diff < maxlevel) ? level + delta + diff : maxlevel;
|
||||
minlevel = (level + delta - diff > minlevel) ? level + delta - diff : minlevel;
|
||||
|
|
|
@ -402,7 +402,7 @@ namespace pvpgn
|
|||
temp->protocol.client.clientexe = NULL;
|
||||
temp->protocol.client.owner = NULL;
|
||||
temp->protocol.client.cdkey = NULL;
|
||||
temp->protocol.client.versioncheck = NULL;
|
||||
temp->protocol.client.versioncheck = nullptr;
|
||||
temp->protocol.account = NULL;
|
||||
temp->protocol.chat.channel = NULL;
|
||||
temp->protocol.chat.last_message = now;
|
||||
|
@ -617,9 +617,6 @@ namespace pvpgn
|
|||
if (c->protocol.account)
|
||||
watchlist->dispatch(c->protocol.account, NULL, c->protocol.client.clienttag, Watch::ET_logout);
|
||||
|
||||
if (c->protocol.client.versioncheck)
|
||||
versioncheck_destroy((t_versioncheck*)c->protocol.client.versioncheck); /* avoid warning */
|
||||
|
||||
if (c->protocol.chat.lastsender)
|
||||
xfree((void *)c->protocol.chat.lastsender); /* avoid warning */
|
||||
|
||||
|
@ -3199,34 +3196,29 @@ namespace pvpgn
|
|||
}
|
||||
|
||||
|
||||
extern t_versioncheck * conn_get_versioncheck(t_connection * c)
|
||||
const VersionCheck *conn_get_versioncheck(t_connection *c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return c->protocol.client.versioncheck;
|
||||
}
|
||||
|
||||
|
||||
extern int conn_set_versioncheck(t_connection * c, t_versioncheck * versioncheck)
|
||||
bool conn_set_versioncheck(t_connection *c, const VersionCheck& versioncheck)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return -1;
|
||||
}
|
||||
if (!versioncheck)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL versioncheck");
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
c->protocol.client.versioncheck = versioncheck;
|
||||
c->protocol.client.versioncheck = &versioncheck;
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern int conn_get_echoback(t_connection * c)
|
||||
|
|
|
@ -150,7 +150,7 @@ namespace pvpgn
|
|||
char const * clientexe;
|
||||
char const * owner;
|
||||
char const * cdkey;
|
||||
t_versioncheck * versioncheck; /* equation and MPQ file used to validate game checksum */
|
||||
const VersionCheck *versioncheck;
|
||||
} client; /* client program specific data */
|
||||
struct {
|
||||
t_queue * outqueue; /* packets waiting to be sent */
|
||||
|
@ -382,8 +382,8 @@ namespace pvpgn
|
|||
extern int conn_quota_exceeded(t_connection * c, char const * message);
|
||||
extern int conn_set_lastsender(t_connection * c, char const * sender);
|
||||
extern char const * conn_get_lastsender(t_connection const * c);
|
||||
extern t_versioncheck * conn_get_versioncheck(t_connection * c);
|
||||
extern int conn_set_versioncheck(t_connection * c, t_versioncheck * versioncheck);
|
||||
const VersionCheck *conn_get_versioncheck(t_connection *c);
|
||||
bool conn_set_versioncheck(t_connection *c, const VersionCheck& versioncheck);
|
||||
extern int conn_get_echoback(t_connection * c);
|
||||
extern void conn_set_echoback(t_connection * c, int echoback);
|
||||
extern int conn_set_ircline(t_connection * c, char const * line);
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
|
||||
#include "compat/strcasecmp.h"
|
||||
|
@ -570,11 +571,14 @@ namespace pvpgn
|
|||
packet_del_ref(rpacket);
|
||||
}
|
||||
|
||||
if ((rpacket = packet_create(packet_class_bnet))) {
|
||||
t_versioncheck *vc;
|
||||
|
||||
if ((rpacket = packet_create(packet_class_bnet)))
|
||||
{
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "[{}] selecting version check", conn_get_socket(c));
|
||||
vc = versioncheck_create(conn_get_archtag(c), conn_get_clienttag(c));
|
||||
const VersionCheck& vc = select_versioncheck_entry(
|
||||
conn_get_archtag(c),
|
||||
conn_get_clienttag(c),
|
||||
conn_get_versionid(c));
|
||||
|
||||
conn_set_versioncheck(c, vc);
|
||||
packet_set_size(rpacket, sizeof(t_server_authreq_109));
|
||||
packet_set_type(rpacket, SERVER_AUTHREQ_109);
|
||||
|
@ -588,10 +592,10 @@ namespace pvpgn
|
|||
|
||||
bn_int_set(&rpacket->u.server_authreq_109.sessionkey, conn_get_sessionkey(c));
|
||||
bn_int_set(&rpacket->u.server_authreq_109.sessionnum, conn_get_sessionnum(c));
|
||||
file_to_mod_time(c, versioncheck_get_mpqfile(vc), &rpacket->u.server_authreq_109.timestamp);
|
||||
packet_append_string(rpacket, versioncheck_get_mpqfile(vc));
|
||||
packet_append_string(rpacket, versioncheck_get_eqn(vc));
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "[{}] selected \"{}\" \"{}\"", conn_get_socket(c), versioncheck_get_mpqfile(vc), versioncheck_get_eqn(vc));
|
||||
file_to_mod_time(c, vc.get_checkrevision_filename().c_str(), &rpacket->u.server_authreq_109.timestamp);
|
||||
packet_append_string(rpacket, vc.get_checkrevision_filename().c_str());
|
||||
packet_append_string(rpacket, vc.get_equation().c_str());
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "[{}] selected \"{}\" \"{}\"", conn_get_socket(c), vc.get_checkrevision_filename(), vc.get_equation());
|
||||
if ((conn_get_clienttag(c) == CLIENTTAG_WARCRAFT3_UINT)
|
||||
|| (conn_get_clienttag(c) == CLIENTTAG_WAR3XP_UINT)) {
|
||||
char padding[128];
|
||||
|
@ -646,19 +650,25 @@ namespace pvpgn
|
|||
packet_del_ref(rpacket);
|
||||
}
|
||||
}
|
||||
else {
|
||||
t_versioncheck *vc;
|
||||
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "[{}] selecting version check", conn_get_socket(c));
|
||||
vc = versioncheck_create(conn_get_archtag(c), conn_get_clienttag(c));
|
||||
|
||||
const VersionCheck& vc = select_versioncheck_entry(
|
||||
conn_get_archtag(c),
|
||||
conn_get_clienttag(c),
|
||||
conn_get_versionid(c));
|
||||
|
||||
conn_set_versioncheck(c, vc);
|
||||
if ((rpacket = packet_create(packet_class_bnet))) {
|
||||
|
||||
if ((rpacket = packet_create(packet_class_bnet)))
|
||||
{
|
||||
packet_set_size(rpacket, sizeof(t_server_authreq1));
|
||||
packet_set_type(rpacket, SERVER_AUTHREQ1);
|
||||
file_to_mod_time(c, versioncheck_get_mpqfile(vc), &rpacket->u.server_authreq1.timestamp);
|
||||
packet_append_string(rpacket, versioncheck_get_mpqfile(vc));
|
||||
packet_append_string(rpacket, versioncheck_get_eqn(vc));
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "[{}] selected \"{}\" \"{}\"", conn_get_socket(c), versioncheck_get_mpqfile(vc), versioncheck_get_eqn(vc));
|
||||
file_to_mod_time(c, vc.get_checkrevision_filename().c_str(), &rpacket->u.server_authreq1.timestamp);
|
||||
packet_append_string(rpacket, vc.get_checkrevision_filename().c_str());
|
||||
packet_append_string(rpacket, vc.get_equation().c_str());
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "[{}] selected \"{}\" \"{}\"", conn_get_socket(c), vc.get_checkrevision_filename(), vc.get_equation());
|
||||
conn_push_outqueue(c, rpacket);
|
||||
packet_del_ref(rpacket);
|
||||
}
|
||||
|
@ -1000,104 +1010,116 @@ namespace pvpgn
|
|||
|
||||
static int _client_authreq1(t_connection * c, t_packet const *const packet)
|
||||
{
|
||||
t_packet *rpacket;
|
||||
|
||||
if (packet_get_size(packet) < sizeof(t_client_authreq1)) {
|
||||
if (packet_get_size(packet) < sizeof(t_client_authreq1))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ1 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_authreq1), packet_get_size(packet));
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool failed = false;
|
||||
|
||||
// The following if statements are sanity checks
|
||||
// The client should have already sent this information in a previous packet and is resending it again in this packet
|
||||
if (bn_int_get(packet->u.client_authreq1.archtag) != conn_get_archtag(c))
|
||||
failed = true;
|
||||
|
||||
if (bn_int_get(packet->u.client_authreq1.clienttag) != conn_get_clienttag(c))
|
||||
failed = true;
|
||||
|
||||
if (bn_int_get(packet->u.client_authreq1.versionid) != conn_get_versionid(c))
|
||||
failed = true;
|
||||
|
||||
|
||||
const char *exeinfo = packet_get_str_const(packet, sizeof(t_client_authreq1), MAX_EXEINFO_STR);
|
||||
if (exeinfo)
|
||||
{
|
||||
char verstr[16];
|
||||
char const *exeinfo;
|
||||
char const *versiontag;
|
||||
int failed;
|
||||
|
||||
failed = 0;
|
||||
if (bn_int_get(packet->u.client_authreq1.archtag) != conn_get_archtag(c))
|
||||
failed = 1;
|
||||
if (bn_int_get(packet->u.client_authreq1.clienttag) != conn_get_clienttag(c))
|
||||
failed = 1;
|
||||
|
||||
if (!(exeinfo = packet_get_str_const(packet, sizeof(t_client_authreq1), MAX_EXEINFO_STR))) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ1 (missing or too long exeinfo)", conn_get_socket(c));
|
||||
exeinfo = "badexe";
|
||||
failed = 1;
|
||||
}
|
||||
conn_set_versionid(c, bn_int_get(packet->u.client_authreq1.versionid));
|
||||
conn_set_checksum(c, bn_int_get(packet->u.client_authreq1.checksum));
|
||||
conn_set_gameversion(c, bn_int_get(packet->u.client_authreq1.gameversion));
|
||||
std::strcpy(verstr, vernum_to_verstr(bn_int_get(packet->u.client_authreq1.gameversion)));
|
||||
conn_set_clientver(c, verstr);
|
||||
conn_set_clientexe(c, exeinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ1 (missing or too long exeinfo)", conn_get_socket(c));
|
||||
conn_set_clientexe(c, "badexe");
|
||||
failed = true;
|
||||
}
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] CLIENT_AUTHREQ1 archtag=0x{:08x} clienttag=0x{:08x} verstr={} exeinfo=\"{}\" versionid=0x{:08x} gameversion=0x{:08x} checksum=0x{:08x}", conn_get_socket(c), bn_int_get(packet->u.client_authreq1.archtag), bn_int_get(packet->u.client_authreq1.clienttag), verstr, exeinfo, conn_get_versionid(c), conn_get_gameversion(c), conn_get_checksum(c));
|
||||
std::string version_string = vernum_to_verstr(bn_int_get(packet->u.client_authreq1.gameversion));
|
||||
conn_set_clientver(c, version_string.c_str());
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] CLIENT_AUTHREQ1 archtag=0x{:08x} clienttag=0x{:08x} verstr={} exeinfo=\"{}\" versionid=0x{:08x} gameversion=0x{:08x} checksum=0x{:08x}", conn_get_socket(c), bn_int_get(packet->u.client_authreq1.archtag), bn_int_get(packet->u.client_authreq1.clienttag), version_string, exeinfo == nullptr ? "badexe" : exeinfo, conn_get_versionid(c), conn_get_gameversion(c), conn_get_checksum(c));
|
||||
|
||||
conn_set_versionid(c, bn_int_get(packet->u.client_authreq1.versionid));
|
||||
conn_set_checksum(c, bn_int_get(packet->u.client_authreq1.checksum));
|
||||
conn_set_gameversion(c, bn_int_get(packet->u.client_authreq1.gameversion));
|
||||
|
||||
|
||||
if ((rpacket = packet_create(packet_class_bnet))) {
|
||||
packet_set_size(rpacket, sizeof(t_server_authreply1));
|
||||
packet_set_type(rpacket, SERVER_AUTHREPLY1);
|
||||
|
||||
|
||||
if (!conn_get_versioncheck(c) && prefs_get_skip_versioncheck())
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] skip versioncheck enabled and client did not request validation", conn_get_socket(c));
|
||||
if (!conn_get_versioncheck(c) && prefs_get_skip_versioncheck())
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] skip versioncheck enabled and client did not request validation", conn_get_socket(c));
|
||||
}
|
||||
else if (prefs_get_allow_unknown_version())
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] skipping versioncheck validation because allow_unknown_version is true", conn_get_socket(c));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (conn_get_versioncheck(c))
|
||||
{
|
||||
if (conn_get_versioncheck(c)->validate_checkrevision_data(bn_int_get(packet->u.client_authreq1.gameversion), bn_int_get(packet->u.client_authreq1.checksum), exeinfo == nullptr ? "badexe" : exeinfo))
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client matches versiontag \"{}\"", conn_get_socket(c), conn_get_versioncheck(c)->get_version_tag());
|
||||
}
|
||||
else
|
||||
switch (versioncheck_validate(conn_get_versioncheck(c), conn_get_archtag(c), conn_get_clienttag(c), exeinfo, conn_get_versionid(c), conn_get_gameversion(c), conn_get_checksum(c))) {
|
||||
case -1: /* failed test... client has been modified */
|
||||
if (!prefs_get_allow_bad_version()) {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client failed test (marking untrusted)", conn_get_socket(c));
|
||||
failed = 1;
|
||||
}
|
||||
else
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client failed test, allowing anyway", conn_get_socket(c));
|
||||
break;
|
||||
case 0: /* not listed in table... can't tell if client has been modified */
|
||||
if (!prefs_get_allow_unknown_version()) {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] unable to test client (marking untrusted)", conn_get_socket(c));
|
||||
failed = 1;
|
||||
}
|
||||
else
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] unable to test client, allowing anyway", conn_get_socket(c));
|
||||
break;
|
||||
/* 1 == test passed... client seems to be ok */
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client failed versioncheck", conn_get_socket(c));
|
||||
failed = true;
|
||||
}
|
||||
|
||||
versiontag = versioncheck_get_versiontag(conn_get_versioncheck(c));
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client matches versiontag \"{}\"", conn_get_socket(c), versiontag);
|
||||
|
||||
if (failed) {
|
||||
conn_set_state(c, conn_state_untrusted);
|
||||
bn_int_set(&rpacket->u.server_authreply1.message, SERVER_AUTHREPLY1_MESSAGE_BADVERSION);
|
||||
packet_append_string(rpacket, "");
|
||||
}
|
||||
else {
|
||||
char *mpqfilename;
|
||||
|
||||
mpqfilename = autoupdate_check(conn_get_archtag(c), conn_get_clienttag(c), conn_get_gamelang(c), versiontag, NULL);
|
||||
|
||||
/* Only handle updates when there is an update file available. */
|
||||
if (mpqfilename != NULL) {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] an upgrade for version {} is available \"{}\"", conn_get_socket(c), versioncheck_get_versiontag(conn_get_versioncheck(c)), mpqfilename);
|
||||
bn_int_set(&rpacket->u.server_authreply1.message, SERVER_AUTHREPLY1_MESSAGE_UPDATE);
|
||||
packet_append_string(rpacket, mpqfilename);
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] no upgrade for {} is available", conn_get_socket(c), versioncheck_get_versiontag(conn_get_versioncheck(c)));
|
||||
bn_int_set(&rpacket->u.server_authreply1.message, SERVER_AUTHREPLY1_MESSAGE_OK);
|
||||
packet_append_string(rpacket, "");
|
||||
}
|
||||
|
||||
if (mpqfilename)
|
||||
xfree((void *)mpqfilename);
|
||||
}
|
||||
|
||||
packet_append_string(rpacket, ""); /* FIXME: what's the second string for? */
|
||||
conn_push_outqueue(c, rpacket);
|
||||
packet_del_ref(rpacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client did not request validation", conn_get_socket(c));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
t_packet *rpacket = packet_create(packet_class_bnet);
|
||||
if (!rpacket)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
packet_set_size(rpacket, sizeof(t_server_authreply1));
|
||||
packet_set_type(rpacket, SERVER_AUTHREPLY1);
|
||||
|
||||
if (failed)
|
||||
{
|
||||
conn_set_state(c, conn_state_untrusted);
|
||||
bn_int_set(&rpacket->u.server_authreply1.message, SERVER_AUTHREPLY1_MESSAGE_BADVERSION);
|
||||
packet_append_string(rpacket, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
char *mpqfilename = autoupdate_check(conn_get_archtag(c), conn_get_clienttag(c), conn_get_gamelang(c), conn_get_versioncheck(c)->get_version_tag().c_str(), nullptr);
|
||||
|
||||
/* Only handle updates when there is an update file available. */
|
||||
if (mpqfilename != nullptr)
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] an upgrade for version {} is available \"{}\"", conn_get_socket(c), conn_get_versioncheck(c)->get_version_tag(), mpqfilename);
|
||||
bn_int_set(&rpacket->u.server_authreply1.message, SERVER_AUTHREPLY1_MESSAGE_UPDATE);
|
||||
packet_append_string(rpacket, mpqfilename);
|
||||
xfree(static_cast<void *>(mpqfilename));
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] no upgrade for {} is available", conn_get_socket(c), conn_get_versioncheck(c)->get_version_tag());
|
||||
bn_int_set(&rpacket->u.server_authreply1.message, SERVER_AUTHREPLY1_MESSAGE_OK);
|
||||
packet_append_string(rpacket, "");
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: what's the second string for?
|
||||
packet_append_string(rpacket, "");
|
||||
conn_push_outqueue(c, rpacket);
|
||||
packet_del_ref(rpacket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1112,10 +1134,8 @@ namespace pvpgn
|
|||
|
||||
{
|
||||
char verstr[16];
|
||||
char const *exeinfo;
|
||||
char const *versiontag;
|
||||
int failed;
|
||||
char const *owner;
|
||||
unsigned int count;
|
||||
unsigned int pos;
|
||||
|
||||
|
@ -1123,19 +1143,23 @@ namespace pvpgn
|
|||
count = bn_int_get(packet->u.client_authreq_109.cdkey_number);
|
||||
pos = sizeof(t_client_authreq_109)+(count * sizeof(t_cdkey_info));
|
||||
|
||||
if (!(exeinfo = packet_get_str_const(packet, pos, MAX_EXEINFO_STR))) {
|
||||
const char * const exeinfo = packet_get_str_const(packet, pos, MAX_EXEINFO_STR);
|
||||
if (!exeinfo)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ_109 (missing or too long exeinfo)", conn_get_socket(c));
|
||||
exeinfo = "badexe";
|
||||
failed = 1;
|
||||
}
|
||||
conn_set_clientexe(c, exeinfo);
|
||||
pos += std::strlen(exeinfo) + 1;
|
||||
conn_set_clientexe(c, exeinfo != nullptr ? exeinfo : "badexe");
|
||||
pos += std::strlen(exeinfo != nullptr ? exeinfo : "badexe") + 1;
|
||||
|
||||
if (!(owner = packet_get_str_const(packet, pos, MAX_OWNER_STR))) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ_109 (missing or too long owner)", conn_get_socket(c));
|
||||
owner = ""; /* maybe owner was missing, use empty string */
|
||||
{
|
||||
const char * const owner = packet_get_str_const(packet, pos, MAX_OWNER_STR);
|
||||
if (!owner) // maybe owner was missing, use empty string
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ_109 (missing or too long owner)", conn_get_socket(c));
|
||||
}
|
||||
conn_set_owner(c, owner != nullptr ? owner : "");
|
||||
}
|
||||
conn_set_owner(c, owner);
|
||||
|
||||
conn_set_checksum(c, bn_int_get(packet->u.client_authreq_109.checksum));
|
||||
conn_set_gameversion(c, bn_int_get(packet->u.client_authreq_109.gameversion));
|
||||
|
@ -1143,63 +1167,67 @@ namespace pvpgn
|
|||
conn_set_clientver(c, verstr);
|
||||
conn_set_clientexe(c, exeinfo);
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] CLIENT_AUTHREQ_109 ticks=0x{:08x}, verstr={} exeinfo=\"{}\" versionid=0x{:08x} gameversion=0x{:08x} checksum=0x{:08x}", conn_get_socket(c), bn_int_get(packet->u.client_authreq_109.ticks), verstr, exeinfo, conn_get_versionid(c), conn_get_gameversion(c), conn_get_checksum(c));
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] CLIENT_AUTHREQ_109 ticks=0x{:08x}, verstr={} exeinfo=\"{}\" versionid=0x{:08x} gameversion=0x{:08x} checksum=0x{:08x}", conn_get_socket(c), bn_int_get(packet->u.client_authreq_109.ticks), verstr, exeinfo != nullptr ? exeinfo : "badexe", conn_get_versionid(c), conn_get_gameversion(c), conn_get_checksum(c));
|
||||
|
||||
if ((rpacket = packet_create(packet_class_bnet))) {
|
||||
if ((rpacket = packet_create(packet_class_bnet)))
|
||||
{
|
||||
packet_set_size(rpacket, sizeof(t_server_authreply_109));
|
||||
packet_set_type(rpacket, SERVER_AUTHREPLY_109);
|
||||
|
||||
|
||||
if (!conn_get_versioncheck(c) && prefs_get_skip_versioncheck())
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] skip versioncheck enabled and client did not request validation", conn_get_socket(c));
|
||||
}
|
||||
else if (prefs_get_allow_unknown_version())
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] skipping versioncheck validation because allow_unknown_version is true", conn_get_socket(c));
|
||||
}
|
||||
else
|
||||
switch (versioncheck_validate(conn_get_versioncheck(c), conn_get_archtag(c), conn_get_clienttag(c), exeinfo, conn_get_versionid(c), conn_get_gameversion(c), conn_get_checksum(c))) {
|
||||
case -1: /* failed test... client has been modified */
|
||||
if (!prefs_get_allow_bad_version()) {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client failed test (closing connection)", conn_get_socket(c));
|
||||
failed = 1;
|
||||
{
|
||||
if (conn_get_versioncheck(c))
|
||||
{
|
||||
if (conn_get_versioncheck(c)->validate_checkrevision_data(bn_int_get(packet->u.client_authreq1.gameversion), bn_int_get(packet->u.client_authreq1.checksum), exeinfo == nullptr ? "badexe" : exeinfo))
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client matches versiontag \"{}\"", conn_get_socket(c), conn_get_versioncheck(c)->get_version_tag());
|
||||
}
|
||||
else
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client failed test, allowing anyway", conn_get_socket(c));
|
||||
break;
|
||||
case 0: /* not listed in table... can't tell if client has been modified */
|
||||
if (!prefs_get_allow_unknown_version()) {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] unable to test client (closing connection)", conn_get_socket(c));
|
||||
failed = 1;
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client failed versioncheck", conn_get_socket(c));
|
||||
failed = true;
|
||||
}
|
||||
else
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] unable to test client, allowing anyway", conn_get_socket(c));
|
||||
break;
|
||||
/* 1 == test passed... client seems to be ok */
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client did not request validation", conn_get_socket(c));
|
||||
}
|
||||
}
|
||||
|
||||
versiontag = versioncheck_get_versiontag(conn_get_versioncheck(c));
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client matches versiontag \"{}\"", conn_get_socket(c), versiontag);
|
||||
|
||||
if (failed) {
|
||||
if (failed)
|
||||
{
|
||||
conn_set_state(c, conn_state_untrusted);
|
||||
bn_int_set(&rpacket->u.server_authreply_109.message, SERVER_AUTHREPLY_109_MESSAGE_BADVERSION);
|
||||
packet_append_string(rpacket, "");
|
||||
}
|
||||
else {
|
||||
char *mpqfilename;
|
||||
|
||||
mpqfilename = autoupdate_check(conn_get_archtag(c), conn_get_clienttag(c), conn_get_gamelang(c), versiontag, NULL);
|
||||
else
|
||||
{
|
||||
char *mpqfilename = autoupdate_check(conn_get_archtag(c), conn_get_clienttag(c), conn_get_gamelang(c), conn_get_versioncheck(c)->get_version_tag().c_str(), NULL);
|
||||
|
||||
/* Only handle updates when there is an update file available. */
|
||||
if (mpqfilename != NULL) {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] an upgrade for {} is available \"{}\"", conn_get_socket(c), versiontag, mpqfilename);
|
||||
if (mpqfilename != nullptr)
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] an upgrade for {} is available \"{}\"", conn_get_socket(c), conn_get_versioncheck(c)->get_version_tag(), mpqfilename);
|
||||
bn_int_set(&rpacket->u.server_authreply_109.message, SERVER_AUTHREPLY_109_MESSAGE_UPDATE);
|
||||
packet_append_string(rpacket, mpqfilename);
|
||||
|
||||
xfree((void *)mpqfilename);
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] no upgrade for {} is available", conn_get_socket(c), versiontag);
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] no upgrade for {} is available", conn_get_socket(c), conn_get_versioncheck(c)->get_version_tag());
|
||||
bn_int_set(&rpacket->u.server_authreply_109.message, SERVER_AUTHREPLY_109_MESSAGE_OK);
|
||||
packet_append_string(rpacket, "");
|
||||
}
|
||||
if (mpqfilename)
|
||||
xfree((void *)mpqfilename);
|
||||
}
|
||||
|
||||
conn_push_outqueue(c, rpacket);
|
||||
|
@ -2427,97 +2455,163 @@ namespace pvpgn
|
|||
|
||||
static int _client_atfriendscreen(t_connection * c, t_packet const *const packet)
|
||||
{
|
||||
char const *myusername;
|
||||
char const *fname;
|
||||
t_connection *dest_c;
|
||||
unsigned char f_cnt = 0;
|
||||
t_account *account;
|
||||
t_packet *rpacket;
|
||||
char const *vt;
|
||||
char const *nvt;
|
||||
t_friend *fr;
|
||||
t_list *flist;
|
||||
t_elem *curr;
|
||||
t_channel *mychannel, *chan;
|
||||
int publicchan = 1;
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "[{}] got CLIENT_ARRANGEDTEAM_FRIENDSCREEN packet", conn_get_socket(c));
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] got CLIENT_ARRANGEDTEAM_FRIENDSCREEN packet", conn_get_socket(c));
|
||||
|
||||
myusername = conn_get_username(c);
|
||||
eventlog(eventlog_level_trace, "handle_bnet", "[{}] AT - Got Username %s", conn_get_socket(c), myusername);
|
||||
|
||||
if (!(rpacket = packet_create(packet_class_bnet))) {
|
||||
eventlog(eventlog_level_error, "handle_bnet", "[{}] AT - can't create friendscreen server packet", conn_get_socket(c));
|
||||
const char *my_username = conn_get_username(c);
|
||||
if (!my_username)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
t_channel *my_channel = conn_get_channel(c);
|
||||
int my_channel_is_public = 1;
|
||||
if (my_channel)
|
||||
{
|
||||
my_channel_is_public = channel_get_flags(my_channel) & channel_flags_public;
|
||||
}
|
||||
|
||||
|
||||
t_packet *rpacket = packet_create(packet_class_bnet);
|
||||
if (!rpacket)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create friendscreen server packet", conn_get_socket(c));
|
||||
return -1;
|
||||
}
|
||||
packet_set_size(rpacket, sizeof(t_server_arrangedteam_friendscreen));
|
||||
packet_set_type(rpacket, SERVER_ARRANGEDTEAM_FRIENDSCREEN);
|
||||
|
||||
|
||||
mychannel = conn_get_channel(c);
|
||||
if ((mychannel))
|
||||
publicchan = channel_get_flags(mychannel) & channel_flags_public;
|
||||
std::uint8_t available_players = 0;
|
||||
|
||||
vt = versioncheck_get_versiontag(conn_get_versioncheck(c));
|
||||
flist = account_get_friends(conn_get_account(c));
|
||||
LIST_TRAVERSE(flist, curr) {
|
||||
if (!(fr = (t_friend*)elem_get_data(curr))) {
|
||||
// begin search for mutual and available friends
|
||||
t_list *my_friend_list = account_get_friends(conn_get_account(c));
|
||||
t_elem *entry;
|
||||
LIST_TRAVERSE(my_friend_list, entry)
|
||||
{
|
||||
t_friend *_friend = static_cast<t_friend *>(elem_get_data(entry));
|
||||
if (!_friend)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list");
|
||||
continue;
|
||||
}
|
||||
|
||||
account = friend_get_account(fr);
|
||||
if (!(dest_c = connlist_find_connection_by_account(account)))
|
||||
continue; // if user is offline, then continue to next friend
|
||||
nvt = versioncheck_get_versiontag(conn_get_versioncheck(dest_c));
|
||||
if (vt && nvt && std::strcmp(vt, nvt))
|
||||
continue; /* friend is using another game/version */
|
||||
if (available_players == std::numeric_limits<decltype(available_players)>::max())
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "Reached maximum amount of available players to send in packet");
|
||||
break;
|
||||
}
|
||||
|
||||
if (friend_get_mutual(fr)) {
|
||||
if (conn_get_dndstr(dest_c))
|
||||
continue; // user is dnd
|
||||
if (conn_get_awaystr(dest_c))
|
||||
continue; // user is away
|
||||
if (conn_get_game(dest_c))
|
||||
continue; // user is some game
|
||||
if (!(chan = conn_get_channel(dest_c)))
|
||||
// skip non-mutual friends
|
||||
if (friend_get_mutual(_friend) == FRIEND_NOTMUTUAL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
t_account *friend_account = friend_get_account(_friend);
|
||||
t_connection *friend_connection = connlist_find_connection_by_account(friend_account);
|
||||
// if user is offline, then continue to next friend
|
||||
if (!friend_connection)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string my_version_tag = conn_get_versioncheck(c) ? conn_get_versioncheck(c)->get_version_tag() : "";
|
||||
const std::string friend_version_tag = conn_get_versioncheck(c) ? conn_get_versioncheck(friend_connection)->get_version_tag() : "";
|
||||
// friend is using another game or is on a different version
|
||||
if (my_version_tag != friend_version_tag)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// friend has Do Not Disturb mode enabled
|
||||
if (conn_get_dndstr(friend_connection))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// friend is away
|
||||
if (conn_get_awaystr(friend_connection))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// friend is not in a channel
|
||||
t_channel *friend_channel = conn_get_channel(friend_connection);
|
||||
if (!friend_channel)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// // don't list YET if in same private channel
|
||||
if (!my_channel_is_public && (friend_channel == my_channel))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *friend_name = account_get_name(friend_account);
|
||||
eventlog(eventlog_level_trace, __FUNCTION__, "Friend {} is available for an AT Game.", friend_name);
|
||||
available_players += 1;
|
||||
packet_append_string(rpacket, friend_name);
|
||||
}
|
||||
|
||||
|
||||
// now list matching users in same private chan
|
||||
if (!my_channel_is_public)
|
||||
{
|
||||
for (t_connection *user_connection = channel_get_first(my_channel); user_connection; user_connection = channel_get_next())
|
||||
{
|
||||
if (available_players == std::numeric_limits<decltype(available_players)>::max())
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "Reached maximum amount of available players to send in packet");
|
||||
break;
|
||||
}
|
||||
|
||||
// skip self
|
||||
if (user_connection == c)
|
||||
{
|
||||
continue;
|
||||
if (!publicchan && (chan == mychannel))
|
||||
continue; // don't list YET if in same private channel
|
||||
}
|
||||
|
||||
fname = account_get_name(account);
|
||||
eventlog(eventlog_level_trace, "handle_bnet", "AT - Friend: {} is available for a AT Game.", fname);
|
||||
f_cnt++;
|
||||
packet_append_string(rpacket, fname);
|
||||
const std::string my_version_tag = conn_get_versioncheck(c) ? conn_get_versioncheck(c)->get_version_tag() : "";
|
||||
const std::string user_version_tag = conn_get_versioncheck(c) ? conn_get_versioncheck(user_connection)->get_version_tag() : "";
|
||||
// user is using another game or is on a different version
|
||||
if (my_version_tag != user_version_tag)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// user is dnd
|
||||
if (conn_get_dndstr(user_connection))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// user is away
|
||||
if (conn_get_awaystr(user_connection))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *username = account_get_name(conn_get_account(user_connection));
|
||||
if (!username)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
eventlog(eventlog_level_trace, __FUNCTION__, "user {} in private channel {} is available for an AT Game.", username, channel_get_name(my_channel));
|
||||
available_players += 1;
|
||||
packet_append_string(rpacket, username);
|
||||
}
|
||||
}
|
||||
|
||||
if (!publicchan) { // now list matching users in same private chan
|
||||
for (dest_c = channel_get_first(mychannel); dest_c; dest_c = channel_get_next()) {
|
||||
if (dest_c == c)
|
||||
continue; // don'tlist yourself
|
||||
nvt = versioncheck_get_versiontag(conn_get_versioncheck(dest_c));
|
||||
if (vt && nvt && std::strcmp(vt, nvt))
|
||||
continue; /* user is using another game/version */
|
||||
if (conn_get_dndstr(dest_c))
|
||||
continue; // user is dnd
|
||||
if (conn_get_awaystr(dest_c))
|
||||
continue; // user is away
|
||||
if (!(conn_get_account(dest_c)))
|
||||
continue;
|
||||
fname = account_get_name(conn_get_account(dest_c));
|
||||
eventlog(eventlog_level_trace, "handle_bnet", "AT - user in private channel: {} is available for a AT Game.", fname);
|
||||
f_cnt++;
|
||||
packet_append_string(rpacket, fname);
|
||||
}
|
||||
if (available_players == 0)
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "No available players");
|
||||
}
|
||||
|
||||
if (!f_cnt)
|
||||
eventlog(eventlog_level_info, "handle_bnet", "AT - no friends available for AT game.");
|
||||
bn_byte_set(&rpacket->u.server_arrangedteam_friendscreen.f_count, f_cnt);
|
||||
bn_byte_set(&rpacket->u.server_arrangedteam_friendscreen.f_count, available_players);
|
||||
|
||||
conn_push_outqueue(c, rpacket);
|
||||
|
||||
packet_del_ref(rpacket);
|
||||
|
||||
return 0;
|
||||
|
@ -3653,12 +3747,12 @@ namespace pvpgn
|
|||
}
|
||||
if (conn_get_versioncheck(cbdata->c) &&
|
||||
conn_get_versioncheck(game_get_owner(game)) &&
|
||||
versioncheck_get_versiontag(conn_get_versioncheck(cbdata->c)) &&
|
||||
versioncheck_get_versiontag(conn_get_versioncheck(game_get_owner(game))) &&
|
||||
std::strcmp(versioncheck_get_versiontag(conn_get_versioncheck(cbdata->c)), versioncheck_get_versiontag(conn_get_versioncheck(game_get_owner(game)))) != 0) {
|
||||
conn_get_versioncheck(cbdata->c)->get_version_tag() != conn_get_versioncheck(game_get_owner(game))->get_version_tag())
|
||||
{
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "[{}] not listing because game is wrong versiontag", conn_get_socket(cbdata->c));
|
||||
return 0;
|
||||
}
|
||||
|
||||
bn_short_set(&glgame.gametype, gtype_to_bngtype(game_get_type(game)));
|
||||
bn_short_set(&glgame.unknown1, SERVER_GAMELISTREPLY_GAME_UNKNOWN1);
|
||||
bn_short_set(&glgame.unknown3, SERVER_GAMELISTREPLY_GAME_UNKNOWN3);
|
||||
|
@ -4716,33 +4810,23 @@ namespace pvpgn
|
|||
|
||||
static int _client_changeclient(t_connection * c, t_packet const *const packet)
|
||||
{
|
||||
t_versioncheck *vc;
|
||||
|
||||
if (packet_get_size(packet) < sizeof(t_client_changeclient)) {
|
||||
if (packet_get_size(packet) < sizeof(t_client_changeclient))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLIENT_CHANGECLIENT packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_changeclient), packet_get_size(packet));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tag_check_in_list(bn_int_get(packet->u.client_changeclient.clienttag), prefs_get_allowed_clients())) {
|
||||
if (conn_get_clienttag(c) != CLIENTTAG_WAR3XP_UINT
|
||||
|| bn_int_get(packet->u.client_changeclient.clienttag) != CLIENTTAG_WARCRAFT3_UINT)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] invalid attempt to change client from {X} to {X}", conn_get_socket(c), conn_get_clienttag(c), bn_int_get(packet->u.client_changeclient.clienttag));
|
||||
conn_set_state(c, conn_state_destroy);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn_set_clienttag(c, bn_int_get(packet->u.client_changeclient.clienttag));
|
||||
|
||||
vc = conn_get_versioncheck(c);
|
||||
versioncheck_set_versiontag(vc, clienttag_uint_to_str(conn_get_clienttag(c)));
|
||||
|
||||
if (vc && versioncheck_get_versiontag(vc)) {
|
||||
switch (versioncheck_validate(vc, conn_get_archtag(c), conn_get_clienttag(c), conn_get_clientexe(c), conn_get_versionid(c), conn_get_gameversion(c), conn_get_checksum(c))) {
|
||||
case -1: /* failed test... client has been modified */
|
||||
case 0: /* not listed in table... can't tell if client has been modified */
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] error revalidating, allowing anyway", conn_get_socket(c));
|
||||
break;
|
||||
}
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client versiontag set to \"{}\"", conn_get_socket(c), versioncheck_get_versiontag(vc));
|
||||
}
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] changed client to {X}", conn_get_socket(c), bn_int_get(packet->u.client_changeclient.clienttag));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -380,8 +380,12 @@ int pre_server_startup(void)
|
|||
|
||||
if (autoupdate_load(prefs_get_mpqfile()) < 0)
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "could not load autoupdate list");
|
||||
if (versioncheck_load(prefs_get_versioncheck_file()) < 0)
|
||||
|
||||
if (!load_versioncheck_conf(prefs_get_versioncheck_file()))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "could not load versioncheck list");
|
||||
}
|
||||
|
||||
if (news_load(prefs_get_newsfile()) < 0)
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "could not load news list");
|
||||
watchlist.reset(new WatchComponent());
|
||||
|
@ -449,7 +453,7 @@ void post_server_shutdown(int status)
|
|||
attrlayer_cleanup();
|
||||
watchlist.reset();
|
||||
news_unload();
|
||||
versioncheck_unload();
|
||||
unload_versioncheck_conf();
|
||||
autoupdate_unload();
|
||||
ipbanlist_save(prefs_get_ipbanfile());
|
||||
ipbanlist_destroy();
|
||||
|
|
|
@ -1415,9 +1415,11 @@ namespace pvpgn
|
|||
|
||||
if (do_restart == restart_mode_all || do_restart == restart_mode_versioncheck)
|
||||
{
|
||||
versioncheck_unload();
|
||||
if (versioncheck_load(prefs_get_versioncheck_file()) < 0)
|
||||
unload_versioncheck_conf();
|
||||
if (!load_versioncheck_conf(prefs_get_versioncheck_file()))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "could not load versioncheck list");
|
||||
}
|
||||
}
|
||||
|
||||
if (do_restart == restart_mode_all || do_restart == restart_mode_ipbans)
|
||||
|
|
|
@ -22,638 +22,393 @@
|
|||
#define VERSIONCHECK_INTERNAL_ACCESS
|
||||
#include "versioncheck.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <forward_list>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "compat/strcasecmp.h"
|
||||
#include "common/list.h"
|
||||
#include "common/xalloc.h"
|
||||
#include "common/format.h"
|
||||
#include "common/eventlog.h"
|
||||
#include "common/util.h"
|
||||
#include "common/field_sizes.h"
|
||||
#include "common/token.h"
|
||||
#include "common/proginfo.h"
|
||||
#include "common/xstring.h"
|
||||
|
||||
#include "json/json.hpp"
|
||||
|
||||
#include "prefs.h"
|
||||
#include "common/setup_after.h"
|
||||
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace bnetd
|
||||
{
|
||||
|
||||
static t_list * versioninfo_head = NULL;
|
||||
static t_versioncheck dummyvc = { "A=42 B=42 C=42 4 A=A^S B=B^B C=C^C A=A^S", "IX86ver1.mpq", "NoVC" };
|
||||
std::forward_list<VersionCheck> vc_entries = {};
|
||||
|
||||
static int versioncheck_compare_exeinfo(t_parsed_exeinfo * pattern, t_parsed_exeinfo * match);
|
||||
bool versioncheck_conf_is_loaded = false;
|
||||
|
||||
extern t_versioncheck * versioncheck_create(t_tag archtag, t_tag clienttag)
|
||||
struct file_metadata parse_file_metadata(const std::string& unparsed_metadata);
|
||||
bool compare_file_metadata(const struct file_metadata& pattern, const struct file_metadata& match, bool skip_timestamp_match);
|
||||
|
||||
|
||||
bool load_versioncheck_conf(const std::string& filename)
|
||||
{
|
||||
t_elem const * curr;
|
||||
t_versioninfo * vi;
|
||||
t_versioncheck * vc;
|
||||
char archtag_str[5];
|
||||
char clienttag_str[5];
|
||||
|
||||
LIST_TRAVERSE_CONST(versioninfo_head, curr)
|
||||
if (versioncheck_conf_is_loaded)
|
||||
{
|
||||
if (!(vi = (t_versioninfo*)elem_get_data(curr))) /* should not happen */
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "version list contains NULL item");
|
||||
continue;
|
||||
}
|
||||
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "version check entry archtag={}, clienttag={}",
|
||||
tag_uint_to_str(archtag_str, vi->archtag),
|
||||
tag_uint_to_str(clienttag_str, vi->clienttag));
|
||||
|
||||
if (vi->archtag != archtag)
|
||||
continue;
|
||||
if (vi->clienttag != clienttag)
|
||||
continue;
|
||||
|
||||
/* FIXME: randomize the selection if more than one match */
|
||||
vc = (t_versioncheck*)xmalloc(sizeof(t_versioncheck));
|
||||
vc->eqn = xstrdup(vi->eqn);
|
||||
vc->mpqfile = xstrdup(vi->mpqfile);
|
||||
vc->versiontag = xstrdup(tag_uint_to_str(clienttag_str, clienttag));
|
||||
|
||||
return vc;
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "Could not load {}, a versioncheck configuration file is already loaded", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* No entries in the file that match, return the dummy because we have to send
|
||||
* some equation and auth mpq to the client. The client is not going to pass the
|
||||
* validation later unless skip_versioncheck or allow_unknown_version is enabled.
|
||||
*/
|
||||
return &dummyvc;
|
||||
}
|
||||
|
||||
|
||||
extern int versioncheck_destroy(t_versioncheck * vc)
|
||||
{
|
||||
if (!vc)
|
||||
std::ifstream file_stream(filename, std::ios::in);
|
||||
if (!file_stream.is_open())
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL vc");
|
||||
return -1;
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "Could not open file \"{}\" for reading", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vc == &dummyvc)
|
||||
return 0;
|
||||
|
||||
xfree((void *)vc->versiontag);
|
||||
xfree((void *)vc->mpqfile);
|
||||
xfree((void *)vc->eqn);
|
||||
xfree(vc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int versioncheck_set_versiontag(t_versioncheck * vc, char const * versiontag)
|
||||
{
|
||||
if (!vc) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL vc");
|
||||
return -1;
|
||||
}
|
||||
if (!versiontag) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL versiontag");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vc->versiontag != NULL) xfree((void *)vc->versiontag);
|
||||
vc->versiontag = xstrdup(versiontag);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
extern char const * versioncheck_get_versiontag(t_versioncheck const * vc)
|
||||
{
|
||||
if (!vc) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL vc");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return vc->versiontag;
|
||||
}
|
||||
|
||||
|
||||
extern char const * versioncheck_get_mpqfile(t_versioncheck const * vc)
|
||||
{
|
||||
if (!vc)
|
||||
json j;
|
||||
try
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL vc");
|
||||
return NULL;
|
||||
file_stream >> j;
|
||||
}
|
||||
|
||||
return vc->mpqfile;
|
||||
}
|
||||
|
||||
|
||||
extern char const * versioncheck_get_eqn(t_versioncheck const * vc)
|
||||
{
|
||||
if (!vc)
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL vc");
|
||||
return NULL;
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "Could not parse JSON data: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return vc->eqn;
|
||||
}
|
||||
|
||||
t_parsed_exeinfo * parse_exeinfo(char const * exeinfo)
|
||||
{
|
||||
t_parsed_exeinfo * parsed_exeinfo;
|
||||
|
||||
if (!exeinfo) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parsed_exeinfo = (t_parsed_exeinfo*)xmalloc(sizeof(t_parsed_exeinfo));
|
||||
parsed_exeinfo->exe = xstrdup(exeinfo);
|
||||
parsed_exeinfo->time = 0;
|
||||
parsed_exeinfo->size = 0;
|
||||
|
||||
if (std::strcmp(prefs_get_version_exeinfo_match(), "parse") == 0)
|
||||
|
||||
int success_count = 0;
|
||||
int total_count = 0;
|
||||
for (auto jclient : json::iterator_wrapper(j))
|
||||
{
|
||||
struct std::tm t1;
|
||||
char *exe;
|
||||
char mask[MAX_EXEINFO_STR + 1];
|
||||
char * marker;
|
||||
unsigned size;
|
||||
char time_invalid = 0;
|
||||
|
||||
if ((exeinfo[0] == '\0') || //happens when using war3-noCD and having deleted war3.org
|
||||
(std::strcmp(exeinfo, "badexe") == 0)) //happens when AUTHREQ had no owner/exeinfo entry
|
||||
total_count += jclient.value().size();
|
||||
for (const auto& entry : jclient.value())
|
||||
{
|
||||
xfree((void *)parsed_exeinfo->exe);
|
||||
xfree((void *)parsed_exeinfo);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "found empty exeinfo");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::memset(&t1, 0, sizeof(t1));
|
||||
t1.tm_isdst = -1;
|
||||
|
||||
exeinfo = strreverse((char *)exeinfo);
|
||||
if (!(marker = std::strchr((char *)exeinfo, ' ')))
|
||||
{
|
||||
xfree((void *)parsed_exeinfo->exe);
|
||||
xfree((void *)parsed_exeinfo);
|
||||
return NULL;
|
||||
}
|
||||
for (; marker[0] == ' '; marker++);
|
||||
|
||||
if (!(marker = std::strchr(marker, ' ')))
|
||||
{
|
||||
xfree((void *)parsed_exeinfo->exe);
|
||||
xfree((void *)parsed_exeinfo);
|
||||
return NULL;
|
||||
}
|
||||
for (; marker[0] == ' '; marker++);
|
||||
|
||||
if (!(marker = std::strchr(marker, ' ')))
|
||||
{
|
||||
xfree((void *)parsed_exeinfo->exe);
|
||||
xfree((void *)parsed_exeinfo);
|
||||
return NULL;
|
||||
}
|
||||
for (; marker[0] == ' '; marker++);
|
||||
marker--;
|
||||
marker[0] = '\0';
|
||||
marker++;
|
||||
|
||||
exe = xstrdup(marker);
|
||||
xfree((void *)parsed_exeinfo->exe);
|
||||
parsed_exeinfo->exe = strreverse((char *)exe);
|
||||
|
||||
exeinfo = strreverse((char *)exeinfo);
|
||||
|
||||
std::sprintf(mask, "%%02u/%%02u/%%u %%02u:%%02u:%%02u %%u");
|
||||
|
||||
if (std::sscanf(exeinfo, mask, &t1.tm_mon, &t1.tm_mday, &t1.tm_year, &t1.tm_hour, &t1.tm_min, &t1.tm_sec, &size) != 7) {
|
||||
if (std::sscanf(exeinfo, "%*s %*s %u", &size) != 1)
|
||||
try
|
||||
{
|
||||
unsigned long versionid = std::strtoul(entry["versionbyte"].get<std::string>().c_str(), nullptr, 0);
|
||||
if (versionid == ULONG_MAX || versionid == 0)
|
||||
{
|
||||
throw std::runtime_error("Invalid versionbyte \"" + entry["versionbyte"].get<std::string>() + "\" in entry \"" + entry["title"].get<std::string>() + "\"");
|
||||
}
|
||||
|
||||
eventlog(eventlog_level_warn, __FUNCTION__, "parser error while parsing pattern \"{}\"", exeinfo);
|
||||
xfree((void *)parsed_exeinfo->exe);
|
||||
xfree((void *)parsed_exeinfo);
|
||||
return NULL; /* neq */
|
||||
unsigned long game_version;
|
||||
if (verstr_to_vernum(entry["version"].get<std::string>().c_str(), &game_version) < 0)
|
||||
{
|
||||
throw std::runtime_error("Invalid version \"" + entry["version"].get<std::string>() + "\" in entry \"" + entry["title"].get<std::string>() + "\"");
|
||||
}
|
||||
|
||||
unsigned long checksum = std::strtoul(entry["hash"].get<std::string>().c_str(), nullptr, 0);
|
||||
if (checksum == ULONG_MAX || checksum == 0)
|
||||
{
|
||||
throw std::runtime_error("Invalid hash \"" + entry["hash"].get<std::string>() + "\" in entry \"" + entry["title"].get<std::string>() + "\"");
|
||||
}
|
||||
|
||||
t_tag architecture = tag_str_to_uint(entry["architecture"].get<std::string>().c_str());
|
||||
if (!tag_check_arch(architecture))
|
||||
{
|
||||
throw std::runtime_error("Invalid architecture \"" + entry["architecture"].get<std::string>() + "\" in entry \"" + entry["title"].get<std::string>() + "\"");
|
||||
}
|
||||
|
||||
t_tag client = tag_str_to_uint(jclient.key().c_str());
|
||||
if (!tag_check_client(client))
|
||||
{
|
||||
throw std::runtime_error("Invalid client \"" + entry["client"].get<std::string>() + "\" in entry \"" + entry["title"].get<std::string>() + "\"");
|
||||
}
|
||||
|
||||
struct file_metadata metadata;
|
||||
if (entry["file_metadata"].get<std::string>() == "NULL")
|
||||
{
|
||||
metadata = {};
|
||||
}
|
||||
else
|
||||
{
|
||||
metadata = parse_file_metadata(entry["file_metadata"].get<std::string>());
|
||||
if (metadata.filename.empty()
|
||||
&& metadata.file_size == 0
|
||||
&& metadata.timestamp == 0)
|
||||
{
|
||||
throw std::runtime_error("Invalid file_metadata \"" + entry["file_metadata"].get<std::string>() + "\" in entry \"" + entry["title"].get<std::string>() + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
// unable to check for errors at this time
|
||||
std::string equation = entry["equation"].get<std::string>();
|
||||
|
||||
std::string checkrevision_filename = entry["checkrevision_file"].get<std::string>();
|
||||
|
||||
std::string version_tag = entry["versiontag"].get<std::string>();
|
||||
|
||||
vc_entries.push_front(VersionCheck(versionid, game_version, checksum, architecture, client,
|
||||
metadata, equation, checkrevision_filename, version_tag));
|
||||
|
||||
success_count += 1;
|
||||
}
|
||||
time_invalid = 1;
|
||||
}
|
||||
|
||||
/* Now we have a Y2K problem :) Thanks for using a 2 digit decimal years, Blizzard. */
|
||||
/* 00-79 -> 2000-2079
|
||||
* * 80-99 -> 1980-1999
|
||||
* * 100+ unchanged */
|
||||
if (t1.tm_year < 80)
|
||||
t1.tm_year = t1.tm_year + 100;
|
||||
|
||||
if (time_invalid)
|
||||
parsed_exeinfo->time = static_cast<std::time_t>(-1);
|
||||
else
|
||||
parsed_exeinfo->time = std::mktime(&t1);
|
||||
parsed_exeinfo->size = size;
|
||||
}
|
||||
|
||||
return parsed_exeinfo;
|
||||
}
|
||||
|
||||
|
||||
/* This implements some dumb kind of pattern matching. Any '?'
|
||||
* signs in the pattern are treated as "don't care" signs. This
|
||||
* means that it doesn't matter what's on this place in the match.
|
||||
*/
|
||||
//static int versioncheck_compare_exeinfo(char const * pattern, char const * match)
|
||||
static int versioncheck_compare_exeinfo(t_parsed_exeinfo * pattern, t_parsed_exeinfo * match)
|
||||
{
|
||||
assert(pattern);
|
||||
assert(match);
|
||||
|
||||
if (!strcasecmp(prefs_get_version_exeinfo_match(), "none"))
|
||||
return 0; /* ignore exeinfo */
|
||||
|
||||
if (std::strlen(pattern->exe) != std::strlen(match->exe))
|
||||
return 1; /* neq */
|
||||
|
||||
if (std::strcmp(prefs_get_version_exeinfo_match(), "exact") == 0) {
|
||||
return strcasecmp(pattern->exe, match->exe);
|
||||
}
|
||||
else if (std::strcmp(prefs_get_version_exeinfo_match(), "exactcase") == 0) {
|
||||
return std::strcmp(pattern->exe, match->exe);
|
||||
}
|
||||
else if (std::strcmp(prefs_get_version_exeinfo_match(), "wildcard") == 0) {
|
||||
unsigned int i;
|
||||
size_t pattern_exelen = std::strlen(pattern->exe);
|
||||
for (i = 0; i < pattern_exelen; i++)
|
||||
if ((pattern->exe[i] != '?') && /* out "don't care" sign */
|
||||
(safe_toupper(pattern->exe[i]) != safe_toupper(match->exe[i])))
|
||||
return 1; /* neq */
|
||||
return 0; /* ok */
|
||||
}
|
||||
else if (std::strcmp(prefs_get_version_exeinfo_match(), "parse") == 0) {
|
||||
|
||||
if (strcasecmp(pattern->exe, match->exe) != 0)
|
||||
{
|
||||
eventlog(eventlog_level_trace, __FUNCTION__, "filename differs");
|
||||
return 1; /* neq */
|
||||
}
|
||||
if (pattern->size != match->size)
|
||||
{
|
||||
eventlog(eventlog_level_trace, __FUNCTION__, "size differs");
|
||||
return 1; /* neq */
|
||||
}
|
||||
if ((pattern->time != -1) && prefs_get_version_exeinfo_maxdiff() && (abs(pattern->time - match->time) > (signed)prefs_get_version_exeinfo_maxdiff()))
|
||||
{
|
||||
eventlog(eventlog_level_trace, __FUNCTION__, "time differs by {}", abs(pattern->time - match->time));
|
||||
return 1;
|
||||
}
|
||||
return 0; /* ok */
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "unknown version exeinfo match method \"{}\"", prefs_get_version_exeinfo_match());
|
||||
return -1; /* neq/fail */
|
||||
}
|
||||
}
|
||||
|
||||
void free_parsed_exeinfo(t_parsed_exeinfo * parsed_exeinfo)
|
||||
{
|
||||
if (parsed_exeinfo)
|
||||
{
|
||||
if (parsed_exeinfo->exe)
|
||||
xfree((void *)parsed_exeinfo->exe);
|
||||
xfree((void *)parsed_exeinfo);
|
||||
}
|
||||
}
|
||||
|
||||
extern int versioncheck_validate(t_versioncheck * vc, t_tag archtag, t_tag clienttag, char const * exeinfo, unsigned long versionid, unsigned long gameversion, unsigned long checksum)
|
||||
{
|
||||
t_elem const * curr;
|
||||
t_versioninfo * vi;
|
||||
int badexe, badcs;
|
||||
t_parsed_exeinfo * parsed_exeinfo;
|
||||
|
||||
if (!vc)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL vc");
|
||||
return -1;
|
||||
}
|
||||
|
||||
badexe = badcs = 0;
|
||||
parsed_exeinfo = parse_exeinfo(exeinfo);
|
||||
LIST_TRAVERSE_CONST(versioninfo_head, curr)
|
||||
{
|
||||
if (!(vi = (t_versioninfo*)elem_get_data(curr))) /* should not happen */
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "version list contains NULL item");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vi->archtag != archtag)
|
||||
continue;
|
||||
if (vi->clienttag != clienttag)
|
||||
continue;
|
||||
if (std::strcmp(vi->eqn, vc->eqn) != 0)
|
||||
continue;
|
||||
if (std::strcmp(vi->mpqfile, vc->mpqfile) != 0)
|
||||
continue;
|
||||
|
||||
if (vi->versionid && vi->versionid != versionid)
|
||||
continue;
|
||||
|
||||
if (vi->gameversion && vi->gameversion != gameversion)
|
||||
continue;
|
||||
|
||||
|
||||
if ((!(parsed_exeinfo)) || (vi->parsed_exeinfo && (versioncheck_compare_exeinfo(vi->parsed_exeinfo, parsed_exeinfo) != 0)))
|
||||
{
|
||||
/*
|
||||
* Found an entry matching but the exeinfo doesn't match.
|
||||
* We need to rember this because if no other matching versions are found
|
||||
* we will return badversion.
|
||||
*/
|
||||
badexe = 1;
|
||||
}
|
||||
else
|
||||
badexe = 0;
|
||||
|
||||
if (vi->checksum && vi->checksum != checksum)
|
||||
{
|
||||
/*
|
||||
* Found an entry matching but the checksum doesn't match.
|
||||
* We need to rember this because if no other matching versions are found
|
||||
* we will return badversion.
|
||||
*/
|
||||
badcs = 1;
|
||||
}
|
||||
else
|
||||
badcs = 0;
|
||||
|
||||
if (vc->versiontag)
|
||||
xfree((void *)vc->versiontag);
|
||||
vc->versiontag = xstrdup(vi->versiontag);
|
||||
|
||||
if (badexe || badcs)
|
||||
continue;
|
||||
|
||||
/* Ok, version and checksum matches or exeinfo/checksum are disabled
|
||||
* anyway we have found a complete match */
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "got a matching entry: {}", vc->versiontag);
|
||||
free_parsed_exeinfo(parsed_exeinfo);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (badcs) /* A match was found but the checksum was different */
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "bad checksum, closest match is: {}", vc->versiontag);
|
||||
free_parsed_exeinfo(parsed_exeinfo);
|
||||
return -1;
|
||||
}
|
||||
if (badexe) /* A match was found but the exeinfo string was different */
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "bad exeinfo, closest match is: {}", vc->versiontag);
|
||||
free_parsed_exeinfo(parsed_exeinfo);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* No match in list */
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "no match in list, setting to: {}", vc->versiontag);
|
||||
free_parsed_exeinfo(parsed_exeinfo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int versioncheck_load(char const * filename)
|
||||
{
|
||||
std::FILE * fp;
|
||||
unsigned int line;
|
||||
unsigned int pos;
|
||||
char * buff;
|
||||
char * temp;
|
||||
char const * eqn;
|
||||
char const * mpqfile;
|
||||
char const * archtag;
|
||||
char const * clienttag;
|
||||
char const * exeinfo;
|
||||
char const * versionid;
|
||||
char const * gameversion;
|
||||
char const * checksum;
|
||||
char const * versiontag;
|
||||
t_versioninfo * vi;
|
||||
|
||||
if (!filename)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(versioninfo_head = list_create()))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "could create list");
|
||||
return -1;
|
||||
}
|
||||
if (!(fp = std::fopen(filename, "r")))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno));
|
||||
list_destroy(versioninfo_head);
|
||||
versioninfo_head = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
line = 1;
|
||||
for (; (buff = file_get_line(fp)); line++)
|
||||
{
|
||||
for (pos = 0; buff[pos] == '\t' || buff[pos] == ' '; pos++);
|
||||
if (buff[pos] == '\0' || buff[pos] == '#')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ((temp = std::strrchr(buff, '#')))
|
||||
{
|
||||
unsigned int len;
|
||||
unsigned int endpos;
|
||||
|
||||
*temp = '\0';
|
||||
len = std::strlen(buff) + 1;
|
||||
for (endpos = len - 1; buff[endpos] == '\t' || buff[endpos] == ' '; endpos--);
|
||||
buff[endpos + 1] = '\0';
|
||||
}
|
||||
|
||||
if (!(eqn = next_token(buff, &pos)))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "missing eqn near line {} of file \"{}\"", line, filename);
|
||||
continue;
|
||||
}
|
||||
line++;
|
||||
if (!(mpqfile = next_token(buff, &pos)))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "missing mpqfile near line {} of file \"{}\"", line, filename);
|
||||
continue;
|
||||
}
|
||||
line++;
|
||||
if (!(archtag = next_token(buff, &pos)))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "missing archtag near line {} of file \"{}\"", line, filename);
|
||||
continue;
|
||||
}
|
||||
line++;
|
||||
if (!(clienttag = next_token(buff, &pos)))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "missing clienttag near line {} of file \"{}\"", line, filename);
|
||||
continue;
|
||||
}
|
||||
line++;
|
||||
if (!(exeinfo = next_token(buff, &pos)))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "missing exeinfo near line {} of file \"{}\"", line, filename);
|
||||
continue;
|
||||
}
|
||||
line++;
|
||||
if (!(versionid = next_token(buff, &pos)))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "missing versionid near line {} of file \"{}\"", line, filename);
|
||||
continue;
|
||||
}
|
||||
line++;
|
||||
if (!(gameversion = next_token(buff, &pos)))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "missing gameversion near line {} of file \"{}\"", line, filename);
|
||||
continue;
|
||||
}
|
||||
line++;
|
||||
if (!(checksum = next_token(buff, &pos)))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "missing checksum near line {} of file \"{}\"", line, filename);
|
||||
continue;
|
||||
}
|
||||
line++;
|
||||
if (!(versiontag = next_token(buff, &pos)))
|
||||
{
|
||||
versiontag = NULL;
|
||||
}
|
||||
|
||||
vi = (t_versioninfo*)xmalloc(sizeof(t_versioninfo));
|
||||
vi->eqn = xstrdup(eqn);
|
||||
vi->mpqfile = xstrdup(mpqfile);
|
||||
if (std::strlen(archtag) != 4)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "invalid arch tag on line {} of file \"{}\"", line, filename);
|
||||
xfree((void *)vi->mpqfile); /* avoid warning */
|
||||
xfree((void *)vi->eqn); /* avoid warning */
|
||||
xfree(vi);
|
||||
continue;
|
||||
}
|
||||
if (!tag_check_arch((vi->archtag = tag_str_to_uint(archtag))))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got unknown archtag \"{}\"", archtag);
|
||||
xfree((void *)vi->mpqfile); /* avoid warning */
|
||||
xfree((void *)vi->eqn); /* avoid warning */
|
||||
xfree(vi);
|
||||
continue;
|
||||
}
|
||||
if (std::strlen(clienttag) != 4)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "invalid client tag on line {} of file \"{}\"", line, filename);
|
||||
xfree((void *)vi->mpqfile); /* avoid warning */
|
||||
xfree((void *)vi->eqn); /* avoid warning */
|
||||
xfree(vi);
|
||||
continue;
|
||||
}
|
||||
if (!tag_check_client((vi->clienttag = tag_str_to_uint(clienttag))))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got unknown clienttag\"{}\"", clienttag);
|
||||
xfree((void *)vi->mpqfile); /* avoid warning */
|
||||
xfree((void *)vi->eqn); /* avoid warning */
|
||||
xfree(vi);
|
||||
continue;
|
||||
}
|
||||
if (std::strcmp(exeinfo, "NULL") == 0)
|
||||
vi->parsed_exeinfo = NULL;
|
||||
else
|
||||
{
|
||||
if (!(vi->parsed_exeinfo = parse_exeinfo(exeinfo)))
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "encountered an error while parsing exeinfo");
|
||||
xfree((void *)vi->mpqfile); /* avoid warning */
|
||||
xfree((void *)vi->eqn); /* avoid warning */
|
||||
xfree(vi);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
vi->versionid = std::strtoul(versionid, NULL, 0);
|
||||
if (verstr_to_vernum(gameversion, &vi->gameversion) < 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "malformed version on line {} of file \"{}\"", line, filename);
|
||||
xfree((void *)vi->parsed_exeinfo); /* avoid warning */
|
||||
xfree((void *)vi->mpqfile); /* avoid warning */
|
||||
xfree((void *)vi->eqn); /* avoid warning */
|
||||
xfree(vi);
|
||||
continue;
|
||||
}
|
||||
|
||||
vi->checksum = std::strtoul(checksum, NULL, 0);
|
||||
if (versiontag)
|
||||
vi->versiontag = xstrdup(versiontag);
|
||||
else
|
||||
vi->versiontag = NULL;
|
||||
|
||||
|
||||
list_append_data(versioninfo_head, vi);
|
||||
}
|
||||
|
||||
file_get_line(NULL); // clear file_get_line buffer
|
||||
if (std::fclose(fp) < 0)
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "could not close versioncheck file \"{}\" after reading (std::fclose: {})", filename, std::strerror(errno));
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "Successfully loaded {} out of {} versioncheck entries", success_count, total_count);
|
||||
|
||||
return 0;
|
||||
versioncheck_conf_is_loaded = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
extern int versioncheck_unload(void)
|
||||
void unload_versioncheck_conf()
|
||||
{
|
||||
t_elem * curr;
|
||||
t_versioninfo * vi;
|
||||
|
||||
if (versioninfo_head)
|
||||
if (versioncheck_conf_is_loaded)
|
||||
{
|
||||
LIST_TRAVERSE(versioninfo_head, curr)
|
||||
vc_entries.clear();
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "Successfully unloaded all version check entries");
|
||||
|
||||
versioncheck_conf_is_loaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
const VersionCheck& select_versioncheck_entry(t_tag architecture, t_tag client, std::uint32_t version_id)
|
||||
{
|
||||
static const VersionCheck invalid_entry("IX86ver1.mpq", "A=42 B=42 C=42 4 A=A^S B=B^B C=C^C A=A^S", "NoVC");
|
||||
|
||||
for (const auto& entry : vc_entries)
|
||||
{
|
||||
if (entry.m_architecture == architecture
|
||||
|| entry.m_client == client
|
||||
|| entry.m_version_id == version_id)
|
||||
{
|
||||
if (!(vi = (t_versioninfo*)elem_get_data(curr))) /* should not happen */
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "version list contains NULL item");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (list_remove_elem(versioninfo_head, &curr) < 0)
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "could not remove item from list");
|
||||
|
||||
if (vi->parsed_exeinfo)
|
||||
{
|
||||
if (vi->parsed_exeinfo->exe)
|
||||
xfree((void *)vi->parsed_exeinfo->exe);
|
||||
xfree((void *)vi->parsed_exeinfo); /* avoid warning */
|
||||
}
|
||||
xfree((void *)vi->mpqfile); /* avoid warning */
|
||||
xfree((void *)vi->eqn); /* avoid warning */
|
||||
if (vi->versiontag)
|
||||
xfree((void *)vi->versiontag); /* avoid warning */
|
||||
xfree(vi);
|
||||
return entry;
|
||||
}
|
||||
|
||||
if (list_destroy(versioninfo_head) < 0)
|
||||
return -1;
|
||||
versioninfo_head = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return invalid_entry;
|
||||
}
|
||||
|
||||
VersionCheck::VersionCheck(std::uint32_t version_id, std::uint32_t game_version, std::uint32_t checksum, t_tag architecture,
|
||||
t_tag client, const struct file_metadata& metadata, const std::string& equation,
|
||||
const std::string& checkrevision_filename, const std::string& version_tag)
|
||||
: m_version_id(version_id),
|
||||
m_game_version(game_version),
|
||||
m_checksum(checksum),
|
||||
m_architecture(architecture),
|
||||
m_client(client),
|
||||
m_metadata(metadata),
|
||||
m_equation(equation),
|
||||
m_checkrevision_filename(checkrevision_filename),
|
||||
m_version_tag(version_tag)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
VersionCheck::VersionCheck(const std::string& checkrevision_filename, const std::string& equation, const std::string& version_tag)
|
||||
: m_version_id(),
|
||||
m_game_version(),
|
||||
m_checksum(),
|
||||
m_architecture(),
|
||||
m_client(),
|
||||
m_metadata(),
|
||||
m_equation(equation),
|
||||
m_checkrevision_filename(checkrevision_filename),
|
||||
m_version_tag(version_tag)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool VersionCheck::validate_checkrevision_data(std::uint32_t game_version, std::uint32_t checksum, const std::string& unparsed_file_metadata) const
|
||||
{
|
||||
if (this->m_version_tag == "NoVC")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->m_game_version != game_version)
|
||||
{
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "Failed CheckRevision: Invalid game version \"{X}\"", game_version);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (this->m_checksum != 0
|
||||
&& prefs_get_allow_bad_version())
|
||||
{
|
||||
if (this->m_checksum != checksum)
|
||||
{
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "Failed CheckRevision: Invalid checksum \"{X}\"", checksum);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// checksum can be disabled globally via setting allow_bad_version = true in conf/bnetd.conf
|
||||
// it can also be disabled on individual versioncheck entries by setting 0 in the checksum field in conf/versioncheck.conf
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "Skipping checksum validation");
|
||||
}
|
||||
|
||||
|
||||
if (unparsed_file_metadata != "NULL"
|
||||
&& std::strcmp(prefs_get_version_exeinfo_match(), "true") == 0)
|
||||
{
|
||||
if (unparsed_file_metadata == "badexe")
|
||||
{
|
||||
// missing or too long file metadata received
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "Failed CheckRevision: Invalid file metadata \"{}\"", unparsed_file_metadata);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct file_metadata parsed_metadata = parse_file_metadata(unparsed_file_metadata);;
|
||||
if (parsed_metadata.filename.empty()
|
||||
&& parsed_metadata.file_size == 0
|
||||
&& parsed_metadata.timestamp == 0)
|
||||
{
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "Failed CheckRevision: Invalid file metadata \"{}\"", unparsed_file_metadata);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip timestamp matching if client is WC3
|
||||
// WC3 installers change the file timestamp to time of installation
|
||||
if (!compare_file_metadata(this->m_metadata, parsed_metadata,
|
||||
this->m_client == CLIENTTAG_WARCRAFT3_UINT || this->m_client == CLIENTTAG_WAR3XP_UINT ? true : false)
|
||||
)
|
||||
{
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "Failed CheckRevision: Invalid file metadata \"{}\"", unparsed_file_metadata);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// metadata matching can be disabled globally via setting version_exeinfo_match = false in conf/bnetd.conf
|
||||
// it can also be disabled on individual versioncheck entries by setting "NULL" in the exeinfo field in conf/versioncheck.conf
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "Skipping file metadata validation");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string VersionCheck::get_equation() const noexcept
|
||||
{
|
||||
return this->m_equation;
|
||||
}
|
||||
|
||||
std::string VersionCheck::get_checkrevision_filename() const noexcept
|
||||
{
|
||||
return this->m_checkrevision_filename;
|
||||
}
|
||||
|
||||
std::string VersionCheck::get_version_tag() const noexcept
|
||||
{
|
||||
return this->m_version_tag;
|
||||
}
|
||||
|
||||
struct file_metadata parse_file_metadata(const std::string& unparsed_metadata)
|
||||
{
|
||||
// happens when using war3-noCD and having deleted war3.org
|
||||
if (unparsed_metadata.empty())
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got empty file metadata string");
|
||||
return {};
|
||||
}
|
||||
|
||||
// happens when AUTHREQ had no owner/exeinfo entry
|
||||
if (unparsed_metadata == "badexe")
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got \"badexe\" as file metadata string");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::smatch tokens;
|
||||
try
|
||||
{
|
||||
if (std::regex_match(unparsed_metadata, tokens, std::regex{ R"(([[:print:]]+\.exe) (\d\d/\d\d/\d\d \d\d:\d\d:\d\d) (\d+))" }) == false)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got invalid file metadata string \"{}\"", unparsed_metadata);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
catch (const std::regex_error& e)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "{} (code {})", e.what(), e.code());
|
||||
return {};
|
||||
}
|
||||
|
||||
// Example metadata string
|
||||
// "Warcraft III.exe 07/07/17 20:15:59 562152"
|
||||
|
||||
std::string filename = tokens[1];
|
||||
|
||||
std::tm timestamp_raw;
|
||||
timestamp_raw.tm_isdst = -1;
|
||||
std::istringstream ss(tokens[2]);
|
||||
// Since year is 2 digits,
|
||||
// Range [69,99] results in values 1969 to 1999, range [00,68] results in 2000-2068
|
||||
ss >> std::get_time(×tamp_raw, "%D %T");
|
||||
if (ss.fail())
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got invalid date and time in file metadata string");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::time_t timestamp = std::mktime(×tamp_raw);
|
||||
if (timestamp == -1)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "time could not be represented as a std::time_t object");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::uint64_t filesize;
|
||||
try
|
||||
{
|
||||
filesize = std::stoull(tokens[3]);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "could not convert file size in file metadata string: {}", e.what());
|
||||
return {};
|
||||
}
|
||||
|
||||
return { timestamp, filesize, filename };
|
||||
}
|
||||
|
||||
bool compare_file_metadata(const struct file_metadata& pattern, const struct file_metadata& match, bool skip_timestamp_match)
|
||||
{
|
||||
if (skip_timestamp_match == false
|
||||
&& pattern.timestamp != match.timestamp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pattern.file_size != match.file_size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Case insensitive comparison of filename
|
||||
if (strcasecmp(pattern.filename.c_str(), match.filename.c_str()) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,84 +18,78 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef INCLUDED_VERSIONCHECK_TYPES
|
||||
#define INCLUDED_VERSIONCHECK_TYPES
|
||||
#ifndef PVPGN_BNETD_VERSIONCHECK_H
|
||||
#define PVPGN_BNETD_VERSIONCHECK_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
#include "common/tag.h"
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace bnetd
|
||||
{
|
||||
|
||||
#ifdef VERSIONCHECK_INTERNAL_ACCESS
|
||||
typedef struct
|
||||
{
|
||||
char const * exe;
|
||||
std::time_t time;
|
||||
int size;
|
||||
} t_parsed_exeinfo;
|
||||
#endif
|
||||
|
||||
#ifdef VERSIONCHECK_INTERNAL_ACCESS
|
||||
typedef struct
|
||||
{
|
||||
char const * eqn;
|
||||
char const * mpqfile;
|
||||
t_tag archtag;
|
||||
t_tag clienttag;
|
||||
char const * versiontag;
|
||||
t_parsed_exeinfo * parsed_exeinfo;
|
||||
unsigned long versionid;
|
||||
unsigned long gameversion;
|
||||
unsigned long checksum;
|
||||
} t_versioninfo;
|
||||
#endif
|
||||
|
||||
typedef struct s_versioncheck
|
||||
#ifdef VERSIONCHECK_INTERNAL_ACCESS
|
||||
{
|
||||
char const * eqn;
|
||||
char const * mpqfile;
|
||||
char const * versiontag;
|
||||
}
|
||||
#endif
|
||||
t_versioncheck;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef JUST_NEED_TYPES
|
||||
#ifndef INCLUDED_VERSIONCHECK_PROTOS
|
||||
#define INCLUDED_VERSIONCHECK_PROTOS
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace bnetd
|
||||
{
|
||||
class VersionCheck;
|
||||
|
||||
extern t_versioncheck * versioncheck_create(t_tag archtag, t_tag clienttag);
|
||||
extern int versioncheck_destroy(t_versioncheck * vc);
|
||||
extern char const * versioncheck_get_mpqfile(t_versioncheck const * vc);
|
||||
extern char const * versioncheck_get_eqn(t_versioncheck const * vc);
|
||||
extern int versioncheck_validate(t_versioncheck * vc, t_tag archtag, t_tag clienttag, char const * exeinfo, unsigned long versionid, unsigned long gameversion, unsigned long checksum);
|
||||
struct file_metadata
|
||||
{
|
||||
std::time_t timestamp;
|
||||
std::uint64_t file_size;
|
||||
std::string filename;
|
||||
};
|
||||
|
||||
extern int versioncheck_load(char const * filename);
|
||||
extern int versioncheck_unload(void);
|
||||
bool load_versioncheck_conf(const std::string& filename);
|
||||
void unload_versioncheck_conf();
|
||||
|
||||
extern char const * versioncheck_get_versiontag(t_versioncheck const * vc);
|
||||
extern int versioncheck_set_versiontag(t_versioncheck * vc, char const * versiontag);
|
||||
const VersionCheck& select_versioncheck_entry(t_tag architecture, t_tag client, std::uint32_t version_id);
|
||||
|
||||
}
|
||||
class VersionCheck
|
||||
{
|
||||
public:
|
||||
bool validate_checkrevision_data(std::uint32_t game_version, std::uint32_t checksum, const std::string& unparsed_file_metadata) const;
|
||||
|
||||
}
|
||||
std::string get_equation() const noexcept;
|
||||
std::string get_checkrevision_filename() const noexcept;
|
||||
std::string get_version_tag() const noexcept;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
~VersionCheck() = default;
|
||||
private:
|
||||
/*******************************************************************************/
|
||||
// Friend functions
|
||||
/*******************************************************************************/
|
||||
friend bool load_versioncheck_conf(const std::string& filename);
|
||||
friend const VersionCheck& select_versioncheck_entry(t_tag architecture, t_tag client, std::uint32_t version_id);
|
||||
|
||||
|
||||
/*******************************************************************************/
|
||||
// Constructors
|
||||
/*******************************************************************************/
|
||||
VersionCheck(std::uint32_t version_id, std::uint32_t game_version, std::uint32_t checksum, t_tag architecture,
|
||||
t_tag client, const struct file_metadata& exe_metadata, const std::string& equation,
|
||||
const std::string& checkrevision_filename, const std::string& version_tag);
|
||||
VersionCheck(const std::string& checkrevision_filename, const std::string& equation, const std::string& version_tag);
|
||||
|
||||
|
||||
/*******************************************************************************/
|
||||
// Member variables
|
||||
/*******************************************************************************/
|
||||
const std::uint32_t m_version_id; // AKA "Version Byte"
|
||||
const std::uint32_t m_game_version;
|
||||
const std::uint32_t m_checksum;
|
||||
const t_tag m_architecture;
|
||||
const t_tag m_client;
|
||||
const struct file_metadata m_metadata;
|
||||
const std::string m_equation;
|
||||
const std::string m_checkrevision_filename;
|
||||
const std::string m_version_tag;
|
||||
};
|
||||
|
||||
} // namespace bnetd
|
||||
|
||||
} // namespace pvpgn
|
||||
|
||||
#endif //PVPGN_BNETD_VERSIONCHECK_H
|
|
@ -240,7 +240,7 @@ const unsigned BNETD_HASHTABLE_SIZE = 61;
|
|||
const int BNETD_REALM_PORT = 6113; /* where D2CS listens */
|
||||
const char * const BNETD_TELNET_ADDRS = ""; /* this means none */
|
||||
const int BNETD_TELNET_PORT = 23; /* used if port not specified */
|
||||
const char * const BNETD_EXEINFO_MATCH = "exact";
|
||||
const char * const BNETD_EXEINFO_MATCH = "true";
|
||||
const unsigned PVPGN_VERSION_TIMEDIV = 0; /* no timediff check by default */
|
||||
const int PVPGN_CACHE_MEMLIMIT = 5000000; /* bytes */
|
||||
const char * const PVPGN_DEFAULT_SYMB = "-_[]";
|
||||
|
|
Loading…
Reference in a new issue