Skip to content

Commit

Permalink
Initial openssl support for net2
Browse files Browse the repository at this point in the history
  • Loading branch information
waveto authored and ry committed Apr 3, 2010
1 parent fdae140 commit fb3a9cd
Show file tree
Hide file tree
Showing 5 changed files with 1,232 additions and 16 deletions.
176 changes: 163 additions & 13 deletions lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ var debugLevel = process.env['NODE_DEBUG'] ? 1 : 0;
function debug () {
if (debugLevel > 0) sys.error.apply(this, arguments);
}

var binding = process.binding('net');

// Note about Buffer interface:
Expand Down Expand Up @@ -45,6 +44,14 @@ var EINPROGRESS = binding.EINPROGRESS;
var ENOENT = binding.ENOENT;
var END_OF_FILE = 42;

// Do we have openssl crypto?
try {
var SecureContext = process.binding('crypto').SecureContext;
var SecureStream = process.binding('crypto').SecureStream;
var crypto = true;
} catch (e) {
var crypto = false;
}

// IDLE TIMEOUTS
//
Expand Down Expand Up @@ -246,6 +253,16 @@ function allocNewPool () {
pool.used = 0;
}

var securePool = null;
function allocNewSecurePool () {
securePool = new Buffer(40*1024);
}
var emptyBuffer = null;
function allocEmptyBuffer () {
emptyBuffer = new Buffer(1);
emptyBuffer.sent = 0;
emptyBuffer.length = 0;
}

