Files
where-are-my-friends/src/rcon.c
T
oysteikt 16e9f12a34 Split off rcon related code into separate source file
Also rename tests and rename some constants, and modify Makefile to
handle the new structure.
2026-06-11 17:02:21 +09:00

191 lines
5.3 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>
#include "rcon.h"
int rcon_connect(const char* address, 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, address, &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) {
size_t body_len = strlen(body);
size_t packet_size = sizeof(int32_t) * 3 + body_len + 2;
unsigned char *packet = malloc(packet_size);
if (!packet) {
perror("malloc failed");
return;
}
int32_t size = htole32(packet_size - sizeof(int32_t));
memcpy(packet, &size, sizeof(int32_t));
int32_t id_ = htole32(id);
memcpy(packet + 4, &id_, sizeof(int32_t));
int32_t type_ = htole32(type);
memcpy(packet + 8, &type_, sizeof(int32_t));
memcpy(packet + 12, body, body_len);
packet[3 * sizeof(int32_t) + body_len] = 0;
packet[3 * sizeof(int32_t) + 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;
}
packet->size = le32toh(packet->size);
if (packet->size < (int32_t)RCON_PACKET_MIN_SIZE || packet->size > (int32_t)RCON_PACKET_MAX_SIZE) {
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;
}
packet->id = le32toh(packet->id);
bytes = recv(sockfd, &packet->type, sizeof(int32_t), MSG_WAITALL);
if (bytes <= 0) {
perror("recv type failed");
return -1;
}
packet->type = le32toh(packet->type);
int body_size = packet->size - sizeof(int32_t) * 2 - 2;
if (body_size > 0 && body_size < (int)sizeof(packet->body)) {
bytes = recv(sockfd, packet->body, body_size, MSG_WAITALL);
if (bytes <= 0) {
perror("recv body failed");
return -1;
}
packet->body[body_size] = '\0';
bytes = recv(sockfd, &packet->_padding, 1, MSG_WAITALL);
if (bytes <= 0) {
perror("recv padding failed");
return -1;
}
} else {
packet->size = sizeof(int32_t) * 2 + 2;
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, RCON_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, RCON_SERVERDATA_EXECCOMMAND, command);
send_packet(sockfd, dummy_id, RCON_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 == RCON_SERVERDATA_RESPONSE_VALUE && response.id == cmd_id) {
strncat(result, response.body, sizeof(result) - strlen(result) - 1);
}
}
return result;
}