From 39d00c6b6bb856ed126c052598418e068ca3f9d2 Mon Sep 17 00:00:00 2001 From: Gamma Squared Date: Tue, 20 Jan 2015 11:46:28 +0100 Subject: [PATCH 1/4] Switched from net to tls --- .gitignore | 3 ++- CHANGELOG.md | 7 +++++++ README.md | 8 ++++---- lib/Peer.js | 18 +++++++++++++----- lib/settings.js | 8 ++++++++ test/peer.test.js | 15 ++++++++++----- 6 files changed, 44 insertions(+), 15 deletions(-) create mode 100644 lib/settings.js diff --git a/.gitignore b/.gitignore index 9303c34..7becbf7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ -npm-debug.log \ No newline at end of file +npm-debug.log +*.pem \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index aad0a9f..56c50e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +0.1.2 / 2015-01-20 +------------------ +* Switched to tls-encrypted connections. + +------------------ +Pre Fork: + 0.1.1 / 2014-01-22 ------------------ * Add `stateChange` events when Peer state changes. diff --git a/README.md b/README.md index 253e0d2..a5934e9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # P2P Node -Low-level library to handle peer-to-peer traffic on Cryptcurrency networks. A raw `socket` object in Node emits `data` events whenever the stream is updated. This library sits on top of a raw socket connection, and instead of emitting `data` events every time the stream updates, it waits and emits `message` events whenever a complete message has arrived. +Low-level library to handle peer-to-peer traffic on Cryptcurrency networks. A raw `socket` object in Node emits `data` events whenever the stream is updated. This library sits on top of a raw tls connection, and instead of emitting `data` events every time the stream updates, it waits and emits `message` events whenever a complete message has arrived. The tls connection is then wrapped as a simple socket connection. It uses the [Bitcoin protocol structure](https://en.bitcoin.it/wiki/Protocol_specification#Message_structure) to parse incoming messages; any stream that's encoded as follows can be parsed: @@ -30,7 +30,7 @@ p.on('message', function(d) { ## Events ### `connect` -When the socket connects +When the socket connects/the connection is established Data object passed to listeners: @@ -41,14 +41,14 @@ Data object passed to listeners: ``` ### `error` -If the socket errors out +If the socket/connection errors out Data object passed to listeners: ``` { peer: Peer, - error: Error object from Socket + error: Error object from Stream } ``` diff --git a/lib/Peer.js b/lib/Peer.js index 7cfa32a..e5db22b 100644 --- a/lib/Peer.js +++ b/lib/Peer.js @@ -1,4 +1,6 @@ var net = require('net'); +var tls = require('tls'); +var settings = require('./settings') var events = require('events'); var util = require('util'); var sha256 = require('crypto-hashing').sha256; @@ -115,13 +117,19 @@ util.inherits(Peer, events.EventEmitter); Peer.prototype.MAX_RECEIVE_BUFFER = 1024*1024*10; Peer.prototype.magicBytes = 0xD9B4BEF9; -Peer.prototype.connect = function connect(socket) { +Peer.prototype.connect = function connect(clTxtStream) { this.state = 'connecting'; this.inbound = new Buffer(this.MAX_RECEIVE_BUFFER); this.inboundCursor = 0; - if (typeof socket === 'undefined' || !(socket instanceof net.Socket)) { - socket = net.createConnection(this.host.port, this.host.host, this.handleConnect.bind(this)); + var target_proto = new tls.createSecurePair().cleartext.__proto__ + + if (typeof clTxtStream === 'undefined' || !(clTxtStream.__proto__ === target_proto)) { + var options = settings.TLS_connection_options; + options.host = this.host.host; + options.port = this.host.port; + clTxtStream = tls.connect(options, this.handleConnect.bind(this)); + this.state='connected' } else { this.state = 'connected'; // Binding to an already-connected socket; will not fire a 'connect' event, but will still fire a 'stateChange' event } @@ -129,7 +137,7 @@ Peer.prototype.connect = function connect(socket) { enumerable: false, configurable: false, writable:false, - value: socket + value: clTxtStream }); this.socket.on('error', this.handleError.bind(this)); this.socket.on('data', this.handleData.bind(this)); @@ -201,7 +209,7 @@ Peer.prototype.send = function send(command, data, callback) { var checksum = this.messageChecksum(data); checksum.copy(out, 20); // checksum data.copy(out, 24); - + this.socket.write(out, null, callback); }; diff --git a/lib/settings.js b/lib/settings.js new file mode 100644 index 0000000..afed413 --- /dev/null +++ b/lib/settings.js @@ -0,0 +1,8 @@ +var fs = require('fs') + +module.exports={ + TLS_connection_options: { + key: fs.readFileSync('lib/key.pem'), + cert: fs.readFileSync('lib/cert.pem') + } +} \ No newline at end of file diff --git a/test/peer.test.js b/test/peer.test.js index bb3cd82..5cabf5e 100644 --- a/test/peer.test.js +++ b/test/peer.test.js @@ -1,13 +1,18 @@ var Peer = require('../lib/Peer').Peer; var assert = require("assert"); +var settings = require('../lib/settings') +var tls = require('tls'); var net = require('net'); +var suboptions = settings.TLS_connection_options; + describe('P2P Peer', function() { it('should properly connect to indicated host', function(done) { var localPeer = false; - var server = net.createServer(function(socket) { + var server = tls.createServer(suboptions, function(clTxtStream) { server.close(); - localPeer.destroy(); + clTxtStream.destroy() + localPeer.destroy() done(); }); server.listen(function() { @@ -23,9 +28,9 @@ describe('P2P Peer', function() { beforeEach(function(done) { serverPeer = false; - server = net.createServer(function(socket) { - serverPeer = new Peer(socket.remoteAddress, socket.remotePort, magic); - serverPeer.connect(socket); + server = tls.createServer(suboptions, function(clTxtStream) { + serverPeer = new Peer(clTxtStream.remoteAddress, clTxtStream.remotePort, magic); + serverPeer.connect(clTxtStream); }); localPeer = false; server.listen(function() { From cdb7fc472267e8d5919d90e9c0ace38074884701 Mon Sep 17 00:00:00 2001 From: Gamma Squared Date: Tue, 27 Jan 2015 13:25:13 +0100 Subject: [PATCH 2/4] Solved timing issiues; using eventhandler .on('connect', disconnect) --- lib/Peer.js | 18 +++++++++++++----- lib/settings.js | 5 +++-- test/peer.test.js | 9 ++++----- test/test-settings.js | 10 ++++++++++ 4 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 test/test-settings.js diff --git a/lib/Peer.js b/lib/Peer.js index e5db22b..78ac381 100644 --- a/lib/Peer.js +++ b/lib/Peer.js @@ -9,7 +9,7 @@ var Host = function Host(host, port) { var _host = false, // Private variables _port = false, _version = false; - + Object.defineProperties(this, { 'host': { get: function() { return _host; }, @@ -24,12 +24,12 @@ var Host = function Host(host, port) { enumerable: true } }); - + if (Array.isArray(host)) { host = new Buffer(host); } _port = +port || this.defaultPort; - + if (typeof host === 'undefined') { _host = 'localhost'; _version = 4; @@ -96,7 +96,7 @@ var Peer = exports.Peer = function Peer(host, port, magic) { value: host }); if (typeof magic !== 'undefined') this.magicBytes = magic; - + var myState = 'new'; Object.defineProperty(this, 'state', { enumerable: true, @@ -104,6 +104,7 @@ var Peer = exports.Peer = function Peer(host, port, magic) { return myState; }, set: function(newValue) { + console.log(newValue) var oldState = myState; this.emit('stateChange', {new: newValue, old: oldState}); myState = newValue; @@ -129,7 +130,6 @@ Peer.prototype.connect = function connect(clTxtStream) { options.host = this.host.host; options.port = this.host.port; clTxtStream = tls.connect(options, this.handleConnect.bind(this)); - this.state='connected' } else { this.state = 'connected'; // Binding to an already-connected socket; will not fire a 'connect' event, but will still fire a 'stateChange' event } @@ -148,11 +148,19 @@ Peer.prototype.connect = function connect(clTxtStream) { }; Peer.prototype.disconnect = function disconnect() { + if(this.state != 'connected'){ + this.on('connect', function(){this.disconnect();}); + return; + } this.state = 'disconnecting'; this.socket.end(); // Inform the other end we're going away }; Peer.prototype.destroy = function destroy() { + if(this.state != 'connected'){ + this.on('connect', function(){console.log(" CALLED!!! "); this.disconnect();}); + return; + } this.socket.destroy(); }; diff --git a/lib/settings.js b/lib/settings.js index afed413..e5cd725 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -2,7 +2,8 @@ var fs = require('fs') module.exports={ TLS_connection_options: { - key: fs.readFileSync('lib/key.pem'), - cert: fs.readFileSync('lib/cert.pem') + key: fs.readFileSync('key.pem'), + cert: fs.readFileSync('cert.pem'), + rejectUnauthorized: false } } \ No newline at end of file diff --git a/test/peer.test.js b/test/peer.test.js index 5cabf5e..2bd5695 100644 --- a/test/peer.test.js +++ b/test/peer.test.js @@ -1,18 +1,17 @@ var Peer = require('../lib/Peer').Peer; var assert = require("assert"); -var settings = require('../lib/settings') +var settings = require('../test/test-settings') var tls = require('tls'); var net = require('net'); -var suboptions = settings.TLS_connection_options; +var suboptions = settings.TLS_server_options; describe('P2P Peer', function() { - it('should properly connect to indicated host', function(done) { + it('should properly connect to and disconnect from indicated host', function(done) { var localPeer = false; var server = tls.createServer(suboptions, function(clTxtStream) { server.close(); - clTxtStream.destroy() - localPeer.destroy() + localPeer.disconnect(); done(); }); server.listen(function() { diff --git a/test/test-settings.js b/test/test-settings.js new file mode 100644 index 0000000..f62c550 --- /dev/null +++ b/test/test-settings.js @@ -0,0 +1,10 @@ +var fs = require('fs') + +module.exports={ + TLS_server_options: { + key: fs.readFileSync('key.pem'), + cert: fs.readFileSync('cert.pem'), + rejectUnauthorized:false, + requestCert:true + } +} \ No newline at end of file From f06d89cf105ffc6fd5b3d5bfab48d5dabf62ca47 Mon Sep 17 00:00:00 2001 From: Gamma Squared Date: Tue, 27 Jan 2015 18:09:07 +0100 Subject: [PATCH 3/4] Implemented a switch to distinguish between tls and net --- lib/Peer.js | 30 ++++++++++++++++++------------ lib/settings.js | 1 + test/peer.test.js | 9 ++++----- test/test-settings.js | 1 + 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/lib/Peer.js b/lib/Peer.js index 78ac381..00e92d5 100644 --- a/lib/Peer.js +++ b/lib/Peer.js @@ -1,6 +1,6 @@ var net = require('net'); -var tls = require('tls'); -var settings = require('./settings') +var settings = require('./settings'); +var onlinePackage = settings.useTLS? require('tls') : net; var events = require('events'); var util = require('util'); var sha256 = require('crypto-hashing').sha256; @@ -104,7 +104,6 @@ var Peer = exports.Peer = function Peer(host, port, magic) { return myState; }, set: function(newValue) { - console.log(newValue) var oldState = myState; this.emit('stateChange', {new: newValue, old: oldState}); myState = newValue; @@ -118,18 +117,25 @@ util.inherits(Peer, events.EventEmitter); Peer.prototype.MAX_RECEIVE_BUFFER = 1024*1024*10; Peer.prototype.magicBytes = 0xD9B4BEF9; -Peer.prototype.connect = function connect(clTxtStream) { +Peer.prototype.connect = function connect(connection) { this.state = 'connecting'; this.inbound = new Buffer(this.MAX_RECEIVE_BUFFER); this.inboundCursor = 0; - var target_proto = new tls.createSecurePair().cleartext.__proto__ + function isActive(connection){ + if(settings.useTLS){ + var target_proto = new onlinePackage.createSecurePair().cleartext.__proto__; + return (connection.__proto__ === target_proto); + } else { + return connection instanceof net.Socket; + } + } - if (typeof clTxtStream === 'undefined' || !(clTxtStream.__proto__ === target_proto)) { - var options = settings.TLS_connection_options; + if (typeof connection === 'undefined' || !isActive(connection)) { + var options = settings.useTLS? settings.TLS_connection_options : {}; options.host = this.host.host; options.port = this.host.port; - clTxtStream = tls.connect(options, this.handleConnect.bind(this)); + connection = onlinePackage.connect(options, this.handleConnect.bind(this)); } else { this.state = 'connected'; // Binding to an already-connected socket; will not fire a 'connect' event, but will still fire a 'stateChange' event } @@ -137,9 +143,9 @@ Peer.prototype.connect = function connect(clTxtStream) { enumerable: false, configurable: false, writable:false, - value: clTxtStream + value: connection }); - this.socket.on('error', this.handleError.bind(this)); + //this.socket.on('error', this.handleError.bind(this)); this.socket.on('data', this.handleData.bind(this)); this.socket.on('end', this.handleEnd.bind(this)); this.socket.on('close', this.handleClose.bind(this)); @@ -158,7 +164,7 @@ Peer.prototype.disconnect = function disconnect() { Peer.prototype.destroy = function destroy() { if(this.state != 'connected'){ - this.on('connect', function(){console.log(" CALLED!!! "); this.disconnect();}); + this.on('connect', function(){this.disconnect();}); return; } this.socket.destroy(); @@ -166,7 +172,7 @@ Peer.prototype.destroy = function destroy() { Peer.prototype.getUUID = function getUUID() { return this.host.host+'~'+this.host.port; -} +}; Peer.prototype.handleConnect = function handleConnect() { this.state = 'connected'; diff --git a/lib/settings.js b/lib/settings.js index e5cd725..513696a 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -1,6 +1,7 @@ var fs = require('fs') module.exports={ + useTLS:false, TLS_connection_options: { key: fs.readFileSync('key.pem'), cert: fs.readFileSync('cert.pem'), diff --git a/test/peer.test.js b/test/peer.test.js index 2bd5695..4686714 100644 --- a/test/peer.test.js +++ b/test/peer.test.js @@ -1,15 +1,14 @@ var Peer = require('../lib/Peer').Peer; var assert = require("assert"); -var settings = require('../test/test-settings') -var tls = require('tls'); -var net = require('net'); +var settings = require('../test/test-settings'); +var onlinePackage = require(settings.useTLS? 'tls' : 'net'); var suboptions = settings.TLS_server_options; describe('P2P Peer', function() { it('should properly connect to and disconnect from indicated host', function(done) { var localPeer = false; - var server = tls.createServer(suboptions, function(clTxtStream) { + var server = onlinePackage.createServer(suboptions, function(clTxtStream) { server.close(); localPeer.disconnect(); done(); @@ -27,7 +26,7 @@ describe('P2P Peer', function() { beforeEach(function(done) { serverPeer = false; - server = tls.createServer(suboptions, function(clTxtStream) { + server = onlinePackage.createServer(suboptions, function(clTxtStream) { serverPeer = new Peer(clTxtStream.remoteAddress, clTxtStream.remotePort, magic); serverPeer.connect(clTxtStream); }); diff --git a/test/test-settings.js b/test/test-settings.js index f62c550..7a20abd 100644 --- a/test/test-settings.js +++ b/test/test-settings.js @@ -1,6 +1,7 @@ var fs = require('fs') module.exports={ + useTLS:false, TLS_server_options: { key: fs.readFileSync('key.pem'), cert: fs.readFileSync('cert.pem'), From 4efb5772bf191f29b4f54a0ec9be065f77ced3ec Mon Sep 17 00:00:00 2001 From: Gamma Squared Date: Thu, 12 Feb 2015 23:58:25 +0100 Subject: [PATCH 4/4] Built a new fallback check for new tls.TLSSocket class. --- lib/Peer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Peer.js b/lib/Peer.js index 00e92d5..9e46e03 100644 --- a/lib/Peer.js +++ b/lib/Peer.js @@ -5,6 +5,8 @@ var events = require('events'); var util = require('util'); var sha256 = require('crypto-hashing').sha256; +var fallback = Number(process.version.match(/^v\d+\.(\d+\.\d+)/)[1]) < 11.3 + var Host = function Host(host, port) { var _host = false, // Private variables _port = false, @@ -125,7 +127,7 @@ Peer.prototype.connect = function connect(connection) { function isActive(connection){ if(settings.useTLS){ var target_proto = new onlinePackage.createSecurePair().cleartext.__proto__; - return (connection.__proto__ === target_proto); + return (fallback? connection.__proto__ === target_proto : connection instanceof onlinePackage.TLSSocket); } else { return connection instanceof net.Socket; }