From 1a6bc169356a42f04d1bfcbbe2ab39d82990f8a5 Mon Sep 17 00:00:00 2001 From: nemo Date: Sat, 25 Jan 2025 18:49:24 +0100 Subject: [PATCH] init --- .gitignore | 1 + Makefile | 5 + main.c | 263 +++++++++++++++++++++++++++++++++++++++++++ status_response.json | 21 ++++ 4 files changed, 290 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 main.c create mode 100644 status_response.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..18874b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +mcsrv diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..39370c3 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +all: + gcc -Wall -Wextra main.c -o mcsrv + +run: all + ./mcsrv diff --git a/main.c b/main.c new file mode 100644 index 0000000..dfe9388 --- /dev/null +++ b/main.c @@ -0,0 +1,263 @@ +#include +#include +#include +#include +#include +#include + +#define PORT 6969 +#define BUFFER_SIZE 10240 + +#define SEGMENT_BITS 0x7F +#define CONTINUE_BIT 0x80 + +typedef uint8_t byte; + +typedef struct MCString_view { + int len; + unsigned char* str; +} MCString_view; + +typedef struct File { + int len; + char* data; +} File; + +byte buffer[BUFFER_SIZE] = {0}; +ssize_t bytes_received = 0; +int current_byte = 0; + +byte request[BUFFER_SIZE] = {0}; +int req_current_byte = 0; + +void print_hex(const char *str, int len) +{ + for (int i = 0; i < len; i++) { + printf("0x%02x, ", (unsigned int) str[i]); + } +} + +void print_str(const char *str, int len) +{ + for (int i = 0; i < len; i++) { + printf("%c,", (unsigned int) str[i]); + } + printf("\n"); +} + +void close_connection(int client_fd) +{ + bytes_received = 0; + current_byte = 0; + req_current_byte = 0; + close(client_fd); + printf("[TCP] connection closed\n"); +} + +byte read_byte(int client_fd) +{ + if (current_byte >= bytes_received) { + bytes_received = recv(client_fd, buffer, BUFFER_SIZE - 1, 0); + current_byte = 0; + + if (bytes_received > 0) { + buffer[bytes_received] = '\0'; + printf("[TCP] %ld bytes reçu :\n", bytes_received); + print_hex((const char*)buffer, bytes_received); + // print_str(buffer, bytes_received); + printf("\n[TCP] fin %ld bytes\n", bytes_received); + } + else if (bytes_received < 0) { + perror("[ERR] Erreur lors de la réception des données"); + close_connection(client_fd); + } + else { + printf("[ERR] Le client s'est déconnecté.\n"); + close_connection(client_fd); + } + } + + return buffer[current_byte++]; +} + +int read_VarInt(int client_fd) +{ + int value = 0; + + for (int position = 0; position < 32; position += 7) { + + uint8_t current_byte = read_byte(client_fd); + value |= (current_byte & SEGMENT_BITS) << position; + + if ((current_byte & CONTINUE_BIT) == 0) break; + + // if (position >= 32) throw new RuntimeException("VarInt is too big"); + } + + return value; +} + +MCString_view read_String_view(int client_fd) +{ + MCString_view str = { + .len = read_VarInt(client_fd), + .str = &buffer[current_byte], + }; + current_byte += str.len; + return str; +} + +uint16_t read_UShort(int client_fd) +{ + uint8_t var1 = read_byte(client_fd); + uint8_t var2 = read_byte(client_fd); + + uint16_t var = (var1 << 8) + var2; + return var; +} + +void write_byte(int client_fd, int value) +{ + (void) client_fd; + request[req_current_byte++] = value; +} + +void write_VarInt(int client_fd, int value) +{ + while (true) { + if ((value & ~SEGMENT_BITS) == 0) { + write_byte(client_fd, value); + return; + } + + write_byte(client_fd, (value & SEGMENT_BITS) | CONTINUE_BIT); + + value >>= 7; + } +} + +void write_String(int client_fd, const char* str, int len) +{ + (void) client_fd; + write_VarInt(client_fd, len); + for (int i = 0; i < len; i++) { + request[req_current_byte++] = str[i]; + } +} + +void send_request(int client_fd) +{ + write(client_fd, request, req_current_byte); +} + +File read_entire_file(const char *filename) +{ + FILE *f = fopen(filename, "r"); + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); /* same as rewind(f); */ + + char *string = malloc(fsize); + fread(string, fsize, 1, f); + fclose(f); + + return (File) { + .len = fsize, + .data = string, + }; +} + +void file_free(File file) +{ + free(file.data); +} + +int main() +{ + int server_fd, client_fd; + struct sockaddr_in server_addr, client_addr; + socklen_t client_addr_len = sizeof(client_addr); + + // Création de la socket + if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + perror("Erreur lors de la création de la socket"); + exit(EXIT_FAILURE); + } + int option = 1; + setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); + + // Configuration de l'adresse du serveur + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; // Accepter les connexions depuis n'importe quelle adresse IP + server_addr.sin_port = htons(PORT); + + // Liaison de la socket à l'adresse et au port + if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { + perror("Erreur lors du bind"); + close(server_fd); + exit(EXIT_FAILURE); + } + + // Mise en écoute pour accepter des connexions + if (listen(server_fd, 5) == -1) { // 5 est le nombre maximum de connexions en file d'attente + perror("Erreur lors du listen"); + close(server_fd); + exit(EXIT_FAILURE); + } + + printf("[INFO] Serveur en écoute sur le port %d...\n", PORT); + + // Boucle principale pour accepter et gérer les connexions clients + while (1) { + // Accepter une connexion cliente + client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len); + if (client_fd == -1) { + perror("Erreur lors de l'acceptation de la connexion"); + continue; + } + printf("[TCP] connection %s:%d\n", + inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + + int packet_length = read_VarInt(client_fd); + int packet_id = read_VarInt(client_fd); + printf("[MCPACKET %d] packet length: %d\n", packet_id, packet_length); + + int protocol_version = read_VarInt(client_fd); + printf("[INFO] protocol version: %d\n", protocol_version); + + // if (protocol_version != 760 /* mc 1.19.2 */) { + // printf("[ERR] protocol version %d non supporter, connection fermer\n", protocol_version); + // close_connection(client_fd); + // continue; + // } + + MCString_view address = read_String_view(client_fd); + uint16_t port = read_UShort(client_fd); + printf("[INFO] server address from client: %.*s:%hu\n", address.len, address.str, port); + + int next_state = read_VarInt(client_fd); + printf("[INFO] next state: %d\n", next_state); + + File status_rep = read_entire_file("status_response.json"); + + // TODO: get VarInt byte size from int, for /*str len*/ + write_VarInt(client_fd, 1 /*packet id*/ + 2 /*str len*/ + status_rep.len); // packet length + write_VarInt(client_fd, packet_id); + write_String(client_fd, status_rep.data, status_rep.len); + + send_request(client_fd); + printf("[INFO] %d byte request sent: ", req_current_byte); + print_hex((const char*) request, 10); + printf("...\n"); + + + file_free(status_rep); + + close_connection(client_fd); + } + + // Fermeture de la socket du serveur + close(server_fd); + + return 0; +} diff --git a/status_response.json b/status_response.json new file mode 100644 index 0000000..05a306c --- /dev/null +++ b/status_response.json @@ -0,0 +1,21 @@ +{ + "version": { + "name": "1.19.2", + "protocol": 760 + }, + "players": { + "max": 100, + "online": 1, + "sample": [ + { + "name": "caca player 69 ?", + "id": "83e2e372-1df3-485d-a360-d9c4caf8b8c4" + } + ] + }, + "description": { + "text": "caca serveur minecraft en \u00a72C" + }, + "favicon": "data:image/png;base64,", + "enforcesSecureChat": false +}