From 42df9f79fedc21c73e9631e54e570e0c377992ac Mon Sep 17 00:00:00 2001 From: Server Ubunto - HOME Date: Fri, 17 Oct 2025 04:51:15 +0100 Subject: [PATCH] Feature: hable to parse NEW_SESSION --- PlayerTracker | Bin 68384 -> 130584 bytes include/parcer.c | 125 +++++++++++++++++ include/parcer.h | 56 ++++++++ include/server_structs.h | 101 ++++++++++++++ include/socket.c | 120 +++++++++------- include/socket.h | 20 +-- source/main.cpp | 288 +++++++++++++++++++++++++++++++++------ 7 files changed, 610 insertions(+), 100 deletions(-) create mode 100644 include/parcer.c create mode 100644 include/parcer.h diff --git a/PlayerTracker b/PlayerTracker index 43c2e04c7129d5615a1079089c3f91371f2bb6b6..5e98092a60675a07e2a0a266a0d7c7e6850fb56f 100755 GIT binary patch literal 130584 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�YJpta7 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 + +int ensure(size_t recv_len, size_t offset, size_t need) { + return (offset + need <= recv_len); +} + +u_int8_t read_uint8(const u_int8_t *buf, size_t recv_len, size_t *offset, int *ok) { + if (!ensure(recv_len, *offset, sizeof(uint8_t))) { + *ok = 0; + return 0; + } + u_int8_t v; + memcpy(&v, buf + *offset, sizeof(v)); + *offset += sizeof(v); + return v; +} + +u_int16_t read_uint16(const u_int8_t *buf, size_t recv_len, size_t *offset, int *ok) { + if (!ensure(recv_len, *offset, sizeof(uint16_t))) { + *ok = 0; + return 0; + } + u_int16_t v; + memcpy(&v, buf + *offset, sizeof(v)); + *offset += sizeof(v); + return (u_int16_t)ntohs(v); +} + +u_int32_t read_uint32(const u_int8_t *buf, size_t recv_len, size_t *offset, int *ok) { + if (!ensure(recv_len, *offset, sizeof(uint32_t))) { + *ok = 0; + return 0; + } + u_int32_t v; + memcpy(&v, buf + *offset, sizeof(v)); + *offset += sizeof(v); + return (u_int32_t)ntohl(v); +} + +int32_t read_int32(const u_int8_t *buf, size_t recv_len, size_t *offset, int *ok) { + if (!ensure(recv_len, *offset, sizeof(int32_t))) { + *ok = 0; + return 0; + } + int32_t v; + memcpy(&v, buf + *offset, sizeof(v)); + *offset += sizeof(v); + return (int32_t)ntohl(v); +} + +void read_bytes(const u_int8_t *buf, size_t recv_len, size_t *offset, u_int8_t *out, size_t len, int *ok) { + if (!ensure(recv_len, *offset, len)) { + *ok = 0; + return; + } + memcpy(out, buf + *offset, len); + *offset += len; +} + +void read_utf32le_string(const uint8_t *buffer, size_t buf_size, size_t *offset, char *dest, size_t max_len, int *ok) { + size_t i = *offset; + size_t j = 0; + + while (i + 3 < buf_size && j < max_len) { + uint32_t codeunit = buffer[i] | (buffer[i + 1] << 8) | (buffer[i + 2] << 16) | (buffer[i + 3] << 24); + + if (codeunit == 0) { + i += 4; // termina a string + break; + } + + if (codeunit < 0x80) { + dest[j++] = (char)codeunit; + } else { + dest[j++] = '?'; // substitui caracteres fora de ASCII + } + + i += 4; + } + + dest[j] = '\0'; + *offset = i; + *ok = 1; +} + +void read_utf16le_string(const uint8_t *buffer, size_t buf_size, size_t *offset, char *dest, size_t max_len, int *ok) { + size_t i = *offset; + size_t j = 0; + + while (i + 1 < buf_size && j < max_len - 1) { + uint16_t codeunit = buffer[i] | (buffer[i + 1] << 8); + + if (codeunit == 0) { + i += 2; // termina a string + break; + } + + if (codeunit < 0x80) { + dest[j++] = (char)codeunit; + } else { + dest[j++] = '?'; // substitui caracteres fora de ASCII + } + + i += 2; + } + + dest[j] = '\0'; + *offset = i; + *ok = 1; +} + +void read_string(const u_int8_t *buf, size_t recv_len, size_t *offset, char *out, size_t len, int *ok) { + if (!ensure(recv_len, *offset, len)) { + *ok = 0; + return; + } + + for (size_t i = 0; i <= len; i++) { + out[i] = (char)buf[*offset + i]; + } + out[len] = '\0'; // Ensure null termination + *offset += len; + *ok = 1; +} diff --git a/include/parcer.h b/include/parcer.h new file mode 100644 index 0000000..c8fa4d2 --- /dev/null +++ b/include/parcer.h @@ -0,0 +1,56 @@ +#ifndef PARCER_H +#define PARCER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include + +// Ensures that there are at least 'need' bytes available in the buffer +// starting from 'offset'. Returns 1 if enough bytes are available, 0 otherwise. +int ensure(size_t recv_len, size_t offset, size_t need); + +// Reads an 8-bit unsigned integer from the buffer. +// Advances the offset by 1 byte. +u_int8_t read_uint8(const u_int8_t *buf, size_t recv_len, size_t *offset, int *ok); + +// Reads a 16-bit unsigned integer from the buffer in network byte order. +// Advances the offset by 2 bytes. +u_int16_t read_uint16(const u_int8_t *buf, size_t recv_len, size_t *offset, int *ok); + +// Reads a 32-bit unsigned integer from the buffer in network byte order. +// Advances the offset by 4 bytes. +u_int32_t read_uint32(const u_int8_t *buf, size_t recv_len, size_t *offset, int *ok); + +// Reads a 32-bit signed integer from the buffer in network byte order. +// Advances the offset by 4 bytes. +int32_t read_int32(const u_int8_t *buf, size_t recv_len, size_t *offset, int *ok); + +// Reads a fixed number of bytes into the output buffer. +// The output buffer must be at least 'len' bytes long. +void read_bytes(const u_int8_t *buf, size_t recv_len, size_t *offset, u_int8_t *out, size_t len, int *ok); + +// Reads a length-prefixed string. The length is a single byte. +// The string is not null-terminated in the buffer, but the function +void read_utf32le_string(const uint8_t *buffer, size_t buf_size, size_t *offset, char *dest, size_t max_len, int *ok); + +// reads up to max_len - 1 characters and null-terminates the destination buffer. +// The string is encoded in UTF-32LE. +void read_utf16le_string(const uint8_t *buffer, size_t buf_size, size_t *offset, char *dest, size_t max_len, int *ok); + +// Reads a null-terminated string from the buffer. +// The string is read into the output buffer, which must be at least max_len bytes long +void read_string(const u_int8_t *buf, size_t recv_len, size_t *offset, char *out, size_t max_len, int *ok); + +#ifdef __cplusplus +} +#endif + +#endif // PARCER_H diff --git a/include/server_structs.h b/include/server_structs.h index 101780a..e766687 100644 --- a/include/server_structs.h +++ b/include/server_structs.h @@ -1,7 +1,10 @@ #ifndef SERVER_STRUCTS_H #define SERVER_STRUCTS_H +#include #include +#include +#include struct handshake { // [not used in the current Remote Telemtry version by AC] // In future versions it will identify the platform type of the client. @@ -50,4 +53,102 @@ struct handshackerResponse{ char trackConfig[50]; } __attribute__((packed)); +struct postion { + float x; + float y; + float z; +} __attribute__((packed)); + +struct carAtributes { + // Related to ACSP_CAR_INFO + u_char isConnected; // 1 = connected, 0 = disconnected + char *carModel; + char *carSkin; + char *driver_name; + char *driver_team; + char *driver_GUID; + + // Related to ACSP_CAR_UPDATE + u_int8_t carID; + postion position; + postion velocity; + u_int8_t carGear; + u_int16_t carRPM; + float_t normalizedSplinePos; + +} __attribute__((packed)); + + +struct trackAtributes { + u_int8_t protocol_version; + u_int8_t session_index; + u_int8_t current_session_index; + u_int8_t session_count; + + char *server_name; + char *track; + char *track_config; + char *session_name; + + u_int8_t typ; + u_int16_t time; + u_int16_t laps; + u_int16_t wait_time; + u_int8_t ambient_temp; + u_int8_t road_temp; + + char *weather_graphics; + int32_t elapsed_ms; +} __attribute__((packed)); + + +enum ACSP_MessageType { + // ============================ + // PROTOCOL VERSION + // ============================ + PROTOCOL_VERSION = 4, + + // ============================ + // SERVER → CLIENT MESSAGES + // ============================ + ACSP_NEW_SESSION = 50, + ACSP_NEW_CONNECTION = 51, + ACSP_CONNECTION_CLOSED = 52, + ACSP_CAR_UPDATE = 53, + ACSP_CAR_INFO = 54, // Response to ACSP_GET_CAR_INFO + ACSP_END_SESSION = 55, + ACSP_VERSION = 56, + ACSP_CHAT = 57, + ACSP_CLIENT_LOADED = 58, + ACSP_SESSION_INFO = 59, + ACSP_ERROR = 60, + ACSP_LAP_COMPLETED = 73, + + // ============================ + // EVENTS + // ============================ + ACSP_CLIENT_EVENT = 130, + + // ============================ + // EVENT TYPES + // ============================ + ACSP_CE_COLLISION_WITH_CAR = 10, + ACSP_CE_COLLISION_WITH_ENV = 11, + + // ============================ + // CLIENT → SERVER COMMANDS + // ============================ + ACSP_REALTIMEPOS_INTERVAL = 200, + ACSP_GET_CAR_INFO = 201, + ACSP_SEND_CHAT = 202, // Sends chat to one car + ACSP_BROADCAST_CHAT = 203, // Sends chat to everybody + ACSP_GET_SESSION_INFO = 204, + ACSP_SET_SESSION_INFO = 205, + ACSP_KICK_USER = 206, + ACSP_NEXT_SESSION = 207, + ACSP_RESTART_SESSION = 208, + ACSP_ADMIN_COMMAND = 209 // Send message plus a string +}; + + #endif // SERVER_STRUCTS_H diff --git a/include/socket.c b/include/socket.c index ff14f1a..baed2dc 100644 --- a/include/socket.c +++ b/include/socket.c @@ -3,62 +3,88 @@ // ========================= // UDP SOCKET FUCNTIONS // ========================= -// @param ip: IP address to connect to -// @param port: Port number to connect to + +// Create a UDP socket // @return: Socket file descriptor, or -1 on error -int connect_udp_socket(const char* ip, uint16_t port) { - int sockfd = socket(AF_INET, SOCK_DGRAM, 0); - if (sockfd < 0) { - perror("socket creation failed"); - return -1; - } - - struct sockaddr_in servaddr; - memset(&servaddr, 0, sizeof(servaddr)); - - servaddr.sin_family = AF_INET; - servaddr.sin_port = htons(port); - servaddr.sin_addr.s_addr = inet_addr(ip); - - if (connect(sockfd, (const struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { - perror("connection to the server failed"); - close(sockfd); - return -1; - } - - return sockfd; +int create_udp_socket(void) { + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + perror("[!] socket() failed"); + return -1; + } + return sockfd; } -int bind_udp_socket(int sockfd, const char* ip, uint16_t port) { - struct sockaddr_in servaddr; - memset(&servaddr, 0, sizeof(servaddr)); +// Create a UDP socket and connect it to a specific IP and port +// Used for sending data (no bind needed) +// @param ip: Destination IP address as a string +// @return: Socket file descriptor, or -1 on error +int connect_udp_socket(const char *ip, uint16_t port) { + int sockfd = create_udp_socket(); + if (sockfd < 0) + return -1; - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = inet_addr(ip); - servaddr.sin_port = htons(port); + struct sockaddr_in servaddr; + memset(&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(port); + servaddr.sin_addr.s_addr = inet_addr(ip); - if (bind(sockfd, (const struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { - perror("bind failed"); - close(sockfd); - return -1; - } + if (connect(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { + perror("[!] connect() failed"); + close(sockfd); + return -1; + } - return 0; + printf("[+] Connected UDP socket to %s:%d\n", ip, port); + return sockfd; } +// Create and bind a UDP socket to a local IP and port +// Used for listening for incoming data +// @param ip: Local IP address to bind to +// @param port: Local port to bind to +// @return: Socket file descriptor, or -1 on error +int create_bound_udp_socket(const char *ip, uint16_t port) { + int sockfd = create_udp_socket(); + if (sockfd < 0) + return -1; + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(ip); + + if (bind(sockfd, (const struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("[!] bind() failed"); + close(sockfd); + return -1; + } + + printf("[+] Bound UDP socket on %s:%d\n", ip, port); + return sockfd; +} + +// Send a UDP message to the specified IP and port +// @param sockfd: Socket file descriptor +// @param message: Message to send +// @param dest_ip: Destination IP address as a string +// @param dest_port: Destination port +// @return: Number of bytes sent, or -1 on error ssize_t send_udp_message(int sockfd, const char *message, const char *dest_ip, uint16_t dest_port) { - struct sockaddr_in destaddr; - memset(&destaddr, 0, sizeof(destaddr)); + struct sockaddr_in destaddr; + memset(&destaddr, 0, sizeof(destaddr)); + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(dest_port); + destaddr.sin_addr.s_addr = inet_addr(dest_ip); - destaddr.sin_family = AF_INET; - destaddr.sin_port = htons(dest_port); - destaddr.sin_addr.s_addr = inet_addr(dest_ip); + ssize_t n = sendto(sockfd, message, strlen(message), 0, (const struct sockaddr *)&destaddr, sizeof(destaddr)); + if (n < 0) { + perror("[!] sendto() failed"); + return -1; + } - ssize_t n = sendto(sockfd, message, strlen(message), 0, (const struct sockaddr*)&destaddr, sizeof(destaddr)); - if (n < 0) { - perror("sendto failed"); - return -1; - } - - return n; + printf("[+] Sent %zd bytes to %s:%d\n", n, dest_ip, dest_port); + return n; } diff --git a/include/socket.h b/include/socket.h index 339feca..499bb2b 100644 --- a/include/socket.h +++ b/include/socket.h @@ -10,12 +10,15 @@ extern "C" { #include #pragma comment(lib, "ws2_32.lib") // link Winsock #else -#include -#include -#include #include -#include +#include +#include +#include +#include #include +#include +#include +#include #endif // ========================= @@ -24,12 +27,13 @@ extern "C" { // Server hosts a UDP socket at 127.0.0.1:12000 // Client sends a message to the server at 11000 +// UDP socket creation & management +int create_udp_socket(void); int connect_udp_socket(const char *ip, uint16_t port); +int create_bound_udp_socket(const char *ip, uint16_t port); -int bind_udp_socket(int sockfd, const char *ip, uint16_t port); - -ssize_t send_udp_message(int sockfd, const char *message, const char *dest_ip, - uint16_t dest_port); +// UDP messaging +ssize_t send_udp_message(int sockfd, const char *message, const char *dest_ip, uint16_t dest_port); #ifdef __cplusplus } diff --git a/source/main.cpp b/source/main.cpp index 52abda1..cf538ca 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,6 +1,5 @@ -#include -#include -#include +#include +#include #include #include #include @@ -8,6 +7,7 @@ #include #include +#include "parcer.h" #include "server_structs.h" #include "socket.h" @@ -18,54 +18,252 @@ const char *SERVER_OUT_IP = "127.0.0.1"; int main(void) { printf("[+] Starting server...\n"); - // Printf information about the server printf("[+] Server listening on port %d\n", SERVER_IN_PORT); - printf("[+] Server sending to %s:%d\n", SERVER_OUT_IP, SERVER_OUT_PORT); + printf("[+] Server sending to %s:%d\n\n", SERVER_OUT_IP, SERVER_OUT_PORT); - // Create UDP socket - int sock_FD = connect_udp_socket(SERVER_OUT_IP, SERVER_OUT_PORT); - if (sock_FD < 0) { - fprintf(stderr, "[-] Failed to create UDP socket\n"); - return -1; + int send_sock_fd = connect_udp_socket("127.0.0.1", 11000); // SEND requests to Server + int recv_sock_fd = create_bound_udp_socket("0.0.0.0", 12000); // LISTEN to server updates + + if (send_sock_fd < 0 || recv_sock_fd < 0) { + fprintf(stderr, "[!] Failed to create sockets (%d)(%d)\n", send_sock_fd, recv_sock_fd); + return EXIT_FAILURE; } - handshake hs; - hs.identifier = 1; - hs.operationId = 1; - hs.version = 0; - // Send handshake message + unsigned long offset = 0; + int ok = 1; + char buffer[1024]; - printf("[+] Sending handshake message...\t"); - ssize_t bytes_sent = send_udp_message((int)sock_FD, (const char *)&hs, SERVER_OUT_IP, uint16_t(SERVER_OUT_PORT)); - if (bytes_sent >= 0) { - printf("OK (%zd bytes)\n", bytes_sent); - } else { - fprintf(stderr, "ERROR. \n"); - close((int)sock_FD); - return -2; + trackAtributes trackInfo; + carAtributes update; + + trackInfo.server_name = (char *)malloc(128); + trackInfo.track = (char *)malloc(128); + trackInfo.track_config = (char *)malloc(64); + trackInfo.weather_graphics = (char *)malloc(64); + trackInfo.session_name = (char *)malloc(64); + + char message[124] = {0}; // just a place to put info messages from the server itself + + while (1) { + offset = 0; + u_int8_t str_len_8 = 0; + memset(buffer, 0, sizeof(buffer)); + + size_t recv_bytes = (size_t)recvfrom(recv_sock_fd, buffer, sizeof(buffer), 0, NULL, NULL); + if (recv_bytes < 0) { + perror("[!] recvfrom() failed"); + close(send_sock_fd); + close(recv_sock_fd); + return EXIT_FAILURE; + } + + // Convert buffer to utf-8 string and print it + printf("[+] Received %zd bytes from client\n", recv_bytes); + + // The first byte of the messages is always an ACSP_MessageType + switch ((int)buffer[0]) { + // ============================ + // SERVER → CLIENT MESSAGES + // ============================ + + case ACSP_NEW_SESSION: + printf("[+] Message Type: ACSP_NEW_SESSION\n"); + offset = 1; // Reset offset to 1 to read after message types + + trackInfo.protocol_version = read_uint8((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + trackInfo.session_index = read_uint8((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + trackInfo.current_session_index = read_uint8((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + trackInfo.session_count = read_uint8((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + + str_len_8 = read_uint8((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + read_utf32le_string((const u_int8_t *)buffer, recv_bytes, &offset, trackInfo.server_name, str_len_8, &ok); + + str_len_8 = read_uint8((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + read_string((const u_int8_t *)buffer, recv_bytes, &offset, trackInfo.track, str_len_8, &ok); + + str_len_8 = read_uint8((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + read_string((const u_int8_t *)buffer, recv_bytes, &offset, trackInfo.track_config, str_len_8, &ok); + + str_len_8 = read_uint8((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + read_string((const u_int8_t *)buffer, recv_bytes, &offset, trackInfo.session_name, str_len_8, &ok); + + trackInfo.typ = read_uint8((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + trackInfo.time = read_uint16((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + trackInfo.laps = read_uint16((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + trackInfo.wait_time = read_uint16((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + trackInfo.ambient_temp = read_uint8((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + trackInfo.road_temp = read_uint8((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + + str_len_8 = read_uint8((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + read_string((const u_int8_t *)buffer, recv_bytes, &offset, trackInfo.weather_graphics, str_len_8, &ok); + + trackInfo.elapsed_ms = read_int32((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + + + if (!ok) { + fprintf(stderr, "[-] Error parsing NEW_SESSION packet (offset=%zu)\n", offset); + break; + } + + printf("\tServer Name: \"%s\"\n", trackInfo.server_name); + printf("\tTrack: \"%s\"\n", trackInfo.track); + printf("\tTrack Config: \"%s\"\n", trackInfo.track_config); + printf("\tSession Name: \"%s\"\n", trackInfo.session_name); + + printf("\tProtocol Version: %d\tSession Index: %d/%d\tCurrent Session Index: %d\n", trackInfo.protocol_version, trackInfo.session_index, trackInfo.session_count, + trackInfo.current_session_index); + + printf("\tType: %d\tTime: %d mins\tLaps: %d\tWait Time: %d secs\n", trackInfo.typ, trackInfo.time, trackInfo.laps, trackInfo.wait_time); + printf("\tAmbient Temp: %d C\tRoad Temp: %d C\n", trackInfo.ambient_temp, trackInfo.road_temp); + printf("\tWeather Graphics: %s\n", trackInfo.weather_graphics); + printf("\tElapsed Time: %d ms\n", trackInfo.elapsed_ms); + + break; + + case ACSP_NEW_CONNECTION: + printf("[+] Message Type: ACSP_NEW_CONNECTION\n"); + break; + + case ACSP_CONNECTION_CLOSED: + printf("[+] Message Type: ACSP_CONNECTION_CLOSED\n"); + break; + + case ACSP_CAR_UPDATE: + printf("[+] Message Type: ACSP_CAR_UPDATE\n"); + + memcpy(&update.carID, buffer + 1, sizeof(uint8_t)); + offset = 1 + sizeof(uint8_t); + memcpy(&update.position, buffer + offset, sizeof(postion)); + offset += sizeof(postion); + memcpy(&update.velocity, buffer + offset, sizeof(postion)); + offset += sizeof(postion); + memcpy(&update.carGear, buffer + offset, sizeof(u_int8_t)); + offset += sizeof(u_int8_t); + memcpy(&update.carRPM, buffer + offset, sizeof(uint16_t)); + offset += sizeof(uint16_t); + memcpy(&update.normalizedSplinePos, buffer + offset, sizeof(float_t)); + offset += sizeof(float_t); + + printf("\tCar %d Position: X: %.2f Y: %.2f Z: %.2f ", update.carID, update.position.x, update.position.y, update.position.z); + printf("Gear: %d RPM: %d\n", update.carGear, update.carRPM); + break; + + case ACSP_CAR_INFO: + printf("[+] Message Type: ACSP_CAR_INFO\n"); + break; + + case ACSP_END_SESSION: + printf("[+] Message Type: ACSP_END_SESSION\n"); + break; + + case ACSP_VERSION: + printf("[+] Message Type: ACSP_VERSION\n"); + break; + + case ACSP_CHAT: + printf("[+] Message Type: ACSP_CHAT\n"); + break; + + case ACSP_CLIENT_LOADED: + printf("[+] Message Type: ACSP_CLIENT_LOADED\n"); + break; + + case ACSP_SESSION_INFO: + printf("[+] Message Type: ACSP_SESSION_INFO\n"); + break; + + case ACSP_ERROR: + printf("[+] Message Type: ACSP_ERROR\n"); + offset = 1; // Reset offset to 1 to read after message types + + str_len_8 = read_uint8((const u_int8_t *)buffer, recv_bytes, &offset, &ok); + read_utf32le_string((const u_int8_t *)buffer, recv_bytes, &offset, message, str_len_8, &ok); + + printf("\tServer Message: \"%s\"\n", message); + break; + + case ACSP_LAP_COMPLETED: + printf("[+] Message Type: ACSP_LAP_COMPLETED\n"); + break; + + // ============================ + // EVENTS + // ============================ + case ACSP_CLIENT_EVENT: + printf("[+] Message Type: ACSP_CLIENT_EVENT\n"); + break; + + // ============================ + // EVENT TYPES + // ============================ + case ACSP_CE_COLLISION_WITH_CAR: + printf("[+] Event Type: ACSP_CE_COLLISION_WITH_CAR\n"); + break; + + case ACSP_CE_COLLISION_WITH_ENV: + printf("[+] Event Type: ACSP_CE_COLLISION_WITH_ENV\n"); + break; + + // ============================ + // CLIENT → SERVER COMMANDS + // ============================ + case ACSP_REALTIMEPOS_INTERVAL: + printf("[+] Command: ACSP_REALTIMEPOS_INTERVAL\n"); + break; + + case ACSP_GET_CAR_INFO: + printf("[+] Command: ACSP_GET_CAR_INFO\n"); + break; + + case ACSP_SEND_CHAT: + printf("[+] Command: ACSP_SEND_CHAT\n"); + break; + + case ACSP_BROADCAST_CHAT: + printf("[+] Command: ACSP_BROADCAST_CHAT\n"); + break; + + case ACSP_GET_SESSION_INFO: + printf("[+] Command: ACSP_GET_SESSION_INFO\n"); + break; + + case ACSP_SET_SESSION_INFO: + printf("[+] Command: ACSP_SET_SESSION_INFO\n"); + break; + + case ACSP_KICK_USER: + printf("[+] Command: ACSP_KICK_USER\n"); + break; + + case ACSP_NEXT_SESSION: + printf("[+] Command: ACSP_NEXT_SESSION\n"); + break; + + case ACSP_RESTART_SESSION: + printf("[+] Command: ACSP_RESTART_SESSION\n"); + break; + + case ACSP_ADMIN_COMMAND: + printf("[+] Command: ACSP_ADMIN_COMMAND\n"); + break; + + // ============================ + // DEFAULT HANDLER + // ============================ + default: + printf("[!] Unknown Message Type: %d\n", buffer[0]); + break; + } + + usleep(10000); // Sleep for 10ms } + close(send_sock_fd); + close(recv_sock_fd); - uint8_t buffer[512]; // bigger than struct - ssize_t bytes_received = recv(sock_FD, buffer, sizeof(buffer), 0); - - if (bytes_received >= sizeof(handshackerResponse)) { - handshackerResponse resp; - memcpy(&resp, buffer, sizeof(handshackerResponse)); - - printf("[+] Received handshake response:\n"); - printf(" Car: %0x\n", resp.carName); - printf(" Driver: %0x\n", resp.driverName); - printf(" Identifier: %d\n", resp.identifier); - printf(" Version: %0x\n", resp.version); - printf(" Track: %0x\n", resp.trackName); - printf(" Config: %0x\n", resp.trackConfig); - } else { - printf("[!] Packet too short for handshake response (%zd bytes)\n", bytes_received); - } - - - - - + free(trackInfo.server_name); + free(trackInfo.track); + free(trackInfo.track_config); + free(trackInfo.weather_graphics); + free(trackInfo.session_name); return 0; }