#define DPMASTER_PORT 27950
// note this defaults on for dedicated servers, off for listen servers
-cvar_t sv_public = {0, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect"};
+cvar_t sv_public = {0, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect; -3: already block at getchallenge level"};
+cvar_t sv_public_rejectreason = {0, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
+extern cvar_t sv_status_privacy;
static cvar_t sv_masters [] =
{
{0, "sv_masterextra1", "69.59.212.88", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
{0, "sv_masterextra2", "64.22.107.125", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
{0, "sv_masterextra3", "92.62.40.73", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
+#ifdef SUPPORTIPV6
+ {0, "sv_masterextra4", "[2001:41d0:2:1628::4450]:27950", "dpmaster.div0.qc.to - default master server 4 (admin: divVerent)"}, // admin: divVerent
+#endif
{0, NULL, NULL, NULL}
};
int serverquerycount = 0;
int serverreplycount = 0;
+challenge_t challenge[MAX_CHALLENGES];
+
/// this is only false if there are still servers left to query
static qboolean serverlist_querysleep = true;
static qboolean serverlist_paused = false;
static unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
static unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
+static unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
+static unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
static int cl_numsockets;
static lhnetsocket_t *cl_sockets[16];
static int nFavorites = 0;
static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
+static int nFavorites_idfp = 0;
+static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
void NetConn_UpdateFavorites(void)
{
const char *p;
nFavorites = 0;
+ nFavorites_idfp = 0;
p = net_slist_favorites.string;
while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
{
- if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
- ++nFavorites;
+ if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
+ // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
+ // (if v6 address contains port, it must start with '[')
+ {
+ strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
+ ++nFavorites_idfp;
+ }
+ else
+ {
+ if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
+ ++nFavorites;
+ }
}
}
entry->info.isfavorite = false;
if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
{
+ char idfp[FP64_SIZE+1];
for(i = 0; i < nFavorites; ++i)
{
if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
break;
}
}
+ if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL))
+ {
+ for(i = 0; i < nFavorites_idfp; ++i)
+ {
+ if(!strcmp(idfp, favorites_idfp[i]))
+ {
+ entry->info.isfavorite = true;
+ break;
+ }
+ }
+ }
}
// FIXME: change this to be more readable (...)
unsigned int packetLen;
unsigned int dataLen;
unsigned int eom;
+ const void *sendme;
+ size_t sendmelen;
// if a reliable message fragment has been lost, send it again
if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
- if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) == (int)packetLen)
+ sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
+ if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
{
conn->lastSendTime = realtime;
packetsReSent++;
}
- totallen += packetLen + 28;
+ totallen += sendmelen + 28;
}
// if we have a new reliable message to send, do so
conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
- NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
+ sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
+ if(sendme)
+ NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
conn->lastSendTime = realtime;
packetsSent++;
reliableMessagesSent++;
- totallen += packetLen + 28;
+ totallen += sendmelen + 28;
}
// if we have an unreliable message to send, do so
conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
- NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
+ sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
+ if(sendme)
+ NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
packetsSent++;
unreliableMessagesSent++;
- totallen += packetLen + 28;
+ totallen += sendmelen + 28;
}
}
}
}
-static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int length, protocolversion_t protocol, double newtimeout)
+static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
{
int originallength = length;
if (length < 8)
unsigned int count;
unsigned int flags;
unsigned int sequence;
- int qlength;
+ size_t qlength;
+ const void *sendme;
+ size_t sendmelen;
+
+ originallength = length;
+ data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
+ if(!data)
+ return 0;
+ if(length < 8)
+ return 0;
qlength = (unsigned int)BuffBigLong(data);
flags = qlength & ~NETFLAG_LENGTH_MASK;
conn->nq.sendSequence++;
- if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) == (int)packetLen)
+ sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
+ if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
{
conn->lastSendTime = realtime;
packetsSent++;
conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
StoreBigLong(temppacket, 8 | NETFLAG_ACK);
StoreBigLong(temppacket + 4, sequence);
- NetConn_Write(conn->mysocket, (unsigned char *)temppacket, 8, &conn->peeraddress);
+ sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
+ if(sendme)
+ NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
if (sequence == conn->nq.receiveSequence)
{
conn->lastMessageTime = realtime;
void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
{
+ crypto_t *crypto;
cls.connect_trying = false;
M_Update_Return_Reason("");
// the connection request succeeded, stop current connection and set up a new connection
Host_ShutdownServer ();
// allocate a net connection to keep track of things
cls.netcon = NetConn_Open(mysocket, peeraddress);
+ crypto = &cls.crypto;
+ if(crypto && crypto->authenticated)
+ {
+ Crypto_ServerFinishInstance(&cls.netcon->crypto, crypto);
+ Con_Printf("%s connection to %s has been established: server is %s@%.*s, I am %.*s@%.*s\n",
+ crypto->use_aes ? "Encrypted" : "Authenticated",
+ cls.netcon->address,
+ crypto->server_idfp[0] ? crypto->server_idfp : "-",
+ crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
+ crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
+ crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
+ );
+ }
Con_Printf("Connection accepted to %s\n", cls.netcon->address);
key_dest = key_game;
m_state = m_none;
if (port != 0)
{
+#ifdef WHY_JUST_WHY
const char *ifname;
/// \TODO: make some basic checks of the IP address (broadcast, ...)
ifname = LHNETADDRESS_GetInterfaceName(senderaddress);
if (ifname != NULL)
{
- dpsnprintf (ipstring, sizeof (ipstring), "[%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x%%%s]:%hu",
- data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
- data[9], data[10], data[11], data[12], data[13], data[14], data[15], data[16],
+ dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
+ (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
+ (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
ifname, port);
}
else
+#endif
{
- dpsnprintf (ipstring, sizeof (ipstring), "[%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x]:%hu",
- data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
- data[9], data[10], data[11], data[12], data[13], data[14], data[15], data[16],
+ dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
+ (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
+ (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
port);
}
}
const char *s;
char *string, addressstring2[128], ipstring[32];
char stringbuf[16384];
+ char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
+ size_t sendlength;
// quakeworld ingame packet
fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
Com_HexDumpToConsole(data, length);
}
- if (length > 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
+ sendlength = sizeof(senddata) - 4;
+ switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
+ {
+ case CRYPTO_NOMATCH:
+ // nothing to do
+ break;
+ case CRYPTO_MATCH:
+ if(sendlength)
+ {
+ memcpy(senddata, "\377\377\377\377", 4);
+ NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
+ }
+ break;
+ case CRYPTO_DISCARD:
+ if(sendlength)
+ {
+ memcpy(senddata, "\377\377\377\377", 4);
+ NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
+ }
+ return true;
+ break;
+ case CRYPTO_REPLACE:
+ string = senddata+4;
+ length = sendlength;
+ break;
+ }
+
+ if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
{
int i = 0, j;
for (j = 0;j < MAX_RCONS;j++)
}
}
}
- if (length > 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
+ if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
{
// darkplaces or quake3
char protocolnames[1400];
}
if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
{
- char rejectreason[32];
+ char rejectreason[128];
cls.connect_trying = false;
string += 7;
length = min(length - 7, (int)sizeof(rejectreason) - 1);
return ret;
}
// netquake control packets, supported for compatibility only
- if (length >= 5 && (control = BuffBigLong(data)) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length)
+ if (length >= 5 && (control = BuffBigLong(data)) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length && !ENCRYPTION_REQUIRED)
{
int n;
serverlist_info_t *info;
lhnetaddress_t clientportaddress;
clientportaddress = *peeraddress;
LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong());
+ // extra ProQuake stuff
+ if (length >= 6)
+ cls.proquake_servermod = MSG_ReadByte(); // MOD_PROQUAKE
+ else
+ cls.proquake_servermod = 0;
+ if (length >= 7)
+ cls.proquake_serverversion = MSG_ReadByte(); // version * 10
+ else
+ cls.proquake_serverversion = 0;
+ if (length >= 8)
+ cls.proquake_serverflags = MSG_ReadByte(); // flags (mainly PQF_CHEATFREE)
+ else
+ cls.proquake_serverflags = 0;
+ if (cls.proquake_servermod == 1)
+ Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
// update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
M_Update_Return_Reason("Accepted");
MSG_WriteByte(&net_message, CCREQ_CONNECT);
MSG_WriteString(&net_message, "QUAKE");
MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
+ // extended proquake stuff
+ MSG_WriteByte(&net_message, 1); // mod = MOD_PROQUAKE
+ // this version matches ProQuake 3.40, the first version to support
+ // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
+ // higher clients, so we pretend we are that version...
+ MSG_WriteByte(&net_message, 34); // version * 10
+ MSG_WriteByte(&net_message, 0); // flags
+ MSG_WriteLong(&net_message, 0); // password
+ // write the packetsize now...
StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
NetConn_Write(cls.connect_mysocket, net_message.data, net_message.cursize, &cls.connect_address);
SZ_Clear(&net_message);
}
}
-#define MAX_CHALLENGES 128
-struct challenge_s
-{
- lhnetaddress_t address;
- double time;
- char string[12];
-}
-challenge[MAX_CHALLENGES];
-
static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
{
int i;
unsigned int nb_clients = 0, nb_bots = 0, i;
int length;
char teambuf[3];
+ const char *crypto_idstring;
SV_VM_Begin();
char *p;
const char *q;
p = qcstatus;
- for(q = str; *q; ++q)
+ for(q = str; *q && p - qcstatus < (ptrdiff_t)(sizeof(qcstatus)) - 1; ++q)
if(*q != '\\' && *q != '\n')
*p++ = *q;
*p = 0;
}
/// \TODO: we should add more information for the full status string
+ crypto_idstring = Crypto_GetInfoResponseDataString();
length = dpsnprintf(out_msg, out_size,
"\377\377\377\377%s\x0A"
"\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
"\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
"%s%s"
"%s%s"
+ "%s%s"
"%s",
fullstatus ? "statusResponse" : "infoResponse",
gamename, com_modname, gameversion.integer, svs.maxclients,
nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
*qcstatus ? "\\qcstatus\\" : "", qcstatus,
challenge ? "\\challenge\\" : "", challenge ? challenge : "",
+ crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
fullstatus ? "\n" : "");
// Make sure it fits in the buffer
return va("%srcon", restricted ? "restricted " : "");
}
-void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos)
+void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
{
if(userlevel)
{
Con_Printf("\n");
if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
- Con_Rcon_Redirect_Init(mysocket, peeraddress);
+ Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
while(s != endpos)
{
size_t l = strlen(s);
char *s, *string, response[1400], addressstring2[128];
static char stringbuf[16384];
qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
+ char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
+ size_t sendlength, response_len;
if (!sv.active)
return false;
Com_HexDumpToConsole(data, length);
}
- if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -2))
+ sendlength = sizeof(senddata) - 4;
+ switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
+ {
+ case CRYPTO_NOMATCH:
+ // nothing to do
+ break;
+ case CRYPTO_MATCH:
+ if(sendlength)
+ {
+ memcpy(senddata, "\377\377\377\377", 4);
+ NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
+ }
+ break;
+ case CRYPTO_DISCARD:
+ if(sendlength)
+ {
+ memcpy(senddata, "\377\377\377\377", 4);
+ NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
+ }
+ return true;
+ break;
+ case CRYPTO_REPLACE:
+ string = senddata+4;
+ length = sendlength;
+ break;
+ }
+
+ if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
{
for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
{
}
challenge[i].time = realtime;
// send the challenge
- NetConn_WriteString(mysocket, va("\377\377\377\377challenge %s", challenge[i].string), peeraddress);
+ dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
+ response_len = strlen(response) + 1;
+ Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
+ NetConn_Write(mysocket, response, response_len, peeraddress);
return true;
}
- if (length > 8 && !memcmp(string, "connect\\", 8) && (islocal || sv_public.integer > -2))
+ if (length > 8 && !memcmp(string, "connect\\", 8))
{
+ crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
string += 7;
length -= 7;
- if (!(s = SearchInfostring(string, "challenge")))
- return true;
- // 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)
+ if(crypto && crypto->authenticated)
+ {
+ // no need to check challenge
+ if(crypto_developer.integer)
+ {
+ Con_Printf("%s connection to %s is being established: client is %s@%.*s, I am %.*s@%.*s\n",
+ crypto->use_aes ? "Encrypted" : "Authenticated",
+ addressstring2,
+ crypto->client_idfp[0] ? crypto->client_idfp : "-",
+ crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
+ crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
+ crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
+ );
+ }
+ }
+ else
+ {
+ if ((s = SearchInfostring(string, "challenge")))
+ {
+ // 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)
+ return true;
+ }
+ }
+
+ if((s = SearchInfostring(string, "message")))
+ Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
+
+ if(!(islocal || sv_public.integer > -2))
+ {
+ if (developer_extra.integer)
+ Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
+ NetConn_WriteString(mysocket, va("\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
return true;
+ }
// check engine protocol
if(!(s = SearchInfostring(string, "protocol")) || strcmp(s, "darkplaces 3"))
if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
{
// this is a known client...
+ if(crypto && crypto->authenticated)
+ {
+ // reject if changing key!
+ if(client->netconnection->crypto.authenticated)
+ {
+ if(
+ strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
+ ||
+ strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
+ ||
+ strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
+ ||
+ strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
+ )
+ {
+ if (developer_extra.integer)
+ Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
+ NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // reject if downgrading!
+ if(client->netconnection->crypto.authenticated)
+ {
+ if (developer_extra.integer)
+ Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
+ NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
+ return true;
+ }
+ }
if (client->spawned)
{
// client crashed and is coming back,
if (developer_extra.integer)
Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
+ if(crypto && crypto->authenticated)
+ Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
SV_VM_Begin();
SV_SendServerinfo(client);
SV_VM_End();
// so we send a duplicate reply
if (developer_extra.integer)
Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
+ if(crypto && crypto->authenticated)
+ Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
}
return true;
Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
// now set up the client
+ if(crypto && crypto->authenticated)
+ Crypto_ServerFinishInstance(&conn->crypto, crypto);
SV_VM_Begin();
SV_ConnectClient(clientnum, conn);
SV_VM_End();
++s;
userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
- RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
+ RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
return true;
}
if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
++s;
userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
- RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
+ RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
return true;
}
if (length >= 5 && !memcmp(string, "rcon ", 5))
if (!ISWHITESPACE(password[0]))
{
const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
- RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
+ RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
}
return true;
}
// protocol
// (this protects more modern protocols against being used for
// Quake packet flood Denial Of Service attacks)
- if (length >= 5 && (i = BuffBigLong(data)) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3))
+ if (length >= 5 && (i = BuffBigLong(data)) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3) && !ENCRYPTION_REQUIRED)
{
int c;
int protocolnumber;
case CCREQ_CONNECT:
if (developer_extra.integer)
Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
- if(!islocal && sv_public.integer <= -2)
+ if(!(islocal || sv_public.integer > -2))
+ {
+ if (developer_extra.integer)
+ Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREP_REJECT);
+ MSG_WriteString(&net_message, va("%s\n", sv_public_rejectreason.string));
+ StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
+ SZ_Clear(&net_message);
break;
+ }
protocolname = MSG_ReadString();
protocolnumber = MSG_ReadByte();
case CCREQ_SERVER_INFO:
if (developer_extra.integer)
Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
- if(!islocal && sv_public.integer <= -1)
+ if(!(islocal || sv_public.integer > -1))
break;
if (sv.active && !strcmp(MSG_ReadString(), "QUAKE"))
{
case CCREQ_PLAYER_INFO:
if (developer_extra.integer)
Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
- if(!islocal && sv_public.integer <= -1)
+ if(!(islocal || sv_public.integer > -1))
break;
if (sv.active)
{
MSG_WriteLong(&net_message, client->colors);
MSG_WriteLong(&net_message, client->frags);
MSG_WriteLong(&net_message, (int)(realtime - client->connecttime));
- MSG_WriteString(&net_message, client->netconnection ? client->netconnection->address : "botclient");
+ if(sv_status_privacy.integer)
+ MSG_WriteString(&net_message, client->netconnection ? "hidden" : "botclient");
+ else
+ MSG_WriteString(&net_message, client->netconnection ? client->netconnection->address : "botclient");
StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
SZ_Clear(&net_message);
case CCREQ_RULE_INFO:
if (developer_extra.integer)
Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
- if(!islocal && sv_public.integer <= -1)
+ if(!(islocal || sv_public.integer > -1))
break;
if (sv.active)
{
SZ_Clear(&net_message);
}
break;
+ case CCREQ_RCON:
+ if (developer_extra.integer)
+ Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
+ if (sv.active && !rcon_secure.integer)
+ {
+ char password[2048];
+ char cmd[2048];
+ char *s;
+ char *endpos;
+ const char *userlevel;
+ strlcpy(password, MSG_ReadString(), sizeof(password));
+ strlcpy(cmd, MSG_ReadString(), sizeof(cmd));
+ s = cmd;
+ endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
+ userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
+ RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
+ return true;
+ }
+ break;
default:
break;
}
Cvar_RegisterVariable(&net_address);
Cvar_RegisterVariable(&net_address_ipv6);
Cvar_RegisterVariable(&sv_public);
+ Cvar_RegisterVariable(&sv_public_rejectreason);
Cvar_RegisterVariable(&sv_heartbeatperiod);
for (i = 0;sv_masters[i].name;i++)
Cvar_RegisterVariable(&sv_masters[i]);