added qw compatible "rcon" support (allows a client who knows the server's rcon_passw...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 23 Feb 2006 14:50:41 +0000 (14:50 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 23 Feb 2006 14:50:41 +0000 (14:50 +0000)
added qw print command packet to client packet processing (needed for remote rcon when not connected to the server)
added qw "packet" command (sends a text message to the specified address)

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@6017 d7cf8633-e32d-0410-b094-e92efae38249

client.h
console.c
console.h
host.c
host_cmd.c
netconn.c
netconn.h
sv_user.c

index b917e48..f3a8bac 100644 (file)
--- a/client.h
+++ b/client.h
@@ -659,6 +659,9 @@ extern cvar_t cl_pmodel;
 extern cvar_t cl_playermodel;
 extern cvar_t cl_playerskin;
 
+extern cvar_t rcon_password;
+extern cvar_t rcon_address;
+
 extern cvar_t cl_upspeed;
 extern cvar_t cl_forwardspeed;
 extern cvar_t cl_backspeed;
index 7c19771..cd11547 100644 (file)
--- a/console.c
+++ b/console.c
@@ -53,6 +53,11 @@ int con_vislines;
 
 qboolean con_initialized;
 
+// used for server replies to rcon command
+qboolean rcon_redirect = false;
+int rcon_redirect_bufferpos = 0;
+char rcon_redirect_buffer[1400];
+
 
 /*
 ==============================================================================
@@ -561,9 +566,13 @@ void Con_Print(const char *msg)
 
        for (;*msg;msg++)
        {
+               // if this print is in response to an rcon command, add the character
+               // to the rcon redirect buffer
+               if (rcon_redirect && rcon_redirect_bufferpos < (int)sizeof(rcon_redirect_buffer) - 1)
+                       rcon_redirect_buffer[rcon_redirect_bufferpos++] = *msg;
+               // if this is the beginning of a new line, print timestamp
                if (index == 0)
                {
-                       // if this is the beginning of a new line, print timestamp
                        const char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
                        // reset the color
                        // FIXME: 1. perhaps we should use a terminal system 2. use a constant instead of 7!
index 644aac0..4b8048c 100644 (file)
--- a/console.h
+++ b/console.h
@@ -28,6 +28,10 @@ extern int con_totallines;
 extern int con_backscroll;
 extern qboolean con_initialized;
 
+extern qboolean rcon_redirect;
+extern int rcon_redirect_bufferpos;
+extern char rcon_redirect_buffer[1400];
+
 void Con_CheckResize (void);
 void Con_Init (void);
 void Con_Init_Commands (void);
diff --git a/host.c b/host.c
index e63a7f7..6edefc9 100644 (file)
--- a/host.c
+++ b/host.c
@@ -122,6 +122,9 @@ void Host_Error (const char *error, ...)
        static qboolean hosterror = false;
        va_list argptr;
 
+       // turn off rcon redirect if it was active when the crash occurred
+       rcon_redirect = false;
+
        va_start (argptr,error);
        dpvsnprintf (hosterrorstring1,sizeof(hosterrorstring1),error,argptr);
        va_end (argptr);
@@ -697,10 +700,6 @@ void Host_ServerFrame (void)
                // set the time and clear the general datagram
                SV_ClearDatagram();
 
-               // check for network packets to the server each world step incase they
-               // come in midframe (particularly if host is running really slow)
-               NetConn_ServerFrame();
-
                // move things around and think unless paused
                if (sv.frametime)
                        SV_Physics();
@@ -770,6 +769,11 @@ void _Host_Frame (float time)
 //
 //-------------------
 
+       // receive server packets now, which might contain rcon commands, which
+       // may change level or other such things we don't want to have happen in
+       // the middle of Host_Frame
+       NetConn_ServerFrame();
+
        // check for commands typed to the host
        Host_GetConsoleCommands();
 
index 5aae255..70572da 100644 (file)
@@ -22,6 +22,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 int current_skill;
 cvar_t sv_cheats = {0, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
+cvar_t rcon_password = {0, "rcon_password", "", "password to authenticate rcon commands"};
+cvar_t rcon_address = {0, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"};
 qboolean allowcheats = false;
 
 /*
@@ -788,6 +790,7 @@ void Host_Name_f (void)
        host_client->nametime = sv.time + 5;
 
        // point the string back at updateclient->name to keep it safe
+       SV_VM_Begin();
        strlcpy (host_client->name, newName, sizeof (host_client->name));
        host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
        if (strcmp(host_client->old_name, host_client->name))
@@ -800,6 +803,7 @@ void Host_Name_f (void)
                MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
                MSG_WriteString (&sv.reliable_datagram, host_client->name);
        }
+       SV_VM_End();
 }
 
 /*
@@ -848,6 +852,7 @@ void Host_Playermodel_f (void)
        host_client->nametime = sv.time + 5;
        */
 
+       SV_VM_Begin();
        // point the string back at updateclient->name to keep it safe
        strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
        if( eval_playermodel )
@@ -860,6 +865,7 @@ void Host_Playermodel_f (void)
                MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
                MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
        }
