Overhaul versioncheck system (#340)
* Overhaul versioncheck system - Removed version_exeinfo_match, skip_versioncheck, and version_exeinfo_maxdiff from bnetd.conf - Modified configuration file to use JSON - Correctly finds the appropriate versioncheck entries - Add versioncheck.md to docs/ * Update JSON for Modern C++ from 3.0.1 to 3.1.2
This commit is contained in:
parent
85f26eb2ab
commit
74b06d022b
22 changed files with 6590 additions and 5090 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"
|
||||
|
@ -209,19 +209,6 @@ star_iconfile = "icons_STAR.bni"
|
|||
# Example: allowed_clients = war3,w3xp
|
||||
allowed_clients = all
|
||||
|
||||
# If this option is enabled, the verification step is skipped if possible.
|
||||
# This only works with clients < 109. It is useful because you no longer
|
||||
# need any of the IX86AUTH?.MPQ and PMACAUTH?.MPQ files. Note that it will
|
||||
# also skip over all the autoupdate checks effectively disabling it.
|
||||
#
|
||||
# If you disable this you must have one or more of the MPQ files. Otherwise
|
||||
# clients will hang when they first connect because they are attempting to
|
||||
# download them. The versioncheck can only be skipped for clients older
|
||||
# than 109. Starting with version 109 the clients will always do version
|
||||
# checking since they do not function properly if the server does not
|
||||
# request it.
|
||||
skip_versioncheck = true
|
||||
|
||||
# If you enable the version checks but want to allow clients that don't pass
|
||||
# the checksum test then enable this.
|
||||
allow_bad_version = true
|
||||
|
@ -232,19 +219,6 @@ 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
|
||||
|
||||
# #
|
||||
##############################################################################
|
||||
|
||||
|
@ -448,7 +422,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
1855
conf/versioncheck.json.in
Normal file
1855
conf/versioncheck.json.in
Normal file
File diff suppressed because it is too large
Load diff
16
docs/versioncheck.md
Normal file
16
docs/versioncheck.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# VersionCheck
|
||||
|
||||
### Configuration File
|
||||
The configuration file is in standard JSON format and consists of arrays for each Battle.net game client whose names are a four-letter string.
|
||||
|
||||
Within the game client arrays are arrays for platform whose names are also a four-letter string (i.e. `IX86`, `PMAC`, `XMAC`).
|
||||
|
||||
Within the platform arrays are arrays for the game client's version ID, traditionally known as a "version byte". The version ID is traditionally written in hex format, as indicated with a preceding "0x" or "0X", but can also be in decimal format. The version ID array consists of two pairs, `checkRevisionFile` and `equation`, and an array, `entries`.
|
||||
|
||||
The `entries` array consists of five pairs: `title`, `version`, `hash`, `fileMetadata`, `versionTag`.
|
||||
- `title`: This is to assist the reader of the configuration file, it does not affect the entry in any way.
|
||||
- `version`: The version number returned by CheckRevision which is obtained from the [VERSIONINFO](https://msdn.microsoft.com/en-us/library/aa381058) resource of the game file. See sample implementation: https://github.com/pvpgn/CheckRevision
|
||||
- `hash`: The hash returned by CheckRevision which uses up to three files from the game to produce the hash. See sample implementation: https://github.com/pvpgn/CheckRevision
|
||||
- `fileMetadata`: The string returned by CheckRevision which consists of the game's filename, last modified date, last modified time, and filesize, all separated by one space (e.g. `war3.exe 08/16/09 19:21:59 471040`). See sample implementation: https://github.com/pvpgn/CheckRevision
|
||||
- Note: This pair is currently unused by PvPGN, but may be used in the future.
|
||||
- `versionTag`: An arbitrary string that must be unique from all other version tags. It is traditionally in the form of the four-letter game string, followed by an underscore and the version (e.g. `WAR3_1282` is used for WarCraft 3: Reign of Chaos 1.28.2).
|
|
@ -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) ? conn_get_versioncheck(c)->get_version_tag().c_str() : nullptr;
|
||||
|
||||
list_append_data(matchlists[queue][level], md);
|
||||
|
||||
|
@ -834,7 +828,11 @@ 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
|
||||
&& conn_get_versioncheck(c)
|
||||
&& !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;
|
||||
|
|
|
@ -4627,7 +4627,7 @@ namespace pvpgn
|
|||
|
||||
|
||||
// disallow get/set value for password hash and username (hash can be cracked easily, account name should be permanent)
|
||||
if (strcasecmp(key, "bnet\\acct\\passhash1") == 0 || strcasecmp(key, "bnet\\acct\\username") == 0 || strcasecmp(key, "bnet\\username") == 0)
|
||||
if (strcasecmp(key, "bnet\\acct\\passhash1") == 0 || strcasecmp(key, "bnet\\acct\\username") == 0 || strcasecmp(key, "bnet\\acct\\verifier") == 0 || strcasecmp(key, "bnet\\acct\\salt") == 0 || strcasecmp(key, "bnet\\username") == 0)
|
||||
{
|
||||
message_send_text(c, message_type_info, c, localize(c, "Access denied due to security reasons."));
|
||||
return -1;
|
||||
|
|
|
@ -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;
|
||||
|
||||
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,7 +29,9 @@
|
|||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
|
||||
#include "compat/strcasecmp.h"
|
||||
#include "compat/strncasecmp.h"
|
||||
|
@ -570,15 +572,12 @@ namespace pvpgn
|
|||
packet_del_ref(rpacket);
|
||||
}
|
||||
|
||||
if ((rpacket = packet_create(packet_class_bnet))) {
|
||||
t_versioncheck *vc;
|
||||
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "[{}] selecting version check", conn_get_socket(c));
|
||||
vc = versioncheck_create(conn_get_archtag(c), conn_get_clienttag(c));
|
||||
conn_set_versioncheck(c, vc);
|
||||
if ((rpacket = packet_create(packet_class_bnet)))
|
||||
{
|
||||
packet_set_size(rpacket, sizeof(t_server_authreq_109));
|
||||
packet_set_type(rpacket, SERVER_AUTHREQ_109);
|
||||
|
||||
// Logon type
|
||||
if ((conn_get_clienttag(c) == CLIENTTAG_WARCRAFT3_UINT))
|
||||
bn_int_set(&rpacket->u.server_authreq_109.logontype, SERVER_AUTHREQ_109_LOGONTYPE_W3);
|
||||
else if ((conn_get_clienttag(c) == CLIENTTAG_WAR3XP_UINT))
|
||||
|
@ -586,18 +585,29 @@ namespace pvpgn
|
|||
else
|
||||
bn_int_set(&rpacket->u.server_authreq_109.logontype, SERVER_AUTHREQ_109_LOGONTYPE);
|
||||
|
||||
|
||||
// Session
|
||||
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));
|
||||
|
||||
|
||||
// CheckRevision
|
||||
std::tuple<std::string, std::string> checkrevision = select_checkrevision(bn_int_get(packet->u.client_countryinfo_109.archtag), bn_int_get(packet->u.client_countryinfo_109.clienttag), bn_int_get(packet->u.client_countryinfo_109.versionid));
|
||||
file_to_mod_time(c, std::get<0>(checkrevision).c_str(), &rpacket->u.server_authreq_109.timestamp); // Checkrevision file timestamp
|
||||
packet_append_string(rpacket, std::get<0>(checkrevision).c_str()); // CheckRevision filename
|
||||
packet_append_string(rpacket, std::get<1>(checkrevision).c_str()); // CheckRevision equation
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "[{}] selected \"{}\" \"{}\"", conn_get_socket(c), std::get<0>(checkrevision), std::get<1>(checkrevision));
|
||||
|
||||
|
||||
// WarCraft 3 Server Signature
|
||||
if ((conn_get_clienttag(c) == CLIENTTAG_WARCRAFT3_UINT)
|
||||
|| (conn_get_clienttag(c) == CLIENTTAG_WAR3XP_UINT)) {
|
||||
|| (conn_get_clienttag(c) == CLIENTTAG_WAR3XP_UINT))
|
||||
{
|
||||
char padding[128];
|
||||
std::memset(padding, 0, 128);
|
||||
packet_append_data(rpacket, padding, 128);
|
||||
}
|
||||
|
||||
conn_push_outqueue(c, rpacket);
|
||||
packet_del_ref(rpacket);
|
||||
}
|
||||
|
@ -633,36 +643,23 @@ namespace pvpgn
|
|||
conn_set_archtag(c, bn_int_get(packet->u.client_progident.archtag));
|
||||
conn_set_clienttag(c, bn_int_get(packet->u.client_progident.clienttag));
|
||||
|
||||
if (prefs_get_skip_versioncheck()) {
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "[{}] attempting to skip version check by sending early authreply", conn_get_socket(c));
|
||||
/* skip over SERVER_AUTHREQ1 and CLIENT_AUTHREQ1 */
|
||||
if ((rpacket = packet_create(packet_class_bnet))) {
|
||||
packet_set_size(rpacket, sizeof(t_server_authreply1));
|
||||
packet_set_type(rpacket, SERVER_AUTHREPLY1);
|
||||
bn_int_set(&rpacket->u.server_authreply1.message, SERVER_AUTHREPLY1_MESSAGE_OK);
|
||||
packet_append_string(rpacket, "");
|
||||
packet_append_string(rpacket, ""); /* FIXME: what's the second string for? */
|
||||
conn_push_outqueue(c, rpacket);
|
||||
packet_del_ref(rpacket);
|
||||
}
|
||||
}
|
||||
else {
|
||||
t_versioncheck *vc;
|
||||
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "[{}] selecting version check", conn_get_socket(c));
|
||||
vc = versioncheck_create(conn_get_archtag(c), conn_get_clienttag(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));
|
||||
|
||||
|
||||
// CheckRevision
|
||||
std::tuple<std::string, std::string> checkrevision = select_checkrevision(bn_int_get(packet->u.client_progident.archtag), bn_int_get(packet->u.client_progident.clienttag), bn_int_get(packet->u.client_progident.versionid));
|
||||
file_to_mod_time(c, std::get<0>(checkrevision).c_str(), &rpacket->u.server_authreq_109.timestamp); // Checkrevision file timestamp
|
||||
packet_append_string(rpacket, std::get<0>(checkrevision).c_str()); // CheckRevision filename
|
||||
packet_append_string(rpacket, std::get<1>(checkrevision).c_str()); // CheckRevision equation
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "[{}] selected \"{}\" \"{}\"", conn_get_socket(c), std::get<0>(checkrevision), std::get<1>(checkrevision));
|
||||
|
||||
|
||||
conn_push_outqueue(c, rpacket);
|
||||
packet_del_ref(rpacket);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1000,212 +997,245 @@ 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;
|
||||
}
|
||||
|
||||
auto send_failed_packet = [](t_connection *c)
|
||||
{
|
||||
char verstr[16];
|
||||
char const *exeinfo;
|
||||
char const *versiontag;
|
||||
int failed;
|
||||
t_packet *rpacket = packet_create(packet_class_bnet);
|
||||
if (rpacket)
|
||||
{
|
||||
packet_set_size(rpacket, sizeof(t_server_authreply1));
|
||||
packet_set_type(rpacket, SERVER_AUTHREPLY1);
|
||||
conn_set_state(c, conn_state_untrusted);
|
||||
|
||||
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;
|
||||
bn_int_set(&rpacket->u.server_authreply1.message, SERVER_AUTHREPLY1_MESSAGE_BADVERSION);
|
||||
packet_append_string(rpacket, "");
|
||||
packet_append_string(rpacket, ""); // undocumented extra null terminator
|
||||
|
||||
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_push_outqueue(c, rpacket);
|
||||
|
||||
packet_del_ref(rpacket);
|
||||
}
|
||||
};
|
||||
|
||||
// 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))
|
||||
{
|
||||
send_failed_packet(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bn_int_get(packet->u.client_authreq1.clienttag) != conn_get_clienttag(c))
|
||||
{
|
||||
send_failed_packet(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bn_int_get(packet->u.client_authreq1.versionid) != conn_get_versionid(c))
|
||||
{
|
||||
send_failed_packet(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const char *exeinfo = packet_get_str_const(packet, sizeof(t_client_authreq1), MAX_EXEINFO_STR);
|
||||
if (exeinfo)
|
||||
{
|
||||
conn_set_clientexe(c, exeinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ1 (missing or too long exeinfo)", conn_get_socket(c));
|
||||
send_failed_packet(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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, 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));
|
||||
std::strcpy(verstr, vernum_to_verstr(bn_int_get(packet->u.client_authreq1.gameversion)));
|
||||
conn_set_clientver(c, verstr);
|
||||
conn_set_clientexe(c, exeinfo);
|
||||
|
||||
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));
|
||||
|
||||
|
||||
if ((rpacket = packet_create(packet_class_bnet))) {
|
||||
const VersionCheck* vc = select_versioncheck(conn_get_archtag(c), conn_get_clienttag(c), conn_get_versionid(c), conn_get_gameversion(c), conn_get_checksum(c));
|
||||
conn_set_versioncheck(c, vc);
|
||||
if (vc)
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client matches versiontag \"{}\"", conn_get_socket(c), conn_get_versioncheck(c)->get_version_tag());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prefs_get_allow_unknown_version())
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] skipping versioncheck because allow_unknown_version is true", conn_get_socket(c));
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client failed versioncheck", conn_get_socket(c));
|
||||
send_failed_packet(c);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
t_packet *rpacket = packet_create(packet_class_bnet);
|
||||
if (rpacket)
|
||||
{
|
||||
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));
|
||||
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 */
|
||||
char *mpqfilename = nullptr;
|
||||
if (vc)
|
||||
{
|
||||
mpqfilename = autoupdate_check(conn_get_archtag(c), conn_get_clienttag(c), conn_get_gamelang(c), vc->get_version_tag().c_str(), nullptr);
|
||||
}
|
||||
|
||||
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);
|
||||
// Only handle updates when there is an update file available.
|
||||
if (mpqfilename)
|
||||
{
|
||||
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), versioncheck_get_versiontag(conn_get_versioncheck(c)));
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] no upgrade is available", conn_get_socket(c));
|
||||
}
|
||||
|
||||
bn_int_set(&rpacket->u.server_authreply1.message, SERVER_AUTHREPLY1_MESSAGE_OK);
|
||||
packet_append_string(rpacket, "");
|
||||
}
|
||||
packet_append_string(rpacket, ""); // FIXME: what's the second string for?
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _client_authreq109(t_connection * c, t_packet const *const packet)
|
||||
{
|
||||
t_packet *rpacket;
|
||||
|
||||
if (packet_get_size(packet) < sizeof(t_client_authreq_109)) {
|
||||
if (packet_get_size(packet) < sizeof(t_client_authreq_109))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ_109 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_authreq_109), packet_get_size(packet));
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto send_failed_packet = [](t_connection *c)
|
||||
{
|
||||
char verstr[16];
|
||||
char const *exeinfo;
|
||||
char const *versiontag;
|
||||
int failed;
|
||||
char const *owner;
|
||||
unsigned int count;
|
||||
unsigned int pos;
|
||||
t_packet *rpacket = packet_create(packet_class_bnet);
|
||||
if (rpacket)
|
||||
{
|
||||
packet_set_size(rpacket, sizeof(t_server_authreply_109));
|
||||
packet_set_type(rpacket, SERVER_AUTHREPLY_109);
|
||||
conn_set_state(c, conn_state_untrusted);
|
||||
|
||||
failed = 0;
|
||||
count = bn_int_get(packet->u.client_authreq_109.cdkey_number);
|
||||
pos = sizeof(t_client_authreq_109)+(count * sizeof(t_cdkey_info));
|
||||
bn_int_set(&rpacket->u.server_authreply_109.message, SERVER_AUTHREPLY_109_MESSAGE_BADVERSION);
|
||||
packet_append_string(rpacket, '\0');
|
||||
|
||||
if (!(exeinfo = packet_get_str_const(packet, pos, MAX_EXEINFO_STR))) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ_109 (missing or too long exeinfo)", conn_get_socket(c));
|
||||
exeinfo = "badexe";
|
||||
failed = 1;
|
||||
conn_push_outqueue(c, rpacket);
|
||||
|
||||
packet_del_ref(rpacket);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
std::uint32_t count = bn_int_get(packet->u.client_authreq_109.cdkey_number);
|
||||
std::size_t position = sizeof(t_client_authreq_109) + (count * sizeof(t_cdkey_info));
|
||||
|
||||
const char *const exeinfo = packet_get_str_const(packet, position, MAX_EXEINFO_STR);
|
||||
if (exeinfo)
|
||||
{
|
||||
conn_set_clientexe(c, exeinfo);
|
||||
pos += std::strlen(exeinfo) + 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 */
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ_109 (missing or too long exeinfo)", conn_get_socket(c));
|
||||
send_failed_packet(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
position += std::strlen(exeinfo) + 1;
|
||||
|
||||
const char *const owner = packet_get_str_const(packet, position, MAX_OWNER_STR);
|
||||
if (owner)
|
||||
{
|
||||
conn_set_owner(c, owner);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ_109 (missing or too long owner)", conn_get_socket(c));
|
||||
conn_set_owner(c, "");
|
||||
}
|
||||
|
||||
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));
|
||||
std::strcpy(verstr, vernum_to_verstr(bn_int_get(packet->u.client_authreq_109.gameversion)));
|
||||
conn_set_clientver(c, verstr);
|
||||
conn_set_clientexe(c, exeinfo);
|
||||
std::string version = vernum_to_verstr(bn_int_get(packet->u.client_authreq_109.gameversion));
|
||||
conn_set_clientver(c, version.c_str());
|
||||
|
||||
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), version, exeinfo, conn_get_versionid(c), conn_get_gameversion(c), conn_get_checksum(c));
|
||||
|
||||
if ((rpacket = packet_create(packet_class_bnet))) {
|
||||
t_packet *rpacket;
|
||||
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
|
||||
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;
|
||||
const VersionCheck* vc = select_versioncheck(conn_get_archtag(c), conn_get_clienttag(c), conn_get_versionid(c), conn_get_gameversion(c), conn_get_checksum(c));
|
||||
conn_set_versioncheck(c, vc);
|
||||
if (vc)
|
||||
{
|
||||
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;
|
||||
{
|
||||
if (prefs_get_allow_unknown_version())
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] skipping versioncheck because allow_unknown_version is true", conn_get_socket(c));
|
||||
}
|
||||
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));
|
||||
send_failed_packet(c);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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_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);
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
if (vc)
|
||||
{
|
||||
// Only handle updates when there is an update file available.
|
||||
if (mpqfilename)
|
||||
{
|
||||
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(static_cast<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 is available", conn_get_socket(c));
|
||||
}
|
||||
|
||||
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);
|
||||
packet_del_ref(rpacket);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2427,97 +2457,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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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)))
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
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(friend_connection) ? 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;
|
||||
}
|
||||
|
||||
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(user_connection) ? 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 (!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);
|
||||
if (available_players == 0)
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "No available players");
|
||||
}
|
||||
|
||||
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 +3749,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 +4812,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;
|
||||
}
|
||||
|
|
|
@ -213,11 +213,8 @@ namespace pvpgn
|
|||
config.update("star_iconfile", prefs_get_star_iconfile());
|
||||
config.update("tosfile", prefs_get_tosfile());
|
||||
config.update("allowed_clients", prefs_get_allowed_clients());
|
||||
config.update("skip_versioncheck", prefs_get_skip_versioncheck());
|
||||
config.update("allow_bad_version", prefs_get_allow_bad_version());
|
||||
config.update("allow_unknown_version", prefs_get_allow_unknown_version());
|
||||
config.update("version_exeinfo_match", prefs_get_version_exeinfo_match());
|
||||
config.update("version_exeinfo_maxdiff", prefs_get_version_exeinfo_maxdiff());
|
||||
config.update("usersync", prefs_get_user_sync_timer());
|
||||
config.update("userflush", prefs_get_user_flush_timer());
|
||||
config.update("userflush_connected", prefs_get_user_flush_connected());
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -118,7 +118,6 @@ namespace pvpgn
|
|||
char const * maildir;
|
||||
char const * log_notice;
|
||||
unsigned int savebyname;
|
||||
unsigned int skip_versioncheck;
|
||||
unsigned int allow_bad_version;
|
||||
unsigned int allow_unknown_version;
|
||||
char const * versioncheck_file;
|
||||
|
@ -494,10 +493,6 @@ namespace pvpgn
|
|||
static const char *conf_get_savebyname(void);
|
||||
static int conf_setdef_savebyname(void);
|
||||
|
||||
static int conf_set_skip_versioncheck(const char *valstr);
|
||||
static const char *conf_get_skip_versioncheck(void);
|
||||
static int conf_setdef_skip_versioncheck(void);
|
||||
|
||||
static int conf_set_allow_bad_version(const char *valstr);
|
||||
static const char *conf_get_allow_bad_version(void);
|
||||
static int conf_setdef_allow_bad_version(void);
|
||||
|
@ -530,14 +525,6 @@ namespace pvpgn
|
|||
static const char *conf_get_ipban_check_int(void);
|
||||
static int conf_setdef_ipban_check_int(void);
|
||||
|
||||
static int conf_set_version_exeinfo_match(const char *valstr);
|
||||
static const char *conf_get_version_exeinfo_match(void);
|
||||
static int conf_setdef_version_exeinfo_match(void);
|
||||
|
||||
static int conf_set_version_exeinfo_maxdiff(const char *valstr);
|
||||
static const char *conf_get_version_exeinfo_maxdiff(void);
|
||||
static int conf_setdef_version_exeinfo_maxdiff(void);
|
||||
|
||||
static int conf_set_max_concurrent_logins(const char *valstr);
|
||||
static const char *conf_get_max_concurrent_logins(void);
|
||||
static int conf_setdef_max_concurrent_logins(void);
|
||||
|
@ -816,7 +803,6 @@ namespace pvpgn
|
|||
{ "maildir", conf_set_maildir, conf_get_maildir, conf_setdef_maildir },
|
||||
{ "log_notice", conf_set_log_notice, conf_get_log_notice, conf_setdef_log_notice },
|
||||
{ "savebyname", conf_set_savebyname, conf_get_savebyname, conf_setdef_savebyname },
|
||||
{ "skip_versioncheck", conf_set_skip_versioncheck, conf_get_skip_versioncheck, conf_setdef_skip_versioncheck },
|
||||
{ "allow_bad_version", conf_set_allow_bad_version, conf_get_allow_bad_version, conf_setdef_allow_bad_version },
|
||||
{ "allow_unknown_version", conf_set_allow_unknown_version, conf_get_allow_unknown_version, conf_setdef_allow_unknown_version },
|
||||
{ "versioncheck_file", conf_set_versioncheck_file, conf_get_versioncheck_file, conf_setdef_versioncheck_file },
|
||||
|
@ -825,8 +811,6 @@ namespace pvpgn
|
|||
{ "hashtable_size", conf_set_hashtable_size, conf_get_hashtable_size, conf_setdef_hashtable_size },
|
||||
{ "telnetaddrs", conf_set_telnetaddrs, conf_get_telnetaddrs, conf_setdef_telnetaddrs },
|
||||
{ "ipban_check_int", conf_set_ipban_check_int, conf_get_ipban_check_int, conf_setdef_ipban_check_int },
|
||||
{ "version_exeinfo_match", conf_set_version_exeinfo_match, conf_get_version_exeinfo_match, conf_setdef_version_exeinfo_match },
|
||||
{ "version_exeinfo_maxdiff", conf_set_version_exeinfo_maxdiff, conf_get_version_exeinfo_maxdiff, conf_setdef_version_exeinfo_maxdiff },
|
||||
{ "max_concurrent_logins", conf_set_max_concurrent_logins, conf_get_max_concurrent_logins, conf_setdef_max_concurrent_logins },
|
||||
{ "mapsfile", conf_set_mapsfile, conf_get_mapsfile, conf_setdef_mapsfile },
|
||||
{ "xplevelfile", conf_set_xplevelfile, conf_get_xplevelfile, conf_setdef_xplevelfile },
|
||||
|
@ -2564,27 +2548,6 @@ namespace pvpgn
|
|||
}
|
||||
|
||||
|
||||
extern unsigned int prefs_get_skip_versioncheck(void)
|
||||
{
|
||||
return prefs_runtime_config.skip_versioncheck;
|
||||
}
|
||||
|
||||
static int conf_set_skip_versioncheck(const char *valstr)
|
||||
{
|
||||
return conf_set_bool(&prefs_runtime_config.skip_versioncheck, valstr, 0);
|
||||
}
|
||||
|
||||
static int conf_setdef_skip_versioncheck(void)
|
||||
{
|
||||
return conf_set_bool(&prefs_runtime_config.skip_versioncheck, NULL, 0);
|
||||
}
|
||||
|
||||
static const char* conf_get_skip_versioncheck(void)
|
||||
{
|
||||
return conf_get_bool(prefs_runtime_config.skip_versioncheck);
|
||||
}
|
||||
|
||||
|
||||
extern unsigned int prefs_get_allow_bad_version(void)
|
||||
{
|
||||
return prefs_runtime_config.allow_bad_version;
|
||||
|
@ -2753,48 +2716,6 @@ namespace pvpgn
|
|||
}
|
||||
|
||||
|
||||
extern char const * prefs_get_version_exeinfo_match(void)
|
||||
{
|
||||
return prefs_runtime_config.version_exeinfo_match;
|
||||
}
|
||||
|
||||
static int conf_set_version_exeinfo_match(const char *valstr)
|
||||
{
|
||||
return conf_set_str(&prefs_runtime_config.version_exeinfo_match, valstr, NULL);
|
||||
}
|
||||
|
||||
static int conf_setdef_version_exeinfo_match(void)
|
||||
{
|
||||
return conf_set_str(&prefs_runtime_config.version_exeinfo_match, NULL, BNETD_EXEINFO_MATCH);
|
||||
}
|
||||
|
||||
static const char* conf_get_version_exeinfo_match(void)
|
||||
{
|
||||
return prefs_runtime_config.version_exeinfo_match;
|
||||
}
|
||||
|
||||
|
||||
extern unsigned int prefs_get_version_exeinfo_maxdiff(void)
|
||||
{
|
||||
return prefs_runtime_config.version_exeinfo_maxdiff;
|
||||
}
|
||||
|
||||
static int conf_set_version_exeinfo_maxdiff(const char *valstr)
|
||||
{
|
||||
return conf_set_int(&prefs_runtime_config.version_exeinfo_maxdiff, valstr, 0);
|
||||
}
|
||||
|
||||
static int conf_setdef_version_exeinfo_maxdiff(void)
|
||||
{
|
||||
return conf_set_int(&prefs_runtime_config.version_exeinfo_maxdiff, NULL, PVPGN_VERSION_TIMEDIV);
|
||||
}
|
||||
|
||||
static const char* conf_get_version_exeinfo_maxdiff(void)
|
||||
{
|
||||
return conf_get_int(prefs_runtime_config.version_exeinfo_maxdiff);
|
||||
}
|
||||
|
||||
|
||||
extern unsigned int prefs_get_max_concurrent_logins(void)
|
||||
{
|
||||
return prefs_runtime_config.max_concurrent_logins;
|
||||
|
|
|
@ -113,7 +113,6 @@ namespace pvpgn
|
|||
extern char const * prefs_get_maildir(void);
|
||||
extern char const * prefs_get_log_notice(void);
|
||||
extern unsigned int prefs_get_savebyname(void);
|
||||
extern unsigned int prefs_get_skip_versioncheck(void);
|
||||
extern unsigned int prefs_get_allow_bad_version(void);
|
||||
extern unsigned int prefs_get_allow_unknown_version(void);
|
||||
extern char const * prefs_get_versioncheck_file(void);
|
||||
|
@ -122,8 +121,6 @@ namespace pvpgn
|
|||
extern unsigned int prefs_get_hashtable_size(void);
|
||||
extern char const * prefs_get_telnet_addrs(void);
|
||||
extern unsigned int prefs_get_ipban_check_int(void);
|
||||
extern char const * prefs_get_version_exeinfo_match(void);
|
||||
extern unsigned int prefs_get_version_exeinfo_maxdiff(void);
|
||||
|
||||
extern unsigned int prefs_get_max_concurrent_logins(void);
|
||||
|
||||
|
|
|
@ -1415,10 +1415,12 @@ 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)
|
||||
{
|
||||
|
|
|
@ -16,645 +16,262 @@
|
|||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#include "common/setup_before.h"
|
||||
#define VERSIONCHECK_INTERNAL_ACCESS
|
||||
#include "versioncheck.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "compat/strcasecmp.h"
|
||||
#include "common/list.h"
|
||||
#include "common/xalloc.h"
|
||||
#include "common/eventlog.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "common/field_sizes.h"
|
||||
#include "common/token.h"
|
||||
#include "common/format.h"
|
||||
#include "common/hash_tuple.hpp"
|
||||
#include "common/proginfo.h"
|
||||
#include "common/xstring.h"
|
||||
#include "compat/strcasecmp.h"
|
||||
#include "common/token.h"
|
||||
#include "common/util.h"
|
||||
|
||||
|
||||
#include "json/json.hpp"
|
||||
|
||||
#include "prefs.h"
|
||||
#include "common/setup_after.h"
|
||||
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace bnetd
|
||||
{
|
||||
std::unordered_map<std::tuple<std::uint32_t, std::uint32_t, t_tag, t_tag>, VersionCheck, hash_tuple::hash<std::tuple<std::uint32_t, std::uint32_t, t_tag, t_tag>>> vc_entries;
|
||||
std::unordered_map<std::tuple<t_tag, t_tag, std::uint32_t>, std::tuple<std::string, std::string>, hash_tuple::hash<std::tuple<t_tag, t_tag, std::uint32_t>>> cr_entries;
|
||||
|
||||
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" };
|
||||
bool versioncheck_conf_is_loaded = false;
|
||||
|
||||
static int versioncheck_compare_exeinfo(t_parsed_exeinfo * pattern, t_parsed_exeinfo * match);
|
||||
// bool validate_file_metadata_format(const std::string& metadata);
|
||||
|
||||
extern t_versioncheck * versioncheck_create(t_tag archtag, t_tag clienttag)
|
||||
|
||||
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];
|
||||
if (versioncheck_conf_is_loaded)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "Could not load {}, a versioncheck configuration file is already loaded", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
LIST_TRAVERSE_CONST(versioninfo_head, curr)
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
|
||||
std::ifstream file_stream(filename, std::ios::in);
|
||||
if (!file_stream.is_open())
|
||||
{
|
||||
if (!(vi = (t_versioninfo*)elem_get_data(curr))) /* should not happen */
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "Could not open file \"{}\" for reading", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
json jconf;
|
||||
try
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "version list contains NULL item");
|
||||
file_stream >> jconf;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "Could not parse {}: {}", filename, e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
vc_entries.reserve(185);
|
||||
|
||||
for (auto jclient : jconf.items())
|
||||
{
|
||||
t_tag client = tag_str_to_uint(jclient.key().c_str());
|
||||
if (!tag_check_client(client))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "Invalid client: {}", jclient.key());
|
||||
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)
|
||||
for (auto jarchitecture : jclient.value().items())
|
||||
{
|
||||
t_tag architecture = tag_str_to_uint(jarchitecture.key().c_str());
|
||||
if (!tag_check_arch(architecture))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "Invalid architecture: {}", jarchitecture.key());
|
||||
continue;
|
||||
if (vi->clienttag != clienttag)
|
||||
}
|
||||
|
||||
for (auto jversion_id : jarchitecture.value().items())
|
||||
{
|
||||
std::uint32_t version_id;
|
||||
try
|
||||
{
|
||||
version_id = std::stoul(jversion_id.key(), nullptr, 0);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "Invalid version id \"{}\": {}", jversion_id.key(), e.what());
|
||||
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));
|
||||
// FIXME: do error checking for these
|
||||
std::string checkrevision_filename = jversion_id.value()["checkRevisionFile"].get<std::string>();
|
||||
std::string checkrevision_equation = jversion_id.value()["equation"].get<std::string>();
|
||||
|
||||
return vc;
|
||||
cr_entries.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(architecture, client, version_id),
|
||||
std::forward_as_tuple(checkrevision_filename, checkrevision_equation)
|
||||
);
|
||||
|
||||
for (auto jentry : jversion_id.value()["entries"])
|
||||
{
|
||||
try
|
||||
{
|
||||
VersionCheck entry(jentry["title"].get<std::string>(),
|
||||
version_id,
|
||||
jentry["version"].get<std::string>(),
|
||||
jentry["hash"].get<std::string>(),
|
||||
architecture,
|
||||
client,
|
||||
jentry["versionTag"].get<std::string>()
|
||||
);
|
||||
|
||||
vc_entries.insert({ std::make_tuple(entry.m_version_id, entry.m_game_version, entry.m_architecture, entry.m_client), entry });
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "Failed to load versioncheck entry: {}", e.what());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "Successfully loaded {} versioncheck entries in {} microseconds", vc_entries.size(), std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count());
|
||||
|
||||
versioncheck_conf_is_loaded = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void unload_versioncheck_conf()
|
||||
{
|
||||
if (versioncheck_conf_is_loaded)
|
||||
{
|
||||
vc_entries.clear();
|
||||
|
||||
cr_entries.clear();
|
||||
|
||||
versioncheck_conf_is_loaded = false;
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "Successfully unloaded all version check entries");
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<std::string, std::string> select_checkrevision(t_tag architecture, t_tag client, std::uint32_t version_id)
|
||||
{
|
||||
static const std::tuple<std::string, std::string> default_checkrevision { "ver-IX86-1.mpq", "A=42 B=42 C=42 4 A=A^S B=B^B C=C^C A=A^S" };
|
||||
|
||||
auto key = cr_entries.find(std::make_tuple(architecture, client, version_id));
|
||||
if (key != cr_entries.end())
|
||||
{
|
||||
return key->second;
|
||||
}
|
||||
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "Could not find corresponding CheckRevision entry, returning default CheckRevision");
|
||||
|
||||
return default_checkrevision;
|
||||
}
|
||||
|
||||
// could use C++17 std::optional here
|
||||
const VersionCheck* select_versioncheck(t_tag architecture, t_tag client, std::uint32_t version_id,
|
||||
std::uint32_t checkrevision_version, std::uint32_t checkrevision_checksum)
|
||||
{
|
||||
auto it = vc_entries.find(std::make_tuple(version_id, checkrevision_version, architecture, client));
|
||||
if (it == vc_entries.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (it->second.m_checksum != checkrevision_checksum
|
||||
&& !prefs_get_allow_bad_version())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &(it->second);
|
||||
}
|
||||
|
||||
VersionCheck::VersionCheck(const std::string& title, std::uint32_t version_id, const std::string& game_version,
|
||||
const std::string& checksum, t_tag architecture, t_tag client, const std::string& version_tag)
|
||||
: m_version_id(version_id), m_architecture(architecture), m_client(client)
|
||||
{
|
||||
if (verstr_to_vernum(game_version.c_str(), reinterpret_cast<unsigned long *>(&this->m_game_version)) < 0)
|
||||
{
|
||||
throw std::runtime_error("Invalid version \"" + game_version + "\" in entry \"" + title + "\"");
|
||||
}
|
||||
|
||||
this->m_checksum = std::stoul(checksum, nullptr, 0);
|
||||
|
||||
// FIXME: check for uniqueness
|
||||
this->m_version_tag = version_tag;
|
||||
}
|
||||
|
||||
std::string VersionCheck::get_version_tag() const
|
||||
{
|
||||
return this->m_version_tag;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
// This function only validates the format of the metadata string
|
||||
// There is no need to validate the information in the string because validating the checksum and file version is sufficient
|
||||
bool validate_file_metadata_format(const std::string& metadata)
|
||||
{
|
||||
if (metadata.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::smatch tokens;
|
||||
if (std::regex_match(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 \"{}\"", metadata);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (const std::regex_error& e)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "{} (code {})", e.what(), e.code());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
return &dummyvc;
|
||||
}
|
||||
|
||||
|
||||
extern int versioncheck_destroy(t_versioncheck * vc)
|
||||
{
|
||||
if (!vc)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL vc");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL vc");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return vc->mpqfile;
|
||||
}
|
||||
|
||||
|
||||
extern char const * versioncheck_get_eqn(t_versioncheck const * vc)
|
||||
{
|
||||
if (!vc)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL vc");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
||||
eventlog(eventlog_level_warn, __FUNCTION__, "parser error while parsing pattern \"{}\"", exeinfo);
|
||||
xfree((void *)parsed_exeinfo->exe);
|
||||
xfree((void *)parsed_exeinfo);
|
||||
return NULL; /* neq */
|
||||
}
|
||||
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)))
|
||||
{
|
||||
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);
|
||||
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));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
extern int versioncheck_unload(void)
|
||||
{
|
||||
t_elem * curr;
|
||||
t_versioninfo * vi;
|
||||
|
||||
if (versioninfo_head)
|
||||
{
|
||||
LIST_TRAVERSE(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 (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);
|
||||
}
|
||||
|
||||
if (list_destroy(versioninfo_head) < 0)
|
||||
return -1;
|
||||
versioninfo_head = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -16,86 +16,81 @@
|
|||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 <tuple>
|
||||
|
||||
#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);
|
||||
|
||||
extern int versioncheck_load(char const * filename);
|
||||
extern int versioncheck_unload(void);
|
||||
// filename, equation
|
||||
std::tuple<std::string, std::string> select_checkrevision(t_tag architecture, t_tag client, std::uint32_t version_id);
|
||||
|
||||
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(t_tag architecture, t_tag client, std::uint32_t version_id,
|
||||
std::uint32_t checkrevision_version, std::uint32_t checkrevision_checksum);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*******************************************************************************/
|
||||
// Conf
|
||||
/*******************************************************************************/
|
||||
bool load_versioncheck_conf(const std::string& filename);
|
||||
void unload_versioncheck_conf();
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class VersionCheck
|
||||
{
|
||||
public:
|
||||
/*******************************************************************************/
|
||||
// Getters
|
||||
/*******************************************************************************/
|
||||
std::string get_version_tag() const;
|
||||
|
||||
/*******************************************************************************/
|
||||
// Deconstructors
|
||||
/*******************************************************************************/
|
||||
~VersionCheck() = default;
|
||||
private:
|
||||
/*******************************************************************************/
|
||||
// Friend functions
|
||||
/*******************************************************************************/
|
||||
friend bool load_versioncheck_conf(const std::string& filename);
|
||||
friend const VersionCheck* select_versioncheck(t_tag architecture, t_tag client, std::uint32_t version_id,
|
||||
std::uint32_t checkrevision_version, std::uint32_t checkrevision_checksum);
|
||||
|
||||
|
||||
/*******************************************************************************/
|
||||
// Constructors
|
||||
/*******************************************************************************/
|
||||
VersionCheck(const std::string& title, std::uint32_t version_id, const std::string& game_version,
|
||||
const std::string& checksum, t_tag architecture, t_tag client, const std::string& version_tag);
|
||||
|
||||
/*******************************************************************************/
|
||||
// Member variables
|
||||
/*******************************************************************************/
|
||||
std::uint32_t m_version_id; // AKA "Version Byte"
|
||||
std::uint32_t m_game_version; // Windows file version
|
||||
std::uint32_t m_checksum;
|
||||
t_tag m_architecture;
|
||||
t_tag m_client;
|
||||
std::string m_version_tag;
|
||||
};
|
||||
|
||||
} // namespace bnetd
|
||||
|
||||
} // namespace pvpgn
|
||||
|
||||
#endif //PVPGN_BNETD_VERSIONCHECK_H
|
|
@ -10,7 +10,7 @@ set(COMMON_SOURCES
|
|||
fdwatch_poll.h fdwatch_select.cpp fdwatch_select.h fdwbackend.cpp
|
||||
fdwbackend.h field_sizes.h file_protocol.h flags.h
|
||||
give_up_root_privileges.cpp give_up_root_privileges.h hashtable.cpp
|
||||
hashtable.h hexdump.cpp hexdump.h init_protocol.h introtate.h
|
||||
hashtable.h hash_tuple.hpp hexdump.cpp hexdump.h init_protocol.h introtate.h
|
||||
irc_protocol.h list.cpp list.h lstr.h make_unique.hpp network.cpp network.h
|
||||
packet.cpp packet.h proginfo.cpp proginfo.h queue.cpp queue.h rcm.cpp rcm.h
|
||||
rlimit.cpp rlimit.h scoped_array.h scoped_ptr.h setup_after.h
|
||||
|
|
67
src/common/hash_tuple.hpp
Normal file
67
src/common/hash_tuple.hpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
#ifndef INCLUDED_PVPGN_HASH_TUPLE_H
|
||||
#define INCLUDED_PVPGN_HASH_TUPLE_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
|
||||
// https://stackoverflow.com/questions/7110301/generic-hash-for-tuples-in-unordered-map-unordered-set
|
||||
namespace pvpgn
|
||||
{
|
||||
namespace hash_tuple
|
||||
{
|
||||
template <typename TT>
|
||||
struct hash
|
||||
{
|
||||
std::size_t operator()(TT const& tt) const
|
||||
{
|
||||
return std::hash<TT>()(tt);
|
||||
}
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
template <class T>
|
||||
inline void hash_combine(std::size_t& seed, T const& v)
|
||||
{
|
||||
seed ^= hash_tuple::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// Recursive template code derived from Matthieu M.
|
||||
template <class Tuple, std::size_t Index = std::tuple_size<Tuple>::value - 1>
|
||||
struct HashValueImpl
|
||||
{
|
||||
static void apply(std::size_t& seed, Tuple const& tuple)
|
||||
{
|
||||
HashValueImpl<Tuple, Index - 1>::apply(seed, tuple);
|
||||
hash_combine(seed, std::get<Index>(tuple));
|
||||
}
|
||||
};
|
||||
|
||||
template <class Tuple>
|
||||
struct HashValueImpl<Tuple, 0>
|
||||
{
|
||||
static void apply(std::size_t& seed, Tuple const& tuple)
|
||||
{
|
||||
hash_combine(seed, std::get<0>(tuple));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <typename ... TT>
|
||||
struct hash<std::tuple<TT...>>
|
||||
{
|
||||
std::size_t operator()(std::tuple<TT...> const& tt) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
HashValueImpl<std::tuple<TT...> >::apply(seed, tt);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -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 = "-_[]";
|
||||
|
|
5370
src/json/json.hpp
5370
src/json/json.hpp
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue