diff --git a/Makefile b/Makefile index c755128..ab2497d 100644 --- a/Makefile +++ b/Makefile @@ -10,12 +10,12 @@ EXENAME = mcrcon PREFIX ?= /usr/local INSTALL = install -LINKER = +LINKER = -lreadline RM = rm -v -f CC = gcc -CFLAGS = -std=gnu99 -Wall -Wextra -Wpedantic -Os -s -EXTRAFLAGS ?= -fstack-protector-strong +CFLAGS = -std=gnu99 -Wall -Wextra -Wpedantic -Os -s +EXTRAFLAGS ?= -fstack-protector-strong ifeq ($(OS), Windows_NT) LINKER = -lws2_32 diff --git a/mcrcon.c b/mcrcon.c index 3324875..826d5f9 100644 --- a/mcrcon.c +++ b/mcrcon.c @@ -45,6 +45,8 @@ #include #include #include + #include + #include #endif #define VERSION "0.7.1" @@ -76,6 +78,8 @@ typedef struct _rc_packet { // Network related functions #ifdef _WIN32 void net_init_WSA(void); +char *readline( const char *prompt ); // Substitute function for gnu readline +void add_history( const char *p ); // Ditto #endif void net_close(int sd); int net_connect(const char *host, const char *port); @@ -100,6 +104,7 @@ int rcon_auth(int sock, char *passwd); int rcon_command(int sock, char *command); + // ============================================= // GLOBAL VARIABLES // ============================================= @@ -565,7 +570,12 @@ rc_packet *packet_build(int id, int cmd, char *s1) // size + id + cmd + s1 + s2 NULL terminator int s1_len = strlen(s1); - if (s1_len > DATA_BUFFSIZE) { + + /* If s1_len == DATA_BUFSIZE pascket.data will not be null terminated + * This will cause a problem in packet_printf (maybe other places) + * This should probably be s1_len >= DATA_BUFFSIZE and explicitly + * null terminate packet.data to be safe */ + if (s1_len > DATA_BUFFSIZE) { fprintf(stderr, "Warning: Command string too long (%d). Maximum allowed: %d.\n", s1_len, DATA_BUFFSIZE); return NULL; } @@ -573,6 +583,7 @@ rc_packet *packet_build(int id, int cmd, char *s1) packet.size = sizeof(int) * 2 + s1_len + 2; packet.id = id; packet.cmd = cmd; + strncpy(packet.data, s1, DATA_BUFFSIZE); return &packet; @@ -648,22 +659,31 @@ int run_commands(int argc, char *argv[]) int run_terminal_mode(int sock) { int ret = 0; - char command[DATA_BUFFSIZE] = {0x00}; puts("Logged in. Type 'quit' or 'exit' to quit."); + char *command = NULL; while (global_connection_alive) { - putchar('>'); - int len = get_line(command, DATA_BUFFSIZE); + command = readline( "> " ); + if ( command == NULL ) + break; + + /* Note that if len is > DATA_BUFFSIZE this will cause a non fatal + * Error in build_packet */ + size_t len = strlen( command ); + if ( len ) { + add_history( command ); + } + if ((strcasecmp(command, "exit") && strcasecmp(command, "quit")) == 0) break; if(command[0] == 'Q' && command[1] == 0) - break; + break; if(len > 0 && global_connection_alive) - ret += rcon_command(sock, command); + ret += rcon_command(sock, command); /* Special case for "stop" command to prevent server-side bug. * https://bugs.mojang.com/browse/MC-154617 @@ -672,22 +692,43 @@ int run_terminal_mode(int sock) * ensure compatibility with other servers using source RCON. * NOTE: strcasecmp() is POSIX function. */ - if (strcasecmp(command, "stop") == 0) { - break; - } + if (strcasecmp(command, "stop") == 0 ) + break; - command[0] = len = 0; + free(command); + command = NULL; + len = 0; } + if ( command != NULL ) { + free(command); + } + return ret; } + +#ifdef _WIN32 +void add_history( const char *p ) { } + // gets line from stdin and deals with rubbish left in the input buffer -int get_line(char *buffer, int bsize) +char *readline( const char *prompt ) { - char *ret = fgets(buffer, bsize, stdin); - if (ret == NULL) - exit(EXIT_FAILURE); + size_t bsize = DATA_BUFFSIZE; + + fputs(prompt, stdout); + + char *buffer = malloc( bsize ); + if ( buffer == NULL ) { + fprintf( stderr, "Could not allocate memory for read buffer\n" ); + exit(EXIT_FAILURE); + } + + char *ret = fgets(buffer, bsize, stdin); + if (ret == NULL) { + free(buffer); + exit(EXIT_FAILURE); + } if (buffer[0] == 0) global_connection_alive = 0; @@ -695,7 +736,7 @@ int get_line(char *buffer, int bsize) // remove unwanted characters from the buffer buffer[strcspn(buffer, "\r\n")] = '\0'; - int len = strlen(buffer); + size_t len = strlen(buffer); // clean input buffer if needed if (len == bsize - 1) { @@ -703,5 +744,6 @@ int get_line(char *buffer, int bsize) while ((ch = getchar()) != '\n' && ch != EOF); } - return len; + return buffer; } +#endif