From 48de6944923517009c98026ee5ea5fdc01de4511 Mon Sep 17 00:00:00 2001 From: divverent Date: Fri, 2 Oct 2009 10:25:17 +0000 Subject: [PATCH] try making srcon work better with many rcon comamnds queued git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@9282 d7cf8633-e32d-0410-b094-e92efae38249 --- client.h | 3 ++- host_cmd.c | 21 ++++++++++++++++++--- netconn.c | 44 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/client.h b/client.h index acab743a..38b6f268 100644 --- a/client.h +++ b/client.h @@ -581,10 +581,11 @@ typedef struct client_static_s // (kept outside client_state_t because it's used between levels) protocolversion_t protocol; -#define MAX_RCONS 64 +#define MAX_RCONS 16 int rcon_trying; lhnetaddress_t rcon_addresses[MAX_RCONS]; char rcon_commands[MAX_RCONS][MAX_INPUTLINE]; + double rcon_timeout[MAX_RCONS]; int rcon_ringpos; // connection information diff --git a/host_cmd.c b/host_cmd.c index 895bb2d2..5b45a09d 100644 --- a/host_cmd.c +++ b/host_cmd.c @@ -34,6 +34,7 @@ cvar_t sv_status_privacy = {CVAR_SAVE, "sv_status_privacy", "0", "do not show IP 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; 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 (1 = time based, 2 = challenge based); NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password"}; +cvar_t rcon_secure_challengetimeout = {0, "rcon_secure_challengetimeout", "5", "challenge-based secure rcon: time out requests if no challenge came within this time interval"}; 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)"}; @@ -2441,12 +2442,25 @@ void Host_Rcon_f (void) // credit: taken from QuakeWorld // simply put together the rcon packet and send it if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1) { - if(!cls.rcon_commands[cls.rcon_ringpos][0]) - ++cls.rcon_trying; + if(cls.rcon_commands[cls.rcon_ringpos][0]) + { + char s[128]; + LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true); + Con_Printf("rcon to %s (for command %s) failed: too many buffered commands (possibly increase MAX_RCONS)\n", s, cls.rcon_commands[cls.rcon_ringpos]); + cls.rcon_commands[cls.rcon_ringpos][0] = 0; + --cls.rcon_trying; + } + for (i = 0;i < MAX_RCONS;i++) + if(cls.rcon_commands[i][0]) + if (!LHNETADDRESS_Compare(&to, &cls.rcon_addresses[i])) + break; + ++cls.rcon_trying; + if(i >= MAX_RCONS) + NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // otherwise we'll request the challenge later strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos])); cls.rcon_addresses[cls.rcon_ringpos] = to; cls.rcon_ringpos = (cls.rcon_ringpos) % 64; - NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); + cls.rcon_timeout[i] = realtime + rcon_secure_challengetimeout.value; } else if(rcon_secure.integer) { @@ -2843,6 +2857,7 @@ void Host_InitCommands (void) Cvar_RegisterVariable (&rcon_password); Cvar_RegisterVariable (&rcon_address); Cvar_RegisterVariable (&rcon_secure); + Cvar_RegisterVariable (&rcon_secure_challengetimeout); 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); note: if rcon_secure is set, client and server clocks must be synced e.g. via NTP"); 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; note: client and server clocks must be synced e.g. via NTP"); Cmd_AddCommand ("pqrcon", Host_PQRcon_f, "sends a command to a proquake 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)"); diff --git a/netconn.c b/netconn.c index 1dfb0cc0..a66e2f32 100755 --- a/netconn.c +++ b/netconn.c @@ -88,6 +88,7 @@ static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_passwor 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; +extern cvar_t rcon_secure_challengetimeout; /* statistic counters */ static int packetsSent = 0; @@ -1022,6 +1023,8 @@ static int clientport2 = -1; static int hostport = -1; void NetConn_UpdateSockets(void) { + int i, j; + if (cls.state != ca_dedicated) { if (clientport2 != cl_netport.integer) @@ -1045,6 +1048,23 @@ void NetConn_UpdateSockets(void) if (sv.active) Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n"); } + + for (j = 0;j < MAX_RCONS;j++) + { + i = (cls.rcon_ringpos + j + 1) % MAX_RCONS; + if(cls.rcon_commands[i][0]) + { + if(realtime > cls.rcon_timeout[i]) + { + char s[128]; + LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true); + Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]); + cls.rcon_commands[i][0] = 0; + --cls.rcon_trying; + break; + } + } + } } static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int length, protocolversion_t protocol, double newtimeout) @@ -1556,12 +1576,15 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat if (length > 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying) { - int i; - for (i = 0;i < MAX_RCONS;i++) + int i, j; + for (j = 0;j < MAX_RCONS;j++) + { + i = (cls.rcon_ringpos + j + 1) % MAX_RCONS; if(cls.rcon_commands[i][0]) if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i])) break; - if (i < MAX_RCONS) + } + if (j < MAX_RCONS) { char buf[1500]; char argbuf[1500]; @@ -1574,6 +1597,21 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat NetConn_Write(mysocket, buf, 46 + strlen(buf + 46), peeraddress); cls.rcon_commands[i][0] = 0; --cls.rcon_trying; + + for (i = 0;i < MAX_RCONS;i++) + if(cls.rcon_commands[i][0]) + if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i])) + break; + if(i < MAX_RCONS) + { + NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress); + // extend the timeout on other requests as we asked for a challenge + for (i = 0;i < MAX_RCONS;i++) + if(cls.rcon_commands[i][0]) + if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i])) + cls.rcon_timeout[i] = realtime + rcon_secure_challengetimeout.value; + } + return true; // we used up the challenge, so we can't use this oen for connecting now anyway } } -- 2.39.2