static cvar_t net_slist_queriespersecond = {0, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
static cvar_t net_slist_queriesperframe = {0, "net_slist_queriesperframe", "4", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
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 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"};
/* statistic counters */
static int packetsSent = 0;
// this is only false if there are still servers left to query
static qboolean serverlist_querysleep = true;
+static qboolean serverlist_paused = false;
// this is pushed a second or two ahead of realtime whenever a master server
// reply is received, to avoid issuing queries while master replies are still
// flooding in (which would make a mess of the ping times)
result = strcmp( B->info.cname, A->info.cname );
break;
case SLIF_GAME:
- result = strcmp( B->info.game, A->info.game );
+ result = strcasecmp( B->info.game, A->info.game );
break;
case SLIF_MAP:
- result = strcmp( B->info.map, A->info.map );
+ result = strcasecmp( B->info.map, A->info.map );
break;
case SLIF_MOD:
- result = strcmp( B->info.mod, A->info.mod );
+ result = strcasecmp( B->info.mod, A->info.mod );
break;
case SLIF_NAME:
- result = strcmp( B->info.name, A->info.name );
+ result = strcasecmp( B->info.name, A->info.name );
break;
default:
Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
sendreliable = true;
}
// outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
- *((int *)(sendbuffer + 0)) = LittleLong((unsigned int)conn->qw.outgoing_sequence | ((unsigned int)sendreliable<<31));
+ *((int *)(sendbuffer + 0)) = LittleLong((unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31));
// last received unreliable packet number, and last received reliable packet number (0 or 1)
*((int *)(sendbuffer + 4)) = LittleLong((unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
packetLen = 8;
- conn->qw.outgoing_sequence++;
+ conn->outgoing_unreliable_sequence++;
// client sends qport in every packet
if (conn == cls.netcon)
{
*((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
packetLen += 2;
// also update cls.qw_outgoing_sequence
- cls.qw_outgoing_sequence = conn->qw.outgoing_sequence;
+ cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
}
if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
{
conn->outgoing_reliablesize[conn->outgoing_packetcounter] += conn->sendMessageLength;
memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
packetLen += conn->sendMessageLength;
- conn->qw.last_reliable_sequence = conn->qw.outgoing_sequence;
+ conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
}
// add the unreliable message if possible
header = (unsigned int *)sendbuffer;
header[0] = BigLong(packetLen | NETFLAG_UNRELIABLE);
- header[1] = BigLong(conn->nq.unreliableSendSequence);
+ header[1] = BigLong(conn->outgoing_unreliable_sequence);
memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
- conn->nq.unreliableSendSequence++;
+ conn->outgoing_unreliable_sequence++;
conn->outgoing_unreliablesize[conn->outgoing_packetcounter] += packetLen;
port = bound(0, cl_netport.integer, 65535);
if (cl_netport.integer != port)
Cvar_SetValueQuick(&cl_netport, port);
- Con_Printf("Client using port %i\n", port);
+ if(port == 0)
+ Con_Printf("Client using an automatically assigned port\n");
+ else
+ 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);
cls.signon = 0; // need all the signon messages before playing
cls.protocol = initialprotocol;
// reset move sequence numbering on this new connection
- cls.movesequence = 1;
cls.servermovesequence = 0;
if (cls.protocol == PROTOCOL_QUAKEWORLD)
Cmd_ForwardStringToServer("new");
dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5d^7 ^%c%3u^7/%3u %-65.65s", info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'), (int)info->ping, ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'), info->numplayers, info->maxplayers, info->name);
dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game, (info->gameversion != gameversion.integer) ? '1' : '4', info->mod, info->map);
if (entry->query == SQS_QUERIED)
- ServerList_ViewList_Remove(entry);
+ {
+ if(!serverlist_paused)
+ ServerList_ViewList_Remove(entry);
+ }
// if not in the slist menu we should print the server to console (if wanted)
else if( serverlist_consoleoutput )
Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
// and finally, update the view set
- ServerList_ViewList_Insert( entry );
+ if(!serverlist_paused)
+ ServerList_ViewList_Insert( entry );
// update the entry's state
serverlist_cache[n].query = SQS_QUERIED;
}
double timeouttime;
static double querycounter = 0;
+ if(!net_slist_pause.integer && serverlist_paused)
+ ServerList_RebuildViewList();
+ serverlist_paused = net_slist_pause.integer;
+
if (serverlist_querysleep)
return;
if( entry->query == SQS_REFRESHING ) {
// yes, so update the reply count (since its not responding anymore)
serverreplycount--;
- ServerList_ViewList_Remove(entry);
+ if(!serverlist_paused)
+ ServerList_ViewList_Remove(entry);
}
entry->query = SQS_TIMEDOUT;
}
}
}
+qboolean RCon_Authenticate(const char *password, const char *s, const char *endpos)
+{
+ const char *text;
+
+ if(!strcmp(rcon_password.string, password))
+ return true;
+
+ if(strcmp(rcon_restricted_password.string, password))
+ return false;
+
+ for(text = s; text != endpos; ++text)
+ if(*text > 0 && (*text < ' ' || *text == ';'))
+ return false; // block possible exploits against the parser/alias expansion
+
+ while(s != endpos)
+ {
+ size_t l = strlen(s);
+ if(l)
+ {
+ text = s;
+
+ if (!COM_ParseToken_Console(&text))
+ return false;
+
+ // com_token now contains the command
+ if(!strstr(va(" %s ", rcon_restricted_commands.string), va(" %s ", com_token)))
+ return false;
+ }
+ s += l + 1;
+ }
+
+ return true;
+}
+
extern void SV_SendServerinfo (client_t *client);
static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
{
if(*s <= ' ' && s != endpos) // skip leading ugly space
++s;
password[i] = 0;
- if (password[0] > ' ' && !strcmp(rcon_password.string, password))
+ if (password[0] > ' ')
{
- // looks like a legitimate rcon command with the correct password
- char *s_ptr = s;
- Con_Printf("server received rcon command from %s:\n", host_client ? host_client->name : addressstring2);
- while(s_ptr != endpos)
+ if (RCon_Authenticate(password, s, endpos))
{
- size_t l = strlen(s_ptr);
- if(l)
- Con_Printf(" %s;", s_ptr);
- s_ptr += l + 1;
- }
- Con_Printf("\n");
- rcon_redirect = true;
- rcon_redirect_bufferpos = 0;
- while(s != endpos)
- {
- size_t l = strlen(s);
- if(l)
- Cmd_ExecuteString(s, src_command);
- s += l + 1;
- }
- rcon_redirect_buffer[rcon_redirect_bufferpos] = 0;
- rcon_redirect = false;
- // print resulting text to client
- // if client is playing, send a reliable reply instead of
- // a command packet
- if (host_client)
- {
- // if the netconnection is loop, then this is the
- // local player on a listen mode server, and it would
- // result in duplicate printing to the console
- // (not that the local player should be using rcon
- // when they have the console)
- if (host_client->netconnection && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
- SV_ClientPrintf("%s", rcon_redirect_buffer);
+ // looks like a legitimate rcon command with the correct password
+ char *s_ptr = s;
+ Con_Printf("server received rcon command from %s:\n", 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");
+ rcon_redirect = true;
+ rcon_redirect_bufferpos = 0;
+ while(s != endpos)
+ {
+ size_t l = strlen(s);
+ if(l)
+ Cmd_ExecuteString(s, src_command);
+ s += l + 1;
+ }
+ rcon_redirect_buffer[rcon_redirect_bufferpos] = 0;
+ rcon_redirect = false;
+ // print resulting text to client
+ // if client is playing, send a reliable reply instead of
+ // a command packet
+ if (host_client)
+ {
+ // if the netconnection is loop, then this is the
+ // local player on a listen mode server, and it would
+ // result in duplicate printing to the console
+ // (not that the local player should be using rcon
+ // when they have the console)
+ if (host_client->netconnection && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
+ SV_ClientPrintf("%s", rcon_redirect_buffer);
+ }
+ else
+ {
+ // qw print command
+ dpsnprintf(response, sizeof(response), "\377\377\377\377n%s", rcon_redirect_buffer);
+ NetConn_WriteString(mysocket, response, peeraddress);
+ }
}
else
{
- // qw print command
- dpsnprintf(response, sizeof(response), "\377\377\377\377n%s", rcon_redirect_buffer);
- NetConn_WriteString(mysocket, response, peeraddress);
+ Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
}
}
return true;
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->qw.outgoing_sequence, conn->qw.incoming_sequence);
+ 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);
}
Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
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(&net_slist_queriespersecond);
Cvar_RegisterVariable(&net_slist_queriesperframe);
Cvar_RegisterVariable(&net_slist_timeout);
Cvar_RegisterVariable(&net_slist_maxtries);
+ Cvar_RegisterVariable(&net_slist_pause);
Cvar_RegisterVariable(&net_messagetimeout);
Cvar_RegisterVariable(&net_connecttimeout);
Cvar_RegisterVariable(&net_connectfloodblockingtimeout);