+       SV_VM_End();
 }
 
 /*
@@ -907,6 +913,7 @@ void Host_Playerskin_f (void)
        host_client->nametime = sv.time + 5;
        */
 
+       SV_VM_Begin();
        // point the string back at updateclient->name to keep it safe
        strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
        if( eval_playerskin )
@@ -921,6 +928,7 @@ void Host_Playerskin_f (void)
                MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
                MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
        }
+       SV_VM_End();
 }
 
 void Host_Version_f (void)
@@ -1114,6 +1122,7 @@ void Host_Color_f(void)
                return;
        }
 
+       SV_VM_Begin();
        if (host_client->edict && (f = PRVM_ED_FindFunction ("SV_ChangeTeam")) && (SV_ChangeTeam = (func_t)(f - prog->functions)))
        {
                Con_DPrint("Calling SV_ChangeTeam\n");
@@ -1141,6 +1150,7 @@ void Host_Color_f(void)
                        MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
                }
        }
+       SV_VM_End();
 }
 
 cvar_t cl_rate = {CVAR_SAVE, "_cl_rate", "10000", "internal storage cvar for current rate (changed by rate command)"};
@@ -1187,9 +1197,11 @@ void Host_Kill_f (void)
                return;
        }
 
+       SV_VM_Begin();
        prog->globals.server->time = sv.time;
        prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
        PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
+       SV_VM_End();
 }
 
 
@@ -1248,8 +1260,10 @@ static void Host_PModel_f (void)
                return;
        }
 
+       SV_VM_Begin();
        if (host_client->edict && (val = PRVM_GETEDICTFIELDVALUE(host_client->edict, eval_pmodel)))
                val->_float = i;
+       SV_VM_End();
 }
 
 //===========================================================================
@@ -1320,6 +1334,7 @@ void Host_Spawn_f (void)
        //      SZ_Clear (&host_client->netconnection->message);
 
        // run the entrance script
+       SV_VM_Begin();
        if (sv.loadgame)
        {
                // loaded games are fully initialized already
@@ -1354,10 +1369,12 @@ void Host_Spawn_f (void)
 
                PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
        }
+       SV_VM_End();
 
        if (!host_client->netconnection)
                return;
 
+       SV_VM_Begin();
        // send time of update
        MSG_WriteByte (&host_client->netconnection->message, svc_time);
        MSG_WriteFloat (&host_client->netconnection->message, sv.time);
@@ -1420,6 +1437,7 @@ void Host_Spawn_f (void)
 
        MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
        MSG_WriteByte (&host_client->netconnection->message, 3);
