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,17 +1,20 @@ | ||||
| #### Version history: | ||||
|  | ||||
| ###### 0.7.3 | ||||
| ###### 0.8.0 | ||||
|  - Implement support for multipacket responses | ||||
|  - Add support to Valve style rcon authentication | ||||
|  - 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 | ||||
|  - Fail immediately if received packet size is out of spec | ||||
|  - Return proper exit code from run_terminal_mode() | ||||
|  - Add error messages to rcon_command() function | ||||
|   | ||||
|  | ||||
| ###### 0.7.2 | ||||
|  - Quit gracefully when Ctrl-D or Ctrl+C is pressed | ||||
|  - 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) | ||||
|  - Fix erroneous string length in packet building function | ||||
|  - Fix typo in ANSI escape sequence for LCYAN | ||||
|  | ||||
							
								
								
									
										2
									
								
								mcrcon.1
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								mcrcon.1
									
									
									
									
									
								
							| @ -1,7 +1,7 @@ | ||||
| .\" Process this file with | ||||
| .\" 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  | ||||
| mcrcon \- send rcon commands to a Minecraft server | ||||
| .SH SYNOPSIS | ||||
|  | ||||
							
								
								
									
										126
									
								
								mcrcon.c
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								mcrcon.c
									
									
									
									
									
								
							| @ -41,7 +41,7 @@ | ||||
