From bec702479a73a63e5454bd7aef014a8ab335e030 Mon Sep 17 00:00:00 2001 From: bones_was_here Date: Thu, 8 Feb 2024 02:21:51 +1000 Subject: [PATCH] rcon: rewrite variable expansion support, related fixes Fixes https://gitlab.com/xonotic/darkplaces/-/issues/407 In 48d83538ef13596d4569d46ae66467b63b945546 I broke the return of the rcon command output to the client. Fixes unterminated string warnings on the server when the rcon_password feature of multiple space-delimited passwords is used. Fixes possibility of the cbuf to use more memory than its intended maximum. In the client you can send the variable such that the server will expand it (not the client) like this: rcon echo $$sv_worldmessage Signed-off-by: bones_was_here --- cmd.c | 43 +++++++++++++++++++++---------------------- cmd.h | 2 ++ netconn.c | 9 +++------ 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/cmd.c b/cmd.c index b12b13d6..3e35b336 100644 --- a/cmd.c +++ b/cmd.c @@ -348,13 +348,9 @@ Cbuf_Execute ============ */ extern qbool prvm_runawaycheck; -static size_t Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *outtext, unsigned maxoutlen, cmd_alias_t *alias); void Cbuf_Execute (cmd_buf_t *cbuf) { cmd_input_t *current; - char preprocessed[MAX_INPUTLINE]; - size_t preprocessed_len; - char *firstchar; unsigned int i = 0; // LadyHavoc: making sure the tokenizebuffer doesn't get filled up by repeated crashes @@ -376,28 +372,11 @@ void Cbuf_Execute (cmd_buf_t *cbuf) */ current->pending = false; + Cmd_PreprocessAndExecuteString(current->source, current->text, current->length, src_local, false); cbuf->size -= current->length; - - firstchar = current->text; - while(*firstchar && ISWHITESPACE(*firstchar)) - ++firstchar; - if((strncmp(firstchar, "alias", 5) || !ISWHITESPACE(firstchar[5])) - && (strncmp(firstchar, "bind", 4) || !ISWHITESPACE(firstchar[4])) - && (strncmp(firstchar, "in_bind", 7) || !ISWHITESPACE(firstchar[7]))) - { - if((preprocessed_len = Cmd_PreprocessString(current->source, current->text, preprocessed, sizeof(preprocessed), NULL))) - Cmd_ExecuteString(current->source, preprocessed, preprocessed_len, src_local, false); - } - else - { - Cmd_ExecuteString(current->source, current->text, current->length, src_local, false); - } - // Recycle memory so using WASD doesn't cause a malloc and free List_Move_Tail(¤t->list, &cbuf->free); - current = NULL; - if (cbuf->wait) { /* @@ -1434,6 +1413,26 @@ static void Cmd_ExecuteAlias (cmd_state_t *cmd, cmd_alias_t *alias) Cbuf_InsertText(cmd, buffer2); } +void Cmd_PreprocessAndExecuteString(cmd_state_t *cmd, const char *text, size_t textlen, cmd_source_t src, qbool lockmutex) +{ + char preprocessed[MAX_INPUTLINE]; + size_t preprocessed_len; + const char *firstchar; + + firstchar = text; + while(*firstchar && ISWHITESPACE(*firstchar)) + ++firstchar; + if((strncmp(firstchar, "alias", 5) || !ISWHITESPACE(firstchar[5])) + && (strncmp(firstchar, "bind", 4) || !ISWHITESPACE(firstchar[4])) + && (strncmp(firstchar, "in_bind", 7) || !ISWHITESPACE(firstchar[7]))) + { + if((preprocessed_len = Cmd_PreprocessString(cmd, text, preprocessed, sizeof(preprocessed), NULL))) + Cmd_ExecuteString(cmd, preprocessed, preprocessed_len, src, lockmutex); + } + else + Cmd_ExecuteString(cmd, text, textlen, src, lockmutex); +} + /* ======== Cmd_List diff --git a/cmd.h b/cmd.h index 97925042..a94ddb16 100644 --- a/cmd.h +++ b/cmd.h @@ -268,6 +268,8 @@ int Cmd_CheckParm (cmd_state_t *cmd, const char *parm); /// Parses a single line of text into arguments and tries to execute it. /// The text can come from the command buffer, a remote client, or stdin. void Cmd_ExecuteString(cmd_state_t *cmd, const char *text, size_t textlen, cmd_source_t src, qbool lockmutex); +/// Like Cmd_ExecuteString, but with variable expansion. +void Cmd_PreprocessAndExecuteString(cmd_state_t *cmd, const char *text, size_t textlen, cmd_source_t src, qbool lockmutex); /// quotes a string so that it can be used as a command argument again; /// quoteset is a string that contains one or more of ", \, $ and specifies diff --git a/netconn.c b/netconn.c index 801e6fc8..b4dc5d50 100644 --- a/netconn.c +++ b/netconn.c @@ -3013,7 +3013,7 @@ static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *pa while((userpass_end = strchr(userpass_start, ' '))) { have_usernames = true; - dp_strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1)); + dp_ustr2stp(buf, sizeof(buf), userpass_start, userpass_end - userpass_start); if(buf[0]) // Ignore empty entries due to leading/duplicate space. if(comparator(peeraddress, buf, password, cs, cslen)) goto allow; @@ -3032,7 +3032,7 @@ static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *pa while((userpass_end = strchr(userpass_start, ' '))) { have_usernames = true; - dp_strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1)); + dp_ustr2stp(buf, sizeof(buf), userpass_start, userpass_end - userpass_start); if(buf[0]) // Ignore empty entries due to leading/duplicate space. if(comparator(peeraddress, buf, password, cs, cslen)) goto check; @@ -3122,10 +3122,7 @@ static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, c if(l) { client_t *host_client_save = host_client; - //Cmd_ExecuteString(cmd_local, s, src_local, true); // no variable expansion - // bones_was_here: prepending allows a loop such as `alias foo "bar; wait; foo"; foo` - // to be broken with an alias or unalias command - Cbuf_InsertText(cmd_local, s); + Cmd_PreprocessAndExecuteString(cmd_local, s, l, src_local, true); host_client = host_client_save; // in case it is a command that changes host_client (like restart) } -- 2.39.2