tls: process accumulated input

When creating TLSSocket on top of the regular socket that already
contains some received data, `_tls_wrap.js` should try to write all that
data to the internal `SSL*` instance.

fix #6940
This commit is contained in:
Fedor Indutny 2014-01-23 16:55:28 +04:00
parent 56ebf308dc
commit c79c304ead
4 changed files with 114 additions and 5 deletions

View File

@ -178,15 +178,18 @@ function TLSSocket(socket, options) {
this.on('error', this._tlsError);
if (!this._handle)
this.once('connect', this._init.bind(this));
else
this._init();
if (!this._handle) {
this.once('connect', function() {
this._init(null);
});
} else {
this._init(socket);
}
}
util.inherits(TLSSocket, net.Socket);
exports.TLSSocket = TLSSocket;
TLSSocket.prototype._init = function() {
TLSSocket.prototype._init = function(socket) {
assert(this._handle);
// lib/net.js expect this value to be non-zero if write hasn't been flushed
@ -265,6 +268,13 @@ TLSSocket.prototype._init = function() {
if (options.handshakeTimeout > 0)
this.setTimeout(options.handshakeTimeout, this._handleTimeout);
// Socket already has some buffered data - emulate receiving it
if (socket && socket._readableState.length) {
var buf;
while ((buf = socket.read()) !== null)
this.ssl.receive(buf);
}
};
TLSSocket.prototype.renegotiate = function(options, callback) {

View File

@ -195,6 +195,31 @@ void TLSCallbacks::Wrap(const FunctionCallbackInfo<Value>& args) {
}
void TLSCallbacks::Receive(const FunctionCallbackInfo<Value>& args) {
HandleScope handle_scope(args.GetIsolate());
TLSCallbacks* wrap = Unwrap<TLSCallbacks>(args.This());
CHECK(Buffer::HasInstance(args[0]));
char* data = Buffer::Data(args[0]);
size_t len = Buffer::Length(args[0]);
uv_buf_t buf;
uv_stream_t* stream = wrap->wrap()->stream();
// Copy given buffer entirely or partiall if handle becomes closed
while (len > 0 && !uv_is_closing((uv_handle_t*) stream)) {
wrap->DoAlloc(reinterpret_cast<uv_handle_t*>(stream), len, &buf);
size_t copy = buf.len > len ? len : buf.len;
memcpy(buf.base, data, copy);
wrap->DoRead(stream, buf.len, &buf, UV_UNKNOWN_HANDLE);
data += copy;
len -= copy;
}
}
void TLSCallbacks::Start(const FunctionCallbackInfo<Value>& args) {
HandleScope scope(node_isolate);
@ -700,6 +725,7 @@ void TLSCallbacks::Initialize(Handle<Object> target,
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "TLSWrap"));
NODE_SET_PROTOTYPE_METHOD(t, "receive", Receive);
NODE_SET_PROTOTYPE_METHOD(t, "start", Start);
NODE_SET_PROTOTYPE_METHOD(t, "setVerifyMode", SetVerifyMode);
NODE_SET_PROTOTYPE_METHOD(t,

View File

@ -110,6 +110,7 @@ class TLSCallbacks : public crypto::SSLWrap<TLSCallbacks>,
static void OnClientHelloParseEnd(void* arg);
static void Wrap(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Receive(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Start(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetVerifyMode(const v8::FunctionCallbackInfo<v8::Value>& args);
static void EnableSessionCallbacks(

View File

@ -0,0 +1,72 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
if (!process.versions.openssl) {
console.error('Skipping because node compiled without OpenSSL.');
process.exit(0);
}
var assert = require('assert');
var fs = require('fs');
var net = require('net');
var tls = require('tls');
var crypto = require('crypto');
var common = require('../common');
var sent = 'hello world';
var received = '';
var ended = 0;
var options = {
key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'),
cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem')
};
var server = net.createServer(function(c) {
setTimeout(function() {
var s = new tls.TLSSocket(c, {
isServer: true,
credentials: crypto.createCredentials(options)
});
s.on('data', function(chunk) {
received += chunk;
});
s.on('end', function() {
ended++;
server.close();
s.destroy();
});
}, 200);
}).listen(common.PORT, function() {
var c = tls.connect(common.PORT, {
rejectUnauthorized: false
}, function() {
c.end(sent);
});
});
process.on('exit', function() {
assert.equal(received, sent);
assert.equal(ended, 1);
});