diff --git a/examples/server.js b/examples/server.js index 9773f2c..42a3691 100644 --- a/examples/server.js +++ b/examples/server.js @@ -15,17 +15,96 @@ var server = pmp.createServer({ }); server.on('connection', function(client) { - client.on("mcpe", packet => console.log(packet)); - client.on("login", data => { - console.log(client.displayName + '(' + client.XUID + ') ' + ' joined the game'); + + client.on("mcpe",packet => console.log(packet)); + + client.on("login",packet => { + console.log("aaaaa"); + + client.writeMCPE("player_status",{ + status:0 + }); +/* + client.writeMCPE('move_player', { + entityId: [0,0], + x: 1, + y: 64 + 1.62, + z: 1, + yaw: 0, + headYaw: 0, + pitch: 0, + mode: 0, + onGround: 1 + }); + + client.writeMCPE("start_game",{ + seed:-1, + dimension:0, + generator:1, + gamemode:1, + entityId:[0,0], + spawnX:1, + spawnY:1, + spawnZ:1, + x:0, + y:1+1.62, + z:0, + isLoadedInCreative:0, + dayCycleStopTime:0, + eduMode:0, + unknown:"" + });*/ +/* + client.writeMCPE('set_spawn_position', { + x: 1, + y: 64, + z: 1 + }); + client.writeMCPE("set_time",{ + time:0, + started:1 + }); + + client.writeMCPE('respawn', { + x: 1, + y: 64, + z: 1 + });*/ }); - client.on('error', err => { + client.on("request_chunk_radius",() => { + client.writeMCPE('chunk_radius_update',{ + chunk_radius:1 + }); + + for (let x = -1; x <=1; x++) { + for (let z = -1; z <=1; z++) { + client.writeBatch([{"name":"mcpe","params":{name:"full_chunk_data",params:{ + chunkX: x, + chunkZ: z, + order: 1, + chunkData:fs.readFileSync(__dirname+"/chunk") + }}}]); + } + } + + client.writeMCPE('player_status', { + status: 3 + }); + + client.writeMCPE('set_time', { + time: 0, + started: 1 + }); + + }); + + client.on('error', function(err) { console.log(err.stack); }); - client.on('end', () => { + client.on('end',function() { console.log("client left"); }) -}); +}); \ No newline at end of file diff --git a/examples/server_simple.js b/examples/server_simple.js new file mode 100644 index 0000000..9773f2c --- /dev/null +++ b/examples/server_simple.js @@ -0,0 +1,31 @@ +'use strict'; + +var pmp = require('../'); +var fs = require("fs"); + +if(process.argv.length !=4) { + console.log("Usage: node server.js "); + process.exit(1); +} + +var server = pmp.createServer({ + host: process.argv[2], + port: parseInt(process.argv[3]), + name: 'MCPE;Minecraft: PE Server;81 81;0.15.0;0;20' +}); + +server.on('connection', function(client) { + client.on("mcpe", packet => console.log(packet)); + + client.on("login", data => { + console.log(client.displayName + '(' + client.XUID + ') ' + ' joined the game'); + }); + + client.on('error', err => { + console.log(err.stack); + }); + + client.on('end', () => { + console.log("client left"); + }) +}); diff --git a/src/createServer.js b/src/createServer.js index 38c9639..ade4d26 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -3,11 +3,13 @@ const raknet = require('raknet'); const zlib = require('zlib'); const ProtoDef = require('protodef').ProtoDef; const Parser = require('protodef').Parser; +const Serializer = require('protodef').Serializer; const jwt = require('jwt-simple'); const crypto = require('crypto'); const Ber = require('asn1').Ber; const merge=require("lodash.merge"); const assert=require("assert"); +var debug = require('debug')("raknet"); // const BN = require('bn.js'); const batchProto = new ProtoDef(); @@ -32,12 +34,50 @@ dataProto.addType("data_chain", ["container", [{ }]]); +function writeLI64(value, buffer, offset) { + buffer.writeInt32LE(value[0], offset+4); + buffer.writeInt32LE(value[1], offset); + return offset + 8; +} + +function computeCheckSum(packetPlaintext,sendCounter,secretKeyBytes) { + let digest = crypto.createHash('sha256'); + let counter=new Buffer(8); + writeLI64(sendCounter,counter,0); + digest.update(counter); + digest.update(packetPlaintext); + digest.update(secretKeyBytes); + let hash = digest.digest(); + + return hash.slice(0,8); +} + + +function readX509PublicKey(key) { + var reader = new Ber.Reader(new Buffer(key, "base64")); + reader.readSequence(); + reader.readSequence(); + reader.readOID(); + reader.readOID(); + return new Buffer(reader.readString(Ber.BitString, true)).slice(1); +} + +function writeX509PublicKey(key) { + var writer = new Ber.Writer(); + writer.startSequence(); + writer.startSequence(); + writer.writeOID("1.2.840.10045.2.1"); + writer.writeOID("1.3.132.0.34"); + writer.endSequence(); + writer.writeBuffer(Buffer.concat([new Buffer([0x00]),key]),Ber.BitString); + writer.endSequence(); + return writer.buffer.toString("base64"); +} + const Transform = require('stream').Transform; const PUBLIC_KEY = 'MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V'; -var encryptionEnabled = false; -var sendCounter = 0; function createServer(options) { options = options || {}; @@ -57,41 +97,19 @@ function createServer(options) { server.maxPlayers = options['max-players'] || 20; server.playerCount = 0; - function readX509PublicKey(key) { - var reader = new Ber.Reader(new Buffer(key, "base64")); - reader.readSequence(); - reader.readSequence(); - reader.readOID(); - reader.readOID(); - return new Buffer(reader.readString(Ber.BitString, true)).slice(1); - } - - function writeX509PublicKey(key) { - var writer = new Ber.Writer(); - writer.startSequence(); - writer.startSequence(); - writer.writeOID("1.2.840.10045.2.1"); - writer.writeOID("1.3.132.0.34"); - writer.endSequence(); - writer.writeBuffer(Buffer.concat([new Buffer([0x00]),key]),Ber.BitString); - writer.endSequence(); - return writer.buffer.toString("base64"); - } server.on("connection", function(client) { - client.counter=0; + client.receiveCounter=0; + client.sendCounter=0; + + client.encryptionEnabled = false; - //if(encryptionEnabled) { - // client.on('mcpe', packet => { decipher.write(packet); }) - //} else { client.on("mcpe", packet => { console.log("actual mcpe",packet); client.emit(packet.name, packet.params) }); - //} - // decipher.on('data', data => console.log(data)) client.on("game_login", packet => { var body = packet.body; @@ -133,81 +151,68 @@ function createServer(options) { publicKey: pubKeyServer, serverToken: "SO SECRET VERY SECURE" // obviously, this is super secure (it's not, change it) }); - //console.log('secret', new Buffer(client.secretKeyBytes)); var decipher = crypto.createDecipheriv('aes-256-cfb8', client.secretKeyBytes, client.secretKeyBytes.slice(0,16)); let customPackets=JSON.parse(JSON.stringify(require("../data/protocol"))); customPackets['types']['encapsulated_packet'][1][1]['type'][1]['fields']['mcpe_encrypted'] = 'restBuffer'; customPackets['types']['encapsulated_packet'][1][0]['type'][1]['mappings']['0xfe'] = 'mcpe_encrypted'; client.encapsulatedPacketParser.proto.addTypes(merge(require('raknet').protocol, customPackets).types); - encryptionEnabled = true; + client.encryptionEnabled = true; client.on("mcpe_encrypted", packet => { - //console.log('THE CONSOLELOG') - //console.log(packet); - //console.log('---------------') + console.log("raw",packet); decipher.write(packet); }); decipher.on('data', data => console.log('decrypt', data)); - - function writeLI64(value, buffer, offset) { - buffer.writeInt32LE(value[0], offset+4); - buffer.writeInt32LE(value[1], offset); - return offset + 8; - } - - function computeCheckSum(packetPlaintext,sendCounter,secretKeyBytes) { - let digest = crypto.createHash('sha256'); - let counter=new Buffer(8); - writeLI64(sendCounter,counter,0); - digest.update(counter); - digest.update(packetPlaintext); - digest.update(secretKeyBytes); - let hash = digest.digest(); - - return hash.slice(0,8); - } + client.cipher=crypto.createCipheriv('aes-256-cfb8', client.secretKeyBytes, client.secretKeyBytes.slice(0,16)); const checksumTransform = new Transform({ transform(chunk,enc,cb) { const packet=chunk.slice(0,chunk.length-8); const checksum=chunk.slice(chunk.length-8); - const computedCheckSum=computeCheckSum(packet,[0,client.counter],client.secretKeyBytes); - const pass=checksum.toString("hex")==computedCheckSum.toString("hex"); + const computedCheckSum=computeCheckSum(packet,[0,client.receiveCounter],client.secretKeyBytes); //assert.equal(checksum.toString("hex"),computedCheckSum.toString("hex")); - client.counter++; - if(pass) this.push(packet); + client.receiveCounter++; + if(checksum.toString("hex")==computedCheckSum.toString("hex")) this.push(packet); cb(); } }); + client.addChecksumTransform = new Transform({ + transform(chunk,enc,cb) { + const packet=Buffer.concat([chunk,computeCheckSum(chunk,[0,client.sendCounter],client.secretKeyBytes)]); + client.sendCounter++; + this.push(packet); + cb(); + } + }); + + checksumTransform.on("data",data => console.log('sliced',data)); decipher.pipe(checksumTransform); const proto=new ProtoDef(); proto.addTypes(require("./datatypes/minecraft")); proto.addTypes(require("../data/protocol").types); - const mcpePacketParser=new Parser(proto,"mcpe_packet"); - checksumTransform.pipe(mcpePacketParser); - mcpePacketParser.on("data",parsed => console.log("parsed data",parsed)); - mcpePacketParser.on("data",parsed => client.emitPacket(parsed)); + client.mcpePacketParser=new Parser(proto,"mcpe_packet"); + client.mcpePacketSerializer=new Serializer(proto,"mcpe_packet"); + checksumTransform.pipe(client.mcpePacketParser); + client.mcpePacketParser.on("data",parsed => console.log("parsed data",parsed)); + client.mcpePacketParser.on("data",parsed => {if(parsed.data.name=="batch") parsed.data.name="batch_non_encrypted"; return client.emitPacket(parsed)}); - //client.on('mcpe', packet => { decipher.write(packet); }) - client.emit('login', { - displayName: client.displayName, - randomId: client.randomId, - skinData: client.skinData, - skinId: client.skinId, - identity: client.identity, - XUID: client.XUID - }); + client.mcpePacketSerializer.pipe(client.addChecksumTransform); + client.addChecksumTransform.pipe(client.cipher); }); - client.writeMCPE = (name, packet) => { - client.writeEncapsulated("mcpe", { - name: name, - params: packet - }); + client.writeMCPE = (name, params) => { + if(client.encryptionEnabled) { + client.mcpePacketSerializer.write({ name, params }); + client.cipher.on('data', data => console.log("crypted",data)); + client.cipher.on('data', data => client.writeEncapsulated("mcpe_encrypted", data)); + } + else { + client.writeEncapsulated("mcpe", { name, params }); + } }; client.writeBatch = function(packets) { @@ -222,10 +227,37 @@ function createServer(options) { client.on('batch', function(packet) { var buf = zlib.inflateSync(packet.payload); - console.log("in batch",buf); var packets = batchProto.parsePacketBuffer("insideBatch", buf).data; packets.forEach(packet => client.readEncapsulatedPacket(Buffer.concat([new Buffer([0xfe]), packet]))); }); + + client.on('client_to_server_handshake',() => { + console.log("plop"); + client.emit('login', { + displayName: client.displayName, + randomId: client.randomId, + skinData: client.skinData, + skinId: client.skinId, + identity: client.identity, + XUID: client.XUID + }); + }); + + + client.on('batch_non_encrypted', function(packet) { + var buf = zlib.inflateSync(packet.payload); + var packets = batchProto.parsePacketBuffer("insideBatch", buf).data; + packets.forEach(packet => { + try { + debug("handle mcpe",packet); + var r = client.mcpePacketParser.parsePacketBuffer(packet); + client.emitPacket(r); + } + catch(err) { + client.emit("error",err); + } + }); + }); }); return server; }