UNPKG

bot18

Version:

A high-frequency cryptocurrency trading bot by Zenbot creator @carlos8f

420 lines (363 loc) 11.9 kB
'use strict'; 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; };