split up work

This commit is contained in:
Protryon 2019-04-15 09:50:10 -07:00
parent 7f6fa031b6
commit b0b43eed0b
15 changed files with 340 additions and 422 deletions

View File

@ -23,8 +23,6 @@ size_t tick_counter;
struct config* cfg;
struct mempool* global_pool;
struct logsess* delog;
struct hashmap* players;
struct queue* playersToLoad;
pthread_mutex_t glob_tick_mut;
pthread_cond_t glob_tick_cond;
struct hashmap* server_map;

View File

@ -1274,9 +1274,11 @@ struct packet {
union pkts data;
};
ssize_t readPacket(struct connection* conn, unsigned char* buf, size_t buflen, struct packet* packet);
struct packet* packet_new(struct mempool* pool, int32_t id);
ssize_t writePacket(struct connection* conn, struct packet* packet);
ssize_t packet_read(struct connection* conn, unsigned char* buf, size_t buflen, struct packet* packet);
ssize_t packet_write(struct connection* conn, struct packet* packet);
void freePacket(int state, int dir, struct packet* packet);

View File

@ -26,6 +26,9 @@ struct server {
struct world* endworld;
struct list* worlds;
struct queue* prepared_connections;
struct hashmap* players;
// struct queue* playersToLoad;
};
//TODO: make this an option

View File

@ -5,7 +5,7 @@
* Author: root
*/
#include "packet.h"
#include "basin/packet.h"
#include <basin/inventory.h>
#include <basin/globals.h>
#include "basin/network.h"

View File

@ -5,7 +5,7 @@
* Author: root
*/
#include "packet.h"
#include "basin/packet.h"
#include <basin/basin.h>
#include <basin/network.h>
#include <basin/queue.h>

View File

@ -5,7 +5,7 @@
* Author: root
*/
#include "packet.h"
#include "basin/packet.h"
#include <basin/basin.h>
#include <basin/queue.h>
#include <basin/game.h>

View File

@ -5,7 +5,7 @@
* Author: root
*/
#include "packet.h"
#include "basin/packet.h"
#include <basin/network.h>
#include <basin/globals.h>
#include <basin/item.h>

254
src/login_stage_handler.c Normal file
View File

@ -0,0 +1,254 @@
//
// Created by p on 4/14/19.
//
#include "login_stage_handler.h"
#include <basin/connection.h>
#include <basin/network.h>
#include <basin/packet.h>
#include <basin/globals.h>
#include <basin/version.h>
#include <avuna/string.h>
#include <openssl/ssl.h>
#include <netdb.h>
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->protocolVersion = (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\":{\"max\":%lu,\"online\":%lu},\"description\":{\"text\":\"%s\"}}", MC_PROTOCOL_VERSION_MIN, conn->server->max_players, conn->server->players->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) goto rete;
unsigned char decSecret[162];
int secLen = RSA_private_decrypt(packet->data.login_server.encryptionresponse.shared_secret_length, packet->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(packet->data.login_server.encryptionresponse.verify_token_length, packet->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;
memcpy(conn->shared_secret, decSecret, 16);
uint8_t pubkey[162];
memcpy(pubkey, public_rsa_publickey, 162);
uint8_t hash[20];
SHA_CTX context;
SHA1_Init(&context);
SHA1_Update(&context, decSecret, 16);
SHA1_Update(&context, pubkey, 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->online_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;
json_parse(&json, data);
struct json_object* tmp = json_get(&json, "id");
if (tmp == NULL || tmp->type != JSON_STRING) {
freeJSON(&json);
goto merr;
}
char* id = trim(tmp->data.string);
tmp = json_get(&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;
conn->aes_ctx_enc = EVP_CIPHER_CTX_new();
if (conn->aes_ctx_enc == NULL) goto rete;
//EVP_CIPHER_CTX_set_padding(conn->aes_ctx_enc, 0);
if (EVP_EncryptInit_ex(conn->aes_ctx_enc, EVP_aes_128_cfb8(), NULL, conn->sharedSecret, conn->sharedSecret) != 1) goto rete;
if (conn->aes_ctx_enc != NULL) {
phook(conn->pool, encrypt_free, conn->aes_ctx_enc);
}
conn->aes_ctx_dec = EVP_CIPHER_CTX_new();
if (conn->aes_ctx_dec == NULL) goto rete;
//EVP_CIPHER_CTX_set_padding(conn->aes_ctx_dec, 0);
if (EVP_DecryptInit_ex(conn->aes_ctx_dec, EVP_aes_128_cfb8(), NULL, conn->sharedSecret, conn->sharedSecret) != 1) goto rete;
if (conn->aes_ctx_dec != NULL) {
phook(conn->pool, decrypt_free, conn->aes_ctx_dec);
}
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 (packet_write(conn, &rep) < 0) goto rete;
conn->disconnect = 1;
goto ret;
}
}
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) {
BEGIN_HASHMAP_ITERATION (players)
struct player* player = (struct player*) value;
if (streq_nocase(name_trimmed, player->name)) {
bad_name = 1;
goto pbn;
}
END_HASHMAP_ITERATION (players)
pbn: ;
}
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)) goto rete;
}
}
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;
}

