]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - netconn.c
edu2p game: change 2nd game dir to 'edu2'
[xonotic/darkplaces.git] / netconn.c
index 70866bfed05c5c0df6f9ff2007bb25aa0dc3f520..27f16c1d8c5f21f6547e733b2c08689a7a3111ba 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -73,9 +73,12 @@ static cvar_t cl_netpacketloss_receive = {0, "cl_netpacketloss_receive","0", "dr
 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;
@@ -97,6 +100,7 @@ int serverreplycount = 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)
@@ -193,16 +197,16 @@ static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_ent
                        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" );
@@ -563,18 +567,18 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                        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)
                {
@@ -590,7 +594,7 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                        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
@@ -711,10 +715,10 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        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;
 
@@ -784,7 +788,10 @@ void NetConn_OpenClientPorts(void)
        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);
@@ -1201,7 +1208,6 @@ void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peer
        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");
@@ -1285,12 +1291,16 @@ static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
        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;
 }
@@ -1674,6 +1684,10 @@ void NetConn_QueryQueueFrame(void)
        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;
 
@@ -1743,7 +1757,8 @@ void NetConn_QueryQueueFrame(void)
                        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;
                }
@@ -1960,6 +1975,40 @@ void NetConn_ClearConnectFlood(lhnetaddress_t *peeraddress)
        }
 }
 
+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)
 {
@@ -2150,48 +2199,55 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        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;
@@ -2600,7 +2656,7 @@ static void Net_Heartbeat_f(void)
 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);
 }
@@ -2669,10 +2725,13 @@ void NetConn_Init(void)
        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);