From 5bafcbf3f8722eda616b1e014e480f2597b9b70d Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sun, 29 Mar 2026 15:32:51 -0400 Subject: [PATCH] RRID port part 2 last commit was 78b30802 --- src/d_clisrv.c | 619 ++++++++++++++++++++++++++++++++++++++++---- src/d_clisrv.h | 72 +++++- src/d_net.c | 33 ++- src/d_net.h | 1 + src/d_netcmd.c | 10 +- src/d_netfil.c | 4 +- src/d_player.h | 2 +- src/i_net.h | 3 +- src/i_tcp.c | 19 +- src/lua_playerlib.c | 6 +- src/typedef.h | 3 + 11 files changed, 700 insertions(+), 72 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 553cdbb11..b358ed4ed 100644 --- a/src/d_clisrv.c +++ b/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; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index bf3cf9b27..ce842a2cb 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -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" diff --git a/src/d_net.c b/src/d_net.c index b8d005aac..b53190b07 100644 --- a/src/d_net.c +++ b/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 { diff --git a/src/d_net.h b/src/d_net.h index 6b96e766e..bf96de632 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -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; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 7d1ce792a..234449009 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -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 diff --git a/src/d_netfil.c b/src/d_netfil.c index 2a1af0087..93c1aaff4 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -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)); } diff --git a/src/d_player.h b/src/d_player.h index c1e709e06..fd5b96984 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -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 diff --git a/src/i_net.h b/src/i_net.h index aeb2ffc69..51c108cb1 100644 --- a/src/i_net.h +++ b/src/i_net.h @@ -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); diff --git a/src/i_tcp.c b/src/i_tcp.c index a65d996ff..804ad4417 100644 --- a/src/i_tcp.c +++ b/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; diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 88dda4bee..9436665dc 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -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; diff --git a/src/typedef.h b/src/typedef.h index 9a4ae05c6..1b2614c8a 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -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);