From cbaaeb8eb264b1d9d11f847f38b165f7a765575c Mon Sep 17 00:00:00 2001 From: deathcap Date: Mon, 25 Jan 2016 23:11:42 -0800 Subject: [PATCH 01/14] Move onEncryptionKeyRequest to client/encryption --- src/client/encrypt.js | 65 +++++++++++++++++++++++++++++++++++++++++++ src/createClient.js | 51 ++------------------------------- 2 files changed, 67 insertions(+), 49 deletions(-) create mode 100644 src/client/encrypt.js diff --git a/src/client/encrypt.js b/src/client/encrypt.js new file mode 100644 index 0000000..872dc63 --- /dev/null +++ b/src/client/encrypt.js @@ -0,0 +1,65 @@ +var crypto = require('crypto'); +var yggserver = require('yggdrasil').server({}); +var debug = require("../debug"); + +module.exports = function(client) { + client.once('encryption_begin', onEncryptionKeyRequest); + + function onEncryptionKeyRequest(packet) { + crypto.randomBytes(16, gotSharedSecret); + + function gotSharedSecret(err, sharedSecret) { + if(err) { + debug(err); + client.emit('error', err); + client.end(); + return; + } + if(haveCredentials) { + joinServerRequest(onJoinServerResponse); + } else { + if(packet.serverId != '-') { + debug('This server appears to be an online server and you are providing no password, the authentication will probably fail'); + } + sendEncryptionKeyResponse(); + } + + function onJoinServerResponse(err) { + if(err) { + client.emit('error', err); + client.end(); + } else { + sendEncryptionKeyResponse(); + } + } + + function joinServerRequest(cb) { + yggserver.join(accessToken, client.session.selectedProfile.id, + packet.serverId, sharedSecret, packet.publicKey, cb); + } + + function sendEncryptionKeyResponse() { + var pubKey = mcPubKeyToURsa(packet.publicKey); + var encryptedSharedSecretBuffer = pubKey.encrypt(sharedSecret, undefined, undefined, ursa.RSA_PKCS1_PADDING); + var encryptedVerifyTokenBuffer = pubKey.encrypt(packet.verifyToken, undefined, undefined, ursa.RSA_PKCS1_PADDING); + client.write('encryption_begin', { + sharedSecret: encryptedSharedSecretBuffer, + verifyToken: encryptedVerifyTokenBuffer + }); + client.setEncryption(sharedSecret); + } + } + } +}; + +function mcPubKeyToURsa(mcPubKeyBuffer) { + var pem = "-----BEGIN PUBLIC KEY-----\n"; + var base64PubKey = mcPubKeyBuffer.toString('base64'); + var maxLineLength = 65; + while(base64PubKey.length > 0) { + pem += base64PubKey.substring(0, maxLineLength) + "\n"; + base64PubKey = base64PubKey.substring(maxLineLength); + } + pem += "-----END PUBLIC KEY-----\n"; + return ursa.createPublicKey(pem, 'utf8'); +} diff --git a/src/createClient.js b/src/createClient.js index 83dc1b7..1bba4e3 100644 --- a/src/createClient.js +++ b/src/createClient.js @@ -3,12 +3,11 @@ var net = require('net'); var dns = require('dns'); var Client = require('./client'); var assert = require('assert'); -var crypto = require('crypto'); var yggdrasil = require('yggdrasil')({}); -var yggserver = require('yggdrasil').server({}); var states = require("./states"); var debug = require("./debug"); var UUID = require('uuid-1345'); +var encrypt = require('./client/encrypt'); module.exports=createClient; @@ -47,7 +46,7 @@ function createClient(options) { var client = new Client(false,version.majorVersion); client.on('connect', onConnect); if(keepAlive) client.on('keep_alive', onKeepAlive); - client.once('encryption_begin', onEncryptionKeyRequest); + encrypt(client); client.once('success', onLogin); client.once("compress", onCompressionRequest); client.on("set_compression", onCompressionRequest); @@ -114,52 +113,6 @@ function createClient(options) { }); } - function onEncryptionKeyRequest(packet) { - crypto.randomBytes(16, gotSharedSecret); - - function gotSharedSecret(err, sharedSecret) { - if(err) { - debug(err); - client.emit('error', err); - client.end(); - return; - } - if(haveCredentials) { - joinServerRequest(onJoinServerResponse); - } else { - if(packet.serverId != '-') { - debug('This server appears to be an online server and you are providing no password, the authentication will probably fail'); - } - sendEncryptionKeyResponse(); - } - - function onJoinServerResponse(err) { - if(err) { - client.emit('error', err); - client.end(); - } else { - sendEncryptionKeyResponse(); - } - } - - function joinServerRequest(cb) { - yggserver.join(accessToken, client.session.selectedProfile.id, - packet.serverId, sharedSecret, packet.publicKey, cb); - } - - function sendEncryptionKeyResponse() { - var pubKey = mcPubKeyToURsa(packet.publicKey); - var encryptedSharedSecretBuffer = pubKey.encrypt(sharedSecret, undefined, undefined, ursa.RSA_PKCS1_PADDING); - var encryptedVerifyTokenBuffer = pubKey.encrypt(packet.verifyToken, undefined, undefined, ursa.RSA_PKCS1_PADDING); - client.write('encryption_begin', { - sharedSecret: encryptedSharedSecretBuffer, - verifyToken: encryptedVerifyTokenBuffer - }); - client.setEncryption(sharedSecret); - } - } - } - function onLogin(packet) { client.state = states.PLAY; client.uuid = packet.uuid; From a727829a98455800b8e1c0223cf0c70556916173 Mon Sep 17 00:00:00 2001 From: deathcap Date: Mon, 25 Jan 2016 23:19:46 -0800 Subject: [PATCH 02/14] Move onKeepAlive to client/keepalive --- src/client/keepalive.js | 15 +++++++++++++++ src/createClient.js | 26 ++------------------------ 2 files changed, 17 insertions(+), 24 deletions(-) create mode 100644 src/client/keepalive.js diff --git a/src/client/keepalive.js b/src/client/keepalive.js new file mode 100644 index 0000000..59b9b88 --- /dev/null +++ b/src/client/keepalive.js @@ -0,0 +1,15 @@ +module.exports = function(client) { + client.on('keep_alive', onKeepAlive); + + var timeout = null; + + function onKeepAlive(packet) { + if (timeout) + clearTimeout(timeout); + timeout = setTimeout(() => client.end(), checkTimeoutInterval); + client.write('keep_alive', { + keepAliveId: packet.keepAliveId + }); + } + +}; diff --git a/src/createClient.js b/src/createClient.js index 1bba4e3..11f343c 100644 --- a/src/createClient.js +++ b/src/createClient.js @@ -8,6 +8,7 @@ var states = require("./states"); var debug = require("./debug"); var UUID = require('uuid-1345'); var encrypt = require('./client/encrypt'); +var keepalive = require('./client/keepalive'); module.exports=createClient; @@ -45,7 +46,7 @@ function createClient(options) { var client = new Client(false,version.majorVersion); client.on('connect', onConnect); - if(keepAlive) client.on('keep_alive', onKeepAlive); + if(keepAlive) keepalive(client); encrypt(client); client.once('success', onLogin); client.once("compress", onCompressionRequest); @@ -85,7 +86,6 @@ function createClient(options) { client.connect(port, host); } - var timeout = null; return client; function onConnect() { @@ -104,14 +104,6 @@ function createClient(options) { function onCompressionRequest(packet) { client.compressionThreshold = packet.threshold; } - function onKeepAlive(packet) { - if (timeout) - clearTimeout(timeout); - timeout = setTimeout(() => client.end(), checkTimeoutInterval); - client.write('keep_alive', { - keepAliveId: packet.keepAliveId - }); - } function onLogin(packet) { client.state = states.PLAY; @@ -119,17 +111,3 @@ function createClient(options) { client.username = packet.username; } } - - - -function mcPubKeyToURsa(mcPubKeyBuffer) { - var pem = "-----BEGIN PUBLIC KEY-----\n"; - var base64PubKey = mcPubKeyBuffer.toString('base64'); - var maxLineLength = 65; - while(base64PubKey.length > 0) { - pem += base64PubKey.substring(0, maxLineLength) + "\n"; - base64PubKey = base64PubKey.substring(maxLineLength); - } - pem += "-----END PUBLIC KEY-----\n"; - return ursa.createPublicKey(pem, 'utf8'); -} From 20e076ebcfce2706d3f5b9b59ff27621aeb04b8f Mon Sep 17 00:00:00 2001 From: deathcap Date: Mon, 25 Jan 2016 23:22:25 -0800 Subject: [PATCH 03/14] Move onCompressionRequest to client/compress --- src/client/compress.js | 9 +++++++++ src/createClient.js | 8 ++------ 2 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 src/client/compress.js diff --git a/src/client/compress.js b/src/client/compress.js new file mode 100644 index 0000000..40d092f --- /dev/null +++ b/src/client/compress.js @@ -0,0 +1,9 @@ +module.exports = function(client) { + client.once("compress", onCompressionRequest); + client.on("set_compression", onCompressionRequest); + + function onCompressionRequest(packet) { + client.compressionThreshold = packet.threshold; + } + // TODO: refactor with transforms/compression.js -- enable it here +}; diff --git a/src/createClient.js b/src/createClient.js index 11f343c..031e2d0 100644 --- a/src/createClient.js +++ b/src/createClient.js @@ -9,6 +9,7 @@ var debug = require("./debug"); var UUID = require('uuid-1345'); var encrypt = require('./client/encrypt'); var keepalive = require('./client/keepalive'); +var compress = require('./client/compress'); module.exports=createClient; @@ -49,8 +50,7 @@ function createClient(options) { if(keepAlive) keepalive(client); encrypt(client); client.once('success', onLogin); - client.once("compress", onCompressionRequest); - client.on("set_compression", onCompressionRequest); + compress(client); if(haveCredentials) { // make a request to get the case-correct username before connecting. var cb = function(err, session) { @@ -101,10 +101,6 @@ function createClient(options) { }); } - function onCompressionRequest(packet) { - client.compressionThreshold = packet.threshold; - } - function onLogin(packet) { client.state = states.PLAY; client.uuid = packet.uuid; From 8c4406b4a2b8e6edfc49f1bd71b0a49592fa4d04 Mon Sep 17 00:00:00 2001 From: deathcap Date: Mon, 25 Jan 2016 23:24:20 -0800 Subject: [PATCH 04/14] Move keepAlive/checkTimeoutInterval to client/keepalive --- src/client/keepalive.js | 7 ++++++- src/createClient.js | 4 +--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/client/keepalive.js b/src/client/keepalive.js index 59b9b88..3fc817b 100644 --- a/src/client/keepalive.js +++ b/src/client/keepalive.js @@ -1,4 +1,9 @@ -module.exports = function(client) { +module.exports = function(client, options) { + var keepAlive = options.keepAlive == null ? true : options.keepAlive; + if (!keepAlive) return; + + var checkTimeoutInterval = options.checkTimeoutInterval || 10 * 1000; + client.on('keep_alive', onKeepAlive); var timeout = null; diff --git a/src/createClient.js b/src/createClient.js index 031e2d0..5e7ac3f 100644 --- a/src/createClient.js +++ b/src/createClient.js @@ -37,8 +37,6 @@ function createClient(options) { assert.ok(options.username, "username is required"); var haveCredentials = options.password != null || (clientToken != null && options.session != null); - var keepAlive = options.keepAlive == null ? true : options.keepAlive; - var checkTimeoutInterval = options.checkTimeoutInterval || 10 * 1000; var optVersion = options.version || require("./version").defaultVersion; var mcData=require("minecraft-data")(optVersion); @@ -47,7 +45,7 @@ function createClient(options) { var client = new Client(false,version.majorVersion); client.on('connect', onConnect); - if(keepAlive) keepalive(client); + keepalive(client, options); encrypt(client); client.once('success', onLogin); compress(client); From 821227d73e68dea365bcd59d8b91a91453154675 Mon Sep 17 00:00:00 2001 From: deathcap Date: Mon, 25 Jan 2016 23:28:14 -0800 Subject: [PATCH 05/14] Move making a request to get case-correct username before connecting to client/caseCorrect --- src/client/caseCorrect.js | 43 +++++++++++++++++++++++++++++++++ src/createClient.js | 50 ++++++--------------------------------- 2 files changed, 50 insertions(+), 43 deletions(-) create mode 100644 src/client/caseCorrect.js diff --git a/src/client/caseCorrect.js b/src/client/caseCorrect.js new file mode 100644 index 0000000..8b99b72 --- /dev/null +++ b/src/client/caseCorrect.js @@ -0,0 +1,43 @@ +var yggdrasil = require('yggdrasil')({}); +var UUID = require('uuid-1345'); + +module.exports = function(client, options) { + var clientToken = options.clientToken || UUID.v4().toString(); + var accessToken; + var haveCredentials = options.password != null || (clientToken != null && options.session != null); + + if(haveCredentials) { + // make a request to get the case-correct username before connecting. + var cb = function(err, session) { + if(err) { + client.emit('error', err); + } else { + client.session = session; + client.username = session.selectedProfile.name; + accessToken = session.accessToken; + client.emit('session'); + client.connect(options.port, options.host); + } + }; + + if (options.session) { + yggdrasil.validate(options.session.accessToken, function(ok) { + if (ok) + cb(null, options.session); + else + yggdrasil.refresh(options.session.accessToken, options.session.clientToken, function(err, _, data) { + cb(err, data); + }); + }); + } + else yggdrasil.auth({ + user: options.username, + pass: options.password, + token: clientToken + }, cb); + } else { + // assume the server is in offline mode and just go for it. + client.username = options.username; + client.connect(options.port, options.host); + } +}; diff --git a/src/createClient.js b/src/createClient.js index 5e7ac3f..b77401e 100644 --- a/src/createClient.js +++ b/src/createClient.js @@ -3,13 +3,13 @@ var net = require('net'); var dns = require('dns'); var Client = require('./client'); var assert = require('assert'); -var yggdrasil = require('yggdrasil')({}); var states = require("./states"); var debug = require("./debug"); -var UUID = require('uuid-1345'); + var encrypt = require('./client/encrypt'); var keepalive = require('./client/keepalive'); var compress = require('./client/compress'); +var caseCorrect = require('./client/caseCorrect'); module.exports=createClient; @@ -30,13 +30,10 @@ Client.prototype.connect = function(port, host) { function createClient(options) { assert.ok(options, "options is required"); - var port = options.port || 25565; - var host = options.host || 'localhost'; - var clientToken = options.clientToken || UUID.v4().toString(); - var accessToken; + options.port = options.port || 25565; + options.host = options.host || 'localhost'; assert.ok(options.username, "username is required"); - var haveCredentials = options.password != null || (clientToken != null && options.session != null); var optVersion = options.version || require("./version").defaultVersion; var mcData=require("minecraft-data")(optVersion); @@ -49,48 +46,15 @@ function createClient(options) { encrypt(client); client.once('success', onLogin); compress(client); - if(haveCredentials) { - // make a request to get the case-correct username before connecting. - var cb = function(err, session) { - if(err) { - client.emit('error', err); - } else { - client.session = session; - client.username = session.selectedProfile.name; - accessToken = session.accessToken; - client.emit('session'); - client.connect(port, host); - } - }; - - if (options.session) { - yggdrasil.validate(options.session.accessToken, function(ok) { - if (ok) - cb(null, options.session); - else - yggdrasil.refresh(options.session.accessToken, options.session.clientToken, function(err, _, data) { - cb(err, data); - }); - }); - } - else yggdrasil.auth({ - user: options.username, - pass: options.password, - token: clientToken - }, cb); - } else { - // assume the server is in offline mode and just go for it. - client.username = options.username; - client.connect(port, host); - } + caseCorrect(client, options); return client; function onConnect() { client.write('set_protocol', { protocolVersion: version.version, - serverHost: host, - serverPort: port, + serverHost: options.host, + serverPort: options.port, nextState: 2 }); client.state = states.LOGIN; From b37dcc4d08aa2b926bbd391d521a470ef8f9cd8c Mon Sep 17 00:00:00 2001 From: deathcap Date: Mon, 25 Jan 2016 23:33:58 -0800 Subject: [PATCH 06/14] Move onConnect/set_protocol to client/setProtocol --- src/client/setProtocol.js | 21 +++++++++++++++++++++ src/createClient.js | 20 +++++--------------- 2 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 src/client/setProtocol.js diff --git a/src/client/setProtocol.js b/src/client/setProtocol.js new file mode 100644 index 0000000..865b44e --- /dev/null +++ b/src/client/setProtocol.js @@ -0,0 +1,21 @@ + +var states = require("../states"); + +module.exports = function(client, options) { + client.on('connect', onConnect); + + function onConnect() { + client.write('set_protocol', { + protocolVersion: options.protocolVersion, + serverHost: options.host, + serverPort: options.port, + nextState: 2 + }); + client.state = states.LOGIN; + client.write('login_start', { + username: client.username + }); + } + + +} diff --git a/src/createClient.js b/src/createClient.js index b77401e..97b6bc4 100644 --- a/src/createClient.js +++ b/src/createClient.js @@ -10,6 +10,7 @@ var encrypt = require('./client/encrypt'); var keepalive = require('./client/keepalive'); var compress = require('./client/compress'); var caseCorrect = require('./client/caseCorrect'); +var setProtocol = require('./client/setProtocol'); module.exports=createClient; @@ -38,10 +39,12 @@ function createClient(options) { var optVersion = options.version || require("./version").defaultVersion; var mcData=require("minecraft-data")(optVersion); var version = mcData.version; + options.majorVersion = version.majorVersion; + options.protocolVersion = version.version; - var client = new Client(false,version.majorVersion); - client.on('connect', onConnect); + var client = new Client(false, options.majorVersion); + setProtocol(client, options); keepalive(client, options); encrypt(client); client.once('success', onLogin); @@ -50,19 +53,6 @@ function createClient(options) { return client; - function onConnect() { - client.write('set_protocol', { - protocolVersion: version.version, - serverHost: options.host, - serverPort: options.port, - nextState: 2 - }); - client.state = states.LOGIN; - client.write('login_start', { - username: client.username - }); - } - function onLogin(packet) { client.state = states.PLAY; client.uuid = packet.uuid; From 34534b2aa687d415f588114407a911b22d6dd91d Mon Sep 17 00:00:00 2001 From: deathcap Date: Mon, 25 Jan 2016 23:36:36 -0800 Subject: [PATCH 07/14] Move onLogin set state=PLAY to client/play --- src/client/play.js | 11 +++++++++++ src/createClient.js | 12 +++--------- 2 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 src/client/play.js diff --git a/src/client/play.js b/src/client/play.js new file mode 100644 index 0000000..ebc491e --- /dev/null +++ b/src/client/play.js @@ -0,0 +1,11 @@ +var states = require("../states"); + +module.exports = function(client, options) { + client.once('success', onLogin); + + function onLogin(packet) { + client.state = states.PLAY; + client.uuid = packet.uuid; + client.username = packet.username; + } +}; diff --git a/src/createClient.js b/src/createClient.js index 97b6bc4..e73bce8 100644 --- a/src/createClient.js +++ b/src/createClient.js @@ -3,7 +3,6 @@ var net = require('net'); var dns = require('dns'); var Client = require('./client'); var assert = require('assert'); -var states = require("./states"); var debug = require("./debug"); var encrypt = require('./client/encrypt'); @@ -11,6 +10,7 @@ var keepalive = require('./client/keepalive'); var compress = require('./client/compress'); var caseCorrect = require('./client/caseCorrect'); var setProtocol = require('./client/setProtocol'); +var play = require('./client/play'); module.exports=createClient; @@ -42,20 +42,14 @@ function createClient(options) { options.majorVersion = version.majorVersion; options.protocolVersion = version.version; - var client = new Client(false, options.majorVersion); + setProtocol(client, options); keepalive(client, options); encrypt(client); - client.once('success', onLogin); + play(client); compress(client); caseCorrect(client, options); return client; - - function onLogin(packet) { - client.state = states.PLAY; - client.uuid = packet.uuid; - client.username = packet.username; - } } From 9c2112802a7c03929b4f3eee0ad94f1d1ff59c48 Mon Sep 17 00:00:00 2001 From: deathcap Date: Mon, 25 Jan 2016 23:40:35 -0800 Subject: [PATCH 08/14] Fix ursa imports --- src/client/encrypt.js | 1 + src/createClient.js | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/client/encrypt.js b/src/client/encrypt.js index 872dc63..654493f 100644 --- a/src/client/encrypt.js +++ b/src/client/encrypt.js @@ -1,5 +1,6 @@ var crypto = require('crypto'); var yggserver = require('yggdrasil').server({}); +var ursa=require("../ursa"); var debug = require("../debug"); module.exports = function(client) { diff --git a/src/createClient.js b/src/createClient.js index e73bce8..d610c1c 100644 --- a/src/createClient.js +++ b/src/createClient.js @@ -1,9 +1,7 @@ -var ursa=require("./ursa"); var net = require('net'); var dns = require('dns'); var Client = require('./client'); var assert = require('assert'); -var debug = require("./debug"); var encrypt = require('./client/encrypt'); var keepalive = require('./client/keepalive'); From de36de84960d664393b9da82aa59bbdfa972b08f Mon Sep 17 00:00:00 2001 From: deathcap Date: Mon, 25 Jan 2016 23:47:23 -0800 Subject: [PATCH 09/14] Break client/caseCorrect dependence on TCP/IP host/port; generalize options.connect(client) --- src/client/caseCorrect.js | 4 ++-- src/createClient.js | 29 ++++++++++++++--------------- src/ping.js | 1 + 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/client/caseCorrect.js b/src/client/caseCorrect.js index 8b99b72..d27f4b9 100644 --- a/src/client/caseCorrect.js +++ b/src/client/caseCorrect.js @@ -16,7 +16,7 @@ module.exports = function(client, options) { client.username = session.selectedProfile.name; accessToken = session.accessToken; client.emit('session'); - client.connect(options.port, options.host); + options.connect(client); } }; @@ -38,6 +38,6 @@ module.exports = function(client, options) { } else { // assume the server is in offline mode and just go for it. client.username = options.username; - client.connect(options.port, options.host); + options.connect(client); } }; diff --git a/src/createClient.js b/src/createClient.js index d610c1c..201124b 100644 --- a/src/createClient.js +++ b/src/createClient.js @@ -12,26 +12,25 @@ var play = require('./client/play'); module.exports=createClient; -Client.prototype.connect = function(port, host) { - var self = this; - if(port == 25565 && net.isIP(host) === 0) { - dns.resolveSrv("_minecraft._tcp." + host, function(err, addresses) { - if(addresses && addresses.length > 0) { - self.setSocket(net.connect(addresses[0].port, addresses[0].name)); - } else { - self.setSocket(net.connect(port, host)); - } - }); - } else { - self.setSocket(net.connect(port, host)); - } -}; - function createClient(options) { assert.ok(options, "options is required"); options.port = options.port || 25565; options.host = options.host || 'localhost'; + options.connect = (client) => { + if(options.port == 25565 && net.isIP(host) === 0) { + dns.resolveSrv("_minecraft._tcp." + options.host, function(err, addresses) { + if(addresses && addresses.length > 0) { + client.setSocket(net.connect(addresses[0].port, addresses[0].name)); + } else { + client.setSocket(net.connect(options.port, options.host)); + } + }); + } else { + client.setSocket(net.connect(options.port, options.host)); + } + }; + assert.ok(options.username, "username is required"); var optVersion = options.version || require("./version").defaultVersion; diff --git a/src/ping.js b/src/ping.js index cf03f2f..c61bd8f 100644 --- a/src/ping.js +++ b/src/ping.js @@ -42,5 +42,6 @@ function ping(options, cb) { client.state = states.STATUS; }); + // TODO: update client.connect(port, host); } From fe327c85e132701f9f19a50fb2c92d67fb1b6b31 Mon Sep 17 00:00:00 2001 From: deathcap Date: Mon, 25 Jan 2016 23:57:10 -0800 Subject: [PATCH 10/14] Move net.socket() and dns.resolveSrv() into client/net_tcp This has been the trickest to refactor/remodularize, since it used to modify the Client prototype to augument connect(host, port). This added a dependence on TCP/IP host/port socketpairs, refactor to add a 'connect' option instead, called with the client instance. Update ping accordingly, but it has to call connect itself since it does not include client/caseCorrect, which normally calls connect. Still may be able to improve further and untangle these interdependencies cleanlier. --- src/client/tcp_dns.js | 21 +++++++++++++++++++++ src/createClient.js | 21 ++------------------- src/ping.js | 20 ++++++++++++-------- 3 files changed, 35 insertions(+), 27 deletions(-) create mode 100644 src/client/tcp_dns.js diff --git a/src/client/tcp_dns.js b/src/client/tcp_dns.js new file mode 100644 index 0000000..4ef5696 --- /dev/null +++ b/src/client/tcp_dns.js @@ -0,0 +1,21 @@ +var net = require('net'); +var dns = require('dns'); + +module.exports = function(client, options) { + options.port = options.port || 25565; + options.host = options.host || 'localhost'; + + options.connect = (client) => { + if(options.port == 25565 && net.isIP(host) === 0) { + dns.resolveSrv("_minecraft._tcp." + options.host, function(err, addresses) { + if(addresses && addresses.length > 0) { + client.setSocket(net.connect(addresses[0].port, addresses[0].name)); + } else { + client.setSocket(net.connect(options.port, options.host)); + } + }); + } else { + client.setSocket(net.connect(options.port, options.host)); + } + }; +}; diff --git a/src/createClient.js b/src/createClient.js index 201124b..528e417 100644 --- a/src/createClient.js +++ b/src/createClient.js @@ -1,5 +1,3 @@ -var net = require('net'); -var dns = require('dns'); var Client = require('./client'); var assert = require('assert'); @@ -9,28 +7,12 @@ var compress = require('./client/compress'); var caseCorrect = require('./client/caseCorrect'); var setProtocol = require('./client/setProtocol'); var play = require('./client/play'); +var tcp_dns = require('./client/tcp_dns'); module.exports=createClient; function createClient(options) { assert.ok(options, "options is required"); - options.port = options.port || 25565; - options.host = options.host || 'localhost'; - - options.connect = (client) => { - if(options.port == 25565 && net.isIP(host) === 0) { - dns.resolveSrv("_minecraft._tcp." + options.host, function(err, addresses) { - if(addresses && addresses.length > 0) { - client.setSocket(net.connect(addresses[0].port, addresses[0].name)); - } else { - client.setSocket(net.connect(options.port, options.host)); - } - }); - } else { - client.setSocket(net.connect(options.port, options.host)); - } - }; - assert.ok(options.username, "username is required"); var optVersion = options.version || require("./version").defaultVersion; @@ -41,6 +23,7 @@ function createClient(options) { var client = new Client(false, options.majorVersion); + tcp_dns(client, options); setProtocol(client, options); keepalive(client, options); encrypt(client); diff --git a/src/ping.js b/src/ping.js index c61bd8f..4a58353 100644 --- a/src/ping.js +++ b/src/ping.js @@ -1,17 +1,20 @@ var net = require('net'); var Client = require('./client'); var states = require("./states"); +var tcp_dns = require('./client/tcp_dns'); module.exports = ping; function ping(options, cb) { - var host = options.host || 'localhost'; - var port = options.port || 25565; + options.host = options.host || 'localhost'; + options.port = options.port || 25565; var optVersion = options.version || require("./version").defaultVersion; var mcData=require("minecraft-data")(optVersion); var version = mcData.version; + options.majorVersion = version.majorVersion; + options.protocolVersion = version.version; - var client = new Client(false,version.majorVersion); + var client = new Client(false,options.majorVersion); client.on('error', function(err) { cb(err); }); @@ -32,16 +35,17 @@ function ping(options, cb) { client.write('ping_start', {}); }); + // TODO: refactor with src/client/setProtocol.js client.on('connect', function() { client.write('set_protocol', { - protocolVersion: version.version, - serverHost: host, - serverPort: port, + protocolVersion: options.protocolVersion, + serverHost: options.host, + serverPort: options.port, nextState: 1 }); client.state = states.STATUS; }); - // TODO: update - client.connect(port, host); + tcp_dns(client, options); + options.connect(client); } From cf5f8c1c4c0516beac60337b6feba99183d5e443 Mon Sep 17 00:00:00 2001 From: deathcap Date: Tue, 26 Jan 2016 00:01:09 -0800 Subject: [PATCH 11/14] Pass options to all src/client/*, for consistency --- src/client/compress.js | 2 +- src/client/encrypt.js | 2 +- src/createClient.js | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/client/compress.js b/src/client/compress.js index 40d092f..cebe34f 100644 --- a/src/client/compress.js +++ b/src/client/compress.js @@ -1,4 +1,4 @@ -module.exports = function(client) { +module.exports = function(client, options) { client.once("compress", onCompressionRequest); client.on("set_compression", onCompressionRequest); diff --git a/src/client/encrypt.js b/src/client/encrypt.js index 654493f..c5c4443 100644 --- a/src/client/encrypt.js +++ b/src/client/encrypt.js @@ -3,7 +3,7 @@ var yggserver = require('yggdrasil').server({}); var ursa=require("../ursa"); var debug = require("../debug"); -module.exports = function(client) { +module.exports = function(client, options) { client.once('encryption_begin', onEncryptionKeyRequest); function onEncryptionKeyRequest(packet) { diff --git a/src/createClient.js b/src/createClient.js index 528e417..40a3d13 100644 --- a/src/createClient.js +++ b/src/createClient.js @@ -26,9 +26,9 @@ function createClient(options) { tcp_dns(client, options); setProtocol(client, options); keepalive(client, options); - encrypt(client); - play(client); - compress(client); + encrypt(client, options); + play(client, options); + compress(client, options); caseCorrect(client, options); return client; From 2f60088fdf90b3b5f547f287e848aecc03b7bcf3 Mon Sep 17 00:00:00 2001 From: deathcap Date: Tue, 26 Jan 2016 00:47:21 -0800 Subject: [PATCH 12/14] Fix missing tcp_dns host option --- src/client/tcp_dns.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/tcp_dns.js b/src/client/tcp_dns.js index 4ef5696..a8e3060 100644 --- a/src/client/tcp_dns.js +++ b/src/client/tcp_dns.js @@ -6,7 +6,7 @@ module.exports = function(client, options) { options.host = options.host || 'localhost'; options.connect = (client) => { - if(options.port == 25565 && net.isIP(host) === 0) { + if(options.port == 25565 && net.isIP(options.host) === 0) { dns.resolveSrv("_minecraft._tcp." + options.host, function(err, addresses) { if(addresses && addresses.length > 0) { client.setSocket(net.connect(addresses[0].port, addresses[0].name)); From bf5ce9e56937b53f0a9e6a9b03a51c8552d066f1 Mon Sep 17 00:00:00 2001 From: deathcap Date: Tue, 26 Jan 2016 00:51:44 -0800 Subject: [PATCH 13/14] Pass haveCredentials/accessToken between client/caseCorrect and client/encrypt --- src/client/caseCorrect.js | 8 ++++---- src/client/encrypt.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/client/caseCorrect.js b/src/client/caseCorrect.js index d27f4b9..2eb634a 100644 --- a/src/client/caseCorrect.js +++ b/src/client/caseCorrect.js @@ -3,10 +3,10 @@ var UUID = require('uuid-1345'); module.exports = function(client, options) { var clientToken = options.clientToken || UUID.v4().toString(); - var accessToken; - var haveCredentials = options.password != null || (clientToken != null && options.session != null); + options.accessToken = null; + options.haveCredentials = options.password != null || (clientToken != null && options.session != null); - if(haveCredentials) { + if(options.haveCredentials) { // make a request to get the case-correct username before connecting. var cb = function(err, session) { if(err) { @@ -14,7 +14,7 @@ module.exports = function(client, options) { } else { client.session = session; client.username = session.selectedProfile.name; - accessToken = session.accessToken; + options.accessToken = session.accessToken; client.emit('session'); options.connect(client); } diff --git a/src/client/encrypt.js b/src/client/encrypt.js index c5c4443..696ddfc 100644 --- a/src/client/encrypt.js +++ b/src/client/encrypt.js @@ -16,7 +16,7 @@ module.exports = function(client, options) { client.end(); return; } - if(haveCredentials) { + if(options.haveCredentials) { joinServerRequest(onJoinServerResponse); } else { if(packet.serverId != '-') { @@ -35,7 +35,7 @@ module.exports = function(client, options) { } function joinServerRequest(cb) { - yggserver.join(accessToken, client.session.selectedProfile.id, + yggserver.join(options.accessToken, client.session.selectedProfile.id, packet.serverId, sharedSecret, packet.publicKey, cb); } From c3d86d7674fd44a5a40078f89c5931034b561def Mon Sep 17 00:00:00 2001 From: deathcap Date: Tue, 26 Jan 2016 01:05:32 -0800 Subject: [PATCH 14/14] Add client.connect(port, host) for backwards-compatibility Would like to remove this method because it is hardcoded to dependent on TCP/IP, but the serverTest uses it. Load tcp_dns as needed. --- src/client.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/client.js b/src/client.js index 200d738..8a0a129 100644 --- a/src/client.js +++ b/src/client.js @@ -215,6 +215,13 @@ class Client extends EventEmitter else this.compressor.write(buffer); } + + // TCP/IP-specific (not generic Stream) method for backwards-compatibility + connect(port, host) { + var options = {port, host}; + require('./client/tcp_dns')(this, options); + options.connect(this); + } } module.exports = Client;