*/
#include "quakedef.h"
+#include "thread.h"
#include "lhnet.h"
// for secure rcon authentication
#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", "[2a03:4000:1::2e26:f351:3]:27950", "dpmaster.sudo.rm-f.org - default master server 4 (admin: divVerent)"}, // admin: divVerent
+#endif
{0, NULL, NULL, NULL}
};
{0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
{0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
{0, "sv_qwmasterextra4", "masterserver.exhale.de:27000", "German master server. (admin: unknown)"},
- {0, "sv_qwmasterextra5", "kubus.rulez.pl:27000", "Poland master server. (admin: unknown)"},
+ {0, "sv_qwmasterextra5", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
{0, NULL, NULL, NULL}
};
static double nextheartbeattime = 0;
-sizebuf_t net_message;
-static unsigned char net_message_buf[NET_MAXMESSAGE];
+sizebuf_t cl_message;
+sizebuf_t sv_message;
+static unsigned char cl_message_buf[NET_MAXMESSAGE];
+static unsigned char sv_message_buf[NET_MAXMESSAGE];
+char cl_readstring[MAX_INPUTLINE];
+char sv_readstring[MAX_INPUTLINE];
cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
cvar_t net_connecttimeout = {0, "net_connecttimeout","15", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods). Must be above 10 seconds."};
-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)"};
+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 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)"};
extern cvar_t rcon_secure;
extern cvar_t rcon_secure_challengetimeout;
-/* statistic counters */
-static int packetsSent = 0;
-static int packetsReSent = 0;
-static int packetsReceived = 0;
-static int receivedDuplicateCount = 0;
-static int droppedDatagrams = 0;
-
-static int unreliableMessagesSent = 0;
-static int unreliableMessagesReceived = 0;
-static int reliableMessagesSent = 0;
-static int reliableMessagesReceived = 0;
-
double masterquerytime = -1000;
int masterquerycount = 0;
int masterreplycount = 0;
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;
/// flooding in (which would make a mess of the ping times)
static double serverlist_querywaittime = 0;
-static unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
-static unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
-
static int cl_numsockets;
static lhnetsocket_t *cl_sockets[16];
static int sv_numsockets;
netconn_t *netconn_list = NULL;
mempool_t *netconn_mempool = NULL;
+void *netconn_mutex = NULL;
cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
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;
+ }
}
}
!(
gameversion_min.integer >= 0 // min/max range set by user/mod?
&& gameversion_max.integer >= 0
- && gameversion_min.integer >= entry->info.gameversion // version of server in min/max range?
- && gameversion_max.integer <= entry->info.gameversion
+ && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
+ && gameversion_max.integer >= entry->info.gameversion
)
)
return;
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 (...)
int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
{
- int length = LHNET_Read(mysocket, data, maxlength, peeraddress);
+ int length;
int i;
+ if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
+ Thread_LockMutex(netconn_mutex);
+ length = LHNET_Read(mysocket, data, maxlength, peeraddress);
+ if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
+ Thread_UnlockMutex(netconn_mutex);
if (length == 0)
return 0;
if (cl_netpacketloss_receive.integer)
for (i = 0;i < cl_numsockets;i++)
if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_send.integer)
return length;
+ if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
+ Thread_LockMutex(netconn_mutex);
ret = LHNET_Write(mysocket, data, length, peeraddress);
+ if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
+ Thread_UnlockMutex(netconn_mutex);
if (developer_networking.integer)
{
char addressstring[128], addressstring2[128];
int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, qboolean quakesignon_suppressreliables)
{
int totallen = 0;
+ unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
+ unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
// if this packet was supposedly choked, but we find ourselves sending one
// anyway, make sure the size counting starts at zero
NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
- packetsSent++;
- unreliableMessagesSent++;
+ conn->packetsSent++;
+ conn->unreliableMessagesSent++;
totallen += packetLen + 28;
}
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++;
+ conn->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++;
+ conn->packetsSent++;
+ conn->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++;
+ conn->packetsSent++;
+ conn->unreliableMessagesSent++;
- totallen += packetLen + 28;
+ totallen += sendmelen + 28;
}
}
LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
}
-void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
+static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
{
lhnetaddress_t address;
lhnetsocket_t *s;
{
int port;
NetConn_CloseClientPorts();
+
+ SV_LockThreadMutex(); // FIXME recursive?
+ Crypto_LoadKeys(); // client sockets
+ SV_UnlockThreadMutex();
+
port = bound(0, cl_netport.integer, 65535);
if (cl_netport.integer != port)
Cvar_SetValueQuick(&cl_netport, port);
LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
}
-qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
+static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
{
lhnetaddress_t address;
lhnetsocket_t *s;
{
int port;
NetConn_CloseServerPorts();
+
+ SV_LockThreadMutex(); // FIXME recursive?
+ Crypto_LoadKeys(); // server sockets
+ SV_UnlockThreadMutex();
+
NetConn_UpdateSockets();
port = bound(0, sv_netport.integer, 65535);
if (port == 0)
return conn;
}
-void NetConn_ClearConnectFlood(lhnetaddress_t *peeraddress);
+void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
void NetConn_Close(netconn_t *conn)
{
netconn_t *c;
// remove connection from list
// allow the client to reconnect immediately
- NetConn_ClearConnectFlood(&(conn->peeraddress));
+ NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
if (conn == netconn_list)
netconn_list = conn->next;
}
}
-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;
+ unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
+ unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
+ unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
if (length < 8)
return 0;
length -= 2;
}
- packetsReceived++;
+ conn->packetsReceived++;
reliable_message = (sequence >> 31) & 1;
reliable_ack = (sequence_ack >> 31) & 1;
sequence &= ~(1<<31);
count = sequence - (conn->qw.incoming_sequence + 1);
if (count > 0)
{
- droppedDatagrams += count;
+ conn->droppedDatagrams += count;
//Con_DPrintf("Dropped %u datagram(s)\n", count);
while (count--)
{
{
// received, now we will be able to send another reliable message
conn->sendMessageLength = 0;
- reliableMessagesReceived++;
+ conn->reliableMessagesReceived++;
}
conn->qw.incoming_sequence = sequence;
if (conn == cls.netcon)
conn->qw.incoming_reliable_sequence ^= 1;
conn->lastMessageTime = realtime;
conn->timeout = realtime + newtimeout;
- unreliableMessagesReceived++;
- SZ_Clear(&net_message);
- SZ_Write(&net_message, data, length);
- MSG_BeginReading();
+ conn->unreliableMessagesReceived++;
+ if (conn == cls.netcon)
+ {
+ SZ_Clear(&cl_message);
+ SZ_Write(&cl_message, data, length);
+ MSG_BeginReading(&cl_message);
+ }
+ else
+ {
+ SZ_Clear(&sv_message);
+ SZ_Write(&sv_message, data, length);
+ MSG_BeginReading(&sv_message);
+ }
return 2;
}
else
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;
if (!(flags & NETFLAG_CTL) && qlength == length)
{
sequence = BuffBigLong(data + 4);
- packetsReceived++;
+ conn->packetsReceived++;
data += 8;
length -= 8;
if (flags & NETFLAG_UNRELIABLE)
if (sequence > conn->nq.unreliableReceiveSequence)
{
count = sequence - conn->nq.unreliableReceiveSequence;
- droppedDatagrams += count;
+ conn->droppedDatagrams += count;
//Con_DPrintf("Dropped %u datagram(s)\n", count);
while (count--)
{
conn->nq.unreliableReceiveSequence = sequence + 1;
conn->lastMessageTime = realtime;
conn->timeout = realtime + newtimeout;
- unreliableMessagesReceived++;
+ conn->unreliableMessagesReceived++;
if (length > 0)
{
- SZ_Clear(&net_message);
- SZ_Write(&net_message, data, length);
- MSG_BeginReading();
+ if (conn == cls.netcon)
+ {
+ SZ_Clear(&cl_message);
+ SZ_Write(&cl_message, data, length);
+ MSG_BeginReading(&cl_message);
+ }
+ else
+ {
+ SZ_Clear(&sv_message);
+ SZ_Write(&sv_message, data, length);
+ MSG_BeginReading(&sv_message);
+ }
return 2;
}
}
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->packetsSent++;
}
}
else
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;
}
if (flags & NETFLAG_EOM)
{
- reliableMessagesReceived++;
+ conn->reliableMessagesReceived++;
length = conn->receiveMessageLength;
conn->receiveMessageLength = 0;
if (length > 0)
{
- SZ_Clear(&net_message);
- SZ_Write(&net_message, conn->receiveMessage, length);
- MSG_BeginReading();
+ if (conn == cls.netcon)
+ {
+ SZ_Clear(&cl_message);
+ SZ_Write(&cl_message, conn->receiveMessage, length);
+ MSG_BeginReading(&cl_message);
+ }
+ else
+ {
+ SZ_Clear(&sv_message);
+ SZ_Write(&sv_message, conn->receiveMessage, length);
+ MSG_BeginReading(&sv_message);
+ }
return 2;
}
}
}
else
- receivedDuplicateCount++;
+ conn->receivedDuplicateCount++;
return 1;
}
}
return 0;
}
-void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
+static 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
CL_Disconnect();
// if we're connecting to a remote server, shut down any local server
if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
+ {
+ SV_LockThreadMutex();
Host_ShutdownServer ();
+ SV_UnlockThreadMutex();
+ }
// 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;
!(
gameversion_min.integer >= 0 // min/max range set by user/mod?
&& gameversion_max.integer >= 0
- && gameversion_min.integer >= info->gameversion // version of server in min/max range?
- && gameversion_max.integer <= info->gameversion
+ && gameversion_min.integer <= info->gameversion // version of server in min/max range?
+ && gameversion_max.integer >= info->gameversion
)
) ? '1' : '4',
info->mod, info->map);
entry = &serverlist_cache[n];
- memset(entry, 0, sizeof(entry));
+ memset(entry, 0, sizeof(*entry));
entry->protocol = protocol;
// store the data the engine cares about (address and ping)
strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
if (port != 0)
{
+#ifdef WHY_JUST_WHY
const char *ifname;
+ char ifnamebuf[16];
/// \TODO: make some basic checks of the IP address (broadcast, ...)
- ifname = LHNETADDRESS_GetInterfaceName(senderaddress);
+ ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
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);
}
}
static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
{
qboolean fromserver;
- int ret, c, control;
+ int ret, c;
const char *s;
char *string, addressstring2[128], ipstring[32];
char stringbuf[16384];
+ char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
+ size_t sendlength;
+ char infostringvalue[MAX_INPUTLINE];
+ char vabuf[1024];
// 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];
// 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);
// TODO: add userinfo stuff here instead of using NQ commands?
- NetConn_WriteString(mysocket, va("\377\377\377\377connect\\protocol\\darkplaces 3\\protocols\\%s\\challenge\\%s", protocolnames, string + 10), peeraddress);
+ NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10), peeraddress);
return true;
}
if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
}
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);
else
Con_Printf("statusResponse without players block?\n");
- if ((s = SearchInfostring(string, "gamename" )) != NULL) strlcpy(info->game, s, sizeof (info->game));
- if ((s = SearchInfostring(string, "modname" )) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
- if ((s = SearchInfostring(string, "mapname" )) != NULL) strlcpy(info->map , s, sizeof (info->map ));
- if ((s = SearchInfostring(string, "hostname" )) != NULL) strlcpy(info->name, s, sizeof (info->name));
- if ((s = SearchInfostring(string, "protocol" )) != NULL) info->protocol = atoi(s);
- if ((s = SearchInfostring(string, "clients" )) != NULL) info->numplayers = atoi(s);
- if ((s = SearchInfostring(string, "bots" )) != NULL) info->numbots = atoi(s);
- if ((s = SearchInfostring(string, "sv_maxclients")) != NULL) info->maxplayers = atoi(s);
- if ((s = SearchInfostring(string, "gameversion" )) != NULL) info->gameversion = atoi(s);
- if ((s = SearchInfostring(string, "qcstatus" )) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
- if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
+ if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
+ if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
+ if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
+ if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
+ if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
+ if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
+ if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
+ if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
+ if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
+ if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
+ if (p != NULL) strlcpy(info->players, p, sizeof(info->players));
info->numhumans = info->numplayers - max(0, info->numbots);
info->freeslots = info->maxplayers - info->numplayers;
info->maxplayers = 0;
info->gameversion = 0;
- if ((s = SearchInfostring(string, "gamename" )) != NULL) strlcpy(info->game, s, sizeof (info->game));
- if ((s = SearchInfostring(string, "modname" )) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
- if ((s = SearchInfostring(string, "mapname" )) != NULL) strlcpy(info->map , s, sizeof (info->map ));
- if ((s = SearchInfostring(string, "hostname" )) != NULL) strlcpy(info->name, s, sizeof (info->name));
- if ((s = SearchInfostring(string, "protocol" )) != NULL) info->protocol = atoi(s);
- if ((s = SearchInfostring(string, "clients" )) != NULL) info->numplayers = atoi(s);
- if ((s = SearchInfostring(string, "bots" )) != NULL) info->numbots = atoi(s);
- if ((s = SearchInfostring(string, "sv_maxclients")) != NULL) info->maxplayers = atoi(s);
- if ((s = SearchInfostring(string, "gameversion" )) != NULL) info->gameversion = atoi(s);
- if ((s = SearchInfostring(string, "qcstatus" )) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
+ if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
+ if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
+ if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
+ if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
+ if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
+ if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
+ if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
+ if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
+ if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
+ if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
info->numhumans = info->numplayers - max(0, info->numbots);
info->freeslots = info->maxplayers - info->numplayers;
cls.qw_qport = qport.integer;
// 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);
- NetConn_WriteString(mysocket, va("\377\377\377\377connect %i %i %i \"%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo), peeraddress);
+ NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377connect %i %i %i \"%s%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo, cls.connect_userinfo), peeraddress);
return true;
}
if (length >= 1 && string[0] == 'j' && cls.connect_trying)
info = &serverlist_cache[n].info;
strlcpy(info->game, "QuakeWorld", sizeof(info->game));
- if ((s = SearchInfostring(string, "*gamedir" )) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
- if ((s = SearchInfostring(string, "map" )) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
- if ((s = SearchInfostring(string, "hostname" )) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
+ if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0;
+ if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0;
+ if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
info->protocol = 0;
info->numplayers = 0; // updated below
info->numhumans = 0; // updated below
- if ((s = SearchInfostring(string, "maxclients" )) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
- if ((s = SearchInfostring(string, "gameversion" )) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
+ if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0;
+ if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
// count active players on server
// (we could gather more info, but we're just after the number)
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 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
{
int n;
serverlist_info_t *info;
data += 4;
length -= 4;
- SZ_Clear(&net_message);
- SZ_Write(&net_message, data, length);
- MSG_BeginReading();
- c = MSG_ReadByte();
+ SZ_Clear(&cl_message);
+ SZ_Write(&cl_message, data, length);
+ MSG_BeginReading(&cl_message);
+ c = MSG_ReadByte(&cl_message);
switch (c)
{
case CCREP_ACCEPT:
{
lhnetaddress_t clientportaddress;
clientportaddress = *peeraddress;
- LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong());
+ LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
+ // extra ProQuake stuff
+ if (length >= 6)
+ cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
+ else
+ cls.proquake_servermod = 0;
+ if (length >= 7)
+ cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
+ else
+ cls.proquake_serverversion = 0;
+ if (length >= 8)
+ cls.proquake_serverflags = MSG_ReadByte(&cl_message); // 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");
if (developer_extra.integer)
Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
cls.connect_trying = false;
- M_Update_Return_Reason((char *)MSG_ReadString());
+ M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
break;
case CCREP_SERVER_INFO:
if (developer_extra.integer)
Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
// LordHavoc: because the quake server may report weird addresses
// we just ignore it and keep the real address
- MSG_ReadString();
+ MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
// search the cache for this server and update it
n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
if (n < 0)
info = &serverlist_cache[n].info;
strlcpy(info->game, "Quake", sizeof(info->game));
strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
- strlcpy(info->name, MSG_ReadString(), sizeof(info->name));
- strlcpy(info->map , MSG_ReadString(), sizeof(info->map));
- info->numplayers = MSG_ReadByte();
- info->maxplayers = MSG_ReadByte();
- info->protocol = MSG_ReadByte();
+ strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
+ strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
+ info->numplayers = MSG_ReadByte(&cl_message);
+ info->maxplayers = MSG_ReadByte(&cl_message);
+ info->protocol = MSG_ReadByte(&cl_message);
NetConn_ClientParsePacket_ServerList_UpdateCache(n);
if (developer_extra.integer)
Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
- Con_Printf("%s\n", MSG_ReadString());
+ Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
break;
case CCREP_PLAYER_INFO:
// we got a CCREP_PLAYER_INFO??
default:
break;
}
- SZ_Clear(&net_message);
+ SZ_Clear(&cl_message);
// we may not have liked the packet, but it was a valid control
// packet, so we're done processing this packet now
return true;
{
int i, length;
lhnetaddress_t peeraddress;
+ unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
NetConn_UpdateSockets();
if (cls.connect_trying && cls.connect_nextsendtime < realtime)
{
// try challenge first (newer DP server or QW)
NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
// then try netquake as a fallback (old server, or netquake)
- SZ_Clear(&net_message);
+ SZ_Clear(&cl_message);
// save space for the header, filled in later
- MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREQ_CONNECT);
- MSG_WriteString(&net_message, "QUAKE");
- MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
- 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);
+ MSG_WriteLong(&cl_message, 0);
+ MSG_WriteByte(&cl_message, CCREQ_CONNECT);
+ MSG_WriteString(&cl_message, "QUAKE");
+ MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
+ // extended proquake stuff
+ MSG_WriteByte(&cl_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(&cl_message, 34); // version * 10
+ MSG_WriteByte(&cl_message, 0); // flags
+ MSG_WriteLong(&cl_message, 0); // password
+ // write the packetsize now...
+ StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
+ SZ_Clear(&cl_message);
}
for (i = 0;i < cl_numsockets;i++)
+ {
while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
+ {
+// R_TimeReport("clientreadnetwork");
NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
+// R_TimeReport("clientparsepacket");
+ }
+ }
NetConn_QueryQueueFrame();
if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
{
Con_Print("Connection timed out\n");
CL_Disconnect();
+ SV_LockThreadMutex();
Host_ShutdownServer ();
+ SV_UnlockThreadMutex();
}
}
-#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;
/// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
{
+ prvm_prog_t *prog = SVVM_prog;
char qcstatus[256];
unsigned int nb_clients = 0, nb_bots = 0, i;
int length;
char teambuf[3];
-
- SV_VM_Begin();
+ const char *crypto_idstring;
+ const char *str;
// How many clients are there?
for (i = 0;i < (unsigned int)svs.maxclients;i++)
}
*qcstatus = 0;
- if(prog->globaloffsets.worldstatus >= 0)
+ str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
+ if(str && *str)
{
- const char *str = PRVM_G_STRING(prog->globaloffsets.worldstatus);
- if(str && *str)
- {
- char *p;
- const char *q;
- p = qcstatus;
- for(q = str; *q; ++q)
- if(*q != '\\' && *q != '\n')
- *p++ = *q;
- *p = 0;
- }
+ char *p;
+ const char *q;
+ p = qcstatus;
+ for(q = str; *q && (size_t)(p - qcstatus) < (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.name, hostname.string, NET_PROTOCOL_VERSION,
+ 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
int nameind, cleanind, pingvalue;
char curchar;
char cleanname [sizeof(cl->name)];
+ const char *str;
+ prvm_edict_t *ed;
// Remove all characters '"' and '\' in the player name
nameind = 0;
pingvalue = 0;
*qcstatus = 0;
- if(prog->fieldoffsets.clientstatus >= 0)
+ ed = PRVM_EDICT_NUM(i + 1);
+ str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
+ if(str && *str)
{
- const char *str = PRVM_E_STRING(PRVM_EDICT_NUM(i + 1), prog->fieldoffsets.clientstatus);
- if(str && *str)
- {
- char *p;
- const char *q;
- p = qcstatus;
- for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
- if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
- *p++ = *q;
- *p = 0;
- }
+ char *p;
+ const char *q;
+ p = qcstatus;
+ for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
+ if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
+ *p++ = *q;
+ *p = 0;
}
- if ((gamemode == GAME_NEXUIZ) && (teamplay.integer > 0))
+ if ((gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC) && (teamplay.integer > 0))
{
if(cl->frags == -666) // spectator
strlcpy(teambuf, " 0", sizeof(teambuf));
}
}
- SV_VM_End();
return true;
bad:
- SV_VM_End();
return false;
}
-static qboolean NetConn_PreventConnectFlood(lhnetaddress_t *peeraddress)
+static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
{
- int floodslotnum, bestfloodslotnum;
+ size_t floodslotnum, bestfloodslotnum;
double bestfloodtime;
lhnetaddress_t noportpeeraddress;
// see if this is a connect flood
noportpeeraddress = *peeraddress;
LHNETADDRESS_SetPort(&noportpeeraddress, 0);
bestfloodslotnum = 0;
- bestfloodtime = sv.connectfloodaddresses[bestfloodslotnum].lasttime;
- for (floodslotnum = 0;floodslotnum < MAX_CONNECTFLOODADDRESSES;floodslotnum++)
+ bestfloodtime = floodlist[bestfloodslotnum].lasttime;
+ for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
{
- if (bestfloodtime >= sv.connectfloodaddresses[floodslotnum].lasttime)
+ if (bestfloodtime >= floodlist[floodslotnum].lasttime)
{
- bestfloodtime = sv.connectfloodaddresses[floodslotnum].lasttime;
+ bestfloodtime = floodlist[floodslotnum].lasttime;
bestfloodslotnum = floodslotnum;
}
- if (sv.connectfloodaddresses[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &sv.connectfloodaddresses[floodslotnum].address) == 0)
+ if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
{
// this address matches an ongoing flood address
- if (realtime < sv.connectfloodaddresses[floodslotnum].lasttime + net_connectfloodblockingtimeout.value)
+ if (realtime < floodlist[floodslotnum].lasttime + floodtime)
{
- // renew the ban on this address so it does not expire
- // until the flood has subsided
- sv.connectfloodaddresses[floodslotnum].lasttime = realtime;
+ if(renew)
+ {
+ // renew the ban on this address so it does not expire
+ // until the flood has subsided
+ floodlist[floodslotnum].lasttime = realtime;
+ }
//Con_Printf("Flood detected!\n");
return true;
}
}
}
// begin a new timeout on this address
- sv.connectfloodaddresses[bestfloodslotnum].address = noportpeeraddress;
- sv.connectfloodaddresses[bestfloodslotnum].lasttime = realtime;
+ floodlist[bestfloodslotnum].address = noportpeeraddress;
+ floodlist[bestfloodslotnum].lasttime = realtime;
//Con_Printf("Flood detection initiated!\n");
return false;
}
-void NetConn_ClearConnectFlood(lhnetaddress_t *peeraddress)
+void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
{
- int floodslotnum;
+ size_t floodslotnum;
lhnetaddress_t noportpeeraddress;
// see if this is a connect flood
noportpeeraddress = *peeraddress;
LHNETADDRESS_SetPort(&noportpeeraddress, 0);
- for (floodslotnum = 0;floodslotnum < MAX_CONNECTFLOODADDRESSES;floodslotnum++)
+ for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
{
- if (sv.connectfloodaddresses[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &sv.connectfloodaddresses[floodslotnum].address) == 0)
+ if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
{
// this address matches an ongoing flood address
// remove the ban
- sv.connectfloodaddresses[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
- sv.connectfloodaddresses[floodslotnum].lasttime = 0;
+ floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
+ floodlist[floodslotnum].lasttime = 0;
//Con_Printf("Flood cleared!\n");
}
}
typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
-qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
+static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
{
char mdfourbuf[16];
long t1, t2;
return !memcmp(mdfourbuf, hash, 16);
}
-qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
+static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
{
char mdfourbuf[16];
int i;
return true;
}
-qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
+static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
{
return !strcmp(password, hash);
}
/// returns a string describing the user level, or NULL for auth failure
-const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
+static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
{
const char *text, *userpass_start, *userpass_end, *userpass_startpass;
static char buf[MAX_INPUTLINE];
qboolean hasquotes;
qboolean restricted = false;
qboolean have_usernames = false;
+ char vabuf[1024];
userpass_start = rcon_password.string;
while((userpass_end = strchr(userpass_start, ' ')))
{
if(!strcmp(com_token, s))
goto match;
- if(!memcmp(va("%s ", com_token), s, strlen(com_token) + 1))
+ if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
goto match;
}
}
allow:
userpass_startpass = strchr(userpass_start, ':');
if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
- return va("%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
+ return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
- return va("%srcon", restricted ? "restricted " : "");
+ return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
}
-void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos)
+static 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);
if(l)
{
client_t *host_client_save = host_client;
- Cmd_ExecuteString(s, src_command);
+ Cmd_ExecuteString(s, src_command, true);
host_client = host_client_save;
// in case it is a command that changes host_client (like restart)
}
}
}
-extern void SV_SendServerinfo (client_t *client);
static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
{
int i, ret, clientnum, best;
double besttime;
client_t *client;
char *s, *string, response[1400], addressstring2[128];
- static char stringbuf[16384];
+ static char stringbuf[16384]; // server only
qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
+ char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
+ size_t sendlength, response_len;
+ char infostringvalue[MAX_INPUTLINE];
+ char vabuf[1024];
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].address = *peeraddress;
NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
}
+ else
+ {
+ // flood control: drop if requesting challenge too often
+ if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
+ return true;
+ }
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 = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
+ {
+ // 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 = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
+ 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(vabuf, sizeof(vabuf), "\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(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
{
if (developer_extra.integer)
Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
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);
- SV_VM_Begin();
+ if(crypto && crypto->authenticated)
+ Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
SV_SendServerinfo(client);
- SV_VM_End();
}
else
{
// 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;
}
}
- if (NetConn_PreventConnectFlood(peeraddress))
+ if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
return true;
// find an empty client slot for this new client
Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
// now set up the client
- SV_VM_Begin();
+ if(crypto && crypto->authenticated)
+ Crypto_ServerFinishInstance(&conn->crypto, crypto);
SV_ConnectClient(clientnum, conn);
- SV_VM_End();
NetConn_Heartbeat(1);
return true;
}
{
const char *challenge = NULL;
+ if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
+ return true;
+
// If there was a challenge in the getinfo message
if (length > 8 && string[7] == ' ')
challenge = string + 8;
{
const char *challenge = NULL;
+ if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
+ return true;
+
// If there was a challenge in the getinfo message
if (length > 10 && string[9] == ' ')
challenge = string + 10;
++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;
const char *protocolname;
data += 4;
length -= 4;
- SZ_Clear(&net_message);
- SZ_Write(&net_message, data, length);
- MSG_BeginReading();
- c = MSG_ReadByte();
+ SZ_Clear(&sv_message);
+ SZ_Write(&sv_message, data, length);
+ MSG_BeginReading(&sv_message);
+ c = MSG_ReadByte(&sv_message);
switch (c)
{
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(&sv_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_REJECT);
+ MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
break;
+ }
- protocolname = MSG_ReadString();
- protocolnumber = MSG_ReadByte();
+ protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
+ protocolnumber = MSG_ReadByte(&sv_message);
if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
{
if (developer_extra.integer)
Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
- SZ_Clear(&net_message);
+ SZ_Clear(&sv_message);
// save space for the header, filled in later
- MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREP_REJECT);
- MSG_WriteString(&net_message, "Incompatible version.\n");
- 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);
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_REJECT);
+ MSG_WriteString(&sv_message, "Incompatible version.\n");
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
break;
}
// or coming back from a timeout
// (if so, keep their stuff intact)
+ crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
+ if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
+ {
+ if (developer_extra.integer)
+ Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
+ SZ_Clear(&sv_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_REJECT);
+ MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
+ return true;
+ }
+
// send a reply
if (developer_extra.integer)
Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
- SZ_Clear(&net_message);
+ SZ_Clear(&sv_message);
// save space for the header, filled in later
- MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREP_ACCEPT);
- MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
- 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);
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_ACCEPT);
+ MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
// if client is already spawned, re-send the
// serverinfo message as they'll need it to play
if (client->spawned)
- {
- SV_VM_Begin();
SV_SendServerinfo(client);
- SV_VM_End();
- }
return true;
}
}
// this is a new client, check for connection flood
- if (NetConn_PreventConnectFlood(peeraddress))
+ if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
break;
// find a slot for the new client
if (developer_extra.integer)
Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
// send back the info about the server connection
- SZ_Clear(&net_message);
+ SZ_Clear(&sv_message);
// save space for the header, filled in later
- MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREP_ACCEPT);
- MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
- 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);
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_ACCEPT);
+ MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
// now set up the client struct
- SV_VM_Begin();
SV_ConnectClient(clientnum, conn);
- SV_VM_End();
NetConn_Heartbeat(1);
return true;
}
if (developer_extra.integer)
Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
// no room; try to let player know
- SZ_Clear(&net_message);
+ SZ_Clear(&sv_message);
// save space for the header, filled in later
- MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREP_REJECT);
- MSG_WriteString(&net_message, "Server is full.\n");
- 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);
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_REJECT);
+ MSG_WriteString(&sv_message, "Server is full.\n");
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
break;
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"))
+
+ if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
+ break;
+
+ if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
{
int numclients;
char myaddressstring[128];
if (developer_extra.integer)
Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
- SZ_Clear(&net_message);
+ SZ_Clear(&sv_message);
// save space for the header, filled in later
- MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
- MSG_WriteString(&net_message, myaddressstring);
- MSG_WriteString(&net_message, hostname.string);
- MSG_WriteString(&net_message, sv.name);
+ MSG_WriteString(&sv_message, myaddressstring);
+ MSG_WriteString(&sv_message, hostname.string);
+ MSG_WriteString(&sv_message, sv.name);
// How many clients are there?
for (i = 0, numclients = 0;i < svs.maxclients;i++)
if (svs.clients[i].active)
numclients++;
- MSG_WriteByte(&net_message, numclients);
- MSG_WriteByte(&net_message, svs.maxclients);
- MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
- 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);
+ MSG_WriteByte(&sv_message, numclients);
+ MSG_WriteByte(&sv_message, svs.maxclients);
+ MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
}
break;
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 (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
break;
+
if (sv.active)
{
int playerNumber, activeNumber, clientNumber;
client_t *client;
- playerNumber = MSG_ReadByte();
+ playerNumber = MSG_ReadByte(&sv_message);
activeNumber = -1;
for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
if (client->active && ++activeNumber == playerNumber)
break;
if (clientNumber != svs.maxclients)
{
- SZ_Clear(&net_message);
+ SZ_Clear(&sv_message);
// save space for the header, filled in later
- MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
- MSG_WriteByte(&net_message, playerNumber);
- MSG_WriteString(&net_message, client->name);
- 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");
- 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);
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
+ MSG_WriteByte(&sv_message, playerNumber);
+ MSG_WriteString(&sv_message, client->name);
+ MSG_WriteLong(&sv_message, client->colors);
+ MSG_WriteLong(&sv_message, client->frags);
+ MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
+ if(sv_status_privacy.integer)
+ MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
+ else
+ MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_message);
}
}
break;
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;
+
+ // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
+
if (sv.active)
{
char *prevCvarName;
cvar_t *var;
// find the search start location
- prevCvarName = MSG_ReadString();
+ prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
// send the response
- SZ_Clear(&net_message);
+ SZ_Clear(&sv_message);
// save space for the header, filled in later
- MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREP_RULE_INFO);
+ MSG_WriteLong(&sv_message, 0);
+ MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
if (var)
{
- MSG_WriteString(&net_message, var->name);
- MSG_WriteString(&net_message, var->string);
+ MSG_WriteString(&sv_message, var->name);
+ MSG_WriteString(&sv_message, var->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);
+ StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+ SZ_Clear(&sv_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(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
+ strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_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;
}
- SZ_Clear(&net_message);
+ SZ_Clear(&sv_message);
// we may not have liked the packet, but it was a valid control
// packet, so we're done processing this packet now
return true;
{
if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->spawned ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
{
- SV_VM_Begin();
SV_ReadClientMessage();
- SV_VM_End();
return ret;
}
}
{
int i, length;
lhnetaddress_t peeraddress;
+ unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
for (i = 0;i < sv_numsockets;i++)
while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
{
Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
- SV_VM_Begin();
SV_DropClient(false);
- SV_VM_End();
}
}
}
if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
{
// search LAN for Quake servers
- SZ_Clear(&net_message);
+ SZ_Clear(&cl_message);
// save space for the header, filled in later
- MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
- MSG_WriteString(&net_message, "QUAKE");
- MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
- StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
- NetConn_Write(cl_sockets[i], net_message.data, net_message.cursize, &broadcastaddress);
- SZ_Clear(&net_message);
+ MSG_WriteLong(&cl_message, 0);
+ MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
+ MSG_WriteString(&cl_message, "QUAKE");
+ MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
+ StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
+ SZ_Clear(&cl_message);
// search LAN for DarkPlaces servers
NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
Con_Print("No server running, can not heartbeat to master server.\n");
}
-void PrintStats(netconn_t *conn)
+static void PrintStats(netconn_t *conn)
{
if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
else
Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
+ Con_Printf("unreliable messages sent = %i\n", conn->unreliableMessagesSent);
+ Con_Printf("unreliable messages recv = %i\n", conn->unreliableMessagesReceived);
+ Con_Printf("reliable messages sent = %i\n", conn->reliableMessagesSent);
+ Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
+ Con_Printf("packetsSent = %i\n", conn->packetsSent);
+ Con_Printf("packetsReSent = %i\n", conn->packetsReSent);
+ Con_Printf("packetsReceived = %i\n", conn->packetsReceived);
+ Con_Printf("receivedDuplicateCount = %i\n", conn->receivedDuplicateCount);
+ Con_Printf("droppedDatagrams = %i\n", conn->droppedDatagrams);
}
void Net_Stats_f(void)
{
netconn_t *conn;
- Con_Printf("unreliable messages sent = %i\n", unreliableMessagesSent);
- Con_Printf("unreliable messages recv = %i\n", unreliableMessagesReceived);
- Con_Printf("reliable messages sent = %i\n", reliableMessagesSent);
- Con_Printf("reliable messages received = %i\n", reliableMessagesReceived);
- Con_Printf("packetsSent = %i\n", packetsSent);
- Con_Printf("packetsReSent = %i\n", packetsReSent);
- Con_Printf("packetsReceived = %i\n", packetsReceived);
- Con_Printf("receivedDuplicateCount = %i\n", receivedDuplicateCount);
- Con_Printf("droppedDatagrams = %i\n", droppedDatagrams);
Con_Print("connections =\n");
for (conn = netconn_list;conn;conn = conn->next)
PrintStats(conn);
Cvar_RegisterVariable(&net_messagetimeout);
Cvar_RegisterVariable(&net_connecttimeout);
Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
+ Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
+ Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
Cvar_RegisterVariable(&cl_netlocalping);
Cvar_RegisterVariable(&cl_netpacketloss_send);
Cvar_RegisterVariable(&cl_netpacketloss_receive);
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]);
}
cl_numsockets = 0;
sv_numsockets = 0;
- net_message.data = net_message_buf;
- net_message.maxsize = sizeof(net_message_buf);
- net_message.cursize = 0;
+ cl_message.data = cl_message_buf;
+ cl_message.maxsize = sizeof(cl_message_buf);
+ cl_message.cursize = 0;
+ sv_message.data = sv_message_buf;
+ sv_message.maxsize = sizeof(sv_message_buf);
+ sv_message.cursize = 0;
LHNET_Init();
+ if (Thread_HasThreads())
+ netconn_mutex = Thread_CreateMutex();
}
void NetConn_Shutdown(void)
NetConn_CloseClientPorts();
NetConn_CloseServerPorts();
LHNET_Shutdown();
+ if (netconn_mutex)
+ Thread_DestroyMutex(netconn_mutex);
+ netconn_mutex = NULL;
}