"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var tls = _interopRequireWildcard(require("tls")); var _events = require("events"); var _message = _interopRequireDefault(require("./message")); var _packet = require("./packet"); var _incomingMessageStream = _interopRequireDefault(require("./incoming-message-stream")); var _outgoingMessageStream = _interopRequireDefault(require("./outgoing-message-stream")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } const DuplexPair = require('native-duplexpair'); class MessageIO extends _events.EventEmitter { constructor(socket, packetSize, debug) { super(); this.socket = void 0; this.debug = void 0; this.tlsNegotiationComplete = void 0; this.incomingMessageStream = void 0; this.outgoingMessageStream = void 0; this.securePair = void 0; this.socket = socket; this.debug = debug; this.tlsNegotiationComplete = false; this.incomingMessageStream = new _incomingMessageStream.default(this.debug); this.incomingMessageStream.on('data', message => { message.on('data', chunk => { this.emit('data', chunk); }); message.on('end', () => { this.emit('message'); }); }); this.outgoingMessageStream = new _outgoingMessageStream.default(this.debug, { packetSize: packetSize }); this.socket.pipe(this.incomingMessageStream); this.outgoingMessageStream.pipe(this.socket); } packetSize(...args) { if (args.length > 0) { const packetSize = args[0]; this.debug.log('Packet size changed from ' + this.outgoingMessageStream.packetSize + ' to ' + packetSize); this.outgoingMessageStream.packetSize = packetSize; } if (this.securePair) { this.securePair.cleartext.setMaxSendFragment(this.outgoingMessageStream.packetSize); } return this.outgoingMessageStream.packetSize; } startTls(secureContext, hostname, trustServerCertificate) { const duplexpair = new DuplexPair(); const securePair = this.securePair = { cleartext: tls.connect({ socket: duplexpair.socket1, servername: hostname, secureContext: secureContext, rejectUnauthorized: !trustServerCertificate }), encrypted: duplexpair.socket2 }; // If an error happens in the TLS layer, there is nothing we can do about it. // Forward the error to the socket so the connection gets properly cleaned up. securePair.cleartext.on('error', err => { // Streams in node.js versions before 8.0.0 don't support `.destroy` if (typeof securePair.encrypted.destroy === 'function') { securePair.encrypted.destroy(); } this.socket.destroy(err); }); securePair.cleartext.on('secureConnect', () => { const cipher = securePair.cleartext.getCipher(); if (cipher) { this.debug.log('TLS negotiated (' + cipher.name + ', ' + cipher.version + ')'); } this.emit('secure', securePair.cleartext); this.encryptAllFutureTraffic(); }); securePair.encrypted.on('data', data => { this.sendMessage(_packet.TYPE.PRELOGIN, data, false); }); } encryptAllFutureTraffic() { const securePair = this.securePair; securePair.cleartext.setMaxSendFragment(this.outgoingMessageStream.packetSize); securePair.encrypted.removeAllListeners('data'); this.outgoingMessageStream.unpipe(this.socket); this.socket.unpipe(this.incomingMessageStream); this.socket.pipe(securePair.encrypted); securePair.encrypted.pipe(this.socket); securePair.cleartext.pipe(this.incomingMessageStream); this.outgoingMessageStream.pipe(securePair.cleartext); this.tlsNegotiationComplete = true; } tlsHandshakeData(data) { const securePair = this.securePair; securePair.encrypted.write(data); } // TODO listen for 'drain' event when socket.write returns false. // TODO implement incomplete request cancelation (2.2.1.6) sendMessage(packetType, data, resetConnection) { const message = new _message.default({ type: packetType, resetConnection: resetConnection }); message.end(data); this.outgoingMessageStream.write(message); return message; } // Temporarily suspends the flow of incoming packets. pause() { this.incomingMessageStream.pause(); } // Resumes the flow of incoming packets. resume() { this.incomingMessageStream.resume(); } } var _default = MessageIO; exports.default = _default; module.exports = MessageIO;