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..9e46e03 100644 --- a/lib/Peer.js +++ b/lib/Peer.js @@ -1,13 +1,17 @@ var net = require('net'); +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; +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, _version = false; - + Object.defineProperties(this, { 'host': { get: function() { return _host; }, @@ -22,12 +26,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; @@ -94,7 +98,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, @@ -115,13 +119,25 @@ 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(connection) { 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)); + function isActive(connection){ + if(settings.useTLS){ + var target_proto = new onlinePackage.createSecurePair().cleartext.__proto__; + return (fallback? connection.__proto__ === target_proto : connection instanceof onlinePackage.TLSSocket); + } else { + return connection instanceof net.Socket; + } + } + + if (typeof connection === 'undefined' || !isActive(connection)) { + var options = settings.useTLS? settings.TLS_connection_options : {}; + options.host = this.host.host; + options.port = this.host.port; + 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 } @@ -129,9 +145,9 @@ Peer.prototype.connect = function connect(socket) { enumerable: false, configurable: false, writable:false, - value: socket + 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)); @@ -140,17 +156,25 @@ Peer.prototype.connect = function connect(socket) { }; 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(){this.disconnect();}); + return; + } this.socket.destroy(); }; Peer.prototype.getUUID = function getUUID() { return this.host.host+'~'+this.host.port; -} +}; Peer.prototype.handleConnect = function handleConnect() { this.state = 'connected'; @@ -201,7 +225,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..513696a --- /dev/null +++ b/lib/settings.js @@ -0,0 +1,10 @@ +var fs = require('fs') + +module.exports={ + useTLS:false, + TLS_connection_options: { + 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 bb3cd82..4686714 100644 --- a/test/peer.test.js +++ b/test/peer.test.js @@ -1,13 +1,16 @@ var Peer = require('../lib/Peer').Peer; var assert = require("assert"); -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 indicated host', function(done) { + it('should properly connect to and disconnect from indicated host', function(done) { var localPeer = false; - var server = net.createServer(function(socket) { + var server = onlinePackage.createServer(suboptions, function(clTxtStream) { server.close(); - localPeer.destroy(); + localPeer.disconnect(); done(); }); server.listen(function() { @@ -23,9 +26,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 = onlinePackage.createServer(suboptions, function(clTxtStream) { + serverPeer = new Peer(clTxtStream.remoteAddress, clTxtStream.remotePort, magic); + serverPeer.connect(clTxtStream); }); localPeer = false; server.listen(function() { diff --git a/test/test-settings.js b/test/test-settings.js new file mode 100644 index 0000000..7a20abd --- /dev/null +++ b/test/test-settings.js @@ -0,0 +1,11 @@ +var fs = require('fs') + +module.exports={ + useTLS:false, + TLS_server_options: { + key: fs.readFileSync('key.pem'), + cert: fs.readFileSync('cert.pem'), + rejectUnauthorized:false, + requestCert:true + } +} \ No newline at end of file