Skip to content

Commit fb3a9cd

Browse files
wavetory
authored andcommitted
Initial openssl support for net2
1 parent fdae140 commit fb3a9cd

File tree

5 files changed

+1232
-16
lines changed

5 files changed

+1232
-16
lines changed

lib/net.js

Lines changed: 163 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ var debugLevel = process.env['NODE_DEBUG'] ? 1 : 0;
99
function debug () {
1010
if (debugLevel > 0) sys.error.apply(this, arguments);
1111
}
12-
1312
var binding = process.binding('net');
1413

1514
// Note about Buffer interface:
@@ -45,6 +44,14 @@ var EINPROGRESS = binding.EINPROGRESS;
4544
var ENOENT = binding.ENOENT;
4645
var END_OF_FILE = 42;
4746

47+
// Do we have openssl crypto?
48+
try {
49+
var SecureContext = process.binding('crypto').SecureContext;
50+
var SecureStream = process.binding('crypto').SecureStream;
51+
var crypto = true;
52+
} catch (e) {
53+
var crypto = false;
54+
}
4855

4956
// IDLE TIMEOUTS
5057
//
@@ -246,6 +253,16 @@ function allocNewPool () {
246253
pool.used = 0;
247254
}
248255

256+
var securePool = null;
257+
function allocNewSecurePool () {
258+
securePool = new Buffer(40*1024);
259+
}
260+
var emptyBuffer = null;
261+
function allocEmptyBuffer () {
262+
emptyBuffer = new Buffer(1);
263+
emptyBuffer.sent = 0;
264+
emptyBuffer.length = 0;
265+
}
249266

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

272289
//debug('pool.used ' + pool.used);
273290
var bytesRead;
291+
var secureBytesRead;
274292

275293
try {
276-
bytesRead = read(self.fd,
294+
if (self.secure) {
295+
if (!securePool) allocNewSecurePool();
296+
secureBytesRead = read(self.fd, securePool, 0, securePool.length);
297+
self.secureStream.readInject(securePool, 0, secureBytesRead);
298+
bytesRead = self.secureStream.readExtract(pool, pool.used, pool.length - pool.used);
299+
if(!self.secureEstablished) {
300+
if (self.secureStream.isInitFinished()) {
301+
self.secureEstablished = true;
302+
if (self._events && self._events['secure']) self.emit('secure');
303+
}
304+
}
305+
if (secureBytesRead === null && !self.server) {
306+
// Client needs to write as part of handshake
307+
this._writeWatcher.start();
308+
}
309+
} else {
310+
bytesRead = read(self.fd,
277311
pool,
278312
pool.used,
279-
pool.length - pool.used);
313+
pool.length - pool.used);
314+
}
280315
} catch (e) {
281-
self.forceClose(e);
316+
if (this.forceClose) this.forceClose(e);
282317
return;
283318
}
284319

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

