aboutsummaryrefslogtreecommitdiff
path: root/src/rcon.c
blob: a302d12839201ac1f259c101a486d1d518b283e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/*
 * src/rcon.c
 * (c) 2020 Jonas Gunz <himself@jonasgunz.de>
 * License: 
*/

/*
 * Length 	int 	Length of remainder of packet
 * Request ID 	int 	Client-generated ID
 * Type 	int 	3 for login, 2 to run a command, 0 for a multi-packet response
 * Payload 	byte[] 	ASCII text
 * 2-byte pad 	byte^2 	Two null bytes
 *
 * sizeof int: 4B
 * Little Endian
 *
 * LOGIN	3
 * LOGINFAIL	-1
 * COMMAND	2
 * RESPONSE	0
 */

#include "rcon.h"

static char* rcon_errors[] = {
	"No Error.",
	"Undefined Error.",
	"Login Denied.",
	"Buffer size incorrect.",
	"Malformed packet recieved."
};

static int rcon_write_int ( char* _buffer, int _len, int32_t _data ) {
	if ( _len < 4 )
		return -1;

	for (int i = 0; i < 4; i++)
		_buffer[i] = (char)(_data >> (i*8));

	return 4;
}

static int rcon_read_int ( char* _buffer, int _len, int32_t* _out ) {
	if ( _len < 4 )
		return -1;

	*_out = 0;
	for (int i = 0; i < 4; i++)
		*_out |= (int32_t)((int32_t)_buffer[i] << (i*8));

	return 4;
}

int rcon_parse_packet ( rcon_packet_t* _packet, char* _buffer, uint32_t _len ) {
	if ( _len < 14 )
		return -1;

	int32_t bytecount = 0;
	
	bytecount += rcon_read_int ( _buffer + bytecount, _len - bytecount, &_packet->length );
	bytecount += rcon_read_int ( _buffer + bytecount, _len - bytecount, &_packet->id );
	bytecount += rcon_read_int ( _buffer + bytecount, _len - bytecount, &_packet->type );

	int32_t payload_len = _len - bytecount - 2;
	_packet->payload_len = payload_len;
	_packet->payload = malloc(payload_len);
	memcpy ( _packet->payload, _buffer + bytecount, payload_len );
	bytecount += payload_len;

	bytecount += 2; // padding
	//TODO check validity
	return bytecount;
}

int rcon_construct_packet ( char* _buffer, uint32_t _len, rcon_packet_t* _packet ) {
	if ( _len < RCON_SEND_PKGSIZE )
		return -1; // Payload does not fit.

	int32_t bytecount = 0;
	_packet->length = _packet->payload_len + 10;

	bytecount += rcon_write_int ( _buffer + bytecount, _len - bytecount, _packet->length );
	bytecount += rcon_write_int ( _buffer + bytecount, _len - bytecount, _packet->id );
	bytecount += rcon_write_int ( _buffer + bytecount, _len - bytecount, _packet->type );

	memcpy ( _buffer + bytecount, _packet->payload, _packet->payload_len );
	bytecount += _packet->payload_len;

	memcpy ( _buffer + bytecount, "\0\0", 2);
	bytecount += 2;

	return bytecount;
}

char* rcon_strerror ( int _errno ) {
	return _errno > 4 ? rcon_errors[1] : rcon_errors[_errno];
}