#include #include #include #include #include #include #include #include #define PORT 6969 #define BUFFER_SIZE 10240 #define SEGMENT_BITS 0x7F #define CONTINUE_BIT 0x80 #define UUID_STR_LEN (32 + 4) #define sizeof_member(type, member) (sizeof( ((type *)0)->member )) typedef uint8_t byte; typedef struct MCString_view { int len; unsigned char* str; } MCString_view; typedef struct MCUUID { byte byte[16]; char str[UUID_STR_LEN]; } MCUUID; typedef struct File { char* data; int len; } 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 > 20 ? 20 : bytes_received); // print_str(buffer, bytes_received); printf("%s\n[TCP] fin %ld bytes\n", bytes_received > 20 ? "..." : "", 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; } char byte_to_char(byte b) { return b >= 10 ? b-10 + 'a' : b + '0'; } MCUUID read_UUID(int client_fd) { MCUUID uuid = {0}; int c = 0; for (int i = 0; i < 4; i++) { uuid.byte[i] = read_byte(client_fd); uuid.str[c++] = byte_to_char(uuid.byte[i] >> 4); uuid.str[c++] = byte_to_char((uint8_t)(uuid.byte[i] << 4) >> 4); } uuid.str[c++] = '-'; for (int i = 4; i < 6; i++) { uuid.byte[i] = read_byte(client_fd); uuid.str[c++] = byte_to_char(uuid.byte[i] >> 4); uuid.str[c++] = byte_to_char((uint8_t)(uuid.byte[i] << 4) >> 4); } uuid.str[c++] = '-'; for (int i = 6; i < 8; i++) { uuid.byte[i] = read_byte(client_fd); uuid.str[c++] = byte_to_char(uuid.byte[i] >> 4); uuid.str[c++] = byte_to_char((uint8_t)(uuid.byte[i] << 4) >> 4); } uuid.str[c++] = '-'; for (int i = 8; i < 10; i++) { uuid.byte[i] = read_byte(client_fd); uuid.str[c++] = byte_to_char(uuid.byte[i] >> 4); uuid.str[c++] = byte_to_char((uint8_t)(uuid.byte[i] << 4) >> 4); } uuid.str[c++] = '-'; for (int i = 10; i < 16; i++) { uuid.byte[i] = read_byte(client_fd); uuid.str[c++] = byte_to_char(uuid.byte[i] >> 4); uuid.str[c++] = byte_to_char((uint8_t)(uuid.byte[i] << 4) >> 4); } return uuid; } 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_UUID(int client_fd, MCUUID value) { for (int i = 0; i < (int) sizeof_member(MCUUID, byte); i++) { write_byte(client_fd, value.byte[i]); } } void write_Boolean(int client_fd, bool value) { write_byte(client_fd, 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 write_Long(int client_fd, int64_t value) { (void) client_fd; request[req_current_byte] = value; req_current_byte += 8; } void send_request(int client_fd) { write(client_fd, request, req_current_byte); printf("[INFO] %d byte request sent: ", req_current_byte); print_hex((const char*) request, req_current_byte > 10 ? 10 : req_current_byte); printf("%s\n", req_current_byte > 10 ? "..." : ""); req_current_byte = 0; } 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); char *string = malloc(fsize); fread(string, fsize, 1, f); fclose(f); return (File) { .len = fsize, .data = string, }; } void write_entire_file(const char *filename, File file) { FILE *f = fopen(filename, "w"); fwrite(file.data, file.len, 1, f); fclose(f); } void file_free(File file) { free(file.data); } void status_protocol(int client_fd) { 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, 0); write_String(client_fd, status_rep.data, status_rep.len); send_request(client_fd); printf("\n[STEP] Ping Status\n"); file_free(status_rep); int request_status_len = read_VarInt(client_fd); int request_status_id = read_VarInt(client_fd); printf("[MCPACKET %d] packet length: %d\n", request_status_id, request_status_len); if (request_status_id != 0 || request_status_len != 1) { printf("[ERR] client not in the status state\n"); close_connection(client_fd); return; } write_VarInt(client_fd, 1 /*packet id*/ + 8 /*Long len*/); write_VarInt(client_fd, 1); // protocol id write_Long(client_fd, (long)time(NULL)); send_request(client_fd); printf("\n[STEP] Switch To Login Start\n"); int login_len = read_VarInt(client_fd); int login_id = read_VarInt(client_fd); printf("[MCPACKET %d] packet length: %d\n", login_id, login_len); printf("[NOTE] je ne sais pas a quoi sert cet requete du client\n"); close_connection(client_fd); } void login_protocol(int client_fd) { printf("\n[STEP] Login Start\n"); int request_len = read_VarInt(client_fd); int request_id = read_VarInt(client_fd); printf("[MCPACKET %d] packet length: %d\n", request_id, request_len); MCString_view pseudo = read_String_view(client_fd); // NOTE: le client envoie l'uuid a la fin de la requete et je n'ai aucune // idée de ce qu'il envoie entre le pseudo et l'uuid current_byte = bytes_received - sizeof_member(MCUUID, byte); MCUUID uuid = read_UUID(client_fd); printf("[INFO] player pseudo = %.*s\n", pseudo.len, pseudo.str); printf("[INFO] player UUID = %.*s\n", UUID_STR_LEN, uuid.str); printf("\n[STEP] Login Success / Acknowledge\n"); write_VarInt(client_fd, 1 /*id*/ + 16 /*uuid*/ + pseudo.len + 2 /*list len & bool*/); write_VarInt(client_fd, 2); write_UUID(client_fd, uuid); write_String(client_fd, (char*)pseudo.str, pseudo.len); write_VarInt(client_fd, 0); // len list de property // write_Boolean(client_fd, true); write_entire_file("debug.hex", (File){(char*)request, req_current_byte}); send_request(client_fd); request_len = read_VarInt(client_fd); request_id = read_VarInt(client_fd); printf("[MCPACKET %d] packet length: %d\n", request_id, request_len); close_connection(client_fd); } 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)); printf("\n[STEP] Handshake / List Ping\n"); 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); switch (next_state) { case 1: // status status_protocol(client_fd); break; case 2: // login login_protocol(client_fd); break; case 3: // transfer printf("[TODO] transfer protocol not programmed\n"); close_connection(client_fd); break; default: printf("[ERR] state n°%d not supported\n", next_state); close_connection(client_fd); continue; } } // Fermeture de la socket du serveur close(server_fd); return 0; }