Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for large responses (>=4096 bytes) #57

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 84 additions & 35 deletions mcrcon.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,16 @@
#define RCON_AUTH_RESPONSE 2
#define RCON_PID 0xBADC0DE

#define DATA_BUFFSIZE 4096
#define DATA_BUFFSIZE 4096
#define RCON_PACKET_MAX_SIZE 4106
#define RCON_PACKET_MIN_SIZE 10

// rcon packet structure
typedef struct _rc_packet {
int size;
int id;
int cmd;
char data[DATA_BUFFSIZE];
char data[DATA_BUFFSIZE+1];
// ignoring string2 for now
} rc_packet;

Expand Down Expand Up @@ -408,16 +410,40 @@ int net_send_packet(int sd, rc_packet *packet)
return ret == -1 ? -1 : 1;
}


size_t net_read_bytes( int sd, void *buffer, size_t bytes )
{
size_t received = 0;

while( received < bytes )
{
int ret = recv( sd, (uint8_t*)buffer + received, bytes - received, 0 );

if ( ret == 0 )
{
fprintf(stderr, "Connection lost.\n");
global_connection_alive = 0;
return 0;
}
received += ret;
}

return received;
}

int net_read_int32( int sd, int32_t *value )
{
return net_read_bytes( sd, value, sizeof(int32_t) );
}

rc_packet *net_recv_packet(int sd)
{
int psize;
static rc_packet packet = {0, 0, 0, { 0x00 }};

// packet.size = packet.id = packet.cmd = 0;
int ret = net_read_int32( sd, &psize );

int ret = recv(sd, (char *) &psize, sizeof(int), 0);

if (ret == 0) {
if (ret == 0) {
fprintf(stderr, "Connection lost.\n");
global_connection_alive = 0;
return NULL;
Expand All @@ -429,28 +455,22 @@ rc_packet *net_recv_packet(int sd)
return NULL;
}

if (psize < 10 || psize > DATA_BUFFSIZE) {
fprintf(stderr, "Warning: invalid packet size (%d). Must over 10 and less than %d.\n", psize, DATA_BUFFSIZE);
if (psize < RCON_PACKET_MIN_SIZE || psize > RCON_PACKET_MAX_SIZE) {
fprintf(stderr, "Warning: invalid packet size (%d). Must >= %d and less <= %d.\n", psize, RCON_PACKET_MIN_SIZE, RCON_PACKET_MAX_SIZE);

if(psize > DATA_BUFFSIZE || psize < 0) psize = DATA_BUFFSIZE;
if(psize > RCON_PACKET_MAX_SIZE || psize < 0) psize = RCON_PACKET_MAX_SIZE;
net_clean_incoming(sd, psize);

return NULL;
}

packet.size = psize;

int received = 0;
while (received < psize) {
ret = recv(sd, (char *) &packet + sizeof(int) + received, psize - received, 0);
if (ret == 0) { /* connection closed before completing receving */
fprintf(stderr, "Connection lost.\n");
global_connection_alive = 0;
return NULL;
}

received += ret;
}
ret = net_read_bytes( sd, &packet.id, psize );
if ( ret == 0 )
{
return NULL;
}

return &packet;
}
Expand Down Expand Up @@ -512,8 +532,11 @@ void print_color(int color)
}
}


// this hacky mess might use some optimizing
void packet_print(rc_packet *packet)
/* Providing a way to not spit out the newline in the case of
* continued packet responses */
void packet_print_nl(rc_packet *packet, int newline )
{
if (global_raw_output == 1) {
for (int i = 0; packet->data[i] != 0; ++i)
Expand Down Expand Up @@ -556,7 +579,14 @@ void packet_print(rc_packet *packet)
}

// print newline if string has no newline
if (packet->data[i-1] != 10 && packet->data[i-1] != 13) putchar('\n');
if ( newline ) {
if (packet->data[i-1] != 10 && packet->data[i-1] != 13) putchar('\n');
}
}

void packet_print( rc_packet *packet )
{
packet_print_nl( packet, 1 );
}

rc_packet *packet_build(int id, int cmd, char *s1)
Expand All @@ -573,7 +603,8 @@ rc_packet *packet_build(int id, int cmd, char *s1)
packet.size = sizeof(int) * 2 + s1_len + 2;
packet.id = id;
packet.cmd = cmd;
strncpy(packet.data, s1, DATA_BUFFSIZE);

strncpy(packet.data, s1, DATA_BUFFSIZE + 1);

return &packet;
}
Expand All @@ -583,6 +614,7 @@ int rcon_auth(int sock, char *passwd)
int ret;

rc_packet *packet = packet_build(RCON_PID, RCON_AUTHENTICATE, passwd);

if (packet == NULL)
return 0;

Expand All @@ -601,24 +633,41 @@ int rcon_auth(int sock, char *passwd)
int rcon_command(int sock, char *command)
{
rc_packet *packet = packet_build(RCON_PID, RCON_EXEC_COMMAND, command);
if (packet == NULL) {

if (packet == NULL) {
global_connection_alive = 0;
return 0;
}

net_send_packet(sock, packet);

packet = net_recv_packet(sock);
if (packet == NULL)
return 0;

if (packet->id != RCON_PID)
return 0;

if (!global_silent_mode) {
if (packet->size > 10)
packet_print(packet);
}
int done = 0;
while( ! done )
{
packet = net_recv_packet(sock);
if (packet == NULL)
return 0;

if (packet->id != RCON_PID)
return 0;

/* RCON is 'broken' when it comes to responses larger that 4096 data bytes. The
* protocoal does not provide a way to say 'more is coming'. Looking at the minecraft
* server source though, it will send a packet with exactly 4096 bytes of data that
* is not NULL terminated if there is > 4096 bytes. Thus, if the response is
* exactly the max packet size and the last byte is not a NULL then there is more
* data coming.
* Question: What happens when it is exactly a 4096 bytes response?
*/
if ( packet->size != RCON_PACKET_MAX_SIZE || packet->data[DATA_BUFFSIZE-1] == 0 ) {
done = 1;
}

if (!global_silent_mode) {
if (packet->size > RCON_PACKET_MIN_SIZE)
packet_print_nl(packet, done);
}
}

return 1;
}
Expand Down