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 }