14 Commits

Author SHA1 Message Date
427fd206ca Oops, uncomment struct field 2024-11-11 23:49:18 +02:00
1a4010cbba Change MAX_PACKET_SIZE and DATA_BUFFSIZE, add notes about packet structure 2024-11-11 22:22:45 +02:00
4488127350 Use fixed width integer types in rcon packet structure 2024-11-10 16:25:19 +02:00
6b563df23d Update README.md 2024-11-09 20:53:55 +02:00
5a2dcf41ac Update README.md 2024-11-09 20:51:36 +02:00
7d3f3c1d61 Update README.md 2024-11-09 20:46:53 +02:00
fc040ce5ea Change maximum packet size to correct value (4096 -> 4106) 2024-11-09 15:22:42 +02:00
489306d4a2 Add windows batch scripts 2024-11-09 13:48:23 +02:00
a8e2a9349e Exit with appropriate return code if password is not provided, fixes #87 2024-11-07 21:39:37 +02:00
deed43ad61 Makefile macOS fix, resolves #82, resolves #102
- Remove "ginstall" as "install" replacement on macOS
- Remove "-D" flag from "install" parameters
2024-11-07 17:14:25 +02:00
aa933d2c1f Add support for Valve style authentication, fixes #106 2024-11-07 14:59:31 +02:00
accae57e4b Remove ".travis.yml" 2024-11-07 14:21:31 +02:00
fa25cde79c Modify compilation flags:
- Remove "-s" flag
- Change stack protector mode from "strong" to "all"
- Change optimization level from "-Os" to "-O2"
2024-11-07 14:06:48 +02:00
e96b2eff6e Remove broken Travis CI links and update package list 2024-11-07 13:54:40 +02:00
10 changed files with 173 additions and 77 deletions

View File

@ -1,20 +0,0 @@
language: c
sudo: false
dist: trusty
cache:
ccache: true
compiler:
- gcc
- clang
env:
global:
- EXTRAFLAGS='-v'
- PREFIX="${HOME}"
install:
- 'true'
script:
- make
- ./mcrcon -h
- ./mcrcon -v
- make install
- make uninstall

View File

@ -1,11 +1,15 @@
#### Version history: #### Version history:
###### 0.7.3
- Add support to Valve style rcon authentication
- Change maximum packet size to correct value (4096 -> 4106)
###### 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
- Make stdout and stderr unbuffered - Make stdout and stderr unbuffered

View File

@ -1,11 +1,11 @@
Building and installing Building and installing
------------------------ -----------------------
Only dependency is C library and POSIX getopt support. Only dependency is C library and POSIX getopt support.
Compiling with GCC or CLANG: Compiling with GCC or CLANG:
cc -std=gnu99 -Wpedantic -Wall -Wextra -Os -s -o mcrcon mcrcon.c cc -std=gnu99 -Wpedantic -Wall -Wextra -O2 -o mcrcon mcrcon.c
Note: on Windows remember to link with winsock by adding `-lws2_32` to your compiler command line. Note: on Windows remember to link with winsock by adding `-lws2_32` to your compiler command line.

View File

@ -1,4 +1,4 @@
Copyright (c) 2012-2021, Tiiffi <tiiffi at gmail> Copyright (c) 2012-2024, Tiiffi <tiiffi at gmail>
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -18,4 +18,3 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source 3. This notice may not be removed or altered from any source
distribution. distribution.

View File

