From 18f04155584c87a958f78827954d428ce55a8aaf Mon Sep 17 00:00:00 2001 From: divverent Date: Mon, 27 Jun 2016 12:49:37 +0000 Subject: [PATCH] Add a cvar net_sourceaddresscheck (default 1) to ignore connect replies from the wrong host. This fixes some existing easy connect takeover exploits (which can, without this check, even attack local matches). From: Rudolf Polzer git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@12258 d7cf8633-e32d-0410-b094-e92efae38249 --- client.h | 1 + crypto.c | 47 ++++++++++++++++++++++++++++++----------------- host_cmd.c | 30 ++++++++++++------------------ netconn.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 85 insertions(+), 39 deletions(-) diff --git a/client.h b/client.h index d9facec2..4b6e5660 100644 --- a/client.h +++ b/client.h @@ -834,6 +834,7 @@ typedef struct client_static_s double connect_nextsendtime; lhnetsocket_t *connect_mysocket; lhnetaddress_t connect_address; + lhnetaddress_t rcon_address; // protocol version of the server we're connected to // (kept outside client_state_t because it's used between levels) protocolversion_t protocol; diff --git a/crypto.c b/crypto.c index 79489532..00bcf0a1 100644 --- a/crypto.c +++ b/crypto.c @@ -7,16 +7,21 @@ #include "libcurl.h" cvar_t crypto_developer = {CVAR_SAVE, "crypto_developer", "0", "print extra info about crypto handshake"}; +cvar_t crypto_aeslevel = {CVAR_SAVE, "crypto_aeslevel", "1", "whether to support AES encryption in authenticated connections (0 = no, 1 = supported, 2 = requested, 3 = required)"}; + cvar_t crypto_servercpupercent = {CVAR_SAVE, "crypto_servercpupercent", "10", "allowed crypto CPU load in percent for server operation (0 = no limit, faster)"}; cvar_t crypto_servercpumaxtime = {CVAR_SAVE, "crypto_servercpumaxtime", "0.01", "maximum allowed crypto CPU time per frame (0 = no limit)"}; cvar_t crypto_servercpudebug = {CVAR_SAVE, "crypto_servercpudebug", "0", "print statistics about time usage by crypto"}; static double crypto_servercpu_accumulator = 0; static double crypto_servercpu_lastrealtime = 0; -cvar_t crypto_aeslevel = {CVAR_SAVE, "crypto_aeslevel", "1", "whether to support AES encryption in authenticated connections (0 = no, 1 = supported, 2 = requested, 3 = required)"}; + +extern cvar_t net_sourceaddresscheck; + int crypto_keyfp_recommended_length; static const char *crypto_idstring = NULL; static char crypto_idstring_buf[512]; + #define PROTOCOL_D0_BLIND_ID FOURCC_D0PK #define PROTOCOL_VLEN (('v' << 0) | ('l' << 8) | ('e' << 16) | ('n' << 24)) @@ -1727,7 +1732,7 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in, break; // if the challenge is not recognized, drop the packet if (i == MAX_CHALLENGES) // challenge mismatch is silent - return CRYPTO_DISCARD; // pre-challenge: rather be silent + return Crypto_SoftServerError(data_out, len_out, "missing challenge in connect"); crypto = Crypto_ServerFindInstance(peeraddress, false); if(!crypto || !crypto->authenticated) @@ -1742,23 +1747,23 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in, id = (cnt ? atoi(cnt) : -1); cnt = InfoString_GetValue(string + 4, "cnt", infostringvalue, sizeof(infostringvalue)); if(!cnt) - return CRYPTO_DISCARD; // pre-challenge: rather be silent + return Crypto_SoftServerError(data_out, len_out, "missing cnt in d0pk"); GetUntilNul(&data_in, &len_in); if(!data_in) - return CRYPTO_DISCARD; // pre-challenge: rather be silent + return Crypto_SoftServerError(data_out, len_out, "missing appended data in d0pk"); if(!strcmp(cnt, "0")) { int i; if (!(s = InfoString_GetValue(string + 4, "challenge", infostringvalue, sizeof(infostringvalue)))) - return CRYPTO_DISCARD; // pre-challenge: rather be silent + return Crypto_SoftServerError(data_out, len_out, "missing challenge in d0pk\\0"); // validate the challenge for (i = 0;i < MAX_CHALLENGES;i++) if(challenge[i].time > 0) if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s)) break; // if the challenge is not recognized, drop the packet - if (i == MAX_CHALLENGES) // challenge mismatch is silent - return CRYPTO_DISCARD; // pre-challenge: rather be silent + if (i == MAX_CHALLENGES) + return Crypto_SoftServerError(data_out, len_out, "invalid challenge in d0pk\\0"); if (!(s = InfoString_GetValue(string + 4, "aeslevel", infostringvalue, sizeof(infostringvalue)))) aeslevel = 0; // not supported @@ -2075,7 +2080,7 @@ static int Crypto_ClientError(char *data_out, size_t *len_out, const char *msg) static int Crypto_SoftClientError(char *data_out, size_t *len_out, const char *msg) { *len_out = 0; - Con_Printf("%s\n", msg); + Con_DPrintf("%s\n", msg); return CRYPTO_DISCARD; } @@ -2185,6 +2190,10 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, int wantserver_aeslevel = 0; qboolean wantserver_issigned = false; + // Must check the source IP here, if we want to prevent other servers' replies from falsely advancing the crypto state, preventing successful connect to the real server. + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) + return Crypto_SoftClientError(data_out, len_out, "challenge message from wrong server"); + // if we have a stored host key for the server, assume serverid to already be selected! // (the loop will refuse to overwrite this one then) wantserver_idfp[0] = 0; @@ -2201,7 +2210,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, GetUntilNul(&data_in, &len_in); if(!data_in) return (wantserverid >= 0) ? Crypto_ClientError(data_out, len_out, "Server tried an unauthenticated connection even though a host key is present") : - (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ServerError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)", NULL) : + (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ClientError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)") : CRYPTO_NOMATCH; // FTEQW extension protocol @@ -2237,7 +2246,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if(!vlen_blind_id_ptr) return (wantserverid >= 0) ? Crypto_ClientError(data_out, len_out, "Server tried an unauthenticated connection even though authentication is required") : - (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ServerError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)", NULL) : + (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ClientError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)") : CRYPTO_NOMATCH; data_in = vlen_blind_id_ptr; @@ -2302,7 +2311,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, default: // dummy, never happens, but to make gcc happy... case 0: if(wantserver_aeslevel >= 3) - return Crypto_ServerError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)", NULL); + return Crypto_ClientError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)"); CDATA->wantserver_aes = false; break; case 1: @@ -2313,7 +2322,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, break; case 3: if(wantserver_aeslevel <= 0) - return Crypto_ServerError(data_out, len_out, "This server requires encryption to be supported (crypto_aeslevel >= 1, and d0_rijndael library must be present)", NULL); + return Crypto_ClientError(data_out, len_out, "This server requires encryption to be supported (crypto_aeslevel >= 1, and d0_rijndael library must be present)"); CDATA->wantserver_aes = true; break; } @@ -2373,7 +2382,6 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, data_out_p += *len_out; *len_out = data_out_p - data_out; } - return CRYPTO_DISCARD; } else @@ -2381,7 +2389,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if(wantserver_idfp[0]) // if we know a host key, honor its encryption setting if(wantserver_aeslevel >= 3) return Crypto_ClientError(data_out, len_out, "Server insists on encryption, but neither can authenticate to the other"); - return (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ServerError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)", NULL) : + return (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ClientError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)") : CRYPTO_NOMATCH; } } @@ -2389,6 +2397,11 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, { const char *cnt; int id; + + // Must check the source IP here, if we want to prevent other servers' replies from falsely advancing the crypto state, preventing successful connect to the real server. + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) + return Crypto_SoftClientError(data_out, len_out, "d0pk\\ message from wrong server"); + cnt = InfoString_GetValue(string + 4, "id", infostringvalue, sizeof(infostringvalue)); id = (cnt ? atoi(cnt) : -1); cnt = InfoString_GetValue(string + 4, "cnt", infostringvalue, sizeof(infostringvalue)); @@ -2402,7 +2415,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, { if(id >= 0) if(CDATA->cdata_id != id) - return Crypto_SoftServerError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); + return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); if(CDATA->next_step != 1) return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\cnt\\%s when expecting %d", cnt, CDATA->next_step)); @@ -2451,7 +2464,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if(id >= 0) if(CDATA->cdata_id != id) - return Crypto_SoftServerError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); + return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); if(CDATA->next_step != 3) return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\cnt\\%s when expecting %d", cnt, CDATA->next_step)); @@ -2533,7 +2546,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if(id >= 0) if(CDATA->cdata_id != id) - return Crypto_SoftServerError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); + return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); if(CDATA->next_step != 5) return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\cnt\\%s when expecting %d", cnt, CDATA->next_step)); diff --git a/host_cmd.c b/host_cmd.c index 10012cc9..1f4048fd 100644 --- a/host_cmd.c +++ b/host_cmd.c @@ -2552,9 +2552,7 @@ static void Host_PQRcon_f (void) { int n; const char *e; - lhnetaddress_t to; lhnetsocket_t *mysocket; - char peer_address[64]; if (Cmd_Argc() == 1) { @@ -2572,9 +2570,7 @@ static void Host_PQRcon_f (void) n = e ? e-rcon_password.string : (int)strlen(rcon_password.string); if (cls.netcon) - { - InfoString_GetValue(cls.userinfo, "*ip", peer_address, sizeof(peer_address)); - } + cls.rcon_address = cls.netcon->peeraddress; else { if (!rcon_address.string[0]) @@ -2582,10 +2578,9 @@ static void Host_PQRcon_f (void) Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n"); return; } - strlcpy(peer_address, rcon_address.string, strlen(rcon_address.string)+1); + LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer); } - LHNETADDRESS_FromString(&to, peer_address, sv_netport.integer); - mysocket = NetConn_ChooseClientSocketForAddress(&to); + mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address); if (mysocket) { sizebuf_t buf; @@ -2598,7 +2593,7 @@ static void Host_PQRcon_f (void) MSG_WriteByte(&buf, 0); // terminate the (possibly partial) string MSG_WriteString(&buf, Cmd_Args()); StoreBigLong(buf.data, NETFLAG_CTL | (buf.cursize & NETFLAG_LENGTH_MASK)); - NetConn_Write(mysocket, buf.data, buf.cursize, &to); + NetConn_Write(mysocket, buf.data, buf.cursize, &cls.rcon_address); SZ_Clear(&buf); } } @@ -2619,7 +2614,6 @@ static void Host_Rcon_f (void) // credit: taken from QuakeWorld { int i, n; const char *e; - lhnetaddress_t to; lhnetsocket_t *mysocket; char vabuf[1024]; @@ -2639,7 +2633,7 @@ static void Host_Rcon_f (void) // credit: taken from QuakeWorld n = e ? e-rcon_password.string : (int)strlen(rcon_password.string); if (cls.netcon) - to = cls.netcon->peeraddress; + cls.rcon_address = cls.netcon->peeraddress; else { if (!rcon_address.string[0]) @@ -2647,9 +2641,9 @@ static void Host_Rcon_f (void) // credit: taken from QuakeWorld Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n"); return; } - LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer); + LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer); } - mysocket = NetConn_ChooseClientSocketForAddress(&to); + mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address); if (mysocket && Cmd_Args()[0]) { // simply put together the rcon packet and send it @@ -2665,13 +2659,13 @@ static void Host_Rcon_f (void) // credit: taken from QuakeWorld } for (i = 0;i < MAX_RCONS;i++) if(cls.rcon_commands[i][0]) - if (!LHNETADDRESS_Compare(&to, &cls.rcon_addresses[i])) + if (!LHNETADDRESS_Compare(&cls.rcon_address, &cls.rcon_addresses[i])) break; ++cls.rcon_trying; if(i >= MAX_RCONS) - NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // otherwise we'll request the challenge later + NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &cls.rcon_address); // otherwise we'll request the challenge later strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos])); - cls.rcon_addresses[cls.rcon_ringpos] = to; + cls.rcon_addresses[cls.rcon_ringpos] = cls.rcon_address; cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value; cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS; } @@ -2685,12 +2679,12 @@ static void Host_Rcon_f (void) // credit: taken from QuakeWorld { buf[40] = ' '; strlcpy(buf + 41, argbuf, sizeof(buf) - 41); - NetConn_Write(mysocket, buf, 41 + (int)strlen(buf + 41), &to); + NetConn_Write(mysocket, buf, 41 + (int)strlen(buf + 41), &cls.rcon_address); } } else { - NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377rcon %.*s %s", n, rcon_password.string, Cmd_Args()), &to); + NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377rcon %.*s %s", n, rcon_password.string, Cmd_Args()), &cls.rcon_address); } } } diff --git a/netconn.c b/netconn.c index a01c174c..0d9f69e4 100755 --- a/netconn.c +++ b/netconn.c @@ -82,6 +82,7 @@ cvar_t net_connecttimeout = {0, "net_connecttimeout","15", "after requesting a c cvar_t net_connectfloodblockingtimeout = {0, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods). Note that this does not include retries from the same IP; these are handled earlier and let in."}; cvar_t net_challengefloodblockingtimeout = {0, "net_challengefloodblockingtimeout", "0.5", "when a challenge packet is received, it will block all future challenge packets from that IP address for this many seconds (cuts down on challenge floods). DarkPlaces clients retry once per second, so this should be <= 1. Failure here may lead to connect attempts failing."}; cvar_t net_getstatusfloodblockingtimeout = {0, "net_getstatusfloodblockingtimeout", "1", "when a getstatus packet is received, it will block all future getstatus packets from that IP address for this many seconds (cuts down on getstatus floods). DarkPlaces retries every 4 seconds, and qstat retries once per second, so this should be <= 1. Failure here may lead to server not showing up in the server list."}; +cvar_t net_sourceaddresscheck = {0, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"}; cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"}; cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"}; @@ -1904,8 +1905,12 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat { // darkplaces or quake3 char protocolnames[1400]; - Protocol_Names(protocolnames, sizeof(protocolnames)); Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2); + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) { + Con_DPrintf("challenge message from wrong server %s\n", addressstring2); + return true; + } + Protocol_Names(protocolnames, sizeof(protocolnames)); #ifdef CONFIG_MENU M_Update_Return_Reason("Got challenge response"); #endif @@ -1918,6 +1923,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying) { // darkplaces or quake3 + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) { + Con_DPrintf("accept message from wrong server %s\n", addressstring2); + return true; + } #ifdef CONFIG_MENU M_Update_Return_Reason("Accepted"); #endif @@ -1927,6 +1936,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying) { char rejectreason[128]; + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) { + Con_DPrintf("reject message from wrong server %s\n", addressstring2); + return true; + } cls.connect_trying = false; string += 7; length = min(length - 7, (int)sizeof(rejectreason) - 1); @@ -2097,6 +2110,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying) { // challenge message + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) { + Con_DPrintf("c message from wrong server %s\n", addressstring2); + return true; + } Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2); #ifdef CONFIG_MENU M_Update_Return_Reason("Got QuakeWorld challenge response"); @@ -2110,6 +2127,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat if (length >= 1 && string[0] == 'j' && cls.connect_trying) { // accept message + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) { + Con_DPrintf("j message from wrong server %s\n", addressstring2); + return true; + } #ifdef CONFIG_MENU M_Update_Return_Reason("QuakeWorld Accepted"); #endif @@ -2167,7 +2188,11 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat } if (string[0] == 'n') { - // qw print command + // qw print command, used by rcon replies too + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) { + Con_DPrintf("n message from wrong server %s\n", addressstring2); + return true; + } Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1); } // we may not have liked the packet, but it was a command packet, so @@ -2203,6 +2228,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat if (cls.connect_trying) { lhnetaddress_t clientportaddress; + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) { + Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2); + break; + } clientportaddress = *peeraddress; LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message)); // extra ProQuake stuff @@ -2229,8 +2258,12 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat } break; case CCREP_REJECT: - if (developer_extra.integer) - Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2); + if (developer_extra.integer) { + Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2); + break; + } + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) + break; cls.connect_trying = false; #ifdef CONFIG_MENU M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring))); @@ -2261,6 +2294,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat #endif break; case CCREP_RCON: // RocketGuy: ProQuake rcon support + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) { + Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2); + break; + } if (developer_extra.integer) Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2); @@ -3835,6 +3872,7 @@ void NetConn_Init(void) Cvar_RegisterVariable(&net_connectfloodblockingtimeout); Cvar_RegisterVariable(&net_challengefloodblockingtimeout); Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout); + Cvar_RegisterVariable(&net_sourceaddresscheck); Cvar_RegisterVariable(&cl_netlocalping); Cvar_RegisterVariable(&cl_netpacketloss_send); Cvar_RegisterVariable(&cl_netpacketloss_receive); -- 2.39.2