mirror of https://github.com/basinserver/basin/
440 lines
21 KiB
C
440 lines
21 KiB
C
//
|
|
// Created by p on 4/14/19.
|
|
//
|
|
|
|
#include "login_stage_handler.h"
|
|
#include <basin/connection.h>
|
|
#include <basin/network.h>
|
|
#include <basin/player.h>
|
|
#include <basin/packet.h>
|
|
#include <basin/globals.h>
|
|
#include <basin/version.h>
|
|
#include <basin/game.h>
|
|
#include <avuna/string.h>
|
|
#include <avuna/json.h>
|
|
#include <avuna/util.h>
|
|
#include <avuna/pmem_hooks.h>
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/md5.h>
|
|
#include <netdb.h>
|
|
#include <arpa/inet.h>
|
|
|
|
|
|
int work_joinServer(struct connection* conn, char* username, char* uuid_string) {
|
|
struct uuid uuid;
|
|
unsigned char* uuidx = (unsigned char*) &uuid;
|
|
if (uuid_string == NULL) {
|
|
if (conn->server->online_mode) return 1;
|
|
MD5_CTX context;
|
|
MD5_Init(&context);
|
|
MD5_Update(&context, username, strlen(username));
|
|
MD5_Final(uuidx, &context);
|
|
} else {
|
|
if (strlen(uuid_string) != 32) return 1;
|
|
char ups[9];
|
|
memcpy(ups, uuid_string, 8);
|
|
ups[8] = 0;
|
|
uuid.uuid1 = ((uint64_t) strtoll(ups, NULL, 16)) << 32;
|
|
memcpy(ups, uuid_string + 8, 8);
|
|
uuid.uuid1 |= ((uint64_t) strtoll(ups, NULL, 16)) & 0xFFFFFFFF;
|
|
memcpy(ups, uuid_string + 16, 8);
|
|
uuid.uuid2 = ((uint64_t) strtoll(ups, NULL, 16)) << 32;
|
|
memcpy(ups, uuid_string + 24, 8);
|
|
uuid.uuid2 |= ((uint64_t) strtoll(ups, NULL, 16)) & 0xFFFFFFFF;
|
|
}
|
|
struct mempool* pool = mempool_new();
|
|
pchild(conn->pool, pool);
|
|
struct packet* resp = packet_new(pool, PKT_LOGIN_CLIENT_LOGINSUCCESS);
|
|
resp->data.login_client.loginsuccess.username = username;
|
|
resp->data.login_client.loginsuccess.uuid = pmalloc(pool, 38);
|
|
snprintf(resp->data.login_client.loginsuccess.uuid, 10, "%08X-", ((uint32_t*) uuidx)[0]);
|
|
snprintf(resp->data.login_client.loginsuccess.uuid + 9, 6, "%04X-", ((uint16_t*) uuidx)[2]);
|
|
snprintf(resp->data.login_client.loginsuccess.uuid + 14, 6, "%04X-", ((uint16_t*) uuidx)[3]);
|
|
snprintf(resp->data.login_client.loginsuccess.uuid + 19, 6, "%04X-", ((uint16_t*) uuidx)[4]);
|
|
snprintf(resp->data.login_client.loginsuccess.uuid + 24, 9, "%08X", ((uint32_t*) (uuidx + 4))[2]);
|
|
snprintf(resp->data.login_client.loginsuccess.uuid + 32, 5, "%04X", ((uint16_t*) uuidx)[7]);
|
|
if (packet_write(conn, resp) < 0) {
|
|
return 1;
|
|
}
|
|
conn->protocol_state = STATE_PLAY;
|
|
struct entity* ep = entity_new(conn->server->next_entity_id++, (double) conn->server->overworld->spawnpos.x + .5, (double) conn->server->overworld->spawnpos.y, (double) conn->server->overworld->spawnpos.z + .5, ENT_PLAYER, 0f, 0f);
|
|
struct player* player = player_new(conn->pool, conn->server, conn, conn->server->overworld, ep, str_dup(resp->data.login_client.loginsuccess.username, 1, conn->pool), uuid, 1); // TODO default gamemode
|
|
conn->player = player;
|
|
hashmap_putint(conn->server->players_by_entity_id, (uint64_t) player->entity->id, player);
|
|
resp->id = PKT_PLAY_CLIENT_JOINGAME;
|
|
resp->data.play_client.joingame.entity_id = ep->id;
|
|
resp->data.play_client.joingame.gamemode = player->gamemode;
|
|
resp->data.play_client.joingame.dimension = conn->server->overworld->dimension;
|
|
resp->data.play_client.joingame.difficulty = (uint8_t) conn->server->difficulty;
|
|
resp->data.play_client.joingame.max_players = (uint8_t) conn->server->max_players;
|
|
resp->data.play_client.joingame.level_type = conn->server->overworld->level_type;
|
|
resp->data.play_client.joingame.reduced_debug_info = 0; // TODO
|
|
if (packet_write(conn, resp) < 0) {
|
|
return 1;
|
|
}
|
|
resp->id = PKT_PLAY_CLIENT_PLUGINMESSAGE;
|
|
resp->data.play_client.pluginmessage.channel = "MC|Brand";
|
|
resp->data.play_client.pluginmessage.data = pmalloc(pool, 16);
|
|
size_t str_length_length = (size_t) writeVarInt(5, (unsigned char*) resp->data.play_client.pluginmessage.data);
|
|
memcpy(resp->data.play_client.pluginmessage.data + str_length_length, "Basin", 5);
|
|
resp->data.play_client.pluginmessage.data_size = str_length_length + 5;
|
|
if (packet_write(conn, resp) < 0) {
|
|
return 1;
|
|
}
|
|
resp->id = PKT_PLAY_CLIENT_SERVERDIFFICULTY;
|
|
resp->data.play_client.serverdifficulty.difficulty = (uint8_t) conn->server->difficulty;
|
|
if (packet_write(conn, resp) < 0) {
|
|
return 1;
|
|
}
|
|
resp->id = PKT_PLAY_CLIENT_SPAWNPOSITION;
|
|
memcpy(&resp->data.play_client.spawnposition.location, &conn->server->overworld->spawnpos, sizeof(struct encpos));
|
|
if (packet_write(conn, resp) < 0) {
|
|
return 1;
|
|
}
|
|
resp->id = PKT_PLAY_CLIENT_PLAYERABILITIES;
|
|
resp->data.play_client.playerabilities.flags = 0; // TODO: allows flying, remove
|
|
resp->data.play_client.playerabilities.flying_speed = 0.05;
|
|
resp->data.play_client.playerabilities.field_of_view_modifier = .1;
|
|
if (packet_write(conn, resp) < 0) {
|
|
return 1;
|
|
}
|
|
resp->id = PKT_PLAY_CLIENT_PLAYERPOSITIONANDLOOK;
|
|
resp->data.play_client.playerpositionandlook.x = ep->x;
|
|
resp->data.play_client.playerpositionandlook.y = ep->y;
|
|
resp->data.play_client.playerpositionandlook.z = ep->z;
|
|
resp->data.play_client.playerpositionandlook.yaw = ep->yaw;
|
|
resp->data.play_client.playerpositionandlook.pitch = ep->pitch;
|
|
resp->data.play_client.playerpositionandlook.flags = 0x0;
|
|
resp->data.play_client.playerpositionandlook.teleport_id = 0;
|
|
if (packet_write(conn, resp) < 0) {
|
|
return 1;
|
|
}
|
|
netmgr_trigger_write(conn->managed_conn);
|
|
resp->id = PKT_PLAY_CLIENT_PLAYERLISTITEM;
|
|
pthread_rwlock_rdlock(&conn->server->players_by_entity_id->rwlock);
|
|
resp->data.play_client.playerlistitem.action_id = 0;
|
|
resp->data.play_client.playerlistitem.number_of_players = (int32_t) (conn->server->players_by_entity_id->entry_count + 1);
|
|
resp->data.play_client.playerlistitem.players = pmalloc(pool, resp->data.play_client.playerlistitem.number_of_players * sizeof(struct listitem_player));
|
|
size_t players_written = 0;
|
|
ITER_MAP(conn->server->players_by_entity_id) {
|
|
struct player* iter_player = (struct player*) value;
|
|
if (players_written < resp->data.play_client.playerlistitem.number_of_players) {
|
|
memcpy(&resp->data.play_client.playerlistitem.players[players_written].uuid, &iter_player->uuid, sizeof(struct uuid));
|
|
resp->data.play_client.playerlistitem.players[players_written].action.addplayer.name = iter_player->name;
|
|
resp->data.play_client.playerlistitem.players[players_written].action.addplayer.number_of_properties = 0;
|
|
resp->data.play_client.playerlistitem.players[players_written].action.addplayer.properties = NULL;
|
|
resp->data.play_client.playerlistitem.players[players_written].action.addplayer.gamemode = iter_player->gamemode;
|
|
resp->data.play_client.playerlistitem.players[players_written].action.addplayer.ping = 0; // TODO
|
|
resp->data.play_client.playerlistitem.players[players_written].action.addplayer.has_display_name = 0;
|
|
resp->data.play_client.playerlistitem.players[players_written].action.addplayer.display_name = NULL;
|
|
players_written++;
|
|
}
|
|
if (player == iter_player) continue;
|
|
struct packet* pkt = packet_new(mempool_new(), PKT_PLAY_CLIENT_PLAYERLISTITEM);
|
|
pkt->data.play_client.playerlistitem.action_id = 0;
|
|
pkt->data.play_client.playerlistitem.number_of_players = 1;
|
|
pkt->data.play_client.playerlistitem.players = pmalloc(pkt->pool, sizeof(struct listitem_player));
|
|
memcpy(&pkt->data.play_client.playerlistitem.players->uuid, &player->uuid, sizeof(struct uuid));
|
|
pkt->data.play_client.playerlistitem.players->action.addplayer.name = str_dup(player->name, 0, pkt->pool);
|
|
pkt->data.play_client.playerlistitem.players->action.addplayer.number_of_properties = 0;
|
|
pkt->data.play_client.playerlistitem.players->action.addplayer.properties = NULL;
|
|
pkt->data.play_client.playerlistitem.players->action.addplayer.gamemode = player->gamemode;
|
|
pkt->data.play_client.playerlistitem.players->action.addplayer.ping = 0; // TODO
|
|
pkt->data.play_client.playerlistitem.players->action.addplayer.has_display_name = 0;
|
|
pkt->data.play_client.playerlistitem.players->action.addplayer.display_name = NULL;
|
|
queue_push(iter_player->outgoing_packets, pkt);
|
|
connection_flush(iter_player);
|
|
ITER_MAP_END();
|
|
}
|
|
pthread_rwlock_unlock(&conn->server->players_by_entity_id->rwlock);
|
|
memcpy(&resp->data.play_client.playerlistitem.players[players_written].uuid, &player->uuid, sizeof(struct uuid));
|
|
resp->data.play_client.playerlistitem.players[players_written].action.addplayer.name = player->name;
|
|
resp->data.play_client.playerlistitem.players[players_written].action.addplayer.number_of_properties = 0;
|
|
resp->data.play_client.playerlistitem.players[players_written].action.addplayer.properties = NULL;
|
|
resp->data.play_client.playerlistitem.players[players_written].action.addplayer.gamemode = player->gamemode;
|
|
resp->data.play_client.playerlistitem.players[players_written].action.addplayer.ping = 0; // TODO
|
|
resp->data.play_client.playerlistitem.players[players_written].action.addplayer.has_display_name = 0;
|
|
resp->data.play_client.playerlistitem.players[players_written].action.addplayer.display_name = NULL;
|
|
players_written++;
|
|
if (packet_write(conn, resp) < 0) {
|
|
return 1;
|
|
}
|
|
resp->id = PKT_PLAY_CLIENT_TIMEUPDATE;
|
|
resp->data.play_client.timeupdate.time_of_day = conn->server->overworld->time;
|
|
resp->data.play_client.timeupdate.world_age = conn->server->overworld->age;
|
|
if (packet_write(conn, resp) < 0) {
|
|
return 1;
|
|
}
|
|
netmgr_trigger_write(conn->managed_conn);
|
|
add_collection(playersToLoad, player);
|
|
broadcastf("yellow", "%s has joined the server!", player->name);
|
|
const char* ip_string = NULL;
|
|
char tip[48];
|
|
if (conn->addr.in6.sin6_family == AF_INET) {
|
|
ip_string = inet_ntop(AF_INET, &conn->addr.in.sin_addr, tip, 48);
|
|
} else if (conn->addr.in6.sin6_family == AF_INET6) {
|
|
if (memseq((unsigned char*) &conn->addr.in6.sin6_addr, 10, 0) && memseq((unsigned char*) &conn->addr.in6.sin6_addr + 10, 2, 0xff)) {
|
|
ip_string = inet_ntop(AF_INET, ((unsigned char*) &conn->addr.in6.sin6_addr) + 12, tip, 48);
|
|
} else ip_string = inet_ntop(AF_INET6, &conn->addr.in6.sin6_addr, tip, 48);
|
|
} else {
|
|
ip_string = "UNKNOWN";
|
|
}
|
|
printf("Player '%s' has joined with IP '%s'\n", player->name, ip_string);
|
|
pfree(pool);
|
|
return 0;
|
|
}
|
|
|
|
int handle_packet_handshake(struct connection* conn, struct packet* packet) {
|
|
conn->host_ip = str_dup(packet->data.handshake_server.handshake.server_address, 0, conn->pool);
|
|
conn->host_port = packet->data.handshake_server.handshake.server_port;
|
|
conn->protocol_version = (uint32_t) packet->data.handshake_server.handshake.protocol_version;
|
|
if ((packet->data.handshake_server.handshake.protocol_version < MC_PROTOCOL_VERSION_MIN || packet->data.handshake_server.handshake.protocol_version > MC_PROTOCOL_VERSION_MAX) && packet->data.handshake_server.handshake.next_state != STATE_STATUS) return -2;
|
|
if (packet->data.handshake_server.handshake.next_state == STATE_STATUS) {
|
|
conn->protocol_state = STATE_STATUS;
|
|
} else if (packet->data.handshake_server.handshake.next_state == STATE_LOGIN) {
|
|
conn->protocol_state = STATE_LOGIN;
|
|
} else return 1;
|
|
return 0;
|
|
}
|
|
|
|
int handle_packet_status(struct connection* conn, struct packet* packet) {
|
|
if (packet->id == PKT_STATUS_SERVER_REQUEST) {
|
|
struct packet* resp = packet_new(packet->pool, PKT_STATUS_CLIENT_RESPONSE);
|
|
resp->data.status_client.response.json_response = pmalloc(packet->pool, 1000);
|
|
resp->data.status_client.response.json_response[999] = 0;
|
|
snprintf(resp->data.status_client.response.json_response, 999, "{\"version\":{\"name\":\"1.11.2\",\"protocol\":%i},\"players_by_entity_id\":{\"max\":%lu,\"online\":%lu},\"description\":{\"text\":\"%s\"}}", MC_PROTOCOL_VERSION_MIN, conn->server->max_players, conn->server->players_by_entity_id->entry_count, conn->server->motd);
|
|
if (packet_write(conn, resp) < 0) return 1;
|
|
} else if (packet->id == PKT_STATUS_SERVER_PING) {
|
|
struct packet* resp = packet_new(packet->pool, PKT_STATUS_CLIENT_PONG);
|
|
if (packet_write(conn, resp) < 0) return 1;
|
|
conn->disconnect = 1;
|
|
} else return 1;
|
|
return 0;
|
|
}
|
|
|
|
void encrypt_free(EVP_CIPHER_CTX* ctx) {
|
|
unsigned char final[256];
|
|
int throw_away = 0;
|
|
EVP_EncryptFinal_ex(ctx, final, &throw_away);
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
}
|
|
|
|
void decrypt_free(EVP_CIPHER_CTX* ctx) {
|
|
unsigned char final[256];
|
|
int throw_away = 0;
|
|
EVP_DecryptFinal_ex(ctx, final, &throw_away);
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
}
|
|
|
|
int handle_encryption_response(struct connection* conn, struct packet* packet) {
|
|
if (conn->verifyToken == 0 || packet->data.login_server.encryptionresponse.shared_secret_length > 162 || packet->data.login_server.encryptionresponse.verify_token_length > 162) {
|
|
return 1;
|
|
}
|
|
unsigned char shared_secret[162];
|
|
int secret_length = RSA_private_decrypt(packet->data.login_server.encryptionresponse.shared_secret_length, packet->data.login_server.encryptionresponse.shared_secret, shared_secret, public_rsa, RSA_PKCS1_PADDING);
|
|
if (secret_length != 16) {
|
|
return 1;
|
|
}
|
|
unsigned char verify_token[162];
|
|
int verify_token_length = RSA_private_decrypt(packet->data.login_server.encryptionresponse.verify_token_length, packet->data.login_server.encryptionresponse.verify_token, verify_token, public_rsa, RSA_PKCS1_PADDING);
|
|
if (verify_token_length != 4) {
|
|
return 1;
|
|
}
|
|
uint32_t verify_token_int = *((uint32_t*) verify_token);
|
|
if (verify_token_int != conn->verifyToken) {
|
|
return 1;
|
|
}
|
|
memcpy(conn->shared_secret, shared_secret, 16);
|
|
uint8_t public_key[162];
|
|
memcpy(public_key, public_rsa_publickey, 162);
|
|
uint8_t hash[20];
|
|
SHA_CTX context;
|
|
SHA1_Init(&context);
|
|
SHA1_Update(&context, shared_secret, 16);
|
|
SHA1_Update(&context, public_key, 162);
|
|
SHA1_Final(hash, &context);
|
|
int is_signed = 0;
|
|
if (hash[0] & 0x80) {
|
|
is_signed = 1;
|
|
for (int i = 0; i < 20; i++) {
|
|
hash[i] = ~hash[i];
|
|
}
|
|
(hash[19])++;
|
|
}
|
|
char hex_hash[32];
|
|
char* hex_hash_signed = hex_hash + 1;
|
|
for (int i = 0; i < 20; i++) {
|
|
snprintf(hex_hash + 1 + (i * 2), 3, "%02X", hash[i]);
|
|
}
|
|
for (int i = 1; i < 41; i++) {
|
|
if (hex_hash[i] == '0') hex_hash_signed++;
|
|
else break;
|
|
}
|
|
hex_hash_signed--;
|
|
struct mempool* ssl_pool = mempool_new();
|
|
if (is_signed) hex_hash_signed[0] = '-';
|
|
else hex_hash_signed++;
|
|
struct sockaddr_in sin;
|
|
//TODO: use getaddrinfo
|
|
struct hostent *host = gethostbyname("sessionserver.mojang.com");
|
|
if (host != NULL) {
|
|
struct in_addr **adl = (struct in_addr **) host->h_addr_list;
|
|
if (adl[0] != NULL) {
|
|
sin.sin_addr.s_addr = adl[0]->s_addr;
|
|
} else goto ssl_error;
|
|
} else goto ssl_error;
|
|
sin.sin_port = htons(443);
|
|
sin.sin_family = AF_INET;
|
|
int session_tls_fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
SSL* ssl = NULL;
|
|
if (session_tls_fd < 0) goto ssl_error;
|
|
phook(ssl_pool, close_hook, (void*) session_tls_fd);
|
|
if (connect(session_tls_fd, (struct sockaddr*) &sin, sizeof(struct sockaddr_in))) goto ssl_error;
|
|
ssl = SSL_new(mojang_ctx);
|
|
phook(ssl_pool, (void (*)(void*)) SSL_shutdown, ssl);
|
|
phook(ssl_pool, (void (*)(void*)) SSL_free, ssl);
|
|
SSL_set_connect_state(ssl);
|
|
SSL_set_fd(ssl, session_tls_fd);
|
|
if (SSL_connect(ssl) != 1) goto ssl_error;
|
|
char write_buf[4096];
|
|
int write_length = snprintf(write_buf, 1024, "GET /session/minecraft/hasJoined?username=%s&serverId=%s HTTP/1.1\r\nHost: sessionserver.mojang.com\r\nUser-Agent: Basin " VERSION "\r\nConnection: close\r\n\r\n", conn->online_username, hex_hash_signed);
|
|
int written = 0;
|
|
while (written < write_length) {
|
|
int r = SSL_write(ssl, write_buf, write_length);
|
|
if (r <= 0) goto ssl_error;
|
|
else written += r;
|
|
}
|
|
int read = 0;
|
|
int r = 0;
|
|
while ((r = SSL_read(ssl, write_buf + read, 4095 - read)) > 0) { // we reuse write_buf as read_buf here
|
|
read += r;
|
|
}
|
|
write_buf[read] = 0;
|
|
char* data = strstr(write_buf, "\r\n\r\n");
|
|
if (data == NULL) goto ssl_error;
|
|
data += 4;
|
|
struct json_object* json = json_make_object(ssl_pool, NULL, 0);
|
|
json_parse(ssl_pool, &json, data);
|
|
struct json_object* tmp = json_get(&json, "id");
|
|
if (tmp == NULL || tmp->type != JSON_STRING) {
|
|
goto ssl_error;
|
|
}
|
|
char* id = str_trim(tmp->data.string);
|
|
tmp = json_get(json, "name");
|
|
if (tmp == NULL || tmp->type != JSON_STRING) {
|
|
goto ssl_error;
|
|
}
|
|
char* name = str_trim(tmp->data.string);
|
|
size_t sl = strlen(name);
|
|
if (sl < 2 || sl > 16) {
|
|
goto ssl_error;
|
|
}
|
|
pthread_rwlock_rdlock(&conn->server->players_by_entity_id->rwlock);
|
|
ITER_MAP(conn->server->players_by_entity_id) {
|
|
struct player* player = (struct player*) value;
|
|
if (str_eq(name, player->name)) {
|
|
player_kick(player, "You have logged in from another location!");
|
|
goto post_map_iteration;
|
|
}
|
|
ITER_MAP_END();
|
|
}
|
|
post_map_iteration:;
|
|
pthread_rwlock_unlock(&conn->server->players_by_entity_id->rwlock);
|
|
conn->aes_ctx_enc = EVP_CIPHER_CTX_new();
|
|
if (conn->aes_ctx_enc == NULL) {
|
|
goto ssl_error;
|
|
}
|
|
if (EVP_EncryptInit_ex(conn->aes_ctx_enc, EVP_aes_128_cfb8(), NULL, conn->shared_secret, conn->shared_secret) != 1) {
|
|
goto ssl_error;
|
|
}
|
|
if (conn->aes_ctx_enc != NULL) {
|
|
phook(conn->pool, (void (*)(void*)) encrypt_free, conn->aes_ctx_enc);
|
|
}
|
|
|
|
conn->aes_ctx_dec = EVP_CIPHER_CTX_new();
|
|
if (conn->aes_ctx_dec == NULL) {
|
|
goto ssl_error;
|
|
}
|
|
if (EVP_DecryptInit_ex(conn->aes_ctx_dec, EVP_aes_128_cfb8(), NULL, conn->shared_secret, conn->shared_secret) != 1) {
|
|
goto ssl_error;
|
|
}
|
|
if (conn->aes_ctx_dec != NULL) {
|
|
phook(conn->pool, (void (*)(void*)) decrypt_free, conn->aes_ctx_dec);
|
|
}
|
|
|
|
if (work_joinServer(conn, name, id)) {
|
|
pfree(ssl_pool);
|
|
return 1;
|
|
}
|
|
pfree(ssl_pool);
|
|
return 0;
|
|
ssl_error: ;
|
|
pfree(ssl_pool);
|
|
struct packet* resp = packet_new(packet->pool, PKT_LOGIN_CLIENT_DISCONNECT);
|
|
resp->data.login_client.disconnect.reason = "{\"text\": \"There was an unresolvable issue with the Mojang sessionserver! Please try again.\"}";
|
|
conn->disconnect = 1;
|
|
packet_write(conn, resp); // failure is ignored, we disconnect regardless
|
|
return 1;
|
|
}
|
|
|
|
int handle_login_start(struct connection* conn, struct packet* packet) {
|
|
if (conn->server->online_mode) {
|
|
if (conn->verifyToken) {
|
|
return 1;
|
|
}
|
|
conn->online_username = pxfer(packet->pool, conn->pool, packet->data.login_server.loginstart.name);
|
|
struct packet* resp = packet_new(packet->pool, PKT_LOGIN_CLIENT_ENCRYPTIONREQUEST);
|
|
resp->data.login_client.encryptionrequest.server_id = "";
|
|
resp->data.login_client.encryptionrequest.public_key = public_rsa_publickey;
|
|
resp->data.login_client.encryptionrequest.public_key_length = 162;
|
|
conn->verifyToken = rand(); // TODO: better RNG
|
|
if (conn->verifyToken == 0) conn->verifyToken = 1;
|
|
resp->data.login_client.encryptionrequest.verify_token = (uint8_t*) &conn->verifyToken;
|
|
resp->data.login_client.encryptionrequest.verify_token_length = 4;
|
|
if (packet_write(conn, resp) < 0) {
|
|
return 1;
|
|
}
|
|
} else {
|
|
int bad_name = 0;
|
|
char* name_trimmed = str_trim(packet->data.login_server.loginstart.name);
|
|
size_t trimmed_length = strlen(name_trimmed);
|
|
if (trimmed_length > 16 || trimmed_length < 2) bad_name = 2;
|
|
if (!bad_name) {
|
|
pthread_rwlock_rdlock(&conn->server->players_by_entity_id->rwlock);
|
|
ITER_MAP(conn->server->players_by_entity_id) { // TODO: name mapping
|
|
struct player* player = (struct player*) value;
|
|
if (str_eq(name_trimmed, player->name)) {
|
|
bad_name = 1;
|
|
goto post_check;
|
|
}
|
|
ITER_MAP_END();
|
|
}
|
|
post_check: ;
|
|
pthread_rwlock_unlock(&conn->server->players_by_entity_id->rwlock);
|
|
}
|
|
if (bad_name) {
|
|
struct packet* resp = packet_new(packet->pool, PKT_LOGIN_CLIENT_DISCONNECT);
|
|
resp->id = PKT_LOGIN_CLIENT_DISCONNECT;
|
|
resp->data.login_client.disconnect.reason = bad_name == 2 ? "{\"text\": \"Invalid name!\"}" : "{\"text\", \"You are already in the server!\"}";
|
|
if (packet_write(conn, resp) < 0) {
|
|
return 1;
|
|
}
|
|
conn->disconnect = 1;
|
|
return 0;
|
|
}
|
|
if (work_joinServer(conn, name_trimmed, NULL)) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int handle_packet_login(struct connection* conn, struct packet* packet) {
|
|
if (packet->id == PKT_LOGIN_SERVER_ENCRYPTIONRESPONSE) {
|
|
return handle_encryption_response(conn, packet);
|
|
} else if (packet->id == PKT_LOGIN_SERVER_LOGINSTART) {
|
|
return handle_login_start(conn, packet);
|
|
} else return 1;
|
|
return 0;
|
|
}
|