Encryption/online mode wip

This commit is contained in:
JavaProphet 2017-01-06 23:38:21 -08:00
parent 6a29bcc777
commit e6b7887024
10 changed files with 528 additions and 170 deletions

View File

@ -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

View File

@ -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);

View File

@ -13,6 +13,8 @@
#include <sys/socket.h>
#include "work.h"
#include <netinet/ip6.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
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);

View File

@ -13,6 +13,94 @@
#include <stdlib.h>
#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

View File

@ -42,6 +42,7 @@
#include "queue.h"
#include "profile.h"
#include "plugin.h"
#include <openssl/rand.h>
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");

View File

@ -23,6 +23,31 @@
#include <stdint.h>
#include <stdlib.h>
#include "network.h"
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/ssl.h>
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;

View File

@ -10,6 +10,7 @@
#include "accept.h"
#include "inventory.h"
#include <openssl/rsa.h>
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);

View File

@ -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;

View File

@ -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 {

View File

@ -32,6 +32,11 @@
#include "item.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <openssl/sha.h>
#include <netdb.h>
#include <openssl/ssl.h>
#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) {