+       SV_VM_End();
 }
 
 /*
@@ -1555,6 +1573,7 @@ void Host_Give_f (void)
        t = Cmd_Argv(1);
        v = atoi (Cmd_Argv(2));
 
+       SV_VM_Begin();
        switch (t[0])
        {
        case '0':
@@ -1684,6 +1703,7 @@ void Host_Give_f (void)
                }
                break;
        }
+       SV_VM_End();
 }
 
 prvm_edict_t   *FindViewthing (void)
@@ -1962,6 +1982,116 @@ static void MaxPlayers_f(void)
 
 //=============================================================================
 
+// QuakeWorld commands
+
+char emodel_name[] =
+       { 'e' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 };
+char pmodel_name[] =
+       { 'p' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 };
+char prespawn_name[] =
+       { 'p'^0xff, 'r'^0xff, 'e'^0xff, 's'^0xff, 'p'^0xff, 'a'^0xff, 'w'^0xff, 'n'^0xff,
+               ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '0'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
+char modellist_name[] =
+       { 'm'^0xff, 'o'^0xff, 'd'^0xff, 'e'^0xff, 'l'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff,
+               ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
+char soundlist_name[] =
+       { 's'^0xff, 'o'^0xff, 'u'^0xff, 'n'^0xff, 'd'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff,
+               ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
+
+/*
+=====================
+Host_Rcon_f
+
+  Send the rest of the command line over as
+  an unconnected command.
+=====================
+*/
+void Host_Rcon_f (void) // credit: taken from QuakeWorld
+{
+       lhnetaddress_t to;
+       lhnetsocket_t *mysocket;
+
+       if (!rcon_password.string)
+       {
+               Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
+               return;
+       }
+
+       if (cls.netcon)
+               to = cls.netcon->peeraddress;
+       else
+       {
+               if (!rcon_address.integer || !rcon_address.string[0])
+               {
+                       Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
+                       return;
+               }
+               LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
+       }
+       mysocket = NetConn_ChooseClientSocketForAddress(&to);
+       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);
+       }
+}
+
+/*
+====================
+Host_Packet_f
+
+packet <destination> <contents>
+
+Contents allows \n escape character
+====================
+*/
+void Host_Packet_f (void) // credit: taken from QuakeWorld
+{
+       char send[2048];
+       int i, l;
+       const char *in;
+       char *out;
+       lhnetaddress_t address;
+       lhnetsocket_t *mysocket;
+
+       if (Cmd_Argc() != 3)
+       {
+               Con_Printf ("packet <destination> <contents>\n");
+               return;
+       }
+
+       if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
+       {
+               Con_Printf ("Bad address\n");
+               return;
+       }
+
+       in = Cmd_Argv(2);
+       out = send+4;
+       send[0] = send[1] = send[2] = send[3] = 0xff;
+
+       l = strlen (in);
+       for (i=0 ; i<l ; i++)
+       {
+               if (out >= send + sizeof(send) - 1)
+                       break;
+               if (in[i] == '\\' && in[i+1] == 'n')
+               {
+                       *out++ = '\n';
+                       i++;
+               }
+               else
+                       *out++ = in[i];
+       }
+       *out = 0;
+
+       mysocket = NetConn_ChooseClientSocketForAddress(&address);
+       if (mysocket)
+               NetConn_WriteString(mysocket, send, &address);
+}
+
+//=============================================================================
+
 /*
 ==================
 Host_InitCommands
@@ -2037,6 +2167,11 @@ void Host_InitCommands (void)
 
        Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");       // By [515]
 
+       Cvar_RegisterVariable (&rcon_password);
+       Cvar_RegisterVariable (&rcon_address);
+       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 ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
+
        Cvar_RegisterVariable(&sv_cheats);
 }
 
index ef47f7e..e912595 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -930,9 +930,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                stringbuf[length] = 0;
                string = stringbuf;
 
+               LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
+
                if (developer.integer)
                {
-                       LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
                        Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
                        Com_HexDumpToConsole(data, length);
                }
@@ -941,7 +942,6 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        char protocolnames[1400];
                        Protocol_Names(protocolnames, sizeof(protocolnames));
-                       LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
                        Con_Printf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
                        M_Update_Return_Reason("Got challenge response");
                        NetConn_WriteString(mysocket, va("\377\377\377\377connect\\protocol\\darkplaces 3\\protocols\\%s\\challenge\\%s", protocolnames, string + 10), peeraddress);
@@ -1086,6 +1086,11 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                if (!strncmp(string, "ack", 3))
                        return true;
                */
+               if (string[0] == 'n')
+               {
+                       // qw print command
+                       Con_Printf("QW print command from server at %s:\n", addressstring2, string + 1);
+               }
                // we may not have liked the packet, but it was a command packet, so
                // we're done processing this packet now
                return true;
@@ -1393,7 +1398,16 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
        double besttime;
        client_t *client;
        netconn_t *conn;
