Spaces:
Configuration error
Configuration error
/** | |
* 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; | |
}; | |