@ -4,7 +4,7 @@
# make # make
# #
# Windows cross compile: # Windows cross compile:
# i686-w64-mingw32-gcc -std=gnu99 -Wall -Wextra -Wpedantic -Os -s -o mcrcon.exe mcrcon.c -lws2_32 # x86_64-w64-mingw32-gcc -std=gnu99 -Wall -Wextra -Wpedantic -O2 -fstack-protector-all -o mcrcon.exe mcrcon.c -lws2_32
EXENAME = mcrcon EXENAME = mcrcon
PREFIX ?= /usr/local PREFIX ?= /usr/local
@ -14,8 +14,8 @@ LINKER =
RM = rm -v -f RM = rm -v -f
CC = gcc CC = gcc
CFLAGS = -std=gnu99 -Wall -Wextra -Wpedantic -Os -s CFLAGS = -std=gnu99 -Wall -Wextra -Wpedantic -O2
EXTRAFLAGS ?= -fstack-protector-strong EXTRAFLAGS ?= -fstack-protector-all
ifeq ($(OS), Windows_NT) ifeq ($(OS), Windows_NT)
LINKER = -lws2_32 LINKER = -lws2_32
@ -23,11 +23,6 @@ ifeq ($(OS), Windows_NT)
RM = cmd /C del /F RM = cmd /C del /F
endif endif
ifeq ($(shell uname), Darwin)
INSTALL = ginstall
CFLAGS = -std=gnu99 -Wall -Wextra -Wpedantic -Os
endif
.PHONY: all .PHONY: all
all: $(EXENAME) all: $(EXENAME)
@ -37,8 +32,8 @@ $(EXENAME): mcrcon.c
ifneq ($(OS), Windows_NT) ifneq ($(OS), Windows_NT)
.PHONY: install .PHONY: install
install: install:
$(INSTALL) -vD $(EXENAME) $(DESTDIR)$(PREFIX)/bin/$(EXENAME) $(INSTALL) -v $(EXENAME) $(DESTDIR)$(PREFIX)/bin/$(EXENAME)
$(INSTALL) -vD -m 0644 mcrcon.1 $(DESTDIR)$(PREFIX)/share/man/man1/mcrcon.1 $(INSTALL) -v -m 0644 mcrcon.1 $(DESTDIR)$(PREFIX)/share/man/man1/mcrcon.1
@echo "\nmcrcon installed. Run 'make uninstall' if you want to uninstall.\n" @echo "\nmcrcon installed. Run 'make uninstall' if you want to uninstall.\n"
.PHONY: uninstall .PHONY: uninstall

View File

