#include #include #include #include #include #include #include #include #include #include const char *LOCALHOST = "127.0.0.1"; #define SERVERDATA_AUTH 3 #define SERVERDATA_AUTH_RESPONSE 2 #define SERVERDATA_EXECCOMMAND 2 #define SERVERDATA_RESPONSE_VALUE 0 typedef struct { int32_t size; int32_t id; int32_t type; char body[4096]; } rcon_packet_t; int read_env_int(const char *var) { const char* port_str = getenv(var); if (port_str == NULL) { fprintf(stderr, "error: %s environment variable not set.\n", var); exit(EXIT_FAILURE); } char* endptr; long val = strtol(port_str, &endptr, 10); if (endptr == port_str || *endptr != '\0') { fprintf(stderr, "error: invalid number format for %s.\n", var); exit(EXIT_FAILURE); } return (int)val; } int rcon_connect(int port) { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } struct timeval tv; tv.tv_sec = 2; tv.tv_usec = 0; setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); if (inet_pton(AF_INET, LOCALHOST, &server_addr.sin_addr) <= 0) { perror("invalid address"); close(sockfd); exit(EXIT_FAILURE); } if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("connection failed"); close(sockfd); exit(EXIT_FAILURE); } printf("connected to rcon server\n"); 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; size_t packet_size = sizeof(int32_t) + size; unsigned char *packet = malloc(packet_size); if (!packet) { perror("malloc failed"); return; } packet[0] = size & 0xFF; packet[1] = (size >> 8) & 0xFF; packet[2] = (size >> 16) & 0xFF; packet[3] = (size >> 24) & 0xFF; packet[4] = id & 0xFF; packet[5] = (id >> 8) & 0xFF; packet[6] = (id >> 16) & 0xFF; packet[7] = (id >> 24) & 0xFF; packet[8] = type & 0xFF; packet[9] = (type >> 8) & 0xFF; packet[10] = (type >> 16) & 0xFF; packet[11] = (type >> 24) & 0xFF; memcpy(packet + 12, body, body_len); packet[12 + body_len] = 0; packet[12 + body_len + 1] = 0; ssize_t sent = send(sockfd, packet, packet_size, 0); if (sent < 0) { perror("send failed"); } free(packet); } int recv_packet(int sockfd, rcon_packet_t *packet) { ssize_t bytes; bytes = recv(sockfd, &packet->size, sizeof(int32_t), MSG_WAITALL); if (bytes <= 0) { if (bytes == 0) { fprintf(stderr, "connection closed by server\n"); } else if (errno == EAGAIN || errno == EWOULDBLOCK) { return -2; // Timeout } else { perror("recv size failed"); } return -1; } if (packet->size < 10 || packet->size > 4110) { fprintf(stderr, "invalid packet size: %d\n", packet->size); return -1; } bytes = recv(sockfd, &packet->id, sizeof(int32_t), MSG_WAITALL); if (bytes <= 0) { perror("recv id failed"); return -1; } bytes = recv(sockfd, &packet->type, sizeof(int32_t), MSG_WAITALL); if (bytes <= 0) { perror("recv type failed"); return -1; } int body_size = packet->size - 10; if (body_size > 0 && body_size < (int)sizeof(packet->body)) { bytes = recv(sockfd, packet->body, body_size + 2, MSG_WAITALL); if (bytes <= 0) { perror("recv body failed"); return -1; } packet->body[body_size] = '\0'; } else { packet->body[0] = '\0'; } return 0; } int rcon_authenticate(int sockfd, const char *password) { printf("authenticating with password: '%s'\n", password); send_packet(sockfd, 1, SERVERDATA_AUTH, password); printf("waiting for auth response...\n"); rcon_packet_t response; if (recv_packet(sockfd, &response) < 0) { fprintf(stderr, "failed to receive auth response\n"); return -1; } printf("received auth packet: id=%d, type=%d\n", response.id, response.type); // Check if authentication failed (id == -1) if (response.id == -1) { fprintf(stderr, "authentication failed - invalid password\n"); return -1; } // Try to read second packet (may timeout, which is OK) rcon_packet_t response2; int result = recv_packet(sockfd, &response2); if (result == 0) { printf("received second auth packet: id=%d, type=%d\n", response2.id, response2.type); } else if (result == -2) { printf("no second packet (timeout - this is normal)\n"); } printf("authenticated successfully\n"); return 0; } char* rcon_command_multipacket(int sockfd, const char *command) { static char result[65536]; result[0] = '\0'; int cmd_id = 100; int dummy_id = 101; send_packet(sockfd, cmd_id, SERVERDATA_EXECCOMMAND, command); send_packet(sockfd, dummy_id, SERVERDATA_EXECCOMMAND, ""); while (1) { rcon_packet_t response; if (recv_packet(sockfd, &response) < 0) { fprintf(stderr, "failed to receive command response\n"); return NULL; } if (response.id == dummy_id) { break; } if (response.type == SERVERDATA_RESPONSE_VALUE && response.id == cmd_id) { strncat(result, response.body, sizeof(result) - strlen(result) - 1); } } 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) { fprintf(stderr, "error: RCON_PASSWORD environment variable not set.\n"); exit(EXIT_FAILURE); } printf("=== RCON Configuration ===\n"); printf("port: %d\n", PORT_NUM); printf("password: '%s'\n", password); printf("==========================\n\n"); int sockfd = rcon_connect(PORT_NUM); if (rcon_authenticate(sockfd, password) < 0) { close(sockfd); exit(EXIT_FAILURE); } const char *get_player_positions_cmd = "execute as @a run data get entity @s Pos"; printf("\nexecuting command: %s\n", get_player_positions_cmd); char *response = rcon_command_multipacket(sockfd, get_player_positions_cmd); if (response) { printf("\n=== player positions ===\n"); printf("%s\n", response); } else { fprintf(stderr, "failed to get player positions\n"); } close(sockfd); return 0; }