mirror of
				https://github.com/Tiiffi/mcrcon.git
				synced 2025-10-31 05:11:08 -04:00 
			
		
		
		
	Compare commits
	
		
			7 Commits
		
	
	
		
			5d1c87b26f
			...
			dba07aacf7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| dba07aacf7 | |||
| bd76b897de | |||
| 1106f27700 | |||
| 00fc3b5bcb | |||
| a0fe9e1645 | |||
| 5f460e8912 | |||
| 6fed74ba74 | 
| @ -1,8 +1,11 @@ | |||||||
| #### Version history: | #### Version history: | ||||||
|  |  | ||||||
| ###### 0.7.3 | ###### 0.8.0 | ||||||
|  |  - Implement support for multipacket responses | ||||||
|  - Add support to Valve style rcon authentication |  - Add support to Valve style rcon authentication | ||||||
|  - Change maximum packet size to correct value (4096 -> 4106) |  - Change maximum packet size to correct value (4096 -> 4106) | ||||||
|  |  - Attempt to add missing newlines in Minecraft servers | ||||||
|  |     * Currently implemented only for the 'help' command | ||||||
|  - Print auth failed message to stderr instead of stdout |  - Print auth failed message to stderr instead of stdout | ||||||
|  - Fail immediately if received packet size is out of spec |  - Fail immediately if received packet size is out of spec | ||||||
|  - Return proper exit code from run_terminal_mode() |  - Return proper exit code from run_terminal_mode() | ||||||
| @ -11,7 +14,7 @@ | |||||||
| ###### 0.7.2 | ###### 0.7.2 | ||||||
|  - Quit gracefully when Ctrl-D or Ctrl+C is pressed |  - Quit gracefully when Ctrl-D or Ctrl+C is pressed | ||||||
|  - Remove "exit" and "quit" as quitting commands |  - Remove "exit" and "quit" as quitting commands | ||||||
|     * these are actual rcon commands on some servers |     * These are actual rcon commands on some servers | ||||||
|  - Suppress compiler warning (strncpy) |  - Suppress compiler warning (strncpy) | ||||||
|  - Fix erroneous string length in packet building function |  - Fix erroneous string length in packet building function | ||||||
|  - Fix typo in ANSI escape sequence for LCYAN |  - Fix typo in ANSI escape sequence for LCYAN | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								mcrcon.1
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								mcrcon.1
									
									
									
									
									
								
							| @ -1,7 +1,7 @@ | |||||||
| .\" Process this file with | .\" Process this file with | ||||||
| .\" groff -man -Tascii mcrcon.1 | .\" groff -man -Tascii mcrcon.1 | ||||||
| .\" | .\" | ||||||
| .TH MCRCON 1 "November 2024" "Version 0.7.3" | .TH MCRCON 1 "December 2024" "Version 0.8.0" | ||||||
| .SH NAME  | .SH NAME  | ||||||
| mcrcon \- send rcon commands to a Minecraft server | mcrcon \- send rcon commands to a Minecraft server | ||||||
| .SH SYNOPSIS | .SH SYNOPSIS | ||||||
|  | |||||||
							
								
								
									
										98
									
								
								mcrcon.c
									
									
									
									
									
								
							
							
						
						
									
										98
									
								
								mcrcon.c
									
									
									
									
									
								
							| @ -41,7 +41,7 @@ | |||||||
