| /** | 
|  * Socket wrapping functions for TLS. | 
|  * | 
|  * @author Dave Longley | 
|  * | 
|  * Copyright (c) 2009-2012 Digital Bazaar, Inc. | 
|  */ | 
| var forge = require('./forge'); | 
| require('./tls'); | 
|   | 
| /** | 
|  * Wraps a forge.net socket with a TLS layer. | 
|  * | 
|  * @param options: | 
|  *   sessionId: a session ID to reuse, null for a new connection if no session | 
|  *     cache is provided or it is empty. | 
|  *   caStore: an array of certificates to trust. | 
|  *   sessionCache: a session cache to use. | 
|  *   cipherSuites: an optional array of cipher suites to use, see | 
|  *     tls.CipherSuites. | 
|  *   socket: the socket to wrap. | 
|  *   virtualHost: the virtual server name to use in a TLS SNI extension. | 
|  *   verify: a handler used to custom verify certificates in the chain. | 
|  *   getCertificate: an optional callback used to get a certificate. | 
|  *   getPrivateKey: an optional callback used to get a private key. | 
|  *   getSignature: an optional callback used to get a signature. | 
|  *   deflate: function(inBytes) if provided, will deflate TLS records using | 
|  *     the deflate algorithm if the server supports it. | 
|  *   inflate: function(inBytes) if provided, will inflate TLS records using | 
|  *     the deflate algorithm if the server supports it. | 
|  * | 
|  * @return the TLS-wrapped socket. | 
|  */ | 
| forge.tls.wrapSocket = function(options) { | 
|   // get raw socket | 
|   var socket = options.socket; | 
|   | 
|   // create TLS socket | 
|   var tlsSocket = { | 
|     id: socket.id, | 
|     // set handlers | 
|     connected: socket.connected || function(e) {}, | 
|     closed: socket.closed || function(e) {}, | 
|     data: socket.data || function(e) {}, | 
|     error: socket.error || function(e) {} | 
|   }; | 
|   | 
|   // create TLS connection | 
|   var c = forge.tls.createConnection({ | 
|     server: false, | 
|     sessionId: options.sessionId || null, | 
|     caStore: options.caStore || [], | 
|     sessionCache: options.sessionCache || null, | 
|     cipherSuites: options.cipherSuites || null, | 
|     virtualHost: options.virtualHost, | 
|     verify: options.verify, | 
|     getCertificate: options.getCertificate, | 
|     getPrivateKey: options.getPrivateKey, | 
|     getSignature: options.getSignature, | 
|     deflate: options.deflate, | 
|     inflate: options.inflate, | 
|     connected: function(c) { | 
|       // first handshake complete, call handler | 
|       if(c.handshakes === 1) { | 
|         tlsSocket.connected({ | 
|           id: socket.id, | 
|           type: 'connect', | 
|           bytesAvailable: c.data.length() | 
|         }); | 
|       } | 
|     }, | 
|     tlsDataReady: function(c) { | 
|       // send TLS data over socket | 
|       return socket.send(c.tlsData.getBytes()); | 
|     }, | 
|     dataReady: function(c) { | 
|       // indicate application data is ready | 
|       tlsSocket.data({ | 
|         id: socket.id, | 
|         type: 'socketData', | 
|         bytesAvailable: c.data.length() | 
|       }); | 
|     }, | 
|     closed: function(c) { | 
|       // close socket | 
|       socket.close(); | 
|     }, | 
|     error: function(c, e) { | 
|       // send error, close socket | 
|       tlsSocket.error({ | 
|         id: socket.id, | 
|         type: 'tlsError', | 
|         message: e.message, | 
|         bytesAvailable: 0, | 
|         error: e | 
|       }); | 
|       socket.close(); | 
|     } | 
|   }); | 
|   | 
|   // handle doing handshake after connecting | 
|   socket.connected = function(e) { | 
|     c.handshake(options.sessionId); | 
|   }; | 
|   | 
|   // handle closing TLS connection | 
|   socket.closed = function(e) { | 
|     if(c.open && c.handshaking) { | 
|       // error | 
|       tlsSocket.error({ | 
|         id: socket.id, | 
|         type: 'ioError', | 
|         message: 'Connection closed during handshake.', | 
|         bytesAvailable: 0 | 
|       }); | 
|     } | 
|     c.close(); | 
|   | 
|     // call socket handler | 
|     tlsSocket.closed({ | 
|       id: socket.id, | 
|       type: 'close', | 
|       bytesAvailable: 0 | 
|     }); | 
|   }; | 
|   | 
|   // handle error on socket | 
|   socket.error = function(e) { | 
|     // error | 
|     tlsSocket.error({ | 
|       id: socket.id, | 
|       type: e.type, | 
|       message: e.message, | 
|       bytesAvailable: 0 | 
|     }); | 
|     c.close(); | 
|   }; | 
|   | 
|   // handle receiving raw TLS data from socket | 
|   var _requiredBytes = 0; | 
|   socket.data = function(e) { | 
|     // drop data if connection not open | 
|     if(!c.open) { | 
|       socket.receive(e.bytesAvailable); | 
|     } else { | 
|       // only receive if there are enough bytes available to | 
|       // process a record | 
|       if(e.bytesAvailable >= _requiredBytes) { | 
|         var count = Math.max(e.bytesAvailable, _requiredBytes); | 
|         var data = socket.receive(count); | 
|         if(data !== null) { | 
|           _requiredBytes = c.process(data); | 
|         } | 
|       } | 
|     } | 
|   }; | 
|   | 
|   /** | 
|    * Destroys this socket. | 
|    */ | 
|   tlsSocket.destroy = function() { | 
|     socket.destroy(); | 
|   }; | 
|   | 
|   /** | 
|    * Sets this socket's TLS session cache. This should be called before | 
|    * the socket is connected or after it is closed. | 
|    * | 
|    * The cache is an object mapping session IDs to internal opaque state. | 
|    * An application might need to change the cache used by a particular | 
|    * tlsSocket between connections if it accesses multiple TLS hosts. | 
|    * | 
|    * @param cache the session cache to use. | 
|    */ | 
|   tlsSocket.setSessionCache = function(cache) { | 
|     c.sessionCache = tls.createSessionCache(cache); | 
|   }; | 
|   | 
|   /** | 
|    * Connects this socket. | 
|    * | 
|    * @param options: | 
|    *           host: the host to connect to. | 
|    *           port: the port to connect to. | 
|    *           policyPort: the policy port to use (if non-default), 0 to | 
|    *              use the flash default. | 
|    *           policyUrl: the policy file URL to use (instead of port). | 
|    */ | 
|   tlsSocket.connect = function(options) { | 
|     socket.connect(options); | 
|   }; | 
|   | 
|   /** | 
|    * Closes this socket. | 
|    */ | 
|   tlsSocket.close = function() { | 
|     c.close(); | 
|   }; | 
|   | 
|   /** | 
|    * Determines if the socket is connected or not. | 
|    * | 
|    * @return true if connected, false if not. | 
|    */ | 
|   tlsSocket.isConnected = function() { | 
|     return c.isConnected && socket.isConnected(); | 
|   }; | 
|   | 
|   /** | 
|    * Writes bytes to this socket. | 
|    * | 
|    * @param bytes the bytes (as a string) to write. | 
|    * | 
|    * @return true on success, false on failure. | 
|    */ | 
|   tlsSocket.send = function(bytes) { | 
|     return c.prepare(bytes); | 
|   }; | 
|   | 
|   /** | 
|    * Reads bytes from this socket (non-blocking). Fewer than the number of | 
|    * bytes requested may be read if enough bytes are not available. | 
|    * | 
|    * This method should be called from the data handler if there are enough | 
|    * bytes available. To see how many bytes are available, check the | 
|    * 'bytesAvailable' property on the event in the data handler or call the | 
|    * bytesAvailable() function on the socket. If the browser is msie, then the | 
|    * bytesAvailable() function should be used to avoid race conditions. | 
|    * Otherwise, using the property on the data handler's event may be quicker. | 
|    * | 
|    * @param count the maximum number of bytes to read. | 
|    * | 
|    * @return the bytes read (as a string) or null on error. | 
|    */ | 
|   tlsSocket.receive = function(count) { | 
|     return c.data.getBytes(count); | 
|   }; | 
|   | 
|   /** | 
|    * Gets the number of bytes available for receiving on the socket. | 
|    * | 
|    * @return the number of bytes available for receiving. | 
|    */ | 
|   tlsSocket.bytesAvailable = function() { | 
|     return c.data.length(); | 
|   }; | 
|   | 
|   return tlsSocket; | 
| }; |