function _doFlush () {
var socket = this.socket;
Expand All @@ -271,20 +288,46 @@ function initStream (self) {

//debug('pool.used ' + pool.used);
var bytesRead;
var secureBytesRead;

try {
bytesRead = read(self.fd,
if (self.secure) {
if (!securePool) allocNewSecurePool();
secureBytesRead = read(self.fd, securePool, 0, securePool.length);
self.secureStream.readInject(securePool, 0, secureBytesRead);
bytesRead = self.secureStream.readExtract(pool, pool.used, pool.length - pool.used);
if(!self.secureEstablished) {
if (self.secureStream.isInitFinished()) {
self.secureEstablished = true;
if (self._events && self._events['secure']) self.emit('secure');
}
}
if (secureBytesRead === null && !self.server) {
// Client needs to write as part of handshake
this._writeWatcher.start();
}
} else {
bytesRead = read(self.fd,
pool,
pool.used,
pool.length - pool.used);
pool.length - pool.used);
}
} catch (e) {
self.forceClose(e);
if (this.forceClose) this.forceClose(e);
return;
}

//debug('bytesRead ' + bytesRead + '\n');

if (bytesRead === 0) {
if (self.secure && bytesRead == 0 && secureBytesRead >0){
// Deal with SSL handshake
if (self.server) {
self._checkForSecureHandshake();
} else {
if (self.secureEstablised) self.flush();
else self._checkForSecureHandshake();
}
} else if (bytesRead === 0) {
self.readable = false;
self._readWatcher.stop();

Expand Down Expand Up @@ -341,10 +384,36 @@ function initStream (self) {
self.writable = false;
}

function Credentials(method) {
if (!crypto) {
throw new Error('node.js not compiled with openssl crypto support.');
}
this.context = new SecureContext();
if (method) this.context.init(method);
else this.context.init();
}

exports.createCredentials = function(cred) {
var c = new Credentials(cred.method);
if (cred.key) c.context.setKey(cred.key);
if (cred.cert) c.context.setCert(cred.cert);
if (cred.ca) {
if ( (typeof(cred.ca) == 'object') && cred.ca.length ) {
for(var i=0; i<cred.ca.length; i++)
c.context.addCACert(cred.ca[i]);
} else {
c.context.addCACert(cred.ca);
}
}
return c;
}
exports.Credentials = Credentials;

function Stream (fd) {
events.EventEmitter.call(this);

this.fd = null;
this.secure = false;

if (parseInt(fd) >= 0) {
this.open(fd);
Expand All @@ -353,6 +422,55 @@ function Stream (fd) {
sys.inherits(Stream, events.EventEmitter);
exports.Stream = Stream;

Stream.prototype.setSecure = function(credentials) {
if (!crypto) {
throw new Error('node.js not compiled with openssl crypto support.');
}
this.secure = true;
this.secureEstablished = false;
// If no credentials given, create a new one for just this Stream
if (!credentials) {
this.credentials = new Credentials();
} else {
this.credentials = credentials;
}
this.secureStream = new SecureStream(this.credentials.context, this.server?1:0);

if (!this.server) {
// If client, trigger handshake
this._checkForSecureHandshake();
}


}

Stream.prototype.verifyPeer = function() {
if (!this.secure) {
throw new Error('Stream is not a secure stream.');
}
return this.secureStream.verifyPeer(this.credentials.context);
}

Stream.prototype._checkForSecureHandshake = function() {
// Do an empty write to see if we need to write out as part of handshake
if (!emptyBuffer) allocEmptyBuffer();
this.write(emptyBuffer);
}

Stream.prototype.getPeerCertificate = function(credentials) {
if (!this.secure) {
throw new Error('Stream is not a secure stream.');
}
return this.secureStream.getPeerCertificate();
}

Stream.prototype.getCipher = function() {
if (!this.secure) {
throw new Error('Stream is not a secure stream.');
}
return this.secureStream.getCurrentCipher();
}


Stream.prototype.open = function (fd) {
initStream(this);
Expand Down Expand Up @@ -411,6 +529,15 @@ Stream.prototype.write = function (data, encoding) {
};


Stream.prototype._shutdownSecure = function () {
this.secureStream.shutdown();
if (!securePool) allocNewSecurePool();
var secureLen = this.secureStream.writeExtract(securePool, 0, securePool.length);
try {
var secureBytesWritten = write(this.fd, securePool, 0, secureLen);
} catch (e) {}
}

// Directly writes the data to socket.
//
// Steps:
Expand All @@ -420,14 +547,15 @@ Stream.prototype.write = function (data, encoding) {
// 3. Slice out remaining
// 4. Unshift remaining onto _writeQueue. Return false.
Stream.prototype._writeOut = function (data, encoding) {
if (!this.writable) throw new Error('Stream is not writable');
if (!this.writable) {
if (this.secure) return false;
else throw new Error('Stream is not writable');
}


var buffer, off, len;
var bytesWritten, charsWritten;

var queuedData = false;

if (typeof data != 'string') {
// 'data' is a buffer, ignore 'encoding'
buffer = data;
Expand Down Expand Up @@ -458,7 +586,7 @@ Stream.prototype._writeOut = function (data, encoding) {
assert(charsWritten <= data.length);
}

assert(bytesWritten > 0);
if (encoding) assert(bytesWritten > 0);

buffer = pool;
len = bytesWritten;
Expand All @@ -478,11 +606,26 @@ Stream.prototype._writeOut = function (data, encoding) {
}
}


// Send the buffer.

try {
bytesWritten = write(this.fd, buffer, off, len);
if (this.secure) {
if (!buffer) return false;
bytesWritten = this.secureStream.writeInject(buffer, off, len);
if (!securePool) allocNewSecurePool();
var secureLen = this.secureStream.writeExtract(securePool, 0, securePool.length);
if (secureLen==-1) {
// Check our read again for secure handshake
this._readWatcher.callback();
secureBytesWritten = 0;
} else {
var secureBytesWritten = write(this.fd, securePool, 0, secureLen);
}
if(!this.secureEstablished && this.secureStream.isInitFinished()) {
this.secureEstablished = true;
if (this._events && this._events['secure']) this.emit('secure');
}
} else {
bytesWritten = write(this.fd, buffer, off, len);
}
} catch (e) {
this.forceClose(e);
return false;
Expand Down Expand Up @@ -675,6 +818,10 @@ Stream.prototype.forceClose = function (exception) {

timeout.unenroll(this);

if (this.secure) {
this.secureStream.close();
}

// FIXME Bug when this.fd == 0
if (typeof this.fd == 'number') {
close(this.fd);
Expand All @@ -691,6 +838,9 @@ Stream.prototype._shutdown = function () {
if (this.writable) {
this.writable = false;

if (this.secure) {
this._shutdownSecure();
}
try {
shutdown(this.fd, 'write')
} catch (e) {
Expand Down
14 changes: 13 additions & 1 deletion src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
#include <node_stdio.h>
#include <node_natives.h>
#include <node_version.h>
#ifdef HAVE_OPENSSL
#include <node_crypto.h>
#endif

#include <v8-debug.h>

Expand Down Expand Up @@ -1179,7 +1182,16 @@ static Handle<Value> Binding(const Arguments& args) {
Buffer::Initialize(exports);
binding_cache->Set(module, exports);
}

#ifdef HAVE_OPENSSL
} else if (!strcmp(*module_v, "crypto")) {
if (binding_cache->Has(module)) {
exports = binding_cache->Get(module)->ToObject();
} else {
exports = Object::New();
InitCrypto(exports);
binding_cache->Set(module, exports);
}
#endif
} else if (!strcmp(*module_v, "natives")) {
if (binding_cache->Has(module)) {
exports = binding_cache->Get(module)->ToObject();
Expand Down
Loading

0 comments on commit fb3a9cd

Please sign in to comment.