|     #include <netdb.h> |     #include <netdb.h> | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #define VERSION "0.7.3" | #define VERSION "0.8.0" | ||||||
| #define IN_NAME "mcrcon" | #define IN_NAME "mcrcon" | ||||||
| #define VER_STR IN_NAME" "VERSION" (built: "__DATE__" "__TIME__")" | #define VER_STR IN_NAME" "VERSION" (built: "__DATE__" "__TIME__")" | ||||||
|  |  | ||||||
| @ -95,7 +95,6 @@ void        packet_print(rc_packet *packet); | |||||||
| bool        rcon_auth(int sock, char *passwd); | bool        rcon_auth(int sock, char *passwd); | ||||||
| int         rcon_command(int sock, char *command); | int         rcon_command(int sock, char *command); | ||||||
|  |  | ||||||
|  |  | ||||||
| // ============================================= | // ============================================= | ||||||
| //  GLOBAL VARIABLES | //  GLOBAL VARIABLES | ||||||
| // ============================================= | // ============================================= | ||||||
| @ -104,6 +103,8 @@ static int flag_silent_mode = 0; | |||||||
| static int  flag_disable_colors = 0; | static int  flag_disable_colors = 0; | ||||||
| static int  flag_wait_seconds = 0; | static int  flag_wait_seconds = 0; | ||||||
| static int  global_connection_alive = 1; | static int  global_connection_alive = 1; | ||||||
|  | static bool global_valve_protocol = false; | ||||||
|  | static bool global_minecraft_newline_fix = false; | ||||||
| static int  global_rsock; | static int  global_rsock; | ||||||
|  |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| @ -166,10 +167,6 @@ int main(int argc, char *argv[]) | |||||||
| 	if (!port) port = "25575"; | 	if (!port) port = "25575"; | ||||||
| 	if (!host) host = "localhost"; | 	if (!host) host = "localhost"; | ||||||
|  |  | ||||||
| 	// disable output buffering (https://github.com/Tiiffi/mcrcon/pull/39) |  | ||||||
| 	setvbuf(stdout, NULL, _IONBF, 0); |  | ||||||
| 	setvbuf(stderr, NULL, _IONBF, 0); |  | ||||||
|  |  | ||||||
| 	if(argc < 1 && pass == NULL) usage(); | 	if(argc < 1 && pass == NULL) usage(); | ||||||
|  |  | ||||||
| 	// default getopt error handler enabled | 	// default getopt error handler enabled | ||||||
| @ -483,9 +480,7 @@ void packet_print(rc_packet *packet) | |||||||
| 	uint8_t *data = packet->data; | 	uint8_t *data = packet->data; | ||||||
|  |  | ||||||
| 	if (flag_raw_output == 1) { | 	if (flag_raw_output == 1) { | ||||||
| 		for (int i = 0; data[i] != 0; ++i) { | 		fputs((char *) data, stdout); | ||||||
| 			putchar(data[i]); |  | ||||||
| 		} |  | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -499,34 +494,38 @@ void packet_print(rc_packet *packet) | |||||||
| 		} else default_color = 0x37; | 		} else default_color = 0x37; | ||||||
| 	#endif | 	#endif | ||||||
|  |  | ||||||
| 	// colors enabled so try to handle the bukkit colors for terminal | 	bool slash = false; | ||||||
| 	if (flag_disable_colors == 0) { | 	bool colors_detected = false; | ||||||
| 		for (i = 0; data[i] != 0; ++i) { |  | ||||||
| 			if (data[i] == 0x0A) print_color(default_color); | 	for (i = 0; data[i] != 0; ++i) | ||||||
|  | 	{ | ||||||
|  | 		if (data[i] == 0x0A) { | ||||||
|  | 			print_color(default_color); | ||||||
|  | 		} | ||||||
| 		else if(data[i] == 0xc2 && data[i + 1] == 0xa7) { | 		else if(data[i] == 0xc2 && data[i + 1] == 0xa7) { | ||||||
|  | 			// Disable new line fixes if Bukkit colors are detected | ||||||
|  | 			colors_detected = true; | ||||||
| 			i += 2; | 			i += 2; | ||||||
|  | 			if (flag_disable_colors == 0) { | ||||||
| 				print_color(data[i]); | 				print_color(data[i]); | ||||||
|  | 			} | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		if (colors_detected == false && global_minecraft_newline_fix && data[i] == '/') { | ||||||
|  | 			slash ? putchar('\n') : (slash = true); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		putchar(data[i]); | 		putchar(data[i]); | ||||||
| 	} | 	} | ||||||
| 	print_color(default_color); // cancel coloring | 	print_color(default_color); // cancel coloring | ||||||
| 	} |  | ||||||
| 	// strip colors |  | ||||||
| 	else { |  | ||||||
| 		for (i = 0; data[i] != 0; ++i) { |  | ||||||
| 			if (data[i] == 0xc2 && data[i + 1] == 0xa7) { |  | ||||||
| 				i += 2; |  | ||||||
| 				continue; |  | ||||||
| 			}	 |  | ||||||
| 			putchar(data[i]); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// print newline if string has no newline | 	// print newline if string has no newline | ||||||
| 	if (data[i - 1] != 10 && data[i - 1] != 13) { | 	if (data[i - 1] != '\n') { | ||||||
| 		putchar('\n'); | 		putchar('\n'); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	fflush(stdout); | ||||||
| } | } | ||||||
|  |  | ||||||
| rc_packet *packet_build(int id, int cmd, char s[static 1]) | rc_packet *packet_build(int id, int cmd, char s[static 1]) | ||||||
| @ -573,6 +572,7 @@ receive: | |||||||
| 	 * so we have to check packet type and try again if necessary. | 	 * so we have to check packet type and try again if necessary. | ||||||
| 	 */ | 	 */ | ||||||
| 	if (packet->cmd != RCON_AUTH_RESPONSE) { | 	if (packet->cmd != RCON_AUTH_RESPONSE) { | ||||||
|  | 		global_valve_protocol = true; | ||||||
| 		goto receive; | 		goto receive; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -580,9 +580,11 @@ receive: | |||||||
| 	return packet->id == -1 ? false : true; | 	return packet->id == -1 ? false : true; | ||||||
| } | } | ||||||
|  |  | ||||||
| // TODO: Add proper error handling and reporting! |  | ||||||
| int rcon_command(int sock, char *command) | int rcon_command(int sock, char *command) | ||||||
| { | { | ||||||
|  | 	if (global_valve_protocol == false && strcasecmp(command, "help") == 0) | ||||||
|  | 		global_minecraft_newline_fix = true; | ||||||
|  |  | ||||||
| 	rc_packet *packet = packet_build(RCON_PID, RCON_EXEC_COMMAND, command); | 	rc_packet *packet = packet_build(RCON_PID, RCON_EXEC_COMMAND, command); | ||||||
| 	if (packet == NULL) { | 	if (packet == NULL) { | ||||||
| 		log_error("Error: packet build() failed!\n"); | 		log_error("Error: packet build() failed!\n"); | ||||||
| @ -594,22 +596,61 @@ int rcon_command(int sock, char *command) | |||||||
| 		return 0; | 		return 0; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Workaround to handle valve multipacket responses | ||||||
|  | 	// This one does not require using select() | ||||||
|  | 	packet = packet_build(0xBADA55, 0xBADA55, ""); | ||||||
|  | 	if (packet == NULL) { | ||||||
|  | 		log_error("Error: packet build() failed!\n"); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!net_send_packet(sock, packet)) { | ||||||
|  | 		log_error("Error: net_send_packet() failed!\n"); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// initialize stuff for select() | ||||||
|  | 	fd_set read_fds; | ||||||
|  | 	FD_ZERO(&read_fds); | ||||||
|  | 	FD_SET(sock, &read_fds); | ||||||
|  |  | ||||||
|  | 	// Set 5 second timeout | ||||||
|  | 	struct timeval timeout = {0}; | ||||||
|  | 	timeout.tv_sec = 5; | ||||||
|  | 	timeout.tv_usec = 0; | ||||||
|  |  | ||||||
|  | 	int incoming = 0; | ||||||
|  |  | ||||||
|  | 	do { | ||||||
| 		packet = net_recv_packet(sock); | 		packet = net_recv_packet(sock); | ||||||
| 		if (packet == NULL) { | 		if (packet == NULL) { | ||||||
| 			log_error("Error: net_recv_packet() failed!\n"); | 			log_error("Error: net_recv_packet() failed!\n"); | ||||||
| 			return 0; | 			return 0; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	if (packet->id != RCON_PID) { | 		// Check for packet id and multipacket guard id | ||||||
|  | 		if (packet->id != RCON_PID && packet->id != 0xBADA55) { | ||||||
| 			log_error("Error: invalid packet id!\n"); | 			log_error("Error: invalid packet id!\n"); | ||||||
| 			return 0; | 			return 0; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	if (!flag_silent_mode) { | 		if (packet->id == 0xBADA55) break; | ||||||
|  |  | ||||||
|  | 		if (flag_silent_mode == false) { | ||||||
| 			if (packet->size > 10) | 			if (packet->size > 10) | ||||||
| 			packet_print(packet); | 			packet_print(packet); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		int result = select(sock + 1, &read_fds, NULL, NULL, &timeout); | ||||||
|  | 		if (result == -1) { | ||||||
|  | 			log_error("Error: select() failed!\n"); | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		incoming = (result > 0 && FD_ISSET(sock, &read_fds)); | ||||||
|  | 	} | ||||||
|  | 	while(incoming); | ||||||
|  |  | ||||||
| 	return 1; | 	return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -645,6 +686,7 @@ int run_terminal_mode(int sock) | |||||||
|  |  | ||||||
| 	while (global_connection_alive) { | 	while (global_connection_alive) { | ||||||
| 		putchar('>'); | 		putchar('>'); | ||||||
|  | 		fflush(stdout); | ||||||
|  |  | ||||||
| 		int len = get_line(command, MAX_COMMAND_LENGTH); | 		int len = get_line(command, MAX_COMMAND_LENGTH); | ||||||
| 		if (len < 1) continue;  | 		if (len < 1) continue;  | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	