287-
if (bytesRead === 0) {
322+
if (self.secure && bytesRead == 0 && secureBytesRead >0){
323+
// Deal with SSL handshake
324+
if (self.server) {
325+
self._checkForSecureHandshake();
326+
} else {
327+
if (self.secureEstablised) self.flush();
328+
else self._checkForSecureHandshake();
329+
}
330+
} else if (bytesRead === 0) {
288331
self.readable = false;
289332
self._readWatcher.stop();
290333

@@ -341,10 +384,36 @@ function initStream (self) {
341384
self.writable = false;
342385
}
343386

387+
function Credentials(method) {
388+
if (!crypto) {
389+
throw new Error('node.js not compiled with openssl crypto support.');
390+
}
391+
this.context = new SecureContext();
392+
if (method) this.context.init(method);
393+
else this.context.init();
394+
}
395+
396+
exports.createCredentials = function(cred) {
397+
var c = new Credentials(cred.method);
398+
if (cred.key) c.context.setKey(cred.key);
399+
if (cred.cert) c.context.setCert(cred.cert);
400+
if (cred.ca) {
401+
if ( (typeof(cred.ca) == 'object') && cred.ca.length ) {
402+
for(var i=0; i<cred.ca.length; i++)
403+
c.context.addCACert(cred.ca[i]);
404+
} else {
405+
c.context.addCACert(cred.ca);
406+
}
407+
}
408+
return c;
409+
}
410+
exports.Credentials = Credentials;
411+
344412
function Stream (fd) {
345413
events.EventEmitter.call(this);
346414

347415
this.fd = null;
416+
this.secure = false;
348417

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

425+
Stream.prototype.setSecure = function(credentials) {
426+
if (!crypto) {
427+
throw new Error('node.js not compiled with openssl crypto support.');
428+
}
429+
this.secure = true;
430+
this.secureEstablished = false;
431+
// If no credentials given, create a new one for just this Stream
432+
if (!credentials) {
433+
this.credentials = new Credentials();
434+
} else {
435+
this.credentials = credentials;
436+
}
437+
this.secureStream = new SecureStream(this.credentials.context, this.server?1:0);
438+
439+
if (!this.server) {
440+
// If client, trigger handshake
441+
this._checkForSecureHandshake();
442+
}
443+
444+
445+
}
446+
447+
Stream.prototype.verifyPeer = function() {
448+
if (!this.secure) {
449+
throw new Error('Stream is not a secure stream.');
450+
}
451+
return this.secureStream.verifyPeer(this.credentials.context);
452+
}
453+
454+
Stream.prototype._checkForSecureHandshake = function() {
455+
// Do an empty write to see if we need to write out as part of handshake
456+
if (!emptyBuffer) allocEmptyBuffer();
457+
this.write(emptyBuffer);
458+
}
459+
460+
Stream.prototype.getPeerCertificate = function(credentials) {
461+
if (!this.secure) {
462+
throw new Error('Stream is not a secure stream.');
463+
}
464+
return this.secureStream.getPeerCertificate();
465+
}
466+
467+
Stream.prototype.getCipher = function() {
468+
if (!this.secure) {
469+
throw new Error('Stream is not a secure stream.');
470+
}
471+
return this.secureStream.getCurrentCipher();
472+
}
473+
356474

357475
Stream.prototype.open = function (fd) {
358476
initStream(this);
@@ -411,6 +529,15 @@ Stream.prototype.write = function (data, encoding) {
411529
};
412530

413531

532+
Stream.prototype._shutdownSecure = function () {
533+
this.secureStream.shutdown();
534+
if (!securePool) allocNewSecurePool();
535+
var secureLen = this.secureStream.writeExtract(securePool, 0, securePool.length);
536+
try {
537+
var secureBytesWritten = write(this.fd, securePool, 0, secureLen);
538+
} catch (e) {}
539+
}
540+
414541
// Directly writes the data to socket.
415542
//
416543
// Steps:
@@ -420,14 +547,15 @@ Stream.prototype.write = function (data, encoding) {
420547
// 3. Slice out remaining
421548
// 4. Unshift remaining onto _writeQueue. Return false.
422549
Stream.prototype._writeOut = function (data, encoding) {
423-
if (!this.writable) throw new Error('Stream is not writable');
550+
if (!this.writable) {
551+
if (this.secure) return false;
552+
else throw new Error('Stream is not writable');
553+
}
424554

425555

426556
var buffer, off, len;
427557
var bytesWritten, charsWritten;
428-
429558
var queuedData = false;
430-
431559
if (typeof data != 'string') {
432560
// 'data' is a buffer, ignore 'encoding'
433561
buffer = data;
@@ -458,7 +586,7 @@ Stream.prototype._writeOut = function (data, encoding) {
458586
assert(charsWritten <= data.length);
459587
}
460588

461-
assert(bytesWritten > 0);
589+
if (encoding) assert(bytesWritten > 0);
462590

463591
buffer = pool;
464592
len = bytesWritten;
@@ -478,11 +606,26 @@ Stream.prototype._writeOut = function (data, encoding) {
478606
}
479607
}
480608

481-
482-
// Send the buffer.
483-
484609
try {
485-
bytesWritten = write(this.fd, buffer, off, len);
610+
if (this.secure) {
611+
if (!buffer) return false;
612+
bytesWritten = this.secureStream.writeInject(buffer, off, len);
613+
if (!securePool) allocNewSecurePool();
614+
var secureLen = this.secureStream.writeExtract(securePool, 0, securePool.length);
615+
if (secureLen==-1) {
616+
// Check our read again for secure handshake
617+
this._readWatcher.callback();
618+
secureBytesWritten = 0;
619+
} else {
620+
var secureBytesWritten = write(this.fd, securePool, 0, secureLen);
621+
}
622+
if(!this.secureEstablished && this.secureStream.isInitFinished()) {
623+
this.secureEstablished = true;
624+
if (this._events && this._events['secure']) this.emit('secure');
625+
}
626+
} else {
627+
bytesWritten = write(this.fd, buffer, off, len);
628+
}
486629
} catch (e) {
487630
this.forceClose(e);
488631
return false;
@@ -675,6 +818,10 @@ Stream.prototype.forceClose = function (exception) {
675818

676819
timeout.unenroll(this);
677820

821+
if (this.secure) {
822+
this.secureStream.close();
823+
}
824+
678825
// FIXME Bug when this.fd == 0
679826
if (typeof this.fd == 'number') {
680827
close(this.fd);
@@ -691,6 +838,9 @@ Stream.prototype._shutdown = function () {
691838
if (this.writable) {
692839
this.writable = false;
693840

841+
if (this.secure) {
842+
this._shutdownSecure();
843+
}
694844
try {
695845
shutdown(this.fd, 'write')
696846
} catch (e) {

src/node.cc

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
#include <node_stdio.h>
3333
#include <node_natives.h>
3434
#include <node_version.h>
35+
#ifdef HAVE_OPENSSL
36+
#include <node_crypto.h>
37+
#endif
3538

3639
#include <v8-debug.h>
3740

@@ -1179,7 +1182,16 @@ static Handle<Value> Binding(const Arguments& args) {
11791182
Buffer::Initialize(exports);
11801183
binding_cache->Set(module, exports);
11811184
}
1182-
1185+
#ifdef HAVE_OPENSSL
1186+
} else if (!strcmp(*module_v, "crypto")) {
1187+
if (binding_cache->Has(module)) {
1188+
exports = binding_cache->Get(module)->ToObject();
1189+
} else {
1190+
exports = Object::New();
1191+
InitCrypto(exports);
1192+
binding_cache->Set(module, exports);
1193+
}
1194+
#endif
11831195
} else if (!strcmp(*module_v, "natives")) {
11841196
if (binding_cache->Has(module)) {
11851197
exports = binding_cache->Get(module)->ToObject();

0 commit comments

Comments
 (0)