Search code examples
phpnode.jssocketspack

PHP to NodeJS conversion not succeeding (TCP socket issue)


I am trying to convert the following PHP code to Node.JS code:

PHP

$TimeStart = microtime(true); // for read timeout purposes

// See http://wiki.vg/Protocol (Status Ping)
$Data = "\x00"; // packet ID = 0 (varint)

$Data .= "\x04"; // Protocol version (varint)
$Data .= Pack( 'c', StrLen( $this->ServerAddress ) ) . $this->ServerAddress; // Server (varint len + UTF-8 addr)
$Data .= Pack( 'n', $this->ServerPort ); // Server port (unsigned short)
$Data .= "\x01"; // Next state: status (varint)

$Data = Pack( 'c', StrLen( $Data ) ) . $Data; // prepend length of packet ID + data

fwrite( $this->Socket, $Data ); // handshake
fwrite( $this->Socket, "\x01\x00" ); // status ping

$Length = $this->ReadVarInt( ); // full packet length

if( $Length < 10 )
{
    return FALSE;
}

fgetc( $this->Socket ); // packet type, in server ping it's 0

$Length = $this->ReadVarInt( ); // string length

$Data = "";
do
{
    if (microtime(true) - $TimeStart > $this->Timeout)
    {
        //exception
    }

    $Remainder = $Length - StrLen( $Data );
    $block = fread( $this->Socket, $Remainder ); // and finally the json string
    // abort if there is no progress
    if (!$block)
    {
        //exception
    }

    $Data .= $block;
} while( StrLen($Data) < $Length );

This is what i thought it was but apparently it's not the same as i dont get any data returned.

var socket = new net.Socket();
socket = net.connect(port, host, function(){
    console.log('Connected');

    /*
    Returns data but protocol is not correctly folllowed
    -----------------------------------------------------*/
    var PACKET_HEAD = new Buffer([
        0, // Packet ID 0
        4 // Version 4 (1.7.2), 5 is 1.7.6
    ]);
    var PACKET_PORT = new Buffer(2);
    var PACKET_NEXTSTATE = new Buffer([1]);
    var PACKET_STATUS_REQ = new Buffer([1, 0]);


    host = new Buffer(host);
    var addrLen = new Buffer(varint.encode(host.length));

    PACKET_PORT.writeUInt16BE(port, 0);

    var packetLength = 2 + addrLen.length + host.length + 2 + 1;
    var lengthBuffer = new Buffer(varint.encode(packetLength));

    var packet = Buffer.concat([
        lengthBuffer,
        PACKET_HEAD,
        addrLen,
        host,
        PACKET_PORT,
        PACKET_NEXTSTATE,
        PACKET_STATUS_REQ
    ], packetLength + 3);

    socket.write(packet);

    /*
    TRY 1
    ------*/
    var packet;
    packet = "\x00";
    packet += "\x04";
    packet += bufferpack.pack('c', host.length.toString()) + host;
    packet += bufferpack.pack('n', port.toString());
    packet += "\x01";
    packet = bufferpack.pack('c', packet.length.toString()) + packet;

    socket.write(packet);
    socket.write("\x01\x00");


    /*TRY 2
    -------*/
    var packet;
    packet = packData(
        '\x00\x00' 
        + packData(host.toString('utf8'))
        + packPort(port)
        + '\x01'
    );

    socket.write(packet);
    socket.write(packData('\x00'));
});



socket.on('error', function(err){
    console.log(errorHandler(err));
    socket.end();
});

socket.on('data', function(data){
    console.log(data);
    socket.end();
});

setTimeout(function(){
    console.log("Timeout");
    socket.end();
    socket.destroy();
}, 1000);


function char(value) {
    return value.toString().charCodeAt(0);
}
function packData(data) {
    return varint.encode(data.length) + data;
}
function packPort(data) {
    return bufferpack.pack('>H', data.toString());
}

I have tried numerous ways to replicate the php code to nodejs end ended up using libraries that mimic the pack behavior (bufferpack).

I think i am missing something but i can't figure out what it is.


Solution

  • Maybe a bit late, but I've made your code working for me.

    The module I've used: https://github.com/ryanrolds/bufferpack

    installing using: npm install bufferpack

    This is the plain code (copied)

    var _connection;
    var connect = function(port, host, callback) {
        _connection = new net.Socket();
        _connection.connect(port, host, function() {
            system.log('Connected to the server, authenticating now.');
    
            var PACKET_HEAD = new Buffer([
                0, // Packet ID 0
                47 // Version 4 (1.7.2), 5 is 1.7.6
            ]);
            var PACKET_PORT = new Buffer(2);
            var PACKET_NEXTSTATE = new Buffer([1]);
            var PACKET_STATUS_REQ = new Buffer([1, 0]);
    
    
            host = new Buffer(host);
            var addrLen = new Buffer(varint.encode(host.length));
    
            PACKET_PORT.writeUInt16BE(port, 0);
    
            var packetLength = 2 + addrLen.length + host.length + 2 + 1;
            var lengthBuffer = new Buffer(varint.encode(packetLength));
    
            var packet = Buffer.concat([
                lengthBuffer,
                PACKET_HEAD,
                addrLen,
                host,
                PACKET_PORT,
                PACKET_NEXTSTATE,
                PACKET_STATUS_REQ
            ], packetLength + 3);
    
            _connection.write(packet);
    
            /*
            TRY 1
            ------*/
            var packet;
            packet = "\x00";
            packet += "\x04";
            packet += bufferpack.pack('c', host.length.toString()) + host;
            packet += bufferpack.pack('n', port.toString());
            packet += "\x01";
            packet = bufferpack.pack('c', packet.length.toString()) + packet;
    
            _connection.write(packet);
            _connection.write("\x01\x00");
    
    
            /*TRY 2
            -------*/
            var packet;
            packet = packData(
                '\x00\x00' 
                + packData(host.toString('utf8'))
                + packPort(port)
                + '\x01'
            );
    
            _connection.write(packet);
            _connection.write(packData('\x00'));
    
        });
        _connection.on('error', function(err){
            system.log(err);
            _connection.end();
        });
        _connection.on('data', function(data){
            system.log(data);
        });
    };
    exports.connect = connect;
    
    function packData(data) {
        return varint.encode(data.length) + data;
    }
    
    function packPort(data) {
        return bufferpack.pack('>H', data.toString());
    }
    

    My github repo: https://github.com/AtlasDev/NodeCraft

    This part is in libs/connection.js

    Hope this still helps, AtlasDev