From 4e0036e5f9632c046c2bb0a797f28ada87c43642 Mon Sep 17 00:00:00 2001 From: Fredrik Robertsen Date: Sat, 7 Feb 2026 22:16:10 +0100 Subject: [PATCH] rcon protocol helpers --- src/main.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 6d2ec3d..930fba8 100644 --- a/src/main.c +++ b/src/main.c @@ -58,9 +58,107 @@ int rcon_connect(int port) { return sockfd; } + +void send_packet(int sockfd, int32_t id, int32_t type, const char *body) { + int body_len = strlen(body); + int32_t size = 10 + body_len; // 4 (id) + 4 (type) + body + 2 (null terminators) + + send(sockfd, &size, sizeof(int32_t), 0); + send(sockfd, &id, sizeof(int32_t), 0); + send(sockfd, &type, sizeof(int32_t), 0); + send(sockfd, body, body_len + 1, 0); + const char null = '\0'; + send(sockfd, &null, 1, 0); +} + +int recv_packet(int sockfd, rcon_packet_t *packet) { + if (recv(sockfd, &packet->size, sizeof(int32_t), 0) <= 0) { return -1; } + if (recv(sockfd, &packet->id, sizeof(int32_t), 0) <= 0) { return -1; } + if (recv(sockfd, &packet->type, sizeof(int32_t), 0) <= 0) { return -1; } + + // read body (size - 10 bytes for id, type, and two null terminators) + int body_size = packet->size - 10; + if (body_size > 0 && body_size < sizeof(packet->body)) { + if (recv(sockfd, packet->body, body_size + 2, 0) <= 0) { + return -1; + } + packet->body[body_size] = '\0'; + } else { + packet->body[0] = '\0'; + } + + return 0; +} + +int rcon_authenticate(int sockfd, const char *password) { + send_packet(sockfd, 1, SERVERDATA_AUTH, password); + + rcon_packet_t response; + if (recv_packet(sockfd, &response) < 0) { + perror("failed to receive auth response\n"); + return -1; + } + + if (response.id == -1) { + perror("authentication failed\n"); + return -1; + } + + printf("authenticated successfully\n"); + return 0; +} + +char* rcon_command(int sockfd, const char *command) { + static char result[8192]; + result[0] = '\0'; + + send_packet(sockfd, 2, SERVERDATA_EXECCOMMAND, command); + + // Handle multi-packet responses + while (1) { + rcon_packet_t response; + if (recv_packet(sockfd, &response) < 0) { + perror("failed to receive command response\n"); + return NULL; + } + + if (response.type == SERVERDATA_RESPONSE_VALUE) { + strncat(result, response.body, sizeof(result) - strlen(result) - 1); + + // Check if response is complete (body length < 4096) + if (strlen(response.body) < 4096) { + break; + } + } + } + + return result; +} + int main(int argc, char **argv) { const int PORT_NUM = read_env_int("RCON_PORT_NUMBER"); + const char *password = getenv("RCON_PASSWORD"); + + if (password == NULL) { + perror("error: RCON_PASSWORD environment variable not set.\n"); + exit(EXIT_FAILURE); + } + printf("port: %d\n", PORT_NUM); - rcon_connect(PORT_NUM); + int sockfd = rcon_connect(PORT_NUM); + + if (rcon_authenticate(sockfd, password) < 0) { + close(sockfd); + exit(EXIT_FAILURE); + } + + // example + char *response = rcon_command(sockfd, "help"); + if (response) { + printf("Response:\n%s\n", response); + } + + close(sockfd); + return 0; }