|
|
- /**
- * 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;
- };
|