#include "quakedef.h"
#include "lhnet.h"
+// for secure rcon authentication
+#include "hmac.h"
+#include "mdfour.h"
+#include <time.h>
+
#define QWMASTER_PORT 27000
#define DPMASTER_PORT 27950
{CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
{CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
{CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
- {0, "sv_masterextra1", "ghdigital.com", "default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
- {0, "sv_masterextra2", "dpmaster.deathmask.net", "default master server 2 (admin: Willis)"}, // admin: Willis
- {0, "sv_masterextra3", "dpmaster.tchr.no", "default master server 3 (admin: tChr)"}, // admin: tChr
+ {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
{0, NULL, NULL, NULL}
};
static unsigned char net_message_buf[NET_MAXMESSAGE];
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","10", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods)"};
+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 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)"};
static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
static cvar_t net_slist_pause = {0, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"};
static cvar_t net_slist_maxtries = {0, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
-static cvar_t net_slist_favorites = {CVAR_SAVE, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
-
+static cvar_t net_slist_favorites = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible"};
static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode"};
static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
+static cvar_t rcon_secure_maxdiff = {0, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
+extern cvar_t rcon_secure;
/* statistic counters */
static int packetsSent = 0;
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"};
cvar_t net_address = {0, "net_address", "0.0.0.0", "network address to open ports on"};
-//cvar_t net_netaddress_ipv6 = {0, "net_address_ipv6", "[0:0:0:0:0:0:0:0]", "network address to open ipv6 ports on"};
+cvar_t net_address_ipv6 = {0, "net_address_ipv6", "[0:0:0:0:0:0:0:0]", "network address to open ipv6 ports on"};
char net_extresponse[NET_EXTRESPONSE_MAX][1400];
int net_extresponse_count = 0;
qboolean serverlist_consoleoutput;
+static int nFavorites = 0;
+static lhnetaddress_t favorites[256];
+
+void NetConn_UpdateFavorites()
+{
+ const char *p;
+ nFavorites = 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;
+ }
+}
+
// helper function to insert a value into the viewset
// spare entries will be removed
static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
{
- int start, end, mid;
- const char *text;
+ int start, end, mid, i;
+ lhnetaddress_t addr;
// reject incompatible servers
if (entry->info.gameversion != gameversion.integer)
return;
// refresh the "favorite" status
- text = net_slist_favorites.string;
entry->info.isfavorite = false;
- while(COM_ParseToken_Console(&text))
+ if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
{
- if(!strcmp(com_token, entry->info.cname))
+ for(i = 0; i < nFavorites; ++i)
{
- entry->info.isfavorite = true;
- break;
+ if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
+ {
+ entry->info.isfavorite = true;
+ break;
+ }
}
}
Con_Printf("Client using port %i\n", port);
NetConn_OpenClientPort("local:2", 0);
NetConn_OpenClientPort(net_address.string, port);
- //NetConn_OpenClientPort(net_address_ipv6.string, port);
+ NetConn_OpenClientPort(net_address_ipv6.string, port);
}
void NetConn_CloseServerPorts(void)
if (opennetports)
{
NetConn_OpenServerPort(net_address.string, port);
- //NetConn_OpenServerPort(net_address_ipv6.string, port);
+ NetConn_OpenServerPort(net_address_ipv6.string, port);
}
if (sv_numsockets == 0)
Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
if (serverlist_consoleoutput)
Con_Printf("querying %s\n", addressstring);
++serverlist_cachecount;
-
-#if 0
- // we should not NEED this part...
- text = net_slist_favorites.string;
- while(COM_ParseToken_Console(&text))
- {
- if(!strcmp(com_token, addressstring))
- entry->isfavorite = 1;
- }
-#endif
}
// if this is the first reply from this server, count it as having replied
pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
return true;
}
+static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
+{
+ masterreplycount++;
+ if (serverlist_consoleoutput)
+ Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
+ while (length >= 7)
+ {
+ char ipstring [128];
+
+ // IPv4 address
+ if (data[0] == '\\')
+ {
+ unsigned short port = data[5] * 256 + data[6];
+
+ if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
+ dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
+
+ // move on to next address in packet
+ data += 7;
+ length -= 7;
+ }
+ // IPv6 address
+ else if (data[0] == '/' && isextended && length >= 19)
+ {
+ unsigned short port = data[17] * 256 + data[18];
+
+ if (port != 0)
+ {
+ 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],
+ ifname, port);
+ }
+ else
+ {
+ 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],
+ port);
+ }
+ }
+
+ // move on to next address in packet
+ data += 19;
+ length -= 19;
+ }
+ else
+ {
+ Con_Print("Error while parsing the server list\n");
+ break;
+ }
+
+ if (serverlist_consoleoutput && developer_networking.integer)
+ Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
+
+ if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
+ break;
+ }
+
+ }
+
+ // begin or resume serverlist queries
+ serverlist_querysleep = false;
+ serverlist_querywaittime = realtime + 3;
+}
+
static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
{
qboolean fromserver;
char rejectreason[32];
cls.connect_trying = false;
string += 7;
- length = max(length - 7, (int)sizeof(rejectreason) - 1);
+ length = min(length - 7, (int)sizeof(rejectreason) - 1);
memcpy(rejectreason, string, length);
rejectreason[length] = 0;
M_Update_Return_Reason(rejectreason);
// Extract the IP addresses
data += 18;
length -= 18;
- masterreplycount++;
- if (serverlist_consoleoutput)
- Con_Print("received DarkPlaces server list...\n");
- while (length >= 7 && data[0] == '\\' && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF) && data[5] * 256 + data[6] != 0)
- {
- dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[1], data[2], data[3], data[4], data[5] * 256 + data[6]);
- if (serverlist_consoleoutput && developer_networking.integer)
- Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
-
- if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
- break;
- }
-
- // move on to next address in packet
- data += 7;
- length -= 7;
- }
- // begin or resume serverlist queries
- serverlist_querysleep = false;
- serverlist_querywaittime = realtime + 3;
+ NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
+ return true;
+ }
+ if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
+ {
+ // Extract the IP addresses
+ data += 21;
+ length -= 21;
+ NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
return true;
}
if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
// (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)
{
- const char *qcstatus = NULL;
+ char qcstatus[256];
unsigned int nb_clients = 0, nb_bots = 0, i;
int length;
+ char teambuf[3];
SV_VM_Begin();
}
}
+ *qcstatus = 0;
if(prog->globaloffsets.worldstatus >= 0)
{
const char *str = PRVM_G_STRING(prog->globaloffsets.worldstatus);
{
char *p;
const char *q;
- qcstatus = p = Mem_Alloc(tempmempool, strlen(str) + 1);
+ p = qcstatus;
for(q = str; *q; ++q)
- if(*q != '\\')
+ if(*q != '\\' && *q != '\n')
*p++ = *q;
*p = 0;
}
fullstatus ? "statusResponse" : "infoResponse",
gamename, com_modname, gameversion.integer, svs.maxclients,
nb_clients, nb_bots, sv.name, hostname.string, NET_PROTOCOL_VERSION,
- qcstatus ? "\\qcstatus\\" : "", qcstatus ? qcstatus : "",
+ *qcstatus ? "\\qcstatus\\" : "", qcstatus,
challenge ? "\\challenge\\" : "", challenge ? challenge : "",
fullstatus ? "\n" : "");
- if(qcstatus)
- {
- Mem_Free((char *)qcstatus);
- qcstatus = NULL;
- }
-
// Make sure it fits in the buffer
if (length < 0)
goto bad;
break;
}
} while (curchar != '\0');
+ cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
pingvalue = (int)(cl->ping * 1000.0f);
if(cl->netconnection)
else
pingvalue = 0;
+ *qcstatus = 0;
if(prog->fieldoffsets.clientstatus >= 0)
{
const char *str = PRVM_E_STRING(PRVM_EDICT_NUM(i + 1), prog->fieldoffsets.clientstatus);
{
char *p;
const char *q;
- qcstatus = p = Mem_Alloc(tempmempool, strlen(str) + 1);
- for(q = str; *q; ++q)
- if(*q != '\\' && *q != ' ')
+ p = qcstatus;
+ for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
+ if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
*p++ = *q;
*p = 0;
}
}
- if(qcstatus)
+ if ((gamemode == GAME_NEXUIZ) && (teamplay.integer > 0))
{
- length = dpsnprintf(ptr, left, "%s %d \"%s\"\n",
+ if(cl->frags == -666) // spectator
+ strlcpy(teambuf, " 0", sizeof(teambuf));
+ else if(cl->colors == 0x44) // red team
+ strlcpy(teambuf, " 1", sizeof(teambuf));
+ else if(cl->colors == 0xDD) // blue team
+ strlcpy(teambuf, " 2", sizeof(teambuf));
+ else if(cl->colors == 0xCC) // yellow team
+ strlcpy(teambuf, " 3", sizeof(teambuf));
+ else if(cl->colors == 0x99) // pink team
+ strlcpy(teambuf, " 4", sizeof(teambuf));
+ else
+ strlcpy(teambuf, " 0", sizeof(teambuf));
+ }
+ else
+ *teambuf = 0;
+
+ // note: team number is inserted according to SoF2 protocol
+ if(*qcstatus)
+ length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
qcstatus,
pingvalue,
+ teambuf,
cleanname);
- Mem_Free((char *)qcstatus);
- qcstatus = NULL;
- }
else
- length = dpsnprintf(ptr, left, "%d %d \"%s\"\n",
+ length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
cl->frags,
pingvalue,
+ teambuf,
cleanname);
if(length < 0)
}
}
+typedef qboolean (*rcon_matchfunc_t) (const char *password, const char *hash, const char *s, int slen);
+
+qboolean hmac_mdfour_matching(const char *password, const char *hash, const char *s, int slen)
+{
+ char mdfourbuf[16];
+ long t1, t2;
+
+ t1 = (long) time(NULL);
+ t2 = strtol(s, NULL, 0);
+ if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
+ return false;
+
+ if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
+ return false;
+
+ return !memcmp(mdfourbuf, hash, 16);
+}
+
+qboolean plaintext_matching(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(const char *password, const char *s, const char *endpos)
+const char *RCon_Authenticate(const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
{
const char *text;
qboolean hasquotes;
- if(!strcmp(rcon_password.string, password))
+ if(comparator(rcon_password.string, password, cs, cslen))
return "rcon";
- if(strcmp(rcon_restricted_password.string, password))
+ if(!comparator(rcon_restricted_password.string, password, cs, cslen))
return NULL;
for(text = s; text != endpos; ++text)
return "restricted rcon";
}
+void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos)
+{
+ if(userlevel)
+ {
+ // looks like a legitimate rcon command with the correct password
+ const char *s_ptr = s;
+ Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
+ while(s_ptr != endpos)
+ {
+ size_t l = strlen(s_ptr);
+ if(l)
+ Con_Printf(" %s;", s_ptr);
+ s_ptr += l + 1;
+ }
+ Con_Printf("\n");
+
+ if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
+ Con_Rcon_Redirect_Init(mysocket, peeraddress);
+ while(s != endpos)
+ {
+ size_t l = strlen(s);
+ if(l)
+ {
+ client_t *host_client_save = host_client;
+ Cmd_ExecuteString(s, src_command);
+ host_client = host_client_save;
+ // in case it is a command that changes host_client (like restart)
+ }
+ s += l + 1;
+ }
+ Con_Rcon_Redirect_End();
+ }
+ else
+ {
+ Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
+ }
+}
+
extern void SV_SendServerinfo (client_t *client);
static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
{
return true;
// check engine protocol
- if (strcmp(SearchInfostring(string, "protocol"), "darkplaces 3"))
+ if(!(s = SearchInfostring(string, "protocol")) || strcmp(s, "darkplaces 3"))
{
if (developer.integer >= 10)
Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
}
return true;
}
+ if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
+ {
+ char *password = string + 20;
+ char *timeval = string + 37;
+ char *s = strchr(timeval, ' ');
+ char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
+ const char *userlevel;
+ if(!s)
+ return true; // invalid packet
+ ++s;
+
+ userlevel = RCon_Authenticate(password, s, endpos, hmac_mdfour_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
+ RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
+ return true;
+ }
if (length >= 5 && !memcmp(string, "rcon ", 5))
{
int i;
char *s = string + 5;
char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
char password[64];
+
+ if(rcon_secure.integer)
+ return true;
+
for (i = 0;!ISWHITESPACE(*s);s++)
if (i < (int)sizeof(password) - 1)
password[i++] = *s;
password[i] = 0;
if (!ISWHITESPACE(password[0]))
{
- const char *userlevel = RCon_Authenticate(password, s, endpos);
- if(userlevel)
- {
- // looks like a legitimate rcon command with the correct password
- char *s_ptr = s;
- Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
- while(s_ptr != endpos)
- {
- size_t l = strlen(s_ptr);
- if(l)
- Con_Printf(" %s;", s_ptr);
- s_ptr += l + 1;
- }
- Con_Printf("\n");
-
- if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
- Con_Rcon_Redirect_Init(mysocket, peeraddress);
- while(s != endpos)
- {
- size_t l = strlen(s);
- if(l)
- {
- client_t *host_client_save = host_client;
- Cmd_ExecuteString(s, src_command);
- host_client = host_client_save;
- // in case it is a command that changes host_client (like restart)
- }
- s += l + 1;
- }
- Con_Rcon_Redirect_End();
- }
- else
- {
- Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
- }
+ const char *userlevel = RCon_Authenticate(password, s, endpos, plaintext_matching, NULL, 0);
+ RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
}
return true;
}
void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
{
- int i;
+ int i, j;
int masternum;
lhnetaddress_t masteraddress;
lhnetaddress_t broadcastaddress;
- lhnetaddress_t serveraddress;
- const char *text;
char request[256];
if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
{
if (cl_sockets[i])
{
+ const char *cmdname, *extraoptions;
int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
}
// build the getservers message to send to the dpmaster master servers
- dpsnprintf(request, sizeof(request), "\377\377\377\377getservers %s %u empty full\x0A", gamename, NET_PROTOCOL_VERSION);
+ if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
+ {
+ cmdname = "getserversExt";
+ extraoptions = " ipv4 ipv6"; // ask for IPv4 and IPv6 servers
+ }
+ else
+ {
+ cmdname = "getservers";
+ extraoptions = "";
+ }
+ dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamename, NET_PROTOCOL_VERSION, extraoptions);
// search internet
for (masternum = 0;sv_masters[masternum].name;masternum++)
}
// search favorite servers
- text = net_slist_favorites.string;
- while(COM_ParseToken_Console(&text))
+ for(j = 0; j < nFavorites; ++j)
{
- if(LHNETADDRESS_FromString(&serveraddress, com_token, 26000) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
+ if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
{
- NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, com_token, true );
+ if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
+ NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
}
}
}
}
// search favorite servers
- text = net_slist_favorites.string;
- while(COM_ParseToken_Console(&text))
+ for(j = 0; j < nFavorites; ++j)
{
- if(LHNETADDRESS_FromString(&serveraddress, com_token, 26000) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
+ if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
{
- // writing AND querying to catch replies for both
- // protocols (in case DP has been queried above, this
- // would only try the DP protocol otherwise)
- NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &serveraddress);
- NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, com_token, true );
+ if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
+ {
+ NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
+ NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
+ }
}
}
}
Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
Cvar_RegisterVariable(&rcon_restricted_password);
Cvar_RegisterVariable(&rcon_restricted_commands);
+ Cvar_RegisterVariable(&rcon_secure_maxdiff);
Cvar_RegisterVariable(&net_slist_queriespersecond);
Cvar_RegisterVariable(&net_slist_queriesperframe);
Cvar_RegisterVariable(&net_slist_timeout);
Cvar_RegisterVariable(&cl_netport);
Cvar_RegisterVariable(&sv_netport);
Cvar_RegisterVariable(&net_address);
- //Cvar_RegisterVariable(&net_address_ipv6);
+ Cvar_RegisterVariable(&net_address_ipv6);
Cvar_RegisterVariable(&sv_public);
Cvar_RegisterVariable(&sv_heartbeatperiod);
for (i = 0;sv_masters[i].name;i++)