17
src/login_stage_handler.h Normal file
View File

@ -0,0 +1,17 @@
//
// Created by p on 4/14/19.
//
#ifndef BASIN_LOGIN_STAGE_HANDLER_H
#define BASIN_LOGIN_STAGE_HANDLER_H
#include <basin/connection.h>
#include <basin/packet.h>
int handle_packet_handshake(struct connection* conn, struct packet* packet);
int handle_packet_login(struct connection* conn, struct packet* packet);
int handle_packet_status(struct connection* conn, struct packet* packet);
#endif //BASIN_LOGIN_STAGE_HANDLER_H

View File

@ -275,9 +275,10 @@ struct server* server_load(struct config_node* server) {
list_append(serv->worlds, serv->overworld);
//add_collection(worlds, nether);
//add_collection(worlds, endworld);
serv-> players = hashmap_thread_new(32, global_pool);
// playersToLoad = queue_new(0, 1, global_pool);
hashmap_put(server_map, serv->name, serv);
struct accept_param* param = pmalloc(serv->pool, sizeof(struct accept_param));
param->server = serv;
}
int main(int argc, char* argv[]) {
@ -354,8 +355,6 @@ int main(int argc, char* argv[]) {
return -1;
}
globalChunkQueue = queue_new(0, 1, global_pool);
players = hashmap_thread_new(32, global_pool);
playersToLoad = queue_new(0, 1, global_pool);
init_materials();
acclog(delog, "Materials Initialized");
init_blocks();

View File

@ -6,7 +6,7 @@
*/
#include "accept.h"
#include "packet.h"
#include "basin/packet.h"
#include <basin/nbt.h>
#include <basin/network.h>
#include <avuna/pmem.h>

View File

@ -1,7 +1,7 @@
#include "packet.h"
#include "basin/packet.h"
#include <basin/globals.h>
#include <basin/inventory.h>
#include <basin/network.h>
@ -26,13 +26,20 @@
#include <time.h>
#include <math.h>
struct packet* packet_new(struct mempool* pool, int32_t id) {
struct packet* packet = pcalloc(pool, sizeof(struct packet));
packet->pool = pool;
packet->id = id;
return packet;
}
#define ADRX if(rx == 0) goto rer;pbuf += rx;ps -= rx;
#define ADX(x) pbuf += x;ps -= x;
#define CPS(x) if(ps < x) goto rer;
#define CPS_OPT(x) if(ps >= x) {
#define ENS(x) if(ps-pi < x) { ps += (x > 256 ? x + 1024 : 1024); pktbuf = prealloc(packet->pool, pktbuf - 10, ps + 10) + 10; }
ssize_t readPacket(struct connection* conn, unsigned char* buf, size_t buflen, struct packet* packet) {
ssize_t packet_read(struct connection* conn, unsigned char* buf, size_t buflen, struct packet* packet) {
void* pktbuf = buf;
int32_t pktlen = (int32_t) buflen;
if (conn->compression_state >= 0) {
@ -549,7 +556,7 @@ ssize_t readPacket(struct connection* conn, unsigned char* buf, size_t buflen, s
return buflen;
}
ssize_t writePacket(struct connection* conn, struct packet* packet) {
ssize_t packet_write(struct connection* conn, struct packet* packet) {
if (conn->protocol_state == STATE_PLAY && packet->id == PKT_PLAY_CLIENT_CHUNKDATA) {
if (!isChunkLoaded(conn->player->world, packet->data.play_client.chunkdata.cx, packet->data.play_client.chunkdata.cz)) return 0;
}

View File

@ -6,7 +6,7 @@
*/
#include "accept.h"
#include "packet.h"
#include "basin/packet.h"
#include <basin/network.h>
#include <basin/inventory.h>
#include <basin/queue.h>

View File

@ -7,7 +7,8 @@
#include "work.h"
#include "accept.h"
#include "packet.h"
#include "basin/packet.h"
#include "login_stage_handler.h"
#include <basin/network.h>
#include <basin/globals.h>
#include <basin/connection.h>
@ -18,6 +19,7 @@
#include <basin/block.h>
#include <basin/item.h>
#include <basin/version.h>
#include <basin/profile.h>
#include <avuna/json.h>
#include <avuna/streams.h>
#include <avuna/queue.h>
@ -39,35 +41,6 @@
#include <sys/ioctl.h>
#include <avuna/util.h>
void closeConn(struct work_param* param, struct conn* conn) {
close(conn->fd);
if (rem_collection(param->conns, conn)) {
errlog(param->logsess, "Failed to delete connection properly! This is bad!");
}
if (conn->player != NULL) {
broadcastf("yellow", "%s has left the server!", conn->player->name);
conn->player->defunct = 1;
conn->player->conn = NULL;
}
if (conn->aes_ctx_enc != NULL) {
char final[256];
int csl2 = 0;
EVP_EncryptFinal_ex(conn->aes_ctx_enc, final, &csl2);
EVP_CIPHER_CTX_free(conn->aes_ctx_enc);
}
if (conn->aes_ctx_dec != NULL) {
char final[256];
int csl2 = 0;
EVP_DecryptFinal_ex(conn->aes_ctx_dec, final, &csl2);
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->readDecBuffer != NULL) xfree(conn->readDecBuffer);
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;
@ -101,7 +74,7 @@ int work_joinServer(struct conn* conn, char* username, char* uuids) {
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;
if (packet_write(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.);
@ -117,26 +90,26 @@ int work_joinServer(struct conn* conn, char* username, char* uuids) {
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;
if (packet_write(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 rx2 = writeVarInt(5, rep.data.play_client.pluginmessage.data);
memcpy(rep.data.play_client.pluginmessage.data + rx2, "Basin", 5);
rep.data.play_client.pluginmessage.data_size = rx2 + 5;
if (writePacket(conn, &rep) < 0) return 1;
if (packet_write(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;
if (packet_write(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;
if (packet_write(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;
if (packet_write(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;
@ -145,7 +118,7 @@ int work_joinServer(struct conn* conn, char* username, char* uuids) {
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;
if (packet_write(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;
@ -192,11 +165,11 @@ int work_joinServer(struct conn* conn, char* username, char* uuids) {
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;
if (packet_write(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;
if (packet_write(conn, &rep) < 0) return 1;
add_collection(playersToLoad, player);
broadcastf("yellow", "%s has joined the server!", player->name);
const char* mip = NULL;
@ -256,379 +229,44 @@ int connection_read(struct netmgr_connection* netmgr_conn, uint8_t* read_buf, si
struct packet* packet = pmalloc(packet_pool, sizeof(struct packet));
packet->pool = packet_pool;
ssize_t read_packet_length = readPacket(conn, packet_buf, (size_t) length, packet);
ssize_t read_packet_length = packet_read(conn, packet_buf, (size_t) length, packet);
if (read_packet_length == -1) goto rete;
int os = conn->state;
struct packet rep;
int df = 0;
if (conn->state == STATE_HANDSHAKE && packet->id == PKT_HANDSHAKE_SERVER_HANDSHAKE) {
conn->host_ip = xstrdup(packet->data.handshake_server.handshake.server_address, 0);
conn->host_port = packet->data.handshake_server.handshake.server_port;
conn->protocolVersion = 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->state = STATE_STATUS;
} else if (packet->data.handshake_server.handshake.next_state == STATE_LOGIN) {
conn->state = STATE_LOGIN;
} else goto rete;
} else if (conn->state == STATE_STATUS) {
if (packet->id == PKT_STATUS_SERVER_REQUEST) {
rep.id = PKT_STATUS_CLIENT_RESPONSE;
rep.data.status_client.response.json_response = xmalloc(1000);
rep.data.status_client.response.json_response[999] = 0;
snprintf(rep.data.status_client.response.json_response, 999, "{\"version\":{\"name\":\"1.11.2\",\"protocol\":%i},\"players\":{\"max\":%i,\"online\":%i},\"description\":{\"text\":\"%s\"}}", MC_PROTOCOL_VERSION_MIN, max_players, players->entry_count, motd);
if (writePacket(conn, &rep) < 0) goto rete;
xfree(rep.data.status_client.response.json_response);
} else if (packet->id == PKT_STATUS_SERVER_PING) {
rep.id = PKT_STATUS_CLIENT_PONG;
if (writePacket(conn, &rep) < 0) goto rete;
conn->state = -1;
} else goto rete;
} else if (conn->state == STATE_LOGIN) {
if (packet->id == PKT_LOGIN_SERVER_ENCRYPTIONRESPONSE) {
if (conn->verifyToken == 0 || packet->data.login_server.encryptionresponse.shared_secret_length > 162 || packet->data.login_server.encryptionresponse.verify_token_length > 162) goto rete;
unsigned char decSecret[162];
int secLen = RSA_private_decrypt(packet->data.login_server.encryptionresponse.shared_secret_length, packet->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(packet->data.login_server.encryptionresponse.verify_token_length, packet->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;
memcpy(conn->sharedSecret, decSecret, 16);
uint8_t pubkey[162];
memcpy(pubkey, public_rsa_publickey, 162);
uint8_t hash[20];
SHA_CTX context;
SHA1_Init(&context);
SHA1_Update(&context, decSecret, 16);
SHA1_Update(&context, pubkey, 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;
json_parse(&json, data);
struct json_object* tmp = json_get(&json, "id");
if (tmp == NULL || tmp->type != JSON_STRING) {
freeJSON(&json);
goto merr;
}
char* id = trim(tmp->data.string);
tmp = json_get(&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;
conn->aes_ctx_enc = EVP_CIPHER_CTX_new();
if (conn->aes_ctx_enc == NULL) goto rete;
//EVP_CIPHER_CTX_set_padding(conn->aes_ctx_enc, 0);
if (EVP_EncryptInit_ex(conn->aes_ctx_enc, EVP_aes_128_cfb8(), NULL, conn->sharedSecret, conn->sharedSecret) != 1) goto rete;
conn->aes_ctx_dec = EVP_CIPHER_CTX_new();
if (conn->aes_ctx_dec == NULL) goto rete;
//EVP_CIPHER_CTX_set_padding(conn->aes_ctx_dec, 0);
if (EVP_DecryptInit_ex(conn->aes_ctx_dec, EVP_aes_128_cfb8(), NULL, conn->sharedSecret, conn->sharedSecret) != 1) 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 (packet->id == PKT_LOGIN_SERVER_LOGINSTART) {
if (online_mode) {
if (conn->verifyToken) goto rete;
conn->onll_username = xstrdup(packet->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(packet->data.login_server.loginstart.name);
size_t rnal = strlen(rna);
if (rnal > 16 || rnal < 2) bn = 2;
if (!bn) {
BEGIN_HASHMAP_ITERATION (players)
struct player* player = (struct player*) value;
if (streq_nocase(rna, player->name)) {
bn = 1;
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;
goto ret;
}
if (work_joinServer(conn, rna, NULL)) goto rete;
}
if (read_packet_length == -1) goto ret_error;
if (conn->protocol_state == STATE_HANDSHAKE && packet->id == PKT_HANDSHAKE_SERVER_HANDSHAKE) {
if (handle_packet_handshake(conn, packet)) {
goto ret_error;
}
} else if (conn->state == STATE_PLAY) {
add_queue(conn->player->incomingPacket, packet);
df = 1;
} else if (conn->protocol_state == STATE_STATUS) {
if (handle_packet_status(conn, packet)) {
goto ret_error;
}
} else if (conn->protocol_state == STATE_LOGIN) {
if (handle_packet_login(conn, packet)) {
goto ret_error;
}
} else if (conn->protocol_state == STATE_PLAY) {
queue_push(conn->player->incomingPacket, packet);
} else {
goto ret_error;
}
beginProfilerSection("movebuf");
memmove(abuf, abuf + length + ls, asze - length - ls);
asze -= length + ls;
if (abuf == conn->readBuffer) conn->readBuffer_size = asze;
else if (abuf == conn->readDecBuffer) conn->readDecBuffer_size = asze;
endProfilerSection("movebuf");
goto ret;
rete: ;
if (!df) {
freePacket(os, 0, packet);
xfree(packet);
}
return -1;
ret_error: ;
pfree(packet->pool);
return 1;
ret: ;
if (!df) {
freePacket(os, 0, packet);
xfree(packet);
}
pfree(packet->pool);
}
return 0;
}
void connection_on_closed(struct netmgr_connection* conn) {
}
void run_work(struct work_param* param) {
if (pipe(param->pipes) != 0) {
printf("Failed to create pipe! %s\n", strerror(errno));
return;
void connection_on_closed(struct netmgr_connection* netmgr_conn) {
struct connection* conn = netmgr_conn->extra;
if (conn->player != NULL) {
broadcastf("yellow", "%s has left the server!", conn->player->name);
conn->player->defunct = 1;
conn->player->conn = NULL;
}
unsigned char wb;
unsigned char* mbuf = xmalloc(1024);
while (1) {
pthread_rwlock_rdlock(&param->conns->data_mutex);
size_t cc = param->conns->count;
struct pollfd fds[cc + 1];
struct conn* conns[cc];
int fdi = 0;
for (int i = 0; i < param->conns->size; i++) {
if (param->conns->data[i] != NULL) {
conns[fdi] = (param->conns->data[i]);
struct conn* conn = conns[fdi];
fds[fdi].fd = conns[fdi]->fd;
fds[fdi].events = POLLIN | ((conn->writeBuffer_size > 0 || (conn->player != NULL && conn->player->outgoingPacket->size > 0)) ? POLLOUT : 0);
fds[fdi++].revents = 0;
if (fdi == cc) break;
}
}
pthread_rwlock_unlock(&param->conns->data_mutex);
fds[cc].fd = param->pipes[0];
fds[cc].events = POLLIN;
fds[cc].revents = 0;
int cp = poll(fds, cc + 1, -1);
if (cp < 0) {
printf("Poll error in worker thread! %s\n", strerror(errno));
} else if (cp == 0) continue;
else if ((fds[cc].revents & POLLIN) == POLLIN) {
if (read(param->pipes[0], &wb, 1) < 1) printf("Error reading from pipe, infinite loop COULD happen here.\n");
if (cp-- == 1) continue;
}
for (int i = 0; i < cc; i++) {
int re = fds[i].revents;
struct conn* conn = conns[i];
if ((re & POLLERR) == POLLERR) {
//printf("POLLERR in worker poll! This is bad!\n");
closeConn(param, conn);
conn = NULL;
goto cont;
}
if ((re & POLLHUP) == POLLHUP) {
closeConn(param, conn);
conn = NULL;
goto cont;
}
if ((re & POLLNVAL) == POLLNVAL) {
printf("Invalid FD in worker poll! This is bad!\n");
closeConn(param, conn);
conn = NULL;
goto cont;
}
if ((re & POLLIN) == POLLIN) {
size_t tr = 0;
ioctl(fds[i].fd, FIONREAD, &tr);
unsigned char* loc;
if (conn->readBuffer == NULL) {
conn->readBuffer = xmalloc(tr); // TODO: max upload?
conn->readBuffer_size = tr;
loc = conn->readBuffer;
} else {
conn->readBuffer_size += tr;
conn->readBuffer = xrealloc(conn->readBuffer, conn->readBuffer_size);
loc = conn->readBuffer + conn->readBuffer_size - tr;
}
ssize_t r = 0;
if (r == 0 && tr == 0) { // nothing to read, but wont block.
ssize_t x = read(fds[i].fd, loc + r, tr - r);
if (x <= 0) {
closeConn(param, conn);
conn = NULL;
goto cont;
}
r += x;
}
while (r < tr) {
ssize_t x = read(fds[i].fd, loc + r, tr - r);
if (x <= 0) {
closeConn(param, conn);
conn = NULL;
goto cont;
}
r += x;
}
int p = 0;
p = handleRead(conn, param, fds[i].fd);
if (p == 1) {
goto cont;
}
}
if ((re & POLLOUT) == POLLOUT && conn != NULL) {
if (conn->player != NULL) {
struct packet* wp = pop_nowait_queue(conn->player->outgoingPacket);
while (wp != NULL) {
int wr = writePacket(conn, wp);
if (wr == -1) {
closeConn(param, conn);
conn = NULL;
goto cont;
}
freePacket(conn->state, 1, wp);
xfree(wp);
wp = pop_nowait_queue(conn->player->outgoingPacket);
}
//printf("%s WBS: %lu\n", conn->player->name, conn->writeBuffer_size);
}
ssize_t mtr = write(fds[i].fd, conn->writeBuffer, conn->writeBuffer_size);
if (mtr < 0 && errno != EAGAIN) {
closeConn(param, conn);
conn = NULL;
goto cont;
} else if (mtr < 0) {
goto cont;
} else if (mtr < conn->writeBuffer_size) {
memmove(conn->writeBuffer, conn->writeBuffer + mtr, conn->writeBuffer_size - mtr);
conn->writeBuffer_size -= mtr;
if (conn->writeBuffer_capacity > conn->writeBuffer_size + 1024 * 1024) {
conn->writeBuffer = xrealloc(conn->writeBuffer, conn->writeBuffer_size);
conn->writeBuffer_capacity = conn->writeBuffer_size;
}
} else {
conn->writeBuffer_size = 0;
conn->writeBuffer_capacity = 0;
xfree(conn->writeBuffer);
conn->writeBuffer = NULL;
}
}
if (conn->state == -1 && conn->writeBuffer_size == 0) {
closeConn(param, conn);
conn = NULL;
}
cont: ;
if (conn != NULL && conn->disconnect) {
closeConn(param, conn);
conn = NULL;
}
if (--cp == 0) break;
}
}
xfree(mbuf);
pthread_cancel (pthread_self());}
pfree(conn->pool);
}

View File

@ -6,7 +6,7 @@
*/
#include "basin/network.h"
#include "packet.h"
#include "basin/packet.h"
#include <basin/game.h>
#include <basin/block.h>
#include <basin/queue.h>