-       char *s, *string, response[512], addressstring2[128], stringbuf[16384];
+       char *s, *string, response[1400], addressstring2[128], stringbuf[16384];
+
+       // see if we can identify the sender as a local player
+       // (this is necessary for rcon to send a reliable reply if the client is
+       //  actually on the server, not sending remotely)
+       for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+               if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
+                       break;
+       if (i == svs.maxclients)
+               host_client = NULL;
 
        if (sv.active)
        {
@@ -1500,7 +1514,9 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                                                                        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();
                                                                                SV_ConnectClient(clientnum, conn);
+                                                                               SV_VM_End();
                                                                                NetConn_Heartbeat(1);
                                                                        }
                                                                }
@@ -1549,6 +1565,46 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                }
                                return true;
                        }
+                       if (length >= 5 && !memcmp(string, "rcon ", 5))
+                       {
+                               int i;
+                               char *s = string + 5;
+                               char password[64];
+                               for (i = 0;*s > ' ';s++)
+                                       if (i < (int)sizeof(password) - 1)
+                                               password[i++] = *s;
+                               password[i] = 0;
+                               if (!strcmp(rcon_password.string, password))
+                               {
+                                       // looks like a legitimate rcon command with the correct password
+                                       Con_Printf("server received rcon command from %s:\n%s\n", host_client ? host_client->name : addressstring2, s);
+                                       rcon_redirect = true;
+                                       rcon_redirect_bufferpos = 0;
+                                       Cmd_ExecuteString(s, src_command);
+                                       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);
+                                       }
+                               }
+                               return true;
+                       }
                        /*
                        if (!strncmp(string, "ping", 4))
                        {
@@ -1654,7 +1710,9 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                                                        NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
                                                                        SZ_Clear(&net_message);
                                                                        // now set up the client struct
+                                                                       SV_VM_Begin();
                                                                        SV_ConnectClient(clientnum, conn);
+                                                                       SV_VM_End();
                                                                        NetConn_Heartbeat(1);
                                                                }
                                                                else
@@ -1767,12 +1825,11 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        }
                }
 #endif
-               for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+               if (host_client)
                {
-                       if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
+                       if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length)) == 2)
                        {
-                               if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length)) == 2)
-                                       SV_ReadClientMessage();
+                               SV_ReadClientMessage();
                                return ret;
                        }
                }
index 8584130..cd44ab1 100755 (executable)
--- a/netconn.h
+++ b/netconn.h
@@ -291,6 +291,11 @@ extern sizebuf_t net_message;
 
 extern cvar_t cl_netlocalping;
 
+extern cvar_t cl_netport;
+extern cvar_t sv_netport;
+extern cvar_t net_address;
+//extern cvar_t net_netaddress_ipv6;
+
 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data);
 void NetConn_CloseClientPorts(void);
 void NetConn_OpenClientPorts(void);
@@ -304,6 +309,9 @@ void NetConn_Shutdown(void);
 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress);
 void NetConn_Close(netconn_t *conn);
 void NetConn_Listen(qboolean state);
+int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress);
+int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress);
+int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress);
 int NetConn_IsLocalGame(void);
 void NetConn_ClientFrame(void);
 void NetConn_ServerFrame(void);
index d2fafa0..cddaf7f 100644 (file)
--- a/sv_user.c
+++ b/sv_user.c
@@ -604,10 +604,13 @@ SV_ReadClientMove
 */
 qboolean SV_ReadClientMove (void)
 {
+       qboolean kickplayer = false;
        int i;
        double oldmovetime;
        usercmd_t *move = &host_client->cmd;
 
+       SV_VM_Begin();
+
        oldmovetime = move->time;
 
        // if this move has been applied, clear it, and start accumulating new data
@@ -694,7 +697,7 @@ qboolean SV_ReadClientMove (void)
                // this fixes the timestamp to prevent a speed cheat from working
                move->time = sv.time;
                // but we kick the player for good measure
-               return true;
+               kickplayer = true;
        }
        else
        {
@@ -711,7 +714,8 @@ qboolean SV_ReadClientMove (void)
                        prog->globals.server->frametime = oldframetime;
                }
        }
-       return false;
+       SV_VM_End();
+       return kickplayer;
 }
 
 void SV_ApplyClientMove (void)