diff --git a/basin/Debug/main.cfg b/basin/Debug/main.cfg index 82de12c..2b3548d 100644 --- a/basin/Debug/main.cfg +++ b/basin/Debug/main.cfg @@ -12,7 +12,7 @@ threads = 30 max-players = 20 motd = A Minecraft Server level-seed = -online-mode = false +online-mode = true white-list = false spawn-animals = true spawn-npcs = true diff --git a/basin/src/accept.c b/basin/src/accept.c index a7ab75c..ce159a1 100644 --- a/basin/src/accept.c +++ b/basin/src/accept.c @@ -42,11 +42,16 @@ void run_accept(struct accept_param* param) { c->writeBuffer_capacity = 0; c->comp = -1; c->state = 0; + c->onll_username = NULL; c->disconnect = 0; c->host_ip = NULL; c->player = NULL; c->host_port = 0; c->protocolVersion = 0; + c->verifyToken = 0; + c->aes_ctx_enc = NULL; + c->aes_ctx_dec = NULL; + memset(c->sharedSecret, 0, 16); if (poll(&spfd, 1, -1) < 0) { printf("Error while polling server: %s\n", strerror(errno)); xfree(c); diff --git a/basin/src/accept.h b/basin/src/accept.h index 9d0f14c..462ffcf 100644 --- a/basin/src/accept.h +++ b/basin/src/accept.h @@ -13,6 +13,8 @@ #include #include "work.h" #include +#include +#include struct accept_param { int server_fd; @@ -41,6 +43,11 @@ struct conn { struct player* player; int disconnect; uint32_t protocolVersion; + uint32_t verifyToken; + char* onll_username; + uint8_t sharedSecret[16]; + EVP_CIPHER_CTX* aes_ctx_enc; + EVP_CIPHER_CTX* aes_ctx_dec; }; void run_accept(struct accept_param* param); diff --git a/basin/src/entity.h b/basin/src/entity.h index 88536be..491a86b 100644 --- a/basin/src/entity.h +++ b/basin/src/entity.h @@ -13,6 +13,94 @@ #include #include "world.h" +/* + + #define ENT_PLAYER 0 + #define ENT_ITEM 1 + #define ENT_XP_ORB 2 + #define ENT_AREA_EFFECT_CLOUD 3 + #define ENT_ELDER_GUARDIAN 4 + #define ENT_WITHER_SKELETON 5 + #define ENT_STRAY 6 + #define ENT_EGG 7 + #define ENT_LEASH_KNOT 8 + #define ENT_PAINTING 9 + #define ENT_ARROW 10 + #define ENT_SNOWBALL 11 + #define ENT_FIREBALL 12 + #define ENT_SMALL_FIREBALL 13 + #define ENT_ENDER_PEARL 14 + #define ENT_EYE_OF_ENDER_SIGNAL 15 + #define ENT_POTION 16 + #define ENT_XP_BOTTLE 17 + #define ENT_ITEM_FRAME 18 + #define ENT_WITHER_SKULL 19 + #define ENT_TNT 20 + #define ENT_FALLING_BLOCK 21 + #define ENT_FIREWORKS_ROCKET 22 + #define ENT_HUSK 23 + #define ENT_SPECTRAL_ARROW 24 + #define ENT_SHULKER_BULLET 25 + #define ENT_DRAGON_FIREBALL 26 + #define ENT_ZOMBIE_VILLAGER 27 + #define ENT_SKELETON_HORSE 28 + #define ENT_ZOMBIE_HORSE 29 + #define ENT_ARMOR_STAND 30 + #define ENT_DONKEY 31 + #define ENT_MULE 32 + #define ENT_EVOCATION_FANGS 33 + #define ENT_EVOCATION_ILLAGER 34 + #define ENT_VEX 35 + #define ENT_VINDICATION_ILLAGER 36 + #define ENT_COMMANDBLOCK_MINECART 40 + #define ENT_BOAT 41 + #define ENT_MINECART 42 + #define ENT_CHEST_MINECART 43 + #define ENT_FURNACE_MINECART 44 + #define ENT_TNT_MINECART 45 + #define ENT_HOPPER_MINECART 46 + #define ENT_SPAWNER_MINECART 47 + #define ENT_CREEPER 50 + #define ENT_SKELETON 51 + #define ENT_SPIDER 52 + #define ENT_GIANT 53 + #define ENT_ZOMBIE 54 + #define ENT_SLIME 55 + #define ENT_GHAST 56 + #define ENT_ZOMBIE_PIGMAN 57 + #define ENT_ENDERMAN 58 + #define ENT_CAVE_SPIDER 59 + #define ENT_SILVERFISH 60 + #define ENT_BLAZE 61 + #define ENT_MAGMA_CUBE 62 + #define ENT_ENDER_DRAGON 63 + #define ENT_WITHER 64 + #define ENT_BAT 65 + #define ENT_WITCH 66 + #define ENT_ENDERMITE 67 + #define ENT_GUARDIAN 68 + #define ENT_SHULKER 69 + #define ENT_PIG 90 + #define ENT_SHEEP 91 + #define ENT_COW 92 + #define ENT_CHICKEN 93 + #define ENT_SQUID 94 + #define ENT_WOLF 95 + #define ENT_MOOSHROOM 96 + #define ENT_SNOWMAN 97 + #define ENT_OCELOT 98 + #define ENT_VILLAGER_GOLEM 99 + #define ENT_HORSE 100 + #define ENT_RABBIT 101 + #define ENT_POLAR_BEAR 102 + #define ENT_LLAMA 103 + #define ENT_LLAMA_SPIT 104 + #define ENT_VILLAGER 120 + #define ENT_ENDER_CRYSTAL 200 + + + */ + #define ENT_UNDEFINED 1 #define ENT_PLAYER 2 #define ENT_CREEPER 3 diff --git a/basin/src/main.c b/basin/src/main.c index 86158fc..518c95d 100644 --- a/basin/src/main.c +++ b/basin/src/main.c @@ -42,6 +42,7 @@ #include "queue.h" #include "profile.h" #include "plugin.h" +#include void main_tick() { pthread_cond_broadcast (&glob_tick_cond); @@ -116,7 +117,9 @@ int main(int argc, char* argv[]) { signal(SIGPIPE, SIG_IGN); struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); - srand(ts.tv_sec * 1000000 + ts.tv_nsec / 1000); + size_t us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000; + srand(us); + RAND_seed(&us, sizeof(size_t)); printf("Loading %s %s\n", DAEMON_NAME, VERSION); #ifdef DEBUG printf("Running in Debug mode!\n"); @@ -204,6 +207,8 @@ int main(int argc, char* argv[]) { printf("Commands Initialized\n"); init_plugins(); printf("Plugins Initialized\n"); + init_encryption(); + printf("Encryption Initialized\n"); for (int i = 0; i < servsl; i++) { struct cnode* serv = servs[i]; const char* bind_mode = getConfigValue(serv, "bind-mode"); diff --git a/basin/src/network.c b/basin/src/network.c index efff3e0..9d0c68d 100644 --- a/basin/src/network.c +++ b/basin/src/network.c @@ -23,6 +23,31 @@ #include #include #include "network.h" +#include +#include +#include + +void init_encryption() { + public_rsa = RSA_new(); + BIGNUM* pn = BN_new(); + if (BN_set_word(pn, RSA_F4) != 1) { + printf("Error generating RSA BIGNUM!\n"); + return; + } + if (RSA_generate_key_ex(public_rsa, 1024, pn, NULL) != 1) { + printf("Error generating RSA keypair!\n"); + return; + } + size_t s = i2d_RSA_PUBKEY(public_rsa, &public_rsa_publickey); + if (s != 162) { + printf("[WARNING] RSA Public Key size was not 162! Size = %lu\n", s); + } + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + const SSL_METHOD* method = SSLv23_client_method(); + mojang_ctx = SSL_CTX_new(method); +} void swapEndian(void* dou, size_t ss) { uint8_t* pxs = (uint8_t*) dou; diff --git a/basin/src/network.h b/basin/src/network.h index cb1ddac..5a83ca9 100644 --- a/basin/src/network.h +++ b/basin/src/network.h @@ -10,6 +10,7 @@ #include "accept.h" #include "inventory.h" +#include struct packet; @@ -29,6 +30,12 @@ struct entity_metadata { size_t metadata_size; }; +RSA* public_rsa; +uint8_t* public_rsa_publickey; +SSL_CTX* mojang_ctx; + +void init_encryption(); + void swapEndian(void* dou, size_t ss); int getVarIntSize(int32_t input); diff --git a/basin/src/packet.c b/basin/src/packet.c index dd15c45..994962b 100644 --- a/basin/src/packet.c +++ b/basin/src/packet.c @@ -35,24 +35,42 @@ ssize_t readPacket(struct conn* conn, unsigned char* buf, size_t buflen, struct void* pktbuf = buf; int32_t pktlen = buflen; int tf = 0; + if (conn->aes_ctx_dec != NULL) { + if (EVP_DecryptInit_ex(conn->aes_ctx_dec, EVP_aes_128_cfb8(), NULL, conn->sharedSecret, conn->sharedSecret) != 1) return -1; + int csl = pktlen + 32; // 16 extra just in case + void* edata = xmalloc(csl); + if (EVP_DecryptUpdate(conn->aes_ctx_dec, edata, &csl, pktbuf, pktlen) != 1) { + xfree(edata); + goto rer; + } + int csl2 = 0; + if (EVP_DecryptFinal_ex(conn->aes_ctx_dec, edata + csl, &csl2) != 1) { + xfree(edata); + goto rer; + } + csl += csl2; + pktbuf = edata; + pktlen = csl; + tf = 1; + } if (conn->comp >= 0) { int32_t dl = 0; int rx = readVarInt(&dl, pktbuf, pktlen); - if (rx == 0) return -1; + if (rx == 0) goto rer; pktlen -= rx; pktbuf += rx; if (dl > 0 && pktlen > 0) { pktlen = dl; - void* decmpbuf = malloc(dl); + void* decmpbuf = xmalloc(dl); z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; int dr = 0; if ((dr = inflateInit(&strm)) != Z_OK) { - free(decmpbuf); + xfree(decmpbuf); printf("Compression initialization error!\n"); - return -1; + goto rer; } strm.avail_in = pktlen; strm.next_in = pktbuf; @@ -61,9 +79,9 @@ ssize_t readPacket(struct conn* conn, unsigned char* buf, size_t buflen, struct do { dr = inflate(&strm, Z_FINISH); if (dr == Z_STREAM_ERROR) { - free(decmpbuf); + xfree(decmpbuf); printf("Compression Read Error\n"); - return -1; + goto rer; } strm.avail_out = pktlen - strm.total_out; strm.next_out = decmpbuf + strm.total_out; @@ -543,6 +561,7 @@ ssize_t readPacket(struct conn* conn, unsigned char* buf, size_t buflen, struct } goto rx; rer: ; + if (tf) xfree(pktbuf); return -1; rx: ; if (tf) xfree(pktbuf); @@ -1920,14 +1939,14 @@ ssize_t writePacket(struct conn* conn, struct packet* packet) { pi += writeVarInt(packet->data.login_client.encryptionrequest.public_key_length, pktbuf + pi); //public_key ENS(packet->data.login_client.encryptionrequest.public_key_length) - memcpy(pktbuf + pi, &packet->data.login_client.encryptionrequest.public_key, packet->data.login_client.encryptionrequest.public_key_length); + memcpy(pktbuf + pi, packet->data.login_client.encryptionrequest.public_key, packet->data.login_client.encryptionrequest.public_key_length); pi += packet->data.login_client.encryptionrequest.public_key_length; //verify_token_length ENS(4) pi += writeVarInt(packet->data.login_client.encryptionrequest.verify_token_length, pktbuf + pi); //verify_token ENS(packet->data.login_client.encryptionrequest.verify_token_length) - memcpy(pktbuf + pi, &packet->data.login_client.encryptionrequest.verify_token, packet->data.login_client.encryptionrequest.verify_token_length); + memcpy(pktbuf + pi, packet->data.login_client.encryptionrequest.verify_token, packet->data.login_client.encryptionrequest.verify_token_length); pi += packet->data.login_client.encryptionrequest.verify_token_length; } else if (id == PKT_LOGIN_CLIENT_LOGINSUCCESS) { //uuid @@ -1962,27 +1981,26 @@ ssize_t writePacket(struct conn* conn, struct packet* packet) { } strm.avail_in = pi; strm.next_in = pktbuf; - void* cdata = malloc(16384); + size_t cc = pi + 32; + void* cdata = xmalloc(cc); size_t ts = 0; - size_t cc = 16384; strm.avail_out = cc - ts; strm.next_out = cdata + ts; do { dr = deflate(&strm, Z_FINISH); ts = strm.total_out; if (ts >= cc) { - cc = ts + 16384; - cdata = realloc(cdata, cc); + cc = ts + 1024; + cdata = xrealloc(cdata, cc); } if (dr == Z_STREAM_ERROR) { - free(cdata); + xfree(cdata); return -1; } strm.avail_out = cc - ts; strm.next_out = cdata + ts; } while (strm.avail_out == 0); deflateEnd(&strm); - cdata = xrealloc(cdata, ts); // shrink preps += writeVarInt(ts + getVarIntSize(pi), prep + preps); preps += writeVarInt(pi, prep + preps); wrt = cdata; @@ -1997,7 +2015,63 @@ ssize_t writePacket(struct conn* conn, struct packet* packet) { wrt = pktbuf; wrt_s = pi; } -//TODO: encrypt + if (conn->aes_ctx_enc != NULL) { + if (EVP_EncryptInit_ex(conn->aes_ctx_enc, EVP_aes_128_cfb8(), NULL, conn->sharedSecret, conn->sharedSecret) != 1) { + wrt_s = -1; + goto rret; + } + int csl = wrt_s + 32; // 16 extra just in case + void* edata = xcalloc(csl); + if (EVP_EncryptUpdate(conn->aes_ctx_enc, edata, &csl, wrt, wrt_s) != 1) { + xfree(edata); + wrt_s = -1; + goto rret; + } + int csl2 = 0; + if (EVP_EncryptFinal_ex(conn->aes_ctx_enc, edata + csl, &csl2) != 1) { + xfree(edata); + wrt_s = -1; + goto rret; + } + csl += csl2; + void* owrt = wrt; + size_t owrts = wrt_s; + //xfree(wrt); + if (!frp) pktbuf = NULL; + wrt = edata; + wrt_s = csl; + // + if (EVP_DecryptInit_ex(conn->aes_ctx_dec, EVP_aes_128_cfb8(), NULL, conn->sharedSecret, conn->sharedSecret) != 1) return -1; + csl = wrt_s + 32; // 16 extra just in case + edata = xcalloc(csl); + if (EVP_DecryptUpdate(conn->aes_ctx_dec, edata, &csl, wrt, wrt_s) != 1) { + xfree(edata); + wrt_s = -1; + goto rret; + } + csl2 = 0; + if (EVP_DecryptFinal_ex(conn->aes_ctx_dec, edata + csl, &csl2) != 1) { + xfree(edata); + wrt_s = -1; + goto rret; + } + csl += csl2; + if (csl != owrts) { + printf("length mismatch! %i != %i\n", csl, owrts); + } else { + printf("original data: "); + for (int i = 0; i < owrts; i++) { + printf("%02X", ((uint8_t*) owrt)[i]); + } + printf("\ndec/enc data: "); + for (int i = 0; i < csl; i++) { + printf("%02X", ((uint8_t*) edata)[i]); + } + printf("\n%s\n", memeq(owrt, owrts, edata, csl) ? "equal!" : "not equal!"); + } + xfree(owrt); + xfree(edata); + } if (conn->writeBuffer == NULL) { conn->writeBuffer = xmalloc(preps); memcpy(conn->writeBuffer, prep, preps); @@ -2017,6 +2091,7 @@ ssize_t writePacket(struct conn* conn, struct packet* packet) { } memcpy(conn->writeBuffer + conn->writeBuffer_size, wrt, wrt_s); conn->writeBuffer_size += wrt_s; + rret: ; if (frp) xfree(wrt); xfree(pktbuf); return wrt_s; diff --git a/basin/src/packet.h b/basin/src/packet.h index a09db70..3f64950 100644 --- a/basin/src/packet.h +++ b/basin/src/packet.h @@ -1082,9 +1082,9 @@ struct pkt_login_client_disconnect { struct pkt_login_client_encryptionrequest { char* server_id; int32_t public_key_length; - int8_t* public_key; + uint8_t* public_key; int32_t verify_token_length; - int8_t* verify_token; + uint8_t* verify_token; }; #define PKT_LOGIN_CLIENT_LOGINSUCCESS 2 @@ -1110,9 +1110,9 @@ struct pkt_login_server_loginstart { struct pkt_login_server_encryptionresponse { int32_t shared_secret_length; - int8_t* shared_secret; + uint8_t* shared_secret; int32_t verify_token_length; - int8_t* verify_token; + uint8_t* verify_token; }; union pkt_handshake_client { diff --git a/basin/src/work.c b/basin/src/work.c index 03b164f..37a92ad 100644 --- a/basin/src/work.c +++ b/basin/src/work.c @@ -32,6 +32,11 @@ #include "item.h" #include #include +#include +#include +#include +#include "version.h" +#include "json.h" void closeConn(struct work_param* param, struct conn* conn) { close(conn->fd); @@ -43,12 +48,160 @@ void closeConn(struct work_param* param, struct conn* conn) { conn->player->defunct = 1; conn->player->conn = NULL; } + if (conn->aes_ctx_enc != NULL) EVP_CIPHER_CTX_free(conn->aes_ctx_enc); + if (conn->aes_ctx_dec != NULL) EVP_CIPHER_CTX_free(conn->aes_ctx_dec); if (conn->host_ip != NULL) xfree(conn->host_ip); if (conn->readBuffer != NULL) xfree(conn->readBuffer); if (conn->writeBuffer != NULL) xfree(conn->writeBuffer); + if (conn->onll_username != NULL) xfree(conn->onll_username); xfree(conn); } +int work_joinServer(struct conn* conn, char* username, char* uuids) { + struct packet rep; + rep.id = PKT_LOGIN_CLIENT_LOGINSUCCESS; + rep.data.login_client.loginsuccess.username = username; + struct uuid uuid; + unsigned char* uuidx = (unsigned char*) &uuid; + if (uuids == NULL) { + if (!online_mode) return 1; + MD5_CTX context; + MD5_Init(&context); + MD5_Update(&context, username, strlen(username)); + MD5_Final(uuidx, &context); + } else { + if (strlen(uuids) != 32) return 1; + char ups[17]; + memcpy(ups, uuids, 16); + ups[16] = 0; + uuid.uuid1 = strtoll(ups, NULL, 16); + memcpy(ups, uuids + 16, 16); + uuid.uuid2 = strtoll(ups, NULL, 16); + } + rep.data.login_client.loginsuccess.uuid = xmalloc(38); + snprintf(rep.data.login_client.loginsuccess.uuid, 10, "%08X-", ((uint32_t*) uuidx)[0]); + snprintf(rep.data.login_client.loginsuccess.uuid + 9, 6, "%04X-", ((uint16_t*) uuidx)[2]); + snprintf(rep.data.login_client.loginsuccess.uuid + 14, 6, "%04X-", ((uint16_t*) uuidx)[3]); + snprintf(rep.data.login_client.loginsuccess.uuid + 19, 6, "%04X-", ((uint16_t*) uuidx)[4]); + snprintf(rep.data.login_client.loginsuccess.uuid + 24, 9, "%08X", ((uint32_t*) (uuidx + 4))[2]); + snprintf(rep.data.login_client.loginsuccess.uuid + 32, 5, "%04X", ((uint16_t*) uuidx)[7]); + if (writePacket(conn, &rep) < 0) return 1; + xfree(rep.data.login_client.loginsuccess.uuid); + conn->state = STATE_PLAY; + struct entity* ep = newEntity(nextEntityID++, (double) overworld->spawnpos.x + .5, (double) overworld->spawnpos.y, (double) overworld->spawnpos.z + .5, ENT_PLAYER, 0., 0.); + struct player* player = newPlayer(ep, xstrdup(rep.data.login_client.loginsuccess.username, 1), uuid, conn, 0); // TODO default gamemode + player->protocolVersion = conn->protocolVersion; + conn->player = player; + put_hashmap(players, player->entity->id, player); + rep.id = PKT_PLAY_CLIENT_JOINGAME; + rep.data.play_client.joingame.entity_id = ep->id; + rep.data.play_client.joingame.gamemode = player->gamemode; + rep.data.play_client.joingame.dimension = overworld->dimension; + rep.data.play_client.joingame.difficulty = difficulty; + rep.data.play_client.joingame.max_players = max_players; + rep.data.play_client.joingame.level_type = overworld->levelType; + rep.data.play_client.joingame.reduced_debug_info = 0; // TODO + if (writePacket(conn, &rep) < 0) return 1; + rep.id = PKT_PLAY_CLIENT_PLUGINMESSAGE; + rep.data.play_client.pluginmessage.channel = "MC|Brand"; + rep.data.play_client.pluginmessage.data = xmalloc(16); + int rx = writeVarInt(5, rep.data.play_client.pluginmessage.data); + memcpy(rep.data.play_client.pluginmessage.data + rx, "Basin", 5); + rep.data.play_client.pluginmessage.data_size = rx + 5; + if (writePacket(conn, &rep) < 0) return 1; + xfree(rep.data.play_client.pluginmessage.data); + rep.id = PKT_PLAY_CLIENT_SERVERDIFFICULTY; + rep.data.play_client.serverdifficulty.difficulty = difficulty; + if (writePacket(conn, &rep) < 0) return 1; + rep.id = PKT_PLAY_CLIENT_SPAWNPOSITION; + memcpy(&rep.data.play_client.spawnposition.location, &overworld->spawnpos, sizeof(struct encpos)); + if (writePacket(conn, &rep) < 0) return 1; + rep.id = PKT_PLAY_CLIENT_PLAYERABILITIES; + rep.data.play_client.playerabilities.flags = 0; // TODO: allows flying, remove + rep.data.play_client.playerabilities.flying_speed = 0.05; + rep.data.play_client.playerabilities.field_of_view_modifier = .1; + if (writePacket(conn, &rep) < 0) return 1; + rep.id = PKT_PLAY_CLIENT_PLAYERPOSITIONANDLOOK; + rep.data.play_client.playerpositionandlook.x = ep->x; + rep.data.play_client.playerpositionandlook.y = ep->y; + rep.data.play_client.playerpositionandlook.z = ep->z; + rep.data.play_client.playerpositionandlook.yaw = ep->yaw; + rep.data.play_client.playerpositionandlook.pitch = ep->pitch; + rep.data.play_client.playerpositionandlook.flags = 0x0; + rep.data.play_client.playerpositionandlook.teleport_id = 0; + if (writePacket(conn, &rep) < 0) return 1; + rep.id = PKT_PLAY_CLIENT_PLAYERLISTITEM; + pthread_rwlock_rdlock(&players->data_mutex); + rep.data.play_client.playerlistitem.action_id = 0; + rep.data.play_client.playerlistitem.number_of_players = players->entry_count + 1; + rep.data.play_client.playerlistitem.players = xmalloc(rep.data.play_client.playerlistitem.number_of_players * sizeof(struct listitem_player)); + size_t px = 0; + BEGIN_HASHMAP_ITERATION (players) + struct player* plx = (struct player*) value; + if (px < rep.data.play_client.playerlistitem.number_of_players) { + memcpy(&rep.data.play_client.playerlistitem.players[px].uuid, &plx->uuid, sizeof(struct uuid)); + rep.data.play_client.playerlistitem.players[px].action.addplayer.name = xstrdup(plx->name, 0); + rep.data.play_client.playerlistitem.players[px].action.addplayer.number_of_properties = 0; + rep.data.play_client.playerlistitem.players[px].action.addplayer.properties = NULL; + rep.data.play_client.playerlistitem.players[px].action.addplayer.gamemode = plx->gamemode; + rep.data.play_client.playerlistitem.players[px].action.addplayer.ping = 0; // TODO + rep.data.play_client.playerlistitem.players[px].action.addplayer.has_display_name = 0; + rep.data.play_client.playerlistitem.players[px].action.addplayer.display_name = NULL; + px++; + } + if (player == plx) continue; + struct packet* pkt = xmalloc(sizeof(struct packet)); + pkt->id = 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 = xmalloc(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 = xstrdup(player->name, 0); + 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; + add_queue(plx->outgoingPacket, pkt); + flush_outgoing(plx); + END_HASHMAP_ITERATION (players) + pthread_rwlock_unlock(&players->data_mutex); + memcpy(&rep.data.play_client.playerlistitem.players[px].uuid, &player->uuid, sizeof(struct uuid)); + rep.data.play_client.playerlistitem.players[px].action.addplayer.name = xstrdup(player->name, 0); + rep.data.play_client.playerlistitem.players[px].action.addplayer.number_of_properties = 0; + rep.data.play_client.playerlistitem.players[px].action.addplayer.properties = NULL; + rep.data.play_client.playerlistitem.players[px].action.addplayer.gamemode = player->gamemode; + rep.data.play_client.playerlistitem.players[px].action.addplayer.ping = 0; // TODO + rep.data.play_client.playerlistitem.players[px].action.addplayer.has_display_name = 0; + rep.data.play_client.playerlistitem.players[px].action.addplayer.display_name = NULL; + px++; + if (writePacket(conn, &rep) < 0) return 1; + rep.id = PKT_PLAY_CLIENT_TIMEUPDATE; + rep.data.play_client.timeupdate.time_of_day = overworld->time; + rep.data.play_client.timeupdate.world_age = overworld->age; + if (writePacket(conn, &rep) < 0) return 1; + add_collection(playersToLoad, player); + //broadcastf("yellow", "%s has joined the server!", player->name); + const char* mip = NULL; + char tip[48]; + if (conn->addr.sin6_family == AF_INET) { + struct sockaddr_in *sip4 = (struct sockaddr_in*) &conn->addr; + mip = inet_ntop(AF_INET, &sip4->sin_addr, tip, 48); + } else if (conn->addr.sin6_family == AF_INET6) { + struct sockaddr_in6 *sip6 = (struct sockaddr_in6*) &conn->addr; + if (memseq((unsigned char*) &sip6->sin6_addr, 10, 0) && memseq((unsigned char*) &sip6->sin6_addr + 10, 2, 0xff)) { + mip = inet_ntop(AF_INET, ((unsigned char*) &sip6->sin6_addr) + 12, tip, 48); + } else mip = inet_ntop(AF_INET6, &sip6->sin6_addr, tip, 48); + } else if (conn->addr.sin6_family == AF_LOCAL) { + mip = "UNIX"; + } else { + mip = "UNKNOWN"; + } + printf("Player '%s' has joined with IP '%s'\n", player->name, mip); + return 0; +} + int handleRead(struct conn* conn, struct work_param* param, int fd) { if (conn->disconnect) return 0; while (conn->readBuffer != NULL && conn->readBuffer_size > 0) { @@ -96,10 +249,145 @@ int handleRead(struct conn* conn, struct work_param* param, int fd) { conn->state = -1; } else goto rete; } else if (conn->state == STATE_LOGIN) { - if (inp->id == PKT_LOGIN_SERVER_LOGINSTART) { + if (inp->id == PKT_LOGIN_SERVER_ENCRYPTIONRESPONSE) { + if (conn->verifyToken == 0 || inp->data.login_server.encryptionresponse.shared_secret_length > 162 || inp->data.login_server.encryptionresponse.verify_token_length > 162) goto rete; + unsigned char decSecret[162]; + int secLen = RSA_private_decrypt(inp->data.login_server.encryptionresponse.shared_secret_length, inp->data.login_server.encryptionresponse.shared_secret, decSecret, public_rsa, RSA_PKCS1_PADDING); + if (secLen != 16) goto rete; + unsigned char decVerifyToken[162]; + int vtLen = RSA_private_decrypt(inp->data.login_server.encryptionresponse.verify_token_length, inp->data.login_server.encryptionresponse.verify_token, decVerifyToken, public_rsa, RSA_PKCS1_PADDING); + if (vtLen != 4) goto rete; + uint32_t vt = *((uint32_t*) decVerifyToken); + if (vt != conn->verifyToken) goto rete; + uint8_t hash[20]; + SHA_CTX context; + SHA1_Init(&context); + SHA1_Update(&context, decSecret, 16); + SHA1_Update(&context, public_rsa_publickey, 162); + SHA1_Final(hash, &context); + int m = 0; + if (hash[0] & 0x80) { + m = 1; + for (int i = 0; i < 20; i++) { + hash[i] = ~hash[i]; + } + (hash[19])++; + } + char fhash[32]; + char* fhash2 = fhash + 1; + for (int i = 0; i < 20; i++) { + snprintf(fhash + 1 + (i * 2), 3, "%02X", hash[i]); + } + for (int i = 1; i < 41; i++) { + if (fhash[i] == '0') fhash2++; + else break; + } + fhash2--; + if (m) fhash2[0] = '-'; + else fhash2++; + struct sockaddr_in sin; + 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 merr; + } else goto merr; + sin.sin_port = htons(443); + sin.sin_family = AF_INET; + int mfd = socket(AF_INET, SOCK_STREAM, 0); + SSL* sct = NULL; + int initssl = 0; + int val = 0; + if (mfd < 0) goto merr; + if (connect(mfd, (struct sockaddr*) &sin, sizeof(struct sockaddr_in))) goto merr; + sct = SSL_new(mojang_ctx); + SSL_set_connect_state(sct); + SSL_set_fd(sct, mfd); + if (SSL_connect(sct) != 1) goto merr; + else initssl = 1; + char cbuf[4096]; + int tl = snprintf(cbuf, 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->onll_username, fhash2); + int tw = 0; + while (tw < tl) { + int r = SSL_write(sct, cbuf, tl); + if (r <= 0) goto merr; + else tw += r; + } + tw = 0; + int r = 0; + while ((r = SSL_read(sct, cbuf + tw, 4095 - tw)) > 0) { + tw += r; + } + cbuf[tw] = 0; + char* data = strstr(cbuf, "\r\n\r\n"); + if (data == NULL) goto merr; + data += 4; + struct json_object json; + parseJSON(&json, data); + struct json_object* tmp = getJSONValue(&json, "id"); + if (tmp == NULL || tmp->type != JSON_STRING) { + freeJSON(&json); + goto merr; + } + char* id = trim(tmp->data.string); + tmp = getJSONValue(&json, "name"); + if (tmp == NULL || tmp->type != JSON_STRING) { + freeJSON(&json); + goto merr; + } + char* name = trim(tmp->data.string); + size_t sl = strlen(name); + if (sl < 2 || sl > 16) { + freeJSON(&json); + goto merr; + } + BEGIN_HASHMAP_ITERATION (players) + struct player* player = (struct player*) value; + if (streq_nocase(name, player->name)) { + kickPlayer(player, "You have logged in from another location!"); + goto pbn2; + } + END_HASHMAP_ITERATION (players) + pbn2: ; + val = 1; + memcpy(conn->sharedSecret, decSecret, 16); + conn->aes_ctx_enc = EVP_CIPHER_CTX_new(); + if (conn->aes_ctx_enc == NULL) goto rete; + conn->aes_ctx_dec = EVP_CIPHER_CTX_new(); + if (conn->aes_ctx_dec == NULL) goto rete; + if (work_joinServer(conn, name, id)) { + freeJSON(&json); + if (initssl) SSL_shutdown(sct); + if (mfd >= 0) close(mfd); + if (sct != NULL) SSL_free(sct); + goto rete; + } + freeJSON(&json); + merr: ; + if (initssl) SSL_shutdown(sct); + if (mfd >= 0) close(mfd); + if (sct != NULL) SSL_free(sct); + if (!val) { + rep.id = PKT_LOGIN_CLIENT_DISCONNECT; + rep.data.login_client.disconnect.reason = xstrdup("{\"text\": \"There was an unresolvable issue with the Mojang sessionserver! Please try again.\"}", 0); + if (writePacket(conn, &rep) < 0) goto rete; + conn->disconnect = 1; + goto ret; + } + } else if (inp->id == PKT_LOGIN_SERVER_LOGINSTART) { if (online_mode) { - //TODO - return -1; + if (conn->verifyToken) goto rete; + conn->onll_username = xstrdup(inp->data.login_server.loginstart.name, 0); + rep.id = PKT_LOGIN_CLIENT_ENCRYPTIONREQUEST; + rep.data.login_client.encryptionrequest.server_id = ""; + rep.data.login_client.encryptionrequest.public_key = public_rsa_publickey; + rep.data.login_client.encryptionrequest.public_key_length = 162; + conn->verifyToken = rand(); + if (conn->verifyToken == 0) conn->verifyToken = 1; + rep.data.login_client.encryptionrequest.verify_token = (uint8_t*) &conn->verifyToken; + rep.data.login_client.encryptionrequest.verify_token_length = 4; + if (writePacket(conn, &rep) < 0) goto rete; } else { int bn = 0; char* rna = trim(inp->data.login_server.loginstart.name); @@ -110,161 +398,19 @@ int handleRead(struct conn* conn, struct work_param* param, int fd) { struct player* player = (struct player*) value; if (streq_nocase(rna, player->name)) { bn = 1; - break; + goto pbn; } END_HASHMAP_ITERATION (players) + pbn: ; } if (bn) { rep.id = PKT_LOGIN_CLIENT_DISCONNECT; rep.data.login_client.disconnect.reason = xstrdup(bn == 2 ? "{\"text\": \"Invalid name!\"}" : "{\"text\", \"You are already in the server!\"}", 0); if (writePacket(conn, &rep) < 0) goto rete; conn->disconnect = 1; - return 0; + goto ret; } - rep.id = PKT_LOGIN_CLIENT_LOGINSUCCESS; - rep.data.login_client.loginsuccess.username = rna; - struct uuid uuid; - unsigned char* uuidx = (unsigned char*) &uuid; - MD5_CTX context; - MD5_Init(&context); - MD5_Update(&context, rna, strlen(rna)); - MD5_Final(uuidx, &context); - rep.data.login_client.loginsuccess.uuid = xmalloc(38); - snprintf(rep.data.login_client.loginsuccess.uuid, 10, "%08X-", ((uint32_t*) uuidx)[0]); - snprintf(rep.data.login_client.loginsuccess.uuid + 9, 6, "%04X-", ((uint16_t*) uuidx)[2]); - snprintf(rep.data.login_client.loginsuccess.uuid + 14, 6, "%04X-", ((uint16_t*) uuidx)[3]); - snprintf(rep.data.login_client.loginsuccess.uuid + 19, 6, "%04X-", ((uint16_t*) uuidx)[4]); - snprintf(rep.data.login_client.loginsuccess.uuid + 24, 9, "%08X", ((uint32_t*) (uuidx + 4))[2]); - snprintf(rep.data.login_client.loginsuccess.uuid + 32, 5, "%04X", ((uint16_t*) uuidx)[7]); - if (writePacket(conn, &rep) < 0) goto rete; - xfree(rep.data.login_client.loginsuccess.uuid); - conn->state = STATE_PLAY; - struct entity* ep = newEntity(nextEntityID++, (double) overworld->spawnpos.x + .5, (double) overworld->spawnpos.y, (double) overworld->spawnpos.z + .5, ENT_PLAYER, 0., 0.); - struct player* player = newPlayer(ep, xstrdup(rep.data.login_client.loginsuccess.username, 1), uuid, conn, 0); // TODO default gamemode - player->protocolVersion = conn->protocolVersion; - conn->player = player; - put_hashmap(players, player->entity->id, player); - rep.id = PKT_PLAY_CLIENT_JOINGAME; - rep.data.play_client.joingame.entity_id = ep->id; - rep.data.play_client.joingame.gamemode = player->gamemode; - rep.data.play_client.joingame.dimension = overworld->dimension; - rep.data.play_client.joingame.difficulty = difficulty; - rep.data.play_client.joingame.max_players = max_players; - rep.data.play_client.joingame.level_type = overworld->levelType; - rep.data.play_client.joingame.reduced_debug_info = 0; // TODO - if (writePacket(conn, &rep) < 0) goto rete; - rep.id = PKT_PLAY_CLIENT_PLUGINMESSAGE; - rep.data.play_client.pluginmessage.channel = "MC|Brand"; - rep.data.play_client.pluginmessage.data = xmalloc(16); - int rx = writeVarInt(5, rep.data.play_client.pluginmessage.data); - memcpy(rep.data.play_client.pluginmessage.data + rx, "Basin", 5); - rep.data.play_client.pluginmessage.data_size = rx + 5; - if (writePacket(conn, &rep) < 0) goto rete; - xfree(rep.data.play_client.pluginmessage.data); - rep.id = PKT_PLAY_CLIENT_SERVERDIFFICULTY; - rep.data.play_client.serverdifficulty.difficulty = difficulty; - if (writePacket(conn, &rep) < 0) goto rete; - rep.id = PKT_PLAY_CLIENT_SPAWNPOSITION; - memcpy(&rep.data.play_client.spawnposition.location, &overworld->spawnpos, sizeof(struct encpos)); - if (writePacket(conn, &rep) < 0) goto rete; - rep.id = PKT_PLAY_CLIENT_PLAYERABILITIES; - rep.data.play_client.playerabilities.flags = 0; // TODO: allows flying, remove - rep.data.play_client.playerabilities.flying_speed = 0.05; - rep.data.play_client.playerabilities.field_of_view_modifier = .1; - if (writePacket(conn, &rep) < 0) goto rete; - rep.id = PKT_PLAY_CLIENT_PLAYERPOSITIONANDLOOK; - rep.data.play_client.playerpositionandlook.x = ep->x; - rep.data.play_client.playerpositionandlook.y = ep->y; - rep.data.play_client.playerpositionandlook.z = ep->z; - rep.data.play_client.playerpositionandlook.yaw = ep->yaw; - rep.data.play_client.playerpositionandlook.pitch = ep->pitch; - rep.data.play_client.playerpositionandlook.flags = 0x0; - rep.data.play_client.playerpositionandlook.teleport_id = 0; - if (writePacket(conn, &rep) < 0) goto rete; - rep.id = PKT_PLAY_CLIENT_PLAYERLISTITEM; - pthread_rwlock_rdlock(&players->data_mutex); - rep.data.play_client.playerlistitem.action_id = 0; - rep.data.play_client.playerlistitem.number_of_players = players->entry_count + 1; - rep.data.play_client.playerlistitem.players = xmalloc(rep.data.play_client.playerlistitem.number_of_players * sizeof(struct listitem_player)); - size_t px = 0; - BEGIN_HASHMAP_ITERATION (players) - struct player* plx = (struct player*) value; - if (px < rep.data.play_client.playerlistitem.number_of_players) { - memcpy(&rep.data.play_client.playerlistitem.players[px].uuid, &plx->uuid, sizeof(struct uuid)); - rep.data.play_client.playerlistitem.players[px].action.addplayer.name = xstrdup(plx->name, 0); - rep.data.play_client.playerlistitem.players[px].action.addplayer.number_of_properties = 0; - rep.data.play_client.playerlistitem.players[px].action.addplayer.properties = NULL; - rep.data.play_client.playerlistitem.players[px].action.addplayer.gamemode = plx->gamemode; - rep.data.play_client.playerlistitem.players[px].action.addplayer.ping = 0; // TODO - rep.data.play_client.playerlistitem.players[px].action.addplayer.has_display_name = 0; - rep.data.play_client.playerlistitem.players[px].action.addplayer.display_name = NULL; - px++; - } - if (player == plx) continue; - struct packet* pkt = xmalloc(sizeof(struct packet)); - pkt->id = 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 = xmalloc(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 = xstrdup(player->name, 0); - 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; - add_queue(plx->outgoingPacket, pkt); - flush_outgoing(plx); - END_HASHMAP_ITERATION (players) - pthread_rwlock_unlock(&players->data_mutex); - memcpy(&rep.data.play_client.playerlistitem.players[px].uuid, &player->uuid, sizeof(struct uuid)); - rep.data.play_client.playerlistitem.players[px].action.addplayer.name = xstrdup(player->name, 0); - rep.data.play_client.playerlistitem.players[px].action.addplayer.number_of_properties = 0; - rep.data.play_client.playerlistitem.players[px].action.addplayer.properties = NULL; - rep.data.play_client.playerlistitem.players[px].action.addplayer.gamemode = player->gamemode; - rep.data.play_client.playerlistitem.players[px].action.addplayer.ping = 0; // TODO - rep.data.play_client.playerlistitem.players[px].action.addplayer.has_display_name = 0; - rep.data.play_client.playerlistitem.players[px].action.addplayer.display_name = NULL; - px++; - if (writePacket(conn, &rep) < 0) goto rete; - rep.id = PKT_PLAY_CLIENT_TIMEUPDATE; - rep.data.play_client.timeupdate.time_of_day = overworld->time; - rep.data.play_client.timeupdate.world_age = overworld->age; - if (writePacket(conn, &rep) < 0) goto rete; - add_collection(playersToLoad, player); - //broadcastf("yellow", "%s has joined the server!", player->name); - const char* mip = NULL; - char tip[48]; - if (conn->addr.sin6_family == AF_INET) { - struct sockaddr_in *sip4 = (struct sockaddr_in*) &conn->addr; - mip = inet_ntop(AF_INET, &sip4->sin_addr, tip, 48); - } else if (conn->addr.sin6_family == AF_INET6) { - struct sockaddr_in6 *sip6 = (struct sockaddr_in6*) &conn->addr; - if (memseq((unsigned char*) &sip6->sin6_addr, 10, 0) && memseq((unsigned char*) &sip6->sin6_addr + 10, 2, 0xff)) { - mip = inet_ntop(AF_INET, ((unsigned char*) &sip6->sin6_addr) + 12, tip, 48); - } else mip = inet_ntop(AF_INET6, &sip6->sin6_addr, tip, 48); - } else if (conn->addr.sin6_family == AF_LOCAL) { - mip = "UNIX"; - } else { - mip = "UNKNOWN"; - } - printf("Player '%s' has joined with IP '%s'\n", player->name, mip); - /*for (int x = -9; x < 10; x++) { - for (int z = -9; z < 10; z++) { - struct chunk* ch = getChunk(overworld, x + (int32_t) ep->x / 16, z + (int32_t) ep->z / 16); - if (ch != NULL) { - struct packet* pkt = xmalloc(sizeof(struct packet)); - pkt->id = PKT_PLAY_CLIENT_CHUNKDATA; - pkt->data.play_client.chunkdata.data = ch; - pkt->data.play_client.chunkdata.ground_up_continuous = 1; - pkt->data.play_client.chunkdata.number_of_block_entities = 0; - pkt->data.play_client.chunkdata.block_entities = NULL; - add_queue(player->conn->outgoingPacket, pkt); - } - } - }*/ - + if (work_joinServer(conn, rna, NULL)) goto rete; } } } else if (conn->state == STATE_PLAY) {