RRID port part 2
last commit was 78b30802
This commit is contained in:
parent
163d4a0e72
commit
5bafcbf3f8
11 changed files with 700 additions and 72 deletions
619
src/d_clisrv.c
619
src/d_clisrv.c
|
|
@ -62,6 +62,7 @@
|
|||
#include "m_cond.h" // netUnlocked
|
||||
#include "g_party.h"
|
||||
#include "k_vote.h"
|
||||
#include "stun.h"
|
||||
|
||||
// cl loading screen
|
||||
#include "v_video.h"
|
||||
|
|
@ -138,6 +139,8 @@ SINT8 nodetoplayer3[MAXNETNODES]; // say the numplayer for this node if any (spl
|
|||
SINT8 nodetoplayer4[MAXNETNODES]; // say the numplayer for this node if any (splitscreen == 3)
|
||||
UINT8 playerpernode[MAXNETNODES]; // used specialy for splitscreen
|
||||
boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
|
||||
boolean nodeneedsauth[MAXNETNODES];
|
||||
|
||||
boolean receivedplayerinfo = false;
|
||||
|
||||
tic_t servermaxping = 20; // server's max delay, in frames. Defaults to 20
|
||||
|
|
@ -183,8 +186,15 @@ char connectedserverdescription[MAXSERVERDESCRIPTION];
|
|||
/// \todo WORK!
|
||||
boolean acceptnewnode = true;
|
||||
|
||||
uint8_t lastReceivedKey[MAXNETNODES][MAXSPLITSCREENPLAYERS][32];
|
||||
uint8_t lastSentChallenge[MAXNETNODES][MAXSPLITSCREENPLAYERS][32];
|
||||
UINT32 ourIP; // Used when populating PT_SERVERCHALLENGE (guards against signature reuse)
|
||||
uint8_t lastReceivedKey[MAXNETNODES][MAXSPLITSCREENPLAYERS][32]; // Player's public key (join process only! active players have it on player_t)
|
||||
uint8_t lastSentChallenge[MAXNETNODES][32]; // The random message we asked them to sign in PT_SERVERCHALLENGE, check it in PT_CLIENTJOIN
|
||||
uint8_t lastChallengeAll[64]; // The message we asked EVERYONE to sign for client-to-client identity proofs
|
||||
uint8_t lastReceivedSignature[MAXPLAYERS][64]; // Everyone's response to lastChallengeAll
|
||||
uint8_t knownWhenChallenged[MAXPLAYERS][32]; // Everyone a client saw at the moment a challenge should be initiated
|
||||
boolean expectChallenge = false; // Were we in-game before a client-to-client challenge should have been sent?
|
||||
|
||||
uint8_t priorKeys[MAXPLAYERS][32]; // Make a note of keys before consuming a new gamestate, and if the server tries to send us a gamestate where keys differ, assume shenanigans
|
||||
|
||||
boolean serverisfull = false; //lets us be aware if the server was full after we check files, but before downloading, so we can ask if the user still wants to download or not
|
||||
tic_t firstconnectattempttime = 0;
|
||||
|
|
@ -192,7 +202,15 @@ tic_t firstconnectattempttime = 0;
|
|||
uint8_t awaitingChallenge[32];
|
||||
|
||||
#ifdef DEVELOP
|
||||
consvar_t cv_sigfail = CVAR_INIT ("sigfail", "Off", CV_SAVE, CV_OnOff, NULL);
|
||||
consvar_t cv_badjoin = CVAR_INIT ("badjoin", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_badtraffic = CVAR_INIT ("badtraffic", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_badresponse = CVAR_INIT ("badresponse", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_noresponse = CVAR_INIT ("noresponse", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_nochallenge = CVAR_INIT ("nochallenge", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_badresults = CVAR_INIT ("badresults", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_noresults = CVAR_INIT ("noresults", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_badtime = CVAR_INIT ("badtime", "0", 0, CV_Unsigned, NULL);
|
||||
consvar_t cv_badip = CVAR_INIT ("badip", "0", 0, CV_Unsigned, NULL);
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -236,6 +254,77 @@ consvar_t cv_kicktime = CVAR_INIT ("kicktime", "10", CV_SAVE, CV_Unsigned, NULL)
|
|||
|
||||
static tic_t stop_spamming[MAXPLAYERS];
|
||||
|
||||
// https://github.com/jameds/holepunch/blob/master/holepunch.c#L75
|
||||
static int IsExternalAddress (const void *p)
|
||||
{
|
||||
const int a = ((const unsigned char*)p)[0];
|
||||
const int b = ((const unsigned char*)p)[1];
|
||||
|
||||
if (*(const int*)p == ~0)/* 255.255.255.255 */
|
||||
return 0;
|
||||
|
||||
switch (a)
|
||||
{
|
||||
case 0:
|
||||
case 10:
|
||||
case 127:
|
||||
return 0;
|
||||
case 172:
|
||||
return (b & ~15) != 16;/* 16 - 31 */
|
||||
case 192:
|
||||
return b != 168;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a message for an authenticating client to sign, with some guarantees about who we are.
|
||||
void GenerateChallenge(uint8_t *buf)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
csprng(buf, sizeof(&buf)); // Random noise as a baseline, but...
|
||||
memcpy(buf, &now, sizeof(now)); // Timestamp limits the reuse window.
|
||||
memcpy(buf + sizeof(now), &ourIP, sizeof(ourIP)); // IP prevents captured signatures from being used elsewhere.
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_badtime.value)
|
||||
{
|
||||
CV_AddValue(&cv_badtime, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_badtime enabled, trashing time in auth message\n");
|
||||
memset(buf, 0, sizeof(now));
|
||||
}
|
||||
|
||||
if (cv_badip.value)
|
||||
{
|
||||
CV_AddValue(&cv_badip, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_badip enabled, trashing IP in auth message\n");
|
||||
memset(buf + sizeof(now), 0, sizeof(ourIP));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Modified servers can throw softballs or reuse challenges.
|
||||
// Don't sign anything that wasn't generated just for us!
|
||||
shouldsign_t ShouldSignChallenge(uint8_t *message)
|
||||
{
|
||||
time_t then, now;
|
||||
UINT32 claimedIP, realIP;
|
||||
|
||||
now = time(NULL);
|
||||
memcpy(&then, message, sizeof(then));
|
||||
memcpy(&claimedIP, message + sizeof(then), sizeof(claimedIP));
|
||||
CONS_Printf("servernode: %d\n", servernode);
|
||||
realIP = I_GetNodeAddressInt(servernode);
|
||||
|
||||
if (abs(now - then) > 60*5)
|
||||
return SIGN_BADTIME;
|
||||
|
||||
if (realIP != claimedIP && IsExternalAddress(&realIP))
|
||||
return SIGN_BADIP;
|
||||
|
||||
return SIGN_OK;
|
||||
}
|
||||
|
||||
static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n)
|
||||
{
|
||||
const size_t d = n / sizeof(ticcmd_t);
|
||||
|
|
@ -920,20 +1009,44 @@ static boolean CL_SendJoin(void)
|
|||
// Don't leak old signatures from prior sessions.
|
||||
memset(&netbuffer->u.clientcfg.challengeResponse, 0, sizeof(((clientconfig_pak *)0)->challengeResponse));
|
||||
|
||||
if (client && netgame)
|
||||
{
|
||||
shouldsign_t safe = ShouldSignChallenge(awaitingChallenge);
|
||||
|
||||
if (safe != SIGN_OK)
|
||||
{
|
||||
if (safe == SIGN_BADIP)
|
||||
{
|
||||
I_Error("External server IP didn't match the message it sent.");
|
||||
}
|
||||
else if (safe == SIGN_BADTIME)
|
||||
{
|
||||
I_Error("External server sent a message with an unusual timestamp.\nCheck your clocks!");
|
||||
}
|
||||
else
|
||||
{
|
||||
I_Error("External server asked for a signature on something strange.\nPlease notify a developer if you've seen this more than once.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
uint8_t signature[64];
|
||||
|
||||
// If our keys are garbage (corrupted profile?), fail here instead of when the server boots us, so the player knows what's going on.
|
||||
crypto_eddsa_sign(signature, secret_key[i], awaitingChallenge, 32);
|
||||
if (crypto_eddsa_check(signature, public_key[i], awaitingChallenge, 32) != 0)
|
||||
I_Error("Couldn't self-verify key associated with player %d.\nkey may be corrupted.\n", i); // I guess this is the most reasonable way to catch a malformed key.
|
||||
I_Error("Couldn't self-verify key associated with player %d.\nKey may be corrupted.\n", i); // I guess this is the most reasonable way to catch a malformed key.
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_sigfail.value)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "SIGFAIL enabled, scrubbing signature from CL_SendJoin\n");
|
||||
memset(signature, 0, 64);
|
||||
}
|
||||
if (cv_badjoin.value)
|
||||
{
|
||||
CV_AddValue(&cv_badjoin, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_badjoin enabled, scrubbing signature from CL_SendJoin\n");
|
||||
memset(signature, 0, 64);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Testing
|
||||
|
|
@ -1475,6 +1588,19 @@ static void CL_LoadReceivedSavegame(boolean reloading)
|
|||
// so they know they can resume the game
|
||||
netbuffer->packettype = PT_RECEIVEDGAMESTATE;
|
||||
HSendPacket(servernode, true, 0, 0);
|
||||
|
||||
if (reloading)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (memcmp(priorKeys[i], players[i].public_key, sizeof(priorKeys[i])) != 0)
|
||||
{
|
||||
HandleSigfail("Gamestate reload contained new keys");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CL_ReloadReceivedSavegame(void)
|
||||
|
|
@ -2912,6 +3038,8 @@ void CL_Reset(void)
|
|||
serverisfull = false;
|
||||
connectiontimeout = (tic_t)cv_nettimeout.value; //reset this temporary hack
|
||||
|
||||
expectChallenge = false;
|
||||
|
||||
#ifdef HAVE_CURL
|
||||
curl_failedwebdownload = false;
|
||||
curl_transfers = 0;
|
||||
|
|
@ -3730,9 +3858,7 @@ void D_ClientServerInit(void)
|
|||
COM_AddCommand("drop", Command_Drop);
|
||||
COM_AddCommand("droprate", Command_Droprate);
|
||||
#endif
|
||||
#ifdef _DEBUG
|
||||
COM_AddCommand("numnodes", Command_Numnodes);
|
||||
#endif
|
||||
|
||||
RegisterNetXCmd(XD_KICK, Got_KickCmd);
|
||||
RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer);
|
||||
|
|
@ -3757,6 +3883,8 @@ static void ResetNode(INT32 node)
|
|||
{
|
||||
nodeingame[node] = false;
|
||||
nodewaiting[node] = 0;
|
||||
nodeneedsauth[node] = false;
|
||||
//CONS_Printf("2: node %d -> %d\n", node, nodeneedsauth[node]);
|
||||
|
||||
nettics[node] = gametic;
|
||||
supposedtics[node] = gametic;
|
||||
|
|
@ -3842,6 +3970,8 @@ void SV_ResetServer(void)
|
|||
|
||||
CV_RevertNetVars();
|
||||
|
||||
expectChallenge = false;
|
||||
|
||||
DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n");
|
||||
}
|
||||
|
||||
|
|
@ -3933,6 +4063,9 @@ static inline void SV_AddNode(INT32 node)
|
|||
// nodeingame when connected not here
|
||||
if (node)
|
||||
nodeingame[node] = true;
|
||||
|
||||
nodeneedsauth[node] = false;
|
||||
CONS_Printf("3: node %d -> %d\n", node, nodeneedsauth[node]);
|
||||
}
|
||||
|
||||
// Xcmd XD_ADDPLAYER
|
||||
|
|
@ -3966,6 +4099,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
|
|||
newplayer->jointime = 0;
|
||||
|
||||
READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME);
|
||||
READSTRINGN(*p, players[newplayernum].public_key, 32);
|
||||
|
||||
console = READUINT8(*p);
|
||||
splitscreenplayer = READUINT8(*p);
|
||||
|
|
@ -4003,20 +4137,11 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
|
|||
|
||||
D_SendPlayerConfig(splitscreenplayer);
|
||||
addedtogame = true;
|
||||
|
||||
CONS_Printf("It's me, node %d, with ID %s! (This is uninitialized memory because Tyron is a nice person :) )\n", node, GetPrettyRRID(lastReceivedKey[node][splitscreenplayer], true));
|
||||
memcpy(lastReceivedKey[node][splitscreenplayer], public_key[splitscreenplayer], 32);
|
||||
}
|
||||
|
||||
players[newplayernum].splitscreenindex = splitscreenplayer;
|
||||
players[newplayernum].bot = false;
|
||||
|
||||
// player_t is the only place in the game that a key is null-terminated, for ease of Lua push.
|
||||
memset(players[newplayernum].public_key, 0, 32 + 1);
|
||||
CONS_Printf("Adding player from node %d with ID %s\n", node, GetPrettyRRID(lastReceivedKey[node][splitscreenplayer], true));
|
||||
memcpy(players[newplayernum].public_key, lastReceivedKey[node][splitscreenplayer], sizeof(players[newplayernum].public_key));
|
||||
CONS_Printf("Node %d now has ID %s\n", node, GetPrettyRRID(players[newplayernum].public_key, true));
|
||||
|
||||
// Previously called at the top of this function, commented as
|
||||
// "caused desyncs in this spot :(". But we can't do this in
|
||||
// G_PlayerReborn, since that only runs for level contexts and
|
||||
|
|
@ -4109,10 +4234,12 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
|||
K_SetBot(newplayernum, skinnum, difficulty, style);
|
||||
}
|
||||
|
||||
static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities, const char *name, const char *name2, const char *name3, const char *name4)
|
||||
static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities,
|
||||
const char *name, uint8_t *key, const char *name2, uint8_t *key2,
|
||||
const char *name3, uint8_t *key3, const char *name4, uint8_t *key4)
|
||||
{
|
||||
INT32 n, newplayernum, i;
|
||||
UINT8 buf[4 + MAXPLAYERNAME + MAXAVAILABILITY];
|
||||
UINT8 buf[4 + MAXPLAYERNAME + 32 + MAXAVAILABILITY];
|
||||
UINT8 *buf_p = buf;
|
||||
boolean newplayer = false;
|
||||
|
||||
|
|
@ -4175,21 +4302,25 @@ static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities, const cha
|
|||
{
|
||||
nodetoplayer[node] = newplayernum;
|
||||
WRITESTRINGN(buf_p, name, MAXPLAYERNAME);
|
||||
WRITESTRINGN(buf_p, key, 32);
|
||||
}
|
||||
else if (playerpernode[node] < 2)
|
||||
{
|
||||
nodetoplayer2[node] = newplayernum;
|
||||
WRITESTRINGN(buf_p, name2, MAXPLAYERNAME);
|
||||
WRITESTRINGN(buf_p, key2, 32);
|
||||
}
|
||||
else if (playerpernode[node] < 3)
|
||||
{
|
||||
nodetoplayer3[node] = newplayernum;
|
||||
WRITESTRINGN(buf_p, name3, MAXPLAYERNAME);
|
||||
WRITESTRINGN(buf_p, key3, 32);
|
||||
}
|
||||
else if (playerpernode[node] < 4)
|
||||
{
|
||||
nodetoplayer4[node] = newplayernum;
|
||||
WRITESTRINGN(buf_p, name4, MAXPLAYERNAME);
|
||||
WRITESTRINGN(buf_p, key4, 32);
|
||||
}
|
||||
|
||||
WRITEUINT8(buf_p, nodetoplayer[node]); // consoleplayer
|
||||
|
|
@ -4303,6 +4434,15 @@ void CL_RemoveSplitscreenPlayer(UINT8 p)
|
|||
SendKick(p, KICK_MSG_PLAYER_QUIT);
|
||||
}
|
||||
|
||||
static void GotOurIP(UINT32 address)
|
||||
{
|
||||
const unsigned char * p = (const unsigned char *)&address;
|
||||
#ifdef DEVELOP
|
||||
CONS_Printf("Got IP of %u.%u.%u.%u\n", p[0], p[1], p[2], p[3]);
|
||||
#endif
|
||||
ourIP = address;
|
||||
}
|
||||
|
||||
// is there a game running
|
||||
boolean Playing(void)
|
||||
{
|
||||
|
|
@ -4340,6 +4480,10 @@ boolean SV_SpawnServer(void)
|
|||
else doomcom->numslots = 1;
|
||||
}
|
||||
|
||||
ourIP = 0;
|
||||
if (netgame && server)
|
||||
STUN_bind(GotOurIP);
|
||||
|
||||
// strictly speaking, i'm not convinced the following is necessary
|
||||
// but I'm not confident enough to remove it entirely in case it breaks something
|
||||
|
||||
|
|
@ -4350,7 +4494,9 @@ boolean SV_SpawnServer(void)
|
|||
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities();
|
||||
SINT8 node = 0;
|
||||
for (; node < MAXNETNODES; node++)
|
||||
result |= SV_AddWaitingPlayers(node, availabilitiesbuffer, cv_playername[0].zstring, cv_playername[1].zstring, cv_playername[2].zstring, cv_playername[3].zstring);
|
||||
result |= SV_AddWaitingPlayers(node, availabilitiesbuffer, cv_playername[0].zstring, public_key[0], cv_playername[1].zstring, public_key[1],
|
||||
cv_playername[2].zstring, public_key[2], cv_playername[3].zstring, public_key[3]);
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -4452,6 +4598,8 @@ static void HandleConnect(SINT8 node)
|
|||
UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value);
|
||||
UINT8 connectedplayers = 0;
|
||||
|
||||
CONS_Printf(">>>> node %d (%s)\n", node, I_GetNodeAddress(node));
|
||||
|
||||
for (i = dedicated ? 1 : 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
// We use this to count players because it is affected by SV_AddWaitingPlayers when
|
||||
|
|
@ -4474,9 +4622,6 @@ static void HandleConnect(SINT8 node)
|
|||
}
|
||||
}
|
||||
|
||||
// Testing
|
||||
// memset(netbuffer->u.clientcfg.challengeResponse, 0, sizeof(netbuffer->u.clientcfg.challengeResponse));
|
||||
|
||||
if (bannednode && bannednode[node].banid != SIZE_MAX)
|
||||
{
|
||||
const char *reason = NULL;
|
||||
|
|
@ -4572,17 +4717,15 @@ static void HandleConnect(SINT8 node)
|
|||
return;
|
||||
}
|
||||
|
||||
if (node == 0) // Server
|
||||
if (node == 0) // Hey, that's us. We're always allowed to do what we want.
|
||||
{
|
||||
memcpy(lastReceivedKey[node][i], public_key[i], sizeof(lastReceivedKey[node][i]));
|
||||
CONS_Printf("We're SERVER! Setting lastReceivedKey on node %d to %s\n", node, GetPrettyRRID(lastReceivedKey[node][i], true));
|
||||
|
||||
}
|
||||
else
|
||||
else // Remote player, gotta check their signature.
|
||||
{
|
||||
CONS_Printf("Adding client. Doing sigcheck for node %d, ID %s\n", node, GetPrettyRRID(lastReceivedKey[i][node], true));
|
||||
CONS_Printf("Adding remote. Doing sigcheck for node %d, ID %s\n", node, GetPrettyRRID(lastReceivedKey[i][node], true));
|
||||
|
||||
sigcheck = crypto_eddsa_check(netbuffer->u.clientcfg.challengeResponse[i], lastReceivedKey[node][i], lastSentChallenge[node][i], 32);
|
||||
sigcheck = crypto_eddsa_check(netbuffer->u.clientcfg.challengeResponse[i], lastReceivedKey[node][i], lastSentChallenge[node], 32);
|
||||
|
||||
if (netgame && sigcheck != 0)
|
||||
{
|
||||
|
|
@ -4628,7 +4771,8 @@ static void HandleConnect(SINT8 node)
|
|||
SV_SendSaveGame(node, false); // send a complete game state
|
||||
DEBFILE("send savegame\n");
|
||||
}
|
||||
SV_AddWaitingPlayers(node, availabilitiesbuffer, names[0], names[1], names[2], names[3]);
|
||||
SV_AddWaitingPlayers(node, availabilitiesbuffer, names[0], lastReceivedKey[node][0], names[1], lastReceivedKey[node][1],
|
||||
names[2], lastReceivedKey[node][2], names[3], lastReceivedKey[node][3]);
|
||||
joindelay += cv_joindelay.value * TICRATE;
|
||||
player_joining = true;
|
||||
}
|
||||
|
|
@ -4700,6 +4844,21 @@ static void HandleTimeout(SINT8 node)
|
|||
M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING);
|
||||
}
|
||||
|
||||
void HandleSigfail(const char *string)
|
||||
{
|
||||
if (server) // This situation is basically guaranteed to be nonsense.
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "Auth error! %s\n", string);
|
||||
return; // Keep the game running, you're probably testing.
|
||||
}
|
||||
|
||||
LUA_HookBool(false, HOOK(GameQuit));
|
||||
D_QuitNetGame();
|
||||
CL_Reset();
|
||||
D_StartTitle();
|
||||
M_StartMessage(va(M_GetText("Signature check failed\n(%s)\nPress Esc\n"),string), NULL, MM_NOTHING);
|
||||
}
|
||||
|
||||
/** Called when a PT_SERVERINFO packet is received
|
||||
*
|
||||
* \param node The packet sender
|
||||
|
|
@ -5322,6 +5481,8 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole)
|
|||
}
|
||||
Net_CloseConnection(node);
|
||||
nodeingame[node] = false;
|
||||
nodeneedsauth[node] = false;
|
||||
CONS_Printf("1: node %d -> %d\n", node, nodeneedsauth[node]);
|
||||
}
|
||||
|
||||
static void PT_CanReceiveGamestate(SINT8 node)
|
||||
|
|
@ -5478,6 +5639,14 @@ static void PT_WillResendGamestate(SINT8 node)
|
|||
if (server || cl_redownloadinggamestate)
|
||||
return;
|
||||
|
||||
// Don't let the server pull a fast one with everyone's identity!
|
||||
// Save the public keys we see, so if the server tries to swap one, we'll know.
|
||||
int i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
memcpy(priorKeys[i], players[i].public_key, sizeof(priorKeys[i]));
|
||||
}
|
||||
|
||||
// Send back a PT_CANRECEIVEGAMESTATE packet to the server
|
||||
// so they know they can start sending the game state
|
||||
netbuffer->packettype = PT_CANRECEIVEGAMESTATE;
|
||||
|
|
@ -5680,9 +5849,28 @@ static void HandlePacketFromAwayNode(SINT8 node)
|
|||
case PT_PLAYERINFO : HandlePlayerInfo (node ); break;
|
||||
case PT_CLIENTKEY:
|
||||
if (server)
|
||||
{
|
||||
PT_ClientKey(node);
|
||||
|
||||
// Client's not in the server yet, but we still need to lock up the node.
|
||||
// Otherwise, someone else could request a challenge on the same node and trash it.
|
||||
nodeneedsauth[node] = true;
|
||||
freezetimeout[node] = I_GetTime() + jointimeout;
|
||||
|
||||
CONS_Printf("4: node %d -> %d\n", node, nodeneedsauth[node]);
|
||||
if (nodeneedsauth[node] == false)
|
||||
{
|
||||
freezetimeout[node] = I_GetTime() + jointimeout;
|
||||
nodeneedsauth[node] = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PT_SERVERCHALLENGE:
|
||||
if (server && serverrunning && node != servernode)
|
||||
{
|
||||
Net_CloseConnection(node);
|
||||
break;
|
||||
}
|
||||
if (cl_mode != CL_WAITCHALLENGE)
|
||||
break;
|
||||
memcpy(awaitingChallenge, netbuffer->u.serverchallenge.secret, sizeof(awaitingChallenge));
|
||||
|
|
@ -5695,6 +5883,19 @@ static void HandlePacketFromAwayNode(SINT8 node)
|
|||
}
|
||||
}
|
||||
|
||||
static char NodeToSplitPlayer(int node, int split)
|
||||
{
|
||||
if (split == 0)
|
||||
return nodetoplayer[node];
|
||||
else if (split == 1)
|
||||
return nodetoplayer2[node];
|
||||
else if (split == 2)
|
||||
return nodetoplayer3[node];
|
||||
else if (split == 3)
|
||||
return nodetoplayer4[node];
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Handles a packet received from a node that is in game
|
||||
*
|
||||
* \param node The packet sender
|
||||
|
|
@ -5716,6 +5917,8 @@ static void HandlePacketFromPlayer(SINT8 node)
|
|||
if (netconsole >= MAXPLAYERS)
|
||||
I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole);
|
||||
#endif
|
||||
|
||||
#ifdef SIGNGAMETRAFFIC
|
||||
if (server)
|
||||
{
|
||||
int splitnodes;
|
||||
|
|
@ -5723,29 +5926,35 @@ static void HandlePacketFromPlayer(SINT8 node)
|
|||
{
|
||||
for (splitnodes = 0; splitnodes < MAXSPLITSCREENPLAYERS; splitnodes++)
|
||||
{
|
||||
// Don't try to enforce signatures for players that aren't present.
|
||||
if (splitnodes > 0 && nodetoplayer2[node] <= 0)
|
||||
break;
|
||||
if (splitnodes > 1 && nodetoplayer3[node] <= 0)
|
||||
break;
|
||||
if (splitnodes > 2 && nodetoplayer4[node] <= 0)
|
||||
break;
|
||||
int targetplayer = NodeToSplitPlayer(node, splitnodes);
|
||||
if (targetplayer == -1)
|
||||
continue;
|
||||
|
||||
const void* message = &netbuffer->u;
|
||||
if (crypto_eddsa_check(netbuffer->signature[splitnodes], lastReceivedKey[node][splitnodes], message, doomcom->datalength - BASEPACKETSIZE))
|
||||
|
||||
if (demo.playback)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "SIGFAIL! Packet type %d from node %d player %d\nkey %s size %d netconsole %d\n",
|
||||
netbuffer->packettype, node, splitnodes,
|
||||
GetPrettyRRID(lastReceivedKey[node][splitnodes], true), doomcom->datalength - BASEPACKETSIZE, netconsole);
|
||||
if (netconsole != -1) // NO IDEA.
|
||||
SendKick(netconsole, KICK_MSG_SIGFAIL);
|
||||
Net_CloseConnection(node);
|
||||
nodeingame[node] = false;
|
||||
return;
|
||||
//CONS_Printf("Throwing out a guest signature from node %d player %d\n", node, splitnodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (crypto_eddsa_check(netbuffer->signature[splitnodes], players[targetplayer].public_key, message, doomcom->datalength - BASEPACKETSIZE))
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "SIGFAIL! Packet type %d from node %d player %d\nkey %s size %d netconsole %d\n",
|
||||
netbuffer->packettype, node, splitnodes,
|
||||
GetPrettyRRID(players[targetplayer].public_key, true), doomcom->datalength - BASEPACKETSIZE, netconsole);
|
||||
|
||||
if (netconsole != -1) // NO IDEA.
|
||||
SendKick(netconsole, KICK_MSG_SIGFAIL);
|
||||
Net_CloseConnection(node);
|
||||
nodeingame[node] = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (netbuffer->packettype)
|
||||
{
|
||||
|
|
@ -5786,6 +5995,141 @@ static void HandlePacketFromPlayer(SINT8 node)
|
|||
case PT_WILLRESENDGAMESTATE: PT_WillResendGamestate(node ); break;
|
||||
case PT_SENDINGLUAFILE : PT_SendingLuaFile (node ); break;
|
||||
case PT_SERVERCFG : break;
|
||||
case PT_CHALLENGEALL: ; // -Wpedantic
|
||||
if (demo.playback || node != servernode) // SERVER should still respond to this to prove its own identity, just not from clients.
|
||||
break;
|
||||
|
||||
int challengeplayers;
|
||||
|
||||
memcpy(lastChallengeAll, netbuffer->u.challengeall.secret, sizeof(lastChallengeAll));
|
||||
|
||||
shouldsign_t safe = ShouldSignChallenge(lastChallengeAll);
|
||||
if (safe != SIGN_OK)
|
||||
{
|
||||
if (safe == SIGN_BADIP)
|
||||
HandleSigfail("External server sent the wrong IP");
|
||||
else if (safe == SIGN_BADTIME)
|
||||
HandleSigfail("Bad timestamp - check your clocks");
|
||||
else
|
||||
HandleSigfail("Unknown auth error - contact a developer");
|
||||
break;
|
||||
}
|
||||
|
||||
netbuffer->packettype = PT_RESPONSEALL;
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_noresponse.value)
|
||||
{
|
||||
CV_AddValue(&cv_noresponse, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_noresponse enabled, not sending PT_RESPONSEALL\n");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Don't leak uninitialized memory.
|
||||
memset(&netbuffer->u.responseall, 0, sizeof(netbuffer->u.responseall));
|
||||
|
||||
for (challengeplayers = 0; challengeplayers <= splitscreen; challengeplayers++)
|
||||
{
|
||||
uint8_t signature[64];
|
||||
CONS_Printf("signing %s pk %s\n", GetPrettyRRID(lastChallengeAll, true), GetPrettyRRID(public_key[challengeplayers], true));
|
||||
|
||||
crypto_eddsa_sign(signature, secret_key[challengeplayers], lastChallengeAll, sizeof(lastChallengeAll));
|
||||
if (crypto_eddsa_check(signature, public_key[challengeplayers], lastChallengeAll, sizeof(lastChallengeAll)) != 0)
|
||||
I_Error("Couldn't self-verify key associated with player %d.\nKey may be corrupted.", challengeplayers);
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_badresponse.value)
|
||||
{
|
||||
CV_AddValue(&cv_badresponse, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_badresponse enabled, scrubbing signature from PT_RESPONSEALL\n");
|
||||
memset(signature, 0, 64);
|
||||
}
|
||||
#endif
|
||||
|
||||
memcpy(netbuffer->u.responseall.signature[challengeplayers], signature, sizeof(signature));
|
||||
}
|
||||
|
||||
HSendPacket(servernode, true, 0, sizeof(netbuffer->u.responseall));
|
||||
break;
|
||||
case PT_RESPONSEALL:
|
||||
if (demo.playback || client)
|
||||
break;
|
||||
|
||||
int responseplayer;
|
||||
for (responseplayer = 0; responseplayer < MAXSPLITSCREENPLAYERS; responseplayer++)
|
||||
{
|
||||
int targetplayer = NodeToSplitPlayer(node, responseplayer);
|
||||
if (targetplayer == -1)
|
||||
continue;
|
||||
|
||||
CONS_Printf("receiving %s pk %s\n", GetPrettyRRID(lastChallengeAll, true), GetPrettyRRID(players[targetplayer].public_key, true));
|
||||
if (crypto_eddsa_check(netbuffer->u.responseall.signature[responseplayer], players[targetplayer].public_key, lastChallengeAll, sizeof(lastChallengeAll)))
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "Invalid PT_RESPONSEALL from node %d player %d split %d\n", node, targetplayer, responseplayer);
|
||||
if (playernode[targetplayer] != 0) // NO IDEA.
|
||||
SendKick(targetplayer, KICK_MSG_SIGFAIL);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(lastReceivedSignature[targetplayer], netbuffer->u.responseall.signature[responseplayer], sizeof(lastReceivedSignature[targetplayer]));
|
||||
CONS_Printf("Writing signature %s for node %d player %d split %d\n", GetPrettyRRID(lastReceivedSignature[targetplayer], true), node, targetplayer, responseplayer);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PT_RESULTSALL: ; // -Wpedantic
|
||||
int resultsplayer;
|
||||
uint8_t allzero[64];
|
||||
memset(allzero, 0, sizeof(allzero));
|
||||
|
||||
CONS_Printf("Got PT_RESULTSALL\n");
|
||||
|
||||
if (demo.playback || server || node != servernode || !expectChallenge)
|
||||
break;
|
||||
|
||||
CONS_Printf("Checking PT_RESULTSALL\n");
|
||||
|
||||
for (resultsplayer = 0; resultsplayer < MAXPLAYERS; resultsplayer++)
|
||||
{
|
||||
if (!playeringame[resultsplayer])
|
||||
{
|
||||
CONS_Printf("Player %d isn't in the game, excluded from checkall\n", resultsplayer);
|
||||
continue;
|
||||
}
|
||||
else if (memcmp(knownWhenChallenged[resultsplayer], allzero, sizeof(allzero)) == 0)
|
||||
{
|
||||
CONS_Printf("That motherfucker wasn't here for the challenge - node %d player %d split %d, not enforcing\n", playernode[resultsplayer], resultsplayer, players[resultsplayer].splitscreenindex);
|
||||
continue;
|
||||
}
|
||||
else if (memcmp(knownWhenChallenged[resultsplayer], players[resultsplayer].public_key, sizeof(knownWhenChallenged[resultsplayer])) != 0)
|
||||
{
|
||||
// A player left after the challenge process started, and someone else took their place.
|
||||
// That means they haven't received a challenge either.
|
||||
CONS_Printf("Has key %s but I remember key %s - node %d player %d split %d, not enforcing\n",
|
||||
GetPrettyRRID(knownWhenChallenged[resultsplayer], true), GetPrettyRRID(players[resultsplayer].public_key, true),
|
||||
playernode[resultsplayer], resultsplayer, players[resultsplayer].splitscreenindex);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (crypto_eddsa_check(netbuffer->u.resultsall.signature[resultsplayer],
|
||||
knownWhenChallenged[resultsplayer], lastChallengeAll, sizeof(lastChallengeAll)))
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "PT_RESULTSALL had invalid signature %s for node %d player %d split %d, something doesn't add up!\n",
|
||||
GetPrettyRRID(netbuffer->u.resultsall.signature[resultsplayer], true), playernode[resultsplayer], resultsplayer, players[resultsplayer].splitscreenindex);
|
||||
HandleSigfail("Server sent invalid client signature.");
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONS_Printf("Checkall client-pass for node %d player %d split %d\n", playernode[resultsplayer], resultsplayer, players[resultsplayer].splitscreenindex);
|
||||
}
|
||||
}
|
||||
}
|
||||
csprng(lastChallengeAll, sizeof(lastChallengeAll));
|
||||
expectChallenge = false;
|
||||
break;
|
||||
default:
|
||||
DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n",
|
||||
netbuffer->packettype, node));
|
||||
|
|
@ -6799,6 +7143,179 @@ static void UpdatePingTable(void)
|
|||
}
|
||||
}
|
||||
|
||||
// It's that time again! Send everyone a safe message to sign, so we can show off their signature and prove we're playing fair.
|
||||
// It's that time again! Send everyone a safe message to sign, so we can show off their signature and prove we're playing fair.
|
||||
static void SendChallenges(void)
|
||||
{
|
||||
int i;
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
netbuffer->packettype = PT_CHALLENGEALL;
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_nochallenge.value)
|
||||
{
|
||||
CV_AddValue(&cv_nochallenge, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_nochallenge enabled, not sending PT_CHALLENGEALL\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
memset(knownWhenChallenged, 0, sizeof(knownWhenChallenged));
|
||||
|
||||
GenerateChallenge(netbuffer->u.challengeall.secret);
|
||||
memcpy(lastChallengeAll, netbuffer->u.challengeall.secret, sizeof(lastChallengeAll));
|
||||
|
||||
memset(lastReceivedSignature, 0, sizeof(lastReceivedSignature));
|
||||
|
||||
for (i = 0; i < MAXNETNODES; i++)
|
||||
{
|
||||
if (nodeingame[i])
|
||||
{
|
||||
CONS_Printf("challenge to node %d, player %d\n", i, nodetoplayer[i]);
|
||||
HSendPacket(i, true, 0, sizeof(challengeall_pak));
|
||||
memcpy(knownWhenChallenged[nodetoplayer[i]], players[nodetoplayer[i]].public_key, sizeof(knownWhenChallenged[nodetoplayer[i]]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Before we start sending out the results, we need to kick everyone who didn't respond.
|
||||
// (If we try to do both at once, clients will still see players who failled in-game when the results arrive...)
|
||||
static void KickUnverifiedPlayers(void)
|
||||
{
|
||||
int i;
|
||||
uint8_t allZero[64];
|
||||
memset(allZero, 0, sizeof(allZero));
|
||||
|
||||
CONS_Printf("KickUnverifiedPlayers start\n");
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
if (memcmp(lastReceivedSignature[i], allZero, sizeof(allZero)) == 0) // We never got a response!
|
||||
{
|
||||
CONS_Printf("No sig from %d\n", i);
|
||||
CONS_Printf("pk then %s, pk now %s\n", GetPrettyRRID(knownWhenChallenged[i], true), GetPrettyRRID(players[i].public_key, true));
|
||||
if (memcmp(&knownWhenChallenged[i], &players[i].public_key, sizeof(knownWhenChallenged[i])) == 0)
|
||||
{
|
||||
if (playernode[i] != servernode)
|
||||
{
|
||||
CONS_Printf("We never got a response from player %d, goodbye\n", i);
|
||||
SendKick(i, KICK_MSG_SIGFAIL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
static void SendChallengeResults(void)
|
||||
{
|
||||
int i;
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
netbuffer->packettype = PT_RESULTSALL;
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_noresults.value)
|
||||
{
|
||||
CV_AddValue(&cv_noresults, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_noresults enabled, not sending PT_RESULTSALL\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t allZero[64];
|
||||
memset(allZero, 0, sizeof(allZero));
|
||||
|
||||
memset(&netbuffer->u.resultsall, 0, sizeof(netbuffer->u.resultsall));
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
|
||||
// Don't try to transmit signatures for players who didn't get here in time to send one.
|
||||
// (Everyone who had their chance should have been kicked by KickUnverifiedPlayers by now.)
|
||||
if (memcmp(lastReceivedSignature[i], allZero, sizeof(allZero)) == 0)
|
||||
continue;
|
||||
|
||||
CONS_Printf("Player %d passed with key %s sig %s, adding...\n", i, GetPrettyRRID(players[i].public_key, true), GetPrettyRRID(lastReceivedSignature[i], true));
|
||||
memcpy(netbuffer->u.resultsall.signature[i], lastReceivedSignature[i], sizeof(netbuffer->u.resultsall.signature[i]));
|
||||
#ifdef DEVELOP
|
||||
if (cv_badresults.value)
|
||||
{
|
||||
CV_AddValue(&cv_badresults, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_badresults enabled, scrubbing signature from PT_RESULTSALL\n");
|
||||
memset(netbuffer->u.resultsall.signature[i], 0, sizeof(netbuffer->u.resultsall.signature[i]));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXNETNODES; i++)
|
||||
{
|
||||
if (nodeingame[i])
|
||||
{
|
||||
CONS_Printf("results to node %d, player %d\n", i, nodetoplayer[i]);
|
||||
HSendPacket(i, true, 0, sizeof(resultsall_pak));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Who should we try to verify when results come in?
|
||||
// Store a public key for every active slot, so if players shuffle during challenge leniency,
|
||||
// we don't incorrectly try to verify someone who didn't even get a challenge, throw a tantrum, and bail.
|
||||
static void CheckPresentPlayers(void)
|
||||
{
|
||||
int i;
|
||||
memset(knownWhenChallenged, 0, sizeof(knownWhenChallenged));
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
{
|
||||
//CONS_Printf("Player %i isn't present for checkall\n", i);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONS_Printf("Player %d (node %d split %d) is present for checkall, make a note of their key %s...\n", i, playernode[i], players[i].splitscreenindex,
|
||||
GetPrettyRRID(players[i].public_key, true));
|
||||
memcpy(knownWhenChallenged[i], players[i].public_key, sizeof(knownWhenChallenged[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle "client-to-client" auth challenge flow.
|
||||
static void UpdateChallenges(void)
|
||||
{
|
||||
if (!(Playing() && netgame))
|
||||
return;
|
||||
|
||||
if (server)
|
||||
{
|
||||
if (leveltime == CHALLENGEALL_START)
|
||||
SendChallenges();
|
||||
|
||||
if (leveltime == CHALLENGEALL_KICKUNRESPONSIVE)
|
||||
KickUnverifiedPlayers();
|
||||
|
||||
if (leveltime == CHALLENGEALL_SENDRESULTS)
|
||||
SendChallengeResults();
|
||||
|
||||
}
|
||||
else // client
|
||||
{
|
||||
if (leveltime <= CHALLENGEALL_START)
|
||||
expectChallenge = true;
|
||||
|
||||
if (leveltime == CHALLENGEALL_START)
|
||||
CheckPresentPlayers();
|
||||
|
||||
if (leveltime > CHALLENGEALL_CLIENTCUTOFF && expectChallenge)
|
||||
HandleSigfail("Didn't receive client signatures.");
|
||||
}
|
||||
}
|
||||
|
||||
static void RenewHolePunch(void)
|
||||
{
|
||||
static time_t past;
|
||||
|
|
@ -6820,7 +7337,7 @@ static void HandleNodeTimeouts(void)
|
|||
if (server)
|
||||
{
|
||||
for (i = 1; i < MAXNETNODES; i++)
|
||||
if (nodeingame[i] && freezetimeout[i] < I_GetTime())
|
||||
if ((nodeingame[i] || nodeneedsauth[i]) && freezetimeout[i] < I_GetTime())
|
||||
Net_ConnectionTimeout(i);
|
||||
|
||||
// In case the cvar value was lowered
|
||||
|
|
@ -6950,6 +7467,8 @@ void NetUpdate(void)
|
|||
|
||||
UpdatePingTable();
|
||||
|
||||
UpdateChallenges();
|
||||
|
||||
if (client)
|
||||
maketic = neededtic;
|
||||
|
||||
|
|
|
|||
|
|
@ -142,9 +142,20 @@ typedef enum
|
|||
PT_CLIENTKEY, // "Here's my public key"
|
||||
PT_SERVERCHALLENGE, // "Prove it"
|
||||
|
||||
PT_CHALLENGEALL, // Prove to the other clients you are who you say you are, sign this random bullshit!
|
||||
PT_RESPONSEALL, // OK, here is my signature on that random bullshit
|
||||
PT_RESULTSALL, // Here's what everyone responded to PT_CHALLENGEALL with, if this is wrong or you don't receive it disconnect
|
||||
|
||||
NUMPACKETTYPE
|
||||
} packettype_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SIGN_OK,
|
||||
SIGN_BADTIME, // Timestamp differs by too much, suspect reuse of an old challenge.
|
||||
SIGN_BADIP // Asked to sign the wrong IP by an external host, suspect reuse of another server's challenge.
|
||||
} shouldsign_t;
|
||||
|
||||
typedef struct consistancy_s
|
||||
{
|
||||
INT16 checksum; // Total consistancy checksum
|
||||
|
|
@ -176,9 +187,7 @@ void SPrintConsistancy(char *out, consistancy_t *c);
|
|||
void Command_Drop(void);
|
||||
void Command_Droprate(void);
|
||||
#endif
|
||||
#ifdef _DEBUG
|
||||
void Command_Numnodes(void);
|
||||
#endif
|
||||
|
||||
// Client to server packet
|
||||
struct clientcmd_pak
|
||||
|
|
@ -392,12 +401,27 @@ struct reqmapqueue_pak
|
|||
|
||||
struct clientkey_pak
|
||||
{
|
||||
char key[MAXSPLITSCREENPLAYERS][32];
|
||||
uint8_t key[MAXSPLITSCREENPLAYERS][32];
|
||||
} ATTRPACK;
|
||||
|
||||
struct serverchallenge_pak
|
||||
{
|
||||
char secret[MAXSPLITSCREENPLAYERS][32];
|
||||
uint8_t secret[32];
|
||||
} ATTRPACK;
|
||||
|
||||
struct challengeall_pak
|
||||
{
|
||||
uint8_t secret[64];
|
||||
} ATTRPACK;
|
||||
|
||||
struct responseall_pak
|
||||
{
|
||||
uint8_t signature[MAXSPLITSCREENPLAYERS][64];
|
||||
} ATTRPACK;
|
||||
|
||||
struct resultsall_pak
|
||||
{
|
||||
uint8_t signature[MAXPLAYERS][64];
|
||||
} ATTRPACK;
|
||||
|
||||
struct netinfo_pak
|
||||
|
|
@ -425,7 +449,9 @@ struct doomdata_t
|
|||
UINT8 ackreturn; // The return of the ack number
|
||||
|
||||
UINT8 packettype;
|
||||
#ifdef SIGNGAMETRAFFIC
|
||||
uint8_t signature[MAXSPLITSCREENPLAYERS][64];
|
||||
#endif
|
||||
UINT8 packetindex;
|
||||
union
|
||||
{
|
||||
|
|
@ -453,8 +479,11 @@ struct doomdata_t
|
|||
netinfo_pak netinfo;
|
||||
say_pak say;
|
||||
reqmapqueue_pak reqmapqueue; // Formerly XD_REQMAPQUEUE
|
||||
clientkey_pak clientkey; // TODO: Tyron, does anyone take any of these sizes even remotely seriously
|
||||
serverchallenge_pak serverchallenge; // Are you even going to update this shit, are you even going to remove this comment
|
||||
clientkey_pak clientkey; // 32 bytes
|
||||
serverchallenge_pak serverchallenge; // 64 bytes
|
||||
challengeall_pak challengeall; // 256 bytes
|
||||
responseall_pak responseall; // 256 bytes
|
||||
resultsall_pak resultsall; // 1024 bytes. Also, you really shouldn't trust anything here.
|
||||
} u; // This is needed to pack diff packet types data together
|
||||
} ATTRPACK;
|
||||
|
||||
|
|
@ -524,6 +553,14 @@ extern char connectedservername[MAXSERVERNAME];
|
|||
extern char connectedservercontact[MAXSERVERCONTACT];
|
||||
extern char connectedserverdescription[MAXSERVERDESCRIPTION];
|
||||
|
||||
extern UINT32 ourIP;
|
||||
extern uint8_t lastReceivedKey[MAXNETNODES][MAXSPLITSCREENPLAYERS][32];
|
||||
extern uint8_t lastSentChallenge[MAXNETNODES][32];
|
||||
extern uint8_t lastChallengeAll[64];
|
||||
extern uint8_t lastReceivedSignature[MAXPLAYERS][64];
|
||||
extern uint8_t knownWhenChallenged[MAXPLAYERS][32];
|
||||
extern boolean expectChallenge;
|
||||
|
||||
void Command_Ping_f(void);
|
||||
extern tic_t connectiontimeout;
|
||||
extern tic_t jointimeout;
|
||||
|
|
@ -548,13 +585,24 @@ extern consvar_t cv_joinnextround;
|
|||
extern consvar_t cv_discordinvites;
|
||||
|
||||
#ifdef DEVELOP
|
||||
extern consvar_t cv_sigfail;
|
||||
extern consvar_t cv_badjoin;
|
||||
extern consvar_t cv_badtraffic;
|
||||
extern consvar_t cv_badresponse;
|
||||
extern consvar_t cv_noresponse;
|
||||
extern consvar_t cv_nochallenge;
|
||||
extern consvar_t cv_badresults;
|
||||
extern consvar_t cv_noresults;
|
||||
extern consvar_t cv_badtime;
|
||||
extern consvar_t cv_badip;
|
||||
#endif
|
||||
|
||||
// Used in d_net, the only dependence
|
||||
tic_t ExpandTics(INT32 low, tic_t basetic);
|
||||
void D_ClientServerInit(void);
|
||||
|
||||
void GenerateChallenge(uint8_t *buf);
|
||||
shouldsign_t ShouldSignChallenge(uint8_t *message);
|
||||
|
||||
// Initialise the other field
|
||||
void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum));
|
||||
void SendNetXCmdForPlayer(UINT8 playerid, netxcmd_t id, const void *param, size_t nparam);
|
||||
|
|
@ -698,12 +746,18 @@ void CL_ClearRewinds(void);
|
|||
rewind_t *CL_SaveRewindPoint(size_t demopos);
|
||||
rewind_t *CL_RewindToTime(tic_t time);
|
||||
|
||||
void HandleSigfail(const char *string);
|
||||
|
||||
void DoSayPacket(SINT8 target, UINT8 flags, UINT8 source, char *message);
|
||||
void DoSayPacketFromCommand(SINT8 target, size_t usedargs, UINT8 flags);
|
||||
void SendServerNotice(SINT8 target, char *message);
|
||||
|
||||
extern uint8_t lastReceivedKey[MAXNETNODES][MAXSPLITSCREENPLAYERS][32];
|
||||
extern uint8_t lastSentChallenge[MAXNETNODES][MAXSPLITSCREENPLAYERS][32];
|
||||
// We give clients a chance to verify each other once per race.
|
||||
// When is that challenge sent, and when should clients bail if they don't receive the responses?
|
||||
#define CHALLENGEALL_START (TICRATE*10)
|
||||
#define CHALLENGEALL_KICKUNRESPONSIVE (TICRATE*12)
|
||||
#define CHALLENGEALL_SENDRESULTS (TICRATE*14)
|
||||
#define CHALLENGEALL_CLIENTCUTOFF (TICRATE*16)
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
|
|
|||
33
src/d_net.c
33
src/d_net.c
|
|
@ -82,6 +82,7 @@ boolean (*I_NetOpenSocket)(void) = NULL;
|
|||
boolean (*I_Ban) (INT32 node) = NULL;
|
||||
void (*I_ClearBans)(void) = NULL;
|
||||
const char *(*I_GetNodeAddress) (INT32 node) = NULL;
|
||||
UINT32 (*I_GetNodeAddressInt) (INT32 node) = NULL;
|
||||
const char *(*I_GetBanAddress) (size_t ban) = NULL;
|
||||
const char *(*I_GetBanMask) (size_t ban) = NULL;
|
||||
const char *(*I_GetBanUsername) (size_t ban) = NULL;
|
||||
|
|
@ -656,12 +657,31 @@ static const char *packettypename[NUMPACKETTYPE] =
|
|||
"TEXTCMD2",
|
||||
"TEXTCMD3",
|
||||
"TEXTCMD4",
|
||||
|
||||
"TELLFILESNEEDED",
|
||||
"MOREFILESNEEDED",
|
||||
|
||||
"CLIENTJOIN",
|
||||
"NODETIMEOUT",
|
||||
|
||||
"LOGIN",
|
||||
"LOGINCHALLENGE",
|
||||
"LOGINAUTH",
|
||||
|
||||
"PING"
|
||||
"PING",
|
||||
|
||||
"SERVERINFOUPDATE",
|
||||
|
||||
"SAY",
|
||||
|
||||
"REQMAPQUEUE",
|
||||
|
||||
"CLIENTKEY",
|
||||
"SERVERCHALLENGE",
|
||||
|
||||
"CHALLENGEALL",
|
||||
"RESPONSEALL",
|
||||
"RESULTSALL"
|
||||
};
|
||||
|
||||
static void DebugPrintpacket(const char *header)
|
||||
|
|
@ -844,6 +864,8 @@ static boolean ShouldDropPacket(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
// Unused because Eidolon correctly pointed out that +512b on every packet was scary.
|
||||
#ifdef SIGNGAMETRAFFIC
|
||||
boolean IsPacketSigned(int packettype)
|
||||
{
|
||||
switch (packettype)
|
||||
|
|
@ -868,6 +890,7 @@ boolean IsPacketSigned(int packettype)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// HSendPacket
|
||||
|
|
@ -877,6 +900,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
|
|||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
doomcom->datalength = (INT16)(packetlength + BASEPACKETSIZE);
|
||||
|
||||
#ifdef SIGNGAMETRAFFIC
|
||||
if (IsPacketSigned(netbuffer->packettype))
|
||||
{
|
||||
int i;
|
||||
|
|
@ -889,11 +913,13 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
|
|||
}
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_sigfail.value)
|
||||
if (cv_badtraffic.value)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "SIGFAIL enabled, scrubbing signature from HSendPacket\n");
|
||||
CV_AddValue(&cv_badtraffic, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_badtraffic enabled, scrubbing signature from HSendPacket\n");
|
||||
memset(netbuffer->signature, 0, sizeof(netbuffer->signature));
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
else
|
||||
|
|
@ -901,6 +927,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
|
|||
//CONS_Printf("NOT signing PT_%d of length %d, it doesn't need to be\n", netbuffer->packettype, packetlength);
|
||||
memset(netbuffer->signature, 0, sizeof(netbuffer->signature));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (node == 0) // Packet is to go back to us
|
||||
{
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ extern SINT8 nodetoplayer3[MAXNETNODES]; // Say the numplayer for this node if a
|
|||
extern SINT8 nodetoplayer4[MAXNETNODES]; // Say the numplayer for this node if any (splitscreen == 3)
|
||||
extern UINT8 playerpernode[MAXNETNODES]; // Used specially for splitscreen
|
||||
extern boolean nodeingame[MAXNETNODES]; // Set false as nodes leave game
|
||||
extern boolean nodeneedsauth[MAXNETNODES];
|
||||
|
||||
extern boolean serverrunning;
|
||||
|
||||
|
|
|
|||
|
|
@ -1348,7 +1348,15 @@ void D_RegisterClientCommands(void)
|
|||
CV_RegisterVar(&cv_mindelay);
|
||||
|
||||
#ifdef DEVELOP
|
||||
CV_RegisterVar(&cv_sigfail);
|
||||
CV_RegisterVar(&cv_badjoin);
|
||||
CV_RegisterVar(&cv_badtraffic);
|
||||
CV_RegisterVar(&cv_badresponse);
|
||||
CV_RegisterVar(&cv_noresponse);
|
||||
CV_RegisterVar(&cv_nochallenge);
|
||||
CV_RegisterVar(&cv_badresults);
|
||||
CV_RegisterVar(&cv_noresults);
|
||||
CV_RegisterVar(&cv_badtime);
|
||||
CV_RegisterVar(&cv_badip);
|
||||
#endif
|
||||
|
||||
// HUD
|
||||
|
|
|
|||
|
|
@ -1341,6 +1341,8 @@ void PT_FileReceived(SINT8 node)
|
|||
SV_EndFileSend(doomcom->remotenode);
|
||||
}
|
||||
|
||||
// Someone knocked on the door with their public key.
|
||||
// Give them a challenge to sign in their PT_CLIENTJOIN.
|
||||
void PT_ClientKey(INT32 node)
|
||||
{
|
||||
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
|
||||
|
|
@ -1351,8 +1353,8 @@ void PT_ClientKey(INT32 node)
|
|||
CONS_Printf("Got keys from node %d, %s / %s / %s / %s\n", node, GetPrettyRRID(lastReceivedKey[node][0], true), GetPrettyRRID(lastReceivedKey[node][1], true), GetPrettyRRID(lastReceivedKey[node][2], true), GetPrettyRRID(lastReceivedKey[node][3], true));
|
||||
|
||||
netbuffer->packettype = PT_SERVERCHALLENGE;
|
||||
GenerateChallenge(lastSentChallenge[node]);
|
||||
|
||||
csprng(lastSentChallenge[node], sizeof(serverchallenge_pak));
|
||||
memcpy(&netbuffer->u.serverchallenge, lastSentChallenge[node], sizeof(serverchallenge_pak));
|
||||
HSendPacket(node, false, 0, sizeof (serverchallenge_pak));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -890,7 +890,7 @@ struct player_t
|
|||
boolean walltransfered;
|
||||
UINT8 walltransferboost;
|
||||
|
||||
uint8_t public_key[32 + 1];
|
||||
uint8_t public_key[32];
|
||||
|
||||
#ifdef HWRENDER
|
||||
fixed_t fovadd; // adjust FOV for hw rendering
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
/// \def MAXPACKETLENGTH
|
||||
/// For use in a LAN
|
||||
#define MAXPACKETLENGTH 2048
|
||||
#define MAXPACKETLENGTH 4096
|
||||
/// \def INETPACKETLENGTH
|
||||
/// For use on the internet
|
||||
#define INETPACKETLENGTH 1450
|
||||
|
|
@ -157,6 +157,7 @@ extern void (*I_NetRegisterHolePunch)(void);
|
|||
extern boolean (*I_Ban) (INT32 node);
|
||||
extern void (*I_ClearBans)(void);
|
||||
extern const char *(*I_GetNodeAddress) (INT32 node);
|
||||
extern UINT32 (*I_GetNodeAddressInt) (INT32 node);
|
||||
extern const char *(*I_GetBanAddress) (size_t ban);
|
||||
extern const char *(*I_GetBanMask) (size_t ban);
|
||||
extern const char *(*I_GetBanUsername) (size_t ban);
|
||||
|
|
|
|||
19
src/i_tcp.c
19
src/i_tcp.c
|
|
@ -457,6 +457,20 @@ static const char *SOCK_GetNodeAddress(INT32 node)
|
|||
return SOCK_AddrToStr(&clientaddress[node]);
|
||||
}
|
||||
|
||||
static UINT32 SOCK_GetNodeAddressInt(INT32 node)
|
||||
{
|
||||
if (nodeconnected[node] && clientaddress[node].any.sa_family == AF_INET)
|
||||
{
|
||||
return clientaddress[node].ip4.sin_addr.s_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
I_Error("SOCK_GetNodeAddressInt: Node %d is not IPv4!\n", node);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *SOCK_GetBanAddress(size_t ban)
|
||||
{
|
||||
if (ban >= numbans)
|
||||
|
|
@ -549,7 +563,7 @@ static void cleanupnodes(void)
|
|||
|
||||
// Why can't I start at zero?
|
||||
for (j = 1; j < MAXNETNODES; j++)
|
||||
if (!(nodeingame[j] || SendingFile(j)))
|
||||
if (!(nodeingame[j] || nodeneedsauth[j] || SendingFile(j)))
|
||||
nodeconnected[j] = false;
|
||||
}
|
||||
|
||||
|
|
@ -579,7 +593,6 @@ static SINT8 getfreenode(void)
|
|||
return -1;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
void Command_Numnodes(void)
|
||||
{
|
||||
INT32 connected = 0;
|
||||
|
|
@ -617,7 +630,6 @@ void Command_Numnodes(void)
|
|||
"Ingame: %d\n",
|
||||
connected, ingame);
|
||||
}
|
||||
#endif
|
||||
|
||||
static boolean hole_punch(ssize_t c)
|
||||
{
|
||||
|
|
@ -1782,6 +1794,7 @@ boolean I_InitTcpNetwork(void)
|
|||
I_Ban = SOCK_Ban;
|
||||
I_ClearBans = SOCK_ClearBans;
|
||||
I_GetNodeAddress = SOCK_GetNodeAddress;
|
||||
I_GetNodeAddressInt = SOCK_GetNodeAddressInt;
|
||||
I_GetBanAddress = SOCK_GetBanAddress;
|
||||
I_GetBanMask = SOCK_GetBanMask;
|
||||
I_GetBanUsername = SOCK_GetBanUsername;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "lua_libs.h"
|
||||
#include "lua_hud.h" // hud_running errors
|
||||
#include "lua_hook.h" // hook_cmd_running errors
|
||||
#include "m_misc.h" // GetPrettyRRID
|
||||
|
||||
static int lib_iteratePlayers(lua_State *L)
|
||||
{
|
||||
|
|
@ -1196,7 +1197,7 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, playerpingtable[( plr - players )]);
|
||||
break;
|
||||
case player_public_key:
|
||||
lua_pushstring(L, plr->public_key);
|
||||
lua_pushstring(L, GetPrettyRRID(plr->public_key, false));
|
||||
break;
|
||||
case player_packetloss:
|
||||
lua_pushinteger(L, playerpacketlosstable[plr - players]);
|
||||
|
|
@ -1953,8 +1954,7 @@ static int player_set(lua_State *L)
|
|||
case player_botvars:
|
||||
return NOSET;
|
||||
case player_jointime:
|
||||
plr->jointime = (tic_t)luaL_checkinteger(L, 3);
|
||||
break;
|
||||
return NOSET;
|
||||
case player_spectatorreentry:
|
||||
plr->spectatorreentry = (tic_t)luaL_checkinteger(L, 3);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@ TYPEDEF (say_pak);
|
|||
TYPEDEF (reqmapqueue_pak);
|
||||
TYPEDEF (clientkey_pak);
|
||||
TYPEDEF (serverchallenge_pak);
|
||||
TYPEDEF (challengeall_pak);
|
||||
TYPEDEF (responseall_pak);
|
||||
TYPEDEF (resultsall_pak);
|
||||
|
||||
// d_event.h
|
||||
TYPEDEF (event_t);
|
||||
|
|
|
|||
Loading…
Reference in a new issue