Search code examples
node.jsminecraft

How does minecraft actually connect to a server?


For the purpose of my mini project I need a proxy server between my client and some server. This code works yet it doesn't work. It starts connecting, goes trough login and stops at encrypting and then after a while it times out and I can't figure out why. I've been rereading the minecraft protocol (https://wiki.vg/Protocol#Handshake) and the handshake but I still can't figure out why it doesn't work. The console.log()'s are for debugging and from it I've deducted that for whatever reason the server never sends the set compression/ login success packets.

var net = require('net');
var mcip = "mc.hypixel.net";
var mcport = 25565;

var server = net.createServer((socket) =>{
        var remoteAddress = socket.remoteAddress + ':' + socket.remotePort;
        console.log('new client connection from %s' + remoteAddress);

        socket.on('data', (d)=>{
            var client = net.connect(mcport,mcip, ()=>{
                console.log("connected to server");
                client.on("data", (data)=>{
                    socket.write(data);
                    console.log(data);
                    console.log("S -> C");
                    client.end();
                });
                client.write(d);
                console.log(d);
                console.log("C -> S");
            });
        });

    });
    server.listen(25565);

Solution

  • A big issue here is that you're re-connecting to Hypixel every time your client sends data! You need to rearrange this to only connect to the server once, when your client connects to you.

        socket.on('data', (d)=>{
            var client = net.connect(mcport,mcip, ()=>{
                console.log("connected to server");
                client.on("data", (data)=>{
                    socket.write(data);
                    console.log(data);
                    console.log("S -> C");
                    client.end();
                });
                client.write(d);
                console.log(d);
                console.log("C -> S");
            });
        });
    

    Becomes this:

        var client = net.connect(mcport,mcip, ()=>{
            console.log("connected to server");
            client.on("data", (data)=>{
                socket.write(data);
                console.log(data);
                console.log("S -> C");
            });
        });
    
        socket.on('data', (d)=>{
            client.write(d);
            console.log(d);
            console.log("C -> S");
        });
    

    Note that now we're not ending the connection to Hypixel, we keep it open & keep sending the clients data. (you should handle closing the connection when your client disconnects, in a socket.on('close', (hadError)=>{...}) handler)

    With this change, the proxy starts to work, but Hypixel will promptly kick you for not connecting via the correct address. In the handshake packet, your connecting minecraft client will set the sever address to the address of your proxy server, since that's what it's connecting to as far as it's concerned. Hypixel sees that this isn't "mc.hypixel.net" & kicks the client.

    Even if this wasn't the case, once the connection reaches the play state, you won't be able to see any meaningful data (as I suspect you want to), as it's all encrypted by an aes CFB-8 stream cipher, the key to which is passed from MC client to server using RSA (you can't feasibly get it).

    Don't let this discourage you tho, it is possible to mitm a minecraft c<->s connection. It's just a lot more involved.