message-io.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var tls = _interopRequireWildcard(require("tls"));
  7. var _events = require("events");
  8. var _message = _interopRequireDefault(require("./message"));
  9. var _packet = require("./packet");
  10. var _incomingMessageStream = _interopRequireDefault(require("./incoming-message-stream"));
  11. var _outgoingMessageStream = _interopRequireDefault(require("./outgoing-message-stream"));
  12. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  13. function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
  14. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { 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; }
  15. const DuplexPair = require('native-duplexpair');
  16. class MessageIO extends _events.EventEmitter {
  17. constructor(socket, packetSize, debug) {
  18. super();
  19. this.socket = socket;
  20. this.debug = debug;
  21. this.tlsNegotiationComplete = false;
  22. this.incomingMessageStream = new _incomingMessageStream.default(this.debug);
  23. this.incomingMessageStream.on('data', message => {
  24. message.on('data', chunk => {
  25. this.emit('data', chunk);
  26. });
  27. message.on('end', () => {
  28. this.emit('message');
  29. });
  30. });
  31. this.outgoingMessageStream = new _outgoingMessageStream.default(this.debug, {
  32. packetSize: packetSize
  33. });
  34. this.socket.pipe(this.incomingMessageStream);
  35. this.outgoingMessageStream.pipe(this.socket);
  36. }
  37. packetSize(...args) {
  38. if (args.length > 0) {
  39. const packetSize = args[0];
  40. this.debug.log('Packet size changed from ' + this.outgoingMessageStream.packetSize + ' to ' + packetSize);
  41. this.outgoingMessageStream.packetSize = packetSize;
  42. }
  43. if (this.securePair) {
  44. this.securePair.cleartext.setMaxSendFragment(this.outgoingMessageStream.packetSize);
  45. }
  46. return this.outgoingMessageStream.packetSize;
  47. }
  48. startTls(secureContext, hostname, trustServerCertificate) {
  49. const duplexpair = new DuplexPair();
  50. const securePair = this.securePair = {
  51. cleartext: tls.connect({
  52. socket: duplexpair.socket1,
  53. servername: hostname,
  54. secureContext: secureContext,
  55. rejectUnauthorized: !trustServerCertificate
  56. }),
  57. encrypted: duplexpair.socket2
  58. }; // If an error happens in the TLS layer, there is nothing we can do about it.
  59. // Forward the error to the socket so the connection gets properly cleaned up.
  60. securePair.cleartext.on('error', err => {
  61. // Streams in node.js versions before 8.0.0 don't support `.destroy`
  62. if (typeof securePair.encrypted.destroy === 'function') {
  63. securePair.encrypted.destroy();
  64. }
  65. this.socket.destroy(err);
  66. });
  67. securePair.cleartext.on('secureConnect', () => {
  68. const cipher = securePair.cleartext.getCipher();
  69. if (cipher) {
  70. this.debug.log('TLS negotiated (' + cipher.name + ', ' + cipher.version + ')');
  71. }
  72. this.emit('secure', securePair.cleartext);
  73. this.encryptAllFutureTraffic();
  74. });
  75. securePair.encrypted.on('data', data => {
  76. this.sendMessage(_packet.TYPE.PRELOGIN, data, false);
  77. });
  78. }
  79. encryptAllFutureTraffic() {
  80. const securePair = this.securePair;
  81. securePair.cleartext.setMaxSendFragment(this.outgoingMessageStream.packetSize);
  82. securePair.encrypted.removeAllListeners('data');
  83. this.outgoingMessageStream.unpipe(this.socket);
  84. this.socket.unpipe(this.incomingMessageStream);
  85. this.socket.pipe(securePair.encrypted);
  86. securePair.encrypted.pipe(this.socket);
  87. securePair.cleartext.pipe(this.incomingMessageStream);
  88. this.outgoingMessageStream.pipe(securePair.cleartext);
  89. this.tlsNegotiationComplete = true;
  90. }
  91. tlsHandshakeData(data) {
  92. const securePair = this.securePair;
  93. securePair.encrypted.write(data);
  94. } // TODO listen for 'drain' event when socket.write returns false.
  95. // TODO implement incomplete request cancelation (2.2.1.6)
  96. sendMessage(packetType, data, resetConnection) {
  97. const message = new _message.default({
  98. type: packetType,
  99. resetConnection: resetConnection
  100. });
  101. message.end(data);
  102. this.outgoingMessageStream.write(message);
  103. return message;
  104. } // Temporarily suspends the flow of incoming packets.
  105. pause() {
  106. this.incomingMessageStream.pause();
  107. } // Resumes the flow of incoming packets.
  108. resume() {
  109. this.incomingMessageStream.resume();
  110. }
  111. }
  112. var _default = MessageIO;
  113. exports.default = _default;
  114. module.exports = MessageIO;