Skip to content

Instantly share code, notes, and snippets.

@MultiMote
Last active August 15, 2017 12:19
Show Gist options
  • Save MultiMote/169265fd74fe94b44941c1b05b296f0d to your computer and use it in GitHub Desktop.
Save MultiMote/169265fd74fe94b44941c1b05b296f0d to your computer and use it in GitHub Desktop.
Battleye RCON c test
// MultiMote. 2017
// just kidding, really
// this is for UDP sockets testing, not a real connector
// I will rewrite it for QT
#include <stdio.h>
#include <stdint.h>
#include <conio.h>
#include <string.h>
#include <pthread.h>
#include "crc32.h"
#include "socket_utils.h"
#define SERVER_IP ""
#define PORT 2302
#define PASSWORD ""
void append_byte(char** dst, char c) {
*(*dst) = c;
++(*dst);
}
void append_str(char** dst, char* src) {
while(*src != '\0') {
append_byte(dst, *src);
++src;
}
}
void append_bytes(char** dst, char* src, char* end) {
while(src != end) {
append_byte(dst, *src);
++src;
}
}
enum be_packet_type {
BE_PACKET_LOGIN = 0x00,
BE_PACKET_COMMAND = 0x01,
BE_PACKET_MESSAGE = 0x02,
};
enum packet_handle_retval {
RV_RESULT_UNKNOWN = 0x00,
RV_LOGIN_OK = 0x01,
RV_LOGIN_BAD = 0x02,
};
char request[1040];
char cmd[1024];
SOCKET s;
struct sockaddr_in si_other;
int sockaddr_len = sizeof(si_other);
char cmd_sequence_number;
char* add_header_to_packet(char* cmd_begin, char* cmd_end) {
char* preq = request;
append_str(&preq, "BE");
unsigned int crc = crc32(cmd_begin, cmd_end);
*((uint32_t*)preq) = crc;
preq += 4;
append_bytes(&preq, cmd_begin, cmd_end);
return preq;
}
int make_packet(enum be_packet_type type, char* data) {
char* pcmd = cmd;
append_byte(&pcmd, 0xFF);
append_byte(&pcmd, type);
switch (type) {
case BE_PACKET_LOGIN:
cmd_sequence_number = 0;
printf("Send login\n");
append_str(&pcmd, PASSWORD);
break;
case BE_PACKET_MESSAGE:
printf("Send ack\n");
append_byte(&pcmd, data[0]);
break;
case BE_PACKET_COMMAND:
printf("Send cmd\n");
append_byte(&pcmd, cmd_sequence_number);
append_str(&pcmd, data);
break;
default:
break;
}
return add_header_to_packet(cmd, pcmd) - request;
}
void send_to_server(char* data, int len) {
if (sendto(s, data, len, 0 , (struct sockaddr *) &si_other, sockaddr_len) == -1) {
printf("sendto err()");
}
printf("Sent\n");
}
void make_and_send_packet(enum be_packet_type type, char* data) {
int len = make_packet(type, data);
send_to_server(request, len);
}
char msg[1024];
enum packet_handle_retval handle_packet(char* data, int len) {
if(data[0] == 'B' && data[1] == 'E') {
// crc check
unsigned char packet_type = data[7];
printf("Packet type: 0x%02x\n", packet_type);
switch (packet_type) {
case BE_PACKET_LOGIN: {
char login_result = data[8];
printf("Logged in: %s\n", login_result ? "OK" : "BAD");
return (login_result == 0x01) ? RV_LOGIN_OK : RV_LOGIN_BAD;
}
case BE_PACKET_MESSAGE: {
char* pmsg = msg;
append_bytes(&pmsg, data + 9, data + len);
append_byte(&pmsg, '\0');
printf("MSG: %s\n", msg);
make_and_send_packet(BE_PACKET_MESSAGE, data + 8); // ack
break;
}
case BE_PACKET_COMMAND: { // TODO, BORED
break;
}
default:
fprintf(stderr, "Unknown packet ID!");
break;
}
} else {
fprintf(stderr, "Not a BE packet!");
}
return RV_RESULT_UNKNOWN;
}
#define RECV_BUF_LEN 2048
char recv_buf[RECV_BUF_LEN];
void *poll_thread(void* param) {
int len = 1;
while(len > 0) {
len = recvfrom(s, recv_buf, RECV_BUF_LEN, 0, (struct sockaddr *) &si_other, &sockaddr_len);
if(len == -1) break;
printf("recv len=%d\n", len);
handle_packet(recv_buf, len);
}
printf("Recv error\n");
_socket_close(&s);
_sockets_cleanup();
}
int main(int argc, char const *argv[]) {
pthread_t thread_info;
_socket_init(&s);
if (s == -1) {
printf("Socket error\n");
return 0;
}
struct hostent *hostname = gethostbyname(SERVER_IP);
if(hostname) {
memcpy(&si_other.sin_addr, hostname->h_addr_list[0], hostname->h_length);
} else {
si_other.sin_addr.s_addr = inet_addr(SERVER_IP);
}
si_other.sin_family = AF_INET;
si_other.sin_port = htons(PORT);
make_and_send_packet(BE_PACKET_LOGIN, NULL);
int len = recvfrom(s, recv_buf, RECV_BUF_LEN, 0, (struct sockaddr *) &si_other, &sockaddr_len);
if(handle_packet(recv_buf, len) == RV_LOGIN_OK) {
printf("Creating thread\n");
pthread_create(&thread_info, 0, poll_thread, NULL);
char c;
do {
c = getchar();
if(c == 'c') {
make_and_send_packet(BE_PACKET_COMMAND, ""); // keepalive
} else if(c == 'p') {
make_and_send_packet(BE_PACKET_COMMAND, "players");
}
} while (c != 'q');
}
printf("Goodbye\n");
_socket_close(&s);
_sockets_cleanup();
return 0;
}
#include "crc32.h"
unsigned int crc32(char *begin, char *end) {
int j;
unsigned int byte, crc, mask;
static unsigned int table[256];
/* Set up the table, if necessary. */
if (table[1] == 0) {
for (byte = 0; byte <= 255; byte++) {
crc = byte;
for (j = 7; j >= 0; j--) { // Do eight times.
mask = -(crc & 1);
crc = (crc >> 1) ^ (0xEDB88320 & mask);
}
table[byte] = crc;
}
}
/* Through with table setup, now calculate the CRC. */
crc = 0xFFFFFFFF;
while (begin != end) {
byte = *begin;
crc = (crc >> 8) ^ table[(crc ^ byte) & 0xFF];
++begin;
}
return ~crc;
}
#ifndef __CRC32_H
#define __CRC32_H
//http://www.hackersdelight.org/hdcodetxt/crc.c.txt
/* This is derived from crc32b but does table lookup. First the table
itself is calculated, if it has not yet been set up.
Not counting the table setup (which would probably be a separate
function), when compiled to Cyclops with GCC, this function executes in
7 + 13n instructions, where n is the number of bytes in the input
message. It should be doable in 4 + 9n instructions. In any case, two
of the 13 or 9 instrucions are load byte.
This is Figure 14-7 in the text. */
unsigned int crc32(char *begin, char *end);
#endif
#ifndef SOCKET_UTILS_H
#define SOCKET_UTILS_H
#ifdef _WIN32
#include <windows.h>
#define close_socket(x) closesocket(x)
//#define SHUT_RDWR SD_BOTH
#define SHUT_RDWR 0x02
#else
#include<sys/socket.h>
#include<arpa/inet.h> //inet_addr
#include<unistd.h> //write
#define close_socket(x) close(x)
#endif
int _socket_init(SOCKET *s) {
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData)) {
perror("Unable to init WinSocks!");
return 1;
}
#endif
// int socket(int domain, int type, int protocol);
*s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (*s == -1) {
perror("Unable to init Sockets!");
return 2;
}
return 0;
}
int _socket_close(SOCKET *s) {
return close_socket(*s);
}
int _socket_shutdown(SOCKET *s) {
return shutdown(*s, SHUT_RDWR);
}
int _socket_is_invalid(SOCKET *s) {
#ifdef _WIN32
return *s == INVALID_SOCKET;
#else
return *s < 0;
#endif
}
void _sockets_cleanup() {
#ifdef _WIN32
WSACleanup();
#endif
}
#endif // SOCKET_UTILS_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment