1 // SOURCE: https://gist.github.com/848444
  2 
  3 // Target API:
  4 //
  5 //  var s = require('net').createStream(25, 'smtp.example.com');
  6 //  s.on('connect', function() {
  7 //   require('starttls')(s, options, function() {
  8 //      if (!s.authorized) {
  9 //        s.destroy();
 10 //        return;
 11 //      }
 12 //
 13 //      s.end("hello world\n");
 14 //    });
 15 //  });
 16 //
 17 //
 18 
 19 /**
 20  * @namespace STARTTLS module
 21  * @name starttls
 22  */
 23 module.exports.starttls = starttls;
 24 
 25 /**
 26  * <p>Upgrades a socket to a secure TLS connection</p>
 27  * 
 28  * @memberOf starttls
 29  * @param {Object} socket Plaintext socket to be upgraded
 30  * @param {Object} options Certificate data for the server
 31  * @param {Function} callback Callback function to be run after upgrade
 32  */
 33 function starttls(socket, options, callback) {
 34     var sslcontext, pair, cleartext;
 35     
 36     socket.removeAllListeners("data");
 37     sslcontext = require('crypto').createCredentials(options);
 38     pair = require('tls').createSecurePair(sslcontext, true);
 39     cleartext = pipe(pair, socket);
 40 
 41     pair.on('secure', function() {
 42         var verifyError = (pair._ssl || pair.ssl).verifyError();
 43 
 44         if (verifyError) {
 45             cleartext.authorized = false;
 46             cleartext.authorizationError = verifyError;
 47         } else {
 48             cleartext.authorized = true;
 49         }
 50 
 51         callback(cleartext);
 52     });
 53 
 54     cleartext._controlReleased = true;
 55     return pair;
 56 }
 57 
 58 function forwardEvents(events, emitterSource, emitterDestination) {
 59     var map = [], name, handler;
 60     
 61     for(var i = 0, len = events.length; i < len; i++) {
 62         name = events[i];
 63 
 64         handler = forwardEvent.bind(emitterDestination, name);
 65         
 66         map.push(name);
 67         emitterSource.on(name, handler);
 68     }
 69     
 70     return map;
 71 }
 72 
 73 function forwardEvent() {
 74     this.emit.apply(this, arguments);
 75 }
 76 
 77 function removeEvents(map, emitterSource) {
 78     for(var i = 0, len = map.length; i < len; i++){
 79         emitterSource.removeAllListeners(map[i]);
 80     }
 81 }
 82 
 83 function pipe(pair, socket) {
 84     pair.encrypted.pipe(socket);
 85     socket.pipe(pair.encrypted);
 86 
 87     pair.fd = socket.fd;
 88     
 89     var cleartext = pair.cleartext;
 90   
 91     cleartext.socket = socket;
 92     cleartext.encrypted = pair.encrypted;
 93     cleartext.authorized = false;
 94 
 95     function onerror(e) {
 96         if (cleartext._controlReleased) {
 97             cleartext.emit('error', e);
 98         }
 99     }
100 
101     var map = forwardEvents(["timeout", "end", "close", "drain", "error"], socket, cleartext);
102   
103     function onclose() {
104         socket.removeListener('error', onerror);
105         socket.removeListener('close', onclose);
106         removeEvents(map,socket);
107     }
108 
109     socket.on('error', onerror);
110     socket.on('close', onclose);
111 
112     return cleartext;
113 }