bot18
Version:
A high-frequency cryptocurrency trading bot by Zenbot creator @carlos8f
420 lines (363 loc) • 11.9 kB
JavaScript
module.exports = connect;
connect.connect = connect;
/* this whole file only exists because tls.start
* doens't exists and tls.connect cannot start server
* connections
*
* copied from _tls_wrap.js
*/
// Target API:
//
// var s = require('net').createStream(25, 'smtp.example.com')
// s.on('connect', function() {
// require('tls-connect')(s, {credentials:creds, isServer:false}, function() {
// if (!s.authorized) {
// s.destroy()
// return
// }
//
// s.end("hello world\n")
// })
// })
var net = require('net')
var tls = require('tls')
var util = require('util')
var assert = require('assert')
var crypto = require('crypto')
var createSecureContext = tls.createSecureContext || crypto.createCredentials
// Returns an array [options] or [options, cb]
// It is the same as the argument of Socket.prototype.connect().
function __normalizeConnectArgs(args) {
var options = {};
if (typeof(args[0]) == 'object') {
// connect(options, [cb])
options = args[0];
} else if (isPipeName(args[0])) {
// connect(path, [cb]);
options.path = args[0];
} else {
// connect(port, [host], [cb])
options.port = args[0];
if (typeof(args[1]) === 'string') {
options.host = args[1];
}
}
var cb = args[args.length - 1];
return typeof(cb) === 'function' ? [options, cb] : [options];
}
function __checkServerIdentity(host, cert) {
// Create regexp to much hostnames
function regexpify(host, wildcards) {
// Add trailing dot (make hostnames uniform)
if (!/\.$/.test(host)) host += '.';
// The same applies to hostname with more than one wildcard,
// if hostname has wildcard when wildcards are not allowed,
// or if there are less than two dots after wildcard (i.e. *.com or *d.com)
//
// also
//
// "The client SHOULD NOT attempt to match a presented identifier in
// which the wildcard character comprises a label other than the
// left-most label (e.g., do not match bar.*.example.net)."
// RFC6125
if (!wildcards && /\*/.test(host) || /[\.\*].*\*/.test(host) ||
/\*/.test(host) && !/\*.*\..+\..+/.test(host)) {
return /$./;
}
// Replace wildcard chars with regexp's wildcard and
// escape all characters that have special meaning in regexps
// (i.e. '.', '[', '{', '*', and others)
var re = host.replace(
/\*([a-z0-9\\-_\.])|[\.,\-\\\^\$+?*\[\]\(\):!\|{}]/g,
function(all, sub) {
if (sub) return '[a-z0-9\\-_]*' + (sub === '-' ? '\\-' : sub);
return '\\' + all;
});
return new RegExp('^' + re + '$', 'i');
}
var dnsNames = [],
uriNames = [],
ips = [],
matchCN = true,
valid = false;
// There're several names to perform check against:
// CN and altnames in certificate extension
// (DNS names, IP addresses, and URIs)
//
// Walk through altnames and generate lists of those names
if (cert.subjectaltname) {
cert.subjectaltname.split(/, /g).forEach(function(altname) {
if (/^DNS:/.test(altname)) {
dnsNames.push(altname.slice(4));
} else if (/^IP Address:/.test(altname)) {
ips.push(altname.slice(11));
} else if (/^URI:/.test(altname)) {
var uri = url.parse(altname.slice(4));
if (uri) uriNames.push(uri.hostname);
}
});
}
// If hostname is an IP address, it should be present in the list of IP
// addresses.
if (net.isIP(host)) {
valid = ips.some(function(ip) {
return ip === host;
});
} else {
// Transform hostname to canonical form
if (!/\.$/.test(host)) host += '.';
// Otherwise check all DNS/URI records from certificate
// (with allowed wildcards)
dnsNames = dnsNames.map(function(name) {
return regexpify(name, true);
});
// Wildcards ain't allowed in URI names
uriNames = uriNames.map(function(name) {
return regexpify(name, false);
});
dnsNames = dnsNames.concat(uriNames);
if (dnsNames.length > 0) matchCN = false;
// Match against Common Name (CN) only if no supported identifiers are
// present.
//
// "As noted, a client MUST NOT seek a match for a reference identifier
// of CN-ID if the presented identifiers include a DNS-ID, SRV-ID,
// URI-ID, or any application-specific identifier types supported by the
// client."
// RFC6125
if (matchCN) {
var commonNames = cert.subject.CN;
if (util.isArray(commonNames)) {
for (var i = 0, k = commonNames.length; i < k; ++i) {
dnsNames.push(regexpify(commonNames[i], true));
}
} else {
dnsNames.push(regexpify(commonNames, true));
}
}
valid = dnsNames.some(function(re) {
return re.test(host);
});
}
return valid;
};
// Target API:
//
// var s = tls.connect({port: 8000, host: "google.com"}, function() {
// if (!s.authorized) {
// s.destroy();
// return;
// }
//
// // s.socket;
//
// s.end("hello world\n");
// });
//
//
function normalizeConnectArgs(listArgs) {
var args = __normalizeConnectArgs(listArgs);
var options = args[0];
var cb = args[1];
if (typeof(listArgs[1]) === 'object') {
options = util._extend(options, listArgs[1]);
} else if (typeof(listArgs[2]) === 'object') {
options = util._extend(options, listArgs[2]);
}
return (cb) ? [options, cb] : [options];
}
function legacyConnect(hostname, options, NPN, credentials) {
assert(options.socket);
var pair = tls.createSecurePair(credentials,
!!options.isServer,
!!options.requestCert,
!!options.rejectUnauthorized,
{
NPNProtocols: NPN.NPNProtocols,
servername: hostname
});
legacyPipe(pair, options.socket);
pair.cleartext._controlReleased = true;
pair.on('error', function(err) {
pair.cleartext.emit('error', err);
});
return pair;
}
function connect(/* [port, host], options, cb */) {
var args = normalizeConnectArgs(arguments);
var options = args[0];
var cb = args[1];
var defaults = {
rejectUnauthorized: '0' !== process.env.NODE_TLS_REJECT_UNAUTHORIZED,
requestCert: true,
isServer: false
};
options = util._extend(defaults, options || {});
var hostname = options.servername ||
options.host ||
options.socket && options.socket._host ||
'127.0.0.1',
NPN = {},
// createCredentials() is deprecated, use tls.createSecureContext instead
// https://github.com/dodo/node-tls-connect/issues/2
credentials = options.credentials || createSecureContext(options);
if (tls.convertNPNProtocols)
tls.convertNPNProtocols(options.NPNProtocols, NPN);
// Wrapping TLS socket inside another TLS socket was requested -
// create legacy secure pair
var socket;
var legacy;
var result;
if (typeof tls.TLSSocket === 'undefined') {
legacy = true;
socket = legacyConnect(hostname, options, NPN, credentials);
result = socket.cleartext;
} else {
legacy = false;
socket = new tls.TLSSocket(options.socket, {
credentials: credentials,
isServer: !!options.isServer,
requestCert: !!options.requestCert,
rejectUnauthorized: !!options.rejectUnauthorized,
NPNProtocols: NPN.NPNProtocols
});
result = socket;
}
if (socket._handle && !socket._connecting) {
onHandle();
} else {
// Not even started connecting yet (or probably resolving dns address),
// catch socket errors and assign handle.
if (!legacy && options.socket) {
options.socket.once('connect', function() {
assert(options.socket._handle);
socket._handle = options.socket._handle;
socket._handle.owner = socket;
socket.emit('connect');
});
}
socket.once('connect', onHandle);
}
if (cb)
result.once('secureConnect', cb);
if (!options.socket) {
assert(!legacy);
var connect_opt;
if (options.path && !options.port) {
connect_opt = { path: options.path };
} else {
connect_opt = {
port: options.port,
host: options.host,
localAddress: options.localAddress
};
}
socket.connect(connect_opt);
}
return result;
function onHandle() {
if (!legacy)
socket._releaseControl();
if (options.session)
socket.setSession(options.session);
if (!legacy) {
if (options.servername)
socket.setServername(options.servername);
if (!options.isServer)
socket._start();
}
socket.on('secure', function() {
var ssl = socket._ssl || socket.ssl;
var verifyError = ssl.verifyError();
// Verify that server's identity matches it's certificate's names
if (!verifyError) {
var cert = result.getPeerCertificate();
var validCert = __checkServerIdentity(hostname, cert);
if (!validCert) {
verifyError = new Error('Hostname/IP doesn\'t match certificate\'s ' +
'altnames');
}
}
if (verifyError) {
result.authorized = false;
result.authorizationError = verifyError.message;
if (options.rejectUnauthorized) {
result.emit('error', verifyError);
result.destroy();
return;
} else {
result.emit('secureConnect');
}
} else {
result.authorized = true;
result.emit('secureConnect');
}
// Uncork incoming data
result.removeListener('end', onHangUp);
});
function onHangUp() {
// NOTE: This logic is shared with _http_client.js
if (!socket._hadError) {
socket._hadError = true;
var error = new Error('socket hang up');
error.code = 'ECONNRESET';
socket.destroy();
socket.emit('error', error);
}
}
result.once('end', onHangUp);
}
};
function legacyPipe(pair, socket) {
pair.encrypted.pipe(socket);
socket.pipe(pair.encrypted);
pair.encrypted.on('close', function() {
process.nextTick(function() {
// Encrypted should be unpiped from socket to prevent possible
// write after destroy.
if (pair.encrypted.unpipe)
pair.encrypted.unpipe(socket);
socket.destroySoon();
});
});
pair.fd = socket.fd;
pair._handle = socket._handle;
var cleartext = pair.cleartext;
cleartext.socket = socket;
cleartext.encrypted = pair.encrypted;
cleartext.authorized = false;
// cycle the data whenever the socket drains, so that
// we can pull some more into it. normally this would
// be handled by the fact that pipe() triggers read() calls
// on writable.drain, but CryptoStreams are a bit more
// complicated. Since the encrypted side actually gets
// its data from the cleartext side, we have to give it a
// light kick to get in motion again.
socket.on('drain', function() {
if (pair.encrypted._pending && pair.encrypted._writePending)
pair.encrypted._writePending();
if (pair.cleartext._pending && pair.cleartext._writePending)
pair.cleartext._writePending();
if (pair.encrypted.read)
pair.encrypted.read(0);
if (pair.cleartext.read)
pair.cleartext.read(0);
});
function onerror(e) {
if (cleartext._controlReleased) {
cleartext.emit('error', e);
}
}
function onclose() {
socket.removeListener('error', onerror);
socket.removeListener('timeout', ontimeout);
}
function ontimeout() {
cleartext.emit('timeout');
}
socket.on('error', onerror);
socket.on('close', onclose);
socket.on('timeout', ontimeout);
return cleartext;
};
;