@ -4,30 +4,36 @@ mcrcon is console based Minecraft [rcon](https://developer.valvesoftware.com/wik
--- ---
### Installing: ### Installing
##### via packet manager: ##### Binary releases
See https://pkgs.org/download/mcrcon for available packages in various Linux distros (note that available packages might be outdated).
- Gentoo Linux: https://packages.gentoo.org/packages/games-util/mcrcon Pre-built binaries are provided for Linux and Windows: https://github.com/Tiiffi/mcrcon/releases/latest
- Arch Linux: https://aur.archlinux.org/packages/mcrcon/
##### building from sources: ##### Via package manager
See https://pkgs.org/download/mcrcon and https://repology.org/project/mcrcon/packages for available packages in various Linux distros (note that some packages might be outdated).
- Fedora: https://packages.fedoraproject.org/pkgs/mcrcon/mcrcon/
- Gentoo: https://packages.gentoo.org/packages/games-util/mcrcon
- Arch: https://aur.archlinux.org/packages/mcrcon/
- NixOS: https://search.nixos.org/packages?show=mcrcon
- Snapcraft: https://snapcraft.io/mcrcon-nsg
- Scoop: https://scoop.sh/#/apps?q=mcrcon
##### Building from sources
```sh ```sh
git clone https://github.com/Tiiffi/mcrcon.git git clone https://github.com/Tiiffi/mcrcon.git
cd mcrcon cd mcrcon
make make
# install is optional
sudo make install sudo make install
``` ```
Check [INSTALL.md](INSTALL.md) for more details. Check [INSTALL.md](INSTALL.md) for more details.
Precompiled binaries (if provided)*: https://github.com/Tiiffi/mcrcon/releases/latest
<sub>*At the moment binaries are provided for Linux and Windows.</sub>
--- ---
### Usage: ### Usage
mcrcon [OPTIONS] [COMMANDS] mcrcon [OPTIONS] [COMMANDS]
Sends rcon commands to Minecraft server. Sends rcon commands to Minecraft server.
@ -56,15 +62,18 @@ MCRCON_PASS
- Command-line options will override environment variables - Command-line options will override environment variables
- Rcon commands with spaces must be enclosed in quotes - Rcon commands with spaces must be enclosed in quotes
Example: ###### Example:
> Send three commands ("say", "save-all", "stop") and wait five seconds between the commands. Send three commands ("say", "save-all", "stop") and wait five seconds between the commands:
```mcrcon -H my.minecraft.server -p password -w 5 "say Server is restarting!" save-all stop``` ```sh
mcrcon -H my.minecraft.server -p password -w 5 "say Server is restarting!" save-all stop
```
--- ---
##### Enable rcon on server ##### How to enable rcon on a Minecraft Server
Remember to enable rcon by adding following lines to [```server.properties```](https://minecraft.gamepedia.com/Server.properties) file.
Enable rcon by adding following lines to [```server.properties```](https://minecraft.gamepedia.com/Server.properties) configuration file.
``` ```
enable-rcon=true enable-rcon=true
rcon.port=25575 rcon.port=25575
@ -73,19 +82,25 @@ rcon.password=your_rcon_pasword
--- ---
##### Contact: ### Contact
* WWW: https://github.com/Tiiffi/mcrcon/ * WWW: https://github.com/Tiiffi/mcrcon/
* MAIL: tiiffi+mcrcon at gmail * MAIL: tiiffi+mcrcon at gmail
* BUG REPORTS: https://github.com/Tiiffi/mcrcon/issues/ * ISSUES: https://github.com/Tiiffi/mcrcon/issues/
When reporting issues, please provide the following information:
- Version of mcrcon: Please specify the precise version number
- Game: Indicate the specific game server you're using (e.g., Minecraft, Valve Source Engine game, ARK, ...)
- Server version: Provide the exact version of the game server
- Mods and Extensions: List all mods and extensions used, including their versions
- Issue Description: Clearly describe the problem you're encountering and the expected behavior.
- Steps to reproduce
If you're tech-savvy, consider providing a packet capture file (PCAP). Remember to use a fake password.
--- ---
### License ### License
This project is licensed under the zlib License - see the [LICENSE](LICENSE) file for details. This project is licensed under the zlib License - see the [LICENSE](LICENSE) file for details.
---
<sub>Master:</sub> ![Master build](https://api.travis-ci.org/Tiiffi/mcrcon.svg?branch=master)
<sub>Develop:</sub> ![Develop build](https://api.travis-ci.org/Tiiffi/mcrcon.svg?branch=develop)

31
create_shortcut.bat Normal file
View File

@ -0,0 +1,31 @@
@echo off
@cls
@set /p host="Enter host (default: "127.0.0.1"): "
@if "%host%"=="" set host=127.0.0.1
@set /p port="Enter port (default: 25575): "
@if "%port%"=="" set port=25575
@set /p passwd="Enter password: "
@if "%passwd%"=="" set passwd=
set name=connect_%host%-%port%
@set /p name="Enter shortcut name (default: "%name%.bat"): "
@if "%name%"=="" set name=connect_%host%-%port%
set command=@mcrcon.exe -t -H %host% -P %port% -p %passwd%
@echo %command% >> %name%.bat
@echo.
@echo Command: "%command%"
@echo.
@echo Shortcut "%name%.bat" created!
@echo.
@set "host="
@set "port="
@set "passwd="
@pause

29
launch.bat Normal file
View File

@ -0,0 +1,29 @@
@echo off
@cls
@if not exist mcrcon.exe (
@echo ERROR: Cannot find "mcrcon.exe". Bailing out!
@echo.
@pause
@exit
)
@set /p host="Enter host (default: 127.0.0.1): "
@if "%host%"=="" set host=127.0.0.1
@set /p port="Enter port (default: 25575): "
@if "%port%"=="" set port=25575
@set /p passwd="Enter password: "
@if "%passwd%"=="" set passwd=
@echo.
mcrcon.exe -t -H %host% -P %port% -p %passwd%
@echo.
@set "host="
@set "port="
@set "passwd="
@pause

View File

@ -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 "October 2021" "Version 0.7.2" .TH MCRCON 1 "November 2024" "Version 0.7.3"
.SH NAME .SH NAME
mcrcon \- send rcon commands to a Minecraft server mcrcon \- send rcon commands to a Minecraft server
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -47,7 +47,7 @@
#include <netdb.h> #include <netdb.h>
#endif #endif
#define VERSION "0.7.2" #define VERSION "0.7.3"
#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__")"
@ -57,17 +57,46 @@
#define RCON_AUTH_RESPONSE 2 #define RCON_AUTH_RESPONSE 2
#define RCON_PID 0xBADC0DE #define RCON_PID 0xBADC0DE
#define DATA_BUFFSIZE 4096 /* NOTE: This is confusing. What is the real max packet size?
* Are null bytes included? Are both null bytes included?
* Perhaps payload end indicator is uin16_t with zero value?
*/
#define DATA_BUFFSIZE 4098 // 2 bytes extra over 4096
#define MAX_PACKET_SIZE 4106
#define MIN_PACKET_SIZE 10
// rcon packet structure // rcon packet structure,
// NOTE(Tiiffi): Alignment problem!
typedef struct _rc_packet { typedef struct _rc_packet {
int size; int32_t size;
int id; int32_t id;
int cmd; int32_t cmd;
char data[DATA_BUFFSIZE]; char data[DATA_BUFFSIZE];
// ignoring string2 for now // ignoring string2 for now
} rc_packet; } rc_packet;
// __attribute__((packed))
/* TODO(Tiiffi):
*
* Correct packet structure is propably something like this:
*
* +---------------------------+
* | Size (4 bytes, int) | Total packet size (excluding this field)
* +---------------------------+
* | ID (4 bytes, int) |
* +---------------------------+
* | Type (4 bytes, int) |
* +---------------------------+
* | Payload (variable length) | Command or response string (up to 4096 bytes)
* | (null-terminated string) |
* +---------------------------+
* | Null Terminator (2 bytes, | 16-bit integer set to zero (0x0000)
* | 16-bit int) | Could be also interpreted as two null bytes
* +---------------------------+
*
* Maximum size 4110 including size field, 4106 excluding size field.
* Take care with the aligment!
*/
// =================================== // ===================================
// FUNCTION DEFINITIONS // FUNCTION DEFINITIONS
@ -209,7 +238,7 @@ int main(int argc, char *argv[])
if (pass == NULL) { if (pass == NULL) {
puts("You must give password (-p password).\nTry 'mcrcon -h' or 'man mcrcon' for help."); puts("You must give password (-p password).\nTry 'mcrcon -h' or 'man mcrcon' for help.");
return 0; exit(EXIT_FAILURE);
} }
if(optind == argc && terminal_mode == 0) if(optind == argc && terminal_mode == 0)
@ -400,7 +429,7 @@ int net_send_packet(int sd, rc_packet *packet)
int bytesleft; // bytes left to send int bytesleft; // bytes left to send
int ret = -1; int ret = -1;
bytesleft = len = packet->size + sizeof(int); bytesleft = len = packet->size + sizeof(int32_t);
while (total < len) { while (total < len) {
ret = send(sd, (char *) packet + total, bytesleft, 0); ret = send(sd, (char *) packet + total, bytesleft, 0);
@ -414,12 +443,12 @@ int net_send_packet(int sd, rc_packet *packet)
rc_packet *net_recv_packet(int sd) rc_packet *net_recv_packet(int sd)
{ {
int psize; int32_t psize;
static rc_packet packet = {0, 0, 0, { 0x00 }}; static rc_packet packet = {0, 0, 0, { 0x00 }};
// packet.size = packet.id = packet.cmd = 0; // packet.size = packet.id = packet.cmd = 0;
int ret = recv(sd, (char *) &psize, sizeof(int), 0); int ret = recv(sd, (char *) &psize, sizeof(psize), 0);
if (ret == 0) { if (ret == 0) {
fprintf(stderr, "Connection lost.\n"); fprintf(stderr, "Connection lost.\n");
@ -427,17 +456,18 @@ rc_packet *net_recv_packet(int sd)
return NULL; return NULL;
} }
if (ret != sizeof(int)) { if (ret != sizeof(psize)) {
fprintf(stderr, "Error: recv() failed. Invalid packet size (%d).\n", ret); fprintf(stderr, "Error: recv() failed. Invalid packet size (%d).\n", ret);
global_connection_alive = 0; global_connection_alive = 0;
return NULL; return NULL;
} }
// NOTE(Tiiffi): This should fail if size is out of spec! // NOTE(Tiiffi): This should fail if size is out of spec!
if (psize < 10 || psize > DATA_BUFFSIZE) { if (psize < MIN_PACKET_SIZE || psize > MAX_PACKET_SIZE) {
fprintf(stderr, "Warning: invalid packet size (%d). Must over 10 and less than %d.\n", psize, DATA_BUFFSIZE); fprintf(stderr, "Warning: invalid packet size (%d). Must over 10 and less than %d.\n", psize, MAX_PACKET_SIZE);
if(psize > DATA_BUFFSIZE || psize < 0) psize = DATA_BUFFSIZE; // WARNING(Tiiffi): This is probably not the way to go. Probably should just fail and exit.
if(psize > MAX_PACKET_SIZE || psize < 0) psize = MAX_PACKET_SIZE;
net_clean_incoming(sd, psize); net_clean_incoming(sd, psize);
return NULL; return NULL;
@ -447,8 +477,8 @@ rc_packet *net_recv_packet(int sd)
int received = 0; int received = 0;
while (received < psize) { while (received < psize) {
ret = recv(sd, (char *) &packet + sizeof(int) + received, psize - received, 0); ret = recv(sd, (char *) &packet + sizeof(int32_t) + received, psize - received, 0);
if (ret == 0) { /* connection closed before completing receving */ if (ret == 0) {
fprintf(stderr, "Connection lost.\n"); fprintf(stderr, "Connection lost.\n");
global_connection_alive = 0; global_connection_alive = 0;
return NULL; return NULL;
@ -568,6 +598,11 @@ rc_packet *packet_build(int id, int cmd, char *s1)
{ {
static rc_packet packet = {0, 0, 0, { 0x00 }}; static rc_packet packet = {0, 0, 0, { 0x00 }};
// NOTE(Tiiffi): Issue report states that outgoing payload has max size of 1460 bytes:
// https://github.com/Tiiffi/mcrcon/issues/45#issuecomment-1000940814
// https://mctools.readthedocs.io/en/master/rcon.html
// Have to do some testing to confirm!
// size + id + cmd + s1 + s2 NULL terminator // size + id + cmd + s1 + s2 NULL terminator
int len = strlen(s1); int len = strlen(s1);
if (len >= DATA_BUFFSIZE) { if (len >= DATA_BUFFSIZE) {
@ -575,7 +610,7 @@ rc_packet *packet_build(int id, int cmd, char *s1)
return NULL; return NULL;
} }
packet.size = sizeof(int) * 2 + len + 2; packet.size = sizeof packet.size * 2 + len + 2;
packet.id = id; packet.id = id;
packet.cmd = cmd; packet.cmd = cmd;
strncpy(packet.data, s1, DATA_BUFFSIZE - 1); strncpy(packet.data, s1, DATA_BUFFSIZE - 1);
@ -595,10 +630,18 @@ int rcon_auth(int sock, char *passwd)
if (!ret) if (!ret)
return 0; // send failed return 0; // send failed
receive:
packet = net_recv_packet(sock); packet = net_recv_packet(sock);
if (packet == NULL) if (packet == NULL)
return 0; return 0;
/* Valve rcon sends empty "RCON_RESPONSEVALUE" packet before real auth response
* so we have to check packet type and try again if necessary.
*/
if (packet->cmd != RCON_AUTH_RESPONSE) {
goto receive;
}
// return 1 if authentication OK // return 1 if authentication OK
return packet->id == -1 ? 0 : 1; return packet->id == -1 ? 0 : 1;
} }
@ -653,7 +696,7 @@ int run_commands(int argc, char *argv[])
int run_terminal_mode(int sock) int run_terminal_mode(int sock)
{ {
int ret = 0; int ret = 0;
char command[DATA_BUFFSIZE] = {0x00}; char command[DATA_BUFFSIZE] = {0};
puts("Logged in.\nType 'Q' or press Ctrl-D / Ctrl-C to disconnect."); puts("Logged in.\nType 'Q' or press Ctrl-D / Ctrl-C to disconnect.");