|     #include <netdb.h> | ||||
| #endif | ||||
|  | ||||
| #define VERSION "0.7.3" | ||||
| #define VERSION "0.8.0" | ||||
| #define IN_NAME "mcrcon" | ||||
| #define VER_STR IN_NAME" "VERSION" (built: "__DATE__" "__TIME__")" | ||||
|  | ||||
| @ -95,16 +95,17 @@ void        packet_print(rc_packet *packet); | ||||
| bool        rcon_auth(int sock, char *passwd); | ||||
| int         rcon_command(int sock, char *command); | ||||
|  | ||||
|  | ||||
| // ============================================= | ||||
| //  GLOBAL VARIABLES | ||||
| // ============================================= | ||||
| static int flag_raw_output = 0; | ||||
| static int flag_silent_mode = 0; | ||||
| static int flag_disable_colors = 0; | ||||
| static int flag_wait_seconds = 0; | ||||
| static int global_connection_alive = 1; | ||||
| static int global_rsock; | ||||
| static int  flag_raw_output = 0; | ||||
| static int  flag_silent_mode = 0; | ||||
| static int  flag_disable_colors = 0; | ||||
| static int  flag_wait_seconds = 0; | ||||
| static int  global_connection_alive = 1; | ||||
| static bool global_valve_protocol = false; | ||||
| static bool global_minecraft_newline_fix = false; | ||||
| static int  global_rsock; | ||||
|  | ||||
| #ifdef _WIN32 | ||||
|   // console coloring on windows | ||||
| @ -166,10 +167,6 @@ int main(int argc, char *argv[]) | ||||
| 	if (!port) port = "25575"; | ||||
| 	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(); | ||||
|  | ||||
| 	// default getopt error handler enabled | ||||
| @ -483,9 +480,7 @@ void packet_print(rc_packet *packet) | ||||
| 	uint8_t *data = packet->data; | ||||
|  | ||||
| 	if (flag_raw_output == 1) { | ||||
| 		for (int i = 0; data[i] != 0; ++i) { | ||||
| 			putchar(data[i]); | ||||
| 		} | ||||
| 		fputs((char *) data, stdout); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| @ -499,34 +494,38 @@ void packet_print(rc_packet *packet) | ||||
| 		} else default_color = 0x37; | ||||
| 	#endif | ||||
|  | ||||
| 	// colors enabled so try to handle the bukkit colors for terminal | ||||
| 	if (flag_disable_colors == 0) { | ||||
| 		for (i = 0; data[i] != 0; ++i) { | ||||
| 			if (data[i] == 0x0A) print_color(default_color); | ||||
| 			else if(data[i] == 0xc2 && data[i + 1] == 0xa7) { | ||||
| 				i += 2; | ||||
| 	bool slash = false; | ||||
| 	bool colors_detected = false; | ||||
|  | ||||
| 	for (i = 0; data[i] != 0; ++i) | ||||
| 	{ | ||||
| 		if (data[i] == 0x0A) { | ||||
| 			print_color(default_color); | ||||
| 		} | ||||
| 		else if(data[i] == 0xc2 && data[i + 1] == 0xa7) { | ||||
| 			// Disable new line fixes if Bukkit colors are detected | ||||
| 			colors_detected = true; | ||||
| 			i += 2; | ||||
| 			if (flag_disable_colors == 0) { | ||||
| 				print_color(data[i]); | ||||
| 				continue; | ||||
| 			} | ||||
| 			putchar(data[i]); | ||||
| 			continue; | ||||
| 		} | ||||
| 		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]); | ||||
|  | ||||
| 		if (colors_detected == false && global_minecraft_newline_fix && data[i] == '/') { | ||||
| 			slash ? putchar('\n') : (slash = true); | ||||
| 		} | ||||
|  | ||||
| 		putchar(data[i]); | ||||
| 	} | ||||
| 	print_color(default_color); // cancel coloring | ||||
|  | ||||
| 	// print newline if string has no newline | ||||
| 	if (data[i - 1] != 10 && data[i - 1] != 13) { | ||||
| 	if (data[i - 1] != '\n') { | ||||
| 		putchar('\n'); | ||||
| 	} | ||||
|  | ||||
| 	fflush(stdout); | ||||
| } | ||||
|  | ||||
| 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. | ||||
| 	 */ | ||||
| 	if (packet->cmd != RCON_AUTH_RESPONSE) { | ||||
| 		global_valve_protocol = true; | ||||
| 		goto receive; | ||||
| 	} | ||||
|  | ||||
| @ -580,9 +580,11 @@ receive: | ||||
| 	return packet->id == -1 ? false : true; | ||||
| } | ||||
|  | ||||
| // TODO: Add proper error handling and reporting! | ||||
| 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); | ||||
| 	if (packet == NULL) { | ||||
| 		log_error("Error: packet build() failed!\n"); | ||||
| @ -594,21 +596,60 @@ int rcon_command(int sock, char *command) | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	packet = net_recv_packet(sock); | ||||
| 	// Workaround to handle valve multipacket responses | ||||
| 	// This one does not require using select() | ||||
| 	packet = packet_build(0xBADA55, 0xBADA55, ""); | ||||
| 	if (packet == NULL) { | ||||
| 		log_error("Error: net_recv_packet() failed!\n"); | ||||
| 		log_error("Error: packet build() failed!\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (packet->id != RCON_PID) { | ||||
| 		log_error("Error: invalid packet id!\n"); | ||||
| 	if (!net_send_packet(sock, packet)) { | ||||
| 		log_error("Error: net_send_packet() failed!\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!flag_silent_mode) { | ||||
| 		if (packet->size > 10) | ||||
| 		packet_print(packet); | ||||
| 	// 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); | ||||
| 		if (packet == NULL) { | ||||
| 			log_error("Error: net_recv_packet() failed!\n"); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		// Check for packet id and multipacket guard id | ||||
| 		if (packet->id != RCON_PID && packet->id != 0xBADA55) { | ||||
| 			log_error("Error: invalid packet id!\n"); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		if (packet->id == 0xBADA55) break; | ||||
|  | ||||
| 		if (flag_silent_mode == false) { | ||||
| 			if (packet->size > 10) | ||||
| 			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; | ||||
| } | ||||
| @ -645,6 +686,7 @@ int run_terminal_mode(int sock) | ||||
|  | ||||
| 	while (global_connection_alive) { | ||||
| 		putchar('>'); | ||||
| 		fflush(stdout); | ||||
|  | ||||
| 		int len = get_line(command, MAX_COMMAND_LENGTH); | ||||
| 		if (len < 1) continue;  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	