]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - host_cmd.c
drawrotpic:
[xonotic/darkplaces.git] / host_cmd.c
index 77c1bfcef2675eaa94775fe3d0bb39194f263d33..ccf6c5bc5c87d114caede29c5563e27fdae1747d 100644 (file)
@@ -22,12 +22,18 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "sv_demo.h"
 #include "image.h"
 
+// for secure rcon authentication
+#include "hmac.h"
+#include "mdfour.h"
+#include <time.h>
+
 int current_skill;
 cvar_t sv_cheats = {0, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
 cvar_t sv_adminnick = {CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
 cvar_t sv_status_privacy = {CVAR_SAVE, "sv_status_privacy", "0", "do not show IP addresses in 'status' replies to clients"};
 cvar_t sv_status_show_qcstatus = {CVAR_SAVE, "sv_status_show_qcstatus", "0", "show the 'qcstatus' field in status replies, not the 'frags' field. Turn this on if your mod uses this field, and the 'frags' field on the other hand has no meaningful value."};
-cvar_t rcon_password = {CVAR_PRIVATE, "rcon_password", "", "password to authenticate rcon commands"};
+cvar_t rcon_password = {CVAR_PRIVATE, "rcon_password", "", "password to authenticate rcon commands; NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password"};
+cvar_t rcon_secure = {CVAR_NQUSERINFOHACK, "rcon_secure", "0", "force secure rcon authentication; NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password"};
 cvar_t rcon_address = {0, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"};
 cvar_t team = {CVAR_USERINFO | CVAR_SAVE, "team", "none", "QW team (4 character limit, example: blue)"};
 cvar_t skin = {CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (example: base)"};
@@ -65,7 +71,7 @@ void Host_Status_f (void)
        void (*print) (const char *fmt, ...);
        char ip[22];
        int frags;
-       
+
        if (cmd_source == src_command)
        {
                // if running a client, try to send over network so the client's status report parser will see the report
@@ -82,6 +88,9 @@ void Host_Status_f (void)
        if (!sv.active)
                return;
        
+       if(cmd_source == src_command)
+               SV_VM_Begin();
+       
        in = 0;
        if (Cmd_Argc() == 2)
        {
@@ -187,6 +196,9 @@ void Host_Status_f (void)
                        print ("^7%s\n", client->name);
                }
        }
+
+       if(cmd_source == src_command)
+               SV_VM_End();
 }
 
 
@@ -357,6 +369,14 @@ void Host_Map_f (void)
        CL_Disconnect ();
        Host_ShutdownServer();
 
+       if(svs.maxclients != svs.maxclients_next)
+       {
+               svs.maxclients = svs.maxclients_next;
+               if (svs.clients)
+                       Mem_Free(svs.clients);
+               svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
+       }
+
        // remove menu
        key_dest = key_game;
 
@@ -505,6 +525,9 @@ void Host_Connect_f (void)
                Con_Print("connect <serveraddress> : connect to a multiplayer game\n");
                return;
        }
+       // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
+       if(!rcon_secure.integer)
+               Cvar_SetQuick(&rcon_password, "");
        CL_EstablishConnection(Cmd_Argv(1));
 }
 
@@ -919,7 +942,7 @@ void Host_Loadgame_f (void)
                                        if (i >= 0 && i < MAX_MODELS)
                                        {
                                                strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
-                                               sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, false);
+                                               sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.modelname : NULL);
                                        }
                                        else
                                                Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
@@ -1042,7 +1065,7 @@ void Host_Name_f (void)
                                i++;
                                continue;
                        }
-                       if (host_client->name[i+1] == STRING_COLOR_RGB_DEFAULT && isxdigit(host_client->name[i+2]) && isxdigit(host_client->name[i+3]) && isxdigit(host_client->name[i+4]))
+                       if (host_client->name[i+1] == STRING_COLOR_RGB_TAG_CHAR && isxdigit(host_client->name[i+2]) && isxdigit(host_client->name[i+3]) && isxdigit(host_client->name[i+4]))
                        {
                                j = i;
                                i += 4;
@@ -2070,7 +2093,7 @@ void Host_Viewmodel_f (void)
        if (!e)
                return;
 
-       m = Mod_ForName (Cmd_Argv(1), false, true, false);
+       m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
        if (!m || !m->loaded || !m->Draw)
        {
                Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
@@ -2295,24 +2318,21 @@ static void MaxPlayers_f(void)
 
        if (Cmd_Argc() != 2)
        {
-               Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients);
+               Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
                return;
        }
 
        if (sv.active)
        {
                Con_Print("maxplayers can not be changed while a server is running.\n");
-               return;
+               Con_Print("It will be changed on next server startup (\"map\" command).\n");
        }
 
        n = atoi(Cmd_Argv(1));
        n = bound(1, n, MAX_SCOREBOARD);
        Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
 
-       if (svs.clients)
-               Mem_Free(svs.clients);
-       svs.maxclients = n;
-       svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
+       svs.maxclients_next = n;
        if (n == 1)
                Cvar_Set ("deathmatch", "0");
        else
@@ -2367,7 +2387,23 @@ void Host_Rcon_f (void) // credit: taken from QuakeWorld
        if (mysocket)
        {
                // simply put together the rcon packet and send it
-               NetConn_WriteString(mysocket, va("\377\377\377\377rcon %s %s", rcon_password.string, Cmd_Args()), &to);
+               if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer)
+               {
+                       char buf[1500];
+                       char argbuf[1500];
+                       dpsnprintf(argbuf, sizeof(argbuf), "%ld %s", (long) time(NULL), Cmd_Args());
+                       memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
+                       if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, strlen(rcon_password.string)))
+                       {
+                               buf[40] = ' ';
+                               strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
+                               NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
+                       }
+               }
+               else
+               {
+                       NetConn_WriteString(mysocket, va("\377\377\377\377rcon %s %s", rcon_password.string, Cmd_Args()), &to);
+               }
        }
 }
 
@@ -2745,7 +2781,9 @@ void Host_InitCommands (void)
 
        Cvar_RegisterVariable (&rcon_password);
        Cvar_RegisterVariable (&rcon_address);
+       Cvar_RegisterVariable (&rcon_secure);
        Cmd_AddCommand ("rcon", Host_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's)");
+       Cmd_AddCommand ("srcon", Host_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's); this always works as if rcon_secure is set");
        Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
        Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
        Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");