You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					250 lines
				
				6.8 KiB
			
		
		
			
		
	
	
					250 lines
				
				6.8 KiB
			| 
								 
											2 years ago
										 
									 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * 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;
							 | 
						||
| 
								 | 
							
								};
							 |