fcoin/lib/net/proxysocket.js
2016-12-16 18:05:04 -08:00

209 lines
4.3 KiB
JavaScript

'use strict';
var util = require('../utils/util');
var crypto = require('../crypto/crypto');
var BufferWriter = require('../utils/writer');
var assert = require('assert');
var EventEmitter = require('events').EventEmitter;
var IOClient = require('socket.io-client');
function ProxySocket(uri) {
if (!(this instanceof ProxySocket))
return new ProxySocket(uri);
EventEmitter.call(this);
this.info = null;
this.socket = new IOClient(uri, { reconnection: false });
this.sendBuffer = [];
this.recvBuffer = [];
this.paused = false;
this.snonce = null;
this.bytesWritten = 0;
this.bytesRead = 0;
this.remoteAddress = null;
this.remotePort = 0;
this.closed = false;
this._init();
}
util.inherits(ProxySocket, EventEmitter);
ProxySocket.prototype._init = function _init() {
var self = this;
this.socket.on('info', function(info) {
if (self.closed)
return;
self.info = info;
if (info.pow) {
self.snonce = new Buffer(info.snonce, 'hex');
self.target = new Buffer(info.target, 'hex');
}
self.emit('info', info);
});
this.socket.on('error', function(err) {
console.error(err);
});
this.socket.on('tcp connect', function(addr, port) {
if (self.closed)
return;
self.remoteAddress = addr;
self.remotePort = port;
self.emit('connect');
});
this.socket.on('tcp data', function(data) {
data = new Buffer(data, 'hex');
if (self.paused) {
self.recvBuffer.push(data);
return;
}
self.bytesRead += data.length;
self.emit('data', data);
});
this.socket.on('tcp close', function(data) {
if (self.closed)
return;
self.closed = true;
self.emit('close');
});
this.socket.on('tcp error', function(e) {
var err = new Error(e.message);
err.code = e.code;
self.emit('error', err);
});
this.socket.on('disconnect', function() {
if (self.closed)
return;
self.closed = true;
self.emit('close');
});
};
ProxySocket.prototype.resolve = function resolve(name, record, callback) {
var e;
this.socket.emit('dns resolve', name, record, function(err, results) {
if (err) {
e = new Error(err.message);
e.code = err.code || null;
callback(e);
return;
}
callback(null, results);
});
};
ProxySocket.prototype.connect = function connect(port, host) {
var nonce = 0;
var i, pow;
this.remoteAddress = host;
this.remotePort = port;
if (this.closed) {
this.sendBuffer.length = 0;
return;
}
if (!this.info) {
this.once('info', connect.bind(this, port, host));
return;
}
if (this.info.pow) {
util.log(
'Solving proof of work to create socket (%d, %s) -- please wait.',
port, host);
pow = new BufferWriter();
pow.writeU32(nonce);
pow.writeBytes(this.snonce);
pow.writeU32(port);
pow.writeString(host, 'ascii');
pow = pow.render();
do {
nonce++;
assert(nonce <= 0xffffffff, 'Could not create socket.');
pow.writeUInt32LE(nonce, 0, true);
} while (util.cmp(crypto.hash256(pow), this.target) > 0);
util.log('Solved proof of work: %d', nonce);
}
this.socket.emit('tcp connect', port, host, nonce);
for (i = 0; i < this.sendBuffer.length; i++)
this.write(this.sendBuffer[i]);
this.sendBuffer.length = 0;
};
ProxySocket.prototype.write = function write(data, callback) {
if (!this.info) {
this.sendBuffer.push(data);
if (callback)
callback();
return true;
}
this.bytesWritten += data.length;
this.socket.emit('tcp data', data.toString('hex'));
if (callback)
callback();
return true;
};
ProxySocket.prototype.pause = function pause() {
this.paused = true;
this.socket.emit('tcp pause');
};
ProxySocket.prototype.resume = function resume() {
var recv = this.recvBuffer;
var i, data;
this.paused = false;
this.recvBuffer = [];
for (i = 0; i < recv.length; i++) {
data = recv[i];
this.bytesRead += data.length;
this.emit('data', data);
}
this.socket.emit('tcp resume');
};
ProxySocket.prototype.destroy = function destroy() {
if (this.closed)
return;
this.closed = true;
this.socket.disconnect();
};
ProxySocket.connect = function connect(uri, port, host) {
var socket = new ProxySocket(uri);
socket.connect(port, host);
return socket;
};
module.exports = ProxySocket;