text: "use strict";
  
  const net = require('net');
  
  const dns = require('dns');
  
  const punycode = require('punycode');
  
  class Connector {
    constructor(options, multiSubnetFailover) {
      this.options = options;
      this.multiSubnetFailover = multiSubnetFailover;
    }
  
    execute(cb) {
      if (net.isIP(this.options.host)) {
        this.executeForIP(cb);
      } else {
        this.executeForHostname(cb);
      }
    }
  
    executeForIP(cb) {
      const socket = net.connect(this.options);
  
      const onError = err => {
        socket.removeListener('error', onError);
        socket.removeListener('connect', onConnect);
        socket.destroy();
        cb(err);
      };
  
      const onConnect = () => {
        socket.removeListener('error', onError);
        socket.removeListener('connect', onConnect);
        cb(null, socket);
      };
  
      socket.on('error', onError);
      socket.on('connect', onConnect);
    }
  
    executeForHostname(cb) {
      dns.lookup(punycode.toASCII(this.options.host), {
        all: true
      }, (err, addresses) => {
        if (err) {
          return cb(err);
        }
  
        if (this.multiSubnetFailover) {
          new ParallelConnectionStrategy(addresses, this.options).connect(cb);
        } else {
          new SequentialConnectionStrategy(addresses, this.options).connect(cb);
        }
      });
    }
  
  }
  
  class ParallelConnectionStrategy {
    constructor(addresses, options) {
      this.addresses = addresses;
      this.options = options;
    }
  
    connect(callback) {
      const addresses = this.addresses;
      const sockets = new Array(addresses.length);
      let errorCount = 0;
  
      const onError = function onError(err) {
        errorCount += 1;
        this.removeListener('error', onError);
        this.removeListener('connect', onConnect);
  
        if (errorCount === addresses.length) {
          callback(new Error('Could not connect (parallel)'));
        }
      };
  
      const onConnect = function onConnect() {
        for (let j = 0; j < sockets.length; j++) {
          const socket = sockets[j];
  
          if (this === socket) {
            continue;
          }
  
          socket.removeListener('error', onError);
          socket.removeListener('connect', onConnect);
          socket.destroy();
        }
  
        callback(null, this);
      };
  
      for (let i = 0, len = addresses.length; i < len; i++) {
        const socket = sockets[i] = net.connect(Object.create(this.options, {
          host: {
            value: addresses[i].address
          }
        }));
        socket.on('error', onError);
        socket.on('connect', onConnect);
      }
    }
  
  }
  
  class SequentialConnectionStrategy {
    constructor(addresses, options) {
      this.addresses = addresses;
      this.options = options;
    }
  
    connect(callback) {
      const addresses = this.addresses;
  
      if (!addresses.length) {
        callback(new Error('Could not connect (sequence)'));
        return;
      }
  
      const next = addresses.shift();
      const socket = net.connect(Object.create(this.options, {
        host: {
          value: next.address
        }
      }));
  
      const onError = err => {
        socket.removeListener('error', onError);
        socket.removeListener('connect', onConnect);
        socket.destroy();
        this.connect(callback);
      };
  
      const onConnect = () => {
        socket.removeListener('error', onError);
        socket.removeListener('connect', onConnect);
        callback(null, socket);
      };
  
      socket.on('error', onError);
      socket.on('connect', onConnect);
    }
  
  }
  
  module.exports.Connector = Connector;
  module.exports.ParallelConnectionStrategy = ParallelConnectionStrategy;
  module.exports.SequentialConnectionStrategy = SequentialConnectionStrategy;